From 3d65cba22e4c3b2609f83b971238abb3ae558a32 Mon Sep 17 00:00:00 2001 From: Brisbanehuang Date: Wed, 22 Jul 2026 12:37:11 -0400 Subject: [PATCH 1/5] fix(proxy): fail over provider-local model 404s --- src/app/v1/_lib/proxy/errors.ts | 19 +++ src/app/v1/_lib/proxy/forwarder.ts | 8 +- .../provider-local-model-unavailable.test.ts | 122 ++++++++++++++++++ .../proxy-forwarder-hedge-first-byte.test.ts | 92 ++++++++++++- .../proxy/proxy-forwarder-retry-limit.test.ts | 61 +++++++++ 5 files changed, 294 insertions(+), 8 deletions(-) create mode 100644 tests/unit/proxy/provider-local-model-unavailable.test.ts diff --git a/src/app/v1/_lib/proxy/errors.ts b/src/app/v1/_lib/proxy/errors.ts index 647b4bfbd..2b010632d 100644 --- a/src/app/v1/_lib/proxy/errors.ts +++ b/src/app/v1/_lib/proxy/errors.ts @@ -573,6 +573,18 @@ function extractErrorContentForDetection(error: Error): string { return error.message; } +const PROVIDER_LOCAL_MODEL_UNAVAILABLE_MARKER = + "not supported by any configured account in this group"; + +/** A model capability gap local to one upstream account pool, not to the request. */ +export function isProviderLocalModelUnavailableError(error: unknown): error is ProxyError { + if (!(error instanceof ProxyError) || error.statusCode !== 404) return false; + + return [error.message, error.upstreamError?.body, error.upstreamError?.rawBody].some((content) => + content?.toLowerCase().includes(PROVIDER_LOCAL_MODEL_UNAVAILABLE_MARKER) + ); +} + /** * 错误规则检测结果缓存 * @@ -948,6 +960,13 @@ export async function categorizeErrorAsync(error: Error): Promise return ErrorCategory.SYSTEM_ERROR; } + // Some upstream account pools use model_not_found for a provider-local capability + // gap. The request may still succeed on another Provider, so classify this exact + // 404 before broad client-input rules match the generic model_not_found wording. + if (isProviderLocalModelUnavailableError(error)) { + return ErrorCategory.RESOURCE_NOT_FOUND; + } + // 优先级 2: 不可重试的客户端输入错误检测(白名单模式) // 使用异步版本确保错误规则已加载 if (await isNonRetryableClientErrorAsync(error)) { diff --git a/src/app/v1/_lib/proxy/forwarder.ts b/src/app/v1/_lib/proxy/forwarder.ts index cb5dd36f8..e26e63c9e 100644 --- a/src/app/v1/_lib/proxy/forwarder.ts +++ b/src/app/v1/_lib/proxy/forwarder.ts @@ -75,6 +75,7 @@ import { isClientAbortError, isEmptyResponseError, isHttp2Error, + isProviderLocalModelUnavailableError, isSSLCertificateError, ProxyError, sanitizeUrl, @@ -2045,10 +2046,12 @@ export class ProxyForwarder { break; // ⭐ 跳出内层循环,进入供应商切换逻辑 } - // ⭐ 5. 上游 404 错误处理(不计入熔断器,先重试当前供应商,重试耗尽后切换) + // ⭐ 5. 上游 404 错误处理(不计入熔断器;Provider 局部模型缺口直接切换) if (errorCategory === ErrorCategory.RESOURCE_NOT_FOUND) { const proxyError = lastError as ProxyError; - const willRetry = attemptCount < maxAttemptsPerProvider; + const providerLocalModelUnavailable = isProviderLocalModelUnavailableError(proxyError); + const willRetry = + !providerLocalModelUnavailable && attemptCount < maxAttemptsPerProvider; logger.warn("ProxyForwarder: Upstream 404 error", { providerId: currentProvider.id, @@ -2059,6 +2062,7 @@ export class ProxyForwarder { attemptNumber: attemptCount, totalProvidersAttempted, willRetry, + providerLocalModelUnavailable, }); // 记录到决策链(标记为 resource_not_found,不计入熔断) diff --git a/tests/unit/proxy/provider-local-model-unavailable.test.ts b/tests/unit/proxy/provider-local-model-unavailable.test.ts new file mode 100644 index 000000000..0ee95f44e --- /dev/null +++ b/tests/unit/proxy/provider-local-model-unavailable.test.ts @@ -0,0 +1,122 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { + ErrorCategory, + ProxyError, + categorizeErrorAsync, + isProviderLocalModelUnavailableError, +} from "@/app/v1/_lib/proxy/errors"; +import type { ErrorDetectionResult } from "@/lib/error-rule-detector"; + +const mocks = vi.hoisted(() => ({ + detectAsync: vi.fn<(content: string) => Promise>(), +})); + +vi.mock("@/lib/error-rule-detector", () => ({ + errorRuleDetector: { detectAsync: mocks.detectAsync }, +})); + +describe("provider-local model availability errors", () => { + beforeEach(() => { + vi.clearAllMocks(); + mocks.detectAsync.mockResolvedValue({ matched: true }); + }); + + it("switches Provider when an account group does not support the requested model", async () => { + const body = JSON.stringify({ + error: { + message: 'Model "gpt-5.6-sol" is not supported by any configured account in this group', + type: "model_not_found", + }, + }); + const response = new Response(body, { + status: 404, + headers: { "content-type": "application/json" }, + }); + const error = await ProxyError.fromUpstreamResponse(response, { + id: 96, + name: "provider-a", + }); + + expect(isProviderLocalModelUnavailableError(error)).toBe(true); + expect(await categorizeErrorAsync(error)).toBe(ErrorCategory.RESOURCE_NOT_FOUND); + expect(mocks.detectAsync).not.toHaveBeenCalled(); + }); + + it("matches the provider-local marker case-insensitively in the extracted message", async () => { + const error = new ProxyError( + "MODEL IS NOT SUPPORTED BY ANY CONFIGURED ACCOUNT IN THIS GROUP", + 404, + { + body: "{}", + providerId: 96, + providerName: "provider-a", + } + ); + + expect(isProviderLocalModelUnavailableError(error)).toBe(true); + expect(await categorizeErrorAsync(error)).toBe(ErrorCategory.RESOURCE_NOT_FOUND); + expect(mocks.detectAsync).not.toHaveBeenCalled(); + }); + + it("recognizes the marker from the raw upstream body", async () => { + const error = new ProxyError("model_not_found", 404, { + body: "{}", + rawBody: "not supported by any configured account in this group", + providerId: 96, + providerName: "provider-a", + }); + + expect(isProviderLocalModelUnavailableError(error)).toBe(true); + expect(await categorizeErrorAsync(error)).toBe(ErrorCategory.RESOURCE_NOT_FOUND); + expect(mocks.detectAsync).not.toHaveBeenCalled(); + }); + + it("keeps ordinary model_not_found responses non-retryable", async () => { + const error = new ProxyError("The requested model was not found", 404, { + body: JSON.stringify({ + error: { + code: "model_not_found", + message: "The requested model was not found", + }, + }), + providerId: 96, + providerName: "provider-a", + }); + + expect(isProviderLocalModelUnavailableError(error)).toBe(false); + expect(await categorizeErrorAsync(error)).toBe(ErrorCategory.NON_RETRYABLE_CLIENT_ERROR); + expect(mocks.detectAsync).toHaveBeenCalledOnce(); + }); + + it("does not match a near-miss account-group message", async () => { + const error = new ProxyError( + "Model is not supported by every configured account in this group", + 404, + { + body: '{"error":"model_not_found"}', + providerId: 96, + providerName: "provider-a", + } + ); + + expect(isProviderLocalModelUnavailableError(error)).toBe(false); + expect(await categorizeErrorAsync(error)).toBe(ErrorCategory.NON_RETRYABLE_CLIENT_ERROR); + expect(mocks.detectAsync).toHaveBeenCalledOnce(); + }); + + it("does not override the same wording on a non-404 client error", async () => { + const error = new ProxyError( + "Model is not supported by any configured account in this group", + 400, + { + body: "invalid request", + providerId: 96, + providerName: "provider-a", + } + ); + + expect(isProviderLocalModelUnavailableError(error)).toBe(false); + expect(await categorizeErrorAsync(error)).toBe(ErrorCategory.NON_RETRYABLE_CLIENT_ERROR); + expect(mocks.detectAsync).toHaveBeenCalledOnce(); + }); +}); diff --git a/tests/unit/proxy/proxy-forwarder-hedge-first-byte.test.ts b/tests/unit/proxy/proxy-forwarder-hedge-first-byte.test.ts index 014219a24..fb95776a2 100644 --- a/tests/unit/proxy/proxy-forwarder-hedge-first-byte.test.ts +++ b/tests/unit/proxy/proxy-forwarder-hedge-first-byte.test.ts @@ -1558,6 +1558,82 @@ describe("ProxyForwarder - first-byte hedge scheduling", () => { } }); + test("provider-local model 404 should not cancel another in-flight hedge candidate", async () => { + vi.useFakeTimers(); + + try { + const provider1 = createProvider({ id: 1, name: "p1", firstByteTimeoutStreamingMs: 100 }); + const provider2 = createProvider({ id: 2, name: "p2", firstByteTimeoutStreamingMs: 100 }); + const session = createSession(); + session.setProvider(provider1); + + mocks.pickRandomProviderWithExclusion + .mockResolvedValueOnce(provider2) + .mockResolvedValueOnce(null); + mocks.categorizeErrorAsync.mockResolvedValue(ProxyErrorCategory.RESOURCE_NOT_FOUND); + + const doForward = vi.spyOn( + ProxyForwarder as unknown as { + doForward: (...args: unknown[]) => Promise; + }, + "doForward" + ); + const controller1 = new AbortController(); + const controller2 = new AbortController(); + const providerLocal404 = new UpstreamProxyError( + 'Model "gpt-5.6-sol" is not supported by any configured account in this group', + 404, + { + body: '{"error":{"type":"model_not_found"}}', + providerId: provider1.id, + providerName: provider1.name, + } + ); + + doForward.mockImplementationOnce(async (attemptSession) => { + const runtime = attemptSession as ProxySession & AttemptRuntime; + runtime.responseController = controller1; + runtime.clearResponseTimeout = vi.fn(); + return createDelayedFailure({ + delayMs: 150, + error: providerLocal404, + controller: controller1, + }); + }); + doForward.mockImplementationOnce(async (attemptSession) => { + const runtime = attemptSession as ProxySession & AttemptRuntime; + runtime.responseController = controller2; + runtime.clearResponseTimeout = vi.fn(); + return createStreamingResponse({ + label: "p2", + firstChunkDelayMs: 80, + controller: controller2, + }); + }); + + const responsePromise = ProxyForwarder.send(session); + + await vi.advanceTimersByTimeAsync(100); + expect(doForward).toHaveBeenCalledTimes(2); + await vi.advanceTimersByTimeAsync(50); + expect(controller2.signal.aborted).toBe(false); + await vi.advanceTimersByTimeAsync(30); + + const response = await responsePromise; + expect(await response.text()).toContain('"provider":"p2"'); + expect(controller2.signal.aborted).toBe(false); + expect(mocks.recordFailure).not.toHaveBeenCalledWith(provider1.id, providerLocal404); + expect(session.getProviderChain()).toEqual( + expect.arrayContaining([ + expect.objectContaining({ id: provider1.id, reason: "resource_not_found" }), + expect.objectContaining({ id: provider2.id, reason: "hedge_winner" }), + ]) + ); + } finally { + vi.useRealTimers(); + } + }); + test.each([ { name: "provider error", @@ -1570,14 +1646,18 @@ describe("ProxyForwarder - first-byte hedge scheduling", () => { }), }, { - name: "resource not found", + name: "provider-local resource not found", category: ProxyErrorCategory.RESOURCE_NOT_FOUND, errorFactory: (provider: Provider) => - new UpstreamProxyError("Provider returned 404: model not found", 404, { - body: '{"error":"model_not_found"}', - providerId: provider.id, - providerName: provider.name, - }), + new UpstreamProxyError( + 'Model "gpt-5.6-sol" is not supported by any configured account in this group', + 404, + { + body: '{"error":{"type":"model_not_found"}}', + providerId: provider.id, + providerName: provider.name, + } + ), }, { name: "system error", diff --git a/tests/unit/proxy/proxy-forwarder-retry-limit.test.ts b/tests/unit/proxy/proxy-forwarder-retry-limit.test.ts index 454b3a7d6..f9b9de688 100644 --- a/tests/unit/proxy/proxy-forwarder-retry-limit.test.ts +++ b/tests/unit/proxy/proxy-forwarder-retry-limit.test.ts @@ -354,6 +354,67 @@ describe("ProxyForwarder - retry limit enforcement", () => { ]); }); + test("provider-local model 404 skips same-provider retries and switches Provider", async () => { + const session = createSession(); + const provider1 = createProvider({ + id: 1, + name: "provider-without-model", + providerVendorId: null, + maxRetryAttempts: 3, + }); + const provider2 = createProvider({ + id: 2, + name: "provider-with-model", + providerVendorId: null, + }); + session.setProvider(provider1); + + mocks.getPreferredProviderEndpoints.mockResolvedValue([]); + vi.mocked(categorizeErrorAsync).mockResolvedValue(ErrorCategory.RESOURCE_NOT_FOUND); + + const doForward = vi.spyOn( + ProxyForwarder as unknown as { doForward: (...args: unknown[]) => unknown }, + "doForward" + ); + const selectAlternative = vi.spyOn( + ProxyForwarder as unknown as { selectAlternative: (...args: unknown[]) => unknown }, + "selectAlternative" + ); + const providerLocal404 = new ProxyError( + 'Model "gpt-5.6-sol" is not supported by any configured account in this group', + 404, + { + body: '{"error":{"type":"model_not_found"}}', + providerId: provider1.id, + providerName: provider1.name, + } + ); + + doForward.mockRejectedValueOnce(providerLocal404).mockResolvedValueOnce( + new Response("{}", { + status: 200, + headers: { "content-type": "application/json", "content-length": "2" }, + }) + ); + selectAlternative.mockResolvedValueOnce(provider2); + + const response = await ProxyForwarder.send(session); + + expect(response.status).toBe(200); + expect(doForward).toHaveBeenCalledTimes(2); + expect(selectAlternative).toHaveBeenCalledTimes(1); + expect(selectAlternative).toHaveBeenCalledWith(session, [provider1.id]); + expect(mocks.recordFailure).not.toHaveBeenCalled(); + expect(session.getProviderChain()).toEqual([ + expect.objectContaining({ + id: provider1.id, + reason: "resource_not_found", + attemptNumber: 1, + }), + expect.objectContaining({ id: provider2.id, reason: "retry_success", attemptNumber: 1 }), + ]); + }); + test("endpoints > maxRetry: should only use top N lowest-latency endpoints", async () => { vi.useFakeTimers(); From aa432c5ad337440b3b0c7620da1dad0c6285636e Mon Sep 17 00:00:00 2001 From: Brisbanehuang Date: Wed, 22 Jul 2026 12:42:55 -0400 Subject: [PATCH 2/5] fix(proxy): preserve local 404 failover past rectifiers --- src/app/v1/_lib/proxy/forwarder.ts | 7 +++++++ tests/unit/proxy/proxy-forwarder-hedge-first-byte.test.ts | 2 +- tests/unit/proxy/proxy-forwarder-retry-limit.test.ts | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/app/v1/_lib/proxy/forwarder.ts b/src/app/v1/_lib/proxy/forwarder.ts index e26e63c9e..6c7a522a3 100644 --- a/src/app/v1/_lib/proxy/forwarder.ts +++ b/src/app/v1/_lib/proxy/forwarder.ts @@ -960,6 +960,7 @@ function getReactiveRectifierDisplayName(rectifierType: ReactiveRectifierType): } async function tryApplyReactiveRectifier(params: { + error: Error; provider: Provider; requestSession: ProxySession; persistSession: ProxySession; @@ -968,6 +969,10 @@ async function tryApplyReactiveRectifier(params: { retryAttemptNumber: number; retryState: ReactiveRectifierRetryState; }): Promise { + if (isProviderLocalModelUnavailableError(params.error)) { + return { matched: false }; + } + const { provider, requestSession, @@ -1824,6 +1829,7 @@ export class ProxyForwarder { // 2.5 Reactive rectifier:命中后对同供应商“整流 + 重试一次” const reactiveRectifierResult = await tryApplyReactiveRectifier({ + error: lastError, provider: currentProvider, requestSession: session, persistSession: session, @@ -4363,6 +4369,7 @@ export class ProxyForwarder { } const reactiveRectifierResult = await tryApplyReactiveRectifier({ + error, provider: attempt.provider, requestSession: attempt.session, persistSession: session, diff --git a/tests/unit/proxy/proxy-forwarder-hedge-first-byte.test.ts b/tests/unit/proxy/proxy-forwarder-hedge-first-byte.test.ts index fb95776a2..afc516718 100644 --- a/tests/unit/proxy/proxy-forwarder-hedge-first-byte.test.ts +++ b/tests/unit/proxy/proxy-forwarder-hedge-first-byte.test.ts @@ -1584,7 +1584,7 @@ describe("ProxyForwarder - first-byte hedge scheduling", () => { 'Model "gpt-5.6-sol" is not supported by any configured account in this group', 404, { - body: '{"error":{"type":"model_not_found"}}', + body: '{"error":{"type":"model_not_found","message":"invalid request: not supported by any configured account in this group"}}', providerId: provider1.id, providerName: provider1.name, } diff --git a/tests/unit/proxy/proxy-forwarder-retry-limit.test.ts b/tests/unit/proxy/proxy-forwarder-retry-limit.test.ts index f9b9de688..18ef29673 100644 --- a/tests/unit/proxy/proxy-forwarder-retry-limit.test.ts +++ b/tests/unit/proxy/proxy-forwarder-retry-limit.test.ts @@ -384,7 +384,7 @@ describe("ProxyForwarder - retry limit enforcement", () => { 'Model "gpt-5.6-sol" is not supported by any configured account in this group', 404, { - body: '{"error":{"type":"model_not_found"}}', + body: '{"error":{"type":"model_not_found","message":"invalid request: not supported by any configured account in this group"}}', providerId: provider1.id, providerName: provider1.name, } From f0a9ef10ae1a686436b62d81d9dda42dd12428b7 Mon Sep 17 00:00:00 2001 From: Brisbanehuang Date: Wed, 22 Jul 2026 12:44:15 -0400 Subject: [PATCH 3/5] test(proxy): preserve fake-200 classification --- src/app/v1/_lib/proxy/errors.ts | 8 +++++++- .../proxy/provider-local-model-unavailable.test.ts | 13 +++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/app/v1/_lib/proxy/errors.ts b/src/app/v1/_lib/proxy/errors.ts index 2b010632d..07c372e05 100644 --- a/src/app/v1/_lib/proxy/errors.ts +++ b/src/app/v1/_lib/proxy/errors.ts @@ -578,7 +578,13 @@ const PROVIDER_LOCAL_MODEL_UNAVAILABLE_MARKER = /** A model capability gap local to one upstream account pool, not to the request. */ export function isProviderLocalModelUnavailableError(error: unknown): error is ProxyError { - if (!(error instanceof ProxyError) || error.statusCode !== 404) return false; + if ( + !(error instanceof ProxyError) || + error.statusCode !== 404 || + error.upstreamError?.statusCodeInferred === true + ) { + return false; + } return [error.message, error.upstreamError?.body, error.upstreamError?.rawBody].some((content) => content?.toLowerCase().includes(PROVIDER_LOCAL_MODEL_UNAVAILABLE_MARKER) diff --git a/tests/unit/proxy/provider-local-model-unavailable.test.ts b/tests/unit/proxy/provider-local-model-unavailable.test.ts index 0ee95f44e..d8931b3c2 100644 --- a/tests/unit/proxy/provider-local-model-unavailable.test.ts +++ b/tests/unit/proxy/provider-local-model-unavailable.test.ts @@ -119,4 +119,17 @@ describe("provider-local model availability errors", () => { expect(await categorizeErrorAsync(error)).toBe(ErrorCategory.NON_RETRYABLE_CLIENT_ERROR); expect(mocks.detectAsync).toHaveBeenCalledOnce(); }); + + it("does not override a synthetic 404 inferred from a fake-200 response", async () => { + const error = new ProxyError("not supported by any configured account in this group", 404, { + body: '{"error":"model_not_found"}', + statusCodeInferred: true, + providerId: 96, + providerName: "provider-a", + }); + + expect(isProviderLocalModelUnavailableError(error)).toBe(false); + expect(await categorizeErrorAsync(error)).toBe(ErrorCategory.NON_RETRYABLE_CLIENT_ERROR); + expect(mocks.detectAsync).toHaveBeenCalledOnce(); + }); }); From 4f6f1b72aa8d7d5f9729dd210a0f1e50bea7acff Mon Sep 17 00:00:00 2001 From: Brisbanehuang Date: Wed, 22 Jul 2026 12:47:26 -0400 Subject: [PATCH 4/5] fix(proxy): keep raw fake-200 payloads out of matching --- src/app/v1/_lib/proxy/errors.ts | 2 +- src/app/v1/_lib/proxy/forwarder.ts | 2 +- tests/unit/proxy/provider-local-model-unavailable.test.ts | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/app/v1/_lib/proxy/errors.ts b/src/app/v1/_lib/proxy/errors.ts index 07c372e05..593269975 100644 --- a/src/app/v1/_lib/proxy/errors.ts +++ b/src/app/v1/_lib/proxy/errors.ts @@ -586,7 +586,7 @@ export function isProviderLocalModelUnavailableError(error: unknown): error is P return false; } - return [error.message, error.upstreamError?.body, error.upstreamError?.rawBody].some((content) => + return [error.message, error.upstreamError?.body].some((content) => content?.toLowerCase().includes(PROVIDER_LOCAL_MODEL_UNAVAILABLE_MARKER) ); } diff --git a/src/app/v1/_lib/proxy/forwarder.ts b/src/app/v1/_lib/proxy/forwarder.ts index 6c7a522a3..896d49abc 100644 --- a/src/app/v1/_lib/proxy/forwarder.ts +++ b/src/app/v1/_lib/proxy/forwarder.ts @@ -2052,7 +2052,7 @@ export class ProxyForwarder { break; // ⭐ 跳出内层循环,进入供应商切换逻辑 } - // ⭐ 5. 上游 404 错误处理(不计入熔断器;Provider 局部模型缺口直接切换) + // 5. 上游 404 错误处理(不计入熔断器;Provider 局部模型缺口直接切换) if (errorCategory === ErrorCategory.RESOURCE_NOT_FOUND) { const proxyError = lastError as ProxyError; const providerLocalModelUnavailable = isProviderLocalModelUnavailableError(proxyError); diff --git a/tests/unit/proxy/provider-local-model-unavailable.test.ts b/tests/unit/proxy/provider-local-model-unavailable.test.ts index d8931b3c2..2746b1a61 100644 --- a/tests/unit/proxy/provider-local-model-unavailable.test.ts +++ b/tests/unit/proxy/provider-local-model-unavailable.test.ts @@ -58,7 +58,7 @@ describe("provider-local model availability errors", () => { expect(mocks.detectAsync).not.toHaveBeenCalled(); }); - it("recognizes the marker from the raw upstream body", async () => { + it("does not use the raw upstream body for provider-local matching", async () => { const error = new ProxyError("model_not_found", 404, { body: "{}", rawBody: "not supported by any configured account in this group", @@ -66,9 +66,9 @@ describe("provider-local model availability errors", () => { providerName: "provider-a", }); - expect(isProviderLocalModelUnavailableError(error)).toBe(true); - expect(await categorizeErrorAsync(error)).toBe(ErrorCategory.RESOURCE_NOT_FOUND); - expect(mocks.detectAsync).not.toHaveBeenCalled(); + expect(isProviderLocalModelUnavailableError(error)).toBe(false); + expect(await categorizeErrorAsync(error)).toBe(ErrorCategory.NON_RETRYABLE_CLIENT_ERROR); + expect(mocks.detectAsync).toHaveBeenCalledOnce(); }); it("keeps ordinary model_not_found responses non-retryable", async () => { From 73387e63036c80c45f037fc59716ea409040c7aa Mon Sep 17 00:00:00 2001 From: Brisbanehuang Date: Wed, 22 Jul 2026 12:55:17 -0400 Subject: [PATCH 5/5] test(proxy): accept deferred commit callback --- tests/integration/proxy-hedge-lifecycle.test.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/integration/proxy-hedge-lifecycle.test.ts b/tests/integration/proxy-hedge-lifecycle.test.ts index ec3e9a533..b4ea4c546 100644 --- a/tests/integration/proxy-hedge-lifecycle.test.ts +++ b/tests/integration/proxy-hedge-lifecycle.test.ts @@ -518,7 +518,8 @@ describe("proxy hedge transport/lifecycle integration (persistence and control-p outputTokens: 3, providerId: 2, statusCode: 200, - }) + }), + expect.objectContaining({ onCommitted: expect.any(Function) }) ); expect(state.updateMessageRequestDetailsIfUnfinalized).not.toHaveBeenCalled(); @@ -615,7 +616,8 @@ describe("proxy hedge transport/lifecycle integration (persistence and control-p expect(state.durableTerminal).toHaveBeenCalledOnce(); expect(state.durableTerminal).toHaveBeenCalledWith( MESSAGE.id, - expect.objectContaining({ statusCode: 502 }) + expect.objectContaining({ statusCode: 502 }), + expect.objectContaining({ onCommitted: expect.any(Function) }) ); expect(agents.release).toHaveBeenCalledOnce(); expect(agents.pool.getPoolStats().activeRequests).toBe(0);