systemprompt_models/wire/canonical/
response.rs1use super::request::{CanonicalContent, flatten_part};
12use super::usage::{CanonicalUsage, CanonicalUsageUpdate};
13use crate::wire::inspect::ForwardedSurface;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum CanonicalStopReason {
17 EndTurn,
18 MaxTokens,
19 StopSequence,
20 ToolUse,
21 Other,
22}
23
24impl CanonicalStopReason {
25 pub const fn anthropic_str(self) -> &'static str {
26 match self {
27 Self::MaxTokens => "max_tokens",
28 Self::StopSequence => "stop_sequence",
29 Self::ToolUse => "tool_use",
30 Self::EndTurn | Self::Other => "end_turn",
31 }
32 }
33
34 pub const fn openai_str(self) -> &'static str {
35 match self {
36 Self::MaxTokens => "length",
37 Self::ToolUse => "tool_calls",
38 Self::EndTurn | Self::StopSequence | Self::Other => "stop",
39 }
40 }
41
42 pub fn from_anthropic(s: &str) -> Self {
43 match s {
44 "end_turn" => Self::EndTurn,
45 "max_tokens" => Self::MaxTokens,
46 "stop_sequence" => Self::StopSequence,
47 "tool_use" => Self::ToolUse,
48 _ => Self::Other,
49 }
50 }
51
52 #[must_use]
55 pub const fn with_tool_use(self, has_tool_use: bool) -> Self {
56 match self {
57 Self::EndTurn | Self::Other if has_tool_use => Self::ToolUse,
58 other => other,
59 }
60 }
61
62 pub fn from_openai(s: &str) -> Self {
63 match s {
64 "stop" => Self::EndTurn,
65 "length" => Self::MaxTokens,
66 "tool_calls" | "function_call" => Self::ToolUse,
67 _ => Self::Other,
68 }
69 }
70}
71
72#[derive(Debug, Clone, Default)]
73pub struct GroundedSource {
74 pub uri: String,
75 pub title: Option<String>,
76 pub snippet: Option<String>,
77 pub relevance: Option<f32>,
78}
79
80#[derive(Debug, Clone, Default)]
81pub struct Grounding {
82 pub sources: Vec<GroundedSource>,
83 pub queries: Vec<String>,
84}
85
86#[derive(Debug, Clone, Default)]
87pub struct CodeExecutionOutput {
88 pub language: Option<String>,
89 pub code: String,
90 pub result: Option<String>,
91 pub outcome: Option<String>,
92}
93
94#[derive(Debug, Clone, Default)]
95pub struct CanonicalResponse {
96 pub id: String,
97 pub model: String,
98 pub content: Vec<CanonicalContent>,
99 pub stop_reason: Option<CanonicalStopReason>,
100 pub usage: CanonicalUsage,
101 pub grounding: Option<Grounding>,
102 pub code_execution: Option<CodeExecutionOutput>,
103 pub raw_finish_reason: Option<String>,
104 pub received_surface: ForwardedSurface,
105}
106
107impl CanonicalResponse {
108 pub fn content_units(&self) -> Vec<String> {
109 let mut units = Vec::with_capacity(self.content.len() + self.received_surface.len());
110 for part in &self.content {
111 let mut out = String::new();
112 flatten_part(&mut out, part);
113 if !out.is_empty() {
114 units.push(out);
115 }
116 }
117 for leaf in self.received_surface.leaves() {
118 units.push(leaf.value.clone());
119 }
120 units
121 }
122}
123
124#[derive(Debug, Clone)]
125pub enum CanonicalEvent {
126 MessageStart {
127 id: String,
128 model: String,
129 usage: CanonicalUsage,
130 },
131 ContentBlockStart {
132 index: u32,
133 block: ContentBlockKind,
134 },
135 TextDelta {
136 index: u32,
137 text: String,
138 },
139 ThinkingDelta {
140 index: u32,
141 text: String,
142 },
143 SignatureDelta {
144 index: u32,
145 signature: String,
146 },
147 EncryptedContentDelta {
148 index: u32,
149 data: String,
150 },
151 ToolUseDelta {
152 index: u32,
153 partial_json: String,
154 },
155 ContentBlockStop {
156 index: u32,
157 },
158 UsageDelta(CanonicalUsageUpdate),
159 MessageStop {
160 id: String,
161 stop_reason: Option<CanonicalStopReason>,
162 },
163 Error(String),
164}
165
166#[derive(Debug, Clone)]
167pub enum ContentBlockKind {
168 Text,
169 Thinking {
170 id: Option<String>,
171 signature: Option<String>,
172 },
173 ToolUse {
174 id: String,
175 name: String,
176 signature: Option<String>,
177 },
178}