Documentation
¶
Overview ¶
Package react implements the ReAct (Reasoning + Acting) agentic pattern on top of the core client. It drives an iterative tool-execution loop in which the LLM alternates between reasoning steps and tool calls until it produces a final answer, which is then parsed into a caller-defined Go type T.
The main entry point is New, which wraps a configured client.Client and returns a type-safe ReAct agent. Use [Execute] to run the loop for a given prompt. Behavior can be tuned with WithMaxIterations and WithStopOnError.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Option ¶
Option is a functional option for configuring ReAct.
func WithMaxIterations ¶
WithMaxIterations sets the maximum number of tool execution iterations. Default: 10
func WithStopOnError ¶
WithStopOnError configures whether to stop execution on tool errors. Default: false
type ReAct ¶
type ReAct[T any] struct { // contains filtered or unexported fields }
ReAct is a type-safe ReAct (Reasoning + Acting) pattern implementation. The generic parameter T defines the expected structure of the final answer.
This pattern automatically handles the tool execution loop with reasoning, and parses the final result into type T.
Example:
type MathResult struct {
Answer int `json:"answer"`
Steps string `json:"steps"`
}
agent := react.New[MathResult](baseClient)
result, err := agent.Execute(ctx, "What is 42 * 17?")
fmt.Printf("Answer: %d\n", result.Data.Answer)
func New ¶
New creates a new type-safe ReAct pattern that wraps a base client. The base client should be configured with memory, tools, and observer.
Memory is required for the ReAct pattern to work (the LLM needs to see tool results). If the client is in stateless mode, this function will return an error.
The generic parameter T defines the expected structure of the final answer. A JSON schema is automatically generated from T and injected into the system prompt to guide the LLM's final response format.
Example:
type MathResult struct {
Answer int `json:"answer" jsonschema:"required"`
Explanation string `json:"explanation" jsonschema:"required"`
}
baseClient, _ := client.New(
provider,
client.WithMemory(memory),
client.WithTools(tool1, tool2),
client.WithObserver(observer),
)
agent, _ := react.New[MathResult](
baseClient,
react.WithMaxIterations(5),
react.WithStopOnError(true),
)
func (*ReAct[T]) Execute ¶
func (r *ReAct[T]) Execute(ctx context.Context, prompt string) (*overview.StructuredOverview[T], error)
Execute runs the ReAct loop for the given prompt and returns the final answer parsed into type T, along with execution statistics.
The loop sends the prompt to the LLM, executes any requested tool calls, and feeds their results back to the LLM until it produces a response with no further tool calls. That response is then unmarshalled into T. If the initial parse fails, Execute sends one follow-up request asking for plain JSON before giving up. The loop is capped at the configured maximum number of iterations (default 10).
Returns an error if the provider call fails, the context is canceled, tool execution fails with stopOnError enabled, or the maximum iteration count is reached without a parseable final answer.
Example:
result, err := agent.Execute(ctx, "What is 42 * 17?")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Answer: %d, steps: %s\n", result.Data.Answer, result.Data.Steps)
func (*ReAct[T]) ExecuteStream ¶ added in v0.4.0
ExecuteStream starts the ReAct reasoning loop with streaming output. Unlike Execute(), which blocks until completion, ExecuteStream returns immediately with a ReactStream that yields events as the agent works.
The stream must be consumed (either via Iter() or Collect()) to avoid resource leaks. Internally, each LLM call uses StreamMessage / StreamContinueConversation while tool execution remains synchronous between LLM calls.
Returns an error immediately (before any streaming) only if the prompt is empty or memory is not configured. All other errors are delivered as ReactEventError events through the stream.
Example:
stream, err := agent.ExecuteStream(ctx, "What are the latest advances in fusion energy?")
if err != nil {
log.Fatal(err)
}
for event, err := range stream.Iter() {
if err != nil { log.Fatal(err) }
switch event.Type {
case react.ReactEventContent:
fmt.Print(event.Content)
case react.ReactEventFinalAnswer:
fmt.Printf("\nResult: %+v\n", event.Result)
}
}
type ReactEvent ¶ added in v0.4.0
type ReactEvent[T any] struct { // Type identifies what kind of event this is. Type ReactEventType `json:"type"` // Iteration is the current 1-based iteration number within the ReAct loop. Iteration int `json:"iteration"` // Content carries a text delta (ReactEventContent) or the full accumulated // content of the final response (ReactEventFinalAnswer). Content string `json:"content,omitempty"` // Reasoning carries a reasoning/thinking delta (ReactEventReasoning only). Reasoning string `json:"reasoning,omitempty"` // ToolName is the name of the tool being called or returning a result. // Populated for ReactEventToolCall and ReactEventToolResult. ToolName string `json:"tool_name,omitempty"` // ToolInput is the JSON-encoded arguments passed to the tool. // Populated for ReactEventToolCall only. ToolInput string `json:"tool_input,omitempty"` // ToolOutput is the string result returned by the tool. // Populated for ReactEventToolResult only. ToolOutput string `json:"tool_output,omitempty"` // Result is the strongly-typed parsed final answer. // Populated only for ReactEventFinalAnswer events. Result *T `json:"result,omitempty"` // Err holds the error for ReactEventError events. // It is not marshaled to JSON; callers should use the error channel of the iterator. Err error `json:"-"` }
ReactEvent represents a single event from the ReAct agent loop. Each event carries exactly one type of payload, identified by the Type field.
type ReactEventType ¶ added in v0.4.0
type ReactEventType string
ReactEventType identifies the phase of the ReAct loop that produced an event.
const ( // ReactEventReasoning indicates the LLM is producing reasoning/thinking tokens. ReactEventReasoning ReactEventType = "reasoning" // ReactEventContent indicates a content delta from the LLM response. ReactEventContent ReactEventType = "content" // ReactEventToolCall indicates the LLM has decided to call a tool. // Emitted once per tool call with the complete call information after the // entire LLM response for that iteration has been consumed. ReactEventToolCall ReactEventType = "tool_call" // ReactEventToolResult indicates a tool has finished executing. // Contains the tool name and its output. ReactEventToolResult ReactEventType = "tool_result" // ReactEventIterationStart signals the beginning of a new reasoning iteration. ReactEventIterationStart ReactEventType = "iteration_start" // ReactEventFinalAnswer indicates the agent has produced a parseable final answer. // The Content field contains the raw response content, and Result contains the parsed T. ReactEventFinalAnswer ReactEventType = "final_answer" // ReactEventError signals an error during execution. // When this event is emitted, the stream is terminated immediately after. ReactEventError ReactEventType = "error" )
type ReactStream ¶ added in v0.4.0
type ReactStream[T any] struct { // contains filtered or unexported fields }
ReactStream wraps the streaming ReAct agent execution loop. It yields ReactEvent values that describe each phase of the agent's work.
The stream must be consumed either via Iter() or Collect() to avoid resource leaks. Breaking out of an Iter() range loop early is safe — the underlying iterator will be abandoned correctly by Go's range-over-func mechanism.
func (*ReactStream[T]) Collect ¶ added in v0.4.0
func (stream *ReactStream[T]) Collect() (*overview.StructuredOverview[T], error)
Collect consumes the entire stream and returns the structured overview, equivalent to what Execute() returns but after streaming all events. Any mid-stream error terminates collection and returns that error.
Use this when you want streaming transport (lower time-to-first-byte) but do not need to process intermediate events.
func (*ReactStream[T]) Iter ¶ added in v0.4.0
func (stream *ReactStream[T]) Iter() iter.Seq2[ReactEvent[T], error]
Iter returns the underlying iterator for range-over-func consumption.
Example:
stream, _ := agent.ExecuteStream(ctx, "Research quantum computing")
for event, err := range stream.Iter() {
if err != nil { log.Fatal(err) }
switch event.Type {
case react.ReactEventContent:
fmt.Print(event.Content) // typewriter effect
case react.ReactEventToolCall:
fmt.Printf("\n[Calling %s]\n", event.ToolName)
case react.ReactEventFinalAnswer:
fmt.Printf("\nResult: %+v\n", event.Result)
}
}