gemini.ts
1 import type { LLMProvider } from '../provider'; 2 import { resolveBinaryPath, spawnCliStreaming, spawnCliQuick } from './shared'; 3 4 const INSTALL_HINT = 'Install it from https://github.com/google-gemini/gemini-cli and authenticate.'; 5 const STDIN_PROMPT = 'Respond according to the instructions provided via stdin.'; 6 7 function resolveGeminiPath(): string { 8 return resolveBinaryPath('gemini'); 9 } 10 11 function handleGeminiExitError(stderr: string): Error | undefined { 12 const msg = stderr.slice(0, 300); 13 if (msg.includes('ModelNotFoundError') || msg.includes('not found')) { 14 return new Error('Model is not available on your account. Try a different model.'); 15 } 16 return undefined; 17 } 18 19 export const geminiProvider: LLMProvider = { 20 name: 'gemini', 21 models: [ 22 { id: 'gemini-3.1-pro-preview', label: 'Gemini 3.1 Pro' }, 23 { id: 'gemini-3-pro-preview', label: 'Gemini 3 Pro' }, 24 { id: 'gemini-3-flash-preview', label: 'Gemini 3 Flash', quick: true }, 25 { id: 'gemini-2.5-pro', label: 'Gemini 2.5 Pro' }, 26 { id: 'gemini-2.5-flash', label: 'Gemini 2.5 Flash' }, 27 ], 28 29 async generate({ content, systemPrompt, model, onChunk, signal }) { 30 const geminiPath = resolveGeminiPath(); 31 let fullText = ''; 32 33 function processLine(line: string): void { 34 const trimmed = line.trim(); 35 if (!trimmed) return; 36 try { 37 const obj = JSON.parse(trimmed) as Record<string, unknown>; 38 if (obj.type === 'message' && obj.role === 'assistant' && obj.delta === true) { 39 const chunk = obj.content; 40 if (typeof chunk === 'string') { 41 fullText += chunk; 42 onChunk?.(chunk, false); 43 } 44 } 45 } catch { 46 // not a JSON event 47 } 48 } 49 50 await spawnCliStreaming({ 51 binPath: geminiPath, 52 cliName: 'Gemini', 53 args: ['-p', STDIN_PROMPT, '-m', model, '--output-format', 'stream-json', '--sandbox'], 54 stdinContent: systemPrompt + '\n\n' + content, 55 processLine, 56 installHint: INSTALL_HINT, 57 handleExitError: handleGeminiExitError, 58 signal, 59 }); 60 61 return fullText.trim(); 62 }, 63 64 quick({ content, systemPrompt, model }) { 65 const geminiPath = resolveGeminiPath(); 66 return spawnCliQuick({ 67 binPath: geminiPath, 68 cliName: 'Gemini', 69 args: ['-p', STDIN_PROMPT, '-m', model, '--output-format', 'text', '--sandbox'], 70 stdinContent: systemPrompt + '\n\n' + content, 71 installHint: INSTALL_HINT, 72 handleExitError: handleGeminiExitError, 73 }); 74 }, 75 };