
Iterators in Go agents: iter.Seq, range-over-func, and stopping early
Streams and runs as iterators: yield, break, cleanup in a defer, and the mistakes that panic.
The whole loopAn agent produces things over time: the pieces of a streamed reply, then a tool call, then its result, then the next turn.
Since Go 1.23, a function can hand such a sequence to an ordinary for ... range loop, and the caller reads it as it is made.
The companion code uses that shape throughout: a model’s stream is an iter.Seq2[Delta, error], one piece of a reply per step, and a whole run is an iter.Seq[Event], one thing that happened per step.
This page covers how these iterators work and the patterns an agent needs, with code from the companion repository, where it is tested; the streaming guide shows the server-sent events reader built the same way, and Streams in Go explains the io.Reader and bufio.Scanner underneath it.
An iterator is a function
The iter package defines two types (an iterator is a function that hands values to a range loop one at a time).
The square brackets hold type parameters: iter.Seq[Event] is a sequence whose values are Events, and V below stands for whatever type you pick.
iter.Seq[V]isfunc(yield func(V) bool): a function that callsyieldonce for each value.iter.Seq2[K, V]isfunc(yield func(K, V) bool): the same with two values per step, such as a value and an error.
for v := range seq { ... } calls seq with a yield function made from the loop’s body.
Each call to yield runs the body once.
When the body finishes normally, yield returns true and the iterator goes on; when the body leaves the loop, with break, return, or a panic, yield returns false, and the iterator must stop.
Calling yield again after it returned false is a bug the runtime catches: the program panics with “range function continued iteration after function for loop body returned false”.
The iterator runs in the caller’s goroutine, and nothing happens until the caller ranges over it. There is no channel to close and no goroutine to stop, unless the iterator starts one itself.
Writing one: a whole reply as a stream
The agent’s loop reads model replies as streams. A model that returns a whole reply at once, such as a scripted model in a test, is adapted with a few lines:
// Streamed turns a Model into a StreamModel whose stream holds the
// whole reply at once, so scripted models and saved replies work with
// the streaming loop.
func Streamed(m Model) StreamModel { return streamed{m} }
type streamed struct{ m Model }
func (s streamed) Stream(ctx context.Context,
req Request) iter.Seq2[Delta, error] {
return func(yield func(Delta, error) bool) {
reply, err := s.m.Generate(ctx, req)
if err != nil {
yield(Delta{Usage: &reply.Usage}, err)
return
}
msg := reply.Message
ds := []Delta{{Text: msg.Content,
ReasoningDetails: msg.ReasoningDetails}}
for i, c := range msg.ToolCalls {
ds = append(ds, Delta{Call: &CallDelta{Index: i, ID: c.ID,
Name: c.Name, Arguments: string(c.Arguments)}})
}
ds = append(ds, Delta{FinishReason: reply.FinishReason,
Usage: &reply.Usage})
for _, d := range ds {
if !yield(d, nil) {
return
}
}
}
}
Two habits are worth copying.
An error is a step of its own, yield(Delta{...}, err), followed by return; the delta beside it carries the usage, since a failed call can still have cost something.
And every later yield checks its result, returning as soon as the caller has stopped reading.
Reading one: values and errors
A caller of an iter.Seq2[Delta, error] checks the error on every step, the way it would check the error from any call:
// Collect assembles a whole reply from a stream, as the loop does, for
// code that wants the reply rather than its pieces, such as a test of
// an adapter's stream (chapter 11).
func Collect(stream iter.Seq2[Delta, error]) (Reply, error) {
var a assembler
for d, err := range stream {
if err != nil { // the error's Delta may carry usage and ID
if d.Usage != nil {
a.reply.Usage = *d.Usage
}
id := cmp.Or(d.ID, a.reply.ID)
return Reply{Usage: a.reply.Usage, ID: id}, err
}
if d.Reset { // a retry started over (chapter 12)
a = assembler{}
continue
}
if err := a.add(d); err != nil {
return Reply{Usage: a.reply.Usage}, err
}
}
return a.result(), nil
}
Collect assembles a whole reply from a stream, for code that wants the reply and not its pieces.
It stops at the first error, keeping the usage the failed step reported, and it handles one more kind of step: a Delta with its Reset flag set, which a retry sends when a stream starts over, meaning the pieces before it belonged to a failed attempt and are discarded. See retries and observability.
Wrapping one: middleware with cleanup
Because a stream is just a function, wrapping one is a function too. The cost meter wraps a model’s stream, passes each step through, and records what the call used:
// Wrap returns a ModelWare that records each attempt's usage in m. It
// goes inside the retries, so failed attempts are recorded too.
func (m *Meter) Wrap(next StreamModel) StreamModel {
return StreamFunc(func(ctx context.Context,
req Request) iter.Seq2[Delta, error] {
return func(yield func(Delta, error) bool) {
var c Metered
defer func() { m.record(NameFrom(ctx), req, c) }()
for d, err := range next.Stream(ctx, req) {
if d.Usage != nil {
c.Usage = *d.Usage
}
c.ID = cmp.Or(d.ID, c.ID)
c.Failed = err != nil
if !yield(d, err) {
return
}
}
}
})
}
The recording is in a defer, so it runs whether the stream ended, failed, or the caller stopped reading partway.
That is the iterator’s version of a Close method: cleanup in a deferred call inside the iterator runs when the caller’s loop ends, for whatever reason, without the caller doing anything.
Stopping early
A whole run is an iterator of events, so a caller that only wants the result reads until the last event:
// Run runs the agent loop on a Model that doesn't stream: it is
// RunStream over Streamed(m), so there is one loop for both.
func Run(ctx context.Context, m Model, tools []Tool,
msgs []Message, lim Limits) (Result, error) {
return RunStream(ctx, Streamed(m), tools, msgs, lim)
}
// RunStream runs the loop and returns when it ends, for callers that
// don't need to see the run as it happens.
func RunStream(ctx context.Context, m StreamModel, tools []Tool,
msgs []Message, lim Limits) (Result, error) {
for ev := range Events(ctx, m, tools, msgs, lim) {
if ev.Kind == StoppedEvent {
return ev.Result, ev.Err
}
}
return Result{}, errNoStop // Events always ends with StoppedEvent
}
A return inside the loop ends the range; the run’s iterator sees its yield return false and stops.
The run’s iterator cancels its own context when that happens, through a deferred cancel, so breaking out of the loop stops the model call in progress.
Stopping a run is as simple as leaving the loop that reads it, and TestBreakOutOfEvents checks that breaking out stops the run.
Iterators with goroutines inside
An iterator can start goroutines, as long as it stops them before it returns. The fan-in that merges several runs’ events into one iterator does exactly that, and the goroutines page walks through how it releases every goroutine when the caller stops reading. That is the trade-off between the two ways of producing values over time: an iterator with no goroutine inside can’t leak one, while a channel always needs a goroutine on the other end, and someone responsible for stopping it.
The standard library’s iterators
Go 1.23 added iterators across the standard library, and they combine with the ones you write:
maps.Keys(m)andmaps.Values(m)iterate over a map.slices.Collect(seq)gathers an iterator into a slice, andslices.Sorted(seq)gathers and sorts it.slices.All,slices.Values, andslices.Backwarditerate over a slice.iter.Pullturns an iterator into anextfunction and astopfunction, for code that has to step through two sequences at once; callstopwhen you finish early, or the iterator is never released.
The agent’s tool lookup uses two of them in one line, slices.Sorted(maps.Keys(byName)), to list the real tools in a stable order for the error the model reads; the errors page shows it.
Mistakes to avoid
- Calling
yieldafter it returned false. The runtime panics. Check its result on every call, and return when it is false. - Ignoring the error in an
iter.Seq2[T, error]. It is a step like any other; check it before using the value. - Cleanup after the loop instead of in a
defer. Code after the lastyielddoesn’t run when the caller breaks out; a deferred call does. - Ranging over the same iterator twice. An iterator like
Eventsruns its work each time it is ranged over: ranging twice runs the agent twice. Keep the results, not the iterator. - A goroutine inside an iterator, left running. If an iterator starts goroutines, it stops them and waits for them before it returns.
Try it
From the code directory of a clone of the companion repository:
$ go test ./ch06/agent -run 'TestBreakOutOfEvents|TestEventsOrder|TestStreamErrorDropsPartialReply' -v
- In
ch06/agent/stream_test.go, changewatchedStreamto callyieldthree times without checking its result, runTestBreakOutOfEvents, and read the panic. - Write a filter, a function that takes an
iter.Seq[Event]and returns one holding only the tool-call events, and range over it in place ofEvents. - Range over
Eventstwice with the same arguments and a scripted model, and count the model calls.
func Streamed(m Model) StreamModel
Streamed turns a Model into a StreamModel whose stream holds the whole reply at once, so scripted models and saved replies work with the streaming loop.
Streamed turns a Model into a StreamModel whose stream holds the whole reply at once, so scripted models and saved replies work with the streaming loop. func Streamed(m Model) StreamModel. In ch06/agent/stream.go, line 55.
type Model interface {
Generate(ctx context.Context, req Request) (Reply, error)
}
Model is anything that can produce the next reply in a conversation: a scripted fake, or a real model such as the OpenRouter client.
Model is anything that can produce the next reply in a conversation: a scripted fake, or a real model such as the OpenRouter client. type Model interface { Generate(ctx context.Context, req Request) (Reply, error) }. In ch06/agent/types.go, line 13.
type StreamModel interface {
Stream(ctx context.Context, req Request) iter.Seq2[Delta, error]
}
A StreamModel produces the next reply as a stream of pieces.
A StreamModel produces the next reply as a stream of pieces. type StreamModel interface { Stream(ctx context.Context, req Request) iter.Seq2[Delta, error] }. In ch06/agent/stream.go, line 44.
type streamed struct{ m Model }
type streamed struct{ m Model }. In ch06/agent/stream.go, line 57.
func (s streamed) Stream(ctx context.Context,
req Request) iter.Seq2[Delta, error]
func (s streamed) Stream(ctx context.Context, req Request) iter.Seq2[Delta, error]. In ch06/agent/stream.go, line 59.
var ctx context.Context
var ctx context.Context. In ch06/agent/stream.go, line 59.
package context ("context")
Package context defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes.
Package context defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes. package context ("context"). Documented at pkg.go.dev, context.
type Context interface {
// Deadline returns the time when work done on behalf of this context
// should be canceled. Deadline returns ok==false when no deadline is
// set. Successive calls to Deadline return the same results.
Deadline() (deadline time.Time, ok bool)
// Done returns a channel that's closed when work done on behalf of this
// context should be canceled. Done may return nil if this context can
// never be canceled. Successive calls to Done return the same value.
// The close of the Done channel may happen asynchronously,
// ...
}
A Context carries a deadline, a cancellation signal, and other values across API boundaries.
A Context carries a deadline, a cancellation signal, and other values across API boundaries. type Context interface { // Deadline returns the time when work done on behalf of this context // should be canceled. Deadline returns ok==false when no deadline is // set. Successive calls to Deadline return the same results. Deadline() (deadline time.Time, ok bool) // Done returns a channel that's closed when work done on behalf of this // context should be canceled. Done may return nil if this context can // never be canceled. Successive calls to Done return the same value. // The close of the Done channel may happen asynchronously, // ... }. Documented at pkg.go.dev, context.Context.
type Request struct {
Messages []Message
Tools []ToolDef
MaxTokens int // longest reply; 0 means the provider's default
}
Request is what the harness sends the model on each turn.
Request is what the harness sends the model on each turn. type Request struct { Messages []Message Tools []ToolDef MaxTokens int // longest reply; 0 means the provider's default }. In ch06/agent/types.go, line 24.
package iter ("iter")
Package iter provides basic definitions and operations related to iterators over sequences.
Package iter provides basic definitions and operations related to iterators over sequences. package iter ("iter"). Documented at pkg.go.dev, iter.
type Seq2[K, V any] func(yield func(K, V) bool)
Seq2 is an iterator over sequences of pairs of values, most commonly key-value pairs.
Seq2 is an iterator over sequences of pairs of values, most commonly key-value pairs. type Seq2[K, V any] func(yield func(K, V) bool). Documented at pkg.go.dev, iter.Seq2.
type Delta struct {
Text string // a piece of the reply's text
Call *CallDelta // a piece of one tool call
FinishReason string // why the reply ended, when it has
Usage *Usage // what the call consumed, at the end
// ReasoningDetails holds reasoning records, a JSON array, which the
// assembler joins into the reply's ReasoningDetails.
ReasoningDetails jsontext.Value
}
A Delta is one piece of a streamed reply.
A Delta is one piece of a streamed reply. type Delta struct { Text string // a piece of the reply's text Call *CallDelta // a piece of one tool call FinishReason string // why the reply ended, when it has Usage *Usage // what the call consumed, at the end // ReasoningDetails holds reasoning records, a JSON array, which the // assembler joins into the reply's ReasoningDetails. ReasoningDetails jsontext.Value }. In ch06/agent/stream.go, line 17.
type error interface {
Error() string
}
The error built-in interface type is the conventional interface for representing an error condition, with the nil value representing no error.
The error built-in interface type is the conventional interface for representing an error condition, with the nil value representing no error. type error interface { Error() string }. Documented at pkg.go.dev, builtin.error.
var yield func(Delta, error) bool
var yield func(Delta, error) bool. In ch06/agent/stream.go, line 61.
type bool bool
bool is the set of boolean values, true and false.
bool is the set of boolean values, true and false. type bool bool. Documented at pkg.go.dev, builtin.bool.
func (Model) Generate(ctx context.Context, req Request) (Reply, error)
func (Model) Generate(ctx context.Context, req Request) (Reply, error). In ch06/agent/types.go, line 14.
var nil Type
nil is a predeclared identifier representing the zero value for a pointer, channel, func, interface, map, or slice type.
nil is a predeclared identifier representing the zero value for a pointer, channel, func, interface, map, or slice type. var nil Type. Documented at pkg.go.dev, builtin.nil.
field Usage *Usage
what the call consumed, at the end
what the call consumed, at the end field Usage *Usage. In ch06/agent/stream.go, line 21.
field Usage Usage
tokens counted for this call, and its cost
tokens counted for this call, and its cost field Usage Usage. In ch06/agent/types.go, line 71.
field Message Message
the assistant's text, tool calls, or both
the assistant's text, tool calls, or both field Message Message. In ch06/agent/types.go, line 69.
field Text string
a piece of the reply's text
a piece of the reply's text field Text string. In ch06/agent/stream.go, line 18.
field Content string
empty when the message has tool calls
empty when the message has tool calls field Content string. In ch06/agent/types.go, line 35.
field ReasoningDetails jsontext.Value
ReasoningDetails holds reasoning records, a JSON array, which the assembler joins into the reply's ReasoningDetails.
ReasoningDetails holds reasoning records, a JSON array, which the assembler joins into the reply's ReasoningDetails. field ReasoningDetails jsontext.Value. In ch06/agent/stream.go, line 25.
field ReasoningDetails jsontext.Value
ReasoningDetails is a reasoning model's record of its thinking, sent back unchanged with the tool calls it led to (chapter 4).
ReasoningDetails is a reasoning model's record of its thinking, sent back unchanged with the tool calls it led to (chapter 4). field ReasoningDetails jsontext.Value. In ch06/agent/types.go, line 42.
field ToolCalls []ToolCall
set on assistant messages that call tools
set on assistant messages that call tools field ToolCalls []ToolCall. In ch06/agent/types.go, line 36.
func append(slice []Type, elems ...Type) []Type
The append built-in function appends elements to the end of a slice.
The append built-in function appends elements to the end of a slice. func append(slice []Type, elems ...Type) []Type. Documented at pkg.go.dev, builtin.append.
field Call *CallDelta
a piece of one tool call
a piece of one tool call field Call *CallDelta. In ch06/agent/stream.go, line 19.
type CallDelta struct {
Index int // which tool call in the reply this belongs to
ID string // set on the first piece only
Name string // set on the first piece only
Arguments string // the next fragment of the arguments
}
A CallDelta is a piece of one tool call.
A CallDelta is a piece of one tool call. type CallDelta struct { Index int // which tool call in the reply this belongs to ID string // set on the first piece only Name string // set on the first piece only Arguments string // the next fragment of the arguments }. In ch06/agent/stream.go, line 31.
field Index int
which tool call in the reply this belongs to
which tool call in the reply this belongs to field Index int. In ch06/agent/stream.go, line 32.
field ID string
set on the first piece only
set on the first piece only field ID string. In ch06/agent/stream.go, line 33.
field Name string
set on the first piece only
set on the first piece only field Name string. In ch06/agent/stream.go, line 34.
field Arguments string
the next fragment of the arguments
the next fragment of the arguments field Arguments string. In ch06/agent/stream.go, line 35.
type string string
string is the set of all strings of 8-bit bytes, conventionally but not necessarily representing UTF-8-encoded text.
string is the set of all strings of 8-bit bytes, conventionally but not necessarily representing UTF-8-encoded text. type string string. Documented at pkg.go.dev, builtin.string.
field Arguments jsontext.Value
JSON the model wrote; may be invalid
JSON the model wrote; may be invalid field Arguments jsontext.Value. In ch06/agent/types.go, line 60.
field FinishReason string
why the reply ended, when it has
why the reply ended, when it has field FinishReason string. In ch06/agent/stream.go, line 20.
field FinishReason string
why the model stopped: "stop", "length", ...
why the model stopped: "stop", "length", ... field FinishReason string. In ch06/agent/types.go, line 70.
func Collect(stream iter.Seq2[Delta, error]) (Reply, error)
Collect assembles a whole reply from a stream, as the loop does, for code that wants the reply rather than its pieces, such as a test of an adapter's stream (chapter 11).
Collect assembles a whole reply from a stream, as the loop does, for code that wants the reply rather than its pieces, such as a test of an adapter's stream (chapter 11). func Collect(stream iter.Seq2[Delta, error]) (Reply, error). In ch15/agent/stream.go, line 201.
var stream iter.Seq2[Delta, error]
var stream iter.Seq2[Delta, error]. In ch15/agent/stream.go, line 201.
package iter ("iter")
Package iter provides basic definitions and operations related to iterators over sequences.
Package iter provides basic definitions and operations related to iterators over sequences. package iter ("iter"). Documented at pkg.go.dev, iter.
type Delta struct {
Text string // a piece of the reply's text
Call *CallDelta // a piece of one tool call
FinishReason string // why the reply ended, when it has
Usage *Usage // what the call consumed, at the end
// ReasoningDetails holds reasoning records, a JSON array, which the
// assembler joins into the reply's ReasoningDetails.
ReasoningDetails jsontext.Value
// ...
}
A Delta is one piece of a streamed reply.
A Delta is one piece of a streamed reply. type Delta struct { Text string // a piece of the reply's text Call *CallDelta // a piece of one tool call FinishReason string // why the reply ended, when it has Usage *Usage // what the call consumed, at the end // ReasoningDetails holds reasoning records, a JSON array, which the // assembler joins into the reply's ReasoningDetails. ReasoningDetails jsontext.Value // ... }. In ch15/agent/stream.go, line 18.
type Reply struct {
Message Message // the assistant's text, tool calls, or both
FinishReason string // why the model stopped: "stop", "length", ...
Usage Usage // tokens counted for this call, and its cost
// ID is the provider's ID for this generation, such as OpenRouter's
// "gen-...", kept even when a stream failed, so its cost can be
// looked up afterward (chapter 12).
ID string
}
Reply is the model's answer to one Request.
Reply is the model's answer to one Request. type Reply struct { Message Message // the assistant's text, tool calls, or both FinishReason string // why the model stopped: "stop", "length", ... Usage Usage // tokens counted for this call, and its cost // ID is the provider's ID for this generation, such as OpenRouter's // "gen-...", kept even when a stream failed, so its cost can be // looked up afterward (chapter 12). ID string }. In ch15/agent/types.go, line 99.
type assembler struct {
text strings.Builder
calls []ToolCall
args [][]byte // each call's arguments so far
reasoning []jsontext.Value // reasoning records, in order
reply Reply
}
An assembler builds a Reply from the pieces of a stream.
An assembler builds a Reply from the pieces of a stream. type assembler struct { text strings.Builder calls []ToolCall args [][]byte // each call's arguments so far reasoning []jsontext.Value // reasoning records, in order reply Reply }. In ch15/agent/stream.go, line 108.
field Usage *Usage
what the call consumed, at the end
what the call consumed, at the end field Usage *Usage. In ch15/agent/stream.go, line 22.
field Usage Usage
tokens counted for this call, and its cost
tokens counted for this call, and its cost field Usage Usage. In ch15/agent/types.go, line 102.
package cmp ("cmp")
Package cmp provides types and functions related to comparing ordered values.
Package cmp provides types and functions related to comparing ordered values. package cmp ("cmp"). Documented at pkg.go.dev, cmp.
func Or[T comparable](vals ...T) T
Or returns the first of its arguments that is not equal to the zero value.
Or returns the first of its arguments that is not equal to the zero value. func Or[T comparable](vals ...T) T. Documented at pkg.go.dev, cmp.Or.
field ID string
ID is the generation's ID, which the assembler copies into Reply.ID (chapter 12).
ID is the generation's ID, which the assembler copies into Reply.ID (chapter 12). field ID string. In ch15/agent/stream.go, line 34.
field ID string
ID is the provider's ID for this generation, such as OpenRouter's "gen-...", kept even when a stream failed, so its cost can be looked up afterward (chapter 12).
ID is the provider's ID for this generation, such as OpenRouter's "gen-...", kept even when a stream failed, so its cost can be looked up afterward (chapter 12). field ID string. In ch15/agent/types.go, line 107.
field Reset bool
Reset says that every Delta before this one belonged to an attempt that failed and is being retried: discard them (chapter 12).
Reset says that every Delta before this one belonged to an attempt that failed and is being retried: discard them (chapter 12). field Reset bool. In ch15/agent/stream.go, line 39.
func (a *assembler) add(d Delta) error
add takes the next piece of the stream.
add takes the next piece of the stream. func (a *assembler) add(d Delta) error. In ch15/agent/stream.go, line 117.
func (a *assembler) result() Reply
result returns the assembled Reply, once the stream has ended.
result returns the assembled Reply, once the stream has ended. func (a *assembler) result() Reply. In ch15/agent/stream.go, line 177.
type Meter struct {
mu sync.Mutex
calls []Metered
last map[string]prev // each agent's previous call
}
A Meter records the usage of every model call, attempt by attempt, with the agent that made it and what the request added since that agent's previous call, so a run's cost can be broken down by agent and by turn.
A Meter records the usage of every model call, attempt by attempt, with the agent that made it and what the request added since that agent's previous call, so a run's cost can be broken down by agent and by turn. type Meter struct { mu sync.Mutex calls []Metered last map[string]prev // each agent's previous call }. In ch15/agent/meter.go, line 22.
func (m *Meter) Wrap(next StreamModel) StreamModel
Wrap returns a ModelWare that records each attempt's usage in m.
Wrap returns a ModelWare that records each attempt's usage in m. func (m *Meter) Wrap(next StreamModel) StreamModel. In ch15/agent/meter.go, line 51.
type StreamModel interface {
Stream(ctx context.Context, req Request) iter.Seq2[Delta, error]
}
A StreamModel produces the next reply as a stream of pieces.
A StreamModel produces the next reply as a stream of pieces. type StreamModel interface { Stream(ctx context.Context, req Request) iter.Seq2[Delta, error] }. In ch15/agent/stream.go, line 58.
type StreamFunc func(ctx context.Context,
req Request) iter.Seq2[Delta, error]
StreamFunc is a function that is a StreamModel, as http.HandlerFunc is a Handler, so a wrapper can return a closure.
StreamFunc is a function that is a StreamModel, as http.HandlerFunc is a Handler, so a wrapper can return a closure. type StreamFunc func(ctx context.Context, req Request) iter.Seq2[Delta, error]. In ch15/agent/middleware.go, line 49.
var ctx context.Context
var ctx context.Context. In ch15/agent/meter.go, line 52.
package context ("context")
Package context defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes.
Package context defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes. package context ("context"). Documented at pkg.go.dev, context.
type Request struct {
Messages []Message
Tools []ToolDef
MaxTokens int // longest reply allowed; 0 means the provider's default
// ResponseFormat, if set, is a JSON Schema the reply's text must
// match, for calls that use no tools, such as a compaction summary
// (chapter 8).
ResponseFormat jsontext.Value
// ...
}
Request is what the harness sends the model on each turn.
Request is what the harness sends the model on each turn. type Request struct { Messages []Message Tools []ToolDef MaxTokens int // longest reply allowed; 0 means the provider's default // ResponseFormat, if set, is a JSON Schema the reply's text must // match, for calls that use no tools, such as a compaction summary // (chapter 8). ResponseFormat jsontext.Value // ... }. In ch15/agent/types.go, line 25.
package iter ("iter")
Package iter provides basic definitions and operations related to iterators over sequences.
Package iter provides basic definitions and operations related to iterators over sequences. package iter ("iter"). Documented at pkg.go.dev, iter.
var yield func(Delta, error) bool
var yield func(Delta, error) bool. In ch15/agent/meter.go, line 54.
type Metered struct {
Agent string
Call int // the agent's call number, from 1
ID string // the generation's ID, to look a failure up
Usage Usage
Growth int // prompt tokens more than the agent's previous call
ResultBytes int // bytes of tool results added since that call
Failed bool
}
A Metered is one attempt of one model call.
A Metered is one attempt of one model call. type Metered struct { Agent string Call int // the agent's call number, from 1 ID string // the generation's ID, to look a failure up Usage Usage Growth int // prompt tokens more than the agent's previous call ResultBytes int // bytes of tool results added since that call Failed bool }. In ch15/agent/meter.go, line 35.
func (m *Meter) record(agent string, req Request, c Metered)
record adds one attempt to the meter, filling in what c, holding the attempt's usage, ID, and outcome, leaves out.
record adds one attempt to the meter, filling in what c, holding the attempt's usage, ID, and outcome, leaves out. func (m *Meter) record(agent string, req Request, c Metered). In ch15/agent/meter.go, line 75.
func NameFrom(ctx context.Context) string
NameFrom returns the agent's name from the context, or "".
NameFrom returns the agent's name from the context, or "". func NameFrom(ctx context.Context) string. In ch15/agent/log.go, line 26.
func (StreamModel) Stream(ctx context.Context, req Request) iter.Seq2[Delta, error]
func (StreamModel) Stream(ctx context.Context, req Request) iter.Seq2[Delta, error]. In ch15/agent/stream.go, line 59.
field ID string
the generation's ID, to look a failure up
the generation's ID, to look a failure up field ID string. In ch15/agent/meter.go, line 38.
package cmp ("cmp")
Package cmp provides types and functions related to comparing ordered values.
Package cmp provides types and functions related to comparing ordered values. package cmp ("cmp"). Documented at pkg.go.dev, cmp.
func Run(ctx context.Context, m Model, tools []Tool,
msgs []Message, lim Limits) (Result, error)
Run runs the agent loop on a Model that doesn't stream: it is RunStream over Streamed(m), so there is one loop for both.
Run runs the agent loop on a Model that doesn't stream: it is RunStream over Streamed(m), so there is one loop for both. func Run(ctx context.Context, m Model, tools []Tool, msgs []Message, lim Limits) (Result, error). In ch06/agent/loop.go, line 16.
var ctx context.Context
var ctx context.Context. In ch06/agent/loop.go, line 16.
package context ("context")
Package context defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes.
Package context defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes. package context ("context"). Documented at pkg.go.dev, context.
type Tool struct {
Def ToolDef
Run func(ctx context.Context, args jsontext.Value) (string, error)
}
Tool is the harness's side of a tool: the definition the model sees, and the Go function that runs it.
Tool is the harness's side of a tool: the definition the model sees, and the Go function that runs it. type Tool struct { Def ToolDef Run func(ctx context.Context, args jsontext.Value) (string, error) }. In ch06/agent/types.go, line 100.
type Message struct {
Role string // "system", "user", "assistant", or "tool"
Content string // empty when the message has tool calls
ToolCalls []ToolCall // set on assistant messages that call tools
ToolCallID string // on tool messages: the call this answers
IsError bool // set on tool messages that report a failure
// ReasoningDetails is a reasoning model's record of its thinking,
// sent back unchanged with the tool calls it led to (chapter 4).
ReasoningDetails jsontext.Value
}
Message is one entry in the conversation.
Message is one entry in the conversation. type Message struct { Role string // "system", "user", "assistant", or "tool" Content string // empty when the message has tool calls ToolCalls []ToolCall // set on assistant messages that call tools ToolCallID string // on tool messages: the call this answers IsError bool // set on tool messages that report a failure // ReasoningDetails is a reasoning model's record of its thinking, // sent back unchanged with the tool calls it led to (chapter 4). ReasoningDetails jsontext.Value }. In ch06/agent/types.go, line 33.
type Limits struct {
MaxTurns int
MaxCost float64 // in dollars; 0 means no cost budget (chapter 5)
}
Limits holds the settings that bound a run.
Limits holds the settings that bound a run. type Limits struct { MaxTurns int MaxCost float64 // in dollars; 0 means no cost budget (chapter 5) }. In ch06/agent/types.go, line 110.
type Result struct {
Messages []Message
Usage Usage // the run's total, over every model call (chapter 5)
}
Result is what a run produced, whether or not it ended in an error.
Result is what a run produced, whether or not it ended in an error. type Result struct { Messages []Message Usage Usage // the run's total, over every model call (chapter 5) }. In ch06/agent/types.go, line 116.
func RunStream(ctx context.Context, m StreamModel, tools []Tool,
msgs []Message, lim Limits) (Result, error)
RunStream runs the loop and returns when it ends, for callers that don't need to see the run as it happens.
RunStream runs the loop and returns when it ends, for callers that don't need to see the run as it happens. func RunStream(ctx context.Context, m StreamModel, tools []Tool, msgs []Message, lim Limits) (Result, error). In ch06/agent/loop.go, line 23.
var ctx context.Context
var ctx context.Context. In ch06/agent/loop.go, line 23.
func Events(ctx context.Context, m StreamModel, tools []Tool,
msgs []Message, lim Limits) iter.Seq[Event]
Events runs the agent loop and reports what happens as it happens.
Events runs the agent loop and reports what happens as it happens. func Events(ctx context.Context, m StreamModel, tools []Tool, msgs []Message, lim Limits) iter.Seq[Event]. In ch06/agent/events.go, line 48.
const StoppedEvent
the run ended; always last
the run ended; always last const StoppedEvent. In ch06/agent/events.go, line 22.
field Result Result
StoppedEvent: the run, as far as it went
StoppedEvent: the run, as far as it went field Result Result. In ch06/agent/events.go, line 32.
field Err error
StoppedEvent: why it stopped; nil for an answer
StoppedEvent: why it stopped; nil for an answer field Err error. In ch06/agent/events.go, line 33.
var errNoStop = errors.New("agent: the run ended without a stop event")
var errNoStop = errors.New("agent: the run ended without a stop event"). In ch06/agent/loop.go, line 35.
func Pull[V any](seq Seq[V]) (next func() (V, bool), stop func())
Pull converts the “push-style” iterator sequence seq into a “pull-style” iterator accessed by the two functions next and stop.
Pull converts the “push-style” iterator sequence seq into a “pull-style” iterator accessed by the two functions next and stop. func Pull[V any](seq Seq[V]) (next func() (V, bool), stop func()). Documented at pkg.go.dev, iter.Pull.