foley.agent.decompose#

decompose_context — narrative passage → a sparse, salience-ranked event list.

The moat of the SELECT stage (report 05 §2): turning prose into a *tastefully sparse, correctly-diegetic* SoundEvent list is the hard, defensible part — not the CLAP encoder. Two impls sit behind the Decomposer seam:

  • KeywordDecomposer — the deterministic default and the hermetic CI fake: a built-in cue lexicon, zero dependencies, same passage → identical list.

  • AnthropicDecomposer — the LLM-backed impl behind the foley[agent] extra; anthropic is imported lazily inside .decompose only, so import foley stays dol-only.

decompose_context() is the pure tool wrapper (Python-API == agent == future-MCP surface): it resolves the default decomposer, calls it, and records the GenAI span on the real path.

Functions

decompose_context(context, *[, max_events, ...])

Decompose a passage into <= max_events sparse SoundEvents.

Classes

AnthropicDecomposer(*[, client, model, ...])

LLM-backed decomposer (foley[agent]): Claude → a structured event list.

KeywordDecomposer()

Deterministic cue-lexicon decomposer — the zero-dependency default and CI fake.

class foley.agent.decompose.AnthropicDecomposer(*, client=None, model='claude-opus-4-8', max_tokens=2000)[source]#

Bases: object

LLM-backed decomposer (foley[agent]): Claude → a structured event list.

anthropic is imported lazily inside decompose() so import foley stays dol-only. Stashes the returned Message on self.last_response so decompose_context() can record the GenAI span (token usage / model / stop_reason). Model, thinking, and structured-output shape follow the claude-api conventions (claude-opus-4-8, adaptive thinking — never budget_tokens).

decompose(context, *, max_events=6, seconds=None)[source]#

Call Claude and round-trip each event through SoundEvent.from_dict().

Return type:

list[SoundEvent]

class foley.agent.decompose.KeywordDecomposer[source]#

Bases: object

Deterministic cue-lexicon decomposer — the zero-dependency default and CI fake.

Scans the passage against _CUE_LEXICON, emits one SoundEvent per matched cue in first-appearance order (so salience descends with reading order), dedupes by canonical query, and truncates to max_events (the sparse density budget). No RNG, no network, no anthropic — same passage → identical list.

decompose(context, *, max_events=6, seconds=None)[source]#

Return <= max_events deterministic SoundEvents for context.

Parameters:
  • context (str) – The narrative passage.

  • max_events (int) – The sparse density cap (the salience budget).

  • seconds (Optional[float]) – Accepted for signature parity (the per-second density window is a later refinement); ignored here.

Return type:

list[SoundEvent]

foley.agent.decompose.decompose_context(context, *, max_events=6, seconds=None, decomposer=None, _span=None)[source]#

Decompose a passage into <= max_events sparse SoundEvents.

The pure SELECT tool (Python-API == agent == future-MCP surface): resolves the default decomposer when decomposer is None, calls it, and records the GenAI span on the real path (the fake’s last_response is None → no-op).

Parameters:
  • context (str) – The narrative passage.

  • max_events (int) – The sparse density cap.

  • seconds (Optional[float]) – Optional passage duration (density-window hint; forwarded, else ignored).

  • decomposer (Optional[Decomposer]) – An injected Decomposer (the DI seam); defaults to _default_decomposer().

  • _span – Internal — the obs span handle find() opens for GenAI recording.

Return type:

list[SoundEvent]