@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.
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +29 -24
- package/dist/cli.js.map +1 -1
- package/dist/env-api-keys.d.ts.map +1 -1
- package/dist/env-api-keys.js +1 -1
- package/dist/env-api-keys.js.map +1 -1
- package/dist/image-models.generated.d.ts +180 -0
- package/dist/image-models.generated.d.ts.map +1 -1
- package/dist/image-models.generated.js +184 -4
- package/dist/image-models.generated.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/models.d.ts +3 -3
- package/dist/models.d.ts.map +1 -1
- package/dist/models.generated.d.ts +5389 -2066
- package/dist/models.generated.d.ts.map +1 -1
- package/dist/models.generated.js +5402 -2301
- package/dist/models.generated.js.map +1 -1
- package/dist/models.js +60 -45
- package/dist/models.js.map +1 -1
- package/dist/oauth.d.ts +6 -1
- package/dist/oauth.d.ts.map +1 -1
- package/dist/oauth.js +4 -1
- package/dist/oauth.js.map +1 -1
- package/dist/providers/amazon-bedrock.d.ts +5 -4
- package/dist/providers/amazon-bedrock.d.ts.map +1 -1
- package/dist/providers/amazon-bedrock.js +303 -330
- package/dist/providers/amazon-bedrock.js.map +1 -1
- package/dist/providers/anthropic.d.ts +5 -4
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +492 -499
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/azure-openai-responses.d.ts +4 -2
- package/dist/providers/azure-openai-responses.d.ts.map +1 -1
- package/dist/providers/azure-openai-responses.js +52 -35
- package/dist/providers/azure-openai-responses.js.map +1 -1
- package/dist/providers/faux.d.ts +19 -22
- package/dist/providers/faux.d.ts.map +1 -1
- package/dist/providers/faux.js +106 -70
- package/dist/providers/faux.js.map +1 -1
- package/dist/providers/github-copilot-headers.d.ts +7 -4
- package/dist/providers/github-copilot-headers.d.ts.map +1 -1
- package/dist/providers/github-copilot-headers.js.map +1 -1
- package/dist/providers/google-shared.d.ts +24 -5
- package/dist/providers/google-shared.d.ts.map +1 -1
- package/dist/providers/google-shared.js +139 -140
- package/dist/providers/google-shared.js.map +1 -1
- package/dist/providers/google-vertex.d.ts +3 -7
- package/dist/providers/google-vertex.d.ts.map +1 -1
- package/dist/providers/google-vertex.js +216 -219
- package/dist/providers/google-vertex.js.map +1 -1
- package/dist/providers/google.d.ts +4 -7
- package/dist/providers/google.d.ts.map +1 -1
- package/dist/providers/google.js +214 -218
- package/dist/providers/google.js.map +1 -1
- package/dist/providers/images/openrouter.d.ts.map +1 -1
- package/dist/providers/images/openrouter.js +37 -27
- package/dist/providers/images/openrouter.js.map +1 -1
- package/dist/providers/mistral.d.ts +7 -6
- package/dist/providers/mistral.d.ts.map +1 -1
- package/dist/providers/mistral.js +287 -265
- package/dist/providers/mistral.js.map +1 -1
- package/dist/providers/openai-codex-responses.d.ts +8 -5
- package/dist/providers/openai-codex-responses.d.ts.map +1 -1
- package/dist/providers/openai-codex-responses.js +493 -391
- package/dist/providers/openai-codex-responses.js.map +1 -1
- package/dist/providers/openai-completions.d.ts +7 -10
- package/dist/providers/openai-completions.d.ts.map +1 -1
- package/dist/providers/openai-completions.js +670 -664
- package/dist/providers/openai-completions.js.map +1 -1
- package/dist/providers/openai-responses-shared.d.ts +5 -4
- package/dist/providers/openai-responses-shared.d.ts.map +1 -1
- package/dist/providers/openai-responses-shared.js +390 -360
- package/dist/providers/openai-responses-shared.js.map +1 -1
- package/dist/providers/openai-responses.d.ts +5 -5
- package/dist/providers/openai-responses.d.ts.map +1 -1
- package/dist/providers/openai-responses.js +134 -109
- package/dist/providers/openai-responses.js.map +1 -1
- package/dist/providers/simple-options.d.ts +4 -2
- package/dist/providers/simple-options.d.ts.map +1 -1
- package/dist/providers/simple-options.js.map +1 -1
- package/dist/providers/transform-messages.d.ts.map +1 -1
- package/dist/providers/transform-messages.js +112 -137
- package/dist/providers/transform-messages.js.map +1 -1
- package/dist/stream.d.ts +0 -1
- package/dist/stream.d.ts.map +1 -1
- package/dist/stream.js +0 -1
- package/dist/stream.js.map +1 -1
- package/dist/types.d.ts +91 -63
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/error-body.d.ts.map +1 -1
- package/dist/utils/error-body.js +15 -4
- package/dist/utils/error-body.js.map +1 -1
- package/dist/utils/estimate.d.ts +4 -0
- package/dist/utils/estimate.d.ts.map +1 -1
- package/dist/utils/estimate.js +3 -1
- package/dist/utils/estimate.js.map +1 -1
- package/dist/utils/headers.d.ts +3 -0
- package/dist/utils/headers.d.ts.map +1 -1
- package/dist/utils/headers.js +21 -0
- package/dist/utils/headers.js.map +1 -1
- package/dist/utils/json-parse.d.ts.map +1 -1
- package/dist/utils/json-parse.js +17 -19
- package/dist/utils/json-parse.js.map +1 -1
- package/dist/utils/oauth/anthropic.d.ts +4 -11
- package/dist/utils/oauth/anthropic.d.ts.map +1 -1
- package/dist/utils/oauth/anthropic.js +100 -117
- package/dist/utils/oauth/anthropic.js.map +1 -1
- package/dist/utils/oauth/device-code.d.ts.map +1 -1
- package/dist/utils/oauth/device-code.js +51 -38
- package/dist/utils/oauth/device-code.js.map +1 -1
- package/dist/utils/oauth/github-copilot.d.ts +6 -9
- package/dist/utils/oauth/github-copilot.d.ts.map +1 -1
- package/dist/utils/oauth/github-copilot.js +47 -42
- package/dist/utils/oauth/github-copilot.js.map +1 -1
- package/dist/utils/oauth/index.d.ts +8 -17
- package/dist/utils/oauth/index.d.ts.map +1 -1
- package/dist/utils/oauth/index.js +27 -23
- package/dist/utils/oauth/index.js.map +1 -1
- package/dist/utils/oauth/oauth-page.d.ts.map +1 -1
- package/dist/utils/oauth/oauth-page.js.map +1 -1
- package/dist/utils/oauth/openai-codex.d.ts +7 -12
- package/dist/utils/oauth/openai-codex.d.ts.map +1 -1
- package/dist/utils/oauth/openai-codex.js +72 -89
- package/dist/utils/oauth/openai-codex.js.map +1 -1
- package/dist/utils/oauth/pkce.d.ts +5 -4
- package/dist/utils/oauth/pkce.d.ts.map +1 -1
- package/dist/utils/oauth/pkce.js.map +1 -1
- package/dist/utils/oauth/types.d.ts +7 -2
- package/dist/utils/oauth/types.d.ts.map +1 -1
- package/dist/utils/oauth/types.js.map +1 -1
- package/dist/utils/provider-retry.d.ts +16 -0
- package/dist/utils/provider-retry.d.ts.map +1 -0
- package/dist/utils/provider-retry.js +94 -0
- package/dist/utils/provider-retry.js.map +1 -0
- package/dist/utils/retry.d.ts +14 -0
- package/dist/utils/retry.d.ts.map +1 -1
- package/dist/utils/retry.js +84 -0
- package/dist/utils/retry.js.map +1 -1
- package/dist/utils/typebox-helpers.d.ts +5 -4
- package/dist/utils/typebox-helpers.d.ts.map +1 -1
- package/dist/utils/typebox-helpers.js.map +1 -1
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +102 -99
- package/dist/utils/validation.js.map +1 -1
- 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
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
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
|
|
507
|
-
const
|
|
508
|
-
switch (
|
|
605
|
+
for (let index = 0; index < transformedMessages.length; index++) {
|
|
606
|
+
const message = transformedMessages[index];
|
|
607
|
+
switch (message.role) {
|
|
509
608
|
case "user": {
|
|
510
|
-
const
|
|
511
|
-
if (
|
|
512
|
-
|
|
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
|
-
|
|
538
|
-
|
|
539
|
-
|
|
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
|
-
|
|
605
|
-
|
|
606
|
-
|
|
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) {
|