Inspiration
When I moved from Ghana, I wanted to make garden egg stew and discovered that garden eggs simply don't exist in an American grocery store. Neither does kpakpo shito, dried smoked fish, or palm nut cream.
Like everyone in that position, I learned to substitute. Italian eggplant for garden eggs. Habanero for kpakpo shito. Some swaps worked. Others quietly ruined the dish, nobody tells you that vegetable oil will technically cook your stew but strip away the deep red colour and taste that make it feel like home.
That knowledge exists. It just lives in phone calls home, WhatsApp groups, and years of trial and error. It's impossible to search and easy to lose. I wanted to turn it into something both people and an AI agent could actually reason with.
What it does
You name a dish. Borrowed Pantry finds real grocery stores near you, works out what you can actually buy there, explains what to substitute for the things you can't find and what changes about the dish when you do, keeps you inside your budget, and remembers all of it for next time.
Along the way it remembers your allergies, what you've already cooked, and most importantly, what other people have discovered. A substitution someone shares in one city surfaces inside someone else's shopping plan in another, months later, with credit.
How I built it
- CockroachDB Cloud as the entire memory layer: 13 tables covering conversations, extracted user facts, cooking history with saved baskets, community-contributed substitutions, and a knowledge graph of ingredients and swaps.
- Distributed Vector Indexing with 1024-dimension embeddings on every ingredient and recipe, so semantic search finds "something like garden eggs" without any keyword overlap.
- Amazon Bedrock running Claude Sonnet 4.6 for reasoning, recipe generation, and request routing, plus Titan Text Embeddings V2 for the vectors.
- AWS Lambda hosting a FastAPI backend behind a function URL.
- CockroachDB MCP Server connected to my editor for live schema inspection during development.
- React and Vite on the frontend, deployed to Vercel.
- OpenStreetMap Overpass API for discovering real grocery stores anywhere in the world, which get saved and reused.
Challenges I ran into
The model kept doing arithmetic in prose, and getting it wrong. Early versions had Claude calculating shopping totals inside its explanation, and the numbers drifted between sentences $27 in one paragraph, $29 two lines later. The fix was to compute the entire basket, the budget fit, and what to cut deterministically in Python, then hand Claude the finished numbers as fixed facts it was explicitly instructed not to recalculate.
Allergy matching that looked right and did nothing. I stored dietary restrictions as labels like "Shellfish allergy" and matched them against ingredient names with substring comparison. It never fired once "shellfish allergy" and "crab" share no substring in either direction. It needed a real category-to-ingredient mapping, not a string trick.
A cleanup script that deleted my most valuable data. I wrote a helper to remove a generated recipe, and it treated any ingredient not currently attached to a recipe as safe to delete. It wiped most of my hand-written substitution knowledge, the authentic swaps I'd verified myself. I restored from the seed script, then rewrote every destructive operation to preview before acting.
Deploying to Lambda surfaced problems local development never did. The CockroachDB SSL certificate lives on my machine but not in Lambda's sandbox, so every database call failed with an opaque error until I bundled the certificate into the deployment package. CORS headers were being set twice, once by FastAPI, once by Lambda's function URL config which the browser rejected outright. And generating a brand new dish takes longer than Lambda's default timeout, which surfaces as a 502 that the browser then misreports as a CORS failure.
The MCP server found a bug I couldn't see. When I asked it to describe my
schema for the README, it reported seven vector indexes on ingredients and seven on recipes instead of one each. Repeated migration runs had been silently
stacking up duplicate unnamed indexes for weeks. Every query still worked, so
nothing in the application ever hinted at it.
Accomplishments that I'm proud of
The community memory loop actually closes. A substitution someone shares gets stored in CockroachDB, retrieved by the agent when a different person plans a different meal, and presented inside their shopping list with attribution. It isn't a feature I described in a pitch you can share a tip and watch it surface in someone else's plan.
The agent learns dishes it has never seen. Name any African dish that isn't in the database and it generates the full recipe, ingredient list, substitution knowledge with real explanations, and store availability, then saves all of it permanently. The catalogue grows on its own, and the next person to ask gets an instant answer.
Substitutions explain themselves. No star ratings, no "4/5 match." Every swap comes with a real reason: pumpkin seeds work for egusi because they're also a fatty seed that thickens the same way, but the soup comes out greener and less nutty. Miso for locust beans because both are fermented soybean products. That's the advice you'd get from someone who actually cooks.
It works wherever you are. Store discovery pulls real groceries near any set of coordinates in the world, saves them, estimates what each one stocks based on what kind of store it is, and reuses that knowledge for everyone after.
The budget numbers are actually correct. Totals, budget fitting, and what to cut are computed deterministically in Python rather than narrated by a language model which took two rewrites to get right, and is the difference between a demo that looks good and one that would survive a user checking the maths.
I shipped it. Backend on Lambda, frontend deployed, database in production, and a stranger in another city can open the URL and get real results
What we learned
Deterministic work belongs in code, not in the prompt. The single biggest quality improvement came from taking arithmetic away from the model and giving it firm numbers to reason from instead.
Persistent memory changes what a product can be. Once knowledge outlives a session, one person's discovery becomes everyone's. That's a different thing from a chatbot with a long context window.
Verify, don't assume. More than once I believed something worked because a console said "success" a schema that never actually created its tables, a server quietly serving stale code after a syntax error, a config setting that reverted. Checking the actual state of the actual system caught all of them.
What's next for Borrowed Pantry: AI Community Memory for Cooking
Community pantry sharing: extending the same memory layer from remembering successful substitutions to coordinating ingredient sharing between trusted neighbours, tracking borrowing history and availability.
Built With
- amazon-bedrock
- amazon-titan
- aws-lambda
- claude
- cockroachdb
- fastapi
- javascript
- mcp
- openstreetmap
- postgresql
- psycopg
- python
- react
- vercel
- vite
Log in or sign up for Devpost to join the conversation.