1use serde::{Deserialize, Serialize};
10use std::collections::BTreeMap;
11
12#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
17#[serde(transparent)]
18pub struct Schema(pub serde_json::Value);
19
20impl Schema {
21 pub fn new(value: serde_json::Value) -> Self {
22 Self(value)
23 }
24
25 pub fn empty_object() -> Self {
26 Self(serde_json::Value::Object(serde_json::Map::new()))
27 }
28
29 pub fn as_value(&self) -> &serde_json::Value {
30 &self.0
31 }
32
33 pub fn into_value(self) -> serde_json::Value {
34 self.0
35 }
36
37 pub fn is_empty(&self) -> bool {
38 self.0.is_null()
39 || (self.0.is_object() && self.0.as_object().map(|m| m.is_empty()).unwrap_or(true))
40 }
41}
42
43impl From<serde_json::Value> for Schema {
44 fn from(v: serde_json::Value) -> Self {
45 Self(v)
46 }
47}
48
49#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
55#[serde(rename_all = "camelCase")]
56pub struct TextContent {
57 #[serde(default, rename = "type")]
64 pub kind: TextContentType,
65 pub text: String,
66 #[serde(default, skip_serializing_if = "Option::is_none")]
69 pub text_signature: Option<String>,
70}
71
72#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
74pub struct TextContentType;
75impl Serialize for TextContentType {
76 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
77 s.serialize_str("text")
78 }
79}
80impl<'de> Deserialize<'de> for TextContentType {
81 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
82 let s = String::deserialize(d)?;
83 if s != "text" {
84 return Err(serde::de::Error::custom(format!(
85 "expected \"text\", got {s:?}"
86 )));
87 }
88 Ok(Self)
89 }
90}
91
92#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
94#[serde(rename_all = "camelCase")]
95pub struct ThinkingContent {
96 #[serde(default, rename = "type")]
97 pub kind: ThinkingContentType,
98 pub thinking: String,
99 #[serde(default, skip_serializing_if = "Option::is_none")]
100 pub thinking_signature: Option<String>,
101 #[serde(default, skip_serializing_if = "is_false")]
104 pub redacted: bool,
105}
106
107#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
108pub struct ThinkingContentType;
109impl Serialize for ThinkingContentType {
110 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
111 s.serialize_str("thinking")
112 }
113}
114impl<'de> Deserialize<'de> for ThinkingContentType {
115 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
116 let s = String::deserialize(d)?;
117 if s != "thinking" {
118 return Err(serde::de::Error::custom(format!(
119 "expected \"thinking\", got {s:?}"
120 )));
121 }
122 Ok(Self)
123 }
124}
125
126fn is_false(b: &bool) -> bool {
127 !*b
128}
129
130#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
132#[serde(rename_all = "camelCase")]
133pub struct ImageContent {
134 #[serde(default, rename = "type")]
138 pub kind: ImageContentType,
139 pub data: String,
140 pub mime_type: String,
141}
142
143#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
144pub struct ImageContentType;
145impl Serialize for ImageContentType {
146 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
147 s.serialize_str("image")
148 }
149}
150impl<'de> Deserialize<'de> for ImageContentType {
151 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
152 let s = String::deserialize(d)?;
153 if s != "image" {
154 return Err(serde::de::Error::custom(format!(
155 "expected \"image\", got {s:?}"
156 )));
157 }
158 Ok(Self)
159 }
160}
161
162#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
164#[serde(rename_all = "camelCase")]
165pub struct ToolCall {
166 #[serde(default, rename = "type")]
167 pub kind: ToolCallType,
168 pub id: String,
169 pub name: String,
170 pub arguments: serde_json::Value,
173 #[serde(default, skip_serializing_if = "Option::is_none")]
175 pub thought_signature: Option<String>,
176 #[serde(default, skip_serializing_if = "Option::is_none")]
178 pub namespace: Option<String>,
179}
180
181#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
182pub struct ToolCallType;
183impl Serialize for ToolCallType {
184 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
185 s.serialize_str("toolCall")
186 }
187}
188impl<'de> Deserialize<'de> for ToolCallType {
189 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
190 let s = String::deserialize(d)?;
191 if s != "toolCall" {
192 return Err(serde::de::Error::custom(format!(
193 "expected \"toolCall\", got {s:?}"
194 )));
195 }
196 Ok(Self)
197 }
198}
199
200#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
204#[serde(tag = "type", rename_all = "camelCase")]
205pub enum Content {
206 Text(TextContent),
207 Thinking(ThinkingContent),
208 Image(ImageContent),
209 ToolCall(ToolCall),
210}
211
212impl Content {
213 pub fn text<S: Into<String>>(s: S) -> Self {
214 Content::Text(TextContent {
215 kind: TextContentType,
216 text: s.into(),
217 text_signature: None,
218 })
219 }
220
221 pub fn thinking<S: Into<String>>(s: S) -> Self {
222 Content::Thinking(ThinkingContent {
223 kind: ThinkingContentType,
224 thinking: s.into(),
225 thinking_signature: None,
226 redacted: false,
227 })
228 }
229
230 pub fn tool_call<I: Into<String>, N: Into<String>>(
231 id: I,
232 name: N,
233 arguments: serde_json::Value,
234 ) -> Self {
235 Content::ToolCall(ToolCall {
236 kind: ToolCallType,
237 id: id.into(),
238 name: name.into(),
239 arguments,
240 thought_signature: None,
241 namespace: None,
242 })
243 }
244
245 pub fn text_only(content: &[Content], sep: &str) -> String {
248 content
249 .iter()
250 .filter_map(|c| match c {
251 Content::Text(t) => Some(t.text.as_str()),
252 _ => None,
253 })
254 .collect::<Vec<_>>()
255 .join(sep)
256 }
257}
258
259#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize)]
264#[serde(rename_all = "camelCase")]
265pub struct ModelCostRates {
266 pub input: f64,
267 pub output: f64,
268 pub cache_read: f64,
269 pub cache_write: f64,
270}
271
272#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
273#[serde(rename_all = "camelCase")]
274pub struct ModelCostTier {
275 #[serde(flatten)]
276 pub rates: ModelCostRates,
277 pub input_tokens_above: i64,
278}
279
280#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
281#[serde(rename_all = "camelCase")]
282pub struct ModelCost {
283 #[serde(flatten)]
284 pub rates: ModelCostRates,
285 #[serde(default, skip_serializing_if = "Vec::is_empty")]
286 pub tiers: Vec<ModelCostTier>,
287}
288
289impl Default for ModelCost {
290 fn default() -> Self {
291 Self {
292 rates: ModelCostRates::default(),
293 tiers: Vec::new(),
294 }
295 }
296}
297
298#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
300#[serde(rename_all = "camelCase")]
301pub struct Usage {
302 pub input: i64,
303 pub output: i64,
304 pub cache_read: i64,
305 pub cache_write: i64,
306 #[serde(default, skip_serializing_if = "Option::is_none")]
308 pub cache_write_1h: Option<i64>,
309 #[serde(default, skip_serializing_if = "Option::is_none")]
311 pub reasoning: Option<i64>,
312 pub total_tokens: i64,
313 pub cost: UsageCost,
314}
315
316#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize)]
317#[serde(rename_all = "camelCase")]
318pub struct UsageCost {
319 pub input: f64,
320 pub output: f64,
321 pub cache_read: f64,
322 pub cache_write: f64,
323 pub total: f64,
324}
325
326impl Usage {
327 pub fn zero() -> Self {
328 Self {
329 input: 0,
330 output: 0,
331 cache_read: 0,
332 cache_write: 0,
333 cache_write_1h: None,
334 reasoning: None,
335 total_tokens: 0,
336 cost: UsageCost::default(),
337 }
338 }
339
340 pub fn add(&mut self, other: &Usage) {
341 self.input += other.input;
342 self.output += other.output;
343 self.cache_read += other.cache_read;
344 self.cache_write += other.cache_write;
345 if other.cache_write_1h.is_some() {
346 self.cache_write_1h =
347 Some(self.cache_write_1h.unwrap_or(0) + other.cache_write_1h.unwrap());
348 }
349 if other.reasoning.is_some() {
350 self.reasoning = Some(self.reasoning.unwrap_or(0) + other.reasoning.unwrap());
351 }
352 self.total_tokens += other.total_tokens;
353 self.cost.input += other.cost.input;
354 self.cost.output += other.cost.output;
355 self.cost.cache_read += other.cost.cache_read;
356 self.cost.cache_write += other.cost.cache_write;
357 self.cost.total += other.cost.total;
358 }
359
360 pub fn context_tokens(&self) -> i64 {
363 let sum = self.total_tokens;
364 if sum > 0 {
365 sum
366 } else {
367 self.input + self.output + self.cache_read + self.cache_write
368 }
369 }
370}
371
372#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
377#[serde(rename_all = "lowercase")]
378pub enum StopReason {
379 Pending,
380 Stop,
381 Length,
382 ToolUse,
383 Error,
384 Aborted,
385 Deferred,
386}
387
388impl StopReason {
389 pub fn as_str(self) -> &'static str {
390 match self {
391 StopReason::Pending => "pending",
392 StopReason::Stop => "stop",
393 StopReason::Length => "length",
394 StopReason::ToolUse => "toolUse",
395 StopReason::Error => "error",
396 StopReason::Aborted => "aborted",
397 StopReason::Deferred => "deferred",
398 }
399 }
400}
401
402#[derive(Debug, Clone, PartialEq, Eq, Hash)]
405pub enum Api {
406 OpenaiCompletions,
407 MistralConversations,
408 OpenaiResponses,
409 AzureOpenaiResponses,
410 OpenaiCodexResponses,
411 AnthropicMessages,
412 BedrockConverseStream,
413 GoogleGenerativeAi,
414 GoogleVertex,
415 PiMessages,
416 Faux,
417 Other(String),
418}
419
420impl Api {
421 pub fn as_str(&self) -> &str {
422 match self {
423 Api::OpenaiCompletions => "openai-completions",
424 Api::MistralConversations => "mistral-conversations",
425 Api::OpenaiResponses => "openai-responses",
426 Api::AzureOpenaiResponses => "azure-openai-responses",
427 Api::OpenaiCodexResponses => "openai-codex-responses",
428 Api::AnthropicMessages => "anthropic-messages",
429 Api::BedrockConverseStream => "bedrock-converse-stream",
430 Api::GoogleGenerativeAi => "google-generative-ai",
431 Api::GoogleVertex => "google-vertex",
432 Api::PiMessages => "pi-messages",
433 Api::Faux => "faux",
434 Api::Other(s) => s.as_str(),
435 }
436 }
437}
438
439impl Serialize for Api {
440 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
441 s.serialize_str(self.as_str())
442 }
443}
444impl<'de> Deserialize<'de> for Api {
445 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
446 let s = String::deserialize(d)?;
447 Ok(match s.as_str() {
448 "openai-completions" => Api::OpenaiCompletions,
449 "mistral-conversations" => Api::MistralConversations,
450 "openai-responses" => Api::OpenaiResponses,
451 "azure-openai-responses" => Api::AzureOpenaiResponses,
452 "openai-codex-responses" => Api::OpenaiCodexResponses,
453 "anthropic-messages" => Api::AnthropicMessages,
454 "bedrock-converse-stream" => Api::BedrockConverseStream,
455 "google-generative-ai" => Api::GoogleGenerativeAi,
456 "google-vertex" => Api::GoogleVertex,
457 "pi-messages" => Api::PiMessages,
458 "faux" => Api::Faux,
459 other => Api::Other(other.to_string()),
460 })
461 }
462}
463
464pub type ProviderId = String;
466
467#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
469#[serde(rename_all = "lowercase")]
470pub enum InputModality {
471 Text,
472 Image,
473}
474
475#[derive(
477 Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
478)]
479#[serde(rename_all = "lowercase")]
480pub enum ThinkingLevel {
481 #[default]
482 Off,
483 Minimal,
484 Low,
485 Medium,
486 High,
487 Xhigh,
488 Max,
489}
490
491pub type ThinkingLevelMap = BTreeMap<ThinkingLevel, Option<String>>;
494
495#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
499#[serde(rename_all = "camelCase")]
500pub struct ThinkingBudgets {
501 #[serde(default, skip_serializing_if = "Option::is_none")]
502 pub minimal: Option<u64>,
503 #[serde(default, skip_serializing_if = "Option::is_none")]
504 pub low: Option<u64>,
505 #[serde(default, skip_serializing_if = "Option::is_none")]
506 pub medium: Option<u64>,
507 #[serde(default, skip_serializing_if = "Option::is_none")]
508 pub high: Option<u64>,
509}
510
511#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
517#[serde(rename_all = "camelCase")]
518pub struct DeferredHandle {
519 pub provider: String,
520 pub model_id: String,
521 pub api: String,
522 pub id: String,
523 #[serde(default, skip_serializing_if = "Option::is_none")]
524 pub expires_at: Option<i64>,
525 #[serde(default, skip_serializing_if = "Option::is_none")]
526 pub poll_after_ms: Option<i64>,
527 #[serde(default, skip_serializing_if = "Option::is_none")]
528 pub data: Option<serde_json::Value>,
529}
530
531#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
537#[serde(untagged)]
538pub enum UserContent {
539 Text(String),
540 Blocks(Vec<Content>),
541}
542
543impl UserContent {
544 pub fn as_text(&self) -> Option<&str> {
545 match self {
546 UserContent::Text(s) => Some(s),
547 UserContent::Blocks(_) => None,
548 }
549 }
550}
551
552impl From<String> for UserContent {
553 fn from(s: String) -> Self {
554 UserContent::Text(s)
555 }
556}
557
558impl From<&str> for UserContent {
559 fn from(s: &str) -> Self {
560 UserContent::Text(s.to_string())
561 }
562}
563
564#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
565#[serde(rename_all = "camelCase")]
566pub struct UserMessage {
567 #[serde(rename = "role")]
568 pub role: UserRole,
569 pub content: UserContent,
570 pub timestamp: i64,
571}
572
573#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
574pub struct UserRole;
575impl Serialize for UserRole {
576 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
577 s.serialize_str("user")
578 }
579}
580impl<'de> Deserialize<'de> for UserRole {
581 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
582 let s = String::deserialize(d)?;
583 if s != "user" {
584 return Err(serde::de::Error::custom(format!(
585 "expected \"user\", got {s:?}"
586 )));
587 }
588 Ok(Self)
589 }
590}
591
592impl UserMessage {
593 pub fn new(content: impl Into<UserContent>, timestamp: i64) -> Self {
594 Self {
595 role: UserRole,
596 content: content.into(),
597 timestamp,
598 }
599 }
600}
601
602#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
603#[serde(rename_all = "camelCase")]
604pub struct ToolResultMessage {
605 #[serde(rename = "role")]
606 pub role: ToolResultRole,
607 pub tool_call_id: String,
608 pub tool_name: String,
609 pub content: Vec<Content>,
611 #[serde(default, skip_serializing_if = "Option::is_none")]
612 pub details: Option<serde_json::Value>,
613 #[serde(default, skip_serializing_if = "Option::is_none")]
614 pub usage: Option<Usage>,
615 #[serde(default, skip_serializing_if = "Vec::is_empty")]
616 pub added_tool_names: Vec<String>,
617 pub is_error: bool,
618 pub timestamp: i64,
619}
620
621#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
622pub struct ToolResultRole;
623impl Serialize for ToolResultRole {
624 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
625 s.serialize_str("toolResult")
626 }
627}
628impl<'de> Deserialize<'de> for ToolResultRole {
629 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
630 let s = String::deserialize(d)?;
631 if s != "toolResult" {
632 return Err(serde::de::Error::custom(format!(
633 "expected \"toolResult\", got {s:?}"
634 )));
635 }
636 Ok(Self)
637 }
638}
639
640#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
642#[serde(rename_all = "camelCase")]
643pub struct AssistantMessage {
644 #[serde(rename = "role")]
645 pub role: AssistantRole,
646 pub content: Vec<Content>,
647 pub api: Api,
648 pub provider: ProviderId,
649 pub model: String,
650 #[serde(default, skip_serializing_if = "Option::is_none")]
651 pub response_model: Option<String>,
652 #[serde(default, skip_serializing_if = "Option::is_none")]
653 pub response_id: Option<String>,
654 pub usage: Usage,
655 pub stop_reason: StopReason,
656 #[serde(default, skip_serializing_if = "Option::is_none")]
657 pub deferred: Option<DeferredHandle>,
658 #[serde(default, skip_serializing_if = "Option::is_none")]
659 pub error_message: Option<String>,
660 #[serde(default, skip_serializing_if = "Option::is_none")]
661 pub raw_stop_reason: Option<String>,
662 #[serde(default, skip_serializing_if = "Option::is_none")]
663 pub end_turn: Option<bool>,
664 pub timestamp: i64,
665}
666
667#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
668pub struct AssistantRole;
669impl Serialize for AssistantRole {
670 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
671 s.serialize_str("assistant")
672 }
673}
674impl<'de> Deserialize<'de> for AssistantRole {
675 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
676 let s = String::deserialize(d)?;
677 if s != "assistant" {
678 return Err(serde::de::Error::custom(format!(
679 "expected \"assistant\", got {s:?}"
680 )));
681 }
682 Ok(Self)
683 }
684}
685
686impl AssistantMessage {
687 pub fn empty(
690 api: Api,
691 provider: impl Into<String>,
692 model: impl Into<String>,
693 timestamp: i64,
694 ) -> Self {
695 Self {
696 role: AssistantRole,
697 content: Vec::new(),
698 api,
699 provider: provider.into(),
700 model: model.into(),
701 response_model: None,
702 response_id: None,
703 usage: Usage::zero(),
704 stop_reason: StopReason::Pending,
705 deferred: None,
706 error_message: None,
707 raw_stop_reason: None,
708 end_turn: None,
709 timestamp,
710 }
711 }
712
713 pub fn terminal(
715 api: Api,
716 provider: impl Into<String>,
717 model: impl Into<String>,
718 stop_reason: StopReason,
719 error_message: impl Into<String>,
720 timestamp: i64,
721 ) -> Self {
722 Self {
723 role: AssistantRole,
724 content: Vec::new(),
725 api,
726 provider: provider.into(),
727 model: model.into(),
728 response_model: None,
729 response_id: None,
730 usage: Usage::zero(),
731 stop_reason,
732 deferred: None,
733 error_message: Some(error_message.into()),
734 raw_stop_reason: None,
735 end_turn: None,
736 timestamp,
737 }
738 }
739}
740
741#[derive(Debug, Clone, PartialEq, Serialize)]
744#[serde(tag = "role", rename_all = "camelCase")]
745pub enum Message {
746 User(UserMessage),
747 Assistant(Box<AssistantMessage>),
748 ToolResult(Box<ToolResultMessage>),
749}
750
751impl<'de> Deserialize<'de> for Message {
757 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
758 let value = serde_json::Value::deserialize(deserializer)?;
759 let role = value
760 .get("role")
761 .and_then(serde_json::Value::as_str)
762 .ok_or_else(|| serde::de::Error::missing_field("role"))?;
763 match role {
764 "user" => serde_json::from_value(value)
765 .map(Message::User)
766 .map_err(serde::de::Error::custom),
767 "assistant" => serde_json::from_value(value)
768 .map(|message| Message::Assistant(Box::new(message)))
769 .map_err(serde::de::Error::custom),
770 "toolResult" => serde_json::from_value(value)
771 .map(|message| Message::ToolResult(Box::new(message)))
772 .map_err(serde::de::Error::custom),
773 other => Err(serde::de::Error::unknown_variant(
774 other,
775 &["user", "assistant", "toolResult"],
776 )),
777 }
778 }
779}
780
781impl From<UserMessage> for Message {
782 fn from(m: UserMessage) -> Self {
783 Message::User(m)
784 }
785}
786impl From<AssistantMessage> for Message {
787 fn from(m: AssistantMessage) -> Self {
788 Message::Assistant(Box::new(m))
789 }
790}
791impl From<ToolResultMessage> for Message {
792 fn from(m: ToolResultMessage) -> Self {
793 Message::ToolResult(Box::new(m))
794 }
795}
796
797#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
803pub enum GrammarFormat {
804 #[serde(rename = "openai_lark")]
805 OpenaiLark,
806 #[serde(rename = "openai_regex")]
807 OpenaiRegex,
808}
809
810pub type GrammarVariants = BTreeMap<GrammarFormat, String>;
811
812#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
814#[serde(tag = "type", rename_all = "snake_case")]
815pub enum ConstrainedSamplingConfig {
816 JsonSchema {
817 #[serde(rename = "strict")]
818 strict: ConstrainedStrictness,
819 },
820 Grammar {
821 variants: GrammarVariants,
822 },
823}
824
825#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
826#[serde(rename_all = "lowercase")]
827pub enum ConstrainedStrictness {
828 Prefer,
829 Require,
830}
831
832#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
834#[serde(rename_all = "camelCase")]
835pub struct Tool {
836 pub name: String,
837 pub description: String,
838 pub parameters: Schema,
839 #[serde(default, skip_serializing_if = "Option::is_none")]
840 pub constrained_sampling: Option<ConstrainedSamplingConfig>,
841}
842
843#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
845#[serde(rename_all = "camelCase")]
846pub struct Context {
847 #[serde(default, skip_serializing_if = "Option::is_none")]
848 pub system_prompt: Option<String>,
849 pub messages: Vec<Message>,
850 #[serde(default, skip_serializing_if = "Vec::is_empty")]
851 pub tools: Vec<Tool>,
852}
853
854impl Context {
855 pub fn new(messages: Vec<Message>) -> Self {
856 Self {
857 system_prompt: None,
858 messages,
859 tools: Vec::new(),
860 }
861 }
862}
863
864#[derive(Debug, Clone, PartialEq)]
879pub enum AssistantMessageEvent {
880 Start {
881 partial: std::sync::Arc<AssistantMessage>,
882 },
883 TextStart {
884 content_index: usize,
885 partial: std::sync::Arc<AssistantMessage>,
886 },
887 TextDelta {
888 content_index: usize,
889 delta: String,
890 partial: std::sync::Arc<AssistantMessage>,
891 },
892 TextEnd {
893 content_index: usize,
894 content: String,
895 partial: std::sync::Arc<AssistantMessage>,
896 },
897 ThinkingStart {
898 content_index: usize,
899 partial: std::sync::Arc<AssistantMessage>,
900 },
901 ThinkingDelta {
902 content_index: usize,
903 delta: String,
904 partial: std::sync::Arc<AssistantMessage>,
905 },
906 ThinkingEnd {
907 content_index: usize,
908 content: String,
909 partial: std::sync::Arc<AssistantMessage>,
910 },
911 ToolCallStart {
912 content_index: usize,
913 partial: std::sync::Arc<AssistantMessage>,
914 },
915 ToolCallDelta {
916 content_index: usize,
917 delta: String,
918 partial: std::sync::Arc<AssistantMessage>,
919 },
920 ToolCallEnd {
921 content_index: usize,
922 tool_call: ToolCall,
923 partial: std::sync::Arc<AssistantMessage>,
924 },
925 Done {
926 reason: DoneReason,
927 message: AssistantMessage,
928 },
929 Error {
930 reason: ErrorReason,
931 error: AssistantMessage,
932 },
933}
934
935#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
937pub enum DoneReason {
938 Stop,
939 Length,
940 ToolUse,
941 Deferred,
942}
943
944impl DoneReason {
945 pub fn as_str(self) -> &'static str {
946 match self {
947 DoneReason::Stop => "stop",
948 DoneReason::Length => "length",
949 DoneReason::ToolUse => "toolUse",
950 DoneReason::Deferred => "deferred",
951 }
952 }
953}
954
955impl From<DoneReason> for StopReason {
956 fn from(r: DoneReason) -> Self {
957 match r {
958 DoneReason::Stop => StopReason::Stop,
959 DoneReason::Length => StopReason::Length,
960 DoneReason::ToolUse => StopReason::ToolUse,
961 DoneReason::Deferred => StopReason::Deferred,
962 }
963 }
964}
965
966#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
968pub enum ErrorReason {
969 Aborted,
970 Error,
971}
972
973impl ErrorReason {
974 pub fn as_str(self) -> &'static str {
975 match self {
976 ErrorReason::Aborted => "aborted",
977 ErrorReason::Error => "error",
978 }
979 }
980}
981
982impl From<ErrorReason> for StopReason {
983 fn from(r: ErrorReason) -> Self {
984 match r {
985 ErrorReason::Aborted => StopReason::Aborted,
986 ErrorReason::Error => StopReason::Error,
987 }
988 }
989}
990
991impl AssistantMessageEvent {
992 pub fn type_tag(&self) -> &'static str {
994 match self {
995 AssistantMessageEvent::Start { .. } => "start",
996 AssistantMessageEvent::TextStart { .. } => "text_start",
997 AssistantMessageEvent::TextDelta { .. } => "text_delta",
998 AssistantMessageEvent::TextEnd { .. } => "text_end",
999 AssistantMessageEvent::ThinkingStart { .. } => "thinking_start",
1000 AssistantMessageEvent::ThinkingDelta { .. } => "thinking_delta",
1001 AssistantMessageEvent::ThinkingEnd { .. } => "thinking_end",
1002 AssistantMessageEvent::ToolCallStart { .. } => "toolcall_start",
1003 AssistantMessageEvent::ToolCallDelta { .. } => "toolcall_delta",
1004 AssistantMessageEvent::ToolCallEnd { .. } => "toolcall_end",
1005 AssistantMessageEvent::Done { .. } => "done",
1006 AssistantMessageEvent::Error { .. } => "error",
1007 }
1008 }
1009
1010 pub fn is_terminal(&self) -> bool {
1012 matches!(
1013 self,
1014 AssistantMessageEvent::Done { .. } | AssistantMessageEvent::Error { .. }
1015 )
1016 }
1017
1018 pub fn partial(&self) -> &AssistantMessage {
1021 match self {
1022 AssistantMessageEvent::Start { partial }
1023 | AssistantMessageEvent::TextStart { partial, .. }
1024 | AssistantMessageEvent::TextDelta { partial, .. }
1025 | AssistantMessageEvent::TextEnd { partial, .. }
1026 | AssistantMessageEvent::ThinkingStart { partial, .. }
1027 | AssistantMessageEvent::ThinkingDelta { partial, .. }
1028 | AssistantMessageEvent::ThinkingEnd { partial, .. }
1029 | AssistantMessageEvent::ToolCallStart { partial, .. }
1030 | AssistantMessageEvent::ToolCallDelta { partial, .. }
1031 | AssistantMessageEvent::ToolCallEnd { partial, .. } => partial,
1032 AssistantMessageEvent::Done { message, .. } => message,
1033 AssistantMessageEvent::Error { error, .. } => error,
1034 }
1035 }
1036}
1037
1038impl Serialize for AssistantMessageEvent {
1040 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
1041 use serde::ser::SerializeMap;
1042 let tag = self.type_tag();
1043 match self {
1044 AssistantMessageEvent::Start { partial } => {
1045 let mut m = s.serialize_map(Some(2))?;
1046 m.serialize_entry("type", tag)?;
1047 m.serialize_entry("partial", &**partial)?;
1048 m.end()
1049 }
1050 AssistantMessageEvent::TextStart {
1051 content_index,
1052 partial,
1053 }
1054 | AssistantMessageEvent::ThinkingStart {
1055 content_index,
1056 partial,
1057 }
1058 | AssistantMessageEvent::ToolCallStart {
1059 content_index,
1060 partial,
1061 } => {
1062 let mut m = s.serialize_map(Some(3))?;
1063 m.serialize_entry("type", tag)?;
1064 m.serialize_entry("contentIndex", content_index)?;
1065 m.serialize_entry("partial", &**partial)?;
1066 m.end()
1067 }
1068 AssistantMessageEvent::TextDelta {
1069 content_index,
1070 delta,
1071 partial,
1072 }
1073 | AssistantMessageEvent::ThinkingDelta {
1074 content_index,
1075 delta,
1076 partial,
1077 } => {
1078 let mut m = s.serialize_map(Some(4))?;
1079 m.serialize_entry("type", tag)?;
1080 m.serialize_entry("contentIndex", content_index)?;
1081 m.serialize_entry("delta", delta)?;
1082 m.serialize_entry("partial", &**partial)?;
1083 m.end()
1084 }
1085 AssistantMessageEvent::ToolCallDelta {
1086 content_index,
1087 delta,
1088 partial,
1089 } => {
1090 let mut m = s.serialize_map(Some(4))?;
1091 m.serialize_entry("type", tag)?;
1092 m.serialize_entry("contentIndex", content_index)?;
1093 m.serialize_entry("delta", delta)?;
1094 m.serialize_entry("partial", &**partial)?;
1095 m.end()
1096 }
1097 AssistantMessageEvent::TextEnd {
1098 content_index,
1099 content,
1100 partial,
1101 }
1102 | AssistantMessageEvent::ThinkingEnd {
1103 content_index,
1104 content,
1105 partial,
1106 } => {
1107 let mut m = s.serialize_map(Some(4))?;
1108 m.serialize_entry("type", tag)?;
1109 m.serialize_entry("contentIndex", content_index)?;
1110 m.serialize_entry("content", content)?;
1111 m.serialize_entry("partial", &**partial)?;
1112 m.end()
1113 }
1114 AssistantMessageEvent::ToolCallEnd {
1115 content_index,
1116 tool_call,
1117 partial,
1118 } => {
1119 let mut m = s.serialize_map(Some(4))?;
1120 m.serialize_entry("type", tag)?;
1121 m.serialize_entry("contentIndex", content_index)?;
1122 m.serialize_entry("toolCall", tool_call)?;
1123 m.serialize_entry("partial", &**partial)?;
1124 m.end()
1125 }
1126 AssistantMessageEvent::Done { reason, message } => {
1127 let mut m = s.serialize_map(Some(3))?;
1128 m.serialize_entry("type", tag)?;
1129 m.serialize_entry("reason", reason.as_str())?;
1130 m.serialize_entry("message", message)?;
1131 m.end()
1132 }
1133 AssistantMessageEvent::Error { reason, error } => {
1134 let mut m = s.serialize_map(Some(3))?;
1135 m.serialize_entry("type", tag)?;
1136 m.serialize_entry("reason", reason.as_str())?;
1137 m.serialize_entry("error", error)?;
1138 m.end()
1139 }
1140 }
1141 }
1142}