UNPKG

@genkit-ai/anthropic

Version:

Genkit AI framework plugin for Anthropic APIs.

366 lines 10.8 kB
import { logger } from "genkit/logging"; import { checkModelName, removeUndefinedProperties } from "../utils.mjs"; import { BaseRunner } from "./base.mjs"; import { betaServerToolUseBlockToPart, toBetaDocumentBlock, unsupportedServerToolError } from "./converters/beta.mjs"; import { citationsDeltaToPart, redactedThinkingBlockToPart, textBlockToPart, textDeltaToPart, thinkingBlockToPart, thinkingDeltaToPart, toolUseBlockToPart, webSearchToolResultBlockToPart } from "./converters/shared.mjs"; const BETA_APIS = [ // 'message-batches-2024-09-24', // 'prompt-caching-2024-07-31', // 'computer-use-2025-01-24', // 'pdfs-2024-09-25', // 'token-counting-2024-11-01', // 'token-efficient-tools-2025-02-19', // 'output-128k-2025-02-19', "files-api-2025-04-14", // 'mcp-client-2025-04-04', // 'dev-full-thinking-2025-05-14', // 'interleaved-thinking-2025-05-14', // 'code-execution-2025-05-22', // 'extended-cache-ttl-2025-04-11', // 'context-1m-2025-08-07', // 'context-management-2025-06-27', // 'model-context-window-exceeded-2025-08-26', // 'skills-2025-10-02', "effort-2025-11-24", // 'advanced-tool-use-2025-11-20', "structured-outputs-2025-11-13", "task-budgets-2026-03-13" ]; function toAnthropicSchema(schema) { const out = structuredClone(schema); delete out.$schema; if (out.type === "object") { out.additionalProperties = false; } for (const key in out) { if (typeof out[key] === "object" && out[key] !== null) { out[key] = toAnthropicSchema(out[key]); } } return out; } class BetaRunner extends BaseRunner { constructor(params) { super(params); } /** * Map a Genkit Part -> Anthropic beta content block param. * Supports: text, images (base64 data URLs), PDFs (document source), * tool_use (client tool request), tool_result (client tool response). */ toAnthropicMessageContent(part) { if (part.reasoning) { const signature = this.getThinkingSignature(part); if (!signature) { throw new Error( "Anthropic thinking parts require a signature when sending back to the API. Preserve the `metadata.thoughtSignature` value from the original response." ); } return { type: "thinking", thinking: part.reasoning, signature }; } const redactedThinking = this.getRedactedThinkingData(part); if (redactedThinking !== void 0) { return { type: "redacted_thinking", data: redactedThinking }; } if (part.text) { return { type: "text", text: part.text }; } if (part.custom?.anthropicDocument) { return toBetaDocumentBlock( part.custom.anthropicDocument ); } if (part.media) { if (part.media.contentType === "anthropic/file") { return { type: "document", source: { type: "file", file_id: part.media.url } }; } if (part.media.contentType === "anthropic/image") { return { type: "image", source: { type: "file", file_id: part.media.url } }; } if (part.media.contentType === "application/pdf") { return { type: "document", source: this.toPdfDocumentSource(part.media) }; } const source = this.toImageSource(part.media); if (source.kind === "base64") { return { type: "image", source: { type: "base64", data: source.data, media_type: source.mediaType } }; } return { type: "image", source: { type: "url", url: source.url } }; } if (part.toolRequest) { if (!part.toolRequest.ref) { throw new Error( `Tool request ref is required for Anthropic API. Part: ${JSON.stringify( part.toolRequest )}` ); } return { type: "tool_use", id: part.toolRequest.ref, name: part.toolRequest.name, input: part.toolRequest.input }; } if (part.toolResponse) { if (!part.toolResponse.ref) { throw new Error( `Tool response ref is required for Anthropic API. Part: ${JSON.stringify( part.toolResponse )}` ); } const betaResult = { type: "tool_result", tool_use_id: part.toolResponse.ref, content: [this.toAnthropicToolResponseContent(part)] }; return betaResult; } throw new Error( `Unsupported genkit part fields encountered for current message role: ${JSON.stringify( part )}.` ); } createMessage(body, abortSignal) { return this.client.beta.messages.create(body, { signal: abortSignal }); } streamMessages(body, abortSignal) { return this.client.beta.messages.stream(body, { signal: abortSignal }); } /** * Build non-streaming request body. */ toAnthropicRequestBody(modelName, request) { const { system, messages } = this.toAnthropicMessages(request.messages); const mappedModelName = request.config?.version ?? checkModelName(modelName); const thinkingConfig = this.toAnthropicThinkingConfig( request.config?.thinking ); const { topP, topK, apiVersion: _1, thinking: _2, maxOutputTokens, stopSequences, version, apiKey, ...restConfig } = request.config ?? {}; const body = { model: mappedModelName, max_tokens: maxOutputTokens ?? this.DEFAULT_MAX_OUTPUT_TOKENS, messages, system, stop_sequences: stopSequences, temperature: request.config?.temperature, top_k: topK, top_p: topP, tool_choice: request.config?.tool_choice, metadata: request.config?.metadata, tools: request.tools?.map((tool) => this.toAnthropicTool(tool)), thinking: thinkingConfig, output_format: this.isStructuredOutputEnabled(request) ? { type: "json_schema", schema: toAnthropicSchema(request.output.schema) } : void 0, betas: Array.isArray(request.config?.betas) ? [...request.config?.betas ?? []] : [...BETA_APIS], ...restConfig }; return removeUndefinedProperties(body); } /** * Build streaming request body. */ toAnthropicStreamingRequestBody(modelName, request) { const { system, messages } = this.toAnthropicMessages(request.messages); const mappedModelName = request.config?.version ?? checkModelName(modelName); const thinkingConfig = this.toAnthropicThinkingConfig( request.config?.thinking ); const { topP, topK, apiVersion: _1, thinking: _2, maxOutputTokens, stopSequences, version, apiKey, ...restConfig } = request.config ?? {}; const body = { model: mappedModelName, max_tokens: maxOutputTokens ?? this.DEFAULT_MAX_OUTPUT_TOKENS, messages, stream: true, system, stop_sequences: stopSequences, temperature: request.config?.temperature, top_k: topK, top_p: topP, tool_choice: request.config?.tool_choice, metadata: request.config?.metadata, tools: request.tools?.map((tool) => this.toAnthropicTool(tool)), thinking: thinkingConfig, output_format: this.isStructuredOutputEnabled(request) ? { type: "json_schema", schema: toAnthropicSchema(request.output.schema) } : void 0, betas: Array.isArray(request.config?.betas) ? [...request.config?.betas ?? []] : [...BETA_APIS], ...restConfig }; return removeUndefinedProperties(body); } toGenkitResponse(message) { return { candidates: [ { index: 0, finishReason: this.fromBetaStopReason(message.stop_reason), message: { role: "model", content: message.content.map( (block) => this.fromBetaContentBlock(block) ) } } ], usage: { inputTokens: message.usage.input_tokens, outputTokens: message.usage.output_tokens }, custom: message, raw: message }; } toGenkitPart(event) { if (event.type === "content_block_start") { return this.fromBetaContentBlock(event.content_block); } if (event.type === "content_block_delta") { if (event.delta.type === "text_delta") { return textDeltaToPart(event.delta); } if (event.delta.type === "thinking_delta") { return thinkingDeltaToPart(event.delta); } if (event.delta.type === "citations_delta") { return citationsDeltaToPart(event.delta); } return void 0; } return void 0; } fromBetaContentBlock(contentBlock) { switch (contentBlock.type) { case "text": return textBlockToPart(contentBlock); case "tool_use": return toolUseBlockToPart({ id: contentBlock.id, name: contentBlock.name ?? "unknown_tool", input: contentBlock.input }); case "thinking": return thinkingBlockToPart(contentBlock); case "redacted_thinking": return redactedThinkingBlockToPart(contentBlock); case "server_tool_use": return betaServerToolUseBlockToPart(contentBlock); case "web_search_tool_result": return webSearchToolResultBlockToPart(contentBlock); // Unsupported beta server tool types case "mcp_tool_use": case "mcp_tool_result": case "web_fetch_tool_result": case "code_execution_tool_result": case "bash_code_execution_tool_result": case "text_editor_code_execution_tool_result": case "container_upload": case "tool_search_tool_result": throw new Error(unsupportedServerToolError(contentBlock.type)); default: { const unknownType = contentBlock.type; logger.warn( `Unexpected Anthropic beta content block type: ${unknownType}. Returning empty text. Content block: ${JSON.stringify(contentBlock)}` ); return { text: "" }; } } } fromBetaStopReason(reason) { switch (reason) { case "max_tokens": case "model_context_window_exceeded": return "length"; case "end_turn": case "stop_sequence": case "tool_use": case "pause_turn": return "stop"; case null: return "unknown"; case "refusal": return "other"; default: return "other"; } } isStructuredOutputEnabled(request) { return !!(request.output?.schema && request.output.constrained && request.output.format === "json"); } } export { BetaRunner }; //# sourceMappingURL=beta.mjs.map