Help us shape the future of Go! Take the 2026 Go Developer Survey

loop

package
v0.2.7 Latest Latest
Warning

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

Go to latest
Published: Oct 28, 2026 License: Apache-2.0 Imports: 22 Imported by: 0

Documentation

Overview

Package loop is a controllable agent loop for CloudWeGo Eino, in an interface-first, nested shape. Read in this order:

loop.go                     the public Loop: what a caller can do
wire.go                     which concrete type fills each role
internal/engine/run.go      the loop itself, and every role it needs
then any internal package wire.go names

Index

Constants

View Source
const (
	StateIdle    = state.Idle
	StateRunning = state.Running
	StateDone    = state.Done
	StateStopped = state.Stopped
	StateError   = state.Error
)
View Source
const FinishToolName = "finish"

FinishToolName is the reserved tool that ends a StopOnFinishTool run. Its arguments become DoneData.FinalContent. It never reaches the ToolExecutor.

Variables

This section is empty.

Functions

func NewToolInfo

func NewToolInfo(name, desc string, params map[string]any) (*schema.ToolInfo, error)

NewToolInfo builds an Eino tool definition from a name, a description and a JSON Schema object for the parameters. A nil schema means no parameters.

Types

type CallOptionsBuilder

type CallOptionsBuilder interface {
	Build(temperature float32) []model.Option
}

CallOptionsBuilder builds the per-call model options. Default: temperature only.

type Config

type Config struct {
	MaxIterations    int           // cap on model calls; the loop never exceeds it
	StopCondition    StopCondition // when the loop ends on its own
	Temperature      float64       // below 0: not sent, so the provider's default applies
	IterationTimeout time.Duration // bound on one model call; zero: none
	TotalTimeout     time.Duration // bound on the whole run; zero: none

	Reflect               ReflectCondition // zero: off; reviews never count toward MaxIterations
	IterationReviewPrompt string           // ReflectOnIteration judge; empty: built-in
	FinishReviewPrompt    string           // ReflectOnFinish judge; empty: built-in
	MaxReopens            int              // ReflectOnFinish: re-opens after drift; zero: 1

	StreamEvents      bool          // false: only error and done events
	EventBuffer       int           // event channel capacity; zero: 64
	EventSink         EventSink     // optional
	DoneDeliveryGrace time.Duration // wait for a reader of the done event; zero: 5s

	Pauses map[string]pause.Handler // tool name → handler, in place of the ToolExecutor

	StopInstructions string // instructions for the stop condition

	AutoNudgeText    string // sent after a text-only reply in StopOnFinishTool; empty: built-in
	DisableAutoNudge bool

	RetryMaxAttempts int // extra attempts after a transient failure; zero: 1; -1: none
	ErrorClassifier  ErrorClassifier

	DisableStreaming                  bool          // call Generate instead of Stream
	IdleChunkTimeout                  time.Duration // longest gap between streamed chunks; zero: 30s
	DisableIterationTimeoutDecoupling bool          // each call inherits the run's remaining time

	HistoryCompactor HistoryCompactor
	CallOptions      CallOptionsBuilder
	ModelIdentity    ModelIdentifier
	CostEstimator    CostEstimator

	TranscriptLogLevel slog.Level // level for transcript lines (they contain prompt text)
}

Config configures one Loop. It holds values and interfaces only, no function fields. DefaultConfig returns sensible values.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig suits an interactive assistant: up to 12 model calls, stop when the model answers without a tool call, intermediate events on.

type ConfigChange

type ConfigChange = state.Change

ConfigChange changes settings while the loop runs. Nil fields are left unchanged.

type CostEstimator

type CostEstimator interface {
	Estimate(provider, model string, inputTokens, outputTokens int) float64
}

CostEstimator prices one turn for LLMTurn events. Default: 0.

type ErrorClassifier

type ErrorClassifier interface {
	IsTransient(err error) bool
}

ErrorClassifier decides whether a failed model call is worth retrying. Default: deadline exceeded, EOF, empty reply, idle stream, connection reset.

type EventSink

type EventSink interface {
	Write(evt event.Event)
}

EventSink receives every event, synchronously, before it is offered to the event channel. It must not block for long.

type HistoryCompactor

type HistoryCompactor interface {
	Compact(msgs []*schema.Message) []*schema.Message
}

HistoryCompactor shapes the history before each model call. Its result is what the model receives; the stored history is not changed.

type Loop

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

Loop runs one agent conversation: call the model, run the tools it asks for, feed the results back, repeat. Start it once; it is safe for concurrent use. New (wire.go) builds it.

func New

func New(cfg Config, llm model.ToolCallingChatModel, tools []*schema.ToolInfo, executor ToolExecutor, logger *slog.Logger) (*Loop, error)

New builds a Loop. It is the only place that picks a concrete type for each role, so this function alone tells you what runs. llm is any Eino tool-calling chat model; when tools is not empty it is bound to them. A nil logger discards log output.

func (*Loop) Events

func (l *Loop) Events() <-chan event.Event

Events returns the event channel, the same one Start returns.

func (*Loop) Inject

func (l *Loop) Inject(message string) bool

Inject queues a user message. It returns false when the queue is full.

func (*Loop) Iteration

func (l *Loop) Iteration() int

Iteration returns the number of model calls started so far.

func (*Loop) Messages

func (l *Loop) Messages() []*schema.Message

Messages returns a copy of the conversation as it stands now.

func (*Loop) ModifyConfig

func (l *Loop) ModifyConfig(c ConfigChange) bool

ModifyConfig queues a settings change. It returns false when the queue is full.

func (*Loop) Result

func (l *Loop) Result() (event.DoneData, bool)

Result returns the outcome. ok is false until the loop has ended.

func (*Loop) Start

func (l *Loop) Start(ctx context.Context, messages []*schema.Message) <-chan event.Event

Start begins the run and returns the event channel, which is closed after the done event. The loop keeps its own copy of messages. A second call returns a channel holding one fatal error.

func (*Loop) State

func (l *Loop) State() State

State returns the lifecycle state.

func (*Loop) Stop

func (l *Loop) Stop()

Stop asks the loop to stop at the next iteration boundary, or at once if a pause handler is waiting for a reply. It cannot be lost to a full queue.

func (*Loop) Wait

func (l *Loop) Wait()

Wait blocks until the run has ended and the event channel is closed.

type ModelIdentifier

type ModelIdentifier interface {
	Identity() (provider, model string)
}

ModelIdentifier names the provider and model for LLMTurn events. It is asked once per turn, so it may change during a run.

type ReflectCondition

type ReflectCondition int
const (
	// ReflectOff never reviews.
	ReflectOff ReflectCondition = iota // default: no reflection
	// ReflectOnIteration reviews every iteration, whatever the stop
	// condition, and feeds any correction into the next iteration.
	ReflectOnIteration
	// ReflectOnFinish judges the final answer against the system prompt and
	// user request. If it drifted, the run re-opens with the judge's notes,
	// at most MaxReopens times. It only applies when the model finishes.
	ReflectOnFinish
)

type State

type State = state.Status

State is the lifecycle state of a Loop.

type StopCondition

type StopCondition int

StopCondition decides when the loop ends on its own.

const (
	// StopOnNoToolFinish ends the loop when the model replies without calling
	// a tool. Suits chat and question answering.
	StopOnNoToolFinish StopCondition = iota
	// StopOnFinishTool runs until the model calls FinishToolName.
	StopOnFinishTool
	// StopOnExplicit runs until a stop signal, the iteration cap or a timeout.
	StopOnExternal
)

type ToolExecutor

type ToolExecutor interface {
	ExecuteTool(ctx context.Context, name, arguments string) (string, error)
}

ToolExecutor runs the tools the model asks for. An error is not fatal: the model receives it as the tool's result and can react.

Directories

Path Synopsis
Package event is the vocabulary of a running loop: every event type and payload.
Package event is the vocabulary of a running loop: every event type and payload.
internal
audit
Package audit builds the LLMTurn record of each model call that led to tool calls.
Package audit builds the LLMTurn record of each model call that led to tool calls.
control
Package control holds the controls sent to a running loop: stop, inject a message, change settings.
Package control holds the controls sent to a running loop: stop, inject a message, change settings.
defaults
Package defaults is what runs when the caller supplies nothing: one no-op or built-in implementation per optional interface, so no other package has to check for nil.
Package defaults is what runs when the caller supplies nothing: one no-op or built-in implementation per optional interface, so no other package has to check for nil.
dispatch
Package dispatch runs one tool call: through the ToolExecutor, or through the pause handler registered for that tool.
Package dispatch runs one tool call: through the ToolExecutor, or through the pause handler registered for that tool.
emitter
Package emitter owns the event channel: it filters, counts dropped events, and guarantees the done event is sent once and waited for.
Package emitter owns the event channel: it filters, counts dropped events, and guarantees the done event is sent once and waited for.
engine
Package engine is the loop itself.
Package engine is the loop itself.
modelcall
Package modelcall makes one logical model call: iteration timeout, streaming with an idle-chunk watchdog, and retries on transient failure.
Package modelcall makes one logical model call: iteration timeout, streaming with an idle-chunk watchdog, and retries on transient failure.
state
Package state holds all mutable state of one loop run behind one mutex.
Package state holds all mutable state of one loop run behind one mutex.
stopping_policy
Package stopping holds one stop policy per stop condition.
Package stopping holds one stop policy per stop condition.
transcript
Package transcript logs the conversation as it grows.
Package transcript logs the conversation as it grows.
Package pause is what a pause handler sees and can do.
Package pause is what a pause handler sees and can do.
Package registry tracks running loops by a key of the caller's choice, such as a user or session id, so another goroutine can stop or steer them.
Package registry tracks running loops by a key of the caller's choice, such as a user or session id, so another goroutine can stop or steer them.

Jump to

Keyboard shortcuts

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