
Interfaces and generics in a Go agent: small interfaces, wrappers, and typed tools
The shapes the rest of the code is built from: one-method interfaces, wrappers, and tools typed by their arguments.
On the agent compass The whole loop, all four stagesAn agent’s code is a few small parts that other code plugs into: a model it calls, tools it runs, and a policy that decides what each request holds. Go gives you two ways to make a part pluggable. An interface says what a value can do, and any type that can do it fits, with no declaration that it does. A generic function works for many types at once, and the compiler checks each use. This page shows both in the companion code, from the one-method interface the loop calls to tools whose JSON schema is generated from a Go struct.
One method: the Model interface
All the loop asks of a model is a reply to the conversation so far. That is the whole interface:
// 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)
}
A type satisfies Model by having a Generate method with that signature, and nothing else.
This is how every interface in Go works, and it is called implicit satisfaction: a type never declares which interfaces it implements, as a class does with implements in Java or C#, and the compiler checks the methods wherever the type is used as the interface.
So a type can satisfy an interface written after it, in a package it has never heard of.
In chapter 12’s code, four types produce replies that way, and the wrappers later on this page satisfy it too:
*agent.Fake, which replays a script of replies, for tests and for runs with no API key;*openrouter.Client, the hand-written client for OpenRouter’s API;*anthropic.Client, the same for Anthropic’s API;*orsdk.Client, an adapter that puts OpenRouter’s official Go SDK behind the same method.
None of them mentions Model.
The loop takes a Model and never learns which one it has, so a test can run the real loop with no network:
// Fake is a Model that replays a script of replies. It picks the next
// reply by counting the assistant messages already in the conversation,
// so it stays in step with the loop.
type Fake struct {
Script []Message
}
// Generate returns the next scripted reply.
func (f *Fake) Generate(_ context.Context, req Request) (Reply, error) {
n := 0
for _, m := range req.Messages {
if m.Role == "assistant" {
n++
}
}
if n >= len(f.Script) {
return Reply{}, fmt.Errorf("reply %d: %w", n+1, ErrScriptExhausted)
}
return Reply{Message: f.Script[n]}, nil
}
The fake is short because the interface asks for one method.
An interface with ten methods would need ten in every fake, and every new client would have ten chances to get something wrong.
Keep interfaces small enough that writing a second implementation is easy; the standard library’s io.Reader and io.Writer, with one method each, are the usual examples.
Declare an interface where it is used
An interface belongs to the code that calls it, and it can list exactly what that code needs.
The program in chapter 12’s main.go needs a client that can do two things, return a whole reply for a compaction summary and stream a reply for the loop, so it declares that, by embedding the two interfaces the agent package defines:
// A model is what the program needs from a client: a whole reply, for
// compaction summaries, and a reply in pieces, for the loop.
type model interface {
agent.Model
agent.StreamModel
}
The lowercase name keeps it private to main: nothing else needs it.
Both hand-written clients satisfy it, because each has both methods.
The SDK adapter has only Generate, so it isn’t a model, and the compiler says so if you pass one where a model is wanted.
The same file declares an interface for errors:
// retryable is an API error that says whether a retry might help, as
// both the OpenRouter client's and the Anthropic client's do.
type retryable interface {
error
Retryable() bool
}
The OpenRouter client’s *APIError and the Anthropic client’s are different types in different packages.
Both have a Retryable method, so one check finds either: errors.AsType[retryable](err) searches the chain for any error with that method, and Errors in an agent shows how the chain is searched.
Asking for behavior instead of a concrete type is what lets the check work for a client the code doesn’t import.
Adapters: from a whole reply to a stream and back
The streaming loop calls a second interface, from Streams in Go:
// A StreamModel produces the next reply as a stream of pieces. The
// stream ends after the last Delta, or with an error.
type StreamModel interface {
Stream(ctx context.Context, req Request) iter.Seq2[Delta, error]
}
A scripted fake returns whole replies, and the SDK adapter can’t stream at all.
Streamed turns any Model into a StreamModel whose stream holds the whole reply at once:
// 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,
ReasoningFrom: msg.ReasoningFrom}}
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
}
}
}
}
Generator goes the other way, collecting a stream into one reply for calls that don’t stream:
// Generator turns a StreamModel into a Model that collects each reply
// whole, so a wrapped model can serve calls that don't stream, such as
// chapter 8's compaction summary, and get the same retries.
func Generator(m StreamModel) Model { return generator{m} }
type generator struct{ m StreamModel }
func (g generator) Generate(ctx context.Context, req Request) (Reply,
error) {
return Collect(g.m.Stream(ctx, req))
}
Both follow the same pattern: an unexported struct that holds the value it adapts, a method that satisfies the other interface, and an exported function that returns the interface. The usual Go advice is to accept interfaces and return concrete types, and the code mostly does. An adapter is the exception: its struct is an implementation detail with nothing for a caller to use, so returning the interface keeps it out of the package’s API.
A function that is an interface
Some implementations are a single function.
Go lets a function type have methods, so a function can satisfy an interface, as http.HandlerFunc does for http.Handler:
// 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]
// Stream calls f.
func (f StreamFunc) Stream(ctx context.Context,
req Request) iter.Seq2[Delta, error] {
return f(ctx, req)
}
StreamFunc(f) is a conversion, not a call: it gives the function f the type StreamFunc, whose Stream method calls f.
With it, code that needs a StreamModel can write a closure instead of declaring a struct.
Wrappers: retries, spans, and logs around a model
A wrapper takes a model and returns another that does something before and after each call: retry it, trace it, log it, count its tokens. The agent package names the shape of a wrapper with two function types:
// A ModelWare wraps a model: it takes the model a call goes to and
// returns one that does something before and after, such as a retry or
// a span. It takes and returns StreamModel, the interface the loop
// calls, so no wrapper can hide the stream (chapter 6's rule).
type ModelWare func(StreamModel) StreamModel
// A ToolWare wraps a tool. It sees the whole Tool, its definition and
// its safety flags as well as its Run, which is what a retry needs to
// know whether the tool is safe to run twice.
type ToolWare func(Tool) Tool
// WrapModel wraps m in ws, the first outermost: a call goes through
// ws[0], then ws[1], and so on, to m.
func WrapModel(m StreamModel, ws ...ModelWare) StreamModel {
for _, w := range slices.Backward(ws) {
m = w(m)
}
return m
}
// WrapTools wraps every tool in ws, the first outermost.
func WrapTools(tools []Tool, ws ...ToolWare) []Tool {
out := slices.Clone(tools)
for i := range out {
for _, w := range slices.Backward(ws) {
out[i] = w(out[i])
}
}
return out
}
WrapModel applies them from the last to the first, so the first wrapper in the list is the outermost: a call passes through ws[0] first.
Here is one, Traced, which records a span for each model call; it returns a closure through StreamFunc, and the closure calls the model it wraps:
// Traced returns a ModelWare that records each model call as a "chat"
// span under the span in the call's context, with the GenAI attributes.
// It goes outside the retries, so one span covers every attempt.
func Traced(rec *Recorder, provider, model string) ModelWare {
return func(m StreamModel) StreamModel {
return StreamFunc(func(ctx context.Context,
req Request) iter.Seq2[Delta, error] {
return func(yield func(Delta, error) bool) {
ctx, span := rec.Start(ctx, opChat+" "+model,
slog.String(attrOperation, opChat),
slog.String(attrProvider, provider),
slog.String(attrRequestModel, model))
var err error
defer func() { span.Finish(err) }()
for d, e := range m.Stream(ctx, req) {
spanDelta(span, d, e)
if err = e; !yield(d, e) {
return
}
}
}
})
}
}
Chapter 12’s program stacks five of them, and the order is part of the design: the span for the whole call goes outside the retries, so one span covers every attempt, and the meter goes inside them, so a failed attempt’s tokens are counted too.
// Model wraps m: the chat span outside the retries, then the retries,
// then, for each attempt, its own span, the log line, and the meter.
func (w *wrapped) Model(m agent.StreamModel) agent.StreamModel {
return agent.WrapModel(m,
agent.Traced(w.rec, w.provider, w.model),
agent.Retrying(w.policy),
agent.TracedAttempts(w.rec),
agent.Logged(w.log, w.model),
w.meter.Wrap)
}
// Tools wraps tools: the span, the log line, retries for the tools safe
// to run twice, and each tool's Timeout. Tool retries have a policy of
// their own: a flaky tool says nothing about the provider.
func (w *wrapped) Tools(tools []agent.Tool) []agent.Tool {
logs := agent.LogOptions{Redact: triage.Redact}
return agent.WrapTools(tools,
agent.TracedTools(w.rec),
agent.LoggedTools(w.log, logs),
agent.RetryTools(w.tools),
agent.Timeouts)
}
The last one, w.meter.Wrap, is a method value.
Meter.Wrap takes a StreamModel and returns one, so w.meter.Wrap, the method bound to w.meter, has exactly the type ModelWare and goes in the list with no adapter.
Tools are wrapped the same way, but a Tool is a struct, not an interface:
// Tool is the harness's side of a tool: the definition the model sees,
// and the Go function that runs it. The model never sees Run.
type Tool struct {
Def ToolDef
Run func(ctx context.Context, args jsontext.Value) (string, error)
// Safety flags (chapter 7). A tool with neither flag set may change
// something outside the run.
ReadOnly bool // it only looks things up
Idempotent bool // running it twice has the same effect as once
// Final marks the tool that ends the run: once a call to it
// succeeds, its arguments become Result.Final (chapter 7).
Final bool
// Timeout, if set, bounds one call to Run; a wrapper applies it
// (chapter 12). Zero means the tool's own limits only.
Timeout time.Duration
}
That is deliberate.
A wrapper that retries a tool has to know whether running it twice is safe, which is what Idempotent says, and a wrapper that applies a time limit needs Timeout.
An interface with only a Run method would hide both.
So a ToolWare takes the whole struct and returns it with Run replaced, keeping the definition and the flags:
// Timeouts is a ToolWare that applies each tool's Timeout. A timeout
// the tool reports itself, such as an MCP client's own deadline, counts
// the same. A tool that ignores its context can't be stopped: Timeouts
// waits for it, and a result that arrives late still counts.
func Timeouts(t Tool) Tool {
if t.Timeout <= 0 {
return t
}
run := t.Run
t.Run = func(ctx context.Context,
args jsontext.Value) (string, error) {
tctx, cancel := context.WithTimeoutCause(ctx, t.Timeout,
ErrToolTimeout)
defer cancel()
out, err := run(tctx, args)
if err == nil || ctx.Err() != nil ||
!errors.Is(context.Cause(tctx), ErrToolTimeout) &&
!timedOut(err) {
return out, err
}
return "", timeoutError(t, err)
}
return t
}
Several implementations, and a slice that is one
What each request holds is decided by a ContextPolicy, from Context management:
// A ContextPolicy builds the messages one request sends from the full
// conversation. The loop keeps the full conversation in
// Result.Messages and never rewrites it; a policy only decides what the
// model sees. Its state, such as how much it has trimmed, lives in a
// ContextState that the loop holds and passes back each turn, so a
// policy keeps nothing between calls and the same conversation and
// state always build the same request.
type ContextPolicy interface {
Build(ctx context.Context, msgs []Message, st ContextState) (Window, error)
}
// A Window is what a policy built: the request's messages, the state
// to pass back next turn, and what building it cost, such as a summary
// call.
type Window struct {
Messages []Message
State ContextState
Usage Usage
}
// ContextState is a context policy's memory between turns. Each field
// is an index into the full conversation or text derived from it.
type ContextState struct {
Cut int // Trim: messages before this are left out
Cleared int // ClearResults: tool results before this are cleared
Summary string // Compact: the latest summary of earlier turns
Covered int // Compact: the summary covers messages before this
}
Trim, ClearResults, Compact, and CacheMarks each implement it, and a run takes whichever it is given.
To use several, Chain returns a slice of policies that is itself a policy:
// Chain applies policies in order, each to the messages the one before
// it built. The state's indexes count the full conversation, so at
// most one policy in a chain may shorten it, Trim or Compact, and the
// policies before it must keep its length, such as ClearResults before
// Trim. Compact clears through its Sent field instead, so its summary
// reads whole results. CacheMarks goes last, to mark what is sent.
func Chain(ps ...ContextPolicy) ContextPolicy { return chain(ps) }
type chain []ContextPolicy
func (c chain) Build(ctx context.Context, msgs []Message,
st ContextState) (Window, error) {
if n := shortening(c); n > 1 {
return Window{}, fmt.Errorf("chain: %d policies shorten the "+
"conversation; use one, Trim or Compact", n)
}
w := Window{Messages: msgs, State: st}
for i, p := range c {
next, err := p.Build(ctx, w.Messages, w.State)
if err != nil {
return Window{Usage: w.Usage.Add(next.Usage)},
fmt.Errorf("policy %d of %d: %w", i+1, len(c), err)
}
next.Usage = w.Usage.Add(next.Usage)
w = next
}
return w, nil
}
chain is a named slice type with a Build method, so it satisfies ContextPolicy, and the loop calls it like any other policy.
Its method runs each policy in turn on what the one before built.
Sometimes code needs to know which concrete types an interface holds.
Chain refuses to hold two policies that both shorten the conversation, and finds them with a type switch:
// shortening counts the policies that shorten the conversation.
func shortening(ps []ContextPolicy) int {
n := 0
for _, p := range ps {
switch p.(type) {
case Trim, *Trim, Compact, *Compact:
n++
}
}
return n
}
Each type appears twice, as a value and as a pointer.
Trim’s method has a value receiver, func (t Trim) Build, and a method with a value receiver belongs to both Trim and *Trim, so a caller could pass either and the switch has to catch both.
A method with a pointer receiver belongs to the pointer only, which is why *agent.Fake is a Model and agent.Fake is not.
Optional behavior: ask with a type assertion
A function can take a small interface and still use more when a value offers it.
Chapter 15’s audit log writes each entry to an io.Writer; when that writer is a file, it also asks the operating system to flush the entry to disk, so a crash can’t lose it:
if s, ok := a.W.(interface{ Sync() error }); ok {
if err := s.Sync(); err != nil {
return fmt.Errorf("audit: %w", err)
}
}
a.W.(interface{ Sync() error }) asks whether the value in a.W also has a Sync method.
An *os.File does, so the entry is synced; the bytes.Buffer a test passes doesn’t, and the check is skipped.
The interface type is written inline, with no name, because nothing else needs it.
The standard library works the same way: io.Copy checks whether its source is an io.WriterTo.
Checking at compile time
Go checks that a type satisfies an interface only where the type is used as one. To check it earlier, assign a value to a blank variable of the interface type:
var _ agent.ContextPolicy = agent.CacheMarks{}
The line does nothing when it runs.
If CacheMarks ever loses its Build method, or the method’s signature changes, the code stops compiling here, with an error that names the missing method, instead of somewhere far from the cause.
Generics: one function for every tool’s arguments
Each tool decodes its own JSON arguments into its own Go type. Without generics, every tool would repeat the decoding and the error the model reads when it fails. With a type parameter, one function does it for every type:
// decodeArgs decodes a tool call's arguments. When they don't decode,
// the error says what was wrong and shows the shape the tool expects,
// so the model can fix its next call.
func decodeArgs[T any](args jsontext.Value, example string) (T, error) {
var v T
err := json.Unmarshal(args, &v, json.RejectUnknownMembers(true))
if err != nil {
return v, &toolError{ErrToolArgs, fmt.Sprintf(
"bad arguments: %s; send JSON like %s", argError(err), example)}
}
return v, nil
}
T is a type parameter: the caller picks it, as in decodeArgs[readArgs](args, example), and the compiler makes a version of the function where T is that type.
any is the constraint, the set of types T may be; any allows every type, which is all this function needs, since it only declares a T and passes its address to json.Unmarshal.
Chapter 7 goes further: the tool’s whole definition comes from its argument type. A tool’s arguments are a struct, with a tag for each field’s description:
type readArgs struct {
Path string `json:"path" desc:"a file path, such as store/store.go"`
Start int `json:"start,omitzero" desc:"the first line to show; default 1"`
End int `json:"end,omitzero" desc:"the last line to show; default 200 lines after start"`
}
NewTool takes the tool’s Go function and generates the JSON schema the model sees from the struct, so the schema and the code that reads the arguments can’t drift apart:
// NewTool makes a Tool from a Go function whose arguments are the
// fields of a struct, A. The schema the model sees is generated from A,
// so the two can't drift apart. NewTool panics on a type that can't be
// a tool's arguments, which is a programmer's mistake.
func NewTool[A any](name, description string,
run func(ctx context.Context, args A) (string, error)) Tool {
t := reflect.TypeFor[A]()
if t.Kind() != reflect.Struct {
panic(fmt.Sprintf("agent: tool %s: arguments must be a struct",
name))
}
s, err := schemaFor(t, nil)
if err != nil {
panic(fmt.Sprintf("agent: tool %s: %v", name, err))
}
params, err := json.Marshal(s)
if err != nil {
panic(fmt.Sprintf("agent: tool %s: %v", name, err))
}
return Tool{
Def: ToolDef{Name: name, Description: description,
Parameters: params, Strict: strictOK(s)},
Run: func(ctx context.Context, args jsontext.Value) (string, error) {
a, err := decodeStrict[A](args, s)
if err != nil {
return "", err
}
return run(ctx, a)
},
}
}
Three things happen in it.
reflect.TypeFor[A]() gets the struct’s type, which is how the schema is built at run time from the fields and their tags.
The function the caller passes takes an A, so inside it a.Path is a string and a.Start an int, checked by the compiler, with no type assertion.
And the Tool it returns isn’t generic: its Run takes raw JSON, and the closure decodes it into an A before calling the typed function.
That last point is what makes it work.
The loop holds its tools in a []Tool, and a slice holds one type.
If Tool were generic, Tool[readArgs] and Tool[searchArgs] would be different types and couldn’t share the slice.
So the type parameter lives only in the constructor, and the closure carries the type inside.
You rarely write the type argument yourself.
In agent.NewTool("code_read", "...", func(_ context.Context, a readArgs) (string, error) {...}), Go infers A from the function’s parameter.
For the read tool in the package’s tests, with a required path and an optional start, the generated schema is this, checked byte for byte by TestSchema:
{"type":"object","properties":{"path":{"type":"string","description":"a file path, relative to the project"},"start":{"type":"integer","description":"the first line"}},"required":["path"],"additionalProperties":false}
The omitzero on start is what leaves it out of required.
Constraints that say more than any
A constraint is an interface, and it can list types or methods. The code that summarizes the recorded experiments takes the median of ints in one place and of float64s in another, with one function:
func median[T int | float64](xs []T) T {
if len(xs) == 0 {
return 0
}
s := slices.Sorted(slices.Values(xs))
if len(s)%2 == 1 {
return s[len(s)/2]
}
return (s[len(s)/2-1] + s[len(s)/2]) / 2
}
int | float64 is a union: T may be either, and since both support +, /, and ordering, the body may use them.
With any, the compiler would reject s[i] + s[j], because not every type can be added.
A constraint can also require a method, written inline:
// next returns the number of the first new run for model, after the
// runs already saved, so runs can be added later.
func next[T interface{ model() string }](file, model string) (int, error) {
saved, err := load[T](file)
if err != nil {
return 0, err
}
n := 1
for _, r := range saved {
if r.model() == model {
n++
}
}
return n, nil
}
Each kind of saved run is its own struct type, and each has a model method that says which model it recorded.
next loads any of them and counts the runs for one model, calling r.model() on each.
Reach for a type parameter when the function’s types matter to its caller, as in decodeArgs, which returns a T, or median, which returns the type it was given.
When a function only calls methods on a value and returns nothing of its type, an interface parameter says the same thing more simply.
Mistakes to avoid
- A large interface written before its users. Declare an interface where it is called, with the methods that code needs; the implementer exports a concrete type.
- An interface for one type that will never have a second. A test fake counts as a second, and
Fakeis whyModelexists; a type no one will ever substitute doesn’t need one. - A nil pointer in an interface. An interface holding a nil
*Tis not nil, soerr != nilis true. Errors in an agent shows the case. - A pointer receiver, then a value where the interface is wanted.
*Fakehas the method andFakedoesn’t, so passingagent.Fake{}as aModelfails to compile. Pass&agent.Fake{}. - A generic type where the values must share a slice.
Tool[A]for eachAcan’t go in one[]Tool; keep the type parameter in a constructor, asNewTooldoes. - Wrappers in the wrong order. In
WrapModel, the first wrapper is the outermost. A span placed inside the retries records each attempt; placed outside them, it records the whole call once.
Try it
From the code directory of a clone of the companion repository:
$ go test ./ch12/agent -run 'TestSchema$|TestDecodeStrict|TestGenerator$|TestChainShortening|TestTimeouts' -v
- In
TestSchemainch12/agent/registry_test.go, add a fieldEnd intwith tagsjson:"end,omitzero" desc:"the last line"toreadArgs, and run the test. It fails and prints the new schema, withendinpropertiesand not inrequired. - Change the
Fake’sGenerateinch12/agent/fake.goto have a value receiver,func (f Fake), and rungo vet ./ch12/.... Every&agent.Fake{}still compiles, since a value receiver belongs to the pointer too; the methods ofFakeare now in both method sets. - In
ch12/main_test.go, addvar _ agent.StreamModel = &agent.Fake{}and rungo vet ./ch12. The compiler refuses it and names the missingStreammethod;agent.Streamed(&agent.Fake{})is the value that fits.
Undo your edits with git restore . in the code directory.
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 ch12/agent/types.go, line 14.
func (Model) Generate(ctx context.Context, req Request) (Reply, error)
func (Model) Generate(ctx context.Context, req Request) (Reply, error). In ch12/agent/types.go, line 15.
var ctx context.Context
var ctx context.Context. In ch12/agent/types.go, line 15.
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 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 ch12/agent/types.go, line 25.
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 ch12/agent/types.go, line 93.
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.
type Fake struct {
Script []Message
}
Fake is a Model that replays a script of replies.
Fake is a Model that replays a script of replies. type Fake struct { Script []Message }. In ch12/agent/fake.go, line 19.
type Message struct {
Role string // "system", "user", "assistant", or "tool"
Content string // may be empty when the message has tool calls
ToolCalls []ToolCall // set on assistant messages that call tools
ToolCallID string // set on tool messages: which 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 // may be empty when the message has tool calls ToolCalls []ToolCall // set on assistant messages that call tools ToolCallID string // set on tool messages: which 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 ch12/agent/types.go, line 39.
func (f *Fake) Generate(_ context.Context, req Request) (Reply, error)
Generate returns the next scripted reply.
Generate returns the next scripted reply. func (f *Fake) Generate(_ context.Context, req Request) (Reply, error). In ch12/agent/fake.go, line 24.
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.
field Messages []Message
field Messages []Message. In ch12/agent/types.go, line 26.
field Role string
"system", "user", "assistant", or "tool"
"system", "user", "assistant", or "tool" field Role string. In ch12/agent/types.go, line 40.
func len(v Type) int
The len built-in function returns the length of v, according to its type:
The len built-in function returns the length of v, according to its type: func len(v Type) int. Documented at pkg.go.dev, builtin.len.
package fmt ("fmt")
Package fmt implements formatted I/O with functions analogous to C's printf and scanf.
Package fmt implements formatted I/O with functions analogous to C's printf and scanf. package fmt ("fmt"). Documented at pkg.go.dev, fmt.
func Errorf(format string, a ...any) (err error)
Errorf formats according to a format specifier and returns the string as a value that satisfies error.
Errorf formats according to a format specifier and returns the string as a value that satisfies error. func Errorf(format string, a ...any) (err error). Documented at pkg.go.dev, fmt.Errorf.
var ErrScriptExhausted = errors.New("fake model: script exhausted")
ErrScriptExhausted means a Fake was asked for more replies than its script holds.
ErrScriptExhausted means a Fake was asked for more replies than its script holds. var ErrScriptExhausted = errors.New("fake model: script exhausted"). In ch12/agent/fake.go, line 12.
field Message Message
the assistant's text, tool calls, or both
the assistant's text, tool calls, or both field Message Message. In ch12/agent/types.go, line 94.
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.
type model interface {
agent.Model
agent.StreamModel
}
A model is what the program needs from a client: a whole reply, for compaction summaries, and a reply in pieces, for the loop.
A model is what the program needs from a client: a whole reply, for compaction summaries, and a reply in pieces, for the loop. type model interface { agent.Model agent.StreamModel }. In ch12/main.go, line 341.
package agent ("github.com/blackwell-systems/go-agents-book/code/ch12/agent")
Package agent is the agent loop and the types it works with.
Package agent is the agent loop and the types it works with. package agent ("github.com/blackwell-systems/go-agents-book/code/ch12/agent"). In ch12/agent.
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 ch12/agent/stream.go, line 58.
type retryable interface {
error
Retryable() bool
}
retryable is an API error that says whether a retry might help, as both the OpenRouter client's and the Anthropic client's do.
retryable is an API error that says whether a retry might help, as both the OpenRouter client's and the Anthropic client's do. type retryable interface { error Retryable() bool }. In ch12/main.go, line 454.
func (retryable) Retryable() bool
func (retryable) Retryable() bool. In ch12/main.go, line 456.
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 (StreamModel) Stream(ctx context.Context, req Request) iter.Seq2[Delta, error]
func (StreamModel) Stream(ctx context.Context, req Request) iter.Seq2[Delta, error]. In ch12/agent/stream.go, line 59.
var ctx context.Context
var ctx context.Context. In ch12/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.
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 ch12/agent/stream.go, line 18.
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 ch12/agent/stream.go, line 69.
type streamed struct{ m Model }
type streamed struct{ m Model }. In ch12/agent/stream.go, line 71.
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 ch12/agent/stream.go, line 73.
var ctx context.Context
var ctx context.Context. In ch12/agent/stream.go, line 73.
var yield func(Delta, error) bool
var yield func(Delta, error) bool. In ch12/agent/stream.go, line 75.
field Usage *Usage
what the call consumed, at the end
what the call consumed, at the end field Usage *Usage. In ch12/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 ch12/agent/types.go, line 96.
field Text string
a piece of the reply's text
a piece of the reply's text field Text string. In ch12/agent/stream.go, line 19.
field Content string
may be empty when the message has tool calls
may be empty when the message has tool calls field Content string. In ch12/agent/types.go, line 41.
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 ch12/agent/stream.go, line 26.
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 ch12/agent/types.go, line 48.
field ReasoningFrom string
ReasoningFrom names the client the records came from (chapter 11); the assembler copies it into the reply.
ReasoningFrom names the client the records came from (chapter 11); the assembler copies it into the reply. field ReasoningFrom string. In ch12/agent/stream.go, line 30.
field ReasoningFrom string
ReasoningFrom names the client that produced ReasoningDetails, such as "anthropic@openrouter.ai" (chapter 11).
ReasoningFrom names the client that produced ReasoningDetails, such as "anthropic@openrouter.ai" (chapter 11). field ReasoningFrom string. In ch12/agent/types.go, line 56.
field ToolCalls []ToolCall
set on assistant messages that call tools
set on assistant messages that call tools field ToolCalls []ToolCall. In ch12/agent/types.go, line 42.
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 ch12/agent/stream.go, line 20.
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 ch12/agent/stream.go, line 45.
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 ch12/agent/stream.go, line 46.
field ID string
set on the first piece only
set on the first piece only field ID string. In ch12/agent/stream.go, line 47.
field Name string
set on the first piece only
set on the first piece only field Name string. In ch12/agent/stream.go, line 48.
field Arguments string
the next fragment of the arguments
the next fragment of the arguments field Arguments string. In ch12/agent/stream.go, line 49.
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 ch12/agent/types.go, line 85.
field FinishReason string
why the reply ended, when it has
why the reply ended, when it has field FinishReason string. In ch12/agent/stream.go, line 21.
field FinishReason string
why the model stopped: "stop", "length", ...
why the model stopped: "stop", "length", ... field FinishReason string. In ch12/agent/types.go, line 95.
func Generator(m StreamModel) Model
Generator turns a StreamModel into a Model that collects each reply whole, so a wrapped model can serve calls that don't stream, such as chapter 8's compaction summary, and get the same retries.
Generator turns a StreamModel into a Model that collects each reply whole, so a wrapped model can serve calls that don't stream, such as chapter 8's compaction summary, and get the same retries. func Generator(m StreamModel) Model. In ch12/agent/middleware.go, line 63.
var m StreamModel
var m StreamModel. In ch12/agent/middleware.go, line 63.
type generator struct{ m StreamModel }
type generator struct{ m StreamModel }. In ch12/agent/middleware.go, line 65.
field m StreamModel
field m StreamModel. In ch12/agent/middleware.go, line 65.
func (g generator) Generate(ctx context.Context, req Request) (Reply,
error)
func (g generator) Generate(ctx context.Context, req Request) (Reply, error). In ch12/agent/middleware.go, line 67.
var ctx context.Context
var ctx context.Context. In ch12/agent/middleware.go, line 67.
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.
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 ch12/agent/stream.go, line 201.
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 ch12/agent/middleware.go, line 49.
var ctx context.Context
var ctx context.Context. In ch12/agent/middleware.go, line 49.
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.
func (f StreamFunc) Stream(ctx context.Context,
req Request) iter.Seq2[Delta, error]
Stream calls f.
Stream calls f. func (f StreamFunc) Stream(ctx context.Context, req Request) iter.Seq2[Delta, error]. In ch12/agent/middleware.go, line 53.
var ctx context.Context
var ctx context.Context. In ch12/agent/middleware.go, line 53.
type ModelWare func(StreamModel) StreamModel
A ModelWare wraps a model: it takes the model a call goes to and returns one that does something before and after, such as a retry or a span.
A ModelWare wraps a model: it takes the model a call goes to and returns one that does something before and after, such as a retry or a span. type ModelWare func(StreamModel) StreamModel. In ch12/agent/middleware.go, line 16.
type ToolWare func(Tool) Tool
A ToolWare wraps a tool.
A ToolWare wraps a tool. type ToolWare func(Tool) Tool. In ch12/agent/middleware.go, line 21.
type Tool struct {
Def ToolDef
Run func(ctx context.Context, args jsontext.Value) (string, error)
// Safety flags (chapter 7). A tool with neither flag set may change
// something outside the run.
ReadOnly bool // it only looks things up
Idempotent bool // running it twice has the same effect as once
// Final marks the tool that ends the run: once a call to it
// ...
}
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) // Safety flags (chapter 7). A tool with neither flag set may change // something outside the run. ReadOnly bool // it only looks things up Idempotent bool // running it twice has the same effect as once // Final marks the tool that ends the run: once a call to it // ... }. In ch12/agent/types.go, line 140.
func WrapModel(m StreamModel, ws ...ModelWare) StreamModel
WrapModel wraps m in ws, the first outermost: a call goes through ws[0], then ws[1], and so on, to m.
WrapModel wraps m in ws, the first outermost: a call goes through ws[0], then ws[1], and so on, to m. func WrapModel(m StreamModel, ws ...ModelWare) StreamModel. In ch12/agent/middleware.go, line 25.
var m StreamModel
var m StreamModel. In ch12/agent/middleware.go, line 25.
var ws []ModelWare
var ws []ModelWare. In ch12/agent/middleware.go, line 25.
package slices ("slices")
Package slices defines various functions useful with slices of any type.
Package slices defines various functions useful with slices of any type. package slices ("slices"). Documented at pkg.go.dev, slices.
func Backward[Slice ~[]E, E any](s Slice) iter.Seq2[int, E]
Backward returns an iterator over index-value pairs in the slice, traversing it backward with descending indices.
Backward returns an iterator over index-value pairs in the slice, traversing it backward with descending indices. func Backward[Slice ~[]E, E any](s Slice) iter.Seq2[int, E]. Documented at pkg.go.dev, slices.Backward.
func WrapTools(tools []Tool, ws ...ToolWare) []Tool
WrapTools wraps every tool in ws, the first outermost.
WrapTools wraps every tool in ws, the first outermost. func WrapTools(tools []Tool, ws ...ToolWare) []Tool. In ch12/agent/middleware.go, line 33.
var ws []ToolWare
var ws []ToolWare. In ch12/agent/middleware.go, line 33.
func Clone[S ~[]E, E any](s S) S
Clone returns a copy of the slice.
Clone returns a copy of the slice. func Clone[S ~[]E, E any](s S) S. Documented at pkg.go.dev, slices.Clone.
func Traced(rec *Recorder, provider, model string) ModelWare
Traced returns a ModelWare that records each model call as a "chat" span under the span in the call's context, with the GenAI attributes.
Traced returns a ModelWare that records each model call as a "chat" span under the span in the call's context, with the GenAI attributes. func Traced(rec *Recorder, provider, model string) ModelWare. In ch12/agent/traced.go, line 27.
type Recorder struct {
Trace string // the trace's ID, random, set by NewRecorder
mu sync.Mutex
spans []*Span
}
A Recorder keeps every span a run starts, in memory, and prints them as a tree.
A Recorder keeps every span a run starts, in memory, and prints them as a tree. type Recorder struct { Trace string // the trace's ID, random, set by NewRecorder mu sync.Mutex spans []*Span }. In ch12/agent/trace.go, line 32.
var ctx context.Context
var ctx context.Context. In ch12/agent/traced.go, line 29.
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.
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 ch12/agent/traced.go, line 31.
var ctx context.Context
var ctx context.Context. In ch12/agent/traced.go, line 32.
func (r *Recorder) Start(ctx context.Context, name string,
attrs ...slog.Attr) (context.Context, *Handle)
Start opens a span named name under the span in ctx, if it belongs to r, and returns a context carrying the new span.
Start opens a span named name under the span in ctx, if it belongs to r, and returns a context carrying the new span. func (r *Recorder) Start(ctx context.Context, name string, attrs ...slog.Attr) (context.Context, *Handle). In ch12/agent/trace.go, line 62.
package slog ("log/slog")
Package slog provides structured logging, in which log records include a message, a severity level, and various other attributes expressed as key-value pairs.
Package slog provides structured logging, in which log records include a message, a severity level, and various other attributes expressed as key-value pairs. package slog ("log/slog"). Documented at pkg.go.dev, log/slog.
func String(key, value string) Attr
String returns an Attr for a string value.
String returns an Attr for a string value. func String(key, value string) Attr. Documented at pkg.go.dev, log/slog.String.
const attrOperation = "gen_ai.operation.name"
const attrOperation = "gen_ai.operation.name". In ch12/agent/genai.go, line 16.
const attrProvider = "gen_ai.provider.name"
const attrProvider = "gen_ai.provider.name". In ch12/agent/genai.go, line 17.
const attrRequestModel = "gen_ai.request.model"
const attrRequestModel = "gen_ai.request.model". In ch12/agent/genai.go, line 19.
func (h *Handle) Finish(err error)
Finish closes the span, with the error the work ended in, if any.
Finish closes the span, with the error the work ended in, if any. func (h *Handle) Finish(err error). In ch12/agent/trace.go, line 103.
func spanDelta(span *Handle, d Delta, err error)
spanDelta adds what one Delta says about the call to its span.
spanDelta adds what one Delta says about the call to its span. func spanDelta(span *Handle, d Delta, err error). In ch12/agent/traced.go, line 85.
type wrapped struct {
policy agent.Policy // the model's
tools agent.Policy // the tools', which shares none of the limits
rec *agent.Recorder // nil unless -trace
meter *agent.Meter
log *slog.Logger
provider string // the host's name, for gen_ai.provider.name
model string
}
wrapped holds what chapter 12 puts around the agent's calls: the retry policies, a recorder for traces, a meter for cost, and a logger.
wrapped holds what chapter 12 puts around the agent's calls: the retry policies, a recorder for traces, a meter for cost, and a logger. type wrapped struct { policy agent.Policy // the model's tools agent.Policy // the tools', which shares none of the limits rec *agent.Recorder // nil unless -trace meter *agent.Meter log *slog.Logger provider string // the host's name, for gen_ai.provider.name model string }. In ch12/reliable.go, line 22.
func (w *wrapped) Model(m agent.StreamModel) agent.StreamModel
Model wraps m: the chat span outside the retries, then the retries, then, for each attempt, its own span, the log line, and the meter.
Model wraps m: the chat span outside the retries, then the retries, then, for each attempt, its own span, the log line, and the meter. func (w *wrapped) Model(m agent.StreamModel) agent.StreamModel. In ch12/reliable.go, line 78.
package agent ("github.com/blackwell-systems/go-agents-book/code/ch12/agent")
Package agent is the agent loop and the types it works with.
Package agent is the agent loop and the types it works with. package agent ("github.com/blackwell-systems/go-agents-book/code/ch12/agent"). In ch12/agent.
field rec *agent.Recorder
nil unless -trace
nil unless -trace field rec *agent.Recorder. In ch12/reliable.go, line 25.
field provider string
the host's name, for gen_ai.provider.name
the host's name, for gen_ai.provider.name field provider string. In ch12/reliable.go, line 28.
func Retrying(p Policy) ModelWare
Retrying returns a ModelWare that retries each call as p says.
Retrying returns a ModelWare that retries each call as p says. func Retrying(p Policy) ModelWare. In ch12/agent/retrying.go, line 17.
field policy agent.Policy
the model's
the model's field policy agent.Policy. In ch12/reliable.go, line 23.
func TracedAttempts(rec *Recorder) ModelWare
TracedAttempts returns a ModelWare that records each attempt, inside the retries, as an "attempt" span with its number and, if it failed, error.type.
TracedAttempts returns a ModelWare that records each attempt, inside the retries, as an "attempt" span with its number and, if it failed, error.type. func TracedAttempts(rec *Recorder) ModelWare. In ch12/agent/traced.go, line 57.
func Logged(log *slog.Logger, model string) ModelWare
Logged returns a ModelWare that writes a line for every model call: the agent, the model, the tokens and cost, and any error.
Logged returns a ModelWare that writes a line for every model call: the agent, the model, the tokens and cost, and any error. func Logged(log *slog.Logger, model string) ModelWare. In ch12/agent/log.go, line 95.
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 ch12/agent/meter.go, line 51.
func (w *wrapped) Tools(tools []agent.Tool) []agent.Tool
Tools wraps tools: the span, the log line, retries for the tools safe to run twice, and each tool's Timeout.
Tools wraps tools: the span, the log line, retries for the tools safe to run twice, and each tool's Timeout. func (w *wrapped) Tools(tools []agent.Tool) []agent.Tool. In ch12/reliable.go, line 90.
var logs agent.LogOptions
var logs agent.LogOptions. In ch12/reliable.go, line 91.
type LogOptions struct {
Content bool
Limit int // bytes per field; 0 means 500
Redact func(string) string // nil means none
}
A LogOptions says what Logged writes: metadata always, and the content of requests and results only when Content is set, cut to Limit bytes and passed through Redact first.
A LogOptions says what Logged writes: metadata always, and the content of requests and results only when Content is set, cut to Limit bytes and passed through Redact first. type LogOptions struct { Content bool Limit int // bytes per field; 0 means 500 Redact func(string) string // nil means none }. In ch12/agent/log.go, line 36.
field Redact func(string) string
nil means none
nil means none field Redact func(string) string. In ch12/agent/log.go, line 39.
package triage ("github.com/blackwell-systems/go-agents-book/code/ch12/triage")
Package triage is the crash-triage agent's job: remind's crash reports, the tools for investigating and fixing them, and the structured diagnosis the agent ends with.
Package triage is the crash-triage agent's job: remind's crash reports, the tools for investigating and fixing them, and the structured diagnosis the agent ends with. package triage ("github.com/blackwell-systems/go-agents-book/code/ch12/triage"). In ch12/triage.
func Redact(s string) string
Redact replaces each credential in s with [REDACTED].
Redact replaces each credential in s with [REDACTED]. func Redact(s string) string. In ch12/triage/redact.go, line 36.
func TracedTools(rec *Recorder) ToolWare
TracedTools returns a ToolWare that records each tool call as an execute_tool span.
TracedTools returns a ToolWare that records each tool call as an execute_tool span. func TracedTools(rec *Recorder) ToolWare. In ch12/agent/traced.go, line 105.
func LoggedTools(log *slog.Logger, o LogOptions) ToolWare
LoggedTools returns a ToolWare that writes a line for every tool call, with the agent, the tool, the call, how long it took, and any error, and the stack of a panic before it reaches the loop's recovery, which keeps the stack from the model.
LoggedTools returns a ToolWare that writes a line for every tool call, with the agent, the tool, the call, how long it took, and any error, and the stack of a panic before it reaches the loop's recovery, which keeps the stack from the model. func LoggedTools(log *slog.Logger, o LogOptions) ToolWare. In ch12/agent/log.go, line 50.
func RetryTools(p Policy) ToolWare
RetryTools returns a ToolWare that retries a tool's failed calls as p says, but only for a tool marked ReadOnly or Idempotent, which is safe to run twice; every other tool runs once, as before.
RetryTools returns a ToolWare that retries a tool's failed calls as p says, but only for a tool marked ReadOnly or Idempotent, which is safe to run twice; every other tool runs once, as before. func RetryTools(p Policy) ToolWare. In ch12/agent/tooltime.go, line 94.
field tools agent.Policy
the tools', which shares none of the limits
the tools', which shares none of the limits field tools agent.Policy. In ch12/reliable.go, line 24.
func Timeouts(t Tool) Tool
Timeouts is a ToolWare that applies each tool's Timeout.
Timeouts is a ToolWare that applies each tool's Timeout. func Timeouts(t Tool) Tool. In ch12/agent/tooltime.go, line 46.
type ToolDef struct {
Name string
Description string
Parameters jsontext.Value // a JSON Schema
// Strict asks the provider to hold the arguments to the schema,
// where it can (chapter 7).
Strict bool
}
ToolDef describes a tool to the model.
ToolDef describes a tool to the model. type ToolDef struct { Name string Description string Parameters jsontext.Value // a JSON Schema // Strict asks the provider to hold the arguments to the schema, // where it can (chapter 7). Strict bool }. In ch12/agent/types.go, line 71.
field Run func(ctx context.Context, args jsontext.Value) (string, error)
field Run func(ctx context.Context, args jsontext.Value) (string, error). In ch12/agent/types.go, line 142.
var ctx context.Context
var ctx context.Context. In ch12/agent/types.go, line 142.
var args jsontext.Value
var args jsontext.Value. In ch12/agent/types.go, line 142.
package jsontext ("encoding/json/jsontext")
Package jsontext implements syntactic processing of JSON as specified in RFC 4627, RFC 7159, RFC 7493, RFC 8259, and RFC 8785.
Package jsontext implements syntactic processing of JSON as specified in RFC 4627, RFC 7159, RFC 7493, RFC 8259, and RFC 8785. package jsontext ("encoding/json/jsontext"). Documented at pkg.go.dev, encoding/json/jsontext.
type Value []byte
Value represents a single raw JSON value, which may be one of the following:
Value represents a single raw JSON value, which may be one of the following: type Value []byte. Documented at pkg.go.dev, encoding/json/jsontext.Value.
field ReadOnly bool
Safety flags (chapter 7).
Safety flags (chapter 7). field ReadOnly bool. In ch12/agent/types.go, line 146.
field Idempotent bool
running it twice has the same effect as once
running it twice has the same effect as once field Idempotent bool. In ch12/agent/types.go, line 147.
field Final bool
Final marks the tool that ends the run: once a call to it succeeds, its arguments become Result.Final (chapter 7).
Final marks the tool that ends the run: once a call to it succeeds, its arguments become Result.Final (chapter 7). field Final bool. In ch12/agent/types.go, line 151.
field Timeout time.Duration
Timeout, if set, bounds one call to Run; a wrapper applies it (chapter 12).
Timeout, if set, bounds one call to Run; a wrapper applies it (chapter 12). field Timeout time.Duration. In ch12/agent/types.go, line 155.
package time ("time")
Package time provides functionality for measuring and displaying time.
Package time provides functionality for measuring and displaying time. package time ("time"). Documented at pkg.go.dev, time.
type Duration int64
A Duration represents the elapsed time between two instants as an int64 nanosecond count.
A Duration represents the elapsed time between two instants as an int64 nanosecond count. type Duration int64. Documented at pkg.go.dev, time.Duration.
var run func(ctx context.Context, args jsontext.Value) (string, error)
var run func(ctx context.Context, args jsontext.Value) (string, error). In ch12/agent/tooltime.go, line 50.
var ctx context.Context
var ctx context.Context. In ch12/agent/tooltime.go, line 51.
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.
var args jsontext.Value
var args jsontext.Value. In ch12/agent/tooltime.go, line 52.
package jsontext ("encoding/json/jsontext")
Package jsontext implements syntactic processing of JSON as specified in RFC 4627, RFC 7159, RFC 7493, RFC 8259, and RFC 8785.
Package jsontext implements syntactic processing of JSON as specified in RFC 4627, RFC 7159, RFC 7493, RFC 8259, and RFC 8785. package jsontext ("encoding/json/jsontext"). Documented at pkg.go.dev, encoding/json/jsontext.
var tctx context.Context
var tctx context.Context. In ch12/agent/tooltime.go, line 53.
var cancel context.CancelFunc
var cancel context.CancelFunc. In ch12/agent/tooltime.go, line 53.
func WithTimeoutCause(parent Context, timeout time.Duration, cause error) (Context, CancelFunc)
WithTimeoutCause behaves like [WithTimeout] but also sets the cause of the returned Context when the timeout expires.
WithTimeoutCause behaves like [WithTimeout] but also sets the cause of the returned Context when the timeout expires. func WithTimeoutCause(parent Context, timeout time.Duration, cause error) (Context, CancelFunc). Documented at pkg.go.dev, context.WithTimeoutCause.
var ErrToolTimeout error = &toolTimeout{}
ErrToolTimeout is the cause set when a tool runs past its Timeout.
ErrToolTimeout is the cause set when a tool runs past its Timeout. var ErrToolTimeout error = &toolTimeout{}. In ch12/agent/tooltime.go, line 16.
func (Context) Err() error
If Done is not yet closed, Err returns nil.
If Done is not yet closed, Err returns nil. func (Context) Err() error. Documented at pkg.go.dev, context.Context.Err.
package errors ("errors")
Package errors implements functions to manipulate errors.
Package errors implements functions to manipulate errors. package errors ("errors"). Documented at pkg.go.dev, errors.
func Is(err, target error) bool
Is reports whether any error in err's tree matches target.
Is reports whether any error in err's tree matches target. func Is(err, target error) bool. Documented at pkg.go.dev, errors.Is.
func Cause(c Context) error
Cause returns a non-nil error explaining why c was canceled.
Cause returns a non-nil error explaining why c was canceled. func Cause(c Context) error. Documented at pkg.go.dev, context.Cause.
func timedOut(err error) bool
timedOut reports whether any error in err's chain is a timeout.
timedOut reports whether any error in err's chain is a timeout. func timedOut(err error) bool. In ch12/agent/classify.go, line 92.
func timeoutError(t Tool, err error) error
timeoutError is the error for a call to t that ran out of time.
timeoutError is the error for a call to t that ran out of time. func timeoutError(t Tool, err error) error. In ch12/agent/tooltime.go, line 75.
type ContextPolicy interface {
Build(ctx context.Context, msgs []Message, st ContextState) (Window, error)
}
A ContextPolicy builds the messages one request sends from the full conversation.
A ContextPolicy builds the messages one request sends from the full conversation. type ContextPolicy interface { Build(ctx context.Context, msgs []Message, st ContextState) (Window, error) }. In ch12/agent/policy.go, line 18.
func (ContextPolicy) Build(ctx context.Context, msgs []Message, st ContextState) (Window, error)
func (ContextPolicy) Build(ctx context.Context, msgs []Message, st ContextState) (Window, error). In ch12/agent/policy.go, line 19.
var ctx context.Context
var ctx context.Context. In ch12/agent/policy.go, line 19.
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 ContextState struct {
Cut int // Trim: messages before this are left out
Cleared int // ClearResults: tool results before this are cleared
Summary string // Compact: the latest summary of earlier turns
Covered int // Compact: the summary covers messages before this
}
ContextState is a context policy's memory between turns.
ContextState is a context policy's memory between turns. type ContextState struct { Cut int // Trim: messages before this are left out Cleared int // ClearResults: tool results before this are cleared Summary string // Compact: the latest summary of earlier turns Covered int // Compact: the summary covers messages before this }. In ch12/agent/policy.go, line 33.
type Window struct {
Messages []Message
State ContextState
Usage Usage
}
A Window is what a policy built: the request's messages, the state to pass back next turn, and what building it cost, such as a summary call.
A Window is what a policy built: the request's messages, the state to pass back next turn, and what building it cost, such as a summary call. type Window struct { Messages []Message State ContextState Usage Usage }. In ch12/agent/policy.go, line 25.
field Messages []Message
field Messages []Message. In ch12/agent/policy.go, line 26.
field State ContextState
field State ContextState. In ch12/agent/policy.go, line 27.
type Usage struct {
PromptTokens int // everything sent, counted in tokens
CompletionTokens int // everything the model wrote
CachedTokens int // prompt tokens served from the cache
ReasoningTokens int // completion tokens spent on reasoning
Cost float64 // in dollars, as the provider reported it
// CacheWriteTokens are prompt tokens written to the cache, which
// some providers bill above the usual input price (chapter 8).
CacheWriteTokens int
// ...
}
Usage is what one model call consumed.
Usage is what one model call consumed. type Usage struct { PromptTokens int // everything sent, counted in tokens CompletionTokens int // everything the model wrote CachedTokens int // prompt tokens served from the cache ReasoningTokens int // completion tokens spent on reasoning Cost float64 // in dollars, as the provider reported it // CacheWriteTokens are prompt tokens written to the cache, which // some providers bill above the usual input price (chapter 8). CacheWriteTokens int // ... }. In ch12/agent/types.go, line 105.
field Cut int
Trim: messages before this are left out
Trim: messages before this are left out field Cut int. In ch12/agent/policy.go, line 34.
type int int
int is a signed integer type that is at least 32 bits in size.
int is a signed integer type that is at least 32 bits in size. type int int. Documented at pkg.go.dev, builtin.int.
field Cleared int
ClearResults: tool results before this are cleared
ClearResults: tool results before this are cleared field Cleared int. In ch12/agent/policy.go, line 35.
field Summary string
Compact: the latest summary of earlier turns
Compact: the latest summary of earlier turns field Summary string. In ch12/agent/policy.go, line 36.
field Covered int
Compact: the summary covers messages before this
Compact: the summary covers messages before this field Covered int. In ch12/agent/policy.go, line 37.
func Chain(ps ...ContextPolicy) ContextPolicy
Chain applies policies in order, each to the messages the one before it built.
Chain applies policies in order, each to the messages the one before it built. func Chain(ps ...ContextPolicy) ContextPolicy. In ch12/agent/policy.go, line 50.
var ps []ContextPolicy
var ps []ContextPolicy. In ch12/agent/policy.go, line 50.
type chain []ContextPolicy
type chain []ContextPolicy. In ch12/agent/policy.go, line 52.
func (c chain) Build(ctx context.Context, msgs []Message,
st ContextState) (Window, error)
func (c chain) Build(ctx context.Context, msgs []Message, st ContextState) (Window, error). In ch12/agent/policy.go, line 54.
var ctx context.Context
var ctx context.Context. In ch12/agent/policy.go, line 54.
func shortening(ps []ContextPolicy) int
shortening counts the policies that shorten the conversation.
shortening counts the policies that shorten the conversation. func shortening(ps []ContextPolicy) int. In ch12/agent/policy.go, line 76.
package fmt ("fmt")
Package fmt implements formatted I/O with functions analogous to C's printf and scanf.
Package fmt implements formatted I/O with functions analogous to C's printf and scanf. package fmt ("fmt"). Documented at pkg.go.dev, fmt.
func (u Usage) Add(v Usage) Usage
Add returns the sum of two Usages.
Add returns the sum of two Usages. func (u Usage) Add(v Usage) Usage. In ch12/agent/types.go, line 122.
var ps []ContextPolicy
var ps []ContextPolicy. In ch12/agent/policy.go, line 76.
type Trim struct {
Limit int // tokens
Chunk int // turns cut at a time; 0 means 4
}
Trim leaves the oldest turns out of the request once it is estimated to be over Limit tokens, keeping the head and the latest turn.
Trim leaves the oldest turns out of the request once it is estimated to be over Limit tokens, keeping the head and the latest turn. type Trim struct { Limit int // tokens Chunk int // turns cut at a time; 0 means 4 }. In ch12/agent/trim.go, line 17.
type Compact struct {
Model Model
Limit int // tokens
Keep int // recent turns kept word for word; 0 means 4
// Sent is applied to the messages the request sends, never to
// what the summary reads, such as ClearResults; it must keep the
// conversation's length.
Sent ContextPolicy
// ...
}
Compact summarizes older turns once the request is estimated to be over Limit tokens: the head stays, older turns are replaced by one summary, and the latest turns stay word for word.
Compact summarizes older turns once the request is estimated to be over Limit tokens: the head stays, older turns are replaced by one summary, and the latest turns stay word for word. type Compact struct { Model Model Limit int // tokens Keep int // recent turns kept word for word; 0 means 4 // Sent is applied to the messages the request sends, never to // what the summary reads, such as ClearResults; it must keep the // conversation's length. Sent ContextPolicy // ... }. In ch12/agent/compact.go, line 28.
var s interface{Sync() error}
var s interface{Sync() error}. In ch15/safety/audit.go, line 79.
field W io.Writer
syncs after each entry if it has a Sync method
syncs after each entry if it has a Sync method field W io.Writer. In ch15/safety/audit.go, line 48.
func (interface).Sync() error
func (interface).Sync() error. In ch15/safety/audit.go, line 79.
package fmt ("fmt")
Package fmt implements formatted I/O with functions analogous to C's printf and scanf.
Package fmt implements formatted I/O with functions analogous to C's printf and scanf. package fmt ("fmt"). Documented at pkg.go.dev, fmt.
package agent ("github.com/blackwell-systems/go-agents-book/code/ch12/agent")
Package agent is the agent loop and the types it works with.
Package agent is the agent loop and the types it works with. package agent ("github.com/blackwell-systems/go-agents-book/code/ch12/agent"). In ch12/agent.
type CacheMarks struct{}
CacheMarks marks where a provider that needs explicit markers may cache the request: the end of the head, which never changes, and the last message, so the next request, which starts with all of this one, can read it back from the cache.
CacheMarks marks where a provider that needs explicit markers may cache the request: the end of the head, which never changes, and the last message, so the next request, which starts with all of this one, can read it back from the cache. type CacheMarks struct{}. In ch12/agent/trim.go, line 113.
func decodeArgs[T any](args jsontext.Value, example string) (T, error)
decodeArgs decodes a tool call's arguments.
decodeArgs decodes a tool call's arguments. func decodeArgs[T any](args jsontext.Value, example string) (T, error). In ch12/agent/tools.go, line 43.
type any = interface{}
any is an alias for interface{} and is equivalent to interface{} in all ways.
any is an alias for interface{} and is equivalent to interface{} in all ways. type any = interface{}. Documented at pkg.go.dev, builtin.any.
var args jsontext.Value
var args jsontext.Value. In ch12/agent/tools.go, line 43.
package jsontext ("encoding/json/jsontext")
Package jsontext implements syntactic processing of JSON as specified in RFC 4627, RFC 7159, RFC 7493, RFC 8259, and RFC 8785.
Package jsontext implements syntactic processing of JSON as specified in RFC 4627, RFC 7159, RFC 7493, RFC 8259, and RFC 8785. package jsontext ("encoding/json/jsontext"). Documented at pkg.go.dev, encoding/json/jsontext.
package json ("encoding/json/v2")
Package json implements semantic processing of JSON as specified in RFC 8259.
Package json implements semantic processing of JSON as specified in RFC 8259. package json ("encoding/json/v2"). Documented at pkg.go.dev, encoding/json/v2.
func Unmarshal(in []byte, out any, opts ...Options) (err error)
Unmarshal decodes a []byte input into a Go value according to the provided unmarshal and decode options (while ignoring marshal or encode options).
Unmarshal decodes a []byte input into a Go value according to the provided unmarshal and decode options (while ignoring marshal or encode options). func Unmarshal(in []byte, out any, opts ...Options) (err error). Documented at pkg.go.dev, encoding/json/v2.Unmarshal.
func RejectUnknownMembers(v bool) Options
RejectUnknownMembers specifies that unknown members should be rejected when unmarshaling a JSON object.
encoding/json/v2.RejectUnknownMembers
RejectUnknownMembers specifies that unknown members should be rejected when unmarshaling a JSON object. func RejectUnknownMembers(v bool) Options. Documented at pkg.go.dev, encoding/json/v2.RejectUnknownMembers.
const true = 0 == 0
Untyped bool.
Untyped bool. const true = 0 == 0. Documented at pkg.go.dev, builtin.true.
type toolError struct {
kind error
msg string
}
A toolError is a problem with one tool call.
A toolError is a problem with one tool call. type toolError struct { kind error msg string }. In ch12/agent/errors.go, line 51.
var ErrToolArgs = fmt.Errorf("%w: bad arguments", ErrTool)
var ErrToolArgs = fmt.Errorf("%w: bad arguments", ErrTool). In ch12/agent/errors.go, line 44.
package fmt ("fmt")
Package fmt implements formatted I/O with functions analogous to C's printf and scanf.
Package fmt implements formatted I/O with functions analogous to C's printf and scanf. package fmt ("fmt"). Documented at pkg.go.dev, fmt.
func Sprintf(format string, a ...any) string
Sprintf formats according to a format specifier and returns the resulting string.
Sprintf formats according to a format specifier and returns the resulting string. func Sprintf(format string, a ...any) string. Documented at pkg.go.dev, fmt.Sprintf.
func argError(err error) string
argError says why arguments didn't decode, in the same words every time.
argError says why arguments didn't decode, in the same words every time. func argError(err error) string. In ch12/agent/tools.go, line 56.
type readArgs struct {
Path string `json:"path" desc:"a file path, such as store/store.go"`
Start int `json:"start,omitzero" desc:"the first line to show; default 1"`
End int `json:"end,omitzero" desc:"the last line to show; default 200 lines after start"`
}
type readArgs struct { Path string `json:"path" desc:"a file path, such as store/store.go"` Start int `json:"start,omitzero" desc:"the first line to show; default 1"` End int `json:"end,omitzero" desc:"the last line to show; default 200 lines after start"` }. In ch12/triage/codetools.go, line 84.
field Path string `json:"path" desc:"a file path, such as store/store.go"`
field Path string `json:"path" desc:"a file path, such as store/store.go"`. In ch12/triage/codetools.go, line 85.
field Start int `json:"start,omitzero" desc:"the first line to show; default 1"`
field Start int `json:"start,omitzero" desc:"the first line to show; default 1"`. In ch12/triage/codetools.go, line 86.
field End int `json:"end,omitzero" desc:"the last line to show; default 200 lines after start"`
field End int `json:"end,omitzero" desc:"the last line to show; default 200 lines after start"`. In ch12/triage/codetools.go, line 87.
func NewTool[A any](name, description string,
run func(ctx context.Context, args A) (string, error)) Tool
NewTool makes a Tool from a Go function whose arguments are the fields of a struct, A. The schema the model sees is generated from A, so the two can't drift apart.
NewTool makes a Tool from a Go function whose arguments are the fields of a struct, A. The schema the model sees is generated from A, so the two can't drift apart. func NewTool[A any](name, description string, run func(ctx context.Context, args A) (string, error)) Tool. In ch12/agent/registry.go, line 19.
type parameter A any
type parameter A any. In ch12/agent/registry.go, line 19.
var description string
var description string. In ch12/agent/registry.go, line 19.
var run func(ctx context.Context, args A) (string, error)
var run func(ctx context.Context, args A) (string, error). In ch12/agent/registry.go, line 20.
var ctx context.Context
var ctx context.Context. In ch12/agent/registry.go, line 20.
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.
package reflect ("reflect")
Package reflect implements run-time reflection, allowing a program to manipulate objects with arbitrary types.
Package reflect implements run-time reflection, allowing a program to manipulate objects with arbitrary types. package reflect ("reflect"). Documented at pkg.go.dev, reflect.
func TypeFor[T any]() Type
TypeFor returns the [Type] that represents the type argument T.
TypeFor returns the [Type] that represents the type argument T. func TypeFor[T any]() Type. Documented at pkg.go.dev, reflect.TypeFor.
func (Type) Kind() Kind
Kind returns the specific kind of this type.
Kind returns the specific kind of this type. func (Type) Kind() Kind. Documented at pkg.go.dev, reflect.Type.Kind.
func panic(v any)
The panic built-in function stops normal execution of the current goroutine.
The panic built-in function stops normal execution of the current goroutine. func panic(v any). Documented at pkg.go.dev, builtin.panic.
package fmt ("fmt")
Package fmt implements formatted I/O with functions analogous to C's printf and scanf.
Package fmt implements formatted I/O with functions analogous to C's printf and scanf. package fmt ("fmt"). Documented at pkg.go.dev, fmt.
func schemaFor(t reflect.Type, seen []reflect.Type) (*schema, error)
schemaFor builds the schema for a Go type.
schemaFor builds the schema for a Go type. func schemaFor(t reflect.Type, seen []reflect.Type) (*schema, error). In ch12/agent/schema.go, line 83.
package json ("encoding/json/v2")
Package json implements semantic processing of JSON as specified in RFC 8259.
Package json implements semantic processing of JSON as specified in RFC 8259. package json ("encoding/json/v2"). Documented at pkg.go.dev, encoding/json/v2.
func Marshal(in any, opts ...Options) (out []byte, err error)
Marshal serializes a Go value as a []byte according to the provided marshal and encode options (while ignoring unmarshal or decode options).
Marshal serializes a Go value as a []byte according to the provided marshal and encode options (while ignoring unmarshal or decode options). func Marshal(in any, opts ...Options) (out []byte, err error). Documented at pkg.go.dev, encoding/json/v2.Marshal.
field Description string
field Description string. In ch12/agent/types.go, line 73.
field Parameters jsontext.Value
a JSON Schema
a JSON Schema field Parameters jsontext.Value. In ch12/agent/types.go, line 74.
field Strict bool
Strict asks the provider to hold the arguments to the schema, where it can (chapter 7).
Strict asks the provider to hold the arguments to the schema, where it can (chapter 7). field Strict bool. In ch12/agent/types.go, line 78.
func strictOK(s *schema) bool
strictOK reports whether a schema meets providers' strict modes, which require every field at every depth to be listed as required.
strictOK reports whether a schema meets providers' strict modes, which require every field at every depth to be listed as required. func strictOK(s *schema) bool. In ch12/agent/schema.go, line 69.
var ctx context.Context
var ctx context.Context. In ch12/agent/registry.go, line 37.
var args jsontext.Value
var args jsontext.Value. In ch12/agent/registry.go, line 37.
package jsontext ("encoding/json/jsontext")
Package jsontext implements syntactic processing of JSON as specified in RFC 4627, RFC 7159, RFC 7493, RFC 8259, and RFC 8785.
Package jsontext implements syntactic processing of JSON as specified in RFC 4627, RFC 7159, RFC 7493, RFC 8259, and RFC 8785. package jsontext ("encoding/json/jsontext"). Documented at pkg.go.dev, encoding/json/jsontext.
func decodeStrict[A any](args jsontext.Value, s *schema) (A, error)
decodeStrict decodes a call's arguments into A and checks them against A's schema: every required field present and not null, no field the schema doesn't list, and strings within their enumerations.
decodeStrict decodes a call's arguments into A and checks them against A's schema: every required field present and not null, no field the schema doesn't list, and strings within their enumerations. func decodeStrict[A any](args jsontext.Value, s *schema) (A, error). In ch12/agent/registry.go, line 55.
func median[T int | float64](xs []T) T
func median[T int | float64](xs []T) T. In evidence/summary.go, line 356.
type parameter T int | float64
type parameter T int | float64. In evidence/summary.go, line 356.
type float64 float64
float64 is the set of all IEEE 754 64-bit floating-point numbers.
float64 is the set of all IEEE 754 64-bit floating-point numbers. type float64 float64. Documented at pkg.go.dev, builtin.float64.
package slices ("slices")
Package slices defines various functions useful with slices of any type.
Package slices defines various functions useful with slices of any type. package slices ("slices"). Documented at pkg.go.dev, slices.
func Sorted[E cmp.Ordered](seq iter.Seq[E]) []E
Sorted collects values from seq into a new slice, sorts the slice, and returns it.
Sorted collects values from seq into a new slice, sorts the slice, and returns it. func Sorted[E cmp.Ordered](seq iter.Seq[E]) []E. Documented at pkg.go.dev, slices.Sorted.
func Values[Slice ~[]E, E any](s Slice) iter.Seq[E]
Values returns an iterator that yields the slice elements in order.
Values returns an iterator that yields the slice elements in order. func Values[Slice ~[]E, E any](s Slice) iter.Seq[E]. Documented at pkg.go.dev, slices.Values.
func next[T interface{ model() string }](file, model string) (int, error)
next returns the number of the first new run for model, after the runs already saved, so runs can be added later.
next returns the number of the first new run for model, after the runs already saved, so runs can be added later. func next[T interface{ model() string }](file, model string) (int, error). In evidence/experiments.go, line 99.
type parameter T interface{model() string}
type parameter T interface{model() string}. In evidence/experiments.go, line 99.
func (interface).model() string
func (interface).model() string. In evidence/experiments.go, line 99.
func load[T any](file string) ([]T, error)
load reads every line of a JSON Lines file into a slice.
load reads every line of a JSON Lines file into a slice. func load[T any](file string) ([]T, error). In evidence/record.go, line 144.
