Skip to content

Commit 67ebc43

Browse files
committed
fix(agents): remove root Anthropic SDK dependency
1 parent 93a6c93 commit 67ebc43

4 files changed

Lines changed: 171 additions & 104 deletions

File tree

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1488,7 +1488,6 @@
14881488
},
14891489
"dependencies": {
14901490
"@agentclientprotocol/sdk": "0.19.0",
1491-
"@anthropic-ai/sdk": "0.81.0",
14921491
"@anthropic-ai/vertex-sdk": "^0.16.0",
14931492
"@aws-sdk/client-bedrock": "3.1032.0",
14941493
"@aws-sdk/client-bedrock-runtime": "3.1032.0",

pnpm-lock.yaml

Lines changed: 0 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/agents/anthropic-transport-stream.test.ts

Lines changed: 64 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,32 @@ import type { Model } from "@mariozechner/pi-ai";
22
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
33
import { attachModelProviderRequestTransport } from "./provider-request-config.js";
44

5-
const {
6-
anthropicCtorMock,
7-
anthropicMessagesStreamMock,
8-
buildGuardedModelFetchMock,
9-
guardedFetchMock,
10-
} = vi.hoisted(() => ({
11-
anthropicCtorMock: vi.fn(),
12-
anthropicMessagesStreamMock: vi.fn(),
5+
const { buildGuardedModelFetchMock, guardedFetchMock } = vi.hoisted(() => ({
136
buildGuardedModelFetchMock: vi.fn(),
147
guardedFetchMock: vi.fn(),
158
}));
169

17-
vi.mock("@anthropic-ai/sdk", () => ({
18-
default: anthropicCtorMock,
19-
}));
20-
2110
vi.mock("./provider-transport-fetch.js", () => ({
2211
buildGuardedModelFetch: buildGuardedModelFetchMock,
2312
}));
2413

2514
let createAnthropicMessagesTransportStreamFn: typeof import("./anthropic-transport-stream.js").createAnthropicMessagesTransportStreamFn;
2615

27-
function emptyEventStream(): AsyncIterable<Record<string, unknown>> {
28-
return (async function* () {})();
16+
function createSseResponse(events: Record<string, unknown>[] = []): Response {
17+
const body = events.map((event) => `data: ${JSON.stringify(event)}\n\n`).join("");
18+
return new Response(body, {
19+
status: 200,
20+
headers: { "content-type": "text/event-stream" },
21+
});
22+
}
23+
24+
function latestAnthropicRequest() {
25+
const [, init] = guardedFetchMock.mock.calls.at(-1) ?? [];
26+
const body = init?.body;
27+
return {
28+
init,
29+
payload: typeof body === "string" ? (JSON.parse(body) as Record<string, unknown>) : {},
30+
};
2931
}
3032

3133
describe("anthropic transport stream", () => {
@@ -35,19 +37,10 @@ describe("anthropic transport stream", () => {
3537
});
3638

3739
beforeEach(() => {
38-
anthropicCtorMock.mockReset();
39-
anthropicMessagesStreamMock.mockReset();
4040
buildGuardedModelFetchMock.mockReset();
4141
guardedFetchMock.mockReset();
4242
buildGuardedModelFetchMock.mockReturnValue(guardedFetchMock);
43-
anthropicMessagesStreamMock.mockReturnValue(emptyEventStream());
44-
anthropicCtorMock.mockImplementation(function mockAnthropicClient() {
45-
return {
46-
messages: {
47-
stream: anthropicMessagesStreamMock,
48-
},
49-
};
50-
});
43+
guardedFetchMock.mockResolvedValue(createSseResponse());
5144
});
5245

5346
it("uses the guarded fetch transport for api-key Anthropic requests", async () => {
@@ -89,26 +82,25 @@ describe("anthropic transport stream", () => {
8982
await stream.result();
9083

9184
expect(buildGuardedModelFetchMock).toHaveBeenCalledWith(model);
92-
expect(anthropicCtorMock).toHaveBeenCalledWith(
85+
expect(guardedFetchMock).toHaveBeenCalledWith(
86+
"https://api.anthropic.com/v1/messages",
9387
expect.objectContaining({
94-
apiKey: "sk-ant-api",
95-
baseURL: "https://api.anthropic.com",
96-
fetch: guardedFetchMock,
97-
defaultHeaders: expect.objectContaining({
88+
method: "POST",
89+
headers: expect.objectContaining({
90+
"x-api-key": "sk-ant-api",
91+
"anthropic-version": "2023-06-01",
92+
"content-type": "application/json",
9893
accept: "application/json",
9994
"anthropic-dangerous-direct-browser-access": "true",
10095
"X-Provider": "anthropic",
10196
"X-Call": "1",
10297
}),
10398
}),
10499
);
105-
expect(anthropicMessagesStreamMock).toHaveBeenCalledWith(
106-
expect.objectContaining({
107-
model: "claude-sonnet-4-6",
108-
stream: true,
109-
}),
110-
undefined,
111-
);
100+
expect(latestAnthropicRequest().payload).toMatchObject({
101+
model: "claude-sonnet-4-6",
102+
stream: true,
103+
});
112104
});
113105

114106
it("ignores non-positive runtime maxTokens overrides and falls back to the model limit", async () => {
@@ -147,14 +139,11 @@ describe("anthropic transport stream", () => {
147139
);
148140
await stream.result();
149141

150-
expect(anthropicMessagesStreamMock).toHaveBeenCalledWith(
151-
expect.objectContaining({
152-
model: "claude-sonnet-4-6",
153-
max_tokens: 8192,
154-
stream: true,
155-
}),
156-
undefined,
157-
);
142+
expect(latestAnthropicRequest().payload).toMatchObject({
143+
model: "claude-sonnet-4-6",
144+
max_tokens: 8192,
145+
stream: true,
146+
});
158147
});
159148

160149
it("ignores fractional runtime maxTokens overrides that floor to zero", async () => {
@@ -193,14 +182,11 @@ describe("anthropic transport stream", () => {
193182
);
194183
await stream.result();
195184

196-
expect(anthropicMessagesStreamMock).toHaveBeenCalledWith(
197-
expect.objectContaining({
198-
model: "claude-sonnet-4-6",
199-
max_tokens: 8192,
200-
stream: true,
201-
}),
202-
undefined,
203-
);
185+
expect(latestAnthropicRequest().payload).toMatchObject({
186+
model: "claude-sonnet-4-6",
187+
max_tokens: 8192,
188+
stream: true,
189+
});
204190
});
205191

206192
it("fails locally when Anthropic maxTokens is non-positive after resolution", async () => {
@@ -243,17 +229,17 @@ describe("anthropic transport stream", () => {
243229
expect(result.errorMessage).toContain(
244230
"Anthropic Messages transport requires a positive maxTokens value",
245231
);
246-
expect(anthropicMessagesStreamMock).not.toHaveBeenCalled();
232+
expect(guardedFetchMock).not.toHaveBeenCalled();
247233
});
248234

249235
it("preserves Anthropic OAuth identity and tool-name remapping with transport overrides", async () => {
250-
anthropicMessagesStreamMock.mockReturnValueOnce(
251-
(async function* () {
252-
yield {
236+
guardedFetchMock.mockResolvedValueOnce(
237+
createSseResponse([
238+
{
253239
type: "message_start",
254240
message: { id: "msg_1", usage: { input_tokens: 10, output_tokens: 0 } },
255-
};
256-
yield {
241+
},
242+
{
257243
type: "content_block_start",
258244
index: 0,
259245
content_block: {
@@ -262,17 +248,17 @@ describe("anthropic transport stream", () => {
262248
name: "Read",
263249
input: { path: "/tmp/a" },
264250
},
265-
};
266-
yield {
251+
},
252+
{
267253
type: "content_block_stop",
268254
index: 0,
269-
};
270-
yield {
255+
},
256+
{
271257
type: "message_delta",
272258
delta: { stop_reason: "tool_use" },
273259
usage: { input_tokens: 10, output_tokens: 5 },
274-
};
275-
})(),
260+
},
261+
]),
276262
);
277263
const model = attachModelProviderRequestTransport(
278264
{
@@ -321,21 +307,17 @@ describe("anthropic transport stream", () => {
321307
);
322308
const result = await stream.result();
323309

324-
expect(anthropicCtorMock).toHaveBeenCalledWith(
310+
expect(guardedFetchMock).toHaveBeenCalledWith(
311+
"https://api.anthropic.com/v1/messages",
325312
expect.objectContaining({
326-
apiKey: null,
327-
authToken: "sk-ant-oat-example",
328-
fetch: guardedFetchMock,
329-
defaultHeaders: expect.objectContaining({
313+
headers: expect.objectContaining({
314+
authorization: "Bearer sk-ant-oat-example",
330315
"x-app": "cli",
331316
"user-agent": expect.stringContaining("claude-cli/"),
332317
}),
333318
}),
334319
);
335-
const firstCallParams = anthropicMessagesStreamMock.mock.calls[0]?.[0] as Record<
336-
string,
337-
unknown
338-
>;
320+
const firstCallParams = latestAnthropicRequest().payload;
339321
expect(firstCallParams.system).toEqual(
340322
expect.arrayContaining([
341323
expect.objectContaining({
@@ -407,10 +389,7 @@ describe("anthropic transport stream", () => {
407389
);
408390
await stream.result();
409391

410-
const firstCallParams = anthropicMessagesStreamMock.mock.calls[0]?.[0] as Record<
411-
string,
412-
unknown
413-
>;
392+
const firstCallParams = latestAnthropicRequest().payload;
414393
expect(firstCallParams.messages).toEqual(
415394
expect.arrayContaining([
416395
expect.objectContaining({
@@ -463,13 +442,10 @@ describe("anthropic transport stream", () => {
463442
);
464443
await stream.result();
465444

466-
expect(anthropicMessagesStreamMock).toHaveBeenCalledWith(
467-
expect.objectContaining({
468-
thinking: { type: "adaptive" },
469-
output_config: { effort: "max" },
470-
}),
471-
undefined,
472-
);
445+
expect(latestAnthropicRequest().payload).toMatchObject({
446+
thinking: { type: "adaptive" },
447+
output_config: { effort: "max" },
448+
});
473449
});
474450

475451
it("maps xhigh thinking effort for Claude Opus 4.7 transport runs", async () => {
@@ -508,12 +484,9 @@ describe("anthropic transport stream", () => {
508484
);
509485
await stream.result();
510486

511-
expect(anthropicMessagesStreamMock).toHaveBeenCalledWith(
512-
expect.objectContaining({
513-
thinking: { type: "adaptive" },
514-
output_config: { effort: "xhigh" },
515-
}),
516-
undefined,
517-
);
487+
expect(latestAnthropicRequest().payload).toMatchObject({
488+
thinking: { type: "adaptive" },
489+
output_config: { effort: "xhigh" },
490+
});
518491
});
519492
});

0 commit comments

Comments
 (0)