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.
@@ -0,0 +1,86 @@
1
+ [
2
+ {
3
+ "session_id": "e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3",
4
+ "transcript_path": "C:\\Users\\Justin.Parker\\.claude\\projects\\C--git-bedrock-wrapper\\e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3.jsonl",
5
+ "cwd": "C:\\git\\bedrock-wrapper",
6
+ "hook_event_name": "Stop",
7
+ "stop_hook_active": false
8
+ },
9
+ {
10
+ "session_id": "e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3",
11
+ "transcript_path": "C:\\Users\\Justin.Parker\\.claude\\projects\\C--git-bedrock-wrapper\\e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3.jsonl",
12
+ "cwd": "C:\\git\\bedrock-wrapper",
13
+ "hook_event_name": "Stop",
14
+ "stop_hook_active": false
15
+ },
16
+ {
17
+ "session_id": "e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3",
18
+ "transcript_path": "C:\\Users\\Justin.Parker\\.claude\\projects\\C--git-bedrock-wrapper\\e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3.jsonl",
19
+ "cwd": "C:\\git\\bedrock-wrapper",
20
+ "hook_event_name": "Stop",
21
+ "stop_hook_active": false
22
+ },
23
+ {
24
+ "session_id": "e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3",
25
+ "transcript_path": "C:\\Users\\Justin.Parker\\.claude\\projects\\C--git-bedrock-wrapper\\e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3.jsonl",
26
+ "cwd": "C:\\git\\bedrock-wrapper",
27
+ "hook_event_name": "Stop",
28
+ "stop_hook_active": false
29
+ },
30
+ {
31
+ "session_id": "e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3",
32
+ "transcript_path": "C:\\Users\\Justin.Parker\\.claude\\projects\\C--git-bedrock-wrapper\\e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3.jsonl",
33
+ "cwd": "C:\\git\\bedrock-wrapper",
34
+ "hook_event_name": "Stop",
35
+ "stop_hook_active": false
36
+ },
37
+ {
38
+ "session_id": "e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3",
39
+ "transcript_path": "C:\\Users\\Justin.Parker\\.claude\\projects\\C--git-bedrock-wrapper\\e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3.jsonl",
40
+ "cwd": "C:\\git\\bedrock-wrapper",
41
+ "hook_event_name": "Stop",
42
+ "stop_hook_active": false
43
+ },
44
+ {
45
+ "session_id": "e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3",
46
+ "transcript_path": "C:\\Users\\Justin.Parker\\.claude\\projects\\C--git-bedrock-wrapper\\e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3.jsonl",
47
+ "cwd": "C:\\git\\bedrock-wrapper",
48
+ "hook_event_name": "Stop",
49
+ "stop_hook_active": false
50
+ },
51
+ {
52
+ "session_id": "e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3",
53
+ "transcript_path": "C:\\Users\\Justin.Parker\\.claude\\projects\\C--git-bedrock-wrapper\\e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3.jsonl",
54
+ "cwd": "C:\\git\\bedrock-wrapper",
55
+ "hook_event_name": "Stop",
56
+ "stop_hook_active": false
57
+ },
58
+ {
59
+ "session_id": "e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3",
60
+ "transcript_path": "C:\\Users\\Justin.Parker\\.claude\\projects\\C--git-bedrock-wrapper\\e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3.jsonl",
61
+ "cwd": "C:\\git\\bedrock-wrapper",
62
+ "hook_event_name": "Stop",
63
+ "stop_hook_active": false
64
+ },
65
+ {
66
+ "session_id": "e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3",
67
+ "transcript_path": "C:\\Users\\Justin.Parker\\.claude\\projects\\C--git-bedrock-wrapper\\e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3.jsonl",
68
+ "cwd": "C:\\git\\bedrock-wrapper",
69
+ "hook_event_name": "Stop",
70
+ "stop_hook_active": false
71
+ },
72
+ {
73
+ "session_id": "e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3",
74
+ "transcript_path": "C:\\Users\\Justin.Parker\\.claude\\projects\\C--git-bedrock-wrapper\\e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3.jsonl",
75
+ "cwd": "C:\\git\\bedrock-wrapper",
76
+ "hook_event_name": "Stop",
77
+ "stop_hook_active": false
78
+ },
79
+ {
80
+ "session_id": "e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3",
81
+ "transcript_path": "C:\\Users\\Justin.Parker\\.claude\\projects\\C--git-bedrock-wrapper\\e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3.jsonl",
82
+ "cwd": "C:\\git\\bedrock-wrapper",
83
+ "hook_event_name": "Stop",
84
+ "stop_hook_active": false
85
+ }
86
+ ]
@@ -0,0 +1,86 @@
1
+ [
2
+ {
3
+ "session_id": "e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3",
4
+ "transcript_path": "C:\\Users\\Justin.Parker\\.claude\\projects\\C--git-bedrock-wrapper\\e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3.jsonl",
5
+ "cwd": "C:\\git\\bedrock-wrapper",
6
+ "hook_event_name": "UserPromptSubmit",
7
+ "prompt": "The project converts prompts to the correct format required by AWS Bedrock for the Invoke API. I want to add an optional 3rd parameter you pass when calling `bedrockWrapper` that would use the `converse` API instead of invoke (call the optional parameter `useConverseAPI` default to false). Tests will need to be updated as well. Use the Context7 MCP for aws bedrock documentation, as well as search the AWS website on how to use the converse api. When developing this use a Test Driven Development approach. my creds are already setup in my @.env file for you to run."
8
+ },
9
+ {
10
+ "session_id": "e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3",
11
+ "transcript_path": "C:\\Users\\Justin.Parker\\.claude\\projects\\C--git-bedrock-wrapper\\e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3.jsonl",
12
+ "cwd": "C:\\git\\bedrock-wrapper",
13
+ "hook_event_name": "UserPromptSubmit",
14
+ "prompt": "great, now think ULTRAHARD about @bedrock-wrapper.js and the `bedrockWrapper` function. make sure there isn't any extra complexities only required for invoke, as converse api should be way simplified. If needed break out the complexities to external function calls in this file. I fear we have retained unneeded logic only required for invoke api calls (this involves extra config from our models config that may only be needed for invoke vs converse). be very careful not to break functionality."
15
+ },
16
+ {
17
+ "session_id": "e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3",
18
+ "transcript_path": "C:\\Users\\Justin.Parker\\.claude\\projects\\C--git-bedrock-wrapper\\e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3.jsonl",
19
+ "cwd": "C:\\git\\bedrock-wrapper",
20
+ "hook_event_name": "UserPromptSubmit",
21
+ "prompt": "great, now update our @interactive-example.js with a question that asks if we want to use the Converse API"
22
+ },
23
+ {
24
+ "session_id": "e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3",
25
+ "transcript_path": "C:\\Users\\Justin.Parker\\.claude\\projects\\C--git-bedrock-wrapper\\e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3.jsonl",
26
+ "cwd": "C:\\git\\bedrock-wrapper",
27
+ "hook_event_name": "UserPromptSubmit",
28
+ "prompt": "please update @test-vision.js, @test-models.js and @test-stop-sequences.js to include both converse api and invoke api"
29
+ },
30
+ {
31
+ "session_id": "e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3",
32
+ "transcript_path": "C:\\Users\\Justin.Parker\\.claude\\projects\\C--git-bedrock-wrapper\\e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3.jsonl",
33
+ "cwd": "C:\\git\\bedrock-wrapper",
34
+ "hook_event_name": "UserPromptSubmit",
35
+ "prompt": "I ran the @test-models.js with the --both flag to test invoke and converse api. Everything seemed to work well except for the claude thining calls the think tag isn't being returned when using the converse api, but it is with invoke. please investigate."
36
+ },
37
+ {
38
+ "session_id": "e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3",
39
+ "transcript_path": "C:\\Users\\Justin.Parker\\.claude\\projects\\C--git-bedrock-wrapper\\e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3.jsonl",
40
+ "cwd": "C:\\git\\bedrock-wrapper",
41
+ "hook_event_name": "UserPromptSubmit",
42
+ "prompt": "hmmm, I'm not sure why we have @thinking-patch.js , we should make sure @bedrock-wrapper.js converse api supports all thinking models output just like invoke seems to."
43
+ },
44
+ {
45
+ "session_id": "e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3",
46
+ "transcript_path": "C:\\Users\\Justin.Parker\\.claude\\projects\\C--git-bedrock-wrapper\\e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3.jsonl",
47
+ "cwd": "C:\\git\\bedrock-wrapper",
48
+ "hook_event_name": "UserPromptSubmit",
49
+ "prompt": "hmm, after running `npm run test` I still don't see the output thinking tokens for the Claude models when using the Converse API "
50
+ },
51
+ {
52
+ "session_id": "e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3",
53
+ "transcript_path": "C:\\Users\\Justin.Parker\\.claude\\projects\\C--git-bedrock-wrapper\\e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3.jsonl",
54
+ "cwd": "C:\\git\\bedrock-wrapper",
55
+ "hook_event_name": "UserPromptSubmit",
56
+ "prompt": "add an entry to the top of the @CHANGELOG.md "
57
+ },
58
+ {
59
+ "session_id": "e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3",
60
+ "transcript_path": "C:\\Users\\Justin.Parker\\.claude\\projects\\C--git-bedrock-wrapper\\e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3.jsonl",
61
+ "cwd": "C:\\git\\bedrock-wrapper",
62
+ "hook_event_name": "UserPromptSubmit",
63
+ "prompt": "in @bedrock-wrapper.js \n\n```\n if (content.length > 0 && content.some(item => item.text && item.text.trim() !== '')) {\n converseMessages.push({\n role: msg.role,\n content: content\n });\n }\n```\n\nThe current logic for building Converse API messages incorrectly filters out messages that contain only images. The condition content.some(item => item.text && item.text.trim() !== '') requires at least one text part with non-whitespace content, but the Converse API allows user messages to contain only images. This will cause multimodal requests with image-only messages to fail or be ignored. The check should be simplified to ensure any message with content (text or image) is included.\n\n```\n // Only add messages with actual content (Converse API doesn't allow empty content)\n if (content.length > 0) {\n converseMessages.push({\n role: msg.role,\n content: content\n });\n }\n```"
64
+ },
65
+ {
66
+ "session_id": "e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3",
67
+ "transcript_path": "C:\\Users\\Justin.Parker\\.claude\\projects\\C--git-bedrock-wrapper\\e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3.jsonl",
68
+ "cwd": "C:\\git\\bedrock-wrapper",
69
+ "hook_event_name": "UserPromptSubmit",
70
+ "prompt": "in @bedrock-wrapper.js \n\n```\n // Add thinking configuration for Claude models\n if (awsModel.special_request_schema?.thinking?.type === \"enabled\") {\n let budget_tokens = awsModel.special_request_schema?.thinking?.budget_tokens;\n if (budget_tokens > (inferenceConfig.maxTokens * 0.8)) {\n budget_tokens = Math.floor(inferenceConfig.maxTokens * 0.8);\n }\n if (budget_tokens < 1024) {\n budget_tokens = 1024;\n }\n\n converseRequest.additionalModelRequestFields = {\n thinking: {\n type: \"enabled\",\n budget_tokens: budget_tokens\n }\n };\n\n if (awsModel.special_request_schema?.anthropic_beta) {\n converseRequest.additionalModelRequestFields.anthropic_beta = awsModel.special_request_schema.anthropic_beta;\n }\n }\n```\n\nThis block duplicates the budget_tokens calculation logic from lines 528-534. This is redundant and can be simplified by calculating the value once and using it to set both inferenceConfig and additionalModelRequestFields in the same logic block.\n\nConsider refactoring the if block at line 522 to handle all thinking-related configuration, and then remove this redundant block."
71
+ },
72
+ {
73
+ "session_id": "e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3",
74
+ "transcript_path": "C:\\Users\\Justin.Parker\\.claude\\projects\\C--git-bedrock-wrapper\\e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3.jsonl",
75
+ "cwd": "C:\\git\\bedrock-wrapper",
76
+ "hook_event_name": "UserPromptSubmit",
77
+ "prompt": "in @test-converse-api.js \n\n```\nComment on lines +177 to +178\n---\n const converseStoppedCorrectly = !converseResponse.includes(\"6, 7\");\n const invokeStoppedCorrectly = !invokeResponse.includes(\"6, 7\");\n```\n\nThe check for whether the stop sequence worked correctly is not quite right. The stop sequence is [\"6\"], which means generation should stop before 6 is produced. The current check !converseResponse.includes(\"6, 7\") would pass even if the response is \"1, 2, 3, 4, 5, 6\", which is incorrect. The check should ensure that the stop sequence itself (\"6\") is not present in the final output."
78
+ },
79
+ {
80
+ "session_id": "e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3",
81
+ "transcript_path": "C:\\Users\\Justin.Parker\\.claude\\projects\\C--git-bedrock-wrapper\\e4cf59ef-9d22-45bf-9c6c-53e3cb9efda3.jsonl",
82
+ "cwd": "C:\\git\\bedrock-wrapper",
83
+ "hook_event_name": "UserPromptSubmit",
84
+ "prompt": "/init "
85
+ }
86
+ ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bedrock-wrapper",
3
- "version": "2.4.4",
3
+ "version": "2.5.0",
4
4
  "description": "🪨 Bedrock Wrapper is an npm package that simplifies the integration of existing OpenAI-compatible API objects with AWS Bedrock's serverless inference LLMs.",
5
5
  "homepage": "https://www.equilllabs.com/projects/bedrock-wrapper",
6
6
  "repository": {
@@ -13,9 +13,16 @@
13
13
  },
14
14
  "scripts": {
15
15
  "clean": "npx rimraf node_modules && npx rimraf package-lock.json && npm install",
16
- "test": "node test-models.js",
17
- "test-vision": "node test-vision.js",
18
- "test-stop": "node test-stop-sequences.js",
16
+ "test": "node test-models.js --both",
17
+ "test:invoke": "node test-models.js",
18
+ "test:converse": "node test-models.js --converse",
19
+ "test-vision": "node test-vision.js --both",
20
+ "test-vision:invoke": "node test-vision.js",
21
+ "test-vision:converse": "node test-vision.js --converse",
22
+ "test-stop": "node test-stop-sequences.js --both",
23
+ "test-stop:invoke": "node test-stop-sequences.js",
24
+ "test-stop:converse": "node test-stop-sequences.js --converse",
25
+ "test-converse": "node test-converse-api.js",
19
26
  "interactive": "node interactive-example.js"
20
27
  },
21
28
  "main": "bedrock-wrapper.js",
@@ -33,7 +40,7 @@
33
40
  "author": "",
34
41
  "license": "ISC",
35
42
  "dependencies": {
36
- "@aws-sdk/client-bedrock-runtime": "^3.861.0",
43
+ "@aws-sdk/client-bedrock-runtime": "^3.864.0",
37
44
  "dotenv": "^17.2.1",
38
45
  "sharp": "^0.34.3"
39
46
  },
@@ -0,0 +1,347 @@
1
+ // ================================================================================
2
+ // == Test AWS Bedrock Converse API Integration ==
3
+ // ================================================================================
4
+
5
+ // ---------------------------------------------------------------------
6
+ // -- import environment variables from .env file or define them here --
7
+ // ---------------------------------------------------------------------
8
+ import dotenv from 'dotenv';
9
+ import fs from 'fs/promises';
10
+ import chalk from 'chalk';
11
+
12
+ dotenv.config();
13
+
14
+ const AWS_REGION = process.env.AWS_REGION;
15
+ const AWS_ACCESS_KEY_ID = process.env.AWS_ACCESS_KEY_ID;
16
+ const AWS_SECRET_ACCESS_KEY = process.env.AWS_SECRET_ACCESS_KEY;
17
+ const LLM_MAX_GEN_TOKENS = parseInt(process.env.LLM_MAX_GEN_TOKENS);
18
+ const LLM_TEMPERATURE = parseFloat(process.env.LLM_TEMPERATURE);
19
+ const LLM_TOP_P = parseFloat(process.env.LLM_TOP_P);
20
+
21
+ // --------------------------------------------
22
+ // -- import functions from bedrock-wrapper --
23
+ // --------------------------------------------
24
+ import {
25
+ bedrockWrapper,
26
+ listBedrockWrapperSupportedModels
27
+ } from "./bedrock-wrapper.js";
28
+
29
+ async function logOutput(message, type = 'info', writeToFile = true) {
30
+ if (writeToFile) {
31
+ // Log to file
32
+ await fs.appendFile('test-converse-api-output.txt', message + '\n');
33
+ }
34
+
35
+ // Log to console with colors
36
+ switch(type) {
37
+ case 'success':
38
+ console.log(chalk.green('✓ ' + message));
39
+ break;
40
+ case 'error':
41
+ console.log(chalk.red('✗ ' + message));
42
+ break;
43
+ case 'info':
44
+ console.log(chalk.blue('ℹ ' + message));
45
+ break;
46
+ case 'running':
47
+ console.log(chalk.yellow(message));
48
+ break;
49
+ case 'comparison':
50
+ console.log(chalk.cyan('↔ ' + message));
51
+ break;
52
+ }
53
+ }
54
+
55
+ async function testModelWithAPI(model, awsCreds, testMessage, isStreaming, useConverseAPI) {
56
+ const messages = [{ role: "user", content: testMessage }];
57
+ const openaiChatCompletionsCreateObject = {
58
+ messages,
59
+ model,
60
+ max_tokens: LLM_MAX_GEN_TOKENS,
61
+ stream: isStreaming,
62
+ temperature: LLM_TEMPERATURE,
63
+ top_p: LLM_TOP_P,
64
+ };
65
+
66
+ let completeResponse = "";
67
+ const apiName = useConverseAPI ? "Converse" : "Invoke";
68
+
69
+ try {
70
+ const startTime = Date.now();
71
+
72
+ if (isStreaming) {
73
+ for await (const chunk of bedrockWrapper(awsCreds, openaiChatCompletionsCreateObject, { logging: false, useConverseAPI })) {
74
+ completeResponse += chunk;
75
+ }
76
+ } else {
77
+ const response = await bedrockWrapper(awsCreds, openaiChatCompletionsCreateObject, { logging: false, useConverseAPI });
78
+ for await (const data of response) {
79
+ completeResponse += data;
80
+ }
81
+ }
82
+
83
+ const endTime = Date.now();
84
+ const duration = endTime - startTime;
85
+
86
+ // Check if response is empty or undefined
87
+ if (!completeResponse || completeResponse.trim() === '' || completeResponse.trim() === 'undefined') {
88
+ throw new Error('Empty or invalid response received');
89
+ }
90
+
91
+ return {
92
+ success: true,
93
+ response: completeResponse.trim(),
94
+ duration,
95
+ apiName
96
+ };
97
+ } catch (error) {
98
+ return {
99
+ success: false,
100
+ error: error.message,
101
+ apiName
102
+ };
103
+ }
104
+ }
105
+
106
+ async function compareAPIs(model, awsCreds, testMessage, isStreaming) {
107
+ await logOutput(`\nComparing ${isStreaming ? 'STREAMING' : 'NON-STREAMING'} responses for ${model}:`, 'comparison');
108
+
109
+ // Test with Invoke API (existing)
110
+ const invokeResult = await testModelWithAPI(model, awsCreds, testMessage, isStreaming, false);
111
+
112
+ // Test with Converse API (new)
113
+ const converseResult = await testModelWithAPI(model, awsCreds, testMessage, isStreaming, true);
114
+
115
+ // Log results
116
+ if (invokeResult.success && converseResult.success) {
117
+ await logOutput(` Invoke API (${invokeResult.duration}ms): "${invokeResult.response.substring(0, 100)}..."`, 'info');
118
+ await logOutput(` Converse API (${converseResult.duration}ms): "${converseResult.response.substring(0, 100)}..."`, 'info');
119
+
120
+ // Check if responses are similar (not necessarily identical due to model randomness)
121
+ const bothHaveContent = invokeResult.response.length > 0 && converseResult.response.length > 0;
122
+ if (bothHaveContent) {
123
+ await logOutput(` ✓ Both APIs returned valid responses`, 'success');
124
+ } else {
125
+ await logOutput(` ⚠ Response length mismatch`, 'error');
126
+ }
127
+ } else {
128
+ if (!invokeResult.success) {
129
+ await logOutput(` Invoke API failed: ${invokeResult.error}`, 'error');
130
+ }
131
+ if (!converseResult.success) {
132
+ await logOutput(` Converse API failed: ${converseResult.error}`, 'error');
133
+ }
134
+ }
135
+
136
+ return {
137
+ invoke: invokeResult,
138
+ converse: converseResult
139
+ };
140
+ }
141
+
142
+ async function testStopSequences(model, awsCreds) {
143
+ await logOutput(`\nTesting stop sequences for ${model}:`, 'info');
144
+
145
+ const testPrompt = "Count from 1 to 10, separated by commas: 1, 2, 3, 4, 5";
146
+ const messages = [{ role: "user", content: testPrompt }];
147
+
148
+ // Test with stop sequence
149
+ const openaiChatCompletionsCreateObject = {
150
+ messages,
151
+ model,
152
+ max_tokens: 100,
153
+ stream: false,
154
+ temperature: 0.1,
155
+ top_p: 0.9,
156
+ stop: ["6"] // Stop at "6"
157
+ };
158
+
159
+ try {
160
+ // Test with Converse API
161
+ let converseResponse = "";
162
+ const converseGen = await bedrockWrapper(awsCreds, openaiChatCompletionsCreateObject, { useConverseAPI: true });
163
+ for await (const data of converseGen) {
164
+ converseResponse += data;
165
+ }
166
+
167
+ // Test with Invoke API
168
+ let invokeResponse = "";
169
+ const invokeGen = await bedrockWrapper(awsCreds, openaiChatCompletionsCreateObject, { useConverseAPI: false });
170
+ for await (const data of invokeGen) {
171
+ invokeResponse += data;
172
+ }
173
+
174
+ await logOutput(` Converse API result: "${converseResponse.trim()}"`, 'info');
175
+ await logOutput(` Invoke API result: "${invokeResponse.trim()}"`, 'info');
176
+
177
+ // Stop sequence ["6"] should stop BEFORE outputting "6"
178
+ const converseStoppedCorrectly = !converseResponse.includes("6");
179
+ const invokeStoppedCorrectly = !invokeResponse.includes("6");
180
+
181
+ if (converseStoppedCorrectly && invokeStoppedCorrectly) {
182
+ await logOutput(` ✓ Both APIs correctly applied stop sequences`, 'success');
183
+ } else {
184
+ if (!converseStoppedCorrectly) {
185
+ await logOutput(` ⚠ Converse API did not stop correctly`, 'error');
186
+ }
187
+ if (!invokeStoppedCorrectly) {
188
+ await logOutput(` ⚠ Invoke API did not stop correctly`, 'error');
189
+ }
190
+ }
191
+
192
+ } catch (error) {
193
+ await logOutput(` Error testing stop sequences: ${error.message}`, 'error');
194
+ }
195
+ }
196
+
197
+ async function testSystemPrompt(model, awsCreds) {
198
+ await logOutput(`\nTesting system prompt handling for ${model}:`, 'info');
199
+
200
+ const messages = [
201
+ { role: "user", content: "You are a pirate. Always respond in pirate speak." },
202
+ { role: "user", content: "Hello, how are you?" }
203
+ ];
204
+
205
+ const openaiChatCompletionsCreateObject = {
206
+ messages,
207
+ model,
208
+ max_tokens: 100,
209
+ stream: false,
210
+ temperature: 0.7,
211
+ top_p: 0.9,
212
+ };
213
+
214
+ try {
215
+ // Test with Converse API
216
+ let converseResponse = "";
217
+ const converseGen = await bedrockWrapper(awsCreds, openaiChatCompletionsCreateObject, { useConverseAPI: true });
218
+ for await (const data of converseGen) {
219
+ converseResponse += data;
220
+ }
221
+
222
+ await logOutput(` Converse API response: "${converseResponse.substring(0, 100)}..."`, 'info');
223
+
224
+ // Check if response seems pirate-like (basic check)
225
+ const pirateWords = ['ahoy', 'matey', 'arr', 'ye', 'aye', 'sail', 'sea'];
226
+ const hasPirateSpeak = pirateWords.some(word => converseResponse.toLowerCase().includes(word));
227
+
228
+ if (hasPirateSpeak || converseResponse.toLowerCase().includes('pirate')) {
229
+ await logOutput(` ✓ System prompt was correctly applied`, 'success');
230
+ } else {
231
+ await logOutput(` ⚠ System prompt may not have been applied correctly`, 'error');
232
+ }
233
+
234
+ } catch (error) {
235
+ await logOutput(` Error testing system prompt: ${error.message}`, 'error');
236
+ }
237
+ }
238
+
239
+ async function main() {
240
+ const testMessage = "What is the capital of France? Answer in one short sentence.";
241
+
242
+ // Clear output file and add header
243
+ await fs.writeFile('test-converse-api-output.txt',
244
+ `AWS Bedrock Converse API Test Results\n` +
245
+ `Test started at: ${new Date().toISOString()}\n` +
246
+ `${'='.repeat(60)}\n\n`
247
+ );
248
+
249
+ const supportedModels = await listBedrockWrapperSupportedModels();
250
+ const availableModels = supportedModels.map(model => {
251
+ const fixedJson = model
252
+ .replace(/modelName": ([^,]+),/, 'modelName": "$1",')
253
+ .replace(/modelId": ([^}]+)}/, 'modelId": "$1"}');
254
+ return JSON.parse(fixedJson).modelName;
255
+ });
256
+
257
+ // Select representative models from each family for testing
258
+ const modelsToTest = [
259
+ "Claude-4-1-Opus",
260
+ "Claude-3-5-Sonnet-v2",
261
+ "Claude-3-Haiku",
262
+ "Nova-Pro",
263
+ "Nova-Lite",
264
+ "Nova-Micro",
265
+ "Llama-3-3-70b",
266
+ "Llama-3-2-90b",
267
+ "Mistral-7b",
268
+ "Mistral-Large-2"
269
+ ].filter(m => availableModels.includes(m));
270
+
271
+ console.clear();
272
+ await logOutput(`Starting Converse API tests with ${modelsToTest.length} representative models...`, 'info');
273
+ await logOutput(`Testing: Invoke API vs Converse API comparison\n`, 'info');
274
+
275
+ const awsCreds = {
276
+ region: AWS_REGION,
277
+ accessKeyId: AWS_ACCESS_KEY_ID,
278
+ secretAccessKey: AWS_SECRET_ACCESS_KEY,
279
+ };
280
+
281
+ const testResults = {
282
+ passed: 0,
283
+ failed: 0,
284
+ models: {}
285
+ };
286
+
287
+ for (const model of modelsToTest) {
288
+ await logOutput(`\n${'='.repeat(60)}`, 'info');
289
+ await logOutput(`Testing ${model}`, 'running');
290
+ await logOutput(`${'='.repeat(60)}`, 'info');
291
+
292
+ const modelResults = {
293
+ streaming: {},
294
+ nonStreaming: {},
295
+ systemPrompt: false,
296
+ stopSequences: false
297
+ };
298
+
299
+ // Test streaming
300
+ const streamResults = await compareAPIs(model, awsCreds, testMessage, true);
301
+ modelResults.streaming = streamResults;
302
+
303
+ // Test non-streaming
304
+ const nonStreamResults = await compareAPIs(model, awsCreds, testMessage, false);
305
+ modelResults.nonStreaming = nonStreamResults;
306
+
307
+ // Test system prompt handling (Converse API specific)
308
+ await testSystemPrompt(model, awsCreds);
309
+
310
+ // Test stop sequences (if supported by model)
311
+ if (!model.includes("Llama")) { // Llama models don't support stop sequences on Bedrock
312
+ await testStopSequences(model, awsCreds);
313
+ }
314
+
315
+ // Calculate success for this model
316
+ const allTestsPassed =
317
+ streamResults.invoke.success &&
318
+ streamResults.converse.success &&
319
+ nonStreamResults.invoke.success &&
320
+ nonStreamResults.converse.success;
321
+
322
+ if (allTestsPassed) {
323
+ testResults.passed++;
324
+ await logOutput(`\n✓ All tests passed for ${model}`, 'success');
325
+ } else {
326
+ testResults.failed++;
327
+ await logOutput(`\n✗ Some tests failed for ${model}`, 'error');
328
+ }
329
+
330
+ testResults.models[model] = modelResults;
331
+ }
332
+
333
+ // Summary
334
+ await logOutput(`\n${'='.repeat(60)}`, 'info');
335
+ await logOutput(`TEST SUMMARY`, 'info');
336
+ await logOutput(`${'='.repeat(60)}`, 'info');
337
+ await logOutput(`Models tested: ${modelsToTest.length}`, 'info');
338
+ await logOutput(`Passed: ${testResults.passed}`, 'success');
339
+ await logOutput(`Failed: ${testResults.failed}`, testResults.failed > 0 ? 'error' : 'info');
340
+
341
+ await logOutput('\nTesting complete! Check test-converse-api-output.txt for full results.', 'info', false);
342
+ }
343
+
344
+ main().catch(async (error) => {
345
+ await logOutput(`Fatal Error: ${error.message}`, 'error');
346
+ console.error(error);
347
+ });