Inspiration
Small independent restaurants keep only about 5 cents of every dollar of sales as pre-tax profit, and wholesale food prices have risen about 35% since 2019 (National Restaurant Association). Supplier price increases rarely arrive as one announcement: they hide in a few cents more per line, a product renamed in a new billing system, or a 10kg bag that becomes 9kg at the same price. Even when an owner spots a rise, they can't tell whether it's the market or their supplier, so they don't know whether to push back, raise menu prices or switch.
Target users: owners and managers of independent cafés, restaurants, bakeries and food stalls who buy from several suppliers and have no finance team.
What it does
Detect: reads invoice PDFs in any layout, matches the same product across suppliers and pack sizes, and flags price increases, shrinkflation and cheaper suppliers, with the yearly cost of each. Investigate: an AI Procurement Investigator chooses which checks to run on a price rise (market benchmark, fuel and currency, supplier options, product comparability, savings), gathers numbered evidence and writes findings that cite it; code then removes fake citations and flags any figure not in the evidence. Underneath, MarginGuard compares each increase with real world-market prices (IMF commodity prices for palm and soybean oil, wheat, rice, sugar, coffee and poultry, plus Brent crude and SGD/USD, via FRED) over matched periods. Commodity data lags by about two months, so it scores only the months both sides cover and reports anything newer separately as not yet checkable. It separates verified facts from unverifiable explanations, labels every benchmark a market proxy rather than a direct quote, and shows sources and dates. Forecast: a simple, back-tested market outlook and clearly labelled what-if scenarios for the supplier's price, plus a profit impact simulator that shows the effect on gross margin. Act: ranked, rules-based recommendations (ask for an explanation, challenge an above-market rise, recover a shrunk pack, switch, review menu prices, ask for a fixed price, get a competing quote), a supplier comparison workspace that compares landed cost per kg or litre, and an action tracker for what you did and what it actually saved. On our 24 sample invoices it found $2,104/year in hidden increases (4.2% of supplier spend) and, crucially, sorted them rather than lumping them together:
Cooking oil — review needed. Over April–July, where market data exists, the supplier moved +7.6% against +3.1% for palm and soybean oil: broadly in line. A further +9.5% landed after the latest published market data, so about $450/year has no market explanation yet. That is flagged for review, not claimed as a saving. Plain flour — in line. The bag shrank 10kg to 9kg (+11.1% per kg) while wheat rose about 14%. Hidden, but consistent with the market, so plan for it. Jasmine rice — future pressure. Supplier +6% while Thai rice export prices rose about 22%. Ask for a fixed price. Eggs — act now. The same eggs are 9.2% cheaper at another current supplier: $250/year, verified from the user's own invoices. That first case is the design decision we are most proud of. Comparing the supplier's April–September rise against April–July market data would have made the supplier look guilty, which is exactly the kind of confident-but-wrong analysis we wanted to avoid.
How we built it
Extraction: pdfplumber pulls the invoice text; an open-source LLM (Qwen2.5-72B via Featherless AI) converts it to structured JSON, validated with pydantic. Verification + self-correction: code re-does the invoice maths (quantity × price = line total; lines = subtotal). If the AI's reading fails, it's told exactly what's wrong and asked to fix it. Remaining problems are shown to the user, never hidden. Product matching: the LLM groups product names across suppliers and naming changes into canonical products. Analysis (no AI): pandas calculates price per base unit, change vs the first recorded price, yearly impact from average monthly quantities, shrinkflation and cross-supplier gaps. Market benchmarks (no AI): items are classified and mapped to FRED series; changes are aligned to the same months, converted from US$ to S$, and compared with a ±5-point tolerance. The part of a supplier's move that falls outside the published market window is split out and reported separately. Market data is live-first: each download from FRED is validated before use; if it fails, the last good download or the copy shipped with the app is used, labelled Cached data with its original download time and the reason. Download time and the latest observation month are shown separately, data more than four months behind is flagged as outdated, and a rate-limited Refresh button forces a new download. New data recalculates the benchmarks and marks earlier AI investigations as out of date. Forecasting and scenarios (no AI): naive, exponential smoothing and 12-month trend, chosen by a rolling 6-month backtest and biased towards the simplest method; forecast month by month to the chosen horizon, with ranges that widen. Kept strictly apart from the user's what-ifs: a commodity price shock, a US dollar shock and a cost pass-through rate compound with the projection rather than overwriting it, and are disabled with an explanation on items that have no market benchmark. Supplier paths are scenarios, not predictions, and the money panel reports the cumulative cost over the selected horizon rather than annualising a short shock. AI Procurement Investigator (agentic): a bounded loop with no agent framework. The LLM sees the evidence gathered so far and replies with JSON naming the next tool; code validates the choice (known tool, allowed arguments, no repeats), runs it and feeds back any rejection, up to 8 steps. Tools are deterministic Python over the invoices, FRED series and user quotes, and each returns evidence with an ID and a provenance label (invoice, live verified, cached, user-entered, demo, calculated, unavailable). Product comparability asks the model inside fixed rules (a unit mismatch is never comparable; a quote with no spec can't be called comparable). The final report is JSON with evidence IDs; code drops citations to evidence that doesn't exist, demotes "verified" claims without valid evidence, flags numbers not found in the evidence, and shows conflicts with the calculated market check. Money comes only from tool output. Each run is labelled live or replayed from saved responses. News search and online catalogues are not connected, and the investigator says so rather than citing them. Recommendations (no AI): rules-based, scored by yearly value × evidence confidence × ease. Money is kept in four separate buckets that are never mixed: identified savings (evidence-backed, best option per item so nothing is double-counted), flagged for review (no market explanation yet, deliberately excluded from savings), expected and realised (from the tracker; expected is never counted as realised). A quote below its minimum order, or labelled a possible substitute, is downgraded rather than counted. The LLM writes the plain-English advice and email from the calculated findings, referencing them by ID, so it can't invent figures. Interface: Streamlit multi-page app (Overview, Cost Intelligence, Forecast & Scenarios, Savings, Data & Checks) with Altair charts, one shared item selection across pages, and the conclusion and next steps above the evidence. A "Midnight Slate" dark theme defined from a single set of design tokens, with WCAG AA contrast throughout and colour reserved for financial meaning. Deployed on Streamlit Community Cloud. Accuracy: 99.2% of line-item fields (595/600) extracted correctly on the 24 sample invoices, with all 48 dates and invoice numbers correct and 23 of 24 invoices read perfectly (measured with evaluate.py against known answers). The one error was a garbled product name ("J000asmine Rice") with all numbers right. Tests: an offline end-to-end pipeline test with a fake LLM; 23 tests for the investigator with a scripted fake model (invalid tools, missing arguments, repeats, the step cap, fabricated citations, unsupported figures, unit mismatches, no alternatives, missing market data, model failure); and 29 unit tests for matched-period benchmarks, currency conversion, forecasts, scenarios, profit impact, quote savings, minimum-order handling and recommendation ranking. Plus a scripted walkthrough of every page and interaction against a strict Streamlit stand-in. Technical components: Python, Streamlit, pandas, Altair, pdfplumber, pydantic, requests, FRED (IMF Primary Commodity Prices, Federal Reserve exchange rates), Featherless AI (OpenAI-compatible API), Qwen2.5-72B-Instruct, reportlab (sample data).
Challenges we ran into
This was our first hackathon, and honestly a lot of the challenge was outside the code. We'd never used Git, PowerShell or a GitHub account before this week, so "git is not recognised" and an app stuck "in the oven" on Streamlit Cloud took up more time than we'd like to admit.
On the product side, the hardest problems were about being right rather than looking right:
- Our first accuracy score for reading invoices was 80%. It turned out the AI was mostly correct; our grader was demanding exact product-name matches. Fixing the grader took it to 99.2%.
- Our first market comparison put a supplier's April–September price rise against market data that only went to July, and called it "above market". It looked impressive and it was unfair. We rebuilt it around matched periods, and our headline number dropped. That was the right trade.
- Streamlit treats text between two dollar signs as a maths formula, so every price on screen was quietly getting mangled.
- On the forecast page, some sliders did nothing visible because they were multiplying a flat 0% forecast. The maths was fine, but to a user it looked broken, so we redesigned the page into three clear steps.
- Live supplier prices need a paid API. Rather than show made-up listings, we used the café's own invoices and quotes the user enters. ## Accomplishments that we're proud of
- Shipping a working, live app in our first hackathon.
- 99.2% field accuracy reading invoices with an open-source model, with every invoice re-checked by arithmetic.
- An AI investigator that decides which checks to run, cites its evidence for every finding, and has its citations and numbers verified by code before we show them.
- The app refuses to overclaim. When market data can't explain part of a price rise yet, it says "review needed" instead of guessing. The cooking oil case is our favourite example.
- Keeping evidence-backed savings, money still to be questioned, and expected vs actual savings strictly separate, so the totals never inflate. ## What we learned
- Check how you're measuring before blaming the model. Our "bad" AI was a bad grader.
- A confident wrong answer is worse than "we don't know yet". Some of our best changes made our numbers smaller and more honest.
- If a control doesn't visibly change anything, it's a bug to the user, even when the math underneath is correct.
- Practical basics we didn't know a week ago: Git, deploying an app, and keeping an API key out of a public repo.
- Building with AI tools is fast, but you still have to test everything and understand why it works. ## What's next for MarginGuard
- Live supplier search through a proper catalogue API, so the investigator can find real cheaper alternatives, not just ones already on your invoices.
- Reading photos of paper invoices with a vision model.
- More benchmarks: dairy, local wholesale market prices and electricity tariffs.
- Connecting the investigator to news and supply-disruption sources it can cite.
- A real database behind the action tracker, so realised savings build up over time.
- Testing it with real cafés and their real invoices.
Log in or sign up for Devpost to join the conversation.