@ai-sdk/amazon-bedrock 4.0.28 → 4.0.30

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 (44) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/anthropic/index.js +1 -1
  3. package/dist/anthropic/index.mjs +1 -1
  4. package/dist/index.js +42 -10
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +42 -10
  7. package/dist/index.mjs.map +1 -1
  8. package/docs/08-amazon-bedrock.mdx +7 -0
  9. package/package.json +10 -6
  10. package/src/bedrock-embedding-model.ts +48 -11
  11. package/src/bedrock-embedding-options.ts +14 -0
  12. package/src/__fixtures__/bedrock-json-only-text-first.1.chunks.txt +0 -7
  13. package/src/__fixtures__/bedrock-json-other-tool.1.chunks.txt +0 -6
  14. package/src/__fixtures__/bedrock-json-other-tool.1.json +0 -24
  15. package/src/__fixtures__/bedrock-json-tool-text-then-weather-then-json.1.chunks.txt +0 -12
  16. package/src/__fixtures__/bedrock-json-tool-with-answer.1.json +0 -29
  17. package/src/__fixtures__/bedrock-json-tool.1.chunks.txt +0 -4
  18. package/src/__fixtures__/bedrock-json-tool.1.json +0 -35
  19. package/src/__fixtures__/bedrock-json-tool.2.chunks.txt +0 -6
  20. package/src/__fixtures__/bedrock-json-tool.2.json +0 -28
  21. package/src/__fixtures__/bedrock-json-tool.3.chunks.txt +0 -7
  22. package/src/__fixtures__/bedrock-json-tool.3.json +0 -36
  23. package/src/__fixtures__/bedrock-json-with-tool.1.chunks.txt +0 -9
  24. package/src/__fixtures__/bedrock-json-with-tool.1.json +0 -41
  25. package/src/__fixtures__/bedrock-json-with-tools.1.chunks.txt +0 -12
  26. package/src/__fixtures__/bedrock-json-with-tools.1.json +0 -50
  27. package/src/__fixtures__/bedrock-tool-call.1.chunks.txt +0 -6
  28. package/src/__fixtures__/bedrock-tool-call.1.json +0 -24
  29. package/src/__fixtures__/bedrock-tool-no-args.chunks.txt +0 -8
  30. package/src/__fixtures__/bedrock-tool-no-args.json +0 -25
  31. package/src/anthropic/bedrock-anthropic-fetch.test.ts +0 -344
  32. package/src/anthropic/bedrock-anthropic-provider.test.ts +0 -456
  33. package/src/bedrock-chat-language-model.test.ts +0 -4569
  34. package/src/bedrock-embedding-model.test.ts +0 -148
  35. package/src/bedrock-event-stream-response-handler.test.ts +0 -233
  36. package/src/bedrock-image-model.test.ts +0 -866
  37. package/src/bedrock-provider.test.ts +0 -457
  38. package/src/bedrock-sigv4-fetch.test.ts +0 -675
  39. package/src/convert-bedrock-usage.test.ts +0 -207
  40. package/src/convert-to-bedrock-chat-messages.test.ts +0 -1175
  41. package/src/inject-fetch-headers.test.ts +0 -135
  42. package/src/normalize-tool-call-id.test.ts +0 -72
  43. package/src/reranking/__fixtures__/bedrock-reranking.1.json +0 -12
  44. package/src/reranking/bedrock-reranking-model.test.ts +0 -299
@@ -1,1175 +0,0 @@
1
- import { BedrockReasoningMetadata } from './bedrock-chat-language-model';
2
- import { convertToBedrockChatMessages } from './convert-to-bedrock-chat-messages';
3
- import { describe, it, expect } from 'vitest';
4
-
5
- describe('system messages', () => {
6
- it('should combine multiple leading system messages into a single system message', async () => {
7
- const { system } = await convertToBedrockChatMessages([
8
- { role: 'system', content: 'Hello' },
9
- { role: 'system', content: 'World' },
10
- ]);
11
-
12
- expect(system).toEqual([{ text: 'Hello' }, { text: 'World' }]);
13
- });
14
-
15
- it('should throw an error if a system message is provided after a non-system message', async () => {
16
- await expect(
17
- convertToBedrockChatMessages([
18
- { role: 'user', content: [{ type: 'text', text: 'Hello' }] },
19
- { role: 'system', content: 'World' },
20
- ]),
21
- ).rejects.toThrowError();
22
- });
23
-
24
- it('should set isSystemCachePoint when system message has cache point', async () => {
25
- const result = await convertToBedrockChatMessages([
26
- {
27
- role: 'system',
28
- content: 'Hello',
29
- providerOptions: { bedrock: { cachePoint: { type: 'default' } } },
30
- },
31
- ]);
32
-
33
- expect(result).toEqual({
34
- system: [{ text: 'Hello' }, { cachePoint: { type: 'default' } }],
35
- messages: [],
36
- });
37
- });
38
- });
39
-
40
- describe('user messages', () => {
41
- it('should convert messages with image parts', async () => {
42
- const imageData = new Uint8Array([0, 1, 2, 3]);
43
-
44
- const { messages } = await convertToBedrockChatMessages([
45
- {
46
- role: 'user',
47
- content: [
48
- { type: 'text', text: 'Hello' },
49
- {
50
- type: 'file',
51
- data: Buffer.from(imageData).toString('base64'),
52
- mediaType: 'image/png',
53
- },
54
- ],
55
- },
56
- ]);
57
-
58
- expect(messages).toEqual([
59
- {
60
- role: 'user',
61
- content: [
62
- { text: 'Hello' },
63
- {
64
- image: {
65
- format: 'png',
66
- source: { bytes: 'AAECAw==' },
67
- },
68
- },
69
- ],
70
- },
71
- ]);
72
- });
73
-
74
- it('should convert messages with document parts', async () => {
75
- const fileData = new Uint8Array([0, 1, 2, 3]);
76
-
77
- const { messages } = await convertToBedrockChatMessages([
78
- {
79
- role: 'user',
80
- content: [
81
- { type: 'text', text: 'Hello' },
82
- {
83
- type: 'file',
84
- data: Buffer.from(fileData).toString('base64'),
85
- mediaType: 'application/pdf',
86
- },
87
- ],
88
- },
89
- ]);
90
-
91
- expect(messages).toMatchInlineSnapshot(`
92
- [
93
- {
94
- "content": [
95
- {
96
- "text": "Hello",
97
- },
98
- {
99
- "document": {
100
- "format": "pdf",
101
- "name": "document-1",
102
- "source": {
103
- "bytes": "AAECAw==",
104
- },
105
- },
106
- },
107
- ],
108
- "role": "user",
109
- },
110
- ]
111
- `);
112
- });
113
-
114
- it('should be converted with actual filename when provided', async () => {
115
- const fileData = new Uint8Array([0, 1, 2, 3]);
116
-
117
- const { messages } = await convertToBedrockChatMessages([
118
- {
119
- role: 'user',
120
- content: [
121
- { type: 'text', text: 'Hello' },
122
- {
123
- type: 'file',
124
- data: Buffer.from(fileData).toString('base64'),
125
- mediaType: 'application/pdf',
126
- filename: 'custom-filename',
127
- },
128
- ],
129
- },
130
- ]);
131
-
132
- expect(messages).toMatchInlineSnapshot(`
133
- [
134
- {
135
- "content": [
136
- {
137
- "text": "Hello",
138
- },
139
- {
140
- "document": {
141
- "format": "pdf",
142
- "name": "custom-filename",
143
- "source": {
144
- "bytes": "AAECAw==",
145
- },
146
- },
147
- },
148
- ],
149
- "role": "user",
150
- },
151
- ]
152
- `);
153
- });
154
-
155
- it('should use consistent document names for prompt cache effectiveness', async () => {
156
- const fileData1 = new Uint8Array([0, 1, 2, 3]);
157
- const fileData2 = new Uint8Array([4, 5, 6, 7]);
158
-
159
- const { messages } = await convertToBedrockChatMessages([
160
- {
161
- role: 'user',
162
- content: [
163
- {
164
- type: 'file',
165
- data: Buffer.from(fileData1).toString('base64'),
166
- mediaType: 'application/pdf',
167
- },
168
- {
169
- type: 'file',
170
- data: Buffer.from(fileData2).toString('base64'),
171
- mediaType: 'application/pdf',
172
- },
173
- ],
174
- },
175
- {
176
- role: 'assistant',
177
- content: [{ type: 'text', text: 'OK' }],
178
- },
179
- {
180
- role: 'user',
181
- content: [
182
- {
183
- type: 'file',
184
- data: Buffer.from(fileData1).toString('base64'),
185
- mediaType: 'application/pdf',
186
- },
187
- ],
188
- },
189
- ]);
190
-
191
- expect(messages).toMatchInlineSnapshot(`
192
- [
193
- {
194
- "content": [
195
- {
196
- "document": {
197
- "format": "pdf",
198
- "name": "document-1",
199
- "source": {
200
- "bytes": "AAECAw==",
201
- },
202
- },
203
- },
204
- {
205
- "document": {
206
- "format": "pdf",
207
- "name": "document-2",
208
- "source": {
209
- "bytes": "BAUGBw==",
210
- },
211
- },
212
- },
213
- ],
214
- "role": "user",
215
- },
216
- {
217
- "content": [
218
- {
219
- "text": "OK",
220
- },
221
- ],
222
- "role": "assistant",
223
- },
224
- {
225
- "content": [
226
- {
227
- "document": {
228
- "format": "pdf",
229
- "name": "document-3",
230
- "source": {
231
- "bytes": "AAECAw==",
232
- },
233
- },
234
- },
235
- ],
236
- "role": "user",
237
- },
238
- ]
239
- `);
240
- });
241
-
242
- it('should extract the system message', async () => {
243
- const { system } = await convertToBedrockChatMessages([
244
- {
245
- role: 'system',
246
- content: 'Hello',
247
- },
248
- ]);
249
-
250
- expect(system).toEqual([{ text: 'Hello' }]);
251
- });
252
-
253
- it('should add cache point to user message content when specified', async () => {
254
- const result = await convertToBedrockChatMessages([
255
- {
256
- role: 'user',
257
- content: [{ type: 'text', text: 'Hello' }],
258
- providerOptions: { bedrock: { cachePoint: { type: 'default' } } },
259
- },
260
- ]);
261
-
262
- expect(result).toEqual({
263
- messages: [
264
- {
265
- role: 'user',
266
- content: [{ text: 'Hello' }, { cachePoint: { type: 'default' } }],
267
- },
268
- ],
269
- system: [],
270
- });
271
- });
272
- });
273
-
274
- describe('assistant messages', () => {
275
- it('should remove trailing whitespace from last assistant message when there is no further user message', async () => {
276
- const result = await convertToBedrockChatMessages([
277
- {
278
- role: 'user',
279
- content: [{ type: 'text', text: 'user content' }],
280
- },
281
- {
282
- role: 'assistant',
283
- content: [{ type: 'text', text: 'assistant content ' }],
284
- },
285
- ]);
286
-
287
- expect(result).toEqual({
288
- messages: [
289
- {
290
- role: 'user',
291
- content: [{ text: 'user content' }],
292
- },
293
- {
294
- role: 'assistant',
295
- content: [{ text: 'assistant content' }],
296
- },
297
- ],
298
- system: [],
299
- });
300
- });
301
-
302
- it('should remove trailing whitespace from last assistant message with multi-part content when there is no further user message', async () => {
303
- const result = await convertToBedrockChatMessages([
304
- {
305
- role: 'user',
306
- content: [{ type: 'text', text: 'user content' }],
307
- },
308
- {
309
- role: 'assistant',
310
- content: [
311
- { type: 'text', text: 'assistant ' },
312
- { type: 'text', text: 'content ' },
313
- ],
314
- },
315
- ]);
316
-
317
- expect(result).toEqual({
318
- messages: [
319
- {
320
- role: 'user',
321
- content: [{ text: 'user content' }],
322
- },
323
- {
324
- role: 'assistant',
325
- content: [{ text: 'assistant ' }, { text: 'content' }],
326
- },
327
- ],
328
- system: [],
329
- });
330
- });
331
-
332
- it('should keep trailing whitespace from assistant message when there is a further user message', async () => {
333
- const result = await convertToBedrockChatMessages([
334
- {
335
- role: 'user',
336
- content: [{ type: 'text', text: 'user content' }],
337
- },
338
- {
339
- role: 'assistant',
340
- content: [{ type: 'text', text: 'assistant content ' }],
341
- },
342
- {
343
- role: 'user',
344
- content: [{ type: 'text', text: 'user content 2' }],
345
- },
346
- ]);
347
-
348
- expect(result).toEqual({
349
- messages: [
350
- {
351
- role: 'user',
352
- content: [{ text: 'user content' }],
353
- },
354
- {
355
- role: 'assistant',
356
- content: [{ text: 'assistant content ' }],
357
- },
358
- {
359
- role: 'user',
360
- content: [{ text: 'user content 2' }],
361
- },
362
- ],
363
- system: [],
364
- });
365
- });
366
-
367
- it('should combine multiple sequential assistant messages into a single message', async () => {
368
- const result = await convertToBedrockChatMessages([
369
- { role: 'user', content: [{ type: 'text', text: 'Hi!' }] },
370
- { role: 'assistant', content: [{ type: 'text', text: 'Hello' }] },
371
- { role: 'assistant', content: [{ type: 'text', text: 'World' }] },
372
- { role: 'assistant', content: [{ type: 'text', text: '!' }] },
373
- ]);
374
-
375
- expect(result).toEqual({
376
- messages: [
377
- { role: 'user', content: [{ text: 'Hi!' }] },
378
- {
379
- role: 'assistant',
380
- content: [{ text: 'Hello' }, { text: 'World' }, { text: '!' }],
381
- },
382
- ],
383
- system: [],
384
- });
385
- });
386
-
387
- it('should add cache point to assistant message content when specified', async () => {
388
- const result = await convertToBedrockChatMessages([
389
- {
390
- role: 'assistant',
391
- content: [{ type: 'text', text: 'Hello' }],
392
- providerOptions: { bedrock: { cachePoint: { type: 'default' } } },
393
- },
394
- ]);
395
-
396
- expect(result).toEqual({
397
- messages: [
398
- {
399
- role: 'assistant',
400
- content: [{ text: 'Hello' }, { cachePoint: { type: 'default' } }],
401
- },
402
- ],
403
- system: [],
404
- });
405
- });
406
-
407
- it('should properly convert reasoning content type', async () => {
408
- const result = await convertToBedrockChatMessages([
409
- {
410
- role: 'user',
411
- content: [{ type: 'text', text: 'Explain your reasoning' }],
412
- },
413
- {
414
- role: 'assistant',
415
- content: [
416
- {
417
- type: 'reasoning',
418
- text: 'This is my step-by-step reasoning process',
419
- providerOptions: {
420
- bedrock: {
421
- signature: 'test-signature',
422
- } satisfies BedrockReasoningMetadata,
423
- },
424
- },
425
- ],
426
- },
427
- ]);
428
-
429
- expect(result).toEqual({
430
- messages: [
431
- {
432
- role: 'user',
433
- content: [{ text: 'Explain your reasoning' }],
434
- },
435
- {
436
- role: 'assistant',
437
- content: [
438
- {
439
- reasoningContent: {
440
- reasoningText: {
441
- text: 'This is my step-by-step reasoning process',
442
- signature: 'test-signature',
443
- },
444
- },
445
- },
446
- ],
447
- },
448
- ],
449
- system: [],
450
- });
451
- });
452
-
453
- it('should properly convert redacted-reasoning content type', async () => {
454
- const reasoningData = 'Redacted reasoning information';
455
- const result = await convertToBedrockChatMessages([
456
- {
457
- role: 'user',
458
- content: [{ type: 'text', text: 'Explain your reasoning' }],
459
- },
460
- {
461
- role: 'assistant',
462
- content: [
463
- {
464
- type: 'reasoning',
465
- text: '',
466
- providerOptions: { bedrock: { redactedData: reasoningData } },
467
- },
468
- ],
469
- },
470
- ]);
471
-
472
- expect(result).toEqual({
473
- messages: [
474
- {
475
- role: 'user',
476
- content: [{ text: 'Explain your reasoning' }],
477
- },
478
- {
479
- role: 'assistant',
480
- content: [
481
- {
482
- reasoningContent: {
483
- redactedReasoning: {
484
- data: reasoningData,
485
- },
486
- },
487
- },
488
- ],
489
- },
490
- ],
491
- system: [],
492
- });
493
- });
494
-
495
- it('should trim trailing whitespace from reasoning content when it is the last part', async () => {
496
- const result = await convertToBedrockChatMessages([
497
- {
498
- role: 'user',
499
- content: [{ type: 'text', text: 'Explain your reasoning' }],
500
- },
501
- {
502
- role: 'assistant',
503
- content: [
504
- {
505
- type: 'reasoning',
506
- text: 'This is my reasoning with trailing space ',
507
- providerOptions: {
508
- bedrock: {
509
- signature: 'test-signature',
510
- } satisfies BedrockReasoningMetadata,
511
- },
512
- },
513
- ],
514
- },
515
- ]);
516
-
517
- expect(result).toEqual({
518
- messages: [
519
- {
520
- role: 'user',
521
- content: [{ text: 'Explain your reasoning' }],
522
- },
523
- {
524
- role: 'assistant',
525
- content: [
526
- {
527
- reasoningContent: {
528
- reasoningText: {
529
- text: 'This is my reasoning with trailing space',
530
- signature: 'test-signature',
531
- },
532
- },
533
- },
534
- ],
535
- },
536
- ],
537
- system: [],
538
- });
539
- });
540
-
541
- it('should handle a mix of text and reasoning content types', async () => {
542
- const result = await convertToBedrockChatMessages([
543
- {
544
- role: 'user',
545
- content: [{ type: 'text', text: 'Explain your reasoning' }],
546
- },
547
- {
548
- role: 'assistant',
549
- content: [
550
- { type: 'text', text: 'My answer is 42.' },
551
- {
552
- type: 'reasoning',
553
- text: 'I calculated this by analyzing the meaning of life',
554
- providerOptions: {
555
- bedrock: {
556
- signature: 'reasoning-process',
557
- } satisfies BedrockReasoningMetadata,
558
- },
559
- },
560
- ],
561
- },
562
- ]);
563
-
564
- expect(result).toEqual({
565
- messages: [
566
- {
567
- role: 'user',
568
- content: [{ text: 'Explain your reasoning' }],
569
- },
570
- {
571
- role: 'assistant',
572
- content: [
573
- { text: 'My answer is 42.' },
574
- {
575
- reasoningContent: {
576
- reasoningText: {
577
- text: 'I calculated this by analyzing the meaning of life',
578
- signature: 'reasoning-process',
579
- },
580
- },
581
- },
582
- ],
583
- },
584
- ],
585
- system: [],
586
- });
587
- });
588
-
589
- it('should filter out empty text blocks in assistant messages', async () => {
590
- const result = await convertToBedrockChatMessages([
591
- {
592
- role: 'user',
593
- content: [{ type: 'text', text: 'Hello' }],
594
- },
595
- {
596
- role: 'assistant',
597
- content: [
598
- { type: 'text', text: '\n\n' },
599
- {
600
- type: 'tool-call',
601
- toolCallId: 'call-123',
602
- toolName: 'test',
603
- input: {},
604
- },
605
- { type: 'text', text: ' ' },
606
- { type: 'text', text: 'actual content' },
607
- ],
608
- },
609
- ]);
610
-
611
- expect(result).toEqual({
612
- messages: [
613
- {
614
- role: 'user',
615
- content: [{ text: 'Hello' }],
616
- },
617
- {
618
- role: 'assistant',
619
- content: [
620
- { toolUse: { toolUseId: 'call-123', name: 'test', input: {} } },
621
- { text: 'actual content' },
622
- ],
623
- },
624
- ],
625
- system: [],
626
- });
627
- });
628
- });
629
-
630
- describe('tool messages', () => {
631
- it('should convert tool result with content array containing text', async () => {
632
- const result = await convertToBedrockChatMessages([
633
- {
634
- role: 'tool',
635
- content: [
636
- {
637
- type: 'tool-result',
638
- toolCallId: 'call-123',
639
- toolName: 'calculator',
640
- output: {
641
- type: 'content',
642
- value: [{ type: 'text', text: 'The result is 42' }],
643
- },
644
- },
645
- ],
646
- },
647
- ]);
648
-
649
- expect(result.messages[0]).toEqual({
650
- role: 'user',
651
- content: [
652
- {
653
- toolResult: {
654
- toolUseId: 'call-123',
655
- content: [{ text: 'The result is 42' }],
656
- },
657
- },
658
- ],
659
- });
660
- });
661
-
662
- it('should convert tool result with content array containing image', async () => {
663
- const result = await convertToBedrockChatMessages([
664
- {
665
- role: 'tool',
666
- content: [
667
- {
668
- type: 'tool-result',
669
- toolCallId: 'call-123',
670
- toolName: 'image-generator',
671
- output: {
672
- type: 'content',
673
- value: [
674
- {
675
- type: 'image-data',
676
- data: 'base64data',
677
- mediaType: 'image/jpeg',
678
- },
679
- ],
680
- },
681
- },
682
- ],
683
- },
684
- ]);
685
-
686
- expect(result.messages[0]).toEqual({
687
- role: 'user',
688
- content: [
689
- {
690
- toolResult: {
691
- toolUseId: 'call-123',
692
- content: [
693
- {
694
- image: {
695
- format: 'jpeg',
696
- source: { bytes: 'base64data' },
697
- },
698
- },
699
- ],
700
- },
701
- },
702
- ],
703
- });
704
- });
705
-
706
- it('should throw error for unsupported image format in tool result content', async () => {
707
- await expect(
708
- convertToBedrockChatMessages([
709
- {
710
- role: 'tool',
711
- content: [
712
- {
713
- type: 'tool-result',
714
- toolCallId: 'call-123',
715
- toolName: 'image-generator',
716
- output: {
717
- type: 'content',
718
- value: [
719
- {
720
- type: 'image-data',
721
- data: 'base64data',
722
- mediaType: 'image/avif', // unsupported format
723
- },
724
- ],
725
- },
726
- },
727
- ],
728
- },
729
- ]),
730
- ).rejects.toThrowErrorMatchingInlineSnapshot(
731
- `[AI_UnsupportedFunctionalityError: Unsupported image mime type: image/avif, expected one of: image/jpeg, image/png, image/gif, image/webp]`,
732
- );
733
- });
734
-
735
- it('should throw error for unsupported mime type in tool result image content', async () => {
736
- await expect(
737
- convertToBedrockChatMessages([
738
- {
739
- role: 'tool',
740
- content: [
741
- {
742
- type: 'tool-result',
743
- toolCallId: 'call-123',
744
- toolName: 'image-generator',
745
- output: {
746
- type: 'content',
747
- value: [
748
- {
749
- type: 'image-data',
750
- data: 'base64data',
751
- mediaType: 'unsupported/mime-type',
752
- },
753
- ],
754
- },
755
- },
756
- ],
757
- },
758
- ]),
759
- ).rejects.toThrowErrorMatchingInlineSnapshot(
760
- `[AI_UnsupportedFunctionalityError: 'media type: unsupported/mime-type' functionality not supported.]`,
761
- );
762
- });
763
-
764
- it('should fallback to stringified result when content is undefined', async () => {
765
- const result = await convertToBedrockChatMessages([
766
- {
767
- role: 'tool',
768
- content: [
769
- {
770
- type: 'tool-result',
771
- toolCallId: 'call-123',
772
- toolName: 'calculator',
773
- output: { type: 'json', value: { value: 42 } },
774
- },
775
- ],
776
- },
777
- ]);
778
-
779
- expect(result.messages[0]).toEqual({
780
- role: 'user',
781
- content: [
782
- {
783
- toolResult: {
784
- toolUseId: 'call-123',
785
- content: [{ text: '{"value":42}' }],
786
- },
787
- },
788
- ],
789
- });
790
- });
791
- });
792
-
793
- describe('citations', () => {
794
- it('should handle citations enabled for PDF', async () => {
795
- const pdfData = new Uint8Array([0, 1, 2, 3]);
796
-
797
- const result = await convertToBedrockChatMessages([
798
- {
799
- role: 'user',
800
- content: [
801
- {
802
- type: 'file',
803
- data: Buffer.from(pdfData).toString('base64'),
804
- mediaType: 'application/pdf',
805
- providerOptions: {
806
- bedrock: {
807
- citations: {
808
- enabled: true,
809
- },
810
- },
811
- },
812
- },
813
- ],
814
- },
815
- ]);
816
-
817
- expect(result.messages[0].content[0]).toEqual({
818
- document: {
819
- format: 'pdf',
820
- name: 'document-1',
821
- source: {
822
- bytes: 'AAECAw==',
823
- },
824
- citations: {
825
- enabled: true,
826
- },
827
- },
828
- });
829
- });
830
-
831
- it('should handle citations disabled for PDF', async () => {
832
- const pdfData = new Uint8Array([0, 1, 2, 3]);
833
-
834
- const result = await convertToBedrockChatMessages([
835
- {
836
- role: 'user',
837
- content: [
838
- {
839
- type: 'file',
840
- data: Buffer.from(pdfData).toString('base64'),
841
- mediaType: 'application/pdf',
842
- providerOptions: {
843
- bedrock: {
844
- citations: {
845
- enabled: false,
846
- },
847
- },
848
- },
849
- },
850
- ],
851
- },
852
- ]);
853
-
854
- expect(result.messages[0].content[0]).toEqual({
855
- document: {
856
- format: 'pdf',
857
- name: 'document-1',
858
- source: {
859
- bytes: 'AAECAw==',
860
- },
861
- },
862
- });
863
- });
864
-
865
- it('should handle no citations specified for PDF (default)', async () => {
866
- const pdfData = new Uint8Array([0, 1, 2, 3]);
867
-
868
- const result = await convertToBedrockChatMessages([
869
- {
870
- role: 'user',
871
- content: [
872
- {
873
- type: 'file',
874
- data: Buffer.from(pdfData).toString('base64'),
875
- mediaType: 'application/pdf',
876
- },
877
- ],
878
- },
879
- ]);
880
-
881
- expect(result.messages[0].content[0]).toEqual({
882
- document: {
883
- format: 'pdf',
884
- name: 'document-1',
885
- source: {
886
- bytes: 'AAECAw==',
887
- },
888
- },
889
- });
890
- });
891
-
892
- it('should handle multiple PDFs with different citation settings', async () => {
893
- const pdfData1 = new Uint8Array([0, 1, 2, 3]);
894
- const pdfData2 = new Uint8Array([4, 5, 6, 7]);
895
-
896
- const result = await convertToBedrockChatMessages([
897
- {
898
- role: 'user',
899
- content: [
900
- {
901
- type: 'file',
902
- data: Buffer.from(pdfData1).toString('base64'),
903
- mediaType: 'application/pdf',
904
- providerOptions: {
905
- bedrock: {
906
- citations: {
907
- enabled: true,
908
- },
909
- },
910
- },
911
- },
912
- {
913
- type: 'file',
914
- data: Buffer.from(pdfData2).toString('base64'),
915
- mediaType: 'application/pdf',
916
- providerOptions: {
917
- bedrock: {
918
- citations: {
919
- enabled: false,
920
- },
921
- },
922
- },
923
- },
924
- ],
925
- },
926
- ]);
927
-
928
- expect(result.messages[0].content).toEqual([
929
- {
930
- document: {
931
- format: 'pdf',
932
- name: 'document-1',
933
- source: {
934
- bytes: 'AAECAw==',
935
- },
936
- citations: {
937
- enabled: true,
938
- },
939
- },
940
- },
941
- {
942
- document: {
943
- format: 'pdf',
944
- name: 'document-2',
945
- source: {
946
- bytes: 'BAUGBw==',
947
- },
948
- },
949
- },
950
- ]);
951
- });
952
- });
953
-
954
- describe('additional file format tests', () => {
955
- it('should throw an error for unsupported file mime type in user message content', async () => {
956
- await expect(
957
- convertToBedrockChatMessages([
958
- {
959
- role: 'user',
960
- content: [
961
- {
962
- type: 'file',
963
- data: 'base64data',
964
- mediaType: 'application/rtf',
965
- },
966
- ],
967
- },
968
- ]),
969
- ).rejects.toThrowErrorMatchingInlineSnapshot(
970
- `[AI_UnsupportedFunctionalityError: Unsupported file mime type: application/rtf, expected one of: application/pdf, text/csv, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, text/html, text/plain, text/markdown]`,
971
- );
972
- });
973
-
974
- it('should handle xlsx files correctly', async () => {
975
- const result = await convertToBedrockChatMessages([
976
- {
977
- role: 'user',
978
- content: [
979
- {
980
- type: 'file',
981
- data: 'base64data',
982
- mediaType:
983
- 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
984
- },
985
- ],
986
- },
987
- ]);
988
-
989
- expect(result).toMatchInlineSnapshot(`
990
- {
991
- "messages": [
992
- {
993
- "content": [
994
- {
995
- "document": {
996
- "format": "xlsx",
997
- "name": "document-1",
998
- "source": {
999
- "bytes": "base64data",
1000
- },
1001
- },
1002
- },
1003
- ],
1004
- "role": "user",
1005
- },
1006
- ],
1007
- "system": [],
1008
- }
1009
- `);
1010
- });
1011
-
1012
- it('should handle docx files correctly', async () => {
1013
- const result = await convertToBedrockChatMessages([
1014
- {
1015
- role: 'user',
1016
- content: [
1017
- {
1018
- type: 'file',
1019
- data: 'base64data',
1020
- mediaType:
1021
- 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
1022
- },
1023
- ],
1024
- },
1025
- ]);
1026
-
1027
- expect(result).toMatchInlineSnapshot(`
1028
- {
1029
- "messages": [
1030
- {
1031
- "content": [
1032
- {
1033
- "document": {
1034
- "format": "docx",
1035
- "name": "document-1",
1036
- "source": {
1037
- "bytes": "base64data",
1038
- },
1039
- },
1040
- },
1041
- ],
1042
- "role": "user",
1043
- },
1044
- ],
1045
- "system": [],
1046
- }
1047
- `);
1048
- });
1049
- });
1050
-
1051
- describe('Mistral tool call ID normalization', () => {
1052
- it('should normalize tool call IDs in tool results when isMistral is true', async () => {
1053
- const result = await convertToBedrockChatMessages(
1054
- [
1055
- {
1056
- role: 'tool',
1057
- content: [
1058
- {
1059
- type: 'tool-result',
1060
- toolCallId: 'tooluse_bpe71yCfRu2b5i-nKGDr5g',
1061
- toolName: 'calculator',
1062
- output: { type: 'text', value: 'The result is 42' },
1063
- },
1064
- ],
1065
- },
1066
- ],
1067
- true,
1068
- );
1069
-
1070
- expect(result.messages[0]).toEqual({
1071
- role: 'user',
1072
- content: [
1073
- {
1074
- toolResult: {
1075
- toolUseId: 'toolusebp',
1076
- content: [{ text: 'The result is 42' }],
1077
- },
1078
- },
1079
- ],
1080
- });
1081
- });
1082
-
1083
- it('should normalize tool call IDs in tool calls when isMistral is true', async () => {
1084
- const result = await convertToBedrockChatMessages(
1085
- [
1086
- {
1087
- role: 'assistant',
1088
- content: [
1089
- {
1090
- type: 'tool-call',
1091
- toolCallId: 'tooluse_xyz123ABC456-def',
1092
- toolName: 'test-tool',
1093
- input: { query: 'test' },
1094
- },
1095
- ],
1096
- },
1097
- ],
1098
- true,
1099
- );
1100
-
1101
- expect(result.messages[0]).toEqual({
1102
- role: 'assistant',
1103
- content: [
1104
- {
1105
- toolUse: {
1106
- toolUseId: 'toolusexy',
1107
- name: 'test-tool',
1108
- input: { query: 'test' },
1109
- },
1110
- },
1111
- ],
1112
- });
1113
- });
1114
-
1115
- it('should not normalize tool call IDs when isMistral is false', async () => {
1116
- const originalId = 'tooluse_bpe71yCfRu2b5i-nKGDr5g';
1117
- const result = await convertToBedrockChatMessages(
1118
- [
1119
- {
1120
- role: 'tool',
1121
- content: [
1122
- {
1123
- type: 'tool-result',
1124
- toolCallId: originalId,
1125
- toolName: 'calculator',
1126
- output: { type: 'text', value: 'The result is 42' },
1127
- },
1128
- ],
1129
- },
1130
- ],
1131
- false,
1132
- );
1133
-
1134
- expect(result.messages[0]).toEqual({
1135
- role: 'user',
1136
- content: [
1137
- {
1138
- toolResult: {
1139
- toolUseId: originalId,
1140
- content: [{ text: 'The result is 42' }],
1141
- },
1142
- },
1143
- ],
1144
- });
1145
- });
1146
-
1147
- it('should default to not normalizing when isMistral is not provided', async () => {
1148
- const originalId = 'tooluse_bpe71yCfRu2b5i-nKGDr5g';
1149
- const result = await convertToBedrockChatMessages([
1150
- {
1151
- role: 'tool',
1152
- content: [
1153
- {
1154
- type: 'tool-result',
1155
- toolCallId: originalId,
1156
- toolName: 'calculator',
1157
- output: { type: 'text', value: 'The result is 42' },
1158
- },
1159
- ],
1160
- },
1161
- ]);
1162
-
1163
- expect(result.messages[0]).toEqual({
1164
- role: 'user',
1165
- content: [
1166
- {
1167
- toolResult: {
1168
- toolUseId: originalId,
1169
- content: [{ text: 'The result is 42' }],
1170
- },
1171
- },
1172
- ],
1173
- });
1174
- });
1175
- });