Skip to main content

synth_ai_core/orchestration/
schemas.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3use std::collections::HashMap;
4
5pub use super::progress::{SeedInfo, StageInfo, TokenUsage};
6
7pub const MAX_INSTRUCTION_LENGTH: usize = 4000;
8pub const MAX_ROLLOUT_SAMPLES: usize = 5;
9pub const MAX_SEED_INFO_COUNT: usize = 50;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct MutationTypeStats {
13    pub attempts: i64,
14    pub acceptances: i64,
15    pub acceptance_rate: f64,
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct MutationSummary {
20    pub by_mutation_type: HashMap<String, MutationTypeStats>,
21    pub total_attempts: i64,
22    pub total_acceptances: i64,
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct SeedAnalysis {
27    pub hard_seeds: Vec<i64>,
28    pub easy_seeds: Vec<i64>,
29    pub baseline_failures: Vec<i64>,
30    pub total_seeds_evaluated: i64,
31    pub total_candidates: i64,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct PhaseSummary {
36    pub phase: String,
37    #[serde(default)]
38    pub duration_seconds: Option<f64>,
39    #[serde(default)]
40    pub rollouts_completed: Option<i64>,
41    #[serde(default)]
42    pub candidates_evaluated: Option<i64>,
43    #[serde(default)]
44    #[serde(alias = "best_score")]
45    pub best_reward: Option<f64>,
46    #[serde(default)]
47    pub extra: HashMap<String, Value>,
48}
49
50fn default_mutation_type() -> String {
51    "unknown".to_string()
52}
53
54fn default_status() -> String {
55    "evaluated".to_string()
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct ProgramCandidate {
60    pub candidate_id: String,
61    pub generation: i64,
62    #[serde(default)]
63    pub stages: HashMap<String, StageInfo>,
64    #[serde(default)]
65    pub parent_id: Option<String>,
66    #[serde(default = "default_mutation_type")]
67    pub mutation_type: String,
68    #[serde(default)]
69    pub mutation_params: Option<Value>,
70    #[serde(default, alias = "accuracy")]
71    pub reward: f64,
72    #[serde(default, alias = "val_accuracy")]
73    pub val_reward: Option<f64>,
74    #[serde(default, alias = "minibatch_score")]
75    pub minibatch_reward: Option<f64>,
76    #[serde(default, alias = "seed_scores")]
77    pub seed_rewards: Option<Vec<Value>>,
78    #[serde(default)]
79    pub seed_info: Option<Vec<SeedInfo>>,
80    #[serde(default, alias = "instance_scores")]
81    pub instance_rewards: Option<Vec<Value>>,
82    #[serde(default)]
83    pub objectives: Option<Value>,
84    #[serde(default)]
85    pub instance_objectives: Option<Value>,
86    #[serde(default)]
87    pub newly_solved_seeds: Option<Vec<i64>>,
88    #[serde(default)]
89    pub artifact_refs: Option<Vec<Value>>,
90    #[serde(default)]
91    pub success_statuses: Option<Vec<Value>>,
92    #[serde(default)]
93    pub token_usage: Option<TokenUsage>,
94    #[serde(default)]
95    pub cost_usd: Option<f64>,
96    #[serde(default)]
97    pub timestamp_ms: Option<i64>,
98    #[serde(default)]
99    pub evaluation_duration_ms: Option<i64>,
100    #[serde(default)]
101    pub transformation: Option<Value>,
102    #[serde(default)]
103    pub prompt_length: Option<i64>,
104    #[serde(default = "default_status")]
105    pub status: String,
106    #[serde(default)]
107    pub context_override_bundle_id: Option<String>,
108    #[serde(default)]
109    pub context_overrides: Option<Vec<Value>>,
110    #[serde(default)]
111    pub override_application_status: Option<String>,
112    #[serde(default)]
113    pub override_application_errors: Option<Vec<Value>>,
114    #[serde(default)]
115    pub context_snapshot_ref: Option<String>,
116}
117
118impl ProgramCandidate {
119    pub fn prompt_summary(&self, max_length: usize) -> String {
120        if self.stages.is_empty() {
121            return String::new();
122        }
123
124        let mut keys: Vec<&String> = self.stages.keys().collect();
125        keys.sort();
126
127        let mut parts: Vec<String> = Vec::new();
128        for key in keys {
129            if let Some(stage) = self.stages.get(key) {
130                let mut instruction = stage.instruction.clone();
131                if instruction.len() > MAX_INSTRUCTION_LENGTH {
132                    instruction.truncate(MAX_INSTRUCTION_LENGTH);
133                    instruction.push_str("...");
134                }
135                if !instruction.is_empty() {
136                    parts.push(format!("[{}]: {}", key.to_uppercase(), instruction));
137                }
138            }
139        }
140
141        let mut summary = parts.join("\n\n");
142        if summary.len() > max_length {
143            summary.truncate(max_length);
144            summary.push_str("...");
145        }
146        summary
147    }
148}