diff --git a/js/plugins/anthropic/package.json b/js/plugins/anthropic/package.json index 32b1c4ba87..b2262b0d7c 100644 --- a/js/plugins/anthropic/package.json +++ b/js/plugins/anthropic/package.json @@ -29,7 +29,7 @@ "genkit": "workspace:^" }, "dependencies": { - "@anthropic-ai/sdk": "^0.68.0" + "@anthropic-ai/sdk": "^0.71.2" }, "devDependencies": { "@types/node": "^20.11.16", @@ -64,6 +64,7 @@ "build": "npm-run-all build:clean check compile", "build:watch": "tsup-node --watch", "test": "tsx --test tests/*_test.ts", + "test:live": "tsx --test tests/live_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/models.ts b/js/plugins/anthropic/src/models.ts index 2ee33d933c..54225f2b24 100644 --- a/js/plugins/anthropic/src/models.ts +++ b/js/plugins/anthropic/src/models.ts @@ -81,15 +81,45 @@ export const KNOWN_CLAUDE_MODELS: Record< 'claude-opus-4': commonRef('claude-opus-4', AnthropicThinkingConfigSchema), 'claude-sonnet-4-5': commonRef( 'claude-sonnet-4-5', - AnthropicThinkingConfigSchema + AnthropicThinkingConfigSchema, + { + supports: { + multiturn: true, + tools: true, + media: true, + systemRole: true, + output: ['text', 'json'], + constrained: 'all', + }, + } ), 'claude-haiku-4-5': commonRef( 'claude-haiku-4-5', - AnthropicThinkingConfigSchema + AnthropicThinkingConfigSchema, + { + supports: { + multiturn: true, + tools: true, + media: true, + systemRole: true, + output: ['text', 'json'], + constrained: 'all', + }, + } ), 'claude-opus-4-1': commonRef( 'claude-opus-4-1', - AnthropicThinkingConfigSchema + AnthropicThinkingConfigSchema, + { + supports: { + multiturn: true, + tools: true, + media: true, + systemRole: true, + output: ['text', 'json'], + constrained: 'all', + }, + } ), }; @@ -216,9 +246,11 @@ export function claudeModel( 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; + const knownModelRef = KNOWN_CLAUDE_MODELS[name]; + let modelInfo = knownModelRef + ? knownModelRef.info + : GENERIC_CLAUDE_MODEL_INFO; + const configSchema = knownModelRef?.configSchema ?? AnthropicConfigSchema; return model< AnthropicBaseConfigSchemaType | AnthropicThinkingConfigSchemaType diff --git a/js/plugins/anthropic/src/runner/beta.ts b/js/plugins/anthropic/src/runner/beta.ts index 6a71fa71d5..9fd7a306a1 100644 --- a/js/plugins/anthropic/src/runner/beta.ts +++ b/js/plugins/anthropic/src/runner/beta.ts @@ -66,6 +66,56 @@ const BETA_UNSUPPORTED_SERVER_TOOL_BLOCK_TYPES = new Set([ 'container_upload', ]); +const BETA_APIS = [ + // 'message-batches-2024-09-24', + // 'prompt-caching-2024-07-31', + // 'computer-use-2025-01-24', + // 'pdfs-2024-09-25', + // 'token-counting-2024-11-01', + // 'token-efficient-tools-2025-02-19', + // 'output-128k-2025-02-19', + // 'files-api-2025-04-14', + // 'mcp-client-2025-04-04', + // 'dev-full-thinking-2025-05-14', + // 'interleaved-thinking-2025-05-14', + // 'code-execution-2025-05-22', + // 'extended-cache-ttl-2025-04-11', + // 'context-1m-2025-08-07', + // 'context-management-2025-06-27', + // 'model-context-window-exceeded-2025-08-26', + // 'skills-2025-10-02', + // 'effort-param-2025-11-24', + // 'advanced-tool-use-2025-11-20', + 'structured-outputs-2025-11-13', +]; + +/** + * Transforms a JSON schema to be compatible with Anthropic's structured output requirements. + * Anthropic requires `additionalProperties: false` on all object types. + */ +function toAnthropicSchema( + schema: Record +): Record { + const out = structuredClone(schema); + + // Remove $schema if present + delete out.$schema; + + // Add additionalProperties: false to objects + if (out.type === 'object') { + out.additionalProperties = false; + } + + // Recursively process nested objects + for (const key in out) { + if (typeof out[key] === 'object' && out[key] !== null) { + out[key] = toAnthropicSchema(out[key] as Record); + } + } + + return out; +} + 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.`; @@ -254,6 +304,7 @@ export class BetaRunner extends BaseRunner { max_tokens: request.config?.maxOutputTokens ?? this.DEFAULT_MAX_OUTPUT_TOKENS, messages, + betas: BETA_APIS, }; if (betaSystem !== undefined) body.system = betaSystem; @@ -281,10 +332,12 @@ export class BetaRunner extends BaseRunner { 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` - ); + // Apply structured output when model supports it and constrained output is requested + if (this.isStructuredOutputEnabled(request)) { + body.output_format = { + type: 'json_schema', + schema: toAnthropicSchema(request.output!.schema!), + }; } return body; @@ -322,6 +375,7 @@ export class BetaRunner extends BaseRunner { request.config?.maxOutputTokens ?? this.DEFAULT_MAX_OUTPUT_TOKENS, messages, stream: true, + betas: BETA_APIS, }; if (betaSystem !== undefined) body.system = betaSystem; @@ -349,12 +403,13 @@ export class BetaRunner extends BaseRunner { 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` - ); + // Apply structured output when model supports it and constrained output is requested + if (this.isStructuredOutputEnabled(request)) { + body.output_format = { + type: 'json_schema', + schema: toAnthropicSchema(request.output!.schema!), + }; } - return body; } @@ -491,4 +546,14 @@ export class BetaRunner extends BaseRunner { return 'other'; } } + + private isStructuredOutputEnabled( + request: GenerateRequest + ): boolean { + return !!( + request.output?.schema && + request.output.constrained && + request.output.format === 'json' + ); + } } diff --git a/js/plugins/anthropic/tests/live_test.ts b/js/plugins/anthropic/tests/live_test.ts new file mode 100644 index 0000000000..ae83a4d4fc --- /dev/null +++ b/js/plugins/anthropic/tests/live_test.ts @@ -0,0 +1,70 @@ +/** + * 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 * as assert from 'assert'; +import { genkit, z } from 'genkit'; +import { describe, it } from 'node:test'; +import { anthropic } from '../src/index.js'; + +const SKIP_LIVE_TESTS = !process.env.ANTHROPIC_API_KEY; + +describe('Anthropic Live Tests', { skip: SKIP_LIVE_TESTS }, () => { + it('should return structured output matching the schema', async () => { + const ai = genkit({ + plugins: [anthropic({ apiVersion: 'beta' })], + }); + + const schema = z.object({ + name: z.string(), + age: z.number(), + city: z.string(), + isStudent: z.boolean(), + isEmployee: z.boolean(), + isRetired: z.boolean(), + isUnemployed: z.boolean(), + isDisabled: z.boolean(), + }); + + const result = await ai.generate({ + model: 'anthropic/claude-sonnet-4-5', + prompt: + 'Generate a fictional person with name "Alice", age 30, and city "New York". Return only the JSON.', + output: { schema, format: 'json', constrained: true }, + }); + + const parsed = result.output; + assert.ok(parsed, 'Should have parsed output'); + assert.deepStrictEqual( + { name: parsed.name, age: parsed.age, city: parsed.city }, + { name: 'Alice', age: 30, city: 'New York' } + ); + + // Check that boolean fields are present and are actually booleans + for (const key of [ + 'isStudent', + 'isEmployee', + 'isRetired', + 'isUnemployed', + 'isDisabled', + ]) { + assert.strictEqual( + typeof parsed[key], + 'boolean', + `Field ${key} should be a boolean but got: ${typeof parsed[key]}` + ); + } + }); +}); diff --git a/js/plugins/anthropic/tests/mocks/anthropic-client.ts b/js/plugins/anthropic/tests/mocks/anthropic-client.ts index 321df8f24f..e2dd4a7458 100644 --- a/js/plugins/anthropic/tests/mocks/anthropic-client.ts +++ b/js/plugins/anthropic/tests/mocks/anthropic-client.ts @@ -366,6 +366,7 @@ export function mockMessageWithContent( } function toBetaMessage(message: Message): BetaMessage { + // @ts-ignore return { ...message, container: null, diff --git a/js/plugins/anthropic/tests/structured_output_test.ts b/js/plugins/anthropic/tests/structured_output_test.ts new file mode 100644 index 0000000000..9a1e31fcd5 --- /dev/null +++ b/js/plugins/anthropic/tests/structured_output_test.ts @@ -0,0 +1,358 @@ +/** + * 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 * as assert from 'assert'; +import { genkit, z } from 'genkit'; +import { describe, test } from 'node:test'; +import { anthropic } from '../src/index.js'; +import { __testClient } from '../src/types.js'; +import { + createMockAnthropicClient, + createMockAnthropicMessage, +} from './mocks/anthropic-client.js'; + +/** + * Test constants for consistent test setup + */ +const TEST_API_KEY = 'test-key'; +const SUPPORTING_MODEL = 'anthropic/claude-sonnet-4-5'; +const NON_SUPPORTING_MODEL = 'anthropic/claude-sonnet-4'; + +/** + * Options for creating a plugin with a mock client + */ +interface CreatePluginOptions { + apiVersion?: 'beta' | 'stable'; + mockClient: Anthropic; +} + +/** + * Creates an Anthropic plugin configured with a mock client for testing + */ +function createPlugin(options: CreatePluginOptions) { + return anthropic({ + apiKey: TEST_API_KEY, + apiVersion: options.apiVersion, + // @ts-ignore + [__testClient]: options.mockClient, + }); +} + +/** + * Creates a Genkit instance with the given plugin + */ +function createGenkitInstance(plugin: ReturnType) { + return genkit({ + plugins: [plugin], + }); +} + +/** + * Helper to get the proper create stub from the mock client for a given API version. + */ +function getCreateStub(mockClient: Anthropic, apiVersion: 'beta' | 'stable') { + return apiVersion === 'beta' + ? (mockClient.beta.messages.create as any) + : (mockClient.messages.create as any); +} + +/** + * Extracts the API request object from the mock for verification + * @param apiVersion - 'beta' or 'stable' to determine which API endpoint to check + */ +function getApiRequest( + mockClient: Anthropic, + apiVersion: 'beta' | 'stable', + callIndex: number = 0 +) { + const stub = getCreateStub(mockClient, apiVersion); + return stub.mock.calls[callIndex]?.arguments[0]; +} + +/** + * Verifies that the API was called the expected number of times + * @param apiVersion - 'beta' or 'stable' to determine which API endpoint to verify + */ +function verifyApiCalled( + mockClient: Anthropic, + apiVersion: 'beta' | 'stable', + expectedCalls: number = 1 +) { + const stub = getCreateStub(mockClient, apiVersion); + assert.strictEqual( + stub.mock.calls.length, + expectedCalls, + `${apiVersion === 'beta' ? 'Beta' : 'Stable'} API should be called ${expectedCalls} time(s)` + ); +} + +/** + * Tests for structured output (constrained generation) functionality. + * These tests verify that output_format is correctly passed to the Anthropic API + * when using the beta API with constrained output, and that it's NOT passed + * in various edge cases (stable API, non-json format, missing schema, etc.) + */ +describe('Structured Output Tests', () => { + test('should pass output_format to API when using beta API with constrained output', async () => { + const mockClient = createMockAnthropicClient({ + messageResponse: createMockAnthropicMessage({ + text: '{"name":"Alice","age":30,"city":"New York","isStudent":false,"isEmployee":true,"isRetired":false,"isUnemployed":false,"isDisabled":false}', + }), + }); + + // Set up plugin with beta API enabled + const plugin = createPlugin({ + apiVersion: 'beta', + mockClient, + }); + + const ai = createGenkitInstance(plugin); + + // Call generate with sonnet 4.5 (supports native constrained output) + await ai.generate({ + model: SUPPORTING_MODEL, + prompt: + 'Generate a fictional person with name "Alice", age 30, and city "New York". Return only the JSON.', + output: { + schema: z.object({ + name: z.string(), + age: z.number(), + city: z.string(), + isStudent: z.boolean(), + isEmployee: z.boolean(), + isRetired: z.boolean(), + isUnemployed: z.boolean(), + isDisabled: z.boolean(), + }), + format: 'json', + constrained: true, + }, + }); + + // Verify the beta API was called + verifyApiCalled(mockClient, 'beta'); + + // Verify output_format was included in the API request + const apiRequest = getApiRequest(mockClient, 'beta'); + assert.ok(apiRequest.output_format, 'Request should have output_format'); + assert.strictEqual( + apiRequest.output_format.type, + 'json_schema', + 'output_format type should be json_schema' + ); + assert.ok( + apiRequest.output_format.schema, + 'output_format should have schema' + ); + // Verify schema transformation: additionalProperties should be false for constrained output + assert.strictEqual( + apiRequest.output_format.schema.additionalProperties, + false, + 'Schema should have additionalProperties: false' + ); + }); + + test('should NOT pass output_format to API when constrained is false and using beta API', async () => { + const mockClient = createMockAnthropicClient({ + messageResponse: createMockAnthropicMessage({ + text: '{"name":"Alice"}', + }), + }); + + // Set up plugin with beta API enabled + const plugin = createPlugin({ + apiVersion: 'beta', + mockClient, + }); + + const ai = createGenkitInstance(plugin); + + // Call generate with constrained: false + await ai.generate({ + model: SUPPORTING_MODEL, + prompt: 'Generate JSON', + output: { + format: 'json', + constrained: false, + schema: z.object({ + name: z.string(), + }), + }, + }); + + // Verify the beta API was called + verifyApiCalled(mockClient, 'beta'); + + // Verify output_format was NOT included when constrained is false + const apiRequest = getApiRequest(mockClient, 'beta'); + assert.strictEqual( + apiRequest.output_format, + undefined, + 'Request should NOT have output_format when constrained is false' + ); + }); + + test('should NOT pass output_format to API when format is not json and using beta API', async () => { + const mockClient = createMockAnthropicClient({ + messageResponse: createMockAnthropicMessage({ + text: 'Some text response', + }), + }); + + // Set up plugin with beta API enabled + const plugin = createPlugin({ + apiVersion: 'beta', + mockClient, + }); + + const ai = createGenkitInstance(plugin); + + // Call generate with format: 'text' (not 'json') + await ai.generate({ + model: SUPPORTING_MODEL, + prompt: 'Generate text', + output: { + format: 'text', + constrained: true, + }, + }); + + // Verify the beta API was called + verifyApiCalled(mockClient, 'beta'); + + // Verify output_format was NOT included when format is not json + const apiRequest = getApiRequest(mockClient, 'beta'); + assert.strictEqual( + apiRequest.output_format, + undefined, + 'Request should NOT have output_format when format is text' + ); + }); + + test('should NOT pass output_format to API when schema is not provided and using beta API', async () => { + const mockClient = createMockAnthropicClient({ + messageResponse: createMockAnthropicMessage({ + text: '{"anything": "goes"}', + }), + }); + + // Set up plugin with beta API enabled + const plugin = createPlugin({ + apiVersion: 'beta', + mockClient, + }); + + const ai = createGenkitInstance(plugin); + + // Call generate with constrained: true but no schema + await ai.generate({ + model: SUPPORTING_MODEL, + prompt: 'Generate JSON', + output: { + format: 'json', + constrained: true, + // No schema provided + }, + }); + + // Verify the beta API was called + verifyApiCalled(mockClient, 'beta'); + + // Verify output_format was NOT included when schema is missing + const apiRequest = getApiRequest(mockClient, 'beta'); + assert.strictEqual( + apiRequest.output_format, + undefined, + 'Request should NOT have output_format when schema is not provided' + ); + }); + + test('should NOT pass output_format to API when model does not support structured output and using beta API', async () => { + const mockClient = createMockAnthropicClient({ + messageResponse: createMockAnthropicMessage({ + text: '{"name":"Alice"}', + }), + }); + + // Set up plugin with beta API enabled + const plugin = createPlugin({ + apiVersion: 'beta', + mockClient, + }); + + const ai = createGenkitInstance(plugin); + + // Call generate with model that does not support structured output + await ai.generate({ + model: NON_SUPPORTING_MODEL, + prompt: 'Generate JSON', + output: { + format: 'json', + constrained: true, + }, + }); + + // Verify the beta API was called + verifyApiCalled(mockClient, 'beta'); + + // Verify output_format was NOT included when model does not support structured output + const apiRequest = getApiRequest(mockClient, 'beta'); + assert.strictEqual( + apiRequest.output_format, + undefined, + 'Request should NOT have output_format when model does not support structured output' + ); + }); + + test('should throw an error when using stable API with non-text output format', async () => { + const mockClient = createMockAnthropicClient({ + messageResponse: createMockAnthropicMessage({ + text: '{"name":"Alice","age":30,"city":"New York"}', + }), + }); + + // Set up plugin with stable API (not beta) + const plugin = createPlugin({ + apiVersion: 'stable', + mockClient, + }); + + const ai = createGenkitInstance(plugin); + + // Call generate with constrained output (would work with beta API) + // Expect an error to be thrown since only text output is supported for stable API + await assert.rejects( + async () => { + await ai.generate({ + model: SUPPORTING_MODEL, + prompt: 'Generate JSON', + output: { + format: 'json', + constrained: true, + schema: z.object({ + name: z.string(), + age: z.number(), + city: z.string(), + }), + }, + }); + }, + /Only text output format is supported for Claude models currently/, + 'Should throw an error for non-text output on stable API' + ); + }); +}); diff --git a/js/pnpm-lock.yaml b/js/pnpm-lock.yaml index 109b07c772..2565d73c8c 100644 --- a/js/pnpm-lock.yaml +++ b/js/pnpm-lock.yaml @@ -153,6 +153,10 @@ importers: zod-to-json-schema: specifier: ^3.22.4 version: 3.24.5(zod@3.25.67) + 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.24.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)) devDependencies: '@types/express': specifier: ^4.17.21 @@ -178,10 +182,6 @@ importers: typescript: specifier: ^4.9.0 version: 4.9.5 - 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.24.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: @@ -260,8 +260,8 @@ importers: plugins/anthropic: dependencies: '@anthropic-ai/sdk': - specifier: ^0.68.0 - version: 0.68.0(zod@3.25.67) + specifier: ^0.71.2 + version: 0.71.2(zod@3.25.67) devDependencies: '@types/node': specifier: ^20.11.16 @@ -961,7 +961,7 @@ importers: version: 0.24.3(encoding@0.1.13) '@anthropic-ai/vertex-sdk': specifier: ^0.4.0 - version: 0.4.3(encoding@0.1.13) + version: 0.4.3(encoding@0.1.13)(zod@3.25.67) '@google-cloud/aiplatform': specifier: ^3.23.0 version: 3.35.0(encoding@0.1.13) @@ -986,6 +986,13 @@ importers: openai: specifier: ^4.52.7 version: 4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.67) + optionalDependencies: + '@google-cloud/bigquery': + specifier: ^7.8.0 + version: 7.9.4(encoding@0.1.13) + firebase-admin: + specifier: '>=12.2' + version: 13.4.0(encoding@0.1.13) devDependencies: '@types/node': specifier: ^20.11.16 @@ -1017,13 +1024,6 @@ importers: typescript: specifier: ^4.9.0 version: 4.9.5 - optionalDependencies: - '@google-cloud/bigquery': - specifier: ^7.8.0 - version: 7.9.4(encoding@0.1.13) - firebase-admin: - specifier: '>=12.2' - version: 13.4.0(encoding@0.1.13) testapps/anthropic: dependencies: @@ -2092,8 +2092,17 @@ packages: '@anthropic-ai/sdk@0.24.3': resolution: {integrity: sha512-916wJXO6T6k8R6BAAcLhLPv/pnLGy7YSEBZXZ1XTFbLcTZE8oTy3oDW9WJf9KKZwMvVcePIfoTSvzXHRcGxkQQ==} - '@anthropic-ai/sdk@0.68.0': - resolution: {integrity: sha512-SMYAmbbiprG8k1EjEPMTwaTqssDT7Ae+jxcR5kWXiqTlbwMR2AthXtscEVWOHkRfyAV5+y3PFYTJRNa3OJWIEw==} + '@anthropic-ai/sdk@0.71.0': + resolution: {integrity: sha512-go1XeWXmpxuiTkosSXpb8tokLk2ZLkIRcXpbWVwJM6gH5OBtHOVsfPfGuqI1oW7RRt4qc59EmYbrXRZ0Ng06Jw==} + hasBin: true + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + peerDependenciesMeta: + zod: + optional: true + + '@anthropic-ai/sdk@0.71.2': + resolution: {integrity: sha512-TGNDEUuEstk/DKu0/TflXAEt+p+p/WhTlFzEnoosvbaDU2LTjm42igSdlL0VijrKpWejtOKxX0b8A7uc+XiSAQ==} hasBin: true peerDependencies: zod: ^3.25.0 || ^4.0.0 @@ -6564,6 +6573,7 @@ packages: next@15.3.3: resolution: {integrity: sha512-JqNj29hHNmCLtNvd090SyRbXJiivQ+58XjCcrC50Crb5g5u2zi7Y2YivbsEfzk6AtVI80akdOQbaMZwWB1Hthw==} engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} + deprecated: This version has a security vulnerability. Please upgrade to a patched version. See https://nextjs.org/blog/CVE-2025-66478 for more details. hasBin: true peerDependencies: '@opentelemetry/api': ^1.1.0 @@ -7271,6 +7281,7 @@ packages: source-map@0.8.0-beta.0: resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} engines: {node: '>= 8'} + deprecated: The work that was done in this beta branch won't be included in future versions spdx-correct@3.2.0: resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} @@ -7931,7 +7942,13 @@ snapshots: transitivePeerDependencies: - encoding - '@anthropic-ai/sdk@0.68.0(zod@3.25.67)': + '@anthropic-ai/sdk@0.71.0(zod@3.25.67)': + dependencies: + json-schema-to-ts: 3.1.1 + optionalDependencies: + zod: 3.25.67 + + '@anthropic-ai/sdk@0.71.2(zod@3.25.67)': dependencies: json-schema-to-ts: 3.1.1 optionalDependencies: @@ -7951,13 +7968,14 @@ snapshots: transitivePeerDependencies: - encoding - '@anthropic-ai/vertex-sdk@0.4.3(encoding@0.1.13)': + '@anthropic-ai/vertex-sdk@0.4.3(encoding@0.1.13)(zod@3.25.67)': dependencies: - '@anthropic-ai/sdk': 0.24.3(encoding@0.1.13) + '@anthropic-ai/sdk': 0.71.0(zod@3.25.67) google-auth-library: 9.15.1(encoding@0.1.13) transitivePeerDependencies: - encoding - supports-color + - zod '@babel/code-frame@7.25.7': dependencies: diff --git a/js/testapps/anthropic/src/beta/basic.ts b/js/testapps/anthropic/src/beta/basic.ts index d1309b3400..d5623310f8 100644 --- a/js/testapps/anthropic/src/beta/basic.ts +++ b/js/testapps/anthropic/src/beta/basic.ts @@ -20,7 +20,11 @@ import { genkit } from 'genkit'; const ai = genkit({ plugins: [ // Default all flows in this sample to the beta surface - anthropic({ apiVersion: 'beta', cacheSystemPrompt: true }), + anthropic({ + apiVersion: 'beta', + cacheSystemPrompt: true, + apiKey: process.env.ANTHROPIC_API_KEY, + }), ], }); @@ -34,6 +38,7 @@ ai.defineFlow('anthropic-beta-hello', async () => { prompt: 'You are Claude on the beta API. Provide a concise greeting that mentions that you are using the beta API.', config: { temperature: 0.6 }, + output: {}, }); return text;