@jackchen_me/open-multi-agent 0.1.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 +280 -0
- package/dist/agent/agent.d.ts +121 -0
- package/dist/agent/agent.d.ts.map +1 -0
- package/dist/agent/agent.js +294 -0
- package/dist/agent/agent.js.map +1 -0
- package/dist/agent/pool.d.ts +128 -0
- package/dist/agent/pool.d.ts.map +1 -0
- package/dist/agent/pool.js +236 -0
- package/dist/agent/pool.js.map +1 -0
- package/dist/agent/runner.d.ts +120 -0
- package/dist/agent/runner.d.ts.map +1 -0
- package/dist/agent/runner.js +274 -0
- package/dist/agent/runner.js.map +1 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +87 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/adapter.d.ts +38 -0
- package/dist/llm/adapter.d.ts.map +1 -0
- package/dist/llm/adapter.js +46 -0
- package/dist/llm/adapter.js.map +1 -0
- package/dist/llm/anthropic.d.ts +56 -0
- package/dist/llm/anthropic.d.ts.map +1 -0
- package/dist/llm/anthropic.js +307 -0
- package/dist/llm/anthropic.js.map +1 -0
- package/dist/llm/openai.d.ts +62 -0
- package/dist/llm/openai.d.ts.map +1 -0
- package/dist/llm/openai.js +424 -0
- package/dist/llm/openai.js.map +1 -0
- package/dist/memory/shared.d.ts +86 -0
- package/dist/memory/shared.d.ts.map +1 -0
- package/dist/memory/shared.js +155 -0
- package/dist/memory/shared.js.map +1 -0
- package/dist/memory/store.d.ts +64 -0
- package/dist/memory/store.d.ts.map +1 -0
- package/dist/memory/store.js +103 -0
- package/dist/memory/store.js.map +1 -0
- package/dist/orchestrator/orchestrator.d.ts +173 -0
- package/dist/orchestrator/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator/orchestrator.js +698 -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 +282 -0
- package/dist/orchestrator/scheduler.js.map +1 -0
- package/dist/task/queue.d.ts +160 -0
- package/dist/task/queue.d.ts.map +1 -0
- package/dist/task/queue.js +337 -0
- package/dist/task/queue.js.map +1 -0
- package/dist/task/task.d.ts +86 -0
- package/dist/task/task.d.ts.map +1 -0
- package/dist/task/task.js +201 -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 +182 -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 +282 -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/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/grep.d.ts +15 -0
- package/dist/tool/built-in/grep.d.ts.map +1 -0
- package/dist/tool/built-in/grep.js +287 -0
- package/dist/tool/built-in/grep.js.map +1 -0
- package/dist/tool/built-in/index.d.ts +36 -0
- package/dist/tool/built-in/index.d.ts.map +1 -0
- package/dist/tool/built-in/index.js +45 -0
- package/dist/tool/built-in/index.js.map +1 -0
- package/dist/tool/executor.d.ts +71 -0
- package/dist/tool/executor.d.ts.map +1 -0
- package/dist/tool/executor.js +116 -0
- package/dist/tool/executor.js.map +1 -0
- package/dist/tool/framework.d.ts +143 -0
- package/dist/tool/framework.d.ts.map +1 -0
- package/dist/tool/framework.js +371 -0
- package/dist/tool/framework.js.map +1 -0
- package/dist/types.d.ts +285 -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/semaphore.d.ts +47 -0
- package/dist/utils/semaphore.d.ts.map +1 -0
- package/dist/utils/semaphore.js +85 -0
- package/dist/utils/semaphore.js.map +1 -0
- package/examples/01-single-agent.ts +131 -0
- package/examples/02-team-collaboration.ts +167 -0
- package/examples/03-task-pipeline.ts +201 -0
- package/examples/04-multi-model-team.ts +261 -0
- package/package.json +49 -0
- package/src/agent/agent.ts +364 -0
- package/src/agent/pool.ts +278 -0
- package/src/agent/runner.ts +413 -0
- package/src/index.ts +166 -0
- package/src/llm/adapter.ts +74 -0
- package/src/llm/anthropic.ts +388 -0
- package/src/llm/openai.ts +522 -0
- package/src/memory/shared.ts +181 -0
- package/src/memory/store.ts +124 -0
- package/src/orchestrator/orchestrator.ts +851 -0
- package/src/orchestrator/scheduler.ts +352 -0
- package/src/task/queue.ts +394 -0
- package/src/task/task.ts +232 -0
- package/src/team/messaging.ts +230 -0
- package/src/team/team.ts +334 -0
- package/src/tool/built-in/bash.ts +187 -0
- package/src/tool/built-in/file-edit.ts +154 -0
- package/src/tool/built-in/file-read.ts +105 -0
- package/src/tool/built-in/file-write.ts +81 -0
- package/src/tool/built-in/grep.ts +362 -0
- package/src/tool/built-in/index.ts +50 -0
- package/src/tool/executor.ts +178 -0
- package/src/tool/framework.ts +557 -0
- package/src/types.ts +362 -0
- package/src/utils/semaphore.ts +89 -0
- package/tsconfig.json +25 -0
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Anthropic Claude adapter implementing {@link LLMAdapter}.
|
|
3
|
+
*
|
|
4
|
+
* Converts between the framework's internal {@link ContentBlock} types and the
|
|
5
|
+
* Anthropic SDK's wire format, handling tool definitions, system prompts, and
|
|
6
|
+
* both batch and streaming response paths.
|
|
7
|
+
*
|
|
8
|
+
* API key resolution order:
|
|
9
|
+
* 1. `apiKey` constructor argument
|
|
10
|
+
* 2. `ANTHROPIC_API_KEY` environment variable
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { AnthropicAdapter } from './anthropic.js'
|
|
15
|
+
*
|
|
16
|
+
* const adapter = new AnthropicAdapter()
|
|
17
|
+
* const response = await adapter.chat(messages, {
|
|
18
|
+
* model: 'claude-opus-4-6',
|
|
19
|
+
* maxTokens: 1024,
|
|
20
|
+
* })
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import Anthropic from '@anthropic-ai/sdk'
|
|
25
|
+
import type {
|
|
26
|
+
ContentBlockParam,
|
|
27
|
+
ImageBlockParam,
|
|
28
|
+
MessageParam,
|
|
29
|
+
TextBlockParam,
|
|
30
|
+
ToolResultBlockParam,
|
|
31
|
+
ToolUseBlockParam,
|
|
32
|
+
Tool as AnthropicTool,
|
|
33
|
+
} from '@anthropic-ai/sdk/resources/messages/messages.js'
|
|
34
|
+
|
|
35
|
+
import type {
|
|
36
|
+
ContentBlock,
|
|
37
|
+
ImageBlock,
|
|
38
|
+
LLMAdapter,
|
|
39
|
+
LLMChatOptions,
|
|
40
|
+
LLMMessage,
|
|
41
|
+
LLMResponse,
|
|
42
|
+
LLMStreamOptions,
|
|
43
|
+
LLMToolDef,
|
|
44
|
+
StreamEvent,
|
|
45
|
+
TextBlock,
|
|
46
|
+
ToolResultBlock,
|
|
47
|
+
ToolUseBlock,
|
|
48
|
+
} from '../types.js'
|
|
49
|
+
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Internal helpers
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Convert a single framework {@link ContentBlock} into an Anthropic
|
|
56
|
+
* {@link ContentBlockParam} suitable for the `messages` array.
|
|
57
|
+
*
|
|
58
|
+
* `tool_result` blocks are only valid inside `user`-role messages, which is
|
|
59
|
+
* handled by {@link toAnthropicMessages} based on role context.
|
|
60
|
+
*/
|
|
61
|
+
function toAnthropicContentBlockParam(block: ContentBlock): ContentBlockParam {
|
|
62
|
+
switch (block.type) {
|
|
63
|
+
case 'text': {
|
|
64
|
+
const param: TextBlockParam = { type: 'text', text: block.text }
|
|
65
|
+
return param
|
|
66
|
+
}
|
|
67
|
+
case 'tool_use': {
|
|
68
|
+
const param: ToolUseBlockParam = {
|
|
69
|
+
type: 'tool_use',
|
|
70
|
+
id: block.id,
|
|
71
|
+
name: block.name,
|
|
72
|
+
input: block.input,
|
|
73
|
+
}
|
|
74
|
+
return param
|
|
75
|
+
}
|
|
76
|
+
case 'tool_result': {
|
|
77
|
+
const param: ToolResultBlockParam = {
|
|
78
|
+
type: 'tool_result',
|
|
79
|
+
tool_use_id: block.tool_use_id,
|
|
80
|
+
content: block.content,
|
|
81
|
+
is_error: block.is_error,
|
|
82
|
+
}
|
|
83
|
+
return param
|
|
84
|
+
}
|
|
85
|
+
case 'image': {
|
|
86
|
+
// Anthropic only accepts a subset of MIME types; we pass them through
|
|
87
|
+
// trusting the caller to supply a valid media_type value.
|
|
88
|
+
const param: ImageBlockParam = {
|
|
89
|
+
type: 'image',
|
|
90
|
+
source: {
|
|
91
|
+
type: 'base64',
|
|
92
|
+
media_type: block.source.media_type as
|
|
93
|
+
| 'image/jpeg'
|
|
94
|
+
| 'image/png'
|
|
95
|
+
| 'image/gif'
|
|
96
|
+
| 'image/webp',
|
|
97
|
+
data: block.source.data,
|
|
98
|
+
},
|
|
99
|
+
}
|
|
100
|
+
return param
|
|
101
|
+
}
|
|
102
|
+
default: {
|
|
103
|
+
// Exhaustiveness guard — TypeScript will flag this at compile time if a
|
|
104
|
+
// new variant is added to ContentBlock without updating this switch.
|
|
105
|
+
const _exhaustive: never = block
|
|
106
|
+
throw new Error(`Unhandled content block type: ${JSON.stringify(_exhaustive)}`)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Convert framework messages into Anthropic's `MessageParam[]` format.
|
|
113
|
+
*
|
|
114
|
+
* The Anthropic API requires strict user/assistant alternation. We do not
|
|
115
|
+
* enforce that here — the caller is responsible for producing a valid
|
|
116
|
+
* conversation history.
|
|
117
|
+
*/
|
|
118
|
+
function toAnthropicMessages(messages: LLMMessage[]): MessageParam[] {
|
|
119
|
+
return messages.map((msg): MessageParam => ({
|
|
120
|
+
role: msg.role,
|
|
121
|
+
content: msg.content.map(toAnthropicContentBlockParam),
|
|
122
|
+
}))
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Convert framework {@link LLMToolDef}s into Anthropic's `Tool` objects.
|
|
127
|
+
*
|
|
128
|
+
* The `inputSchema` on {@link LLMToolDef} is already a plain JSON Schema
|
|
129
|
+
* object, so we just need to reshape the wrapper.
|
|
130
|
+
*/
|
|
131
|
+
function toAnthropicTools(tools: readonly LLMToolDef[]): AnthropicTool[] {
|
|
132
|
+
return tools.map((t): AnthropicTool => ({
|
|
133
|
+
name: t.name,
|
|
134
|
+
description: t.description,
|
|
135
|
+
input_schema: {
|
|
136
|
+
type: 'object',
|
|
137
|
+
...(t.inputSchema as Record<string, unknown>),
|
|
138
|
+
},
|
|
139
|
+
}))
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Convert an Anthropic SDK `ContentBlock` into a framework {@link ContentBlock}.
|
|
144
|
+
*
|
|
145
|
+
* We only map the subset of SDK types that the framework exposes. Unknown
|
|
146
|
+
* variants (thinking, server_tool_use, etc.) are converted to a text block
|
|
147
|
+
* carrying a stringified representation so data is never silently dropped.
|
|
148
|
+
*/
|
|
149
|
+
function fromAnthropicContentBlock(
|
|
150
|
+
block: Anthropic.Messages.ContentBlock,
|
|
151
|
+
): ContentBlock {
|
|
152
|
+
switch (block.type) {
|
|
153
|
+
case 'text': {
|
|
154
|
+
const text: TextBlock = { type: 'text', text: block.text }
|
|
155
|
+
return text
|
|
156
|
+
}
|
|
157
|
+
case 'tool_use': {
|
|
158
|
+
const toolUse: ToolUseBlock = {
|
|
159
|
+
type: 'tool_use',
|
|
160
|
+
id: block.id,
|
|
161
|
+
name: block.name,
|
|
162
|
+
input: block.input as Record<string, unknown>,
|
|
163
|
+
}
|
|
164
|
+
return toolUse
|
|
165
|
+
}
|
|
166
|
+
default: {
|
|
167
|
+
// Graceful degradation for SDK types we don't model (thinking, etc.).
|
|
168
|
+
const fallback: TextBlock = {
|
|
169
|
+
type: 'text',
|
|
170
|
+
text: `[unsupported block type: ${(block as { type: string }).type}]`,
|
|
171
|
+
}
|
|
172
|
+
return fallback
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ---------------------------------------------------------------------------
|
|
178
|
+
// Adapter implementation
|
|
179
|
+
// ---------------------------------------------------------------------------
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* LLM adapter backed by the Anthropic Claude API.
|
|
183
|
+
*
|
|
184
|
+
* Thread-safe — a single instance may be shared across concurrent agent runs.
|
|
185
|
+
* The underlying SDK client is stateless across requests.
|
|
186
|
+
*/
|
|
187
|
+
export class AnthropicAdapter implements LLMAdapter {
|
|
188
|
+
readonly name = 'anthropic'
|
|
189
|
+
|
|
190
|
+
readonly #client: Anthropic
|
|
191
|
+
|
|
192
|
+
constructor(apiKey?: string) {
|
|
193
|
+
this.#client = new Anthropic({
|
|
194
|
+
apiKey: apiKey ?? process.env['ANTHROPIC_API_KEY'],
|
|
195
|
+
})
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// -------------------------------------------------------------------------
|
|
199
|
+
// chat()
|
|
200
|
+
// -------------------------------------------------------------------------
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Send a synchronous (non-streaming) chat request and return the complete
|
|
204
|
+
* {@link LLMResponse}.
|
|
205
|
+
*
|
|
206
|
+
* Throws an `Anthropic.APIError` on non-2xx responses. Callers should catch
|
|
207
|
+
* and handle these (e.g. rate limits, context window exceeded).
|
|
208
|
+
*/
|
|
209
|
+
async chat(messages: LLMMessage[], options: LLMChatOptions): Promise<LLMResponse> {
|
|
210
|
+
const anthropicMessages = toAnthropicMessages(messages)
|
|
211
|
+
|
|
212
|
+
const response = await this.#client.messages.create(
|
|
213
|
+
{
|
|
214
|
+
model: options.model,
|
|
215
|
+
max_tokens: options.maxTokens ?? 4096,
|
|
216
|
+
messages: anthropicMessages,
|
|
217
|
+
system: options.systemPrompt,
|
|
218
|
+
tools: options.tools ? toAnthropicTools(options.tools) : undefined,
|
|
219
|
+
temperature: options.temperature,
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
signal: options.abortSignal,
|
|
223
|
+
},
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
const content = response.content.map(fromAnthropicContentBlock)
|
|
227
|
+
|
|
228
|
+
return {
|
|
229
|
+
id: response.id,
|
|
230
|
+
content,
|
|
231
|
+
model: response.model,
|
|
232
|
+
stop_reason: response.stop_reason ?? 'end_turn',
|
|
233
|
+
usage: {
|
|
234
|
+
input_tokens: response.usage.input_tokens,
|
|
235
|
+
output_tokens: response.usage.output_tokens,
|
|
236
|
+
},
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// -------------------------------------------------------------------------
|
|
241
|
+
// stream()
|
|
242
|
+
// -------------------------------------------------------------------------
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Send a streaming chat request and yield {@link StreamEvent}s as they
|
|
246
|
+
* arrive from the API.
|
|
247
|
+
*
|
|
248
|
+
* Sequence guarantees:
|
|
249
|
+
* - Zero or more `text` events containing incremental deltas
|
|
250
|
+
* - Zero or more `tool_use` events when the model calls a tool (emitted once
|
|
251
|
+
* per tool use, after input JSON has been fully assembled)
|
|
252
|
+
* - Exactly one terminal event: `done` (with the complete {@link LLMResponse}
|
|
253
|
+
* as `data`) or `error` (with an `Error` as `data`)
|
|
254
|
+
*/
|
|
255
|
+
async *stream(
|
|
256
|
+
messages: LLMMessage[],
|
|
257
|
+
options: LLMStreamOptions,
|
|
258
|
+
): AsyncIterable<StreamEvent> {
|
|
259
|
+
const anthropicMessages = toAnthropicMessages(messages)
|
|
260
|
+
|
|
261
|
+
// MessageStream gives us typed events and handles SSE reconnect internally.
|
|
262
|
+
const stream = this.#client.messages.stream(
|
|
263
|
+
{
|
|
264
|
+
model: options.model,
|
|
265
|
+
max_tokens: options.maxTokens ?? 4096,
|
|
266
|
+
messages: anthropicMessages,
|
|
267
|
+
system: options.systemPrompt,
|
|
268
|
+
tools: options.tools ? toAnthropicTools(options.tools) : undefined,
|
|
269
|
+
temperature: options.temperature,
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
signal: options.abortSignal,
|
|
273
|
+
},
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
// Accumulate tool-use input JSON as it streams in.
|
|
277
|
+
// key = content block index, value = partially assembled input JSON string
|
|
278
|
+
const toolInputBuffers = new Map<number, { id: string; name: string; json: string }>()
|
|
279
|
+
|
|
280
|
+
try {
|
|
281
|
+
for await (const event of stream) {
|
|
282
|
+
switch (event.type) {
|
|
283
|
+
case 'content_block_start': {
|
|
284
|
+
const block = event.content_block
|
|
285
|
+
if (block.type === 'tool_use') {
|
|
286
|
+
toolInputBuffers.set(event.index, {
|
|
287
|
+
id: block.id,
|
|
288
|
+
name: block.name,
|
|
289
|
+
json: '',
|
|
290
|
+
})
|
|
291
|
+
}
|
|
292
|
+
break
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
case 'content_block_delta': {
|
|
296
|
+
const delta = event.delta
|
|
297
|
+
|
|
298
|
+
if (delta.type === 'text_delta') {
|
|
299
|
+
const textEvent: StreamEvent = { type: 'text', data: delta.text }
|
|
300
|
+
yield textEvent
|
|
301
|
+
} else if (delta.type === 'input_json_delta') {
|
|
302
|
+
const buf = toolInputBuffers.get(event.index)
|
|
303
|
+
if (buf !== undefined) {
|
|
304
|
+
buf.json += delta.partial_json
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
break
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
case 'content_block_stop': {
|
|
311
|
+
const buf = toolInputBuffers.get(event.index)
|
|
312
|
+
if (buf !== undefined) {
|
|
313
|
+
// Parse the accumulated JSON and emit a tool_use event.
|
|
314
|
+
let parsedInput: Record<string, unknown> = {}
|
|
315
|
+
try {
|
|
316
|
+
const parsed: unknown = JSON.parse(buf.json)
|
|
317
|
+
if (
|
|
318
|
+
parsed !== null &&
|
|
319
|
+
typeof parsed === 'object' &&
|
|
320
|
+
!Array.isArray(parsed)
|
|
321
|
+
) {
|
|
322
|
+
parsedInput = parsed as Record<string, unknown>
|
|
323
|
+
}
|
|
324
|
+
} catch {
|
|
325
|
+
// Malformed JSON from the model — surface as an empty object
|
|
326
|
+
// rather than crashing the stream.
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const toolUseBlock: ToolUseBlock = {
|
|
330
|
+
type: 'tool_use',
|
|
331
|
+
id: buf.id,
|
|
332
|
+
name: buf.name,
|
|
333
|
+
input: parsedInput,
|
|
334
|
+
}
|
|
335
|
+
const toolUseEvent: StreamEvent = { type: 'tool_use', data: toolUseBlock }
|
|
336
|
+
yield toolUseEvent
|
|
337
|
+
toolInputBuffers.delete(event.index)
|
|
338
|
+
}
|
|
339
|
+
break
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// message_start, message_delta, message_stop — we handle the final
|
|
343
|
+
// response via stream.finalMessage() below rather than piecemeal.
|
|
344
|
+
default:
|
|
345
|
+
break
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Await the fully assembled final message (token counts, stop_reason, etc.)
|
|
350
|
+
const finalMessage = await stream.finalMessage()
|
|
351
|
+
const content = finalMessage.content.map(fromAnthropicContentBlock)
|
|
352
|
+
|
|
353
|
+
const finalResponse: LLMResponse = {
|
|
354
|
+
id: finalMessage.id,
|
|
355
|
+
content,
|
|
356
|
+
model: finalMessage.model,
|
|
357
|
+
stop_reason: finalMessage.stop_reason ?? 'end_turn',
|
|
358
|
+
usage: {
|
|
359
|
+
input_tokens: finalMessage.usage.input_tokens,
|
|
360
|
+
output_tokens: finalMessage.usage.output_tokens,
|
|
361
|
+
},
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const doneEvent: StreamEvent = { type: 'done', data: finalResponse }
|
|
365
|
+
yield doneEvent
|
|
366
|
+
} catch (err) {
|
|
367
|
+
const error = err instanceof Error ? err : new Error(String(err))
|
|
368
|
+
const errorEvent: StreamEvent = { type: 'error', data: error }
|
|
369
|
+
yield errorEvent
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Re-export types that consumers of this module commonly need alongside the adapter.
|
|
375
|
+
export type {
|
|
376
|
+
ContentBlock,
|
|
377
|
+
ImageBlock,
|
|
378
|
+
LLMAdapter,
|
|
379
|
+
LLMChatOptions,
|
|
380
|
+
LLMMessage,
|
|
381
|
+
LLMResponse,
|
|
382
|
+
LLMStreamOptions,
|
|
383
|
+
LLMToolDef,
|
|
384
|
+
StreamEvent,
|
|
385
|
+
TextBlock,
|
|
386
|
+
ToolResultBlock,
|
|
387
|
+
ToolUseBlock,
|
|
388
|
+
}
|