deepstrike_sdk/providers/
mod.rs1use async_trait::async_trait;
2use compact_str::CompactString;
3use deepstrike_core::context::renderer::InternalRenderedContext;
4use deepstrike_core::runtime::session::ProviderReplay;
5use deepstrike_core::types::message::{Content, CoreMessage, Role, ToolCall, ToolSchema};
6use futures::{Stream, StreamExt};
7
8pub mod anthropic;
9pub mod openai;
10pub mod provider_error;
11pub mod request_plan;
12
13pub use provider_error::{ProviderError, ProviderErrorKind};
14
15pub use request_plan::{
16 CostObservation, NormalizedProviderUsage, PricingRates, PricingSnapshot,
17 ProviderRequestEndpoint, ProviderRequestPlan, ProviderUsage, RecordedPromptMeasurement,
18 RequestPlanError, UnpricedReason, measurement_for_plan, normalize_provider_usage,
19 price_provider_usage, record_prompt_measurement,
20};
21
22pub type ProviderRunState = serde_json::Value;
24
25#[derive(Debug, Clone, Default)]
28pub struct RuntimePolicy {
29 pub max_turns: Option<u32>,
30 pub timeout_ms: Option<u64>,
31}
32
33#[derive(Debug, Clone)]
35pub enum StreamEvent {
36 TextDelta {
37 delta: String,
38 },
39 ThinkingDelta {
40 delta: String,
41 },
42 ToolCall {
43 id: String,
44 name: String,
45 arguments: serde_json::Value,
46 },
47 Usage {
49 total_tokens: u32,
50 input_tokens: u32,
52 output_tokens: u32,
53 cache_read_input_tokens: u32,
55 cache_creation_input_tokens: u32,
57 cache_read_input_tokens_by_slot: Option<CacheReadBySlot>,
61 stop_reason: Option<String>,
64 },
65 Done,
66}
67
68#[derive(Debug, Clone, Default)]
71pub struct CacheReadBySlot {
72 pub system: Option<u32>,
73 pub tools: Option<u32>,
74 pub messages: Option<u32>,
75}
76
77#[async_trait]
78pub trait LLMProvider: Send + Sync {
79 fn context_route(&self) -> serde_json::Value {
82 serde_json::json!({ "kind": "opaque", "implementation": std::any::type_name::<Self>() })
83 }
84
85 fn prepare_context_request(
89 &self,
90 context: &InternalRenderedContext,
91 tools: &[ToolSchema],
92 extensions: Option<&serde_json::Value>,
93 state: Option<&ProviderRunState>,
94 ) -> crate::Result<serde_json::Value> {
95 Ok(serde_json::json!({
96 "scope": "adapter_input", "context": context, "tools": tools,
97 "extensions": extensions, "state": state,
98 }))
99 }
100
101 async fn stream_prepared(
104 &self,
105 _prepared: &serde_json::Value,
106 context: &InternalRenderedContext,
107 tools: &[ToolSchema],
108 extensions: Option<&serde_json::Value>,
109 state: Option<&ProviderRunState>,
110 ) -> crate::Result<Box<dyn Stream<Item = crate::Result<StreamEvent>> + Send + Unpin>> {
111 self.stream(context, tools, extensions, state).await
112 }
113
114 fn create_run_state(&self) -> Option<ProviderRunState> {
116 None
117 }
118
119 fn runtime_policy(&self) -> RuntimePolicy {
121 RuntimePolicy::default()
122 }
123
124 fn peek_provider_replay(
125 &self,
126 _content: &str,
127 _tool_calls: &[ToolCall],
128 ) -> Option<ProviderReplay> {
129 None
130 }
131
132 fn seed_provider_replay(
133 &self,
134 _content: &str,
135 _tool_calls: &[ToolCall],
136 _replay: &ProviderReplay,
137 ) {
138 }
139
140 fn commit_stream_replay(&self, _content: &str, _tool_calls: &[ToolCall]) {}
141
142 async fn complete(
144 &self,
145 context: &InternalRenderedContext,
146 tools: &[ToolSchema],
147 extensions: Option<&serde_json::Value>,
148 ) -> crate::Result<CoreMessage> {
149 let mut stream = self.stream(context, tools, extensions, None).await?;
150 collect_message_from_stream(&mut stream).await
151 }
152
153 async fn stream(
154 &self,
155 context: &InternalRenderedContext,
156 tools: &[ToolSchema],
157 extensions: Option<&serde_json::Value>,
158 state: Option<&ProviderRunState>,
159 ) -> crate::Result<Box<dyn Stream<Item = crate::Result<StreamEvent>> + Send + Unpin>>;
160}
161
162pub async fn collect_message_from_stream(
163 stream: &mut (dyn Stream<Item = crate::Result<StreamEvent>> + Send + Unpin),
164) -> crate::Result<CoreMessage> {
165 let mut content = String::new();
166 let mut tool_calls = Vec::new();
167 while let Some(evt) = stream.next().await {
168 match evt? {
169 StreamEvent::TextDelta { delta } => content.push_str(&delta),
170 StreamEvent::ThinkingDelta { .. } => {}
171 StreamEvent::ToolCall {
172 id,
173 name,
174 arguments,
175 } => {
176 tool_calls.push(ToolCall {
177 id: CompactString::new(&id),
178 name: CompactString::new(&name),
179 arguments,
180 });
181 }
182 StreamEvent::Usage { .. } | StreamEvent::Done => {}
183 }
184 }
185 Ok(CoreMessage {
186 role: Role::Assistant,
187 content: Content::Text(content),
188 tool_calls,
189 })
190}
191
192#[derive(Debug, Clone, Default)]
194pub struct TokenUsage {
195 pub input_tokens: u32,
197 pub output_tokens: u32,
198 pub cache_read_input_tokens: u32,
200 pub cache_creation_input_tokens: u32,
202}
203
204impl TokenUsage {
205 pub fn total_tokens(&self) -> u32 {
206 self.input_tokens + self.output_tokens
207 }
208}
209
210#[derive(Debug, Clone)]
212pub struct ProviderToolSpec {
213 pub name: String,
214 pub description: String,
215 pub parameters: serde_json::Value,
216}