terpene_engine

Terpene Engine – Cannabis phytochemistry modulation layer.

Loads terpene profiles and strain definitions from terpene_profiles.yaml, computes composite NCM deltas per strain, resolves entourage synergies, and provides the sativa/indica gradient interpolation used by the cascade engine for bipolar ENDOCANNABINOID_DRIFT staging.

# πŸ”₯ the stoner goddess gets her pharmacology right πŸ’€

class terpene_engine.TerpeneProfile(name, polarity, boiling_point_c, aroma, ncm_deltas, flavor_mods, effects)[source]

Bases: object

A single cannabis terpene with its pharmacological and sensory properties.

Immutable record describing one aromatic terpene: its sativa/indica polarity, the temperature at which it volatilises, an aroma blurb, the parsed ncm_deltas (neurochemical-modulation shifts), a dict of 11D gustatory flavor_mods, and a human-readable effects string. Instances are constructed by TerpeneEngine._ensure_loaded from the terpenes section of terpene_profiles.yaml (the ncm_deltas field is produced by running each YAML delta string through parse_delta_string). They are looked up via TerpeneEngine.get_terpene and consumed by TerpeneEngine.compute_strain_effect when weighting a strain.

Parameters:
name: str
polarity: float
boiling_point_c: float
aroma: str
ncm_deltas: Dict[str, float]
flavor_mods: Dict[str, float]
effects: str
class terpene_engine.StrainProfile(name, strain_gradient, classification, thc_pct, description, terpene_weights)[source]

Bases: object

A cannabis strain described by its terpene composition and gradient.

Immutable record for one strain: its strain_gradient position on the indica-to-sativa axis (0.0-1.0), a coarse classification label, average THC percentage, a description, and a terpene_weights mapping of terpene name to fractional weight. Instances are built by TerpeneEngine._ensure_loaded from the strains section of terpene_profiles.yaml, retrieved by TerpeneEngine.get_strain, ranked by TerpeneEngine.find_strain_by_gradient, and read by TerpeneEngine.compute_strain_effect to drive the per-strain NCM math.

Parameters:
name: str
strain_gradient: float
classification: str
thc_pct: float
description: str
terpene_weights: Dict[str, float]
class terpene_engine.StrainEffect(strain_name, strain_gradient, composite_deltas, entourage_bonuses, total_deltas, flavor_shifts, dominant_terpene, pole_label, active_entourage_rules)[source]

Bases: object

The fully computed pharmacological effect of a single cannabis strain.

Result bundle produced by TerpeneEngine.compute_strain_effect: it carries the strain name and gradient alongside the composite_deltas (weighted sum of every terpene’s NCM shifts), the entourage_bonuses contributed by fired synergy rules, the merged-and-saturated total_deltas, the aggregate 11D flavor_shifts, the dominant terpene, a sativa/indica/hybrid pole_label, and the list of entourage rules that activated. This is a derived, read-mostly value object; its to_dict method renders it for downstream consumers and serialisation.

Parameters:
strain_name: str
strain_gradient: float
composite_deltas: Dict[str, float]
entourage_bonuses: Dict[str, float]
total_deltas: Dict[str, float]
flavor_shifts: Dict[str, float]
dominant_terpene: str
pole_label: str
active_entourage_rules: List[str]
to_dict()[source]

Render this strain effect as a compact, rounded JSON-friendly dict.

Serialises the most useful fields of the StrainEffect for logging or transport: the strain name, the rounded gradient and pole label, the dominant terpene, the merged total_deltas and flavor_shifts (each value rounded to keep the payload readable), and the names of the entourage rules that fired. Note that composite_deltas and entourage_bonuses are intentionally omitted in favour of the already-merged total_deltas. Pure transformation over self with no side effects. Invoked on instances returned by TerpeneEngine.compute_strain_effect; no other module imports this class in the repo yet.

Return type:

dict

Returns:

A dict with keys strain, gradient, pole, dominant_terpene, deltas, flavor_shifts, and entourage_rules.

class terpene_engine.TerpeneEngine(yaml_path=None)[source]

Bases: object

Cannabis phytochemistry computation engine.

Loads terpene profiles and strains from YAML, computes composite NCM effects, resolves entourage synergies, and provides the bipolar sativa/indica gradient for cascade interpolation.

Parameters:

yaml_path (Optional[str])

__init__(yaml_path=None)[source]

Initialize the instance.

Parameters:

yaml_path (Optional[str]) – Path to terpene_profiles.yaml. Defaults to same directory as this module.

Return type:

None

get_terpene(name)[source]

Look up a single terpene profile by name, case-insensitively.

Normalises the requested name to the upper-cased, underscore-joined key convention used in the loaded table and returns the matching TerpeneProfile (or None when absent). Triggers _ensure_loaded first so the YAML is hydrated on demand; otherwise it is a pure dict read with no side effects. Defined on TerpeneEngine, which is not yet imported elsewhere in the repo, so it is reached only via a constructed engine instance (the module is referenced by name in the output header that scrape_leafly.py writes, but is not yet imported by other code).

Parameters:

name (str) – Terpene name; spaces are treated as underscores and case is ignored.

Return type:

Optional[TerpeneProfile]

Returns:

The matching TerpeneProfile, or None if no such terpene.

get_strain(name)[source]

Look up a single strain profile by name, case- and separator-insensitively.

Normalises the requested name by upper-casing and folding both spaces and hyphens to underscores (so "Blue Dream" and "blue-dream" resolve to the same key) and returns the matching StrainProfile or None. Calls _ensure_loaded to hydrate the YAML on first use; otherwise a pure dict read. Used internally by compute_strain_effect to resolve the strain it is asked to compute, and otherwise reached via a constructed TerpeneEngine instance since the module has no other importers in the repo yet.

Parameters:

name (str) – Strain name; spaces and hyphens are folded to underscores and case is ignored.

Return type:

Optional[StrainProfile]

Returns:

The matching StrainProfile, or None if no such strain.

list_terpenes()[source]

Return every loaded terpene name in sorted order.

Convenience accessor that hydrates the YAML via _ensure_loaded and returns an alphabetically sorted list of the keys in self._terpenes, giving callers a quick catalogue of the available terpenes. Pure read with no side effects beyond the lazy load. Reached only through a constructed TerpeneEngine instance, as the module is not imported elsewhere in the repo yet.

Return type:

List[str]

Returns:

Sorted list of terpene names.

list_strains()[source]

Return every loaded strain name, sorted by a stable string key.

Convenience accessor that hydrates the YAML via _ensure_loaded and returns the keys of self._strains sorted with str as the sort key, guarding against the case where the YAML loader coerces purely numeric strain names into ints (which would otherwise be unsortable against strings). Pure read apart from the lazy load. Reached only through a constructed TerpeneEngine instance, as nothing else in the repo imports the module yet.

Return type:

List[str]

Returns:

List of strain names sorted by their string form.

compute_strain_effect(strain_name)[source]

Compute the full pharmacological effect of a cannabis strain.

Resolves terpene weights to composite NCM deltas, applies entourage synergy rules, computes flavor axis shifts, and determines the sativa/indica pole label.

Parameters:

strain_name (str) – Name of the strain to compute.

Return type:

Optional[StrainEffect]

Returns:

StrainEffect dataclass or None if strain not found.

compute_gradient_blend(gradient)[source]

Interpolate between sativa and indica NCM pole signatures.

Used by the cascade engine to lerp ENDOCANNABINOID_DRIFT stage deltas based on strain_gradient position.

Parameters:

gradient (float) – 0.0 (pure indica) to 1.0 (pure sativa).

Return type:

Dict[str, float]

Returns:

Dict of NCM node -> delta value, interpolated between poles.

get_cadence_state(gradient)[source]

Return the appropriate cadence state for a strain gradient.

Parameters:

gradient (float) – 0.0 (indica) to 1.0 (sativa).

Returns:

β€˜stoned_indica’, β€˜stoned’, or β€˜stoned_sativa’.

Return type:

str

get_pole_info(pole)[source]

Return a copy of the raw pole definition for sativa or indica.

Hydrates the YAML via _ensure_loaded and returns a shallow copy of the named pole’s entry from self._poles (typically holding its ncm_signature and related metadata), or an empty dict when the pole is absent. The copy keeps callers from mutating the engine’s loaded state. The same ncm_signature data is consumed internally by compute_gradient_blend to interpolate between poles. Reached only via a constructed TerpeneEngine instance, as the module has no other importers in the repo yet.

Parameters:

pole (str) – Pole key, normally "sativa" or "indica".

Return type:

Dict[str, Any]

Returns:

A shallow copy of the pole definition dict, or an empty dict.

find_strain_by_gradient(target_gradient, n=3)[source]

Find the N strains closest to a target gradient value.

Parameters:
  • target_gradient (float) – Desired sativa/indica position.

  • n (int) – Number of results to return.

Return type:

List[StrainProfile]

Returns:

List of StrainProfile sorted by distance to target.