1use std::fmt;
9use std::sync::Arc;
10
11use locode_protocol::{ContentBlock, Usage};
12use locode_provider::{
13 AnthropicProvider, Completion, MockProvider, OpenAiResponsesProvider, Provider, StopReason,
14};
15
16pub struct ProviderInit {
18 pub session_id: String,
21}
22
23pub struct BuiltProvider {
26 pub provider: Arc<dyn Provider>,
28 pub model: String,
30}
31
32impl fmt::Debug for BuiltProvider {
33 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34 f.debug_struct("BuiltProvider")
35 .field("model", &self.model)
36 .finish_non_exhaustive() }
38}
39
40#[derive(Debug)]
43pub struct ProviderBuildError(pub String);
44
45impl fmt::Display for ProviderBuildError {
46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 f.write_str(&self.0)
48 }
49}
50
51impl std::error::Error for ProviderBuildError {}
52
53pub type ProviderFactory =
56 Box<dyn Fn(&ProviderInit) -> Result<BuiltProvider, ProviderBuildError> + Send + Sync>;
57
58pub struct ProviderRegistry {
64 entries: Vec<(String, ProviderFactory)>,
65}
66
67impl ProviderRegistry {
68 #[must_use]
70 pub fn new() -> Self {
71 ProviderRegistry {
72 entries: Vec::new(),
73 }
74 }
75
76 #[must_use]
79 pub fn builtin() -> Self {
80 Self::new()
81 .register("anthropic", |_init| {
82 let provider = AnthropicProvider::from_env()
83 .map_err(|e| ProviderBuildError(format!("anthropic wire: {e}")))?;
84 let model = provider.config().model.clone();
85 Ok(BuiltProvider {
86 provider: Arc::new(provider),
87 model,
88 })
89 })
90 .register("openai-responses", |init| {
91 let mut provider = OpenAiResponsesProvider::from_env()
92 .map_err(|e| ProviderBuildError(format!("openai-responses wire: {e}")))?;
93 provider.config_mut().prompt_cache_key = Some(init.session_id.clone());
96 let model = provider.config().model.clone();
97 Ok(BuiltProvider {
98 provider: Arc::new(provider),
99 model,
100 })
101 })
102 .register("mock", |_init| {
103 let provider = MockProvider::new(vec![Completion {
104 content: vec![ContentBlock::Text {
105 text: "Mock run complete.".to_string(),
106 }],
107 usage: Usage::default(),
108 stop: StopReason::EndTurn,
109 }]);
110 Ok(BuiltProvider {
111 provider: Arc::new(provider),
112 model: "mock-1".to_string(),
113 })
114 })
115 }
116
117 #[must_use]
120 pub fn register<F>(mut self, name: impl Into<String>, factory: F) -> Self
121 where
122 F: Fn(&ProviderInit) -> Result<BuiltProvider, ProviderBuildError> + Send + Sync + 'static,
123 {
124 let name = name.into();
125 let factory: ProviderFactory = Box::new(factory);
126 match self.entries.iter_mut().find(|(n, _)| *n == name) {
127 Some(entry) => entry.1 = factory,
128 None => self.entries.push((name, factory)),
129 }
130 self
131 }
132
133 #[must_use]
135 pub fn names(&self) -> Vec<&str> {
136 self.entries.iter().map(|(n, _)| n.as_str()).collect()
137 }
138
139 pub fn build(
145 &self,
146 name: &str,
147 init: &ProviderInit,
148 ) -> Result<BuiltProvider, ProviderBuildError> {
149 let factory = self
150 .entries
151 .iter()
152 .find(|(n, _)| n == name)
153 .map(|(_, f)| f)
154 .ok_or_else(|| {
155 ProviderBuildError(format!(
156 "unknown --api-schema `{name}`; available: {}",
157 self.names().join(", ")
158 ))
159 })?;
160 factory(init)
161 }
162}
163
164impl Default for ProviderRegistry {
165 fn default() -> Self {
166 Self::builtin()
167 }
168}