From b574d952671813202915efb9d651fce5b87c6a38 Mon Sep 17 00:00:00 2001 From: Taimoor Date: Thu, 28 May 2026 09:45:33 +0500 Subject: [PATCH 1/2] fix(inference): suppress Sentry noise for Ollama 'does not support tools' streaming 400 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `stream_native_chat` error handler fired a Sentry event for every Ollama model that returns HTTP 400 "does not support tools", even though the retry loop in `chat()` already handles this case correctly by re-issuing the request without tool definitions. Root cause: the streaming error path checked `is_budget_exhausted_http_400`, `is_custom_openai_upstream_bad_request_http_400`, `is_provider_access_policy_denied_http_403`, and `is_provider_config_rejection_http` — but not `is_native_tool_schema_unsupported`, which already existed and matched the exact Ollama wire shape. The check was only present in the non-streaming `chat()` path (line 1871), so each streaming attempt produced a TAURI-RUST-4K7 Sentry event before the retry succeeded. Fix: add an `is_native_tool_schema_unsupported` check between the config-rejection and `should_report_provider_http_failure` arms in `stream_native_chat`'s non-2xx handler. Matched events are demoted to `log::info!` — the caller's retry loop handles the actual recovery. Tests: two new unit tests: - `is_native_tool_schema_unsupported_matches_ollama_does_not_support_tools` — pin detection of all known phrases, non-matching statuses, and unrelated 400 bodies - `err_supports_no_tools_retry_matches_streaming_error_format` — verify the retry predicate correctly parses the anyhow string produced by stream_native_chat Closes #2787 --- .../inference/provider/compatible.rs | 10 +++ .../inference/provider/compatible_tests.rs | 75 +++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/src/openhuman/inference/provider/compatible.rs b/src/openhuman/inference/provider/compatible.rs index f2789c296a..388863da88 100644 --- a/src/openhuman/inference/provider/compatible.rs +++ b/src/openhuman/inference/provider/compatible.rs @@ -981,6 +981,16 @@ impl OpenAiCompatibleProvider { Some(native_request.model.as_str()), status, ); + } else if Self::is_native_tool_schema_unsupported(status, &body) { + // Model rejects tool definitions (e.g. Ollama "does not support tools"). + // The caller's retry loop already handles this by re-issuing without + // tools — suppress the Sentry event so noise doesn't accumulate for + // every model that lacks tool-calling support (TAURI-RUST-4K7). + log::info!( + "[stream] {} model rejected tool schema (status={}) — caller will retry without tools", + self.name, + status, + ); } else if super::should_report_provider_http_failure(status) { crate::core::observability::report_error( message.as_str(), diff --git a/src/openhuman/inference/provider/compatible_tests.rs b/src/openhuman/inference/provider/compatible_tests.rs index 3e1b7dbff1..510699926e 100644 --- a/src/openhuman/inference/provider/compatible_tests.rs +++ b/src/openhuman/inference/provider/compatible_tests.rs @@ -1556,3 +1556,78 @@ fn enrich_404_message_adds_hint_when_no_fallback() { "must not add hint when fallback is enabled: {result_with_fallback}" ); } + +/// Verify that `is_native_tool_schema_unsupported` correctly detects the Ollama +/// "does not support tools" 400 error (TAURI-RUST-4K7). The function must return +/// `true` for every phrase in the list and `false` for unrelated 400 bodies and +/// for non-400 statuses — so `stream_native_chat` can suppress the Sentry event +/// and the retry loop in `chat()` can re-issue the request without tools. +#[test] +fn is_native_tool_schema_unsupported_matches_ollama_does_not_support_tools() { + use reqwest::StatusCode; + + let bodies = [ + r#"{"error":{"message":"registry.ollama.ai/library/gemma3:1b-it-qat does not support tools","type":"invalid_request_error","param":null,"code":null}}"#, + r#"{"error": "function calling is not supported by this model"}"#, + r#"{"error": {"message": "unknown parameter: tools"}}"#, + r#"{"error": "unsupported parameter: tools"}"#, + r#"{"error": "unrecognized field `tools`"}"#, + r#"{"error": "tool_choice is not supported"}"#, + ]; + + for body in &bodies { + assert!( + OpenAiCompatibleProvider::is_native_tool_schema_unsupported( + StatusCode::BAD_REQUEST, + body + ), + "expected detection for body: {body}" + ); + } + + // Non-400 status must not match even with the right body. + assert!( + !OpenAiCompatibleProvider::is_native_tool_schema_unsupported( + StatusCode::INTERNAL_SERVER_ERROR, + r#"{"error":"does not support tools"}"#, + ), + "500 with tool-schema body must not match" + ); + + // Unrelated 400 bodies must not match. + for body in [ + r#"{"error": "model not found"}"#, + r#"{"error": "invalid temperature"}"#, + r#"{"error": "model field is required"}"#, + ] { + assert!( + !OpenAiCompatibleProvider::is_native_tool_schema_unsupported( + StatusCode::BAD_REQUEST, + body + ), + "must not false-positive on unrelated body: {body}" + ); + } +} + +/// Verify `err_supports_no_tools_retry` correctly parses the anyhow error string +/// that `stream_native_chat` returns for a "does not support tools" 400, so the +/// retry loop in `chat()` can act on it. +#[test] +fn err_supports_no_tools_retry_matches_streaming_error_format() { + // Simulate the anyhow message produced by stream_native_chat: + // "{name} streaming API error ({status}): {sanitized_body}" + let err = r#"ollama streaming API error (400 Bad Request): {"error":{"message":"registry.ollama.ai/library/gemma3:1b-it-qat does not support tools","type":"invalid_request_error"}}"#; + assert!( + OpenAiCompatibleProvider::err_supports_no_tools_retry(err), + "must match Ollama streaming API error with tools message" + ); + + // Unrelated streaming error must not match. + assert!( + !OpenAiCompatibleProvider::err_supports_no_tools_retry( + "ollama streaming API error (500 Internal Server Error): upstream crashed" + ), + "must not false-positive on unrelated streaming error" + ); +} From 46b010ea9049f3c0b10be4c1201bfadd56f6e301 Mon Sep 17 00:00:00 2001 From: Taimoor Date: Fri, 29 May 2026 04:24:51 +0500 Subject: [PATCH 2/2] ci: retrigger after sccache infrastructure flake (os error 10054)