Skip to content

API reference

The typed public API, rendered from docstrings. Reigner has no untyped public surfaces, so signatures here are authoritative.

Top-level package

Reigner: a single-agent, retrieval-shaped, citation-faithful agent library.

Plugin

Base for all plugins. Override the hooks you need; the rest no-op.

name is used in logs and error messages; it defaults to the class name when left blank.

before_tool_call async

before_tool_call(
    call: ToolCall, state: AgentState
) -> ToolCall

Inspect or rewrite a tool call before it runs. Return the call.

after_tool_call async

after_tool_call(
    call: ToolCall,
    result: ToolResultEvent,
    state: AgentState,
) -> ToolResultEvent

Inspect or rewrite a tool result after truncation. Return the result.

result is the bounded :class:ToolResultEvent (so result.truncated is meaningful); a returned copy with a rewritten result payload is what lands in both the model's context and the emitted event.

on_compaction async

on_compaction(state: AgentState, level: int) -> None

Observe a compaction tier firing (level is the tier index).

on_final_answer async

on_final_answer(
    answer: FinalAnswerEvent, state: AgentState
) -> FinalAnswerEvent

Inspect or rewrite the final answer before it is emitted.

on_error async

on_error(error: ErrorEvent, state: AgentState) -> None

Observe a terminal error before it is emitted.

on_oracle_escalation async

on_oracle_escalation(
    event: OracleEscalationEvent, state: AgentState
) -> None

Observe a single-turn oracle escalation being armed.

on_steering async

on_steering(
    event: SteeringAcceptedEvent, state: AgentState
) -> None

Observe a queued steering message being accepted into the loop.

SessionStore

Filesystem-backed store of session JSONL + meta sidecars.

One :class:SessionStore is owned by a :class:Harness; every :class:Session shares it. The store doesn't cache file handles — each :meth:append_event opens, writes, flushes, closes. Crash-safe per event at the cost of one open per event; that's the trade we want for an artifact you debug after the fact.

root property

root: Path

The store's root directory.

exists

exists(session_id: str) -> bool

Return whether a session JSONL file exists for session_id.

list

list() -> list[SessionMeta]

Return meta for every session under the root, sorted by id.

Malformed sessions (unreadable JSONL, version mismatch) are skipped so a single broken file doesn't poison the listing.

append_event

append_event(session_id: str, event: Event) -> None

Serialize event and append it as one line to {sid}.jsonl.

Flushes after each write so a crash mid-conversation loses at most the in-flight event. Meta is not touched here — call :meth:write_meta when summary fields need refreshing.

write_session_events

write_session_events(
    session_id: str, event_list: Iterable[Event]
) -> int

Write event_list as the complete JSONL for session_id (overwrites).

Used by :meth:Session.fork to snapshot a parent's event prefix into a fresh child file. The caller owns session_id consistency — events whose session_id field doesn't match are written as-is, so rewrite them (dataclasses.replace) before calling if a self-contained transcript is wanted. Returns the number of events written.

load_events

load_events(session_id: str) -> Iterator[Event]

Iterate the events of one session in write order.

Streaming on purpose: long sessions stay friendly to memory. Callers that want a list should wrap with list(...).

read_meta

read_meta(session_id: str) -> SessionMeta

Return meta for session_id, rebuilding from JSONL if missing.

If the sidecar exists it's the source of truth; if absent we scan the events to fill in what we can (event_count, created, last_updated). parent_id and title are unrecoverable from events alone and default to None after a rebuild.

write_meta

write_meta(session_id: str, meta: SessionMeta) -> None

Atomically write meta to the sidecar via tmp + os.replace.

set_title

set_title(
    session_id: str, title: str | None
) -> SessionMeta

Set (or clear) a session's title and return the updated metadata.

export

export(session_id: str, dest_path: str | Path) -> Path

Write the session's JSONL to dest_path plus a sidecar meta.

Takes a single .jsonl path and emits {dest}.meta.json next to it so title/created/parent_id survive the round trip.

Returns:

Type Description
Path

The destination JSONL path.

import_

import_(src_path: str | Path) -> str

Import a previously-exported session into this store.

Reuses the source session_id (from the sidecar meta if present, otherwise from the first event in the JSONL). On collision with an existing local session id, raises :class:FileExistsError so the caller can rename or delete the existing one. Returns the session id.

SessionMeta dataclass

Summary record for one session, persisted as {sid}.meta.json.

total_tokens and total_cost are deliberately absent — no event carries them yet. They'll be added as fields when later tasks emit usage events; the schema_version bumps in lockstep so old sessions still load via a migration path.

to_json

to_json() -> str

Serialize the metadata to indented JSON.

from_json classmethod

from_json(raw: str) -> SessionMeta

Parse :class:SessionMeta from its JSON form.

Parameters:

Name Type Description Default
raw str

JSON text previously produced by :meth:to_json.

required

Returns:

Type Description
SessionMeta

The decoded metadata.

Raises:

Type Description
ValueError

If the JSON root is not an object.

SchemaVersionMismatch

If the stored version is unsupported.

tool

tool(
    *,
    readonly: bool = False,
    cache: bool = False,
    truncate_chars: int | None = None,
    description: str | None = None,
    pseudo: bool = False,
) -> Callable[[F], F]

Decorate an async function as a Reigner tool.

Validates MCP-clean constraints at decoration time, generates a JSON Schema for the arguments via Pydantic, and attaches a ToolSpec to the function as __reigner_spec__. The original callable is returned unchanged — direct calls work in tests without going through the registry.

Parameters:

Name Type Description Default
readonly bool

Eligible for G9 caching and G11 parallel execution.

False
cache bool

Opt-in result caching keyed on (name, args).

False
truncate_chars int | None

Per-tool override of the G2 truncation budget.

None
description str | None

Overrides the function's docstring on the tool schema.

None
pseudo bool

Intercepted locally by the loop; implies readonly=True.

False

Returns:

Type Description
Callable[[F], F]

A decorator that attaches a ToolSpec and returns the function as-is.

Tools

reigner.tools

Reigner tool system — the @tool decorator, registry, and built-in tools.

ToolResult module-attribute

ToolResult = dict[str, Any]

Documented type alias for tool return values.

Calls for bounded, self-describing results (has_more, truncated, available_keys). Not enforced as a class because the right self-description fields vary by tool.

ArtifactStore

Schema-aware view of a compiled artifact root for read-side tools.

resolve

resolve(rel: str) -> Path

Resolve a root-relative path, rejecting traversal escapes.

rel must be a non-empty relative POSIX-style path. The resolved path is rejected if it doesn't stay under self.root.

resolve_entity

resolve_entity(**identifiers: str) -> Path

Resolve an entity directory from its entity_path identifiers.

tools

tools() -> list[RunnableToolAdapter]

Build the six built-in artifact tools as RunnableTool wrappers.

ToolDefinitionError

Bases: TypeError

Raised at decoration time when a function violates MCP-clean constraints.

ToolSpec dataclass

Metadata + invocation handle for a registered tool.

Structurally satisfies harness.state.ToolSpec Protocol — name, description, readonly, and json_schema() are the surface AgentState reads. The remaining flags (cache, truncate_chars, pseudo) are consumed by the loop guardrails and by profile filtering in the registry.

json_schema

json_schema() -> dict[str, Any]

JSON Schema for the tool's arguments. Surfaces in the model's tool list.

call async

call(**kwargs: Any) -> Any

Invoke the wrapped tool function.

Exceptions propagate. The loop wraps invocations and converts exceptions into ErrorEvent so failures stay observable.

FsTools

Root-bound sandbox for raw filesystem tools.

Pass exactly one of root (single-root mode) or roots (multi-root mode). See the module docstring for how the two modes differ.

Parameters:

Name Type Description Default
root str | Path | None

Single directory the agent is allowed to see. All tool paths are interpreted relative to this root and validated against it. Mutually exclusive with roots.

None
roots dict[str, str | Path] | None

Name→directory map exposed as one virtual tree. Each root name becomes a top-level directory; the first path segment selects the root. Mutually exclusive with root.

None
write_enabled bool

When True, tools() also emits fs_write. Defaults to False so the read-only case is the obvious default.

False
max_read_chars int

Per-call character cap for fs_read.

8000
max_grep_matches int

Total match cap for fs_grep.

20
max_ls_entries int

Entry cap for fs_ls.

200
max_glob_results int

Result cap for fs_glob.

200
text_extensions frozenset[str] | set[str] | None

Suffix allowlist (lowercase, with leading dot) for fs_read and the default fs_grep filter.

None
ignored_dirs frozenset[str] | set[str] | None

Directory basenames skipped during recursive walks.

None

root property

root: Path

The sole root, in single-root mode. Raises in multi-root mode.

iter_roots

iter_roots() -> list[tuple[str, Path]]

(name, absolute-path) for every root, in configured order.

resolve

resolve(rel: str) -> tuple[str, Path]

Resolve a virtual-tree path, rejecting traversal escapes.

rel must be a non-empty relative POSIX-style path. In single-root mode it is interpreted directly under the sole root. In multi-root mode the first segment names the root and the remainder is validated inside that root. Returns (root_name, absolute_path)root_name is "" in single-root mode.

The resolved path is rejected if it doesn't stay under its selected root: this catches both .. traversal and symlinks pointing outside the root (Path.resolve() follows symlinks before the check).

display

display(root_name: str, path: Path) -> str

Render an absolute path as the agent-facing virtual-tree string.

Root-relative in single-root mode; root-prefixed (name/rel) in multi-root mode so cross-repo references stay unambiguous.

is_ignored

is_ignored(
    path: Path,
    base: Path | None = None,
    *,
    include_hidden: bool = False,
) -> bool

True if path should be skipped during walks.

Skips any segment in :attr:ignored_dirs. When include_hidden is False (the default), also skips any segment starting with . except the ./.. placeholders. Segments are measured relative to base — the owning root a caller already knows; when omitted it is inferred (a path outside every root is treated as ignored).

is_text_extension

is_text_extension(path: Path) -> bool

True if path's suffix is in :attr:text_extensions.

tools

tools() -> list[RunnableToolAdapter]

Build the FS tools as RunnableTool wrappers.

Returns four tools by default (fs_read, fs_grep, fs_glob, fs_ls); fs_write is appended only when :attr:write_enabled is True.

ToolRegistrationError

Bases: ValueError

Raised when a tool can't be registered (missing spec, name collision).

ToolRegistry

In-memory map of tool name → ToolSpec, scoped to one Harness.

Construct empty, then register decorated functions or bare ToolSpecs. The Session asks for for_profile(profile) to get its tool surface for each call; nothing here mutates per-session state.

register

register(
    func_or_spec: Callable[..., Any]
    | ToolSpec
    | RunnableToolAdapter,
) -> ToolSpec

Register a decorated function, a bare ToolSpec, or a RunnableToolAdapter.

Returns the underlying spec.

Raises ToolRegistrationError on
  • a function without __reigner_spec__ (forgot @tool)
  • a name already present in the registry

get

get(name: str) -> ToolSpec

Look up a registered tool by name. KeyError if not present.

schemas

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

JSON Schema list for every registered tool, in insertion order.

Provider-agnostic. Model adapters translate to provider-specific tool-schema shapes.

for_profile

for_profile(profile: Profile) -> list[RunnableToolAdapter]

Return the tool subset visible under profile as adapters.

full — every registered tool. read_only — readonly tools plus pseudo-tools. eval — readonly tools plus pseudo-tools except escalate_to_oracle and request_clarification (both defeat determinism).

Adapters are constructed fresh on each call; with ~10 tools per harness the allocation cost is negligible. The loop wants .run(args), so this is the call site that hands out runnables.

tool

tool(
    *,
    readonly: bool = False,
    cache: bool = False,
    truncate_chars: int | None = None,
    description: str | None = None,
    pseudo: bool = False,
) -> Callable[[F], F]

Decorate an async function as a Reigner tool.

Validates MCP-clean constraints at decoration time, generates a JSON Schema for the arguments via Pydantic, and attaches a ToolSpec to the function as __reigner_spec__. The original callable is returned unchanged — direct calls work in tests without going through the registry.

Parameters:

Name Type Description Default
readonly bool

Eligible for G9 caching and G11 parallel execution.

False
cache bool

Opt-in result caching keyed on (name, args).

False
truncate_chars int | None

Per-tool override of the G2 truncation budget.

None
description str | None

Overrides the function's docstring on the tool schema.

None
pseudo bool

Intercepted locally by the loop; implies readonly=True.

False

Returns:

Type Description
Callable[[F], F]

A decorator that attaches a ToolSpec and returns the function as-is.

Sessions

reigner.sessions

Forkable, branchable, durable sessions.

ReplayError

Bases: ValueError

Raised when an event log can't be reconstructed.

The common case: a session recorded under SCHEMA_VERSION 1 (before UserQueryEvent) has no query rows, so there's nothing to seed a round from. Such sessions remain inspectable via Session.events() but cannot be reconstructed, forked, or replayed.

InvalidSessionId

Bases: ValueError

Raised when a session id contains characters that aren't filename-safe.

SessionMeta dataclass

Summary record for one session, persisted as {sid}.meta.json.

total_tokens and total_cost are deliberately absent — no event carries them yet. They'll be added as fields when later tasks emit usage events; the schema_version bumps in lockstep so old sessions still load via a migration path.

to_json

to_json() -> str

Serialize the metadata to indented JSON.

from_json classmethod

from_json(raw: str) -> SessionMeta

Parse :class:SessionMeta from its JSON form.

Parameters:

Name Type Description Default
raw str

JSON text previously produced by :meth:to_json.

required

Returns:

Type Description
SessionMeta

The decoded metadata.

Raises:

Type Description
ValueError

If the JSON root is not an object.

SchemaVersionMismatch

If the stored version is unsupported.

SessionNotFound

Bases: FileNotFoundError

Raised when a session id has no JSONL file under the store root.

SessionStore

Filesystem-backed store of session JSONL + meta sidecars.

One :class:SessionStore is owned by a :class:Harness; every :class:Session shares it. The store doesn't cache file handles — each :meth:append_event opens, writes, flushes, closes. Crash-safe per event at the cost of one open per event; that's the trade we want for an artifact you debug after the fact.

root property

root: Path

The store's root directory.

exists

exists(session_id: str) -> bool

Return whether a session JSONL file exists for session_id.

list

list() -> list[SessionMeta]

Return meta for every session under the root, sorted by id.

Malformed sessions (unreadable JSONL, version mismatch) are skipped so a single broken file doesn't poison the listing.

append_event

append_event(session_id: str, event: Event) -> None

Serialize event and append it as one line to {sid}.jsonl.

Flushes after each write so a crash mid-conversation loses at most the in-flight event. Meta is not touched here — call :meth:write_meta when summary fields need refreshing.

write_session_events

write_session_events(
    session_id: str, event_list: Iterable[Event]
) -> int

Write event_list as the complete JSONL for session_id (overwrites).

Used by :meth:Session.fork to snapshot a parent's event prefix into a fresh child file. The caller owns session_id consistency — events whose session_id field doesn't match are written as-is, so rewrite them (dataclasses.replace) before calling if a self-contained transcript is wanted. Returns the number of events written.

load_events

load_events(session_id: str) -> Iterator[Event]

Iterate the events of one session in write order.

Streaming on purpose: long sessions stay friendly to memory. Callers that want a list should wrap with list(...).

read_meta

read_meta(session_id: str) -> SessionMeta

Return meta for session_id, rebuilding from JSONL if missing.

If the sidecar exists it's the source of truth; if absent we scan the events to fill in what we can (event_count, created, last_updated). parent_id and title are unrecoverable from events alone and default to None after a rebuild.

write_meta

write_meta(session_id: str, meta: SessionMeta) -> None

Atomically write meta to the sidecar via tmp + os.replace.

set_title

set_title(
    session_id: str, title: str | None
) -> SessionMeta

Set (or clear) a session's title and return the updated metadata.

export

export(session_id: str, dest_path: str | Path) -> Path

Write the session's JSONL to dest_path plus a sidecar meta.

Takes a single .jsonl path and emits {dest}.meta.json next to it so title/created/parent_id survive the round trip.

Returns:

Type Description
Path

The destination JSONL path.

import_

import_(src_path: str | Path) -> str

Import a previously-exported session into this store.

Reuses the source session_id (from the sidecar meta if present, otherwise from the first event in the JSONL). On collision with an existing local session id, raises :class:FileExistsError so the caller can rename or delete the existing one. Returns the session id.

SessionNode dataclass

One session in a fork tree.

walk

walk() -> Iterator[SessionNode]

Yield this node and all descendants in display order.

reconstruct

reconstruct(
    events: Sequence[Event],
    *,
    settings: SettingsConfig,
    session_id: str,
    role: str = "",
) -> AgentState

Synchronous wrapper around :func:areconstruct.

Session.load / Session.fork are sync, but the loop is async. We run the coroutine to completion on a private event loop: directly via asyncio.run when no loop is running, otherwise on a worker thread so the call is safe from inside async code (e.g. async tests). The reconstruction is fully self-contained (stub adapter/tools, no shared I/O), so a separate loop is harmless.

round_boundaries

round_boundaries(events: Sequence[Event]) -> list[int]

Indices of every UserQueryEvent — one per conversational round.

at_turn=N (1-based) refers to round_boundaries(events)[N - 1].

build_forest

build_forest(store: SessionStore) -> list[SessionNode]

Build all session families from persisted parent_id relationships.

Sessions with a missing parent are roots. Corrupt cyclic lineage never creates a recursive graph: the edge that would close a cycle is skipped, promoting that node to a root instead.

Plugins

reigner.plugins

Hook-based extension points for the agent loop.

Subclass :class:Plugin, override the hooks you need, and list the dotted path in reigner.yaml under plugins:. :class:PluginHost is the dispatcher the loop drives; most users never touch it directly.

Two plugins ship in the box: :class:MetricsPlugin (OpenTelemetry spans, needs the otel extra) and :class:PiiRedactPlugin (regex redaction of tool results and the final answer). Importing them here is cheap — neither pulls a heavy dependency at import time; MetricsPlugin only touches OpenTelemetry when instantiated.

Plugin

Base for all plugins. Override the hooks you need; the rest no-op.

name is used in logs and error messages; it defaults to the class name when left blank.

before_tool_call async

before_tool_call(
    call: ToolCall, state: AgentState
) -> ToolCall

Inspect or rewrite a tool call before it runs. Return the call.

after_tool_call async

after_tool_call(
    call: ToolCall,
    result: ToolResultEvent,
    state: AgentState,
) -> ToolResultEvent

Inspect or rewrite a tool result after truncation. Return the result.

result is the bounded :class:ToolResultEvent (so result.truncated is meaningful); a returned copy with a rewritten result payload is what lands in both the model's context and the emitted event.

on_compaction async

on_compaction(state: AgentState, level: int) -> None

Observe a compaction tier firing (level is the tier index).

on_final_answer async

on_final_answer(
    answer: FinalAnswerEvent, state: AgentState
) -> FinalAnswerEvent

Inspect or rewrite the final answer before it is emitted.

on_error async

on_error(error: ErrorEvent, state: AgentState) -> None

Observe a terminal error before it is emitted.

on_oracle_escalation async

on_oracle_escalation(
    event: OracleEscalationEvent, state: AgentState
) -> None

Observe a single-turn oracle escalation being armed.

on_steering async

on_steering(
    event: SteeringAcceptedEvent, state: AgentState
) -> None

Observe a queued steering message being accepted into the loop.

PluginHost

Dispatches the seven hooks across an ordered list of plugins.

empty classmethod

empty() -> PluginHost

A host with no plugins — every hook is a pass-through no-op.

before_tool_call async

before_tool_call(
    call: ToolCall, state: AgentState
) -> ToolCall

Fold before_tool_call across plugins; raise on the first failure.

after_tool_call async

after_tool_call(
    call: ToolCall,
    result: ToolResultEvent,
    state: AgentState,
) -> ToolResultEvent

Fold after_tool_call across plugins; raise on the first failure.

on_final_answer async

on_final_answer(
    answer: FinalAnswerEvent, state: AgentState
) -> FinalAnswerEvent

Fold on_final_answer across plugins; raise on the first failure.

on_compaction async

on_compaction(state: AgentState, level: int) -> None

Notify every plugin of a compaction pass; failures are isolated.

on_error async

on_error(error: ErrorEvent, state: AgentState) -> None

Notify every plugin of an error event; failures are isolated.

on_oracle_escalation async

on_oracle_escalation(
    event: OracleEscalationEvent, state: AgentState
) -> None

Notify every plugin of an oracle escalation; failures are isolated.

on_steering async

on_steering(
    event: SteeringAcceptedEvent, state: AgentState
) -> None

Notify every plugin of a steering message; failures are isolated.

MetricsPlugin

Bases: Plugin

Trace tool calls and loop events as OpenTelemetry spans.

before_tool_call async

before_tool_call(
    call: ToolCall, state: AgentState
) -> ToolCall

Open a span for the tool call, closed by :meth:after_tool_call.

after_tool_call async

after_tool_call(
    call: ToolCall,
    result: ToolResultEvent,
    state: AgentState,
) -> ToolResultEvent

Close the tool-call span, annotating it with truncation/cache flags.

on_compaction async

on_compaction(state: AgentState, level: int) -> None

Emit a span for a compaction pass.

on_error async

on_error(error: ErrorEvent, state: AgentState) -> None

Emit a span for an error event.

on_oracle_escalation async

on_oracle_escalation(
    event: OracleEscalationEvent, state: AgentState
) -> None

Emit a span for an oracle escalation.

on_steering async

on_steering(
    event: SteeringAcceptedEvent, state: AgentState
) -> None

Emit a span for a steering message.

PiiRedactPlugin

Bases: Plugin

Replace regex matches with a token in tool results and the final answer.

__init__

__init__(
    patterns: list[str | Pattern[str]],
    *,
    token: str = DEFAULT_TOKEN,
) -> None

Compile patterns up front so a bad pattern fails loudly at wiring time.

Each entry is a regex string or an already-compiled pattern. token is the replacement substituted for every match.

after_tool_call async

after_tool_call(
    call: ToolCall,
    result: ToolResultEvent,
    state: AgentState,
) -> ToolResultEvent

Scrub the result payload before it lands in the model's context.

on_final_answer async

on_final_answer(
    answer: FinalAnswerEvent, state: AgentState
) -> FinalAnswerEvent

Scrub the answer text before it is emitted to the user.

load_plugins

load_plugins(paths: list[DottedPath]) -> list[Plugin]

Resolve dotted paths to live :class:Plugin instances, in order.

Artifacts

reigner.artifacts

Artifact system — schema, writer, manifest, and conventions.

This package owns the write side and the schema contract; the read-side ArtifactStore lives in reigner.tools.artifacts.

ExtractionMeta dataclass

Per-entity manifest written alongside an entity's artifacts.

Attributes:

Name Type Description
schema_version str

Version of the artifact schema used for extraction.

identifiers dict[str, str]

Identity fields locating the entity (e.g. entity_id).

files list[str]

Artifact file paths written for this entity, relative to root.

extractor dict[str, Any] | None

Optional extractor-owned payload (hashes, model, cost).

written_at str

ISO-8601 UTC timestamp of when the manifest was written.

to_json

to_json() -> str

Serialize the manifest to indented, key-sorted JSON.

from_json classmethod

from_json(s: str) -> ExtractionMeta

Reconstruct an :class:ExtractionMeta from its JSON form.

Parameters:

Name Type Description Default
s str

JSON text previously produced by :meth:to_json.

required

Returns:

Type Description
ExtractionMeta

The decoded manifest, defaulting written_at if absent.

ArtifactSchema dataclass

Declarative shape of compiled artifacts on disk.

Used by the writer to lay out files and validate extraction outputs and by the read-side ArtifactStore to know what paths and field names are valid.

entity_path_keys

entity_path_keys() -> tuple[str, ...]

Return the placeholder names in entity_path, in order.

required_sections

required_sections() -> list[SectionSpec]

Return the non-glob sections that must be present.

section

section(name: str) -> SectionSpec | None

Look up a section by exact name, falling back to glob prefixes.

Parameters:

Name Type Description Default
name str

Section name to resolve.

required

Returns:

Type Description
SectionSpec | None

The matching :class:SectionSpec, or None if nothing matches.

json_artifact

json_artifact(name: str) -> JsonArtifactSpec | None

Look up a JSON artifact spec by name.

Parameters:

Name Type Description Default
name str

Artifact filename to resolve.

required

Returns:

Type Description
JsonArtifactSpec | None

The matching :class:JsonArtifactSpec, or None if absent.

document_qa_default classmethod

document_qa_default() -> ArtifactSchema

Default schema for the document_qa recipe.

generic_default classmethod

generic_default() -> ArtifactSchema

Default schema for non-uniform / mixed corpora.

Where :meth:document_qa_default assumes every document shares a shape, this offers corpus-agnostic sections any document can fill: one required summary slot, the rest optional. Use it when a single corpus mixes document kinds (a textbook, a law, a manual) that no fixed schema fits.

Section targeting is weaker as a result — a document may leave most slots empty — so retrieval recovers the long tail via bm25 over the raw text rather than relying on well-populated sections.

from_yaml classmethod

from_yaml(path: str | Path) -> ArtifactSchema

Load and validate a schema from a YAML file.

Parameters:

Name Type Description Default
path str | Path

Path to the schema YAML file.

required

Returns:

Type Description
ArtifactSchema

The parsed :class:ArtifactSchema.

Raises:

Type Description
ValueError

If the YAML root is not a mapping.

to_json_schema

to_json_schema() -> dict[str, Any]

Return a JSON Schema describing the expected extraction output.

The schema describes an object with two top-level keys: sections (mapping section name → str) and json_artifacts (mapping filename → object). The LLMExtractor consumes this for its prompt template; the design here is intentionally minimal.

Returns:

Type Description
dict[str, Any]

A JSON-Schema mapping for the extractor's structured output.

JsonArtifactSpec dataclass

A JSON artifact under the entity directory.

fields declares expected top-level keys and their Python types. By default every declared field is required; pass required_fields to relax this. An empty fields dict means "no field-level validation" and the artifact is treated as optional.

required_field_names property

required_field_names: set[str]

Field names that must be present (all declared fields by default).

SectionSpec dataclass

A prose-section artifact under the entity directory.

Section names containing * are globs: they declare an allowed prefix (e.g. sections/*) but cannot be required, since there is no fixed name to enforce.

is_glob property

is_glob: bool

Whether the section name is a glob prefix rather than a fixed name.

ArtifactWriteError

Bases: RuntimeError

Raised on filesystem-level write failures.

ArtifactWriter

Schema-aware, atomic writer for an entity's compiled artifacts.

Bound to an artifact root and a schema; the only sanctioned way to write under library/artifacts/. Write-side only — never exposed as a tool.

write_entity

write_entity(
    *,
    sections: dict[str, str] | None = None,
    json_artifacts: dict[str, dict[str, Any]] | None = None,
    meta: dict[str, Any] | None = None,
    **identifiers: str,
) -> Path

Atomically write all artifacts for one entity.

Parameters:

Name Type Description Default
sections dict[str, str] | None

section name -> prose content. Section names match SectionSpec.name from the schema; nested names like sections/business write to nested files.

None
json_artifacts dict[str, dict[str, Any]] | None

JSON artifact filename -> payload dict.

None
meta dict[str, Any] | None

extractor-owned manifest fields (source_hash, prompt_hash, model, …). Merged into extraction_meta.json under the extractor key.

None
**identifiers str

the entity_path placeholders (e.g. ticker=..., fiscal_year=...). Must exactly match the schema's entity_path keys.

{}

Returns:

Type Description
Path

The absolute entity directory.

entity_dir

entity_dir(**identifiers: str) -> Path

Resolve the absolute entity directory under the writer's root.

SchemaValidationError

Bases: ValueError

Raised when sections or JSON artifacts violate the schema.