diff --git a/package.json b/package.json index e20f3b3..8c149a6 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ }, "dependencies": { "@ai-sdk/anthropic": "^2.0.45", + "@ai-sdk/google": "^2.0.39", "@anthropic-ai/sdk": "^0.68.0", "@axe-core/puppeteer": "^4.10.2", "@genkit-ai/compat-oai": "1.23.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ef8fb9d..f0c1046 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@ai-sdk/anthropic': specifier: ^2.0.45 version: 2.0.45(zod@3.25.76) + '@ai-sdk/google': + specifier: ^2.0.39 + version: 2.0.39(zod@3.25.76) '@anthropic-ai/sdk': specifier: ^0.68.0 version: 0.68.0(zod@3.25.76) @@ -259,6 +262,12 @@ packages: peerDependencies: zod: ^3.25.76 || ^4.1.8 + '@ai-sdk/google@2.0.39': + resolution: {integrity: sha512-uCqNNABzIvY6e4dutFO5P428HcaiwvgvBPUsqI2W7qB8ee+Oz5WtNGKPeR5gnf9736HaB3UrIUZFWGhr4/HwVQ==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + '@ai-sdk/provider-utils@3.0.17': resolution: {integrity: sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw==} engines: {node: '>=18'} @@ -7389,6 +7398,12 @@ snapshots: '@vercel/oidc': 3.0.3 zod: 3.25.76 + '@ai-sdk/google@2.0.39(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.17(zod@3.25.76) + zod: 3.25.76 + '@ai-sdk/provider-utils@3.0.17(zod@3.25.76)': dependencies: '@ai-sdk/provider': 2.0.0 diff --git a/runner/codegen/ai-sdk-runner.ts b/runner/codegen/ai-sdk-runner.ts index dfb30f5..db2d6f1 100644 --- a/runner/codegen/ai-sdk-runner.ts +++ b/runner/codegen/ai-sdk-runner.ts @@ -17,6 +17,7 @@ import { SystemModelMessage, TextPart, } from 'ai'; +import {google, GoogleGenerativeAIProviderOptions} from '@ai-sdk/google'; import {anthropic, AnthropicProviderOptions} from '@ai-sdk/anthropic'; import z from 'zod'; import {callWithTimeout} from '../utils/timeout.js'; @@ -27,6 +28,10 @@ const SUPPORTED_MODELS = [ 'claude-opus-4.1-with-thinking', 'claude-sonnet-4.5-no-thinking', 'claude-sonnet-4.5-with-thinking', + 'gemini-2.5-flash-lite', + 'gemini-2.5-flash', + 'gemini-2.5-pro', + 'gemini-3-pro-preview', ] as const; // Increased to a very high value as we rely on an actual timeout @@ -131,7 +136,8 @@ export class AiSDKRunner implements LlmRunner { private async _getAiSdkModelOptions( request: LocalLlmGenerateTextRequestOptions, ): Promise<{model: LanguageModel; providerOptions: {}}> { - switch (request.model) { + const modelName = request.model as (typeof SUPPORTED_MODELS)[number]; + switch (modelName) { case 'claude-opus-4.1-no-thinking': case 'claude-opus-4.1-with-thinking': { const thinkingEnabled = request.model.endsWith('with-thinking'); @@ -149,11 +155,23 @@ export class AiSDKRunner implements LlmRunner { return { model: anthropic('claude-sonnet-4-5'), providerOptions: { - sendReasoning: true, - thinking: {type: 'enabled'}, + sendReasoning: thinkingEnabled, + thinking: {type: thinkingEnabled ? 'enabled' : 'disabled'}, } satisfies AnthropicProviderOptions, }; } + case 'gemini-2.5-flash-lite': + case 'gemini-2.5-flash': + case 'gemini-2.5-pro': + case 'gemini-3-pro-preview': + return { + model: google(modelName), + providerOptions: { + thinkingConfig: { + includeThoughts: request.thinkingConfig?.includeThoughts, + }, + } satisfies GoogleGenerativeAIProviderOptions, + }; default: throw new Error(`Unexpected model in AI SDK runner: ${request.model}.`); } diff --git a/runner/codegen/genkit/providers/gemini.ts b/runner/codegen/genkit/providers/gemini.ts index d5f8e29..b72fa50 100644 --- a/runner/codegen/genkit/providers/gemini.ts +++ b/runner/codegen/genkit/providers/gemini.ts @@ -82,7 +82,9 @@ export class GeminiModelProvider extends GenkitModelProvider { validateGeneratedFiles(files: LlmResponseFile[]): boolean { // Gemini responses occasionally get truncated on `class=`. // Consider these cases as invalid so they don't influence the results. - return files.length === 0 || !files.some(file => file.code.trim().endsWith('class=')); + // TODO: Consider re-enabling this. + // return files.length === 0 || !files.some(file => file.code.trim().endsWith('class=')); + return true; } private async countGeminiTokens(