> built 2026-10-16 01:23 UTC from e157a1c (main) · foley 0.0.41. Details: build_info.json

# index.html.md

<!-- generated by epythet -->

# foley

Find (or generate) the **right sound effect for a moment of narration** — and weave it in.

foley is a **retrieval-first façade for sound effects**: one simple surface over many
sound *sources* (your own library, service APIs, and generative-AI models), a searchable
*index* of every sound (by keyword **and** meaning), an *agent* that picks the right
sound for a narrative context, and a *compositor* that places it under the voice.

It’s the SFX sibling of [`arioso`](https://github.com/thorwhalen/arioso) (a unified
façade over AI music-generation backends): same discipline — one entry function,
config-driven plugin adapters, a unified vocabulary translated per-backend, zero
required core deps with lazy optional-deps — but centered on **search** rather than
generation, with generation as just one of several sources.

> **Status: v1.** All four stages — source, index, select, weave — plus the MCP server
> and the licensing/provenance, evaluation, and observability layers are implemented
> (Epic #13 complete). The API below is live; see the [roadmap]()
> for what’s next. Follow along in [`misc/docs/`]().

## The idea

```python
import foley

# The headline — right sounds for a narrative context:
# decompose the passage into salient sound events → search → verify → decide
candidates = foley.find("She pushed open the heavy oak door; rain hammered outside.")

# Direct hybrid search of your library (text query or a reference clip)
hits = foley.search("distant thunder rumble", k=10)  # commercial use is the default

# Generate a sound when nothing fits (arioso-style; pluggable backends)
# needs foley[stable-audio] and the gated stabilityai/stable-audio-open-1.0 weights
# (accept its license on Hugging Face, set HF_TOKEN); backend="elevenlabs" needs
# ELEVENLABS_API_KEY and is paid
clip = foley.generate("a single wooden door creak", backend="stable_audio", duration=3)

# Grow the library — ingest auto-tags and embeds every file (captioning is pluggable)
foley.ingest("~/my_sounds/", license="user-owned")  # omit it and rights stay unknown
foley.add_from(
    "freesound", query="ocean waves", license="cc0"
)  # needs FREESOUND_API_KEY

# Compose: place the sounds under the narration (find → plan → weave)
timeline = foley.plan(candidates)  # the editable sound-design plan
result = foley.weave("narration.wav", timeline)  # mastered mix + SDH captions + credits
result.notes  # how placements were timed, e.g. estimates when foley[align] is absent
```

## How it works — four stages

| Stage      | What it does                                                                                    | Built on                                                                                                                           |
|------------|-------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------|
| **Source** | your own files · Freesound (CC0) · generate (Stable Audio Open / ElevenLabs)                    | config-driven adapters, per-sound license tracking                                                                                 |
| **Index**  | probe → tag → caption → embed every sound; hybrid keyword+semantic search                       | PANNs · CLAP · LanceDB (local→cloud via `dol`); captions come from metadata until a learned captioner is plugged in                |
| **Select** | decompose a narrative context → search → verify → generate-or-retrieve                          | CLAP retrieval + LLM decomposition + a verification ladder                                                                         |
| **Weave**  | align to the voice, duck, place, master → mastered mix + editable timeline + captions + credits | word timing (WhisperX transcription with `foley[align]`; otherwise evenly spaced script words; `notes` says which) · LUFS/EBU-R128 |

The selection tools publish as an **MCP server** (via `py2mcp`) so the same capabilities
drive the agent, a CLI, and external hosts.

## For AI agents

foley is **AI-first**: most callers are agents (directly, and via downstream packages like
`braidio` and `nw`). The headline is one call — hand it narration text and it chooses tasteful,
license-clean sounds and (given the audio) weaves them in:

```python
import foley

# Plan only — choose sounds, get an editable timeline + a rationale:
result = foley.score("She pushed open the heavy oak door; rain hammered outside.")
print(result.rationale)

# Plan + render — weave under the actual narration audio:
result = foley.score(segments, audio="narration.wav", commercial_ok=True)
result.weave.audio  # mastered mix   result.weave.captions_vtt   # SDH captions
result.weave.credits  # attribution    result.timeline             # still editable
```

`foley.score(...)` is the stable contract `braidio`/`nw` call. The same surface is available as
**MCP tools** (`foley_score`, `foley_guide`, `foley_search`, `foley_preview`, `foley_weave`, …) —
serve them over stdio (`foley-mcp`) or authenticated HTTP (`foley-mcp --http --token …`).

Ship the **agent kit** into your agent host (a `foley-sound-design` skill, a `/foley-score`
slash command, and a `sound-designer` subagent):

```default
foley agent-install            # → ./.claude/{skills,commands,agents}/…
```

The **one rule** is restraint — most sentences need no sound. The design is grounded in the
research reports below; taste heuristics (layering, ducking, onset, licensing) live in the skill.

## Design & research

foley’s design is grounded in five research reports (unified, cited):

- [Design]() · [Roadmap]()
- [Research reports]() — sound sources, SFX generation, recognition/
  tagging, embeddings/indexing, and the context-retrieval agent
- [Deep-research prompt library]() — 12
  ready-to-run prompts covering every dimension of the project

## Install

```bash
pip install foley                         # core — dol-only, light; `import foley` stays minimal
pip install "foley[audio,clap,index]"     # the usual retrieval stack (CLAP + hybrid index)
foley demo                                # no-download smoke test on the bundled fixture
foley agent-install                       # drop the agent kit into ./.claude (skill + command + subagent)
```

foley is **zero-dep at its core** and grows by capability via optional extras — each imported
lazily, so a bare install stays light. What each adds:

| Extra                                       | Adds                                                               |
|---------------------------------------------|--------------------------------------------------------------------|
| `audio`                                     | numpy/soundfile DSP + LUFS mastering                               |
| `clap`                                      | the CLAP text↔audio retrieval engine                               |
| `index` / `index-sqlite`                    | the hybrid vector+keyword index (LanceDB / sqlite-vec)             |
| `freesound` · `stable-audio` · `elevenlabs` | live retrieve / generate sources                                   |
| `agent` · `local-llm`                       | the SELECT LLM rungs (Claude / a local OpenAI-compatible endpoint) |
| `align` · `weave` · `provenance` · `c2pa`   | word timing (WhisperX), OTIO/rubberband, watermarking, signed C2PA |
| `mcp` · `obs`                               | the MCP server · OpenTelemetry export                              |

**Nothing paid runs unless you ask for it.** Having `ANTHROPIC_API_KEY` set does not switch the SELECT rungs to Claude: pass `llm="anthropic"` to `find()` / `score()` or set `FOLEY_LLM=anthropic` (`llm="local"` uses `FOLEY_LLM_BASE_URL`). By default they run the free deterministic fakes, or your local endpoint if one is configured. `with foley.offline():` (or `FOLEY_OFFLINE=1`) blocks every external source and LLM call foley makes, on every surface, raising `foley.runtime.EgressBlocked` (model weights are never downloaded offline: a model not already cached raises `foley.runtime.ModelNotCached`, naming the command to fetch it while online).

**Every paid call is priced and capped.** `foley.estimate("generate", backend="elevenlabs", duration=3)` says what a call costs before it is made (`None` means unknown, never free). A `find` / `score` / `generate` run stops before the first paid call that would take it past its `Budget(max_usd=...)` (default $1, or $3 when you chose Claude with `llm="anthropic"` / `FOLEY_LLM=anthropic`, since a default `find` with Claude is bounded at about $2.52), and a call of unknown cost needs `Budget(approve_unknown_cost=True)`. Every paid response is kept in `~/.local/share/foley/generations/` before QC, so an identical request is served from there instead of paying twice. What a backend could not honour is never silent: a parameter that changes the result (`seed` on ElevenLabs) raises, and every other drop or clamp is listed in the returned candidate’s `notes`.

`foley.check_requirements()` (and the `foley_capabilities` MCP tool) report what’s installed and
what’s degraded. Full docs: **[thorwhalen.github.io/foley](https://thorwhalen.github.io/foley)**.

## Changelog

Per-release changes are in [CHANGELOG.md]().

## License

MIT

<p class="epythet-aggregates">This documentation as a single file: <a href="foley.md">foley.md</a> (Markdown, for agents).</p>


# _autosummary/foley.agent.decompose.html.md

# 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`](_autosummary/foley.base.html.md#foley.base.SoundEvent) list is the hard, defensible part —
not the CLAP encoder. Two impls sit behind the [`Decomposer`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Decomposer)
seam:

* [`KeywordDecomposer`](_autosummary/foley.agent.decompose.html.md#foley.agent.decompose.KeywordDecomposer) — the deterministic default *and* the hermetic CI fake:
  a built-in cue lexicon, zero dependencies, same passage → identical list.
* [`AnthropicDecomposer`](_autosummary/foley.agent.decompose.html.md#foley.agent.decompose.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()`](_autosummary/foley.agent.decompose.html.md#foley.agent.decompose.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`](_autosummary/foley.agent.decompose.html.md#foley.agent.decompose.decompose_context)(context, \*[, max_events, ...])   | Decompose a passage into `<= max_events` sparse `SoundEvent`s.   |
|------------------------------------------------------------------------------------------------------|------------------------------------------------------------------|

### Classes

| [`AnthropicDecomposer`](_autosummary/foley.agent.decompose.html.md#foley.agent.decompose.AnthropicDecomposer)(\*[, client, model, ...])   | LLM-backed decomposer (`foley[agent]`): Claude → a structured event list.       |
|--------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------|
| [`KeywordDecomposer`](_autosummary/foley.agent.decompose.html.md#foley.agent.decompose.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)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

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

`anthropic` is imported lazily inside [`decompose()`](_autosummary/foley.agent.decompose.html.md#foley.agent.decompose.AnthropicDecomposer.decompose) so `import foley` stays
dol-only. Stashes the returned `Message` on `self.last_response` so
[`decompose_context()`](_autosummary/foley.agent.decompose.html.md#foley.agent.decompose.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)

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

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`SoundEvent`](_autosummary/foley.base.html.md#foley.base.SoundEvent)]

### *class* foley.agent.decompose.KeywordDecomposer

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#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)

Return `<= max_events` deterministic `SoundEvent`s for `context`.

* **Parameters:**
  * **context** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The narrative passage.
  * **max_events** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The sparse density cap (the salience budget).
  * **seconds** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`float`](https://docs.python.org/3/builtins/functions.html#float)]) – Accepted for signature parity (the per-second density window is a
    later refinement); ignored here.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`SoundEvent`](_autosummary/foley.base.html.md#foley.base.SoundEvent)]

### foley.agent.decompose.decompose_context(context, , max_events=6, seconds=None, decomposer=None, \_span=None)

Decompose a passage into `<= max_events` sparse `SoundEvent`s.

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`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The narrative passage.
  * **max_events** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The sparse density cap.
  * **seconds** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`float`](https://docs.python.org/3/builtins/functions.html#float)]) – Optional passage duration (density-window hint; forwarded, else ignored).
  * **decomposer** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Decomposer`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Decomposer)]) – An injected [`Decomposer`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Decomposer) (the DI seam);
    defaults to `_default_decomposer()`.
  * **\_span** – Internal — the obs span handle `find()` opens for GenAI recording.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`SoundEvent`](_autosummary/foley.base.html.md#foley.base.SoundEvent)]


# _autosummary/foley.agent.html.md

# foley.agent

The SELECT stage — the search-agent that finds the right sound for a context (#7).

The headline capability: a narrative *context* → verified, license-clean sound
candidates, and the sparse plan that places them. The loop (report 05 §5):

> decompose_context → (per event) refine_query / search_sounds
> : → gate_candidates (fail-closed license gate, FIRST)
>   → verify_match (clap → listen → judge ladder)
>   → decide (retrieve-vs-generate, the single branch)
>   → place_in_timeline → a sparse SoundDesignTimeline

Design discipline (mirrors `foley.index`): this package is **dol-only at import** —
the LLM rungs sit behind the [`Decomposer`](_autosummary/foley.agent.html.md#foley.agent.Decomposer) / [`Judge`](_autosummary/foley.agent.html.md#foley.agent.Judge) / [`Refiner`](_autosummary/foley.agent.html.md#foley.agent.Refiner)
protocols with deterministic fakes as defaults, and `anthropic` (the `foley[agent]`
extra) is imported lazily inside the real impls only. So `foley.find("a paragraph")`
works out of the box and the whole loop is exercisable in CI with no network / key /
heavy dependency. Every `find()` opens one `foley.obs` run scope, so search +
verify + generate aggregate into a single reproducible run-manifest.

### Functions

| [`find`](_autosummary/foley.agent.html.md#foley.agent.find)(context, \*[, max_events, seconds, ...])       | The headline: a narrative context → verified, license-clean sound candidates.             |
|------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------|
| [`plan`](_autosummary/foley.agent.html.md#foley.agent.plan)(candidates, \*[, transcript])                  | Fold verified candidates into the SPARSE `SoundDesignTimeline` (the SELECT→WEAVE bridge). |
| [`decompose_context`](_autosummary/foley.agent.html.md#foley.agent.decompose_context)(context, \*[, max_events, ...])   | Decompose a passage into `<= max_events` sparse `SoundEvent`s.                            |
| [`refine_query`](_autosummary/foley.agent.html.md#foley.agent.refine_query)(query, \*[, n, hint, refiner, \_span]) | Expand `query` into up to `n` paraphrases for multi-query retrieval.                      |
| [`search_sounds`](_autosummary/foley.agent.html.md#foley.agent.search_sounds)(queries, \*[, k, library, filters])   | Hybrid search for one query, or a multi-query RRF-merge (the SELECT retrieval tool).      |
| [`verify_match`](_autosummary/foley.agent.html.md#foley.agent.verify_match)(event, candidate, \*[, level, ...])    | Verify `candidate` against `event` up to rung `level` (AND-confirming ladder).            |
| [`gate_candidates`](_autosummary/foley.agent.html.md#foley.agent.gate_candidates)(candidates, intended_use)           | Fail-closed license gate — run BEFORE verification/ranking (invariant #3).                |
| [`decide`](_autosummary/foley.agent.html.md#foley.agent.decide)(event, kept, verified, \*, ...)              | The single generate-vs-retrieve branch — a PURE function (report 05 §4).                  |
| [`generate_sound`](_autosummary/foley.agent.html.md#foley.agent.generate_sound)(query, \*[, backend, library])       | Generate a clip for `query` (the fallback tool) — a thin wrapper over `foley.generate`.   |
| [`place_in_timeline`](_autosummary/foley.agent.html.md#foley.agent.place_in_timeline)(clip, \*[, onset, gain, ...])     | Emit the SPARSE per-item plan subset (`clip_ref·onset·gain·layer·loop`) — no more.        |

### Classes

| [`Decomposer`](_autosummary/foley.agent.html.md#foley.agent.Decomposer)(\*args, \*\*kwargs)                    | Narrative context → a sparse, salience-ranked, diegetic-tagged event list.                                                     |
|----------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------|
| [`Judge`](_autosummary/foley.agent.html.md#foley.agent.Judge)(\*args, \*\*kwargs)                         | One rung of the verify ladder: does this candidate match this event? (report 10 §4.2).                                         |
| [`Refiner`](_autosummary/foley.agent.html.md#foley.agent.Refiner)(\*args, \*\*kwargs)                       | One event query → 2–4 paraphrases/expansions (query-expansion for retrieval).                                                  |
| [`Budget`](_autosummary/foley.agent.html.md#foley.agent.Budget)([max_refine_loops, max_generations, ...])  | Bounded-cost accounting: per-event loop counts, and a cumulative dollar cap (#57).                                             |
| [`Decision`](_autosummary/foley.agent.html.md#foley.agent.Decision)(action[, candidate, reason])             | The tiny result of [`decide()`](_autosummary/foley.agent.html.md#foley.agent.decide); `reason` feeds the refine hint + the audit Step. |
| [`DecideAction`](_autosummary/foley.agent.html.md#foley.agent.DecideAction)(\*values)                            | What [`decide()`](_autosummary/foley.agent.html.md#foley.agent.decide) chose for one event (the single branch's outcomes).             |
| [`KeywordDecomposer`](_autosummary/foley.agent.html.md#foley.agent.KeywordDecomposer)()                               | Deterministic cue-lexicon decomposer — the zero-dependency default and CI fake.                                                |
| [`AnthropicDecomposer`](_autosummary/foley.agent.html.md#foley.agent.AnthropicDecomposer)(\*[, client, model, ...])     | LLM-backed decomposer (`foley[agent]`): Claude → a structured event list.                                                      |
| [`KeywordRefiner`](_autosummary/foley.agent.html.md#foley.agent.KeywordRefiner)()                                  | Deterministic template-expansion refiner — the default and CI fake.                                                            |
| [`AnthropicRefiner`](_autosummary/foley.agent.html.md#foley.agent.AnthropicRefiner)(\*[, client, model, max_tokens]) | LLM-backed refiner (`foley[agent]`): Claude → a paraphrase list.                                                               |
| [`ClapJudge`](_autosummary/foley.agent.html.md#foley.agent.ClapJudge)()                                       | The zero-config `clap` rung: gate on the retrieval cosine (no ML, no network).                                                 |
| [`StringOverlapJudge`](_autosummary/foley.agent.html.md#foley.agent.StringOverlapJudge)(\*[, threshold])               | Deterministic stand-in for the `listen`/`judge` rungs (the hermetic CI fake).                                                  |
| [`AnthropicJudge`](_autosummary/foley.agent.html.md#foley.agent.AnthropicJudge)(\*[, client, model, max_tokens])   | LLM arbiter for the `judge` rung (`foley[agent]`): Claude → a `Verdict`.                                                       |
| [`AudioLMJudge`](_autosummary/foley.agent.html.md#foley.agent.AudioLMJudge)(\*[, pipeline, model, ...])          | The audio-LM `listen` rung (`foley[fit]`): Qwen2-Audio "does this contain {event}?".                                           |

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

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

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

`anthropic` is imported lazily inside [`decompose()`](_autosummary/foley.agent.decompose.html.md#module-foley.agent.decompose) so `import foley` stays
dol-only. Stashes the returned `Message` on `self.last_response` so
[`decompose_context()`](_autosummary/foley.agent.html.md#foley.agent.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)

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

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`SoundEvent`](_autosummary/foley.base.html.md#foley.base.SoundEvent)]

### *class* foley.agent.AnthropicJudge(, client=None, model='claude-opus-4-8', max_tokens=500)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

LLM arbiter for the `judge` rung (`foley[agent]`): Claude → a `Verdict`.

`anthropic` imported lazily inside [`judge()`](_autosummary/foley.agent.html.md#foley.agent.AnthropicJudge.judge). Stashes the `Message` on
`self.last_response` for the GenAI span.

#### judge(event, candidate, , level=VerifyLevel.judge)

Call Claude to arbitrate the match; returns a `Verdict` at `level`.

* **Return type:**
  [`Verdict`](_autosummary/foley.base.html.md#foley.base.Verdict)

### *class* foley.agent.AnthropicRefiner(, client=None, model='claude-opus-4-8', max_tokens=500)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

LLM-backed refiner (`foley[agent]`): Claude → a paraphrase list.

`anthropic` imported lazily inside [`refine()`](_autosummary/foley.agent.refine.html.md#module-foley.agent.refine). Stashes the `Message` on
`self.last_response` for the GenAI span.

#### refine(query, , n=3, hint=None)

Call Claude for `n` paraphrases; the original `query` is always first.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]

### *class* foley.agent.AudioLMJudge(, pipeline=None, model='Qwen/Qwen2-Audio-7B-Instruct', max_tokens=300, tau=0.5)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

The audio-LM `listen` rung (`foley[fit]`): Qwen2-Audio “does this contain {event}?”.

`transformers`/`torch` are imported lazily inside [`judge()`](_autosummary/foley.agent.html.md#foley.agent.AudioLMJudge.judge); an injected
`pipeline` (the test seam, mirroring the Stable-Audio fake-pipeline seam) drives the
whole byte-decode → prompt → P(yes) → `Verdict` path with no torch. Stashes
`self.model` + `self.last_response` so `verify_match`’s GenAI span stays
informative (same getattr contract [`AnthropicJudge`](_autosummary/foley.agent.html.md#foley.agent.AnthropicJudge) uses).

#### judge(event, candidate, , level=VerifyLevel.listen)

Listen to the clip and return the AQAScore `Verdict` (`P(yes) ≥ tau`).

* **Return type:**
  [`Verdict`](_autosummary/foley.base.html.md#foley.base.Verdict)

### *class* foley.agent.Budget(max_refine_loops=1, max_generations=1, allow_generate=True, max_usd=1.0, approve_unknown_cost=None, \_refines=0, \_gens=0, \_spent_usd=0.0, \_unknown_calls=0, \_lock=<factory>)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Bounded-cost accounting: per-event loop counts, and a cumulative dollar cap (#57).

Two kinds of bound:

* **Per event** — `max_refine_loops` / `max_generations`: the loop calls
  [`refine_ok()`](_autosummary/foley.agent.html.md#foley.agent.Budget.refine_ok) / [`gen_ok()`](_autosummary/foley.agent.html.md#foley.agent.Budget.gen_ok) to test, then [`spend_refine()`](_autosummary/foley.agent.html.md#foley.agent.Budget.spend_refine) /
  [`spend_gen()`](_autosummary/foley.agent.html.md#foley.agent.Budget.spend_gen) to charge; [`reset()`](_autosummary/foley.agent.html.md#foley.agent.Budget.reset) zeroes them at each event.
* **Per run, cumulative** — `max_usd` (default $1): every paid call (generation,
  paid LLM rung) is checked with [`authorize()`](_autosummary/foley.agent.html.md#foley.agent.Budget.authorize) before it is made and recorded with [`charge()`](_autosummary/foley.agent.html.md#foley.agent.Budget.charge)
  after. [`reset()`](_autosummary/foley.agent.html.md#foley.agent.Budget.reset) never clears it, so the cap holds across the whole
  `find` / `score` run. A call whose cost is unknown (estimate `None`) is
  refused unless `approve_unknown_cost` (or `$FOLEY_APPROVE_UNKNOWN_COST=1`).

#### authorize(estimate_usd, , what)

Alias of [`check()`](_autosummary/foley.agent.html.md#foley.agent.Budget.check) (kept for callers that only test affordability).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### charge(usd)

Record a call’s cost (`None` = unknown: counted, not summed).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### check(estimate_usd, , what)

Raise if this run cannot afford `estimate_usd` (nothing is reserved).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### gen_ok()

Whether a generation fallback is allowed and within budget.

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

#### refine_ok()

Whether another refine→re-retrieve pass is within budget.

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

#### reserve(estimate_usd, , what)

Check, then count `estimate_usd` as spent at once (atomic under a lock).

Reserving before the call is what keeps concurrent calls on one budget (MCP
tools run in a threadpool) from all passing the check before any is charged.
[`settle()`](_autosummary/foley.agent.html.md#foley.agent.Budget.settle) later replaces the reservation with the actual cost, if known.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### reset()

Zero the spend counters so the caps apply *per event*, not per passage.

The `find` loop calls this at the top of each event so one hard event’s
refine/generate spend never starves later events (the documented per-event
semantics).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### settle(reserved_usd, actual_usd)

Replace a reservation with the actual cost (`None` actual: keep the reservation).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### spend_gen()

Charge one generation.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### spend_refine()

Charge one refine loop.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### *property* spent_usd *: [float](https://docs.python.org/3/builtins/functions.html#float)*

Dollars charged so far this run (calls of unknown cost count as 0 here).

### *class* foley.agent.ClapJudge

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

The zero-config `clap` rung: gate on the retrieval cosine (no ML, no network).

`match` iff the candidate’s `clap_score` clears `tau_clap`; `confidence` is
the score clamped to `0..1` so it is comparable to the higher rungs’ 0..1 scores.

#### judge(event, candidate, , level=VerifyLevel.clap, tau_clap=0.35)

Return the `clap`-rung `Verdict` for `candidate`.

* **Return type:**
  [`Verdict`](_autosummary/foley.base.html.md#foley.base.Verdict)

### *class* foley.agent.DecideAction(\*values)

Bases: [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Enum`](https://docs.python.org/3/library/enum.html#enum.Enum)

What [`decide()`](_autosummary/foley.agent.html.md#foley.agent.decide) chose for one event (the single branch’s outcomes).

### *class* foley.agent.Decision(action, candidate=None, reason='')

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

The tiny result of [`decide()`](_autosummary/foley.agent.html.md#foley.agent.decide); `reason` feeds the refine hint + the audit Step.

### *class* foley.agent.Decomposer(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

Narrative context → a sparse, salience-ranked, diegetic-tagged event list.

Turns a story/commentary passage into `<= max_events` `SoundEvent`s,
enforcing the salience/density budget and the anachronism (`era_place`) guard.
The default is the deterministic [`KeywordDecomposer`](_autosummary/foley.agent.decompose.html.md#foley.agent.decompose.KeywordDecomposer);
[`AnthropicDecomposer`](_autosummary/foley.agent.decompose.html.md#foley.agent.decompose.AnthropicDecomposer) is the LLM-backed impl.

### *class* foley.agent.Judge(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

One rung of the verify ladder: does this candidate match this event? (report 10 §4.2).

`level` selects the rung — `clap` (cheap score gate),
`listen` (audio-LM), `judge` (LLM arbitration + scene consistency). The
returned `Verdict` carries `level` == the rung that produced it. Only the
`judge` rung’s real impl calls the LLM.

### *class* foley.agent.KeywordDecomposer

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#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)

Return `<= max_events` deterministic `SoundEvent`s for `context`.

* **Parameters:**
  * **context** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The narrative passage.
  * **max_events** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The sparse density cap (the salience budget).
  * **seconds** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`float`](https://docs.python.org/3/builtins/functions.html#float)]) – Accepted for signature parity (the per-second density window is a
    later refinement); ignored here.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`SoundEvent`](_autosummary/foley.base.html.md#foley.base.SoundEvent)]

### *class* foley.agent.KeywordRefiner

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Deterministic template-expansion refiner — the default and CI fake.

Expands a query into `n` distinct paraphrases via fixed descriptor templates; a
verify-failure `hint` nudges one extra variant. No RNG, no `anthropic` — same
query → identical paraphrase list.

#### refine(query, , n=3, hint=None)

Return up to `n` distinct paraphrases of `query` (the first is `query`).

* **Parameters:**
  * **query** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The event query to expand.
  * **n** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – How many paraphrases to return (2–4 is typical).
  * **hint** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional verify-failure reason to steer re-retrieval.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]

### *class* foley.agent.Refiner(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

One event query → 2–4 paraphrases/expansions (query-expansion for retrieval).

`hint` carries the verify-failure reason so the re-retrieval loop can steer the
next paraphrases. v1 feeds the list into a multi-query RRF search (embedding-fusion
is a later drop-in behind this same seam). The default is the deterministic
[`KeywordRefiner`](_autosummary/foley.agent.refine.html.md#foley.agent.refine.KeywordRefiner).

### *class* foley.agent.StringOverlapJudge(, threshold=0.3)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Deterministic stand-in for the `listen`/`judge` rungs (the hermetic CI fake).

Jaccard token overlap of the event query vs the candidate’s caption + tags. Lets the
full ladder + [`decide()`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.decide) branch run with zero ML/network. The
returned `Verdict` echoes back the requested `level`.

#### judge(event, candidate, , level=VerifyLevel.listen)

Return the token-overlap `Verdict` at the requested `level`.

* **Return type:**
  [`Verdict`](_autosummary/foley.base.html.md#foley.base.Verdict)

### foley.agent.decide(event, kept, verified, , tau_retrieve, budget, loop)

The single generate-vs-retrieve branch — a PURE function (report 05 §4).

Chooses among [`DecideAction`](_autosummary/foley.agent.html.md#foley.agent.DecideAction) from the already-gated (`kept`) and
already-verified (`verified`) sets. It performs no I/O and never calls
`keep`/`search`/`generate` — the [`foley.agent.tools`](_autosummary/foley.agent.tools.html.md#module-foley.agent.tools) loop acts on the
returned [`Decision`](_autosummary/foley.agent.html.md#foley.agent.Decision).

Policy (report 05 §4):

> * a verified clip clearing `tau_retrieve` → `USE` (the best one);
> * verified-but-low-confidence with refine budget → `REFINE` (feed the reason back);
> * a non-diegetic cue, or a diegetic gap with no verified match, with generate budget
>   → `GENERATE`;
> * otherwise → `DROP` (silence), unless a lower-confidence verified clip exists and
>   generation is off, in which case fall back to that best-effort pick.
* **Parameters:**
  * **event** ([`SoundEvent`](_autosummary/foley.base.html.md#foley.base.SoundEvent)) – The event being resolved.
  * **kept** ([`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]) – The license-clean candidates (each `license_ok is True`).
  * **verified** ([`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]) – The subset of `kept` whose verdict matched.
  * **tau_retrieve** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – The confidence threshold for auto-accepting a retrieved clip.
  * **budget** ([`Budget`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.Budget)) – The per-event cost budget.
  * **loop** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The current refine-loop index (for the audit reason).
* **Return type:**
  [`Decision`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.Decision)
* **Returns:**
  A [`Decision`](_autosummary/foley.agent.html.md#foley.agent.Decision).

### foley.agent.decompose_context(context, , max_events=6, seconds=None, decomposer=None, \_span=None)

Decompose a passage into `<= max_events` sparse `SoundEvent`s.

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`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The narrative passage.
  * **max_events** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The sparse density cap.
  * **seconds** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`float`](https://docs.python.org/3/builtins/functions.html#float)]) – Optional passage duration (density-window hint; forwarded, else ignored).
  * **decomposer** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Decomposer`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Decomposer)]) – An injected [`Decomposer`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Decomposer) (the DI seam);
    defaults to `_default_decomposer()`.
  * **\_span** – Internal — the obs span handle `find()` opens for GenAI recording.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`SoundEvent`](_autosummary/foley.base.html.md#foley.base.SoundEvent)]

### foley.agent.find(context, , max_events=6, seconds=None, intended_use=None, backend='auto', verify='listen', stream=False, k=10, tau_retrieve=0.5, tau_clap=0.35, max_refine_loops=1, budget=None, library=None, decomposer=None, judge=None, refiner=None, llm=None)

The headline: a narrative context → verified, license-clean sound candidates.

`decompose → (per event) refine/search → verify_match ladder → decide (with the
fail-closed license gate FIRST) → place` (report 05 §5). Works out of the box —
`foley.find("She pushed open the heavy oak door; rain hammered outside.")` — with
deterministic defaults; every model / threshold / seam is an optional keyword.

* **Parameters:**
  * **context** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The narrative passage.
  * **max_events** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The sparse density cap on decomposed events.
  * **seconds** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`float`](https://docs.python.org/3/builtins/functions.html#float)]) – Optional passage duration (density-window hint; forwarded).
  * **intended_use** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`IntendedUse`](_autosummary/foley.base.html.md#foley.base.IntendedUse)]) – The caller’s rights intent (default:
    [`foley.licensing.DEFAULT_INTENDED_USE`](_autosummary/foley.licensing.html.md#foley.licensing.DEFAULT_INTENDED_USE) — commercial publishing;
    NC / SA material is refused unless you pass a different intent).
  * **backend** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Generation backend for the fallback (`'auto'` → `foley.generate`’s default).
  * **verify** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`VerifyLevel`](_autosummary/foley.base.html.md#foley.base.VerifyLevel)]) – The max verify rung — `'clap'` | `'listen'` | `'judge'`.
  * **stream** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – If `True`, return a generator yielding one `Candidate` per
    resolved event; else return the collected `list`.
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Retrieval shortlist depth per query.
  * **tau_retrieve** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Confidence threshold to auto-accept a retrieved clip.
  * **tau_clap** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – The `clap`-rung gate threshold.
  * **max_refine_loops** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Max refine→re-retrieve passes per event (also the default
    [`Budget`](_autosummary/foley.agent.html.md#foley.agent.Budget)).
  * **budget** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Budget`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.Budget)]) – An explicit [`Budget`](_autosummary/foley.agent.html.md#foley.agent.Budget) (overrides `max_refine_loops`). It is
    also the run’s spend cap: `Budget(max_usd=...)` (default $1; $3 when
    `llm='anthropic'` or `$FOLEY_LLM=anthropic`, whose default run is
    bounded at about $2.52 — [`foley.cost.DEFAULT_MAX_USD_BY_LLM`](_autosummary/foley.cost.html.md#foley.cost.DEFAULT_MAX_USD_BY_LLM)) bounds
    every paid call the run makes — generations and paid LLM rungs —
    cumulatively, and a call of unknown cost needs
    `Budget(approve_unknown_cost=True)` (#57).
  * **library** – Target `SoundLibrary` (default: the process-wide default).
  * **refiner** (*decomposer / judge /*) – Injected DI seams
    ([`Decomposer`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Decomposer) / `Judge` / `Refiner`);
    each defaults to the `llm` provider’s implementation.
  * **llm** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Which LLM the decompose / refine / judge rungs use when not injected —
    `'fake'` | `'local'` | `'anthropic'`. `None` reads `$FOLEY_LLM`,
    then falls back to the free default (a configured local endpoint, else the
    deterministic fake). A key being present never opts in to paid calls.
* **Return type:**
  `Union`[[`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)], [`Iterator`](https://docs.python.org/3/library/typing.html#typing.Iterator)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]]
* **Returns:**
  `list[Candidate]` (`stream=False`) or an `Iterator[Candidate]`
  (`stream=True`) — one verified, license-clean candidate per resolved event.

### foley.agent.gate_candidates(candidates, intended_use)

Fail-closed license gate — run BEFORE verification/ranking (invariant #3).

For each candidate: apply [`keep_sound()`](_autosummary/foley.licensing.html.md#foley.licensing.keep_sound) (any exception ⇒
`False`, fail-closed), record the result on `candidate.license_ok`, and keep
ONLY where it is `True` — a `None` (never gated) or `False` is dropped. This is
the single rights-rejection point; [`verify_match()`](_autosummary/foley.agent.verify.html.md#foley.agent.verify.verify_match) asserts
its survivors are license-clean.

* **Parameters:**
  * **candidates** ([`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]) – The retrieved shortlist (`license_ok` typically `None`).
  * **intended_use** ([`IntendedUse`](_autosummary/foley.base.html.md#foley.base.IntendedUse)) – The caller’s declared rights intent.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]
* **Returns:**
  The license-clean sublist (each with `license_ok is True`), order preserved.

### foley.agent.generate_sound(query, , backend='auto', library=None)

Generate a clip for `query` (the fallback tool) — a thin wrapper over `foley.generate`.

Returns a `Candidate` (`origin=generated`); raises the `GenerationError`
hierarchy on refusal / no-result (caught fail-closed by `_generate_and_reverify()`).

* **Return type:**
  [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)

### foley.agent.place_in_timeline(clip, , onset=None, gain=0.0, layer=Layer.sfx_fg, loop=False)

Emit the SPARSE per-item plan subset (`clip_ref·onset·gain·layer·loop`) — no more.

`onset` stays a SYMBOLIC anchor string (never a resolved offset); mix / processing /
alignment / mastering are WEAVE’s (#8) job, not SELECT’s.

* **Return type:**
  [`TimelineItem`](_autosummary/foley.base.html.md#foley.base.TimelineItem)

### foley.agent.plan(candidates, , transcript=None)

Fold verified candidates into the SPARSE `SoundDesignTimeline` (the SELECT→WEAVE bridge).

One `TimelineItem` per candidate (`onset·gain·layer·loop` only, from its
`SoundEvent`), joined to the run-artifact via `run_manifest_ref` — the
reserved #8 `plan_ref` slot is filled when called inside an active `foley.obs`
run scope (`None`-safe otherwise).

* **Parameters:**
  * **candidates** ([`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]) – The candidates returned by [`find()`](_autosummary/foley.agent.html.md#foley.agent.find).
  * **transcript** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional narration transcript (WEAVE resolves the reference).
* **Return type:**
  [`SoundDesignTimeline`](_autosummary/foley.base.html.md#foley.base.SoundDesignTimeline)

### foley.agent.refine_query(query, , n=3, hint=None, refiner=None, \_span=None)

Expand `query` into up to `n` paraphrases for multi-query retrieval.

* **Parameters:**
  * **query** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The event query to expand.
  * **n** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Number of paraphrases.
  * **hint** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional verify-failure reason to steer re-retrieval.
  * **refiner** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Refiner`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Refiner)]) – An injected [`Refiner`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Refiner) (the DI seam);
    defaults to `_default_refiner()`.
  * **\_span** – Internal — the obs span handle for GenAI recording.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]

### foley.agent.search_sounds(queries, , k=10, library=None, filters=None)

Hybrid search for one query, or a multi-query RRF-merge (the SELECT retrieval tool).

Delegates a single query VERBATIM to `SoundLibrary.search()` (so the retrieval
ranking the [nDCG@10](mailto:nDCG@10) gate measures is untouched); a multi-query list runs each query
and RRF-merges (`k=RRF_K`) — the query-expansion recall lever, exercised only on a
refine pass.

* **Parameters:**
  * **queries** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]) – One query string, or a list of paraphrases to merge.
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Number of results.
  * **library** – Target library (default: the process-wide default).
  * **filters** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Extra `SoundLibrary.search()` kwargs (e.g. the license prefilter).
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]

### foley.agent.verify_match(event, candidate, , level=VerifyLevel.clap, judge=None, tau_clap=0.35, \_span=None)

Verify `candidate` against `event` up to rung `level` (AND-confirming ladder).

Runs the `clap` gate always; if `level` is higher **and** the clap gate passed,
escalates to the injected/​default judge for that rung and returns *its* verdict
(`Verdict.level` == the producing rung).

* **Parameters:**
  * **event** ([`SoundEvent`](_autosummary/foley.base.html.md#foley.base.SoundEvent)) – The wanted `SoundEvent`.
  * **candidate** ([`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)) – A **license-clean** `Candidate` — this MUST run after the
    [`gate_candidates()`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.gate_candidates) gate (asserted).
  * **level** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`VerifyLevel`](_autosummary/foley.base.html.md#foley.base.VerifyLevel)) – The max rung to climb (`clap` | `listen` | `judge`).
  * **judge** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Judge`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Judge)]) – An injected [`Judge`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Judge) for the higher rungs
    (the DI seam; defaults per `_default_judge()`).
  * **tau_clap** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – The clap-gate threshold.
  * **\_span** – Internal — the obs span handle for GenAI recording on the LLM rung.
* **Raises:**
  [**AssertionError**](https://docs.python.org/3/builtins/exceptions.html#AssertionError) – If `candidate.license_ok` is not `True` (verify-before-gate
      is a bug — the license gate is the fail-closed first pass).
* **Return type:**
  [`Verdict`](_autosummary/foley.base.html.md#foley.base.Verdict)

### Modules

| [`decompose`](_autosummary/foley.agent.decompose.html.md#module-foley.agent.decompose)   | `decompose_context` — narrative passage → a sparse, salience-ranked event list.           |
|-------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------|
| [`llm`](_autosummary/foley.agent.llm.html.md#module-foley.agent.llm)               | Which LLM the SELECT rungs use — one explicit, spend-safe resolver (the `llm=` seam).     |
| [`local_llm`](_autosummary/foley.agent.local_llm.html.md#module-foley.agent.local_llm)   | Offline / local-LLM SELECT rungs — OpenAI-compatible `Decomposer` / `Judge` / `Refiner`.  |
| [`mcp`](_autosummary/foley.agent.mcp.html.md#module-foley.agent.mcp)               | The MCP surface — foley's façade as agent-callable tools (py2mcp, #12, report 05/10).     |
| [`policy`](_autosummary/foley.agent.policy.html.md#module-foley.agent.policy)         | The SELECT policy: the fail-closed rights gate + the single generate-vs-retrieve branch.  |
| [`preview`](_autosummary/foley.agent.preview.html.md#module-foley.agent.preview)       | Audition UX — preview a sound, find similar ones, and refine by relevance feedback (#12). |
| [`protocols`](_autosummary/foley.agent.protocols.html.md#module-foley.agent.protocols)   | The structural DI seams of the SELECT stage — `Decomposer` / `Judge` / `Refiner`.         |
| [`refine`](_autosummary/foley.agent.refine.html.md#module-foley.agent.refine)         | `refine_query` — one event query → 2–4 paraphrases for multi-query retrieval.             |
| [`session`](_autosummary/foley.agent.session.html.md#module-foley.agent.session)       | Session-scoped audition state — cached candidates, picks, and rejects (#12).              |
| [`tools`](_autosummary/foley.agent.tools.html.md#module-foley.agent.tools)           | The SELECT orchestration: `find()` / `plan()` + the small pure tool wrappers.             |
| [`verify`](_autosummary/foley.agent.verify.html.md#module-foley.agent.verify)         | `verify_match` — the retrieve→verify ladder (clap → listen → judge) + `Judge` impls.      |


# _autosummary/foley.agent.llm.html.md

# foley.agent.llm

Which LLM the SELECT rungs use — one explicit, spend-safe resolver (the `llm=` seam).

The decomposer, refiner and judges each have a hermetic fake and two real
implementations (a local OpenAI-compatible endpoint and Anthropic). Which one runs
used to depend on whether an API key happened to be in the environment, so a machine
with `ANTHROPIC_API_KEY` set turned every `find()` — and the test suite — into
paid calls. This module is the single place that choice is made, and a paid or remote
provider is never chosen implicitly:

1. the explicit `llm=` argument (`'fake'` | `'local'` | `'anthropic'`), else
2. the `$FOLEY_LLM` environment variable (same values), else
3. the free default: `'local'` when `$FOLEY_LLM_BASE_URL` is a loopback endpoint
   (Ollama, llama.cpp on this machine), otherwise `'fake'`. A remote endpoint (an
   OpenAI-compatible cloud API) may cost money, so it needs `llm='local'`.

A key being present never upgrades anything. Asking for a provider that cannot run
(no SDK, no key, no endpoint) raises an error naming what is missing. Under
[`foley.offline()`](_autosummary/foley.html.md#foley.offline) a provider that sends data off the device raises
[`EgressBlocked`](_autosummary/foley.runtime.html.md#foley.runtime.EgressBlocked) — at resolution time *and* when a real rung is
called ([`require_llm_egress()`](_autosummary/foley.agent.llm.html.md#foley.agent.llm.require_llm_egress)), so an injected `AnthropicJudge()` is covered too.

[`PROVIDERS`](_autosummary/foley.agent.llm.html.md#foley.agent.llm.PROVIDERS) is the provider table: each provider’s rung classes. It is the one
place a new provider, or per-provider cost data, is added.

### Module Attributes

| [`LLM_ENV_VAR`](_autosummary/foley.agent.llm.html.md#foley.agent.llm.LLM_ENV_VAR)     | The environment variable that opts in to a provider when `llm=` is not passed.          |
|------------------------------------------------------------------|-----------------------------------------------------------------------------------------|
| [`PROVIDERS`](_autosummary/foley.agent.llm.html.md#foley.agent.llm.PROVIDERS)       | provider -> rung kind -> `"module:Class"` (imported lazily; the real ones need extras). |
| [`LLM_CHOICES`](_autosummary/foley.agent.llm.html.md#foley.agent.llm.LLM_CHOICES)     | The accepted providers.                                                                 |
| [`RUNG_MAX_TOKENS`](_autosummary/foley.agent.llm.html.md#foley.agent.llm.RUNG_MAX_TOKENS) | Default `max_tokens` of each rung (the classes read these, so estimates match).         |

### Functions

| [`resolve_llm`](_autosummary/foley.agent.llm.html.md#foley.agent.llm.resolve_llm)([llm, implicit_local])            | Resolve which LLM provider the SELECT rungs use (see the module docstring).                                                             |
|------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------|
| [`make_rung`](_autosummary/foley.agent.llm.html.md#foley.agent.llm.make_rung)(kind[, llm, implicit_local])        | Build the `kind` rung (`'decomposer'` | `'refiner'` | `'judge'`) for `llm`.                                                             |
| [`llm_egress`](_autosummary/foley.agent.llm.html.md#foley.agent.llm.llm_egress)(provider)                          | The `data_egress` class (`'local'` | `'external'`) of an LLM provider.                                                                  |
| [`require_llm_egress`](_autosummary/foley.agent.llm.html.md#foley.agent.llm.require_llm_egress)(provider)                  | Raise [`EgressBlocked`](_autosummary/foley.runtime.html.md#foley.runtime.EgressBlocked) if the runtime forbids `provider` now. |
| [`llm_call_estimate`](_autosummary/foley.agent.llm.html.md#foley.agent.llm.llm_call_estimate)(provider, \*[, model, ...]) | An upper bound on one rung call's USD cost, or `None` when it is unknown.                                                               |
| [`guard_llm_call`](_autosummary/foley.agent.llm.html.md#foley.agent.llm.guard_llm_call)(provider)                      | The call-time egress check every real rung runs before calling its model.                                                               |
| [`metered_create`](_autosummary/foley.agent.llm.html.md#foley.agent.llm.metered_create)(client, \*[, sleep])           | `client.messages.create(**request)`, priced, capped and charged (#57).                                                                  |
| [`is_loopback_host`](_autosummary/foley.agent.llm.html.md#foley.agent.llm.is_loopback_host)(host)                        | Whether `host` is this machine (`localhost`, `127.*`, `::1`, `0.0.0.0`).                                                                |

### foley.agent.llm.LLM_CHOICES *= ('fake', 'local', 'anthropic')*

The accepted providers. `'fake'` is deterministic and free; the other two are real.

### foley.agent.llm.LLM_ENV_VAR *= 'FOLEY_LLM'*

The environment variable that opts in to a provider when `llm=` is not passed.

### foley.agent.llm.PROVIDERS *: [dict](https://docs.python.org/3/builtins/stdtypes.html#dict)[[str](https://docs.python.org/3/builtins/stdtypes.html#str), [dict](https://docs.python.org/3/builtins/stdtypes.html#dict)[[str](https://docs.python.org/3/builtins/stdtypes.html#str), [str](https://docs.python.org/3/builtins/stdtypes.html#str)]]* *= {'anthropic': {'decomposer': 'foley.agent.decompose:AnthropicDecomposer', 'judge': 'foley.agent.verify:AnthropicJudge', 'refiner': 'foley.agent.refine:AnthropicRefiner'}, 'fake': {'decomposer': 'foley.agent.decompose:KeywordDecomposer', 'judge': 'foley.agent.verify:StringOverlapJudge', 'refiner': 'foley.agent.refine:KeywordRefiner'}, 'local': {'decomposer': 'foley.agent.local_llm:LocalLLMDecomposer', 'judge': 'foley.agent.local_llm:LocalLLMJudge', 'refiner': 'foley.agent.local_llm:LocalLLMRefiner'}}*

provider -> rung kind -> `"module:Class"` (imported lazily; the real ones need extras).

### foley.agent.llm.RUNG_MAX_TOKENS *= {'decomposer': 2000, 'judge': 500, 'refiner': 500}*

Default `max_tokens` of each rung (the classes read these, so estimates match).

### foley.agent.llm.guard_llm_call(provider)

The call-time egress check every real rung runs before calling its model.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### foley.agent.llm.is_loopback_host(host)

Whether `host` is this machine (`localhost`, `127.*`, `::1`, `0.0.0.0`).

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### foley.agent.llm.llm_call_estimate(provider, , model=None, max_tokens=None, kind=None, input_chars=0)

An upper bound on one rung call’s USD cost, or `None` when it is unknown.

`0.0` for the fake and an on-device endpoint. For Anthropic: the input-token
bound (`chars / CHARS_PER_TOKEN_FLOOR` of the prompt — the rung’s fixed prompt
and allowance when `kind` is given, plus `input_chars`) × the input price, plus
`max_tokens` × the output price (thinking counts toward `max_tokens`). A remote
endpoint, or an Anthropic model with no listed price, is `None`.

* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`float`](https://docs.python.org/3/builtins/functions.html#float)]

### foley.agent.llm.llm_egress(provider)

The `data_egress` class (`'local'` | `'external'`) of an LLM provider.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### foley.agent.llm.make_rung(kind, llm=None, , implicit_local=True, \*\*kwargs)

Build the `kind` rung (`'decomposer'` | `'refiner'` | `'judge'`) for `llm`.

* **Parameters:**
  * **kind** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The rung kind (a key of each [`PROVIDERS`](_autosummary/foley.agent.llm.html.md#foley.agent.llm.PROVIDERS) row).
  * **llm** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – The provider (see [`resolve_llm()`](_autosummary/foley.agent.llm.html.md#foley.agent.llm.resolve_llm)).
  * **implicit_local** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Forwarded to [`resolve_llm()`](_autosummary/foley.agent.llm.html.md#foley.agent.llm.resolve_llm).
  * **\*\*kwargs** – Passed to the rung’s constructor.

### foley.agent.llm.metered_create(client, , sleep=None, \*\*request)

`client.messages.create(**request)`, priced, capped and charged (#57).

The bound for *this* request (its real prompt length and `max_tokens`) is
reserved on every budget in force before it is sent, so it can never take a run
past its cap; afterwards the reservation becomes the actual cost from
`response.usage`. A request the API rejects (429 / 5xx — not billed) is retried
up to `METERED_ATTEMPTS` times. Anything else (a timeout, a dropped
connection) keeps the reservation — it may have been billed — and raises. The SDK
never re-sends behind this accounting: foley builds its clients with
`max_retries=0`, and an injected client is used through
`with_options(max_retries=0)`. Tokens the API adds itself (structured-output
grammar, thinking) are not in the bound; [`settle()`](_autosummary/foley.cost.html.md#foley.cost.settle) records the
true cost from `usage` afterwards.

### foley.agent.llm.require_llm_egress(provider)

Raise [`EgressBlocked`](_autosummary/foley.runtime.html.md#foley.runtime.EgressBlocked) if the runtime forbids `provider` now.

Called by [`resolve_llm()`](_autosummary/foley.agent.llm.html.md#foley.agent.llm.resolve_llm) and again by each real rung right before it calls its
model, so a rung built online and called inside [`foley.offline()`](_autosummary/foley.html.md#foley.offline) is refused.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### foley.agent.llm.resolve_llm(llm=None, , implicit_local=True)

Resolve which LLM provider the SELECT rungs use (see the module docstring).

* **Parameters:**
  * **llm** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – `'fake'` | `'local'` | `'anthropic'`, or `None` to read
    `$FOLEY_LLM` and then fall back to the free default.
  * **implicit_local** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Whether the free default may be a loopback local endpoint.
    `False` keeps the default the deterministic fake (the hermetic eval).
* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)
* **Returns:**
  One of [`LLM_CHOICES`](_autosummary/foley.agent.llm.html.md#foley.agent.llm.LLM_CHOICES).
* **Raises:**
  * [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If `llm` (or `$FOLEY_LLM`) is not one of [`LLM_CHOICES`](_autosummary/foley.agent.llm.html.md#foley.agent.llm.LLM_CHOICES).
  * [**RuntimeError**](https://docs.python.org/3/builtins/exceptions.html#RuntimeError) – If the requested provider cannot run (SDK, key or endpoint missing).
  * [**EgressBlocked**](_autosummary/foley.runtime.html.md#foley.runtime.EgressBlocked) – If the provider sends data off the device under [`foley.offline()`](_autosummary/foley.html.md#foley.offline).


# _autosummary/foley.agent.local_llm.html.md

# foley.agent.local_llm

Offline / local-LLM SELECT rungs — OpenAI-compatible `Decomposer` / `Judge` / `Refiner`.

The offline sibling of the Anthropic-backed SELECT rungs: instead of calling the hosted
Claude API, these hit any **OpenAI-compatible** chat endpoint — an on-device server such as
**Ollama** (`http://localhost:11434/v1`), **llama.cpp**’s server, or **vLLM** — so
`foley.find` can run its LLM decomposition / judging / refinement with \*\*nothing leaving
the device\*\* (report 12’s offline posture). They satisfy the same
[`foley.agent.protocols`](_autosummary/foley.agent.protocols.html.md#module-foley.agent.protocols) seams and REUSE the exact system prompts + JSON schemas the
Anthropic rungs use (one prompt SSOT), differing only in the transport.

Zero-config wiring: set `FOLEY_LLM_BASE_URL` (+ optionally `FOLEY_LLM_MODEL` /
`FOLEY_LLM_API_KEY`) and the SELECT defaults auto-upgrade to these — see
[`local_llm_configured()`](_autosummary/foley.agent.local_llm.html.md#foley.agent.local_llm.local_llm_configured), consulted by `_default_decomposer` / `_default_judge` /
`_default_refiner`. `openai` (the thin OpenAI client, `foley[local-llm]`) is imported
LAZILY inside the call path only, so `import foley` / `import foley.agent` stay dol-only;
tests inject a fake `client` and never touch the network.

### Module Attributes

| [`DEFAULT_LOCAL_MODEL`](_autosummary/foley.agent.local_llm.html.md#foley.agent.local_llm.DEFAULT_LOCAL_MODEL)   | The default local model id (override per call or via `FOLEY_LLM_MODEL`).   |
|------------------------------------------------------------------------|----------------------------------------------------------------------------|

### Functions

| [`local_llm_configured`](_autosummary/foley.agent.local_llm.html.md#foley.agent.local_llm.local_llm_configured)()   | True iff a local OpenAI-compatible endpoint is configured (`FOLEY_LLM_BASE_URL`).   |
|---------------------------------------------------------------------------|-------------------------------------------------------------------------------------|

### Classes

| [`LocalLLMDecomposer`](_autosummary/foley.agent.local_llm.html.md#foley.agent.local_llm.LocalLLMDecomposer)(\*[, client, model, ...])     | OpenAI-compatible [`Decomposer`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Decomposer) (local endpoint).   |
|---------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------|
| [`LocalLLMJudge`](_autosummary/foley.agent.local_llm.html.md#foley.agent.local_llm.LocalLLMJudge)(\*[, client, model, max_tokens])   | OpenAI-compatible [`Judge`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Judge) for the `judge` rung.         |
| [`LocalLLMRefiner`](_autosummary/foley.agent.local_llm.html.md#foley.agent.local_llm.LocalLLMRefiner)(\*[, client, model, max_tokens]) | OpenAI-compatible [`Refiner`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Refiner) (local endpoint).         |

### foley.agent.local_llm.DEFAULT_LOCAL_MODEL *= 'llama3.1'*

The default local model id (override per call or via `FOLEY_LLM_MODEL`).

### *class* foley.agent.local_llm.LocalLLMDecomposer(, client=None, model=None, max_tokens=2000)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

OpenAI-compatible [`Decomposer`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Decomposer) (local endpoint).

#### decompose(context, , max_events=6, seconds=None)

Decompose `context` into `<= max_events` events via the local LLM.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`SoundEvent`](_autosummary/foley.base.html.md#foley.base.SoundEvent)]

### *class* foley.agent.local_llm.LocalLLMJudge(, client=None, model=None, max_tokens=500)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

OpenAI-compatible [`Judge`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Judge) for the `judge` rung.

#### judge(event, candidate, , level=None)

Arbitrate the match via the local LLM; returns a `Verdict` at `level`.

* **Return type:**
  [`Verdict`](_autosummary/foley.base.html.md#foley.base.Verdict)

### *class* foley.agent.local_llm.LocalLLMRefiner(, client=None, model=None, max_tokens=500)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

OpenAI-compatible [`Refiner`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Refiner) (local endpoint).

#### refine(query, , n=3, hint=None)

Return `n` paraphrases via the local LLM (the original `query` always first).

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]

### foley.agent.local_llm.local_llm_configured()

True iff a local OpenAI-compatible endpoint is configured (`FOLEY_LLM_BASE_URL`).

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)


# _autosummary/foley.agent.mcp.html.md

# foley.agent.mcp

The MCP surface — foley’s façade as agent-callable tools (py2mcp, #12, report 05/10).

Exposes the SELECT→preview→WEAVE loop to an LLM agent (or any MCP client) as a set of
**JSON-safe** tools built with `py2mcp` (`mk_mcp_server` over stdio). The single rule:
tools take/return only JSON (ids, scores, license summaries, timeline dicts, store keys) —
**never** a [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate), a [`WeaveResult`](_autosummary/foley.html.md#foley.WeaveResult), or numpy
audio. Heavy objects stay server-side; audio is referenced by a byte-store key.

The tool functions are thin wrappers over the existing façade (a single seam, three
surfaces: Python / CLI / MCP): they resolve the shared [`foley.index.SoundLibrary`](_autosummary/foley.index.html.md#foley.index.SoundLibrary)
and per-session [`foley.agent.session.SessionStore`](_autosummary/foley.agent.session.html.md#foley.agent.session.SessionStore) (both injectable for tests).
`py2mcp` / `fastmcp` are imported LAZILY inside [`build_mcp_server()`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.build_mcp_server) only, so
`import foley` and `import foley.agent` stay dol-only.

### Module Attributes

| [`TOOLS`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.TOOLS)   | The full JSON-safe tool surface (SSOT), in a stable order.   |
|----------------------------------------------------------|--------------------------------------------------------------|

### Functions

| [`build_mcp_server`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.build_mcp_server)(\*[, library, session, ...])    | Build the foley MCP server (lazy `py2mcp`); registers the JSON-safe tool surface.         |
|---------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------|
| [`foley_capabilities`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.foley_capabilities)()                             | What foley can do here — keys / extras / system deps / offline / sources / degraded.      |
| [`foley_drop_pick`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.foley_drop_pick)(sound_id[, session])             | Remove a previously-picked sound from the session.                                        |
| [`foley_find`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.foley_find)(context[, max_events, verify, ...])   | Find verified, license-clean sound candidates for a narrative passage.                    |
| [`foley_generate`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.foley_generate)(prompt[, backend, ...])           | Generate a sound from a text prompt (local backends only when offline).                   |
| [`foley_guide`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.foley_guide)()                                    | How to sound-design a narration with foley — the operating playbook for an agent.         |
| [`foley_list_picks`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.foley_list_picks)([session])                      | The sounds picked in this session (the 'persist picks' read side).                        |
| [`foley_nudge`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.foley_nudge)(timeline, item_id, delta_s)          | Shift a timeline item's onset by `delta_s` seconds; returns the new timeline.             |
| [`foley_pick`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.foley_pick)(sound_id[, session, layer, onset])    | Accept a sound into the session (persisted); `foley_plan` folds picks into a timeline.    |
| [`foley_plan`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.foley_plan)([session, transcript, candidate_ids]) | Fold picks (or explicit candidate ids) into a JSON sound-design timeline.                 |
| [`foley_preview`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.foley_preview)(sound_id[, seconds, session])      | Produce a short audition of a sound; returns its store key (never audio bytes).           |
| [`foley_refine`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.foley_refine)([session, query, picked_ids, ...])  | Relevance-feedback refinement: expand the query, boost picks, drop rejects, re-rank.      |
| [`foley_reject`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.foley_reject)(sound_id[, session, reason])        | Reject a sound (feeds `foley_refine` relevance feedback).                                 |
| [`foley_score`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.foley_score)(context[, commercial_ok, ...])       | Score a narration passage → an editable sound-design timeline + a per-event rationale.    |
| [`foley_search`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.foley_search)(query[, k, commercial_ok, ...])     | Hybrid (CLAP + keyword) search of the library for a text query; returns candidate rows.   |
| [`foley_set_gain`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.foley_set_gain)(timeline, item_id, gain_db)       | Set a timeline item's gain (dB); returns the new timeline.                                |
| [`foley_set_master`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.foley_set_master)(timeline[, target_lufs, ...])   | Set the timeline's master target (LUFS / true-peak); returns the new timeline.            |
| [`foley_similar_to`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.foley_similar_to)(sound_id[, k, ...])             | "More like this" — the library neighbours of a sound (by id); returns candidate rows.     |
| [`foley_status`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.foley_status)([session])                          | The current runtime posture + this session's pick/reject counts.                          |
| [`foley_swap_clip`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.foley_swap_clip)(timeline, item_id, sound_id)     | Swap a timeline item's clip; returns the new timeline.                                    |
| [`foley_timeline_captions`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.foley_timeline_captions)(timeline[, fmt])         | Export SDH captions for a timeline (`fmt='vtt'` | `'srt'`).                               |
| [`foley_toggle`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.foley_toggle)(timeline, item_id, enabled)         | Mute/unmute a timeline item; returns the new timeline.                                    |
| [`foley_weave`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.foley_weave)(narration, timeline[, session])      | Render a timeline under the narration; returns the mix by store key + captions + credits. |
| [`make_http_app`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.make_http_app)(\*, auth[, path, ...])             | Build a bearer-auth-gated ASGI app serving the foley MCP tools over streamable HTTP.      |
| [`serve`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.serve)(\*[, name, runtime])                       | Build and run the foley MCP server over stdio (blocks); enforces an offline posture.      |
| [`serve_http`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.serve_http)(\*[, host, port, path])               | Build and serve the foley MCP tools over authenticated streamable HTTP (blocks).          |

### foley.agent.mcp.TOOLS *= [<function foley_find>, <function foley_search>, <function foley_similar_to>, <function foley_preview>, <function foley_refine>, <function foley_pick>, <function foley_drop_pick>, <function foley_reject>, <function foley_list_picks>, <function foley_generate>, <function foley_plan>, <function foley_weave>, <function foley_swap_clip>, <function foley_set_gain>, <function foley_nudge>, <function foley_toggle>, <function foley_set_master>, <function foley_timeline_captions>, <function foley_score>, <function foley_guide>, <function foley_capabilities>, <function foley_status>]*

The full JSON-safe tool surface (SSOT), in a stable order.

### foley.agent.mcp.build_mcp_server(, library=None, session='default', runtime=None, byte_store=None, include=None, name='foley', max_usd=None)

Build the foley MCP server (lazy `py2mcp`); registers the JSON-safe tool surface.

Validates that every source declares a `data_egress` (fail-closed), binds the
injectable library / runtime / byte-store, and hands the resolved tool functions to
`py2mcp.mk_mcp_server`. Never starts a server or touches the network.

* **Parameters:**
  * **library** – The [`foley.index.SoundLibrary`](_autosummary/foley.index.html.md#foley.index.SoundLibrary) (default: the shared one).
  * **session** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The default session id.
  * **runtime** – A [`foley.runtime.RuntimeConfig`](_autosummary/foley.runtime.html.md#foley.runtime.RuntimeConfig) (default: the active one).
  * **byte_store** – A `MutableMapping[str, bytes]` for previews / rendered mixes.
  * **include** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]) – Optional subset of tool names to expose.
  * **name** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The MCP server name.
  * **max_usd** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`float`](https://docs.python.org/3/builtins/functions.html#float)]) – The server’s cumulative spend cap across every paid tool call, for
    its lifetime (default [`foley.cost.default_max_usd()`](_autosummary/foley.cost.html.md#foley.cost.default_max_usd): $1, or $3 under
    `$FOLEY_LLM=anthropic`).
* **Returns:**
  A `fastmcp.FastMCP` server.

### foley.agent.mcp.foley_capabilities()

What foley can do here — keys / extras / system deps / offline / sources / degraded.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### foley.agent.mcp.foley_drop_pick(sound_id, session='default')

Remove a previously-picked sound from the session.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### foley.agent.mcp.foley_find(context, max_events=6, verify='listen', commercial_ok=True, k=10, session='default')

Find verified, license-clean sound candidates for a narrative passage.

Caches the full candidates in the session so `foley_plan` / `foley_weave` can
rehydrate them by id. Returns compact candidate rows.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)

### foley.agent.mcp.foley_generate(prompt, backend='stable_audio', commercial_ok=True, session='default')

Generate a sound from a text prompt (local backends only when offline).

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### foley.agent.mcp.foley_guide()

How to sound-design a narration with foley — the operating playbook for an agent.

Call this first if you are unsure of the workflow: it returns the tool order, the taste
heuristics (restraint, layering, ducking, licensing), and the offline posture.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### foley.agent.mcp.foley_list_picks(session='default')

The sounds picked in this session (the ‘persist picks’ read side).

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)

### foley.agent.mcp.foley_nudge(timeline, item_id, delta_s)

Shift a timeline item’s onset by `delta_s` seconds; returns the new timeline.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### foley.agent.mcp.foley_pick(sound_id, session='default', layer=None, onset=None)

Accept a sound into the session (persisted); `foley_plan` folds picks into a timeline.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### foley.agent.mcp.foley_plan(session='default', transcript=None, candidate_ids=None)

Fold picks (or explicit candidate ids) into a JSON sound-design timeline.

Each pick’s explicit `layer` / `onset` (set via [`foley_pick()`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.foley_pick)) is overlaid
onto its candidate’s [`SoundEvent`](_autosummary/foley.base.html.md#foley.base.SoundEvent) so `plan` honors the agent’s
placement choices — a numeric `onset` becomes an absolute-seconds anchor (see
[`foley.weave.anchor.parse_symbolic_anchor()`](_autosummary/foley.weave.anchor.html.md#foley.weave.anchor.parse_symbolic_anchor)).

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### foley.agent.mcp.foley_preview(sound_id, seconds=6, session='default')

Produce a short audition of a sound; returns its store key (never audio bytes).

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### foley.agent.mcp.foley_refine(session='default', query=None, picked_ids=None, rejected_ids=None, hint=None, k=10)

Relevance-feedback refinement: expand the query, boost picks, drop rejects, re-rank.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### foley.agent.mcp.foley_reject(sound_id, session='default', reason=None)

Reject a sound (feeds `foley_refine` relevance feedback).

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### foley.agent.mcp.foley_score(context, commercial_ok=True, verify='listen', max_events=6, session='default')

Score a narration passage → an editable sound-design timeline + a per-event rationale.

The one-call SELECT→plan entry for an agent: pass the narration text and get back a JSON
timeline plus a rationale for each chosen sound. Weaving (rendering the mastered mix) is a
**separate** step (`foley_weave`) so you can review / edit the timeline first — swap a
clip, nudge an onset, drop a cue — before committing to a render.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### foley.agent.mcp.foley_search(query, k=10, commercial_ok=True, ucs_category=None, rerank=False, session='default')

Hybrid (CLAP + keyword) search of the library for a text query; returns candidate rows.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)

### foley.agent.mcp.foley_set_gain(timeline, item_id, gain_db)

Set a timeline item’s gain (dB); returns the new timeline.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### foley.agent.mcp.foley_set_master(timeline, target_lufs=None, peak_dbfs=None)

Set the timeline’s master target (LUFS / true-peak); returns the new timeline.

Either field may be set independently — omitting one keeps the podcast default for
that field (so `peak_dbfs=-2.0` alone tightens only the true-peak ceiling).

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### foley.agent.mcp.foley_similar_to(sound_id, k=10, commercial_ok=True, session='default')

“More like this” — the library neighbours of a sound (by id); returns candidate rows.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)

### foley.agent.mcp.foley_status(session='default')

The current runtime posture + this session’s pick/reject counts.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### foley.agent.mcp.foley_swap_clip(timeline, item_id, sound_id)

Swap a timeline item’s clip; returns the new timeline.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### foley.agent.mcp.foley_timeline_captions(timeline, fmt='vtt')

Export SDH captions for a timeline (`fmt='vtt'` | `'srt'`).

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### foley.agent.mcp.foley_toggle(timeline, item_id, enabled)

Mute/unmute a timeline item; returns the new timeline.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### foley.agent.mcp.foley_weave(narration, timeline, session='default')

Render a timeline under the narration; returns the mix by store key + captions + credits.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### foley.agent.mcp.make_http_app(, auth, path='/mcp', json_response=False, library=None, runtime=None, byte_store=None, include=None, name='foley')

Build a bearer-auth-gated ASGI app serving the foley MCP tools over streamable HTTP.

HTTP exposes the tool surface to the network, so `auth` is \*\*required and
fail-closed\*\*: pass `auth={'bearer_tokens': [...]}` (a non-empty iterable of accepted
tokens) — a request without a matching `Authorization: Bearer <token>` header gets a
`401`. The returned app (a Starlette/ASGI callable) mounts in any ASGI host (uvicorn,
gunicorn, a parent FastAPI). `py2mcp` / `fastmcp` are imported lazily inside
[`build_mcp_server()`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.build_mcp_server), so `import foley` stays dol-only.

* **Parameters:**
  * **auth** ([`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)) – `{'bearer_tokens': [...]}` — required; empty/missing raises (fail-closed).
  * **path** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The MCP HTTP mount path.
  * **json_response** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Return a single JSON response instead of an SSE stream (simple clients).
  * **name** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – As [`build_mcp_server()`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.build_mcp_server).
* **Returns:**
  An ASGI application (the bearer-gated MCP HTTP app).
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If `auth` carries no bearer tokens (no anonymous HTTP access).

### foley.agent.mcp.serve(, name='foley', runtime=None, \*\*kwargs)

Build and run the foley MCP server over stdio (blocks); enforces an offline posture.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### foley.agent.mcp.serve_http(, host='127.0.0.1', port=8000, auth, path='/mcp', \*\*kwargs)

Build and serve the foley MCP tools over authenticated streamable HTTP (blocks).

Wraps [`make_http_app()`](_autosummary/foley.agent.mcp.html.md#foley.agent.mcp.make_http_app) and runs it with uvicorn. `auth` is required (fail-closed).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)


# _autosummary/foley.agent.policy.html.md

# foley.agent.policy

The SELECT policy: the fail-closed rights gate + the single generate-vs-retrieve branch.

Two load-bearing invariants live here as two **physically separate pure functions**, so
neither can bypass the other:

* [`gate_candidates()`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.gate_candidates) — the ONLY rights-rejection point. Runs the fail-closed
  [`foley.licensing.keep_sound()`](_autosummary/foley.licensing.html.md#foley.licensing.keep_sound) gate over the candidate set **before** any
  > verification/ranking (invariant #3), sets `Candidate.license_ok`, and drops every
  > non-`True` candidate.
* [`decide()`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.decide) — the ONLY generate-vs-retrieve branch. A **pure** function of an
  already-gated+verified set; it never calls `keep`/`search`/`generate` (the
  [`foley.agent.tools`](_autosummary/foley.agent.tools.html.md#module-foley.agent.tools) loop owns all side effects).

[`Budget`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.Budget) bounds the refine→re-retrieve and generate loops so a hard event can’t
run away. This module is stdlib-only (imports only [`foley.base`](_autosummary/foley.base.html.md#module-foley.base) /
[`foley.licensing`](_autosummary/foley.licensing.html.md#module-foley.licensing)) and does no I/O and no obs — the loop emits the audit Steps.

### Functions

| [`decide`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.decide)(event, kept, verified, \*, ...)    | The single generate-vs-retrieve branch — a PURE function (report 05 §4).   |
|--------------------------------------------------------------------------------------------|----------------------------------------------------------------------------|
| [`gate_candidates`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.gate_candidates)(candidates, intended_use) | Fail-closed license gate — run BEFORE verification/ranking (invariant #3). |

### Classes

| [`Budget`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.Budget)([max_refine_loops, max_generations, ...])   | Bounded-cost accounting: per-event loop counts, and a cumulative dollar cap (#57).                                             |
|-----------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------|
| [`DecideAction`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.DecideAction)(\*values)                             | What [`decide()`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.decide) chose for one event (the single branch's outcomes).             |
| [`Decision`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.Decision)(action[, candidate, reason])              | The tiny result of [`decide()`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.decide); `reason` feeds the refine hint + the audit Step. |

### *class* foley.agent.policy.Budget(max_refine_loops=1, max_generations=1, allow_generate=True, max_usd=1.0, approve_unknown_cost=None, \_refines=0, \_gens=0, \_spent_usd=0.0, \_unknown_calls=0, \_lock=<factory>)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Bounded-cost accounting: per-event loop counts, and a cumulative dollar cap (#57).

Two kinds of bound:

* **Per event** — `max_refine_loops` / `max_generations`: the loop calls
  [`refine_ok()`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.Budget.refine_ok) / [`gen_ok()`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.Budget.gen_ok) to test, then [`spend_refine()`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.Budget.spend_refine) /
  [`spend_gen()`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.Budget.spend_gen) to charge; [`reset()`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.Budget.reset) zeroes them at each event.
* **Per run, cumulative** — `max_usd` (default $1): every paid call (generation,
  paid LLM rung) is checked with [`authorize()`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.Budget.authorize) before it is made and recorded with [`charge()`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.Budget.charge)
  after. [`reset()`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.Budget.reset) never clears it, so the cap holds across the whole
  `find` / `score` run. A call whose cost is unknown (estimate `None`) is
  refused unless `approve_unknown_cost` (or `$FOLEY_APPROVE_UNKNOWN_COST=1`).

#### authorize(estimate_usd, , what)

Alias of [`check()`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.Budget.check) (kept for callers that only test affordability).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### charge(usd)

Record a call’s cost (`None` = unknown: counted, not summed).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### check(estimate_usd, , what)

Raise if this run cannot afford `estimate_usd` (nothing is reserved).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### gen_ok()

Whether a generation fallback is allowed and within budget.

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

#### refine_ok()

Whether another refine→re-retrieve pass is within budget.

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

#### reserve(estimate_usd, , what)

Check, then count `estimate_usd` as spent at once (atomic under a lock).

Reserving before the call is what keeps concurrent calls on one budget (MCP
tools run in a threadpool) from all passing the check before any is charged.
[`settle()`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.Budget.settle) later replaces the reservation with the actual cost, if known.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### reset()

Zero the spend counters so the caps apply *per event*, not per passage.

The `find` loop calls this at the top of each event so one hard event’s
refine/generate spend never starves later events (the documented per-event
semantics).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### settle(reserved_usd, actual_usd)

Replace a reservation with the actual cost (`None` actual: keep the reservation).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### spend_gen()

Charge one generation.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### spend_refine()

Charge one refine loop.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### *property* spent_usd *: [float](https://docs.python.org/3/builtins/functions.html#float)*

Dollars charged so far this run (calls of unknown cost count as 0 here).

### *class* foley.agent.policy.DecideAction(\*values)

Bases: [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Enum`](https://docs.python.org/3/library/enum.html#enum.Enum)

What [`decide()`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.decide) chose for one event (the single branch’s outcomes).

### *class* foley.agent.policy.Decision(action, candidate=None, reason='')

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

The tiny result of [`decide()`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.decide); `reason` feeds the refine hint + the audit Step.

### foley.agent.policy.decide(event, kept, verified, , tau_retrieve, budget, loop)

The single generate-vs-retrieve branch — a PURE function (report 05 §4).

Chooses among [`DecideAction`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.DecideAction) from the already-gated (`kept`) and
already-verified (`verified`) sets. It performs no I/O and never calls
`keep`/`search`/`generate` — the [`foley.agent.tools`](_autosummary/foley.agent.tools.html.md#module-foley.agent.tools) loop acts on the
returned [`Decision`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.Decision).

Policy (report 05 §4):

> * a verified clip clearing `tau_retrieve` → `USE` (the best one);
> * verified-but-low-confidence with refine budget → `REFINE` (feed the reason back);
> * a non-diegetic cue, or a diegetic gap with no verified match, with generate budget
>   → `GENERATE`;
> * otherwise → `DROP` (silence), unless a lower-confidence verified clip exists and
>   generation is off, in which case fall back to that best-effort pick.
* **Parameters:**
  * **event** ([`SoundEvent`](_autosummary/foley.base.html.md#foley.base.SoundEvent)) – The event being resolved.
  * **kept** ([`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]) – The license-clean candidates (each `license_ok is True`).
  * **verified** ([`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]) – The subset of `kept` whose verdict matched.
  * **tau_retrieve** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – The confidence threshold for auto-accepting a retrieved clip.
  * **budget** ([`Budget`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.Budget)) – The per-event cost budget.
  * **loop** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The current refine-loop index (for the audit reason).
* **Return type:**
  [`Decision`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.Decision)
* **Returns:**
  A [`Decision`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.Decision).

### foley.agent.policy.gate_candidates(candidates, intended_use)

Fail-closed license gate — run BEFORE verification/ranking (invariant #3).

For each candidate: apply [`keep_sound()`](_autosummary/foley.licensing.html.md#foley.licensing.keep_sound) (any exception ⇒
`False`, fail-closed), record the result on `candidate.license_ok`, and keep
ONLY where it is `True` — a `None` (never gated) or `False` is dropped. This is
the single rights-rejection point; [`verify_match()`](_autosummary/foley.agent.verify.html.md#foley.agent.verify.verify_match) asserts
its survivors are license-clean.

* **Parameters:**
  * **candidates** ([`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]) – The retrieved shortlist (`license_ok` typically `None`).
  * **intended_use** ([`IntendedUse`](_autosummary/foley.base.html.md#foley.base.IntendedUse)) – The caller’s declared rights intent.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]
* **Returns:**
  The license-clean sublist (each with `license_ok is True`), order preserved.


# _autosummary/foley.agent.preview.html.md

# foley.agent.preview

Audition UX — preview a sound, find similar ones, and refine by relevance feedback (#12).

The interactive/agentic layer over the existing retrieval engine, all thin over
[`foley.index.SoundLibrary`](_autosummary/foley.index.html.md#foley.index.SoundLibrary):

* [`preview()`](_autosummary/foley.agent.preview.html.md#foley.agent.preview.preview) — synthesize a short audition clip for a sound and reference it by a
  **store key** (never raw bytes over the wire); degrades to `preview_uri=None` when
  the audio codec extra is absent.
* [`similar_to()`](_autosummary/foley.agent.preview.html.md#foley.agent.preview.similar_to) — “more like this”: `sound_id` / [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)
  → by-id neighbours (`SoundLibrary.similar`), or a raw clip → audio-to-audio search
  (`SoundLibrary.search_clip`). A NEW clip/candidate-in entrypoint, distinct from the
  existing by-id `SoundLibrary.similar`.
* [`refine()`](_autosummary/foley.agent.preview.html.md#foley.agent.preview.refine) — TRUE relevance feedback (distinct from the query-paraphrase
  [`foley.refine_query()`](_autosummary/foley.html.md#foley.refine_query)): expand the query for recall, pull neighbours of the
  > session’s **picks**, drop its **rejects**, and re-rank.

Dol-only core (numpy/soundfile only inside [`preview()`](_autosummary/foley.agent.preview.html.md#foley.agent.preview.preview)’s encode path).

### Module Attributes

| [`PREVIEW_SECONDS`](_autosummary/foley.agent.preview.html.md#foley.agent.preview.PREVIEW_SECONDS)   | Default audition length (seconds).   |
|--------------------------------------------------------------------|--------------------------------------|

### Functions

| [`preview`](_autosummary/foley.agent.preview.html.md#foley.agent.preview.preview)(candidate_or_id, \*[, seconds, ...])    | Produce a short audition of a sound; set its `preview_uri` to a store key.            |
|--------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------|
| [`refine`](_autosummary/foley.agent.preview.html.md#foley.agent.preview.refine)([session, query, picked_ids, ...])       | Relevance-feedback refinement: expand for recall, boost picks, drop rejects, re-rank. |
| [`similar_to`](_autosummary/foley.agent.preview.html.md#foley.agent.preview.similar_to)(clip_or_candidate, \*[, k, library]) | "More like this" — neighbours of a sound id / candidate, or of a raw clip.            |

### Classes

| [`RefineResult`](_autosummary/foley.agent.preview.html.md#foley.agent.preview.RefineResult)(queries, results)   | The output of [`refine()`](_autosummary/foley.agent.preview.html.md#foley.agent.preview.refine): the expanded queries and the re-ranked candidates.   |
|-----------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------|

### foley.agent.preview.PREVIEW_SECONDS *: [int](https://docs.python.org/3/builtins/functions.html#int)* *= 6*

Default audition length (seconds).

### *class* foley.agent.preview.RefineResult(queries, results)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

The output of [`refine()`](_autosummary/foley.agent.preview.html.md#foley.agent.preview.refine): the expanded queries and the re-ranked candidates.

### foley.agent.preview.preview(candidate_or_id, , seconds=6, library=None, byte_store=None, session=None)

Produce a short audition of a sound; set its `preview_uri` to a store key.

Writes the first `seconds` of the clip (FLAC) into `byte_store` under its
content key and points `Candidate.preview_uri` at that key — referencing the
audio, never returning bytes. Fail-safe: if the audio codec extra (`foley[audio]`)
or the clip is unavailable, `preview_uri` is `None` (the sound id + duration
still let a client fetch it).

* **Parameters:**
  * **candidate_or_id** – A [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate) or a sound id.
  * **seconds** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Audition length.
  * **library** – The [`foley.index.SoundLibrary`](_autosummary/foley.index.html.md#foley.index.SoundLibrary) (default: the shared one).
  * **byte_store** – A `MutableMapping[str, bytes]` to hold the preview (default: none —
    then `preview_uri` stays `None`).
  * **session** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`SessionStore`](_autosummary/foley.agent.session.html.md#foley.agent.session.SessionStore)]) – Optional session (unused here; accepted for a uniform signature).
* **Return type:**
  [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)
* **Returns:**
  The candidate with `preview_uri` set (or `None` on graceful degradation).

### foley.agent.preview.refine(session=None, , query=None, picked_ids=(), rejected_ids=(), hint=None, n=3, k=10, library=None, refiner=None)

Relevance-feedback refinement: expand for recall, boost picks, drop rejects, re-rank.

Distinct from [`foley.refine_query()`](_autosummary/foley.html.md#foley.refine_query) (which only paraphrases a query): this reads
the session’s picks/rejects (or the explicit `picked_ids` / `rejected_ids`),
expands the query into paraphrases for recall, gathers neighbours of every pick, drops
the rejects, and re-ranks by score.

* **Parameters:**
  * **session** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`SessionStore`](_autosummary/foley.agent.session.html.md#foley.agent.session.SessionStore)]) – The audition session (source of picks/rejects when not passed explicitly).
  * **query** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – The base text query to expand (optional).
  * **rejected_ids** ([`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`...`](https://docs.python.org/3/builtins/constants.html#Ellipsis)]) – Explicit feedback (override the session’s).
  * **hint** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – A steer for the query expansion.
  * **n** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Paraphrases to request.
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Result depth.
  * **library** – The [`foley.index.SoundLibrary`](_autosummary/foley.index.html.md#foley.index.SoundLibrary) (default: the shared one).
  * **refiner** – The query-expansion seam (default: the deterministic fake).
* **Return type:**
  [`RefineResult`](_autosummary/foley.agent.preview.html.md#foley.agent.preview.RefineResult)
* **Returns:**
  A [`RefineResult`](_autosummary/foley.agent.preview.html.md#foley.agent.preview.RefineResult).

### foley.agent.preview.similar_to(clip_or_candidate, , k=10, library=None)

“More like this” — neighbours of a sound id / candidate, or of a raw clip.

A `str` id or a [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate) uses by-id neighbours
(`SoundLibrary.similar`, self excluded); a raw working-array / bytes clip uses
audio-to-audio search (`SoundLibrary.search_clip`).

* **Parameters:**
  * **clip_or_candidate** – A sound id, a [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate), or a clip.
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – How many neighbours to return.
  * **library** – The [`foley.index.SoundLibrary`](_autosummary/foley.index.html.md#foley.index.SoundLibrary) (default: the shared one).
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]
* **Returns:**
  A list of [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate).


# _autosummary/foley.agent.protocols.html.md

# foley.agent.protocols

The structural DI seams of the SELECT stage — `Decomposer` / `Judge` / `Refiner`.

Three `@runtime_checkable` [`typing.Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)s (PEP 544), each a
behaviour-free, open-closed contract that every implementation (the deterministic
fake *and* the Anthropic-backed real impl) satisfies. They are dependency-injected
into [`foley.agent.find()`](_autosummary/foley.agent.html.md#foley.agent.find) by keyword (`decomposer=` / `judge=` / `refiner=`),
defaulting to a hermetic fake when `anthropic` (the `foley[agent]` extra) is absent.

Stdlib-only: the `base` shapes are imported under `TYPE_CHECKING` only (mirroring
[`foley.index.protocols`](_autosummary/foley.index.protocols.html.md#module-foley.index.protocols)), so importing this module pulls no heavy dependency and
keeps `import foley` dol-only.

### Classes

| [`Decomposer`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Decomposer)(\*args, \*\*kwargs)   | Narrative context → a sparse, salience-ranked, diegetic-tagged event list.             |
|-----------------------------------------------------------------------------------|----------------------------------------------------------------------------------------|
| [`Judge`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Judge)(\*args, \*\*kwargs)        | One rung of the verify ladder: does this candidate match this event? (report 10 §4.2). |
| [`Refiner`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Refiner)(\*args, \*\*kwargs)      | One event query → 2–4 paraphrases/expansions (query-expansion for retrieval).          |

### *class* foley.agent.protocols.Decomposer(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

Narrative context → a sparse, salience-ranked, diegetic-tagged event list.

Turns a story/commentary passage into `<= max_events` `SoundEvent`s,
enforcing the salience/density budget and the anachronism (`era_place`) guard.
The default is the deterministic [`KeywordDecomposer`](_autosummary/foley.agent.decompose.html.md#foley.agent.decompose.KeywordDecomposer);
[`AnthropicDecomposer`](_autosummary/foley.agent.decompose.html.md#foley.agent.decompose.AnthropicDecomposer) is the LLM-backed impl.

### *class* foley.agent.protocols.Judge(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

One rung of the verify ladder: does this candidate match this event? (report 10 §4.2).

`level` selects the rung — `clap` (cheap score gate),
`listen` (audio-LM), `judge` (LLM arbitration + scene consistency). The
returned `Verdict` carries `level` == the rung that produced it. Only the
`judge` rung’s real impl calls the LLM.

### *class* foley.agent.protocols.Refiner(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

One event query → 2–4 paraphrases/expansions (query-expansion for retrieval).

`hint` carries the verify-failure reason so the re-retrieval loop can steer the
next paraphrases. v1 feeds the list into a multi-query RRF search (embedding-fusion
is a later drop-in behind this same seam). The default is the deterministic
[`KeywordRefiner`](_autosummary/foley.agent.refine.html.md#foley.agent.refine.KeywordRefiner).


# _autosummary/foley.agent.refine.html.md

# foley.agent.refine

`refine_query` — one event query → 2–4 paraphrases for multi-query retrieval.

Retrieval quality is sensitive to phrasing (report 05 §2.2): several phrasings of the
same intent raise recall. v1 emits a *multi-query* list that
[`search_sounds()`](_autosummary/foley.agent.tools.html.md#foley.agent.tools.search_sounds) runs and RRF-merges (CLAP text-embedding
mean-pool fusion is a later drop-in behind this same seam). `refine_query` is also the
re-retrieval lever in the verify→refine loop, steered by a failure `hint`.

Two impls behind the [`Refiner`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Refiner) seam: the deterministic
[`KeywordRefiner`](_autosummary/foley.agent.refine.html.md#foley.agent.refine.KeywordRefiner) (default + CI fake) and the LLM-backed
[`AnthropicRefiner`](_autosummary/foley.agent.refine.html.md#foley.agent.refine.AnthropicRefiner) (`foley[agent]`; `anthropic` imported lazily inside the
method only, so `import foley` stays dol-only).

### Functions

| [`refine_query`](_autosummary/foley.agent.refine.html.md#foley.agent.refine.refine_query)(query, \*[, n, hint, refiner, \_span])   | Expand `query` into up to `n` paraphrases for multi-query retrieval.   |
|--------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------|

### Classes

| [`AnthropicRefiner`](_autosummary/foley.agent.refine.html.md#foley.agent.refine.AnthropicRefiner)(\*[, client, model, max_tokens])   | LLM-backed refiner (`foley[agent]`): Claude → a paraphrase list.    |
|------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------|
| [`KeywordRefiner`](_autosummary/foley.agent.refine.html.md#foley.agent.refine.KeywordRefiner)()                                    | Deterministic template-expansion refiner — the default and CI fake. |

### *class* foley.agent.refine.AnthropicRefiner(, client=None, model='claude-opus-4-8', max_tokens=500)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

LLM-backed refiner (`foley[agent]`): Claude → a paraphrase list.

`anthropic` imported lazily inside [`refine()`](_autosummary/foley.agent.refine.html.md#foley.agent.refine.AnthropicRefiner.refine). Stashes the `Message` on
`self.last_response` for the GenAI span.

#### refine(query, , n=3, hint=None)

Call Claude for `n` paraphrases; the original `query` is always first.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]

### *class* foley.agent.refine.KeywordRefiner

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Deterministic template-expansion refiner — the default and CI fake.

Expands a query into `n` distinct paraphrases via fixed descriptor templates; a
verify-failure `hint` nudges one extra variant. No RNG, no `anthropic` — same
query → identical paraphrase list.

#### refine(query, , n=3, hint=None)

Return up to `n` distinct paraphrases of `query` (the first is `query`).

* **Parameters:**
  * **query** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The event query to expand.
  * **n** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – How many paraphrases to return (2–4 is typical).
  * **hint** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional verify-failure reason to steer re-retrieval.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]

### foley.agent.refine.refine_query(query, , n=3, hint=None, refiner=None, \_span=None)

Expand `query` into up to `n` paraphrases for multi-query retrieval.

* **Parameters:**
  * **query** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The event query to expand.
  * **n** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Number of paraphrases.
  * **hint** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional verify-failure reason to steer re-retrieval.
  * **refiner** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Refiner`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Refiner)]) – An injected [`Refiner`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Refiner) (the DI seam);
    defaults to `_default_refiner()`.
  * **\_span** – Internal — the obs span handle for GenAI recording.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]


# _autosummary/foley.agent.session.html.md

# foley.agent.session

Session-scoped audition state — cached candidates, picks, and rejects (#12).

An interactive/agentic foley loop is stateful: `find` / `search` surface candidates,
the user (or an LLM) **previews**, **picks** and **rejects** them, and `plan` / `weave`
then consume the picks. [`SessionStore`](_autosummary/foley.agent.session.html.md#foley.agent.session.SessionStore) holds that state in three namespaced `dol`
stores under `FOLEY_DATA_DIR/sessions/{id}/` (swap in any Mapping for the cloud):

* **candidates** — the full `Candidate.to_dict()` keyed by sound id; the \*rehydration
  source\* so `plan` / `weave` can rebuild real [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate) objects
  from ids without the agent ever handling the heavy object. Internal — never exposed as
  writable MCP CRUD (protects the `from_dict` source).
* **picks** — accepted sounds (+ optional layer/onset) that `plan` folds into a timeline.
* **rejects** — dismissed sounds that feed `refine`’s relevance feedback.

Stdlib + `dol` only (via [`foley.stores`](_autosummary/foley.stores.html.md#module-foley.stores)); keeps `import foley` dol-only.

### Classes

| [`SessionStore`](_autosummary/foley.agent.session.html.md#foley.agent.session.SessionStore)([session_id, candidates, ...])   | Three namespaced stores for one audition session (candidates / picks / rejects).   |
|------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------|

### *class* foley.agent.session.SessionStore(session_id='default', candidates=None, picks=None, rejects=None)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Three namespaced stores for one audition session (candidates / picks / rejects).

Each store defaults to a [`foley.stores.make_session_store()`](_autosummary/foley.stores.html.md#foley.stores.make_session_store) JSON store; tests
inject plain dicts. All values are JSON-safe dicts.

* **Parameters:**
  * **session_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The session namespace.
  * **rejects** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Optional injected `MutableMapping` stores.

#### add_pick(sound_id, , layer=None, onset=None)

Persist an accepted pick (+ optional layer/onset); return the pick count.

* **Return type:**
  [`int`](https://docs.python.org/3/builtins/functions.html#int)

#### add_reject(sound_id, , reason=None)

Record a rejected sound (feeds `refine` relevance feedback); return the count.

* **Return type:**
  [`int`](https://docs.python.org/3/builtins/functions.html#int)

#### cache_candidates(candidates)

Cache each candidate’s full `to_dict()` keyed by sound id; return the count cached.

* **Return type:**
  [`int`](https://docs.python.org/3/builtins/functions.html#int)

#### drop_pick(sound_id)

Remove a pick (idempotent); return the remaining pick count.

* **Return type:**
  [`int`](https://docs.python.org/3/builtins/functions.html#int)

#### list_picks()

All persisted picks.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]

#### list_rejects()

All recorded rejects.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]

#### picked_ids()

The picked sound ids.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]

#### rehydrate(ids)

Rebuild [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate) objects for `ids` from the cache.

Missing ids are skipped. Uses `Candidate.from_dict` (rebuilds the nested
`SoundRecord` / `LicenseRecord` / `Verdict`).

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]

#### rejected_ids()

The rejected sound ids.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]


# _autosummary/foley.agent.tools.html.md

# foley.agent.tools

The SELECT orchestration: `find()` / `plan()` + the small pure tool wrappers.

This is the one seam that serves the Python API, the agent loop, and the future MCP
server (#12) — the same functions, three surfaces (report 05 §5.2). It OWNS every
side-effecting call (`search` → `gate` → `verify` → `decide` → `generate` →
`place`); [`decide()`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.decide) is a pure tail step that only branches.

`find()` opens **one** `foley.obs.run('find')` scope, so the nested
[`foley.search()`](_autosummary/foley.html.md#foley.search) / [`foley.generate()`](_autosummary/foley.html.md#foley.generate) façade calls aggregate into a single
reproducible run-manifest (report 10 §1.3); each stage emits a typed
[`Step`](_autosummary/foley.obs.run_artifact.html.md#foley.obs.run_artifact.Step) and each LLM rung a GenAI span. The plan it emits
is the **sparse** [`SoundDesignTimeline`](_autosummary/foley.base.html.md#foley.base.SoundDesignTimeline) subset — WEAVE (#8) resolves
anchors, mix, and mastering.

`import foley` stays dol-only: `foley.search` / `foley.generate` and the
generation-error hierarchy are imported lazily inside the functions that call them.

### Functions

| [`estimate_find_usd`](_autosummary/foley.agent.tools.html.md#foley.agent.tools.estimate_find_usd)(\*[, max_events, backend, ...])   | An upper bound on what one [`find()`](_autosummary/foley.agent.tools.html.md#foley.agent.tools.find) run can spend, or `None` if unknown.   |
|------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------|
| [`find`](_autosummary/foley.agent.tools.html.md#foley.agent.tools.find)(context, \*[, max_events, seconds, ...])       | The headline: a narrative context → verified, license-clean sound candidates.                                             |
| [`generate_sound`](_autosummary/foley.agent.tools.html.md#foley.agent.tools.generate_sound)(query, \*[, backend, library])       | Generate a clip for `query` (the fallback tool) — a thin wrapper over `foley.generate`.                                   |
| [`place_in_timeline`](_autosummary/foley.agent.tools.html.md#foley.agent.tools.place_in_timeline)(clip, \*[, onset, gain, ...])     | Emit the SPARSE per-item plan subset (`clip_ref·onset·gain·layer·loop`) — no more.                                        |
| [`plan`](_autosummary/foley.agent.tools.html.md#foley.agent.tools.plan)(candidates, \*[, transcript])                  | Fold verified candidates into the SPARSE `SoundDesignTimeline` (the SELECT→WEAVE bridge).                                 |
| [`search_sounds`](_autosummary/foley.agent.tools.html.md#foley.agent.tools.search_sounds)(queries, \*[, k, library, filters])   | Hybrid search for one query, or a multi-query RRF-merge (the SELECT retrieval tool).                                      |

### foley.agent.tools.estimate_find_usd(, max_events=6, backend='auto', llm=None, k=10, verify='listen', max_refine_loops=1, max_generations=1, injected=None, context_chars=0)

An upper bound on what one [`find()`](_autosummary/foley.agent.tools.html.md#foley.agent.tools.find) run can spend, or `None` if unknown.

Every generation (`max_generations` per event through `backend`) plus every
LLM call the default rungs could make: one decomposition, `max_refine_loops`
refinements per event, `k` judge calls per retrieval pass above the `clap`
rung, and one re-verification per generation. Injected rungs are the caller’s to
meter (their real calls are still checked one by one).

* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`float`](https://docs.python.org/3/builtins/functions.html#float)]

### foley.agent.tools.find(context, , max_events=6, seconds=None, intended_use=None, backend='auto', verify='listen', stream=False, k=10, tau_retrieve=0.5, tau_clap=0.35, max_refine_loops=1, budget=None, library=None, decomposer=None, judge=None, refiner=None, llm=None)

The headline: a narrative context → verified, license-clean sound candidates.

`decompose → (per event) refine/search → verify_match ladder → decide (with the
fail-closed license gate FIRST) → place` (report 05 §5). Works out of the box —
`foley.find("She pushed open the heavy oak door; rain hammered outside.")` — with
deterministic defaults; every model / threshold / seam is an optional keyword.

* **Parameters:**
  * **context** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The narrative passage.
  * **max_events** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The sparse density cap on decomposed events.
  * **seconds** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`float`](https://docs.python.org/3/builtins/functions.html#float)]) – Optional passage duration (density-window hint; forwarded).
  * **intended_use** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`IntendedUse`](_autosummary/foley.base.html.md#foley.base.IntendedUse)]) – The caller’s rights intent (default:
    [`foley.licensing.DEFAULT_INTENDED_USE`](_autosummary/foley.licensing.html.md#foley.licensing.DEFAULT_INTENDED_USE) — commercial publishing;
    NC / SA material is refused unless you pass a different intent).
  * **backend** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Generation backend for the fallback (`'auto'` → `foley.generate`’s default).
  * **verify** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`VerifyLevel`](_autosummary/foley.base.html.md#foley.base.VerifyLevel)]) – The max verify rung — `'clap'` | `'listen'` | `'judge'`.
  * **stream** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – If `True`, return a generator yielding one `Candidate` per
    resolved event; else return the collected `list`.
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Retrieval shortlist depth per query.
  * **tau_retrieve** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Confidence threshold to auto-accept a retrieved clip.
  * **tau_clap** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – The `clap`-rung gate threshold.
  * **max_refine_loops** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Max refine→re-retrieve passes per event (also the default
    `Budget`).
  * **budget** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Budget`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.Budget)]) – An explicit `Budget` (overrides `max_refine_loops`). It is
    also the run’s spend cap: `Budget(max_usd=...)` (default $1; $3 when
    `llm='anthropic'` or `$FOLEY_LLM=anthropic`, whose default run is
    bounded at about $2.52 — [`foley.cost.DEFAULT_MAX_USD_BY_LLM`](_autosummary/foley.cost.html.md#foley.cost.DEFAULT_MAX_USD_BY_LLM)) bounds
    every paid call the run makes — generations and paid LLM rungs —
    cumulatively, and a call of unknown cost needs
    `Budget(approve_unknown_cost=True)` (#57).
  * **library** – Target `SoundLibrary` (default: the process-wide default).
  * **refiner** (*decomposer / judge /*) – Injected DI seams
    ([`Decomposer`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Decomposer) / `Judge` / `Refiner`);
    each defaults to the `llm` provider’s implementation.
  * **llm** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Which LLM the decompose / refine / judge rungs use when not injected —
    `'fake'` | `'local'` | `'anthropic'`. `None` reads `$FOLEY_LLM`,
    then falls back to the free default (a configured local endpoint, else the
    deterministic fake). A key being present never opts in to paid calls.
* **Return type:**
  `Union`[[`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)], [`Iterator`](https://docs.python.org/3/library/typing.html#typing.Iterator)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]]
* **Returns:**
  `list[Candidate]` (`stream=False`) or an `Iterator[Candidate]`
  (`stream=True`) — one verified, license-clean candidate per resolved event.

### foley.agent.tools.generate_sound(query, , backend='auto', library=None)

Generate a clip for `query` (the fallback tool) — a thin wrapper over `foley.generate`.

Returns a `Candidate` (`origin=generated`); raises the `GenerationError`
hierarchy on refusal / no-result (caught fail-closed by `_generate_and_reverify()`).

* **Return type:**
  [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)

### foley.agent.tools.place_in_timeline(clip, , onset=None, gain=0.0, layer=Layer.sfx_fg, loop=False)

Emit the SPARSE per-item plan subset (`clip_ref·onset·gain·layer·loop`) — no more.

`onset` stays a SYMBOLIC anchor string (never a resolved offset); mix / processing /
alignment / mastering are WEAVE’s (#8) job, not SELECT’s.

* **Return type:**
  [`TimelineItem`](_autosummary/foley.base.html.md#foley.base.TimelineItem)

### foley.agent.tools.plan(candidates, , transcript=None)

Fold verified candidates into the SPARSE `SoundDesignTimeline` (the SELECT→WEAVE bridge).

One `TimelineItem` per candidate (`onset·gain·layer·loop` only, from its
`SoundEvent`), joined to the run-artifact via `run_manifest_ref` — the
reserved #8 `plan_ref` slot is filled when called inside an active `foley.obs`
run scope (`None`-safe otherwise).

* **Parameters:**
  * **candidates** ([`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]) – The candidates returned by [`find()`](_autosummary/foley.agent.tools.html.md#foley.agent.tools.find).
  * **transcript** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional narration transcript (WEAVE resolves the reference).
* **Return type:**
  [`SoundDesignTimeline`](_autosummary/foley.base.html.md#foley.base.SoundDesignTimeline)

### foley.agent.tools.search_sounds(queries, , k=10, library=None, filters=None)

Hybrid search for one query, or a multi-query RRF-merge (the SELECT retrieval tool).

Delegates a single query VERBATIM to `SoundLibrary.search()` (so the retrieval
ranking the [nDCG@10](mailto:nDCG@10) gate measures is untouched); a multi-query list runs each query
and RRF-merges (`k=RRF_K`) — the query-expansion recall lever, exercised only on a
refine pass.

* **Parameters:**
  * **queries** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]) – One query string, or a list of paraphrases to merge.
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Number of results.
  * **library** – Target library (default: the process-wide default).
  * **filters** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Extra `SoundLibrary.search()` kwargs (e.g. the license prefilter).
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]


# _autosummary/foley.agent.verify.html.md

# foley.agent.verify

`verify_match` — the retrieve→verify ladder (clap → listen → judge) + `Judge` impls.

Retrieval gives a ranked shortlist; verification confirms the *intent* before a clip is
accepted (report 05 §3). Three rungs, cheapest first:

* `clap` — [`ClapJudge`](_autosummary/foley.agent.verify.html.md#foley.agent.verify.ClapJudge), the zero-config gate on the retrieval cosine (no ML).
* `listen` — an audio-LM “does this contain {event}?” check; **#7 ships the
  deterministic :class:\`StringOverlapJudge\`** here (caption/tag overlap). The real
  Qwen2-Audio impl is a future [`Judge`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Judge) behind this same
  `VerifyLevel` seam — no orchestrator change.
* `judge` — [`AnthropicJudge`](_autosummary/foley.agent.verify.html.md#foley.agent.verify.AnthropicJudge), an LLM arbiter (ties + scene consistency),
  `foley[agent]`; `anthropic` imported lazily.

The ladder is AND-confirming: the `clap` gate must pass before a higher rung is asked
to confirm. \*\*This module is the extension point #10b (the Tier-2 fit-judge) plugs
into\*\* — a new `Judge` bound at `level=judge` via the `judge=` keyword, with its
own fit-precision metric; it does not touch retrieval ranking (the [nDCG@10](mailto:nDCG@10) gate).

### Functions

| [`verify_match`](_autosummary/foley.agent.verify.html.md#foley.agent.verify.verify_match)(event, candidate, \*[, level, ...])   | Verify `candidate` against `event` up to rung `level` (AND-confirming ladder).   |
|-----------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------|

### Classes

| [`AnthropicJudge`](_autosummary/foley.agent.verify.html.md#foley.agent.verify.AnthropicJudge)(\*[, client, model, max_tokens])   | LLM arbiter for the `judge` rung (`foley[agent]`): Claude → a `Verdict`.             |
|----------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------|
| [`AudioLMJudge`](_autosummary/foley.agent.verify.html.md#foley.agent.verify.AudioLMJudge)(\*[, pipeline, model, ...])          | The audio-LM `listen` rung (`foley[fit]`): Qwen2-Audio "does this contain {event}?". |
| [`ClapJudge`](_autosummary/foley.agent.verify.html.md#foley.agent.verify.ClapJudge)()                                       | The zero-config `clap` rung: gate on the retrieval cosine (no ML, no network).       |
| [`StringOverlapJudge`](_autosummary/foley.agent.verify.html.md#foley.agent.verify.StringOverlapJudge)(\*[, threshold])               | Deterministic stand-in for the `listen`/`judge` rungs (the hermetic CI fake).        |

### *class* foley.agent.verify.AnthropicJudge(, client=None, model='claude-opus-4-8', max_tokens=500)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

LLM arbiter for the `judge` rung (`foley[agent]`): Claude → a `Verdict`.

`anthropic` imported lazily inside [`judge()`](_autosummary/foley.agent.verify.html.md#foley.agent.verify.AnthropicJudge.judge). Stashes the `Message` on
`self.last_response` for the GenAI span.

#### judge(event, candidate, , level=VerifyLevel.judge)

Call Claude to arbitrate the match; returns a `Verdict` at `level`.

* **Return type:**
  [`Verdict`](_autosummary/foley.base.html.md#foley.base.Verdict)

### *class* foley.agent.verify.AudioLMJudge(, pipeline=None, model='Qwen/Qwen2-Audio-7B-Instruct', max_tokens=300, tau=0.5)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

The audio-LM `listen` rung (`foley[fit]`): Qwen2-Audio “does this contain {event}?”.

`transformers`/`torch` are imported lazily inside [`judge()`](_autosummary/foley.agent.verify.html.md#foley.agent.verify.AudioLMJudge.judge); an injected
`pipeline` (the test seam, mirroring the Stable-Audio fake-pipeline seam) drives the
whole byte-decode → prompt → P(yes) → `Verdict` path with no torch. Stashes
`self.model` + `self.last_response` so `verify_match`’s GenAI span stays
informative (same getattr contract [`AnthropicJudge`](_autosummary/foley.agent.verify.html.md#foley.agent.verify.AnthropicJudge) uses).

#### judge(event, candidate, , level=VerifyLevel.listen)

Listen to the clip and return the AQAScore `Verdict` (`P(yes) ≥ tau`).

* **Return type:**
  [`Verdict`](_autosummary/foley.base.html.md#foley.base.Verdict)

### *class* foley.agent.verify.ClapJudge

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

The zero-config `clap` rung: gate on the retrieval cosine (no ML, no network).

`match` iff the candidate’s `clap_score` clears `tau_clap`; `confidence` is
the score clamped to `0..1` so it is comparable to the higher rungs’ 0..1 scores.

#### judge(event, candidate, , level=VerifyLevel.clap, tau_clap=0.35)

Return the `clap`-rung `Verdict` for `candidate`.

* **Return type:**
  [`Verdict`](_autosummary/foley.base.html.md#foley.base.Verdict)

### *class* foley.agent.verify.StringOverlapJudge(, threshold=0.3)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Deterministic stand-in for the `listen`/`judge` rungs (the hermetic CI fake).

Jaccard token overlap of the event query vs the candidate’s caption + tags. Lets the
full ladder + [`decide()`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.decide) branch run with zero ML/network. The
returned `Verdict` echoes back the requested `level`.

#### judge(event, candidate, , level=VerifyLevel.listen)

Return the token-overlap `Verdict` at the requested `level`.

* **Return type:**
  [`Verdict`](_autosummary/foley.base.html.md#foley.base.Verdict)

### foley.agent.verify.verify_match(event, candidate, , level=VerifyLevel.clap, judge=None, tau_clap=0.35, \_span=None)

Verify `candidate` against `event` up to rung `level` (AND-confirming ladder).

Runs the `clap` gate always; if `level` is higher **and** the clap gate passed,
escalates to the injected/​default judge for that rung and returns *its* verdict
(`Verdict.level` == the producing rung).

* **Parameters:**
  * **event** ([`SoundEvent`](_autosummary/foley.base.html.md#foley.base.SoundEvent)) – The wanted `SoundEvent`.
  * **candidate** ([`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)) – A **license-clean** `Candidate` — this MUST run after the
    [`gate_candidates()`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.gate_candidates) gate (asserted).
  * **level** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`VerifyLevel`](_autosummary/foley.base.html.md#foley.base.VerifyLevel)) – The max rung to climb (`clap` | `listen` | `judge`).
  * **judge** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Judge`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Judge)]) – An injected [`Judge`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Judge) for the higher rungs
    (the DI seam; defaults per `_default_judge()`).
  * **tau_clap** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – The clap-gate threshold.
  * **\_span** – Internal — the obs span handle for GenAI recording on the LLM rung.
* **Raises:**
  [**AssertionError**](https://docs.python.org/3/builtins/exceptions.html#AssertionError) – If `candidate.license_ok` is not `True` (verify-before-gate
      is a bug — the license gate is the fail-closed first pass).
* **Return type:**
  [`Verdict`](_autosummary/foley.base.html.md#foley.base.Verdict)


# _autosummary/foley.agent_kit.html.md

# foley.agent_kit

Install foley’s shipped agent kit — the consumer skill + Claude slash command + subagent.

`pip install foley` ships an agent-facing kit under `foley/data/`: the
`foley-sound-design` skill (`gh skill`-installable), a `/foley-score` slash command, and a
`sound-designer` subagent. [`install_agent_kit()`](_autosummary/foley.agent_kit.html.md#foley.agent_kit.install_agent_kit) copies them into a target `.claude/`
directory so an agent host (Claude Code) discovers them — the “ready to use out of the box” step
for the AI-first surface. Stdlib-only, so importing this keeps `import foley` dol-only.

### Functions

| [`install_agent_kit`](_autosummary/foley.agent_kit.html.md#foley.agent_kit.install_agent_kit)([dest, overwrite])   | Copy the shipped skill + slash command + subagent into `dest` (a `.claude` dir).   |
|-----------------------------------------------------------------------------------------|------------------------------------------------------------------------------------|

### foley.agent_kit.install_agent_kit(dest='./.claude', , overwrite=False)

Copy the shipped skill + slash command + subagent into `dest` (a `.claude` dir).

Installs:

* `dest/skills/foley-sound-design/` — the consumer skill (the sound-design playbook),
* `dest/commands/foley-score.md` — the `/foley-score` slash command,
* `dest/agents/sound-designer.md` — the `sound-designer` subagent.

* **Parameters:**
  * **dest** – The target agent-config dir (default `./.claude` in the cwd; pass `~/.claude`
    to install globally for every project).
  * **overwrite** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Replace existing files/dirs (default: skip what already exists).
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]
* **Returns:**
  The list of installed paths (as strings) — empty entries that already existed are skipped.


# _autosummary/foley.audio.html.md

# foley.audio

Audio I/O and DSP primitives for foley.

The foundation every other foley layer decodes/encodes/transforms audio through.
It commits to a small, explicit set of representations (report 09):

> * **Working** (in RAM, between DSP ops): a `float32` NumPy array at
>   `48 kHz` — shape `(frames,)` mono or `(frames, channels)`. This is
>   the lingua franca of `soundfile`/`librosa`/`soxr` and exactly what
>   CLAP expects.
> * **Archive** (bytes at rest): **FLAC**, source sample-rate and bit-depth
>   preserved — lossless, ~40-60% smaller than WAV, self-describing.
> * **Delivery / preview** (derived): **Opus** by default (not implemented in
>   the foundation — an FFmpeg/`foley[ffmpeg]` concern surfaced later).

Design rules honoured here:

> * **Zero-dep import.** This module imports only the stdlib at top level; the
>   heavy libraries (`numpy`, `soundfile`, `soxr`, `librosa`,
>   `pyloudnorm`) are lazy-imported *inside* the functions that need them, so
>   `import foley.audio` always succeeds on a bare install and only the
>   function you call pays for (and requires) its dependency.
> * **No magic numbers.** Every default (sample rate, dtype, archive subtype,
>   resample quality, trim/fade/loudness targets) is a named module-level
>   constant used as a keyword-only argument default.
> * **Never bundle FFmpeg / pydub / torchaudio.** MP3/AAC/Opus transcode is an
>   optional external-tool concern, not part of this royalty-free core.

All ops assume the working representation (float array, time on axis 0). `load`
is the one function that produces it from arbitrary sources (path, raw bytes, or
a file-like object).

### Module Attributes

| [`AudioSource`](_autosummary/foley.audio.html.md#foley.audio.AudioSource)   | a filesystem path, raw encoded bytes, or an already-open binary file-like object (e.g. `io.BytesIO`).   |
|----------------------------------------------------------------|---------------------------------------------------------------------------------------------------------|

### Functions

| [`encode`](_autosummary/foley.audio.html.md#foley.audio.encode)(samples, sample_rate, \*[, fmt, subtype])   | Encode `samples` fully in memory and return the container bytes.      |
|-----------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------|
| [`ensure_channels`](_autosummary/foley.audio.html.md#foley.audio.ensure_channels)(samples, \*, channels)             | Coerce `samples` to exactly `channels` channels.                      |
| [`fade`](_autosummary/foley.audio.html.md#foley.audio.fade)(samples, sample_rate, \*[, fade_in_s, ...])   | Apply in/out gain ramps to `samples` (a short declick by default).    |
| [`load`](_autosummary/foley.audio.html.md#foley.audio.load)(src, \*[, target_sr, mono, dtype])            | Decode audio into a float working array.                              |
| [`loudness_normalize`](_autosummary/foley.audio.html.md#foley.audio.loudness_normalize)(samples, sample_rate, \*)       | Loudness-normalize to `target_lufs`, then keep it peak-safe.          |
| [`resample`](_autosummary/foley.audio.html.md#foley.audio.resample)(samples, sample_rate, \*[, ...])          | Resample `samples` to `target_sr` (a no-op when already there).       |
| [`save`](_autosummary/foley.audio.html.md#foley.audio.save)(samples, sample_rate, dst, \*[, fmt, ...])    | Write `samples` to `dst` as `fmt`/`subtype` (default = FLAC archive). |
| [`to_mono`](_autosummary/foley.audio.html.md#foley.audio.to_mono)(samples)                                   | Down-mix to mono by averaging channels; 1-D input passes through.     |
| [`to_working`](_autosummary/foley.audio.html.md#foley.audio.to_working)(samples, sample_rate, \*[, mono, ...])  | Produce the canonical CLAP/QC working array from an arbitrary clip.   |
| [`trim_silence`](_autosummary/foley.audio.html.md#foley.audio.trim_silence)(samples, sample_rate, \*[, top_db])   | Strip leading/trailing silence, returning the clip and its kept span. |

### foley.audio.AudioSource

a filesystem path, raw encoded bytes, or an
already-open binary file-like object (e.g. `io.BytesIO`).

* **Type:**
  A source `load` can decode

alias of [`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) | [`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes) | [`BinaryIO`](https://docs.python.org/3/library/typing.html#typing.BinaryIO)

### foley.audio.encode(samples, sample_rate, , fmt='flac', subtype='PCM_24')

Encode `samples` fully in memory and return the container bytes.

This is the producer for the content-addressed byte store: default output is
the FLAC archive form.

* **Parameters:**
  * **samples** (`ndarray`) – The working array to encode.
  * **sample_rate** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Sample rate in Hz.
  * **fmt** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Container/codec name (case-insensitive).
  * **subtype** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Sample subtype (e.g. `PCM_24`).
* **Return type:**
  [`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes)
* **Returns:**
  The encoded audio as `bytes`.

Lazy dependency: `soundfile`.

### foley.audio.ensure_channels(samples, , channels)

Coerce `samples` to exactly `channels` channels.

Mappings: mono -> N by duplication; N -> mono by mean; N -> M (N != M, both
> 1) by collapsing to mono then tiling up to M.

* **Parameters:**
  * **samples** (`ndarray`) – Mono or multichannel working array.
  * **channels** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Target channel count (must be >= 1).
* **Return type:**
  `ndarray`
* **Returns:**
  A `(frames,)` array when `channels == 1`, else a
  `(frames, channels)` array.
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If `channels < 1`.

Lazy dependency: `numpy` (only for the up-mix / tile path).

### foley.audio.fade(samples, sample_rate, , fade_in_s=0.01, fade_out_s=0.01, kind='linear')

Apply in/out gain ramps to `samples` (a short declick by default).

Ramp lengths are clamped to at most `len(samples) // 2` so the in- and
out-ramps never overlap on tiny inputs.

* **Parameters:**
  * **samples** (`ndarray`) – Working array (mono or multichannel).
  * **sample_rate** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Sample rate in Hz (converts the fade durations to samples).
  * **fade_in_s** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Fade-in duration in seconds.
  * **fade_out_s** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Fade-out duration in seconds.
  * **kind** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – `'linear'` or `'equal_power'` ramp shape.
* **Return type:**
  `ndarray`
* **Returns:**
  A new array with the fade envelope applied (input is not mutated).

Lazy dependency: `numpy`.

### foley.audio.load(src, , target_sr=None, mono=False, dtype='float32')

Decode audio into a float working array.

`src` may be a filesystem path, raw encoded `bytes` (wrapped in a
`BytesIO` so nothing touches disk), or any binary file-like object.

* **Parameters:**
  * **src** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike), [`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes), [`BinaryIO`](https://docs.python.org/3/library/typing.html#typing.BinaryIO)]) – Path, raw bytes, or file-like object to decode.
  * **target_sr** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`int`](https://docs.python.org/3/builtins/functions.html#int)]) – If given, resample the decoded audio to this rate (via
    [`resample()`](_autosummary/foley.audio.html.md#foley.audio.resample)); otherwise the native rate is returned.
  * **mono** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – If `True`, down-mix multichannel audio to mono.
  * **dtype** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – NumPy dtype string for the returned array (default `float32`).
* **Return type:**
  [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[`ndarray`, [`int`](https://docs.python.org/3/builtins/functions.html#int)]
* **Returns:**
  A `(samples, sample_rate)` tuple. `samples` has shape `(frames,)`
  (mono) or `(frames, channels)`; `sample_rate` reflects any resample.

Lazy dependencies: `soundfile` (and `soxr` when `target_sr` differs).

### foley.audio.loudness_normalize(samples, sample_rate, , target_lufs=-16.0, peak_ceiling_dbfs=-1.0, min_block_s=0.4)

Loudness-normalize to `target_lufs`, then keep it peak-safe.

Integrated loudness is measured (ITU-R BS.1770-4 / EBU R128), the signal is
scaled to `target_lufs`, and finally attenuated so its sample peak sits at
or below `peak_ceiling_dbfs`. Two inputs are returned **unchanged** (flag
them, don’t amplify): near-silent input (measured loudness at or below
`LUFS_GATE_FLOOR`), and a clip shorter than one BS.1770 gating block
(`min_block_s`) — which `pyloudnorm` cannot measure and would otherwise
raise `ValueError` on (routine for one-shots: clicks, blips, gunshots).

* **Parameters:**
  * **samples** (`ndarray`) – Working array (mono or multichannel, time on axis 0 — the layout
    pyloudnorm expects).
  * **sample_rate** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Sample rate in Hz.
  * **target_lufs** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Desired integrated loudness (default = foley’s podcast
    target).
  * **peak_ceiling_dbfs** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Sample-peak ceiling (dBFS) applied after loudness
    normalization. Note this is a *sample*-peak limit, not an inter-sample
    true-peak (dBTP) limit — see [`foley.qc.true_peak_dbtp()`](_autosummary/foley.qc.html.md#foley.qc.true_peak_dbtp) for the
    oversampled measurement.
  * **min_block_s** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Minimum clip length (seconds) that can be loudness-measured;
    shorter clips are returned unchanged with `measured = -inf`.
* **Return type:**
  [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[`ndarray`, [`float`](https://docs.python.org/3/builtins/functions.html#float)]
* **Returns:**
  `(normalized, measured_input_lufs)`. When the input is near-silent or
  too short to measure, `normalized` is the unchanged input and
  `measured_input_lufs` is at or below `LUFS_GATE_FLOOR` (`-inf` for
  the too-short case).

Lazy dependencies: `pyloudnorm` (+ `numpy`).

### foley.audio.resample(samples, sample_rate, , target_sr=48000, quality='HQ')

Resample `samples` to `target_sr` (a no-op when already there).

* **Parameters:**
  * **samples** (`ndarray`) – Working array (mono `(frames,)` or `(frames, channels)`).
  * **sample_rate** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The array’s current rate in Hz.
  * **target_sr** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Desired output rate in Hz (default = the working rate).
  * **quality** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – soxr quality preset (`QQ`/`LQ`/`MQ`/`HQ`/`VHQ`).
* **Return type:**
  `ndarray`
* **Returns:**
  The resampled array (the input unchanged when `sample_rate ==
  target_sr`). dtype is preserved by soxr.

Lazy dependency: `soxr`.

### foley.audio.save(samples, sample_rate, dst, , fmt='flac', subtype='PCM_24')

Write `samples` to `dst` as `fmt`/`subtype` (default = FLAC archive).

* **Parameters:**
  * **samples** (`ndarray`) – The working array to write (shape `(frames,)` or
    `(frames, channels)`).
  * **sample_rate** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Sample rate in Hz.
  * **dst** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike), [`BinaryIO`](https://docs.python.org/3/library/typing.html#typing.BinaryIO)]) – Destination path or writable binary file-like object.
  * **fmt** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Container/codec name (case-insensitive; passed to libsndfile).
  * **subtype** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Sample subtype (e.g. `PCM_24`, `PCM_16`, `FLOAT`).
* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

Lazy dependency: `soundfile`.

### foley.audio.to_mono(samples)

Down-mix to mono by averaging channels; 1-D input passes through.

* **Parameters:**
  **samples** (`ndarray`) – Mono `(frames,)` or multichannel `(frames, channels)` array.
* **Return type:**
  `ndarray`
* **Returns:**
  A 1-D mono array (dtype preserved).

### foley.audio.to_working(samples, sample_rate, , mono=True, target_sr=48000, dtype='float32')

Produce the canonical CLAP/QC working array from an arbitrary clip.

Down-mixes (when `mono`), resamples to `target_sr`, and casts to
`dtype` — the `float32` @ 48 kHz mono array every embedder/tagger/QC
check consumes.

* **Parameters:**
  * **samples** (`ndarray`) – Decoded working array (mono or multichannel).
  * **sample_rate** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The array’s current rate in Hz.
  * **mono** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – If `True`, down-mix to mono.
  * **target_sr** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Working sample rate in Hz.
  * **dtype** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Output NumPy dtype string.
* **Return type:**
  `ndarray`
* **Returns:**
  The canonical working array.

Lazy dependency: `soxr` (only when `sample_rate != target_sr`).

### foley.audio.trim_silence(samples, sample_rate, , top_db=30.0)

Strip leading/trailing silence, returning the clip and its kept span.

Silence detection runs on a transient mono down-mix (so librosa’s time-last
convention never clashes with foley’s time-first `(frames, channels)`
layout); the returned sample indices then slice the *original* array along
axis 0, preserving its channel layout.

* **Parameters:**
  * **samples** (`ndarray`) – Working array (mono or multichannel).
  * **sample_rate** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Sample rate in Hz (kept in the signature for API symmetry;
    trimming is index-based).
  * **top_db** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – A frame is silent when it sits at least this many dB below the
    reference (peak) level.
* **Return type:**
  [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[`ndarray`, [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`int`](https://docs.python.org/3/builtins/functions.html#int), [`int`](https://docs.python.org/3/builtins/functions.html#int)]]
* **Returns:**
  `(trimmed, (start_sample, end_sample))`. On all-silent (or otherwise
  degenerate) input the original array is returned unchanged with a
  full-length span `(0, len(samples))`.

Lazy dependency: `librosa`.


# _autosummary/foley.base.html.md

# foley.base

Canonical data models for foley — the single source of truth (SSOT) types.

This module is stdlib-only and declarative: it defines the dataclasses, enums,
and affordance registries every other layer shares, plus generic dict/JSON
(de)serialization. It contains NO policy (see `foley.licensing`) and NO I/O
(see `foley.stores` / `foley.audio`).

Serialization contract:

> * `record.to_dict()`  -> a plain dict (nested dataclasses recursed via
>   `dataclasses.asdict`; enum members preserved, and JSON-safe because every
>   enum subclasses `str`).
> * `record.to_json()`  -> a JSON string.
> * `Cls.from_dict(d)` / `Cls.from_json(s)` -> reconstructs, coercing enum
>   fields from their string values and nested `LicenseRecord` from its dict.
>   Unknown keys are ignored (forward-compatible schema evolution); missing keys
>   fall back to field defaults.

### Module Attributes

| [`QUERY_AFFORDANCES`](_autosummary/foley.base.html.md#foley.base.QUERY_AFFORDANCES)      | Unified query-stage parameters (search / find / filter surface).        |
|-------------------------------------------------------------------------|-------------------------------------------------------------------------|
| [`GENERATION_AFFORDANCES`](_autosummary/foley.base.html.md#foley.base.GENERATION_AFFORDANCES) | Unified generation-stage parameters (generate backends map onto these). |
| [`MASTER_PROFILES`](_autosummary/foley.base.html.md#foley.base.MASTER_PROFILES)        | The named delivery targets (report 06 §5.2).                            |

### Functions

| [`resolve_master`](_autosummary/foley.base.html.md#foley.base.resolve_master)(master)   | Resolve a master spec (profile name, explicit profile, or `None`) to a `MasterProfile`.   |
|---------------------------------------------------------------------------|-------------------------------------------------------------------------------------------|

### Classes

| [`AcquisitionMethod`](_autosummary/foley.base.html.md#foley.base.AcquisitionMethod)(\*values)                       | How a sound entered foley (retrieval channel or origin).                                 |
|----------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------|
| [`Affordance`](_autosummary/foley.base.html.md#foley.base.Affordance)(name, type, description[, ...])        | Descriptor for a unified parameter affordance (arioso analog).                           |
| [`Anchor`](_autosummary/foley.base.html.md#foley.base.Anchor)(\*values)                                  | How a WEAVE `Placement` binds its symbolic time to the narration (report 06 §2.4).       |
| [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)(sound[, origin, event, ...])            | A ranked, license-checked, (optionally) verified sound for one SoundEvent.               |
| [`CandidateOrigin`](_autosummary/foley.base.html.md#foley.base.CandidateOrigin)(\*values)                         | Whether a candidate was retrieved from the index or freshly generated.                   |
| [`IntendedUse`](_autosummary/foley.base.html.md#foley.base.IntendedUse)([commercial, publish, ...])           | What the caller intends to do with a sound; consumed by `keep()`.                        |
| [`Layer`](_autosummary/foley.base.html.md#foley.base.Layer)(\*values)                                   | Mix layer (shared by `SoundEvent` now and `TimelineItem` later).                         |
| [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)(source[, source_id, ...])           | Per-sound rights + provenance.                                                           |
| [`MasterProfile`](_autosummary/foley.base.html.md#foley.base.MasterProfile)([target_lufs, true_peak_db, lra])   | Loudness master target — the delivery spec as data, not code (report 06 §5.2).           |
| [`Placement`](_autosummary/foley.base.html.md#foley.base.Placement)([anchor, ref, onset, pre_roll, ...])    | WHERE/WHEN a clip sits — a symbolic anchor plus its resolved time (report 06 §6.3).      |
| [`Processing`](_autosummary/foley.base.html.md#foley.base.Processing)([gain_db, pan, distance, ...])         | HOW a clip sounds — all optional with identity defaults (report 06 §3, §6.3).            |
| [`Salience`](_autosummary/foley.base.html.md#foley.base.Salience)(\*values)                                | How prominent a sound event is within a passage.                                         |
| [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)()                               | Adds `to_dict`/`to_json`/`from_dict`/`from_json` to a dataclass.                         |
| [`SoundDesignTimeline`](_autosummary/foley.base.html.md#foley.base.SoundDesignTimeline)([items, ...])                 | The editable, re-renderable sound-design plan — the SELECT→WEAVE bridge and render SSOT. |
| [`SoundEvent`](_autosummary/foley.base.html.md#foley.base.SoundEvent)(query[, layer, diegetic, ...])         | One salient, physically-audible event decomposed from a passage.                         |
| [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord)(id[, content_sha256, hash_algo, ...]) | Canonical SSOT per sound.                                                                |
| [`StorageMode`](_autosummary/foley.base.html.md#foley.base.StorageMode)(\*values)                             | How a sound's bytes are held (DERIVED from `license.cache_bytes_ok`).                    |
| [`TimelineItem`](_autosummary/foley.base.html.md#foley.base.TimelineItem)(clip_ref[, onset, gain, layer, ...]) | One placed sound on the sound-design timeline — sparse seed + resolved render fields.    |
| [`Verdict`](_autosummary/foley.base.html.md#foley.base.Verdict)(match, confidence[, reason, level])       | The result of one verification rung for a candidate.                                     |
| [`VerifyLevel`](_autosummary/foley.base.html.md#foley.base.VerifyLevel)(\*values)                             | Which rung of the verification ladder produced a `Verdict`.                              |

### *class* foley.base.AcquisitionMethod(\*values)

Bases: [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Enum`](https://docs.python.org/3/library/enum.html#enum.Enum)

How a sound entered foley (retrieval channel or origin).

### *class* foley.base.Affordance(name, type, description, default=None, stage='query', carries_meaning=False)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Descriptor for a unified parameter affordance (arioso analog).

#### name

Canonical parameter name used at the façade level.

#### type

Expected Python type.

#### description

Human-readable description.

#### default

Default value (`None` = no default / required).

#### stage

`'query'` (search/find/filter) or `'generate'`.

#### carries_meaning

Dropping it would change what the caller gets (a seed they
will rely on to reproduce, content they asked to exclude), so a backend
that cannot honour it raises instead of dropping it (#53).

### *class* foley.base.Anchor(\*values)

Bases: [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Enum`](https://docs.python.org/3/library/enum.html#enum.Enum)

How a WEAVE `Placement` binds its symbolic time to the narration (report 06 §2.4).

`absolute` is a fixed offset; the rest resolve against the forced-aligned
`word_timeline` — `word` to a trigger word’s onset, `sentence` across a
sentence span, `scene`/`paragraph` to a boundary’s first spoken word.

### *class* foley.base.Candidate(sound, origin=CandidateOrigin.retrieved, event=None, clap_score=None, bm25_score=None, rrf_score=None, rerank_score=None, verdict=None, license_ok=None, preview_uri=None, notes=<factory>, cost_estimate_usd=None, cost_actual_usd=None)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

A ranked, license-checked, (optionally) verified sound for one SoundEvent.

Retrieval and generation return the SAME shape; only `origin` differs.
Nested `sound` / `event` / `verdict` dataclasses are decoded generically
by `_decode()` — no per-field code needed.

### *class* foley.base.CandidateOrigin(\*values)

Bases: [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Enum`](https://docs.python.org/3/library/enum.html#enum.Enum)

Whether a candidate was retrieved from the index or freshly generated.

### foley.base.GENERATION_AFFORDANCES *: [dict](https://docs.python.org/3/builtins/stdtypes.html#dict)[[str](https://docs.python.org/3/builtins/stdtypes.html#str), [Affordance](_autosummary/foley.base.html.md#foley.base.Affordance)]* *= {'duration': Affordance(name='duration', type=<class 'float'>, description='Seconds; None => backend default', default=None, stage='generate', carries_meaning=False), 'loop': Affordance(name='loop', type=<class 'bool'>, description='Seamless-loopable clip', default=False, stage='generate', carries_meaning=False), 'negative_prompt': Affordance(name='negative_prompt', type=<class 'str'>, description='Content to exclude', default=None, stage='generate', carries_meaning=True), 'output_format': Affordance(name='output_format', type=<class 'str'>, description='wav|opus|mp3', default='wav', stage='generate', carries_meaning=False), 'prompt': Affordance(name='prompt', type=<class 'str'>, description='Sound description', default=None, stage='generate', carries_meaning=False), 'prompt_influence': Affordance(name='prompt_influence', type=<class 'float'>, description="0..1 unified adherence; None => the backend's native default", default=None, stage='generate', carries_meaning=False), 'seed': Affordance(name='seed', type=<class 'int'>, description='Reproducibility (capture in provenance)', default=None, stage='generate', carries_meaning=True), 'steps': Affordance(name='steps', type=<class 'int'>, description='Diffusion/flow steps', default=None, stage='generate', carries_meaning=False)}*

Unified generation-stage parameters (generate backends map onto these).

### *class* foley.base.IntendedUse(commercial=True, publish=True, redistribute_standalone=False, will_train=False, can_attribute=True, revenue_usd=0, allow_voice_or_trademark=False)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

What the caller intends to do with a sound; consumed by `keep()`.

### *class* foley.base.Layer(\*values)

Bases: [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Enum`](https://docs.python.org/3/library/enum.html#enum.Enum)

Mix layer (shared by `SoundEvent` now and `TimelineItem` later).

### *class* foley.base.LicenseRecord(source, source_id=None, source_url=None, acquisition_method=AcquisitionMethod.user, retrieved_at=None, adapter_version=None, content_sha256=None, license_id='unknown', license_name=None, license_version=None, license_url=None, rights_holder=None, creator_name=None, creator_url=None, commercial_ok=False, embed_in_derivative_ok=True, redistribute_standalone_ok=False, cache_bytes_ok=False, modification_ok=False, ai_training_ok=False, ai_training_scope=None, gen_ai_preference=None, revenue_cap_usd=None, requires_attribution=False, attribution_text=None, notice_text_required=None, transformations=<factory>, is_ai_generated=False, generator_model=None, generator_version=None, generation_prompt=None, generation_seed=None, generation_params=<factory>, watermark=None, c2pa_manifest_ref=None, contains_recognizable_voice=False, potential_trademark=False, disclosure_recommended=False, rights_verified=False, verified_at=None, schema_version=1)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

Per-sound rights + provenance. SSOT for BOTH `keep()` and storage mode.

The derived permission flags default fail-closed here (the bare-record
baseline) — with one deliberate exception: `embed_in_derivative_ok`
defaults `True` (the normal case for a licensed sound). That is not a live
bypass: `keep()` checks `rights_verified` first, so an unverified record
is rejected regardless. Populate the flags from the `license_id` via
`foley.licensing.apply_license_flags` (source overrides win). Never
hand-set the derived flags — always route through the policy layer.

### foley.base.MASTER_PROFILES *: [dict](https://docs.python.org/3/builtins/stdtypes.html#dict)[[str](https://docs.python.org/3/builtins/stdtypes.html#str), [MasterProfile](_autosummary/foley.base.html.md#foley.base.MasterProfile)]* *= {'broadcast_atsc': MasterProfile(target_lufs=-24.0, true_peak_db=-2.0, lra=7.0), 'broadcast_ebu': MasterProfile(target_lufs=-23.0, true_peak_db=-1.0, lra=7.0), 'podcast': MasterProfile(target_lufs=-16.0, true_peak_db=-1.0, lra=11.0), 'streaming': MasterProfile(target_lufs=-14.0, true_peak_db=-1.0, lra=11.0)}*

The named delivery targets (report 06 §5.2). [`resolve_master()`](_autosummary/foley.base.html.md#foley.base.resolve_master) maps a
profile name to one of these; these values are the SSOT so no LUFS literal
hides in code.

### *class* foley.base.MasterProfile(target_lufs=-16.0, true_peak_db=-1.0, lra=11.0)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

Loudness master target — the delivery spec as data, not code (report 06 §5.2).

### *class* foley.base.Placement(anchor=Anchor.absolute, ref=None, onset=0.0, pre_roll=0.0, duration=None, loop=False)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

WHERE/WHEN a clip sits — a symbolic anchor plus its resolved time (report 06 §6.3).

Filled by WEAVE’s aligner+anchor pass. `onset` is the resolved start in
seconds (distinct from the sparse `TimelineItem.onset` symbolic string);
`pre_roll` shifts the clip earlier so its salient transient — not its file
start — lands on the anchor (report 06 §2.4).

### *class* foley.base.Processing(gain_db=0.0, pan=0.0, distance=0.0, reverb_send=0.0, fade_in=0.008, fade_out=0.012, duck_bed=False)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

HOW a clip sounds — all optional with identity defaults (report 06 §3, §6.3).

Every field is a no-op at its default, so a sparse item (no `processing`)
renders untouched; the mixer departs from dry/centered/full-level only when a
field is set.

### foley.base.QUERY_AFFORDANCES *: [dict](https://docs.python.org/3/builtins/stdtypes.html#dict)[[str](https://docs.python.org/3/builtins/stdtypes.html#str), [Affordance](_autosummary/foley.base.html.md#foley.base.Affordance)]* *= {'audioset_label': Affordance(name='audioset_label', type=<class 'str'>, description='AudioSet ontology facet (rolls up children)', default=None, stage='query', carries_meaning=False), 'commercial_ok': Affordance(name='commercial_ok', type=<class 'bool'>, description='License filter shorthand', default=None, stage='query', carries_meaning=False), 'duration_range': Affordance(name='duration_range', type=<class 'tuple'>, description='(min_s, max_s)', default=None, stage='query', carries_meaning=False), 'filters': Affordance(name='filters', type=<class 'dict'>, description='Metadata predicates (SQL-style)', default=None, stage='query', carries_meaning=False), 'k': Affordance(name='k', type=<class 'int'>, description='Number of results', default=10, stage='query', carries_meaning=False), 'license': Affordance(name='license', type=<class 'str'>, description='Explicit license id constraint', default=None, stage='query', carries_meaning=False), 'min_snr': Affordance(name='min_snr', type=<class 'float'>, description='QC filter: min SNR dB', default=None, stage='query', carries_meaning=False), 'rerank': Affordance(name='rerank', type=<class 'bool'>, description='Apply second-stage rerank', default=False, stage='query', carries_meaning=False), 'semantic_text': Affordance(name='semantic_text', type=<class 'str'>, description='Query for CLAP semantic space', default=None, stage='query', carries_meaning=False), 'sort': Affordance(name='sort', type=<class 'str'>, description='score|duration|created|downloads', default='score', stage='query', carries_meaning=False), 'text': Affordance(name='text', type=<class 'str'>, description='Natural-language query', default=None, stage='query', carries_meaning=False), 'ucs_category': Affordance(name='ucs_category', type=<class 'str'>, description='UCS CatID facet', default=None, stage='query', carries_meaning=False)}*

Unified query-stage parameters (search / find / filter surface).

### *class* foley.base.Salience(\*values)

Bases: [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Enum`](https://docs.python.org/3/library/enum.html#enum.Enum)

How prominent a sound event is within a passage.

### *class* foley.base.SerializableMixin

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Adds `to_dict`/`to_json`/`from_dict`/`from_json` to a dataclass.

Stdlib-only, DRY, no-magic (de)serialization. Every SSOT dataclass below
inherits this instead of hand-rolling per-class encoders/decoders.

#### *classmethod* from_dict(d)

Reconstruct an instance from a plain dict.

Enum fields and nested dataclasses are coerced via `_decode()`.
Unknown keys are ignored (forward-compatible); missing keys fall back
to field defaults.

* **Parameters:**
  **d** ([`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)) – A plain dict (typically from `to_dict()` or `json.loads`).
* **Return type:**
  [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

#### *classmethod* from_json(s)

Reconstruct an instance from a JSON string.

* **Parameters:**
  **s** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – A JSON string (typically from `to_json()`).
* **Return type:**
  [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

#### to_dict()

Return the recursive plain-dict form.

Enum members are preserved (and remain JSON-safe because every enum
subclasses `str`); nested dataclasses are recursed via
`dataclasses.asdict`.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

#### to_json(, indent=None)

Return a JSON string (str-enums serialize to their `.value`).

* **Parameters:**
  **indent** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`int`](https://docs.python.org/3/builtins/functions.html#int)]) – Optional pretty-print indent passed to `json.dumps`.
* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### *class* foley.base.SoundDesignTimeline(items=<factory>, run_manifest_ref=None, transcript_ref=None, schema_version=1, narration_ref=None, word_timeline=<factory>, master=<factory>)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

The editable, re-renderable sound-design plan — the SELECT→WEAVE bridge and render SSOT.

SELECT (#7) emits the SPARSE form (just `items` + the run/transcript joins) via
`foley.agent.plan()`. WEAVE (#8) grows it additively: `narration_ref` binds the
voice audio, `word_timeline` caches the forced alignment (the reproducible seed),
`master` carries the loudness target, and each item gains its resolved
`Placement`/`Processing`. `render(timeline, library)` is then a PURE function of
this data + the library, so editing any field and re-rendering reproduces exactly that
change. `run_manifest_ref` == `foley.obs.RunManifest.run_id` (the reserved #8
`plan_ref` join).

### *class* foley.base.SoundEvent(query, layer=Layer.sfx_fg, diegetic=True, salience=Salience.medium, onset=None, loop=False, ucs_catid=None, audioset=<factory>, era_place=None)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

One salient, physically-audible event decomposed from a passage.

### *class* foley.base.SoundRecord(id, content_sha256=None, hash_algo='sha256', uri=None, storage_mode=StorageMode.by_reference, archive_format=None, source_sample_rate=None, source_bit_depth=None, license=<factory>, caption=None, tags=<factory>, ucs_category=None, ucs_subcategory=None, audioset_labels=<factory>, duration_s=None, sample_rate=None, channels=None, loudness_lufs=None, format=None, qc=None, embedding_model=None, embedding_dim=None, embedding_ref=None, named_cue=None, schema_version=1)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

Canonical SSOT per sound.

Audio bytes + CLAP vector live in SEPARATE stores keyed by the same id; this
record holds a content-hash `uri`, never raw bytes.

### *class* foley.base.StorageMode(\*values)

Bases: [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Enum`](https://docs.python.org/3/library/enum.html#enum.Enum)

How a sound’s bytes are held (DERIVED from `license.cache_bytes_ok`).

### *class* foley.base.TimelineItem(clip_ref, onset=None, gain=0.0, layer=Layer.sfx_fg, loop=False, id=None, placement=None, processing=None, event=None, enabled=True)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

One placed sound on the sound-design timeline — sparse seed + resolved render fields.

SELECT (#7) sets only the sparse flat fields (`clip_ref·onset·gain·layer·loop`);
WEAVE (#8) additively fills `id` / `placement` / `processing` (and may carry
the originating `event` for provenance). `enabled` is a non-destructive mute.
The sparse flat fields are never removed — they stay SELECT’s SSOT input; the render
reads the resolved `placement`/`processing` (falling back to the flat fields when
those are absent).

### *class* foley.base.Verdict(match, confidence, reason='', level=VerifyLevel.clap)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

The result of one verification rung for a candidate.

### *class* foley.base.VerifyLevel(\*values)

Bases: [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Enum`](https://docs.python.org/3/library/enum.html#enum.Enum)

Which rung of the verification ladder produced a `Verdict`.

### foley.base.resolve_master(master)

Resolve a master spec (profile name, explicit profile, or `None`) to a `MasterProfile`.

* **Parameters:**
  **master** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`MasterProfile`](_autosummary/foley.base.html.md#foley.base.MasterProfile), [`None`](https://docs.python.org/3/builtins/constants.html#None)]) – A [`MASTER_PROFILES`](_autosummary/foley.base.html.md#foley.base.MASTER_PROFILES) key (e.g. `'podcast'`), an explicit
  [`MasterProfile`](_autosummary/foley.base.html.md#foley.base.MasterProfile), or `None` (-> the podcast default).
* **Return type:**
  [`MasterProfile`](_autosummary/foley.base.html.md#foley.base.MasterProfile)
* **Returns:**
  The resolved [`MasterProfile`](_autosummary/foley.base.html.md#foley.base.MasterProfile).
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If `master` is an unknown profile name.


# _autosummary/foley.cli.html.md

# foley.cli

The `foley` command-line interface (stdlib `argparse`, zero new deps).

Subcommands:

```default
foley demo                       # offline ingest->search over the bundled fixture
foley bootstrap [--rings 0,1] [--corpora fsd50k,foleyset] [--data-dir DIR]
                [--accept-ai-restricted] [--no-commercial-filter]
foley ingest PATH [--license ID] [--min-status warn|pass|fail] [--no-qc]
foley search QUERY [-k N] [--commercial-ok]
```

Wired as the `foley` console entry point (`[project.scripts]`). Every command
is a thin call into the library facade ([`foley.bootstrap()`](_autosummary/foley.html.md#foley.bootstrap), [`foley.demo()`](_autosummary/foley.html.md#foley.demo),
[`foley.ingest()`](_autosummary/foley.html.md#foley.ingest), [`foley.search()`](_autosummary/foley.html.md#foley.search)); the CLI only parses args and prints.

### Functions

| [`build_parser`](_autosummary/foley.cli.html.md#foley.cli.build_parser)()   | Build the `foley` argument parser.          |
|-------------------------------------------------------------------|---------------------------------------------|
| [`main`](_autosummary/foley.cli.html.md#foley.cli.main)([argv])     | Entry point for the `foley` console script. |

### foley.cli.build_parser()

Build the `foley` argument parser.

* **Return type:**
  [`ArgumentParser`](https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser)

### foley.cli.main(argv=None)

Entry point for the `foley` console script.

* **Return type:**
  [`int`](https://docs.python.org/3/builtins/functions.html#int)


# _autosummary/foley.cost.html.md

# foley.cost

Cost: what a call will cost before it is made, and a cumulative cap on a run (#57).

Three pieces, each small:

* **Pricing as data.** Each source declares `SOURCE_CONFIG['pricing']` — the falaw
  convention (`unit`, `amount_usd`, plus the `source` and `seen` date the price
  was read from). [`estimate_call()`](_autosummary/foley.cost.html.md#foley.cost.estimate_call) turns it into dollars for one call, and returns
  `None` — never `0.0` — when the cost cannot be known (no pricing, or a
  per-second price with no duration and no maximum).
* **A run-scoped budget.** [`spend_scope()`](_autosummary/foley.cost.html.md#foley.cost.spend_scope) makes a
  [`Budget`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.Budget) the active one for a `find` / `score` /
  > `generate` run; [`authorize()`](_autosummary/foley.cost.html.md#foley.cost.authorize) is called before every paid call and refuses the
  > first one that would take the run past `max_usd` (default [`default_max_usd()`](_autosummary/foley.cost.html.md#foley.cost.default_max_usd):

  [`DEFAULT_MAX_USD`](_autosummary/foley.cost.html.md#foley.cost.DEFAULT_MAX_USD), $1 — maintainer decision on #57 — or, when the caller chose
  : a paid LLM for the SELECT rungs, [`DEFAULT_MAX_USD_BY_LLM`](_autosummary/foley.cost.html.md#foley.cost.DEFAULT_MAX_USD_BY_LLM), #88), or whose cost
    is unknown unless the budget approves unknown costs.
* **Two errors.** [`BudgetExceeded`](_autosummary/foley.cost.html.md#foley.cost.BudgetExceeded) and its subclass [`CostApprovalRequired`](_autosummary/foley.cost.html.md#foley.cost.CostApprovalRequired).

Stdlib-only.

### Module Attributes

| [`DEFAULT_MAX_USD`](_autosummary/foley.cost.html.md#foley.cost.DEFAULT_MAX_USD)          | The default cumulative cap for one run (a `find` / `score` / `generate` call).                                                                                                             |
|---------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`DEFAULT_MAX_USD_BY_LLM`](_autosummary/foley.cost.html.md#foley.cost.DEFAULT_MAX_USD_BY_LLM)   | The default cap of a [`foley.find()`](_autosummary/foley.html.md#foley.find) run whose LLM rungs the caller chose to pay for (`llm=` or `$FOLEY_LLM`), keyed by provider. |
| [`APPROVE_UNKNOWN_COST_ENV`](_autosummary/foley.cost.html.md#foley.cost.APPROVE_UNKNOWN_COST_ENV) | Set to 1 to approve calls whose cost is unknown, for every run that does not say.                                                                                                          |

### Functions

| [`estimate_call`](_autosummary/foley.cost.html.md#foley.cost.estimate_call)(config, \*\*affordances)   | The USD cost of one call to the source `config` declares, or `None` if unknown.                                                |
|-------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------|
| [`spend_scope`](_autosummary/foley.cost.html.md#foley.cost.spend_scope)([budget])                    | Put `budget` in force for the block; every enclosing budget stays in force too.                                                |
| [`scoped_iter`](_autosummary/foley.cost.html.md#foley.cost.scoped_iter)(iterator_factory[, budget])  | Iterate `iterator_factory()` with `budget` in force only while it runs.                                                        |
| [`active_budget`](_autosummary/foley.cost.html.md#foley.cost.active_budget)()                          | The innermost [`Budget`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.Budget) of the current run, or `None`. |
| [`check`](_autosummary/foley.cost.html.md#foley.cost.check)(estimate_usd, \*, what)            | Raise unless every budget in force can afford `estimate_usd` (reserves nothing).                                               |
| [`authorize`](_autosummary/foley.cost.html.md#foley.cost.authorize)(estimate_usd, \*, what)        | Admit a paid call: every budget in force must afford it, and each reserves it.                                                 |
| [`settle`](_autosummary/foley.cost.html.md#foley.cost.settle)(reserved_usd, actual_usd)         | Replace each budget's reservation with the actual cost (`None`: keep it).                                                      |
| [`release`](_autosummary/foley.cost.html.md#foley.cost.release)(reserved_usd)                    | Undo a reservation for a call that was never sent.                                                                             |
| [`default_max_usd`](_autosummary/foley.cost.html.md#foley.cost.default_max_usd)([llm])                   | The cap of a run given no budget: higher when the caller chose a paid LLM.                                                     |
| [`charge`](_autosummary/foley.cost.html.md#foley.cost.charge)(usd)                              | Record `usd` against every budget in force (a no-op outside any run).                                                          |

### Exceptions

| [`BudgetExceeded`](_autosummary/foley.cost.html.md#foley.cost.BudgetExceeded)       | Raised before a paid call that would take the run past its `max_usd` cap.          |
|-----------------------------------------------------------------------|------------------------------------------------------------------------------------|
| [`CostApprovalRequired`](_autosummary/foley.cost.html.md#foley.cost.CostApprovalRequired) | Raised before a call whose cost is unknown, when the budget has not approved that. |

### foley.cost.APPROVE_UNKNOWN_COST_ENV *= 'FOLEY_APPROVE_UNKNOWN_COST'*

Set to 1 to approve calls whose cost is unknown, for every run that does not say.

### *exception* foley.cost.BudgetExceeded

Bases: [`RuntimeError`](https://docs.python.org/3/builtins/exceptions.html#RuntimeError)

Raised before a paid call that would take the run past its `max_usd` cap.

### *exception* foley.cost.CostApprovalRequired

Bases: [`BudgetExceeded`](_autosummary/foley.cost.html.md#foley.cost.BudgetExceeded)

Raised before a call whose cost is unknown, when the budget has not approved that.

### foley.cost.DEFAULT_MAX_USD *: [float](https://docs.python.org/3/builtins/functions.html#float)* *= 1.0*

The default cumulative cap for one run (a `find` / `score` / `generate` call).

### foley.cost.DEFAULT_MAX_USD_BY_LLM *: [dict](https://docs.python.org/3/builtins/stdtypes.html#dict)[[str](https://docs.python.org/3/builtins/stdtypes.html#str), [float](https://docs.python.org/3/builtins/functions.html#float)]* *= {'anthropic': 3.0}*

The default cap of a [`foley.find()`](_autosummary/foley.html.md#foley.find) run whose LLM rungs the caller chose to pay
for (`llm=` or `$FOLEY_LLM`), keyed by provider. With the defaults (6 events,
`k=10`, `verify='listen'`) Claude’s upper bound is about $2.52, so the $1 cap
would refuse every such run before its first call; $3 lets it run and still caps
it. A run usually spends far less than its bound (#88). `Budget(max_usd=...)`
overrides it.

### foley.cost.active_budget()

The innermost [`Budget`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.Budget) of the current run, or `None`.

### foley.cost.authorize(estimate_usd, , what)

Admit a paid call: every budget in force must afford it, and each reserves it.

The reservation is atomic per budget, so concurrent calls cannot all pass before
any is counted. Call [`settle()`](_autosummary/foley.cost.html.md#foley.cost.settle) afterwards with the actual cost when known; a
call that fails after being sent keeps its reservation (it may have been billed);
one refused before sending is released with [`release()`](_autosummary/foley.cost.html.md#foley.cost.release).

* **Raises:**
  * [**CostApprovalRequired**](_autosummary/foley.cost.html.md#foley.cost.CostApprovalRequired) – If `estimate_usd` is `None` and a budget in force has
        not approved unknown costs (`Budget.approve_unknown_cost` /
        `$FOLEY_APPROVE_UNKNOWN_COST`).
  * [**BudgetExceeded**](_autosummary/foley.cost.html.md#foley.cost.BudgetExceeded) – If a budget’s spend plus `estimate_usd` exceeds its `max_usd`.
* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### foley.cost.charge(usd)

Record `usd` against every budget in force (a no-op outside any run).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### foley.cost.check(estimate_usd, , what)

Raise unless every budget in force can afford `estimate_usd` (reserves nothing).

For a whole-run upper bound (`find`’s pre-flight).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### foley.cost.default_max_usd(llm=None)

The cap of a run given no budget: higher when the caller chose a paid LLM.

The provider is the one named by `llm` or, failing that, `$FOLEY_LLM` (the
free default never resolves to a paid one); see [`DEFAULT_MAX_USD_BY_LLM`](_autosummary/foley.cost.html.md#foley.cost.DEFAULT_MAX_USD_BY_LLM).

* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

```pycon
>>> default_max_usd("fake")
1.0
>>> default_max_usd("anthropic")
3.0
```

### foley.cost.estimate_call(config, \*\*affordances)

The USD cost of one call to the source `config` declares, or `None` if unknown.

`config['pricing']` units:

* `'free'` (local inference, a free API tier) → `0.0`;
* `'per_call'` → `amount_usd`;
* `'per_second'` → `amount_usd` × the requested `duration`, or × the backend’s
  `native_defaults['duration_max_s']` when the duration is left to the backend
  (an upper bound), else `None`.

* **Parameters:**
  * **config** ([`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)) – A `SOURCE_CONFIG`.
  * **\*\*affordances** – The canonical affordances of the call (`duration`, …).
* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`float`](https://docs.python.org/3/builtins/functions.html#float)]

### foley.cost.release(reserved_usd)

Undo a reservation for a call that was never sent.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### foley.cost.scoped_iter(iterator_factory, budget=None)

Iterate `iterator_factory()` with `budget` in force only while it runs.

A generator that holds a [`spend_scope()`](_autosummary/foley.cost.html.md#foley.cost.spend_scope) across `yield` would leave its
budget active in the caller’s context between items (so the caller’s own calls, or
another interleaved stream, would charge it). This re-enters the scope for each
step and leaves it before handing the item out; closing the stream closes the
inner iterator inside the scope too.

### foley.cost.settle(reserved_usd, actual_usd)

Replace each budget’s reservation with the actual cost (`None`: keep it).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### foley.cost.spend_scope(budget=None)

Put `budget` in force for the block; every enclosing budget stays in force too.

Budgets stack: a paid call must fit **every** budget in force (so an explicit,
stricter `budget` inside a `find` / `score` run still applies) and is charged
to each of them once. With no `budget`: inside a run, nothing changes; outside
one, a fresh default [`Budget`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.Budget) is used ($1).

* **Yields:**
  The innermost budget in force.


# _autosummary/foley.eval.baseline.html.md

# foley.eval.baseline

The committed nDCG baseline — the PR gate’s SSOT + staleness stamps.

The gate blocks a PR whose `ndcg@10` falls below `value - tolerance` (report
08 §5). The baseline is a committed **number** (not a frozen per-clip run, which
would be BLAS-drift-fragile across the CI matrix), stamped with the sha256 of the
golden seed + the Ring-0 manifest: if either fixture is edited, the stamp
mismatches and the harness warns “baseline stale — run `foley eval
--update-baseline`”, so a metric shift is attributable to the *system*, not a
silently-shifted corpus. Re-baselining is a deliberate, reviewable one-line diff.

### Module Attributes

| [`DEFAULT_BASELINE_PATH`](_autosummary/foley.eval.baseline.html.md#foley.eval.baseline.DEFAULT_BASELINE_PATH)   | Package data dir (ships in the wheel); the baseline lives beside the seed.        |
|--------------------------------------------------------------------------|-----------------------------------------------------------------------------------|
| [`DEFAULT_TOLERANCE`](_autosummary/foley.eval.baseline.html.md#foley.eval.baseline.DEFAULT_TOLERANCE)       | The regression tolerance from report 08 §5 (Δ [nDCG@10](mailto:nDCG@10) ≥ −0.02). |

### Functions

| [`is_stale`](_autosummary/foley.eval.baseline.html.md#foley.eval.baseline.is_stale)(baseline, \*, seed_path, manifest_path)   | True if the baseline's fixture stamps no longer match the fixtures on disk.   |
|-----------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------|
| [`load_baseline`](_autosummary/foley.eval.baseline.html.md#foley.eval.baseline.load_baseline)([path])                              | Load the committed baseline dict from `path`.                                 |
| [`write_baseline`](_autosummary/foley.eval.baseline.html.md#foley.eval.baseline.write_baseline)(report, \*[, path, metric, ...])    | Write a fresh baseline from `report` (the `--update-baseline` action).        |

### foley.eval.baseline.DEFAULT_BASELINE_PATH *= PosixPath('/home/runner/work/foley/foley/foley/data/golden/baseline.json')*

Package data dir (ships in the wheel); the baseline lives beside the seed.

### foley.eval.baseline.DEFAULT_TOLERANCE *= 0.02*

The regression tolerance from report 08 §5 (Δ [nDCG@10](mailto:nDCG@10) ≥ −0.02).

### foley.eval.baseline.is_stale(baseline, , seed_path, manifest_path)

True if the baseline’s fixture stamps no longer match the fixtures on disk.

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### foley.eval.baseline.load_baseline(path=PosixPath('/home/runner/work/foley/foley/foley/data/golden/baseline.json'))

Load the committed baseline dict from `path`.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### foley.eval.baseline.write_baseline(report, , path=PosixPath('/home/runner/work/foley/foley/foley/data/golden/baseline.json'), metric='ndcg@10', tolerance=0.02, seed_path, manifest_path, embedder_model_id='foley-eval/hashing-bow-v1', dim=64, rrf_k=60, updated_at, n_items, revision='gld-v1')

Write a fresh baseline from `report` (the `--update-baseline` action).

Records the mean metric value plus sha256 stamps of the seed + manifest so a
later fixture edit is detected. `updated_at` is passed in (not read from the
clock) so the caller controls reproducibility. `revision` labels the golden-set
generation (bumped when the frozen set is regrown).

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)
* **Returns:**
  The baseline dict that was written.


# _autosummary/foley.eval.embedder.html.md

# foley.eval.embedder

A deterministic, numpy-only text embedder for the retrieval eval gate.

[`HashingBowEmbedder`](_autosummary/foley.eval.embedder.html.md#foley.eval.embedder.HashingBowEmbedder) is a hashing bag-of-words text embedder — the
shippable sibling of the test suite’s `FakeEmbedder`. It exists so the nDCG PR
gate is **deterministic and CLAP-free**: unlike `FakeEmbedder`’s random
`embed_audio` (whose content-noise vectors make RRF rankings platform-dependent
— the flakiness documented in `tests/test_bootstrap.py`), the eval harness
*injects* `embed_text(caption + tags)` as each clip’s vector, so both search
legs (vector cosine and BM25) carry the same caption bag-of-words. Every golden
query then shares 2–3 tokens with its answer clip → integer rank-1 in both legs →
a bit-exact, cross-platform-stable nDCG.

It is a text-only embedder: `embed_audio` is intentionally unimplemented (the
gate never calls it — vectors are injected via
[`foley.index.library.SoundLibrary.add()`](_autosummary/foley.index.library.html.md#foley.index.library.SoundLibrary.add)).

### Module Attributes

| [`DEFAULT_DIM`](_autosummary/foley.eval.embedder.html.md#foley.eval.embedder.DEFAULT_DIM)   | Default embedding width (matches the test `FakeEmbedder` for parity).   |
|----------------------------------------------------------------|-------------------------------------------------------------------------|

### Classes

| [`HashingBowEmbedder`](_autosummary/foley.eval.embedder.html.md#foley.eval.embedder.HashingBowEmbedder)(\*[, dim])   | Deterministic hashing bag-of-words text embedder (L2-normalized).   |
|----------------------------------------------------------------------------------|---------------------------------------------------------------------|

### foley.eval.embedder.DEFAULT_DIM *= 64*

Default embedding width (matches the test `FakeEmbedder` for parity).

### *class* foley.eval.embedder.HashingBowEmbedder(, dim=64)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Deterministic hashing bag-of-words text embedder (L2-normalized).

Two texts sharing a token share a dimension, so cosine similarity tracks
lexical overlap — plausible *and* reproducible (a stable `hashlib` hash,
never the salted builtin `hash`). Suitable only for the eval gate /
offline retrieval regression; real retrieval uses CLAP.

#### embed_text(text)

Embed `text` (or a list of texts) -> a `(n, dim)` float32 array.


# _autosummary/foley.eval.fidelity.html.md

# foley.eval.fidelity

Set-level generation-fidelity — Fréchet Audio Distance (FAD) + Kernel Audio Distance (KAD).

The distribution-level question (report 08 §4.2): \*is generator A’s output distribution
closer to real SFX than generator B’s?\* — a **release-level backend comparison** over
hundreds of samples, never a per-clip gate. Both metrics take `(n, d)` \*\*embedding
arrays\*\*, so the hermetic-CI path and the real path differ only by which embedder
produced them (a fake in CI, PANNs Wavegram-Logmel / CLAP behind `foley[fit]` in prod —
“FAD-P” is just [`frechet_distance()`](_autosummary/foley.eval.fidelity.html.md#foley.eval.fidelity.frechet_distance) fed PANNs embeddings). Every score carries a
mandatory [`FidelityStamp`](_autosummary/foley.eval.fidelity.html.md#foley.eval.fidelity.FidelityStamp), because FAD/KAD numbers are \*\*not comparable across
embeddings, toolkits, or versions\*\*.

Pure numpy / stdlib — `numpy` is imported function-locally (`import foley` stays
dol-only), and the PSD matrix-square-root trace is computed via `numpy.linalg.eigvals`
(there is **no** `scipy.linalg.sqrtm` in the numpy-only CI env). Reference toolkits
(`fadtk` / `kadtk`) are optional externals for reproducing exact published numbers;
the functions here cover the math.

### Functions

| [`frechet_distance`](_autosummary/foley.eval.fidelity.html.md#foley.eval.fidelity.frechet_distance)(x_ref, x_gen)                   | Fréchet distance between two embedding sets (the FAD formula).            |
|---------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------|
| [`generation_fidelity`](_autosummary/foley.eval.fidelity.html.md#foley.eval.fidelity.generation_fidelity)(ref_wavs, gen_wavs, \*, ...) | Embed two wav sets through an injected embedder and compute FAD or KAD.   |
| [`kernel_audio_distance`](_autosummary/foley.eval.fidelity.html.md#foley.eval.fidelity.kernel_audio_distance)(x_ref, x_gen, \*[, ...])   | Kernel Audio Distance — the unbiased RBF-MMD² between two embedding sets. |

### Classes

| [`FidelityResult`](_autosummary/foley.eval.fidelity.html.md#foley.eval.fidelity.FidelityResult)(metric, value, stamp)            | A stamped set-level fidelity score (attached to `FitReport.fidelity`).   |
|--------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------|
| [`FidelityStamp`](_autosummary/foley.eval.fidelity.html.md#foley.eval.fidelity.FidelityStamp)(embedding, toolkit, version, ...) | Mandatory provenance for a FAD/KAD score — it is meaningless without it. |

### *class* foley.eval.fidelity.FidelityResult(metric, value, stamp)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

A stamped set-level fidelity score (attached to `FitReport.fidelity`).

### *class* foley.eval.fidelity.FidelityStamp(embedding, toolkit, version, n_ref, n_gen)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Mandatory provenance for a FAD/KAD score — it is meaningless without it.

FAD/KAD are not comparable across embeddings/toolkits/versions, so a bare number is
uninterpretable; this stamp makes the basis of comparison legible.

### foley.eval.fidelity.frechet_distance(x_ref, x_gen)

Fréchet distance between two embedding sets (the FAD formula).

`‖μ_r − μ_g‖² + Tr(Σ_r + Σ_g − 2(Σ_r Σ_g)^½)` — lower = the generated set’s
embedding distribution is closer to the reference. Feed PANNs Wavegram-Logmel
embeddings for “FAD-P”, or CLAP for a domain-matched FAD.

* **Parameters:**
  * **x_ref** – Reference embeddings `(n_ref, d)`.
  * **x_gen** – Generated embeddings `(n_gen, d)`.
* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)
* **Returns:**
  The FAD as a `float` (`≥ 0` up to numerical round-off; `~0` for identical
  distributions).

### foley.eval.fidelity.generation_fidelity(ref_wavs, gen_wavs, , embedder, sr=48000, metric='fad', toolkit='foley-numpy', version='1')

Embed two wav sets through an injected embedder and compute FAD or KAD.

The real fidelity path is a **zero-call-site-change embedder swap**: a fake /
hashing embedder in CI, PANNs / CLAP (`foley[fit]`) in prod. The embedder’s
`model_id` is stamped into the result.

* **Parameters:**
  * **ref_wavs** – An iterable of reference waveforms (1-D arrays).
  * **gen_wavs** – An iterable of generated waveforms (1-D arrays).
  * **embedder** – An object with `embed_audio(wav, sr) -> vector` and `model_id`.
  * **sr** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The sample rate passed to `embed_audio`.
  * **metric** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – `'fad'` (default) or `'kad'`.
  * **toolkit** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Provenance label for the stamp (default `'foley-numpy'`).
  * **version** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Provenance version for the stamp.
* **Return type:**
  [`FidelityResult`](_autosummary/foley.eval.fidelity.html.md#foley.eval.fidelity.FidelityResult)
* **Returns:**
  A [`FidelityResult`](_autosummary/foley.eval.fidelity.html.md#foley.eval.fidelity.FidelityResult) carrying the score and its [`FidelityStamp`](_autosummary/foley.eval.fidelity.html.md#foley.eval.fidelity.FidelityStamp).

### foley.eval.fidelity.kernel_audio_distance(x_ref, x_gen, , bandwidth=None)

Kernel Audio Distance — the unbiased RBF-MMD² between two embedding sets.

Distribution-free and small-sample-convergent; preferred over FAD for the small
reference/generated sets of early-stage eval. The diagonal (same-sample) kernel terms
are dropped (the unbiased estimator), so `KAD(X, X) ≈ 0` and can be slightly
negative.

* **Parameters:**
  * **x_ref** – Reference embeddings `(n_ref, d)`.
  * **x_gen** – Generated embeddings `(n_gen, d)`.
  * **bandwidth** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`float`](https://docs.python.org/3/builtins/functions.html#float)]) – RBF bandwidth σ (default: the median heuristic over the pooled set).
* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)
* **Returns:**
  The unbiased MMD² as a `float`.


# _autosummary/foley.eval.fit.html.md

# foley.eval.fit

Tier-2 fit evaluation — “does the accepted clip actually match the intent?” (report 08 §2/§5).

The judge-based sibling of [`foley.eval.retrieval`](_autosummary/foley.eval.retrieval.html.md#module-foley.eval.retrieval). Where Tier-1 asks \*did we rank the
right sound?\* (a per-PR nDCG gate), Tier-2 asks \*does the clip the pipeline accepted
genuinely depict the event?\* — audited over a **seeded stratified sample** of the golden
set by the authoritative fit-judge (report 08 §2). It is \*\*nightly / pre-release and
cost-gated, never a silent per-PR gate\*\*: [`FitReport.gate()`](_autosummary/foley.eval.fit.html.md#foley.eval.fit.FitReport.gate) is a no-op unless the
caller passes an explicit `min_fit_precision` floor, and `foley eval-fit` is
deliberately off the CI command path.

Discipline: it **reuses** the Tier-1 golden builders (`load_golden` /
`build_eval_library`) and the #7 SELECT tools (`search_sounds` / `gate_candidates`
/ `verify_match`), but it **never** imports `build_run` / `evaluate_run` /
`to_qrels` — the three symbols that construct or score the retrieval run — so it
structurally cannot compute or perturb the [nDCG@10](mailto:nDCG@10) gate. Metrics are pure numpy/stdlib;
the fit-judge is injected (a deterministic fake in CI, an audio-LM / LLM behind
`foley[fit]` / `foley[agent]` in prod). Fit judging runs strictly AFTER the
fail-closed license gate (`verify_match` asserts `candidate.license_ok` is `True`).

### Functions

| [`fit_f1`](_autosummary/foley.eval.fit.html.md#foley.eval.fit.fit_f1)(matches, relevants)                     | The harmonic mean of [`fit_precision()`](_autosummary/foley.eval.fit.html.md#foley.eval.fit.fit_precision) and [`fit_recall()`](_autosummary/foley.eval.fit.html.md#foley.eval.fit.fit_recall).   |
|-------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`fit_precision`](_autosummary/foley.eval.fit.html.md#foley.eval.fit.fit_precision)(matches, relevants)              | Fraction of judge-accepted candidates that are actually gold-relevant.                                                                                     |
| [`fit_recall`](_autosummary/foley.eval.fit.html.md#foley.eval.fit.fit_recall)(matches, relevants)                 | `TP / (TP + FN)` — of the gold-relevant candidates, how many the judge confirmed.                                                                          |
| [`run_fit_eval`](_autosummary/foley.eval.fit.html.md#foley.eval.fit.run_fit_eval)(\*[, golden, golden_path, ...])   | Run the Tier-2 fit eval over a stratified golden sample → a [`FitReport`](_autosummary/foley.eval.fit.html.md#foley.eval.fit.FitReport).                                    |
| [`stratified_sample`](_autosummary/foley.eval.fit.html.md#foley.eval.fit.stratified_sample)(units, \*, strata_keys, ...) | Deterministic seeded stratified draw over `units` — the cost gate.                                                                                         |

### Classes

| [`FitReport`](_autosummary/foley.eval.fit.html.md#foley.eval.fit.FitReport)([per_item, fit_precision, ...])   | Tier-2 fit metrics over a stratified golden sample (JSON-friendly, mirrors `RetrievalReport`).   |
|----------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------|

### *class* foley.eval.fit.FitReport(per_item=<factory>, fit_precision=0.0, fit_recall=0.0, fit_f1=0.0, fit_score=0.0, auto_accept_rate=0.0, n_accepted=0, n_confirmed=0, strata=<factory>, calibration=None, fidelity=<factory>, judge_model='', embedder_model_id='', seed=0, k=10, schema_version=1)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Tier-2 fit metrics over a stratified golden sample (JSON-friendly, mirrors `RetrievalReport`).

The fit numbers are co-emitted with the judge-vs-human `calibration` (an
[`AlphaResult`](_autosummary/foley.eval.reliability.html.md#foley.eval.reliability.AlphaResult) dict) so the §5 human-calibration
guardrail is legible: a `fit_precision` is only trusted where α certifies the judge.
`fidelity` holds stamped FAD/KAD results for release-level backend comparison. Both
default empty on the pure-fake path (no human labels, no generated-set wavs).

#### format_regression_diff(baseline)

A human diff of `fit_precision` vs a committed baseline (trend reporting).

The committed fit-baseline stamp is deferred until real judges produce stable
numbers; this ships the trend path only.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

#### gate(min_fit_precision)

The Tier-2 gate — a NO-OP returning `True` unless a floor is supplied.

Structural guarantee that Tier-2 is never a silent per-PR gate: CI passes no
floor, so this is always `True`; only the nightly/pre-release runner sets one.

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### foley.eval.fit.fit_f1(matches, relevants)

The harmonic mean of [`fit_precision()`](_autosummary/foley.eval.fit.html.md#foley.eval.fit.fit_precision) and [`fit_recall()`](_autosummary/foley.eval.fit.html.md#foley.eval.fit.fit_recall).

* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

### foley.eval.fit.fit_precision(matches, relevants)

Fraction of judge-accepted candidates that are actually gold-relevant.

`TP / (TP + FP)` where a candidate is a positive iff the fit-judge said `match`
and gold-relevant iff its golden grade ≥ 1. This measures the JUDGE’s auto-accept
*purity* (a low value ⇒ the judge waves through wrong clips) — NOT the ranker. Pure,
closed-form, and the decisive correctness oracle (the Ring-0 + fake-judge plumbing
makes the end-to-end value tautological, so the metric math is tested in isolation).

* **Parameters:**
  * **matches** ([`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)[[`bool`](https://docs.python.org/3/builtins/functions.html#bool)]) – Per-candidate fit-judge `match` booleans.
  * **relevants** ([`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)[[`bool`](https://docs.python.org/3/builtins/functions.html#bool)]) – Per-candidate gold-relevance booleans (grade ≥ 1).
* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)
* **Returns:**
  The fit precision in `[0, 1]` (`0.0` when nothing was accepted).

### foley.eval.fit.fit_recall(matches, relevants)

`TP / (TP + FN)` — of the gold-relevant candidates, how many the judge confirmed.

* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

### foley.eval.fit.run_fit_eval(, golden=None, golden_path=PosixPath('/home/runner/work/foley/foley/foley/data/golden/seed.json'), sample=None, strata_keys=('family', 'diegetic'), fit_judge=None, embedder=None, level=VerifyLevel.judge, seed=0, k=10, llm=None)

Run the Tier-2 fit eval over a stratified golden sample → a [`FitReport`](_autosummary/foley.eval.fit.html.md#foley.eval.fit.FitReport).

Composes the Tier-1 golden builders + the #7 SELECT loop, applies the authoritative
fit-judge as an independent audit, and aggregates fit-precision/recall/F1 + fit-score

+ auto-accept-rate, with a per-stratum breakdown. Report-only unless
  `min_fit_precision` is supplied.

* **Parameters:**
  * **golden** – A pre-loaded golden list (default: load `golden_path`).
  * **golden_path** – The golden JSON (default: the bundled Ring-0 seed set).
  * **sample** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`int`](https://docs.python.org/3/builtins/functions.html#int)]) – The stratified sample cap (default: the whole set — the cost gate).
  * **strata_keys** – The stratification axes (default `('family', 'diegetic')`).
  * **fit_judge** – The injected authoritative [`Judge`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Judge)
    (default: `foley.agent.verify._default_fit_judge()` for `llm` — the
    hermetic fake unless `llm` / `$FOLEY_LLM` opts in).
  * **embedder** – The Ring-0 text/​audio embedder (default: `HashingBowEmbedder`).
  * **level** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`VerifyLevel`](_autosummary/foley.base.html.md#foley.base.VerifyLevel)) – The verify rung the fit-judge audits at — `'listen'` or `'judge'`
    (default `VerifyLevel.judge`). `'clap'` is rejected: it never invokes the
    fit-judge (it only re-runs the retrieval clap gate), so it cannot measure fit.
  * **seed** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The sampling RNG seed.
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The retrieval shortlist depth per event.
* **Return type:**
  [`FitReport`](_autosummary/foley.eval.fit.html.md#foley.eval.fit.FitReport)
* **Returns:**
  A [`FitReport`](_autosummary/foley.eval.fit.html.md#foley.eval.fit.FitReport). Gating is the caller’s job via [`FitReport.gate()`](_autosummary/foley.eval.fit.html.md#foley.eval.fit.FitReport.gate).
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If `level` is `'clap'` (fit judging requires a listen/judge rung).

### foley.eval.fit.stratified_sample(units, , strata_keys, sample, seed)

Deterministic seeded stratified draw over `units` — the cost gate.

Round-robins across strata (so an easy family cannot dominate a capped sample);
`sample=None` keeps the full set. Each unit is `(GoldenItem, event_index,
event_dict)`.

* **Parameters:**
  * **units** ([`list`](https://docs.python.org/3/builtins/stdtypes.html#list)) – The full `(item, ev_idx, ev_dict)` unit list.
  * **strata_keys** – The stratification axes (e.g. `('family', 'diegetic')`).
  * **sample** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`int`](https://docs.python.org/3/builtins/functions.html#int)]) – The total draw size, or `None` for all.
  * **seed** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The RNG seed (reproducibility).
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)


# _autosummary/foley.eval.golden.html.md

# foley.eval.golden

The frozen golden set + the deterministic Ring-0 retrieval harness.

A [`GoldenItem`](_autosummary/foley.eval.golden.html.md#foley.eval.golden.GoldenItem) is one `(narrative context → expected sounds)` judgment;
its `answer_clip_ids` + `grade` become the TREC `qrels` the metrics score
against. [`build_eval_library()`](_autosummary/foley.eval.golden.html.md#foley.eval.golden.build_eval_library) stands up an ephemeral, in-memory library
over the bundled Ring-0 synthetic fixture with **injected caption vectors** (via
[`HashingBowEmbedder`](_autosummary/foley.eval.embedder.html.md#foley.eval.embedder.HashingBowEmbedder)), so retrieval is deterministic
and CLAP-free; [`run_ring0_retrieval_eval()`](_autosummary/foley.eval.golden.html.md#foley.eval.golden.run_ring0_retrieval_eval) runs every golden query through
the real `SoundLibrary.search` path and scores it.

The eval fixtures (`seed.json`, `baseline.json`) are **package data** under
`foley/data/golden/` — so they ship in the wheel and `foley.evaluate()` /
`foley eval` work on a bare `pip install` (not only from a source checkout).
`build_eval_library` builds records **directly** with stable
`ring0:<stem>` ids (not via `ingest_one`’s content-hash ids), so the run doc
ids join 1:1 with the qrels — no alias map, no content-hash keyspace trap. The
gate therefore does NOT exercise `ingest_one` (that path is covered by
`test_ingest`/`test_bootstrap`).

### Module Attributes

| [`RING0_MANIFEST_PATH`](_autosummary/foley.eval.golden.html.md#foley.eval.golden.RING0_MANIFEST_PATH)   | The tiny wav-backed demo fixture (6 clips) — decoded by `foley.bootstrap` / `demo`.   |
|------------------------------------------------------------------------|---------------------------------------------------------------------------------------|
| [`RING0_CORPUS_PATH`](_autosummary/foley.eval.golden.html.md#foley.eval.golden.RING0_CORPUS_PATH)     | The larger caption-only **eval** corpus (the 6 demo clips + ~150 synthetic clips).    |

### Functions

| [`build_eval_library`](_autosummary/foley.eval.golden.html.md#foley.eval.golden.build_eval_library)(\*[, embedder, manifest_path])   | Build the ephemeral Ring-0 eval library (stem ids + injected caption vectors).   |
|------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------|
| [`load_golden`](_autosummary/foley.eval.golden.html.md#foley.eval.golden.load_golden)([path])                                 | Load and validate the frozen golden set from `path` (JSON list).                 |
| [`run_ring0_retrieval_eval`](_autosummary/foley.eval.golden.html.md#foley.eval.golden.run_ring0_retrieval_eval)(\*[, k, ...])              | Score the golden set's queries against the Ring-0 library — the gate input.      |
| [`to_qrels`](_autosummary/foley.eval.golden.html.md#foley.eval.golden.to_qrels)(golden)                                    | Flatten the golden set into TREC qrels: `{query_id: {clip_id: grade}}`.          |

### Classes

| [`GoldenItem`](_autosummary/foley.eval.golden.html.md#foley.eval.golden.GoldenItem)(id, context, expected_events, ...)   | One frozen `(context → expected sounds)` judgment (report 08 §1.3).   |
|--------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------|

### *class* foley.eval.golden.GoldenItem(id, context, expected_events, answer_clip_ids, grade, negatives=<factory>, labeler='llm+human', schema_version=1)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

One frozen `(context → expected sounds)` judgment (report 08 §1.3).

#### id

Unique item id (e.g. `gld_0001`).

#### context

The narrative paragraph (the future SELECT input).

#### expected_events

One or more `{query, ucs_catid, layer, diegetic,
salience, ...}` dicts; `query` is the string fed to `search`.

#### answer_clip_ids

`{ucs_catid: [clip_id, ...]}` — the acceptable clips.

#### grade

`{clip_id: grade}` (2 ideal / 1 acceptable / 0 wrong).

#### negatives

Free-text distractors (unused by scoring; documentation).

#### labeler

Provenance of the labels (`llm+human` etc.).

#### schema_version

The GoldenItem schema version.

### foley.eval.golden.RING0_CORPUS_PATH *= PosixPath('/home/runner/work/foley/foley/foley/data/golden/corpus.json')*

The larger caption-only **eval** corpus (the 6 demo clips + ~150 synthetic clips). The
retrieval eval needs only captions/tags/vectors — never decoded PCM — so this corpus
ships without wav files, decoupling the nDCG gate’s scale from the demo fixture’s size.

### foley.eval.golden.RING0_MANIFEST_PATH *= PosixPath('/home/runner/work/foley/foley/foley/data/ring0/manifest.json')*

The tiny wav-backed demo fixture (6 clips) — decoded by `foley.bootstrap` / `demo`.

### foley.eval.golden.build_eval_library(, embedder=None, manifest_path=PosixPath('/home/runner/work/foley/foley/foley/data/golden/corpus.json'))

Build the ephemeral Ring-0 eval library (stem ids + injected caption vectors).

Each manifest clip becomes a [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord) with id
`ring0:<stem>` and a vector = `embedder.embed_text(caption + tags)` — so
both the vector and BM25 legs carry the caption bag-of-words and the answer
clip lands at integer rank 1 (deterministic, cross-platform).

* **Parameters:**
  * **embedder** – A text embedder (default: `HashingBowEmbedder`).
  * **manifest_path** – The Ring-0 `manifest.json` (default: the bundled fixture).
* **Returns:**
  A populated in-memory [`SoundLibrary`](_autosummary/foley.index.library.html.md#foley.index.library.SoundLibrary).

### foley.eval.golden.load_golden(path=PosixPath('/home/runner/work/foley/foley/foley/data/golden/seed.json'))

Load and validate the frozen golden set from `path` (JSON list).

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`GoldenItem`](_autosummary/foley.eval.golden.html.md#foley.eval.golden.GoldenItem)]

### foley.eval.golden.run_ring0_retrieval_eval(, k=10, golden_path=PosixPath('/home/runner/work/foley/foley/foley/data/golden/seed.json'), embedder=None)

Score the golden set’s queries against the Ring-0 library — the gate input.

Runs every golden `expected_events[].query` through the real
`SoundLibrary.search()` path (vector ⊕ BM25 ⊕ RRF) and evaluates the
resulting runs against the golden qrels.

* **Parameters:**
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Retrieval cutoff (and the metric `@k`).
  * **golden_path** – The golden set JSON.
  * **embedder** – The eval embedder (default: `HashingBowEmbedder`).
* **Return type:**
  [`RetrievalReport`](_autosummary/foley.eval.retrieval.html.md#foley.eval.retrieval.RetrievalReport)
* **Returns:**
  A `RetrievalReport` (per-query + mean [nDCG@10](mailto:nDCG@10) / recall / mAP / MRR).

### foley.eval.golden.to_qrels(golden)

Flatten the golden set into TREC qrels: `{query_id: {clip_id: grade}}`.

One qrels row per `(item, event)` — `query_id = f"{item.id}::{event_idx}"`
— future-proofing multi-event items (seed items are single-event today). Each
clip in `answer_clip_ids` carries its `grade`.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`int`](https://docs.python.org/3/builtins/functions.html#int)]]


# _autosummary/foley.eval.html.md

# foley.eval

Tier-1 retrieval evaluation — metrics, a frozen golden set, and the nDCG gate.

This subpackage answers “did we retrieve the right sound?” with TREC-style
ranking metrics (`nDCG@10` / `Recall@k` / `mAP@10` / `MRR@10`) over a
frozen golden set, and ships the **PR gate**: a pytest check that blocks a change
whose `nDCG@10` regresses by more than `0.02` on the Ring-0 golden set. Every
part is pure numpy/stdlib and deterministic — no `ranx`/`numba`, no CLAP, no
downloads — so the gate runs on every index/embedder/prompt PR in foley’s CI.

Tier-2 fit-judging (#10b) — “does this clip match the intent?” — lives alongside
in [`fit`](_autosummary/foley.eval.fit.html.md#module-foley.eval.fit) (the judge-based fit harness), [`reliability`](_autosummary/foley.eval.reliability.html.md#module-foley.eval.reliability)
(Krippendorff’s α + judge-vs-human calibration), and [`fidelity`](_autosummary/foley.eval.fidelity.html.md#module-foley.eval.fidelity)
(set-level FAD/KAD generation fidelity). Tier-2 is nightly/pre-release + cost-gated —
NOT a per-PR gate — and never touches the retrieval ranking. All numpy imports are
function-local so `import foley` stays dol-only.

### Functions

| [`ndcg_at_k`](_autosummary/foley.eval.html.md#foley.eval.ndcg_at_k)(qrels_q, run_q[, k])                    | Normalized DCG at `k` with linear gains (0.0 when no graded answer).                                                                                     |
|----------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`recall_at_k`](_autosummary/foley.eval.html.md#foley.eval.recall_at_k)(qrels_q, run_q[, k, rel_lvl])         | Fraction of ALL relevant docs (grade ≥ `rel_lvl`) retrieved in the top-`k`.                                                                              |
| [`precision_at_k`](_autosummary/foley.eval.html.md#foley.eval.precision_at_k)(qrels_q, run_q[, k, rel_lvl])      | Fraction of the top-`k` that is relevant (denominator is literal `k`).                                                                                   |
| [`average_precision_at_k`](_autosummary/foley.eval.html.md#foley.eval.average_precision_at_k)(qrels_q, run_q[, k, ...])  | Average precision (trec_eval `map`: divide by TOTAL relevant, not `min(R,k)`).                                                                           |
| [`mrr_at_k`](_autosummary/foley.eval.html.md#foley.eval.mrr_at_k)(qrels_q, run_q[, k, rel_lvl])            | Reciprocal rank of the first relevant doc in the top-`k` (0.0 if none).                                                                                  |
| [`mean_over_queries`](_autosummary/foley.eval.html.md#foley.eval.mean_over_queries)(values)                         | Macro-average of per-query metric values (0.0 for an empty list).                                                                                        |
| [`build_run`](_autosummary/foley.eval.html.md#foley.eval.build_run)(candidates)                             | Turn an ordered `search()` result into a run row with distinct scores.                                                                                   |
| [`evaluate_run`](_autosummary/foley.eval.html.md#foley.eval.evaluate_run)(run, qrels, \*[, k, metrics, ...])   | Score a `run` against `qrels`, returning a [`RetrievalReport`](_autosummary/foley.eval.html.md#foley.eval.RetrievalReport).                                             |
| [`load_golden`](_autosummary/foley.eval.html.md#foley.eval.load_golden)([path])                               | Load and validate the frozen golden set from `path` (JSON list).                                                                                         |
| [`to_qrels`](_autosummary/foley.eval.html.md#foley.eval.to_qrels)(golden)                                  | Flatten the golden set into TREC qrels: `{query_id: {clip_id: grade}}`.                                                                                  |
| [`build_eval_library`](_autosummary/foley.eval.html.md#foley.eval.build_eval_library)(\*[, embedder, manifest_path]) | Build the ephemeral Ring-0 eval library (stem ids + injected caption vectors).                                                                           |
| [`run_ring0_retrieval_eval`](_autosummary/foley.eval.html.md#foley.eval.run_ring0_retrieval_eval)(\*[, k, ...])            | Score the golden set's queries against the Ring-0 library — the gate input.                                                                              |
| [`load_baseline`](_autosummary/foley.eval.html.md#foley.eval.load_baseline)([path])                             | Load the committed baseline dict from `path`.                                                                                                            |
| [`write_baseline`](_autosummary/foley.eval.html.md#foley.eval.write_baseline)(report, \*[, path, metric, ...])   | Write a fresh baseline from `report` (the `--update-baseline` action).                                                                                   |
| [`is_stale`](_autosummary/foley.eval.html.md#foley.eval.is_stale)(baseline, \*, seed_path, manifest_path)  | True if the baseline's fixture stamps no longer match the fixtures on disk.                                                                              |
| [`run_fit_eval`](_autosummary/foley.eval.html.md#foley.eval.run_fit_eval)(\*[, golden, golden_path, ...])      | Run the Tier-2 fit eval over a stratified golden sample → a [`FitReport`](_autosummary/foley.eval.html.md#foley.eval.FitReport).                                  |
| [`fit_precision`](_autosummary/foley.eval.html.md#foley.eval.fit_precision)(matches, relevants)                 | Fraction of judge-accepted candidates that are actually gold-relevant.                                                                                   |
| [`fit_recall`](_autosummary/foley.eval.html.md#foley.eval.fit_recall)(matches, relevants)                    | `TP / (TP + FN)` — of the gold-relevant candidates, how many the judge confirmed.                                                                        |
| [`fit_f1`](_autosummary/foley.eval.html.md#foley.eval.fit_f1)(matches, relevants)                        | The harmonic mean of [`fit_precision()`](_autosummary/foley.eval.html.md#foley.eval.fit_precision) and [`fit_recall()`](_autosummary/foley.eval.html.md#foley.eval.fit_recall). |
| [`stratified_sample`](_autosummary/foley.eval.html.md#foley.eval.stratified_sample)(units, \*, strata_keys, ...)    | Deterministic seeded stratified draw over `units` — the cost gate.                                                                                       |
| [`krippendorff_alpha`](_autosummary/foley.eval.html.md#foley.eval.krippendorff_alpha)(reliability_data, \*[, ...])   | Krippendorff's α over `(raters × units)` ratings (`nan` = missing).                                                                                      |
| [`percent_agreement`](_autosummary/foley.eval.html.md#foley.eval.percent_agreement)(reliability_data)               | Raw pairwise percent agreement, reported ALONGSIDE α (report 08 §2.3).                                                                                   |
| [`reliability_band`](_autosummary/foley.eval.html.md#foley.eval.reliability_band)(alpha)                           | Map an α (or κ) to Krippendorff's benchmark band.                                                                                                        |
| [`calibrate_judge_vs_human`](_autosummary/foley.eval.html.md#foley.eval.calibrate_judge_vs_human)(human_grades, ...)       | Compute judge-vs-human agreement and the promotion verdict (report 08 §2.3).                                                                             |
| [`frechet_distance`](_autosummary/foley.eval.html.md#foley.eval.frechet_distance)(x_ref, x_gen)                    | Fréchet distance between two embedding sets (the FAD formula).                                                                                           |
| [`kernel_audio_distance`](_autosummary/foley.eval.html.md#foley.eval.kernel_audio_distance)(x_ref, x_gen, \*[, ...])    | Kernel Audio Distance — the unbiased RBF-MMD² between two embedding sets.                                                                                |
| [`generation_fidelity`](_autosummary/foley.eval.html.md#foley.eval.generation_fidelity)(ref_wavs, gen_wavs, \*, ...)  | Embed two wav sets through an injected embedder and compute FAD or KAD.                                                                                  |

### Classes

| [`RetrievalReport`](_autosummary/foley.eval.html.md#foley.eval.RetrievalReport)([per_query, mean, ranks, k])      | Per-query + mean Tier-1 metrics over a golden set (JSON-friendly).                             |
|----------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------|
| [`GoldenItem`](_autosummary/foley.eval.html.md#foley.eval.GoldenItem)(id, context, expected_events, ...)     | One frozen `(context → expected sounds)` judgment (report 08 §1.3).                            |
| [`HashingBowEmbedder`](_autosummary/foley.eval.html.md#foley.eval.HashingBowEmbedder)(\*[, dim])                     | Deterministic hashing bag-of-words text embedder (L2-normalized).                              |
| [`FitReport`](_autosummary/foley.eval.html.md#foley.eval.FitReport)([per_item, fit_precision, ...])         | Tier-2 fit metrics over a stratified golden sample (JSON-friendly, mirrors `RetrievalReport`). |
| [`AlphaResult`](_autosummary/foley.eval.html.md#foley.eval.AlphaResult)(alpha, level, n_units, n_raters, ...) | A judge-vs-human calibration record (surfaced as `FitReport.calibration`).                     |
| [`FidelityStamp`](_autosummary/foley.eval.html.md#foley.eval.FidelityStamp)(embedding, toolkit, version, ...)   | Mandatory provenance for a FAD/KAD score — it is meaningless without it.                       |
| [`FidelityResult`](_autosummary/foley.eval.html.md#foley.eval.FidelityResult)(metric, value, stamp)              | A stamped set-level fidelity score (attached to `FitReport.fidelity`).                         |

### *class* foley.eval.AlphaResult(alpha, level, n_units, n_raters, percent_agreement, band, promoted)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

A judge-vs-human calibration record (surfaced as `FitReport.calibration`).

Codifies the promotion rule in one legible object: a model judge is `promoted` to
unattended use only once its agreement with the human raters reaches the
`ALPHA_RELIABLE` band.

### *class* foley.eval.FidelityResult(metric, value, stamp)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

A stamped set-level fidelity score (attached to `FitReport.fidelity`).

### *class* foley.eval.FidelityStamp(embedding, toolkit, version, n_ref, n_gen)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Mandatory provenance for a FAD/KAD score — it is meaningless without it.

FAD/KAD are not comparable across embeddings/toolkits/versions, so a bare number is
uninterpretable; this stamp makes the basis of comparison legible.

### *class* foley.eval.FitReport(per_item=<factory>, fit_precision=0.0, fit_recall=0.0, fit_f1=0.0, fit_score=0.0, auto_accept_rate=0.0, n_accepted=0, n_confirmed=0, strata=<factory>, calibration=None, fidelity=<factory>, judge_model='', embedder_model_id='', seed=0, k=10, schema_version=1)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Tier-2 fit metrics over a stratified golden sample (JSON-friendly, mirrors `RetrievalReport`).

The fit numbers are co-emitted with the judge-vs-human `calibration` (an
[`AlphaResult`](_autosummary/foley.eval.reliability.html.md#foley.eval.reliability.AlphaResult) dict) so the §5 human-calibration
guardrail is legible: a `fit_precision` is only trusted where α certifies the judge.
`fidelity` holds stamped FAD/KAD results for release-level backend comparison. Both
default empty on the pure-fake path (no human labels, no generated-set wavs).

#### format_regression_diff(baseline)

A human diff of `fit_precision` vs a committed baseline (trend reporting).

The committed fit-baseline stamp is deferred until real judges produce stable
numbers; this ships the trend path only.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

#### gate(min_fit_precision)

The Tier-2 gate — a NO-OP returning `True` unless a floor is supplied.

Structural guarantee that Tier-2 is never a silent per-PR gate: CI passes no
floor, so this is always `True`; only the nightly/pre-release runner sets one.

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### *class* foley.eval.GoldenItem(id, context, expected_events, answer_clip_ids, grade, negatives=<factory>, labeler='llm+human', schema_version=1)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

One frozen `(context → expected sounds)` judgment (report 08 §1.3).

#### id

Unique item id (e.g. `gld_0001`).

#### context

The narrative paragraph (the future SELECT input).

#### expected_events

One or more `{query, ucs_catid, layer, diegetic,
salience, ...}` dicts; `query` is the string fed to `search`.

#### answer_clip_ids

`{ucs_catid: [clip_id, ...]}` — the acceptable clips.

#### grade

`{clip_id: grade}` (2 ideal / 1 acceptable / 0 wrong).

#### negatives

Free-text distractors (unused by scoring; documentation).

#### labeler

Provenance of the labels (`llm+human` etc.).

#### schema_version

The GoldenItem schema version.

### *class* foley.eval.HashingBowEmbedder(, dim=64)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Deterministic hashing bag-of-words text embedder (L2-normalized).

Two texts sharing a token share a dimension, so cosine similarity tracks
lexical overlap — plausible *and* reproducible (a stable `hashlib` hash,
never the salted builtin `hash`). Suitable only for the eval gate /
offline retrieval regression; real retrieval uses CLAP.

#### embed_text(text)

Embed `text` (or a list of texts) -> a `(n, dim)` float32 array.

### *class* foley.eval.RetrievalReport(per_query=<factory>, mean=<factory>, ranks=<factory>, k=10)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Per-query + mean Tier-1 metrics over a golden set (JSON-friendly).

#### format_regression_diff(baseline)

A human diff for a failing gate: mean vs baseline + the worst queries.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### foley.eval.average_precision_at_k(qrels_q, run_q, k=10, , rel_lvl=1)

Average precision (trec_eval `map`: divide by TOTAL relevant, not `min(R,k)`).

* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

### foley.eval.build_eval_library(, embedder=None, manifest_path=PosixPath('/home/runner/work/foley/foley/foley/data/golden/corpus.json'))

Build the ephemeral Ring-0 eval library (stem ids + injected caption vectors).

Each manifest clip becomes a [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord) with id
`ring0:<stem>` and a vector = `embedder.embed_text(caption + tags)` — so
both the vector and BM25 legs carry the caption bag-of-words and the answer
clip lands at integer rank 1 (deterministic, cross-platform).

* **Parameters:**
  * **embedder** – A text embedder (default: [`HashingBowEmbedder`](_autosummary/foley.eval.html.md#foley.eval.HashingBowEmbedder)).
  * **manifest_path** – The Ring-0 `manifest.json` (default: the bundled fixture).
* **Returns:**
  A populated in-memory [`SoundLibrary`](_autosummary/foley.index.library.html.md#foley.index.library.SoundLibrary).

### foley.eval.build_run(candidates)

Turn an ordered `search()` result into a run row with distinct scores.

Assigns each candidate a strictly-descending integer score
(`len - rank`) in the order returned, so the run carries \*\*no score
ties\*\* and `_ranked_ids()` reproduces the search ranking exactly — the
determinism the gate relies on. The doc id is `candidate.sound.id`.

* **Parameters:**
  **candidates** – An ordered `list[Candidate]` from
  [`foley.index.library.SoundLibrary.search()`](_autosummary/foley.index.library.html.md#foley.index.library.SoundLibrary.search) (best first).
* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]
* **Returns:**
  `{clip_id: score}` with distinct descending scores.

### foley.eval.calibrate_judge_vs_human(human_grades, judge_grades, , level='ordinal')

Compute judge-vs-human agreement and the promotion verdict (report 08 §2.3).

Stacks the human rating row(s) and the model-judge row into one reliability matrix
and returns an [`AlphaResult`](_autosummary/foley.eval.html.md#foley.eval.AlphaResult); `promoted` is `True` iff α reaches the
reliable band — the model judge may then be trusted unattended on that slice.

* **Parameters:**
  * **human_grades** – A 1-D per-unit human grade sequence, or a 2-D `(humans × units)`
    matrix (`nan` = missing).
  * **judge_grades** – The model judge’s 1-D per-unit grades (`nan` = missing).
  * **level** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The measurement level (default `'ordinal'`).
* **Return type:**
  [`AlphaResult`](_autosummary/foley.eval.reliability.html.md#foley.eval.reliability.AlphaResult)
* **Returns:**
  An [`AlphaResult`](_autosummary/foley.eval.html.md#foley.eval.AlphaResult).

### foley.eval.evaluate_run(run, qrels, , k=10, metrics=('ndcg@10', 'recall@10', 'map@10', 'mrr@10'), rel_lvl=1)

Score a `run` against `qrels`, returning a [`RetrievalReport`](_autosummary/foley.eval.html.md#foley.eval.RetrievalReport).

Only query ids present in `qrels` are scored (a run row without a
ground-truth judgment is ignored). `ranks` records, per query, the 1-based
rank of the highest-graded answer (for the failure diff).

* **Return type:**
  [`RetrievalReport`](_autosummary/foley.eval.retrieval.html.md#foley.eval.retrieval.RetrievalReport)

### foley.eval.fit_f1(matches, relevants)

The harmonic mean of [`fit_precision()`](_autosummary/foley.eval.html.md#foley.eval.fit_precision) and [`fit_recall()`](_autosummary/foley.eval.html.md#foley.eval.fit_recall).

* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

### foley.eval.fit_precision(matches, relevants)

Fraction of judge-accepted candidates that are actually gold-relevant.

`TP / (TP + FP)` where a candidate is a positive iff the fit-judge said `match`
and gold-relevant iff its golden grade ≥ 1. This measures the JUDGE’s auto-accept
*purity* (a low value ⇒ the judge waves through wrong clips) — NOT the ranker. Pure,
closed-form, and the decisive correctness oracle (the Ring-0 + fake-judge plumbing
makes the end-to-end value tautological, so the metric math is tested in isolation).

* **Parameters:**
  * **matches** ([`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)[[`bool`](https://docs.python.org/3/builtins/functions.html#bool)]) – Per-candidate fit-judge `match` booleans.
  * **relevants** ([`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)[[`bool`](https://docs.python.org/3/builtins/functions.html#bool)]) – Per-candidate gold-relevance booleans (grade ≥ 1).
* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)
* **Returns:**
  The fit precision in `[0, 1]` (`0.0` when nothing was accepted).

### foley.eval.fit_recall(matches, relevants)

`TP / (TP + FN)` — of the gold-relevant candidates, how many the judge confirmed.

* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

### foley.eval.frechet_distance(x_ref, x_gen)

Fréchet distance between two embedding sets (the FAD formula).

`‖μ_r − μ_g‖² + Tr(Σ_r + Σ_g − 2(Σ_r Σ_g)^½)` — lower = the generated set’s
embedding distribution is closer to the reference. Feed PANNs Wavegram-Logmel
embeddings for “FAD-P”, or CLAP for a domain-matched FAD.

* **Parameters:**
  * **x_ref** – Reference embeddings `(n_ref, d)`.
  * **x_gen** – Generated embeddings `(n_gen, d)`.
* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)
* **Returns:**
  The FAD as a `float` (`≥ 0` up to numerical round-off; `~0` for identical
  distributions).

### foley.eval.generation_fidelity(ref_wavs, gen_wavs, , embedder, sr=48000, metric='fad', toolkit='foley-numpy', version='1')

Embed two wav sets through an injected embedder and compute FAD or KAD.

The real fidelity path is a **zero-call-site-change embedder swap**: a fake /
hashing embedder in CI, PANNs / CLAP (`foley[fit]`) in prod. The embedder’s
`model_id` is stamped into the result.

* **Parameters:**
  * **ref_wavs** – An iterable of reference waveforms (1-D arrays).
  * **gen_wavs** – An iterable of generated waveforms (1-D arrays).
  * **embedder** – An object with `embed_audio(wav, sr) -> vector` and `model_id`.
  * **sr** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The sample rate passed to `embed_audio`.
  * **metric** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – `'fad'` (default) or `'kad'`.
  * **toolkit** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Provenance label for the stamp (default `'foley-numpy'`).
  * **version** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Provenance version for the stamp.
* **Return type:**
  [`FidelityResult`](_autosummary/foley.eval.fidelity.html.md#foley.eval.fidelity.FidelityResult)
* **Returns:**
  A [`FidelityResult`](_autosummary/foley.eval.html.md#foley.eval.FidelityResult) carrying the score and its [`FidelityStamp`](_autosummary/foley.eval.html.md#foley.eval.FidelityStamp).

### foley.eval.is_stale(baseline, , seed_path, manifest_path)

True if the baseline’s fixture stamps no longer match the fixtures on disk.

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### foley.eval.kernel_audio_distance(x_ref, x_gen, , bandwidth=None)

Kernel Audio Distance — the unbiased RBF-MMD² between two embedding sets.

Distribution-free and small-sample-convergent; preferred over FAD for the small
reference/generated sets of early-stage eval. The diagonal (same-sample) kernel terms
are dropped (the unbiased estimator), so `KAD(X, X) ≈ 0` and can be slightly
negative.

* **Parameters:**
  * **x_ref** – Reference embeddings `(n_ref, d)`.
  * **x_gen** – Generated embeddings `(n_gen, d)`.
  * **bandwidth** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`float`](https://docs.python.org/3/builtins/functions.html#float)]) – RBF bandwidth σ (default: the median heuristic over the pooled set).
* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)
* **Returns:**
  The unbiased MMD² as a `float`.

### foley.eval.krippendorff_alpha(reliability_data, , level='ordinal', value_domain=None)

Krippendorff’s α over `(raters × units)` ratings (`nan` = missing).

`α = 1 − D_o/D_e` — 1 is perfect agreement, ~0 is chance, negative is systematic
disagreement. Ordinal is the default (foley’s 0–3 grades); missing ratings are
tolerated (units with < 2 ratings are dropped, no imputation). Pure numpy.

* **Parameters:**
  * **reliability_data** – A 2-D array-like `(raters × units)`; `nan` = missing.
  * **level** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – `'ordinal'` (default) | `'nominal'` | `'interval'`.
  * **value_domain** – Optional explicit value set (for a label unobserved by some rater).
* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)
* **Returns:**
  α as a bare `float` (`1.0` when there is no expected disagreement, `D_e = 0`).

### foley.eval.load_baseline(path=PosixPath('/home/runner/work/foley/foley/foley/data/golden/baseline.json'))

Load the committed baseline dict from `path`.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### foley.eval.load_golden(path=PosixPath('/home/runner/work/foley/foley/foley/data/golden/seed.json'))

Load and validate the frozen golden set from `path` (JSON list).

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`GoldenItem`](_autosummary/foley.eval.golden.html.md#foley.eval.golden.GoldenItem)]

### foley.eval.mean_over_queries(values)

Macro-average of per-query metric values (0.0 for an empty list).

* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

### foley.eval.mrr_at_k(qrels_q, run_q, k=10, , rel_lvl=1)

Reciprocal rank of the first relevant doc in the top-`k` (0.0 if none).

* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

### foley.eval.ndcg_at_k(qrels_q, run_q, k=10)

Normalized DCG at `k` with linear gains (0.0 when no graded answer).

* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

### foley.eval.percent_agreement(reliability_data)

Raw pairwise percent agreement, reported ALONGSIDE α (report 08 §2.3).

On foley’s skewed ‘most candidates irrelevant’ label distribution a chance-corrected
α can look pessimistic, so the uncorrected agreement is co-reported for context.

* **Parameters:**
  **reliability_data** – A 2-D array-like `(raters × units)`; `nan` = missing.
* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)
* **Returns:**
  The fraction of same-unit rater pairs that agree (`0.0` if no pairs).

### foley.eval.precision_at_k(qrels_q, run_q, k=10, , rel_lvl=1)

Fraction of the top-`k` that is relevant (denominator is literal `k`).

* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

### foley.eval.recall_at_k(qrels_q, run_q, k=10, , rel_lvl=1)

Fraction of ALL relevant docs (grade ≥ `rel_lvl`) retrieved in the top-`k`.

* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

### foley.eval.reliability_band(alpha)

Map an α (or κ) to Krippendorff’s benchmark band.

* **Parameters:**
  **alpha** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – A reliability coefficient in `(-∞, 1]`.
* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)
* **Returns:**
  `'reliable'` (α ≥ 0.8), `'tentative'` (0.667 ≤ α < 0.8), else
  `'revise-rubric'`.

### foley.eval.run_fit_eval(, golden=None, golden_path=PosixPath('/home/runner/work/foley/foley/foley/data/golden/seed.json'), sample=None, strata_keys=('family', 'diegetic'), fit_judge=None, embedder=None, level=VerifyLevel.judge, seed=0, k=10, llm=None)

Run the Tier-2 fit eval over a stratified golden sample → a [`FitReport`](_autosummary/foley.eval.html.md#foley.eval.FitReport).

Composes the Tier-1 golden builders + the #7 SELECT loop, applies the authoritative
fit-judge as an independent audit, and aggregates fit-precision/recall/F1 + fit-score

+ auto-accept-rate, with a per-stratum breakdown. Report-only unless
  `min_fit_precision` is supplied.

* **Parameters:**
  * **golden** – A pre-loaded golden list (default: load `golden_path`).
  * **golden_path** – The golden JSON (default: the bundled Ring-0 seed set).
  * **sample** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`int`](https://docs.python.org/3/builtins/functions.html#int)]) – The stratified sample cap (default: the whole set — the cost gate).
  * **strata_keys** – The stratification axes (default `('family', 'diegetic')`).
  * **fit_judge** – The injected authoritative [`Judge`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Judge)
    (default: `foley.agent.verify._default_fit_judge()` for `llm` — the
    hermetic fake unless `llm` / `$FOLEY_LLM` opts in).
  * **embedder** – The Ring-0 text/​audio embedder (default: [`HashingBowEmbedder`](_autosummary/foley.eval.html.md#foley.eval.HashingBowEmbedder)).
  * **level** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`VerifyLevel`](_autosummary/foley.base.html.md#foley.base.VerifyLevel)) – The verify rung the fit-judge audits at — `'listen'` or `'judge'`
    (default `VerifyLevel.judge`). `'clap'` is rejected: it never invokes the
    fit-judge (it only re-runs the retrieval clap gate), so it cannot measure fit.
  * **seed** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The sampling RNG seed.
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The retrieval shortlist depth per event.
* **Return type:**
  [`FitReport`](_autosummary/foley.eval.fit.html.md#foley.eval.fit.FitReport)
* **Returns:**
  A [`FitReport`](_autosummary/foley.eval.html.md#foley.eval.FitReport). Gating is the caller’s job via [`FitReport.gate()`](_autosummary/foley.eval.html.md#foley.eval.FitReport.gate).
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If `level` is `'clap'` (fit judging requires a listen/judge rung).

### foley.eval.run_ring0_retrieval_eval(, k=10, golden_path=PosixPath('/home/runner/work/foley/foley/foley/data/golden/seed.json'), embedder=None)

Score the golden set’s queries against the Ring-0 library — the gate input.

Runs every golden `expected_events[].query` through the real
`SoundLibrary.search()` path (vector ⊕ BM25 ⊕ RRF) and evaluates the
resulting runs against the golden qrels.

* **Parameters:**
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Retrieval cutoff (and the metric `@k`).
  * **golden_path** – The golden set JSON.
  * **embedder** – The eval embedder (default: [`HashingBowEmbedder`](_autosummary/foley.eval.html.md#foley.eval.HashingBowEmbedder)).
* **Return type:**
  [`RetrievalReport`](_autosummary/foley.eval.retrieval.html.md#foley.eval.retrieval.RetrievalReport)
* **Returns:**
  A [`RetrievalReport`](_autosummary/foley.eval.html.md#foley.eval.RetrievalReport) (per-query + mean [nDCG@10](mailto:nDCG@10) / recall / mAP / MRR).

### foley.eval.stratified_sample(units, , strata_keys, sample, seed)

Deterministic seeded stratified draw over `units` — the cost gate.

Round-robins across strata (so an easy family cannot dominate a capped sample);
`sample=None` keeps the full set. Each unit is `(GoldenItem, event_index,
event_dict)`.

* **Parameters:**
  * **units** ([`list`](https://docs.python.org/3/builtins/stdtypes.html#list)) – The full `(item, ev_idx, ev_dict)` unit list.
  * **strata_keys** – The stratification axes (e.g. `('family', 'diegetic')`).
  * **sample** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`int`](https://docs.python.org/3/builtins/functions.html#int)]) – The total draw size, or `None` for all.
  * **seed** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The RNG seed (reproducibility).
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)

### foley.eval.to_qrels(golden)

Flatten the golden set into TREC qrels: `{query_id: {clip_id: grade}}`.

One qrels row per `(item, event)` — `query_id = f"{item.id}::{event_idx}"`
— future-proofing multi-event items (seed items are single-event today). Each
clip in `answer_clip_ids` carries its `grade`.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`int`](https://docs.python.org/3/builtins/functions.html#int)]]

### foley.eval.write_baseline(report, , path=PosixPath('/home/runner/work/foley/foley/foley/data/golden/baseline.json'), metric='ndcg@10', tolerance=0.02, seed_path, manifest_path, embedder_model_id='foley-eval/hashing-bow-v1', dim=64, rrf_k=60, updated_at, n_items, revision='gld-v1')

Write a fresh baseline from `report` (the `--update-baseline` action).

Records the mean metric value plus sha256 stamps of the seed + manifest so a
later fixture edit is detected. `updated_at` is passed in (not read from the
clock) so the caller controls reproducibility. `revision` labels the golden-set
generation (bumped when the frozen set is regrown).

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)
* **Returns:**
  The baseline dict that was written.

### Modules

| [`baseline`](_autosummary/foley.eval.baseline.html.md#module-foley.eval.baseline)       | The committed nDCG baseline — the PR gate's SSOT + staleness stamps.                           |
|--------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------|
| [`embedder`](_autosummary/foley.eval.embedder.html.md#module-foley.eval.embedder)       | A deterministic, numpy-only text embedder for the retrieval eval gate.                         |
| [`fidelity`](_autosummary/foley.eval.fidelity.html.md#module-foley.eval.fidelity)       | Set-level generation-fidelity — Fréchet Audio Distance (FAD) + Kernel Audio Distance (KAD).    |
| [`fit`](_autosummary/foley.eval.fit.html.md#module-foley.eval.fit)                 | Tier-2 fit evaluation — "does the accepted clip actually match the intent?" (report 08 §2/§5). |
| [`golden`](_autosummary/foley.eval.golden.html.md#module-foley.eval.golden)           | The frozen golden set + the deterministic Ring-0 retrieval harness.                            |
| [`reliability`](_autosummary/foley.eval.reliability.html.md#module-foley.eval.reliability) | Inter-rater reliability — Krippendorff's α + the judge-vs-human calibration guardrail.         |
| [`retrieval`](_autosummary/foley.eval.retrieval.html.md#module-foley.eval.retrieval)     | Tier-1 retrieval metrics + run assembly — pure stdlib, CI-safe, ranx-exact.                    |


# _autosummary/foley.eval.reliability.html.md

# foley.eval.reliability

Inter-rater reliability — Krippendorff’s α + the judge-vs-human calibration guardrail.

Whatever judges foley’s fit — humans or an LLM/audio-LM — you must \*\*quantify agreement
before trusting the scores\*\* (report 08 §2.3). This module is the numbers side of that
guardrail: one pure-numpy Krippendorff’s α (the most general reliability coefficient —
any number of raters, ordinal/graded labels, missing-data tolerant), reused for BOTH
human inter-rater reliability on the gold grades AND judge-vs-human calibration (the
model judge is just one more rater row). A model judge is only promoted to unattended
use once it reaches human-level agreement on a calibration slice.

Pure numpy / stdlib, deterministic — `numpy` is imported function-locally so importing
this module keeps `import foley` dol-only (the same discipline as
[`foley.eval.retrieval`](_autosummary/foley.eval.retrieval.html.md#module-foley.eval.retrieval)). No `scipy`, no external reliability toolkit (a PyPI
`krippendorff` oracle cross-check lives behind an opt-in test only).

### Module Attributes

| [`ALPHA_RELIABLE`](_autosummary/foley.eval.reliability.html.md#foley.eval.reliability.ALPHA_RELIABLE)   | Krippendorff's benchmark bands for a reliability coefficient (report 08 §2.3).   |
|-------------------------------------------------------------------|----------------------------------------------------------------------------------|

### Functions

| [`calibrate_judge_vs_human`](_autosummary/foley.eval.reliability.html.md#foley.eval.reliability.calibrate_judge_vs_human)(human_grades, ...)     | Compute judge-vs-human agreement and the promotion verdict (report 08 §2.3).   |
|--------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------|
| [`krippendorff_alpha`](_autosummary/foley.eval.reliability.html.md#foley.eval.reliability.krippendorff_alpha)(reliability_data, \*[, ...]) | Krippendorff's α over `(raters × units)` ratings (`nan` = missing).            |
| [`percent_agreement`](_autosummary/foley.eval.reliability.html.md#foley.eval.reliability.percent_agreement)(reliability_data)             | Raw pairwise percent agreement, reported ALONGSIDE α (report 08 §2.3).         |
| [`reliability_band`](_autosummary/foley.eval.reliability.html.md#foley.eval.reliability.reliability_band)(alpha)                         | Map an α (or κ) to Krippendorff's benchmark band.                              |

### Classes

| [`AlphaResult`](_autosummary/foley.eval.reliability.html.md#foley.eval.reliability.AlphaResult)(alpha, level, n_units, n_raters, ...)   | A judge-vs-human calibration record (surfaced as `FitReport.calibration`).   |
|------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------|

### foley.eval.reliability.ALPHA_RELIABLE *: [float](https://docs.python.org/3/builtins/functions.html#float)* *= 0.8*

Krippendorff’s benchmark bands for a reliability coefficient (report 08 §2.3).

### *class* foley.eval.reliability.AlphaResult(alpha, level, n_units, n_raters, percent_agreement, band, promoted)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

A judge-vs-human calibration record (surfaced as `FitReport.calibration`).

Codifies the promotion rule in one legible object: a model judge is `promoted` to
unattended use only once its agreement with the human raters reaches the
[`ALPHA_RELIABLE`](_autosummary/foley.eval.reliability.html.md#foley.eval.reliability.ALPHA_RELIABLE) band.

### foley.eval.reliability.calibrate_judge_vs_human(human_grades, judge_grades, , level='ordinal')

Compute judge-vs-human agreement and the promotion verdict (report 08 §2.3).

Stacks the human rating row(s) and the model-judge row into one reliability matrix
and returns an [`AlphaResult`](_autosummary/foley.eval.reliability.html.md#foley.eval.reliability.AlphaResult); `promoted` is `True` iff α reaches the
reliable band — the model judge may then be trusted unattended on that slice.

* **Parameters:**
  * **human_grades** – A 1-D per-unit human grade sequence, or a 2-D `(humans × units)`
    matrix (`nan` = missing).
  * **judge_grades** – The model judge’s 1-D per-unit grades (`nan` = missing).
  * **level** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The measurement level (default `'ordinal'`).
* **Return type:**
  [`AlphaResult`](_autosummary/foley.eval.reliability.html.md#foley.eval.reliability.AlphaResult)
* **Returns:**
  An [`AlphaResult`](_autosummary/foley.eval.reliability.html.md#foley.eval.reliability.AlphaResult).

### foley.eval.reliability.krippendorff_alpha(reliability_data, , level='ordinal', value_domain=None)

Krippendorff’s α over `(raters × units)` ratings (`nan` = missing).

`α = 1 − D_o/D_e` — 1 is perfect agreement, ~0 is chance, negative is systematic
disagreement. Ordinal is the default (foley’s 0–3 grades); missing ratings are
tolerated (units with < 2 ratings are dropped, no imputation). Pure numpy.

* **Parameters:**
  * **reliability_data** – A 2-D array-like `(raters × units)`; `nan` = missing.
  * **level** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – `'ordinal'` (default) | `'nominal'` | `'interval'`.
  * **value_domain** – Optional explicit value set (for a label unobserved by some rater).
* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)
* **Returns:**
  α as a bare `float` (`1.0` when there is no expected disagreement, `D_e = 0`).

### foley.eval.reliability.percent_agreement(reliability_data)

Raw pairwise percent agreement, reported ALONGSIDE α (report 08 §2.3).

On foley’s skewed ‘most candidates irrelevant’ label distribution a chance-corrected
α can look pessimistic, so the uncorrected agreement is co-reported for context.

* **Parameters:**
  **reliability_data** – A 2-D array-like `(raters × units)`; `nan` = missing.
* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)
* **Returns:**
  The fraction of same-unit rater pairs that agree (`0.0` if no pairs).

### foley.eval.reliability.reliability_band(alpha)

Map an α (or κ) to Krippendorff’s benchmark band.

* **Parameters:**
  **alpha** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – A reliability coefficient in `(-∞, 1]`.
* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)
* **Returns:**
  `'reliable'` (α ≥ 0.8), `'tentative'` (0.667 ≤ α < 0.8), else
  `'revise-rubric'`.


# _autosummary/foley.eval.retrieval.html.md

# foley.eval.retrieval

Tier-1 retrieval metrics + run assembly — pure stdlib, CI-safe, ranx-exact.

The metrics (`nDCG@10`, `Recall@k`, `Precision@k`, `mAP@10`, `MRR@10`)
are hand-rolled in stdlib (no `ranx`/`numba`) so the nDCG PR gate runs in
foley’s numpy-only CI with zero heavy deps — yet they are \*\*numerically
identical\*\* to `ranx`/`pytrec_eval` (guarded by the opt-in
`tests/eval/test_metrics_vs_ranx.py` oracle). nDCG uses the **linear** Järvelin
gain `Σ grade_i / log2(rank+1)` — the definition `ranx`’s `"ndcg@10"` and
`pytrec_eval`’s `ndcg_cut_10` actually implement (report 08 §1.1’s
`2^grade−1` is a doc bug).

Vocabulary (TREC-style):

> * **qrels** — `{query_id: {clip_id: grade}}` — the ground-truth relevance
>   grades (0 = wrong, 1 = acceptable, 2 = ideal); the raw grade is the gain,
>   so the metrics are grade-scale-agnostic.
> * **run** — `{query_id: {clip_id: score}}` — the system’s scored results.

[`build_run()`](_autosummary/foley.eval.retrieval.html.md#foley.eval.retrieval.build_run) assigns strictly-descending integer scores in the search order,
so a run has **no score ties** and its ranking reproduces `search()`’s
(already id-tiebroken) order exactly — the source of cross-platform determinism.

### Module Attributes

| [`DEFAULT_METRICS`](_autosummary/foley.eval.retrieval.html.md#foley.eval.retrieval.DEFAULT_METRICS)   | The metrics reported by [`evaluate_run()`](_autosummary/foley.eval.retrieval.html.md#foley.eval.retrieval.evaluate_run) ([nDCG@10](mailto:nDCG@10) is the sole PR gate).   |
|--------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------|

### Functions

| [`average_precision_at_k`](_autosummary/foley.eval.retrieval.html.md#foley.eval.retrieval.average_precision_at_k)(qrels_q, run_q[, k, ...])   | Average precision (trec_eval `map`: divide by TOTAL relevant, not `min(R,k)`).                               |
|-----------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------|
| [`build_run`](_autosummary/foley.eval.retrieval.html.md#foley.eval.retrieval.build_run)(candidates)                              | Turn an ordered `search()` result into a run row with distinct scores.                                       |
| [`evaluate_run`](_autosummary/foley.eval.retrieval.html.md#foley.eval.retrieval.evaluate_run)(run, qrels, \*[, k, metrics, ...])    | Score a `run` against `qrels`, returning a [`RetrievalReport`](_autosummary/foley.eval.retrieval.html.md#foley.eval.retrieval.RetrievalReport). |
| [`mean_over_queries`](_autosummary/foley.eval.retrieval.html.md#foley.eval.retrieval.mean_over_queries)(values)                          | Macro-average of per-query metric values (0.0 for an empty list).                                            |
| [`mrr_at_k`](_autosummary/foley.eval.retrieval.html.md#foley.eval.retrieval.mrr_at_k)(qrels_q, run_q[, k, rel_lvl])             | Reciprocal rank of the first relevant doc in the top-`k` (0.0 if none).                                      |
| [`ndcg_at_k`](_autosummary/foley.eval.retrieval.html.md#foley.eval.retrieval.ndcg_at_k)(qrels_q, run_q[, k])                     | Normalized DCG at `k` with linear gains (0.0 when no graded answer).                                         |
| [`precision_at_k`](_autosummary/foley.eval.retrieval.html.md#foley.eval.retrieval.precision_at_k)(qrels_q, run_q[, k, rel_lvl])       | Fraction of the top-`k` that is relevant (denominator is literal `k`).                                       |
| [`recall_at_k`](_autosummary/foley.eval.retrieval.html.md#foley.eval.retrieval.recall_at_k)(qrels_q, run_q[, k, rel_lvl])          | Fraction of ALL relevant docs (grade ≥ `rel_lvl`) retrieved in the top-`k`.                                  |

### Classes

| [`RetrievalReport`](_autosummary/foley.eval.retrieval.html.md#foley.eval.retrieval.RetrievalReport)([per_query, mean, ranks, k])   | Per-query + mean Tier-1 metrics over a golden set (JSON-friendly).   |
|-------------------------------------------------------------------------------------------------|----------------------------------------------------------------------|

### foley.eval.retrieval.DEFAULT_METRICS *= ('ndcg@10', 'recall@10', 'map@10', 'mrr@10')*

The metrics reported by [`evaluate_run()`](_autosummary/foley.eval.retrieval.html.md#foley.eval.retrieval.evaluate_run) ([nDCG@10](mailto:nDCG@10) is the sole PR gate).

### *class* foley.eval.retrieval.RetrievalReport(per_query=<factory>, mean=<factory>, ranks=<factory>, k=10)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Per-query + mean Tier-1 metrics over a golden set (JSON-friendly).

#### format_regression_diff(baseline)

A human diff for a failing gate: mean vs baseline + the worst queries.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### foley.eval.retrieval.average_precision_at_k(qrels_q, run_q, k=10, , rel_lvl=1)

Average precision (trec_eval `map`: divide by TOTAL relevant, not `min(R,k)`).

* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

### foley.eval.retrieval.build_run(candidates)

Turn an ordered `search()` result into a run row with distinct scores.

Assigns each candidate a strictly-descending integer score
(`len - rank`) in the order returned, so the run carries \*\*no score
ties\*\* and `_ranked_ids()` reproduces the search ranking exactly — the
determinism the gate relies on. The doc id is `candidate.sound.id`.

* **Parameters:**
  **candidates** – An ordered `list[Candidate]` from
  [`foley.index.library.SoundLibrary.search()`](_autosummary/foley.index.library.html.md#foley.index.library.SoundLibrary.search) (best first).
* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]
* **Returns:**
  `{clip_id: score}` with distinct descending scores.

### foley.eval.retrieval.evaluate_run(run, qrels, , k=10, metrics=('ndcg@10', 'recall@10', 'map@10', 'mrr@10'), rel_lvl=1)

Score a `run` against `qrels`, returning a [`RetrievalReport`](_autosummary/foley.eval.retrieval.html.md#foley.eval.retrieval.RetrievalReport).

Only query ids present in `qrels` are scored (a run row without a
ground-truth judgment is ignored). `ranks` records, per query, the 1-based
rank of the highest-graded answer (for the failure diff).

* **Return type:**
  [`RetrievalReport`](_autosummary/foley.eval.retrieval.html.md#foley.eval.retrieval.RetrievalReport)

### foley.eval.retrieval.mean_over_queries(values)

Macro-average of per-query metric values (0.0 for an empty list).

* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

### foley.eval.retrieval.mrr_at_k(qrels_q, run_q, k=10, , rel_lvl=1)

Reciprocal rank of the first relevant doc in the top-`k` (0.0 if none).

* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

### foley.eval.retrieval.ndcg_at_k(qrels_q, run_q, k=10)

Normalized DCG at `k` with linear gains (0.0 when no graded answer).

* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

### foley.eval.retrieval.precision_at_k(qrels_q, run_q, k=10, , rel_lvl=1)

Fraction of the top-`k` that is relevant (denominator is literal `k`).

* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

### foley.eval.retrieval.recall_at_k(qrels_q, run_q, k=10, , rel_lvl=1)

Fraction of ALL relevant docs (grade ≥ `rel_lvl`) retrieved in the top-`k`.

* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)


# _autosummary/foley.html.md

# foley

foley — a retrieval-first façade for sound effects.

foley finds (or generates) the right sound effect for a moment of narration and
weaves it in. It is the SFX sibling of `arioso` (a unified façade over AI
music-generation backends): one simple surface over many sound *sources* (a
bring-your-own library, service APIs like Freesound, and generative-AI models),
a searchable *index* of every sound (by keyword *and* meaning, via CLAP
embeddings + hybrid search), an *agent* that selects the right sound for a
narrative context, and a *compositor* that places it under the voice.

Four stages:

```default
SOURCE  ->  INDEX  ->  SELECT  ->  WEAVE
(get)      (find)      (choose)    (compose)
```

The façade (v1 — Epic #13 complete; the surface below is live — see
`misc/docs/design.md` / `misc/docs/roadmap.md` for the roadmap):

```default
import foley

foley.find("She pushed open the heavy oak door; rain hammered outside.")
foley.search("distant thunder rumble", k=10)
foley.generate("a single wooden door creak", backend="stable_audio")
foley.ingest("~/my_sounds/")
```

The design is grounded in the research reports under `misc/docs/research/`.

The whole four-stage surface (source → index → select → weave) plus the MCP server and
the licensing/provenance, evaluation, and observability layers is implemented and
re-exported here (see `__all__` and the `find` / `search` / `generate` /
`ingest` / `weave` façade functions below). The retrieval-agnostic **foundation**
every later stage stands on:

> * **Data models** (`foley.base`) — the SSOT dataclasses/enums shared across
>   layers ([`SoundRecord`](_autosummary/foley.html.md#foley.SoundRecord), [`LicenseRecord`](_autosummary/foley.html.md#foley.LicenseRecord), [`Candidate`](_autosummary/foley.html.md#foley.Candidate),
>   [`SoundEvent`](_autosummary/foley.html.md#foley.SoundEvent), [`Verdict`](_autosummary/foley.html.md#foley.Verdict), [`IntendedUse`](_autosummary/foley.html.md#foley.IntendedUse)), the two
>   > affordance registries, and generic dict/JSON (de)serialization.
> * **License policy** (`foley.licensing`) — the `license_id` -> flag-set
>   SSOT (`LICENSE_FLAGS`), flag derivation, and the fail-closed
>   [`keep()`](_autosummary/foley.html.md#foley.keep) gate.
> * **Storage** (`foley.stores`) — content-addressed byte store + metadata
>   store built from `dol`, and [`store_sound()`](_autosummary/foley.html.md#foley.store_sound) (the by-value vs
>   by-reference gate driven by `LicenseRecord.cache_bytes_ok`).
> * **QC** (`foley.qc`) — Tier-0 deterministic audio checks
>   ([`run_qc()`](_autosummary/foley.html.md#foley.run_qc) -> [`QCReport`](_autosummary/foley.html.md#foley.QCReport), thresholds in [`QCThresholds`](_autosummary/foley.html.md#foley.QCThresholds)).
> * **Audio** (`foley.audio`) — I/O + DSP primitives. Exposed as a submodule
>   (`foley.audio`) with the key functions also re-exported here.

Import cost: `import foley` pulls only `dol` (a light core dependency used by
`foley.stores`); `numpy`/`soundfile`/`soxr`/`librosa`/`pyloudnorm` are
lazy-imported inside the audio/QC functions that need them (install via the
`foley[audio]` extra), so a bare install imports cleanly.

### Functions

| [`resolve_master`](_autosummary/foley.html.md#foley.resolve_master)(master)                              | Resolve a master spec (profile name, explicit profile, or `None`) to a `MasterProfile`.                                                                                        |
|------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`license_meta`](_autosummary/foley.html.md#foley.license_meta)(license_id)                            | Return the display [`LicenseMeta`](_autosummary/foley.html.md#foley.LicenseMeta) for `license_id` (fail-closed fallback).                                                       |
| [`license_id_from_cc_url`](_autosummary/foley.html.md#foley.license_id_from_cc_url)(url)                         | Map a Creative-Commons license URL **or label** to `(license_id, verified)`.                                                                                                   |
| [`derive_license_flags`](_autosummary/foley.html.md#foley.derive_license_flags)(license_id, \*[, overrides])   | Look up the flag set for a `license_id` (fail-closed fallback), then apply per-source overrides.                                                                               |
| [`apply_license_flags`](_autosummary/foley.html.md#foley.apply_license_flags)(record, \*[, overrides])        | Populate `record`'s eight derived flags from its `license_id` (+ overrides), in place, and return it.                                                                          |
| [`keep`](_autosummary/foley.html.md#foley.keep)(record, intended_use)                          | Fail-closed candidate license gate (report 07 §8.2).                                                                                                                           |
| [`keep_sound`](_autosummary/foley.html.md#foley.keep_sound)(sound_record, intended_use)              | Convenience: apply [`keep()`](_autosummary/foley.html.md#foley.keep) to a `SoundRecord`'s nested license.                                                                |
| [`content_key`](_autosummary/foley.html.md#foley.content_key)(data, \*[, algo])                       | Return the content-address key for `data` — its hex digest.                                                                                                                    |
| [`make_byte_store`](_autosummary/foley.html.md#foley.make_byte_store)([rootdir])                          | Build the content-addressable blob store: `Mapping[content_key -> bytes]`.                                                                                                     |
| [`make_meta_store`](_autosummary/foley.html.md#foley.make_meta_store)([rootdir])                          | Build the metadata store: `Mapping[sound_id -> SoundRecord]` (JSON files).                                                                                                     |
| [`make_run_store`](_autosummary/foley.html.md#foley.make_run_store)([rootdir])                           | Build the run-artifact store: `Mapping[run_id -> RunManifest dict]` (JSON files).                                                                                              |
| [`store_sound`](_autosummary/foley.html.md#foley.store_sound)(record[, data, cache_bytes_ok])         | Persist a sound, choosing by-value vs by-reference from `cache_bytes_ok`.                                                                                                      |
| [`run_qc`](_autosummary/foley.html.md#foley.run_qc)(samples, sample_rate, \*[, thresholds])      | Run every Tier-0 check and fold the results into a [`QCReport`](_autosummary/foley.html.md#foley.QCReport).                                                                  |
| [`has_nan_inf`](_autosummary/foley.html.md#foley.has_nan_inf)(samples)                                | Return `True` if any sample is `NaN` or `Inf` (corrupt-clip guard).                                                                                                            |
| [`duration_s`](_autosummary/foley.html.md#foley.duration_s)(samples, sample_rate)                    | Clip duration in seconds: `frames / sample_rate`.                                                                                                                              |
| [`dc_offset`](_autosummary/foley.html.md#foley.dc_offset)(samples)                                  | Largest per-channel absolute DC offset, `max_c |mean_n x[n, c]|`.                                                                                                              |
| [`is_silent`](_autosummary/foley.html.md#foley.is_silent)(samples, \*[, rms_floor_dbfs])            | Return `True` when whole-clip RMS falls below `rms_floor_dbfs`.                                                                                                                |
| [`detect_clipping`](_autosummary/foley.html.md#foley.detect_clipping)(samples, \*[, full_scale, ...])     | Detect hard (flat-topped) clipping.                                                                                                                                            |
| [`true_peak_dbtp`](_autosummary/foley.html.md#foley.true_peak_dbtp)(samples, sample_rate, \*[, ...])     | Inter-sample true-peak level in dBTP.                                                                                                                                          |
| [`estimate_snr`](_autosummary/foley.html.md#foley.estimate_snr)(samples, sample_rate, \*[, ...])       | Estimate SNR in dB (advisory — a busy-street SFX legitimately scores low).                                                                                                     |
| [`needs_edge_fade`](_autosummary/foley.html.md#foley.needs_edge_fade)(samples, \*[, rel_peak_dbfs])       | Return `True` when the first or last sample sits above `rel_peak_dbfs` relative to the clip peak — i.e. a nonzero boundary that clicks under narration and needs a short fade. |
| [`measure_lufs`](_autosummary/foley.html.md#foley.measure_lufs)(samples, sample_rate, \*[, ...])       | Integrated loudness (LUFS, ITU-R BS.1770-4) via `pyloudnorm` (lazy).                                                                                                           |
| [`load`](_autosummary/foley.html.md#foley.load)(src, \*[, target_sr, mono, dtype])             | Decode audio into a float working array.                                                                                                                                       |
| [`save`](_autosummary/foley.html.md#foley.save)(samples, sample_rate, dst, \*[, fmt, ...])     | Write `samples` to `dst` as `fmt`/`subtype` (default = FLAC archive).                                                                                                          |
| [`encode`](_autosummary/foley.html.md#foley.encode)(samples, sample_rate, \*[, fmt, subtype])    | Encode `samples` fully in memory and return the container bytes.                                                                                                               |
| [`resample`](_autosummary/foley.html.md#foley.resample)(samples, sample_rate, \*[, ...])           | Resample `samples` to `target_sr` (a no-op when already there).                                                                                                                |
| [`to_mono`](_autosummary/foley.html.md#foley.to_mono)(samples)                                    | Down-mix to mono by averaging channels; 1-D input passes through.                                                                                                              |
| [`ensure_channels`](_autosummary/foley.html.md#foley.ensure_channels)(samples, \*, channels)              | Coerce `samples` to exactly `channels` channels.                                                                                                                               |
| [`trim_silence`](_autosummary/foley.html.md#foley.trim_silence)(samples, sample_rate, \*[, top_db])    | Strip leading/trailing silence, returning the clip and its kept span.                                                                                                          |
| [`fade`](_autosummary/foley.html.md#foley.fade)(samples, sample_rate, \*[, fade_in_s, ...])    | Apply in/out gain ramps to `samples` (a short declick by default).                                                                                                             |
| [`loudness_normalize`](_autosummary/foley.html.md#foley.loudness_normalize)(samples, sample_rate, \*)        | Loudness-normalize to `target_lufs`, then keep it peak-safe.                                                                                                                   |
| [`to_working`](_autosummary/foley.html.md#foley.to_working)(samples, sample_rate, \*[, mono, ...])   | Produce the canonical CLAP/QC working array from an arbitrary clip.                                                                                                            |
| [`default_library`](_autosummary/foley.html.md#foley.default_library)()                                   | The process-wide default library (local stores + CLAP + best index).                                                                                                           |
| [`search`](_autosummary/foley.html.md#foley.search)(query, \*[, k, filters, ...])                | Hybrid (CLAP vector ⊕ BM25) search of the default library.                                                                                                                     |
| [`estimate`](_autosummary/foley.html.md#foley.estimate)(verb, \*\*kwargs)                          | What a call would cost in USD before making it — `None` when it cannot be known (#57).                                                                                         |
| [`similar`](_autosummary/foley.html.md#foley.similar)(sound_id, \*[, k, commercial_ok])           | Find sounds similar to a stored sound (audio<->audio) in the default library.                                                                                                  |
| [`default_embedder`](_autosummary/foley.html.md#foley.default_embedder)()                                  | Return a process-wide default [`ClapEmbedder`](_autosummary/foley.html.md#foley.ClapEmbedder) (loaded once, reused).                                                             |
| [`default_index`](_autosummary/foley.html.md#foley.default_index)(\*, data_dir, dim)                    | Build the best available persistent index for a library.                                                                                                                       |
| [`lancedb_available`](_autosummary/foley.html.md#foley.lancedb_available)()                                 | True if `lancedb` is importable (the `foley[index]` extra is present).                                                                                                         |
| [`sqlite_vec_loadable`](_autosummary/foley.html.md#foley.sqlite_vec_loadable)()                               | True if `sqlite_vec` is installed AND this interpreter can load it.                                                                                                            |
| [`hybrid_search`](_autosummary/foley.html.md#foley.hybrid_search)(query, \*, embedder, vindex, kindex)  | Embed `query`, run the vector + keyword rankers, and RRF-fuse them.                                                                                                            |
| [`vector_search`](_autosummary/foley.html.md#foley.vector_search)(qvec, \*, vindex[, k, where])         | Pure audio<->audio (or clip->library) vector search — no keyword leg.                                                                                                          |
| [`reciprocal_rank_fusion`](_autosummary/foley.html.md#foley.reciprocal_rank_fusion)(ranked_id_lists, \*[, k])    | Fuse several ranked id lists into one, by reciprocal rank.                                                                                                                     |
| [`fuse_hits`](_autosummary/foley.html.md#foley.fuse_hits)(vector_hits, keyword_hits, \*, k[, ...])  | RRF-fuse a vector ranker's hits with a keyword ranker's hits.                                                                                                                  |
| [`resolve_catid`](_autosummary/foley.html.md#foley.resolve_catid)(\*[, tags, caption, ...])             | Resolve inputs to a best UCS CatID by the staged precedence.                                                                                                                   |
| [`parse_ucs_filename`](_autosummary/foley.html.md#foley.parse_ucs_filename)(filename, \*[, table])           | Parse a UCS-conformant filename to `(ucs_category, ucs_subcategory)`.                                                                                                          |
| [`default_tagger`](_autosummary/foley.html.md#foley.default_tagger)()                                    | The default supervised tagger (PANNs CNN14; `foley[tag]`).                                                                                                                     |
| [`default_zeroshot_tagger`](_autosummary/foley.html.md#foley.default_zeroshot_tagger)([embedder])                 | The default zero-shot tagger (CLAP vs UCS subcategories; `foley[clap]`).                                                                                                       |
| [`ingest`](_autosummary/foley.html.md#foley.ingest)(path, \*[, library, backend, qc, ...])       | Ingest a folder (or single file) of sounds into the default library.                                                                                                           |
| [`restamp_rights`](_autosummary/foley.html.md#foley.restamp_rights)([library, license, ids, ...])        | Re-stamp the licence of stored sounds; a dry run unless `apply=True`.                                                                                                          |
| [`ingest_one`](_autosummary/foley.html.md#foley.ingest_one)(src, \*[, library, sound_id, ...])       | Ingest one clip into `library` and return an [`IngestResult`](_autosummary/foley.html.md#foley.IngestResult).                                                                    |
| [`ingest_folder`](_autosummary/foley.html.md#foley.ingest_folder)(path, \*[, library, recursive, ...])  | Ingest every audio file under `path` and return an [`IngestReport`](_autosummary/foley.html.md#foley.IngestReport).                                                              |
| [`bootstrap`](_autosummary/foley.html.md#foley.bootstrap)(\*[, rings, corpora, data_dir, ...])      | Seed `library` from the selected bulk corpora, returning per-corpus reports.                                                                                                   |
| [`demo`](_autosummary/foley.html.md#foley.demo)(\*[, library, query, k])                       | Ingest the bundled Ring-0 fixture and run one search — the smoke test.                                                                                                         |
| [`add_from`](_autosummary/foley.html.md#foley.add_from)(source, \*, query[, license, limit, ...])  | Search a live `source` and ingest its license-clean hits into `library`.                                                                                                       |
| [`list_sources`](_autosummary/foley.html.md#foley.list_sources)(\*[, egress_allow])                    | Return the names of registered live sources (runs discovery first).                                                                                                            |
| [`register_source`](_autosummary/foley.html.md#foley.register_source)(name, config[, adapter])            | Register a live source directly (out-of-tree plugin or a test double).                                                                                                         |
| [`generate`](_autosummary/foley.html.md#foley.generate)(prompt, \*[, backend, library, ...])       | Generate a sound effect for `prompt` and add it to the library (by-value).                                                                                                     |
| [`candidate_of`](_autosummary/foley.html.md#foley.candidate_of)(result)                                | Wrap a stored [`IngestResult`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult) as a generated candidate.                                          |
| `art50_checklist`(record)                                                                            | Return the per-clip EU AI Act Art.                                                                                                                                             |
| `scan_prompt`(prompt)                                                                                | Scan a generation `prompt` for trademarked-audio-logo + voice-clone risk.                                                                                                      |
| [`find`](_autosummary/foley.html.md#foley.find)(context, \*[, max_events, seconds, ...])       | The headline: a narrative context → verified, license-clean sound candidates.                                                                                                  |
| [`plan`](_autosummary/foley.html.md#foley.plan)(candidates, \*[, transcript])                  | Fold verified candidates into the SPARSE [`SoundDesignTimeline`](_autosummary/foley.html.md#foley.SoundDesignTimeline) (the SELECT→WEAVE bridge).                                       |
| [`decompose_context`](_autosummary/foley.html.md#foley.decompose_context)(context, \*[, max_events, ...])   | Decompose a passage into `<= max_events` sparse [`SoundEvent`](_autosummary/foley.html.md#foley.SoundEvent)s.                                                                  |
| [`refine_query`](_autosummary/foley.html.md#foley.refine_query)(query, \*[, n, hint, refiner, \_span]) | Expand `query` into up to `n` paraphrases for multi-query retrieval.                                                                                                           |
| [`verify_match`](_autosummary/foley.html.md#foley.verify_match)(event, candidate, \*[, level, ...])    | Verify `candidate` against `event` up to rung `level` (AND-confirming ladder).                                                                                                 |
| [`decide`](_autosummary/foley.html.md#foley.decide)(event, kept, verified, \*, ...)              | The single generate-vs-retrieve branch — a PURE function (report 05 §4).                                                                                                       |
| [`score`](_autosummary/foley.html.md#foley.score)(segments, \*[, audio, transcript, ...])       | Choose sounds for narration text and (optionally) weave them into the narration audio.                                                                                         |
| [`install_agent_kit`](_autosummary/foley.html.md#foley.install_agent_kit)([dest, overwrite])                | Copy the shipped skill + slash command + subagent into `dest` (a `.claude` dir).                                                                                               |
| [`mcp_server`](_autosummary/foley.html.md#foley.mcp_server)(\*[, library, session, runtime, ...])    | Build the foley MCP server (lazy `py2mcp`); registers the JSON-safe tool surface.                                                                                              |
| [`build_mcp_server`](_autosummary/foley.html.md#foley.build_mcp_server)(\*[, library, session, ...])       | Build the foley MCP server (lazy `py2mcp`); registers the JSON-safe tool surface.                                                                                              |
| [`make_http_app`](_autosummary/foley.html.md#foley.make_http_app)(\*, auth[, path, ...])                | Build a bearer-auth-gated ASGI app serving the foley MCP tools over streamable HTTP.                                                                                           |
| [`serve_http`](_autosummary/foley.html.md#foley.serve_http)(\*[, host, port, path])                  | Build and serve the foley MCP tools over authenticated streamable HTTP (blocks).                                                                                               |
| [`preview`](_autosummary/foley.html.md#foley.preview)(candidate_or_id, \*[, seconds, ...])        | Produce a short audition of a sound; set its `preview_uri` to a store key.                                                                                                     |
| [`similar_to`](_autosummary/foley.html.md#foley.similar_to)(clip_or_candidate, \*[, k, library])     | "More like this" — neighbours of a sound id / candidate, or of a raw clip.                                                                                                     |
| [`refine`](_autosummary/foley.html.md#foley.refine)([session, query, picked_ids, ...])           | Relevance-feedback refinement: expand for recall, boost picks, drop rejects, re-rank.                                                                                          |
| [`make_session_store`](_autosummary/foley.html.md#foley.make_session_store)([session_id, name, rootdir])     | Build a per-session JSON store: `Mapping[key -> dict]` under `sessions/{id}/{name}/`.                                                                                          |
| [`offline`](_autosummary/foley.html.md#foley.offline)([config])                                   | Alias of `offline_scope()` — `with foley.offline(): ...` for local-first runs.                                                                                                 |
| [`is_offline`](_autosummary/foley.html.md#foley.is_offline)()                                        | Whether an offline runtime scope is currently active.                                                                                                                          |
| [`using_source_accounts`](_autosummary/foley.html.md#foley.using_source_accounts)(\*\*accounts)                 | Bind per-request source accounts (a key, and what goes with it) for a `with` block.                                                                                            |
| [`check_requirements`](_autosummary/foley.html.md#foley.check_requirements)(\*[, names, verbose])            | Report which optional foley capabilities are available (`{name: is_available}`).                                                                                               |
| [`verify_and_setup`](_autosummary/foley.html.md#foley.verify_and_setup)(\*[, names])                       | Return a per-requirement status + guidance report (never runs an installer).                                                                                                   |
| [`capability_report`](_autosummary/foley.html.md#foley.capability_report)(\*[, runtime])                    | A JSON-safe capability + posture snapshot for the CLI, docs, and the MCP tool.                                                                                                 |
| [`evaluate`](_autosummary/foley.html.md#foley.evaluate)(\*[, golden, k])                           | Run the Tier-1 retrieval eval over the golden set ([nDCG@10](mailto:nDCG@10) / recall / mAP / MRR).                                                                            |
| [`evaluate_fit`](_autosummary/foley.html.md#foley.evaluate_fit)(\*[, golden, sample, level, ...])      | Run the Tier-2 **fit** eval over the golden set — "does the accepted clip fit?" (#10b).                                                                                        |
| [`credits`](_autosummary/foley.html.md#foley.credits)(sounds, \*[, title, only_required, ...])    | Build the TASL attribution [`Credits`](_autosummary/foley.provenance.html.md#foley.provenance.Credits) for `sounds`.                                                     |
| [`credits_for`](_autosummary/foley.html.md#foley.credits_for)(sounds, \*[, title, ...])               | Build the deduplicated [`Credits`](_autosummary/foley.html.md#foley.Credits) for the sounds used in a run.                                                                  |
| [`attribution_line`](_autosummary/foley.html.md#foley.attribution_line)(source, \*[, fmt])                 | Render one sound's TASL attribution line.                                                                                                                                      |
| [`credit_entry`](_autosummary/foley.html.md#foley.credit_entry)(record, \*[, title])                   | Build a [`CreditEntry`](_autosummary/foley.html.md#foley.CreditEntry) from a record (title override optional).                                                                  |

### Classes

| [`StorageMode`](_autosummary/foley.html.md#foley.StorageMode)(\*values)                             | How a sound's bytes are held (DERIVED from `license.cache_bytes_ok`).                                                                |
|----------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------|
| [`AcquisitionMethod`](_autosummary/foley.html.md#foley.AcquisitionMethod)(\*values)                       | How a sound entered foley (retrieval channel or origin).                                                                             |
| [`CandidateOrigin`](_autosummary/foley.html.md#foley.CandidateOrigin)(\*values)                         | Whether a candidate was retrieved from the index or freshly generated.                                                               |
| [`Salience`](_autosummary/foley.html.md#foley.Salience)(\*values)                                | How prominent a sound event is within a passage.                                                                                     |
| [`Layer`](_autosummary/foley.html.md#foley.Layer)(\*values)                                   | Mix layer (shared by `SoundEvent` now and `TimelineItem` later).                                                                     |
| [`VerifyLevel`](_autosummary/foley.html.md#foley.VerifyLevel)(\*values)                             | Which rung of the verification ladder produced a `Verdict`.                                                                          |
| [`Affordance`](_autosummary/foley.html.md#foley.Affordance)(name, type, description[, ...])        | Descriptor for a unified parameter affordance (arioso analog).                                                                       |
| [`SerializableMixin`](_autosummary/foley.html.md#foley.SerializableMixin)()                               | Adds `to_dict`/`to_json`/`from_dict`/`from_json` to a dataclass.                                                                     |
| [`LicenseRecord`](_autosummary/foley.html.md#foley.LicenseRecord)(source[, source_id, ...])           | Per-sound rights + provenance.                                                                                                       |
| [`SoundRecord`](_autosummary/foley.html.md#foley.SoundRecord)(id[, content_sha256, hash_algo, ...]) | Canonical SSOT per sound.                                                                                                            |
| [`SoundEvent`](_autosummary/foley.html.md#foley.SoundEvent)(query[, layer, diegetic, ...])         | One salient, physically-audible event decomposed from a passage.                                                                     |
| [`Verdict`](_autosummary/foley.html.md#foley.Verdict)(match, confidence[, reason, level])       | The result of one verification rung for a candidate.                                                                                 |
| [`Candidate`](_autosummary/foley.html.md#foley.Candidate)(sound[, origin, event, ...])            | A ranked, license-checked, (optionally) verified sound for one SoundEvent.                                                           |
| [`IntendedUse`](_autosummary/foley.html.md#foley.IntendedUse)([commercial, publish, ...])           | What the caller intends to do with a sound; consumed by `keep()`.                                                                    |
| [`TimelineItem`](_autosummary/foley.html.md#foley.TimelineItem)(clip_ref[, onset, gain, layer, ...]) | One placed sound on the sound-design timeline — sparse seed + resolved render fields.                                                |
| [`SoundDesignTimeline`](_autosummary/foley.html.md#foley.SoundDesignTimeline)([items, ...])                 | The editable, re-renderable sound-design plan — the SELECT→WEAVE bridge and render SSOT.                                             |
| [`Anchor`](_autosummary/foley.html.md#foley.Anchor)(\*values)                                  | How a WEAVE `Placement` binds its symbolic time to the narration (report 06 §2.4).                                                   |
| [`Placement`](_autosummary/foley.html.md#foley.Placement)([anchor, ref, onset, pre_roll, ...])    | WHERE/WHEN a clip sits — a symbolic anchor plus its resolved time (report 06 §6.3).                                                  |
| [`Processing`](_autosummary/foley.html.md#foley.Processing)([gain_db, pan, distance, ...])         | HOW a clip sounds — all optional with identity defaults (report 06 §3, §6.3).                                                        |
| [`MasterProfile`](_autosummary/foley.html.md#foley.MasterProfile)([target_lufs, true_peak_db, lra])   | Loudness master target — the delivery spec as data, not code (report 06 §5.2).                                                       |
| [`LicenseFlags`](_autosummary/foley.html.md#foley.LicenseFlags)([commercial_ok, ...])                | The eight derivable flags for one `license_id` (the table row type).                                                                 |
| [`LicenseMeta`](_autosummary/foley.html.md#foley.LicenseMeta)(display_name[, url])                  | Human-facing display metadata for one `license_id` (name + canonical URL).                                                           |
| [`QCStatus`](_autosummary/foley.html.md#foley.QCStatus)(\*values)                                | Overall verdict for a clip (subclasses `str` so it is JSON-safe).                                                                    |
| [`QCThresholds`](_autosummary/foley.html.md#foley.QCThresholds)([clip_full_scale, ...])              | All Tier-0 QC defaults (report 08 §3 table), each an explicit field.                                                                 |
| [`QCReport`](_autosummary/foley.html.md#foley.QCReport)(duration_s, sample_rate, channels, ...)  | Per-clip Tier-0 QC result.                                                                                                           |
| [`SoundLibrary`](_autosummary/foley.html.md#foley.SoundLibrary)(\*[, sounds, meta, vindex, ...])     | A searchable, license-aware library of sounds (the foley INDEX façade).                                                              |
| [`Embedder`](_autosummary/foley.html.md#foley.Embedder)(\*args, \*\*kwargs)                      | A joint text<->audio embedding space (CLAP by default).                                                                              |
| [`ClapEmbedder`](_autosummary/foley.html.md#foley.ClapEmbedder)([model_id, device])                  | LAION-CLAP text<->audio embedder (the default retrieval engine).                                                                     |
| [`VectorIndex`](_autosummary/foley.html.md#foley.VectorIndex)(\*args, \*\*kwargs)                   | Approximate-nearest-neighbour store over embedding vectors.                                                                          |
| [`KeywordIndex`](_autosummary/foley.html.md#foley.KeywordIndex)(\*args, \*\*kwargs)                  | BM25 / full-text index over each sound's tags + caption.                                                                             |
| [`MemoryIndex`](_autosummary/foley.html.md#foley.MemoryIndex)(\*[, dim])                            | In-memory vector + keyword index (numpy cosine + compact BM25).                                                                      |
| [`LanceIndex`](_autosummary/foley.html.md#foley.LanceIndex)(\*, uri, dim[, table_name])            | LanceDB-backed index: one table with a vector column + a native FTS index.                                                           |
| [`SqliteVecIndex`](_autosummary/foley.html.md#foley.SqliteVecIndex)(\*, path, dim)                     | Single-file index: sqlite-vec `vec0` KNN + stdlib FTS5 keyword search.                                                               |
| [`FusedHit`](_autosummary/foley.html.md#foley.FusedHit)(id[, rrf_score, clap_score, bm25_score]) | One fused retrieval hit: an id plus the scores that produced it.                                                                     |
| [`CatIdResolution`](_autosummary/foley.html.md#foley.CatIdResolution)([catid, category, ...])           | The result of resolving free tags/caption/labels to a UCS CatID.                                                                     |
| [`Tagger`](_autosummary/foley.html.md#foley.Tagger)(\*args, \*\*kwargs)                        | Map a clip to `(label, score)` pairs against a label vocabulary.                                                                     |
| [`Captioner`](_autosummary/foley.html.md#foley.Captioner)(\*args, \*\*kwargs)                     | Produce one natural-language sentence describing a clip (report 03).                                                                 |
| [`ClapZeroShotTagger`](_autosummary/foley.html.md#foley.ClapZeroShotTagger)(\*[, embedder, labels, ...])   | Zero-shot tagger: score a clip against a label set via CLAP cosine.                                                                  |
| [`PannsTagger`](_autosummary/foley.html.md#foley.PannsTagger)(\*[, device, threshold])              | PANNs CNN14 supervised tagger over the 527 AudioSet classes (`foley[tag]`).                                                          |
| [`IngestResult`](_autosummary/foley.html.md#foley.IngestResult)(id, status[, record, qc, ...])       | The outcome of ingesting one clip.                                                                                                   |
| [`IngestReport`](_autosummary/foley.html.md#foley.IngestReport)(root[, results, notes])              | The rolled-up outcome of a folder ingest (JSON-serializable).                                                                        |
| [`RunManifest`](_autosummary/foley.html.md#foley.RunManifest)(run_id, op[, created_at, ...])        | The reproducible run-artifact for one foley operation (trace ⊕ plan ⊕ provenance).                                                   |
| [`SpanRecord`](_autosummary/foley.html.md#foley.SpanRecord)(name, span_id[, parent_id, kind, ...]) | One node of the run's span tree (the trace half of the artifact).                                                                    |
| [`Judge`](_autosummary/foley.html.md#foley.Judge)(\*args, \*\*kwargs)                         | One rung of the verify ladder: does this candidate match this event? (report 10 §4.2).                                               |
| [`Budget`](_autosummary/foley.html.md#foley.Budget)([max_refine_loops, max_generations, ...])  | Bounded-cost accounting: per-event loop counts, and a cumulative dollar cap (#57).                                                   |
| [`Decision`](_autosummary/foley.html.md#foley.Decision)(action[, candidate, reason])             | The tiny result of [`decide()`](_autosummary/foley.html.md#foley.decide); `reason` feeds the refine hint + the audit Step.       |
| [`WeaveResult`](_autosummary/foley.html.md#foley.WeaveResult)(audio, sr, timeline, credits, ...)    | The output of `weave()` — the v1 Definition-of-Done deliverable.                                                                     |
| [`ScoreResult`](_autosummary/foley.html.md#foley.ScoreResult)(timeline, events[, weave])            | The output of [`score()`](_autosummary/foley.html.md#foley.score) — the editable plan + per-event rationale (+ mix when woven). |
| [`ScoredEvent`](_autosummary/foley.html.md#foley.ScoredEvent)(segment, query, sound_id, ...)        | One chosen sound placed for a narration event (a JSON-friendly rationale row).                                                       |
| [`SessionStore`](_autosummary/foley.html.md#foley.SessionStore)([session_id, candidates, ...])       | Three namespaced stores for one audition session (candidates / picks / rejects).                                                     |
| [`RuntimeConfig`](_autosummary/foley.html.md#foley.RuntimeConfig)([offline, data_egress_allow, ...])  | A frozen runtime posture — the local-first / offline contract as data.                                                               |
| [`Credits`](_autosummary/foley.html.md#foley.Credits)([entries, title, schema_version])         | A deduplicated, ordered collection of [`CreditEntry`](_autosummary/foley.html.md#foley.CreditEntry) for one run.                      |
| [`CreditEntry`](_autosummary/foley.html.md#foley.CreditEntry)(sound_id[, title, author, ...])       | One rendered TASL credit for a single sound (a flat, serializable row).                                                              |

### Exceptions

| [`GenerationError`](_autosummary/foley.html.md#foley.GenerationError)(message, \*, report, status)     | Raised by [`foley.generate()`](_autosummary/foley.html.md#foley.generate) when a backend yields no stored sound.                       |
|---------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------|
| [`SafetyRefusal`](_autosummary/foley.html.md#foley.SafetyRefusal)(message, \*, hits, report)         | Raised (fail-closed) when a generation prompt trips a #9b safety gate.                                                                   |
| [`TrademarkRefusal`](_autosummary/foley.html.md#foley.TrademarkRefusal)(message, \*, hits, report)      | A [`SafetyRefusal`](_autosummary/foley.html.md#foley.SafetyRefusal) for a prompt naming a trademarked audio logo (report 07 §7.2).          |
| [`RecognizableVoiceRefusal`](_autosummary/foley.html.md#foley.RecognizableVoiceRefusal)(message, \*, hits, ...) | A [`SafetyRefusal`](_autosummary/foley.html.md#foley.SafetyRefusal) for a prompt requesting a recognizable / cloned voice (report 07 §7.1). |

### *class* foley.AcquisitionMethod(\*values)

Bases: [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Enum`](https://docs.python.org/3/library/enum.html#enum.Enum)

How a sound entered foley (retrieval channel or origin).

### *class* foley.Affordance(name, type, description, default=None, stage='query', carries_meaning=False)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Descriptor for a unified parameter affordance (arioso analog).

#### name

Canonical parameter name used at the façade level.

#### type

Expected Python type.

#### description

Human-readable description.

#### default

Default value (`None` = no default / required).

#### stage

`'query'` (search/find/filter) or `'generate'`.

#### carries_meaning

Dropping it would change what the caller gets (a seed they
will rely on to reproduce, content they asked to exclude), so a backend
that cannot honour it raises instead of dropping it (#53).

### *class* foley.Anchor(\*values)

Bases: [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Enum`](https://docs.python.org/3/library/enum.html#enum.Enum)

How a WEAVE `Placement` binds its symbolic time to the narration (report 06 §2.4).

`absolute` is a fixed offset; the rest resolve against the forced-aligned
`word_timeline` — `word` to a trigger word’s onset, `sentence` across a
sentence span, `scene`/`paragraph` to a boundary’s first spoken word.

### *class* foley.Budget(max_refine_loops=1, max_generations=1, allow_generate=True, max_usd=1.0, approve_unknown_cost=None, \_refines=0, \_gens=0, \_spent_usd=0.0, \_unknown_calls=0, \_lock=<factory>)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Bounded-cost accounting: per-event loop counts, and a cumulative dollar cap (#57).

Two kinds of bound:

* **Per event** — `max_refine_loops` / `max_generations`: the loop calls
  [`refine_ok()`](_autosummary/foley.html.md#foley.Budget.refine_ok) / [`gen_ok()`](_autosummary/foley.html.md#foley.Budget.gen_ok) to test, then [`spend_refine()`](_autosummary/foley.html.md#foley.Budget.spend_refine) /
  [`spend_gen()`](_autosummary/foley.html.md#foley.Budget.spend_gen) to charge; [`reset()`](_autosummary/foley.html.md#foley.Budget.reset) zeroes them at each event.
* **Per run, cumulative** — `max_usd` (default $1): every paid call (generation,
  paid LLM rung) is checked with [`authorize()`](_autosummary/foley.html.md#foley.Budget.authorize) before it is made and recorded with [`charge()`](_autosummary/foley.html.md#foley.Budget.charge)
  after. [`reset()`](_autosummary/foley.html.md#foley.Budget.reset) never clears it, so the cap holds across the whole
  `find` / `score` run. A call whose cost is unknown (estimate `None`) is
  refused unless `approve_unknown_cost` (or `$FOLEY_APPROVE_UNKNOWN_COST=1`).

#### authorize(estimate_usd, , what)

Alias of [`check()`](_autosummary/foley.html.md#foley.Budget.check) (kept for callers that only test affordability).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### charge(usd)

Record a call’s cost (`None` = unknown: counted, not summed).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### check(estimate_usd, , what)

Raise if this run cannot afford `estimate_usd` (nothing is reserved).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### gen_ok()

Whether a generation fallback is allowed and within budget.

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

#### refine_ok()

Whether another refine→re-retrieve pass is within budget.

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

#### reserve(estimate_usd, , what)

Check, then count `estimate_usd` as spent at once (atomic under a lock).

Reserving before the call is what keeps concurrent calls on one budget (MCP
tools run in a threadpool) from all passing the check before any is charged.
[`settle()`](_autosummary/foley.html.md#foley.Budget.settle) later replaces the reservation with the actual cost, if known.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### reset()

Zero the spend counters so the caps apply *per event*, not per passage.

The `find` loop calls this at the top of each event so one hard event’s
refine/generate spend never starves later events (the documented per-event
semantics).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### settle(reserved_usd, actual_usd)

Replace a reservation with the actual cost (`None` actual: keep the reservation).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### spend_gen()

Charge one generation.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### spend_refine()

Charge one refine loop.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### *property* spent_usd *: [float](https://docs.python.org/3/builtins/functions.html#float)*

Dollars charged so far this run (calls of unknown cost count as 0 here).

### *class* foley.Candidate(sound, origin=CandidateOrigin.retrieved, event=None, clap_score=None, bm25_score=None, rrf_score=None, rerank_score=None, verdict=None, license_ok=None, preview_uri=None, notes=<factory>, cost_estimate_usd=None, cost_actual_usd=None)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

A ranked, license-checked, (optionally) verified sound for one SoundEvent.

Retrieval and generation return the SAME shape; only `origin` differs.
Nested `sound` / `event` / `verdict` dataclasses are decoded generically
by `_decode()` — no per-field code needed.

### *class* foley.CandidateOrigin(\*values)

Bases: [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Enum`](https://docs.python.org/3/library/enum.html#enum.Enum)

Whether a candidate was retrieved from the index or freshly generated.

### *class* foley.Captioner(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

Produce one natural-language sentence describing a clip (report 03).

The caption feeds the BM25 keyword index and human display. Default is a
dedicated AAC model (EnCLAP); Qwen2-Audio is a richer promptable upgrade.
Both are `foley[caption]` adapters plugged in behind this protocol.

#### caption(wav, sr)

Return a one-sentence caption for the clip.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### *class* foley.CatIdResolution(catid=None, category=None, subcategory=None, source=None, confidence=0.0, matched_terms=<factory>)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

The result of resolving free tags/caption/labels to a UCS CatID.

`catid` feeds `ucs_category` and
`subcategory` feeds `ucs_subcategory` on
ingest, and `ucs_catid` on the query side.

### *class* foley.ClapEmbedder(model_id='laion/larger_clap_general', , device=None)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

LAION-CLAP text<->audio embedder (the default retrieval engine).

Returns **L2-normalized** `float32` embeddings so a plain inner product is
cosine similarity. `embed_text` always returns a 2-D `(n, dim)` array;
`embed_audio` returns a 1-D `(dim,)` array for one clip.

#### model_id

The HF checkpoint id.

#### dim

The embedding dimensionality.

#### open_source

Whether the checkpoint is an open-source model (the LAION
releases are). Read by the ingest AI-use gate for sounds whose rights
holder allows only open-source models (#69).

#### *property* device *: [str](https://docs.python.org/3/builtins/stdtypes.html#str)*

The resolved torch device string (`'cuda'`/`'cpu'`).

#### *property* dim *: [int](https://docs.python.org/3/builtins/functions.html#int)*

The embedding dimensionality (512 for the default; resolved for others).

For a non-default checkpoint this fetches only the model’s `config.json`
(via `AutoConfig`) — never the ~1.7 GB weights — so building an index
for it does not force a model download. Falls back to the loaded model’s
config if the standalone config lacks `projection_dim`.

#### embed_audio(wav, sr)

Embed one audio clip -> `(dim,)` L2-normalized.

The clip is down-mixed to mono and resampled to 48 kHz (what CLAP expects)
via [`foley.audio`](_autosummary/foley.audio.html.md#module-foley.audio) before embedding.

* **Parameters:**
  * **wav** (`ndarray`) – A working-array clip (`float32`; mono or multichannel).
  * **sr** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The clip’s sample rate in Hz.
* **Return type:**
  `ndarray`

#### embed_text(text)

Embed one or more query strings -> `(n_texts, dim)` L2-normalized.

* **Return type:**
  `ndarray`

### *class* foley.ClapZeroShotTagger(, embedder=None, labels=None, prompt='this is a sound of {label}', threshold=0.0)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Zero-shot tagger: score a clip against a label set via CLAP cosine.

Reuses a [`ClapEmbedder`](_autosummary/foley.index.embedders.html.md#foley.index.embedders.ClapEmbedder) (the same model the
Index uses), so the audio is embedded in the same joint space as the label
prompts and no extra weights load. The default label set is the UCS
subcategory names (foley’s own vocabulary), so tags land in-taxonomy.

#### *property* embedder

The CLAP embedder (injected or the process-wide default).

#### *property* labels *: [list](https://docs.python.org/3/builtins/stdtypes.html#list)[[str](https://docs.python.org/3/builtins/stdtypes.html#str)]*

natural UCS `category subcategory` phrases).

Natural multi-word phrases (`"weather rain"`, `"glass break"`) are far
better CLAP prompts — and better BM25 tags / taxonomy-resolver input — than
bare abstract subcategory words (`"Break"`, `"Buzz"`), which are a known
zero-shot artifact (anomalously close to everything). Absolute tag-quality
calibration (thresholds, label curation) is an eval-harness concern (#10).

* **Type:**
  The label vocabulary ([*default*](_autosummary/foley.html.md#foley.Affordance.default)

#### tag(wav, sr, , taxonomy='custom', top_k=10)

Return the top-`k` `(label, cosine)` tags for the clip, best first.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

#### tag_vector(audio_vec, , top_k=10)

Score a **precomputed** (L2-normalized) audio vector against the labels.

The efficiency seam (report 03 Part 2): the Index already embeds every
sound with this model, so on ingest the retrieval vector is reused here —
no second CLAP forward pass.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

### *class* foley.CreditEntry(sound_id, title=None, author=None, author_url=None, source=None, source_url=None, license_id='unknown', license_name=None, license_url=None, modified=False, requires_attribution=False, attribution_text=None, notice_text_required=None, is_ai_generated=False, generator_model=None, disclosure_recommended=False, watermark=None, c2pa_manifest_ref=None)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

One rendered TASL credit for a single sound (a flat, serializable row).

Built by [`credit_entry()`](_autosummary/foley.html.md#foley.credit_entry) from a record’s rights fields; rendered to a
single attribution line by [`attribution_line()`](_autosummary/foley.html.md#foley.attribution_line). Carries the AI-disclosure

+ watermark / C2PA fields as pass-throughs so the JSON manifest becomes the
  content-credentials carrier once #6/#9b populate them (`None` today).

### *class* foley.Credits(entries=(), title='Credits', schema_version=1)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

A deduplicated, ordered collection of [`CreditEntry`](_autosummary/foley.html.md#foley.CreditEntry) for one run.

Iterable and sized; renders to `CREDITS.md` via [`markdown`](_autosummary/foley.html.md#foley.Credits.markdown) and to a
JSON manifest via [`manifest`](_autosummary/foley.html.md#foley.Credits.manifest) (== `to_dict()`). Both are deterministic
(no timestamps) and diffable.

#### *property* manifest *: [dict](https://docs.python.org/3/builtins/stdtypes.html#dict)*

The machine-readable JSON manifest (a plain dict).

#### *property* markdown *: [str](https://docs.python.org/3/builtins/stdtypes.html#str)*

The rendered `CREDITS.md` document.

### *class* foley.Decision(action, candidate=None, reason='')

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

The tiny result of [`decide()`](_autosummary/foley.html.md#foley.decide); `reason` feeds the refine hint + the audit Step.

### *class* foley.Embedder(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

A joint text<->audio embedding space (CLAP by default).

One space serves both text->audio search (`embed_text` a query) and
audio<->audio similarity (`embed_audio` a clip). Implementations MUST
return **L2-normalized** `float32` arrays so a plain inner product is
cosine similarity, and MUST stamp `model_id`/`dim` so mixed-model
libraries stay coherent (each [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord) records the
`embedding_model`/`embedding_dim` it was indexed under).

#### model_id

The checkpoint id (e.g. `'laion/larger_clap_general'`).

#### dim

The embedding dimensionality (e.g. `512`).

#### embed_audio(wav, sr)

Embed one audio clip.

* **Parameters:**
  * **wav** (`ndarray`) – A working-array clip (`float32`, mono preferred). CLAP expects
    48 kHz; implementations resample as needed.
  * **sr** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The clip’s sample rate in Hz.
* **Return type:**
  `ndarray`
* **Returns:**
  A 1-D `(dim,)` L2-normalized `float32` array.

#### embed_text(text)

Embed one or more query strings.

* **Parameters:**
  **text** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]) – A single string or a list of strings.
* **Return type:**
  `ndarray`
* **Returns:**
  A 2-D `(n_texts, dim)` L2-normalized `float32` array (`n_texts`
  is `1` for a single string) — always 2-D so callers can index
  `[0]` for the single-query case.

### *class* foley.FusedHit(id, rrf_score=None, clap_score=None, bm25_score=None)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

One fused retrieval hit: an id plus the scores that produced it.

The raw component scores are carried through (not just the fused rank score)
so the façade can stamp them onto a [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)
(`clap_score` / `bm25_score` / `rrf_score`) for display and debugging.

#### id

The sound id.

#### rrf_score

The fused RRF score (`None` for a pure-vector search).

#### clap_score

Cosine similarity from the vector ranker (`None` if the id
appeared only in the keyword list).

#### bm25_score

BM25 score from the keyword ranker (`None` if the id
appeared only in the vector list).

### *exception* foley.GenerationError(message, , report, status)

Bases: [`RuntimeError`](https://docs.python.org/3/builtins/exceptions.html#RuntimeError)

Raised by [`foley.generate()`](_autosummary/foley.html.md#foley.generate) when a backend yields no stored sound.

Carries the full [`IngestReport`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestReport) and the terminal
[`status`](_autosummary/foley.html.md#foley.GenerationError.status) so a caller can react distinctly to `quarantined` (QC-rejected —
e.g. regenerate), `rights_blocked`, or `error`. The lower-level
[`generate()`](_autosummary/foley.html.md#foley.generate) workhorse never raises this — it always returns an inspectable
report; only the public [`foley.generate()`](_autosummary/foley.html.md#foley.generate) promise raises.

#### report

The [`IngestReport`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestReport) from the run.

#### status

The terminal [`IngestResult`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult) status
(`'quarantined'` | `'rights_blocked'` | `'error'` | …).

### *class* foley.IngestReport(root, results=<factory>, notes=<factory>)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

The rolled-up outcome of a folder ingest (JSON-serializable).

#### error(path, exc)

Record a per-file error without aborting the run.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### *property* errored *: [list](https://docs.python.org/3/builtins/stdtypes.html#list)[[IngestResult](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult)]*

Results that raised during ingest.

#### exception *= None*

The exception behind the last `error` result, kept so a raising façade can
chain it (`raise ... from`); a plain attribute, never serialized.

#### *property* ingested *: [list](https://docs.python.org/3/builtins/stdtypes.html#list)[[IngestResult](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult)]*

Results that were added to the library (`pass` or `warn`).

#### *property* quarantined *: [list](https://docs.python.org/3/builtins/stdtypes.html#list)[[IngestResult](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult)]*

Results rejected by the QC gate.

#### record(result)

Append one [`IngestResult`](_autosummary/foley.html.md#foley.IngestResult).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### *property* rights_blocked *: [list](https://docs.python.org/3/builtins/stdtypes.html#list)[[IngestResult](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult)]*

Results refused by the fail-closed AI-training/license rights gate.

#### *property* skipped *: [list](https://docs.python.org/3/builtins/stdtypes.html#list)[[IngestResult](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult)]*

Results skipped as content-addressed duplicates.

#### summary()

A counts dict for a console/CLI summary.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### *class* foley.IngestResult(id, status, record=None, qc=None, notes=<factory>, error=None, cost_estimate_usd=None, cost_actual_usd=None)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

The outcome of ingesting one clip.

`status`: `'pass'`/`'warn'` (ingested), `'quarantined'` (QC-rejected,
not added), `'skipped_dup'` (content already in the library),
`'rights_blocked'` (license forbids AI training / embedding, refused before
embed — see [`ingest_one()`](_autosummary/foley.html.md#foley.ingest_one)), `'skipped_license'` (dropped by a
bootstrap commercial-use / fail-closed license filter), or `'error'`.
`record` is present only when the clip was ingested.

### *class* foley.IntendedUse(commercial=True, publish=True, redistribute_standalone=False, will_train=False, can_attribute=True, revenue_usd=0, allow_voice_or_trademark=False)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

What the caller intends to do with a sound; consumed by `keep()`.

### *class* foley.Judge(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

One rung of the verify ladder: does this candidate match this event? (report 10 §4.2).

`level` selects the rung — `clap` (cheap score gate),
`listen` (audio-LM), `judge` (LLM arbitration + scene consistency). The
returned [`Verdict`](_autosummary/foley.html.md#foley.Verdict) carries `level` == the rung that produced it. Only the
`judge` rung’s real impl calls the LLM.

### *class* foley.KeywordIndex(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

BM25 / full-text index over each sound’s tags + caption.

The default is LanceDB’s Tantivy FTS (report 04 §3.4); SQLite FTS5 is the
single-file fallback. Same `where` push-down contract as
[`VectorIndex`](_autosummary/foley.html.md#foley.VectorIndex).

#### bm25(query, k, , where=None)

Return the top-`k` BM25 matches for `query`, best first.

* **Parameters:**
  * **query** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – A natural-language / keyword query.
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Number of matches to return.
  * **where** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Optional metadata predicates for push-down filtering.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]
* **Returns:**
  `[(id, bm25_score), ...]` in descending-score order.

#### index(id, text, meta)

Insert or replace the searchable text (and light metadata) for `id`.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### *class* foley.LanceIndex(, uri, dim, table_name='sounds')

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

LanceDB-backed index: one table with a vector column + a native FTS index.

Writes are staged per id and flushed on the next read (or explicit
[`commit()`](_autosummary/foley.html.md#foley.LanceIndex.commit)), so a sound’s vector ([`upsert()`](_autosummary/foley.html.md#foley.LanceIndex.upsert)) and text ([`index()`](_autosummary/foley.index.html.md#module-foley.index))
— which arrive as two separate protocol calls — are merged into one row and
written as an efficient batch. Vector search is exact cosine (no ANN index is
built at this tier; adding one is a scale-time optimization). Fusion is done
by [`foley.index.search`](_autosummary/foley.index.search.html.md#module-foley.index.search), not by LanceDB’s native hybrid reranker, so
ranking matches every other backend.

One-table constraint: the vector column is mandatory, so \*\*every indexed row
needs a vector\*\*. `SoundLibrary.add` enforces this (it raises without an
embedding source), so the façade path is safe; a bare `index()` with no
matching `upsert()` stages a text-only row that stays unflushed (never
keyword-searchable). For a keyword-only library with no embeddings, use
[`MemoryIndex`](_autosummary/foley.html.md#foley.MemoryIndex) or [`SqliteVecIndex`](_autosummary/foley.html.md#foley.SqliteVecIndex) (independent vector/text tables).

#### bm25(query, k, , where=None)

Native full-text (BM25) search; returns `[(id, score), ...]`.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

#### commit()

Flush all staged writes to the LanceDB table.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### *property* db

The lazily-connected LanceDB database handle.

#### get_vector(id)

Return the stored vector for `id` (staged or persisted), else `None`.

* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[`ndarray`]

#### index(id, text, meta)

Stage the searchable text for `id` (flushed on the next read/commit).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### knn(vector, k, , where=None)

Exact cosine KNN; returns `[(id, cosine_similarity), ...]`.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

#### *property* table

The lazily-opened (or created-empty) LanceDB table.

#### upsert(id, vector, meta)

Stage the vector for `id` (flushed on the next read/commit).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### *class* foley.Layer(\*values)

Bases: [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Enum`](https://docs.python.org/3/library/enum.html#enum.Enum)

Mix layer (shared by `SoundEvent` now and `TimelineItem` later).

### *class* foley.LicenseFlags(commercial_ok=False, embed_in_derivative_ok=False, redistribute_standalone_ok=False, cache_bytes_ok=False, modification_ok=False, ai_training_ok=False, requires_attribution=False, revenue_cap_usd=None)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

The eight derivable flags for one `license_id` (the table row type).

### *class* foley.LicenseMeta(display_name, url=None)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Human-facing display metadata for one `license_id` (name + canonical URL).

The presentation sibling of [`LicenseFlags`](_autosummary/foley.html.md#foley.LicenseFlags): where `LicenseFlags` holds
the *permission* row consulted by [`keep()`](_autosummary/foley.html.md#foley.keep), `LicenseMeta` holds the
*display* row consulted by the credits/attribution layer
([`foley.provenance.credits`](_autosummary/foley.provenance.credits.html.md#module-foley.provenance.credits)). Kept here so `licensing` stays the single
license authority; a record’s own `license_name` / `license_url` (when a
source populated them) take precedence over this default.

### *class* foley.LicenseRecord(source, source_id=None, source_url=None, acquisition_method=AcquisitionMethod.user, retrieved_at=None, adapter_version=None, content_sha256=None, license_id='unknown', license_name=None, license_version=None, license_url=None, rights_holder=None, creator_name=None, creator_url=None, commercial_ok=False, embed_in_derivative_ok=True, redistribute_standalone_ok=False, cache_bytes_ok=False, modification_ok=False, ai_training_ok=False, ai_training_scope=None, gen_ai_preference=None, revenue_cap_usd=None, requires_attribution=False, attribution_text=None, notice_text_required=None, transformations=<factory>, is_ai_generated=False, generator_model=None, generator_version=None, generation_prompt=None, generation_seed=None, generation_params=<factory>, watermark=None, c2pa_manifest_ref=None, contains_recognizable_voice=False, potential_trademark=False, disclosure_recommended=False, rights_verified=False, verified_at=None, schema_version=1)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

Per-sound rights + provenance. SSOT for BOTH `keep()` and storage mode.

The derived permission flags default fail-closed here (the bare-record
baseline) — with one deliberate exception: `embed_in_derivative_ok`
defaults `True` (the normal case for a licensed sound). That is not a live
bypass: `keep()` checks `rights_verified` first, so an unverified record
is rejected regardless. Populate the flags from the `license_id` via
`foley.licensing.apply_license_flags` (source overrides win). Never
hand-set the derived flags — always route through the policy layer.

### *class* foley.MasterProfile(target_lufs=-16.0, true_peak_db=-1.0, lra=11.0)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

Loudness master target — the delivery spec as data, not code (report 06 §5.2).

### *class* foley.MemoryIndex(, dim=None)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

In-memory vector + keyword index (numpy cosine + compact BM25).

Not persistent — everything lives in dicts, lost on process exit. It exists
so the full hybrid + façade path is testable and usable with only `numpy`
(no LanceDB, no torch), and as a genuine zero-config tier for small or
ephemeral libraries. The vector and text stores are independent dicts, so
[`upsert()`](_autosummary/foley.html.md#foley.MemoryIndex.upsert) and [`index()`](_autosummary/foley.index.html.md#module-foley.index) never contend.

#### bm25(query, k, , where=None)

Return the top-`k` BM25 matches for `query` (best first).

A compact Okapi BM25 (`k1=1.5`, `b=0.75`) recomputed per query — O(N)
in the corpus size, which is fine for the in-memory tier’s scale.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

#### commit()

No-op (writes are immediate); present for interface symmetry.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### get_vector(id)

Return the stored vector for `id` (or `None`).

* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[`ndarray`]

#### index(id, text, meta)

Insert or replace the searchable text (and light metadata) for `id`.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### knn(vector, k, , where=None)

Return the `k` cosine-nearest ids to `vector` (most-similar first).

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

#### upsert(id, vector, meta)

Insert or replace the vector (and light metadata) for `id`.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### *class* foley.PannsTagger(, device='cpu', threshold=0.1)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

PANNs CNN14 supervised tagger over the 527 AudioSet classes (`foley[tag]`).

The checkpoint auto-downloads to `~/panns_data` (~327 MB) on the first
[`tag()`](_autosummary/foley.html.md#foley.PannsTagger.tag). PANNs expects 32 kHz mono; the clip is resampled via
[`foley.audio.to_working()`](_autosummary/foley.audio.html.md#foley.audio.to_working).

#### tag(wav, sr, , taxonomy='audioset', top_k=10)

Return the top-`k` `(AudioSet label, score)` tags, best first.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

### *class* foley.Placement(anchor=Anchor.absolute, ref=None, onset=0.0, pre_roll=0.0, duration=None, loop=False)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

WHERE/WHEN a clip sits — a symbolic anchor plus its resolved time (report 06 §6.3).

Filled by WEAVE’s aligner+anchor pass. `onset` is the resolved start in
seconds (distinct from the sparse `TimelineItem.onset` symbolic string);
`pre_roll` shifts the clip earlier so its salient transient — not its file
start — lands on the anchor (report 06 §2.4).

### *class* foley.Processing(gain_db=0.0, pan=0.0, distance=0.0, reverb_send=0.0, fade_in=0.008, fade_out=0.012, duck_bed=False)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

HOW a clip sounds — all optional with identity defaults (report 06 §3, §6.3).

Every field is a no-op at its default, so a sparse item (no `processing`)
renders untouched; the mixer departs from dry/centered/full-level only when a
field is set.

### *class* foley.QCReport(duration_s, sample_rate, channels, clipped_ratio, clipped_max_run, dc_offset, rms_dbfs, is_silent, needs_edge_fade, has_nan_inf, true_peak_dbtp=None, snr_db=None, loudness_lufs=None, status=QCStatus.pass_, notes=<factory>)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Per-clip Tier-0 QC result.

Mirrors the fields the `SoundRecord` schema already carries
(`duration_s`, `sample_rate`, `channels`, `loudness_lufs`) plus the
deterministic check outputs, an overall `status`, and human-readable
`notes` for every firing condition. Serialize with [`to_dict()`](_autosummary/foley.html.md#foley.QCReport.to_dict) into
`SoundRecord.qc`.

#### to_dict()

Return a plain, JSON-safe dict (`status` as its string value).

Non-finite floats are swept to `None` at this boundary too (belt-and-
suspenders over the construction-time `_json_safe()` guard) so no
`Infinity`/`NaN` can ever reach `json.dumps` regardless of who set a
field.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### *class* foley.QCStatus(\*values)

Bases: [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Enum`](https://docs.python.org/3/library/enum.html#enum.Enum)

Overall verdict for a clip (subclasses `str` so it is JSON-safe).

### *class* foley.QCThresholds(clip_full_scale=0.999, clip_min_run=3, clip_reject_ratio=0.0001, clip_reject_run=10, true_peak_max_dbtp=-1.0, true_peak_oversample=4, dc_offset_fail=0.01, dc_offset_warn=0.001, silence_rms_dbfs=-60.0, snr_clean_db=20.0, snr_quiet_percentile=10.0, snr_frame_s=0.025, snr_hop_s=0.01, edge_rel_peak_dbfs=-40.0, edge_fade_s=0.01, lufs_gate_floor=-70.0, lufs_outlier_lu=6.0, duration_min_s=0.1, deliver_min_sample_rate=44100)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

All Tier-0 QC defaults (report 08 §3 table), each an explicit field.

Grouped by check. Pass a customized instance to [`run_qc()`](_autosummary/foley.html.md#foley.run_qc) (or the
per-check keyword arguments) to override any threshold without editing code.

### *exception* foley.RecognizableVoiceRefusal(message, , hits, report)

Bases: [`SafetyRefusal`](_autosummary/foley.sources.html.md#foley.sources.SafetyRefusal)

A [`SafetyRefusal`](_autosummary/foley.html.md#foley.SafetyRefusal) for a prompt requesting a recognizable / cloned voice (report 07 §7.1).

### *class* foley.RunManifest(run_id, op, created_at=None, foley_version=None, inputs=<factory>, params=<factory>, spans=<factory>, steps=<factory>, ingest_report=None, result_ids=<factory>, candidate_scores=<factory>, credits_ref=None, disclosure_refs=<factory>, seeds=<factory>, plan_ref=None, trace_ref=None, status='ok', error=None, schema_version=1)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

The reproducible run-artifact for one foley operation (trace ⊕ plan ⊕ provenance).

Persisted by `emit_run_manifest()` into a run store keyed by `run_id`.
Sensitive prompt/query text lives redacted (see [`foley.obs.redact`](_autosummary/foley.obs.redact.html.md#module-foley.obs.redact)); chosen
clips are held by reference (`SoundRecord` id) so the manifest stays light and a
trace can be replayed against a fresh index.

### *class* foley.RuntimeConfig(offline=False, data_egress_allow=<factory>, telemetry=True, redaction_mode='hash', http_resilience=True)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

A frozen runtime posture — the local-first / offline contract as data.

* **Parameters:**
  * **offline** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Whether this posture is offline/local-first.
  * **data_egress_allow** ([`frozenset`](https://docs.python.org/3/builtins/stdtypes.html#frozenset)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – The egress classes a source may use to be available
    (`{'local'}` offline; `{'local','external'}` online).
  * **telemetry** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Whether the obs run-artifact export is on.
  * **redaction_mode** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – `'hash'` (default, salted), `'off'` (drop), or `'full'`
    (raw — local-debug only).
  * **http_resilience** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Whether HTTP source adapters are wrapped with the
    throttle/backoff/circuit-breaker ([`foley.sources.resilience`](_autosummary/foley.sources.resilience.html.md#module-foley.sources.resilience)).

#### allows(data_egress)

Whether a source declaring `data_egress` is available under this posture.

An unknown/absent declaration is **rejected** (fail-closed): a source that does
not say where its data goes is never used offline.

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

#### *classmethod* default()

The online default: all egress allowed, telemetry on, hashed redaction.

* **Return type:**
  [`RuntimeConfig`](_autosummary/foley.runtime.html.md#foley.runtime.RuntimeConfig)

#### *classmethod* from_env()

Build from the environment: `FOLEY_OFFLINE` in {1,true,yes} → offline-local.

* **Return type:**
  [`RuntimeConfig`](_autosummary/foley.runtime.html.md#foley.runtime.RuntimeConfig)

#### *classmethod* offline_local()

The local-first offline posture: local-only egress, telemetry off, hashed redaction.

* **Return type:**
  [`RuntimeConfig`](_autosummary/foley.runtime.html.md#foley.runtime.RuntimeConfig)

### *exception* foley.SafetyRefusal(message, , hits, report)

Bases: [`GenerationError`](_autosummary/foley.sources.html.md#foley.sources.GenerationError)

Raised (fail-closed) when a generation prompt trips a #9b safety gate.

A pre-synthesis hard stop: nothing is generated or stored. Distinct from a
resilience `error` result (a backend/ingest failure the workhorse records
without raising) — a safety refusal is a deliberate refusal of an unsafe
request. Its `report` is an empty pre-generation report and `status` is
`'refused'`. Carries `hits` (the matched marks/patterns). Subclass of
[`GenerationError`](_autosummary/foley.html.md#foley.GenerationError) so the public [`foley.generate()`](_autosummary/foley.html.md#foley.generate) `Raises` clause
already covers it. See [`foley.provenance.disclosure.scan_prompt()`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.scan_prompt).

### *class* foley.Salience(\*values)

Bases: [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Enum`](https://docs.python.org/3/library/enum.html#enum.Enum)

How prominent a sound event is within a passage.

### *class* foley.ScoreResult(timeline, events, weave=None)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

The output of [`score()`](_autosummary/foley.html.md#foley.score) — the editable plan + per-event rationale (+ mix when woven).

#### *property* n_sounds *: [int](https://docs.python.org/3/builtins/functions.html#int)*

How many sounds were placed (the restraint check — fewer than one-per-sentence).

#### *property* rationale *: [str](https://docs.python.org/3/builtins/stdtypes.html#str)*

A short, agent/human-readable summary of what was chosen and why.

### *class* foley.ScoredEvent(segment, query, sound_id, origin, confidence, reason)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

One chosen sound placed for a narration event (a JSON-friendly rationale row).

#### to_dict()

Plain-dict form (for the MCP projection / a caller’s log).

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### *class* foley.SerializableMixin

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Adds `to_dict`/`to_json`/`from_dict`/`from_json` to a dataclass.

Stdlib-only, DRY, no-magic (de)serialization. Every SSOT dataclass below
inherits this instead of hand-rolling per-class encoders/decoders.

#### *classmethod* from_dict(d)

Reconstruct an instance from a plain dict.

Enum fields and nested dataclasses are coerced via `_decode()`.
Unknown keys are ignored (forward-compatible); missing keys fall back
to field defaults.

* **Parameters:**
  **d** ([`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)) – A plain dict (typically from `to_dict()` or `json.loads`).
* **Return type:**
  [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

#### *classmethod* from_json(s)

Reconstruct an instance from a JSON string.

* **Parameters:**
  **s** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – A JSON string (typically from `to_json()`).
* **Return type:**
  [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

#### to_dict()

Return the recursive plain-dict form.

Enum members are preserved (and remain JSON-safe because every enum
subclasses `str`); nested dataclasses are recursed via
`dataclasses.asdict`.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

#### to_json(, indent=None)

Return a JSON string (str-enums serialize to their `.value`).

* **Parameters:**
  **indent** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`int`](https://docs.python.org/3/builtins/functions.html#int)]) – Optional pretty-print indent passed to `json.dumps`.
* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### *class* foley.SessionStore(session_id='default', candidates=None, picks=None, rejects=None)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Three namespaced stores for one audition session (candidates / picks / rejects).

Each store defaults to a [`foley.stores.make_session_store()`](_autosummary/foley.stores.html.md#foley.stores.make_session_store) JSON store; tests
inject plain dicts. All values are JSON-safe dicts.

* **Parameters:**
  * **session_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The session namespace.
  * **rejects** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Optional injected `MutableMapping` stores.

#### add_pick(sound_id, , layer=None, onset=None)

Persist an accepted pick (+ optional layer/onset); return the pick count.

* **Return type:**
  [`int`](https://docs.python.org/3/builtins/functions.html#int)

#### add_reject(sound_id, , reason=None)

Record a rejected sound (feeds `refine` relevance feedback); return the count.

* **Return type:**
  [`int`](https://docs.python.org/3/builtins/functions.html#int)

#### cache_candidates(candidates)

Cache each candidate’s full `to_dict()` keyed by sound id; return the count cached.

* **Return type:**
  [`int`](https://docs.python.org/3/builtins/functions.html#int)

#### drop_pick(sound_id)

Remove a pick (idempotent); return the remaining pick count.

* **Return type:**
  [`int`](https://docs.python.org/3/builtins/functions.html#int)

#### list_picks()

All persisted picks.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]

#### list_rejects()

All recorded rejects.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]

#### picked_ids()

The picked sound ids.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]

#### rehydrate(ids)

Rebuild [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate) objects for `ids` from the cache.

Missing ids are skipped. Uses `Candidate.from_dict` (rebuilds the nested
`SoundRecord` / `LicenseRecord` / `Verdict`).

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]

#### rejected_ids()

The rejected sound ids.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]

### *class* foley.SoundDesignTimeline(items=<factory>, run_manifest_ref=None, transcript_ref=None, schema_version=1, narration_ref=None, word_timeline=<factory>, master=<factory>)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

The editable, re-renderable sound-design plan — the SELECT→WEAVE bridge and render SSOT.

SELECT (#7) emits the SPARSE form (just `items` + the run/transcript joins) via
`foley.agent.plan()`. WEAVE (#8) grows it additively: `narration_ref` binds the
voice audio, `word_timeline` caches the forced alignment (the reproducible seed),
`master` carries the loudness target, and each item gains its resolved
`Placement`/`Processing`. `render(timeline, library)` is then a PURE function of
this data + the library, so editing any field and re-rendering reproduces exactly that
change. `run_manifest_ref` == `foley.obs.RunManifest.run_id` (the reserved #8
`plan_ref` join).

### *class* foley.SoundEvent(query, layer=Layer.sfx_fg, diegetic=True, salience=Salience.medium, onset=None, loop=False, ucs_catid=None, audioset=<factory>, era_place=None)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

One salient, physically-audible event decomposed from a passage.

### *class* foley.SoundLibrary(, sounds=None, meta=None, vindex=None, kindex=None, embedder=None, data_dir=None, candidate_k=50, rrf_k=60)

Bases: [`Mapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Mapping)

A searchable, license-aware library of sounds (the foley INDEX façade).

Read it as a `Mapping` of `SoundRecord`s; search it with
:meth:`search` (text) / [`search_clip()`](_autosummary/foley.html.md#foley.SoundLibrary.search_clip) (a reference clip) / [`similar()`](_autosummary/foley.html.md#foley.similar)
(audio<->audio by id); browse it with [`filter()`](_autosummary/foley.html.md#foley.SoundLibrary.filter); grow it with [`add()`](_autosummary/foley.html.md#foley.SoundLibrary.add).

#### add(record, , data=None, vector=None)

Store a sound and index it (the ingest write path).

Persists bytes via [`store_sound()`](_autosummary/foley.stores.html.md#foley.stores.store_sound) (honouring the
by-value/by-reference license gate), upserts the CLAP vector into the
vector index, and indexes `caption``+``tags` into the keyword index.

A sound is retrieval-first, so it MUST carry an embedding: supply either
`data` (bytes to embed — note a by-reference sound is embedded from its
transient bytes even though they are not cached) or a precomputed
`vector`. Adding with neither raises, rather than silently indexing a
vectorless row (which the single-table [`LanceIndex`](_autosummary/foley.html.md#foley.LanceIndex) cannot persist,
producing backend-dependent search results).

* **Parameters:**
  * **record** ([`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord)) – The record to add (mutated by `store_sound` with resolved
    storage fields, and stamped with the embedding model/dim).
  * **data** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes)]) – The archive bytes (required for by-value storage; also the
    source for computing `vector` when it is not supplied).
  * **vector** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[`ndarray`]) – A precomputed CLAP embedding; when omitted and `data` is
    given, it is computed via the library’s embedder.
* **Return type:**
  [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord)
* **Returns:**
  The same (persisted, indexed) `record`.
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If neither `data` nor `vector` is provided (no way to
      obtain an embedding).

#### array(sound_id, , sr=None, mono=True)

Decode a sound to a working array (`float32`).

* **Parameters:**
  * **sound_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The record id.
  * **sr** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`int`](https://docs.python.org/3/builtins/functions.html#int)]) – Target sample rate (default: the working rate, 48 kHz).
  * **mono** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Down-mix to mono (default `True`).
* **Return type:**
  `ndarray`
* **Returns:**
  The decoded working array.

#### audio(sound_id)

Return a sound’s archive bytes (by-value from the store, or from a
local by-reference path).

* **Parameters:**
  **sound_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The record id.
* **Return type:**
  [`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes)
* **Returns:**
  The raw archive bytes.
* **Raises:**
  [**LookupError**](https://docs.python.org/3/builtins/exceptions.html#LookupError) – If the bytes are neither cached (by-value) nor readable
      from a local `uri` — a remote by-reference sound needs its
      source adapter (subtask #5) to fetch.

#### *property* data_dir *: [Path](https://docs.python.org/3/library/pathlib.html#pathlib.Path)*

The data root for default stores/index.

#### *property* embedder

The text<->audio embedder (CLAP by default).

#### filter(\*\*facets)

Browse the library by metadata facets (no ranking).

Accepts the same facet keywords as [`search()`](_autosummary/foley.html.md#foley.search)’s filters
(`commercial_ok`, `ucs_category`, `min_snr`, `duration_range`)
plus any `record_attr=value` equality predicate.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord)]

#### *property* kindex

The keyword index.

#### *property* meta

The metadata store (`id -> SoundRecord`).

#### search(query, , k=10, filters=None, commercial_ok=None, ucs_category=None, min_snr=None, duration_range=None, rerank=False)

Hybrid (CLAP vector ⊕ BM25) search for a text query.

* **Parameters:**
  * **query** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The natural-language query.
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Number of results to return.
  * **filters** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Extra `{record_attr: value}` equality predicates.
  * **commercial_ok** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`bool`](https://docs.python.org/3/builtins/functions.html#bool)]) – If `True`, keep only commercially-usable sounds.
  * **ucs_category** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Keep only sounds with this UCS CatID.
  * **min_snr** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`float`](https://docs.python.org/3/builtins/functions.html#float)]) – Keep only sounds whose QC `snr_db` is at least this.
  * **duration_range** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`float`](https://docs.python.org/3/builtins/functions.html#float), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]) – Keep only sounds whose `duration_s` is in
    `(min, max)`.
  * **rerank** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Re-order the shortlist by direct query<->audio cosine
    (fills the CLAP score for keyword-only hits).
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]
* **Returns:**
  Up to `k` :class:

  ```
  `
  ```

  ~foley.base.Candidate\`s, best first.

#### search_clip(clip, , sr=None, k=10)

Search by a reference audio clip (audio<->audio via CLAP).

* **Parameters:**
  * **clip** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike), [`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes), [`BinaryIO`](https://docs.python.org/3/library/typing.html#typing.BinaryIO)]) – A working array, or a path/bytes/file decodable by
    [`foley.audio.load()`](_autosummary/foley.audio.html.md#foley.audio.load).
  * **sr** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`int`](https://docs.python.org/3/builtins/functions.html#int)]) – Sample rate when `clip` is already a working array.
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Number of results.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]
* **Returns:**
  Up to `k` :class:

  ```
  `
  ```

  ~foley.base.Candidate\`s, most-similar first.

#### similar(sound_id, , k=10)

Return the `k` sounds most similar to `sound_id` (audio<->audio).

Uses the stored vector (no re-decoding); the query sound itself is
excluded from the results.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]

#### *property* sounds

The content-addressed byte store.

#### update_record(record)

Rewrite a stored record’s metadata and re-index its keyword text.

For corrections that do not touch the audio (a re-stamped licence, a removed
caption): the bytes and the CLAP vector stay as they are.

* **Raises:**
  [**KeyError**](https://docs.python.org/3/builtins/exceptions.html#KeyError) – If `record.id` is not in the library.
* **Return type:**
  [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord)

#### *property* vindex

The vector index.

### *class* foley.SoundRecord(id, content_sha256=None, hash_algo='sha256', uri=None, storage_mode=StorageMode.by_reference, archive_format=None, source_sample_rate=None, source_bit_depth=None, license=<factory>, caption=None, tags=<factory>, ucs_category=None, ucs_subcategory=None, audioset_labels=<factory>, duration_s=None, sample_rate=None, channels=None, loudness_lufs=None, format=None, qc=None, embedding_model=None, embedding_dim=None, embedding_ref=None, named_cue=None, schema_version=1)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

Canonical SSOT per sound.

Audio bytes + CLAP vector live in SEPARATE stores keyed by the same id; this
record holds a content-hash `uri`, never raw bytes.

### *class* foley.SpanRecord(name, span_id, parent_id=None, kind=None, start_ms=None, duration_ms=None, status='ok', attributes=<factory>, events=<factory>, error=None)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

One node of the run’s span tree (the trace half of the artifact).

Built by the recorder from its own clock + id-factory, independent of any tracer,
so the tree is complete even when the OTel mirror is a total no-op.

### *class* foley.SqliteVecIndex(, path, dim)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Single-file index: sqlite-vec `vec0` KNN + stdlib FTS5 keyword search.

The whole index is one SQLite file behind two virtual tables (independent, so
[`upsert()`](_autosummary/foley.html.md#foley.SqliteVecIndex.upsert) and [`index()`](_autosummary/foley.index.html.md#module-foley.index) never contend). \*\*Requires an interpreter
whose `sqlite3` permits loadable extensions\*\* — probe with
[`sqlite_vec_loadable()`](_autosummary/foley.html.md#foley.sqlite_vec_loadable) before constructing; the constructor raises a
clear error otherwise.

#### bm25(query, k, , where=None)

FTS5 BM25 search; returns `[(id, score), ...]` best-first.

FTS5’s `rank` is more-negative-is-better; it is negated so the returned
score is larger-is-better (consistent with the other backends).

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

#### close()

Close the underlying SQLite connection.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### commit()

Commit any pending SQLite transaction (writes auto-commit already).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### get_vector(id)

Return the stored vector for `id` (or `None`).

* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[`ndarray`]

#### index(id, text, meta)

Insert or replace the searchable text for `id` in the FTS5 table.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### knn(vector, k, , where=None)

KNN over `vec0` (cosine); returns `[(id, cosine_similarity), ...]`.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

#### upsert(id, vector, meta)

Insert or replace the vector for `id` in the `vec0` table.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### *class* foley.StorageMode(\*values)

Bases: [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Enum`](https://docs.python.org/3/library/enum.html#enum.Enum)

How a sound’s bytes are held (DERIVED from `license.cache_bytes_ok`).

### *class* foley.Tagger(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

Map a clip to `(label, score)` pairs against a label vocabulary.

Used on ingest to auto-fill `SoundRecord.audioset_labels` (supervised) and
`tags` (zero-shot). The default supervised tagger is PANNs CNN14 over
AudioSet; the default zero-shot tagger scores a clip against a custom/UCS
label set via CLAP (report 03). BEATs/AST are drop-in upgrades. Consumes the
working array (`float32`, any sr — the impl resamples to its model’s rate).

#### tag(wav, sr, , taxonomy='audioset', top_k=10)

Return the top-`k` `(label, score)` tags for the clip, best first.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

### *class* foley.TimelineItem(clip_ref, onset=None, gain=0.0, layer=Layer.sfx_fg, loop=False, id=None, placement=None, processing=None, event=None, enabled=True)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

One placed sound on the sound-design timeline — sparse seed + resolved render fields.

SELECT (#7) sets only the sparse flat fields (`clip_ref·onset·gain·layer·loop`);
WEAVE (#8) additively fills `id` / `placement` / `processing` (and may carry
the originating `event` for provenance). `enabled` is a non-destructive mute.
The sparse flat fields are never removed — they stay SELECT’s SSOT input; the render
reads the resolved `placement`/`processing` (falling back to the flat fields when
those are absent).

### *exception* foley.TrademarkRefusal(message, , hits, report)

Bases: [`SafetyRefusal`](_autosummary/foley.sources.html.md#foley.sources.SafetyRefusal)

A [`SafetyRefusal`](_autosummary/foley.html.md#foley.SafetyRefusal) for a prompt naming a trademarked audio logo (report 07 §7.2).

### *class* foley.VectorIndex(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

Approximate-nearest-neighbour store over embedding vectors.

The default is LanceDB (report 04 §2); Qdrant/pgvector/sqlite-vec bind the
same protocol behind the scenes. `where` is an optional metadata push-down
the façade may pass; a backend that cannot push filters down MAY ignore it
(the façade over-fetches and post-filters to stay correct either way).

#### get_vector(id)

Return the stored vector for `id` (or `None` if absent).

Needed by `SoundLibrary.similar` (fetch a sound’s own vector, then run
[`knn()`](_autosummary/foley.html.md#foley.VectorIndex.knn)) and by the optional CLAP rerank (score keyword-only hits).

* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[`ndarray`]

#### knn(vector, k, , where=None)

Return the `k` nearest ids to `vector`, most-similar first.

* **Parameters:**
  * **vector** (`ndarray`) – A `(dim,)` query vector (already L2-normalized).
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Number of neighbours to return.
  * **where** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Optional metadata predicates for push-down filtering.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]
* **Returns:**
  `[(id, cosine_similarity), ...]` in descending-similarity order.

#### upsert(id, vector, meta)

Insert or replace the vector (and light metadata) for `id`.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### *class* foley.Verdict(match, confidence, reason='', level=VerifyLevel.clap)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

The result of one verification rung for a candidate.

### *class* foley.VerifyLevel(\*values)

Bases: [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Enum`](https://docs.python.org/3/library/enum.html#enum.Enum)

Which rung of the verification ladder produced a `Verdict`.

### *class* foley.WeaveResult(audio, sr, timeline, credits, captions_vtt, captions_srt, master_report, run_manifest_ref=None, content_credential=None, watermark=None, notes=<factory>)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

The output of `weave()` — the v1 Definition-of-Done deliverable.

A mastered mix + an editable, re-renderable timeline + credits + SDH captions + a
reproducible run-artifact join, plus the fail-safe provenance re-assertion.

#### notes *: [list](https://docs.python.org/3/builtins/stdtypes.html#list)*

What the caller should know about how the mix was made, e.g. that word timings
are estimates because no forced aligner is installed (#65).

### foley.add_from(source, , query, license='cc0', limit=50, library=None, intended_use=None, adapter=None, on_unsupported=None, \*\*affordances)

Search a live `source` and ingest its license-clean hits into `library`.

Progressive disclosure: `add_from("freesound", query="ocean waves")` works out
of the box (CC0-only, into the process-wide default library); every other knob
is an optional keyword. Each hit is license-gated BEFORE any bytes are fetched
(fail-closed), then routed through [`ingest_one()`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.ingest_one), which
applies the by-reference storage gate from the sound’s own license.

* **Parameters:**
  * **source** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – A registered live-source name (e.g. `'freesound'`).
  * **query** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The natural-language search query.
  * **license** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – License constraint pushed into the source query (default
    `'cc0'`). The per-item fail-closed guard enforces the source’s
    accepted-license allowlist regardless.
  * **limit** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Max candidates to request from the source.
  * **library** – Target [`SoundLibrary`](_autosummary/foley.index.library.html.md#foley.index.library.SoundLibrary) (default: the
    process-wide default library).
  * **intended_use** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`IntendedUse`](_autosummary/foley.base.html.md#foley.base.IntendedUse)]) – The rights intent each candidate is gated against (default:
    `DEFAULT_INTENDED_USE`).
  * **adapter** – An optional pre-built adapter to use instead of the registry’s
    (the dependency-injection seam — a test passes a fake-transport
    adapter; production omits it and the registry lazily builds one).
  * **on_unsupported** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – A search parameter the source cannot honour: `None`
    (default) drops it with a note in `report.notes`; `'raise'` raises
    (see `foley.sources._dispatch.translate_affordances()`).
  * **\*\*affordances** – Extra unified affordances forwarded to the adapter’s
    `search` (e.g. `duration_range`, `sort`).
* **Return type:**
  [`IngestReport`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestReport)
* **Returns:**
  An [`IngestReport`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestReport) — inspect `.ingested` for the
  stored records (each `storage_mode == by_reference` for Freesound) and
  `.summary()` for counts, exactly like [`foley.ingest()`](_autosummary/foley.html.md#foley.ingest).

### foley.apply_license_flags(record, , overrides=None)

Populate `record`’s eight derived flags from its `license_id`
(+ overrides), in place, and return it.

Does NOT touch `rights_verified` — verification is a separate concern.

* **Parameters:**
  * **record** ([`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)) – The [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord) to populate.
  * **overrides** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Optional per-source flag overrides (see
    [`derive_license_flags()`](_autosummary/foley.html.md#foley.derive_license_flags)).
* **Return type:**
  [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)
* **Returns:**
  The same (mutated) `record`.

### foley.attribution_line(source, , fmt='markdown')

Render one sound’s TASL attribution line.

A source-supplied `attribution_text` (if non-empty) is returned **verbatim**;
otherwise the line is synthesized from Title/Author/Source/License, with a
`(modified)` notice and an AI-disclosure segment appended as applicable.

* **Parameters:**
  * **source** (`Union`[[`CreditEntry`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.CreditEntry), [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord), [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate), [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)]) – A [`CreditEntry`](_autosummary/foley.html.md#foley.CreditEntry), or any credit input (coerced first).
  * **fmt** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – `'markdown'` (hyperlinked list-item body) or `'plain'` (text with
    URLs in parentheses).
* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)
* **Returns:**
  The attribution line (no leading bullet / trailing newline).

### foley.bootstrap(, rings=(0, 1), corpora=None, data_dir=None, library=None, roots=None, accept_ai_restricted=False, commercial_only=None, \*\*ingest_one_kw)

Seed `library` from the selected bulk corpora, returning per-corpus reports.

* **Parameters:**
  * **rings** ([`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`int`](https://docs.python.org/3/builtins/functions.html#int), [`...`](https://docs.python.org/3/builtins/constants.html#Ellipsis)]) – Which rings to include (default `(0, 1)` — Ring 2 is never
    default; it is opt-in via `corpora=[...]` + `accept_ai_restricted`).
  * **corpora** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]) – Explicit corpus-name allowlist; overrides `rings` when given.
  * **data_dir** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Root under which each corpus lives at `data_dir/<name>`
    (default: the library’s data dir / `$FOLEY_DATA_DIR`).
  * **library** – Target [`SoundLibrary`](_autosummary/foley.index.library.html.md#foley.index.library.SoundLibrary) (default: the
    process-wide default library).
  * **roots** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]) – Optional per-corpus root overrides (`{name: path}`) — for corpora
    downloaded somewhere other than `data_dir/<name>`.
  * **accept_ai_restricted** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Consent gate for Ring-2 / `ai_training_ok=False`
    corpora. `False` (default) refuses them; `True` records explicit
    operator consent and admits them.
  * **commercial_only** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`bool`](https://docs.python.org/3/builtins/functions.html#bool)]) – Force the per-clip commercial filter on/off. `None`
    (default) derives it from the ring (Ring 1 → on, else off).
  * **\*\*ingest_one_kw** – Forwarded to [`foley.index.ingest.ingest_one()`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.ingest_one)
    (`do_supervised`, `do_zeroshot`, `min_status`, `thresholds` …).
* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`IngestReport`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestReport)]
* **Returns:**
  `{corpus_name: IngestReport}` — inspect each `.summary()`.

### foley.build_mcp_server(, library=None, session='default', runtime=None, byte_store=None, include=None, name='foley', max_usd=None)

Build the foley MCP server (lazy `py2mcp`); registers the JSON-safe tool surface.

Validates that every source declares a `data_egress` (fail-closed), binds the
injectable library / runtime / byte-store, and hands the resolved tool functions to
`py2mcp.mk_mcp_server`. Never starts a server or touches the network.

* **Parameters:**
  * **library** – The [`foley.index.SoundLibrary`](_autosummary/foley.index.html.md#foley.index.SoundLibrary) (default: the shared one).
  * **session** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The default session id.
  * **runtime** – A [`foley.runtime.RuntimeConfig`](_autosummary/foley.runtime.html.md#foley.runtime.RuntimeConfig) (default: the active one).
  * **byte_store** – A `MutableMapping[str, bytes]` for previews / rendered mixes.
  * **include** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]) – Optional subset of tool names to expose.
  * **name** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The MCP server name.
  * **max_usd** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`float`](https://docs.python.org/3/builtins/functions.html#float)]) – The server’s cumulative spend cap across every paid tool call, for
    its lifetime (default [`foley.cost.default_max_usd()`](_autosummary/foley.cost.html.md#foley.cost.default_max_usd): $1, or $3 under
    `$FOLEY_LLM=anthropic`).
* **Returns:**
  A `fastmcp.FastMCP` server.

### foley.candidate_of(result)

Wrap a stored [`IngestResult`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult) as a generated candidate.

The report-10 §4.2 shape: retrieval and generation return the same
[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate), differing only in `origin`. Use on a
`pass` / `warn` result (its `record` is the canonical, stored
[`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord)).

* **Parameters:**
  **result** ([`IngestResult`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult)) – A stored ingest result (`result.record` is not `None`).
* **Return type:**
  [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)
* **Returns:**
  A [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate) with `origin=CandidateOrigin.generated`.

### foley.capability_report(, runtime=None)

A JSON-safe capability + posture snapshot for the CLI, docs, and the MCP tool.

Groups requirements into `keys` (env), `extras` (importable), `system`
(binary), adds the current offline posture and the available source list, and
lists `degraded_tools` — capabilities whose requirement is unmet.

* **Parameters:**
  **runtime** – A [`foley.runtime.RuntimeConfig`](_autosummary/foley.runtime.html.md#foley.runtime.RuntimeConfig) (default: the active one).
* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)
* **Returns:**
  `{keys, extras, system, offline, sources, llm, degraded_tools}` — all
  JSON-safe. `llm` is the provider the SELECT rungs resolve to right now
  (see [`foley.agent.llm.resolve_llm()`](_autosummary/foley.agent.llm.html.md#foley.agent.llm.resolve_llm)), or the reason it cannot resolve.

### foley.check_requirements(, names=None, verbose=False)

Report which optional foley capabilities are available (`{name: is_available}`).

* **Parameters:**
  * **names** ([`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`...`](https://docs.python.org/3/builtins/constants.html#Ellipsis)] | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Which requirements to check (default: the full assembled set).
  * **verbose** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – If `True`, print an actionable hint for each missing requirement.
* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`bool`](https://docs.python.org/3/builtins/functions.html#bool)]
* **Returns:**
  `{requirement_name: available}`. Everything-absent is fine — foley degrades
  (deterministic fakes, offline mode, in-process DSP); the report just shows what
  each capability would unlock.

### foley.content_key(data, , algo='sha256')

Return the content-address key for `data` — its hex digest.

Using the hash as the key gives free deduplication (identical bytes map to
the same key) and immutability (a key always names the exact same bytes).

* **Parameters:**
  * **data** ([`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes)) – The raw bytes to address (e.g. a FLAC archive blob).
  * **algo** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – A `hashlib` algorithm name (defaults to `HASH_ALGO`).
* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)
* **Returns:**
  The lowercase hex digest of `data` under `algo`.

### foley.credit_entry(record, , title=None)

Build a [`CreditEntry`](_autosummary/foley.html.md#foley.CreditEntry) from a record (title override optional).

Every field is read straight off the [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord) (flags
never re-derived); `modified` reflects a non-empty `transformations` list.

* **Parameters:**
  * **record** (`Union`[[`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord), [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate), [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)]) – A [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord), [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate),
    or [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord).
  * **title** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Explicit title override (else resolved from caption/tags/…).
* **Return type:**
  [`CreditEntry`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.CreditEntry)

### foley.credits(sounds, , title='Credits', only_required=False, write_to=None)

Build the TASL attribution [`Credits`](_autosummary/foley.provenance.html.md#foley.provenance.Credits) for `sounds`.

Works standalone today (given any iterable of sounds), and is what the WEAVE
stage will call at render time. Inspect `.markdown` (a `CREDITS.md`
document) / `.manifest` (a JSON-serializable dict) on the result.

* **Parameters:**
  * **sounds** – An iterable of [`SoundRecord`](_autosummary/foley.html.md#foley.SoundRecord) / [`Candidate`](_autosummary/foley.html.md#foley.Candidate) /
    [`LicenseRecord`](_autosummary/foley.html.md#foley.LicenseRecord) (e.g. the result of [`search()`](_autosummary/foley.html.md#foley.search), or the
    sounds placed in a timeline).
  * **title** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The credits heading.
  * **only_required** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Keep only legally-required attributions (drops CC0 /
    user-owned courtesy credits). Default credits everything.
  * **write_to** – Optional directory; when given, writes `CREDITS.md` and
    `credits.json` into it (created if missing).
* **Returns:**
  A [`Credits`](_autosummary/foley.provenance.html.md#foley.provenance.Credits).

### foley.credits_for(sounds, , title='Credits', only_required=False, sort='appearance')

Build the deduplicated [`Credits`](_autosummary/foley.html.md#foley.Credits) for the sounds used in a run.

* **Parameters:**
  * **sounds** ([`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[`Union`[[`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord), [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate), [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)]]) – An iterable of records / candidates / license records.
  * **title** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The credits heading (also carried in the manifest).
  * **only_required** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Keep only entries whose license *requires* attribution
    (drops CC0 / user-owned courtesy credits). Default `False` credits
    everything (never-discard-provenance).
  * **sort** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – `'appearance'` (default: first-seen order), `'author'`, or
    `'title'` (case-insensitive alpha).
* **Return type:**
  [`Credits`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.Credits)
* **Returns:**
  A [`Credits`](_autosummary/foley.html.md#foley.Credits); identical sounds (same id) are credited once
  (first-writer-wins).

### foley.dc_offset(samples)

Largest per-channel absolute DC offset, `max_c |mean_n x[n, c]|`.

* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

### foley.decide(event, kept, verified, , tau_retrieve, budget, loop)

The single generate-vs-retrieve branch — a PURE function (report 05 §4).

Chooses among `DecideAction` from the already-gated (`kept`) and
already-verified (`verified`) sets. It performs no I/O and never calls
`keep`/`search`/`generate` — the [`foley.agent.tools`](_autosummary/foley.agent.tools.html.md#module-foley.agent.tools) loop acts on the
returned [`Decision`](_autosummary/foley.html.md#foley.Decision).

Policy (report 05 §4):

> * a verified clip clearing `tau_retrieve` → `USE` (the best one);
> * verified-but-low-confidence with refine budget → `REFINE` (feed the reason back);
> * a non-diegetic cue, or a diegetic gap with no verified match, with generate budget
>   → `GENERATE`;
> * otherwise → `DROP` (silence), unless a lower-confidence verified clip exists and
>   generation is off, in which case fall back to that best-effort pick.
* **Parameters:**
  * **event** ([`SoundEvent`](_autosummary/foley.base.html.md#foley.base.SoundEvent)) – The event being resolved.
  * **kept** ([`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]) – The license-clean candidates (each `license_ok is True`).
  * **verified** ([`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]) – The subset of `kept` whose verdict matched.
  * **tau_retrieve** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – The confidence threshold for auto-accepting a retrieved clip.
  * **budget** ([`Budget`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.Budget)) – The per-event cost budget.
  * **loop** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The current refine-loop index (for the audit reason).
* **Return type:**
  [`Decision`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.Decision)
* **Returns:**
  A [`Decision`](_autosummary/foley.html.md#foley.Decision).

### foley.decompose_context(context, , max_events=6, seconds=None, decomposer=None, \_span=None)

Decompose a passage into `<= max_events` sparse [`SoundEvent`](_autosummary/foley.html.md#foley.SoundEvent)s.

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`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The narrative passage.
  * **max_events** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The sparse density cap.
  * **seconds** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`float`](https://docs.python.org/3/builtins/functions.html#float)]) – Optional passage duration (density-window hint; forwarded, else ignored).
  * **decomposer** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Decomposer`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Decomposer)]) – An injected [`Decomposer`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Decomposer) (the DI seam);
    defaults to `_default_decomposer()`.
  * **\_span** – Internal — the obs span handle `find()` opens for GenAI recording.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`SoundEvent`](_autosummary/foley.base.html.md#foley.base.SoundEvent)]

### foley.default_embedder()

Return a process-wide default [`ClapEmbedder`](_autosummary/foley.html.md#foley.ClapEmbedder) (loaded once, reused).

Cached so repeated `foley.search()` calls share a single loaded model.

* **Return type:**
  [`ClapEmbedder`](_autosummary/foley.index.embedders.html.md#foley.index.embedders.ClapEmbedder)

### foley.default_index(, data_dir, dim)

Build the best available persistent index for a library.

Degradation ladder: LanceDB (`foley[index]`) → sqlite-vec
(`foley[index-sqlite]`, if loadable) → an informative error. The
non-persistent [`MemoryIndex`](_autosummary/foley.html.md#foley.MemoryIndex) is never chosen automatically (a library
must survive restarts); inject it explicitly for tests/ephemeral use.

* **Parameters:**
  * **data_dir** – The library data root (a `pathlib.Path`-like).
  * **dim** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The embedding dimensionality from the active embedder.
* **Returns:**
  A ready index object (both `VectorIndex` and `KeywordIndex`).
* **Raises:**
  [**RuntimeError**](https://docs.python.org/3/builtins/exceptions.html#RuntimeError) – If no persistent backend is installed/usable.

### foley.default_library()

The process-wide default library (local stores + CLAP + best index).

* **Return type:**
  [`SoundLibrary`](_autosummary/foley.index.library.html.md#foley.index.library.SoundLibrary)

### foley.default_tagger()

The default supervised tagger (PANNs CNN14; `foley[tag]`).

* **Return type:**
  [`PannsTagger`](_autosummary/foley.index.taggers.html.md#foley.index.taggers.PannsTagger)

### foley.default_zeroshot_tagger(embedder=None)

The default zero-shot tagger (CLAP vs UCS subcategories; `foley[clap]`).

Cached **per embedder** so the tagger is bound to the SAME embedder that
produced the audio vector it scores — otherwise (report seam) the cosine would
cross two unrelated embedding spaces. `embedder=None` uses the process-wide
default embedder.

* **Return type:**
  [`ClapZeroShotTagger`](_autosummary/foley.index.taggers.html.md#foley.index.taggers.ClapZeroShotTagger)

### foley.demo(, library=None, query='rain on a window', k=3)

Ingest the bundled Ring-0 fixture and run one search — the smoke test.

Needs no corpus download (the fixture ships in the wheel), but does need the
runtime extras: `foley[audio]` to decode and, unless a library is injected,
`foley[clap]` for the default embedder (its model downloads on first use).
Uses an ephemeral in-memory library (so it never mutates `$FOLEY_DATA_DIR`)
unless one is injected. The fixture’s per-clip captions + tags (from its
`manifest.json`) flow into the keyword index so a plain-text query resolves.

* **Parameters:**
  * **library** – Optional target library (tests inject a `FakeEmbedder` one;
    the default builds a memory library with the real CLAP embedder).
  * **query** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The demo search query.
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – How many hits to request.
* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)
* **Returns:**
  `{"ingested": <summary dict>, "top_hit": <id or None>, "caption": <str>}`.

### foley.derive_license_flags(license_id, , overrides=None)

Look up the flag set for a `license_id` (fail-closed fallback), then
apply per-source overrides.

* **Parameters:**
  * **license_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The normalized license id (SPDX or foley-specific token).
  * **overrides** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Optional per-source flag overrides — e.g. Freesound forces
    `cache_bytes_ok=False` on CC0. Keys must be `LicenseFlags` fields.
* **Return type:**
  [`LicenseFlags`](_autosummary/foley.licensing.html.md#foley.licensing.LicenseFlags)
* **Returns:**
  The resolved [`LicenseFlags`](_autosummary/foley.html.md#foley.LicenseFlags) (fallback = all-False
  `UNKNOWN_LICENSE_FLAGS` for unrecognized ids).
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If `overrides` contains a key that is not a
      [`LicenseFlags`](_autosummary/foley.html.md#foley.LicenseFlags) field.

### foley.detect_clipping(samples, , full_scale=0.999, min_run=3)

Detect hard (flat-topped) clipping.

A frame is “hot” when any channel reaches `|x| >= full_scale`. Only
maximal hot runs of length `>= min_run` count as clip events.

* **Parameters:**
  * **samples** (`ndarray`) – Waveform in `[-1, 1]`.
  * **full_scale** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Absolute level at/above which a sample is full-scale.
  * **min_run** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Minimum consecutive full-scale frames to count as clipping.
* **Return type:**
  [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`float`](https://docs.python.org/3/builtins/functions.html#float), [`int`](https://docs.python.org/3/builtins/functions.html#int)]
* **Returns:**
  `(clipped_ratio, max_run_length)` — the fraction of frames inside
  counting runs, and the longest counting run (`(0.0, 0)` if none).

### foley.duration_s(samples, sample_rate)

Clip duration in seconds: `frames / sample_rate`.

* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

### foley.encode(samples, sample_rate, , fmt='flac', subtype='PCM_24')

Encode `samples` fully in memory and return the container bytes.

This is the producer for the content-addressed byte store: default output is
the FLAC archive form.

* **Parameters:**
  * **samples** (`ndarray`) – The working array to encode.
  * **sample_rate** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Sample rate in Hz.
  * **fmt** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Container/codec name (case-insensitive).
  * **subtype** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Sample subtype (e.g. `PCM_24`).
* **Return type:**
  [`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes)
* **Returns:**
  The encoded audio as `bytes`.

Lazy dependency: `soundfile`.

### foley.ensure_channels(samples, , channels)

Coerce `samples` to exactly `channels` channels.

Mappings: mono -> N by duplication; N -> mono by mean; N -> M (N != M, both
> 1) by collapsing to mono then tiling up to M.

* **Parameters:**
  * **samples** (`ndarray`) – Mono or multichannel working array.
  * **channels** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Target channel count (must be >= 1).
* **Return type:**
  `ndarray`
* **Returns:**
  A `(frames,)` array when `channels == 1`, else a
  `(frames, channels)` array.
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If `channels < 1`.

Lazy dependency: `numpy` (only for the up-mix / tile path).

### foley.estimate(verb, \*\*kwargs)

What a call would cost in USD before making it — `None` when it cannot be known (#57).

`None` is never “free”: it means the price is unknown (no pricing declared, a
remote LLM endpoint, an unlisted model), and such a call needs the run’s approval
(`Budget(approve_unknown_cost=True)`). Paid amounts are upper bounds.

* **Parameters:**
  * **verb** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – `'generate'` (`backend=`, plus the generation affordances — priced
    exactly as the call would be: same drops, same clamp); `'find'` /
    `'score'` (the run’s upper bound: every generation and LLM call it could
    make — what `find` checks against its budget before the first paid call;
    pass `context=` / `segments=` so the decomposer’s prompt is sized, and
    `score` sums one `find` per segment);
    `'search'` / `'similar'` / `'ingest'` (local: `0.0`);
    `'add_from'` (`source=`).
  * **\*\*kwargs** – The same keywords the verb takes.
* **Raises:**
  * [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – For an unknown verb.
  * [**KeyError**](https://docs.python.org/3/builtins/exceptions.html#KeyError) – For an unknown backend / source.

### foley.estimate_snr(samples, sample_rate, , quiet_percentile=10.0, frame_s=0.025, hop_s=0.01)

Estimate SNR in dB (advisory — a busy-street SFX legitimately scores low).

The noise floor is the mean short-time RMS of the quietest
`quiet_percentile` percent of frames; the signal level is the whole-clip
RMS. A near-noise-free clip (quiet frames -> ~0) yields a very high value;
an exactly-zero floor returns `inf` and a silent clip returns `-inf`.

* **Parameters:**
  * **samples** (`ndarray`) – Waveform in `[-1, 1]` (down-mixed to mono internally).
  * **sample_rate** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Sample rate in Hz (sizes the frames).
  * **quiet_percentile** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Percent of quietest frames forming the noise floor.
  * **frame_s** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Short-time frame length in seconds.
  * **hop_s** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Hop between frames in seconds.
* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)
* **Returns:**
  SNR in dB.

### foley.evaluate(, golden=None, k=10)

Run the Tier-1 retrieval eval over the golden set ([nDCG@10](mailto:nDCG@10) / recall / mAP / MRR).

Scores every golden query through the real [`SoundLibrary.search()`](_autosummary/foley.html.md#foley.SoundLibrary.search) path
against a deterministic, CLAP-free Ring-0 library — the same computation the
PR gate asserts on. See [`foley.eval`](_autosummary/foley.eval.html.md#module-foley.eval).

* **Parameters:**
  * **golden** – Optional path to a golden-set JSON (default: the frozen seed).
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Retrieval cutoff and metric `@k`.
* **Returns:**
  A [`foley.eval.RetrievalReport`](_autosummary/foley.eval.html.md#foley.eval.RetrievalReport).

### foley.evaluate_fit(, golden=None, sample=None, level=VerifyLevel.judge, fit_judge=None, embedder=None, seed=0, k=10, llm=None)

Run the Tier-2 **fit** eval over the golden set — “does the accepted clip fit?” (#10b).

The judge-based sibling of [`evaluate()`](_autosummary/foley.html.md#foley.evaluate): over a seeded stratified sample it runs
the SELECT pipeline and audits each license-clean candidate with the authoritative
fit-judge, returning a [`foley.eval.FitReport`](_autosummary/foley.eval.html.md#foley.eval.FitReport) (fit-precision / recall / F1 +
fit-score + auto-accept-rate + per-stratum breakdown). Works out of the box on the
Ring-0 fixture with the deterministic fake judge — no network, key, or heavy deps.
**Nightly / pre-release and cost-gated**: report-only — gating is the caller’s job via
`FitReport.gate()`. It never touches the retrieval ranking (the Tier-1 nDCG gate).

* **Parameters:**
  * **golden** – Optional golden-set JSON path (default: the frozen Ring-0 seed).
  * **sample** – Optional stratified sample cap (default: the whole set — the cost gate).
  * **level** – The verify rung the fit-judge audits at — `'listen'` or `'judge'`
    (default `VerifyLevel.judge`); `'clap'` is rejected.
  * **fit_judge** – An injected authoritative judge (default: the `llm` provider’s judge
    — the hermetic [`StringOverlapJudge`](_autosummary/foley.agent.html.md#foley.agent.StringOverlapJudge) fake unless `llm`
    opts in; the audio-LM [`AudioLMJudge`](_autosummary/foley.agent.html.md#foley.agent.AudioLMJudge) is injection-only).
  * **embedder** – The Ring-0 embedder (default: the CLAP-free `HashingBowEmbedder`).
  * **seed** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The sampling RNG seed.
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Retrieval shortlist depth per event.
  * **llm** – The fit-judge’s provider when `fit_judge` is not given —
    `'anthropic'` for the nightly arbiter (or `$FOLEY_LLM`); `None`
    keeps the deterministic fake (a local endpoint is never picked implicitly).
* **Returns:**
  A [`foley.eval.FitReport`](_autosummary/foley.eval.html.md#foley.eval.FitReport).

### foley.fade(samples, sample_rate, , fade_in_s=0.01, fade_out_s=0.01, kind='linear')

Apply in/out gain ramps to `samples` (a short declick by default).

Ramp lengths are clamped to at most `len(samples) // 2` so the in- and
out-ramps never overlap on tiny inputs.

* **Parameters:**
  * **samples** (`ndarray`) – Working array (mono or multichannel).
  * **sample_rate** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Sample rate in Hz (converts the fade durations to samples).
  * **fade_in_s** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Fade-in duration in seconds.
  * **fade_out_s** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Fade-out duration in seconds.
  * **kind** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – `'linear'` or `'equal_power'` ramp shape.
* **Return type:**
  `ndarray`
* **Returns:**
  A new array with the fade envelope applied (input is not mutated).

Lazy dependency: `numpy`.

### foley.find(context, , max_events=6, seconds=None, intended_use=None, backend='auto', verify='listen', stream=False, k=10, tau_retrieve=0.5, tau_clap=0.35, max_refine_loops=1, budget=None, library=None, decomposer=None, judge=None, refiner=None, llm=None)

The headline: a narrative context → verified, license-clean sound candidates.

`decompose → (per event) refine/search → verify_match ladder → decide (with the
fail-closed license gate FIRST) → place` (report 05 §5). Works out of the box —
`foley.find("She pushed open the heavy oak door; rain hammered outside.")` — with
deterministic defaults; every model / threshold / seam is an optional keyword.

* **Parameters:**
  * **context** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The narrative passage.
  * **max_events** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The sparse density cap on decomposed events.
  * **seconds** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`float`](https://docs.python.org/3/builtins/functions.html#float)]) – Optional passage duration (density-window hint; forwarded).
  * **intended_use** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`IntendedUse`](_autosummary/foley.base.html.md#foley.base.IntendedUse)]) – The caller’s rights intent (default:
    [`foley.licensing.DEFAULT_INTENDED_USE`](_autosummary/foley.licensing.html.md#foley.licensing.DEFAULT_INTENDED_USE) — commercial publishing;
    NC / SA material is refused unless you pass a different intent).
  * **backend** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Generation backend for the fallback (`'auto'` → `foley.generate`’s default).
  * **verify** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`VerifyLevel`](_autosummary/foley.base.html.md#foley.base.VerifyLevel)]) – The max verify rung — `'clap'` | `'listen'` | `'judge'`.
  * **stream** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – If `True`, return a generator yielding one [`Candidate`](_autosummary/foley.html.md#foley.Candidate) per
    resolved event; else return the collected `list`.
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Retrieval shortlist depth per query.
  * **tau_retrieve** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Confidence threshold to auto-accept a retrieved clip.
  * **tau_clap** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – The `clap`-rung gate threshold.
  * **max_refine_loops** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Max refine→re-retrieve passes per event (also the default
    [`Budget`](_autosummary/foley.html.md#foley.Budget)).
  * **budget** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Budget`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.Budget)]) – An explicit [`Budget`](_autosummary/foley.html.md#foley.Budget) (overrides `max_refine_loops`). It is
    also the run’s spend cap: `Budget(max_usd=...)` (default $1; $3 when
    `llm='anthropic'` or `$FOLEY_LLM=anthropic`, whose default run is
    bounded at about $2.52 — [`foley.cost.DEFAULT_MAX_USD_BY_LLM`](_autosummary/foley.cost.html.md#foley.cost.DEFAULT_MAX_USD_BY_LLM)) bounds
    every paid call the run makes — generations and paid LLM rungs —
    cumulatively, and a call of unknown cost needs
    `Budget(approve_unknown_cost=True)` (#57).
  * **library** – Target [`SoundLibrary`](_autosummary/foley.html.md#foley.SoundLibrary) (default: the process-wide default).
  * **refiner** (*decomposer / judge /*) – Injected DI seams
    ([`Decomposer`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Decomposer) / `Judge` / `Refiner`);
    each defaults to the `llm` provider’s implementation.
  * **llm** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Which LLM the decompose / refine / judge rungs use when not injected —
    `'fake'` | `'local'` | `'anthropic'`. `None` reads `$FOLEY_LLM`,
    then falls back to the free default (a configured local endpoint, else the
    deterministic fake). A key being present never opts in to paid calls.
* **Return type:**
  `Union`[[`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)], [`Iterator`](https://docs.python.org/3/library/typing.html#typing.Iterator)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]]
* **Returns:**
  `list[Candidate]` (`stream=False`) or an `Iterator[Candidate]`
  (`stream=True`) — one verified, license-clean candidate per resolved event.

### foley.fuse_hits(vector_hits, keyword_hits, , k, rrf_k=60)

RRF-fuse a vector ranker’s hits with a keyword ranker’s hits.

* **Parameters:**
  * **vector_hits** ([`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]) – `[(id, cosine_similarity), ...]` best-first (from
    [`knn()`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.VectorIndex.knn)).
  * **keyword_hits** ([`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]) – `[(id, bm25_score), ...]` best-first (from
    [`bm25()`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.KeywordIndex.bm25)).
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Number of fused hits to return.
  * **rrf_k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The RRF damping constant.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`FusedHit`](_autosummary/foley.index.search.html.md#foley.index.search.FusedHit)]
* **Returns:**
  The top-`k` :class:

  ```
  `
  ```

  FusedHit\`s, each carrying its raw component scores.

### foley.generate(prompt, , backend='stable_audio', library=None, store=True, adapter=None, watermark=None, on_flagged='refuse', watermarker=None, provenance_store=None, on_unsupported=None, reuse_cached=True, budget=None, \*\*affordances)

Generate a sound effect for `prompt` and add it to the library (by-value).

Progressive disclosure: `foley.generate("a single wooden door creak")` works
out of the box (the local Stable Audio Open backend, into the process-wide
default library). The generated audio is stored **by-value** with a content-hash
id, so it becomes a first-class, re-searchable library entry — every generation
is a future free retrieval (the generation flywheel). It flows through the SAME
[`ingest_one()`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.ingest_one) pipeline as every other source, with
operator consent for the generator license’s AI-training restriction (the record
keeps `ai_training_ok=False`, so [`keep()`](_autosummary/foley.html.md#foley.keep) still refuses it for
training uses).

* **Parameters:**
  * **prompt** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The natural-language sound description.
  * **backend** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – A registered generate source — `"stable_audio"` (default, local;
    needs `foley[stable-audio]`) or `"elevenlabs"` (hosted;
    `foley[elevenlabs]` + `$ELEVENLABS_API_KEY`).
  * **library** – Target library (default: the process-wide default library).
  * **store** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – If `False`, synthesize + enrich a preview without adding it.
  * **adapter** – Optional pre-built adapter (the DI seam; production omits it and the
    registry lazily builds one).
  * **watermark** – `True` require an AudioSeal watermark, `False` never, `None`
    (default, auto) watermark iff `foley[provenance]` is installed (#9b).
  * **on_flagged** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – `'refuse'` (default, fail-closed) or `'warn'` for a prompt
    that trips the trademarked-audio / recognizable-voice safety gate (#9b).
  * **watermarker** – An injected watermarker (the DI seam; tests pass a fake).
  * **provenance_store** – A `MutableMapping` for content-credential sidecars
    (default: [`foley.stores.make_provenance_store()`](_autosummary/foley.stores.html.md#foley.stores.make_provenance_store)).
  * **on_unsupported** – A parameter the backend cannot honour: `None` (default)
    raises for a meaning-carrying one (`seed`, `negative_prompt`) and drops
    the rest with a note; `'warn'` drops everything with a note + warning;
    `'note'` silently-but-recorded; `'raise'` raises for any.
  * **reuse_cached** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Serve an identical paid request from the generations cache
    (default `True`): a paid sound is never paid for twice (#59).
  * **budget** – A [`Budget`](_autosummary/foley.html.md#foley.Budget) for this call (default: the active run’s, else a
    fresh one — a $1 cap; an unknown cost needs approval) (#57).
  * **\*\*affordances** – Unified generation affordances (`duration`,
    `prompt_influence`, `negative_prompt`, `steps`, `seed`, `loop`,
    `output_format` — see `GENERATION_AFFORDANCES`).
* **Returns:**
  The stored [`Candidate`](_autosummary/foley.html.md#foley.Candidate) (`origin=generated`) — its `sound` is the
  canonical, by-value [`SoundRecord`](_autosummary/foley.html.md#foley.SoundRecord) (a content-hash id); its `notes`
  list every dropped, clamped or substituted parameter, and
  `cost_estimate_usd` / `cost_actual_usd` what it cost (`None` = unknown).
* **Raises:**
  * [**SafetyRefusal**](_autosummary/foley.html.md#foley.SafetyRefusal) – If the prompt trips a safety gate and `on_flagged='refuse'`
        (a [`GenerationError`](_autosummary/foley.html.md#foley.GenerationError) subclass — `TrademarkRefusal` /
        `RecognizableVoiceRefusal`).
  * [**GenerationError**](_autosummary/foley.html.md#foley.GenerationError) – If the backend yields no stored sound (QC-quarantined,
        rights-blocked, or a synthesis/ingest error). The message carries the root
        cause and the exception is chained to it; it also carries the full
        `report` and terminal `status` so callers can react distinctly.
  * [**SourceConfigurationError**](_autosummary/foley.sources.base.html.md#foley.sources.base.SourceConfigurationError) – If the backend is not configured (a missing key or
        plan) — the message names the env var and where to get the key.
  * **UnsupportedParameter** – For a meaning-carrying parameter the backend cannot
        honour (see `on_unsupported`).
  * **BudgetExceeded / CostApprovalRequired** – Before a paid call the budget refuses.
  * [**EgressBlocked**](_autosummary/foley.runtime.html.md#foley.runtime.EgressBlocked) – For an external backend under [`offline()`](_autosummary/foley.html.md#foley.offline), or
        [`ModelNotCached`](_autosummary/foley.runtime.html.md#foley.runtime.ModelNotCached) for a local one whose weights are
        not on this machine.

### foley.has_nan_inf(samples)

Return `True` if any sample is `NaN` or `Inf` (corrupt-clip guard).

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### foley.hybrid_search(query, , embedder, vindex, kindex, k=10, candidate_k=50, rrf_k=60, where=None)

Embed `query`, run the vector + keyword rankers, and RRF-fuse them.

* **Parameters:**
  * **query** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The natural-language query.
  * **embedder** ([`Embedder`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.Embedder)) – Text<->audio embedder (its `embed_text` produces the query
    vector).
  * **vindex** ([`VectorIndex`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.VectorIndex)) – The vector index (CLAP KNN).
  * **kindex** ([`KeywordIndex`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.KeywordIndex)) – The keyword index (BM25).
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Number of fused results to return.
  * **candidate_k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Shortlist depth pulled from each ranker before fusion.
  * **rrf_k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The RRF damping constant.
  * **where** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Optional metadata push-down passed to both rankers.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`FusedHit`](_autosummary/foley.index.search.html.md#foley.index.search.FusedHit)]
* **Returns:**
  The top-`k` fused :class:

  ```
  `
  ```

  FusedHit\`s.

### foley.ingest(path, , library=None, backend='local', qc=True, recursive=True, \*\*kw)

Ingest a folder (or single file) of sounds into the default library.

`probe -> QC -> tag -> zero-shot -> caption -> embed -> SoundRecord` for
each file, returning an [`IngestReport`](_autosummary/foley.index.html.md#foley.index.IngestReport). See
[`foley.index.ingest_folder()`](_autosummary/foley.index.html.md#foley.index.ingest_folder) / [`ingest_one()`](_autosummary/foley.index.html.md#foley.index.ingest_one) for the
per-file options (`license`, taggers, `min_status`, …).

* **Parameters:**
  * **path** – A folder (walked) or a single audio file.
  * **library** – Target library (default: the process-wide default library).
  * **backend** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – `"local"` ingests filesystem audio; other backends (a source
    adapter pull) route through `add_from` (subtask #5) — kept in the
    signature for forward-compat.
  * **qc** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Run the Tier-0 QC gate (quarantines failing clips).
  * **recursive** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Recurse into sub-folders.
  * **\*\*kw** – Forwarded to [`foley.index.ingest_one()`](_autosummary/foley.index.html.md#foley.index.ingest_one) — notably `license`:
    omit it and the files’ rights are **unknown** (indexed, but
    [`keep()`](_autosummary/foley.html.md#foley.keep) refuses them for every use); pass `license="user-owned"`
    to assert you own them (#55).

### foley.ingest_folder(path, , library=None, recursive=True, exts=('.wav', '.flac', '.aiff', '.aif', '.ogg', '.mp3', '.opus', '.m4a'), on_error='collect', \*\*ingest_one_kw)

Ingest every audio file under `path` and return an [`IngestReport`](_autosummary/foley.html.md#foley.IngestReport).

* **Parameters:**
  * **path** – A folder (walked) or a single audio file.
  * **library** – Target library (default: the process-wide default).
  * **recursive** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Recurse into sub-folders.
  * **exts** ([`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`...`](https://docs.python.org/3/builtins/constants.html#Ellipsis)]) – Audio extensions to ingest.
  * **on_error** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – `'collect'` records per-file errors and continues;
    `'raise'` re-raises the first error. Either way an
    [`EgressBlocked`](_autosummary/foley.runtime.html.md#foley.runtime.EgressBlocked) (offline, the embedder’s weights not
    cached) raises at once: every file would fail the same way.
  * **\*\*ingest_one_kw** – Forwarded to [`ingest_one()`](_autosummary/foley.html.md#foley.ingest_one) (license, taggers, QC
    flags, …).
* **Return type:**
  [`IngestReport`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestReport)
* **Returns:**
  An [`IngestReport`](_autosummary/foley.html.md#foley.IngestReport) (with `.summary()` counts and per-file results).

### foley.ingest_one(src, , library=None, sound_id=None, source_uri=None, license=None, tagger=None, zeroshot_tagger=None, captioner=None, do_qc=True, min_status=QCStatus.warn, do_supervised=True, do_zeroshot=True, do_caption=True, thresholds=QCThresholds(clip_full_scale=0.999, clip_min_run=3, clip_reject_ratio=0.0001, clip_reject_run=10, true_peak_max_dbtp=-1.0, true_peak_oversample=4, dc_offset_fail=0.01, dc_offset_warn=0.001, silence_rms_dbfs=-60.0, snr_clean_db=20.0, snr_quiet_percentile=10.0, snr_frame_s=0.025, snr_hop_s=0.01, edge_rel_peak_dbfs=-40.0, edge_fade_s=0.01, lufs_gate_floor=-70.0, lufs_outlier_lu=6.0, duration_min_s=0.1, deliver_min_sample_rate=44100), store=True, allow_ai_training_forbidden=False, seed_tags=None, commercial=None)

Ingest one clip into `library` and return an [`IngestResult`](_autosummary/foley.html.md#foley.IngestResult).

Pipeline: probe + decode-once -> content-address dedup -> QC gate -> embed
(once) -> supervised + zero-shot tags -> caption -> resolve UCS -> assemble
`SoundRecord` -> [`SoundLibrary.add()`](_autosummary/foley.html.md#foley.SoundLibrary.add).

* **Parameters:**
  * **src** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike), [`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes), [`BinaryIO`](https://docs.python.org/3/library/typing.html#typing.BinaryIO)]) – A path, `bytes`, or file-like audio source.
  * **library** – Target [`SoundLibrary`](_autosummary/foley.index.library.html.md#foley.index.library.SoundLibrary) (default: the
    process-wide default library).
  * **sound_id** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional canonical id override. Defaults to `None` → the
    content-hash of the decoded PCM (the local-ingest identity, used as the
    record `id` and dedup key). A live source adapter passes a short,
    case-stable, source-native id (e.g. `'freesound:12345'`) so dedup keys
    on the stable id rather than on re-fetched (lossy, byte-varying) preview
    bytes; when it does, the PCM hash is computed only for the (skipped)
    default and is not persisted. Separately, `content_sha256` records the
    hash of the stored FLAC **archive** bytes (set by `store_sound`), which
    is a different byte source from this PCM hash.
  * **source_uri** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional by-reference fetchable URI override. Defaults to
    `None` → the resolved local path when `src` is path-like. A live
    adapter passes the stable source page URL (e.g.
    `'https://freesound.org/s/12345/'`) that [`foley.stores.store_sound()`](_autosummary/foley.stores.html.md#foley.stores.store_sound)
    requires for a by-reference sound.
  * **license** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)]) – Rights record, or a `license_id` string the caller asserts
    (`'user-owned'`), or `None` (default): rights **unknown**, so the clip
    is indexed but [`foley.keep()`](_autosummary/foley.html.md#foley.keep) refuses it (#55). See
    `resolve_ingest_license()`.
  * **tagger** – Supervised [`Tagger`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.Tagger) (default: PANNs
    via [`default_tagger()`](_autosummary/foley.index.taggers.html.md#foley.index.taggers.default_tagger)).
  * **zeroshot_tagger** – Zero-shot tagger (default: CLAP via
    [`default_zeroshot_tagger()`](_autosummary/foley.index.taggers.html.md#foley.index.taggers.default_zeroshot_tagger)).
  * **captioner** – Optional [`Captioner`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.Captioner) (default:
    none — the caption stage is off unless one is injected).
  * **do_qc** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Run the Tier-0 QC gate.
  * **min_status** ([`QCStatus`](_autosummary/foley.qc.html.md#foley.qc.QCStatus)) – Admission floor — a QC status worse than this is quarantined
    (default `warn`: only `fail` clips are rejected).
  * **do_caption** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Toggle each enrichment stage.
  * **thresholds** ([`QCThresholds`](_autosummary/foley.qc.html.md#foley.qc.QCThresholds)) – QC thresholds.
  * **store** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – If `False`, assemble the record but do not add it to the library
    (probe/QC/enrich only).
  * **seed_tags** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`list`](https://docs.python.org/3/builtins/stdtypes.html#list)]) – Optional caller-supplied tags (e.g. a corpus’s folder-path
    taxonomy) unioned into the record’s `tags` alongside the
    supervised/zero-shot tags — so they feed the BM25 keyword index.
  * **allow_ai_training_forbidden** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – The universal fail-closed rights gate. A
    sound whose license has `ai_training_ok=False` (e.g. Sonniss,
    BBC RemArc) is refused with status `'rights_blocked'` *before* it is
    embedded or stored — CLAP-embedding-and-persisting is itself a form of
    AI training on the corpus. Pass `True` to record explicit operator
    consent and admit it anyway (see `foley.bootstrap.bootstrap()`’s
    `accept_ai_restricted`). Protects every ingest path, not just
    bootstrap.
  * **commercial** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`bool`](https://docs.python.org/3/builtins/functions.html#bool)]) – Whether the AI use is for a commercial purpose (decides a
    `nc_open_source_only` scope, #69). `None` (default) means
    [`DEFAULT_INTENDED_USE`](_autosummary/foley.licensing.html.md#foley.licensing.DEFAULT_INTENDED_USE)’s (commercial).
* **Return type:**
  [`IngestResult`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult)
* **Returns:**
  An [`IngestResult`](_autosummary/foley.html.md#foley.IngestResult); its `record` is `None` when quarantined, a
  duplicate, or rights-blocked.

### foley.install_agent_kit(dest='./.claude', , overwrite=False)

Copy the shipped skill + slash command + subagent into `dest` (a `.claude` dir).

Installs:

* `dest/skills/foley-sound-design/` — the consumer skill (the sound-design playbook),
* `dest/commands/foley-score.md` — the `/foley-score` slash command,
* `dest/agents/sound-designer.md` — the `sound-designer` subagent.

* **Parameters:**
  * **dest** – The target agent-config dir (default `./.claude` in the cwd; pass `~/.claude`
    to install globally for every project).
  * **overwrite** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Replace existing files/dirs (default: skip what already exists).
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]
* **Returns:**
  The list of installed paths (as strings) — empty entries that already existed are skipped.

### foley.is_offline()

Whether an offline runtime scope is currently active.

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### foley.is_silent(samples, , rms_floor_dbfs=-60.0)

Return `True` when whole-clip RMS falls below `rms_floor_dbfs`.

A zero (exactly silent) clip has RMS `0` -> `-inf` dBFS -> `True`.

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### foley.keep(record, intended_use)

Fail-closed candidate license gate (report 07 §8.2).

Run BEFORE ranking/verification in the agent’s `decide()`. Unknown or
unverified rights => reject. Any single unmet requirement => reject.

* **Parameters:**
  * **record** ([`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)) – The candidate’s rights record.
  * **intended_use** ([`IntendedUse`](_autosummary/foley.base.html.md#foley.base.IntendedUse)) – The caller’s declared intent.
* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)
* **Returns:**
  `True` only if every requirement in `intended_use` is satisfied by
  `record`; `False` otherwise (including unverified rights).

### foley.keep_sound(sound_record, intended_use)

Convenience: apply [`keep()`](_autosummary/foley.html.md#foley.keep) to a `SoundRecord`’s nested license.

* **Parameters:**
  * **sound_record** – A [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord) (its `.license` is the
    SSOT consulted).
  * **intended_use** ([`IntendedUse`](_autosummary/foley.base.html.md#foley.base.IntendedUse)) – The caller’s declared intent.
* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)
* **Returns:**
  The result of `keep(sound_record.license, intended_use)`.

### foley.lancedb_available()

True if `lancedb` is importable (the `foley[index]` extra is present).

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### foley.license_id_from_cc_url(url)

Map a Creative-Commons license URL **or label** to `(license_id, verified)`.

The single SSOT for turning an external source’s license string into a foley
`license_id` (FSD50K, Clotho, the Freesound API, …). It **never widens rights**:
the string is split into tokens, and

* any NoDerivatives / ShareAlike sign (`nd`, `sa`, `no derivatives`,
  `share alike`, `sharealike`) → `('unknown', False)`: foley has no row
  expressing those restrictions, so the sound is refused everywhere;
* the Public Domain Mark → `('PDM-1.0', False)`: a claim about the work, not a
  licence anyone granted, so never auto-verified (#56);
* CC0 (`publicdomain/zero`, `cc0`, `creative commons 0`) → `CC0-1.0`;
* Sampling+ → `CC-Sampling+-1.0`;
* `by` / `attribution` with any mention of commercial use (`nc`,
  `noncommercial`, `non commercial`, `no commercial use`) → `CC-BY-NC-<v>`,
  else `CC-BY-<v>`. **The version is kept** (`/by/3.0/` → `CC-BY-3.0`); a
  versionless label gets `DEFAULT_CC_VERSION`; a version foley has no row
  for (a `2.1/jp` port) fails closed;
* anything else — including a string with a token this parser does not know —
  → `('unknown', False)`.

* **Parameters:**
  **url** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – A CC license URL, a CC label string, or `None`.
* **Return type:**
  [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`bool`](https://docs.python.org/3/builtins/functions.html#bool)]
* **Returns:**
  `(license_id, rights_verified)`.

### foley.license_meta(license_id)

Return the display [`LicenseMeta`](_autosummary/foley.html.md#foley.LicenseMeta) for `license_id` (fail-closed fallback).

* **Parameters:**
  **license_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The normalized license id.
* **Return type:**
  [`LicenseMeta`](_autosummary/foley.licensing.html.md#foley.licensing.LicenseMeta)
* **Returns:**
  The mapped [`LicenseMeta`](_autosummary/foley.html.md#foley.LicenseMeta), or `UNKNOWN_LICENSE_META` for an
  unrecognized / `Proprietary-*` id.

### foley.list_sources(, egress_allow=None)

Return the names of registered live sources (runs discovery first).

* **Parameters:**
  **egress_allow** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`frozenset`](https://docs.python.org/3/builtins/stdtypes.html#frozenset)]) – If given, keep only sources whose declared
  `config['data_egress']` is in this set (the local-first / offline
  filter — see [`foley.runtime.RuntimeConfig`](_autosummary/foley.runtime.html.md#foley.runtime.RuntimeConfig)). A source that does
  not declare `data_egress` is **excluded** (fail-closed).
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]

### foley.load(src, , target_sr=None, mono=False, dtype='float32')

Decode audio into a float working array.

`src` may be a filesystem path, raw encoded `bytes` (wrapped in a
`BytesIO` so nothing touches disk), or any binary file-like object.

* **Parameters:**
  * **src** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike), [`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes), [`BinaryIO`](https://docs.python.org/3/library/typing.html#typing.BinaryIO)]) – Path, raw bytes, or file-like object to decode.
  * **target_sr** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`int`](https://docs.python.org/3/builtins/functions.html#int)]) – If given, resample the decoded audio to this rate (via
    [`resample()`](_autosummary/foley.html.md#foley.resample)); otherwise the native rate is returned.
  * **mono** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – If `True`, down-mix multichannel audio to mono.
  * **dtype** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – NumPy dtype string for the returned array (default `float32`).
* **Return type:**
  [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[`ndarray`, [`int`](https://docs.python.org/3/builtins/functions.html#int)]
* **Returns:**
  A `(samples, sample_rate)` tuple. `samples` has shape `(frames,)`
  (mono) or `(frames, channels)`; `sample_rate` reflects any resample.

Lazy dependencies: `soundfile` (and `soxr` when `target_sr` differs).

### foley.loudness_normalize(samples, sample_rate, , target_lufs=-16.0, peak_ceiling_dbfs=-1.0, min_block_s=0.4)

Loudness-normalize to `target_lufs`, then keep it peak-safe.

Integrated loudness is measured (ITU-R BS.1770-4 / EBU R128), the signal is
scaled to `target_lufs`, and finally attenuated so its sample peak sits at
or below `peak_ceiling_dbfs`. Two inputs are returned **unchanged** (flag
them, don’t amplify): near-silent input (measured loudness at or below
`LUFS_GATE_FLOOR`), and a clip shorter than one BS.1770 gating block
(`min_block_s`) — which `pyloudnorm` cannot measure and would otherwise
raise `ValueError` on (routine for one-shots: clicks, blips, gunshots).

* **Parameters:**
  * **samples** (`ndarray`) – Working array (mono or multichannel, time on axis 0 — the layout
    pyloudnorm expects).
  * **sample_rate** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Sample rate in Hz.
  * **target_lufs** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Desired integrated loudness (default = foley’s podcast
    target).
  * **peak_ceiling_dbfs** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Sample-peak ceiling (dBFS) applied after loudness
    normalization. Note this is a *sample*-peak limit, not an inter-sample
    true-peak (dBTP) limit — see [`foley.qc.true_peak_dbtp()`](_autosummary/foley.qc.html.md#foley.qc.true_peak_dbtp) for the
    oversampled measurement.
  * **min_block_s** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Minimum clip length (seconds) that can be loudness-measured;
    shorter clips are returned unchanged with `measured = -inf`.
* **Return type:**
  [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[`ndarray`, [`float`](https://docs.python.org/3/builtins/functions.html#float)]
* **Returns:**
  `(normalized, measured_input_lufs)`. When the input is near-silent or
  too short to measure, `normalized` is the unchanged input and
  `measured_input_lufs` is at or below `LUFS_GATE_FLOOR` (`-inf` for
  the too-short case).

Lazy dependencies: `pyloudnorm` (+ `numpy`).

### foley.make_byte_store(rootdir=PosixPath('/home/runner/.local/share/foley/audio'))

Build the content-addressable blob store: `Mapping[content_key -> bytes]`.

The local default is `dol.Files` (bytes values on disk). For cloud storage,
build the equivalent store from any `dol` Mapping (e.g. an S3 store) and pass
it directly to [`store_sound()`](_autosummary/foley.html.md#foley.store_sound) instead of calling this factory — the
`store_sound` gate treats `sounds` as an opaque `MutableMapping`.

* **Parameters:**
  **rootdir** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]) – Directory that holds the blobs (created if missing).
* **Return type:**
  [`MutableMapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.MutableMapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes)]
* **Returns:**
  A `MutableMapping[str, bytes]` keyed by [`content_key()`](_autosummary/foley.html.md#foley.content_key).

### foley.make_http_app(, auth, path='/mcp', json_response=False, library=None, runtime=None, byte_store=None, include=None, name='foley')

Build a bearer-auth-gated ASGI app serving the foley MCP tools over streamable HTTP.

HTTP exposes the tool surface to the network, so `auth` is \*\*required and
fail-closed\*\*: pass `auth={'bearer_tokens': [...]}` (a non-empty iterable of accepted
tokens) — a request without a matching `Authorization: Bearer <token>` header gets a
`401`. The returned app (a Starlette/ASGI callable) mounts in any ASGI host (uvicorn,
gunicorn, a parent FastAPI). `py2mcp` / `fastmcp` are imported lazily inside
[`build_mcp_server()`](_autosummary/foley.html.md#foley.build_mcp_server), so `import foley` stays dol-only.

* **Parameters:**
  * **auth** ([`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)) – `{'bearer_tokens': [...]}` — required; empty/missing raises (fail-closed).
  * **path** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The MCP HTTP mount path.
  * **json_response** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Return a single JSON response instead of an SSE stream (simple clients).
  * **name** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – As [`build_mcp_server()`](_autosummary/foley.html.md#foley.build_mcp_server).
* **Returns:**
  An ASGI application (the bearer-gated MCP HTTP app).
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If `auth` carries no bearer tokens (no anonymous HTTP access).

### foley.make_meta_store(rootdir=PosixPath('/home/runner/.local/share/foley/meta'))

Build the metadata store: `Mapping[sound_id -> SoundRecord]` (JSON files).

`SoundRecord` values are (de)serialized transparently via the
`SerializableMixin` (`to_dict` / `from_dict`); each record is written as
a percent-encoded `{sound_id}.json` file while the store’s keys stay the
bare `sound_id` (invariant #3 — the id is escaped at this boundary so an
externally-derived id can never escape `rootdir` or collide via `/`/`..`).

* **Parameters:**
  **rootdir** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]) – Directory that holds the metadata JSON files (created if missing).
* **Return type:**
  [`MutableMapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.MutableMapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord)]
* **Returns:**
  A `MutableMapping[str, SoundRecord]` keyed by `sound_id`.

### foley.make_run_store(rootdir=PosixPath('/home/runner/.local/share/foley/runs'))

Build the run-artifact store: `Mapping[run_id -> RunManifest dict]` (JSON files).

The by-value carrier for #11’s reproducible run-manifests (one per instrumented
`find()` / `generate()` / … ). An exact sibling of `make_provenance_store()`:
escapes the `run_id` to a safe `{enc}.json` filename (invariant #3) while
exposing bare `run_id` keys; values are plain dicts ((de)serialized by
`dol.JsonFiles`). Local by default; swap in any `dol` Mapping for the cloud.

* **Parameters:**
  **rootdir** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]) – Directory that holds the run JSON files (created if missing).
* **Return type:**
  [`MutableMapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.MutableMapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]
* **Returns:**
  A `MutableMapping[str, dict]` keyed by `run_id`.

### foley.make_session_store(session_id='default', name='picks', , rootdir=None)

Build a per-session JSON store: `Mapping[key -> dict]` under `sessions/{id}/{name}/`.

The by-value carrier for #12’s audition state — one store per namespace
(`candidates` / `picks` / `rejects`). A sibling of [`make_run_store()`](_autosummary/foley.html.md#foley.make_run_store):
percent-encodes each key to a safe `{enc}.json` filename (invariant #3) while
exposing bare keys; values are plain dicts. Local by default; swap in any `dol`
Mapping for the cloud.

* **Parameters:**
  * **session_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The session namespace (default `'default'`).
  * **name** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The store namespace within the session (`candidates` / `picks` /
    `rejects`).
  * **rootdir** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)], [`None`](https://docs.python.org/3/builtins/constants.html#None)]) – Root sessions directory (default: `DEFAULT_SESSION_DIR`).
* **Return type:**
  [`MutableMapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.MutableMapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]
* **Returns:**
  A `MutableMapping[str, dict]` keyed by the bare key.

### foley.mcp_server(, library=None, session='default', runtime=None, byte_store=None, include=None, name='foley', max_usd=None)

Build the foley MCP server (lazy `py2mcp`); registers the JSON-safe tool surface.

Validates that every source declares a `data_egress` (fail-closed), binds the
injectable library / runtime / byte-store, and hands the resolved tool functions to
`py2mcp.mk_mcp_server`. Never starts a server or touches the network.

* **Parameters:**
  * **library** – The [`foley.index.SoundLibrary`](_autosummary/foley.index.html.md#foley.index.SoundLibrary) (default: the shared one).
  * **session** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The default session id.
  * **runtime** – A [`foley.runtime.RuntimeConfig`](_autosummary/foley.runtime.html.md#foley.runtime.RuntimeConfig) (default: the active one).
  * **byte_store** – A `MutableMapping[str, bytes]` for previews / rendered mixes.
  * **include** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]) – Optional subset of tool names to expose.
  * **name** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The MCP server name.
  * **max_usd** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`float`](https://docs.python.org/3/builtins/functions.html#float)]) – The server’s cumulative spend cap across every paid tool call, for
    its lifetime (default [`foley.cost.default_max_usd()`](_autosummary/foley.cost.html.md#foley.cost.default_max_usd): $1, or $3 under
    `$FOLEY_LLM=anthropic`).
* **Returns:**
  A `fastmcp.FastMCP` server.

### foley.measure_lufs(samples, sample_rate, , gate_floor_lufs=-70.0, min_block_s=0.4)

Integrated loudness (LUFS, ITU-R BS.1770-4) via `pyloudnorm` (lazy).

Returns `None` when `pyloudnorm` is unavailable, the clip is shorter than
one gating block (`min_block_s`), the samples are non-finite, or the
measured loudness is at/below the gate floor (near-silent / unstable — do
not amplify, just flag).

* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`float`](https://docs.python.org/3/builtins/functions.html#float)]

### foley.needs_edge_fade(samples, , rel_peak_dbfs=-40.0)

Return `True` when the first or last sample sits above `rel_peak_dbfs`
relative to the clip peak — i.e. a nonzero boundary that clicks under
narration and needs a short fade.

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### foley.offline(config=None)

Alias of `offline_scope()` — `with foley.offline(): ...` for local-first runs.

### foley.parse_ucs_filename(filename, , table=None)

Parse a UCS-conformant filename to `(ucs_category, ucs_subcategory)`.

Fail-quiet: returns `(None, None)` when the name is not UCS-conformant or
its CatID token is unknown (so a wrong subcategory is never emitted).

* **Parameters:**
  * **filename** – A path or filename (only the basename’s token 0 is used).
  * **table** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`UcsTable`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.UcsTable)]) – The UCS table to resolve against (defaults to
    `default_ucs_table()`).
* **Return type:**
  [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)], [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]

### foley.plan(candidates, , transcript=None)

Fold verified candidates into the SPARSE [`SoundDesignTimeline`](_autosummary/foley.html.md#foley.SoundDesignTimeline) (the SELECT→WEAVE bridge).

One [`TimelineItem`](_autosummary/foley.html.md#foley.TimelineItem) per candidate (`onset·gain·layer·loop` only, from its
[`SoundEvent`](_autosummary/foley.html.md#foley.SoundEvent)), joined to the run-artifact via `run_manifest_ref` — the
reserved #8 `plan_ref` slot is filled when called inside an active `foley.obs`
run scope (`None`-safe otherwise).

* **Parameters:**
  * **candidates** ([`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]) – The candidates returned by [`find()`](_autosummary/foley.html.md#foley.find).
  * **transcript** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional narration transcript (WEAVE resolves the reference).
* **Return type:**
  [`SoundDesignTimeline`](_autosummary/foley.base.html.md#foley.base.SoundDesignTimeline)

### foley.preview(candidate_or_id, , seconds=6, library=None, byte_store=None, session=None)

Produce a short audition of a sound; set its `preview_uri` to a store key.

Writes the first `seconds` of the clip (FLAC) into `byte_store` under its
content key and points `Candidate.preview_uri` at that key — referencing the
audio, never returning bytes. Fail-safe: if the audio codec extra (`foley[audio]`)
or the clip is unavailable, `preview_uri` is `None` (the sound id + duration
still let a client fetch it).

* **Parameters:**
  * **candidate_or_id** – A [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate) or a sound id.
  * **seconds** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Audition length.
  * **library** – The [`foley.index.SoundLibrary`](_autosummary/foley.index.html.md#foley.index.SoundLibrary) (default: the shared one).
  * **byte_store** – A `MutableMapping[str, bytes]` to hold the preview (default: none —
    then `preview_uri` stays `None`).
  * **session** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`SessionStore`](_autosummary/foley.agent.session.html.md#foley.agent.session.SessionStore)]) – Optional session (unused here; accepted for a uniform signature).
* **Return type:**
  [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)
* **Returns:**
  The candidate with `preview_uri` set (or `None` on graceful degradation).

### foley.reciprocal_rank_fusion(ranked_id_lists, , k=60)

Fuse several ranked id lists into one, by reciprocal rank.

* **Parameters:**
  * **ranked_id_lists** ([`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]) – Each element is a list of ids in descending-relevance
    order (best first). Lists may overlap and may differ in length.
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The RRF damping constant (default `RRF_K` = 60).
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]
* **Returns:**
  `[(id, fused_score), ...]` sorted by fused score descending, ties
  broken by `id` ascending (so the fusion is fully deterministic).

### foley.refine(session=None, , query=None, picked_ids=(), rejected_ids=(), hint=None, n=3, k=10, library=None, refiner=None)

Relevance-feedback refinement: expand for recall, boost picks, drop rejects, re-rank.

Distinct from [`foley.refine_query()`](_autosummary/foley.html.md#foley.refine_query) (which only paraphrases a query): this reads
the session’s picks/rejects (or the explicit `picked_ids` / `rejected_ids`),
expands the query into paraphrases for recall, gathers neighbours of every pick, drops
the rejects, and re-ranks by score.

* **Parameters:**
  * **session** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`SessionStore`](_autosummary/foley.agent.session.html.md#foley.agent.session.SessionStore)]) – The audition session (source of picks/rejects when not passed explicitly).
  * **query** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – The base text query to expand (optional).
  * **rejected_ids** ([`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`...`](https://docs.python.org/3/builtins/constants.html#Ellipsis)]) – Explicit feedback (override the session’s).
  * **hint** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – A steer for the query expansion.
  * **n** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Paraphrases to request.
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Result depth.
  * **library** – The [`foley.index.SoundLibrary`](_autosummary/foley.index.html.md#foley.index.SoundLibrary) (default: the shared one).
  * **refiner** – The query-expansion seam (default: the deterministic fake).
* **Return type:**
  [`RefineResult`](_autosummary/foley.agent.preview.html.md#foley.agent.preview.RefineResult)
* **Returns:**
  A `RefineResult`.

### foley.refine_query(query, , n=3, hint=None, refiner=None, \_span=None)

Expand `query` into up to `n` paraphrases for multi-query retrieval.

* **Parameters:**
  * **query** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The event query to expand.
  * **n** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Number of paraphrases.
  * **hint** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional verify-failure reason to steer re-retrieval.
  * **refiner** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Refiner`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Refiner)]) – An injected [`Refiner`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Refiner) (the DI seam);
    defaults to `_default_refiner()`.
  * **\_span** – Internal — the obs span handle for GenAI recording.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]

### foley.register_source(name, config, adapter=None)

Register a live source directly (out-of-tree plugin or a test double).

Overwrites any existing entry for `name` — the seam a test uses to inject a
fake-transport-backed adapter. If `adapter` is `None` it is lazily built
from `config` on first `get_source()` (the source must then be an
importable `foley.sources.<name>` package).

* **Parameters:**
  * **name** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The source name (the [`add_from()`](_autosummary/foley.html.md#foley.add_from) / `get_source()` key).
  * **config** ([`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)) – The `SOURCE_CONFIG` declaration.
  * **adapter** – An optional pre-instantiated adapter (bypasses lazy loading).
* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### foley.resample(samples, sample_rate, , target_sr=48000, quality='HQ')

Resample `samples` to `target_sr` (a no-op when already there).

* **Parameters:**
  * **samples** (`ndarray`) – Working array (mono `(frames,)` or `(frames, channels)`).
  * **sample_rate** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The array’s current rate in Hz.
  * **target_sr** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Desired output rate in Hz (default = the working rate).
  * **quality** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – soxr quality preset (`QQ`/`LQ`/`MQ`/`HQ`/`VHQ`).
* **Return type:**
  `ndarray`
* **Returns:**
  The resampled array (the input unchanged when `sample_rate ==
  target_sr`). dtype is preserved by soxr.

Lazy dependency: `soxr`.

### foley.resolve_catid(, tags=(), caption=None, audioset_labels=(), filename=None, table=None, audioset_map=None)

Resolve inputs to a best UCS CatID by the staged precedence.

* **Parameters:**
  * **tags** ([`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Free tags on the sound.
  * **caption** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Free-text caption/description.
  * **audioset_labels** ([`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – AudioSet MIDs or names (e.g. from PANNs).
  * **filename** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional UCS-style filename/path (its token-0 CatID wins if
    recognized).
  * **table** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`UcsTable`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.UcsTable)]) – UCS table (defaults to `default_ucs_table()`).
  * **audioset_map** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`AudioSetUcsMap`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.AudioSetUcsMap)]) – AudioSet->UCS map (defaults to
    [`default_audioset_ucs_map()`](_autosummary/foley.index.taxonomy.audioset.html.md#foley.index.taxonomy.audioset.default_audioset_ucs_map)).
* **Return type:**
  [`CatIdResolution`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.CatIdResolution)
* **Returns:**
  A [`CatIdResolution`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.CatIdResolution) (falsy when
  nothing resolved).

### foley.resolve_master(master)

Resolve a master spec (profile name, explicit profile, or `None`) to a `MasterProfile`.

* **Parameters:**
  **master** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`MasterProfile`](_autosummary/foley.base.html.md#foley.base.MasterProfile), [`None`](https://docs.python.org/3/builtins/constants.html#None)]) – A `MASTER_PROFILES` key (e.g. `'podcast'`), an explicit
  [`MasterProfile`](_autosummary/foley.html.md#foley.MasterProfile), or `None` (-> the podcast default).
* **Return type:**
  [`MasterProfile`](_autosummary/foley.base.html.md#foley.base.MasterProfile)
* **Returns:**
  The resolved [`MasterProfile`](_autosummary/foley.html.md#foley.MasterProfile).
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If `master` is an unknown profile name.

### foley.restamp_rights(library=None, , license='unknown', ids=None, where=None, select='legacy-user-owned', apply=False)

Re-stamp the licence of stored sounds; a dry run unless `apply=True`.

The migration for libraries built before #55 / #56. A re-ingest does not do it
(content-addressed dedup skips files already stored), so this rewrites the stored
records in place:

* `foley restamp-rights --apply` — every sound an older foley stamped
  `user-owned` without being told becomes `unknown` (fail closed);
* `foley restamp-rights --license user-owned --apply` — the same sounds, but you
  assert that you own them (now recorded with a `verified_at` timestamp);
* `foley restamp-rights --select legacy-elevenlabs --license elevenlabs-paid-plan
  --apply` — ElevenLabs generations made before the plan was explicit;
* `foley restamp-rights --select has-license-url --license from-url --apply` —
  every record that kept its source’s licence string (Freesound pulls) is
  re-derived by today’s mapper (CC versions, PDM, NC spellings);
* corpus clips (Clotho, FSD50K) are repaired by re-running `foley bootstrap`,
  which re-stamps already-stored clips from the corpus metadata.

* **Parameters:**
  * **library** – The [`SoundLibrary`](_autosummary/foley.index.library.html.md#foley.index.library.SoundLibrary) (default: the default one).
  * **license** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The `license_id` to stamp (a `LICENSE_FLAGS` row; default
    `'unknown'`), or `FROM_LICENSE_URL` to re-derive each record from
    its own `license_url`.
  * **ids** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]) – Re-stamp exactly these sound ids (overrides `where` / `select`).
  * **where** – A predicate `LicenseRecord -> bool` choosing the records (overrides
    `select`).
  * **select** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – A `LEGACY_SELECTORS` name (default `'legacy-user-owned'`).
  * **apply** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Write the change. `False` (default) only reports what would change.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]
* **Returns:**
  The ids that were (or, in a dry run, would be) re-stamped.
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If `license` or `select` is unknown.

### foley.run_qc(samples, sample_rate, , thresholds=QCThresholds(clip_full_scale=0.999, clip_min_run=3, clip_reject_ratio=0.0001, clip_reject_run=10, true_peak_max_dbtp=-1.0, true_peak_oversample=4, dc_offset_fail=0.01, dc_offset_warn=0.001, silence_rms_dbfs=-60.0, snr_clean_db=20.0, snr_quiet_percentile=10.0, snr_frame_s=0.025, snr_hop_s=0.01, edge_rel_peak_dbfs=-40.0, edge_fade_s=0.01, lufs_gate_floor=-70.0, lufs_outlier_lu=6.0, duration_min_s=0.1, deliver_min_sample_rate=44100))

Run every Tier-0 check and fold the results into a [`QCReport`](_autosummary/foley.html.md#foley.QCReport).

Status rules (evaluated in order):
: FAIL if `has_nan_inf` OR `is_silent` OR
  `clipped_max_run >= clip_reject_run` OR
  `clipped_ratio > clip_reject_ratio` OR `duration_s < duration_min_s`.
  WARN if `dc_offset > dc_offset_fail` OR `needs_edge_fade` OR
  (`snr_db` is a finite value `< snr_clean_db`) OR
  (`true_peak_dbtp` is a finite value `> true_peak_max_dbtp`).
  Otherwise PASS.

Each firing condition appends a human-readable string to `notes`. Two
thresholds are intentionally NOT evaluated on a single source clip here
because they belong to later stages: the library-median loudness-outlier
check (`+/- lufs_outlier_lu`, a library-level concern) and the delivery
sample-rate target (`deliver_min_sample_rate`, enforced at the weave/master
stage).

* **Parameters:**
  * **samples** (`ndarray`) – Waveform in `[-1, 1]` (mono or `(frames, channels)`).
  * **sample_rate** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Sample rate in Hz.
  * **thresholds** ([`QCThresholds`](_autosummary/foley.qc.html.md#foley.qc.QCThresholds)) – Overridable QC thresholds (defaults to shipped values).
* **Return type:**
  [`QCReport`](_autosummary/foley.qc.html.md#foley.qc.QCReport)
* **Returns:**
  A populated [`QCReport`](_autosummary/foley.html.md#foley.QCReport).

### foley.save(samples, sample_rate, dst, , fmt='flac', subtype='PCM_24')

Write `samples` to `dst` as `fmt`/`subtype` (default = FLAC archive).

* **Parameters:**
  * **samples** (`ndarray`) – The working array to write (shape `(frames,)` or
    `(frames, channels)`).
  * **sample_rate** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Sample rate in Hz.
  * **dst** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike), [`BinaryIO`](https://docs.python.org/3/library/typing.html#typing.BinaryIO)]) – Destination path or writable binary file-like object.
  * **fmt** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Container/codec name (case-insensitive; passed to libsndfile).
  * **subtype** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Sample subtype (e.g. `PCM_24`, `PCM_16`, `FLOAT`).
* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

Lazy dependency: `soundfile`.

### foley.score(segments, , audio=None, transcript=None, library=None, intended_use=None, commercial_ok=None, max_events=6, verify='listen', master='podcast', weave=None, llm=None, budget=None, \*\*weave_kwargs)

Choose sounds for narration text and (optionally) weave them into the narration audio.

Progressive disclosure — the AI-first headline:

```default
foley.score("She pushed open the heavy oak door; rain hammered outside.")  # plan only
foley.score(segments, audio="narration.wav")  # + mastered mix, captions, credits
```

For each segment it runs the SELECT loop (`decompose → search → verify → decide`) with
the fail-closed license gate and tasteful restraint, folds the chosen sounds into ONE
editable [`SoundDesignTimeline`](_autosummary/foley.base.html.md#foley.base.SoundDesignTimeline), and — when `audio` is given (or
`weave=True`) — aligns + weaves into a mastered mix. Returns a [`ScoreResult`](_autosummary/foley.html.md#foley.ScoreResult).

* **Parameters:**
  * **segments** – The narration text — a single string, or a list of segment strings.
  * **audio** – The narration voice audio (path / bytes / ndarray / a library ref). When
    given, the result is woven into a mastered mix (set `weave=False` to skip).
  * **transcript** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – The full narration transcript for alignment (default: the segments joined).
  * **library** – The [`foley.index.SoundLibrary`](_autosummary/foley.index.html.md#foley.index.SoundLibrary) (default: the process-wide default).
  * **intended_use** – The rights intent (default:
    [`foley.licensing.DEFAULT_INTENDED_USE`](_autosummary/foley.licensing.html.md#foley.licensing.DEFAULT_INTENDED_USE) — commercial publishing, the
    same default as [`foley.find()`](_autosummary/foley.html.md#foley.find) and every MCP tool).
  * **commercial_ok** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`bool`](https://docs.python.org/3/builtins/functions.html#bool)]) – Shorthand override of the default intent’s `commercial`;
    `False` explicitly admits non-commercial material.
  * **max_events** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The sparse density cap **per segment** (restraint).
  * **verify** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The max verify rung — `'clap'` | `'listen'` | `'judge'`.
  * **master** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The delivery [`MASTER_PROFILES`](_autosummary/foley.base.html.md#foley.base.MASTER_PROFILES) target (`'podcast'` default).
  * **weave** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`bool`](https://docs.python.org/3/builtins/functions.html#bool)]) – Force weaving on/off; default auto (`True` iff `audio` is given).
  * **llm** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Which LLM the SELECT rungs use (`'fake'` | `'local'` | `'anthropic'`;
    `None` reads `$FOLEY_LLM`, else the free default) — see [`foley.find()`](_autosummary/foley.html.md#foley.find).
  * **budget** – The run’s [`Budget`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.Budget) — one spend cap
    (`max_usd`) across every segment’s paid calls (#57). Default: $1, or $3
    when `llm` (or `$FOLEY_LLM`) is `'anthropic'`
    ([`foley.cost.default_max_usd()`](_autosummary/foley.cost.html.md#foley.cost.default_max_usd), #88).
  * **\*\*weave_kwargs** – Forwarded to `foley.weave()` (e.g. `sign_cert`, `watermark`).
* **Return type:**
  [`ScoreResult`](_autosummary/foley.html.md#foley.ScoreResult)
* **Returns:**
  A [`ScoreResult`](_autosummary/foley.html.md#foley.ScoreResult) (`timeline` + `events` rationale; `weave` when woven).

### foley.search(query, , k=10, filters=None, commercial_ok=None, ucs_category=None, min_snr=None, duration_range=None, rerank=False)

Hybrid (CLAP vector ⊕ BM25) search of the default library.

Convenience wrapper over `foley.library.search(...)` — see
[`foley.index.SoundLibrary.search()`](_autosummary/foley.index.html.md#foley.index.SoundLibrary.search). Constructs the process-wide default
library (local stores + CLAP + best available index) on first use.

`commercial_ok` defaults to the one rights intent every verb shares
([`foley.licensing.DEFAULT_INTENDED_USE`](_autosummary/foley.licensing.html.md#foley.licensing.DEFAULT_INTENDED_USE): commercial), so only commercially
usable sounds come back; pass `commercial_ok=False` to include the rest.

### foley.serve_http(, host='127.0.0.1', port=8000, auth, path='/mcp', \*\*kwargs)

Build and serve the foley MCP tools over authenticated streamable HTTP (blocks).

Wraps [`make_http_app()`](_autosummary/foley.html.md#foley.make_http_app) and runs it with uvicorn. `auth` is required (fail-closed).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### foley.similar(sound_id, , k=10, commercial_ok=None)

Find sounds similar to a stored sound (audio<->audio) in the default library.

See [`foley.index.SoundLibrary.similar()`](_autosummary/foley.index.html.md#foley.index.SoundLibrary.similar). Like [`search()`](_autosummary/foley.html.md#foley.search), only sounds
cleared for the default (commercial) intent are returned unless
`commercial_ok=False`.

### foley.similar_to(clip_or_candidate, , k=10, library=None)

“More like this” — neighbours of a sound id / candidate, or of a raw clip.

A `str` id or a [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate) uses by-id neighbours
(`SoundLibrary.similar`, self excluded); a raw working-array / bytes clip uses
audio-to-audio search (`SoundLibrary.search_clip`).

* **Parameters:**
  * **clip_or_candidate** – A sound id, a [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate), or a clip.
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – How many neighbours to return.
  * **library** – The [`foley.index.SoundLibrary`](_autosummary/foley.index.html.md#foley.index.SoundLibrary) (default: the shared one).
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]
* **Returns:**
  A list of [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate).

### foley.sqlite_vec_loadable()

True if `sqlite_vec` is installed AND this interpreter can load it.

The macOS system / pyenv CPython builds frequently ship a `sqlite3` without
loadable-extension support (no `enable_load_extension`); on those,
sqlite-vec cannot be used even when 

```
``
```

pip install\`\`ed. This probes both.

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### foley.store_sound(record, data=None, , sounds, meta, cache_bytes_ok=None)

Persist a sound, choosing by-value vs by-reference from `cache_bytes_ok`.

The choice is driven by the sound’s own license (invariant #1): unless
`cache_bytes_ok` is passed explicitly, it is read from
`record.license.cache_bytes_ok`. A sound whose bytes may NOT be cached
(e.g. Freesound CC0, whose TOS forbids caching even though the file is legally
redistributable — invariant #2) is stored **by reference**: no bytes are
written, only its fetchable `uri` plus provenance.

* **Parameters:**
  * **record** ([`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord)) – The `SoundRecord` to persist; its nested `license` is the SSOT
    for the storage mode. Mutated in place with the resolved
    `storage_mode` / `uri` / `content_sha256` and written into
    `meta`.
  * **data** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes)]) – The canonical archive bytes (FLAC). Required for by-value storage;
    for by-reference it is optional — if given, its hash is recorded in
    `content_sha256` for provenance but the bytes are NOT stored.
  * **sounds** ([`MutableMapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.MutableMapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes)]) – The content-addressed byte store (see [`make_byte_store()`](_autosummary/foley.html.md#foley.make_byte_store)).
  * **meta** ([`MutableMapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.MutableMapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord)]) – The metadata store (see [`make_meta_store()`](_autosummary/foley.html.md#foley.make_meta_store)).
  * **cache_bytes_ok** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`bool`](https://docs.python.org/3/builtins/functions.html#bool)]) – Optional override. `None` (the default) means “use
    `record.license.cache_bytes_ok`”.
* **Return type:**
  [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord)
* **Returns:**
  The same (mutated) `record`, after it has been written into `meta`.
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If `record.id` is empty/non-`str` (checked first, so a bad
      id never leaves an orphan blob), or if the sound resolves to
      by-reference storage but `record.uri` is empty (a by-reference sound
      must name a fetchable source URL).

#### NOTE
The blob is written BEFORE the record so a crash can never leave a
metadata reference dangling against a missing blob.

### foley.to_mono(samples)

Down-mix to mono by averaging channels; 1-D input passes through.

* **Parameters:**
  **samples** (`ndarray`) – Mono `(frames,)` or multichannel `(frames, channels)` array.
* **Return type:**
  `ndarray`
* **Returns:**
  A 1-D mono array (dtype preserved).

### foley.to_working(samples, sample_rate, , mono=True, target_sr=48000, dtype='float32')

Produce the canonical CLAP/QC working array from an arbitrary clip.

Down-mixes (when `mono`), resamples to `target_sr`, and casts to
`dtype` — the `float32` @ 48 kHz mono array every embedder/tagger/QC
check consumes.

* **Parameters:**
  * **samples** (`ndarray`) – Decoded working array (mono or multichannel).
  * **sample_rate** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The array’s current rate in Hz.
  * **mono** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – If `True`, down-mix to mono.
  * **target_sr** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Working sample rate in Hz.
  * **dtype** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Output NumPy dtype string.
* **Return type:**
  `ndarray`
* **Returns:**
  The canonical working array.

Lazy dependency: `soxr` (only when `sample_rate != target_sr`).

### foley.trim_silence(samples, sample_rate, , top_db=30.0)

Strip leading/trailing silence, returning the clip and its kept span.

Silence detection runs on a transient mono down-mix (so librosa’s time-last
convention never clashes with foley’s time-first `(frames, channels)`
layout); the returned sample indices then slice the *original* array along
axis 0, preserving its channel layout.

* **Parameters:**
  * **samples** (`ndarray`) – Working array (mono or multichannel).
  * **sample_rate** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Sample rate in Hz (kept in the signature for API symmetry;
    trimming is index-based).
  * **top_db** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – A frame is silent when it sits at least this many dB below the
    reference (peak) level.
* **Return type:**
  [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[`ndarray`, [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`int`](https://docs.python.org/3/builtins/functions.html#int), [`int`](https://docs.python.org/3/builtins/functions.html#int)]]
* **Returns:**
  `(trimmed, (start_sample, end_sample))`. On all-silent (or otherwise
  degenerate) input the original array is returned unchanged with a
  full-length span `(0, len(samples))`.

Lazy dependency: `librosa`.

### foley.true_peak_dbtp(samples, sample_rate, , oversample=4)

Inter-sample true-peak level in dBTP.

Each channel is band-limited-upsampled `oversample``x (numpy FFT), the
peak magnitude is taken across all channels, and converted to dBTP. Returns
``-inf` for a fully silent clip. `sample_rate` is accepted for interface
symmetry (FFT interpolation is rate-independent).

* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

### foley.using_source_accounts(\*\*accounts)

Bind per-request source accounts (a key, and what goes with it) for a `with` block.

The key is bound through `ocracy.kit.using_credentials`, so
`resolve_source_key()` uses it; the rest of the account (ElevenLabs: its
`plan`, which decides the licence) is bound **with** that key, and only applies
while that same key is the one bound (#98):

```default
with foley.using_source_accounts(
    elevenlabs={"key": user_key, "plan": "elevenlabs-free-plan"},
    freesound=user_freesound_key,  # a string is a key alone
):
    foley.generate("a door creaks", backend="elevenlabs")
```

A source given `None` (or an empty key) binds nothing, so an optional request
header can be passed straight through.

The binding lives in a `ContextVar`: it covers what runs inside the block, in
this context. Consume a lazy result (`find(stream=True)`) inside the block, and
run work handed to a thread pool under `contextvars.copy_context()`; outside the
block the operator’s key and plan apply again.

* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – For a field an account cannot carry.

### foley.vector_search(qvec, , vindex, k=10, where=None)

Pure audio<->audio (or clip->library) vector search — no keyword leg.

Used by `SoundLibrary.similar` and by searching with a reference clip.
Hits keep their cosine similarity in `clap_score` and preserve the index’s
own descending-similarity order (`rrf_score` is left `None` — there is no
fusion).

* **Parameters:**
  * **qvec** (`ndarray`) – An already-L2-normalized `(dim,)` query vector.
  * **vindex** ([`VectorIndex`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.VectorIndex)) – The vector index.
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Number of neighbours to return.
  * **where** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Optional metadata push-down.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`FusedHit`](_autosummary/foley.index.search.html.md#foley.index.search.FusedHit)]
* **Returns:**
  Up to `k` :class:

  ```
  `
  ```

  FusedHit\`s in descending-similarity order.

### foley.verify_and_setup(, names=None)

Return a per-requirement status + guidance report (never runs an installer).

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]
* **Returns:**
  `{name: {'available', 'purpose', 'install', 'url', 'probe'}}`.

### foley.verify_match(event, candidate, , level=VerifyLevel.clap, judge=None, tau_clap=0.35, \_span=None)

Verify `candidate` against `event` up to rung `level` (AND-confirming ladder).

Runs the `clap` gate always; if `level` is higher **and** the clap gate passed,
escalates to the injected/​default judge for that rung and returns *its* verdict
(`Verdict.level` == the producing rung).

* **Parameters:**
  * **event** ([`SoundEvent`](_autosummary/foley.base.html.md#foley.base.SoundEvent)) – The wanted [`SoundEvent`](_autosummary/foley.html.md#foley.SoundEvent).
  * **candidate** ([`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)) – A **license-clean** [`Candidate`](_autosummary/foley.html.md#foley.Candidate) — this MUST run after the
    [`gate_candidates()`](_autosummary/foley.agent.policy.html.md#foley.agent.policy.gate_candidates) gate (asserted).
  * **level** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`VerifyLevel`](_autosummary/foley.base.html.md#foley.base.VerifyLevel)) – The max rung to climb (`clap` | `listen` | `judge`).
  * **judge** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Judge`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Judge)]) – An injected [`Judge`](_autosummary/foley.agent.protocols.html.md#foley.agent.protocols.Judge) for the higher rungs
    (the DI seam; defaults per `_default_judge()`).
  * **tau_clap** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – The clap-gate threshold.
  * **\_span** – Internal — the obs span handle for GenAI recording on the LLM rung.
* **Raises:**
  [**AssertionError**](https://docs.python.org/3/builtins/exceptions.html#AssertionError) – If `candidate.license_ok` is not `True` (verify-before-gate
      is a bug — the license gate is the fail-closed first pass).
* **Return type:**
  [`Verdict`](_autosummary/foley.base.html.md#foley.base.Verdict)

### Modules

| [`audio`](_autosummary/foley.audio.html.md#module-foley.audio)               | Audio I/O and DSP primitives for foley.                                                                                                              |
|-----------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`index`](_autosummary/foley.index.html.md#module-foley.index)               | foley INDEX stage — make every sound findable by keyword *and* meaning.                                                                              |
| [`obs`](_autosummary/foley.obs.html.md#module-foley.obs)                   | Observability & the reproducible run-artifact for foley (#11).                                                                                       |
| [`agent`](_autosummary/foley.agent.html.md#module-foley.agent)               | The SELECT stage — the search-agent that finds the right sound for a context (#7).                                                                   |
| `weave`                                                                                 |                                                                                                                                                      |
| [`eval`](_autosummary/foley.eval.html.md#module-foley.eval)                 | Tier-1 retrieval evaluation — metrics, a frozen golden set, and the nDCG gate.                                                                       |
| [`provenance`](_autosummary/foley.provenance.html.md#module-foley.provenance)     | Provenance layer for foley — attribution/credits (and, later, disclosure).                                                                           |
| [`agent_kit`](_autosummary/foley.agent_kit.html.md#module-foley.agent_kit)       | Install foley's shipped agent kit — the consumer skill + Claude slash command + subagent.                                                            |
| [`base`](_autosummary/foley.base.html.md#module-foley.base)                 | Canonical data models for foley — the single source of truth (SSOT) types.                                                                           |
| [`cli`](_autosummary/foley.cli.html.md#module-foley.cli)                   | The `foley` command-line interface (stdlib `argparse`, zero new deps).                                                                               |
| [`cost`](_autosummary/foley.cost.html.md#module-foley.cost)                 | Cost: what a call will cost before it is made, and a cumulative cap on a run (#57).                                                                  |
| [`licensing`](_autosummary/foley.licensing.html.md#module-foley.licensing)       | License policy for foley: the license_id -> flag-set SSOT, flag derivation (with per-source overrides), and the fail-closed candidate `keep()` gate. |
| [`qc`](_autosummary/foley.qc.html.md#module-foley.qc)                     | Tier-0 deterministic audio QC for foley (research report 08 §3).                                                                                     |
| [`requirements`](_autosummary/foley.requirements.html.md#module-foley.requirements) | Onboarding — check what foley needs, tell the user how to get it (accompy-style, #12).                                                               |
| [`runtime`](_autosummary/foley.runtime.html.md#module-foley.runtime)           | Runtime posture — local-first / offline mode as one verifiable contract (#12, report 12).                                                            |
| [`sources`](_autosummary/foley.sources.html.md#module-foley.sources)           | Bulk-corpus source adapters for foley's SOURCE stage.                                                                                                |
| [`stores`](_autosummary/foley.stores.html.md#module-foley.stores)             | dol-backed storage for foley: content-addressed bytes + a metadata store.                                                                            |


# _autosummary/foley.index.embedders.html.md

# foley.index.embedders

Embedders — the joint text<->audio space that powers retrieval.

The default is **LAION-CLAP** `laion/larger_clap_general` (512-d, Apache-2.0):
one space serves both text->audio search and audio<->audio similarity, it is
trained on general/environmental sound (not just music/speech), and its license
is clean for a widely-installed façade (report 04 §1.2, §6.3). It lives behind
the [`Embedder`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.Embedder) protocol so MS-CLAP, PANNs, or GLAP
can be dropped in by keyword injection; every record stores the
`embedding_model`/`embedding_dim` it was indexed under so mixed-model
libraries stay coherent.

`torch`/`transformers` are lazy-imported (the `foley[clap]` extra), so
importing this module — and constructing a [`ClapEmbedder`](_autosummary/foley.index.embedders.html.md#foley.index.embedders.ClapEmbedder) — costs only the
stdlib; the ~1.7 GB checkpoint loads on the first `embed_*` call.

### Module Attributes

| [`DEFAULT_CLAP_MODEL_ID`](_autosummary/foley.index.embedders.html.md#foley.index.embedders.DEFAULT_CLAP_MODEL_ID)   | The default CLAP checkpoint (LAION, Apache-2.0, general/environmental sound).   |
|--------------------------------------------------------------------------|---------------------------------------------------------------------------------|
| [`CLAP_SAMPLE_RATE`](_autosummary/foley.index.embedders.html.md#foley.index.embedders.CLAP_SAMPLE_RATE)        | Sample rate CLAP expects at its audio input (report 04 §1.2).                   |
| [`DEFAULT_CLAP_DIM`](_autosummary/foley.index.embedders.html.md#foley.index.embedders.DEFAULT_CLAP_DIM)        | `projection_dim=512`).                                                          |

### Functions

| [`default_embedder`](_autosummary/foley.index.embedders.html.md#foley.index.embedders.default_embedder)()   | Return a process-wide default [`ClapEmbedder`](_autosummary/foley.index.embedders.html.md#foley.index.embedders.ClapEmbedder) (loaded once, reused).   |
|-----------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------|

### Classes

| [`ClapEmbedder`](_autosummary/foley.index.embedders.html.md#foley.index.embedders.ClapEmbedder)([model_id, device])   | LAION-CLAP text<->audio embedder (the default retrieval engine).   |
|-------------------------------------------------------------------------------------|--------------------------------------------------------------------|

### foley.index.embedders.CLAP_SAMPLE_RATE *: [int](https://docs.python.org/3/builtins/functions.html#int)* *= 48000*

Sample rate CLAP expects at its audio input (report 04 §1.2).

### *class* foley.index.embedders.ClapEmbedder(model_id='laion/larger_clap_general', , device=None)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

LAION-CLAP text<->audio embedder (the default retrieval engine).

Returns **L2-normalized** `float32` embeddings so a plain inner product is
cosine similarity. `embed_text` always returns a 2-D `(n, dim)` array;
`embed_audio` returns a 1-D `(dim,)` array for one clip.

#### model_id

The HF checkpoint id.

#### dim

The embedding dimensionality.

#### open_source

Whether the checkpoint is an open-source model (the LAION
releases are). Read by the ingest AI-use gate for sounds whose rights
holder allows only open-source models (#69).

#### *property* device *: [str](https://docs.python.org/3/builtins/stdtypes.html#str)*

The resolved torch device string (`'cuda'`/`'cpu'`).

#### *property* dim *: [int](https://docs.python.org/3/builtins/functions.html#int)*

The embedding dimensionality (512 for the default; resolved for others).

For a non-default checkpoint this fetches only the model’s `config.json`
(via `AutoConfig`) — never the ~1.7 GB weights — so building an index
for it does not force a model download. Falls back to the loaded model’s
config if the standalone config lacks `projection_dim`.

#### embed_audio(wav, sr)

Embed one audio clip -> `(dim,)` L2-normalized.

The clip is down-mixed to mono and resampled to 48 kHz (what CLAP expects)
via [`foley.audio`](_autosummary/foley.audio.html.md#module-foley.audio) before embedding.

* **Parameters:**
  * **wav** (`ndarray`) – A working-array clip (`float32`; mono or multichannel).
  * **sr** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The clip’s sample rate in Hz.
* **Return type:**
  `ndarray`

#### embed_text(text)

Embed one or more query strings -> `(n_texts, dim)` L2-normalized.

* **Return type:**
  `ndarray`

### foley.index.embedders.DEFAULT_CLAP_DIM *: [int](https://docs.python.org/3/builtins/functions.html#int)* *= 512*

`projection_dim=512`).

* **Type:**
  Embedding width for the default checkpoint (verified

### foley.index.embedders.DEFAULT_CLAP_MODEL_ID *: [str](https://docs.python.org/3/builtins/stdtypes.html#str)* *= 'laion/larger_clap_general'*

The default CLAP checkpoint (LAION, Apache-2.0, general/environmental sound).

### foley.index.embedders.default_embedder()

Return a process-wide default [`ClapEmbedder`](_autosummary/foley.index.embedders.html.md#foley.index.embedders.ClapEmbedder) (loaded once, reused).

Cached so repeated `foley.search()` calls share a single loaded model.

* **Return type:**
  [`ClapEmbedder`](_autosummary/foley.index.embedders.html.md#foley.index.embedders.ClapEmbedder)


# _autosummary/foley.index.html.md

# foley.index

foley INDEX stage — make every sound findable by keyword *and* meaning.

The retrieval keystone (report 04 / report 10 §5): a CLAP joint embedding space,
a hybrid vector+keyword index with Reciprocal Rank Fusion, a `dol`-native
[`SoundLibrary`](_autosummary/foley.index.html.md#foley.index.SoundLibrary) façade composing the byte/metadata stores with the two
indexes, and the UCS/AudioSet taxonomy resolver — all behind small, swappable
protocols with zero-config defaults.

Everything heavy (`torch`/`transformers`/`lancedb`/`sqlite_vec`) is
lazy-imported inside the method that needs it, so `import foley.index` costs
only the stdlib; install the capability you use via the matching extra
(`foley[clap]`, `foley[index]`, `foley[index-sqlite]`).

### Functions

| [`default_embedder`](_autosummary/foley.index.html.md#foley.index.default_embedder)()                                 | Return a process-wide default [`ClapEmbedder`](_autosummary/foley.index.html.md#foley.index.ClapEmbedder) (loaded once, reused).   |
|-----------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------|
| [`default_tagger`](_autosummary/foley.index.html.md#foley.index.default_tagger)()                                   | The default supervised tagger (PANNs CNN14; `foley[tag]`).                                                           |
| [`default_zeroshot_tagger`](_autosummary/foley.index.html.md#foley.index.default_zeroshot_tagger)([embedder])                | The default zero-shot tagger (CLAP vs UCS subcategories; `foley[clap]`).                                             |
| [`ingest_one`](_autosummary/foley.index.html.md#foley.index.ingest_one)(src, \*[, library, sound_id, ...])      | Ingest one clip into `library` and return an [`IngestResult`](_autosummary/foley.index.html.md#foley.index.IngestResult).          |
| [`ingest_folder`](_autosummary/foley.index.html.md#foley.index.ingest_folder)(path, \*[, library, recursive, ...]) | Ingest every audio file under `path` and return an [`IngestReport`](_autosummary/foley.index.html.md#foley.index.IngestReport).    |
| [`resolve_ingest_license`](_autosummary/foley.index.html.md#foley.index.resolve_ingest_license)(license, \*[, source_url])  | The rights record for a local ingest: fail-closed unless rights are asserted (#55).                                  |
| [`restamp_rights`](_autosummary/foley.index.html.md#foley.index.restamp_rights)([library, license, ids, ...])       | Re-stamp the licence of stored sounds; a dry run unless `apply=True`.                                                |
| [`default_index`](_autosummary/foley.index.html.md#foley.index.default_index)(\*, data_dir, dim)                   | Build the best available persistent index for a library.                                                             |
| [`lancedb_available`](_autosummary/foley.index.html.md#foley.index.lancedb_available)()                                | True if `lancedb` is importable (the `foley[index]` extra is present).                                               |
| [`sqlite_vec_loadable`](_autosummary/foley.index.html.md#foley.index.sqlite_vec_loadable)()                              | True if `sqlite_vec` is installed AND this interpreter can load it.                                                  |
| [`reciprocal_rank_fusion`](_autosummary/foley.index.html.md#foley.index.reciprocal_rank_fusion)(ranked_id_lists, \*[, k])   | Fuse several ranked id lists into one, by reciprocal rank.                                                           |
| [`fuse_hits`](_autosummary/foley.index.html.md#foley.index.fuse_hits)(vector_hits, keyword_hits, \*, k[, ...]) | RRF-fuse a vector ranker's hits with a keyword ranker's hits.                                                        |
| [`hybrid_search`](_autosummary/foley.index.html.md#foley.index.hybrid_search)(query, \*, embedder, vindex, kindex) | Embed `query`, run the vector + keyword rankers, and RRF-fuse them.                                                  |
| [`vector_search`](_autosummary/foley.index.html.md#foley.index.vector_search)(qvec, \*, vindex[, k, where])        | Pure audio<->audio (or clip->library) vector search — no keyword leg.                                                |
| [`default_library`](_autosummary/foley.index.html.md#foley.index.default_library)()                                  | The process-wide default library (local stores + CLAP + best index).                                                 |
| [`resolve_catid`](_autosummary/foley.index.html.md#foley.index.resolve_catid)(\*[, tags, caption, ...])            | Resolve inputs to a best UCS CatID by the staged precedence.                                                         |
| [`parse_ucs_filename`](_autosummary/foley.index.html.md#foley.index.parse_ucs_filename)(filename, \*[, table])          | Parse a UCS-conformant filename to `(ucs_category, ucs_subcategory)`.                                                |

### Classes

| [`Embedder`](_autosummary/foley.index.html.md#foley.index.Embedder)(\*args, \*\*kwargs)                      | A joint text<->audio embedding space (CLAP by default).                     |
|----------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------|
| [`VectorIndex`](_autosummary/foley.index.html.md#foley.index.VectorIndex)(\*args, \*\*kwargs)                   | Approximate-nearest-neighbour store over embedding vectors.                 |
| [`KeywordIndex`](_autosummary/foley.index.html.md#foley.index.KeywordIndex)(\*args, \*\*kwargs)                  | BM25 / full-text index over each sound's tags + caption.                    |
| [`Tagger`](_autosummary/foley.index.html.md#foley.index.Tagger)(\*args, \*\*kwargs)                        | Map a clip to `(label, score)` pairs against a label vocabulary.            |
| [`Captioner`](_autosummary/foley.index.html.md#foley.index.Captioner)(\*args, \*\*kwargs)                     | Produce one natural-language sentence describing a clip (report 03).        |
| [`ClapEmbedder`](_autosummary/foley.index.html.md#foley.index.ClapEmbedder)([model_id, device])                  | LAION-CLAP text<->audio embedder (the default retrieval engine).            |
| [`ClapZeroShotTagger`](_autosummary/foley.index.html.md#foley.index.ClapZeroShotTagger)(\*[, embedder, labels, ...])   | Zero-shot tagger: score a clip against a label set via CLAP cosine.         |
| [`PannsTagger`](_autosummary/foley.index.html.md#foley.index.PannsTagger)(\*[, device, threshold])              | PANNs CNN14 supervised tagger over the 527 AudioSet classes (`foley[tag]`). |
| [`IngestResult`](_autosummary/foley.index.html.md#foley.index.IngestResult)(id, status[, record, qc, ...])       | The outcome of ingesting one clip.                                          |
| [`IngestReport`](_autosummary/foley.index.html.md#foley.index.IngestReport)(root[, results, notes])              | The rolled-up outcome of a folder ingest (JSON-serializable).               |
| [`MemoryIndex`](_autosummary/foley.index.html.md#foley.index.MemoryIndex)(\*[, dim])                            | In-memory vector + keyword index (numpy cosine + compact BM25).             |
| [`LanceIndex`](_autosummary/foley.index.html.md#foley.index.LanceIndex)(\*, uri, dim[, table_name])            | LanceDB-backed index: one table with a vector column + a native FTS index.  |
| [`SqliteVecIndex`](_autosummary/foley.index.html.md#foley.index.SqliteVecIndex)(\*, path, dim)                     | Single-file index: sqlite-vec `vec0` KNN + stdlib FTS5 keyword search.      |
| [`FusedHit`](_autosummary/foley.index.html.md#foley.index.FusedHit)(id[, rrf_score, clap_score, bm25_score]) | One fused retrieval hit: an id plus the scores that produced it.            |
| [`SoundLibrary`](_autosummary/foley.index.html.md#foley.index.SoundLibrary)(\*[, sounds, meta, vindex, ...])     | A searchable, license-aware library of sounds (the foley INDEX façade).     |
| [`CatIdResolution`](_autosummary/foley.index.html.md#foley.index.CatIdResolution)([catid, category, ...])           | The result of resolving free tags/caption/labels to a UCS CatID.            |

### *class* foley.index.Captioner(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

Produce one natural-language sentence describing a clip (report 03).

The caption feeds the BM25 keyword index and human display. Default is a
dedicated AAC model (EnCLAP); Qwen2-Audio is a richer promptable upgrade.
Both are `foley[caption]` adapters plugged in behind this protocol.

#### caption(wav, sr)

Return a one-sentence caption for the clip.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### *class* foley.index.CatIdResolution(catid=None, category=None, subcategory=None, source=None, confidence=0.0, matched_terms=<factory>)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

The result of resolving free tags/caption/labels to a UCS CatID.

`catid` feeds `ucs_category` and
`subcategory` feeds `ucs_subcategory` on
ingest, and `ucs_catid` on the query side.

### *class* foley.index.ClapEmbedder(model_id='laion/larger_clap_general', , device=None)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

LAION-CLAP text<->audio embedder (the default retrieval engine).

Returns **L2-normalized** `float32` embeddings so a plain inner product is
cosine similarity. `embed_text` always returns a 2-D `(n, dim)` array;
`embed_audio` returns a 1-D `(dim,)` array for one clip.

#### model_id

The HF checkpoint id.

#### dim

The embedding dimensionality.

#### open_source

Whether the checkpoint is an open-source model (the LAION
releases are). Read by the ingest AI-use gate for sounds whose rights
holder allows only open-source models (#69).

#### *property* device *: [str](https://docs.python.org/3/builtins/stdtypes.html#str)*

The resolved torch device string (`'cuda'`/`'cpu'`).

#### *property* dim *: [int](https://docs.python.org/3/builtins/functions.html#int)*

The embedding dimensionality (512 for the default; resolved for others).

For a non-default checkpoint this fetches only the model’s `config.json`
(via `AutoConfig`) — never the ~1.7 GB weights — so building an index
for it does not force a model download. Falls back to the loaded model’s
config if the standalone config lacks `projection_dim`.

#### embed_audio(wav, sr)

Embed one audio clip -> `(dim,)` L2-normalized.

The clip is down-mixed to mono and resampled to 48 kHz (what CLAP expects)
via [`foley.audio`](_autosummary/foley.audio.html.md#module-foley.audio) before embedding.

* **Parameters:**
  * **wav** (`ndarray`) – A working-array clip (`float32`; mono or multichannel).
  * **sr** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The clip’s sample rate in Hz.
* **Return type:**
  `ndarray`

#### embed_text(text)

Embed one or more query strings -> `(n_texts, dim)` L2-normalized.

* **Return type:**
  `ndarray`

### *class* foley.index.ClapZeroShotTagger(, embedder=None, labels=None, prompt='this is a sound of {label}', threshold=0.0)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Zero-shot tagger: score a clip against a label set via CLAP cosine.

Reuses a [`ClapEmbedder`](_autosummary/foley.index.embedders.html.md#foley.index.embedders.ClapEmbedder) (the same model the
Index uses), so the audio is embedded in the same joint space as the label
prompts and no extra weights load. The default label set is the UCS
subcategory names (foley’s own vocabulary), so tags land in-taxonomy.

#### *property* embedder

The CLAP embedder (injected or the process-wide default).

#### *property* labels *: [list](https://docs.python.org/3/builtins/stdtypes.html#list)[[str](https://docs.python.org/3/builtins/stdtypes.html#str)]*

natural UCS `category subcategory` phrases).

Natural multi-word phrases (`"weather rain"`, `"glass break"`) are far
better CLAP prompts — and better BM25 tags / taxonomy-resolver input — than
bare abstract subcategory words (`"Break"`, `"Buzz"`), which are a known
zero-shot artifact (anomalously close to everything). Absolute tag-quality
calibration (thresholds, label curation) is an eval-harness concern (#10).

* **Type:**
  The label vocabulary ([*default*](_autosummary/foley.html.md#foley.Affordance.default)

#### tag(wav, sr, , taxonomy='custom', top_k=10)

Return the top-`k` `(label, cosine)` tags for the clip, best first.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

#### tag_vector(audio_vec, , top_k=10)

Score a **precomputed** (L2-normalized) audio vector against the labels.

The efficiency seam (report 03 Part 2): the Index already embeds every
sound with this model, so on ingest the retrieval vector is reused here —
no second CLAP forward pass.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

### *class* foley.index.Embedder(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

A joint text<->audio embedding space (CLAP by default).

One space serves both text->audio search (`embed_text` a query) and
audio<->audio similarity (`embed_audio` a clip). Implementations MUST
return **L2-normalized** `float32` arrays so a plain inner product is
cosine similarity, and MUST stamp `model_id`/`dim` so mixed-model
libraries stay coherent (each [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord) records the
`embedding_model`/`embedding_dim` it was indexed under).

#### model_id

The checkpoint id (e.g. `'laion/larger_clap_general'`).

#### dim

The embedding dimensionality (e.g. `512`).

#### embed_audio(wav, sr)

Embed one audio clip.

* **Parameters:**
  * **wav** (`ndarray`) – A working-array clip (`float32`, mono preferred). CLAP expects
    48 kHz; implementations resample as needed.
  * **sr** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The clip’s sample rate in Hz.
* **Return type:**
  `ndarray`
* **Returns:**
  A 1-D `(dim,)` L2-normalized `float32` array.

#### embed_text(text)

Embed one or more query strings.

* **Parameters:**
  **text** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]) – A single string or a list of strings.
* **Return type:**
  `ndarray`
* **Returns:**
  A 2-D `(n_texts, dim)` L2-normalized `float32` array (`n_texts`
  is `1` for a single string) — always 2-D so callers can index
  `[0]` for the single-query case.

### *class* foley.index.FusedHit(id, rrf_score=None, clap_score=None, bm25_score=None)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

One fused retrieval hit: an id plus the scores that produced it.

The raw component scores are carried through (not just the fused rank score)
so the façade can stamp them onto a [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)
(`clap_score` / `bm25_score` / `rrf_score`) for display and debugging.

#### id

The sound id.

#### rrf_score

The fused RRF score (`None` for a pure-vector search).

#### clap_score

Cosine similarity from the vector ranker (`None` if the id
appeared only in the keyword list).

#### bm25_score

BM25 score from the keyword ranker (`None` if the id
appeared only in the vector list).

### *class* foley.index.IngestReport(root, results=<factory>, notes=<factory>)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

The rolled-up outcome of a folder ingest (JSON-serializable).

#### error(path, exc)

Record a per-file error without aborting the run.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### *property* errored *: [list](https://docs.python.org/3/builtins/stdtypes.html#list)[[IngestResult](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult)]*

Results that raised during ingest.

#### exception *= None*

The exception behind the last `error` result, kept so a raising façade can
chain it (`raise ... from`); a plain attribute, never serialized.

#### *property* ingested *: [list](https://docs.python.org/3/builtins/stdtypes.html#list)[[IngestResult](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult)]*

Results that were added to the library (`pass` or `warn`).

#### *property* quarantined *: [list](https://docs.python.org/3/builtins/stdtypes.html#list)[[IngestResult](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult)]*

Results rejected by the QC gate.

#### record(result)

Append one [`IngestResult`](_autosummary/foley.index.html.md#foley.index.IngestResult).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### *property* rights_blocked *: [list](https://docs.python.org/3/builtins/stdtypes.html#list)[[IngestResult](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult)]*

Results refused by the fail-closed AI-training/license rights gate.

#### *property* skipped *: [list](https://docs.python.org/3/builtins/stdtypes.html#list)[[IngestResult](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult)]*

Results skipped as content-addressed duplicates.

#### summary()

A counts dict for a console/CLI summary.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### *class* foley.index.IngestResult(id, status, record=None, qc=None, notes=<factory>, error=None, cost_estimate_usd=None, cost_actual_usd=None)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

The outcome of ingesting one clip.

`status`: `'pass'`/`'warn'` (ingested), `'quarantined'` (QC-rejected,
not added), `'skipped_dup'` (content already in the library),
`'rights_blocked'` (license forbids AI training / embedding, refused before
embed — see [`ingest_one()`](_autosummary/foley.index.html.md#foley.index.ingest_one)), `'skipped_license'` (dropped by a
bootstrap commercial-use / fail-closed license filter), or `'error'`.
`record` is present only when the clip was ingested.

### *class* foley.index.KeywordIndex(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

BM25 / full-text index over each sound’s tags + caption.

The default is LanceDB’s Tantivy FTS (report 04 §3.4); SQLite FTS5 is the
single-file fallback. Same `where` push-down contract as
[`VectorIndex`](_autosummary/foley.index.html.md#foley.index.VectorIndex).

#### bm25(query, k, , where=None)

Return the top-`k` BM25 matches for `query`, best first.

* **Parameters:**
  * **query** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – A natural-language / keyword query.
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Number of matches to return.
  * **where** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Optional metadata predicates for push-down filtering.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]
* **Returns:**
  `[(id, bm25_score), ...]` in descending-score order.

#### index(id, text, meta)

Insert or replace the searchable text (and light metadata) for `id`.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### *class* foley.index.LanceIndex(, uri, dim, table_name='sounds')

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

LanceDB-backed index: one table with a vector column + a native FTS index.

Writes are staged per id and flushed on the next read (or explicit
[`commit()`](_autosummary/foley.index.html.md#foley.index.LanceIndex.commit)), so a sound’s vector ([`upsert()`](_autosummary/foley.index.html.md#foley.index.LanceIndex.upsert)) and text ([`index()`](_autosummary/foley.index.html.md#foley.index.LanceIndex.index))
— which arrive as two separate protocol calls — are merged into one row and
written as an efficient batch. Vector search is exact cosine (no ANN index is
built at this tier; adding one is a scale-time optimization). Fusion is done
by [`foley.index.search`](_autosummary/foley.index.search.html.md#module-foley.index.search), not by LanceDB’s native hybrid reranker, so
ranking matches every other backend.

One-table constraint: the vector column is mandatory, so \*\*every indexed row
needs a vector\*\*. `SoundLibrary.add` enforces this (it raises without an
embedding source), so the façade path is safe; a bare `index()` with no
matching `upsert()` stages a text-only row that stays unflushed (never
keyword-searchable). For a keyword-only library with no embeddings, use
[`MemoryIndex`](_autosummary/foley.index.html.md#foley.index.MemoryIndex) or [`SqliteVecIndex`](_autosummary/foley.index.html.md#foley.index.SqliteVecIndex) (independent vector/text tables).

#### bm25(query, k, , where=None)

Native full-text (BM25) search; returns `[(id, score), ...]`.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

#### commit()

Flush all staged writes to the LanceDB table.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### *property* db

The lazily-connected LanceDB database handle.

#### get_vector(id)

Return the stored vector for `id` (staged or persisted), else `None`.

* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[`ndarray`]

#### index(id, text, meta)

Stage the searchable text for `id` (flushed on the next read/commit).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### knn(vector, k, , where=None)

Exact cosine KNN; returns `[(id, cosine_similarity), ...]`.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

#### *property* table

The lazily-opened (or created-empty) LanceDB table.

#### upsert(id, vector, meta)

Stage the vector for `id` (flushed on the next read/commit).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### *class* foley.index.MemoryIndex(, dim=None)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

In-memory vector + keyword index (numpy cosine + compact BM25).

Not persistent — everything lives in dicts, lost on process exit. It exists
so the full hybrid + façade path is testable and usable with only `numpy`
(no LanceDB, no torch), and as a genuine zero-config tier for small or
ephemeral libraries. The vector and text stores are independent dicts, so
[`upsert()`](_autosummary/foley.index.html.md#foley.index.MemoryIndex.upsert) and [`index()`](_autosummary/foley.index.html.md#foley.index.MemoryIndex.index) never contend.

#### bm25(query, k, , where=None)

Return the top-`k` BM25 matches for `query` (best first).

A compact Okapi BM25 (`k1=1.5`, `b=0.75`) recomputed per query — O(N)
in the corpus size, which is fine for the in-memory tier’s scale.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

#### commit()

No-op (writes are immediate); present for interface symmetry.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### get_vector(id)

Return the stored vector for `id` (or `None`).

* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[`ndarray`]

#### index(id, text, meta)

Insert or replace the searchable text (and light metadata) for `id`.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### knn(vector, k, , where=None)

Return the `k` cosine-nearest ids to `vector` (most-similar first).

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

#### upsert(id, vector, meta)

Insert or replace the vector (and light metadata) for `id`.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### *class* foley.index.PannsTagger(, device='cpu', threshold=0.1)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

PANNs CNN14 supervised tagger over the 527 AudioSet classes (`foley[tag]`).

The checkpoint auto-downloads to `~/panns_data` (~327 MB) on the first
[`tag()`](_autosummary/foley.index.html.md#foley.index.PannsTagger.tag). PANNs expects 32 kHz mono; the clip is resampled via
[`foley.audio.to_working()`](_autosummary/foley.audio.html.md#foley.audio.to_working).

#### tag(wav, sr, , taxonomy='audioset', top_k=10)

Return the top-`k` `(AudioSet label, score)` tags, best first.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

### *class* foley.index.SoundLibrary(, sounds=None, meta=None, vindex=None, kindex=None, embedder=None, data_dir=None, candidate_k=50, rrf_k=60)

Bases: [`Mapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Mapping)

A searchable, license-aware library of sounds (the foley INDEX façade).

Read it as a `Mapping` of `SoundRecord`s; search it with
:meth:`search` (text) / [`search_clip()`](_autosummary/foley.index.html.md#foley.index.SoundLibrary.search_clip) (a reference clip) / [`similar()`](_autosummary/foley.index.html.md#foley.index.SoundLibrary.similar)
(audio<->audio by id); browse it with [`filter()`](_autosummary/foley.index.html.md#foley.index.SoundLibrary.filter); grow it with [`add()`](_autosummary/foley.index.html.md#foley.index.SoundLibrary.add).

#### add(record, , data=None, vector=None)

Store a sound and index it (the ingest write path).

Persists bytes via [`store_sound()`](_autosummary/foley.stores.html.md#foley.stores.store_sound) (honouring the
by-value/by-reference license gate), upserts the CLAP vector into the
vector index, and indexes `caption``+``tags` into the keyword index.

A sound is retrieval-first, so it MUST carry an embedding: supply either
`data` (bytes to embed — note a by-reference sound is embedded from its
transient bytes even though they are not cached) or a precomputed
`vector`. Adding with neither raises, rather than silently indexing a
vectorless row (which the single-table [`LanceIndex`](_autosummary/foley.index.html.md#foley.index.LanceIndex) cannot persist,
producing backend-dependent search results).

* **Parameters:**
  * **record** ([`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord)) – The record to add (mutated by `store_sound` with resolved
    storage fields, and stamped with the embedding model/dim).
  * **data** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes)]) – The archive bytes (required for by-value storage; also the
    source for computing `vector` when it is not supplied).
  * **vector** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[`ndarray`]) – A precomputed CLAP embedding; when omitted and `data` is
    given, it is computed via the library’s embedder.
* **Return type:**
  [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord)
* **Returns:**
  The same (persisted, indexed) `record`.
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If neither `data` nor `vector` is provided (no way to
      obtain an embedding).

#### array(sound_id, , sr=None, mono=True)

Decode a sound to a working array (`float32`).

* **Parameters:**
  * **sound_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The record id.
  * **sr** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`int`](https://docs.python.org/3/builtins/functions.html#int)]) – Target sample rate (default: the working rate, 48 kHz).
  * **mono** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Down-mix to mono (default `True`).
* **Return type:**
  `ndarray`
* **Returns:**
  The decoded working array.

#### audio(sound_id)

Return a sound’s archive bytes (by-value from the store, or from a
local by-reference path).

* **Parameters:**
  **sound_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The record id.
* **Return type:**
  [`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes)
* **Returns:**
  The raw archive bytes.
* **Raises:**
  [**LookupError**](https://docs.python.org/3/builtins/exceptions.html#LookupError) – If the bytes are neither cached (by-value) nor readable
      from a local `uri` — a remote by-reference sound needs its
      source adapter (subtask #5) to fetch.

#### *property* data_dir *: [Path](https://docs.python.org/3/library/pathlib.html#pathlib.Path)*

The data root for default stores/index.

#### *property* embedder

The text<->audio embedder (CLAP by default).

#### filter(\*\*facets)

Browse the library by metadata facets (no ranking).

Accepts the same facet keywords as [`search()`](_autosummary/foley.index.search.html.md#module-foley.index.search)’s filters
(`commercial_ok`, `ucs_category`, `min_snr`, `duration_range`)
plus any `record_attr=value` equality predicate.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord)]

#### *property* kindex

The keyword index.

#### *property* meta

The metadata store (`id -> SoundRecord`).

#### search(query, , k=10, filters=None, commercial_ok=None, ucs_category=None, min_snr=None, duration_range=None, rerank=False)

Hybrid (CLAP vector ⊕ BM25) search for a text query.

* **Parameters:**
  * **query** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The natural-language query.
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Number of results to return.
  * **filters** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Extra `{record_attr: value}` equality predicates.
  * **commercial_ok** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`bool`](https://docs.python.org/3/builtins/functions.html#bool)]) – If `True`, keep only commercially-usable sounds.
  * **ucs_category** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Keep only sounds with this UCS CatID.
  * **min_snr** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`float`](https://docs.python.org/3/builtins/functions.html#float)]) – Keep only sounds whose QC `snr_db` is at least this.
  * **duration_range** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`float`](https://docs.python.org/3/builtins/functions.html#float), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]) – Keep only sounds whose `duration_s` is in
    `(min, max)`.
  * **rerank** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Re-order the shortlist by direct query<->audio cosine
    (fills the CLAP score for keyword-only hits).
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]
* **Returns:**
  Up to `k` :class:

  ```
  `
  ```

  ~foley.base.Candidate\`s, best first.

#### search_clip(clip, , sr=None, k=10)

Search by a reference audio clip (audio<->audio via CLAP).

* **Parameters:**
  * **clip** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike), [`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes), [`BinaryIO`](https://docs.python.org/3/library/typing.html#typing.BinaryIO)]) – A working array, or a path/bytes/file decodable by
    [`foley.audio.load()`](_autosummary/foley.audio.html.md#foley.audio.load).
  * **sr** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`int`](https://docs.python.org/3/builtins/functions.html#int)]) – Sample rate when `clip` is already a working array.
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Number of results.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]
* **Returns:**
  Up to `k` :class:

  ```
  `
  ```

  ~foley.base.Candidate\`s, most-similar first.

#### similar(sound_id, , k=10)

Return the `k` sounds most similar to `sound_id` (audio<->audio).

Uses the stored vector (no re-decoding); the query sound itself is
excluded from the results.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]

#### *property* sounds

The content-addressed byte store.

#### update_record(record)

Rewrite a stored record’s metadata and re-index its keyword text.

For corrections that do not touch the audio (a re-stamped licence, a removed
caption): the bytes and the CLAP vector stay as they are.

* **Raises:**
  [**KeyError**](https://docs.python.org/3/builtins/exceptions.html#KeyError) – If `record.id` is not in the library.
* **Return type:**
  [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord)

#### *property* vindex

The vector index.

### *class* foley.index.SqliteVecIndex(, path, dim)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Single-file index: sqlite-vec `vec0` KNN + stdlib FTS5 keyword search.

The whole index is one SQLite file behind two virtual tables (independent, so
[`upsert()`](_autosummary/foley.index.html.md#foley.index.SqliteVecIndex.upsert) and [`index()`](_autosummary/foley.index.html.md#foley.index.SqliteVecIndex.index) never contend). \*\*Requires an interpreter
whose `sqlite3` permits loadable extensions\*\* — probe with
[`sqlite_vec_loadable()`](_autosummary/foley.index.html.md#foley.index.sqlite_vec_loadable) before constructing; the constructor raises a
clear error otherwise.

#### bm25(query, k, , where=None)

FTS5 BM25 search; returns `[(id, score), ...]` best-first.

FTS5’s `rank` is more-negative-is-better; it is negated so the returned
score is larger-is-better (consistent with the other backends).

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

#### close()

Close the underlying SQLite connection.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### commit()

Commit any pending SQLite transaction (writes auto-commit already).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### get_vector(id)

Return the stored vector for `id` (or `None`).

* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[`ndarray`]

#### index(id, text, meta)

Insert or replace the searchable text for `id` in the FTS5 table.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### knn(vector, k, , where=None)

KNN over `vec0` (cosine); returns `[(id, cosine_similarity), ...]`.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

#### upsert(id, vector, meta)

Insert or replace the vector for `id` in the `vec0` table.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### *class* foley.index.Tagger(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

Map a clip to `(label, score)` pairs against a label vocabulary.

Used on ingest to auto-fill `SoundRecord.audioset_labels` (supervised) and
`tags` (zero-shot). The default supervised tagger is PANNs CNN14 over
AudioSet; the default zero-shot tagger scores a clip against a custom/UCS
label set via CLAP (report 03). BEATs/AST are drop-in upgrades. Consumes the
working array (`float32`, any sr — the impl resamples to its model’s rate).

#### tag(wav, sr, , taxonomy='audioset', top_k=10)

Return the top-`k` `(label, score)` tags for the clip, best first.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

### *class* foley.index.VectorIndex(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

Approximate-nearest-neighbour store over embedding vectors.

The default is LanceDB (report 04 §2); Qdrant/pgvector/sqlite-vec bind the
same protocol behind the scenes. `where` is an optional metadata push-down
the façade may pass; a backend that cannot push filters down MAY ignore it
(the façade over-fetches and post-filters to stay correct either way).

#### get_vector(id)

Return the stored vector for `id` (or `None` if absent).

Needed by `SoundLibrary.similar` (fetch a sound’s own vector, then run
[`knn()`](_autosummary/foley.index.html.md#foley.index.VectorIndex.knn)) and by the optional CLAP rerank (score keyword-only hits).

* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[`ndarray`]

#### knn(vector, k, , where=None)

Return the `k` nearest ids to `vector`, most-similar first.

* **Parameters:**
  * **vector** (`ndarray`) – A `(dim,)` query vector (already L2-normalized).
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Number of neighbours to return.
  * **where** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Optional metadata predicates for push-down filtering.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]
* **Returns:**
  `[(id, cosine_similarity), ...]` in descending-similarity order.

#### upsert(id, vector, meta)

Insert or replace the vector (and light metadata) for `id`.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### foley.index.default_embedder()

Return a process-wide default [`ClapEmbedder`](_autosummary/foley.index.html.md#foley.index.ClapEmbedder) (loaded once, reused).

Cached so repeated `foley.search()` calls share a single loaded model.

* **Return type:**
  [`ClapEmbedder`](_autosummary/foley.index.embedders.html.md#foley.index.embedders.ClapEmbedder)

### foley.index.default_index(, data_dir, dim)

Build the best available persistent index for a library.

Degradation ladder: LanceDB (`foley[index]`) → sqlite-vec
(`foley[index-sqlite]`, if loadable) → an informative error. The
non-persistent [`MemoryIndex`](_autosummary/foley.index.html.md#foley.index.MemoryIndex) is never chosen automatically (a library
must survive restarts); inject it explicitly for tests/ephemeral use.

* **Parameters:**
  * **data_dir** – The library data root (a `pathlib.Path`-like).
  * **dim** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The embedding dimensionality from the active embedder.
* **Returns:**
  A ready index object (both `VectorIndex` and `KeywordIndex`).
* **Raises:**
  [**RuntimeError**](https://docs.python.org/3/builtins/exceptions.html#RuntimeError) – If no persistent backend is installed/usable.

### foley.index.default_library()

The process-wide default library (local stores + CLAP + best index).

* **Return type:**
  [`SoundLibrary`](_autosummary/foley.index.library.html.md#foley.index.library.SoundLibrary)

### foley.index.default_tagger()

The default supervised tagger (PANNs CNN14; `foley[tag]`).

* **Return type:**
  [`PannsTagger`](_autosummary/foley.index.taggers.html.md#foley.index.taggers.PannsTagger)

### foley.index.default_zeroshot_tagger(embedder=None)

The default zero-shot tagger (CLAP vs UCS subcategories; `foley[clap]`).

Cached **per embedder** so the tagger is bound to the SAME embedder that
produced the audio vector it scores — otherwise (report seam) the cosine would
cross two unrelated embedding spaces. `embedder=None` uses the process-wide
default embedder.

* **Return type:**
  [`ClapZeroShotTagger`](_autosummary/foley.index.taggers.html.md#foley.index.taggers.ClapZeroShotTagger)

### foley.index.fuse_hits(vector_hits, keyword_hits, , k, rrf_k=60)

RRF-fuse a vector ranker’s hits with a keyword ranker’s hits.

* **Parameters:**
  * **vector_hits** ([`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]) – `[(id, cosine_similarity), ...]` best-first (from
    [`knn()`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.VectorIndex.knn)).
  * **keyword_hits** ([`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]) – `[(id, bm25_score), ...]` best-first (from
    [`bm25()`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.KeywordIndex.bm25)).
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Number of fused hits to return.
  * **rrf_k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The RRF damping constant.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`FusedHit`](_autosummary/foley.index.search.html.md#foley.index.search.FusedHit)]
* **Returns:**
  The top-`k` :class:

  ```
  `
  ```

  FusedHit\`s, each carrying its raw component scores.

### foley.index.hybrid_search(query, , embedder, vindex, kindex, k=10, candidate_k=50, rrf_k=60, where=None)

Embed `query`, run the vector + keyword rankers, and RRF-fuse them.

* **Parameters:**
  * **query** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The natural-language query.
  * **embedder** ([`Embedder`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.Embedder)) – Text<->audio embedder (its `embed_text` produces the query
    vector).
  * **vindex** ([`VectorIndex`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.VectorIndex)) – The vector index (CLAP KNN).
  * **kindex** ([`KeywordIndex`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.KeywordIndex)) – The keyword index (BM25).
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Number of fused results to return.
  * **candidate_k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Shortlist depth pulled from each ranker before fusion.
  * **rrf_k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The RRF damping constant.
  * **where** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Optional metadata push-down passed to both rankers.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`FusedHit`](_autosummary/foley.index.search.html.md#foley.index.search.FusedHit)]
* **Returns:**
  The top-`k` fused :class:

  ```
  `
  ```

  FusedHit\`s.

### foley.index.ingest_folder(path, , library=None, recursive=True, exts=('.wav', '.flac', '.aiff', '.aif', '.ogg', '.mp3', '.opus', '.m4a'), on_error='collect', \*\*ingest_one_kw)

Ingest every audio file under `path` and return an [`IngestReport`](_autosummary/foley.index.html.md#foley.index.IngestReport).

* **Parameters:**
  * **path** – A folder (walked) or a single audio file.
  * **library** – Target library (default: the process-wide default).
  * **recursive** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Recurse into sub-folders.
  * **exts** ([`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`...`](https://docs.python.org/3/builtins/constants.html#Ellipsis)]) – Audio extensions to ingest.
  * **on_error** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – `'collect'` records per-file errors and continues;
    `'raise'` re-raises the first error. Either way an
    [`EgressBlocked`](_autosummary/foley.runtime.html.md#foley.runtime.EgressBlocked) (offline, the embedder’s weights not
    cached) raises at once: every file would fail the same way.
  * **\*\*ingest_one_kw** – Forwarded to [`ingest_one()`](_autosummary/foley.index.html.md#foley.index.ingest_one) (license, taggers, QC
    flags, …).
* **Return type:**
  [`IngestReport`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestReport)
* **Returns:**
  An [`IngestReport`](_autosummary/foley.index.html.md#foley.index.IngestReport) (with `.summary()` counts and per-file results).

### foley.index.ingest_one(src, , library=None, sound_id=None, source_uri=None, license=None, tagger=None, zeroshot_tagger=None, captioner=None, do_qc=True, min_status=QCStatus.warn, do_supervised=True, do_zeroshot=True, do_caption=True, thresholds=QCThresholds(clip_full_scale=0.999, clip_min_run=3, clip_reject_ratio=0.0001, clip_reject_run=10, true_peak_max_dbtp=-1.0, true_peak_oversample=4, dc_offset_fail=0.01, dc_offset_warn=0.001, silence_rms_dbfs=-60.0, snr_clean_db=20.0, snr_quiet_percentile=10.0, snr_frame_s=0.025, snr_hop_s=0.01, edge_rel_peak_dbfs=-40.0, edge_fade_s=0.01, lufs_gate_floor=-70.0, lufs_outlier_lu=6.0, duration_min_s=0.1, deliver_min_sample_rate=44100), store=True, allow_ai_training_forbidden=False, seed_tags=None, commercial=None)

Ingest one clip into `library` and return an [`IngestResult`](_autosummary/foley.index.html.md#foley.index.IngestResult).

Pipeline: probe + decode-once -> content-address dedup -> QC gate -> embed
(once) -> supervised + zero-shot tags -> caption -> resolve UCS -> assemble
`SoundRecord` -> [`SoundLibrary.add()`](_autosummary/foley.index.html.md#foley.index.SoundLibrary.add).

* **Parameters:**
  * **src** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike), [`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes), [`BinaryIO`](https://docs.python.org/3/library/typing.html#typing.BinaryIO)]) – A path, `bytes`, or file-like audio source.
  * **library** – Target [`SoundLibrary`](_autosummary/foley.index.library.html.md#foley.index.library.SoundLibrary) (default: the
    process-wide default library).
  * **sound_id** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional canonical id override. Defaults to `None` → the
    content-hash of the decoded PCM (the local-ingest identity, used as the
    record `id` and dedup key). A live source adapter passes a short,
    case-stable, source-native id (e.g. `'freesound:12345'`) so dedup keys
    on the stable id rather than on re-fetched (lossy, byte-varying) preview
    bytes; when it does, the PCM hash is computed only for the (skipped)
    default and is not persisted. Separately, `content_sha256` records the
    hash of the stored FLAC **archive** bytes (set by `store_sound`), which
    is a different byte source from this PCM hash.
  * **source_uri** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional by-reference fetchable URI override. Defaults to
    `None` → the resolved local path when `src` is path-like. A live
    adapter passes the stable source page URL (e.g.
    `'https://freesound.org/s/12345/'`) that [`foley.stores.store_sound()`](_autosummary/foley.stores.html.md#foley.stores.store_sound)
    requires for a by-reference sound.
  * **license** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)]) – Rights record, or a `license_id` string the caller asserts
    (`'user-owned'`), or `None` (default): rights **unknown**, so the clip
    is indexed but [`foley.keep()`](_autosummary/foley.html.md#foley.keep) refuses it (#55). See
    [`resolve_ingest_license()`](_autosummary/foley.index.html.md#foley.index.resolve_ingest_license).
  * **tagger** – Supervised [`Tagger`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.Tagger) (default: PANNs
    via [`default_tagger()`](_autosummary/foley.index.taggers.html.md#foley.index.taggers.default_tagger)).
  * **zeroshot_tagger** – Zero-shot tagger (default: CLAP via
    [`default_zeroshot_tagger()`](_autosummary/foley.index.taggers.html.md#foley.index.taggers.default_zeroshot_tagger)).
  * **captioner** – Optional [`Captioner`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.Captioner) (default:
    none — the caption stage is off unless one is injected).
  * **do_qc** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Run the Tier-0 QC gate.
  * **min_status** ([`QCStatus`](_autosummary/foley.qc.html.md#foley.qc.QCStatus)) – Admission floor — a QC status worse than this is quarantined
    (default `warn`: only `fail` clips are rejected).
  * **do_caption** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Toggle each enrichment stage.
  * **thresholds** ([`QCThresholds`](_autosummary/foley.qc.html.md#foley.qc.QCThresholds)) – QC thresholds.
  * **store** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – If `False`, assemble the record but do not add it to the library
    (probe/QC/enrich only).
  * **seed_tags** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`list`](https://docs.python.org/3/builtins/stdtypes.html#list)]) – Optional caller-supplied tags (e.g. a corpus’s folder-path
    taxonomy) unioned into the record’s `tags` alongside the
    supervised/zero-shot tags — so they feed the BM25 keyword index.
  * **allow_ai_training_forbidden** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – The universal fail-closed rights gate. A
    sound whose license has `ai_training_ok=False` (e.g. Sonniss,
    BBC RemArc) is refused with status `'rights_blocked'` *before* it is
    embedded or stored — CLAP-embedding-and-persisting is itself a form of
    AI training on the corpus. Pass `True` to record explicit operator
    consent and admit it anyway (see `foley.bootstrap.bootstrap()`’s
    `accept_ai_restricted`). Protects every ingest path, not just
    bootstrap.
  * **commercial** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`bool`](https://docs.python.org/3/builtins/functions.html#bool)]) – Whether the AI use is for a commercial purpose (decides a
    `nc_open_source_only` scope, #69). `None` (default) means
    [`DEFAULT_INTENDED_USE`](_autosummary/foley.licensing.html.md#foley.licensing.DEFAULT_INTENDED_USE)’s (commercial).
* **Return type:**
  [`IngestResult`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult)
* **Returns:**
  An [`IngestResult`](_autosummary/foley.index.html.md#foley.index.IngestResult); its `record` is `None` when quarantined, a
  duplicate, or rights-blocked.

### foley.index.lancedb_available()

True if `lancedb` is importable (the `foley[index]` extra is present).

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### foley.index.parse_ucs_filename(filename, , table=None)

Parse a UCS-conformant filename to `(ucs_category, ucs_subcategory)`.

Fail-quiet: returns `(None, None)` when the name is not UCS-conformant or
its CatID token is unknown (so a wrong subcategory is never emitted).

* **Parameters:**
  * **filename** – A path or filename (only the basename’s token 0 is used).
  * **table** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`UcsTable`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.UcsTable)]) – The UCS table to resolve against (defaults to
    `default_ucs_table()`).
* **Return type:**
  [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)], [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]

### foley.index.reciprocal_rank_fusion(ranked_id_lists, , k=60)

Fuse several ranked id lists into one, by reciprocal rank.

* **Parameters:**
  * **ranked_id_lists** ([`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]) – Each element is a list of ids in descending-relevance
    order (best first). Lists may overlap and may differ in length.
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The RRF damping constant (default `RRF_K` = 60).
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]
* **Returns:**
  `[(id, fused_score), ...]` sorted by fused score descending, ties
  broken by `id` ascending (so the fusion is fully deterministic).

### foley.index.resolve_catid(, tags=(), caption=None, audioset_labels=(), filename=None, table=None, audioset_map=None)

Resolve inputs to a best UCS CatID by the staged precedence.

* **Parameters:**
  * **tags** ([`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Free tags on the sound.
  * **caption** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Free-text caption/description.
  * **audioset_labels** ([`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – AudioSet MIDs or names (e.g. from PANNs).
  * **filename** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional UCS-style filename/path (its token-0 CatID wins if
    recognized).
  * **table** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`UcsTable`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.UcsTable)]) – UCS table (defaults to `default_ucs_table()`).
  * **audioset_map** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`AudioSetUcsMap`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.AudioSetUcsMap)]) – AudioSet->UCS map (defaults to
    [`default_audioset_ucs_map()`](_autosummary/foley.index.taxonomy.audioset.html.md#foley.index.taxonomy.audioset.default_audioset_ucs_map)).
* **Return type:**
  [`CatIdResolution`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.CatIdResolution)
* **Returns:**
  A [`CatIdResolution`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.CatIdResolution) (falsy when
  nothing resolved).

### foley.index.resolve_ingest_license(license, , source_url=None)

The rights record for a local ingest: fail-closed unless rights are asserted (#55).

* `None` (no licence given) → `license_id='unknown'`, `rights_verified=False`:
  [`foley.keep()`](_autosummary/foley.html.md#foley.keep) refuses it for every use. The bytes are still kept locally
  > (`cache_bytes_ok=True`: it is the user’s own disk, no terms of service apply),
  > so the clip is searchable and can be re-stamped later without re-ingesting.
* a `str` → that `license_id`, which must be a `LICENSE_FLAGS` row, with
  `rights_verified=True` (the caller is asserting it) and `verified_at` set —
  e.g. `'user-owned'` for the user’s own recordings.
* a [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord) → used as given.

* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If a `str` licence id has no `LICENSE_FLAGS` row.
* **Return type:**
  [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)

### foley.index.restamp_rights(library=None, , license='unknown', ids=None, where=None, select='legacy-user-owned', apply=False)

Re-stamp the licence of stored sounds; a dry run unless `apply=True`.

The migration for libraries built before #55 / #56. A re-ingest does not do it
(content-addressed dedup skips files already stored), so this rewrites the stored
records in place:

* `foley restamp-rights --apply` — every sound an older foley stamped
  `user-owned` without being told becomes `unknown` (fail closed);
* `foley restamp-rights --license user-owned --apply` — the same sounds, but you
  assert that you own them (now recorded with a `verified_at` timestamp);
* `foley restamp-rights --select legacy-elevenlabs --license elevenlabs-paid-plan
  --apply` — ElevenLabs generations made before the plan was explicit;
* `foley restamp-rights --select has-license-url --license from-url --apply` —
  every record that kept its source’s licence string (Freesound pulls) is
  re-derived by today’s mapper (CC versions, PDM, NC spellings);
* corpus clips (Clotho, FSD50K) are repaired by re-running `foley bootstrap`,
  which re-stamps already-stored clips from the corpus metadata.

* **Parameters:**
  * **library** – The [`SoundLibrary`](_autosummary/foley.index.library.html.md#foley.index.library.SoundLibrary) (default: the default one).
  * **license** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The `license_id` to stamp (a `LICENSE_FLAGS` row; default
    `'unknown'`), or `FROM_LICENSE_URL` to re-derive each record from
    its own `license_url`.
  * **ids** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]) – Re-stamp exactly these sound ids (overrides `where` / `select`).
  * **where** – A predicate `LicenseRecord -> bool` choosing the records (overrides
    `select`).
  * **select** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – A `LEGACY_SELECTORS` name (default `'legacy-user-owned'`).
  * **apply** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Write the change. `False` (default) only reports what would change.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]
* **Returns:**
  The ids that were (or, in a dry run, would be) re-stamped.
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If `license` or `select` is unknown.

### foley.index.sqlite_vec_loadable()

True if `sqlite_vec` is installed AND this interpreter can load it.

The macOS system / pyenv CPython builds frequently ship a `sqlite3` without
loadable-extension support (no `enable_load_extension`); on those,
sqlite-vec cannot be used even when 

```
``
```

pip install\`\`ed. This probes both.

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### foley.index.vector_search(qvec, , vindex, k=10, where=None)

Pure audio<->audio (or clip->library) vector search — no keyword leg.

Used by `SoundLibrary.similar` and by searching with a reference clip.
Hits keep their cosine similarity in `clap_score` and preserve the index’s
own descending-similarity order (`rrf_score` is left `None` — there is no
fusion).

* **Parameters:**
  * **qvec** (`ndarray`) – An already-L2-normalized `(dim,)` query vector.
  * **vindex** ([`VectorIndex`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.VectorIndex)) – The vector index.
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Number of neighbours to return.
  * **where** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Optional metadata push-down.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`FusedHit`](_autosummary/foley.index.search.html.md#foley.index.search.FusedHit)]
* **Returns:**
  Up to `k` :class:

  ```
  `
  ```

  FusedHit\`s in descending-similarity order.

### Modules

| [`taxonomy`](_autosummary/foley.index.taxonomy.html.md#module-foley.index.taxonomy)   | UCS + AudioSet taxonomy: the `tags -> UCS CatID` resolver for foley.            |
|-----------------------------------------------------------------------------------------|---------------------------------------------------------------------------------|
| [`embedders`](_autosummary/foley.index.embedders.html.md#module-foley.index.embedders) | Embedders — the joint text<->audio space that powers retrieval.                 |
| [`indexes`](_autosummary/foley.index.indexes.html.md#module-foley.index.indexes)     | Concrete vector + keyword index backends (the swappable retrieval engines).     |
| [`ingest`](_autosummary/foley.index.ingest.html.md#module-foley.index.ingest)       | The ingestion pipeline — turn any audio file into a searchable SoundRecord.     |
| [`library`](_autosummary/foley.index.library.html.md#module-foley.index.library)     | The `SoundLibrary` façade — one searchable, license-aware sound library.        |
| [`protocols`](_autosummary/foley.index.protocols.html.md#module-foley.index.protocols) | Structural contracts for the foley index (the retrieval boundary).              |
| [`search`](_autosummary/foley.index.search.html.md#module-foley.index.search)       | Hybrid retrieval: reciprocal rank fusion over a vector list and a keyword list. |
| [`taggers`](_autosummary/foley.index.taggers.html.md#module-foley.index.taggers)     | Taggers — auto-fill a sound's semantic labels on ingest (report 03).            |


# _autosummary/foley.index.indexes.html.md

# foley.index.indexes

Concrete vector + keyword index backends (the swappable retrieval engines).

Each backend satisfies BOTH [`VectorIndex`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.VectorIndex) and
[`KeywordIndex`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.KeywordIndex), so one object serves as both
`vindex` and `kindex` in [`SoundLibrary`](_autosummary/foley.index.library.html.md#foley.index.library.SoundLibrary). Three
tiers, same protocol (report 04 §6.2 — “small protocols, swapped stores”):

> * [`MemoryIndex`](_autosummary/foley.index.indexes.html.md#foley.index.indexes.MemoryIndex)   — pure `numpy` cosine + a compact stdlib BM25. Zero
>   persistence, zero optional-extra (just numpy). The always-available default
>   for tests and small ephemeral libraries; it also exercises foley’s own RRF.
> * [`LanceIndex`](_autosummary/foley.index.indexes.html.md#foley.index.indexes.LanceIndex)    — LanceDB: one table with a vector column + native
>   full-text index (no `tantivy` needed). Local dir or `s3://` unchanged —
>   the recommended persistent default (`foley[index]`).
> * [`SqliteVecIndex`](_autosummary/foley.index.indexes.html.md#foley.index.indexes.SqliteVecIndex) — sqlite-vec (`vec0`) + stdlib FTS5 in one file
>   (`foley[index-sqlite]`). Minimalist single-file option; \*\*requires an
>   interpreter whose `sqlite3` allows loadable extensions\*\* (see
>   [`sqlite_vec_loadable()`](_autosummary/foley.index.indexes.html.md#foley.index.indexes.sqlite_vec_loadable)).

All three lazy-import their heavy dependency inside methods, so importing this
module costs only the stdlib. Fusion is NOT done in-engine: `knn()` and
`bm25()` return raw ranked lists and [`foley.index.search`](_autosummary/foley.index.search.html.md#module-foley.index.search) fuses them via
one shared RRF, so ranking is identical across backends.

### Functions

| [`default_index`](_autosummary/foley.index.indexes.html.md#foley.index.indexes.default_index)(\*, data_dir, dim)   | Build the best available persistent index for a library.               |
|-------------------------------------------------------------------------------------|------------------------------------------------------------------------|
| [`lancedb_available`](_autosummary/foley.index.indexes.html.md#foley.index.indexes.lancedb_available)()                | True if `lancedb` is importable (the `foley[index]` extra is present). |
| [`sqlite_vec_loadable`](_autosummary/foley.index.indexes.html.md#foley.index.indexes.sqlite_vec_loadable)()              | True if `sqlite_vec` is installed AND this interpreter can load it.    |

### Classes

| [`LanceIndex`](_autosummary/foley.index.indexes.html.md#foley.index.indexes.LanceIndex)(\*, uri, dim[, table_name])   | LanceDB-backed index: one table with a vector column + a native FTS index.   |
|-------------------------------------------------------------------------------------------|------------------------------------------------------------------------------|
| [`MemoryIndex`](_autosummary/foley.index.indexes.html.md#foley.index.indexes.MemoryIndex)(\*[, dim])                   | In-memory vector + keyword index (numpy cosine + compact BM25).              |
| [`SqliteVecIndex`](_autosummary/foley.index.indexes.html.md#foley.index.indexes.SqliteVecIndex)(\*, path, dim)            | Single-file index: sqlite-vec `vec0` KNN + stdlib FTS5 keyword search.       |

### *class* foley.index.indexes.LanceIndex(, uri, dim, table_name='sounds')

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

LanceDB-backed index: one table with a vector column + a native FTS index.

Writes are staged per id and flushed on the next read (or explicit
[`commit()`](_autosummary/foley.index.indexes.html.md#foley.index.indexes.LanceIndex.commit)), so a sound’s vector ([`upsert()`](_autosummary/foley.index.indexes.html.md#foley.index.indexes.LanceIndex.upsert)) and text ([`index()`](_autosummary/foley.index.indexes.html.md#foley.index.indexes.LanceIndex.index))
— which arrive as two separate protocol calls — are merged into one row and
written as an efficient batch. Vector search is exact cosine (no ANN index is
built at this tier; adding one is a scale-time optimization). Fusion is done
by [`foley.index.search`](_autosummary/foley.index.search.html.md#module-foley.index.search), not by LanceDB’s native hybrid reranker, so
ranking matches every other backend.

One-table constraint: the vector column is mandatory, so \*\*every indexed row
needs a vector\*\*. `SoundLibrary.add` enforces this (it raises without an
embedding source), so the façade path is safe; a bare `index()` with no
matching `upsert()` stages a text-only row that stays unflushed (never
keyword-searchable). For a keyword-only library with no embeddings, use
[`MemoryIndex`](_autosummary/foley.index.indexes.html.md#foley.index.indexes.MemoryIndex) or [`SqliteVecIndex`](_autosummary/foley.index.indexes.html.md#foley.index.indexes.SqliteVecIndex) (independent vector/text tables).

#### bm25(query, k, , where=None)

Native full-text (BM25) search; returns `[(id, score), ...]`.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

#### commit()

Flush all staged writes to the LanceDB table.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### *property* db

The lazily-connected LanceDB database handle.

#### get_vector(id)

Return the stored vector for `id` (staged or persisted), else `None`.

* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[`ndarray`]

#### index(id, text, meta)

Stage the searchable text for `id` (flushed on the next read/commit).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### knn(vector, k, , where=None)

Exact cosine KNN; returns `[(id, cosine_similarity), ...]`.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

#### *property* table

The lazily-opened (or created-empty) LanceDB table.

#### upsert(id, vector, meta)

Stage the vector for `id` (flushed on the next read/commit).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### *class* foley.index.indexes.MemoryIndex(, dim=None)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

In-memory vector + keyword index (numpy cosine + compact BM25).

Not persistent — everything lives in dicts, lost on process exit. It exists
so the full hybrid + façade path is testable and usable with only `numpy`
(no LanceDB, no torch), and as a genuine zero-config tier for small or
ephemeral libraries. The vector and text stores are independent dicts, so
[`upsert()`](_autosummary/foley.index.indexes.html.md#foley.index.indexes.MemoryIndex.upsert) and [`index()`](_autosummary/foley.index.indexes.html.md#foley.index.indexes.MemoryIndex.index) never contend.

#### bm25(query, k, , where=None)

Return the top-`k` BM25 matches for `query` (best first).

A compact Okapi BM25 (`k1=1.5`, `b=0.75`) recomputed per query — O(N)
in the corpus size, which is fine for the in-memory tier’s scale.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

#### commit()

No-op (writes are immediate); present for interface symmetry.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### get_vector(id)

Return the stored vector for `id` (or `None`).

* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[`ndarray`]

#### index(id, text, meta)

Insert or replace the searchable text (and light metadata) for `id`.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### knn(vector, k, , where=None)

Return the `k` cosine-nearest ids to `vector` (most-similar first).

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

#### upsert(id, vector, meta)

Insert or replace the vector (and light metadata) for `id`.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### *class* foley.index.indexes.SqliteVecIndex(, path, dim)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Single-file index: sqlite-vec `vec0` KNN + stdlib FTS5 keyword search.

The whole index is one SQLite file behind two virtual tables (independent, so
[`upsert()`](_autosummary/foley.index.indexes.html.md#foley.index.indexes.SqliteVecIndex.upsert) and [`index()`](_autosummary/foley.index.indexes.html.md#foley.index.indexes.SqliteVecIndex.index) never contend). \*\*Requires an interpreter
whose `sqlite3` permits loadable extensions\*\* — probe with
[`sqlite_vec_loadable()`](_autosummary/foley.index.indexes.html.md#foley.index.indexes.sqlite_vec_loadable) before constructing; the constructor raises a
clear error otherwise.

#### bm25(query, k, , where=None)

FTS5 BM25 search; returns `[(id, score), ...]` best-first.

FTS5’s `rank` is more-negative-is-better; it is negated so the returned
score is larger-is-better (consistent with the other backends).

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

#### close()

Close the underlying SQLite connection.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### commit()

Commit any pending SQLite transaction (writes auto-commit already).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### get_vector(id)

Return the stored vector for `id` (or `None`).

* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[`ndarray`]

#### index(id, text, meta)

Insert or replace the searchable text for `id` in the FTS5 table.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### knn(vector, k, , where=None)

KNN over `vec0` (cosine); returns `[(id, cosine_similarity), ...]`.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

#### upsert(id, vector, meta)

Insert or replace the vector for `id` in the `vec0` table.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### foley.index.indexes.default_index(, data_dir, dim)

Build the best available persistent index for a library.

Degradation ladder: LanceDB (`foley[index]`) → sqlite-vec
(`foley[index-sqlite]`, if loadable) → an informative error. The
non-persistent [`MemoryIndex`](_autosummary/foley.index.indexes.html.md#foley.index.indexes.MemoryIndex) is never chosen automatically (a library
must survive restarts); inject it explicitly for tests/ephemeral use.

* **Parameters:**
  * **data_dir** – The library data root (a `pathlib.Path`-like).
  * **dim** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The embedding dimensionality from the active embedder.
* **Returns:**
  A ready index object (both `VectorIndex` and `KeywordIndex`).
* **Raises:**
  [**RuntimeError**](https://docs.python.org/3/builtins/exceptions.html#RuntimeError) – If no persistent backend is installed/usable.

### foley.index.indexes.lancedb_available()

True if `lancedb` is importable (the `foley[index]` extra is present).

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### foley.index.indexes.sqlite_vec_loadable()

True if `sqlite_vec` is installed AND this interpreter can load it.

The macOS system / pyenv CPython builds frequently ship a `sqlite3` without
loadable-extension support (no `enable_load_extension`); on those,
sqlite-vec cannot be used even when 

```
``
```

pip install\`\`ed. This probes both.

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)


# _autosummary/foley.index.ingest.html.md

# foley.index.ingest

The ingestion pipeline — turn any audio file into a searchable SoundRecord.

`probe -> QC -> supervised tag -> zero-shot tag -> caption -> resolve taxonomy
-> embed -> assemble -> store` (report 03 Stages 0-5, report 08 §3 QC gate,
report 09 §5 decode-once). It is almost entirely **composition** over primitives
that already exist:

> * decode / archive: [`foley.audio.load()`](_autosummary/foley.audio.html.md#foley.audio.load) / [`encode()`](_autosummary/foley.audio.html.md#foley.audio.encode)
>   (decode once, then fan the one array out to QC + taggers + embedder),
> * QC gate: [`foley.qc.run_qc()`](_autosummary/foley.qc.html.md#foley.qc.run_qc) (a `fail` clip is quarantined, not added),
> * embed: the library’s [`ClapEmbedder`](_autosummary/foley.index.embedders.html.md#foley.index.embedders.ClapEmbedder) (the vector
>   is reused for zero-shot tagging — no second CLAP pass),
> * taxonomy: [`foley.index.taxonomy.resolve_catid()`](_autosummary/foley.index.taxonomy.html.md#foley.index.taxonomy.resolve_catid) (tags+caption -> UCS),
> * store + index: [`foley.index.library.SoundLibrary.add()`](_autosummary/foley.index.library.html.md#foley.index.library.SoundLibrary.add)
>   (the by-value/by-reference license gate + vector upsert + BM25 index).

Enrichment stages degrade gracefully: a missing `foley[tag]` (PANNs) or
captioner is skipped with a note, never a crash — only the CLAP embedding is
required (retrieval-first). Everything heavy is lazy-imported; importing this
module costs only the stdlib.

### Module Attributes

| [`AUDIO_EXTS`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.AUDIO_EXTS)               | Audio file extensions the folder walker ingests.                                                                                                                                   |
|---------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`UNASSERTABLE_LICENSE_IDS`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.UNASSERTABLE_LICENSE_IDS) | `unknown` by definition, `PDM-1.0` (a claim about the work, not a grant — verify it by building the `LicenseRecord` yourself), and the legacy `ElevenLabs-SFX` row (say the plan). |
| [`ASSERTABLE_LICENSE_IDS`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.ASSERTABLE_LICENSE_IDS)   | The ids a caller may assert on ingest / restamp (`foley ingest --license`).                                                                                                        |
| [`LEGACY_SELECTORS`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.LEGACY_SELECTORS)         | Named selections for [`restamp_rights()`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.restamp_rights) (`foley restamp-rights --select NAME`).                                                     |
| [`FROM_LICENSE_URL`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.FROM_LICENSE_URL)         | re-derive each record's id from the licence URL / label its source served (kept in `license_url`).                                                                                 |

### Functions

| [`content_id`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.content_id)(src)                                    | Return the reproducible content-hash id foley assigns to `src`.                                                   |
|-----------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------|
| [`has_license_url`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.has_license_url)(lic)                               | Whether `lic` kept the licence string its source served (re-derivable).                                           |
| [`ingest_folder`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.ingest_folder)(path, \*[, library, recursive, ...]) | Ingest every audio file under `path` and return an [`IngestReport`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestReport). |
| [`ingest_one`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.ingest_one)(src, \*[, library, sound_id, ...])      | Ingest one clip into `library` and return an [`IngestResult`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult).       |
| [`is_legacy_default_user_owned`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.is_legacy_default_user_owned)(lic)                  | Whether `lic` is the `user-owned` stamp foley used to put on any unlicensed ingest.                               |
| [`is_legacy_elevenlabs`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.is_legacy_elevenlabs)(lic)                          | Whether `lic` is an ElevenLabs generation stamped before the plan was explicit (#56).                             |
| [`iter_audio_files`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.iter_audio_files)(path, \*[, recursive, exts])      | Yield the audio files under `path` (or `path` itself if it is a file).                                            |
| [`resolve_ingest_license`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.resolve_ingest_license)(license, \*[, source_url])  | The rights record for a local ingest: fail-closed unless rights are asserted (#55).                               |
| [`restamp_rights`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.restamp_rights)([library, license, ids, ...])       | Re-stamp the licence of stored sounds; a dry run unless `apply=True`.                                             |

### Classes

| [`IngestReport`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestReport)(root[, results, notes])        | The rolled-up outcome of a folder ingest (JSON-serializable).   |
|----------------------------------------------------------------------------------------------|-----------------------------------------------------------------|
| [`IngestResult`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult)(id, status[, record, qc, ...]) | The outcome of ingesting one clip.                              |

### foley.index.ingest.ASSERTABLE_LICENSE_IDS *= frozenset({'CC-BY-2.0', 'CC-BY-2.5', 'CC-BY-3.0', 'CC-BY-4.0', 'CC-BY-NC-2.0', 'CC-BY-NC-2.5', 'CC-BY-NC-3.0', 'CC-BY-NC-4.0', 'CC-Sampling+-1.0', 'CC0-1.0', 'MIT', 'Pixabay-Content', 'RemArc', 'Sonniss-GDC', 'Stability-Community', 'elevenlabs-free-plan', 'elevenlabs-paid-plan', 'user-owned'})*

The ids a caller may assert on ingest / restamp (`foley ingest --license`).

### foley.index.ingest.AUDIO_EXTS *: [tuple](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[str](https://docs.python.org/3/builtins/stdtypes.html#str), ...]* *= ('.wav', '.flac', '.aiff', '.aif', '.ogg', '.mp3', '.opus', '.m4a')*

Audio file extensions the folder walker ingests.

### foley.index.ingest.FROM_LICENSE_URL *= 'from-url'*

re-derive each record’s id from the
licence URL / label its source served (kept in `license_url`).

* **Type:**
  `restamp_rights(license=FROM_LICENSE_URL)`

### *class* foley.index.ingest.IngestReport(root, results=<factory>, notes=<factory>)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

The rolled-up outcome of a folder ingest (JSON-serializable).

#### error(path, exc)

Record a per-file error without aborting the run.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### *property* errored *: [list](https://docs.python.org/3/builtins/stdtypes.html#list)[[IngestResult](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult)]*

Results that raised during ingest.

#### exception *= None*

The exception behind the last `error` result, kept so a raising façade can
chain it (`raise ... from`); a plain attribute, never serialized.

#### *property* ingested *: [list](https://docs.python.org/3/builtins/stdtypes.html#list)[[IngestResult](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult)]*

Results that were added to the library (`pass` or `warn`).

#### *property* quarantined *: [list](https://docs.python.org/3/builtins/stdtypes.html#list)[[IngestResult](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult)]*

Results rejected by the QC gate.

#### record(result)

Append one [`IngestResult`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### *property* rights_blocked *: [list](https://docs.python.org/3/builtins/stdtypes.html#list)[[IngestResult](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult)]*

Results refused by the fail-closed AI-training/license rights gate.

#### *property* skipped *: [list](https://docs.python.org/3/builtins/stdtypes.html#list)[[IngestResult](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult)]*

Results skipped as content-addressed duplicates.

#### summary()

A counts dict for a console/CLI summary.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### *class* foley.index.ingest.IngestResult(id, status, record=None, qc=None, notes=<factory>, error=None, cost_estimate_usd=None, cost_actual_usd=None)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

The outcome of ingesting one clip.

`status`: `'pass'`/`'warn'` (ingested), `'quarantined'` (QC-rejected,
not added), `'skipped_dup'` (content already in the library),
`'rights_blocked'` (license forbids AI training / embedding, refused before
embed — see [`ingest_one()`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.ingest_one)), `'skipped_license'` (dropped by a
bootstrap commercial-use / fail-closed license filter), or `'error'`.
`record` is present only when the clip was ingested.

### foley.index.ingest.LEGACY_SELECTORS *= {'has-license-url': <function has_license_url>, 'legacy-elevenlabs': <function is_legacy_elevenlabs>, 'legacy-user-owned': <function is_legacy_default_user_owned>}*

Named selections for [`restamp_rights()`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.restamp_rights) (`foley restamp-rights --select NAME`).

### foley.index.ingest.UNASSERTABLE_LICENSE_IDS *= frozenset({'ElevenLabs-SFX', 'PDM-1.0', 'unknown'})*

`unknown` by definition,
`PDM-1.0` (a claim about the work, not a grant — verify it by building the
`LicenseRecord` yourself), and the legacy `ElevenLabs-SFX` row (say the plan).

* **Type:**
  Licence ids that can never be *asserted* as verified

### foley.index.ingest.content_id(src)

Return the reproducible content-hash id foley assigns to `src`.

Decodes `src` (path / bytes / file-like) to canonical PCM and hashes it —
the SAME value [`ingest_one()`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.ingest_one) mints as the record id when `sound_id` is
`None`. Exposed so a caller can learn a clip’s stored id *before* ingesting
it (e.g. the generate façade keys a content-credential sidecar by the id and
sets `license.c2pa_manifest_ref` in the same pass — see
`foley.sources.generate.generate()`). Cheap-ish: it fully decodes `src`.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### foley.index.ingest.has_license_url(lic)

Whether `lic` kept the licence string its source served (re-derivable).

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### foley.index.ingest.ingest_folder(path, , library=None, recursive=True, exts=('.wav', '.flac', '.aiff', '.aif', '.ogg', '.mp3', '.opus', '.m4a'), on_error='collect', \*\*ingest_one_kw)

Ingest every audio file under `path` and return an [`IngestReport`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestReport).

* **Parameters:**
  * **path** – A folder (walked) or a single audio file.
  * **library** – Target library (default: the process-wide default).
  * **recursive** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Recurse into sub-folders.
  * **exts** ([`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`...`](https://docs.python.org/3/builtins/constants.html#Ellipsis)]) – Audio extensions to ingest.
  * **on_error** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – `'collect'` records per-file errors and continues;
    `'raise'` re-raises the first error. Either way an
    [`EgressBlocked`](_autosummary/foley.runtime.html.md#foley.runtime.EgressBlocked) (offline, the embedder’s weights not
    cached) raises at once: every file would fail the same way.
  * **\*\*ingest_one_kw** – Forwarded to [`ingest_one()`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.ingest_one) (license, taggers, QC
    flags, …).
* **Return type:**
  [`IngestReport`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestReport)
* **Returns:**
  An [`IngestReport`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestReport) (with `.summary()` counts and per-file results).

### foley.index.ingest.ingest_one(src, , library=None, sound_id=None, source_uri=None, license=None, tagger=None, zeroshot_tagger=None, captioner=None, do_qc=True, min_status=QCStatus.warn, do_supervised=True, do_zeroshot=True, do_caption=True, thresholds=QCThresholds(clip_full_scale=0.999, clip_min_run=3, clip_reject_ratio=0.0001, clip_reject_run=10, true_peak_max_dbtp=-1.0, true_peak_oversample=4, dc_offset_fail=0.01, dc_offset_warn=0.001, silence_rms_dbfs=-60.0, snr_clean_db=20.0, snr_quiet_percentile=10.0, snr_frame_s=0.025, snr_hop_s=0.01, edge_rel_peak_dbfs=-40.0, edge_fade_s=0.01, lufs_gate_floor=-70.0, lufs_outlier_lu=6.0, duration_min_s=0.1, deliver_min_sample_rate=44100), store=True, allow_ai_training_forbidden=False, seed_tags=None, commercial=None)

Ingest one clip into `library` and return an [`IngestResult`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult).

Pipeline: probe + decode-once -> content-address dedup -> QC gate -> embed
(once) -> supervised + zero-shot tags -> caption -> resolve UCS -> assemble
`SoundRecord` -> `SoundLibrary.add()`.

* **Parameters:**
  * **src** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike), [`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes), [`BinaryIO`](https://docs.python.org/3/library/typing.html#typing.BinaryIO)]) – A path, `bytes`, or file-like audio source.
  * **library** – Target [`SoundLibrary`](_autosummary/foley.index.library.html.md#foley.index.library.SoundLibrary) (default: the
    process-wide default library).
  * **sound_id** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional canonical id override. Defaults to `None` → the
    content-hash of the decoded PCM (the local-ingest identity, used as the
    record `id` and dedup key). A live source adapter passes a short,
    case-stable, source-native id (e.g. `'freesound:12345'`) so dedup keys
    on the stable id rather than on re-fetched (lossy, byte-varying) preview
    bytes; when it does, the PCM hash is computed only for the (skipped)
    default and is not persisted. Separately, `content_sha256` records the
    hash of the stored FLAC **archive** bytes (set by `store_sound`), which
    is a different byte source from this PCM hash.
  * **source_uri** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional by-reference fetchable URI override. Defaults to
    `None` → the resolved local path when `src` is path-like. A live
    adapter passes the stable source page URL (e.g.
    `'https://freesound.org/s/12345/'`) that [`foley.stores.store_sound()`](_autosummary/foley.stores.html.md#foley.stores.store_sound)
    requires for a by-reference sound.
  * **license** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)]) – Rights record, or a `license_id` string the caller asserts
    (`'user-owned'`), or `None` (default): rights **unknown**, so the clip
    is indexed but [`foley.keep()`](_autosummary/foley.html.md#foley.keep) refuses it (#55). See
    [`resolve_ingest_license()`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.resolve_ingest_license).
  * **tagger** – Supervised [`Tagger`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.Tagger) (default: PANNs
    via [`default_tagger()`](_autosummary/foley.index.taggers.html.md#foley.index.taggers.default_tagger)).
  * **zeroshot_tagger** – Zero-shot tagger (default: CLAP via
    [`default_zeroshot_tagger()`](_autosummary/foley.index.taggers.html.md#foley.index.taggers.default_zeroshot_tagger)).
  * **captioner** – Optional [`Captioner`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.Captioner) (default:
    none — the caption stage is off unless one is injected).
  * **do_qc** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Run the Tier-0 QC gate.
  * **min_status** ([`QCStatus`](_autosummary/foley.qc.html.md#foley.qc.QCStatus)) – Admission floor — a QC status worse than this is quarantined
    (default `warn`: only `fail` clips are rejected).
  * **do_caption** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Toggle each enrichment stage.
  * **thresholds** ([`QCThresholds`](_autosummary/foley.qc.html.md#foley.qc.QCThresholds)) – QC thresholds.
  * **store** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – If `False`, assemble the record but do not add it to the library
    (probe/QC/enrich only).
  * **seed_tags** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`list`](https://docs.python.org/3/builtins/stdtypes.html#list)]) – Optional caller-supplied tags (e.g. a corpus’s folder-path
    taxonomy) unioned into the record’s `tags` alongside the
    supervised/zero-shot tags — so they feed the BM25 keyword index.
  * **allow_ai_training_forbidden** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – The universal fail-closed rights gate. A
    sound whose license has `ai_training_ok=False` (e.g. Sonniss,
    BBC RemArc) is refused with status `'rights_blocked'` *before* it is
    embedded or stored — CLAP-embedding-and-persisting is itself a form of
    AI training on the corpus. Pass `True` to record explicit operator
    consent and admit it anyway (see `foley.bootstrap.bootstrap()`’s
    `accept_ai_restricted`). Protects every ingest path, not just
    bootstrap.
  * **commercial** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`bool`](https://docs.python.org/3/builtins/functions.html#bool)]) – Whether the AI use is for a commercial purpose (decides a
    `nc_open_source_only` scope, #69). `None` (default) means
    [`DEFAULT_INTENDED_USE`](_autosummary/foley.licensing.html.md#foley.licensing.DEFAULT_INTENDED_USE)’s (commercial).
* **Return type:**
  [`IngestResult`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult)
* **Returns:**
  An [`IngestResult`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult); its `record` is `None` when quarantined, a
  duplicate, or rights-blocked.

### foley.index.ingest.is_legacy_default_user_owned(lic)

Whether `lic` is the `user-owned` stamp foley used to put on any unlicensed ingest.

Before #55, `foley.ingest(path)` with no licence stamped every file
`user-owned` + verified. An explicit assertion now also records `verified_at`;
the old default never did, which is what tells them apart.

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### foley.index.ingest.is_legacy_elevenlabs(lic)

Whether `lic` is an ElevenLabs generation stamped before the plan was explicit (#56).

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### foley.index.ingest.iter_audio_files(path, , recursive=True, exts=('.wav', '.flac', '.aiff', '.aif', '.ogg', '.mp3', '.opus', '.m4a'))

Yield the audio files under `path` (or `path` itself if it is a file).

The shared corpus/folder walk — reused by [`ingest_folder()`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.ingest_folder) and the
bulk-corpus adapters in [`foley.sources`](_autosummary/foley.sources.html.md#module-foley.sources) so the traversal is not forked.

* **Parameters:**
  * **path** – A folder (walked) or a single audio file.
  * **recursive** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Recurse into sub-folders.
  * **exts** ([`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`...`](https://docs.python.org/3/builtins/constants.html#Ellipsis)]) – Audio extensions to include (lowercased suffix match).
* **Yields:**
  Each matching file as a [`pathlib.Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path), in sorted order.
* **Return type:**
  [*Iterator*](https://docs.python.org/3/library/typing.html#typing.Iterator)[[*Path*](https://docs.python.org/3/library/pathlib.html#pathlib.Path)]

### foley.index.ingest.resolve_ingest_license(license, , source_url=None)

The rights record for a local ingest: fail-closed unless rights are asserted (#55).

* `None` (no licence given) → `license_id='unknown'`, `rights_verified=False`:
  [`foley.keep()`](_autosummary/foley.html.md#foley.keep) refuses it for every use. The bytes are still kept locally
  > (`cache_bytes_ok=True`: it is the user’s own disk, no terms of service apply),
  > so the clip is searchable and can be re-stamped later without re-ingesting.
* a `str` → that `license_id`, which must be a `LICENSE_FLAGS` row, with
  `rights_verified=True` (the caller is asserting it) and `verified_at` set —
  e.g. `'user-owned'` for the user’s own recordings.
* a [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord) → used as given.

* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If a `str` licence id has no `LICENSE_FLAGS` row.
* **Return type:**
  [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)

### foley.index.ingest.restamp_rights(library=None, , license='unknown', ids=None, where=None, select='legacy-user-owned', apply=False)

Re-stamp the licence of stored sounds; a dry run unless `apply=True`.

The migration for libraries built before #55 / #56. A re-ingest does not do it
(content-addressed dedup skips files already stored), so this rewrites the stored
records in place:

* `foley restamp-rights --apply` — every sound an older foley stamped
  `user-owned` without being told becomes `unknown` (fail closed);
* `foley restamp-rights --license user-owned --apply` — the same sounds, but you
  assert that you own them (now recorded with a `verified_at` timestamp);
* `foley restamp-rights --select legacy-elevenlabs --license elevenlabs-paid-plan
  --apply` — ElevenLabs generations made before the plan was explicit;
* `foley restamp-rights --select has-license-url --license from-url --apply` —
  every record that kept its source’s licence string (Freesound pulls) is
  re-derived by today’s mapper (CC versions, PDM, NC spellings);
* corpus clips (Clotho, FSD50K) are repaired by re-running `foley bootstrap`,
  which re-stamps already-stored clips from the corpus metadata.

* **Parameters:**
  * **library** – The [`SoundLibrary`](_autosummary/foley.index.library.html.md#foley.index.library.SoundLibrary) (default: the default one).
  * **license** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The `license_id` to stamp (a `LICENSE_FLAGS` row; default
    `'unknown'`), or [`FROM_LICENSE_URL`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.FROM_LICENSE_URL) to re-derive each record from
    its own `license_url`.
  * **ids** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]) – Re-stamp exactly these sound ids (overrides `where` / `select`).
  * **where** – A predicate `LicenseRecord -> bool` choosing the records (overrides
    `select`).
  * **select** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – A [`LEGACY_SELECTORS`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.LEGACY_SELECTORS) name (default `'legacy-user-owned'`).
  * **apply** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Write the change. `False` (default) only reports what would change.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]
* **Returns:**
  The ids that were (or, in a dry run, would be) re-stamped.
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If `license` or `select` is unknown.


# _autosummary/foley.index.library.html.md

# foley.index.library

The `SoundLibrary` façade — one searchable, license-aware sound library.

Composes the four injected storage concerns behind a stable interface (report
04 §6.1), so each swaps local->cloud with no change to retrieval logic:

```default
SoundLibrary
├── sounds : Mapping[content_key -> bytes]   # audio blobs   (dol.Files -> S3)
├── meta   : Mapping[id -> SoundRecord]       # canonical SSOT (JSON -> S3/PG)
├── vindex : VectorIndex                      # CLAP 512-d    (LanceDB/sqlite/memory)
└── kindex : KeywordIndex (BM25)              # tags+caption  (same, or separate)
```

Progressive disclosure: `SoundLibrary()` works out of the box with sensible
local defaults (all components lazily constructed), while every store, index, and
the embedder is an optional keyword injection (open-closed). The library is a
read-only `Mapping` of `SoundRecord`s (``lib[sound_id]``);
`add()` is the write path that stores bytes and updates both indexes.

`search` embeds the query, runs the vector KNN and the BM25 keyword search, and
RRF-fuses them ([`foley.index.search`](_autosummary/foley.index.search.html.md#module-foley.index.search)) — nothing here changes between the
in-memory, LanceDB, or sqlite backends, or between local and cloud storage.

### Functions

| [`default_library`](_autosummary/foley.index.library.html.md#foley.index.library.default_library)()   | The process-wide default library (local stores + CLAP + best index).   |
|----------------------------------------------------------------------|------------------------------------------------------------------------|

### Classes

| [`SoundLibrary`](_autosummary/foley.index.library.html.md#foley.index.library.SoundLibrary)(\*[, sounds, meta, vindex, ...])   | A searchable, license-aware library of sounds (the foley INDEX façade).   |
|--------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------|

### *class* foley.index.library.SoundLibrary(, sounds=None, meta=None, vindex=None, kindex=None, embedder=None, data_dir=None, candidate_k=50, rrf_k=60)

Bases: [`Mapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Mapping)

A searchable, license-aware library of sounds (the foley INDEX façade).

Read it as a `Mapping` of `SoundRecord`s; search it with
:meth:`search` (text) / [`search_clip()`](_autosummary/foley.index.library.html.md#foley.index.library.SoundLibrary.search_clip) (a reference clip) / [`similar()`](_autosummary/foley.index.library.html.md#foley.index.library.SoundLibrary.similar)
(audio<->audio by id); browse it with [`filter()`](_autosummary/foley.index.library.html.md#foley.index.library.SoundLibrary.filter); grow it with [`add()`](_autosummary/foley.index.library.html.md#foley.index.library.SoundLibrary.add).

#### add(record, , data=None, vector=None)

Store a sound and index it (the ingest write path).

Persists bytes via [`store_sound()`](_autosummary/foley.stores.html.md#foley.stores.store_sound) (honouring the
by-value/by-reference license gate), upserts the CLAP vector into the
vector index, and indexes `caption``+``tags` into the keyword index.

A sound is retrieval-first, so it MUST carry an embedding: supply either
`data` (bytes to embed — note a by-reference sound is embedded from its
transient bytes even though they are not cached) or a precomputed
`vector`. Adding with neither raises, rather than silently indexing a
vectorless row (which the single-table `LanceIndex` cannot persist,
producing backend-dependent search results).

* **Parameters:**
  * **record** ([`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord)) – The record to add (mutated by `store_sound` with resolved
    storage fields, and stamped with the embedding model/dim).
  * **data** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes)]) – The archive bytes (required for by-value storage; also the
    source for computing `vector` when it is not supplied).
  * **vector** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[`ndarray`]) – A precomputed CLAP embedding; when omitted and `data` is
    given, it is computed via the library’s embedder.
* **Return type:**
  [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord)
* **Returns:**
  The same (persisted, indexed) `record`.
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If neither `data` nor `vector` is provided (no way to
      obtain an embedding).

#### array(sound_id, , sr=None, mono=True)

Decode a sound to a working array (`float32`).

* **Parameters:**
  * **sound_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The record id.
  * **sr** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`int`](https://docs.python.org/3/builtins/functions.html#int)]) – Target sample rate (default: the working rate, 48 kHz).
  * **mono** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Down-mix to mono (default `True`).
* **Return type:**
  `ndarray`
* **Returns:**
  The decoded working array.

#### audio(sound_id)

Return a sound’s archive bytes (by-value from the store, or from a
local by-reference path).

* **Parameters:**
  **sound_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The record id.
* **Return type:**
  [`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes)
* **Returns:**
  The raw archive bytes.
* **Raises:**
  [**LookupError**](https://docs.python.org/3/builtins/exceptions.html#LookupError) – If the bytes are neither cached (by-value) nor readable
      from a local `uri` — a remote by-reference sound needs its
      source adapter (subtask #5) to fetch.

#### *property* data_dir *: [Path](https://docs.python.org/3/library/pathlib.html#pathlib.Path)*

The data root for default stores/index.

#### *property* embedder

The text<->audio embedder (CLAP by default).

#### filter(\*\*facets)

Browse the library by metadata facets (no ranking).

Accepts the same facet keywords as [`search()`](_autosummary/foley.index.library.html.md#foley.index.library.SoundLibrary.search)’s filters
(`commercial_ok`, `ucs_category`, `min_snr`, `duration_range`)
plus any `record_attr=value` equality predicate.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord)]

#### *property* kindex

The keyword index.

#### *property* meta

The metadata store (`id -> SoundRecord`).

#### search(query, , k=10, filters=None, commercial_ok=None, ucs_category=None, min_snr=None, duration_range=None, rerank=False)

Hybrid (CLAP vector ⊕ BM25) search for a text query.

* **Parameters:**
  * **query** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The natural-language query.
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Number of results to return.
  * **filters** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Extra `{record_attr: value}` equality predicates.
  * **commercial_ok** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`bool`](https://docs.python.org/3/builtins/functions.html#bool)]) – If `True`, keep only commercially-usable sounds.
  * **ucs_category** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Keep only sounds with this UCS CatID.
  * **min_snr** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`float`](https://docs.python.org/3/builtins/functions.html#float)]) – Keep only sounds whose QC `snr_db` is at least this.
  * **duration_range** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`float`](https://docs.python.org/3/builtins/functions.html#float), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]) – Keep only sounds whose `duration_s` is in
    `(min, max)`.
  * **rerank** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Re-order the shortlist by direct query<->audio cosine
    (fills the CLAP score for keyword-only hits).
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]
* **Returns:**
  Up to `k` :class:

  ```
  `
  ```

  ~foley.base.Candidate\`s, best first.

#### search_clip(clip, , sr=None, k=10)

Search by a reference audio clip (audio<->audio via CLAP).

* **Parameters:**
  * **clip** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike), [`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes), [`BinaryIO`](https://docs.python.org/3/library/typing.html#typing.BinaryIO)]) – A working array, or a path/bytes/file decodable by
    [`foley.audio.load()`](_autosummary/foley.audio.html.md#foley.audio.load).
  * **sr** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`int`](https://docs.python.org/3/builtins/functions.html#int)]) – Sample rate when `clip` is already a working array.
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Number of results.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]
* **Returns:**
  Up to `k` :class:

  ```
  `
  ```

  ~foley.base.Candidate\`s, most-similar first.

#### similar(sound_id, , k=10)

Return the `k` sounds most similar to `sound_id` (audio<->audio).

Uses the stored vector (no re-decoding); the query sound itself is
excluded from the results.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]

#### *property* sounds

The content-addressed byte store.

#### update_record(record)

Rewrite a stored record’s metadata and re-index its keyword text.

For corrections that do not touch the audio (a re-stamped licence, a removed
caption): the bytes and the CLAP vector stay as they are.

* **Raises:**
  [**KeyError**](https://docs.python.org/3/builtins/exceptions.html#KeyError) – If `record.id` is not in the library.
* **Return type:**
  [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord)

#### *property* vindex

The vector index.

### foley.index.library.default_library()

The process-wide default library (local stores + CLAP + best index).

* **Return type:**
  [`SoundLibrary`](_autosummary/foley.index.library.html.md#foley.index.library.SoundLibrary)


# _autosummary/foley.index.protocols.html.md

# foley.index.protocols

Structural contracts for the foley index (the retrieval boundary).

These three `Protocol``s are the small, swappable seams report 04 §6.2 calls for:
keep the boundary as *protocols*, give each a zero-config sensible default, and
let every piece be replaced by keyword injection. They are the SSOT for the
Index-stage contracts (report 10 §4.2) and are deliberately kept here, separate
from any concrete backend, so the façade (:mod:`foley.index.library`) and the
search logic (:mod:`foley.index.search`) depend only on the interface — never on
``torch` / `lancedb` / `sqlite_vec`.

> * [`Embedder`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.Embedder)     — the joint text<->audio space (CLAP default).
> * [`VectorIndex`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.VectorIndex)  — approximate-nearest-neighbour over embeddings.
> * [`KeywordIndex`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.KeywordIndex) — BM25 / full-text over tags + caption.

A single backend object may satisfy *both* index protocols at once (LanceDB holds
the vector column and the FTS index in one table); the façade simply passes it as
both `vindex` and `kindex`.

This module is stdlib-only: `ndarray` appears solely in annotations, imported
under `TYPE_CHECKING` so `import foley.index.protocols` never pulls numpy.

### Classes

| [`Captioner`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.Captioner)(\*args, \*\*kwargs)    | Produce one natural-language sentence describing a clip (report 03).   |
|-----------------------------------------------------------------------------------|------------------------------------------------------------------------|
| [`Embedder`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.Embedder)(\*args, \*\*kwargs)     | A joint text<->audio embedding space (CLAP by default).                |
| [`KeywordIndex`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.KeywordIndex)(\*args, \*\*kwargs) | BM25 / full-text index over each sound's tags + caption.               |
| [`Tagger`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.Tagger)(\*args, \*\*kwargs)       | Map a clip to `(label, score)` pairs against a label vocabulary.       |
| [`VectorIndex`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.VectorIndex)(\*args, \*\*kwargs)  | Approximate-nearest-neighbour store over embedding vectors.            |

### *class* foley.index.protocols.Captioner(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

Produce one natural-language sentence describing a clip (report 03).

The caption feeds the BM25 keyword index and human display. Default is a
dedicated AAC model (EnCLAP); Qwen2-Audio is a richer promptable upgrade.
Both are `foley[caption]` adapters plugged in behind this protocol.

#### caption(wav, sr)

Return a one-sentence caption for the clip.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### *class* foley.index.protocols.Embedder(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

A joint text<->audio embedding space (CLAP by default).

One space serves both text->audio search (`embed_text` a query) and
audio<->audio similarity (`embed_audio` a clip). Implementations MUST
return **L2-normalized** `float32` arrays so a plain inner product is
cosine similarity, and MUST stamp `model_id`/`dim` so mixed-model
libraries stay coherent (each [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord) records the
`embedding_model`/`embedding_dim` it was indexed under).

#### model_id

The checkpoint id (e.g. `'laion/larger_clap_general'`).

#### dim

The embedding dimensionality (e.g. `512`).

#### embed_audio(wav, sr)

Embed one audio clip.

* **Parameters:**
  * **wav** (`ndarray`) – A working-array clip (`float32`, mono preferred). CLAP expects
    48 kHz; implementations resample as needed.
  * **sr** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The clip’s sample rate in Hz.
* **Return type:**
  `ndarray`
* **Returns:**
  A 1-D `(dim,)` L2-normalized `float32` array.

#### embed_text(text)

Embed one or more query strings.

* **Parameters:**
  **text** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]) – A single string or a list of strings.
* **Return type:**
  `ndarray`
* **Returns:**
  A 2-D `(n_texts, dim)` L2-normalized `float32` array (`n_texts`
  is `1` for a single string) — always 2-D so callers can index
  `[0]` for the single-query case.

### *class* foley.index.protocols.KeywordIndex(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

BM25 / full-text index over each sound’s tags + caption.

The default is LanceDB’s Tantivy FTS (report 04 §3.4); SQLite FTS5 is the
single-file fallback. Same `where` push-down contract as
[`VectorIndex`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.VectorIndex).

#### bm25(query, k, , where=None)

Return the top-`k` BM25 matches for `query`, best first.

* **Parameters:**
  * **query** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – A natural-language / keyword query.
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Number of matches to return.
  * **where** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Optional metadata predicates for push-down filtering.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]
* **Returns:**
  `[(id, bm25_score), ...]` in descending-score order.

#### index(id, text, meta)

Insert or replace the searchable text (and light metadata) for `id`.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### *class* foley.index.protocols.Tagger(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

Map a clip to `(label, score)` pairs against a label vocabulary.

Used on ingest to auto-fill `SoundRecord.audioset_labels` (supervised) and
`tags` (zero-shot). The default supervised tagger is PANNs CNN14 over
AudioSet; the default zero-shot tagger scores a clip against a custom/UCS
label set via CLAP (report 03). BEATs/AST are drop-in upgrades. Consumes the
working array (`float32`, any sr — the impl resamples to its model’s rate).

#### tag(wav, sr, , taxonomy='audioset', top_k=10)

Return the top-`k` `(label, score)` tags for the clip, best first.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

### *class* foley.index.protocols.VectorIndex(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

Approximate-nearest-neighbour store over embedding vectors.

The default is LanceDB (report 04 §2); Qdrant/pgvector/sqlite-vec bind the
same protocol behind the scenes. `where` is an optional metadata push-down
the façade may pass; a backend that cannot push filters down MAY ignore it
(the façade over-fetches and post-filters to stay correct either way).

#### get_vector(id)

Return the stored vector for `id` (or `None` if absent).

Needed by `SoundLibrary.similar` (fetch a sound’s own vector, then run
[`knn()`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.VectorIndex.knn)) and by the optional CLAP rerank (score keyword-only hits).

* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[`ndarray`]

#### knn(vector, k, , where=None)

Return the `k` nearest ids to `vector`, most-similar first.

* **Parameters:**
  * **vector** (`ndarray`) – A `(dim,)` query vector (already L2-normalized).
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Number of neighbours to return.
  * **where** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Optional metadata predicates for push-down filtering.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]
* **Returns:**
  `[(id, cosine_similarity), ...]` in descending-similarity order.

#### upsert(id, vector, meta)

Insert or replace the vector (and light metadata) for `id`.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)


# _autosummary/foley.index.search.html.md

# foley.index.search

Hybrid retrieval: reciprocal rank fusion over a vector list and a keyword list.

Pure orchestration + fusion. This module depends only on the small index
protocols ([`foley.index.protocols`](_autosummary/foley.index.protocols.html.md#module-foley.index.protocols)) — never on `torch`/`lancedb`/
`sqlite_vec` — so the fusion rule is one SSOT exercised identically by every
backend and directly unit-testable.

Why rank fusion (report 04 §3.2): dense CLAP cosine lives in `[-1, 1]` while
BM25 scores are unbounded, so **averaging them lets one drown the other**.
**Reciprocal Rank Fusion** (RRF; Cormack, Clarke & Büttcher, SIGIR 2009) instead
fuses on *rank position*:

```default
score(d) = Σ_rankers  1 / (k + rank_r(d))          # k = 60 (standard)
```

A document ranked high by *either* the vector list or the BM25 list floats up;
`k=60` damps the long tail. It is one line, parameter-light, and robust — the
right first-stage fusion for a mix of semantic captions and terse literal tags.

The concrete backends (LanceDB, sqlite-vec) can fuse natively in-engine; foley
deliberately fuses here instead so the ranking is byte-identical across backends
and gated by the eval harness (report 08) rather than an engine’s internals.

### Module Attributes

| [`RRF_K`](_autosummary/foley.index.search.html.md#foley.index.search.RRF_K)               | Standard RRF damping constant (Cormack et al., SIGIR 2009).      |
|----------------------------------------------------------------------|------------------------------------------------------------------|
| [`DEFAULT_CANDIDATE_K`](_autosummary/foley.index.search.html.md#foley.index.search.DEFAULT_CANDIDATE_K) | Per-ranker shortlist depth pulled from each index before fusion. |

### Functions

| [`fuse_hits`](_autosummary/foley.index.search.html.md#foley.index.search.fuse_hits)(vector_hits, keyword_hits, \*, k[, ...])   | RRF-fuse a vector ranker's hits with a keyword ranker's hits.         |
|-------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------|
| [`hybrid_search`](_autosummary/foley.index.search.html.md#foley.index.search.hybrid_search)(query, \*, embedder, vindex, kindex)   | Embed `query`, run the vector + keyword rankers, and RRF-fuse them.   |
| [`reciprocal_rank_fusion`](_autosummary/foley.index.search.html.md#foley.index.search.reciprocal_rank_fusion)(ranked_id_lists, \*[, k])     | Fuse several ranked id lists into one, by reciprocal rank.            |
| [`vector_search`](_autosummary/foley.index.search.html.md#foley.index.search.vector_search)(qvec, \*, vindex[, k, where])          | Pure audio<->audio (or clip->library) vector search — no keyword leg. |

### Classes

| [`FusedHit`](_autosummary/foley.index.search.html.md#foley.index.search.FusedHit)(id[, rrf_score, clap_score, bm25_score])   | One fused retrieval hit: an id plus the scores that produced it.   |
|------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------|

### foley.index.search.DEFAULT_CANDIDATE_K *: [int](https://docs.python.org/3/builtins/functions.html#int)* *= 50*

Per-ranker shortlist depth pulled from each index before fusion. Fusing deeper
lists than the requested `k` lets a doc ranked mid-list by one ranker but top
by the other still surface.

### *class* foley.index.search.FusedHit(id, rrf_score=None, clap_score=None, bm25_score=None)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

One fused retrieval hit: an id plus the scores that produced it.

The raw component scores are carried through (not just the fused rank score)
so the façade can stamp them onto a [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)
(`clap_score` / `bm25_score` / `rrf_score`) for display and debugging.

#### id

The sound id.

#### rrf_score

The fused RRF score (`None` for a pure-vector search).

#### clap_score

Cosine similarity from the vector ranker (`None` if the id
appeared only in the keyword list).

#### bm25_score

BM25 score from the keyword ranker (`None` if the id
appeared only in the vector list).

### foley.index.search.RRF_K *: [int](https://docs.python.org/3/builtins/functions.html#int)* *= 60*

Standard RRF damping constant (Cormack et al., SIGIR 2009). Larger => flatter.

### foley.index.search.fuse_hits(vector_hits, keyword_hits, , k, rrf_k=60)

RRF-fuse a vector ranker’s hits with a keyword ranker’s hits.

* **Parameters:**
  * **vector_hits** ([`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]) – `[(id, cosine_similarity), ...]` best-first (from
    [`knn()`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.VectorIndex.knn)).
  * **keyword_hits** ([`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]) – `[(id, bm25_score), ...]` best-first (from
    [`bm25()`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.KeywordIndex.bm25)).
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Number of fused hits to return.
  * **rrf_k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The RRF damping constant.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`FusedHit`](_autosummary/foley.index.search.html.md#foley.index.search.FusedHit)]
* **Returns:**
  The top-`k` :class:

  ```
  `
  ```

  FusedHit\`s, each carrying its raw component scores.

### foley.index.search.hybrid_search(query, , embedder, vindex, kindex, k=10, candidate_k=50, rrf_k=60, where=None)

Embed `query`, run the vector + keyword rankers, and RRF-fuse them.

* **Parameters:**
  * **query** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The natural-language query.
  * **embedder** ([`Embedder`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.Embedder)) – Text<->audio embedder (its `embed_text` produces the query
    vector).
  * **vindex** ([`VectorIndex`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.VectorIndex)) – The vector index (CLAP KNN).
  * **kindex** ([`KeywordIndex`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.KeywordIndex)) – The keyword index (BM25).
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Number of fused results to return.
  * **candidate_k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Shortlist depth pulled from each ranker before fusion.
  * **rrf_k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The RRF damping constant.
  * **where** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Optional metadata push-down passed to both rankers.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`FusedHit`](_autosummary/foley.index.search.html.md#foley.index.search.FusedHit)]
* **Returns:**
  The top-`k` fused :class:

  ```
  `
  ```

  FusedHit\`s.

### foley.index.search.reciprocal_rank_fusion(ranked_id_lists, , k=60)

Fuse several ranked id lists into one, by reciprocal rank.

* **Parameters:**
  * **ranked_id_lists** ([`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]) – Each element is a list of ids in descending-relevance
    order (best first). Lists may overlap and may differ in length.
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – The RRF damping constant (default [`RRF_K`](_autosummary/foley.index.search.html.md#foley.index.search.RRF_K) = 60).
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]
* **Returns:**
  `[(id, fused_score), ...]` sorted by fused score descending, ties
  broken by `id` ascending (so the fusion is fully deterministic).

### foley.index.search.vector_search(qvec, , vindex, k=10, where=None)

Pure audio<->audio (or clip->library) vector search — no keyword leg.

Used by `SoundLibrary.similar` and by searching with a reference clip.
Hits keep their cosine similarity in `clap_score` and preserve the index’s
own descending-similarity order (`rrf_score` is left `None` — there is no
fusion).

* **Parameters:**
  * **qvec** (`ndarray`) – An already-L2-normalized `(dim,)` query vector.
  * **vindex** ([`VectorIndex`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.VectorIndex)) – The vector index.
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Number of neighbours to return.
  * **where** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Optional metadata push-down.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`FusedHit`](_autosummary/foley.index.search.html.md#foley.index.search.FusedHit)]
* **Returns:**
  Up to `k` :class:

  ```
  `
  ```

  FusedHit\`s in descending-similarity order.


# _autosummary/foley.index.taggers.html.md

# foley.index.taggers

Taggers — auto-fill a sound’s semantic labels on ingest (report 03).

Two default taggers behind the [`Tagger`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.Tagger) protocol:

> * [`ClapZeroShotTagger`](_autosummary/foley.index.taggers.html.md#foley.index.taggers.ClapZeroShotTagger) — foley’s default. Scores a clip against a
>   custom/UCS label set by CLAP cosine (report 03 Part 2), \*\*reusing the same
>   embedder the Index already loads\*\* — so ingest never loads CLAP twice and
>   it needs no dependency beyond `foley[clap]`.
> * [`PannsTagger`](_autosummary/foley.index.taggers.html.md#foley.index.taggers.PannsTagger) — PANNs CNN14 supervised tagging over the 527 AudioSet
>   classes (report 03 Part 1); `foley[tag]`. The checkpoint auto-downloads
>   (~327 MB) on first use.

Captioners (EnCLAP / Qwen2-Audio, `foley[caption]`) plug in behind the
[`Captioner`](_autosummary/foley.index.protocols.html.md#foley.index.protocols.Captioner) protocol as adapters; none ships as a
default yet (no clean, permissively-licensed, pip-installable checkpoint), so the
ingest caption stage is off unless a captioner is injected.

Heavy deps (`torch`/`transformers`/`panns_inference`) are lazy-imported, so
importing this module costs only the stdlib.

### Module Attributes

| [`ZEROSHOT_PROMPT`](_autosummary/foley.index.taggers.html.md#foley.index.taggers.ZEROSHOT_PROMPT)            | Prompt template for zero-shot CLAP tagging (report 03 Part 2).                  |
|-----------------------------------------------------------------------------|---------------------------------------------------------------------------------|
| [`PANNS_SAMPLE_RATE`](_autosummary/foley.index.taggers.html.md#foley.index.taggers.PANNS_SAMPLE_RATE)          | PANNs CNN14 expects 32 kHz mono audio.                                          |
| [`PANNS_DATA_DIR`](_autosummary/foley.index.taggers.html.md#foley.index.taggers.PANNS_DATA_DIR)             | Where panns-inference keeps (and, when missing, `wget`-downloads) its files.    |
| [`PANNS_CHECKPOINT_MIN_BYTES`](_autosummary/foley.index.taggers.html.md#foley.index.taggers.PANNS_CHECKPOINT_MIN_BYTES) | panns-inference treats a smaller checkpoint as incomplete and fetches it again. |

### Functions

| [`default_tagger`](_autosummary/foley.index.taggers.html.md#foley.index.taggers.default_tagger)()                    | The default supervised tagger (PANNs CNN14; `foley[tag]`).               |
|--------------------------------------------------------------------------------------|--------------------------------------------------------------------------|
| [`default_zeroshot_tagger`](_autosummary/foley.index.taggers.html.md#foley.index.taggers.default_zeroshot_tagger)([embedder]) | The default zero-shot tagger (CLAP vs UCS subcategories; `foley[clap]`). |

### Classes

| [`ClapZeroShotTagger`](_autosummary/foley.index.taggers.html.md#foley.index.taggers.ClapZeroShotTagger)(\*[, embedder, labels, ...])   | Zero-shot tagger: score a clip against a label set via CLAP cosine.         |
|----------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------|
| [`PannsTagger`](_autosummary/foley.index.taggers.html.md#foley.index.taggers.PannsTagger)(\*[, device, threshold])              | PANNs CNN14 supervised tagger over the 527 AudioSet classes (`foley[tag]`). |

### *class* foley.index.taggers.ClapZeroShotTagger(, embedder=None, labels=None, prompt='this is a sound of {label}', threshold=0.0)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Zero-shot tagger: score a clip against a label set via CLAP cosine.

Reuses a [`ClapEmbedder`](_autosummary/foley.index.embedders.html.md#foley.index.embedders.ClapEmbedder) (the same model the
Index uses), so the audio is embedded in the same joint space as the label
prompts and no extra weights load. The default label set is the UCS
subcategory names (foley’s own vocabulary), so tags land in-taxonomy.

#### *property* embedder

The CLAP embedder (injected or the process-wide default).

#### *property* labels *: [list](https://docs.python.org/3/builtins/stdtypes.html#list)[[str](https://docs.python.org/3/builtins/stdtypes.html#str)]*

natural UCS `category subcategory` phrases).

Natural multi-word phrases (`"weather rain"`, `"glass break"`) are far
better CLAP prompts — and better BM25 tags / taxonomy-resolver input — than
bare abstract subcategory words (`"Break"`, `"Buzz"`), which are a known
zero-shot artifact (anomalously close to everything). Absolute tag-quality
calibration (thresholds, label curation) is an eval-harness concern (#10).

* **Type:**
  The label vocabulary ([*default*](_autosummary/foley.html.md#foley.Affordance.default)

#### tag(wav, sr, , taxonomy='custom', top_k=10)

Return the top-`k` `(label, cosine)` tags for the clip, best first.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

#### tag_vector(audio_vec, , top_k=10)

Score a **precomputed** (L2-normalized) audio vector against the labels.

The efficiency seam (report 03 Part 2): the Index already embeds every
sound with this model, so on ingest the retrieval vector is reused here —
no second CLAP forward pass.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

### foley.index.taggers.PANNS_CHECKPOINT_MIN_BYTES *= 300000000*

panns-inference treats a smaller checkpoint as incomplete and fetches it again.

### foley.index.taggers.PANNS_DATA_DIR *= '~/panns_data'*

Where panns-inference keeps (and, when missing, `wget`-downloads) its files.

### foley.index.taggers.PANNS_SAMPLE_RATE *: [int](https://docs.python.org/3/builtins/functions.html#int)* *= 32000*

PANNs CNN14 expects 32 kHz mono audio.

### *class* foley.index.taggers.PannsTagger(, device='cpu', threshold=0.1)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

PANNs CNN14 supervised tagger over the 527 AudioSet classes (`foley[tag]`).

The checkpoint auto-downloads to `~/panns_data` (~327 MB) on the first
[`tag()`](_autosummary/foley.index.taggers.html.md#foley.index.taggers.PannsTagger.tag). PANNs expects 32 kHz mono; the clip is resampled via
[`foley.audio.to_working()`](_autosummary/foley.audio.html.md#foley.audio.to_working).

#### tag(wav, sr, , taxonomy='audioset', top_k=10)

Return the top-`k` `(AudioSet label, score)` tags, best first.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

### foley.index.taggers.ZEROSHOT_PROMPT *: [str](https://docs.python.org/3/builtins/stdtypes.html#str)* *= 'this is a sound of {label}'*

Prompt template for zero-shot CLAP tagging (report 03 Part 2).

### foley.index.taggers.default_tagger()

The default supervised tagger (PANNs CNN14; `foley[tag]`).

* **Return type:**
  [`PannsTagger`](_autosummary/foley.index.taggers.html.md#foley.index.taggers.PannsTagger)

### foley.index.taggers.default_zeroshot_tagger(embedder=None)

The default zero-shot tagger (CLAP vs UCS subcategories; `foley[clap]`).

Cached **per embedder** so the tagger is bound to the SAME embedder that
produced the audio vector it scores — otherwise (report seam) the cosine would
cross two unrelated embedding spaces. `embedder=None` uses the process-wide
default embedder.

* **Return type:**
  [`ClapZeroShotTagger`](_autosummary/foley.index.taggers.html.md#foley.index.taggers.ClapZeroShotTagger)


# _autosummary/foley.index.taxonomy.audioset.html.md

# foley.index.taxonomy.audioset

AudioSet-label -> UCS-CatID overlap map (report 04 §5.3, [11 §3.1]).

The machine-label layer (PANNs/PaSST emit AudioSet labels on ingest) is bridged
to the human-facing UCS browse tree by a small overlap map. This is the
conceptual seed of the EnvSound-UCS “Rosetta stone”; the full table drops in as
`taxonomy/data/audioset_ucs.json` and merges over the seed. Every target CatID
is validated against the UCS table at load time (fail-fast on a broken map).

### Functions

| [`default_audioset_ucs_map`](_autosummary/foley.index.taxonomy.audioset.html.md#foley.index.taxonomy.audioset.default_audioset_ucs_map)()                        | The process-wide default AudioSet->UCS map (seed + any JSON drop).   |
|----------------------------------------------------------------------------------------------------|----------------------------------------------------------------------|
| [`load_audioset_ucs_map`](_autosummary/foley.index.taxonomy.audioset.html.md#foley.index.taxonomy.audioset.load_audioset_ucs_map)(\*[, data_dir, table, ...]) | Build the AudioSet(name|MID) -> UCS-CatID map.                       |

### foley.index.taxonomy.audioset.default_audioset_ucs_map()

The process-wide default AudioSet->UCS map (seed + any JSON drop).

* **Return type:**
  [`AudioSetUcsMap`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.AudioSetUcsMap)

### foley.index.taxonomy.audioset.load_audioset_ucs_map(, data_dir=None, table=None, include_seed=True)

Build the AudioSet(name|MID) -> UCS-CatID map.

* **Parameters:**
  * **data_dir** – Directory to look for `audioset_ucs.json` in (defaults to the
    package’s `taxonomy/data/`); merged over the seed when present.
  * **table** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`UcsTable`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.UcsTable)]) – The UCS table every target CatID must exist in (defaults to
    [`default_ucs_table()`](_autosummary/foley.index.taxonomy.ucs.html.md#foley.index.taxonomy.ucs.default_ucs_table)).
  * **include_seed** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Start from the in-code seed map (default `True`).
* **Return type:**
  [`AudioSetUcsMap`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.AudioSetUcsMap)
* **Returns:**
  A ready [`AudioSetUcsMap`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.AudioSetUcsMap).
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If any entry targets a CatID absent from `table`.


# _autosummary/foley.index.taxonomy.html.md

# foley.index.taxonomy

UCS + AudioSet taxonomy: the `tags -> UCS CatID` resolver for foley.

Two complementary controlled vocabularies (report 04 §5): **UCS** (the industry
browse tree and normalization target) and the **AudioSet ontology** (the machine
label layer taggers emit). This package resolves a sound’s free tags, caption,
AudioSet labels, and/or UCS-style filename onto a UCS CatID, filling
`ucs_category`/`ucs_subcategory` on ingest and
`ucs_catid` on the query side.

Progressive disclosure — the one-call path is:

```default
from foley.index.taxonomy import resolve_catid
resolve_catid(caption="a heavy wooden door creaks open")   # -> CatIdResolution
```

Everything else (the UCS table, the AudioSet map, a custom resolver) is optional
keyword injection. The seed tables live in code; the full UCS master and the
EnvSound-UCS mapping drop in later as JSON under `data/` with no logic change.

### Functions

| [`load_ucs_table`](_autosummary/foley.index.taxonomy.html.md#foley.index.taxonomy.load_ucs_table)(\*[, data_dir, include_seed])      | Build the UCS lookup: the seed rows, overridden/extended by a JSON drop.   |
|----------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------|
| [`default_ucs_table`](_autosummary/foley.index.taxonomy.html.md#foley.index.taxonomy.default_ucs_table)()                               | The process-wide default UCS table (seed + any JSON drop), built once.     |
| [`parse_ucs_filename`](_autosummary/foley.index.taxonomy.html.md#foley.index.taxonomy.parse_ucs_filename)(filename, \*[, table])         | Parse a UCS-conformant filename to `(ucs_category, ucs_subcategory)`.      |
| [`parse_catid_token`](_autosummary/foley.index.taxonomy.html.md#foley.index.taxonomy.parse_catid_token)(filename)                       | Return token 0 (the CatID candidate) of a UCS-style filename, else `None`. |
| [`load_audioset_ucs_map`](_autosummary/foley.index.taxonomy.html.md#foley.index.taxonomy.load_audioset_ucs_map)(\*[, data_dir, table, ...]) | Build the AudioSet(name|MID) -> UCS-CatID map.                             |
| [`default_audioset_ucs_map`](_autosummary/foley.index.taxonomy.html.md#foley.index.taxonomy.default_audioset_ucs_map)()                        | The process-wide default AudioSet->UCS map (seed + any JSON drop).         |
| [`resolve_catid`](_autosummary/foley.index.taxonomy.html.md#foley.index.taxonomy.resolve_catid)(\*[, tags, caption, ...])           | Resolve inputs to a best UCS CatID by the staged precedence.               |

### Classes

| [`UcsRow`](_autosummary/foley.index.taxonomy.html.md#foley.index.taxonomy.UcsRow)(catid, category, subcategory[, ...])   | One Universal Category System entry.                                                                          |
|------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------|
| [`UcsTable`](_autosummary/foley.index.taxonomy.html.md#foley.index.taxonomy.UcsTable)([by_catid, order, \_ci_index])       | A loaded UCS lookup: by CatID (exact + case-insensitive) and by synonym.                                      |
| [`AudioSetUcsMap`](_autosummary/foley.index.taxonomy.html.md#foley.index.taxonomy.AudioSetUcsMap)([by_name, by_mid])             | AudioSet-label -> UCS-CatID overlap map (report 04 §5.3).                                                     |
| [`CatIdResolution`](_autosummary/foley.index.taxonomy.html.md#foley.index.taxonomy.CatIdResolution)([catid, category, ...])       | The result of resolving free tags/caption/labels to a UCS CatID.                                              |
| [`TaxonomyResolver`](_autosummary/foley.index.taxonomy.html.md#foley.index.taxonomy.TaxonomyResolver)(\*args, \*\*kwargs)          | Resolve a [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord) to a UCS CatID. |
| [`KeywordResolver`](_autosummary/foley.index.taxonomy.html.md#foley.index.taxonomy.KeywordResolver)(\*[, table, audioset_map])    | The default stdlib resolver — staged keyword/synonym/AudioSet resolution.                                     |

### *class* foley.index.taxonomy.AudioSetUcsMap(by_name=<factory>, by_mid=<factory>)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

AudioSet-label -> UCS-CatID overlap map (report 04 §5.3).

Keyed primarily by lowercased label **name** (robust); `by_mid` carries the
best-effort `/m/...` machine ids as a secondary key. Every target CatID is
validated against the UCS table at load time (fail-fast on a broken map).

#### resolve(label)

Map one AudioSet label (a MID or a name) to a UCS CatID (or `None`).

* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]

### *class* foley.index.taxonomy.CatIdResolution(catid=None, category=None, subcategory=None, source=None, confidence=0.0, matched_terms=<factory>)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

The result of resolving free tags/caption/labels to a UCS CatID.

`catid` feeds `ucs_category` and
`subcategory` feeds `ucs_subcategory` on
ingest, and `ucs_catid` on the query side.

### *class* foley.index.taxonomy.KeywordResolver(, table=None, audioset_map=None)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

The default stdlib resolver — staged keyword/synonym/AudioSet resolution.

Zero heavy dependencies. Bind a different [`TaxonomyResolver`](_autosummary/foley.index.taxonomy.html.md#foley.index.taxonomy.TaxonomyResolver) (e.g. a
future CLAP zero-shot resolver) by keyword injection to upgrade quality.

#### resolve(record)

Resolve a [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord)’s tags/caption/labels/uri.

* **Return type:**
  [`CatIdResolution`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.CatIdResolution)

### *class* foley.index.taxonomy.TaxonomyResolver(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

Resolve a [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord) to a UCS CatID.

#### resolve(record)

Return the CatID resolution for `record`.

* **Return type:**
  [`CatIdResolution`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.CatIdResolution)

### *class* foley.index.taxonomy.UcsRow(catid, category, subcategory, synonyms=(), confident=False)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

One Universal Category System entry.

A `catid` (e.g. `'DOORWood'`) is an OPAQUE key — the category prefix is
variable-length and not reliably splittable — so `catid -> (category,
subcategory)` is always a table lookup, never a string split (report
04 §5.2). `confident=False` marks an APPROXIMATE CatID reconstructed for the
seed table; verify it against the UCS master before treating it as
authoritative.

### *class* foley.index.taxonomy.UcsTable(by_catid=<factory>, order=<factory>, \_ci_index=<factory>)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

A loaded UCS lookup: by CatID (exact + case-insensitive) and by synonym.

#### by_catid

`CatID -> UcsRow` (case-sensitive primary key).

#### order

The CatIDs in stable insertion order (deterministic tie-breaking).

#### get(catid)

Look up a row by CatID: exact first, then case-insensitive.

* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`UcsRow`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.UcsRow)]

### foley.index.taxonomy.default_audioset_ucs_map()

The process-wide default AudioSet->UCS map (seed + any JSON drop).

* **Return type:**
  [`AudioSetUcsMap`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.AudioSetUcsMap)

### foley.index.taxonomy.default_ucs_table()

The process-wide default UCS table (seed + any JSON drop), built once.

* **Return type:**
  [`UcsTable`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.UcsTable)

### foley.index.taxonomy.load_audioset_ucs_map(, data_dir=None, table=None, include_seed=True)

Build the AudioSet(name|MID) -> UCS-CatID map.

* **Parameters:**
  * **data_dir** – Directory to look for `audioset_ucs.json` in (defaults to the
    package’s `taxonomy/data/`); merged over the seed when present.
  * **table** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`UcsTable`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.UcsTable)]) – The UCS table every target CatID must exist in (defaults to
    [`default_ucs_table()`](_autosummary/foley.index.taxonomy.ucs.html.md#foley.index.taxonomy.ucs.default_ucs_table)).
  * **include_seed** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Start from the in-code seed map (default `True`).
* **Return type:**
  [`AudioSetUcsMap`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.AudioSetUcsMap)
* **Returns:**
  A ready [`AudioSetUcsMap`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.AudioSetUcsMap).
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If any entry targets a CatID absent from `table`.

### foley.index.taxonomy.load_ucs_table(, data_dir=None, include_seed=True)

Build the UCS lookup: the seed rows, overridden/extended by a JSON drop.

* **Parameters:**
  * **data_dir** – Directory to look for `ucs_full.json` in (defaults to the
    package’s `taxonomy/data/`). When present, its rows override the
    seed on CatID collision and add the rest of the ~750-row master.
  * **include_seed** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Start from the in-code seed table (default `True`).
* **Return type:**
  [`UcsTable`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.UcsTable)
* **Returns:**
  A ready [`UcsTable`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.UcsTable).

### foley.index.taxonomy.parse_catid_token(filename)

Return token 0 (the CatID candidate) of a UCS-style filename, else `None`.

Strips directory and extension; requires at least one `_` (the field
delimiter). Does not validate the token against the table.

* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]

### foley.index.taxonomy.parse_ucs_filename(filename, , table=None)

Parse a UCS-conformant filename to `(ucs_category, ucs_subcategory)`.

Fail-quiet: returns `(None, None)` when the name is not UCS-conformant or
its CatID token is unknown (so a wrong subcategory is never emitted).

* **Parameters:**
  * **filename** – A path or filename (only the basename’s token 0 is used).
  * **table** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`UcsTable`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.UcsTable)]) – The UCS table to resolve against (defaults to
    [`default_ucs_table()`](_autosummary/foley.index.taxonomy.html.md#foley.index.taxonomy.default_ucs_table)).
* **Return type:**
  [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)], [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]

### foley.index.taxonomy.resolve_catid(, tags=(), caption=None, audioset_labels=(), filename=None, table=None, audioset_map=None)

Resolve inputs to a best UCS CatID by the staged precedence.

* **Parameters:**
  * **tags** ([`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Free tags on the sound.
  * **caption** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Free-text caption/description.
  * **audioset_labels** ([`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – AudioSet MIDs or names (e.g. from PANNs).
  * **filename** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional UCS-style filename/path (its token-0 CatID wins if
    recognized).
  * **table** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`UcsTable`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.UcsTable)]) – UCS table (defaults to [`default_ucs_table()`](_autosummary/foley.index.taxonomy.html.md#foley.index.taxonomy.default_ucs_table)).
  * **audioset_map** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`AudioSetUcsMap`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.AudioSetUcsMap)]) – AudioSet->UCS map (defaults to
    [`default_audioset_ucs_map()`](_autosummary/foley.index.taxonomy.audioset.html.md#foley.index.taxonomy.audioset.default_audioset_ucs_map)).
* **Return type:**
  [`CatIdResolution`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.CatIdResolution)
* **Returns:**
  A [`CatIdResolution`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.CatIdResolution) (falsy when
  nothing resolved).

### Modules

| [`audioset`](_autosummary/foley.index.taxonomy.audioset.html.md#module-foley.index.taxonomy.audioset)   | AudioSet-label -> UCS-CatID overlap map (report 04 §5.3, [11 §3.1]).             |
|--------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------|
| [`model`](_autosummary/foley.index.taxonomy.model.html.md#module-foley.index.taxonomy.model)         | Data structures for the UCS/AudioSet taxonomy resolver (stdlib-only).            |
| [`resolver`](_autosummary/foley.index.taxonomy.resolver.html.md#module-foley.index.taxonomy.resolver)   | Resolve free tags/caption/labels to a UCS CatID (the EnvSound-UCS pipeline).     |
| [`seed`](_autosummary/foley.index.taxonomy.seed.html.md#module-foley.index.taxonomy.seed)           | Seed taxonomy data — a representative in-code UCS subset + AudioSet overlap map. |
| [`ucs`](_autosummary/foley.index.taxonomy.ucs.html.md#module-foley.index.taxonomy.ucs)             | UCS table loading + the UCS-filename CatID parse (report 04 §5.2, §6.4).         |


# _autosummary/foley.index.taxonomy.model.html.md

# foley.index.taxonomy.model

Data structures for the UCS/AudioSet taxonomy resolver (stdlib-only).

These are plain, immutable-ish records — the resolver logic lives in
[`ucs`](_autosummary/foley.index.taxonomy.ucs.html.md#module-foley.index.taxonomy.ucs), [`audioset`](_autosummary/foley.index.taxonomy.audioset.html.md#module-foley.index.taxonomy.audioset), and
[`resolver`](_autosummary/foley.index.taxonomy.resolver.html.md#module-foley.index.taxonomy.resolver). Nothing here imports numpy/torch; the
whole taxonomy layer is a pure lookup over dicts (report 04 §5.3 — “taxonomies do
faceting and browse”, the CLAP vector does the heavy retrieval).

### Classes

| [`AudioSetUcsMap`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.AudioSetUcsMap)([by_name, by_mid])           | AudioSet-label -> UCS-CatID overlap map (report 04 §5.3).                |
|----------------------------------------------------------------------------------------------|--------------------------------------------------------------------------|
| [`CatIdResolution`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.CatIdResolution)([catid, category, ...])     | The result of resolving free tags/caption/labels to a UCS CatID.         |
| [`UcsRow`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.UcsRow)(catid, category, subcategory[, ...]) | One Universal Category System entry.                                     |
| [`UcsTable`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.UcsTable)([by_catid, order, \_ci_index])     | A loaded UCS lookup: by CatID (exact + case-insensitive) and by synonym. |

### *class* foley.index.taxonomy.model.AudioSetUcsMap(by_name=<factory>, by_mid=<factory>)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

AudioSet-label -> UCS-CatID overlap map (report 04 §5.3).

Keyed primarily by lowercased label **name** (robust); `by_mid` carries the
best-effort `/m/...` machine ids as a secondary key. Every target CatID is
validated against the UCS table at load time (fail-fast on a broken map).

#### resolve(label)

Map one AudioSet label (a MID or a name) to a UCS CatID (or `None`).

* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]

### *class* foley.index.taxonomy.model.CatIdResolution(catid=None, category=None, subcategory=None, source=None, confidence=0.0, matched_terms=<factory>)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

The result of resolving free tags/caption/labels to a UCS CatID.

`catid` feeds `ucs_category` and
`subcategory` feeds `ucs_subcategory` on
ingest, and `ucs_catid` on the query side.

### *class* foley.index.taxonomy.model.UcsRow(catid, category, subcategory, synonyms=(), confident=False)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

One Universal Category System entry.

A `catid` (e.g. `'DOORWood'`) is an OPAQUE key — the category prefix is
variable-length and not reliably splittable — so `catid -> (category,
subcategory)` is always a table lookup, never a string split (report
04 §5.2). `confident=False` marks an APPROXIMATE CatID reconstructed for the
seed table; verify it against the UCS master before treating it as
authoritative.

### *class* foley.index.taxonomy.model.UcsTable(by_catid=<factory>, order=<factory>, \_ci_index=<factory>)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

A loaded UCS lookup: by CatID (exact + case-insensitive) and by synonym.

#### by_catid

`CatID -> UcsRow` (case-sensitive primary key).

#### order

The CatIDs in stable insertion order (deterministic tie-breaking).

#### get(catid)

Look up a row by CatID: exact first, then case-insensitive.

* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`UcsRow`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.UcsRow)]


# _autosummary/foley.index.taxonomy.resolver.html.md

# foley.index.taxonomy.resolver

Resolve free tags/caption/labels to a UCS CatID (the EnvSound-UCS pipeline).

Staged precedence, highest wins (report 04 §5.3, [11 §3.1] — normalize
heterogeneous labels onto UCS via mapping/synonym tables):

> filename-parse  >  keyword/synonym  >  audioset-map  >  none

Matching is word-boundary aware (so `"rain"` matches “heavy rain” but not
“brainstorm”), a subcategory hit outranks a category-name hit, and multi-word
phrases outrank single tokens — with a deterministic table-order final tiebreak.
The always-available default is this stdlib [`KeywordResolver`](_autosummary/foley.index.taxonomy.resolver.html.md#foley.index.taxonomy.resolver.KeywordResolver); a richer
CLAP zero-shot resolver (embed once, argmax over UCS label prompts, report
03 Part 2) can be dropped in behind the same [`TaxonomyResolver`](_autosummary/foley.index.taxonomy.resolver.html.md#foley.index.taxonomy.resolver.TaxonomyResolver) protocol.

### Functions

| [`resolve_catid`](_autosummary/foley.index.taxonomy.resolver.html.md#foley.index.taxonomy.resolver.resolve_catid)(\*[, tags, caption, ...])   | Resolve inputs to a best UCS CatID by the staged precedence.   |
|--------------------------------------------------------------------------------------------|----------------------------------------------------------------|

### Classes

| [`KeywordResolver`](_autosummary/foley.index.taxonomy.resolver.html.md#foley.index.taxonomy.resolver.KeywordResolver)(\*[, table, audioset_map])   | The default stdlib resolver — staged keyword/synonym/AudioSet resolution.                                     |
|-----------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------|
| [`TaxonomyResolver`](_autosummary/foley.index.taxonomy.resolver.html.md#foley.index.taxonomy.resolver.TaxonomyResolver)(\*args, \*\*kwargs)         | Resolve a [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord) to a UCS CatID. |

### *class* foley.index.taxonomy.resolver.KeywordResolver(, table=None, audioset_map=None)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

The default stdlib resolver — staged keyword/synonym/AudioSet resolution.

Zero heavy dependencies. Bind a different [`TaxonomyResolver`](_autosummary/foley.index.taxonomy.resolver.html.md#foley.index.taxonomy.resolver.TaxonomyResolver) (e.g. a
future CLAP zero-shot resolver) by keyword injection to upgrade quality.

#### resolve(record)

Resolve a [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord)’s tags/caption/labels/uri.

* **Return type:**
  [`CatIdResolution`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.CatIdResolution)

### *class* foley.index.taxonomy.resolver.TaxonomyResolver(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

Resolve a [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord) to a UCS CatID.

#### resolve(record)

Return the CatID resolution for `record`.

* **Return type:**
  [`CatIdResolution`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.CatIdResolution)

### foley.index.taxonomy.resolver.resolve_catid(, tags=(), caption=None, audioset_labels=(), filename=None, table=None, audioset_map=None)

Resolve inputs to a best UCS CatID by the staged precedence.

* **Parameters:**
  * **tags** ([`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Free tags on the sound.
  * **caption** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Free-text caption/description.
  * **audioset_labels** ([`Sequence`](https://docs.python.org/3/library/typing.html#typing.Sequence)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – AudioSet MIDs or names (e.g. from PANNs).
  * **filename** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional UCS-style filename/path (its token-0 CatID wins if
    recognized).
  * **table** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`UcsTable`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.UcsTable)]) – UCS table (defaults to `default_ucs_table()`).
  * **audioset_map** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`AudioSetUcsMap`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.AudioSetUcsMap)]) – AudioSet->UCS map (defaults to
    [`default_audioset_ucs_map()`](_autosummary/foley.index.taxonomy.audioset.html.md#foley.index.taxonomy.audioset.default_audioset_ucs_map)).
* **Return type:**
  [`CatIdResolution`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.CatIdResolution)
* **Returns:**
  A [`CatIdResolution`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.CatIdResolution) (falsy when
  nothing resolved).


# _autosummary/foley.index.taxonomy.seed.html.md

# foley.index.taxonomy.seed

Seed taxonomy data — a representative in-code UCS subset + AudioSet overlap map.

This is the OPEN/CLOSED seam (report 04 §5.3, [11 §3.1]): a small hand-curated
seed lives here in code; the FULL UCS master (82 categories / ~750 subcategories)
and the EnvSound-UCS AudioSet->UCS / FSD50K->UCS tables drop in later as JSON
under `taxonomy/data/` and are merged OVER this seed with zero logic change.

Confidence: only `WEATHRain` and `DOORWood` are treated as authoritative CatIDs;
every other row carries `confident=False` (\*\*APPROXIMATE — verify against the UCS
master before shipping as authoritative\*\*). The seed is deliberately partial; it is
NOT the 750-row master.

### Module Attributes

| [`SEED_UCS_TABLE`](_autosummary/foley.index.taxonomy.seed.html.md#foley.index.taxonomy.seed.SEED_UCS_TABLE)        | catid, category, subcategory, synonyms (lowercased), confident.   |
|------------------------------------------------------------------------|-------------------------------------------------------------------|
| [`SEED_AUDIOSET_UCS_MAP`](_autosummary/foley.index.taxonomy.seed.html.md#foley.index.taxonomy.seed.SEED_AUDIOSET_UCS_MAP) | AudioSet label NAME (lowercased) -> UCS CatID.                    |

### foley.index.taxonomy.seed.SEED_AUDIOSET_UCS_MAP *: [list](https://docs.python.org/3/builtins/stdtypes.html#list)[[dict](https://docs.python.org/3/builtins/stdtypes.html#dict)]* *= [{'catid': 'DOORWood', 'mid': '/m/02dgv', 'name': 'door'}, {'catid': 'DOORMetal', 'mid': '/m/02y_763', 'name': 'sliding door'}, {'catid': 'WEATHRain', 'mid': '/m/06mb1', 'name': 'rain'}, {'catid': 'WEATHRain', 'mid': '/m/07r10fb', 'name': 'raindrop'}, {'catid': 'WEATHThunder', 'mid': '/m/0ngt1', 'name': 'thunder'}, {'catid': 'WEATHThunder', 'mid': '/m/0jb2l', 'name': 'thunderstorm'}, {'catid': 'WEATHWind', 'mid': '/m/03m9d0z', 'name': 'wind'}, {'catid': 'FOOTConc', 'mid': '/m/07qann', 'name': 'walk, footsteps'}, {'catid': 'FIREGeneral', 'mid': '/m/0jbk', 'name': 'fire'}, {'catid': 'FIREGeneral', 'mid': '/m/07pc8k4', 'name': 'crackle'}, {'catid': 'WATRRun', 'mid': '/m/0838f', 'name': 'water'}, {'catid': 'WATRSplash', 'mid': '/m/07qlwh6', 'name': 'splash, splatter'}, {'catid': 'LIQDPour', 'mid': '/m/07q0yl5', 'name': 'pour'}, {'catid': 'GLASBreak', 'mid': '/m/039jq', 'name': 'glass'}, {'catid': 'GLASBreak', 'mid': '/m/07rjwbb', 'name': 'shatter'}, {'catid': 'EXPLGeneral', 'mid': '/m/014zdl', 'name': 'explosion'}, {'catid': 'GUNHandgun', 'mid': '/m/032s66', 'name': 'gunshot, gunfire'}, {'catid': 'VEHCarExt', 'mid': '/m/07yv9', 'name': 'vehicle'}, {'catid': 'VEHCarExt', 'mid': '/m/0k4j', 'name': 'car'}, {'catid': 'VEHHorn', 'mid': '/m/0912c9', 'name': 'vehicle horn, honk'}, {'catid': 'ANMLDog', 'mid': '/m/0bt9lr', 'name': 'dog'}, {'catid': 'ANMLDog', 'mid': '/m/05tny_', 'name': 'bark'}, {'catid': 'ANMLCat', 'mid': '/m/01yrx', 'name': 'cat'}, {'catid': 'ANMLBird', 'mid': '/m/015p6', 'name': 'bird'}, {'catid': 'COMMPhone', 'mid': '/m/07cx4', 'name': 'telephone'}]*

AudioSet label NAME (lowercased) -> UCS CatID. `mid` is best-effort (verify
against `audioset/ontology.json` before trusting). Every `catid` here MUST
exist in [`SEED_UCS_TABLE`](_autosummary/foley.index.taxonomy.seed.html.md#foley.index.taxonomy.seed.SEED_UCS_TABLE) — [`load_audioset_ucs_map()`](_autosummary/foley.index.taxonomy.audioset.html.md#foley.index.taxonomy.audioset.load_audioset_ucs_map)
asserts this at load time.

### foley.index.taxonomy.seed.SEED_UCS_TABLE *: [list](https://docs.python.org/3/builtins/stdtypes.html#list)[[dict](https://docs.python.org/3/builtins/stdtypes.html#dict)]* *= [{'category': 'DOORS', 'catid': 'DOORWood', 'confident': True, 'subcategory': 'Wood', 'synonyms': ['wooden door', 'oak door', 'door creak', 'door open', 'door close', 'door slam']}, {'category': 'DOORS', 'catid': 'DOORMetal', 'confident': False, 'subcategory': 'Metal', 'synonyms': ['metal door', 'steel door', 'hatch', 'cell door']}, {'category': 'DOORS', 'catid': 'DOORKnob', 'confident': False, 'subcategory': 'Knob', 'synonyms': ['doorknob', 'door handle', 'latch', 'handle turn']}, {'category': 'WEATHER', 'catid': 'WEATHRain', 'confident': True, 'subcategory': 'Rain', 'synonyms': ['rain', 'rainfall', 'raining', 'rain on window', 'downpour', 'drizzle']}, {'category': 'WEATHER', 'catid': 'WEATHWind', 'confident': False, 'subcategory': 'Wind', 'synonyms': ['wind', 'gust', 'breeze', 'howling wind', 'windy']}, {'category': 'WEATHER', 'catid': 'WEATHThunder', 'confident': False, 'subcategory': 'Thunder', 'synonyms': ['thunder', 'thunderclap', 'thunderstorm', 'thunder crack', 'rumble']}, {'category': 'FOOTSTEPS', 'catid': 'FOOTWood', 'confident': False, 'subcategory': 'Wood', 'synonyms': ['footsteps wood', 'walking wood floor', 'footfall wood']}, {'category': 'FOOTSTEPS', 'catid': 'FOOTConc', 'confident': False, 'subcategory': 'Concrete', 'synonyms': ['footsteps concrete', 'walking pavement', 'footsteps sidewalk']}, {'category': 'FOOTSTEPS', 'catid': 'FOOTGravel', 'confident': False, 'subcategory': 'Gravel', 'synonyms': ['footsteps gravel', 'walking gravel', 'crunch gravel']}, {'category': 'IMPACTS', 'catid': 'IMPTGeneral', 'confident': False, 'subcategory': 'General', 'synonyms': ['impact', 'hit', 'thud', 'bang', 'smash', 'crash']}, {'category': 'DESTRUCTION', 'catid': 'DESTGeneral', 'confident': False, 'subcategory': 'General', 'synonyms': ['destruction', 'collapse', 'debris', 'rubble', 'demolition']}, {'category': 'GLASS', 'catid': 'GLASBreak', 'confident': False, 'subcategory': 'Break', 'synonyms': ['glass break', 'glass shatter', 'shattering glass', 'broken glass']}, {'category': 'WATER', 'catid': 'WATRRun', 'confident': False, 'subcategory': 'Running', 'synonyms': ['running water', 'stream', 'faucet', 'tap water', 'flowing water']}, {'category': 'WATER', 'catid': 'WATRSplash', 'confident': False, 'subcategory': 'Splash', 'synonyms': ['splash', 'splashing', 'water splash', 'plop']}, {'category': 'LIQUID', 'catid': 'LIQDPour', 'confident': False, 'subcategory': 'Pour', 'synonyms': ['pour', 'pouring', 'glug', 'liquid pour']}, {'category': 'FIRE', 'catid': 'FIREGeneral', 'confident': False, 'subcategory': 'General', 'synonyms': ['fire', 'flames', 'campfire', 'crackling fire', 'burning']}, {'category': 'FIRE', 'catid': 'FIREWhoosh', 'confident': False, 'subcategory': 'Whoosh', 'synonyms': ['fire whoosh', 'flame burst', 'fireball', 'ignite whoosh']}, {'category': 'VEHICLES', 'catid': 'VEHCarInt', 'confident': False, 'subcategory': 'Car Interior', 'synonyms': ['car interior', 'in car', 'car cabin']}, {'category': 'VEHICLES', 'catid': 'VEHCarExt', 'confident': False, 'subcategory': 'Car Exterior', 'synonyms': ['car pass by', 'car exterior', 'car drive by', 'engine car']}, {'category': 'VEHICLES', 'catid': 'VEHHorn', 'confident': False, 'subcategory': 'Horn', 'synonyms': ['car horn', 'honk', 'vehicle horn', 'beep horn']}, {'category': 'AMBIENCE', 'catid': 'AMBUrban', 'confident': False, 'subcategory': 'Urban', 'synonyms': ['city ambience', 'street ambience', 'urban background', 'traffic ambience']}, {'category': 'AMBIENCE', 'catid': 'AMBNature', 'confident': False, 'subcategory': 'Nature', 'synonyms': ['nature ambience', 'forest ambience', 'outdoor ambience', 'birdsong bed']}, {'category': 'AMBIENCE', 'catid': 'AMBRoom', 'confident': False, 'subcategory': 'Room Tone', 'synonyms': ['room tone', 'roomtone', 'interior ambience', 'quiet room']}, {'category': 'USER INTERFACE', 'catid': 'GUIBeep', 'confident': False, 'subcategory': 'Beep', 'synonyms': ['ui beep', 'button', 'menu click', 'notification', 'ui blip']}, {'category': 'USER INTERFACE', 'catid': 'GUIWhoosh', 'confident': False, 'subcategory': 'Whoosh', 'synonyms': ['ui whoosh', 'swipe', 'ui swipe', 'transition whoosh']}, {'category': 'ANIMALS', 'catid': 'ANMLDog', 'confident': False, 'subcategory': 'Dog', 'synonyms': ['dog', 'bark', 'dog bark', 'growl', 'whine dog']}, {'category': 'ANIMALS', 'catid': 'ANMLCat', 'confident': False, 'subcategory': 'Cat', 'synonyms': ['cat', 'meow', 'purr', 'cat meow']}, {'category': 'ANIMALS', 'catid': 'ANMLBird', 'confident': False, 'subcategory': 'Bird', 'synonyms': ['bird', 'birdsong', 'chirp', 'tweet', 'bird call']}, {'category': 'GUNS', 'catid': 'GUNHandgun', 'confident': False, 'subcategory': 'Handgun', 'synonyms': ['gunshot', 'pistol', 'handgun', 'gun fire', 'shot']}, {'category': 'EXPLOSIONS', 'catid': 'EXPLGeneral', 'confident': False, 'subcategory': 'General', 'synonyms': ['explosion', 'blast', 'boom', 'detonation', 'explode']}, {'category': 'CLOTH', 'catid': 'CLOTHMove', 'confident': False, 'subcategory': 'Movement', 'synonyms': ['cloth', 'cloth movement', 'fabric', 'clothing rustle', 'cloth rustle']}, {'category': 'PAPER', 'catid': 'PAPRHandle', 'confident': False, 'subcategory': 'Handling', 'synonyms': ['paper', 'paper rustle', 'page turn', 'paper crumple']}, {'category': 'MACHINES', 'catid': 'MACHGeneral', 'confident': False, 'subcategory': 'General', 'synonyms': ['machine', 'motor', 'mechanism', 'machinery', 'servo']}, {'category': 'ELECTRICITY', 'catid': 'ELECBuzz', 'confident': False, 'subcategory': 'Buzz', 'synonyms': ['electric buzz', 'spark', 'zap', 'electricity', 'arc']}, {'category': 'COMMUNICATIONS', 'catid': 'COMMPhone', 'confident': False, 'subcategory': 'Phone', 'synonyms': ['phone ring', 'telephone', 'ringtone', 'cell phone', 'dial tone']}]*

catid, category, subcategory, synonyms
(lowercased), confident. `confident=False` => APPROXIMATE CatID.

* **Type:**
  Representative UCS seed. Each row


# _autosummary/foley.index.taxonomy.ucs.html.md

# foley.index.taxonomy.ucs

UCS table loading + the UCS-filename CatID parse (report 04 §5.2, §6.4).

A UCS filename is underscore-delimited with the **CatID first**:

```default
CatID_FXName_CreatorID_SourceID[_UserCategory_UserData].ext
```

Only token 0 (the CatID) carries the controlled taxonomy; it is looked up in the
table (never string-split, since the category prefix is variable-length). The
loader merges the in-code seed with an optional full-table JSON drop under
`taxonomy/data/ucs_full.json` (JSON wins on CatID collision) — open/closed.

### Functions

| [`default_ucs_table`](_autosummary/foley.index.taxonomy.ucs.html.md#foley.index.taxonomy.ucs.default_ucs_table)()                          | The process-wide default UCS table (seed + any JSON drop), built once.     |
|-----------------------------------------------------------------------------------------------|----------------------------------------------------------------------------|
| [`load_ucs_table`](_autosummary/foley.index.taxonomy.ucs.html.md#foley.index.taxonomy.ucs.load_ucs_table)(\*[, data_dir, include_seed]) | Build the UCS lookup: the seed rows, overridden/extended by a JSON drop.   |
| [`parse_catid_token`](_autosummary/foley.index.taxonomy.ucs.html.md#foley.index.taxonomy.ucs.parse_catid_token)(filename)                  | Return token 0 (the CatID candidate) of a UCS-style filename, else `None`. |
| [`parse_ucs_filename`](_autosummary/foley.index.taxonomy.ucs.html.md#foley.index.taxonomy.ucs.parse_ucs_filename)(filename, \*[, table])    | Parse a UCS-conformant filename to `(ucs_category, ucs_subcategory)`.      |

### foley.index.taxonomy.ucs.default_ucs_table()

The process-wide default UCS table (seed + any JSON drop), built once.

* **Return type:**
  [`UcsTable`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.UcsTable)

### foley.index.taxonomy.ucs.load_ucs_table(, data_dir=None, include_seed=True)

Build the UCS lookup: the seed rows, overridden/extended by a JSON drop.

* **Parameters:**
  * **data_dir** – Directory to look for `ucs_full.json` in (defaults to the
    package’s `taxonomy/data/`). When present, its rows override the
    seed on CatID collision and add the rest of the ~750-row master.
  * **include_seed** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Start from the in-code seed table (default `True`).
* **Return type:**
  [`UcsTable`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.UcsTable)
* **Returns:**
  A ready [`UcsTable`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.UcsTable).

### foley.index.taxonomy.ucs.parse_catid_token(filename)

Return token 0 (the CatID candidate) of a UCS-style filename, else `None`.

Strips directory and extension; requires at least one `_` (the field
delimiter). Does not validate the token against the table.

* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]

### foley.index.taxonomy.ucs.parse_ucs_filename(filename, , table=None)

Parse a UCS-conformant filename to `(ucs_category, ucs_subcategory)`.

Fail-quiet: returns `(None, None)` when the name is not UCS-conformant or
its CatID token is unknown (so a wrong subcategory is never emitted).

* **Parameters:**
  * **filename** – A path or filename (only the basename’s token 0 is used).
  * **table** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`UcsTable`](_autosummary/foley.index.taxonomy.model.html.md#foley.index.taxonomy.model.UcsTable)]) – The UCS table to resolve against (defaults to
    [`default_ucs_table()`](_autosummary/foley.index.taxonomy.ucs.html.md#foley.index.taxonomy.ucs.default_ucs_table)).
* **Return type:**
  [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)], [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]


# _autosummary/foley.licensing.html.md

# foley.licensing

License policy for foley: the license_id -> flag-set SSOT, flag derivation
(with per-source overrides), and the fail-closed candidate `keep()` gate.

Stdlib-only (imports only `foley.base`). The dependency direction is one-way:
`licensing -> base`.

The two operational vs copyright flags are DISTINCT and must not be conflated:

> * `redistribute_standalone_ok` is a COPYRIGHT question (may the raw file be
>   re-exposed standalone?).
> * `cache_bytes_ok` is a TOS/OPERATIONAL question (may foley persist the
>   bytes at all?).

E.g. Freesound CC0 is legally redistributable yet its API TOS forbids caching,
so the Freesound adapter passes `overrides={'cache_bytes_ok': False}` — the
item is redistributable but stored by-reference.

### Module Attributes

| [`DEFAULT_INTENDED_USE`](_autosummary/foley.licensing.html.md#foley.licensing.DEFAULT_INTENDED_USE)     | The ONE rights intent every verb and surface defaults to (#63, maintainer decision 2026-10-03): commercial publishing, attributable.   |
|---------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------|
| [`UNKNOWN_LICENSE_FLAGS`](_autosummary/foley.licensing.html.md#foley.licensing.UNKNOWN_LICENSE_FLAGS)    | Fail-closed fallback for unknown / `Proprietary-*` license ids (all False, cap None).                                                  |
| [`LICENSE_FLAGS`](_autosummary/foley.licensing.html.md#foley.licensing.LICENSE_FLAGS)            | `license_id` -> default flag set (report 07 §8.1 seed table).                                                                          |
| [`CC_VERSIONS`](_autosummary/foley.licensing.html.md#foley.licensing.CC_VERSIONS)              | The CC versions foley has rows for (Freesound, FSD50K, Clotho and Wikimedia use these).                                                |
| [`DEFAULT_CC_VERSION`](_autosummary/foley.licensing.html.md#foley.licensing.DEFAULT_CC_VERSION)       | The version assumed when a source gives a CC family with no version at all (Freesound's search labels: `"Attribution"`).               |
| [`UNKNOWN_LICENSE_META`](_autosummary/foley.licensing.html.md#foley.licensing.UNKNOWN_LICENSE_META)     | Fail-closed display fallback for unknown / `Proprietary-*` license ids.                                                                |
| [`LICENSE_META`](_autosummary/foley.licensing.html.md#foley.licensing.LICENSE_META)             | `license_id` -> display name + canonical URL (one row per [`LICENSE_FLAGS`](_autosummary/foley.licensing.html.md#foley.licensing.LICENSE_FLAGS) key).         |
| [`AI_TRAINING_SCOPES`](_autosummary/foley.licensing.html.md#foley.licensing.AI_TRAINING_SCOPES)       | The `ai_training_scope` values (`None` = no narrowing beyond `ai_training_ok`).                                                        |
| [`GEN_AI_PREFERENCE_SCOPES`](_autosummary/foley.licensing.html.md#foley.licensing.GEN_AI_PREFERENCE_SCOPES) | Freesound `gen_ai_preference` -> `(ai_training_ok override, ai_training_scope)`.                                                       |

### Functions

| [`ai_scope_from_gen_ai_preference`](_autosummary/foley.licensing.html.md#foley.licensing.ai_scope_from_gen_ai_preference)(preference)       | Map a Freesound `gen_ai_preference` to `(ai_training_ok override, scope)`.                                               |
|----------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------|
| [`ai_use_permitted`](_autosummary/foley.licensing.html.md#foley.licensing.ai_use_permitted)(record, \*, ...)                 | Whether AI use of the sound (CLAP-embedding it counts) is allowed here.                                                  |
| [`apply_license_flags`](_autosummary/foley.licensing.html.md#foley.licensing.apply_license_flags)(record, \*[, overrides])      | Populate `record`'s eight derived flags from its `license_id` (+ overrides), in place, and return it.                    |
| [`derive_license_flags`](_autosummary/foley.licensing.html.md#foley.licensing.derive_license_flags)(license_id, \*[, overrides]) | Look up the flag set for a `license_id` (fail-closed fallback), then apply per-source overrides.                         |
| [`intended_use_for`](_autosummary/foley.licensing.html.md#foley.licensing.intended_use_for)([intended_use, commercial_ok])   | The rights intent a verb runs under — every verb and surface resolves it here (#63).                                     |
| [`keep`](_autosummary/foley.licensing.html.md#foley.licensing.keep)(record, intended_use)                        | Fail-closed candidate license gate (report 07 §8.2).                                                                     |
| [`keep_sound`](_autosummary/foley.licensing.html.md#foley.licensing.keep_sound)(sound_record, intended_use)            | Convenience: apply [`keep()`](_autosummary/foley.licensing.html.md#foley.licensing.keep) to a `SoundRecord`'s nested license.          |
| [`license_id_from_cc_url`](_autosummary/foley.licensing.html.md#foley.licensing.license_id_from_cc_url)(url)                       | Map a Creative-Commons license URL **or label** to `(license_id, verified)`.                                             |
| [`license_meta`](_autosummary/foley.licensing.html.md#foley.licensing.license_meta)(license_id)                          | Return the display [`LicenseMeta`](_autosummary/foley.licensing.html.md#foley.licensing.LicenseMeta) for `license_id` (fail-closed fallback). |

### Classes

| [`LicenseFlags`](_autosummary/foley.licensing.html.md#foley.licensing.LicenseFlags)([commercial_ok, ...])   | The eight derivable flags for one `license_id` (the table row type).       |
|---------------------------------------------------------------------------------------|----------------------------------------------------------------------------|
| [`LicenseMeta`](_autosummary/foley.licensing.html.md#foley.licensing.LicenseMeta)(display_name[, url])     | Human-facing display metadata for one `license_id` (name + canonical URL). |

### foley.licensing.AI_TRAINING_SCOPES *: [tuple](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[str](https://docs.python.org/3/builtins/stdtypes.html#str), ...]* *= ('none', 'open_source_only', 'nc_open_source_only')*

The `ai_training_scope` values (`None` = no narrowing beyond `ai_training_ok`).

### foley.licensing.CC_VERSIONS *: [tuple](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[str](https://docs.python.org/3/builtins/stdtypes.html#str), ...]* *= ('2.0', '2.5', '3.0', '4.0')*

The CC versions foley has rows for (Freesound, FSD50K, Clotho and Wikimedia use these).

### foley.licensing.DEFAULT_CC_VERSION *= '4.0'*

The version assumed when a source gives a CC family with no version at all
(Freesound’s search labels: `"Attribution"`). Rights are the same across versions;
only the credit’s version is a guess, so the record keeps the label it was given.

### foley.licensing.DEFAULT_INTENDED_USE *= IntendedUse(commercial=True, publish=True, redistribute_standalone=False, will_train=False, can_attribute=True, revenue_usd=0, allow_voice_or_trademark=False)*

The ONE rights intent every verb and surface defaults to (#63, maintainer decision
2026-10-03): commercial publishing, attributable. NC and SA material is refused
unless a caller explicitly passes a different [`IntendedUse`](_autosummary/foley.base.html.md#foley.base.IntendedUse) (or
`commercial_ok=False`). Treat it as read-only; copy with `dataclasses.replace`.

### foley.licensing.GEN_AI_PREFERENCE_SCOPES *: [dict](https://docs.python.org/3/builtins/stdtypes.html#dict)[[str](https://docs.python.org/3/builtins/stdtypes.html#str), [tuple](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[bool](https://docs.python.org/3/builtins/functions.html#bool) | [None](https://docs.python.org/3/builtins/constants.html#None), [str](https://docs.python.org/3/builtins/stdtypes.html#str) | [None](https://docs.python.org/3/builtins/constants.html#None)]]* *= {'no-additional-preferences': (None, None), 'no-gen-ai': (False, 'none'), 'noncommercial-open-source-models': (None, 'nc_open_source_only'), 'open-source-models': (None, 'open_source_only')}*

Freesound `gen_ai_preference` -> `(ai_training_ok override, ai_training_scope)`.
`None` as the override leaves the CC row’s flag alone. An unlisted value fails
closed (see [`ai_scope_from_gen_ai_preference()`](_autosummary/foley.licensing.html.md#foley.licensing.ai_scope_from_gen_ai_preference)).

### foley.licensing.LICENSE_FLAGS *: [dict](https://docs.python.org/3/builtins/stdtypes.html#dict)[[str](https://docs.python.org/3/builtins/stdtypes.html#str), [LicenseFlags](_autosummary/foley.licensing.html.md#foley.licensing.LicenseFlags)]* *= {'CC-BY-2.0': LicenseFlags(commercial_ok=True, embed_in_derivative_ok=True, redistribute_standalone_ok=True, cache_bytes_ok=True, modification_ok=True, ai_training_ok=True, requires_attribution=True, revenue_cap_usd=None), 'CC-BY-2.5': LicenseFlags(commercial_ok=True, embed_in_derivative_ok=True, redistribute_standalone_ok=True, cache_bytes_ok=True, modification_ok=True, ai_training_ok=True, requires_attribution=True, revenue_cap_usd=None), 'CC-BY-3.0': LicenseFlags(commercial_ok=True, embed_in_derivative_ok=True, redistribute_standalone_ok=True, cache_bytes_ok=True, modification_ok=True, ai_training_ok=True, requires_attribution=True, revenue_cap_usd=None), 'CC-BY-4.0': LicenseFlags(commercial_ok=True, embed_in_derivative_ok=True, redistribute_standalone_ok=True, cache_bytes_ok=True, modification_ok=True, ai_training_ok=True, requires_attribution=True, revenue_cap_usd=None), 'CC-BY-NC-2.0': LicenseFlags(commercial_ok=False, embed_in_derivative_ok=True, redistribute_standalone_ok=True, cache_bytes_ok=True, modification_ok=True, ai_training_ok=False, requires_attribution=True, revenue_cap_usd=None), 'CC-BY-NC-2.5': LicenseFlags(commercial_ok=False, embed_in_derivative_ok=True, redistribute_standalone_ok=True, cache_bytes_ok=True, modification_ok=True, ai_training_ok=False, requires_attribution=True, revenue_cap_usd=None), 'CC-BY-NC-3.0': LicenseFlags(commercial_ok=False, embed_in_derivative_ok=True, redistribute_standalone_ok=True, cache_bytes_ok=True, modification_ok=True, ai_training_ok=False, requires_attribution=True, revenue_cap_usd=None), 'CC-BY-NC-4.0': LicenseFlags(commercial_ok=False, embed_in_derivative_ok=True, redistribute_standalone_ok=True, cache_bytes_ok=True, modification_ok=True, ai_training_ok=False, requires_attribution=True, revenue_cap_usd=None), 'CC-Sampling+-1.0': LicenseFlags(commercial_ok=False, embed_in_derivative_ok=True, redistribute_standalone_ok=False, cache_bytes_ok=True, modification_ok=True, ai_training_ok=False, requires_attribution=True, revenue_cap_usd=None), 'CC0-1.0': LicenseFlags(commercial_ok=True, embed_in_derivative_ok=True, redistribute_standalone_ok=True, cache_bytes_ok=True, modification_ok=True, ai_training_ok=True, requires_attribution=False, revenue_cap_usd=None), 'ElevenLabs-SFX': LicenseFlags(commercial_ok=True, embed_in_derivative_ok=True, redistribute_standalone_ok=False, cache_bytes_ok=True, modification_ok=True, ai_training_ok=False, requires_attribution=False, revenue_cap_usd=None), 'MIT': LicenseFlags(commercial_ok=True, embed_in_derivative_ok=True, redistribute_standalone_ok=True, cache_bytes_ok=True, modification_ok=True, ai_training_ok=True, requires_attribution=True, revenue_cap_usd=None), 'PDM-1.0': LicenseFlags(commercial_ok=True, embed_in_derivative_ok=True, redistribute_standalone_ok=True, cache_bytes_ok=True, modification_ok=True, ai_training_ok=True, requires_attribution=False, revenue_cap_usd=None), 'Pixabay-Content': LicenseFlags(commercial_ok=True, embed_in_derivative_ok=True, redistribute_standalone_ok=False, cache_bytes_ok=True, modification_ok=True, ai_training_ok=False, requires_attribution=False, revenue_cap_usd=None), 'RemArc': LicenseFlags(commercial_ok=False, embed_in_derivative_ok=True, redistribute_standalone_ok=False, cache_bytes_ok=True, modification_ok=True, ai_training_ok=False, requires_attribution=True, revenue_cap_usd=None), 'Sonniss-GDC': LicenseFlags(commercial_ok=True, embed_in_derivative_ok=True, redistribute_standalone_ok=False, cache_bytes_ok=True, modification_ok=True, ai_training_ok=False, requires_attribution=False, revenue_cap_usd=None), 'Stability-Community': LicenseFlags(commercial_ok=True, embed_in_derivative_ok=True, redistribute_standalone_ok=False, cache_bytes_ok=True, modification_ok=True, ai_training_ok=False, requires_attribution=False, revenue_cap_usd=1000000), 'elevenlabs-free-plan': LicenseFlags(commercial_ok=False, embed_in_derivative_ok=True, redistribute_standalone_ok=False, cache_bytes_ok=True, modification_ok=True, ai_training_ok=False, requires_attribution=True, revenue_cap_usd=None), 'elevenlabs-paid-plan': LicenseFlags(commercial_ok=True, embed_in_derivative_ok=True, redistribute_standalone_ok=False, cache_bytes_ok=True, modification_ok=True, ai_training_ok=False, requires_attribution=False, revenue_cap_usd=None), 'unknown': LicenseFlags(commercial_ok=False, embed_in_derivative_ok=False, redistribute_standalone_ok=False, cache_bytes_ok=False, modification_ok=False, ai_training_ok=False, requires_attribution=False, revenue_cap_usd=None), 'user-owned': LicenseFlags(commercial_ok=True, embed_in_derivative_ok=True, redistribute_standalone_ok=True, cache_bytes_ok=True, modification_ok=True, ai_training_ok=True, requires_attribution=False, revenue_cap_usd=None)}*

`license_id` -> default flag set (report 07 §8.1 seed table).

Order of `LicenseFlags` positional args:

```default
commercial, embed, redistribute_standalone, cache_bytes, modification,
ai_training, requires_attribution, revenue_cap_usd
```

* **Type:**
  SSOT

### foley.licensing.LICENSE_META *: [dict](https://docs.python.org/3/builtins/stdtypes.html#dict)[[str](https://docs.python.org/3/builtins/stdtypes.html#str), [LicenseMeta](_autosummary/foley.licensing.html.md#foley.licensing.LicenseMeta)]* *= {'CC-BY-2.0': LicenseMeta(display_name='CC BY 2.0', url='https://creativecommons.org/licenses/by/2.0/'), 'CC-BY-2.5': LicenseMeta(display_name='CC BY 2.5', url='https://creativecommons.org/licenses/by/2.5/'), 'CC-BY-3.0': LicenseMeta(display_name='CC BY 3.0', url='https://creativecommons.org/licenses/by/3.0/'), 'CC-BY-4.0': LicenseMeta(display_name='CC BY 4.0', url='https://creativecommons.org/licenses/by/4.0/'), 'CC-BY-NC-2.0': LicenseMeta(display_name='CC BY-NC 2.0', url='https://creativecommons.org/licenses/by-nc/2.0/'), 'CC-BY-NC-2.5': LicenseMeta(display_name='CC BY-NC 2.5', url='https://creativecommons.org/licenses/by-nc/2.5/'), 'CC-BY-NC-3.0': LicenseMeta(display_name='CC BY-NC 3.0', url='https://creativecommons.org/licenses/by-nc/3.0/'), 'CC-BY-NC-4.0': LicenseMeta(display_name='CC BY-NC 4.0', url='https://creativecommons.org/licenses/by-nc/4.0/'), 'CC-Sampling+-1.0': LicenseMeta(display_name='CC Sampling+ 1.0', url='https://creativecommons.org/licenses/sampling+/1.0/'), 'CC0-1.0': LicenseMeta(display_name='CC0 1.0 Universal (Public Domain Dedication)', url='https://creativecommons.org/publicdomain/zero/1.0/'), 'ElevenLabs-SFX': LicenseMeta(display_name='ElevenLabs Sound Effects Terms (legacy: paid plan assumed)', url='https://elevenlabs.io/terms-of-use'), 'MIT': LicenseMeta(display_name='MIT License', url='https://opensource.org/license/mit'), 'PDM-1.0': LicenseMeta(display_name='Public Domain Mark 1.0 (unverified claim)', url='https://creativecommons.org/publicdomain/mark/1.0/'), 'Pixabay-Content': LicenseMeta(display_name='Pixabay Content License', url='https://pixabay.com/service/license-summary/'), 'RemArc': LicenseMeta(display_name='BBC RemArc Licence', url='https://sound-effects.bbcrewind.co.uk/licensing'), 'Sonniss-GDC': LicenseMeta(display_name='Sonniss GDC Game Audio Bundle License', url='https://sonniss.com/gdc-bundle-license'), 'Stability-Community': LicenseMeta(display_name='Stability AI Community License', url='https://stability.ai/community-license-agreement'), 'elevenlabs-free-plan': LicenseMeta(display_name='ElevenLabs Terms (free plan: non-commercial, attribution required)', url='https://elevenlabs.io/terms-of-use'), 'elevenlabs-paid-plan': LicenseMeta(display_name='ElevenLabs Terms (paid plan)', url='https://elevenlabs.io/terms-of-use'), 'unknown': LicenseMeta(display_name='Unknown / unverified license', url=None), 'user-owned': LicenseMeta(display_name='User-owned / original work', url=None)}*

`license_id` -> display name + canonical URL (one row per
[`LICENSE_FLAGS`](_autosummary/foley.licensing.html.md#foley.licensing.LICENSE_FLAGS) key). Used only for human-readable credits; never for
permission decisions (those come from [`LICENSE_FLAGS`](_autosummary/foley.licensing.html.md#foley.licensing.LICENSE_FLAGS)).

* **Type:**
  SSOT

### *class* foley.licensing.LicenseFlags(commercial_ok=False, embed_in_derivative_ok=False, redistribute_standalone_ok=False, cache_bytes_ok=False, modification_ok=False, ai_training_ok=False, requires_attribution=False, revenue_cap_usd=None)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

The eight derivable flags for one `license_id` (the table row type).

### *class* foley.licensing.LicenseMeta(display_name, url=None)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Human-facing display metadata for one `license_id` (name + canonical URL).

The presentation sibling of [`LicenseFlags`](_autosummary/foley.licensing.html.md#foley.licensing.LicenseFlags): where `LicenseFlags` holds
the *permission* row consulted by [`keep()`](_autosummary/foley.licensing.html.md#foley.licensing.keep), `LicenseMeta` holds the
*display* row consulted by the credits/attribution layer
([`foley.provenance.credits`](_autosummary/foley.provenance.credits.html.md#module-foley.provenance.credits)). Kept here so `licensing` stays the single
license authority; a record’s own `license_name` / `license_url` (when a
source populated them) take precedence over this default.

### foley.licensing.UNKNOWN_LICENSE_FLAGS *= LicenseFlags(commercial_ok=False, embed_in_derivative_ok=False, redistribute_standalone_ok=False, cache_bytes_ok=False, modification_ok=False, ai_training_ok=False, requires_attribution=False, revenue_cap_usd=None)*

Fail-closed fallback for unknown / `Proprietary-*` license ids (all False, cap None).

### foley.licensing.UNKNOWN_LICENSE_META *= LicenseMeta(display_name='Unknown / unverified license', url=None)*

Fail-closed display fallback for unknown / `Proprietary-*` license ids.

### foley.licensing.ai_scope_from_gen_ai_preference(preference)

Map a Freesound `gen_ai_preference` to `(ai_training_ok override, scope)`.

All four published values are honoured (only `no-gen-ai` was before #69). A
missing value means no stated preference; an unrecognised one fails closed.

* **Return type:**
  [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`bool`](https://docs.python.org/3/builtins/functions.html#bool)], [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]

### foley.licensing.ai_use_permitted(record, , open_source_model, commercial)

Whether AI use of the sound (CLAP-embedding it counts) is allowed here.

`ai_training_ok` must hold, and the record’s `ai_training_scope` must admit
this use: `open_source_only` needs an open-source model, `nc_open_source_only`
also needs a non-commercial purpose, `none` admits nothing.

* **Parameters:**
  * **record** ([`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)) – The rights record.
  * **open_source_model** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Whether the model using the audio is open source (the
    embedder’s `open_source` attribute; LAION-CLAP is).
  * **commercial** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Whether the purpose is commercial (foley’s default intent).
* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### foley.licensing.apply_license_flags(record, , overrides=None)

Populate `record`’s eight derived flags from its `license_id`
(+ overrides), in place, and return it.

Does NOT touch `rights_verified` — verification is a separate concern.

* **Parameters:**
  * **record** ([`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)) – The [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord) to populate.
  * **overrides** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Optional per-source flag overrides (see
    [`derive_license_flags()`](_autosummary/foley.licensing.html.md#foley.licensing.derive_license_flags)).
* **Return type:**
  [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)
* **Returns:**
  The same (mutated) `record`.

### foley.licensing.derive_license_flags(license_id, , overrides=None)

Look up the flag set for a `license_id` (fail-closed fallback), then
apply per-source overrides.

* **Parameters:**
  * **license_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The normalized license id (SPDX or foley-specific token).
  * **overrides** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Optional per-source flag overrides — e.g. Freesound forces
    `cache_bytes_ok=False` on CC0. Keys must be `LicenseFlags` fields.
* **Return type:**
  [`LicenseFlags`](_autosummary/foley.licensing.html.md#foley.licensing.LicenseFlags)
* **Returns:**
  The resolved [`LicenseFlags`](_autosummary/foley.licensing.html.md#foley.licensing.LicenseFlags) (fallback = all-False
  `UNKNOWN_LICENSE_FLAGS` for unrecognized ids).
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If `overrides` contains a key that is not a
      [`LicenseFlags`](_autosummary/foley.licensing.html.md#foley.licensing.LicenseFlags) field.

### foley.licensing.intended_use_for(intended_use=None, , commercial_ok=None)

The rights intent a verb runs under — every verb and surface resolves it here (#63).

An explicit `intended_use` wins; else [`DEFAULT_INTENDED_USE`](_autosummary/foley.licensing.html.md#foley.licensing.DEFAULT_INTENDED_USE), with
`commercial` overridden when `commercial_ok` is given (`False` is the explicit
opt-in to non-commercial material). Always a fresh copy, so callers may mutate it.

* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If both are given and disagree on `commercial`.
* **Return type:**
  [`IntendedUse`](_autosummary/foley.base.html.md#foley.base.IntendedUse)

### foley.licensing.keep(record, intended_use)

Fail-closed candidate license gate (report 07 §8.2).

Run BEFORE ranking/verification in the agent’s `decide()`. Unknown or
unverified rights => reject. Any single unmet requirement => reject.

* **Parameters:**
  * **record** ([`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)) – The candidate’s rights record.
  * **intended_use** ([`IntendedUse`](_autosummary/foley.base.html.md#foley.base.IntendedUse)) – The caller’s declared intent.
* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)
* **Returns:**
  `True` only if every requirement in `intended_use` is satisfied by
  `record`; `False` otherwise (including unverified rights).

### foley.licensing.keep_sound(sound_record, intended_use)

Convenience: apply [`keep()`](_autosummary/foley.licensing.html.md#foley.licensing.keep) to a `SoundRecord`’s nested license.

* **Parameters:**
  * **sound_record** – A [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord) (its `.license` is the
    SSOT consulted).
  * **intended_use** ([`IntendedUse`](_autosummary/foley.base.html.md#foley.base.IntendedUse)) – The caller’s declared intent.
* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)
* **Returns:**
  The result of `keep(sound_record.license, intended_use)`.

### foley.licensing.license_id_from_cc_url(url)

Map a Creative-Commons license URL **or label** to `(license_id, verified)`.

The single SSOT for turning an external source’s license string into a foley
`license_id` (FSD50K, Clotho, the Freesound API, …). It **never widens rights**:
the string is split into tokens, and

* any NoDerivatives / ShareAlike sign (`nd`, `sa`, `no derivatives`,
  `share alike`, `sharealike`) → `('unknown', False)`: foley has no row
  expressing those restrictions, so the sound is refused everywhere;
* the Public Domain Mark → `('PDM-1.0', False)`: a claim about the work, not a
  licence anyone granted, so never auto-verified (#56);
* CC0 (`publicdomain/zero`, `cc0`, `creative commons 0`) → `CC0-1.0`;
* Sampling+ → `CC-Sampling+-1.0`;
* `by` / `attribution` with any mention of commercial use (`nc`,
  `noncommercial`, `non commercial`, `no commercial use`) → `CC-BY-NC-<v>`,
  else `CC-BY-<v>`. **The version is kept** (`/by/3.0/` → `CC-BY-3.0`); a
  versionless label gets [`DEFAULT_CC_VERSION`](_autosummary/foley.licensing.html.md#foley.licensing.DEFAULT_CC_VERSION); a version foley has no row
  for (a `2.1/jp` port) fails closed;
* anything else — including a string with a token this parser does not know —
  → `('unknown', False)`.

* **Parameters:**
  **url** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – A CC license URL, a CC label string, or `None`.
* **Return type:**
  [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`bool`](https://docs.python.org/3/builtins/functions.html#bool)]
* **Returns:**
  `(license_id, rights_verified)`.

### foley.licensing.license_meta(license_id)

Return the display [`LicenseMeta`](_autosummary/foley.licensing.html.md#foley.licensing.LicenseMeta) for `license_id` (fail-closed fallback).

* **Parameters:**
  **license_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The normalized license id.
* **Return type:**
  [`LicenseMeta`](_autosummary/foley.licensing.html.md#foley.licensing.LicenseMeta)
* **Returns:**
  The mapped [`LicenseMeta`](_autosummary/foley.licensing.html.md#foley.licensing.LicenseMeta), or [`UNKNOWN_LICENSE_META`](_autosummary/foley.licensing.html.md#foley.licensing.UNKNOWN_LICENSE_META) for an
  unrecognized / `Proprietary-*` id.


# _autosummary/foley.obs.html.md

# foley.obs

Observability & the reproducible run-artifact for foley (#11).

The cross-cutting layer that makes foley debuggable, auditable, and re-renderable —
the “snapshots/stories for the SFX layer”. Every instrumented operation
(`generate` / `add_from` / `ingest` / `search` / `similar` — and, later, a
#7 `find()` / #8 `weave()` scope) can emit one [`RunManifest`](_autosummary/foley.obs.html.md#foley.obs.RunManifest):
simultaneously the debug **trace** (span tree), the reproducible **plan/seed** record,
and the **provenance** record — report 10 §1.3.

**Manifest-first**: the run-artifact is pure stdlib and exists on a bare
`pip install foley`; OpenTelemetry is an *optional mirror* behind the
`foley[obs]` extra (vendor-neutral GenAI spans for Langfuse/Datadog/…). \*\*Off by
default\*\*: `import foley` stays dol-only and a plain façade call is a byte-for-byte
no-op until `foley.obs.enable()` / `$FOLEY_OBS=1` / `with foley.obs.run(...)`.
Sensitive prompt/query/narration text is **redacted** (salted content hash) by default.

All four submodules are stdlib-only at import; `opentelemetry` loads lazily inside
[`OTelTracer`](_autosummary/foley.obs.trace.html.md#foley.obs.trace.OTelTracer) only, so importing this package is dol-only.

### Functions

| [`emit_run_manifest`](_autosummary/foley.obs.html.md#foley.obs.emit_run_manifest)(store, manifest, \*[, redactor])   | Serialize `manifest` (redacting the payload) and write it to `store`.                                                                  |
|-------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------|
| [`ingest_digest`](_autosummary/foley.obs.html.md#foley.obs.ingest_digest)(report)                                | A leak-free digest of an [`IngestReport`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestReport) for a manifest. |
| [`load_run`](_autosummary/foley.obs.html.md#foley.obs.load_run)(store, run_id)                              | Load and typed-rehydrate a [`RunManifest`](_autosummary/foley.obs.html.md#foley.obs.RunManifest) from `store` (the read helper).                |
| [`run`](_autosummary/foley.obs.html.md#foley.obs.run)([op, inputs, params])                            | Open an explicit run scope that aggregates nested façade calls into ONE manifest.                                                      |
| [`facade_run`](_autosummary/foley.obs.html.md#foley.obs.facade_run)(op, \*[, inputs, params])                 | The get-or-create seam every façade wraps its body in.                                                                                 |
| [`current_run`](_autosummary/foley.obs.html.md#foley.obs.current_run)()                                        | The active [`RunRecorder`](_autosummary/foley.obs.html.md#foley.obs.RunRecorder), or `_NULL_RUN` (for the shared child span).                   |
| [`enable`](_autosummary/foley.obs.html.md#foley.obs.enable)(\*\*overrides)                                | Turn observability on process-wide (and apply any [`ObsConfig`](_autosummary/foley.obs.html.md#foley.obs.ObsConfig) overrides).               |
| [`disable`](_autosummary/foley.obs.html.md#foley.obs.disable)()                                            | Turn observability off process-wide (façades revert to a byte-for-byte no-op).                                                         |
| [`configure`](_autosummary/foley.obs.html.md#foley.obs.configure)(\*\*overrides)                             | Apply [`ObsConfig`](_autosummary/foley.obs.html.md#foley.obs.ObsConfig) overrides WITHOUT flipping `enabled` (the test-injection seam).       |
| [`reset`](_autosummary/foley.obs.html.md#foley.obs.reset)()                                              | Restore the default config (test teardown; clears injected store/tracer/clock).                                                        |
| [`is_enabled`](_autosummary/foley.obs.html.md#foley.obs.is_enabled)()                                         | Whether observability is on (via [`enable()`](_autosummary/foley.obs.html.md#foley.obs.enable) or `$FOLEY_OBS` in {1,true,yes}).           |
| [`get_tracer`](_autosummary/foley.obs.html.md#foley.obs.get_tracer)(\*[, prefer_otel])                        | Return the effective [`Tracer`](_autosummary/foley.obs.html.md#foley.obs.Tracer) (OTel-backed when available, else no-op).                 |
| [`redact_text`](_autosummary/foley.obs.html.md#foley.obs.redact_text)(text, \*[, mode, salt, preview_chars])   | Redact one string per `mode`.                                                                                                          |

### Classes

| [`RunManifest`](_autosummary/foley.obs.html.md#foley.obs.RunManifest)(run_id, op[, created_at, ...])        | The reproducible run-artifact for one foley operation (trace ⊕ plan ⊕ provenance).                                                                                          |
|----------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`SpanRecord`](_autosummary/foley.obs.html.md#foley.obs.SpanRecord)(name, span_id[, parent_id, kind, ...]) | One node of the run's span tree (the trace half of the artifact).                                                                                                           |
| [`Step`](_autosummary/foley.obs.html.md#foley.obs.Step)(kind[, seq, event_index, span_id, ...])      | One SELECT-stage step of a `find()` run (the plan half of the artifact, #7).                                                                                                |
| [`ObsConfig`](_autosummary/foley.obs.html.md#foley.obs.ObsConfig)([enabled, force_disabled, ...])         | Process-wide observability configuration (flipped by [`enable()`](_autosummary/foley.obs.html.md#foley.obs.enable)/[`disable()`](_autosummary/foley.obs.html.md#foley.obs.disable)). |
| [`RunRecorder`](_autosummary/foley.obs.html.md#foley.obs.RunRecorder)(manifest, \*, tracer, redactor, ...)  | Builds one [`RunManifest`](_autosummary/foley.obs.html.md#foley.obs.RunManifest) (span tree + composed shapes), tracer-independent.                                                  |
| [`Tracer`](_autosummary/foley.obs.html.md#foley.obs.Tracer)(\*args, \*\*kwargs)                        | Starts mirror spans; the DI seam (default no-op, OTel-backed when present).                                                                                                 |
| [`NoOpTracer`](_autosummary/foley.obs.html.md#foley.obs.NoOpTracer)()                                      | The default [`Tracer`](_autosummary/foley.obs.html.md#foley.obs.Tracer) — yields the shared `_NOOP_SPAN`, zero deps.                                                            |
| [`Redactor`](_autosummary/foley.obs.html.md#foley.obs.Redactor)([mode, salt, preview_chars, fields])     | The SSOT applier: redacts sensitive keys in values, attribute dicts, and manifests.                                                                                         |
| [`RedactionMode`](_autosummary/foley.obs.html.md#foley.obs.RedactionMode)(\*values)                           | How a sensitive string is rendered in telemetry (`str`-Enum → serializes cleanly).                                                                                          |

### *class* foley.obs.NoOpTracer

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

The default [`Tracer`](_autosummary/foley.obs.html.md#foley.obs.Tracer) — yields the shared `_NOOP_SPAN`, zero deps.

#### start_as_current_span(name, , kind=None, attributes=None)

Yield the no-op span (context-manager protocol; nothing is recorded).

### *class* foley.obs.ObsConfig(enabled=False, force_disabled=False, redaction_mode=RedactionMode.hash, salt='foley-obs-v1', prefer_otel=True, run_store=None, tracer=None, clock=<built-in function time>, id_factory=<function ObsConfig.<lambda>>, now=<function \_iso_now>)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Process-wide observability configuration (flipped by [`enable()`](_autosummary/foley.obs.html.md#foley.obs.enable)/[`disable()`](_autosummary/foley.obs.html.md#foley.obs.disable)).

#### clock()

time() -> floating-point number

Return the current time in seconds since the Epoch.
Fractions of a second may be present if the system clock provides them.

#### now()

ISO-8601 UTC timestamp (real wall-clock; overridden to `None` in tests).

* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]

### *class* foley.obs.RedactionMode(\*values)

Bases: [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Enum`](https://docs.python.org/3/library/enum.html#enum.Enum)

How a sensitive string is rendered in telemetry (`str`-Enum → serializes cleanly).

### *class* foley.obs.Redactor(mode=RedactionMode.hash, salt='foley-obs-v1', preview_chars=0, fields=<factory>)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

The SSOT applier: redacts sensitive keys in values, attribute dicts, and manifests.

#### redact_attrs(attrs)

Redact every sensitive key in a (shallow) attribute/inputs dict.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

#### redact_error(exc)

Redact an exception for storage/export.

An exception message can echo the raw prompt/query/narration (hosted
backends commonly do), so by default only the exception **type name** is
recorded (safe + still useful); the full `repr` is kept only in
`full` mode (opt-in local debug).

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

#### redact_manifest(payload)

Deep-walk `payload` (dict/list), redacting any sensitive key at any depth.

The emit-time net: catches `inputs.query` / `seeds[*].prompt` / any nested
sensitive value a caller stuffed past the record-time layer.

#### redact_value(key, value)

Redact `value` iff `key` is a sensitive field and `value` is a string.

### *class* foley.obs.RunManifest(run_id, op, created_at=None, foley_version=None, inputs=<factory>, params=<factory>, spans=<factory>, steps=<factory>, ingest_report=None, result_ids=<factory>, candidate_scores=<factory>, credits_ref=None, disclosure_refs=<factory>, seeds=<factory>, plan_ref=None, trace_ref=None, status='ok', error=None, schema_version=1)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

The reproducible run-artifact for one foley operation (trace ⊕ plan ⊕ provenance).

Persisted by [`emit_run_manifest()`](_autosummary/foley.obs.html.md#foley.obs.emit_run_manifest) into a run store keyed by `run_id`.
Sensitive prompt/query text lives redacted (see [`foley.obs.redact`](_autosummary/foley.obs.redact.html.md#module-foley.obs.redact)); chosen
clips are held by reference (`SoundRecord` id) so the manifest stays light and a
trace can be replayed against a fresh index.

### *class* foley.obs.RunRecorder(manifest, , tracer, redactor, run_store, clock, id_factory)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Builds one [`RunManifest`](_autosummary/foley.obs.html.md#foley.obs.RunManifest) (span tree + composed shapes), tracer-independent.

#### add_step(step)

Append a SELECT-stage [`Step`](_autosummary/foley.obs.html.md#foley.obs.Step) (#7), deep-redacting `detail` at record time.

The record-time net (belt over the emit-time sweep): event text carried in
`detail` (under the `query` key) is hashed here so a manifest read straight
off `self.manifest.steps` — before any emit — never holds raw narration.
`seq` is assigned here (append position) when the caller left it `None`.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### emit()

Persist the manifest to the run store — swallowing any write failure.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### set_plan_ref(plan_ref)

Fill the reserved #8 `plan_ref` slot (a light join dict — no text).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### span(name, , kind=None, \*\*attributes)

Open a span: append a [`SpanRecord`](_autosummary/foley.obs.html.md#foley.obs.SpanRecord) (from our own clock/ids) + mirror it.

### *class* foley.obs.SpanRecord(name, span_id, parent_id=None, kind=None, start_ms=None, duration_ms=None, status='ok', attributes=<factory>, events=<factory>, error=None)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

One node of the run’s span tree (the trace half of the artifact).

Built by the recorder from its own clock + id-factory, independent of any tracer,
so the tree is complete even when the OTel mirror is a total no-op.

### *class* foley.obs.Step(kind, seq=None, event_index=None, span_id=None, status='ok', detail=<factory>)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

One SELECT-stage step of a `find()` run (the plan half of the artifact, #7).

The typed entry type of `RunManifest.steps`: a compact, redacted record of
each `decompose`/`refine`/`search`/`license_gate`/`verify`/`decide`/
`generate`/`place` stage, in `seq` order, with `span_id` joining the step
back to the [`SpanRecord`](_autosummary/foley.obs.html.md#foley.obs.SpanRecord) that timed it. `detail` is a small,
already-redacted dict (event text lives under the `query` key so the redactor
catches it). Rehydrated element-wise by `RunManifest.from_dict()` exactly like
`spans` (see the module docstring).

### *class* foley.obs.Tracer(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

Starts mirror spans; the DI seam (default no-op, OTel-backed when present).

### foley.obs.configure(\*\*overrides)

Apply [`ObsConfig`](_autosummary/foley.obs.html.md#foley.obs.ObsConfig) overrides WITHOUT flipping `enabled` (the test-injection seam).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### foley.obs.current_run()

The active [`RunRecorder`](_autosummary/foley.obs.html.md#foley.obs.RunRecorder), or `_NULL_RUN` (for the shared child span).

### foley.obs.disable()

Turn observability off process-wide (façades revert to a byte-for-byte no-op).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### foley.obs.emit_run_manifest(store, manifest, , redactor=None)

Serialize `manifest` (redacting the payload) and write it to `store`.

The plain-dict write path keeps an emit-time interposition point for the
belt-and-suspenders redaction sweep (do NOT couple the write to
auto-serialization). Returns the `run_id`.

* **Parameters:**
  * **store** ([`MutableMapping`](https://docs.python.org/3/library/typing.html#typing.MutableMapping)) – A `MutableMapping[str, dict]` (default:
    [`foley.stores.make_run_store()`](_autosummary/foley.stores.html.md#foley.stores.make_run_store); a dict in tests).
  * **manifest** ([`RunManifest`](_autosummary/foley.obs.run_artifact.html.md#foley.obs.run_artifact.RunManifest)) – The [`RunManifest`](_autosummary/foley.obs.html.md#foley.obs.RunManifest) to persist.
  * **redactor** – An optional [`Redactor`](_autosummary/foley.obs.redact.html.md#foley.obs.redact.Redactor) applied to the full
    payload before the write (the emit-time net).
* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### foley.obs.enable(\*\*overrides)

Turn observability on process-wide (and apply any [`ObsConfig`](_autosummary/foley.obs.html.md#foley.obs.ObsConfig) overrides).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### foley.obs.facade_run(op, , inputs=None, params=None)

The get-or-create seam every façade wraps its body in.

Nested (a run is already active) → a child span into the SAME manifest, no emit
(the outer owner emits). Enabled + top-level → a new recorder that emits one
manifest on exit. Disabled → the zero-cost `_NULL_RUN`.

### foley.obs.get_tracer(, prefer_otel=True)

Return the effective [`Tracer`](_autosummary/foley.obs.html.md#foley.obs.Tracer) (OTel-backed when available, else no-op).

* **Parameters:**
  **prefer_otel** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – When `True` (default) and `opentelemetry` is importable, return
  an `OTelTracer` (itself a free no-op until the host configures an SDK);
  any construction failure falls back to the no-op. `False` forces the stdlib
  [`NoOpTracer`](_autosummary/foley.obs.html.md#foley.obs.NoOpTracer) — the hermetic-test lever (this dev env may have otel).
* **Return type:**
  [`Tracer`](_autosummary/foley.obs.trace.html.md#foley.obs.trace.Tracer)
* **Returns:**
  A [`Tracer`](_autosummary/foley.obs.html.md#foley.obs.Tracer) (never raises).

### foley.obs.ingest_digest(report)

A leak-free digest of an [`IngestReport`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestReport) for a manifest.

Records the per-clip **outcomes** (id, status, QC status, notes) + summary counts —
NOT the full `SoundRecord`s. The records carry `caption` (which equals the
raw generation prompt for a generated clip) + `tags` and already live in the meta
store keyed by the id referenced in `result_ids`, so embedding them would both
leak prompt text into the manifest and duplicate data. Duck-typed over the report
(`.summary()` + `.results`) to avoid an obs → index import coupling.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### foley.obs.is_enabled()

Whether observability is on (via [`enable()`](_autosummary/foley.obs.html.md#foley.obs.enable) or `$FOLEY_OBS` in {1,true,yes}).

`force_disabled` (set by [`foley.runtime.offline_scope()`](_autosummary/foley.runtime.html.md#foley.runtime.offline_scope) for a telemetry-off
posture) and a telemetry-off runtime (`$FOLEY_OFFLINE`) hard-override both — so
offline mode’s “nothing leaves the device” contract holds even when `$FOLEY_OBS`
is exported.

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### foley.obs.load_run(store, run_id)

Load and typed-rehydrate a [`RunManifest`](_autosummary/foley.obs.html.md#foley.obs.RunManifest) from `store` (the read helper).

* **Return type:**
  [`RunManifest`](_autosummary/foley.obs.run_artifact.html.md#foley.obs.run_artifact.RunManifest)

### foley.obs.redact_text(text, , mode=RedactionMode.hash, salt='foley-obs-v1', preview_chars=0)

Redact one string per `mode`.

* **Parameters:**
  * **text** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – The (possibly sensitive) string, or `None`.
  * **mode** ([`RedactionMode`](_autosummary/foley.obs.redact.html.md#foley.obs.redact.RedactionMode)) – `off` → `None`; `full` → `text` verbatim; `hash` (default) →
    `{"sha256": <salted hex>, "len": <n>}` (+ `"preview"` only if
    `preview_chars > 0`).
  * **salt** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Salt mixed into the hash (injectable; default fixed for diffability).
  * **preview_chars** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – If > 0 (hash mode), include a leading `text[:preview_chars]`
    preview. Default 0 → **zero content leak**.
* **Returns:**
  `None`, the raw string, or a hash dict — depending on `mode`.

### foley.obs.reset()

Restore the default config (test teardown; clears injected store/tracer/clock).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### foley.obs.run(op='run', , inputs=None, params=None, \*\*overrides)

Open an explicit run scope that aggregates nested façade calls into ONE manifest.

Forces observability on for the scope even when it is merely *disabled* (the #7
`find()` aggregation entrypoint). The one thing it does **not** override is the
hard-off `force_disabled` flag: [`foley.runtime.offline_scope()`](_autosummary/foley.runtime.html.md#foley.runtime.offline_scope) sets it for a
telemetry-off posture, so an explicit `run()` inside offline mode records and
exports **nothing** — the “nothing leaves the device” contract holds on the
`find()`/`weave()` paths too, not just the `facade_run` ones. Get-or-create: a
nested [`run()`](_autosummary/foley.obs.html.md#foley.obs.run) reuses the active recorder. `overrides` are per-scope
[`ObsConfig`](_autosummary/foley.obs.html.md#foley.obs.ObsConfig) fields (e.g. `run_store`, `prefer_otel`, `clock`).

### Modules

| [`recorder`](_autosummary/foley.obs.recorder.html.md#module-foley.obs.recorder)         | The run-recorder: owns the manifest, drives the OTel mirror, off by default (#11).   |
|---------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------|
| [`redact`](_autosummary/foley.obs.redact.html.md#module-foley.obs.redact)             | Redaction of sensitive narration / prompt / query text in telemetry (#11).           |
| [`run_artifact`](_autosummary/foley.obs.run_artifact.html.md#module-foley.obs.run_artifact) | The reproducible run-artifact SSOT — `RunManifest` + `SpanRecord` (#11).             |
| [`trace`](_autosummary/foley.obs.trace.html.md#module-foley.obs.trace)               | The OpenTelemetry MIRROR seam for foley's observability (#11).                       |


# _autosummary/foley.obs.recorder.html.md

# foley.obs.recorder

The run-recorder: owns the manifest, drives the OTel mirror, off by default (#11).

This is where the manifest-first design lives. A [`RunRecorder`](_autosummary/foley.obs.recorder.html.md#foley.obs.recorder.RunRecorder) builds the
[`RunManifest`](_autosummary/foley.obs.run_artifact.html.md#foley.obs.run_artifact.RunManifest) + its span tree from its own clock +
id-factory — **independent of the tracer** — so the artifact is complete with a total
no-op tracer. The façades reach it through [`facade_run()`](_autosummary/foley.obs.recorder.html.md#foley.obs.recorder.facade_run) (a get-or-create,
off-by-default context manager) and [`current_run()`](_autosummary/foley.obs.recorder.html.md#foley.obs.recorder.current_run); a #7 `find()` opens an
explicit [`run()`](_autosummary/foley.obs.recorder.html.md#foley.obs.recorder.run) scope so nested façade calls aggregate into **one** manifest.

Discipline (mirroring the rest of foley): **off by default** — a plain
`import foley` + a plain `foley.generate(...)` is a byte-for-byte no-op that
touches no store and stays dol-only. `foley.obs.enable()` / `$FOLEY_OBS=1` /
`with foley.obs.run(...)` turn it on. Emitting a manifest needs NO extra (all
stdlib); only the OTel span mirror needs `foley[obs]`. Recording never raises into a
façade — a store-write failure degrades gracefully.

### Functions

| [`configure`](_autosummary/foley.obs.recorder.html.md#foley.obs.recorder.configure)(\*\*overrides)             | Apply [`ObsConfig`](_autosummary/foley.obs.recorder.html.md#foley.obs.recorder.ObsConfig) overrides WITHOUT flipping `enabled` (the test-injection seam).   |
|---------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------|
| [`current_run`](_autosummary/foley.obs.recorder.html.md#foley.obs.recorder.current_run)()                        | The active [`RunRecorder`](_autosummary/foley.obs.recorder.html.md#foley.obs.recorder.RunRecorder), or `_NULL_RUN` (for the shared child span).               |
| [`disable`](_autosummary/foley.obs.recorder.html.md#foley.obs.recorder.disable)()                            | Turn observability off process-wide (façades revert to a byte-for-byte no-op).                                                     |
| [`enable`](_autosummary/foley.obs.recorder.html.md#foley.obs.recorder.enable)(\*\*overrides)                | Turn observability on process-wide (and apply any [`ObsConfig`](_autosummary/foley.obs.recorder.html.md#foley.obs.recorder.ObsConfig) overrides).           |
| [`facade_run`](_autosummary/foley.obs.recorder.html.md#foley.obs.recorder.facade_run)(op, \*[, inputs, params]) | The get-or-create seam every façade wraps its body in.                                                                             |
| [`is_enabled`](_autosummary/foley.obs.recorder.html.md#foley.obs.recorder.is_enabled)()                         | Whether observability is on (via [`enable()`](_autosummary/foley.obs.recorder.html.md#foley.obs.recorder.enable) or `$FOLEY_OBS` in {1,true,yes}).       |
| [`reset`](_autosummary/foley.obs.recorder.html.md#foley.obs.recorder.reset)()                              | Restore the default config (test teardown; clears injected store/tracer/clock).                                                    |
| [`run`](_autosummary/foley.obs.recorder.html.md#foley.obs.recorder.run)([op, inputs, params])            | Open an explicit run scope that aggregates nested façade calls into ONE manifest.                                                  |

### Classes

| [`ObsConfig`](_autosummary/foley.obs.recorder.html.md#foley.obs.recorder.ObsConfig)([enabled, force_disabled, ...])        | Process-wide observability configuration (flipped by [`enable()`](_autosummary/foley.obs.recorder.html.md#foley.obs.recorder.enable)/[`disable()`](_autosummary/foley.obs.recorder.html.md#foley.obs.recorder.disable)).   |
|---------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`RunRecorder`](_autosummary/foley.obs.recorder.html.md#foley.obs.recorder.RunRecorder)(manifest, \*, tracer, redactor, ...) | Builds one `RunManifest` (span tree + composed shapes), tracer-independent.                                                                                                   |

### *class* foley.obs.recorder.ObsConfig(enabled=False, force_disabled=False, redaction_mode=RedactionMode.hash, salt='foley-obs-v1', prefer_otel=True, run_store=None, tracer=None, clock=<built-in function time>, id_factory=<function ObsConfig.<lambda>>, now=<function \_iso_now>)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Process-wide observability configuration (flipped by [`enable()`](_autosummary/foley.obs.recorder.html.md#foley.obs.recorder.enable)/[`disable()`](_autosummary/foley.obs.recorder.html.md#foley.obs.recorder.disable)).

#### clock()

time() -> floating-point number

Return the current time in seconds since the Epoch.
Fractions of a second may be present if the system clock provides them.

#### now()

ISO-8601 UTC timestamp (real wall-clock; overridden to `None` in tests).

* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]

### *class* foley.obs.recorder.RunRecorder(manifest, , tracer, redactor, run_store, clock, id_factory)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Builds one `RunManifest` (span tree + composed shapes), tracer-independent.

#### add_step(step)

Append a SELECT-stage `Step` (#7), deep-redacting `detail` at record time.

The record-time net (belt over the emit-time sweep): event text carried in
`detail` (under the `query` key) is hashed here so a manifest read straight
off `self.manifest.steps` — before any emit — never holds raw narration.
`seq` is assigned here (append position) when the caller left it `None`.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### emit()

Persist the manifest to the run store — swallowing any write failure.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### set_plan_ref(plan_ref)

Fill the reserved #8 `plan_ref` slot (a light join dict — no text).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

#### span(name, , kind=None, \*\*attributes)

Open a span: append a `SpanRecord` (from our own clock/ids) + mirror it.

### foley.obs.recorder.configure(\*\*overrides)

Apply [`ObsConfig`](_autosummary/foley.obs.recorder.html.md#foley.obs.recorder.ObsConfig) overrides WITHOUT flipping `enabled` (the test-injection seam).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### foley.obs.recorder.current_run()

The active [`RunRecorder`](_autosummary/foley.obs.recorder.html.md#foley.obs.recorder.RunRecorder), or `_NULL_RUN` (for the shared child span).

### foley.obs.recorder.disable()

Turn observability off process-wide (façades revert to a byte-for-byte no-op).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### foley.obs.recorder.enable(\*\*overrides)

Turn observability on process-wide (and apply any [`ObsConfig`](_autosummary/foley.obs.recorder.html.md#foley.obs.recorder.ObsConfig) overrides).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### foley.obs.recorder.facade_run(op, , inputs=None, params=None)

The get-or-create seam every façade wraps its body in.

Nested (a run is already active) → a child span into the SAME manifest, no emit
(the outer owner emits). Enabled + top-level → a new recorder that emits one
manifest on exit. Disabled → the zero-cost `_NULL_RUN`.

### foley.obs.recorder.is_enabled()

Whether observability is on (via [`enable()`](_autosummary/foley.obs.recorder.html.md#foley.obs.recorder.enable) or `$FOLEY_OBS` in {1,true,yes}).

`force_disabled` (set by [`foley.runtime.offline_scope()`](_autosummary/foley.runtime.html.md#foley.runtime.offline_scope) for a telemetry-off
posture) and a telemetry-off runtime (`$FOLEY_OFFLINE`) hard-override both — so
offline mode’s “nothing leaves the device” contract holds even when `$FOLEY_OBS`
is exported.

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### foley.obs.recorder.reset()

Restore the default config (test teardown; clears injected store/tracer/clock).

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### foley.obs.recorder.run(op='run', , inputs=None, params=None, \*\*overrides)

Open an explicit run scope that aggregates nested façade calls into ONE manifest.

Forces observability on for the scope even when it is merely *disabled* (the #7
`find()` aggregation entrypoint). The one thing it does **not** override is the
hard-off `force_disabled` flag: [`foley.runtime.offline_scope()`](_autosummary/foley.runtime.html.md#foley.runtime.offline_scope) sets it for a
telemetry-off posture, so an explicit `run()` inside offline mode records and
exports **nothing** — the “nothing leaves the device” contract holds on the
`find()`/`weave()` paths too, not just the `facade_run` ones. Get-or-create: a
nested [`run()`](_autosummary/foley.obs.recorder.html.md#foley.obs.recorder.run) reuses the active recorder. `overrides` are per-scope
[`ObsConfig`](_autosummary/foley.obs.recorder.html.md#foley.obs.recorder.ObsConfig) fields (e.g. `run_store`, `prefer_otel`, `clock`).


# _autosummary/foley.obs.redact.html.md

# foley.obs.redact

Redaction of sensitive narration / prompt / query text in telemetry (#11).

A published product’s narration is frequently confidential or pre-release, yet foley
exfiltrates prompt/query text to LLM, generation, and search backends (report 12
§privacy). So every value foley writes into a span attribute or a run-manifest is
passed through this module’s SSOT redactor, which — by **default** — replaces a
sensitive string with a salted content **hash** + length (never the raw text), so a
manifest still *joins* a prompt to its provenance without exposing it.

Stdlib-only. Applied at TWO boundaries (belt-and-suspenders, mirroring
`qc._json_safe`’s construction-time clamp + serialization sweep):

* **record-time** (primary): the recorder routes every sensitive value through
  [`Redactor.redact_value()`](_autosummary/foley.obs.redact.html.md#foley.obs.redact.Redactor.redact_value) before it enters the in-memory `SpanRecord` /
  > `RunManifest` — so raw text never reaches the OTel mirror either;
* **emit-time** (net): [`Redactor.redact_manifest()`](_autosummary/foley.obs.redact.html.md#foley.obs.redact.Redactor.redact_manifest) deep-walks the serialized
  manifest before the store write, catching anything stuffed past record-time.

### Module Attributes

| [`REDACT_FIELDS`](_autosummary/foley.obs.redact.html.md#foley.obs.redact.REDACT_FIELDS)   | The attribute / field keys whose string values are sensitive and redacted.   |
|------------------------------------------------------------------|------------------------------------------------------------------------------|

### Functions

| [`redact_text`](_autosummary/foley.obs.redact.html.md#foley.obs.redact.redact_text)(text, \*[, mode, salt, preview_chars])   | Redact one string per `mode`.   |
|-------------------------------------------------------------------------------------------------------|---------------------------------|

### Classes

| [`RedactionMode`](_autosummary/foley.obs.redact.html.md#foley.obs.redact.RedactionMode)(\*values)                       | How a sensitive string is rendered in telemetry (`str`-Enum → serializes cleanly).   |
|------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------|
| [`Redactor`](_autosummary/foley.obs.redact.html.md#foley.obs.redact.Redactor)([mode, salt, preview_chars, fields]) | The SSOT applier: redacts sensitive keys in values, attribute dicts, and manifests.  |

### foley.obs.redact.REDACT_FIELDS *: [frozenset](https://docs.python.org/3/builtins/stdtypes.html#frozenset)[[str](https://docs.python.org/3/builtins/stdtypes.html#str)]* *= frozenset({'context_text', 'gen_ai.completion', 'gen_ai.prompt', 'generation_prompt', 'narration', 'negative_prompt', 'onset', 'prompt', 'query'})*

The attribute / field keys whose string values are sensitive and redacted. The
narration/prompt/query surfaces report 12 §11 names; `context_text` / `narration`
/ `gen_ai.*` producers arrive with the #7 agent, but the seam + keys exist now.

### *class* foley.obs.redact.RedactionMode(\*values)

Bases: [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Enum`](https://docs.python.org/3/library/enum.html#enum.Enum)

How a sensitive string is rendered in telemetry (`str`-Enum → serializes cleanly).

### *class* foley.obs.redact.Redactor(mode=RedactionMode.hash, salt='foley-obs-v1', preview_chars=0, fields=<factory>)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

The SSOT applier: redacts sensitive keys in values, attribute dicts, and manifests.

#### redact_attrs(attrs)

Redact every sensitive key in a (shallow) attribute/inputs dict.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

#### redact_error(exc)

Redact an exception for storage/export.

An exception message can echo the raw prompt/query/narration (hosted
backends commonly do), so by default only the exception **type name** is
recorded (safe + still useful); the full `repr` is kept only in
`full` mode (opt-in local debug).

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

#### redact_manifest(payload)

Deep-walk `payload` (dict/list), redacting any sensitive key at any depth.

The emit-time net: catches `inputs.query` / `seeds[*].prompt` / any nested
sensitive value a caller stuffed past the record-time layer.

#### redact_value(key, value)

Redact `value` iff `key` is a sensitive field and `value` is a string.

### foley.obs.redact.redact_text(text, , mode=RedactionMode.hash, salt='foley-obs-v1', preview_chars=0)

Redact one string per `mode`.

* **Parameters:**
  * **text** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – The (possibly sensitive) string, or `None`.
  * **mode** ([`RedactionMode`](_autosummary/foley.obs.redact.html.md#foley.obs.redact.RedactionMode)) – `off` → `None`; `full` → `text` verbatim; `hash` (default) →
    `{"sha256": <salted hex>, "len": <n>}` (+ `"preview"` only if
    `preview_chars > 0`).
  * **salt** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Salt mixed into the hash (injectable; default fixed for diffability).
  * **preview_chars** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – If > 0 (hash mode), include a leading `text[:preview_chars]`
    preview. Default 0 → **zero content leak**.
* **Returns:**
  `None`, the raw string, or a hash dict — depending on `mode`.


# _autosummary/foley.obs.run_artifact.html.md

# foley.obs.run_artifact

The reproducible run-artifact SSOT — `RunManifest` + `SpanRecord` (#11).

Every instrumented foley operation (`generate` / `add_from` / `ingest` /
`search` / `similar` — and, later, a #7 `find()` / #8 `weave()` scope) emits
one [`RunManifest`](_autosummary/foley.obs.run_artifact.html.md#foley.obs.run_artifact.RunManifest): simultaneously the debug **trace** (the span tree), the
reproducible **plan/seed** record (inputs, params, seeds, chosen clips), and the
**provenance** record (credits + disclosure refs) — report 10 §1.3. It is the join
that makes foley debuggable, evaluable, and reproducible; #8’s
`SoundDesignTimeline.run_manifest_ref` will point at `RunManifest.run_id`.

Pure data: both dataclasses subclass [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin), so
`to_dict` / `to_json` / `from_dict` (incl. `spans` rehydrating element-wise to
[`SpanRecord`](_autosummary/foley.obs.run_artifact.html.md#foley.obs.run_artifact.SpanRecord), exactly like `IngestReport.results`) come for free, and adding
fields later (token usage, cost, timings for #7) is schema-safe with no
`SCHEMA_VERSION` bump. The manifest **composes** the existing shapes — it embeds
`IngestReport.to_dict()` and `Credits.manifest` and references `Candidate` ids +
`LicenseRecord` generation fields — it never forks them.

### Functions

| [`emit_run_manifest`](_autosummary/foley.obs.run_artifact.html.md#foley.obs.run_artifact.emit_run_manifest)(store, manifest, \*[, redactor])   | Serialize `manifest` (redacting the payload) and write it to `store`.                                                                  |
|-------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------|
| [`ingest_digest`](_autosummary/foley.obs.run_artifact.html.md#foley.obs.run_artifact.ingest_digest)(report)                                | A leak-free digest of an [`IngestReport`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestReport) for a manifest. |
| [`load_run`](_autosummary/foley.obs.run_artifact.html.md#foley.obs.run_artifact.load_run)(store, run_id)                              | Load and typed-rehydrate a [`RunManifest`](_autosummary/foley.obs.run_artifact.html.md#foley.obs.run_artifact.RunManifest) from `store` (the read helper).                |

### Classes

| [`RunManifest`](_autosummary/foley.obs.run_artifact.html.md#foley.obs.run_artifact.RunManifest)(run_id, op[, created_at, ...])        | The reproducible run-artifact for one foley operation (trace ⊕ plan ⊕ provenance).   |
|----------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------|
| [`SpanRecord`](_autosummary/foley.obs.run_artifact.html.md#foley.obs.run_artifact.SpanRecord)(name, span_id[, parent_id, kind, ...]) | One node of the run's span tree (the trace half of the artifact).                    |
| [`Step`](_autosummary/foley.obs.run_artifact.html.md#foley.obs.run_artifact.Step)(kind[, seq, event_index, span_id, ...])      | One SELECT-stage step of a `find()` run (the plan half of the artifact, #7).         |

### *class* foley.obs.run_artifact.RunManifest(run_id, op, created_at=None, foley_version=None, inputs=<factory>, params=<factory>, spans=<factory>, steps=<factory>, ingest_report=None, result_ids=<factory>, candidate_scores=<factory>, credits_ref=None, disclosure_refs=<factory>, seeds=<factory>, plan_ref=None, trace_ref=None, status='ok', error=None, schema_version=1)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

The reproducible run-artifact for one foley operation (trace ⊕ plan ⊕ provenance).

Persisted by [`emit_run_manifest()`](_autosummary/foley.obs.run_artifact.html.md#foley.obs.run_artifact.emit_run_manifest) into a run store keyed by `run_id`.
Sensitive prompt/query text lives redacted (see [`foley.obs.redact`](_autosummary/foley.obs.redact.html.md#module-foley.obs.redact)); chosen
clips are held by reference (`SoundRecord` id) so the manifest stays light and a
trace can be replayed against a fresh index.

### *class* foley.obs.run_artifact.SpanRecord(name, span_id, parent_id=None, kind=None, start_ms=None, duration_ms=None, status='ok', attributes=<factory>, events=<factory>, error=None)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

One node of the run’s span tree (the trace half of the artifact).

Built by the recorder from its own clock + id-factory, independent of any tracer,
so the tree is complete even when the OTel mirror is a total no-op.

### *class* foley.obs.run_artifact.Step(kind, seq=None, event_index=None, span_id=None, status='ok', detail=<factory>)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

One SELECT-stage step of a `find()` run (the plan half of the artifact, #7).

The typed entry type of `RunManifest.steps`: a compact, redacted record of
each `decompose`/`refine`/`search`/`license_gate`/`verify`/`decide`/
`generate`/`place` stage, in `seq` order, with `span_id` joining the step
back to the [`SpanRecord`](_autosummary/foley.obs.run_artifact.html.md#foley.obs.run_artifact.SpanRecord) that timed it. `detail` is a small,
already-redacted dict (event text lives under the `query` key so the redactor
catches it). Rehydrated element-wise by `RunManifest.from_dict()` exactly like
`spans` (see the module docstring).

### foley.obs.run_artifact.emit_run_manifest(store, manifest, , redactor=None)

Serialize `manifest` (redacting the payload) and write it to `store`.

The plain-dict write path keeps an emit-time interposition point for the
belt-and-suspenders redaction sweep (do NOT couple the write to
auto-serialization). Returns the `run_id`.

* **Parameters:**
  * **store** ([`MutableMapping`](https://docs.python.org/3/library/typing.html#typing.MutableMapping)) – A `MutableMapping[str, dict]` (default:
    [`foley.stores.make_run_store()`](_autosummary/foley.stores.html.md#foley.stores.make_run_store); a dict in tests).
  * **manifest** ([`RunManifest`](_autosummary/foley.obs.run_artifact.html.md#foley.obs.run_artifact.RunManifest)) – The [`RunManifest`](_autosummary/foley.obs.run_artifact.html.md#foley.obs.run_artifact.RunManifest) to persist.
  * **redactor** – An optional [`Redactor`](_autosummary/foley.obs.redact.html.md#foley.obs.redact.Redactor) applied to the full
    payload before the write (the emit-time net).
* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### foley.obs.run_artifact.ingest_digest(report)

A leak-free digest of an [`IngestReport`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestReport) for a manifest.

Records the per-clip **outcomes** (id, status, QC status, notes) + summary counts —
NOT the full `SoundRecord`s. The records carry `caption` (which equals the
raw generation prompt for a generated clip) + `tags` and already live in the meta
store keyed by the id referenced in `result_ids`, so embedding them would both
leak prompt text into the manifest and duplicate data. Duck-typed over the report
(`.summary()` + `.results`) to avoid an obs → index import coupling.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### foley.obs.run_artifact.load_run(store, run_id)

Load and typed-rehydrate a [`RunManifest`](_autosummary/foley.obs.run_artifact.html.md#foley.obs.run_artifact.RunManifest) from `store` (the read helper).

* **Return type:**
  [`RunManifest`](_autosummary/foley.obs.run_artifact.html.md#foley.obs.run_artifact.RunManifest)


# _autosummary/foley.obs.trace.html.md

# foley.obs.trace

The OpenTelemetry MIRROR seam for foley’s observability (#11).

foley’s reproducible run-artifact (the [`RunManifest`](_autosummary/foley.obs.run_artifact.html.md#foley.obs.run_artifact.RunManifest))
is built by the recorder from its own clock + id-factory, \*\*independent of any
tracer\*\* — so a bare `pip install foley` still emits a complete manifest with zero
dependencies. This module is *only* the optional OpenTelemetry **mirror**: when the
`foley[obs]` extra is installed, foley additionally emits vendor-neutral OTel GenAI
spans (consumable by Langfuse / Datadog / Honeycomb / …); when it is not, a
stdlib [`NoOpTracer`](_autosummary/foley.obs.trace.html.md#foley.obs.trace.NoOpTracer) makes every instrumentation call a free no-op.

`opentelemetry` is imported **lazily inside** [`OTelTracer`](_autosummary/foley.obs.trace.html.md#foley.obs.trace.OTelTracer) only, never at
module top level, so importing this module (and `import foley`) stays dol-only.
foley never configures an SDK / exporter — that is the host application’s job
(`trace.set_tracer_provider(...)`); until it does, even the OTel-backed tracer is a
free `ProxyTracer` no-op, so instrumentation is safe and ~zero-cost by default.

### Module Attributes

| [`GENAI`](_autosummary/foley.obs.trace.html.md#foley.obs.trace.GENAI)   | The GenAI semantic-convention attribute keys foley emits.   |
|----------------------------------------------------------|-------------------------------------------------------------|

### Functions

| [`get_tracer`](_autosummary/foley.obs.trace.html.md#foley.obs.trace.get_tracer)(\*[, prefer_otel])   | Return the effective [`Tracer`](_autosummary/foley.obs.trace.html.md#foley.obs.trace.Tracer) (OTel-backed when available, else no-op).   |
|----------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------|

### Classes

| [`NoOpSpan`](_autosummary/foley.obs.trace.html.md#foley.obs.trace.NoOpSpan)()                 | A zero-cost span: every method does nothing; `trace_id` is always `None`.                                             |
|-----------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------|
| [`NoOpTracer`](_autosummary/foley.obs.trace.html.md#foley.obs.trace.NoOpTracer)()               | The default [`Tracer`](_autosummary/foley.obs.trace.html.md#foley.obs.trace.Tracer) — yields the shared `_NOOP_SPAN`, zero deps.      |
| [`OTelTracer`](_autosummary/foley.obs.trace.html.md#foley.obs.trace.OTelTracer)()               | An OpenTelemetry-backed [`Tracer`](_autosummary/foley.obs.trace.html.md#foley.obs.trace.Tracer) (lazy import; requires `foley[obs]`). |
| [`Span`](_autosummary/foley.obs.trace.html.md#foley.obs.trace.Span)(\*args, \*\*kwargs)   | The minimal mirror-span surface foley code calls (structural).                                                        |
| [`Tracer`](_autosummary/foley.obs.trace.html.md#foley.obs.trace.Tracer)(\*args, \*\*kwargs) | Starts mirror spans; the DI seam (default no-op, OTel-backed when present).                                           |

### foley.obs.trace.GENAI *= {'data_source_id': 'gen_ai.data_source.id', 'error_type': 'error.type', 'finish_reasons': 'gen_ai.response.finish_reasons', 'input_tokens': 'gen_ai.usage.input_tokens', 'operation': 'gen_ai.operation.name', 'output_tokens': 'gen_ai.usage.output_tokens', 'provider': 'gen_ai.provider.name', 'request_model': 'gen_ai.request.model', 'response_model': 'gen_ai.response.model'}*

The GenAI semantic-convention attribute keys foley emits. HARDCODED as strings on
purpose — the `opentelemetry.semconv._incubating` constants are Development-
stability and shift across minor versions. This dict is the single place they are named.

### *class* foley.obs.trace.NoOpSpan

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

A zero-cost span: every method does nothing; `trace_id` is always `None`.

### *class* foley.obs.trace.NoOpTracer

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

The default [`Tracer`](_autosummary/foley.obs.trace.html.md#foley.obs.trace.Tracer) — yields the shared `_NOOP_SPAN`, zero deps.

#### start_as_current_span(name, , kind=None, attributes=None)

Yield the no-op span (context-manager protocol; nothing is recorded).

### *class* foley.obs.trace.OTelTracer

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

An OpenTelemetry-backed [`Tracer`](_autosummary/foley.obs.trace.html.md#foley.obs.trace.Tracer) (lazy import; requires `foley[obs]`).

#### start_as_current_span(name, , kind=None, attributes=None)

Open a real OTel span, mapping `kind` to `SpanKind` and yielding a mirror.

### *class* foley.obs.trace.Span(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

The minimal mirror-span surface foley code calls (structural).

### *class* foley.obs.trace.Tracer(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

Starts mirror spans; the DI seam (default no-op, OTel-backed when present).

### foley.obs.trace.get_tracer(, prefer_otel=True)

Return the effective [`Tracer`](_autosummary/foley.obs.trace.html.md#foley.obs.trace.Tracer) (OTel-backed when available, else no-op).

* **Parameters:**
  **prefer_otel** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – When `True` (default) and `opentelemetry` is importable, return
  an [`OTelTracer`](_autosummary/foley.obs.trace.html.md#foley.obs.trace.OTelTracer) (itself a free no-op until the host configures an SDK);
  any construction failure falls back to the no-op. `False` forces the stdlib
  [`NoOpTracer`](_autosummary/foley.obs.trace.html.md#foley.obs.trace.NoOpTracer) — the hermetic-test lever (this dev env may have otel).
* **Return type:**
  [`Tracer`](_autosummary/foley.obs.trace.html.md#foley.obs.trace.Tracer)
* **Returns:**
  A [`Tracer`](_autosummary/foley.obs.trace.html.md#foley.obs.trace.Tracer) (never raises).


# _autosummary/foley.provenance.credits.html.md

# foley.provenance.credits

TASL attribution / credits generator — the provenance render layer (#9a).

Turns the sounds used in a foley run into a **credits block** (human-readable
`CREDITS.md`) and a **machine-readable JSON manifest**, honoring the Creative
Commons *TASL* recipe — \*\*T\*\*itle, \*\*A\*\*uthor, \*\*S\*\*ource, \*\*L\*\*icense — plus a
modification notice and a forward-compatible AI-disclosure line.

Design (report 07 · 10 §\`\`provenance/

```
``
```

):

* \*\*Reads the `LicenseRecord` SSOT; never re-derives license flags.\*\* Each
  credit is built purely from fields already on the record
  (`creator_name` / `source_url` / `license_id` / `requires_attribution` /
  `transformations` / `is_ai_generated` …) plus the display name + URL looked
  up from [`foley.licensing.license_meta()`](_autosummary/foley.licensing.html.md#foley.licensing.license_meta).
* **Never discard provenance.** *Every* sound is credited — including CC0 and
  user-owned ones that need no legal attribution — while the per-entry
  `requires_attribution` flag in the JSON manifest marks which credits are
  legally required.
* **Graceful degradation.** Every optional field has an ordered fallback (a
  missing caption synthesizes a title from tags; a missing creator falls back to
  the source), so a credit is always producible — even from a bare
  [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord).
* **Stdlib-only + deterministic.** No heavy deps and no timestamps, so
  `CREDITS.md` / `credits.json` are byte-stable and diffable. The AudioSeal
  watermark / C2PA writers + the EU AI Act Art. 50 checklist are the sibling
  `disclosure.py` (#9b, after generation); this module only *reads* those
  fields off the record and passes them through the manifest.

### Module Attributes

| [`CreditInput`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.CreditInput)   | a full record, a ranked candidate, or a bare rights record (each yields one credit entry).   |
|----------------------------------------------------------------|----------------------------------------------------------------------------------------------|

### Functions

| [`attribution_line`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.attribution_line)(source, \*[, fmt])          | Render one sound's TASL attribution line.                                                                     |
|-----------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------|
| [`credit_entry`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.credit_entry)(record, \*[, title])            | Build a [`CreditEntry`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.CreditEntry) from a record (title override optional). |
| [`credits_for`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.credits_for)(sounds, \*[, title, ...])        | Build the deduplicated [`Credits`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.Credits) for the sounds used in a run. |
| [`credits_manifest`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.credits_manifest)(credits)                    | The machine-readable manifest for `credits` — a JSON-native dict.                                             |
| [`render_credits_md`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.render_credits_md)(credits, \*[, title, ...]) | Render `credits` as a deterministic `CREDITS.md` document.                                                    |

### Classes

| [`CreditEntry`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.CreditEntry)(sound_id[, title, author, ...])   | One rendered TASL credit for a single sound (a flat, serializable row).                                         |
|------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------|
| [`Credits`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.Credits)([entries, title, schema_version])     | A deduplicated, ordered collection of [`CreditEntry`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.CreditEntry) for one run. |

### *class* foley.provenance.credits.CreditEntry(sound_id, title=None, author=None, author_url=None, source=None, source_url=None, license_id='unknown', license_name=None, license_url=None, modified=False, requires_attribution=False, attribution_text=None, notice_text_required=None, is_ai_generated=False, generator_model=None, disclosure_recommended=False, watermark=None, c2pa_manifest_ref=None)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

One rendered TASL credit for a single sound (a flat, serializable row).

Built by [`credit_entry()`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.credit_entry) from a record’s rights fields; rendered to a
single attribution line by [`attribution_line()`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.attribution_line). Carries the AI-disclosure

+ watermark / C2PA fields as pass-throughs so the JSON manifest becomes the
  content-credentials carrier once #6/#9b populate them (`None` today).

### foley.provenance.credits.CreditInput

a full record, a ranked candidate, or a bare
rights record (each yields one credit entry).

* **Type:**
  What the credits layer accepts

alias of [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord) | [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate) | [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)

### *class* foley.provenance.credits.Credits(entries=(), title='Credits', schema_version=1)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

A deduplicated, ordered collection of [`CreditEntry`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.CreditEntry) for one run.

Iterable and sized; renders to `CREDITS.md` via [`markdown`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.Credits.markdown) and to a
JSON manifest via [`manifest`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.Credits.manifest) (== `to_dict()`). Both are deterministic
(no timestamps) and diffable.

#### *property* manifest *: [dict](https://docs.python.org/3/builtins/stdtypes.html#dict)*

The machine-readable JSON manifest (a plain dict).

#### *property* markdown *: [str](https://docs.python.org/3/builtins/stdtypes.html#str)*

The rendered `CREDITS.md` document.

### foley.provenance.credits.attribution_line(source, , fmt='markdown')

Render one sound’s TASL attribution line.

A source-supplied `attribution_text` (if non-empty) is returned **verbatim**;
otherwise the line is synthesized from Title/Author/Source/License, with a
`(modified)` notice and an AI-disclosure segment appended as applicable.

* **Parameters:**
  * **source** (`Union`[[`CreditEntry`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.CreditEntry), [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord), [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate), [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)]) – A [`CreditEntry`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.CreditEntry), or any credit input (coerced first).
  * **fmt** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – `'markdown'` (hyperlinked list-item body) or `'plain'` (text with
    URLs in parentheses).
* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)
* **Returns:**
  The attribution line (no leading bullet / trailing newline).

### foley.provenance.credits.credit_entry(record, , title=None)

Build a [`CreditEntry`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.CreditEntry) from a record (title override optional).

Every field is read straight off the [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord) (flags
never re-derived); `modified` reflects a non-empty `transformations` list.

* **Parameters:**
  * **record** (`Union`[[`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord), [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate), [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)]) – A [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord), [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate),
    or [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord).
  * **title** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Explicit title override (else resolved from caption/tags/…).
* **Return type:**
  [`CreditEntry`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.CreditEntry)

### foley.provenance.credits.credits_for(sounds, , title='Credits', only_required=False, sort='appearance')

Build the deduplicated [`Credits`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.Credits) for the sounds used in a run.

* **Parameters:**
  * **sounds** ([`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[`Union`[[`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord), [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate), [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)]]) – An iterable of records / candidates / license records.
  * **title** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The credits heading (also carried in the manifest).
  * **only_required** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Keep only entries whose license *requires* attribution
    (drops CC0 / user-owned courtesy credits). Default `False` credits
    everything (never-discard-provenance).
  * **sort** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – `'appearance'` (default: first-seen order), `'author'`, or
    `'title'` (case-insensitive alpha).
* **Return type:**
  [`Credits`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.Credits)
* **Returns:**
  A [`Credits`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.Credits); identical sounds (same id) are credited once
  (first-writer-wins).

### foley.provenance.credits.credits_manifest(credits)

The machine-readable manifest for `credits` — a JSON-native dict.

`entries` is a plain `list` of per-credit dicts (not the `tuple` that
`dataclasses.asdict` would preserve), so the manifest equals
`json.loads(credits.to_json())` — the exact shape written to `credits.json`.
It carries the full field set, including the `requires_attribution` mark and
the `watermark` / `c2pa_manifest_ref` pass-throughs (`None` until #9b).

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### foley.provenance.credits.render_credits_md(credits, , title=None, heading_level=2)

Render `credits` as a deterministic `CREDITS.md` document.

* **Parameters:**
  * **credits** ([`Credits`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.Credits)) – The [`Credits`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.Credits) to render.
  * **title** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Heading override (default: `credits.title`).
  * **heading_level** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Markdown heading level for the title (default `2` → `##`).
* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)
* **Returns:**
  The Markdown document (bulleted attribution lines; a placeholder note when
  empty), ending in a single trailing newline.


# _autosummary/foley.provenance.disclosure.html.md

# foley.provenance.disclosure

Disclosure, watermarking & safety for AI-generated audio (#9b).

The sibling of [`foley.provenance.credits`](_autosummary/foley.provenance.credits.html.md#module-foley.provenance.credits): where `credits` renders TASL
attribution, this module makes a *generated* clip \*\*traceable, disclosed, and
safe\*\* for a published product (report 07 §6–7):

* **AudioSeal watermark** — an imperceptible, detectable mark embedded on every
  generated clip so downstream systems (and foley itself) can verify it was
  machine-made. Populates `LicenseRecord.watermark`. Meta AudioSeal is MIT
  (code + weights) but a **16 kHz mono speech** model used off-label on 44.1 kHz
  stereo SFX, so the per-clip mark is a **soft-binding** provenance signal (the
  achieved detection probability is recorded); C2PA is the hard-binding carrier.
* **Content Credential (C2PA)** — #9b writes a portable, self-asserted JSON
  “content credential” *sidecar* (a C2PA-shaped assertion dict: the “AI use”
  action, the training-mining opt-out, and the TASL/license) into a provenance
  store, and points `LicenseRecord.c2pa_manifest_ref` at it. A real
  *signed + embedded* C2PA manifest (via `c2pa-python`) over the final mix is a
  weave/export concern deferred to #8; [`build_content_credential()`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.build_content_credential) is the SSOT
  dict that step promotes verbatim.
* **EU AI Act Art. 50** — [`art50_checklist()`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.art50_checklist) is a pure reader over the
  `LicenseRecord` reporting which transparency obligations a clip has met vs.
  still pending (deadline **2 Aug 2026**).
* **Safety gates** — [`scan_prompt()`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.scan_prompt) matches a generation prompt against a
  registry of trademarked audio logos (THX, NBC chimes, Netflix Ta-dum, …) and
  recognizable-voice patterns. The generate façade *decides* on the result
  (fail-closed refuse by default, or warn-and-flag); this module only *detects*.

Layering: this module depends only on [`foley.base`](_autosummary/foley.base.html.md#module-foley.base) / [`foley.stores`](_autosummary/foley.stores.html.md#module-foley.stores)
(never on [`foley.sources`](_autosummary/foley.sources.html.md#module-foley.sources)). The safety-refusal exceptions live in
[`foley.sources.generate`](_autosummary/foley.sources.html.md#foley.sources.generate) (their `GenerationError` taxonomy home) and are
*raised there*; `disclosure` returns a [`PromptScan`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.PromptScan) and the caller
decides. The module top level is **stdlib-only** — `audioseal` / `torch` /
`torchaudio` are imported *inside* the watermark functions (the
`foley[provenance]` extra), so importing this module (and `import foley`) stays
dependency-light.

### Module Attributes

| [`DEFAULT_WATERMARK_MESSAGE`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.DEFAULT_WATERMARK_MESSAGE)      | The FIXED 16-bit foley provenance id embedded by AudioSeal.                                                 |
|---------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------|
| [`IPTC_TRAINED_ALGORITHMIC_MEDIA`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.IPTC_TRAINED_ALGORITHMIC_MEDIA) | IPTC digitalSourceType vocabulary (C2PA `c2pa.actions` assertion).                                          |
| [`TRADEMARK_REGISTRY`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.TRADEMARK_REGISTRY)             | Seed registry of branded audio logos foley must not knowingly generate for commercial use (report 07 §7.2). |

### Functions

| [`art50_checklist`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.art50_checklist)(record)                        | Return the per-clip EU AI Act Art.                                                                      |
|-------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------|
| [`build_content_credential`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.build_content_credential)(record, \*, asset_id) | Build the portable, self-asserted C2PA-shaped content-credential dict.                                  |
| [`detect_watermark`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.detect_watermark)(audio_bytes)                  | Detect a foley watermark in `audio_bytes` (lazy AudioSeal).                                             |
| [`resolve_watermarker`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.resolve_watermarker)(watermark, watermarker)    | Resolve the effective [`Watermarker`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.Watermarker) for a generate call. |
| [`scan_prompt`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.scan_prompt)(prompt)                            | Scan a generation `prompt` for trademarked-audio-logo + voice-clone risk.                               |
| [`write_content_credential`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.write_content_credential)(store, asset_id, ...) | Write `credential` into `store` keyed by `asset_id`; return `asset_id`.                                 |

### Classes

| [`AudioSealWatermarker`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.AudioSealWatermarker)(\*[, message])       | The default [`Watermarker`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.Watermarker) — Meta AudioSeal, run on CPU, deterministic.   |
|--------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------|
| [`PromptScan`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.PromptScan)([trademark_hits, voice_hits])  | The result of scanning a generation prompt for safety flags (pure).                                                     |
| [`TrademarkEntry`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.TrademarkEntry)(canonical, aliases)        | One trademarked audio logo + the casefolded prompt substrings that flag it.                                             |
| [`WatermarkResult`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.WatermarkResult)(audio_bytes, meta[, ...]) | The output of a [`Watermarker`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.Watermarker): the marked bytes + provenance meta.       |
| [`Watermarker`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.Watermarker)(\*args, \*\*kwargs)           | Embeds a detectable provenance watermark into audio bytes (the DI seam).                                                |

### Exceptions

| [`WatermarkUnavailable`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.WatermarkUnavailable)   | Raised when a watermark is explicitly requested but `foley[provenance]` is absent.   |
|-------------------------------------------------------------------------|--------------------------------------------------------------------------------------|

### *class* foley.provenance.disclosure.AudioSealWatermarker(, message=61470)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

The default [`Watermarker`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.Watermarker) — Meta AudioSeal, run on CPU, deterministic.

`audioseal` / `torch` / `torchaudio` are imported lazily inside the
methods (the `foley[provenance]` extra), so importing this class stays
dependency-light. Watermarking runs on **CPU** with a **fixed message** and
`eval()`/`no_grad` for cross-machine reproducibility (the dedup invariant).
Because AudioSeal is a 16 kHz mono model, 44.1 kHz stereo SFX is watermarked
per channel via a 16 kHz round-trip (lossy) — the mark is a soft-binding
signal and the achieved detection probability is recorded in the meta.

#### embed(audio_bytes, , message=None)

Embed the AudioSeal watermark; return the marked bytes + meta.

* **Parameters:**
  * **audio_bytes** ([`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes)) – The clip’s container bytes (WAV/FLAC/…).
  * **message** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`int`](https://docs.python.org/3/builtins/functions.html#int)]) – The 16-bit id to embed (default [`DEFAULT_WATERMARK_MESSAGE`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.DEFAULT_WATERMARK_MESSAGE)).
* **Return type:**
  [`WatermarkResult`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.WatermarkResult)
* **Returns:**
  A [`WatermarkResult`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.WatermarkResult) whose `audio_bytes` is lossless WAV
  (float32) — a deterministic PCM round-trip so its content-hash id is
  reproducible — and whose `meta` is the `LicenseRecord.watermark` dict.

#### *property* version *: [str](https://docs.python.org/3/builtins/stdtypes.html#str)*

The installed `audioseal` package version (best-effort).

### foley.provenance.disclosure.DEFAULT_WATERMARK_MESSAGE *: [int](https://docs.python.org/3/builtins/functions.html#int)* *= 61470*

The FIXED 16-bit foley provenance id embedded by AudioSeal. It MUST be constant
(no per-call nonce / timestamp): a deterministic watermark keeps the stored
content-hash id reproducible so a byte-identical regeneration still dedups
(`skipped_dup`) — the #6 generation-flywheel promise. See
[`AudioSealWatermarker.embed()`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.AudioSealWatermarker.embed).

### foley.provenance.disclosure.IPTC_TRAINED_ALGORITHMIC_MEDIA *= 'http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia'*

IPTC digitalSourceType vocabulary (C2PA `c2pa.actions` assertion).

### *class* foley.provenance.disclosure.PromptScan(trademark_hits=(), voice_hits=())

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

The result of scanning a generation prompt for safety flags (pure).

#### *property* contains_recognizable_voice *: [bool](https://docs.python.org/3/builtins/functions.html#bool)*

True if the prompt matched a recognizable-voice / clone pattern.

#### *property* flagged *: [bool](https://docs.python.org/3/builtins/functions.html#bool)*

True if the prompt matched any trademark or recognizable-voice pattern.

#### *property* potential_trademark *: [bool](https://docs.python.org/3/builtins/functions.html#bool)*

True if the prompt matched a branded-audio-logo entry.

### foley.provenance.disclosure.TRADEMARK_REGISTRY *: [tuple](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[TrademarkEntry](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.TrademarkEntry), ...]* *= (TrademarkEntry(canonical='THX Deep Note', aliases=frozenset({'deep note', 'thx'})), TrademarkEntry(canonical='NBC chimes', aliases=frozenset({'nbc chime', 'nbc three-note', 'nbc chimes'})), TrademarkEntry(canonical='Netflix Ta-dum', aliases=frozenset({'ta dum', 'netflix sound', 'tudum', 'netflix intro', 'netflix chime', 'ta-dum'})), TrademarkEntry(canonical='MGM lion roar', aliases=frozenset({'mgm roar', 'mgm lion', 'metro-goldwyn-mayer lion'})), TrademarkEntry(canonical='20th Century Fox fanfare', aliases=frozenset({'century fox intro', 'fox fanfare', '20th century fox fanfare'})), TrademarkEntry(canonical='Intel five-note bong', aliases=frozenset({'intel bong', 'intel chime', 'intel jingle', 'intel inside'})), TrademarkEntry(canonical="Homer Simpson D'oh", aliases=frozenset({"d'oh", 'homer doh', 'homer simpson doh'})))*

Seed registry of branded audio logos foley must not knowingly generate for
commercial use (report 07 §7.2). Each entry maps a canonical mark to a set of
casefolded trigger substrings. Extend freely — this is a best-effort aid, not a
legal guarantee.

### *class* foley.provenance.disclosure.TrademarkEntry(canonical, aliases)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

One trademarked audio logo + the casefolded prompt substrings that flag it.

### *class* foley.provenance.disclosure.WatermarkResult(audio_bytes, meta, detection_prob=None)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

The output of a [`Watermarker`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.Watermarker): the marked bytes + provenance meta.

### *exception* foley.provenance.disclosure.WatermarkUnavailable

Bases: [`RuntimeError`](https://docs.python.org/3/builtins/exceptions.html#RuntimeError)

Raised when a watermark is explicitly requested but `foley[provenance]` is absent.

### *class* foley.provenance.disclosure.Watermarker(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

Embeds a detectable provenance watermark into audio bytes (the DI seam).

The default is [`AudioSealWatermarker`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.AudioSealWatermarker); tests inject a deterministic
fake — exactly the `adapter=` / `pipeline=` seam the source adapters use.
`embed` MUST be deterministic for a fixed `message` (same input bytes →
same output bytes) so the stored content-hash id stays reproducible and the
generation-flywheel dedup keeps working.

#### embed(audio_bytes, , message=61470)

Return the watermarked bytes + a provenance meta dict.

* **Return type:**
  [`WatermarkResult`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.WatermarkResult)

### foley.provenance.disclosure.art50_checklist(record)

Return the per-clip EU AI Act Art. 50 transparency checklist (pure reader).

Reports, per obligation, whether it is `required` for this clip and whether it
has been `met` — so a caller can see what a publish still needs (e.g. the
machine-readable mark is *pending* when `foley[provenance]` was absent at
generation). A pure [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord) reader (flags never
re-derived), mirroring [`foley.provenance.credits`](_autosummary/foley.provenance.credits.html.md#module-foley.provenance.credits). The render-scoped
rollup over a whole mix is a weave/#8 concern.

* **Parameters:**
  **record** (`Union`[[`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord), [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate), [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)]) – A [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord), [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate),
  or [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord).
* **Returns:**
  {name: {required, met, detail}},
  “pending”: […], “publish_ready”: bool, “provenance”: {…}}\`\`.
* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### foley.provenance.disclosure.build_content_credential(record, , asset_id, asset_hash=None)

Build the portable, self-asserted C2PA-shaped content-credential dict.

A pure reader over the `LicenseRecord` SSOT (flags never re-derived), mirroring
[`foley.provenance.credits.credit_entry()`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.credit_entry)’s discipline. The `manifest`
sub-object is byte-for-byte the dict a future signer (`c2pa.Builder`, #8/weave)
promotes into a real signed + embedded manifest — so the credential shape can
never fork between the generate-time sidecar and the export-time manifest.

* **Parameters:**
  * **record** (`Union`[[`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord), [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate), [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)]) – A [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord), [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate),
    or [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord).
  * **asset_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The clip’s content-hash id (the sidecar store key / ref).
  * **asset_hash** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Optional `{"alg": "sha256", "value": <hex>}` of the asset bytes.
* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)
* **Returns:**
  A JSON-serializable content-credential dict (`signed`/`embedded` False —
  it is self-asserted until #8 signs it).

### foley.provenance.disclosure.detect_watermark(audio_bytes)

Detect a foley watermark in `audio_bytes` (lazy AudioSeal).

Downmixes to mono + resamples to 16 kHz, then runs the AudioSeal detector.

* **Parameters:**
  **audio_bytes** ([`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes)) – The clip’s container bytes.
* **Return type:**
  [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`float`](https://docs.python.org/3/builtins/functions.html#float), [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`int`](https://docs.python.org/3/builtins/functions.html#int)]]
* **Returns:**
  `(probability, recovered_message)` — `probability` in `[0, 1]` that a
  watermark is present, and the recovered 16-bit id (`None` if the
  probability is below 0.5).

### foley.provenance.disclosure.resolve_watermarker(watermark, watermarker)

Resolve the effective [`Watermarker`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.Watermarker) for a generate call.

Progressive disclosure: generation works with or without `foley[provenance]`.

* **Parameters:**
  * **watermark** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`bool`](https://docs.python.org/3/builtins/functions.html#bool)]) – `True` require a watermark (raise if unavailable), `False`
    never watermark, `None` (auto) watermark iff `audioseal` is installed.
  * **watermarker** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Watermarker`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.Watermarker)]) – An injected watermarker (the DI seam) — wins over auto-detect.
* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Watermarker`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.Watermarker)]
* **Returns:**
  A [`Watermarker`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.Watermarker), or `None` when watermarking is off/unavailable.
* **Raises:**
  [**WatermarkUnavailable**](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.WatermarkUnavailable) – If `watermark=True` but `audioseal` is not installed
      (and no `watermarker` was injected).

### foley.provenance.disclosure.scan_prompt(prompt)

Scan a generation `prompt` for trademarked-audio-logo + voice-clone risk.

Pure and stdlib-only (casefold substring + regex). Returns a
[`PromptScan`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.PromptScan); it never raises and makes no decision — the generate
façade decides (fail-closed refuse by default, or warn-and-flag).

* **Parameters:**
  **prompt** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The natural-language generation prompt.
* **Return type:**
  [`PromptScan`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.PromptScan)
* **Returns:**
  A [`PromptScan`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.PromptScan) naming the matched marks / voice patterns.

### foley.provenance.disclosure.write_content_credential(store, asset_id, credential)

Write `credential` into `store` keyed by `asset_id`; return `asset_id`.

`store` is any `MutableMapping[str, dict]` (default:
[`foley.stores.make_provenance_store()`](_autosummary/foley.stores.html.md#foley.stores.make_provenance_store), a local JSON-file store; swap for a
cloud `dol` store to move sidecars off-box).

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)


# _autosummary/foley.provenance.html.md

# foley.provenance

Provenance layer for foley — attribution/credits (and, later, disclosure).

This subpackage keeps published output legal and traceable. Its first half (#9a)
is [`foley.provenance.credits`](_autosummary/foley.provenance.credits.html.md#module-foley.provenance.credits) — the stdlib-only TASL attribution / credits
generator (`CREDITS.md` + JSON manifest) that reads the `LicenseRecord` SSOT.

Its second half (#9b) is [`foley.provenance.disclosure`](_autosummary/foley.provenance.disclosure.html.md#module-foley.provenance.disclosure) — AudioSeal
watermarking, the portable C2PA content-credential sidecar, the EU AI Act Art. 50
checklist, and the trademarked-audio / recognizable-voice safety scan. It is
intentionally NOT imported here so `import foley` stays dependency-light: its
module top level is stdlib-only, but `audioseal` / `torch` / `torchaudio`
(the `foley[provenance]` extra) are imported lazily inside its watermark
functions. Import it explicitly (`from foley.provenance import disclosure`) or
reach the pure helpers via `foley.art50_checklist` / `foley.scan_prompt`.

### Functions

| [`attribution_line`](_autosummary/foley.provenance.html.md#foley.provenance.attribution_line)(source, \*[, fmt])          | Render one sound's TASL attribution line.                                                                     |
|-----------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------|
| [`credit_entry`](_autosummary/foley.provenance.html.md#foley.provenance.credit_entry)(record, \*[, title])            | Build a [`CreditEntry`](_autosummary/foley.provenance.html.md#foley.provenance.CreditEntry) from a record (title override optional). |
| [`credits_for`](_autosummary/foley.provenance.html.md#foley.provenance.credits_for)(sounds, \*[, title, ...])        | Build the deduplicated [`Credits`](_autosummary/foley.provenance.html.md#foley.provenance.Credits) for the sounds used in a run. |
| [`credits_manifest`](_autosummary/foley.provenance.html.md#foley.provenance.credits_manifest)(credits)                    | The machine-readable manifest for `credits` — a JSON-native dict.                                             |
| [`render_credits_md`](_autosummary/foley.provenance.html.md#foley.provenance.render_credits_md)(credits, \*[, title, ...]) | Render `credits` as a deterministic `CREDITS.md` document.                                                    |

### Classes

| [`CreditEntry`](_autosummary/foley.provenance.html.md#foley.provenance.CreditEntry)(sound_id[, title, author, ...])   | One rendered TASL credit for a single sound (a flat, serializable row).                                         |
|------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------|
| [`Credits`](_autosummary/foley.provenance.html.md#foley.provenance.Credits)([entries, title, schema_version])     | A deduplicated, ordered collection of [`CreditEntry`](_autosummary/foley.provenance.html.md#foley.provenance.CreditEntry) for one run. |

### *class* foley.provenance.CreditEntry(sound_id, title=None, author=None, author_url=None, source=None, source_url=None, license_id='unknown', license_name=None, license_url=None, modified=False, requires_attribution=False, attribution_text=None, notice_text_required=None, is_ai_generated=False, generator_model=None, disclosure_recommended=False, watermark=None, c2pa_manifest_ref=None)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

One rendered TASL credit for a single sound (a flat, serializable row).

Built by [`credit_entry()`](_autosummary/foley.provenance.html.md#foley.provenance.credit_entry) from a record’s rights fields; rendered to a
single attribution line by [`attribution_line()`](_autosummary/foley.provenance.html.md#foley.provenance.attribution_line). Carries the AI-disclosure

+ watermark / C2PA fields as pass-throughs so the JSON manifest becomes the
  content-credentials carrier once #6/#9b populate them (`None` today).

### *class* foley.provenance.Credits(entries=(), title='Credits', schema_version=1)

Bases: [`SerializableMixin`](_autosummary/foley.base.html.md#foley.base.SerializableMixin)

A deduplicated, ordered collection of [`CreditEntry`](_autosummary/foley.provenance.html.md#foley.provenance.CreditEntry) for one run.

Iterable and sized; renders to `CREDITS.md` via [`markdown`](_autosummary/foley.provenance.html.md#foley.provenance.Credits.markdown) and to a
JSON manifest via [`manifest`](_autosummary/foley.provenance.html.md#foley.provenance.Credits.manifest) (== `to_dict()`). Both are deterministic
(no timestamps) and diffable.

#### *property* manifest *: [dict](https://docs.python.org/3/builtins/stdtypes.html#dict)*

The machine-readable JSON manifest (a plain dict).

#### *property* markdown *: [str](https://docs.python.org/3/builtins/stdtypes.html#str)*

The rendered `CREDITS.md` document.

### foley.provenance.attribution_line(source, , fmt='markdown')

Render one sound’s TASL attribution line.

A source-supplied `attribution_text` (if non-empty) is returned **verbatim**;
otherwise the line is synthesized from Title/Author/Source/License, with a
`(modified)` notice and an AI-disclosure segment appended as applicable.

* **Parameters:**
  * **source** (`Union`[[`CreditEntry`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.CreditEntry), [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord), [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate), [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)]) – A [`CreditEntry`](_autosummary/foley.provenance.html.md#foley.provenance.CreditEntry), or any credit input (coerced first).
  * **fmt** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – `'markdown'` (hyperlinked list-item body) or `'plain'` (text with
    URLs in parentheses).
* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)
* **Returns:**
  The attribution line (no leading bullet / trailing newline).

### foley.provenance.credit_entry(record, , title=None)

Build a [`CreditEntry`](_autosummary/foley.provenance.html.md#foley.provenance.CreditEntry) from a record (title override optional).

Every field is read straight off the [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord) (flags
never re-derived); `modified` reflects a non-empty `transformations` list.

* **Parameters:**
  * **record** (`Union`[[`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord), [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate), [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)]) – A [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord), [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate),
    or [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord).
  * **title** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Explicit title override (else resolved from caption/tags/…).
* **Return type:**
  [`CreditEntry`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.CreditEntry)

### foley.provenance.credits_for(sounds, , title='Credits', only_required=False, sort='appearance')

Build the deduplicated [`Credits`](_autosummary/foley.provenance.html.md#foley.provenance.Credits) for the sounds used in a run.

* **Parameters:**
  * **sounds** ([`Iterable`](https://docs.python.org/3/library/typing.html#typing.Iterable)[`Union`[[`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord), [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate), [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)]]) – An iterable of records / candidates / license records.
  * **title** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The credits heading (also carried in the manifest).
  * **only_required** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Keep only entries whose license *requires* attribution
    (drops CC0 / user-owned courtesy credits). Default `False` credits
    everything (never-discard-provenance).
  * **sort** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – `'appearance'` (default: first-seen order), `'author'`, or
    `'title'` (case-insensitive alpha).
* **Return type:**
  [`Credits`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.Credits)
* **Returns:**
  A [`Credits`](_autosummary/foley.provenance.html.md#foley.provenance.Credits); identical sounds (same id) are credited once
  (first-writer-wins).

### foley.provenance.credits_manifest(credits)

The machine-readable manifest for `credits` — a JSON-native dict.

`entries` is a plain `list` of per-credit dicts (not the `tuple` that
`dataclasses.asdict` would preserve), so the manifest equals
`json.loads(credits.to_json())` — the exact shape written to `credits.json`.
It carries the full field set, including the `requires_attribution` mark and
the `watermark` / `c2pa_manifest_ref` pass-throughs (`None` until #9b).

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### foley.provenance.render_credits_md(credits, , title=None, heading_level=2)

Render `credits` as a deterministic `CREDITS.md` document.

* **Parameters:**
  * **credits** ([`Credits`](_autosummary/foley.provenance.credits.html.md#foley.provenance.credits.Credits)) – The [`Credits`](_autosummary/foley.provenance.html.md#foley.provenance.Credits) to render.
  * **title** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Heading override (default: `credits.title`).
  * **heading_level** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Markdown heading level for the title (default `2` → `##`).
* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)
* **Returns:**
  The Markdown document (bulleted attribution lines; a placeholder note when
  empty), ending in a single trailing newline.

### Modules

| [`credits`](_autosummary/foley.provenance.credits.html.md#module-foley.provenance.credits)       | TASL attribution / credits generator — the provenance render layer (#9a).   |
|------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------|
| [`disclosure`](_autosummary/foley.provenance.disclosure.html.md#module-foley.provenance.disclosure) | Disclosure, watermarking & safety for AI-generated audio (#9b).             |


# _autosummary/foley.qc.html.md

# foley.qc

Tier-0 deterministic audio QC for foley (research report 08 §3).

Pure, per-clip range-checks over a floating-point waveform in `[-1, 1]`:
clipping, true-peak, DC offset, whole-clip silence, SNR, edge clicks, loudness
(LUFS), duration, and `NaN`/`Inf` sanity. Every threshold is an explicit
[`QCThresholds`](_autosummary/foley.qc.html.md#foley.qc.QCThresholds) field — there are **no inline policy literals** — so the
whole table is a single, overridable source of truth.

Zero-dependency import contract:
: This module imports **stdlib only** at the top level. `numpy` is
  lazy-imported inside each function body (like `foley.audio`), so
  `import foley.qc` succeeds on a bare install with no scientific stack.
  [`measure_lufs()`](_autosummary/foley.qc.html.md#foley.qc.measure_lufs) additionally lazy-imports `pyloudnorm` and returns
  `None` when it (or a measurable clip) is absent; everything else is
  numpy-only.

Waveform convention:
: `samples` is a float array shaped `(frames,)` (mono) or
  `(frames, channels)`. dBFS is `20·log10(|x|)` with full scale at 1.0.

Typical use (at ingest, a later phase):

```default
report = run_qc(working_array, sample_rate)
sound_record.qc = report.to_dict()   # becomes filterable metadata
```

### Module Attributes

| [`LUFS_MIN_BLOCK_S`](_autosummary/foley.qc.html.md#foley.qc.LUFS_MIN_BLOCK_S)      | BS.1770 integrated-loudness gating block = 400 ms; a clip shorter than one block cannot be measured (`pyloudnorm` raises), so [`measure_lufs()`](_autosummary/foley.qc.html.md#foley.qc.measure_lufs) returns `None` for it.   |
|------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`DEFAULT_QC_THRESHOLDS`](_autosummary/foley.qc.html.md#foley.qc.DEFAULT_QC_THRESHOLDS) | The shipped default thresholds; the default for every keyword below.                                                                                                                                                   |

### Functions

| [`dc_offset`](_autosummary/foley.qc.html.md#foley.qc.dc_offset)(samples)                              | Largest per-channel absolute DC offset, `max_c |mean_n x[n, c]|`.                                                                                                              |
|--------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`detect_clipping`](_autosummary/foley.qc.html.md#foley.qc.detect_clipping)(samples, \*[, full_scale, ...]) | Detect hard (flat-topped) clipping.                                                                                                                                            |
| [`duration_s`](_autosummary/foley.qc.html.md#foley.qc.duration_s)(samples, sample_rate)                | Clip duration in seconds: `frames / sample_rate`.                                                                                                                              |
| [`estimate_snr`](_autosummary/foley.qc.html.md#foley.qc.estimate_snr)(samples, sample_rate, \*[, ...])   | Estimate SNR in dB (advisory — a busy-street SFX legitimately scores low).                                                                                                     |
| [`has_nan_inf`](_autosummary/foley.qc.html.md#foley.qc.has_nan_inf)(samples)                            | Return `True` if any sample is `NaN` or `Inf` (corrupt-clip guard).                                                                                                            |
| [`is_silent`](_autosummary/foley.qc.html.md#foley.qc.is_silent)(samples, \*[, rms_floor_dbfs])        | Return `True` when whole-clip RMS falls below `rms_floor_dbfs`.                                                                                                                |
| [`measure_lufs`](_autosummary/foley.qc.html.md#foley.qc.measure_lufs)(samples, sample_rate, \*[, ...])   | Integrated loudness (LUFS, ITU-R BS.1770-4) via `pyloudnorm` (lazy).                                                                                                           |
| [`needs_edge_fade`](_autosummary/foley.qc.html.md#foley.qc.needs_edge_fade)(samples, \*[, rel_peak_dbfs])   | Return `True` when the first or last sample sits above `rel_peak_dbfs` relative to the clip peak — i.e. a nonzero boundary that clicks under narration and needs a short fade. |
| [`run_qc`](_autosummary/foley.qc.html.md#foley.qc.run_qc)(samples, sample_rate, \*[, thresholds])  | Run every Tier-0 check and fold the results into a [`QCReport`](_autosummary/foley.qc.html.md#foley.qc.QCReport).                                                                  |
| [`true_peak_dbtp`](_autosummary/foley.qc.html.md#foley.qc.true_peak_dbtp)(samples, sample_rate, \*[, ...]) | Inter-sample true-peak level in dBTP.                                                                                                                                          |

### Classes

| [`QCReport`](_autosummary/foley.qc.html.md#foley.qc.QCReport)(duration_s, sample_rate, channels, ...)   | Per-clip Tier-0 QC result.                                           |
|-----------------------------------------------------------------------------------------------------|----------------------------------------------------------------------|
| [`QCStatus`](_autosummary/foley.qc.html.md#foley.qc.QCStatus)(\*values)                                 | Overall verdict for a clip (subclasses `str` so it is JSON-safe).    |
| [`QCThresholds`](_autosummary/foley.qc.html.md#foley.qc.QCThresholds)([clip_full_scale, ...])               | All Tier-0 QC defaults (report 08 §3 table), each an explicit field. |

### foley.qc.DEFAULT_QC_THRESHOLDS *= QCThresholds(clip_full_scale=0.999, clip_min_run=3, clip_reject_ratio=0.0001, clip_reject_run=10, true_peak_max_dbtp=-1.0, true_peak_oversample=4, dc_offset_fail=0.01, dc_offset_warn=0.001, silence_rms_dbfs=-60.0, snr_clean_db=20.0, snr_quiet_percentile=10.0, snr_frame_s=0.025, snr_hop_s=0.01, edge_rel_peak_dbfs=-40.0, edge_fade_s=0.01, lufs_gate_floor=-70.0, lufs_outlier_lu=6.0, duration_min_s=0.1, deliver_min_sample_rate=44100)*

The shipped default thresholds; the default for every keyword below.

### foley.qc.LUFS_MIN_BLOCK_S *: [float](https://docs.python.org/3/builtins/functions.html#float)* *= 0.4*

BS.1770 integrated-loudness gating block = 400 ms; a clip shorter than one
block cannot be measured (`pyloudnorm` raises), so [`measure_lufs()`](_autosummary/foley.qc.html.md#foley.qc.measure_lufs)
returns `None` for it. This is a fixed property of the algorithm, not a
tunable QC policy threshold, hence a module constant rather than a field.

### *class* foley.qc.QCReport(duration_s, sample_rate, channels, clipped_ratio, clipped_max_run, dc_offset, rms_dbfs, is_silent, needs_edge_fade, has_nan_inf, true_peak_dbtp=None, snr_db=None, loudness_lufs=None, status=QCStatus.pass_, notes=<factory>)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Per-clip Tier-0 QC result.

Mirrors the fields the `SoundRecord` schema already carries
(`duration_s`, `sample_rate`, `channels`, `loudness_lufs`) plus the
deterministic check outputs, an overall `status`, and human-readable
`notes` for every firing condition. Serialize with [`to_dict()`](_autosummary/foley.qc.html.md#foley.qc.QCReport.to_dict) into
`SoundRecord.qc`.

#### to_dict()

Return a plain, JSON-safe dict (`status` as its string value).

Non-finite floats are swept to `None` at this boundary too (belt-and-
suspenders over the construction-time `_json_safe()` guard) so no
`Infinity`/`NaN` can ever reach `json.dumps` regardless of who set a
field.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### *class* foley.qc.QCStatus(\*values)

Bases: [`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Enum`](https://docs.python.org/3/library/enum.html#enum.Enum)

Overall verdict for a clip (subclasses `str` so it is JSON-safe).

### *class* foley.qc.QCThresholds(clip_full_scale=0.999, clip_min_run=3, clip_reject_ratio=0.0001, clip_reject_run=10, true_peak_max_dbtp=-1.0, true_peak_oversample=4, dc_offset_fail=0.01, dc_offset_warn=0.001, silence_rms_dbfs=-60.0, snr_clean_db=20.0, snr_quiet_percentile=10.0, snr_frame_s=0.025, snr_hop_s=0.01, edge_rel_peak_dbfs=-40.0, edge_fade_s=0.01, lufs_gate_floor=-70.0, lufs_outlier_lu=6.0, duration_min_s=0.1, deliver_min_sample_rate=44100)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

All Tier-0 QC defaults (report 08 §3 table), each an explicit field.

Grouped by check. Pass a customized instance to [`run_qc()`](_autosummary/foley.qc.html.md#foley.qc.run_qc) (or the
per-check keyword arguments) to override any threshold without editing code.

### foley.qc.dc_offset(samples)

Largest per-channel absolute DC offset, `max_c |mean_n x[n, c]|`.

* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

### foley.qc.detect_clipping(samples, , full_scale=0.999, min_run=3)

Detect hard (flat-topped) clipping.

A frame is “hot” when any channel reaches `|x| >= full_scale`. Only
maximal hot runs of length `>= min_run` count as clip events.

* **Parameters:**
  * **samples** (`ndarray`) – Waveform in `[-1, 1]`.
  * **full_scale** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Absolute level at/above which a sample is full-scale.
  * **min_run** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Minimum consecutive full-scale frames to count as clipping.
* **Return type:**
  [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`float`](https://docs.python.org/3/builtins/functions.html#float), [`int`](https://docs.python.org/3/builtins/functions.html#int)]
* **Returns:**
  `(clipped_ratio, max_run_length)` — the fraction of frames inside
  counting runs, and the longest counting run (`(0.0, 0)` if none).

### foley.qc.duration_s(samples, sample_rate)

Clip duration in seconds: `frames / sample_rate`.

* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

### foley.qc.estimate_snr(samples, sample_rate, , quiet_percentile=10.0, frame_s=0.025, hop_s=0.01)

Estimate SNR in dB (advisory — a busy-street SFX legitimately scores low).

The noise floor is the mean short-time RMS of the quietest
`quiet_percentile` percent of frames; the signal level is the whole-clip
RMS. A near-noise-free clip (quiet frames -> ~0) yields a very high value;
an exactly-zero floor returns `inf` and a silent clip returns `-inf`.

* **Parameters:**
  * **samples** (`ndarray`) – Waveform in `[-1, 1]` (down-mixed to mono internally).
  * **sample_rate** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Sample rate in Hz (sizes the frames).
  * **quiet_percentile** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Percent of quietest frames forming the noise floor.
  * **frame_s** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Short-time frame length in seconds.
  * **hop_s** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Hop between frames in seconds.
* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)
* **Returns:**
  SNR in dB.

### foley.qc.has_nan_inf(samples)

Return `True` if any sample is `NaN` or `Inf` (corrupt-clip guard).

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### foley.qc.is_silent(samples, , rms_floor_dbfs=-60.0)

Return `True` when whole-clip RMS falls below `rms_floor_dbfs`.

A zero (exactly silent) clip has RMS `0` -> `-inf` dBFS -> `True`.

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### foley.qc.measure_lufs(samples, sample_rate, , gate_floor_lufs=-70.0, min_block_s=0.4)

Integrated loudness (LUFS, ITU-R BS.1770-4) via `pyloudnorm` (lazy).

Returns `None` when `pyloudnorm` is unavailable, the clip is shorter than
one gating block (`min_block_s`), the samples are non-finite, or the
measured loudness is at/below the gate floor (near-silent / unstable — do
not amplify, just flag).

* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`float`](https://docs.python.org/3/builtins/functions.html#float)]

### foley.qc.needs_edge_fade(samples, , rel_peak_dbfs=-40.0)

Return `True` when the first or last sample sits above `rel_peak_dbfs`
relative to the clip peak — i.e. a nonzero boundary that clicks under
narration and needs a short fade.

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### foley.qc.run_qc(samples, sample_rate, , thresholds=QCThresholds(clip_full_scale=0.999, clip_min_run=3, clip_reject_ratio=0.0001, clip_reject_run=10, true_peak_max_dbtp=-1.0, true_peak_oversample=4, dc_offset_fail=0.01, dc_offset_warn=0.001, silence_rms_dbfs=-60.0, snr_clean_db=20.0, snr_quiet_percentile=10.0, snr_frame_s=0.025, snr_hop_s=0.01, edge_rel_peak_dbfs=-40.0, edge_fade_s=0.01, lufs_gate_floor=-70.0, lufs_outlier_lu=6.0, duration_min_s=0.1, deliver_min_sample_rate=44100))

Run every Tier-0 check and fold the results into a [`QCReport`](_autosummary/foley.qc.html.md#foley.qc.QCReport).

Status rules (evaluated in order):
: FAIL if `has_nan_inf` OR `is_silent` OR
  `clipped_max_run >= clip_reject_run` OR
  `clipped_ratio > clip_reject_ratio` OR `duration_s < duration_min_s`.
  WARN if `dc_offset > dc_offset_fail` OR `needs_edge_fade` OR
  (`snr_db` is a finite value `< snr_clean_db`) OR
  (`true_peak_dbtp` is a finite value `> true_peak_max_dbtp`).
  Otherwise PASS.

Each firing condition appends a human-readable string to `notes`. Two
thresholds are intentionally NOT evaluated on a single source clip here
because they belong to later stages: the library-median loudness-outlier
check (`+/- lufs_outlier_lu`, a library-level concern) and the delivery
sample-rate target (`deliver_min_sample_rate`, enforced at the weave/master
stage).

* **Parameters:**
  * **samples** (`ndarray`) – Waveform in `[-1, 1]` (mono or `(frames, channels)`).
  * **sample_rate** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Sample rate in Hz.
  * **thresholds** ([`QCThresholds`](_autosummary/foley.qc.html.md#foley.qc.QCThresholds)) – Overridable QC thresholds (defaults to shipped values).
* **Return type:**
  [`QCReport`](_autosummary/foley.qc.html.md#foley.qc.QCReport)
* **Returns:**
  A populated [`QCReport`](_autosummary/foley.qc.html.md#foley.qc.QCReport).

### foley.qc.true_peak_dbtp(samples, sample_rate, , oversample=4)

Inter-sample true-peak level in dBTP.

Each channel is band-limited-upsampled `oversample``x (numpy FFT), the
peak magnitude is taken across all channels, and converted to dBTP. Returns
``-inf` for a fully silent clip. `sample_rate` is accepted for interface
symmetry (FFT interpolation is rate-independent).

* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)


# _autosummary/foley.requirements.html.md

# foley.requirements

Onboarding — check what foley needs, tell the user how to get it (accompy-style, #12).

Generalizes WEAVE’s binary-only [`foley.weave.requirements`](_autosummary/foley.weave.requirements.html.md#module-foley.weave.requirements) into a single
capability audit across everything foley can optionally use: **system binaries**
(`ffmpeg` / `rubberband` — reused verbatim from the WEAVE SSOT), **API keys**
(derived from each source adapter’s declared `config['auth']` — one SSOT for the env
var + sign-up URL), and **importable extras** (`py2mcp` for the MCP server, the CLAP /
index / provenance stacks). A [`Requirement`](_autosummary/foley.weave.requirements.html.md#foley.weave.requirements.Requirement) carries a
`probe` discriminator (`binary` / `env` / `importable`) so one dispatch handles
all three. Nothing here runs an installer — it surfaces the exact per-platform command
or sign-up URL so the user (or agent) can opt in. Stdlib-only; keeps `import foley`
dol-only.

### Functions

| [`build_requirements`](_autosummary/foley.requirements.html.md#foley.requirements.build_requirements)()                     | Assemble the full requirement set: system binaries + API keys + importable extras.   |
|-------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------|
| [`capability_report`](_autosummary/foley.requirements.html.md#foley.requirements.capability_report)(\*[, runtime])         | A JSON-safe capability + posture snapshot for the CLI, docs, and the MCP tool.       |
| [`check_requirements`](_autosummary/foley.requirements.html.md#foley.requirements.check_requirements)(\*[, names, verbose]) | Report which optional foley capabilities are available (`{name: is_available}`).     |
| [`provider_key_env_vars`](_autosummary/foley.requirements.html.md#foley.requirements.provider_key_env_vars)()                  | Every env var that holds a provider credential (the sources' keys + Anthropic's).    |
| [`verify_and_setup`](_autosummary/foley.requirements.html.md#foley.requirements.verify_and_setup)(\*[, names])            | Return a per-requirement status + guidance report (never runs an installer).         |

### foley.requirements.build_requirements()

Assemble the full requirement set: system binaries + API keys + importable extras.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`Requirement`](_autosummary/foley.weave.requirements.html.md#foley.weave.requirements.Requirement)]

### foley.requirements.capability_report(, runtime=None)

A JSON-safe capability + posture snapshot for the CLI, docs, and the MCP tool.

Groups requirements into `keys` (env), `extras` (importable), `system`
(binary), adds the current offline posture and the available source list, and
lists `degraded_tools` — capabilities whose requirement is unmet.

* **Parameters:**
  **runtime** – A [`foley.runtime.RuntimeConfig`](_autosummary/foley.runtime.html.md#foley.runtime.RuntimeConfig) (default: the active one).
* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)
* **Returns:**
  `{keys, extras, system, offline, sources, llm, degraded_tools}` — all
  JSON-safe. `llm` is the provider the SELECT rungs resolve to right now
  (see [`foley.agent.llm.resolve_llm()`](_autosummary/foley.agent.llm.html.md#foley.agent.llm.resolve_llm)), or the reason it cannot resolve.

### foley.requirements.check_requirements(, names=None, verbose=False)

Report which optional foley capabilities are available (`{name: is_available}`).

* **Parameters:**
  * **names** ([`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`...`](https://docs.python.org/3/builtins/constants.html#Ellipsis)] | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Which requirements to check (default: the full assembled set).
  * **verbose** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – If `True`, print an actionable hint for each missing requirement.
* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`bool`](https://docs.python.org/3/builtins/functions.html#bool)]
* **Returns:**
  `{requirement_name: available}`. Everything-absent is fine — foley degrades
  (deterministic fakes, offline mode, in-process DSP); the report just shows what
  each capability would unlock.

### foley.requirements.provider_key_env_vars()

Every env var that holds a provider credential (the sources’ keys + Anthropic’s).

Derived from the same SSOT as [`check_requirements()`](_autosummary/foley.requirements.html.md#foley.requirements.check_requirements) (each source’s
`config['auth']`), so a newly added paid source is covered automatically — the
test suite scrubs exactly these so no test can spend money by accident.

* **Return type:**
  [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`...`](https://docs.python.org/3/builtins/constants.html#Ellipsis)]

### foley.requirements.verify_and_setup(, names=None)

Return a per-requirement status + guidance report (never runs an installer).

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]
* **Returns:**
  `{name: {'available', 'purpose', 'install', 'url', 'probe'}}`.


# _autosummary/foley.runtime.html.md

# foley.runtime

Runtime posture — local-first / offline mode as one verifiable contract (#12, report 12).

`RuntimeConfig` couples the three things “offline” must mean into a single frozen,
inspectable value, each a **consumer of an existing SSOT** (no parallel policy):

* **data egress** — `data_egress_allow` filters the source registry by each adapter’s
  already-declared `config['data_egress']` (`foley.sources` SSOT), so a network
  adapter is simply not available offline.
* **telemetry** — `telemetry=False` disables the observability run-artifact export
  (`foley.obs`), so nothing leaves the device.
* **redaction** — `redaction_mode` routes narration-derived fields through the ready
  `foley.obs.redact.REDACT_FIELDS` redactor, so prompts/queries/narration never sit
  in even a local run store.

[`offline()`](_autosummary/foley.runtime.html.md#foley.runtime.offline) (`offline_scope`) applies the posture for the duration of a `with`
block via a [`contextvars.ContextVar`](https://docs.python.org/3/library/contextvars.html#contextvars.ContextVar) and **restores** the prior obs state on
exit — per-call granularity, not a process-global flip. Stdlib-only, so importing this
keeps `import foley` dol-only.

### Module Attributes

| [`LOCAL`](_autosummary/foley.runtime.html.md#foley.runtime.LOCAL)   | The egress classes a source may declare (`config['data_egress']` SSOT).   |
|----------------------------------------------------------|---------------------------------------------------------------------------|

### Functions

| [`current_runtime`](_autosummary/foley.runtime.html.md#foley.runtime.current_runtime)()                                  | The active [`RuntimeConfig`](_autosummary/foley.runtime.html.md#foley.runtime.RuntimeConfig); outside any scope, the one `$FOLEY_OFFLINE` selects.   |
|-----------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|
| [`is_offline`](_autosummary/foley.runtime.html.md#foley.runtime.is_offline)()                                       | Whether an offline runtime scope is currently active.                                                                             |
| [`load_pretrained`](_autosummary/foley.runtime.html.md#foley.runtime.load_pretrained)(loader, model_id, \*[, ...])       | Call `loader(model_id, **kwargs)` — a `from_pretrained` — honouring the posture.                                                  |
| [`no_download`](_autosummary/foley.runtime.html.md#foley.runtime.no_download)(what, \*, how_to_fetch)                | Under an offline posture, forbid outbound connections in this context for the block.                                              |
| [`offline`](_autosummary/foley.runtime.html.md#foley.runtime.offline)([config])                                  | Alias of [`offline_scope()`](_autosummary/foley.runtime.html.md#foley.runtime.offline_scope) — `with foley.offline(): ...` for local-first runs.     |
| [`offline_scope`](_autosummary/foley.runtime.html.md#foley.runtime.offline_scope)([config])                            | Apply a [`RuntimeConfig`](_autosummary/foley.runtime.html.md#foley.runtime.RuntimeConfig) for the `with` block, restoring obs state on exit.         |
| [`require_egress`](_autosummary/foley.runtime.html.md#foley.runtime.require_egress)(data_egress, \*, what)              | Raise [`EgressBlocked`](_autosummary/foley.runtime.html.md#foley.runtime.EgressBlocked) unless the active runtime allows `data_egress`.              |
| [`require_local_files`](_autosummary/foley.runtime.html.md#foley.runtime.require_local_files)(paths, \*, what, how_to_fetch) | Under an offline posture, raise [`ModelNotCached`](_autosummary/foley.runtime.html.md#foley.runtime.ModelNotCached) unless every path exists.         |
| [`runtime_scope`](_autosummary/foley.runtime.html.md#foley.runtime.runtime_scope)(config)                              | Make `config` the current posture for the block — the ContextVar only.                                                            |

### Classes

| [`RuntimeConfig`](_autosummary/foley.runtime.html.md#foley.runtime.RuntimeConfig)([offline, data_egress_allow, ...])   | A frozen runtime posture — the local-first / offline contract as data.   |
|-----------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------|

### Exceptions

| [`EgressBlocked`](_autosummary/foley.runtime.html.md#foley.runtime.EgressBlocked)   | Raised when a call would send data off the device under an offline posture.     |
|------------------------------------------------------------------|---------------------------------------------------------------------------------|
| [`ModelNotCached`](_autosummary/foley.runtime.html.md#foley.runtime.ModelNotCached)  | Raised under an offline posture when a model's weights are not on this machine. |

### *exception* foley.runtime.EgressBlocked

Bases: [`PermissionError`](https://docs.python.org/3/builtins/exceptions.html#PermissionError)

Raised when a call would send data off the device under an offline posture.

Every external path checks the active [`RuntimeConfig`](_autosummary/foley.runtime.html.md#foley.runtime.RuntimeConfig) through
[`require_egress()`](_autosummary/foley.runtime.html.md#foley.runtime.require_egress) — the source registry, the generate and pull façades, and
the LLM resolver — so `with foley.offline():` holds on every surface, not only
in the MCP tools.

### foley.runtime.LOCAL *: [str](https://docs.python.org/3/builtins/stdtypes.html#str)* *= 'local'*

The egress classes a source may declare (`config['data_egress']` SSOT).

### *exception* foley.runtime.ModelNotCached

Bases: [`EgressBlocked`](_autosummary/foley.runtime.html.md#foley.runtime.EgressBlocked)

Raised under an offline posture when a model’s weights are not on this machine.

foley never downloads weights inside [`offline()`](_autosummary/foley.runtime.html.md#foley.runtime.offline) (#86); the message names the
model and how to fetch it beforehand, online.

### *class* foley.runtime.RuntimeConfig(offline=False, data_egress_allow=<factory>, telemetry=True, redaction_mode='hash', http_resilience=True)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

A frozen runtime posture — the local-first / offline contract as data.

* **Parameters:**
  * **offline** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Whether this posture is offline/local-first.
  * **data_egress_allow** ([`frozenset`](https://docs.python.org/3/builtins/stdtypes.html#frozenset)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – The egress classes a source may use to be available
    (`{'local'}` offline; `{'local','external'}` online).
  * **telemetry** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Whether the obs run-artifact export is on.
  * **redaction_mode** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – `'hash'` (default, salted), `'off'` (drop), or `'full'`
    (raw — local-debug only).
  * **http_resilience** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Whether HTTP source adapters are wrapped with the
    throttle/backoff/circuit-breaker ([`foley.sources.resilience`](_autosummary/foley.sources.resilience.html.md#module-foley.sources.resilience)).

#### allows(data_egress)

Whether a source declaring `data_egress` is available under this posture.

An unknown/absent declaration is **rejected** (fail-closed): a source that does
not say where its data goes is never used offline.

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

#### *classmethod* default()

The online default: all egress allowed, telemetry on, hashed redaction.

* **Return type:**
  [`RuntimeConfig`](_autosummary/foley.runtime.html.md#foley.runtime.RuntimeConfig)

#### *classmethod* from_env()

Build from the environment: `FOLEY_OFFLINE` in {1,true,yes} → offline-local.

* **Return type:**
  [`RuntimeConfig`](_autosummary/foley.runtime.html.md#foley.runtime.RuntimeConfig)

#### *classmethod* offline_local()

The local-first offline posture: local-only egress, telemetry off, hashed redaction.

* **Return type:**
  [`RuntimeConfig`](_autosummary/foley.runtime.html.md#foley.runtime.RuntimeConfig)

### foley.runtime.current_runtime()

The active [`RuntimeConfig`](_autosummary/foley.runtime.html.md#foley.runtime.RuntimeConfig); outside any scope, the one `$FOLEY_OFFLINE` selects.

* **Return type:**
  [`RuntimeConfig`](_autosummary/foley.runtime.html.md#foley.runtime.RuntimeConfig)

### foley.runtime.is_offline()

Whether an offline runtime scope is currently active.

* **Return type:**
  [`bool`](https://docs.python.org/3/builtins/functions.html#bool)

### foley.runtime.load_pretrained(loader, model_id, , how_to_fetch=None, \*\*kwargs)

Call `loader(model_id, **kwargs)` — a `from_pretrained` — honouring the posture.

Online, it is a plain call. Under [`offline()`](_autosummary/foley.runtime.html.md#foley.runtime.offline) it passes
`local_files_only=True` (no Hub request at all) and turns a cache miss (an
`OSError`) into [`ModelNotCached`](_autosummary/foley.runtime.html.md#foley.runtime.ModelNotCached); any other error passes through as is.
foley’s `from_pretrained` loads (CLAP, Stable Audio) go through here; loaders
with no local-only switch (AudioSeal, whisperX) go through [`no_download()`](_autosummary/foley.runtime.html.md#foley.runtime.no_download).

* **Parameters:**
  * **loader** – e.g. `ClapModel.from_pretrained`.
  * **model_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The Hub repo id.
  * **how_to_fetch** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – The pre-download instruction for the error (default: the
    `huggingface-cli download` command).
  * **\*\*kwargs** – Passed to `loader`.

### foley.runtime.no_download(what, , how_to_fetch)

Under an offline posture, forbid outbound connections in this context for the block.

For model loaders that have no local-only switch (AudioSeal, whisperX): inside the
block a non-loopback connection or name lookup made from this context is refused,
and an error whose cause is that refusal is re-raised as [`ModelNotCached`](_autosummary/foley.runtime.html.md#foley.runtime.ModelNotCached)
naming `what` and `how_to_fetch`. A cached model still loads (the libraries fall
back to their cache when the network is refused); an unrelated error passes through
unchanged. Online it does nothing. Threads a loader starts itself do not inherit
the block (they start with a fresh context); the loaders guarded here make their
first request from the calling thread.

### foley.runtime.offline(config=None)

Alias of [`offline_scope()`](_autosummary/foley.runtime.html.md#foley.runtime.offline_scope) — `with foley.offline(): ...` for local-first runs.

### foley.runtime.offline_scope(config=None)

Apply a [`RuntimeConfig`](_autosummary/foley.runtime.html.md#foley.runtime.RuntimeConfig) for the `with` block, restoring obs state on exit.

Defaults to [`RuntimeConfig.offline_local()`](_autosummary/foley.runtime.html.md#foley.runtime.RuntimeConfig.offline_local). Disables telemetry export and sets
the redaction mode for the scope; the prior obs enabled-state and redaction mode are
captured on entry and restored on exit (so a scope never leaks its posture).

* **Parameters:**
  **config** ([`RuntimeConfig`](_autosummary/foley.runtime.html.md#foley.runtime.RuntimeConfig) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – The posture to apply (default: offline-local).
* **Yields:**
  The applied [`RuntimeConfig`](_autosummary/foley.runtime.html.md#foley.runtime.RuntimeConfig).

### foley.runtime.require_egress(data_egress, , what)

Raise [`EgressBlocked`](_autosummary/foley.runtime.html.md#foley.runtime.EgressBlocked) unless the active runtime allows `data_egress`.

* **Parameters:**
  * **data_egress** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – The egress class the call needs (`'local'` | `'external'`);
    `None` (undeclared) is always refused — callers that read a source’s
    declaration map a missing one to `'external'` first.
  * **what** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – A short description for the error (`"source 'elevenlabs'"`).
* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### foley.runtime.require_local_files(paths, , what, how_to_fetch, min_bytes=None)

Under an offline posture, raise [`ModelNotCached`](_autosummary/foley.runtime.html.md#foley.runtime.ModelNotCached) unless every path exists.

`min_bytes` maps a path to the size below which the library would re-download it
(a truncated download), so such a file counts as missing too.

For model files a library downloads itself (PANNs fetches its checkpoint and label
CSV with `wget`, even at import), checked before that library is touched.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### foley.runtime.runtime_scope(config)

Make `config` the current posture for the block — the ContextVar only.

Unlike [`offline_scope()`](_autosummary/foley.runtime.html.md#foley.runtime.offline_scope) it leaves the process-wide obs settings alone (safe to
enter from many threads at once); telemetry still follows the posture, because
[`foley.obs.is_enabled()`](_autosummary/foley.obs.html.md#foley.obs.is_enabled) reads [`current_runtime()`](_autosummary/foley.runtime.html.md#foley.runtime.current_runtime).


# _autosummary/foley.sources.base.html.md

# foley.sources.base

The bulk-corpus source contract — a downloaded corpus as an ingestable stream.

foley’s SOURCE stage has two shapes. This module defines the **bulk-corpus**
shape used by `foley.bootstrap.bootstrap()` to seed the library from corpora
the user has already downloaded to local disk (FSD50K, Clotho, FoleySet, …). It
is deliberately narrower than the live/HTTP `SourceAdapter` (search / get /
download / generate) that subtask #5 introduces: a bulk corpus only has to
*enumerate its clips* and *say what each clip’s license is*. Everything else —
decode, QC, tag, embed, dedup, the by-value/by-reference storage gate — is reused
verbatim from [`foley.index.ingest.ingest_one()`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.ingest_one); adapters never touch the
library.

The two responsibilities of an adapter:

> * [`CorpusAdapter.iter_clips()`](_autosummary/foley.sources.base.html.md#foley.sources.base.CorpusAdapter.iter_clips) — yield a [`ClipSpec`](_autosummary/foley.sources.base.html.md#foley.sources.base.ClipSpec) per audio file,
> * [`CorpusAdapter.resolve_license()`](_autosummary/foley.sources.base.html.md#foley.sources.base.CorpusAdapter.resolve_license) — map a clip’s corpus metadata to a
>   fully-derived [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord) (via the licensing SSOT,
>   never hand-set flags), setting `rights_verified` True **only** for an
>   authoritatively-recognized license (fail-closed otherwise).

Concrete adapters register themselves in [`CORPUS_REGISTRY`](_autosummary/foley.sources.base.html.md#foley.sources.base.CORPUS_REGISTRY) via
[`register_corpus()`](_autosummary/foley.sources.base.html.md#foley.sources.base.register_corpus); [`corpora_in_rings()`](_autosummary/foley.sources.base.html.md#foley.sources.base.corpora_in_rings) / [`select_corpora()`](_autosummary/foley.sources.base.html.md#foley.sources.base.select_corpora) drive
the ring policy in the bootstrap orchestrator. The registry is a plain dict — no
auto-discovery / `SOURCE_CONFIG` (that is #5’s concern).

This module is also the home of the **live-source contracts** (auto-discovered by
[`foley.sources.registry`](_autosummary/foley.sources.registry.html.md#module-foley.sources.registry), not registered here): the retrieve
[`SourceAdapter`](_autosummary/foley.sources.base.html.md#foley.sources.base.SourceAdapter) (`search` / `get` / `download` — Freesound, #5) and
its sibling generate [`GenerateAdapter`](_autosummary/foley.sources.base.html.md#foley.sources.base.GenerateAdapter) (`generate` → [`GeneratedClip`](_autosummary/foley.sources.base.html.md#foley.sources.base.GeneratedClip)
— Stable Audio Open, ElevenLabs, #6). All three adapter kinds share the license
SSOT builders — [`bulk_license()`](_autosummary/foley.sources.base.html.md#foley.sources.base.bulk_license) (bulk), [`api_license()`](_autosummary/foley.sources.base.html.md#foley.sources.base.api_license) (retrieve), and
[`generated_license()`](_autosummary/foley.sources.base.html.md#foley.sources.base.generated_license) (generate) — thin wrappers over `_build_license()`
so the derived permission flags stay single-sourced.

### Module Attributes

| [`CORPUS_REGISTRY`](_autosummary/foley.sources.base.html.md#foley.sources.base.CORPUS_REGISTRY)   | Registry of concrete bulk-corpus adapters, keyed by `adapter.name`.   |
|--------------------------------------------------------------------|-----------------------------------------------------------------------|

### Functions

| [`check_prompt_influence`](_autosummary/foley.sources.base.html.md#foley.sources.base.check_prompt_influence)(value)                     | Raise `ValueError` unless `value` is `None` or a real number in `0..1`.                                                                            |
|----------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------|
| [`native_adherence`](_autosummary/foley.sources.base.html.md#foley.sources.base.native_adherence)(config, prompt_influence, notes) | Map foley's unified `prompt_influence` (0..1) onto a source's native scale.                                                                        |
| [`resolve_source_key`](_autosummary/foley.sources.base.html.md#foley.sources.base.resolve_source_key)(config[, api_key])             | A source's API token: `api_key`, else a bound key, else its env var.                                                                               |
| [`using_source_accounts`](_autosummary/foley.sources.base.html.md#foley.sources.base.using_source_accounts)(\*\*accounts)               | Bind per-request source accounts (a key, and what goes with it) for a `with` block.                                                                |
| [`bound_source_account`](_autosummary/foley.sources.base.html.md#foley.sources.base.bound_source_account)(source)                      | The account bound for `source` in this context, or `None` when no key is bound.                                                                    |
| [`key_fingerprint`](_autosummary/foley.sources.base.html.md#foley.sources.base.key_fingerprint)(key)                              | A short, one-way id for an API key: tells accounts apart without keeping the key.                                                                  |
| [`source_account_id`](_autosummary/foley.sources.base.html.md#foley.sources.base.source_account_id)(config[, api_key])              | Which account a call to the source is made on: `'operator'`, or a key's fingerprint.                                                               |
| [`bulk_license`](_autosummary/foley.sources.base.html.md#foley.sources.base.bulk_license)(\*, source, license_id, ...[, ...])  | Build a bulk-acquisition [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord), flags derived.                   |
| [`per_clip_license_meta`](_autosummary/foley.sources.base.html.md#foley.sources.base.per_clip_license_meta)(license_url, \*[, ...])     | The `ClipSpec.meta` rights fields for a clip that carries its own CC licence.                                                                      |
| [`license_from_clip_meta`](_autosummary/foley.sources.base.html.md#foley.sources.base.license_from_clip_meta)(source, spec)              | Build a clip's rights record from [`per_clip_license_meta()`](_autosummary/foley.sources.base.html.md#foley.sources.base.per_clip_license_meta) fields (fail-closed).                   |
| [`api_license`](_autosummary/foley.sources.base.html.md#foley.sources.base.api_license)(\*, source, license_id, ...[, ...])   | Build an API-acquisition [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord), flags derived.                   |
| [`generated_license`](_autosummary/foley.sources.base.html.md#foley.sources.base.generated_license)(\*, source, license_id, ...)    | Build an AI-generated [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord), flags derived + provenance stamped. |
| [`register_corpus`](_autosummary/foley.sources.base.html.md#foley.sources.base.register_corpus)(adapter)                          | Register `adapter` in [`CORPUS_REGISTRY`](_autosummary/foley.sources.base.html.md#foley.sources.base.CORPUS_REGISTRY) (idempotent) and return it.                                 |
| [`ring_of`](_autosummary/foley.sources.base.html.md#foley.sources.base.ring_of)(name)                                     | Return the ring of the registered corpus `name` (raises `KeyError`).                                                                               |
| [`corpora_in_rings`](_autosummary/foley.sources.base.html.md#foley.sources.base.corpora_in_rings)(rings)                           | Registered adapters whose ring is in `rings` (sorted by name).                                                                                     |
| [`select_corpora`](_autosummary/foley.sources.base.html.md#foley.sources.base.select_corpora)(\*[, rings, corpora])              | Resolve the adapters a bootstrap run should touch.                                                                                                 |

### Classes

| [`ClipSpec`](_autosummary/foley.sources.base.html.md#foley.sources.base.ClipSpec)(path, source_id[, meta])              | One clip inside a local bulk corpus, described but not yet ingested.            |
|-------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------|
| [`CorpusAdapter`](_autosummary/foley.sources.base.html.md#foley.sources.base.CorpusAdapter)(\*args, \*\*kwargs)              | A downloaded bulk corpus presented as an ingestable stream of clips.            |
| [`SourceAdapter`](_autosummary/foley.sources.base.html.md#foley.sources.base.SourceAdapter)(\*args, \*\*kwargs)              | The live/HTTP source contract (report 10 §4.2): `search` + `get` + `download`.  |
| [`GenerateAdapter`](_autosummary/foley.sources.base.html.md#foley.sources.base.GenerateAdapter)(\*args, \*\*kwargs)            | The generation source contract (report 10 §4.2) — a SIBLING of `SourceAdapter`. |
| [`GeneratedClip`](_autosummary/foley.sources.base.html.md#foley.sources.base.GeneratedClip)(audio_bytes, candidate[, notes]) | One freshly-generated sound: its transient bytes + a provisional candidate.     |
| [`UniformCorpus`](_autosummary/foley.sources.base.html.md#foley.sources.base.UniformCorpus)(name, ring, ...[, ...])          | A bulk corpus where **every** clip carries the same license.                    |

### Exceptions

| [`SourceConfigurationError`](_autosummary/foley.sources.base.html.md#foley.sources.base.SourceConfigurationError)   | A source cannot run until the user configures it: a missing key, plan or endpoint.   |
|-----------------------------------------------------------------------------|--------------------------------------------------------------------------------------|

### foley.sources.base.CORPUS_REGISTRY *: [dict](https://docs.python.org/3/builtins/stdtypes.html#dict)[[str](https://docs.python.org/3/builtins/stdtypes.html#str), [CorpusAdapter](_autosummary/foley.sources.base.html.md#foley.sources.base.CorpusAdapter)]* *= {'bbc_remarc': UniformCorpus(name='bbc_remarc', ring=2, default_license_id='RemArc', source='bbc_remarc', rights_verified=True, tag_hints_from_path=False), 'clotho': ClothoEvalCorpus(name='clotho', ring=0, default_license_id='CC-BY-4.0', source='clotho', rights_verified=True, tag_hints_from_path=False, include_captions=False), 'foleyset': UniformCorpus(name='foleyset', ring=0, default_license_id='CC-BY-4.0', source='foleyset', rights_verified=True, tag_hints_from_path=True), 'fsd50k': <foley.sources.fsd50k.Fsd50kCorpus object>, 'sonniss': UniformCorpus(name='sonniss', ring=2, default_license_id='Sonniss-GDC', source='sonniss', rights_verified=True, tag_hints_from_path=False)}*

Registry of concrete bulk-corpus adapters, keyed by `adapter.name`.

### *class* foley.sources.base.ClipSpec(path, source_id, meta=<factory>)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

One clip inside a local bulk corpus, described but not yet ingested.

#### path

Absolute path to the audio file on disk.

#### source_id

The corpus-native id (e.g. an FSD50K `fname`); used only for
reporting/provenance — the stored `SoundRecord.id` is still the
content hash minted by the ingest pipeline.

#### meta

Free-form per-clip metadata the adapter carries to
[`CorpusAdapter.resolve_license()`](_autosummary/foley.sources.base.html.md#foley.sources.base.CorpusAdapter.resolve_license) and (optionally) to caption/tag
hints — e.g. `{"license_id", "creator_name", "source_url",
"caption", "tag_hints"}`.

### *class* foley.sources.base.CorpusAdapter(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

A downloaded bulk corpus presented as an ingestable stream of clips.

Implementations are plain objects (usually a small `@dataclass`) carrying
three class-level facts (`name` / `ring` / `default_license_id`) and the
two methods below. They perform **no** embedding, storage, or library access.

#### corpus_dir(data_dir)

The on-disk root for this corpus under `data_dir` (`data_dir/name`).

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

#### default_license_id *: [str](https://docs.python.org/3/builtins/stdtypes.html#str)*

The corpus compilation license id (a per-clip license may still override).

#### iter_clips(root)

Yield a [`ClipSpec`](_autosummary/foley.sources.base.html.md#foley.sources.base.ClipSpec) for every ingestable clip under `root`.

* **Return type:**
  [`Iterator`](https://docs.python.org/3/library/typing.html#typing.Iterator)[[`ClipSpec`](_autosummary/foley.sources.base.html.md#foley.sources.base.ClipSpec)]

#### name *: [str](https://docs.python.org/3/builtins/stdtypes.html#str)*

Registry key / CLI name — `'fsd50k'` | `'clotho'` | `'foleyset'` | …

#### resolve_license(spec)

Return the fully-derived rights record for `spec` (licensing SSOT).

* **Return type:**
  [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)

#### ring *: [int](https://docs.python.org/3/builtins/functions.html#int)*

`0` ship-in-repo, `1` fetch, `2` opt-in/quarantined.

* **Type:**
  Bootstrap ring

### *class* foley.sources.base.GenerateAdapter(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

The generation source contract (report 10 §4.2) — a SIBLING of `SourceAdapter`.

A **generate** adapter (Stable Audio Open local, ElevenLabs Sound Effects
hosted; #6) synthesizes a sound from a prompt. It is a deliberate sibling of
the retrieve [`SourceAdapter`](_autosummary/foley.sources.base.html.md#foley.sources.base.SourceAdapter) rather than an extra method on it, so that
`SourceAdapter` stays `runtime_checkable` for the retrieve trio
(`search` / `get` / `download`) alone.

Like a retrieve adapter, a generate adapter performs **no** storage or library
access: it maps foley’s unified [`GENERATION_AFFORDANCES`](_autosummary/foley.base.html.md#foley.base.GENERATION_AFFORDANCES)
(prompt, duration, prompt_influence, negative_prompt, steps, seed, loop,
output_format) to its backend’s native params (via `SOURCE_CONFIG['param_map']`,
warning-and-dropping unsupported ones), builds the audio + a generated
[`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord), and returns a [`GeneratedClip`](_autosummary/foley.sources.base.html.md#foley.sources.base.GeneratedClip). The
`foley.sources.generate.generate()` façade converges it on the shared
`ingest_one` pipeline (by-value, operator-consented).

#### generate(prompt, \*\*affordances)

Synthesize a sound for `prompt`; return its bytes + provisional candidate.

* **Return type:**
  [`GeneratedClip`](_autosummary/foley.sources.base.html.md#foley.sources.base.GeneratedClip)

#### name *: [str](https://docs.python.org/3/builtins/stdtypes.html#str)*

Registry / façade key — `'stable_audio'` | `'elevenlabs'` | …

### *class* foley.sources.base.GeneratedClip(audio_bytes, candidate, notes=<factory>)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

One freshly-generated sound: its transient bytes + a provisional candidate.

The return type of a [`GenerateAdapter`](_autosummary/foley.sources.base.html.md#foley.sources.base.GenerateAdapter)’s `generate` — the envelope
that reconciles report 10 §4.2 (`generate -> Candidate`: retrieval and
generation return the same shape) with foley’s two non-negotiables:

* **adapters never touch storage** — so the bytes ride *with* the candidate in
  an explicit field rather than being written anywhere, and
* **\`\`ingest_one\`\` is never forked** — the `foley.sources.generate.generate()`
  façade hands [`audio_bytes`](_autosummary/foley.sources.base.html.md#foley.sources.base.GeneratedClip.audio_bytes) to the one shared pipeline (by-value), exactly
  as [`foley.sources.pull.add_from()`](_autosummary/foley.sources.pull.html.md#foley.sources.pull.add_from) does for a retrieved download.

Generation is retrieval’s `search -> Candidate` and `download -> bytes`
*fused* into one call, because there is no server to re-fetch the bytes from.

Provisional/canonical split — on [`candidate`](_autosummary/foley.sources.base.html.md#foley.sources.base.GeneratedClip.candidate)`.sound` only
`license` / `caption` / `tags` are authoritative. `id` (a discarded
`"<source>:pending"` placeholder), `uri`, `content_sha256`,
`storage_mode`, `qc`, `duration_s`, `sample_rate`, `channels` and the
`embedding_*` fields are all minted downstream by
[`ingest_one()`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.ingest_one) (the façade passes `sound_id=None` so
the stored id is the decoded-PCM content hash). [`audio_bytes`](_autosummary/foley.sources.base.html.md#foley.sources.base.GeneratedClip.audio_bytes) lives ONLY
here and is never serialized — a `SoundRecord` holds a `uri`, never bytes.

#### audio_bytes

The generated audio as encoded container bytes (WAV/FLAC/…),
transient and in-memory only — consumed once by the ingest pipeline.

#### candidate

The report-10 [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)
(`origin=CandidateOrigin.generated`) carrying the authoritative
[`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord) (`is_ai_generated=True` + the
generation-provenance block) plus the prompt caption + seed tags.

#### notes

Generation-time messages (`on_unsupported_param='warn'` drops,
output-format fallbacks, …). The
`foley.sources.generate.generate()` façade folds these into the
stored [`IngestResult`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult)‘s `notes` so they
surface in the run report.

### *class* foley.sources.base.SourceAdapter(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

The live/HTTP source contract (report 10 §4.2): `search` + `get` + `download`.

A **retrieve** adapter (Freesound, #5) fetches existing sounds from a service:
`search` returns ranked [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)s, `get` resolves
one id to a [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord), and `download` returns the
(transient, TOS-permitting) bytes to embed. It returns the SAME
`Candidate` / `SoundRecord` shapes as the retrieval index, so callers see
one uniform interface whether audio is remote, cached, or local.

Distinct from (and complementary to) the narrow bulk-corpus
[`CorpusAdapter`](_autosummary/foley.sources.base.html.md#foley.sources.base.CorpusAdapter): a live adapter does NOT re-implement enrichment or
storage — it converges on the same [`foley.index.ingest.ingest_one()`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.ingest_one)
pipeline via [`foley.sources.pull.add_from()`](_autosummary/foley.sources.pull.html.md#foley.sources.pull.add_from) (it *wraps* the corpus
machinery, it does not fork it). **Generation** adapters (#6: Stable Audio
Open, ElevenLabs) will add a sibling `generate` surface; it is intentionally
out of scope here so this Protocol stays runtime-checkable for retrieve
adapters.

#### download(source_id)

Return a sound’s bytes (honoring `cache_bytes_ok` at the storage gate).

* **Return type:**
  [`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes)

#### get(source_id)

Resolve one source id to a metadata [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord).

* **Return type:**
  [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord)

#### search(query, \*\*kw)

Return ranked candidates for `query` (license filter pushed native).

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]

### *exception* foley.sources.base.SourceConfigurationError

Bases: [`RuntimeError`](https://docs.python.org/3/builtins/exceptions.html#RuntimeError)

A source cannot run until the user configures it: a missing key, plan or endpoint.

Not transient, so it is never recorded as one failed hit among many: the generate
and pull façades let it propagate, and its message names what to set (the env var
and, for a key, its sign-up URL) (#64).

### *class* foley.sources.base.UniformCorpus(name, ring, default_license_id, source, rights_verified=True, tag_hints_from_path=False)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

A bulk corpus where **every** clip carries the same license.

Covers FoleySet (CC-BY), Sonniss (Sonniss-GDC), BBC RemArc (RemArc) and any
other single-license drop: it walks the audio tree and stamps one license.
`rights_verified` is a constructor field so a corpus whose blanket license
is authoritatively known passes the gate, while a merely-assumed one can stay
fail-closed. Subclass to enrich per-clip `meta` (see
[`ClothoEvalCorpus`](_autosummary/foley.sources.clotho.html.md#foley.sources.clotho.ClothoEvalCorpus)).

#### corpus_dir(data_dir)

`data_dir/<name>` — the conventional on-disk root for this corpus.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

#### iter_clips(root)

Yield one [`ClipSpec`](_autosummary/foley.sources.base.html.md#foley.sources.base.ClipSpec) per audio file under `root`.

When `tag_hints_from_path` is set, the clip’s parent folder names
(relative to `root`) are carried in `meta['tag_hints']` — corpora like
FoleySet encode a Foley taxonomy in their directory structure.

* **Return type:**
  [`Iterator`](https://docs.python.org/3/library/typing.html#typing.Iterator)[[`ClipSpec`](_autosummary/foley.sources.base.html.md#foley.sources.base.ClipSpec)]

#### resolve_license(spec)

Stamp the corpus’s uniform license (derived via the licensing SSOT).

* **Return type:**
  [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)

### foley.sources.base.api_license(, source, license_id, rights_verified, overrides=None, source_id=None, source_url=None, license_url=None, creator_name=None, attribution_text=None)

Build an API-acquisition [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord), flags derived.

The live-source sibling of [`bulk_license()`](_autosummary/foley.sources.base.html.md#foley.sources.base.bulk_license) (`acquisition_method=api`),
used by HTTP [`SourceAdapter`](_autosummary/foley.sources.base.html.md#foley.sources.base.SourceAdapter)s (Freesound, …). It exposes the
`overrides` seam so an adapter can flip an operational flag on top of the
per-item copyright license **without** minting a new `license_id` — the
Freesound case: keep the sound’s own CC id (`CC0-1.0` / `CC-BY-4.0` / …)
but pass `overrides={'cache_bytes_ok': False}` because the API TOS forbids
caching the bytes even for CC0. `redistribute_standalone_ok` (copyright) and
`cache_bytes_ok` (TOS) are distinct; only the latter is flipped.

* **Parameters:**
  * **source** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Provenance source tag (e.g. `'freesound'`).
  * **license_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The per-item normalized license id (a key of `LICENSE_FLAGS`).
  * **rights_verified** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – `True` only for an authoritatively-recognized license
    (fail-closed gate input); MUST be `True` for [`foley.keep()`](_autosummary/foley.html.md#foley.keep) to
    admit the sound.
  * **overrides** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Per-source flag overrides applied on top of `license_id`’s row
    (e.g. `{'cache_bytes_ok': False}`). Keys must be `LicenseFlags`
    fields (validated by [`derive_license_flags()`](_autosummary/foley.licensing.html.md#foley.licensing.derive_license_flags)).
  * **source_id** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – The source-native id (e.g. a Freesound numeric id), for provenance.
  * **source_url** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – A human-resolvable URL for the item (attribution + the stable
    by-reference re-fetch handle).
  * **license_url** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – The license URL/label exactly as the source served it.
  * **creator_name** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – The uploader/creator (required for CC-BY attribution).
  * **attribution_text** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – A ready-made attribution string, if supplied.
* **Return type:**
  [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)
* **Returns:**
  A populated `LicenseRecord` with its derived flags (+ overrides) applied.

### foley.sources.base.bound_source_account(source)

The account bound for `source` in this context, or `None` when no key is bound.

A key bound without its account (`ocracy.kit.using_credentials(<source>=key)`,
or a key rebound over the one an account came with) gives `{}`: its plan is
unknown, and the caller must not fall back to the operator’s.

* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]

### foley.sources.base.bulk_license(, source, license_id, rights_verified, source_id=None, source_url=None, license_url=None, creator_name=None, attribution_text=None)

Build a bulk-acquisition [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord), flags derived.

The SSOT license builder every **bulk-corpus** adapter routes through
(`acquisition_method=bulk`); a thin wrapper over `_build_license()`. Its
signature has no `overrides` (a downloaded corpus is cacheable by-value).

* **Parameters:**
  * **source** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Provenance source tag (e.g. `'fsd50k'`, `'foleyset'`).
  * **license_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The normalized license id (a key of `LICENSE_FLAGS`, else it
    falls back to the all-False `unknown` flags).
  * **rights_verified** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Whether the license is authoritatively known (fail-closed
    gate input — pass `False` for unrecognized/ambiguous licenses).
  * **source_id** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – The corpus-native clip id, for provenance.
  * **source_url** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – A human-resolvable URL for the clip (attribution/credits).
  * **license_url** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – The licence URL/label exactly as the corpus served it (kept so the
    credit links the deed the clip was actually released under).
  * **creator_name** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – The uploader/creator (required for CC-BY attribution).
  * **attribution_text** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – A ready-made attribution string, if the corpus supplies one.
* **Return type:**
  [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)
* **Returns:**
  A populated `LicenseRecord` with its derived flags applied.

### foley.sources.base.check_prompt_influence(value)

Raise `ValueError` unless `value` is `None` or a real number in `0..1`.

Called when a generation is planned, before any budget is reserved, and again
by [`native_adherence()`](_autosummary/foley.sources.base.html.md#foley.sources.base.native_adherence). A `bool` or a numeric string is refused rather
than coerced: `True` would silently mean the maximum.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### foley.sources.base.corpora_in_rings(rings)

Registered adapters whose ring is in `rings` (sorted by name).

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`CorpusAdapter`](_autosummary/foley.sources.base.html.md#foley.sources.base.CorpusAdapter)]

### foley.sources.base.generated_license(, source, license_id, generator_model, generation_prompt, rights_verified=True, generator_version=None, generation_seed=None, generation_params=None, disclosure_recommended=True, watermark=None, c2pa_manifest_ref=None, overrides=None, source_id=None, source_url=None, license_url=None, creator_name=None, attribution_text=None)

Build an AI-generated [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord), flags derived + provenance stamped.

The generation sibling of [`bulk_license()`](_autosummary/foley.sources.base.html.md#foley.sources.base.bulk_license) / [`api_license()`](_autosummary/foley.sources.base.html.md#foley.sources.base.api_license)
(`acquisition_method=generated`): it routes through the shared
`_build_license()` core — so the eight permission flags are DERIVED from
`license_id` by [`apply_license_flags()`](_autosummary/foley.licensing.html.md#foley.licensing.apply_license_flags), never hand-set —
and then stamps the generation-provenance block in exactly one place, so every
generate adapter (Stable Audio Open, ElevenLabs, …) records provenance
identically (open-closed).

The consequences flow automatically from the two generator rows in
[`LICENSE_FLAGS`](_autosummary/foley.licensing.html.md#foley.licensing.LICENSE_FLAGS): `cache_bytes_ok=True` (⇒ by-value
storage), `ai_training_ok=False` (the record keeps this restriction — the
generate façade consents to *embed+persist* via `allow_ai_training_forbidden`
but never flips the flag, so [`foley.keep()`](_autosummary/foley.html.md#foley.keep) still rejects the sound for any
`IntendedUse(will_train=True)`), and — for `Stability-Community` —
`revenue_cap_usd=1_000_000` (enforced by [`foley.keep()`](_autosummary/foley.html.md#foley.keep) at select time).

* **Parameters:**
  * **source** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The generator tag (e.g. `'stable_audio'` / `'elevenlabs'`).
  * **license_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The generator license id (`'Stability-Community'` /
    `'ElevenLabs-SFX'` — a key of `LICENSE_FLAGS`).
  * **generator_model** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The model identifier (e.g. `'stable-audio-open-1.0'`,
    `'eleven_text_to_sound_v2'`).
  * **generation_prompt** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The user prompt, verbatim.
  * **rights_verified** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – `True` (a generator license is authoritatively known);
    MUST be `True` or [`foley.keep()`](_autosummary/foley.html.md#foley.keep) rejects the sound.
  * **generator_version** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional model/version string.
  * **generation_seed** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`int`](https://docs.python.org/3/builtins/functions.html#int)]) – The reproducibility seed (an `int` for a seeded
    Stable-Audio-Open run; `None` for a non-deterministic backend).
  * **generation_params** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – The RESOLVED NATIVE params actually sent to the backend
    (e.g. `guidance_scale` — not the unified `prompt_influence` —
    `audio_end_in_s`, …), for reproducibility + audit.
  * **disclosure_recommended** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – EU AI Act Art. 50 hint (default `True`); makes the
    credits AI-disclosure line render immediately (#9a already reads it).
  * **watermark** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Pass-through carrier for #9b (AudioSeal); `None` until then.
  * **c2pa_manifest_ref** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Pass-through carrier for #9b (C2PA); `None` until then.
  * **overrides** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Optional per-source flag overrides (rare for generation).
  * **source_id** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional source-native id, for provenance.
  * **source_url** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional human-resolvable URL.
  * **license_url** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional license URL/label.
  * **creator_name** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional creator (usually unset for generation).
  * **attribution_text** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional ready-made attribution string.
* **Return type:**
  [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)
* **Returns:**
  A populated `LicenseRecord` with derived flags applied AND
  `is_ai_generated=True` plus the full generation-provenance block.

### foley.sources.base.key_fingerprint(key)

A short, one-way id for an API key: tells accounts apart without keeping the key.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### foley.sources.base.license_from_clip_meta(source, spec)

Build a clip’s rights record from [`per_clip_license_meta()`](_autosummary/foley.sources.base.html.md#foley.sources.base.per_clip_license_meta) fields (fail-closed).

* **Return type:**
  [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)

### foley.sources.base.native_adherence(config, prompt_influence, notes)

Map foley’s unified `prompt_influence` (0..1) onto a source’s native scale.

The source declares the mapping on `config["param_map"]["prompt_influence"]`:
`coerce` (`{"kind": "identity"}` or `{"kind": "linear", "lo": ..., "hi": ...}`,
0 -> `lo`, 1 -> `hi`) and `native_default` (the model’s own value, or `None`
to send nothing and let the API apply its default). Native scales differ widely
(ElevenLabs 0..1, Stable Audio Open’s CFG 1..15), so one unified default would be
wrong for most of them: `None` means “the model’s own default”.

A source whose `native_defaults["distilled"]` is true was post-trained for its
native default (e.g. distilled Stable Audio models expect CFG 1), so a requested
`prompt_influence` is not applied: the native default is used and `notes` says
so (#70).

* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If `prompt_influence` is outside `0..1`.
* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`float`](https://docs.python.org/3/builtins/functions.html#float)]

### foley.sources.base.per_clip_license_meta(license_url, , creator_name=None, source_url=None)

The `ClipSpec.meta` rights fields for a clip that carries its own CC licence.

Shared by the corpora whose clips each keep their Freesound licence (FSD50K,
Clotho): the licence string goes through the one CC mapper
([`license_id_from_cc_url()`](_autosummary/foley.licensing.html.md#foley.licensing.license_id_from_cc_url) — version kept, unknown fails
closed), and the string itself is kept as `license_url` so the credit links the
deed the clip was released under. Read back by [`license_from_clip_meta()`](_autosummary/foley.sources.base.html.md#foley.sources.base.license_from_clip_meta).

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### foley.sources.base.register_corpus(adapter)

Register `adapter` in [`CORPUS_REGISTRY`](_autosummary/foley.sources.base.html.md#foley.sources.base.CORPUS_REGISTRY) (idempotent) and return it.

* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If a *different* adapter is already registered under the name.
* **Return type:**
  [`CorpusAdapter`](_autosummary/foley.sources.base.html.md#foley.sources.base.CorpusAdapter)

### foley.sources.base.resolve_source_key(config, api_key=None)

A source’s API token: `api_key`, else a bound key, else its env var.

The chain is the fleet’s facade kit (`ocracy.kit.resolve_credential`), so a
server can bind a per-request key with `ocracy.kit.using_credentials(<source>=key)`.
`config["auth"]` names the env var and the sign-up URL.

* **Raises:**
  [**SourceConfigurationError**](_autosummary/foley.sources.base.html.md#foley.sources.base.SourceConfigurationError) – Naming the env var and where to get a key.
* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### foley.sources.base.ring_of(name)

Return the ring of the registered corpus `name` (raises `KeyError`).

* **Return type:**
  [`int`](https://docs.python.org/3/builtins/functions.html#int)

### foley.sources.base.select_corpora(, rings=(0, 1), corpora=None)

Resolve the adapters a bootstrap run should touch.

An explicit `corpora` allowlist (by name) wins over `rings`; otherwise
every registered adapter in the given rings is selected. Ring 2 is never in
the default `rings` — it is opt-in only.

* **Raises:**
  [**KeyError**](https://docs.python.org/3/builtins/exceptions.html#KeyError) – If a name in `corpora` is not registered.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`CorpusAdapter`](_autosummary/foley.sources.base.html.md#foley.sources.base.CorpusAdapter)]

### foley.sources.base.source_account_id(config, api_key=None)

Which account a call to the source is made on: `'operator'`, or a key’s fingerprint.

The operator’s account is the source’s env key (or no key at all: a cache entry
can then still be replayed offline, and rotating the operator’s key does not
orphan the cache). Any other key, given as `api_key` or bound for the request,
is another account, told apart by [`key_fingerprint()`](_autosummary/foley.sources.base.html.md#foley.sources.base.key_fingerprint) (#98).

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### foley.sources.base.using_source_accounts(\*\*accounts)

Bind per-request source accounts (a key, and what goes with it) for a `with` block.

The key is bound through `ocracy.kit.using_credentials`, so
[`resolve_source_key()`](_autosummary/foley.sources.base.html.md#foley.sources.base.resolve_source_key) uses it; the rest of the account (ElevenLabs: its
`plan`, which decides the licence) is bound **with** that key, and only applies
while that same key is the one bound (#98):

```default
with foley.using_source_accounts(
    elevenlabs={"key": user_key, "plan": "elevenlabs-free-plan"},
    freesound=user_freesound_key,  # a string is a key alone
):
    foley.generate("a door creaks", backend="elevenlabs")
```

A source given `None` (or an empty key) binds nothing, so an optional request
header can be passed straight through.

The binding lives in a `ContextVar`: it covers what runs inside the block, in
this context. Consume a lazy result (`find(stream=True)`) inside the block, and
run work handed to a thread pool under `contextvars.copy_context()`; outside the
block the operator’s key and plan apply again.

* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – For a field an account cannot carry.


# _autosummary/foley.sources.bbc_remarc.html.md

# foley.sources.bbc_remarc

BBC RemArc (Rewind Archive) — a Ring-2, quarantined corpus.

The BBC Rewind sound-effects archive (report 11 §1.4) is released under the
**RemArc** license: personal / educational / research use only — `commercial_ok
= False` **and** `ai_training_ok = False` (two hard flags). It is a Ring-2
corpus: web-only (no bulk API), so the user must have downloaded the WAVs
themselves, and it is refused by the fail-closed ingest gate unless the operator
passes explicit consent (`accept_ai_restricted=True`).

### Module Attributes

| [`BBC_REMARC`](_autosummary/foley.sources.bbc_remarc.html.md#foley.sources.bbc_remarc.BBC_REMARC)   | The BBC RemArc Ring-2 adapter (non-commercial AND ai_training_ok=False).   |
|---------------------------------------------------------------|----------------------------------------------------------------------------|

### foley.sources.bbc_remarc.BBC_REMARC *= UniformCorpus(name='bbc_remarc', ring=2, default_license_id='RemArc', source='bbc_remarc', rights_verified=True, tag_hints_from_path=False)*

The BBC RemArc Ring-2 adapter (non-commercial AND ai_training_ok=False).


# _autosummary/foley.sources.clotho.html.md

# foley.sources.clotho

Clotho-eval — a captioned Ring-0 corpus that doubles as a retrieval fixture.

Clotho (report 11 §1.2 / §2.1) is an audio-captioning benchmark built from a
Freesound subset; its *evaluation* split is a Ring-0 seed corpus and a retrieval
fixture. Audio must be downloaded locally; this adapter only enumerates, licenses
and (optionally) captions it.

Rights are **per clip and per asset** (#68, report 14 §3.11):

* **Audio** keeps each file’s own Freesound licence, read from the `license`
  column of `clotho_metadata_*.csv` with its CC version (CC0, CC BY 3.0,
  CC BY-NC 3.0, Sampling+ in the eval split). A clip absent from the metadata, or
  under an unrecognised licence, fails closed (`unknown`, unverified). So no
  non-commercial clip passes [`foley.keep()`](_autosummary/foley.html.md#foley.keep) under commercial intent.
* **Captions** are licensed by Tampere University for **non-commercial use only**
  (not CC-BY 4.0, as report 11 said). They are therefore **not** put into the
  library’s keyword index by default: an index is not intent-aware at query time,
  and foley’s default intent is commercial. A non-commercial / eval-only library can
  opt in: `register_corpus(dataclasses.replace(CLOTHO, include_captions=True))`.

### Module Attributes

| [`CAPTION_LICENSE`](_autosummary/foley.sources.clotho.html.md#foley.sources.clotho.CAPTION_LICENSE)   | it is never a sound's licence, only the reason captions stay out of a commercial index).   |
|--------------------------------------------------------------------|--------------------------------------------------------------------------------------------|
| [`CLOTHO`](_autosummary/foley.sources.clotho.html.md#foley.sources.clotho.CLOTHO)            | The Clotho-eval Ring-0 adapter (audio licensed per clip; captions excluded).               |

### Classes

| [`ClothoEvalCorpus`](_autosummary/foley.sources.clotho.html.md#foley.sources.clotho.ClothoEvalCorpus)(name, ring, ...[, ...])   | Ring-0 Clotho-eval adapter: per-clip Freesound licences; captions opt-in (NC).   |
|---------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------|

### foley.sources.clotho.CAPTION_LICENSE *= 'Tampere-University-Clotho-captions (non-commercial only)'*

it is never a
sound’s licence, only the reason captions stay out of a commercial index).

* **Type:**
  What the caption text is licensed under (not a LICENSE_FLAGS row

### foley.sources.clotho.CLOTHO *= ClothoEvalCorpus(name='clotho', ring=0, default_license_id='CC-BY-4.0', source='clotho', rights_verified=True, tag_hints_from_path=False, include_captions=False)*

The Clotho-eval Ring-0 adapter (audio licensed per clip; captions excluded).

### *class* foley.sources.clotho.ClothoEvalCorpus(name, ring, default_license_id, source, rights_verified=True, tag_hints_from_path=False, include_captions=False)

Bases: [`UniformCorpus`](_autosummary/foley.sources.base.html.md#foley.sources.base.UniformCorpus)

Ring-0 Clotho-eval adapter: per-clip Freesound licences; captions opt-in (NC).

#### include_captions *: [bool](https://docs.python.org/3/builtins/functions.html#bool)* *= False*

Put the (non-commercial) human captions into the keyword index. Off by default.

#### iter_clips(root)

Yield clips carrying their per-file licence (and caption, when opted in).

* **Return type:**
  [`Iterator`](https://docs.python.org/3/library/typing.html#typing.Iterator)[[`ClipSpec`](_autosummary/foley.sources.base.html.md#foley.sources.base.ClipSpec)]

#### resolve_license(spec)

The clip’s own licence from its metadata row (`unknown` when absent).

* **Return type:**
  [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)


# _autosummary/foley.sources.elevenlabs.adapter.html.md

# foley.sources.elevenlabs.adapter

ElevenLabs Sound Effects generate adapter — hosted Text-to-SFX v2 over HTTP.

Turns a natural-language prompt into a [`GeneratedClip`](_autosummary/foley.sources.base.html.md#foley.sources.base.GeneratedClip)
via the ElevenLabs `POST /v1/sound-generation` endpoint (report 02 §ElevenLabs).
The response is raw audio bytes (default MP3 44.1 kHz), stored **by-value** (the
generation flywheel) once the `foley.sources.generate.generate()` façade routes
it through [`ingest_one()`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.ingest_one).

Two ElevenLabs-specific quirks the adapter handles:

* **\`\`output_format\`\` is a URL query parameter**, not a JSON body field; everything
  else (`text` / `model_id` / `prompt_influence` / `loop` /
  `duration_seconds`) is the JSON body.
* **No seed / negative-prompt / step control** — generation is non-deterministic, so
  `generation_seed` stays `None` and those unified affordances warn-and-drop.

HTTP is dependency-injected (a [`Transport`](_autosummary/foley.sources.http.html.md#foley.sources.http.Transport)), so the
adapter is fully testable with no network and `import foley` stays dol-only; the
real `requests` lives only behind
[`requests_transport()`](_autosummary/foley.sources.http.html.md#foley.sources.http.requests_transport) (the `foley[elevenlabs]` extra).
The adapter performs NO storage/library access — it builds the audio + a generated
[`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord) and returns; the façade converges it on the
shared ingest pipeline.

### Module Attributes

| [`Adapter`](_autosummary/foley.sources.elevenlabs.adapter.html.md#foley.sources.elevenlabs.adapter.Adapter)   | Registry convention (arioso): the loader imports `adapter.Adapter`.   |
|------------------------------------------------------------|-----------------------------------------------------------------------|

### Classes

| [`Adapter`](_autosummary/foley.sources.elevenlabs.adapter.html.md#foley.sources.elevenlabs.adapter.Adapter)                                          | Registry convention (arioso): the loader imports `adapter.Adapter`.                                                                                |
|---------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------|
| [`ElevenLabsAdapter`](_autosummary/foley.sources.elevenlabs.adapter.html.md#foley.sources.elevenlabs.adapter.ElevenLabsAdapter)([config, api_key, http, plan]) | ElevenLabs Sound Effects generate adapter (a [`GenerateAdapter`](_autosummary/foley.sources.base.html.md#foley.sources.base.GenerateAdapter)). |

### foley.sources.elevenlabs.adapter.Adapter

Registry convention (arioso): the loader imports `adapter.Adapter`.

### *class* foley.sources.elevenlabs.adapter.ElevenLabsAdapter(config=None, , api_key=None, http=None, plan=None)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

ElevenLabs Sound Effects generate adapter (a [`GenerateAdapter`](_autosummary/foley.sources.base.html.md#foley.sources.base.GenerateAdapter)).

* **Parameters:**
  * **config** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – The `SOURCE_CONFIG` (defaults to the module’s). Passed positionally
    by the registry’s lazy loader (the arioso `Adapter(config)` convention).
  * **api_key** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – The ElevenLabs token. Defaults to `$ELEVENLABS_API_KEY`.
  * **http** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Transport`](_autosummary/foley.sources.http.html.md#foley.sources.http.Transport)]) – The injected [`Transport`](_autosummary/foley.sources.http.html.md#foley.sources.http.Transport) (defaults to
    [`requests_transport()`](_autosummary/foley.sources.http.html.md#foley.sources.http.requests_transport)); tests pass a fake.
  * **plan** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – The ElevenLabs account plan (`'elevenlabs-paid-plan'` |
    `'elevenlabs-free-plan'`), which decides the generated sound’s licence.
    Defaults to `$FOLEY_ELEVENLABS_PLAN`; with neither, `generate` refuses
    before calling the API (#56). When the key in use is a per-request one
    (bound, not passed as `api_key=`), the plan is the one bound with it
    ([`using_source_accounts()`](_autosummary/foley.sources.base.html.md#foley.sources.base.using_source_accounts)), never this or the
    env’s (#98).

#### account_id()

The account a call is made on, for the generations cache (`'operator'` or a fingerprint).

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

#### *property* api_key *: [str](https://docs.python.org/3/builtins/stdtypes.html#str)*

The ElevenLabs token (constructor, else a bound key, else `$ELEVENLABS_API_KEY`).

#### generate(prompt, , duration=None, prompt_influence=None, negative_prompt=None, steps=None, seed=None, loop=False, output_format='wav', \*\*kw)

Generate a sound effect for `prompt`; return its bytes + provisional candidate.

* **Parameters:**
  * **prompt** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The natural-language sound description (required).
  * **duration** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`float`](https://docs.python.org/3/builtins/functions.html#float)]) – Seconds (clamped to the native `0.5..30` range). `None`
    lets the model auto-determine the length from the prompt.
  * **prompt_influence** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`float`](https://docs.python.org/3/builtins/functions.html#float)]) – `0..1` (identity map to the native field; higher =
    closer to the prompt, less variety). `None` (default) omits it, so
    the API applies its own default.
  * **seed** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`int`](https://docs.python.org/3/builtins/functions.html#int)]) – No ElevenLabs SFX equivalent — a
    non-default value warns-and-drops (recorded in the clip notes);
    `generation_seed` stays `None`.
  * **loop** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Produce a seamless-loopable clip (native `loop`; v2 default).
  * **output_format** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Coarse foley token (`wav` | `opus` | `mp3`)
    translated to the native enum; `wav` is the lossless `pcm_48000`
    wrapped in a WAV header here, falling back to MP3 (with a note) if
    the account’s plan refuses PCM. Ingest re-archives to FLAC.
  * **\*\*kw** – Extra/unknown affordances (warn-and-dropped).
* **Return type:**
  [`GeneratedClip`](_autosummary/foley.sources.base.html.md#foley.sources.base.GeneratedClip)
* **Returns:**
  A [`GeneratedClip`](_autosummary/foley.sources.base.html.md#foley.sources.base.GeneratedClip) (`origin=generated`,
  `license_id` = the account plan, `is_ai_generated=True`).
* **Raises:**
  [**RuntimeError**](https://docs.python.org/3/builtins/exceptions.html#RuntimeError) – If the plan is unknown (before any API call).

#### *property* plan *: [str](https://docs.python.org/3/builtins/stdtypes.html#str)*

The account plan = the generated sound’s `license_id` (fail-closed when unknown).

It follows the key: a bound (per-request) key brings its own plan, and one
bound without a plan is refused rather than labelled with the operator’s.

#### request_salt()

What besides the request decides the result: the plan (it decides the licence).

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)


# _autosummary/foley.sources.elevenlabs.config.html.md

# foley.sources.elevenlabs.config

`SOURCE_CONFIG` for the ElevenLabs Sound Effects generator (report 02 · 07 · 10 §4.1).

Stdlib-only and declarative — imports nothing heavy, so
[`foley.sources.registry.discover_sources()`](_autosummary/foley.sources.registry.html.md#foley.sources.registry.discover_sources) can read it cheaply (it imports
only `config.py`; the adapter + `requests` load lazily). It declares the hosted
Text-to-SFX v2 endpoint, the `xi-api-key` token auth, maps foley’s unified
[`GENERATION_AFFORDANCES`](_autosummary/foley.base.html.md#foley.base.GENERATION_AFFORDANCES) onto the native request fields, and — as
required for every source — the **license block**, with `cache_bytes_ok=True`
(generated audio is stored **by-value** — the opposite of Freesound).

License note (report 07, #56): what a generation may be used for depends on the
**account’s plan**, which foley cannot see. A paid plan’s SFX are royalty-free and
embeddable without attribution; a free plan’s are non-commercial and need
attribution. So the plan is never assumed: it comes from the adapter’s `plan=` or
`$FOLEY_ELEVENLABS_PLAN` (`elevenlabs-paid-plan` | `elevenlabs-free-plan`, the
same codes as `an`), each value being its own `LICENSE_FLAGS` row, and an unknown
plan refuses to generate (no paid call is made). The Prohibited-Use policy forbids
standalone redistribution of the raw SFX, which is why both rows set
`redistribute_standalone_ok=False`.


# _autosummary/foley.sources.elevenlabs.html.md

# foley.sources.elevenlabs

ElevenLabs Sound Effects generate source (hosted; #6).

Auto-discovered by [`foley.sources.registry.discover_sources()`](_autosummary/foley.sources.registry.html.md#foley.sources.registry.discover_sources), which imports
ONLY [`config`](_autosummary/foley.sources.elevenlabs.config.html.md#module-foley.sources.elevenlabs.config) (stdlib-only). The
[`ElevenLabsAdapter`](_autosummary/foley.sources.elevenlabs.adapter.html.md#foley.sources.elevenlabs.adapter.ElevenLabsAdapter) and its lazy
`requests` dependency (the `foley[elevenlabs]` extra) load on first use — never
at `import foley` or during discovery — via the module `__getattr__` below.

### Classes

| `Adapter`                                          |                                                                                                                                                    |
|----------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------|
| `ElevenLabsAdapter`([config, api_key, http, plan]) | ElevenLabs Sound Effects generate adapter (a [`GenerateAdapter`](_autosummary/foley.sources.base.html.md#foley.sources.base.GenerateAdapter)). |

### Modules

| [`adapter`](_autosummary/foley.sources.elevenlabs.adapter.html.md#module-foley.sources.elevenlabs.adapter)   | ElevenLabs Sound Effects generate adapter — hosted Text-to-SFX v2 over HTTP.           |
|----------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------|
| [`config`](_autosummary/foley.sources.elevenlabs.config.html.md#module-foley.sources.elevenlabs.config)     | `SOURCE_CONFIG` for the ElevenLabs Sound Effects generator (report 02 · 07 · 10 §4.1). |


# _autosummary/foley.sources.foleyset.html.md

# foley.sources.foleyset

FoleySet — a CC-BY, Foley-native starter corpus (Ring 0).

FoleySet (report 11 §3.2) is a Creative-Commons-BY dataset of Foley recordings
organized by a two-level Foley taxonomy in its directory structure. It is a
ship-in-repo Ring-0 corpus: uniform `CC-BY-4.0` (attribution required), stored
by-value (cacheable), with the folder-path taxonomy carried as tag hints.

Ingesting requires the user to have downloaded FoleySet locally; this adapter
only enumerates + licenses it (see [`foley.sources.base`](_autosummary/foley.sources.base.html.md#module-foley.sources.base)).

### Module Attributes

| [`FOLEYSET`](_autosummary/foley.sources.foleyset.html.md#foley.sources.foleyset.FOLEYSET)   | The FoleySet Ring-0 adapter (uniform CC-BY, folder-path tag hints).   |
|-------------------------------------------------------------|-----------------------------------------------------------------------|

### foley.sources.foleyset.FOLEYSET *= UniformCorpus(name='foleyset', ring=0, default_license_id='CC-BY-4.0', source='foleyset', rights_verified=True, tag_hints_from_path=True)*

The FoleySet Ring-0 adapter (uniform CC-BY, folder-path tag hints).


# _autosummary/foley.sources.freesound.adapter.html.md

# foley.sources.freesound.adapter

Freesound APIv2 retrieve adapter — CC0 sounds, stored strictly by-reference.

Freesound (report 01 §Freesound) is foley’s anchor retrieve source. This adapter
turns a natural-language query into ranked, license-clean
[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)s over the Freesound APIv2, honoring two
load-bearing constraints:

* **CC0-only (for #5), pushed into the query.** The CC0 filter is sent as the
  native `filter=license:"Creative Commons 0"` so non-CC0 sounds never leave the
  server; each returned item is *also* re-checked fail-closed against the
  `accepted_license_ids` allowlist — the server filter is a promise foley does
  not control, so a non-CC0 item that slips through is dropped, never indexed.
* **By-reference storage (TOS).** The Freesound API TOS forbids caching the audio
  bytes even for CC0, so every sound is licensed `cache_bytes_ok=False` (a
  per-item override on top of its own CC id — see
  [`api_license()`](_autosummary/foley.sources.base.html.md#foley.sources.base.api_license)). foley keeps the stable sound-page URI
  + provenance + the CLAP vector; the bytes fetched here (a token-tier preview
  > transcode) are transient — embedded once by [`ingest_one()`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.ingest_one),
  > then discarded, never persisted.

HTTP is dependency-injected (a [`Transport`](_autosummary/foley.sources.http.html.md#foley.sources.http.Transport)), so the
adapter is fully testable with no network and `import foley` stays dol-only; the
real `requests` lives only behind
[`requests_transport()`](_autosummary/foley.sources.http.html.md#foley.sources.http.requests_transport) (the `foley[freesound]` extra).
Ingestion (decode / QC / embed / tag / store) is NOT reimplemented here — the
[`foley.sources.pull.add_from()`](_autosummary/foley.sources.pull.html.md#foley.sources.pull.add_from) façade routes every hit through the shared
`ingest_one` pipeline (it wraps the corpus machinery, it does not fork it).

### Module Attributes

| [`Adapter`](_autosummary/foley.sources.freesound.adapter.html.md#foley.sources.freesound.adapter.Adapter)   | Registry convention (arioso): the loader imports `adapter.Adapter`.   |
|------------------------------------------------------------|-----------------------------------------------------------------------|

### Classes

| [`Adapter`](_autosummary/foley.sources.freesound.adapter.html.md#foley.sources.freesound.adapter.Adapter)                                   | Registry convention (arioso): the loader imports `adapter.Adapter`.                                                                        |
|--------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------|
| [`FreesoundAdapter`](_autosummary/foley.sources.freesound.adapter.html.md#foley.sources.freesound.adapter.FreesoundAdapter)([config, api_key, http]) | Live Freesound APIv2 retrieve adapter (a [`SourceAdapter`](_autosummary/foley.sources.base.html.md#foley.sources.base.SourceAdapter)). |

### foley.sources.freesound.adapter.Adapter

Registry convention (arioso): the loader imports `adapter.Adapter`.

### *class* foley.sources.freesound.adapter.FreesoundAdapter(config=None, , api_key=None, http=None)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Live Freesound APIv2 retrieve adapter (a [`SourceAdapter`](_autosummary/foley.sources.base.html.md#foley.sources.base.SourceAdapter)).

* **Parameters:**
  * **config** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – The `SOURCE_CONFIG` (defaults to the module’s). Passed positionally
    by the registry’s lazy loader (the arioso `Adapter(config)` convention).
  * **api_key** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – The Freesound token. Defaults to `$FREESOUND_API_KEY`.
  * **http** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`Transport`](_autosummary/foley.sources.http.html.md#foley.sources.http.Transport)]) – The injected [`Transport`](_autosummary/foley.sources.http.html.md#foley.sources.http.Transport) (defaults to
    [`requests_transport()`](_autosummary/foley.sources.http.html.md#foley.sources.http.requests_transport)); tests pass a fake.

#### *property* api_key *: [str](https://docs.python.org/3/builtins/stdtypes.html#str)*

The Freesound token (constructor, else a bound key, else `$FREESOUND_API_KEY`).

#### download(source_id, , preview_url=None)

Return a sound’s transient preview bytes (token-tier; never cached).

* **Parameters:**
  * **source_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The Freesound id (either id form).
  * **preview_url** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional known preview URL (from a prior search hit) — used
    directly to save a round-trip; otherwise the sound instance is
    fetched to resolve it.
* **Return type:**
  [`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes)
* **Returns:**
  The preview audio bytes (embedded once, then discarded — see the
  by-reference storage contract).
* **Raises:**
  [**LookupError**](https://docs.python.org/3/builtins/exceptions.html#LookupError) – If the sound exposes no preview.

#### get(source_id)

Resolve one Freesound id (`'12345'` or `'freesound:12345'`) to a record.

* **Raises:**
  [**LookupError**](https://docs.python.org/3/builtins/exceptions.html#LookupError) – If the sound’s license is not in the accepted allowlist.
* **Return type:**
  [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord)

#### search(query, , license='cc0', k=15, duration_range=None, sort=None, \*\*kw)

Search Freesound for `query`; return license-clean candidates.

The `license='cc0'` filter is pushed into the native Solr `filter` so
non-CC0 sounds never leave the server; every returned item is still
re-checked fail-closed before it becomes a candidate.

* **Parameters:**
  * **license** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – License constraint. `'cc0'` (default) sends the CC0 filter;
    `None` sends none (the per-item guard still enforces the
    `accepted_license_ids` allowlist, so results stay CC0 for #5).
  * **k** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Max results (Freesound caps `page_size` at 150).
  * **duration_range** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`float`](https://docs.python.org/3/builtins/functions.html#float), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]) – Optional `(min_s, max_s)` native duration filter.
  * **sort** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional native sort key (default: Freesound relevance).
  * **\*\*kw** – Ignored extra affordances (`on_unsupported_param='warn'`).
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]
* **Returns:**
  Up to `k` [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)s (`origin=retrieved`),
  each carrying a by-reference [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord) and a
  transient `preview_uri`.


# _autosummary/foley.sources.freesound.config.html.md

# foley.sources.freesound.config

`SOURCE_CONFIG` for the Freesound APIv2 retrieve source (report 01 · 07 · 10 §4.1).

Stdlib-only and declarative — imports nothing heavy, so
[`foley.sources.registry.discover_sources()`](_autosummary/foley.sources.registry.html.md#foley.sources.registry.discover_sources) can read it cheaply. It maps the
unified foley query vocabulary onto Freesound’s native params, declares the
token-auth env var, the APIv2 endpoints, the rate limits, and — crucially — the
**license block**: `cache_bytes_ok=False` (the Freesound API TOS forbids caching
the bytes, even for CC0) and a CC0-only `accepted_license_ids` allowlist for #5.

The `cache_bytes_ok=False` fact is a top-level property of the *source* (the TOS
constraint is invariant across every Freesound CC variant), distinct from the
per-item copyright license each sound carries. See
[`foley.sources.freesound.adapter`](_autosummary/foley.sources.freesound.adapter.html.md#module-foley.sources.freesound.adapter).


# _autosummary/foley.sources.freesound.html.md

# foley.sources.freesound

Freesound APIv2 retrieve source — CC0 sounds, stored by-reference (`foley[freesound]`).

Declares `SOURCE_CONFIG` (auto-discovered by
[`foley.sources.registry`](_autosummary/foley.sources.registry.html.md#module-foley.sources.registry)) and the `FreesoundAdapter` (aliased
`Adapter` for the registry’s lazy loader). Importing this package is dol-only —
and it is also **discovery-light**: [`foley.sources.registry.discover_sources()`](_autosummary/foley.sources.registry.html.md#foley.sources.registry.discover_sources)
imports only `config.py`, so `adapter.py` (and, later, its lazy `requests`)
is not loaded until an adapter is actually built. To preserve that, the adapter
classes are exposed via a module-level `__getattr__` rather than eagerly imported
here.

### Classes

| `Adapter`                                   |                                                                                                                                            |
|---------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------|
| `FreesoundAdapter`([config, api_key, http]) | Live Freesound APIv2 retrieve adapter (a [`SourceAdapter`](_autosummary/foley.sources.base.html.md#foley.sources.base.SourceAdapter)). |

### Modules

| [`adapter`](_autosummary/foley.sources.freesound.adapter.html.md#module-foley.sources.freesound.adapter)   | Freesound APIv2 retrieve adapter — CC0 sounds, stored strictly by-reference.        |
|---------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------|
| [`config`](_autosummary/foley.sources.freesound.config.html.md#module-foley.sources.freesound.config)     | `SOURCE_CONFIG` for the Freesound APIv2 retrieve source (report 01 · 07 · 10 §4.1). |


# _autosummary/foley.sources.fsd50k.html.md

# foley.sources.fsd50k

FSD50K — the labeled, commercial-safe backbone corpus (Ring 1).

FSD50K (report 11 §1.1, Zenodo 4060432) is ~51 k Freesound clips with AudioSet
labels. Its *compilation* is CC-BY-4.0, but \*\*each clip carries its own Freesound
license\*\* (CC0 / CC-BY / CC-BY-NC / Sampling+), so licensing is resolved
**per-clip** from the shipped metadata, not stamped uniformly. The bootstrap
Ring-1 policy then drops the non-commercial slice (CC-BY-NC / Sampling+) via the
fail-closed commercial filter, keeping the ~85 % CC0/CC-BY backbone.

Per-clip metadata lives in the FSD50K `*_clips_info_FSD50K.json` files
(`{fname: {title, description, tags, license, uploader, ...}}`) where `license`
is a Creative-Commons URL. This adapter reads those, maps the URL to a foley
`license_id`, and fails closed (`license_id='unknown'`, `rights_verified=
False`) for any clip whose license is missing or unrecognized — such clips are
then dropped by [`foley.keep()`](_autosummary/foley.html.md#foley.keep). The bytes are a Zenodo bulk download, so
they are cacheable (`cache_bytes_ok=True`, stored by-value) — do NOT copy #5’s
Freesound-*API* `cache_bytes_ok=False` override.

#### NOTE
The exact metadata filename/columns are per the published FSD50K layout; if a
future release moves them, this adapter degrades to fail-closed rather than
guessing. Verify against the real download when wiring the fetch (#4 ships the
local-dir ingestion; auto-download is a fast-follow).

### Module Attributes

| [`FSD50K`](_autosummary/foley.sources.fsd50k.html.md#foley.sources.fsd50k.FSD50K)   | The FSD50K Ring-1 adapter.   |
|-----------------------------------------------------------|------------------------------|

### Classes

| [`Fsd50kCorpus`](_autosummary/foley.sources.fsd50k.html.md#foley.sources.fsd50k.Fsd50kCorpus)()   | Ring-1 FSD50K adapter with per-clip Freesound license resolution.   |
|-------------------------------------------------------------------|---------------------------------------------------------------------|

### foley.sources.fsd50k.FSD50K *= <foley.sources.fsd50k.Fsd50kCorpus object>*

The FSD50K Ring-1 adapter.

### *class* foley.sources.fsd50k.Fsd50kCorpus

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Ring-1 FSD50K adapter with per-clip Freesound license resolution.

#### corpus_dir(data_dir)

`data_dir/fsd50k` — the conventional on-disk root.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

#### iter_clips(root)

Yield a clip per audio file, carrying its per-clip license metadata.

Each clip’s `meta` gets `{license_id, rights_verified, creator_name,
source_url}` resolved from the FSD50K clips-info JSON (fail-closed when a
clip is absent from the metadata).

* **Return type:**
  [`Iterator`](https://docs.python.org/3/library/typing.html#typing.Iterator)[[`ClipSpec`](_autosummary/foley.sources.base.html.md#foley.sources.base.ClipSpec)]

#### resolve_license(spec)

Build the per-clip rights record from the metadata in `spec.meta`.

* **Return type:**
  [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)


# _autosummary/foley.sources.html.md

# foley.sources

Bulk-corpus source adapters for foley’s SOURCE stage.

This package presents downloaded corpora (FSD50K, Clotho, FoleySet, and the
opt-in Sonniss / BBC RemArc) as an ingestable stream of clips + per-clip
licenses, consumed by `foley.bootstrap.bootstrap()`. Importing the package
registers every adapter in `CORPUS_REGISTRY`.

Three adapter kinds share the one ingest pipeline: the narrow **bulk-corpus**
[`CorpusAdapter`](_autosummary/foley.sources.html.md#foley.sources.CorpusAdapter) (#4 — enumerate + license), the live **retrieve**
[`SourceAdapter`](_autosummary/foley.sources.html.md#foley.sources.SourceAdapter) (#5 — Freesound, via [`add_from()`](_autosummary/foley.sources.html.md#foley.sources.add_from)), and the live
**generate** [`GenerateAdapter`](_autosummary/foley.sources.html.md#foley.sources.GenerateAdapter) (#6 — Stable Audio Open / ElevenLabs, via
[`generate()`](_autosummary/foley.sources.html.md#foley.sources.generate)). The retrieve + generate adapters are auto-discovered by
[`foley.sources.registry`](_autosummary/foley.sources.registry.html.md#module-foley.sources.registry) and *wrap* the ingest machinery, never fork it.

### Functions

| [`bulk_license`](_autosummary/foley.sources.html.md#foley.sources.bulk_license)(\*, source, license_id, ...[, ...])   | Build a bulk-acquisition [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord), flags derived.                   |
|-----------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------|
| [`corpora_in_rings`](_autosummary/foley.sources.html.md#foley.sources.corpora_in_rings)(rings)                            | Registered adapters whose ring is in `rings` (sorted by name).                                                                                     |
| [`register_corpus`](_autosummary/foley.sources.html.md#foley.sources.register_corpus)(adapter)                           | Register `adapter` in `CORPUS_REGISTRY` (idempotent) and return it.                                                                                |
| [`ring_of`](_autosummary/foley.sources.html.md#foley.sources.ring_of)(name)                                      | Return the ring of the registered corpus `name` (raises `KeyError`).                                                                               |
| [`select_corpora`](_autosummary/foley.sources.html.md#foley.sources.select_corpora)(\*[, rings, corpora])               | Resolve the adapters a bootstrap run should touch.                                                                                                 |
| [`api_license`](_autosummary/foley.sources.html.md#foley.sources.api_license)(\*, source, license_id, ...[, ...])    | Build an API-acquisition [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord), flags derived.                   |
| [`add_from`](_autosummary/foley.sources.html.md#foley.sources.add_from)(source, \*, query[, license, limit, ...]) | Search a live `source` and ingest its license-clean hits into `library`.                                                                           |
| [`discover_sources`](_autosummary/foley.sources.html.md#foley.sources.discover_sources)()                                 | Scan [`foley.sources`](_autosummary/foley.sources.html.md#module-foley.sources) sub-packages for a `SOURCE_CONFIG` and register them.                          |
| [`get_source`](_autosummary/foley.sources.html.md#foley.sources.get_source)(name)                                   | Return the `{'config', 'adapter'}` entry for `name`, lazily building the adapter.                                                                  |
| [`list_sources`](_autosummary/foley.sources.html.md#foley.sources.list_sources)(\*[, egress_allow])                   | Return the names of registered live sources (runs discovery first).                                                                                |
| [`register_source`](_autosummary/foley.sources.html.md#foley.sources.register_source)(name, config[, adapter])           | Register a live source directly (out-of-tree plugin or a test double).                                                                             |
| [`generated_license`](_autosummary/foley.sources.html.md#foley.sources.generated_license)(\*, source, license_id, ...)     | Build an AI-generated [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord), flags derived + provenance stamped. |
| [`generate`](_autosummary/foley.sources.html.md#foley.sources.generate)(prompt, \*[, backend, library, ...])      | Generate a sound via `backend` and ingest it (by-value) into `library`.                                                                            |
| [`candidate_of`](_autosummary/foley.sources.html.md#foley.sources.candidate_of)(result)                               | Wrap a stored [`IngestResult`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult) as a generated candidate.              |

### Classes

| [`ClipSpec`](_autosummary/foley.sources.html.md#foley.sources.ClipSpec)(path, source_id[, meta])              | One clip inside a local bulk corpus, described but not yet ingested.            |
|-------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------|
| [`CorpusAdapter`](_autosummary/foley.sources.html.md#foley.sources.CorpusAdapter)(\*args, \*\*kwargs)              | A downloaded bulk corpus presented as an ingestable stream of clips.            |
| [`UniformCorpus`](_autosummary/foley.sources.html.md#foley.sources.UniformCorpus)(name, ring, ...[, ...])          | A bulk corpus where **every** clip carries the same license.                    |
| [`ClothoEvalCorpus`](_autosummary/foley.sources.html.md#foley.sources.ClothoEvalCorpus)(name, ring, ...[, ...])       | Ring-0 Clotho-eval adapter: per-clip Freesound licences; captions opt-in (NC).  |
| [`Fsd50kCorpus`](_autosummary/foley.sources.html.md#foley.sources.Fsd50kCorpus)()                                 | Ring-1 FSD50K adapter with per-clip Freesound license resolution.               |
| [`SourceAdapter`](_autosummary/foley.sources.html.md#foley.sources.SourceAdapter)(\*args, \*\*kwargs)              | The live/HTTP source contract (report 10 §4.2): `search` + `get` + `download`.  |
| [`GenerateAdapter`](_autosummary/foley.sources.html.md#foley.sources.GenerateAdapter)(\*args, \*\*kwargs)            | The generation source contract (report 10 §4.2) — a SIBLING of `SourceAdapter`. |
| [`GeneratedClip`](_autosummary/foley.sources.html.md#foley.sources.GeneratedClip)(audio_bytes, candidate[, notes]) | One freshly-generated sound: its transient bytes + a provisional candidate.     |

### Exceptions

| [`GenerationError`](_autosummary/foley.sources.html.md#foley.sources.GenerationError)(message, \*, report, status)     | Raised by [`foley.generate()`](_autosummary/foley.html.md#foley.generate) when a backend yields no stored sound.        |
|---------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------|
| [`SafetyRefusal`](_autosummary/foley.sources.html.md#foley.sources.SafetyRefusal)(message, \*, hits, report)         | Raised (fail-closed) when a generation prompt trips a #9b safety gate.                                                                   |
| [`TrademarkRefusal`](_autosummary/foley.sources.html.md#foley.sources.TrademarkRefusal)(message, \*, hits, report)      | A [`SafetyRefusal`](_autosummary/foley.sources.html.md#foley.sources.SafetyRefusal) for a prompt naming a trademarked audio logo (report 07 §7.2).          |
| [`RecognizableVoiceRefusal`](_autosummary/foley.sources.html.md#foley.sources.RecognizableVoiceRefusal)(message, \*, hits, ...) | A [`SafetyRefusal`](_autosummary/foley.sources.html.md#foley.sources.SafetyRefusal) for a prompt requesting a recognizable / cloned voice (report 07 §7.1). |

### *class* foley.sources.ClipSpec(path, source_id, meta=<factory>)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

One clip inside a local bulk corpus, described but not yet ingested.

#### path

Absolute path to the audio file on disk.

#### source_id

The corpus-native id (e.g. an FSD50K `fname`); used only for
reporting/provenance — the stored `SoundRecord.id` is still the
content hash minted by the ingest pipeline.

#### meta

Free-form per-clip metadata the adapter carries to
[`CorpusAdapter.resolve_license()`](_autosummary/foley.sources.html.md#foley.sources.CorpusAdapter.resolve_license) and (optionally) to caption/tag
hints — e.g. `{"license_id", "creator_name", "source_url",
"caption", "tag_hints"}`.

### *class* foley.sources.ClothoEvalCorpus(name, ring, default_license_id, source, rights_verified=True, tag_hints_from_path=False, include_captions=False)

Bases: [`UniformCorpus`](_autosummary/foley.sources.base.html.md#foley.sources.base.UniformCorpus)

Ring-0 Clotho-eval adapter: per-clip Freesound licences; captions opt-in (NC).

#### include_captions *: [bool](https://docs.python.org/3/builtins/functions.html#bool)* *= False*

Put the (non-commercial) human captions into the keyword index. Off by default.

#### iter_clips(root)

Yield clips carrying their per-file licence (and caption, when opted in).

* **Return type:**
  [`Iterator`](https://docs.python.org/3/library/typing.html#typing.Iterator)[[`ClipSpec`](_autosummary/foley.sources.base.html.md#foley.sources.base.ClipSpec)]

#### resolve_license(spec)

The clip’s own licence from its metadata row (`unknown` when absent).

* **Return type:**
  [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)

### *class* foley.sources.CorpusAdapter(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

A downloaded bulk corpus presented as an ingestable stream of clips.

Implementations are plain objects (usually a small `@dataclass`) carrying
three class-level facts (`name` / `ring` / `default_license_id`) and the
two methods below. They perform **no** embedding, storage, or library access.

#### corpus_dir(data_dir)

The on-disk root for this corpus under `data_dir` (`data_dir/name`).

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

#### default_license_id *: [str](https://docs.python.org/3/builtins/stdtypes.html#str)*

The corpus compilation license id (a per-clip license may still override).

#### iter_clips(root)

Yield a [`ClipSpec`](_autosummary/foley.sources.html.md#foley.sources.ClipSpec) for every ingestable clip under `root`.

* **Return type:**
  [`Iterator`](https://docs.python.org/3/library/typing.html#typing.Iterator)[[`ClipSpec`](_autosummary/foley.sources.base.html.md#foley.sources.base.ClipSpec)]

#### name *: [str](https://docs.python.org/3/builtins/stdtypes.html#str)*

Registry key / CLI name — `'fsd50k'` | `'clotho'` | `'foleyset'` | …

#### resolve_license(spec)

Return the fully-derived rights record for `spec` (licensing SSOT).

* **Return type:**
  [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)

#### ring *: [int](https://docs.python.org/3/builtins/functions.html#int)*

`0` ship-in-repo, `1` fetch, `2` opt-in/quarantined.

* **Type:**
  Bootstrap ring

### *class* foley.sources.Fsd50kCorpus

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Ring-1 FSD50K adapter with per-clip Freesound license resolution.

#### corpus_dir(data_dir)

`data_dir/fsd50k` — the conventional on-disk root.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

#### iter_clips(root)

Yield a clip per audio file, carrying its per-clip license metadata.

Each clip’s `meta` gets `{license_id, rights_verified, creator_name,
source_url}` resolved from the FSD50K clips-info JSON (fail-closed when a
clip is absent from the metadata).

* **Return type:**
  [`Iterator`](https://docs.python.org/3/library/typing.html#typing.Iterator)[[`ClipSpec`](_autosummary/foley.sources.base.html.md#foley.sources.base.ClipSpec)]

#### resolve_license(spec)

Build the per-clip rights record from the metadata in `spec.meta`.

* **Return type:**
  [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)

### *class* foley.sources.GenerateAdapter(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

The generation source contract (report 10 §4.2) — a SIBLING of `SourceAdapter`.

A **generate** adapter (Stable Audio Open local, ElevenLabs Sound Effects
hosted; #6) synthesizes a sound from a prompt. It is a deliberate sibling of
the retrieve [`SourceAdapter`](_autosummary/foley.sources.html.md#foley.sources.SourceAdapter) rather than an extra method on it, so that
`SourceAdapter` stays `runtime_checkable` for the retrieve trio
(`search` / `get` / `download`) alone.

Like a retrieve adapter, a generate adapter performs **no** storage or library
access: it maps foley’s unified [`GENERATION_AFFORDANCES`](_autosummary/foley.base.html.md#foley.base.GENERATION_AFFORDANCES)
(prompt, duration, prompt_influence, negative_prompt, steps, seed, loop,
output_format) to its backend’s native params (via `SOURCE_CONFIG['param_map']`,
warning-and-dropping unsupported ones), builds the audio + a generated
[`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord), and returns a [`GeneratedClip`](_autosummary/foley.sources.html.md#foley.sources.GeneratedClip). The
`foley.sources.generate.generate()` façade converges it on the shared
`ingest_one` pipeline (by-value, operator-consented).

#### generate(prompt, \*\*affordances)

Synthesize a sound for `prompt`; return its bytes + provisional candidate.

* **Return type:**
  [`GeneratedClip`](_autosummary/foley.sources.base.html.md#foley.sources.base.GeneratedClip)

#### name *: [str](https://docs.python.org/3/builtins/stdtypes.html#str)*

Registry / façade key — `'stable_audio'` | `'elevenlabs'` | …

### *class* foley.sources.GeneratedClip(audio_bytes, candidate, notes=<factory>)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

One freshly-generated sound: its transient bytes + a provisional candidate.

The return type of a [`GenerateAdapter`](_autosummary/foley.sources.html.md#foley.sources.GenerateAdapter)’s `generate` — the envelope
that reconciles report 10 §4.2 (`generate -> Candidate`: retrieval and
generation return the same shape) with foley’s two non-negotiables:

* **adapters never touch storage** — so the bytes ride *with* the candidate in
  an explicit field rather than being written anywhere, and
* **\`\`ingest_one\`\` is never forked** — the `foley.sources.generate.generate()`
  façade hands [`audio_bytes`](_autosummary/foley.sources.html.md#foley.sources.GeneratedClip.audio_bytes) to the one shared pipeline (by-value), exactly
  as [`foley.sources.pull.add_from()`](_autosummary/foley.sources.pull.html.md#foley.sources.pull.add_from) does for a retrieved download.

Generation is retrieval’s `search -> Candidate` and `download -> bytes`
*fused* into one call, because there is no server to re-fetch the bytes from.

Provisional/canonical split — on [`candidate`](_autosummary/foley.sources.html.md#foley.sources.GeneratedClip.candidate)`.sound` only
`license` / `caption` / `tags` are authoritative. `id` (a discarded
`"<source>:pending"` placeholder), `uri`, `content_sha256`,
`storage_mode`, `qc`, `duration_s`, `sample_rate`, `channels` and the
`embedding_*` fields are all minted downstream by
[`ingest_one()`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.ingest_one) (the façade passes `sound_id=None` so
the stored id is the decoded-PCM content hash). [`audio_bytes`](_autosummary/foley.sources.html.md#foley.sources.GeneratedClip.audio_bytes) lives ONLY
here and is never serialized — a `SoundRecord` holds a `uri`, never bytes.

#### audio_bytes

The generated audio as encoded container bytes (WAV/FLAC/…),
transient and in-memory only — consumed once by the ingest pipeline.

#### candidate

The report-10 [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)
(`origin=CandidateOrigin.generated`) carrying the authoritative
[`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord) (`is_ai_generated=True` + the
generation-provenance block) plus the prompt caption + seed tags.

#### notes

Generation-time messages (`on_unsupported_param='warn'` drops,
output-format fallbacks, …). The
`foley.sources.generate.generate()` façade folds these into the
stored [`IngestResult`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult)‘s `notes` so they
surface in the run report.

### *exception* foley.sources.GenerationError(message, , report, status)

Bases: [`RuntimeError`](https://docs.python.org/3/builtins/exceptions.html#RuntimeError)

Raised by [`foley.generate()`](_autosummary/foley.html.md#foley.generate) when a backend yields no stored sound.

Carries the full [`IngestReport`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestReport) and the terminal
[`status`](_autosummary/foley.sources.html.md#foley.sources.GenerationError.status) so a caller can react distinctly to `quarantined` (QC-rejected —
e.g. regenerate), `rights_blocked`, or `error`. The lower-level
[`generate()`](_autosummary/foley.sources.html.md#foley.sources.generate) workhorse never raises this — it always returns an inspectable
report; only the public [`foley.generate()`](_autosummary/foley.html.md#foley.generate) promise raises.

#### report

The [`IngestReport`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestReport) from the run.

#### status

The terminal [`IngestResult`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult) status
(`'quarantined'` | `'rights_blocked'` | `'error'` | …).

### *exception* foley.sources.RecognizableVoiceRefusal(message, , hits, report)

Bases: [`SafetyRefusal`](_autosummary/foley.sources.html.md#foley.sources.SafetyRefusal)

A [`SafetyRefusal`](_autosummary/foley.sources.html.md#foley.sources.SafetyRefusal) for a prompt requesting a recognizable / cloned voice (report 07 §7.1).

### *exception* foley.sources.SafetyRefusal(message, , hits, report)

Bases: [`GenerationError`](_autosummary/foley.sources.html.md#foley.sources.GenerationError)

Raised (fail-closed) when a generation prompt trips a #9b safety gate.

A pre-synthesis hard stop: nothing is generated or stored. Distinct from a
resilience `error` result (a backend/ingest failure the workhorse records
without raising) — a safety refusal is a deliberate refusal of an unsafe
request. Its `report` is an empty pre-generation report and `status` is
`'refused'`. Carries `hits` (the matched marks/patterns). Subclass of
[`GenerationError`](_autosummary/foley.sources.html.md#foley.sources.GenerationError) so the public [`foley.generate()`](_autosummary/foley.html.md#foley.generate) `Raises` clause
already covers it. See [`foley.provenance.disclosure.scan_prompt()`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.scan_prompt).

### *class* foley.sources.SourceAdapter(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

The live/HTTP source contract (report 10 §4.2): `search` + `get` + `download`.

A **retrieve** adapter (Freesound, #5) fetches existing sounds from a service:
`search` returns ranked [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)s, `get` resolves
one id to a [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord), and `download` returns the
(transient, TOS-permitting) bytes to embed. It returns the SAME
`Candidate` / `SoundRecord` shapes as the retrieval index, so callers see
one uniform interface whether audio is remote, cached, or local.

Distinct from (and complementary to) the narrow bulk-corpus
[`CorpusAdapter`](_autosummary/foley.sources.html.md#foley.sources.CorpusAdapter): a live adapter does NOT re-implement enrichment or
storage — it converges on the same [`foley.index.ingest.ingest_one()`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.ingest_one)
pipeline via [`foley.sources.pull.add_from()`](_autosummary/foley.sources.pull.html.md#foley.sources.pull.add_from) (it *wraps* the corpus
machinery, it does not fork it). **Generation** adapters (#6: Stable Audio
Open, ElevenLabs) will add a sibling `generate` surface; it is intentionally
out of scope here so this Protocol stays runtime-checkable for retrieve
adapters.

#### download(source_id)

Return a sound’s bytes (honoring `cache_bytes_ok` at the storage gate).

* **Return type:**
  [`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes)

#### get(source_id)

Resolve one source id to a metadata [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord).

* **Return type:**
  [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord)

#### search(query, \*\*kw)

Return ranked candidates for `query` (license filter pushed native).

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)]

### *exception* foley.sources.TrademarkRefusal(message, , hits, report)

Bases: [`SafetyRefusal`](_autosummary/foley.sources.html.md#foley.sources.SafetyRefusal)

A [`SafetyRefusal`](_autosummary/foley.sources.html.md#foley.sources.SafetyRefusal) for a prompt naming a trademarked audio logo (report 07 §7.2).

### *class* foley.sources.UniformCorpus(name, ring, default_license_id, source, rights_verified=True, tag_hints_from_path=False)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

A bulk corpus where **every** clip carries the same license.

Covers FoleySet (CC-BY), Sonniss (Sonniss-GDC), BBC RemArc (RemArc) and any
other single-license drop: it walks the audio tree and stamps one license.
`rights_verified` is a constructor field so a corpus whose blanket license
is authoritatively known passes the gate, while a merely-assumed one can stay
fail-closed. Subclass to enrich per-clip `meta` (see
[`ClothoEvalCorpus`](_autosummary/foley.sources.clotho.html.md#foley.sources.clotho.ClothoEvalCorpus)).

#### corpus_dir(data_dir)

`data_dir/<name>` — the conventional on-disk root for this corpus.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

#### iter_clips(root)

Yield one [`ClipSpec`](_autosummary/foley.sources.html.md#foley.sources.ClipSpec) per audio file under `root`.

When `tag_hints_from_path` is set, the clip’s parent folder names
(relative to `root`) are carried in `meta['tag_hints']` — corpora like
FoleySet encode a Foley taxonomy in their directory structure.

* **Return type:**
  [`Iterator`](https://docs.python.org/3/library/typing.html#typing.Iterator)[[`ClipSpec`](_autosummary/foley.sources.base.html.md#foley.sources.base.ClipSpec)]

#### resolve_license(spec)

Stamp the corpus’s uniform license (derived via the licensing SSOT).

* **Return type:**
  [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)

### foley.sources.add_from(source, , query, license='cc0', limit=50, library=None, intended_use=None, adapter=None, on_unsupported=None, \*\*affordances)

Search a live `source` and ingest its license-clean hits into `library`.

Progressive disclosure: `add_from("freesound", query="ocean waves")` works out
of the box (CC0-only, into the process-wide default library); every other knob
is an optional keyword. Each hit is license-gated BEFORE any bytes are fetched
(fail-closed), then routed through [`ingest_one()`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.ingest_one), which
applies the by-reference storage gate from the sound’s own license.

* **Parameters:**
  * **source** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – A registered live-source name (e.g. `'freesound'`).
  * **query** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The natural-language search query.
  * **license** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – License constraint pushed into the source query (default
    `'cc0'`). The per-item fail-closed guard enforces the source’s
    accepted-license allowlist regardless.
  * **limit** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Max candidates to request from the source.
  * **library** – Target [`SoundLibrary`](_autosummary/foley.index.library.html.md#foley.index.library.SoundLibrary) (default: the
    process-wide default library).
  * **intended_use** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`IntendedUse`](_autosummary/foley.base.html.md#foley.base.IntendedUse)]) – The rights intent each candidate is gated against (default:
    `DEFAULT_INTENDED_USE`).
  * **adapter** – An optional pre-built adapter to use instead of the registry’s
    (the dependency-injection seam — a test passes a fake-transport
    adapter; production omits it and the registry lazily builds one).
  * **on_unsupported** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – A search parameter the source cannot honour: `None`
    (default) drops it with a note in `report.notes`; `'raise'` raises
    (see `foley.sources._dispatch.translate_affordances()`).
  * **\*\*affordances** – Extra unified affordances forwarded to the adapter’s
    `search` (e.g. `duration_range`, `sort`).
* **Return type:**
  [`IngestReport`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestReport)
* **Returns:**
  An [`IngestReport`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestReport) — inspect `.ingested` for the
  stored records (each `storage_mode == by_reference` for Freesound) and
  `.summary()` for counts, exactly like [`foley.ingest()`](_autosummary/foley.html.md#foley.ingest).

### foley.sources.api_license(, source, license_id, rights_verified, overrides=None, source_id=None, source_url=None, license_url=None, creator_name=None, attribution_text=None)

Build an API-acquisition [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord), flags derived.

The live-source sibling of [`bulk_license()`](_autosummary/foley.sources.html.md#foley.sources.bulk_license) (`acquisition_method=api`),
used by HTTP [`SourceAdapter`](_autosummary/foley.sources.html.md#foley.sources.SourceAdapter)s (Freesound, …). It exposes the
`overrides` seam so an adapter can flip an operational flag on top of the
per-item copyright license **without** minting a new `license_id` — the
Freesound case: keep the sound’s own CC id (`CC0-1.0` / `CC-BY-4.0` / …)
but pass `overrides={'cache_bytes_ok': False}` because the API TOS forbids
caching the bytes even for CC0. `redistribute_standalone_ok` (copyright) and
`cache_bytes_ok` (TOS) are distinct; only the latter is flipped.

* **Parameters:**
  * **source** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Provenance source tag (e.g. `'freesound'`).
  * **license_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The per-item normalized license id (a key of `LICENSE_FLAGS`).
  * **rights_verified** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – `True` only for an authoritatively-recognized license
    (fail-closed gate input); MUST be `True` for [`foley.keep()`](_autosummary/foley.html.md#foley.keep) to
    admit the sound.
  * **overrides** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Per-source flag overrides applied on top of `license_id`’s row
    (e.g. `{'cache_bytes_ok': False}`). Keys must be `LicenseFlags`
    fields (validated by [`derive_license_flags()`](_autosummary/foley.licensing.html.md#foley.licensing.derive_license_flags)).
  * **source_id** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – The source-native id (e.g. a Freesound numeric id), for provenance.
  * **source_url** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – A human-resolvable URL for the item (attribution + the stable
    by-reference re-fetch handle).
  * **license_url** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – The license URL/label exactly as the source served it.
  * **creator_name** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – The uploader/creator (required for CC-BY attribution).
  * **attribution_text** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – A ready-made attribution string, if supplied.
* **Return type:**
  [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)
* **Returns:**
  A populated `LicenseRecord` with its derived flags (+ overrides) applied.

### foley.sources.bulk_license(, source, license_id, rights_verified, source_id=None, source_url=None, license_url=None, creator_name=None, attribution_text=None)

Build a bulk-acquisition [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord), flags derived.

The SSOT license builder every **bulk-corpus** adapter routes through
(`acquisition_method=bulk`); a thin wrapper over `_build_license()`. Its
signature has no `overrides` (a downloaded corpus is cacheable by-value).

* **Parameters:**
  * **source** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – Provenance source tag (e.g. `'fsd50k'`, `'foleyset'`).
  * **license_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The normalized license id (a key of `LICENSE_FLAGS`, else it
    falls back to the all-False `unknown` flags).
  * **rights_verified** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Whether the license is authoritatively known (fail-closed
    gate input — pass `False` for unrecognized/ambiguous licenses).
  * **source_id** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – The corpus-native clip id, for provenance.
  * **source_url** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – A human-resolvable URL for the clip (attribution/credits).
  * **license_url** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – The licence URL/label exactly as the corpus served it (kept so the
    credit links the deed the clip was actually released under).
  * **creator_name** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – The uploader/creator (required for CC-BY attribution).
  * **attribution_text** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – A ready-made attribution string, if the corpus supplies one.
* **Return type:**
  [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)
* **Returns:**
  A populated `LicenseRecord` with its derived flags applied.

### foley.sources.candidate_of(result)

Wrap a stored [`IngestResult`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult) as a generated candidate.

The report-10 §4.2 shape: retrieval and generation return the same
[`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate), differing only in `origin`. Use on a
`pass` / `warn` result (its `record` is the canonical, stored
[`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord)).

* **Parameters:**
  **result** ([`IngestResult`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestResult)) – A stored ingest result (`result.record` is not `None`).
* **Return type:**
  [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate)
* **Returns:**
  A [`Candidate`](_autosummary/foley.base.html.md#foley.base.Candidate) with `origin=CandidateOrigin.generated`.

### foley.sources.corpora_in_rings(rings)

Registered adapters whose ring is in `rings` (sorted by name).

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`CorpusAdapter`](_autosummary/foley.sources.base.html.md#foley.sources.base.CorpusAdapter)]

### foley.sources.discover_sources()

Scan [`foley.sources`](_autosummary/foley.sources.html.md#module-foley.sources) sub-packages for a `SOURCE_CONFIG` and register them.

A valid live source is a sub-package of [`foley.sources`](_autosummary/foley.sources.html.md#module-foley.sources) (`ispkg` and
not `_`-prefixed) whose `config.py` defines a `SOURCE_CONFIG` dict with a
`name`. Flat modules (bulk-corpus adapters + helpers) are skipped, so this
never cross-captures the corpus adapters. Only `config.py` is imported here
(stdlib-cheap); the adapter loads lazily in [`get_source()`](_autosummary/foley.sources.html.md#foley.sources.get_source). Idempotent —
an already-registered name (e.g. a test double) is never overwritten.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]
* **Returns:**
  The list of discovered source names.

### foley.sources.generate(prompt, , backend='stable_audio', library=None, store=True, adapter=None, watermark=None, on_flagged='refuse', watermarker=None, provenance_store=None, on_unsupported=None, reuse_cached=True, generations_cache=None, \*\*affordances)

Generate a sound via `backend` and ingest it (by-value) into `library`.

Progressive disclosure: `generate("a single wooden door creak")` works out of
the box (the local Stable Audio Open backend, into the process-wide default
library); every other knob is an optional keyword. The generated bytes are
routed through the shared [`ingest_one()`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.ingest_one) pipeline
(decode → QC → embed → tag → store), stored by-value with a content-hash id.

Disclosure/safety (#9b), all optional and degrading gracefully:

* **Safety gate** — the prompt is scanned for trademarked audio logos + cloned
  voices *before* any synthesis spend ([`foley.provenance.disclosure.scan_prompt()`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.scan_prompt)).
  Fail-closed by default (`on_flagged='refuse'` raises [`SafetyRefusal`](_autosummary/foley.sources.html.md#foley.sources.SafetyRefusal));
  `on_flagged='warn'` proceeds but stamps `potential_trademark` /
  `contains_recognizable_voice` on the record so [`foley.keep()`](_autosummary/foley.html.md#foley.keep) drops it
  downstream.
* **Watermark** — with `foley[provenance]` installed, the generated bytes are
  AudioSeal-watermarked *before* ingest (the stored bytes carry the mark),
  setting `license.watermark`. Without the extra, generation proceeds
  unmarked (a note is recorded).
* **Content credential** — a portable C2PA-shaped JSON sidecar recording the
  AI origin + license is written for every stored generation and referenced by
  `license.c2pa_manifest_ref` (stdlib; always on).

* **Parameters:**
  * **prompt** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The natural-language sound description.
  * **backend** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – A registered generate-source name (`'stable_audio'` (default,
    local) | `'elevenlabs'` (hosted)).
  * **library** – Target [`SoundLibrary`](_autosummary/foley.index.library.html.md#foley.index.library.SoundLibrary) (default: the
    process-wide default library).
  * **store** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – If `False`, synthesize + enrich but do not add to the library
    (probe/QC/embed only — a preview; no content-credential sidecar).
  * **adapter** – An optional pre-built adapter (the dependency-injection seam — a
    test passes a fake-transport / fake-pipeline adapter; production omits
    it and the registry lazily builds one).
  * **watermark** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`bool`](https://docs.python.org/3/builtins/functions.html#bool)]) – `True` require an AudioSeal watermark (error if
    `foley[provenance]` absent), `False` never watermark, `None`
    (default, auto) watermark iff AudioSeal is installed.
  * **on_flagged** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – `'refuse'` (default, fail-closed) raise on a safety-flagged
    prompt; `'warn'` proceed and flag the record.
  * **watermarker** – An injected [`Watermarker`](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.Watermarker)
    (the DI seam; wins over auto-detect — tests pass a fake).
  * **provenance_store** – A `MutableMapping[str, dict]` for content-credential
    sidecars (default: [`foley.stores.make_provenance_store()`](_autosummary/foley.stores.html.md#foley.stores.make_provenance_store)).
  * **on_unsupported** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – What to do with a parameter the backend cannot honour —
    `'raise'` | `'warn'` | `'note'`; `None` (default) raises for a
    meaning-carrying one (`seed`, `negative_prompt`) and drops the rest
    with a note. See `foley.sources._dispatch.translate_affordances()`.
  * **reuse_cached** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Serve an identical paid request from the generations cache
    instead of paying again (default `True`; #59).
  * **generations_cache** – A [`GenerationsCache`](_autosummary/foley.stores.html.md#foley.stores.GenerationsCache) (default: local).
  * **\*\*affordances** – Unified generation affordances (`duration`,
    `prompt_influence`, `negative_prompt`, `steps`, `seed`, `loop`,
    `output_format` — see [`foley.base.GENERATION_AFFORDANCES`](_autosummary/foley.base.html.md#foley.base.GENERATION_AFFORDANCES)).
* **Return type:**
  [`IngestReport`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestReport)
* **Returns:**
  An [`IngestReport`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestReport) — inspect `.ingested` for the
  stored record (`storage_mode == by_value`) and `.summary()` for counts.
  A backend or ingest failure is recorded as a single `error` result, never
  raised (`add_from`-symmetric resilience).
* **Raises:**
  * [**SafetyRefusal**](_autosummary/foley.sources.html.md#foley.sources.SafetyRefusal) – If the prompt trips a safety gate and `on_flagged='refuse'`.
  * [**WatermarkUnavailable**](_autosummary/foley.provenance.disclosure.html.md#foley.provenance.disclosure.WatermarkUnavailable) – If `watermark=True` but `foley[provenance]` is absent.

### foley.sources.generated_license(, source, license_id, generator_model, generation_prompt, rights_verified=True, generator_version=None, generation_seed=None, generation_params=None, disclosure_recommended=True, watermark=None, c2pa_manifest_ref=None, overrides=None, source_id=None, source_url=None, license_url=None, creator_name=None, attribution_text=None)

Build an AI-generated [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord), flags derived + provenance stamped.

The generation sibling of [`bulk_license()`](_autosummary/foley.sources.html.md#foley.sources.bulk_license) / [`api_license()`](_autosummary/foley.sources.html.md#foley.sources.api_license)
(`acquisition_method=generated`): it routes through the shared
`_build_license()` core — so the eight permission flags are DERIVED from
`license_id` by [`apply_license_flags()`](_autosummary/foley.licensing.html.md#foley.licensing.apply_license_flags), never hand-set —
and then stamps the generation-provenance block in exactly one place, so every
generate adapter (Stable Audio Open, ElevenLabs, …) records provenance
identically (open-closed).

The consequences flow automatically from the two generator rows in
[`LICENSE_FLAGS`](_autosummary/foley.licensing.html.md#foley.licensing.LICENSE_FLAGS): `cache_bytes_ok=True` (⇒ by-value
storage), `ai_training_ok=False` (the record keeps this restriction — the
generate façade consents to *embed+persist* via `allow_ai_training_forbidden`
but never flips the flag, so [`foley.keep()`](_autosummary/foley.html.md#foley.keep) still rejects the sound for any
`IntendedUse(will_train=True)`), and — for `Stability-Community` —
`revenue_cap_usd=1_000_000` (enforced by [`foley.keep()`](_autosummary/foley.html.md#foley.keep) at select time).

* **Parameters:**
  * **source** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The generator tag (e.g. `'stable_audio'` / `'elevenlabs'`).
  * **license_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The generator license id (`'Stability-Community'` /
    `'ElevenLabs-SFX'` — a key of `LICENSE_FLAGS`).
  * **generator_model** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The model identifier (e.g. `'stable-audio-open-1.0'`,
    `'eleven_text_to_sound_v2'`).
  * **generation_prompt** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The user prompt, verbatim.
  * **rights_verified** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – `True` (a generator license is authoritatively known);
    MUST be `True` or [`foley.keep()`](_autosummary/foley.html.md#foley.keep) rejects the sound.
  * **generator_version** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional model/version string.
  * **generation_seed** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`int`](https://docs.python.org/3/builtins/functions.html#int)]) – The reproducibility seed (an `int` for a seeded
    Stable-Audio-Open run; `None` for a non-deterministic backend).
  * **generation_params** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – The RESOLVED NATIVE params actually sent to the backend
    (e.g. `guidance_scale` — not the unified `prompt_influence` —
    `audio_end_in_s`, …), for reproducibility + audit.
  * **disclosure_recommended** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – EU AI Act Art. 50 hint (default `True`); makes the
    credits AI-disclosure line render immediately (#9a already reads it).
  * **watermark** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Pass-through carrier for #9b (AudioSeal); `None` until then.
  * **c2pa_manifest_ref** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Pass-through carrier for #9b (C2PA); `None` until then.
  * **overrides** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Optional per-source flag overrides (rare for generation).
  * **source_id** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional source-native id, for provenance.
  * **source_url** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional human-resolvable URL.
  * **license_url** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional license URL/label.
  * **creator_name** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional creator (usually unset for generation).
  * **attribution_text** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Optional ready-made attribution string.
* **Return type:**
  [`LicenseRecord`](_autosummary/foley.base.html.md#foley.base.LicenseRecord)
* **Returns:**
  A populated `LicenseRecord` with derived flags applied AND
  `is_ai_generated=True` plus the full generation-provenance block.

### foley.sources.get_source(name)

Return the `{'config', 'adapter'}` entry for `name`, lazily building the adapter.

Runs a discovery pass if `name` is not yet known, then instantiates the
adapter on first use (cached in the entry).

* **Parameters:**
  **name** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The source name.
* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)
* **Returns:**
  The registry entry (`{'config': dict, 'adapter': SourceAdapter, ...}`).
* **Raises:**
  * [**KeyError**](https://docs.python.org/3/builtins/exceptions.html#KeyError) – If no such source is registered (after discovery).
  * [**EgressBlocked**](_autosummary/foley.runtime.html.md#foley.runtime.EgressBlocked) – If the source sends data off the device and an offline scope
        is active ([`foley.offline()`](_autosummary/foley.html.md#foley.offline) / `$FOLEY_OFFLINE`).

### foley.sources.list_sources(, egress_allow=None)

Return the names of registered live sources (runs discovery first).

* **Parameters:**
  **egress_allow** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`frozenset`](https://docs.python.org/3/builtins/stdtypes.html#frozenset)]) – If given, keep only sources whose declared
  `config['data_egress']` is in this set (the local-first / offline
  filter — see [`foley.runtime.RuntimeConfig`](_autosummary/foley.runtime.html.md#foley.runtime.RuntimeConfig)). A source that does
  not declare `data_egress` is **excluded** (fail-closed).
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]

### foley.sources.register_corpus(adapter)

Register `adapter` in `CORPUS_REGISTRY` (idempotent) and return it.

* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If a *different* adapter is already registered under the name.
* **Return type:**
  [`CorpusAdapter`](_autosummary/foley.sources.base.html.md#foley.sources.base.CorpusAdapter)

### foley.sources.register_source(name, config, adapter=None)

Register a live source directly (out-of-tree plugin or a test double).

Overwrites any existing entry for `name` — the seam a test uses to inject a
fake-transport-backed adapter. If `adapter` is `None` it is lazily built
from `config` on first [`get_source()`](_autosummary/foley.sources.html.md#foley.sources.get_source) (the source must then be an
importable `foley.sources.<name>` package).

* **Parameters:**
  * **name** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The source name (the [`add_from()`](_autosummary/foley.sources.html.md#foley.sources.add_from) / [`get_source()`](_autosummary/foley.sources.html.md#foley.sources.get_source) key).
  * **config** ([`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)) – The `SOURCE_CONFIG` declaration.
  * **adapter** – An optional pre-instantiated adapter (bypasses lazy loading).
* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### foley.sources.ring_of(name)

Return the ring of the registered corpus `name` (raises `KeyError`).

* **Return type:**
  [`int`](https://docs.python.org/3/builtins/functions.html#int)

### foley.sources.select_corpora(, rings=(0, 1), corpora=None)

Resolve the adapters a bootstrap run should touch.

An explicit `corpora` allowlist (by name) wins over `rings`; otherwise
every registered adapter in the given rings is selected. Ring 2 is never in
the default `rings` — it is opt-in only.

* **Raises:**
  [**KeyError**](https://docs.python.org/3/builtins/exceptions.html#KeyError) – If a name in `corpora` is not registered.
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`CorpusAdapter`](_autosummary/foley.sources.base.html.md#foley.sources.base.CorpusAdapter)]

### Modules

| [`base`](_autosummary/foley.sources.base.html.md#module-foley.sources.base)                 | The bulk-corpus source contract — a downloaded corpus as an ingestable stream.            |
|-------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------|
| [`bbc_remarc`](_autosummary/foley.sources.bbc_remarc.html.md#module-foley.sources.bbc_remarc)     | BBC RemArc (Rewind Archive) — a Ring-2, quarantined corpus.                               |
| [`clotho`](_autosummary/foley.sources.clotho.html.md#module-foley.sources.clotho)             | Clotho-eval — a captioned Ring-0 corpus that doubles as a retrieval fixture.              |
| [`elevenlabs`](_autosummary/foley.sources.elevenlabs.html.md#module-foley.sources.elevenlabs)     | ElevenLabs Sound Effects generate source (hosted; #6).                                    |
| [`foleyset`](_autosummary/foley.sources.foleyset.html.md#module-foley.sources.foleyset)         | FoleySet — a CC-BY, Foley-native starter corpus (Ring 0).                                 |
| [`freesound`](_autosummary/foley.sources.freesound.html.md#module-foley.sources.freesound)       | Freesound APIv2 retrieve source — CC0 sounds, stored by-reference (`foley[freesound]`).   |
| [`fsd50k`](_autosummary/foley.sources.fsd50k.html.md#module-foley.sources.fsd50k)             | FSD50K — the labeled, commercial-safe backbone corpus (Ring 1).                           |
| [`http`](_autosummary/foley.sources.http.html.md#module-foley.sources.http)                 | Injectable HTTP transport for foley's live source adapters.                               |
| [`pull`](_autosummary/foley.sources.pull.html.md#module-foley.sources.pull)                 | `add_from` — the live-source pull façade (SOURCE → INDEX in one call).                    |
| [`registry`](_autosummary/foley.sources.registry.html.md#module-foley.sources.registry)         | Live-source adapter registry: auto-discovery + lazy loading (arioso's registry, ported).  |
| [`resilience`](_autosummary/foley.sources.resilience.html.md#module-foley.sources.resilience)     | Graceful degradation for HTTP source adapters — throttle · backoff · circuit-break (#12). |
| [`sonniss`](_autosummary/foley.sources.sonniss.html.md#module-foley.sources.sonniss)           | Sonniss GameAudioGDC — a Ring-2, quarantined corpus.                                      |
| [`stable_audio`](_autosummary/foley.sources.stable_audio.html.md#module-foley.sources.stable_audio) | Stable Audio Open 1.0 generate source (local default; #6).                                |


# _autosummary/foley.sources.http.html.md

# foley.sources.http

Injectable HTTP transport for foley’s live source adapters.

Live SOURCE adapters (Freesound, #5; hosted generators, #6) speak HTTP. To keep
`import foley` dependency-light (dol-only) and the test suite hermetic (no
network, no HTTP library), the HTTP call is a **dependency-injected callable** —
a [`Transport`](_autosummary/foley.sources.http.html.md#foley.sources.http.Transport) — that each adapter takes by keyword, defaulting to
[`requests_transport()`](_autosummary/foley.sources.http.html.md#foley.sources.http.requests_transport). Tests inject a fake transport returning canned
responses; the real `requests` is imported lazily inside
[`requests_transport()`](_autosummary/foley.sources.http.html.md#foley.sources.http.requests_transport) and **nowhere else**, so it stays behind the
`foley[freesound]` extra (never pulled by the core or by the fake-injection
tests).

A [`Transport`](_autosummary/foley.sources.http.html.md#foley.sources.http.Transport) is any callable with the shape:

```default
transport(method, url, *, params=None, headers=None, json=None) -> Response
```

and a [`Response`](_autosummary/foley.sources.http.html.md#foley.sources.http.Response) is any object exposing `status_code` / `content` /
`json()` — a structural subset of `requests.Response` (which therefore
satisfies it with no wrapper).

The keyword-only `json` body was added for the hosted **generate** adapters (#6:
ElevenLabs Sound Effects POSTs a JSON body), where Freesound (#5) only ever did
GET-with-params. It defaults to `None` and is passed straight through to
`requests.request(..., json=...)`, so every existing GET caller (and the
Freesound fake transport) is unaffected.

### Module Attributes

| [`DEFAULT_TIMEOUT_S`](_autosummary/foley.sources.http.html.md#foley.sources.http.DEFAULT_TIMEOUT_S)   | Per-request timeout (seconds) for the default requests transport.   |
|----------------------------------------------------------------------|---------------------------------------------------------------------|

### Functions

| [`requests_transport`](_autosummary/foley.sources.http.html.md#foley.sources.http.requests_transport)(method, url, \*[, params, ...])   | The default [`Transport`](_autosummary/foley.sources.http.html.md#foley.sources.http.Transport) — a thin, lazy wrapper over `requests`.   |
|-------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------|

### Classes

| [`Response`](_autosummary/foley.sources.http.html.md#foley.sources.http.Response)(\*args, \*\*kwargs)   | The minimal HTTP response surface an adapter needs.                                                               |
|---------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------|
| [`Transport`](_autosummary/foley.sources.http.html.md#foley.sources.http.Transport)(\*args, \*\*kwargs)  | A callable performing ONE HTTP request and returning a [`Response`](_autosummary/foley.sources.http.html.md#foley.sources.http.Response). |

### foley.sources.http.DEFAULT_TIMEOUT_S *: [int](https://docs.python.org/3/builtins/functions.html#int)* *= 30*

Per-request timeout (seconds) for the default requests transport. A live
adapter never blocks foley indefinitely on a hung connection.

### *class* foley.sources.http.Response(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

The minimal HTTP response surface an adapter needs.

A structural subset of `requests.Response` — `requests` satisfies it with
no wrapper, and a test double is a tiny dataclass with the same three members.

#### content *: [bytes](https://docs.python.org/3/builtins/stdtypes.html#bytes)*

Raw response body bytes (used for audio/preview downloads).

#### json()

Decode the response body as JSON.

* **Return type:**
  [`Any`](https://docs.python.org/3/library/typing.html#typing.Any)

#### status_code *: [int](https://docs.python.org/3/builtins/functions.html#int)*

HTTP status code (200 on success).

### *class* foley.sources.http.Transport(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

A callable performing ONE HTTP request and returning a [`Response`](_autosummary/foley.sources.http.html.md#foley.sources.http.Response).

The dependency-injection seam: the default is [`requests_transport()`](_autosummary/foley.sources.http.html.md#foley.sources.http.requests_transport);
tests pass a fake. Keyword-only `params` / `headers` mirror `requests`.

### foley.sources.http.requests_transport(method, url, , params=None, headers=None, json=None)

The default [`Transport`](_autosummary/foley.sources.http.html.md#foley.sources.http.Transport) — a thin, lazy wrapper over `requests`.

`requests` is imported HERE and only here (the `foley[freesound]` /
`foley[elevenlabs]` extras), so the foley core stays dol-only and the
fake-injection test path never needs an HTTP library at all. The returned
`requests.Response` structurally satisfies [`Response`](_autosummary/foley.sources.http.html.md#foley.sources.http.Response).

* **Parameters:**
  * **method** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – HTTP method (`'GET'` / `'POST'` …).
  * **url** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The full request URL.
  * **params** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Optional query-string parameters.
  * **headers** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Optional request headers (e.g. the auth token).
  * **json** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – Optional JSON request body (POST), serialized by `requests`.
* **Return type:**
  [`Response`](_autosummary/foley.sources.http.html.md#foley.sources.http.Response)
* **Returns:**
  The `requests.Response` (a [`Response`](_autosummary/foley.sources.http.html.md#foley.sources.http.Response)).


# _autosummary/foley.sources.pull.html.md

# foley.sources.pull

`add_from` — the live-source pull façade (SOURCE → INDEX in one call).

The live analog of `foley.bootstrap.bootstrap()`’s per-corpus ingest loop:
resolve a registered live [`SourceAdapter`](_autosummary/foley.sources.base.html.md#foley.sources.base.SourceAdapter), search it
(with the license filter pushed into the query), gate each hit through the
fail-closed [`foley.keep()`](_autosummary/foley.html.md#foley.keep) license check, fetch its (transient) bytes, and run
them through the SAME [`foley.index.ingest.ingest_one()`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.ingest_one) pipeline the local and
bulk paths use — so decode / QC / embed / tag / store are **not forked**.

For Freesound this stores every sound BY-REFERENCE (`cache_bytes_ok=False`): the
transient preview bytes are embedded once, then discarded; only the stable URI +
provenance + the CLAP vector persist. The audio is re-fetched on demand via the
adapter (there is no local blob to serve — `library.audio(id)` raises for a
remote by-reference sound; that is the contract).

### Functions

| [`add_from`](_autosummary/foley.sources.pull.html.md#foley.sources.pull.add_from)(source, \*, query[, license, limit, ...])   | Search a live `source` and ingest its license-clean hits into `library`.   |
|-------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------|

### foley.sources.pull.add_from(source, , query, license='cc0', limit=50, library=None, intended_use=None, adapter=None, on_unsupported=None, \*\*affordances)

Search a live `source` and ingest its license-clean hits into `library`.

Progressive disclosure: `add_from("freesound", query="ocean waves")` works out
of the box (CC0-only, into the process-wide default library); every other knob
is an optional keyword. Each hit is license-gated BEFORE any bytes are fetched
(fail-closed), then routed through [`ingest_one()`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.ingest_one), which
applies the by-reference storage gate from the sound’s own license.

* **Parameters:**
  * **source** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – A registered live-source name (e.g. `'freesound'`).
  * **query** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The natural-language search query.
  * **license** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – License constraint pushed into the source query (default
    `'cc0'`). The per-item fail-closed guard enforces the source’s
    accepted-license allowlist regardless.
  * **limit** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Max candidates to request from the source.
  * **library** – Target [`SoundLibrary`](_autosummary/foley.index.library.html.md#foley.index.library.SoundLibrary) (default: the
    process-wide default library).
  * **intended_use** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`IntendedUse`](_autosummary/foley.base.html.md#foley.base.IntendedUse)]) – The rights intent each candidate is gated against (default:
    `DEFAULT_INTENDED_USE`).
  * **adapter** – An optional pre-built adapter to use instead of the registry’s
    (the dependency-injection seam — a test passes a fake-transport
    adapter; production omits it and the registry lazily builds one).
  * **on_unsupported** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – A search parameter the source cannot honour: `None`
    (default) drops it with a note in `report.notes`; `'raise'` raises
    (see `foley.sources._dispatch.translate_affordances()`).
  * **\*\*affordances** – Extra unified affordances forwarded to the adapter’s
    `search` (e.g. `duration_range`, `sort`).
* **Return type:**
  [`IngestReport`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestReport)
* **Returns:**
  An [`IngestReport`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.IngestReport) — inspect `.ingested` for the
  stored records (each `storage_mode == by_reference` for Freesound) and
  `.summary()` for counts, exactly like [`foley.ingest()`](_autosummary/foley.html.md#foley.ingest).


# _autosummary/foley.sources.registry.html.md

# foley.sources.registry

Live-source adapter registry: auto-discovery + lazy loading (arioso’s registry, ported).

Scans [`foley.sources`](_autosummary/foley.sources.html.md#module-foley.sources) for sub-**packages** that declare a `SOURCE_CONFIG`
(a `config.py` with the plugin declaration), registers them, and lazily imports

+ instantiates each adapter only on first use. Mirrors `arioso.registry` /
  `PLATFORM_CONFIG` (report 10 §4.1).

This is the **live-source** registry (Freesound; hosted generators in #6) — a
*separate* registry from the bulk-corpus
[`foley.sources.base.CORPUS_REGISTRY`](_autosummary/foley.sources.base.html.md#foley.sources.base.CORPUS_REGISTRY). The two adapter kinds have different
contracts (`search` / `get` / `download` vs `iter_clips` /
`resolve_license`) and different façades ([`foley.add_from()`](_autosummary/foley.html.md#foley.add_from) vs
[`foley.bootstrap()`](_autosummary/foley.html.md#foley.bootstrap)). They stay disjoint **by construction**: discovery only
picks up sub-packages (`ispkg` and not `_`-prefixed), so the flat bulk-corpus
modules (`fsd50k.py` …) and the flat helper modules here (`base`, `http`,
`pull`, `registry`) are never cross-captured.

Out-of-tree plugins — and test doubles — register directly via
[`register_source()`](_autosummary/foley.sources.registry.html.md#foley.sources.registry.register_source) (no package needed).

### Module Attributes

| [`SOURCE_REGISTRY`](_autosummary/foley.sources.registry.html.md#foley.sources.registry.SOURCE_REGISTRY)   | Public read alias of the registry (callers/tests inspect discovered sources).   |
|--------------------------------------------------------------------|---------------------------------------------------------------------------------|

### Functions

| [`discover_sources`](_autosummary/foley.sources.registry.html.md#foley.sources.registry.discover_sources)()                       | Scan [`foley.sources`](_autosummary/foley.sources.html.md#module-foley.sources) sub-packages for a `SOURCE_CONFIG` and register them.   |
|-------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------|
| [`get_source`](_autosummary/foley.sources.registry.html.md#foley.sources.registry.get_source)(name)                         | Return the `{'config', 'adapter'}` entry for `name`, lazily building the adapter.                                                                |
| [`list_sources`](_autosummary/foley.sources.registry.html.md#foley.sources.registry.list_sources)(\*[, egress_allow])         | Return the names of registered live sources (runs discovery first).                                                                              |
| [`local_sources`](_autosummary/foley.sources.registry.html.md#foley.sources.registry.local_sources)()                          | The names of sources that run entirely on-device (`data_egress == 'local'`).                                                                     |
| [`register_source`](_autosummary/foley.sources.registry.html.md#foley.sources.registry.register_source)(name, config[, adapter]) | Register a live source directly (out-of-tree plugin or a test double).                                                                           |
| [`require_source_egress`](_autosummary/foley.sources.registry.html.md#foley.sources.registry.require_source_egress)(name[, config])    | Raise [`EgressBlocked`](_autosummary/foley.runtime.html.md#foley.runtime.EgressBlocked) if source `name` may not run now.               |
| [`source_egress`](_autosummary/foley.sources.registry.html.md#foley.sources.registry.source_egress)(name)                      | The declared `data_egress` class of source `name` (`None` if undeclared).                                                                        |

### foley.sources.registry.SOURCE_REGISTRY *= {}*

Public read alias of the registry (callers/tests inspect discovered sources).

### foley.sources.registry.discover_sources()

Scan [`foley.sources`](_autosummary/foley.sources.html.md#module-foley.sources) sub-packages for a `SOURCE_CONFIG` and register them.

A valid live source is a sub-package of [`foley.sources`](_autosummary/foley.sources.html.md#module-foley.sources) (`ispkg` and
not `_`-prefixed) whose `config.py` defines a `SOURCE_CONFIG` dict with a
`name`. Flat modules (bulk-corpus adapters + helpers) are skipped, so this
never cross-captures the corpus adapters. Only `config.py` is imported here
(stdlib-cheap); the adapter loads lazily in [`get_source()`](_autosummary/foley.sources.registry.html.md#foley.sources.registry.get_source). Idempotent —
an already-registered name (e.g. a test double) is never overwritten.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]
* **Returns:**
  The list of discovered source names.

### foley.sources.registry.get_source(name)

Return the `{'config', 'adapter'}` entry for `name`, lazily building the adapter.

Runs a discovery pass if `name` is not yet known, then instantiates the
adapter on first use (cached in the entry).

* **Parameters:**
  **name** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The source name.
* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)
* **Returns:**
  The registry entry (`{'config': dict, 'adapter': SourceAdapter, ...}`).
* **Raises:**
  * [**KeyError**](https://docs.python.org/3/builtins/exceptions.html#KeyError) – If no such source is registered (after discovery).
  * [**EgressBlocked**](_autosummary/foley.runtime.html.md#foley.runtime.EgressBlocked) – If the source sends data off the device and an offline scope
        is active ([`foley.offline()`](_autosummary/foley.html.md#foley.offline) / `$FOLEY_OFFLINE`).

### foley.sources.registry.list_sources(, egress_allow=None)

Return the names of registered live sources (runs discovery first).

* **Parameters:**
  **egress_allow** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`frozenset`](https://docs.python.org/3/builtins/stdtypes.html#frozenset)]) – If given, keep only sources whose declared
  `config['data_egress']` is in this set (the local-first / offline
  filter — see [`foley.runtime.RuntimeConfig`](_autosummary/foley.runtime.html.md#foley.runtime.RuntimeConfig)). A source that does
  not declare `data_egress` is **excluded** (fail-closed).
* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]

### foley.sources.registry.local_sources()

The names of sources that run entirely on-device (`data_egress == 'local'`).

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]

### foley.sources.registry.register_source(name, config, adapter=None)

Register a live source directly (out-of-tree plugin or a test double).

Overwrites any existing entry for `name` — the seam a test uses to inject a
fake-transport-backed adapter. If `adapter` is `None` it is lazily built
from `config` on first [`get_source()`](_autosummary/foley.sources.registry.html.md#foley.sources.registry.get_source) (the source must then be an
importable `foley.sources.<name>` package).

* **Parameters:**
  * **name** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The source name (the `add_from()` / [`get_source()`](_autosummary/foley.sources.registry.html.md#foley.sources.registry.get_source) key).
  * **config** ([`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)) – The `SOURCE_CONFIG` declaration.
  * **adapter** – An optional pre-instantiated adapter (bypasses lazy loading).
* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### foley.sources.registry.require_source_egress(name, config=None)

Raise [`EgressBlocked`](_autosummary/foley.runtime.html.md#foley.runtime.EgressBlocked) if source `name` may not run now.

The one offline check every source path goes through: [`get_source()`](_autosummary/foley.sources.registry.html.md#foley.sources.registry.get_source), and the
generate / pull façades when an adapter is injected. The egress class comes from
`config['data_egress']` (the passed config, else the registered one); a source
that declares none is treated as external (fail-closed), so it is refused offline.

* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### foley.sources.registry.source_egress(name)

The declared `data_egress` class of source `name` (`None` if undeclared).

* **Return type:**
  [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]


# _autosummary/foley.sources.resilience.html.md

# foley.sources.resilience

Graceful degradation for HTTP source adapters — throttle · backoff · circuit-break (#12).

[`resilient()`](_autosummary/foley.sources.resilience.html.md#foley.sources.resilience.resilient) wraps the injectable [`Transport`](_autosummary/foley.sources.http.html.md#foley.sources.http.Transport) DI seam
with three composed policies so a flaky or rate-limited remote source degrades
predictably instead of hammering the API or crashing a run:

* **throttle** — a token bucket honouring the source config’s declared `rate`
  (`per_min` / `per_day`) so foley never exceeds the published limits;
* **backoff** — exponential retry on `429` / `5xx` (and transport exceptions),
  honouring a `Retry-After` header when present;
* **circuit-break** — after N consecutive failures the breaker opens and fast-fails
  with [`SourceUnavailable`](_autosummary/foley.sources.resilience.html.md#foley.sources.resilience.SourceUnavailable) for a cool-down window (no thundering herd).

All timing goes through injected `clock` / `sleep` callables (default
`time.monotonic` / `time.sleep`) so the whole thing is deterministically testable
with a fake clock and a no-op sleep — no real network, no real waiting. Stdlib-only.

### Module Attributes

| [`SECONDS_PER_DAY`](_autosummary/foley.sources.resilience.html.md#foley.sources.resilience.SECONDS_PER_DAY)   | Seconds in a rolling rate-limit day (the `per_day` window length).   |
|--------------------------------------------------------------------|----------------------------------------------------------------------|

### Functions

| [`make_resilient_transport_from_config`](_autosummary/foley.sources.resilience.html.md#foley.sources.resilience.make_resilient_transport_from_config)(config, \*)   | Build a resilient transport from a source `config` (its `rate` drives the throttle).                                                    |
|-----------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------|
| [`resilient`](_autosummary/foley.sources.resilience.html.md#foley.sources.resilience.resilient)(transport, \*[, rate, retry, ...])       | Wrap a [`Transport`](_autosummary/foley.sources.http.html.md#foley.sources.http.Transport) with throttle + backoff + circuit-break. |

### Classes

| [`BreakerPolicy`](_autosummary/foley.sources.resilience.html.md#foley.sources.resilience.BreakerPolicy)([fail_threshold, reset_timeout_s])   | Circuit-breaker thresholds.                                   |
|-----------------------------------------------------------------------------------------------------|---------------------------------------------------------------|
| [`RetryPolicy`](_autosummary/foley.sources.resilience.html.md#foley.sources.resilience.RetryPolicy)([max_attempts, retry_on, ...])         | Exponential-backoff retry policy for transient HTTP failures. |

### Exceptions

| [`SourceUnavailable`](_autosummary/foley.sources.resilience.html.md#foley.sources.resilience.SourceUnavailable)   | Raised when a source's circuit breaker is open or its retries are exhausted.   |
|----------------------------------------------------------------------|--------------------------------------------------------------------------------|

### *class* foley.sources.resilience.BreakerPolicy(fail_threshold=5, reset_timeout_s=30.0)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Circuit-breaker thresholds.

### *class* foley.sources.resilience.RetryPolicy(max_attempts=4, retry_on=(429, 500, 502, 503, 504), base_delay_s=0.5, max_delay_s=30.0, respect_retry_after=True)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Exponential-backoff retry policy for transient HTTP failures.

### foley.sources.resilience.SECONDS_PER_DAY *: [float](https://docs.python.org/3/builtins/functions.html#float)* *= 86400.0*

Seconds in a rolling rate-limit day (the `per_day` window length).

### *exception* foley.sources.resilience.SourceUnavailable

Bases: [`RuntimeError`](https://docs.python.org/3/builtins/exceptions.html#RuntimeError)

Raised when a source’s circuit breaker is open or its retries are exhausted.

### foley.sources.resilience.make_resilient_transport_from_config(config, , base=None, \*\*inject)

Build a resilient transport from a source `config` (its `rate` drives the throttle).

* **Parameters:**
  * **config** ([`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)) – A `SOURCE_CONFIG` dict (reads `config['rate']`).
  * **base** – The base transport (default: [`foley.sources.http.requests_transport()`](_autosummary/foley.sources.http.html.md#foley.sources.http.requests_transport)).
  * **\*\*inject** – `retry` / `breaker` / `clock` / `sleep` overrides.
* **Returns:**
  A resilient transport wrapping `base`.

### foley.sources.resilience.resilient(transport, \*, rate=None, retry=None, breaker=None, clock=<built-in function monotonic>, sleep=<built-in function sleep>, resend_after_transport_error=True)

Wrap a [`Transport`](_autosummary/foley.sources.http.html.md#foley.sources.http.Transport) with throttle + backoff + circuit-break.

* **Parameters:**
  * **transport** – The base transport callable `(method, url, *, params, headers, json)`.
  * **rate** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – The source config’s `rate` dict (`{'per_min':…, 'per_day':…}`).
  * **retry** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`RetryPolicy`](_autosummary/foley.sources.resilience.html.md#foley.sources.resilience.RetryPolicy)]) – The [`RetryPolicy`](_autosummary/foley.sources.resilience.html.md#foley.sources.resilience.RetryPolicy) (default: 4 attempts on 429/5xx).
  * **breaker** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`BreakerPolicy`](_autosummary/foley.sources.resilience.html.md#foley.sources.resilience.BreakerPolicy)]) – The [`BreakerPolicy`](_autosummary/foley.sources.resilience.html.md#foley.sources.resilience.BreakerPolicy) (default: open after 5 consecutive fails).
  * **clock** ([`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)[[], [`float`](https://docs.python.org/3/builtins/functions.html#float)]) – Monotonic time source (injected for tests).
  * **sleep** ([`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable)[[[`float`](https://docs.python.org/3/builtins/functions.html#float)], [`None`](https://docs.python.org/3/builtins/constants.html#None)]) – Blocking sleep (injected for tests).
  * **resend_after_transport_error** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Retry a non-GET request after a transport-level
    failure (timeout, reset). `False` for a paid source: the provider may
    have accepted — and billed — the request before the connection failed, so
    re-sending would be an unauthorized second paid call. Status-coded
    rejections (429 / 5xx) are still retried: those are answers, not charges.
* **Returns:**
  A transport with the same signature, plus a `.reset()` method. Raises
  [`SourceUnavailable`](_autosummary/foley.sources.resilience.html.md#foley.sources.resilience.SourceUnavailable) when the breaker is open or retries are exhausted.


# _autosummary/foley.sources.sonniss.html.md

# foley.sources.sonniss

Sonniss GameAudioGDC — a Ring-2, quarantined corpus.

Sonniss GameAudioGDC (report 11 §1.3) is a large, commercially-usable SFX drop,
BUT its license **prohibits using the audio to train AI/ML models** — and
CLAP-embedding + persisting a corpus into foley’s index is exactly that kind of
derivation. So it is licensed `Sonniss-GDC` (`commercial_ok=True` but
`ai_training_ok=False`) and lives in **Ring 2**: never fetched, never in the
default bootstrap rings, and refused by the fail-closed ingest gate unless the
operator passes explicit consent (`accept_ai_restricted=True`).

The user points the adapter at their own downloaded Sonniss tree; foley never
downloads or bundles it.

### Module Attributes

| [`SONNISS`](_autosummary/foley.sources.sonniss.html.md#foley.sources.sonniss.SONNISS)   | The Sonniss Ring-2 adapter (ai_training_ok=False → quarantined by default).   |
|------------------------------------------------------------|-------------------------------------------------------------------------------|

### foley.sources.sonniss.SONNISS *= UniformCorpus(name='sonniss', ring=2, default_license_id='Sonniss-GDC', source='sonniss', rights_verified=True, tag_hints_from_path=False)*

The Sonniss Ring-2 adapter (ai_training_ok=False → quarantined by default).


# _autosummary/foley.sources.stable_audio.adapter.html.md

# foley.sources.stable_audio.adapter

Stable Audio Open 1.0 generate adapter — local `diffusers` inference.

Turns a natural-language prompt into a [`GeneratedClip`](_autosummary/foley.sources.base.html.md#foley.sources.base.GeneratedClip)
via `diffusers.StableAudioPipeline` running on-device (report 02 §Stable Audio
Open). The model produces a 44.1 kHz stereo waveform (up to ~47 s); the adapter
encodes it to container bytes, which the `foley.sources.generate.generate()`
façade stores **by-value** (the generation flywheel) through the shared
[`ingest_one()`](_autosummary/foley.index.ingest.html.md#foley.index.ingest.ingest_one) pipeline.

Load-bearing details verified against the diffusers docs + HF model card:

* \*\*The CFG kwarg is `guidance_scale``** (NOT ``cfg_scale`), and it MUST be passed
  explicitly or the pipeline silently uses its own default 7.0. foley maps its
  unified `prompt_influence` (0..1) onto it linearly (1..15, declared in the
  config’s `coerce`); `None` sends the model’s own default, and a distilled
  checkpoint (`native_defaults["distilled"]`) always gets its native default.
* \*\*Output is channel-first `(channels, samples)``** — transposed to the
  time-first ``(frames, channels)` foley/soundfile convention before encoding.
* **The sample rate is read off the pipeline** (`pipe.vae.sampling_rate` = 44100),
  never hardcoded.
* **\`\`loop\`\` and \`\`output_format\`\` have no native pipeline param** (loop is a future
  WEAVE crossfade; format is the audio-write layer) — they warn-and-drop; the
  adapter never fabricates a pipeline kwarg.

`torch` / `diffusers` are imported lazily inside the methods (the
`foley[stable-audio]` extra), so `import foley` and source discovery stay
dol-only. The local-model DI seam is `pipeline=` (the analog of the hosted
adapters’ `http=`): inject a fake pipeline and no ML stack loads at all — the
whole adapter is testable with no torch. The adapter performs NO storage/library
access; the façade converges it on the shared ingest pipeline.

### Module Attributes

| [`Adapter`](_autosummary/foley.sources.stable_audio.adapter.html.md#foley.sources.stable_audio.adapter.Adapter)   | Registry convention (arioso): the loader imports `adapter.Adapter`.   |
|------------------------------------------------------------|-----------------------------------------------------------------------|

### Classes

| [`Adapter`](_autosummary/foley.sources.stable_audio.adapter.html.md#foley.sources.stable_audio.adapter.Adapter)                                | Registry convention (arioso): the loader imports `adapter.Adapter`.                                                                                   |
|-----------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`StableAudioAdapter`](_autosummary/foley.sources.stable_audio.adapter.html.md#foley.sources.stable_audio.adapter.StableAudioAdapter)([config, pipeline]) | Local Stable Audio Open 1.0 generate adapter (a [`GenerateAdapter`](_autosummary/foley.sources.base.html.md#foley.sources.base.GenerateAdapter)). |

### foley.sources.stable_audio.adapter.Adapter

Registry convention (arioso): the loader imports `adapter.Adapter`.

### *class* foley.sources.stable_audio.adapter.StableAudioAdapter(config=None, , pipeline=None)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Local Stable Audio Open 1.0 generate adapter (a [`GenerateAdapter`](_autosummary/foley.sources.base.html.md#foley.sources.base.GenerateAdapter)).

* **Parameters:**
  * **config** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – The `SOURCE_CONFIG` (defaults to the module’s). Passed positionally
    by the registry’s lazy loader (the arioso `Adapter(config)` convention).
  * **pipeline** – An optional pre-built pipeline (the dependency-injection seam —
    a test injects a fake callable exposing `.vae.sampling_rate` +
    `.device`; production omits it and the real
    `diffusers.StableAudioPipeline` lazy-loads on first generation).

#### generate(prompt, , duration=None, prompt_influence=None, negative_prompt=None, steps=None, seed=None, loop=False, output_format='wav', \*\*kw)

Generate a sound for `prompt`; return its bytes + provisional candidate.

* **Parameters:**
  * **prompt** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The natural-language sound description (required).
  * **duration** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`float`](https://docs.python.org/3/builtins/functions.html#float)]) – Seconds (clamped to the model’s ~47.55 s max). `None` uses a
    sane 10 s default (NOT the 47.55 s maximum).
  * **prompt_influence** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`float`](https://docs.python.org/3/builtins/functions.html#float)]) – `0..1` unified adherence, mapped linearly onto the
    native `guidance_scale` (1..15). `None` (default) sends the
    model’s own default (7.0 for Stable Audio Open 1.0); a distilled
    checkpoint ignores it with a note.
  * **negative_prompt** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – Content to exclude (ignored by the model when guidance
    ≤ 1).
  * **steps** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`int`](https://docs.python.org/3/builtins/functions.html#int)]) – Diffusion steps (native `num_inference_steps`); default 200.
  * **seed** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`int`](https://docs.python.org/3/builtins/functions.html#int)]) – Reproducibility seed → a per-device `torch.Generator`; recorded
    in provenance. `None` → non-deterministic (no torch import).
  * **loop** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – No native param (a future WEAVE crossfade) — warn-and-drop.
  * **output_format** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – No native param (the audio-write layer) — warn-and-drop;
    the clip is archived as FLAC by ingest regardless.
  * **\*\*kw** – Extra/unknown affordances (warn-and-dropped).
* **Return type:**
  [`GeneratedClip`](_autosummary/foley.sources.base.html.md#foley.sources.base.GeneratedClip)
* **Returns:**
  A [`GeneratedClip`](_autosummary/foley.sources.base.html.md#foley.sources.base.GeneratedClip) (`origin=generated`,
  `license_id='Stability-Community'`, `is_ai_generated=True`,
  `generation_seed` captured).


# _autosummary/foley.sources.stable_audio.config.html.md

# foley.sources.stable_audio.config

`SOURCE_CONFIG` for the local Stable Audio Open 1.0 generator (report 02 · 07 · 10 §4.1).

Stdlib-only and declarative — imports neither `torch` nor `diffusers`, so
[`foley.sources.registry.discover_sources()`](_autosummary/foley.sources.registry.html.md#foley.sources.registry.discover_sources) can read it cheaply (it imports
only `config.py`; the adapter + the heavy ML stack load lazily on first
generation). It declares the local `diffusers.StableAudioPipeline` backend, maps
foley’s unified [`GENERATION_AFFORDANCES`](_autosummary/foley.base.html.md#foley.base.GENERATION_AFFORDANCES) onto the pipeline’s
native `__call__` params, and — as required for every source — the \*\*license
block\*\*: `default_license_id='Stability-Community'` with `cache_bytes_ok=True`
(generated audio is stored **by-value** — the generation flywheel).

Commercial guardrail (report 02/07): the Stability AI Community License permits
commercial use free **under $1M annual revenue**; that cap flows automatically from
the `LICENSE_FLAGS['Stability-Community']` row (`revenue_cap_usd=1_000_000`,
never hand-set) and is enforced by [`foley.keep()`](_autosummary/foley.html.md#foley.keep) at select time.

Two foley affordances have **no** native pipeline param — `loop` (a future WEAVE
crossfade) and `output_format` (the audio-write layer, not the model) — so they
are listed unsupported and warn-and-drop; the adapter never fabricates a kwarg.


# _autosummary/foley.sources.stable_audio.html.md

# foley.sources.stable_audio

Stable Audio Open 1.0 generate source (local default; #6).

Auto-discovered by [`foley.sources.registry.discover_sources()`](_autosummary/foley.sources.registry.html.md#foley.sources.registry.discover_sources), which imports
ONLY [`config`](_autosummary/foley.sources.stable_audio.config.html.md#module-foley.sources.stable_audio.config) (stdlib-only — no `torch` /
`diffusers`). The [`StableAudioAdapter`](_autosummary/foley.sources.stable_audio.adapter.html.md#foley.sources.stable_audio.adapter.StableAudioAdapter)
and its heavy ML stack (the `foley[stable-audio]` extra) load on first use — never
at `import foley` or during discovery — via the module `__getattr__` below.

### Classes

| `Adapter`                                |                                                                                                                                                       |
|------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
| `StableAudioAdapter`([config, pipeline]) | Local Stable Audio Open 1.0 generate adapter (a [`GenerateAdapter`](_autosummary/foley.sources.base.html.md#foley.sources.base.GenerateAdapter)). |

### Modules

| [`adapter`](_autosummary/foley.sources.stable_audio.adapter.html.md#module-foley.sources.stable_audio.adapter)   | Stable Audio Open 1.0 generate adapter — local `diffusers` inference.                     |
|------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------|
| [`config`](_autosummary/foley.sources.stable_audio.config.html.md#module-foley.sources.stable_audio.config)     | `SOURCE_CONFIG` for the local Stable Audio Open 1.0 generator (report 02 · 07 · 10 §4.1). |


# _autosummary/foley.stores.html.md

# foley.stores

dol-backed storage for foley: content-addressed bytes + a metadata store.

This module provides the two persistence surfaces the whole façade stands on and
the single gate that decides whether a sound is held *by value* or *by reference*:

> * a **content-addressed byte store** — `Mapping[content_key -> bytes]`,
>   keyed by the sha256 hex digest of the bytes (dedup + immutability),
> * a **metadata store** — `Mapping[sound_id -> SoundRecord]` (JSON on disk),
> * [`store_sound()`](_autosummary/foley.stores.html.md#foley.stores.store_sound) — the by-value vs by-reference gate, driven by
>   `LicenseRecord.cache_bytes_ok`.

Both stores are plain `MutableMapping` objects built from `dol`. Local disk is
the default (`dol.Files` / `dol.JsonFiles`); moving to the cloud is a \*\*store
injection\*\* — pass any `dol` Mapping (e.g. an S3-backed store) for `sounds`
and an S3/Postgres Mapping for `meta` — never a change to business logic.
Content keys make local↔cloud copies idempotent and dedup-safe. Foundation does
NOT implement the S3 store (a later `dol`-plugin concern); it only guarantees
the interface is a plain `MutableMapping`.

Storage layout follows app-data-lifecycle: data lives under
`~/.local/share/foley` (overridable via `$FOLEY_DATA_DIR`), never inside the
package.

Invariants wired here (see the skill):
: #1 `LicenseRecord` is the SSOT for the storage mode — `store_sound` reads
  : `record.license.cache_bytes_ok` to choose by-value vs by-reference.
  <br/>
  #2 `cache_bytes_ok` (TOS/operational) is DISTINCT from
  : `redistribute_standalone_ok` (copyright): a Freesound CC0 item is
    legally redistributable yet `cache_bytes_ok=False`, so it is stored
    by-reference (URI + provenance only, NO bytes cached).
  <br/>
  #3 The meta store is safe by construction — `sound_id` values become
  : on-disk filenames, and once SOURCE adapters mint external-derived ids
    (`freesound:123`, URLs, arbitrary strings) an unescaped id could carry
    `/` / `..` / drive letters / NUL and escape the meta dir or collide.
    [`make_meta_store()`](_autosummary/foley.stores.html.md#foley.stores.make_meta_store) percent-encodes every id into a single, non-dot
    filename component (reversibly, so listing still yields the original id),
    and [`store_sound()`](_autosummary/foley.stores.html.md#foley.stores.store_sound) rejects an empty id before any write. Hex content
    ids are unaffected (percent-encoding is a no-op on them).

### Module Attributes

| [`HASH_ALGO`](_autosummary/foley.stores.html.md#foley.stores.HASH_ALGO)               | Hash algorithm used for content addressing (matches `SoundRecord.hash_algo`).   |
|--------------------------------------------------------------------------|---------------------------------------------------------------------------------|
| [`META_FILE_SUFFIX`](_autosummary/foley.stores.html.md#foley.stores.META_FILE_SUFFIX)        | Suffix given to on-disk metadata files (keys stay the bare `sound_id`).         |
| [`FOLEY_DATA_DIR`](_autosummary/foley.stores.html.md#foley.stores.FOLEY_DATA_DIR)          | data under `~/.local/share`, never in the package).                             |
| [`DEFAULT_PROVENANCE_DIR`](_autosummary/foley.stores.html.md#foley.stores.DEFAULT_PROVENANCE_DIR)  | `Mapping[content_id -> credential dict]`.                                       |
| [`DEFAULT_RUN_DIR`](_autosummary/foley.stores.html.md#foley.stores.DEFAULT_RUN_DIR)         | `Mapping[run_id -> RunManifest dict]`.                                          |
| [`DEFAULT_SESSION_DIR`](_autosummary/foley.stores.html.md#foley.stores.DEFAULT_SESSION_DIR)     | `sessions/{id}/{candidates,picks,rejects}/`.                                    |
| [`DEFAULT_GENERATIONS_DIR`](_autosummary/foley.stores.html.md#foley.stores.DEFAULT_GENERATIONS_DIR) | Every paid generation's bytes + the request that produced them (#59).           |
| [`Rootdir`](_autosummary/foley.stores.html.md#foley.stores.Rootdir)                 | A filesystem location (path or path-like string) for a local store root.        |

### Functions

| [`content_key`](_autosummary/foley.stores.html.md#foley.stores.content_key)(data, \*[, algo])                   | Return the content-address key for `data` — its hex digest.                           |
|--------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------|
| [`make_byte_store`](_autosummary/foley.stores.html.md#foley.stores.make_byte_store)([rootdir])                      | Build the content-addressable blob store: `Mapping[content_key -> bytes]`.            |
| [`make_generations_store`](_autosummary/foley.stores.html.md#foley.stores.make_generations_store)([rootdir])               | Build the paid-generations cache (local files by default; any `dol` pair works).      |
| [`make_meta_store`](_autosummary/foley.stores.html.md#foley.stores.make_meta_store)([rootdir])                      | Build the metadata store: `Mapping[sound_id -> SoundRecord]` (JSON files).            |
| [`make_provenance_store`](_autosummary/foley.stores.html.md#foley.stores.make_provenance_store)([rootdir])                | Build the content-credential store: `Mapping[content_id -> dict]` (JSON files).       |
| [`make_run_store`](_autosummary/foley.stores.html.md#foley.stores.make_run_store)([rootdir])                       | Build the run-artifact store: `Mapping[run_id -> RunManifest dict]` (JSON files).     |
| [`make_session_store`](_autosummary/foley.stores.html.md#foley.stores.make_session_store)([session_id, name, rootdir]) | Build a per-session JSON store: `Mapping[key -> dict]` under `sessions/{id}/{name}/`. |
| [`store_sound`](_autosummary/foley.stores.html.md#foley.stores.store_sound)(record[, data, cache_bytes_ok])     | Persist a sound, choosing by-value vs by-reference from `cache_bytes_ok`.             |

### Classes

| [`GenerationsCache`](_autosummary/foley.stores.html.md#foley.stores.GenerationsCache)(audio, requests)   | Where paid generations are kept, so a paid response is never lost or paid twice (#59).   |
|--------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------|

### foley.stores.DEFAULT_GENERATIONS_DIR *= PosixPath('/home/runner/.local/share/foley/generations')*

Every paid generation’s bytes + the request that produced them (#59).

### foley.stores.DEFAULT_PROVENANCE_DIR *= PosixPath('/home/runner/.local/share/foley/provenance')*

`Mapping[content_id -> credential dict]`.

* **Type:**
  Content-credential sidecars (#9b)

### foley.stores.DEFAULT_RUN_DIR *= PosixPath('/home/runner/.local/share/foley/runs')*

`Mapping[run_id -> RunManifest dict]`.

* **Type:**
  Reproducible run-manifests (#11)

### foley.stores.DEFAULT_SESSION_DIR *= PosixPath('/home/runner/.local/share/foley/sessions')*

`sessions/{id}/{candidates,picks,rejects}/`.

* **Type:**
  Per-session audition state (#12)

### foley.stores.FOLEY_DATA_DIR *= PosixPath('/home/runner/.local/share/foley')*

data under `~/.local/share`, never in
the package). Override with `$FOLEY_DATA_DIR`.

* **Type:**
  Default data root (app-data-lifecycle

### *class* foley.stores.GenerationsCache(audio, requests)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Where paid generations are kept, so a paid response is never lost or paid twice (#59).

Two `dol` mappings:

* `audio` — `content_key -> bytes`: every paid response, written **before**
  QC or ingest, so a quarantined or undecodable generation is still retrievable.
* `requests` — `request digest -> dict`: the canonical request (backend,
  prompt, the native request it resolves to, the model version) mapped to the
  content key plus the licence and notes it was generated under. An identical
  request is served from here instead of calling the provider again.

#### evict(request_key, , keep_audio=False)

Forget a cached request (so the next identical one generates anew).

* **Parameters:**
  * **request_key** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The request digest (noted on the result that cached it).
  * **keep_audio** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Keep the bytes (default: delete them too, unless another
    request still points at them).
* **Return type:**
  [`None`](https://docs.python.org/3/builtins/constants.html#None)

### foley.stores.HASH_ALGO *= 'sha256'*

Hash algorithm used for content addressing (matches `SoundRecord.hash_algo`).

### foley.stores.META_FILE_SUFFIX *= '.json'*

Suffix given to on-disk metadata files (keys stay the bare `sound_id`).

### foley.stores.Rootdir

A filesystem location (path or path-like string) for a local store root.

alias of [`str`](https://docs.python.org/3/builtins/stdtypes.html#str) | `os.PathLike[str]`

### foley.stores.content_key(data, , algo='sha256')

Return the content-address key for `data` — its hex digest.

Using the hash as the key gives free deduplication (identical bytes map to
the same key) and immutability (a key always names the exact same bytes).

* **Parameters:**
  * **data** ([`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes)) – The raw bytes to address (e.g. a FLAC archive blob).
  * **algo** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – A `hashlib` algorithm name (defaults to [`HASH_ALGO`](_autosummary/foley.stores.html.md#foley.stores.HASH_ALGO)).
* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)
* **Returns:**
  The lowercase hex digest of `data` under `algo`.

### foley.stores.make_byte_store(rootdir=PosixPath('/home/runner/.local/share/foley/audio'))

Build the content-addressable blob store: `Mapping[content_key -> bytes]`.

The local default is `dol.Files` (bytes values on disk). For cloud storage,
build the equivalent store from any `dol` Mapping (e.g. an S3 store) and pass
it directly to [`store_sound()`](_autosummary/foley.stores.html.md#foley.stores.store_sound) instead of calling this factory — the
`store_sound` gate treats `sounds` as an opaque `MutableMapping`.

* **Parameters:**
  **rootdir** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]) – Directory that holds the blobs (created if missing).
* **Return type:**
  [`MutableMapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.MutableMapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes)]
* **Returns:**
  A `MutableMapping[str, bytes]` keyed by [`content_key()`](_autosummary/foley.stores.html.md#foley.stores.content_key).

### foley.stores.make_generations_store(rootdir=PosixPath('/home/runner/.local/share/foley/generations'))

Build the paid-generations cache (local files by default; any `dol` pair works).

* **Parameters:**
  **rootdir** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]) – Directory holding `audio/` (bytes) and `requests/` (JSON).
* **Return type:**
  [`GenerationsCache`](_autosummary/foley.stores.html.md#foley.stores.GenerationsCache)
* **Returns:**
  A [`GenerationsCache`](_autosummary/foley.stores.html.md#foley.stores.GenerationsCache).

### foley.stores.make_meta_store(rootdir=PosixPath('/home/runner/.local/share/foley/meta'))

Build the metadata store: `Mapping[sound_id -> SoundRecord]` (JSON files).

`SoundRecord` values are (de)serialized transparently via the
`SerializableMixin` (`to_dict` / `from_dict`); each record is written as
a percent-encoded `{sound_id}.json` file while the store’s keys stay the
bare `sound_id` (invariant #3 — the id is escaped at this boundary so an
externally-derived id can never escape `rootdir` or collide via `/`/`..`).

* **Parameters:**
  **rootdir** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]) – Directory that holds the metadata JSON files (created if missing).
* **Return type:**
  [`MutableMapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.MutableMapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord)]
* **Returns:**
  A `MutableMapping[str, SoundRecord]` keyed by `sound_id`.

### foley.stores.make_provenance_store(rootdir=PosixPath('/home/runner/.local/share/foley/provenance'))

Build the content-credential store: `Mapping[content_id -> dict]` (JSON files).

The by-value sidecar carrier for #9b’s portable “content credential” (a
C2PA-shaped assertion dict written next to each generated clip; a
`SoundRecord`’s `license.c2pa_manifest_ref` points here by content id). Like
[`make_meta_store()`](_autosummary/foley.stores.html.md#foley.stores.make_meta_store) it escapes the id to a safe `{enc}.json` filename
(invariant #3) while exposing bare `content_id` keys; values are plain dicts
((de)serialized natively by `dol.JsonFiles`). Local `dol.JsonFiles` by
default; swap in any `dol` Mapping to move sidecars to the cloud.

* **Parameters:**
  **rootdir** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]) – Directory that holds the credential JSON files (created if missing).
* **Return type:**
  [`MutableMapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.MutableMapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]
* **Returns:**
  A `MutableMapping[str, dict]` keyed by content id.

### foley.stores.make_run_store(rootdir=PosixPath('/home/runner/.local/share/foley/runs'))

Build the run-artifact store: `Mapping[run_id -> RunManifest dict]` (JSON files).

The by-value carrier for #11’s reproducible run-manifests (one per instrumented
`find()` / `generate()` / … ). An exact sibling of [`make_provenance_store()`](_autosummary/foley.stores.html.md#foley.stores.make_provenance_store):
escapes the `run_id` to a safe `{enc}.json` filename (invariant #3) while
exposing bare `run_id` keys; values are plain dicts ((de)serialized by
`dol.JsonFiles`). Local by default; swap in any `dol` Mapping for the cloud.

* **Parameters:**
  **rootdir** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]]) – Directory that holds the run JSON files (created if missing).
* **Return type:**
  [`MutableMapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.MutableMapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]
* **Returns:**
  A `MutableMapping[str, dict]` keyed by `run_id`.

### foley.stores.make_session_store(session_id='default', name='picks', , rootdir=None)

Build a per-session JSON store: `Mapping[key -> dict]` under `sessions/{id}/{name}/`.

The by-value carrier for #12’s audition state — one store per namespace
(`candidates` / `picks` / `rejects`). A sibling of [`make_run_store()`](_autosummary/foley.stores.html.md#foley.stores.make_run_store):
percent-encodes each key to a safe `{enc}.json` filename (invariant #3) while
exposing bare keys; values are plain dicts. Local by default; swap in any `dol`
Mapping for the cloud.

* **Parameters:**
  * **session_id** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The session namespace (default `'default'`).
  * **name** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – The store namespace within the session (`candidates` / `picks` /
    `rejects`).
  * **rootdir** (`Union`[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)], [`None`](https://docs.python.org/3/builtins/constants.html#None)]) – Root sessions directory (default: [`DEFAULT_SESSION_DIR`](_autosummary/foley.stores.html.md#foley.stores.DEFAULT_SESSION_DIR)).
* **Return type:**
  [`MutableMapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.MutableMapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]
* **Returns:**
  A `MutableMapping[str, dict]` keyed by the bare key.

### foley.stores.store_sound(record, data=None, , sounds, meta, cache_bytes_ok=None)

Persist a sound, choosing by-value vs by-reference from `cache_bytes_ok`.

The choice is driven by the sound’s own license (invariant #1): unless
`cache_bytes_ok` is passed explicitly, it is read from
`record.license.cache_bytes_ok`. A sound whose bytes may NOT be cached
(e.g. Freesound CC0, whose TOS forbids caching even though the file is legally
redistributable — invariant #2) is stored **by reference**: no bytes are
written, only its fetchable `uri` plus provenance.

* **Parameters:**
  * **record** ([`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord)) – The `SoundRecord` to persist; its nested `license` is the SSOT
    for the storage mode. Mutated in place with the resolved
    `storage_mode` / `uri` / `content_sha256` and written into
    `meta`.
  * **data** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes)]) – The canonical archive bytes (FLAC). Required for by-value storage;
    for by-reference it is optional — if given, its hash is recorded in
    `content_sha256` for provenance but the bytes are NOT stored.
  * **sounds** ([`MutableMapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.MutableMapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`bytes`](https://docs.python.org/3/builtins/stdtypes.html#bytes)]) – The content-addressed byte store (see [`make_byte_store()`](_autosummary/foley.stores.html.md#foley.stores.make_byte_store)).
  * **meta** ([`MutableMapping`](https://docs.python.org/3/library/collections.abc.html#collections.abc.MutableMapping)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord)]) – The metadata store (see [`make_meta_store()`](_autosummary/foley.stores.html.md#foley.stores.make_meta_store)).
  * **cache_bytes_ok** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`bool`](https://docs.python.org/3/builtins/functions.html#bool)]) – Optional override. `None` (the default) means “use
    `record.license.cache_bytes_ok`”.
* **Return type:**
  [`SoundRecord`](_autosummary/foley.base.html.md#foley.base.SoundRecord)
* **Returns:**
  The same (mutated) `record`, after it has been written into `meta`.
* **Raises:**
  [**ValueError**](https://docs.python.org/3/builtins/exceptions.html#ValueError) – If `record.id` is empty/non-`str` (checked first, so a bad
      id never leaves an orphan blob), or if the sound resolves to
      by-reference storage but `record.uri` is empty (a by-reference sound
      must name a fetchable source URL).

#### NOTE
The blob is written BEFORE the record so a crash can never leave a
metadata reference dangling against a missing blob.


# _autosummary/foley.weave.align.html.md

# foley.weave.align

Forced-alignment adapters — the `Aligner` seam’s fake default + WhisperX real impl.

Forced alignment takes narration audio and its known transcript and returns
word-level timestamps (report 06 §2). foley ships two implementations behind the
[`Aligner`](_autosummary/foley.weave.protocols.html.md#foley.weave.protocols.Aligner) seam, mirroring
[`foley.agent.decompose`](_autosummary/foley.agent.decompose.html.md#module-foley.agent.decompose)’s fake/real discipline exactly:

* [`FakeAligner`](_autosummary/foley.weave.align.html.md#foley.weave.align.FakeAligner) — the deterministic, **torch-free** default: it evenly
  spaces the transcript’s words across the clip’s duration. No model, no network,
  fully reproducible — so hermetic CI (and any bare install) gets a usable
  `word_timeline` with zero heavy dependencies.
* [`WhisperXAligner`](_autosummary/foley.weave.align.html.md#foley.weave.align.WhisperXAligner) — the real ≈±50 ms impl behind `foley[align]`
  (`whisperx` + `torch`), lazy-imported inside its method so `import
  foley.weave` stays dol-only.

`_default_aligner` auto-upgrades to WhisperX when `foley[align]` is installed,
else falls back to the fake — the progressive-disclosure rule.

### Module Attributes

| [`WORDS_PER_SECOND`](_autosummary/foley.weave.align.html.md#foley.weave.align.WORDS_PER_SECOND)     | Fallback speaking cadence used only when the clip's duration is unknown (empty audio); otherwise [`FakeAligner`](_autosummary/foley.weave.align.html.md#foley.weave.align.FakeAligner) spreads words across the real audio duration so its timings are audio-length-aware.   |
|-----------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`WHISPERX_SAMPLE_RATE`](_autosummary/foley.weave.align.html.md#foley.weave.align.WHISPERX_SAMPLE_RATE) | WhisperX runs alignment at 16 kHz mono (its wav2vec2 CTC model's native rate).                                                                                                                                                                      |

### Functions

| [`aligner_name`](_autosummary/foley.weave.align.html.md#foley.weave.align.aligner_name)(aligner)   | `fake` / `whisperx` for the built-ins, else the class name (run manifest).   |
|--------------------------------------------------------------------------|------------------------------------------------------------------------------|

### Classes

| [`FakeAligner`](_autosummary/foley.weave.align.html.md#foley.weave.align.FakeAligner)()                                  | Deterministic, torch-free `Aligner` — evenly spaces the transcript's words.      |
|-------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------|
| [`WhisperXAligner`](_autosummary/foley.weave.align.html.md#foley.weave.align.WhisperXAligner)(\*[, model_size, device, ...]) | The real ≈±50 ms `Aligner` (`foley[align]`) — faster-whisper ASR + wav2vec2 CTC. |

### *class* foley.weave.align.FakeAligner

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Deterministic, torch-free `Aligner` — evenly spaces the transcript’s words.

The hermetic default: given a transcript, it returns one `{'word','start','end'}`
per whitespace token, spread uniformly across the clip’s duration (or at
[`WORDS_PER_SECOND`](_autosummary/foley.weave.align.html.md#foley.weave.align.WORDS_PER_SECOND) when the audio is empty). No transcript → an empty
`word_timeline` (the render then falls back to absolute anchors).

#### word_timeline(audio, sample_rate, , transcript=None, language='en')

Return an evenly-spaced word timeline for `transcript` over `audio`’s duration.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]

### foley.weave.align.WHISPERX_SAMPLE_RATE *: [int](https://docs.python.org/3/builtins/functions.html#int)* *= 16000*

WhisperX runs alignment at 16 kHz mono (its wav2vec2 CTC model’s native rate).

### foley.weave.align.WORDS_PER_SECOND *: [float](https://docs.python.org/3/builtins/functions.html#float)* *= 2.5*

Fallback speaking cadence used only when the clip’s duration is unknown
(empty audio); otherwise [`FakeAligner`](_autosummary/foley.weave.align.html.md#foley.weave.align.FakeAligner) spreads words across the real
audio duration so its timings are audio-length-aware.

### *class* foley.weave.align.WhisperXAligner(, model_size='small', device='cpu', batch_size=16)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

The real ≈±50 ms `Aligner` (`foley[align]`) — faster-whisper ASR + wav2vec2 CTC.

Lazy-imports `whisperx` inside [`word_timeline()`](_autosummary/foley.weave.align.html.md#foley.weave.align.WhisperXAligner.word_timeline) so `import foley.weave`
stays dol-only. Transcribes (if no transcript) then force-aligns at 16 kHz mono.

* **Parameters:**
  * **model_size** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – faster-whisper model name (`tiny`/`base`/`small`/…).
  * **device** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – `'cpu'` or `'cuda'`.
  * **batch_size** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Transcription batch size.

#### word_timeline(audio, sample_rate, , transcript=None, language='en')

Force-align `audio` to its transcript, returning word-level timestamps.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]

### foley.weave.align.aligner_name(aligner)

`fake` / `whisperx` for the built-ins, else the class name (run manifest).

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)


# _autosummary/foley.weave.anchor.html.md

# foley.weave.anchor

Symbolic-anchor → sample-onset heuristics — the SELECT→WEAVE timing bridge (report 06 §2.4).

Pure, stdlib-only functions that turn a placed sound’s *symbolic* anchor (SELECT’s
`"on 'pushed open'"` string, or a resolved [`Placement`](_autosummary/foley.base.html.md#foley.base.Placement)) into a
concrete trigger time (seconds) against a forced-aligned `word_timeline`
(`[{'word','start','end'}, ...]`). Cheapest-first, exactly as report 06 §2.4:

* **word anchor** (one-shots) — fire on the onset of the trigger word;
* **pre-roll** — shift a clip earlier so its salient transient lands on the anchor;
* **sentence span** (beds) — start at the first word of the sentence, run to the last;
* **scene / paragraph boundary** (stingers, ambience swaps) — the boundary’s first word;
* **pause snapping** — nudge an onset to the nearest inter-word gap to avoid masking speech.

These are pure `(anchor, word_timeline) -> seconds` functions with no I/O and no
heavy dependency, so they are independently unit-testable and keep `import
foley.weave` dol-only. `foley.weave.anchor.parse_symbolic_anchor` is the single
SSOT bridge from SELECT’s sparse `TimelineItem.onset` string to a
[`Placement`](_autosummary/foley.base.html.md#foley.base.Placement).

### Module Attributes

| [`SEGMENT_GAP_S`](_autosummary/foley.weave.anchor.html.md#foley.weave.anchor.SEGMENT_GAP_S)   | Inter-word gap (seconds) that ends a sentence segment (report 06 §2.4 uses `> ~350 ms` as a lightweight sentence segmenter over the aligned transcript).   |
|------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|

### Functions

| [`parse_symbolic_anchor`](_autosummary/foley.weave.anchor.html.md#foley.weave.anchor.parse_symbolic_anchor)(onset, \*[, layer, loop])   | Parse SELECT's sparse symbolic `onset` string into a resolved-later `Placement`.   |
|----------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------|
| [`resolve_anchor`](_autosummary/foley.weave.anchor.html.md#foley.weave.anchor.resolve_anchor)(placement, word_timeline, \*)      | Resolve a `Placement` to a concrete `(onset_seconds, duration_seconds)`.           |

### foley.weave.anchor.SEGMENT_GAP_S *: [float](https://docs.python.org/3/builtins/functions.html#float)* *= 0.35*

Inter-word gap (seconds) that ends a sentence segment (report 06 §2.4 uses
`> ~350 ms` as a lightweight sentence segmenter over the aligned transcript).

### foley.weave.anchor.parse_symbolic_anchor(onset, , layer=Layer.sfx_fg, loop=False)

Parse SELECT’s sparse symbolic `onset` string into a resolved-later `Placement`.

The anchor *type* follows the item’s role (report 06 §2.4): beds
(`loop` / `ambience` / `music`) span a **sentence**, stingers land on a
**scene** boundary, and everything else is a **word** anchor; the `ref` is the
quoted phrase the sound lands on. A `None` onset means “no cue”: beds span from
the start, one-shots sit at `absolute` 0. A bare number (defensive — SELECT is
told never to emit one) is read as an absolute offset in seconds.

* **Parameters:**
  * **onset** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str)]) – The symbolic anchor string (e.g. `"on 'pushed open'"`) or `None`.
  * **layer** ([`Layer`](_autosummary/foley.base.html.md#foley.base.Layer)) – The item’s mix layer (selects span/boundary vs word anchoring).
  * **loop** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Whether the item is a looping bed (span-anchored).
* **Return type:**
  [`Placement`](_autosummary/foley.base.html.md#foley.base.Placement)
* **Returns:**
  A `Placement` whose `onset` (seconds) is resolved later by
  [`resolve_anchor()`](_autosummary/foley.weave.anchor.html.md#foley.weave.anchor.resolve_anchor) against the `word_timeline`.

### foley.weave.anchor.resolve_anchor(placement, word_timeline, , snap_pauses=False)

Resolve a `Placement` to a concrete `(onset_seconds, duration_seconds)`.

Dispatches on `placement.anchor` (report 06 §2.4): `absolute` uses the
stored offset; `word` fires on the trigger word; `sentence` spans its
sentence (filling `duration` when unset — beds loop to fill it); `scene` /
`paragraph` land on the boundary. `pre_roll` is then subtracted (clamped at
0), and — when `snap_pauses` — the onset snaps to the nearest inter-word gap.

* **Parameters:**
  * **placement** ([`Placement`](_autosummary/foley.base.html.md#foley.base.Placement)) – The symbolic placement to resolve.
  * **word_timeline** ([`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]) – The forced-aligned `[{'word','start','end'}, ...]`.
  * **snap_pauses** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – If `True`, snap the resolved onset to the nearest pause.
* **Return type:**
  [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`float`](https://docs.python.org/3/builtins/functions.html#float), [`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`float`](https://docs.python.org/3/builtins/functions.html#float)]]
* **Returns:**
  `(onset_seconds, duration_seconds_or_None)`. `duration` is filled only
  for a span anchor whose `placement.duration` was unset.


# _autosummary/foley.weave.html.md

# foley.weave

### Modules


# _autosummary/foley.weave.master.html.md

# foley.weave.master

Mastering — loudness-normalize + true-peak-limit the finished mix (report 06 §5).

Normalises the whole program to a [`MasterProfile`](_autosummary/foley.base.html.md#foley.base.MasterProfile) integrated-loudness
target (ITU-R BS.1770-4 / EBU R128 via `pyloudnorm`) and holds an inter-sample
**true-peak** ceiling. It reuses [`foley.audio.loudness_normalize()`](_autosummary/foley.audio.html.md#foley.audio.loudness_normalize) (the same
BS.1770 meter Tier-0 QC uses) for the LUFS stage and [`foley.qc.true_peak_dbtp()`](_autosummary/foley.qc.html.md#foley.qc.true_peak_dbtp)
(oversampled) for the ceiling — so `pyloudnorm` comes from the existing `audio`
extra (no redundant dependency) and is imported function-locally, keeping `import
foley.weave` dol-only.

The default `engine='auto'` masters fully in-process (portable, testable).
`engine='ffmpeg'` runs the two-pass `ffmpeg loudnorm` “guarantee the numbers” master
(report 06 §5.4) — measure, then apply a linear loudnorm to the measured values — and
**fails safe** back to the in-process path when the `ffmpeg` binary is unavailable or
errors, so weaving never depends on it. `ffmpeg` is a WEAVE system requirement (see
[`foley.weave.requirements`](_autosummary/foley.weave.requirements.html.md#module-foley.weave.requirements)); everything is lazy so `import foley.weave` stays dol-only.

### Functions

| [`master`](_autosummary/foley.weave.master.html.md#foley.weave.master.master)(mix, sample_rate, profile, \*[, engine])   | Master `mix` to `profile` — LUFS-normalise then true-peak-limit (report 06 §5).   |
|----------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------|

### Classes

| [`MasterReport`](_autosummary/foley.weave.master.html.md#foley.weave.master.MasterReport)(target_lufs, input_lufs, ...)   | What the master stage did — a JSON-serialisable audit row for the run-artifact.   |
|-----------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------|

### *class* foley.weave.master.MasterReport(target_lufs, input_lufs, output_lufs, true_peak_dbtp, true_peak_ceiling_db, gain_db, limited, engine)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

What the master stage did — a JSON-serialisable audit row for the run-artifact.

#### to_dict()

Return the plain-dict form (for `WeaveResult.master_report` / obs).

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)

### foley.weave.master.master(mix, sample_rate, profile, , engine='auto')

Master `mix` to `profile` — LUFS-normalise then true-peak-limit (report 06 §5).

* **Parameters:**
  * **mix** (`ndarray`) – The summed working mix (stereo `(frames, 2)` float32).
  * **sample_rate** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Sample rate in Hz.
  * **profile** ([`MasterProfile`](_autosummary/foley.base.html.md#foley.base.MasterProfile)) – The [`MasterProfile`](_autosummary/foley.base.html.md#foley.base.MasterProfile) (target LUFS / true-peak / LRA).
  * **engine** ([`str`](https://docs.python.org/3/builtins/stdtypes.html#str)) – `'auto'`/`'inprocess'` master in-process (default, portable). `'ffmpeg'`
    uses the two-pass `ffmpeg loudnorm` “guarantee the numbers” master (report 06
    §5.4) and **fails safe** back to in-process if ffmpeg is unavailable or errors.
* **Return type:**
  [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[`ndarray`, [`MasterReport`](_autosummary/foley.weave.master.html.md#foley.weave.master.MasterReport)]
* **Returns:**
  `(mastered_mix, MasterReport)`. Loudness is measured before and after so the
  report is a faithful audit; when a true-peak limit engages, output LUFS may
  sit slightly below target (peak-safety wins over exact loudness).


# _autosummary/foley.weave.mix.html.md

# foley.weave.mix

Pure-numpy mixing DSP — making a clip sit under the voice (report 06 §3).

Deterministic, testable, shell-out-free primitives for the WEAVE mixer: level
(gain staging), envelope-follower ducking, constant-power panning, a distance
recipe (attenuation + air-absorption low-pass), a pure-numpy reverb send,
equal-power crossfades / declicks, seamless loop/trim to a duration, and overlay.

Everything operates on the working representation (`float32`, time on axis 0);
the mix graph is **stereo** `(frames, 2)` so panning composes — [`constant_power_pan()`](_autosummary/foley.weave.mix.html.md#foley.weave.mix.constant_power_pan)
is the mono→stereo point. `numpy` is imported lazily inside each function so
`import foley.weave` stays dol-only (mirroring [`foley.audio`](_autosummary/foley.audio.html.md#module-foley.audio)). Nothing here
reaches for `scipy`/`pyroomacoustics`; those are optional-upgrade paths for a
later slice behind `foley[weave]`.

### Module Attributes

| [`LAYER_GAIN_DB`](_autosummary/foley.weave.mix.html.md#foley.weave.mix.LAYER_GAIN_DB)         | Base per-layer gain (dB, relative to the voice bus at 0 dB).                               |
|------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|
| [`DISTANCE_MAX_ATTEN_DB`](_autosummary/foley.weave.mix.html.md#foley.weave.mix.DISTANCE_MAX_ATTEN_DB) | Attenuation (dB) applied at maximum distance (distance == 1.0); ~inverse-distance.         |
| [`DISTANCE_MAX_LP`](_autosummary/foley.weave.mix.html.md#foley.weave.mix.DISTANCE_MAX_LP)       | Air-absorption one-pole low-pass coefficient at maximum distance (0 = none, →1 = darkest). |
| [`REVERB_IR_S`](_autosummary/foley.weave.mix.html.md#foley.weave.mix.REVERB_IR_S)           | Reverb-send impulse-response length (seconds) and decay time-constant.                     |

### Functions

| [`apply_distance`](_autosummary/foley.weave.mix.html.md#foley.weave.mix.apply_distance)(clip, sample_rate, distance)        | Apply a distance recipe: attenuation + air-absorption low-pass (report 06 §3.4).                |
|-----------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------|
| [`constant_power_pan`](_autosummary/foley.weave.mix.html.md#foley.weave.mix.constant_power_pan)(clip, pan)                      | Pan a (mono or stereo) clip to a stereo image with a constant-power law (report 06 §3.3).       |
| [`convolution_reverb`](_autosummary/foley.weave.mix.html.md#foley.weave.mix.convolution_reverb)(clip, sample_rate, ir, \*, ...) | Convolution reverb with a real (recorded or synthesised) impulse response (report 06 §3.5).     |
| [`db_to_lin`](_autosummary/foley.weave.mix.html.md#foley.weave.mix.db_to_lin)(db)                                      | Convert decibels to a linear amplitude factor (`10 ** (db/20)`).                                |
| [`declick`](_autosummary/foley.weave.mix.html.md#foley.weave.mix.declick)(clip, sample_rate, \*[, fade_in, ...])     | Apply short in/out fades so an edit point produces no click (report 06 §3.6).                   |
| [`equal_power_crossfade`](_autosummary/foley.weave.mix.html.md#foley.weave.mix.equal_power_crossfade)(a, b, overlap_samples)       | Concatenate `a` then `b` with an equal-power crossfade over `overlap_samples` (report 06 §3.6). |
| [`fit_duration`](_autosummary/foley.weave.mix.html.md#foley.weave.mix.fit_duration)(clip, sample_rate, \*[, ...])         | Fit `clip` to `duration` seconds — seamless loop (beds) or trim/pass-through (report 06 §4).    |
| [`overlay`](_autosummary/foley.weave.mix.html.md#foley.weave.mix.overlay)(bus, clip, onset_samples)                  | Add `clip` into `bus` at `onset_samples` (report 06 §6.4 `overlay`).                            |
| [`reverb_send`](_autosummary/foley.weave.mix.html.md#foley.weave.mix.reverb_send)(clip, sample_rate, amount)             | Mix a pure-numpy exponential-decay reverb tail into `clip` (report 06 §3.5).                    |
| [`speech_duck_gain`](_autosummary/foley.weave.mix.html.md#foley.weave.mix.speech_duck_gain)(n_samples, sample_rate, ...)      | Build a (linear) gain envelope that dips to `duck_db` during speech spans (report 06 §3.2).     |
| [`time_stretch`](_autosummary/foley.weave.mix.html.md#foley.weave.mix.time_stretch)(clip, sample_rate, \*, rate)          | Pitch-preserving time-stretch by `rate` via `rubberband` (report 06 §4, optional).              |

### foley.weave.mix.DISTANCE_MAX_ATTEN_DB *: [float](https://docs.python.org/3/builtins/functions.html#float)* *= -12.0*

Attenuation (dB) applied at maximum distance (distance == 1.0); ~inverse-distance.

### foley.weave.mix.DISTANCE_MAX_LP *: [float](https://docs.python.org/3/builtins/functions.html#float)* *= 0.85*

Air-absorption one-pole low-pass coefficient at maximum distance (0 = none, →1 = darkest).

### foley.weave.mix.LAYER_GAIN_DB *: [dict](https://docs.python.org/3/builtins/stdtypes.html#dict)[[Layer](_autosummary/foley.base.html.md#foley.base.Layer), [float](https://docs.python.org/3/builtins/functions.html#float)]* *= {Layer.ambience: -21.0, Layer.music: -21.0, Layer.sfx_fg: -9.0, Layer.stinger: -6.0, Layer.voice: 0.0}*

Base per-layer gain (dB, relative to the voice bus at 0 dB). The render applies
this before each item’s own `processing.gain_db` so levels have a sane default.

### foley.weave.mix.REVERB_IR_S *: [float](https://docs.python.org/3/builtins/functions.html#float)* *= 0.18*

Reverb-send impulse-response length (seconds) and decay time-constant.

### foley.weave.mix.apply_distance(clip, sample_rate, distance)

Apply a distance recipe: attenuation + air-absorption low-pass (report 06 §3.4).

Distance reads as a *combination* of cues: quieter (~inverse-distance) and duller
(a low-pass, as air absorbs highs). The reverb component of distance is applied
separately via [`reverb_send()`](_autosummary/foley.weave.mix.html.md#foley.weave.mix.reverb_send). `distance == 0` is a no-op.

* **Parameters:**
  * **clip** (`ndarray`) – Working array (mono or stereo).
  * **sample_rate** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Sample rate in Hz (kept for API symmetry / future filters).
  * **distance** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – 0 (near) .. 1 (far).
* **Return type:**
  `ndarray`
* **Returns:**
  The attenuated, low-passed clip (a new array; input not mutated).

### foley.weave.mix.constant_power_pan(clip, pan)

Pan a (mono or stereo) clip to a stereo image with a constant-power law (report 06 §3.3).

`L = cos(θ)`, `R = sin(θ)` with `θ = (pan+1)·π/4` and `pan ∈ [-1, 1]`, so
`L² + R²` (perceived loudness) stays flat across the field — no ~3 dB centre dip.

* **Parameters:**
  * **clip** (`ndarray`) – Mono `(frames,)` or stereo `(frames, 2)` working array.
  * **pan** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – -1 (hard left) .. 0 (centre) .. +1 (hard right).
* **Return type:**
  `ndarray`
* **Returns:**
  A stereo `(frames, 2)` array.

### foley.weave.mix.convolution_reverb(clip, sample_rate, ir, , amount)

Convolution reverb with a real (recorded or synthesised) impulse response (report 06 §3.5).

The optional upgrade over [`reverb_send()`](_autosummary/foley.weave.mix.html.md#foley.weave.mix.reverb_send)’s synthetic exponential tail: convolves
`clip` with a supplied IR (a recorded room/plate, or one generated by
`pyroomacoustics`) via FFT — unity-gain-normalised, dry/wet-blended by `amount`,
and truncated to the clip length so the placement onset is unchanged. Pure-numpy (no
heavy dependency): only *producing* an IR needs an external tool; applying one does not.
`amount == 0` or an empty IR is a no-op (fully dry).

* **Parameters:**
  * **clip** (`ndarray`) – Working array (mono or stereo).
  * **sample_rate** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Sample rate in Hz (kept for API symmetry).
  * **ir** (`ndarray`) – The impulse response (mono `(n,)` or stereo `(n, 2)`).
  * **amount** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – 0 (dry) .. 1 (fully wet).
* **Return type:**
  `ndarray`
* **Returns:**
  The dry/wet-blended clip (a new float32 array; input not mutated).

### foley.weave.mix.db_to_lin(db)

Convert decibels to a linear amplitude factor (`10 ** (db/20)`).

* **Return type:**
  [`float`](https://docs.python.org/3/builtins/functions.html#float)

### foley.weave.mix.declick(clip, sample_rate, , fade_in=0.008, fade_out=0.012)

Apply short in/out fades so an edit point produces no click (report 06 §3.6).

Thin wrapper over [`foley.audio.fade()`](_autosummary/foley.audio.html.md#foley.audio.fade) (linear ramps), sized in seconds.

* **Return type:**
  `ndarray`

### foley.weave.mix.equal_power_crossfade(a, b, overlap_samples)

Concatenate `a` then `b` with an equal-power crossfade over `overlap_samples` (report 06 §3.6).

The tail of `a` and head of `b` are ramped with `cos`/`sin` gains so
perceived loudness stays flat across the seam (no ~3 dB dip). Used for seamless
bed loops and ambience swaps.

* **Parameters:**
  * **a** (`ndarray`) – The leading clip (mono or stereo).
  * **b** (`ndarray`) – The trailing clip (same channel layout as `a`).
  * **overlap_samples** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Crossfade length in samples (clamped to both clip lengths).
* **Return type:**
  `ndarray`
* **Returns:**
  The crossfaded concatenation `(len(a) + len(b) - overlap,)` frames.

### foley.weave.mix.fit_duration(clip, sample_rate, , duration=None, loop=False, crossfade_s=0.05, stretch=False)

Fit `clip` to `duration` seconds — seamless loop (beds) or trim/pass-through (report 06 §4).

`duration is None` returns the clip unchanged (a one-shot plays once). When
`loop` and the clip is shorter than `duration`, it is tiled and equal-power
crossfaded at the seam to fill the span; anything longer than `duration` is
trimmed. A non-looping short clip is returned as-is (it plays once). When `stretch`,
the clip is pitch-preservingly time-stretched to exactly fill `duration` (via
[`time_stretch()`](_autosummary/foley.weave.mix.html.md#foley.weave.mix.time_stretch)), falling back to loop/trim if `rubberband` is unavailable.

* **Parameters:**
  * **clip** (`ndarray`) – Working array (mono or stereo).
  * **sample_rate** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Sample rate in Hz.
  * **duration** ([`float`](https://docs.python.org/3/builtins/functions.html#float) | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Target duration in seconds (`None` = full clip length).
  * **loop** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Seamless-loop to fill `duration` when shorter.
  * **crossfade_s** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Equal-power crossfade length at each loop seam.
  * **stretch** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – Pitch-preserving time-stretch to fill `duration` (needs `rubberband`).
* **Return type:**
  `ndarray`
* **Returns:**
  The duration-fitted clip.

### foley.weave.mix.overlay(bus, clip, onset_samples)

Add `clip` into `bus` at `onset_samples` (report 06 §6.4 `overlay`).

Sums (never replaces) into a copy of the bus; a clip whose tail would exceed the
bus length is truncated to fit (the mix length equals the narration length).

* **Parameters:**
  * **bus** (`ndarray`) – The destination layer bus (stereo `(frames, 2)`).
  * **clip** (`ndarray`) – The (mono or stereo) clip to place.
  * **onset_samples** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Start offset in samples (clamped at 0).
* **Return type:**
  `ndarray`
* **Returns:**
  A new bus with the clip summed in.

### foley.weave.mix.reverb_send(clip, sample_rate, amount)

Mix a pure-numpy exponential-decay reverb tail into `clip` (report 06 §3.5).

A deterministic dry/wet blend: the clip is convolved with a short
exponentially-decaying impulse response and mixed back at `amount`. This is the
zero-dependency default; `pyroomacoustics` / recorded-IR convolution is the
optional `foley[weave]` upgrade. `amount == 0` is a no-op (fully dry).

* **Parameters:**
  * **clip** (`ndarray`) – Working array (mono or stereo).
  * **sample_rate** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Sample rate in Hz.
  * **amount** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – 0 (dry) .. 1 (fully wet).
* **Return type:**
  `ndarray`
* **Returns:**
  The dry/wet-blended clip (a new array; input not mutated).

### foley.weave.mix.speech_duck_gain(n_samples, sample_rate, speech_spans, , duck_db=-10.0, attack=0.02, release=0.3)

Build a (linear) gain envelope that dips to `duck_db` during speech spans (report 06 §3.2).

A one-pole attack/release smooths the transitions so the bed dips and recovers
without clicks. Deterministic and testable — the envelope-follower alternative to
`ffmpeg sidechaincompress`.

* **Parameters:**
  * **n_samples** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Length of the bed bus in samples.
  * **sample_rate** ([`int`](https://docs.python.org/3/builtins/functions.html#int)) – Sample rate in Hz.
  * **speech_spans** ([`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`float`](https://docs.python.org/3/builtins/functions.html#float), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]) – `[(start_s, end_s), ...]` from the word timeline.
  * **duck_db** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Attenuation depth during speech (negative dB).
  * **attack** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Attack time constant (seconds) as the bed drops.
  * **release** ([`float`](https://docs.python.org/3/builtins/functions.html#float)) – Release time constant (seconds) as the bed recovers.
* **Return type:**
  `ndarray`
* **Returns:**
  A `(n_samples,)` linear gain envelope in `[db_to_lin(duck_db), 1.0]`.

### foley.weave.mix.time_stretch(clip, sample_rate, , rate)

Pitch-preserving time-stretch by `rate` via `rubberband` (report 06 §4, optional).

`rate > 1` shortens (plays faster), `< 1` lengthens — so fitting a clip of `n`
samples to a `target` span uses `rate = n / target`. Lazy: needs `pyrubberband`
(which shells out to the `rubberband` binary, a WEAVE system requirement); raises
[`RuntimeError`](https://docs.python.org/3/builtins/exceptions.html#RuntimeError) when unavailable so callers (e.g. [`fit_duration()`](_autosummary/foley.weave.mix.html.md#foley.weave.mix.fit_duration)) fall back
to loop/trim. Preserves the channel layout.

* **Return type:**
  `ndarray`


# _autosummary/foley.weave.protocols.html.md

# foley.weave.protocols

The structural DI seams of the WEAVE stage — `Aligner` / `ApplyStrategy`.

Two `@runtime_checkable` [`typing.Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)s (PEP 544), each a
behaviour-free, open-closed contract that every implementation (the deterministic
fake *and* the heavy real impl) satisfies. They are dependency-injected into
`foley.weave.render()` / `foley.weave.weave()` by keyword
(`aligner=` / `apply_strategy=`), defaulting to a hermetic fake / pure-numpy
impl when the heavy extra (`foley[align]`) is absent — mirroring
[`foley.agent.protocols`](_autosummary/foley.agent.protocols.html.md#module-foley.agent.protocols) exactly.

Stdlib-only: the `base` shapes are imported under `TYPE_CHECKING` only, so
importing this module pulls no heavy dependency and keeps `import foley` and
`import foley.weave` dol-only.

### Classes

| [`Aligner`](_autosummary/foley.weave.protocols.html.md#foley.weave.protocols.Aligner)(\*args, \*\*kwargs)       | Narration audio + its transcript → word-level timestamps (forced alignment).   |
|------------------------------------------------------------------------------------|--------------------------------------------------------------------------------|
| [`ApplyStrategy`](_autosummary/foley.weave.protocols.html.md#foley.weave.protocols.ApplyStrategy)(\*args, \*\*kwargs) | How ONE hydrated item's clip is placed onto its layer bus (report 10 §4.2).    |

### *class* foley.weave.protocols.Aligner(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

Narration audio + its transcript → word-level timestamps (forced alignment).

Returns a `word_timeline`: a list of `{'word': str, 'start': float, 'end':
float}` dicts (seconds), the raw material the anchor heuristics (report 06
§2.4) turn into sample onsets. The default is the deterministic,
torch-free [`FakeAligner`](_autosummary/foley.weave.align.html.md#foley.weave.align.FakeAligner) (evenly spaces the
transcript’s words across the clip); [`WhisperXAligner`](_autosummary/foley.weave.align.html.md#foley.weave.align.WhisperXAligner)
is the real ≈±50 ms impl behind `foley[align]`.

### *class* foley.weave.protocols.ApplyStrategy(\*args, \*\*kwargs)

Bases: [`Protocol`](https://docs.python.org/3/library/typing.html#typing.Protocol)

How ONE hydrated item’s clip is placed onto its layer bus (report 10 §4.2).

The seam that lets the render swap placement policy without touching the
render loop: the default `FullRender` applies the
full per-item DSP chain (fit-duration → gain/pan/distance/reverb → declick →
overlay), while `PlaceOnly` does a dry declicked
overlay (fast preview / DSP-free fallback). `item` is already anchor-resolved
(`item.placement.onset` is in seconds); `bus` and `clip` are stereo
`float32` working arrays at `sample_rate`.


# _autosummary/foley.weave.reaction.html.md

# foley.weave.reaction

Audience-reaction timing — a crowd answers an event, it does not coincide with it.

The mistake this module exists to prevent: syncing a cheer, laugh or applause clip to the
*instant of the on-screen event*. A real audience needs time to see, recognise and react,
so the reaction must start **after** the event has become legible, then swell, then decay.
Placing it on the event reads as a laugh track pasted on the cut; placing it after reads
as a room reacting.

Pure and stdlib-only. The numbers are working craft defaults (see [`REACTIONS`](_autosummary/foley.weave.reaction.html.md#foley.weave.reaction.REACTIONS)), not
measurements: published latency data for audience laughter/applause is thin, and the
figures here are the conventional sound-editing ones (a beat of recognition, a swell of a
second or so, a tail that thins out). Tune them per scene with `latency=`.

Rules encoded here (each is one keyword or field):

* **Legibility, not occurrence.** `legible_at` is when the audience can *see* the event
  (an entrance when the figure is mostly on screen, a reveal when the new face has settled),
  and the latency is added to that, never to the start of the motion.
* **Rise, then fall.** A reaction has an attack (`rise_s`) to `peak` and a `decay_s`
  tail; `gain_points` returns the envelope as `(t, gain)` breakpoints.
* **Second waves are smaller.** A repeat of the same reaction peaks lower
  ([`REPEAT_PEAK`](_autosummary/foley.weave.reaction.html.md#foley.weave.reaction.REPEAT_PEAK)), so cheers “die down” and bump again rather than climb forever.
* **Cut a crowd with a hush.** [`hush_plan()`](_autosummary/foley.weave.reaction.html.md#foley.weave.reaction.hush_plan) places a “shh” after the applause has run
  its course and ducks the applause under it, so the characters can sleep.

### Module Attributes

| [`REACTIONS`](_autosummary/foley.weave.reaction.html.md#foley.weave.reaction.REACTIONS)    | Working defaults per reaction kind (seconds).                                                                                                            |
|---------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`REPEAT_PEAK`](_autosummary/foley.weave.reaction.html.md#foley.weave.reaction.REPEAT_PEAK)  | Peak multiplier for each successive repeat of the same kind in one scene.                                                                                |
| [`HUSH_AFTER_S`](_autosummary/foley.weave.reaction.html.md#foley.weave.reaction.HUSH_AFTER_S) | Seconds the applause is allowed to run at full before a hush, and how long the hush takes to bring the room down (the applause is ducked, not hard-cut). |

### Functions

| [`hush_plan`](_autosummary/foley.weave.reaction.html.md#foley.weave.reaction.hush_plan)(applause, \*[, after])                   | Where a "shh" goes to quiet `applause`, and the applause cue ducked to match.   |
|-----------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------|
| [`reaction_cue`](_autosummary/foley.weave.reaction.html.md#foley.weave.reaction.reaction_cue)(kind, legible_at, \*[, latency, ...]) | Place a `kind` response to an event that became legible at `legible_at`.        |
| [`reaction_plan`](_autosummary/foley.weave.reaction.html.md#foley.weave.reaction.reaction_plan)(events, \*[, table])                 | Place `[(kind, legible_at), ...]`, shrinking repeats of a kind automatically.   |

### Classes

| [`Reaction`](_autosummary/foley.weave.reaction.html.md#foley.weave.reaction.Reaction)(latency_s, rise_s, decay_s[, peak])   | How one kind of crowd response is shaped, relative to the event that triggers it.   |
|-------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------|
| [`ReactionCue`](_autosummary/foley.weave.reaction.html.md#foley.weave.reaction.ReactionCue)(kind, event_t, onset, peak_t, ...) | One placed crowd response (times are shot-local seconds).                           |

### foley.weave.reaction.HUSH_AFTER_S *: [float](https://docs.python.org/3/builtins/functions.html#float)* *= 1.2*

Seconds the applause is allowed to run at full before a hush, and how long the hush
takes to bring the room down (the applause is ducked, not hard-cut).

### foley.weave.reaction.REACTIONS *: [Mapping](https://docs.python.org/3/library/typing.html#typing.Mapping)[[str](https://docs.python.org/3/builtins/stdtypes.html#str), [Reaction](_autosummary/foley.weave.reaction.html.md#foley.weave.reaction.Reaction)]* *= {'applause': Reaction(latency_s=0.55, rise_s=1.1, decay_s=2.5, peak=0.9), 'cheer': Reaction(latency_s=0.5, rise_s=0.9, decay_s=3.0, peak=1.0), 'gasp': Reaction(latency_s=0.2, rise_s=0.15, decay_s=0.6, peak=0.7), 'laugh': Reaction(latency_s=0.6, rise_s=0.5, decay_s=1.8, peak=0.9), 'whistle': Reaction(latency_s=0.45, rise_s=0.05, decay_s=0.5, peak=0.8)}*

Working defaults per reaction kind (seconds). Overridable per call.

### foley.weave.reaction.REPEAT_PEAK *: [float](https://docs.python.org/3/builtins/functions.html#float)* *= 0.65*

Peak multiplier for each successive repeat of the same kind in one scene.

### *class* foley.weave.reaction.Reaction(latency_s, rise_s, decay_s, peak=1.0)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

How one kind of crowd response is shaped, relative to the event that triggers it.

### *class* foley.weave.reaction.ReactionCue(kind, event_t, onset, peak_t, end, peak)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

One placed crowd response (times are shot-local seconds).

#### gain_points()

Envelope breakpoints `(t, gain)`: silence -> peak -> silence.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`float`](https://docs.python.org/3/builtins/functions.html#float), [`float`](https://docs.python.org/3/builtins/functions.html#float)]]

### foley.weave.reaction.hush_plan(applause, , after=None)

Where a “shh” goes to quiet `applause`, and the applause cue ducked to match.

Returns `(shh_onset, ducked_applause)`. The hush lands `HUSH_AFTER_S` after the
applause peak (or at `after`), and the applause fades out over `HUSH_DUCK_S` from
then, so the crowd is *answering* the hush rather than being cut.

* **Return type:**
  [`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`float`](https://docs.python.org/3/builtins/functions.html#float), [`ReactionCue`](_autosummary/foley.weave.reaction.html.md#foley.weave.reaction.ReactionCue)]

```pycon
>>> t, a = hush_plan(reaction_cue("applause", 8.0))
>>> a.end > t
True
```

### foley.weave.reaction.reaction_cue(kind, legible_at, , latency=None, repeat=0, table={'applause': Reaction(latency_s=0.55, rise_s=1.1, decay_s=2.5, peak=0.9), 'cheer': Reaction(latency_s=0.5, rise_s=0.9, decay_s=3.0, peak=1.0), 'gasp': Reaction(latency_s=0.2, rise_s=0.15, decay_s=0.6, peak=0.7), 'laugh': Reaction(latency_s=0.6, rise_s=0.5, decay_s=1.8, peak=0.9), 'whistle': Reaction(latency_s=0.45, rise_s=0.05, decay_s=0.5, peak=0.8)})

Place a `kind` response to an event that became legible at `legible_at`.

`repeat` is how many earlier responses of this kind the scene already had; each one
scales the peak by [`REPEAT_PEAK`](_autosummary/foley.weave.reaction.html.md#foley.weave.reaction.REPEAT_PEAK).

* **Return type:**
  [`ReactionCue`](_autosummary/foley.weave.reaction.html.md#foley.weave.reaction.ReactionCue)

```pycon
>>> c = reaction_cue("cheer", 1.0)
>>> c.onset > c.event_t
True
>>> reaction_cue("cheer", 6.0, repeat=1).peak < reaction_cue("cheer", 1.0).peak
True
```

### foley.weave.reaction.reaction_plan(events, , table={'applause': Reaction(latency_s=0.55, rise_s=1.1, decay_s=2.5, peak=0.9), 'cheer': Reaction(latency_s=0.5, rise_s=0.9, decay_s=3.0, peak=1.0), 'gasp': Reaction(latency_s=0.2, rise_s=0.15, decay_s=0.6, peak=0.7), 'laugh': Reaction(latency_s=0.6, rise_s=0.5, decay_s=1.8, peak=0.9), 'whistle': Reaction(latency_s=0.45, rise_s=0.05, decay_s=0.5, peak=0.8)})

Place `[(kind, legible_at), ...]`, shrinking repeats of a kind automatically.

* **Return type:**
  [`list`](https://docs.python.org/3/builtins/stdtypes.html#list)[[`ReactionCue`](_autosummary/foley.weave.reaction.html.md#foley.weave.reaction.ReactionCue)]

```pycon
>>> [c.kind for c in reaction_plan([("cheer", 1.0), ("laugh", 4.0), ("cheer", 6.0)])]
['cheer', 'laugh', 'cheer']
```


# _autosummary/foley.weave.requirements.html.md

# foley.weave.requirements

System-dependency onboarding for WEAVE’s optional upgrade paths (accompy-style).

`ffmpeg` (the two-pass `loudnorm` “guarantee the numbers” master, report 06 §5.4)
and `rubberband` (time-stretch / pitch for loop fitting) are **system** binaries,
never pip dependencies — the default WEAVE path is pure-numpy + `pyloudnorm` and
degrades gracefully when they are absent. This module is the SSOT for what those
binaries are, how to detect them (`shutil.which`), and how to install them per
platform; it mirrors accompy’s `check_requirements` progressive-disclosure
onboarding. Stdlib-only, so importing it keeps `import foley.weave` dol-only.

### Module Attributes

| [`REQUIREMENTS`](_autosummary/foley.weave.requirements.html.md#foley.weave.requirements.REQUIREMENTS)   | The SSOT of WEAVE's optional system dependencies.   |
|-----------------------------------------------------------------|-----------------------------------------------------|

### Functions

| [`check_requirements`](_autosummary/foley.weave.requirements.html.md#foley.weave.requirements.check_requirements)(\*[, names, verbose])   | Report which optional WEAVE system binaries are available (`shutil.which`).   |
|---------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------|
| [`verify_and_setup`](_autosummary/foley.weave.requirements.html.md#foley.weave.requirements.verify_and_setup)(\*[, names])              | Check the optional system deps and return a per-dep status + guidance report. |

### Classes

| [`Requirement`](_autosummary/foley.weave.requirements.html.md#foley.weave.requirements.Requirement)(name, purpose, url, install[, probe])   | A single optional dependency: what it is, how to get it, why foley wants it.   |
|------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------|

### foley.weave.requirements.REQUIREMENTS *: [dict](https://docs.python.org/3/builtins/stdtypes.html#dict)[[str](https://docs.python.org/3/builtins/stdtypes.html#str), [Requirement](_autosummary/foley.weave.requirements.html.md#foley.weave.requirements.Requirement)]* *= {'ffmpeg': Requirement(name='ffmpeg', purpose="two-pass loudnorm 'guarantee-the-numbers' master (report 06 §5.4)", url='https://ffmpeg.org/download.html', install={'darwin': 'brew install ffmpeg', 'linux': 'sudo apt-get install -y ffmpeg', 'win32': 'winget install --id=Gyan.FFmpeg -e'}, probe='binary'), 'rubberband': Requirement(name='rubberband', purpose='high-quality time-stretch / pitch-shift for loop fitting', url='https://breakfastquay.com/rubberband/', install={'darwin': 'brew install rubberband', 'linux': 'sudo apt-get install -y rubberband-cli', 'win32': 'download from https://breakfastquay.com/rubberband/'}, probe='binary')}*

The SSOT of WEAVE’s optional system dependencies. Both are opt-in upgrades; the
bare install renders + masters entirely in-process, so neither is required.

### *class* foley.weave.requirements.Requirement(name, purpose, url, install, probe='binary')

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

A single optional dependency: what it is, how to get it, why foley wants it.

`probe` selects how availability is checked (default `'binary'` via
`shutil.which` — WEAVE’s system binaries): `'binary'` | `'env'` (an
environment variable is set) | `'importable'` (a module is importable). The
generalized [`foley.requirements`](_autosummary/foley.requirements.html.md#module-foley.requirements) onboarding dispatches on it.

### foley.weave.requirements.check_requirements(, names=None, verbose=False)

Report which optional WEAVE system binaries are available (`shutil.which`).

* **Parameters:**
  * **names** ([`tuple`](https://docs.python.org/3/builtins/stdtypes.html#tuple)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`...`](https://docs.python.org/3/builtins/constants.html#Ellipsis)] | [`None`](https://docs.python.org/3/builtins/constants.html#None)) – Which requirements to check (default: all of [`REQUIREMENTS`](_autosummary/foley.weave.requirements.html.md#foley.weave.requirements.REQUIREMENTS)).
  * **verbose** ([`bool`](https://docs.python.org/3/builtins/functions.html#bool)) – If `True`, print an install hint for each missing binary.
* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`bool`](https://docs.python.org/3/builtins/functions.html#bool)]
* **Returns:**
  `{name: is_available}`. All-absent is fine — WEAVE degrades to its
  in-process path; the report just tells the user what each binary would unlock.

### foley.weave.requirements.verify_and_setup(, names=None)

Check the optional system deps and return a per-dep status + guidance report.

Does **not** run installers (system-binary installs need the user’s consent and
sudo). Surfaces the exact per-platform command so the user can opt in.

* **Return type:**
  [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)[[`str`](https://docs.python.org/3/builtins/stdtypes.html#str), [`dict`](https://docs.python.org/3/builtins/stdtypes.html#dict)]
* **Returns:**
  `{name: {'available': bool, 'purpose': str, 'install': str, 'url': str}}`.


# _autosummary/foley.weave.timeline.html.md

# foley.weave.timeline

The sound-design timeline as an editable document — hydrate, edit, caption.

The timeline is the SSOT and the reproducible seed; `foley.weave.render()` is a
pure projection of it. This module owns the *document* operations, all pure and
stdlib-only (no numpy, no I/O):

* [`hydrate()`](_autosummary/foley.weave.timeline.html.md#foley.weave.timeline.hydrate) — resolve each item’s symbolic anchor into a concrete
  [`Placement`](_autosummary/foley.base.html.md#foley.base.Placement) and fill processing/id defaults, **idempotently**
  > (`hydrate ∘ hydrate == hydrate`) and without clobbering a hand-set placement.
* Pure edit transforms ([`swap_clip()`](_autosummary/foley.weave.timeline.html.md#foley.weave.timeline.swap_clip), [`nudge()`](_autosummary/foley.weave.timeline.html.md#foley.weave.timeline.nudge), [`set_gain()`](_autosummary/foley.weave.timeline.html.md#foley.weave.timeline.set_gain),
  [`toggle()`](_autosummary/foley.weave.timeline.html.md#foley.weave.timeline.toggle), [`set_master()`](_autosummary/foley.weave.timeline.html.md#foley.weave.timeline.set_master)) — each returns a NEW timeline, so an edit +
  > re-render reproduces exactly that change (the “editable, re-renderable” DoD).
* Accessibility **SDH captions** ([`to_webvtt()`](_autosummary/foley.weave.timeline.html.md#foley.weave.timeline.to_webvtt) / [`to_srt()`](_autosummary/foley.weave.timeline.html.md#foley.weave.timeline.to_srt)) — bracketed
  SFX labels (`[door creak]`) derived from the *items*, never from the narration
  transcript, so no speech text leaks into a shipped caption file.

### Module Attributes

| [`DISPLAY_WINDOW_S`](_autosummary/foley.weave.timeline.html.md#foley.weave.timeline.DISPLAY_WINDOW_S)   | How long a one-shot's caption stays on screen (seconds) when the item has no resolved duration of its own.   |
|---------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------|

### Functions

| [`hydrate`](_autosummary/foley.weave.timeline.html.md#foley.weave.timeline.hydrate)(timeline[, word_timeline])         | Resolve every item's anchor + fill processing/id, returning a NEW timeline.         |
|---------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------|
| [`nudge`](_autosummary/foley.weave.timeline.html.md#foley.weave.timeline.nudge)(timeline, item_id, delta_s)          | Shift an item's resolved onset by `delta_s` seconds — a NEW timeline.               |
| [`set_gain`](_autosummary/foley.weave.timeline.html.md#foley.weave.timeline.set_gain)(timeline, item_id, gain_db)       | Set an item's processing gain (dB, voice-relative) — a NEW timeline.                |
| [`set_master`](_autosummary/foley.weave.timeline.html.md#foley.weave.timeline.set_master)(timeline, master)               | Set the timeline's master profile (name or `MasterProfile`) — a NEW timeline.       |
| [`swap_clip`](_autosummary/foley.weave.timeline.html.md#foley.weave.timeline.swap_clip)(timeline, item_id, new_clip_ref) | Swap an item's clip (keeping its placement/processing) — a NEW timeline.            |
| [`to_srt`](_autosummary/foley.weave.timeline.html.md#foley.weave.timeline.to_srt)(timeline, \*[, window_s])           | Render the timeline's SFX cues as an SRT SDH caption file (report 06 / issue #8).   |
| [`to_webvtt`](_autosummary/foley.weave.timeline.html.md#foley.weave.timeline.to_webvtt)(timeline, \*[, window_s])        | Render the timeline's SFX cues as a WebVTT SDH caption file (report 06 / issue #8). |
| [`toggle`](_autosummary/foley.weave.timeline.html.md#foley.weave.timeline.toggle)(timeline, item_id, enabled)         | Non-destructively mute/unmute an item — a NEW timeline.                             |

### foley.weave.timeline.DISPLAY_WINDOW_S *: [float](https://docs.python.org/3/builtins/functions.html#float)* *= 2.0*

How long a one-shot’s caption stays on screen (seconds) when the item has no
resolved duration of its own.

### foley.weave.timeline.hydrate(timeline, word_timeline=None)

Resolve every item’s anchor + fill processing/id, returning a NEW timeline.

Idempotent: an item that already carries a [`Placement`](_autosummary/foley.base.html.md#foley.base.Placement) keeps
its symbolic anchor (a hand edit is never clobbered) but is re-resolved against
`word_timeline` so a re-recorded/re-aligned narration re-flows the SFX
(report 06 §6.1). `processing` falls back to the sparse `gain` and `id` to a
stable content hash. The resolved `word_timeline` is cached on the returned
timeline (the reproducible seed).

* **Parameters:**
  * **timeline** ([`SoundDesignTimeline`](_autosummary/foley.base.html.md#foley.base.SoundDesignTimeline)) – The (sparse or partially-resolved) timeline.
  * **word_timeline** ([`Optional`](https://docs.python.org/3/library/typing.html#typing.Optional)[[`list`](https://docs.python.org/3/builtins/stdtypes.html#list)]) – The forced alignment; defaults to `timeline.word_timeline`.
* **Return type:**
  [`SoundDesignTimeline`](_autosummary/foley.base.html.md#foley.base.SoundDesignTimeline)
* **Returns:**
  A hydrated copy with resolved placements, filled processing, stable ids.

### foley.weave.timeline.nudge(timeline, item_id, delta_s)

Shift an item’s resolved onset by `delta_s` seconds — a NEW timeline.

Resolves the item’s anchor against the timeline’s `word_timeline` first, then shifts
the resolved onset and pins the anchor to `absolute` so the manual offset survives a
re-hydrate. This means nudging a still-symbolic (not-yet-hydrated) item on a timeline
that carries alignment shifts from the item’s *true* resolved onset, not from 0. On a
pre-alignment timeline with no `word_timeline` a word/sentence anchor is genuinely
unresolvable, so there the shift is relative to 0 — hydrate/weave first for such items.

* **Return type:**
  [`SoundDesignTimeline`](_autosummary/foley.base.html.md#foley.base.SoundDesignTimeline)

### foley.weave.timeline.set_gain(timeline, item_id, gain_db)

Set an item’s processing gain (dB, voice-relative) — a NEW timeline.

* **Return type:**
  [`SoundDesignTimeline`](_autosummary/foley.base.html.md#foley.base.SoundDesignTimeline)

### foley.weave.timeline.set_master(timeline, master)

Set the timeline’s master profile (name or `MasterProfile`) — a NEW timeline.

* **Return type:**
  [`SoundDesignTimeline`](_autosummary/foley.base.html.md#foley.base.SoundDesignTimeline)

### foley.weave.timeline.swap_clip(timeline, item_id, new_clip_ref)

Swap an item’s clip (keeping its placement/processing) — a NEW timeline.

* **Return type:**
  [`SoundDesignTimeline`](_autosummary/foley.base.html.md#foley.base.SoundDesignTimeline)

### foley.weave.timeline.to_srt(timeline, , window_s=2.0)

Render the timeline’s SFX cues as an SRT SDH caption file (report 06 / issue #8).

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### foley.weave.timeline.to_webvtt(timeline, , window_s=2.0)

Render the timeline’s SFX cues as a WebVTT SDH caption file (report 06 / issue #8).

Each enabled, placed item becomes one `[bracketed]` cue at its resolved onset.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

### foley.weave.timeline.toggle(timeline, item_id, enabled)

Non-destructively mute/unmute an item — a NEW timeline.

* **Return type:**
  [`SoundDesignTimeline`](_autosummary/foley.base.html.md#foley.base.SoundDesignTimeline)


# about-this-build.html.md

<!-- generated by epythet -->

# About this build

This documentation was built on **2026-10-16 01:23 UTC** from commit <a href="https://github.com/reeleehq/foley/commit/e157a1cc037f2453ea186c3e0570bc3adede0dd8"><code>e157a1c</code></a> on branch <code>main</code>, for **foley 0.0.41** (from <code>pyproject.toml</code>).

#### WARNING
The documentation and the package may be misaligned:

- The documented version (0.0.41) is behind the latest release on PyPI (0.0.42): `pip install foley` gives newer code than these docs describe.

## Source

|                     |                                                                                                                                                       |
|---------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
| Commit              | <a href="https://github.com/reeleehq/foley/commit/e157a1cc037f2453ea186c3e0570bc3adede0dd8"><code>e157a1cc037f2453ea186c3e0570bc3adede0dd8</code></a> |
| Branch              | <code>main</code>                                                                                                                                     |
| Tags at this commit | none                                                                                                                                                  |
| Working tree        | clean                                                                                                                                                 |
| Remote              | <code>https://github.com/reeleehq/foley</code>                                                                                                        |

## Continuous integration

|              |                                                                                            |
|--------------|--------------------------------------------------------------------------------------------|
| Repository   | <code>reeleehq/foley</code>                                                                |
| Run          | <a href="https://github.com/reeleehq/foley/actions/runs/38834647970">38834647970</a>       |
| Ref          | <code>refs/heads/main</code>                                                               |
| Event commit | <code>e157a1cc037f2453ea186c3e0570bc3adede0dd8</code> (in the history of the built commit) |

## Tools

|          |         |
|----------|---------|
| epythet  | 0.2.17  |
| Sphinx   | 9.1.0   |
| docutils | 0.22.4  |
| Python   | 3.12.15 |

## Configuration as resolved

|               |                                                                   |
|---------------|-------------------------------------------------------------------|
| theme         | <code>auto</code> (Sphinx theme <code>pydata_sphinx_theme</code>) |
| accent        | <code>#7c397e</code>                                              |
| api_generator | <code>autosummary</code>                                          |
| ignore        | <code>tests/</code>, <code>scrap/</code>, <code>examples/</code>  |
| agent_outputs | <code>true</code>                                                 |
| aggregates    | <code>md</code>                                                   |
| ai_artifacts  | <code>true</code>                                                 |

## Package on PyPI

Latest release: <a href="https://pypi.org/project/foley/0.0.42/">0.0.42</a>, newer than the documented version (0.0.41).

## Reproduce

```bash
git clone https://github.com/reeleehq/foley && cd foley
git checkout e157a1cc037f2453ea186c3e0570bc3adede0dd8
pip install "epythet==0.2.17"
epythet quickstart . --ignore tests/ scrap/ examples/
```

The same data, for machines: <a href="build_info.json"><code>build_info.json</code></a> (schema version 1).


# ai-agents.html.md

<!-- generated by epythet -->

# For AI agents

`foley` ships artifacts for coding agents alongside its code. This page lists
them, says where each lives in the repository, and points at the
machine-readable copies of this documentation.

## Skills

Skills are folders holding a `SKILL.md` (the [Agent Skills](https://agentskills.io) format): a description that tells an agent when to use it and a body with the procedure. Install one into your agent with `gh skill` (any host: `--agent claude-code`, `copilot`, `cursor`, `codex`, `gemini`), or use the copy bundled in the wheel.

### `foley-sound-design`

Use when sound-designing a NARRATION with the `foley` package — choosing sound effects for narrated/voiced text and weaving them under the voice. Triggers on: “add sound effects to this narration”, “score this passage”, “sound-design this audiobook/podcast/video script”, “find a sound for this line”, “weave SFX into the voiceover”, “make a sound-design timeline”, or any task that pairs narration text (or a transcript) with choosing + placing sounds. Covers the find→plan→weave loop, the one-call `foley.score()`, the MCP tool surface, and the taste heuristics (restraint, layering, ducking, licensing).

```bash
gh skill install thorwhalen/foley foley-sound-design --agent claude-code
```

Source: [`foley/data/skills/foley-sound-design`](https://github.com/thorwhalen/foley/tree/HEAD/foley/data/skills/foley-sound-design) (bundled with the pip package).

### `foley-dev-add-source`

Use when adding a new sound SOURCE to foley — either a retrieve adapter (a sound-library/API such as Freesound, BBC Sound Effects, Epidemic, Storyblocks, Pond5) or a generate adapter (a text-to-SFX model/API such as Stable Audio Open, ElevenLabs Sound Effects, AudioGen). Covers the SOURCE_CONFIG plugin pattern (mirroring arioso’s per-platform config), the SourceAdapter protocol, unified query/generation vocabulary → native-param translation, lazy optional-deps, and the mandatory per-source license policy (commercial_ok / redistribute_standalone_ok / cache_bytes_ok / ai_training_ok). Triggers on adding a source, adding an adapter, wiring a new SFX API or generator into foley.

```bash
gh skill install thorwhalen/foley foley-dev-add-source --agent claude-code
```

Source: [`skills/foley-dev-add-source`](https://github.com/thorwhalen/foley/tree/HEAD/skills/foley-dev-add-source).

### `foley-dev-implement`

Use when implementing (or planning to implement) any foley epic subtask — building the SOURCE / INDEX / SELECT / WEAVE stages or the cross-cutting layers of the foley SFX façade. Covers the build loop, the module layout, the canonical data models (SoundRecord, LicenseRecord, Candidate, SoundDesignTimeline), the non-negotiable invariants (license SSOT + fail-closed gate, cache_bytes_ok vs redistribute_standalone_ok, the eval PR gate, AI disclosure), the façade discipline (mirror arioso/accompy), and the branch→PR git flow. Triggers on foley implementation, foley subtask, foley adapter/index/agent/weave work, foley base.py / data models.

```bash
gh skill install thorwhalen/foley foley-dev-implement --agent claude-code
```

Source: [`skills/foley-dev-implement`](https://github.com/thorwhalen/foley/tree/HEAD/skills/foley-dev-implement).

The bundled skills are also on disk after `pip install foley`, under the package’s `data/skills/` directory; link them into an agent without network access with `skill link-skills <that directory>`.

## Instruction files

Files agents read before working in this repository.

- [`.claude/CLAUDE.md`](https://github.com/thorwhalen/foley/tree/HEAD/.claude/CLAUDE.md): read by Claude Code

## Machine-readable documentation

This site publishes the same documentation in forms that fit an agent’s context window:

- [`llms.txt`](https://thorwhalen.github.io/foley/llms.txt): an index of every page with a one-line description ([llms.txt](https://llmstxt.org) format)
- [`foley.md`](https://thorwhalen.github.io/foley/foley.md): the whole documentation as one Markdown file
- `<page>.html.md`: a rendered Markdown twin of every page, advertised from each page’s `<head>` with `<link rel="alternate" type="text/markdown">`
- [`objects.inv`](https://thorwhalen.github.io/foley/objects.inv): the Sphinx inventory: a symbol-to-URL index (`sphobjinv convert plain objects.inv -`)


# api.html.md

# API reference

| [`foley`](_autosummary/foley.html.md#module-foley)   | foley — a retrieval-first façade for sound effects.   |
|-----------------------------------------------------------------------|-------------------------------------------------------|


