diff --git a/crates/protocol/src/decision.rs b/crates/protocol/src/decision.rs new file mode 100644 index 000000000..7d1ea28a8 --- /dev/null +++ b/crates/protocol/src/decision.rs @@ -0,0 +1,136 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +//! Provider-neutral decision questions and answers, separate from LLM messages. +//! +//! Enums use snake-case `type` tags and a `data` payload in serialized form. +//! Fields are public; providers and callers are responsible for valid values. + +use std::collections::BTreeMap; + +use serde::{Deserialize, Serialize}; +use serde_json::Value; + +use crate::{ModelId, Usage}; + +/// Answer probability on a `[0, 1]` scale. +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] +#[serde(transparent)] +pub struct Probability(pub f64); + +/// Position in the request's rubric, including fractional positions. +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] +#[serde(transparent)] +pub struct ScoreValue(pub f64); + +/// Provider confidence; its scale and meaning are provider-specific. +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] +#[serde(transparent)] +pub struct ProviderConfidence(pub f64); + +/// Shared context evaluated against independent, named questions. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct DecisionRequest { + /// Optional until a target is selected. + pub model: Option, + /// Conversation, application state, or other material to evaluate. + pub context: Value, + /// Independent questions keyed by their IDs. + pub questions: BTreeMap, +} + +/// Instructions and the expected answer shape for one question. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct DecisionQuestion { + /// Structured or textual instructions shared with the provider. + pub instructions: Value, + /// Expected answer shape. + pub kind: DecisionKind, +} + +/// The answer shape and any options or ordered rubric levels. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(tag = "type", content = "data", rename_all = "snake_case")] +pub enum DecisionKind { + /// A Boolean judgment or probability of true. + Boolean { + /// Meaning of a true answer, when needed. + true_description: Option, + /// Meaning of a false answer, when needed. + false_description: Option, + }, + /// Select one of the declared options. + Choice { + /// Nonempty options with unique IDs, preserving caller order. + options: Vec, + }, + /// A position on an ordered rubric, not an arbitrary numeric measurement. + Score { + /// At least two levels, ordered low to high and indexed from zero. + levels: Vec, + }, +} + +/// An identified choice with an optional structured description. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct ChoiceOption { + /// Stable identifier used by choice answers and distributions. + pub id: String, + /// Meaning of this option, when its ID alone is insufficient. + pub description: Option, +} + +/// Answers keyed by the matching request's question IDs. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct DecisionResponse { + /// Provider-reported response identifier. + pub id: Option, + /// Provider-reported model identifier. + pub model: Option, + /// Typed answers corresponding to the request's questions. + pub answers: BTreeMap, + /// Available token counts; absent counts remain unknown. + #[serde(default)] + pub usage: Usage, +} + +/// An answer and separate, optional provider confidence. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct DecisionAnswer { + /// The estimate for the matching question. + pub value: DecisionValue, + /// Provider-specific confidence, distinct from answer probabilities. + pub provider_confidence: Option, +} + +/// A typed estimate; missing distributions remain unknown. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(tag = "type", content = "data", rename_all = "snake_case")] +pub enum DecisionValue { + /// A Boolean judgment or probability, without an implicit threshold. + Boolean(BooleanEstimate), + /// One selected option with an optional complete distribution. + Choice { + /// Must name an option in the matching question. + selected: String, + /// Maps every declared option ID to its probability when available. + probabilities: Option>, + }, + /// A fractional position in the matching request's rubric. + Score { + /// Must lie in `0..=N-1` for the request's N levels. + value: ScoreValue, + /// Follows the request's level order. Retain that request to interpret it. + probabilities: Option>, + }, +} + +/// Preserves Boolean-only answers without inventing probability or certainty. +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] +#[serde(tag = "type", content = "data", rename_all = "snake_case")] +pub enum BooleanEstimate { + /// A Boolean judgment with no probability supplied. + Value(bool), + /// Probability of true; algorithms choose their own thresholds. + ProbabilityTrue(Probability), +} diff --git a/crates/protocol/src/lib.rs b/crates/protocol/src/lib.rs index 0d76b4d17..b32dc6c43 100644 --- a/crates/protocol/src/lib.rs +++ b/crates/protocol/src/lib.rs @@ -7,6 +7,7 @@ pub mod category; pub mod client; pub mod codex_namespaces; +pub mod decision; pub mod envelope; pub mod format; pub mod llm; @@ -16,6 +17,7 @@ pub mod stream; pub use category::*; pub use client::*; +pub use decision::*; pub use envelope::*; pub use format::*; pub use llm::*;