DecisionModel unstable
Defines the provider-neutral service for structured decisions.
decide encodes one input as JSON, sends its named decisions in one provider
call, and validates the answers. Failures are reported as AiError values.
decideSettled makes the same call but reports each decision separately, so
valid answers survive when others are missing, refused, or invalid.
Constructors
Creates a DecisionModel that encodes inputs as JSON and validates provider answers.
Answers must cover every decision and use its labels. Distributions must
sum to 1 within 1e-6, optional confidence must be in [0, 1], and ratings must be
in [0, criteria.length - 1], and boxes must have edges in [0, 1] that do
not cross. With decide, refused decisions fail with
AiError.ContentPolicyError and other unanswered decisions fail with
AiError.InvalidOutputError; decideSettled reports them per decision
instead. Encoding failures use AiError.InvalidUserInputError.
Details
After encoding, each Decision.Image in the input is replaced with a
reference string, and the provider receives the images in images. Set
supportsImages: true to accept images. Otherwise, an input with images
fails with AiError.InvalidUserInputError.
Set supportsLocate: true to accept locate decisions; only then does
decide receive them. Otherwise, a definition with a locate decision fails
with AiError.InvalidUserInputError, as does a locate decision whose path
holds no image.
Providers that round each probability to probabilityPrecision decimal
places may return distributions whose sum drifts from 1 by up to half a unit
of the last place per label. Setting probabilityPrecision accepts that
drift and rescales the distribution to sum to 1; larger drift still fails.
See
- DecisionModel for the service shape returned by this constructor
- ProviderOptions for the input passed to the provider implementation
- ProviderResponse for the provider response contract consumed by this constructor
Signature
declare function make(params: { readonly decide: (options: ProviderOptions<Probability | Classify<string> | Rate<string>>) => Effect<ProviderResponse, AiError>; readonly probabilityPrecision?: number; readonly supportsImages?: boolean; readonly supportsLocate?: false;} | { readonly decide: (options: ProviderOptions) => Effect<ProviderResponse, AiError>; readonly probabilityPrecision?: number; readonly supportsImages?: boolean; readonly supportsLocate: true;}): Effect<DecisionModel>Decisions
Answers a decision definition using the current DecisionModel service.
Encodes the input with Schema.toCodecJson, requiring the schema's encoding services.
Explicit undefined fields become null; absent fields stay absent.
Custom declarations need a JSON codec annotation or encoding fails.
Returned answers and probability dictionaries have null prototypes.
Fails with AiError.ContentPolicyError if the provider refuses any decision,
and otherwise with AiError.InvalidOutputError for the first unanswered
decision in definition order.
See
- DecisionModel for the service this function requires
- decideSettled for keeping valid answers when others fail
Signature
declare function decide<Input extends Constraint, Decisions extends Record<string, Any>>(definition: Definition<Input, Decisions>, options: DecideOptions<Input>): Effect<DecideResponse<Decisions>, AiError, DecisionModel | Input["EncodingServices"]>Example Triaging a ticket
import { Effect, Schema } from "effect"import { Decision, DecisionModel } from "effect/ai"
const TicketTriage = Decision.make({ input: Schema.String, decisions: { urgent: Decision.probability({ instructions: "The message is time-sensitive", criteria: { false: "No time pressure", true: "Needs action now" } }) }})
const program = Effect.gen(function*() { const { answers, usage } = yield* DecisionModel.decide(TicketTriage, { input: "My card was charged twice, please fix this today" }) return { probability: answers.urgent.probability, usage }})decideSettled
unstableAnswers each decision with a Result containing a validated answer or a
Decision.Unanswered reason. Input encoding and provider failures still
fail the effect. Token usage covers the whole call.
Details
Provider response decoding errors fail the whole call. Per-decision reasons
reflect the first failed validation check; "NoDistribution" requires an
otherwise valid answer.
See
- decide for failing unless every decision is answered
Signature
declare function decideSettled<Input extends Constraint, Decisions extends Record<string, Any>>(definition: Definition<Input, Decisions>, options: DecideOptions<Input>): Effect<DecideSettledResponse<Decisions>, AiError, DecisionModel | Input["EncodingServices"]>Example Falling back for unanswered decisions
import { Effect, Result, Schema } from "effect"import { Decision, DecisionModel } from "effect/ai"
const TicketTriage = Decision.make({ input: Schema.String, decisions: { department: Decision.classify({ instructions: "Which team should handle this", criteria: { billing: "payments", technical: "bugs" } }), urgent: Decision.probability({ instructions: "The message is time-sensitive" }) }})
const program = Effect.gen(function*() { const { outcomes } = yield* DecisionModel.decideSettled(TicketTriage, { input: "My card was charged twice, please fix this today" }) const department = Result.isSuccess(outcomes.department) && outcomes.department.success.probabilities.billing >= 0.8 ? "billing" : "needs review" const urgent = Result.isSuccess(outcomes.urgent) ? outcomes.urgent.success.probability : undefined return { department, urgent }})Models
Answers for every decision in a definition with usage and response metadata.
See
- DecisionUsage for token usage metadata
Signature
interface DecideResponse<Decisions extends Record<string, Decision.Any>> { readonly answers: Decision.Answers<Decisions>; readonly id?: string; readonly metadata: DecideResponseMetadata; readonly modelId?: string; readonly usage: DecisionUsage;}Provider-specific response metadata keyed by provider name. Provider packages augment this interface.
Signature
interface DecideResponseMetadata extends ProviderMetadata { [key: string]: Json;}Per-decision outcomes, token usage, and response metadata for the whole request.
See
- decideSettled for producing this response
Signature
interface DecideSettledResponse<Decisions extends Record<string, Decision.Any>> { readonly id?: string; readonly metadata: DecideResponseMetadata; readonly modelId?: string; readonly outcomes: Decision.Outcomes<Decisions>; readonly usage: DecisionUsage;}Decision operations over a definition.
Signature
interface DecisionModel { readonly "~effect/ai/DecisionModel": "~effect/ai/DecisionModel"; readonly decide: <Input extends Constraint, Decisions extends Record<string, Any>>(definition: Definition<Input, Decisions>, options: DecideOptions<Input>) => Effect<DecideResponse<Decisions>, AiError, Input["EncodingServices"]>; readonly decideSettled: <Input extends Constraint, Decisions extends Record<string, Any>>(definition: Definition<Input, Decisions>, options: DecideOptions<Input>) => Effect<DecideSettledResponse<Decisions>, AiError, Input["EncodingServices"]>;}DecisionUsage
unstableProvider-reported token usage. Unreported counts are undefined.
Signature
declare class DecisionUsage extends { readonly inputTokens?: number; readonly outputTokens?: number;} { constructor(...args: [props?: { readonly inputTokens?: number; readonly outputTokens?: number; }, options?: MakeOptions]);}An image in the input, declared with a Decision.Image field.
data is a base64 string, a byte array, or a URL.
Signature
interface Image { readonly data: string | Uint8Array<ArrayBufferLike> | URL; readonly mediaType: string;}Provider answer for any decision kind.
Signature
type ProviderAnswer = ProviderClassifyAnswer | ProviderRateAnswer | ProviderProbabilityAnswer | ProviderRefusalAnswer | ProviderLocateAnswerProvider classify answer. Omit probabilities when no distribution is available.
Signature
interface ProviderClassifyAnswer { readonly _tag: "Classify"; readonly confidence?: number; readonly label: string; readonly probabilities?: Readonly<Record<string, number>>;}An image extracted from the encoded input. state holds reference where
the image was, and path is where it was first found. data is a base64
string or a URL.
Signature
interface ProviderImage { readonly image: { readonly data: string | URL; readonly mediaType: string; }; readonly path: readonly Array<string | number>; readonly reference: string;}Provider answer for a locate decision.
Signature
interface ProviderLocateAnswer { readonly _tag: "Locate"; readonly box: BoundingBox; readonly confidence?: number;}Provider answer for a probability decision.
Signature
interface ProviderProbabilityAnswer { readonly _tag: "Probability"; readonly probability: number;}Provider rate answer. The highest probability determines the label, with
ties going to the first criterion. Omit probabilities when unavailable.
Signature
interface ProviderRateAnswer { readonly _tag: "Rate"; readonly confidence?: number; readonly probabilities?: Readonly<Record<string, number>>; readonly rating: number;}Provider refusal to answer a decision.
Signature
interface ProviderRefusalAnswer { readonly _tag: "Refusal";}Provider response for a decision request.
answers is keyed like the requested decisions. Each answer is validated
against its decision before it is returned to the caller. Omit the key of a
decision the provider did not answer. id, modelId and metadata are
passed through; metadata defaults to {}.
Signature
interface ProviderResponse { readonly answers: Readonly<Record<string, ProviderAnswer>>; readonly id?: string; readonly metadata?: DecideResponseMetadata; readonly modelId?: string; readonly usage: { readonly inputTokens: number | undefined; readonly outputTokens: number | undefined; };}Options
Options for answering a decision definition.
Signature
interface DecideOptions<Input extends Schema.Constraint> { readonly input: Input["Type"];}Provider input options for a decision request.
state is encoded with Schema.toCodecJson, not stringified.
All decisions must be answered in one call.
images holds the Decision.Image values found in the input and is omitted
when there are none.
Decisions is the union of decision kinds the provider receives.
Signature
interface ProviderOptions<Decisions extends Decision.Any = Decision.Any> { readonly decisions: Record<string, Decisions>; readonly images?: readonly Array<ProviderImage>; readonly state: Json;}Services
DecisionModel
unstableService key for answering decisions about an input. Create implementations
with make.
See
- decide for answering a definition through the current service
Signature
declare const DecisionModel: Context.Service<DecisionModel, DecisionModel>