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

systemone

package
v1.14.0 Latest Latest
Warning

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

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

Documentation

Overview

Package systemone implements the System One protocol that decision models speak: a state and a set of typed questions go out, and one typed answer per question comes back. It is shared by the plugins that serve such models; the question types callers declare decisions with are in the public package go/plugins/systemone/exp.

Index

Constants

View Source
const (
	// KindKeyword marks a property as a question and names its type.
	KindKeyword = "x-systemone"
	// LevelsKeyword carries a score question's ordered level descriptions.
	LevelsKeyword = "x-levels"
	// TrueKeyword and FalseKeyword carry a noul question's optional
	// criteria, which a NoulOf emits from its type parameter.
	TrueKeyword  = "x-true"
	FalseKeyword = "x-false"
	// GuidanceKeyword carries a question's structured guidance from the
	// Guided companions, keyed as on the wire: by option, by level index,
	// or by "true" and "false".
	GuidanceKeyword = "x-guidance"
	// InstructionsKeyword carries a runtime question's instructions when
	// they are structured rather than text, which a description cannot
	// hold.
	InstructionsKeyword = "x-instructions"
)

The schema keywords a question is encoded with. The output schema is the one channel the generate loop hands a model besides the messages, so the question set rides on it: each property is one question, its description is the instructions, and these keywords carry what plain JSON Schema has no keyword for. CompileQuestions reads them back.

View Source
const (
	KindChoice = "choice"
	KindScore  = "score"
	KindNoul   = "noul"
)

The question types, as the API names them.

View Source
const EnumQuestionID = "choice"

EnumQuestionID names the one question an enum-format request asks.

View Source
const RequestTimeout = 30 * time.Second

RequestTimeout bounds one attempt when the plugin builds the HTTP client.

Variables

This section is empty.

Functions

func AnswersText

func AnswersText(resp *Response, questions map[string]Question, enum bool) (string, error)

AnswersText renders the answers as the message text: for the enum format the chosen option itself, otherwise a JSON object keyed by question ID whose values are the answers projected onto the fields the output type declares. A score's legend is rebuilt from the rubric's strings: for a level sent with guidance the API echoes the guidance, which the legend's strings cannot hold. A score is clamped to the rubric, since float rounding in the expected level can land a hair past the top level, which the schema's bounds would reject along with every other answer in the call.

func CompileQuestions

func CompileQuestions(schema map[string]any, preamble string) (map[string]Question, error)

CompileQuestions reads the questions an output schema encodes: one per property, each marked with the x-systemone keyword the answer types emit. A property without the marker is rejected, since the model has no way to answer a shape it does not know. The preamble, the request's system text, goes in front of every question's own instructions, followed by the schema's own description when it has one, as it is for the enum format.

func EnumQuestion

func EnumQuestion(schema map[string]any, preamble string) (map[string]Question, error)

EnumQuestion turns an enum output schema into a single choice question, so the built-in enum format works on the model with no decision type. The instructions are the preamble, the request's system text, followed by the schema's description when it has one; the options keep the order the values were given in, and an option's description is its own name, since an enum carries none.

func ErrorMessage

func ErrorMessage(body []byte) string

ErrorMessage reads the message out of an error body. The endpoints use four shapes: {error: {message}} or {error: "..."}, a {message}, a Cloudflare {errors: [{code, message}]} envelope, and a validation list, which TypeSafe's API sends under detail as {loc, msg} records and OpenRouter sends as the bare array of {path, message} records. A body in none of these shapes is quoted as it came, and an empty one gives "".

func NewModel

func NewModel(c *Client, name, id string, spec Spec) *ai.ModelAction

NewModel builds the model action for one model ID under its registered name.

The model claims constrained output, so the loop hands it the output schema untouched, and claims the system role and context so the loop does not rewrite either into instruction text that would land in the state.

func SystemPreamble

func SystemPreamble(messages []*ai.Message) (string, error)

SystemPreamble gathers the system messages into the text that goes in front of every question's instructions. The model has no system role: the state is what it evaluates and the questions are what it is told, so a system message belongs with the questions, never in the state. Only text can be instructions; loop plumbing left in a history is skipped.

Types

type Client

type Client struct {
	// HTTP sends the requests.
	HTTP *http.Client
	// BaseURL is the origin the endpoint's paths are joined to.
	BaseURL string
	// APIKey is sent as a bearer token. A local server, such as Ollama,
	// takes none, and no authorization header is sent without one.
	APIKey string
	// Headers are sent on every request, after the authorization header.
	Headers  http.Header
	Endpoint *Endpoint
}

Client posts questions to one endpoint.

func (*Client) Decide

func (c *Client) Decide(ctx context.Context, model string, req *Request) (*Response, error)

Decide posts one request and decodes the answers.

func (*Client) ListModels

func (c *Client) ListModels(ctx context.Context) ([]ModelInfo, error)

ListModels fetches the endpoint's model listing. It is sent once, with no retries: the listing serves the Dev UI, and the caller falls back to the known models when it fails. The listing's envelope is not pinned down by the API reference, so a bare array and the usual wrapping keys are all accepted.

type Config

type Config struct {
	// StateJSON parses each text part of the state as JSON, so a prompt
	// template that renders a JSON document produces an object state
	// rather than a string one. A text that is not JSON is an error. A
	// fallback to another model takes that model's config, so set it there
	// too, or send the state as a data part, which needs no parsing.
	StateJSON bool `` /* 151-byte string literal not displayed */

	// Extra is merged into the top-level fields of the request body. It
	// reaches fields this package does not model, such as a gateway's
	// session_id or trace. It cannot name a field the request builds
	// itself: model, state, questions, images, audio, or videos.
	Extra map[string]any `` /* 177-byte string literal not displayed */
}

Config is the per-request configuration of a decision model. go/plugins/systemone/exp exports it as Config.

type Endpoint

type Endpoint struct {
	// Name names the endpoint in errors.
	Name string
	// Path is the request path under the base URL.
	Path string
	// ModelsPath is the path of the model listing under the base URL,
	// empty when the endpoint has none.
	ModelsPath string
	// Route builds one model's request when it is not the native one. It
	// gets the model ID and the native body, which it may change, and
	// returns a suffix to the path, which is joined with a slash when it
	// does not start with one, and the body to send. It is per model
	// because one host can serve models on different routes, as Workers AI
	// does. When nil, the native body goes to Path with the model ID as
	// given.
	Route func(model string, body map[string]any) (suffix string, out any, err error)
	// Unwrap extracts the native response from the endpoint's envelope.
	// When nil, the response is the native body. An error it returns is
	// reported under Name, with its status kept, or as UNKNOWN when it has
	// none: the server answered with a success code, so neither the request
	// nor the server is known to be at fault.
	Unwrap func(body []byte) ([]byte, error)
}

Endpoint is how a server takes System One requests: the paths, and the hooks for a server whose wire is not the native one.

type Info

type Info struct {
	Model    string                    `json:"model"`
	Provider string                    `json:"provider,omitempty"`
	ID       string                    `json:"id,omitempty"`
	Answers  map[string]map[string]any `json:"answers,omitempty"`
}

Info is the raw response of a model built by NewModel: the model version that answered, a gateway's provider and generation ID, and the answers as the API sent them. go/plugins/systemone/exp exports it as Info.

type Media

type Media struct {
	Images, Audio, Videos []string
}

Media is the media a request carries, as base64 data URLs by kind, each in the order its parts appear, messages first and documents after. Media is an extension some servers make to the protocol, in the images, audio, and videos fields.

func BuildState

func BuildState(req *ai.ModelRequest, stateJSON bool, reads Reads) (any, Media, error)

BuildState turns the request into the state and the media: one message as its value, several as {role, content} records, and with documents attached an object of both, so a question can name the context it is about. System messages are instructions, not state, and are left out.

Media parts of a kind the model reads become the request's media and contribute nothing to the state; a media part of any other kind is refused. A message of media alone is still a turn, with the empty string as its content, so the turns around it keep their roles and a request of that one message sends the empty string as its state.

type ModelInfo

type ModelInfo struct {
	ID          string `json:"id,omitempty"`
	Name        string `json:"name"`
	Description string `json:"description,omitempty"`
	ReleaseDate string `json:"release_date,omitempty"`
}

ModelInfo is one entry of the models listing. A listing names a model by its name, or, as OpenRouter's does, by its ID with a display name beside it; ModelInfo.Model is whichever names the model.

func (ModelInfo) Model

func (m ModelInfo) Model() string

Model is the ID a request names the model by.

type Option

type Option struct {
	Name        string
	Description any
}

Option is one option of a choice question and its description: a string, or the guidance that replaces it on the wire.

type Options

type Options []Option

Options is a choice question's options with their descriptions, in the order they were declared. It encodes as a JSON object whose keys keep that order, where a Go map would sort them.

func (Options) MarshalJSON

func (c Options) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler.

type Question

type Question struct {
	Type string `json:"type"`
	// Instructions is the question's text, or the structured value a
	// runtime question gives, after the preamble.
	Instructions any `json:"instructions"`
	// Criteria is the options and their descriptions for a choice question,
	// in order; a map of side to description for a noul question; and an
	// ordered list of level descriptions for a score question. A
	// description is a string, or the guidance that replaces it on the wire.
	Criteria any `json:"criteria,omitempty"`

	// Labels are a score question's level strings, which the legend is
	// built from whatever form the levels take on the wire.
	Labels []string `json:"-"`
}

Question is one question on the wire.

type Reads

type Reads struct {
	Images, Audio, Video bool
}

Reads says which kinds of media a model reads besides text and JSON.

type Request

type Request struct {
	State any
	// Media is sent in the images, audio, and videos fields, which the
	// servers place before the state. Each field is sent only when it has
	// media, since a server that does not read a kind can refuse its field.
	Media     Media
	Questions map[string]Question
	// Extra is merged into the top-level fields, which is the escape hatch
	// to a field this package does not model. It cannot replace a field
	// the request builds itself: model, state, questions, images, audio, or
	// videos.
	Extra map[string]any
}

Request is the native request body, before the endpoint's envelope.

func (*Request) Body

func (r *Request) Body(model string) map[string]any

Body builds the native JSON body.

type Response

type Response struct {
	Info
	Usage struct {
		InputTokens  int      `json:"input_tokens"`
		OutputTokens int      `json:"output_tokens"`
		Cost         *float64 `json:"cost,omitempty"`
	} `json:"usage"`
}

Response is the native response body. Provider, ID, and the cost are gateway additions, absent from a model vendor's own responses.

type Spec

type Spec struct {
	// Label names the model in the Dev UI.
	Label string
	// Reads is the media requests may carry. A kind the model does not
	// read is refused before a request is sent.
	Reads Reads
}

Spec describes one model to NewModel.

Directories

Path Synopsis
Package systemonetest provides a fake System One endpoint for the tests of the plugins that serve decision models.
Package systemonetest provides a fake System One endpoint for the tests of the plugins that serve decision models.

Jump to

Keyboard shortcuts

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