Commit graph

10 commits

Author SHA1 Message Date
b2c546e86a feat: wire secondary-use window hints into recipe engine (#83)
Secondary-state items (stale bread, overripe bananas, day-old rice, etc.)
are now surfaced to the recipe engine so relevant recipes get matched even
when the ingredient is phrased differently in the corpus (e.g. "day-old
rice" vs. "rice").

Backend:
- Add rice and tortillas entries to SECONDARY_WINDOW in expiration_predictor
- Add secondary_pantry_items: dict[str, str] field to RecipeRequest schema
  (maps product_name → secondary_state label, e.g. {"Bread": "stale"})
- Add _SECONDARY_STATE_SYNONYMS lookup in recipe_engine — keyed by
  (category, state_label), returns corpus-matching ingredient phrases
- Update _expand_pantry_set() to accept secondary_pantry_items and inject
  synonym terms into the expanded pantry set used for FTS matching

Frontend:
- Add secondary_pantry_items to RecipeRequest interface in api.ts
- Add secondaryPantryItems param to _buildRequest / suggest / loadMore
  in the recipes store
- Add secondaryPantryItems computed to RecipesView — reads secondary_state
  from inventory items (expired but still in secondary window) and builds
  the product_name → state_label map
- Pass secondaryPantryItems into handleSuggest and handleLoadMore

Closes #83
2026-04-18 19:06:53 -07:00
200a6ef87b feat(recipes): complexity badges, time hints, Surprise Me, Just Pick One
#55 — Complexity rating on recipe cards:
  - Derived from direction text via _classify_method_complexity()
  - Badge displayed on every card: easy (green), moderate (amber), involved (red)
  - Filterable via complexity filter chips in the results bar

#58 — Cooking time + difficulty as filter domains:
  - estimated_time_min derived from step count + complexity
  - Time hint (~Nm) shown on every card
  - complexity_filter and max_time_min fields in RecipeRequest
  - Both applied in the engine before suggestions are built

#53 — Surprise Me: picks a random suggestion from the filtered pool,
  avoids repeating the last pick. Shown in a spotlight card.

#57 — Just Pick One: surfaces the top-matched suggestion in the same
  spotlight card. One tap to commit to cooking it.

Closes #55, #58, #53, #57
2026-04-16 09:27:34 -07:00
2ad71f2636 feat(recipes): pantry match floor filter — 'can make now' toggle
Adds pantry_match_only flag to RecipeRequest. When enabled, any recipe
with one or more missing ingredients (after swaps) is excluded from
results. Swapped ingredients count as covered.

Frontend: toggle checkbox in recipe settings panel, disabled when
shopping mode is active (the two modes are mutually exclusive). Hides
the max-missing input when pantry-match-only is on (irrelevant there).

Closes #63
2026-04-16 09:12:24 -07:00
c02e538cb2 feat: remove assembly results from suggest() -- moved to Build Your Own tab 2026-04-14 11:39:57 -07:00
6da86dd0a7 feat(hard-day): tier-sort results — premade first, simple second
Hard Day Mode now prioritises results by effort tier before match_count:
  Tier 0 (premade): frozen/instant title keywords, or ≤2 ingredients with
    heat/microwave-only steps (frozen dinner, heat-and-eat, microwave meal)
  Tier 1 (super simple): ≤3 ingredients + any easy method (quesadilla,
    cheese toast, scrambled eggs)
  Tier 2 (easy/moderate): everything else that passed the 'involved' filter

Assembly templates default to tier 1 (inherently simple). Normal mode sort
is unchanged — match_count only.
2026-04-08 23:18:29 -07:00
793df1b5cf feat: saved recipes, recipe browser, and recipe detail panel
- Saved recipes: save/unsave, star rating, notes, tags, collections (migrations 018-020)
- Recipe browser: domain/category browsing with pantry match badges, pagination
- Recipe detail panel: full directions, ingredient checklist, swap candidates, prep notes
- Grocery links: affiliate links for missing ingredients
- Nutrition filters and display chips on recipe cards
- Bookmark toggle persisted to saved_recipes table
- Tier gates on saved recipes (paid) and collections (premium)
- Browser telemetry for domain/category click tracking
- Cloud compose: CLOUD_DATA_ROOT volume mount for per-user SQLite trees
- manage.sh: cf-orch agent sidecar in local stack
- README: updated feature list and stack description
2026-04-08 14:35:02 -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
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
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