Skip to content

latent.events

Agent event types matching agent-studio AgentProtocol.

Classes

AgentEvent

AgentEvent()

Base class for all agent events.

Error

Error(message: str)

Agent encountered an error (terminal event).

FunctionCall

FunctionCall(name: str, arguments: str)

Inner function block of a :class:ToolCall.

arguments is a JSON-encoded string, not a Python dict — this is the wire format LiteLLM / OpenAI / Anthropic-via-LiteLLM all use. Parse with json.loads when inspecting in Python; emit via json.dumps at the call site (or use :meth:ToolCall.with_arguments for the common case).

GuidelineMatched

GuidelineMatched(guideline_id: str, when: str, then: str)

A guideline was matched and injected for this turn.

JourneyCompleted

JourneyCompleted(journey_name: str, conversation_id: str)

The active journey completed all steps.

JourneyStepAdvanced

JourneyStepAdvanced(journey_name: str, step_name: str, step_index: int, conversation_id: str)

The active journey advanced to a new step.

LLMCallEnd

LLMCallEnd(run_id: str, output_preview: str | None = None, input_tokens: int = 0, output_tokens: int = 0, model_id: str | None = None, timestamp: float = 0.0)

LLM call completed.

LLMCallStart

LLMCallStart(run_id: str, input_preview: str, model_id: str | None = None, timestamp: float = 0.0)

LLM call started.

Message

Message(role: str, content: str | list[dict[str, Any]], id: str | None = None, tool_calls: list[ToolCall] | None = None, tool_call_id: str | None = None)

A single message in the conversation history.

tool_calls and tool_call_id lock to OpenAI / LiteLLM:

  • On an assistant message that invoked tools, set tool_calls to a list of :class:ToolCall instances — the same dataclass the agent stream yields. Persisting a step's calls into history is a pure list copy, zero translation.
  • On a tool message that carries the result of one call, set role="tool", tool_call_id="<matching id>", and content to the stringified output. One role="tool" row per result; do not embed results inside the assistant's tool_calls entries.

Field layout matches LiteLLM ChatCompletionMessage verbatim — no internal-shape translation. See :mod:latent.protocol.

Metadata

Metadata(attributes: MetadataAttributes | dict[str, MetadataValue], target: Literal['turn', 'step'] = 'turn')

Primitive trace attributes emitted by an agent stream.

Values are intentionally limited to OpenTelemetry-safe primitives. Omit absent values rather than passing None. target is advisory: stream consumers decide whether and how to attach the attributes to a trace span.

MetadataAttributes

MetadataAttributes()

Immutable dict copy used to preserve Metadata validation invariants.

PhaseCompleted

PhaseCompleted(phase_name: str, output_preview: str | None = None)

A pipeline phase has completed execution.

PhaseRouted

PhaseRouted(from_phase: str, to_phase: str, reason: str | None = None)

Pipeline routed from one phase to another.

PhaseStarted

PhaseStarted(phase_name: str, retry_count: int = 0)

A pipeline phase has started execution.

ReasoningDelta

ReasoningDelta(text: str)

Incremental reasoning/thinking chunk.

RetrieverEnd

RetrieverEnd(run_id: str, documents: list[str], name: str | None = None, timestamp: float = 0.0, parent_run_id: str | None = None)

A retriever query completed.

Emitted after :class:RetrieverStart by the same decorator. documents carries content previews; consumers that need the full retrieved chunks consult the underlying retriever directly — this event is observability, not the canonical retrieval result.

RetrieverStart

RetrieverStart(run_id: str, query: str, name: str | None = None, timestamp: float = 0.0, parent_run_id: str | None = None)

A retriever (vector store, BM25 index, etc.) began a query.

Emitted by a function decorated with :func:latent.retriever.retriever at the start of each retrieval call. The trace pipeline opens a per-retriever span keyed off run_id. RetrieverEnd carries the matching run_id so a single retrieval round forms a (start, end) pair regardless of nesting or parallel retrievals.

StepBoundary

StepBoundary(step: int, label: str | None = None)

Marks the start of an agent loop iteration.

Contract (load-bearing for BaseAgent.invoke()):

  • Emitted exactly once at the start of each iteration, before any other events for that iteration.
  • All subsequent events (LLMCallStart / LLMCallEnd / TextDelta / ReasoningDelta / ToolCall / ToolResult / Usage) belong to the iteration that preceded the next StepBoundary or end-of-stream.
  • Subclassed agents that override stream() must preserve this invariant. If they cannot, they must override BaseAgent.invoke() with their own segmentation logic.

invoke() uses StepBoundary as the segmentation primitive between Step records. Violating the contract silently regresses every buffered consumer of the agent — the terminal-step text derivation relies on this being well-formed.

label is an optional, free-form marker describing the kind of step, surfaced to trace/observability consumers — e.g. a short-circuit / canned fast-response step sets label="fast_response" so it's distinguishable from a normal LLM iteration. Purely advisory; invoke() segmentation ignores it.

TextDelta

TextDelta(text: str)

Incremental text chunk from the agent.

ToolCall

ToolCall(id: str, function: FunctionCall, type: str = 'function')

Agent is invoking a tool.

Fields mirror LiteLLM's ChatCompletionAssistantToolCall verbatim — no parallel TypedDict, no translation when persisting into :attr:Message.tool_calls. The same dataclass plays two roles: the streaming event the agent yields, and the value type stored on historical assistant messages. Consumers correlate results by id against the matching Message(role="tool", tool_call_id=…) row.

ToolResult

ToolResult(tool_call_id: str, output: Any = None, error: str | None = None)

Result from a tool invocation.

Persists as Message(role="tool", content=str(output_or_error), tool_call_id=tool_call_id). Kept Python-native (output: Any, error: str | None) because streaming consumers — tracing, supervisor UIs — want the structured output; the string conversion happens once when the result lands in conversation history.

Usage

Usage(input_tokens: int = 0, output_tokens: int = 0, model_id: str | None = None, cache_creation_input_tokens: int = 0, cache_read_input_tokens: int = 0)

Token usage information for a single LLM call.

input_tokens is the TOTAL input, cache included — the cache fields are informational subsets of it and must never be added on top. Callers needing disjoint accounting use :attr:uncached_input_tokens.

VerificationResult

VerificationResult(accepted: bool, feedback: str | None = None, tools_triggered: list[str] | None = None)

Result of the self-verification step after a response is generated.

Methods

ToolCall.from_arguments

from_arguments(id: str, name: str, arguments: dict[str, Any] | str | None) -> ToolCall

Build a :class:ToolCall from Python-native arguments.

Most call sites have a dict they need to ship as the LiteLLM-spec JSON string. This helper handles the encoding once so emission sites stay terse: ToolCall.from_arguments(id=…, name=…, arguments={"x": 1}).

Usage.uncached_input_tokens

Input tokens that were neither written to nor read from cache.

Attributes

MetadataValue