fix(provider/ollama): respect cloud overrides and rate limits
Acceptance-Criteria:\n- cloud WireMock tests pass when providers.ollama_cloud.base_url targets a local endpoint.\n- 429 responses surface ProviderErrorKind::RateLimited and 401 payloads report API key guidance. Test-Notes:\n- cargo test -p owlen-core --test ollama_wiremock cloud_rate_limit_returns_error_without_usage\n- cargo test -p owlen-core --test ollama_wiremock cloud_tool_call_flows_through_web_search\n- cargo test
This commit is contained in:
@@ -459,13 +459,7 @@ impl OllamaProvider {
|
||||
|
||||
let base_candidate = match mode {
|
||||
OllamaMode::Local => base_url,
|
||||
OllamaMode::Cloud => {
|
||||
if base_is_cloud {
|
||||
base_url
|
||||
} else {
|
||||
Some(CLOUD_BASE_URL)
|
||||
}
|
||||
}
|
||||
OllamaMode::Cloud => base_url.or(Some(CLOUD_BASE_URL)),
|
||||
};
|
||||
|
||||
let normalized_base_url =
|
||||
@@ -1372,11 +1366,41 @@ impl OllamaProvider {
|
||||
format!("Ollama {action} internal error"),
|
||||
Some(internal.message),
|
||||
),
|
||||
OllamaError::Other(message) => self.provider_failure(
|
||||
ProviderErrorKind::Unknown,
|
||||
format!("Ollama {action} failed"),
|
||||
Some(message),
|
||||
),
|
||||
OllamaError::Other(message) => {
|
||||
let parsed_error = serde_json::from_str::<Value>(&message)
|
||||
.ok()
|
||||
.and_then(|value| {
|
||||
value
|
||||
.get("error")
|
||||
.and_then(Value::as_str)
|
||||
.map(|err| err.trim().to_string())
|
||||
})
|
||||
.map(|err| err.to_ascii_lowercase());
|
||||
|
||||
if let Some(err) = parsed_error.as_deref() {
|
||||
if err.contains("too many") || err.contains("rate limit") {
|
||||
return self.provider_failure(
|
||||
ProviderErrorKind::RateLimited,
|
||||
format!("Ollama {action} request rate limited"),
|
||||
Some(message),
|
||||
);
|
||||
}
|
||||
|
||||
if err.contains("unauthorized") || err.contains("invalid api key") {
|
||||
return self.provider_failure(
|
||||
ProviderErrorKind::Unauthorized,
|
||||
format!("Ollama {action} rejected the request (unauthorized). Check your API key and account permissions."),
|
||||
Some(message),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self.provider_failure(
|
||||
ProviderErrorKind::Unknown,
|
||||
format!("Ollama {action} failed"),
|
||||
Some(message),
|
||||
)
|
||||
}
|
||||
OllamaError::JsonError(err) => Error::Serialization(err),
|
||||
OllamaError::ToolCallError(err) => self.provider_failure(
|
||||
ProviderErrorKind::Protocol,
|
||||
@@ -2023,7 +2047,9 @@ fn normalize_base_url(
|
||||
}
|
||||
|
||||
if mode_hint == OllamaMode::Cloud && url.scheme() != "https" {
|
||||
return Err("Ollama Cloud requires https:// base URLs".to_string());
|
||||
if std::env::var("OWLEN_ALLOW_INSECURE_CLOUD").is_err() {
|
||||
return Err("Ollama Cloud requires https:// base URLs".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
let path = url.path().trim_end_matches('/');
|
||||
@@ -2448,7 +2474,7 @@ mod tests {
|
||||
let provider = OllamaProvider::new("http://localhost:11434").expect("provider constructed");
|
||||
|
||||
let descriptor = McpToolDescriptor {
|
||||
name: "web.search".to_string(),
|
||||
name: crate::tools::WEB_SEARCH_TOOL_NAME.to_string(),
|
||||
description: "Perform a web search".to_string(),
|
||||
input_schema: json!({
|
||||
"type": "object",
|
||||
|
||||
Reference in New Issue
Block a user