synaps_cli/runtime/openai/
types.rs1use serde::ser::SerializeStruct;
7use serde::{Deserialize, Serialize, Serializer};
8use serde_json::Value;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct ToolDefinition {
14 #[serde(rename = "type")]
15 pub kind: String,
16 pub function: FunctionDefinition,
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct FunctionDefinition {
21 pub name: String,
22 #[serde(skip_serializing_if = "Option::is_none")]
23 pub description: Option<String>,
24 pub parameters: Value,
25}
26
27impl ToolDefinition {
28 pub fn function(
29 name: impl Into<String>,
30 description: impl Into<String>,
31 parameters: Value,
32 ) -> Self {
33 Self {
34 kind: "function".to_string(),
35 function: FunctionDefinition {
36 name: name.into(),
37 description: Some(description.into()),
38 parameters,
39 },
40 }
41 }
42}
43
44#[derive(Debug, Clone, PartialEq, Eq)]
47pub enum ToolChoice {
48 None,
49 Auto,
50 Required,
51 Function(String),
52}
53
54impl Serialize for ToolChoice {
55 fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
56 match self {
57 ToolChoice::None => ser.serialize_str("none"),
58 ToolChoice::Auto => ser.serialize_str("auto"),
59 ToolChoice::Required => ser.serialize_str("required"),
60 ToolChoice::Function(name) => {
61 #[derive(Serialize)]
62 struct Named<'a> {
63 name: &'a str,
64 }
65 let mut s = ser.serialize_struct("ToolChoice", 2)?;
66 s.serialize_field("type", "function")?;
67 s.serialize_field("function", &Named { name })?;
68 s.end()
69 }
70 }
71 }
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
77pub struct ToolCall {
78 pub id: String,
79 #[serde(rename = "type")]
80 pub kind: String,
81 pub function: FunctionCall,
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
85pub struct FunctionCall {
86 pub name: String,
87 pub arguments: String,
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
95pub struct ChatMessage {
96 pub role: String,
97
98 pub content: Option<String>,
100
101 #[serde(skip_serializing_if = "Option::is_none")]
102 pub tool_calls: Option<Vec<ToolCall>>,
103
104 #[serde(skip_serializing_if = "Option::is_none")]
105 pub tool_call_id: Option<String>,
106
107 #[serde(skip_serializing_if = "Option::is_none")]
108 pub name: Option<String>,
109}
110
111impl ChatMessage {
112 pub fn user(content: impl Into<String>) -> Self {
113 Self {
114 role: "user".into(),
115 content: Some(content.into()),
116 tool_calls: None,
117 tool_call_id: None,
118 name: None,
119 }
120 }
121 pub fn system(content: impl Into<String>) -> Self {
122 Self {
123 role: "system".into(),
124 content: Some(content.into()),
125 tool_calls: None,
126 tool_call_id: None,
127 name: None,
128 }
129 }
130 pub fn assistant(content: impl Into<String>) -> Self {
131 Self {
132 role: "assistant".into(),
133 content: Some(content.into()),
134 tool_calls: None,
135 tool_call_id: None,
136 name: None,
137 }
138 }
139 pub fn assistant_tool_calls(tool_calls: Vec<ToolCall>) -> Self {
140 Self {
141 role: "assistant".into(),
142 content: None,
143 tool_calls: Some(tool_calls),
144 tool_call_id: None,
145 name: None,
146 }
147 }
148 pub fn tool_result(
149 tool_call_id: impl Into<String>,
150 name: impl Into<String>,
151 content: impl Into<String>,
152 ) -> Self {
153 Self {
154 role: "tool".into(),
155 content: Some(content.into()),
156 tool_calls: None,
157 tool_call_id: Some(tool_call_id.into()),
158 name: Some(name.into()),
159 }
160 }
161
162 pub fn content(&self) -> Option<&str> {
163 self.content.as_deref()
164 }
165}
166
167#[derive(Debug, Clone, Default)]
170pub struct ChatOptions {
171 pub max_tokens: Option<u32>,
172 pub temperature: Option<f32>,
173 pub tools: Option<Vec<ToolDefinition>>,
174 pub tool_choice: Option<ToolChoice>,
175}
176
177#[derive(Debug, Clone, Serialize)]
178pub struct StreamOptions {
179 pub include_usage: bool,
180}
181
182#[derive(Debug, Clone, Serialize)]
183pub struct ChatRequest {
184 pub model: String,
185 pub messages: Vec<ChatMessage>,
186 pub stream: bool,
187 #[serde(skip_serializing_if = "Option::is_none")]
188 pub stream_options: Option<StreamOptions>,
189 #[serde(skip_serializing_if = "Option::is_none")]
190 pub max_tokens: Option<u32>,
191 #[serde(skip_serializing_if = "Option::is_none")]
192 pub temperature: Option<f32>,
193 #[serde(skip_serializing_if = "Option::is_none")]
194 pub tools: Option<Vec<ToolDefinition>>,
195 #[serde(skip_serializing_if = "Option::is_none")]
196 pub tool_choice: Option<ToolChoice>,
197}
198
199#[derive(Debug, Clone, Copy, PartialEq, Eq)]
202pub enum FinishReason {
203 Stop,
204 Length,
205 ToolCalls,
206 ContentFilter,
207}
208
209#[derive(Debug, Clone)]
212#[non_exhaustive]
213pub enum OaiEvent {
214 RoleStart(String),
215 TextDelta(String),
216 ToolCallStart {
217 index: u32,
218 id: String,
219 name: String,
220 },
221 ToolCallArgumentsDelta {
222 index: u32,
223 id: String,
224 delta: String,
225 },
226 ToolCallsComplete {
227 calls: Vec<ToolCall>,
228 truncated: bool,
229 },
230 Usage {
231 prompt_tokens: u32,
232 completion_tokens: u32,
233 cached_tokens: u32,
234 },
235 Warning(String),
236 Done,
237}
238
239#[derive(Clone)]
242pub struct ProviderConfig {
243 pub base_url: String,
244 pub api_key: String,
245 pub model: String,
246 pub provider: String,
247}
248
249impl std::fmt::Debug for ProviderConfig {
250 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
251 f.debug_struct("ProviderConfig")
252 .field("base_url", &self.base_url)
253 .field("api_key", &"[REDACTED]")
254 .field("model", &self.model)
255 .finish()
256 }
257}