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#[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}