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 {
|
let base_candidate = match mode {
|
||||||
OllamaMode::Local => base_url,
|
OllamaMode::Local => base_url,
|
||||||
OllamaMode::Cloud => {
|
OllamaMode::Cloud => base_url.or(Some(CLOUD_BASE_URL)),
|
||||||
if base_is_cloud {
|
|
||||||
base_url
|
|
||||||
} else {
|
|
||||||
Some(CLOUD_BASE_URL)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let normalized_base_url =
|
let normalized_base_url =
|
||||||
@@ -1372,11 +1366,41 @@ impl OllamaProvider {
|
|||||||
format!("Ollama {action} internal error"),
|
format!("Ollama {action} internal error"),
|
||||||
Some(internal.message),
|
Some(internal.message),
|
||||||
),
|
),
|
||||||
OllamaError::Other(message) => self.provider_failure(
|
OllamaError::Other(message) => {
|
||||||
ProviderErrorKind::Unknown,
|
let parsed_error = serde_json::from_str::<Value>(&message)
|
||||||
format!("Ollama {action} failed"),
|
.ok()
|
||||||
Some(message),
|
.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::JsonError(err) => Error::Serialization(err),
|
||||||
OllamaError::ToolCallError(err) => self.provider_failure(
|
OllamaError::ToolCallError(err) => self.provider_failure(
|
||||||
ProviderErrorKind::Protocol,
|
ProviderErrorKind::Protocol,
|
||||||
@@ -2023,7 +2047,9 @@ fn normalize_base_url(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if mode_hint == OllamaMode::Cloud && url.scheme() != "https" {
|
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('/');
|
let path = url.path().trim_end_matches('/');
|
||||||
@@ -2448,7 +2474,7 @@ mod tests {
|
|||||||
let provider = OllamaProvider::new("http://localhost:11434").expect("provider constructed");
|
let provider = OllamaProvider::new("http://localhost:11434").expect("provider constructed");
|
||||||
|
|
||||||
let descriptor = McpToolDescriptor {
|
let descriptor = McpToolDescriptor {
|
||||||
name: "web.search".to_string(),
|
name: crate::tools::WEB_SEARCH_TOOL_NAME.to_string(),
|
||||||
description: "Perform a web search".to_string(),
|
description: "Perform a web search".to_string(),
|
||||||
input_schema: json!({
|
input_schema: json!({
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|||||||
Reference in New Issue
Block a user