Skip to main content

switchyard_protocol/
decision.rs

1// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Provider-neutral decision questions and answers, separate from LLM messages.
5//!
6//! Enums use snake-case `type` tags and a `data` payload in serialized form.
7//! Fields are public; providers and callers are responsible for valid values.
8
9use std::collections::BTreeMap;
10
11use serde::{Deserialize, Serialize};
12use serde_json::Value;
13
14use crate::{ModelId, Usage};
15
16/// Answer probability on a `[0, 1]` scale.
17#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
18#[serde(transparent)]
19pub struct Probability(pub f64);
20
21/// Position in the request's rubric, including fractional positions.
22#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
23#[serde(transparent)]
24pub struct ScoreValue(pub f64);
25
26/// Provider confidence; its scale and meaning are provider-specific.
27#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
28#[serde(transparent)]
29pub struct ProviderConfidence(pub f64);
30
31/// Shared context evaluated against independent, named questions.
32#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
33pub struct DecisionRequest {
34    /// Optional until a target is selected.
35    pub model: Option<ModelId>,
36    /// Conversation, application state, or other material to evaluate.
37    pub context: Value,
38    /// Independent questions keyed by their IDs.
39    pub questions: BTreeMap<String, DecisionQuestion>,
40}
41
42/// Instructions and the expected answer shape for one question.
43#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
44pub struct DecisionQuestion {
45    /// Structured or textual instructions shared with the provider.
46    pub instructions: Value,
47    /// Expected answer shape.
48    pub kind: DecisionKind,
49}
50
51/// The answer shape and any options or ordered rubric levels.
52#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
53#[serde(tag = "type", content = "data", rename_all = "snake_case")]
54pub enum DecisionKind {
55    /// A Boolean judgment or probability of true.
56    Boolean {
57        /// Meaning of a true answer, when needed.
58        true_description: Option<Value>,
59        /// Meaning of a false answer, when needed.
60        false_description: Option<Value>,
61    },
62    /// Select one of the declared options.
63    Choice {
64        /// Nonempty options with unique IDs, preserving caller order.
65        options: Vec<ChoiceOption>,
66    },
67    /// A position on an ordered rubric, not an arbitrary numeric measurement.
68    Score {
69        /// At least two levels, ordered low to high and indexed from zero.
70        levels: Vec<Value>,
71    },
72}
73
74/// An identified choice with an optional structured description.
75#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
76pub struct ChoiceOption {
77    /// Stable identifier used by choice answers and distributions.
78    pub id: String,
79    /// Meaning of this option, when its ID alone is insufficient.
80    pub description: Option<Value>,
81}
82
83/// Answers keyed by the matching request's question IDs.
84#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
85pub struct DecisionResponse {
86    /// Provider-reported response identifier.
87    pub id: Option<String>,
88    /// Provider-reported model identifier.
89    pub model: Option<ModelId>,
90    /// Typed answers corresponding to the request's questions.
91    pub answers: BTreeMap<String, DecisionAnswer>,
92    /// Available token counts; absent counts remain unknown.
93    #[serde(default)]
94    pub usage: Usage,
95}
96
97/// An answer and separate, optional provider confidence.
98#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
99pub struct DecisionAnswer {
100    /// The estimate for the matching question.
101    pub value: DecisionValue,
102    /// Provider-specific confidence, distinct from answer probabilities.
103    pub provider_confidence: Option<ProviderConfidence>,
104}
105
106/// A typed estimate; missing distributions remain unknown.
107#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
108#[serde(tag = "type", content = "data", rename_all = "snake_case")]
109pub enum DecisionValue {
110    /// A Boolean judgment or probability, without an implicit threshold.
111    Boolean(BooleanEstimate),
112    /// One selected option with an optional complete distribution.
113    Choice {
114        /// Must name an option in the matching question.
115        selected: String,
116        /// Maps every declared option ID to its probability when available.
117        probabilities: Option<BTreeMap<String, Probability>>,
118    },
119    /// A fractional position in the matching request's rubric.
120    Score {
121        /// Must lie in `0..=N-1` for the request's N levels.
122        value: ScoreValue,
123        /// Follows the request's level order. Retain that request to interpret it.
124        probabilities: Option<Vec<Probability>>,
125    },
126}
127
128/// Preserves Boolean-only answers without inventing probability or certainty.
129#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
130#[serde(tag = "type", content = "data", rename_all = "snake_case")]
131pub enum BooleanEstimate {
132    /// A Boolean judgment with no probability supplied.
133    Value(bool),
134    /// Probability of true; algorithms choose their own thresholds.
135    ProbabilityTrue(Probability),
136}