mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
fix(observability): demote unknown-provider list_models error (Sentry TAURI-RUST-X) (#2783)
This commit is contained in:
@@ -9,10 +9,26 @@ use crate::openhuman::inference::{device, presets, sentiment, SentimentResult};
|
||||
use crate::openhuman::inference::{LocalAiEmbeddingResult, LocalAiStatus};
|
||||
use crate::rpc::RpcOutcome;
|
||||
use serde_json::{json, Value};
|
||||
use tracing::{debug, error};
|
||||
use tracing::{debug, error, warn};
|
||||
|
||||
const LOG_PREFIX: &str = "[inference::ops]";
|
||||
|
||||
/// User picked a provider id (slug) that isn't registered in the cloud
|
||||
/// provider list — e.g. selecting `"ollama"` as a cloud provider when it's
|
||||
/// actually a local runtime. Matches the literal phrase emitted at
|
||||
/// `src/openhuman/inference/provider/ops.rs:54`
|
||||
/// (`"no cloud provider with id or slug '{}' found"`).
|
||||
///
|
||||
/// Used by [`inference_list_models`] to demote this user-config case to
|
||||
/// `warn!` so it stops escalating to Sentry (TAURI-RUST-X, ~5740 events).
|
||||
/// The matcher is anchored on the exact phrase so unrelated sibling
|
||||
/// failures (TAURI-RUST-12 JSON parse, TAURI-RUST-2W reqwest builder,
|
||||
/// TAURI-RUST-JP local ollama_admin transport) still surface as real
|
||||
/// errors.
|
||||
fn is_unknown_provider_user_config(err: &str) -> bool {
|
||||
err.contains("no cloud provider with id or slug")
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize)]
|
||||
pub struct InferenceTestProviderModelResult {
|
||||
pub reply: String,
|
||||
@@ -245,7 +261,29 @@ pub async fn inference_list_models(provider_id: &str) -> Result<RpcOutcome<Value
|
||||
let result = providers::ops::list_configured_models(provider_id).await;
|
||||
match &result {
|
||||
Ok(_) => debug!("{LOG_PREFIX} list_models:ok"),
|
||||
Err(err) => error!(error = %err, "{LOG_PREFIX} list_models:error"),
|
||||
Err(err) => {
|
||||
if is_unknown_provider_user_config(err) {
|
||||
// User selected a provider id that isn't a registered
|
||||
// cloud provider (e.g. picking "ollama", a local runtime).
|
||||
// Demote to `warn!` so it stays in local logs but doesn't
|
||||
// escalate to Sentry. Targets TAURI-RUST-X (~5740 events).
|
||||
warn!(
|
||||
provider_id,
|
||||
error = %err,
|
||||
"{LOG_PREFIX} list_models:unknown-provider (user-config)"
|
||||
);
|
||||
} else {
|
||||
// Real error — embed `{err}` in the format string so
|
||||
// Sentry's event title carries the actionable cause
|
||||
// instead of the opaque `list_models:error` shape that
|
||||
// made TAURI-RUST-X untriageable.
|
||||
error!(
|
||||
provider_id,
|
||||
error = %err,
|
||||
"{LOG_PREFIX} list_models:error: {err}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
@@ -258,3 +258,61 @@ async fn inference_openai_oauth_disconnect_returns_removed_flag() {
|
||||
assert_eq!(outcome.value["disconnected"], true);
|
||||
assert_eq!(outcome.logs, vec!["openai oauth disconnected"]);
|
||||
}
|
||||
|
||||
// ── is_unknown_provider_user_config (TAURI-RUST-X) ───────────────────────
|
||||
//
|
||||
// `inference_list_models` calls `providers::ops::list_configured_models`,
|
||||
// which surfaces a `String` error when the user-selected provider id isn't
|
||||
// registered in the cloud-provider list (e.g. picking "ollama" — a local
|
||||
// runtime — as a cloud provider). The error string is emitted at
|
||||
// `src/openhuman/inference/provider/ops.rs:54`. Before this fix the emit
|
||||
// site at `inference/ops.rs:248` escalated every such error to `error!`,
|
||||
// which sentry-tracing ships to Sentry as `"[inference::ops]
|
||||
// list_models:error"` — 5740+ events with the underlying error hidden in
|
||||
// a tracing field where no Sentry classifier can reach it. The helper
|
||||
// gate keeps the demote anchored so unrelated failures (HTTP / JSON / IO)
|
||||
// still escalate.
|
||||
|
||||
#[test]
|
||||
fn is_unknown_provider_user_config_matches_canonical_emit_site_string() {
|
||||
// Verbatim shape from `provider/ops.rs:54`:
|
||||
// format!("no cloud provider with id or slug '{}' found", provider_id)
|
||||
// Latest TAURI-RUST-X event (Sentry id 95) carried provider_id="ollama";
|
||||
// every well-formed provider id slug must trigger the demote.
|
||||
assert!(is_unknown_provider_user_config(
|
||||
"no cloud provider with id or slug 'ollama' found"
|
||||
));
|
||||
assert!(is_unknown_provider_user_config(
|
||||
"no cloud provider with id or slug 'made-up-custom-id' found"
|
||||
));
|
||||
assert!(is_unknown_provider_user_config(
|
||||
"no cloud provider with id or slug '' found"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_unknown_provider_user_config_rejects_other_list_models_failures() {
|
||||
// Defense in depth: the sibling list_models Sentry issues
|
||||
// (TAURI-RUST-12 JSON parse, TAURI-RUST-2W HTTP builder, etc.) are
|
||||
// real bugs that MUST still escalate to Sentry. The matcher must stay
|
||||
// strictly anchored on the "no cloud provider with id or slug" phrase
|
||||
// so it can't accidentally silence them.
|
||||
for raw in [
|
||||
// TAURI-RUST-12 (362 events) — provider/ops.rs JSON decode failure
|
||||
"[providers][list_models] failed to parse JSON: error decoding response",
|
||||
// TAURI-RUST-2W (100 events) — provider/ops.rs reqwest builder failure
|
||||
"[providers][list_models] HTTP request failed: builder error",
|
||||
// TAURI-RUST-JP (8 events) — local_ai ollama_admin transport failure
|
||||
"[local_ai:ollama_admin] list_models: request send failed",
|
||||
// Generic shapes from elsewhere in the call chain
|
||||
"request timed out after 30s",
|
||||
"permission denied accessing config",
|
||||
"no cloud provider configured for slug 'openai' (role 'chat')",
|
||||
"",
|
||||
] {
|
||||
assert!(
|
||||
!is_unknown_provider_user_config(raw),
|
||||
"must NOT demote real error: {raw:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user