Skip to main content

lash_remote_protocol/protocol/
turn_input.rs

1#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
2pub struct RemoteProtocolTurnOptions {
3    #[serde(default = "empty_protocol_turn_payload")]
4    pub payload: serde_json::Value,
5}
6
7fn empty_protocol_turn_payload() -> serde_json::Value {
8    serde_json::Value::Object(serde_json::Map::new())
9}
10
11impl Default for RemoteProtocolTurnOptions {
12    fn default() -> Self {
13        Self {
14            payload: empty_protocol_turn_payload(),
15        }
16    }
17}
18
19impl RemoteProtocolTurnOptions {
20    pub fn empty() -> Self {
21        Self::default()
22    }
23
24    pub fn is_empty(&self) -> bool {
25        match &self.payload {
26            serde_json::Value::Object(map) => map.is_empty(),
27            _ => false,
28        }
29    }
30}
31
32#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
33pub struct RemoteTurnInput {
34    pub protocol_version: u32,
35    #[serde(default)]
36    pub items: Vec<RemoteInputItem>,
37    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
38    pub image_blobs_base64: HashMap<String, String>,
39    #[serde(default, skip_serializing_if = "Option::is_none")]
40    pub protocol_turn_options: Option<RemoteProtocolTurnOptions>,
41    #[serde(default, skip_serializing_if = "Option::is_none")]
42    pub trace_turn_id: Option<String>,
43    #[serde(default, skip_serializing_if = "Option::is_none")]
44    pub prompt_layer: Option<RemotePromptLayer>,
45}
46
47impl RemoteTurnInput {
48    pub fn text(text: impl Into<String>) -> Self {
49        Self {
50            protocol_version: REMOTE_PROTOCOL_VERSION,
51            items: vec![RemoteInputItem::Text { text: text.into() }],
52            image_blobs_base64: HashMap::new(),
53            protocol_turn_options: None,
54            trace_turn_id: None,
55            prompt_layer: None,
56        }
57    }
58
59    pub fn validate(&self) -> Result<(), RemoteProtocolError> {
60        ensure_protocol_version(self.protocol_version)?;
61        for item in &self.items {
62            if let RemoteInputItem::ImageRef { id } = item {
63                require_non_empty("RemoteInputItem::ImageRef", "id", id)?;
64            }
65        }
66        for (id, blob) in &self.image_blobs_base64 {
67            require_non_empty("RemoteTurnInput", "image_blobs_base64 key", id)?;
68            if blob.trim().is_empty() {
69                return Err(RemoteProtocolError::InvalidImageBlob {
70                    id: id.clone(),
71                    message: "base64 payload cannot be empty".to_string(),
72                });
73            }
74        }
75        Ok(())
76    }
77}
78
79#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
80#[serde(tag = "type", rename_all = "snake_case")]
81pub enum RemoteInputItem {
82    Text { text: String },
83    ImageRef { id: String },
84}
85
86#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
87pub struct RemoteTurnRequest {
88    pub protocol_version: u32,
89    pub session_id: String,
90    pub turn_id: String,
91    #[serde(default, skip_serializing_if = "Option::is_none")]
92    pub idempotency_key: Option<String>,
93    pub input: RemoteTurnInput,
94    #[serde(default, skip_serializing_if = "Vec::is_empty")]
95    pub tool_grants: Vec<RemoteToolGrant>,
96    #[serde(default, skip_serializing_if = "Option::is_none")]
97    pub model_intent: Option<RemoteModelIntent>,
98    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
99    pub metadata: HashMap<String, serde_json::Value>,
100}
101
102impl RemoteTurnRequest {
103    pub fn validate(&self) -> Result<(), RemoteProtocolError> {
104        ensure_protocol_version(self.protocol_version)?;
105        require_non_empty("RemoteTurnRequest", "session_id", &self.session_id)?;
106        require_non_empty("RemoteTurnRequest", "turn_id", &self.turn_id)?;
107        if self.input.protocol_version != self.protocol_version {
108            return Err(RemoteProtocolError::MismatchedNestedProtocolVersion {
109                parent: "RemoteTurnRequest",
110                child: "input",
111                parent_version: self.protocol_version,
112                child_version: self.input.protocol_version,
113            });
114        }
115        self.input.validate()?;
116        RemoteToolGrant::validate_all(&self.tool_grants)?;
117        if let Some(model_intent) = &self.model_intent {
118            model_intent.validate()?;
119        }
120        Ok(())
121    }
122}
123
124#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
125pub struct RemoteTurnResult {
126    pub protocol_version: u32,
127    pub session_id: String,
128    pub turn_id: String,
129    pub status: RemoteTurnStatus,
130    pub outcome: RemoteTurnOutcome,
131    pub assistant_output: RemoteAssistantOutput,
132    #[serde(default)]
133    pub usage: RemoteTurnUsageSummary,
134    #[serde(default)]
135    pub execution: RemoteExecutionSummary,
136    #[serde(default, skip_serializing_if = "Vec::is_empty")]
137    pub tool_calls: Vec<RemoteToolCallSummary>,
138    #[serde(default, skip_serializing_if = "Vec::is_empty")]
139    pub issues: Vec<RemoteTurnIssue>,
140    #[serde(default, skip_serializing_if = "Vec::is_empty")]
141    pub activities: Vec<RemoteTurnActivity>,
142    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
143    pub metadata: HashMap<String, serde_json::Value>,
144}