Agents#
wreath.agents connects model providers, MCP tools, durable jobs, ChatOps identities, approvals, memory, artifacts, and recording without a vendor SDK or a second runtime. Nothing runs on the ordinary request path unless an application creates an agent.
A bounded runtime#
Create the outbound client through the application so Wreath owns its pool, DNS and destination policy for the lifespan. The adapter streams response bytes directly into the selected backplane.
import os
from wreath import Wreath
from wreath.agents import (
AgentCatalog,
AgentProfile,
AgentRuntime,
HTTPClientTransport,
OpenAIResponsesBackplane,
)
app = Wreath()
http = app.http_client("models", base_url="https://api.openai.com")
transport = HTTPClientTransport(http, base_url="https://api.openai.com/v1")
models = OpenAIResponsesBackplane(
api_key=os.environ["OPENAI_API_KEY"],
transport=transport,
)
profile = AgentProfile(
name="support",
backplane=models,
model="gpt-5",
max_turns=6,
max_tool_calls=8,
max_output_tokens=2_000,
max_total_tokens=12_000,
timeout=90,
)
runtime = AgentRuntime(AgentCatalog((profile,), default="support"))The same normalized contract is implemented by AnthropicMessagesBackplane, GeminiGenerateContentBackplane, OpenAICompatibleBackplane, and AzureOpenAIBackplane. Azure accepts either an API key or an asynchronous Entra token provider and fetches a fresh token for each attempt. A catalog may map tenants to different profiles. Retryable provider failure may cross to a declared fallback only before the provider emits an event; partial output and tool effects are never replayed through another model.
For deployments that select a provider by capability, residency and live health, put a RoutedBackplane behind the profile. ModelCandidate declares immutable capability and region facts; ModelRoutePolicy fixes each tenant's ordered candidate set. Construction refuses a tenant with no statically eligible candidate. Request-time routing may narrow, but never widen, those constraints, and never fails over after a provider event.
AgentRuntime.execute(prompt, context=...) yields normalized text, tool-call, usage, and completion events. AgentRuntime.run(...) also implements ChatOps' vendor-neutral AgentBackend protocol.
Governed tools without protocol loopback#
MCPToolCatalog selects a fixed subset of an existing MCP registry. Selection compiles schemas once and refuses unknown, duplicate, over-limit, sampling, and elicitation tools before requests begin.
from typing import Any
from wreath import Request
from wreath.agents import (
AgentCatalog,
AgentProfile,
AgentRuntime,
MCPToolCatalog,
)
from wreath.mcp import MCP
def support_agent(models: Any) -> AgentRuntime:
mcp = MCP(name="support-tools", version="1")
@mcp.tool(
description="Read one support case.",
action="Case::read",
resource='Case::"requested"',
)
async def read_case(request: Request, case_id: str) -> dict[str, Any]:
return await request.state.cases.read(request.identity, case_id)
profile = AgentProfile(
name="support",
backplane=models,
model="gpt-5",
tools=("read_case",),
delegation_scope=frozenset({"Case::read"}),
)
return AgentRuntime(
AgentCatalog((profile,)),
tools=MCPToolCatalog(mcp),
)Invocation calls the tool directly. It still uses the MCP binding schema, Cedar and role/permission/second-factor checks, tenant-and-principal rate limits, progress, error normalization, and recording. Every call receives an unambiguous effect ID. Operations that require an interactive MCP client session refuse at profile construction instead of failing halfway through a turn.
RemoteMCPClient speaks MCP Streamable HTTP directly to an HTTPS endpoint. It owns initialize/initialized, protocol and session headers, bounded paginated discovery, schema drift detection, JSON and SSE responses, cancellation, close, token lookup and effect-aware audit records. RemoteMCPToolCatalog combines connected clients and refuses tool-name collisions. MCPHTTPClientTransport binds it to an application-owned HTTPClient without enabling redirects or an off-origin credential path. A remote tool with an unknown outcome is never retried.
FederatedToolCatalog combines local and remote catalogs under explicit namespace prefixes. It compiles each participating child once, preserves declared tool order and invokes the owning child directly. Qualified names make equal tool names from different servers unambiguous; malformed names, catalog drift and collisions refuse before an invocation begins.
ChatOps and federated authority#
Register a runtime as one durable command:
from wreath.agents import AgentRuntime
from wreath.chat import ChatOps
def register_support_agent(chat: ChatOps, runtime: AgentRuntime) -> None:
chat.agent(
"ask",
runtime,
description="Ask the support agent",
action="Support::ask",
resource="conversation",
)ChatOps.agent requires the normal durable ChatOps jobs and transactional inbox owners. Slack, Teams, and Discord identity resolution happens before command authorization. The resolved Wreath principal is carried into the agent invocation; a profile with delegation_scope calls the existing Principal.narrow(...) owner, so an agent can only lose authority. The direct MCP executor evaluates that narrowed identity again for every tool.
Safety-biased durable effects and approval#
DurableAgent adapts a model completion and effect executor to JobRunner. Turn and tool-call IDs are stable, jobs have automatic retries disabled, and tenant, principal, job key, attempt, and fence are checked before model work. A tool effect is checkpointed only after it succeeds. A checkpoint observed before execution is skipped; recovery attempts are refused because a local checkpoint cannot prove whether a suspended remote effect happened. An unknown outcome is surfaced for an operator instead of guessed safe. Use a stable tool-call ID as the remote system's idempotency key when that system supports one. End-to-end recovery requires a store and effect sink that atomically claim the effect or preserve an explicit unknown state.
ApprovalStore is the persistence contract for human approval. The included InMemoryApprovalStore is bounded, expiring, single-use, tenant-and-principal bound, and may require authentication newer than the approval request. Production deployments can implement the same contract over their durable store.
ChatApprovalFlow(chat, approvals) registers bounded dynamic action prefixes and renders native Slack Block Kit buttons, Teams Adaptive Card actions, or ephemeral Discord components. Approval and denial use only the verified, server-resolved tenant and identity. The action payload carries an opaque approval ID, never authority; the store owns the action, resource, expiry and fresh-auth requirement.
Memory and artifacts#
ContextAssembler performs one bounded store read, checks every returned record's tenant, principal, and conversation, and selects deterministically under item and character ceilings. A memory store must declare positive retention and implement erasure. Records retain explicit trust and provenance labels; Wreath does not silently promote external text to system instructions.
AgentArtifactManager writes through an existing ObjectStore. Byte and artifact-count ceilings are checked before an over-limit chunk reaches storage. Streaming writes hash and forward each chunk in one pass, and returned metadata includes ownership, media type, trust, digest, object stat, and a Provenance envelope.
Recording#
Pass AgentObservability to AgentRuntime to record model and tool boundaries:
from typing import Any
from wreath.agents import (
AgentCatalog,
AgentObservability,
AgentProfile,
AgentRuntime,
MCPToolCatalog,
)
from wreath.mcp import MCP
def observed_agent(
profile: AgentProfile,
mcp: MCP,
deployment_observer: Any,
) -> AgentRuntime:
return AgentRuntime(
AgentCatalog((profile,)),
tools=MCPToolCatalog(mcp),
observability=AgentObservability(observer=deployment_observer),
)The default record contains identity, correlation, provider/model or tool/call ID, duration, outcome, usage, and fallback status—not prompts, arguments, or results. Payload capture requires an explicit bounded RedactionPolicy; structured capture also requires an application redactor. Recorder failures are counted and cannot turn a completed tool effect into a retry.
wreath.agents#
Provider-neutral model agents with governed tools and bounded execution.
AgentBudgetExceeded#
AgentArtifact#
AgentArtifact(key: 'str', artifact_id: 'str', ordinal: 'int', tenant: 'str', principal_id: 'str', conversation: 'str', digest: 'str', media_type: 'str', trust: 'ArtifactTrust', provenance: 'Provenance', stat: 'ObjectStat')
AgentArtifact(key: str, artifact_id: str, ordinal: int, tenant: str, principal_id: str, conversation: str, digest: str, media_type: str, trust: ArtifactTrust, provenance: Provenance, stat: ObjectStat) -> NoneAgentArtifactManager#
AgentArtifactManager(store: ObjectStore, *, max_bytes: int, max_artifacts: int) -> Noneerase#
async erase(context: Any, artifact_id: str, *, ordinal: int) -> Nonekey#
key(context: Any, artifact_id: str, *, ordinal: int) -> strwrite#
async write(context: Any, *, artifact_id: str, ordinal: int, body: bytes | bytearray | memoryview, media_type: str = 'application/octet-stream', trust: ArtifactTrust = 'model') -> AgentArtifactwrite_stream#
async write_stream(context: Any, *, artifact_id: str, ordinal: int, chunks: AsyncIterable[bytes], media_type: str = 'application/octet-stream', trust: ArtifactTrust = 'model') -> AgentArtifactAgentCapturePolicy#
AgentCapturePolicy(redaction: 'RedactionPolicy', fields: 'frozenset[str]', redact: 'Callable[[str, Any], str] | None' = None)
AgentCapturePolicy(redaction: RedactionPolicy, fields: frozenset[str], redact: Callable[[str, Any], str] | None = None) -> NoneAgentCatalog#
AgentCatalog(profiles: tuple[AgentProfile, ...], *, default: str | None = None, tenants: Mapping[str, str] | None = None) -> Noneprofiles (property)#
profiles: tuple[AgentProfile, ...]select#
select(tenant: str, *, requested: str | None = None) -> AgentProfileAgentConfigurationError#
AgentInvocationContext#
AgentInvocationContext(tenant: 'str', principal: 'Any', conversation: 'str', correlation_id: 'str | None' = None, delegation: 'Any' = None, metadata: 'Mapping[str, Any]' = <factory>)
AgentInvocationContext(tenant: str, principal: Any, conversation: str, correlation_id: str | None = None, delegation: Any = None, metadata: Mapping[str, Any] = <factory>) -> NoneAgentObservation#
AgentObservation(kind: "Literal['model', 'tool']", tenant: 'str', principal_id: 'str', conversation: 'str', correlation_id: 'str | None', duration: 'float', outcome: 'AgentOutcome', provider: 'str | None' = None, model: 'str | None' = None, request_id: 'str | None' = None, tool: 'str | None' = None, call_id: 'str | None' = None, usage: 'AgentUsage | None' = None, fallback: 'bool' = False, payload: 'tuple[CapturedAgentPayload, ...]' = ())
AgentObservation(kind: "Literal['model', 'tool']", tenant: str, principal_id: str, conversation: str, correlation_id: str | None, duration: float, outcome: AgentOutcome, provider: str | None = None, model: str | None = None, request_id: str | None = None, tool: str | None = None, call_id: str | None = None, usage: AgentUsage | None = None, fallback: bool = False, payload: tuple[CapturedAgentPayload, ...] = ()) -> NoneAgentObservability#
AgentObservability(*, observer: AgentObserver | None = None, capture: AgentCapturePolicy | None = None) -> Nonemodel#
async model(context: Any, *, provider: str, model: str, request_id: str | None, duration: float, outcome: AgentOutcome, usage: AgentUsage | None = None, fallback: bool = False, payloads: Mapping[str, Any] | None = None) -> Nonetool#
async tool(context: Any, *, tool: str, call_id: str, duration: float, outcome: AgentOutcome, payloads: Mapping[str, Any] | None = None) -> NoneAgentObserver#
AgentObserver(*args, **kwargs)record#
async record(event: AgentObservation) -> NoneAgentProfile#
AgentProfile(name: 'str', backplane: 'ModelBackplane', model: 'str', system_prompt: 'str' = '', tools: 'tuple[str, ...]' = (), fallbacks: 'tuple[ModelTarget, ...]' = (), max_turns: 'int' = 8, max_tool_calls: 'int' = 16, max_output_tokens: 'int | None' = None, max_total_tokens: 'int | None' = None, max_prompt_bytes: 'int' = 262144, max_tool_argument_bytes: 'int' = 1048576, max_tool_result_bytes: 'int' = 1048576, timeout: 'float | None' = 120.0, delegation_scope: 'frozenset[str]' = frozenset(), delegation_ttl: 'float' = 300.0)
AgentProfile(name: str, backplane: ModelBackplane, model: str, system_prompt: str = '', tools: tuple[str, ...] = (), fallbacks: tuple[ModelTarget, ...] = (), max_turns: int = 8, max_tool_calls: int = 16, max_output_tokens: int | None = None, max_total_tokens: int | None = None, max_prompt_bytes: int = 262144, max_tool_argument_bytes: int = 1048576, max_tool_result_bytes: int = 1048576, timeout: float | None = 120.0, delegation_scope: frozenset[str] = frozenset(), delegation_ttl: float = 300.0) -> Nonetargets (property)#
targets: tuple[ModelTarget, ...]AgentRuntime#
AgentRuntime(catalog: AgentCatalog, *, tools: _ToolCatalog | None = None, observability: Any = None) -> Noneexecute#
execute(prompt: str, *, context: AgentInvocationContext, profile: str | None = None) -> AsyncIterator[ModelResponseEvent]run#
run(request: AgentRequest) -> AsyncIterator[AgentEvent]AgentUsage#
AgentUsage(input_tokens: 'int' = 0, output_tokens: 'int' = 0, cached_input_tokens: 'int' = 0)
AgentUsage(input_tokens: int = 0, output_tokens: int = 0, cached_input_tokens: int = 0) -> Nonetotal_tokens (property)#
total_tokens: intAnthropicMessagesBackplane#
AnthropicMessagesBackplane(*, api_key: str, transport: Transport, model: str | None = None, base_url: str = 'https://api.anthropic.com/v1', api_version: str = '2023-06-01', streaming: bool = True, default_max_output_tokens: int = 4096, max_retries: int = 2, max_response_bytes: int = 16777216) -> Nonestream (inherited from _Backplane)#
stream(request: ModelRequest) -> AsyncIterator[ModelResponseEvent]ApprovalDenied#
ApprovalExpired#
ApprovalGrant#
ApprovalGrant(approval_id: 'str', tenant: 'str', principal_id: 'str', action: 'str', resource: 'str | None', approved_at: 'float')
ApprovalGrant(approval_id: str, tenant: str, principal_id: str, action: str, resource: str | None, approved_at: float) -> NoneApprovalMismatch#
ApprovalRequest#
ApprovalRequest(approval_id: 'str', tenant: 'str', principal_id: 'str', action: 'str', resource: 'str | None', issued_at: 'float', expires_at: 'float', require_fresh_auth: 'bool' = False, state: "Literal['pending', 'denied', 'used']" = 'pending')
ApprovalRequest(approval_id: str, tenant: str, principal_id: str, action: str, resource: str | None, issued_at: float, expires_at: float, require_fresh_auth: bool = False, state: "Literal['pending', 'denied', 'used']" = 'pending') -> NoneApprovalStore#
ApprovalStore(*args, **kwargs)claim#
async claim(approval_id: str, *, tenant: str, principal_id: str, authenticated_at: float | None = None) -> ApprovalGrantdeny#
async deny(approval_id: str, *, tenant: str, principal_id: str) -> Noneissue#
async issue(*, approval_id: str, tenant: str, principal_id: str, action: str, resource: str | None = None, ttl: float, require_fresh_auth: bool = False, issued_at: float | None = None) -> ApprovalRequestArtifactLimitExceeded#
AssembledContext#
AssembledContext(records: 'tuple[MemoryRecord, ...]', characters: 'int')
AssembledContext(records: tuple[MemoryRecord, ...], characters: int) -> NoneAzureOpenAIBackplane#
AzureOpenAIBackplane(*, endpoint: str, deployment: str, transport: Transport, api_key: str | None = None, token_provider: TokenProvider | None = None, api_version: str | None = None, streaming: bool = True, max_retries: int = 2, max_response_bytes: int = 16777216) -> Nonestream#
stream(request: ModelRequest) -> AsyncIterator[ModelResponseEvent]BackplaneError#
BackplaneError(message: str, *, retryable: bool = False, status: int | None = None, request_id: str | None = None, output_started: bool = False) -> NoneContextAssembler#
ContextAssembler(store: MemoryStore, *, max_items: int = 32, max_chars: int = 16000) -> Noneassemble#
async assemble(*, tenant: str, principal_id: str, conversation: str) -> AssembledContexterase#
async erase(*, tenant: str, principal_id: str, conversation: str) -> Noneremember#
async remember(record: MemoryRecord) -> Noneretention_seconds (property)#
retention_seconds: floatCapturedAgentPayload#
CapturedAgentPayload(field: 'str', length: 'int', value: 'str | None' = None, truncated: 'bool' = False)
CapturedAgentPayload(field: str, length: int, value: str | None = None, truncated: bool = False) -> NoneChatApprovalFlow#
ChatApprovalFlow(chat: ChatOps, store: ApprovalStore) -> Noneissue#
async issue(context: ChatContext, *, approval_id: str, action: str, resource: str | None = None, ttl: float, require_fresh_auth: bool = False) -> ChatReplyon_approved#
on_approved(action: str, handler: Callable[[ChatContext, ApprovalGrant], Awaitable[ChatReply]]) -> Noneschema_owners (property)#
schema_owners: tuple[ApprovalStore, ...]DurableAgent#
DurableAgent(*, name: str, jobs: Any, backend: DurableModelBackend, tools: DurableToolExecutor, checkpoints: EffectCheckpointStore, principal_id: str | None = None) -> Noneenqueue#
async enqueue(invocation: Any, *, prompt: str, message_id: str) -> int | NoneDurableEffectContext#
DurableEffectContext(turn_id: 'str', tenant: 'str', principal_id: 'str', correlation_id: 'str | None', job_context: 'Any')
DurableEffectContext(turn_id: str, tenant: str, principal_id: str, correlation_id: str | None, job_context: Any) -> Nonefence (property)#
fence: intDurableModelBackend#
DurableModelBackend(*args, **kwargs)complete#
async complete(turn: DurableTurn) -> ModelResultDurableToolExecutor#
DurableToolExecutor(*args, **kwargs)execute#
async execute(call: ToolCall, *, context: DurableEffectContext) -> AnyDurableTurn#
DurableTurn(turn_id: 'str', tenant: 'str', principal_id: 'str', conversation: 'str', prompt: 'str', correlation_id: 'str | None' = None)
DurableTurn(turn_id: str, tenant: str, principal_id: str, conversation: str, prompt: str, correlation_id: str | None = None) -> Noneas_payload#
as_payload() -> dict[str, str | None]from_invocation (classmethod)#
from_invocation(invocation: Any, *, prompt: str, message_id: str) -> DurableTurnfrom_payload (classmethod)#
from_payload(payload: Mapping[str, Any]) -> DurableTurnEffectCheckpoint#
EffectCheckpoint(turn_id: 'str', call_id: 'str', tenant: 'str', principal_id: 'str', fence: 'int')
EffectCheckpoint(turn_id: str, call_id: str, tenant: str, principal_id: str, fence: int) -> NoneEffectCheckpointStore#
EffectCheckpointStore(*args, **kwargs)complete#
async complete(checkpoint: EffectCheckpoint) -> boolcompleted#
async completed(turn_id: str, call_id: str, *, tenant: str, principal_id: str) -> EffectCheckpoint | Nonevalidate_fence#
async validate_fence(turn_id: str, *, tenant: str, principal_id: str, fence: int) -> NoneGeminiGenerateContentBackplane#
GeminiGenerateContentBackplane(*, api_key: str, transport: Transport, model: str | None = None, base_url: str = 'https://generativelanguage.googleapis.com/v1beta', streaming: bool = True, max_retries: int = 2, max_response_bytes: int = 16777216) -> Nonestream (inherited from _Backplane)#
stream(request: ModelRequest) -> AsyncIterator[ModelResponseEvent]FederatedToolCatalog#
FederatedToolCatalog(namespaces: Mapping[str, Any], *, separator: str = '__', max_tools: int = 32) -> Noneselect#
select(names: tuple[str, ...]) -> _FederatedToolSetHTTPClientTransport#
HTTPClientTransport(client: HTTPClient, *, base_url: str) -> NoneInMemoryApprovalStore#
InMemoryApprovalStore(*, max_entries: int = 4096, clock: Callable[[], float] = <built-in function time>) -> Noneclaim#
async claim(approval_id: str, *, tenant: str, principal_id: str, authenticated_at: float | None = None) -> ApprovalGrantdeny#
async deny(approval_id: str, *, tenant: str, principal_id: str) -> Noneissue#
async issue(*, approval_id: str, tenant: str, principal_id: str, action: str, resource: str | None = None, ttl: float, require_fresh_auth: bool = False, issued_at: float | None = None) -> ApprovalRequestMemoryRecord#
MemoryRecord(memory_id: 'str', tenant: 'str', principal_id: 'str', conversation: 'str', content: 'str', trust: 'MemoryTrust', provenance: 'str', created_at: 'float')
MemoryRecord(memory_id: str, tenant: str, principal_id: str, conversation: str, content: str, trust: MemoryTrust, provenance: str, created_at: float) -> NoneMemoryStore#
MemoryStore(*args, **kwargs)append#
async append(record: MemoryRecord) -> Noneerase#
async erase(*, tenant: str, principal_id: str, conversation: str) -> Nonerecent#
async recent(*, tenant: str, principal_id: str, conversation: str, limit: int) -> Sequence[MemoryRecord]MCPToolCatalog#
MCPToolCatalog(mcp: Any, *, max_tools: int = 32) -> Noneselect#
select(names: tuple[str, ...]) -> _SelectedMCPToolsMCPHTTPResponse#
MCPHTTPResponse(url: 'str', status: 'int', headers: 'Mapping[str, str]', body: 'bytes')
MCPHTTPResponse(url: str, status: int, headers: Mapping[str, str], body: bytes) -> NoneMCPHTTPClientTransport#
MCPHTTPClientTransport(client: HTTPClient, *, endpoint: str) -> Noneclose#
async close() -> Nonerequest#
async request(method: str, url: str, *, headers: Mapping[str, str], body: bytes | None, max_response_bytes: int) -> MCPHTTPResponseMCPHTTPTransport#
MCPHTTPTransport(*args, **kwargs)close#
async close() -> Nonerequest#
async request(method: str, url: str, *, headers: Mapping[str, str], body: bytes | None, max_response_bytes: int) -> MCPHTTPResponseMCPInvocationAudit#
MCPInvocationAudit(client: 'str', tool: 'str', call_id: 'str', effect_id: 'str', tenant: 'str', principal_id: 'str', conversation: 'str', correlation_id: 'str | None', outcome: 'str')
MCPInvocationAudit(client: str, tool: str, call_id: str, effect_id: str, tenant: str, principal_id: str, conversation: str, correlation_id: str | None, outcome: str) -> NoneMCPInvocationObserver#
MCPInvocationObserver(*args, **kwargs)record#
async record(event: MCPInvocationAudit) -> NoneMCPRemoteError#
MCPRemoteScopeError#
MCPTokenProvider#
MCPTokenProvider(*args, **kwargs)token#
async token(origin: str) -> strMCPToolCatalogDrift#
MCPToolLimitExceeded#
ModelBackplane#
ModelBackplane(*args, **kwargs)stream#
stream(request: ModelRequest) -> AsyncIterator[ModelResponseEvent]ModelCandidate#
ModelCandidate(name: 'str', target: 'ModelTarget', capabilities: 'frozenset[str]', regions: 'frozenset[str]')
ModelCandidate(name: str, target: ModelTarget, capabilities: frozenset[str], regions: frozenset[str]) -> NoneModelMessage#
ModelMessage(role: "Literal['system', 'user', 'assistant', 'tool']", content: 'str', name: 'str | None' = None, call_id: 'str | None' = None)
ModelMessage(role: "Literal['system', 'user', 'assistant', 'tool']", content: str, name: str | None = None, call_id: str | None = None) -> NoneModelRequest#
ModelRequest(model: 'str', messages: 'tuple[ModelMessage, ...]', tools: 'tuple[ToolSpecification, ...]' = (), max_output_tokens: 'int | None' = None, temperature: 'float | None' = None, metadata: 'Mapping[str, Any]' = <factory>)
ModelRequest(model: str, messages: tuple[ModelMessage, ...], tools: tuple[ToolSpecification, ...] = (), max_output_tokens: int | None = None, temperature: float | None = None, metadata: Mapping[str, Any] = <factory>) -> NoneModelResult#
ModelResult(output: 'str' = '', tool_calls: 'tuple[ToolCall, ...]' = ())
ModelResult(output: str = '', tool_calls: tuple[ToolCall, ...] = ()) -> NoneModelResponseEvent#
ModelResponseEvent(kind: "Literal['text', 'tool_call', 'usage', 'completed']", text: 'str | None' = None, tool_name: 'str | None' = None, tool_call_id: 'str | None' = None, arguments: 'Mapping[str, Any] | None' = None, usage: 'ModelUsage | None' = None, provider_request_id: 'str | None' = None)
ModelResponseEvent(kind: "Literal['text', 'tool_call', 'usage', 'completed']", text: str | None = None, tool_name: str | None = None, tool_call_id: str | None = None, arguments: Mapping[str, Any] | None = None, usage: ModelUsage | None = None, provider_request_id: str | None = None) -> Nonecompleted (classmethod)#
completed(*, request_id: str | None = None) -> ModelResponseEventtext_delta (classmethod)#
text_delta(text: str, *, request_id: str | None = None) -> ModelResponseEventtool_call (classmethod)#
tool_call(name: str, call_id: str, arguments: Mapping[str, Any], *, request_id: str | None = None) -> ModelResponseEventusage_report (classmethod)#
usage_report(usage: ModelUsage, *, request_id: str | None = None) -> ModelResponseEventModelRoutePolicy#
ModelRoutePolicy(tenant: 'str', candidates: 'tuple[str, ...]', required_capabilities: 'frozenset[str]', allowed_regions: 'frozenset[str]')
ModelRoutePolicy(tenant: str, candidates: tuple[str, ...], required_capabilities: frozenset[str], allowed_regions: frozenset[str]) -> NoneModelTarget#
ModelTarget(backplane: 'ModelBackplane', model: 'str')
ModelTarget(backplane: ModelBackplane, model: str) -> NoneModelUsage#
ModelUsage(input_tokens: 'int' = 0, output_tokens: 'int' = 0, cached_input_tokens: 'int' = 0)
ModelUsage(input_tokens: int = 0, output_tokens: int = 0, cached_input_tokens: int = 0) -> Nonetotal_tokens (property)#
total_tokens: intOpenAICompatibleBackplane#
OpenAICompatibleBackplane(*, base_url: str, transport: Transport, api_key: str | None = None, model: str | None = None, streaming: bool = True, max_retries: int = 2, max_response_bytes: int = 16777216) -> Nonestream (inherited from _Backplane)#
stream(request: ModelRequest) -> AsyncIterator[ModelResponseEvent]OpenAIResponsesBackplane#
OpenAIResponsesBackplane(*, api_key: str, transport: Transport, model: str | None = None, base_url: str = 'https://api.openai.com/v1', streaming: bool = True, max_retries: int = 2, max_response_bytes: int = 16777216) -> Nonestream (inherited from _Backplane)#
stream(request: ModelRequest) -> AsyncIterator[ModelResponseEvent]PostgresApprovalStore#
PostgresApprovalStore(session_factory: Callable[[], Any], *, schema: str = 'wreath', clock: Callable[[], float] = <built-in function time>) -> Noneclaim#
async claim(approval_id: str, *, tenant: str, principal_id: str, authenticated_at: float | None = None) -> ApprovalGrantcomponent#
component() -> Componentdeny#
async deny(approval_id: str, *, tenant: str, principal_id: str) -> Noneissue#
async issue(*, approval_id: str, tenant: str, principal_id: str, action: str, resource: str | None = None, ttl: float, require_fresh_auth: bool = False, issued_at: float | None = None) -> ApprovalRequestpurge#
async purge(*, limit: int = 1000) -> intschema_sql#
schema_sql() -> strRemoteMCPClient#
RemoteMCPClient(endpoint: str, *, transport: MCPHTTPTransport, name: str = 'remote', token_provider: MCPTokenProvider | None = None, audit: MCPInvocationObserver | None = None, max_tools: int = 128, max_pages: int = 16, max_schema_bytes: int = 65536, max_response_bytes: int = 4194304, max_sse_events: int = 256) -> Noneclose#
async close() -> Noneconnect#
async connect() -> Noneinvoke#
async invoke(name: str, arguments: Mapping[str, Any], *, call_id: str, context: Any) -> Mapping[str, Any]protocol_version (property)#
protocol_version: str | Nonesession_id (property)#
session_id: str | Nonespecifications (property)#
specifications: tuple[Any, ...]RemoteMCPToolCatalog#
RemoteMCPToolCatalog(clients: Sequence[RemoteMCPClient], *, max_clients: int = 16) -> Noneconnect#
async connect() -> Noneselect#
select(names: tuple[str, ...]) -> RemoteMCPToolSetRemoteMCPToolSet#
RemoteMCPToolSet(owners: Mapping[str, RemoteMCPClient], names: tuple[str, ...]) -> Noneinvoke#
async invoke(name: str, arguments: Mapping[str, Any], *, call_id: str, context: Any) -> Mapping[str, Any]RoutedBackplane#
RoutedBackplane(candidates: Iterable[ModelCandidate], policies: Iterable[ModelRoutePolicy], *, health: Callable[[ModelCandidate], bool] | None = None) -> Nonestream#
stream(request: ModelRequest) -> AsyncIterator[ModelResponseEvent]StaleAgentFence#
StaleAgentFence(turn_id: str, fence: int) -> NoneToolCall#
ToolCall(name: 'str', arguments: 'Mapping[str, Any]', call_id: 'str | None' = None)
ToolCall(name: str, arguments: Mapping[str, Any], call_id: str | None = None) -> NoneToolSpecification#
ToolSpecification(name: 'str', description: 'str', input_schema: 'Mapping[str, Any]')
ToolSpecification(name: str, description: str, input_schema: Mapping[str, Any]) -> NoneUnknownModelOutcome#
UnknownToolOutcome#
stable_tool_call_id#
stable_tool_call_id(turn_id: str, index: int, name: str, arguments: Mapping[str, Any]) -> strstable_turn_id#
stable_turn_id(tenant: str, principal_id: str, conversation: str, message_id: str) -> str