1use everruns_provider::{ToolCall, ToolDefinition};
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct ChatMessage {
12 pub role: MessageRole,
13 pub content: String,
14 #[serde(skip_serializing_if = "Option::is_none")]
16 pub tool_calls: Option<Vec<ToolCall>>,
17 #[serde(skip_serializing_if = "Option::is_none")]
19 pub tool_call_id: Option<String>,
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
24#[serde(rename_all = "lowercase")]
25pub enum MessageRole {
26 System,
27 User,
28 Assistant,
29 Tool,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct LlmConfig {
35 pub model: String,
37 pub temperature: Option<f32>,
39 pub max_tokens: Option<u32>,
41 pub system_prompt: Option<String>,
43 #[serde(default)]
45 pub tools: Vec<ToolDefinition>,
46}
47
48#[derive(Debug, Clone)]
50pub enum LlmStreamEvent {
51 TextDelta(String),
53 ToolCalls(Vec<ToolCall>),
55 Done(CompletionMetadata),
57 Error(String),
59}
60
61#[derive(Debug, Clone, Default, Serialize, Deserialize)]
63pub struct CompletionMetadata {
64 pub total_tokens: Option<u32>,
66 pub prompt_tokens: Option<u32>,
68 pub completion_tokens: Option<u32>,
70 pub model: String,
72 pub finish_reason: Option<String>,
74}
75
76#[derive(Debug, Clone, Serialize)]
78pub struct ChatRequest {
79 pub model: String,
80 pub messages: Vec<OpenAiMessage>,
81 #[serde(skip_serializing_if = "Option::is_none")]
82 pub temperature: Option<f32>,
83 #[serde(skip_serializing_if = "Option::is_none")]
84 pub max_tokens: Option<u32>,
85 pub stream: bool,
86 #[serde(skip_serializing_if = "Option::is_none")]
87 pub tools: Option<Vec<OpenAiTool>>,
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize)]
95pub struct OpenAiMessage {
96 pub role: String,
97 #[serde(skip_serializing_if = "Option::is_none")]
98 pub content: Option<String>,
99 #[serde(skip_serializing_if = "Option::is_none")]
100 pub tool_calls: Option<Vec<OpenAiToolCall>>,
101 #[serde(skip_serializing_if = "Option::is_none")]
102 pub tool_call_id: Option<String>,
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct OpenAiTool {
107 pub r#type: String,
108 pub function: OpenAiFunction,
109}
110
111#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct OpenAiFunction {
113 pub name: String,
114 pub description: String,
115 pub parameters: serde_json::Value,
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct OpenAiToolCall {
120 pub id: String,
121 pub r#type: String,
122 pub function: OpenAiFunctionCall,
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct OpenAiFunctionCall {
127 pub name: String,
128 pub arguments: String,
129}
130
131impl ChatMessage {
136 pub fn to_openai(&self) -> OpenAiMessage {
138 let role = match self.role {
139 MessageRole::System => "system",
140 MessageRole::User => "user",
141 MessageRole::Assistant => "assistant",
142 MessageRole::Tool => "tool",
143 };
144
145 OpenAiMessage {
146 role: role.to_string(),
147 content: Some(self.content.clone()),
148 tool_calls: self.tool_calls.as_ref().map(|calls| {
149 calls
150 .iter()
151 .map(|tc| OpenAiToolCall {
152 id: tc.id.clone(),
153 r#type: "function".to_string(),
154 function: OpenAiFunctionCall {
155 name: tc.name.clone(),
156 arguments: serde_json::to_string(&tc.arguments).unwrap_or_default(),
157 },
158 })
159 .collect()
160 }),
161 tool_call_id: self.tool_call_id.clone(),
162 }
163 }
164}
165
166impl LlmConfig {
167 pub fn tools_to_openai(&self) -> Vec<OpenAiTool> {
169 self.tools
170 .iter()
171 .map(|tool| {
172 let (name, description, parameters) = match tool {
173 ToolDefinition::Builtin(builtin) => {
174 (&builtin.name, &builtin.description, &builtin.parameters)
175 }
176 ToolDefinition::ClientSide(client) => {
177 (&client.name, &client.description, &client.parameters)
178 }
179 };
180
181 OpenAiTool {
182 r#type: "function".to_string(),
183 function: OpenAiFunction {
184 name: name.clone(),
185 description: description.clone(),
186 parameters: parameters.clone(),
187 },
188 }
189 })
190 .collect()
191 }
192}
193
194#[derive(Debug, Clone, Deserialize)]
200pub struct OpenAiModelsResponse {
201 pub data: Vec<OpenAiModelInfo>,
202}
203
204#[derive(Debug, Clone, Deserialize)]
206pub struct OpenAiModelInfo {
207 pub id: String,
209 pub created: i64,
211 pub owned_by: String,
213}
214
215impl OpenAiModelInfo {
216 pub fn is_chat_model(&self) -> bool {
221 let id = self.id.as_str();
222
223 if id.starts_with("text-embedding")
225 || id.starts_with("dall-e")
226 || id.starts_with("tts-")
227 || id.starts_with("whisper")
228 || id.starts_with("davinci")
229 || id.starts_with("babbage")
230 || id.starts_with("omni-moderation")
231 || id.starts_with("sora-")
232 || id.starts_with("gpt-image")
233 || id.starts_with("codex-")
234 || id.contains("-transcribe")
235 || id.contains("-realtime")
236 || id.contains("-audio")
237 || id.contains("-tts")
238 {
239 return false;
240 }
241
242 id.starts_with("gpt-")
244 || id.starts_with("o1")
245 || id.starts_with("o3")
246 || id.starts_with("o4")
247 || id.starts_with("chatgpt-")
248 }
249}