mirror of
https://github.com/samvallad33/vestige.git
synced 2026-04-25 00:36:22 +02:00
fix(predict): surface degraded state instead of silent empty responses
All four PredictiveMemory calls (predict_needed_memories, get_proactive_suggestions, get_top_interests, prediction_accuracy) return Result, and all four were being swallowed by `.unwrap_or_default()` / `.unwrap_or(0.0)` — every lock-poisoning or internal error produced a response indistinguishable from a genuine cold-start "I have no predictions yet." Callers (dashboard, Claude Code, Cursor) had no way to tell "the system is broken" from "there genuinely isn't anything to predict." Now each call uses `unwrap_or_else` to (a) `tracing::warn!` the error with its source channel for observability, (b) flip a local `degraded` flag. The JSON response gains a new `predict_degraded: bool` field. Empty + degraded=false = cold start (expected). Empty + degraded=true = something went wrong, check logs. 6 existing predict tests pass (return shape unchanged on success path).
This commit is contained in:
parent
01d2e006dc
commit
72e353ae02
1 changed files with 54 additions and 5 deletions
|
|
@ -67,20 +67,62 @@ pub async fn execute(
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get predictions from predictive memory
|
// Get predictions from predictive memory.
|
||||||
|
//
|
||||||
|
// Each of these four calls can fail (lock poisoning, internal PredictiveMemory
|
||||||
|
// errors). Before v2.0.7 the failures were silently swallowed by
|
||||||
|
// `unwrap_or_default()`, producing an empty response that was indistinguishable
|
||||||
|
// from a genuine cold-start "I don't have any predictions yet" state. That
|
||||||
|
// ambiguity is a user-visible bug: callers can't tell "model is degraded"
|
||||||
|
// from "nothing to predict." Now we track whether ANY call errored and expose
|
||||||
|
// it as `predict_degraded: true` in the response, with per-channel errors
|
||||||
|
// logged via `tracing::warn!` for observability.
|
||||||
|
let mut degraded = false;
|
||||||
let predictions = cog
|
let predictions = cog
|
||||||
.predictive_memory
|
.predictive_memory
|
||||||
.predict_needed_memories(&session_ctx)
|
.predict_needed_memories(&session_ctx)
|
||||||
.unwrap_or_default();
|
.unwrap_or_else(|e| {
|
||||||
|
tracing::warn!(
|
||||||
|
target: "vestige::predict",
|
||||||
|
error = %e,
|
||||||
|
"predict_needed_memories failed; returning empty predictions"
|
||||||
|
);
|
||||||
|
degraded = true;
|
||||||
|
Vec::new()
|
||||||
|
});
|
||||||
let suggestions = cog
|
let suggestions = cog
|
||||||
.predictive_memory
|
.predictive_memory
|
||||||
.get_proactive_suggestions(0.3)
|
.get_proactive_suggestions(0.3)
|
||||||
.unwrap_or_default();
|
.unwrap_or_else(|e| {
|
||||||
|
tracing::warn!(
|
||||||
|
target: "vestige::predict",
|
||||||
|
error = %e,
|
||||||
|
"get_proactive_suggestions failed; returning empty suggestions"
|
||||||
|
);
|
||||||
|
degraded = true;
|
||||||
|
Vec::new()
|
||||||
|
});
|
||||||
let top_interests = cog
|
let top_interests = cog
|
||||||
.predictive_memory
|
.predictive_memory
|
||||||
.get_top_interests(10)
|
.get_top_interests(10)
|
||||||
.unwrap_or_default();
|
.unwrap_or_else(|e| {
|
||||||
let accuracy = cog.predictive_memory.prediction_accuracy().unwrap_or(0.0);
|
tracing::warn!(
|
||||||
|
target: "vestige::predict",
|
||||||
|
error = %e,
|
||||||
|
"get_top_interests failed; returning empty interests"
|
||||||
|
);
|
||||||
|
degraded = true;
|
||||||
|
Vec::new()
|
||||||
|
});
|
||||||
|
let accuracy = cog.predictive_memory.prediction_accuracy().unwrap_or_else(|e| {
|
||||||
|
tracing::warn!(
|
||||||
|
target: "vestige::predict",
|
||||||
|
error = %e,
|
||||||
|
"prediction_accuracy failed; returning 0.0"
|
||||||
|
);
|
||||||
|
degraded = true;
|
||||||
|
0.0
|
||||||
|
});
|
||||||
|
|
||||||
// Build speculative context
|
// Build speculative context
|
||||||
let speculative_context = vestige_core::PredictionContext {
|
let speculative_context = vestige_core::PredictionContext {
|
||||||
|
|
@ -123,6 +165,13 @@ pub async fn execute(
|
||||||
})).collect::<Vec<_>>(),
|
})).collect::<Vec<_>>(),
|
||||||
"top_interests": top_interests,
|
"top_interests": top_interests,
|
||||||
"prediction_accuracy": accuracy,
|
"prediction_accuracy": accuracy,
|
||||||
|
// predict_degraded == true means at least one of the four underlying
|
||||||
|
// PredictiveMemory calls errored (lock poisoning, internal failure)
|
||||||
|
// and the corresponding field above was substituted with an empty
|
||||||
|
// default. Callers should treat an empty response as "genuinely nothing
|
||||||
|
// to predict" only when predict_degraded is false. See tracing logs
|
||||||
|
// under target "vestige::predict" for per-channel error details.
|
||||||
|
"predict_degraded": degraded,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue