1use async_trait::async_trait;
4use serde::{Deserialize, Serialize};
5use thiserror::Error;
6
7use openclaw_core::types::TokenUsage;
8use std::pin::Pin;
9
10#[derive(Error, Debug)]
12pub enum ProviderError {
13 #[error("API error: {status} - {message}")]
15 Api {
16 status: u16,
18 message: String,
20 },
21
22 #[error("Network error: {0}")]
24 Network(#[from] reqwest::Error),
25
26 #[error("Serialization error: {0}")]
28 Serialization(#[from] serde_json::Error),
29
30 #[error("Rate limited, retry after {retry_after_secs} seconds")]
32 RateLimited {
33 retry_after_secs: u64,
35 },
36
37 #[error("Invalid configuration: {0}")]
39 Config(String),
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct CompletionRequest {
45 pub model: String,
47
48 pub messages: Vec<Message>,
50
51 pub system: Option<String>,
53
54 pub max_tokens: u32,
56
57 pub temperature: f32,
59
60 pub stop: Option<Vec<String>>,
62
63 pub tools: Option<Vec<Tool>>,
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct Message {
70 pub role: Role,
72
73 pub content: MessageContent,
75}
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
79#[serde(rename_all = "lowercase")]
80pub enum Role {
81 User,
83 Assistant,
85 System,
87 Tool,
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize)]
93#[serde(untagged)]
94pub enum MessageContent {
95 Text(String),
97 Blocks(Vec<ContentBlock>),
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
103#[serde(tag = "type", rename_all = "snake_case")]
104pub enum ContentBlock {
105 Text {
107 text: String,
109 },
110 Image {
112 source: ImageSource,
114 },
115 ToolUse {
117 id: String,
119 name: String,
121 input: serde_json::Value,
123 },
124 ToolResult {
126 tool_use_id: String,
128 content: String,
130 is_error: Option<bool>,
132 },
133}
134
135#[derive(Debug, Clone, Serialize, Deserialize)]
137pub struct ImageSource {
138 #[serde(rename = "type")]
140 pub source_type: String,
141 pub media_type: String,
143 pub data: String,
145}
146
147#[derive(Debug, Clone, Serialize, Deserialize)]
149pub struct Tool {
150 pub name: String,
152 pub description: String,
154 pub input_schema: serde_json::Value,
156}
157
158#[derive(Debug, Clone, Serialize, Deserialize)]
160pub struct CompletionResponse {
161 pub id: String,
163
164 pub model: String,
166
167 pub content: Vec<ContentBlock>,
169
170 pub stop_reason: Option<StopReason>,
172
173 pub usage: TokenUsage,
175}
176
177#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
179#[serde(rename_all = "snake_case")]
180pub enum StopReason {
181 EndTurn,
183 MaxTokens,
185 StopSequence,
187 ToolUse,
189}
190
191#[derive(Debug, Clone, Serialize, Deserialize)]
193pub struct StreamingChunk {
194 pub chunk_type: ChunkType,
196 pub delta: Option<String>,
198 pub index: Option<usize>,
200}
201
202#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
204#[serde(rename_all = "snake_case")]
205pub enum ChunkType {
206 MessageStart,
208 ContentBlockStart,
210 ContentBlockDelta,
212 ContentBlockStop,
214 MessageDelta,
216 MessageStop,
218}
219
220#[async_trait]
222pub trait Provider: Send + Sync {
223 fn name(&self) -> &str;
225
226 async fn list_models(&self) -> Result<Vec<String>, ProviderError>;
228
229 async fn complete(
231 &self,
232 request: CompletionRequest,
233 ) -> Result<CompletionResponse, ProviderError>;
234
235 async fn complete_stream(
237 &self,
238 request: CompletionRequest,
239 ) -> Result<
240 Pin<Box<dyn futures::Stream<Item = Result<StreamingChunk, ProviderError>> + Send>>,
241 ProviderError,
242 >;
243}