Skip to content
Open
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
46 changes: 46 additions & 0 deletions src/openhuman/inference/provider/config_rejection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,17 @@ pub fn is_provider_config_rejection_message(body: &str) -> bool {
// this is the `type` field used by litellm/Anthropic-style
// envelopes for the same class of user-state error.
"not_found_error",
// TAURI-RUST-1V — reliable-provider chain rolls up exhausted
// fallbacks into `All providers/models failed. Attempts:\n…\nThe
// model `<id>` may not be available on your provider. Configure
// a fallback chain via `reliability.model_fallbacks` in …`.
// Emitted at `src/openhuman/inference/provider/reliable.rs:332`.
// The remediation is "fix your `model_fallbacks` config" — pure
// user-config, nothing Sentry can act on. Anchor on the canonical
// remediation phrase so this doesn't collide with unrelated
// mentions of model availability (`reliable.rs:332` is the sole
// producer in-tree).
"may not be available on your provider",
];

let lower = body.to_ascii_lowercase();
Expand Down Expand Up @@ -288,6 +299,41 @@ mod tests {
}
}

#[test]
fn detects_reliable_chain_exhaustion_rollup() {
// TAURI-RUST-1V — `reliable.rs:325` rolls every attempt into
// `All providers/models failed. Attempts:\n…\nThe model `<id>`
// may not be available on your provider. Configure a fallback
// chain via `reliability.model_fallbacks` in …`. The wrapped err
// bubbles to `memory_sync::composio::bus` which previously
// emitted it as a raw `tracing::error!` — 10.7k events / 14d on
// self-hosted Sentry. The remediation lives entirely in the
// user's `reliability.model_fallbacks` config; Sentry has no
// remediation path.
let rollup = "All providers/models failed. Attempts:\n\
provider=openhuman model=gemini-3-flash-preview attempt 1/3: \
non_retryable; error=custom_openai API error (404 Not Found): \
<html>...</html>\n\
The model `gemini-3-flash-preview` may not be available on \
your provider. Configure a fallback chain via \
`reliability.model_fallbacks` in your config to route around \
unavailable models.";
assert!(
is_provider_config_rejection_message(rollup),
"TAURI-RUST-1V multi-line rollup must classify as provider config-rejection"
);

// Single-line `reliable.rs:332` emission (without the outer
// rollup wrapper) also matches — defensive against callers that
// surface only the inner remediation message.
let bare = "The model `chat-v1` may not be available on your provider. \
Configure a fallback chain via `reliability.model_fallbacks` in …";
assert!(
is_provider_config_rejection_message(bare),
"bare `may not be available on your provider` phrase must classify"
);
}

#[test]
fn unknown_model_helper_matches_openai_compatible_bodies() {
// TAURI-RUST-2Z1 — the OpenHuman hosted backend now emits the
Expand Down
24 changes: 20 additions & 4 deletions src/openhuman/memory_sync/composio/bus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,10 +352,26 @@ impl EventHandler for ComposioTriggerSubscriber {
);
}
Err(e) => {
tracing::error!(
label = %envelope.display_label,
error = %e,
"[composio][triage] run_triage failed"
// Route through the central observability classifier
// so user-config / budget-exhausted / provider-state
// rollups from `reliable.rs` (e.g. `The model
// \`<id>\` may not be available on your provider …`)
// get demoted to info-level breadcrumbs instead of
// surfacing as raw Sentry errors. Previously this
// call used `tracing::error!` directly and bypassed
// the classifier — 10.7k events / 14d on self-hosted
// Sentry TAURI-RUST-1V, dominated by
// ProviderConfigRejection-class rollups whose inner
// attempts the provider layer already demoted.
let detail = format!(
"[composio][triage] run_triage failed (label={}): {e:#}",
envelope.display_label
);
crate::core::observability::report_error_or_expected(
detail.as_str(),
"composio",
"trigger_triage",
&[("label", envelope.display_label.as_str())],
);
}
}
Expand Down
Loading