1use anyhow::Result;
2use async_trait::async_trait;
3use futures::stream::BoxStream;
4use serde::{Deserialize, Serialize};
5
6use crate::message::{ChatResponse, Message, StreamChunk};
7use crate::tool::Tool;
8
9#[async_trait]
11pub trait Provider: Send + Sync {
12 fn name(&self) -> &str;
14
15 fn max_output_tokens(&self) -> Option<u32> {
18 None }
20
21 async fn chat(
23 &self,
24 messages: &[Message],
25 tools: Option<&[Tool]>,
26 options: &ChatOptions,
27 ) -> Result<ChatResponse>;
28
29 fn stream_chat<'a>(
31 &'a self,
32 messages: &'a [Message],
33 tools: Option<&'a [Tool]>,
34 options: &'a ChatOptions,
35 ) -> BoxStream<'a, Result<StreamChunk>>;
36}
37
38#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
46#[serde(rename_all = "snake_case")]
47pub enum CacheStrategy {
48 Off,
50 SystemOnly,
52 #[default]
54 SystemAndTools,
55 SystemAndTailTurn {
58 threshold_tokens: u32,
61 },
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct ChatOptions {
67 #[serde(skip_serializing_if = "Option::is_none")]
69 pub temperature: Option<f32>,
70 #[serde(skip_serializing_if = "Option::is_none")]
72 pub max_tokens: Option<u32>,
73 #[serde(skip_serializing_if = "Option::is_none")]
75 pub top_p: Option<f32>,
76 #[serde(skip_serializing_if = "Option::is_none")]
78 pub stop: Option<Vec<String>>,
79 #[serde(skip_serializing_if = "Option::is_none")]
81 pub system: Option<String>,
82 #[serde(skip_serializing_if = "Option::is_none")]
87 pub model: Option<String>,
88 #[serde(default)]
90 pub cache_strategy: CacheStrategy,
91 #[serde(default, skip_serializing_if = "Option::is_none")]
97 pub request_id: Option<String>,
98 #[serde(skip)]
106 pub cancel: Option<tokio_util::sync::CancellationToken>,
107}
108
109impl Default for ChatOptions {
110 fn default() -> Self {
111 Self {
112 temperature: Some(0.7),
113 max_tokens: Some(4096),
114 top_p: None,
115 stop: None,
116 system: None,
117 model: None,
118 cache_strategy: CacheStrategy::default(),
119 request_id: None,
120 cancel: None,
121 }
122 }
123}
124
125impl ChatOptions {
126 pub fn new() -> Self {
128 Self::default()
129 }
130
131 pub fn temperature(mut self, temperature: f32) -> Self {
133 self.temperature = Some(temperature);
134 self
135 }
136
137 pub fn max_tokens(mut self, max_tokens: u32) -> Self {
139 self.max_tokens = Some(max_tokens);
140 self
141 }
142
143 pub fn system<S: Into<String>>(mut self, system: S) -> Self {
145 self.system = Some(system.into());
146 self
147 }
148
149 pub fn top_p(mut self, top_p: f32) -> Self {
151 self.top_p = Some(top_p);
152 self
153 }
154
155 pub fn model<S: Into<String>>(mut self, model: S) -> Self {
157 self.model = Some(model.into());
158 self
159 }
160
161 pub fn cache_strategy(mut self, strategy: CacheStrategy) -> Self {
163 self.cache_strategy = strategy;
164 self
165 }
166
167 pub fn request_id<S: Into<String>>(mut self, request_id: S) -> Self {
170 self.request_id = Some(request_id.into());
171 self
172 }
173
174 pub fn cancel_with(mut self, token: tokio_util::sync::CancellationToken) -> Self {
178 self.cancel = Some(token);
179 self
180 }
181
182 pub fn deterministic(max_tokens: u32) -> Self {
184 Self {
185 temperature: Some(0.0),
186 max_tokens: Some(max_tokens),
187 ..Default::default()
188 }
189 }
190
191 pub fn factual(max_tokens: u32) -> Self {
193 Self {
194 temperature: Some(0.1),
195 max_tokens: Some(max_tokens),
196 top_p: Some(0.9),
197 ..Default::default()
198 }
199 }
200
201 pub fn creative(max_tokens: u32) -> Self {
203 Self {
204 temperature: Some(0.3),
205 max_tokens: Some(max_tokens),
206 ..Default::default()
207 }
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214
215 #[test]
216 fn test_chat_options_default() {
217 let opts = ChatOptions::default();
218 assert_eq!(opts.temperature, Some(0.7));
219 assert_eq!(opts.max_tokens, Some(4096));
220 }
221
222 #[test]
223 fn test_chat_options_builder() {
224 let opts = ChatOptions::new()
225 .temperature(0.5)
226 .max_tokens(2048)
227 .system("Test");
228 assert_eq!(opts.temperature, Some(0.5));
229 assert_eq!(opts.max_tokens, Some(2048));
230 assert_eq!(opts.system, Some("Test".to_string()));
231 }
232
233 #[test]
234 fn test_chat_options_deterministic() {
235 let opts = ChatOptions::deterministic(50);
236 assert_eq!(opts.temperature, Some(0.0));
237 assert_eq!(opts.max_tokens, Some(50));
238 }
239
240 #[test]
241 fn test_chat_options_factual() {
242 let opts = ChatOptions::factual(200);
243 assert_eq!(opts.temperature, Some(0.1));
244 assert_eq!(opts.max_tokens, Some(200));
245 assert_eq!(opts.top_p, Some(0.9));
246 }
247
248 #[test]
249 fn test_chat_options_creative() {
250 let opts = ChatOptions::creative(400);
251 assert_eq!(opts.temperature, Some(0.3));
252 assert_eq!(opts.max_tokens, Some(400));
253 }
254}