use std::collections::BTreeMap;
use super::contract::DecisionContract;
use super::question::QuestionSet;
#[derive(Clone, Debug)]
pub struct DecisionRequest<'a> {
pub model: &'a str,
pub provider: &'a str,
pub state: &'a serde_json::Value,
pub questions: &'a QuestionSet,
#[allow(dead_code)]
pub contract: &'a DecisionContract,
pub effort: &'a str,
pub temperature: f64,
pub evaluation_cost_limit: Option<f64>,
pub run_cost_limit: Option<f64>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ConfidenceProvenance {
#[allow(dead_code)]
VendorDistribution,
ModelReported,
}
#[derive(Clone, Debug, PartialEq)]
pub enum RawAnswer {
ModelReported {
selection: ReportedSelection,
confidence: f64,
evidence: Option<String>,
},
Boolean {
probability: f64,
reported_confidence: Option<f64>,
evidence: Option<String>,
},
Choice {
selected: Option<String>,
probabilities: BTreeMap<String, f64>,
reported_confidence: Option<f64>,
evidence: Option<String>,
},
Score {
probabilities: BTreeMap<String, f64>,
score: Option<f64>,
reported_confidence: Option<f64>,
evidence: Option<String>,
},
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ReportedSelection {
Boolean(bool),
Choice(String),
Score(String),
}
#[derive(Clone, Debug, PartialEq)]
pub struct RawDecisionResponse {
pub usage: Option<Box<crate::llm::usage::LlmUsage>>,
pub native_transport: Option<super::receipt::NativeTransportReceipt>,
pub answers: BTreeMap<String, RawAnswer>,
pub provenance: ConfidenceProvenance,
pub served_model: Option<String>,
pub input_tokens: Option<u64>,
pub output_tokens: Option<u64>,
pub physical_attempts: u32,
}
#[derive(Clone, Debug, PartialEq)]
#[non_exhaustive]
pub enum DecisionTransportError {
Accounted {
error: Box<DecisionTransportError>,
usage: Box<crate::llm::usage::LlmUsage>,
served_model: Option<String>,
},
AuthorityDenied,
LocalAdmissionDenied {
diagnostic: String,
},
Refused {
reason: RefusalReason,
diagnostic: String,
},
StateTooLarge {
provider_reason: String,
limit_tokens: Option<usize>,
},
RateLimited {
retry_after_ms: Option<u64>,
},
Overloaded,
UnsupportedOptions {
diagnostic: String,
},
TransportFailed {
diagnostic: String,
},
}
impl DecisionTransportError {
pub fn with_usage(
self,
usage: crate::llm::usage::LlmUsage,
served_model: Option<String>,
) -> Self {
Self::Accounted {
error: Box::new(self),
usage: Box::new(usage),
served_model,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RefusalReason {
ProviderRefusal,
SchemaInvalid,
OutputTruncated,
}
impl RefusalReason {
pub fn as_str(self) -> &'static str {
match self {
Self::ProviderRefusal => "provider_refusal",
Self::SchemaInvalid => "schema_invalid",
Self::OutputTruncated => "output_truncated",
}
}
}
#[async_trait::async_trait]
pub trait DecisionBackend: Send + Sync {
async fn evaluate(
&self,
request: DecisionRequest<'_>,
) -> Result<RawDecisionResponse, DecisionTransportError>;
}