跳到正文
DEV Community· Love Yadav·· 5 小時前AI 評分46

Demeter:以 AI 處理 WhatsApp 餐單、避免漏記取消的廚房管理平台

Haunted by WhatsApp Cancellations, So I Built a Ghost That Never Cooks the Books

AI 導讀

Demeter 是為家庭食品及 tiffin 業務而建的廚房管理平台,可把 WhatsApp 等訊息整理成每日包餐表。平台透過 NVIDIA NIM 兩階段 AI 流程,以 Gemma 2 27B 擷取訂單資料,再由 Llama 3.1 Nemotron 70B 核實;所有變更須經業主批准,餐量及帳單則由程式計算。

正文

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine

What I Built

Demeter is a kitchen management platform for independent home-food and tiffin businesses. It turns messy customer messages like "Bhaiyya don't send food today", "send 2 lunches tomorrow" or "out of station till Monday" into a reliable daily packing sheet.

A home tiffin kitchen typically serves 15 to 100 regular subscribers, and their changes arrive over WhatsApp, SMS and calls in casual, mixed-language text. Owners track them in paper diaries or spreadsheets. A missed cancellation means cooked food goes to waste. A missed addition means a hungry customer. At month end, billing means scrolling back through weeks of chat history.

Demeter is built around one rule: AI proposes, the owner approves, code calculates.

  1. Models propose. The AI reads an incoming message, extracts structured order operations and highlights the exact words in the message that justify each one.
  2. The owner approves. The owner sees a side-by-side before-and-after calendar preview and approves with one click. The approval is checked against a previewHash, so what the owner saw is exactly what gets committed.
  3. Deterministic code calculates. Meal quantities, delivery rosters and monthly invoices are computed by plain TypeScript functions. The LLM never does the math.
  4. Baseline invariance. A one-off skip or extra is stored as a date-specific override. It never overwrites the customer's permanent weekly schedule.

How I read the theme. The ghost in this machine is the AI itself. It sits quietly between the WhatsApp thread and the kitchen and whispers proposals, but it cannot touch anything real. The README states this as an invariant: LLMs only write drafts to the proposals collection, and only human approval commits data to operational collections. The other ghosts are the cancellations and changes that used to vanish into chat history until someone's food went to waste. The project is named after Demeter, the Greek goddess of grain and harvest, who made sure no one went hungry. For me the idea fits the season: home-cooked food is something people remember, and a kitchen shouldn't lose track of the people it feeds.

Demo

Demeter can also be run locally against synthetic demo data (fictional customers A, B and C), with no real customer messages involved:

pnpm db:init                                      # create indexes, verify snapshot transactions
pnpm owner:provision --user-id demo-owner --confirm
pnpm seed:demo --confirm                          # seed demo customers + recurring schedules
pnpm session:generate                             # HMAC-signed session cookie for the browser/CLI
pnpm dev                                          # http://localhost:3000

What you can then try at http://localhost:3000/dashboard:

  • Paste a customer message such as "Bhaiyya Monday 2 tiffin bhej dena" as a source.
  • Open the review workbench and click Preview Effect to see the calendar change from 1 meal to 2.
  • Approve the proposal and finalize the daily packing sheet. Finalizing is blocked while any message for that day is still unresolved.
  • Open the building-clustered dispatch roster, or export the sheet as CSV.

An automated end-to-end check covers the same path (customer creation, source ingestion, proposal, preview hash, atomic approval, duplicate-request retry, immutable sheet):

pnpm test:backend --cookie-file .demo-cookie --write-demo

The Tiffining workspace: WhatsApp orders become reviewed meal plans, and every change waits for the owner’s approval.

Code

GitHub logo loveyadav1015 / demeter

Demeter turns messy WhatsApp tiffin orders into reliable packing sheets. AI proposes, the owner approves, code calculates, so no meal is wasted and no cancellation is missed.

Demeter— Intelligent Meal & Tiffin Kitchen Management Platform

Named after Demeter, the Greek goddess of grain and harvest, who made sure no one went hungry.

Demeter is a production-grade, full-stack kitchen management platform purpose-built for independent home-food and tiffin businesses. Built with Next.js 16 (App Router), React 19, MongoDB Atlas (ACID Transactions), Better-Auth, and a two-stage NVIDIA NIM AI pipeline (Gemma 2 27B + Nemotron 70B), it bridges messy customer messages, recurring subscriptions, cryptographic approvals, immutable packing sheets, dispatch route logistics, and monthly billing.


Table of Contents

The application lives in the Tiffining directory of the repository. The MIT license applies.

How I Built It

The workflow at a glance

flowchart LR
    subgraph IN["1. Intake"]
        A["Customer message<br/>WhatsApp / SMS / manual paste"] --> B["Source stored<br/>SHA-256 fingerprint<br/>24h duplicate suppression"]
    end

    subgraph AI["2. AI proposes"]
        B --> C["Stage 1: extract facts + evidence spans<br/>gemma-2-27b-it"]
        C --> D["Stage 2: verify + classify<br/>llama-3.1-nemotron-70b-instruct"]
        D --> E["Draft proposal<br/>status: needs_review"]
    end

    subgraph HUMAN["3. Owner approves"]
        E --> F["Review workbench<br/>before / after calendar diff"]
        F --> G["Preview Effect<br/>previewHash"]
        G --> H{"Owner approves?"}
    end

    subgraph CODE["4. Code commits and calculates"]
        H -- Yes --> I["Verify previewHash<br/>then atomic ACID commit<br/>daily override"]
        I --> J["Finalize day<br/>0 pending reviews required"]
        J --> K["Immutable packing sheet REV-N"]
        K --> L["Dispatch route<br/>CSV export"]
        K --> M["Monthly invoice<br/>wa.me billing link"]
    end

    H -- No --> N["Dismiss or defer<br/>baseline schedule untouched"]

The two-stage AI pipeline

Message understanding is a two-stage pipeline served through the NVIDIA NIM API:

  • Stage 1, extraction: google/gemma-2-27b-it pulls out candidate operations (intent kind, target dates, quantities) together with the character spans of evidence in the original message.
  • Stage 2, verification: nvidia/llama-3.1-nemotron-70b-instruct checks the extraction. It looks at whether the intent clarity score is at least 0.8, whether the evidence spans are valid, and whether a plan replacement is explicit.
flowchart TD
    M["Raw text<br/>'Bhaiyya Monday 2 tiffin bhej dena'"] --> S1["Stage 1: fact and span extraction"]
    S1 --> X["Candidate operations<br/>intent kind, target dates,<br/>quantities, evidence spans"]
    X --> S2["Stage 2: verification and classification"]
    S2 --> V{"Checks<br/>clarity score at least 0.8<br/>evidence spans valid<br/>plan replacement explicit"}
    V -- "Pass" --> P["Draft proposal<br/>ready for review"]
    V -- "Ambiguous or low score" --> R["Draft proposal flagged<br/>with specific warning labels"]
    V -- "Not an order" --> D["Classified unsupported / no_change<br/>auto-dismissed from cooking queue"]
    P --> H["Human review: the only path to production data"]
    R --> H

The model names, base URL, daily request and token limits, and stage timeouts are all environment variables (NVIDIA_EXTRACTION_MODEL, NVIDIA_VERIFICATION_MODEL, NVIDIA_BASE_URL, AI_DAILY_REQUEST_LIMIT, AI_DAILY_INPUT_TOKEN_LIMIT, and so on).

The safe-write gate

Every change that reaches the database passes the same gate, so duplicate taps, two open tabs and stale screens cannot corrupt the kitchen's state:

flowchart LR
    Req["Mutation request<br/>Idempotency-Key +<br/>expectedStateRevision"] --> K{"Key already<br/>in receipts?"}
    K -- Yes --> C["Return cached receipt<br/>HTTP 200, no re-execution"]
    K -- No --> Rv{"expectedStateRevision<br/>equals current revision?"}
    Rv -- No --> X["HTTP 409<br/>STALE_REVISION"]
    Rv -- Yes --> T["ACID transaction<br/>apply override, revision + 1"]
    T --> S["Save audit receipt"]
    S --> OK["Commit revision N+1"]

The rest of the stack

  • App: Next.js 16 (App Router), React 19, Tailwind CSS 4, Radix/Shadcn UI primitives.
  • Data: MongoDB 7.7 (Atlas) with multi-document snapshot transactions. There are 12 collections, including proposals, sheets, receipts and aiRuns.
  • Auth: Better-Auth with HMAC-SHA256 signed session cookies and origin/CSRF checks on every mutating request.
  • WhatsApp intake: @whiskeysockets/baileys (WhatsApp Web multi-device protocol), plus manual paste.
  • Validation: Zod 4 on every input, output and document.
  • Dates: @js-temporal/polyfill for timezone-aware calendar math (Asia/Kolkata).
  • API: 53 business operations with an OpenAPI 3.1 spec at docs/openapi.json.
  • Audit: Every AI call is recorded in aiRuns with token metrics, model provenance and latency. Privacy tooling covers a GDPR-style export, erasure of raw message text, and cascading account deletion.

The Math Behind It

The core idea is that everything a kitchen counts is arithmetic, and arithmetic belongs in code, not in a language model. Here are the rules Demeter follows, written as math.

Notation: c is a customer, d is a service date, w(d) is the weekday of d, p_c(w) is customer c's baseline quantity for weekday w, and o_c(d) is a date-specific override if one exists.

1. Effective quantity (baseline invariance)

A pause is simply an override of zero. The weekly plan p is never modified by an approval, only the override set o grows:

qc(d)={oc(d)if an approved override exists for (c,d) pc(w(d))otherwise

subject to 0≤qc(d)≤Qmax⁡ , where Qmax⁡ is the kitchen's meal quantity cap from its settings.

2. The day's packing total

With A(d) as the set of active customers on date d :

T(d)=∑c∈A(d)qc(d)

Illustrative example (numbers invented for explanation). Three customers each have a baseline of 1 meal on Monday, so T=1+1+1=3 . Then a message arrives: "Monday 2 tiffin bhej dena" from customer A, and another says "don't send food today" from customer B. After the owner approves both proposals, qA=2 and qB=0 , so T=2+0+1=3 . The total is unchanged, but the roster is very different, and next Monday B's baseline of 1 is still intact.

3. The finalize gate

Let P(d) be the set of unresolved review items for date d . A packing sheet can be finalized only when nothing is pending:

finalize(d)  ⟺  ∣P(d)∣=0

Once finalized, the sheet is stored as an immutable revision (REV-1, REV-2, ...).

4. Approval must match what the owner saw

Let H(⋅) be the cryptographic hash used for previewHash. An approval is accepted only if the state the owner previewed has not drifted:

approve  ⟺  H(previewnow)=hprovided

5. AI acceptance rule

With s as the intent clarity score from Stage 2, a candidate is a clean draft only if s≥0.8 and its evidence spans are valid, meaning each span [a,b) satisfies 0≤a<b≤len(text) . Anything else is flagged with warning labels. Either way, the draft changes nothing until a human approves.

6. Concurrency and idempotency

Let r be the kitchen's state revision and rexp the revision the client expects. A write succeeds only if they match, and then advances the revision:

rexp=r  ⇒  r←r+1,rexp≠r  ⇒  HTTP 409

For a repeated Idempotency-Key k , the cached receipt is returned, so a request executes at most once.

7. Monthly invoice

For month m , with n(d) delivered meals on day d , meal rate ρ and net credits and adjustments αm :

Invoicem=ρ∑d∈mn(d)+αm

Because n(d) comes from finalized, immutable sheets, the monthly total is reproducible: no scrolling back through chats, no disputes about what was sent.

8. AI budget arithmetic

The example configuration caps usage at 100 AI requests and 1,000,000 input tokens per day, with at most 16,000 input tokens per call. That implies an average budget of:

1,000,000 tokens100 requests=10,000 tokens per request

which comfortably fits short customer messages, and keeps the cost of running the AI predictable.

Why Does Open Innovation Matter?

The model is configuration, not a contract. Extraction and verification models are set through environment variables, so switching models is a config change and not a rewrite. Because the weights are open, the same models can be served by whichever provider or infrastructure the kitchen can afford.

An auditable AI. For a tool that touches someone's daily income, "the AI decided" isn't enough. With named open models, per-run provenance in aiRuns, evidence spans and a human approval step, every proposal can be traced to a specific model and a specific piece of the customer's message.

Cost and limits you control. The README calls for a free API key from build.nvidia.com, and the daily request and token ceilings are configurable (see the budget arithmetic above). For a small home business, predictable and low cost matters.

Open pieces end to end. Beyond the models, the WhatsApp integration (Baileys) is open source and the project is MIT licensed. Nothing about the core workflow depends on a single closed vendor's product continuing to exist.

Where it falls short today. Inference currently goes through the hosted NVIDIA NIM API and the database is MongoDB Atlas, so Demeter does not yet run fully offline. The open-weight models are what make a local deployment possible in principle, but the repo as it stands uses a hosted endpoint, and I'd rather say that plainly than overclaim.

Prize Categories

Best Use of MongoDB Atlas: Demeter uses MongoDB Atlas across 12 collections with multi-document ACID transactions as the data layer behind an open-weight NVIDIA Nemotron pipeline, storing Zod-validated WhatsApp AI drafts alongside subscriptions and executing cryptographically verified order updates without billing corruption.

來源:DEV Community · dev.to