UNPKG

genkitx-anthropic

Version:

Firebase Genkit AI framework plugin for Anthropic APIs.

430 lines 12.4 kB
import { __async, __forAwait, __spreadProps, __spreadValues } from "./chunk-MLCSNVBT.mjs"; import { Message as GenkitMessage, z } from "genkit"; import { GenerationCommonConfigSchema } from "genkit"; import { modelRef } from "genkit/model"; const AnthropicConfigSchema = GenerationCommonConfigSchema.extend({ tool_choice: z.union([ z.object({ type: z.literal("auto") }), z.object({ type: z.literal("any") }), z.object({ type: z.literal("tool"), name: z.string() }) ]).optional(), metadata: z.object({ user_id: z.string().optional() }).optional() }); const claude37Sonnet = modelRef({ name: "anthropic/claude-3-7-sonnet", info: { versions: ["claude-3-7-sonnet-20250219", "claude-3-7-sonnet-latest"], label: "Anthropic - Claude 3.7 Sonnet", supports: { multiturn: true, tools: true, media: true, systemRole: true, output: ["text"] } }, configSchema: AnthropicConfigSchema, version: "claude-3-7-sonnet-latest" }); const claude35Sonnet = modelRef({ name: "anthropic/claude-3-5-sonnet", info: { versions: [ "claude-3-5-sonnet-20240620", "claude-3-5-sonnet-20241022", "claude-3-5-sonnet-latest" ], label: "Anthropic - Claude 3.5 Sonnet", supports: { multiturn: true, tools: true, media: true, systemRole: true, output: ["text"] } }, configSchema: AnthropicConfigSchema, version: "claude-3-5-sonnet-latest" }); const claude3Opus = modelRef({ name: "anthropic/claude-3-opus", info: { versions: ["claude-3-opus-20240229"], label: "Anthropic - Claude 3 Opus", supports: { multiturn: true, tools: true, media: true, systemRole: true, output: ["text"] } }, configSchema: AnthropicConfigSchema, version: "claude-3-opus-20240229" }); const claude3Sonnet = modelRef({ name: "anthropic/claude-3-sonnet", info: { versions: ["claude-3-sonnet-20240229"], label: "Anthropic - Claude 3 Sonnet", supports: { multiturn: true, tools: true, media: true, systemRole: true, output: ["text"] } }, configSchema: AnthropicConfigSchema, version: "claude-3-sonnet-20240229" }); const claude3Haiku = modelRef({ name: "anthropic/claude-3-haiku", info: { versions: ["claude-3-haiku-20240307"], label: "Anthropic - Claude 3 Haiku", supports: { multiturn: true, tools: true, media: true, systemRole: true, output: ["text"] } }, configSchema: AnthropicConfigSchema, version: "claude-3-haiku-20240307" }); const claude35Haiku = modelRef({ name: "anthropic/claude-3-5-haiku", info: { versions: ["claude-3-5-haiku-20241022", "claude-3-5-haiku-latest"], label: "Anthropic - Claude 3.5 Haiku", supports: { multiturn: true, tools: true, media: true, systemRole: true, output: ["text"] } }, configSchema: AnthropicConfigSchema, version: "claude-3-5-haiku-latest" }); const SUPPORTED_CLAUDE_MODELS = { "claude-3-7-sonnet": claude37Sonnet, "claude-3-5-sonnet": claude35Sonnet, "claude-3-opus": claude3Opus, "claude-3-sonnet": claude3Sonnet, "claude-3-haiku": claude3Haiku, "claude-3-5-haiku": claude35Haiku }; function toAnthropicRole(role, toolMessageType) { switch (role) { case "user": return "user"; case "model": return "assistant"; case "tool": return toolMessageType === "tool_use" ? "assistant" : "user"; default: throw new Error(`role ${role} doesn't map to an Anthropic role.`); } } const isMediaObject = (obj) => typeof obj === "object" && obj !== null && "url" in obj && typeof obj.url === "string"; const extractDataFromBase64Url = (url) => { const match = url.match(/^data:([^;]+);base64,(.+)$/); return match && { contentType: match[1], data: match[2] }; }; function toAnthropicToolResponseContent(part) { var _a, _b, _c, _d, _e, _f, _g, _h, _i; if (!part.toolResponse) { throw Error( `Invalid genkit part provided to toAnthropicToolResponseContent: ${JSON.stringify( part )}.` ); } const isMedia = isMediaObject((_a = part.toolResponse) == null ? void 0 : _a.output); const isString = typeof ((_b = part.toolResponse) == null ? void 0 : _b.output) === "string"; let base64Data; if (isMedia) { base64Data = extractDataFromBase64Url( ((_c = part.toolResponse) == null ? void 0 : _c.output).url ); } else if (isString) { base64Data = extractDataFromBase64Url((_d = part.toolResponse) == null ? void 0 : _d.output); } return base64Data ? { type: "image", source: { type: "base64", data: base64Data.data, media_type: (_g = (_f = (_e = part.toolResponse) == null ? void 0 : _e.output) == null ? void 0 : _f.contentType) != null ? _g : base64Data.contentType } } : { type: "text", text: isString ? (_h = part.toolResponse) == null ? void 0 : _h.output : JSON.stringify((_i = part.toolResponse) == null ? void 0 : _i.output) }; } function toAnthropicMessageContent(part) { var _a, _b; if (part.text) { return { type: "text", text: part.text }; } if (part.media) { const { data, contentType } = (_a = extractDataFromBase64Url(part.media.url)) != null ? _a : {}; if (!data) { throw Error( `Invalid genkit part media provided to toAnthropicMessageContent: ${JSON.stringify( part.media )}.` ); } return { type: "image", source: { type: "base64", data, // @ts-expect-error TODO: improve these types media_type: (_b = part.media.contentType) != null ? _b : contentType } }; } if (part.toolRequest) { return { type: "tool_use", id: part.toolRequest.ref, name: part.toolRequest.name, input: part.toolRequest.input }; } if (part.toolResponse) { return { type: "tool_result", tool_use_id: part.toolResponse.ref, content: [toAnthropicToolResponseContent(part)] }; } throw Error( `Unsupported genkit part fields encountered for current message role: ${JSON.stringify( part )}.` ); } function toAnthropicMessages(messages) { var _a, _b, _c; const system = ((_a = messages[0]) == null ? void 0 : _a.role) === "system" ? (_c = (_b = messages[0].content) == null ? void 0 : _b[0]) == null ? void 0 : _c.text : void 0; const messagesToIterate = system ? messages.slice(1) : messages; const anthropicMsgs = []; for (const message of messagesToIterate) { const msg = new GenkitMessage(message); const content = msg.content.map(toAnthropicMessageContent); const toolMessageType = content.find( (c) => c.type === "tool_use" || c.type === "tool_result" ); const role = toAnthropicRole(message.role, toolMessageType == null ? void 0 : toolMessageType.type); anthropicMsgs.push({ role, content }); } return { system, messages: anthropicMsgs }; } function toAnthropicTool(tool) { return { name: tool.name, description: tool.description, input_schema: tool.inputSchema }; } function fromAnthropicContentBlock(contentBlock) { return contentBlock.type === "tool_use" ? { toolRequest: { ref: contentBlock.id, name: contentBlock.name, input: contentBlock.input } } : { text: contentBlock.text }; } function fromAnthropicContentBlockChunk(event) { if (event.type !== "content_block_start" && event.type !== "content_block_delta") { return; } const eventField = event.type === "content_block_start" ? "content_block" : "delta"; return ["text", "text_delta"].includes(event[eventField].type) ? { text: event[eventField].text } : { toolRequest: { ref: event[eventField].id, name: event[eventField].name, input: event[eventField].input } }; } function fromAnthropicStopReason(reason) { switch (reason) { case "max_tokens": return "length"; case "end_turn": // fall through case "stop_sequence": // fall through case "tool_use": return "stop"; case null: return "unknown"; default: return "other"; } } function fromAnthropicResponse(response) { return { candidates: [ { index: 0, finishReason: fromAnthropicStopReason(response.stop_reason), message: { role: "model", content: response.content.map(fromAnthropicContentBlock) } } ], usage: { inputTokens: response.usage.input_tokens, outputTokens: response.usage.output_tokens }, custom: response }; } function toAnthropicRequestBody(modelName, request, stream, cacheSystemPrompt) { var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m; const model = SUPPORTED_CLAUDE_MODELS[modelName]; if (!model) throw new Error(`Unsupported model: ${modelName}`); const { system, messages } = toAnthropicMessages(request.messages); const mappedModelName = (_c = (_b = (_a = request.config) == null ? void 0 : _a.version) != null ? _b : model.version) != null ? _c : modelName; const body = { system: cacheSystemPrompt ? [ { type: "text", text: system, // @ts-expect-error cache_control is in beta cache_control: { type: "ephemeral" } } ] : system, messages, tools: (_d = request.tools) == null ? void 0 : _d.map(toAnthropicTool), max_tokens: (_f = (_e = request.config) == null ? void 0 : _e.maxOutputTokens) != null ? _f : 4096, model: mappedModelName, top_k: (_g = request.config) == null ? void 0 : _g.topK, top_p: (_h = request.config) == null ? void 0 : _h.topP, temperature: (_i = request.config) == null ? void 0 : _i.temperature, stop_sequences: (_j = request.config) == null ? void 0 : _j.stopSequences, metadata: (_k = request.config) == null ? void 0 : _k.metadata, tool_choice: (_l = request.config) == null ? void 0 : _l.tool_choice, stream }; if (((_m = request.output) == null ? void 0 : _m.format) && request.output.format !== "text") { throw new Error( `Only text output format is supported for Claude models currently` ); } for (const key in body) { if (!body[key] || Array.isArray(body[key]) && !body[key].length) delete body[key]; } return body; } function claudeRunner(name, client, cacheSystemPrompt) { return (request, streamingCallback) => __async(this, null, function* () { let response; const body = toAnthropicRequestBody( name, request, !!streamingCallback, cacheSystemPrompt ); if (streamingCallback) { const stream = client.messages.stream(body); try { for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield iter.next()).done; more = false) { const chunk = temp.value; const c = fromAnthropicContentBlockChunk(chunk); if (c) { streamingCallback({ index: 0, content: [c] }); } } } catch (temp) { error = [temp]; } finally { try { more && (temp = iter.return) && (yield temp.call(iter)); } finally { if (error) throw error[0]; } } response = yield stream.finalMessage(); } else { response = yield client.messages.create(body); } return fromAnthropicResponse(response); }); } function claudeModel(ai, name, client, cacheSystemPrompt) { const modelId = `anthropic/${name}`; const model = SUPPORTED_CLAUDE_MODELS[name]; if (!model) throw new Error(`Unsupported model: ${name}`); return ai.defineModel( __spreadProps(__spreadValues({ name: modelId }, model.info), { configSchema: model.configSchema }), claudeRunner(name, client, cacheSystemPrompt) ); } export { AnthropicConfigSchema, SUPPORTED_CLAUDE_MODELS, claude35Haiku, claude35Sonnet, claude37Sonnet, claude3Haiku, claude3Opus, claude3Sonnet, claudeModel, claudeRunner, fromAnthropicContentBlockChunk, fromAnthropicResponse, fromAnthropicStopReason, toAnthropicMessageContent, toAnthropicMessages, toAnthropicRequestBody, toAnthropicRole, toAnthropicTool, toAnthropicToolResponseContent }; //# sourceMappingURL=claude.mjs.map