flavor_engine

Vape Cart Flavor Engine — 11D gustatory interpolation and NCM coupling.

Implements the 6-phase blend pipeline:
  1. Raw weighted vector summation

  2. Interaction matrix corrections (suppression/synergy/cross-modal)

  3. Temporal phase reconstruction → TDS string

  4. NCM cascade computation (Hill saturation + receptor synergies)

  5. Nearest-neighbor classification + novelty score

  6. Derived metrics (palatability, reward_density, cling, nostalgia)

Also: morph, temperature modification, cascade trigger detection, emergence.

class flavor_engine.FlavorProfile(name, vector, delta_str, temporal, norimaki_inv=None, retronasal=0.5, category='other')[source]

Bases: object

A single named flavour entry loaded from the vape-cart catalogue.

Holds the immutable sensory description of one flavour: its 11D gustatory vector, a compact NCM (neurochemical-modulation) delta_str parsed lazily by ncm_delta_parser, a temporal envelope dict (onset/peak/decay), optional Norimaki inverse coordinates, the retronasal fraction, and a coarse category. Instances are constructed by FlavorEngine._ensure_loaded from the parsed vape_cart.yaml and are also produced fresh by FlavorEngine.apply_temperature to represent a temperature-shifted variant. Consumed by the blend pipeline and surfaced to the bot via tools/flavor_tool.py (the lookup action serialises a profile to JSON).

Parameters:
name: str
vector: List[float]
delta_str: str
temporal: Dict[str, Any]
norimaki_inv: List[float] | None = None
retronasal: float = 0.5
category: str = 'other'
property dominant_axis: str

Name of the strongest gustatory axis in this flavour’s vector.

Scans the 11D vector and returns the label of the axis with the largest magnitude, giving a quick one-word characterisation of what the flavour tastes of most. Pure computation over self.vector and the module-level AXES list with no side effects. Called by tools/flavor_tool.py in the lookup action to enrich the JSON it returns to the bot.

Returns:

The AXES label of the highest-valued component.

Return type:

str

class flavor_engine.CompositeResult(vector, raw_vector, ncm_deltas, tds_string, temporal_phases, derived_metrics, nearest_flavor, nearest_similarity, novelty_score, emergence_flags, cascade_triggers, attractor=None, ingredients=<factory>)[source]

Bases: object

Full output bundle of a blend or morph computation.

Aggregates every artefact produced by FlavorEngine.blend (and therefore FlavorEngine.morph, which delegates to it): the interaction-corrected and raw 11D vectors, the resolved NCM delta dict, the temporal-dominance string and its underlying phase list, the derived hedonic metrics, the nearest known flavour with its cosine similarity and the complementary novelty score, plus any triggered emergence flags, cascade IDs, and attractor basin. The to_dict method renders a rounded JSON-friendly view; tools/flavor_tool.py consumes the dataclass directly (reading ncm_deltas to inject limbic deltas) and serialises it for the bot.

Parameters:
vector: List[float]
raw_vector: List[float]
ncm_deltas: Dict[str, float]
tds_string: str
temporal_phases: List[Dict[str, Any]]
derived_metrics: Dict[str, float]
nearest_flavor: str
nearest_similarity: float
novelty_score: float
emergence_flags: List[str]
cascade_triggers: List[str]
attractor: str | None = None
ingredients: List[Dict[str, Any]]
to_dict()[source]

Render this result as a rounded, JSON-serialisable dictionary.

Produces a compact view of the composite for transport to the bot: vector components and NCM deltas are rounded to three decimals and keys are renamed to short forms (metrics, nearest, similarity, novelty, cascades). Pure transformation of self with no side effects. Called by tools/flavor_tool.py (the blend and morph actions) which wraps the output in json.dumps for the tool response.

Returns:

A flat dictionary of rounded vectors, deltas, the TDS string, temporal phases, derived metrics, nearest-flavour info, and the emergence/cascade/attractor fields.

Return type:

dict

class flavor_engine.FlavorEngine(yaml_path=None)[source]

Bases: object

Stateless-per-call 11D gustatory computation engine for the vape cart.

Loads the vape_cart.yaml catalogue (flavours, interaction matrix, temporal/temperature/preparation models, NCM synergy rules, emergence rules, and attractor basins) on first use and exposes the blend pipeline plus flavour lookups. The engine is the analytical core behind tools/flavor_tool.py, which instantiates a fresh FlavorEngine per tool call and routes the bot’s list / lookup / blend / morph actions to list_flavors, get_flavor, blend, and morph. It leans on utils.cosine for similarity and lazily imports ncm_delta_parser for delta strings and neurochemical node names; it does no Redis, network, or event-bus work itself (those side effects live in the tool wrapper).

Parameters:

yaml_path (Optional[str])

__init__(yaml_path=None)[source]

Construct an engine bound to a catalogue path, loading nothing yet.

Records where vape_cart.yaml lives (defaulting to the copy beside this module) and sets up empty caches; the YAML is not read until the first public call triggers _ensure_loaded, so construction is cheap and side-effect free. Instantiated per tool invocation by tools/flavor_tool.py.

Parameters:

yaml_path (Optional[str]) – Path to the vape-cart catalogue YAML. When None the bundled vape_cart.yaml next to this file is used.

get_flavor(name)[source]

Look up one flavour profile by name, case- and space-insensitively.

Ensures the catalogue is loaded, then normalises name to the catalogue key form (uppercased with spaces replaced by underscores) and returns the matching FlavorProfile or None if absent. Called by tools/flavor_tool.py for the bot’s lookup action.

Parameters:

name (str) – A flavour name in any casing, e.g. "breast milk" or "BREAST_MILK".

Returns:

The matching profile, or None if the name is not in the catalogue.

Return type:

Optional[FlavorProfile]

list_flavors()[source]

Return the sorted catalogue keys of every loaded flavour.

Ensures the YAML is parsed, then returns the flavour names (catalogue-key form) in alphabetical order. Called by tools/flavor_tool.py for the bot’s list action, which wraps the names plus a count in JSON.

Returns:

Alphabetically sorted flavour catalogue keys; empty if the catalogue failed to load.

Return type:

List[str]

blend(recipe)[source]

Run the full 6-phase blend pipeline.

Parameters:

recipe (List[Dict[str, Any]]) – Each dict: {flavor: str, weight: float, prep: str?, temp_c: float?}

Return type:

CompositeResult

morph(flavor_a, flavor_b, t=0.5)[source]

Interpolate between two flavours, returning the blend at fraction t.

Clamps t to [0, 1] and delegates to blend with a two-item recipe weighted 1 - t for flavor_a and t for flavor_b, so t = 0 yields flavour A, t = 1 yields flavour B, and intermediate values cross-fade through the full six-phase pipeline (including all NCM and emergence side effects of blend). Called by tools/flavor_tool.py for the bot’s morph action.

Parameters:
  • flavor_a (str) – Name of the start flavour (weight 1 - t).

  • flavor_b (str) – Name of the end flavour (weight t).

  • t (float) – Interpolation fraction; clamped into [0, 1].

Returns:

The blended composite at the requested point.

Return type:

CompositeResult

apply_temperature(profile, temp_c)[source]

Return a temperature-shifted copy of a flavour profile.

Runs the private temperature model (_apply_temperature) over a copy of the profile’s vector and packages the result as a fresh FlavorProfile named "<name>@<temp_c>C", leaving the original untouched. The retronasal scale computed internally is discarded here (it only matters during a weighted blend). This public helper has no in-repo callers found via grep (the blend path uses the private _apply_temperature directly); it is exposed for external/programmatic use.

Parameters:
  • profile (FlavorProfile) – The source flavour to transform.

  • temp_c (float) – Serving temperature in degrees Celsius.

Returns:

A new profile carrying the temperature-adjusted vector and the original’s delta string, temporal envelope, and metadata.

Return type:

FlavorProfile