@@ -0,0 +1,524 @@
//! System One-style models that can extract, compute, estimate,
//! and forecast values from inputs.
//! They are strongly typed and output confidence scores and probabilities against their outputs.
//!
//! Request: [`ExtractionRequest`] · Response: [`ExtractionResponse`]
use jiff ::civil ::{ Date , DateTime , Time } ;
use serde ::{ Deserialize , Serialize } ;
use std ::collections ::HashMap ;
/// The item type required for `list` fields.
#[ derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq) ]
#[ serde(rename_all = " lowercase " ) ]
pub enum ListItems {
/// List of text items.
Text ,
/// List of numbers.
Number ,
/// List of ISO dates.
Date ,
}
/// Scale for estimate/forecast grids.
#[ derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default) ]
#[ serde(rename_all = " lowercase " ) ]
pub enum Scale {
/// Linear scale (default).
#[ default ]
Linear ,
/// Logarithmic scale.
Log ,
}
/// Interval for forecast fields.
#[ derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq) ]
#[ serde(rename_all = " lowercase " ) ]
pub enum ForecastInterval {
/// One step of the grid.
Step ,
/// Per minute.
Minute ,
/// Per hour.
Hour ,
/// Per day.
Day ,
/// Per week.
Week ,
/// Per month.
Month ,
/// Per quarter.
Quarter ,
/// Per year.
Year ,
}
/// A value that may be a number or an ISO date string. Used for the
/// `min`/`max` bounds of estimate/forecast fields (ISO date for date
/// estimates) and for the `step` grid.
#[ derive(Debug, Clone, Serialize, Deserialize, PartialEq) ]
#[ serde(untagged) ]
pub enum NumberOrDate {
/// A numeric bound or step size.
Number ( f64 ) ,
/// An ISO date string (for date estimates).
Date ( Date ) ,
}
/// A step size for estimate grids: a positive number (days for date
/// estimates), or the strings `"month"` / `"year"`.
#[ derive(Debug, Clone, Serialize, Deserialize, PartialEq) ]
#[ serde(untagged) ]
pub enum EstimateStep {
/// A positive numeric step (days for date estimates).
Number ( f64 ) ,
/// Calendar-aware step: `"month"`.
Month ,
/// Calendar-aware step: `"year"`.
Year ,
}
#[ derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize) ]
#[ serde(rename_all = " lowercase " ) ]
pub enum DurationUnit {
Seconds ,
Minutes ,
Hours ,
Days ,
Weeks ,
Months ,
Years ,
}
/// A typed field specification
#[ derive(Debug, Clone, Serialize, Deserialize, PartialEq) ]
#[ serde(tag = " type " , rename_all = " lowercase " ) ]
pub enum Field {
/// A string extracted from the input.
#[ serde(rename = " text " ) ]
Text {
/// What the field should contain.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
instructions : Option < String > ,
/// Probability of null above which the value is null (default 0.5).
/// Must be in `(0, 1]`.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
null_threshold : Option < f64 > ,
} ,
/// A numeric value.
#[ serde(rename = " number " ) ]
Number {
/// What the field should contain.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
instructions : Option < String > ,
/// Unit label for the number (e.g. `USD`).
#[ serde(skip_serializing_if = " Option::is_none " ) ]
unit : Option < String > ,
/// Whether the number must be an integer. Numbers and durations only.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
integer : Option < bool > ,
/// Number of decimal places (0– 6). Numbers, durations and number
/// lists only.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
decimals : Option < u8 > ,
/// Probability of null above which the value is null (default 0.5).
#[ serde(skip_serializing_if = " Option::is_none " ) ]
null_threshold : Option < f64 > ,
} ,
/// An ISO date value.
#[ serde(rename = " date " ) ]
Date {
/// What the field should contain.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
instructions : Option < String > ,
/// Probability of null above which the value is null (default 0.5).
#[ serde(skip_serializing_if = " Option::is_none " ) ]
null_threshold : Option < f64 > ,
} ,
/// An ISO time value.
#[ serde(rename = " time " ) ]
Time {
/// What the field should contain.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
instructions : Option < String > ,
/// Probability of null above which the value is null (default 0.5).
#[ serde(skip_serializing_if = " Option::is_none " ) ]
null_threshold : Option < f64 > ,
} ,
/// An ISO date-time value.
#[ serde(rename = " datetime " ) ]
Datetime {
/// What the field should contain.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
instructions : Option < String > ,
/// Probability of null above which the value is null (default 0.5).
#[ serde(skip_serializing_if = " Option::is_none " ) ]
null_threshold : Option < f64 > ,
} ,
/// A duration value.
#[ serde(rename = " duration " ) ]
Duration {
/// What the field should contain.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
instructions : Option < String > ,
/// Unit label
#[ serde(skip_serializing_if = " Option::is_none " ) ]
unit : Option < DurationUnit > ,
/// Whether the duration must be an integer. Numbers and durations only.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
integer : Option < bool > ,
/// Number of decimal places (0– 6). Numbers, durations and number
/// lists only.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
decimals : Option < u8 > ,
/// Probability of null above which the value is null (default 0.5).
#[ serde(skip_serializing_if = " Option::is_none " ) ]
null_threshold : Option < f64 > ,
} ,
/// A list of typed items.
#[ serde(rename = " list " ) ]
List {
/// What the field should contain.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
instructions : Option < String > ,
/// Required for list fields: the type of each item.
items : ListItems ,
/// Number of decimal places (0– 6) for number lists.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
decimals : Option < u8 > ,
/// Probability of null above which the value is null (default 0.5).
#[ serde(skip_serializing_if = " Option::is_none " ) ]
null_threshold : Option < f64 > ,
} ,
/// A probabilistic estimate over a bounded grid.
#[ serde(rename = " estimate " ) ]
Estimate {
/// What the field should contain.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
instructions : Option < String > ,
/// Required for estimate/forecast: lower bound (a number, or an ISO
/// date for date estimates).
min : NumberOrDate ,
/// Required for estimate/forecast: upper bound (a number, or an ISO
/// date for date estimates).
max : NumberOrDate ,
/// Estimate grid: a positive number (days for date estimates), or
/// `"month"` / `"year"`.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
step : Option < EstimateStep > ,
/// Grid scale: `linear` (default) or `log`.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
scale : Option < Scale > ,
/// Probability of null above which the value is null (default 0.5).
#[ serde(skip_serializing_if = " Option::is_none " ) ]
null_threshold : Option < f64 > ,
} ,
/// A forecast: a sequence of estimates over a horizon.
#[ serde(rename = " forecast " ) ]
Forecast {
/// What the field should contain.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
instructions : Option < String > ,
/// Required for estimate/forecast: lower bound (a number, or an ISO
/// date for date estimates).
min : NumberOrDate ,
/// Required for estimate/forecast: upper bound (a number, or an ISO
/// date for date estimates).
max : NumberOrDate ,
/// Estimate grid: a positive number (days for date estimates), or
/// `"month"` / `"year"`.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
step : Option < EstimateStep > ,
/// Grid scale: `linear` (default) or `log`.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
scale : Option < Scale > ,
/// Forecast only: number of steps (1– 64).
#[ serde(skip_serializing_if = " Option::is_none " ) ]
horizon : Option < u32 > ,
/// Forecast only: the interval between steps.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
interval : Option < ForecastInterval > ,
/// Probability of null above which the value is null (default 0.5).
#[ serde(skip_serializing_if = " Option::is_none " ) ]
null_threshold : Option < f64 > ,
} ,
}
/// An image input. Either a data URL string or an explicit base64 object.
#[ derive(Debug, Clone, Serialize, Deserialize, PartialEq) ]
#[ serde(untagged) ]
pub enum Image {
/// A data URL string (must start with `data:`, case-insensitive).
DataUrl (
/// The data URL, e.g. `data:image/png;base64,...`.
String ,
) ,
/// An explicitly split base64 image.
Base64 {
/// MIME content type of the image (PNG, JPEG or WebP).
content_type : String ,
/// Base64-encoded image bytes.
base64 : String ,
} ,
}
/// Optional request settings.
#[ derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default) ]
pub struct RequestOptions {
/// Add 99 percentiles to estimates (default `false`).
#[ serde(default, skip_serializing_if = " Option::is_none " ) ]
pub include_quantiles : Option < bool > ,
/// Return the text that spans index into (default `false`).
#[ serde(default, skip_serializing_if = " Option::is_none " ) ]
pub return_state_text : Option < bool > ,
}
/// The extraction request body.
#[ derive(Debug, Clone, Serialize, Deserialize, PartialEq) ]
pub struct ExtractionRequest < Model > {
/// The model selector
pub model : Model ,
/// The text to read and predict from. Must be non-empty unless images
/// are sent. Text answers are spans of this string.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub state : Option < String > ,
/// Required. Map of field name to a typed field spec. At least one
/// field; no upper limit. Fields that do not fit next to the state in
/// one 8,192-token pass are split across parallel passes. Field names
/// must match `^[A-Za-z_][A-Za-z0-9_.\-]{0,63}$`.
pub fields : HashMap < String , Field > ,
/// Optional embedded PNG, JPEG or WebP images placed before the state
/// (max 4; 4 MiB each, 8 MiB total decoded; request body max 16 MiB).
/// Remote URLs are not accepted.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub images : Option < Vec < Image > > ,
/// ISO date that anchors dates written without a year.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub reference_date : Option < Date > ,
/// Optional request settings.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub options : Option < RequestOptions > ,
}
/// The value of an answer. Shape depends on the field type; `null` when the
/// input does not determine it.
#[ derive(Debug, Clone, Serialize, Deserialize, PartialEq) ]
#[ serde(untagged) ]
pub enum List {
// A list of strings
Strings ( Vec < String > ) ,
// A list of numbers
Numbers ( Vec < f64 > ) ,
// A list of dates
Dates ( Vec < Date > ) ,
}
/// A forecast step: one estimate per step, with median and interval bounds.
#[ derive(Debug, Clone, Serialize, Deserialize, PartialEq) ]
pub struct ForecastStep {
/// The point (median) value for this step.
pub value : f64 ,
/// Mean of the distribution for this step.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub mean : Option < f64 > ,
/// Lower bound of the interval for this step.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub p10 : Option < f64 > ,
/// Upper bound of the interval for this step.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub p90 : Option < f64 > ,
}
/// One answer per requested field, keyed by field name. Tagged by `type`,
/// mirroring the field spec that produced it.
#[ derive(Debug, Clone, Serialize, Deserialize, PartialEq) ]
#[ serde(tag = " type " , rename_all = " lowercase " ) ]
pub enum Answer {
/// Answer for a text field: an exact span of the state, with character
/// offsets.
#[ serde(rename = " text " ) ]
Text {
/// The extracted value, or null when the input does not determine it.
value : Option < String > ,
/// The model's best guess before applying the null threshold.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
guess : Option < String > ,
/// Probability that the field is null.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
p_null : Option < f64 > ,
/// Confidence score in `[0, 1]`.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
confidence : Option < f64 > ,
/// Character offsets `[start, end]` of the span into the state.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
span : Option < [ usize ; 2 ] > ,
} ,
/// Answer for a number field.
#[ serde(rename = " number " ) ]
Number {
/// The extracted value, or null when the input does not determine it.
value : Option < f64 > ,
/// The model's best guess before applying the null threshold.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
guess : Option < f64 > ,
/// Probability that the field is null.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
p_null : Option < f64 > ,
/// Confidence score in `[0, 1]`.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
confidence : Option < f64 > ,
} ,
/// Answer for a date field.
#[ serde(rename = " date " ) ]
Date {
/// The computed/extracted ISO date, or null.
value : Option < Date > ,
/// The model's best guess before applying the null threshold.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
guess : Option < Date > ,
/// Probability that the field is null.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
p_null : Option < f64 > ,
/// Confidence score in `[0, 1]`.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
confidence : Option < f64 > ,
} ,
/// Answer for a time field.
#[ serde(rename = " time " ) ]
Time {
/// The extracted ISO time, or null.
value : Option < Time > ,
/// The model's best guess before applying the null threshold.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
guess : Option < Time > ,
/// Probability that the field is null.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
p_null : Option < f64 > ,
/// Confidence score in `[0, 1]`.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
confidence : Option < f64 > ,
} ,
/// Answer for a datetime field.
#[ serde(rename = " datetime " ) ]
Datetime {
/// The extracted ISO date-time, or null.
value : Option < DateTime > ,
/// The model's best guess before applying the null threshold.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
guess : Option < DateTime > ,
/// Probability that the field is null.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
p_null : Option < f64 > ,
/// Confidence score in `[0, 1]`.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
confidence : Option < f64 > ,
} ,
/// Answer for a duration field.
#[ serde(rename = " duration " ) ]
Duration {
/// The extracted duration, or null.
value : Option < f64 > ,
/// The model's best guess before applying the null threshold.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
guess : Option < f64 > ,
/// Probability that the field is null.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
p_null : Option < f64 > ,
/// Confidence score in `[0, 1]`.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
confidence : Option < f64 > ,
} ,
/// Answer for a list field: values with per-item spans.
#[ serde(rename = " list " ) ]
List {
/// The extracted items, or null.
value : Option < List > ,
/// Per-item character offsets `[start, end]` into the state.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
spans : Option < Vec < [ usize ; 2 ] > > ,
/// Probability that the field is null.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
p_null : Option < f64 > ,
/// Confidence score in `[0, 1]`.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
confidence : Option < f64 > ,
} ,
/// Answer for an estimate field: a distribution summary.
#[ serde(rename = " estimate " ) ]
Estimate {
/// The point estimate (median).
value : NumberOrDate ,
/// Mean of the estimated distribution.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
mean : Option < f64 > ,
/// Median of the estimated distribution.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
median : Option < f64 > ,
/// 10th percentile of the estimated distribution.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
p10 : Option < f64 > ,
/// 90th percentile of the estimated distribution.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
p90 : Option < f64 > ,
/// Probability the true value is below the grid minimum.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
p_below : Option < f64 > ,
/// Probability the true value is above the grid maximum.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
p_above : Option < f64 > ,
/// 99 percentiles, when `options.include_quantiles` is set.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
quantiles : Option < Vec < f64 > > ,
} ,
/// Answer for a forecast field: one estimate per step.
#[ serde(rename = " forecast " ) ]
Forecast {
/// One estimate per step.
#[ serde(default) ]
steps : Vec < ForecastStep > ,
/// Probability that the field is null.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
p_null : Option < f64 > ,
/// Confidence score in `[0, 1]`.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
confidence : Option < f64 > ,
} ,
}
/// Token and pass accounting for a request.
#[ derive(Debug, Clone, Serialize, Deserialize, PartialEq) ]
pub struct Usage {
/// Tokens consumed by the state text.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub state_tokens : Option < u64 > ,
/// Tokens consumed by images.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub image_tokens : Option < u64 > ,
/// Tokens consumed by the field schema.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub schema_tokens : Option < u64 > ,
/// Total input tokens.
pub input_tokens : u64 ,
/// Total output tokens.
pub output_tokens : u64 ,
/// Number of parallel passes used.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub passes : Option < u64 > ,
}
/// The extraction response body.
#[ derive(Debug, Clone, Serialize, Deserialize, PartialEq) ]
pub struct ExtractionResponse < Model > {
/// The model that produced this response
pub model : Model ,
/// One answer per field, keyed by field name. `value` is null when the
/// input does not determine it.
pub answers : HashMap < String , Answer > ,
/// Whether the input was truncated to fit the context window.
pub truncated : bool ,
/// The text that spans index into, when `options.return_state_text` is set.
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub state_text : Option < String > ,
/// Token and pass accounting for the request.
pub usage : Usage ,
}