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:
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):
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,LicenseRecord,Candidate,SoundEvent,Verdict,IntendedUse), the twoaffordance registries, and generic dict/JSON (de)serialization.
License policy (
foley.licensing) — thelicense_id-> flag-set SSOT (LICENSE_FLAGS), flag derivation, and the fail-closedkeep()gate.Storage (
foley.stores) — content-addressed byte store + metadata store built fromdol, andstore_sound()(the by-value vs by-reference gate driven byLicenseRecord.cache_bytes_ok).QC (
foley.qc) — Tier-0 deterministic audio checks (run_qc()->QCReport, thresholds inQCThresholds).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 a master spec (profile name, explicit profile, or |
|
Return the display |
Map a Creative-Commons license URL or label to |
|
|
Look up the flag set for a |
|
Populate |
|
Fail-closed candidate license gate (report 07 §8.2). |
|
Convenience: apply |
|
Return the content-address key for |
|
Build the content-addressable blob store: |
|
Build the metadata store: |
|
Build the run-artifact store: |
|
Persist a sound, choosing by-value vs by-reference from |
|
Run every Tier-0 check and fold the results into a |
|
Return |
|
Clip duration in seconds: |
|
Largest per-channel absolute DC offset, |
|
Return |
|
Detect hard (flat-topped) clipping. |
|
Inter-sample true-peak level in dBTP. |
|
Estimate SNR in dB (advisory — a busy-street SFX legitimately scores low). |
|
Return |
|
Integrated loudness (LUFS, ITU-R BS.1770-4) via |
|
Decode audio into a float working array. |
|
Write |
|
Encode |
|
Resample |
|
Down-mix to mono by averaging channels; 1-D input passes through. |
|
Coerce |
|
Strip leading/trailing silence, returning the clip and its kept span. |
|
Apply in/out gain ramps to |
|
Loudness-normalize to |
|
Produce the canonical CLAP/QC working array from an arbitrary clip. |
The process-wide default library (local stores + CLAP + best index). |
|
|
Hybrid (CLAP vector ⊕ BM25) search of the default library. |
|
What a call would cost in USD before making it — |
|
Find sounds similar to a stored sound (audio<->audio) in the default library. |
Return a process-wide default |
|
|
Build the best available persistent index for a library. |
True if |
|
True if |
|
|
Embed |
|
Pure audio<->audio (or clip->library) vector search — no keyword leg. |
|
Fuse several ranked id lists into one, by reciprocal rank. |
|
RRF-fuse a vector ranker's hits with a keyword ranker's hits. |
|
Resolve inputs to a best UCS CatID by the staged precedence. |
|
Parse a UCS-conformant filename to |
The default supervised tagger (PANNs CNN14; |
|
|
The default zero-shot tagger (CLAP vs UCS subcategories; |
|
Ingest a folder (or single file) of sounds into the default library. |
|
Re-stamp the licence of stored sounds; a dry run unless |
|
Ingest one clip into |
|
Ingest every audio file under |
|
Seed |
|
Ingest the bundled Ring-0 fixture and run one search — the smoke test. |
|
Search a live |
|
Return the names of registered live sources (runs discovery first). |
|
Register a live source directly (out-of-tree plugin or a test double). |
|
Generate a sound effect for |
|
Wrap a stored |
|
Return the per-clip EU AI Act Art. |
|
Scan a generation |
|
The headline: a narrative context → verified, license-clean sound candidates. |
|
Fold verified candidates into the SPARSE |
|
Decompose a passage into |
|
Expand |
|
Verify |
|
The single generate-vs-retrieve branch — a PURE function (report 05 §4). |
|
Choose sounds for narration text and (optionally) weave them into the narration audio. |
|
Copy the shipped skill + slash command + subagent into |
|
Build the foley MCP server (lazy |
|
Build the foley MCP server (lazy |
|
Build a bearer-auth-gated ASGI app serving the foley MCP tools over streamable HTTP. |
|
Build and serve the foley MCP tools over authenticated streamable HTTP (blocks). |
|
Produce a short audition of a sound; set its |
|
"More like this" — neighbours of a sound id / candidate, or of a raw clip. |
|
Relevance-feedback refinement: expand for recall, boost picks, drop rejects, re-rank. |
|
Build a per-session JSON store: |
|
Alias of |
Whether an offline runtime scope is currently active. |
|
|
Bind per-request source accounts (a key, and what goes with it) for a |
|
Report which optional foley capabilities are available ( |
|
Return a per-requirement status + guidance report (never runs an installer). |
|
A JSON-safe capability + posture snapshot for the CLI, docs, and the MCP tool. |
|
Run the Tier-1 retrieval eval over the golden set (nDCG@10 / recall / mAP / MRR). |
|
Run the Tier-2 fit eval over the golden set — "does the accepted clip fit?" (#10b). |
|
Build the TASL attribution |
|
Build the deduplicated |
|
Render one sound's TASL attribution line. |
|
Build a |
Classes
|
How a sound's bytes are held (DERIVED from |
|
How a sound entered foley (retrieval channel or origin). |
|
Whether a candidate was retrieved from the index or freshly generated. |
|
How prominent a sound event is within a passage. |
|
Mix layer (shared by |
|
Which rung of the verification ladder produced a |
|
Descriptor for a unified parameter affordance (arioso analog). |
Adds |
|
|
Per-sound rights + provenance. |
|
Canonical SSOT per sound. |
|
One salient, physically-audible event decomposed from a passage. |
|
The result of one verification rung for a candidate. |
|
A ranked, license-checked, (optionally) verified sound for one SoundEvent. |
|
What the caller intends to do with a sound; consumed by |
|
One placed sound on the sound-design timeline — sparse seed + resolved render fields. |
|
The editable, re-renderable sound-design plan — the SELECT→WEAVE bridge and render SSOT. |
|
How a WEAVE |
|
WHERE/WHEN a clip sits — a symbolic anchor plus its resolved time (report 06 §6.3). |
|
HOW a clip sounds — all optional with identity defaults (report 06 §3, §6.3). |
|
Loudness master target — the delivery spec as data, not code (report 06 §5.2). |
|
The eight derivable flags for one |
|
Human-facing display metadata for one |
|
Overall verdict for a clip (subclasses |
|
All Tier-0 QC defaults (report 08 §3 table), each an explicit field. |
|
Per-clip Tier-0 QC result. |
|
A searchable, license-aware library of sounds (the foley INDEX façade). |
|
A joint text<->audio embedding space (CLAP by default). |
|
LAION-CLAP text<->audio embedder (the default retrieval engine). |
|
Approximate-nearest-neighbour store over embedding vectors. |
|
BM25 / full-text index over each sound's tags + caption. |
|
In-memory vector + keyword index (numpy cosine + compact BM25). |
|
LanceDB-backed index: one table with a vector column + a native FTS index. |
|
Single-file index: sqlite-vec |
|
One fused retrieval hit: an id plus the scores that produced it. |
|
The result of resolving free tags/caption/labels to a UCS CatID. |
|
Map a clip to |
|
Produce one natural-language sentence describing a clip (report 03). |
|
Zero-shot tagger: score a clip against a label set via CLAP cosine. |
|
PANNs CNN14 supervised tagger over the 527 AudioSet classes ( |
|
The outcome of ingesting one clip. |
|
The rolled-up outcome of a folder ingest (JSON-serializable). |
|
The reproducible run-artifact for one foley operation (trace ⊕ plan ⊕ provenance). |
|
One node of the run's span tree (the trace half of the artifact). |
|
One rung of the verify ladder: does this candidate match this event? (report 10 §4.2). |
|
Bounded-cost accounting: per-event loop counts, and a cumulative dollar cap (#57). |
|
The tiny result of |
|
The output of |
|
The output of |
|
One chosen sound placed for a narration event (a JSON-friendly rationale row). |
|
Three namespaced stores for one audition session (candidates / picks / rejects). |
|
A frozen runtime posture — the local-first / offline contract as data. |
|
A deduplicated, ordered collection of |
|
One rendered TASL credit for a single sound (a flat, serializable row). |
Exceptions
|
Raised by |
|
Raised (fail-closed) when a generation prompt trips a #9b safety gate. |
|
A |
|
A |
- class foley.AcquisitionMethod(*values)[source]#
-
How a sound entered foley (retrieval channel or origin).
- class foley.Affordance(name, type, description, default=None, stage='query', carries_meaning=False)[source]#
Bases:
objectDescriptor 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)[source]#
-
How a WEAVE
Placementbinds its symbolic time to the narration (report 06 §2.4).absoluteis a fixed offset; the rest resolve against the forced-alignedword_timeline—wordto a trigger word’s onset,sentenceacross a sentence span,scene/paragraphto 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>)[source]#
Bases:
objectBounded-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 callsrefine_ok()/gen_ok()to test, thenspend_refine()/spend_gen()to charge;reset()zeroes them at each event.Per run, cumulative —
max_usd(default $1): every paid call (generation, paid LLM rung) is checked withauthorize()before it is made and recorded withcharge()after.reset()never clears it, so the cap holds across the wholefind/scorerun. A call whose cost is unknown (estimateNone) is refused unlessapprove_unknown_cost(or$FOLEY_APPROVE_UNKNOWN_COST=1).
- authorize(estimate_usd, *, what)[source]#
Alias of
check()(kept for callers that only test affordability).- Return type:
- check(estimate_usd, *, what)[source]#
Raise if this run cannot afford
estimate_usd(nothing is reserved).- Return type:
- reserve(estimate_usd, *, what)[source]#
Check, then count
estimate_usdas 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()later replaces the reservation with the actual cost, if known.- Return type:
- reset()[source]#
Zero the spend counters so the caps apply per event, not per passage.
The
findloop 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:
- 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)[source]#
Bases:
SerializableMixinA ranked, license-checked, (optionally) verified sound for one SoundEvent.
Retrieval and generation return the SAME shape; only
origindiffers. Nestedsound/event/verdictdataclasses are decoded generically by_decode()— no per-field code needed.
- class foley.CandidateOrigin(*values)[source]#
-
Whether a candidate was retrieved from the index or freshly generated.
- class foley.Captioner(*args, **kwargs)[source]#
Bases:
ProtocolProduce 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.
- class foley.CatIdResolution(catid=None, category=None, subcategory=None, source=None, confidence=0.0, matched_terms=<factory>)[source]#
Bases:
objectThe result of resolving free tags/caption/labels to a UCS CatID.
catidfeedsucs_categoryandsubcategoryfeedsucs_subcategoryon ingest, anducs_catidon the query side.
- class foley.ClapEmbedder(model_id='laion/larger_clap_general', *, device=None)[source]#
Bases:
objectLAION-CLAP text<->audio embedder (the default retrieval engine).
Returns L2-normalized
float32embeddings so a plain inner product is cosine similarity.embed_textalways returns a 2-D(n, dim)array;embed_audioreturns 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 dim: int#
The embedding dimensionality (512 for the default; resolved for others).
For a non-default checkpoint this fetches only the model’s
config.json(viaAutoConfig) — 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 lacksprojection_dim.
- embed_audio(wav, sr)[source]#
Embed one audio clip ->
(dim,)L2-normalized.The clip is down-mixed to mono and resampled to 48 kHz (what CLAP expects) via
foley.audiobefore embedding.- Parameters:
wav (
ndarray) – A working-array clip (float32; mono or multichannel).sr (
int) – The clip’s sample rate in Hz.
- Return type:
ndarray
- class foley.ClapZeroShotTagger(*, embedder=None, labels=None, prompt='this is a sound of {label}', threshold=0.0)[source]#
Bases:
objectZero-shot tagger: score a clip against a label set via CLAP cosine.
Reuses a
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[str]#
natural UCS
category subcategoryphrases).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
- tag(wav, sr, *, taxonomy='custom', top_k=10)[source]#
Return the top-
k(label, cosine)tags for the clip, best first.
- 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)[source]#
Bases:
SerializableMixinOne rendered TASL credit for a single sound (a flat, serializable row).
Built by
credit_entry()from a record’s rights fields; rendered to a single attribution line byattribution_line(). Carries the AI-disclosurewatermark / C2PA fields as pass-throughs so the JSON manifest becomes the content-credentials carrier once #6/#9b populate them (
Nonetoday).
- class foley.Credits(entries=(), title='Credits', schema_version=1)[source]#
Bases:
SerializableMixinA deduplicated, ordered collection of
CreditEntryfor one run.Iterable and sized; renders to
CREDITS.mdviamarkdownand to a JSON manifest viamanifest(==to_dict()). Both are deterministic (no timestamps) and diffable.
- class foley.Decision(action, candidate=None, reason='')[source]#
Bases:
objectThe tiny result of
decide();reasonfeeds the refine hint + the audit Step.
- class foley.Embedder(*args, **kwargs)[source]#
Bases:
ProtocolA joint text<->audio embedding space (CLAP by default).
One space serves both text->audio search (
embed_texta query) and audio<->audio similarity (embed_audioa clip). Implementations MUST return L2-normalizedfloat32arrays so a plain inner product is cosine similarity, and MUST stampmodel_id/dimso mixed-model libraries stay coherent (eachSoundRecordrecords theembedding_model/embedding_dimit 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)[source]#
Embed one audio clip.
- Parameters:
wav (
ndarray) – A working-array clip (float32, mono preferred). CLAP expects 48 kHz; implementations resample as needed.sr (
int) – The clip’s sample rate in Hz.
- Return type:
ndarray- Returns:
A 1-D
(dim,)L2-normalizedfloat32array.
- class foley.FusedHit(id, rrf_score=None, clap_score=None, bm25_score=None)[source]#
Bases:
objectOne 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(clap_score/bm25_score/rrf_score) for display and debugging.- id#
The sound id.
- rrf_score#
The fused RRF score (
Nonefor a pure-vector search).
- clap_score#
Cosine similarity from the vector ranker (
Noneif the id appeared only in the keyword list).
- bm25_score#
BM25 score from the keyword ranker (
Noneif the id appeared only in the vector list).
- exception foley.GenerationError(message, *, report, status)[source]#
Bases:
RuntimeErrorRaised by
foley.generate()when a backend yields no stored sound.Carries the full
IngestReportand the terminalstatusso a caller can react distinctly toquarantined(QC-rejected — e.g. regenerate),rights_blocked, orerror. The lower-levelgenerate()workhorse never raises this — it always returns an inspectable report; only the publicfoley.generate()promise raises.- report#
The
IngestReportfrom the run.
- status#
The terminal
IngestResultstatus ('quarantined'|'rights_blocked'|'error'| …).
- class foley.IngestReport(root, results=<factory>, notes=<factory>)[source]#
Bases:
SerializableMixinThe rolled-up outcome of a folder ingest (JSON-serializable).
- property errored: list[IngestResult]#
Results that raised during ingest.
- exception = None#
The exception behind the last
errorresult, kept so a raising façade can chain it (raise ... from); a plain attribute, never serialized.
- property ingested: list[IngestResult]#
Results that were added to the library (
passorwarn).
- property quarantined: list[IngestResult]#
Results rejected by the QC gate.
- record(result)[source]#
Append one
IngestResult.- Return type:
- property rights_blocked: list[IngestResult]#
Results refused by the fail-closed AI-training/license rights gate.
- property skipped: list[IngestResult]#
Results skipped as content-addressed duplicates.
- class foley.IngestResult(id, status, record=None, qc=None, notes=<factory>, error=None, cost_estimate_usd=None, cost_actual_usd=None)[source]#
Bases:
SerializableMixinThe 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 — seeingest_one()),'skipped_license'(dropped by a bootstrap commercial-use / fail-closed license filter), or'error'.recordis 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)[source]#
Bases:
SerializableMixinWhat the caller intends to do with a sound; consumed by
keep().
- class foley.Judge(*args, **kwargs)[source]#
Bases:
ProtocolOne rung of the verify ladder: does this candidate match this event? (report 10 §4.2).
levelselects the rung —clap(cheap score gate),listen(audio-LM),judge(LLM arbitration + scene consistency). The returnedVerdictcarrieslevel== the rung that produced it. Only thejudgerung’s real impl calls the LLM.
- class foley.KeywordIndex(*args, **kwargs)[source]#
Bases:
ProtocolBM25 / 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
wherepush-down contract asVectorIndex.
- class foley.LanceIndex(*, uri, dim, table_name='sounds')[source]#
Bases:
objectLanceDB-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()), so a sound’s vector (upsert()) and text (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 byfoley.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.addenforces this (it raises without an embedding source), so the façade path is safe; a bareindex()with no matchingupsert()stages a text-only row that stays unflushed (never keyword-searchable). For a keyword-only library with no embeddings, useMemoryIndexorSqliteVecIndex(independent vector/text tables).- property db#
The lazily-connected LanceDB database handle.
- get_vector(id)[source]#
Return the stored vector for
id(staged or persisted), elseNone.- Return type:
Optional[ndarray]
- index(id, text, meta)[source]#
Stage the searchable text for
id(flushed on the next read/commit).- Return type:
- property table#
The lazily-opened (or created-empty) LanceDB table.
- 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)[source]#
Bases:
objectThe eight derivable flags for one
license_id(the table row type).
- class foley.LicenseMeta(display_name, url=None)[source]#
Bases:
objectHuman-facing display metadata for one
license_id(name + canonical URL).The presentation sibling of
LicenseFlags: whereLicenseFlagsholds the permission row consulted bykeep(),LicenseMetaholds the display row consulted by the credits/attribution layer (foley.provenance.credits). Kept here solicensingstays the single license authority; a record’s ownlicense_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)[source]#
Bases:
SerializableMixinPer-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_okdefaultsTrue(the normal case for a licensed sound). That is not a live bypass:keep()checksrights_verifiedfirst, so an unverified record is rejected regardless. Populate the flags from thelicense_idviafoley.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)[source]#
Bases:
SerializableMixinLoudness master target — the delivery spec as data, not code (report 06 §5.2).
- class foley.MemoryIndex(*, dim=None)[source]#
Bases:
objectIn-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, soupsert()andindex()never contend.- bm25(query, k, *, where=None)[source]#
Return the top-
kBM25 matches forquery(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.
- index(id, text, meta)[source]#
Insert or replace the searchable text (and light metadata) for
id.- Return type:
- class foley.PannsTagger(*, device='cpu', threshold=0.1)[source]#
Bases:
objectPANNs CNN14 supervised tagger over the 527 AudioSet classes (
foley[tag]).The checkpoint auto-downloads to
~/panns_data(~327 MB) on the firsttag(). PANNs expects 32 kHz mono; the clip is resampled viafoley.audio.to_working().
- class foley.Placement(anchor=Anchor.absolute, ref=None, onset=0.0, pre_roll=0.0, duration=None, loop=False)[source]#
Bases:
SerializableMixinWHERE/WHEN a clip sits — a symbolic anchor plus its resolved time (report 06 §6.3).
Filled by WEAVE’s aligner+anchor pass.
onsetis the resolved start in seconds (distinct from the sparseTimelineItem.onsetsymbolic string);pre_rollshifts 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)[source]#
Bases:
SerializableMixinHOW 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>)[source]#
Bases:
objectPer-clip Tier-0 QC result.
Mirrors the fields the
SoundRecordschema already carries (duration_s,sample_rate,channels,loudness_lufs) plus the deterministic check outputs, an overallstatus, and human-readablenotesfor every firing condition. Serialize withto_dict()intoSoundRecord.qc.
- class foley.QCStatus(*values)[source]#
-
Overall verdict for a clip (subclasses
strso 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)[source]#
Bases:
objectAll Tier-0 QC defaults (report 08 §3 table), each an explicit field.
Grouped by check. Pass a customized instance to
run_qc()(or the per-check keyword arguments) to override any threshold without editing code.
- exception foley.RecognizableVoiceRefusal(message, *, hits, report)[source]#
Bases:
SafetyRefusalA
SafetyRefusalfor 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)[source]#
Bases:
SerializableMixinThe reproducible run-artifact for one foley operation (trace ⊕ plan ⊕ provenance).
Persisted by
emit_run_manifest()into a run store keyed byrun_id. Sensitive prompt/query text lives redacted (seefoley.obs.redact); chosen clips are held by reference (SoundRecordid) 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)[source]#
Bases:
objectA frozen runtime posture — the local-first / offline contract as data.
- Parameters:
offline (
bool) – Whether this posture is offline/local-first.data_egress_allow (
frozenset[str]) – The egress classes a source may use to be available ({'local'}offline;{'local','external'}online).telemetry (
bool) – Whether the obs run-artifact export is on.redaction_mode (
str) –'hash'(default, salted),'off'(drop), or'full'(raw — local-debug only).http_resilience (
bool) – Whether HTTP source adapters are wrapped with the throttle/backoff/circuit-breaker (foley.sources.resilience).
- allows(data_egress)[source]#
Whether a source declaring
data_egressis 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:
- classmethod default()[source]#
The online default: all egress allowed, telemetry on, hashed redaction.
- Return type:
- classmethod from_env()[source]#
Build from the environment:
FOLEY_OFFLINEin {1,true,yes} → offline-local.- Return type:
- exception foley.SafetyRefusal(message, *, hits, report)[source]#
Bases:
GenerationErrorRaised (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
errorresult (a backend/ingest failure the workhorse records without raising) — a safety refusal is a deliberate refusal of an unsafe request. Itsreportis an empty pre-generation report andstatusis'refused'. Carrieshits(the matched marks/patterns). Subclass ofGenerationErrorso the publicfoley.generate()Raisesclause already covers it. Seefoley.provenance.disclosure.scan_prompt().
- class foley.ScoreResult(timeline, events, weave=None)[source]#
Bases:
objectThe output of
score()— the editable plan + per-event rationale (+ mix when woven).
- class foley.ScoredEvent(segment, query, sound_id, origin, confidence, reason)[source]#
Bases:
objectOne chosen sound placed for a narration event (a JSON-friendly rationale row).
- class foley.SerializableMixin[source]#
Bases:
objectAdds
to_dict/to_json/from_dict/from_jsonto 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)[source]#
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) – A plain dict (typically fromto_dict()orjson.loads).- Return type:
- classmethod from_json(s)[source]#
Reconstruct an instance from a JSON string.
- Parameters:
s (
str) – A JSON string (typically fromto_json()).- Return type:
- class foley.SessionStore(session_id='default', candidates=None, picks=None, rejects=None)[source]#
Bases:
objectThree namespaced stores for one audition session (candidates / picks / rejects).
Each store defaults to a
foley.stores.make_session_store()JSON store; tests inject plain dicts. All values are JSON-safe dicts.- Parameters:
- add_pick(sound_id, *, layer=None, onset=None)[source]#
Persist an accepted pick (+ optional layer/onset); return the pick count.
- Return type:
- add_reject(sound_id, *, reason=None)[source]#
Record a rejected sound (feeds
refinerelevance feedback); return the count.- Return type:
- cache_candidates(candidates)[source]#
Cache each candidate’s full
to_dict()keyed by sound id; return the count cached.- Return type:
- drop_pick(sound_id)[source]#
Remove a pick (idempotent); return the remaining pick count.
- Return type:
- class foley.SoundDesignTimeline(items=<factory>, run_manifest_ref=None, transcript_ref=None, schema_version=1, narration_ref=None, word_timeline=<factory>, master=<factory>)[source]#
Bases:
SerializableMixinThe 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) viafoley.agent.plan(). WEAVE (#8) grows it additively:narration_refbinds the voice audio,word_timelinecaches the forced alignment (the reproducible seed),mastercarries the loudness target, and each item gains its resolvedPlacement/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 #8plan_refjoin).
- 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)[source]#
Bases:
SerializableMixinOne 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)[source]#
Bases:
MappingA searchable, license-aware library of sounds (the foley INDEX façade).
Read it as a
MappingofSoundRecord`s; search it with :meth:`search(text) /search_clip()(a reference clip) /similar()(audio<->audio by id); browse it withfilter(); grow it withadd().- add(record, *, data=None, vector=None)[source]#
Store a sound and index it (the ingest write path).
Persists bytes via
store_sound()(honouring the by-value/by-reference license gate), upserts the CLAP vector into the vector index, and indexescaption``+``tagsinto 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 precomputedvector. Adding with neither raises, rather than silently indexing a vectorless row (which the single-tableLanceIndexcannot persist, producing backend-dependent search results).- Parameters:
record (
SoundRecord) – The record to add (mutated bystore_soundwith resolved storage fields, and stamped with the embedding model/dim).data (
Optional[bytes]) – The archive bytes (required for by-value storage; also the source for computingvectorwhen it is not supplied).vector (
Optional[ndarray]) – A precomputed CLAP embedding; when omitted anddatais given, it is computed via the library’s embedder.
- Return type:
- Returns:
The same (persisted, indexed)
record.- Raises:
ValueError – If neither
datanorvectoris provided (no way to obtain an embedding).
- audio(sound_id)[source]#
Return a sound’s archive bytes (by-value from the store, or from a local by-reference path).
- Parameters:
sound_id (
str) – The record id.- Return type:
- Returns:
The raw archive bytes.
- Raises:
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 embedder#
The text<->audio embedder (CLAP by default).
- filter(**facets)[source]#
Browse the library by metadata facets (no ranking).
Accepts the same facet keywords as
search()’s filters (commercial_ok,ucs_category,min_snr,duration_range) plus anyrecord_attr=valueequality predicate.- Return type:
- 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)[source]#
Hybrid (CLAP vector ⊕ BM25) search for a text query.
- Parameters:
query (
str) – The natural-language query.k (
int) – Number of results to return.filters (
Optional[dict]) – Extra{record_attr: value}equality predicates.commercial_ok (
Optional[bool]) – IfTrue, keep only commercially-usable sounds.ucs_category (
Optional[str]) – Keep only sounds with this UCS CatID.min_snr (
Optional[float]) – Keep only sounds whose QCsnr_dbis at least this.duration_range (
Optional[tuple[float,float]]) – Keep only sounds whoseduration_sis in(min, max).rerank (
bool) – Re-order the shortlist by direct query<->audio cosine (fills the CLAP score for keyword-only hits).
- Return type:
- Returns:
Up to
k:class:`~foley.base.Candidate`s, best first.
- search_clip(clip, *, sr=None, k=10)[source]#
Search by a reference audio clip (audio<->audio via CLAP).
- Parameters:
- Return type:
- Returns:
Up to
k:class:`~foley.base.Candidate`s, most-similar first.
- similar(sound_id, *, k=10)[source]#
Return the
ksounds most similar tosound_id(audio<->audio).Uses the stored vector (no re-decoding); the query sound itself is excluded from the results.
- property sounds#
The content-addressed byte store.
- update_record(record)[source]#
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 – If
record.idis not in the library.- Return type:
- 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)[source]#
Bases:
SerializableMixinCanonical 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)[source]#
Bases:
SerializableMixinOne 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)[source]#
Bases:
objectSingle-file index: sqlite-vec
vec0KNN + stdlib FTS5 keyword search.The whole index is one SQLite file behind two virtual tables (independent, so
upsert()andindex()never contend). **Requires an interpreter whosesqlite3permits loadable extensions** — probe withsqlite_vec_loadable()before constructing; the constructor raises a clear error otherwise.- bm25(query, k, *, where=None)[source]#
FTS5 BM25 search; returns
[(id, score), ...]best-first.FTS5’s
rankis more-negative-is-better; it is negated so the returned score is larger-is-better (consistent with the other backends).
- index(id, text, meta)[source]#
Insert or replace the searchable text for
idin the FTS5 table.- Return type:
- class foley.StorageMode(*values)[source]#
-
How a sound’s bytes are held (DERIVED from
license.cache_bytes_ok).
- class foley.Tagger(*args, **kwargs)[source]#
Bases:
ProtocolMap a clip to
(label, score)pairs against a label vocabulary.Used on ingest to auto-fill
SoundRecord.audioset_labels(supervised) andtags(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).
- 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)[source]#
Bases:
SerializableMixinOne 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 fillsid/placement/processing(and may carry the originatingeventfor provenance).enabledis a non-destructive mute. The sparse flat fields are never removed — they stay SELECT’s SSOT input; the render reads the resolvedplacement/processing(falling back to the flat fields when those are absent).
- exception foley.TrademarkRefusal(message, *, hits, report)[source]#
Bases:
SafetyRefusalA
SafetyRefusalfor a prompt naming a trademarked audio logo (report 07 §7.2).
- class foley.VectorIndex(*args, **kwargs)[source]#
Bases:
ProtocolApproximate-nearest-neighbour store over embedding vectors.
The default is LanceDB (report 04 §2); Qdrant/pgvector/sqlite-vec bind the same protocol behind the scenes.
whereis 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)[source]#
Return the stored vector for
id(orNoneif absent).Needed by
SoundLibrary.similar(fetch a sound’s own vector, then runknn()) and by the optional CLAP rerank (score keyword-only hits).- Return type:
Optional[ndarray]
- class foley.Verdict(match, confidence, reason='', level=VerifyLevel.clap)[source]#
Bases:
SerializableMixinThe result of one verification rung for a candidate.
- class foley.WeaveResult(audio, sr, timeline, credits, captions_vtt, captions_srt, master_report, run_manifest_ref=None, content_credential=None, watermark=None, notes=<factory>)[source]#
Bases:
objectThe 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.
- foley.add_from(source, *, query, license='cc0', limit=50, library=None, intended_use=None, adapter=None, on_unsupported=None, **affordances)[source]#
Search a live
sourceand ingest its license-clean hits intolibrary.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 throughingest_one(), which applies the by-reference storage gate from the sound’s own license.- Parameters:
source (
str) – A registered live-source name (e.g.'freesound').query (
str) – The natural-language search query.license (
Optional[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) – Max candidates to request from the source.library – Target
SoundLibrary(default: the process-wide default library).intended_use (
Optional[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[str]) – A search parameter the source cannot honour:None(default) drops it with a note inreport.notes;'raise'raises (seefoley.sources._dispatch.translate_affordances()).**affordances – Extra unified affordances forwarded to the adapter’s
search(e.g.duration_range,sort).
- Return type:
- Returns:
An
IngestReport— inspect.ingestedfor the stored records (eachstorage_mode == by_referencefor Freesound) and.summary()for counts, exactly likefoley.ingest().
- foley.apply_license_flags(record, *, overrides=None)[source]#
Populate
record’s eight derived flags from itslicense_id(+ overrides), in place, and return it.Does NOT touch
rights_verified— verification is a separate concern.- Parameters:
record (
LicenseRecord) – TheLicenseRecordto populate.overrides (
Optional[dict]) – Optional per-source flag overrides (seederive_license_flags()).
- Return type:
- Returns:
The same (mutated)
record.
- foley.attribution_line(source, *, fmt='markdown')[source]#
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,SoundRecord,Candidate,LicenseRecord]) – ACreditEntry, or any credit input (coerced first).fmt (
str) –'markdown'(hyperlinked list-item body) or'plain'(text with URLs in parentheses).
- Return type:
- 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)[source]#
Seed
libraryfrom the selected bulk corpora, returning per-corpus reports.- Parameters:
rings (
tuple[int,...]) – Which rings to include (default(0, 1)— Ring 2 is never default; it is opt-in viacorpora=[...]+accept_ai_restricted).corpora (
Optional[list[str]]) – Explicit corpus-name allowlist; overridesringswhen given.data_dir (
Optional[str]) – Root under which each corpus lives atdata_dir/<name>(default: the library’s data dir /$FOLEY_DATA_DIR).library – Target
SoundLibrary(default: the process-wide default library).roots (
Optional[dict[str,str]]) – Optional per-corpus root overrides ({name: path}) — for corpora downloaded somewhere other thandata_dir/<name>.accept_ai_restricted (
bool) – Consent gate for Ring-2 /ai_training_ok=Falsecorpora.False(default) refuses them;Truerecords explicit operator consent and admits them.commercial_only (
Optional[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()(do_supervised,do_zeroshot,min_status,thresholds…).
- Return type:
- 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)[source]#
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 topy2mcp.mk_mcp_server. Never starts a server or touches the network.- Parameters:
library – The
foley.index.SoundLibrary(default: the shared one).session (
str) – The default session id.runtime – A
foley.runtime.RuntimeConfig(default: the active one).byte_store – A
MutableMapping[str, bytes]for previews / rendered mixes.include (
Optional[list[str]]) – Optional subset of tool names to expose.name (
str) – The MCP server name.max_usd (
Optional[float]) – The server’s cumulative spend cap across every paid tool call, for its lifetime (defaultfoley.cost.default_max_usd(): $1, or $3 under$FOLEY_LLM=anthropic).
- Returns:
A
fastmcp.FastMCPserver.
- foley.candidate_of(result)[source]#
Wrap a stored
IngestResultas a generated candidate.The report-10 §4.2 shape: retrieval and generation return the same
Candidate, differing only inorigin. Use on apass/warnresult (itsrecordis the canonical, storedSoundRecord).- Parameters:
result (
IngestResult) – A stored ingest result (result.recordis notNone).- Return type:
- Returns:
A
Candidatewithorigin=CandidateOrigin.generated.
- foley.capability_report(*, runtime=None)[source]#
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 listsdegraded_tools— capabilities whose requirement is unmet.- Parameters:
runtime – A
foley.runtime.RuntimeConfig(default: the active one).- Return type:
- Returns:
{keys, extras, system, offline, sources, llm, degraded_tools}— all JSON-safe.llmis the provider the SELECT rungs resolve to right now (seefoley.agent.llm.resolve_llm()), or the reason it cannot resolve.
- foley.check_requirements(*, names=None, verbose=False)[source]#
Report which optional foley capabilities are available (
{name: is_available}).- Parameters:
- Return type:
- 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')[source]#
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).
- foley.credit_entry(record, *, title=None)[source]#
Build a
CreditEntryfrom a record (title override optional).Every field is read straight off the
LicenseRecord(flags never re-derived);modifiedreflects a non-emptytransformationslist.- Parameters:
record (
Union[SoundRecord,Candidate,LicenseRecord]) – ASoundRecord,Candidate, orLicenseRecord.title (
Optional[str]) – Explicit title override (else resolved from caption/tags/…).
- Return type:
- foley.credits(sounds, *, title='Credits', only_required=False, write_to=None)[source]#
Build the TASL attribution
Creditsforsounds.Works standalone today (given any iterable of sounds), and is what the WEAVE stage will call at render time. Inspect
.markdown(aCREDITS.mddocument) /.manifest(a JSON-serializable dict) on the result.- Parameters:
sounds – An iterable of
SoundRecord/Candidate/LicenseRecord(e.g. the result ofsearch(), or the sounds placed in a timeline).title (
str) – The credits heading.only_required (
bool) – Keep only legally-required attributions (drops CC0 / user-owned courtesy credits). Default credits everything.write_to – Optional directory; when given, writes
CREDITS.mdandcredits.jsoninto it (created if missing).
- Returns:
A
Credits.
- foley.credits_for(sounds, *, title='Credits', only_required=False, sort='appearance')[source]#
Build the deduplicated
Creditsfor the sounds used in a run.- Parameters:
sounds (
Iterable[Union[SoundRecord,Candidate,LicenseRecord]]) – An iterable of records / candidates / license records.title (
str) – The credits heading (also carried in the manifest).only_required (
bool) – Keep only entries whose license requires attribution (drops CC0 / user-owned courtesy credits). DefaultFalsecredits everything (never-discard-provenance).sort (
str) –'appearance'(default: first-seen order),'author', or'title'(case-insensitive alpha).
- Return type:
- Returns:
A
Credits; identical sounds (same id) are credited once (first-writer-wins).
- foley.dc_offset(samples)[source]#
Largest per-channel absolute DC offset,
max_c |mean_n x[n, c]|.- Return type:
- foley.decide(event, kept, verified, *, tau_retrieve, budget, loop)[source]#
The single generate-vs-retrieve branch — a PURE function (report 05 §4).
Chooses among
DecideActionfrom the already-gated (kept) and already-verified (verified) sets. It performs no I/O and never callskeep/search/generate— thefoley.agent.toolsloop acts on the returnedDecision.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) – The event being resolved.kept (
list[Candidate]) – The license-clean candidates (eachlicense_ok is True).verified (
list[Candidate]) – The subset ofkeptwhose verdict matched.tau_retrieve (
float) – The confidence threshold for auto-accepting a retrieved clip.budget (
Budget) – The per-event cost budget.loop (
int) – The current refine-loop index (for the audit reason).
- Return type:
- Returns:
A
Decision.
- foley.decompose_context(context, *, max_events=6, seconds=None, decomposer=None, _span=None)[source]#
Decompose a passage into
<= max_eventssparseSoundEvents.The pure SELECT tool (Python-API == agent == future-MCP surface): resolves the default decomposer when
decomposerisNone, calls it, and records the GenAI span on the real path (the fake’slast_responseisNone→ no-op).- Parameters:
context (
str) – The narrative passage.max_events (
int) – The sparse density cap.seconds (
Optional[float]) – Optional passage duration (density-window hint; forwarded, else ignored).decomposer (
Optional[Decomposer]) – An injectedDecomposer(the DI seam); defaults to_default_decomposer()._span – Internal — the obs span handle
find()opens for GenAI recording.
- Return type:
- foley.default_embedder()[source]#
Return a process-wide default
ClapEmbedder(loaded once, reused).Cached so repeated
foley.search()calls share a single loaded model.- Return type:
- foley.default_index(*, data_dir, dim)[source]#
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-persistentMemoryIndexis 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) – The embedding dimensionality from the active embedder.
- Returns:
A ready index object (both
VectorIndexandKeywordIndex).- Raises:
RuntimeError – If no persistent backend is installed/usable.
- foley.default_library()[source]#
The process-wide default library (local stores + CLAP + best index).
- Return type:
- foley.default_tagger()[source]#
The default supervised tagger (PANNs CNN14;
foley[tag]).- Return type:
- foley.default_zeroshot_tagger(embedder=None)[source]#
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=Noneuses the process-wide default embedder.- Return type:
- foley.demo(*, library=None, query='rain on a window', k=3)[source]#
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 itsmanifest.json) flow into the keyword index so a plain-text query resolves.- Parameters:
- Return type:
- Returns:
{"ingested": <summary dict>, "top_hit": <id or None>, "caption": <str>}.
- foley.derive_license_flags(license_id, *, overrides=None)[source]#
Look up the flag set for a
license_id(fail-closed fallback), then apply per-source overrides.- Parameters:
- Return type:
- Returns:
The resolved
LicenseFlags(fallback = all-FalseUNKNOWN_LICENSE_FLAGSfor unrecognized ids).- Raises:
ValueError – If
overridescontains a key that is not aLicenseFlagsfield.
- foley.detect_clipping(samples, *, full_scale=0.999, min_run=3)[source]#
Detect hard (flat-topped) clipping.
A frame is “hot” when any channel reaches
|x| >= full_scale. Only maximal hot runs of length>= min_runcount as clip events.- Parameters:
- Return type:
- 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)[source]#
Clip duration in seconds:
frames / sample_rate.- Return type:
- foley.encode(samples, sample_rate, *, fmt='flac', subtype='PCM_24')[source]#
Encode
samplesfully 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:
- Return type:
- Returns:
The encoded audio as
bytes.
Lazy dependency:
soundfile.
- foley.ensure_channels(samples, *, channels)[source]#
Coerce
samplesto exactlychannelschannels.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) – Target channel count (must be >= 1).
- Return type:
ndarray- Returns:
A
(frames,)array whenchannels == 1, else a(frames, channels)array.- Raises:
ValueError – If
channels < 1.
Lazy dependency:
numpy(only for the up-mix / tile path).
- foley.estimate(verb, **kwargs)[source]#
What a call would cost in USD before making it —
Nonewhen it cannot be known (#57).Noneis 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) –'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 — whatfindchecks against its budget before the first paid call; passcontext=/segments=so the decomposer’s prompt is sized, andscoresums onefindper segment);'search'/'similar'/'ingest'(local:0.0);'add_from'(source=).**kwargs – The same keywords the verb takes.
- Raises:
ValueError – For an unknown verb.
KeyError – For an unknown backend / source.
- foley.estimate_snr(samples, sample_rate, *, quiet_percentile=10.0, frame_s=0.025, hop_s=0.01)[source]#
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_percentilepercent 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 returnsinfand a silent clip returns-inf.- Parameters:
samples (
ndarray) – Waveform in[-1, 1](down-mixed to mono internally).sample_rate (
int) – Sample rate in Hz (sizes the frames).quiet_percentile (
float) – Percent of quietest frames forming the noise floor.frame_s (
float) – Short-time frame length in seconds.hop_s (
float) – Hop between frames in seconds.
- Return type:
- Returns:
SNR in dB.
- foley.evaluate(*, golden=None, k=10)[source]#
Run the Tier-1 retrieval eval over the golden set (nDCG@10 / recall / mAP / MRR).
Scores every golden query through the real
SoundLibrary.search()path against a deterministic, CLAP-free Ring-0 library — the same computation the PR gate asserts on. Seefoley.eval.- Parameters:
golden – Optional path to a golden-set JSON (default: the frozen seed).
k (
int) – Retrieval cutoff and metric@k.
- Returns:
- foley.evaluate_fit(*, golden=None, sample=None, level=VerifyLevel.judge, fit_judge=None, embedder=None, seed=0, k=10, llm=None)[source]#
Run the Tier-2 fit eval over the golden set — “does the accepted clip fit?” (#10b).
The judge-based sibling of
evaluate(): over a seeded stratified sample it runs the SELECT pipeline and audits each license-clean candidate with the authoritative fit-judge, returning afoley.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 viaFitReport.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'(defaultVerifyLevel.judge);'clap'is rejected.fit_judge – An injected authoritative judge (default: the
llmprovider’s judge — the hermeticStringOverlapJudgefake unlessllmopts in; the audio-LMAudioLMJudgeis injection-only).embedder – The Ring-0 embedder (default: the CLAP-free
HashingBowEmbedder).seed (
int) – The sampling RNG seed.k (
int) – Retrieval shortlist depth per event.llm – The fit-judge’s provider when
fit_judgeis not given —'anthropic'for the nightly arbiter (or$FOLEY_LLM);Nonekeeps the deterministic fake (a local endpoint is never picked implicitly).
- Returns:
- foley.fade(samples, sample_rate, *, fade_in_s=0.01, fade_out_s=0.01, kind='linear')[source]#
Apply in/out gain ramps to
samples(a short declick by default).Ramp lengths are clamped to at most
len(samples) // 2so the in- and out-ramps never overlap on tiny inputs.- Parameters:
- 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)[source]#
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) – The narrative passage.max_events (
int) – The sparse density cap on decomposed events.seconds (
Optional[float]) – Optional passage duration (density-window hint; forwarded).intended_use (
Optional[IntendedUse]) – The caller’s rights intent (default:foley.licensing.DEFAULT_INTENDED_USE— commercial publishing; NC / SA material is refused unless you pass a different intent).backend (
str) – Generation backend for the fallback ('auto'→foley.generate’s default).verify (
Union[str,VerifyLevel]) – The max verify rung —'clap'|'listen'|'judge'.stream (
bool) – IfTrue, return a generator yielding oneCandidateper resolved event; else return the collectedlist.k (
int) – Retrieval shortlist depth per query.tau_retrieve (
float) – Confidence threshold to auto-accept a retrieved clip.tau_clap (
float) – Theclap-rung gate threshold.max_refine_loops (
int) – Max refine→re-retrieve passes per event (also the defaultBudget).budget (
Optional[Budget]) – An explicitBudget(overridesmax_refine_loops). It is also the run’s spend cap:Budget(max_usd=...)(default $1; $3 whenllm='anthropic'or$FOLEY_LLM=anthropic, whose default run is bounded at about $2.52 —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 needsBudget(approve_unknown_cost=True)(#57).library – Target
SoundLibrary(default: the process-wide default).refiner (decomposer / judge /) – Injected DI seams (
Decomposer/Judge/Refiner); each defaults to thellmprovider’s implementation.llm (
Optional[str]) – Which LLM the decompose / refine / judge rungs use when not injected —'fake'|'local'|'anthropic'.Nonereads$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:
- Returns:
list[Candidate](stream=False) or anIterator[Candidate](stream=True) — one verified, license-clean candidate per resolved event.
- foley.fuse_hits(vector_hits, keyword_hits, *, k, rrf_k=60)[source]#
RRF-fuse a vector ranker’s hits with a keyword ranker’s hits.
- Parameters:
- Return type:
- 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)[source]#
Generate a sound effect for
promptand 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 SAMEingest_one()pipeline as every other source, with operator consent for the generator license’s AI-training restriction (the record keepsai_training_ok=False, sokeep()still refuses it for training uses).- Parameters:
prompt (
str) – The natural-language sound description.backend (
str) – A registered generate source —"stable_audio"(default, local; needsfoley[stable-audio]) or"elevenlabs"(hosted;foley[elevenlabs]+$ELEVENLABS_API_KEY).library – Target library (default: the process-wide default library).
store (
bool) – IfFalse, 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 –
Truerequire an AudioSeal watermark,Falsenever,None(default, auto) watermark ifffoley[provenance]is installed (#9b).on_flagged (
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
MutableMappingfor content-credential sidecars (default: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) – Serve an identical paid request from the generations cache (defaultTrue): a paid sound is never paid for twice (#59).budget – A
Budgetfor 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— seeGENERATION_AFFORDANCES).
- Returns:
The stored
Candidate(origin=generated) — itssoundis the canonical, by-valueSoundRecord(a content-hash id); itsnoteslist every dropped, clamped or substituted parameter, andcost_estimate_usd/cost_actual_usdwhat it cost (None= unknown).- Raises:
SafetyRefusal – If the prompt trips a safety gate and
on_flagged='refuse'(aGenerationErrorsubclass —TrademarkRefusal/RecognizableVoiceRefusal).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
reportand terminalstatusso callers can react distinctly.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 – For an external backend under
offline(), orModelNotCachedfor a local one whose weights are not on this machine.
- foley.has_nan_inf(samples)[source]#
Return
Trueif any sample isNaNorInf(corrupt-clip guard).- Return type:
- foley.hybrid_search(query, *, embedder, vindex, kindex, k=10, candidate_k=50, rrf_k=60, where=None)[source]#
Embed
query, run the vector + keyword rankers, and RRF-fuse them.- Parameters:
query (
str) – The natural-language query.embedder (
Embedder) – Text<->audio embedder (itsembed_textproduces the query vector).vindex (
VectorIndex) – The vector index (CLAP KNN).kindex (
KeywordIndex) – The keyword index (BM25).k (
int) – Number of fused results to return.candidate_k (
int) – Shortlist depth pulled from each ranker before fusion.rrf_k (
int) – The RRF damping constant.where (
Optional[dict]) – Optional metadata push-down passed to both rankers.
- Return type:
- Returns:
The top-
kfused :class:`FusedHit`s.
- foley.ingest(path, *, library=None, backend='local', qc=True, recursive=True, **kw)[source]#
Ingest a folder (or single file) of sounds into the default library.
probe -> QC -> tag -> zero-shot -> caption -> embed -> SoundRecordfor each file, returning anIngestReport. Seefoley.index.ingest_folder()/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) –"local"ingests filesystem audio; other backends (a source adapter pull) route throughadd_from(subtask #5) — kept in the signature for forward-compat.qc (
bool) – Run the Tier-0 QC gate (quarantines failing clips).recursive (
bool) – Recurse into sub-folders.**kw – Forwarded to
foley.index.ingest_one()— notablylicense: omit it and the files’ rights are unknown (indexed, butkeep()refuses them for every use); passlicense="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)[source]#
Ingest every audio file under
pathand return anIngestReport.- Parameters:
path – A folder (walked) or a single audio file.
library – Target library (default: the process-wide default).
recursive (
bool) – Recurse into sub-folders.on_error (
str) –'collect'records per-file errors and continues;'raise're-raises the first error. Either way anEgressBlocked(offline, the embedder’s weights not cached) raises at once: every file would fail the same way.**ingest_one_kw – Forwarded to
ingest_one()(license, taggers, QC flags, …).
- Return type:
- Returns:
An
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)[source]#
Ingest one clip into
libraryand return anIngestResult.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,PathLike,bytes,BinaryIO]) – A path,bytes, or file-like audio source.library – Target
SoundLibrary(default: the process-wide default library).sound_id (
Optional[str]) – Optional canonical id override. Defaults toNone→ the content-hash of the decoded PCM (the local-ingest identity, used as the recordidand 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_sha256records the hash of the stored FLAC archive bytes (set bystore_sound), which is a different byte source from this PCM hash.source_uri (
Optional[str]) – Optional by-reference fetchable URI override. Defaults toNone→ the resolved local path whensrcis path-like. A live adapter passes the stable source page URL (e.g.'https://freesound.org/s/12345/') thatfoley.stores.store_sound()requires for a by-reference sound.license (
Optional[LicenseRecord]) – Rights record, or alicense_idstring the caller asserts ('user-owned'), orNone(default): rights unknown, so the clip is indexed butfoley.keep()refuses it (#55). Seeresolve_ingest_license().tagger – Supervised
Tagger(default: PANNs viadefault_tagger()).zeroshot_tagger – Zero-shot tagger (default: CLAP via
default_zeroshot_tagger()).captioner – Optional
Captioner(default: none — the caption stage is off unless one is injected).do_qc (
bool) – Run the Tier-0 QC gate.min_status (
QCStatus) – Admission floor — a QC status worse than this is quarantined (defaultwarn: onlyfailclips are rejected).do_caption (
bool) – Toggle each enrichment stage.thresholds (
QCThresholds) – QC thresholds.store (
bool) – IfFalse, assemble the record but do not add it to the library (probe/QC/enrich only).seed_tags (
Optional[list]) – Optional caller-supplied tags (e.g. a corpus’s folder-path taxonomy) unioned into the record’stagsalongside the supervised/zero-shot tags — so they feed the BM25 keyword index.allow_ai_training_forbidden (
bool) – The universal fail-closed rights gate. A sound whose license hasai_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. PassTrueto record explicit operator consent and admit it anyway (seefoley.bootstrap.bootstrap()’saccept_ai_restricted). Protects every ingest path, not just bootstrap.commercial (
Optional[bool]) – Whether the AI use is for a commercial purpose (decides anc_open_source_onlyscope, #69).None(default) meansDEFAULT_INTENDED_USE’s (commercial).
- Return type:
- Returns:
An
IngestResult; itsrecordisNonewhen quarantined, a duplicate, or rights-blocked.
- foley.install_agent_kit(dest='./.claude', *, overwrite=False)[source]#
Copy the shipped skill + slash command + subagent into
dest(a.claudedir).Installs:
dest/skills/foley-sound-design/— the consumer skill (the sound-design playbook),dest/commands/foley-score.md— the/foley-scoreslash command,dest/agents/sound-designer.md— thesound-designersubagent.
- Parameters:
dest – The target agent-config dir (default
./.claudein the cwd; pass~/.claudeto install globally for every project).overwrite (
bool) – Replace existing files/dirs (default: skip what already exists).
- Return type:
- Returns:
The list of installed paths (as strings) — empty entries that already existed are skipped.
- foley.is_silent(samples, *, rms_floor_dbfs=-60.0)[source]#
Return
Truewhen whole-clip RMS falls belowrms_floor_dbfs.A zero (exactly silent) clip has RMS
0->-infdBFS ->True.- Return type:
- foley.keep(record, intended_use)[source]#
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) – The candidate’s rights record.intended_use (
IntendedUse) – The caller’s declared intent.
- Return type:
- Returns:
Trueonly if every requirement inintended_useis satisfied byrecord;Falseotherwise (including unverified rights).
- foley.keep_sound(sound_record, intended_use)[source]#
Convenience: apply
keep()to aSoundRecord’s nested license.- Parameters:
sound_record – A
SoundRecord(its.licenseis the SSOT consulted).intended_use (
IntendedUse) – The caller’s declared intent.
- Return type:
- Returns:
The result of
keep(sound_record.license, intended_use).
- foley.lancedb_available()[source]#
True if
lancedbis importable (thefoley[index]extra is present).- Return type:
- foley.license_id_from_cc_url(url)[source]#
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, andany 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/attributionwith any mention of commercial use (nc,noncommercial,non commercial,no commercial use) →CC-BY-NC-<v>, elseCC-BY-<v>. The version is kept (/by/3.0/→CC-BY-3.0); a versionless label getsDEFAULT_CC_VERSION; a version foley has no row for (a2.1/jpport) fails closed;anything else — including a string with a token this parser does not know — →
('unknown', False).
- foley.license_meta(license_id)[source]#
Return the display
LicenseMetaforlicense_id(fail-closed fallback).- Parameters:
license_id (
str) – The normalized license id.- Return type:
- Returns:
The mapped
LicenseMeta, orUNKNOWN_LICENSE_METAfor an unrecognized /Proprietary-*id.
- foley.list_sources(*, egress_allow=None)[source]#
Return the names of registered live sources (runs discovery first).
- Parameters:
egress_allow (
Optional[frozenset]) – If given, keep only sources whose declaredconfig['data_egress']is in this set (the local-first / offline filter — seefoley.runtime.RuntimeConfig). A source that does not declaredata_egressis excluded (fail-closed).- Return type:
- foley.load(src, *, target_sr=None, mono=False, dtype='float32')[source]#
Decode audio into a float working array.
srcmay be a filesystem path, raw encodedbytes(wrapped in aBytesIOso nothing touches disk), or any binary file-like object.- Parameters:
src (
Union[str,PathLike,bytes,BinaryIO]) – Path, raw bytes, or file-like object to decode.target_sr (
Optional[int]) – If given, resample the decoded audio to this rate (viaresample()); otherwise the native rate is returned.mono (
bool) – IfTrue, down-mix multichannel audio to mono.dtype (
str) – NumPy dtype string for the returned array (defaultfloat32).
- Return type:
- Returns:
A
(samples, sample_rate)tuple.sampleshas shape(frames,)(mono) or(frames, channels);sample_ratereflects any resample.
Lazy dependencies:
soundfile(andsoxrwhentarget_srdiffers).
- foley.loudness_normalize(samples, sample_rate, *, target_lufs=-16.0, peak_ceiling_dbfs=-1.0, min_block_s=0.4)[source]#
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 belowpeak_ceiling_dbfs. Two inputs are returned unchanged (flag them, don’t amplify): near-silent input (measured loudness at or belowLUFS_GATE_FLOOR), and a clip shorter than one BS.1770 gating block (min_block_s) — whichpyloudnormcannot measure and would otherwise raiseValueErroron (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) – Sample rate in Hz.target_lufs (
float) – Desired integrated loudness (default = foley’s podcast target).peak_ceiling_dbfs (
float) – Sample-peak ceiling (dBFS) applied after loudness normalization. Note this is a sample-peak limit, not an inter-sample true-peak (dBTP) limit — seefoley.qc.true_peak_dbtp()for the oversampled measurement.min_block_s (
float) – Minimum clip length (seconds) that can be loudness-measured; shorter clips are returned unchanged withmeasured = -inf.
- Return type:
- Returns:
(normalized, measured_input_lufs). When the input is near-silent or too short to measure,normalizedis the unchanged input andmeasured_input_lufsis at or belowLUFS_GATE_FLOOR(-inffor the too-short case).
Lazy dependencies:
pyloudnorm(+numpy).
- foley.make_byte_store(rootdir=PosixPath('/home/runner/.local/share/foley/audio'))[source]#
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 anydolMapping (e.g. an S3 store) and pass it directly tostore_sound()instead of calling this factory — thestore_soundgate treatssoundsas an opaqueMutableMapping.- Parameters:
rootdir (
Union[str,PathLike[str]]) – Directory that holds the blobs (created if missing).- Return type:
- Returns:
A
MutableMapping[str, bytes]keyed bycontent_key().
- foley.make_http_app(*, auth, path='/mcp', json_response=False, library=None, runtime=None, byte_store=None, include=None, name='foley')[source]#
Build a bearer-auth-gated ASGI app serving the foley MCP tools over streamable HTTP.
HTTP exposes the tool surface to the network, so
authis **required and fail-closed**: passauth={'bearer_tokens': [...]}(a non-empty iterable of accepted tokens) — a request without a matchingAuthorization: Bearer <token>header gets a401. The returned app (a Starlette/ASGI callable) mounts in any ASGI host (uvicorn, gunicorn, a parent FastAPI).py2mcp/fastmcpare imported lazily insidebuild_mcp_server(), soimport foleystays dol-only.- Parameters:
auth (
dict) –{'bearer_tokens': [...]}— required; empty/missing raises (fail-closed).path (
str) – The MCP HTTP mount path.json_response (
bool) – Return a single JSON response instead of an SSE stream (simple clients).name (
str) – Asbuild_mcp_server().
- Returns:
An ASGI application (the bearer-gated MCP HTTP app).
- Raises:
ValueError – If
authcarries no bearer tokens (no anonymous HTTP access).
- foley.make_meta_store(rootdir=PosixPath('/home/runner/.local/share/foley/meta'))[source]#
Build the metadata store:
Mapping[sound_id -> SoundRecord](JSON files).SoundRecordvalues are (de)serialized transparently via theSerializableMixin(to_dict/from_dict); each record is written as a percent-encoded{sound_id}.jsonfile while the store’s keys stay the baresound_id(invariant #3 — the id is escaped at this boundary so an externally-derived id can never escaperootdiror collide via//..).
- foley.make_run_store(rootdir=PosixPath('/home/runner/.local/share/foley/runs'))[source]#
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 ofmake_provenance_store(): escapes therun_idto a safe{enc}.jsonfilename (invariant #3) while exposing barerun_idkeys; values are plain dicts ((de)serialized bydol.JsonFiles). Local by default; swap in anydolMapping for the cloud.
- foley.make_session_store(session_id='default', name='picks', *, rootdir=None)[source]#
Build a per-session JSON store:
Mapping[key -> dict]undersessions/{id}/{name}/.The by-value carrier for #12’s audition state — one store per namespace (
candidates/picks/rejects). A sibling ofmake_run_store(): percent-encodes each key to a safe{enc}.jsonfilename (invariant #3) while exposing bare keys; values are plain dicts. Local by default; swap in anydolMapping for the cloud.- Parameters:
- Return type:
- 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 topy2mcp.mk_mcp_server. Never starts a server or touches the network.- Parameters:
library – The
foley.index.SoundLibrary(default: the shared one).session (
str) – The default session id.runtime – A
foley.runtime.RuntimeConfig(default: the active one).byte_store – A
MutableMapping[str, bytes]for previews / rendered mixes.include (
Optional[list[str]]) – Optional subset of tool names to expose.name (
str) – The MCP server name.max_usd (
Optional[float]) – The server’s cumulative spend cap across every paid tool call, for its lifetime (defaultfoley.cost.default_max_usd(): $1, or $3 under$FOLEY_LLM=anthropic).
- Returns:
A
fastmcp.FastMCPserver.
- foley.measure_lufs(samples, sample_rate, *, gate_floor_lufs=-70.0, min_block_s=0.4)[source]#
Integrated loudness (LUFS, ITU-R BS.1770-4) via
pyloudnorm(lazy).Returns
Nonewhenpyloudnormis 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).
- foley.needs_edge_fade(samples, *, rel_peak_dbfs=-40.0)[source]#
Return
Truewhen the first or last sample sits aboverel_peak_dbfsrelative to the clip peak — i.e. a nonzero boundary that clicks under narration and needs a short fade.- Return type:
- foley.offline(config=None)[source]#
Alias of
offline_scope()—with foley.offline(): ...for local-first runs.
- foley.parse_ucs_filename(filename, *, table=None)[source]#
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).
- foley.plan(candidates, *, transcript=None)[source]#
Fold verified candidates into the SPARSE
SoundDesignTimeline(the SELECT→WEAVE bridge).One
TimelineItemper candidate (onset·gain·layer·looponly, from itsSoundEvent), joined to the run-artifact viarun_manifest_ref— the reserved #8plan_refslot is filled when called inside an activefoley.obsrun scope (None-safe otherwise).
- foley.preview(candidate_or_id, *, seconds=6, library=None, byte_store=None, session=None)[source]#
Produce a short audition of a sound; set its
preview_urito a store key.Writes the first
secondsof the clip (FLAC) intobyte_storeunder its content key and pointsCandidate.preview_uriat that key — referencing the audio, never returning bytes. Fail-safe: if the audio codec extra (foley[audio]) or the clip is unavailable,preview_uriisNone(the sound id + duration still let a client fetch it).- Parameters:
candidate_or_id – A
Candidateor a sound id.seconds (
int) – Audition length.library – The
foley.index.SoundLibrary(default: the shared one).byte_store – A
MutableMapping[str, bytes]to hold the preview (default: none — thenpreview_uristaysNone).session (
Optional[SessionStore]) – Optional session (unused here; accepted for a uniform signature).
- Return type:
- Returns:
The candidate with
preview_uriset (orNoneon graceful degradation).
- foley.reciprocal_rank_fusion(ranked_id_lists, *, k=60)[source]#
Fuse several ranked id lists into one, by reciprocal rank.
- Parameters:
- Return type:
- Returns:
[(id, fused_score), ...]sorted by fused score descending, ties broken byidascending (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)[source]#
Relevance-feedback refinement: expand for recall, boost picks, drop rejects, re-rank.
Distinct from
foley.refine_query()(which only paraphrases a query): this reads the session’s picks/rejects (or the explicitpicked_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[SessionStore]) – The audition session (source of picks/rejects when not passed explicitly).query (
Optional[str]) – The base text query to expand (optional).rejected_ids (
tuple[str,...]) – Explicit feedback (override the session’s).n (
int) – Paraphrases to request.k (
int) – Result depth.library – The
foley.index.SoundLibrary(default: the shared one).refiner – The query-expansion seam (default: the deterministic fake).
- Return type:
- Returns:
A
RefineResult.
- foley.refine_query(query, *, n=3, hint=None, refiner=None, _span=None)[source]#
Expand
queryinto up tonparaphrases for multi-query retrieval.- Parameters:
query (
str) – The event query to expand.n (
int) – Number of paraphrases.hint (
Optional[str]) – Optional verify-failure reason to steer re-retrieval.refiner (
Optional[Refiner]) – An injectedRefiner(the DI seam); defaults to_default_refiner()._span – Internal — the obs span handle for GenAI recording.
- Return type:
- foley.register_source(name, config, adapter=None)[source]#
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. IfadapterisNoneit is lazily built fromconfigon firstget_source()(the source must then be an importablefoley.sources.<name>package).- Parameters:
name (
str) – The source name (theadd_from()/get_source()key).config (
dict) – TheSOURCE_CONFIGdeclaration.adapter – An optional pre-instantiated adapter (bypasses lazy loading).
- Return type:
- foley.resample(samples, sample_rate, *, target_sr=48000, quality='HQ')[source]#
Resample
samplestotarget_sr(a no-op when already there).- Parameters:
- 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)[source]#
Resolve inputs to a best UCS CatID by the staged precedence.
- Parameters:
audioset_labels (
Sequence[str]) – AudioSet MIDs or names (e.g. from PANNs).filename (
Optional[str]) – Optional UCS-style filename/path (its token-0 CatID wins if recognized).table (
Optional[UcsTable]) – UCS table (defaults todefault_ucs_table()).audioset_map (
Optional[AudioSetUcsMap]) – AudioSet->UCS map (defaults todefault_audioset_ucs_map()).
- Return type:
- Returns:
A
CatIdResolution(falsy when nothing resolved).
- foley.resolve_master(master)[source]#
Resolve a master spec (profile name, explicit profile, or
None) to aMasterProfile.- Parameters:
master (
Union[str,MasterProfile,None]) – AMASTER_PROFILESkey (e.g.'podcast'), an explicitMasterProfile, orNone(-> the podcast default).- Return type:
- Returns:
The resolved
MasterProfile.- Raises:
ValueError – If
masteris an unknown profile name.
- foley.restamp_rights(library=None, *, license='unknown', ids=None, where=None, select='legacy-user-owned', apply=False)[source]#
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 stampeduser-ownedwithout being told becomesunknown(fail closed);foley restamp-rights --license user-owned --apply— the same sounds, but you assert that you own them (now recorded with averified_attimestamp);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(default: the default one).license (
str) – Thelicense_idto stamp (aLICENSE_FLAGSrow; default'unknown'), orFROM_LICENSE_URLto re-derive each record from its ownlicense_url.ids (
Optional[list[str]]) – Re-stamp exactly these sound ids (overrideswhere/select).where – A predicate
LicenseRecord -> boolchoosing the records (overridesselect).select (
str) – ALEGACY_SELECTORSname (default'legacy-user-owned').apply (
bool) – Write the change.False(default) only reports what would change.
- Return type:
- Returns:
The ids that were (or, in a dry run, would be) re-stamped.
- Raises:
ValueError – If
licenseorselectis 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))[source]#
Run every Tier-0 check and fold the results into a
QCReport.- Status rules (evaluated in order):
FAIL if
has_nan_infORis_silentORclipped_max_run >= clip_reject_runORclipped_ratio > clip_reject_ratioORduration_s < duration_min_s. WARN ifdc_offset > dc_offset_failORneeds_edge_fadeOR (snr_dbis a finite value< snr_clean_db) OR (true_peak_dbtpis 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) – Sample rate in Hz.thresholds (
QCThresholds) – Overridable QC thresholds (defaults to shipped values).
- Return type:
- Returns:
A populated
QCReport.
- foley.save(samples, sample_rate, dst, *, fmt='flac', subtype='PCM_24')[source]#
Write
samplestodstasfmt/subtype(default = FLAC archive).- Parameters:
samples (
ndarray) – The working array to write (shape(frames,)or(frames, channels)).sample_rate (
int) – Sample rate in Hz.dst (
Union[str,PathLike,BinaryIO]) – Destination path or writable binary file-like object.fmt (
str) – Container/codec name (case-insensitive; passed to libsndfile).subtype (
str) – Sample subtype (e.g.PCM_24,PCM_16,FLOAT).
- Return type:
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)[source]#
Choose sounds for narration text and (optionally) weave them into the narration audio.
Progressive disclosure — the AI-first headline:
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 editableSoundDesignTimeline, and — whenaudiois given (orweave=True) — aligns + weaves into a mastered mix. Returns aScoreResult.- 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=Falseto skip).transcript (
Optional[str]) – The full narration transcript for alignment (default: the segments joined).library – The
foley.index.SoundLibrary(default: the process-wide default).intended_use – The rights intent (default:
foley.licensing.DEFAULT_INTENDED_USE— commercial publishing, the same default asfoley.find()and every MCP tool).commercial_ok (
Optional[bool]) – Shorthand override of the default intent’scommercial;Falseexplicitly admits non-commercial material.max_events (
int) – The sparse density cap per segment (restraint).verify (
str) – The max verify rung —'clap'|'listen'|'judge'.master (
str) – The deliveryMASTER_PROFILEStarget ('podcast'default).weave (
Optional[bool]) – Force weaving on/off; default auto (Trueiffaudiois given).llm (
Optional[str]) – Which LLM the SELECT rungs use ('fake'|'local'|'anthropic';Nonereads$FOLEY_LLM, else the free default) — seefoley.find().budget – The run’s
Budget— one spend cap (max_usd) across every segment’s paid calls (#57). Default: $1, or $3 whenllm(or$FOLEY_LLM) is'anthropic'(foley.cost.default_max_usd(), #88).**weave_kwargs – Forwarded to
foley.weave()(e.g.sign_cert,watermark).
- Return type:
- Returns:
A
ScoreResult(timeline+eventsrationale;weavewhen woven).
- foley.search(query, *, k=10, filters=None, commercial_ok=None, ucs_category=None, min_snr=None, duration_range=None, rerank=False)[source]#
Hybrid (CLAP vector ⊕ BM25) search of the default library.
Convenience wrapper over
foley.library.search(...)— seefoley.index.SoundLibrary.search(). Constructs the process-wide default library (local stores + CLAP + best available index) on first use.commercial_okdefaults to the one rights intent every verb shares (foley.licensing.DEFAULT_INTENDED_USE: commercial), so only commercially usable sounds come back; passcommercial_ok=Falseto include the rest.
- foley.serve_http(*, host='127.0.0.1', port=8000, auth, path='/mcp', **kwargs)[source]#
Build and serve the foley MCP tools over authenticated streamable HTTP (blocks).
Wraps
make_http_app()and runs it with uvicorn.authis required (fail-closed).- Return type:
- foley.similar(sound_id, *, k=10, commercial_ok=None)[source]#
Find sounds similar to a stored sound (audio<->audio) in the default library.
See
foley.index.SoundLibrary.similar(). Likesearch(), only sounds cleared for the default (commercial) intent are returned unlesscommercial_ok=False.
- foley.similar_to(clip_or_candidate, *, k=10, library=None)[source]#
“More like this” — neighbours of a sound id / candidate, or of a raw clip.
A
strid or aCandidateuses by-id neighbours (SoundLibrary.similar, self excluded); a raw working-array / bytes clip uses audio-to-audio search (SoundLibrary.search_clip).
- foley.sqlite_vec_loadable()[source]#
True if
sqlite_vecis installed AND this interpreter can load it.The macOS system / pyenv CPython builds frequently ship a
sqlite3without loadable-extension support (noenable_load_extension); on those, sqlite-vec cannot be used even when ``pip install``ed. This probes both.- Return type:
- foley.store_sound(record, data=None, *, sounds, meta, cache_bytes_ok=None)[source]#
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_okis passed explicitly, it is read fromrecord.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 fetchableuriplus provenance.- Parameters:
record (
SoundRecord) – TheSoundRecordto persist; its nestedlicenseis the SSOT for the storage mode. Mutated in place with the resolvedstorage_mode/uri/content_sha256and written intometa.data (
Optional[bytes]) – The canonical archive bytes (FLAC). Required for by-value storage; for by-reference it is optional — if given, its hash is recorded incontent_sha256for provenance but the bytes are NOT stored.sounds (
MutableMapping[str,bytes]) – The content-addressed byte store (seemake_byte_store()).meta (
MutableMapping[str,SoundRecord]) – The metadata store (seemake_meta_store()).cache_bytes_ok (
Optional[bool]) – Optional override.None(the default) means “userecord.license.cache_bytes_ok”.
- Return type:
- Returns:
The same (mutated)
record, after it has been written intometa.- Raises:
ValueError – If
record.idis empty/non-str(checked first, so a bad id never leaves an orphan blob), or if the sound resolves to by-reference storage butrecord.uriis 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)[source]#
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')[source]#
Produce the canonical CLAP/QC working array from an arbitrary clip.
Down-mixes (when
mono), resamples totarget_sr, and casts todtype— thefloat32@ 48 kHz mono array every embedder/tagger/QC check consumes.- Parameters:
- Return type:
ndarray- Returns:
The canonical working array.
Lazy dependency:
soxr(only whensample_rate != target_sr).
- foley.trim_silence(samples, sample_rate, *, top_db=30.0)[source]#
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:
- Return type:
- 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)[source]#
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 ``-inffor a fully silent clip.sample_rateis accepted for interface symmetry (FFT interpolation is rate-independent).- Return type:
- foley.using_source_accounts(**accounts)[source]#
Bind per-request source accounts (a key, and what goes with it) for a
withblock.The key is bound through
ocracy.kit.using_credentials, soresolve_source_key()uses it; the rest of the account (ElevenLabs: itsplan, which decides the licence) is bound with that key, and only applies while that same key is the one bound (#98):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 undercontextvars.copy_context(); outside the block the operator’s key and plan apply again.- Raises:
ValueError – For a field an account cannot carry.
- foley.vector_search(qvec, *, vindex, k=10, where=None)[source]#
Pure audio<->audio (or clip->library) vector search — no keyword leg.
Used by
SoundLibrary.similarand by searching with a reference clip. Hits keep their cosine similarity inclap_scoreand preserve the index’s own descending-similarity order (rrf_scoreis leftNone— there is no fusion).- Parameters:
qvec (
ndarray) – An already-L2-normalized(dim,)query vector.vindex (
VectorIndex) – The vector index.k (
int) – Number of neighbours to return.
- Return type:
- Returns:
Up to
k:class:`FusedHit`s in descending-similarity order.
- foley.verify_and_setup(*, names=None)[source]#
Return a per-requirement status + guidance report (never runs an installer).
- foley.verify_match(event, candidate, *, level=VerifyLevel.clap, judge=None, tau_clap=0.35, _span=None)[source]#
Verify
candidateagainsteventup to runglevel(AND-confirming ladder).Runs the
clapgate always; iflevelis 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) – The wantedSoundEvent.candidate (
Candidate) – A license-cleanCandidate— this MUST run after thegate_candidates()gate (asserted).level (
str|VerifyLevel) – The max rung to climb (clap|listen|judge).judge (
Optional[Judge]) – An injectedJudgefor the higher rungs (the DI seam; defaults per_default_judge()).tau_clap (
float) – The clap-gate threshold._span – Internal — the obs span handle for GenAI recording on the LLM rung.
- Raises:
AssertionError – If
candidate.license_okis notTrue(verify-before-gate is a bug — the license gate is the fail-closed first pass).- Return type:
Modules
Audio I/O and DSP primitives for foley. |
|
foley INDEX stage — make every sound findable by keyword and meaning. |
|
Observability & the reproducible run-artifact for foley (#11). |
|
The SELECT stage — the search-agent that finds the right sound for a context (#7). |
|
|
|
Tier-1 retrieval evaluation — metrics, a frozen golden set, and the nDCG gate. |
|
Provenance layer for foley — attribution/credits (and, later, disclosure). |
|
Install foley's shipped agent kit — the consumer skill + Claude slash command + subagent. |
|
Canonical data models for foley — the single source of truth (SSOT) types. |
|
The |
|
Cost: what a call will cost before it is made, and a cumulative cap on a run (#57). |
|
License policy for foley: the license_id -> flag-set SSOT, flag derivation (with per-source overrides), and the fail-closed candidate |
|
Tier-0 deterministic audio QC for foley (research report 08 §3). |
|
Onboarding — check what foley needs, tell the user how to get it (accompy-style, #12). |
|
Runtime posture — local-first / offline mode as one verifiable contract (#12, report 12). |
|
Bulk-corpus source adapters for foley's SOURCE stage. |
|
dol-backed storage for foley: content-addressed bytes + a metadata store. |