The problem

A 40-seat restaurant plans its week on gut feel. You find out about the wet Wednesday, the match two streets away or the Monday holiday when the dining room is already empty, or packed. Chains have insight teams for this. An independent place has the owner, at 11 pm, after service.

I've seen this up close. Before this hackathon I built an invoice reader for a family-run restaurant in Spain (it has processed 715 supplier delivery notes), and the weekly planning there is still done by hand.

A chatbot doesn't fix it. When I ask Nemotron for this week's specials at a Los Angeles restaurant without giving it any data, it suggests a Korean BBQ bibimbap, a quinoa falafel wrap, a peach and burrata salad and a chef's table lunch on Wednesday, the day 22.4 mm of rain is forecast there. None of that is about this week, and none of it can be checked.

Judge it in 60 seconds

  1. Open https://tablecast-app.vercel.app and press Watch this run (Gjelina, Los Angeles). It's a real run I recorded on 21 Oct 2026, replayed in a few seconds and labelled as a replay.
  2. Look at the green header. Do this week comes first: the three dishes with their days and prices, the event, when to post and the supplier order, and each line takes you to its card and its evidence. Next to it: 24 of 26 claims verified against their sources, 19 sources fetched live, 77 s, $0.082 of inference.
  3. Your week: 22.4 mm of rain on Wednesday and 11.3 mm on Thursday become a braised white bean and kale stew for the two wet lunches, sold from the takeaway counter next door and priced against Gjelina's real Los Angeles menu. Tavily Map found that menu and set aside the New York and Las Vegas ones. Hover or tap any source chip to read the exact sentence behind a claim.
  4. Proof & engine: the two claims that failed are marked in place, with the reason (a "20 times" that no cited sentence says, and a critic's fee complaint stretched past its line). Next to the plan sits the same Nemotron with no data: one call, $0.0029, and a generic list with nothing to check. The plan took 14 more calls and $0.0787. The engine room lists every Nemotron call with its tokens, latency and cost.
  5. Try any restaurant live. Quick (Nemotron Ultra writes) takes one to two minutes. Economy (Nemotron Super) takes about 2 to 3 minutes and costs a third to half as much. Each replay card says which one wrote it.

What it does

You type the restaurant, the city and a goal ("win more lunch covers"), and you can add a photo of the menu. Tablecast reads the coming week and gives back a plan:

  • Your week: the 7-day forecast at the door, public holidays, events within 5 km, and the 2 to 4 moments that will move covers, each with something to do about it.
  • Your block: every restaurant, bar and café within 600 m (OpenStreetMap) and what competitors charge.
  • How you come across: what guests and critics like and complain about.
  • The moves: three dishes to test, built on the real menu and tied to the days they suit, plus an event and a post with the best time to publish it.
  • Launch: a specials board, a post image, a supplier order (email and CSV) and the week as a calendar file.
  • Proof & engine: every source, every check and what each model call cost.

Accessibility

Plans come in English or Spanish, whatever language the sources are in. It works on a 390 px phone and prints as a PDF. Everything works from the keyboard with a visible focus ring, the source chips open on focus as well as on hover or tap, and with reduced motion turned on nothing slides or drifts. axe-core finds no violations on the home page, on a full plan or on the About page, on desktop and on a phone.

How it uses NVIDIA Nemotron on Nebius Token Factory

Every model call goes to Nebius Token Factory through the official openai SDK. I picked a Nemotron for each step:

Step Model Why this one
Web research loop Nemotron 3.5 Lightning, reasoning_effort "none", parallel tool calls to Tavily lots of short "what next" decisions, about 0.6 s each; capped at 20 s, with Ultra as backup
Menu photo (optional) Nemotron 3.5 Super VL, forced function call turns a photo into dishes and prices
Write the plan, Quick Nemotron 3 Ultra, medium effort, one forced streamed function call against a strict schema best average in my eval, about 30 s to write and one to two minutes end to end; you watch the plan being drafted
Write the plan, Economy Nemotron 3 Super, medium effort (Ultra takes over if time runs out) just as faithful at a third to half the cost, about 2 min to write and 2 to 3 min end to end
Repair failed claims Nemotron 3 Ultra, low effort, one small call per claim, in parallel every fix gets checked again by code

I measured it, found what broke and changed the design

I wrote scripts/eval-routing.ts to gather the evidence for 8 fixed restaurants once. Each writer setting then writes from the same evidence, and code scores the plans. I've run it five times (157 plans, $8.01 of inference), each time after changing something:

  • v3 to v4: cite sentence ids instead of copying quotes. Ultra kept copying the right words under the wrong source, so only 35% of its claims passed first time. Now every source is split into numbered sentences and the writer cites ids like S9.4. First pass went up to 73%.
  • v4 to v5: a stricter check, after I audited my own demo. The old check let through a dish with "oregano and basil" that cited a menu line mentioning neither, and a $24 headline price that matched no dish. Now the claim's own words have to be in what it cites, and every price in the text is checked. Every writer lost first-pass points: Ultra at low effort went from 73% to 41%, and Quick sits at 45% in v6. The check got stricter; the writer didn't get worse.
  • v5 to v6: sources in another language. A plan in English citing a Spanish menu can't share words with it, so correct claims were failing in Madrid and Barcelona. Across languages the code checks numbers and names, and the app tells you when it did that.
  • v6 to v7: a review of my own demo run found three more things. The check had called Gjelina's English menu Italian because of dish names like "Cacio e Pepe", so it skipped the word check. Now the word check always runs first, and a page counts as another language only if its running text is. The plan said the Vegetable Sandwich had an "add egg" option for $3, but on the menu that line sits under the BLT. Now an add-on belongs to the dish above it. And the plan scheduled an order for the day before it was made. Now the writer gets the restaurant's own date and the week as a calendar, and code checks every date. Re-checked with no model calls, v7 rejects 39 claims that v6 had passed in my cached plans. Reading them, 15 had a real error and 24 were right but worded beyond their lines.
  • Picking the models. The same writer moved by up to 6 points between two rounds, so I only act on bigger gaps. Ultra at medium effort has the best average, so it writes Quick. Super is just as faithful at a third to half the cost but takes about 2 minutes, so it writes Economy. Lightning stays as the researcher.

Then I tested it where I hadn't tuned it. I committed a list of 8 new restaurants to the repo before running anything (Austin, New Orleans, Toronto, Manchester, Melbourne, Lisbon, Mexico City and Rome), gathered their evidence once and had both writers plan them with v7, next to the same model with no tools and no data:

Held-out, v7 Quick (Ultra) Economy (Super) Same model, no data
Verified, first pass to after repair 38% to 73% 63% to 74% nothing to check
Dishes tied to this week's data 42% 33% 0 of 48
Plan dates before the restaurant's today 0 of 165 0 of 154 not applicable
End to end (median) 59 s 157 s 2 to 3 s
Cost per plan (median) $0.096 $0.041 under $0.004

That's lower than on the tuning set, where the same plans re-checked with v7 score 82%. The round also found a bug: one Quick run died when Token Factory's stream errored on a 4,305-digit number in the model's JSON. Now a server error ends that attempt instead of the run, and the plan is retried once. The held-out round cost $1.16 of inference and 56 Tavily credits.

How a cited claim gets checked (code decides, not a model)

  1. Everything the agent sees becomes a numbered source (the forecast, events, web pages, the block, the menu photo), split into numbered sentences.
  2. Each claim cites the sentence ids it stands on, and you can read those sentences.
  3. Code checks that each cited sentence exists, that every number in the claim is in it, and that the claim's specific words are there too. Any price in the plan's text has to match a dish, a menu price or a checked claim, and no date in the plan may fall before today.
  4. Claims that fail go back to Nemotron once, in parallel. A fix only counts if it passes the same check. Anything that still fails is shown as unverified. It isn't hidden.

A web page only becomes a source if it's on the restaurant's own site or names its city, street or postcode. I re-checked 543 verified claims from my eval with the place check: it rejected 6, and on reading all 6 rejections were correct (a competitor's private room, the Las Vegas branch, a combo price).

So how good is "verified"? With Claude Code's help I read 100 verified and 50 rejected claims one by one against their cited lines, a seeded random sample, half from the held-out round (eval/AUDIT.md lists every verdict). 94 of the 100 verified claims were right, 4 had a minor slip and 2 were wrong. Both wrong ones kept every word of their sentence and changed its meaning: rave reviews read as a complaint, and a metaphor about jazz read as live music. Of the 50 rejections, 12 were true claims rejected for their wording. This is my own audit, not an independent one.

How it uses Tavily, and what that adds (measured)

The clearest example is in the London replay. In the week I recorded it (Dishoom Covent Garden, 21 Oct), Ticketmaster listed 25 shows nearby. Only Tavily's news search found that the Ballon d'Or ceremony comes to London for the first time on Monday 26 October. The plan built a moment and a dish around that night, and both claims are verified against the press release.

Tavily is how the agent sees the live web. Search finds the menu, reviews, press, local news and a competitor with the same cuisine. Extract reads the pages it picks. Map runs on the restaurant's own site first: it finds this branch's menu page and sets aside other branches' menus before research starts. To see what Tavily really adds, I wrote the same 8 weeks with and without the Tavily sources (eval/TAVILY.md). It doesn't raise the number of verified claims; it changes what they're about. With Tavily, 7 of 8 plans show the restaurant's real menu checked on a fetched page (0 of 8 without), and 6 of 8 plans verify what guests say (0 of 8). Without it, the writer makes up a menu from memory.

Challenges

  • Keeping a language model honest about numbers across about 30 sources. What worked was a deterministic verifier, sentence-id citations and one repair pass that gets checked again.
  • Reasoning loops, silent streams and server errors on the biggest models. I added loop and stall guards, a retry on a failed attempt, a lower-effort fallback and a time budget for the whole run.
  • Restaurants with the same name, and public map servers that were overloaded. I added the place check, backup Overpass mirrors and a Nominatim fallback that says when its list is partial.

What I'm proud of

  • The routing choice is part of the product (Quick or Economy), with five measured rounds and a held-out round behind it.
  • From a restaurant's name to dishes, an event, a post, a supplier order and a calendar in one to two minutes, for 6 to 12 cents of inference in my recorded runs.
  • I built a check strict enough to fail my own demo run, twice, and I changed the design both times.

What I learned

Open models work well for this kind of agent if you measure them. Lightning is a great tool dispatcher. Ultra and Super write long structured outputs reliably once you budget their reasoning. And the decision about what counts as verified should be made by code, not by another model.

About the problem itself, three things I didn't expect. A restaurant's website is often more than one restaurant: Gjelina's lists menus for Los Angeles, New York and Las Vegas, and a plan priced from the wrong branch looks fine and is wrong, so Map now picks this branch's menu before research starts. The nights that move covers aren't always on a ticketing API: Ticketmaster listed 25 shows around Dishoom that week and missed the Ballon d'Or. And names collide more than I thought: the place check turned away the Las Vegas branch and a competitor's private room as evidence about the restaurant.

What's next

A scheduled Monday run on Economy, when nobody is waiting for it. POS sales data to learn which dishes actually sold. Support for restaurant groups. Distribution through POS systems, reservation platforms and food distributors (the shopping list is already an order).

Known limits

Verification proves that a claim matches the sentences it cites. It doesn't prove that the source is right or that a dish will sell. In my audit, 2 of 100 verified claims were wrong in a way code can't see, and 12 of 50 rejections were true claims. On restaurants I never tuned on, 73 to 74% of claims end up verified, not the 84 to 88% of the tuning set. Forecasts more than about 3 days out are uncertain. Eight tuning restaurants and eight held-out ones is a small eval, and I point out where differences fall inside its noise.

Where this comes from

I first built this idea as Tastecraft, on a proprietary taste API. Tablecast is a separate project, rebuilt from scratch for open infrastructure: NVIDIA Nemotron on the Nebius Token Factory, plus Tavily for the live web. It shares no code or data with that earlier project, and the repo's commit history starts here.

Built With

Share this project:

Updates

Submission history