Documentation
ΒΆ
Overview ΒΆ
Package discern is a small decision layer over two hosted System One judgment APIs: TypeSafe Jev (package jev) and Cloudflare Workers AI Clef (package clef).
One typed Request asks one or more independent questions about a state. Each question is a Choice (pick one of named options), a Noul (the probability that the answer is yes) or a Score (a probability-weighted position on ordered levels). The caller picks the provider for every request: Jev, Clef, or an explicit run on both.
jc, err := jev.New(apiKey, jev.WithModel("jev-1.13.0"))
cc, err := clef.New(accountID, apiToken, clef.WithModel(clef.ModelFlash))
ev, err := discern.New(discern.WithAdapter(jc), discern.WithAdapter(cc))
res, err := ev.Evaluate(ctx, discern.Request{
Providers: discern.ClefOnly,
State: discern.Text(ticketText),
Questions: []discern.Question{
discern.Choice("queue", discern.Text("Which support queue should handle this ticket?"),
discern.Opt("billing", discern.Text("Invoices, refunds, failed payments")),
discern.Opt("technical", discern.Text("Errors, outages, integration problems"))),
},
})
Observations ΒΆ
A Result always carries every provider's full observation: the chosen option and the probability of every option in request order, Noul and Score values, confidence when reported, usage, model and request identifiers. Optional provider fields are pointers, so an absent value is never read as zero.
Confidence is a concentration statistic computed from the probabilities. It is not the probability that an answer is correct, and values from Jev and Clef are not comparable. A Noul value is a probability, not a boolean. Neither provider returns a rationale; the SDK never invents one.
Recommendations ΒΆ
A Policy is an explicit set of threshold checks per question, applied locally to the observations. Without a policy no recommendation is made: every question gets the NoRule status and the observations are returned unchanged. Policies come from WithDefaultPolicy (per evaluator) or WithPolicy and WithoutPolicy (per call; the last option wins and the evaluator is never modified). Recommend re-applies another policy to a stored result without any provider call.
Each question gets one Recommendation with a status (Accepted, BelowThreshold, ReviewRequired, NotApplicable, Incomplete or NoRule), machine-readable reason codes, every evaluated check, and a deterministic summary. For a dual run the providers must agree (same Choice option, or Noul and Score values within the policy's Agreement tolerance); disagreement needs review unless the policy names a PreferOnConflict provider. When one provider of a dual run fails, questions are Incomplete unless the policy sets AllowPartial; with AllowPartial, Evaluate can return an error matching ErrPartialFailure together with Accepted recommendations for the surviving provider. Result.Overall is Accepted only if every question the policy covers is Accepted.
Calls, billing and retries ΒΆ
Every selected provider gets exactly one attempt. The SDK never retries, falls back to another provider, hedges or races requests: each call may be billed, and a dual run makes two potentially billable calls, run concurrently. One attempt includes the standard library's own transparent replays, which the adapters disable: net/http's HTTP/2 client would otherwise resend a request whose stream was refused or reset, even after the server read it. Request, policy and adapter validation all happen before any call, so a local error means nothing was sent. Retry hints from providers are reported in ProviderError and Failure; whether repeating a call is safe is the caller's decision. Injected HTTP transports that retry or follow redirects on their own are outside these guarantees.
The SDK never reads environment variables itself. When no HTTP client is injected, the adapters use http.DefaultTransport, which honors the standard proxy variables (HTTPS_PROXY, HTTP_PROXY and NO_PROXY) as net/http always does; inject a client to control that.
Errors ΒΆ
Evaluate returns a non-nil Result exactly when at least one call was attempted. Persist it before inspecting the error. ErrPartialFailure means one of two providers failed and the other's observation is usable; under a policy with AllowPartial its recommendations may be Accepted. Local failures match ErrInvalidRequest, ErrInvalidPolicy or ErrUnsupported, and the error of a call that was made never matches them. Provider failures are *ProviderError values that match ErrRejected or ErrMalformedResponse, or wrap context.Canceled and context.DeadlineExceeded. Error strings never contain credentials, URLs, request state or provider bodies. Tools that walk error chains, such as testify's failure messages, also print the causes; the jev and clef adapters keep only sanitized causes, which never include credentials, URLs, payloads or provider messages.
Records ΒΆ
A Result is a versioned JSON record (SchemaVersion) that never contains the state, instructions, descriptions, credentials or provider bodies. It does contain caller identifiers (question, option and level IDs, the correlation ID and evidence references) and provider outputs, which may still be sensitive; Redact removes the optional identifiers. Provider-reported identifiers (the model, request ID and ray ID) are kept only when they are visible ASCII of at most 256 bytes, so provider-echoed text cannot reach a record or a summary. ParseResult restores a record and checks its internal references and that its recommendations and overall verdict are consistent with its policy snapshot. A record supports traceability and local policy replay; it is not proof that an answer is true, not tamper-evident, and not a promise that a repeated call would return the same values.
Testing ΒΆ
Package discerntest provides fake adapters with call counting, so application code can be tested without network access.
Index ΒΆ
- Constants
- Variables
- type Adapter
- type Agreement
- type Answer
- type BatchItem
- type Call
- type Check
- type CheckResult
- type ChoiceAnswer
- type ChoiceOption
- type EvalOption
- type Evaluator
- type Failure
- type FailureKind
- type Kind
- type LevelProbability
- type Library
- type Metric
- type NoulAnswer
- type Observation
- func (o *Observation) Answer(questionID string) (Answer, bool)
- func (o *Observation) Choice(questionID string) (ChoiceAnswer, bool)
- func (o *Observation) Clone() Observation
- func (o *Observation) Noul(questionID string) (NoulAnswer, bool)
- func (o *Observation) Score(questionID string) (ScoreAnswer, bool)
- type Op
- type Option
- type OptionProbability
- type Outcome
- type OutcomeStatus
- type Overall
- type Policy
- type PolicySnapshot
- type PolicySource
- type Provider
- type ProviderError
- type ProviderSet
- type Question
- type QuestionInfo
- type QuestionPolicy
- type ReasonCode
- type Recommendation
- type RecommendationStatus
- type Redaction
- type Request
- type Result
- func (r *Result) Clone() *Result
- func (r *Result) Observation(p Provider) (Observation, bool)
- func (r *Result) Outcome(p Provider) (Outcome, bool)
- func (r *Result) Recommendation(questionID string) (Recommendation, bool)
- func (r *Result) Redact(opts Redaction) *Result
- func (r *Result) SelectedAnswers(questionID string) []Answer
- type ResultStatus
- type ScoreAnswer
- type ScoreLevel
- type Selected
- type Usage
- type ValidationError
- type Value
Examples ΒΆ
Constants ΒΆ
const ( // MaxQuestionIDLength is the longest question ID (Clef's documented limit). MaxQuestionIDLength = 100 // MinChoiceOptions and MaxChoiceOptions bound a Choice question's options. MinChoiceOptions = 2 MaxChoiceOptions = 255 // MinScoreLevels and MaxScoreLevels bound a Score question's levels. MinScoreLevels = 2 MaxScoreLevels = 10 // MaxIdentifierLength bounds option IDs, level IDs, the correlation ID, each // evidence reference, check IDs, check models and the policy version, in // bytes. These identifiers must be valid UTF-8 without control or invisible // format characters (Unicode categories Cc and Cf, such as U+200B and U+202E). // They are compared byte for byte, without Unicode normalization, so two IDs // that look the same but are encoded differently are different IDs. MaxIdentifierLength = 256 // MaxEvidenceRefs bounds Request.EvidenceRefs. MaxEvidenceRefs = 32 )
Documented library limits. Some are narrower than a provider contract so that a request valid for one provider is valid for both, and records stay bounded.
const DefaultBatchConcurrency = 4
DefaultBatchConcurrency is the number of requests EvaluateBatch runs at once unless WithBatchConcurrency says otherwise.
const SchemaVersion = "discern.result/v1"
SchemaVersion identifies the JSON layout of a Result. Changes within v1 are additive only.
Variables ΒΆ
var ( // ErrInvalidConfig reports an invalid constructor argument or option. ErrInvalidConfig = errors.New("discern: invalid configuration") // ErrInvalidRequest reports a request that fails local validation. ErrInvalidRequest = errors.New("discern: invalid request") // ErrInvalidPolicy reports a recommendation policy that fails validation. ErrInvalidPolicy = errors.New("discern: invalid policy") // ErrUnsupported reports a request that a selected provider cannot accept, // such as more questions than the provider allows. ErrUnsupported = errors.New("discern: unsupported by provider") // ErrRejected reports a provider that refused the call: a non-2xx HTTP // status (including a redirect), or a Cloudflare envelope with success:false // or any errors[] entry. ErrRejected = errors.New("discern: rejected by provider") // ErrMalformedResponse reports a provider response that could not be decoded // or failed validation, including one larger than the configured limit and // one whose model, request ID or ray ID is not a provider identifier (see // Observation). ErrMalformedResponse = errors.New("discern: malformed provider response") // ErrPartialFailure reports a dual run in which exactly one provider failed. // The Result still holds the surviving provider's observation. ErrPartialFailure = errors.New("discern: partial failure") // ErrInvalidRecord reports a stored result record that ParseResult rejects. ErrInvalidRecord = errors.New("discern: invalid result record") )
Sentinel errors. Match them with errors.Is.
ErrInvalidConfig, ErrInvalidRequest, ErrInvalidPolicy and ErrUnsupported are local: an error matching one of them means no provider call was made. The error of a call that was made never matches them, nor ErrInvalidRecord, and matches ErrPartialFailure only as Evaluate reports it: an adapter call error that matches one of these sentinels is recorded with a static cause instead.
Functions ΒΆ
This section is empty.
Types ΒΆ
type Adapter ΒΆ
Adapter is the provider boundary. The jev and clef packages implement it, and discerntest provides fakes.
Prepare validates the request against the provider's documented limits and encodes the exact bytes to send, without any I/O. It returns an error matching ErrInvalidRequest or ErrUnsupported when the request cannot be sent. Such errors are returned to callers unchanged, so they must not contain caller data (a *ValidationError, which names fields rather than values, fits). Any other Prepare error is reported as ErrUnsupported with a fixed message that names only the provider; the adapter's error stays reachable through errors.Is and errors.As. The request is the adapter's own copy, so the returned Call may keep it.
New calls Provider and Model to check the adapter; one for which either panics, such as a typed nil pointer, is rejected with ErrInvalidConfig. Adapters must be safe for concurrent use.
type Agreement ΒΆ
type Agreement struct {
MaxDifference float64 `json:"max_difference"`
}
Agreement is the largest allowed absolute difference between the two providers' Noul or Score values. The comparison allows 1e-9 of floating-point noise, so zero is valid but accepts values up to 1e-9 apart rather than only strictly equal ones.
type Answer ΒΆ
type Answer struct {
QuestionID string `json:"question_id"`
Kind Kind `json:"kind"`
Choice *ChoiceAnswer `json:"choice,omitempty"`
Noul *NoulAnswer `json:"noul,omitempty"`
Score *ScoreAnswer `json:"score,omitempty"`
}
Answer is one typed answer. Exactly one of Choice, Noul and Score is set, matching Kind.
type BatchItem ΒΆ
BatchItem is one EvaluateBatch result, with the same contract as Evaluate: Result is nil exactly when no call was attempted for the item.
type Call ΒΆ
type Call func(ctx context.Context) (*Observation, error)
Call performs one prepared provider call. It must honor ctx, make at most one attempt, and return either an Observation or an error (preferably a *ProviderError). A call error that matches a local sentinel, such as ErrInvalidRequest, is recorded with a static cause, because those sentinels mean that no call was made.
type Check ΒΆ
type Check struct {
// ID is the rule identifier recorded in results. When empty, it defaults to
// "<question>/checks/<index>".
ID string `json:"id"`
Provider Provider `json:"provider"`
Model string `json:"model"`
Metric Metric `json:"metric"`
Op Op `json:"op"`
Threshold float64 `json:"threshold"`
}
Check is one threshold rule. An empty Provider applies the check to every provider; an empty Model applies it to every model. A check scoped to a model matches the model the provider reported, falling back to the requested model, so an alias that moves to a new version stops matching.
type CheckResult ΒΆ
type CheckResult struct {
RuleID string `json:"rule_id"`
Provider Provider `json:"provider"`
ScopeProvider Provider `json:"scope_provider"`
ScopeModel string `json:"scope_model"`
ObservedModel string `json:"observed_model"`
Metric Metric `json:"metric"`
Op Op `json:"op"`
Threshold float64 `json:"threshold"`
Observed *float64 `json:"observed"`
Passed bool `json:"passed"`
Skipped ReasonCode `json:"skipped,omitempty"`
}
CheckResult is one evaluated check for one provider, or the agreement test (RuleID "<question>/agreement", empty Provider). Observed is nil when the metric was absent or the check was skipped.
A Choice agreement test compares options, not numbers: its Op is Equal, its Threshold is 0 and its Observed is nil, and Passed says whether both providers chose the same option.
type ChoiceAnswer ΒΆ
type ChoiceAnswer struct {
Option string `json:"option"`
Confidence *float64 `json:"confidence,omitempty"`
Probabilities []OptionProbability `json:"probabilities"`
}
ChoiceAnswer is a Choice result: the chosen option, the probability of every option in request order, and the provider's confidence when reported.
Confidence is a concentration statistic computed from the probabilities, not the probability that the answer is correct.
type ChoiceOption ΒΆ
ChoiceOption is one Choice alternative. ID is sent to the provider as the option name; Description may be the zero Value (sent as null).
type EvalOption ΒΆ
type EvalOption func(*evalConfig)
EvalOption configures one Evaluate or EvaluateBatch call. It never changes the Evaluator. The last policy option wins.
func WithPolicy ΒΆ
func WithPolicy(p Policy) EvalOption
WithPolicy replaces the effective policy for this call only. The policy is copied now; the evaluator's default is untouched.
func WithoutPolicy ΒΆ
func WithoutPolicy() EvalOption
WithoutPolicy disables the default policy for this call: the result holds observations and NoRule recommendations only.
type Evaluator ΒΆ
type Evaluator struct {
// contains filtered or unexported fields
}
Evaluator runs requests on registered adapters and applies recommendation policies. It is immutable after New and safe for concurrent use.
func New ΒΆ
New returns an Evaluator. It returns an error matching ErrInvalidConfig when no adapter is registered, an adapter is nil (including a typed nil pointer, such as a nil *jev.Client, whose Provider or Model panics) or duplicates a provider, or the batch concurrency is below 1, and one matching ErrInvalidPolicy when the default policy is invalid.
func (*Evaluator) Evaluate ΒΆ
Evaluate validates the request and the effective policy, prepares every selected adapter, and then makes one call per selected provider; a dual request runs both calls concurrently and waits for both. One provider's failure never cancels the other.
The returned Result is non-nil exactly when at least one call was attempted; persist it before inspecting err. err is nil when every provider succeeded (Result.Status Complete); it matches ErrPartialFailure when one of two failed (Partial); otherwise it joins the provider errors (Failed). When no call was made, err matches ErrInvalidRequest, ErrUnsupported or ErrInvalidPolicy, or is the context's error.
Example ΒΆ
package main
import (
"context"
"fmt"
"github.com/mrz1836/go-discern"
"github.com/mrz1836/go-discern/discerntest"
)
// exampleRequest is a synthetic support ticket with one routing question.
func exampleRequest(providers discern.ProviderSet) discern.Request {
return discern.Request{
Providers: providers,
State: discern.Text("Subject: Charged twice\n\nMy invoice was paid twice this month."),
Questions: []discern.Question{
discern.Choice("queue", discern.Text("Which support queue should handle this ticket?"),
discern.Opt("billing", discern.Text("Invoices, refunds, failed payments")),
discern.Opt("technical", discern.Text("Errors, outages, integration problems")),
discern.Opt("account", discern.Text("Login, profile, permissions"))),
},
CorrelationID: "ticket-1042",
}
}
// exampleObservation is what a fake provider answers for exampleRequest.
func exampleObservation(model, option string, billing, technical, account float64) discern.Observation {
return discern.Observation{Model: model, Answers: []discern.Answer{{
QuestionID: "queue", Kind: discern.KindChoice,
Choice: &discern.ChoiceAnswer{Option: option, Probabilities: []discern.OptionProbability{
{Option: "billing", Probability: billing},
{Option: "technical", Probability: technical},
{Option: "account", Probability: account},
}},
}}}
}
func main() {
fake := discerntest.Static(discern.Clef, exampleObservation("clef-flash", "billing", 0.87, 0.1, 0.03))
ev, err := discern.New(discern.WithAdapter(fake))
if err != nil {
fmt.Println(err)
return
}
res, err := ev.Evaluate(context.Background(), exampleRequest(discern.ClefOnly))
if err != nil {
fmt.Println(err)
return
}
obs, _ := res.Observation(discern.Clef)
q, _ := obs.Choice("queue")
fmt.Println(res.Status, q.Option)
for _, p := range q.Probabilities {
fmt.Println(p.Option, p.Probability)
}
fmt.Println(fake.Calls(), "call")
}
Output: complete billing billing 0.87 technical 0.1 account 0.03 1 call
Example (DualWithPolicy) ΒΆ
package main
import (
"context"
"errors"
"fmt"
"github.com/mrz1836/go-discern"
"github.com/mrz1836/go-discern/discerntest"
)
// exampleRequest is a synthetic support ticket with one routing question.
func exampleRequest(providers discern.ProviderSet) discern.Request {
return discern.Request{
Providers: providers,
State: discern.Text("Subject: Charged twice\n\nMy invoice was paid twice this month."),
Questions: []discern.Question{
discern.Choice("queue", discern.Text("Which support queue should handle this ticket?"),
discern.Opt("billing", discern.Text("Invoices, refunds, failed payments")),
discern.Opt("technical", discern.Text("Errors, outages, integration problems")),
discern.Opt("account", discern.Text("Login, profile, permissions"))),
},
CorrelationID: "ticket-1042",
}
}
// exampleObservation is what a fake provider answers for exampleRequest.
func exampleObservation(model, option string, billing, technical, account float64) discern.Observation {
return discern.Observation{Model: model, Answers: []discern.Answer{{
QuestionID: "queue", Kind: discern.KindChoice,
Choice: &discern.ChoiceAnswer{Option: option, Probabilities: []discern.OptionProbability{
{Option: "billing", Probability: billing},
{Option: "technical", Probability: technical},
{Option: "account", Probability: account},
}},
}}}
}
func main() {
jev := discerntest.Static(discern.Jev, exampleObservation("jev-1.13.0", "billing", 0.91, 0.06, 0.03))
clef := discerntest.Static(discern.Clef, exampleObservation("clef", "billing", 0.87, 0.1, 0.03))
routing := discern.Policy{
Version: "routing-v3",
Questions: []discern.QuestionPolicy{{
Question: "queue",
Checks: []discern.Check{
{ID: "queue-jev", Provider: discern.Jev, Metric: discern.MetricChoiceProbability, Op: discern.AtLeast, Threshold: 0.80},
{ID: "queue-clef", Provider: discern.Clef, Metric: discern.MetricChoiceProbability, Op: discern.AtLeast, Threshold: 0.75},
},
}},
}
ev, err := discern.New(discern.WithAdapter(jev), discern.WithAdapter(clef), discern.WithDefaultPolicy(routing))
if err != nil {
fmt.Println(err)
return
}
res, err := ev.Evaluate(context.Background(), exampleRequest(discern.JevAndClef))
switch {
case err == nil:
case errors.Is(err, discern.ErrPartialFailure):
fmt.Println("one provider failed")
default:
fmt.Println(err)
return
}
rec, _ := res.Recommendation("queue")
fmt.Println(rec.Status, rec.Selected.Option, rec.Selected.Providers)
fmt.Println(rec.Reasons)
fmt.Println(rec.Summary)
fmt.Println(res.Overall.Status)
}
Output: accepted billing [jev clef] [providers_agree] accepted billing: jev choice.probability 0.91 β₯ 0.8; clef choice.probability 0.87 β₯ 0.75; both chose billing accepted
func (*Evaluator) EvaluateBatch ΒΆ
func (e *Evaluator) EvaluateBatch(ctx context.Context, reqs []Request, opts ...EvalOption) ([]BatchItem, error)
EvaluateBatch evaluates independent requests with at most WithBatchConcurrency requests in flight. Every item is validated and prepared first: if any item is invalid, it returns an error naming the index and makes no call at all. Otherwise each item follows Evaluate's contract and the items are returned in input order. Items are billed separately and never deduplicated. When ctx is done, items not yet started get Result nil and the context's error.
If an adapter panics, no further item is started; the panic is re-raised once the items in flight have finished.
Prepared requests (including each adapter's encoded body) are held until their item runs, so peak memory is roughly the size of the batch's input.
type Failure ΒΆ
type Failure struct {
Kind FailureKind `json:"kind"`
StatusCode int `json:"status_code,omitempty"`
RequestID string `json:"request_id,omitempty"`
RayID string `json:"ray_id,omitempty"`
// RetryAfter is the provider's retry hint, when supplied. It is a hint, not
// proof that repeating the call is safe.
RetryAfter *time.Duration `json:"retry_after_ns,omitempty"`
// Codes are provider error codes, such as Cloudflare errors[].code.
Codes []int `json:"codes,omitempty"`
}
Failure is the storage-safe form of a provider error. RequestID and RayID are provider identifiers, like an Observation's: a value that is not empty or 1 to 256 bytes of visible ASCII is dropped before it is recorded.
type FailureKind ΒΆ
type FailureKind string
FailureKind classifies a provider failure. The list is closed; parsers of stored records should treat an unknown kind as a failure of unknown cause.
const ( // FailureTimeout means the call's own timeout expired, such as the adapter's // timeout or its HTTP client's, rather than the caller's deadline. FailureTimeout FailureKind = "timeout" // FailureDeadlineExceeded means the caller's context deadline expired. FailureDeadlineExceeded FailureKind = "deadline_exceeded" // FailureCanceled means the caller's context was canceled. FailureCanceled FailureKind = "canceled" // FailureTransport means the request failed in transit (DNS, TLS, connection // reset, or an incomplete body). The provider may or may not have run it. FailureTransport FailureKind = "transport" // FailureRejected means the provider refused the call: a non-2xx HTTP status // (including a redirect), or a Cloudflare envelope with success:false or any // errors[] entry. It matches ErrRejected. FailureRejected FailureKind = "rejected" // FailureMalformed means the response failed decoding or validation. It matches // ErrMalformedResponse. FailureMalformed FailureKind = "malformed_response" // FailureTooLarge means the response exceeded the configured size limit. It // matches ErrMalformedResponse. FailureTooLarge FailureKind = "response_too_large" // FailureAdapter means an adapter returned an error that is neither a // *ProviderError nor a ProviderError value, such as a test fake's error, or a // ProviderError without a kind. FailureAdapter FailureKind = "adapter_error" )
Failure kinds.
type LevelProbability ΒΆ
type LevelProbability struct {
Level string `json:"level"`
Probability float64 `json:"probability"`
}
LevelProbability is the probability of one Score level.
type Metric ΒΆ
type Metric string
Metric is an observed quantity a Check compares with its threshold.
const ( // MetricChoiceProbability is the probability of the option the provider chose, // in [0,1]. Choice only. MetricChoiceProbability Metric = "choice.probability" // MetricConfidence is the provider's concentration statistic, in [0,1]. It is // not the probability that the answer is correct, and values from different // providers are not comparable. Choice and Score only. MetricConfidence Metric = "confidence" // MetricNoul is the Noul probability of yes, in [0,1]. Noul only. MetricNoul Metric = "noul" // MetricScore is the fractional Score value, in [0, levels-1]. Score only. MetricScore Metric = "score" // MetricAgreement appears only in CheckResult, for the dual-provider agreement // test. It is not valid in a Check. MetricAgreement Metric = "agreement" )
Metrics.
type NoulAnswer ΒΆ
type NoulAnswer struct {
Value float64 `json:"value"`
}
NoulAnswer is a Noul result. Value is the probability that the answer is yes, in [0,1]; it is not a boolean and carries no separate confidence.
type Observation ΒΆ
type Observation struct {
// Model is the model the provider reported, or empty if it reported none.
Model string `json:"model,omitempty"`
// RequestID is the provider's request identifier, when supplied.
RequestID string `json:"request_id,omitempty"`
// RayID is Cloudflare's cf-ray identifier, when supplied.
RayID string `json:"ray_id,omitempty"`
Usage *Usage `json:"usage,omitempty"`
Answers []Answer `json:"answers"`
}
Observation is a validated provider response. Answers are in request order. Optional provider fields are pointers or empty strings when absent.
Model, RequestID and RayID are provider identifiers: each is empty or 1 to 256 bytes of visible ASCII (0x21 to 0x7E). An observation that breaks the rule fails validation as FailureMalformed, so provider-echoed text never reaches a record or a recommendation summary.
func (*Observation) Answer ΒΆ
func (o *Observation) Answer(questionID string) (Answer, bool)
Answer returns a copy of the answer to the question.
func (*Observation) Choice ΒΆ
func (o *Observation) Choice(questionID string) (ChoiceAnswer, bool)
Choice returns a copy of the Choice answer to the question.
func (*Observation) Clone ΒΆ
func (o *Observation) Clone() Observation
Clone returns a deep copy of the observation.
func (*Observation) Noul ΒΆ
func (o *Observation) Noul(questionID string) (NoulAnswer, bool)
Noul returns the Noul answer to the question.
func (*Observation) Score ΒΆ
func (o *Observation) Score(questionID string) (ScoreAnswer, bool)
Score returns a copy of the Score answer to the question.
type Op ΒΆ
type Op string
Op is a threshold comparison: observed <op> threshold.
const ( // AtLeast passes when observed >= threshold. AtLeast Op = "gte" // Above passes when observed > threshold. Above Op = "gt" // AtMost passes when observed <= threshold. AtMost Op = "lte" // Below passes when observed < threshold. Below Op = "lt" // Equal appears only in CheckResult, for a Choice agreement test (both // providers chose the same option). It is not valid in a Check. Equal Op = "eq" )
Comparison operators.
type Option ΒΆ
type Option func(*config)
Option configures an Evaluator in New. Nil options are ignored, and the last option of each kind wins, except that registering a second adapter for the same provider is an error.
func WithAdapter ΒΆ
WithAdapter registers a provider adapter, such as one from jev.New or clef.New.
func WithBatchConcurrency ΒΆ
WithBatchConcurrency sets how many requests EvaluateBatch runs at once. A dual request makes two concurrent calls, so up to 2n calls can be in flight. n must be at least 1.
func WithDefaultPolicy ΒΆ
WithDefaultPolicy sets the policy applied to every evaluation that does not override it. The policy is copied now and validated in New.
type OptionProbability ΒΆ
type OptionProbability struct {
Option string `json:"option"`
Probability float64 `json:"probability"`
}
OptionProbability is the probability of one Choice option.
type Outcome ΒΆ
type Outcome struct {
Provider Provider `json:"provider"`
RequestedModel string `json:"requested_model"`
Status OutcomeStatus `json:"status"`
Elapsed time.Duration `json:"elapsed_ns"`
Observation *Observation `json:"observation,omitempty"`
Failure *Failure `json:"failure,omitempty"`
}
Outcome is one provider call. Exactly one of Observation and Failure is set.
type OutcomeStatus ΒΆ
type OutcomeStatus string
OutcomeStatus is the status of one provider call.
const ( // OutcomeSucceeded means the call returned a valid observation. OutcomeSucceeded OutcomeStatus = "succeeded" // OutcomeFailed means the call failed; Outcome.Failure says how. OutcomeFailed OutcomeStatus = "failed" )
Outcome statuses.
type Overall ΒΆ
type Overall struct {
// Status is NoRule when no question is covered; otherwise the most severe
// covered status, so it is Accepted only if every covered question is.
Status RecommendationStatus `json:"status"`
// Uncovered lists the questions with status NoRule, in request order.
Uncovered []string `json:"uncovered"`
}
Overall is the per-request verdict over the questions the policy covers.
type Policy ΒΆ
type Policy struct {
// Version is a caller label recorded with every result. The SDK does not
// interpret it.
Version string `json:"version"`
// Questions holds at most one entry per question ID.
Questions []QuestionPolicy `json:"questions"`
// PreferOnConflict, when set, accepts the named provider's answer if the two
// providers disagree and that provider passes its checks. The default ("")
// makes disagreement ReviewRequired.
PreferOnConflict Provider `json:"prefer_on_conflict"`
// AllowPartial, when true, evaluates the surviving provider of a dual run alone
// when the other failed. The default (false) makes such questions Incomplete.
//
// With AllowPartial, Evaluate can return an error matching ErrPartialFailure
// together with Accepted recommendations that select the surviving provider:
// read the recommendations, not only the error, before acting.
AllowPartial bool `json:"allow_partial"`
}
Policy is an explicit, typed recommendation policy. It is applied locally to observations and never causes a provider call. The zero Policy is invalid: Version is required.
Policy and its parts carry JSON tags, so a policy can be loaded from configuration. Decode it with a json.Decoder that has DisallowUnknownFields set: otherwise a misspelled key, such as "treshold", is silently ignored and leaves a zero threshold that every value meets.
A dual run evaluates each provider's answer against the checks that apply to it, so a policy for dual runs needs at least one applicable check for each provider: with checks scoped only to the preferred provider, the other provider has none, and the question is NotApplicable on dual runs.
type PolicySnapshot ΒΆ
type PolicySnapshot struct {
Source PolicySource `json:"source"`
Policy *Policy `json:"policy"`
Unmatched []string `json:"unmatched"`
}
PolicySnapshot records the effective policy of a result, with defaults made explicit. Unmatched lists policy questions that matched no request question.
type PolicySource ΒΆ
type PolicySource string
PolicySource says where a result's effective policy came from.
const ( // PolicyNone means no policy was configured. PolicyNone PolicySource = "none" // PolicyClientDefault means the evaluator's WithDefaultPolicy applied. PolicyClientDefault PolicySource = "client_default" // PolicyCallOverride means a per-call WithPolicy applied. PolicyCallOverride PolicySource = "call_override" // PolicyDisabled means a per-call WithoutPolicy disabled the default. PolicyDisabled PolicySource = "disabled" // PolicyRecommend means Recommend applied the policy to a stored result. PolicyRecommend PolicySource = "recommend" )
Policy sources.
type ProviderError ΒΆ
type ProviderError struct {
Provider Provider
Kind FailureKind
StatusCode int
RequestID string
RayID string
RetryAfter time.Duration
HasRetryAfter bool
Codes []int
// contains filtered or unexported fields
}
ProviderError describes a failed provider call. Its Error string is built only from the provider, kind and HTTP status: it never contains credentials, URLs, headers, request state or response bodies. Tools that walk error chains, such as testify's failure messages, also print the cause returned by Unwrap. The jev and clef adapters store only sanitized causes, which never include credentials, URLs, payloads or provider messages; a custom adapter's cause is its own.
Adapters may return a ProviderError as a pointer or as a value. Request and ray IDs that are not provider identifiers (see Observation) are dropped when the error is recorded.
RetryAfter is a provider hint, not proof that repeating the call is safe or free. HasRetryAfter distinguishes an explicit zero from an absent hint.
func NewProviderError ΒΆ
func NewProviderError(provider Provider, kind FailureKind, cause error, messages ...string) *ProviderError
NewProviderError returns a ProviderError for adapters. The cause is reachable through errors.Is and errors.As but never appears in Error; tools that walk error chains print it, so it must not carry secrets, URLs or payloads (store a *url.Error's inner error, not the *url.Error). The messages are provider-supplied texts, such as Cloudflare errors[].message, and are available only through ProviderMessages.
func (ProviderError) Error ΒΆ
func (e ProviderError) Error() string
Error returns "discern: <provider>: <kind>", with the HTTP status when known.
func (ProviderError) GoString ΒΆ
func (e ProviderError) GoString() string
GoString prints the metadata fields only; the cause and provider messages are omitted so that %#v cannot leak them.
func (ProviderError) Is ΒΆ
func (e ProviderError) Is(target error) bool
Is maps the failure kind onto the package sentinels.
func (ProviderError) ProviderMessages ΒΆ
func (e ProviderError) ProviderMessages() []string
ProviderMessages returns provider-supplied error texts, such as Cloudflare errors[].message. They may echo request content, so they are excluded from Error, GoString and result records; logging them is the caller's decision.
func (ProviderError) Unwrap ΒΆ
func (e ProviderError) Unwrap() error
Unwrap returns the sanitized cause, such as context.Canceled.
type ProviderSet ΒΆ
type ProviderSet string
ProviderSet selects the providers a request runs on. The zero value is invalid, so every request must choose explicitly.
const ( // JevOnly runs the request on Jev. JevOnly ProviderSet = "jev" // ClefOnly runs the request on Clef. ClefOnly ProviderSet = "clef" // JevAndClef runs the request on both providers concurrently: two potentially // billable calls. JevAndClef ProviderSet = "jev+clef" )
Provider sets.
func (ProviderSet) Has ΒΆ
func (s ProviderSet) Has(p Provider) bool
Has reports whether the set selects p.
func (ProviderSet) Providers ΒΆ
func (s ProviderSet) Providers() []Provider
Providers returns the selected providers in fixed order (Jev, then Clef), or nil for an invalid set.
type Question ΒΆ
type Question struct {
// ID identifies the question; it must match [A-Za-z0-9_.-]{1,100}.
ID string
// Kind is the question type.
Kind Kind
// Instructions say what to decide: non-empty text, or a JSON object or array.
Instructions Value
// Options are the Choice alternatives, in the order sent to the provider.
// Order matters: models can lean toward earlier options.
Options []ChoiceOption
// Levels are the Score levels, lowest first.
Levels []ScoreLevel
// Yes and No optionally describe what a yes and a no mean for a Noul question.
Yes, No Value
}
Question is one typed question. Build it with Choice, Noul or Score.
func Choice ΒΆ
func Choice(id string, instructions Value, options ...ChoiceOption) Question
Choice returns a Choice question with the options in the given order.
type QuestionInfo ΒΆ
type QuestionInfo struct {
ID string `json:"id"`
Kind Kind `json:"kind"`
Options []string `json:"options,omitempty"`
Levels []string `json:"levels,omitempty"`
}
QuestionInfo records a question's identity and its option or level IDs, in request order. It never holds instructions or descriptions.
type QuestionPolicy ΒΆ
type QuestionPolicy struct {
Question string `json:"question"`
Checks []Check `json:"checks"`
// Agreement is the tolerance for dual Noul and Score questions; without it a
// dual numeric question is NotApplicable. It is invalid on Choice questions,
// which agree when both providers choose the same option.
Agreement *Agreement `json:"agreement"`
}
QuestionPolicy holds the checks for one question. All applicable checks must pass (AND); each check is evaluated separately for each provider it applies to.
type ReasonCode ΒΆ
type ReasonCode string
ReasonCode is a machine-readable reason for a recommendation. The list is closed and documented; parsers of stored records must treat an unknown code as non-accepting.
const ( // ReasonNoPolicy means no policy was in effect (NoRule). ReasonNoPolicy ReasonCode = "no_policy" // ReasonQuestionNotInPolicy means the policy has no entry for the question (NoRule). ReasonQuestionNotInPolicy ReasonCode = "question_not_in_policy" // ReasonProviderFailed means a provider the policy needs failed (Incomplete). ReasonProviderFailed ReasonCode = "provider_failed" // ReasonMetricAbsent means a checked metric was not reported (Incomplete). ReasonMetricAbsent ReasonCode = "metric_absent" // ReasonNoApplicableCheck means no check applies to a provider (NotApplicable). ReasonNoApplicableCheck ReasonCode = "no_applicable_check" // ReasonModelMismatch means a provider's checks are all scoped to other models // (NotApplicable). ReasonModelMismatch ReasonCode = "model_mismatch" // ReasonAgreementMissing means a dual Noul or Score question has no Agreement // tolerance (NotApplicable). ReasonAgreementMissing ReasonCode = "agreement_missing" // ReasonProvidersDisagree means the two providers disagreed. ReasonProvidersDisagree ReasonCode = "providers_disagree" // ReasonBelowThreshold means at least one applicable check failed. It comes // with BelowThreshold, and with ReviewRequired when the providers of a dual // run disagreed and a check also failed. ReasonBelowThreshold ReasonCode = "below_threshold" // ReasonThresholdsMet means a single provider passed every check (Accepted). ReasonThresholdsMet ReasonCode = "thresholds_met" // ReasonProvidersAgree means both providers agreed and passed (Accepted). ReasonProvidersAgree ReasonCode = "providers_agree" // ReasonPreferredOnConflict means the preferred provider passed and was selected // despite a disagreement (Accepted). ReasonPreferredOnConflict ReasonCode = "preferred_on_conflict" // ReasonPartialSingleProvider means AllowPartial evaluated the surviving // provider of a dual run alone. ReasonPartialSingleProvider ReasonCode = "partial_single_provider" // ReasonModelCheckSkipped means a check scoped to another model was skipped // for a provider while other checks still applied to it, which can signal // that the provider's model changed. It is informational and never changes // the status. ReasonModelCheckSkipped ReasonCode = "model_check_skipped" )
Reason codes.
type Recommendation ΒΆ
type Recommendation struct {
QuestionID string `json:"question_id"`
Status RecommendationStatus `json:"status"`
Selected *Selected `json:"selected"`
Reasons []ReasonCode `json:"reasons"`
Checks []CheckResult `json:"checks"`
Summary string `json:"summary"`
}
Recommendation is the policy outcome for one question. Selected is nil unless Status is Accepted. Summary is deterministic text built from observed numbers and checks; it is for people and is not a stable contract (the reason codes are).
type RecommendationStatus ΒΆ
type RecommendationStatus string
RecommendationStatus is the outcome of applying a policy to one question.
const ( // Accepted means every applicable check passed (and, for dual runs, the // providers agreed or the preferred provider won the conflict). Accepted RecommendationStatus = "accepted" // BelowThreshold means at least one applicable check failed. BelowThreshold RecommendationStatus = "below_threshold" // ReviewRequired means the two providers disagreed and no conflict preference // resolved it. ReviewRequired RecommendationStatus = "review_required" // NotApplicable means the policy could not be applied, for example because no // check matched a provider or model. NotApplicable RecommendationStatus = "not_applicable" // Incomplete means a required provider failed or a required metric was absent. Incomplete RecommendationStatus = "incomplete" // NoRule means no policy covered the question. NoRule RecommendationStatus = "no_rule" )
Recommendation statuses. Accepted through Incomplete are listed from least to most severe, the order in which Overall picks the most severe covered status. NoRule, for a question no policy covers, is outside that order.
type Request ΒΆ
type Request struct {
// Providers selects the providers. It is required.
Providers ProviderSet
// State is the content to evaluate: a string, object or array. It must not
// be blank: text of only white space, and an empty object or array, are
// rejected before any call.
State Value
// Questions are evaluated independently against State.
Questions []Question
// CorrelationID is an optional caller label stored verbatim in the result.
CorrelationID string
// EvidenceRefs are optional opaque references stored verbatim in the result.
// They are never fetched or validated beyond their size.
EvidenceRefs []string
}
Request is one evaluation: a state and the questions asked about it.
Evaluate copies the request on entry; callers must still not mutate it from another goroutine while a call is in progress.
type Result ΒΆ
type Result struct {
SchemaVersion string `json:"schema_version"`
Library Library `json:"library"`
CorrelationID string `json:"correlation_id"`
EvidenceRefs []string `json:"evidence_refs"`
// Redacted lists the fields that Redact removed.
Redacted []string `json:"redacted"`
Providers ProviderSet `json:"providers"`
Status ResultStatus `json:"status"`
// StartedAt is the client wall-clock time, in UTC, when the calls started.
StartedAt time.Time `json:"started_at"`
// Elapsed is the client-observed time for all calls, not provider compute time.
Elapsed time.Duration `json:"elapsed_ns"`
Questions []QuestionInfo `json:"questions"`
Outcomes []Outcome `json:"outcomes"`
Policy PolicySnapshot `json:"policy"`
Recommendations []Recommendation `json:"recommendations"`
Overall Overall `json:"overall"`
}
Result is the typed, storable record of one evaluation: every provider outcome, the effective policy, and the recommendations derived from them. Marshal it with encoding/json and restore it with ParseResult.
A Result holds caller identifiers (question, option and level IDs, the correlation ID and evidence references) and provider outputs, but never the state, instructions, descriptions, credentials or provider bodies. It is not proof that an answer is true, not tamper-evident, and not a guarantee that a repeated evaluation would return the same values.
func ParseResult ΒΆ
ParseResult restores a Result encoded with encoding/json. It returns an error matching ErrInvalidRecord for another schema version or a record whose parts are not consistent with each other or with the rules a live evaluation follows:
- outcomes that do not match the providers or the status, or answers that do not match the questions (every observation must pass the checks a live one does, with answers and probabilities in request order, as Evaluate stores them);
- a correlation ID or evidence references that a request could not carry, a reported model, request ID or ray ID that is not a provider identifier (empty, or 1 to 256 bytes of visible ASCII), or a negative elapsed time;
- recommendations that do not match the policy snapshot: without a policy (source none or disabled) every question is NoRule with exactly the no_policy reason; with one, a question is NoRule with exactly the question_not_in_policy reason when the policy has no entry for it, and is not NoRule and carries neither reason when the policy has one;
- an Overall that is not the aggregation of the recommendations (the most severe covered status, and the NoRule questions in request order), or a selection that points at a provider or option the record does not support.
The checks are consistency checks, not a replay: recommendations are not recomputed, so records written by other v1 versions of this library still parse. Statuses and reason codes it does not know are kept, except where a rule above needs a specific value. Unknown fields are ignored.
Example ΒΆ
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/mrz1836/go-discern"
"github.com/mrz1836/go-discern/discerntest"
)
// exampleRequest is a synthetic support ticket with one routing question.
func exampleRequest(providers discern.ProviderSet) discern.Request {
return discern.Request{
Providers: providers,
State: discern.Text("Subject: Charged twice\n\nMy invoice was paid twice this month."),
Questions: []discern.Question{
discern.Choice("queue", discern.Text("Which support queue should handle this ticket?"),
discern.Opt("billing", discern.Text("Invoices, refunds, failed payments")),
discern.Opt("technical", discern.Text("Errors, outages, integration problems")),
discern.Opt("account", discern.Text("Login, profile, permissions"))),
},
CorrelationID: "ticket-1042",
}
}
// exampleObservation is what a fake provider answers for exampleRequest.
func exampleObservation(model, option string, billing, technical, account float64) discern.Observation {
return discern.Observation{Model: model, Answers: []discern.Answer{{
QuestionID: "queue", Kind: discern.KindChoice,
Choice: &discern.ChoiceAnswer{Option: option, Probabilities: []discern.OptionProbability{
{Option: "billing", Probability: billing},
{Option: "technical", Probability: technical},
{Option: "account", Probability: account},
}},
}}}
}
func main() {
fake := discerntest.Static(discern.Jev, exampleObservation("jev-1.13.0", "billing", 0.91, 0.06, 0.03))
ev, err := discern.New(discern.WithAdapter(fake))
if err != nil {
fmt.Println(err)
return
}
res, err := ev.Evaluate(context.Background(), exampleRequest(discern.JevOnly))
if err != nil {
fmt.Println(err)
return
}
data, err := json.Marshal(res.Redact(discern.Redaction{CorrelationID: true}))
if err != nil {
fmt.Println(err)
return
}
restored, err := discern.ParseResult(data)
if err != nil {
fmt.Println(err)
return
}
obs, _ := restored.Observation(discern.Jev)
q, _ := obs.Choice("queue")
fmt.Println(restored.SchemaVersion, restored.Redacted, q.Option)
}
Output: discern.result/v1 [correlation_id] billing
func Recommend ΒΆ
Recommend applies a policy to an existing result without any provider call. It returns a new Result whose policy source is PolicyRecommend; res is not modified. It returns ErrInvalidPolicy for an invalid policy and ErrInvalidRecord when the parts of res it keeps are inconsistent. The parts it replaces (the policy snapshot, recommendations and overall verdict) are not checked, so a result built by hand may leave them zero.
Two-sided gates, such as escalating at noul >= 0.9 and closing at noul <= 0.1, are two Recommend calls with two policies.
Example ΒΆ
package main
import (
"context"
"fmt"
"github.com/mrz1836/go-discern"
"github.com/mrz1836/go-discern/discerntest"
)
// exampleRequest is a synthetic support ticket with one routing question.
func exampleRequest(providers discern.ProviderSet) discern.Request {
return discern.Request{
Providers: providers,
State: discern.Text("Subject: Charged twice\n\nMy invoice was paid twice this month."),
Questions: []discern.Question{
discern.Choice("queue", discern.Text("Which support queue should handle this ticket?"),
discern.Opt("billing", discern.Text("Invoices, refunds, failed payments")),
discern.Opt("technical", discern.Text("Errors, outages, integration problems")),
discern.Opt("account", discern.Text("Login, profile, permissions"))),
},
CorrelationID: "ticket-1042",
}
}
// exampleObservation is what a fake provider answers for exampleRequest.
func exampleObservation(model, option string, billing, technical, account float64) discern.Observation {
return discern.Observation{Model: model, Answers: []discern.Answer{{
QuestionID: "queue", Kind: discern.KindChoice,
Choice: &discern.ChoiceAnswer{Option: option, Probabilities: []discern.OptionProbability{
{Option: "billing", Probability: billing},
{Option: "technical", Probability: technical},
{Option: "account", Probability: account},
}},
}}}
}
func main() {
fake := discerntest.Static(discern.Jev, exampleObservation("jev-1.13.0", "technical", 0.3, 0.62, 0.08))
ev, err := discern.New(discern.WithAdapter(fake))
if err != nil {
fmt.Println(err)
return
}
res, err := ev.Evaluate(context.Background(), exampleRequest(discern.JevOnly))
if err != nil {
fmt.Println(err)
return
}
// Replay two candidate thresholds locally: no further provider calls.
for _, threshold := range []float64{0.6, 0.7} {
pol := discern.Policy{Version: fmt.Sprintf("trial-%.1f", threshold), Questions: []discern.QuestionPolicy{{
Question: "queue",
Checks: []discern.Check{{Metric: discern.MetricChoiceProbability, Op: discern.AtLeast, Threshold: threshold}},
}}}
again, err := discern.Recommend(res, pol)
if err != nil {
fmt.Println(err)
return
}
rec, _ := again.Recommendation("queue")
fmt.Println(pol.Version, rec.Status)
}
fmt.Println(fake.Calls(), "call")
}
Output: trial-0.6 accepted trial-0.7 below_threshold 1 call
func (*Result) Observation ΒΆ
func (r *Result) Observation(p Provider) (Observation, bool)
Observation returns a copy of the provider's observation, or false if the provider was not selected or failed.
func (*Result) Outcome ΒΆ
Outcome returns a copy of the provider's outcome, or false if the provider was not selected.
func (*Result) Recommendation ΒΆ
func (r *Result) Recommendation(questionID string) (Recommendation, bool)
Recommendation returns a copy of the question's recommendation.
func (*Result) Redact ΒΆ
Redact returns a deep copy with the selected fields removed and listed in Redacted. Question, option and level IDs cannot be redacted; keep them free of sensitive data.
func (*Result) SelectedAnswers ΒΆ
SelectedAnswers returns copies of the answers an accepted recommendation selected, in fixed provider order. It returns nil unless the question's status is Accepted.
type ResultStatus ΒΆ
type ResultStatus string
ResultStatus summarizes the provider outcomes of one evaluation.
const ( // Complete means every selected provider succeeded. Complete ResultStatus = "complete" // Partial means one of two selected providers failed. Partial ResultStatus = "partial" // Failed means every selected provider failed. Failed ResultStatus = "failed" )
Result statuses.
type ScoreAnswer ΒΆ
type ScoreAnswer struct {
Value float64 `json:"value"`
Confidence *float64 `json:"confidence,omitempty"`
Probabilities []LevelProbability `json:"probabilities"`
}
ScoreAnswer is a Score result. Value is the probability-weighted level position in [0, levels-1] and may be fractional. Probabilities are in level order.
func (ScoreAnswer) Nearest ΒΆ
func (s ScoreAnswer) Nearest() string
Nearest returns the ID of the level nearest to Value; halves round up. It returns "" if the answer has no levels.
Example ΒΆ
package main
import (
"fmt"
"github.com/mrz1836/go-discern"
)
func main() {
s := discern.ScoreAnswer{Value: 1.05, Probabilities: []discern.LevelProbability{
{Level: "cosmetic", Probability: 0.0},
{Level: "degraded", Probability: 0.95},
{Level: "blocked", Probability: 0.05},
}}
fmt.Println(s.Nearest())
}
Output: degraded
type ScoreLevel ΒΆ
ScoreLevel is one Score level. ID is local: the provider sees only the level's position. Description is required.
type Selected ΒΆ
type Selected struct {
Providers []Provider `json:"providers"`
Option string `json:"option,omitempty"`
}
Selected points at the accepted answers: the providers whose answers were selected, in fixed order, and for Choice the selected option. Numeric questions carry no blended value; use Result.SelectedAnswers for the typed answers.
type Usage ΒΆ
type Usage struct {
InputTokens *int64 `json:"input_tokens,omitempty"`
OutputTokens *int64 `json:"output_tokens,omitempty"`
// Neurons is Cloudflare's cf-ai-neurons billing figure, when supplied.
Neurons *float64 `json:"neurons,omitempty"`
}
Usage is provider-reported metering. Each field is nil when not reported; token counts from different providers are not one bill.
type ValidationError ΒΆ
ValidationError describes a local validation failure. Err is the matching sentinel (ErrInvalidConfig, ErrInvalidRequest, ErrInvalidPolicy, ErrUnsupported or ErrInvalidRecord). Field is a path such as "questions[1].options[0].id"; it uses indexes, not caller values. Reason never contains caller data.
func (ValidationError) Error ΒΆ
func (e ValidationError) Error() string
Error returns the sentinel message followed by the field and reason.
func (ValidationError) Unwrap ΒΆ
func (e ValidationError) Unwrap() error
Unwrap returns the sentinel.
type Value ΒΆ
type Value struct {
// contains filtered or unexported fields
}
Value is an immutable JSON value used for state, instructions and descriptions. Build one with Text, JSON or Marshal. The zero Value encodes as null, which is accepted only as a Choice option description.
A Value is checked once, when it is built: its validity, top-level type and whether it is blank are cached, so validating and encoding a large state never rescans or copies it more than needed.
func JSON ΒΆ
JSON returns a Value holding raw JSON bytes, which are copied and later sent verbatim: they are never re-encoded, so large integers and key order survive. The bytes must be valid JSON and valid UTF-8, and no object may repeat a key; that is checked when the request is validated, before any call.
func Marshal ΒΆ
Marshal encodes v once with encoding/json, without HTML escaping, and returns the result as a Value. The result follows the same rules as JSON; for example, a json.RawMessage field with duplicate keys makes the Value invalid.
func Text ΒΆ
Text returns a Value holding a JSON string. The string must be valid UTF-8; that is checked when the request is validated, before any call.
func (Value) AppendJSON ΒΆ
AppendJSON appends the encoded value to dst, like MarshalJSON, without an intermediate copy. It returns an error matching ErrInvalidRequest, and dst unchanged, for invalid JSON or UTF-8, or JSON with duplicate object keys.
func (Value) MarshalJSON ΒΆ
MarshalJSON returns the encoded value: a JSON string for Text, the raw bytes for JSON and Marshal, and null for the zero Value. HTML characters are not escaped. It returns an error matching ErrInvalidRequest for invalid JSON or UTF-8, or JSON with duplicate object keys.
Source Files
ΒΆ
Directories
ΒΆ
| Path | Synopsis |
|---|---|
|
Package clef is the discern adapter for Cloudflare Workers AI Clef, a hosted System One judgment model.
|
Package clef is the discern adapter for Cloudflare Workers AI Clef, a hosted System One judgment model. |
|
Package discerntest provides fake adapters for testing code that uses discern, without network access or provider credentials.
|
Package discerntest provides fake adapters for testing code that uses discern, without network access or provider credentials. |
|
Package main routes synthetic support tickets with go-discern.
|
Package main routes synthetic support tickets with go-discern. |
|
internal
|
|
|
adapter
Package adapter is the plumbing the Jev and Clef adapters share: their common options and the validation of them, the function that holds each Client's connection settings where no formatting can reach them, the one attempt each call makes, and the errors built along the way.
|
Package adapter is the plumbing the Jev and Clef adapters share: their common options and the validation of them, the function that holds each Client's connection settings where no formatting can reach them, the one attempt each call makes, and the errors built along the way. |
|
httpx
Package httpx is the single HTTP transport contract shared by the Jev and Clef adapters: one bounded POST attempt per call, no redirects, no retries, and errors that never carry URLs, headers, credentials or bodies.
|
Package httpx is the single HTTP transport contract shared by the Jev and Clef adapters: one bounded POST attempt per call, no redirects, no retries, and errors that never carry URLs, headers, credentials or bodies. |
|
ident
Package ident holds the byte-level identifier rules that discern and its adapters share, so that each rule is written once: visible ASCII for what a provider reports or a header carries, and a short name alphabet for question IDs and model names.
|
Package ident holds the byte-level identifier rules that discern and its adapters share, so that each rule is written once: visible ASCII for what a provider reports or a header carries, and a short name alphabet for question IDs and model names. |
|
jsonx
Package jsonx holds JSON primitives that encoding/json does not offer: a duplicate-key check and a string unquoter, both decoding escapes exactly as encoding/json does and allocating as little as the input allows, and a scan for the end of a string literal.
|
Package jsonx holds JSON primitives that encoding/json does not offer: a duplicate-key check and a string unquoter, both decoding escapes exactly as encoding/json does and allocating as little as the input allows, and a scan for the end of a string literal. |
|
livetest
Package livetest holds go-discern's opt-in live contract suite: tests that call the real Jev and Clef APIs and assert the provider behavior the adapters rely on, so a provider change shows up as a failing test whose message starts with "provider behavior changed?".
|
Package livetest holds go-discern's opt-in live contract suite: tests that call the real Jev and Clef APIs and assert the provider behavior the adapters rely on, so a provider change shows up as a failing test whose message starts with "provider behavior changed?". |
|
systemone
Package systemone encodes System One requests and strictly decodes System One responses for the Jev and Clef adapters.
|
Package systemone encodes System One requests and strictly decodes System One responses for the Jev and Clef adapters. |
|
testutil
Package testutil holds the test plumbing that go-discern's packages share: race-aware allocation guards, in-memory and recording HTTP fakes, fmt leak assertions, error extraction and JSON helpers.
|
Package testutil holds the test plumbing that go-discern's packages share: race-aware allocation guards, in-memory and recording HTTP fakes, fmt leak assertions, error extraction and JSON helpers. |
|
Package jev is the discern adapter for TypeSafe's hosted Jev System One API (POST https://api.typesafe.ai/v1/systemone, authenticated with a bearer API key).
|
Package jev is the discern adapter for TypeSafe's hosted Jev System One API (POST https://api.typesafe.ai/v1/systemone, authenticated with a bearer API key). |
