# Project export: Mood Food

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 12.0
- Tagline: Mood Food is an AI-powered Chrome extension that connects your emotions to what you eat. With a single click, it reads your mood and recommends personalized meals to match.
- Devpost: https://devpost.com/software/mood-food-jatpy1
- GitHub: https://github.com/Orca6722/CalHacks12-DevSnacks/
- Video: https://www.youtube.com/embed/Wu1rbKrexag?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 1 GitHub contributor(s) — Michael Bar On (2 commits)

## Devpost submission (written by the team)

No Devpost description available.

## README (from the GitHub repository)

No README available.

## Detected evidence (automated analysis)

Indexed codebase: 15 recognized source files, 20 KB.
- FastAPI (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — claimed on Devpost, not found in the code
- Tailwind CSS (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (18 of 18)

```
.gitignore
backend/agents/commit_agent.py
backend/agents/mood_agent.py
backend/agents/order_agent.py
backend/agents/reco_agent.py
backend/api/server.py
backend/data/menus.csv
backend/README.md
backend/requirements.txt
backend/run_agents.py
backend/scripts/seed_menus.py
backend/shared/chroma_client.py
backend/shared/chroma_init.py
backend/shared/doordash.py
backend/shared/git_utils.py
backend/shared/groq_client.py
backend/shared/models.py
backend/shared/settings.py
```

### Dependencies

- backend/requirements.txt: chromadb@>=0.5.5, fastapi@>=0.115.0, groq@>=0.9.0, numpy@>=1.26.0, pandas@>=2.2.2, pydantic@>=2.9.0, pyjwt@>=2.9.0, python-dotenv@>=1.0.1, requests@>=2.32.3, sentence-transformers@>=3.0.1, uagents@>=0.12.0, uvicorn@>=0.30.0

### Recent commits (newest first)

- Starter Code
- Initial commit

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

### backend/requirements.txt

```
uagents>=0.12.0
fastapi>=0.115.0
uvicorn>=0.30.0
pydantic>=2.9.0
python-dotenv>=1.0.1
chromadb>=0.5.5
sentence-transformers>=3.0.1
numpy>=1.26.0
pandas>=2.2.2
groq>=0.9.0
pyjwt>=2.9.0
requests>=2.32.3
```

### backend/api/server.py

```python
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import json
import os
from ..shared.settings import LAST_RECO_PATH, USER_ID
from ..shared.chroma_client import add_feedback
from ..shared.doordash import build_body_from_place_order, create_delivery, get_delivery

app = FastAPI(title="DevSnacksAI API")

@app.get("/api/recommendations")
async def get_recommendations():
    if not os.path.exists(LAST_RECO_PATH):
        return {"items": []}
    with open(LAST_RECO_PATH, "r", encoding="utf-8") as f:
        data = json.load(f)
    return data

class FeedbackIn(BaseModel):
    item_id: str
    liked: bool
    notes: str | None = None

@app.post("/api/feedback")
async def post_feedback(body: FeedbackIn):
    add_feedback(user_id=USER_ID, item_id=body.item_id, liked=body.liked, notes=body.notes)
    return {"ok": True}

class OrderIn(BaseModel):
    item_id: str
    item_name: str
    dropoff_address: str
    dropoff_phone_number: str
    dropoff_contact_given_name: str | None = None
    dropoff_contact_family_name: str | None = None
    tip_cents: int | None = 0

@app.post("/api/order")
async def post_order(body: OrderIn):
    try:
        dd_body = build_body_from_place_order(body.model_dump())
        res = create_delivery(dd_body)
        return {"ok": True, "external_delivery_id": res.get("external_delivery_id"), "raw": res}
    except Exception as e:
        raise HTTPException(400, f"DoorDash error: {e}")

@app.get("/api/order/{external_delivery_id}")
async def order_status(external_delivery_id: str):
    try:
        res = get_delivery(external_delivery_id)
        return res
    except Exception as e:
        raise HTTPException(400, f"DoorDash error: {e}")
```

### backend/run_agents.py

```python
from uagents import Bureau
from .agents.commit_agent import commit_agent
from .agents.mood_agent import mood_agent
from .agents.reco_agent import reco_agent
from .agents.order_agent import order_agent
from .shared.settings import COMMIT_POLL_SECONDS

# Wire addresses for message routing via agent storage
@commit_agent.on_startup
async def _wire_commit(ctx):
    ctx.storage.set("mood_addr", mood_agent.address)

@mood_agent.on_startup
async def _wire_mood(ctx):
    ctx.storage.set("reco_addr", reco_agent.address)

# Adjust commit polling interval dynamically (optional)
commit_agent._intervals.clear()
commit_agent.interval(COMMIT_POLL_SECONDS)(lambda ctx: None)  # hack to set interval value

if __name__ == "__main__":
    bureau = Bureau()
    bureau.add(commit_agent)
    bureau.add(mood_agent)
    bureau.add(reco_agent)
    bureau.add(order_agent)
    print("Running uAgents Bureau...\n",
          f"commit_agent: {commit_agent.address}\n",
          f"mood_agent:   {mood_agent.address}\n",
          f"reco_agent:   {reco_agent.address}\n",
          f"order_agent:  {order_agent.address}")
    bureau.run()
```

### backend/shared/chroma_init.py

```python
import csv
import os
from .chroma_client import ensure_collections, add_menus
from .settings import DATA_DIR


def seed_from_csv():
    ensure_collections()
    path = os.path.join(DATA_DIR, "menus.csv")
    rows = []
    with open(path, newline='', encoding='utf-8') as f:
        r = csv.DictReader(f)
        for row in r:
            rows.append(row)
    add_menus(rows)
    print(f"Seeded {len(rows)} menu items into ChromaDB")


if __name__ == "__main__":
    seed_from_csv()
```

### backend/agents/commit_agent.py

```python
from uagents import Agent, Context
from ..shared.models import CommitBatch
from ..shared.git_utils import read_local_commits

commit_agent = Agent(name="commit_agent")

@commit_agent.on_interval(period=60.0)
async def poll_repo(ctx: Context):
    commits = read_local_commits()
    batch = CommitBatch(repo="local", commits=commits)
    # Send to MoodAgent (address injected by run_agents)
    target = ctx.storage.get("mood_addr")
    if target:
        await ctx.send(target, batch)
        ctx.logger.info(f"CommitAgent: sent {len(commits)} commits → MoodAgent")
```

### backend/scripts/seed_menus.py

```python
import argparse
import shutil
from backend.shared.chroma_client import get_collection, _client
from backend.shared.chroma_init import seed_from_csv

if __name__ == "__main__":
    ap = argparse.ArgumentParser()
    ap.add_argument("--reset", action="store_true", help="reset collections before seeding")
    args = ap.parse_args()

    if args.reset:
        try:
            for name in ("menus", "prefs", "commits"):
                _client.delete_collection(name)
            print("Collections reset.")
        except Exception:
            pass
    seed_from_csv()
```

### backend/shared/git_utils.py

```python
import subprocess
from .settings import COMMIT_MAX


def read_local_commits(max_count: int | None = None) -> list[str]:
    """Reads commit subjects from the current git repository."""
    n = max_count or COMMIT_MAX
    try:
        out = subprocess.check_output([
            "git", "log", f"-n", str(n), "--pretty=%s"
        ], text=True)
        commits = [line.strip() for line in out.splitlines() if line.strip()]
        return commits
    except Exception:
        return [
            "feat: initial commit",
            "fix: finally squashed heisenbug 😮‍💨",
            "chore: refactor cache and add tests",
            "wip: not sure why this crashes at 2am"
        ]
```

### backend/shared/settings.py

```python
import os
from dotenv import load_dotenv

load_dotenv()

GROQ_API_KEY = os.getenv("GROQ_API_KEY", "")
CHROMA_DB_DIR = os.getenv("CHROMA_DB_DIR", "./.chroma")
USER_ID = os.getenv("USER_ID", "demo")
COMMIT_POLL_SECONDS = int(os.getenv("COMMIT_POLL_SECONDS", "60"))
COMMIT_MAX = int(os.getenv("COMMIT_MAX", "40"))
DATA_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "data"))
LAST_RECO_PATH = os.path.join(DATA_DIR, "last_reco.json")

# DoorDash
DOORDASH_DEVELOPER_ID = os.getenv("DOORDASH_DEVELOPER_ID", "")
DOORDASH_KEY_ID = os.getenv("DOORDASH_KEY_ID", "")
DOORDASH_SIGNING_SECRET = os.getenv("DOORDASH_SIGNING_SECRET", "")  # base64url string from portal
DOORDASH_BASE = os.getenv("DOORDASH_BASE", "https://openapi.doordash.com")
```

### backend/agents/order_agent.py

```python
from uagents import Agent, Context
from ..shared.models import PlaceOrder, DeliveryResponse
from ..shared.doordash import build_body_from_place_order, create_delivery

order_agent = Agent(name="order_agent")

@order_agent.on_message(PlaceOrder)
async def place(ctx: Context, msg: PlaceOrder):
    try:
        body = build_body_from_place_order(msg.model_dump())
        res = create_delivery(body)
        out = DeliveryResponse(
            external_delivery_id=res.get("external_delivery_id", body["external_delivery_id"]),
            status=res.get("status", "created"),
            eta=res.get("dropoff_time_estimated_seconds"),
            tracking_url=res.get("tracking_url"),
            raw=res,
        )
        ctx.logger.info(f"OrderAgent: Created delivery {out.external_delivery_id}")
    except Exception as e:
        ctx.logger.error(f"OrderAgent error: {e}")
```

### backend/agents/mood_agent.py

```python
from uagents import Agent, Context
from ..shared.models import CommitBatch, MoodSignal
from ..shared.groq_client import extract_mood_json

mood_agent = Agent(name="mood_agent")

@mood_agent.on_message(CommitBatch)
async def to_mood(ctx: Context, msg: CommitBatch):
    try:
        mood = extract_mood_json(msg.commits)
    except Exception as e:
        ctx.logger.error(f"Groq error: {e}")
        mood = {
            "mood": "neutral", "energy": "med", "valence": "neutral",
            "diet": "light", "budget": 12, "spice": "low", "time": "evening",
            "query": "light, quick, under $12"
        }
    signal = MoodSignal(
        mood=mood["mood"], energy=mood["energy"], valence=mood["valence"],
        budget=float(mood["budget"]), diet=mood["diet"], spice=mood["spice"], time=mood["time"],
        query=mood["query"]
    )
    target = ctx.storage.get("reco_addr")
    if target:
        await ctx.send(target, signal)
        ctx.logger.info(f"MoodAgent: sent MoodSignal → RecoAgent :: {signal.query}")
```

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