/ lib / providers / gemini.ts
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  };