Types and errors

The exported dataclasses — Config, TokenEvent, AgentContext — field by field, plus BudgetExceededError and the one thing to know about catching it.

Four exported types. Three are plain dataclasses you can type-hint against; the fourth is the only exception the SDK raises on purpose.

Config

The configuration init() builds. You rarely construct one, but you may want to read it, and three field names differ from the init() keyword that sets them:

init() keywordConfig field
agent_namedefault_agent
teamdefault_team
gatewaydefault_gateway

Every other field matches its keyword: api_key, endpoint, env, flush_interval_ms, enable_routing, routing_rules, routing_refresh_interval, critical_task_types, enable_budget_enforcement, budget_check_timeout, enable_prompt_analysis, prompt_size_threshold, on_oversized_prompt, debug.

endpoint is normalised on construction — a trailing slash is stripped — so the value you read back may differ by one character from the value you passed.

TokenEvent

One captured LLM call, and the unit the backend ingests.

Identity

FieldNotes
idUUID for the event.
timestampISO-8601, UTC.
request_idSeparate UUID used as the deduplication key, so a retried batch cannot double-count.

The call

FieldNotes
modelWhat actually ran.
provider"anthropic", "openai", "google", …
input_tokens, output_tokensReported by the provider, not estimated.
cache_read_tokens, cache_write_tokensPrompt-cache traffic, priced at their own rates.
cost_usdComputed in the SDK from the pricing catalogue.
latency_msEnd-to-end.
caller_fileThe file and line in your code that made the call, resolved past framework internals.

Attribution

FieldNotes
agent_idDefaults to "unknown" rather than being dropped.
envFrom init(env=…).
team_id, task_type, session_id, customer_id, cost_centerFrom the scope in force.
user_idReserved; not set by the SDK.
gatewayThe gateway the call rode through, or None for a direct call. Orthogonal to provider. Added in 0.4.0.

Routing

FieldNotes
was_routedWhether a rule fired.
original_modelWhat was requested, when model is not it.

Errors

FieldNotes
is_errorThe provider call raised instead of returning.
error_typeThe exception class name.

A failed call still costs money at some providers and always costs latency, so it is recorded rather than dropped.

Prompt analysis

Set only with enable_prompt_analysis=True, and never containing prompt text: system_prompt_hash, system_prompt_tokens, message_count, conversation_tokens, has_few_shot, few_shot_pairs, tool_result_tokens, conversation_turns, context_window_utilization.

Methods

to_dict() returns a JSON-serialisable dict with None values omitted.

Values are clamped on construction

Every event normalises itself into the backend's ingest contract as it is built: string fields truncate (model, provider, and gateway at 64 characters; agent_id, team_id, task_type, customer_id, cost_center, and env at 128; caller_file at 512), counters floor at zero, and context_window_utilization clamps into 0–1.

This is not cosmetic. The backend parses a whole ingest batch as one model, so a single out-of-range field would reject every event in that flush — and the emitter would retry the same payload and then drop all of it. A 200-character customer_id costs you the end of one string instead of a batch of events.

AgentContext

The scope object pushed by agent(), tag(), and the framework helpers. Every field is optional: agent, team, task_type, session_id, customer_id, cost_center.

Scopes live in contextvars, so each thread and each asyncio task sees its own stack. Reading one directly is rarely necessary; it is exported so you can type-hint code that builds attribution generically.

BudgetExceededError

class BudgetExceededError(RuntimeError)

Raised before the provider call when the pre-call budget check returns a blocking decision. The message comes from the backend and names the budget.

It subclasses RuntimeError, which is the detail worth knowing: a broad except Exception around your LLM call will swallow enforcement and turn a deliberate block into a silent failure. Catch it explicitly if you want to degrade instead.

try:
    response = client.messages.create(...)
except capsera.BudgetExceededError as exc:
    return fallback_response(str(exc))

This is the only exception the SDK raises deliberately. Everything else — delivery failure, an unreachable backend, an unparseable response, a broken gateway install — degrades to missing telemetry. See Reliability.