Documentation
¶
Overview ¶
Package decide is the chassis-owned `ai://decide` exec dispatch surface.
Where ai://chat turns context into generated language and ai://embed turns content into a vector, ai://decide turns state plus bounded, typed questions into probabilistic judgments:
noul a proposition → P(true) choice one of the given names → the name, plus a distribution over names score an ordered rubric → an interpolated score, plus a distribution
The answer space is always supplied by the caller, so a backend can never return an option that was not offered.
**Judgments, not policy.** decide returns probabilities and nothing else. Thresholds, fallbacks, allow/deny and escalation belong to the stack, in ordinary WHEN clauses over the result. There is deliberately no threshold parameter anywhere in this package.
Like chat and embed it is a thin registry: backends self-register via init() in their subpackages, and the chassis activates one with a blank import. The contract types below name no provider; a backend translates them to its own wire shape (e.g. `noul` may travel as `boolean`).
**Boundary of trust.** Same as chat and embed: RequiredSecrets() declares the standardized secret names a backend needs; the ExecAI handler materializes them through the per-tenant store (with the optional env fallback); cleartext rides only in the *secrets.SecretBag passed to Decide.
Index ¶
- Constants
- func Normalize(req Request, resp *Response) error
- func ParseState(raw gjson.Result) (json.RawMessage, error)
- func Register(name string, c Constructor)
- func Registered() []string
- type Answer
- type Backend
- type CodedError
- type Config
- type Constructor
- type InvalidAnswerError
- type InvalidWithError
- type MissingSecretError
- type NoBackendError
- type Option
- type ProviderHTTPError
- type ProviderNetError
- type ProviderParseError
- type Question
- type Request
- type Response
- type TimeoutError
Constants ¶
const ( TypeNoul = "noul" TypeChoice = "choice" TypeScore = "score" )
Question types. These are the provider-independent names; a backend maps them onto its wire vocabulary.
Variables ¶
This section is empty.
Functions ¶
func Normalize ¶
Normalize enforces the answer contract on a backend's response, in place:
- every question has exactly one answer, of the type it was asked as;
- a choice is one of the offered names, and its Probability is the distribution's value for that name;
- a score lies on the level-index scale, 0 … len(Levels)-1;
- every probability, and a reported confidence, is a finite number in [0, 1];
- distributions keep only offered names / level indices;
- answers come back in question order, and nothing else survives.
Any violation fails the whole call: a stack never sees a partial answer set, because a missing path reads as zero in a WHEN comparison.
func ParseState ¶
func ParseState(raw gjson.Result) (json.RawMessage, error)
ParseState checks that raw (a JSON value) is usable as decision state: a non-empty string, an object, or an array. It returns the value unchanged.
func Register ¶
func Register(name string, c Constructor)
Register adds a backend constructor. Called from a backend package's init(); the chassis activates a backend with a blank import.
Re-registering an existing name overwrites the constructor (test support); registration order is preserved on first-registration only.
func Registered ¶
func Registered() []string
Registered returns the names of currently registered backends, sorted.
Types ¶
type Answer ¶
type Answer struct {
Key string
Type string
Probability *float64
Choice string
Score *float64
Probabilities map[string]float64
Confidence *float64
}
Answer is one question's typed judgment. Which fields are meaningful depends on Type; after Normalize they are:
noul Probability = P(true)
choice Choice, Probability = P(Choice), Probabilities by option name
score Score, Probabilities by level index ("0" = lowest)
Score is interpolated on the level-index scale: 0 is the lowest level and len(Levels)-1 the highest. A score answer carries no Probability — the distribution and the score describe different things.
Confidence is the provider's own confidence in the answer, carried only when the provider reports one (Jev reports it for choice and score, not noul). It is not a probability and is never derived from one.
type Backend ¶
type Backend interface {
// Name returns the registered name (must match the Register key).
Name() string
// DefaultModel is the model used when a request omits WITH model.
DefaultModel() string
// RequiredSecrets are the standardized secret names this backend needs.
RequiredSecrets() []string
// Decide asks every question in req against req.State in one call. It
// translates the provider's answers into Response and returns them as
// reported; Normalize (called by the handler) enforces the contract —
// every question answered, choices from the offered set, probabilities
// in range. The bag carries cleartext for every name in
// RequiredSecrets(); implementations keep it in local variables only.
Decide(ctx context.Context, req Request, bag *secrets.SecretBag) (Response, error)
}
Backend is the chassis-facing interface every decision backend implements.
Lifecycle: backends register by name via Register() in an init(); the chassis resolves one per ai://decide EXEC. Decide must be safe for concurrent use — the chassis dispatches ops in parallel on fan-out.
type CodedError ¶
CodedError is implemented by decide errors that carry a stable txco_decide_* code. The ExecAI handler surfaces the code at `<into>.error.code` so a stack can dispatch on it.
type Config ¶
type Config struct {
// HTTPClient is the chassis-owned http.Client (egress-guarded). Every
// outbound call from a backend MUST use it.
HTTPClient *http.Client
// ZeroDataRetention asks the provider not to retain request data, where
// the backend can express that (chassis config
// decide-zero-data-retention).
ZeroDataRetention bool
}
Config carries decide-package construction options resolved from chassis config. Backends extend it with their own fields without breaking callers (same convention as chat.Config and embed.Config).
type Constructor ¶
Constructor builds a Backend from resolved config.
type InvalidAnswerError ¶
InvalidAnswerError flags a well-formed provider response whose answers break the contract: a question left unanswered, an answer of the wrong type, a choice that was not offered, a probability out of range. The whole call fails — the op never returns a partial set of answers.
func (*InvalidAnswerError) Code ¶
func (e *InvalidAnswerError) Code() string
func (*InvalidAnswerError) Error ¶
func (e *InvalidAnswerError) Error() string
type InvalidWithError ¶
type InvalidWithError struct {
Reason string
}
InvalidWithError flags a request the chassis refuses before any provider call: a malformed WITH clause, or a question outside a backend's known limits. Nothing is billed.
func (*InvalidWithError) Code ¶
func (e *InvalidWithError) Code() string
func (*InvalidWithError) Error ¶
func (e *InvalidWithError) Error() string
type MissingSecretError ¶
MissingSecretError is returned when a backend's RequiredSecrets() name is absent from the per-tenant store (and env fallback, when enabled).
func (*MissingSecretError) Code ¶
func (e *MissingSecretError) Code() string
func (*MissingSecretError) Error ¶
func (e *MissingSecretError) Error() string
type NoBackendError ¶
NoBackendError is returned by Resolve when a provider hint names an unregistered backend, or when no backend is registered at all.
func (*NoBackendError) Code ¶
func (e *NoBackendError) Code() string
func (*NoBackendError) Error ¶
func (e *NoBackendError) Error() string
type Option ¶
Option is one choice: the name the answer returns, and a description of what it covers.
type ProviderHTTPError ¶
ProviderHTTPError carries a non-2xx provider response (message already extracted, sanitized and bounded by the backend).
func (*ProviderHTTPError) Code ¶
func (e *ProviderHTTPError) Code() string
func (*ProviderHTTPError) Error ¶
func (e *ProviderHTTPError) Error() string
type ProviderNetError ¶
type ProviderNetError struct {
Reason string
}
ProviderNetError carries a network/DNS failure reaching the provider (after the backend's retry budget is spent).
func (*ProviderNetError) Code ¶
func (e *ProviderNetError) Code() string
func (*ProviderNetError) Error ¶
func (e *ProviderNetError) Error() string
type ProviderParseError ¶
ProviderParseError flags an empty or malformed provider response body.
func (*ProviderParseError) Code ¶
func (e *ProviderParseError) Code() string
func (*ProviderParseError) Error ¶
func (e *ProviderParseError) Error() string
type Question ¶
type Question struct {
// Key is the caller's id for the question; the answer comes back under
// the same key.
Key string
// Type is TypeNoul, TypeChoice, or TypeScore.
Type string
// Instructions is the question itself.
Instructions string
// Choices are the offered options for a choice question, in author
// order.
Choices []Option
// Levels are a score question's rubric, lowest to highest.
Levels []string
// True and False optionally describe what each answer to a noul
// question means. Empty = not supplied.
True string
False string
}
Question is one bounded question. Which criteria field is set depends on Type.
func ParseQuestions ¶
ParseQuestions decodes and validates the WITH `questions` object:
{ "<key>": { "type": "noul|choice|score", "instructions": "…", "criteria": … } }
criteria by type:
noul optional; an object whose keys are only "true" and/or "false" choice required; an object of option name → description (≥ 1 option) score required; an array of level labels, lowest first (≥ 2 levels)
Author order is preserved. Unknown question fields are refused so a typo (`options` for `criteria`) fails loudly instead of being dropped. Provider-specific limits (e.g. a maximum number of choices) are the backend's to enforce before it sends anything.
type Request ¶
type Request struct {
// State is the evidence every question is asked against: a JSON string,
// object, or array, passed to the provider as data.
State json.RawMessage
// Questions in author order. Keys are unique.
Questions []Question
// Model is the resolved model identifier.
Model string
// Intent is a trace-only label (e.g. "pony_ingest_place").
Intent string
}
Request is the chassis-normalized decision request.
type Response ¶
type Response struct {
// Answers, one per question. After Normalize they are in question order.
Answers []Answer
// Provider is the backend name; Model is the model requested.
Provider string
Model string
// ResolvedModel is the model the provider reports having run, verbatim
// ("" when it reports none). It may be an alias rather than a version;
// the chassis records it and invents nothing.
ResolvedModel string
// Provider-reported token usage (0 if unreported). Recorded in trace and
// the envelope; never charged to fuel.
InputTokens int64
OutputTokens int64
// Cost is the provider-reported cost in USD; nil when unreported.
Cost *float64
// GenerationID is the provider's id for this call, for reconciliation.
GenerationID string
// LatencyMS is wall-clock from request build to response parse.
LatencyMS int64
// Retries is the count of provider retries performed.
Retries int
}
Response is the chassis-normalized decision result. Backends translate the provider's on-wire shape into this struct.
type TimeoutError ¶
type TimeoutError struct {
Reason string
}
TimeoutError reports that the op's deadline (WITH timeout, or ai-default-timeout) passed before the provider answered.
func (*TimeoutError) Code ¶
func (e *TimeoutError) Code() string
func (*TimeoutError) Error ¶
func (e *TimeoutError) Error() string
Directories
¶
| Path | Synopsis |
|---|---|
|
Package vercel is the ai://decide backend for Vercel AI Gateway's evaluation API: POST https://ai-gateway.vercel.sh/v1/evaluate, documented at https://vercel.com/docs/ai-gateway/modalities/evaluation.
|
Package vercel is the ai://decide backend for Vercel AI Gateway's evaluation API: POST https://ai-gateway.vercel.sh/v1/evaluate, documented at https://vercel.com/docs/ai-gateway/modalities/evaluation. |