diff --git a/src/openai/mod.rs b/src/openai/mod.rs index a18bf89..81efcce 100644 --- a/src/openai/mod.rs +++ b/src/openai/mod.rs @@ -2,3 +2,4 @@ pub mod chat; pub mod decisions; pub mod embeddings; pub mod models; +pub mod system_one; diff --git a/src/openai/system_one.rs b/src/openai/system_one.rs new file mode 100644 index 0000000..f48e053 --- /dev/null +++ b/src/openai/system_one.rs @@ -0,0 +1,265 @@ +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +/// The top-level request structure for the system one endpoint. +/// +/// This represents the complete request body that can be sent to the API. +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct SystemOneRequest { + /// The content to evaluate. Can be a string, object, or array. + /// + /// For text content, use a plain string. For structured data (like chat logs or application state), + /// use an object or array. + pub state: State, + + /// The model that handles the request. + pub model: Model, + + /// A map of typed questions to evaluate against the state. + /// + /// Each key is a question ID you choose, and the value is a [Question] object. + /// The answers will be returned under the same keys. The question ID is not sent to the model. + pub questions: HashMap, +} + +/// Represents the possible state formats that can be evaluated. +/// +/// This is a convenience enum that wraps the different possible state formats: +/// - Plain text (String) +/// - Structured data (Object/Array) +/// - Null (for models that support it) +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(untagged)] +pub enum State { + /// Plain text state + Text(String), + + /// Structured state (object or array) + Structured(serde_json::Value), + + /// Null state (for models that support it) + Null, +} + +/// The main question type enum that represents all possible question types. +/// +/// Each variant corresponds to a different question type (Noul, Choice, Score) +/// and contains the appropriate fields for that type. +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(tag = "type", rename_all = "lowercase")] +pub enum Question { + /// Yes/no question type + Noul(NoulQuestion), + + /// Multiple choice question type + Choice(ChoiceQuestion), + + /// Scoring question type + Score(ScoreQuestion), +} + +/// Represents a yes/no question (Noul = "no/yes/uncertain/likely" type) +/// +/// Returns the probability that the answer is yes (value near 1) or no (value near 0). +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct NoulQuestion { + /// The yes/no question to evaluate. + /// + /// Can be a plain string, or a structured object containing the question and referenced data. + pub instructions: QuestionInstructions, + + /// Optional descriptions of what a yes and no mean. + /// + /// Provides additional context for interpreting the yes/no answer. + #[serde(skip_serializing_if = "Option::is_none")] + pub criteria: Option, +} + +/// Criteria for a Noul question, describing what true/false mean. +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct NoulCriteria { + /// What a yes (value near 1) means. + pub r#true: QuestionInstructions, + + /// What a no (value near 0) means. + pub r#false: QuestionInstructions, +} + +/// Represents a multiple choice question. +/// +/// Returns the chosen option and the full probability distribution. +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct ChoiceQuestion { + /// What the model should decide. + /// + /// Can be a plain string, or a structured object containing the question and referenced data. + pub instructions: QuestionInstructions, + + /// A map of options to their descriptions. + /// + /// Each key is an option you define, and the value is its description. + /// Use null for options that don't need additional description. + /// Maximum of 255 options per Choice question. + pub criteria: HashMap>, +} + +/// Represents a scoring question. +/// +/// Returns a probability-weighted value across the defined levels. +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct ScoreQuestion { + /// What the model should rate. + /// + /// Can be a plain string, or a structured object containing the question and referenced data. + pub instructions: QuestionInstructions, + + /// An ordered array of level descriptions. + /// + /// Must have at least 2 levels, maximum of 10. The first element is the lowest level, + /// the last is the highest. + pub criteria: Vec, +} + +/// Represents the possible formats for question instructions and criteria. +/// +/// This is a convenience enum that wraps different possible formats: +/// - Plain text (String) +/// - Structured data (Object/Array) +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(untagged)] +pub enum QuestionInstructions { + /// Plain text instructions + Text(String), + + /// Structured instructions (object or array) + Structured(serde_json::Value), +} + +/// The top-level response structure from the system one endpoint. +/// +/// This represents the complete response body returned by the API. +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct SystemOneResponse { + /// The model that performed the evaluation. + pub model: Model, + + /// A map of answers, keyed by the same IDs used in the request questions. + pub answers: HashMap, + + /// Token usage information for the request. + pub usage: Usage, +} + +/// Represents the possible answer types that can be returned. +/// +/// Each variant corresponds to a different question type (Noul, Choice, Score) +/// and contains the appropriate fields for that type. +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(tag = "type", rename_all = "lowercase")] +pub enum Answer { + /// Answer to a Noul question + Noul(NoulAnswer), + + /// Answer to a Choice question + Choice(ChoiceAnswer), + + /// Answer to a Score question + Score(ScoreAnswer), +} + +/// Answer to a Noul question (yes/no question). +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct NoulAnswer { + /// The yes/no answer on a scale from 0 (no) to 1 (yes). + pub noul: f32, +} + +/// Answer to a Choice question (multiple choice). +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct ChoiceAnswer { + /// The option with the highest probability. + pub choice: String, + + /// The probability of each option, they sum to 1. + pub probabilities: HashMap, + + /// How certain the model is (0 to 1). + /// + /// 0 means all options are equally likely, 1 means maximum certainty. + pub confidence: f32, +} + +/// Answer to a Score question. +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct ScoreAnswer { + /// The expected level index, weighted by probability. + /// + /// Can be between two levels (e.g., 1.5 would be halfway between level 1 and 2). + pub score: f32, + + /// The description of each level index. + pub legend: HashMap, + + /// The probability of each level index, they sum to 1. + pub probabilities: HashMap, + + /// How certain the model is (0 to 1). + /// + /// Derived from the probabilities distribution. + pub confidence: f32, +} + +/// Token usage information for the request. +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct Usage { + /// Number of input tokens (prompt tokens for all questions). + pub input_tokens: u32, + + /// Number of output tokens (always 0 for this API). + pub output_tokens: u32, +} + +/// Optional file input for multimodal models. +/// +/// Represents an array of input files (images, audio, etc.) provided as data URLs. +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct FileInput { + /// Array of data URLs for input files. + /// + /// For images: data:image/...;base64,... + /// For audio: data:audio/...;base64,... + pub files: Vec, +} + +/// Alternative name for file input (alias for `files`). +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct ImageInput { + /// Array of image data URLs. + /// + /// Same format as `files` but specifically for images. + pub images: Vec, +} + +/// Complete request structure that includes optional file inputs. +/// +/// This is a convenience structure that combines the basic request with optional +/// file inputs for multimodal models. +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct MultimodalRequest { + /// The content to evaluate (same as in Request). + pub state: State, + + /// Optional file inputs (images, audio, etc.). + #[serde(skip_serializing_if = "Option::is_none")] + pub files: Option>, + + /// Alternative name for file inputs (alias for `files`). + #[serde(skip_serializing_if = "Option::is_none")] + pub images: Option>, + + /// The model that handles the request (same as in Request). + pub model: String, + + /// A map of typed questions to evaluate (same as in Request). + pub questions: HashMap, +}