fix(anthropic-provider): resolved Anthropic request and stream handling

- Added a 4-slot concurrency limiter for Anthropic multi-image resize work.
- Prioritized client-owned request fields and `claudeCodeVersion`-based `User-Agent` headers.
- Ignored terminal `message_delta` envelopes and treated missing `usage`/`delta` payloads as non-fatal.
- Stopped ping-only streams from consuming first-event watchdog logic and preserved timeout retries.
This commit is contained in:
can1357
2026-06-09 23:17:26 +02:00
parent 228c1ddd8d
commit f80d45aa6b
16 changed files with 721 additions and 99 deletions
@@ -224,6 +224,38 @@ describe("Anthropic Copilot auth config", () => {
expect(result.baseURL).toBe("http://127.0.0.1:8317");
});
it("sends Content-Type and anthropic-version on Copilot anthropic requests", () => {
const result = buildAnthropicClientOptions({
model: makeCopilotClaudeModel(),
apiKey: "ghu_test",
extraBetas: [],
stream: true,
dynamicHeaders: {},
});
// The client posts JSON.stringify(params); without these the request goes
// out with no Content-Type at all (Bun does not default it for string
// bodies when a plain headers object is supplied).
expect(result.defaultHeaders["Content-Type"]).toBe("application/json");
expect(result.defaultHeaders["anthropic-version"]).toBe("2023-06-01");
});
it("merges Copilot headers case-insensitively so auth headers cannot duplicate", () => {
const result = buildAnthropicClientOptions({
model: { ...makeCopilotClaudeModel(), headers: { ...OPENCODE_HEADERS, authorization: "Bearer override" } },
apiKey: "ghu_test",
extraBetas: [],
stream: true,
dynamicHeaders: {},
});
// A miscased duplicate would survive Object.assign and the Headers
// constructor then joins both values comma-separated on the wire.
const authKeys = Object.keys(result.defaultHeaders).filter(key => key.toLowerCase() === "authorization");
expect(authKeys).toHaveLength(1);
expect(result.defaultHeaders[authKeys[0]]).toBe("Bearer override");
});
it("builds anthropic auth URLs from the normalized service root", () => {
const url = buildAnthropicUrl({
apiKey: "test-key",