Skip to content
 
 

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FitFindr

FitFindr is a multi-tool AI agent that helps users search for secondhand clothing items, style them with an existing wardrobe, and generate a short shareable outfit caption.

The project demonstrates how an agent can use multiple tools, maintain state across tool calls, handle errors, and adapt when a tool returns no useful result.


Project Overview

A user enters a natural language request such as:

vintage graphic tee under $30

FitFindr then:

  1. Searches a mock secondhand listings dataset.
  2. Selects the best matching item.
  3. Suggests an outfit using the user's wardrobe.
  4. Creates a short social media-style fit card.

The agent does not simply call every tool no matter what happens. It checks the result of each step before deciding whether to continue.


Setup

Install dependencies:

pip install -r requirements.txt

Create a .env file in the project root:

GROQ_API_KEY=your_key_here

Run the app:

python app.py

Then open the local URL shown in the terminal, usually:

http://127.0.0.1:7860

File Structure

OutfitAgent/
├── app.py
├── agent.py
├── tools.py
├── data/
│   ├── listings.json
│   └── wardrobe_schema.json
├── utils/
│   └── data_loader.py
├── tests/
│   ├── conftest.py
│   └── test_tools.py
├── planning.md
├── requirements.txt
└── README.md

Tool Inventory

Tool 1: search_listings(description, size, max_price)

Purpose: Searches the mock secondhand listings dataset for matching items.

Inputs:

  • description (str): Search keywords, such as "vintage graphic tee".
  • size (str | None): Optional size filter. If None, size filtering is skipped.
  • max_price (float | None): Optional maximum price. If None, price filtering is skipped.

Output:

Returns a list[dict] of matching listings sorted by relevance.

Each listing dictionary includes:

  • id
  • title
  • description
  • category
  • style_tags
  • size
  • condition
  • price
  • colors
  • brand
  • platform

Failure handling:

If no listings match, the tool returns an empty list []. It does not crash.


Tool 2: suggest_outfit(new_item, wardrobe)

Purpose: Suggests one or two complete outfit ideas using the selected thrifted item and the user's wardrobe.

Inputs:

  • new_item (dict): The selected listing from search_listings.
  • wardrobe (dict): The user's wardrobe dictionary with an "items" list.

Output:

Returns a non-empty string containing outfit suggestions.

Failure handling:

If the wardrobe is empty, the tool asks the LLM for general styling advice instead of crashing or returning nothing.


Tool 3: create_fit_card(outfit, new_item)

Purpose: Creates a short, casual caption for the complete outfit.

Inputs:

  • outfit (str): The outfit suggestion from suggest_outfit.
  • new_item (dict): The selected listing.

Output:

Returns a 2–4 sentence caption that mentions the item, price, platform, and outfit vibe.

Failure handling:

If outfit is empty or missing, the tool returns a descriptive error message string instead of raising an exception.


Stretch Feature: Retry Logic with Fallback

I added retry logic with fallback.

If the first search returns no results and the user provided a size filter, the agent retries the search without the size filter. If the fallback search returns results, the agent continues with the best fallback item and tells the user that the size filter was removed.

Example:

graphic tee size M under $30

If no exact size M graphic tee is found, the agent retries:

search_listings("graphic tee", None, 30.0)

If fallback results exist, the UI displays a message like:

No results found for size M. Retried without the size filter.

If the fallback also fails, the agent returns an error message.


Planning Loop Explanation

The main planning loop is implemented in run_agent() inside agent.py.

The loop works like this:

  1. Create a new session dictionary.
  2. Parse the user query into description, size, and max_price.
  3. Call search_listings.
  4. If no results are found and size was provided, retry without the size filter.
  5. If still no results are found, set session["error"] and return early.
  6. Select the top search result.
  7. Store the selected item in the session.
  8. Call suggest_outfit.
  9. Store the outfit suggestion in the session.
  10. Call create_fit_card.
  11. Store the fit card in the session.
  12. Return the completed session.

The planning loop changes behavior depending on what the tools return. It does not continue to outfit generation if no listing is found.


State Management

The agent uses a session dictionary to store all data from one interaction.

Example session structure:

{
    "query": query,
    "parsed": {},
    "search_results": [],
    "selected_item": None,
    "wardrobe": wardrobe,
    "outfit_suggestion": None,
    "fit_card": None,
    "error": None,
    "fallback_message": None
}

The session allows information from one tool to flow into the next:

search_listings
    ↓
selected_item
    ↓
suggest_outfit
    ↓
outfit_suggestion
    ↓
create_fit_card
    ↓
fit_card

This means the user does not need to repeat information between steps.


Error Handling

search_listings

Failure mode: no listings match.

Response: the agent retries without size if size was provided. If there are still no results, the agent returns:

No matching listings found. Try broadening your search or increasing your budget.

suggest_outfit

Failure mode: wardrobe is empty.

Response: the tool still returns general styling advice for the selected item.

create_fit_card

Failure mode: outfit input is empty.

Response: the tool returns:

Could not create a fit card because no outfit suggestion was provided.

Testing

Tests are written with pytest in tests/test_tools.py.

Run tests with:

pytest tests/test_tools.py -v

The tests check:

  • search_listings returns results for valid queries.
  • search_listings returns [] for impossible queries.
  • Price filtering works.
  • Size filtering works.
  • suggest_outfit returns a string with the example wardrobe.
  • suggest_outfit returns a string with an empty wardrobe.
  • create_fit_card returns a caption for valid input.
  • create_fit_card handles an empty outfit string.

All tests passed before connecting the tools into the full agent loop.


Example Successful Interaction

User query:

vintage graphic tee under $30

The agent parses:

{
    "description": "vintage graphic tee",
    "size": None,
    "max_price": 30.0
}

The agent calls:

search_listings("vintage graphic tee", None, 30.0)

A top listing is selected, such as:

Y2K Baby Tee — Butterfly Print

Then the agent calls:

suggest_outfit(selected_item, wardrobe)

The outfit suggestion might pair the tee with baggy jeans, chunky sneakers, and a denim jacket.

Then the agent calls:

create_fit_card(outfit_suggestion, selected_item)

The final output includes:

  1. The selected listing.
  2. The outfit idea.
  3. The fit card caption.

Example No-Results Interaction

User query:

designer ballgown size XXS under $5

The agent searches the dataset and finds no matching listings. It may retry without the size filter, but if no listings match after fallback, it returns:

No matching listings found. Try broadening your search or increasing your budget.

The agent does not call suggest_outfit or create_fit_card when no item is found.


AI Usage

I used ChatGPT to help with implementation and documentation.

Example 1: Tool implementation

I gave ChatGPT the starter code for tools.py, including the function signatures and TODO comments. I asked for an implementation of search_listings that used load_listings(), filtered by size and price, scored keyword matches, and returned an empty list when no results were found.

Before using the code, I checked that it matched the assignment requirements and then verified it with pytest tests.

Example 2: Planning loop implementation

I gave ChatGPT the agent.py starter code, the session dictionary structure, and my planning loop description. I asked for a run_agent() implementation that parsed the query, called each tool in order, stored results in session state, and returned early if search failed.

I reviewed the code to make sure the agent did not call suggest_outfit or create_fit_card when search_listings returned no results.

Example 3: Documentation

I used ChatGPT to help turn my implementation details into clear planning.md and README.md sections. I reviewed and edited the content so it matched my actual code and testing results.


Demo Video Checklist

The demo video shows:

  • A complete successful interaction using all three tools.
  • The selected listing flowing into the outfit suggestion.
  • The outfit suggestion flowing into the fit card.
  • A no-results or fallback scenario.
  • The app running through the Gradio interface.

Reflection

This project helped me understand that an AI agent is more than a single LLM call. The important part is the planning loop: deciding what tool to call, checking the result, storing state, and deciding whether to continue or stop.

Testing each tool individually made the full agent easier to debug. Once the tools passed pytest, most remaining issues were in the planning loop or UI formatting instead of the tool logic itself.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages