Skip to content

latent.agents.invoke

Buffered agent consumption — invoke() API on BaseAgent.

invoke() consumes an agent's event stream and produces a list of Step records, one per agent loop iteration (bracketed by StepBoundary), plus the terminal step's text as the customer-facing result.

Tool-call shape — locked down to LiteLLM ChatCompletion types:

Step.tool_calls    : list[ToolCall]  (same dataclass yielded by stream())
Step.tool_messages : list[Message]   (role="tool" rows)

Same :class:ToolCall and :class:Message types as conversation history. Persisting a step is pure copy:

history.append(Message(role="assistant", tool_calls=step.tool_calls))
history.extend(step.tool_messages)

No translation, no parallel TypedDicts, no internal "combined call+result" record (ToolCallRecord was removed in 5.4.0).

Classes

InvokeResult

InvokeResult(text: str, steps: list[Step])

Return value of BaseAgent.invoke().

text is the terminal step's text — derived from steps[-1] (or whichever step is_terminal_step returns True for) after the consumption loop completes. Never from a live mutable buffer cleared on a boundary signal. Live-clear was the discipline in PR #75 of the consuming runtime, which shipped a regression when the boundary ordering didn't match expectations — structural derivation is insulated.

steps carries the full per-iteration breakdown. Non-terminal step text is preserved here (for debugging / dataset views) but is not included in text.

Consumers that need raw events use BaseAgent.stream() directly — invoke() is the buffered API; bypassing its structured fields would defeat the purpose.

Step

Step(step: int, reasoning: str = '', text: str = '', tool_calls: list[ToolCall] = list(), tool_messages: list[Message] = list())

One iteration of the agent loop, bracketed by StepBoundary.

Aggregates everything emitted between two consecutive StepBoundary events (or between the final StepBoundary and end-of-stream): reasoning content, customer-facing text, the tool calls the LLM invoked, and the tool result messages those calls produced.

Whether a step is "terminal" (i.e. its text is the customer-facing answer) is determined by BaseAgent.is_terminal_step(step). The flag is not stored on the dataclass to avoid drift with the rule — derive on demand.

Distinct from latent.agents.guided.journeys.Step which is a GuidedAgent journey configuration with different fields.

Attributes

StreamWrapper