middleware

package
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 27, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package middleware provides the tracing and metrics of agent runs, which plug into an agent with agent.WithMiddleware, and the types other middleware builds on. Tracing and metrics follow the OpenTelemetry GenAI semantic conventions without depending on OpenTelemetry; the github.com/nostosx/agentx-contrib/otel module adapts them, and github.com/nostosx/agentx-contrib/middleware provides logging and retries.

For model calls outside the agent loop, such as the model of a actx.Summarize strategy, the package also provides plain decorators: TraceModel and MeterModel wrap a model.Model, and TraceTools and MeterTools are tool.Middleware.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AddEvent

func AddEvent(ctx context.Context, name string, attrs ...Attr)

AddEvent records an event on the span of ctx, if it is an EventSpan.

func ErrorType

func ErrorType(err error) string

ErrorType returns the low-cardinality error.type the spans and metrics of this package record for err: "timeout", "canceled", the provider's error type or HTTP status code, "panic" or "_OTHER". Middleware that records errors, such as retries, uses it to stay consistent.

func MarkCompacted

func MarkCompacted(ctx context.Context, run *loop.Run)

MarkCompacted records that run compacted its conversation; ctx must belong to the run. Its later model spans carry gen_ai.conversation.compacted.

func MeterTools

func MeterTools(m Meter, opts ...MetricsOption) tool.Middleware

MeterTools records gen_ai.execute_tool.duration for each tool call.

func Metrics

func Metrics(m Meter, opts ...MetricsOption) loop.Middleware

Metrics returns runtime middleware that records the metrics described at Meter for each run and its model and tool calls.

Register it after Tracing, so measurements carry the spans, and before retries, so a model call's duration includes them.

func RecordSpanError

func RecordSpanError(span Span, err error)

RecordSpanError records err on span with a low-cardinality error.type, as the spans of this package do.

func TraceTools

func TraceTools(t Tracer, opts ...TraceOption) tool.Middleware

TraceTools records a span for each tool call.

func Tracing

func Tracing(t Tracer, opts ...TraceOption) loop.Middleware

Tracing returns runtime middleware that records an invoke_agent span for each run, with the spans of TraceModel and TraceTools as its children. The spans carry the agent name, the session ID as gen_ai.conversation.id, the run ID and the turn.

The run's context carries the tracer, so hooks and compaction add their spans with StartSpan, and a sub-agent traced the same way nests under the execute_tool span that runs it.

func Wrap

func Wrap(m model.Model, mws ...Model) model.Model

Wrap applies middlewares to m. The first middleware is the outermost.

Types

type Attr

type Attr struct {
	Key   string
	Value any
}

Attr is a span attribute.

type EventSpan

type EventSpan interface {
	Span
	AddEvent(name string, attrs ...Attr)
}

EventSpan is a Span that records events. Spans that do not implement it drop the events of this package, such as the retries of a model call.

type Float64Histogram

type Float64Histogram interface {
	Record(ctx context.Context, v float64, attrs ...Attr)
}

Float64Histogram records a distribution of floats.

type Funcs

type Funcs struct {
	Next         model.Model
	GenerateFunc func(ctx context.Context, req *model.Request) (*model.Response, error)
	StreamFunc   func(ctx context.Context, req *model.Request) iter.Seq2[*model.StreamEvent, error]
}

Funcs builds a model from functions. A nil function delegates to Next, and Describe reports Next's description.

func (*Funcs) Describe

func (f *Funcs) Describe() model.Description

Describe implements model.Describer.

func (*Funcs) Generate

func (f *Funcs) Generate(ctx context.Context, req *model.Request) (*model.Response, error)

Generate implements model.Model.

func (*Funcs) Stream

func (f *Funcs) Stream(ctx context.Context, req *model.Request) iter.Seq2[*model.StreamEvent, error]

Stream implements model.Model.

type Instrument

type Instrument struct {
	Name        string
	Description string
	// Unit is a UCUM unit, such as "s" or "{token}".
	Unit string
	// Buckets are the explicit bucket boundaries the conventions advise for
	// a histogram. Adapters may ignore them.
	Buckets []float64
}

Instrument describes a metric instrument.

type Int64Counter

type Int64Counter interface {
	Add(ctx context.Context, n int64, attrs ...Attr)
}

Int64Counter adds to a monotonic sum.

type Int64Histogram

type Int64Histogram interface {
	Record(ctx context.Context, v int64, attrs ...Attr)
}

Int64Histogram records a distribution of integers.

type Meter

type Meter interface {
	Int64Counter(Instrument) Int64Counter
	Int64Histogram(Instrument) Int64Histogram
	Float64Histogram(Instrument) Float64Histogram
}

Meter creates metric instruments. Adapt OpenTelemetry or another metrics library to it; agentx itself has no metrics dependency. Implementations and their instruments must be safe for concurrent use. The context of a measurement holds the span of the measured operation, for exemplars.

Metrics and MeterModel record these instruments of the OpenTelemetry GenAI semantic conventions (https://github.com/open-telemetry/semantic-conventions-genai):

  • gen_ai.client.inference.duration (s), and for streaming calls gen_ai.client.inference.time_to_first_chunk and gen_ai.client.inference.time_per_output_chunk;
  • the gen_ai.client.inference.usage.input_tokens, .output_tokens, .cache_read.input_tokens, .cache_write.input_tokens and .reasoning.output_tokens counters, with gen_ai.token.modality "unknown", as providers do not break usage down by modality;
  • the gen_ai.client.inference.operation.input_tokens and .output_tokens histograms;
  • gen_ai.execute_tool.duration (s), whose count is the number of tool calls;
  • gen_ai.invoke_agent.duration (s), with agentx.run.status, and the gen_ai.invoke_agent.inference_calls and .tool_calls histograms, which count only a run's own calls, not those of its sub-agents.

Model metrics carry gen_ai.operation.name, gen_ai.provider.name, gen_ai.request.model, gen_ai.response.model and server.address and server.port; run and tool metrics carry gen_ai.agent.name. Failed operations add error.type as spans do.

type MetricsOption

type MetricsOption func(*metricsConfig)

MetricsOption configures metrics.

func LegacyMetrics

func LegacyMetrics() MetricsOption

LegacyMetrics also records the model metrics that earlier releases of the conventions defined and the current ones replaced, for dashboards that still query them: the gen_ai.client.token.usage histogram, with gen_ai.token.type "input" or "output", and gen_ai.client.operation.duration.

type Model

type Model func(model.Model) model.Model

Model wraps a model.Model.

func MeterModel

func MeterModel(m Meter, opts ...MetricsOption) Model

MeterModel records the model metrics described at Meter for each call.

func TraceModel

func TraceModel(t Tracer, opts ...TraceOption) Model

TraceModel records a span for each model call. The provider, operation and default model come from the model's model.Describer.

type NoopMeter

type NoopMeter struct{}

NoopMeter discards measurements.

func (NoopMeter) Float64Histogram

func (NoopMeter) Float64Histogram(Instrument) Float64Histogram

Float64Histogram implements Meter.

func (NoopMeter) Int64Counter

func (NoopMeter) Int64Counter(Instrument) Int64Counter

Int64Counter implements Meter.

func (NoopMeter) Int64Histogram

func (NoopMeter) Int64Histogram(Instrument) Int64Histogram

Int64Histogram implements Meter.

type NoopTracer

type NoopTracer struct{}

NoopTracer discards spans.

func (NoopTracer) Start

func (NoopTracer) Start(ctx context.Context, _ string, _ SpanKind, _ ...Attr) (context.Context, Span)

Start implements Tracer.

type Span

type Span interface {
	SetAttributes(attrs ...Attr)
	// RecordError records err and marks the span as failed: adapters should
	// set the span status to error.
	RecordError(err error)
	End()
}

Span is an in-progress trace span.

func SpanFromContext

func SpanFromContext(ctx context.Context) Span

SpanFromContext returns the innermost span the tracing of this package started in ctx, or a span that discards everything.

func StartSpan

func StartSpan(ctx context.Context, name string, kind SpanKind, attrs ...Attr) (context.Context, Span)

StartSpan starts a span with the tracer of the run ctx belongs to, as installed by Tracing, for work done inside the run, such as hooks. Without such a tracer it returns ctx and a span that discards everything.

type SpanKind

type SpanKind int

SpanKind is the kind of a span, as in OpenTelemetry.

const (
	// SpanKindInternal marks work done in the process, such as agent runs
	// and tool calls.
	SpanKindInternal SpanKind = iota
	// SpanKindClient marks calls to a remote service, such as model calls.
	SpanKindClient
)

type TraceOption

type TraceOption func(*traceConfig)

TraceOption configures tracing.

func RecordContent

func RecordContent() TraceOption

RecordContent records the content of model and tool calls: the system prompt, messages and tool names of model calls and runs (gen_ai.system_instructions, gen_ai.input.messages, gen_ai.output.messages and gen_ai.tool.definitions, as JSON strings), and the arguments and results of tool calls (gen_ai.tool.call.arguments and gen_ai.tool.call.result). They may contain sensitive data and can be large, so the conventions make them opt-in.

type Tracer

type Tracer interface {
	Start(ctx context.Context, name string, kind SpanKind, attrs ...Attr) (context.Context, Span)
}

Tracer starts spans. Adapt OpenTelemetry or another tracing library to it; agentx itself has no tracing dependency.

Spans follow the OpenTelemetry GenAI semantic conventions (https://github.com/open-telemetry/semantic-conventions-genai):

  • "invoke_agent {agent}" (internal) covers a run of the agent loop;
  • "{operation} {model}" (client), such as "chat gpt-6-astra", covers a model call;
  • "execute_tool {tool}" (internal) covers a tool call. A sub-agent run by agent.AsTool is a child of its execute_tool span.

Work the conventions do not cover gets agentx spans: "hook {event}" for the hooks of a lifecycle event and "compact_context" for an automatic compaction. Retries of a model call are "agentx.retry" events.

Attributes use the gen_ai.* names of the conventions, plus agentx.run_id, agentx.turn and agentx.run.status. The span of an agent a run was handed off to has agentx.handoff.from, the agent that handed it off; a run that a guardrail blocked has agentx.guardrail.name and agentx.guardrail.kind and an "agentx.guardrail" event, which also holds the guardrail's message when content is recorded. A failed operation sets error.type to the provider's error code, "timeout", "canceled", "panic", "tool_error" for a tool that returned an error result, or "_OTHER".

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL