Ratel Docs
ReferencePython packages

ratel-ai

Raw API of ratel-ai — every exported class, function, and constant of the Python SDK.

v0.7.0 · Python · PyPI · source

Generated from the doc comments at ratel-ai/ratel@d8635d5 — do not edit by hand. Regenerate with pnpm sync:api in apps/docs.

Classes

AdaptiveRankingStatus

The adaptive-ranking status, enriched with the models a pause involves.

A plain str== "active" and .startswith("paused") work as before — that also exposes, when the arm is paused by a model change, the fingerprint the graph was built with, the currently active model, and whether it is a dim_mismatch (different width) vs a same-width different model. All three are None unless the status is a paused: .... Mirrors the object the TS SDK returns, so the detail is reachable without parsing stderr.

Attributes:

  • active: str | None
  • built: str | None
  • dim_mismatch: bool | None

IntentGraph

IntentGraph()

A shared usage-ranking intent graph (ADR-0014).

Clusters of past queries, each remembering the capabilities invoked after them. Hand the same instance to a tool catalog and a skill catalog: one cluster carries both a tool and a skill edge map, so sharing gives one set of clusters with all the evidence behind it, while separate graphs duplicate every cluster and split the evidence.

An empty graph — knows nothing until a search is followed by an invoke.

Attributes:

  • cluster_count: int — How many clusters the graph holds. 0 is the cold-start state, in which it contributes nothing to ranking.
  • rev: int — Monotonic write counter, bumped once per mutation. Never affects ranking — a primitive for your storage layer. Snapshot it after each save: a later value means unsaved learning (save-when-changed), and a stored graph whose rev is higher than the one you loaded was written by another process (stale-base detection).

from_json

from_json(json: str) -> IntentGraph

Adopt a graph in the protocol/v1 wire form.

Accepts output from ratel-graph build, a previous to_json(), or Ratel Cloud. Raises ValueError if it is malformed or declares a schema version this build does not read.

to_json

to_json() -> str

Serialize to the protocol/v1 wire form.

For inspection, or to carry what was learned across processes. The graph is in-process only; persistence is yours. It mutates on every confirmed invoke, so unsaved observations are lost on a crash — persist on a cadence or at shutdown. Use rev to save only when it changed and to detect a concurrent writer; single-writer is the supported model.

SENSITIVE: the output contains the raw text of past user queries (the cluster members). Treat a persisted graph like your query/telemetry log — restrict permissions (0600), keep it out of version control and images, and do not ship it to a less-trusted store.

DimensionMismatchError

A query/corpus embedding dimension mismatch.

EmbedderError

Embedding model load / inference failure (subclass of RuntimeError).

EndpointEmbeddingConfig

OpenAI-compatible embedding endpoint configuration.

Attributes:

  • model: str
  • url: str

ExecutableTool

ExecutableTool(id: str, name: str, description: str, input_schema: dict[str, Any] = dict(), output_schema: dict[str, Any] = dict(), execute: Executor = None)

A Tool plus the handler that runs it. Registered into a ToolCatalog.

Attributes:

  • execute: Executor = field(default=None)

HuggingFaceEmbeddingConfig

In-process HuggingFace embedding model configuration.

Attributes:

  • huggingface: str

LocalEmbeddingConfig

In-process local-directory embedding model configuration.

Attributes:

  • local: str

McpServerHandle

McpServerHandle(tool_ids: list[str], server_instructions: str | None, close: Callable[[], Awaitable[None]])

What register_mcp_server returns: the registration's outcome.

Attributes:

  • tool_ids (list[str]) — namespaced <server>__<tool> ids in upstream list order (all pages). Duplicate tool names may appear more than once; ToolCatalog.register keeps the last row per id.
  • server_instructions (str | None) — the instructions value passed to register_mcp_server (the caller reads it from its own initialize result), or None.
  • close (Callable[[], Awaitable[None]]) — async teardown — the on_close passed to register_mcp_server, or a no-op. The session itself stays caller-owned either way.

McpToolsListError

McpToolsListError(message: str, code: McpToolsListErrorCode)

Raised when MCP tools/list pagination is invalid or exceeds the page cap.

Set the error message and pagination failure code.

Attributes:

  • code = code
  • name = 'McpToolsListError'

OllamaEmbeddingConfig

Local Ollama embedding endpoint configuration.

Attributes:

  • ollama: str

PendingReplace

PendingReplace(added: int = 0, removed: int = 0, updated: int = 0, unchanged: int = 0, _driver: Awaitable[None] | None = None)

What replace_all returns: final counts over a pending embedding pass.

The corpus swap commits synchronously, so the counts are already final when replace_all returns and can be read before the embedding pass settles — a source can report what a reload changed even when that pass fails. Always await the result regardless, so the embedding pass runs and its failure is raised rather than swallowed.

Note that this compares unequal to a bare ReplaceOutcome with the same counts: a dataclass __eq__ requires both sides to be the same class, so no field configuration changes that. Await first — the awaited value is a plain ReplaceOutcome — and compare that.

ReplaceOutcome

ReplaceOutcome(added: int = 0, removed: int = 0, updated: int = 0, unchanged: int = 0)

What a replace_all changed, counted by id.

updated covers any field edit (including a body-only rewrite); unchanged ids are identical to what was registered and keep their cached embedding, so a reload that changed nothing costs no embedding calls.

Attributes:

  • added: int = 0
  • removed: int = 0
  • unchanged: int = 0
  • updated: int = 0

SearchHit

A single search result: the matched tool id and its relevance score.

Attributes:

  • fused: bool — Whether score is a Reciprocal Rank Fusion score (ordering-only). True when the usage arm fused into this search or the method is hybrid; False for a plain BM25/semantic result. Uniform across one result list; lets you detect which scale score is on.
  • rank: int — 0-based position in this result list (best is 0). Stable across methods and across the fused switch — the field to order or threshold on, in place of the scale-shifting score.
  • score: float — Relevance score; higher ranks first. The scale depends on the search method (raw BM25 / cosine / RRF) AND on fused — with adaptive ranking a matched query returns small RRF scores while an unmatched one on the same catalog returns the raw score. Order by rank, branch on fused; treat score as a within-list hint only.
  • tool_id: str — Id of the matched tool, as passed to register.

Skill

Skill(id: str, name: str, description: str, tags: list[str] = list(), tools: list[str] = list(), metadata: dict[str, list[str]] = dict(), body: str = '')

Skill metadata: what the index ranks and the capability tools surface.

Attributes:

  • body: str = ''
  • description: str
  • id: str
  • metadata: dict[str, list[str]] = field(default_factory=dict)
  • name: str
  • tags: list[str] = field(default_factory=list)
  • tools: list[str] = field(default_factory=list)

SkillCatalog

SkillCatalog(trace: TraceSinkConfig | None = None, method: SearchMethod = 'bm25', embedding: EmbeddingSpec | None = None)

Registry of skills. Register once, then search and load bodies by id.

Create an empty skill catalog.

Parameters:

  • trace (TraceSinkConfig | None) — where trace events go; None keeps the default no-op sink.
  • method (SearchMethod) — default retrieval method for search — see ToolCatalog.__init__; a "semantic"/"hybrid" catalog embeds inside register, and dense results come from search_async.
  • embedding (EmbeddingSpec | None) — model for semantic/hybrid retrieval — see ToolCatalog.__init__; retained and validated under "bm25" too.

Attributes:

  • experimental_adaptive_ranking_status: AdaptiveRankingStatus — Adaptive-ranking status: active, inactive, unknown, or paused.

drain_trace_events

drain_trace_events() -> list[dict[str, Any]]

Drain captured trace envelopes; [] unless the sink is "memory".

experimental_disable_adaptive_ranking

experimental_disable_adaptive_ranking() -> None

Turn adaptive usage ranking off; the graph keeps what it learned.

experimental_enable_adaptive_ranking

experimental_enable_adaptive_ranking(graph: IntentGraph, *, warn_on_model_mismatch: bool = True, rebuild_on_model_change: bool = False) -> None

Turn on adaptive usage ranking against graph (ADR-0014).

Wires both halves: this catalog ranks against what users have actually invoked after similar queries, and keeps learning as it is used. Pass the same :class:IntentGraph to the other catalog so both learn into one set of clusters.

Only queries matching a cluster are affected. With a graph attached the hit score becomes a fusion score rather than a raw BM25 score, so use rank for ordering and fused to detect the scale, not the raw score.

Set rebuild_on_model_change to auto-recover a model-mismatched graph on the next dense search rather than staying paused until you call :meth:experimental_rebuild_intent_graph yourself. Off by default — the rebuild is an embedding pass (cost, possible :class:EmbedderError, mutates the graph).

experimental_rebuild_intent_graph

async experimental_rebuild_intent_graph() -> None

Re-embed the graph's members under the current model; preserves learning.

get

get(skill_id: str) -> Skill | None

Return the registered Skill for an id, or None if unknown.

has

has(skill_id: str) -> bool

Return whether a skill with this id is registered.

invoke

invoke(skill_id: str) -> str

Return a skill's body for dispatch, recording a skill_invoke event.

Synchronous, unlike ToolCatalog.invoke — the body is already in memory, there is no handler to run.

Parameters:

  • skill_id (str) — id of a registered skill.

Returns: str — The skill's body (Markdown), verbatim as registered.

Raises:

  • ValueError — on an unknown id — callers at the capability-tool boundary translate this into a structured error for the agent.

record_event

record_event(event: dict[str, Any]) -> None

Record a trace event into the catalog's sink.

See ToolCatalog.record_event for the event contract.

register

register(skills: Skill | Iterable[Skill]) -> Awaitable[None]

Register one skill or many — the single entry point for both.

Metadata is stored synchronously when register(...) is called (name/description/tags are indexed; tools, metadata, body are stored but not indexed), so a forgotten await never drops the corpus. The returned awaitable drives only the embedding pass: on a "semantic"/"hybrid" catalog it embeds the batch off-thread and surfaces errors when awaited; a BM25 catalog never loads a model. Always await the result. Re-registering an id replaces it in place.

A model or dimension change is not recovered in place — construct a new catalog and re-register.

Parameters:

  • skills (Skill | Iterable[Skill]) — a single Skill or an iterable of them. Pass the whole batch at once for a single embedding request.

Raises:

  • EmbedderError — on a semantic/hybrid catalog, if embedding fails (when awaited).
  • RuntimeError — if another operation already owns the registry — dense work, but also an in-flight BM25 search_async holding the read lock. registry busy is retryable, not fatal.

replace_all

replace_all(skills: Iterable[Skill]) -> PendingReplace

Make skills the entire content of the catalog.

Ids absent from the batch are removed, the rest are added or updated — the whole-catalog counterpart to register, for a source that reloads a catalog rather than pushing individual changes. Mutates in place, so every holder of this catalog observes the reload without being rebuilt.

Removal is unconditional. Skills registered in-process are dropped just like any others, since the batch is the catalog. A host that keeps local skills alongside a remote source composes the batch itself: await catalog.replace_all([*local_skills, *remote_skills]).

Like register, the corpus swap lands synchronously and the returned awaitable drives only the embedding pass — which embeds just the new and re-worded skills, so reloading an unchanged catalog costs nothing. If it fails, the new corpus is already live and BM25 still ranks it; semantic search raises until a later pass succeeds. Always await the result.

Parameters:

  • skills (Iterable[Skill]) — the complete catalog contents. A repeated id keeps its last entry; an empty iterable clears the catalog.

Returns: PendingReplace — A PendingReplace: the counts, already final, over the pending embedding pass. Read them without awaiting; await the result to drive the embedding pass.

Raises:

  • TypeError — if any item is not a Skill.
  • EmbedderError — on a semantic/hybrid catalog, if embedding fails (when awaited).
  • RuntimeError — if another operation already owns the registry — dense work, but also an in-flight BM25 search_async holding the read lock. registry busy is retryable, not fatal.
search(query: str, top_k: int, origin: SearchOrigin = 'direct', method: SearchMethod | None = None) -> list[SkillHit]

Rank registered skills synchronously with BM25.

The skill twin of ToolCatalog.search: a dense resolved method raises immediately with guidance to use search_async.

Returns: list[SkillHit] — Up to top_k SkillHits, best first.

search_async

async search_async(query: str, top_k: int, origin: SearchOrigin = 'direct', method: SearchMethod | None = None) -> list[SkillHit]

Rank skills asynchronously with BM25, semantic, or hybrid retrieval.

Dense methods require a complete cache built explicitly beforehand.

size

size() -> int

Return the number of registered skills.

SkillHit

A single skill search result: the matched skill id and its relevance score.

The skill analogue of SearchHit (tool_idskill_id).

Attributes:

  • fused: bool — Whether score is an RRF score — as on SearchHit.fused.
  • rank: int — 0-based position — as on SearchHit.rank.
  • score: float — Relevance score; higher ranks first. Scale depends on the method and on fused, as on SearchHit.score. Order by rank, branch on fused.
  • skill_id: str — Id of the matched skill, as passed to register.

SkillRegistry

SkillRegistry(embedding: EmbeddingSpec | None = None, *, method: SearchMethod = 'bm25', spec: str | None = None, huggingface: str | None = None, local: str | None = None, ollama: str | None = None, url: str | None = None, model: str | None = None, revision: str | None = None, api_key_env: str | None = None, query_prefix: str | None = None, doc_prefix: str | None = None, pooling: str | None = None, download: bool | None = None)

Typed Python facade over the private native skill registry.

Create a metadata registry with an optional embedding model.

A "semantic"/"hybrid" method makes register embed eagerly (inside the call, on a worker thread); "bm25" keeps registration model-free.

Attributes:

  • experimental_adaptive_ranking_status: AdaptiveRankingStatus — Adaptive-ranking status; a str that also carries a pause's model detail.

drain_trace_events

drain_trace_events() -> list[dict[str, Any]]

Drain captured native trace events.

experimental_disable_adaptive_ranking

experimental_disable_adaptive_ranking() -> None

Turn adaptive usage ranking off; the graph keeps what it learned.

experimental_enable_adaptive_ranking

experimental_enable_adaptive_ranking(graph: IntentGraph, *, warn_on_model_mismatch: bool = True, rebuild_on_model_change: bool = False) -> None

Turn on adaptive usage ranking against graph (ADR-0014).

Wires both halves: this registry ranks against what users have actually invoked after similar queries, and keeps learning as it is used. Pass the same :class:IntentGraph to the other registry so both learn into one set of clusters.

Only queries matching a cluster are affected. With a graph attached the hit score becomes a fusion score rather than a raw BM25 score, so use rank for ordering and fused to detect the scale, not the raw score.

Set rebuild_on_model_change to recover automatically instead: the next dense (semantic/hybrid) search re-embeds the graph under the current model before searching, so a persisted graph self-heals after a model swap. It is off by default because the rebuild is an embedding pass — expensive, able to raise :class:EmbedderError, and it mutates the graph (new centroids, bumped rev). Recovery is lazy: status stays paused until that first dense search.

experimental_rebuild_intent_graph

async experimental_rebuild_intent_graph() -> None

Re-embed the graph's members under the current model; preserves learning.

record_event

record_event(event: dict[str, Any]) -> None

Record an SDK-layer trace event.

register

register(item: Skill | Iterable[Skill] | str, name: str | None = None, description: str | None = None, tags: list[str] | None = None, tools: list[str] | None = None, metadata: dict[str, list[str]] | None = None, body: str | None = None) -> Awaitable[None]

Register one Skill, many Skills, or a flat (id, name, …) tuple.

Metadata is indexed synchronously when register(...) is called (a forgotten await never drops the corpus); the returned awaitable drives only the embedding pass. On a "semantic"/"hybrid" registry it embeds in one batched, off-thread pass (errors surface when awaited); "bm25" has nothing to embed. Always await the result.

replace_all

replace_all(skills: Iterable[Skill]) -> PendingReplace

Make skills the entire corpus — see SkillCatalog.replace_all.

The corpus swap lands synchronously (a forgotten await never leaves a half-applied reload); the returned PendingReplace already carries the counts and drives only the embedding pass when awaited. Always await the result.

search

search(query: str, top_k: int) -> list[SkillHit]

Run synchronous, model-free BM25 retrieval.

search_async

async search_async(query: str, top_k: int, origin: SearchOrigin = 'direct', method: SearchMethod = 'bm25') -> list[SkillHit]

Search immediately with BM25 or run dense retrieval on a worker thread.

search_with_method

search_with_method(query: str, top_k: int, origin: SearchOrigin, method: SearchMethod) -> list[SkillHit]

Run BM25 synchronously; dense retrieval is async-only.

search_with_origin

search_with_origin(query: str, top_k: int, origin: SearchOrigin) -> list[SkillHit]

Run BM25 retrieval with an explicit trace origin.

set_trace_sink

set_trace_sink(kind: str, session_id: str | None = None, path: str | None = None) -> None

Replace the native trace sink.

Tool

Tool(id: str, name: str, description: str, input_schema: dict[str, Any] = dict(), output_schema: dict[str, Any] = dict())

Tool metadata: what the index ranks and the capability tools surface.

Attributes:

  • description: str
  • id: str
  • input_schema: dict[str, Any] = field(default_factory=dict)
  • name: str
  • output_schema: dict[str, Any] = field(default_factory=dict)

ToolCatalog

ToolCatalog(trace: TraceSinkConfig | None = None, method: SearchMethod = 'bm25', embedding: EmbeddingSpec | None = None)

Registry + executors. Register tools once, then search and invoke by id.

Create an empty catalog.

Parameters:

  • trace (TraceSinkConfig | None) — where trace events go; None keeps the default no-op sink.
  • method (SearchMethod) — default retrieval method for search — "bm25" (the historical, model-free behavior), "semantic" or "hybrid". A per-call method= overrides it. A "semantic"/"hybrid" catalog embeds inside register; dense results come from search_async.
  • embedding (EmbeddingSpec | None) — model for semantic/hybrid retrieval (a path string or a keyed dict — see EmbeddingSpec). Retained and validated even under "bm25" so a later async semantic override can use it.

Attributes:

  • experimental_adaptive_ranking_status: AdaptiveRankingStatus — Adaptive-ranking status: active, inactive, unknown, or paused.

drain_trace_events

drain_trace_events() -> list[dict[str, Any]]

Drain captured trace envelopes; [] unless the sink is "memory".

experimental_disable_adaptive_ranking

experimental_disable_adaptive_ranking() -> None

Turn adaptive usage ranking off; the graph keeps what it learned.

experimental_enable_adaptive_ranking

experimental_enable_adaptive_ranking(graph: IntentGraph, *, warn_on_model_mismatch: bool = True, rebuild_on_model_change: bool = False) -> None

Turn on adaptive usage ranking against graph (ADR-0014).

Wires both halves: this catalog ranks against what users have actually invoked after similar queries, and keeps learning as it is used. Pass the same :class:IntentGraph to the other catalog so both learn into one set of clusters.

Only queries matching a cluster are affected. With a graph attached the hit score becomes a fusion score rather than a raw BM25 score, so use rank for ordering and fused to detect the scale, not the raw score.

Set rebuild_on_model_change to auto-recover a model-mismatched graph on the next dense search rather than staying paused until you call :meth:experimental_rebuild_intent_graph yourself. Off by default — the rebuild is an embedding pass (cost, possible :class:EmbedderError, mutates the graph).

experimental_rebuild_intent_graph

async experimental_rebuild_intent_graph() -> None

Re-embed the graph's members under the current model; preserves learning.

get

get(tool_id: str) -> Tool | None

Return the metadata-only Tool for an id, or None if unknown.

get_executable

get_executable(tool_id: str) -> ExecutableTool | None

Return the ExecutableTool (metadata plus handler) for an id, or None.

has

has(tool_id: str) -> bool

Return whether a tool with this id is registered.

invoke

async invoke(tool_id: str, args: dict[str, Any]) -> Any

Run a registered tool's handler and return its result.

This is the canonical place that absorbs the sync/async executor difference: the handler is called first and the result awaited only if it is awaitable, so plain functions and async def executors (e.g. MCP/HTTP tools) are both supported. Callers must route invocations here rather than re-deriving that logic. Emits invoke_start / invoke_end / invoke_error trace events and wraps the call in an execute_tool OTel span (ADR-0007).

Parameters:

  • tool_id (str) — id of a registered tool.
  • args (dict[str, Any]) — the arguments dict passed to the handler.

Returns: Any — Whatever the handler returns (awaited if it returned an awaitable).

Raises:

  • ValueError — if tool_id is not registered.
  • Exception — whatever the handler raises, re-raised after an invoke_error trace event is recorded.

record_event

record_event(event: dict[str, Any]) -> None

Record a trace event into the catalog's sink.

Parameters:

  • event (dict[str, Any]) — a dict matching one of the core-owned TraceEvent shapes (ADR-0007), e.g. {"type": "gateway_search", ...}.

Raises:

  • ValueError — if the dict doesn't match any known event shape.

register

register(tools: ExecutableTool | Iterable[ExecutableTool]) -> Awaitable[None]

Register one tool or many — the single entry point for both.

Metadata and the executor handler are stored synchronously, the instant register(...) is called, so a forgotten await can never silently drop the corpus. The returned awaitable drives only the embedding pass: on a "semantic"/"hybrid" catalog it embeds the batch in one pass on a worker thread (the event loop is never blocked), and embedding errors (model load / endpoint / auth / dimension) surface when it is awaited. A BM25 catalog never loads a model and the awaitable is a no-op. Always await the result — a semantic/hybrid search after an un-awaited register raises rather than ranking an empty corpus. Re-registering an id replaces it in place; the index never holds a duplicate.

A model or dimension change is not recovered in place — construct a new catalog and re-register.

Parameters:

  • tools (ExecutableTool | Iterable[ExecutableTool]) — a single ExecutableTool or an iterable of them; each execute must be set. Pass the whole batch at once for a single embedding request; separate register calls embed separately.

Raises:

  • ValueError — if any execute is None, or a schema isn't JSON-serializable.
  • EmbedderError — on a semantic/hybrid catalog, if embedding fails (when awaited).
  • RuntimeError — if a dense operation already owns the registry.

search

search(query: str, top_k: int, origin: SearchOrigin = 'direct', method: SearchMethod | None = None) -> list[SearchHit]

Rank registered tools synchronously with BM25.

Parameters:

  • query (str) — what the caller wants to do.
  • top_k (int) — max hits to return.
  • origin (SearchOrigin) — who initiated the search — labels the trace event only.
  • method (SearchMethod | None) — per-call override of the catalog's default retrieval method ("bm25" | "semantic" | "hybrid").

Returns: list[SearchHit] — Up to top_k SearchHits, best first.

Raises:

  • ValueError — if method is not "bm25", "semantic" or "hybrid".
  • RuntimeError — if the resolved method is semantic/hybrid; use search_async for dense retrieval.

search_async

async search_async(query: str, top_k: int, origin: SearchOrigin = 'direct', method: SearchMethod | None = None) -> list[SearchHit]

Rank tools asynchronously with BM25, semantic, or hybrid retrieval.

Dense methods require the corpus to have been embedded by register on a semantic/hybrid catalog; searching never embeds missing corpus vectors.

ToolRegistry

ToolRegistry(embedding: EmbeddingSpec | None = None, *, method: SearchMethod = 'bm25', spec: str | None = None, huggingface: str | None = None, local: str | None = None, ollama: str | None = None, url: str | None = None, model: str | None = None, revision: str | None = None, api_key_env: str | None = None, query_prefix: str | None = None, doc_prefix: str | None = None, pooling: str | None = None, download: bool | None = None)

Typed Python facade over the private native tool registry.

Create a metadata registry with an optional embedding model.

A "semantic"/"hybrid" method makes register embed eagerly (inside the call, on a worker thread); "bm25" keeps registration model-free.

Attributes:

  • experimental_adaptive_ranking_status: AdaptiveRankingStatus — Adaptive-ranking status; a str that also carries a pause's model detail.

drain_trace_events

drain_trace_events() -> list[dict[str, Any]]

Drain captured native trace events.

experimental_disable_adaptive_ranking

experimental_disable_adaptive_ranking() -> None

Turn adaptive usage ranking off; the graph keeps what it learned.

experimental_enable_adaptive_ranking

experimental_enable_adaptive_ranking(graph: IntentGraph, *, warn_on_model_mismatch: bool = True, rebuild_on_model_change: bool = False) -> None

Turn on adaptive usage ranking against graph (ADR-0014).

Wires both halves: this registry ranks against what users have actually invoked after similar queries, and keeps learning as it is used. Pass the same :class:IntentGraph to the other registry so both learn into one set of clusters.

Only queries matching a cluster are affected. With a graph attached the hit score becomes a fusion score rather than a raw BM25 score, so use rank for ordering and fused to detect the scale, not the raw score.

On a model change the arm pauses and a one-time warning is issued unless warn_on_model_mismatch is False; call :meth:experimental_rebuild_intent_graph.

Set rebuild_on_model_change to recover automatically instead: the next dense (semantic/hybrid) search re-embeds the graph under the current model before searching, so a persisted graph self-heals after a model swap. It is off by default because the rebuild is an embedding pass — expensive, able to raise :class:EmbedderError, and it mutates the graph (new centroids, bumped rev). Recovery is lazy: status stays paused until that first dense search.

experimental_rebuild_intent_graph

async experimental_rebuild_intent_graph() -> None

Re-embed the graph's members under the current model; preserves learning.

Call after changing the embedding model: a graph's centroids are only comparable to queries from the model that built them, so on a swap the usage arm pauses until this runs.

record_event

record_event(event: dict[str, Any]) -> None

Record an SDK-layer trace event.

register

register(item: Tool | Iterable[Tool] | str, name: str | None = None, description: str | None = None, input_schema: dict[str, Any] | None = None, output_schema: dict[str, Any] | None = None) -> Awaitable[None]

Register one Tool, many Tools, or a flat (id, name, …) tuple.

Metadata is indexed synchronously, the instant register(...) is called, so a forgotten await can never silently drop the corpus. The returned awaitable drives only the embedding pass — on a "semantic"/"hybrid" registry it embeds in one batched, off-thread pass (embedding errors surface when awaited); "bm25" has nothing to embed and the awaitable is a no-op. Always await the result.

search

search(query: str, top_k: int) -> list[SearchHit]

Run synchronous, model-free BM25 retrieval.

search_async

async search_async(query: str, top_k: int, origin: SearchOrigin = 'direct', method: SearchMethod = 'bm25') -> list[SearchHit]

Search immediately with BM25 or run dense retrieval on a worker thread.

search_with_method

search_with_method(query: str, top_k: int, origin: SearchOrigin, method: SearchMethod) -> list[SearchHit]

Run BM25 synchronously; dense retrieval is async-only.

search_with_origin

search_with_origin(query: str, top_k: int, origin: SearchOrigin) -> list[SearchHit]

Run BM25 retrieval with an explicit trace origin.

set_trace_sink

set_trace_sink(kind: str, session_id: str | None = None, path: str | None = None) -> None

Replace the native trace sink.

TraceSinkConfig

TraceSinkConfig(kind: str, session_id: str | None = None, path: str | None = None)

Where the catalog's trace events go. Mirrors the TS TraceSinkConfig union.

kind: "noop" | "memory" | "jsonl". session_id is required for memory/jsonl; path is required for jsonl.

Attributes:

  • kind: str
  • path: str | None = None
  • session_id: str | None = None

UpstreamServerInfo

UpstreamServerInfo(name: str, description: str | None = None, instructions: str | None = None, tool_count: int | None = None, needs_auth: bool = False)

An upstream MCP server, as advertised in the capability-tool descriptions.

Pass a list of these to search_capabilities_tool (or the deprecated search_tools_tool) to append a "this catalog aggregates…" listing to the tool description shown to the model, and to enrich each search result's server group with the upstream's description and instructions.

Attributes:

  • name (str) — the catalog namespace of the server — the <name> prefix of its <name>__<tool> tool ids.
  • description (str | None) — what the server offers; compacted to one line in listings.
  • instructions (str | None) — the server's usage instructions (from its MCP initialize result), surfaced on matching server groups.
  • tool_count (int | None) — number of tools the server contributed, shown in listings.
  • needs_auth (bool) — True when the upstream rejected its boot connection with a 401 / re-auth needed; listings append "(auth required)".

Functions

configure_telemetry

configure_telemetry(*, api_key: str | None = None, endpoint: str | None = None, logs_endpoint: str | None = None, headers: dict[str, str] | None = None, service_name: str | None = None, capture_content: ContentCapture | str | None = None, include_span_and_events: bool | None = None, export_all_spans: bool = False) -> Any

Register Ratel-owned OTLP trace and Logs exporters for the greenfield case.

Ships the spans and EventRecords this SDK emits to Ratel Cloud (or any OTLP endpoint) by delegating to ratel_ai_telemetry.init, which needs the OpenTelemetry SDK — install it with pip install 'ratel-ai[otlp]'. A host already running its own OpenTelemetry providers should skip this (SDK telemetry flows to them) and add both ratel_span_processor and ratel_log_record_processor.

capture_content / include_span_and_events opt into message/tool content capture in code via set_content_capture: capture_content sets an exact mode, include_span_and_events is bool sugar (True -> SPAN_AND_EVENT, False -> NO_CONTENT); capture_content wins when both are given. A provided option beats OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT (the env var is the fallback when neither is given, as in OTel). The returned handle's shutdown() clears the override, restoring env-driven behavior; the clear is generation-scoped, so a stale handle shutting down late never clobbers an override a newer configure_telemetry/set_content_capture call installed.

Parameters:

  • api_key (str | None) — Ratel Cloud API key override; defaults to RATEL_API_KEY.
  • endpoint (str | None) — OTLP endpoint override; defaults to RATEL_OTLP_ENDPOINT, then the superseded RATEL_URL (which warns).
  • logs_endpoint (str | None) — OTLP logs endpoint override; defaults to the sibling /v1/logs URL derived from endpoint.
  • headers (dict[str, str] | None) — Extra headers sent with every export request.
  • service_name (str | None) — service.name resource attribute; defaults per init.
  • capture_content (ContentCapture | str | None) — Exact content-capture mode to set (see above).
  • include_span_and_events (bool | None) — Boolean sugar for capture_content (see above).
  • export_all_spans (bool) — Export every span, not just the gen_ai.*/ratel.* signal. Default False: this high-level path defaults to ratel_signal_filter so unrelated HTTP/database/application spans are not shipped to Ratel (privacy + cost). Set True to forward all spans.

Returns: Any — A per-call shutdown handle (handle.shutdown() / handle.force_flush()), the same shape on every path. Attribute access delegates to the shared handle init returns; because that handle is shared across callers, shutting it down stops export for all of them.

Raises:

  • ModuleNotFoundError — if the OpenTelemetry exporter is not installed (pip install 'ratel-ai[otlp]').
  • ValueError — if capture_content is not a recognized mode — raised before any exporter is wired, so a bad option has no side effects.

format_upstream_line

format_upstream_line(s: UpstreamServerInfo) -> str

Render one upstream server as a listing bullet for a tool description.

Format: - <name> — <description> (<N> tools) (auth required), where the description is whitespace-collapsed and truncated to ~160 chars, and each trailing part appears only when the corresponding field is set.

Parameters:

  • s (UpstreamServerInfo) — the upstream server to render.

Returns: str — A single - -prefixed line, ready to join into the "this catalog aggregates…" listing.

get_skill_content_tool

get_skill_content_tool(catalog: SkillCatalog) -> ExecutableTool

Build the get_skill_content tool: load a skill's full body by id.

The returned tool resolves skillId and answers {"body": <markdown>} via SkillCatalog.invoke (which records a skill_invoke trace event). It never raises into the host: an unknown or non-string id comes back as a structured {"error": ..., "isError": True} payload the model can recover from, mirroring invoke_tool.

Parameters:

  • catalog (SkillCatalog) — the skill catalog to load bodies from.

Returns: ExecutableTool — An ExecutableTool to put in the agent's direct tool list alongside search_capabilities.

invoke_tool_tool

invoke_tool_tool(catalog: ToolCatalog, *, on_unauthorized: OnUnauthorized | None = None) -> ExecutableTool

Build the invoke_tool tool: run any catalog tool by id.

The returned tool resolves toolId, forwards the nested args object to ToolCatalog.invoke, and never raises into the host: an unknown id, malformed args, or a tool exception all come back as a structured {"error": ..., "isError": True} payload the model can recover from. A flattened call (arguments at the top level instead of nested under args) is tolerated. When the tool raises an UnauthorizedError, the payload is {"error": "needs_auth", ...} with a re-auth hint and, for namespaced <server>__<tool> ids, the upstream server name.

Parameters:

  • catalog (ToolCatalog) — the catalog to resolve and run tool ids against.
  • on_unauthorized (OnUnauthorized | None) — called with the upstream server name when a tool raises UnauthorizedError; may be sync or async. Skipped when the tool id has no <server>__ namespace prefix.

Returns: ExecutableTool — An ExecutableTool to put in the agent's direct tool list.

register_mcp_server

async register_mcp_server(catalog: ToolCatalog, *, name: str, session: Any, transport_label: str = 'unknown', instructions: str | None = None, on_close: Callable[[], Awaitable[None]] | None = None) -> McpServerHandle

Ingest an initialized MCP ClientSession into the catalog.

Parameters:

  • catalog (ToolCatalog) — the catalog to register the upstream tools into.
  • name (str) — namespace prefix for tool ids (<name>__<tool>).
  • session (Any) — an initialized mcp.ClientSession owned by the caller.
  • transport_label (str) — recorded on the upstream_register trace event.
  • instructions (str | None) — the upstream's server instructions (from initialize), if any.
  • on_close (Callable[[], Awaitable[None]] | None) — optional async teardown invoked by the handle's close().

Returns: McpServerHandle — An McpServerHandle with the registered tool ids.

Raises:

  • ImportError — if the optional mcp package is not installed (pip install 'ratel-ai[mcp]').
  • McpToolsListError — if tools/list repeats a cursor or exceeds the page cap.

search_capabilities_tool

search_capabilities_tool(catalog: ToolCatalog, skill_catalog: SkillCatalog | None = None, *, upstream_servers: Sequence[UpstreamServerInfo] | None = None) -> ExecutableTool

Build the search_capabilities tool: unified discovery over tools AND skills.

The returned tool ranks two independent buckets, each with its own top-K budget — so a relevant skill is never starved out of the results by a large number of matching tools (and the two BM25 corpora are never score-compared). Tools land grouped by upstream server; a matched skill's declared tools are pulled into the tools bucket additively (score 0), so the agent gets the playbook and its toolkit in one turn. The skills bucket (and the mention of get_skill_content in the description) is only advertised when a non-empty skill_catalog is wired in.

Parameters:

  • catalog (ToolCatalog) — the tool catalog to search.
  • skill_catalog (SkillCatalog | None) — optional skill catalog, ranked against the same query in its own bucket.
  • upstream_servers (Sequence[UpstreamServerInfo] | None) — upstream MCP servers to advertise in the tool description and to enrich result server groups with.

Returns: ExecutableTool — An ExecutableTool to put in the agent's direct tool list.

search_tools_tool

search_tools_tool(catalog: ToolCatalog, *, upstream_servers: Sequence[UpstreamServerInfo] | None = None) -> ExecutableTool

Build the pre-0.2.0 tools-only discovery tool (id search_tools).

Keeps the original behaviour: a flat {groups} result and no skills bucket. New code should use ratel_ai.search_capabilities_tool instead. Registering both lets a host serve the old and new names during a migration window.

Parameters:

  • catalog (ToolCatalog) — the tool catalog to search.
  • upstream_servers (Sequence[UpstreamServerInfo] | None) — upstream MCP servers to advertise in the tool description and to enrich result server groups with.

Returns: ExecutableTool — An ExecutableTool to put in the agent's direct tool list.

Deprecated — Since 0.2.0: use ratel_ai.search_capabilities_tool. Tracked for removal in RAT-250.

Constants

  • GET_SKILL_CONTENT_ID = 'get_skill_content' — Id (and name) of the skill-loading tool built by get_skill_content_tool.
  • INVOKE_TOOL_ID = 'invoke_tool' — Id (and name) of the invocation tool built by invoke_tool_tool.
  • SEARCH_CAPABILITIES_ID = 'search_capabilities' — Id (and name) of the discovery tool built by search_capabilities_tool.
  • SEARCH_TOOLS_ID = 'search_tools' — Id (and name) of the deprecated pre-0.2.0 discovery tool built by search_tools_tool; superseded by ratel_ai.SEARCH_CAPABILITIES_ID.
  • EmbeddingModelConfig = Union[HuggingFaceEmbeddingConfig, LocalEmbeddingConfig, OllamaEmbeddingConfig, EndpointEmbeddingConfig] — Mutually exclusive keyed embedding-source configurations.
  • EmbeddingSpec = Union[str, EmbeddingModelConfig] — Embedding selection; a bare string is a local model directory path.
  • Executor = Callable[[dict[str, Any]], Union[Awaitable[Any], Any]] — A tool handler: takes the tool's arguments dict, returns the result. May be sync or async (tool inputs are heterogeneous across the catalog); ToolCatalog.invoke absorbs the difference.
  • OnUnauthorized = Callable[[str], Union[Awaitable[None], None]] — Notified when the underlying tool raises UnauthorizedError, with the upstream server name inferred from the toolId. May be sync or async.
  • SearchMethod = str — Retrieval engine: "bm25" (lexical, model-free, the default), "semantic" (dense embeddings) or "hybrid" (both, fused).
  • SearchOrigin = str — Who initiated a search: "direct" (host code, the default) or "agent" (a model calling a capability tool). Labels the emitted trace event only — ranking is unaffected.

On this page