1use std::pin::Pin;
4
5use crate::parser::{GenericStreamParser, StreamEventExt};
6use crate::types::Thinking;
7use crate::Result;
8use bytes::Bytes;
9use futures::Stream;
10use ollama_sdk_macros::FromBytes;
11use serde::{Deserialize, Serialize};
12
13use super::{Role, ThinkingLevel};
14
15#[derive(Serialize, Default, Debug, Clone)]
21pub struct ChatRequest {
22 pub model: String,
24 pub messages: Vec<ChatRequestMessage>,
26 #[serde(skip_serializing_if = "Option::is_none")]
29 pub stream: Option<bool>,
30 #[serde(skip_serializing_if = "Option::is_none")]
32 pub tools: Option<Vec<ToolSpec>>,
33 #[serde(default)]
35 pub think: Thinking,
36}
37
38#[derive(Deserialize, Serialize, Debug, Clone)]
40#[serde(untagged)]
41pub enum ChatRequestMessage {
42 Message(RegularChatRequestMessage),
44 ToolCallResult(ToolCallResultMessage),
46}
47
48#[derive(Deserialize, Serialize, Debug, Clone)]
50pub struct RegularChatRequestMessage {
51 pub role: Role,
53 pub content: String,
55 #[serde(default)]
57 pub tool_calls: Vec<FunctionalTool>,
58}
59
60impl RegularChatRequestMessage {
61 pub fn new(role: Role, content: String) -> Self {
63 Self {
64 role,
65 content,
66 tool_calls: Vec::new(),
67 }
68 }
69
70 pub fn add_tool_call(mut self, tool: FunctionalTool) -> Self {
72 self.tool_calls.push(tool);
73 self
74 }
75
76 pub fn to_chat_request_message(self) -> ChatRequestMessage {
78 ChatRequestMessage::Message(self)
79 }
80}
81
82#[derive(Deserialize, Serialize, Debug, Clone)]
84pub struct ToolCallResultMessage {
85 pub role: Role,
87 pub name: String,
89 pub content: String,
91 pub tool_call_id: String,
93}
94
95impl ToolCallResultMessage {
96 pub fn new(name: String, content: String, tool_call_id: String) -> Self {
98 Self {
99 role: Role::Tool,
100 name,
101 content,
102 tool_call_id,
103 }
104 }
105
106 pub fn to_chat_request_message(self) -> ChatRequestMessage {
108 ChatRequestMessage::ToolCallResult(self)
109 }
110}
111
112#[derive(Serialize, Debug, Clone)]
114#[serde(tag = "type")]
115pub enum ToolSpec {
116 Function { function: FunctionalTool },
118}
119
120#[derive(Deserialize, Serialize, Debug, Clone)]
122pub struct FunctionalTool {
123 pub name: String,
125 #[serde(skip_serializing_if = "Option::is_none")]
127 pub description: Option<String>,
128 pub parameters: serde_json::Value,
130}
131
132#[derive(Deserialize, Serialize, Default, FromBytes, Debug, Clone)]
136pub struct ChatResponse {
137 pub model: String,
139 #[serde(default)]
141 pub created_at: String,
142 pub message: ChatResponseMessage,
144 pub done: bool,
146}
147
148#[derive(Deserialize, Serialize, Default, Debug, Clone)]
150pub struct ChatResponseMessage {
151 pub role: Role,
153 pub content: String,
155 #[serde(default)]
157 pub thinking: String,
158 #[serde(default)]
160 pub tool_calls: Vec<ToolCall>,
161}
162
163#[derive(Deserialize, Serialize, Debug, Clone)]
165pub struct ToolCall {
166 pub id: String,
168 pub function: FunctionInvocation,
170}
171
172#[derive(Deserialize, Serialize, Debug, Clone)]
174pub struct FunctionInvocation {
175 pub index: Option<usize>,
177 pub name: String,
179 pub arguments: serde_json::Value,
181}
182
183#[derive(Serialize, Default, Debug, Clone)]
188pub struct SimpleChatRequest {
189 pub model: String,
191 pub messages: Vec<ChatRequestMessage>,
193 pub think: Thinking,
195}
196
197impl SimpleChatRequest {
198 pub fn new(model: String) -> Self {
200 Self {
201 model,
202 messages: Vec::new(),
203 think: Thinking::default(),
204 }
205 }
206
207 pub fn add_message(mut self, message: RegularChatRequestMessage) -> Self {
209 self.messages.push(ChatRequestMessage::Message(message));
210 self
211 }
212
213 pub fn add_tool_call_result(mut self, message: ToolCallResultMessage) -> Self {
215 self.messages
216 .push(ChatRequestMessage::ToolCallResult(message));
217 self
218 }
219
220 pub fn enable_thinking(mut self) -> Self {
222 self.think = Thinking::Boolean(true);
223 self
224 }
225
226 pub fn disable_thinking(mut self) -> Self {
228 self.think = Thinking::Boolean(false);
229 self
230 }
231
232 pub fn set_thinking_level(mut self, level: ThinkingLevel) -> Self {
234 self.think = Thinking::Level(level);
235 self
236 }
237}
238
239#[derive(Serialize, Default, Debug, Clone)]
244pub struct StreamingChatRequest {
245 pub model: String,
247 pub messages: Vec<ChatRequestMessage>,
249 #[serde(skip_serializing_if = "Option::is_none")]
251 pub tools: Option<Vec<ToolSpec>>,
252 pub think: Thinking,
254}
255
256impl StreamingChatRequest {
257 pub fn new(model: String) -> Self {
259 Self {
260 model,
261 messages: Vec::new(),
262 tools: None,
263 think: Thinking::default(),
264 }
265 }
266
267 pub fn add_message(mut self, message: ChatRequestMessage) -> Self {
269 self.messages.push(message);
270 self
271 }
272
273 pub fn add_regular_message(mut self, message: RegularChatRequestMessage) -> Self {
275 self.messages.push(ChatRequestMessage::Message(message));
276 self
277 }
278
279 pub fn add_tool_call_result(mut self, message: ToolCallResultMessage) -> Self {
281 self.messages
282 .push(ChatRequestMessage::ToolCallResult(message));
283 self
284 }
285
286 pub fn enable_thinking(mut self) -> Self {
288 self.think = Thinking::Boolean(true);
289 self
290 }
291
292 pub fn disable_thinking(mut self) -> Self {
294 self.think = Thinking::Boolean(false);
295 self
296 }
297
298 pub fn set_thinking_level(mut self, level: ThinkingLevel) -> Self {
300 self.think = Thinking::Level(level);
301 self
302 }
303
304 pub fn tools(mut self, tools: Vec<ToolSpec>) -> Self {
306 self.tools = Some(tools);
307 self
308 }
309}
310
311impl From<SimpleChatRequest> for ChatRequest {
312 fn from(value: SimpleChatRequest) -> Self {
314 ChatRequest {
315 model: value.model,
316 messages: value.messages,
317 stream: Some(false),
318 think: value.think,
319 tools: None,
320 }
321 }
322}
323
324impl From<StreamingChatRequest> for ChatRequest {
325 fn from(value: StreamingChatRequest) -> Self {
327 ChatRequest {
328 model: value.model,
329 messages: value.messages,
330 stream: Some(true),
331 think: value.think,
332 tools: value.tools,
333 }
334 }
335}
336
337#[derive(Deserialize, Serialize, Debug)]
339pub enum ChatStreamEvent {
340 Message(ChatResponse),
342 Error(String),
344 Partial {
346 partial: String,
348 error: Option<String>,
350 },
351}
352
353pub struct ChatStream {
355 pub inner: Pin<Box<dyn Stream<Item = Result<ChatStreamEvent>> + Send>>,
356}
357
358impl Stream for ChatStream {
359 type Item = Result<ChatStreamEvent>;
360
361 fn poll_next(
362 mut self: std::pin::Pin<&mut Self>,
363 cx: &mut std::task::Context<'_>,
364 ) -> std::task::Poll<Option<Self::Item>> {
365 self.inner.as_mut().poll_next(cx)
366 }
367}
368
369impl ChatStream {
370 pub fn from_bytes_stream<S>(stream: S) -> Self
371 where
372 S: Stream<Item = Result<Bytes>> + Send + Unpin + 'static,
373 {
374 let parser = GenericStreamParser::<S, ChatResponse, ChatStreamEvent>::new(stream);
375 ChatStream {
376 inner: Box::pin(parser),
377 }
378 }
379}
380
381impl StreamEventExt<ChatResponse> for ChatStreamEvent {
382 fn from_message(msg: ChatResponse) -> Self {
383 ChatStreamEvent::Message(msg)
384 }
385
386 fn from_error(err: String) -> Self {
387 ChatStreamEvent::Error(err)
388 }
389
390 fn partial(partial: String, error: Option<String>) -> Self {
391 ChatStreamEvent::Partial { partial, error }
392 }
393}