use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::Json;
use crate::api::event::DataSchema;
use super::response::{CostEstimate, Usage};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct LlmOptimizationKind(String);
impl LlmOptimizationKind {
pub const INPUT_COMPRESSION: &'static str = "input_compression";
pub const MODEL_ROUTING: &'static str = "model_routing";
#[must_use]
pub fn new(value: impl Into<String>) -> Self {
Self(value.into())
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
#[must_use]
pub fn input_compression() -> Self {
Self::new(Self::INPUT_COMPRESSION)
}
#[must_use]
pub fn model_routing() -> Self {
Self::new(Self::MODEL_ROUTING)
}
}
impl From<String> for LlmOptimizationKind {
fn from(value: String) -> Self {
Self::new(value)
}
}
impl From<&str> for LlmOptimizationKind {
fn from(value: &str) -> Self {
Self::new(value)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct LlmOptimizationModel {
pub model: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub provider: Option<String>,
}
impl LlmOptimizationModel {
#[must_use]
pub fn new(model: impl Into<String>) -> Self {
Self {
model: model.into(),
provider: None,
}
}
#[must_use]
pub fn with_provider(mut self, provider: impl Into<String>) -> Self {
self.provider = Some(provider.into());
self
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct LlmOptimizationModelTransition {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub baseline: Option<LlmOptimizationModel>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub effective: Option<LlmOptimizationModel>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct LlmOptimizationTokens {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub prompt_tokens: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub completion_tokens: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cache_read_tokens: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cache_write_tokens: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total_tokens: Option<u64>,
}
impl LlmOptimizationTokens {
#[must_use]
pub fn saved_prompt(prompt_tokens: u64) -> Self {
Self {
prompt_tokens: Some(prompt_tokens),
total_tokens: Some(prompt_tokens),
..Self::default()
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum LlmOptimizationEvidenceQuality {
Observed,
Estimated,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct LlmOptimizationTokenImpact {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub baseline: Option<LlmOptimizationTokens>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub effective: Option<LlmOptimizationTokens>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub saved: Option<LlmOptimizationTokens>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub quality: Option<LlmOptimizationEvidenceQuality>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub estimation_method: Option<String>,
}
pub trait LlmOptimizationPayload: Serialize {
const SCHEMA_NAME: &'static str;
const SCHEMA_VERSION: &'static str;
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LlmOptimizationContribution {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub id: Option<Uuid>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sequence: Option<u64>,
pub producer: String,
pub kind: LlmOptimizationKind,
#[serde(default)]
pub applied: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub model_transition: Option<LlmOptimizationModelTransition>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub token_impact: Option<LlmOptimizationTokenImpact>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub payload_schema: Option<DataSchema>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub payload: Option<Json>,
#[serde(flatten)]
pub extra: BTreeMap<String, Json>,
}
impl LlmOptimizationContribution {
#[must_use]
pub fn new(producer: impl Into<String>, kind: impl Into<LlmOptimizationKind>) -> Self {
Self {
id: None,
sequence: None,
producer: producer.into(),
kind: kind.into(),
applied: true,
model_transition: None,
token_impact: None,
payload_schema: None,
payload: None,
extra: BTreeMap::new(),
}
}
pub fn with_payload<T: LlmOptimizationPayload>(
mut self,
payload: &T,
) -> Result<Self, serde_json::Error> {
self.payload_schema = Some(DataSchema {
name: T::SCHEMA_NAME.to_string(),
version: T::SCHEMA_VERSION.to_string(),
});
self.payload = Some(serde_json::to_value(payload)?);
Ok(self)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum LlmOptimizationSummaryStatus {
Complete,
Partial,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LlmOptimizationSummary {
pub schema_version: String,
pub calculation_version: String,
pub status: LlmOptimizationSummaryStatus,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub limitations: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub baseline_model: Option<LlmOptimizationModel>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub effective_model: Option<LlmOptimizationModel>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub effective_usage: Option<Usage>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub baseline_usage: Option<Usage>,
pub tokens_saved: LlmOptimizationTokens,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub baseline_cost: Option<CostEstimate>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub actual_cost: Option<CostEstimate>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub estimated_cost_saved: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub currency: Option<String>,
pub contributions: Vec<LlmOptimizationContribution>,
}