apollo/providers/
traits.rs1use async_trait::async_trait;
5use serde::{Deserialize, Serialize};
6
7use crate::tools::ToolSpec;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct ChatMessage {
12 pub role: String,
13 pub content: String,
14 #[serde(skip_serializing_if = "Option::is_none")]
16 pub tool_use_id: Option<String>,
17}
18
19impl ChatMessage {
20 pub fn system(content: impl Into<String>) -> Self {
21 Self {
22 role: "system".into(),
23 content: content.into(),
24 tool_use_id: None,
25 }
26 }
27 pub fn user(content: impl Into<String>) -> Self {
28 Self {
29 role: "user".into(),
30 content: content.into(),
31 tool_use_id: None,
32 }
33 }
34 pub fn assistant(content: impl Into<String>) -> Self {
35 Self {
36 role: "assistant".into(),
37 content: content.into(),
38 tool_use_id: None,
39 }
40 }
41 pub fn tool_result(id: impl Into<String>, content: impl Into<String>) -> Self {
42 Self {
43 role: "tool_result".into(),
44 content: content.into(),
45 tool_use_id: Some(id.into()),
46 }
47 }
48 pub fn is_tool_result(&self) -> bool {
49 self.role == "tool_result"
50 }
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct ToolCall {
56 pub id: String,
57 pub name: String,
58 pub arguments: String,
59}
60
61#[derive(Debug, Clone, Default)]
63pub struct ChatResponse {
64 pub text: Option<String>,
65 pub tool_calls: Vec<ToolCall>,
66 pub usage: Option<Usage>,
67}
68
69#[derive(Debug, Clone, Default)]
70pub struct Usage {
71 pub input_tokens: u32,
72 pub output_tokens: u32,
73}
74
75impl ChatResponse {
76 pub fn has_tool_calls(&self) -> bool {
77 !self.tool_calls.is_empty()
78 }
79 pub fn text_or_empty(&self) -> &str {
80 self.text.as_deref().unwrap_or("")
81 }
82}
83
84#[derive(Debug, Clone, Copy)]
86pub struct ChatRequest<'a> {
87 pub messages: &'a [ChatMessage],
88 pub tools: Option<&'a [ToolSpec]>,
89 pub model: &'a str,
90 pub temperature: f64,
91 pub max_tokens: Option<u32>,
92}
93
94#[derive(Debug, Clone, Default)]
96pub struct ProviderCapabilities {
97 pub native_tools: bool,
99 pub streaming: bool,
101 pub vision: bool,
103 pub max_context: u32,
105 pub native_web_search: bool,
108}
109
110#[async_trait]
113pub trait Provider: Send + Sync {
114 fn name(&self) -> &str;
116
117 fn capabilities(&self) -> ProviderCapabilities {
119 ProviderCapabilities::default()
120 }
121
122 async fn chat(&self, request: &ChatRequest<'_>) -> anyhow::Result<ChatResponse>;
124
125 async fn simple_chat(&self, message: &str, model: &str) -> anyhow::Result<String> {
127 let messages = [ChatMessage::user(message)];
128 let request = ChatRequest {
129 messages: &messages,
130 tools: None,
131 model,
132 temperature: 0.7,
133 max_tokens: None,
134 };
135 let response = self.chat(&request).await?;
136 Ok(response.text.unwrap_or_default())
137 }
138}