Initial kitchen scaffold — Phase 1 kitchen port (build-verified 2026-07-11)

FastAPI backend (Python 3.11, MSSQL ODBC for SambaPOS, Azure DI OCR),
kitchen_db on central PG. React/TS/Vite frontend with navy sidebar layout.

Backend: auth.py (APP_SLUG=kitchen, SimpleNamespace — archive routes use
.kitchen_id/.is_admin without modification), main.py (51 migrations, scheduler,
internal router for KDS bookings feed), api/internal.py, full archive API
(31 routers: invoices, recipes, menus, sambapos, resos, newbook, disputes,
purchase_orders, etc.), models, migrations, OCR pipeline.
kitchen_id pinned to 1 (B1 — single hotel).

Frontend: AuthGate (app=kitchen, token shim for archive compat — B5b pending),
Layout (navy sidebar, 6 sections, Lucide icons, teal --app-primary),
App.tsx (Outlet pattern, UploadApp outside Layout), index.css (full :root block).
strict: false — archive components have type issues; build clean.

Note: 45 archive components call fetch('/api/...') without /kitchen/ prefix
(B5b). Runtime 404s; deferred until after initial testing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
jtricerolph 2026-07-12 12:15:39 +00:00
commit 8d688b459d
10003 changed files with 1928395 additions and 0 deletions

View file

@ -0,0 +1,208 @@
# LLM Integration Plan — Review & Enhancements
> **Supersedes** the original 3-feature plan. Reviewed and expanded to cover 11 features across 6 phases, with master kill switch, removal manifest, cost guardrails, and detailed UX specification.
## Context
The kitchen-invoice-flash system has several manual data entry bottlenecks where an LLM can reduce friction. Using **Claude Haiku** via the Anthropic Python SDK. Estimated cost: ~$0.50-1/month for a single kitchen. Mixed trigger approach: auto for cheap label parsing, user-triggered button for invoice analysis and ingredient matching.
**Scope**: Original 3 features + Tier 1 (A, B, C) + Tier 2 (D, E, F, G) + new Feature H (line item reconciliation).
**Instance model**: Single instance (not multi-kitchen). Settings are global, not per-kitchen.
**API key storage**: Plaintext in DB (matching existing pattern for Azure, NewBook, etc.).
---
## 1. What the Plan Gets Right
- **Model choice**: Claude Haiku is correct — structured extraction, not creative generation.
- **API key in Settings**: Follows the established `KitchenSettings` pattern (same as `azure_key`, `resos_api_key`, etc.).
- **Structured output via `tool_use`**: Guarantees parseable JSON. Right approach over prompt-based JSON.
- **Graceful degradation**: LLM features are additive, never blocking. Existing regex/trigram still works without a key.
- **Trigger strategy**: Auto for cheap label parsing, user-triggered buttons for invoice assist and ingredient matching.
---
## 2. Infrastructure Enhancements
### 2a. Client Instantiation
Single shared client instance, re-initialized if API key changes in settings.
### 2b. Response Caching
New `llm_analysis_cache` table:
- Columns: `id`, `feature`, `input_hash` (SHA-256), `result_json`, `model_used`, `prompt_version`, `created_at`
- Unique constraint: `(feature, input_hash, prompt_version)`
- Feature 1 (labels): cache by text hash, TTL 30 days. Feature 3 (matching): TTL 7 days. Feature 2 (invoices): skip caching.
### 2c. Usage Tracking
New `llm_usage_log` table:
- Columns: `id`, `feature`, `model`, `input_tokens`, `output_tokens`, `latency_ms`, `success`, `error_message`, `created_at`
- `GET /api/settings/llm-usage` endpoint (aggregated last 30 days). Display on Settings page.
### 2d. Configurable Model
`llm_model` column in `KitchenSettings` (default `"claude-haiku-4-5-latest"`). Settings UI shows dropdown (Haiku / Sonnet).
### 2e. Prompt Versioning
Constants in `llm_service.py`. Cache lookup includes `prompt_version` — version bump auto-invalidates stale cache.
### 2f. Error UX
Three-state `llm_status` in API responses: `"success"`, `"unavailable"` (no key), `"error"` (call failed + message).
### 2g. Rate Limiting
`asyncio.Semaphore(5)` caps concurrent LLM calls.
### 2h. Master Kill Switch
`llm_enabled` (Boolean, default **False**) in `KitchenSettings`. When disabled: zero AI footprint in frontend, no API calls, no logging. See `LLM-MANIFEST.md` for full details.
### 2i. Removal Manifest
`LLM-MANIFEST.md` in project root — updated each phase. All LLM code marked with breadcrumb comments: `LLM FEATURE — see LLM-MANIFEST.md for removal instructions`.
### 2j. Cost Guardrails (5 layers)
1. **Per-feature toggles**`llm_features_enabled` JSONB column, individually disable features
2. **Monthly token budget**`llm_monthly_token_limit` (default 500,000 tokens ~$1.25/month)
3. **Auto-trigger throttle** — per-entity cooldown via cache check
4. **Single-call token cap**`max_tokens` on every API call
5. **Cost visibility** — Usage stats card on Settings page
### 2k. Prompt Injection Mitigation
`tool_use` structured output mitigates this. System messages note input is "untrusted product/invoice text".
---
## 3. Features
### Original Features
#### Feature 1: Product Label Allergen Parsing (Auto)
- Trigger: automatic when `product_ingredients` text is populated
- `analyse_product_label(ingredients_text, flag_categories)``[{flag_id, status: "contains"|"may_contain"|"suitable_for", reason}]`
- ~550 tokens/call, ~$0.001
#### Feature 2: Invoice OCR Assist (User-triggered)
- "AI Assist" button on Review page
- `assist_invoice_ocr(ocr_data, supplier_list, line_items)` → supplier match, date correction, pack size extraction, OCR corrections
- Batches line items in groups of 15-20 for 50+ line invoices
- ~1,900 tokens/call, ~$0.003
#### Feature 3: Smart Ingredient Matching (User-triggered)
- "AI Match" button when trigram results have low confidence
- `rank_ingredient_matches(description, candidates)` → re-ranked list with confidence scores
- ~650 tokens/call, ~$0.001
### Tier 1 — High Value
#### A. Recipe Text Allergen Scanning
Same `analyse_product_label()` function with recipe text as input. Catches contextual allergens regex misses.
#### B. Menu Description Generation
"Generate Description" button in PublishToMenuModal. Customer-facing descriptions with allergen callout.
#### C. Dispute Email Drafting
"Draft Email" button on DisputeDetailModal. Professional supplier email requesting credit.
### Tier 2 — Medium Value
#### D. Smart Duplicate Detection
"AI Check" button when creating ingredients. Reuses `rank_ingredient_matches()`.
#### E. OCR Field Extraction Fallback
Automatic when regex returns null. Mark LLM-extracted fields with `source: "llm"`.
#### F. Supplier Alias Resolution
LLM fallback when `identify_supplier()` returns no match.
#### G. Ingredient Yield Estimation
Auto-hint on ingredient creation: "Typical yield: ~85%".
#### H. Invoice Line Item Reconciliation (Auto)
Matches unmatched line items against supplier's own historical naming from past 90 days.
---
## 4. Frontend UX
### Core Principles
1. **Suggestions only, never auto-change data** (except allergen "Contains" following existing Brakes pattern)
2. **Visible loading + notification** for auto-triggered features
3. **Sparkle icon** as consistent AI indicator
### Auto-triggered Features
| Feature | Where | Visual |
|---------|-------|--------|
| Label Parsing | IngredientModal | Spinner → toast → sparkle icon suggestions |
| Recipe Scanning | RecipeEditor | Spinner on flag matrix → sparkle suggestions |
| OCR Fallback | Invoice upload | Dashed amber border + "AI extracted" tooltip |
| Reconciliation | Review page | Amber "AI match" badge |
### User-triggered Features
| Feature | Where | Trigger |
|---------|-------|---------|
| AI Assist | Review top bar | Button → yellow-highlighted suggestions |
| AI Match | IngredientModal | Button → re-sorted dropdown |
| Menu Description | PublishToMenuModal | "Generate" button → pre-filled textarea |
| Dispute Email | DisputeDetailModal | "Draft Email" button → pre-filled text |
| Duplicate Detection | IngredientModal | "AI Check" button → warning panel |
| Supplier Alias | Review page | Auto suggestion banner |
| Yield Estimation | IngredientModal | Auto hint below field |
---
## 5. OCR Correction Enhancements (Feature 2)
LLM significantly enhances 7 existing OCR correction scenarios:
1. **Qty × Price ≠ Total** — identifies which field Azure misread
2. **Line items vs invoice total mismatch** — identifies delivery charges, subtotal rows
3. **Description content vs value** — recommends which is the correct description
4. **SKU in description** — distinguishes product codes from descriptions
5. **Weight-as-quantity** — handles non-standard weight formats
6. **Subtotal/discount row detection** — catches "Goods Total", "Delivery Surcharge", etc.
7. **Gross-to-net VAT** — identifies VAT treatment from invoice context
---
## 6. Implementation Order
### Phase 1: Infrastructure ✅
- `backend/services/llm_service.py` — client, caching, logging, rate limiting
- `backend/requirements.txt``anthropic>=0.40.0`
- `backend/models/settings.py` — LLM columns
- `backend/models/llm.py` — usage log + cache models
- `backend/migrations/add_llm_infrastructure.py`
- `backend/api/settings.py` — LLM fields, usage stats endpoint
- `LLM-MANIFEST.md` — removal manifest
### Phase 2: Label Parsing + Recipe Text (Features 1 + A)
- `analyse_product_label()` with caching
- `/analyse-label` endpoint
- Frontend auto-trigger + sparkle suggestions
### Phase 3: Invoice OCR Assist + Reconciliation (Features 2 + E + H)
- `assist_invoice_ocr()` with batching
- `reconcile_line_items()` for supplier history
- "AI Assist" button + suggestion UI
- LLM fallback for regex field extraction
### Phase 4: Ingredient Matching + Supplier (Features 3 + D + F)
- `rank_ingredient_matches()`, supplier alias matching
- "AI Match" button, duplicate detection
### Phase 5: Text Generation (Features B + C)
- Menu description generation
- Dispute email drafting
### Phase 6: Polish + Yield (Feature G)
- Yield estimation hints
- Usage dashboard
- Prompt tuning
---
## 7. Verification Checklist
1. Add API key in Settings → saved, model dropdown works
2. `llm_enabled = False` (default) → zero AI footprint anywhere
3. Enable → full LLM settings section appears
4. Ingredient with "Contains: wheat flour, milk" → Gluten, Dairy suggestions
5. Upload invoice → "AI Assist" → corrections + suggestions
6. Invalid API key → toast error, regex/trigram still works
7. Budget exceeded → graceful degradation
8. All AI suggestions visually distinct with sparkle icon