1use anyhow::Result;
10use oxi_sdk::{Oxi, OxiBuilder};
11use std::sync::Arc;
12
13pub struct OxiosEngine {
15 oxi: Oxi,
16 default_model_id: String,
17}
18
19impl OxiosEngine {
20 pub fn new(default_model_id: impl Into<String>) -> Self {
25 let oxi = OxiBuilder::new().with_builtins().build();
26 Self {
27 oxi,
28 default_model_id: default_model_id.into(),
29 }
30 }
31
32 pub fn oxi(&self) -> &Oxi {
37 &self.oxi
38 }
39
40 pub fn resolve_model(&self, model_id: &str) -> Result<oxi_sdk::Model> {
45 self.oxi.resolve_model(model_id)
46 }
47
48 pub fn create_provider(&self, name: &str) -> Result<Arc<dyn oxi_sdk::Provider>> {
53 self.oxi.create_provider(name)
54 }
55}
56
57pub trait EngineProvider: Send + Sync {
66 fn create_provider(&self, provider_name: &str) -> Result<Arc<dyn oxi_sdk::Provider>>;
68
69 fn resolve_model(&self, model_id: &str) -> Result<oxi_sdk::Model>;
71
72 fn default_model_id(&self) -> &str;
74}
75
76pub struct OxiEngineProvider {
78 engine: OxiosEngine,
79}
80
81impl OxiEngineProvider {
82 pub fn new(default_model_id: impl Into<String>) -> Self {
84 Self {
85 engine: OxiosEngine::new(default_model_id),
86 }
87 }
88}
89
90impl EngineProvider for OxiEngineProvider {
91 fn create_provider(&self, provider_name: &str) -> Result<Arc<dyn oxi_sdk::Provider>> {
92 self.engine.create_provider(provider_name)
93 }
94
95 fn resolve_model(&self, model_id: &str) -> Result<oxi_sdk::Model> {
96 self.engine.resolve_model(model_id)
97 }
98
99 fn default_model_id(&self) -> &str {
100 &self.engine.default_model_id
101 }
102}
103
104impl std::fmt::Debug for OxiEngineProvider {
105 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106 f.debug_struct("OxiEngineProvider")
107 .field("default_model_id", &self.engine.default_model_id)
108 .finish()
109 }
110}
111
112#[cfg(test)]
117mod tests {
118 use super::*;
119
120 #[test]
121 fn test_resolve_model_with_provider_prefix() {
122 let engine = OxiEngineProvider::new("anthropic/claude-sonnet-4-20250514");
123 let model = engine.resolve_model("openai/gpt-4o").unwrap();
124 assert_eq!(model.provider, "openai");
125 assert_eq!(model.id, "gpt-4o");
126 }
127
128 #[test]
129 fn test_resolve_model_without_provider_prefix() {
130 let engine = OxiEngineProvider::new("anthropic/claude-sonnet-4-20250514");
131 let model = engine.resolve_model("claude-sonnet-4-20250514").unwrap();
132 assert_eq!(model.provider, "anthropic");
133 }
134
135 #[test]
136 fn test_default_model_id() {
137 let engine = OxiEngineProvider::new("anthropic/claude-sonnet-4-20250514");
138 assert_eq!(
139 engine.default_model_id(),
140 "anthropic/claude-sonnet-4-20250514"
141 );
142 }
143
144 #[test]
145 fn test_resolve_model_not_found() {
146 let engine = OxiEngineProvider::new("anthropic/claude-sonnet-4-20250514");
147 let result = engine.resolve_model("nonexistent/model-xyz");
148 assert!(result.is_err());
149 }
150
151 #[test]
152 fn test_create_provider_anthropic() {
153 let engine = OxiEngineProvider::new("anthropic/claude-sonnet-4-20250514");
154 let provider = engine.create_provider("anthropic");
155 assert!(provider.is_ok());
156 }
157
158 #[test]
159 fn test_create_provider_not_found() {
160 let engine = OxiEngineProvider::new("anthropic/claude-sonnet-4-20250514");
161 let result = engine.create_provider("nonexistent_provider");
162 assert!(result.is_err());
163 }
164}