Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 136 additions & 0 deletions crates/protocol/src/decision.rs
Original file line number Diff line number Diff line change
@@ -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<ModelId>,
/// Conversation, application state, or other material to evaluate.
pub context: Value,
/// Independent questions keyed by their IDs.
pub questions: BTreeMap<String, DecisionQuestion>,
}

/// 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<Value>,
/// Meaning of a false answer, when needed.
false_description: Option<Value>,
},
/// Select one of the declared options.
Choice {
/// Nonempty options with unique IDs, preserving caller order.
options: Vec<ChoiceOption>,
},
/// 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<Value>,
},
}

/// 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<Value>,
}

/// 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<String>,
/// Provider-reported model identifier.
pub model: Option<ModelId>,
/// Typed answers corresponding to the request's questions.
pub answers: BTreeMap<String, DecisionAnswer>,
/// 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<ProviderConfidence>,
}

/// 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<BTreeMap<String, Probability>>,
},
/// 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<Vec<Probability>>,
},
}

/// 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),
}
2 changes: 2 additions & 0 deletions crates/protocol/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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::*;
Expand Down
Loading