mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-21 05:32:53 +00:00
fix: preserve fallback error details
This commit is contained in:
@@ -56,6 +56,8 @@ export function logModelFallbackDecision(params: {
|
||||
: "none";
|
||||
const reasonText = params.reason ?? "unknown";
|
||||
const observedError = buildErrorObservationFields(params.error);
|
||||
const detailText = observedError.providerErrorMessagePreview ?? observedError.errorPreview;
|
||||
const detailSuffix = detailText ? ` detail=${sanitizeForLog(detailText)}` : "";
|
||||
decisionLog.warn("model fallback decision", {
|
||||
event: "model_fallback_decision",
|
||||
tags: ["error_handling", "model_fallback", params.decision],
|
||||
@@ -88,6 +90,6 @@ export function logModelFallbackDecision(params: {
|
||||
})),
|
||||
consoleMessage:
|
||||
`model fallback decision: decision=${params.decision} requested=${sanitizeForLog(params.requestedProvider)}/${sanitizeForLog(params.requestedModel)} ` +
|
||||
`candidate=${sanitizeForLog(params.candidate.provider)}/${sanitizeForLog(params.candidate.model)} reason=${reasonText} next=${nextText}`,
|
||||
`candidate=${sanitizeForLog(params.candidate.provider)}/${sanitizeForLog(params.candidate.model)} reason=${reasonText} next=${nextText}${detailSuffix}`,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -78,6 +78,20 @@ describe("fallback-state", () => {
|
||||
expect(resolved.reasonSummary).toBe("rate limit burst");
|
||||
});
|
||||
|
||||
it("prefers formatted transient error details over generic rate-limit labels", () => {
|
||||
const resolved = resolveDemoFallbackTransition({
|
||||
attempts: [
|
||||
{
|
||||
...baseAttempt,
|
||||
error: "429 Too Many Requests: Claude Max usage limit reached, try again in 6 minutes.",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(resolved.reasonSummary).toContain("HTTP 429: Too Many Requests");
|
||||
expect(resolved.reasonSummary).toContain("Claude Max usage limit reached");
|
||||
});
|
||||
|
||||
it("refreshes reason when fallback remains active with same model pair", () => {
|
||||
const resolved = resolveDemoFallbackTransition({
|
||||
attempts: [{ ...baseAttempt, reason: "timeout" }],
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import { formatRawAssistantErrorForUi } from "../agents/pi-embedded-helpers.js";
|
||||
import type { SessionEntry } from "../config/sessions.js";
|
||||
import { normalizeOptionalString } from "../shared/string-coerce.js";
|
||||
import { formatProviderModelRef } from "./model-runtime.js";
|
||||
import type { RuntimeFallbackAttempt } from "./reply/agent-runner-execution.js";
|
||||
|
||||
const FALLBACK_REASON_PART_MAX = 80;
|
||||
const TRANSIENT_FALLBACK_REASONS = new Set(["rate_limit", "overloaded", "timeout"]);
|
||||
const TRANSIENT_ERROR_DETAIL_HINT_RE =
|
||||
/\b(?:429|5\d\d|too many requests|usage limit|quota|try again in|retry[- ]after|seconds?|minutes?|hours?|temporarily unavailable|overloaded|service unavailable|throttl)\b/i;
|
||||
|
||||
export type FallbackNoticeState = Pick<
|
||||
SessionEntry,
|
||||
@@ -20,7 +24,32 @@ function truncateFallbackReasonPart(value: string, max = FALLBACK_REASON_PART_MA
|
||||
return `${text.slice(0, Math.max(0, max - 1)).trimEnd()}…`;
|
||||
}
|
||||
|
||||
function formatFallbackAttemptErrorPreview(attempt: RuntimeFallbackAttempt): string | undefined {
|
||||
const rawError = attempt.error?.trim();
|
||||
if (!rawError) {
|
||||
return undefined;
|
||||
}
|
||||
if (!attempt.reason || !TRANSIENT_FALLBACK_REASONS.has(attempt.reason)) {
|
||||
return undefined;
|
||||
}
|
||||
if (!TRANSIENT_ERROR_DETAIL_HINT_RE.test(rawError)) {
|
||||
return undefined;
|
||||
}
|
||||
const formatted = formatRawAssistantErrorForUi(rawError)
|
||||
.replace(/^⚠️\s*/, "")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
if (!formatted || /unknown error/i.test(formatted)) {
|
||||
return undefined;
|
||||
}
|
||||
return formatted;
|
||||
}
|
||||
|
||||
export function formatFallbackAttemptReason(attempt: RuntimeFallbackAttempt): string {
|
||||
const errorPreview = formatFallbackAttemptErrorPreview(attempt);
|
||||
if (errorPreview) {
|
||||
return errorPreview;
|
||||
}
|
||||
const reason = attempt.reason?.trim();
|
||||
if (reason) {
|
||||
return reason.replace(/_/g, " ");
|
||||
|
||||
Reference in New Issue
Block a user