From b0559c240d3a2b6965b63a4569c140615bbb5fbc Mon Sep 17 00:00:00 2001 From: Shanu Date: Wed, 20 May 2026 18:01:21 +0530 Subject: [PATCH 1/4] feat(inference): enhance error logging for provider configuration rejections in OpenAiCompatibleProvider --- .../inference/provider/compatible.rs | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/openhuman/inference/provider/compatible.rs b/src/openhuman/inference/provider/compatible.rs index d17bf8dd17..1d21c66881 100644 --- a/src/openhuman/inference/provider/compatible.rs +++ b/src/openhuman/inference/provider/compatible.rs @@ -490,6 +490,13 @@ impl OpenAiCompatibleProvider { Some(model), status, ); + } else if super::is_provider_config_rejection_http(status, self.name.as_str(), &error) { + super::log_provider_config_rejection( + "responses_api", + self.name.as_str(), + Some(model), + status, + ); } else if super::should_report_provider_http_failure(status) { crate::core::observability::report_error( message.as_str(), @@ -856,6 +863,13 @@ impl OpenAiCompatibleProvider { Some(native_request.model.as_str()), status, ); + } else if super::is_provider_config_rejection_http(status, self.name.as_str(), &body) { + super::log_provider_config_rejection( + "streaming_chat", + self.name.as_str(), + Some(native_request.model.as_str()), + status, + ); } else if super::should_report_provider_http_failure(status) { crate::core::observability::report_error( message.as_str(), @@ -1348,6 +1362,13 @@ impl Provider for OpenAiCompatibleProvider { Some(model), status, ); + } else if super::is_provider_config_rejection_http(status, self.name.as_str(), &error) { + super::log_provider_config_rejection( + "chat_completions", + self.name.as_str(), + Some(model), + status, + ); } else if super::should_report_provider_http_failure(status) { crate::core::observability::report_error( message.as_str(), @@ -1797,6 +1818,13 @@ impl Provider for OpenAiCompatibleProvider { Some(model), status, ); + } else if super::is_provider_config_rejection_http(status, self.name.as_str(), &error) { + super::log_provider_config_rejection( + "native_chat", + self.name.as_str(), + Some(model), + status, + ); } else if super::should_report_provider_http_failure(status) { crate::core::observability::report_error( message.as_str(), @@ -1952,6 +1980,17 @@ impl Provider for OpenAiCompatibleProvider { Some(model_owned.as_str()), status, ); + } else if super::is_provider_config_rejection_http( + status, + provider_name.as_str(), + &raw_error, + ) { + super::log_provider_config_rejection( + "stream_chat", + provider_name.as_str(), + Some(model_owned.as_str()), + status, + ); } else if super::should_report_provider_http_failure(status) { crate::core::observability::report_error( message.as_str(), From 41d36123616603970381034411ae9292800739fe Mon Sep 17 00:00:00 2001 From: Shanu Date: Wed, 20 May 2026 18:01:46 +0530 Subject: [PATCH 2/4] fix(inference): correct formatting in error reporting for OpenAiCompatibleProvider --- src/openhuman/inference/provider/compatible.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openhuman/inference/provider/compatible.rs b/src/openhuman/inference/provider/compatible.rs index 1d21c66881..dccfea5fe1 100644 --- a/src/openhuman/inference/provider/compatible.rs +++ b/src/openhuman/inference/provider/compatible.rs @@ -496,7 +496,7 @@ impl OpenAiCompatibleProvider { self.name.as_str(), Some(model), status, - ); + ); } else if super::should_report_provider_http_failure(status) { crate::core::observability::report_error( message.as_str(), From 86ddc6f1d861c924ae269a22effb63cb58be6e55 Mon Sep 17 00:00:00 2001 From: Shanu Date: Thu, 21 May 2026 11:59:19 +0530 Subject: [PATCH 3/4] test(inference): add unit test for streaming chat config rejection without Sentry report --- .../inference/provider/compatible_tests.rs | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/src/openhuman/inference/provider/compatible_tests.rs b/src/openhuman/inference/provider/compatible_tests.rs index 7285dac030..84dd205c2d 100644 --- a/src/openhuman/inference/provider/compatible_tests.rs +++ b/src/openhuman/inference/provider/compatible_tests.rs @@ -1,4 +1,8 @@ use super::*; +use sentry::test::TestTransport; +use std::sync::Arc; +use wiremock::matchers::{method, path}; +use wiremock::{Mock, MockServer, ResponseTemplate}; fn make_provider(name: &str, url: &str, key: Option<&str>) -> OpenAiCompatibleProvider { OpenAiCompatibleProvider::new(name, url, key, AuthStyle::Bearer) @@ -374,6 +378,73 @@ async fn chat_via_responses_requires_non_system_message() { .contains("requires at least one non-system message")); } +#[tokio::test] +async fn streaming_chat_config_rejection_propagates_error_without_sentry_report() { + // Representative guardrail for the new provider-config-rejection + // suppression branches in compatible.rs: streaming_chat should still + // return an error, but it must not call report_error/Sentry for this + // deterministic user-config state. + let mock_server = MockServer::start().await; + Mock::given(method("POST")) + .and(path("/chat/completions")) + .respond_with( + ResponseTemplate::new(400) + .set_body_string("invalid temperature: only 1 is allowed for this model"), + ) + .mount(&mock_server) + .await; + + let transport = TestTransport::new(); + let sentry_options = sentry::ClientOptions { + dsn: Some("https://public@sentry.invalid/1".parse().unwrap()), + transport: Some(Arc::new(transport.clone())), + ..Default::default() + }; + let sentry_hub = Arc::new(sentry::Hub::new( + Some(Arc::new(sentry_options.into())), + Arc::new(Default::default()), + )); + let _sentry_guard = sentry::HubSwitchGuard::new(sentry_hub); + + let provider = OpenAiCompatibleProvider::new( + "custom_openai", + &mock_server.uri(), + None, + AuthStyle::None, + ); + let request = NativeChatRequest { + model: "kimi-k2".to_string(), + messages: vec![NativeMessage { + role: "user".to_string(), + content: Some("hello".to_string()), + tool_call_id: None, + tool_calls: None, + }], + temperature: Some(0.7), + stream: Some(true), + tools: None, + tool_choice: None, + thread_id: None, + stream_options: Some(super::compatible_types::OpenAiStreamOptions { + include_usage: true, + }), + }; + let (delta_tx, _delta_rx) = tokio::sync::mpsc::channel(8); + + let err = provider + .stream_native_chat(None, &request, &delta_tx, 0) + .await + .expect_err("400 provider config-rejection must still propagate as Err"); + assert!( + err.to_string().contains("streaming API error"), + "err: {err}" + ); + assert!( + transport.fetch_and_clear_events().is_empty(), + "provider config-rejection must not be reported to Sentry" + ); +} + // ---------------------------------------------------------- // Custom endpoint path tests (Issue #114) // ---------------------------------------------------------- From 72dda7d11055a1aaf4f9f37a5027e00425309eab Mon Sep 17 00:00:00 2001 From: Shanu Date: Thu, 21 May 2026 11:59:41 +0530 Subject: [PATCH 4/4] refactor(inference): simplify instantiation of OpenAiCompatibleProvider in streaming chat test --- src/openhuman/inference/provider/compatible_tests.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/openhuman/inference/provider/compatible_tests.rs b/src/openhuman/inference/provider/compatible_tests.rs index 84dd205c2d..fd7d0266e7 100644 --- a/src/openhuman/inference/provider/compatible_tests.rs +++ b/src/openhuman/inference/provider/compatible_tests.rs @@ -406,12 +406,8 @@ async fn streaming_chat_config_rejection_propagates_error_without_sentry_report( )); let _sentry_guard = sentry::HubSwitchGuard::new(sentry_hub); - let provider = OpenAiCompatibleProvider::new( - "custom_openai", - &mock_server.uri(), - None, - AuthStyle::None, - ); + let provider = + OpenAiCompatibleProvider::new("custom_openai", &mock_server.uri(), None, AuthStyle::None); let request = NativeChatRequest { model: "kimi-k2".to_string(), messages: vec![NativeMessage {