MagicCode/packages/providers/src/anthropic.ts

74 lines
2.2 KiB
TypeScript
Raw Normal View History

2026-10-11 00:11:20 +00:00
import Anthropic from "@anthropic-ai/sdk";
import type { ChatRequest, LLMProvider, Message, StopReason, StreamEvent } from "@magiccode/core";
function toAnthropicMessages(messages: Message[]): Anthropic.MessageParam[] {
return messages.map((m) => ({
role: m.role,
content: m.content.map((b) => {
switch (b.type) {
case "text":
return { type: "text" as const, text: b.text };
case "tool_use":
return { type: "tool_use" as const, id: b.id, name: b.name, input: b.input };
case "tool_result":
return {
type: "tool_result" as const,
tool_use_id: b.toolUseId,
content: b.content,
is_error: b.isError ?? false,
};
}
}),
}));
}
function toStopReason(r: string | null): StopReason {
if (r === "end_turn" || r === "tool_use" || r === "max_tokens") return r;
return "other";
}
/** Utilise ANTHROPIC_API_KEY depuis l'environnement. */
export function createAnthropicProvider(model: string): LLMProvider {
const client = new Anthropic();
return {
id: "anthropic",
model,
capabilities: { tools: true },
async *chat(req: ChatRequest, signal?: AbortSignal): AsyncIterable<StreamEvent> {
const stream = client.messages.stream(
{
model,
max_tokens: req.maxTokens ?? 8192,
system: req.system,
tools: req.tools.map((t) => ({
name: t.name,
description: t.description,
input_schema: t.inputSchema as Anthropic.Tool.InputSchema,
})),
messages: toAnthropicMessages(req.messages),
},
{ signal },
);
for await (const ev of stream) {
if (ev.type === "content_block_delta" && ev.delta.type === "text_delta") {
yield { type: "text_delta", text: ev.delta.text };
}
}
const final = await stream.finalMessage();
for (const block of final.content) {
if (block.type === "tool_use") {
yield {
type: "tool_use",
id: block.id,
name: block.name,
input: block.input as Record<string, unknown>,
};
}
}
yield { type: "done", stopReason: toStopReason(final.stop_reason) };
},
};
}