diff --git a/packages/ai/src/llm/opus-4-1.ts b/packages/ai/src/llm/opus-4-1.ts new file mode 100644 index 000000000..24f6020a3 --- /dev/null +++ b/packages/ai/src/llm/opus-4-1.ts @@ -0,0 +1,65 @@ +import type { LanguageModelV2 } from '@ai-sdk/provider'; +import { createFallback } from './ai-fallback'; +import { anthropicModel } from './providers/anthropic'; +import { vertexModel } from './providers/vertex'; + +// Lazy initialization to allow mocking in tests +let _opus41Instance: ReturnType | null = null; + +function initializeOpus41() { + if (_opus41Instance) { + return _opus41Instance; + } + + // Build models array based on available credentials + const models: LanguageModelV2[] = []; + + // Only include Anthropic if API key is available + if (process.env.ANTHROPIC_API_KEY) { + try { + models.push(anthropicModel('claude-opus-4-1-20250805')); + console.info('Opus41: Anthropic model added to fallback chain'); + } catch (error) { + console.warn('Opus41: Failed to initialize Anthropic model:', error); + } + } + + // Ensure we have at least one model + if (models.length === 0) { + throw new Error( + 'No AI models available. Please set either Vertex AI (VERTEX_CLIENT_EMAIL and VERTEX_PRIVATE_KEY) or Anthropic (ANTHROPIC_API_KEY) credentials.' + ); + } + + console.info(`Opus41: Initialized with ${models.length} model(s) in fallback chain`); + + _opus41Instance = createFallback({ + models, + modelResetInterval: 60000, + retryAfterOutput: true, + onError: (err) => console.error(`FALLBACK. Here is the error: ${err}`), + }); + + return _opus41Instance; +} + +// Export a proxy that initializes on first use +export const Opus41 = new Proxy({} as ReturnType, { + get(_target, prop) { + const instance = initializeOpus41(); + // Direct property access without receiver to avoid proxy conflicts + return instance[prop as keyof typeof instance]; + }, + has(_target, prop) { + const instance = initializeOpus41(); + return prop in instance; + }, + ownKeys(_target) { + const instance = initializeOpus41(); + return Reflect.ownKeys(instance); + }, + getOwnPropertyDescriptor(_target, prop) { + const instance = initializeOpus41(); + return Reflect.getOwnPropertyDescriptor(instance, prop); + }, +}); diff --git a/packages/ai/src/llm/sonnet-4.ts b/packages/ai/src/llm/sonnet-4.ts index 86e090f4a..8c282766a 100644 --- a/packages/ai/src/llm/sonnet-4.ts +++ b/packages/ai/src/llm/sonnet-4.ts @@ -34,6 +34,15 @@ function initializeSonnet4() { } } + if (process.env.ANTHROPIC_API_KEY) { + try { + models.push(anthropicModel('claude-opus-4-1-20250805')); + console.info('Opus41: Anthropic model added to fallback chain'); + } catch (error) { + console.warn('Opus41: Failed to initialize Anthropic model:', error); + } + } + // Ensure we have at least one model if (models.length === 0) { throw new Error(