Skip to main content

vv_agent/app_server/protocol/
turn.rs

1use std::collections::BTreeMap;
2
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6use ts_rs::TS;
7
8pub type UserInput = Value;
9
10#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, TS)]
11#[serde(rename_all = "camelCase")]
12pub struct TurnStartParams {
13    pub thread_id: String,
14    #[serde(default, deserialize_with = "deserialize_input_items")]
15    pub input: Vec<UserInput>,
16    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
17    pub metadata: BTreeMap<String, Value>,
18}
19
20#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]
21#[serde(rename_all = "camelCase", deny_unknown_fields)]
22pub struct TurnResumeParams {
23    pub thread_id: String,
24    pub turn_id: String,
25    pub checkpoint_key: String,
26}
27
28#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, TS)]
29#[serde(rename_all = "camelCase")]
30pub struct TurnInterruptParams {
31    pub thread_id: String,
32    #[serde(default)]
33    pub expected_turn_id: String,
34    #[serde(default, skip_serializing_if = "String::is_empty")]
35    pub reason: String,
36}
37
38#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, TS)]
39#[serde(rename_all = "camelCase")]
40pub struct TurnSteerParams {
41    pub thread_id: String,
42    #[serde(default)]
43    pub expected_turn_id: String,
44    #[serde(default, deserialize_with = "deserialize_input_items")]
45    pub input: Vec<UserInput>,
46}
47
48pub type TurnFollowUpParams = TurnSteerParams;
49
50#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]
51#[serde(rename_all = "camelCase")]
52pub struct TurnStartResponse {
53    pub thread_id: String,
54    pub turn_id: String,
55    pub status: TurnStatus,
56}
57
58#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, TS)]
59#[serde(rename_all = "camelCase", deny_unknown_fields)]
60pub struct TurnResumeResponse {
61    pub thread_id: String,
62    pub turn_id: String,
63    pub run_id: String,
64    pub status: TurnStatus,
65    #[serde(default, skip_serializing_if = "Option::is_none")]
66    pub final_output: Option<String>,
67    #[serde(default, skip_serializing_if = "Option::is_none")]
68    pub completion_reason: Option<String>,
69    #[serde(default, skip_serializing_if = "Option::is_none")]
70    pub completion_tool_name: Option<String>,
71    #[serde(default, skip_serializing_if = "Option::is_none")]
72    pub partial_output: Option<String>,
73    #[serde(default, skip_serializing_if = "Option::is_none")]
74    pub checkpoint: Option<CheckpointSummary>,
75    #[serde(default, skip_serializing_if = "Option::is_none")]
76    pub interruption: Option<InterruptionSummary>,
77    #[serde(default, skip_serializing_if = "Option::is_none")]
78    pub error: Option<String>,
79}
80
81#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]
82#[serde(rename_all = "camelCase")]
83pub struct TurnInterruptResponse {
84    pub thread_id: String,
85    pub turn_id: String,
86    pub cancelled: bool,
87}
88
89#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]
90#[serde(rename_all = "camelCase")]
91pub struct TurnControlResponse {
92    pub thread_id: String,
93    pub turn_id: String,
94    pub queued: bool,
95}
96
97pub type TurnSteerResponse = TurnControlResponse;
98pub type TurnFollowUpResponse = TurnControlResponse;
99
100#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]
101#[serde(rename_all = "camelCase")]
102pub struct TurnStartedParams {
103    pub thread_id: String,
104    pub turn_id: String,
105    #[serde(default, skip_serializing_if = "Option::is_none")]
106    pub run_id: Option<String>,
107    #[serde(default, skip_serializing_if = "Option::is_none")]
108    pub status: Option<TurnStatus>,
109}
110
111#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, TS)]
112#[serde(rename_all = "camelCase")]
113pub struct TurnCompletedParams {
114    pub thread_id: String,
115    pub turn_id: String,
116    #[serde(default, skip_serializing_if = "Option::is_none")]
117    pub run_id: Option<String>,
118    pub status: TurnStatus,
119    #[serde(default, skip_serializing_if = "Option::is_none")]
120    pub final_output: Option<String>,
121    #[serde(default, skip_serializing_if = "Option::is_none")]
122    pub completion_reason: Option<String>,
123    #[serde(default, skip_serializing_if = "Option::is_none")]
124    pub completion_tool_name: Option<String>,
125    #[serde(default, skip_serializing_if = "Option::is_none")]
126    pub partial_output: Option<String>,
127    #[serde(default, skip_serializing_if = "Option::is_none")]
128    pub error: Option<String>,
129    #[serde(default, skip_serializing_if = "Option::is_none")]
130    pub token_usage: Option<AppTokenUsage>,
131    #[serde(default, skip_serializing_if = "Option::is_none")]
132    pub budget_usage: Option<BTreeMap<String, Value>>,
133    #[serde(default, skip_serializing_if = "Option::is_none")]
134    pub budget_exhaustion: Option<BTreeMap<String, Value>>,
135    #[serde(default, skip_serializing_if = "Option::is_none")]
136    pub checkpoint: Option<CheckpointSummary>,
137    #[serde(default, skip_serializing_if = "Option::is_none")]
138    pub interruption: Option<InterruptionSummary>,
139}
140
141#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]
142#[serde(rename_all = "camelCase", deny_unknown_fields)]
143pub struct CheckpointSummary {
144    pub key: String,
145    pub resume_attempt: u64,
146    pub cycle_index: u64,
147    pub status: CheckpointSummaryStatus,
148    pub terminal_acknowledged: bool,
149}
150
151#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]
152#[serde(rename_all = "snake_case")]
153pub enum CheckpointSummaryStatus {
154    Pending,
155    Running,
156    WaitUser,
157    Completed,
158    Failed,
159    MaxCycles,
160    ReconciliationRequired,
161}
162
163#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]
164#[serde(rename_all = "camelCase", deny_unknown_fields)]
165pub struct InterruptionSummary {
166    pub reason: String,
167    pub operation_id: String,
168    pub operation_kind: InterruptionOperationKind,
169    pub cycle_index: u64,
170    pub risk: String,
171    pub idempotency_support: InterruptionIdempotencySupport,
172}
173
174#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]
175#[serde(rename_all = "snake_case")]
176pub enum InterruptionOperationKind {
177    Model,
178    Tool,
179}
180
181#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]
182#[serde(rename_all = "snake_case")]
183pub enum InterruptionIdempotencySupport {
184    Supported,
185    Unsupported,
186    Unknown,
187}
188
189#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, TS)]
190#[serde(rename_all = "camelCase")]
191pub struct AppTurn {
192    pub turn_id: String,
193    pub thread_id: String,
194    #[serde(default)]
195    pub run_id: Option<String>,
196    pub status: TurnStatus,
197    pub started_at: f64,
198    #[serde(default)]
199    pub completed_at: Option<f64>,
200    #[serde(default, deserialize_with = "deserialize_input_items")]
201    pub input: Vec<UserInput>,
202    #[serde(default)]
203    pub result: BTreeMap<String, Value>,
204}
205
206fn deserialize_input_items<'de, D>(deserializer: D) -> Result<Vec<UserInput>, D::Error>
207where
208    D: serde::Deserializer<'de>,
209{
210    let items = Vec::<Value>::deserialize(deserializer)?;
211    if items.iter().all(Value::is_object) {
212        Ok(items)
213    } else {
214        Err(serde::de::Error::custom("input must be a list of objects"))
215    }
216}
217
218#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]
219#[serde(rename_all = "camelCase")]
220pub enum TurnStatus {
221    Queued,
222    Running,
223    Completed,
224    Failed,
225    Interrupted,
226}
227
228#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]
229pub struct AppTokenUsage {
230    pub prompt_tokens: u64,
231    pub completion_tokens: u64,
232    pub total_tokens: u64,
233    pub cached_tokens: u64,
234    pub reasoning_tokens: u64,
235    pub input_tokens: u64,
236    pub output_tokens: u64,
237    pub cache_creation_tokens: u64,
238    pub cache_usage: AppCacheUsage,
239}
240
241#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]
242pub struct AppCacheUsage {
243    pub status: String,
244    pub read_tokens: Option<u64>,
245    pub write_tokens: Option<u64>,
246    pub uncached_input_tokens: Option<u64>,
247    pub source: Option<String>,
248}