mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-08 05:32:54 +00:00
fix(agents): classify embedded provider business denials for fallback
This commit is contained in:
@@ -70,6 +70,31 @@ describe("classifyEmbeddedAgentRunResultForModelFallback", () => {
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it("uses provider-scoped failover matching for business-denial payloads", () => {
|
||||
const result = classifyEmbeddedAgentRunResultForModelFallback({
|
||||
provider: "openrouter",
|
||||
model: "claude-3.5-sonnet",
|
||||
result: {
|
||||
payloads: [
|
||||
{
|
||||
isError: true,
|
||||
text: "Key limit exceeded",
|
||||
},
|
||||
],
|
||||
meta: {
|
||||
durationMs: 42,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
message: "openrouter/claude-3.5-sonnet ended with a provider error: Key limit exceeded",
|
||||
reason: "billing",
|
||||
code: "embedded_error_payload",
|
||||
rawError: "Key limit exceeded",
|
||||
});
|
||||
});
|
||||
|
||||
it("does not retry unclassified non-GPT error payloads", () => {
|
||||
const result = classifyEmbeddedAgentRunResultForModelFallback({
|
||||
provider: "custom",
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import { isSilentReplyPayloadText } from "../../auto-reply/tokens.js";
|
||||
import {
|
||||
isAuthErrorMessage,
|
||||
isAuthPermanentErrorMessage,
|
||||
isBillingErrorMessage,
|
||||
} from "../embedded-agent-helpers/failover-matches.js";
|
||||
import { classifyFailoverReason } from "../embedded-agent-helpers/errors.js";
|
||||
import type { FailoverReason } from "../embedded-agent-helpers/types.js";
|
||||
import { isGpt5ModelId } from "../gpt5-prompt-overlay.js";
|
||||
import type { ModelFallbackResultClassification } from "../model-fallback.js";
|
||||
@@ -63,20 +59,20 @@ function classifyHarnessResult(params: {
|
||||
|
||||
function classifyBusinessDenialErrorPayloadReason(
|
||||
errorText: string,
|
||||
provider: string,
|
||||
): Extract<FailoverReason, "auth" | "auth_permanent" | "billing"> | null {
|
||||
if (!errorText.trim()) {
|
||||
return null;
|
||||
}
|
||||
if (isBillingErrorMessage(errorText)) {
|
||||
return "billing";
|
||||
const failoverReason = classifyFailoverReason(errorText, { provider });
|
||||
switch (failoverReason) {
|
||||
case "auth":
|
||||
case "auth_permanent":
|
||||
case "billing":
|
||||
return failoverReason;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
if (isAuthPermanentErrorMessage(errorText)) {
|
||||
return "auth_permanent";
|
||||
}
|
||||
if (isAuthErrorMessage(errorText)) {
|
||||
return "auth";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function classifyEmbeddedAgentRunResultForModelFallback(params: {
|
||||
@@ -128,7 +124,7 @@ export function classifyEmbeddedAgentRunResultForModelFallback(params: {
|
||||
code: "incomplete_result",
|
||||
};
|
||||
}
|
||||
const failoverReason = classifyBusinessDenialErrorPayloadReason(errorText);
|
||||
const failoverReason = classifyBusinessDenialErrorPayloadReason(errorText, params.provider);
|
||||
if (failoverReason) {
|
||||
return {
|
||||
message: `${params.provider}/${params.model} ended with a provider error: ${errorText}`,
|
||||
|
||||
Reference in New Issue
Block a user