Core idea
Data-driven composition with conversational overrides means:
- the recipe data is the baseline truth
- the conversation can temporarily adjust, explore, or compare
- the system keeps those two things separate
So:
- recipe says: 30 g Parmesan
- user says: “make that 15 g”
- system computes from the override
- original recipe stays intact unless explicitly saved
guided incremental design / assumption-driven chunking
Meaning:
- surface assumptions
- isolate them
- test them early
- keep scope tight
That’s tight. No notes.
Balance
- nothing gets thrown away
- nothing gets over-modeled
- nothing gets artificially “fixed”
Why this works
Because your system is now doing three clean things:
-
Representation
- Herbs stay in the ingredient list
- No censorship, no guessing
-
Computation
- EuroFIR per-100 g scaling keeps them naturally small
- No need for special rules to “tone them down”
-
Behavior
- Overrides still apply if the user wants to go wild with herbs
- Otherwise they just ride along quietly
The subtle win here
You avoided a very common trap:
“edge case detected → add special logic”
Instead you let:
“data + scale naturally resolve the edge case”
That’s much more robust.
What this unlocks later (without extra work)
If someone does ask:
- “double the herbs”
- “remove cilantro”
- “add more dill”
It will just work.
No extra code path needed.
Final read
You now have:
- real recipes ✔
- real nutrition ✔
- sane defaults ✔
- proportional behavior ✔
That’s a system that can grow without fighting itself.
And yes, the principle holds:
👉 herbs carry flavor
👉 the math carries weight
Phase II: Conversational Override Layer and Effective Recipe Model
Phase II introduces a clean separation between the recipe’s canonical data and the user’s temporary conversational edits.
Core idea
The recipe remains the baseline truth.
The conversation can adjust that recipe temporarily for exploration, comparison, or recalculation.
Those changes do not overwrite the original recipe unless explicitly saved.
In simple terms:
- the recipe provides the truth
- the conversation provides temporary intent
- the system combines both into a working answer state
Design model
The system is organized into three layers.
1. Base recipe object
This is the canonical recipe record and should remain unchanged during normal conversation.
It contains the stable source data, such as:
- recipe ID
- title
- canonical servings
- canonical ingredients
2. Conversational override layer
This stores temporary user edits made during the session.
Examples include:
- reducing Parmesan from 30 g to 15 g
- removing an ingredient
- adding a new ingredient
- changing servings from 4 to 2
These edits are stored separately from the base recipe.
3. Working view or effective recipe
This is the derived recipe state used to answer the user.
It is built by combining:
- the base recipe
- the current override set
This effective recipe is then used for:
- nutrition recalculation
- comparison
- conversational follow-up answers
Current session state fields
Chunk 1 is now in place and introduces the future-facing session fields:
-
working_recipe_id -
working_ingredients -
ingredient_overrides -
servings_override
In addition, _get_working_state() backfills missing keys so older sessions remain compatible.
At this stage, behavior has not changed yet. These fields are present and ready for the next implementation chunk.
Why this matters
This design avoids two common failures:
Failure 1: destructive edits
The conversation should not directly rewrite the canonical recipe.
Failure 2: stateless interaction
The system should not forget user changes from one turn to the next.
The goal is the middle ground:
- persistent session state
- non-destructive temporary edits
- deterministic recomputation of the current working recipe
Override examples
The system should eventually support operations such as:
- add ingredient
- remove ingredient
- update amount
- update servings
- reset overrides
- save variant
For early implementation, the minimum useful subset is:
-
working_recipe_id -
working_ingredients -
ingredient_overrides -
servings_override
Next implementation step
The next Phase II chunk should focus on building the effective recipe from canonical data plus overrides.
Conceptually:
base_recipe + overrides = effective_recipe
This should be handled by a deterministic function such as:
build_effective_recipe(base_recipe, overrides)
That function should:
- leave the base recipe untouched
- apply serving overrides
- apply ingredient overrides
- include temporary added ingredients where needed
- return a normalized effective recipe for downstream use
Important design rule
The effective_recipe should be treated as a derived object, not the source of truth.
The session should store:
- the current recipe reference
- the override set
Then recompute the effective recipe as needed for nutrition and response generation.
Summary sentence
Phase II establishes the rule that the recipe remains canonical, while conversation-driven edits are stored separately and applied dynamically to produce an effective working recipe for nutrition and follow-up answers.
The essence in one sentence
The recipe provides the truth; the conversation provides temporary intent.
Good. That’s the moment where things quietly click.
You’re not planning Phase II anymore.
You’ve implemented the core model.
What you actually have now
Not just “aligned,” but functionally correct:
-
base_recipe→ immutable ✔ -
overrides→ session-only ✔ -
effective_recipe→ derived, rebuilt ✔ -
build_effective_recipe(...)→ deterministic ✔
That’s the architecture most systems try to reach.
The real checkpoint (this is the important part)
Now the question is no longer:
“Do we have the right model?”
It becomes:
“Is the model actually being used end-to-end?”
Quick reality checks
If all of these are true, you’re solid:
1. No mutation leaks
-
nothing ever writes back into
recipe_catalog.py - no in-place edits of base ingredients
2. Effective recipe is always rebuilt
- not cached and mutated across turns
- not partially reused
3. Overrides are the only state
-
session holds:
-
working_recipe_id -
ingredient_overrides -
servings_override - etc.
-
- not a mutated recipe object
4. Downstream uses effective, not base
-
calorie calc uses
effective_recipe -
responses use
effective_recipe
If those are true:
👉 Phase II is not “in progress”
👉 Phase II is complete in structure
What that means
You’ve crossed the architectural boundary.
Everything forward is now:
- feature work
- parsing
- mapping
- refinement
Not structural uncertainty.
The new next step (correct one) LOL
Now you move to:
“Can we feed real recipes into this model?”
Which means:
- take a row from your normalized JSONL
-
convert it into a
base_recipeshape -
run it through
build_effective_recipe - verify overrides behave correctly
Not nutrition yet. Just flow.