refactor(ollama): replace handcrafted HTTP logic with ollama‑rs client and simplify request handling
- Switch to `ollama-rs` crate for chat, model listing, and streaming. - Remove custom request building, authentication handling, and debug logging. - Drop unsupported tool conversion; now ignore tool descriptors with a warning. - Refactor model fetching to use local model info and optional cloud details. - Consolidate error mapping via `map_ollama_error`. - Update health check to use the new HTTP client. - Delete obsolete `provider_interface.rs` test as the provider interface has changed.
This commit is contained in:
@@ -134,10 +134,13 @@ impl Config {
|
||||
config.ensure_defaults();
|
||||
config.mcp.apply_backward_compat();
|
||||
config.apply_schema_migrations(&previous_version);
|
||||
config.expand_provider_env_vars()?;
|
||||
config.validate()?;
|
||||
Ok(config)
|
||||
} else {
|
||||
Ok(Config::default())
|
||||
let mut config = Config::default();
|
||||
config.expand_provider_env_vars()?;
|
||||
Ok(config)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,6 +204,13 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_provider_env_vars(&mut self) -> Result<()> {
|
||||
for (provider_name, provider) in self.providers.iter_mut() {
|
||||
expand_provider_entry(provider_name, provider)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validate configuration invariants and surface actionable error messages.
|
||||
pub fn validate(&self) -> Result<()> {
|
||||
self.validate_default_provider()?;
|
||||
@@ -336,6 +346,56 @@ fn default_ollama_provider_config() -> ProviderConfig {
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_provider_entry(provider_name: &str, provider: &mut ProviderConfig) -> Result<()> {
|
||||
if let Some(ref mut base_url) = provider.base_url {
|
||||
let expanded = expand_env_string(
|
||||
base_url.as_str(),
|
||||
&format!("providers.{provider_name}.base_url"),
|
||||
)?;
|
||||
*base_url = expanded;
|
||||
}
|
||||
|
||||
if let Some(ref mut api_key) = provider.api_key {
|
||||
let expanded = expand_env_string(
|
||||
api_key.as_str(),
|
||||
&format!("providers.{provider_name}.api_key"),
|
||||
)?;
|
||||
*api_key = expanded;
|
||||
}
|
||||
|
||||
for (extra_key, extra_value) in provider.extra.iter_mut() {
|
||||
if let serde_json::Value::String(current) = extra_value {
|
||||
let expanded = expand_env_string(
|
||||
current.as_str(),
|
||||
&format!("providers.{provider_name}.{}", extra_key),
|
||||
)?;
|
||||
*current = expanded;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expand_env_string(input: &str, field_path: &str) -> Result<String> {
|
||||
if !input.contains('$') {
|
||||
return Ok(input.to_string());
|
||||
}
|
||||
|
||||
match shellexpand::env(input) {
|
||||
Ok(expanded) => Ok(expanded.into_owned()),
|
||||
Err(err) => match err.cause {
|
||||
std::env::VarError::NotPresent => Err(crate::Error::Config(format!(
|
||||
"Environment variable {} referenced in {field_path} is not set",
|
||||
err.var_name
|
||||
))),
|
||||
std::env::VarError::NotUnicode(_) => Err(crate::Error::Config(format!(
|
||||
"Environment variable {} referenced in {field_path} contains invalid Unicode",
|
||||
err.var_name
|
||||
))),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Default configuration path with user home expansion
|
||||
pub fn default_config_path() -> PathBuf {
|
||||
if let Some(config_dir) = dirs::config_dir() {
|
||||
@@ -836,6 +896,48 @@ pub fn session_timeout(config: &Config) -> Duration {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn expand_provider_env_vars_resolves_api_key() {
|
||||
std::env::set_var("OWLEN_TEST_API_KEY", "super-secret");
|
||||
|
||||
let mut config = Config::default();
|
||||
if let Some(ollama) = config.providers.get_mut("ollama") {
|
||||
ollama.api_key = Some("${OWLEN_TEST_API_KEY}".to_string());
|
||||
}
|
||||
|
||||
config
|
||||
.expand_provider_env_vars()
|
||||
.expect("environment expansion succeeded");
|
||||
|
||||
assert_eq!(
|
||||
config.providers["ollama"].api_key.as_deref(),
|
||||
Some("super-secret")
|
||||
);
|
||||
|
||||
std::env::remove_var("OWLEN_TEST_API_KEY");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expand_provider_env_vars_errors_for_missing_variable() {
|
||||
std::env::remove_var("OWLEN_TEST_MISSING");
|
||||
|
||||
let mut config = Config::default();
|
||||
if let Some(ollama) = config.providers.get_mut("ollama") {
|
||||
ollama.api_key = Some("${OWLEN_TEST_MISSING}".to_string());
|
||||
}
|
||||
|
||||
let error = config
|
||||
.expand_provider_env_vars()
|
||||
.expect_err("missing variables should error");
|
||||
|
||||
match error {
|
||||
crate::Error::Config(message) => {
|
||||
assert!(message.contains("OWLEN_TEST_MISSING"));
|
||||
}
|
||||
other => panic!("expected config error, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_storage_platform_specific_paths() {
|
||||
let config = Config::default();
|
||||
|
||||
Reference in New Issue
Block a user