Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/openhuman/embeddings/ollama.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")],
Expand All @@ -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",
&[
Expand Down
60 changes: 60 additions & 0 deletions src/openhuman/embeddings/ollama_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,66 @@ 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"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[minor] is_some() doesn't pin the specific ExpectedErrorKind variant. If a future broader matcher catches this message but classifies it as a different kind, this test would pass silently.

Consider matching the exact variant for consistency with the other tests' precision:

assert_eq!(
    crate::core::observability::expected_error_kind(msg),
    Some(ExpectedErrorKind::LocalAiCapabilityUnavailable),
    "GP — LocalAiCapabilityUnavailable matcher must catch this; closed by this PR"
);

Not blocking — the other five tests all use assert_eq! with an exact expected value, so this would also bring stylistic consistency.

);
}

#[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_eq!(
crate::core::observability::expected_error_kind(msg),
Some(crate::core::observability::ExpectedErrorKind::LocalAiCapabilityUnavailable),
"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]
Expand Down
10 changes: 10 additions & 0 deletions src/openhuman/embeddings/openai_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,16 @@ async fn embed_connection_refused() {
assert!(err.is::<reqwest::Error>());
}

#[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]
Expand Down
Loading