@nodetool-ai/runtime 0.8.2 → 0.8.4-a13.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/.nodetool-build-stamp +1 -1
- package/dist/canvas-codec.d.ts +3 -0
- package/dist/canvas-codec.d.ts.map +1 -0
- package/dist/canvas-codec.js +12 -0
- package/dist/canvas-codec.js.map +1 -0
- package/dist/context-packer.d.ts +1 -1
- package/dist/context-packer.js +1 -1
- package/dist/context.d.ts +3 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +16 -5
- package/dist/context.js.map +1 -1
- package/dist/host-binaries.d.ts.map +1 -1
- package/dist/host-binaries.js +4 -3
- package/dist/host-binaries.js.map +1 -1
- package/dist/http-client-spans.d.ts +9 -0
- package/dist/http-client-spans.d.ts.map +1 -0
- package/dist/http-client-spans.js +88 -0
- package/dist/http-client-spans.js.map +1 -0
- package/dist/image-codec.d.ts +6 -3
- package/dist/image-codec.d.ts.map +1 -1
- package/dist/image-codec.js +17 -17
- package/dist/image-codec.js.map +1 -1
- package/dist/image-model-tasks.d.ts +20 -0
- package/dist/image-model-tasks.d.ts.map +1 -1
- package/dist/image-model-tasks.js +44 -1
- package/dist/image-model-tasks.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/media-ref-bytes.d.ts.map +1 -1
- package/dist/media-ref-bytes.js +31 -6
- package/dist/media-ref-bytes.js.map +1 -1
- package/dist/otlp-json-trace-exporter.d.ts +89 -0
- package/dist/otlp-json-trace-exporter.d.ts.map +1 -0
- package/dist/otlp-json-trace-exporter.js +155 -0
- package/dist/otlp-json-trace-exporter.js.map +1 -0
- package/dist/prompt-asset-refs.d.ts +12 -1
- package/dist/prompt-asset-refs.d.ts.map +1 -1
- package/dist/prompt-asset-refs.js +9 -4
- package/dist/prompt-asset-refs.js.map +1 -1
- package/dist/providers/antigravity-provider.d.ts +113 -0
- package/dist/providers/antigravity-provider.d.ts.map +1 -0
- package/dist/providers/antigravity-provider.js +527 -0
- package/dist/providers/antigravity-provider.js.map +1 -0
- package/dist/providers/api-route-provider.d.ts +22 -0
- package/dist/providers/api-route-provider.d.ts.map +1 -0
- package/dist/providers/api-route-provider.js +56 -0
- package/dist/providers/api-route-provider.js.map +1 -0
- package/dist/providers/azure-openai-provider.d.ts +69 -0
- package/dist/providers/azure-openai-provider.d.ts.map +1 -0
- package/dist/providers/azure-openai-provider.js +233 -0
- package/dist/providers/azure-openai-provider.js.map +1 -0
- package/dist/providers/base-provider.d.ts.map +1 -1
- package/dist/providers/base-provider.js +41 -4
- package/dist/providers/base-provider.js.map +1 -1
- package/dist/providers/bedrock-provider.d.ts +52 -0
- package/dist/providers/bedrock-provider.d.ts.map +1 -0
- package/dist/providers/bedrock-provider.js +648 -0
- package/dist/providers/bedrock-provider.js.map +1 -0
- package/dist/providers/bedrock-transport.d.ts +37 -0
- package/dist/providers/bedrock-transport.d.ts.map +1 -0
- package/dist/providers/bedrock-transport.js +186 -0
- package/dist/providers/bedrock-transport.js.map +1 -0
- package/dist/providers/chatgpt-provider.d.ts +48 -0
- package/dist/providers/chatgpt-provider.d.ts.map +1 -0
- package/dist/providers/chatgpt-provider.js +551 -0
- package/dist/providers/chatgpt-provider.js.map +1 -0
- package/dist/providers/claude-agent-provider.d.ts +29 -51
- package/dist/providers/claude-agent-provider.d.ts.map +1 -1
- package/dist/providers/claude-agent-provider.js +144 -202
- package/dist/providers/claude-agent-provider.js.map +1 -1
- package/dist/providers/claude-cli.d.ts +265 -0
- package/dist/providers/claude-cli.d.ts.map +1 -0
- package/dist/providers/claude-cli.js +488 -0
- package/dist/providers/claude-cli.js.map +1 -0
- package/dist/providers/codex-provider.d.ts +9 -2
- package/dist/providers/codex-provider.d.ts.map +1 -1
- package/dist/providers/codex-provider.js +25 -11
- package/dist/providers/codex-provider.js.map +1 -1
- package/dist/providers/context-window.d.ts.map +1 -1
- package/dist/providers/context-window.js +1 -0
- package/dist/providers/context-window.js.map +1 -1
- package/dist/providers/core-tools.d.ts +4 -1
- package/dist/providers/core-tools.d.ts.map +1 -1
- package/dist/providers/core-tools.js +5 -3
- package/dist/providers/core-tools.js.map +1 -1
- package/dist/providers/cost-calculator.d.ts.map +1 -1
- package/dist/providers/cost-calculator.js +9 -0
- package/dist/providers/cost-calculator.js.map +1 -1
- package/dist/providers/credential-check.d.ts +8 -0
- package/dist/providers/credential-check.d.ts.map +1 -1
- package/dist/providers/credential-check.js +22 -0
- package/dist/providers/credential-check.js.map +1 -1
- package/dist/providers/custom-openai-provider.d.ts.map +1 -1
- package/dist/providers/custom-openai-provider.js +19 -1
- package/dist/providers/custom-openai-provider.js.map +1 -1
- package/dist/providers/dreamina-pending.d.ts +22 -0
- package/dist/providers/dreamina-pending.d.ts.map +1 -0
- package/dist/providers/dreamina-pending.js +86 -0
- package/dist/providers/dreamina-pending.js.map +1 -0
- package/dist/providers/dreamina-provider.d.ts +10 -3
- package/dist/providers/dreamina-provider.d.ts.map +1 -1
- package/dist/providers/dreamina-provider.js +325 -45
- package/dist/providers/dreamina-provider.js.map +1 -1
- package/dist/providers/extension-tab.d.ts +42 -0
- package/dist/providers/extension-tab.d.ts.map +1 -0
- package/dist/providers/extension-tab.js +200 -0
- package/dist/providers/extension-tab.js.map +1 -0
- package/dist/providers/fake-provider.d.ts +3 -1
- package/dist/providers/fake-provider.d.ts.map +1 -1
- package/dist/providers/fake-provider.js +63 -9
- package/dist/providers/fake-provider.js.map +1 -1
- package/dist/providers/fake-schema-answer.d.ts +10 -0
- package/dist/providers/fake-schema-answer.d.ts.map +1 -0
- package/dist/providers/fake-schema-answer.js +64 -0
- package/dist/providers/fake-schema-answer.js.map +1 -0
- package/dist/providers/fal-provider.d.ts.map +1 -1
- package/dist/providers/fal-provider.js +5 -2
- package/dist/providers/fal-provider.js.map +1 -1
- package/dist/providers/gemini-web-provider.d.ts +76 -0
- package/dist/providers/gemini-web-provider.d.ts.map +1 -0
- package/dist/providers/gemini-web-provider.js +827 -0
- package/dist/providers/gemini-web-provider.js.map +1 -0
- package/dist/providers/http-transport.d.ts.map +1 -1
- package/dist/providers/http-transport.js +2 -0
- package/dist/providers/http-transport.js.map +1 -1
- package/dist/providers/huggingface-provider.d.ts.map +1 -1
- package/dist/providers/huggingface-provider.js +17 -1
- package/dist/providers/huggingface-provider.js.map +1 -1
- package/dist/providers/index.d.ts +18 -2
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +66 -15
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/llama-provider.d.ts +8 -0
- package/dist/providers/llama-provider.d.ts.map +1 -1
- package/dist/providers/llama-provider.js +23 -5
- package/dist/providers/llama-provider.js.map +1 -1
- package/dist/providers/lmstudio-provider.js +2 -2
- package/dist/providers/lmstudio-provider.js.map +1 -1
- package/dist/providers/manifest-models.d.ts +2 -12
- package/dist/providers/manifest-models.d.ts.map +1 -1
- package/dist/providers/manifest-models.js +45 -3
- package/dist/providers/manifest-models.js.map +1 -1
- package/dist/providers/meta-provider.d.ts +53 -6
- package/dist/providers/meta-provider.d.ts.map +1 -1
- package/dist/providers/meta-provider.js +289 -6
- package/dist/providers/meta-provider.js.map +1 -1
- package/dist/providers/meta-segmentation.d.ts +27 -0
- package/dist/providers/meta-segmentation.d.ts.map +1 -0
- package/dist/providers/meta-segmentation.js +141 -0
- package/dist/providers/meta-segmentation.js.map +1 -0
- package/dist/providers/meta-transcription.d.ts +31 -0
- package/dist/providers/meta-transcription.d.ts.map +1 -0
- package/dist/providers/meta-transcription.js +187 -0
- package/dist/providers/meta-transcription.js.map +1 -0
- package/dist/providers/node-llama-cpp-provider.d.ts +6 -3
- package/dist/providers/node-llama-cpp-provider.d.ts.map +1 -1
- package/dist/providers/node-llama-cpp-provider.js +77 -62
- package/dist/providers/node-llama-cpp-provider.js.map +1 -1
- package/dist/providers/ollama-provider.d.ts +23 -0
- package/dist/providers/ollama-provider.d.ts.map +1 -1
- package/dist/providers/ollama-provider.js +140 -27
- package/dist/providers/ollama-provider.js.map +1 -1
- package/dist/providers/openai-compat/client.d.ts +10 -1
- package/dist/providers/openai-compat/client.d.ts.map +1 -1
- package/dist/providers/openai-compat/client.js +21 -3
- package/dist/providers/openai-compat/client.js.map +1 -1
- package/dist/providers/openai-compat/index.d.ts +1 -1
- package/dist/providers/openai-compat/index.d.ts.map +1 -1
- package/dist/providers/openai-compat/index.js +1 -1
- package/dist/providers/openai-compat/index.js.map +1 -1
- package/dist/providers/openai-compat/types.d.ts +4 -0
- package/dist/providers/openai-compat/types.d.ts.map +1 -1
- package/dist/providers/openai-compat-provider.d.ts +11 -0
- package/dist/providers/openai-compat-provider.d.ts.map +1 -1
- package/dist/providers/openai-compat-provider.js +47 -2
- package/dist/providers/openai-compat-provider.js.map +1 -1
- package/dist/providers/openai-provider.d.ts +15 -0
- package/dist/providers/openai-provider.d.ts.map +1 -1
- package/dist/providers/openai-provider.js +50 -12
- package/dist/providers/openai-provider.js.map +1 -1
- package/dist/providers/opper-provider.d.ts +31 -0
- package/dist/providers/opper-provider.d.ts.map +1 -0
- package/dist/providers/opper-provider.js +91 -0
- package/dist/providers/opper-provider.js.map +1 -0
- package/dist/providers/provider-request-log.d.ts.map +1 -1
- package/dist/providers/provider-request-log.js +3 -2
- package/dist/providers/provider-request-log.js.map +1 -1
- package/dist/providers/python-provider.d.ts.map +1 -1
- package/dist/providers/python-provider.js +5 -3
- package/dist/providers/python-provider.js.map +1 -1
- package/dist/providers/responses-api.d.ts +12 -2
- package/dist/providers/responses-api.d.ts.map +1 -1
- package/dist/providers/responses-api.js +54 -3
- package/dist/providers/responses-api.js.map +1 -1
- package/dist/providers/safe-url.d.ts.map +1 -1
- package/dist/providers/safe-url.js +8 -0
- package/dist/providers/safe-url.js.map +1 -1
- package/dist/providers/scripted-provider.d.ts.map +1 -1
- package/dist/providers/scripted-provider.js +6 -1
- package/dist/providers/scripted-provider.js.map +1 -1
- package/dist/providers/types.d.ts +16 -0
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/providers/types.js.map +1 -1
- package/dist/providers/vgenv-provider.d.ts +33 -0
- package/dist/providers/vgenv-provider.d.ts.map +1 -0
- package/dist/providers/vgenv-provider.js +261 -0
- package/dist/providers/vgenv-provider.js.map +1 -0
- package/dist/providers/vllm-provider.d.ts +7 -0
- package/dist/providers/vllm-provider.d.ts.map +1 -1
- package/dist/providers/vllm-provider.js +15 -3
- package/dist/providers/vllm-provider.js.map +1 -1
- package/dist/python-bridge-base.d.ts +16 -6
- package/dist/python-bridge-base.d.ts.map +1 -1
- package/dist/python-bridge-base.js +117 -40
- package/dist/python-bridge-base.js.map +1 -1
- package/dist/python-bridge-codec.d.ts +16 -1
- package/dist/python-bridge-codec.d.ts.map +1 -1
- package/dist/python-bridge-codec.js +60 -2
- package/dist/python-bridge-codec.js.map +1 -1
- package/dist/python-bridge-framing.d.ts +10 -1
- package/dist/python-bridge-framing.d.ts.map +1 -1
- package/dist/python-bridge-framing.js +33 -8
- package/dist/python-bridge-framing.js.map +1 -1
- package/dist/python-bridge-types.d.ts +26 -5
- package/dist/python-bridge-types.d.ts.map +1 -1
- package/dist/python-bridge-types.js.map +1 -1
- package/dist/python-graph-resolver.d.ts.map +1 -1
- package/dist/python-graph-resolver.js +12 -2
- package/dist/python-graph-resolver.js.map +1 -1
- package/dist/python-node-executor.d.ts +14 -3
- package/dist/python-node-executor.d.ts.map +1 -1
- package/dist/python-node-executor.js +185 -3
- package/dist/python-node-executor.js.map +1 -1
- package/dist/python-stdio-bridge.d.ts +6 -0
- package/dist/python-stdio-bridge.d.ts.map +1 -1
- package/dist/python-stdio-bridge.js +58 -12
- package/dist/python-stdio-bridge.js.map +1 -1
- package/dist/run-trace-context.d.ts +2 -27
- package/dist/run-trace-context.d.ts.map +1 -1
- package/dist/run-trace-context.js +4 -51
- package/dist/run-trace-context.js.map +1 -1
- package/dist/run-trace-processor.d.ts +3 -0
- package/dist/run-trace-processor.d.ts.map +1 -1
- package/dist/run-trace-processor.js +63 -46
- package/dist/run-trace-processor.js.map +1 -1
- package/dist/run-trace-scope.d.ts +28 -0
- package/dist/run-trace-scope.d.ts.map +1 -0
- package/dist/run-trace-scope.js +51 -0
- package/dist/run-trace-scope.js.map +1 -0
- package/dist/run-trace-serialization.d.ts.map +1 -1
- package/dist/run-trace-serialization.js +4 -3
- package/dist/run-trace-serialization.js.map +1 -1
- package/dist/span-record.d.ts +4 -0
- package/dist/span-record.d.ts.map +1 -0
- package/dist/span-record.js +48 -0
- package/dist/span-record.js.map +1 -0
- package/dist/swappable-python-bridge.d.ts +6 -6
- package/dist/swappable-python-bridge.d.ts.map +1 -1
- package/dist/swappable-python-bridge.js +10 -10
- package/dist/swappable-python-bridge.js.map +1 -1
- package/dist/telemetry.d.ts +2 -2
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +33 -34
- package/dist/telemetry.js.map +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +55 -3
- package/dist/testing.js.map +1 -1
- package/dist/token-counter.d.ts +4 -13
- package/dist/token-counter.d.ts.map +1 -1
- package/dist/token-counter.js +68 -18
- package/dist/token-counter.js.map +1 -1
- package/dist/trace-context-manager.d.ts +12 -0
- package/dist/trace-context-manager.d.ts.map +1 -0
- package/dist/trace-context-manager.js +30 -0
- package/dist/trace-context-manager.js.map +1 -0
- package/dist/trace-exporters.d.ts +2 -3
- package/dist/trace-exporters.d.ts.map +1 -1
- package/dist/trace-exporters.js +5 -52
- package/dist/trace-exporters.js.map +1 -1
- package/dist/trace-semantic-conventions.d.ts +2 -0
- package/dist/trace-semantic-conventions.d.ts.map +1 -0
- package/dist/trace-semantic-conventions.js +2 -0
- package/dist/trace-semantic-conventions.js.map +1 -0
- package/dist/traced-storage.d.ts +17 -0
- package/dist/traced-storage.d.ts.map +1 -0
- package/dist/traced-storage.js +41 -0
- package/dist/traced-storage.js.map +1 -0
- package/dist/tracing-module.d.ts +3 -0
- package/dist/tracing-module.d.ts.map +1 -0
- package/dist/tracing-module.js +12 -0
- package/dist/tracing-module.js.map +1 -0
- package/package.json +37 -18
|
@@ -0,0 +1,648 @@
|
|
|
1
|
+
import { PROVIDER_IDS, isRecord, isString } from "@nodetool-ai/protocol";
|
|
2
|
+
import { BaseProvider } from "./base-provider.js";
|
|
3
|
+
import { decodeEventStream, signAwsRequest } from "./bedrock-transport.js";
|
|
4
|
+
import { sniffImageMime } from "./image-mime.js";
|
|
5
|
+
import { safeFetch } from "./safe-url.js";
|
|
6
|
+
const DEFAULT_REGION = "us-east-1";
|
|
7
|
+
const REGION_PATTERN = /^[a-z]{2}(-[a-z]+)+-\d+$/;
|
|
8
|
+
const SERVICE = "bedrock";
|
|
9
|
+
const DEFAULT_MAX_TOKENS = 4096;
|
|
10
|
+
const IMAGE_FORMATS = {
|
|
11
|
+
"image/png": "png",
|
|
12
|
+
"image/jpeg": "jpeg",
|
|
13
|
+
"image/gif": "gif",
|
|
14
|
+
"image/webp": "webp"
|
|
15
|
+
};
|
|
16
|
+
const DOCUMENT_FORMATS = {
|
|
17
|
+
"application/pdf": "pdf",
|
|
18
|
+
"text/csv": "csv",
|
|
19
|
+
"application/msword": "doc",
|
|
20
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": "docx",
|
|
21
|
+
"application/vnd.ms-excel": "xls",
|
|
22
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "xlsx",
|
|
23
|
+
"text/html": "html",
|
|
24
|
+
"text/plain": "txt",
|
|
25
|
+
"text/markdown": "md"
|
|
26
|
+
};
|
|
27
|
+
/** Event-stream exception types and the HTTP status Bedrock documents for each. */
|
|
28
|
+
const STREAM_EXCEPTION_STATUS = {
|
|
29
|
+
throttlingException: 429,
|
|
30
|
+
validationException: 400,
|
|
31
|
+
accessDeniedException: 403,
|
|
32
|
+
modelStreamErrorException: 424,
|
|
33
|
+
modelTimeoutException: 408,
|
|
34
|
+
serviceUnavailableException: 503,
|
|
35
|
+
internalServerException: 500
|
|
36
|
+
};
|
|
37
|
+
function imageFamily(modelId) {
|
|
38
|
+
const id = modelId.toLowerCase();
|
|
39
|
+
if (/amazon\.(nova-canvas|titan-image)/.test(id))
|
|
40
|
+
return "amazon";
|
|
41
|
+
if (/stability\./.test(id))
|
|
42
|
+
return "stability";
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
function toBytes(data) {
|
|
46
|
+
if (data instanceof Uint8Array)
|
|
47
|
+
return { bytes: data };
|
|
48
|
+
const match = /^data:([^;,]+)?(;base64)?,(.*)$/s.exec(data);
|
|
49
|
+
if (match) {
|
|
50
|
+
const payload = match[2]
|
|
51
|
+
? Buffer.from(match[3], "base64")
|
|
52
|
+
: Buffer.from(decodeURIComponent(match[3]), "utf8");
|
|
53
|
+
return { bytes: new Uint8Array(payload), mime: match[1] };
|
|
54
|
+
}
|
|
55
|
+
return { bytes: new Uint8Array(Buffer.from(data, "base64")) };
|
|
56
|
+
}
|
|
57
|
+
function textOf(content) {
|
|
58
|
+
if (content == null)
|
|
59
|
+
return "";
|
|
60
|
+
if (isString(content))
|
|
61
|
+
return content;
|
|
62
|
+
return content
|
|
63
|
+
.filter((part) => part.type === "text")
|
|
64
|
+
.map((part) => part.text)
|
|
65
|
+
.join("\n");
|
|
66
|
+
}
|
|
67
|
+
/** Bedrock document names allow letters, digits, spaces, hyphens, parentheses and brackets. */
|
|
68
|
+
function documentName(title, index) {
|
|
69
|
+
const cleaned = (title ?? "")
|
|
70
|
+
.replace(/[^A-Za-z0-9\s\-()[\]]/g, " ")
|
|
71
|
+
.replace(/\s+/g, " ")
|
|
72
|
+
.trim();
|
|
73
|
+
return cleaned || `document ${index + 1}`;
|
|
74
|
+
}
|
|
75
|
+
function thinkingFields(model, thinking) {
|
|
76
|
+
if (!thinking || thinking.type === "disabled")
|
|
77
|
+
return undefined;
|
|
78
|
+
if (!model.toLowerCase().includes("anthropic."))
|
|
79
|
+
return undefined;
|
|
80
|
+
if (thinking.type === "adaptive")
|
|
81
|
+
return { thinking: { type: "adaptive" } };
|
|
82
|
+
return { thinking: { type: "enabled", budget_tokens: thinking.budgetTokens } };
|
|
83
|
+
}
|
|
84
|
+
function reasoningBlocks(blocks) {
|
|
85
|
+
return (blocks ?? []).map((block) => block.type === "thinking"
|
|
86
|
+
? { reasoningContent: { reasoningText: { text: block.thinking, signature: block.signature } } }
|
|
87
|
+
: { reasoningContent: { redactedContent: block.data } });
|
|
88
|
+
}
|
|
89
|
+
/** Read a Converse response's reasoning blocks back into the shape the agent loop carries between turns. */
|
|
90
|
+
function thinkingBlocksOf(content) {
|
|
91
|
+
const blocks = [];
|
|
92
|
+
for (const block of content) {
|
|
93
|
+
const reasoning = block.reasoningContent;
|
|
94
|
+
if (!isRecord(reasoning))
|
|
95
|
+
continue;
|
|
96
|
+
const text = reasoning.reasoningText;
|
|
97
|
+
if (isRecord(text) && isString(text.text)) {
|
|
98
|
+
blocks.push({
|
|
99
|
+
type: "thinking",
|
|
100
|
+
thinking: text.text,
|
|
101
|
+
signature: isString(text.signature) ? text.signature : ""
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
else if (isString(reasoning.redactedContent)) {
|
|
105
|
+
blocks.push({ type: "redacted_thinking", data: reasoning.redactedContent });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return blocks;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Amazon Bedrock. Chat runs through the Converse API, which speaks one request
|
|
112
|
+
* shape for every Bedrock chat model: Claude, Llama, Mistral, Nova and the
|
|
113
|
+
* rest. Image generation runs through InvokeModel for Amazon Nova Canvas,
|
|
114
|
+
* Titan Image and Stability models.
|
|
115
|
+
*
|
|
116
|
+
* Requests are signed with SigV4 from an IAM access key. Models are listed
|
|
117
|
+
* from the region's foundation models and cross-region inference profiles
|
|
118
|
+
* (`eu.anthropic.claude-…`), or from `BEDROCK_MODEL_IDS` when set.
|
|
119
|
+
*/
|
|
120
|
+
export class BedrockProvider extends BaseProvider {
|
|
121
|
+
static requiredSecrets() {
|
|
122
|
+
return ["BEDROCK_ACCESS_KEY_ID", "BEDROCK_SECRET_ACCESS_KEY"];
|
|
123
|
+
}
|
|
124
|
+
region;
|
|
125
|
+
credentials;
|
|
126
|
+
modelIds;
|
|
127
|
+
_fetch;
|
|
128
|
+
now;
|
|
129
|
+
listing = null;
|
|
130
|
+
constructor(secrets, options = {}) {
|
|
131
|
+
super(PROVIDER_IDS.BEDROCK);
|
|
132
|
+
const accessKeyId = secrets.BEDROCK_ACCESS_KEY_ID?.trim();
|
|
133
|
+
const secretAccessKey = secrets.BEDROCK_SECRET_ACCESS_KEY?.trim();
|
|
134
|
+
if (!accessKeyId || !secretAccessKey) {
|
|
135
|
+
throw new Error("BEDROCK_ACCESS_KEY_ID and BEDROCK_SECRET_ACCESS_KEY are required");
|
|
136
|
+
}
|
|
137
|
+
this.region = secrets.BEDROCK_REGION?.trim() || DEFAULT_REGION;
|
|
138
|
+
this.credentials = {
|
|
139
|
+
accessKeyId,
|
|
140
|
+
secretAccessKey,
|
|
141
|
+
sessionToken: secrets.BEDROCK_SESSION_TOKEN?.trim() || undefined
|
|
142
|
+
};
|
|
143
|
+
this.modelIds = (secrets.BEDROCK_MODEL_IDS ?? "")
|
|
144
|
+
.split(",")
|
|
145
|
+
.map((id) => id.trim())
|
|
146
|
+
.filter((id) => id.length > 0);
|
|
147
|
+
this._fetch = options.fetchFn ?? globalThis.fetch.bind(globalThis);
|
|
148
|
+
this.now = options.now ?? (() => new Date());
|
|
149
|
+
}
|
|
150
|
+
getContainerEnv() {
|
|
151
|
+
const env = {
|
|
152
|
+
BEDROCK_ACCESS_KEY_ID: this.credentials.accessKeyId,
|
|
153
|
+
BEDROCK_SECRET_ACCESS_KEY: this.credentials.secretAccessKey,
|
|
154
|
+
BEDROCK_REGION: this.region
|
|
155
|
+
};
|
|
156
|
+
if (this.credentials.sessionToken) {
|
|
157
|
+
env.BEDROCK_SESSION_TOKEN = this.credentials.sessionToken;
|
|
158
|
+
}
|
|
159
|
+
return env;
|
|
160
|
+
}
|
|
161
|
+
declaredCapabilities() {
|
|
162
|
+
return ["text_to_image"];
|
|
163
|
+
}
|
|
164
|
+
async hasToolSupport(_model) {
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
async request(host, method, path, body, signal) {
|
|
168
|
+
// The region becomes part of the host name, so it must be a region and
|
|
169
|
+
// nothing else.
|
|
170
|
+
if (!REGION_PATTERN.test(this.region)) {
|
|
171
|
+
throw new Error(`BEDROCK_REGION is not an AWS region: ${this.region}`);
|
|
172
|
+
}
|
|
173
|
+
const url = new URL(`https://${host}.${this.region}.amazonaws.com${path}`);
|
|
174
|
+
const payload = body === undefined ? "" : JSON.stringify(body);
|
|
175
|
+
const headers = signAwsRequest({
|
|
176
|
+
method,
|
|
177
|
+
url,
|
|
178
|
+
body: payload,
|
|
179
|
+
region: this.region,
|
|
180
|
+
service: SERVICE,
|
|
181
|
+
credentials: this.credentials,
|
|
182
|
+
headers: body === undefined ? { accept: "application/json" } : { "content-type": "application/json", accept: "application/json" },
|
|
183
|
+
now: this.now()
|
|
184
|
+
});
|
|
185
|
+
const response = await this._fetch(url.toString(), {
|
|
186
|
+
method,
|
|
187
|
+
headers,
|
|
188
|
+
body: body === undefined ? undefined : payload,
|
|
189
|
+
signal
|
|
190
|
+
});
|
|
191
|
+
if (!response.ok) {
|
|
192
|
+
const text = await response.text().catch(() => "");
|
|
193
|
+
let message = text;
|
|
194
|
+
try {
|
|
195
|
+
const parsed = JSON.parse(text);
|
|
196
|
+
if (isRecord(parsed) && isString(parsed.message))
|
|
197
|
+
message = parsed.message;
|
|
198
|
+
else if (isRecord(parsed) && isString(parsed.Message))
|
|
199
|
+
message = parsed.Message;
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
// Not JSON: keep the raw body as the message.
|
|
203
|
+
}
|
|
204
|
+
throw new Error(`Bedrock error ${response.status}: ${message || response.statusText}`);
|
|
205
|
+
}
|
|
206
|
+
return response;
|
|
207
|
+
}
|
|
208
|
+
modelPath(model, action) {
|
|
209
|
+
return `/model/${encodeURIComponent(model)}/${action}`;
|
|
210
|
+
}
|
|
211
|
+
// ── Model discovery ────────────────────────────────────────────────
|
|
212
|
+
async listModels() {
|
|
213
|
+
if (this.modelIds.length > 0) {
|
|
214
|
+
return {
|
|
215
|
+
language: this.modelIds
|
|
216
|
+
.filter((id) => !imageFamily(id))
|
|
217
|
+
.map((id) => ({ id, name: id, provider: this.provider })),
|
|
218
|
+
image: this.modelIds
|
|
219
|
+
.filter((id) => imageFamily(id))
|
|
220
|
+
.map((id) => ({ id, name: id, provider: this.provider, supportedTasks: ["text_to_image"] }))
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
this.listing ??= this.fetchModelListing().catch((error) => {
|
|
224
|
+
this.listing = null;
|
|
225
|
+
throw error;
|
|
226
|
+
});
|
|
227
|
+
return this.listing;
|
|
228
|
+
}
|
|
229
|
+
async fetchModelListing() {
|
|
230
|
+
const foundation = await (await this.request("bedrock", "GET", "/foundation-models")).json();
|
|
231
|
+
const summaries = (foundation.modelSummaries ?? []).filter((m) => isString(m.modelId) && m.modelLifecycle?.status !== "LEGACY");
|
|
232
|
+
const textModels = new Set(summaries.filter((m) => m.outputModalities?.includes("TEXT")).map((m) => m.modelId));
|
|
233
|
+
const label = (m) => [m.providerName, m.modelName].filter(Boolean).join(" ") || m.modelId;
|
|
234
|
+
const language = summaries
|
|
235
|
+
.filter((m) => textModels.has(m.modelId) && m.inferenceTypesSupported?.includes("ON_DEMAND"))
|
|
236
|
+
.map((m) => ({ id: m.modelId, name: label(m), provider: this.provider }));
|
|
237
|
+
const image = summaries
|
|
238
|
+
.filter((m) => m.outputModalities?.includes("IMAGE") &&
|
|
239
|
+
m.inferenceTypesSupported?.includes("ON_DEMAND") &&
|
|
240
|
+
imageFamily(m.modelId))
|
|
241
|
+
.map((m) => ({
|
|
242
|
+
id: m.modelId,
|
|
243
|
+
name: label(m),
|
|
244
|
+
provider: this.provider,
|
|
245
|
+
supportedTasks: ["text_to_image"]
|
|
246
|
+
}));
|
|
247
|
+
let nextToken;
|
|
248
|
+
do {
|
|
249
|
+
const query = new URLSearchParams({ typeEquals: "SYSTEM_DEFINED", maxResults: "1000" });
|
|
250
|
+
if (nextToken)
|
|
251
|
+
query.set("nextToken", nextToken);
|
|
252
|
+
const page = await (await this.request("bedrock", "GET", `/inference-profiles?${query}`)).json();
|
|
253
|
+
for (const profile of page.inferenceProfileSummaries ?? []) {
|
|
254
|
+
if (!isString(profile.inferenceProfileId) || profile.status === "INACTIVE")
|
|
255
|
+
continue;
|
|
256
|
+
const servesText = (profile.models ?? []).some((m) => textModels.has(String(m.modelArn ?? "").split("/").pop() ?? ""));
|
|
257
|
+
if (!servesText)
|
|
258
|
+
continue;
|
|
259
|
+
language.push({
|
|
260
|
+
id: profile.inferenceProfileId,
|
|
261
|
+
name: profile.inferenceProfileName || profile.inferenceProfileId,
|
|
262
|
+
provider: this.provider
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
nextToken = page.nextToken || undefined;
|
|
266
|
+
} while (nextToken);
|
|
267
|
+
return { language, image };
|
|
268
|
+
}
|
|
269
|
+
async getAvailableLanguageModels() {
|
|
270
|
+
try {
|
|
271
|
+
return (await this.listModels()).language;
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
// A failed listing (bad key, missing bedrock:ListFoundationModels
|
|
275
|
+
// permission, offline) leaves the picker empty instead of failing discovery.
|
|
276
|
+
return [];
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
async getAvailableImageModels() {
|
|
280
|
+
try {
|
|
281
|
+
return (await this.listModels()).image;
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
// Same as the language listing: discovery never throws.
|
|
285
|
+
return [];
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
// ── Converse request building ──────────────────────────────────────
|
|
289
|
+
async mediaBytes(source, signal) {
|
|
290
|
+
const hasData = source.data !== undefined &&
|
|
291
|
+
(source.data instanceof Uint8Array ? source.data.length > 0 : source.data.length > 0);
|
|
292
|
+
if (hasData) {
|
|
293
|
+
const decoded = toBytes(source.data);
|
|
294
|
+
return { bytes: decoded.bytes, mime: source.mimeType ?? decoded.mime };
|
|
295
|
+
}
|
|
296
|
+
const uri = source.uri ?? "";
|
|
297
|
+
if (uri.startsWith("data:")) {
|
|
298
|
+
const decoded = toBytes(uri);
|
|
299
|
+
return { bytes: decoded.bytes, mime: source.mimeType ?? decoded.mime };
|
|
300
|
+
}
|
|
301
|
+
if (/^https?:\/\//i.test(uri)) {
|
|
302
|
+
const response = await safeFetch(uri, signal ? { signal } : undefined, 5, this._fetch);
|
|
303
|
+
if (!response.ok)
|
|
304
|
+
throw new Error(`Failed to fetch ${uri}: ${response.status}`);
|
|
305
|
+
return {
|
|
306
|
+
bytes: new Uint8Array(await response.arrayBuffer()),
|
|
307
|
+
mime: source.mimeType ?? response.headers.get("content-type")?.split(";")[0] ?? undefined
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
throw new Error("Bedrock media content needs inline data, a data: URI, or an https URL");
|
|
311
|
+
}
|
|
312
|
+
async convertParts(content, signal) {
|
|
313
|
+
if (content == null)
|
|
314
|
+
return [];
|
|
315
|
+
if (isString(content))
|
|
316
|
+
return content ? [{ text: content }] : [];
|
|
317
|
+
const blocks = [];
|
|
318
|
+
let documents = 0;
|
|
319
|
+
for (const part of content) {
|
|
320
|
+
if (part.type === "text") {
|
|
321
|
+
if (part.text)
|
|
322
|
+
blocks.push({ text: part.text });
|
|
323
|
+
}
|
|
324
|
+
else if (part.type === "image_url") {
|
|
325
|
+
const { bytes, mime } = await this.mediaBytes(part.image, signal);
|
|
326
|
+
const format = IMAGE_FORMATS[sniffImageMime(bytes) ?? mime ?? ""];
|
|
327
|
+
if (!format)
|
|
328
|
+
throw new Error("Bedrock accepts PNG, JPEG, GIF or WebP images");
|
|
329
|
+
blocks.push({ image: { format, source: { bytes: Buffer.from(bytes).toString("base64") } } });
|
|
330
|
+
}
|
|
331
|
+
else if (part.type === "document") {
|
|
332
|
+
const { bytes, mime } = await this.mediaBytes(part.document, signal);
|
|
333
|
+
const format = DOCUMENT_FORMATS[mime ?? ""];
|
|
334
|
+
if (!format)
|
|
335
|
+
throw new Error(`Bedrock does not accept documents of type ${mime ?? "unknown"}`);
|
|
336
|
+
blocks.push({
|
|
337
|
+
document: {
|
|
338
|
+
format,
|
|
339
|
+
name: documentName(part.document.title, documents++),
|
|
340
|
+
source: { bytes: Buffer.from(bytes).toString("base64") }
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
throw new Error(`Bedrock chat does not accept ${part.type} content`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return blocks;
|
|
349
|
+
}
|
|
350
|
+
async buildMessages(messages, signal) {
|
|
351
|
+
const system = [];
|
|
352
|
+
const out = [];
|
|
353
|
+
const push = (role, content) => {
|
|
354
|
+
if (content.length === 0)
|
|
355
|
+
return;
|
|
356
|
+
const last = out.at(-1);
|
|
357
|
+
// Converse requires alternating roles: parallel tool results and
|
|
358
|
+
// back-to-back user turns merge into one message.
|
|
359
|
+
if (last?.role === role)
|
|
360
|
+
last.content.push(...content);
|
|
361
|
+
else
|
|
362
|
+
out.push({ role, content });
|
|
363
|
+
};
|
|
364
|
+
for (const message of messages) {
|
|
365
|
+
if (message.role === "system") {
|
|
366
|
+
const text = textOf(message.content);
|
|
367
|
+
if (text)
|
|
368
|
+
system.push({ text });
|
|
369
|
+
}
|
|
370
|
+
else if (message.role === "user") {
|
|
371
|
+
push("user", await this.convertParts(message.content, signal));
|
|
372
|
+
}
|
|
373
|
+
else if (message.role === "assistant") {
|
|
374
|
+
const blocks = [];
|
|
375
|
+
if (message.toolCalls?.length) {
|
|
376
|
+
blocks.push(...reasoningBlocks(message._anthropicThinkingBlocks));
|
|
377
|
+
}
|
|
378
|
+
const text = textOf(message.content);
|
|
379
|
+
if (text)
|
|
380
|
+
blocks.push({ text });
|
|
381
|
+
for (const call of message.toolCalls ?? []) {
|
|
382
|
+
blocks.push({ toolUse: { toolUseId: call.id, name: call.name, input: call.args } });
|
|
383
|
+
}
|
|
384
|
+
push("assistant", blocks);
|
|
385
|
+
}
|
|
386
|
+
else if (message.role === "tool") {
|
|
387
|
+
const parts = isString(message.content)
|
|
388
|
+
? [{ text: message.content || "(empty)" }]
|
|
389
|
+
: await this.convertParts(message.content, signal);
|
|
390
|
+
push("user", [
|
|
391
|
+
{
|
|
392
|
+
toolResult: {
|
|
393
|
+
toolUseId: message.toolCallId ?? "",
|
|
394
|
+
content: parts.length > 0 ? parts : [{ text: "(empty)" }],
|
|
395
|
+
status: message.isError ? "error" : "success"
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
]);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return { system, messages: out };
|
|
402
|
+
}
|
|
403
|
+
toolConfig(tools, toolChoice) {
|
|
404
|
+
const specs = (tools ?? []).filter((tool) => tool.type !== "code_interpreter");
|
|
405
|
+
if (specs.length === 0)
|
|
406
|
+
return undefined;
|
|
407
|
+
let choice = { auto: {} };
|
|
408
|
+
if (toolChoice === "any")
|
|
409
|
+
choice = { any: {} };
|
|
410
|
+
else if (toolChoice && specs.some((tool) => tool.name === toolChoice)) {
|
|
411
|
+
choice = { tool: { name: toolChoice } };
|
|
412
|
+
}
|
|
413
|
+
return {
|
|
414
|
+
tools: specs.map((tool) => ({
|
|
415
|
+
toolSpec: {
|
|
416
|
+
name: tool.name,
|
|
417
|
+
description: tool.description || tool.name,
|
|
418
|
+
inputSchema: { json: tool.inputSchema ?? { type: "object", properties: {} } }
|
|
419
|
+
}
|
|
420
|
+
})),
|
|
421
|
+
toolChoice: choice
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
async buildRequest(args) {
|
|
425
|
+
const { system, messages } = await this.buildMessages(args.messages, args.signal);
|
|
426
|
+
const inferenceConfig = {
|
|
427
|
+
maxTokens: args.maxTokens ?? DEFAULT_MAX_TOKENS
|
|
428
|
+
};
|
|
429
|
+
const additional = thinkingFields(args.model, args.thinking);
|
|
430
|
+
if (additional) {
|
|
431
|
+
if (args.toolChoice && args.toolChoice !== "auto" && args.tools?.some((tool) => tool.type !== "code_interpreter" && (args.toolChoice === "any" || args.toolChoice === tool.name))) {
|
|
432
|
+
throw new Error("Bedrock thinking does not support forced tool selection");
|
|
433
|
+
}
|
|
434
|
+
if (args.thinking?.type === "manual") {
|
|
435
|
+
inferenceConfig.maxTokens = Math.max(args.maxTokens ?? DEFAULT_MAX_TOKENS, args.thinking.budgetTokens + 1);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
if (args.temperature !== undefined)
|
|
440
|
+
inferenceConfig.temperature = args.temperature;
|
|
441
|
+
if (args.topP !== undefined)
|
|
442
|
+
inferenceConfig.topP = args.topP;
|
|
443
|
+
}
|
|
444
|
+
const request = { messages, inferenceConfig };
|
|
445
|
+
if (system.length > 0)
|
|
446
|
+
request.system = system;
|
|
447
|
+
const toolConfig = this.toolConfig(args.tools, args.toolChoice);
|
|
448
|
+
if (toolConfig)
|
|
449
|
+
request.toolConfig = toolConfig;
|
|
450
|
+
if (additional)
|
|
451
|
+
request.additionalModelRequestFields = additional;
|
|
452
|
+
this.recordRequestPayload(request);
|
|
453
|
+
return request;
|
|
454
|
+
}
|
|
455
|
+
recordUsage(model, usage) {
|
|
456
|
+
if (!usage)
|
|
457
|
+
return;
|
|
458
|
+
const cachedTokens = usage.cacheReadInputTokens ?? 0;
|
|
459
|
+
const cacheWriteTokens = usage.cacheWriteInputTokens ?? 0;
|
|
460
|
+
// Converse reports cache reads and writes apart from inputTokens; the
|
|
461
|
+
// cost calculator expects inputTokens to include them.
|
|
462
|
+
this.trackUsage(model, {
|
|
463
|
+
inputTokens: (usage.inputTokens ?? 0) + cachedTokens + cacheWriteTokens,
|
|
464
|
+
outputTokens: usage.outputTokens ?? 0,
|
|
465
|
+
cachedTokens,
|
|
466
|
+
cacheWriteTokens
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
// ── Chat ───────────────────────────────────────────────────────────
|
|
470
|
+
async generateMessage(args) {
|
|
471
|
+
const request = await this.buildRequest(args);
|
|
472
|
+
const response = await this.request("bedrock-runtime", "POST", this.modelPath(args.model, "converse"), request, args.signal);
|
|
473
|
+
const body = (await response.json());
|
|
474
|
+
this.recordUsage(args.model, body.usage);
|
|
475
|
+
const content = body.output?.message?.content ?? [];
|
|
476
|
+
const text = content
|
|
477
|
+
.map((block) => (isString(block.text) ? block.text : ""))
|
|
478
|
+
.join("");
|
|
479
|
+
const thinking = thinkingBlocksOf(content);
|
|
480
|
+
const toolCalls = [];
|
|
481
|
+
for (const block of content) {
|
|
482
|
+
const use = block.toolUse;
|
|
483
|
+
if (!isRecord(use))
|
|
484
|
+
continue;
|
|
485
|
+
toolCalls.push({
|
|
486
|
+
id: String(use.toolUseId ?? ""),
|
|
487
|
+
name: String(use.name ?? ""),
|
|
488
|
+
args: isRecord(use.input) ? use.input : {},
|
|
489
|
+
_anthropicThinkingBlocks: thinking.length > 0 ? thinking : undefined
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
const message = { role: "assistant", content: text };
|
|
493
|
+
if (toolCalls.length > 0)
|
|
494
|
+
message.toolCalls = toolCalls;
|
|
495
|
+
if (thinking.length > 0)
|
|
496
|
+
message._anthropicThinkingBlocks = thinking;
|
|
497
|
+
return message;
|
|
498
|
+
}
|
|
499
|
+
async *generateMessages(args) {
|
|
500
|
+
const request = await this.buildRequest(args);
|
|
501
|
+
const response = await this.request("bedrock-runtime", "POST", this.modelPath(args.model, "converse-stream"), request, args.signal);
|
|
502
|
+
if (!response.body)
|
|
503
|
+
throw new Error("Bedrock returned an empty stream");
|
|
504
|
+
const tools = new Map();
|
|
505
|
+
const thinkingByIndex = new Map();
|
|
506
|
+
const toolCalls = [];
|
|
507
|
+
for await (const event of decodeEventStream(response.body)) {
|
|
508
|
+
const payload = event.payload.length > 0
|
|
509
|
+
? JSON.parse(new TextDecoder().decode(event.payload))
|
|
510
|
+
: {};
|
|
511
|
+
if (event.headers[":message-type"] === "exception") {
|
|
512
|
+
const type = event.headers[":exception-type"] ?? "exception";
|
|
513
|
+
const message = isRecord(payload) && isString(payload.message) ? payload.message : type;
|
|
514
|
+
throw new Error(`Bedrock error ${STREAM_EXCEPTION_STATUS[type] ?? 500}: ${message}`);
|
|
515
|
+
}
|
|
516
|
+
if (!isRecord(payload))
|
|
517
|
+
continue;
|
|
518
|
+
const index = Number(payload.contentBlockIndex ?? 0);
|
|
519
|
+
switch (event.headers[":event-type"]) {
|
|
520
|
+
case "contentBlockStart": {
|
|
521
|
+
const start = payload.start;
|
|
522
|
+
if (isRecord(start) && isRecord(start.toolUse)) {
|
|
523
|
+
tools.set(index, {
|
|
524
|
+
id: String(start.toolUse.toolUseId ?? ""),
|
|
525
|
+
name: String(start.toolUse.name ?? ""),
|
|
526
|
+
input: ""
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
break;
|
|
530
|
+
}
|
|
531
|
+
case "contentBlockDelta": {
|
|
532
|
+
const delta = payload.delta;
|
|
533
|
+
if (!isRecord(delta))
|
|
534
|
+
break;
|
|
535
|
+
if (isString(delta.text)) {
|
|
536
|
+
if (delta.text)
|
|
537
|
+
yield { type: "chunk", content: delta.text, done: false };
|
|
538
|
+
}
|
|
539
|
+
else if (isRecord(delta.toolUse) && isString(delta.toolUse.input)) {
|
|
540
|
+
const tool = tools.get(index);
|
|
541
|
+
if (tool)
|
|
542
|
+
tool.input += delta.toolUse.input;
|
|
543
|
+
}
|
|
544
|
+
else if (isRecord(delta.reasoningContent)) {
|
|
545
|
+
const reasoning = delta.reasoningContent;
|
|
546
|
+
let last = thinkingByIndex.get(index);
|
|
547
|
+
if (isString(reasoning.text)) {
|
|
548
|
+
if (last?.type !== "thinking") {
|
|
549
|
+
last = { type: "thinking", thinking: "", signature: "" };
|
|
550
|
+
thinkingByIndex.set(index, last);
|
|
551
|
+
}
|
|
552
|
+
last.thinking += reasoning.text;
|
|
553
|
+
yield { type: "chunk", content: reasoning.text, done: false, thinking: true };
|
|
554
|
+
}
|
|
555
|
+
else if (isString(reasoning.signature) && last?.type === "thinking") {
|
|
556
|
+
last.signature += reasoning.signature;
|
|
557
|
+
}
|
|
558
|
+
else if (isString(reasoning.redactedContent)) {
|
|
559
|
+
thinkingByIndex.set(index, { type: "redacted_thinking", data: reasoning.redactedContent });
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
break;
|
|
563
|
+
}
|
|
564
|
+
case "contentBlockStop": {
|
|
565
|
+
const tool = tools.get(index);
|
|
566
|
+
if (!tool)
|
|
567
|
+
break;
|
|
568
|
+
tools.delete(index);
|
|
569
|
+
let input;
|
|
570
|
+
try {
|
|
571
|
+
input = tool.input ? JSON.parse(tool.input) : {};
|
|
572
|
+
}
|
|
573
|
+
catch {
|
|
574
|
+
throw new Error("Bedrock returned incomplete tool input JSON");
|
|
575
|
+
}
|
|
576
|
+
toolCalls.push({
|
|
577
|
+
id: tool.id,
|
|
578
|
+
name: tool.name,
|
|
579
|
+
args: isRecord(input) ? input : {}
|
|
580
|
+
});
|
|
581
|
+
break;
|
|
582
|
+
}
|
|
583
|
+
case "metadata":
|
|
584
|
+
this.recordUsage(args.model, payload.usage);
|
|
585
|
+
break;
|
|
586
|
+
default:
|
|
587
|
+
break;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
const thinking = [...thinkingByIndex.values()];
|
|
591
|
+
for (const call of toolCalls) {
|
|
592
|
+
if (thinking.length > 0)
|
|
593
|
+
call._anthropicThinkingBlocks = [...thinking];
|
|
594
|
+
yield call;
|
|
595
|
+
}
|
|
596
|
+
yield { type: "chunk", content: "", done: true };
|
|
597
|
+
}
|
|
598
|
+
// ── Image generation ───────────────────────────────────────────────
|
|
599
|
+
async textToImage(params) {
|
|
600
|
+
if (!params.prompt)
|
|
601
|
+
throw new Error("The input prompt cannot be empty.");
|
|
602
|
+
const modelId = params.model.id;
|
|
603
|
+
const family = imageFamily(modelId);
|
|
604
|
+
if (!family)
|
|
605
|
+
throw new Error(`Bedrock image generation does not support ${modelId}`);
|
|
606
|
+
let body;
|
|
607
|
+
if (family === "amazon") {
|
|
608
|
+
const config = { numberOfImages: 1 };
|
|
609
|
+
if (params.width && params.height) {
|
|
610
|
+
config.width = Math.round(params.width / 16) * 16;
|
|
611
|
+
config.height = Math.round(params.height / 16) * 16;
|
|
612
|
+
}
|
|
613
|
+
if (params.guidanceScale != null)
|
|
614
|
+
config.cfgScale = params.guidanceScale;
|
|
615
|
+
if (params.seed != null && params.seed >= 0)
|
|
616
|
+
config.seed = params.seed;
|
|
617
|
+
if (params.quality === "premium" || params.quality === "standard") {
|
|
618
|
+
config.quality = params.quality;
|
|
619
|
+
}
|
|
620
|
+
const textParams = { text: params.prompt };
|
|
621
|
+
if (params.negativePrompt)
|
|
622
|
+
textParams.negativeText = params.negativePrompt;
|
|
623
|
+
body = { taskType: "TEXT_IMAGE", textToImageParams: textParams, imageGenerationConfig: config };
|
|
624
|
+
}
|
|
625
|
+
else {
|
|
626
|
+
body = { prompt: params.prompt, output_format: "png" };
|
|
627
|
+
if (params.negativePrompt)
|
|
628
|
+
body.negative_prompt = params.negativePrompt;
|
|
629
|
+
if (params.aspectRatio)
|
|
630
|
+
body.aspect_ratio = params.aspectRatio;
|
|
631
|
+
if (params.seed != null && params.seed >= 0)
|
|
632
|
+
body.seed = params.seed;
|
|
633
|
+
}
|
|
634
|
+
const response = await this.request("bedrock-runtime", "POST", this.modelPath(modelId, "invoke"), body, params.signal);
|
|
635
|
+
const result = (await response.json());
|
|
636
|
+
if (result.error)
|
|
637
|
+
throw new Error(`Bedrock image generation failed: ${result.error}`);
|
|
638
|
+
const filtered = result.finish_reasons?.find((reason) => reason);
|
|
639
|
+
if (filtered)
|
|
640
|
+
throw new Error(`Bedrock image generation was filtered: ${filtered}`);
|
|
641
|
+
const image = result.images?.[0];
|
|
642
|
+
if (!image)
|
|
643
|
+
throw new Error("Bedrock image generation returned no image data.");
|
|
644
|
+
this.trackUsage(modelId, { imageCount: 1 });
|
|
645
|
+
return new Uint8Array(Buffer.from(image, "base64"));
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
//# sourceMappingURL=bedrock-provider.js.map
|