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
Markdown
# 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