pluginapi

package
v0.1.96 Latest Latest
Warning

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

Go to latest
Published: Sep 22, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package pluginapi is the public contract between GoModel and its plugins.

A plugin imports this package and nothing else from GoModel. The package depends on the standard library only, so a plugin built as a shared object shares exactly the standard library and this package with the host.

Plugins implement Plugin plus any of the optional hook interfaces (RequestHook, PromptHook, ResponseHook, StreamHook, RouteStrategy, CompleteHook). Hooks receive an Exchange: a unified, dialect-neutral view of the request (Prompt) and response (Completion) that GoModel maps back onto the wire format after the hook returns.

Index

Constants

View Source
const Version = "0.2.0"

Version is the pluginapi contract version. It is informational: GoModel reports it in diagnostics and stamps it into BuildInfo, but never uses it to accept or reject a plugin (the Go toolchain already enforces that a shared object was built from identical sources).

Variables

This section is empty.

Functions

This section is empty.

Types

type Action

type Action string

Action is what a hook asks GoModel to do with the request or response.

const (
	// ActionAllow continues, with any edits already applied to the Exchange.
	ActionAllow Action = "allow"
	// ActionBlock rejects the request with Decision.Status, Code, and Message
	// rendered in the endpoint's native error dialect.
	ActionBlock Action = "block"
	// ActionRespond short-circuits: Decision.Response is sent to the client as
	// the completion with HTTP 200 (as a single-chunk stream when streaming).
	ActionRespond Action = "respond"
	// ActionWarn continues and records Decision.Detail in the audit trail and
	// the X-GoModel-Guardrail response headers.
	ActionWarn Action = "warn"
)

type Attempt

type Attempt struct {
	// Seq is the attempt number, starting at 1.
	Seq int
	// Kind is the attempt kind, for example "primary" or "failover".
	Kind string
	// Provider is the provider type; ProviderName the instance; Model the
	// provider model.
	Provider, ProviderName, Model string
	// StatusCode is the upstream HTTP status, when known.
	StatusCode int
	// Success reports whether the attempt produced a usable response.
	Success bool
	// ErrorCode is the gateway error code when the attempt failed.
	ErrorCode string
	// Duration is the wall-clock time of the attempt.
	Duration time.Duration
}

Attempt is one provider call made for the request.

type BuildInfo

type BuildInfo struct {
	// GoVersion is the Go toolchain version, for example "go1.27.1".
	GoVersion string
	// PluginAPIVersion is the [Version] of this package at build time.
	PluginAPIVersion string
}

BuildInfo records the toolchain a plugin binary was built with. It is filled by the `gomodel plugin build` helper and used only to produce a readable error when a shared object cannot be loaded.

type CacheInfo

type CacheInfo struct {
	// PlannedPrefixMessages is how many leading messages the provider cache
	// planner will mark as the cached prefix. Editing a message with a lower
	// index invalidates the cache for the session; appending does not.
	PlannedPrefixMessages int
	// SessionTarget is the sticky "provider/model" for the session, if any.
	SessionTarget string
}

CacheInfo exposes prompt-cache planning so plugins can avoid breaking it.

type ChangeKind

type ChangeKind string

ChangeKind says how a message (or completion choice) was edited.

const (
	// ChangeEdited marks a message whose parts or parameters were rewritten
	// in place; the host re-encodes only the touched parts.
	ChangeEdited ChangeKind = "edited"
	// ChangeInserted marks a message added by a plugin; the host encodes it
	// from the unified form.
	ChangeInserted ChangeKind = "inserted"
	// ChangeRemoved marks an original message a plugin removed.
	ChangeRemoved ChangeKind = "removed"
	// ChangeReplaced marks a completion choice whose text was replaced
	// wholesale with [Completion.ReplaceText].
	ChangeReplaced ChangeKind = "replaced"
)

type Changes

type Changes struct {
	// Messages maps message IDs (or "choice:<index>" for completions) to the
	// kind of change. Untouched messages are absent.
	Messages map[string]ChangeKind
	// Params holds parameters set through SetParam, by name.
	Params map[string]any
	// Dirty is true after any edit.
	Dirty bool
	// Edits counts the edit calls made so far, repeats on one message
	// included, so a host can tell whether a step edited anything.
	Edits int
}

Changes is the host-facing edit record of a Prompt or Completion.

type Choice

type Choice struct {
	Index int
	// Message uses the same Part model as the request: text, tool_call,
	// reasoning, refusal.
	Message Message
	// FinishReason is the OpenAI-style reason: "stop", "length",
	// "tool_calls", "content_filter".
	FinishReason string
}

Choice is one completion candidate. Chat completions may return several; Responses and Anthropic return one.

type CompleteHook

type CompleteHook interface {
	OnComplete(ctx context.Context, x *Exchange)
}

CompleteHook runs after the client response is fully written. It never blocks the client; panics and slow calls are logged only.

type Completion

type Completion struct {
	ID      string
	Model   string
	Choices []Choice
	Usage   Usage
	// Raw is the response as received from the provider. Read-only.
	Raw json.RawMessage
	// contains filtered or unexported fields
}

Completion is the unified response. Read it freely; edit it through its methods so the host re-encodes only what changed.

func (*Completion) Changes

func (c *Completion) Changes() Changes

Changes reports what was edited, keyed by "choice:<index>" where index is the position in Choices. Host-facing.

func (*Completion) ReplaceText

func (c *Completion) ReplaceText(choice int, text string) error

ReplaceText drops every text part of the choice and keeps a single text part with the given text, placed where the first text part was (or first). Tool calls and other non-text parts are kept. Used for redaction and synthetic answers.

func (*Completion) Reset

func (c *Completion) Reset()

Reset clears change tracking. Host-facing; plugin authors never call it.

func (*Completion) SetFinishReason

func (c *Completion) SetFinishReason(choice int, reason string) error

SetFinishReason sets the finish reason of the given choice. "content_filter" is the OpenAI-compatible way to say the response was cut.

func (*Completion) SetTargetText added in v0.1.91

func (c *Completion) SetTargetText(t TextTarget, text string) error

SetTargetText replaces the text of a target listed by Completion.TextTargets.

func (*Completion) SetText

func (c *Completion) SetText(choice, partIdx int, text string) error

SetText replaces the text of part partIdx of the given choice. The part must be a text part.

func (*Completion) SetToolArguments added in v0.1.91

func (c *Completion) SetToolArguments(choice int, callID string, args json.RawMessage) error

SetToolArguments replaces the arguments of tool call callID in the given choice. args must be valid JSON. Plugins that anonymize or restore values use it so the client runs the tool with the intended arguments.

func (*Completion) Text

func (c *Completion) Text(choice int) string

Text returns the text parts of the given choice concatenated, or "" when the choice does not exist.

func (*Completion) TextTargets added in v0.1.91

func (c *Completion) TextTargets() []TextTarget

TextTargets lists every text part of every choice, in order.

type Config added in v0.1.91

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

Config reads an instance configuration key by key, with the coercions a value needs whether it came from the dashboard form or from config.yaml: numbers written as strings, lists written as comma-separated text, bools written as yes/no. Every reader returns its default when the key is absent, null, or invalid, and the first problem is kept for Config.Err, so a decoder reads all keys in a row and checks once:

cfg, err := pluginapi.ParseConfig(Name, p.Manifest().ConfigSchema, raw)
if err != nil {
	return err
}
s.model = cfg.String("model", "")
s.action = cfg.Choice("action", "block", "block", "respond", "warn")
s.maxTokens = cfg.Int("max_tokens", 256, 1, 1<<20)
return cfg.Err()

func ParseConfig added in v0.1.91

func ParseConfig(plugin string, schema []Field, raw json.RawMessage) (*Config, error)

ParseConfig decodes raw, which may be empty or null for an empty configuration. A key that is not in schema is an error, so a typo in config.yaml is reported instead of ignored. Error messages start with the plugin name.

func (*Config) BlockStatus added in v0.1.91

func (c *Config) BlockStatus(key string) int

BlockStatus reads an HTTP status for Decision.Status: empty means the phase default (0), anything else must be between 400 and 599.

func (*Config) Bool added in v0.1.91

func (c *Config) Bool(key string) bool

Bool reads a bool, also written as true/false, yes/no, on/off, or 1/0; absent, null, or "" is false.

func (*Config) Choice added in v0.1.91

func (c *Config) Choice(key, def string, allowed ...string) string

Choice reads a select value; absent, null, or "" keeps def, anything else must be one of allowed.

func (*Config) Err added in v0.1.91

func (c *Config) Err() error

Err returns the first problem a reader found, or nil.

func (*Config) Float added in v0.1.91

func (c *Config) Float(key string, def, lo, hi float64) float64

Float reads a number within [lo, hi]; absent, null, or "" keeps def.

func (*Config) Int added in v0.1.91

func (c *Config) Int(key string, def, lo, hi int) int

Int reads a whole number within [lo, hi]; absent, null, or "" keeps def.

func (*Config) Lines added in v0.1.91

func (c *Config) Lines(key string) []string

Lines reads a textarea as lines: one string split on newlines, or a JSON array of strings. Lines are kept as written. Absent or null is nil.

func (*Config) List added in v0.1.91

func (c *Config) List(key string) []string

List reads a list of strings: a JSON array, or one string split on commas and newlines. Items are trimmed and blanks dropped. Absent or null is nil; an empty list is an empty, non-nil slice.

func (*Config) OptionalFloat added in v0.1.91

func (c *Config) OptionalFloat(key string, lo, hi float64) *float64

OptionalFloat reads a number within [lo, hi]; absent, null, or "" is nil.

func (*Config) Raw added in v0.1.91

func (c *Config) Raw(key string) json.RawMessage

Raw returns the value of key as stored, or nil when absent or null.

func (*Config) Roles added in v0.1.91

func (c *Config) Roles(key string, def ...Role) map[Role]bool

Roles reads a checkbox list of roles (system, user, assistant, tool) into a set; "system" also selects RoleDeveloper, the Responses spelling of system. Absent or null selects def.

func (*Config) String added in v0.1.91

func (c *Config) String(key, def string) string

String reads a string; absent or null keeps def.

type ContentEditor added in v0.1.92

type ContentEditor interface {
	EditsContent() bool
}

ContentEditor is implemented by a plugin whose manifest declares Mutates but whose configuration decides whether it actually edits content: a presidio instance that only flags detections, a string_replace instance that only blocks. GoModel asks a configured instance before work it does solely so an editing plugin sees the whole request — replaying the stored history of a chained Responses request instead of letting the provider resolve previous_response_id itself. It never relaxes how a hook runs. A mutating plugin that does not implement it is taken to edit.

type DanglingToolError

type DanglingToolError struct {
	// MessageID is the removed message.
	MessageID string
	// PartnerID is the message that now lacks its call or result.
	PartnerID string
	// CallID is the tool call whose pairing broke.
	CallID string
}

DanglingToolError is returned by Prompt.Remove when the removal leaves a tool call without its result or a result without its call. The removal is applied; remove PartnerID as well to make the conversation consistent again. The host rejects a prompt that still has dangling pairs.

func (*DanglingToolError) Error

func (e *DanglingToolError) Error() string

type Decision

type Decision struct {
	// Action selects what happens next. The zero value is treated as
	// [ActionAllow].
	Action Action
	// Status is the HTTP status for [ActionBlock], 400 to 599. Zero, or a
	// value outside that range, means the phase default: 400 in request
	// phases, 502 in response phases.
	Status int
	// Code is a machine-readable reason such as "content_policy".
	Code string
	// Message is the human-readable reason sent to the client on block.
	Message string
	// Response is the synthetic completion for [ActionRespond].
	Response *Completion
	// Detail is a JSON-serializable summary stored in the audit trail. It
	// must not contain secrets.
	Detail any
	// NoStore asks GoModel not to store the response of this request in the
	// response cache (exact or semantic), so a later request with the same
	// or a similar body runs the plugins again instead of replaying it. Set
	// it when the reply carries request-specific data a plugin puts back on
	// the way out, such as de-anonymized PII. It is honoured with any
	// Action, from any phase, and from any instance of the request.
	NoStore bool
}

Decision is the result of a synchronous hook.

func Allow

func Allow() Decision

Allow returns a Decision that lets the exchange continue.

func Block

func Block(status int, code, message string) Decision

Block returns a Decision that rejects the exchange with the given HTTP status (0 for the phase default), machine-readable code, and message.

func Respond

func Respond(text string) Decision

Respond returns a Decision that answers the request with a one-choice assistant completion containing text, keeping agent loops alive instead of surfacing an error.

func Warn

func Warn(code, message string, detail any) Decision

Warn returns a Decision that lets the exchange continue while recording code, message, and detail in the audit trail and response headers.

func (Decision) Blocks

func (d Decision) Blocks() bool

Blocks reports whether the decision stops the exchange from reaching the provider or the client as-is: ActionBlock or ActionRespond.

type Enforcement added in v0.1.91

type Enforcement struct {
	// Action is [ActionBlock], [ActionRespond], or [ActionWarn]. A plugin
	// with actions of its own (replace, anonymize) handles those itself and
	// leaves the rest to Enforce.
	Action Action
	// Message is the error message for block, the audit note for warn, and
	// the assistant reply for respond when RespondText is empty.
	Message string
	// BlockStatus is the HTTP status for block; 0 is the phase default.
	BlockStatus int
	// RespondText is the assistant reply for respond, when it differs from
	// Message.
	RespondText string
}

Enforcement is what a guardrail does with a finding, shared by the built-in guardrails so their block, respond, and warn behave alike. Read it from the conventional keys with Config and render a finding with Enforcement.Enforce or Enforcement.Reject.

func (Enforcement) Enforce added in v0.1.91

func (e Enforcement) Enforce(code string, detail any) Decision

Enforce renders a finding as the configured action: a block error, a respond completion, or a warning, each with code and detail.

func (Enforcement) Reject added in v0.1.91

func (e Enforcement) Reject(code string, detail any) Decision

Reject renders a finding that must not pass: respond when that is the configured action, a block error otherwise, even when the action is warn.

type EventKind

type EventKind string

EventKind is the type of a parsed stream event. Unknown kinds must be treated like EventOther.

const (
	EventTextDelta      EventKind = "text_delta"
	EventToolCallDelta  EventKind = "tool_call_delta"
	EventReasoningDelta EventKind = "reasoning_delta"
	EventFinish         EventKind = "finish"
	EventUsage          EventKind = "usage"
	EventOther          EventKind = "other"
)

type Exchange

type Exchange struct {
	// Meta is read-only identity and routing information.
	Meta Meta
	// Prompt is the unified request. Edit it through its methods.
	Prompt *Prompt
	// Response is the unified response. Edit it through its methods.
	Response *Completion
	// Stream is the accumulated stream state for streaming requests.
	Stream *StreamState
	// Headers carries inbound request headers and outbound response headers.
	Headers *Headers
	// Values is a per-request bag for passing state between a plugin's own
	// hooks (for example from OnPrompt to OnResponse). Keys should be
	// prefixed with the plugin name to avoid collisions.
	Values Values
}

Exchange is the unified view of one request/response pair that every hook receives. Which fields are set depends on the phase: Prompt is nil for non-inference routes, Response is nil until the response phase, and Stream is nil for non-streaming requests.

type Field

type Field struct {
	// Key is the JSON key in the instance config.
	Key string
	// Label is the human-readable name shown next to the control.
	Label string
	// Input selects the control; defaults to [InputText].
	Input Input
	// Required rejects configs that omit the key.
	Required bool
	// Help is a short explanation shown under the control.
	Help string
	// Placeholder is the control's placeholder text.
	Placeholder string
	// Default is used when the key is absent. Must be JSON-serializable.
	Default any
	// Options lists the choices of select and checkboxes inputs.
	Options []Option
	// Scope selects the editor; see [FieldScope].
	Scope FieldScope
}

Field is one dashboard form field and one validated configuration key.

func BlockStatusField added in v0.1.91

func BlockStatusField() Field

BlockStatusField is the conventional "block_status" form field, read with Config.BlockStatus.

type FieldScope

type FieldScope string

FieldScope says which editor shows a Field.

const (
	// ScopeInstance (the default) shows the field in the plugin instance editor.
	ScopeInstance FieldScope = ""
	// ScopeRoute shows the field in the virtual model editor; used by
	// [RouteStrategy] plugins whose settings belong to a route.
	ScopeRoute FieldScope = "route"
)

type Headers

type Headers struct {
	// Request is a copy of the inbound headers with credential headers
	// redacted. Edits affect upstream passthrough headers and the audit
	// record.
	Request http.Header
	// Response is applied to the client response: a header set here is sent
	// with the given values. A header set to the single empty string is
	// removed from the response instead, including headers the gateway adds
	// on its own.
	Response http.Header
	// Upstream adds headers to the provider call. A host that does not
	// support it ignores the field.
	Upstream http.Header
}

Headers carries the HTTP headers of an exchange.

type HealthChecker added in v0.1.91

type HealthChecker interface {
	Health(ctx context.Context) error
}

HealthChecker is implemented by plugins whose instances depend on something outside the process: a sidecar, a remote classifier, a policy service. GoModel calls Health off the request path, once an instance is built and again on every guardrail refresh (one minute by default), with a short deadline. A non-nil error marks the instance degraded in the admin views and the dashboard, with the error text as the reason. That text is shown to operators and logged, so like Decision.Detail it must not contain secrets; it is truncated to a few hundred characters. Health never changes how traffic is handled: fail_mode decides what a failing hook does. Plugins without external dependencies need not implement it.

type Host

type Host interface {
	// Logger returns a logger pre-tagged with the plugin and instance name.
	Logger() *slog.Logger
	// Inference runs internal chat completions through the gateway. Routing,
	// usage accounting, and budgets apply; requests carry origin "plugin".
	Inference() Inference
	// History loads earlier turns that are not in the request body: Responses
	// requests referencing previous_response_id or a conversation. Returns
	// nil, nil when there is nothing stored.
	History(ctx context.Context, meta Meta) ([]Message, error)
	// Metrics registers and updates counters and histograms under the
	// plugin's own metric namespace.
	Metrics() Metrics
	// HTTPClient returns a client for calling external services (a
	// classifier, a PII detector, a policy engine). It is shared by every
	// instance, honours the gateway's proxy environment and connection
	// limits, and has a 60 s request timeout as a backstop; the instance
	// timeout_ms bounds each call more tightly through the hook's context,
	// so build requests with http.NewRequestWithContext.
	HTTPClient() *http.Client
}

Host is what GoModel offers a plugin instance. It is passed to Plugin.Init and stays valid until Plugin.Close.

type Inference

type Inference interface {
	Complete(ctx context.Context, req InferenceRequest) (*Completion, error)
}

Inference runs a chat completion through the gateway on behalf of a plugin.

type InferenceRequest

type InferenceRequest struct {
	// Model is a "provider/model" reference, an alias, or a virtual model.
	Model string
	// UserPath optionally overrides the user path the internal call is scoped
	// to (for budgets and audit). Empty means the current request's path.
	UserPath string
	// Messages is the conversation to send.
	Messages []Message
	// MaxTokens caps the completion length; zero leaves it to the model.
	MaxTokens int
	// Temperature is the sampling temperature; nil leaves it to the model.
	Temperature *float64
}

InferenceRequest describes an internal chat completion.

type Input

type Input string

Input selects the dashboard control used to edit a Field.

const (
	// InputText is a single-line text box.
	InputText Input = "text"
	// InputTextarea is a multi-line text box.
	InputTextarea Input = "textarea"
	// InputNumber is a numeric input.
	InputNumber Input = "number"
	// InputSelect is a single-choice dropdown over Field.Options.
	InputSelect Input = "select"
	// InputCheckboxes is a multi-choice list over Field.Options.
	InputCheckboxes Input = "checkboxes"
	// InputSecret is a masked text box; the value is stored encrypted.
	InputSecret Input = "secret"
	// InputModel is a model picker listing the gateway's models; the value
	// is a "provider/model" selector, an alias, or a virtual model.
	InputModel Input = "model"
	// InputBool is an on/off toggle. The value is stored as a JSON boolean;
	// configuration files may also write true/false as text.
	InputBool Input = "bool"
	// InputList is a free-text list of strings, entered one per line in the
	// dashboard. The value is stored as a JSON array of strings;
	// configuration files may write a list or one comma-separated line.
	InputList Input = "list"
)

type Kind

type Kind string

Kind names a hook a plugin implements. A Manifest lists its Kinds so GoModel can validate configuration before calling anything; at load time the list is checked against the interfaces the plugin value actually satisfies.

const (
	// KindRequest marks a [RequestHook]: runs before model resolution.
	KindRequest Kind = "request"
	// KindPrompt marks a [PromptHook]: runs after routing, before the provider call.
	KindPrompt Kind = "prompt"
	// KindResponse marks a [ResponseHook]: runs on a complete response.
	KindResponse Kind = "response"
	// KindStream marks a [StreamHook]: runs per streamed event.
	KindStream Kind = "stream"
	// KindRoute marks a [RouteStrategy]: picks a target for a virtual model.
	KindRoute Kind = "route"
	// KindComplete marks a [CompleteHook]: runs after the client response is written.
	KindComplete Kind = "complete"
)

type Manifest

type Manifest struct {
	// Name is the stable identifier used in configuration and workflows.
	Name string
	// Version is the plugin's own version, shown in logs and the dashboard.
	Version string
	// Description is a one-line summary for the dashboard.
	Description string
	// BuiltWith is filled by the build helper; leave empty otherwise.
	BuiltWith BuildInfo
	// Kinds lists the hooks the plugin implements.
	Kinds []Kind
	// Mutates declares that the plugin edits the Prompt, Completion, or
	// stream. Non-mutating plugins may run concurrently with the provider call.
	Mutates bool
	// Guardrail declares that the plugin's instances are guardrails: policies
	// applied to prompts, responses, or streams, whether they block, answer,
	// warn, or rewrite (a judge, a pattern blocker, a header or prompt
	// editor). Plugins that never touch traffic, such as routing strategies,
	// leave it false. The dashboard marks guardrail plugins and their
	// instances with a shield; nothing in the runtime depends on it.
	Guardrail bool
	// ConfigSchema drives the dashboard form and config validation. The
	// validated config is passed to [Plugin.Init] as JSON.
	ConfigSchema []Field
}

Manifest describes a plugin type: its identity, the hooks it implements, and the configuration form it needs.

type Message

type Message struct {
	// ID is stable within the exchange and survives edits. The host assigns
	// IDs; messages inserted by plugins get "new-N" IDs.
	ID   string
	Role Role
	// Parts is the content in order. Tool calls follow any text.
	Parts []Part
	// Name is the optional participant name (chat "name").
	Name string
	// ToolCallID links a [RoleTool] message to the call it answers.
	ToolCallID string
	// CacheBreakpoint reports an explicit prompt-cache marker on this
	// message (Anthropic cache_control).
	CacheBreakpoint bool
}

Message is one turn of the conversation.

func TextMessage

func TextMessage(role Role, text string) Message

TextMessage builds a message with a single text part.

func (Message) Text

func (m Message) Text() string

Text returns the message's text parts concatenated in order. Tool result text is included for RoleTool messages.

type Meta

type Meta struct {
	// RequestID is the gateway request identifier.
	RequestID string
	// Dialect is the client API dialect: "openai" or "anthropic_messages".
	Dialect string
	// Endpoint is the request URL path, for example "/v1/chat/completions".
	Endpoint string
	// Operation is the gateway operation name, for example "chat_completions".
	Operation string

	// UserPath is the effective user path the request is scoped to.
	UserPath string
	// AuthKeyID identifies the API key used, never the key itself.
	AuthKeyID string
	// Labels are the request labels attached by tagging rules.
	Labels map[string]string
	// SessionID is the detected conversation session, if any.
	SessionID string

	// RequestedModel is the model the client asked for.
	RequestedModel string
	// Provider is the resolved provider type, for example "anthropic".
	Provider string
	// ProviderName is the resolved provider instance name.
	ProviderName string
	// Model is the resolved provider model.
	Model string
	// VirtualModelSource is the virtual model that produced the resolution,
	// empty when the client addressed a provider model directly.
	VirtualModelSource string

	// WorkflowVersionID identifies the workflow version in effect.
	WorkflowVersionID string
	// Features lists workflow feature flags in effect.
	Features map[string]bool
	// Stream reports whether the client asked for a streaming response.
	Stream bool

	// Attempts lists provider attempts made so far (response phases only).
	Attempts []Attempt
	// Cache describes prompt-cache planning and session affinity.
	Cache CacheInfo
	// Origin says who issued the request: "client", "plugin", or another
	// gateway-internal source.
	Origin string
}

Meta is a read-only snapshot of request identity and routing facts. Fields that are not known yet in a phase are empty (for example the resolved Provider and Model during RequestHook).

type Metrics

type Metrics interface {
	// Inc increments a counter by one.
	Inc(name string, labels map[string]string)
	// Observe records a histogram sample.
	Observe(name string, value float64, labels map[string]string)
}

Metrics records plugin metrics. Names are prefixed by the host with the plugin name; labels become metric labels.

type Option

type Option struct {
	// Value is stored in the config.
	Value string
	// Label is shown to the operator.
	Label string
}

Option is one choice of a select or checkboxes Field.

func RoleOptions added in v0.1.91

func RoleOptions() []Option

RoleOptions are the checkbox options for a roles field read by Config.Roles.

type Params

type Params struct {
	// Model is the model the request is addressed to after routing.
	Model string
	// MaxTokens is the completion length cap (chat max_tokens, Responses
	// max_output_tokens); nil when unset.
	MaxTokens   *int
	Temperature *float64
	TopP        *float64
	// Stream reports a streaming request.
	Stream bool
	// ToolChoice is the tool_choice value as sent (string or object).
	ToolChoice any
	// Extra is a read-only view of the other body-level fields, keyed by
	// their JSON name.
	Extra map[string]any
}

Params are the request parameters GoModel models. Edit them through Prompt.SetParam; direct assignment is not applied.

type Part

type Part struct {
	Kind PartKind
	// Text is the content of text, reasoning, and refusal parts.
	Text string
	// MediaType is the MIME type of media parts, when known.
	MediaType string
	// Data is inline media as received; nil when URL-referenced.
	Data []byte
	// URL references remote or data-URI media.
	URL string
	// ToolCall is set for [PartToolCall].
	ToolCall *ToolCall
	// ToolResult is set for [PartToolResult].
	ToolResult *ToolResult
	// Raw is the original JSON encoding of parts GoModel keeps verbatim
	// (opaque parts, and media parts of the Responses dialect). Read-only.
	Raw json.RawMessage
}

Part is one piece of message content.

func (Part) DecodeMedia added in v0.1.91

func (p Part) DecodeMedia() (data []byte, mediaType string, ok bool)

DecodeMedia returns the inline payload of an image or audio part and its media type: an image carried as a data: URI, or audio carried as base64. ok is false for a part referenced by URL (nothing inline to decode), for other part kinds, and for a payload that does not decode. It is the reader half of Prompt.SetMedia.

type PartKind

type PartKind string

PartKind is the type of a content Part. Plugins must treat unknown kinds as opaque: new kinds may be added in minor releases.

const (
	// PartText is plain text; Text is set.
	PartText PartKind = "text"
	// PartImage is an image; URL or Data+MediaType is set. A data: URI stays
	// in URL undecoded, with MediaType set from the URI.
	PartImage PartKind = "image"
	// PartAudio is inline audio; Data holds the payload as received (base64
	// text for chat input_audio) and MediaType the format ("audio/wav").
	PartAudio PartKind = "audio"
	// PartFile is a file reference or inline file; URL, Data, or Raw is set.
	PartFile PartKind = "file"
	// PartToolCall is a tool invocation by the assistant; ToolCall is set.
	PartToolCall PartKind = "tool_call"
	// PartToolResult is the result of a tool call; ToolResult is set.
	PartToolResult PartKind = "tool_result"
	// PartReasoning is model reasoning text; Text is set.
	PartReasoning PartKind = "reasoning"
	// PartRefusal is a model refusal; Text is set.
	PartRefusal PartKind = "refusal"
	// PartOpaque is content GoModel does not model. Raw holds the original
	// encoding and the part round-trips unchanged.
	PartOpaque PartKind = "opaque"
)

type Plugin

type Plugin interface {
	// Manifest describes the plugin. It must be cheap and side-effect free.
	Manifest() Manifest
	// Init receives the instance configuration (validated against
	// Manifest.ConfigSchema) and a Host for logging, metrics, and internal
	// inference. It is called once per configured instance, at startup and
	// again when an operator updates the instance.
	Init(ctx context.Context, config json.RawMessage, host Host) error
	// Close releases resources. It is called when the instance is removed or
	// replaced.
	Close(ctx context.Context) error
}

Plugin is the base interface every plugin implements. Hooks are optional interfaces detected by type assertion on the same value.

type Prompt

type Prompt struct {
	// Messages is the conversation in order, including system messages.
	Messages []Message
	// Tools are the tool definitions sent with the request.
	Tools []Tool
	// Params are the modelled request parameters.
	Params Params
	// Raw is the request body as received. Read-only.
	Raw json.RawMessage
	// contains filtered or unexported fields
}

Prompt is the unified request: the conversation, tools, and parameters. Read it freely; edit it only through its methods so the host can re-encode exactly what changed.

func (*Prompt) Append

func (p *Prompt) Append(m Message) string

Append adds m at the end of the conversation and returns its generated ID.

func (*Prompt) Changes

func (p *Prompt) Changes() Changes

Changes reports what was edited. Host-facing; plugins do not need it.

func (*Prompt) Clone

func (p *Prompt) Clone() *Prompt

Clone returns an independent copy of the prompt and its change tracking: later edits on either side do not reach the other, and the host can apply the copy's edits on its own (see Changes). Raw and Tools are shared, as nothing edits them. Host-facing; plugins do not need it.

func (*Prompt) Insert

func (p *Prompt) Insert(at int, m Message) string

Insert adds m at position at (clamped to the conversation bounds) and returns its generated ID. Any ID on m is replaced.

func (*Prompt) LastUser

func (p *Prompt) LastUser() *Message

LastUser returns the most recent user message, or nil.

func (*Prompt) Message

func (p *Prompt) Message(id string) *Message

Message returns the message with the given ID, or nil. The pointer is valid until the next Insert, Append, or Remove.

func (*Prompt) NewSince

func (p *Prompt) NewSince(n int) []Message

NewSince returns the messages after the first n, for plugins that only scan new turns.

func (*Prompt) Remove

func (p *Prompt) Remove(msgID string) error

Remove drops the message with the given ID. Removing a message a plugin inserted just forgets it; removing an original message is recorded for the host. When the removal leaves a tool call without its result (or the reverse) the message is still removed and a DanglingToolError names the partner message to remove next.

func (*Prompt) Reset

func (p *Prompt) Reset()

Reset clears change tracking after the host has built the prompt or applied the edits. Host-facing; plugin authors never call it.

func (*Prompt) SetMedia added in v0.1.91

func (p *Prompt) SetMedia(msgID string, partIdx int, mediaType string, data []byte) error

SetMedia replaces the payload of the image or audio part partIdx of message msgID with data of the given media type ("image/png", "audio/wav"). The part is re-encoded in the wire format the message arrived in: a data: URI for an image, base64 input audio for chat audio; other members of the wire part, such as an image's detail hint, are kept. Use Prompt.SetToolResult for media inside a tool result.

func (*Prompt) SetParam

func (p *Prompt) SetParam(name string, value any)

SetParam records a request parameter change: "max_tokens", "temperature", "top_p", or any other body-level key. The typed Params fields are updated for the three modelled names; the host decides which other keys it can apply. "model" and "stream" cannot be changed after routing.

func (*Prompt) SetTargetText added in v0.1.91

func (p *Prompt) SetTargetText(t TextTarget, text string) error

SetTargetText replaces the text of a target listed by Prompt.TextTargets. It reads the current state of the message, so successive edits to different text parts of one tool result compose.

func (*Prompt) SetText

func (p *Prompt) SetText(msgID string, partIdx int, text string) error

SetText replaces the text of part partIdx of message msgID. The part must be a text part.

func (*Prompt) SetToolArguments

func (p *Prompt) SetToolArguments(msgID, callID string, args json.RawMessage) error

SetToolArguments replaces the arguments of tool call callID in message msgID. args must be valid JSON.

func (*Prompt) SetToolResult

func (p *Prompt) SetToolResult(msgID, callID string, parts []Part) error

SetToolResult replaces the content of the result for call callID in message msgID.

func (*Prompt) SystemText

func (p *Prompt) SystemText() string

SystemText returns the text of system and developer messages.

func (*Prompt) Text

func (p *Prompt) Text(roles ...Role) string

Text returns the text parts of every message with one of the given roles (all roles when none is given), joined by newlines.

func (*Prompt) TextTargets added in v0.1.91

func (p *Prompt) TextTargets(roles ...Role) []TextTarget

TextTargets lists every text part of the messages with one of the given roles (all roles when none is given), in conversation order. Text inside tool results is included as its own target; text split across parts is reported as separate targets.

func (*Prompt) ToolCalls

func (p *Prompt) ToolCalls() []ToolCallRef

ToolCalls lists every tool call in the conversation with the message that holds it and whether a matching result exists.

func (*Prompt) Validate

func (p *Prompt) Validate() error

Validate reports the first tool call/result pair a removal broke, as a DanglingToolError. Host-facing: the host calls it before applying edits.

type PromptHook

type PromptHook interface {
	OnPrompt(ctx context.Context, x *Exchange) (Decision, error)
}

PromptHook is the guardrails phase: it runs after routing and before the provider call, with the resolved provider and model in x.Meta. It may edit x.Prompt, block the request, or answer it with a Respond decision.

type RequestHook

type RequestHook interface {
	OnRequest(ctx context.Context, x *Exchange) (Decision, error)
}

RequestHook runs after authentication and session detection, before model resolution. Edits made to x.Prompt affect routing; Meta routing fields are still empty at this point.

type ResponseHook

type ResponseHook interface {
	OnResponse(ctx context.Context, x *Exchange) (Decision, error)
}

ResponseHook runs on a complete non-streaming response, or on the assembled response of a buffered stream, before it is sent to the client. It may edit x.Response.

type Role

type Role string

Role is the author of a Message.

const (
	RoleSystem    Role = "system"
	RoleDeveloper Role = "developer"
	RoleUser      Role = "user"
	RoleAssistant Role = "assistant"
	// RoleTool marks a tool result message (chat "tool" role, Responses
	// function_call_output).
	RoleTool Role = "tool"
)

type RouteCandidate

type RouteCandidate struct {
	// Provider is the provider name and Model the provider model; Qualified
	// is "provider/model".
	Provider, Model, Qualified string
	// Weight is the configured weight; zero when the route has none.
	Weight float64
	// InputPerMtok and OutputPerMtok are prices per million tokens, when
	// known.
	InputPerMtok, OutputPerMtok *float64
}

RouteCandidate is one target a virtual model may route to.

type RouteChoice

type RouteChoice struct {
	// Qualified is the chosen candidate's "provider/model".
	Qualified string
	// Reason is recorded in the audit trail.
	Reason string
}

RouteChoice is the target picked by RouteStrategy.Select.

type RouteOutcome

type RouteOutcome struct {
	Source  string
	Target  RouteTarget
	Success bool
	// StatusCode is the upstream HTTP status, when known.
	StatusCode int
	Latency    time.Duration
	// Timeout reports that the attempt hit the provider timeout.
	Timeout bool
}

RouteOutcome reports how an attempt at a routed target went.

type RouteRequest

type RouteRequest struct {
	// Source is the virtual model being routed.
	Source string
	// SessionID is the detected session; SessionTarget the sticky
	// "provider/model" for it, when one exists.
	SessionID, SessionTarget string
	// Candidates are the eligible targets, in configured order.
	Candidates []RouteCandidate
	Meta       Meta
	// Prompt is the unified request; nil when the operation has none.
	Prompt *Prompt
	// Config is the virtual model's strategy_config as JSON.
	Config json.RawMessage
}

RouteRequest is what a RouteStrategy selects from.

type RouteStrategy

type RouteStrategy interface {
	Select(ctx context.Context, req RouteRequest) (RouteChoice, error)
	OnAttemptEnd(outcome RouteOutcome)
}

RouteStrategy is a load-balancing strategy for virtual models. Select picks one of the candidates; OnAttemptEnd reports how the attempt went so the strategy can adapt.

type RouteTarget

type RouteTarget struct {
	Provider, Model string
}

RouteTarget identifies a provider model.

func (RouteTarget) Qualified

func (t RouteTarget) Qualified() string

Qualified returns "provider/model".

type StreamAction

type StreamAction string

StreamAction is what a StreamHook asks GoModel to do with an event.

const (
	// StreamPass forwards the event unchanged.
	StreamPass StreamAction = "pass"
	// StreamDrop suppresses the event.
	StreamDrop StreamAction = "drop"
	// StreamReplace forwards the event with StreamDecision.Text instead of
	// its own text: a text, reasoning, or tool-call argument delta.
	StreamReplace StreamAction = "replace"
	// StreamTerminate ends the stream with StreamDecision.Terminate.
	StreamTerminate StreamAction = "terminate"
)

type StreamDecision

type StreamDecision struct {
	Action StreamAction
	// Text is the replacement text for [StreamReplace].
	Text string
	// Terminate is the decision rendered when the stream is cut: a block
	// error or a [Respond] completion.
	Terminate *Decision
}

StreamDecision is the result of StreamHook.OnStreamEvent.

func Drop

func Drop() StreamDecision

Drop suppresses the event.

func Pass

func Pass() StreamDecision

Pass forwards the event unchanged.

func Replace

func Replace(text string) StreamDecision

Replace forwards the event with text instead of its own text.

func Terminate

func Terminate(d Decision) StreamDecision

Terminate ends the stream with d.

type StreamEvent

type StreamEvent struct {
	// Seq is the event number, starting at 1.
	Seq  int
	Kind EventKind
	// Choice is the choice index the event belongs to.
	Choice int
	// Call is the index of the tool call a tool-call delta belongs to
	// within its choice; 0 for other kinds. Each tool call's arguments are
	// a window of their own under lookbehind and coalescing.
	Call int
	// Text is the delta text for text, tool-call argument, and reasoning
	// deltas.
	Text string
	// Overlap is the number of leading characters (runes) of Text that were
	// already presented in an earlier event of this window (a choice's
	// text, or the arguments of one of its tool calls): under a lookbehind
	// StreamPolicy GoModel withholds a tail of text and shows it again in
	// front of the next delta, after this plugin's earlier decision was
	// applied to it. An edit whose match ends within the first Overlap
	// characters was applied then and must not be applied again; edits that
	// extend past Overlap are new. 0 when nothing was withheld.
	Overlap int
	// Final marks the last event of a window (the stream ended, or a delta
	// of another kind closed it): nothing is withheld after it, so an edit
	// a plugin put off because the text could still grow is due now.
	Final bool
	// Raw is the event as received. Read-only.
	Raw json.RawMessage
}

StreamEvent is one parsed event of a streaming response.

type StreamHook

type StreamHook interface {
	StreamPolicy() StreamPolicy
	OnStreamEvent(ctx context.Context, x *Exchange, ev *StreamEvent) (StreamDecision, error)
	OnStreamEnd(ctx context.Context, x *Exchange) (Decision, error)
}

StreamHook runs per parsed stream event. StreamPolicy tells GoModel how to drive the hook (observe only, transform events in flight, or buffer the whole stream). OnStreamEnd runs once after the last event.

type StreamMode

type StreamMode string

StreamMode says how GoModel drives a StreamHook.

const (
	// StreamObserve forwards events untouched; the hook only watches. Its
	// decisions are ignored except [StreamTerminate].
	StreamObserve StreamMode = "observe"
	// StreamTransform lets the hook rewrite, drop, or terminate events in
	// flight, holding back LookbehindChars of text (and of tool-call
	// arguments, per call) so a match spanning events can be rewritten.
	StreamTransform StreamMode = "transform"
	// StreamBuffer collects the whole stream (up to MaxBufferBytes) and runs
	// the plugin's [ResponseHook] on the assembled completion.
	StreamBuffer StreamMode = "buffer"
)

type StreamPolicy

type StreamPolicy struct {
	Mode StreamMode
	// LookbehindChars is how many trailing characters GoModel withholds in
	// transform mode so the hook can rewrite text that spans events.
	LookbehindChars int
	// MinChunkChars, in transform mode, makes GoModel collect the text
	// deltas of a choice (and, per call, its tool-call argument deltas)
	// until at least this many new characters (runes)
	// are pending and present them to the hook as one text event, so a
	// hook whose per-call cost is high (a classifier, a named-entity
	// detector) runs on windows of useful size instead of on every token.
	// A non-text event and the end of the stream flush what is pending
	// early. Text reaches the client only after the hook saw it, so the
	// client waits for up to MinChunkChars characters of text at a time.
	// The largest value among the in-flight instances of a stream applies
	// to all of them, capped by the host at 16384 characters. Zero presents
	// deltas as they arrive.
	MinChunkChars int
	// MaxBufferBytes caps buffering in buffer mode; zero means the host
	// default. The buffer is shared by every plugin buffering the same
	// stream, so the largest cap asked for applies, and the host default
	// when any of them asks for none.
	MaxBufferBytes int
}

StreamPolicy configures how a StreamHook is driven.

type StreamState

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

StreamState accumulates what has streamed so far, as the client receives it: text a transform hook replaced or dropped is recorded that way, so a hook reading it in StreamHook.OnStreamEnd sees the delivered text. The host appends events; hooks read it.

func (*StreamState) Append

func (s *StreamState) Append(ev *StreamEvent)

Append records an event. Host-facing: only text deltas contribute to Text; every event counts toward Events.

func (*StreamState) Events

func (s *StreamState) Events() int

Events returns the number of events appended so far.

func (*StreamState) ReplaceTail

func (s *StreamState) ReplaceTail(ev *StreamEvent, tail int, text string)

ReplaceTail records ev as delivered with text in place of its window: the last tail runes recorded for the choice (the withheld text shown again in front of ev under lookbehind, plus any of ev's own text already recorded) are removed and text is appended. An empty text records a dropped window. Host-facing: only text deltas change Text; every event counts toward Events.

func (*StreamState) Text

func (s *StreamState) Text(choice int) string

Text returns the text streamed so far for the given choice.

type TextTarget added in v0.1.91

type TextTarget struct {
	// MessageID is the prompt message holding the text. Empty for a
	// completion target.
	MessageID string
	// Choice is the completion choice holding the text. Zero for a prompt
	// target.
	Choice int
	// Role is the role of the message holding the text; [RoleAssistant] for
	// completion targets.
	Role Role
	// Part is the index in Message.Parts of the text part, or of the tool
	// result that holds it when CallID is set.
	Part int
	// CallID is the tool call whose result holds the text. Empty for a plain
	// text part.
	CallID string
	// ResultPart is the index in ToolResult.Parts of the text part when
	// CallID is set.
	ResultPart int
	// Text is the text as it was when the target was listed.
	Text string
}

TextTarget locates one piece of editable text: a text part of a prompt message, a text part inside a tool result, or a text part of a completion choice. Plugins that scan or rewrite text list targets with Prompt.TextTargets or Completion.TextTargets and write back with the matching SetTargetText, instead of walking parts by hand.

type Tool

type Tool struct {
	Name        string
	Description string
	// Parameters is the JSON schema of the tool's arguments.
	Parameters json.RawMessage
	// Raw is the tool definition as sent.
	Raw json.RawMessage
}

Tool is a tool definition sent with the request. Read-only in this version.

type ToolCall

type ToolCall struct {
	// ID is the provider-visible call id (chat tool_calls[].id, Responses
	// call_id, Anthropic tool_use.id).
	ID string
	// Name is the tool name.
	Name string
	// Arguments is the argument JSON. When the wire format carried the
	// arguments as a string that parses as JSON, the parsed value is exposed;
	// otherwise a JSON string.
	Arguments json.RawMessage
	// Server is the MCP server name for calls routed through the MCP gateway.
	Server string
}

ToolCall is a tool invocation emitted by the model.

type ToolCallRef

type ToolCallRef struct {
	// MessageID is the message holding the call.
	MessageID string
	Call      ToolCall
	// HasResult reports whether a tool result for the call is present.
	HasResult bool
}

ToolCallRef locates a tool call in the conversation.

type ToolResult

type ToolResult struct {
	// CallID matches ToolCall.ID.
	CallID string
	// Parts is the result content: text, image, or opaque parts.
	Parts []Part
	// IsError reports a failed tool execution (Anthropic is_error).
	IsError bool
}

ToolResult is the result returned for a tool call.

type Usage

type Usage struct {
	InputTokens  int
	OutputTokens int
	TotalTokens  int
	// CachedInputTokens is the part of InputTokens served from a prompt cache.
	CachedInputTokens int
}

Usage is token accounting in provider-neutral names.

type Values

type Values map[string]any

Values is a per-request key/value bag. The host allocates it before the first hook runs.

func (Values) Get

func (v Values) Get(key string) (any, bool)

Get returns the value stored under key and whether it was present. It is safe to call on a nil map.

func (Values) Set

func (v Values) Set(key string, value any)

Set stores value under key. The map must be non-nil; the host always allocates Exchange.Values.

Directories

Path Synopsis
Package plugintest helps test pluginapi plugins without GoModel: a fake pluginapi.Host with scripted inference and recorded metrics, builders for prompts, completions, and exchanges, and a stream driver that feeds events to a pluginapi.StreamHook the way the host does, lookbehind, overlap, and chunk coalescing included.
Package plugintest helps test pluginapi plugins without GoModel: a fake pluginapi.Host with scripted inference and recorded metrics, builders for prompts, completions, and exchanges, and a stream driver that feeds events to a pluginapi.StreamHook the way the host does, lookbehind, overlap, and chunk coalescing included.

Jump to

Keyboard shortcuts

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