UNPKG

ai-sdk-provider-gemini-cli

Version:

Community AI SDK provider for Google Gemini using the official CLI/SDK

461 lines (363 loc) • 10.6 kB
# Gemini CLI Provider for AI SDK v5 Guide This guide covers how to use the Gemini CLI Provider with Vercel AI SDK v5. ## Table of Contents - [Installation](#installation) - [Authentication](#authentication) - [Basic Usage](#basic-usage) - [Streaming](#streaming) - [Conversation History](#conversation-history) - [System Messages](#system-messages) - [Structured Output](#structured-output) - [Error Handling](#error-handling) - [Advanced Features](#advanced-features) - [Best Practices](#best-practices) ## Installation ```bash # Install the beta versions npm install ai-sdk-provider-gemini-cli@beta ai@beta # Install and set up Gemini CLI npm install -g @google/gemini-cli gemini # Follow authentication setup ``` ## Authentication ### OAuth Authentication (Recommended) ```typescript import { createGeminiProvider } from 'ai-sdk-provider-gemini-cli'; const gemini = createGeminiProvider({ authType: 'oauth-personal', }); ``` ### API Key Authentication ```typescript const gemini = createGeminiProvider({ authType: 'api-key', apiKey: process.env.GEMINI_API_KEY, }); ``` ## Basic Usage ### Text Generation ```typescript import { generateText } from 'ai'; import { createGeminiProvider } from 'ai-sdk-provider-gemini-cli'; const gemini = createGeminiProvider({ authType: 'oauth-personal', }); async function generate() { const result = await generateText({ model: gemini('gemini-2.5-pro'), prompt: 'Write a haiku about coding', }); console.log(result.text); console.log(`Tokens used: ${result.usage?.totalTokens}`); } ``` ### Model Configuration ```typescript const model = gemini('gemini-2.5-pro', { temperature: 0.7, // Creativity (0-2) maxOutputTokens: 1000, // Max tokens to generate topP: 0.95, // Nucleus sampling topK: 40, // Top-k sampling }); ``` ## Streaming ### Basic Streaming ```typescript import { streamText } from 'ai'; async function stream() { const result = await streamText({ model: gemini('gemini-2.5-pro'), prompt: 'Tell me a story about a robot', }); // Stream chunks as they arrive for await (const chunk of result.textStream) { process.stdout.write(chunk); } // Access full text after streaming const fullText = await result.text; console.log('\n\nFull text length:', fullText.length); } ``` ### Progress Tracking ```typescript async function streamWithProgress() { const result = await streamText({ model: gemini('gemini-2.5-pro'), prompt: 'Write a detailed article about AI', }); let charCount = 0; const startTime = Date.now(); for await (const chunk of result.textStream) { charCount += chunk.length; const elapsed = ((Date.now() - startTime) / 1000).toFixed(1); process.stdout.write(`\ršŸ“ Generated: ${charCount} chars | Time: ${elapsed}s`); } console.log('\nāœ… Complete!'); } ``` ## Conversation History ### Multi-turn Conversations ```typescript async function conversation() { const result = await generateText({ model: gemini('gemini-2.5-pro'), messages: [ { role: 'user', content: 'My name is Alice' }, { role: 'assistant', content: 'Nice to meet you, Alice! How can I help you today?' }, { role: 'user', content: 'What is my name?' }, ], }); console.log(result.text); // Should remember "Alice" } ``` ### Building Conversation Context ```typescript const messages = []; function addUserMessage(content: string) { messages.push({ role: 'user', content }); } function addAssistantMessage(content: string) { messages.push({ role: 'assistant', content }); } async function continueConversation(userInput: string) { addUserMessage(userInput); const result = await generateText({ model: gemini('gemini-2.5-pro'), messages, }); addAssistantMessage(result.text); return result.text; } ``` ## System Messages ### Setting Model Behavior ```typescript async function withSystemMessage() { const result = await generateText({ model: gemini('gemini-2.5-pro'), system: 'You are a helpful coding assistant. Always include code examples in your responses.', prompt: 'How do I read a file in Node.js?', }); console.log(result.text); // Will include code examples } ``` ### Complex System Instructions ```typescript const system = `You are an expert TypeScript developer. - Always use modern ES6+ syntax - Include type annotations - Follow best practices - Explain your code clearly`; const result = await generateText({ model: gemini('gemini-2.5-pro'), system, prompt: 'Create a generic cache class', }); ``` ## Structured Output ### Basic Object Generation ```typescript import { generateObject } from 'ai'; import { z } from 'zod'; async function generateProduct() { const result = await generateObject({ model: gemini('gemini-2.5-pro'), schema: z.object({ name: z.string().describe('Product name'), price: z.number().describe('Price in USD'), inStock: z.boolean().describe('Availability'), }), prompt: 'Generate a laptop product', }); console.log(result.object); // { name: "UltraBook Pro", price: 1299.99, inStock: true } } ``` ### Nested Structures ```typescript const CompanySchema = z.object({ name: z.string(), founded: z.number(), employees: z.array(z.object({ name: z.string(), role: z.string(), department: z.string(), })), metrics: z.object({ revenue: z.number(), growth: z.number(), }), }); const result = await generateObject({ model: gemini('gemini-2.5-pro'), schema: CompanySchema, prompt: 'Generate a tech startup company profile', }); ``` ### Handling Validation ```typescript try { const result = await generateObject({ model: gemini('gemini-2.5-pro'), schema: z.object({ description: z.string().max(100), // Strict limit }), prompt: 'Describe quantum computing', }); } catch (error) { // Note: Error may say "could not parse" but usually means // validation failed (e.g., string too long) console.error('Validation failed:', error.message); } ``` ## Error Handling ### Basic Error Handling ```typescript try { const result = await generateText({ model: gemini('gemini-2.5-pro'), prompt: 'Hello', }); } catch (error) { if (error.name === 'AbortError') { console.log('Request was cancelled'); } else if (error.message.includes('quota')) { console.log('Rate limit exceeded'); } else { console.error('Unexpected error:', error); } } ``` ### Timeout Management ```typescript async function withTimeout() { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 10000); // 10 seconds try { const result = await generateText({ model: gemini('gemini-2.5-pro'), prompt: 'Write a detailed analysis', abortSignal: controller.signal, }); clearTimeout(timeout); return result.text; } catch (error) { if (error.name === 'AbortError') { console.log('Request timed out'); } throw error; } } ``` **Note**: Due to gemini-cli-core limitations, aborted requests continue in the background even though the SDK throws AbortError. ## Advanced Features ### Multimodal Input (Images) ```typescript import { readFileSync } from 'fs'; async function analyzeImage() { const imageBuffer = readFileSync('diagram.png'); const base64Image = imageBuffer.toString('base64'); const result = await generateText({ model: gemini('gemini-2.5-pro'), messages: [{ role: 'user', content: [ { type: 'text', text: 'What is shown in this image?' }, { type: 'image', data: base64Image }, ], }], }); console.log(result.text); } ``` ### Token Usage Monitoring ```typescript async function trackUsage() { const results = []; for (const prompt of prompts) { const result = await generateText({ model: gemini('gemini-2.5-pro'), prompt, }); results.push({ prompt: prompt.substring(0, 50), inputTokens: result.usage?.inputTokens || 0, outputTokens: result.usage?.outputTokens || 0, totalTokens: result.usage?.totalTokens || 0, }); } const totalTokens = results.reduce((sum, r) => sum + r.totalTokens, 0); console.log('Total tokens used:', totalTokens); } ``` ## Best Practices ### 1. Model Selection - Use **gemini-3-pro-preview** for enhanced reasoning capabilities - Use **gemini-2.5-pro** for production workloads requiring the previous generation stable model - Use **gemini-2.5-flash** for simpler tasks where speed is important ### 2. Prompt Engineering ```typescript // Be specific and clear const goodPrompt = `Write a Python function that: 1. Takes a list of integers as input 2. Returns the sum of even numbers 3. Includes type hints 4. Has a docstring`; // Avoid vague prompts const badPrompt = 'Write a function'; ``` ### 3. Error Recovery ```typescript async function generateWithRetry(prompt: string, maxRetries = 3) { for (let i = 0; i < maxRetries; i++) { try { const result = await generateText({ model: gemini('gemini-2.5-pro'), prompt, }); return result; } catch (error) { if (i === maxRetries - 1) throw error; // Wait before retry (exponential backoff) await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i))); } } } ``` ### 4. Memory Management For long conversations, consider truncating message history: ```typescript function truncateMessages(messages: any[], maxMessages = 20) { if (messages.length <= maxMessages) return messages; // Keep system message (if any) and recent messages const systemMsg = messages.find(m => m.role === 'system'); const recentMessages = messages.slice(-maxMessages); return systemMsg ? [systemMsg, ...recentMessages] : recentMessages; } ``` ### 5. Streaming Best Practices ```typescript // Clean up resources on error async function safeStream() { let result; try { result = await streamText({ model: gemini('gemini-2.5-pro'), prompt: 'Tell me a story', }); for await (const chunk of result.textStream) { process.stdout.write(chunk); } } catch (error) { console.error('Stream error:', error); // Ensure stream is properly closed if (result?.textStream) { result.textStream.return?.(); } } } ``` ## Next Steps - Explore the [examples](../../examples/) directory for more patterns - Check [TROUBLESHOOTING.md](./TROUBLESHOOTING.md) for common issues - Review [BREAKING_CHANGES.md](./BREAKING_CHANGES.md) if migrating from v4