foley.index#

foley INDEX stage — make every sound findable by keyword and meaning.

The retrieval keystone (report 04 / report 10 §5): a CLAP joint embedding space, a hybrid vector+keyword index with Reciprocal Rank Fusion, a dol-native SoundLibrary façade composing the byte/metadata stores with the two indexes, and the UCS/AudioSet taxonomy resolver — all behind small, swappable protocols with zero-config defaults.

Everything heavy (torch/transformers/lancedb/sqlite_vec) is lazy-imported inside the method that needs it, so import foley.index costs only the stdlib; install the capability you use via the matching extra (foley[clap], foley[index], foley[index-sqlite]).

Functions

default_embedder()

Return a process-wide default ClapEmbedder (loaded once, reused).

default_tagger()

The default supervised tagger (PANNs CNN14; foley[tag]).

default_zeroshot_tagger([embedder])

The default zero-shot tagger (CLAP vs UCS subcategories; foley[clap]).

ingest_one(src, *[, library, sound_id, ...])

Ingest one clip into library and return an IngestResult.

ingest_folder(path, *[, library, recursive, ...])

Ingest every audio file under path and return an IngestReport.

resolve_ingest_license(license, *[, source_url])

The rights record for a local ingest: fail-closed unless rights are asserted (#55).

restamp_rights([library, license, ids, ...])

Re-stamp the licence of stored sounds; a dry run unless apply=True.

default_index(*, data_dir, dim)

Build the best available persistent index for a library.

lancedb_available()

True if lancedb is importable (the foley[index] extra is present).

sqlite_vec_loadable()

True if sqlite_vec is installed AND this interpreter can load it.

reciprocal_rank_fusion(ranked_id_lists, *[, k])

Fuse several ranked id lists into one, by reciprocal rank.

fuse_hits(vector_hits, keyword_hits, *, k[, ...])

RRF-fuse a vector ranker's hits with a keyword ranker's hits.

hybrid_search(query, *, embedder, vindex, kindex)

Embed query, run the vector + keyword rankers, and RRF-fuse them.

vector_search(qvec, *, vindex[, k, where])

Pure audio<->audio (or clip->library) vector search — no keyword leg.

default_library()

The process-wide default library (local stores + CLAP + best index).

resolve_catid(*[, tags, caption, ...])

Resolve inputs to a best UCS CatID by the staged precedence.

parse_ucs_filename(filename, *[, table])

Parse a UCS-conformant filename to (ucs_category, ucs_subcategory).

Classes

Embedder(*args, **kwargs)

A joint text<->audio embedding space (CLAP by default).

VectorIndex(*args, **kwargs)

Approximate-nearest-neighbour store over embedding vectors.

KeywordIndex(*args, **kwargs)

BM25 / full-text index over each sound's tags + caption.

Tagger(*args, **kwargs)

Map a clip to (label, score) pairs against a label vocabulary.

Captioner(*args, **kwargs)

Produce one natural-language sentence describing a clip (report 03).

ClapEmbedder([model_id, device])

LAION-CLAP text<->audio embedder (the default retrieval engine).

ClapZeroShotTagger(*[, embedder, labels, ...])

Zero-shot tagger: score a clip against a label set via CLAP cosine.

PannsTagger(*[, device, threshold])

PANNs CNN14 supervised tagger over the 527 AudioSet classes (foley[tag]).

IngestResult(id, status[, record, qc, ...])

The outcome of ingesting one clip.

IngestReport(root[, results, notes])

The rolled-up outcome of a folder ingest (JSON-serializable).

MemoryIndex(*[, dim])

In-memory vector + keyword index (numpy cosine + compact BM25).

LanceIndex(*, uri, dim[, table_name])

LanceDB-backed index: one table with a vector column + a native FTS index.

SqliteVecIndex(*, path, dim)

Single-file index: sqlite-vec vec0 KNN + stdlib FTS5 keyword search.

FusedHit(id[, rrf_score, clap_score, bm25_score])

One fused retrieval hit: an id plus the scores that produced it.

SoundLibrary(*[, sounds, meta, vindex, ...])

A searchable, license-aware library of sounds (the foley INDEX façade).

CatIdResolution([catid, category, ...])

The result of resolving free tags/caption/labels to a UCS CatID.

class foley.index.Captioner(*args, **kwargs)[source]#

Bases: Protocol

Produce one natural-language sentence describing a clip (report 03).

The caption feeds the BM25 keyword index and human display. Default is a dedicated AAC model (EnCLAP); Qwen2-Audio is a richer promptable upgrade. Both are foley[caption] adapters plugged in behind this protocol.

caption(wav, sr)[source]#

Return a one-sentence caption for the clip.

Return type:

str

class foley.index.CatIdResolution(catid=None, category=None, subcategory=None, source=None, confidence=0.0, matched_terms=<factory>)[source]#

Bases: object

The result of resolving free tags/caption/labels to a UCS CatID.

catid feeds ucs_category and subcategory feeds ucs_subcategory on ingest, and ucs_catid on the query side.

class foley.index.ClapEmbedder(model_id='laion/larger_clap_general', *, device=None)[source]#

Bases: object

LAION-CLAP text<->audio embedder (the default retrieval engine).

Returns L2-normalized float32 embeddings so a plain inner product is cosine similarity. embed_text always returns a 2-D (n, dim) array; embed_audio returns a 1-D (dim,) array for one clip.

model_id#

The HF checkpoint id.

dim#

The embedding dimensionality.

open_source#

Whether the checkpoint is an open-source model (the LAION releases are). Read by the ingest AI-use gate for sounds whose rights holder allows only open-source models (#69).

property device: str#

The resolved torch device string ('cuda'/'cpu').

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 (via AutoConfig) — never the ~1.7 GB weights — so building an index for it does not force a model download. Falls back to the loaded model’s config if the standalone config lacks projection_dim.

embed_audio(wav, sr)[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.audio before embedding.

Parameters:
  • wav (ndarray) – A working-array clip (float32; mono or multichannel).

  • sr (int) – The clip’s sample rate in Hz.

Return type:

ndarray

embed_text(text)[source]#

Embed one or more query strings -> (n_texts, dim) L2-normalized.

Return type:

ndarray

class foley.index.ClapZeroShotTagger(*, embedder=None, labels=None, prompt='this is a sound of {label}', threshold=0.0)[source]#

Bases: object

Zero-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 subcategory phrases).

Natural multi-word phrases ("weather rain", "glass break") are far better CLAP prompts — and better BM25 tags / taxonomy-resolver input — than bare abstract subcategory words ("Break", "Buzz"), which are a known zero-shot artifact (anomalously close to everything). Absolute tag-quality calibration (thresholds, label curation) is an eval-harness concern (#10).

Type:

The label vocabulary (default

tag(wav, sr, *, taxonomy='custom', top_k=10)[source]#

Return the top-k (label, cosine) tags for the clip, best first.

Return type:

list[tuple[str, float]]

tag_vector(audio_vec, *, top_k=10)[source]#

Score a precomputed (L2-normalized) audio vector against the labels.

The efficiency seam (report 03 Part 2): the Index already embeds every sound with this model, so on ingest the retrieval vector is reused here — no second CLAP forward pass.

Return type:

list[tuple[str, float]]

class foley.index.Embedder(*args, **kwargs)[source]#

Bases: Protocol

A joint text<->audio embedding space (CLAP by default).

One space serves both text->audio search (embed_text a query) and audio<->audio similarity (embed_audio a clip). Implementations MUST return L2-normalized float32 arrays so a plain inner product is cosine similarity, and MUST stamp model_id/dim so mixed-model libraries stay coherent (each SoundRecord records the embedding_model/embedding_dim it was indexed under).

model_id#

The checkpoint id (e.g. 'laion/larger_clap_general').

dim#

The embedding dimensionality (e.g. 512).

embed_audio(wav, sr)[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-normalized float32 array.

embed_text(text)[source]#

Embed one or more query strings.

Parameters:

text (Union[str, list[str]]) – A single string or a list of strings.

Return type:

ndarray

Returns:

A 2-D (n_texts, dim) L2-normalized float32 array (n_texts is 1 for a single string) — always 2-D so callers can index [0] for the single-query case.

class foley.index.FusedHit(id, rrf_score=None, clap_score=None, bm25_score=None)[source]#

Bases: object

One fused retrieval hit: an id plus the scores that produced it.

The raw component scores are carried through (not just the fused rank score) so the façade can stamp them onto a Candidate (clap_score / bm25_score / rrf_score) for display and debugging.

id#

The sound id.

rrf_score#

The fused RRF score (None for a pure-vector search).

clap_score#

Cosine similarity from the vector ranker (None if the id appeared only in the keyword list).

bm25_score#

BM25 score from the keyword ranker (None if the id appeared only in the vector list).

class foley.index.IngestReport(root, results=<factory>, notes=<factory>)[source]#

Bases: SerializableMixin

The rolled-up outcome of a folder ingest (JSON-serializable).

error(path, exc)[source]#

Record a per-file error without aborting the run.

Return type:

None

property errored: list[IngestResult]#

Results that raised during ingest.

exception = None#

The exception behind the last error result, kept so a raising façade can chain it (raise ... from); a plain attribute, never serialized.

property ingested: list[IngestResult]#

Results that were added to the library (pass or warn).

property quarantined: list[IngestResult]#

Results rejected by the QC gate.

record(result)[source]#

Append one IngestResult.

Return type:

None

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.

summary()[source]#

A counts dict for a console/CLI summary.

Return type:

dict

class foley.index.IngestResult(id, status, record=None, qc=None, notes=<factory>, error=None, cost_estimate_usd=None, cost_actual_usd=None)[source]#

Bases: SerializableMixin

The outcome of ingesting one clip.

status: 'pass'/'warn' (ingested), 'quarantined' (QC-rejected, not added), 'skipped_dup' (content already in the library), 'rights_blocked' (license forbids AI training / embedding, refused before embed — see ingest_one()), 'skipped_license' (dropped by a bootstrap commercial-use / fail-closed license filter), or 'error'. record is present only when the clip was ingested.

class foley.index.KeywordIndex(*args, **kwargs)[source]#

Bases: Protocol

BM25 / full-text index over each sound’s tags + caption.

The default is LanceDB’s Tantivy FTS (report 04 §3.4); SQLite FTS5 is the single-file fallback. Same where push-down contract as VectorIndex.

bm25(query, k, *, where=None)[source]#

Return the top-k BM25 matches for query, best first.

Parameters:
  • query (str) – A natural-language / keyword query.

  • k (int) – Number of matches to return.

  • where (Optional[dict]) – Optional metadata predicates for push-down filtering.

Return type:

list[tuple[str, float]]

Returns:

[(id, bm25_score), ...] in descending-score order.

index(id, text, meta)[source]#

Insert or replace the searchable text (and light metadata) for id.

Return type:

None

class foley.index.LanceIndex(*, uri, dim, table_name='sounds')[source]#

Bases: object

LanceDB-backed index: one table with a vector column + a native FTS index.

Writes are staged per id and flushed on the next read (or explicit commit()), 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 by foley.index.search, not by LanceDB’s native hybrid reranker, so ranking matches every other backend.

One-table constraint: the vector column is mandatory, so **every indexed row needs a vector**. SoundLibrary.add enforces this (it raises without an embedding source), so the façade path is safe; a bare index() with no matching upsert() stages a text-only row that stays unflushed (never keyword-searchable). For a keyword-only library with no embeddings, use MemoryIndex or SqliteVecIndex (independent vector/text tables).

bm25(query, k, *, where=None)[source]#

Native full-text (BM25) search; returns [(id, score), ...].

Return type:

list[tuple[str, float]]

commit()[source]#

Flush all staged writes to the LanceDB table.

Return type:

None

property db#

The lazily-connected LanceDB database handle.

get_vector(id)[source]#

Return the stored vector for id (staged or persisted), else None.

Return type:

Optional[ndarray]

index(id, text, meta)[source]#

Stage the searchable text for id (flushed on the next read/commit).

Return type:

None

knn(vector, k, *, where=None)[source]#

Exact cosine KNN; returns [(id, cosine_similarity), ...].

Return type:

list[tuple[str, float]]

property table#

The lazily-opened (or created-empty) LanceDB table.

upsert(id, vector, meta)[source]#

Stage the vector for id (flushed on the next read/commit).

Return type:

None

class foley.index.MemoryIndex(*, dim=None)[source]#

Bases: object

In-memory vector + keyword index (numpy cosine + compact BM25).

Not persistent — everything lives in dicts, lost on process exit. It exists so the full hybrid + façade path is testable and usable with only numpy (no LanceDB, no torch), and as a genuine zero-config tier for small or ephemeral libraries. The vector and text stores are independent dicts, so upsert() and index() never contend.

bm25(query, k, *, where=None)[source]#

Return the top-k BM25 matches for query (best first).

A compact Okapi BM25 (k1=1.5, b=0.75) recomputed per query — O(N) in the corpus size, which is fine for the in-memory tier’s scale.

Return type:

list[tuple[str, float]]

commit()[source]#

No-op (writes are immediate); present for interface symmetry.

Return type:

None

get_vector(id)[source]#

Return the stored vector for id (or None).

Return type:

Optional[ndarray]

index(id, text, meta)[source]#

Insert or replace the searchable text (and light metadata) for id.

Return type:

None

knn(vector, k, *, where=None)[source]#

Return the k cosine-nearest ids to vector (most-similar first).

Return type:

list[tuple[str, float]]

upsert(id, vector, meta)[source]#

Insert or replace the vector (and light metadata) for id.

Return type:

None

class foley.index.PannsTagger(*, device='cpu', threshold=0.1)[source]#

Bases: object

PANNs CNN14 supervised tagger over the 527 AudioSet classes (foley[tag]).

The checkpoint auto-downloads to ~/panns_data (~327 MB) on the first tag(). PANNs expects 32 kHz mono; the clip is resampled via foley.audio.to_working().

tag(wav, sr, *, taxonomy='audioset', top_k=10)[source]#

Return the top-k (AudioSet label, score) tags, best first.

Return type:

list[tuple[str, float]]

class foley.index.SoundLibrary(*, sounds=None, meta=None, vindex=None, kindex=None, embedder=None, data_dir=None, candidate_k=50, rrf_k=60)[source]#

Bases: Mapping

A searchable, license-aware library of sounds (the foley INDEX façade).

Read it as a Mapping of SoundRecord`s; search it with :meth:`search (text) / search_clip() (a reference clip) / similar() (audio<->audio by id); browse it with filter(); grow it with add().

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 indexes caption``+``tags into the keyword index.

A sound is retrieval-first, so it MUST carry an embedding: supply either data (bytes to embed — note a by-reference sound is embedded from its transient bytes even though they are not cached) or a precomputed vector. Adding with neither raises, rather than silently indexing a vectorless row (which the single-table LanceIndex cannot persist, producing backend-dependent search results).

Parameters:
  • record (SoundRecord) – The record to add (mutated by store_sound with 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 computing vector when it is not supplied).

  • vector (Optional[ndarray]) – A precomputed CLAP embedding; when omitted and data is given, it is computed via the library’s embedder.

Return type:

SoundRecord

Returns:

The same (persisted, indexed) record.

Raises:

ValueError – If neither data nor vector is provided (no way to obtain an embedding).

array(sound_id, *, sr=None, mono=True)[source]#

Decode a sound to a working array (float32).

Parameters:
  • sound_id (str) – The record id.

  • sr (Optional[int]) – Target sample rate (default: the working rate, 48 kHz).

  • mono (bool) – Down-mix to mono (default True).

Return type:

ndarray

Returns:

The decoded working array.

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:

bytes

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 data_dir: Path#

The data root for default stores/index.

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 any record_attr=value equality predicate.

Return type:

list[SoundRecord]

property kindex#

The keyword index.

property meta#

The metadata store (id -> SoundRecord).

search(query, *, k=10, filters=None, commercial_ok=None, ucs_category=None, min_snr=None, duration_range=None, rerank=False)[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]) – If True, keep only commercially-usable sounds.

  • ucs_category (Optional[str]) – Keep only sounds with this UCS CatID.

  • min_snr (Optional[float]) – Keep only sounds whose QC snr_db is at least this.

  • duration_range (Optional[tuple[float, float]]) – Keep only sounds whose duration_s is in (min, max).

  • rerank (bool) – Re-order the shortlist by direct query<->audio cosine (fills the CLAP score for keyword-only hits).

Return type:

list[Candidate]

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:

list[Candidate]

Returns:

Up to k :class:`~foley.base.Candidate`s, most-similar first.

similar(sound_id, *, k=10)[source]#

Return the k sounds most similar to sound_id (audio<->audio).

Uses the stored vector (no re-decoding); the query sound itself is excluded from the results.

Return type:

list[Candidate]

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.id is not in the library.

Return type:

SoundRecord

property vindex#

The vector index.

class foley.index.SqliteVecIndex(*, path, dim)[source]#

Bases: object

Single-file index: sqlite-vec vec0 KNN + stdlib FTS5 keyword search.

The whole index is one SQLite file behind two virtual tables (independent, so upsert() and index() never contend). **Requires an interpreter whose sqlite3 permits loadable extensions** — probe with sqlite_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 rank is more-negative-is-better; it is negated so the returned score is larger-is-better (consistent with the other backends).

Return type:

list[tuple[str, float]]

close()[source]#

Close the underlying SQLite connection.

Return type:

None

commit()[source]#

Commit any pending SQLite transaction (writes auto-commit already).

Return type:

None

get_vector(id)[source]#

Return the stored vector for id (or None).

Return type:

Optional[ndarray]

index(id, text, meta)[source]#

Insert or replace the searchable text for id in the FTS5 table.

Return type:

None

knn(vector, k, *, where=None)[source]#

KNN over vec0 (cosine); returns [(id, cosine_similarity), ...].

Return type:

list[tuple[str, float]]

upsert(id, vector, meta)[source]#

Insert or replace the vector for id in the vec0 table.

Return type:

None

class foley.index.Tagger(*args, **kwargs)[source]#

Bases: Protocol

Map a clip to (label, score) pairs against a label vocabulary.

Used on ingest to auto-fill SoundRecord.audioset_labels (supervised) and tags (zero-shot). The default supervised tagger is PANNs CNN14 over AudioSet; the default zero-shot tagger scores a clip against a custom/UCS label set via CLAP (report 03). BEATs/AST are drop-in upgrades. Consumes the working array (float32, any sr — the impl resamples to its model’s rate).

tag(wav, sr, *, taxonomy='audioset', top_k=10)[source]#

Return the top-k (label, score) tags for the clip, best first.

Return type:

list[tuple[str, float]]

class foley.index.VectorIndex(*args, **kwargs)[source]#

Bases: Protocol

Approximate-nearest-neighbour store over embedding vectors.

The default is LanceDB (report 04 §2); Qdrant/pgvector/sqlite-vec bind the same protocol behind the scenes. where is an optional metadata push-down the façade may pass; a backend that cannot push filters down MAY ignore it (the façade over-fetches and post-filters to stay correct either way).

get_vector(id)[source]#

Return the stored vector for id (or None if absent).

Needed by SoundLibrary.similar (fetch a sound’s own vector, then run knn()) and by the optional CLAP rerank (score keyword-only hits).

Return type:

Optional[ndarray]

knn(vector, k, *, where=None)[source]#

Return the k nearest ids to vector, most-similar first.

Parameters:
  • vector (ndarray) – A (dim,) query vector (already L2-normalized).

  • k (int) – Number of neighbours to return.

  • where (Optional[dict]) – Optional metadata predicates for push-down filtering.

Return type:

list[tuple[str, float]]

Returns:

[(id, cosine_similarity), ...] in descending-similarity order.

upsert(id, vector, meta)[source]#

Insert or replace the vector (and light metadata) for id.

Return type:

None

foley.index.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:

ClapEmbedder

foley.index.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-persistent MemoryIndex is never chosen automatically (a library must survive restarts); inject it explicitly for tests/ephemeral use.

Parameters:
  • data_dir – The library data root (a pathlib.Path-like).

  • dim (int) – The embedding dimensionality from the active embedder.

Returns:

A ready index object (both VectorIndex and KeywordIndex).

Raises:

RuntimeError – If no persistent backend is installed/usable.

foley.index.default_library()[source]#

The process-wide default library (local stores + CLAP + best index).

Return type:

SoundLibrary

foley.index.default_tagger()[source]#

The default supervised tagger (PANNs CNN14; foley[tag]).

Return type:

PannsTagger

foley.index.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=None uses the process-wide default embedder.

Return type:

ClapZeroShotTagger

foley.index.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:
  • vector_hits (list[tuple[str, float]]) – [(id, cosine_similarity), ...] best-first (from knn()).

  • keyword_hits (list[tuple[str, float]]) – [(id, bm25_score), ...] best-first (from bm25()).

  • k (int) – Number of fused hits to return.

  • rrf_k (int) – The RRF damping constant.

Return type:

list[FusedHit]

Returns:

The top-k :class:`FusedHit`s, each carrying its raw component scores.

Embed query, run the vector + keyword rankers, and RRF-fuse them.

Parameters:
  • query (str) – The natural-language query.

  • embedder (Embedder) – Text<->audio embedder (its embed_text produces 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:

list[FusedHit]

Returns:

The top-k fused :class:`FusedHit`s.

foley.index.ingest_folder(path, *, library=None, recursive=True, exts=('.wav', '.flac', '.aiff', '.aif', '.ogg', '.mp3', '.opus', '.m4a'), on_error='collect', **ingest_one_kw)[source]#

Ingest every audio file under path and return an IngestReport.

Parameters:
  • path – A folder (walked) or a single audio file.

  • library – Target library (default: the process-wide default).

  • recursive (bool) – Recurse into sub-folders.

  • exts (tuple[str, ...]) – Audio extensions to ingest.

  • on_error (str) – 'collect' records per-file errors and continues; 'raise' re-raises the first error. Either way an EgressBlocked (offline, the embedder’s weights not cached) raises at once: every file would fail the same way.

  • **ingest_one_kw – Forwarded to ingest_one() (license, taggers, QC flags, …).

Return type:

IngestReport

Returns:

An IngestReport (with .summary() counts and per-file results).

foley.index.ingest_one(src, *, library=None, sound_id=None, source_uri=None, license=None, tagger=None, zeroshot_tagger=None, captioner=None, do_qc=True, min_status=QCStatus.warn, do_supervised=True, do_zeroshot=True, do_caption=True, thresholds=QCThresholds(clip_full_scale=0.999, clip_min_run=3, clip_reject_ratio=0.0001, clip_reject_run=10, true_peak_max_dbtp=-1.0, true_peak_oversample=4, dc_offset_fail=0.01, dc_offset_warn=0.001, silence_rms_dbfs=-60.0, snr_clean_db=20.0, snr_quiet_percentile=10.0, snr_frame_s=0.025, snr_hop_s=0.01, edge_rel_peak_dbfs=-40.0, edge_fade_s=0.01, lufs_gate_floor=-70.0, lufs_outlier_lu=6.0, duration_min_s=0.1, deliver_min_sample_rate=44100), store=True, allow_ai_training_forbidden=False, seed_tags=None, commercial=None)[source]#

Ingest one clip into library and return an IngestResult.

Pipeline: probe + decode-once -> content-address dedup -> QC gate -> embed (once) -> supervised + zero-shot tags -> caption -> resolve UCS -> assemble SoundRecord -> SoundLibrary.add().

Parameters:
  • src (Union[str, 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 to None → the content-hash of the decoded PCM (the local-ingest identity, used as the record id and dedup key). A live source adapter passes a short, case-stable, source-native id (e.g. 'freesound:12345') so dedup keys on the stable id rather than on re-fetched (lossy, byte-varying) preview bytes; when it does, the PCM hash is computed only for the (skipped) default and is not persisted. Separately, content_sha256 records the hash of the stored FLAC archive bytes (set by store_sound), which is a different byte source from this PCM hash.

  • source_uri (Optional[str]) – Optional by-reference fetchable URI override. Defaults to None → the resolved local path when src is path-like. A live adapter passes the stable source page URL (e.g. 'https://freesound.org/s/12345/') that foley.stores.store_sound() requires for a by-reference sound.

  • license (Optional[LicenseRecord]) – Rights record, or a license_id string the caller asserts ('user-owned'), or None (default): rights unknown, so the clip is indexed but foley.keep() refuses it (#55). See resolve_ingest_license().

  • tagger – Supervised Tagger (default: PANNs via default_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 (default warn: only fail clips are rejected).

  • do_caption (bool) – Toggle each enrichment stage.

  • thresholds (QCThresholds) – QC thresholds.

  • store (bool) – If False, 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’s tags alongside 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 has ai_training_ok=False (e.g. Sonniss, BBC RemArc) is refused with status 'rights_blocked' before it is embedded or stored — CLAP-embedding-and-persisting is itself a form of AI training on the corpus. Pass True to record explicit operator consent and admit it anyway (see foley.bootstrap.bootstrap()’s accept_ai_restricted). Protects every ingest path, not just bootstrap.

  • commercial (Optional[bool]) – Whether the AI use is for a commercial purpose (decides a nc_open_source_only scope, #69). None (default) means DEFAULT_INTENDED_USE’s (commercial).

Return type:

IngestResult

Returns:

An IngestResult; its record is None when quarantined, a duplicate, or rights-blocked.

foley.index.lancedb_available()[source]#

True if lancedb is importable (the foley[index] extra is present).

Return type:

bool

foley.index.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).

Parameters:
  • filename – A path or filename (only the basename’s token 0 is used).

  • table (Optional[UcsTable]) – The UCS table to resolve against (defaults to default_ucs_table()).

Return type:

tuple[Optional[str], Optional[str]]

foley.index.reciprocal_rank_fusion(ranked_id_lists, *, k=60)[source]#

Fuse several ranked id lists into one, by reciprocal rank.

Parameters:
  • ranked_id_lists (list[list[str]]) – Each element is a list of ids in descending-relevance order (best first). Lists may overlap and may differ in length.

  • k (int) – The RRF damping constant (default RRF_K = 60).

Return type:

list[tuple[str, float]]

Returns:

[(id, fused_score), ...] sorted by fused score descending, ties broken by id ascending (so the fusion is fully deterministic).

foley.index.resolve_catid(*, tags=(), caption=None, audioset_labels=(), filename=None, table=None, audioset_map=None)[source]#

Resolve inputs to a best UCS CatID by the staged precedence.

Parameters:
Return type:

CatIdResolution

Returns:

A CatIdResolution (falsy when nothing resolved).

foley.index.resolve_ingest_license(license, *, source_url=None)[source]#

The rights record for a local ingest: fail-closed unless rights are asserted (#55).

  • None (no licence given) → license_id='unknown', rights_verified=False: foley.keep() refuses it for every use. The bytes are still kept locally

    (cache_bytes_ok=True: it is the user’s own disk, no terms of service apply), so the clip is searchable and can be re-stamped later without re-ingesting.

  • a str → that license_id, which must be a LICENSE_FLAGS row, with rights_verified=True (the caller is asserting it) and verified_at set — e.g. 'user-owned' for the user’s own recordings.

  • a LicenseRecord → used as given.

Raises:

ValueError – If a str licence id has no LICENSE_FLAGS row.

Return type:

LicenseRecord

foley.index.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 stamped user-owned without being told becomes unknown (fail closed);

  • foley restamp-rights --license user-owned --apply — the same sounds, but you assert that you own them (now recorded with a verified_at timestamp);

  • foley restamp-rights --select legacy-elevenlabs --license elevenlabs-paid-plan --apply — ElevenLabs generations made before the plan was explicit;

  • foley restamp-rights --select has-license-url --license from-url --apply — every record that kept its source’s licence string (Freesound pulls) is re-derived by today’s mapper (CC versions, PDM, NC spellings);

  • corpus clips (Clotho, FSD50K) are repaired by re-running foley bootstrap, which re-stamps already-stored clips from the corpus metadata.

Parameters:
  • library – The SoundLibrary (default: the default one).

  • license (str) – The license_id to stamp (a LICENSE_FLAGS row; default 'unknown'), or FROM_LICENSE_URL to re-derive each record from its own license_url.

  • ids (Optional[list[str]]) – Re-stamp exactly these sound ids (overrides where / select).

  • where – A predicate LicenseRecord -> bool choosing the records (overrides select).

  • select (str) – A LEGACY_SELECTORS name (default 'legacy-user-owned').

  • apply (bool) – Write the change. False (default) only reports what would change.

Return type:

list[str]

Returns:

The ids that were (or, in a dry run, would be) re-stamped.

Raises:

ValueError – If license or select is unknown.

foley.index.sqlite_vec_loadable()[source]#

True if sqlite_vec is installed AND this interpreter can load it.

The macOS system / pyenv CPython builds frequently ship a sqlite3 without loadable-extension support (no enable_load_extension); on those, sqlite-vec cannot be used even when ``pip install``ed. This probes both.

Return type:

bool

Pure audio<->audio (or clip->library) vector search — no keyword leg.

Used by SoundLibrary.similar and by searching with a reference clip. Hits keep their cosine similarity in clap_score and preserve the index’s own descending-similarity order (rrf_score is left None — there is no fusion).

Parameters:
  • qvec (ndarray) – An already-L2-normalized (dim,) query vector.

  • vindex (VectorIndex) – The vector index.

  • k (int) – Number of neighbours to return.

  • where (Optional[dict]) – Optional metadata push-down.

Return type:

list[FusedHit]

Returns:

Up to k :class:`FusedHit`s in descending-similarity order.

Modules

taxonomy

UCS + AudioSet taxonomy: the tags -> UCS CatID resolver for foley.

embedders

Embedders — the joint text<->audio space that powers retrieval.

indexes

Concrete vector + keyword index backends (the swappable retrieval engines).

ingest

The ingestion pipeline — turn any audio file into a searchable SoundRecord.

library

The SoundLibrary façade — one searchable, license-aware sound library.

protocols

Structural contracts for the foley index (the retrieval boundary).

search

Hybrid retrieval: reciprocal rank fusion over a vector list and a keyword list.

taggers

Taggers — auto-fill a sound's semantic labels on ingest (report 03).