@ai-sdk/anthropic 3.0.18 → 3.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/index.js +1 -1
  3. package/dist/index.mjs +1 -1
  4. package/docs/05-anthropic.mdx +1096 -0
  5. package/package.json +8 -3
  6. package/src/__fixtures__/anthropic-code-execution-20250825.1.chunks.txt +248 -0
  7. package/src/__fixtures__/anthropic-code-execution-20250825.1.json +70 -0
  8. package/src/__fixtures__/anthropic-code-execution-20250825.2.chunks.txt +984 -0
  9. package/src/__fixtures__/anthropic-code-execution-20250825.2.json +111 -0
  10. package/src/__fixtures__/anthropic-code-execution-20250825.pptx-skill.chunks.txt +691 -0
  11. package/src/__fixtures__/anthropic-code-execution-20250825.pptx-skill.json +1801 -0
  12. package/src/__fixtures__/anthropic-json-other-tool.1.chunks.txt +13 -0
  13. package/src/__fixtures__/anthropic-json-other-tool.1.json +26 -0
  14. package/src/__fixtures__/anthropic-json-output-format.1.chunks.txt +120 -0
  15. package/src/__fixtures__/anthropic-json-output-format.1.json +25 -0
  16. package/src/__fixtures__/anthropic-json-tool.1.chunks.txt +9 -0
  17. package/src/__fixtures__/anthropic-json-tool.1.json +37 -0
  18. package/src/__fixtures__/anthropic-json-tool.2.chunks.txt +14 -0
  19. package/src/__fixtures__/anthropic-mcp.1.chunks.txt +17 -0
  20. package/src/__fixtures__/anthropic-mcp.1.json +39 -0
  21. package/src/__fixtures__/anthropic-memory-20250818.1.json +28 -0
  22. package/src/__fixtures__/anthropic-message-delta-input-tokens.chunks.txt +8 -0
  23. package/src/__fixtures__/anthropic-programmatic-tool-calling.1.chunks.txt +278 -0
  24. package/src/__fixtures__/anthropic-programmatic-tool-calling.1.json +106 -0
  25. package/src/__fixtures__/anthropic-tool-no-args.chunks.txt +13 -0
  26. package/src/__fixtures__/anthropic-tool-no-args.json +31 -0
  27. package/src/__fixtures__/anthropic-tool-search-bm25.1.chunks.txt +47 -0
  28. package/src/__fixtures__/anthropic-tool-search-bm25.1.json +67 -0
  29. package/src/__fixtures__/anthropic-tool-search-regex.1.chunks.txt +51 -0
  30. package/src/__fixtures__/anthropic-tool-search-regex.1.json +65 -0
  31. package/src/__fixtures__/anthropic-web-fetch-tool.1.chunks.txt +64 -0
  32. package/src/__fixtures__/anthropic-web-fetch-tool.1.json +54 -0
  33. package/src/__fixtures__/anthropic-web-fetch-tool.2.json +56 -0
  34. package/src/__fixtures__/anthropic-web-fetch-tool.error.json +46 -0
  35. package/src/__fixtures__/anthropic-web-search-tool.1.chunks.txt +120 -0
  36. package/src/__fixtures__/anthropic-web-search-tool.1.json +181 -0
  37. package/src/__snapshots__/anthropic-messages-language-model.test.ts.snap +16719 -0
  38. package/src/anthropic-error.test.ts +42 -0
  39. package/src/anthropic-error.ts +26 -0
  40. package/src/anthropic-message-metadata.ts +105 -0
  41. package/src/anthropic-messages-api.ts +1188 -0
  42. package/src/anthropic-messages-language-model.test.ts +7170 -0
  43. package/src/anthropic-messages-language-model.ts +2067 -0
  44. package/src/anthropic-messages-options.ts +213 -0
  45. package/src/anthropic-prepare-tools.test.ts +1219 -0
  46. package/src/anthropic-prepare-tools.ts +341 -0
  47. package/src/anthropic-provider.test.ts +162 -0
  48. package/src/anthropic-provider.ts +152 -0
  49. package/src/anthropic-tools.ts +182 -0
  50. package/src/convert-anthropic-messages-usage.ts +32 -0
  51. package/src/convert-to-anthropic-messages-prompt.test.ts +2902 -0
  52. package/src/convert-to-anthropic-messages-prompt.ts +1050 -0
  53. package/src/forward-anthropic-container-id-from-last-step.ts +38 -0
  54. package/src/get-cache-control.ts +63 -0
  55. package/src/index.ts +10 -0
  56. package/src/internal/index.ts +4 -0
  57. package/src/map-anthropic-stop-reason.ts +28 -0
  58. package/src/tool/bash_20241022.ts +33 -0
  59. package/src/tool/bash_20250124.ts +33 -0
  60. package/src/tool/code-execution_20250522.ts +61 -0
  61. package/src/tool/code-execution_20250825.ts +281 -0
  62. package/src/tool/computer_20241022.ts +87 -0
  63. package/src/tool/computer_20250124.ts +130 -0
  64. package/src/tool/memory_20250818.ts +62 -0
  65. package/src/tool/text-editor_20241022.ts +63 -0
  66. package/src/tool/text-editor_20250124.ts +63 -0
  67. package/src/tool/text-editor_20250429.ts +64 -0
  68. package/src/tool/text-editor_20250728.ts +80 -0
  69. package/src/tool/tool-search-bm25_20251119.ts +98 -0
  70. package/src/tool/tool-search-regex_20251119.ts +110 -0
  71. package/src/tool/web-fetch-20250910.ts +145 -0
  72. package/src/tool/web-search_20250305.ts +136 -0
  73. package/src/version.ts +6 -0
@@ -0,0 +1,1219 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { prepareTools } from './anthropic-prepare-tools';
3
+ import { CacheControlValidator } from './get-cache-control';
4
+ import { webFetch_20250910OutputSchema } from './tool/web-fetch-20250910';
5
+ import { webSearch_20250305OutputSchema } from './tool/web-search_20250305';
6
+ import {
7
+ anthropicMessagesChunkSchema,
8
+ anthropicMessagesResponseSchema,
9
+ } from './anthropic-messages-api';
10
+
11
+ describe('prepareTools', () => {
12
+ it('should return undefined tools and tool_choice when tools are null', async () => {
13
+ const result = await prepareTools({
14
+ tools: undefined,
15
+ toolChoice: undefined,
16
+ supportsStructuredOutput: true,
17
+ });
18
+ expect(result).toEqual({
19
+ tools: undefined,
20
+ tool_choice: undefined,
21
+ toolWarnings: [],
22
+ betas: new Set(),
23
+ });
24
+ });
25
+
26
+ it('should return undefined tools and tool_choice when tools are empty', async () => {
27
+ const result = await prepareTools({
28
+ tools: [],
29
+ toolChoice: undefined,
30
+ supportsStructuredOutput: true,
31
+ });
32
+ expect(result).toEqual({
33
+ tools: undefined,
34
+ tool_choice: undefined,
35
+ toolWarnings: [],
36
+ betas: new Set(),
37
+ });
38
+ });
39
+
40
+ it('should correctly prepare function tools', async () => {
41
+ const result = await prepareTools({
42
+ tools: [
43
+ {
44
+ type: 'function',
45
+ name: 'testFunction',
46
+ description: 'A test function',
47
+ inputSchema: { type: 'object', properties: {} },
48
+ },
49
+ ],
50
+ toolChoice: undefined,
51
+ supportsStructuredOutput: true,
52
+ });
53
+ expect(result.tools).toEqual([
54
+ {
55
+ cache_control: undefined,
56
+ name: 'testFunction',
57
+ description: 'A test function',
58
+ input_schema: { type: 'object', properties: {} },
59
+ },
60
+ ]);
61
+ expect(result.toolChoice).toBeUndefined();
62
+ expect(result.toolWarnings).toEqual([]);
63
+ });
64
+
65
+ it('should correctly preserve tool input examples', async () => {
66
+ const result = await prepareTools({
67
+ tools: [
68
+ {
69
+ type: 'function',
70
+ name: 'tool_with_examples',
71
+ description: 'tool with examples',
72
+ inputSchema: {
73
+ type: 'object',
74
+ properties: { a: { type: 'number' } },
75
+ },
76
+ inputExamples: [{ input: { a: 1 } }, { input: { a: 2 } }],
77
+ },
78
+ ],
79
+ toolChoice: undefined,
80
+ supportsStructuredOutput: true,
81
+ });
82
+
83
+ expect(result).toMatchInlineSnapshot(`
84
+ {
85
+ "betas": Set {
86
+ "structured-outputs-2025-11-13",
87
+ "advanced-tool-use-2025-11-20",
88
+ },
89
+ "toolChoice": undefined,
90
+ "toolWarnings": [],
91
+ "tools": [
92
+ {
93
+ "cache_control": undefined,
94
+ "description": "tool with examples",
95
+ "input_examples": [
96
+ {
97
+ "a": 1,
98
+ },
99
+ {
100
+ "a": 2,
101
+ },
102
+ ],
103
+ "input_schema": {
104
+ "properties": {
105
+ "a": {
106
+ "type": "number",
107
+ },
108
+ },
109
+ "type": "object",
110
+ },
111
+ "name": "tool_with_examples",
112
+ },
113
+ ],
114
+ }
115
+ `);
116
+ });
117
+
118
+ describe('strict mode for function tools', () => {
119
+ it('should include strict and structured-outputs beta when supportsStructuredOutput is true and strict is true', async () => {
120
+ const result = await prepareTools({
121
+ tools: [
122
+ {
123
+ type: 'function',
124
+ name: 'testFunction',
125
+ description: 'A test function',
126
+ inputSchema: { type: 'object', properties: {} },
127
+ strict: true,
128
+ },
129
+ ],
130
+ toolChoice: undefined,
131
+ supportsStructuredOutput: true,
132
+ });
133
+
134
+ expect(result).toMatchInlineSnapshot(`
135
+ {
136
+ "betas": Set {
137
+ "structured-outputs-2025-11-13",
138
+ },
139
+ "toolChoice": undefined,
140
+ "toolWarnings": [],
141
+ "tools": [
142
+ {
143
+ "cache_control": undefined,
144
+ "description": "A test function",
145
+ "input_schema": {
146
+ "properties": {},
147
+ "type": "object",
148
+ },
149
+ "name": "testFunction",
150
+ "strict": true,
151
+ },
152
+ ],
153
+ }
154
+ `);
155
+ });
156
+
157
+ it('should include beta but not strict property when strict is undefined and supportsStructuredOutput is true', async () => {
158
+ const result = await prepareTools({
159
+ tools: [
160
+ {
161
+ type: 'function',
162
+ name: 'testFunction',
163
+ description: 'A test function',
164
+ inputSchema: { type: 'object', properties: {} },
165
+ },
166
+ ],
167
+ toolChoice: undefined,
168
+ supportsStructuredOutput: true,
169
+ });
170
+
171
+ expect(result).toMatchInlineSnapshot(`
172
+ {
173
+ "betas": Set {
174
+ "structured-outputs-2025-11-13",
175
+ },
176
+ "toolChoice": undefined,
177
+ "toolWarnings": [],
178
+ "tools": [
179
+ {
180
+ "cache_control": undefined,
181
+ "description": "A test function",
182
+ "input_schema": {
183
+ "properties": {},
184
+ "type": "object",
185
+ },
186
+ "name": "testFunction",
187
+ },
188
+ ],
189
+ }
190
+ `);
191
+ });
192
+
193
+ it('should not include strict or beta when supportsStructuredOutput is false', async () => {
194
+ const result = await prepareTools({
195
+ tools: [
196
+ {
197
+ type: 'function',
198
+ name: 'testFunction',
199
+ description: 'A test function',
200
+ inputSchema: { type: 'object', properties: {} },
201
+ strict: true,
202
+ },
203
+ ],
204
+ toolChoice: undefined,
205
+ supportsStructuredOutput: false,
206
+ });
207
+
208
+ expect(result).toMatchInlineSnapshot(`
209
+ {
210
+ "betas": Set {},
211
+ "toolChoice": undefined,
212
+ "toolWarnings": [],
213
+ "tools": [
214
+ {
215
+ "cache_control": undefined,
216
+ "description": "A test function",
217
+ "input_schema": {
218
+ "properties": {},
219
+ "type": "object",
220
+ },
221
+ "name": "testFunction",
222
+ },
223
+ ],
224
+ }
225
+ `);
226
+ });
227
+
228
+ it('should include beta when strict is false and supportsStructuredOutput is true', async () => {
229
+ const result = await prepareTools({
230
+ tools: [
231
+ {
232
+ type: 'function',
233
+ name: 'testFunction',
234
+ description: 'A test function',
235
+ inputSchema: { type: 'object', properties: {} },
236
+ strict: false,
237
+ },
238
+ ],
239
+ toolChoice: undefined,
240
+ supportsStructuredOutput: true,
241
+ });
242
+
243
+ expect(result).toMatchInlineSnapshot(`
244
+ {
245
+ "betas": Set {
246
+ "structured-outputs-2025-11-13",
247
+ },
248
+ "toolChoice": undefined,
249
+ "toolWarnings": [],
250
+ "tools": [
251
+ {
252
+ "cache_control": undefined,
253
+ "description": "A test function",
254
+ "input_schema": {
255
+ "properties": {},
256
+ "type": "object",
257
+ },
258
+ "name": "testFunction",
259
+ "strict": false,
260
+ },
261
+ ],
262
+ }
263
+ `);
264
+ });
265
+ });
266
+
267
+ describe('provider-defined tools', () => {
268
+ describe('computer_20241022', () => {
269
+ it('should correctly prepare computer_20241022 tool', async () => {
270
+ const result = await prepareTools({
271
+ tools: [
272
+ {
273
+ type: 'provider',
274
+ id: 'anthropic.computer_20241022',
275
+ name: 'computer',
276
+ args: {
277
+ displayWidthPx: 800,
278
+ displayHeightPx: 600,
279
+ displayNumber: 1,
280
+ },
281
+ },
282
+ ],
283
+ toolChoice: undefined,
284
+ supportsStructuredOutput: true,
285
+ });
286
+
287
+ expect(result).toMatchInlineSnapshot(`
288
+ {
289
+ "betas": Set {
290
+ "computer-use-2024-10-22",
291
+ },
292
+ "toolChoice": undefined,
293
+ "toolWarnings": [],
294
+ "tools": [
295
+ {
296
+ "cache_control": undefined,
297
+ "display_height_px": 600,
298
+ "display_number": 1,
299
+ "display_width_px": 800,
300
+ "name": "computer",
301
+ "type": "computer_20241022",
302
+ },
303
+ ],
304
+ }
305
+ `);
306
+ });
307
+ });
308
+
309
+ describe('text_editor_20241022', () => {
310
+ it('should correctly prepare text_editor_20241022 tool', async () => {
311
+ const result = await prepareTools({
312
+ tools: [
313
+ {
314
+ type: 'provider',
315
+ id: 'anthropic.text_editor_20241022',
316
+ name: 'text_editor',
317
+ args: {},
318
+ },
319
+ ],
320
+ toolChoice: undefined,
321
+ supportsStructuredOutput: true,
322
+ });
323
+ expect(result).toMatchInlineSnapshot(`
324
+ {
325
+ "betas": Set {
326
+ "computer-use-2024-10-22",
327
+ },
328
+ "toolChoice": undefined,
329
+ "toolWarnings": [],
330
+ "tools": [
331
+ {
332
+ "cache_control": undefined,
333
+ "name": "str_replace_editor",
334
+ "type": "text_editor_20241022",
335
+ },
336
+ ],
337
+ }
338
+ `);
339
+ });
340
+ });
341
+
342
+ it('should correctly prepare bash_20241022 tool', async () => {
343
+ const result = await prepareTools({
344
+ tools: [
345
+ {
346
+ type: 'provider',
347
+ id: 'anthropic.bash_20241022',
348
+ name: 'bash',
349
+ args: {},
350
+ },
351
+ ],
352
+ toolChoice: undefined,
353
+ supportsStructuredOutput: true,
354
+ });
355
+
356
+ expect(result).toMatchInlineSnapshot(`
357
+ {
358
+ "betas": Set {
359
+ "computer-use-2024-10-22",
360
+ },
361
+ "toolChoice": undefined,
362
+ "toolWarnings": [],
363
+ "tools": [
364
+ {
365
+ "cache_control": undefined,
366
+ "name": "bash",
367
+ "type": "bash_20241022",
368
+ },
369
+ ],
370
+ }
371
+ `);
372
+ });
373
+
374
+ it('should correctly prepare text_editor_20250728 with max_characters', async () => {
375
+ const result = await prepareTools({
376
+ tools: [
377
+ {
378
+ type: 'provider',
379
+ id: 'anthropic.text_editor_20250728',
380
+ name: 'str_replace_based_edit_tool',
381
+ args: { maxCharacters: 10000 },
382
+ },
383
+ ],
384
+ toolChoice: undefined,
385
+ supportsStructuredOutput: true,
386
+ });
387
+ expect(result).toMatchInlineSnapshot(`
388
+ {
389
+ "betas": Set {},
390
+ "toolChoice": undefined,
391
+ "toolWarnings": [],
392
+ "tools": [
393
+ {
394
+ "cache_control": undefined,
395
+ "max_characters": 10000,
396
+ "name": "str_replace_based_edit_tool",
397
+ "type": "text_editor_20250728",
398
+ },
399
+ ],
400
+ }
401
+ `);
402
+ });
403
+
404
+ it('should correctly prepare text_editor_20250728 without max_characters', async () => {
405
+ const result = await prepareTools({
406
+ tools: [
407
+ {
408
+ type: 'provider',
409
+ id: 'anthropic.text_editor_20250728',
410
+ name: 'str_replace_based_edit_tool',
411
+ args: {},
412
+ },
413
+ ],
414
+ toolChoice: undefined,
415
+ supportsStructuredOutput: true,
416
+ });
417
+ expect(result).toMatchInlineSnapshot(`
418
+ {
419
+ "betas": Set {},
420
+ "toolChoice": undefined,
421
+ "toolWarnings": [],
422
+ "tools": [
423
+ {
424
+ "cache_control": undefined,
425
+ "max_characters": undefined,
426
+ "name": "str_replace_based_edit_tool",
427
+ "type": "text_editor_20250728",
428
+ },
429
+ ],
430
+ }
431
+ `);
432
+ });
433
+
434
+ it('should correctly prepare web_search_20250305', async () => {
435
+ const result = await prepareTools({
436
+ tools: [
437
+ {
438
+ type: 'provider',
439
+ id: 'anthropic.web_search_20250305',
440
+ name: 'web_search',
441
+ args: {
442
+ maxUses: 10,
443
+ allowedDomains: ['https://www.google.com'],
444
+ userLocation: { type: 'approximate', city: 'New York' },
445
+ },
446
+ },
447
+ ],
448
+ toolChoice: undefined,
449
+ supportsStructuredOutput: true,
450
+ });
451
+ expect(result).toMatchInlineSnapshot(`
452
+ {
453
+ "betas": Set {},
454
+ "toolChoice": undefined,
455
+ "toolWarnings": [],
456
+ "tools": [
457
+ {
458
+ "allowed_domains": [
459
+ "https://www.google.com",
460
+ ],
461
+ "blocked_domains": undefined,
462
+ "cache_control": undefined,
463
+ "max_uses": 10,
464
+ "name": "web_search",
465
+ "type": "web_search_20250305",
466
+ "user_location": {
467
+ "city": "New York",
468
+ "type": "approximate",
469
+ },
470
+ },
471
+ ],
472
+ }
473
+ `);
474
+ });
475
+
476
+ it('should correctly prepare web_fetch_20250910', async () => {
477
+ const result = await prepareTools({
478
+ tools: [
479
+ {
480
+ type: 'provider',
481
+ id: 'anthropic.web_fetch_20250910',
482
+ name: 'web_fetch',
483
+ args: {
484
+ maxUses: 10,
485
+ allowedDomains: ['https://www.google.com'],
486
+ citations: { enabled: true },
487
+ maxContentTokens: 1000,
488
+ },
489
+ },
490
+ ],
491
+ toolChoice: undefined,
492
+ supportsStructuredOutput: true,
493
+ });
494
+
495
+ expect(result).toMatchInlineSnapshot(`
496
+ {
497
+ "betas": Set {
498
+ "web-fetch-2025-09-10",
499
+ },
500
+ "toolChoice": undefined,
501
+ "toolWarnings": [],
502
+ "tools": [
503
+ {
504
+ "allowed_domains": [
505
+ "https://www.google.com",
506
+ ],
507
+ "blocked_domains": undefined,
508
+ "cache_control": undefined,
509
+ "citations": {
510
+ "enabled": true,
511
+ },
512
+ "max_content_tokens": 1000,
513
+ "max_uses": 10,
514
+ "name": "web_fetch",
515
+ "type": "web_fetch_20250910",
516
+ },
517
+ ],
518
+ }
519
+ `);
520
+ });
521
+
522
+ it('should correctly prepare tool_search_regex_20251119', async () => {
523
+ const result = await prepareTools({
524
+ tools: [
525
+ {
526
+ type: 'provider',
527
+ id: 'anthropic.tool_search_regex_20251119',
528
+ name: 'tool_search',
529
+ args: {},
530
+ },
531
+ ],
532
+ toolChoice: undefined,
533
+ supportsStructuredOutput: true,
534
+ });
535
+
536
+ expect(result).toMatchInlineSnapshot(`
537
+ {
538
+ "betas": Set {
539
+ "advanced-tool-use-2025-11-20",
540
+ },
541
+ "toolChoice": undefined,
542
+ "toolWarnings": [],
543
+ "tools": [
544
+ {
545
+ "name": "tool_search_tool_regex",
546
+ "type": "tool_search_tool_regex_20251119",
547
+ },
548
+ ],
549
+ }
550
+ `);
551
+ });
552
+
553
+ it('should correctly prepare tool_search_bm25_20251119', async () => {
554
+ const result = await prepareTools({
555
+ tools: [
556
+ {
557
+ type: 'provider',
558
+ id: 'anthropic.tool_search_bm25_20251119',
559
+ name: 'tool_search',
560
+ args: {},
561
+ },
562
+ ],
563
+ toolChoice: undefined,
564
+ supportsStructuredOutput: true,
565
+ });
566
+
567
+ expect(result).toMatchInlineSnapshot(`
568
+ {
569
+ "betas": Set {
570
+ "advanced-tool-use-2025-11-20",
571
+ },
572
+ "toolChoice": undefined,
573
+ "toolWarnings": [],
574
+ "tools": [
575
+ {
576
+ "name": "tool_search_tool_bm25",
577
+ "type": "tool_search_tool_bm25_20251119",
578
+ },
579
+ ],
580
+ }
581
+ `);
582
+ });
583
+ });
584
+
585
+ describe('deferLoading for function tools', () => {
586
+ it('should include defer_loading when set to true', async () => {
587
+ const result = await prepareTools({
588
+ tools: [
589
+ {
590
+ type: 'function',
591
+ name: 'testFunction',
592
+ description: 'A test function',
593
+ inputSchema: { type: 'object', properties: {} },
594
+ providerOptions: {
595
+ anthropic: { deferLoading: true },
596
+ },
597
+ },
598
+ ],
599
+ toolChoice: undefined,
600
+ supportsStructuredOutput: true,
601
+ });
602
+
603
+ expect(result).toMatchInlineSnapshot(`
604
+ {
605
+ "betas": Set {
606
+ "structured-outputs-2025-11-13",
607
+ },
608
+ "toolChoice": undefined,
609
+ "toolWarnings": [],
610
+ "tools": [
611
+ {
612
+ "cache_control": undefined,
613
+ "defer_loading": true,
614
+ "description": "A test function",
615
+ "input_schema": {
616
+ "properties": {},
617
+ "type": "object",
618
+ },
619
+ "name": "testFunction",
620
+ },
621
+ ],
622
+ }
623
+ `);
624
+ });
625
+
626
+ it('should include defer_loading when set to false', async () => {
627
+ const result = await prepareTools({
628
+ tools: [
629
+ {
630
+ type: 'function',
631
+ name: 'testFunction',
632
+ description: 'A test function',
633
+ inputSchema: { type: 'object', properties: {} },
634
+ providerOptions: {
635
+ anthropic: { deferLoading: false },
636
+ },
637
+ },
638
+ ],
639
+ toolChoice: undefined,
640
+ supportsStructuredOutput: true,
641
+ });
642
+
643
+ expect(result).toMatchInlineSnapshot(`
644
+ {
645
+ "betas": Set {
646
+ "structured-outputs-2025-11-13",
647
+ },
648
+ "toolChoice": undefined,
649
+ "toolWarnings": [],
650
+ "tools": [
651
+ {
652
+ "cache_control": undefined,
653
+ "defer_loading": false,
654
+ "description": "A test function",
655
+ "input_schema": {
656
+ "properties": {},
657
+ "type": "object",
658
+ },
659
+ "name": "testFunction",
660
+ },
661
+ ],
662
+ }
663
+ `);
664
+ });
665
+
666
+ it('should not include defer_loading when not specified', async () => {
667
+ const result = await prepareTools({
668
+ tools: [
669
+ {
670
+ type: 'function',
671
+ name: 'testFunction',
672
+ description: 'A test function',
673
+ inputSchema: { type: 'object', properties: {} },
674
+ },
675
+ ],
676
+ toolChoice: undefined,
677
+ supportsStructuredOutput: true,
678
+ });
679
+
680
+ expect(result.tools?.[0]).not.toHaveProperty('defer_loading');
681
+ });
682
+ });
683
+
684
+ describe('allowedCallers for function tools (programmatic tool calling)', () => {
685
+ it('should include allowed_callers and advanced-tool-use beta when allowedCallers is set', async () => {
686
+ const result = await prepareTools({
687
+ tools: [
688
+ {
689
+ type: 'function',
690
+ name: 'query_database',
691
+ description: 'Query a database',
692
+ inputSchema: {
693
+ type: 'object',
694
+ properties: { sql: { type: 'string' } },
695
+ },
696
+ providerOptions: {
697
+ anthropic: {
698
+ allowedCallers: ['code_execution_20250825'],
699
+ },
700
+ },
701
+ },
702
+ ],
703
+ toolChoice: undefined,
704
+ supportsStructuredOutput: true,
705
+ });
706
+
707
+ expect(result).toMatchInlineSnapshot(`
708
+ {
709
+ "betas": Set {
710
+ "structured-outputs-2025-11-13",
711
+ "advanced-tool-use-2025-11-20",
712
+ },
713
+ "toolChoice": undefined,
714
+ "toolWarnings": [],
715
+ "tools": [
716
+ {
717
+ "allowed_callers": [
718
+ "code_execution_20250825",
719
+ ],
720
+ "cache_control": undefined,
721
+ "description": "Query a database",
722
+ "input_schema": {
723
+ "properties": {
724
+ "sql": {
725
+ "type": "string",
726
+ },
727
+ },
728
+ "type": "object",
729
+ },
730
+ "name": "query_database",
731
+ },
732
+ ],
733
+ }
734
+ `);
735
+ });
736
+
737
+ it('should not include allowed_callers when not specified', async () => {
738
+ const result = await prepareTools({
739
+ tools: [
740
+ {
741
+ type: 'function',
742
+ name: 'testFunction',
743
+ description: 'A test function',
744
+ inputSchema: { type: 'object', properties: {} },
745
+ },
746
+ ],
747
+ toolChoice: undefined,
748
+ supportsStructuredOutput: true,
749
+ });
750
+
751
+ expect(result.tools?.[0]).not.toHaveProperty('allowed_callers');
752
+ });
753
+
754
+ it('should include both deferLoading and allowedCallers when both are set', async () => {
755
+ const result = await prepareTools({
756
+ tools: [
757
+ {
758
+ type: 'function',
759
+ name: 'query_database',
760
+ description: 'Query a database',
761
+ inputSchema: {
762
+ type: 'object',
763
+ properties: { sql: { type: 'string' } },
764
+ },
765
+ providerOptions: {
766
+ anthropic: {
767
+ deferLoading: true,
768
+ allowedCallers: ['code_execution_20250825'],
769
+ },
770
+ },
771
+ },
772
+ ],
773
+ toolChoice: undefined,
774
+ supportsStructuredOutput: true,
775
+ });
776
+
777
+ expect(result.tools?.[0]).toHaveProperty('defer_loading', true);
778
+ expect(result.tools?.[0]).toHaveProperty('allowed_callers', [
779
+ 'code_execution_20250825',
780
+ ]);
781
+ expect(result.betas).toContain('advanced-tool-use-2025-11-20');
782
+ });
783
+ });
784
+
785
+ it('should add warnings for unsupported tools', async () => {
786
+ const result = await prepareTools({
787
+ tools: [
788
+ {
789
+ type: 'provider',
790
+ id: 'unsupported.tool',
791
+ name: 'unsupported_tool',
792
+ args: {},
793
+ },
794
+ ],
795
+ toolChoice: undefined,
796
+ supportsStructuredOutput: true,
797
+ });
798
+
799
+ expect(result.tools).toEqual([]);
800
+ expect(result.toolChoice).toBeUndefined();
801
+ expect(result.toolWarnings).toMatchInlineSnapshot(`
802
+ [
803
+ {
804
+ "feature": "provider-defined tool unsupported.tool",
805
+ "type": "unsupported",
806
+ },
807
+ ]
808
+ `);
809
+ });
810
+
811
+ it('should handle tool choice "auto"', async () => {
812
+ const result = await prepareTools({
813
+ tools: [
814
+ {
815
+ type: 'function',
816
+ name: 'testFunction',
817
+ description: 'Test',
818
+ inputSchema: {},
819
+ },
820
+ ],
821
+ toolChoice: { type: 'auto' },
822
+ supportsStructuredOutput: true,
823
+ });
824
+
825
+ expect(result.toolChoice).toEqual({ type: 'auto' });
826
+ });
827
+
828
+ it('should handle tool choice "required"', async () => {
829
+ const result = await prepareTools({
830
+ tools: [
831
+ {
832
+ type: 'function',
833
+ name: 'testFunction',
834
+ description: 'Test',
835
+ inputSchema: {},
836
+ },
837
+ ],
838
+ toolChoice: { type: 'required' },
839
+ supportsStructuredOutput: true,
840
+ });
841
+
842
+ expect(result.toolChoice).toEqual({ type: 'any' });
843
+ });
844
+
845
+ it('should handle tool choice "none"', async () => {
846
+ const result = await prepareTools({
847
+ tools: [
848
+ {
849
+ type: 'function',
850
+ name: 'testFunction',
851
+ description: 'Test',
852
+ inputSchema: {},
853
+ },
854
+ ],
855
+ toolChoice: { type: 'none' },
856
+ supportsStructuredOutput: true,
857
+ });
858
+ expect(result.tools).toBeUndefined();
859
+ expect(result.toolChoice).toBeUndefined();
860
+ });
861
+
862
+ it('should handle tool choice "tool"', async () => {
863
+ const result = await prepareTools({
864
+ tools: [
865
+ {
866
+ type: 'function',
867
+ name: 'testFunction',
868
+ description: 'Test',
869
+ inputSchema: {},
870
+ },
871
+ ],
872
+ toolChoice: { type: 'tool', toolName: 'testFunction' },
873
+ supportsStructuredOutput: true,
874
+ });
875
+ expect(result.toolChoice).toEqual({ type: 'tool', name: 'testFunction' });
876
+ });
877
+
878
+ it('should set cache control', async () => {
879
+ const result = await prepareTools({
880
+ tools: [
881
+ {
882
+ type: 'function',
883
+ name: 'testFunction',
884
+ description: 'Test',
885
+ inputSchema: {},
886
+ providerOptions: {
887
+ anthropic: {
888
+ cacheControl: { type: 'ephemeral' },
889
+ },
890
+ },
891
+ },
892
+ ],
893
+ toolChoice: undefined,
894
+ supportsStructuredOutput: true,
895
+ });
896
+
897
+ expect(result.tools).toMatchInlineSnapshot(`
898
+ [
899
+ {
900
+ "cache_control": {
901
+ "type": "ephemeral",
902
+ },
903
+ "description": "Test",
904
+ "input_schema": {},
905
+ "name": "testFunction",
906
+ },
907
+ ]
908
+ `);
909
+ });
910
+
911
+ it('should limit cache breakpoints to 4', async () => {
912
+ const cacheControlValidator = new CacheControlValidator();
913
+ const result = await prepareTools({
914
+ tools: [
915
+ {
916
+ type: 'function',
917
+ name: 'tool1',
918
+ description: 'Test 1',
919
+ inputSchema: {},
920
+ providerOptions: {
921
+ anthropic: { cacheControl: { type: 'ephemeral' } },
922
+ },
923
+ },
924
+ {
925
+ type: 'function',
926
+ name: 'tool2',
927
+ description: 'Test 2',
928
+ inputSchema: {},
929
+ providerOptions: {
930
+ anthropic: { cacheControl: { type: 'ephemeral' } },
931
+ },
932
+ },
933
+ {
934
+ type: 'function',
935
+ name: 'tool3',
936
+ description: 'Test 3',
937
+ inputSchema: {},
938
+ providerOptions: {
939
+ anthropic: { cacheControl: { type: 'ephemeral' } },
940
+ },
941
+ },
942
+ {
943
+ type: 'function',
944
+ name: 'tool4',
945
+ description: 'Test 4',
946
+ inputSchema: {},
947
+ providerOptions: {
948
+ anthropic: { cacheControl: { type: 'ephemeral' } },
949
+ },
950
+ },
951
+ {
952
+ type: 'function',
953
+ name: 'tool5',
954
+ description: 'Test 5 (should be rejected)',
955
+ inputSchema: {},
956
+ providerOptions: {
957
+ anthropic: { cacheControl: { type: 'ephemeral' } },
958
+ },
959
+ },
960
+ ],
961
+ toolChoice: undefined,
962
+ supportsStructuredOutput: true,
963
+ cacheControlValidator,
964
+ });
965
+
966
+ // First 4 should have cache_control
967
+ expect(result.tools?.[0]).toHaveProperty('cache_control', {
968
+ type: 'ephemeral',
969
+ });
970
+ expect(result.tools?.[1]).toHaveProperty('cache_control', {
971
+ type: 'ephemeral',
972
+ });
973
+ expect(result.tools?.[2]).toHaveProperty('cache_control', {
974
+ type: 'ephemeral',
975
+ });
976
+ expect(result.tools?.[3]).toHaveProperty('cache_control', {
977
+ type: 'ephemeral',
978
+ });
979
+
980
+ // 5th should be rejected (cache_control should be undefined)
981
+ expect(result.tools?.[4]).toHaveProperty('cache_control', undefined);
982
+
983
+ expect(cacheControlValidator.getWarnings()).toMatchInlineSnapshot(`
984
+ [
985
+ {
986
+ "details": "Maximum 4 cache breakpoints exceeded (found 5). This breakpoint will be ignored.",
987
+ "feature": "cacheControl breakpoint limit",
988
+ "type": "unsupported",
989
+ },
990
+ ]
991
+ `);
992
+ });
993
+ });
994
+
995
+ describe('webFetch_20250910OutputSchema', () => {
996
+ it('should not fail validation when title is null', async () => {
997
+ const problematicResponse = {
998
+ type: 'web_fetch_result',
999
+ url: 'https://test.com',
1000
+ retrievedAt: '2025-12-08T20:46:31.114158',
1001
+ content: {
1002
+ type: 'document',
1003
+ title: null,
1004
+ source: {
1005
+ type: 'text',
1006
+ mediaType: 'text/plain',
1007
+ data: '',
1008
+ },
1009
+ },
1010
+ };
1011
+
1012
+ const schema = webFetch_20250910OutputSchema();
1013
+
1014
+ const result = await schema.validate!(problematicResponse);
1015
+
1016
+ expect(result.success).toBe(true);
1017
+ });
1018
+
1019
+ it('should accept valid response with string title', async () => {
1020
+ const validResponse = {
1021
+ type: 'web_fetch_result',
1022
+ url: 'https://test.com',
1023
+ retrievedAt: '2025-12-08T20:46:31.114158',
1024
+ content: {
1025
+ type: 'document',
1026
+ title: 'Example Title',
1027
+ source: {
1028
+ type: 'text',
1029
+ mediaType: 'text/plain',
1030
+ data: 'Some content',
1031
+ },
1032
+ },
1033
+ };
1034
+
1035
+ const schema = webFetch_20250910OutputSchema();
1036
+ const result = await schema.validate!(validResponse);
1037
+
1038
+ expect(result.success).toBe(true);
1039
+ });
1040
+ });
1041
+
1042
+ describe('webSearch_20250305OutputSchema', () => {
1043
+ it('should not fail validation when title is null', async () => {
1044
+ const problematicResponse = [
1045
+ {
1046
+ url: 'https://test.com',
1047
+ title: null,
1048
+ pageAge: 'April 30, 2025',
1049
+ encryptedContent:
1050
+ 'EqgfCioIARgBIiQ3YTAwMjY1Mi1mZjM5LTQ1NGUtODgxNC1kNjNjNTk1ZWI3Y...',
1051
+ type: 'web_search_result',
1052
+ },
1053
+ ];
1054
+
1055
+ const schema = webSearch_20250305OutputSchema();
1056
+
1057
+ const result = await schema.validate!(problematicResponse);
1058
+
1059
+ expect(result.success).toBe(true);
1060
+ });
1061
+
1062
+ it('should accept valid response with string title', async () => {
1063
+ const validResponse = [
1064
+ {
1065
+ url: 'https://test.com',
1066
+ title: 'Test title',
1067
+ pageAge: 'April 30, 2025',
1068
+ encryptedContent:
1069
+ 'EqgfCioIARgBIiQ3YTAwMjY1Mi1mZjM5LTQ1NGUtODgxNC1kNjNjNTk1ZWI3Y...',
1070
+ type: 'web_search_result',
1071
+ },
1072
+ ];
1073
+
1074
+ const schema = webSearch_20250305OutputSchema();
1075
+ const result = await schema.validate!(validResponse);
1076
+
1077
+ expect(result.success).toBe(true);
1078
+ });
1079
+ });
1080
+
1081
+ describe('anthropicMessagesResponseSchema - web_fetch_tool_result', () => {
1082
+ it('should accept PDF response with base64 source', async () => {
1083
+ const pdfResponse = {
1084
+ type: 'message',
1085
+ id: '123',
1086
+ model: 'claude-3-5-sonnet-20241022',
1087
+ content: [
1088
+ {
1089
+ type: 'web_fetch_tool_result',
1090
+ tool_use_id: 'srvtoolu_01234567890abcdef',
1091
+ content: {
1092
+ type: 'web_fetch_result',
1093
+ url: 'https://test.com',
1094
+ retrieved_at: '2025-12-08T20:46:31.114158',
1095
+ content: {
1096
+ type: 'document',
1097
+ title: 'Example Title',
1098
+ source: {
1099
+ type: 'base64',
1100
+ media_type: 'application/pdf',
1101
+ data: 'JVBERi0xLjcNJeLjz9MNC',
1102
+ },
1103
+ },
1104
+ },
1105
+ },
1106
+ ],
1107
+ stop_reason: 'end_turn',
1108
+ usage: {
1109
+ input_tokens: 100,
1110
+ output_tokens: 50,
1111
+ },
1112
+ };
1113
+
1114
+ const schema = anthropicMessagesResponseSchema();
1115
+ const result = await schema.validate!(pdfResponse);
1116
+
1117
+ expect(result.success).toBe(true);
1118
+ });
1119
+
1120
+ it('should accept text source in response', async () => {
1121
+ const textResponse = {
1122
+ type: 'message',
1123
+ id: '123',
1124
+ model: 'claude-3-5-sonnet-20241022',
1125
+ content: [
1126
+ {
1127
+ type: 'web_fetch_tool_result',
1128
+ tool_use_id: 'srvtoolu_01234567890abcdef',
1129
+ content: {
1130
+ type: 'web_fetch_result',
1131
+ url: 'https://test.com',
1132
+ retrieved_at: '2025-12-08T20:46:31.114158',
1133
+ content: {
1134
+ type: 'document',
1135
+ title: 'Example Title',
1136
+ source: {
1137
+ type: 'text',
1138
+ media_type: 'text/plain',
1139
+ data: 'content',
1140
+ },
1141
+ },
1142
+ },
1143
+ },
1144
+ ],
1145
+ stop_reason: 'end_turn',
1146
+ usage: {
1147
+ input_tokens: 100,
1148
+ output_tokens: 50,
1149
+ },
1150
+ };
1151
+
1152
+ const schema = anthropicMessagesResponseSchema();
1153
+ const result = await schema.validate!(textResponse);
1154
+
1155
+ expect(result.success).toBe(true);
1156
+ });
1157
+ });
1158
+
1159
+ describe('anthropicMessagesChunkSchema - web_fetch_tool_result', () => {
1160
+ it('should accept base64 PDF source in streaming response', async () => {
1161
+ const pdfChunk = {
1162
+ type: 'content_block_start',
1163
+ index: 10,
1164
+ content_block: {
1165
+ type: 'web_fetch_tool_result',
1166
+ tool_use_id: 'srvtoolu_01234567890abcdef',
1167
+ content: {
1168
+ type: 'web_fetch_result',
1169
+ url: 'https://test.com',
1170
+ retrieved_at: '2025-12-08T20:46:31.114158',
1171
+ content: {
1172
+ type: 'document',
1173
+ title: null,
1174
+ source: {
1175
+ type: 'base64',
1176
+ media_type: 'application/pdf',
1177
+ data: 'JVBERi0xLjcNJeLjz9MNC',
1178
+ },
1179
+ },
1180
+ },
1181
+ },
1182
+ };
1183
+
1184
+ const schema = anthropicMessagesChunkSchema();
1185
+ const result = await schema.validate!(pdfChunk);
1186
+
1187
+ expect(result.success).toBe(true);
1188
+ });
1189
+
1190
+ it('should accept text source in streaming response', async () => {
1191
+ const pdfChunk = {
1192
+ type: 'content_block_start',
1193
+ index: 10,
1194
+ content_block: {
1195
+ type: 'web_fetch_tool_result',
1196
+ tool_use_id: 'srvtoolu_01234567890abcdef',
1197
+ content: {
1198
+ type: 'web_fetch_result',
1199
+ url: 'https://test.com',
1200
+ retrieved_at: '2025-12-08T20:46:31.114158',
1201
+ content: {
1202
+ type: 'document',
1203
+ title: null,
1204
+ source: {
1205
+ type: 'text',
1206
+ media_type: 'text/plain',
1207
+ data: 'content',
1208
+ },
1209
+ },
1210
+ },
1211
+ },
1212
+ };
1213
+
1214
+ const schema = anthropicMessagesChunkSchema();
1215
+ const result = await schema.validate!(pdfChunk);
1216
+
1217
+ expect(result.success).toBe(true);
1218
+ });
1219
+ });