@tanstack/ai-anthropic 0.2.0 → 0.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/dist/esm/adapters/summarize.js +5 -6
- package/dist/esm/adapters/summarize.js.map +1 -1
- package/dist/esm/adapters/text.js +98 -46
- package/dist/esm/adapters/text.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/package.json +7 -5
- package/src/adapters/summarize.ts +4 -5
- package/src/adapters/text.ts +125 -54
- package/src/index.ts +0 -2
|
@@ -52,20 +52,19 @@ class AnthropicSummarizeAdapter extends BaseSummarizeAdapter {
|
|
|
52
52
|
const delta = event.delta.text;
|
|
53
53
|
accumulatedContent += delta;
|
|
54
54
|
yield {
|
|
55
|
-
type: "
|
|
56
|
-
id,
|
|
55
|
+
type: "TEXT_MESSAGE_CONTENT",
|
|
56
|
+
messageId: id,
|
|
57
57
|
model,
|
|
58
58
|
timestamp: Date.now(),
|
|
59
59
|
delta,
|
|
60
|
-
content: accumulatedContent
|
|
61
|
-
role: "assistant"
|
|
60
|
+
content: accumulatedContent
|
|
62
61
|
};
|
|
63
62
|
}
|
|
64
63
|
} else if (event.type === "message_delta") {
|
|
65
64
|
outputTokens = event.usage.output_tokens;
|
|
66
65
|
yield {
|
|
67
|
-
type: "
|
|
68
|
-
id,
|
|
66
|
+
type: "RUN_FINISHED",
|
|
67
|
+
runId: id,
|
|
69
68
|
model,
|
|
70
69
|
timestamp: Date.now(),
|
|
71
70
|
finishReason: event.delta.stop_reason,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"summarize.js","sources":["../../../src/adapters/summarize.ts"],"sourcesContent":["import { BaseSummarizeAdapter } from '@tanstack/ai/adapters'\nimport {\n createAnthropicClient,\n generateId,\n getAnthropicApiKeyFromEnv,\n} from '../utils'\nimport type { ANTHROPIC_MODELS } from '../model-meta'\nimport type {\n StreamChunk,\n SummarizationOptions,\n SummarizationResult,\n} from '@tanstack/ai'\nimport type { AnthropicClientConfig } from '../utils'\n\n/**\n * Configuration for Anthropic summarize adapter\n */\nexport interface AnthropicSummarizeConfig extends AnthropicClientConfig {}\n\n/**\n * Anthropic-specific provider options for summarization\n */\nexport interface AnthropicSummarizeProviderOptions {\n /** Temperature for response generation (0-1) */\n temperature?: number\n /** Maximum tokens in the response */\n maxTokens?: number\n}\n\n/** Model type for Anthropic summarization */\nexport type AnthropicSummarizeModel = (typeof ANTHROPIC_MODELS)[number]\n\n/**\n * Anthropic Summarize Adapter\n *\n * Tree-shakeable adapter for Anthropic summarization functionality.\n * Import only what you need for smaller bundle sizes.\n */\nexport class AnthropicSummarizeAdapter<\n TModel extends AnthropicSummarizeModel,\n> extends BaseSummarizeAdapter<TModel, AnthropicSummarizeProviderOptions> {\n readonly kind = 'summarize' as const\n readonly name = 'anthropic' as const\n\n private client: ReturnType<typeof createAnthropicClient>\n\n constructor(config: AnthropicSummarizeConfig, model: TModel) {\n super({}, model)\n this.client = createAnthropicClient(config)\n }\n\n async summarize(options: SummarizationOptions): Promise<SummarizationResult> {\n const systemPrompt = this.buildSummarizationPrompt(options)\n\n const response = await this.client.messages.create({\n model: options.model,\n messages: [{ role: 'user', content: options.text }],\n system: systemPrompt,\n max_tokens: options.maxLength || 500,\n temperature: 0.3,\n stream: false,\n })\n\n const content = response.content\n .map((c) => (c.type === 'text' ? c.text : ''))\n .join('')\n\n return {\n id: response.id,\n model: response.model,\n summary: content,\n usage: {\n promptTokens: response.usage.input_tokens,\n completionTokens: response.usage.output_tokens,\n totalTokens: response.usage.input_tokens + response.usage.output_tokens,\n },\n }\n }\n\n async *summarizeStream(\n options: SummarizationOptions,\n ): AsyncIterable<StreamChunk> {\n const systemPrompt = this.buildSummarizationPrompt(options)\n const id = generateId(this.name)\n const model = options.model\n let accumulatedContent = ''\n let inputTokens = 0\n let outputTokens = 0\n\n const stream = await this.client.messages.create({\n model: options.model,\n messages: [{ role: 'user', content: options.text }],\n system: systemPrompt,\n max_tokens: options.maxLength || 500,\n temperature: 0.3,\n stream: true,\n })\n\n for await (const event of stream) {\n if (event.type === 'message_start') {\n inputTokens = event.message.usage.input_tokens\n } else if (event.type === 'content_block_delta') {\n if (event.delta.type === 'text_delta') {\n const delta = event.delta.text\n accumulatedContent += delta\n yield {\n type: '
|
|
1
|
+
{"version":3,"file":"summarize.js","sources":["../../../src/adapters/summarize.ts"],"sourcesContent":["import { BaseSummarizeAdapter } from '@tanstack/ai/adapters'\nimport {\n createAnthropicClient,\n generateId,\n getAnthropicApiKeyFromEnv,\n} from '../utils'\nimport type { ANTHROPIC_MODELS } from '../model-meta'\nimport type {\n StreamChunk,\n SummarizationOptions,\n SummarizationResult,\n} from '@tanstack/ai'\nimport type { AnthropicClientConfig } from '../utils'\n\n/**\n * Configuration for Anthropic summarize adapter\n */\nexport interface AnthropicSummarizeConfig extends AnthropicClientConfig {}\n\n/**\n * Anthropic-specific provider options for summarization\n */\nexport interface AnthropicSummarizeProviderOptions {\n /** Temperature for response generation (0-1) */\n temperature?: number\n /** Maximum tokens in the response */\n maxTokens?: number\n}\n\n/** Model type for Anthropic summarization */\nexport type AnthropicSummarizeModel = (typeof ANTHROPIC_MODELS)[number]\n\n/**\n * Anthropic Summarize Adapter\n *\n * Tree-shakeable adapter for Anthropic summarization functionality.\n * Import only what you need for smaller bundle sizes.\n */\nexport class AnthropicSummarizeAdapter<\n TModel extends AnthropicSummarizeModel,\n> extends BaseSummarizeAdapter<TModel, AnthropicSummarizeProviderOptions> {\n readonly kind = 'summarize' as const\n readonly name = 'anthropic' as const\n\n private client: ReturnType<typeof createAnthropicClient>\n\n constructor(config: AnthropicSummarizeConfig, model: TModel) {\n super({}, model)\n this.client = createAnthropicClient(config)\n }\n\n async summarize(options: SummarizationOptions): Promise<SummarizationResult> {\n const systemPrompt = this.buildSummarizationPrompt(options)\n\n const response = await this.client.messages.create({\n model: options.model,\n messages: [{ role: 'user', content: options.text }],\n system: systemPrompt,\n max_tokens: options.maxLength || 500,\n temperature: 0.3,\n stream: false,\n })\n\n const content = response.content\n .map((c) => (c.type === 'text' ? c.text : ''))\n .join('')\n\n return {\n id: response.id,\n model: response.model,\n summary: content,\n usage: {\n promptTokens: response.usage.input_tokens,\n completionTokens: response.usage.output_tokens,\n totalTokens: response.usage.input_tokens + response.usage.output_tokens,\n },\n }\n }\n\n async *summarizeStream(\n options: SummarizationOptions,\n ): AsyncIterable<StreamChunk> {\n const systemPrompt = this.buildSummarizationPrompt(options)\n const id = generateId(this.name)\n const model = options.model\n let accumulatedContent = ''\n let inputTokens = 0\n let outputTokens = 0\n\n const stream = await this.client.messages.create({\n model: options.model,\n messages: [{ role: 'user', content: options.text }],\n system: systemPrompt,\n max_tokens: options.maxLength || 500,\n temperature: 0.3,\n stream: true,\n })\n\n for await (const event of stream) {\n if (event.type === 'message_start') {\n inputTokens = event.message.usage.input_tokens\n } else if (event.type === 'content_block_delta') {\n if (event.delta.type === 'text_delta') {\n const delta = event.delta.text\n accumulatedContent += delta\n yield {\n type: 'TEXT_MESSAGE_CONTENT',\n messageId: id,\n model,\n timestamp: Date.now(),\n delta,\n content: accumulatedContent,\n }\n }\n } else if (event.type === 'message_delta') {\n outputTokens = event.usage.output_tokens\n yield {\n type: 'RUN_FINISHED',\n runId: id,\n model,\n timestamp: Date.now(),\n finishReason: event.delta.stop_reason as\n | 'stop'\n | 'length'\n | 'content_filter'\n | null,\n usage: {\n promptTokens: inputTokens,\n completionTokens: outputTokens,\n totalTokens: inputTokens + outputTokens,\n },\n }\n }\n }\n }\n\n private buildSummarizationPrompt(options: SummarizationOptions): string {\n let prompt = 'You are a professional summarizer. '\n\n switch (options.style) {\n case 'bullet-points':\n prompt += 'Provide a summary in bullet point format. '\n break\n case 'paragraph':\n prompt += 'Provide a summary in paragraph format. '\n break\n case 'concise':\n prompt += 'Provide a very concise summary in 1-2 sentences. '\n break\n default:\n prompt += 'Provide a clear and concise summary. '\n }\n\n if (options.focus && options.focus.length > 0) {\n prompt += `Focus on the following aspects: ${options.focus.join(', ')}. `\n }\n\n if (options.maxLength) {\n prompt += `Keep the summary under ${options.maxLength} tokens. `\n }\n\n return prompt\n }\n}\n\n/**\n * Creates an Anthropic summarize adapter with explicit API key.\n * Type resolution happens here at the call site.\n *\n * @param model - The model name (e.g., 'claude-sonnet-4-5', 'claude-3-5-haiku-latest')\n * @param apiKey - Your Anthropic API key\n * @param config - Optional additional configuration\n * @returns Configured Anthropic summarize adapter instance with resolved types\n */\nexport function createAnthropicSummarize<\n TModel extends AnthropicSummarizeModel,\n>(\n model: TModel,\n apiKey: string,\n config?: Omit<AnthropicSummarizeConfig, 'apiKey'>,\n): AnthropicSummarizeAdapter<TModel> {\n return new AnthropicSummarizeAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates an Anthropic summarize adapter with automatic API key detection.\n * Type resolution happens here at the call site.\n *\n * @param model - The model name (e.g., 'claude-sonnet-4-5', 'claude-3-5-haiku-latest')\n * @param config - Optional configuration (excluding apiKey which is auto-detected)\n * @returns Configured Anthropic summarize adapter instance with resolved types\n */\nexport function anthropicSummarize<TModel extends AnthropicSummarizeModel>(\n model: TModel,\n config?: Omit<AnthropicSummarizeConfig, 'apiKey'>,\n): AnthropicSummarizeAdapter<TModel> {\n const apiKey = getAnthropicApiKeyFromEnv()\n return createAnthropicSummarize(model, apiKey, config)\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -27,8 +27,7 @@ class AnthropicTextAdapter extends BaseTextAdapter {
|
|
|
27
27
|
} catch (error) {
|
|
28
28
|
const err = error;
|
|
29
29
|
yield {
|
|
30
|
-
type: "
|
|
31
|
-
id: generateId(this.name),
|
|
30
|
+
type: "RUN_ERROR",
|
|
32
31
|
model: options.model,
|
|
33
32
|
timestamp: Date.now(),
|
|
34
33
|
error: {
|
|
@@ -165,16 +164,15 @@ class AnthropicTextAdapter extends BaseTextAdapter {
|
|
|
165
164
|
const imageSource = part.source.type === "data" ? {
|
|
166
165
|
type: "base64",
|
|
167
166
|
data: part.source.value,
|
|
168
|
-
media_type:
|
|
167
|
+
media_type: part.source.mimeType
|
|
169
168
|
} : {
|
|
170
169
|
type: "url",
|
|
171
170
|
url: part.source.value
|
|
172
171
|
};
|
|
173
|
-
const { mediaType: _mediaType, ...meta } = metadata || {};
|
|
174
172
|
return {
|
|
175
173
|
type: "image",
|
|
176
174
|
source: imageSource,
|
|
177
|
-
...
|
|
175
|
+
...metadata
|
|
178
176
|
};
|
|
179
177
|
}
|
|
180
178
|
case "document": {
|
|
@@ -182,7 +180,7 @@ class AnthropicTextAdapter extends BaseTextAdapter {
|
|
|
182
180
|
const docSource = part.source.type === "data" ? {
|
|
183
181
|
type: "base64",
|
|
184
182
|
data: part.source.value,
|
|
185
|
-
media_type:
|
|
183
|
+
media_type: part.source.mimeType
|
|
186
184
|
} : {
|
|
187
185
|
type: "url",
|
|
188
186
|
url: part.source.value
|
|
@@ -279,38 +277,70 @@ class AnthropicTextAdapter extends BaseTextAdapter {
|
|
|
279
277
|
const timestamp = Date.now();
|
|
280
278
|
const toolCallsMap = /* @__PURE__ */ new Map();
|
|
281
279
|
let currentToolIndex = -1;
|
|
280
|
+
const runId = genId();
|
|
281
|
+
const messageId = genId();
|
|
282
|
+
let stepId = null;
|
|
283
|
+
let hasEmittedRunStarted = false;
|
|
284
|
+
let hasEmittedTextMessageStart = false;
|
|
282
285
|
try {
|
|
283
286
|
for await (const event of stream) {
|
|
287
|
+
if (!hasEmittedRunStarted) {
|
|
288
|
+
hasEmittedRunStarted = true;
|
|
289
|
+
yield {
|
|
290
|
+
type: "RUN_STARTED",
|
|
291
|
+
runId,
|
|
292
|
+
model,
|
|
293
|
+
timestamp
|
|
294
|
+
};
|
|
295
|
+
}
|
|
284
296
|
if (event.type === "content_block_start") {
|
|
285
297
|
if (event.content_block.type === "tool_use") {
|
|
286
298
|
currentToolIndex++;
|
|
287
299
|
toolCallsMap.set(currentToolIndex, {
|
|
288
300
|
id: event.content_block.id,
|
|
289
301
|
name: event.content_block.name,
|
|
290
|
-
input: ""
|
|
302
|
+
input: "",
|
|
303
|
+
started: false
|
|
291
304
|
});
|
|
292
305
|
} else if (event.content_block.type === "thinking") {
|
|
293
306
|
accumulatedThinking = "";
|
|
307
|
+
stepId = genId();
|
|
308
|
+
yield {
|
|
309
|
+
type: "STEP_STARTED",
|
|
310
|
+
stepId,
|
|
311
|
+
model,
|
|
312
|
+
timestamp,
|
|
313
|
+
stepType: "thinking"
|
|
314
|
+
};
|
|
294
315
|
}
|
|
295
316
|
} else if (event.type === "content_block_delta") {
|
|
296
317
|
if (event.delta.type === "text_delta") {
|
|
318
|
+
if (!hasEmittedTextMessageStart) {
|
|
319
|
+
hasEmittedTextMessageStart = true;
|
|
320
|
+
yield {
|
|
321
|
+
type: "TEXT_MESSAGE_START",
|
|
322
|
+
messageId,
|
|
323
|
+
model,
|
|
324
|
+
timestamp,
|
|
325
|
+
role: "assistant"
|
|
326
|
+
};
|
|
327
|
+
}
|
|
297
328
|
const delta = event.delta.text;
|
|
298
329
|
accumulatedContent += delta;
|
|
299
330
|
yield {
|
|
300
|
-
type: "
|
|
301
|
-
|
|
331
|
+
type: "TEXT_MESSAGE_CONTENT",
|
|
332
|
+
messageId,
|
|
302
333
|
model,
|
|
303
334
|
timestamp,
|
|
304
335
|
delta,
|
|
305
|
-
content: accumulatedContent
|
|
306
|
-
role: "assistant"
|
|
336
|
+
content: accumulatedContent
|
|
307
337
|
};
|
|
308
338
|
} else if (event.delta.type === "thinking_delta") {
|
|
309
339
|
const delta = event.delta.thinking;
|
|
310
340
|
accumulatedThinking += delta;
|
|
311
341
|
yield {
|
|
312
|
-
type: "
|
|
313
|
-
|
|
342
|
+
type: "STEP_FINISHED",
|
|
343
|
+
stepId: stepId || genId(),
|
|
314
344
|
model,
|
|
315
345
|
timestamp,
|
|
316
346
|
delta,
|
|
@@ -319,47 +349,69 @@ class AnthropicTextAdapter extends BaseTextAdapter {
|
|
|
319
349
|
} else if (event.delta.type === "input_json_delta") {
|
|
320
350
|
const existing = toolCallsMap.get(currentToolIndex);
|
|
321
351
|
if (existing) {
|
|
352
|
+
if (!existing.started) {
|
|
353
|
+
existing.started = true;
|
|
354
|
+
yield {
|
|
355
|
+
type: "TOOL_CALL_START",
|
|
356
|
+
toolCallId: existing.id,
|
|
357
|
+
toolName: existing.name,
|
|
358
|
+
model,
|
|
359
|
+
timestamp,
|
|
360
|
+
index: currentToolIndex
|
|
361
|
+
};
|
|
362
|
+
}
|
|
322
363
|
existing.input += event.delta.partial_json;
|
|
323
364
|
yield {
|
|
324
|
-
type: "
|
|
325
|
-
|
|
365
|
+
type: "TOOL_CALL_ARGS",
|
|
366
|
+
toolCallId: existing.id,
|
|
326
367
|
model,
|
|
327
368
|
timestamp,
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
type: "function",
|
|
331
|
-
function: {
|
|
332
|
-
name: existing.name,
|
|
333
|
-
arguments: event.delta.partial_json
|
|
334
|
-
}
|
|
335
|
-
},
|
|
336
|
-
index: currentToolIndex
|
|
369
|
+
delta: event.delta.partial_json,
|
|
370
|
+
args: existing.input
|
|
337
371
|
};
|
|
338
372
|
}
|
|
339
373
|
}
|
|
340
374
|
} else if (event.type === "content_block_stop") {
|
|
341
375
|
const existing = toolCallsMap.get(currentToolIndex);
|
|
342
|
-
if (existing
|
|
376
|
+
if (existing) {
|
|
377
|
+
if (!existing.started) {
|
|
378
|
+
existing.started = true;
|
|
379
|
+
yield {
|
|
380
|
+
type: "TOOL_CALL_START",
|
|
381
|
+
toolCallId: existing.id,
|
|
382
|
+
toolName: existing.name,
|
|
383
|
+
model,
|
|
384
|
+
timestamp,
|
|
385
|
+
index: currentToolIndex
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
let parsedInput = {};
|
|
389
|
+
try {
|
|
390
|
+
parsedInput = existing.input ? JSON.parse(existing.input) : {};
|
|
391
|
+
} catch {
|
|
392
|
+
parsedInput = {};
|
|
393
|
+
}
|
|
343
394
|
yield {
|
|
344
|
-
type: "
|
|
345
|
-
|
|
395
|
+
type: "TOOL_CALL_END",
|
|
396
|
+
toolCallId: existing.id,
|
|
397
|
+
toolName: existing.name,
|
|
346
398
|
model,
|
|
347
399
|
timestamp,
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
400
|
+
input: parsedInput
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
if (hasEmittedTextMessageStart && accumulatedContent) {
|
|
404
|
+
yield {
|
|
405
|
+
type: "TEXT_MESSAGE_END",
|
|
406
|
+
messageId,
|
|
407
|
+
model,
|
|
408
|
+
timestamp
|
|
357
409
|
};
|
|
358
410
|
}
|
|
359
411
|
} else if (event.type === "message_stop") {
|
|
360
412
|
yield {
|
|
361
|
-
type: "
|
|
362
|
-
|
|
413
|
+
type: "RUN_FINISHED",
|
|
414
|
+
runId,
|
|
363
415
|
model,
|
|
364
416
|
timestamp,
|
|
365
417
|
finishReason: "stop"
|
|
@@ -369,8 +421,8 @@ class AnthropicTextAdapter extends BaseTextAdapter {
|
|
|
369
421
|
switch (event.delta.stop_reason) {
|
|
370
422
|
case "tool_use": {
|
|
371
423
|
yield {
|
|
372
|
-
type: "
|
|
373
|
-
|
|
424
|
+
type: "RUN_FINISHED",
|
|
425
|
+
runId,
|
|
374
426
|
model,
|
|
375
427
|
timestamp,
|
|
376
428
|
finishReason: "tool_calls",
|
|
@@ -384,8 +436,8 @@ class AnthropicTextAdapter extends BaseTextAdapter {
|
|
|
384
436
|
}
|
|
385
437
|
case "max_tokens": {
|
|
386
438
|
yield {
|
|
387
|
-
type: "
|
|
388
|
-
|
|
439
|
+
type: "RUN_ERROR",
|
|
440
|
+
runId,
|
|
389
441
|
model,
|
|
390
442
|
timestamp,
|
|
391
443
|
error: {
|
|
@@ -397,8 +449,8 @@ class AnthropicTextAdapter extends BaseTextAdapter {
|
|
|
397
449
|
}
|
|
398
450
|
default: {
|
|
399
451
|
yield {
|
|
400
|
-
type: "
|
|
401
|
-
|
|
452
|
+
type: "RUN_FINISHED",
|
|
453
|
+
runId,
|
|
402
454
|
model,
|
|
403
455
|
timestamp,
|
|
404
456
|
finishReason: "stop",
|
|
@@ -416,8 +468,8 @@ class AnthropicTextAdapter extends BaseTextAdapter {
|
|
|
416
468
|
} catch (error) {
|
|
417
469
|
const err = error;
|
|
418
470
|
yield {
|
|
419
|
-
type: "
|
|
420
|
-
|
|
471
|
+
type: "RUN_ERROR",
|
|
472
|
+
runId,
|
|
421
473
|
model,
|
|
422
474
|
timestamp,
|
|
423
475
|
error: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text.js","sources":["../../../src/adapters/text.ts"],"sourcesContent":["import { BaseTextAdapter } from '@tanstack/ai/adapters'\nimport { convertToolsToProviderFormat } from '../tools/tool-converter'\nimport { validateTextProviderOptions } from '../text/text-provider-options'\nimport {\n createAnthropicClient,\n generateId,\n getAnthropicApiKeyFromEnv,\n} from '../utils'\nimport type {\n ANTHROPIC_MODELS,\n AnthropicChatModelProviderOptionsByName,\n AnthropicModelInputModalitiesByName,\n} from '../model-meta'\nimport type {\n StructuredOutputOptions,\n StructuredOutputResult,\n} from '@tanstack/ai/adapters'\nimport type {\n Base64ImageSource,\n Base64PDFSource,\n DocumentBlockParam,\n ImageBlockParam,\n MessageParam,\n TextBlockParam,\n URLImageSource,\n URLPDFSource,\n} from '@anthropic-ai/sdk/resources/messages'\nimport type Anthropic_SDK from '@anthropic-ai/sdk'\nimport type {\n ContentPart,\n Modality,\n ModelMessage,\n StreamChunk,\n TextOptions,\n} from '@tanstack/ai'\nimport type {\n ExternalTextProviderOptions,\n InternalTextProviderOptions,\n} from '../text/text-provider-options'\nimport type {\n AnthropicDocumentMetadata,\n AnthropicImageMetadata,\n AnthropicMessageMetadataByModality,\n AnthropicTextMetadata,\n} from '../message-types'\nimport type { AnthropicClientConfig } from '../utils'\n\n/**\n * Configuration for Anthropic text adapter\n */\nexport interface AnthropicTextConfig extends AnthropicClientConfig {}\n\n/**\n * Anthropic-specific provider options for text/chat\n */\nexport type AnthropicTextProviderOptions = ExternalTextProviderOptions\n\ntype AnthropicContentBlocks =\n Extract<MessageParam['content'], Array<unknown>> extends Array<infer Block>\n ? Array<Block>\n : never\ntype AnthropicContentBlock =\n AnthropicContentBlocks extends Array<infer Block> ? Block : never\n\n// ===========================\n// Type Resolution Helpers\n// ===========================\n\n/**\n * Resolve provider options for a specific model.\n * If the model has explicit options in the map, use those; otherwise use base options.\n */\ntype ResolveProviderOptions<TModel extends string> =\n TModel extends keyof AnthropicChatModelProviderOptionsByName\n ? AnthropicChatModelProviderOptionsByName[TModel]\n : AnthropicTextProviderOptions\n\n/**\n * Resolve input modalities for a specific model.\n * If the model has explicit modalities in the map, use those; otherwise use default.\n */\ntype ResolveInputModalities<TModel extends string> =\n TModel extends keyof AnthropicModelInputModalitiesByName\n ? AnthropicModelInputModalitiesByName[TModel]\n : readonly ['text', 'image', 'document']\n\n// ===========================\n// Adapter Implementation\n// ===========================\n\n/**\n * Anthropic Text (Chat) Adapter\n *\n * Tree-shakeable adapter for Anthropic chat/text completion functionality.\n * Import only what you need for smaller bundle sizes.\n */\nexport class AnthropicTextAdapter<\n TModel extends (typeof ANTHROPIC_MODELS)[number],\n TProviderOptions extends object = ResolveProviderOptions<TModel>,\n TInputModalities extends ReadonlyArray<Modality> =\n ResolveInputModalities<TModel>,\n> extends BaseTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n AnthropicMessageMetadataByModality\n> {\n readonly kind = 'text' as const\n readonly name = 'anthropic' as const\n\n private client: Anthropic_SDK\n\n constructor(config: AnthropicTextConfig, model: TModel) {\n super({}, model)\n this.client = createAnthropicClient(config)\n }\n\n async *chatStream(\n options: TextOptions<AnthropicTextProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n try {\n const requestParams = this.mapCommonOptionsToAnthropic(options)\n\n const stream = await this.client.beta.messages.create(\n { ...requestParams, stream: true },\n {\n signal: options.request?.signal,\n headers: options.request?.headers,\n },\n )\n\n yield* this.processAnthropicStream(stream, options.model, () =>\n generateId(this.name),\n )\n } catch (error: unknown) {\n const err = error as Error & { status?: number; code?: string }\n yield {\n type: 'error',\n id: generateId(this.name),\n model: options.model,\n timestamp: Date.now(),\n error: {\n message: err.message || 'Unknown error occurred',\n code: err.code || String(err.status),\n },\n }\n }\n }\n\n /**\n * Generate structured output using Anthropic's tool-based approach.\n * Anthropic doesn't have native structured output, so we use a tool with the schema\n * and force the model to call it.\n * The outputSchema is already JSON Schema (converted in the ai layer).\n */\n async structuredOutput(\n options: StructuredOutputOptions<AnthropicTextProviderOptions>,\n ): Promise<StructuredOutputResult<unknown>> {\n const { chatOptions, outputSchema } = options\n\n const requestParams = this.mapCommonOptionsToAnthropic(chatOptions)\n\n // Create a tool that will capture the structured output\n // Anthropic's SDK requires input_schema with type: 'object' literal\n const structuredOutputTool = {\n name: 'structured_output',\n description:\n 'Use this tool to provide your response in the required structured format.',\n input_schema: {\n type: 'object' as const,\n properties: outputSchema.properties ?? {},\n required: outputSchema.required ?? [],\n },\n }\n\n try {\n // Make non-streaming request with tool_choice forced to our structured output tool\n const response = await this.client.messages.create(\n {\n ...requestParams,\n stream: false,\n tools: [structuredOutputTool],\n tool_choice: { type: 'tool', name: 'structured_output' },\n },\n {\n signal: chatOptions.request?.signal,\n headers: chatOptions.request?.headers,\n },\n )\n\n // Extract the tool use content from the response\n let parsed: unknown = null\n let rawText = ''\n\n for (const block of response.content) {\n if (block.type === 'tool_use' && block.name === 'structured_output') {\n parsed = block.input\n rawText = JSON.stringify(block.input)\n break\n }\n }\n\n if (parsed === null) {\n // Fallback: try to extract text content and parse as JSON\n rawText = response.content\n .map((b) => {\n if (b.type === 'text') {\n return b.text\n }\n return ''\n })\n .join('')\n try {\n parsed = JSON.parse(rawText)\n } catch {\n throw new Error(\n `Failed to extract structured output from response. Content: ${rawText.slice(0, 200)}${rawText.length > 200 ? '...' : ''}`,\n )\n }\n }\n\n return {\n data: parsed,\n rawText,\n }\n } catch (error: unknown) {\n const err = error as Error\n throw new Error(\n `Structured output generation failed: ${err.message || 'Unknown error occurred'}`,\n )\n }\n }\n\n private mapCommonOptionsToAnthropic(\n options: TextOptions<AnthropicTextProviderOptions>,\n ) {\n const modelOptions = options.modelOptions as\n | InternalTextProviderOptions\n | undefined\n\n const formattedMessages = this.formatMessages(options.messages)\n const tools = options.tools\n ? convertToolsToProviderFormat(options.tools)\n : undefined\n\n const validProviderOptions: Partial<InternalTextProviderOptions> = {}\n if (modelOptions) {\n const validKeys: Array<keyof InternalTextProviderOptions> = [\n 'container',\n 'context_management',\n 'mcp_servers',\n 'service_tier',\n 'stop_sequences',\n 'system',\n 'thinking',\n 'tool_choice',\n 'top_k',\n ]\n for (const key of validKeys) {\n if (key in modelOptions) {\n const value = modelOptions[key]\n if (key === 'tool_choice' && typeof value === 'string') {\n ;(validProviderOptions as Record<string, unknown>)[key] = {\n type: value,\n }\n } else {\n ;(validProviderOptions as Record<string, unknown>)[key] = value\n }\n }\n }\n }\n\n const thinkingBudget =\n validProviderOptions.thinking?.type === 'enabled'\n ? validProviderOptions.thinking.budget_tokens\n : undefined\n const defaultMaxTokens = options.maxTokens || 1024\n const maxTokens =\n thinkingBudget && thinkingBudget >= defaultMaxTokens\n ? thinkingBudget + 1\n : defaultMaxTokens\n\n const requestParams: InternalTextProviderOptions = {\n model: options.model,\n max_tokens: maxTokens,\n temperature: options.temperature,\n top_p: options.topP,\n messages: formattedMessages,\n system: options.systemPrompts?.join('\\n'),\n tools: tools,\n ...validProviderOptions,\n }\n validateTextProviderOptions(requestParams)\n return requestParams\n }\n\n private convertContentPartToAnthropic(\n part: ContentPart,\n ): TextBlockParam | ImageBlockParam | DocumentBlockParam {\n switch (part.type) {\n case 'text': {\n const metadata = part.metadata as AnthropicTextMetadata | undefined\n return {\n type: 'text',\n text: part.content,\n ...metadata,\n }\n }\n\n case 'image': {\n const metadata = part.metadata as AnthropicImageMetadata | undefined\n const imageSource: Base64ImageSource | URLImageSource =\n part.source.type === 'data'\n ? {\n type: 'base64',\n data: part.source.value,\n media_type: metadata?.mediaType ?? 'image/jpeg',\n }\n : {\n type: 'url',\n url: part.source.value,\n }\n const { mediaType: _mediaType, ...meta } = metadata || {}\n return {\n type: 'image',\n source: imageSource,\n ...meta,\n }\n }\n case 'document': {\n const metadata = part.metadata as AnthropicDocumentMetadata | undefined\n const docSource: Base64PDFSource | URLPDFSource =\n part.source.type === 'data'\n ? {\n type: 'base64',\n data: part.source.value,\n media_type: 'application/pdf',\n }\n : {\n type: 'url',\n url: part.source.value,\n }\n return {\n type: 'document',\n source: docSource,\n ...metadata,\n }\n }\n case 'audio':\n case 'video':\n throw new Error(\n `Anthropic does not support ${part.type} content directly`,\n )\n default: {\n const _exhaustiveCheck: never = part\n throw new Error(\n `Unsupported content part type: ${(_exhaustiveCheck as ContentPart).type}`,\n )\n }\n }\n }\n\n private formatMessages(\n messages: Array<ModelMessage>,\n ): InternalTextProviderOptions['messages'] {\n const formattedMessages: InternalTextProviderOptions['messages'] = []\n\n for (const message of messages) {\n const role = message.role\n\n if (role === 'tool' && message.toolCallId) {\n formattedMessages.push({\n role: 'user',\n content: [\n {\n type: 'tool_result',\n tool_use_id: message.toolCallId,\n content:\n typeof message.content === 'string' ? message.content : '',\n },\n ],\n })\n continue\n }\n\n if (role === 'assistant' && message.toolCalls?.length) {\n const contentBlocks: AnthropicContentBlocks = []\n\n if (message.content) {\n const content =\n typeof message.content === 'string' ? message.content : ''\n const textBlock: AnthropicContentBlock = {\n type: 'text',\n text: content,\n }\n contentBlocks.push(textBlock)\n }\n\n for (const toolCall of message.toolCalls) {\n let parsedInput: unknown = {}\n try {\n parsedInput = toolCall.function.arguments\n ? JSON.parse(toolCall.function.arguments)\n : {}\n } catch {\n parsedInput = toolCall.function.arguments\n }\n\n const toolUseBlock: AnthropicContentBlock = {\n type: 'tool_use',\n id: toolCall.id,\n name: toolCall.function.name,\n input: parsedInput,\n }\n contentBlocks.push(toolUseBlock)\n }\n\n formattedMessages.push({\n role: 'assistant',\n content: contentBlocks,\n })\n\n continue\n }\n\n if (role === 'user' && Array.isArray(message.content)) {\n const contentBlocks = message.content.map((part) =>\n this.convertContentPartToAnthropic(part),\n )\n formattedMessages.push({\n role: 'user',\n content: contentBlocks,\n })\n continue\n }\n\n formattedMessages.push({\n role: role === 'assistant' ? 'assistant' : 'user',\n content:\n typeof message.content === 'string'\n ? message.content\n : message.content\n ? message.content.map((c) =>\n this.convertContentPartToAnthropic(c),\n )\n : '',\n })\n }\n\n return formattedMessages\n }\n\n private async *processAnthropicStream(\n stream: AsyncIterable<Anthropic_SDK.Beta.BetaRawMessageStreamEvent>,\n model: string,\n genId: () => string,\n ): AsyncIterable<StreamChunk> {\n let accumulatedContent = ''\n let accumulatedThinking = ''\n const timestamp = Date.now()\n const toolCallsMap = new Map<\n number,\n { id: string; name: string; input: string }\n >()\n let currentToolIndex = -1\n\n try {\n for await (const event of stream) {\n if (event.type === 'content_block_start') {\n if (event.content_block.type === 'tool_use') {\n currentToolIndex++\n toolCallsMap.set(currentToolIndex, {\n id: event.content_block.id,\n name: event.content_block.name,\n input: '',\n })\n } else if (event.content_block.type === 'thinking') {\n accumulatedThinking = ''\n }\n } else if (event.type === 'content_block_delta') {\n if (event.delta.type === 'text_delta') {\n const delta = event.delta.text\n accumulatedContent += delta\n yield {\n type: 'content',\n id: genId(),\n model: model,\n timestamp,\n delta,\n content: accumulatedContent,\n role: 'assistant',\n }\n } else if (event.delta.type === 'thinking_delta') {\n const delta = event.delta.thinking\n accumulatedThinking += delta\n yield {\n type: 'thinking',\n id: genId(),\n model: model,\n timestamp,\n delta,\n content: accumulatedThinking,\n }\n } else if (event.delta.type === 'input_json_delta') {\n const existing = toolCallsMap.get(currentToolIndex)\n if (existing) {\n existing.input += event.delta.partial_json\n\n yield {\n type: 'tool_call',\n id: genId(),\n model: model,\n timestamp,\n toolCall: {\n id: existing.id,\n type: 'function',\n function: {\n name: existing.name,\n arguments: event.delta.partial_json,\n },\n },\n index: currentToolIndex,\n }\n }\n }\n } else if (event.type === 'content_block_stop') {\n const existing = toolCallsMap.get(currentToolIndex)\n if (existing && existing.input === '') {\n yield {\n type: 'tool_call',\n id: genId(),\n model: model,\n timestamp,\n toolCall: {\n id: existing.id,\n type: 'function',\n function: {\n name: existing.name,\n arguments: '{}',\n },\n },\n index: currentToolIndex,\n }\n }\n } else if (event.type === 'message_stop') {\n yield {\n type: 'done',\n id: genId(),\n model: model,\n timestamp,\n finishReason: 'stop',\n }\n } else if (event.type === 'message_delta') {\n if (event.delta.stop_reason) {\n switch (event.delta.stop_reason) {\n case 'tool_use': {\n yield {\n type: 'done',\n id: genId(),\n model: model,\n timestamp,\n finishReason: 'tool_calls',\n usage: {\n promptTokens: event.usage.input_tokens || 0,\n completionTokens: event.usage.output_tokens || 0,\n totalTokens:\n (event.usage.input_tokens || 0) +\n (event.usage.output_tokens || 0),\n },\n }\n break\n }\n case 'max_tokens': {\n yield {\n type: 'error',\n id: genId(),\n model: model,\n timestamp,\n error: {\n message:\n 'The response was cut off because the maximum token limit was reached.',\n code: 'max_tokens',\n },\n }\n break\n }\n default: {\n yield {\n type: 'done',\n id: genId(),\n model: model,\n timestamp,\n finishReason: 'stop',\n usage: {\n promptTokens: event.usage.input_tokens || 0,\n completionTokens: event.usage.output_tokens || 0,\n totalTokens:\n (event.usage.input_tokens || 0) +\n (event.usage.output_tokens || 0),\n },\n }\n }\n }\n }\n }\n }\n } catch (error: unknown) {\n const err = error as Error & { status?: number; code?: string }\n\n yield {\n type: 'error',\n id: genId(),\n model: model,\n timestamp,\n error: {\n message: err.message || 'Unknown error occurred',\n code: err.code || String(err.status),\n },\n }\n }\n }\n}\n\n/**\n * Creates an Anthropic chat adapter with explicit API key.\n * Type resolution happens here at the call site.\n */\nexport function createAnthropicChat<\n TModel extends (typeof ANTHROPIC_MODELS)[number],\n>(\n model: TModel,\n apiKey: string,\n config?: Omit<AnthropicTextConfig, 'apiKey'>,\n): AnthropicTextAdapter<\n TModel,\n ResolveProviderOptions<TModel>,\n ResolveInputModalities<TModel>\n> {\n return new AnthropicTextAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates an Anthropic text adapter with automatic API key detection.\n * Type resolution happens here at the call site.\n */\nexport function anthropicText<TModel extends (typeof ANTHROPIC_MODELS)[number]>(\n model: TModel,\n config?: Omit<AnthropicTextConfig, 'apiKey'>,\n): AnthropicTextAdapter<\n TModel,\n ResolveProviderOptions<TModel>,\n ResolveInputModalities<TModel>\n> {\n const apiKey = getAnthropicApiKeyFromEnv()\n return createAnthropicChat(model, apiKey, config)\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"text.js","sources":["../../../src/adapters/text.ts"],"sourcesContent":["import { BaseTextAdapter } from '@tanstack/ai/adapters'\nimport { convertToolsToProviderFormat } from '../tools/tool-converter'\nimport { validateTextProviderOptions } from '../text/text-provider-options'\nimport {\n createAnthropicClient,\n generateId,\n getAnthropicApiKeyFromEnv,\n} from '../utils'\nimport type {\n ANTHROPIC_MODELS,\n AnthropicChatModelProviderOptionsByName,\n AnthropicModelInputModalitiesByName,\n} from '../model-meta'\nimport type {\n StructuredOutputOptions,\n StructuredOutputResult,\n} from '@tanstack/ai/adapters'\nimport type {\n Base64ImageSource,\n Base64PDFSource,\n DocumentBlockParam,\n ImageBlockParam,\n MessageParam,\n TextBlockParam,\n URLImageSource,\n URLPDFSource,\n} from '@anthropic-ai/sdk/resources/messages'\nimport type Anthropic_SDK from '@anthropic-ai/sdk'\nimport type {\n ContentPart,\n Modality,\n ModelMessage,\n StreamChunk,\n TextOptions,\n} from '@tanstack/ai'\nimport type {\n ExternalTextProviderOptions,\n InternalTextProviderOptions,\n} from '../text/text-provider-options'\nimport type {\n AnthropicDocumentMetadata,\n AnthropicImageMetadata,\n AnthropicMessageMetadataByModality,\n AnthropicTextMetadata,\n} from '../message-types'\nimport type { AnthropicClientConfig } from '../utils'\n\n/**\n * Configuration for Anthropic text adapter\n */\nexport interface AnthropicTextConfig extends AnthropicClientConfig {}\n\n/**\n * Anthropic-specific provider options for text/chat\n */\nexport type AnthropicTextProviderOptions = ExternalTextProviderOptions\n\ntype AnthropicContentBlocks =\n Extract<MessageParam['content'], Array<unknown>> extends Array<infer Block>\n ? Array<Block>\n : never\ntype AnthropicContentBlock =\n AnthropicContentBlocks extends Array<infer Block> ? Block : never\n\n// ===========================\n// Type Resolution Helpers\n// ===========================\n\n/**\n * Resolve provider options for a specific model.\n * If the model has explicit options in the map, use those; otherwise use base options.\n */\ntype ResolveProviderOptions<TModel extends string> =\n TModel extends keyof AnthropicChatModelProviderOptionsByName\n ? AnthropicChatModelProviderOptionsByName[TModel]\n : AnthropicTextProviderOptions\n\n/**\n * Resolve input modalities for a specific model.\n * If the model has explicit modalities in the map, use those; otherwise use default.\n */\ntype ResolveInputModalities<TModel extends string> =\n TModel extends keyof AnthropicModelInputModalitiesByName\n ? AnthropicModelInputModalitiesByName[TModel]\n : readonly ['text', 'image', 'document']\n\n// ===========================\n// Adapter Implementation\n// ===========================\n\n/**\n * Anthropic Text (Chat) Adapter\n *\n * Tree-shakeable adapter for Anthropic chat/text completion functionality.\n * Import only what you need for smaller bundle sizes.\n */\nexport class AnthropicTextAdapter<\n TModel extends (typeof ANTHROPIC_MODELS)[number],\n TProviderOptions extends object = ResolveProviderOptions<TModel>,\n TInputModalities extends ReadonlyArray<Modality> =\n ResolveInputModalities<TModel>,\n> extends BaseTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n AnthropicMessageMetadataByModality\n> {\n readonly kind = 'text' as const\n readonly name = 'anthropic' as const\n\n private client: Anthropic_SDK\n\n constructor(config: AnthropicTextConfig, model: TModel) {\n super({}, model)\n this.client = createAnthropicClient(config)\n }\n\n async *chatStream(\n options: TextOptions<AnthropicTextProviderOptions>,\n ): AsyncIterable<StreamChunk> {\n try {\n const requestParams = this.mapCommonOptionsToAnthropic(options)\n\n const stream = await this.client.beta.messages.create(\n { ...requestParams, stream: true },\n {\n signal: options.request?.signal,\n headers: options.request?.headers,\n },\n )\n\n yield* this.processAnthropicStream(stream, options.model, () =>\n generateId(this.name),\n )\n } catch (error: unknown) {\n const err = error as Error & { status?: number; code?: string }\n yield {\n type: 'RUN_ERROR',\n model: options.model,\n timestamp: Date.now(),\n error: {\n message: err.message || 'Unknown error occurred',\n code: err.code || String(err.status),\n },\n }\n }\n }\n\n /**\n * Generate structured output using Anthropic's tool-based approach.\n * Anthropic doesn't have native structured output, so we use a tool with the schema\n * and force the model to call it.\n * The outputSchema is already JSON Schema (converted in the ai layer).\n */\n async structuredOutput(\n options: StructuredOutputOptions<AnthropicTextProviderOptions>,\n ): Promise<StructuredOutputResult<unknown>> {\n const { chatOptions, outputSchema } = options\n\n const requestParams = this.mapCommonOptionsToAnthropic(chatOptions)\n\n // Create a tool that will capture the structured output\n // Anthropic's SDK requires input_schema with type: 'object' literal\n const structuredOutputTool = {\n name: 'structured_output',\n description:\n 'Use this tool to provide your response in the required structured format.',\n input_schema: {\n type: 'object' as const,\n properties: outputSchema.properties ?? {},\n required: outputSchema.required ?? [],\n },\n }\n\n try {\n // Make non-streaming request with tool_choice forced to our structured output tool\n const response = await this.client.messages.create(\n {\n ...requestParams,\n stream: false,\n tools: [structuredOutputTool],\n tool_choice: { type: 'tool', name: 'structured_output' },\n },\n {\n signal: chatOptions.request?.signal,\n headers: chatOptions.request?.headers,\n },\n )\n\n // Extract the tool use content from the response\n let parsed: unknown = null\n let rawText = ''\n\n for (const block of response.content) {\n if (block.type === 'tool_use' && block.name === 'structured_output') {\n parsed = block.input\n rawText = JSON.stringify(block.input)\n break\n }\n }\n\n if (parsed === null) {\n // Fallback: try to extract text content and parse as JSON\n rawText = response.content\n .map((b) => {\n if (b.type === 'text') {\n return b.text\n }\n return ''\n })\n .join('')\n try {\n parsed = JSON.parse(rawText)\n } catch {\n throw new Error(\n `Failed to extract structured output from response. Content: ${rawText.slice(0, 200)}${rawText.length > 200 ? '...' : ''}`,\n )\n }\n }\n\n return {\n data: parsed,\n rawText,\n }\n } catch (error: unknown) {\n const err = error as Error\n throw new Error(\n `Structured output generation failed: ${err.message || 'Unknown error occurred'}`,\n )\n }\n }\n\n private mapCommonOptionsToAnthropic(\n options: TextOptions<AnthropicTextProviderOptions>,\n ) {\n const modelOptions = options.modelOptions as\n | InternalTextProviderOptions\n | undefined\n\n const formattedMessages = this.formatMessages(options.messages)\n const tools = options.tools\n ? convertToolsToProviderFormat(options.tools)\n : undefined\n\n const validProviderOptions: Partial<InternalTextProviderOptions> = {}\n if (modelOptions) {\n const validKeys: Array<keyof InternalTextProviderOptions> = [\n 'container',\n 'context_management',\n 'mcp_servers',\n 'service_tier',\n 'stop_sequences',\n 'system',\n 'thinking',\n 'tool_choice',\n 'top_k',\n ]\n for (const key of validKeys) {\n if (key in modelOptions) {\n const value = modelOptions[key]\n if (key === 'tool_choice' && typeof value === 'string') {\n ;(validProviderOptions as Record<string, unknown>)[key] = {\n type: value,\n }\n } else {\n ;(validProviderOptions as Record<string, unknown>)[key] = value\n }\n }\n }\n }\n\n const thinkingBudget =\n validProviderOptions.thinking?.type === 'enabled'\n ? validProviderOptions.thinking.budget_tokens\n : undefined\n const defaultMaxTokens = options.maxTokens || 1024\n const maxTokens =\n thinkingBudget && thinkingBudget >= defaultMaxTokens\n ? thinkingBudget + 1\n : defaultMaxTokens\n\n const requestParams: InternalTextProviderOptions = {\n model: options.model,\n max_tokens: maxTokens,\n temperature: options.temperature,\n top_p: options.topP,\n messages: formattedMessages,\n system: options.systemPrompts?.join('\\n'),\n tools: tools,\n ...validProviderOptions,\n }\n validateTextProviderOptions(requestParams)\n return requestParams\n }\n\n private convertContentPartToAnthropic(\n part: ContentPart,\n ): TextBlockParam | ImageBlockParam | DocumentBlockParam {\n switch (part.type) {\n case 'text': {\n const metadata = part.metadata as AnthropicTextMetadata | undefined\n return {\n type: 'text',\n text: part.content,\n ...metadata,\n }\n }\n\n case 'image': {\n const metadata = part.metadata as AnthropicImageMetadata | undefined\n const imageSource: Base64ImageSource | URLImageSource =\n part.source.type === 'data'\n ? {\n type: 'base64',\n data: part.source.value,\n media_type: part.source.mimeType as\n | 'image/jpeg'\n | 'image/png'\n | 'image/gif'\n | 'image/webp',\n }\n : {\n type: 'url',\n url: part.source.value,\n }\n return {\n type: 'image',\n source: imageSource,\n ...metadata,\n }\n }\n case 'document': {\n const metadata = part.metadata as AnthropicDocumentMetadata | undefined\n const docSource: Base64PDFSource | URLPDFSource =\n part.source.type === 'data'\n ? {\n type: 'base64',\n data: part.source.value,\n media_type: part.source.mimeType as 'application/pdf',\n }\n : {\n type: 'url',\n url: part.source.value,\n }\n return {\n type: 'document',\n source: docSource,\n ...metadata,\n }\n }\n case 'audio':\n case 'video':\n throw new Error(\n `Anthropic does not support ${part.type} content directly`,\n )\n default: {\n const _exhaustiveCheck: never = part\n throw new Error(\n `Unsupported content part type: ${(_exhaustiveCheck as ContentPart).type}`,\n )\n }\n }\n }\n\n private formatMessages(\n messages: Array<ModelMessage>,\n ): InternalTextProviderOptions['messages'] {\n const formattedMessages: InternalTextProviderOptions['messages'] = []\n\n for (const message of messages) {\n const role = message.role\n\n if (role === 'tool' && message.toolCallId) {\n formattedMessages.push({\n role: 'user',\n content: [\n {\n type: 'tool_result',\n tool_use_id: message.toolCallId,\n content:\n typeof message.content === 'string' ? message.content : '',\n },\n ],\n })\n continue\n }\n\n if (role === 'assistant' && message.toolCalls?.length) {\n const contentBlocks: AnthropicContentBlocks = []\n\n if (message.content) {\n const content =\n typeof message.content === 'string' ? message.content : ''\n const textBlock: AnthropicContentBlock = {\n type: 'text',\n text: content,\n }\n contentBlocks.push(textBlock)\n }\n\n for (const toolCall of message.toolCalls) {\n let parsedInput: unknown = {}\n try {\n parsedInput = toolCall.function.arguments\n ? JSON.parse(toolCall.function.arguments)\n : {}\n } catch {\n parsedInput = toolCall.function.arguments\n }\n\n const toolUseBlock: AnthropicContentBlock = {\n type: 'tool_use',\n id: toolCall.id,\n name: toolCall.function.name,\n input: parsedInput,\n }\n contentBlocks.push(toolUseBlock)\n }\n\n formattedMessages.push({\n role: 'assistant',\n content: contentBlocks,\n })\n\n continue\n }\n\n if (role === 'user' && Array.isArray(message.content)) {\n const contentBlocks = message.content.map((part) =>\n this.convertContentPartToAnthropic(part),\n )\n formattedMessages.push({\n role: 'user',\n content: contentBlocks,\n })\n continue\n }\n\n formattedMessages.push({\n role: role === 'assistant' ? 'assistant' : 'user',\n content:\n typeof message.content === 'string'\n ? message.content\n : message.content\n ? message.content.map((c) =>\n this.convertContentPartToAnthropic(c),\n )\n : '',\n })\n }\n\n return formattedMessages\n }\n\n private async *processAnthropicStream(\n stream: AsyncIterable<Anthropic_SDK.Beta.BetaRawMessageStreamEvent>,\n model: string,\n genId: () => string,\n ): AsyncIterable<StreamChunk> {\n let accumulatedContent = ''\n let accumulatedThinking = ''\n const timestamp = Date.now()\n const toolCallsMap = new Map<\n number,\n { id: string; name: string; input: string; started: boolean }\n >()\n let currentToolIndex = -1\n\n // AG-UI lifecycle tracking\n const runId = genId()\n const messageId = genId()\n let stepId: string | null = null\n let hasEmittedRunStarted = false\n let hasEmittedTextMessageStart = false\n\n try {\n for await (const event of stream) {\n // Emit RUN_STARTED on first event\n if (!hasEmittedRunStarted) {\n hasEmittedRunStarted = true\n yield {\n type: 'RUN_STARTED',\n runId,\n model,\n timestamp,\n }\n }\n\n if (event.type === 'content_block_start') {\n if (event.content_block.type === 'tool_use') {\n currentToolIndex++\n toolCallsMap.set(currentToolIndex, {\n id: event.content_block.id,\n name: event.content_block.name,\n input: '',\n started: false,\n })\n } else if (event.content_block.type === 'thinking') {\n accumulatedThinking = ''\n // Emit STEP_STARTED for thinking\n stepId = genId()\n yield {\n type: 'STEP_STARTED',\n stepId,\n model,\n timestamp,\n stepType: 'thinking',\n }\n }\n } else if (event.type === 'content_block_delta') {\n if (event.delta.type === 'text_delta') {\n // Emit TEXT_MESSAGE_START on first text content\n if (!hasEmittedTextMessageStart) {\n hasEmittedTextMessageStart = true\n yield {\n type: 'TEXT_MESSAGE_START',\n messageId,\n model,\n timestamp,\n role: 'assistant',\n }\n }\n\n const delta = event.delta.text\n accumulatedContent += delta\n yield {\n type: 'TEXT_MESSAGE_CONTENT',\n messageId,\n model,\n timestamp,\n delta,\n content: accumulatedContent,\n }\n } else if (event.delta.type === 'thinking_delta') {\n const delta = event.delta.thinking\n accumulatedThinking += delta\n yield {\n type: 'STEP_FINISHED',\n stepId: stepId || genId(),\n model,\n timestamp,\n delta,\n content: accumulatedThinking,\n }\n } else if (event.delta.type === 'input_json_delta') {\n const existing = toolCallsMap.get(currentToolIndex)\n if (existing) {\n // Emit TOOL_CALL_START on first args delta\n if (!existing.started) {\n existing.started = true\n yield {\n type: 'TOOL_CALL_START',\n toolCallId: existing.id,\n toolName: existing.name,\n model,\n timestamp,\n index: currentToolIndex,\n }\n }\n\n existing.input += event.delta.partial_json\n\n yield {\n type: 'TOOL_CALL_ARGS',\n toolCallId: existing.id,\n model,\n timestamp,\n delta: event.delta.partial_json,\n args: existing.input,\n }\n }\n }\n } else if (event.type === 'content_block_stop') {\n const existing = toolCallsMap.get(currentToolIndex)\n if (existing) {\n // If tool call wasn't started yet (no args), start it now\n if (!existing.started) {\n existing.started = true\n yield {\n type: 'TOOL_CALL_START',\n toolCallId: existing.id,\n toolName: existing.name,\n model,\n timestamp,\n index: currentToolIndex,\n }\n }\n\n // Emit TOOL_CALL_END\n let parsedInput: unknown = {}\n try {\n parsedInput = existing.input ? JSON.parse(existing.input) : {}\n } catch {\n parsedInput = {}\n }\n\n yield {\n type: 'TOOL_CALL_END',\n toolCallId: existing.id,\n toolName: existing.name,\n model,\n timestamp,\n input: parsedInput,\n }\n }\n\n // Emit TEXT_MESSAGE_END if we had text content\n if (hasEmittedTextMessageStart && accumulatedContent) {\n yield {\n type: 'TEXT_MESSAGE_END',\n messageId,\n model,\n timestamp,\n }\n }\n } else if (event.type === 'message_stop') {\n yield {\n type: 'RUN_FINISHED',\n runId,\n model,\n timestamp,\n finishReason: 'stop',\n }\n } else if (event.type === 'message_delta') {\n if (event.delta.stop_reason) {\n switch (event.delta.stop_reason) {\n case 'tool_use': {\n yield {\n type: 'RUN_FINISHED',\n runId,\n model,\n timestamp,\n finishReason: 'tool_calls',\n usage: {\n promptTokens: event.usage.input_tokens || 0,\n completionTokens: event.usage.output_tokens || 0,\n totalTokens:\n (event.usage.input_tokens || 0) +\n (event.usage.output_tokens || 0),\n },\n }\n break\n }\n case 'max_tokens': {\n yield {\n type: 'RUN_ERROR',\n runId,\n model,\n timestamp,\n error: {\n message:\n 'The response was cut off because the maximum token limit was reached.',\n code: 'max_tokens',\n },\n }\n break\n }\n default: {\n yield {\n type: 'RUN_FINISHED',\n runId,\n model,\n timestamp,\n finishReason: 'stop',\n usage: {\n promptTokens: event.usage.input_tokens || 0,\n completionTokens: event.usage.output_tokens || 0,\n totalTokens:\n (event.usage.input_tokens || 0) +\n (event.usage.output_tokens || 0),\n },\n }\n }\n }\n }\n }\n }\n } catch (error: unknown) {\n const err = error as Error & { status?: number; code?: string }\n\n yield {\n type: 'RUN_ERROR',\n runId,\n model,\n timestamp,\n error: {\n message: err.message || 'Unknown error occurred',\n code: err.code || String(err.status),\n },\n }\n }\n }\n}\n\n/**\n * Creates an Anthropic chat adapter with explicit API key.\n * Type resolution happens here at the call site.\n */\nexport function createAnthropicChat<\n TModel extends (typeof ANTHROPIC_MODELS)[number],\n>(\n model: TModel,\n apiKey: string,\n config?: Omit<AnthropicTextConfig, 'apiKey'>,\n): AnthropicTextAdapter<\n TModel,\n ResolveProviderOptions<TModel>,\n ResolveInputModalities<TModel>\n> {\n return new AnthropicTextAdapter({ apiKey, ...config }, model)\n}\n\n/**\n * Creates an Anthropic text adapter with automatic API key detection.\n * Type resolution happens here at the call site.\n */\nexport function anthropicText<TModel extends (typeof ANTHROPIC_MODELS)[number]>(\n model: TModel,\n config?: Omit<AnthropicTextConfig, 'apiKey'>,\n): AnthropicTextAdapter<\n TModel,\n ResolveProviderOptions<TModel>,\n ResolveInputModalities<TModel>\n> {\n const apiKey = getAnthropicApiKeyFromEnv()\n return createAnthropicChat(model, apiKey, config)\n}\n"],"names":[],"mappings":"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"}
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { AnthropicTextAdapter, anthropicText, createAnthropicChat, type AnthropicTextConfig, type AnthropicTextProviderOptions, } from './adapters/text.js';
|
|
2
2
|
export { AnthropicSummarizeAdapter, anthropicSummarize, createAnthropicSummarize, type AnthropicSummarizeConfig, type AnthropicSummarizeProviderOptions, } from './adapters/summarize.js';
|
|
3
3
|
export type { AnthropicChatModelProviderOptionsByName, AnthropicModelInputModalitiesByName, } from './model-meta.js';
|
|
4
|
-
export type { AnthropicTextMetadata, AnthropicImageMetadata, AnthropicDocumentMetadata, AnthropicAudioMetadata, AnthropicVideoMetadata,
|
|
4
|
+
export type { AnthropicTextMetadata, AnthropicImageMetadata, AnthropicDocumentMetadata, AnthropicAudioMetadata, AnthropicVideoMetadata, AnthropicMessageMetadataByModality, } from './message-types.js';
|
|
5
5
|
export { convertToolsToProviderFormat } from './tools/tool-converter.js';
|
|
6
6
|
export type { AnthropicTool, CustomTool } from './tools.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/ai-anthropic",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Anthropic Claude adapter for TanStack AI",
|
|
5
5
|
"author": "",
|
|
6
6
|
"license": "MIT",
|
|
@@ -32,12 +32,14 @@
|
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@anthropic-ai/sdk": "^0.71.0"
|
|
34
34
|
},
|
|
35
|
-
"devDependencies": {
|
|
36
|
-
"@vitest/coverage-v8": "4.0.14"
|
|
37
|
-
},
|
|
38
35
|
"peerDependencies": {
|
|
39
36
|
"zod": "^4.0.0",
|
|
40
|
-
"@tanstack/ai": "^0.
|
|
37
|
+
"@tanstack/ai": "^0.4.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@vitest/coverage-v8": "4.0.14",
|
|
41
|
+
"zod": "^4.2.0",
|
|
42
|
+
"@tanstack/ai": "0.4.0"
|
|
41
43
|
},
|
|
42
44
|
"scripts": {
|
|
43
45
|
"build": "vite build",
|
|
@@ -104,20 +104,19 @@ export class AnthropicSummarizeAdapter<
|
|
|
104
104
|
const delta = event.delta.text
|
|
105
105
|
accumulatedContent += delta
|
|
106
106
|
yield {
|
|
107
|
-
type: '
|
|
108
|
-
id,
|
|
107
|
+
type: 'TEXT_MESSAGE_CONTENT',
|
|
108
|
+
messageId: id,
|
|
109
109
|
model,
|
|
110
110
|
timestamp: Date.now(),
|
|
111
111
|
delta,
|
|
112
112
|
content: accumulatedContent,
|
|
113
|
-
role: 'assistant',
|
|
114
113
|
}
|
|
115
114
|
}
|
|
116
115
|
} else if (event.type === 'message_delta') {
|
|
117
116
|
outputTokens = event.usage.output_tokens
|
|
118
117
|
yield {
|
|
119
|
-
type: '
|
|
120
|
-
id,
|
|
118
|
+
type: 'RUN_FINISHED',
|
|
119
|
+
runId: id,
|
|
121
120
|
model,
|
|
122
121
|
timestamp: Date.now(),
|
|
123
122
|
finishReason: event.delta.stop_reason as
|
package/src/adapters/text.ts
CHANGED
|
@@ -135,8 +135,7 @@ export class AnthropicTextAdapter<
|
|
|
135
135
|
} catch (error: unknown) {
|
|
136
136
|
const err = error as Error & { status?: number; code?: string }
|
|
137
137
|
yield {
|
|
138
|
-
type: '
|
|
139
|
-
id: generateId(this.name),
|
|
138
|
+
type: 'RUN_ERROR',
|
|
140
139
|
model: options.model,
|
|
141
140
|
timestamp: Date.now(),
|
|
142
141
|
error: {
|
|
@@ -314,17 +313,20 @@ export class AnthropicTextAdapter<
|
|
|
314
313
|
? {
|
|
315
314
|
type: 'base64',
|
|
316
315
|
data: part.source.value,
|
|
317
|
-
media_type:
|
|
316
|
+
media_type: part.source.mimeType as
|
|
317
|
+
| 'image/jpeg'
|
|
318
|
+
| 'image/png'
|
|
319
|
+
| 'image/gif'
|
|
320
|
+
| 'image/webp',
|
|
318
321
|
}
|
|
319
322
|
: {
|
|
320
323
|
type: 'url',
|
|
321
324
|
url: part.source.value,
|
|
322
325
|
}
|
|
323
|
-
const { mediaType: _mediaType, ...meta } = metadata || {}
|
|
324
326
|
return {
|
|
325
327
|
type: 'image',
|
|
326
328
|
source: imageSource,
|
|
327
|
-
...
|
|
329
|
+
...metadata,
|
|
328
330
|
}
|
|
329
331
|
}
|
|
330
332
|
case 'document': {
|
|
@@ -334,7 +336,7 @@ export class AnthropicTextAdapter<
|
|
|
334
336
|
? {
|
|
335
337
|
type: 'base64',
|
|
336
338
|
data: part.source.value,
|
|
337
|
-
media_type: 'application/pdf',
|
|
339
|
+
media_type: part.source.mimeType as 'application/pdf',
|
|
338
340
|
}
|
|
339
341
|
: {
|
|
340
342
|
type: 'url',
|
|
@@ -460,12 +462,30 @@ export class AnthropicTextAdapter<
|
|
|
460
462
|
const timestamp = Date.now()
|
|
461
463
|
const toolCallsMap = new Map<
|
|
462
464
|
number,
|
|
463
|
-
{ id: string; name: string; input: string }
|
|
465
|
+
{ id: string; name: string; input: string; started: boolean }
|
|
464
466
|
>()
|
|
465
467
|
let currentToolIndex = -1
|
|
466
468
|
|
|
469
|
+
// AG-UI lifecycle tracking
|
|
470
|
+
const runId = genId()
|
|
471
|
+
const messageId = genId()
|
|
472
|
+
let stepId: string | null = null
|
|
473
|
+
let hasEmittedRunStarted = false
|
|
474
|
+
let hasEmittedTextMessageStart = false
|
|
475
|
+
|
|
467
476
|
try {
|
|
468
477
|
for await (const event of stream) {
|
|
478
|
+
// Emit RUN_STARTED on first event
|
|
479
|
+
if (!hasEmittedRunStarted) {
|
|
480
|
+
hasEmittedRunStarted = true
|
|
481
|
+
yield {
|
|
482
|
+
type: 'RUN_STARTED',
|
|
483
|
+
runId,
|
|
484
|
+
model,
|
|
485
|
+
timestamp,
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
469
489
|
if (event.type === 'content_block_start') {
|
|
470
490
|
if (event.content_block.type === 'tool_use') {
|
|
471
491
|
currentToolIndex++
|
|
@@ -473,30 +493,51 @@ export class AnthropicTextAdapter<
|
|
|
473
493
|
id: event.content_block.id,
|
|
474
494
|
name: event.content_block.name,
|
|
475
495
|
input: '',
|
|
496
|
+
started: false,
|
|
476
497
|
})
|
|
477
498
|
} else if (event.content_block.type === 'thinking') {
|
|
478
499
|
accumulatedThinking = ''
|
|
500
|
+
// Emit STEP_STARTED for thinking
|
|
501
|
+
stepId = genId()
|
|
502
|
+
yield {
|
|
503
|
+
type: 'STEP_STARTED',
|
|
504
|
+
stepId,
|
|
505
|
+
model,
|
|
506
|
+
timestamp,
|
|
507
|
+
stepType: 'thinking',
|
|
508
|
+
}
|
|
479
509
|
}
|
|
480
510
|
} else if (event.type === 'content_block_delta') {
|
|
481
511
|
if (event.delta.type === 'text_delta') {
|
|
512
|
+
// Emit TEXT_MESSAGE_START on first text content
|
|
513
|
+
if (!hasEmittedTextMessageStart) {
|
|
514
|
+
hasEmittedTextMessageStart = true
|
|
515
|
+
yield {
|
|
516
|
+
type: 'TEXT_MESSAGE_START',
|
|
517
|
+
messageId,
|
|
518
|
+
model,
|
|
519
|
+
timestamp,
|
|
520
|
+
role: 'assistant',
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
482
524
|
const delta = event.delta.text
|
|
483
525
|
accumulatedContent += delta
|
|
484
526
|
yield {
|
|
485
|
-
type: '
|
|
486
|
-
|
|
487
|
-
model
|
|
527
|
+
type: 'TEXT_MESSAGE_CONTENT',
|
|
528
|
+
messageId,
|
|
529
|
+
model,
|
|
488
530
|
timestamp,
|
|
489
531
|
delta,
|
|
490
532
|
content: accumulatedContent,
|
|
491
|
-
role: 'assistant',
|
|
492
533
|
}
|
|
493
534
|
} else if (event.delta.type === 'thinking_delta') {
|
|
494
535
|
const delta = event.delta.thinking
|
|
495
536
|
accumulatedThinking += delta
|
|
496
537
|
yield {
|
|
497
|
-
type: '
|
|
498
|
-
|
|
499
|
-
model
|
|
538
|
+
type: 'STEP_FINISHED',
|
|
539
|
+
stepId: stepId || genId(),
|
|
540
|
+
model,
|
|
500
541
|
timestamp,
|
|
501
542
|
delta,
|
|
502
543
|
content: accumulatedThinking,
|
|
@@ -504,49 +545,79 @@ export class AnthropicTextAdapter<
|
|
|
504
545
|
} else if (event.delta.type === 'input_json_delta') {
|
|
505
546
|
const existing = toolCallsMap.get(currentToolIndex)
|
|
506
547
|
if (existing) {
|
|
548
|
+
// Emit TOOL_CALL_START on first args delta
|
|
549
|
+
if (!existing.started) {
|
|
550
|
+
existing.started = true
|
|
551
|
+
yield {
|
|
552
|
+
type: 'TOOL_CALL_START',
|
|
553
|
+
toolCallId: existing.id,
|
|
554
|
+
toolName: existing.name,
|
|
555
|
+
model,
|
|
556
|
+
timestamp,
|
|
557
|
+
index: currentToolIndex,
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
507
561
|
existing.input += event.delta.partial_json
|
|
508
562
|
|
|
509
563
|
yield {
|
|
510
|
-
type: '
|
|
511
|
-
|
|
512
|
-
model
|
|
564
|
+
type: 'TOOL_CALL_ARGS',
|
|
565
|
+
toolCallId: existing.id,
|
|
566
|
+
model,
|
|
513
567
|
timestamp,
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
type: 'function',
|
|
517
|
-
function: {
|
|
518
|
-
name: existing.name,
|
|
519
|
-
arguments: event.delta.partial_json,
|
|
520
|
-
},
|
|
521
|
-
},
|
|
522
|
-
index: currentToolIndex,
|
|
568
|
+
delta: event.delta.partial_json,
|
|
569
|
+
args: existing.input,
|
|
523
570
|
}
|
|
524
571
|
}
|
|
525
572
|
}
|
|
526
573
|
} else if (event.type === 'content_block_stop') {
|
|
527
574
|
const existing = toolCallsMap.get(currentToolIndex)
|
|
528
|
-
if (existing
|
|
575
|
+
if (existing) {
|
|
576
|
+
// If tool call wasn't started yet (no args), start it now
|
|
577
|
+
if (!existing.started) {
|
|
578
|
+
existing.started = true
|
|
579
|
+
yield {
|
|
580
|
+
type: 'TOOL_CALL_START',
|
|
581
|
+
toolCallId: existing.id,
|
|
582
|
+
toolName: existing.name,
|
|
583
|
+
model,
|
|
584
|
+
timestamp,
|
|
585
|
+
index: currentToolIndex,
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// Emit TOOL_CALL_END
|
|
590
|
+
let parsedInput: unknown = {}
|
|
591
|
+
try {
|
|
592
|
+
parsedInput = existing.input ? JSON.parse(existing.input) : {}
|
|
593
|
+
} catch {
|
|
594
|
+
parsedInput = {}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
yield {
|
|
598
|
+
type: 'TOOL_CALL_END',
|
|
599
|
+
toolCallId: existing.id,
|
|
600
|
+
toolName: existing.name,
|
|
601
|
+
model,
|
|
602
|
+
timestamp,
|
|
603
|
+
input: parsedInput,
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// Emit TEXT_MESSAGE_END if we had text content
|
|
608
|
+
if (hasEmittedTextMessageStart && accumulatedContent) {
|
|
529
609
|
yield {
|
|
530
|
-
type: '
|
|
531
|
-
|
|
532
|
-
model
|
|
610
|
+
type: 'TEXT_MESSAGE_END',
|
|
611
|
+
messageId,
|
|
612
|
+
model,
|
|
533
613
|
timestamp,
|
|
534
|
-
toolCall: {
|
|
535
|
-
id: existing.id,
|
|
536
|
-
type: 'function',
|
|
537
|
-
function: {
|
|
538
|
-
name: existing.name,
|
|
539
|
-
arguments: '{}',
|
|
540
|
-
},
|
|
541
|
-
},
|
|
542
|
-
index: currentToolIndex,
|
|
543
614
|
}
|
|
544
615
|
}
|
|
545
616
|
} else if (event.type === 'message_stop') {
|
|
546
617
|
yield {
|
|
547
|
-
type: '
|
|
548
|
-
|
|
549
|
-
model
|
|
618
|
+
type: 'RUN_FINISHED',
|
|
619
|
+
runId,
|
|
620
|
+
model,
|
|
550
621
|
timestamp,
|
|
551
622
|
finishReason: 'stop',
|
|
552
623
|
}
|
|
@@ -555,9 +626,9 @@ export class AnthropicTextAdapter<
|
|
|
555
626
|
switch (event.delta.stop_reason) {
|
|
556
627
|
case 'tool_use': {
|
|
557
628
|
yield {
|
|
558
|
-
type: '
|
|
559
|
-
|
|
560
|
-
model
|
|
629
|
+
type: 'RUN_FINISHED',
|
|
630
|
+
runId,
|
|
631
|
+
model,
|
|
561
632
|
timestamp,
|
|
562
633
|
finishReason: 'tool_calls',
|
|
563
634
|
usage: {
|
|
@@ -572,9 +643,9 @@ export class AnthropicTextAdapter<
|
|
|
572
643
|
}
|
|
573
644
|
case 'max_tokens': {
|
|
574
645
|
yield {
|
|
575
|
-
type: '
|
|
576
|
-
|
|
577
|
-
model
|
|
646
|
+
type: 'RUN_ERROR',
|
|
647
|
+
runId,
|
|
648
|
+
model,
|
|
578
649
|
timestamp,
|
|
579
650
|
error: {
|
|
580
651
|
message:
|
|
@@ -586,9 +657,9 @@ export class AnthropicTextAdapter<
|
|
|
586
657
|
}
|
|
587
658
|
default: {
|
|
588
659
|
yield {
|
|
589
|
-
type: '
|
|
590
|
-
|
|
591
|
-
model
|
|
660
|
+
type: 'RUN_FINISHED',
|
|
661
|
+
runId,
|
|
662
|
+
model,
|
|
592
663
|
timestamp,
|
|
593
664
|
finishReason: 'stop',
|
|
594
665
|
usage: {
|
|
@@ -608,9 +679,9 @@ export class AnthropicTextAdapter<
|
|
|
608
679
|
const err = error as Error & { status?: number; code?: string }
|
|
609
680
|
|
|
610
681
|
yield {
|
|
611
|
-
type: '
|
|
612
|
-
|
|
613
|
-
model
|
|
682
|
+
type: 'RUN_ERROR',
|
|
683
|
+
runId,
|
|
684
|
+
model,
|
|
614
685
|
timestamp,
|
|
615
686
|
error: {
|
|
616
687
|
message: err.message || 'Unknown error occurred',
|