use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SharingScope {
Request,
#[default]
Session,
Tenant,
Global,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RetentionTier {
Ephemeral,
ShortLived,
SessionDuration,
LongLived,
Permanent,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PlacementTarget {
CacheablePrefix,
DeferredToolBlock,
ArtifactReference,
RetrievalOnDemand,
SessionMemorySummary,
NonCacheableSuffix,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ModelClass {
Economy,
Standard,
Premium,
Critical,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum IntentType {
CacheStability,
ContentExtraction,
Serialization,
Priority,
ModelRouting,
Placement,
Retention,
ToolScope,
Compression,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CacheStabilityIntent {
pub stability_score: f64,
pub stable_prefix_end: usize,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub recommended_retention_tier: Option<RetentionTier>,
pub scope_label: SharingScope,
pub confidence: f64,
pub evidence_count: u32,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ContentExtractionIntent {
pub block_id: String,
pub variable_pattern: String,
pub extraction_strategy: String,
pub scope_label: SharingScope,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SerializationIntent {
pub fanout_width: u32,
pub expected_savings_tokens: u64,
pub reuse_probability: f64,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub added_latency_ms: Option<f64>,
pub scope_label: SharingScope,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PriorityIntent {
pub latency_sensitivity: f64,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub workflow_phase: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub caller_tier: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ModelRoutingIntent {
pub model_class: ModelClass,
pub complexity_score: f64,
pub criticality: f64,
pub fallback_allowed: bool,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PlacementIntent {
pub block_id: String,
pub target: PlacementTarget,
pub stability_score: f64,
pub scope_label: SharingScope,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RetentionIntent {
pub recommended_tier: RetentionTier,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub expected_session_duration_secs: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub inter_call_gap_p50_ms: Option<f64>,
pub scope_label: SharingScope,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ToolScopeIntent {
pub active_tools: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub phase_label: Option<String>,
pub deferred_tools: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CompressionIntent {
pub block_id: String,
pub compression_ratio: f64,
pub reversible: bool,
pub contribution_score: f64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "intent_type", rename_all = "snake_case")]
pub enum OptimizationIntent {
CacheStability(CacheStabilityIntent),
ContentExtraction(ContentExtractionIntent),
Serialization(SerializationIntent),
Priority(PriorityIntent),
ModelRouting(ModelRoutingIntent),
Placement(PlacementIntent),
Retention(RetentionIntent),
ToolScope(ToolScopeIntent),
Compression(CompressionIntent),
}
impl OptimizationIntent {
pub fn discriminant(&self) -> IntentType {
match self {
Self::CacheStability(_) => IntentType::CacheStability,
Self::ContentExtraction(_) => IntentType::ContentExtraction,
Self::Serialization(_) => IntentType::Serialization,
Self::Priority(_) => IntentType::Priority,
Self::ModelRouting(_) => IntentType::ModelRouting,
Self::Placement(_) => IntentType::Placement,
Self::Retention(_) => IntentType::Retention,
Self::ToolScope(_) => IntentType::ToolScope,
Self::Compression(_) => IntentType::Compression,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct OptimizationIntentBundle {
pub request_id: Uuid,
pub agent_identity: AgentIdentity,
pub policy_version: String,
pub intents: Vec<OptimizationIntent>,
pub created_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub struct AgentIdentity {
pub agent_id: String,
pub template_version: String,
pub toolset_hash: String,
pub model_family: String,
pub tenant_scope: String,
}
impl std::fmt::Display for AgentIdentity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}@{}", self.agent_id, self.template_version)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TranslationStatus {
Applied,
Degraded,
Ignored,
Rejected,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(tag = "code", rename_all = "snake_case")]
pub enum ReasonCode {
FullySupported,
UnsupportedByBackend,
UnsupportedByModel,
BackendLimitReached,
InsufficientEvidence,
FeatureDisabled,
UnsafeForRequest,
PluginIncomplete,
NotRelevant,
Custom {
reason: String,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct IntentOutcome {
pub intent_id: Uuid,
pub intent_type: IntentType,
pub status: TranslationStatus,
pub reason: ReasonCode,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub detail: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TranslationReport {
pub request_id: Uuid,
pub plugin_id: String,
pub outcomes: Vec<IntentOutcome>,
pub created_at: DateTime<Utc>,
}
impl TranslationReport {
pub fn all_applied(&self) -> bool {
self.outcomes
.iter()
.all(|o| o.status == TranslationStatus::Applied)
}
pub fn outcomes_by_status(&self, status: TranslationStatus) -> Vec<&IntentOutcome> {
self.outcomes
.iter()
.filter(|o| o.status == status)
.collect()
}
pub fn count_by_status(&self, status: TranslationStatus) -> usize {
self.outcomes.iter().filter(|o| o.status == status).count()
}
pub fn all_ignored(
bundle: &OptimizationIntentBundle,
plugin_id: &str,
reason: ReasonCode,
detail: Option<String>,
) -> Self {
let outcomes = bundle
.intents
.iter()
.map(|intent| IntentOutcome {
intent_id: Uuid::new_v4(),
intent_type: intent.discriminant(),
status: TranslationStatus::Ignored,
reason: reason.clone(),
detail: detail.clone(),
})
.collect();
Self {
request_id: bundle.request_id,
plugin_id: plugin_id.to_string(),
outcomes,
created_at: Utc::now(),
}
}
}
#[cfg(test)]
#[path = "../../tests/unit/acg/types_tests.rs"]
mod tests;