genkitx-anthropic
Version:
Firebase Genkit AI framework plugin for Anthropic APIs.
430 lines • 12.4 kB
JavaScript
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