




















@@ -34,6 +34,7 @@ import {
3434matchesFormatErrorPattern,
3535} from "./failover-matches.js";
3636import {
37+classifyProviderPluginError,
3738classifyProviderSpecificError,
3839matchesProviderContextOverflow,
3940} from "./provider-error-patterns.js";
@@ -264,6 +265,7 @@ type PaymentRequiredFailoverReason = Extract<FailoverReason, "billing" | "rate_l
264265export type FailoverSignal = {
265266status?: number;
266267code?: string;
268+errorType?: string;
267269message?: string;
268270provider?: string;
269271};
@@ -633,16 +635,30 @@ export function classifyFailoverReasonFromHttpStatus(
633635message?: string,
634636opts?: { provider?: string },
635637): FailoverReason | null {
638+const hasProviderStatusSignal = Boolean(opts?.provider && typeof status === "number");
636639const messageClassification = message
637- ? classifyFailoverClassificationFromMessage(message, opts?.provider)
640+ ? classifyFailoverClassificationFromMessage(message, opts?.provider, {
641+includeProviderPluginHooks: !hasProviderStatusSignal,
642+})
638643 : null;
644+const providerPluginReason = hasProviderStatusSignal
645+ ? classifyProviderPluginError({
646+errorMessage: message ?? "",
647+provider: opts?.provider,
648+ status,
649+})
650+ : null;
651+const effectiveMessageClassification = providerPluginReason
652+ ? toReasonClassification(providerPluginReason)
653+ : messageClassification;
639654return failoverReasonFromClassification(
640655classifyFailoverClassificationFromHttpStatus(
641656status,
642657message,
643-messageClassification,
658+effectiveMessageClassification,
644659status,
645660opts?.provider,
661+{ preserveProviderSignalClassification: providerPluginReason !== null },
646662),
647663);
648664}
@@ -653,6 +669,7 @@ function classifyFailoverClassificationFromHttpStatus(
653669messageClassification: FailoverClassification | null,
654670explicitStatus: number | undefined,
655671provider?: string,
672+opts?: { preserveProviderSignalClassification?: boolean },
656673): FailoverClassification | null {
657674const messageReason = failoverReasonFromClassification(messageClassification);
658675if (typeof status !== "number" || !Number.isFinite(status)) {
@@ -685,6 +702,9 @@ function classifyFailoverClassificationFromHttpStatus(
685702return toReasonClassification("rate_limit");
686703}
687704if (status === 401 || status === 403) {
705+if (opts?.preserveProviderSignalClassification && messageClassification) {
706+return messageClassification;
707+}
688708if (message && isAuthPermanentErrorMessage(message)) {
689709return toReasonClassification("auth_permanent");
690710}
@@ -868,6 +888,7 @@ function isExactUnknownNoDetailsError(raw: string): boolean {
868888function classifyFailoverClassificationFromMessage(
869889raw: string,
870890provider?: string,
891+opts?: { includeProviderPluginHooks?: boolean },
871892): FailoverClassification | null {
872893if (isImageDimensionErrorMessage(raw)) {
873894return null;
@@ -960,7 +981,10 @@ function classifyFailoverClassificationFromMessage(
960981return toReasonClassification("timeout");
961982}
962983// Provider-specific patterns as a final catch (Bedrock, Groq, Together AI, etc.)
963-const providerSpecific = classifyProviderSpecificError(raw);
984+const providerSpecific = classifyProviderSpecificError(
985+{ errorMessage: raw, provider },
986+{ includePluginHooks: opts?.includeProviderPluginHooks },
987+);
964988if (providerSpecific) {
965989return toReasonClassification(providerSpecific);
966990}
@@ -969,34 +993,58 @@ function classifyFailoverClassificationFromMessage(
969993970994export function classifyFailoverSignal(signal: FailoverSignal): FailoverClassification | null {
971995const inferredStatus = inferSignalStatus(signal);
996+const explicitStatus =
997+typeof signal.status === "number" && Number.isFinite(signal.status) ? signal.status : undefined;
972998if (
973999signal.message &&
9741000isTransportHtmlErrorStatus(inferredStatus) &&
9751001isHtmlErrorResponse(signal.message, inferredStatus)
9761002) {
9771003return toReasonClassification("timeout");
9781004}
1005+const hasStructuredProviderSignal = Boolean(
1006+signal.provider &&
1007+(explicitStatus !== undefined || signal.code !== undefined || signal.errorType !== undefined),
1008+);
9791009const messageClassification = signal.message
980- ? classifyFailoverClassificationFromMessage(signal.message, signal.provider)
1010+ ? classifyFailoverClassificationFromMessage(signal.message, signal.provider, {
1011+includeProviderPluginHooks: !hasStructuredProviderSignal,
1012+})
9811013 : null;
1014+const providerPluginReason =
1015+hasStructuredProviderSignal &&
1016+signal.provider &&
1017+(signal.message || signal.code || signal.errorType || typeof inferredStatus === "number")
1018+ ? classifyProviderPluginError({
1019+errorMessage: signal.message ?? "",
1020+provider: signal.provider,
1021+status: explicitStatus,
1022+code: signal.code,
1023+errorType: signal.errorType,
1024+})
1025+ : null;
1026+const effectiveMessageClassification = providerPluginReason
1027+ ? toReasonClassification(providerPluginReason)
1028+ : messageClassification;
9821029const codeReason = classifyFailoverReasonFromCode(signal.code);
9831030if (codeReason === "auth_permanent") {
9841031return toReasonClassification(codeReason);
9851032}
9861033const statusClassification = classifyFailoverClassificationFromHttpStatus(
9871034inferredStatus,
9881035signal.message,
989-messageClassification,
1036+effectiveMessageClassification,
9901037signal.status,
9911038signal.provider,
1039+{ preserveProviderSignalClassification: providerPluginReason !== null },
9921040);
9931041if (statusClassification) {
9941042return statusClassification;
9951043}
9961044if (codeReason) {
9971045return toReasonClassification(codeReason);
9981046}
999-return messageClassification;
1047+return effectiveMessageClassification;
10001048}
1001104910021050export function classifyProviderRuntimeFailureKind(
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。