middleware

package
v1.5.3 Latest Latest
Warning

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

Go to latest
Published: Aug 8, 2026 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package middleware provides composable hooks for the agent execution loop.

It mirrors the middleware system introduced in langchain's agent factory (langchain.agents.middleware), exposing hooks that wrap model requests and tool calls.

Index

Constants

This section is empty.

Variables

View Source
var ErrNoResponse = errors.New("no response from human in the loop")

ErrNoResponse is returned by HumanInTheLoop when the human provided no input.

Functions

This section is empty.

Types

type Chain

type Chain struct {
	// contains filtered or unexported fields
}

Chain composes a list of middleware into a single model and tool pipeline.

func NewChain

func NewChain(middleware ...Middleware) *Chain

NewChain builds a Chain from the given middleware. The middleware are invoked in order for the "before" phase and in reverse order for the "after" phase, forming a nested onion.

func (*Chain) Get

func (c *Chain) Get() []Middleware

Get returns the underlying middleware list.

func (*Chain) RunModel

func (c *Chain) RunModel(
	ctx context.Context,
	state *State,
	req *ModelRequest,
	plan func(ctx context.Context) ([]schema.AgentAction, *schema.AgentFinish, error),
) *ModelResponse

RunModel executes the model pipeline composed of all middleware.

The final step invokes plan, which must return actions and/or a finish. If plan returns an error, it is recorded on the response and the after hooks still run so they can recover (e.g. ModelRetry).

func (*Chain) RunTool

func (c *Chain) RunTool(
	ctx context.Context,
	state *State,
	req *ToolRequest,
	call func(ctx context.Context, input string) (string, error),
) *ToolResponse

RunTool executes the tool pipeline composed of all middleware.

The final step invokes call, which executes the tool and returns an observation. Errors are recorded on the response so after-hooks can recover.

type HumanInTheLoop

type HumanInTheLoop struct {
	PassThrough
	// contains filtered or unexported fields
}

HumanInTheLoop inserts a human approval step before tool calls are executed, mirroring HumanInTheLoopMiddleware in langchain.

func NewHumanInTheLoop

func NewHumanInTheLoop(cfg HumanInTheLoopConfig) *HumanInTheLoop

NewHumanInTheLoop builds the middleware.

func (*HumanInTheLoop) AfterModel

func (m *HumanInTheLoop) AfterModel(ctx context.Context, state *State, req *ModelRequest, resp *ModelResponse, next Next)

AfterModel passes through.

func (*HumanInTheLoop) AfterTool

func (m *HumanInTheLoop) AfterTool(ctx context.Context, state *State, req *ToolRequest, resp *ToolResponse, next Next)

AfterTool passes through.

func (*HumanInTheLoop) BeforeModel

func (m *HumanInTheLoop) BeforeModel(ctx context.Context, state *State, req *ModelRequest, next Next)

BeforeModel passes through.

func (*HumanInTheLoop) BeforeTool

func (m *HumanInTheLoop) BeforeTool(ctx context.Context, state *State, req *ToolRequest, next Next)

BeforeTool runs the human validator.

type HumanInTheLoopConfig

type HumanInTheLoopConfig struct {
	// BeforeToolValidator, when set, lets a human approve/reject a tool call.
	// If it returns false, the tool observation is set to a rejection message.
	BeforeToolValidator func(action string, input string) (bool, string)
	// FallbackObservation is used when a tool call is rejected.
	FallbackObservation string
}

HumanInTheLoopConfig configures a HumanInTheLoop middleware.

type Middleware

type Middleware interface {
	// BeforeModel runs before the model is invoked.
	BeforeModel(ctx context.Context, state *State, req *ModelRequest, next Next)
	// AfterModel runs after the model returns.
	AfterModel(ctx context.Context, state *State, req *ModelRequest, resp *ModelResponse, next Next)
	// BeforeTool runs before a tool is called.
	BeforeTool(ctx context.Context, state *State, req *ToolRequest, next Next)
	// AfterTool runs after a tool returns.
	AfterTool(ctx context.Context, state *State, req *ToolRequest, resp *ToolResponse, next Next)
}

Middleware is a composable hook that can wrap model requests and tool calls.

All hooks are optional: implementations only need to define the methods they care about. The zero value of a hook func is nil, which skips that hook.

type MiddlewareConfig

type MiddlewareConfig struct {
	BeforeModel func(ctx context.Context, state *State, req *ModelRequest, next Next)
	AfterModel  func(ctx context.Context, state *State, req *ModelRequest, resp *ModelResponse, next Next)
	BeforeTool  func(ctx context.Context, state *State, req *ToolRequest, next Next)
	AfterTool   func(ctx context.Context, state *State, req *ToolRequest, resp *ToolResponse, next Next)
}

MiddlewareConfig collects the optional hook funcs for a middleware.

type MiddlewareFunc

type MiddlewareFunc struct {
	// contains filtered or unexported fields
}

MiddlewareFunc adapts a set of hook functions into a Middleware.

func NewMiddleware

func NewMiddleware(cfg MiddlewareConfig) *MiddlewareFunc

NewMiddleware builds a MiddlewareFunc from the provided hook funcs.

func (*MiddlewareFunc) AfterModel

func (m *MiddlewareFunc) AfterModel(ctx context.Context, state *State, req *ModelRequest, resp *ModelResponse, next Next)

AfterModel implements Middleware.

func (*MiddlewareFunc) AfterTool

func (m *MiddlewareFunc) AfterTool(ctx context.Context, state *State, req *ToolRequest, resp *ToolResponse, next Next)

AfterTool implements Middleware.

func (*MiddlewareFunc) BeforeModel

func (m *MiddlewareFunc) BeforeModel(ctx context.Context, state *State, req *ModelRequest, next Next)

BeforeModel implements Middleware.

func (*MiddlewareFunc) BeforeTool

func (m *MiddlewareFunc) BeforeTool(ctx context.Context, state *State, req *ToolRequest, next Next)

BeforeTool implements Middleware.

type ModelCallLimit

type ModelCallLimit struct {
	PassThrough
	// contains filtered or unexported fields
}

ModelCallLimit aborts the agent after MaxCalls model invocations.

It counts every model request that passes through the before hook. When the limit is exceeded it records an error on the model response and skips the underlying invocation.

func NewModelCallLimit

func NewModelCallLimit(maxCalls int) *ModelCallLimit

NewModelCallLimit builds a ModelCallLimit middleware.

func (*ModelCallLimit) AfterModel

func (m *ModelCallLimit) AfterModel(ctx context.Context, state *State, req *ModelRequest, resp *ModelResponse, next Next)

AfterModel records an error when the limit was exceeded.

func (*ModelCallLimit) BeforeModel

func (m *ModelCallLimit) BeforeModel(ctx context.Context, state *State, req *ModelRequest, next Next)

BeforeModel enforces the call limit.

type ModelRequest

type ModelRequest struct {
	// Inputs are the inputs passed to the model, including the scratchpad.
	Inputs map[string]any
	// Options are the chain call options.
	Options []any
	// Step is the index of the current iteration.
	Step int
	// Skip, when set by a before-hook, prevents the underlying plan from
	// running. The after-hooks still execute so they can record the outcome.
	Skip bool
}

ModelRequest wraps a single model (plan) invocation.

type ModelResponse

type ModelResponse struct {
	// Actions are the actions the model decided to take.
	Actions []schema.AgentAction
	// Finish is set when the model decided to finish.
	Finish *schema.AgentFinish
	// Err is set when the model invocation failed.
	Err error
}

ModelResponse is the result of a model invocation.

type ModelRetry

type ModelRetry struct {
	PassThrough
	// contains filtered or unexported fields
}

ModelRetry retries the model invocation when it fails, up to MaxAttempts.

func NewModelRetry

func NewModelRetry(cfg RetryConfig) *ModelRetry

NewModelRetry builds a ModelRetry middleware.

func (*ModelRetry) AfterModel

func (m *ModelRetry) AfterModel(ctx context.Context, state *State, req *ModelRequest, resp *ModelResponse, next Next)

AfterModel retries the pipeline when the model returns an error.

func (*ModelRetry) BeforeModel

func (m *ModelRetry) BeforeModel(ctx context.Context, state *State, req *ModelRequest, next Next)

BeforeModel passes through.

type Next

type Next func(ctx context.Context)

Next is the function that continues the chain to the next middleware.

type PII

type PII struct {
	PassThrough
	// contains filtered or unexported fields
}

PII replaces likely email addresses and phone numbers in model requests with placeholders, mirroring PIIMiddleware in langchain. It is a thin, dependency free heuristic.

func NewPII

func NewPII(enabled bool) *PII

NewPII builds a PII middleware. When enabled, it redacts PII from inputs.

func (*PII) AfterModel

func (p *PII) AfterModel(ctx context.Context, state *State, req *ModelRequest, resp *ModelResponse, next Next)

AfterModel passes through.

func (*PII) AfterTool

func (p *PII) AfterTool(ctx context.Context, state *State, req *ToolRequest, resp *ToolResponse, next Next)

AfterTool passes through.

func (*PII) BeforeModel

func (p *PII) BeforeModel(ctx context.Context, state *State, req *ModelRequest, next Next)

BeforeModel redacts PII from the model inputs.

func (*PII) BeforeTool

func (p *PII) BeforeTool(ctx context.Context, state *State, req *ToolRequest, next Next)

BeforeTool passes through.

type PassThrough

type PassThrough struct{}

PassThrough is a no-op middleware useful as a base or in tests.

func (PassThrough) AfterModel

func (PassThrough) AfterModel(ctx context.Context, state *State, req *ModelRequest, resp *ModelResponse, next Next)

AfterModel implements Middleware.

func (PassThrough) AfterTool

func (PassThrough) AfterTool(ctx context.Context, state *State, req *ToolRequest, resp *ToolResponse, next Next)

AfterTool implements Middleware.

func (PassThrough) BeforeModel

func (PassThrough) BeforeModel(ctx context.Context, state *State, req *ModelRequest, next Next)

BeforeModel implements Middleware.

func (PassThrough) BeforeTool

func (PassThrough) BeforeTool(ctx context.Context, state *State, req *ToolRequest, next Next)

BeforeTool implements Middleware.

type RetryConfig

type RetryConfig struct {
	// MaxAttempts is the maximum number of model attempts before giving up.
	MaxAttempts int
	// ShouldRetry reports whether a given error should be retried.
	ShouldRetry func(error) bool
	// OnRetry is invoked before each retry with the attempt index and error.
	OnRetry func(attempt int, err error)
}

RetryConfig configures ModelRetry.

type State

type State struct {
	// Inputs are the original agent inputs.
	Inputs map[string]string
	// Steps are the intermediate steps accumulated so far.
	Steps []schema.AgentStep
	// Output is the final output, set when the agent finishes.
	Output map[string]any
}

State is the mutable state shared across middleware during a single agent run. It carries the inputs, the accumulated steps, and the final output.

type Summarization

type Summarization struct {
	PassThrough
	// MaxSteps is the maximum number of steps to retain before summarizing.
	MaxSteps int
	// SummarizeFunc, when set, is called to compact the steps.
	SummarizeFunc func(steps []schema.AgentStep) []schema.AgentStep
}

Summarization trims the accumulated steps to a trailing window so that long agent runs stay within the model context window. It mirrors SummarizationMiddleware in langchain.

func NewSummarization

func NewSummarization(maxSteps int, f func([]schema.AgentStep) []schema.AgentStep) *Summarization

NewSummarization builds a Summarization middleware.

func (*Summarization) AfterModel

func (m *Summarization) AfterModel(ctx context.Context, state *State, req *ModelRequest, resp *ModelResponse, next Next)

AfterModel passes through.

func (*Summarization) AfterTool

func (m *Summarization) AfterTool(ctx context.Context, state *State, req *ToolRequest, resp *ToolResponse, next Next)

AfterTool passes through.

func (*Summarization) BeforeModel

func (m *Summarization) BeforeModel(ctx context.Context, state *State, req *ModelRequest, next Next)

BeforeModel compacts the steps when they exceed MaxSteps.

func (*Summarization) BeforeTool

func (m *Summarization) BeforeTool(ctx context.Context, state *State, req *ToolRequest, next Next)

BeforeTool passes through.

type TodoList

type TodoList struct {
	PassThrough
	// contains filtered or unexported fields
}

TodoList injects a task list into the model request, mirroring TodoListMiddleware in langchain.

func NewTodoList

func NewTodoList(cfg TodoListConfig) *TodoList

NewTodoList builds a TodoList middleware.

func (*TodoList) AfterModel

func (m *TodoList) AfterModel(ctx context.Context, state *State, req *ModelRequest, resp *ModelResponse, next Next)

AfterModel passes through.

func (*TodoList) AfterTool

func (m *TodoList) AfterTool(ctx context.Context, state *State, req *ToolRequest, resp *ToolResponse, next Next)

AfterTool passes through.

func (*TodoList) BeforeModel

func (m *TodoList) BeforeModel(ctx context.Context, state *State, req *ModelRequest, next Next)

BeforeModel injects the todo list.

func (*TodoList) BeforeTool

func (m *TodoList) BeforeTool(ctx context.Context, state *State, req *ToolRequest, next Next)

BeforeTool passes through.

type TodoListConfig

type TodoListConfig struct {
	// TodoList is the list of tasks the agent should complete.
	TodoList []string
	// InjectSystemMessage, when set, is a function that adds the todo list to
	// the model inputs.
	InjectSystemMessage func(inputs map[string]any, todos []string)
}

TodoListConfig configures a TodoList middleware.

type ToolCallLimit

type ToolCallLimit struct {
	PassThrough
	// contains filtered or unexported fields
}

ToolCallLimit aborts the agent after MaxCalls tool invocations.

func NewToolCallLimit

func NewToolCallLimit(maxCalls int) *ToolCallLimit

NewToolCallLimit builds a ToolCallLimit middleware.

func (*ToolCallLimit) AfterTool

func (m *ToolCallLimit) AfterTool(ctx context.Context, state *State, req *ToolRequest, resp *ToolResponse, next Next)

AfterTool records an error when the limit was exceeded.

func (*ToolCallLimit) BeforeTool

func (m *ToolCallLimit) BeforeTool(ctx context.Context, state *State, req *ToolRequest, next Next)

BeforeTool enforces the limit.

type ToolError

type ToolError struct {
	PassThrough
	// FormatObservation, when set, transforms the raw error into an observation.
	FormatObservation func(err error) string
}

ToolError converts a tool error into a regular observation so the agent can recover instead of aborting. This mirrors ToolErrorMiddleware in langchain.

func NewToolError

func NewToolError(format func(error) string) *ToolError

NewToolError builds a ToolError middleware.

func (*ToolError) AfterTool

func (m *ToolError) AfterTool(ctx context.Context, state *State, req *ToolRequest, resp *ToolResponse, next Next)

AfterTool swallows tool errors and records them as observations.

func (*ToolError) BeforeTool

func (m *ToolError) BeforeTool(ctx context.Context, state *State, req *ToolRequest, next Next)

BeforeTool passes through.

type ToolRequest

type ToolRequest struct {
	// Action is the action describing the tool to call.
	Action schema.AgentAction
	// Step is the index of the current iteration.
	Step int
	// Skip, when set by a before-hook, prevents the underlying tool from
	// running. The after-hooks still execute so they can record the outcome.
	Skip bool
}

ToolRequest wraps a single tool call.

type ToolResponse

type ToolResponse struct {
	// Observation is the tool result.
	Observation string
	// Err is set when the tool call failed.
	Err error
}

ToolResponse is the result of a tool call.

type ToolRetry

type ToolRetry struct {
	PassThrough
	// contains filtered or unexported fields
}

ToolRetry retries a failing tool call up to MaxAttempts.

func NewToolRetry

func NewToolRetry(maxAttempts int, onRetry func(attempt int, err error)) *ToolRetry

NewToolRetry builds a ToolRetry middleware.

func (*ToolRetry) AfterTool

func (m *ToolRetry) AfterTool(ctx context.Context, state *State, req *ToolRequest, resp *ToolResponse, next Next)

AfterTool retries the tool call when it fails.

func (*ToolRetry) BeforeTool

func (m *ToolRetry) BeforeTool(ctx context.Context, state *State, req *ToolRequest, next Next)

BeforeTool passes through.

Jump to

Keyboard shortcuts

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