jeevesagent¶
JeevesAgent — model-agnostic, MCP-native agent harness.
Submodules¶
- jeevesagent.agent
- jeevesagent.architecture
- jeevesagent.core
- jeevesagent.data
- jeevesagent.governance
- jeevesagent.graph
- jeevesagent.jeeves
- jeevesagent.loader
- jeevesagent.mcp
- jeevesagent.memory
- jeevesagent.model
- jeevesagent.observability
- jeevesagent.runtime
- jeevesagent.security
- jeevesagent.skills
- jeevesagent.team
- jeevesagent.tools
- jeevesagent.vectorstore
Exceptions¶
Invalid, missing, or revoked API credentials. |
|
A run was halted because a budget limit was hit. |
|
A user-driven interruption (signal, timeout) ended the run. |
|
Invalid or unresolvable configuration passed to |
|
The provider's safety system blocked the request or response. |
|
A certified value failed its freshness policy. |
|
The request was malformed or violated the provider's API |
|
Emitted when a memory query is likely to silently miss data |
|
Base class for all harness errors. |
|
A certified value failed its lineage policy. |
|
An MCP transport, handshake, or protocol error. |
|
The memory backend failed an operation. |
|
A call to the underlying model adapter failed. |
|
The model's final answer did not validate against the supplied |
|
Raised when a tool argument resolves outside its workdir. |
|
A model call failed in a way that retrying will not fix. |
|
A tool call was denied by the permission layer or a user hook. |
|
The provider returned a 429 / quota-exhausted response. |
|
The durable runtime journal is unreadable or inconsistent. |
|
The sandbox refused or failed to execute a tool. |
|
Raised on invalid skill construction or frontmatter. |
|
A tool invocation failed at the tool's own boundary. |
|
A model call failed in a way that may succeed on retry. |
Classes¶
Actor + adversarial critic with optional different models. |
|
A fully-async, MCP-native, model-agnostic agent harness. |
|
Renderable graph of an agent's structure. |
|
Mutable per-run state shared between |
|
Trivial permission policy: every call is allowed. |
|
Talks to Claude via |
|
Strategy interface for driving the agent loop. |
|
An immutable, signed entry in the audit log. |
|
The append-only signed log surface. |
|
Public + per-agent private state for the architecture. |
|
Coordinator + agents + decider, mediated by a shared |
|
One contribution on the blackboard. |
|
Resource governance — tokens, calls, cost, wall clock. |
|
Result of a budget check before each step. |
|
A value carrying provenance metadata for freshness/lineage checks. |
|
Bi-temporal fact store backed by a Chroma collection. |
|
Memory backed by |
|
Vector store backed by |
|
Embeddings via Cohere's |
|
Periodic consolidator for any |
|
Bundled protocol implementations passed to every architecture. |
|
Echo-style model for tests and demos. |
|
Text-to-vector embedding model used by the memory subsystem. |
|
A single (input, decisions, tool calls, output) tuple from history. |
|
A single observable record from a running session. |
|
Enum where members are also (and must be) strings |
|
Vector store backed by |
|
A semantic claim extracted from one or more episodes. |
|
Storage surface for bi-temporal facts. |
|
JSONL append-only audit log with HMAC signatures. |
|
Restrict a tool host's path-typed arguments to declared roots. |
|
Maximum age for certified values from each source. |
|
Per-peer handoff configuration. |
|
Deterministic SHA256-seeded unit vectors. |
|
Aggregator over user-registered lifecycle callbacks. |
|
Implements |
|
List-backed signed audit log. |
|
Dict-backed bi-temporal fact store. |
|
Dict-backed journal. Process-local; lost on exit. |
|
Dict-backed implementation of |
|
In-process vector store backed by a Python list. |
|
No durability. Each step runs immediately. |
|
A dict-backed |
|
Connection details for the Jeeves Gateway. |
|
ToolHost-shaped wrapper around the Jeeves Gateway. |
|
Storage surface for the durable runtime. |
|
Runtime that journals every step's result for replay. |
|
Allow-list of source prefixes for the entire lineage chain. |
|
Talks to any LiteLLM-supported provider. |
|
One client per MCP server. Holds the live |
|
Aggregates many |
|
How to find and talk to a single MCP server. |
|
Tiered memory: working blocks, episodic store, semantic graph. |
|
An in-context memory block, pinned to every prompt. |
|
A single chat message in the model's conversation. |
|
Enum where members are also (and must be) strings |
|
LLM provider interface. One adapter per lab (Anthropic, OpenAI, ...). |
|
A single chunk from a streaming model call. |
|
N debaters + optional judge orchestration. |
|
Pass-through wrapper around a |
|
No-op telemetry. Very cheap; safe to call on every loop step. |
|
OpenTelemetry-backed |
|
Embeddings via OpenAI's |
|
Talks to OpenAI via |
|
Outcome of a permission check or pre-tool hook. |
|
Decides whether a tool call is allowed. |
|
A list of plan steps in execution order. |
|
Planner → step executor → synthesizer. |
|
One step of a plan. |
|
Postgres-backed bi-temporal fact store. |
|
Postgres-backed journal. Production-grade durable replay. |
|
Postgres-backed |
|
|
|
Vector store backed by Postgres + |
|
Observe-think-act in a tight loop. |
|
Plan-then-tool-execute with placeholder substitution. |
|
A list of ReWOO steps (no required ordering — dependencies |
|
One step of a ReWOO plan: id + tool + args. |
|
!!! abstract "Usage Documentation" |
|
Bi-temporal fact store over plain Redis hashes. |
|
Redis-backed |
|
Wrap a base architecture with evaluator + reflector + lesson |
|
Exponential-backoff-with-jitter retry schedule. |
|
Enum where members are also (and must be) strings |
|
Classify input → dispatch to ONE specialist |
|
One specialist + classification metadata. |
|
Typed, immutable context for one agent run. |
|
Final outcome of an |
|
Durable execution. Wraps every side effect in a journal entry. |
|
Handle to an open durable session held by a |
|
Isolation layer for tool execution. |
|
Model that emits canned responses, one per call to |
|
One hit from |
|
Resolution and redaction of named secrets. |
|
Wrap a base architecture with iterative critique / refine. |
|
A loadable agent skill. |
|
Lightweight skill descriptor — what loads at startup. |
|
A keyed collection of |
|
A folder of skills + an optional label. |
|
A trace span handle. Concrete telemetry adapters return their own |
|
Durable bi-temporal fact store rooted at a sqlite file. |
|
SQLite-backed journal. Durable across process restarts. |
|
|
|
Mode + allow/deny-list permission policy. |
|
The output of executing one step. |
|
Run each tool call in a fresh child Python process. |
|
Coordinator + workers, glued by a |
|
Peer agents passing control through handoff tools. |
|
Namespace for multi-agent team builders. |
|
OpenTelemetry-compatible tracing/metrics surface. |
|
One node in the Tree-of-Thoughts search tree. |
|
A registered tool: definition plus the callable that executes it. |
|
A model-emitted request to invoke a tool. |
|
Schema description of a tool the model can call. |
|
Tool registry change notification (MCP listChanged etc.). |
|
MCP-aware tool registry. Lazy-loads schemas on demand. |
|
Outcome of a tool invocation. |
|
Branch + evaluate + prune. BFS beam search over thoughts. |
|
Token and cost accounting for a model call. |
|
Pure-Python embedding-backed |
|
Async protocol for vector stores. |
|
Embeddings via Voyage AI's |
|
Context manager that installs a |
Functions¶
|
Build a |
|
Walk an |
|
Map an exception from any model SDK to the framework's taxonomy. |
|
Return the framework's default workdir for built-in tools, |
|
Stable hash of arbitrary JSON-serializable parts. |
|
Build a |
|
Return all three filesystem tools (read + write + edit) |
|
Return the |
|
Return a fresh ULID, optionally prefixed for readability. |
|
Build a |
|
Coerce |
|
Run an |
|
Promote a callable to a |
|
Walk the agent, render to Mermaid, write to |
|
Build a |
Package Contents¶
- exception jeevesagent.AuthenticationError(message: str, *, cause: BaseException | None = None)[source]¶
Bases:
PermanentModelErrorInvalid, missing, or revoked API credentials.
Initialize self. See help(type(self)) for accurate signature.
- exception jeevesagent.BudgetExceeded(reason: str)[source]¶
Bases:
JeevesAgentErrorA run was halted because a budget limit was hit.
Initialize self. See help(type(self)) for accurate signature.
- reason¶
- exception jeevesagent.CancelledByUser[source]¶
Bases:
JeevesAgentErrorA user-driven interruption (signal, timeout) ended the run.
Initialize self. See help(type(self)) for accurate signature.
- exception jeevesagent.ConfigError[source]¶
Bases:
JeevesAgentErrorInvalid or unresolvable configuration passed to
Agent.Initialize self. See help(type(self)) for accurate signature.
- exception jeevesagent.ContentFilterError(message: str, *, cause: BaseException | None = None)[source]¶
Bases:
PermanentModelErrorThe provider’s safety system blocked the request or response.
Typically a permanent failure for the same prompt; users may rephrase but the framework should not silently retry.
Initialize self. See help(type(self)) for accurate signature.
- exception jeevesagent.FreshnessError[source]¶
Bases:
JeevesAgentErrorA certified value failed its freshness policy.
Initialize self. See help(type(self)) for accurate signature.
- exception jeevesagent.InvalidRequestError(message: str, *, cause: BaseException | None = None)[source]¶
Bases:
PermanentModelErrorThe request was malformed or violated the provider’s API contract — bad parameters, oversized prompt, unknown model name, etc. Fix the request, don’t retry.
Initialize self. See help(type(self)) for accurate signature.
- exception jeevesagent.IsolationWarning[source]¶
Bases:
UserWarningEmitted when a memory query is likely to silently miss data because the caller forgot to pass
user_id.Concrete trigger: a backend’s
recall/recall_factsruns withuser_id=Noneagainst a store whose persisted records include at least one non-Noneuser_id— the partition is safe (the anonymous bucket and named-user buckets are isolated), but the developer probably wired up multi-tenancy somewhere and forgot to passuser_idhere, so they will see suspiciously empty recall results.Subclass of
UserWarningso it goes through Python’s standardwarningsfilter machinery — apps can silence, promote-to-error, or log it however they want, e.g.:import warnings from jeevesagent import IsolationWarning warnings.simplefilter("error", IsolationWarning) # raise on hit
Initialize self. See help(type(self)) for accurate signature.
- exception jeevesagent.JeevesAgentError[source]¶
Bases:
ExceptionBase class for all harness errors.
Initialize self. See help(type(self)) for accurate signature.
- exception jeevesagent.LineageError[source]¶
Bases:
JeevesAgentErrorA certified value failed its lineage policy.
Initialize self. See help(type(self)) for accurate signature.
- exception jeevesagent.MCPError[source]¶
Bases:
JeevesAgentErrorAn MCP transport, handshake, or protocol error.
Initialize self. See help(type(self)) for accurate signature.
- exception jeevesagent.MemoryStoreError[source]¶
Bases:
JeevesAgentErrorThe memory backend failed an operation.
Initialize self. See help(type(self)) for accurate signature.
- exception jeevesagent.ModelError(message: str, *, cause: BaseException | None = None)[source]¶
Bases:
JeevesAgentErrorA call to the underlying model adapter failed.
Base of the model-error taxonomy: catch this to handle every model failure regardless of whether it is transient or permanent. The SDK exception that triggered the classification is attached via
__cause__(andcause) so debug code can still inspect the raw error.Initialize self. See help(type(self)) for accurate signature.
- cause = None¶
- exception jeevesagent.OutputValidationError(message: str, *, raw: str, schema: type, cause: BaseException | None = None)[source]¶
Bases:
JeevesAgentErrorThe model’s final answer did not validate against the supplied
output_schema.Raised by
Agent.run()when the caller passedoutput_schema=and the model’s final assistant text could not be parsed/validated as the requested Pydantic model — even after the optional one-shot “retry with the validation error” turn.Carries the raw model output (
raw), the underlying Pydanticpydantic.ValidationError(cause, also exposed via__cause__), and the schema that was being targeted (schema) so callers can build whatever recovery strategy they need (re-prompt with extra examples, fall back to free-text, etc.).Initialize self. See help(type(self)) for accurate signature.
- cause = None¶
- raw¶
- schema¶
- exception jeevesagent.PathEscapeError[source]¶
Bases:
ValueErrorRaised when a tool argument resolves outside its workdir.
Initialize self. See help(type(self)) for accurate signature.
- exception jeevesagent.PermanentModelError(message: str, *, cause: BaseException | None = None)[source]¶
Bases:
ModelErrorA model call failed in a way that retrying will not fix.
Wrong API key, malformed request, content-filter rejection, deprecated model name, etc. The retry layer raises these immediately without backoff so callers can fail fast and surface the real problem.
Initialize self. See help(type(self)) for accurate signature.
- exception jeevesagent.PermissionDenied(tool: str, reason: str)[source]¶
Bases:
JeevesAgentErrorA tool call was denied by the permission layer or a user hook.
Initialize self. See help(type(self)) for accurate signature.
- reason¶
- tool¶
- exception jeevesagent.RateLimitError(message: str, *, retry_after: float | None = None, cause: BaseException | None = None)[source]¶
Bases:
TransientModelErrorThe provider returned a 429 / quota-exhausted response.
Carries
retry_afterwhen the provider supplied one. Subclass ofTransientModelErrorso generic transient handlers cover it; catchRateLimitErrorspecifically when you need to surface “slow down” to the caller (e.g. propagate a 429 to your own clients).Initialize self. See help(type(self)) for accurate signature.
- exception jeevesagent.RuntimeJournalError[source]¶
Bases:
JeevesAgentErrorThe durable runtime journal is unreadable or inconsistent.
Initialize self. See help(type(self)) for accurate signature.
- exception jeevesagent.SandboxError[source]¶
Bases:
JeevesAgentErrorThe sandbox refused or failed to execute a tool.
Initialize self. See help(type(self)) for accurate signature.
- exception jeevesagent.SkillError[source]¶
Bases:
ValueErrorRaised on invalid skill construction or frontmatter.
Initialize self. See help(type(self)) for accurate signature.
- exception jeevesagent.ToolError[source]¶
Bases:
JeevesAgentErrorA tool invocation failed at the tool’s own boundary.
Initialize self. See help(type(self)) for accurate signature.
- exception jeevesagent.TransientModelError(message: str, *, retry_after: float | None = None, cause: BaseException | None = None)[source]¶
Bases:
ModelErrorA model call failed in a way that may succeed on retry.
Covers HTTP 5xx responses, network errors, timeouts, and provider-side rate limits. The retry layer treats this family as retryable and applies backoff.
retry_after(in seconds) carries a provider-supplied hint when one is available — e.g. anRetry-AfterHTTP header on a 429 response. The retry layer respects the larger of the policy’s computed backoff andretry_afterso we never wait less than the provider asked for.Initialize self. See help(type(self)) for accurate signature.
- retry_after = None¶
- class jeevesagent.ActorCritic(*, actor: jeevesagent.agent.api.Agent, critic: jeevesagent.agent.api.Agent, max_rounds: int = 3, approval_threshold: float = 0.9, critique_template: str | None = None, refine_template: str | None = None)[source]¶
Actor + adversarial critic with optional different models.
Constructor parameters:
actor(required): the generatingAgent. Sees the original prompt on round 0 and a refine prompt on subsequent rounds.critic(required): the reviewingAgent. Sees the original prompt + the actor’s current output and produces structured JSON critique.max_rounds: cap on critique-refine cycles after the initial generation. Default 3.approval_threshold: terminate whencritique.scoreis at or above this value. Default 0.9.critique_template/refine_template: override the default prompts. Templates use{prompt},{output},{critique},{issues_bulleted}.
- declared_workers() dict[str, jeevesagent.agent.api.Agent][source]¶
- async run(session: jeevesagent.architecture.base.AgentSession, deps: jeevesagent.architecture.base.Dependencies, prompt: str) collections.abc.AsyncIterator[jeevesagent.core.types.Event][source]¶
- name = 'actor-critic'¶
- class jeevesagent.Agent(instructions: str, *, model: jeevesagent.core.protocols.Model | str | None = None, memory: jeevesagent.core.protocols.Memory | None = None, runtime: jeevesagent.core.protocols.Runtime | None = None, budget: jeevesagent.core.protocols.Budget | None = None, permissions: jeevesagent.core.protocols.Permissions | None = None, hooks: jeevesagent.security.hooks.HookRegistry | None = None, tools: list[jeevesagent.tools.registry.Tool | collections.abc.Callable[Ellipsis, object]] | jeevesagent.core.protocols.ToolHost | jeevesagent.tools.registry.Tool | collections.abc.Callable[Ellipsis, object] | None = None, telemetry: jeevesagent.core.protocols.Telemetry | None = None, audit_log: jeevesagent.security.audit.AuditLog | None = None, max_turns: int = DEFAULT_MAX_TURNS, auto_consolidate: bool = False, architecture: jeevesagent.architecture.Architecture | str | None = None, skills: list[Any] | None = None, retry_policy: jeevesagent.governance.retry.RetryPolicy | None = None)[source]¶
A fully-async, MCP-native, model-agnostic agent harness.
- add_tool(item: jeevesagent.tools.registry.Tool | collections.abc.Callable[Ellipsis, object]) jeevesagent.tools.registry.Tool[source]¶
Register a tool after construction.
Convenience for plugin-style code that adds tools after the
Agentexists. Only works when the underlying tool host is anInProcessToolHost(the default — and the only host that has a writable registry today).Returns the constructed
Toolso callers can introspect the auto-derived schema.
- after_tool(fn: jeevesagent.security.hooks.PostToolHook) jeevesagent.security.hooks.PostToolHook[source]¶
Register a best-effort post-tool callback.
- before_tool(fn: jeevesagent.security.hooks.PreToolHook) jeevesagent.security.hooks.PreToolHook[source]¶
Register a pre-tool hook. First denial wins; allow otherwise.
- async consolidate() int[source]¶
Manually trigger memory consolidation.
Returns the number of new facts the consolidator extracted, or
0when the memory backend doesn’t expose a fact store.Useful when
auto_consolidate=False(the default) and you want to batch consolidation at a controlled cadence — e.g. once a day, or before shutdown.
- classmethod from_config(path: str | pathlib.Path, *, model: jeevesagent.core.protocols.Model | None = None, memory: jeevesagent.core.protocols.Memory | None = None, runtime: jeevesagent.core.protocols.Runtime | None = None, tools: list[jeevesagent.tools.registry.Tool | collections.abc.Callable[Ellipsis, object]] | jeevesagent.core.protocols.ToolHost | None = None) Agent[source]¶
Construct an
Agentfrom a TOML config file.Designed for ops/devops users who want declarative agent config separate from code. Supports the textual / numeric bits — instructions, model spec (string), max_turns, auto_consolidate, budget — and lets callers pass concrete instances for the things TOML can’t reasonably express (real
Memory,Runtime, customModel, tools).Example
agent.toml:instructions = "You are a research assistant." model = "claude-opus-4-7" max_turns = 100 auto_consolidate = true [budget] max_tokens = 200_000 max_cost_usd = 5.0 max_wall_clock_minutes = 10 soft_warning_at = 0.8
Then:
agent = Agent.from_config("agent.toml")
- classmethod from_dict(cfg: dict[str, Any], *, model: jeevesagent.core.protocols.Model | None = None, memory: jeevesagent.core.protocols.Memory | None = None, runtime: jeevesagent.core.protocols.Runtime | None = None, tools: list[jeevesagent.tools.registry.Tool | collections.abc.Callable[Ellipsis, object]] | jeevesagent.core.protocols.ToolHost | None = None) Agent[source]¶
Construct an
Agentfrom a parsed config dict.Same shape as
from_config()but skips the file read. Useful when the config comes from somewhere other than a TOML file — environment variables, a Pydantic settings model, ayaml.safe_loadresult, an HTTP API, etc.Recognised keys (all optional except
instructionsandmodel):instructions: str— requiredmodel: str— required (or passmodel=kwarg)max_turns: intauto_consolidate: boolbudget: dictwith any ofmax_tokens,max_input_tokens,max_output_tokens,max_cost_usd,max_wall_clock_minutes,soft_warning_at
- async generate_graph(path: str | pathlib.Path | None = None, *, title: str | None = None) str[source]¶
Render this agent’s structure as a Mermaid graph.
Walks the agent + its architecture + all sub-agents + every agent’s tools, producing a graph that captures the full team, tool attachments, and architecture-specific relationships (delegate / handoff / classify / etc.).
Returns the Mermaid text. If
pathis provided, also writes to disk — extension determines the format:.mmd— raw Mermaid source.md— Markdown with the diagram in amermaidfence (renders on GitHub, IDE markdown previews, Jupyter).png/.svg— rendered viamermaid.ink; falls back to.mmdnext to the path on network failure
Example:
mermaid_text = await agent.generate_graph("graph.md") print(mermaid_text)
Pass
title=to override the diagram title (defaults to the file’s stem, or"Agent"if no path is given).
- async recall(query: str, *, kind: str = 'episodic', limit: int = 5) list[Any][source]¶
Convenience wrapper around
self.memory.recall(query, ...).Returns episodes matching
query. For semantic / fact-store recall, useself.memory.facts.recall_text(...)directly.
- remove_tool(name: str) bool[source]¶
Unregister a tool by name. Returns
Trueif a tool was removed,Falseif no tool with that name was registered.Same constraint as
add_tool(): only works withInProcessToolHost.
- async resume(session_id: str, prompt: str, *, user_id: str | None = None, metadata: collections.abc.Mapping[str, Any] | None = None, context: jeevesagent.core.context.RunContext | None = None, extra_tools: list[jeevesagent.tools.registry.Tool] | None = None, emit: Emit | None = None, output_schema: type[pydantic.BaseModel] | None = None, output_validation_retries: int = 1) jeevesagent.core.types.RunResult[source]¶
Resume a previously-interrupted run from its journal.
Equivalent to
agent.run(prompt, session_id=session_id, ...)with the same kwarg surface asrun(). Exists as a separate method so the intent is explicit at the call site — when a durableRuntime(e.g.SqliteRuntime) is configured, completed steps replay from the journal instead of re-executing.
- async run(prompt: str, *, user_id: str | None = None, session_id: str | None = None, metadata: collections.abc.Mapping[str, Any] | None = None, context: jeevesagent.core.context.RunContext | None = None, extra_tools: list[jeevesagent.tools.registry.Tool] | None = None, emit: Emit | None = None, output_schema: type[pydantic.BaseModel] | None = None, output_validation_retries: int = 1) jeevesagent.core.types.RunResult[source]¶
Run the agent to completion and return its
RunResult.user_idis the namespace partition for memory recall and persistence — episodes and facts stored with oneuser_idare never visible to a query scoped to a differentuser_id.Noneis the “anonymous / single-tenant” bucket. SeeRunContextfor the partitioning contract.Pass
session_idto resume a journaled run — when paired with a durable runtime (e.g.SqliteRuntime), already-completed steps replay from the journal instead of re-executing. Without a durable runtime,session_idjust labels the run.metadatais a free-form bag for application context the framework does not interpret (locale, request id, feature flags). Tools and hooks read it viaget_run_context().metadata.contextaccepts a fully-formedRunContextinstead of the individual kwargs — useful when passing context through multi-agent boundaries that received their parent’s context as a single object. When bothcontextand the individual kwargs are provided, the kwargs override the corresponding fields oncontext.extra_toolsinjects additionalTools for this run only — the agent’s configuredToolHostis wrapped so the model sees the extras alongside whatever tools were registered at construction. Used by multi-agent architectures that need to inject coordination tools (e.g. Swarm’shandoff(target, message)) into a peer agent’s loop without permanently mutating that agent’s static configuration.emitis an awaitable callback invoked once perEventproduced during the run (model chunks, tool calls, tool results, architecture progress, errors, …). DefaultNonedrops events on the floor (regularrunsemantics — return only the finalRunResult). Multi-agent architectures pass an emit that forwards a sub-Agent’s events into the parent’s stream, so calls likeawait worker.run(prompt, emit=parent_send)surface the worker’s token-by-token streaming to the outermostagent.stream(...)consumer.output_schemarequests a structured, validated final answer. Pass any PydanticBaseModelsubclass and the framework will (1) append a JSON-schema directive to the system prompt instructing the model to emit a final answer that matches, (2) parse the final assistant text against the schema, and (3) populateRunResult.parsedwith the validated instance.RunResult.outputkeeps the raw text so you can log or display it. Up tooutput_validation_retriesextra turns are spent recovering from a parse failure (the model is given the validation error as feedback and asked to try again); if it still fails after the retry budget, the run raisesOutputValidationError. Set retries to 0 to fail fast.
- async stream(prompt: str, *, user_id: str | None = None, session_id: str | None = None, metadata: collections.abc.Mapping[str, Any] | None = None, context: jeevesagent.core.context.RunContext | None = None, extra_tools: list[jeevesagent.tools.registry.Tool] | None = None, output_schema: type[pydantic.BaseModel] | None = None, output_validation_retries: int = 1) collections.abc.AsyncIterator[jeevesagent.core.types.Event][source]¶
Stream
Events as the loop produces them.The loop runs as a background task; events are pushed through a bounded memory stream so a slow consumer applies backpressure. Breaking out of the iteration cancels the producer cleanly.
session_idworks the same asrun()’s — pass an existing one to resume against a durable runtime’s journal.extra_toolsworks the same asrun()’s.
- async tools_list() list[str][source]¶
Return the names of all currently-registered tools.
Convenience that works for any
ToolHost. Callstool_host.list_tools()under the hood and returns just the names; useself.tool_host.list_tools()directly for the fullToolDefrecords.
- with_tool(fn: collections.abc.Callable[Ellipsis, object]) collections.abc.Callable[Ellipsis, object][source]¶
Decorator-style equivalent of
add_tool().Usage:
@agent.with_tool async def search(query: str) -> str: '''Search a knowledge base.''' return f"results for {query}"
Returns the original function unchanged (so it can still be called normally), and registers it as a tool on the agent’s underlying
InProcessToolHost. Same constraint asadd_tool(): the host must be writable.
- property architecture: jeevesagent.architecture.Architecture¶
The configured
Architecturestrategy.Default is
ReAct. Passarchitecture=toAgent(...)to override.
- property budget: jeevesagent.core.protocols.Budget¶
The configured
Budget.
- property hooks: jeevesagent.core.protocols.HookHost¶
- property instructions: str¶
The system prompt the agent runs with.
Surfaced as a public property so multi-agent architectures (e.g.
Supervisor) can read each worker’s intended role when composing instructions for the supervising model.
- property memory: jeevesagent.core.protocols.Memory¶
The configured
Memorybackend.
- property model: jeevesagent.core.protocols.Model¶
The configured
Modeladapter.
- property permissions: jeevesagent.core.protocols.Permissions¶
The configured
Permissionspolicy.
- property runtime: jeevesagent.core.protocols.Runtime¶
The configured
Runtime.
- property skills: Any | None¶
The
SkillRegistryof skills registered on this agent (orNoneif no skills were configured). Useful for inspecting / mutating the skill set after construction.
- property tool_host: jeevesagent.core.protocols.ToolHost¶
The configured
ToolHost.
- class jeevesagent.AgentGraph[source]¶
Renderable graph of an agent’s structure.
- class jeevesagent.AgentSession[source]¶
Mutable per-run state shared between
Agentand anArchitecture.The
Agentconstructs this once per run, the architecture mutates it as iteration progresses, and theAgentreads the final state to build aRunResult.metadatais a free-form dict architectures use for things that don’t deserve their own field — multi-agent architectures stash worker handoff state, planners stash plans, etc.- cumulative_usage: jeevesagent.core.types.Usage¶
- messages: list[jeevesagent.core.types.Message] = []¶
- class jeevesagent.AllowAll[source]¶
Trivial permission policy: every call is allowed.
The default for
Agentwhen no permissions are configured.- async check(call: jeevesagent.core.types.ToolCall, *, context: collections.abc.Mapping[str, Any]) jeevesagent.core.types.PermissionDecision[source]¶
- class jeevesagent.AnthropicModel(model: str = 'claude-opus-4-7', *, client: Any = None, api_key: str | None = None, max_tokens: int = DEFAULT_MAX_TOKENS)[source]¶
Talks to Claude via
anthropic.AsyncAnthropic.- async complete(messages: list[jeevesagent.core.types.Message], *, tools: list[jeevesagent.core.types.ToolDef] | None = None, temperature: float = 1.0, max_tokens: int | None = None) tuple[str, list[jeevesagent.core.types.ToolCall], jeevesagent.core.types.Usage, str][source]¶
Single-shot non-streaming completion.
Calls
client.messages.create(...)(nostream=True, nostreamcontext manager) — Anthropic returns the fullMessagein one HTTP response. We walk itscontentblocks once to assemble(text, tool_calls, usage, stop_reason). Used by the non-streaming hot path (agent.run());agent.stream()keeps usingstream().Falls back to consuming
stream()if the underlying client raises (test fakes that only support streaming, or transports that don’t honour single-shot creation).
- async stream(messages: list[jeevesagent.core.types.Message], *, tools: list[jeevesagent.core.types.ToolDef] | None = None, temperature: float = 1.0, max_tokens: int | None = None) collections.abc.AsyncIterator[jeevesagent.core.types.ModelChunk][source]¶
- name = 'claude-opus-4-7'¶
- class jeevesagent.Architecture[source]¶
Bases:
ProtocolStrategy interface for driving the agent loop.
Implementations are async generators: they
yieldEventvalues for every milestone they want surfaced (model chunks, tool calls, tool results, budget warnings, errors, architecture-specific progress events).See
Subagent.mdfor the catalogue of architectures and the design rationale behind the protocol shape.- declared_workers() dict[str, jeevesagent.agent.api.Agent][source]¶
Sub-Agents this architecture composes, keyed by role name.
Used by multi-agent architectures (Supervisor, Actor-Critic, Debate, Router, Blackboard, Swarm) to expose their workers for introspection (logging, telemetry, eval). Single-agent architectures return
{}.
- run(session: AgentSession, deps: Dependencies, prompt: str) collections.abc.AsyncIterator[jeevesagent.core.types.Event][source]¶
Drive iteration; yield events as they happen.
The architecture mutates
session(turns, output, cumulative_usage, messages, interrupted, interruption_reason, metadata) as it iterates and yieldsEvents for the caller to forward (or ignore, in non-streaming runs).Implementations are async generators — declared
async def run(...) -> AsyncIterator[Event]:withyieldstatements in the body.
- class jeevesagent.AuditEntry(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelAn immutable, signed entry in the audit log.
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- model_config¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- timestamp: datetime.datetime¶
- class jeevesagent.AuditLog[source]¶
Bases:
ProtocolThe append-only signed log surface.
- class jeevesagent.Blackboard[source]¶
Public + per-agent private state for the architecture.
- post(author: str, content: str, *, kind: str = 'contribution', private_to: str | None = None) BlackboardEntry[source]¶
- render_for(agent_name: str) str[source]¶
Format the blackboard state as a string for
agent_name.Includes every public entry and the agent’s own private scratchpad if any.
- private: dict[str, list[BlackboardEntry]]¶
- public: list[BlackboardEntry] = []¶
- class jeevesagent.BlackboardArchitecture(*, agents: dict[str, jeevesagent.agent.api.Agent], coordinator: jeevesagent.agent.api.Agent | None = None, decider: jeevesagent.agent.api.Agent | None = None, max_rounds: int = 10, coordinator_instructions: str | None = None, decider_instructions: str | None = None)[source]¶
Coordinator + agents + decider, mediated by a shared blackboard.
- declared_workers() dict[str, jeevesagent.agent.api.Agent][source]¶
- async run(session: jeevesagent.architecture.base.AgentSession, deps: jeevesagent.architecture.base.Dependencies, prompt: str) collections.abc.AsyncIterator[jeevesagent.core.types.Event][source]¶
- name = 'blackboard'¶
- class jeevesagent.BlackboardEntry[source]¶
One contribution on the blackboard.
- timestamp: datetime.datetime¶
- class jeevesagent.Budget[source]¶
Bases:
ProtocolResource governance — tokens, calls, cost, wall clock.
- async allows_step() jeevesagent.core.types.BudgetStatus[source]¶
- class jeevesagent.BudgetStatus(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelResult of a budget check before each step.
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- classmethod blocked_(reason: str) BudgetStatus[source]¶
- classmethod ok_() BudgetStatus[source]¶
- classmethod warn_(reason: str) BudgetStatus[source]¶
- model_config¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- state: Literal['ok', 'warn', 'blocked']¶
- class jeevesagent.CertifiedValue(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelA value carrying provenance metadata for freshness/lineage checks.
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- fetched_at: datetime.datetime¶
- model_config¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- valid_until: datetime.datetime | None = None¶
- value: Any¶
- class jeevesagent.ChromaFactStore(client: Any, *, embedder: jeevesagent.core.protocols.Embedder | None = None, collection_name: str = DEFAULT_FACTS_COLLECTION)[source]¶
Bi-temporal fact store backed by a Chroma collection.
- async all_facts() list[jeevesagent.core.types.Fact][source]¶
- async append(fact: jeevesagent.core.types.Fact) str[source]¶
- classmethod ephemeral(*, embedder: jeevesagent.core.protocols.Embedder | None = None, collection_name: str = DEFAULT_FACTS_COLLECTION) ChromaFactStore[source]¶
- classmethod local(persist_directory: str, *, embedder: jeevesagent.core.protocols.Embedder | None = None, collection_name: str = DEFAULT_FACTS_COLLECTION) ChromaFactStore[source]¶
- async query(*, subject: str | None = None, predicate: str | None = None, object_: str | None = None, valid_at: datetime.datetime | None = None, limit: int = 10, user_id: str | None = None) list[jeevesagent.core.types.Fact][source]¶
- async recall_text(query: str, *, limit: int = 5, valid_at: datetime.datetime | None = None, user_id: str | None = None) list[jeevesagent.core.types.Fact][source]¶
- property embedder: jeevesagent.core.protocols.Embedder¶
- class jeevesagent.ChromaMemory(client: Any, *, embedder: jeevesagent.core.protocols.Embedder | None = None, collection_name: str = DEFAULT_COLLECTION, fact_store: Any | None = None)[source]¶
Memory backed by
chromadb.Construct via
local()for an on-disk persistent client orephemeral()for a process-local in-memory client.- classmethod ephemeral(*, embedder: jeevesagent.core.protocols.Embedder | None = None, collection_name: str = DEFAULT_COLLECTION, with_facts: bool = False, facts_collection_name: str = 'jeeves_facts') ChromaMemory[source]¶
In-memory client (lost on process exit). Great for tests.
- classmethod local(persist_directory: str, *, embedder: jeevesagent.core.protocols.Embedder | None = None, collection_name: str = DEFAULT_COLLECTION, with_facts: bool = False, facts_collection_name: str = 'jeeves_facts') ChromaMemory[source]¶
Persistent on-disk client at
persist_directory.with_facts=Trueattaches aChromaFactStorerooted at the same client so facts persist alongside episodes in the same on-disk store.
- async recall(query: str, *, kind: str = 'episodic', limit: int = 5, time_range: tuple[datetime.datetime, datetime.datetime] | None = None, user_id: str | None = None) list[jeevesagent.core.types.Episode][source]¶
- async recall_facts(query: str, *, limit: int = 5, valid_at: datetime.datetime | None = None, user_id: str | None = None) list[jeevesagent.core.types.Fact][source]¶
- async remember(episode: jeevesagent.core.types.Episode) str[source]¶
- async session_messages(session_id: str, *, user_id: str | None = None, limit: int = 20) list[jeevesagent.core.types.Message][source]¶
- async working() list[jeevesagent.core.types.MemoryBlock][source]¶
- class jeevesagent.ChromaVectorStore(embedder: jeevesagent.core.protocols.Embedder, *, collection_name: str = 'jeeves_vectors', persist_directory: str | None = None, client: Any = None)[source]¶
Vector store backed by
chromadb.- async add(chunks: list[jeevesagent.loader.base.Chunk], ids: list[str] | None = None) list[str][source]¶
- classmethod from_chunks(chunks: list[jeevesagent.loader.base.Chunk], *, embedder: jeevesagent.core.protocols.Embedder, ids: list[str] | None = None, collection_name: str = 'jeeves_vectors', persist_directory: str | None = None, client: Any = None) ChromaVectorStore[source]¶
- Async:
One-shot: construct a ChromaVectorStore + add
chunks.
- classmethod from_texts(texts: list[str], *, embedder: jeevesagent.core.protocols.Embedder, metadatas: list[dict[str, Any]] | None = None, ids: list[str] | None = None, collection_name: str = 'jeeves_vectors', persist_directory: str | None = None, client: Any = None) ChromaVectorStore[source]¶
- Async:
One-shot: construct a ChromaVectorStore from raw text strings (each becomes a
Chunkwith the matching metadata dict, or empty ifmetadatasis None).
- async search(query: str, *, k: int = 4, filter: collections.abc.Mapping[str, Any] | None = None, diversity: float | None = None) list[jeevesagent.vectorstore.base.SearchResult][source]¶
- async search_by_vector(vector: list[float], *, k: int = 4, filter: collections.abc.Mapping[str, Any] | None = None, diversity: float | None = None) list[jeevesagent.vectorstore.base.SearchResult][source]¶
- property embedder: jeevesagent.core.protocols.Embedder¶
- name = 'chroma'¶
- class jeevesagent.CohereEmbedder(model: str = 'embed-english-v3.0', *, client: Any | None = None, api_key: str | None = None, input_type: str = 'search_document')[source]¶
Embeddings via Cohere’s
cohereSDK.Models and dimensions:
embed-english-v3.0/embed-multilingual-v3.0-> 1024embed-english-light-v3.0/embed-multilingual-light-v3.0-> 384
input_typeis required by Cohere v3 models:"search_document"(default) — corpus / fact-store entries"search_query"— retrieval queries"classification"/"clustering"for non-retrieval uses
- class jeevesagent.ConsolidationWorker(memory: jeevesagent.core.protocols.Memory, *, interval_seconds: float = 60.0, on_consolidated: OnConsolidatedCb | None = None, on_error: OnErrorCb | None = None)[source]¶
Periodic consolidator for any
Memorybackend.- async run_forever() None[source]¶
Sleep
interval_secondsthen consolidate. Repeat until cancelled.Spawn this in an
anyio.create_task_group()— the cancel scope at scope exit terminates the worker cooperatively.
- async run_once() int[source]¶
Run a single consolidation pass. Returns the number of new facts extracted (
0when no fact store / nothing changed).Errors in
memory.consolidate()are routed toon_errorand not re-raised, so callers can use this in a polling loop without wrapping it in their own try/except.
- class jeevesagent.Consolidator(*, model: jeevesagent.core.protocols.Model, system_prompt: str = DEFAULT_SYSTEM_PROMPT, max_facts_per_episode: int = 20)[source]¶
Wraps a
Modelto extractFactrows from episodes.- async consolidate(episodes: collections.abc.Iterable[jeevesagent.core.types.Episode], *, store: jeevesagent.memory.facts.FactStore) list[jeevesagent.core.types.Fact][source]¶
Process
episodes; append extracted facts tostore; return the newFactinstances in extraction order.Uses
store.append_manywhen available so the underlying store can batch the embedder calls (oneembed_batchAPI round-trip instead of N individualembedcalls). Falls back to per-factappendfor stores that haven’t implementedappend_many.
- class jeevesagent.Dependencies[source]¶
Bundled protocol implementations passed to every architecture.
Constructed once per run from the
Agent’s configured backends. Architectures treat this as read-only — they call methods on the contained protocols but don’t mutate the struct itself.Multi-agent architectures (Supervisor, Router, etc.) will grow helper methods on this class —
fresh_session,scope_for_worker,with_extra_tools,spawn_child— as they land in v0.5+. v0.3 keeps it as a passive struct.- audit_log: jeevesagent.security.audit.AuditLog | None¶
- context: jeevesagent.core.context.RunContext¶
Typed scope for the run —
user_id(memory namespace),session_id(conversation thread),run_id(this specific invocation), andmetadata(free-form app context). SeeRunContextfor the per-field semantics.
- fast_budget: bool = True¶
Skip
budget.allows_step()andbudget.consume(...)when budget isNoBudget.
- fast_hooks: bool = True¶
Skip
hooks.pre_tool/hooks.post_tooldispatch when no hooks have been registered.
- fast_permissions: bool = True¶
Skip per-tool
permissions.check(...)when permissions is the no-opAllowAll.
- fast_runtime: bool = True¶
Inline
await fn(*args)(skippingruntime.step(...)wrapping + idempotency-key derivation) when runtime isInProcRuntime.
- fast_telemetry: bool = True¶
Skip
telemetry.trace(...)contextmanagers +emit_metriccalls whentelemetryisNoTelemetry.
- permissions: jeevesagent.core.protocols.Permissions¶
- runtime: jeevesagent.core.protocols.Runtime¶
- streaming: bool = False¶
Whether a downstream consumer is reading from
agent.stream(). When True, architectures should preserve real-time event-arrival semantics so a consumer that breaks out of the iterator triggers prompt cancellation. When False (the default foragent.run()), architectures may batch events for fewer task-group / channel allocations on the hot path.
- telemetry: jeevesagent.core.protocols.Telemetry¶
- class jeevesagent.EchoModel(*, prefix: str = 'Echo: ', chunk_delay_s: float = 0.0, cost_per_token: float = 0.0)[source]¶
Echo-style model for tests and demos.
- async complete(messages: list[jeevesagent.core.types.Message], *, tools: list[jeevesagent.core.types.ToolDef] | None = None, temperature: float = 1.0, max_tokens: int | None = None) tuple[str, list[jeevesagent.core.types.ToolCall], jeevesagent.core.types.Usage, str][source]¶
Single-shot echo. Returns the echoed user prompt as one string with synthetic usage. No per-token chunking — used by the non-streaming hot path (
agent.run()).
- async stream(messages: list[jeevesagent.core.types.Message], *, tools: list[jeevesagent.core.types.ToolDef] | None = None, temperature: float = 1.0, max_tokens: int | None = None) collections.abc.AsyncIterator[jeevesagent.core.types.ModelChunk][source]¶
- class jeevesagent.Embedder[source]¶
Bases:
ProtocolText-to-vector embedding model used by the memory subsystem.
- class jeevesagent.Episode(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelA single (input, decisions, tool calls, output) tuple from history.
user_idis the framework-managed namespace partition. Episodes persisted with oneuser_idvalue are never visible to memory recall queries scoped to a differentuser_id.Noneis its own bucket — the “anonymous / single-tenant” namespace — and does not see episodes belonging to a non-Noneuser_id(and vice versa). Set automatically fromRunContextby the agent loop; pass explicitly when constructing episodes outside a run.Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- occurred_at: datetime.datetime = None¶
- class jeevesagent.Event(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelA single observable record from a running session.
Carries a discriminator (
kind) plus a free-form payload. Construct via the class methods to ensure consistent shapes.Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- classmethod architecture_event(session_id: str, name: str, **data: Any) Event[source]¶
Generic architecture-progress event.
nameis a namespaced string identifying the source architecture and the kind of progress (e.g."self_refine.critique","reflexion.lesson_persisted","router.classified").datais merged into the payload alongsidenameso consumers can pattern-match onnameand read structured fields off the rest.
- classmethod budget_exceeded(session_id: str, status: BudgetStatus) Event[source]¶
- classmethod budget_warning(session_id: str, status: BudgetStatus) Event[source]¶
- classmethod error(session_id: str, exc: BaseException) Event[source]¶
- classmethod model_chunk(session_id: str, chunk: ModelChunk) Event[source]¶
- classmethod tool_result(session_id: str, result: ToolResult) Event[source]¶
- at: datetime.datetime = None¶
- class jeevesagent.EventKind[source]¶
Bases:
enum.StrEnumEnum where members are also (and must be) strings
Initialize self. See help(type(self)) for accurate signature.
- ARCHITECTURE_EVENT = 'architecture_event'¶
Generic architecture-progress event. Carries a namespaced
namein the payload (e.g."self_refine.critique","reflexion.lesson_persisted","router.classified") so each architecture can stream its own progress signal without expandingEventKind.
- BUDGET_EXCEEDED = 'budget_exceeded'¶
- BUDGET_WARNING = 'budget_warning'¶
- COMPLETED = 'completed'¶
- ERROR = 'error'¶
- MEMORY_RECALL = 'memory_recall'¶
- MEMORY_WRITE = 'memory_write'¶
- MODEL_CHUNK = 'model_chunk'¶
- PERMISSION_ASK = 'permission_ask'¶
- PERMISSION_DECISION = 'permission_decision'¶
- STARTED = 'started'¶
- TOOL_CALL = 'tool_call'¶
- TOOL_RESULT = 'tool_result'¶
- class jeevesagent.FAISSVectorStore(embedder: jeevesagent.core.protocols.Embedder, *, dimension: int | None = None, index_factory_string: str = 'HNSW32', metric: str = 'ip')[source]¶
Vector store backed by
faiss-cpu.- async add(chunks: list[jeevesagent.loader.base.Chunk], ids: list[str] | None = None) list[str][source]¶
- classmethod from_chunks(chunks: list[jeevesagent.loader.base.Chunk], *, embedder: jeevesagent.core.protocols.Embedder, ids: list[str] | None = None, dimension: int | None = None, index_factory_string: str = 'HNSW32', metric: str = 'ip') FAISSVectorStore[source]¶
- Async:
One-shot: construct a FAISSVectorStore + add
chunks.
- classmethod from_texts(texts: list[str], *, embedder: jeevesagent.core.protocols.Embedder, metadatas: list[dict[str, Any]] | None = None, ids: list[str] | None = None, dimension: int | None = None, index_factory_string: str = 'HNSW32', metric: str = 'ip') FAISSVectorStore[source]¶
- Async:
One-shot: construct a FAISSVectorStore from raw text strings (each becomes a
Chunkwith the matching metadata dict, or empty ifmetadatasis None).
- async search(query: str, *, k: int = 4, filter: collections.abc.Mapping[str, Any] | None = None, diversity: float | None = None) list[jeevesagent.vectorstore.base.SearchResult][source]¶
- async search_by_vector(vector: list[float], *, k: int = 4, filter: collections.abc.Mapping[str, Any] | None = None, diversity: float | None = None) list[jeevesagent.vectorstore.base.SearchResult][source]¶
- property embedder: jeevesagent.core.protocols.Embedder¶
- name = 'faiss'¶
- class jeevesagent.Fact(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelA semantic claim extracted from one or more episodes.
Bi-temporal:
valid_from/valid_untiltracks when the fact was true in the world;recorded_attracks when we learned it.user_idis the framework-managed namespace partition. Facts persisted with oneuser_idvalue are never visible to recall queries scoped to a differentuser_id. Set automatically fromRunContextby the agent loop / consolidator; pass explicitly when constructing facts outside a run.Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- recorded_at: datetime.datetime = None¶
- valid_from: datetime.datetime = None¶
- valid_until: datetime.datetime | None = None¶
- class jeevesagent.FactStore[source]¶
Bases:
ProtocolStorage surface for bi-temporal facts.
- async all_facts() list[jeevesagent.core.types.Fact][source]¶
- async append(fact: jeevesagent.core.types.Fact) str[source]¶
- async query(*, subject: str | None = None, predicate: str | None = None, object_: str | None = None, valid_at: datetime.datetime | None = None, limit: int = 10, user_id: str | None = None) list[jeevesagent.core.types.Fact][source]¶
- async recall_text(query: str, *, limit: int = 5, valid_at: datetime.datetime | None = None, user_id: str | None = None) list[jeevesagent.core.types.Fact][source]¶
- class jeevesagent.FileAuditLog(path: str | pathlib.Path, *, secret: str = '')[source]¶
JSONL append-only audit log with HMAC signatures.
On construction we read any pre-existing entries to recover the highest seq, so a process restart picks up where the last one left off.
- async append(*, session_id: str, actor: str, action: str, payload: dict[str, Any]) jeevesagent.core.types.AuditEntry[source]¶
- async query(*, session_id: str | None = None, action: str | None = None) list[jeevesagent.core.types.AuditEntry][source]¶
- property path: pathlib.Path¶
- class jeevesagent.FilesystemSandbox(inner: jeevesagent.core.protocols.ToolHost, *, roots: collections.abc.Iterable[str | pathlib.Path], path_args: collections.abc.Iterable[str] | None = None, auto_detect: bool = True)[source]¶
Restrict a tool host’s path-typed arguments to declared roots.
- async call(tool: str, args: collections.abc.Mapping[str, Any], *, call_id: str = '') jeevesagent.core.types.ToolResult[source]¶
- property inner: jeevesagent.core.protocols.ToolHost¶
- property roots: tuple[pathlib.Path, Ellipsis]¶
- class jeevesagent.FreshnessPolicy[source]¶
Maximum age for certified values from each source.
per_sourcemaps a source-prefix (matched withstartswith) to atimedelta. The first prefix that matches wins.defaultis used when no prefix matches; if alsoNone, the policy treats all values as fresh.- classmethod from_dict(per_source: dict[str, datetime.timedelta] | None = None, *, default: datetime.timedelta | None = None) FreshnessPolicy[source]¶
- max_age_for(source: str) datetime.timedelta | None[source]¶
- default: datetime.timedelta | None = None¶
- per_source: tuple[tuple[str, datetime.timedelta], Ellipsis] = ()¶
- class jeevesagent.Handoff[source]¶
Per-peer handoff configuration.
agent— the peerAgent.input_type— optional Pydantic model. When set, the generated handoff tool’s input schema mirrors this model’s fields, so the calling model gets a typed schema (instead of a stringmessage). The validated payload is exposed toinput_filterand surfaces in theswarm.handoffevent.input_filter— optional callback(history, payload) → promptfor selective context forwarding. Default behavior respects the Swarm’spass_full_historyflag.description— override the generated tool’s description. Useful when the agent’s name is opaque (“billing_v2”) but the description should be user-friendly.tool_name— override the auto-generated tool name. Default is"transfer_to_<key>"where<key>is the peer’s key in the swarm’sagentsdict.
- agent: jeevesagent.agent.api.Agent¶
- input_filter: InputFilter | None = None¶
- input_type: type[pydantic.BaseModel] | None = None¶
- class jeevesagent.HashEmbedder(dimensions: int = DEFAULT_HASH_DIMENSIONS)[source]¶
Deterministic SHA256-seeded unit vectors.
Each text gets a fresh
random.Randomseeded by the SHA256 of its UTF-8 bytes, then samplesdimensionsGaussian values and L2-normalises the result. Same text always produces the same vector; different texts produce well-distributed vectors with cosine distances that correlate with literal text equality (not semantic similarity).Use this in tests (fast, no network) and as a default for in-memory backends that need some vector but don’t need real semantic recall.
- class jeevesagent.HookHost[source]¶
Bases:
ProtocolAggregator over user-registered lifecycle callbacks.
- async on_event(event: jeevesagent.core.types.Event) None[source]¶
- async post_tool(call: jeevesagent.core.types.ToolCall, result: jeevesagent.core.types.ToolResult) None[source]¶
- async pre_tool(call: jeevesagent.core.types.ToolCall) jeevesagent.core.types.PermissionDecision[source]¶
- class jeevesagent.HookRegistry[source]¶
Implements
HookHost.- async on_event(event: jeevesagent.core.types.Event) None[source]¶
- async post_tool(call: jeevesagent.core.types.ToolCall, result: jeevesagent.core.types.ToolResult) None[source]¶
Best-effort post-tool callbacks. Failures and timeouts are absorbed so they cannot affect the result the loop returns.
- async pre_tool(call: jeevesagent.core.types.ToolCall) jeevesagent.core.types.PermissionDecision[source]¶
Run all pre-tool hooks. First deny wins; otherwise allow.
- register_post_tool(hook: PostToolHook) PostToolHook[source]¶
- register_pre_tool(hook: PreToolHook) PreToolHook[source]¶
- post_tool_hooks: list[PostToolHook] = []¶
- pre_tool_hooks: list[PreToolHook] = []¶
- class jeevesagent.InMemoryAuditLog(*, secret: str = '')[source]¶
List-backed signed audit log.
- async all_entries() list[jeevesagent.core.types.AuditEntry][source]¶
- class jeevesagent.InMemoryFactStore(*, embedder: jeevesagent.core.protocols.Embedder | None = None)[source]¶
Dict-backed bi-temporal fact store.
All operations are coordinated by an
anyio.Lockso concurrent appends from the consolidator and reads from the agent loop don’t tear the index.When an
embedderis supplied, every appended fact’s triple ("subject predicate object") is embedded and stored alongside the fact, andrecall_text()ranks by cosine similarity against the query’s embedding. When no embedder is given,recall_text()falls back to token-overlap matching.- async all_facts() list[jeevesagent.core.types.Fact][source]¶
- async append(fact: jeevesagent.core.types.Fact) str[source]¶
Append a fact, invalidating any superseded predecessors.
Supersession rule: any existing fact with matching subject + predicate, currently valid (
valid_until is None), and a differentobjectgets itsvalid_untilset to the new fact’svalid_from.
- async append_many(facts: collections.abc.Iterable[jeevesagent.core.types.Fact]) list[str][source]¶
Append a batch of facts. Embedder calls are coalesced via
Embedder.embed_batch()when an embedder is configured — one network round-trip for the batch instead of N.
- async query(*, subject: str | None = None, predicate: str | None = None, object_: str | None = None, valid_at: datetime.datetime | None = None, limit: int = 10, user_id: str | None = None) list[jeevesagent.core.types.Fact][source]¶
- async recall_text(query: str, *, limit: int = 5, valid_at: datetime.datetime | None = None, user_id: str | None = None) list[jeevesagent.core.types.Fact][source]¶
Rank facts against
query.With an embedder configured: cosine-similarity over the query’s embedding vs each fact triple’s stored embedding. Without one: token-overlap with a small stop-word list (longer overlaps win, ties break by shorter haystack = more specific match).
user_idpartitions the candidate set as a hard namespace boundary — seeFactfor semantics.
- snapshot() dict[str, jeevesagent.core.types.Fact][source]¶
- property embedder: jeevesagent.core.protocols.Embedder | None¶
- class jeevesagent.InMemoryMemory(*, consolidator: jeevesagent.memory.consolidator.Consolidator | None = None, fact_store: jeevesagent.memory.facts.FactStore | None = None)[source]¶
Dict-backed implementation of
Memory.- async consolidate() None[source]¶
Process unconsolidated episodes through the configured
Consolidator, appending facts toself.facts.
- async recall(query: str, *, kind: str = 'episodic', limit: int = 5, time_range: tuple[datetime.datetime, datetime.datetime] | None = None, user_id: str | None = None) list[jeevesagent.core.types.Episode][source]¶
- async recall_facts(query: str, *, limit: int = 5, valid_at: datetime.datetime | None = None, user_id: str | None = None) list[jeevesagent.core.types.Fact][source]¶
- async remember(episode: jeevesagent.core.types.Episode) str[source]¶
- async session_messages(session_id: str, *, user_id: str | None = None, limit: int = 20) list[jeevesagent.core.types.Message][source]¶
Return user/assistant pairs from prior runs of this session.
Materialises each persisted
Episodefor the givensession_id(within theuser_idpartition) into a[USER input, ASSISTANT output]pair, ordered oldest-first and capped atlimitturns total — i.e. up tolimit / 2Q/A exchanges. Tool-call traces are not replayed; the final assistant text per turn is sufficient context for follow-ups.
- async working() list[jeevesagent.core.types.MemoryBlock][source]¶
- class jeevesagent.InMemoryVectorStore(embedder: jeevesagent.core.protocols.Embedder)[source]¶
In-process vector store backed by a Python list.
- async add(chunks: list[jeevesagent.loader.base.Chunk], ids: list[str] | None = None) list[str][source]¶
- classmethod from_chunks(chunks: list[jeevesagent.loader.base.Chunk], *, embedder: jeevesagent.core.protocols.Embedder, ids: list[str] | None = None) InMemoryVectorStore[source]¶
- Async:
One-shot: construct an InMemoryVectorStore + add
chunks.
- classmethod from_texts(texts: list[str], *, embedder: jeevesagent.core.protocols.Embedder, metadatas: list[dict[str, Any]] | None = None, ids: list[str] | None = None) InMemoryVectorStore[source]¶
- Async:
One-shot: construct an InMemoryVectorStore from raw text strings (each becomes a
Chunkwith the matching metadata dict, or empty ifmetadatasis None).
- classmethod load(path: str | pathlib.Path, *, embedder: jeevesagent.core.protocols.Embedder) InMemoryVectorStore[source]¶
- Async:
Restore a store previously
save()-d. Pass the same embedder kind/dimensions or queries will produce nonsense scores.
- async save(path: str | pathlib.Path) None[source]¶
Write the full store (chunks + vectors + ids) to a JSON file. The embedder is NOT serialized — supply the same embedder when calling
load().
- async search(query: str, *, k: int = 4, filter: collections.abc.Mapping[str, Any] | None = None, diversity: float | None = None) list[jeevesagent.vectorstore.base.SearchResult][source]¶
- async search_by_vector(vector: list[float], *, k: int = 4, filter: collections.abc.Mapping[str, Any] | None = None, diversity: float | None = None) list[jeevesagent.vectorstore.base.SearchResult][source]¶
- async search_hybrid(query: str, *, k: int = 4, filter: collections.abc.Mapping[str, Any] | None = None, alpha: float = 0.5) list[jeevesagent.vectorstore.base.SearchResult][source]¶
Hybrid lexical (BM25) + vector search via RRF.
alphais in [0, 1]: 0 = pure BM25, 1 = pure vector, 0.5 = even weighting (RRF default). Both rankings are computed independently and fused by Reciprocal Rank Fusion, then the top-ksurvivors are returned.Embeddings catch semantic similarity (“automobile” ↔ “car”), BM25 catches exact-term hits (model names, error codes, person names) — together they outperform either alone on most retrieval benchmarks.
- property embedder: jeevesagent.core.protocols.Embedder¶
- name = 'in-memory'¶
- class jeevesagent.InProcRuntime[source]¶
No durability. Each step runs immediately.
- async session(session_id: str) collections.abc.AsyncIterator[InProcSession][source]¶
- async step(name: str, fn: collections.abc.Callable[Ellipsis, collections.abc.Awaitable[Any]], *args: Any, idempotency_key: str | None = None, **kwargs: Any) Any[source]¶
- stream_step(name: str, fn: collections.abc.Callable[Ellipsis, collections.abc.AsyncIterator[Any]], *args: Any, **kwargs: Any) collections.abc.AsyncIterator[Any][source]¶
- name = 'inproc'¶
- class jeevesagent.InProcessToolHost(tools: list[Tool | collections.abc.Callable[Ellipsis, Any]] | None = None)[source]¶
A dict-backed
ToolHost.- async call(tool: str, args: collections.abc.Mapping[str, Any], *, call_id: str = '') jeevesagent.core.types.ToolResult[source]¶
- register(item: Tool | collections.abc.Callable[Ellipsis, Any]) Tool[source]¶
- async watch() collections.abc.AsyncIterator[jeevesagent.core.types.ToolEvent][source]¶
In-process registry is static; the generator yields nothing.
Iterating over an empty tuple keeps this an async generator (so the return type is
AsyncIterator) without ever producing an event at runtime.
- class jeevesagent.JeevesGateway(config: JeevesConfig, *, registry: jeevesagent.mcp.registry.MCPRegistry | None = None)[source]¶
ToolHost-shaped wrapper around the Jeeves Gateway.
- as_mcp_server() jeevesagent.mcp.spec.MCPServerSpec[source]¶
Return the
MCPServerSpecdescribing this gateway.
- as_registry() jeevesagent.mcp.registry.MCPRegistry[source]¶
Return a one-server
MCPRegistryrooted at this gateway.
- async call(tool: str, args: collections.abc.Mapping[str, Any], *, call_id: str = '') jeevesagent.core.types.ToolResult[source]¶
- classmethod from_env(*, env_var: str = JEEVES_API_KEY_ENV, base_url: str | None = None, server_name: str = JEEVES_DEFAULT_SERVER_NAME) JeevesGateway[source]¶
Build a gateway from the
JEEVES_API_KEYenvironment variable.
- property config: JeevesConfig¶
- class jeevesagent.JournaledRuntime(store: jeevesagent.runtime.journal.JournalStore | None = None)[source]¶
Runtime that journals every step’s result for replay.
Pass any
JournalStore(in-memory for tests, sqlite for durable single-process use, future Postgres/DBOS adapters for multi-process / multi-host).- async session(session_id: str) collections.abc.AsyncIterator[JournaledSession][source]¶
- async step(name: str, fn: collections.abc.Callable[Ellipsis, collections.abc.Awaitable[Any]], *args: Any, idempotency_key: str | None = None, **kwargs: Any) Any[source]¶
- stream_step(name: str, fn: collections.abc.Callable[Ellipsis, collections.abc.AsyncIterator[Any]], *args: Any, **kwargs: Any) collections.abc.AsyncIterator[Any][source]¶
- name = 'journaled'¶
- property store: jeevesagent.runtime.journal.JournalStore¶
- class jeevesagent.LineagePolicy[source]¶
Allow-list of source prefixes for the entire lineage chain.
A
CertifiedValueis acceptable if every entry invalue.lineage(interpreted as a source prefix) starts with one of the allowed prefixes.
- class jeevesagent.LiteLLMModel(model: str, *, api_key: str | None = None, client: Any | None = None, **litellm_kwargs: Any)[source]¶
Bases:
jeevesagent.model.openai.OpenAIModelTalks to any LiteLLM-supported provider.
Inherits chunk normalisation, tool-call delta aggregation, and message-conversion from
OpenAIModelbecause LiteLLM produces OpenAI-shaped outputs.
- class jeevesagent.MCPClient(spec: jeevesagent.mcp.spec.MCPServerSpec, *, session: Any | None = None)[source]¶
One client per MCP server. Holds the live
ClientSession.- async call_tool(name: str, args: dict[str, Any]) Any[source]¶
Invoke
namewithargs. Returns the SDK’s CallToolResult.
- async connect() None[source]¶
Open the transport and initialise the session.
No-op if already connected (or a fake session was injected at construction time).
- async list_tools() list[Any][source]¶
Return whatever the SDK gave us — a list of tool descriptors.
Each descriptor has
name,description,inputSchema. We don’t translate toToolDefhere — the registry does that, since it also assigns names with disambiguation.
- property spec: jeevesagent.mcp.spec.MCPServerSpec¶
- class jeevesagent.MCPRegistry(items: list[jeevesagent.mcp.spec.MCPServerSpec | jeevesagent.mcp.client.MCPClient] | None = None)[source]¶
Aggregates many
MCPClientinstances into a singleToolHost.- async call(tool: str, args: collections.abc.Mapping[str, Any], *, call_id: str = '') jeevesagent.core.types.ToolResult[source]¶
- async watch() collections.abc.AsyncIterator[jeevesagent.core.types.ToolEvent][source]¶
listChangednotifications. Not yet implemented; yields nothing.
- class jeevesagent.MCPServerSpec[source]¶
How to find and talk to a single MCP server.
Construct via the class methods
stdio()orhttp()rather than the bare constructor — they enforce the right combination of fields per transport.- classmethod http(name: str, url: str, headers: dict[str, str] | None = None, *, description: str = '') MCPServerSpec[source]¶
Connect to
urlvia Streamable HTTP transport.
- classmethod stdio(name: str, command: str, args: list[str] | tuple[str, Ellipsis] | None = None, env: dict[str, str] | None = None, *, description: str = '') MCPServerSpec[source]¶
Spawn
commandas a subprocess and speak JSON-RPC over its stdio.
- transport: Literal['stdio', 'http']¶
- class jeevesagent.Memory[source]¶
Bases:
ProtocolTiered memory: working blocks, episodic store, semantic graph.
- async append_block(name: str, content: str) None[source]¶
Append to a named block, creating it if absent.
- async recall(query: str, *, kind: str = 'episodic', limit: int = 5, time_range: tuple[datetime.datetime, datetime.datetime] | None = None, user_id: str | None = None) list[jeevesagent.core.types.Episode][source]¶
Retrieve episodes (or facts, when
kind='semantic').When
user_idis supplied, results are restricted to episodes stored with that exactuser_idvalue.Noneis its own bucket (the “anonymous / single-tenant” namespace) — episodes stored withuser_id=Noneare never visible to a query withuser_id="alice"and vice versa. Backends MUST honour this filter to preserve the framework’s multi-tenant safety contract.
- async recall_facts(query: str, *, limit: int = 5, valid_at: datetime.datetime | None = None, user_id: str | None = None) list[jeevesagent.core.types.Fact][source]¶
Retrieve bi-temporal facts matching
query.Backends that don’t expose a fact store return
[]. The agent loop calls this directly rather than duck-typing onmemory.factsso backends without fact support don’t need any opt-out mechanism.user_idfilters by namespace partition with the same semantics asrecall():Noneis its own bucket and does not cross-contaminate with non-None values.
- async remember(episode: jeevesagent.core.types.Episode) str[source]¶
Persist an episode. Returns the episode ID.
- async session_messages(session_id: str, *, user_id: str | None = None, limit: int = 20) list[jeevesagent.core.types.Message][source]¶
Return the most-recent
limituser/assistant turns from the conversation identified bysession_id, in order (oldest first).This is the conversation-continuity primitive — the agent loop calls it at the top of every run so that reusing a
session_idactually continues the chat (the model sees previous turns as realMessagehistory) rather than starting fresh and relying solely on semantic recall.user_idMUST be respected by backends as a hard namespace partition: messages persisted under oneuser_idare never visible to a query scoped to a different one. Backends without persisted message logs return[]— the agent loop falls back to the semantic-recall path in that case.
- async working() list[jeevesagent.core.types.MemoryBlock][source]¶
All in-context blocks. Pinned to every prompt.
- class jeevesagent.MemoryBlock(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelAn in-context memory block, pinned to every prompt.
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- updated_at: datetime.datetime = None¶
- class jeevesagent.Message(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelA single chat message in the model’s conversation.
tool_callsis populated on assistant messages that emitted tool calls in the previous turn — real provider adapters (Anthropictool_useblocks, OpenAItool_callsarray) need to reconstruct the right wire format from this.Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- model_config¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class jeevesagent.Mode[source]¶
Bases:
enum.StrEnumEnum where members are also (and must be) strings
Initialize self. See help(type(self)) for accurate signature.
- ACCEPT_EDITS = 'acceptEdits'¶
- BYPASS = 'bypassPermissions'¶
- DEFAULT = 'default'¶
- class jeevesagent.Model[source]¶
Bases:
ProtocolLLM provider interface. One adapter per lab (Anthropic, OpenAI, …).
The required surface is
stream(...)— every adapter must implement it. Adapters MAY additionally overridecomplete(...)with a non-streaming (single-shot) call; if not,completefalls back to consuming the stream internally and assembling the full response, which is correct but slower (per-chunk wire + parsing overhead). Architectures usecompleteon the non-streaming hot path (agent.run()) andstreamwhen a consumer is reading fromagent.stream().- stream(messages: list[jeevesagent.core.types.Message], *, tools: list[jeevesagent.core.types.ToolDef] | None = None, temperature: float = 1.0, max_tokens: int | None = None) collections.abc.AsyncIterator[jeevesagent.core.types.ModelChunk][source]¶
Stream completion chunks. Each chunk is text, tool_call, or finish.
- class jeevesagent.ModelChunk(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelA single chunk from a streaming model call.
Discriminated by
kind. Exactly one of the optional fields is set depending on the kind.Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- kind: Literal['text', 'tool_call', 'finish']¶
- class jeevesagent.MultiAgentDebate(*, debaters: list[jeevesagent.agent.api.Agent], judge: jeevesagent.agent.api.Agent | None = None, rounds: int = 2, convergence_check: bool = True, convergence_similarity: float = 0.85, debater_instructions: str | None = None, judge_instructions: str | None = None)[source]¶
N debaters + optional judge orchestration.
- declared_workers() dict[str, jeevesagent.agent.api.Agent][source]¶
- async run(session: jeevesagent.architecture.base.AgentSession, deps: jeevesagent.architecture.base.Dependencies, prompt: str) collections.abc.AsyncIterator[jeevesagent.core.types.Event][source]¶
- name = 'debate'¶
- class jeevesagent.NoSandbox(inner: jeevesagent.core.protocols.ToolHost)[source]¶
Pass-through wrapper around a
ToolHost.- async call(tool: str, args: collections.abc.Mapping[str, Any], *, call_id: str = '') jeevesagent.core.types.ToolResult[source]¶
- property inner: jeevesagent.core.protocols.ToolHost¶
- class jeevesagent.NoTelemetry[source]¶
No-op telemetry. Very cheap; safe to call on every loop step.
- async trace(name: str, **attrs: Any) collections.abc.AsyncIterator[jeevesagent.core.types.Span][source]¶
- class jeevesagent.OTelTelemetry(*, tracer_provider: Any | None = None, meter_provider: Any | None = None, instrumentation_name: str = 'jeevesagent')[source]¶
OpenTelemetry-backed
Telemetry.- async trace(name: str, **attrs: Any) collections.abc.AsyncIterator[jeevesagent.core.types.Span][source]¶
- class jeevesagent.OpenAIEmbedder(model: str = 'text-embedding-3-small', *, dimensions: int | None = None, client: Any | None = None, api_key: str | None = None)[source]¶
Embeddings via OpenAI’s
embeddings.createAPI.Dimensions are fixed by the model:
text-embedding-3-small-> 1536text-embedding-3-large-> 3072text-embedding-ada-002-> 1536
Pass
dimensions=only fortext-embedding-3-*models, which support thedimensionsparameter for projection.
- class jeevesagent.OpenAIModel(model: str = 'gpt-4o', *, client: Any = None, api_key: str | None = None, base_url: str | None = None)[source]¶
Talks to OpenAI via
openai.AsyncOpenAI.- async complete(messages: list[jeevesagent.core.types.Message], *, tools: list[jeevesagent.core.types.ToolDef] | None = None, temperature: float = 1.0, max_tokens: int | None = None) tuple[str, list[jeevesagent.core.types.ToolCall], jeevesagent.core.types.Usage, str][source]¶
Single-shot completion (no per-chunk yields).
Tries the OpenAI non-streaming endpoint (
stream=False) first. If that fails — e.g. when a test fake client only supports streaming, or a transport doesn’t honorstream=False— falls back to consumingstream()internally and accumulating the result. The fallback still saves the per-chunk yield + Event construction overhead on the architecture side because ReAct callscompletewith a singleawait.
- async stream(messages: list[jeevesagent.core.types.Message], *, tools: list[jeevesagent.core.types.ToolDef] | None = None, temperature: float = 1.0, max_tokens: int | None = None) collections.abc.AsyncIterator[jeevesagent.core.types.ModelChunk][source]¶
- name = 'gpt-4o'¶
- class jeevesagent.PermissionDecision(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelOutcome of a permission check or pre-tool hook.
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- classmethod allow_(reason: str | None = None) PermissionDecision[source]¶
- classmethod ask_(reason: str | None = None) PermissionDecision[source]¶
- classmethod deny_(reason: str) PermissionDecision[source]¶
- decision: Literal['allow', 'deny', 'ask']¶
- model_config¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class jeevesagent.Permissions[source]¶
Bases:
ProtocolDecides whether a tool call is allowed.
- async check(call: jeevesagent.core.types.ToolCall, *, context: collections.abc.Mapping[str, Any]) jeevesagent.core.types.PermissionDecision[source]¶
- class jeevesagent.Plan(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelA list of plan steps in execution order.
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- class jeevesagent.PlanAndExecute(*, max_steps: int = 8, planner_prompt: str | None = None, executor_prompt: str | None = None, synthesizer_prompt: str | None = None)[source]¶
Planner → step executor → synthesizer.
- declared_workers() dict[str, jeevesagent.agent.api.Agent][source]¶
- async run(session: jeevesagent.architecture.base.AgentSession, deps: jeevesagent.architecture.base.Dependencies, prompt: str) collections.abc.AsyncIterator[jeevesagent.core.types.Event][source]¶
- name = 'plan-and-execute'¶
- class jeevesagent.PlanStep(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelOne step of a plan.
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- class jeevesagent.PostgresFactStore(pool: Any, *, embedder: jeevesagent.core.protocols.Embedder | None = None)[source]¶
Postgres-backed bi-temporal fact store.
- async all_facts() list[jeevesagent.core.types.Fact][source]¶
- async append(fact: jeevesagent.core.types.Fact) str[source]¶
- async append_many(facts: collections.abc.Iterable[jeevesagent.core.types.Fact]) list[str][source]¶
- classmethod connect(dsn: str, *, embedder: jeevesagent.core.protocols.Embedder | None = None, min_size: int = 1, max_size: int = 10) PostgresFactStore[source]¶
- Async:
- async query(*, subject: str | None = None, predicate: str | None = None, object_: str | None = None, valid_at: datetime.datetime | None = None, limit: int = 10, user_id: str | None = None) list[jeevesagent.core.types.Fact][source]¶
- async recall_text(query: str, *, limit: int = 5, valid_at: datetime.datetime | None = None, user_id: str | None = None) list[jeevesagent.core.types.Fact][source]¶
- schema_sql() list[str][source]¶
Return the DDL for this fact store’s schema.
Exposed so tests can assert on the SQL strings, and so migration scripts can apply the schema in their own transaction.
- property embedder: jeevesagent.core.protocols.Embedder | None¶
- class jeevesagent.PostgresJournalStore(pool: Any)[source]¶
Postgres-backed journal. Production-grade durable replay.
Same shape as
SqliteJournalStorebut usesasyncpgand a Postgres database. Designed for users who already run a Postgres instance for the rest of their stack (memory, audit, app state) and want their durable-runtime journal to live there too.Why not a DBOS adapter?
DBOS Python’s workflow model requires
@DBOS.workflow()and@DBOS.communicator()decorators at module-load time. OurRuntime.step(name, fn, *args)API takes arbitrary callables at runtime, which doesn’t compose cleanly with DBOS’s static-decoration model.PostgresJournalStoregives the same durability guarantee through our existingJournaledRuntimearchitecture, with no decorator intrusion on user code.
- class jeevesagent.PostgresMemory(pool: Any, *, embedder: jeevesagent.core.protocols.Embedder | None = None, namespace: str = DEFAULT_NAMESPACE, fact_store: Any | None = None)[source]¶
Postgres-backed
Memory.poolis anasyncpg.Pool(or anything with the same API). Tests can pass a fake pool whoseacquire()returns a fake connection.- classmethod connect(dsn: str, *, embedder: jeevesagent.core.protocols.Embedder | None = None, namespace: str = DEFAULT_NAMESPACE, min_size: int = 1, max_size: int = 10, with_facts: bool = False) PostgresMemory[source]¶
- Async:
Open an asyncpg pool and register the pgvector codec.
When
with_facts=TrueaPostgresFactStorerooted at the same pool is attached asself.factsso the agent loop’s memory.facts integration just works.
- async init_schema() None[source]¶
Apply
schema_sql()against the connected pool.When a
PostgresFactStoreis attached asself.facts, its schema is initialised in the same call.
- async recall(query: str, *, kind: str = 'episodic', limit: int = 5, time_range: tuple[datetime.datetime, datetime.datetime] | None = None, user_id: str | None = None) list[jeevesagent.core.types.Episode][source]¶
- async recall_facts(query: str, *, limit: int = 5, valid_at: datetime.datetime | None = None, user_id: str | None = None) list[jeevesagent.core.types.Fact][source]¶
- async remember(episode: jeevesagent.core.types.Episode) str[source]¶
- schema_sql() list[str][source]¶
Return the DDL needed to bootstrap this backend’s schema.
Exposed so tests can assert on the SQL without running it; also usable from migration scripts that want to apply the schema in their own transaction.
- async session_messages(session_id: str, *, user_id: str | None = None, limit: int = 20) list[jeevesagent.core.types.Message][source]¶
- async working() list[jeevesagent.core.types.MemoryBlock][source]¶
- class jeevesagent.PostgresRuntime(pool: Any)[source]¶
Bases:
jeevesagent.runtime.journaled.JournaledRuntimeJournaledRuntimebacked by Postgres for cross-host durable replay.- classmethod connect(dsn: str, *, min_size: int = 1, max_size: int = 10) PostgresRuntime[source]¶
- Async:
Open a fresh asyncpg pool and return the runtime rooted at it.
- name = 'postgres'¶
- class jeevesagent.PostgresVectorStore(embedder: jeevesagent.core.protocols.Embedder, *, dsn: str, table: str = 'jeeves_vectors', dimension: int | None = None)[source]¶
Vector store backed by Postgres +
pgvector.- async add(chunks: list[jeevesagent.loader.base.Chunk], ids: list[str] | None = None) list[str][source]¶
- classmethod from_chunks(chunks: list[jeevesagent.loader.base.Chunk], *, embedder: jeevesagent.core.protocols.Embedder, ids: list[str] | None = None, dsn: str, table: str = 'jeeves_vectors', dimension: int | None = None) PostgresVectorStore[source]¶
- Async:
One-shot: construct a PostgresVectorStore + add
chunks.
- classmethod from_texts(texts: list[str], *, embedder: jeevesagent.core.protocols.Embedder, metadatas: list[dict[str, Any]] | None = None, ids: list[str] | None = None, dsn: str, table: str = 'jeeves_vectors', dimension: int | None = None) PostgresVectorStore[source]¶
- Async:
One-shot: construct a PostgresVectorStore from raw text strings (each becomes a
Chunkwith the matching metadata dict, or empty ifmetadatasis None).
- async search(query: str, *, k: int = 4, filter: collections.abc.Mapping[str, Any] | None = None, diversity: float | None = None) list[jeevesagent.vectorstore.base.SearchResult][source]¶
- async search_by_vector(vector: list[float], *, k: int = 4, filter: collections.abc.Mapping[str, Any] | None = None, diversity: float | None = None) list[jeevesagent.vectorstore.base.SearchResult][source]¶
- property embedder: jeevesagent.core.protocols.Embedder¶
- name = 'postgres'¶
- class jeevesagent.ReAct(*, max_turns: int | None = None)[source]¶
Observe-think-act in a tight loop.
The default architecture for every
Agent. Other architectures wrap or replace this strategy; seeSubagent.md.max_turnsoverridesDependencies.max_turnsfor this architecture only — useful when wrapping ReAct inside another architecture that sets its own per-leaf cap (Reflexion, Plan-and-Execute, etc.).Nonemeans “use whatever the Agent was configured with”.- declared_workers() dict[str, jeevesagent.agent.api.Agent][source]¶
- async run(session: jeevesagent.architecture.base.AgentSession, deps: jeevesagent.architecture.base.Dependencies, prompt: str) collections.abc.AsyncIterator[jeevesagent.core.types.Event][source]¶
- name = 'react'¶
- class jeevesagent.ReWOO(*, max_steps: int = 8, planner_prompt: str | None = None, solver_prompt: str | None = None, parallel_levels: bool = True)[source]¶
Plan-then-tool-execute with placeholder substitution.
- declared_workers() dict[str, jeevesagent.agent.api.Agent][source]¶
- async run(session: jeevesagent.architecture.base.AgentSession, deps: jeevesagent.architecture.base.Dependencies, prompt: str) collections.abc.AsyncIterator[jeevesagent.core.types.Event][source]¶
- name = 'rewoo'¶
- class jeevesagent.ReWOOPlan(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelA list of ReWOO steps (no required ordering — dependencies are inferred from
{{En}}placeholders).Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- class jeevesagent.ReWOOStep(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelOne step of a ReWOO plan: id + tool + args.
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- class jeevesagent.ReWOOStepResult(/, **data: Any)[source]¶
Bases:
pydantic.BaseModel- !!! abstract “Usage Documentation”
[Models](../concepts/models.md)
A base class for creating Pydantic models.
- __class_vars__¶
The names of the class variables defined on the model.
- __private_attributes__¶
Metadata about the private attributes of the model.
- __signature__¶
The synthesized __init__ [Signature][inspect.Signature] of the model.
- __pydantic_complete__¶
Whether model building is completed, or if there are still undefined fields.
- __pydantic_core_schema__¶
The core schema of the model.
- __pydantic_custom_init__¶
Whether the model has a custom __init__ function.
- __pydantic_decorators__¶
Metadata containing the decorators defined on the model. This replaces Model.__validators__ and Model.__root_validators__ from Pydantic V1.
- __pydantic_generic_metadata__¶
Metadata for generic models; contains data used for a similar purpose to __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.
- __pydantic_parent_namespace__¶
Parent namespace of the model, used for automatic rebuilding of models.
- __pydantic_post_init__¶
The name of the post-init method for the model, if defined.
- __pydantic_root_model__¶
Whether the model is a [RootModel][pydantic.root_model.RootModel].
- __pydantic_serializer__¶
The pydantic-core SchemaSerializer used to dump instances of the model.
- __pydantic_validator__¶
The pydantic-core SchemaValidator used to validate instances of the model.
- __pydantic_fields__¶
A dictionary of field names and their corresponding [FieldInfo][pydantic.fields.FieldInfo] objects.
- __pydantic_computed_fields__¶
A dictionary of computed field names and their corresponding [ComputedFieldInfo][pydantic.fields.ComputedFieldInfo] objects.
- __pydantic_extra__¶
A dictionary containing extra values, if [extra][pydantic.config.ConfigDict.extra] is set to ‘allow’.
- __pydantic_fields_set__¶
The names of fields explicitly set during instantiation.
- __pydantic_private__¶
Values of private attributes set on the model instance.
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- class jeevesagent.RedisFactStore(client: Any, *, embedder: jeevesagent.core.protocols.Embedder | None = None, key_prefix: str = DEFAULT_KEY_PREFIX)[source]¶
Bi-temporal fact store over plain Redis hashes.
- async all_facts() list[jeevesagent.core.types.Fact][source]¶
- async append(fact: jeevesagent.core.types.Fact) str[source]¶
- classmethod connect(url: str = 'redis://localhost:6379/0', *, embedder: jeevesagent.core.protocols.Embedder | None = None, key_prefix: str = DEFAULT_KEY_PREFIX) RedisFactStore[source]¶
- Async:
- async query(*, subject: str | None = None, predicate: str | None = None, object_: str | None = None, valid_at: datetime.datetime | None = None, limit: int = 10, user_id: str | None = None) list[jeevesagent.core.types.Fact][source]¶
- async recall_text(query: str, *, limit: int = 5, valid_at: datetime.datetime | None = None, user_id: str | None = None) list[jeevesagent.core.types.Fact][source]¶
- property embedder: jeevesagent.core.protocols.Embedder¶
- class jeevesagent.RedisMemory(client: Any, *, embedder: jeevesagent.core.protocols.Embedder | None = None, key_prefix: str = DEFAULT_KEY_PREFIX, index_name: str = DEFAULT_INDEX_NAME, use_vector_index: bool = True, fact_store: Any | None = None)[source]¶
Redis-backed
Memory. Useconnect()to construct.- classmethod connect(url: str = 'redis://localhost:6379/0', *, embedder: jeevesagent.core.protocols.Embedder | None = None, key_prefix: str = DEFAULT_KEY_PREFIX, index_name: str = DEFAULT_INDEX_NAME, use_vector_index: bool = True, with_facts: bool = False, fact_key_prefix: str = 'jeeves:fact:') RedisMemory[source]¶
- Async:
Open an async Redis connection.
with_facts=Trueattaches aRedisFactStoresharing the same client; facts go to{fact_key_prefix}*keys so they don’t collide with episode keys.
- async ensure_index() None[source]¶
Create the RediSearch HNSW index, if not already present.
Skipped silently when
use_vector_index=Falseor when RediSearch isn’t available on the server.
- async recall(query: str, *, kind: str = 'episodic', limit: int = 5, time_range: tuple[datetime.datetime, datetime.datetime] | None = None, user_id: str | None = None) list[jeevesagent.core.types.Episode][source]¶
- async recall_facts(query: str, *, limit: int = 5, valid_at: datetime.datetime | None = None, user_id: str | None = None) list[jeevesagent.core.types.Fact][source]¶
- async remember(episode: jeevesagent.core.types.Episode) str[source]¶
- async session_messages(session_id: str, *, user_id: str | None = None, limit: int = 20) list[jeevesagent.core.types.Message][source]¶
- async working() list[jeevesagent.core.types.MemoryBlock][source]¶
- class jeevesagent.Reflexion(*, base: jeevesagent.architecture.base.Architecture | None = None, max_attempts: int = 3, threshold: float = 0.8, evaluator_prompt: str | None = None, reflector_prompt: str | None = None, lessons_block_name: str = 'reflexion_lessons', lesson_store: jeevesagent.vectorstore.base.VectorStore | None = None, top_k_lessons: int = 5)[source]¶
Wrap a base architecture with evaluator + reflector + lesson memory.
See module docstring for the full mechanism. Constructor parameters:
base— architecture to retry. DefaultReAct.max_attempts— cap on retries within a single run. Default 3.threshold— minimum evaluator score to terminate as success. Default 0.8.evaluator_prompt/reflector_prompt— override the default system prompts.lessons_block_name— memory working-block name for persisted lessons. Default"reflexion_lessons". Multiple Reflexion-wrapped agents in the same memory should pick distinct names.lesson_store— optionalVectorStoreenabling selective recall. When set, lessons are stored as embedded chunks and only the top-top_k_lessonsmost relevant lessons are surfaced on each attempt (instead of all past lessons). Avoids context bloat as lessons accumulate.top_k_lessons— how many lessons to recall per attempt (selective-recall mode only). Default 5.
- declared_workers() dict[str, jeevesagent.agent.api.Agent][source]¶
- async run(session: jeevesagent.architecture.base.AgentSession, deps: jeevesagent.architecture.base.Dependencies, prompt: str) collections.abc.AsyncIterator[jeevesagent.core.types.Event][source]¶
- name = 'reflexion'¶
- class jeevesagent.RetryPolicy[source]¶
Exponential-backoff-with-jitter retry schedule.
The default is sensible for production: up to 3 attempts (one initial + two retries), starting at 1 s, doubling each attempt, capped at 30 s, with ±10% jitter so synchronised clients don’t reform a thundering herd.
Examples:
# default — sensible for most apps RetryPolicy() # disable retries (fail fast) RetryPolicy.disabled() # aggressive — survives long provider blips RetryPolicy.aggressive() # tuned to a specific SLO RetryPolicy(max_attempts=4, initial_delay_s=0.5, max_delay_s=15)
The schedule applies between attempts: the first call has no delay, the second is delayed by
initial_delay_s(± jitter), the third byinitial_delay_s * multiplier(± jitter), etc., each capped atmax_delay_s. Provider-suppliedRetry-Afterhints (carried onretry_after) override the computed delay when they ask for more time — we never sleep less than the provider asked for.- classmethod aggressive() RetryPolicy[source]¶
Up to 6 attempts, faster initial backoff, longer cap. Use when the underlying provider is known-flaky and the caller prefers slow success over fast failure.
- classmethod disabled() RetryPolicy[source]¶
Single attempt, no retries — fail fast on any error.
- initial_delay_s: float = 1.0¶
Backoff before the FIRST retry (i.e. between attempts 1 and 2). Subsequent retries use
initial_delay_s * multiplier**n.
- jitter: float = 0.1¶
Fractional ±jitter applied to each computed delay.
0.1= ±10%. Set to0for deterministic backoff (useful in tests).
- max_attempts: int = 3¶
Maximum total attempts including the first call.
1means no retries; the call either succeeds or raises immediately. The minimum-meaningful retry policy is thereforemax_attempts=2.
- class jeevesagent.Role[source]¶
Bases:
enum.StrEnumEnum where members are also (and must be) strings
Initialize self. See help(type(self)) for accurate signature.
- ASSISTANT = 'assistant'¶
- SYSTEM = 'system'¶
- TOOL = 'tool'¶
- USER = 'user'¶
- class jeevesagent.Router(*, routes: list[RouterRoute], fallback_route: str | None = None, require_confidence_above: float = 0.0, classifier_prompt: str | None = None)[source]¶
Classify input → dispatch to ONE specialist
Agent.- declared_workers() dict[str, jeevesagent.agent.api.Agent][source]¶
- async run(session: jeevesagent.architecture.base.AgentSession, deps: jeevesagent.architecture.base.Dependencies, prompt: str) collections.abc.AsyncIterator[jeevesagent.core.types.Event][source]¶
- name = 'router'¶
- class jeevesagent.RouterRoute[source]¶
One specialist + classification metadata.
nameis what the classifier emits in itsroute:line and must be unique within a Router.descriptionis shown to the classifier alongside the name — keep it specific and distinguishing so the classifier picks reliably.- agent: jeevesagent.agent.api.Agent¶
- class jeevesagent.RunContext[source]¶
Typed, immutable context for one agent run.
Set once at the start of
Agent.run()and propagated to every architecture, tool, hook, sub-agent, and memory operation via acontextvars.ContextVar. The framework treatsuser_idandsession_idas first-class fields (typed, namespaced);metadatais an opaque bag for app-specific keys the framework does not interpret.Construct one directly when you need to spawn work outside an active run with explicit scope:
ctx = RunContext(user_id="alice", session_id="conv_42") async with set_run_context(ctx): await my_tool(...)
Inside an agent run, prefer
get_run_context()over constructing a new one — that gives you the live context the framework set up.- with_overrides(*, user_id: str | None | _Sentinel = _Sentinel.UNSET, session_id: str | None | _Sentinel = _Sentinel.UNSET, run_id: str | _Sentinel = _Sentinel.UNSET, metadata: collections.abc.Mapping[str, Any] | _Sentinel = _Sentinel.UNSET) RunContext[source]¶
Return a new context with selected fields replaced.
Used by multi-agent architectures when spawning sub-agents that need to inherit most of the parent’s context but with a derived
session_idor augmentedmetadata. The sentinel makes “leave this field unchanged” distinguishable from “explicitly set this field toNone”.
- metadata: collections.abc.Mapping[str, Any]¶
Free-form application context. Use this for keys the framework does not need to understand — locale, request id, feature flags, tenant id beyond
user_id, etc. Read inside tools / hooks viaget_run_context().metadata.
- run_id: str = ''¶
Unique identifier for this single
Agent.run()invocation. Distinct fromsession_id(which identifies a conversation that may span many runs). Auto-set byAgent.run(); an explicit value passed in by the caller is overridden.
- session_id: str | None = None¶
Conversation thread identifier. Reusing the same
session_idacross calls signals “continue this conversation” — the framework will rehydrate prior session messages so the model sees real chat history, not just memory recall.Nonemeans “fresh conversation”; the framework auto-generates one insideAgent.run()if not supplied.
- user_id: str | None = None¶
Namespace for memory recall + persistence.
Noneis the “anonymous / single-tenant” bucket; episodes / facts stored withuser_id=Nonenever see episodes / facts stored with a non-Noneuser_idand vice versa. The framework treats this as a hard partition key, not a soft filter.
- class jeevesagent.RunResult(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelFinal outcome of an
Agent.runcall.outputis always the raw assistant text (the JSON itself when a structured-output schema was supplied).parsedis the validated Pydantic instance — populated only when the caller passedoutput_schema=toAgent.run(). Use whichever fits the call site:# free-form text run result = await agent.run("summarise this PDF") print(result.output) # structured-output run result = await agent.run(prompt, output_schema=Invoice) invoice: Invoice = result.parsed # typed, validated
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- property duration: datetime.timedelta¶
Wall-clock latency between
started_atandfinished_at.
- finished_at: datetime.datetime¶
- model_config¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- parsed: Any | None = None¶
The validated Pydantic instance when
output_schema=was supplied toAgent.run();Noneotherwise. Typed asAnyto keep the runtime type free; the call site has the schema and can cast or annotate as needed.
- started_at: datetime.datetime¶
- class jeevesagent.Runtime[source]¶
Bases:
ProtocolDurable execution. Wraps every side effect in a journal entry.
- session(session_id: str) contextlib.AbstractAsyncContextManager[RuntimeSession][source]¶
Open or resume a durable session.
- async signal(session_id: str, name: str, payload: Any) None[source]¶
Send an external signal (e.g., human approval) to a session.
- async step(name: str, fn: collections.abc.Callable[Ellipsis, collections.abc.Awaitable[Any]], *args: Any, idempotency_key: str | None = None, **kwargs: Any) Any[source]¶
Execute
fnas a journaled step. Replays cached on resume.
- stream_step(name: str, fn: collections.abc.Callable[Ellipsis, collections.abc.AsyncIterator[Any]], *args: Any, **kwargs: Any) collections.abc.AsyncIterator[Any][source]¶
Execute a streaming step. Replays the aggregate on resume.
- class jeevesagent.RuntimeSession[source]¶
Bases:
ProtocolHandle to an open durable session held by a
Runtime.
- class jeevesagent.Sandbox[source]¶
Bases:
ProtocolIsolation layer for tool execution.
- async execute(tool: jeevesagent.core.types.ToolDef, args: collections.abc.Mapping[str, Any]) jeevesagent.core.types.ToolResult[source]¶
- with_filesystem(root: str) contextlib.AbstractAsyncContextManager[None][source]¶
Temporary filesystem sandbox for the duration of the context.
- class jeevesagent.ScriptedModel(turns: list[ScriptedTurn])[source]¶
Model that emits canned responses, one per call to
stream().- async complete(messages: list[jeevesagent.core.types.Message], *, tools: list[jeevesagent.core.types.ToolDef] | None = None, temperature: float = 1.0, max_tokens: int | None = None) tuple[str, list[jeevesagent.core.types.ToolCall], jeevesagent.core.types.Usage, str][source]¶
Single-shot replay of the next scripted turn.
Mirrors
stream()but returns the turn’s text + tool_calls + usage in one tuple. Used by the non-streaming hot path (agent.run());agent.stream()keeps usingstream()for per-chunk replay.
- async stream(messages: list[jeevesagent.core.types.Message], *, tools: list[jeevesagent.core.types.ToolDef] | None = None, temperature: float = 1.0, max_tokens: int | None = None) collections.abc.AsyncIterator[jeevesagent.core.types.ModelChunk][source]¶
- class jeevesagent.ScriptedTurn[source]¶
-
- tool_calls: list[jeevesagent.core.types.ToolCall] = []¶
- class jeevesagent.SearchResult[source]¶
One hit from
VectorStore.search().chunk— the matched chunk (with its full metadata).score— similarity in [-1, 1] for cosine; backend- specific for other distance metrics. Higher = more similar.id— the store-assigned id (so callers candelete()orget_by_ids()later).
- class jeevesagent.SelfRefine(*, base: jeevesagent.architecture.base.Architecture | None = None, max_rounds: int = 3, critic_prompt: str | None = None, refiner_prompt: str | None = None, stop_phrase: str = 'no issues')[source]¶
Wrap a base architecture with iterative critique / refine.
basedefaults toReAct; the round-0 generator runs the base architecture’s full strategy. Subsequent rounds are text-only model calls — no tools, just critique and rewrite.- declared_workers() dict[str, jeevesagent.agent.api.Agent][source]¶
- async run(session: jeevesagent.architecture.base.AgentSession, deps: jeevesagent.architecture.base.Dependencies, prompt: str) collections.abc.AsyncIterator[jeevesagent.core.types.Event][source]¶
- name = 'self-refine'¶
- class jeevesagent.Skill(path: str | pathlib.Path, *, source_label: str | None = None)[source]¶
A loadable agent skill.
- classmethod from_text(text: str, *, source_label: str | None = None) Skill[source]¶
Build an inline skill from a SKILL.md-formatted string.
No filesystem path; bundled scripts and
tools.pyaren’t accessible. Useful for one-off skill definitions in code.
- list_files() list[pathlib.Path][source]¶
Enumerate every file bundled with this skill.
- metadata¶
- path¶
- property pending_tools: list[jeevesagent.tools.registry.Tool]¶
The Tool instances this skill will register on load.
Both Mode B (Python @tool from
tools.py) and Mode C (subprocess wrappers from frontmattertools:manifest) contribute to this list. Empty for pure markdown skills.
- class jeevesagent.SkillMetadata[source]¶
Lightweight skill descriptor — what loads at startup.
The body is NOT in here; it’s read on demand via
Skill.load_body(). Keep this small — it lives in the system prompt for the entire agent’s lifetime.
- class jeevesagent.SkillRegistry(items: collections.abc.Iterable[SkillSpec] | None = None)[source]¶
A keyed collection of
Skillinstances.- add(skill: jeevesagent.skills.skill.Skill) None[source]¶
Append (or override) a single skill after construction.
- catalog_section() str[source]¶
The markdown bullet list that gets appended to the agent’s system prompt.
Empty registry → empty string (so the constructor can unconditionally call this without polluting the system prompt with a blank “Available skills” header).
- get(name: str) jeevesagent.skills.skill.Skill | None[source]¶
- load(name: str) str[source]¶
Return the full body of a skill (the load_skill tool’s result). Raises
SkillErrorfor unknown names so the model gets a clear error in the tool result.Does NOT register pending Tools. For the full load-and- register flow, see
load_with_tools().
- load_with_tools(name: str) tuple[str, list[jeevesagent.tools.registry.Tool]][source]¶
Return
(body, newly_pending_tools)— the body of the skill plus the Tool instances the framework should register with the agent’s tool host on this load.Idempotent: subsequent calls for the same skill return the body and an empty tool list, since registration only needs to happen once.
- metadata_map() collections.abc.Mapping[str, jeevesagent.skills.skill.SkillMetadata][source]¶
All currently-registered skills’ metadata, keyed by name. Cheap to compute — used to build the catalog section.
- remove(name: str) jeevesagent.skills.skill.Skill | None[source]¶
Drop a skill by name. Returns the removed instance or
Noneif no such skill was registered.
- class jeevesagent.SkillSource[source]¶
A folder of skills + an optional label.
- classmethod coerce(item: SkillSource | str | pathlib.Path | tuple[str | pathlib.Path, str]) SkillSource[source]¶
Normalize one user-supplied source spec.
Accepts: *
SkillSource(...)— used as-is *str/Path— bare path, no label *(path, label)— path with explicit label
- discover() list[jeevesagent.skills.skill.Skill][source]¶
Find every SKILL.md under this source directory.
Recurses one level (most common layout:
skills/<name>/SKILL.md) but also handles deeper nesting. Each SKILL.md becomes oneSkillinstance with this source’s label attached.
- path: pathlib.Path¶
- class jeevesagent.Span(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelA trace span handle. Concrete telemetry adapters return their own representation; this is the value-object contract for in-process use.
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- started_at: datetime.datetime = None¶
- class jeevesagent.SqliteFactStore(path: str | pathlib.Path, *, embedder: jeevesagent.core.protocols.Embedder | None = None)[source]¶
Durable bi-temporal fact store rooted at a sqlite file.
- async all_facts() list[jeevesagent.core.types.Fact][source]¶
- async append(fact: jeevesagent.core.types.Fact) str[source]¶
Append a fact, invalidating any superseded predecessors.
Same supersession rule as
InMemoryFactStore: if there’s an existing currently-valid fact with matching subject + predicate but different object, set itsvalid_untilto the new fact’svalid_from.
- async query(*, subject: str | None = None, predicate: str | None = None, object_: str | None = None, valid_at: datetime.datetime | None = None, limit: int = 10, user_id: str | None = None) list[jeevesagent.core.types.Fact][source]¶
- async recall_text(query: str, *, limit: int = 5, valid_at: datetime.datetime | None = None, user_id: str | None = None) list[jeevesagent.core.types.Fact][source]¶
- property embedder: jeevesagent.core.protocols.Embedder | None¶
- property path: pathlib.Path¶
- class jeevesagent.SqliteJournalStore(path: str | pathlib.Path)[source]¶
SQLite-backed journal. Durable across process restarts.
- property path: pathlib.Path¶
- class jeevesagent.SqliteRuntime(path: str | pathlib.Path)[source]¶
Bases:
jeevesagent.runtime.journaled.JournaledRuntimeJournaledRuntimewith aSqliteJournalStore.- name = 'sqlite'¶
- property path: pathlib.Path¶
- class jeevesagent.StandardPermissions(*, mode: Mode = Mode.DEFAULT, allowed_tools: list[str] | None = None, denied_tools: list[str] | None = None)[source]¶
Mode + allow/deny-list permission policy.
- async check(call: jeevesagent.core.types.ToolCall, *, context: collections.abc.Mapping[str, Any]) jeevesagent.core.types.PermissionDecision[source]¶
- classmethod strict() StandardPermissions[source]¶
Convenience: default-mode permissions with no overrides.
- class jeevesagent.StepResult(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelThe output of executing one step.
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- class jeevesagent.SubprocessSandbox(inner: jeevesagent.core.protocols.ToolHost, *, timeout_seconds: float = 30.0)[source]¶
Run each tool call in a fresh child Python process.
- async call(tool: str, args: collections.abc.Mapping[str, Any], *, call_id: str = '') jeevesagent.core.types.ToolResult[source]¶
- property inner: jeevesagent.core.protocols.ToolHost¶
- class jeevesagent.Supervisor(*, workers: dict[str, jeevesagent.agent.api.Agent], base: jeevesagent.architecture.base.Architecture | None = None, instructions_template: str | None = None, delegate_tool_name: str = 'delegate', forward_tool_name: str = 'forward_message')[source]¶
Coordinator + workers, glued by a
delegatetool.The supervisor’s base architecture (default
ReAct) sees a freshdelegate(worker, instructions)tool that routes calls to the named workerAgent. Worker outputs come back as tool results just like any other tool call.Constructor¶
workers: dict mapping role-names to fully-builtAgentinstances. Names must be valid identifiers (the model emits them as theworkerargument).base: the architecture the supervisor itself runs. DefaultReAct. Wrap insideReflexionto learn delegation patterns across runs.instructions_template: format string with{worker_descriptions}. Default teaches the supervisor to delegate effectively. The agent’s owninstructionsare prepended (so domain context survives).delegate_tool_name: defaults to"delegate". Customize to avoid clashes with user-defined tools that happen to have the same name.forward_tool_name: defaults to"forward_message". The supervisor calls this with a worker name to return that worker’s last output VERBATIM as the supervisor’s final response. Skips a synthesis round-trip — the `langchain.com/blog/benchmarking-multi-agent-architectures`_ benchmark showed +50% quality on tasks where the supervisor would otherwise paraphrase a worker’s output.
- add_worker(name: str, agent: jeevesagent.agent.api.Agent) None[source]¶
Register a worker between runs.
Safe to call between
Agent.run()invocations on the agent that owns this supervisor; the new worker becomes available fordelegate(name, ...)on the next run. Calling mid-run is undefined — the supervisor’s prompt is composed at run start.
- declared_workers() dict[str, jeevesagent.agent.api.Agent][source]¶
- remove_worker(name: str) jeevesagent.agent.api.Agent | None[source]¶
Unregister a worker by name. Returns the removed Agent if it was registered,
Noneotherwise. Same lifecycle rules asadd_worker().
- async run(session: jeevesagent.architecture.base.AgentSession, deps: jeevesagent.architecture.base.Dependencies, prompt: str) collections.abc.AsyncIterator[jeevesagent.core.types.Event][source]¶
- name = 'supervisor'¶
- class jeevesagent.Swarm(*, agents: dict[str, jeevesagent.agent.api.Agent | Handoff], entry_agent: str, max_handoffs: int = 8, detect_cycles: bool = True, pass_full_history: bool = True, handoff_tool_name: str = 'handoff')[source]¶
Peer agents passing control through handoff tools.
- declared_workers() dict[str, jeevesagent.agent.api.Agent][source]¶
- async run(session: jeevesagent.architecture.base.AgentSession, deps: jeevesagent.architecture.base.Dependencies, prompt: str) collections.abc.AsyncIterator[jeevesagent.core.types.Event][source]¶
- name = 'swarm'¶
- class jeevesagent.Team[source]¶
Namespace for multi-agent team builders.
Every classmethod returns a fully-built
Agentwhose architecture is the corresponding multi-agent strategy. The returned Agent has the standardrun/stream/ etc. interface — call sites don’t change between single-agent and team agents.- static actor_critic(actor: jeevesagent.agent.api.Agent, critic: jeevesagent.agent.api.Agent, *, instructions: str = '', model: jeevesagent.core.protocols.Model | str | None = None, memory: jeevesagent.core.protocols.Memory | None = None, runtime: jeevesagent.core.protocols.Runtime | None = None, budget: jeevesagent.core.protocols.Budget | None = None, permissions: jeevesagent.core.protocols.Permissions | None = None, hooks: jeevesagent.security.hooks.HookRegistry | None = None, tools: list[jeevesagent.tools.registry.Tool | collections.abc.Callable[Ellipsis, object]] | jeevesagent.core.protocols.ToolHost | jeevesagent.tools.registry.Tool | collections.abc.Callable[Ellipsis, object] | None = None, telemetry: jeevesagent.core.protocols.Telemetry | None = None, audit_log: jeevesagent.security.audit.AuditLog | None = None, max_turns: int = DEFAULT_MAX_TURNS, auto_consolidate: bool = False, skills: list[Any] | None = None, max_rounds: int = 3, approval_threshold: float = 0.9, critique_template: str | None = None, refine_template: str | None = None) jeevesagent.agent.api.Agent[source]¶
Build an actor-critic pair where the critic reviews the actor’s output (with structured JSON scoring + rubric) and the actor refines below
approval_threshold.
- static blackboard(agents: dict[str, jeevesagent.agent.api.Agent], *, coordinator: jeevesagent.agent.api.Agent | None = None, decider: jeevesagent.agent.api.Agent | None = None, instructions: str = '', model: jeevesagent.core.protocols.Model | str | None = None, memory: jeevesagent.core.protocols.Memory | None = None, runtime: jeevesagent.core.protocols.Runtime | None = None, budget: jeevesagent.core.protocols.Budget | None = None, permissions: jeevesagent.core.protocols.Permissions | None = None, hooks: jeevesagent.security.hooks.HookRegistry | None = None, tools: list[jeevesagent.tools.registry.Tool | collections.abc.Callable[Ellipsis, object]] | jeevesagent.core.protocols.ToolHost | jeevesagent.tools.registry.Tool | collections.abc.Callable[Ellipsis, object] | None = None, telemetry: jeevesagent.core.protocols.Telemetry | None = None, audit_log: jeevesagent.security.audit.AuditLog | None = None, max_turns: int = DEFAULT_MAX_TURNS, auto_consolidate: bool = False, skills: list[Any] | None = None, max_rounds: int = 10, coordinator_instructions: str | None = None, decider_instructions: str | None = None) jeevesagent.agent.api.Agent[source]¶
Build a blackboard team where
agentscollaborate via a shared workspace; an optionalcoordinatorselects who acts each round and an optionaldeciderdecides when the work is done.
- static debate(debaters: list[jeevesagent.agent.api.Agent], *, judge: jeevesagent.agent.api.Agent | None = None, instructions: str = '', model: jeevesagent.core.protocols.Model | str | None = None, memory: jeevesagent.core.protocols.Memory | None = None, runtime: jeevesagent.core.protocols.Runtime | None = None, budget: jeevesagent.core.protocols.Budget | None = None, permissions: jeevesagent.core.protocols.Permissions | None = None, hooks: jeevesagent.security.hooks.HookRegistry | None = None, tools: list[jeevesagent.tools.registry.Tool | collections.abc.Callable[Ellipsis, object]] | jeevesagent.core.protocols.ToolHost | jeevesagent.tools.registry.Tool | collections.abc.Callable[Ellipsis, object] | None = None, telemetry: jeevesagent.core.protocols.Telemetry | None = None, audit_log: jeevesagent.security.audit.AuditLog | None = None, max_turns: int = DEFAULT_MAX_TURNS, auto_consolidate: bool = False, skills: list[Any] | None = None, rounds: int = 2, convergence_check: bool = True, convergence_similarity: float = 0.85, debater_instructions: str | None = None, judge_instructions: str | None = None) jeevesagent.agent.api.Agent[source]¶
Build a multi-agent debate where
debatersargue forrounds(with optional convergence early-exit). Ifjudgeis provided, the judge synthesizes a final answer; otherwise majority vote wins.
- static router(routes: list[jeevesagent.architecture.RouterRoute], *, instructions: str = '', model: jeevesagent.core.protocols.Model | str | None = None, memory: jeevesagent.core.protocols.Memory | None = None, runtime: jeevesagent.core.protocols.Runtime | None = None, budget: jeevesagent.core.protocols.Budget | None = None, permissions: jeevesagent.core.protocols.Permissions | None = None, hooks: jeevesagent.security.hooks.HookRegistry | None = None, tools: list[jeevesagent.tools.registry.Tool | collections.abc.Callable[Ellipsis, object]] | jeevesagent.core.protocols.ToolHost | jeevesagent.tools.registry.Tool | collections.abc.Callable[Ellipsis, object] | None = None, telemetry: jeevesagent.core.protocols.Telemetry | None = None, audit_log: jeevesagent.security.audit.AuditLog | None = None, max_turns: int = DEFAULT_MAX_TURNS, auto_consolidate: bool = False, skills: list[Any] | None = None, fallback_route: str | None = None, require_confidence_above: float = 0.0, classifier_prompt: str | None = None) jeevesagent.agent.api.Agent[source]¶
Build a router that classifies once and dispatches to ONE specialist
Agent. Cheaper than Supervisor for tasks with clear specialist boundaries (one classifier call + one specialist run, no synthesis pass).
- static supervisor(workers: dict[str, jeevesagent.agent.api.Agent], *, instructions: str = '', model: jeevesagent.core.protocols.Model | str | None = None, memory: jeevesagent.core.protocols.Memory | None = None, runtime: jeevesagent.core.protocols.Runtime | None = None, budget: jeevesagent.core.protocols.Budget | None = None, permissions: jeevesagent.core.protocols.Permissions | None = None, hooks: jeevesagent.security.hooks.HookRegistry | None = None, tools: list[jeevesagent.tools.registry.Tool | collections.abc.Callable[Ellipsis, object]] | jeevesagent.core.protocols.ToolHost | jeevesagent.tools.registry.Tool | collections.abc.Callable[Ellipsis, object] | None = None, telemetry: jeevesagent.core.protocols.Telemetry | None = None, audit_log: jeevesagent.security.audit.AuditLog | None = None, max_turns: int = DEFAULT_MAX_TURNS, auto_consolidate: bool = False, skills: list[Any] | None = None, instructions_template: str | None = None, delegate_tool_name: str = 'delegate', forward_tool_name: str = 'forward_message') jeevesagent.agent.api.Agent[source]¶
Build a coordinator Agent that delegates to
workers.The coordinator can call
delegate(worker, instructions)to dispatch a subtask, orforward_message(worker)to return a worker’s output verbatim. Multiple delegations in one turn run in parallel.
- static swarm(agents: dict[str, jeevesagent.agent.api.Agent | jeevesagent.architecture.swarm.Handoff], entry_agent: str, *, instructions: str = '', model: jeevesagent.core.protocols.Model | str | None = None, memory: jeevesagent.core.protocols.Memory | None = None, runtime: jeevesagent.core.protocols.Runtime | None = None, budget: jeevesagent.core.protocols.Budget | None = None, permissions: jeevesagent.core.protocols.Permissions | None = None, hooks: jeevesagent.security.hooks.HookRegistry | None = None, tools: list[jeevesagent.tools.registry.Tool | collections.abc.Callable[Ellipsis, object]] | jeevesagent.core.protocols.ToolHost | jeevesagent.tools.registry.Tool | collections.abc.Callable[Ellipsis, object] | None = None, telemetry: jeevesagent.core.protocols.Telemetry | None = None, audit_log: jeevesagent.security.audit.AuditLog | None = None, max_turns: int = DEFAULT_MAX_TURNS, auto_consolidate: bool = False, skills: list[Any] | None = None, max_handoffs: int = 8, detect_cycles: bool = True, pass_full_history: bool = True, handoff_tool_name: str = 'handoff') jeevesagent.agent.api.Agent[source]¶
Build a peer-swarm of agents that hand off control via a
handofftool (or per-targettransfer_to_<name>tools when peers are wrapped inHandoffwith aninput_type).entry_agentis the peer that receives the first message.
- class jeevesagent.Telemetry[source]¶
Bases:
ProtocolOpenTelemetry-compatible tracing/metrics surface.
- trace(name: str, **attrs: Any) contextlib.AbstractAsyncContextManager[jeevesagent.core.types.Span][source]¶
- class jeevesagent.ThoughtNode(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelOne node in the Tree-of-Thoughts search tree.
Children are stored implicitly (each node has a
parent_id). The full tree is reconstructable from the node list ToT keeps in its session metadata.Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- class jeevesagent.Tool[source]¶
A registered tool: definition plus the callable that executes it.
- async execute(args: collections.abc.Mapping[str, Any]) Any[source]¶
Invoke the underlying callable.
Async functions are awaited; sync functions are dispatched to a worker thread via
anyio.to_thread.run_sync()so they don’t block the event loop.
- fn: collections.abc.Callable[Ellipsis, Any]¶
- class jeevesagent.ToolCall(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelA model-emitted request to invoke a tool.
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- class jeevesagent.ToolDef(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelSchema description of a tool the model can call.
Mirrors the JSON-Schema-flavored shape used across MCP and provider APIs.
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- model_config¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class jeevesagent.ToolEvent(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelTool registry change notification (MCP listChanged etc.).
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- at: datetime.datetime = None¶
- kind: Literal['added', 'removed', 'updated']¶
- class jeevesagent.ToolHost[source]¶
Bases:
ProtocolMCP-aware tool registry. Lazy-loads schemas on demand.
- async call(tool: str, args: collections.abc.Mapping[str, Any], *, call_id: str = '') jeevesagent.core.types.ToolResult[source]¶
Invoke
toolwithargs. Thecall_idis propagated into the returnedToolResultso the loop can correlate results with the originating model-emitted call.
- watch() collections.abc.AsyncIterator[jeevesagent.core.types.ToolEvent][source]¶
Notifications when the tool list changes (MCP listChanged).
- class jeevesagent.ToolResult(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelOutcome of a tool invocation.
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- classmethod denied_(call_id: str, reason: str, **kwargs: Any) ToolResult[source]¶
- classmethod error_(call_id: str, message: str, **kwargs: Any) ToolResult[source]¶
- classmethod success(call_id: str, output: Any, **kwargs: Any) ToolResult[source]¶
- output: Any = None¶
- started_at: datetime.datetime = None¶
- class jeevesagent.TreeOfThoughts(*, branch_factor: int = 3, max_depth: int = 3, beam_width: int = 2, solved_threshold: float = 1.0, min_score: float = 0.0, parallel: bool = True, proposer_prompt: str | None = None, evaluator_prompt: str | None = None)[source]¶
Branch + evaluate + prune. BFS beam search over thoughts.
- declared_workers() dict[str, jeevesagent.agent.api.Agent][source]¶
- async run(session: jeevesagent.architecture.base.AgentSession, deps: jeevesagent.architecture.base.Dependencies, prompt: str) collections.abc.AsyncIterator[jeevesagent.core.types.Event][source]¶
- name = 'tree-of-thoughts'¶
- class jeevesagent.Usage(/, **data: Any)[source]¶
Bases:
pydantic.BaseModelToken and cost accounting for a model call.
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
- model_config¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class jeevesagent.VectorMemory(*, embedder: jeevesagent.core.protocols.Embedder | None = None, max_episodes: int | None = None, consolidator: jeevesagent.memory.consolidator.Consolidator | None = None, fact_store: jeevesagent.memory.facts.FactStore | None = None)[source]¶
Pure-Python embedding-backed
Memory.- async consolidate() None[source]¶
Process unconsolidated episodes through the configured
Consolidator, appending facts toself.facts.No-op when no consolidator is configured.
- async recall(query: str, *, kind: str = 'episodic', limit: int = 5, time_range: tuple[datetime.datetime, datetime.datetime] | None = None, user_id: str | None = None) list[jeevesagent.core.types.Episode][source]¶
- async recall_facts(query: str, *, limit: int = 5, valid_at: datetime.datetime | None = None, user_id: str | None = None) list[jeevesagent.core.types.Fact][source]¶
- async remember(episode: jeevesagent.core.types.Episode) str[source]¶
- async session_messages(session_id: str, *, user_id: str | None = None, limit: int = 20) list[jeevesagent.core.types.Message][source]¶
- async working() list[jeevesagent.core.types.MemoryBlock][source]¶
- property embedder: jeevesagent.core.protocols.Embedder¶
- class jeevesagent.VectorStore[source]¶
Bases:
ProtocolAsync protocol for vector stores.
Six methods cover the lifecycle: add (embed + store), delete, search (by query string), search_by_vector (precomputed), count, get_by_ids.
Backends that aren’t natively async (FAISS, Chroma) wrap their sync calls in
anyio.to_thread.run_sync()so they don’t block the event loop.- async add(chunks: list[jeevesagent.loader.base.Chunk], ids: list[str] | None = None) list[str][source]¶
Embed + store
chunks. Returns the assigned ids (caller-provided or generated).
- async delete(ids: list[str]) None[source]¶
Remove the named chunks. Unknown ids are silently skipped (idempotent).
- async get_by_ids(ids: list[str]) list[jeevesagent.loader.base.Chunk][source]¶
Fetch chunks by id, in the same order as
ids. Unknown ids are skipped (the result may be shorter than the input).
- async search(query: str, *, k: int = 4, filter: collections.abc.Mapping[str, Any] | None = None, diversity: float | None = None) list[SearchResult][source]¶
Embed
queryand return the top-kchunks ranked by similarity.filter(optional) restricts candidates by metadata.diversity(optional, 0..1) enables MMR reranking for varied results.
- class jeevesagent.VoyageEmbedder(model: str = 'voyage-3', *, client: Any | None = None, api_key: str | None = None, input_type: str = 'document')[source]¶
Embeddings via Voyage AI’s
voyageaiSDK.Models and dimensions:
voyage-3/voyage-3-large/voyage-code-3-> 1024voyage-3-lite-> 512
input_typecontrols how Voyage encodes the text:"document"(default) — for corpus / fact-store entries"query"— for retrieval queries
Pass an explicit
input_type=if your embedder is dedicated to one role; for the agent loop’s mixed use (we embed both stored triples and recall queries through the same embedder), the"document"default is the safer choice.
- class jeevesagent.set_run_context(context: RunContext)[source]¶
Context manager that installs a
RunContextfor the duration of anasync withblock.The framework uses this internally inside
Agent.run()to expose the live context to tools and hooks. Application code rarely needs it, but it is the supported way to invoke a tool outside an agent loop with explicit scope — for example in background workers that share tool implementations with the agent:async with set_run_context(RunContext(user_id="alice")): await some_tool(...)
Behaves correctly under structured concurrency: nested
async withblocks restore the prior context on exit, andanyiotask-group spawns inherit the active context automatically.
- jeevesagent.bash_tool(workdir: pathlib.Path | str | None = None, *, name: str = 'bash', timeout: float = 30.0, allow_pattern: collections.abc.Callable[[str], bool] | None = None, extra_env: dict[str, str] | None = None) jeevesagent.tools.registry.Tool[source]¶
Build a
Toolthat runs a shell command with the workdir as the current working directory.Default safety:
Commands matching the built-in destructive patterns (
rm -rf /,sudo,mkfs, fork bombs, …) are rejected before being executed.Commands run with a default
timeoutof 30 seconds; the subprocess is killed on timeout.The shell is invoked via
/bin/sh -c <command>, so pipelines + redirections work the way you’d expect.
Knobs:
allow_pattern— a callable that takes the command string and returns True if the command should run. When provided, it OVERRIDES the default deny list — you take full responsibility.extra_env— extra environment variables merged into the subprocess env.timeout— seconds before the command is killed.
workdiris optional;Noneuses the framework’s default tempdir (shared with the other built-in tools).
- async jeevesagent.build_graph(agent: jeevesagent.agent.api.Agent, *, title: str = 'Agent') AgentGraph[source]¶
Walk an
Agentand return its renderableAgentGraph.
- jeevesagent.classify_model_error(exc: BaseException) jeevesagent.core.errors.ModelError | None[source]¶
Map an exception from any model SDK to the framework’s taxonomy.
Returns
Nonewhen the exception is not recognised as a model-call failure — let callers decide whether to wrap it in something else or propagate. Returns an instance of one ofTransientModelError/RateLimitError/AuthenticationError/InvalidRequestError/ContentFilterError/PermanentModelErrorotherwise.SDK imports are lazy — having e.g. the
anthropicpackage installed is not required for OpenAI classification to work, and vice versa.
- jeevesagent.default_workdir() pathlib.Path[source]¶
Return the framework’s default workdir for built-in tools, creating it lazily on first call.
The directory is a fresh tempdir under
$TMPDIR/jeeves_agent_*, created once per process. All built-in tool factories share it when called without an explicitworkdirargument, so an Agent that registersread_tool()andwrite_tool()(no args) sees the same place.The directory is NOT auto-cleaned at process exit — leave that to the OS’s tempdir cleanup so debug data survives a crash.
- jeevesagent.deterministic_hash(*parts: Any) str[source]¶
Stable hash of arbitrary JSON-serializable parts.
Used as an idempotency key for journaled steps. The hash is stable across processes and Python versions because the input is canonicalised via
json.dumps(..., sort_keys=True).
- jeevesagent.edit_tool(workdir: pathlib.Path | str | None = None, *, name: str = 'edit') jeevesagent.tools.registry.Tool[source]¶
Build a
Toolthat does find-and-replace inside an existing file underworkdir.- The tool’s signature seen by the model:
- ``edit(path: str, old_string: str, new_string: str,
replace_all: bool = False)``
Behaviour matches Claude Code’s Edit tool:
old_stringmust be EXACTLY present in the file. Mismatch (whitespace, indentation, line breaks) → error.old_stringmust appear EXACTLY once in the file unlessreplace_all=Trueis passed — forces the model to give enough surrounding context for unambiguous matches.new_stringreplacesold_string(or every occurrence ifreplace_all=True).
workdiris optional;Noneuses the framework’s default tempdir (shared with the other built-in tools).
- jeevesagent.filesystem_tools(workdir: pathlib.Path | str | None = None) list[jeevesagent.tools.registry.Tool][source]¶
Return all three filesystem tools (read + write + edit) bound to a single workdir.
bash_toolis excluded — pair them only when you want shell access too.workdiris optional;Noneuses the framework’s default tempdir (shared with bash_tool() called the same way).
- jeevesagent.get_run_context() RunContext[source]¶
Return the
RunContextfor the currently-running agent.Inside an active
Agent.run()call this returns the live context withuser_id,session_id,run_id, andmetadatapopulated. Outside any active run (test code, direct@toolinvocation, REPL exploration) this returns the default emptyRunContext— never raises.Tools that need scope information call this rather than taking extra parameters:
@tool async def fetch_user_orders() -> str: ctx = get_run_context() return await db.query("orders", user_id=ctx.user_id)
- jeevesagent.new_id(prefix: str = '') str[source]¶
Return a fresh ULID, optionally prefixed for readability.
>>> new_id("ep").startswith("ep_") True
- jeevesagent.read_tool(workdir: pathlib.Path | str | None = None, *, name: str = 'read', line_limit: int = _DEFAULT_READ_LINE_LIMIT) jeevesagent.tools.registry.Tool[source]¶
Build a
Toolthat reads a text file underworkdir.- The tool’s signature seen by the model:
read(path: str, offset: int = 0, limit: int | None = None)
Returns the file’s text with line numbers prefixed (one line per output line), in the same format Claude Code’s Read tool uses — that lets the
edittool work without ambiguity later. Long files are truncated toline_limitlines per call; passoffset/limitto read further chunks.Errors (file-not-found, path-escape) are returned as a string starting with
"ERROR: "rather than raising — the model sees them as a tool result and can adjust.workdiris optional;Noneuses the framework’s default tempdir (shared with the other built-in tools called without a workdir).
- jeevesagent.resolve_architecture(spec: jeevesagent.architecture.base.Architecture | str | None) jeevesagent.architecture.base.Architecture[source]¶
Coerce
specto a concreteArchitecture.None→ReAct(the default)str→ looked up inKNOWN(only"react"in v0.3)Architecture instance → returned as-is
Unknown strings raise
ConfigErrorwith a list of known names.
- async jeevesagent.run_architecture(architecture: jeevesagent.architecture.Architecture, prompt: str, *, instructions: str = '', model: jeevesagent.core.protocols.Model | str | None = None, memory: jeevesagent.core.protocols.Memory | None = None, runtime: jeevesagent.core.protocols.Runtime | None = None, budget: jeevesagent.core.protocols.Budget | None = None, permissions: jeevesagent.core.protocols.Permissions | None = None, hooks: jeevesagent.security.hooks.HookRegistry | None = None, tools: list[jeevesagent.tools.registry.Tool | collections.abc.Callable[Ellipsis, object]] | jeevesagent.core.protocols.ToolHost | jeevesagent.tools.registry.Tool | collections.abc.Callable[Ellipsis, object] | None = None, telemetry: jeevesagent.core.protocols.Telemetry | None = None, audit_log: jeevesagent.security.audit.AuditLog | None = None, max_turns: int = DEFAULT_MAX_TURNS, auto_consolidate: bool = False) jeevesagent.core.types.RunResult[source]¶
Run an
Architectureonce with a minimal Agent shell.Useful for testing orchestrators in isolation or for one-shot scripts where you don’t want to construct an Agent yourself.
The default
modelis the framework’s resolver default (set viamodel=or env / config); pass an explicit model or string id to override.Example:
sup = Supervisor(workers={"a": agent_a}) result = await run_architecture( sup, "do the thing", model="gpt-4.1-mini" )
- jeevesagent.tool(fn: collections.abc.Callable[Ellipsis, Any]) Tool[source]¶
- jeevesagent.tool(*, name: str | None = None, description: str | None = None, destructive: bool = False) collections.abc.Callable[[collections.abc.Callable[Ellipsis, Any]], Tool]
Promote a callable to a
Tool.Use as
@tool(bare) or@tool(name=..., description=..., destructive=...). The schema is derived from parameter annotations; primitive types map to their JSON-Schema equivalents, anything else falls back tostring.
- async jeevesagent.write_graph(agent: jeevesagent.agent.api.Agent, path: str | pathlib.Path, *, title: str | None = None) str[source]¶
Walk the agent, render to Mermaid, write to
path.Extension dispatch:
.mmd— raw Mermaid source.md— Markdown with the diagram in amermaidfence.png/.svg— fetched frommermaid.ink; on network failure, writes.mmdnext to the requested path and returns the Mermaid text anyway
Returns the Mermaid text in every case.
- jeevesagent.write_tool(workdir: pathlib.Path | str | None = None, *, name: str = 'write', create_parents: bool = True) jeevesagent.tools.registry.Tool[source]¶
Build a
Toolthat writes / overwrites a text file underworkdir.- The tool’s signature seen by the model:
write(path: str, content: str)
Overwrites existing files. With
create_parents=True(the default), missing parent directories are created automatically.Returns a confirmation string with the byte count, or an
"ERROR: "-prefixed message on failure.workdiris optional;Noneuses the framework’s default tempdir (shared with the other built-in tools).