pi-mono/packages/ai/test/github-copilot-request-size-overflow.test.ts
Petr Houška 73f8de80bc fix(ai): recover from Copilot request-size overflow
Copilot rejects request bodies over its byte-size limit with HTTP 413 and a generic
'failed to parse request' body, so the existing token-count heuristics never matched.
Treat any HTTP 413 as request-size overflow regardless of provider or body text, so
the agent compacts and retries instead of surfacing a raw API error.

Validated against the live Copilot API (40MB image payload): main classifies the 413
as a plain error, this branch detects it as overflow.
2026-10-19 22:46:26 +02:00

47 lines
1.8 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import { stream } from "../src/api/openai-responses.ts";
import type { Model } from "../src/types.ts";
import { isContextOverflow } from "../src/utils/overflow.ts";
import { normalizeContext } from "../src/utils/transcript.ts";
const model: Model<"openai-responses"> = {
id: "copilot-test-model",
name: "Copilot test model",
api: "openai-responses",
provider: "github-copilot",
baseUrl: "https://copilot.invalid",
reasoning: true,
input: ["text", "image"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 1_000_000,
maxTokens: 128_000,
};
describe("Copilot HTTP request-size errors", () => {
it.each([
{ status: 413, nested: true, overflow: true },
{ status: 413, nested: false, overflow: true },
{ status: 400, nested: true, overflow: false },
])("classifies HTTP $status with nested=$nested through the real SDK", async ({ status, nested, overflow }) => {
const error = { message: "failed to parse request", code: "" };
const fetch = vi.fn<typeof globalThis.fetch>().mockResolvedValue(
new Response(JSON.stringify(nested ? { error } : error), {
status,
headers: { "content-type": "application/json" },
}),
);
const result = await stream(
model,
normalizeContext({ messages: [{ role: "user", content: "Continue", timestamp: 0 }] }),
{ apiKey: "test-key", fetch, maxRetries: 0 },
).result();
expect(fetch).toHaveBeenCalledTimes(1);
expect(result.provider).toBe("github-copilot");
expect(result.stopReason).toBe("error");
expect(result.usage.totalTokens).toBe(0);
expect(result.errorMessage).toContain(`(${status})`);
expect(result.errorMessage).toContain("failed to parse request");
expect(isContextOverflow(result, model.contextWindow)).toBe(overflow);
});
});