bedrock-wrapper 2.4.4 → 2.5.0

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.
@@ -15,6 +15,7 @@ import { bedrock_models } from "./bedrock-models.js";
15
15
  import {
16
16
  BedrockRuntimeClient,
17
17
  InvokeModelCommand, InvokeModelWithResponseStreamCommand,
18
+ ConverseCommand, ConverseStreamCommand,
18
19
  } from "@aws-sdk/client-bedrock-runtime";
19
20
  // helper functions
20
21
  import {
@@ -62,14 +63,101 @@ async function processImage(imageInput) {
62
63
  return processedImage.toString('base64');
63
64
  }
64
65
 
65
- export async function* bedrockWrapper(awsCreds, openaiChatCompletionsCreateObject, { logging = false } = {} ) {
66
- const { region, accessKeyId, secretAccessKey } = awsCreds;
67
- let { messages, model, max_tokens, stream, temperature, top_p, include_thinking_data, stop, stop_sequences } = openaiChatCompletionsCreateObject;
68
-
66
+ function processReasoningTags(text, awsModel) {
67
+ if (!text) return text;
68
+
69
+ // Check if this is a GPT-OSS model (has reasoning tags)
70
+ const hasReasoningTags = text.includes('<reasoning>') && text.includes('</reasoning>');
71
+
72
+ if (!hasReasoningTags) {
73
+ return text;
74
+ }
75
+
76
+ // If model should preserve reasoning, return as-is
77
+ if (awsModel.preserve_reasoning) {
78
+ return text;
79
+ }
80
+
81
+ // Strip reasoning tags for non-thinking GPT-OSS models
82
+ return text.replace(/<reasoning>[\s\S]*?<\/reasoning>/g, '').trim();
83
+ }
69
84
 
70
- let {awsModelId, awsModel} = findAwsModelWithId(model);
85
+ // Convert messages to Converse API format
86
+ async function convertToConverseFormat(messages) {
87
+ const converseMessages = [];
88
+ let systemPrompts = [];
89
+
90
+ for (const msg of messages) {
91
+ if (msg.role === "system") {
92
+ // System messages are handled separately in Converse API
93
+ if (typeof msg.content === 'string') {
94
+ systemPrompts.push({ text: msg.content });
95
+ } else if (Array.isArray(msg.content)) {
96
+ // Extract text from content array
97
+ const textContent = msg.content
98
+ .filter(item => item.type === 'text')
99
+ .map(item => item.text || item)
100
+ .join('\n');
101
+ if (textContent) {
102
+ systemPrompts.push({ text: textContent });
103
+ }
104
+ }
105
+ } else {
106
+ // Convert user and assistant messages
107
+ let content = [];
108
+
109
+ if (typeof msg.content === 'string') {
110
+ content = [{ text: msg.content }];
111
+ } else if (Array.isArray(msg.content)) {
112
+ for (const item of msg.content) {
113
+ if (item.type === 'text') {
114
+ content.push({ text: item.text || item });
115
+ } else if (item.type === 'image') {
116
+ // Handle image content
117
+ if (item.source && item.source.data) {
118
+ content.push({
119
+ image: {
120
+ format: 'jpeg',
121
+ source: {
122
+ bytes: Buffer.from(item.source.data, 'base64')
123
+ }
124
+ }
125
+ });
126
+ }
127
+ } else if (item.type === 'image_url') {
128
+ // Process image URL to base64
129
+ const processedImage = await processImage(
130
+ typeof item.image_url === 'string' ?
131
+ item.image_url :
132
+ item.image_url.url
133
+ );
134
+ content.push({
135
+ image: {
136
+ format: 'jpeg',
137
+ source: {
138
+ bytes: Buffer.from(processedImage, 'base64')
139
+ }
140
+ }
141
+ });
142
+ }
143
+ }
144
+ }
145
+
146
+ // Only add messages with actual content (Converse API doesn't allow empty content)
147
+ if (content.length > 0) {
148
+ converseMessages.push({
149
+ role: msg.role,
150
+ content: content
151
+ });
152
+ }
153
+ }
154
+ }
155
+
156
+ return { messages: converseMessages, system: systemPrompts };
157
+ }
71
158
 
72
- // cleanup message content before formatting prompt message
159
+ // Process messages for Invoke API (complex model-specific formatting)
160
+ async function processMessagesForInvoke(messages, awsModel) {
73
161
  let message_cleaned = [];
74
162
  let system_message = "";
75
163
 
@@ -130,15 +218,17 @@ export async function* bedrockWrapper(awsCreds, openaiChatCompletionsCreateObjec
130
218
  message_cleaned.push({role: "assistant", content: ""});
131
219
  }
132
220
  }
221
+
222
+ return { message_cleaned, system_message };
223
+ }
133
224
 
134
- let prompt;
135
-
136
- // format prompt message from message array
225
+ // Build prompt for Invoke API (model-specific formatting)
226
+ function buildInvokePrompt(message_cleaned, awsModel) {
137
227
  if (awsModel.messages_api) {
138
228
  // convert message array to prompt object if model supports messages api
139
- prompt = message_cleaned;
229
+ return message_cleaned;
140
230
  } else {
141
- prompt = awsModel.bos_text;
231
+ let prompt = awsModel.bos_text;
142
232
  let eom_text_inserted = false;
143
233
 
144
234
  for (let i = 0; i < message_cleaned.length; i++) {
@@ -194,45 +284,13 @@ export async function* bedrockWrapper(awsCreds, openaiChatCompletionsCreateObjec
194
284
  prompt += `\n${awsModel.eom_text}`;
195
285
  }
196
286
  }
287
+ return prompt;
197
288
  }
289
+ }
198
290
 
199
- // Add logging to see the final prompt
200
- if (logging) {
201
- console.log("\nFinal formatted prompt:", prompt);
202
- }
203
-
204
- let max_gen_tokens = max_tokens <= awsModel.max_supported_response_tokens ? max_tokens : awsModel.max_supported_response_tokens;
205
-
206
- if (awsModel.special_request_schema?.thinking?.type === "enabled") {
207
- // temperature may only be set to 1 when thinking is enabled
208
- temperature = 1;
209
- // top_p must be unset when thinking is enabled
210
- top_p = undefined;
211
- // bugget_tokens can not be greater than 80% of max_gen_tokens
212
- let budget_tokens = awsModel.special_request_schema?.thinking?.budget_tokens;
213
- if (budget_tokens > (max_gen_tokens * 0.8)) {
214
- budget_tokens = Math.floor(max_gen_tokens * 0.8);
215
- }
216
- if (budget_tokens < 1024) {
217
- budget_tokens = 1024;
218
- }
219
- // if awsModel.special_request_schema?.thinking?.budget_tokens, set it to budget_tokens
220
- if (awsModel.special_request_schema?.thinking?.budget_tokens) {
221
- awsModel.special_request_schema.thinking.budget_tokens = budget_tokens;
222
- // max_gen_tokens has to be greater than budget_tokens
223
- if (max_gen_tokens <= budget_tokens) {
224
- // make max_gen_tokens 20% greater than budget_tokens
225
- max_gen_tokens = Math.floor(budget_tokens * 1.2);
226
- }
227
- }
228
- }
229
-
230
- // if (logging) {
231
- // console.log("\nMax tokens:", max_gen_tokens);
232
- // }
233
-
234
- // Format the request payload using the model's native structure.
235
- const request = awsModel.messages_api ? (() => {
291
+ // Build request object for Invoke API (model-specific)
292
+ function buildInvokeRequest(prompt, awsModel, max_gen_tokens, temperature, top_p, stop_sequences, stop, system_message) {
293
+ if (awsModel.messages_api) {
236
294
  // Check if this is a Nova model (has schemaVersion in special_request_schema)
237
295
  if (awsModel.special_request_schema?.schemaVersion === "messages-v1") {
238
296
  // Nova model format - convert messages to Nova's expected format
@@ -304,44 +362,36 @@ export async function* bedrockWrapper(awsCreds, openaiChatCompletionsCreateObjec
304
362
  ...awsModel.special_request_schema
305
363
  };
306
364
  }
307
- })() : {
308
- prompt: typeof prompt === 'string' ? prompt : {
309
- messages: prompt.map(msg => ({
310
- role: msg.role,
311
- content: Array.isArray(msg.content) ?
312
- msg.content.map(item =>
313
- item.type === 'text' ? item.text : item
314
- ).join('\n') :
315
- msg.content
316
- }))
317
- },
318
- // Optional inference parameters:
319
- [awsModel.max_tokens_param_name]: max_gen_tokens,
320
- temperature: temperature,
321
- top_p: top_p,
322
- ...(() => {
323
- const stopSequencesValue = stop_sequences || stop;
324
- return awsModel.stop_sequences_param_name && stopSequencesValue ? {
325
- [awsModel.stop_sequences_param_name]: Array.isArray(stopSequencesValue) ? stopSequencesValue : [stopSequencesValue]
326
- } : {};
327
- })(),
328
- ...awsModel.special_request_schema
329
- };
330
-
331
- // Create a Bedrock Runtime client in the AWS Region of your choice
332
- const client = new BedrockRuntimeClient({
333
- region: region,
334
- credentials: {
335
- accessKeyId: accessKeyId,
336
- secretAccessKey: secretAccessKey,
337
- },
338
- });
339
-
340
- if (logging) {
341
- console.log("\nFinal request:", JSON.stringify(request, null, 2));
365
+ } else {
366
+ return {
367
+ prompt: typeof prompt === 'string' ? prompt : {
368
+ messages: prompt.map(msg => ({
369
+ role: msg.role,
370
+ content: Array.isArray(msg.content) ?
371
+ msg.content.map(item =>
372
+ item.type === 'text' ? item.text : item
373
+ ).join('\n') :
374
+ msg.content
375
+ }))
376
+ },
377
+ // Optional inference parameters:
378
+ [awsModel.max_tokens_param_name]: max_gen_tokens,
379
+ temperature: temperature,
380
+ top_p: top_p,
381
+ ...(() => {
382
+ const stopSequencesValue = stop_sequences || stop;
383
+ return awsModel.stop_sequences_param_name && stopSequencesValue ? {
384
+ [awsModel.stop_sequences_param_name]: Array.isArray(stopSequencesValue) ? stopSequencesValue : [stopSequencesValue]
385
+ } : {};
386
+ })(),
387
+ ...awsModel.special_request_schema
388
+ };
342
389
  }
390
+ }
343
391
 
344
- if (stream) {
392
+ // Execute Invoke API call (streaming and non-streaming)
393
+ async function* executeInvokeAPI(client, request, awsModelId, shouldStream, awsModel, include_thinking_data) {
394
+ if (shouldStream) {
345
395
  const responseStream = await client.send(
346
396
  new InvokeModelWithResponseStreamCommand({
347
397
  contentType: "application/json",
@@ -361,6 +411,8 @@ export async function* bedrockWrapper(awsCreds, openaiChatCompletionsCreateObjec
361
411
  is_thinking = false;
362
412
  result = `</think>\n\n${result}`;
363
413
  }
414
+ // Process reasoning tags for GPT-OSS models
415
+ result = processReasoningTags(result, awsModel);
364
416
  yield result;
365
417
  } else {
366
418
  if (include_thinking_data && awsModel.thinking_response_chunk_element) {
@@ -417,6 +469,9 @@ export async function* bedrockWrapper(awsCreds, openaiChatCompletionsCreateObjec
417
469
  text_result = "";
418
470
  }
419
471
 
472
+ // Process reasoning tags for GPT-OSS models
473
+ text_result = processReasoningTags(text_result, awsModel);
474
+
420
475
  let result = thinking_result ? `<think>${thinking_result}</think>\n\n${text_result}` : text_result;
421
476
 
422
477
  // Ensure final result is a string, in case thinking_result was also empty
@@ -424,7 +479,245 @@ export async function* bedrockWrapper(awsCreds, openaiChatCompletionsCreateObjec
424
479
  result = "";
425
480
  }
426
481
  yield result;
427
- }
482
+ }
483
+ }
484
+
485
+ export async function* bedrockWrapper(awsCreds, openaiChatCompletionsCreateObject, { logging = false, useConverseAPI = false } = {} ) {
486
+ const { region, accessKeyId, secretAccessKey } = awsCreds;
487
+ let { messages, model, max_tokens, stream, temperature, top_p, include_thinking_data, stop, stop_sequences } = openaiChatCompletionsCreateObject;
488
+
489
+ let {awsModelId, awsModel} = findAwsModelWithId(model);
490
+
491
+ // Create a Bedrock Runtime client
492
+ const client = new BedrockRuntimeClient({
493
+ region: region,
494
+ credentials: {
495
+ accessKeyId: accessKeyId,
496
+ secretAccessKey: secretAccessKey,
497
+ },
498
+ });
499
+
500
+ // Calculate max tokens (shared between both APIs)
501
+ let max_gen_tokens = max_tokens <= awsModel.max_supported_response_tokens ? max_tokens : awsModel.max_supported_response_tokens;
502
+
503
+ // Check if model supports streaming
504
+ const modelSupportsStreaming = awsModel.streaming_supported !== false;
505
+ const shouldStream = stream && modelSupportsStreaming;
506
+
507
+ // ============================
508
+ // CONVERSE API PATH (SIMPLIFIED)
509
+ // ============================
510
+ if (useConverseAPI) {
511
+ // Convert messages to Converse API format (no model-specific complexity)
512
+ const { messages: converseMessages, system: systemPrompts } = await convertToConverseFormat(messages);
513
+
514
+ // Build inference configuration (handle thinking mode for Claude models)
515
+ const inferenceConfig = {
516
+ maxTokens: max_gen_tokens,
517
+ temperature: temperature,
518
+ ...(top_p !== undefined && { topP: top_p })
519
+ };
520
+
521
+ // Handle thinking mode for Claude models
522
+ let budget_tokens;
523
+ if (awsModel.special_request_schema?.thinking?.type === "enabled") {
524
+ // Apply thinking mode constraints for Converse API
525
+ inferenceConfig.temperature = 1; // temperature must be 1 for thinking
526
+ delete inferenceConfig.topP; // top_p must be unset for thinking
527
+
528
+ // Calculate thinking budget configuration
529
+ budget_tokens = awsModel.special_request_schema?.thinking?.budget_tokens;
530
+ if (budget_tokens > (max_gen_tokens * 0.8)) {
531
+ budget_tokens = Math.floor(max_gen_tokens * 0.8);
532
+ }
533
+ if (budget_tokens < 1024) {
534
+ budget_tokens = 1024;
535
+ }
536
+
537
+ // Ensure max tokens is sufficient for thinking
538
+ if (inferenceConfig.maxTokens <= budget_tokens) {
539
+ inferenceConfig.maxTokens = Math.floor(budget_tokens * 1.2);
540
+ }
541
+ }
542
+
543
+ // Add stop sequences if provided (unified format)
544
+ const stopSequencesValue = stop_sequences || stop;
545
+ if (stopSequencesValue) {
546
+ inferenceConfig.stopSequences = Array.isArray(stopSequencesValue) ?
547
+ stopSequencesValue : [stopSequencesValue];
548
+ }
549
+
550
+ // Build the Converse API request (simple, unified format)
551
+ const converseRequest = {
552
+ modelId: awsModelId,
553
+ messages: converseMessages,
554
+ inferenceConfig: inferenceConfig
555
+ };
556
+
557
+ // Add system prompts if any
558
+ if (systemPrompts.length > 0) {
559
+ converseRequest.system = systemPrompts;
560
+ }
561
+
562
+ // Add thinking configuration for Claude models
563
+ if (awsModel.special_request_schema?.thinking?.type === "enabled") {
564
+ converseRequest.additionalModelRequestFields = {
565
+ thinking: {
566
+ type: "enabled",
567
+ budget_tokens: budget_tokens
568
+ }
569
+ };
570
+
571
+ if (awsModel.special_request_schema?.anthropic_beta) {
572
+ converseRequest.additionalModelRequestFields.anthropic_beta = awsModel.special_request_schema.anthropic_beta;
573
+ }
574
+ }
575
+
576
+ if (logging) {
577
+ console.log("\nConverse API request:", JSON.stringify(converseRequest, null, 2));
578
+ }
579
+
580
+ if (shouldStream) {
581
+ // Use ConverseStream for streaming responses
582
+ const responseStream = await client.send(new ConverseStreamCommand(converseRequest));
583
+
584
+ let is_thinking = false;
585
+ let should_think = include_thinking_data && awsModel.special_request_schema?.thinking?.type === "enabled";
586
+
587
+ for await (const event of responseStream.stream) {
588
+ if (event.contentBlockDelta) {
589
+ const text = event.contentBlockDelta.delta?.text;
590
+ const thinking = event.contentBlockDelta.delta?.thinking;
591
+ const reasoningContent = event.contentBlockDelta.delta?.reasoningContent;
592
+
593
+ // Handle Claude thinking data (streaming) - check both reasoningContent and thinking
594
+ const thinkingText = reasoningContent?.reasoningText?.text || thinking;
595
+ if (should_think && thinkingText) {
596
+ if (!is_thinking) {
597
+ is_thinking = true;
598
+ yield `<think>${thinkingText}`;
599
+ } else {
600
+ yield thinkingText;
601
+ }
602
+ }
603
+ // Handle regular text content
604
+ else if (text) {
605
+ // End thinking mode if we were in it
606
+ if (is_thinking) {
607
+ is_thinking = false;
608
+ yield `</think>\n\n${text}`;
609
+ } else {
610
+ // Process reasoning tags for GPT-OSS models only
611
+ const processedText = processReasoningTags(text, awsModel);
612
+ if (processedText) {
613
+ yield processedText;
614
+ }
615
+ }
616
+ }
617
+ }
618
+ }
619
+
620
+ // Close thinking tag if still open
621
+ if (is_thinking) {
622
+ yield "</think>";
623
+ }
624
+ } else {
625
+ // Use Converse for non-streaming responses
626
+ const response = await client.send(new ConverseCommand(converseRequest));
627
+
628
+ if (logging) {
629
+ console.log("\nConverse API response:", JSON.stringify(response, null, 2));
630
+ }
631
+
632
+ // Extract text and thinking from response (handle Claude thinking)
633
+ if (response.output && response.output.message && response.output.message.content) {
634
+ let thinking_result = "";
635
+ let text_result = "";
636
+
637
+ for (const contentBlock of response.output.message.content) {
638
+ // Extract thinking data for Claude models (from reasoningContent)
639
+ if (include_thinking_data && contentBlock.reasoningContent &&
640
+ awsModel.special_request_schema?.thinking?.type === "enabled") {
641
+ const reasoningText = contentBlock.reasoningContent.reasoningText?.text;
642
+ if (reasoningText) {
643
+ thinking_result += reasoningText;
644
+ }
645
+ }
646
+
647
+ // Also check for legacy thinking field format
648
+ if (include_thinking_data && contentBlock.thinking &&
649
+ awsModel.special_request_schema?.thinking?.type === "enabled") {
650
+ thinking_result += contentBlock.thinking;
651
+ }
652
+
653
+ // Extract regular text content
654
+ if (contentBlock.text) {
655
+ text_result += contentBlock.text;
656
+ }
657
+ }
658
+
659
+ // Process reasoning tags for GPT-OSS models
660
+ text_result = processReasoningTags(text_result, awsModel);
661
+
662
+ // Combine thinking and text for Claude models
663
+ let result = thinking_result ? `<think>${thinking_result}</think>\n\n${text_result}` : text_result;
664
+
665
+ if (result) {
666
+ yield result;
667
+ }
668
+ }
669
+ }
670
+ return; // Exit early when using Converse API
671
+ }
672
+
673
+ // ============================
674
+ // INVOKE API PATH (COMPLEX, MODEL-SPECIFIC)
675
+ // ============================
676
+
677
+ // Process messages for Invoke API (complex, model-specific)
678
+ const { message_cleaned, system_message } = await processMessagesForInvoke(messages, awsModel);
679
+
680
+ // Build prompt for Invoke API (complex, model-specific)
681
+ const prompt = buildInvokePrompt(message_cleaned, awsModel);
682
+
683
+ if (logging) {
684
+ console.log("\nFinal formatted prompt:", prompt);
685
+ }
686
+
687
+ // Handle thinking mode adjustments (Invoke API specific)
688
+ if (awsModel.special_request_schema?.thinking?.type === "enabled") {
689
+ // temperature may only be set to 1 when thinking is enabled
690
+ temperature = 1;
691
+ // top_p must be unset when thinking is enabled
692
+ top_p = undefined;
693
+ // budget_tokens can not be greater than 80% of max_gen_tokens
694
+ let budget_tokens = awsModel.special_request_schema?.thinking?.budget_tokens;
695
+ if (budget_tokens > (max_gen_tokens * 0.8)) {
696
+ budget_tokens = Math.floor(max_gen_tokens * 0.8);
697
+ }
698
+ if (budget_tokens < 1024) {
699
+ budget_tokens = 1024;
700
+ }
701
+ // if awsModel.special_request_schema?.thinking?.budget_tokens, set it to budget_tokens
702
+ if (awsModel.special_request_schema?.thinking?.budget_tokens) {
703
+ awsModel.special_request_schema.thinking.budget_tokens = budget_tokens;
704
+ // max_gen_tokens has to be greater than budget_tokens
705
+ if (max_gen_tokens <= budget_tokens) {
706
+ // make max_gen_tokens 20% greater than budget_tokens
707
+ max_gen_tokens = Math.floor(budget_tokens * 1.2);
708
+ }
709
+ }
710
+ }
711
+
712
+ // Build request for Invoke API (complex, model-specific)
713
+ const request = buildInvokeRequest(prompt, awsModel, max_gen_tokens, temperature, top_p, stop_sequences, stop, system_message);
714
+
715
+ if (logging) {
716
+ console.log("\nFinal request:", JSON.stringify(request, null, 2));
717
+ }
718
+
719
+ // Execute Invoke API call (complex, model-specific response parsing)
720
+ yield* executeInvokeAPI(client, request, awsModelId, shouldStream, awsModel, include_thinking_data);
428
721
  }
429
722
 
430
723
 
@@ -0,0 +1,116 @@
1
+ // ================================================================================
2
+ // == Example: Using the AWS Bedrock Converse API with bedrock-wrapper ==
3
+ // ================================================================================
4
+
5
+ import dotenv from 'dotenv';
6
+ import { bedrockWrapper } from "./bedrock-wrapper.js";
7
+
8
+ dotenv.config();
9
+
10
+ const AWS_REGION = process.env.AWS_REGION;
11
+ const AWS_ACCESS_KEY_ID = process.env.AWS_ACCESS_KEY_ID;
12
+ const AWS_SECRET_ACCESS_KEY = process.env.AWS_SECRET_ACCESS_KEY;
13
+
14
+ async function main() {
15
+ const awsCreds = {
16
+ region: AWS_REGION,
17
+ accessKeyId: AWS_ACCESS_KEY_ID,
18
+ secretAccessKey: AWS_SECRET_ACCESS_KEY,
19
+ };
20
+
21
+ // Example conversation with system prompt
22
+ const messages = [
23
+ {
24
+ role: "user",
25
+ content: "What are the benefits of using the Converse API over the Invoke API?"
26
+ }
27
+ ];
28
+
29
+ const openaiChatCompletionsCreateObject = {
30
+ messages,
31
+ model: "Claude-3-Haiku", // Works with any supported model
32
+ max_tokens: 500,
33
+ stream: true, // Can be true or false
34
+ temperature: 0.7,
35
+ top_p: 0.9,
36
+ stop: ["END", "STOP"] // Optional stop sequences
37
+ };
38
+
39
+ console.log("=".repeat(60));
40
+ console.log("Example: AWS Bedrock Converse API");
41
+ console.log("=".repeat(60));
42
+ console.log("\nUsing model:", openaiChatCompletionsCreateObject.model);
43
+ console.log("Streaming:", openaiChatCompletionsCreateObject.stream);
44
+ console.log("\nResponse:");
45
+ console.log("-".repeat(40));
46
+
47
+ let completeResponse = "";
48
+
49
+ try {
50
+ // Use the Converse API by setting useConverseAPI: true
51
+ for await (const chunk of bedrockWrapper(awsCreds, openaiChatCompletionsCreateObject, {
52
+ useConverseAPI: true, // ← Enable Converse API
53
+ logging: false // Set to true to see API requests/responses
54
+ })) {
55
+ completeResponse += chunk;
56
+ process.stdout.write(chunk); // Display streamed output
57
+ }
58
+
59
+ console.log("\n" + "-".repeat(40));
60
+ console.log("\n✅ Successfully used the Converse API!");
61
+
62
+ // Uncomment to see the complete response
63
+ // console.log("\nComplete Response:", completeResponse);
64
+
65
+ } catch (error) {
66
+ console.error("\n❌ Error:", error.message);
67
+ }
68
+
69
+ // Example 2: Comparing Invoke API vs Converse API
70
+ console.log("\n" + "=".repeat(60));
71
+ console.log("Comparing Invoke API vs Converse API");
72
+ console.log("=".repeat(60));
73
+
74
+ const simpleMessage = [
75
+ { role: "user", content: "What is 2+2? Answer in one word." }
76
+ ];
77
+
78
+ const compareRequest = {
79
+ messages: simpleMessage,
80
+ model: "Claude-3-Haiku",
81
+ max_tokens: 50,
82
+ stream: false,
83
+ temperature: 0.1,
84
+ top_p: 0.9
85
+ };
86
+
87
+ // Test with Invoke API
88
+ console.log("\n1. Using Invoke API (default):");
89
+ let invokeResponse = "";
90
+ const invokeStart = Date.now();
91
+ const invokeGen = await bedrockWrapper(awsCreds, compareRequest, { useConverseAPI: false });
92
+ for await (const data of invokeGen) {
93
+ invokeResponse += data;
94
+ }
95
+ const invokeTime = Date.now() - invokeStart;
96
+ console.log(` Response: ${invokeResponse}`);
97
+ console.log(` Time: ${invokeTime}ms`);
98
+
99
+ // Test with Converse API
100
+ console.log("\n2. Using Converse API:");
101
+ let converseResponse = "";
102
+ const converseStart = Date.now();
103
+ const converseGen = await bedrockWrapper(awsCreds, compareRequest, { useConverseAPI: true });
104
+ for await (const data of converseGen) {
105
+ converseResponse += data;
106
+ }
107
+ const converseTime = Date.now() - converseStart;
108
+ console.log(` Response: ${converseResponse}`);
109
+ console.log(` Time: ${converseTime}ms`);
110
+
111
+ console.log("\n" + "=".repeat(60));
112
+ console.log("✨ Example complete!");
113
+ console.log("=".repeat(60));
114
+ }
115
+
116
+ main().catch(console.error);