From 0a111172f5852274245f354010b82ed2895a41f8 Mon Sep 17 00:00:00 2001 From: Jacob Date: Fri, 9 Oct 2026 02:28:26 -0400 Subject: [PATCH] feat: openai decisions types --- src/openai/decisions.rs | 309 ++++++++++++++++++++++++++++++++++++++++ src/openai/mod.rs | 1 + 2 files changed, 310 insertions(+) create mode 100644 src/openai/decisions.rs diff --git a/src/openai/decisions.rs b/src/openai/decisions.rs new file mode 100644 index 0000000..f0d7cfa --- /dev/null +++ b/src/openai/decisions.rs @@ -0,0 +1,309 @@ +use serde::{Deserialize, Serialize}; + +/// Represents the main request to the Decisions API +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct DecisionRequest { + /// The model that evaluates the request. Currently, only `gpt-6-luna` is supported. + pub model: Model, + + /// The text or images to evaluate for every question. + /// Can be a simple string or an array of user messages containing text and images. + #[serde(flatten)] + pub input: DecisionInput, + + /// Array of questions to evaluate against the input + #[serde(rename = "questions")] + pub questions: Vec, +} + +/// Represents the input to be evaluated, which can be either a string or an array of messages +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(untagged)] +pub enum DecisionInput { + /// Simple text input + Text(String), + /// Array of user messages containing text and images + Messages(Vec), +} + +/// Represents a message in the input, typically from a user +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct DecisionInputMessage { + /// The role of the message sender (always "user" for decisions) + pub role: String, + + /// The content of the message, which can be a string or an array of parts + #[serde(flatten)] + pub content: DecisionInputContent, +} + +/// Represents the content of a message, which can be a string or an array of parts +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(untagged)] +pub enum DecisionInputContent { + /// Simple text content + Text(String), + /// Array of text and image parts + Parts(Vec), +} + +/// Represents a part of a message, which can be text or an image +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(tag = "type")] +pub enum DecisionInputPart { + /// Text part of a message + #[serde(rename = "input_text")] + Text { + /// The text content + text: String, + }, + /// Image part of a message + #[serde(rename = "input_image")] + Image { + /// The image URL (base64 data URL or HTTP(S) URL) + image_url: String, + /// Optional detail level for the image + #[serde(skip_serializing_if = "Option::is_none")] + detail: Option, + }, +} + +/// Represents the detail level for an image +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] +#[serde(rename_all = "lowercase")] +pub enum ImageDetail { + /// Low detail + Low, + /// High detail + High, + /// Auto detail (default) + Auto, + /// Original detail + Original, +} + +/// Represents a question in the Decisions API +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(tag = "type", rename_all = "lowercase")] +pub enum DecisionQuestion { + /// A predicate question checks a condition + Predicate(PredicateQuestion), + /// A choice question selects from fixed options + Choice(ChoiceQuestion), + /// A score question evaluates against ordered levels + Score(ScoreQuestion), +} + +/// A predicate question checks a condition and returns a probability +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct PredicateQuestion { + /// Optional name to identify the question in the response + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, + + /// Instructions for evaluating the condition + pub instructions: String, +} + +/// A choice question selects from fixed options +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct ChoiceQuestion { + /// Optional name to identify the question in the response + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, + + /// Instructions for making the choice + pub instructions: String, + + /// Array of available choices + pub choices: Vec, +} + +/// Represents a single choice option +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct ChoiceOption { + /// The value of the choice + pub value: Choice, + + /// Optional description of when this choice applies + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, +} + +/// A score question evaluates against ordered levels +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct ScoreQuestion { + /// Optional name to identify the question in the response + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, + + /// Instructions for scoring + pub instructions: String, + + /// Array of ordered levels for scoring + pub levels: Vec, +} + +/// Represents a single score level +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct ScoreLevel { + /// The label for this level + pub label: String, + + /// Optional description of this level + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, +} + +/// Represents the response from the Decisions API +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct DecisionResponse { + /// Array of answers to the questions + pub answers: Vec, + + /// The model used for the decision + pub model: Model, + + /// Token usage information + pub usage: TokenUsage, +} + +/// Represents an answer to a question +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(tag = "type", rename_all = "lowercase")] +pub enum DecisionAnswer { + /// Answer to a predicate question + Predicate(PredicateAnswer), + /// Answer to a choice question + Choice(ChoiceAnswer), + /// Answer to a score question + Score(ScoreAnswer), + /// The model refused to answer this question + Refusal(RefusalAnswer), +} + +/// Answer to a predicate question +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct PredicateAnswer { + /// Optional name of the question + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, + + /// Probability that the condition is true (0.0 to 1.0) + pub probability: f64, +} + +/// Choice values are typed: a string and a boolean with the same text are distinct +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(untagged)] +pub enum Choice { + /// String + String(String), + /// Boolean + Boolean(bool), +} + +/// Answer to a choice question +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct ChoiceAnswer { + /// Optional name of the question + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, + + /// The selected choice value + pub choice: Choice, + + /// Confidence score (0.0 to 1.0) + pub confidence: f64, + + /// Probabilities for each choice option + pub probabilities: Vec, +} + +/// Represents the probability distribution for a choice answer +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct ChoiceProbability { + /// The choice value + pub value: Choice, + + /// Probability of this choice (0.0 to 1.0) + pub probability: f64, +} + +/// Answer to a score question +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct ScoreAnswer { + /// Optional name of the question + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, + + /// The computed score (can be between level indices) + pub score: f64, + + /// Confidence score (0.0 to 1.0) + pub confidence: f64, + + /// Probabilities for each score level + pub probabilities: Vec, +} + +/// Represents the probability distribution for a score answer +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct ScoreProbability { + /// The level index + pub value: i32, + + /// The level label + pub label: String, + + /// Probability of this level (0.0 to 1.0) + pub probability: f64, +} + +/// Answer indicating the model refused to answer a question +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct RefusalAnswer { + /// Optional name of the question + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, +} + +/// Represents token usage information +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct TokenUsage { + /// Total number of input tokens + pub input_tokens: i32, + + /// Details about input tokens + pub input_tokens_details: TokenUsageDetails, + + /// Total number of output tokens + pub output_tokens: i32, + + /// Details about output tokens + pub output_tokens_details: OutputTokenDetails, + + /// Total number of tokens (input + output) + pub total_tokens: i32, +} + +/// Represents details about input tokens +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct TokenUsageDetails { + /// Number of tokens written to cache + pub cache_write_tokens: i32, + + /// Number of tokens read from cache + pub cached_tokens: i32, +} + +/// Represents details about output tokens +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct OutputTokenDetails { + /// Number of reasoning tokens used + pub reasoning_tokens: i32, +} + +/// Optional safety identifier provided by the caller +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct SafetyIdentifier(pub String); diff --git a/src/openai/mod.rs b/src/openai/mod.rs index 58726e4..a18bf89 100644 --- a/src/openai/mod.rs +++ b/src/openai/mod.rs @@ -1,3 +1,4 @@ pub mod chat; +pub mod decisions; pub mod embeddings; pub mod models;