From 1a22b1ddc61d3bb6fdf7727ab5c4e567ab225ec5 Mon Sep 17 00:00:00 2001 From: oxoxDev Date: Tue, 19 May 2026 18:58:48 +0530 Subject: [PATCH 1/2] fix: route embedding errors through expected classifier --- src/openhuman/embeddings/ollama.rs | 8 ++-- src/openhuman/embeddings/ollama_tests.rs | 59 ++++++++++++++++++++++++ src/openhuman/embeddings/openai.rs | 4 +- src/openhuman/embeddings/openai_tests.rs | 10 ++++ 4 files changed, 75 insertions(+), 6 deletions(-) diff --git a/src/openhuman/embeddings/ollama.rs b/src/openhuman/embeddings/ollama.rs index be569473df..68d0cd3d62 100644 --- a/src/openhuman/embeddings/ollama.rs +++ b/src/openhuman/embeddings/ollama.rs @@ -244,8 +244,8 @@ impl EmbeddingProvider for OllamaEmbedding { "ollama embed request failed (is Ollama running at {}?): {e}", self.base_url ); - crate::core::observability::report_error( - message.as_str(), + crate::core::observability::report_error_or_expected( + &message, "embeddings", "ollama_embed", &[("model", self.model.as_str()), ("failure", "transport")], @@ -266,8 +266,8 @@ impl EmbeddingProvider for OllamaEmbedding { format!(": {detail}") } ); - crate::core::observability::report_error( - message.as_str(), + crate::core::observability::report_error_or_expected( + &message, "embeddings", "ollama_embed", &[ diff --git a/src/openhuman/embeddings/ollama_tests.rs b/src/openhuman/embeddings/ollama_tests.rs index 17eeb35cff..86ae817156 100644 --- a/src/openhuman/embeddings/ollama_tests.rs +++ b/src/openhuman/embeddings/ollama_tests.rs @@ -327,6 +327,65 @@ async fn embed_connection_refused() { ); } +// OPENHUMAN-TAURI-{GP,MA,KM,GX} wire shapes — currently routed through +// `report_error_or_expected` (Sentry classifier ladder) by this PR. The ladder +// matches GP (LocalAiCapabilityUnavailable) today; MA/KM/GX still fall through +// to capture because `observability::expected_error_kind` has no matcher arm +// for "ollama model not found" / "ollama daemon unreachable". Those matcher +// arms are blocked behind PR #2063 + #2188 merging (both touch +// `src/core/observability.rs`) and will land in the follow-up classifier +// batch. Tests below lock the CURRENT state so the follow-up flips them. + +#[test] +fn ma_wire_shape_current_state_unclassified() { + let msg = r#"ollama embed failed with status 404 Not Found: {"error":"model \"bge-m3\" not found, try pulling it first"}"#; + assert_eq!( + crate::core::observability::expected_error_kind(msg), + None, + "MA — matcher arm pending follow-up classifier batch (post #2063 + #2188 merge)" + ); +} + +#[test] +fn km_wire_shape_current_state_unclassified() { + let msg = r#"ollama embed failed with status 404 Not Found: {"error":"model \"nomic-embed-text:latest\" not found, try pulling it first"}"#; + assert_eq!( + crate::core::observability::expected_error_kind(msg), + None, + "KM — matcher arm pending follow-up classifier batch" + ); +} + +#[test] +fn gp_wire_shape_classifies() { + let msg = + "Vision is disabled for this RAM tier. Switch to the 4-8 GB tier or above to enable it."; + assert!( + crate::core::observability::expected_error_kind(msg).is_some(), + "GP — LocalAiCapabilityUnavailable matcher must catch this; closed by this PR" + ); +} + +#[test] +fn gx_wire_shape_current_state_unclassified() { + let msg = "ollama embeddings opted-in but daemon unreachable at http://localhost:11434; falling back to cloud embeddings for this session"; + assert_eq!( + crate::core::observability::expected_error_kind(msg), + None, + "GX — matcher arm pending follow-up classifier batch" + ); +} + +#[test] +fn ollama_parse_error_wire_shape_stays_unexpected() { + let msg = "ollama embed response parse failed: invalid type: expected sequence"; + assert_eq!( + crate::core::observability::expected_error_kind(msg), + None, + "real parse bugs must still reach Sentry" + ); +} + // ── embed_one (trait default) ─────────────────────────── #[tokio::test] diff --git a/src/openhuman/embeddings/openai.rs b/src/openhuman/embeddings/openai.rs index 1d74e49a52..7b993ebeab 100644 --- a/src/openhuman/embeddings/openai.rs +++ b/src/openhuman/embeddings/openai.rs @@ -129,8 +129,8 @@ impl EmbeddingProvider for OpenAiEmbedding { "[openai] embed error: status={status}, body={text}" ); let message = format!("Embedding API error {status}: {text}"); - crate::core::observability::report_error( - message.as_str(), + crate::core::observability::report_error_or_expected( + &message, "embeddings", "openai_embed", &[ diff --git a/src/openhuman/embeddings/openai_tests.rs b/src/openhuman/embeddings/openai_tests.rs index 527bdb9fc6..1e1e44b5e3 100644 --- a/src/openhuman/embeddings/openai_tests.rs +++ b/src/openhuman/embeddings/openai_tests.rs @@ -326,6 +326,16 @@ async fn embed_connection_refused() { assert!(err.is::()); } +#[test] +fn openai_embedding_api_error_stays_unexpected() { + let msg = "Embedding API error 401 Unauthorized: invalid_api_key"; + assert_eq!( + crate::core::observability::expected_error_kind(msg), + None, + "OpenAI API key errors should continue to reach Sentry" + ); +} + // ── embed_one (trait default) ─────────────────────────── #[tokio::test] From 65ea727bd53f0a7cd04fcd71c83ec7dff3567997 Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Tue, 19 May 2026 20:09:59 -0700 Subject: [PATCH 2/2] test(embeddings): pin GP wire-shape to LocalAiCapabilityUnavailable variant Matches the exact ExpectedErrorKind variant in gp_wire_shape_classifies instead of `is_some()`, for stylistic consistency with the other classifier tests and to avoid passing if a future broader matcher catches the message under a different variant. Addresses @graycyrus review comment on ollama_tests.rs:355. --- src/openhuman/embeddings/ollama_tests.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/openhuman/embeddings/ollama_tests.rs b/src/openhuman/embeddings/ollama_tests.rs index 86ae817156..41a3f11984 100644 --- a/src/openhuman/embeddings/ollama_tests.rs +++ b/src/openhuman/embeddings/ollama_tests.rs @@ -360,8 +360,9 @@ fn km_wire_shape_current_state_unclassified() { fn gp_wire_shape_classifies() { let msg = "Vision is disabled for this RAM tier. Switch to the 4-8 GB tier or above to enable it."; - assert!( - crate::core::observability::expected_error_kind(msg).is_some(), + assert_eq!( + crate::core::observability::expected_error_kind(msg), + Some(crate::core::observability::ExpectedErrorKind::LocalAiCapabilityUnavailable), "GP — LocalAiCapabilityUnavailable matcher must catch this; closed by this PR" ); }