Skip to main content

oxi_sdk/
builder.rs

1//! OxiBuilder and Oxi — SDK entry point
2
3use anyhow::Result;
4use std::sync::Arc;
5
6use oxi_agent::{ProviderResolver, ToolRegistry};
7use oxi_ai::{Model, ModelRegistry, Provider, ProviderRegistry};
8
9use crate::agent_builder::AgentBuilder;
10
11/// Oxi AI engine instance — holds isolated provider and model registries.
12///
13/// Created via [`OxiBuilder`]. Provides access to providers, models,
14/// provider creation, and agent building.
15///
16/// Implements [`ProviderResolver`] so it can be passed directly to
17/// [`oxi_agent::Agent::new_with_resolver`] for fully isolated operation.
18pub struct Oxi {
19    providers: Arc<ProviderRegistry>,
20    models: Arc<ModelRegistry>,
21    tools: Arc<ToolRegistry>,
22    /// Whether to include built-in provider resolution (from create_builtin_provider).
23    include_builtins: bool,
24}
25
26impl Oxi {
27    /// Create an agent builder with the given config.
28    pub fn agent(&self, config: oxi_agent::AgentConfig) -> AgentBuilder<'_> {
29        AgentBuilder::new(self, config)
30    }
31
32    /// Get the provider registry.
33    pub fn providers(&self) -> &ProviderRegistry {
34        &self.providers
35    }
36
37    /// Get the model registry.
38    pub fn models(&self) -> &ModelRegistry {
39        &self.models
40    }
41
42    /// Get the shared tool registry.
43    pub fn tools(&self) -> Arc<ToolRegistry> {
44        Arc::clone(&self.tools)
45    }
46
47    /// Resolve a model ID to a Model.
48    ///
49    /// Accepts `"provider/model"` or bare `"model"` (defaults to "anthropic").
50    pub fn resolve_model(&self, model_id: &str) -> Result<Model> {
51        let parts: Vec<&str> = model_id.splitn(2, '/').collect();
52        let (provider, model) = if parts.len() == 2 {
53            (parts[0], parts[1])
54        } else {
55            ("anthropic", parts[0])
56        };
57        self.models
58            .lookup(provider, model)
59            .ok_or_else(|| anyhow::anyhow!("Model '{}' not found", model_id))
60    }
61
62    /// Create a provider instance for a given provider name.
63    ///
64    /// Checks the local `ProviderRegistry` first, then falls back
65    /// to built-in providers (if `with_builtins()` was called).
66    pub fn create_provider(&self, name: &str) -> Result<Arc<dyn Provider>> {
67        // 1. Check custom providers registered via OxiBuilder::provider()
68        if let Some(p) = self.providers.get_custom(name) {
69            return Ok(p);
70        }
71        // 2. Fall back to built-in providers (stateless creation)
72        if self.include_builtins {
73            if let Some(p) = oxi_ai::create_builtin_provider(name) {
74                return Ok(Arc::from(p));
75            }
76        }
77        Err(anyhow::anyhow!("Provider '{}' not found", name))
78    }
79
80    /// Get the provider registry (Arc clone).
81    pub fn providers_arc(&self) -> Arc<ProviderRegistry> {
82        Arc::clone(&self.providers)
83    }
84
85    /// Get the model registry (Arc clone).
86    pub fn models_arc(&self) -> Arc<ModelRegistry> {
87        Arc::clone(&self.models)
88    }
89
90    /// Check whether built-in providers are enabled.
91    pub fn has_builtins(&self) -> bool {
92        self.include_builtins
93    }
94}
95
96/// Implement ProviderResolver so Oxi can be used as Agent's resolver.
97impl ProviderResolver for Oxi {
98    fn resolve_provider(&self, name: &str) -> Option<Arc<dyn Provider>> {
99        self.create_provider(name).ok()
100    }
101
102    fn resolve_model(&self, model_id: &str) -> Option<Model> {
103        self.resolve_model(model_id).ok()
104    }
105}
106
107/// Builder for creating an Oxi instance.
108pub struct OxiBuilder {
109    providers: ProviderRegistry,
110    models: ModelRegistry,
111    tools: ToolRegistry,
112    include_builtins: bool,
113}
114
115impl OxiBuilder {
116    /// Create a new empty builder (no builtins, no providers, no models).
117    pub fn new() -> Self {
118        Self {
119            providers: ProviderRegistry::new(),
120            models: ModelRegistry::new(),
121            tools: ToolRegistry::new(),
122            include_builtins: false,
123        }
124    }
125
126    /// Register all built-in models and enable built-in provider creation.
127    ///
128    /// This loads 50+ model definitions from the oxi-ai static database
129    /// and enables `create_builtin_provider()` fallback in [`Oxi::create_provider`].
130    pub fn with_builtins(mut self) -> Self {
131        self.models = ModelRegistry::from_static();
132        self.include_builtins = true;
133        self
134    }
135
136    /// Register a custom provider.
137    pub fn provider(self, name: &str, p: impl Provider + 'static) -> Self {
138        self.providers.register(name, p);
139        self
140    }
141
142    /// Register a custom tool in the shared tool registry.
143    pub fn tool(self, tool: impl oxi_agent::AgentTool + 'static) -> Self {
144        self.tools.register(tool);
145        self
146    }
147
148    /// Register a custom model.
149    pub fn model(self, model: Model) -> Self {
150        self.models.register(model);
151        self
152    }
153
154    /// Build the Oxi engine instance.
155    pub fn build(self) -> Oxi {
156        Oxi {
157            providers: Arc::new(self.providers),
158            models: Arc::new(self.models),
159            tools: Arc::new(self.tools),
160            include_builtins: self.include_builtins,
161        }
162    }
163}
164
165impl Default for OxiBuilder {
166    fn default() -> Self {
167        Self::new()
168    }
169}