Inspiration
Anyone who has worked in or visited a café near closing time has seen it happen. Trays of sandwiches, pastries, and cartons of milk that were perfectly good an hour earlier get thrown straight in the bin. The food was fine to sell. The only reason it did not sell is that nobody dropped the price in time.
I found out later how big that problem actually is. Over £3 billion of food is thrown away every year across UK coffee shops and food service, and a lot of it is avoidable. Some shops try to fight it with a blunt rule, like 25% off everything at closing, but that is a rough guess. It gives away margin on items that would have sold anyway, and it does not discount enough on the things about to expire. I wanted to build something that made that decision properly, item by item, instead of with one lazy rule.
What it does
ShelfLife automates price reductions for perishable stock. You scan a product's barcode, and it looks the product up in Open Food Facts to fill in the name and category for you. You add the price, expiry date, and stock, and it lands on a dashboard as a shelf ticket, colour coded by how close it is to expiring.
When you hit "Reprice", the app sends that product's details to an AI, which weighs the days until expiry, how much stock is left, and the time of day, then returns a new price along with a short explanation of why. There is a hard floor at 30% of the original price and a ceiling at the original price, so it can never do anything silly, and every price change is logged so you can see the history.
How I built it
The backend is Python and Flask, with SQLite for storage across two tables, one for products and one for the price change history. The pricing decisions run through Claude, which receives the product details and returns both a price and a reasoning string in JSON that the app parses and displays.
Barcode scanning happens in the browser with the html5-qrcode library, using the device camera. The scanned barcode goes to a Flask route that queries Open Food Facts server side and hands back the product name and category. The front end is plain HTML, CSS, and vanilla JavaScript, with a dashboard styled to look like paper shelf tickets, which felt right for the subject.
What I learned
This was the first time I properly connected a language model to a real application rather than just calling it in a notebook, and the thing that surprised me most was how much the quality lives in the prompt. My first version just asked for a price, and I got inconsistent answers and text I could not parse. Forcing the model to return a strict JSON shape, and clamping the result in code afterwards, was what made it reliable.
I also learned a lot about the boundary between the browser and the server. My first instinct was to call the Open Food Facts API directly from the front end, which failed immediately, and understanding why pushed me to move that logic to the backend where it belonged.
Challenges I ran into
The biggest one was that same Open Food Facts lookup. Calling it from the browser was blocked by CORS, and hammering it while the scanner fired repeatedly got me rate limited. Moving the call to the server fixed both, but then the server requests started coming back as 403 Forbidden. It turned out the API blocks requests that do not send a proper User-Agent header identifying the app, which took a while to track down because the error gave nothing away.
Getting the AI output to behave was the other big one. A language model returning a raw number is not deterministic, so I had to constrain it in the prompt and then enforce those limits in code as a backup. There were also plenty of smaller battles, like a mistyped column name that silently broke my primary key, and a database write that quietly did nothing because a URL parameter was arriving as a string instead of an integer.
What's next for ShelfLife
Automatic repricing on a schedule instead of a button press, proper accounts so different shops can keep their own stock separate, and a summary of how much waste and revenue the discounts actually saved.
Log in or sign up for Devpost to join the conversation.