🐛 Fix Ollama cloud detection — ollama.com uses native /api/chat + Bearer auth
This commit is contained in:
@@ -37,13 +37,41 @@ impl OllamaProvider {
|
||||
}
|
||||
|
||||
/// Returns true if this is an OpenAI-compatible endpoint (not native Ollama).
|
||||
/// Heuristic: if api_key is set or URL is not localhost/127.0.0.1, treat as OpenAI-compatible.
|
||||
/// OpenAI-compatible endpoints use /chat/completions with Bearer auth.
|
||||
/// Ollama-native endpoints use /api/chat (local or cloud with Bearer).
|
||||
fn is_openai_compatible(&self) -> bool {
|
||||
if self.api_key.is_some() {
|
||||
let lower = self.base_url.to_lowercase();
|
||||
|
||||
// ollama.com always uses native /api/chat (even with api_key)
|
||||
if lower.contains("ollama.com") {
|
||||
return false;
|
||||
}
|
||||
// Localhost is always native
|
||||
if lower.contains("localhost") || lower.contains("127.0.0.1") || lower.contains("0.0.0.0") {
|
||||
return false;
|
||||
}
|
||||
// URLs with /v1 or /openai/ in the path are OpenAI-compatible
|
||||
if lower.contains("/v1") || lower.contains("/openai/") {
|
||||
return true;
|
||||
}
|
||||
// Remote + api_key: probably OpenAI-compatible
|
||||
// Remote + no api_key: probably native
|
||||
self.api_key.is_some()
|
||||
}
|
||||
|
||||
/// Normalize the base URL: strip /v1 for ollama.com, upgrade http→https
|
||||
fn normalized_base_url(&self) -> String {
|
||||
let lower = self.base_url.to_lowercase();
|
||||
!lower.contains("localhost") && !lower.contains("127.0.0.1") && !lower.contains("0.0.0.0")
|
||||
if lower.contains("ollama.com") {
|
||||
// Strip /v1 suffix if present and upgrade to https
|
||||
let cleaned = self.base_url
|
||||
.replace("http://", "https://")
|
||||
.trim_end_matches('/')
|
||||
.trim_end_matches("/v1")
|
||||
.to_string();
|
||||
return cleaned;
|
||||
}
|
||||
self.base_url.trim_end_matches('/').to_string()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +105,7 @@ impl Provider for OllamaProvider {
|
||||
"stream": false,
|
||||
});
|
||||
|
||||
let url = format!("{}/chat/completions", self.base_url);
|
||||
let url = format!("{}/chat/completions", self.normalized_base_url());
|
||||
let mut request = self.client.post(&url).json(&body);
|
||||
|
||||
// Add bearer auth if api_key is set
|
||||
@@ -135,20 +163,24 @@ impl Provider for OllamaProvider {
|
||||
}
|
||||
});
|
||||
|
||||
let url = format!("{}/api/chat", self.base_url);
|
||||
let response = self
|
||||
let url = format!("{}/api/chat", self.normalized_base_url());
|
||||
let mut request = self
|
||||
.client
|
||||
.post(&url)
|
||||
.json(&body)
|
||||
.send()
|
||||
.await
|
||||
.json(&body);
|
||||
|
||||
// Add bearer auth if api_key is set (ollama.com cloud needs this)
|
||||
if let Some(ref key) = self.api_key {
|
||||
request = request.header("Authorization", format!("Bearer {}", key));
|
||||
}
|
||||
|
||||
let response = request.send().await
|
||||
.map_err(|e| gyandoot_core::ProviderError::Request(e))?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
return Err(gyandoot_core::ProviderError::Api {
|
||||
status: response.status().as_u16(),
|
||||
message: format!("Ollama API error: {}", response.status()),
|
||||
});
|
||||
let status = response.status().as_u16();
|
||||
let error_text = response.text().await.unwrap_or_default();
|
||||
return Err(gyandoot_core::ProviderError::from_response(status, &error_text));
|
||||
}
|
||||
|
||||
let data: serde_json::Value = response.json().await.map_err(|e| {
|
||||
@@ -198,22 +230,32 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_openai_compatible_with_api_key() {
|
||||
fn test_localhost_with_key_is_native() {
|
||||
let provider = OllamaProvider::new(
|
||||
"http://localhost:11434".to_string(),
|
||||
"llama3".to_string(),
|
||||
Some("test-key".to_string()),
|
||||
);
|
||||
assert!(provider.is_openai_compatible());
|
||||
assert!(!provider.is_openai_compatible());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_openai_compatible_with_remote_url() {
|
||||
fn test_is_ollama_dot_com_is_native() {
|
||||
let provider = OllamaProvider::new(
|
||||
"http://ollama.com/v1".to_string(),
|
||||
"qwen3.5".to_string(),
|
||||
None,
|
||||
);
|
||||
assert!(!provider.is_openai_compatible());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_openai_compatible_with_openai_url() {
|
||||
let provider = OllamaProvider::new(
|
||||
"https://api.deepseek.com/v1".to_string(),
|
||||
"deepseek-chat".to_string(),
|
||||
Some("key".to_string()),
|
||||
);
|
||||
assert!(provider.is_openai_compatible());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user