/**
 * Decision inference types — typed, calibrated judgements from a model that
 * emits no text.
 *
 * `decide` is the third inference type, alongside `generate` and `stream`. A
 * decision model takes one `state` plus a map of named typed questions and
 * returns one typed answer per question, all evaluated in a single parallel
 * pass. TypeSafe's Jev is the first such model; Vercel's AI SDK models the
 * same class separately from `LanguageModelV4`, for the same reason.
 *
 * The vocabulary here is deliberately **provider-neutral**. TypeSafe calls a
 * yes/no question a `noul` and answers it in a field of the same name; both
 * the Vercel AI SDK and Pydantic AI independently renamed that to `boolean` /
 * `probability` when exposing it. This file follows them, and the mapping to
 * a vendor's wire format belongs in that vendor's provider.
 *
 * Names are domain-prefixed `Decision*` to stay globally unique across
 * `src/lib/types/` (CLAUDE.md rule 9). Note `evaluate`/`Evaluation*` is a
 * different, unrelated feature — scoring a generated response with RAGAS.
 */
import type { NeurolinkCredentials } from "./providers.js";
/**
 * Anything a decision model accepts as free-form content. `state` may be a
 * plain string or structured JSON (chat logs, records); `instructions` and
 * `criteria` values likewise.
 */
export type DecisionInput = string | number | boolean | readonly DecisionInput[] | {
    readonly [key: string]: DecisionInput;
};
/** The content being judged. One shared state per request. */
export type DecisionState = DecisionInput;
/**
 * A yes/no question. Answered with a probability and **no confidence of its
 * own** — gate it on distance from 0.5 via {@link decisionBooleanConfidence}.
 */
export type DecisionBooleanQuestion = {
    type: "boolean";
    instructions: DecisionInput;
    /** Optional descriptions of what a yes and a no mean. */
    criteria?: {
        true?: DecisionInput | null;
        false?: DecisionInput | null;
    };
};
/**
 * Pick one option. `criteria` maps option name → rubric description.
 *
 * The answer carries the full probability distribution, not just the winner,
 * so a single choice question over N options also **ranks** all N.
 */
export type DecisionChoiceQuestion = {
    type: "choice";
    instructions: DecisionInput;
    criteria: Readonly<Record<string, DecisionInput | null>>;
};
/**
 * Rate against an ordered rubric. The answer is the **0-based index**, so a
 * four-level rubric scores 0–3. At least two levels are required.
 */
export type DecisionScoreQuestion = {
    type: "score";
    instructions: DecisionInput;
    criteria: readonly (DecisionInput | null)[];
};
export type DecisionQuestion = DecisionBooleanQuestion | DecisionChoiceQuestion | DecisionScoreQuestion;
/** Questions keyed by an id you choose; answers return under the same ids. */
export type DecisionQuestionMap = Readonly<Record<string, DecisionQuestion>>;
export type DecisionBooleanAnswer = {
    type: "boolean";
    /** Probability the statement is true, 0–1. */
    probability: number;
};
export type DecisionChoiceAnswer = {
    type: "choice";
    /** The highest-probability option name. */
    choice: string;
    /** Every option mapped to its probability. Key order is not stable. */
    probabilities: Readonly<Record<string, number>>;
    /** Calibrated certainty, 0–1, derived from the distribution. */
    confidence: number;
};
export type DecisionScoreAnswer = {
    type: "score";
    /** Probability-weighted level index; lands between levels. */
    score: number;
    /** Level index (as a string key) → the description supplied. */
    legend: Readonly<Record<string, string>>;
    /** Level index (as a string key) → probability. */
    probabilities: Readonly<Record<string, number>>;
    confidence: number;
};
export type DecisionAnswer = DecisionBooleanAnswer | DecisionChoiceAnswer | DecisionScoreAnswer;
export type DecisionAnswerMap = Readonly<Record<string, DecisionAnswer>>;
export type DecisionUsage = {
    inputTokens: number;
    outputTokens: number;
};
/** An image or video as the caller supplies it: raw bytes, a file path, or a data URL. */
export type DecisionMediaSource = Buffer | string;
export type DecisionRequest = {
    state: DecisionState;
    questions: DecisionQuestionMap;
    /**
     * Images the model reads alongside `state`. Each is a Buffer, a local file
     * path or a `data:image/…;base64,` URL; an http(s) URL is refused. Only a
     * provider whose descriptor declares `decisionLimits.media` accepts them.
     */
    images?: readonly DecisionMediaSource[];
    /** One video, in the same forms. Sending images as well is allowed. */
    video?: DecisionMediaSource;
    /** Overrides the provider's configured model for this call only. */
    model?: string;
    signal?: AbortSignal;
    /** Overrides the configured timeout for this call only. */
    timeoutMs?: number;
};
export type DecisionResult = {
    /** The RESOLVED model id (e.g. "jev-1.13.0"), not the alias sent. */
    model: string;
    provider: string;
    answers: DecisionAnswerMap;
    usage: DecisionUsage;
    /** Vendor request id, for support escalation. */
    requestId?: string;
    /** Wall-clock round trip measured client-side. */
    latencyMs: number;
    /** Server-side time behind the edge; isolates model time from network. */
    upstreamMs?: number;
    /** Encoded size of the images and video sent. Absent for a text-only request. */
    mediaBytes?: number;
};
/**
 * Input a decision provider can actually read, declared on its descriptor.
 *
 * A request over either limit is refused before any network call with
 * `max_tokens_exceeded`, so no decision is ever made on input the model
 * silently cut off. Every internal consumer already treats that error as
 * "carry on as before".
 */
export type DecisionLimits = {
    /**
     * Estimated tokens of `state` (serialized first when it is not a string)
     * for any model NOT listed in `models` — an alias, a typo, a self-hosted
     * name — so it should be the tightest window the provider has.
     */
    maxStateTokens: number;
    /** Absent when the provider has no cap on questions per request. */
    maxQuestions?: number;
    /**
     * Tokens charged per non-ASCII character. The default estimate assumes ~4
     * characters per token, which holds for English and is several times too
     * generous for other scripts on an English tokenizer. Absent = default
     * estimate for every character.
     */
    nonAsciiTokensPerChar?: number;
    /**
     * Tokens charged per ASCII digit. A tokenizer that reads every digit as its
     * own token makes numbers, ids and timestamps several times longer than the
     * default estimate of four characters per token. Absent = digits are
     * estimated like any other ASCII character.
     */
    digitTokensPerChar?: number;
    /**
     * Tokens charged per ASCII punctuation or symbol character (`,` `.` `{` `"`
     * `:` and the like). The same tokenizers that read each digit alone read most
     * punctuation alone too, so JSON, logs and lists of numbers run far above four
     * characters a token. Absent = punctuation is estimated like any other ASCII
     * character.
     */
    symbolTokensPerChar?: number;
    /**
     * Tokens charged per character outside the Basic Multilingual Plane (emoji
     * and the like), which is counted separately from `nonAsciiTokensPerChar`
     * because it costs about twice as much. Absent = charged at
     * `nonAsciiTokensPerChar`.
     */
    astralTokensPerChar?: number;
    /** Per-model limits, keyed by model id; each field overrides the one above. */
    models?: Readonly<Record<string, {
        maxStateTokens: number;
        nonAsciiTokensPerChar?: number;
    }>>;
    /** What the provider accepts besides text. Absent means text only. */
    media?: DecisionMediaLimits;
};
export type DecisionMediaLimits = {
    /** Most images one request may carry. */
    maxImages: number;
    /** Whether a video is accepted (at most one). */
    video: boolean;
    /** Ceiling on the encoded request body, in bytes. */
    maxRequestBytes: number;
};
/** An image's size in pixels, read from its header. */
export type DecisionImageDimensions = {
    width: number;
    height: number;
};
/** Media after preparation: everything is a `data:` URL, ready for the wire. */
export type DecisionPreparedMedia = {
    images: string[];
    video?: string;
    /** Encoded size of every URL above. */
    bytes: number;
};
/**
 * Discriminated by a string literal, not a boolean: the package is also
 * compiled without strictNullChecks, where a boolean discriminant does not
 * narrow.
 */
export type DecisionMediaResult = {
    status: "prepared";
    media: DecisionPreparedMedia | undefined;
} | {
    status: "refused";
    message: string;
};
export type DecisionMediaKind = "image" | "video";
/** One image or video turned into a data URL, or the reason it could not be. Same literal-discriminant rule as above. */
export type DecisionMediaConversion = {
    status: "ok";
    dataUrl: string;
} | {
    status: "error";
    message: string;
};
/** A media file read from disk, or the reason it could not be. */
export type DecisionMediaFileRead = {
    status: "ok";
    buffer: Buffer;
} | {
    status: "error";
    message: string;
};
/**
 * Why a decision call failed, normalised across vendors. TypeSafe alone
 * returns two different error envelopes, so a provider must flatten them.
 */
export type DecisionErrorKind = "authentication" | "invalid_request" | "max_tokens_exceeded" | "rate_limit" | "overloaded" | "server" | "timeout" | "network";
export type DecisionError = {
    kind: DecisionErrorKind;
    message: string;
    status?: number;
    requestId?: string;
    /** True when a retry could plausibly succeed. */
    retryable: boolean;
};
/** One entry from a decision provider's model listing. */
export type DecisionModelCard = {
    name: string;
    description: string;
    releaseDate: string;
};
/**
 * Narrowed view of a choice answer, safe to consume without re-narrowing.
 * Returned by the reader helpers so call sites need no type assertion.
 */
export type DecisionChoiceReading = {
    choice: string;
    confidence: number;
    probabilities: Readonly<Record<string, number>>;
    /**
     * Every option ordered by probability, highest first. A choice question is
     * therefore also a ranking — the basis for catalogue selection.
     */
    ranked: readonly {
        name: string;
        probability: number;
    }[];
};
export type DecisionScoreReading = {
    score: number;
    confidence: number;
    legend: Readonly<Record<string, string>>;
    probabilities: Readonly<Record<string, number>>;
};
/**
 * What `NeuroLink.decide()` accepts: a decision request plus the usual
 * provider/credential selection every inference type shares.
 */
export type DecisionOptions = DecisionRequest & {
    /**
     * Provider name or alias. Defaults to the first registered provider whose
     * descriptor declares `"decide"` in `inferenceKinds`.
     */
    provider?: string;
    /** Per-call credential overrides, as `generate()` takes. */
    credentials?: NeurolinkCredentials;
};
/**
 * Injected fail-open decision caller — typically a bound `NeuroLink.tryDecide`,
 * which returns `null` on any failure rather than throwing.
 *
 * Every internal consumer of the `decide` inference type takes one of these
 * instead of importing a provider, exactly as the tool router takes a
 * `generateFn`. It is what keeps the consumers provider-import-free, and what
 * makes "no decision provider configured" indistinguishable from "the call
 * failed" at every call site: both are `null`, and both mean *carry on as
 * before*.
 */
export type DecisionCallerFn = (options: DecisionOptions) => Promise<DecisionResult | null>;
/**
 * Shared gate for acting on a yes/no answer.
 *
 * A `boolean` answer carries no confidence of its own, so two separate bars
 * apply: which side of the coin flip it fell on, and how far from the flip it
 * landed. Both must be cleared, which is why "probability 0.55" never counts
 * as a yes.
 */
export type DecisionBooleanGate = {
    /** Minimum probability to read the answer as "yes". Default 0.5. */
    minProbability?: number;
    /** Minimum {@link decisionBooleanConfidence} to act at all. Default 0.4. */
    minConfidence?: number;
};
