Documentation
¶
Overview ¶
Package runner drives the agent through a real conversation — either a single one-shot prompt (Headless) or a multi-turn stdin REPL (REPL). Both share an Agent under the hood and route partial text + tool-call summaries through the same streaming consumer.
Index ¶
- Constants
- Variables
- func AnsiMagenta() string
- func FormatAlertLine(from, kind, text string) string
- func Headless(ctx context.Context, m adkmodel.LLM, prompt string, stdout, stderr io.Writer, ...) (int, error)
- func IsTerminal(w io.Writer) bool
- func REPL(ctx context.Context, m adkmodel.LLM, stdin io.Reader, stdout, stderr io.Writer, ...) (int, error)
- func REPLWithAgent(ctx context.Context, a *agent.Agent, m adkmodel.LLM, stdin io.Reader, ...) (int, error)
- func WriteEvents(events iter.Seq2[*session.Event, error], out, info io.Writer, ...) error
- func WriteSummary(w io.Writer, t *usage.Tracker, modelID string)
- type EventsOption
Constants ¶
const ( ExitOK = 0 ExitAgentError = 1 ExitConfigError = 2 )
Exit codes — kept distinct so CI can disambiguate failure modes.
Variables ¶
var ErrNotTerminal = errors.New("runner: stdin is not a terminal; turnInterrupter unavailable")
ErrNotTerminal is returned when newTurnInterrupter is called with a stdin that isn't a terminal. The REPL falls back to legacy behavior in that case (no mid-turn cancel; the existing process- level SIGINT continues to mean "exit").
Functions ¶
func AnsiMagenta ¶ added in v1.2.0
func AnsiMagenta() string
AnsiMagenta returns the ANSI escape used for ↪-prefixed lines so downstream alert sinks can match the runner's color choice without duplicating the constant.
func FormatAlertLine ¶ added in v1.2.0
FormatAlertLine renders one background-agent Alert as a single "↪ <from> <kind>: <text>" line in the same shape as the ↪ google_search lines from v1.1.0. Reuses the magenta sigil so consumers see a consistent "server-side / out-of-band activity" signal regardless of source.
Exposed so consumers wiring their own alert sinks (a Slack adapter, a web UI, a logger) format alerts consistently with what the CLI shows. Use with paint(line, runner.AnsiMagenta(), color) when you want ANSI styling; raw string otherwise.
func Headless ¶
func Headless(ctx context.Context, m adkmodel.LLM, prompt string, stdout, stderr io.Writer, tracker *usage.Tracker, pricing usage.Pricing, agentOpts []agent.Option, eventsOpts ...EventsOption) (int, error)
Headless executes prompt against m and streams the assistant's text to stdout as partial events arrive. Tool-call summaries are written to stderr as one line per call. Returns an exit code suitable for os.Exit.
agentOpts lets the caller pass extra agent.Options (typically WithTools, WithToolsets, WithSystemInstructionPrefix). Pass nil for no tools and the default instruction.
tracker (optional) records per-turn usage; when supplied, the caller can write a summary using its totals after Headless returns. Pass nil to skip accounting.
eventsOpts forwards through to WriteEvents (e.g. WithColor) so callers can opt into ANSI styling without reaching into the formatter directly.
A trailing newline is always added to stdout when at least one chunk was written, so shell pipelines see a clean terminator.
func IsTerminal ¶
IsTerminal reports whether w is connected to a terminal (TTY). Use to gate WithColor: pass WithColor(IsTerminal(os.Stdout)) so color renders interactively but not when output is captured to a file or piped to another process.
Returns false for any writer that isn't a *os.File (buffers, pipes, network connections). On Unix and modern Windows, character devices report ModeCharDevice; that's the signal we trust.
func REPL ¶
func REPL(ctx context.Context, m adkmodel.LLM, stdin io.Reader, stdout, stderr io.Writer, tracker *usage.Tracker, pricing usage.Pricing, agentOpts []agent.Option, eventsOpts ...EventsOption) (int, error)
REPL drives a multi-turn stdin loop against m. Each line is sent through the same Agent so the ADK runner accumulates conversation history across turns (the in-memory session service appends events per session ID, which the Agent reuses).
Prompts are written to stdout, partial assistant text streams back inline, and tool-call summaries go to stderr — same shape as Headless. Built-in commands: `/exit`, `/quit`, EOF (Ctrl-D).
agentOpts mirrors Headless. The same tracker/pricing pair is used across every turn so the final summary is meaningful. eventsOpts (e.g. WithColor) forward through to WriteEvents for every turn.
SIGINT (Ctrl+C) is owned by the REPL when stdin is a TTY: the double-press-within-1s exits gesture works both during a turn (the per-turn turnInterrupter handles raw-mode bytes) and between turns (this function's signal handler manages the SIGINT state machine). The bundled CLI's main.go does NOT include SIGINT in its signal.NotifyContext for that reason; passing a ctx whose parent cancels on SIGINT would defeat the gesture.
func REPLWithAgent ¶ added in v1.8.0
func REPLWithAgent(ctx context.Context, a *agent.Agent, m adkmodel.LLM, stdin io.Reader, stdout, stderr io.Writer, tracker *usage.Tracker, pricing usage.Pricing, eventsOpts ...EventsOption) (int, error)
REPLWithAgent runs the REPL against a pre-constructed Agent. Useful for tests that need a reference to the agent (to call Inject from outside the loop, for example) and for library consumers that construct the Agent themselves. Equivalent to REPL minus the agent.New() call at the top.
func WriteEvents ¶
func WriteEvents(events iter.Seq2[*session.Event, error], out, info io.Writer, opts ...EventsOption) error
WriteEvents formats events from agent.Run(...) for human-readable streaming display. It's the library-friendly counterpart to the formatter inside Headless / REPL — library callers who want their agent loop to look like an interactive chat session can pass the returned iterator straight in.
Routing:
- Partial assistant text (event.Partial == true) streams to out as it arrives, with no prefix — so a model's reply renders character-by-character like an interactive chat.
- Tool calls render as `→ name(key=value, ...)` to info.
- Tool responses render as `← name(key=value, ...)` to info.
- Server-side built-in evidence (Gemini grounding metadata) renders as `↪ google_search: ...` to info, AFTER the model's text, since the metadata only lands on the aggregated final event.
- Final TurnComplete events are skipped (they repeat the text already streamed via Partial events).
Identical → / ← / ↪ lines are deduplicated within one WriteEvents call. ADK's streaming response aggregator can yield the same FunctionCall part across more than one event (an intermediate aggregate plus a final); Vertex's grounding metadata had the same shape of multi-emission. Two genuinely different tool calls (same name, different args) render separately because the formatted line differs.
out and info may point at the same writer (e.g. both os.Stdout) when you want a single combined stream — useful for tmux capture or piping. They're separate parameters so the default CLI path can keep tool chatter on stderr away from the assistant's reply on stdout.
Pass WithColor(true) to enable ANSI styling — typically gated on IsTerminal(out) so piped output stays clean.
Returns the first iterator error, or nil on clean completion. A trailing newline is written to out if any text was streamed.
Types ¶
type EventsOption ¶
type EventsOption func(*eventsConfig)
EventsOption configures WriteEvents. Use the With* helpers below.
func WithColor ¶
func WithColor(on bool) EventsOption
WithColor enables ANSI color codes in WriteEvents output. Tool calls and responses render in cyan; partial assistant text in green. Off by default — colored output looks like garbage when piped to a file, so callers must opt in (typically guarded by IsTerminal(out)).