# Project export: DripAI

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 10.0
- Tagline: An AI-powered product catered to give you an outfit plan for the day based on your wardrobe and the weather conditions in your area.
- Devpost: https://devpost.com/software/dripai
- GitHub: https://github.com/LuuAlex/Cal_Hacks_10.0
- Team: 3 GitHub contributor(s) — Alex Luu (14 commits), Satvik Lolla (8 commits), Vrushank Prakash (4 commits)

## Devpost submission (written by the team)

### Inspiration

As Berkeley students, it is often difficult to gauge the weather and decide what clothes to wear. Students end up wearing clothes that suit either the morning chill or the afternoon heat, and it can be frustrating and uncomfortable while they perform their daily activities. An app/website that helps people to select the right clothes for the varying weather conditions would be extremely helpful.

### What it does

DripAI solves this problem by making accurate recommendations on what clothes to bring/wear based on daily weather conditions and a person’s style. The user first answers questions about where they live, what time they will be outside for, what activities they will be doing out, and what clothes they prefer to wear. The user’s location and time they will be out are fed into a weather API, and an LLM makes recommendations and an image that displays what clothes would be ideal in the given weather conditions.

### How we built it

We used a Weather API to generate a result of the weather at a given location and time range. This information is fed into the Llama LLM from TogetherAI, which generates a recommendation. Stable diffusion is then used to generate an image based on the recommendation. This is outputted to the user via the website we created using Reflex.

### Challenges we ran into

Integrating the front end and the back end together using State Ensuring that the LLM outputted accurate recommendations based on the user’s data. Handling the delay in generating answers from the LLM.

### Accomplishments we're proud of

Able to successfully generate an image to illustrate the response given by the AI Developed an easy-to-use UI to get users their outfit plans ASAP Combined the response from the Llama LLM and the UI into a product that users can easily interact with.

### What we learned

We learned how to extract information from an API. Using the info from the API, we learned how exactly to feed it into the LLM. Using state-of-the-art models to power our product and output recommendations

### What's next

A user login system that customizes their wardrobes online & saves input data so that the user can run the app with the same parameters for similar days Allow users to upload pictures of themselves to see how the suggested outfits would look on them A VR try-on to allow users to see their recommended fit in real-time A way to share and comment on fits in a social media sense, from which we can feed this data back into the recommendation system

## README (from the GitHub repository)

# Welcome to Reflex!

This is the base Reflex template - installed when you run `reflex init`.

If you want to use a different template, pass the `--template` flag to `reflex init`.
For example, if you want a more basic starting point, you can run:

```bash
reflex init --template blank
```

## About this Template

This template has the following directory structure:

```bash
├── README.md
├── assets
├── rxconfig.py
└── {your_app}
    ├── __init__.py
    ├── components
    │   ├── __init__.py
    │   └── sidebar.py
    ├── pages
    │   ├── __init__.py
    │   ├── dashboard.py
    │   ├── index.py
    │   └── settings.py
    ├── state.py
    ├── styles.py
    ├── templates
    │   ├── __init__.py
    │   └── template.py
    └── {your_app}.py
```

See the [Project Structure docs](https://reflex.dev/docs/getting-started/project-structure/) for more information on general Reflex project structure.

### Adding Pages

In this template, the pages in your app are defined in `{your_app}/pages/`.
Each page is a function that returns a Reflex component.
For example, to edit this page you can modify `{your_app}/pages/index.py`.
See the [pages docs](https://reflex.dev/docs/components/pages/) for more information on pages.

In this template, instead of using `rx.add_page` or the `@rx.page` decorator,
we use the `@template` decorator from `{your_app}/templates/template.py`.

To add a new page:

1. Add a new file in `{your_app}/pages/`. We recommend using one file per page, but you can also group pages in a single file.
2. Add a new function with the `@template` decorator, which takes the same arguments as `@rx.page`.
3. Import the page in your `{your_app}/pages/__init__.py` file and it will automatically be added to the app.


### Adding Components

In order to keep your code organized, we recommend putting components that are
used across multiple pages in the `{your_app}/components/` directory.

In this template, we have a sidebar component in `{your_app}/components/sidebar.py`.

### Adding State

In this template, we define the base state of the app in `{your_app}/state.py`.
The base state is useful for general app state that is used across multiple pages.

In this template, the base state handles the toggle for the sidebar.

As your app grows, we recommend using [substates](https://reflex.dev/docs/state/substates/)
to organize your state. You can either define substates in their own files, or if the state is
specific to a page, you can define it in the page file itself.


## Detected evidence (automated analysis)

Indexed codebase: 13 recognized source files, 24 KB.
- Python (language) — detected in the code

## Codebase structure (from repository index)

### Files (17 of 17)

```
.gitignore
CalHacks10/__init__.py
CalHacks10/.gitignore
CalHacks10/CalHacks10.py
CalHacks10/components/__init__.py
CalHacks10/LLMClass.py
CalHacks10/pages/__init__.py
CalHacks10/pages/index.py
CalHacks10/pages/suggestion.py
CalHacks10/README.md
CalHacks10/requirements.txt
CalHacks10/rxconfig.py
CalHacks10/state.py
CalHacks10/styles.py
README.md
requirements.txt
rxconfig.py
```

### Dependencies

- CalHacks10/requirements.txt: reflex@==0.3.1
- requirements.txt: python-weather, reflex@==0.3.1, together

### Recent commits (newest first)

- final code
- final product
- merge our work
- fixed stuff
- deleted stuff
- frontend
- FULL BACKEND WOKS
- resolve merge
- add button
- fixed so does not need extra button
- update ui
- merge:
- weather data display works
- added styling
- save work
- WORKING BUILD IMAGE
- data fetch should work
- start combining backend, frontend
- added stuff
- merged

## Key source files (fetched from GitHub, selected and truncated for size)

### requirements.txt

```
together
python-weather
reflex==0.3.1

```

### CalHacks10/requirements.txt

```

reflex==0.3.1

```

### CalHacks10/pages/index.py

```python
from CalHacks10 import styles
from CalHacks10.state import State
from typing import List

import reflex as rx

hour_options = ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"]
minute_options = ["00", "01", "02", "03", "04", "05", "06", "07", "08", "09"]
for i in range(10,60):
    minute_options.append(f"{i}")
am_pm_options = ["AM", "PM"]

@rx.page(route="/", title="Home", image="/github.svg")
def index() -> rx.Component:
    questions = [
        (
            "What city and state you are in?"
        ),
        (
           "When do you plan to go outside?"
        ),
        (
           "When do you plan to come back?"
        ),
        (
           "What are the activities you plan to do during this time?"
        ),
        (
           "What are some clothes that you'd like to wear today?"
        ),
    ]

    return rx.container(
        rx.card(
            rx.box(
                rx.hstack(
                    title(),
                    rx.spacer(),
                    dark_mode(),
                    width="100%",
                    align="right",
                ),
                rx.box(margin_y="1em"),
                instruction(),
            ),
            margin="8px"
        ),
        rx.box(margin_y="1em"),
        rx.card(
            rx.box(
                input_bar("Enter your location in city, state format (ex: Berkeley, CA).", State.location, State.set_location, questions[0]),
                rx.box(margin_y="2em"),
                time_input_bar([State.time_period1_hours, State.time_period1_mins, State.time_period1_ampm], [State.set_time_period1_hours, State.set_time_period1_mins, State.set_time_period1_ampm], questions[1]),
                rx.box(margin_y="2em"),
                time_input_bar([State.time_period2_hours, State.time_period2_mins, State.time_period2_ampm], [State.set_time_period2_hours, State.set_time_period2_mins, State.set_time_period2_ampm], questions[2]),
                rx.box(margin_y="2em"),
                input_bar("Enter the activities you are planning for the day as a list.", State.activity, State.set_activity, questions[3]),
                rx.box(margin_y="2em"),
                input_bar("Enter the clothes you currently own.", State.clothes_preference, State.set_clothes_preference, questions[4]),
            ),
            margin="8px"
        ),
        rx.box(margin_y="1em"),
        rx.card(
            submit(),
            margin="8px"
        ),
        max_width="70em"
    )

def title() -> rx.Component:
    return rx.text(
        "DripAI",
        background_image="linear-gradient(271.68deg, #FF1B6B 10.75%, #45CAFF 88.52%)",
        background_clip="text",
        font_weight="bold",
        font_size="4em",
        text_align="center",
    )

def instruction() -> rx.Component:
    return rx.box(
        rx.text(
            "Having trouble deciding what to wear today? Don't want to check the weather app? Fill out these questions to get a personalized fit!",
            font_size="20px"
        ),
    )

def input_bar(placeholder, stateVar, changeVar, question) -> rx.Component:
    return rx.box(
        rx.box(
            rx.text(
                question,
                font_size="18px"
            ),
            text_align="left",
            
        ),
        rx.box(margin_y="1em"),
        rx.input(
            value=stateVar,
            placeholder=placeholder,
            on_change=changeVar
        )
    )

def time_input_bar(stateVar, changeVar, question) -> rx.Component:
    return rx.box(
        rx.text(
            question,
            font_size="18px"
        ),
        rx.box(margin_y="1em"),
        rx.hstack(
            rx.select(hour_options, on_change=changeVar[0], value=stateVar[0], placeholder="Hours"),
            rx.box(":", margin_y="1em"),
            rx.select(minute_options, on_change=changeVar[1], value=stateVar[1], placeholder="Minutes"),
            rx.select(am_pm_options, on_change=changeVar[2], value=stateVar[2], placeholder="AM/PM")
        )
    )

def submit() -> rx.Component:
    return rx.box(
        rx.button(
            "Submit",
            on_click=State.answer, 
            on_mouse_up=rx.redirect("/suggestion"),
            color = 'light green',
        )
    )
def dark_mode() -> rx.Component:
    return rx.box(
        rx.button(rx.icon(tag="moon"), on_click=rx.toggle_color_mode,)
    )

```

### rxconfig.py

```python
import reflex as rx

config = rx.Config(
    app_name="CalHacks10",
)
```

### CalHacks10/__init__.py

```python
"""Base template for Reflex."""
from .LLMClass import LLM
```

### CalHacks10/rxconfig.py

```python
import reflex as rx

config = rx.Config(
    app_name="CalHacks10",
)
```

### CalHacks10/CalHacks10.py

```python
"""Welcome to Reflex!."""

from CalHacks10 import styles

# Import all the pages.
from CalHacks10.pages import *

import reflex as rx

# Create the app and compile it.

# Fonts to include.
STYLESHEETS = [
    "https://fonts.googleapis.com/css2?family=Instrument+Sans:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500;1,600;1,700&family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&family=IBM+Plex+Mono:ital,wght@0,500;0,600;1,600&display=swap",
]

style = {
    "font_family": "Instrument Sans",
    "font_size": "16px",
}


app = rx.App(
    style=style,
    stylesheets=STYLESHEETS,
)
app.compile()
```

### CalHacks10/styles.py

```python
"""Styles for the app."""

import reflex as rx

border_radius = "0.375rem"
box_shadow = "0px 0px 0px 1px rgba(84, 82, 95, 0.14)"
border = "1px solid #F4F3F6"
text_color = "black"
accent_text_color = "#1A1060"
accent_color = "#F5EFFE"
hover_accent_color = {"_hover": {"color": accent_color}}
hover_accent_bg = {"_hover": {"bg": accent_color}}
content_width_vw = "90vw"
sidebar_width = "20em"

template_page_style = {"padding_top": "5em", "padding_x": ["auto", "2em"]}

template_content_style = {
    "width": "100%",
    "align_items": "flex-start",
    "box_shadow": box_shadow,
    "border_radius": border_radius,
    "padding": "1em",
    "margin_bottom": "2em",
}

link_style = {
    "color": text_color,
    "text_decoration": "none",
    **hover_accent_color,
}

overlapping_button_style = {
    "background_color": "white",
    "border": border,
    "border_radius": border_radius,
}

base_style = {
    rx.MenuButton: {
        "width": "3em",
        "height": "3em",
        **overlapping_button_style,
    },
    rx.MenuItem: hover_accent_bg,
}

markdown_style = {
    "code": lambda text: rx.code(text, color="#1F1944", bg="#EAE4FD"),
    "a": lambda text, **props: rx.link(
        text,
        **props,
        font_weight="bold",
        color="#03030B",
        text_decoration="underline",
        text_decoration_color="#AD9BF8",
        _hover={
            "color": "#AD9BF8",
            "text_decoration": "underline",
            "text_decoration_color": "#03030B",
        },
    ),
}

```

### CalHacks10/state.py

```python
import reflex as rx
import datetime

from .LLMClass import LLM

class State(rx.State):
    time_period1_hours = ""
    time_period1_mins = ""
    time_period1_ampm = ""

    time_period2_hours = ""
    time_period2_mins = ""
    time_period2_ampm = ""

    location = ""
    clothes_preference = ""
    activity = ""

    output = ""
    image = ""
    weather: list[tuple[str, tuple]] = []
    new_weather: list[tuple] = []
    loading_screen = True

    def create_time(self, hours, mins, ampm):
        hours = int(hours)
        mins = int(mins)

        if ampm == "AM" and hours == 12:
            hours = 0
        
        elif ampm == "PM" and hours < 12:
            hours += 12
        
        # Create a new datetime object with the time in 24-hour format
        time_24_hour = datetime.time(hours, mins)
        return time_24_hour

    def create_time(self, hours, mins, ampm):
        hours = int(hours)
        mins = int(mins)

        if ampm == "AM" and hours == 12:
            hours = 0
        
        elif ampm == "PM" and hours < 12:
            hours += 12
        
        # Create a new datetime object with the time in 24-hour format
        time_24_hour = datetime.time(hours, mins)
        return time_24_hour

    async def answer(self):
        self.output = ""
        self.image = ""
        self.weather = []
        self.new_weather = []
        self.loading_screen = True

        
        start = self.create_time(self.time_period1_hours, self.time_period1_mins, self.time_period1_ampm)
        end = self.create_time(self.time_period2_hours, self.time_period2_mins, self.time_period2_ampm)
        self.weather, self.output, self.image = await LLM.run_entire_llm(self.location, start, end, self.activity, self.clothes_preference)
        self.weatherDisplayCreateRows()
        self.loading_screen = False
        
    def weatherDisplayCreateRows(self):
      rv = []
      data = self.weather
      for item in data:
          temp = []
          AMPM = "AM"
          hour = int(item[0][0:2])
          min = item[0][3:5]
          if hour > 12:
              AMPM = "PM"
              hour -= 12
          if hour == 12:
              AMPM = "PM"
          if hour == 0:
              hour = 12
          new_time = f"{hour}:{min} {AMPM}"
          temp.append(new_time)
          temp.append(item[1][1])
          temp.append(item[1][3])
          rv.append(tuple(temp))
      self.new_weather = rv
      
    def load(self):
        self.output = ""
        self.image = ""
        self.weather = []
        self.new_weather = []
        self.loading_screen = True

```

### CalHacks10/LLMClass.py

```python
import python_weather
import together
import base64
import requests
import io
from PIL import Image                                                                                                                                                                                                                                      
import datetime

class LLM():
    f = open("CalHacks10/api_key.txt", "r").read()
    together.api_key = f

    async def getweather(city, start_hr, end_hr):
        # declare the client. the measuring unit used defaults to the metric system (celcius, km/h, etc.)
        async with python_weather.Client(unit=python_weather.IMPERIAL) as client:
            # fetch a weather forecast from a city
            weather = await client.get(city)    
            # get the weather forecast for a few days
            forecast = next(weather.forecasts)
            
            # hourly forecasts
            elapsed = {}
            for hourly in forecast.hourly:
                if hourly.time.hour >= start_hr.hour - 3 and hourly.time.hour <= end_hr.hour + 3:
                    elapsed[str(hourly.time)] = ("Temperature:", hourly.temperature, "Weather description:", hourly.description, str(hourly.kind))
        
        return elapsed

    def to_prompt(weather_dict, location, activity_description, clothes, start_hr, end_hr):
        return f'''You will be giving a detailed suggestion of clothes. These are the inputs that I put:
                    Location: {location}
                    Time period spent outside: {str(start_hr)} to  {str(end_hr)}
                    The weather at each hour is this: {weather_dict}
                    I plan to do this: {activity_description}
                    And may wear these clothes: {clothes}
                    Now, give a detailed suggestion of clothes (including color combinations) that fit the weather.
                    Format your answer as a couple of sentences. DO NOT ONLY USE THE CLOTHES GIVEN.
                    '''

    def llm_text(wd, location, start_hr, end_hr, activity_description, clothes):
        p = LLM.to_prompt(wd, location, activity_description, clothes, start_hr, end_hr)
        output = together.Complete.create(
            prompt = p, 
            model = "lmsys/vicuna-7b-v1.5", 
            max_tokens = 512,
            temperature = 0.3,
            top_k = 60,
            top_p = 0.6,
            repetition_penalty = 1.1,
            stop = ['<human>', '\n\n']
        )
        return output['output']['choices'][0]['text']

    async def run_entire_llm(location, start_hr, end_hr, activity_description, clothes):
        url = "https://api.together.xyz/inference"                                                                                                                                                                                                           
        model = "stabilityai/stable-diffusion-xl-base-1.0"
        wd = await LLM.getweather(location, start_hr, end_hr)
        prompt = LLM.llm_text(wd, location, start_hr, end_hr, activity_description, clothes)
                                                                                                                                                                                                                                                            
        payload = {                                                                                                                                                                                                                                          
            "model": model,                                                                                                                                                                                                                                  
             "prompt": prompt,                                                                                                                                                                                                                                
             "results": 2,                                                                                                                                                                                                                               
             "width": 1024,
             "height": 1024,
             "steps": 20,
             "seed": 42,
         }                                                                                                                                                                                                                                                    
        headers = {                                                                                                                                                                                                                                          
             "accept": "application/json",                                                                                                                                                                                                                    
             "content-type": "application/json",                                                                                                                                                                                                              
             "Authorization": f"Bearer {LLM.f}"                                                                                                                                                                                     
        }                                                                                                                                                                                                                                    
        response = requests.post(
[truncated — 885 more characters]
```

[2 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]