Commit graph

42 commits

Author SHA1 Message Date
dd09aa21f4 fix: add FTS5 sync triggers so recipe inserts are indexed immediately
Migration 015 did a one-time rebuild of recipes_fts at creation time but
omitted triggers, so rows inserted after that point were invisible to MATCH
queries. Adds AFTER INSERT/UPDATE/DELETE triggers to 015 (fresh DBs / tests)
and migration 016 to backfill them on existing databases.

Fixes 3 failing tests: test_search_recipes_by_ingredient_names,
test_level1_returns_ranked_suggestions, test_level2_returns_swap_candidates.
2026-04-02 23:14:22 -07:00
dda8be48c9 feat: wire cf-orch agent sidecar and scheduler coordinator integration (closes #7)
- compose.override.yml: cf-orch agent sidecar (port 7702) self-registers with
  coordinator at COORDINATOR_URL; advertise-host configurable via CF_ORCH_ADVERTISE_HOST
- scheduler.py: pass coordinator_url=settings.COORDINATOR_URL and service_name="kiwi"
  so VRAM leases appear as "kiwi" on the orchestrator dashboard
- environment.yml: add psutil>=5.9 (required by cf-orch agent eviction executor)
- .env.example: document CF_ORCH_ADVERTISE_HOST
2026-04-02 22:57:21 -07:00
8fec5b6402 chore: inventory endpoint cleanup, expiry predictor, tiers, gitignore test artifacts 2026-04-02 22:12:51 -07:00
1f819c4ee0 feat(frontend): recipe UI — filters, dismissal, load more, prep notes, nutrition chips
- Style/category filter panel with active chip display
- Dismiss (excluded_ids) support — recipes don't reappear until next fresh search
- Load more appends next batch without full re-fetch
- Prep notes 'Before you start:' section above directions
- Nutrition macro chips (kcal, fat, protein, carbs, fiber, sugar, sodium)
- Composables extracted for reuse
2026-04-02 22:12:45 -07:00
1a493e0ad9 feat: recipe engine — assembly templates, prep notes, FTS fixes, texture backfill
- Assembly template system (13 templates: burrito, fried rice, omelette, stir fry,
  pasta, sandwich, grain bowl, soup/stew, casserole, pancakes, porridge, pie, pudding)
  with role-based matching, whole-word single-keyword guard, deterministic titles
  via MD5 pantry hash
- Prep-state stripping: strips 'melted butter' → 'butter' for coverage checks;
  reconstructs actionable states as 'Before you start:' cooking instructions
  (NutritionPanel prep_notes field + RecipesView.vue display block)
- FTS5 fixes: always double-quote all terms; strip apostrophes to prevent
  syntax errors on brands like "Stouffer's"; 'plant-based' → bare 'based' crash
- Bidirectional synonym expansion: alt-meat, alt-chicken, alt-beef, alt-pork
  mapped to canonical texture class; pantry expansion covers 'hamburger' from
  'burger patties' etc.
- Texture profile backfill script (378K ingredient_profiles rows) with macro-derived
  classification in priority order (fatty → creamy → starchy → firm → fibrous →
  tender → liquid → neutral); oats/legumes starchy-first fix
- LLM prompt: ban flavoured/sweetened ingredients (vanilla yoghurt) from savoury
- Migrations 014 (nutrition macros) + 015 (recipe FTS index)
- Nutrition estimation pipeline script
- gitignore MagicMock sqlite test artifacts
2026-04-02 22:12:35 -07:00
b9c308ab28 fix: docuvision fast-path falls through when parse yields no items
_parse_json_from_text always returns a dict (never None), so the
previous `if parsed is not None` guard was permanently true — garbled
docuvision output would return an empty skeleton instead of falling
through to the local VLM. Replace the check with a meaningful-content
test (items or merchant present). Add two tests: one that asserts the
fallthrough behavior on an empty parse, one that confirms the fast path
is taken when parsing succeeds.
2026-04-02 13:49:38 -07:00
3016efa65b fix: address recipe/OCR quality issues from review 2026-04-02 12:41:59 -07:00
22e57118df feat: add DocuvisionClient + cf-docuvision fast-path for OCR
Introduces a thin HTTP client for the cf-docuvision service and wires it
as a fast path in VisionLanguageOCR.extract_receipt_data(). When CF_ORCH_URL
is set, the pipeline attempts docuvision allocation via CFOrchClient before
loading the heavy local VLM; falls back gracefully if unavailable.
2026-04-02 12:33:05 -07:00
b9eadcdf0e feat(frontend): warm organic design overhaul — Fraunces/DM fonts, saffron accent, compact inventory shelf view
- EditItemModal: replace all hardcoded colors (#eee, #f5f5f5, #2196F3, etc.) with CSS variable tokens; restyle modal header with display font, blur backdrop, and theme-aware form elements
- ReceiptsView: replace emoji headings, hardcoded spinner, and non-theme .button class with themed equivalents; all colors through var(--color-*) tokens
- RecipesView: fix broken --color-warning-rgb / --color-primary-rgb references (not defined in theme); use --color-warning-bg and --color-info-bg instead; apply section-title to heading
- SettingsView: apply section-title display-font class to heading for consistency
- InventoryList: remove three dead functions (formatDate, getDaysUntilExpiry, getExpiryClass) that caused TS6133 build errors
2026-04-01 22:29:55 -07:00
828efede42 fix: align frontend InventoryItem type with actual API response
InventoryItemResponse returns flat fields (product_name, barcode, category)
not a nested product object. Frontend interface and templates were using
item.product.name / item.product.brand which threw TypeError on render,
blanking the inventory tab.

- InventoryItem: remove product:Product, add product_name/barcode/category
- InventoryStats: fix total_products→available_items, expired→expired_items,
  items_by_location→locations
- item IDs are int not string — update deleteItem/updateItem/consumeItem sigs
- EditItemModal, RecipesView, InventoryList: fix all item.product.xxx refs
2026-04-01 17:30:21 -07:00
e11f91e14d fix: nginx /kiwi/ alias for direct port access
Vite builds with VITE_BASE_URL=/kiwi so assets are referenced as
/kiwi/assets/... in index.html. When accessed via Caddy at the /kiwi
path, Caddy strips the prefix and nginx gets /assets/... correctly.
When accessed directly at localhost:8515, nginx had no /kiwi/ route
so the JS/CSS 404'd and the SPA never booted (blank page on hard refresh).

Add location ^~ /kiwi/ { alias ...; } — ^~ prevents the regex
\.(js|css|...)$ location from intercepting /kiwi/ paths first.
2026-04-01 17:06:59 -07:00
33a5cdec37 feat: cloud auth bypass, VRAM leasing, barcode EXIF fix, pipeline improvements
- cloud_session.py: CLOUD_AUTH_BYPASS_IPS with CIDR support; X-Real-IP for
  Docker bridge NAT-aware client IP resolution; local-dev DB path under
  CLOUD_DATA_ROOT for bypass sessions
- compose.cloud.yml: thread CLOUD_AUTH_BYPASS_IPS from shell env; document
  Docker bridge CIDR requirement in .env.example
- nginx.cloud.conf + nginx.conf: client_max_body_size 20m for barcode uploads
- barcode_scanner.py: EXIF orientation correction (PIL ImageOps.exif_transpose)
  before cv2 decode; rotation coverage extended to [90, 180, 270, 45, 135]
  to catch sideways barcodes the 270° case was missing
- llm_recipe.py: CF-core VRAM lease acquire/release wrapping LLMRouter calls
- tasks/runner.py + config.py: COORDINATOR_URL + recipe_llm VRAM budget (4GB)
- recipes.py: per-request Store creation inside asyncio.to_thread worker to
  avoid SQLite check_same_thread violations
- download_datasets.py: HF_PARQUET_FILES strategy for repos without dataset
  builders (lishuyang/recipepairs direct parquet download)
- derive_substitutions.py: use recipepairs_recipes.parquet for ingredient
  lookup; numpy array detection; JSON category parsing
- test_build_flavorgraph_index.py: rewritten for CSV-based index format
- pyproject.toml: add Pillow>=10.0 for EXIF rotation support
2026-04-01 16:06:23 -07:00
77627cec23 fix: data pipeline — R-vector parser, allrecipes dataset, unique recipe index
- build_recipe_index.py: add _parse_r_vector() for food.com R format, add
  _parse_allrecipes_text() for corbt/all-recipes text format, _row_to_fields()
  dispatcher handles both columnar (food.com) and single-text (all-recipes)
- build_flavorgraph_index.py: switch from graph.json to nodes/edges CSVs
  matching actual FlavorGraph repo structure
- download_datasets.py: switch recipe source to corbt/all-recipes (2.1M
  recipes, 807MB) replacing near-empty AkashPS11/recipes_data_food.com
- 007_recipe_corpus.sql: add UNIQUE constraint on external_id to prevent
  duplicate inserts on pipeline reruns
2026-03-31 21:36:13 -07:00
9b890f5fde Merge branch 'feature/recipe-ui'
Recipe and Settings tabs complete. 96-module clean build.
2026-03-31 19:20:20 -07:00
1e70b4b1f6 feat: recipe + settings frontend — Recipes and Settings tabs
- RecipesView: level selector (1-4), constraints/allergies tag inputs,
  hard day mode toggle, max missing input, expiry-first pantry extraction,
  recipe cards with collapsible swaps/directions, grocery links, rate
  limit banner
- SettingsView: cooking equipment tag input with quick-add chips, save
  with confirmation feedback
- stores/recipes.ts: Pinia store for recipe state + suggest() action
- stores/settings.ts: Pinia store for cooking_equipment persistence
- api.ts: RecipeRequest/Result/Suggestion types + recipesAPI + settingsAPI
- App.vue: two new tabs (Recipes, Settings), lazy inventory load on tab switch
2026-03-31 19:20:13 -07:00
13d52a6b2c fix: renumber background_tasks migration 006→013 (conflict with element_profiles) 2026-03-31 14:28:10 -07:00
1c9221d27c Merge branch 'feature/shared-task-scheduler' 2026-03-31 14:27:51 -07:00
aeea8fc1c1 Merge branch 'feature/recipe-engine'
Recipe engine Phase 3 complete (Tasks 1-15):
- Data pipeline: USDA FDC, FlavorGraph, food.com corpus, substitution pairs
- ElementClassifier, SubstitutionEngine, RecipeEngine (Levels 1-4)
- StyleAdapter with 5 cuisine templates
- LLMRecipeGenerator (Level 3 scaffold + Level 4 wildcard)
- User settings with cooking_equipment for hard day mode
- Grocery affiliate links (Amazon Fresh, Walmart, Instacart)
- Recipe + staple + settings API endpoints with tier gating
- 55 tests passing
2026-03-31 14:27:32 -07:00
9371df1c95 feat: recipe engine Phase 3 — StyleAdapter, LLM levels 3-4, user settings
Task 13: StyleAdapter with 5 cuisine templates (Italian, Latin, East Asian,
Eastern European, Mediterranean). Each template includes weighted method_bias
(sums to 1.0), element-filtered aromatics/depth/structure helpers, and
seasoning/finishing-fat vectors. StyleTemplate is a fully immutable frozen
dataclass with tuple fields.

Task 14: LLMRecipeGenerator for Levels 3 and 4. Level 3 builds a structured
element-scaffold prompt; Level 4 generates a minimal wildcard prompt (<1500
chars). Allergy hard-exclusion wired through RecipeRequest.allergies into
both prompt builders and the generate() call path. Parsed LLM response
(title, ingredients, directions, notes) fully propagated to RecipeSuggestion.

Task 15: User settings key-value store. Migration 012 adds user_settings
table. Store.get_setting / set_setting with upsert. GET/PUT /settings/{key}
endpoints with Pydantic SettingBody, key allowlist, get_session dependency.
RecipeEngine reads cooking_equipment from settings when hard_day_mode=True.

55 tests passing.
2026-03-31 14:15:18 -07:00
0d65744cb6 feat: StyleAdapter — 5 cuisine templates with element dimension biasing 2026-03-31 12:54:42 -07:00
ea22dc8b47 fix: recipes endpoint — inject session tier before all gate checks 2026-03-31 12:52:10 -07:00
ce61b5f422 feat: recipe + staple API endpoints with tier gating 2026-03-31 12:49:38 -07:00
3943a8c99d fix: grocery_links — guard against empty ingredient names in build_links 2026-03-31 12:44:58 -07:00
e8fb57f6a2 feat: RecipeEngine Level 1-2 — grocery links + affiliate deeplink builder
Add GroceryLink schema model and grocery_links field to RecipeResult.
Introduce GroceryLinkBuilder service (Amazon Fresh, Walmart, Instacart)
using env-var affiliate tags; no links emitted when tags are absent.
Wire link builder into RecipeEngine.suggest() for levels 1-2.
Add test_grocery_links_free_tier to verify structure contract.

35 tests passing.
2026-03-31 12:23:07 -07:00
37737b06de feat: RecipeEngine Level 1-2 — corpus match, substitution, grocery list, hard day mode 2026-03-31 11:50:28 -07:00
6a54204cfc fix: store — deserialize recipe JSON columns in _row_to_dict 2026-03-31 11:33:49 -07:00
3c4f568b3e feat: store — recipe search, rate-limit check, substitution feedback logging 2026-03-31 11:29:54 -07:00
636bffda5a feat(tasks): add background task scheduler for LLM expiry fallback
Uses circuitforge_core.tasks.scheduler. VRAM detection via cf-orch when
available, falling back to unlimited. Adds expiry_llm_fallback task type
to background-predict expiry dates for items the LUT doesn't cover.
2026-03-31 09:25:48 -07:00
3527d61655 fix: test fixture — add protein_delta to substitution_pairs inserts 2026-03-30 23:16:15 -07:00
96850c6d2a feat: SubstitutionEngine — deterministic swap candidates with compensation hints 2026-03-30 23:13:49 -07:00
e44d36e32f fix: pipeline scripts — connection safety, remove unused recipes_path arg, fix inserted counter, pre-load profile index 2026-03-30 23:10:52 -07:00
e57ae74e27 fix: staple library — consistent tofu_firm slug, load error handling, typed yield_formats, expanded test coverage 2026-03-30 23:10:51 -07:00
a03807951b fix: ElementClassifier — guard empty input, safe JSON decode, dedup heuristic elements, strengthen test assertions 2026-03-30 23:10:49 -07:00
727336fab9 feat: staple library -- seitan, tempeh, firm tofu with yield format profiles 2026-03-30 23:01:52 -07:00
e377bd85aa feat: ElementClassifier -- ingredient element tagging with heuristic fallback 2026-03-30 22:59:46 -07:00
bad6dd175c feat: data pipeline -- recipe corpus + substitution pair derivation 2026-03-30 22:55:41 -07:00
27ec14b40f feat: tiers -- leftover_mode rate-limited free, style_picker paid+, staple_library free 2026-03-30 22:50:35 -07:00
59b6a8265f feat: data pipeline -- FlavorGraph molecule index builder 2026-03-30 22:46:53 -07:00
97203313c1 feat: data pipeline -- USDA FDC ingredient index builder 2026-03-30 22:44:25 -07:00
e56881d943 feat: migrations 005-011 — fix receipts column bug + recipe engine tables 2026-03-30 22:33:25 -07:00
18fde9747f chore: add recipe engine pipeline dependencies 2026-03-30 22:30:05 -07:00
8cbde774e5 chore: initial commit — kiwi Phase 2 complete
Pantry tracker app with:
- FastAPI backend + Vue 3 SPA frontend
- SQLite via circuitforge-core (migrations 001-005)
- Inventory CRUD, barcode scan, receipt OCR pipeline
- Expiry prediction (deterministic + LLM fallback)
- CF-core tier system integration
- Cloud session support (menagerie)
2026-03-30 22:20:48 -07:00