agent_evaluate_v2_contract/
execution.rs1use std::collections::BTreeMap;
4
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
12#[serde(rename_all = "snake_case")]
13pub enum ExecutionMode {
14 InSandbox,
16 OutSandbox,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
21#[serde(rename_all = "snake_case")]
22pub enum SpaceAccessMode {
23 ReadOnly,
24 ReadWrite,
25}
26
27#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
28#[serde(rename_all = "snake_case")]
29pub enum SandboxPlacement {
30 Computer(String),
31 ProviderPool(String),
32}
33
34#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
35#[serde(rename_all = "camelCase", deny_unknown_fields)]
36pub struct SandboxMountSpec {
37 pub alias: String,
38 pub space_id: String,
39 pub mode: SpaceAccessMode,
40 #[serde(default, skip_serializing_if = "Option::is_none")]
41 pub path_prefix: Option<String>,
42}
43
44#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
47#[serde(rename_all = "camelCase", deny_unknown_fields)]
48pub struct WorkspaceSpec {
49 pub provider: String,
50 pub placement: SandboxPlacement,
51 #[serde(default, skip_serializing_if = "Option::is_none")]
52 pub template_id: Option<String>,
53 #[serde(default, skip_serializing_if = "Option::is_none")]
54 pub template_version: Option<String>,
55 #[serde(default, skip_serializing_if = "Option::is_none")]
58 pub image_snapshot_id: Option<String>,
59 #[serde(default)]
60 pub mounts: Vec<SandboxMountSpec>,
61 pub ttl_ms: u64,
62 #[serde(default)]
63 pub profile: BTreeMap<String, String>,
64}
65
66#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
69#[serde(rename_all = "camelCase", deny_unknown_fields)]
70pub struct AgentSpec {
71 pub entrypoint: String,
72 #[serde(default)]
73 pub env: BTreeMap<String, String>,
74}
75
76#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
77#[serde(rename_all = "camelCase", deny_unknown_fields)]
78pub struct ExecutionSpec {
79 pub mode: ExecutionMode,
80 pub workspace: WorkspaceSpec,
81 #[serde(default, skip_serializing_if = "Option::is_none")]
82 pub agent: Option<AgentSpec>,
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
89#[serde(rename_all = "snake_case")]
90pub enum TraceLevel {
91 Driver,
94 Runtime,
97}
98
99impl TraceLevel {
100 pub const fn satisfies(self, required: Self) -> bool {
102 (self as u8) >= (required as u8)
103 }
104
105 pub const fn as_str(self) -> &'static str {
106 match self {
107 Self::Driver => "driver",
108 Self::Runtime => "runtime",
109 }
110 }
111}
112
113#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
114#[serde(rename_all = "camelCase", deny_unknown_fields)]
115pub struct EvalTurn {
116 pub user: String,
117}
118
119#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
120#[serde(rename_all = "camelCase", deny_unknown_fields)]
121pub struct VerifyCommand {
122 pub name: String,
123 pub command: String,
124 #[serde(default)]
125 pub expected_exit_code: i32,
126}
127
128#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
129#[serde(rename_all = "camelCase", deny_unknown_fields)]
130pub struct EvalBudget {
131 pub timeout_ms: u64,
132 pub max_output_bytes: usize,
133 pub max_cost_micros: u64,
134}
135
136impl Default for EvalBudget {
137 fn default() -> Self {
138 Self {
139 timeout_ms: 300_000,
140 max_output_bytes: 8 * 1024 * 1024,
141 max_cost_micros: 0,
142 }
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use super::TraceLevel;
149
150 #[test]
151 fn runtime_trace_satisfies_driver_requirement_but_not_the_reverse() {
152 assert!(TraceLevel::Runtime.satisfies(TraceLevel::Driver));
153 assert!(TraceLevel::Runtime.satisfies(TraceLevel::Runtime));
154 assert!(TraceLevel::Driver.satisfies(TraceLevel::Driver));
155 assert!(!TraceLevel::Driver.satisfies(TraceLevel::Runtime));
156 }
157}