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 },
51 Done,
52}
53
54#[derive(Debug, Clone, Default)]
57pub struct CacheReadBySlot {
58 pub system: Option<u32>,
59 pub tools: Option<u32>,
60 pub messages: Option<u32>,
61}
62
63#[async_trait]
64pub trait LLMProvider: Send + Sync {
65 fn create_run_state(&self) -> Option<ProviderRunState> {
67 None
68 }
69
70 fn runtime_policy(&self) -> RuntimePolicy {
72 RuntimePolicy::default()
73 }
74
75 fn peek_provider_replay(
76 &self,
77 _content: &str,
78 _tool_calls: &[ToolCall],
79 ) -> Option<ProviderReplay> {
80 None
81 }
82
83 fn seed_provider_replay(
84 &self,
85 _content: &str,
86 _tool_calls: &[ToolCall],
87 _replay: &ProviderReplay,
88 ) {
89 }
90
91 fn commit_stream_replay(&self, _content: &str, _tool_calls: &[ToolCall]) {}
92
93 async fn complete(
95 &self,
96 context: &RenderedContext,
97 tools: &[ToolSchema],
98 extensions: Option<&serde_json::Value>,
99 ) -> crate::Result<Message> {
100 let mut stream = self.stream(context, tools, extensions, None).await?;
101 collect_message_from_stream(&mut stream).await
102 }
103
104 async fn stream(
105 &self,
106 context: &RenderedContext,
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}
112
113pub async fn collect_message_from_stream(
114 stream: &mut (dyn Stream<Item = crate::Result<StreamEvent>> + Send + Unpin),
115) -> crate::Result<Message> {
116 let mut content = String::new();
117 let mut tool_calls = Vec::new();
118 while let Some(evt) = stream.next().await {
119 match evt? {
120 StreamEvent::TextDelta { delta } => content.push_str(&delta),
121 StreamEvent::ThinkingDelta { .. } => {}
122 StreamEvent::ToolCall {
123 id,
124 name,
125 arguments,
126 } => {
127 tool_calls.push(ToolCall {
128 id: CompactString::new(&id),
129 name: CompactString::new(&name),
130 arguments,
131 });
132 }
133 StreamEvent::Usage { .. } | StreamEvent::Done => {}
134 }
135 }
136 Ok(Message {
137 role: Role::Assistant,
138 content: Content::Text(content),
139 tool_calls,
140 token_count: None,
141 })
142}
143
144#[derive(Debug, Clone, Default)]
146pub struct TokenUsage {
147 pub input_tokens: u32,
149 pub output_tokens: u32,
150 pub cache_read_input_tokens: u32,
152 pub cache_creation_input_tokens: u32,
154}
155
156impl TokenUsage {
157 pub fn total_tokens(&self) -> u32 {
158 self.input_tokens + self.output_tokens
159 }
160}
161
162#[derive(Debug, Clone)]
164pub struct ProviderToolSpec {
165 pub name: String,
166 pub description: String,
167 pub parameters: serde_json::Value,
168}