kitchen/docs/archive/LLM-INTEGRATION-PLAN.md
jtricerolph 8d688b459d 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>
2026-07-12 12:15:39 +00:00

208 lines
9.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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