@fleetagent/pi-ai 0.1.9 → 0.2.1

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 (148) hide show
  1. package/dist/cli.d.ts.map +1 -1
  2. package/dist/cli.js +29 -24
  3. package/dist/cli.js.map +1 -1
  4. package/dist/env-api-keys.d.ts.map +1 -1
  5. package/dist/env-api-keys.js +1 -1
  6. package/dist/env-api-keys.js.map +1 -1
  7. package/dist/image-models.generated.d.ts +180 -0
  8. package/dist/image-models.generated.d.ts.map +1 -1
  9. package/dist/image-models.generated.js +184 -4
  10. package/dist/image-models.generated.js.map +1 -1
  11. package/dist/index.d.ts +2 -0
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js.map +1 -1
  14. package/dist/models.d.ts +3 -3
  15. package/dist/models.d.ts.map +1 -1
  16. package/dist/models.generated.d.ts +5389 -2066
  17. package/dist/models.generated.d.ts.map +1 -1
  18. package/dist/models.generated.js +5402 -2301
  19. package/dist/models.generated.js.map +1 -1
  20. package/dist/models.js +60 -45
  21. package/dist/models.js.map +1 -1
  22. package/dist/oauth.d.ts +6 -1
  23. package/dist/oauth.d.ts.map +1 -1
  24. package/dist/oauth.js +4 -1
  25. package/dist/oauth.js.map +1 -1
  26. package/dist/providers/amazon-bedrock.d.ts +5 -4
  27. package/dist/providers/amazon-bedrock.d.ts.map +1 -1
  28. package/dist/providers/amazon-bedrock.js +303 -330
  29. package/dist/providers/amazon-bedrock.js.map +1 -1
  30. package/dist/providers/anthropic.d.ts +5 -4
  31. package/dist/providers/anthropic.d.ts.map +1 -1
  32. package/dist/providers/anthropic.js +492 -499
  33. package/dist/providers/anthropic.js.map +1 -1
  34. package/dist/providers/azure-openai-responses.d.ts +4 -2
  35. package/dist/providers/azure-openai-responses.d.ts.map +1 -1
  36. package/dist/providers/azure-openai-responses.js +52 -35
  37. package/dist/providers/azure-openai-responses.js.map +1 -1
  38. package/dist/providers/faux.d.ts +19 -22
  39. package/dist/providers/faux.d.ts.map +1 -1
  40. package/dist/providers/faux.js +106 -70
  41. package/dist/providers/faux.js.map +1 -1
  42. package/dist/providers/github-copilot-headers.d.ts +7 -4
  43. package/dist/providers/github-copilot-headers.d.ts.map +1 -1
  44. package/dist/providers/github-copilot-headers.js.map +1 -1
  45. package/dist/providers/google-shared.d.ts +24 -5
  46. package/dist/providers/google-shared.d.ts.map +1 -1
  47. package/dist/providers/google-shared.js +139 -140
  48. package/dist/providers/google-shared.js.map +1 -1
  49. package/dist/providers/google-vertex.d.ts +3 -7
  50. package/dist/providers/google-vertex.d.ts.map +1 -1
  51. package/dist/providers/google-vertex.js +216 -219
  52. package/dist/providers/google-vertex.js.map +1 -1
  53. package/dist/providers/google.d.ts +4 -7
  54. package/dist/providers/google.d.ts.map +1 -1
  55. package/dist/providers/google.js +214 -218
  56. package/dist/providers/google.js.map +1 -1
  57. package/dist/providers/images/openrouter.d.ts.map +1 -1
  58. package/dist/providers/images/openrouter.js +37 -27
  59. package/dist/providers/images/openrouter.js.map +1 -1
  60. package/dist/providers/mistral.d.ts +7 -6
  61. package/dist/providers/mistral.d.ts.map +1 -1
  62. package/dist/providers/mistral.js +287 -265
  63. package/dist/providers/mistral.js.map +1 -1
  64. package/dist/providers/openai-codex-responses.d.ts +8 -5
  65. package/dist/providers/openai-codex-responses.d.ts.map +1 -1
  66. package/dist/providers/openai-codex-responses.js +493 -391
  67. package/dist/providers/openai-codex-responses.js.map +1 -1
  68. package/dist/providers/openai-completions.d.ts +7 -10
  69. package/dist/providers/openai-completions.d.ts.map +1 -1
  70. package/dist/providers/openai-completions.js +670 -664
  71. package/dist/providers/openai-completions.js.map +1 -1
  72. package/dist/providers/openai-responses-shared.d.ts +5 -4
  73. package/dist/providers/openai-responses-shared.d.ts.map +1 -1
  74. package/dist/providers/openai-responses-shared.js +390 -360
  75. package/dist/providers/openai-responses-shared.js.map +1 -1
  76. package/dist/providers/openai-responses.d.ts +5 -5
  77. package/dist/providers/openai-responses.d.ts.map +1 -1
  78. package/dist/providers/openai-responses.js +134 -109
  79. package/dist/providers/openai-responses.js.map +1 -1
  80. package/dist/providers/simple-options.d.ts +4 -2
  81. package/dist/providers/simple-options.d.ts.map +1 -1
  82. package/dist/providers/simple-options.js.map +1 -1
  83. package/dist/providers/transform-messages.d.ts.map +1 -1
  84. package/dist/providers/transform-messages.js +112 -137
  85. package/dist/providers/transform-messages.js.map +1 -1
  86. package/dist/stream.d.ts +0 -1
  87. package/dist/stream.d.ts.map +1 -1
  88. package/dist/stream.js +0 -1
  89. package/dist/stream.js.map +1 -1
  90. package/dist/types.d.ts +91 -63
  91. package/dist/types.d.ts.map +1 -1
  92. package/dist/types.js.map +1 -1
  93. package/dist/utils/error-body.d.ts.map +1 -1
  94. package/dist/utils/error-body.js +15 -4
  95. package/dist/utils/error-body.js.map +1 -1
  96. package/dist/utils/estimate.d.ts +4 -0
  97. package/dist/utils/estimate.d.ts.map +1 -1
  98. package/dist/utils/estimate.js +3 -1
  99. package/dist/utils/estimate.js.map +1 -1
  100. package/dist/utils/headers.d.ts +3 -0
  101. package/dist/utils/headers.d.ts.map +1 -1
  102. package/dist/utils/headers.js +21 -0
  103. package/dist/utils/headers.js.map +1 -1
  104. package/dist/utils/json-parse.d.ts.map +1 -1
  105. package/dist/utils/json-parse.js +17 -19
  106. package/dist/utils/json-parse.js.map +1 -1
  107. package/dist/utils/oauth/anthropic.d.ts +4 -11
  108. package/dist/utils/oauth/anthropic.d.ts.map +1 -1
  109. package/dist/utils/oauth/anthropic.js +100 -117
  110. package/dist/utils/oauth/anthropic.js.map +1 -1
  111. package/dist/utils/oauth/device-code.d.ts.map +1 -1
  112. package/dist/utils/oauth/device-code.js +51 -38
  113. package/dist/utils/oauth/device-code.js.map +1 -1
  114. package/dist/utils/oauth/github-copilot.d.ts +6 -9
  115. package/dist/utils/oauth/github-copilot.d.ts.map +1 -1
  116. package/dist/utils/oauth/github-copilot.js +47 -42
  117. package/dist/utils/oauth/github-copilot.js.map +1 -1
  118. package/dist/utils/oauth/index.d.ts +8 -17
  119. package/dist/utils/oauth/index.d.ts.map +1 -1
  120. package/dist/utils/oauth/index.js +27 -23
  121. package/dist/utils/oauth/index.js.map +1 -1
  122. package/dist/utils/oauth/oauth-page.d.ts.map +1 -1
  123. package/dist/utils/oauth/oauth-page.js.map +1 -1
  124. package/dist/utils/oauth/openai-codex.d.ts +7 -12
  125. package/dist/utils/oauth/openai-codex.d.ts.map +1 -1
  126. package/dist/utils/oauth/openai-codex.js +72 -89
  127. package/dist/utils/oauth/openai-codex.js.map +1 -1
  128. package/dist/utils/oauth/pkce.d.ts +5 -4
  129. package/dist/utils/oauth/pkce.d.ts.map +1 -1
  130. package/dist/utils/oauth/pkce.js.map +1 -1
  131. package/dist/utils/oauth/types.d.ts +7 -2
  132. package/dist/utils/oauth/types.d.ts.map +1 -1
  133. package/dist/utils/oauth/types.js.map +1 -1
  134. package/dist/utils/provider-retry.d.ts +16 -0
  135. package/dist/utils/provider-retry.d.ts.map +1 -0
  136. package/dist/utils/provider-retry.js +94 -0
  137. package/dist/utils/provider-retry.js.map +1 -0
  138. package/dist/utils/retry.d.ts +14 -0
  139. package/dist/utils/retry.d.ts.map +1 -1
  140. package/dist/utils/retry.js +84 -0
  141. package/dist/utils/retry.js.map +1 -1
  142. package/dist/utils/typebox-helpers.d.ts +5 -4
  143. package/dist/utils/typebox-helpers.d.ts.map +1 -1
  144. package/dist/utils/typebox-helpers.js.map +1 -1
  145. package/dist/utils/validation.d.ts.map +1 -1
  146. package/dist/utils/validation.js +102 -99
  147. package/dist/utils/validation.js.map +1 -1
  148. package/package.json +2 -2
@@ -2,11 +2,142 @@ import { BedrockRuntimeClient, BedrockRuntimeServiceException, StopReason as Bed
2
2
  import { NodeHttpHandler } from "@smithy/node-http-handler";
3
3
  import { calculateCost } from "../models.js";
4
4
  import { AssistantMessageEventStream } from "../utils/event-stream.js";
5
+ import { providerHeadersToRecord } from "../utils/headers.js";
5
6
  import { parseStreamingJson } from "../utils/json-parse.js";
6
7
  import { createHttpProxyAgentsForTarget } from "../utils/node-http-proxy.js";
7
8
  import { sanitizeSurrogates } from "../utils/sanitize-unicode.js";
8
9
  import { adjustMaxTokensForThinking, buildBaseOptions, clampReasoning } from "./simple-options.js";
9
10
  import { transformMessages } from "./transform-messages.js";
11
+ function configureNodeBedrockRuntime(config, model, configuredRegion, endpointRegion, useExplicitEndpoint, hasConfiguredProfile) {
12
+ if (configuredRegion)
13
+ config.region = configuredRegion;
14
+ else if (endpointRegion && useExplicitEndpoint)
15
+ config.region = endpointRegion;
16
+ else if (!hasConfiguredProfile)
17
+ config.region = "us-east-1";
18
+ if (process.env.AWS_BEDROCK_SKIP_AUTH === "1") {
19
+ config.credentials = { accessKeyId: "dummy-access-key", secretAccessKey: "dummy-secret-key" };
20
+ }
21
+ const proxyAgents = createHttpProxyAgentsForTarget(model.baseUrl);
22
+ if (proxyAgents)
23
+ config.requestHandler = new NodeHttpHandler(proxyAgents);
24
+ else if (process.env.AWS_BEDROCK_FORCE_HTTP1 === "1")
25
+ config.requestHandler = new NodeHttpHandler();
26
+ }
27
+ function createBedrockRuntimeConfig(model, options) {
28
+ const config = { profile: options.profile };
29
+ const configuredRegion = getConfiguredBedrockRegion(options);
30
+ const hasConfiguredProfile = hasConfiguredBedrockProfile();
31
+ const endpointRegion = getStandardBedrockEndpointRegion(model.baseUrl);
32
+ const useExplicitEndpoint = shouldUseExplicitBedrockEndpoint(model.baseUrl, configuredRegion, hasConfiguredProfile);
33
+ if (useExplicitEndpoint)
34
+ config.endpoint = model.baseUrl;
35
+ if (typeof process !== "undefined" && (process.versions?.node || process.versions?.bun)) {
36
+ configureNodeBedrockRuntime(config, model, configuredRegion, endpointRegion, useExplicitEndpoint, hasConfiguredProfile);
37
+ }
38
+ else {
39
+ config.region =
40
+ configuredRegion || (endpointRegion && useExplicitEndpoint ? endpointRegion : undefined) || "us-east-1";
41
+ }
42
+ const bearerToken = options.bearerToken || process.env.AWS_BEARER_TOKEN_BEDROCK || undefined;
43
+ if (bearerToken !== undefined && process.env.AWS_BEDROCK_SKIP_AUTH !== "1") {
44
+ config.token = { token: bearerToken };
45
+ config.authSchemePreference = ["httpBearerAuth"];
46
+ }
47
+ return config;
48
+ }
49
+ function createBedrockCommandInput(model, context, options) {
50
+ const cacheRetention = resolveCacheRetention(options.cacheRetention);
51
+ const inferenceMaxTokens = options.maxTokens ?? (isAnthropicClaudeModel(model) ? model.maxTokens : undefined);
52
+ return {
53
+ modelId: model.id,
54
+ messages: convertMessages(context, model, cacheRetention),
55
+ system: buildSystemPrompt(context.systemPrompt, model, cacheRetention),
56
+ inferenceConfig: {
57
+ ...(inferenceMaxTokens !== undefined && { maxTokens: inferenceMaxTokens }),
58
+ ...(options.temperature !== undefined && { temperature: options.temperature }),
59
+ },
60
+ toolConfig: convertToolConfig(context.tools, options.toolChoice),
61
+ additionalModelRequestFields: buildAdditionalModelRequestFields(model, options),
62
+ ...(options.requestMetadata !== undefined && { requestMetadata: options.requestMetadata }),
63
+ };
64
+ }
65
+ function handleBedrockStreamEvent(item, state) {
66
+ if (item.messageStart) {
67
+ if (item.messageStart.role !== ConversationRole.ASSISTANT) {
68
+ throw new Error("Unexpected assistant message start but got user message start instead");
69
+ }
70
+ state.stream.push({ type: "start", partial: state.output });
71
+ return;
72
+ }
73
+ if (item.contentBlockStart) {
74
+ handleContentBlockStart(item.contentBlockStart, state.blocks, state.output, state.stream);
75
+ return;
76
+ }
77
+ if (item.contentBlockDelta) {
78
+ handleContentBlockDelta(item.contentBlockDelta, state);
79
+ return;
80
+ }
81
+ if (item.contentBlockStop) {
82
+ handleContentBlockStop(item.contentBlockStop, state.blocks, state.output, state.stream);
83
+ return;
84
+ }
85
+ if (item.messageStop) {
86
+ state.output.stopReason = mapStopReason(item.messageStop.stopReason);
87
+ return;
88
+ }
89
+ if (item.metadata) {
90
+ handleMetadata(item.metadata, state.model, state.output);
91
+ return;
92
+ }
93
+ const exception = item.internalServerException ??
94
+ item.modelStreamErrorException ??
95
+ item.validationException ??
96
+ item.throttlingException ??
97
+ item.serviceUnavailableException;
98
+ if (exception)
99
+ throw exception;
100
+ }
101
+ function validateBedrockStreamCompletion(output, signal) {
102
+ if (signal?.aborted)
103
+ throw new Error("Request was aborted");
104
+ if (output.stopReason === "pending")
105
+ throw new Error("Bedrock stream ended without a stop reason");
106
+ if (output.stopReason === "error" || output.stopReason === "aborted")
107
+ throw new Error("An unknown error occurred");
108
+ }
109
+ async function executeBedrockStream(context, options, state) {
110
+ const client = new BedrockRuntimeClient(createBedrockRuntimeConfig(state.model, options));
111
+ const customHeaders = providerHeadersToRecord(options.headers);
112
+ if (customHeaders)
113
+ addCustomHeadersMiddleware(client, customHeaders);
114
+ let commandInput = createBedrockCommandInput(state.model, context, options);
115
+ const nextCommandInput = await options.onPayload?.(commandInput, state.model);
116
+ if (nextCommandInput !== undefined)
117
+ commandInput = nextCommandInput;
118
+ const response = await client.send(new ConverseStreamCommand(commandInput), { abortSignal: options.signal });
119
+ if (response.$metadata.httpStatusCode !== undefined) {
120
+ const responseHeaders = {};
121
+ if (response.$metadata.requestId)
122
+ responseHeaders["x-amzn-requestid"] = response.$metadata.requestId;
123
+ await options.onResponse?.({ status: response.$metadata.httpStatusCode, headers: responseHeaders }, state.model);
124
+ }
125
+ for await (const item of response.stream)
126
+ handleBedrockStreamEvent(item, state);
127
+ validateBedrockStreamCompletion(state.output, options.signal);
128
+ state.stream.push({ type: "done", reason: state.output.stopReason, message: state.output });
129
+ state.stream.end();
130
+ }
131
+ function handleBedrockStreamFailure(error, options, state) {
132
+ for (const block of state.output.content) {
133
+ delete block.index;
134
+ delete block.partialJson;
135
+ }
136
+ state.output.stopReason = options.signal?.aborted ? "aborted" : "error";
137
+ state.output.errorMessage = formatBedrockError(error);
138
+ state.stream.push({ type: "error", reason: state.output.stopReason, error: state.output });
139
+ state.stream.end();
140
+ }
10
141
  export const streamBedrock = (model, context, options = {}) => {
11
142
  const stream = new AssistantMessageEventStream();
12
143
  (async () => {
@@ -27,155 +158,17 @@ export const streamBedrock = (model, context, options = {}) => {
27
158
  stopReason: "stop",
28
159
  timestamp: Date.now(),
29
160
  };
30
- const blocks = output.content;
31
- const config = {
32
- profile: options.profile,
161
+ const state = {
162
+ blocks: output.content,
163
+ output,
164
+ stream,
165
+ model,
33
166
  };
34
- const configuredRegion = getConfiguredBedrockRegion(options);
35
- const hasConfiguredProfile = hasConfiguredBedrockProfile();
36
- const endpointRegion = getStandardBedrockEndpointRegion(model.baseUrl);
37
- const useExplicitEndpoint = shouldUseExplicitBedrockEndpoint(model.baseUrl, configuredRegion, hasConfiguredProfile);
38
- // Only pin standard AWS Bedrock runtime endpoints when no region/profile is configured.
39
- // This preserves custom endpoints (VPC/proxy) from #3402 without forcing built-in
40
- // catalog defaults such as us-east-1 to override AWS_REGION/AWS_PROFILE.
41
- if (useExplicitEndpoint) {
42
- config.endpoint = model.baseUrl;
43
- }
44
- // Resolve bearer token for Bedrock API key auth.
45
- const bearerToken = options.bearerToken || process.env.AWS_BEARER_TOKEN_BEDROCK || undefined;
46
- const useBearerToken = bearerToken !== undefined && process.env.AWS_BEDROCK_SKIP_AUTH !== "1";
47
- // in Node.js/Bun environment only
48
- if (typeof process !== "undefined" && (process.versions?.node || process.versions?.bun)) {
49
- // Region resolution: explicit option > env vars > SDK default chain.
50
- // When AWS_PROFILE is set, we leave region undefined so the SDK can
51
- // resovle it from aws profile configs. Otherwise fall back to us-east-1.
52
- if (configuredRegion) {
53
- config.region = configuredRegion;
54
- }
55
- else if (endpointRegion && useExplicitEndpoint) {
56
- config.region = endpointRegion;
57
- }
58
- else if (!hasConfiguredProfile) {
59
- config.region = "us-east-1";
60
- }
61
- // Support proxies that don't need authentication
62
- if (process.env.AWS_BEDROCK_SKIP_AUTH === "1") {
63
- config.credentials = {
64
- accessKeyId: "dummy-access-key",
65
- secretAccessKey: "dummy-secret-key",
66
- };
67
- }
68
- const proxyAgents = createHttpProxyAgentsForTarget(model.baseUrl);
69
- if (proxyAgents) {
70
- // Bedrock runtime uses NodeHttp2Handler by default since v3.798.0, which is based
71
- // on `http2` module and has no support for http agent.
72
- // Use NodeHttpHandler to support HTTP(S) proxy agents.
73
- config.requestHandler = new NodeHttpHandler(proxyAgents);
74
- }
75
- else if (process.env.AWS_BEDROCK_FORCE_HTTP1 === "1") {
76
- // Some custom endpoints require HTTP/1.1 instead of HTTP/2
77
- config.requestHandler = new NodeHttpHandler();
78
- }
79
- }
80
- else {
81
- // Non-Node environment (browser): fall back to us-east-1 since
82
- // there's no config file resolution available.
83
- config.region =
84
- configuredRegion || (endpointRegion && useExplicitEndpoint ? endpointRegion : undefined) || "us-east-1";
85
- }
86
- if (useBearerToken) {
87
- config.token = { token: bearerToken };
88
- config.authSchemePreference = ["httpBearerAuth"];
89
- }
90
167
  try {
91
- const client = new BedrockRuntimeClient(config);
92
- if (options.headers && Object.keys(options.headers).length > 0) {
93
- addCustomHeadersMiddleware(client, options.headers);
94
- }
95
- const cacheRetention = resolveCacheRetention(options.cacheRetention);
96
- const inferenceMaxTokens = options.maxTokens ?? (isAnthropicClaudeModel(model) ? model.maxTokens : undefined);
97
- let commandInput = {
98
- modelId: model.id,
99
- messages: convertMessages(context, model, cacheRetention),
100
- system: buildSystemPrompt(context.systemPrompt, model, cacheRetention),
101
- inferenceConfig: {
102
- ...(inferenceMaxTokens !== undefined && { maxTokens: inferenceMaxTokens }),
103
- ...(options.temperature !== undefined && { temperature: options.temperature }),
104
- },
105
- toolConfig: convertToolConfig(context.tools, options.toolChoice),
106
- additionalModelRequestFields: buildAdditionalModelRequestFields(model, options),
107
- ...(options.requestMetadata !== undefined && { requestMetadata: options.requestMetadata }),
108
- };
109
- const nextCommandInput = await options?.onPayload?.(commandInput, model);
110
- if (nextCommandInput !== undefined) {
111
- commandInput = nextCommandInput;
112
- }
113
- const command = new ConverseStreamCommand(commandInput);
114
- const response = await client.send(command, { abortSignal: options.signal });
115
- if (response.$metadata.httpStatusCode !== undefined) {
116
- const responseHeaders = {};
117
- if (response.$metadata.requestId) {
118
- responseHeaders["x-amzn-requestid"] = response.$metadata.requestId;
119
- }
120
- await options?.onResponse?.({ status: response.$metadata.httpStatusCode, headers: responseHeaders }, model);
121
- }
122
- for await (const item of response.stream) {
123
- if (item.messageStart) {
124
- if (item.messageStart.role !== ConversationRole.ASSISTANT) {
125
- throw new Error("Unexpected assistant message start but got user message start instead");
126
- }
127
- stream.push({ type: "start", partial: output });
128
- }
129
- else if (item.contentBlockStart) {
130
- handleContentBlockStart(item.contentBlockStart, blocks, output, stream);
131
- }
132
- else if (item.contentBlockDelta) {
133
- handleContentBlockDelta(item.contentBlockDelta, blocks, output, stream);
134
- }
135
- else if (item.contentBlockStop) {
136
- handleContentBlockStop(item.contentBlockStop, blocks, output, stream);
137
- }
138
- else if (item.messageStop) {
139
- output.stopReason = mapStopReason(item.messageStop.stopReason);
140
- }
141
- else if (item.metadata) {
142
- handleMetadata(item.metadata, model, output);
143
- }
144
- else if (item.internalServerException) {
145
- throw item.internalServerException;
146
- }
147
- else if (item.modelStreamErrorException) {
148
- throw item.modelStreamErrorException;
149
- }
150
- else if (item.validationException) {
151
- throw item.validationException;
152
- }
153
- else if (item.throttlingException) {
154
- throw item.throttlingException;
155
- }
156
- else if (item.serviceUnavailableException) {
157
- throw item.serviceUnavailableException;
158
- }
159
- }
160
- if (options.signal?.aborted) {
161
- throw new Error("Request was aborted");
162
- }
163
- if (output.stopReason === "error" || output.stopReason === "aborted") {
164
- throw new Error("An unknown error occurred");
165
- }
166
- stream.push({ type: "done", reason: output.stopReason, message: output });
167
- stream.end();
168
+ await executeBedrockStream(context, options, state);
168
169
  }
169
170
  catch (error) {
170
- for (const block of output.content) {
171
- delete block.index;
172
- // partialJson is only a streaming scratch buffer; never persist it.
173
- delete block.partialJson;
174
- }
175
- output.stopReason = options.signal?.aborted ? "aborted" : "error";
176
- output.errorMessage = formatBedrockError(error);
177
- stream.push({ type: "error", reason: output.stopReason, error: output });
178
- stream.end();
171
+ handleBedrockStreamFailure(error, options, state);
179
172
  }
180
173
  })();
181
174
  return stream;
@@ -289,55 +282,70 @@ function handleContentBlockStart(event, blocks, output, stream) {
289
282
  stream.push({ type: "toolcall_start", contentIndex: blocks.length - 1, partial: output });
290
283
  }
291
284
  }
292
- function handleContentBlockDelta(event, blocks, output, stream) {
285
+ function handleTextContentDelta(text, contentBlockIndex, block, index, state) {
286
+ let textBlock = block;
287
+ let textIndex = index;
288
+ if (!textBlock) {
289
+ const newBlock = { type: "text", text: "", index: contentBlockIndex };
290
+ state.output.content.push(newBlock);
291
+ textIndex = state.blocks.length - 1;
292
+ textBlock = state.blocks[textIndex];
293
+ state.stream.push({ type: "text_start", contentIndex: textIndex, partial: state.output });
294
+ }
295
+ if (textBlock.type !== "text")
296
+ return;
297
+ textBlock.text += text;
298
+ state.stream.push({ type: "text_delta", contentIndex: textIndex, delta: text, partial: state.output });
299
+ }
300
+ function handleToolUseContentDelta(input, block, index, state) {
301
+ if (block.type !== "toolCall")
302
+ return;
303
+ block.partialJson = (block.partialJson || "") + input;
304
+ block.arguments = parseStreamingJson(block.partialJson);
305
+ state.stream.push({ type: "toolcall_delta", contentIndex: index, delta: input, partial: state.output });
306
+ }
307
+ function handleReasoningContentDelta(reasoningContent, contentBlockIndex, block, index, state) {
308
+ let thinkingBlock = block;
309
+ let thinkingIndex = index;
310
+ if (!thinkingBlock) {
311
+ const newBlock = { type: "thinking", thinking: "", thinkingSignature: "", index: contentBlockIndex };
312
+ state.output.content.push(newBlock);
313
+ thinkingIndex = state.blocks.length - 1;
314
+ thinkingBlock = state.blocks[thinkingIndex];
315
+ state.stream.push({ type: "thinking_start", contentIndex: thinkingIndex, partial: state.output });
316
+ }
317
+ if (thinkingBlock?.type !== "thinking")
318
+ return;
319
+ if (reasoningContent.text) {
320
+ thinkingBlock.thinking += reasoningContent.text;
321
+ state.stream.push({
322
+ type: "thinking_delta",
323
+ contentIndex: thinkingIndex,
324
+ delta: reasoningContent.text,
325
+ partial: state.output,
326
+ });
327
+ }
328
+ if (reasoningContent.signature) {
329
+ thinkingBlock.thinkingSignature = (thinkingBlock.thinkingSignature || "") + reasoningContent.signature;
330
+ }
331
+ }
332
+ function handleContentBlockDelta(event, state) {
293
333
  const contentBlockIndex = event.contentBlockIndex;
294
334
  const delta = event.delta;
295
- let index = blocks.findIndex((b) => b.index === contentBlockIndex);
296
- let block = blocks[index];
297
- if (delta?.text !== undefined) {
298
- // If no text block exists yet, create one, as `handleContentBlockStart` is not sent for text blocks
299
- if (!block) {
300
- const newBlock = { type: "text", text: "", index: contentBlockIndex };
301
- output.content.push(newBlock);
302
- index = blocks.length - 1;
303
- block = blocks[index];
304
- stream.push({ type: "text_start", contentIndex: index, partial: output });
305
- }
306
- if (block.type === "text") {
307
- block.text += delta.text;
308
- stream.push({ type: "text_delta", contentIndex: index, delta: delta.text, partial: output });
309
- }
335
+ if (!delta)
336
+ return;
337
+ const index = state.blocks.findIndex((block) => block.index === contentBlockIndex);
338
+ const block = state.blocks[index];
339
+ if (delta.text !== undefined) {
340
+ handleTextContentDelta(delta.text, contentBlockIndex, block, index, state);
341
+ return;
310
342
  }
311
- else if (delta?.toolUse && block?.type === "toolCall") {
312
- block.partialJson = (block.partialJson || "") + (delta.toolUse.input || "");
313
- block.arguments = parseStreamingJson(block.partialJson);
314
- stream.push({ type: "toolcall_delta", contentIndex: index, delta: delta.toolUse.input || "", partial: output });
315
- }
316
- else if (delta?.reasoningContent) {
317
- let thinkingBlock = block;
318
- let thinkingIndex = index;
319
- if (!thinkingBlock) {
320
- const newBlock = { type: "thinking", thinking: "", thinkingSignature: "", index: contentBlockIndex };
321
- output.content.push(newBlock);
322
- thinkingIndex = blocks.length - 1;
323
- thinkingBlock = blocks[thinkingIndex];
324
- stream.push({ type: "thinking_start", contentIndex: thinkingIndex, partial: output });
325
- }
326
- if (thinkingBlock?.type === "thinking") {
327
- if (delta.reasoningContent.text) {
328
- thinkingBlock.thinking += delta.reasoningContent.text;
329
- stream.push({
330
- type: "thinking_delta",
331
- contentIndex: thinkingIndex,
332
- delta: delta.reasoningContent.text,
333
- partial: output,
334
- });
335
- }
336
- if (delta.reasoningContent.signature) {
337
- thinkingBlock.thinkingSignature =
338
- (thinkingBlock.thinkingSignature || "") + delta.reasoningContent.signature;
339
- }
340
- }
343
+ if (delta.toolUse && block?.type === "toolCall") {
344
+ handleToolUseContentDelta(delta.toolUse.input || "", block, index, state);
345
+ return;
346
+ }
347
+ if (delta.reasoningContent) {
348
+ handleReasoningContentDelta(delta.reasoningContent, contentBlockIndex, block, index, state);
341
349
  }
342
350
  }
343
351
  function handleMetadata(event, model, output) {
@@ -500,159 +508,124 @@ function normalizeToolCallId(id) {
500
508
  const sanitized = id.replace(/[^a-zA-Z0-9_-]/g, "_");
501
509
  return sanitized.length > 64 ? sanitized.slice(0, 64) : sanitized;
502
510
  }
511
+ function convertUserMessage(message) {
512
+ const content = [];
513
+ if (typeof message.content === "string") {
514
+ content.push({ text: sanitizeSurrogates(message.content) });
515
+ }
516
+ else {
517
+ for (const block of message.content) {
518
+ switch (block.type) {
519
+ case "text":
520
+ content.push({ text: sanitizeSurrogates(block.text) });
521
+ break;
522
+ case "image":
523
+ content.push({ image: createImageBlock(block.mimeType, block.data) });
524
+ break;
525
+ }
526
+ }
527
+ }
528
+ return content.length > 0 ? { role: ConversationRole.USER, content } : undefined;
529
+ }
530
+ function convertAssistantContentBlock(block, model) {
531
+ switch (block.type) {
532
+ case "text":
533
+ return block.text.trim().length > 0 ? { text: sanitizeSurrogates(block.text) } : undefined;
534
+ case "toolCall":
535
+ return { toolUse: { toolUseId: block.id, name: block.name, input: block.arguments } };
536
+ case "thinking":
537
+ if (block.thinking.trim().length === 0)
538
+ return undefined;
539
+ if (!supportsThinkingSignature(model)) {
540
+ return { reasoningContent: { reasoningText: { text: sanitizeSurrogates(block.thinking) } } };
541
+ }
542
+ if (!block.thinkingSignature || block.thinkingSignature.trim().length === 0) {
543
+ return { text: sanitizeSurrogates(block.thinking) };
544
+ }
545
+ return {
546
+ reasoningContent: {
547
+ reasoningText: {
548
+ text: sanitizeSurrogates(block.thinking),
549
+ signature: block.thinkingSignature,
550
+ },
551
+ },
552
+ };
553
+ }
554
+ }
555
+ function convertAssistantMessage(message, model) {
556
+ if (message.content.length === 0)
557
+ return undefined;
558
+ const content = [];
559
+ for (const block of message.content) {
560
+ const converted = convertAssistantContentBlock(block, model);
561
+ if (converted)
562
+ content.push(converted);
563
+ }
564
+ return content.length > 0 ? { role: ConversationRole.ASSISTANT, content } : undefined;
565
+ }
566
+ function convertToolResult(message) {
567
+ return {
568
+ toolResult: {
569
+ toolUseId: message.toolCallId,
570
+ content: message.content.map((block) => block.type === "image"
571
+ ? { image: createImageBlock(block.mimeType, block.data) }
572
+ : { text: sanitizeSurrogates(block.text) }),
573
+ status: message.isError ? ToolResultStatus.ERROR : ToolResultStatus.SUCCESS,
574
+ },
575
+ };
576
+ }
577
+ function collectToolResultBatch(messages, startIndex) {
578
+ const content = [];
579
+ let lastIndex = startIndex;
580
+ for (let index = startIndex; index < messages.length; index++) {
581
+ const message = messages[index];
582
+ if (message.role !== "toolResult")
583
+ break;
584
+ content.push(convertToolResult(message));
585
+ lastIndex = index;
586
+ }
587
+ return { content, lastIndex };
588
+ }
589
+ function appendPromptCachePoint(messages, model, cacheRetention) {
590
+ if (cacheRetention === "none" || !supportsPromptCaching(model) || messages.length === 0)
591
+ return;
592
+ const lastMessage = messages[messages.length - 1];
593
+ if (lastMessage.role !== ConversationRole.USER || !lastMessage.content)
594
+ return;
595
+ lastMessage.content.push({
596
+ cachePoint: {
597
+ type: CachePointType.DEFAULT,
598
+ ...(cacheRetention === "long" ? { ttl: CacheTTL.ONE_HOUR } : {}),
599
+ },
600
+ });
601
+ }
503
602
  function convertMessages(context, model, cacheRetention) {
504
603
  const result = [];
505
604
  const transformedMessages = transformMessages(context.messages, model, normalizeToolCallId);
506
- for (let i = 0; i < transformedMessages.length; i++) {
507
- const m = transformedMessages[i];
508
- switch (m.role) {
605
+ for (let index = 0; index < transformedMessages.length; index++) {
606
+ const message = transformedMessages[index];
607
+ switch (message.role) {
509
608
  case "user": {
510
- const content = [];
511
- if (typeof m.content === "string") {
512
- content.push({ text: sanitizeSurrogates(m.content) });
513
- }
514
- else {
515
- for (const c of m.content) {
516
- switch (c.type) {
517
- case "text":
518
- content.push({ text: sanitizeSurrogates(c.text) });
519
- break;
520
- case "image":
521
- content.push({ image: createImageBlock(c.mimeType, c.data) });
522
- break;
523
- default:
524
- continue;
525
- }
526
- }
527
- }
528
- if (content.length === 0)
529
- continue;
530
- result.push({
531
- role: ConversationRole.USER,
532
- content,
533
- });
609
+ const converted = convertUserMessage(message);
610
+ if (converted)
611
+ result.push(converted);
534
612
  break;
535
613
  }
536
614
  case "assistant": {
537
- // Skip assistant messages with empty content (e.g., from aborted requests)
538
- // Bedrock rejects messages with empty content arrays
539
- if (m.content.length === 0) {
540
- continue;
541
- }
542
- const contentBlocks = [];
543
- for (const c of m.content) {
544
- switch (c.type) {
545
- case "text":
546
- // Skip empty text blocks
547
- if (c.text.trim().length === 0)
548
- continue;
549
- contentBlocks.push({ text: sanitizeSurrogates(c.text) });
550
- break;
551
- case "toolCall":
552
- contentBlocks.push({
553
- toolUse: { toolUseId: c.id, name: c.name, input: c.arguments },
554
- });
555
- break;
556
- case "thinking":
557
- // Skip empty thinking blocks
558
- if (c.thinking.trim().length === 0)
559
- continue;
560
- // Only Anthropic models support the signature field in reasoningText.
561
- // For other models, we omit the signature to avoid errors like:
562
- // "This model doesn't support the reasoningContent.reasoningText.signature field"
563
- if (supportsThinkingSignature(model)) {
564
- // Signatures arrive after thinking deltas. If a partial or externally
565
- // persisted message lacks a signature, Bedrock rejects the replayed
566
- // reasoning block. Fall back to plain text, matching Anthropic.
567
- if (!c.thinkingSignature || c.thinkingSignature.trim().length === 0) {
568
- contentBlocks.push({ text: sanitizeSurrogates(c.thinking) });
569
- }
570
- else {
571
- contentBlocks.push({
572
- reasoningContent: {
573
- reasoningText: {
574
- text: sanitizeSurrogates(c.thinking),
575
- signature: c.thinkingSignature,
576
- },
577
- },
578
- });
579
- }
580
- }
581
- else {
582
- contentBlocks.push({
583
- reasoningContent: {
584
- reasoningText: { text: sanitizeSurrogates(c.thinking) },
585
- },
586
- });
587
- }
588
- break;
589
- default:
590
- continue;
591
- }
592
- }
593
- // Skip if all content blocks were filtered out
594
- if (contentBlocks.length === 0) {
595
- continue;
596
- }
597
- result.push({
598
- role: ConversationRole.ASSISTANT,
599
- content: contentBlocks,
600
- });
615
+ const converted = convertAssistantMessage(message, model);
616
+ if (converted)
617
+ result.push(converted);
601
618
  break;
602
619
  }
603
620
  case "toolResult": {
604
- // Collect all consecutive toolResult messages into a single user message
605
- // Bedrock requires all tool results to be in one message
606
- const toolResults = [];
607
- // Add current tool result with all content blocks combined
608
- toolResults.push({
609
- toolResult: {
610
- toolUseId: m.toolCallId,
611
- content: m.content.map((c) => c.type === "image"
612
- ? { image: createImageBlock(c.mimeType, c.data) }
613
- : { text: sanitizeSurrogates(c.text) }),
614
- status: m.isError ? ToolResultStatus.ERROR : ToolResultStatus.SUCCESS,
615
- },
616
- });
617
- // Look ahead for consecutive toolResult messages
618
- let j = i + 1;
619
- while (j < transformedMessages.length && transformedMessages[j].role === "toolResult") {
620
- const nextMsg = transformedMessages[j];
621
- toolResults.push({
622
- toolResult: {
623
- toolUseId: nextMsg.toolCallId,
624
- content: nextMsg.content.map((c) => c.type === "image"
625
- ? { image: createImageBlock(c.mimeType, c.data) }
626
- : { text: sanitizeSurrogates(c.text) }),
627
- status: nextMsg.isError ? ToolResultStatus.ERROR : ToolResultStatus.SUCCESS,
628
- },
629
- });
630
- j++;
631
- }
632
- // Skip the messages we've already processed
633
- i = j - 1;
634
- result.push({
635
- role: ConversationRole.USER,
636
- content: toolResults,
637
- });
621
+ const batch = collectToolResultBatch(transformedMessages, index);
622
+ result.push({ role: ConversationRole.USER, content: batch.content });
623
+ index = batch.lastIndex;
638
624
  break;
639
625
  }
640
- default:
641
- continue;
642
- }
643
- }
644
- // Add cache point to the last user message for supported Claude models when caching is enabled
645
- if (cacheRetention !== "none" && supportsPromptCaching(model) && result.length > 0) {
646
- const lastMessage = result[result.length - 1];
647
- if (lastMessage.role === ConversationRole.USER && lastMessage.content) {
648
- lastMessage.content.push({
649
- cachePoint: {
650
- type: CachePointType.DEFAULT,
651
- ...(cacheRetention === "long" ? { ttl: CacheTTL.ONE_HOUR } : {}),
652
- },
653
- });
654
626
  }
655
627
  }
628
+ appendPromptCachePoint(result, model, cacheRetention);
656
629
  return result;
657
630
  }
658
631
  function convertToolConfig(tools, toolChoice) {