Skip to main content

synth_ai_core/data/
levers.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4use std::collections::HashMap;
5
6#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
7#[serde(rename_all = "snake_case")]
8pub enum ScopeKind {
9    Org,
10    System,
11    Run,
12    Branch,
13    Iteration,
14    Candidate,
15    Stage,
16    Seed,
17    Rollout,
18    Evaluation,
19    Custom,
20}
21
22#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
23pub struct ScopeKey {
24    pub kind: ScopeKind,
25    pub id: String,
26}
27
28impl ScopeKey {
29    pub fn new(kind: ScopeKind, id: impl Into<String>) -> Self {
30        Self {
31            kind,
32            id: id.into(),
33        }
34    }
35}
36
37#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
38#[serde(rename_all = "snake_case")]
39pub enum LeverKind {
40    Prompt,
41    Context,
42    Code,
43    Constraint,
44    Note,
45    Spec,
46    GraphYaml,
47    Variable,
48    Experiment,
49    Custom,
50}
51
52#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
53#[serde(rename_all = "snake_case")]
54pub enum LeverFormat {
55    Text,
56    Json,
57    Yaml,
58    File,
59    Custom,
60}
61
62#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
63#[serde(rename_all = "snake_case")]
64pub enum LeverMutability {
65    Optimizer,
66    Human,
67    System,
68}
69
70#[derive(Clone, Debug, Serialize, Deserialize)]
71pub struct LeverConstraints {
72    #[serde(default, skip_serializing_if = "Option::is_none")]
73    pub allowed_values: Option<Vec<Value>>,
74    #[serde(default, skip_serializing_if = "Option::is_none")]
75    pub min_value: Option<f64>,
76    #[serde(default, skip_serializing_if = "Option::is_none")]
77    pub max_value: Option<f64>,
78    #[serde(default, skip_serializing_if = "Option::is_none")]
79    pub regex: Option<String>,
80    #[serde(default, skip_serializing_if = "Option::is_none")]
81    pub max_bytes: Option<usize>,
82    #[serde(default, skip_serializing_if = "Option::is_none")]
83    pub max_tokens: Option<usize>,
84    #[serde(default, skip_serializing_if = "Option::is_none")]
85    pub mime_type: Option<String>,
86}
87
88#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
89#[serde(rename_all = "snake_case")]
90pub enum LeverActor {
91    Optimizer,
92    Human,
93    System,
94}
95
96#[derive(Clone, Debug, Serialize, Deserialize)]
97pub struct LeverProvenance {
98    pub actor: LeverActor,
99    pub reason: String,
100    #[serde(default, skip_serializing_if = "Option::is_none")]
101    pub source_event_id: Option<String>,
102    #[serde(default, skip_serializing_if = "Option::is_none")]
103    pub source_trace_id: Option<String>,
104}
105
106#[derive(Clone, Debug, Serialize, Deserialize)]
107pub struct Lever {
108    pub lever_id: String,
109    pub kind: LeverKind,
110    pub scope: Vec<ScopeKey>,
111    pub value: Value,
112    pub value_format: LeverFormat,
113    #[serde(default, skip_serializing_if = "Option::is_none")]
114    pub constraints: Option<LeverConstraints>,
115    pub mutability: LeverMutability,
116    #[serde(default, skip_serializing_if = "Option::is_none")]
117    pub provenance: Option<LeverProvenance>,
118    #[serde(default)]
119    pub version: i64,
120    #[serde(default, skip_serializing_if = "Option::is_none")]
121    pub created_at: Option<DateTime<Utc>>,
122    #[serde(default, skip_serializing_if = "Option::is_none")]
123    pub parent_version: Option<i64>,
124}
125
126#[derive(Clone, Debug, Serialize, Deserialize)]
127pub struct LeverSnapshot {
128    pub lever_id: String,
129    pub resolved_scope: Vec<ScopeKey>,
130    pub version: i64,
131    pub value: Value,
132    #[serde(default, skip_serializing_if = "Option::is_none")]
133    pub applied_at: Option<DateTime<Utc>>,
134}
135
136#[derive(Clone, Debug, Serialize, Deserialize)]
137pub struct LeverMutation {
138    pub lever_id: String,
139    pub parent_version: i64,
140    pub new_version: i64,
141    pub mutation_type: String,
142    #[serde(default)]
143    pub delta: Value,
144    #[serde(default, skip_serializing_if = "Option::is_none")]
145    pub created_at: Option<DateTime<Utc>>,
146    #[serde(default)]
147    pub optimizer_id: String,
148}
149
150/// MIPRO-specific lever summary payload (as returned in prompt-learning results).
151#[derive(Clone, Debug, Serialize, Deserialize)]
152pub struct MiproLeverSummary {
153    #[serde(default, skip_serializing_if = "Option::is_none")]
154    pub prompt_lever_id: Option<String>,
155    #[serde(default)]
156    pub candidate_lever_versions: HashMap<String, i64>,
157    #[serde(default, skip_serializing_if = "Option::is_none")]
158    pub best_candidate_id: Option<String>,
159    #[serde(default, skip_serializing_if = "Option::is_none")]
160    pub selected_candidate_id: Option<String>,
161    #[serde(default)]
162    pub baseline_candidate_id: String,
163    #[serde(default)]
164    pub lever_count: usize,
165    #[serde(default)]
166    pub mutation_count: usize,
167    #[serde(default)]
168    pub latest_version: i64,
169}