Project Info
Here’s a cleaner, more human-sounding hackathon writeup with markdown formatting, fewer “AI-polished” phrases, and no em dashes.
Inspiration
Most Berkeley freshmen show up in the same spot. They are living on their own for the first time, eating most of their meals on a dining plan, and walking past a free gym on the way to class. It is a great time to build healthy habits, but it is also really easy to drift. Some people gain weight without noticing. Some get run down and sick. Some lose weight without meaning to. The hard part is not logging food. Plenty of apps already do that. The hard part is making a decision in the moment. You are standing in Crossroads with a tray. You want to eat better, get a little leaner, put on muscle, or just feel better, but you do not know which of the twenty things in front of you actually helps with that. Normal nutrition apps cannot help because they do not know what Berkeley is serving today. That gap between a health goal and the actual dining hall tray is what BearFuel is built for.
What it does
BearFuel helps Berkeley students decide what to eat from the dining halls based on their personal goals. A student enters basic information like height, weight, age, activity level, goal, number of meals per day, dietary restrictions, and allergens. BearFuel then: pulls the current UC Berkeley dining hall menu uses the real nutrition facts for each dish calculates personal calorie and protein targets builds a meal-by-meal plan using only dishes being served that day respects dietary restrictions and allergens gives macro totals and a short reason for each meal The goal is simple: answer the question, “What should I put on my tray?” Students can also browse the full menu, see nutrition for every item, and open a dish detail page for more information. BearFuel is not trying to be another tracker. It is meant to give students a useful answer before they eat.
How we built it
BearFuel has three main parts: SwiftUI iOS app The app is built with SwiftUI using an MVVM structure for iOS 17. It includes onboarding, a Today screen with macro rings, a searchable menu browser, and dish detail pages. We used a Berkeley blue and gold visual style and included dark mode support. Python and FastAPI backend The backend scrapes and normalizes Berkeley dining hall menus, stores cached menu data by date, calls Claude, and returns clean JSON to the app. The Anthropic API key stays on the server and never reaches the client. Claude as the meal planning engine Claude takes the available dishes and builds a plan that fits the user’s goal. We use structured JSON output so the backend can validate the result instead of trusting freeform text. We split the responsibilities carefully. Python handles the safety-critical math and guardrails. Claude handles the flexible part: choosing real dishes from the menu and putting them together into meals. For calorie targets, we use the Mifflin-St Jeor equation: From there, the backend applies the user’s goal, calorie floors, and deficit caps. Those rules are enforced in code, not left to the model. Claude only selects meals after the targets and constraints have already been calculated. The backend then checks the model’s totals and can retry or fall back if the plan does not meet the requirements.
Challenges we ran into
The biggest challenge was getting real nutrition data. At first, the public Berkeley dining page looked like it only showed dish names, allergens, dietary tags, and carbon ratings. We could not find calories, protein, carbs, or fat anywhere on the main page. For a while, we thought we would have to estimate nutrition with AI, which would have made the app much less trustworthy. Then we noticed that clicking on a dish opens a popup with full nutrition facts. It had calories, protein, carbs, fat, sodium, fiber, and other USDA-style information. That was exactly what we needed. The next challenge was figuring out where the popup data came from. We expected an AJAX request or a hidden WordPress endpoint. We opened the Network tab, clicked a dish, and saw nothing. No request at all. That ended up being the key insight. The nutrition data was already inside the page HTML when it loaded. The site was just hiding and showing it with JavaScript. That changed our whole approach. We did not need to reverse engineer an API. We needed to scrape the raw HTML without cleaning away the hidden blocks. This also led to one of the funniest bugs of the project. The nutrition section used a misspelled CSS class: nutration-details. Our selector kept failing because we were spelling “nutrition” correctly. Other challenges included: making sure macro totals were accurate validating Claude’s arithmetic in code re-prompting when the model returned an invalid plan building calorie guardrails that avoid unsafe recommendations writing the app in a way that feels supportive instead of guilt-based Accomplishments we are proud of Real nutrition data BearFuel does not guess macros. Every calorie and protein number comes from Berkeley’s own dining data. That makes the recommendations much more defensible. Useful AI with real constraints Claude is not just writing a paragraph of advice. It is choosing from the actual dishes available today and building a plan around hard nutrition targets, dietary restrictions, and allergens. Safety built in from the start The backend enforces calorie floors, deficit caps, and goal limits. The app also uses supportive language and points students toward campus nutrition resources when appropriate. A demo that can survive spotty data Menus are cached by date, and the app has graceful fallbacks. The demo does not depend on the dining site working perfectly at that exact moment.
What we learned
We learned not to assume an API is missing just because it is not obvious. The data we needed was already on the page the whole time. We just had to inspect the raw HTML closely enough to find it. We also learned that AI works best when it is given a narrow job. In BearFuel, code handles the math, safety rules, validation, and fallback behavior. Claude handles the part that is naturally fuzzy: turning a list of available dishes into a reasonable meal plan. Structured outputs made a huge difference. Instead of treating the model response like a finished answer, we treat it like data that must pass checks before the user sees it. We also learned that anything involving weight, food, and body composition needs careful wording. Small product choices matter. BearFuel is designed to help students build habits, not make them feel judged. What is next Weekly planning Students should be able to plan across multiple days instead of only seeing today’s menu. Swap a dish If a student does not want one item, BearFuel should be able to replace it while keeping the meal close to the same targets. Apple Health integration Activity data could help targets adjust based on how much the student actually moved, trained, or lifted that day. Dining hall notifications BearFuel could send reminders around dining hall hours so students do not have to remember to check the app. More campuses Berkeley’s menu system is used in other places too, so the same approach could work at more universities with relatively small changes. Habit tracking without guilt Over time, students could see patterns in their eating habits without turning the app into a strict calorie tracker or a guilt machine.
BearFuel — Berkeley AI Hackathon 2026
The wedge: UC Berkeley freshmen eat on a meal plan, have free gym access, and don't cook. The single highest-leverage nutrition intervention is "tell me what to put on my tray at Crossroads tonight." Generic meal-plan apps can't do this. BearFuel can.
What it does
BearFuel takes a student's stats and body-composition goal, pulls today's real Berkeley dining-hall menu (with actual USDA-derived nutrition per dish), and uses Claude to assemble a personalized meal plan from dishes that are actually being served — hitting their calorie and protein targets.
The data insight (key technical story)
Berkeley dining embeds full USDA-derived nutrition facts for every dish in their menu system. The data-location attributes in the page HTML are base64-encoded paths to public XML files:
https://dining.berkeley.edu/wp-content/uploads/menus-exportimport/{Location}_{YYYYMMDD}.xml
Each XML is EatecExchange format with structured nutrition for every recipe (kcal, protein, carb, fat, fiber, sugar, sodium, …). One HTTP request per location per day gives every dish with complete USDA-derived nutrition — no AJAX, no per-item API calls, no estimation.
Verified against the Berkeley site: Cinnamon Raisin Bagels → 250.83 kcal, 8.05g protein, 50.14g carb, 1.34g fat ✓
Architecture
┌──────────────────┐ HTTPS/JSON ┌─────────────────────────┐ Anthropic API
│ SwiftUI iOS app │ <───────────────> │ FastAPI backend │ <──────────────> Claude
│ (the client) │ │ scraper + planner + AI │
└──────────────────┘ └─────────────────────────┘
- API key lives only in the backend. The iOS app never sees it.
- Cache-first: scraper writes
cache/menu_{date}.json; endpoints read from cache. No live scrape at request time. - Demo-safe:
DEMO_MODE=trueservescache/menu_demo.json(committed seed). iOS app bundlesdemo_plan.jsonas fallback if backend is unreachable.
Running locally
Backend
cd backend
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
# Copy and fill in your API key
cp .env.example .env
# Edit .env: ANTHROPIC_API_KEY=sk-ant-...
# Option A: demo mode (no live scrape)
DEMO_MODE=true uvicorn main:app --reload --port 8002
# Option B: live mode (scrapes today's menu on first /menu request)
uvicorn main:app --reload --port 8002
# Or pre-seed the cache manually
python seed_cache.py # copies demo seed to today's date
python scraper.py # scrapes live Berkeley data for today
Verify:
curl http://localhost:8002/health
curl "http://localhost:8002/menu?date=today&location=Crossroads" | python3 -m json.tool | head -30
curl -X POST http://localhost:8002/plan \
-H 'Content-Type: application/json' \
-d '{"profile":{"height_cm":178,"weight_kg":75,"age":19,"sex":"male","activity_level":"moderate","goal_type":"lean_gain","meals_per_day":3,"diet_restrictions":[],"exclude_allergens":[],"preferred_locations":["Crossroads"]},"date":"today"}'
iOS
cd ios
# Install XcodeGen if needed: brew install xcodegen
xcodegen generate # produces BearFuel.xcodeproj
open BearFuel.xcodeproj
- Set
Config.swiftbaseURLto your backend URL - Build and run on Simulator (iOS 17+)
- The app loads
demo_plan.jsonfrom the bundle if the backend is unreachable
Health & safety guardrails
All enforced in backend/planner.py — never delegated to the model:
| Guardrail | Implementation |
|---|---|
| Calorie floor | ≥1500 kcal/day (men), ≥1200 (women); clamped with user-visible note |
| Deficit cap | Max −20% below TDEE |
| Surplus cap | Max +10% above TDEE |
| Low BMI guard | BMI < 17.5 → switch to maintenance + link to Berkeley dietitian |
| Input validation | Height/weight/age range checks via Pydantic |
| Framing | Language is fueling/energy/habits — no weight loss imagery |
| Disclaimer | Always shown on every plan |
Sponsor context (Anthropic / Claude)
Claude is the headline capability: given 200+ real Berkeley dining-hall dishes with USDA nutrition, it composes a day's meals that hit a personalized calorie/protein target under dietary, allergen, and location constraints — something no static macro calculator can do. The server recomputes all totals from real nutrition data after Claude selects dishes, so the numbers are always accurate.
Model: claude-sonnet-4-6 (configurable via ANTHROPIC_MODEL in .env).
Analysis
View
Metric
- 4
- 4
Figures cover GitHub contributors during the hackathon window. A co-authored commit counts in full for each author, so per-member totals add up to more than the whole-team figures.
Technology
- AnthropicIn code
- FastAPIIn code
- PythonIn code
- SwiftIn code
4 of 4 appear in the indexed code.
AI coding agents
No AI coding agent signals were found in this repository.
Detected from committed agent config files and commit authorship. Absence of a signal is not proof an agent was unused.
Codebase size
Source size
195 KB
Source files
22
Counts recognized source files only; vendored directories, binaries and lockfiles are excluded, so this is smaller than the repository on disk.
Repository
yukai0/AIhackathon2026
30 files · 393 KB · @ 717a188
Structure
Interface
8 files · 27%Screens, components and styles rendered to the user.
Application logic
15 files · 50%Domain rules, services and shared utilities.
Data & schema
1 file · 3%Schema definitions, migrations and data access.
Supporting
Layers are inferred from where files sit in the tree, not from reading the code. A project that names its directories unconventionally will read oddly here — open the file browser to check anything the diagram implies.
Languages
- Swift72%
- Python25%
- Markdown2%
- YAML0%
Share of indexed source by file size. Binary and vendored files are excluded.
Dependencies
backend/requirements.txt
pypi · 9- anthropic
- beautifulsoup4
- fastapi
- httpx
- lxml
- pydantic
- python-dotenv
- requests
- uvicorn[standard]
Declared in the repository’s manifests at the indexed commit. A declared package is not proof it is used, and runtime dependencies are listed first.
Feature verification
Anthropic API key stays server-side, never reaches clientVerified
The Anthropic API key stays on the server and never reaches the client
Claimed on Devposthigh confidencebackend/planner.py:67— ANTHROPIC_API_KEY read from server env via os.environ, never exposed in models/responsesios/BearFuel/Config.swift:5— iOS Config only holds baseURL, no API key present client-side
Backend checks model totals, retries/falls back if plan invalidVerified
The backend then checks the model's totals and can retry or fall back if the plan does not meet the requirements
Claimed on Devposthigh confidencebackend/planner.py:744— for attempt in range(2) retries Claude call; on exhaustion falls back to _greedy_fallback deterministic planbackend/planner.py:442— _plan_is_good_enough checks day totals against target tolerance bands
Berkeley dining menu scraping (data-location base64 -> XML)Verified
Pulls the current UC Berkeley dining hall menu via base64-encoded data-location attributes decoding to EatecExchange XML files
Claimed on readmehigh confidencebackend/scraper.py:91— _discover_xml_urls fetches /menus/ page, regexes data-location base64 attrs, decodes to XML pathsbackend/scraper.py:250— _parse_xml parses EatecExchange XML root/menu/recipes/recipe elements
Cache-first architecture, no live scrape at request time; demo-safe DEMO_MODE with iOS bundled fallbackVerified
Cache-first: scraper writes cache/menu_{date}.json; endpoints read from cache. Demo-safe: DEMO_MODE=true serves cache/menu_demo.json; iOS app bundles demo_plan.json as fallback
Claimed on readmehigh confidencebackend/main.py:32— _load_menu_cache reads from cache file, DEMO_MODE serves DEMO_CACHE_FILEbackend/cache/menu_demo.json— Committed demo seed file existsios/BearFuel/Views/Today/TodayViewModel.swift:198— loadFallbackPlan decodes bundled demo_plan.json when backend unreachableios/BearFuel/Resources/demo_plan.json— Bundled demo plan resource exists
Calorie floors, deficit caps enforced in code (not left to model)Verified
Python handles the safety-critical math and guardrails... calorie floors, and deficit caps. Those rules are enforced in code, not left to the model
Claimed on Devposthigh confidencebackend/planner.py:28— CALORIE_FLOORS, MAX_DEFICIT_PCT, MAX_SURPLUS_PCT constantsbackend/planner.py:148— compute_targets clamps adj to deficit/surplus caps and calorie floor before Claude ever sees targets
Claude as meal planning engine with structured JSON outputVerified
Claude takes the available dishes and builds a plan... We use structured JSON output so the backend can validate the result instead of trusting freeform text
Claimed on Devposthigh confidencebackend/planner.py:512— Prompt requests strict JSON schema responsebackend/planner.py:639— _parse_claude_response parses/validates JSON, checks item IDs, recomputes totals from real nutrition data rather than trusting model
FastAPI backend scrapes/normalizes menus, caches by date, calls Claude, returns JSONVerified
The backend scrapes and normalizes Berkeley dining hall menus, stores cached menu data by date, calls Claude, and returns clean JSON to the app
Claimed on Devposthigh confidencebackend/main.py:22— FastAPI app with /menu, /plan, /scrape endpointsbackend/scraper.py:301— scrape_menu writes cache/menu_{date}.jsonbackend/planner.py:746— client.messages.create calls Anthropic Claude
Full menu browsing with nutrition and dish detail pageVerified
Students can browse the full menu, see nutrition for every item, and open a dish detail page
Claimed on Devposthigh confidenceios/BearFuel/Views/Menu/MenuBrowserView.swift:13— Searchable menu list with search text bindingios/BearFuel/Views/Menu/DishDetailView.swift:3— DishDetailView renders nutrition overview/facts/allergen/carbon cards for a given MenuItem
Low BMI guard switching to maintenance + dietitian linkVerified
Low BMI guard: BMI < 17.5 -> switch to maintenance + link to Berkeley dietitian
Claimed on readmehigh confidencebackend/planner.py:88— bmi < LOW_BMI_THRESHOLD and goal_type == cut triggers HealthWarning and switches profile goal_type to maintain, message links to dining.berkeley.edu/dietitian
Macro totals and per-meal rationaleVerified
Gives macro totals and a short reason for each meal
Claimed on Devposthigh confidencebackend/planner.py:665— MealSlot includes totals (kcal/protein/carb/fat) and rationale field parsed from Claude response
Meal-by-meal plan built from today's actual dishesVerified
Builds a meal-by-meal plan using only dishes being served that day
Claimed on Devposthigh confidencebackend/planner.py:697— generate_plan filters items to profile, shortlists candidates, and builds MealSlot list via Claude or greedy fallback
Model claude-sonnet-4-6 configurable via ANTHROPIC_MODELVerified
Model: claude-sonnet-4-6 (configurable via ANTHROPIC_MODEL in .env)
Claimed on readmehigh confidencebackend/planner.py:18— MODEL = os.getenv("ANTHROPIC_MODEL", "claude-sonnet-4-6")
Personal calorie/protein target calculation (Mifflin-St Jeor)Verified
Calculates personal calorie and protein targets using the Mifflin-St Jeor equation
Claimed on readmehigh confidencebackend/planner.py:71— compute_bmr implements Mifflin-St Jeor formula by sexbackend/planner.py:176— target_protein computed via g/kg multiplier based on goal type
Real USDA-derived nutrition facts per dishVerified
Uses the real nutrition facts for each dish (kcal, protein, carbs, fat, sodium, fiber, etc.)
Claimed on Devposthigh confidencebackend/scraper.py:172— Parses pipe-delimited nutrients attribute into kcal, fat, sodium, carb, fiber, sugar, protein, cholesterol
Respects dietary restrictions and allergensVerified
Respects dietary restrictions and allergens when building the plan
Claimed on Devposthigh confidencebackend/planner.py:199— _filter_items excludes allergens and enforces _matches_diet_restrictions before candidates reach the planner/AI
SwiftUI iOS app with MVVM, iOS 17Verified
The app is built with SwiftUI using an MVVM structure for iOS 17
Claimed on Devposthigh confidenceios/project.yml:18— deploymentTarget set to 17.0ios/BearFuel/Views/Today/TodayViewModel.swift:1— ViewModel classes (TodayViewModel, MenuViewModel) separate from Views, ObservableObject pattern
Berkeley blue/gold visual style with dark mode supportCode-supported
We used a Berkeley blue and gold visual style and included dark mode support
Claimed on Devpostmedium confidenceios/BearFuel/DesignSystem.swift:5— BerkeleyTheme defines navy/blue/gold palette; UI uses systemBackground/secondarySystemBackground which auto-adapt to dark mode, but no explicit dark-mode-specific styling or preferredColorScheme found
Input validation via Pydantic range checksCode-supported
Input validation | Height/weight/age range checks via Pydantic
Claimed on readmemedium confidencebackend/models.py:1— models.py defines Pydantic models used across backend; range-check field constraints not individually confirmed line-by-line in this triage
Onboarding, Today screen with macro rings, searchable menu browser, dish detail pagesCode-supported
It includes onboarding, a Today screen with macro rings, a searchable menu browser, and dish detail pages
Claimed on Devpostmedium confidenceios/BearFuel/Views/Today/TodayView.swift:126— targetsCard renders GradientProgressRing macro ringsios/BearFuel/Views/Profile/ProfileStore.swift:24— hasCompletedOnboarding computed from profile fields, but there is no dedicated onboarding flow screen; profile entry happens in ProfileView tab, not a first-run wizard
Supportive/non-guilt framing language, always-shown disclaimerCode-supported
Framing: fueling/energy/habits language, no weight loss imagery. Disclaimer always shown on every plan
Claimed on readmemedium confidenceios/BearFuel/Views/Today/TodayView.swift:30— disclaimerBanner rendered whenever plan.disclaimer is non-empty; wording/tone of warnings in planner.py (e.g. 'switched to a maintenance plan') is supportive, but a guaranteed always-shown disclaimer independent of plan state was not confirmed
Swap a dish while keeping targetsCode-supported
What is next: Swap a dish - replace an item while keeping the meal close to the same targets
Claimed on Devpostlow confidenceios/BearFuel/Views/Today/TodayView.swift:519— MealCard/DishRow already support an 'alternatives'/'onSubstitute' UI menu for swapping a dish, which is more built than the README's future-work framing suggests
Apple Health integrationClaimed only
What is next: Apple Health integration for activity data adjusting targets
Claimed on Devposthigh confidenceDining hall notificationsClaimed only
What is next: Dining hall notifications around dining hall hours
Claimed on Devposthigh confidenceHabit tracking without guiltClaimed only
What is next: Habit tracking without guilt, seeing patterns over time
Claimed on Devposthigh confidenceMisspelled 'nutration-details' CSS class discovery bugClaimed only
The nutrition section used a misspelled CSS class: nutration-details... key insight leading to scraping raw HTML
Claimed on Devpostmedium confidenceSupport for more campusesClaimed only
What is next: More campuses using the same menu system approach
Claimed on Devposthigh confidenceWeekly planning across multiple daysClaimed only
What is next: Weekly planning - students should be able to plan across multiple days instead of only today's menu
Claimed on Devposthigh confidence
An AI agent derived these features from the project’s Devpost page and readme, then searched the code for each one. Verified features are backed by cited code; claimed-only features had no supporting code, which is not by itself proof a feature is missing.
Export this project's context (description, README, evidence, key source files) to chat with an AI agent elsewhere.