deepstrike_sdk/providers/
mod.rs1use async_trait::async_trait;
2use compact_str::CompactString;
3use deepstrike_core::context::renderer::RenderedContext;
4use deepstrike_core::runtime::session::ProviderReplay;
5use deepstrike_core::types::message::{Content, Message, Role, ToolCall, ToolSchema};
6use futures::{Stream, StreamExt};
7
8pub mod anthropic;
9pub mod openai;
10
11pub type ProviderRunState = serde_json::Value;
13
14#[derive(Debug, Clone, Default)]
17pub struct RuntimePolicy {
18 pub max_turns: Option<u32>,
19 pub timeout_ms: Option<u64>,
20}
21
22#[derive(Debug, Clone)]
24pub enum StreamEvent {
25 TextDelta {
26 delta: String,
27 },
28 ThinkingDelta {
29 delta: String,
30 },
31 ToolCall {
32 id: String,
33 name: String,
34 arguments: serde_json::Value,
35 },
36 Usage {
38 total_tokens: u32,
39 input_tokens: u32,
41 output_tokens: u32,
42 cache_read_input_tokens: u32,
44 cache_creation_input_tokens: u32,
46 cache_read_input_tokens_by_slot: Option<CacheReadBySlot>,
50 stop_reason: Option<String>,
53 },
54 Done,
55}
56
57#[derive(Debug, Clone, Default)]
60pub struct CacheReadBySlot {
61 pub system: Option<u32>,
62 pub tools: Option<u32>,
63 pub messages: Option<u32>,
64}
65
66#[async_trait]
67pub trait LLMProvider: Send + Sync {
68 fn create_run_state(&self) -> Option<ProviderRunState> {
70 None
71 }
72
73 fn runtime_policy(&self) -> RuntimePolicy {
75 RuntimePolicy::default()
76 }
77
78 fn peek_provider_replay(
79 &self,
80 _content: &str,
81 _tool_calls: &[ToolCall],
82 ) -> Option<ProviderReplay> {
83 None
84 }
85
86 fn seed_provider_replay(
87 &self,
88 _content: &str,
89 _tool_calls: &[ToolCall],
90 _replay: &ProviderReplay,
91 ) {
92 }
93
94 fn commit_stream_replay(&self, _content: &str, _tool_calls: &[ToolCall]) {}
95
96 async fn complete(
98 &self,
99 context: &RenderedContext,
100 tools: &[ToolSchema],
101 extensions: Option<&serde_json::Value>,
102 ) -> crate::Result<Message> {
103 let mut stream = self.stream(context, tools, extensions, None).await?;
104 collect_message_from_stream(&mut stream).await
105 }
106
107 async fn stream(
108 &self,
109 context: &RenderedContext,
110 tools: &[ToolSchema],
111 extensions: Option<&serde_json::Value>,
112 state: Option<&ProviderRunState>,
113 ) -> crate::Result<Box<dyn Stream<Item = crate::Result<StreamEvent>> + Send + Unpin>>;
114}
115
116pub async fn collect_message_from_stream(
117 stream: &mut (dyn Stream<Item = crate::Result<StreamEvent>> + Send + Unpin),
118) -> crate::Result<Message> {
119 let mut content = String::new();
120 let mut tool_calls = Vec::new();
121 while let Some(evt) = stream.next().await {
122 match evt? {
123 StreamEvent::TextDelta { delta } => content.push_str(&delta),
124 StreamEvent::ThinkingDelta { .. } => {}
125 StreamEvent::ToolCall {
126 id,
127 name,
128 arguments,
129 } => {
130 tool_calls.push(ToolCall {
131 id: CompactString::new(&id),
132 name: CompactString::new(&name),
133 arguments,
134 });
135 }
136 StreamEvent::Usage { .. } | StreamEvent::Done => {}
137 }
138 }
139 Ok(Message {
140 role: Role::Assistant,
141 content: Content::Text(content),
142 tool_calls,
143 token_count: None,
144 })
145}
146
147#[derive(Debug, Clone, Default)]
149pub struct TokenUsage {
150 pub input_tokens: u32,
152 pub output_tokens: u32,
153 pub cache_read_input_tokens: u32,
155 pub cache_creation_input_tokens: u32,
157}
158
159impl TokenUsage {
160 pub fn total_tokens(&self) -> u32 {
161 self.input_tokens + self.output_tokens
162 }
163}
164
165#[derive(Debug, Clone)]
167pub struct ProviderToolSpec {
168 pub name: String,
169 pub description: String,
170 pub parameters: serde_json::Value,
171}