diff --git a/js/plugins/anthropic/.gitignore b/js/plugins/anthropic/.gitignore
new file mode 100644
index 0000000000..d83aca04ae
--- /dev/null
+++ b/js/plugins/anthropic/.gitignore
@@ -0,0 +1,3 @@
+lib/
+node_modules/
+coverage/
diff --git a/js/plugins/anthropic/.npmignore b/js/plugins/anthropic/.npmignore
new file mode 100644
index 0000000000..d265d4ab73
--- /dev/null
+++ b/js/plugins/anthropic/.npmignore
@@ -0,0 +1,17 @@
+# typescript source files
+src/
+tests/
+tsconfig.json
+tsup.common.ts
+tsup.config.ts
+
+# GitHub files
+.github/
+.gitignore
+.npmignore
+CODE_OF_CONDUCT.md
+CONTRIBUTING.md
+
+# Developer related files
+.devcontainer/
+.vscode/
diff --git a/js/plugins/anthropic/NOTICE b/js/plugins/anthropic/NOTICE
new file mode 100644
index 0000000000..dc335bb090
--- /dev/null
+++ b/js/plugins/anthropic/NOTICE
@@ -0,0 +1,8 @@
+This project includes code derived from the Firebase Genkit Anthropic community plugin
+(https://github.com/BloomLabsInc/genkit-plugins/tree/main/plugins/anthropic).
+
+Copyright 2024 Bloom Labs Inc.
+Copyright 2025 Google LLC.
+
+Licensed under the Apache License, Version 2.0.
+See the LICENSE file distributed with this project for the full license text.
diff --git a/js/plugins/anthropic/README.md b/js/plugins/anthropic/README.md
new file mode 100644
index 0000000000..ec9c9115c0
--- /dev/null
+++ b/js/plugins/anthropic/README.md
@@ -0,0 +1,195 @@
+# Firebase Genkit + Anthropic AI
+
+
Firebase Genkit <> Anthropic AI Plugin
+
+Anthropic AI plugin for Google Firebase Genkit
+
+`@genkit-ai/anthropic` is the official Anthropic plugin for [Firebase Genkit](https://github.com/firebase/genkit). It supersedes the earlier community package `genkitx-anthropic` and is now maintained by Google.
+
+## Supported models
+
+The plugin supports the most recent Anthropic models: **Claude Sonnet 4.5**, **Claude Opus 4.1**, **Claude Haiku 4.5**, **Claude Sonnet 4**, **Claude Opus 4**, **Claude 3.5 Haiku**, and **Claude 3 Haiku**.
+
+## Installation
+
+Install the plugin in your project with your favorite package manager:
+
+- `npm install @genkit-ai/anthropic`
+- `yarn add @genkit-ai/anthropic`
+- `pnpm add @genkit-ai/anthropic`
+
+## Usage
+
+### Initialize
+
+```typescript
+import { genkit } from 'genkit';
+import { anthropic } from '@genkit-ai/anthropic';
+
+const ai = genkit({
+ plugins: [anthropic({ apiKey: process.env.ANTHROPIC_API_KEY })],
+ // specify a default model for generate here if you wish:
+ model: anthropic.model('claude-sonnet-4-5'),
+});
+```
+
+### Basic examples
+
+The simplest way to generate text is by using the `generate` method:
+
+```typescript
+const response = await ai.generate({
+ model: anthropic.model('claude-3-haiku'),
+ prompt: 'Tell me a joke.',
+});
+
+console.log(response.text);
+```
+
+### Multi-modal prompt
+
+```typescript
+// ...initialize Genkit instance (as shown above)...
+
+const response = await ai.generate({
+ prompt: [
+ { text: 'What animal is in the photo?' },
+ { media: { url: imageUrl } },
+ ],
+ config: {
+ // control of the level of visual detail when processing image embeddings
+ // Low detail level also decreases the token usage
+ visualDetailLevel: 'low',
+ },
+});
+console.log(response.text);
+```
+
+### Extended thinking
+
+Claude 4 models can expose their internal reasoning. Enable it per-request with the Anthropic thinking config and read the reasoning from the response:
+
+```typescript
+const response = await ai.generate({
+ prompt: 'Walk me through your reasoning for Fermat’s little theorem.',
+ config: {
+ thinking: {
+ enabled: true,
+ budgetTokens: 4096, // Must be >= 1024 and less than max_tokens
+ },
+ },
+});
+
+console.log(response.text); // Final assistant answer
+console.log(response.reasoning); // Summarized thinking steps
+```
+
+When thinking is enabled, request bodies sent through the plugin include the `thinking` payload (`{ type: 'enabled', budget_tokens: … }`) that Anthropic's API expects, and streamed responses deliver `reasoning` parts as they arrive so you can render the chain-of-thought incrementally.
+
+### Beta API Limitations
+
+The beta API surface provides access to experimental features, but some server-managed tool blocks are not yet supported by this plugin. The following beta API features will cause an error if encountered:
+
+- `web_fetch_tool_result`
+- `code_execution_tool_result`
+- `bash_code_execution_tool_result`
+- `text_editor_code_execution_tool_result`
+- `mcp_tool_result`
+- `mcp_tool_use`
+- `container_upload`
+
+Note that `server_tool_use` and `web_search_tool_result` ARE supported and work with both stable and beta APIs.
+
+### Within a flow
+
+```typescript
+import { z } from 'genkit';
+
+// ...initialize Genkit instance (as shown above)...
+
+export const jokeFlow = ai.defineFlow(
+ {
+ name: 'jokeFlow',
+ inputSchema: z.string(),
+ outputSchema: z.string(),
+ },
+ async (subject) => {
+ const llmResponse = await ai.generate({
+ prompt: `tell me a joke about ${subject}`,
+ });
+ return llmResponse.text;
+ }
+);
+```
+
+### Direct model usage (without Genkit instance)
+
+The plugin supports Genkit Plugin API v2, which allows you to use models directly without initializing the full Genkit framework:
+
+```typescript
+import { anthropic } from '@genkit-ai/anthropic';
+
+// Create a model reference directly
+const claude = anthropic.model('claude-sonnet-4-5');
+
+// Use the model directly
+const response = await claude({
+ messages: [
+ {
+ role: 'user',
+ content: [{ text: 'Tell me a joke.' }],
+ },
+ ],
+});
+
+console.log(response);
+```
+
+You can also create model references using the plugin's `model()` method:
+
+```typescript
+import { anthropic } from '@genkit-ai/anthropic';
+
+// Create model references
+const claudeSonnet45 = anthropic.model('claude-sonnet-4-5');
+const claudeOpus41 = anthropic.model('claude-opus-4-1');
+const claude35Haiku = anthropic.model('claude-3-5-haiku');
+
+// Use the model reference directly
+const response = await claudeSonnet45({
+ messages: [
+ {
+ role: 'user',
+ content: [{ text: 'Hello!' }],
+ },
+ ],
+});
+```
+
+This approach is useful for:
+
+- Framework developers who need raw model access
+- Testing models in isolation
+- Using Genkit models in non-Genkit applications
+
+## Acknowledgements
+
+This plugin builds on the community work published as [`genkitx-anthropic`](https://github.com/BloomLabsInc/genkit-plugins/blob/main/plugins/anthropic/README.md) by Bloom Labs Inc. Their Apache 2.0–licensed implementation provided the foundation for this maintained package.
+
+## Contributing
+
+Want to contribute to the project? That's awesome! Head over to our [Contribution Guidelines](CONTRIBUTING.md).
+
+## Need support?
+
+> [!NOTE]
+> This repository depends on Google's Firebase Genkit. For issues and questions related to Genkit, please refer to instructions available in [Genkit's repository](https://github.com/firebase/genkit).
+
+
+## Credits
+
+This plugin is maintained by Google with acknowledgement to the community contributions from [Bloom Labs Inc](https://github.com/BloomLabsInc).
+
+## License
+
+This project is licensed under the [Apache 2.0 License](https://github.com/BloomLabsInc/genkit-plugins/blob/main/LICENSE).
diff --git a/js/plugins/anthropic/package.json b/js/plugins/anthropic/package.json
new file mode 100644
index 0000000000..e3c3e5f036
--- /dev/null
+++ b/js/plugins/anthropic/package.json
@@ -0,0 +1,70 @@
+{
+ "name": "@genkit-ai/anthropic",
+ "description": "Genkit AI framework plugin for Anthropic APIs.",
+ "keywords": [
+ "genkit",
+ "genkit-plugin",
+ "genkit-model",
+ "anthropic",
+ "anthropic-ai",
+ "claude-4",
+ "haiku-4",
+ "opus",
+ "haiku",
+ "sonnet",
+ "ai",
+ "genai",
+ "generative-ai"
+ ],
+ "version": "1.21.0",
+ "type": "commonjs",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/firebase/genkit.git",
+ "directory": "js/plugins/anthropic"
+ },
+ "author": "genkit",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "genkit": "workspace:^"
+ },
+ "dependencies": {
+ "@anthropic-ai/sdk": "^0.68.0"
+ },
+ "devDependencies": {
+ "@types/node": "^20.11.16",
+ "check-node-version": "^4.2.1",
+ "genkit": "workspace:*",
+ "npm-run-all": "^4.1.5",
+ "rimraf": "^6.0.1",
+ "tsup": "^8.3.5",
+ "tsx": "^4.19.2",
+ "typescript": "^4.9.0"
+ },
+ "types": "./lib/index.d.ts",
+ "exports": {
+ ".": {
+ "types": "./lib/index.d.ts",
+ "require": "./lib/index.js",
+ "import": "./lib/index.mjs",
+ "default": "./lib/index.js"
+ }
+ },
+ "files": [
+ "lib"
+ ],
+ "publishConfig": {
+ "provenance": true,
+ "access": "public"
+ },
+ "scripts": {
+ "check": "tsc",
+ "compile": "tsup-node",
+ "build:clean": "rimraf ./lib",
+ "build": "npm-run-all build:clean check compile",
+ "build:watch": "tsup-node --watch",
+ "test": "tsx --test tests/*_test.ts",
+ "test:file": "tsx --test",
+ "test:coverage": "check-node-version --node '>=22' && tsx --test --experimental-test-coverage --test-coverage-include='src/**/*.ts' ./tests/**/*_test.ts"
+ }
+}
diff --git a/js/plugins/anthropic/src/index.ts b/js/plugins/anthropic/src/index.ts
new file mode 100644
index 0000000000..d5a0fef9cb
--- /dev/null
+++ b/js/plugins/anthropic/src/index.ts
@@ -0,0 +1,155 @@
+/**
+ * Copyright 2024 Bloom Labs Inc
+ * Copyright 2025 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Anthropic from '@anthropic-ai/sdk';
+import { genkitPluginV2, type GenkitPluginV2 } from 'genkit/plugin';
+
+import { ActionMetadata, ModelReference, z } from 'genkit';
+import { ModelAction } from 'genkit/model';
+import { ActionType } from 'genkit/registry';
+import { listActions } from './list.js';
+import {
+ AnthropicConfigSchemaType,
+ ClaudeConfig,
+ ClaudeModelName,
+ KNOWN_CLAUDE_MODELS,
+ KnownClaudeModels,
+ claudeModel,
+ claudeModelReference,
+} from './models.js';
+import { InternalPluginOptions, PluginOptions, __testClient } from './types.js';
+
+const PROMPT_CACHING_BETA_HEADER_VALUE = 'prompt-caching-2024-07-31';
+
+/**
+ * Gets or creates an Anthropic client instance.
+ * Supports test client injection for internal testing.
+ */
+function getAnthropicClient(options?: PluginOptions): Anthropic {
+ // Check for test client injection first (internal use only)
+ const internalOptions = options as InternalPluginOptions | undefined;
+ if (internalOptions?.[__testClient]) {
+ return internalOptions[__testClient];
+ }
+
+ // Production path: create real client
+ const apiKey = options?.apiKey || process.env.ANTHROPIC_API_KEY;
+ if (!apiKey) {
+ throw new Error(
+ 'Please pass in the API key or set the ANTHROPIC_API_KEY environment variable'
+ );
+ }
+ const defaultHeaders: Record = {};
+ if (options?.cacheSystemPrompt) {
+ defaultHeaders['anthropic-beta'] = PROMPT_CACHING_BETA_HEADER_VALUE;
+ }
+ return new Anthropic({ apiKey, defaultHeaders });
+}
+
+/**
+ * This module provides an interface to the Anthropic AI models through the Genkit plugin system.
+ * It allows users to interact with various Claude models by providing an API key and optional configuration.
+ *
+ * The main export is the `anthropic` plugin, which can be configured with an API key either directly or through
+ * environment variables. It initializes the Anthropic client and makes available the Claude models for use.
+ *
+ * Exports:
+ * - anthropic: The main plugin function to interact with the Anthropic AI.
+ *
+ * Usage:
+ * To use the Claude models, initialize the anthropic plugin inside `genkit()` and pass the configuration options. If no API key is provided in the options, the environment variable `ANTHROPIC_API_KEY` must be set. If you want to cache the system prompt, set `cacheSystemPrompt` to `true`. **Note:** Prompt caching is in beta and may change. To learn more, see https://docs.anthropic.com/en/docs/prompt-caching.
+ *
+ * Example:
+ * ```
+ * import { anthropic } from '@genkit-ai/anthropic';
+ * import { genkit } from 'genkit';
+ *
+ * const ai = genkit({
+ * plugins: [
+ * anthropic({ apiKey: 'your-api-key', cacheSystemPrompt: false })
+ * ... // other plugins
+ * ]
+ * });
+ *
+ * // Access models via the plugin's model() method:
+ * const model = anthropic.model('claude-sonnet-4');
+ * ```
+ */
+function anthropicPlugin(options?: PluginOptions): GenkitPluginV2 {
+ const client = getAnthropicClient(options);
+ const defaultApiVersion = options?.apiVersion;
+
+ let listActionsCache: ActionMetadata[] | null = null;
+
+ return genkitPluginV2({
+ name: 'anthropic',
+ init: async () => {
+ const actions: ModelAction[] = [];
+ for (const name of Object.keys(KNOWN_CLAUDE_MODELS)) {
+ const action = claudeModel({
+ name,
+ client,
+ cacheSystemPrompt: options?.cacheSystemPrompt,
+ defaultApiVersion,
+ });
+ actions.push(action);
+ }
+ return actions;
+ },
+ resolve: (actionType: ActionType, name: string) => {
+ if (actionType === 'model') {
+ // Strip the 'anthropic/' namespace prefix if present
+ const modelName = name.startsWith('anthropic/') ? name.slice(10) : name;
+ return claudeModel({
+ name: modelName,
+ client,
+ cacheSystemPrompt: options?.cacheSystemPrompt,
+ defaultApiVersion,
+ });
+ }
+ return undefined;
+ },
+ list: async () => {
+ if (listActionsCache) return listActionsCache;
+ listActionsCache = await listActions(client);
+ return listActionsCache;
+ },
+ });
+}
+
+export type AnthropicPlugin = {
+ (pluginOptions?: PluginOptions): GenkitPluginV2;
+ model(
+ name: KnownClaudeModels | (ClaudeModelName & {}),
+ config?: ClaudeConfig
+ ): ModelReference;
+ model(name: string, config?: any): ModelReference;
+};
+
+/**
+ * Anthropic AI plugin for Genkit.
+ * Includes Claude models (3, 3.5, and 4 series).
+ */
+export const anthropic = anthropicPlugin as AnthropicPlugin;
+(anthropic as any).model = (
+ name: string,
+ config?: any
+): ModelReference => {
+ return claudeModelReference(name, config);
+};
+
+export default anthropic;
diff --git a/js/plugins/anthropic/src/list.ts b/js/plugins/anthropic/src/list.ts
new file mode 100644
index 0000000000..f04b1cf082
--- /dev/null
+++ b/js/plugins/anthropic/src/list.ts
@@ -0,0 +1,192 @@
+/**
+ * Copyright 2024 Bloom Labs Inc
+ * Copyright 2025 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Anthropic from '@anthropic-ai/sdk';
+import { modelActionMetadata } from 'genkit/plugin';
+
+import { ActionMetadata, ModelReference, z } from 'genkit';
+import { GENERIC_CLAUDE_MODEL_INFO, KNOWN_CLAUDE_MODELS } from './models.js';
+import { AnthropicConfigSchema } from './types.js';
+
+function normalizeModelId(modelId: string): string {
+ // Strip date suffixes (e.g. "-20241001") or "-latest" so lookups hit canonical keys.
+ return modelId.replace(/-(?:\d{8}|latest)$/i, '');
+}
+
+type ModelMetadataParams = Parameters[0];
+
+interface MergeKnownModelMetadataParams {
+ modelId: string;
+ ref: ModelReference;
+ metadataByName: Map;
+ orderedNames: string[];
+}
+/**
+ * Integrates metadata for a known Claude model into the aggregated list.
+ *
+ * It merges version information collected from the Anthropic API onto the
+ * canonical model definition while preserving any additional metadata that
+ * may already exist in the accumulator.
+ */
+function mergeKnownModelMetadata({
+ modelId,
+ ref,
+ metadataByName,
+ orderedNames,
+}: MergeKnownModelMetadataParams): void {
+ // Merge onto any prior metadata we have for this named model.
+ const existing = metadataByName.get(ref.name);
+ const priorInfo = existing?.info ?? ref.info ?? {};
+ const priorVersions = Array.isArray(priorInfo.versions)
+ ? priorInfo.versions
+ : (ref.info?.versions ?? []);
+
+ // Track every concrete model ID surfaced by the API so they appear as selectable versions.
+ const versions = new Set(priorVersions);
+ versions.add(modelId);
+
+ metadataByName.set(ref.name, {
+ name: ref.name,
+ info: {
+ ...priorInfo,
+ versions: Array.from(versions),
+ },
+ configSchema: ref.configSchema,
+ });
+
+ if (!existing) {
+ // Preserve the discovery order for determinism.
+ orderedNames.push(ref.name);
+ }
+}
+
+interface MergeFallbackModelMetadataParams {
+ modelId: string;
+ normalizedId: string;
+ displayName?: string;
+ metadataByName: Map;
+ orderedNames: string[];
+}
+
+/**
+ * Creates or updates metadata entries for Anthropic models that are not
+ * explicitly enumerated in `KNOWN_CLAUDE_MODELS`.
+ *
+ * The resulting metadata uses a generic Claude descriptor while capturing
+ * the specific model ID returned by the API so it can be surfaced in the
+ * Genkit UI.
+ */
+function mergeFallbackModelMetadata({
+ modelId,
+ normalizedId,
+ displayName,
+ metadataByName,
+ orderedNames,
+}: MergeFallbackModelMetadataParams): void {
+ const fallbackName = `anthropic/${modelId}`;
+ const existing = metadataByName.get(fallbackName);
+ const fallbackLabel =
+ displayName ??
+ `Anthropic - ${normalizedId !== modelId ? normalizedId : modelId}`;
+
+ if (existing) {
+ const priorVersions = existing.info?.versions ?? [];
+ const versions = new Set(
+ Array.isArray(priorVersions) ? priorVersions : []
+ );
+ versions.add(modelId);
+
+ metadataByName.set(fallbackName, {
+ ...existing,
+ info: {
+ ...existing.info,
+ versions: Array.from(versions),
+ },
+ });
+ return;
+ }
+
+ metadataByName.set(fallbackName, {
+ name: fallbackName,
+ info: {
+ ...GENERIC_CLAUDE_MODEL_INFO,
+ label: fallbackLabel,
+ versions: modelId ? [modelId] : [...GENERIC_CLAUDE_MODEL_INFO.versions],
+ supports: {
+ ...GENERIC_CLAUDE_MODEL_INFO.supports,
+ output: [...GENERIC_CLAUDE_MODEL_INFO.supports.output],
+ },
+ },
+ configSchema: AnthropicConfigSchema,
+ });
+ orderedNames.push(fallbackName);
+}
+
+/**
+ * Retrieves available Anthropic models from the API and converts them into Genkit action metadata.
+ *
+ * This function queries the Anthropic API for the list of available models, matches them against
+ * known Claude models, and generates metadata for both known and unknown models. The resulting
+ * metadata includes version information and configuration schemas suitable for use in Genkit.
+ *
+ * @param client - The Anthropic API client instance
+ * @returns A promise that resolves to an array of action metadata for all discovered models
+ */
+export async function listActions(
+ client: Anthropic
+): Promise {
+ const clientModels = (await client.models.list()).data;
+ const metadataByName = new Map();
+ const orderedNames: string[] = [];
+
+ for (const modelInfo of clientModels) {
+ const modelId = modelInfo.id;
+ if (!modelId) {
+ continue;
+ }
+
+ const normalizedId = normalizeModelId(modelId);
+ const ref = KNOWN_CLAUDE_MODELS[normalizedId];
+
+ if (ref) {
+ mergeKnownModelMetadata({
+ modelId,
+ ref,
+ metadataByName,
+ orderedNames,
+ });
+ continue;
+ }
+
+ // For models we don't explicitly track, synthesize a generic entry that still surfaces the ID.
+ mergeFallbackModelMetadata({
+ modelId,
+ normalizedId,
+ displayName: modelInfo.display_name ?? undefined,
+ metadataByName,
+ orderedNames,
+ });
+ }
+
+ return orderedNames.map((name) => {
+ const metadata = metadataByName.get(name);
+ if (!metadata) {
+ throw new Error(`Missing metadata for model: ${name}`);
+ }
+ return modelActionMetadata(metadata);
+ });
+}
diff --git a/js/plugins/anthropic/src/models.ts b/js/plugins/anthropic/src/models.ts
new file mode 100644
index 0000000000..ab9f1a198e
--- /dev/null
+++ b/js/plugins/anthropic/src/models.ts
@@ -0,0 +1,371 @@
+/**
+ * Copyright 2024 Bloom Labs Inc
+ * Copyright 2025 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type Anthropic from '@anthropic-ai/sdk';
+import type {
+ GenerateRequest,
+ GenerateResponseData,
+ ModelReference,
+ StreamingCallback,
+} from 'genkit';
+import { z } from 'genkit';
+import type { GenerateResponseChunkData, ModelAction } from 'genkit/model';
+import { modelRef } from 'genkit/model';
+import { model } from 'genkit/plugin';
+
+import type { ModelInfo } from 'genkit/model';
+import { BetaRunner, Runner } from './runner/index.js';
+import {
+ AnthropicBaseConfigSchema,
+ AnthropicBaseConfigSchemaType,
+ AnthropicConfigSchema,
+ AnthropicThinkingConfigSchema,
+ resolveBetaEnabled,
+ type ClaudeModelParams,
+ type ClaudeRunnerParams,
+} from './types.js';
+
+// This contains all the Anthropic config schema types
+type ConfigSchemaType =
+ | AnthropicBaseConfigSchemaType
+ | AnthropicThinkingConfigSchemaType;
+
+/**
+ * Creates a model reference for a Claude model.
+ * Computes the default version from info.versions array and sets it on the modelRef.
+ */
+function commonRef(
+ name: string,
+ info?: ModelInfo,
+ configSchema: ConfigSchemaType = AnthropicConfigSchema
+): ModelReference {
+ // Compute default version from info.versions array
+ let defaultVersion: string | undefined;
+ if (info?.versions && info.versions.length > 0) {
+ // Prefer version with '-latest' suffix
+ const latestVersion = info.versions.find((v) => v.endsWith('-latest'));
+ if (latestVersion) {
+ defaultVersion = latestVersion;
+ } else if (info.versions.includes(name)) {
+ // If base name exists in versions array, use it directly
+ defaultVersion = name;
+ } else {
+ // Otherwise use first version
+ defaultVersion = info.versions[0];
+ }
+ }
+
+ return modelRef({
+ name: `anthropic/${name}`,
+ configSchema,
+ version: defaultVersion,
+ info: info ?? {
+ supports: {
+ multiturn: true,
+ tools: true,
+ media: true,
+ systemRole: true,
+ output: ['text'],
+ },
+ },
+ });
+}
+
+export const KNOWN_CLAUDE_MODELS: Record<
+ string,
+ ModelReference<
+ AnthropicBaseConfigSchemaType | AnthropicThinkingConfigSchemaType
+ >
+> = {
+ 'claude-3-haiku': commonRef(
+ 'claude-3-haiku',
+ {
+ versions: ['claude-3-haiku-20240307'],
+ label: 'Anthropic - Claude 3 Haiku',
+ supports: {
+ multiturn: true,
+ tools: true,
+ media: true,
+ systemRole: true,
+ output: ['text'],
+ },
+ },
+ AnthropicBaseConfigSchema
+ ),
+ 'claude-3-5-haiku': commonRef(
+ 'claude-3-5-haiku',
+ {
+ versions: ['claude-3-5-haiku-20241022', 'claude-3-5-haiku'],
+ label: 'Anthropic - Claude 3.5 Haiku',
+ supports: {
+ multiturn: true,
+ tools: true,
+ media: true,
+ systemRole: true,
+ output: ['text'],
+ },
+ },
+ AnthropicBaseConfigSchema
+ ),
+ 'claude-sonnet-4': commonRef(
+ 'claude-sonnet-4',
+ {
+ versions: ['claude-sonnet-4-20250514'],
+ label: 'Anthropic - Claude Sonnet 4',
+ supports: {
+ multiturn: true,
+ tools: true,
+ media: true,
+ systemRole: true,
+ output: ['text'],
+ },
+ },
+ AnthropicThinkingConfigSchema
+ ),
+ 'claude-opus-4': commonRef(
+ 'claude-opus-4',
+ {
+ versions: ['claude-opus-4-20250514'],
+ label: 'Anthropic - Claude Opus 4',
+ supports: {
+ multiturn: true,
+ tools: true,
+ media: true,
+ systemRole: true,
+ output: ['text'],
+ },
+ },
+ AnthropicThinkingConfigSchema
+ ),
+ 'claude-sonnet-4-5': commonRef(
+ 'claude-sonnet-4-5',
+ {
+ versions: ['claude-sonnet-4-5-20250929', 'claude-sonnet-4-5'],
+ label: 'Anthropic - Claude Sonnet 4.5',
+ supports: {
+ multiturn: true,
+ tools: true,
+ media: true,
+ systemRole: true,
+ output: ['text'],
+ },
+ },
+ AnthropicThinkingConfigSchema
+ ),
+ 'claude-haiku-4-5': commonRef(
+ 'claude-haiku-4-5',
+ {
+ versions: ['claude-haiku-4-5-20251001', 'claude-haiku-4-5'],
+ label: 'Anthropic - Claude Haiku 4.5',
+ supports: {
+ multiturn: true,
+ tools: true,
+ media: true,
+ systemRole: true,
+ output: ['text'],
+ },
+ },
+ AnthropicThinkingConfigSchema
+ ),
+ 'claude-opus-4-1': commonRef(
+ 'claude-opus-4-1',
+ {
+ versions: ['claude-opus-4-1-20250805', 'claude-opus-4-1'],
+ label: 'Anthropic - Claude Opus 4.1',
+ supports: {
+ multiturn: true,
+ tools: true,
+ media: true,
+ systemRole: true,
+ output: ['text'],
+ },
+ },
+ AnthropicThinkingConfigSchema
+ ),
+};
+
+/**
+ * Gets the un-prefixed model name from a modelReference.
+ */
+export function extractVersion(
+ model: ModelReference | undefined,
+ modelName: string
+): string {
+ if (model?.version) {
+ return model.version;
+ }
+ // Fallback: extract from model name (remove 'anthropic/' prefix if present)
+ return modelName.replace(/^anthropic\//, '');
+}
+
+/**
+ * Generic Claude model info for unknown/unsupported models.
+ * Used when a model name is not in KNOWN_CLAUDE_MODELS.
+ */
+export const GENERIC_CLAUDE_MODEL_INFO = {
+ versions: [],
+ label: 'Anthropic - Claude',
+ supports: {
+ multiturn: true,
+ tools: true,
+ media: true,
+ systemRole: true,
+ output: ['text'],
+ },
+};
+
+export type KnownClaudeModels = keyof typeof KNOWN_CLAUDE_MODELS;
+export type ClaudeModelName = string;
+export type AnthropicConfigSchemaType = typeof AnthropicConfigSchema;
+export type AnthropicThinkingConfigSchemaType =
+ typeof AnthropicThinkingConfigSchema;
+export type ClaudeConfig = z.infer;
+
+/**
+ * Creates the runner used by Genkit to interact with the Claude model.
+ * @param params Configuration for the Claude runner.
+ * @param configSchema The config schema for this model (used for type inference).
+ * @returns The runner that Genkit will call when the model is invoked.
+ */
+export function claudeRunner(
+ params: ClaudeRunnerParams,
+ configSchema: TConfigSchema
+) {
+ const { defaultApiVersion, ...runnerParams } = params;
+
+ if (!runnerParams.client) {
+ throw new Error('Anthropic client is required to create a runner');
+ }
+
+ let stableRunner: Runner | null = null;
+ let betaRunner: BetaRunner | null = null;
+
+ return async (
+ request: GenerateRequest,
+ {
+ streamingRequested,
+ sendChunk,
+ abortSignal,
+ }: {
+ streamingRequested: boolean;
+ sendChunk: StreamingCallback;
+ abortSignal: AbortSignal;
+ }
+ ): Promise => {
+ // Cast to AnthropicConfigSchema for internal runner which expects the full schema
+ const normalizedRequest = request as unknown as GenerateRequest<
+ typeof AnthropicConfigSchema
+ >;
+ const isBeta = resolveBetaEnabled(
+ normalizedRequest.config,
+ defaultApiVersion
+ );
+ const runner = isBeta
+ ? (betaRunner ??= new BetaRunner(runnerParams))
+ : (stableRunner ??= new Runner(runnerParams));
+ return runner.run(normalizedRequest, {
+ streamingRequested,
+ sendChunk,
+ abortSignal,
+ });
+ };
+}
+
+/**
+ * Creates a model reference for a Claude model.
+ * This allows referencing models without initializing the plugin.
+ */
+export function claudeModelReference(
+ name: string,
+ config?: z.infer
+): ModelReference {
+ const knownModel = KNOWN_CLAUDE_MODELS[name];
+ if (knownModel) {
+ return modelRef({
+ name: knownModel.name,
+ info: knownModel.info,
+ configSchema: knownModel.configSchema,
+ version: knownModel.version,
+ config,
+ });
+ }
+
+ // For unknown models, create a basic reference
+ return modelRef({
+ name: `anthropic/${name}`,
+ configSchema: AnthropicConfigSchema,
+ config,
+ });
+}
+
+/**
+ * Defines a Claude model with the given name and Anthropic client.
+ * Accepts any model name and lets the API validate it. If the model is in KNOWN_CLAUDE_MODELS, uses that modelRef
+ * for better defaults; otherwise creates a generic model reference.
+ */
+export function claudeModel(
+ paramsOrName: ClaudeModelParams | string,
+ client?: Anthropic,
+ cacheSystemPrompt?: boolean,
+ defaultApiVersion?: 'stable' | 'beta'
+): ModelAction {
+ const params =
+ typeof paramsOrName === 'string'
+ ? {
+ name: paramsOrName,
+ client:
+ client ??
+ (() => {
+ throw new Error(
+ 'Anthropic client is required to create a model action'
+ );
+ })(),
+ cacheSystemPrompt,
+ defaultApiVersion,
+ }
+ : paramsOrName;
+
+ const {
+ name,
+ client: runnerClient,
+ cacheSystemPrompt: cachePrompt,
+ defaultApiVersion: apiVersion,
+ } = params;
+ // Use supported model ref if available, otherwise create generic model ref
+ const modelRef = KNOWN_CLAUDE_MODELS[name];
+ const modelInfo = modelRef ? modelRef.info : GENERIC_CLAUDE_MODEL_INFO;
+ const configSchema = modelRef?.configSchema ?? AnthropicConfigSchema;
+
+ return model<
+ AnthropicBaseConfigSchemaType | AnthropicThinkingConfigSchemaType
+ >(
+ {
+ name: `anthropic/${name}`,
+ ...modelInfo,
+ configSchema: configSchema,
+ },
+ claudeRunner(
+ {
+ name,
+ client: runnerClient,
+ cacheSystemPrompt: cachePrompt,
+ defaultApiVersion: apiVersion,
+ },
+ configSchema
+ )
+ );
+}
diff --git a/js/plugins/anthropic/src/runner/base.ts b/js/plugins/anthropic/src/runner/base.ts
new file mode 100644
index 0000000000..e6b7132e28
--- /dev/null
+++ b/js/plugins/anthropic/src/runner/base.ts
@@ -0,0 +1,550 @@
+/**
+ * Copyright 2025 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Anthropic } from '@anthropic-ai/sdk';
+import type { DocumentBlockParam } from '@anthropic-ai/sdk/resources/messages';
+import type {
+ GenerateRequest,
+ GenerateResponseChunkData,
+ GenerateResponseData,
+ MessageData,
+ Part,
+ Role,
+} from 'genkit';
+import { Message as GenkitMessage } from 'genkit';
+import type { ToolDefinition } from 'genkit/model';
+
+import {
+ AnthropicConfigSchema,
+ Media,
+ MediaSchema,
+ MediaType,
+ MediaTypeSchema,
+ type ClaudeRunnerParams,
+ type ThinkingConfig,
+} from '../types.js';
+
+import {
+ RunnerContentBlockParam,
+ RunnerMessage,
+ RunnerMessageParam,
+ RunnerRequestBody,
+ RunnerStream,
+ RunnerStreamEvent,
+ RunnerStreamingRequestBody,
+ RunnerTool,
+ RunnerToolResponseContent,
+ RunnerTypes,
+} from './types.js';
+
+const ANTHROPIC_THINKING_CUSTOM_KEY = 'anthropicThinking';
+
+/**
+ * Shared runner logic for Anthropic SDK integrations.
+ *
+ * Concrete subclasses pass in their SDK-specific type bundle via `RunnerTypes`,
+ * letting this base class handle message/tool translation once for both the
+ * stable and beta APIs that share the same conceptual surface.
+ */
+export abstract class BaseRunner {
+ protected name: string;
+ protected client: Anthropic;
+ protected cacheSystemPrompt?: boolean;
+
+ /**
+ * Default maximum output tokens for Claude models when not specified in the request.
+ */
+ protected readonly DEFAULT_MAX_OUTPUT_TOKENS = 4096;
+
+ constructor(params: ClaudeRunnerParams) {
+ this.name = params.name;
+ this.client = params.client;
+ this.cacheSystemPrompt = params.cacheSystemPrompt;
+ }
+
+ /**
+ * Converts a Genkit role to the corresponding Anthropic role.
+ */
+ protected toAnthropicRole(
+ role: Role,
+ toolMessageType?: 'tool_use' | 'tool_result'
+ ): 'user' | 'assistant' {
+ if (role === 'user') {
+ return 'user';
+ }
+ if (role === 'model') {
+ return 'assistant';
+ }
+ if (role === 'tool') {
+ return toolMessageType === 'tool_use' ? 'assistant' : 'user';
+ }
+ throw new Error(`Unsupported genkit role: ${role}`);
+ }
+
+ protected isMediaType(value: string): value is MediaType {
+ return MediaTypeSchema.safeParse(value).success;
+ }
+
+ protected isMediaObject(obj: unknown): obj is Media {
+ return MediaSchema.safeParse(obj).success;
+ }
+
+ /**
+ * Checks if a URL is a data URL (starts with 'data:').
+ */
+ protected isDataUrl(url: string): boolean {
+ return url.startsWith('data:');
+ }
+
+ protected extractDataFromBase64Url(
+ url: string
+ ): { data: string; contentType: string } | null {
+ const match = url.match(/^data:([^;]+);base64,(.+)$/);
+ return (
+ match && {
+ contentType: match[1],
+ data: match[2],
+ }
+ );
+ }
+
+ /**
+ * Both the stable and beta Anthropic SDKs accept the same JSON shape for PDF
+ * document sources (either `type: 'base64'` with a base64 payload or `type: 'url'`
+ * with a public URL). Even though the return type references the stable SDK
+ * union, TypeScript’s structural typing lets the beta runner reuse this helper.
+ */
+ protected toPdfDocumentSource(media: Media): DocumentBlockParam['source'] {
+ if (media.contentType !== 'application/pdf') {
+ throw new Error(
+ `PDF contentType mismatch: expected application/pdf, got ${media.contentType}`
+ );
+ }
+ const url = media.url;
+ if (this.isDataUrl(url)) {
+ const extracted = this.extractDataFromBase64Url(url);
+ if (!extracted) {
+ throw new Error(
+ `Invalid PDF data URL format: ${url.substring(0, 50)}...`
+ );
+ }
+ const { data, contentType } = extracted;
+ if (contentType !== 'application/pdf') {
+ throw new Error(
+ `PDF contentType mismatch: expected application/pdf, got ${contentType}`
+ );
+ }
+ return {
+ type: 'base64',
+ media_type: 'application/pdf',
+ data,
+ };
+ }
+ return {
+ type: 'url',
+ url,
+ };
+ }
+
+ /**
+ * Normalizes Genkit `Media` into either a base64 payload or a remote URL
+ * accepted by the Anthropic SDK. Anthropic supports both `data:` URLs (which
+ * we forward as base64) and remote `https` URLs without additional handling.
+ */
+ protected toImageSource(
+ media: Media
+ ):
+ | { kind: 'base64'; data: string; mediaType: MediaType }
+ | { kind: 'url'; url: string } {
+ if (this.isDataUrl(media.url)) {
+ const extracted = this.extractDataFromBase64Url(media.url);
+ const { data, contentType } = extracted ?? {};
+ if (!data || !contentType) {
+ throw new Error(
+ `Invalid genkit part media provided to toAnthropicMessageContent: ${JSON.stringify(
+ media
+ )}.`
+ );
+ }
+
+ const resolvedMediaType = contentType;
+ if (!resolvedMediaType) {
+ throw new Error('Media type is required but was not provided');
+ }
+ if (!this.isMediaType(resolvedMediaType)) {
+ // Provide helpful error message for text files
+ if (resolvedMediaType === 'text/plain') {
+ throw new Error(
+ `Unsupported media type: ${resolvedMediaType}. Text files should be sent as text content in the message, not as media. For example, use { text: '...' } instead of { media: { url: '...', contentType: 'text/plain' } }`
+ );
+ }
+ throw new Error(`Unsupported media type: ${resolvedMediaType}`);
+ }
+ return {
+ kind: 'base64',
+ data,
+ mediaType: resolvedMediaType,
+ };
+ }
+
+ if (!media.url) {
+ throw new Error('Media url is required but was not provided');
+ }
+
+ // For non-data URLs, use the provided contentType or default to a generic type
+ // Note: Anthropic will validate the actual content when fetching from URL
+ if (media.contentType) {
+ if (!this.isMediaType(media.contentType)) {
+ // Provide helpful error message for text files
+ if (media.contentType === 'text/plain') {
+ throw new Error(
+ `Unsupported media type: ${media.contentType}. Text files should be sent as text content in the message, not as media. For example, use { text: '...' } instead of { media: { url: '...', contentType: 'text/plain' } }`
+ );
+ }
+ throw new Error(`Unsupported media type: ${media.contentType}`);
+ }
+ }
+
+ return {
+ kind: 'url',
+ url: media.url,
+ };
+ }
+
+ /**
+ * Converts tool response output to the appropriate Anthropic content format.
+ * Handles Media objects, data URLs, strings, and other outputs.
+ */
+ protected toAnthropicToolResponseContent(
+ part: Part
+ ): RunnerToolResponseContent {
+ const output = part.toolResponse?.output ?? {};
+
+ // Handle Media objects (images returned by tools)
+ if (this.isMediaObject(output)) {
+ const { data, contentType } =
+ this.extractDataFromBase64Url(output.url) ?? {};
+ if (data && contentType) {
+ if (!this.isMediaType(contentType)) {
+ // Provide helpful error message for text files
+ if (contentType === 'text/plain') {
+ throw new Error(
+ `Unsupported media type: ${contentType}. Text files should be sent as text content, not as media.`
+ );
+ }
+ throw new Error(`Unsupported media type: ${contentType}`);
+ }
+ return {
+ type: 'image',
+ source: {
+ type: 'base64',
+ data,
+ media_type: contentType,
+ },
+ };
+ }
+ }
+
+ // Handle string outputs - check if it's a data URL
+ if (typeof output === 'string') {
+ // Check if string is a data URL (e.g., "data:image/gif;base64,...")
+ if (this.isDataUrl(output)) {
+ const { data, contentType } =
+ this.extractDataFromBase64Url(output) ?? {};
+ if (data && contentType) {
+ if (!this.isMediaType(contentType)) {
+ // Provide helpful error message for text files
+ if (contentType === 'text/plain') {
+ throw new Error(
+ `Unsupported media type: ${contentType}. Text files should be sent as text content, not as media.`
+ );
+ }
+ throw new Error(`Unsupported media type: ${contentType}`);
+ }
+ return {
+ type: 'image',
+ source: {
+ type: 'base64',
+ data,
+ media_type: contentType,
+ },
+ };
+ }
+ }
+ // Regular string output
+ return {
+ type: 'text',
+ text: output,
+ };
+ }
+
+ // Handle other outputs by stringifying
+ return {
+ type: 'text',
+ text: JSON.stringify(output),
+ };
+ }
+
+ protected createThinkingPart(thinking: string, signature?: string): Part {
+ const custom =
+ signature !== undefined
+ ? {
+ [ANTHROPIC_THINKING_CUSTOM_KEY]: { signature },
+ }
+ : undefined;
+ return custom
+ ? {
+ reasoning: thinking,
+ custom,
+ }
+ : {
+ reasoning: thinking,
+ };
+ }
+
+ protected getThinkingSignature(part: Part): string | undefined {
+ const custom = part.custom as Record | undefined;
+ const thinkingValue = custom?.[ANTHROPIC_THINKING_CUSTOM_KEY];
+ if (
+ typeof thinkingValue === 'object' &&
+ thinkingValue !== null &&
+ 'signature' in thinkingValue &&
+ typeof (thinkingValue as { signature: unknown }).signature === 'string'
+ ) {
+ return (thinkingValue as { signature: string }).signature;
+ }
+ return undefined;
+ }
+
+ protected getRedactedThinkingData(part: Part): string | undefined {
+ const custom = part.custom as Record | undefined;
+ const redacted = custom?.redactedThinking;
+ return typeof redacted === 'string' ? redacted : undefined;
+ }
+
+ protected toAnthropicThinkingConfig(
+ config: ThinkingConfig | undefined
+ ):
+ | { type: 'enabled'; budget_tokens: number }
+ | { type: 'disabled' }
+ | undefined {
+ if (!config) return undefined;
+
+ const { enabled, budgetTokens } = config;
+
+ if (enabled === true) {
+ if (budgetTokens === undefined) {
+ return undefined;
+ }
+ return { type: 'enabled', budget_tokens: budgetTokens };
+ }
+
+ if (enabled === false) {
+ return { type: 'disabled' };
+ }
+
+ if (budgetTokens !== undefined) {
+ return { type: 'enabled', budget_tokens: budgetTokens };
+ }
+
+ return undefined;
+ }
+
+ protected toWebSearchToolResultPart(params: {
+ toolUseId: string;
+ content: unknown;
+ type: string;
+ }): Part {
+ const { toolUseId, content, type } = params;
+ return {
+ text: `[Anthropic server tool result ${toolUseId}] ${JSON.stringify(content)}`,
+ custom: {
+ anthropicServerToolResult: {
+ type,
+ toolUseId,
+ content,
+ },
+ },
+ };
+ }
+
+ /**
+ * Converts a Genkit Part to the corresponding Anthropic content block.
+ * Each runner implements this to return its specific API type.
+ */
+ protected abstract toAnthropicMessageContent(
+ part: Part
+ ): RunnerContentBlockParam;
+
+ /**
+ * Converts Genkit messages to Anthropic format.
+ * Extracts system message and converts remaining messages using the runner's
+ * toAnthropicMessageContent implementation.
+ */
+ protected toAnthropicMessages(messages: MessageData[]): {
+ system?: string;
+ messages: RunnerMessageParam[];
+ } {
+ let system: string | undefined;
+
+ if (messages[0]?.role === 'system') {
+ const systemMessage = messages[0];
+ const textParts: string[] = [];
+
+ for (const part of systemMessage.content ?? []) {
+ if (part.text) {
+ textParts.push(part.text);
+ } else if (part.media || part.toolRequest || part.toolResponse) {
+ throw new Error(
+ 'System messages can only contain text content. Media, tool requests, and tool responses are not supported in system messages.'
+ );
+ }
+ }
+
+ // Concatenate multiple text parts into a single string.
+ // Note: The Anthropic SDK supports system as string | Array,
+ // so we could alternatively preserve the multi-part structure as:
+ // system = textParts.map(text => ({ type: 'text', text }))
+ // However, concatenation is simpler and maintains semantic equivalence while
+ // keeping the cache control logic straightforward in the concrete runners.
+ system = textParts.length > 0 ? textParts.join('\n\n') : undefined;
+ }
+
+ const messagesToIterate =
+ system !== undefined ? messages.slice(1) : messages;
+ const anthropicMsgs: RunnerMessageParam[] = [];
+
+ for (const message of messagesToIterate) {
+ const msg = new GenkitMessage(message);
+
+ // Detect tool message kind from Genkit Parts (no SDK typing needed)
+ const hadToolUse = msg.content.some((p) => !!p.toolRequest);
+ const hadToolResult = msg.content.some((p) => !!p.toolResponse);
+
+ const toolMessageType = hadToolUse
+ ? ('tool_use' as const)
+ : hadToolResult
+ ? ('tool_result' as const)
+ : undefined;
+
+ const role = this.toAnthropicRole(message.role, toolMessageType);
+
+ const content = msg.content.map((part) =>
+ this.toAnthropicMessageContent(part)
+ );
+
+ anthropicMsgs.push({ role, content });
+ }
+
+ return { system, messages: anthropicMsgs };
+ }
+
+ /**
+ * Converts a Genkit ToolDefinition to an Anthropic Tool object.
+ */
+ protected toAnthropicTool(tool: ToolDefinition): RunnerTool {
+ return {
+ name: tool.name,
+ description: tool.description,
+ input_schema: tool.inputSchema,
+ } as RunnerTool;
+ }
+
+ /**
+ * Converts an Anthropic request to a non-streaming Anthropic API request body.
+ * @param modelName The name of the Anthropic model to use.
+ * @param request The Genkit GenerateRequest to convert.
+ * @param cacheSystemPrompt Whether to cache the system prompt.
+ * @returns The converted Anthropic API non-streaming request body.
+ * @throws An error if an unsupported output format is requested.
+ */
+ protected abstract toAnthropicRequestBody(
+ modelName: string,
+ request: GenerateRequest,
+ cacheSystemPrompt?: boolean
+ ): RunnerRequestBody;
+
+ /**
+ * Converts an Anthropic request to a streaming Anthropic API request body.
+ * @param modelName The name of the Anthropic model to use.
+ * @param request The Genkit GenerateRequest to convert.
+ * @param cacheSystemPrompt Whether to cache the system prompt.
+ * @returns The converted Anthropic API streaming request body.
+ * @throws An error if an unsupported output format is requested.
+ */
+ protected abstract toAnthropicStreamingRequestBody(
+ modelName: string,
+ request: GenerateRequest,
+ cacheSystemPrompt?: boolean
+ ): RunnerStreamingRequestBody;
+
+ protected abstract createMessage(
+ body: RunnerRequestBody,
+ abortSignal: AbortSignal
+ ): Promise>;
+
+ protected abstract streamMessages(
+ body: RunnerStreamingRequestBody,
+ abortSignal: AbortSignal
+ ): RunnerStream;
+
+ protected abstract toGenkitResponse(
+ message: RunnerMessage
+ ): GenerateResponseData;
+
+ protected abstract toGenkitPart(
+ event: RunnerStreamEvent
+ ): Part | undefined;
+
+ public async run(
+ request: GenerateRequest,
+ options: {
+ streamingRequested: boolean;
+ sendChunk: (chunk: GenerateResponseChunkData) => void;
+ abortSignal: AbortSignal;
+ }
+ ): Promise {
+ const { streamingRequested, sendChunk, abortSignal } = options;
+
+ if (streamingRequested) {
+ const body = this.toAnthropicStreamingRequestBody(
+ this.name,
+ request,
+ this.cacheSystemPrompt
+ );
+ const stream = this.streamMessages(body, abortSignal);
+ for await (const event of stream) {
+ const part = this.toGenkitPart(event);
+ if (part) {
+ sendChunk({
+ index: 0,
+ content: [part],
+ });
+ }
+ }
+ const finalMessage = await stream.finalMessage();
+ return this.toGenkitResponse(finalMessage);
+ }
+
+ const body = this.toAnthropicRequestBody(
+ this.name,
+ request,
+ this.cacheSystemPrompt
+ );
+ const response = await this.createMessage(body, abortSignal);
+ return this.toGenkitResponse(response);
+ }
+}
diff --git a/js/plugins/anthropic/src/runner/beta.ts b/js/plugins/anthropic/src/runner/beta.ts
new file mode 100644
index 0000000000..6a71fa71d5
--- /dev/null
+++ b/js/plugins/anthropic/src/runner/beta.ts
@@ -0,0 +1,494 @@
+/**
+ * Copyright 2025 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { BetaMessageStream } from '@anthropic-ai/sdk/lib/BetaMessageStream.js';
+import type {
+ BetaContentBlock,
+ BetaImageBlockParam,
+ BetaMessage,
+ MessageCreateParams as BetaMessageCreateParams,
+ MessageCreateParamsNonStreaming as BetaMessageCreateParamsNonStreaming,
+ MessageCreateParamsStreaming as BetaMessageCreateParamsStreaming,
+ BetaMessageParam,
+ BetaRawMessageStreamEvent,
+ BetaRedactedThinkingBlockParam,
+ BetaRequestDocumentBlock,
+ BetaStopReason,
+ BetaTextBlockParam,
+ BetaThinkingBlockParam,
+ BetaTool,
+ BetaToolResultBlockParam,
+ BetaToolUseBlockParam,
+} from '@anthropic-ai/sdk/resources/beta/messages';
+
+import type {
+ GenerateRequest,
+ GenerateResponseData,
+ ModelResponseData,
+ Part,
+} from 'genkit';
+import { logger } from 'genkit/logging';
+
+import { KNOWN_CLAUDE_MODELS, extractVersion } from '../models.js';
+import { AnthropicConfigSchema, type ClaudeRunnerParams } from '../types.js';
+import { BaseRunner } from './base.js';
+import { RunnerTypes } from './types.js';
+
+/**
+ * Server-managed tool blocks emitted by the beta API that Genkit cannot yet
+ * interpret. We fail fast on these so callers do not accidentally treat them as
+ * locally executable tool invocations.
+ */
+/**
+ * Server tool types that exist in beta but are not yet supported.
+ * Note: server_tool_use and web_search_tool_result ARE supported (same as stable API).
+ */
+const BETA_UNSUPPORTED_SERVER_TOOL_BLOCK_TYPES = new Set([
+ 'web_fetch_tool_result',
+ 'code_execution_tool_result',
+ 'bash_code_execution_tool_result',
+ 'text_editor_code_execution_tool_result',
+ 'mcp_tool_result',
+ 'mcp_tool_use',
+ 'container_upload',
+]);
+
+const unsupportedServerToolError = (blockType: string): string =>
+ `Anthropic beta runner does not yet support server-managed tool block '${blockType}'. Please retry against the stable API or wait for dedicated support.`;
+
+interface BetaRunnerTypes extends RunnerTypes {
+ Message: BetaMessage;
+ Stream: BetaMessageStream;
+ StreamEvent: BetaRawMessageStreamEvent;
+ RequestBody: BetaMessageCreateParamsNonStreaming;
+ StreamingRequestBody: BetaMessageCreateParamsStreaming;
+ Tool: BetaTool;
+ MessageParam: BetaMessageParam;
+ ToolResponseContent: BetaTextBlockParam | BetaImageBlockParam;
+ ContentBlockParam:
+ | BetaTextBlockParam
+ | BetaImageBlockParam
+ | BetaRequestDocumentBlock
+ | BetaToolUseBlockParam
+ | BetaToolResultBlockParam
+ | BetaThinkingBlockParam
+ | BetaRedactedThinkingBlockParam;
+}
+
+/**
+ * Runner for the Anthropic Beta API.
+ */
+export class BetaRunner extends BaseRunner {
+ constructor(params: ClaudeRunnerParams) {
+ 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).
+ */
+ protected toAnthropicMessageContent(
+ part: Part
+ ):
+ | BetaTextBlockParam
+ | BetaImageBlockParam
+ | BetaRequestDocumentBlock
+ | BetaToolUseBlockParam
+ | BetaToolResultBlockParam
+ | BetaThinkingBlockParam
+ | BetaRedactedThinkingBlockParam {
+ 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 `custom.anthropicThinking.signature` value from the original response.'
+ );
+ }
+ return {
+ type: 'thinking',
+ thinking: part.reasoning,
+ signature,
+ };
+ }
+
+ const redactedThinking = this.getRedactedThinkingData(part);
+ if (redactedThinking !== undefined) {
+ return {
+ type: 'redacted_thinking',
+ data: redactedThinking,
+ };
+ }
+
+ // Text
+ if (part.text) {
+ return { type: 'text', text: part.text };
+ }
+
+ // Media
+ if (part.media) {
+ 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,
+ },
+ };
+ }
+
+ // Tool request (client tool use)
+ 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,
+ };
+ }
+
+ // Tool response (client tool result)
+ 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: BetaToolResultBlockParam = {
+ 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
+ )}.`
+ );
+ }
+
+ protected createMessage(
+ body: BetaMessageCreateParamsNonStreaming,
+ abortSignal: AbortSignal
+ ): Promise {
+ return this.client.beta.messages.create(body, { signal: abortSignal });
+ }
+
+ protected streamMessages(
+ body: BetaMessageCreateParamsStreaming,
+ abortSignal: AbortSignal
+ ): BetaMessageStream {
+ return this.client.beta.messages.stream(body, { signal: abortSignal });
+ }
+
+ /**
+ * Build non-streaming request body.
+ */
+ protected toAnthropicRequestBody(
+ modelName: string,
+ request: GenerateRequest,
+ cacheSystemPrompt?: boolean
+ ): BetaMessageCreateParamsNonStreaming {
+ const model = KNOWN_CLAUDE_MODELS[modelName];
+ const { system, messages } = this.toAnthropicMessages(request.messages);
+ const mappedModelName =
+ request.config?.version ?? extractVersion(model, modelName);
+
+ let betaSystem: BetaMessageCreateParamsNonStreaming['system'];
+
+ if (system !== undefined) {
+ betaSystem = cacheSystemPrompt
+ ? [
+ {
+ type: 'text' as const,
+ text: system,
+ cache_control: { type: 'ephemeral' as const },
+ },
+ ]
+ : system;
+ }
+
+ const body: BetaMessageCreateParamsNonStreaming = {
+ model: mappedModelName,
+ max_tokens:
+ request.config?.maxOutputTokens ?? this.DEFAULT_MAX_OUTPUT_TOKENS,
+ messages,
+ };
+
+ if (betaSystem !== undefined) body.system = betaSystem;
+ if (request.config?.stopSequences !== undefined)
+ body.stop_sequences = request.config.stopSequences;
+ if (request.config?.temperature !== undefined)
+ body.temperature = request.config.temperature;
+ if (request.config?.topK !== undefined) body.top_k = request.config.topK;
+ if (request.config?.topP !== undefined) body.top_p = request.config.topP;
+ if (request.config?.tool_choice !== undefined) {
+ body.tool_choice = request.config
+ .tool_choice as BetaMessageCreateParams['tool_choice'];
+ }
+ if (request.config?.metadata !== undefined) {
+ body.metadata = request.config
+ .metadata as BetaMessageCreateParams['metadata'];
+ }
+ if (request.tools) {
+ body.tools = request.tools.map((tool) => this.toAnthropicTool(tool));
+ }
+ const thinkingConfig = this.toAnthropicThinkingConfig(
+ request.config?.thinking
+ );
+ if (thinkingConfig) {
+ body.thinking = thinkingConfig as BetaMessageCreateParams['thinking'];
+ }
+
+ if (request.output?.format && request.output.format !== 'text') {
+ throw new Error(
+ `Only text output format is supported for Claude models currently`
+ );
+ }
+
+ return body;
+ }
+
+ /**
+ * Build streaming request body.
+ */
+ protected toAnthropicStreamingRequestBody(
+ modelName: string,
+ request: GenerateRequest,
+ cacheSystemPrompt?: boolean
+ ): BetaMessageCreateParamsStreaming {
+ const model = KNOWN_CLAUDE_MODELS[modelName];
+ const { system, messages } = this.toAnthropicMessages(request.messages);
+ const mappedModelName =
+ request.config?.version ?? extractVersion(model, modelName);
+
+ const betaSystem =
+ system === undefined
+ ? undefined
+ : cacheSystemPrompt
+ ? [
+ {
+ type: 'text' as const,
+ text: system,
+ cache_control: { type: 'ephemeral' as const },
+ },
+ ]
+ : system;
+
+ const body: BetaMessageCreateParamsStreaming = {
+ model: mappedModelName,
+ max_tokens:
+ request.config?.maxOutputTokens ?? this.DEFAULT_MAX_OUTPUT_TOKENS,
+ messages,
+ stream: true,
+ };
+
+ if (betaSystem !== undefined) body.system = betaSystem;
+ if (request.config?.stopSequences !== undefined)
+ body.stop_sequences = request.config.stopSequences;
+ if (request.config?.temperature !== undefined)
+ body.temperature = request.config.temperature;
+ if (request.config?.topK !== undefined) body.top_k = request.config.topK;
+ if (request.config?.topP !== undefined) body.top_p = request.config.topP;
+ if (request.config?.tool_choice !== undefined) {
+ body.tool_choice = request.config
+ .tool_choice as BetaMessageCreateParams['tool_choice'];
+ }
+ if (request.config?.metadata !== undefined) {
+ body.metadata = request.config
+ .metadata as BetaMessageCreateParams['metadata'];
+ }
+ if (request.tools) {
+ body.tools = request.tools.map((tool) => this.toAnthropicTool(tool));
+ }
+ const thinkingConfig = this.toAnthropicThinkingConfig(
+ request.config?.thinking
+ );
+ if (thinkingConfig) {
+ body.thinking = thinkingConfig as BetaMessageCreateParams['thinking'];
+ }
+
+ if (request.output?.format && request.output.format !== 'text') {
+ throw new Error(
+ `Only text output format is supported for Claude models currently`
+ );
+ }
+
+ return body;
+ }
+
+ protected toGenkitResponse(message: BetaMessage): GenerateResponseData {
+ 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,
+ };
+ }
+
+ protected toGenkitPart(event: BetaRawMessageStreamEvent): Part | undefined {
+ if (event.type === 'content_block_start') {
+ const blockType = (event.content_block as { type?: string }).type;
+ if (
+ blockType &&
+ BETA_UNSUPPORTED_SERVER_TOOL_BLOCK_TYPES.has(blockType)
+ ) {
+ throw new Error(unsupportedServerToolError(blockType));
+ }
+ return this.fromBetaContentBlock(event.content_block);
+ }
+ if (event.type === 'content_block_delta') {
+ if (event.delta.type === 'text_delta') {
+ return { text: event.delta.text };
+ }
+ if (event.delta.type === 'thinking_delta') {
+ return { reasoning: event.delta.thinking };
+ }
+ // server/client tool input_json_delta not supported yet
+ return undefined;
+ }
+ return undefined;
+ }
+
+ private fromBetaContentBlock(contentBlock: BetaContentBlock): Part {
+ switch (contentBlock.type) {
+ case 'tool_use': {
+ return {
+ toolRequest: {
+ ref: contentBlock.id,
+ name: contentBlock.name ?? 'unknown_tool',
+ input: contentBlock.input,
+ },
+ };
+ }
+
+ case 'mcp_tool_use':
+ throw new Error(unsupportedServerToolError(contentBlock.type));
+
+ case 'server_tool_use': {
+ const baseName = contentBlock.name ?? 'unknown_tool';
+ const serverToolName =
+ 'server_name' in contentBlock && contentBlock.server_name
+ ? `${contentBlock.server_name}/${baseName}`
+ : baseName;
+ return {
+ text: `[Anthropic server tool ${serverToolName}] input: ${JSON.stringify(contentBlock.input)}`,
+ custom: {
+ anthropicServerToolUse: {
+ id: contentBlock.id,
+ name: serverToolName,
+ input: contentBlock.input,
+ },
+ },
+ };
+ }
+
+ case 'web_search_tool_result':
+ return this.toWebSearchToolResultPart({
+ type: contentBlock.type,
+ toolUseId: contentBlock.tool_use_id,
+ content: contentBlock.content,
+ });
+
+ case 'text':
+ return { text: contentBlock.text };
+
+ case 'thinking':
+ return this.createThinkingPart(
+ contentBlock.thinking,
+ contentBlock.signature
+ );
+
+ case 'redacted_thinking':
+ return { custom: { redactedThinking: contentBlock.data } };
+
+ default: {
+ if (BETA_UNSUPPORTED_SERVER_TOOL_BLOCK_TYPES.has(contentBlock.type)) {
+ throw new Error(unsupportedServerToolError(contentBlock.type));
+ }
+ const unknownType = (contentBlock as { type: string }).type;
+ logger.warn(
+ `Unexpected Anthropic beta content block type: ${unknownType}. Returning empty text. Content block: ${JSON.stringify(
+ contentBlock
+ )}`
+ );
+ return { text: '' };
+ }
+ }
+ }
+
+ private fromBetaStopReason(
+ reason: BetaStopReason | null
+ ): ModelResponseData['finishReason'] {
+ 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';
+ }
+ }
+}
diff --git a/js/plugins/anthropic/src/runner/index.ts b/js/plugins/anthropic/src/runner/index.ts
new file mode 100644
index 0000000000..ce7e3c6fdd
--- /dev/null
+++ b/js/plugins/anthropic/src/runner/index.ts
@@ -0,0 +1,19 @@
+/**
+ * Copyright 2025 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export { BaseRunner } from './base.js';
+export { BetaRunner } from './beta.js';
+export { Runner } from './stable.js';
diff --git a/js/plugins/anthropic/src/runner/stable.ts b/js/plugins/anthropic/src/runner/stable.ts
new file mode 100644
index 0000000000..0c8f7ffc4f
--- /dev/null
+++ b/js/plugins/anthropic/src/runner/stable.ts
@@ -0,0 +1,514 @@
+/**
+ * Copyright 2025 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { MessageStream } from '@anthropic-ai/sdk/lib/MessageStream.js';
+import type {
+ ContentBlock,
+ DocumentBlockParam,
+ ImageBlockParam,
+ Message,
+ MessageCreateParams,
+ MessageCreateParamsNonStreaming,
+ MessageCreateParamsStreaming,
+ MessageParam,
+ MessageStreamEvent,
+ RedactedThinkingBlockParam,
+ TextBlockParam,
+ ThinkingBlockParam,
+ Tool,
+ ToolResultBlockParam,
+ ToolUseBlockParam,
+} from '@anthropic-ai/sdk/resources/messages';
+import type {
+ GenerateRequest,
+ GenerateResponseData,
+ ModelResponseData,
+ Part,
+} from 'genkit';
+import { logger } from 'genkit/logging';
+
+import { KNOWN_CLAUDE_MODELS, extractVersion } from '../models.js';
+import { AnthropicConfigSchema, type ClaudeRunnerParams } from '../types.js';
+import { BaseRunner } from './base.js';
+import { RunnerTypes as BaseRunnerTypes } from './types.js';
+interface RunnerTypes extends BaseRunnerTypes {
+ Message: Message;
+ Stream: MessageStream;
+ StreamEvent: MessageStreamEvent;
+ RequestBody: MessageCreateParamsNonStreaming;
+ StreamingRequestBody: MessageCreateParamsStreaming;
+ Tool: Tool;
+ MessageParam: MessageParam;
+ ToolResponseContent: TextBlockParam | ImageBlockParam;
+ ContentBlockParam:
+ | TextBlockParam
+ | ImageBlockParam
+ | DocumentBlockParam
+ | ToolUseBlockParam
+ | ToolResultBlockParam
+ | ThinkingBlockParam
+ | RedactedThinkingBlockParam;
+}
+
+export class Runner extends BaseRunner {
+ constructor(params: ClaudeRunnerParams) {
+ super(params);
+ }
+
+ protected toAnthropicMessageContent(
+ part: Part
+ ):
+ | TextBlockParam
+ | ImageBlockParam
+ | DocumentBlockParam
+ | ToolUseBlockParam
+ | ToolResultBlockParam
+ | ThinkingBlockParam
+ | RedactedThinkingBlockParam {
+ 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 `custom.anthropicThinking.signature` value from the original response.'
+ );
+ }
+ return {
+ type: 'thinking',
+ thinking: part.reasoning,
+ signature,
+ };
+ }
+
+ const redactedThinking = this.getRedactedThinkingData(part);
+ if (redactedThinking !== undefined) {
+ return {
+ type: 'redacted_thinking',
+ data: redactedThinking,
+ };
+ }
+
+ if (part.text) {
+ return {
+ type: 'text',
+ text: part.text,
+ citations: null,
+ };
+ }
+
+ if (part.media) {
+ 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
+ )}`
+ );
+ }
+ return {
+ type: 'tool_result',
+ tool_use_id: part.toolResponse.ref,
+ content: [this.toAnthropicToolResponseContent(part)],
+ };
+ }
+
+ throw new Error(
+ `Unsupported genkit part fields encountered for current message role: ${JSON.stringify(
+ part
+ )}.`
+ );
+ }
+
+ protected toAnthropicRequestBody(
+ modelName: string,
+ request: GenerateRequest,
+ cacheSystemPrompt?: boolean
+ ): MessageCreateParamsNonStreaming {
+ const model = KNOWN_CLAUDE_MODELS[modelName];
+ const { system, messages } = this.toAnthropicMessages(request.messages);
+ const mappedModelName =
+ request.config?.version ?? extractVersion(model, modelName);
+
+ const systemValue =
+ system === undefined
+ ? undefined
+ : cacheSystemPrompt
+ ? [
+ {
+ type: 'text' as const,
+ text: system,
+ cache_control: { type: 'ephemeral' as const },
+ },
+ ]
+ : system;
+
+ const body: MessageCreateParamsNonStreaming = {
+ model: mappedModelName,
+ max_tokens:
+ request.config?.maxOutputTokens ?? this.DEFAULT_MAX_OUTPUT_TOKENS,
+ messages,
+ };
+
+ if (systemValue !== undefined) {
+ body.system = systemValue;
+ }
+
+ if (request.tools) {
+ body.tools = request.tools.map((tool) => this.toAnthropicTool(tool));
+ }
+ if (request.config?.topK !== undefined) {
+ body.top_k = request.config.topK;
+ }
+ if (request.config?.topP !== undefined) {
+ body.top_p = request.config.topP;
+ }
+ if (request.config?.temperature !== undefined) {
+ body.temperature = request.config.temperature;
+ }
+ if (request.config?.stopSequences !== undefined) {
+ body.stop_sequences = request.config.stopSequences;
+ }
+ if (request.config?.metadata !== undefined) {
+ body.metadata = request.config.metadata;
+ }
+ if (request.config?.tool_choice !== undefined) {
+ body.tool_choice = request.config.tool_choice;
+ }
+ const thinkingConfig = this.toAnthropicThinkingConfig(
+ request.config?.thinking
+ );
+ if (thinkingConfig) {
+ body.thinking = thinkingConfig as MessageCreateParams['thinking'];
+ }
+
+ if (request.output?.format && request.output.format !== 'text') {
+ throw new Error(
+ `Only text output format is supported for Claude models currently`
+ );
+ }
+ return body;
+ }
+
+ protected toAnthropicStreamingRequestBody(
+ modelName: string,
+ request: GenerateRequest,
+ cacheSystemPrompt?: boolean
+ ): MessageCreateParamsStreaming {
+ const model = KNOWN_CLAUDE_MODELS[modelName];
+ const { system, messages } = this.toAnthropicMessages(request.messages);
+ const mappedModelName =
+ request.config?.version ?? extractVersion(model, modelName);
+
+ const systemValue =
+ system === undefined
+ ? undefined
+ : cacheSystemPrompt
+ ? [
+ {
+ type: 'text' as const,
+ text: system,
+ cache_control: { type: 'ephemeral' as const },
+ },
+ ]
+ : system;
+
+ const body: MessageCreateParamsStreaming = {
+ model: mappedModelName,
+ max_tokens:
+ request.config?.maxOutputTokens ?? this.DEFAULT_MAX_OUTPUT_TOKENS,
+ messages,
+ stream: true,
+ };
+
+ if (systemValue !== undefined) {
+ body.system = systemValue;
+ }
+
+ if (request.tools) {
+ body.tools = request.tools.map((tool) => this.toAnthropicTool(tool));
+ }
+ if (request.config?.topK !== undefined) {
+ body.top_k = request.config.topK;
+ }
+ if (request.config?.topP !== undefined) {
+ body.top_p = request.config.topP;
+ }
+ if (request.config?.temperature !== undefined) {
+ body.temperature = request.config.temperature;
+ }
+ if (request.config?.stopSequences !== undefined) {
+ body.stop_sequences = request.config.stopSequences;
+ }
+ if (request.config?.metadata !== undefined) {
+ body.metadata = request.config.metadata;
+ }
+ if (request.config?.tool_choice !== undefined) {
+ body.tool_choice = request.config.tool_choice;
+ }
+ const thinkingConfig = this.toAnthropicThinkingConfig(
+ request.config?.thinking
+ );
+ if (thinkingConfig) {
+ body.thinking =
+ thinkingConfig as MessageCreateParamsStreaming['thinking'];
+ }
+
+ if (request.output?.format && request.output.format !== 'text') {
+ throw new Error(
+ `Only text output format is supported for Claude models currently`
+ );
+ }
+ return body;
+ }
+
+ protected async createMessage(
+ body: MessageCreateParamsNonStreaming,
+ abortSignal: AbortSignal
+ ): Promise {
+ return await this.client.messages.create(body, { signal: abortSignal });
+ }
+
+ protected streamMessages(
+ body: MessageCreateParamsStreaming,
+ abortSignal: AbortSignal
+ ): MessageStream {
+ return this.client.messages.stream(body, { signal: abortSignal });
+ }
+
+ protected toGenkitResponse(message: Message): GenerateResponseData {
+ return this.fromAnthropicResponse(message);
+ }
+
+ protected toGenkitPart(event: MessageStreamEvent): Part | undefined {
+ return this.fromAnthropicContentBlockChunk(event);
+ }
+
+ protected fromAnthropicContentBlockChunk(
+ event: MessageStreamEvent
+ ): Part | undefined {
+ // Handle content_block_delta events
+ if (event.type === 'content_block_delta') {
+ const delta = event.delta;
+
+ if (delta.type === 'input_json_delta') {
+ throw new Error(
+ 'Anthropic streaming tool input (input_json_delta) is not yet supported. Please disable streaming or upgrade this plugin.'
+ );
+ }
+
+ if (delta.type === 'text_delta') {
+ return { text: delta.text };
+ }
+
+ if (delta.type === 'thinking_delta') {
+ return { reasoning: delta.thinking };
+ }
+
+ // signature_delta - ignore
+ return undefined;
+ }
+
+ // Handle content_block_start events
+ if (event.type === 'content_block_start') {
+ const block = event.content_block;
+
+ switch (block.type) {
+ case 'server_tool_use':
+ return {
+ text: `[Anthropic server tool ${block.name}] input: ${JSON.stringify(block.input)}`,
+ custom: {
+ anthropicServerToolUse: {
+ id: block.id,
+ name: block.name,
+ input: block.input,
+ },
+ },
+ };
+
+ case 'web_search_tool_result':
+ return this.toWebSearchToolResultPart({
+ type: block.type,
+ toolUseId: block.tool_use_id,
+ content: block.content,
+ });
+
+ case 'text':
+ return { text: block.text };
+
+ case 'thinking':
+ return this.createThinkingPart(block.thinking, block.signature);
+
+ case 'redacted_thinking':
+ return { custom: { redactedThinking: block.data } };
+
+ case 'tool_use':
+ return {
+ toolRequest: {
+ ref: block.id,
+ name: block.name,
+ input: block.input,
+ },
+ };
+
+ default: {
+ const unknownType = (block as { type: string }).type;
+ logger.warn(
+ `Unexpected Anthropic content block type in stream: ${unknownType}. Returning undefined. Content block: ${JSON.stringify(block)}`
+ );
+ return undefined;
+ }
+ }
+ }
+
+ // Other event types (message_start, message_delta, etc.) - ignore
+ return undefined;
+ }
+
+ protected fromAnthropicContentBlock(contentBlock: ContentBlock): Part {
+ switch (contentBlock.type) {
+ case 'server_tool_use':
+ return {
+ text: `[Anthropic server tool ${contentBlock.name}] input: ${JSON.stringify(contentBlock.input)}`,
+ custom: {
+ anthropicServerToolUse: {
+ id: contentBlock.id,
+ name: contentBlock.name,
+ input: contentBlock.input,
+ },
+ },
+ };
+
+ case 'web_search_tool_result':
+ return this.toWebSearchToolResultPart({
+ type: contentBlock.type,
+ toolUseId: contentBlock.tool_use_id,
+ content: contentBlock.content,
+ });
+
+ case 'tool_use':
+ return {
+ toolRequest: {
+ ref: contentBlock.id,
+ name: contentBlock.name,
+ input: contentBlock.input,
+ },
+ };
+
+ case 'text':
+ return { text: contentBlock.text };
+
+ case 'thinking':
+ return this.createThinkingPart(
+ contentBlock.thinking,
+ contentBlock.signature
+ );
+
+ case 'redacted_thinking':
+ return { custom: { redactedThinking: contentBlock.data } };
+
+ default: {
+ const unknownType = (contentBlock as { type: string }).type;
+ logger.warn(
+ `Unexpected Anthropic content block type: ${unknownType}. Returning empty text. Content block: ${JSON.stringify(contentBlock)}`
+ );
+ return { text: '' };
+ }
+ }
+ }
+
+ protected fromAnthropicStopReason(
+ reason: Message['stop_reason']
+ ): ModelResponseData['finishReason'] {
+ 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';
+ }
+ }
+
+ protected fromAnthropicResponse(response: Message): GenerateResponseData {
+ return {
+ candidates: [
+ {
+ index: 0,
+ finishReason: this.fromAnthropicStopReason(response.stop_reason),
+ message: {
+ role: 'model',
+ content: response.content.map((block) =>
+ this.fromAnthropicContentBlock(block)
+ ),
+ },
+ },
+ ],
+ usage: {
+ inputTokens: response.usage.input_tokens,
+ outputTokens: response.usage.output_tokens,
+ },
+ custom: response,
+ };
+ }
+}
diff --git a/js/plugins/anthropic/src/runner/types.ts b/js/plugins/anthropic/src/runner/types.ts
new file mode 100644
index 0000000000..5fd04c6911
--- /dev/null
+++ b/js/plugins/anthropic/src/runner/types.ts
@@ -0,0 +1,78 @@
+/**
+ * Copyright 2025 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Type contract that each Anthropic runner passes into the generic `BaseRunner`.
+ *
+ * The concrete runners (stable vs. beta SDKs) bind these slots to their SDK’s
+ * concrete interfaces so the shared logic in `BaseRunner` can stay strongly typed
+ * without knowing which SDK variant it is talking to.
+ *
+ * Properties are `unknown` by default, so every subclass must plug in the
+ * correct Anthropic types to keep the generic plumbing sound.
+ */
+type RunnerTypes = {
+ Message: unknown;
+ Stream: AsyncIterable & { finalMessage(): Promise };
+ StreamEvent: unknown;
+ RequestBody: unknown;
+ StreamingRequestBody: unknown;
+ Tool: unknown;
+ MessageParam: unknown;
+ ContentBlockParam: unknown;
+ ToolResponseContent: unknown;
+};
+
+type RunnerMessage = ApiTypes['Message'];
+
+/** Streaming handle that yields Anthropic events and exposes the final message. */
+type RunnerStream = ApiTypes['Stream'];
+
+/** Discrete event emitted by the Anthropic stream (delta, block start, etc.). */
+type RunnerStreamEvent = ApiTypes['StreamEvent'];
+
+/** Non-streaming request payload shape for create-message calls. */
+type RunnerRequestBody = ApiTypes['RequestBody'];
+type RunnerStreamingRequestBody =
+ ApiTypes['StreamingRequestBody'];
+
+/** Tool definition compatible with the target Anthropic SDK. */
+type RunnerTool = ApiTypes['Tool'];
+
+/** Anthropic message param shape used when sending history to the API. */
+type RunnerMessageParam =
+ ApiTypes['MessageParam'];
+
+/** Content block that the runner sends to Anthropic for a single part. */
+type RunnerContentBlockParam =
+ ApiTypes['ContentBlockParam'];
+
+/** Tool response block that Anthropic expects when returning tool output. */
+type RunnerToolResponseContent =
+ ApiTypes['ToolResponseContent'];
+
+export {
+ RunnerContentBlockParam,
+ RunnerMessage,
+ RunnerMessageParam,
+ RunnerRequestBody,
+ RunnerStream,
+ RunnerStreamEvent,
+ RunnerStreamingRequestBody,
+ RunnerTool,
+ RunnerToolResponseContent,
+ RunnerTypes,
+};
diff --git a/js/plugins/anthropic/src/types.ts b/js/plugins/anthropic/src/types.ts
new file mode 100644
index 0000000000..9796d71c36
--- /dev/null
+++ b/js/plugins/anthropic/src/types.ts
@@ -0,0 +1,166 @@
+/**
+ * Copyright 2024 Bloom Labs Inc
+ * Copyright 2025 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type Anthropic from '@anthropic-ai/sdk';
+import { z } from 'genkit';
+import { GenerationCommonConfigSchema } from 'genkit/model';
+
+/**
+ * Internal symbol for dependency injection in tests.
+ * Not part of the public API.
+ * @internal
+ */
+export const __testClient = Symbol('testClient');
+
+/**
+ * Plugin configuration options for the Anthropic plugin.
+ */
+export interface PluginOptions {
+ apiKey?: string;
+ cacheSystemPrompt?: boolean;
+ /** Default API surface for all requests unless overridden per-request. */
+ apiVersion?: 'stable' | 'beta';
+}
+
+/**
+ * Internal plugin options that include test client injection.
+ * @internal
+ */
+export interface InternalPluginOptions extends PluginOptions {
+ [__testClient]?: Anthropic;
+}
+
+/**
+ * Shared parameters required to construct Claude helpers.
+ */
+interface ClaudeHelperParamsBase {
+ name: string;
+ client: Anthropic;
+ cacheSystemPrompt?: boolean;
+ defaultApiVersion?: 'stable' | 'beta';
+}
+
+/**
+ * Parameters for creating a Claude model action.
+ */
+export interface ClaudeModelParams extends ClaudeHelperParamsBase {}
+
+/**
+ * Parameters for creating a Claude runner.
+ */
+export interface ClaudeRunnerParams extends ClaudeHelperParamsBase {}
+
+export const AnthropicBaseConfigSchema = 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(),
+ /** Optional shorthand to pick API surface for this request. */
+ apiVersion: z.enum(['stable', 'beta']).optional(),
+});
+
+export type AnthropicBaseConfigSchemaType = typeof AnthropicBaseConfigSchema;
+
+export const ThinkingConfigSchema = z
+ .object({
+ enabled: z.boolean().optional(),
+ budgetTokens: z.number().int().min(1_024).optional(),
+ })
+ .superRefine((value, ctx) => {
+ if (value.enabled && value.budgetTokens === undefined) {
+ ctx.addIssue({
+ code: z.ZodIssueCode.custom,
+ path: ['budgetTokens'],
+ message: 'budgetTokens is required when thinking is enabled',
+ });
+ }
+ });
+
+export const AnthropicThinkingConfigSchema = AnthropicBaseConfigSchema.extend({
+ thinking: ThinkingConfigSchema.optional(),
+});
+
+export const AnthropicConfigSchema = AnthropicThinkingConfigSchema;
+
+export type ThinkingConfig = z.infer;
+export type AnthropicBaseConfig = z.infer;
+export type AnthropicThinkingConfig = z.infer<
+ typeof AnthropicThinkingConfigSchema
+>;
+export type ClaudeConfig = AnthropicThinkingConfig | AnthropicBaseConfig;
+
+/**
+ * Media object representation with URL and optional content type.
+ */
+export interface Media {
+ url: string;
+ contentType?: string;
+}
+
+export const MediaSchema = z.object({
+ url: z.string(),
+ contentType: z.string().optional(),
+});
+
+export const MediaTypeSchema = z.enum([
+ 'image/jpeg',
+ 'image/png',
+ 'image/gif',
+ 'image/webp',
+]);
+
+export type MediaType = z.infer;
+
+export const MEDIA_TYPES = {
+ JPEG: 'image/jpeg',
+ PNG: 'image/png',
+ GIF: 'image/gif',
+ WEBP: 'image/webp',
+} as const satisfies Record;
+
+/**
+ * Resolve whether beta API should be used for this call.
+ * Priority:
+ * 1. request.config.apiVersion (per-request override - explicit stable or beta)
+ * 2. pluginDefaultApiVersion (plugin-wide default)
+ * 3. otherwise stable
+ */
+export function resolveBetaEnabled(
+ cfg: AnthropicThinkingConfig | AnthropicBaseConfig | undefined,
+ pluginDefaultApiVersion?: 'stable' | 'beta'
+): boolean {
+ if (cfg?.apiVersion !== undefined) {
+ return cfg.apiVersion === 'beta';
+ }
+ if (pluginDefaultApiVersion === 'beta') return true;
+ return false;
+}
diff --git a/js/plugins/anthropic/tsconfig.json b/js/plugins/anthropic/tsconfig.json
new file mode 100644
index 0000000000..596e2cf729
--- /dev/null
+++ b/js/plugins/anthropic/tsconfig.json
@@ -0,0 +1,4 @@
+{
+ "extends": "../../tsconfig.json",
+ "include": ["src"]
+}
diff --git a/js/plugins/anthropic/tsup.config.ts b/js/plugins/anthropic/tsup.config.ts
new file mode 100644
index 0000000000..d55507161f
--- /dev/null
+++ b/js/plugins/anthropic/tsup.config.ts
@@ -0,0 +1,22 @@
+/**
+ * Copyright 2025 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { defineConfig, type Options } from 'tsup';
+import { defaultOptions } from '../../tsup.common';
+
+export default defineConfig({
+ ...(defaultOptions as Options),
+});
diff --git a/js/pnpm-lock.yaml b/js/pnpm-lock.yaml
index 00f3027cfd..328d92253e 100644
--- a/js/pnpm-lock.yaml
+++ b/js/pnpm-lock.yaml
@@ -181,7 +181,7 @@ importers:
optionalDependencies:
'@genkit-ai/firebase':
specifier: ^1.16.1
- version: 1.16.1(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@1.22.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1))
+ version: 1.16.1(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@1.23.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1))
doc-snippets:
dependencies:
@@ -257,6 +257,37 @@ importers:
specifier: ^4.9.0
version: 4.9.5
+ plugins/anthropic:
+ dependencies:
+ '@anthropic-ai/sdk':
+ specifier: ^0.68.0
+ version: 0.68.0(zod@3.25.67)
+ devDependencies:
+ '@types/node':
+ specifier: ^20.11.16
+ version: 20.19.1
+ check-node-version:
+ specifier: ^4.2.1
+ version: 4.2.1
+ genkit:
+ specifier: workspace:*
+ version: link:../../genkit
+ npm-run-all:
+ specifier: ^4.1.5
+ version: 4.1.5
+ rimraf:
+ specifier: ^6.0.1
+ version: 6.0.1
+ tsup:
+ specifier: ^8.3.5
+ version: 8.5.0(postcss@8.4.47)(tsx@4.20.3)(typescript@4.9.5)(yaml@2.8.0)
+ tsx:
+ specifier: ^4.19.2
+ version: 4.20.3
+ typescript:
+ specifier: ^4.9.0
+ version: 4.9.5
+
plugins/checks:
dependencies:
'@genkit-ai/ai':
@@ -988,6 +1019,25 @@ importers:
specifier: '>=12.2'
version: 13.4.0(encoding@0.1.13)
+ testapps/anthropic:
+ dependencies:
+ '@genkit-ai/anthropic':
+ specifier: workspace:*
+ version: link:../../plugins/anthropic
+ genkit:
+ specifier: workspace:*
+ version: link:../../genkit
+ devDependencies:
+ cross-env:
+ specifier: ^10.1.0
+ version: 10.1.0
+ tsx:
+ specifier: ^4.19.2
+ version: 4.20.3
+ typescript:
+ specifier: ^5.6.2
+ version: 5.8.3
+
testapps/basic-gemini:
dependencies:
'@genkit-ai/firebase':
@@ -1001,7 +1051,7 @@ importers:
version: link:../../plugins/google-genai
'@google/genai':
specifier: ^1.29.0
- version: 1.29.0
+ version: 1.29.1
express:
specifier: ^4.20.0
version: 4.21.2
@@ -1029,7 +1079,7 @@ importers:
version: link:../../plugins/compat-oai
'@genkit-ai/express':
specifier: ^1.1.0
- version: 1.12.0(@genkit-ai/core@1.22.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@genkit))(express@5.1.0)(genkit@genkit)
+ version: 1.12.0(@genkit-ai/core@1.23.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@genkit))(express@5.1.0)(genkit@genkit)
genkit:
specifier: workspace:*
version: link:../../genkit
@@ -1596,7 +1646,7 @@ importers:
version: link:../../plugins/ollama
genkitx-openai:
specifier: ^0.10.1
- version: 0.10.1(@genkit-ai/ai@1.22.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@genkit))(@genkit-ai/core@1.22.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@genkit))(encoding@0.1.13)(ws@8.18.3)
+ version: 0.10.1(@genkit-ai/ai@1.23.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@genkit))(@genkit-ai/core@1.23.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@genkit))(encoding@0.1.13)(ws@8.18.3)
devDependencies:
rimraf:
specifier: ^6.0.1
@@ -2036,6 +2086,15 @@ packages:
'@anthropic-ai/sdk@0.24.3':
resolution: {integrity: sha512-916wJXO6T6k8R6BAAcLhLPv/pnLGy7YSEBZXZ1XTFbLcTZE8oTy3oDW9WJf9KKZwMvVcePIfoTSvzXHRcGxkQQ==}
+ '@anthropic-ai/sdk@0.68.0':
+ resolution: {integrity: sha512-SMYAmbbiprG8k1EjEPMTwaTqssDT7Ae+jxcR5kWXiqTlbwMR2AthXtscEVWOHkRfyAV5+y3PFYTJRNa3OJWIEw==}
+ hasBin: true
+ peerDependencies:
+ zod: ^3.25.0 || ^4.0.0
+ peerDependenciesMeta:
+ zod:
+ optional: true
+
'@anthropic-ai/sdk@0.9.1':
resolution: {integrity: sha512-wa1meQ2WSfoY8Uor3EdrJq0jTiZJoKoSii2ZVWRY1oN4Tlr5s59pADg9T79FTbPe1/se5c3pBeZgJL63wmuoBA==}
@@ -2196,6 +2255,10 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0-0
+ '@babel/runtime@7.28.4':
+ resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==}
+ engines: {node: '>=6.9.0'}
+
'@babel/template@7.25.7':
resolution: {integrity: sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==}
engines: {node: '>=6.9.0'}
@@ -2625,11 +2688,11 @@ packages:
'@firebase/webchannel-wrapper@1.0.3':
resolution: {integrity: sha512-2xCRM9q9FlzGZCdgDMJwc0gyUkWFtkosy7Xxr6sFgQwn+wMNIWd7xIvYNauU1r64B5L5rsGKy/n9TKJ0aAFeqQ==}
- '@genkit-ai/ai@1.22.0':
- resolution: {integrity: sha512-TDKO+zWyM5YI8zE4a0IlqlpgHuLB4B4islzgWDvzdQlbjtyJp0ayODAMFhS2ruQ6+a/UdXDySRrOX/RcqF4yjA==}
+ '@genkit-ai/ai@1.23.0':
+ resolution: {integrity: sha512-9CuZJYZnnmAtqVcOEDU/bSCJYPELu2oveEoOSECVA6St3bmebID9mL0F51AKGi6YyB1Xz+GLny35PTbqxrhAWw==}
- '@genkit-ai/core@1.22.0':
- resolution: {integrity: sha512-etVlpwJkPoy91xR6H5+S/AWZPJMeovb7N35+B90md1+6xWcodQF7WZ3chKcH31Xamlz+jTIvd3riiZGY9RFumg==}
+ '@genkit-ai/core@1.23.0':
+ resolution: {integrity: sha512-JG5QPwM49HZmH69ky2DZUevJLEkeIkAo1EdIbYC0E32cH4VZp0Mn1ooA73oTLZm0/D/LrJ2zc5487Eu3gIhs9Q==}
'@genkit-ai/express@1.12.0':
resolution: {integrity: sha512-QAxSS07dX5ovSfsUB4s90KaDnv4zg1wnoxCZCa+jBsYUyv9NvCCTsOk25xAQgGxc7xi3+MD+3AsPier5oZILIg==}
@@ -2743,8 +2806,8 @@ packages:
resolution: {integrity: sha512-HqYqoivNtkq59po8m7KI0n+lWKdz4kabENncYQXZCX/hBWJfXtKAfR/2nUQsP+TwSfHKoA7zDL2RrJYIv/j3VQ==}
engines: {node: '>=18.0.0'}
- '@google/genai@1.29.0':
- resolution: {integrity: sha512-cQP7Ssa06W+MSAyVtL/812FBtZDoDehnFObIpK1xo5Uv4XvqBcVZ8OhXgihOIXWn7xvPQGvLclR8+yt3Ysnd9g==}
+ '@google/genai@1.29.1':
+ resolution: {integrity: sha512-Buywpq0A6xf9cOdhiWCi5KUiDBbZkjCH5xbl+xxNQRItoYQgd31p0OKyn5cUnT0YNzC/pAmszqXoOc7kncqfFQ==}
engines: {node: '>=20.0.0'}
peerDependencies:
'@modelcontextprotocol/sdk': ^1.20.1
@@ -4579,6 +4642,10 @@ packages:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
engines: {node: '>=4'}
+ chalk@3.0.0:
+ resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==}
+ engines: {node: '>=8'}
+
chalk@4.1.2:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
@@ -4590,6 +4657,11 @@ packages:
charenc@0.0.2:
resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==}
+ check-node-version@4.2.1:
+ resolution: {integrity: sha512-YYmFYHV/X7kSJhuN/QYHUu998n/TRuDe8UenM3+m5NrkiH670lb9ILqHIvBencvJc4SDh+XcbXMR4b+TtubJiw==}
+ engines: {node: '>=8.3.0'}
+ hasBin: true
+
chokidar@4.0.3:
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
engines: {node: '>= 14.16.0'}
@@ -5275,8 +5347,8 @@ packages:
resolution: {integrity: sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==}
engines: {node: '>=18'}
- genkit@1.22.0:
- resolution: {integrity: sha512-GoVVO3EnNHrjkMkUPRvgx1MjBHKvOlZAu/ffMIJgLFxrH7rrUbvfHXE6Nk7uh5BNvET7+DApyhbhqz9G8sy+mQ==}
+ genkit@1.23.0:
+ resolution: {integrity: sha512-8BOpwt40Yqadc6shvfcKk4fTzrtlIV8ntv9Mqx3Yul9BbM4kRXW+h6nYMaPMKT4ZdErzZgnfxU7e6t1lXFFP4A==}
genkitx-openai@0.10.1:
resolution: {integrity: sha512-E9/DzyQcBUSTy81xT2pvEmdnn9Q/cKoojEt6lD/EdOeinhqE9oa59d/kuXTokCMekTrj3Rk7LtNBQIDjnyjNOA==}
@@ -5369,8 +5441,8 @@ packages:
resolution: {integrity: sha512-V6eky/xz2mcKfAd1Ioxyd6nmA61gao3n01C+YeuIwu3vzM9EDR6wcVzMSIbLMDXWeoi9SHYctXuKYC5uJUT3eQ==}
engines: {node: '>=14'}
- google-logging-utils@1.1.2:
- resolution: {integrity: sha512-YsFPGVgDFf4IzSwbwIR0iaFJQFmR5Jp7V1WuYSjuRgAm9yWqsMhKE9YPlL+wvFLnc/wMiFV4SQUD9Y/JMpxIxQ==}
+ google-logging-utils@1.1.3:
+ resolution: {integrity: sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==}
engines: {node: '>=14'}
google-p12-pem@4.0.1:
@@ -5899,6 +5971,10 @@ packages:
json-parse-even-better-errors@2.3.1:
resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
+ json-schema-to-ts@3.1.1:
+ resolution: {integrity: sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==}
+ engines: {node: '>=16'}
+
json-schema-traverse@0.4.1:
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
@@ -6295,6 +6371,9 @@ packages:
makeerror@1.0.12:
resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==}
+ map-values@1.0.1:
+ resolution: {integrity: sha512-BbShUnr5OartXJe1GeccAWtfro11hhgNJg6G9/UtWKjVGvV5U4C09cg5nk8JUevhXODaXY+hQ3xxMUKSs62ONQ==}
+
markdown-it@14.1.0:
resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==}
hasBin: true
@@ -6528,6 +6607,9 @@ packages:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
+ object-filter@1.0.2:
+ resolution: {integrity: sha512-NahvP2vZcy1ZiiYah30CEPw0FpDcSkSePJBMpzl5EQgCmISijiGuJm3SPYp7U+Lf2TljyaIw3E5EgkEx/TNEVA==}
+
object-hash@3.0.0:
resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
engines: {node: '>= 6'}
@@ -6893,6 +6975,9 @@ packages:
resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==}
engines: {node: '>=0.6'}
+ queue-microtask@1.2.3:
+ resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+
range-parser@1.2.1:
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
engines: {node: '>= 0.6'}
@@ -7006,6 +7091,9 @@ packages:
resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==}
engines: {node: '>= 18'}
+ run-parallel@1.2.0:
+ resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+
safe-array-concat@1.1.3:
resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==}
engines: {node: '>=0.4'}
@@ -7378,6 +7466,9 @@ packages:
resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==}
engines: {node: '>= 14.0.0'}
+ ts-algebra@2.0.0:
+ resolution: {integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==}
+
ts-interface-checker@0.1.13:
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
@@ -7815,6 +7906,12 @@ snapshots:
transitivePeerDependencies:
- encoding
+ '@anthropic-ai/sdk@0.68.0(zod@3.25.67)':
+ dependencies:
+ json-schema-to-ts: 3.1.1
+ optionalDependencies:
+ zod: 3.25.67
+
'@anthropic-ai/sdk@0.9.1(encoding@0.1.13)':
dependencies:
'@types/node': 18.19.112
@@ -8012,6 +8109,8 @@ snapshots:
'@babel/core': 7.25.7
'@babel/helper-plugin-utils': 7.25.7
+ '@babel/runtime@7.28.4': {}
+
'@babel/template@7.25.7':
dependencies:
'@babel/code-frame': 7.25.7
@@ -8508,9 +8607,9 @@ snapshots:
'@firebase/webchannel-wrapper@1.0.3': {}
- '@genkit-ai/ai@1.22.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@1.22.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1))':
+ '@genkit-ai/ai@1.23.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@1.23.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1))':
dependencies:
- '@genkit-ai/core': 1.22.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@1.22.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1))
+ '@genkit-ai/core': 1.23.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@1.23.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1))
'@opentelemetry/api': 1.9.0
'@types/node': 20.19.1
colorette: 2.0.20
@@ -8529,9 +8628,9 @@ snapshots:
- supports-color
optional: true
- '@genkit-ai/ai@1.22.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@genkit)':
+ '@genkit-ai/ai@1.23.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@genkit)':
dependencies:
- '@genkit-ai/core': 1.22.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@genkit)
+ '@genkit-ai/core': 1.23.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@genkit)
'@opentelemetry/api': 1.9.0
'@types/node': 20.19.1
colorette: 2.0.20
@@ -8549,7 +8648,7 @@ snapshots:
- genkit
- supports-color
- '@genkit-ai/core@1.22.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@1.22.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1))':
+ '@genkit-ai/core@1.23.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@1.23.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1))':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/context-async-hooks': 1.25.1(@opentelemetry/api@1.9.0)
@@ -8571,7 +8670,7 @@ snapshots:
zod: 3.25.67
zod-to-json-schema: 3.24.5(zod@3.25.67)
optionalDependencies:
- '@genkit-ai/firebase': 1.16.1(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@1.22.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1))
+ '@genkit-ai/firebase': 1.16.1(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@1.23.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1))
transitivePeerDependencies:
- '@google-cloud/firestore'
- encoding
@@ -8581,7 +8680,7 @@ snapshots:
- supports-color
optional: true
- '@genkit-ai/core@1.22.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@genkit)':
+ '@genkit-ai/core@1.23.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@genkit)':
dependencies:
'@opentelemetry/api': 1.9.0
'@opentelemetry/context-async-hooks': 1.25.1(@opentelemetry/api@1.9.0)
@@ -8612,9 +8711,9 @@ snapshots:
- genkit
- supports-color
- '@genkit-ai/express@1.12.0(@genkit-ai/core@1.22.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@genkit))(express@5.1.0)(genkit@genkit)':
+ '@genkit-ai/express@1.12.0(@genkit-ai/core@1.23.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@genkit))(express@5.1.0)(genkit@genkit)':
dependencies:
- '@genkit-ai/core': 1.22.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@genkit)
+ '@genkit-ai/core': 1.23.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@genkit)
body-parser: 1.20.3
cors: 2.8.5
express: 5.1.0
@@ -8622,12 +8721,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@genkit-ai/firebase@1.16.1(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@1.22.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1))':
+ '@genkit-ai/firebase@1.16.1(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@1.23.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1))':
dependencies:
- '@genkit-ai/google-cloud': 1.16.1(encoding@0.1.13)(genkit@1.22.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1))
+ '@genkit-ai/google-cloud': 1.16.1(encoding@0.1.13)(genkit@1.23.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1))
'@google-cloud/firestore': 7.11.1(encoding@0.1.13)
firebase-admin: 13.4.0(encoding@0.1.13)
- genkit: 1.22.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)
+ genkit: 1.23.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)
optionalDependencies:
firebase: 11.9.1
transitivePeerDependencies:
@@ -8648,7 +8747,7 @@ snapshots:
- supports-color
optional: true
- '@genkit-ai/google-cloud@1.16.1(encoding@0.1.13)(genkit@1.22.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1))':
+ '@genkit-ai/google-cloud@1.16.1(encoding@0.1.13)(genkit@1.23.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1))':
dependencies:
'@google-cloud/logging-winston': 6.0.1(encoding@0.1.13)(winston@3.17.0)
'@google-cloud/opentelemetry-cloud-monitoring-exporter': 0.19.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/resources@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-metrics@1.25.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)
@@ -8664,7 +8763,7 @@ snapshots:
'@opentelemetry/sdk-metrics': 1.25.1(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-node': 0.52.1(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.9.0)
- genkit: 1.22.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)
+ genkit: 1.23.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)
google-auth-library: 9.15.1(encoding@0.1.13)
node-fetch: 3.3.2
winston: 3.17.0
@@ -8891,7 +8990,7 @@ snapshots:
- encoding
- supports-color
- '@google/genai@1.29.0':
+ '@google/genai@1.29.1':
dependencies:
google-auth-library: 10.5.0
ws: 8.18.3
@@ -10821,6 +10920,11 @@ snapshots:
escape-string-regexp: 1.0.5
supports-color: 5.5.0
+ chalk@3.0.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ supports-color: 7.2.0
+
chalk@4.1.2:
dependencies:
ansi-styles: 4.3.0
@@ -10830,6 +10934,15 @@ snapshots:
charenc@0.0.2: {}
+ check-node-version@4.2.1:
+ dependencies:
+ chalk: 3.0.0
+ map-values: 1.0.1
+ minimist: 1.2.8
+ object-filter: 1.0.2
+ run-parallel: 1.2.0
+ semver: 6.3.1
+
chokidar@4.0.3:
dependencies:
readdirp: 4.1.2
@@ -11708,15 +11821,15 @@ snapshots:
gcp-metadata@8.1.2:
dependencies:
gaxios: 7.1.3
- google-logging-utils: 1.1.2
+ google-logging-utils: 1.1.3
json-bigint: 1.0.0
transitivePeerDependencies:
- supports-color
- genkit@1.22.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1):
+ genkit@1.23.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1):
dependencies:
- '@genkit-ai/ai': 1.22.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@1.22.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1))
- '@genkit-ai/core': 1.22.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@1.22.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1))
+ '@genkit-ai/ai': 1.23.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@1.23.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1))
+ '@genkit-ai/core': 1.23.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@1.23.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1))
uuid: 10.0.0
transitivePeerDependencies:
- '@google-cloud/firestore'
@@ -11726,10 +11839,10 @@ snapshots:
- supports-color
optional: true
- genkitx-openai@0.10.1(@genkit-ai/ai@1.22.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@genkit))(@genkit-ai/core@1.22.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@genkit))(encoding@0.1.13)(ws@8.18.3):
+ genkitx-openai@0.10.1(@genkit-ai/ai@1.23.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@genkit))(@genkit-ai/core@1.23.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@genkit))(encoding@0.1.13)(ws@8.18.3):
dependencies:
- '@genkit-ai/ai': 1.22.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@genkit)
- '@genkit-ai/core': 1.22.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@genkit)
+ '@genkit-ai/ai': 1.23.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@genkit)
+ '@genkit-ai/core': 1.23.0(@google-cloud/firestore@7.11.1(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@13.4.0(encoding@0.1.13))(firebase@11.9.1)(genkit@genkit)
openai: 4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.67)
zod: 3.25.67
transitivePeerDependencies:
@@ -11829,7 +11942,7 @@ snapshots:
ecdsa-sig-formatter: 1.0.11
gaxios: 7.1.3
gcp-metadata: 8.1.2
- google-logging-utils: 1.1.2
+ google-logging-utils: 1.1.3
gtoken: 8.0.0
jws: 4.0.0
transitivePeerDependencies:
@@ -11881,7 +11994,7 @@ snapshots:
- encoding
- supports-color
- google-logging-utils@1.1.2: {}
+ google-logging-utils@1.1.3: {}
google-p12-pem@4.0.1:
dependencies:
@@ -12698,6 +12811,11 @@ snapshots:
json-parse-even-better-errors@2.3.1: {}
+ json-schema-to-ts@3.1.1:
+ dependencies:
+ '@babel/runtime': 7.28.4
+ ts-algebra: 2.0.0
+
json-schema-traverse@0.4.1: {}
json-schema-traverse@1.0.0: {}
@@ -13034,6 +13152,8 @@ snapshots:
dependencies:
tmpl: 1.0.5
+ map-values@1.0.1: {}
+
markdown-it@14.1.0:
dependencies:
argparse: 2.0.1
@@ -13245,6 +13365,8 @@ snapshots:
object-assign@4.1.1: {}
+ object-filter@1.0.2: {}
+
object-hash@3.0.0: {}
object-inspect@1.13.1: {}
@@ -13606,6 +13728,8 @@ snapshots:
dependencies:
side-channel: 1.1.0
+ queue-microtask@1.2.3: {}
+
range-parser@1.2.1: {}
raw-body@2.5.2:
@@ -13769,6 +13893,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ run-parallel@1.2.0:
+ dependencies:
+ queue-microtask: 1.2.3
+
safe-array-concat@1.1.3:
dependencies:
call-bind: 1.0.8
@@ -14213,6 +14341,8 @@ snapshots:
triple-beam@1.4.1: {}
+ ts-algebra@2.0.0: {}
+
ts-interface-checker@0.1.13: {}
ts-jest@29.4.0(@babel/core@7.25.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.7))(jest-util@29.7.0)(jest@29.7.0(@types/node@20.19.1)(ts-node@10.9.2(@types/node@20.19.1)(typescript@4.9.5)))(typescript@4.9.5):