@open-multi-agent/core 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +373 -0
- package/dist/agent/agent.d.ts +153 -0
- package/dist/agent/agent.d.ts.map +1 -0
- package/dist/agent/agent.js +559 -0
- package/dist/agent/agent.js.map +1 -0
- package/dist/agent/loop-detector.d.ts +39 -0
- package/dist/agent/loop-detector.d.ts.map +1 -0
- package/dist/agent/loop-detector.js +122 -0
- package/dist/agent/loop-detector.js.map +1 -0
- package/dist/agent/pool.d.ts +158 -0
- package/dist/agent/pool.d.ts.map +1 -0
- package/dist/agent/pool.js +320 -0
- package/dist/agent/pool.js.map +1 -0
- package/dist/agent/runner.d.ts +242 -0
- package/dist/agent/runner.d.ts.map +1 -0
- package/dist/agent/runner.js +943 -0
- package/dist/agent/runner.js.map +1 -0
- package/dist/agent/structured-output.d.ts +33 -0
- package/dist/agent/structured-output.d.ts.map +1 -0
- package/dist/agent/structured-output.js +116 -0
- package/dist/agent/structured-output.js.map +1 -0
- package/dist/cli/oma.d.ts +30 -0
- package/dist/cli/oma.d.ts.map +1 -0
- package/dist/cli/oma.js +433 -0
- package/dist/cli/oma.js.map +1 -0
- package/dist/dashboard/layout-tasks.d.ts +23 -0
- package/dist/dashboard/layout-tasks.d.ts.map +1 -0
- package/dist/dashboard/layout-tasks.js +79 -0
- package/dist/dashboard/layout-tasks.js.map +1 -0
- package/dist/dashboard/render-team-run-dashboard.d.ts +11 -0
- package/dist/dashboard/render-team-run-dashboard.d.ts.map +1 -0
- package/dist/dashboard/render-team-run-dashboard.js +456 -0
- package/dist/dashboard/render-team-run-dashboard.js.map +1 -0
- package/dist/errors.d.ts +14 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +20 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +79 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +92 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/adapter.d.ts +54 -0
- package/dist/llm/adapter.d.ts.map +1 -0
- package/dist/llm/adapter.js +101 -0
- package/dist/llm/adapter.js.map +1 -0
- package/dist/llm/anthropic.d.ts +57 -0
- package/dist/llm/anthropic.d.ts.map +1 -0
- package/dist/llm/anthropic.js +432 -0
- package/dist/llm/anthropic.js.map +1 -0
- package/dist/llm/azure-openai.d.ts +74 -0
- package/dist/llm/azure-openai.d.ts.map +1 -0
- package/dist/llm/azure-openai.js +267 -0
- package/dist/llm/azure-openai.js.map +1 -0
- package/dist/llm/bedrock.d.ts +41 -0
- package/dist/llm/bedrock.d.ts.map +1 -0
- package/dist/llm/bedrock.js +345 -0
- package/dist/llm/bedrock.js.map +1 -0
- package/dist/llm/copilot.d.ts +92 -0
- package/dist/llm/copilot.d.ts.map +1 -0
- package/dist/llm/copilot.js +433 -0
- package/dist/llm/copilot.js.map +1 -0
- package/dist/llm/deepseek.d.ts +21 -0
- package/dist/llm/deepseek.d.ts.map +1 -0
- package/dist/llm/deepseek.js +24 -0
- package/dist/llm/deepseek.js.map +1 -0
- package/dist/llm/gemini.d.ts +65 -0
- package/dist/llm/gemini.d.ts.map +1 -0
- package/dist/llm/gemini.js +427 -0
- package/dist/llm/gemini.js.map +1 -0
- package/dist/llm/grok.d.ts +21 -0
- package/dist/llm/grok.d.ts.map +1 -0
- package/dist/llm/grok.js +24 -0
- package/dist/llm/grok.js.map +1 -0
- package/dist/llm/minimax.d.ts +21 -0
- package/dist/llm/minimax.d.ts.map +1 -0
- package/dist/llm/minimax.js +24 -0
- package/dist/llm/minimax.js.map +1 -0
- package/dist/llm/openai-common.d.ts +65 -0
- package/dist/llm/openai-common.d.ts.map +1 -0
- package/dist/llm/openai-common.js +286 -0
- package/dist/llm/openai-common.js.map +1 -0
- package/dist/llm/openai.d.ts +63 -0
- package/dist/llm/openai.d.ts.map +1 -0
- package/dist/llm/openai.js +256 -0
- package/dist/llm/openai.js.map +1 -0
- package/dist/llm/qiniu.d.ts +21 -0
- package/dist/llm/qiniu.d.ts.map +1 -0
- package/dist/llm/qiniu.js +24 -0
- package/dist/llm/qiniu.js.map +1 -0
- package/dist/mcp.d.ts +3 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +2 -0
- package/dist/mcp.js.map +1 -0
- package/dist/memory/shared.d.ts +162 -0
- package/dist/memory/shared.d.ts.map +1 -0
- package/dist/memory/shared.js +294 -0
- package/dist/memory/shared.js.map +1 -0
- package/dist/memory/store.d.ts +72 -0
- package/dist/memory/store.d.ts.map +1 -0
- package/dist/memory/store.js +121 -0
- package/dist/memory/store.js.map +1 -0
- package/dist/orchestrator/orchestrator.d.ts +245 -0
- package/dist/orchestrator/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator/orchestrator.js +1400 -0
- package/dist/orchestrator/orchestrator.js.map +1 -0
- package/dist/orchestrator/scheduler.d.ts +112 -0
- package/dist/orchestrator/scheduler.d.ts.map +1 -0
- package/dist/orchestrator/scheduler.js +256 -0
- package/dist/orchestrator/scheduler.js.map +1 -0
- package/dist/task/queue.d.ts +191 -0
- package/dist/task/queue.d.ts.map +1 -0
- package/dist/task/queue.js +408 -0
- package/dist/task/queue.js.map +1 -0
- package/dist/task/task.d.ts +90 -0
- package/dist/task/task.d.ts.map +1 -0
- package/dist/task/task.js +206 -0
- package/dist/task/task.js.map +1 -0
- package/dist/team/messaging.d.ts +106 -0
- package/dist/team/messaging.d.ts.map +1 -0
- package/dist/team/messaging.js +183 -0
- package/dist/team/messaging.js.map +1 -0
- package/dist/team/team.d.ts +141 -0
- package/dist/team/team.d.ts.map +1 -0
- package/dist/team/team.js +293 -0
- package/dist/team/team.js.map +1 -0
- package/dist/tool/built-in/bash.d.ts +12 -0
- package/dist/tool/built-in/bash.d.ts.map +1 -0
- package/dist/tool/built-in/bash.js +133 -0
- package/dist/tool/built-in/bash.js.map +1 -0
- package/dist/tool/built-in/delegate.d.ts +29 -0
- package/dist/tool/built-in/delegate.d.ts.map +1 -0
- package/dist/tool/built-in/delegate.js +92 -0
- package/dist/tool/built-in/delegate.js.map +1 -0
- package/dist/tool/built-in/file-edit.d.ts +14 -0
- package/dist/tool/built-in/file-edit.d.ts.map +1 -0
- package/dist/tool/built-in/file-edit.js +130 -0
- package/dist/tool/built-in/file-edit.js.map +1 -0
- package/dist/tool/built-in/file-read.d.ts +12 -0
- package/dist/tool/built-in/file-read.d.ts.map +1 -0
- package/dist/tool/built-in/file-read.js +82 -0
- package/dist/tool/built-in/file-read.js.map +1 -0
- package/dist/tool/built-in/file-write.d.ts +11 -0
- package/dist/tool/built-in/file-write.d.ts.map +1 -0
- package/dist/tool/built-in/file-write.js +70 -0
- package/dist/tool/built-in/file-write.js.map +1 -0
- package/dist/tool/built-in/fs-walk.d.ts +23 -0
- package/dist/tool/built-in/fs-walk.d.ts.map +1 -0
- package/dist/tool/built-in/fs-walk.js +78 -0
- package/dist/tool/built-in/fs-walk.js.map +1 -0
- package/dist/tool/built-in/glob.d.ts +12 -0
- package/dist/tool/built-in/glob.d.ts.map +1 -0
- package/dist/tool/built-in/glob.js +82 -0
- package/dist/tool/built-in/glob.js.map +1 -0
- package/dist/tool/built-in/grep.d.ts +15 -0
- package/dist/tool/built-in/grep.d.ts.map +1 -0
- package/dist/tool/built-in/grep.js +218 -0
- package/dist/tool/built-in/grep.js.map +1 -0
- package/dist/tool/built-in/index.d.ts +48 -0
- package/dist/tool/built-in/index.d.ts.map +1 -0
- package/dist/tool/built-in/index.js +56 -0
- package/dist/tool/built-in/index.js.map +1 -0
- package/dist/tool/executor.d.ts +100 -0
- package/dist/tool/executor.d.ts.map +1 -0
- package/dist/tool/executor.js +184 -0
- package/dist/tool/executor.js.map +1 -0
- package/dist/tool/framework.d.ts +167 -0
- package/dist/tool/framework.d.ts.map +1 -0
- package/dist/tool/framework.js +402 -0
- package/dist/tool/framework.js.map +1 -0
- package/dist/tool/mcp.d.ts +31 -0
- package/dist/tool/mcp.d.ts.map +1 -0
- package/dist/tool/mcp.js +175 -0
- package/dist/tool/mcp.js.map +1 -0
- package/dist/tool/text-tool-extractor.d.ts +32 -0
- package/dist/tool/text-tool-extractor.d.ts.map +1 -0
- package/dist/tool/text-tool-extractor.js +195 -0
- package/dist/tool/text-tool-extractor.js.map +1 -0
- package/dist/types.d.ts +916 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/keywords.d.ts +18 -0
- package/dist/utils/keywords.d.ts.map +1 -0
- package/dist/utils/keywords.js +32 -0
- package/dist/utils/keywords.js.map +1 -0
- package/dist/utils/semaphore.d.ts +49 -0
- package/dist/utils/semaphore.d.ts.map +1 -0
- package/dist/utils/semaphore.js +89 -0
- package/dist/utils/semaphore.js.map +1 -0
- package/dist/utils/tokens.d.ts +7 -0
- package/dist/utils/tokens.d.ts.map +1 -0
- package/dist/utils/tokens.js +30 -0
- package/dist/utils/tokens.js.map +1 -0
- package/dist/utils/trace.d.ts +12 -0
- package/dist/utils/trace.d.ts.map +1 -0
- package/dist/utils/trace.js +30 -0
- package/dist/utils/trace.js.map +1 -0
- package/docs/DECISIONS.md +49 -0
- package/docs/cli.md +265 -0
- package/docs/context-management.md +24 -0
- package/docs/featured-partner.md +28 -0
- package/docs/observability.md +56 -0
- package/docs/providers.md +78 -0
- package/docs/shared-memory.md +27 -0
- package/docs/tool-configuration.md +152 -0
- package/package.json +96 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Azure OpenAI adapter implementing {@link LLMAdapter}.
|
|
3
|
+
*
|
|
4
|
+
* Azure OpenAI uses regional deployment endpoints and API versioning that differ
|
|
5
|
+
* from standard OpenAI:
|
|
6
|
+
*
|
|
7
|
+
* - Endpoint: `https://{resource-name}.openai.azure.com`
|
|
8
|
+
* - API version: Query parameter (e.g., `?api-version=2024-10-21`)
|
|
9
|
+
* - Model/Deployment: Users deploy models with custom names; the `model` field
|
|
10
|
+
* in agent config should contain the Azure deployment name, not the underlying
|
|
11
|
+
* model name (e.g., `model: 'my-gpt4-deployment'`)
|
|
12
|
+
*
|
|
13
|
+
* The OpenAI SDK provides an `AzureOpenAI` client class that handles these
|
|
14
|
+
* Azure-specific requirements. This adapter uses that client while reusing all
|
|
15
|
+
* message conversion logic from `openai-common.ts`.
|
|
16
|
+
*
|
|
17
|
+
* Environment variable resolution order:
|
|
18
|
+
* 1. Constructor arguments
|
|
19
|
+
* 2. `AZURE_OPENAI_API_KEY` environment variable
|
|
20
|
+
* 3. `AZURE_OPENAI_ENDPOINT` environment variable
|
|
21
|
+
* 4. `AZURE_OPENAI_API_VERSION` environment variable (defaults to '2024-10-21')
|
|
22
|
+
* 5. `AZURE_OPENAI_DEPLOYMENT` as an optional fallback when `model` is blank
|
|
23
|
+
*
|
|
24
|
+
* Note: Azure introduced a next-generation v1 API (August 2025) that uses the standard
|
|
25
|
+
* OpenAI() client with baseURL set to `{endpoint}/openai/v1/` and requires no api-version.
|
|
26
|
+
* That path is not yet supported by this adapter. To use it, pass `provider: 'openai'`
|
|
27
|
+
* with `baseURL: 'https://{resource}.openai.azure.com/openai/v1/'` in your agent config.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* import { AzureOpenAIAdapter } from './azure-openai.js'
|
|
32
|
+
*
|
|
33
|
+
* const adapter = new AzureOpenAIAdapter()
|
|
34
|
+
* const response = await adapter.chat(messages, {
|
|
35
|
+
* model: 'my-gpt4-deployment', // Azure deployment name, not 'gpt-4'
|
|
36
|
+
* maxTokens: 1024,
|
|
37
|
+
* })
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
import { AzureOpenAI } from 'openai';
|
|
41
|
+
import { toOpenAITool, fromOpenAICompletion, normalizeFinishReason, buildOpenAIMessageList, } from './openai-common.js';
|
|
42
|
+
import { extractToolCallsFromText } from '../tool/text-tool-extractor.js';
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// Adapter implementation
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
const DEFAULT_AZURE_OPENAI_API_VERSION = '2024-10-21';
|
|
47
|
+
function resolveAzureDeploymentName(model) {
|
|
48
|
+
const explicitModel = model.trim();
|
|
49
|
+
if (explicitModel.length > 0)
|
|
50
|
+
return explicitModel;
|
|
51
|
+
const fallbackDeployment = process.env['AZURE_OPENAI_DEPLOYMENT']?.trim();
|
|
52
|
+
if (fallbackDeployment !== undefined && fallbackDeployment.length > 0) {
|
|
53
|
+
return fallbackDeployment;
|
|
54
|
+
}
|
|
55
|
+
throw new Error('Azure OpenAI deployment is required. Set agent model to your deployment name, or set AZURE_OPENAI_DEPLOYMENT.');
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* LLM adapter backed by Azure OpenAI Chat Completions API.
|
|
59
|
+
*
|
|
60
|
+
* Thread-safe — a single instance may be shared across concurrent agent runs.
|
|
61
|
+
*/
|
|
62
|
+
export class AzureOpenAIAdapter {
|
|
63
|
+
name = 'azure-openai';
|
|
64
|
+
#client;
|
|
65
|
+
/**
|
|
66
|
+
* @param apiKey - Azure OpenAI API key (falls back to AZURE_OPENAI_API_KEY env var)
|
|
67
|
+
* @param endpoint - Azure endpoint URL (falls back to AZURE_OPENAI_ENDPOINT env var)
|
|
68
|
+
* @param apiVersion - API version string (falls back to AZURE_OPENAI_API_VERSION, defaults to '2024-10-21')
|
|
69
|
+
*/
|
|
70
|
+
constructor(apiKey, endpoint, apiVersion) {
|
|
71
|
+
this.#client = new AzureOpenAI({
|
|
72
|
+
apiKey: apiKey ?? process.env['AZURE_OPENAI_API_KEY'],
|
|
73
|
+
endpoint: endpoint ?? process.env['AZURE_OPENAI_ENDPOINT'],
|
|
74
|
+
apiVersion: apiVersion ?? process.env['AZURE_OPENAI_API_VERSION'] ?? DEFAULT_AZURE_OPENAI_API_VERSION,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
// -------------------------------------------------------------------------
|
|
78
|
+
// chat()
|
|
79
|
+
// -------------------------------------------------------------------------
|
|
80
|
+
/**
|
|
81
|
+
* Send a synchronous (non-streaming) chat request and return the complete
|
|
82
|
+
* {@link LLMResponse}.
|
|
83
|
+
*
|
|
84
|
+
* Throws an `AzureOpenAI.APIError` on non-2xx responses. Callers should catch and
|
|
85
|
+
* handle these (e.g. rate limits, context length exceeded, deployment not found).
|
|
86
|
+
*/
|
|
87
|
+
async chat(messages, options) {
|
|
88
|
+
const deploymentName = resolveAzureDeploymentName(options.model);
|
|
89
|
+
const openAIMessages = buildOpenAIMessageList(messages, options.systemPrompt);
|
|
90
|
+
const completion = await this.#client.chat.completions.create({
|
|
91
|
+
// Sampling params first so extraBody can override them. Structural
|
|
92
|
+
// fields (model/messages/tools/stream) come after extraBody so users
|
|
93
|
+
// cannot accidentally clobber them via extraBody.
|
|
94
|
+
// `top_k` / `min_p` deliberately omitted — vLLM-only, not accepted
|
|
95
|
+
// by Azure OpenAI's hosted endpoint.
|
|
96
|
+
max_tokens: options.maxTokens,
|
|
97
|
+
temperature: options.temperature,
|
|
98
|
+
frequency_penalty: options.frequencyPenalty,
|
|
99
|
+
presence_penalty: options.presencePenalty,
|
|
100
|
+
top_p: options.topP,
|
|
101
|
+
parallel_tool_calls: options.parallelToolCalls,
|
|
102
|
+
reasoning_effort: options.thinking?.effort,
|
|
103
|
+
...options.extraBody,
|
|
104
|
+
model: deploymentName,
|
|
105
|
+
messages: openAIMessages,
|
|
106
|
+
tools: options.tools ? options.tools.map(toOpenAITool) : undefined,
|
|
107
|
+
stream: false,
|
|
108
|
+
}, {
|
|
109
|
+
signal: options.abortSignal,
|
|
110
|
+
});
|
|
111
|
+
const toolNames = options.tools?.map(t => t.name);
|
|
112
|
+
return fromOpenAICompletion(completion, toolNames);
|
|
113
|
+
}
|
|
114
|
+
// -------------------------------------------------------------------------
|
|
115
|
+
// stream()
|
|
116
|
+
// -------------------------------------------------------------------------
|
|
117
|
+
/**
|
|
118
|
+
* Send a streaming chat request and yield {@link StreamEvent}s incrementally.
|
|
119
|
+
*
|
|
120
|
+
* Sequence guarantees match {@link OpenAIAdapter.stream}:
|
|
121
|
+
* - Zero or more `text` events
|
|
122
|
+
* - Zero or more `tool_use` events (emitted once per tool call, after
|
|
123
|
+
* arguments have been fully assembled)
|
|
124
|
+
* - Exactly one terminal event: `done` or `error`
|
|
125
|
+
*/
|
|
126
|
+
async *stream(messages, options) {
|
|
127
|
+
const deploymentName = resolveAzureDeploymentName(options.model);
|
|
128
|
+
const openAIMessages = buildOpenAIMessageList(messages, options.systemPrompt);
|
|
129
|
+
// We request usage in the final chunk so we can include it in the `done` event.
|
|
130
|
+
const streamResponse = await this.#client.chat.completions.create({
|
|
131
|
+
// See chat() above for the field-ordering rationale and the
|
|
132
|
+
// `top_k` / `min_p` exclusion.
|
|
133
|
+
max_tokens: options.maxTokens,
|
|
134
|
+
temperature: options.temperature,
|
|
135
|
+
frequency_penalty: options.frequencyPenalty,
|
|
136
|
+
presence_penalty: options.presencePenalty,
|
|
137
|
+
top_p: options.topP,
|
|
138
|
+
parallel_tool_calls: options.parallelToolCalls,
|
|
139
|
+
reasoning_effort: options.thinking?.effort,
|
|
140
|
+
...options.extraBody,
|
|
141
|
+
model: deploymentName,
|
|
142
|
+
messages: openAIMessages,
|
|
143
|
+
tools: options.tools ? options.tools.map(toOpenAITool) : undefined,
|
|
144
|
+
stream: true,
|
|
145
|
+
stream_options: { include_usage: true },
|
|
146
|
+
}, {
|
|
147
|
+
signal: options.abortSignal,
|
|
148
|
+
});
|
|
149
|
+
// Accumulate state across chunks.
|
|
150
|
+
let completionId = '';
|
|
151
|
+
let completionModel = '';
|
|
152
|
+
let finalFinishReason = 'stop';
|
|
153
|
+
let inputTokens = 0;
|
|
154
|
+
let outputTokens = 0;
|
|
155
|
+
// tool_calls are streamed piecemeal; key = tool call index
|
|
156
|
+
const toolCallBuffers = new Map();
|
|
157
|
+
// Full text accumulator for the `done` response.
|
|
158
|
+
let fullText = '';
|
|
159
|
+
try {
|
|
160
|
+
for await (const chunk of streamResponse) {
|
|
161
|
+
completionId = chunk.id;
|
|
162
|
+
completionModel = chunk.model;
|
|
163
|
+
// Usage is only populated in the final chunk when stream_options.include_usage is set.
|
|
164
|
+
if (chunk.usage !== null && chunk.usage !== undefined) {
|
|
165
|
+
inputTokens = chunk.usage.prompt_tokens;
|
|
166
|
+
outputTokens = chunk.usage.completion_tokens;
|
|
167
|
+
}
|
|
168
|
+
const choice = chunk.choices[0];
|
|
169
|
+
if (choice === undefined)
|
|
170
|
+
continue;
|
|
171
|
+
const delta = choice.delta;
|
|
172
|
+
// --- text delta ---
|
|
173
|
+
if (delta.content !== null && delta.content !== undefined) {
|
|
174
|
+
fullText += delta.content;
|
|
175
|
+
const textEvent = { type: 'text', data: delta.content };
|
|
176
|
+
yield textEvent;
|
|
177
|
+
}
|
|
178
|
+
// --- tool call delta ---
|
|
179
|
+
for (const toolCallDelta of delta.tool_calls ?? []) {
|
|
180
|
+
const idx = toolCallDelta.index;
|
|
181
|
+
if (!toolCallBuffers.has(idx)) {
|
|
182
|
+
toolCallBuffers.set(idx, {
|
|
183
|
+
id: toolCallDelta.id ?? '',
|
|
184
|
+
name: toolCallDelta.function?.name ?? '',
|
|
185
|
+
argsJson: '',
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
const buf = toolCallBuffers.get(idx);
|
|
189
|
+
// buf is guaranteed to exist: we just set it above.
|
|
190
|
+
if (buf !== undefined) {
|
|
191
|
+
if (toolCallDelta.id)
|
|
192
|
+
buf.id = toolCallDelta.id;
|
|
193
|
+
if (toolCallDelta.function?.name)
|
|
194
|
+
buf.name = toolCallDelta.function.name;
|
|
195
|
+
if (toolCallDelta.function?.arguments) {
|
|
196
|
+
buf.argsJson += toolCallDelta.function.arguments;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (choice.finish_reason !== null && choice.finish_reason !== undefined) {
|
|
201
|
+
finalFinishReason = choice.finish_reason;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// Emit accumulated tool_use events after the stream ends.
|
|
205
|
+
const finalToolUseBlocks = [];
|
|
206
|
+
for (const buf of toolCallBuffers.values()) {
|
|
207
|
+
let parsedInput = {};
|
|
208
|
+
try {
|
|
209
|
+
const parsed = JSON.parse(buf.argsJson);
|
|
210
|
+
if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
211
|
+
parsedInput = parsed;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
// Malformed JSON — surface as empty object.
|
|
216
|
+
}
|
|
217
|
+
const toolUseBlock = {
|
|
218
|
+
type: 'tool_use',
|
|
219
|
+
id: buf.id,
|
|
220
|
+
name: buf.name,
|
|
221
|
+
input: parsedInput,
|
|
222
|
+
};
|
|
223
|
+
finalToolUseBlocks.push(toolUseBlock);
|
|
224
|
+
const toolUseEvent = { type: 'tool_use', data: toolUseBlock };
|
|
225
|
+
yield toolUseEvent;
|
|
226
|
+
}
|
|
227
|
+
// Build the complete content array for the done response.
|
|
228
|
+
const doneContent = [];
|
|
229
|
+
if (fullText.length > 0) {
|
|
230
|
+
const textBlock = { type: 'text', text: fullText };
|
|
231
|
+
doneContent.push(textBlock);
|
|
232
|
+
}
|
|
233
|
+
doneContent.push(...finalToolUseBlocks);
|
|
234
|
+
// Fallback: extract tool calls from text when streaming produced no
|
|
235
|
+
// native tool_calls (same logic as fromOpenAICompletion).
|
|
236
|
+
if (finalToolUseBlocks.length === 0 && fullText.length > 0 && options.tools) {
|
|
237
|
+
const toolNames = options.tools.map(t => t.name);
|
|
238
|
+
const extracted = extractToolCallsFromText(fullText, toolNames);
|
|
239
|
+
if (extracted.length > 0) {
|
|
240
|
+
doneContent.push(...extracted);
|
|
241
|
+
for (const block of extracted) {
|
|
242
|
+
yield { type: 'tool_use', data: block };
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
const hasToolUseBlocks = doneContent.some(b => b.type === 'tool_use');
|
|
247
|
+
const resolvedStopReason = hasToolUseBlocks && finalFinishReason === 'stop'
|
|
248
|
+
? 'tool_use'
|
|
249
|
+
: normalizeFinishReason(finalFinishReason);
|
|
250
|
+
const finalResponse = {
|
|
251
|
+
id: completionId,
|
|
252
|
+
content: doneContent,
|
|
253
|
+
model: completionModel,
|
|
254
|
+
stop_reason: resolvedStopReason,
|
|
255
|
+
usage: { input_tokens: inputTokens, output_tokens: outputTokens },
|
|
256
|
+
};
|
|
257
|
+
const doneEvent = { type: 'done', data: finalResponse };
|
|
258
|
+
yield doneEvent;
|
|
259
|
+
}
|
|
260
|
+
catch (err) {
|
|
261
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
262
|
+
const errorEvent = { type: 'error', data: error };
|
|
263
|
+
yield errorEvent;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
//# sourceMappingURL=azure-openai.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"azure-openai.js","sourceRoot":"","sources":["../../src/llm/azure-openai.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview AWS Bedrock LLM adapter implementing {@link LLMAdapter}.
|
|
3
|
+
*
|
|
4
|
+
* Uses the Converse / ConverseStream APIs from `@aws-sdk/client-bedrock-runtime`
|
|
5
|
+
* (the unified Anthropic-shaped schema) so the same adapter works across Claude,
|
|
6
|
+
* Llama, Mistral, Cohere, and Titan model families without per-model shims.
|
|
7
|
+
*
|
|
8
|
+
* Region resolution order:
|
|
9
|
+
* 1. `region` constructor argument
|
|
10
|
+
* 2. `AWS_REGION` environment variable
|
|
11
|
+
* 3. `'us-east-1'` (hard fallback)
|
|
12
|
+
*
|
|
13
|
+
* AWS credentials are resolved via the SDK default provider chain:
|
|
14
|
+
* env vars → shared config file → EC2/ECS/Lambda IAM role.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* import { BedrockAdapter } from './bedrock.js'
|
|
19
|
+
*
|
|
20
|
+
* const adapter = new BedrockAdapter('us-east-1')
|
|
21
|
+
* const response = await adapter.chat(messages, {
|
|
22
|
+
* model: 'anthropic.claude-3-5-haiku-20241022-v1:0',
|
|
23
|
+
* maxTokens: 1024,
|
|
24
|
+
* })
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
import type { ContentBlock, ImageBlock, LLMAdapter, LLMChatOptions, LLMMessage, LLMResponse, LLMStreamOptions, LLMToolDef, StreamEvent, TextBlock, ToolResultBlock, ToolUseBlock } from '../types.js';
|
|
28
|
+
/**
|
|
29
|
+
* LLM adapter backed by AWS Bedrock Converse / ConverseStream APIs.
|
|
30
|
+
*
|
|
31
|
+
* Thread-safe — a single instance may be shared across concurrent agent runs.
|
|
32
|
+
*/
|
|
33
|
+
export declare class BedrockAdapter implements LLMAdapter {
|
|
34
|
+
#private;
|
|
35
|
+
readonly name = "bedrock";
|
|
36
|
+
constructor(region?: string);
|
|
37
|
+
chat(messages: LLMMessage[], options: LLMChatOptions): Promise<LLMResponse>;
|
|
38
|
+
stream(messages: LLMMessage[], options: LLMStreamOptions): AsyncIterable<StreamEvent>;
|
|
39
|
+
}
|
|
40
|
+
export type { ContentBlock, ImageBlock, LLMAdapter, LLMChatOptions, LLMMessage, LLMResponse, LLMStreamOptions, LLMToolDef, StreamEvent, TextBlock, ToolResultBlock, ToolUseBlock };
|
|
41
|
+
//# sourceMappingURL=bedrock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bedrock.d.ts","sourceRoot":"","sources":["../../src/llm/bedrock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAeH,OAAO,KAAK,EACV,YAAY,EACZ,UAAU,EACV,UAAU,EACV,cAAc,EACd,UAAU,EACV,WAAW,EACX,gBAAgB,EAChB,UAAU,EAEV,WAAW,EACX,SAAS,EACT,eAAe,EACf,YAAY,EACb,MAAM,aAAa,CAAA;AA2JpB;;;;GAIG;AACH,qBAAa,cAAe,YAAW,UAAU;;IAC/C,QAAQ,CAAC,IAAI,aAAY;gBAIb,MAAM,CAAC,EAAE,MAAM;IASrB,IAAI,CAAC,QAAQ,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,WAAW,CAAC;IA4C1E,MAAM,CAAC,QAAQ,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,gBAAgB,GAAG,aAAa,CAAC,WAAW,CAAC;CAkI7F;AAED,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,gBAAgB,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,eAAe,EAAE,YAAY,EAAE,CAAA"}
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview AWS Bedrock LLM adapter implementing {@link LLMAdapter}.
|
|
3
|
+
*
|
|
4
|
+
* Uses the Converse / ConverseStream APIs from `@aws-sdk/client-bedrock-runtime`
|
|
5
|
+
* (the unified Anthropic-shaped schema) so the same adapter works across Claude,
|
|
6
|
+
* Llama, Mistral, Cohere, and Titan model families without per-model shims.
|
|
7
|
+
*
|
|
8
|
+
* Region resolution order:
|
|
9
|
+
* 1. `region` constructor argument
|
|
10
|
+
* 2. `AWS_REGION` environment variable
|
|
11
|
+
* 3. `'us-east-1'` (hard fallback)
|
|
12
|
+
*
|
|
13
|
+
* AWS credentials are resolved via the SDK default provider chain:
|
|
14
|
+
* env vars → shared config file → EC2/ECS/Lambda IAM role.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* import { BedrockAdapter } from './bedrock.js'
|
|
19
|
+
*
|
|
20
|
+
* const adapter = new BedrockAdapter('us-east-1')
|
|
21
|
+
* const response = await adapter.chat(messages, {
|
|
22
|
+
* model: 'anthropic.claude-3-5-haiku-20241022-v1:0',
|
|
23
|
+
* maxTokens: 1024,
|
|
24
|
+
* })
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
import { BedrockRuntimeClient, ConverseCommand, ConverseStreamCommand, } from '@aws-sdk/client-bedrock-runtime';
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// Internal helpers
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
const MEDIA_TYPE_TO_FORMAT = {
|
|
32
|
+
'image/jpeg': 'jpeg',
|
|
33
|
+
'image/png': 'png',
|
|
34
|
+
'image/gif': 'gif',
|
|
35
|
+
'image/webp': 'webp',
|
|
36
|
+
};
|
|
37
|
+
function base64ToUint8Array(b64) {
|
|
38
|
+
return Buffer.from(b64, 'base64');
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Convert a single framework {@link ContentBlock} into a Bedrock
|
|
42
|
+
* {@link BedrockContentBlock} for the messages array.
|
|
43
|
+
*
|
|
44
|
+
* Reasoning blocks are dropped on input — see the `case 'reasoning'` arm below
|
|
45
|
+
* for the round-trip limitation that forces this.
|
|
46
|
+
*/
|
|
47
|
+
function toBedrockContentBlock(block) {
|
|
48
|
+
switch (block.type) {
|
|
49
|
+
case 'text':
|
|
50
|
+
return { text: block.text };
|
|
51
|
+
case 'tool_use':
|
|
52
|
+
// DocumentType is not publicly exported from the SDK; cast the whole block.
|
|
53
|
+
return {
|
|
54
|
+
toolUse: { toolUseId: block.id, name: block.name, input: block.input },
|
|
55
|
+
};
|
|
56
|
+
case 'tool_result': {
|
|
57
|
+
const rawContent = block.content;
|
|
58
|
+
const textContent = typeof rawContent === 'string' ? rawContent : JSON.stringify(rawContent);
|
|
59
|
+
return {
|
|
60
|
+
toolResult: {
|
|
61
|
+
toolUseId: block.tool_use_id,
|
|
62
|
+
content: [{ text: textContent }],
|
|
63
|
+
status: block.is_error ? 'error' : 'success',
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
case 'image': {
|
|
68
|
+
const format = MEDIA_TYPE_TO_FORMAT[block.source.media_type] ?? 'png';
|
|
69
|
+
return {
|
|
70
|
+
image: {
|
|
71
|
+
format,
|
|
72
|
+
source: { bytes: base64ToUint8Array(block.source.data) },
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
case 'reasoning':
|
|
77
|
+
// Bedrock requires the original reasoning text plus its `signature`
|
|
78
|
+
// (and `redactedData` for redacted blocks) to be echoed back unchanged
|
|
79
|
+
// when continuing a multi-turn tool-use conversation with extended
|
|
80
|
+
// thinking. The IR now carries these fields on {@link ReasoningBlock}
|
|
81
|
+
// (added in #200), but wiring them through Bedrock's
|
|
82
|
+
// `reasoningContent.reasoningText.signature` shape has been deferred
|
|
83
|
+
// to a follow-up PR — until then, we drop reasoning on input.
|
|
84
|
+
return null;
|
|
85
|
+
default: {
|
|
86
|
+
const _exhaustive = block;
|
|
87
|
+
throw new Error(`Unhandled content block type: ${JSON.stringify(_exhaustive)}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Convert framework messages into Bedrock `Message[]`.
|
|
93
|
+
*
|
|
94
|
+
* System prompt is passed separately via `options.systemPrompt` and handled
|
|
95
|
+
* in `chat()`/`stream()` — it never appears as a message role in the framework.
|
|
96
|
+
* Reasoning blocks are silently dropped on input — see {@link toBedrockContentBlock}
|
|
97
|
+
* for why; multi-turn tool-use flows with extended thinking are tracked as
|
|
98
|
+
* a follow-up to #200's phase-1 IR additions.
|
|
99
|
+
*/
|
|
100
|
+
function toBedrockMessages(messages) {
|
|
101
|
+
const bedrockMessages = [];
|
|
102
|
+
for (const msg of messages) {
|
|
103
|
+
const content = [];
|
|
104
|
+
for (const block of msg.content) {
|
|
105
|
+
const converted = toBedrockContentBlock(block);
|
|
106
|
+
if (converted !== null)
|
|
107
|
+
content.push(converted);
|
|
108
|
+
}
|
|
109
|
+
if (content.length > 0) {
|
|
110
|
+
bedrockMessages.push({ role: msg.role, content });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return bedrockMessages;
|
|
114
|
+
}
|
|
115
|
+
function toBedrockTools(tools) {
|
|
116
|
+
// Tool is a discriminated union with a required $unknown variant; cast to satisfy it.
|
|
117
|
+
const bedrockTools = tools.map((t) => ({
|
|
118
|
+
toolSpec: {
|
|
119
|
+
name: t.name,
|
|
120
|
+
description: t.description,
|
|
121
|
+
inputSchema: { json: t.inputSchema },
|
|
122
|
+
},
|
|
123
|
+
}));
|
|
124
|
+
return { tools: bedrockTools };
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Convert a Bedrock response {@link BedrockContentBlock} into a framework
|
|
128
|
+
* {@link ContentBlock}.
|
|
129
|
+
*
|
|
130
|
+
* `toolUse.input` arrives as a parsed object in chat() responses.
|
|
131
|
+
*/
|
|
132
|
+
function fromBedrockContentBlock(block) {
|
|
133
|
+
if (block.text !== undefined) {
|
|
134
|
+
const text = { type: 'text', text: block.text };
|
|
135
|
+
return text;
|
|
136
|
+
}
|
|
137
|
+
if (block.toolUse !== undefined) {
|
|
138
|
+
const toolUse = {
|
|
139
|
+
type: 'tool_use',
|
|
140
|
+
id: block.toolUse.toolUseId ?? '',
|
|
141
|
+
name: block.toolUse.name ?? '',
|
|
142
|
+
input: block.toolUse.input ?? {},
|
|
143
|
+
};
|
|
144
|
+
return toolUse;
|
|
145
|
+
}
|
|
146
|
+
if (block.reasoningContent !== undefined) {
|
|
147
|
+
const r = block.reasoningContent;
|
|
148
|
+
const reasoning = {
|
|
149
|
+
type: 'reasoning',
|
|
150
|
+
text: r.reasoningText?.text ?? '',
|
|
151
|
+
};
|
|
152
|
+
return reasoning;
|
|
153
|
+
}
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
function buildInferenceConfig(options) {
|
|
157
|
+
const cfg = {};
|
|
158
|
+
if (options.maxTokens !== undefined)
|
|
159
|
+
cfg.maxTokens = options.maxTokens ?? 4096;
|
|
160
|
+
if (options.temperature !== undefined)
|
|
161
|
+
cfg.temperature = options.temperature;
|
|
162
|
+
if (options.topP !== undefined)
|
|
163
|
+
cfg.topP = options.topP;
|
|
164
|
+
return Object.keys(cfg).length > 0 ? cfg : undefined;
|
|
165
|
+
}
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
// Adapter implementation
|
|
168
|
+
// ---------------------------------------------------------------------------
|
|
169
|
+
/**
|
|
170
|
+
* LLM adapter backed by AWS Bedrock Converse / ConverseStream APIs.
|
|
171
|
+
*
|
|
172
|
+
* Thread-safe — a single instance may be shared across concurrent agent runs.
|
|
173
|
+
*/
|
|
174
|
+
export class BedrockAdapter {
|
|
175
|
+
name = 'bedrock';
|
|
176
|
+
#client;
|
|
177
|
+
constructor(region) {
|
|
178
|
+
const resolvedRegion = region ?? process.env['AWS_REGION'] ?? 'us-east-1';
|
|
179
|
+
this.#client = new BedrockRuntimeClient({ region: resolvedRegion });
|
|
180
|
+
}
|
|
181
|
+
// -------------------------------------------------------------------------
|
|
182
|
+
// chat()
|
|
183
|
+
// -------------------------------------------------------------------------
|
|
184
|
+
async chat(messages, options) {
|
|
185
|
+
const bedrockMessages = toBedrockMessages(messages);
|
|
186
|
+
const system = options.systemPrompt ? [{ text: options.systemPrompt }] : undefined;
|
|
187
|
+
const input = {
|
|
188
|
+
modelId: options.model,
|
|
189
|
+
messages: bedrockMessages,
|
|
190
|
+
system,
|
|
191
|
+
toolConfig: options.tools ? toBedrockTools(options.tools) : undefined,
|
|
192
|
+
inferenceConfig: buildInferenceConfig(options) ?? { maxTokens: 4096 },
|
|
193
|
+
};
|
|
194
|
+
if (options.topK !== undefined || options.extraBody) {
|
|
195
|
+
input.additionalModelRequestFields = {
|
|
196
|
+
...(options.topK !== undefined ? { top_k: options.topK } : {}),
|
|
197
|
+
...options.extraBody,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
const response = await this.#client.send(new ConverseCommand(input), {
|
|
201
|
+
abortSignal: options.abortSignal,
|
|
202
|
+
});
|
|
203
|
+
const rawContent = response.output?.message?.content ?? [];
|
|
204
|
+
const content = rawContent
|
|
205
|
+
.map(fromBedrockContentBlock)
|
|
206
|
+
.filter((b) => b !== null);
|
|
207
|
+
return {
|
|
208
|
+
id: response.$metadata.requestId ?? '',
|
|
209
|
+
content,
|
|
210
|
+
model: options.model,
|
|
211
|
+
stop_reason: response.stopReason ?? 'end_turn',
|
|
212
|
+
usage: {
|
|
213
|
+
input_tokens: response.usage?.inputTokens ?? 0,
|
|
214
|
+
output_tokens: response.usage?.outputTokens ?? 0,
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
// -------------------------------------------------------------------------
|
|
219
|
+
// stream()
|
|
220
|
+
// -------------------------------------------------------------------------
|
|
221
|
+
async *stream(messages, options) {
|
|
222
|
+
const bedrockMessages = toBedrockMessages(messages);
|
|
223
|
+
const system = options.systemPrompt ? [{ text: options.systemPrompt }] : undefined;
|
|
224
|
+
const input = {
|
|
225
|
+
modelId: options.model,
|
|
226
|
+
messages: bedrockMessages,
|
|
227
|
+
system,
|
|
228
|
+
toolConfig: options.tools ? toBedrockTools(options.tools) : undefined,
|
|
229
|
+
inferenceConfig: buildInferenceConfig(options) ?? { maxTokens: 4096 },
|
|
230
|
+
};
|
|
231
|
+
if (options.topK !== undefined || options.extraBody) {
|
|
232
|
+
input.additionalModelRequestFields = {
|
|
233
|
+
...(options.topK !== undefined ? { top_k: options.topK } : {}),
|
|
234
|
+
...options.extraBody,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
// Accumulate tool-use input JSON deltas; keyed by content block index.
|
|
238
|
+
const toolBuffers = new Map();
|
|
239
|
+
// Accumulate reasoning text deltas; keyed by content block index. Each
|
|
240
|
+
// index becomes one ReasoningBlock in the final `done` payload, matching
|
|
241
|
+
// what `chat()` produces for the same response shape.
|
|
242
|
+
const reasoningBuffers = new Map();
|
|
243
|
+
// Accumulated content blocks for the done event.
|
|
244
|
+
const accumulatedContent = [];
|
|
245
|
+
let stopReason = 'end_turn';
|
|
246
|
+
let inputTokens = 0;
|
|
247
|
+
let outputTokens = 0;
|
|
248
|
+
const requestId = '';
|
|
249
|
+
try {
|
|
250
|
+
const response = await this.#client.send(new ConverseStreamCommand(input), {
|
|
251
|
+
abortSignal: options.abortSignal,
|
|
252
|
+
});
|
|
253
|
+
for await (const event of response.stream ?? []) {
|
|
254
|
+
if (event.contentBlockStart?.start?.toolUse) {
|
|
255
|
+
const { toolUseId, name } = event.contentBlockStart.start.toolUse;
|
|
256
|
+
const index = event.contentBlockStart.contentBlockIndex ?? 0;
|
|
257
|
+
toolBuffers.set(index, { toolUseId: toolUseId ?? '', name: name ?? '', json: '' });
|
|
258
|
+
}
|
|
259
|
+
if (event.contentBlockDelta?.delta) {
|
|
260
|
+
const delta = event.contentBlockDelta.delta;
|
|
261
|
+
const index = event.contentBlockDelta.contentBlockIndex ?? 0;
|
|
262
|
+
if (delta.text !== undefined) {
|
|
263
|
+
const textEvent = { type: 'text', data: delta.text };
|
|
264
|
+
yield textEvent;
|
|
265
|
+
accumulatedContent.push({ type: 'text', text: delta.text });
|
|
266
|
+
}
|
|
267
|
+
else if (delta.toolUse?.input !== undefined) {
|
|
268
|
+
const buf = toolBuffers.get(index);
|
|
269
|
+
if (buf)
|
|
270
|
+
buf.json += delta.toolUse.input;
|
|
271
|
+
}
|
|
272
|
+
else if (delta.reasoningContent?.text !== undefined) {
|
|
273
|
+
const text = delta.reasoningContent.text;
|
|
274
|
+
const reasoningEvent = { type: 'reasoning', data: text };
|
|
275
|
+
yield reasoningEvent;
|
|
276
|
+
const buf = reasoningBuffers.get(index) ?? { text: '' };
|
|
277
|
+
buf.text += text;
|
|
278
|
+
reasoningBuffers.set(index, buf);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
if (event.contentBlockStop !== undefined) {
|
|
282
|
+
const index = event.contentBlockStop.contentBlockIndex ?? 0;
|
|
283
|
+
const reasoningBuf = reasoningBuffers.get(index);
|
|
284
|
+
if (reasoningBuf) {
|
|
285
|
+
const reasoningBlock = { type: 'reasoning', text: reasoningBuf.text };
|
|
286
|
+
accumulatedContent.push(reasoningBlock);
|
|
287
|
+
reasoningBuffers.delete(index);
|
|
288
|
+
}
|
|
289
|
+
const buf = toolBuffers.get(index);
|
|
290
|
+
if (buf) {
|
|
291
|
+
let parsedInput = {};
|
|
292
|
+
try {
|
|
293
|
+
const parsed = JSON.parse(buf.json || '{}');
|
|
294
|
+
if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
295
|
+
parsedInput = parsed;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
// malformed JSON → empty object
|
|
300
|
+
}
|
|
301
|
+
const toolUseBlock = {
|
|
302
|
+
type: 'tool_use',
|
|
303
|
+
id: buf.toolUseId,
|
|
304
|
+
name: buf.name,
|
|
305
|
+
input: parsedInput,
|
|
306
|
+
};
|
|
307
|
+
accumulatedContent.push(toolUseBlock);
|
|
308
|
+
const toolUseEvent = { type: 'tool_use', data: toolUseBlock };
|
|
309
|
+
yield toolUseEvent;
|
|
310
|
+
toolBuffers.delete(index);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (event.messageStop) {
|
|
314
|
+
stopReason = event.messageStop.stopReason ?? 'end_turn';
|
|
315
|
+
}
|
|
316
|
+
if (event.metadata?.usage) {
|
|
317
|
+
inputTokens = event.metadata.usage.inputTokens ?? 0;
|
|
318
|
+
outputTokens = event.metadata.usage.outputTokens ?? 0;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
// Safety net: if Bedrock ever omits a contentBlockStop for a reasoning
|
|
322
|
+
// block, flush whatever we buffered so the done payload still matches
|
|
323
|
+
// what chat() would have returned for the same response.
|
|
324
|
+
for (const [, buf] of reasoningBuffers) {
|
|
325
|
+
accumulatedContent.push({ type: 'reasoning', text: buf.text });
|
|
326
|
+
}
|
|
327
|
+
reasoningBuffers.clear();
|
|
328
|
+
const finalResponse = {
|
|
329
|
+
id: requestId,
|
|
330
|
+
content: accumulatedContent,
|
|
331
|
+
model: options.model,
|
|
332
|
+
stop_reason: stopReason,
|
|
333
|
+
usage: { input_tokens: inputTokens, output_tokens: outputTokens },
|
|
334
|
+
};
|
|
335
|
+
const doneEvent = { type: 'done', data: finalResponse };
|
|
336
|
+
yield doneEvent;
|
|
337
|
+
}
|
|
338
|
+
catch (err) {
|
|
339
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
340
|
+
const errorEvent = { type: 'error', data: error };
|
|
341
|
+
yield errorEvent;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
//# sourceMappingURL=bedrock.js.map
|