Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:24:51 +08:00
commit 8aebb293cd
31 changed files with 7386 additions and 0 deletions

View File

@@ -0,0 +1,245 @@
/**
* Complete Cloudflare Worker with Gemini API
*
* Demonstrates:
* - Fetch-based Gemini API integration (no SDK dependencies)
* - Streaming responses to client
* - Multi-turn chat with session storage
* - Error handling for production
* - CORS configuration
*
* Deploy:
* - npx wrangler deploy
* - Set GEMINI_API_KEY in Cloudflare dashboard or wrangler.toml
*/
interface Env {
GEMINI_API_KEY: string;
}
interface ChatMessage {
role: 'user' | 'model';
parts: Array<{ text: string }>;
}
export default {
/**
* Main request handler
*/
async fetch(request: Request, env: Env): Promise<Response> {
// CORS headers
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
};
// Handle preflight
if (request.method === 'OPTIONS') {
return new Response(null, { headers: corsHeaders });
}
const url = new URL(request.url);
try {
// Route: POST /api/chat (non-streaming)
if (url.pathname === '/api/chat' && request.method === 'POST') {
return await handleChat(request, env, corsHeaders);
}
// Route: POST /api/chat/stream (streaming)
if (url.pathname === '/api/chat/stream' && request.method === 'POST') {
return await handleChatStream(request, env, corsHeaders);
}
// Route: GET / (health check)
if (url.pathname === '/' && request.method === 'GET') {
return new Response(
JSON.stringify({ status: 'ok', service: 'Gemini API Worker' }),
{ headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
);
}
// 404 for unknown routes
return new Response('Not Found', { status: 404, headers: corsHeaders });
} catch (error: any) {
return new Response(
JSON.stringify({ error: error.message }),
{ status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
);
}
}
};
/**
* Handle non-streaming chat request
*/
async function handleChat(request: Request, env: Env, corsHeaders: any): Promise<Response> {
const { message, history = [] } = await request.json() as {
message: string;
history?: ChatMessage[];
};
// Build contents array with history
const contents: ChatMessage[] = [
...history,
{ role: 'user', parts: [{ text: message }] }
];
// Call Gemini API
const response = await fetch(
`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-goog-api-key': env.GEMINI_API_KEY,
},
body: JSON.stringify({ contents }),
}
);
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error?.message || 'Gemini API error');
}
const data = await response.json();
const assistantReply = data.candidates[0]?.content?.parts[0]?.text;
// Return response with updated history
return new Response(
JSON.stringify({
reply: assistantReply,
history: [
...history,
{ role: 'user', parts: [{ text: message }] },
{ role: 'model', parts: [{ text: assistantReply }] }
],
usage: data.usageMetadata
}),
{ headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
);
}
/**
* Handle streaming chat request
*/
async function handleChatStream(request: Request, env: Env, corsHeaders: any): Promise<Response> {
const { message, history = [] } = await request.json() as {
message: string;
history?: ChatMessage[];
};
const contents: ChatMessage[] = [
...history,
{ role: 'user', parts: [{ text: message }] }
];
// Create a TransformStream to stream to client
const { readable, writable } = new TransformStream();
const writer = writable.getWriter();
const encoder = new TextEncoder();
// Start streaming in background
(async () => {
try {
const response = await fetch(
`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:streamGenerateContent`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-goog-api-key': env.GEMINI_API_KEY,
},
body: JSON.stringify({ contents }),
}
);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
if (!response.body) {
throw new Error('Response body is null');
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (line.trim() === '' || line.startsWith('data: [DONE]')) continue;
if (!line.startsWith('data: ')) continue;
try {
const data = JSON.parse(line.slice(6));
const text = data.candidates[0]?.content?.parts[0]?.text;
if (text) {
await writer.write(encoder.encode(text));
}
} catch (e) {
// Skip invalid JSON
}
}
}
await writer.close();
} catch (error: any) {
await writer.write(encoder.encode(`\n\nError: ${error.message}`));
await writer.close();
}
})();
return new Response(readable, {
headers: {
...corsHeaders,
'Content-Type': 'text/plain; charset=utf-8',
'Transfer-Encoding': 'chunked',
},
});
}
/**
* Example Client Usage (JavaScript):
*
* // Non-streaming
* const response = await fetch('https://your-worker.workers.dev/api/chat', {
* method: 'POST',
* headers: { 'Content-Type': 'application/json' },
* body: JSON.stringify({
* message: 'What is quantum computing?',
* history: []
* })
* });
* const data = await response.json();
* console.log(data.reply);
*
* // Streaming
* const response = await fetch('https://your-worker.workers.dev/api/chat/stream', {
* method: 'POST',
* headers: { 'Content-Type': 'application/json' },
* body: JSON.stringify({
* message: 'Write a story',
* history: []
* })
* });
* const reader = response.body.getReader();
* const decoder = new TextDecoder();
* while (true) {
* const { done, value } = await reader.read();
* if (done) break;
* console.log(decoder.decode(value));
* }
*/

206
templates/code-execution.ts Normal file
View File

@@ -0,0 +1,206 @@
/**
* Google Gemini API - Code Execution Example
*
* Demonstrates how to enable code execution so the model can generate
* and run Python code to solve computational problems.
*
* Features:
* - Basic code execution
* - Data analysis with code
* - Chart generation
* - Error handling for failed execution
*
* Requirements:
* - @google/genai@1.27.0+
* - GEMINI_API_KEY environment variable
*
* Note: Code Execution is NOT available on gemini-2.5-flash-lite
*/
import { GoogleGenAI } from '@google/genai';
const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY! });
async function basicCodeExecution() {
console.log('=== Basic Code Execution Example ===\n');
const response = await ai.models.generateContent({
model: 'gemini-2.5-flash',
contents:
'What is the sum of the first 50 prime numbers? Generate and run code for the calculation, and make sure you get all 50.',
config: {
tools: [{ codeExecution: {} }],
},
});
// Parse response parts
console.log('Response parts:\n');
for (const part of response.candidates[0].content.parts) {
if (part.text) {
console.log('📝 Text:', part.text);
}
if (part.executableCode) {
console.log('\n💻 Generated Code:');
console.log(part.executableCode.code);
}
if (part.codeExecutionResult) {
console.log('\n✅ Execution Output:');
console.log(part.codeExecutionResult.output);
}
}
console.log('\n=== Basic Code Execution Complete ===');
}
async function dataAnalysisExample() {
console.log('\n=== Data Analysis Example ===\n');
const prompt = `
Analyze this sales data and calculate:
1. Total revenue
2. Average sale price
3. Best-selling month
4. Month with highest revenue
Use pandas or numpy for analysis.
Data (CSV format):
month,sales,revenue
Jan,150,45000
Feb,200,62000
Mar,175,53000
Apr,220,68000
May,190,58000
`;
const response = await ai.models.generateContent({
model: 'gemini-2.5-flash',
contents: prompt,
config: {
tools: [{ codeExecution: {} }],
},
});
for (const part of response.candidates[0].content.parts) {
if (part.text) {
console.log('📊 Analysis:', part.text);
}
if (part.executableCode) {
console.log('\n💻 Analysis Code:');
console.log(part.executableCode.code);
}
if (part.codeExecutionResult) {
console.log('\n📈 Results:');
console.log(part.codeExecutionResult.output);
}
}
console.log('\n=== Data Analysis Complete ===');
}
async function chartGenerationExample() {
console.log('\n=== Chart Generation Example ===\n');
const response = await ai.models.generateContent({
model: 'gemini-2.5-flash',
contents:
'Create a bar chart showing the distribution of prime numbers under 100 by their last digit. Generate the chart code and describe any patterns you see.',
config: {
tools: [{ codeExecution: {} }],
},
});
for (const part of response.candidates[0].content.parts) {
if (part.text) {
console.log('📊 Chart Description:', part.text);
}
if (part.executableCode) {
console.log('\n📉 Chart Code:');
console.log(part.executableCode.code);
}
if (part.codeExecutionResult) {
console.log('\n✓ Chart generated');
// Note: Image data would be in output
}
}
console.log('\n=== Chart Generation Complete ===');
}
async function chatWithCodeExecution() {
console.log('\n=== Chat with Code Execution Example ===\n');
const chat = await ai.chats.create({
model: 'gemini-2.5-flash',
config: {
tools: [{ codeExecution: {} }],
},
});
// First message
console.log('User: I have a math question for you.');
let response = await chat.sendMessage('I have a math question for you.');
console.log(`Assistant: ${response.text}\n`);
// Second message (will generate and execute code)
console.log('User: Calculate the Fibonacci sequence up to the 20th number and sum them.');
response = await chat.sendMessage(
'Calculate the Fibonacci sequence up to the 20th number and sum them.'
);
for (const part of response.candidates[0].content.parts) {
if (part.text) {
console.log('Assistant:', part.text);
}
if (part.executableCode) {
console.log('\nCode:');
console.log(part.executableCode.code);
}
if (part.codeExecutionResult) {
console.log('\nOutput:');
console.log(part.codeExecutionResult.output);
}
}
console.log('\n=== Chat with Code Execution Complete ===');
}
async function errorHandlingExample() {
console.log('\n=== Error Handling Example ===\n');
const response = await ai.models.generateContent({
model: 'gemini-2.5-flash',
contents: 'Write code that divides by zero and see what happens',
config: {
tools: [{ codeExecution: {} }],
},
});
for (const part of response.candidates[0].content.parts) {
if (part.codeExecutionResult) {
if (part.codeExecutionResult.outcome === 'OUTCOME_FAILED') {
console.error('❌ Code execution failed:');
console.error(part.codeExecutionResult.output);
} else {
console.log('✅ Success:');
console.log(part.codeExecutionResult.output);
}
}
}
console.log('\n=== Error Handling Example Complete ===');
}
// Run all examples
async function main() {
await basicCodeExecution();
await dataAnalysisExample();
await chartGenerationExample();
await chatWithCodeExecution();
await errorHandlingExample();
}
main().catch((error) => {
console.error('Error:', error.message);
process.exit(1);
});

View File

@@ -0,0 +1,278 @@
/**
* Google Gemini API - Combined Advanced Features Example
*
* Demonstrates how to use all Phase 2 advanced features together:
* - Context Caching (cost optimization)
* - Code Execution (computational tasks)
* - Grounding with Google Search (real-time information)
*
* Use Case: Financial analysis chatbot that:
* 1. Caches financial data and analysis instructions
* 2. Uses code execution for calculations and data analysis
* 3. Uses grounding for current market information
*
* Requirements:
* - @google/genai@1.27.0+
* - GEMINI_API_KEY environment variable
* - Google Cloud project (for grounding)
*/
import { GoogleGenAI } from '@google/genai';
const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY! });
async function financialAnalysisChatbot() {
console.log('=== Combined Advanced Features: Financial Analysis Chatbot ===\n');
// Step 1: Create cache with financial data and analysis instructions
console.log('Step 1: Creating cache with financial data...');
const financialData = `
Company Financial Data (Q1-Q4 2024):
Revenue:
Q1: $2.5M
Q2: $3.1M
Q3: $2.9M
Q4: $3.8M
Expenses:
Q1: $1.8M
Q2: $2.2M
Q3: $2.1M
Q4: $2.6M
Customer Acquisition:
Q1: 1,200 new customers
Q2: 1,500 new customers
Q3: 1,350 new customers
Q4: 1,800 new customers
`.trim();
const cache = await ai.caches.create({
model: 'gemini-2.5-flash-001', // Explicit version required for caching
config: {
displayName: 'financial-analysis-cache',
systemInstruction: `
You are a financial analyst chatbot. You have access to:
1. Company financial data (cached)
2. Code execution for calculations
3. Google Search for current market information
Provide detailed, accurate financial analysis.
`,
contents: financialData,
ttl: '3600s', // Cache for 1 hour
},
});
console.log(`✓ Cache created: ${cache.name}\n`);
// Step 2: Create chat with all advanced features enabled
console.log('Step 2: Creating chat with code execution and grounding...\n');
const chat = await ai.chats.create({
model: cache.name, // Use cached context
config: {
tools: [
{ codeExecution: {} }, // Enable code execution
{ googleSearch: {} }, // Enable grounding
],
},
});
// Query 1: Analysis requiring code execution (uses cache)
console.log('===========================================');
console.log('Query 1: Calculate year-over-year growth');
console.log('===========================================\n');
let response = await chat.sendMessage(`
Calculate the following for 2024:
1. Total annual revenue
2. Total annual expenses
3. Net profit
4. Profit margin percentage
5. Quarter-over-quarter revenue growth rates
Use code to perform these calculations.
`);
console.log('📊 Financial Analysis:\n');
for (const part of response.candidates[0].content.parts) {
if (part.text) {
console.log(part.text);
}
if (part.executableCode) {
console.log('\n💻 Calculations Code:');
console.log(part.executableCode.code);
}
if (part.codeExecutionResult) {
console.log('\n📈 Results:');
console.log(part.codeExecutionResult.output);
}
}
// Query 2: Current market information (uses grounding)
console.log('\n\n===========================================');
console.log('Query 2: Compare with industry benchmarks');
console.log('===========================================\n');
response = await chat.sendMessage(`
What are the current industry benchmarks for SaaS companies in terms of:
1. Profit margins
2. Customer acquisition cost trends
3. Growth rate expectations
Use current market data to provide context.
`);
console.log('📰 Market Context:\n');
console.log(response.text);
if (response.candidates[0].groundingMetadata) {
console.log('\n✓ Used current market data from:');
const sources = response.candidates[0].groundingMetadata.webPages || [];
sources.slice(0, 3).forEach((source, i) => {
console.log(`${i + 1}. ${source.title}`);
console.log(` ${source.url}`);
});
}
// Query 3: Combined analysis (uses cache + code + grounding)
console.log('\n\n===========================================');
console.log('Query 3: Comprehensive recommendation');
console.log('===========================================\n');
response = await chat.sendMessage(`
Based on:
1. Our company's financial performance (from cached data)
2. Calculated growth metrics (using code execution)
3. Current industry trends (from search)
Provide a comprehensive recommendation for Q1 2025 strategy.
`);
console.log('💡 Strategic Recommendation:\n');
console.log(response.text);
// Show which features were used
console.log('\n\n📋 Features Used in This Query:');
let featuresUsed = [];
// Check for code execution
const hasCode = response.candidates[0].content.parts.some(
(part) => part.executableCode
);
if (hasCode) featuresUsed.push('✓ Code Execution');
// Check for grounding
if (response.candidates[0].groundingMetadata) {
featuresUsed.push('✓ Google Search Grounding');
}
// Cache is always used when we use cache.name as model
featuresUsed.push('✓ Context Caching');
featuresUsed.forEach((feature) => console.log(feature));
// Clean up
console.log('\n\nStep 3: Cleaning up...');
await ai.caches.delete({ name: cache.name });
console.log('✓ Cache deleted');
console.log('\n=== Financial Analysis Chatbot Complete ===');
}
async function researchAssistant() {
console.log('\n\n=== Combined Advanced Features: Research Assistant ===\n');
// Create cache with research paper
console.log('Step 1: Caching research paper...');
const researchPaper = `
Title: Climate Change Impact on Arctic Ecosystems (2024)
Abstract:
This study examines the effects of climate change on Arctic ecosystems
over the past decade...
[Imagine full research paper content here]
Key Findings:
- Temperature increase of 2.3°C in Arctic regions
- 15% reduction in sea ice coverage
- Migration pattern changes in polar species
- etc.
`.trim();
const cache = await ai.caches.create({
model: 'gemini-2.5-flash-001',
config: {
displayName: 'research-paper-cache',
systemInstruction: 'You are a research assistant. Analyze papers and provide insights.',
contents: researchPaper,
ttl: '1800s', // 30 minutes
},
});
console.log(`✓ Cache created\n`);
// Create chat with all tools
const chat = await ai.chats.create({
model: cache.name,
config: {
tools: [{ codeExecution: {} }, { googleSearch: {} }],
},
});
// Query combining all features
console.log('Query: Comprehensive climate analysis\n');
const response = await chat.sendMessage(`
1. Calculate the average temperature change per year from the data in the paper (use code)
2. Find the latest climate predictions for the next decade (use search)
3. Synthesize findings from both sources into a summary
`);
console.log('Response:\n');
for (const part of response.candidates[0].content.parts) {
if (part.text) console.log(part.text);
if (part.executableCode) {
console.log('\nCode:');
console.log(part.executableCode.code);
}
if (part.codeExecutionResult) {
console.log('\nResults:');
console.log(part.codeExecutionResult.output);
}
}
if (response.candidates[0].groundingMetadata) {
console.log('\nSources:');
const sources = response.candidates[0].groundingMetadata.webPages || [];
sources.slice(0, 2).forEach((s) => console.log(`- ${s.title}`));
}
// Clean up
await ai.caches.delete({ name: cache.name });
console.log('\n=== Research Assistant Complete ===');
}
// Run examples
async function main() {
await financialAnalysisChatbot();
await researchAssistant();
console.log('\n\n✅ All advanced features demonstrated!');
console.log('\nKey Takeaways:');
console.log('- Context Caching: Saves costs by reusing large context');
console.log('- Code Execution: Enables computational analysis');
console.log('- Grounding: Provides real-time, fact-checked information');
console.log('- Combined: Creates powerful, cost-effective AI applications');
}
main().catch((error) => {
console.error('Error:', error.message);
process.exit(1);
});

View File

@@ -0,0 +1,154 @@
/**
* Google Gemini API - Context Caching Example
*
* Demonstrates how to create and use context caching to reduce costs by up to 90%
* when using the same large context (documents, videos, system instructions) repeatedly.
*
* Features:
* - Create cache with large document
* - Use cache for multiple queries
* - Update cache TTL
* - List and delete caches
*
* Requirements:
* - @google/genai@1.27.0+
* - GEMINI_API_KEY environment variable
*
* Note: You must use explicit model version suffixes like 'gemini-2.5-flash-001',
* not just 'gemini-2.5-flash' when creating caches.
*/
import { GoogleGenAI } from '@google/genai';
import fs from 'fs';
const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY! });
async function contextCachingExample() {
console.log('=== Context Caching Example ===\n');
// Example: Large document content
const largeDocument = `
This is a large legal document that will be analyzed multiple times.
Article 1: ...
Article 2: ...
... (imagine thousands of lines of legal text)
`.trim();
// 1. Create a cache with large content
console.log('1. Creating cache...');
const cache = await ai.caches.create({
model: 'gemini-2.5-flash-001', // Must use explicit version suffix!
config: {
displayName: 'legal-doc-cache',
systemInstruction: 'You are an expert legal analyst. Analyze documents and provide clear summaries.',
contents: largeDocument,
ttl: '3600s', // Cache for 1 hour
},
});
console.log(`✓ Cache created: ${cache.name}`);
console.log(` Display name: ${cache.displayName}`);
console.log(` Expires at: ${cache.expireTime}\n`);
// 2. Use cache for multiple queries (saves tokens!)
console.log('2. Using cache for first query...');
const response1 = await ai.models.generateContent({
model: cache.name, // Use cache name as model
contents: 'Summarize the key points of Article 1',
});
console.log(`Response: ${response1.text}\n`);
console.log('3. Using cache for second query...');
const response2 = await ai.models.generateContent({
model: cache.name,
contents: 'What are the legal implications of Article 2?',
});
console.log(`Response: ${response2.text}\n`);
// 3. Update cache TTL to extend lifetime
console.log('4. Extending cache TTL to 2 hours...');
await ai.caches.update({
name: cache.name,
config: {
ttl: '7200s', // Extend to 2 hours
},
});
console.log('✓ Cache TTL updated\n');
// 4. List all caches
console.log('5. Listing all caches...');
const caches = await ai.caches.list();
caches.forEach((c) => {
console.log(` - ${c.displayName}: ${c.name}`);
});
console.log();
// 5. Delete cache when done
console.log('6. Deleting cache...');
await ai.caches.delete({ name: cache.name });
console.log('✓ Cache deleted\n');
console.log('=== Context Caching Complete ===');
}
async function videoCachingExample() {
console.log('\n=== Video Caching Example ===\n');
// Upload a video file
console.log('1. Uploading video file...');
const videoFile = await ai.files.upload({
file: fs.createReadStream('./example-video.mp4'),
});
console.log('2. Waiting for video processing...');
let processedFile = videoFile;
while (processedFile.state.name === 'PROCESSING') {
await new Promise((resolve) => setTimeout(resolve, 2000));
processedFile = await ai.files.get({ name: videoFile.name });
}
console.log(`✓ Video processed: ${processedFile.uri}\n`);
// Create cache with video
console.log('3. Creating cache with video...');
const cache = await ai.caches.create({
model: 'gemini-2.5-flash-001',
config: {
displayName: 'video-analysis-cache',
systemInstruction: 'You are an expert video analyzer.',
contents: [processedFile],
ttl: '300s', // 5 minutes
},
});
console.log(`✓ Cache created: ${cache.name}\n`);
// Use cache for multiple video queries
console.log('4. Query 1: What happens in the first minute?');
const response1 = await ai.models.generateContent({
model: cache.name,
contents: 'What happens in the first minute?',
});
console.log(`Response: ${response1.text}\n`);
console.log('5. Query 2: Describe the main characters');
const response2 = await ai.models.generateContent({
model: cache.name,
contents: 'Describe the main characters',
});
console.log(`Response: ${response2.text}\n`);
// Clean up
await ai.caches.delete({ name: cache.name });
await ai.files.delete({ name: videoFile.name });
console.log('✓ Cache and video file deleted');
console.log('\n=== Video Caching Complete ===');
}
// Run examples
contextCachingExample()
.then(() => videoCachingExample())
.catch((error) => {
console.error('Error:', error.message);
process.exit(1);
});

View File

@@ -0,0 +1,156 @@
/**
* Basic Function Calling with Gemini API
*
* Demonstrates:
* - Defining function declarations (tools)
* - Detecting when model wants to call a function
* - Executing functions and returning results
* - Multi-turn function calling workflow
*
* Prerequisites:
* - npm install @google/genai@1.27.0
* - export GEMINI_API_KEY="..."
*
* ⚠️ IMPORTANT: Gemini 2.5 Flash-Lite does NOT support function calling!
* Use gemini-2.5-flash or gemini-2.5-pro
*/
import { GoogleGenAI, FunctionCallingConfigMode } from '@google/genai';
async function main() {
const ai = new GoogleGenAI({
apiKey: process.env.GEMINI_API_KEY,
});
try {
// Step 1: Define function declarations
const getCurrentWeather = {
name: 'get_current_weather',
description: 'Get the current weather for a specific location',
parametersJsonSchema: {
type: 'object',
properties: {
location: {
type: 'string',
description: 'The city name, e.g. San Francisco, Tokyo, London'
},
unit: {
type: 'string',
enum: ['celsius', 'fahrenheit'],
description: 'Temperature unit'
}
},
required: ['location']
}
};
// Step 2: Make request with tools
console.log('User: What\'s the weather in Tokyo?\n');
const response1 = await ai.models.generateContent({
model: 'gemini-2.5-flash', // ⚠️ NOT flash-lite!
contents: 'What\'s the weather in Tokyo?',
config: {
tools: [
{ functionDeclarations: [getCurrentWeather] }
]
}
});
// Step 3: Check if model wants to call a function
const functionCall = response1.candidates[0]?.content?.parts?.find(
part => part.functionCall
)?.functionCall;
if (!functionCall) {
console.log('Model response (no function call):', response1.text);
return;
}
console.log('Model wants to call function:');
console.log('- Function name:', functionCall.name);
console.log('- Arguments:', JSON.stringify(functionCall.args, null, 2));
console.log('');
// Step 4: Execute the function (your implementation)
console.log('Executing function...\n');
const weatherData = await getCurrentWeatherImpl(
functionCall.args.location,
functionCall.args.unit || 'celsius'
);
console.log('Function result:', JSON.stringify(weatherData, null, 2));
console.log('');
// Step 5: Send function result back to model
const response2 = await ai.models.generateContent({
model: 'gemini-2.5-flash',
contents: [
{ parts: [{ text: 'What\'s the weather in Tokyo?' }] },
response1.candidates[0].content, // Original assistant response with function call
{
parts: [
{
functionResponse: {
name: functionCall.name,
response: weatherData
}
}
]
}
],
config: {
tools: [
{ functionDeclarations: [getCurrentWeather] }
]
}
});
console.log('Model final response:');
console.log(response2.text);
} catch (error: any) {
console.error('Error:', error.message);
}
}
/**
* Mock implementation of weather API
* Replace with actual API call in production
*/
async function getCurrentWeatherImpl(location: string, unit: string) {
// Simulate API call
await new Promise(resolve => setTimeout(resolve, 500));
// Mock data
return {
location,
temperature: unit === 'celsius' ? 22 : 72,
unit,
conditions: 'Partly cloudy',
humidity: 65,
windSpeed: 10
};
}
/**
* Function Calling Modes:
*
* AUTO (default): Model decides whether to call functions
* ANY: Force model to call at least one function
* NONE: Disable function calling for this request
*
* Example with mode:
*
* config: {
* tools: [...],
* toolConfig: {
* functionCallingConfig: {
* mode: FunctionCallingConfigMode.ANY,
* allowedFunctionNames: ['get_current_weather']
* }
* }
* }
*/
main();

View File

@@ -0,0 +1,177 @@
/**
* Parallel Function Calling with Gemini API
*
* Demonstrates:
* - Multiple independent function calls in one request
* - Handling multiple function call responses
* - Compositional (sequential) vs parallel execution
* - Complex multi-step workflows
*
* Prerequisites:
* - npm install @google/genai@1.27.0
* - export GEMINI_API_KEY="..."
*
* ⚠️ IMPORTANT: Only gemini-2.5-flash and gemini-2.5-pro support function calling
*/
import { GoogleGenAI } from '@google/genai';
async function main() {
const ai = new GoogleGenAI({
apiKey: process.env.GEMINI_API_KEY,
});
try {
// Define multiple functions
const getWeather = {
name: 'get_weather',
description: 'Get current weather for a location',
parametersJsonSchema: {
type: 'object',
properties: {
location: { type: 'string', description: 'City name' }
},
required: ['location']
}
};
const getPopulation = {
name: 'get_population',
description: 'Get population of a city',
parametersJsonSchema: {
type: 'object',
properties: {
city: { type: 'string', description: 'City name' }
},
required: ['city']
}
};
const getTimezone = {
name: 'get_timezone',
description: 'Get timezone information for a location',
parametersJsonSchema: {
type: 'object',
properties: {
location: { type: 'string', description: 'City name' }
},
required: ['location']
}
};
// Make request that requires multiple independent functions
console.log('User: What is the weather, population, and timezone of Tokyo?\n');
const response1 = await ai.models.generateContent({
model: 'gemini-2.5-flash',
contents: 'What is the weather, population, and timezone of Tokyo?',
config: {
tools: [
{ functionDeclarations: [getWeather, getPopulation, getTimezone] }
]
}
});
// Extract all function calls
const functionCalls = response1.candidates[0]?.content?.parts?.filter(
part => part.functionCall
) || [];
console.log(`Model wants to call ${functionCalls.length} functions in parallel:\n`);
// Execute all functions in parallel
const functionResponses = await Promise.all(
functionCalls.map(async (part) => {
const functionCall = part.functionCall!;
console.log(`- Calling ${functionCall.name} with args:`, functionCall.args);
// Execute function
let result;
if (functionCall.name === 'get_weather') {
result = await getWeatherImpl(functionCall.args.location);
} else if (functionCall.name === 'get_population') {
result = await getPopulationImpl(functionCall.args.city);
} else if (functionCall.name === 'get_timezone') {
result = await getTimezoneImpl(functionCall.args.location);
}
return {
functionResponse: {
name: functionCall.name,
response: result
}
};
})
);
console.log('\nAll functions executed.\n');
// Send all function results back to model
const response2 = await ai.models.generateContent({
model: 'gemini-2.5-flash',
contents: [
{ parts: [{ text: 'What is the weather, population, and timezone of Tokyo?' }] },
response1.candidates[0].content,
{ parts: functionResponses }
],
config: {
tools: [
{ functionDeclarations: [getWeather, getPopulation, getTimezone] }
]
}
});
console.log('Model final response:');
console.log(response2.text);
} catch (error: any) {
console.error('Error:', error.message);
}
}
/**
* Mock function implementations
*/
async function getWeatherImpl(location: string) {
await new Promise(resolve => setTimeout(resolve, 300));
return {
location,
temperature: 22,
conditions: 'Sunny',
humidity: 60
};
}
async function getPopulationImpl(city: string) {
await new Promise(resolve => setTimeout(resolve, 300));
return {
city,
population: 13960000,
metropolitan: 37400000
};
}
async function getTimezoneImpl(location: string) {
await new Promise(resolve => setTimeout(resolve, 300));
return {
location,
timezone: 'Asia/Tokyo',
offset: '+09:00'
};
}
/**
* Parallel vs Compositional Function Calling:
*
* PARALLEL: Functions are independent and can run simultaneously
* - Example: "What is the weather AND population of Tokyo?"
* - Model calls get_weather() and get_population() together
*
* COMPOSITIONAL: Functions depend on each other (sequential)
* - Example: "What is the weather at my current location?"
* - Model first calls get_current_location(), then uses result for get_weather()
*
* Gemini automatically determines which pattern to use based on dependencies.
*/
main();

View File

@@ -0,0 +1,271 @@
/**
* Google Gemini API - Grounding with Google Search Example
*
* Demonstrates how to enable grounding to connect the model to real-time
* web information, reducing hallucinations and providing up-to-date responses
* with citations.
*
* Features:
* - Basic grounding with Google Search (Gemini 2.5)
* - Dynamic retrieval with threshold (Gemini 1.5)
* - Chat with grounding
* - Combining grounding with function calling
* - Checking grounding metadata and citations
*
* Requirements:
* - @google/genai@1.27.0+
* - GEMINI_API_KEY environment variable
* - Google Cloud project (grounding requires GCP project, not just API key)
*
* Note: Use `googleSearch` for Gemini 2.5 models (recommended)
* Use `googleSearchRetrieval` for Gemini 1.5 models (legacy)
*/
import { GoogleGenAI, DynamicRetrievalConfigMode } from '@google/genai';
const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY! });
async function basicGrounding() {
console.log('=== Basic Grounding Example (Gemini 2.5) ===\n');
const response = await ai.models.generateContent({
model: 'gemini-2.5-flash',
contents: 'Who won the euro 2024?',
config: {
tools: [{ googleSearch: {} }],
},
});
console.log('Response:', response.text);
// Check if grounding was used
if (response.candidates[0].groundingMetadata) {
console.log('\n✓ Search was performed!');
console.log('\nGrounding Metadata:');
console.log(JSON.stringify(response.candidates[0].groundingMetadata, null, 2));
} else {
console.log('\n✓ Model answered from its own knowledge (no search needed)');
}
console.log('\n=== Basic Grounding Complete ===');
}
async function dynamicRetrievalExample() {
console.log('\n=== Dynamic Retrieval Example (Gemini 1.5) ===\n');
const response = await ai.models.generateContent({
model: 'gemini-1.5-flash',
contents: 'Who won the euro 2024?',
config: {
tools: [
{
googleSearchRetrieval: {
dynamicRetrievalConfig: {
mode: DynamicRetrievalConfigMode.MODE_DYNAMIC,
dynamicThreshold: 0.7, // Search only if confidence < 70%
},
},
},
],
},
});
console.log('Response:', response.text);
if (response.candidates[0].groundingMetadata) {
console.log('\n✓ Search performed (confidence < 70%)');
} else {
console.log('\n✓ Answered from knowledge (confidence >= 70%)');
}
console.log('\n=== Dynamic Retrieval Complete ===');
}
async function chatWithGrounding() {
console.log('\n=== Chat with Grounding Example ===\n');
const chat = await ai.chats.create({
model: 'gemini-2.5-flash',
config: {
tools: [{ googleSearch: {} }],
},
});
// First message
console.log('User: What are the latest developments in quantum computing?');
let response = await chat.sendMessage('What are the latest developments in quantum computing?');
console.log(`\nAssistant: ${response.text}`);
// Check and display sources
if (response.candidates[0].groundingMetadata) {
const sources = response.candidates[0].groundingMetadata.webPages || [];
console.log(`\n📚 Sources used: ${sources.length}`);
sources.forEach((source, i) => {
console.log(`${i + 1}. ${source.title}`);
console.log(` ${source.url}`);
});
}
// Follow-up question
console.log('\n\nUser: Which company made the biggest breakthrough?');
response = await chat.sendMessage('Which company made the biggest breakthrough?');
console.log(`\nAssistant: ${response.text}`);
if (response.candidates[0].groundingMetadata) {
const sources = response.candidates[0].groundingMetadata.webPages || [];
console.log(`\n📚 Sources used: ${sources.length}`);
sources.forEach((source, i) => {
console.log(`${i + 1}. ${source.title} - ${source.url}`);
});
}
console.log('\n=== Chat with Grounding Complete ===');
}
async function groundingWithFunctionCalling() {
console.log('\n=== Grounding + Function Calling Example ===\n');
// Define weather function
const weatherFunction = {
name: 'get_current_weather',
description: 'Get current weather for a location',
parametersJsonSchema: {
type: 'object',
properties: {
location: { type: 'string', description: 'City name' },
unit: { type: 'string', enum: ['celsius', 'fahrenheit'] },
},
required: ['location'],
},
};
const response = await ai.models.generateContent({
model: 'gemini-2.5-flash',
contents: 'What is the weather like in the city that won Euro 2024?',
config: {
tools: [{ googleSearch: {} }, { functionDeclarations: [weatherFunction] }],
},
});
console.log('User query: What is the weather like in the city that won Euro 2024?');
console.log('\nModel will:');
console.log('1. Use Google Search to find Euro 2024 winner');
console.log('2. Call get_current_weather function with the city');
console.log('3. Combine both results in response\n');
// Check for function calls
const functionCall = response.candidates[0].content.parts.find((part) => part.functionCall);
if (functionCall) {
console.log('✓ Function call detected:');
console.log(` Function: ${functionCall.functionCall?.name}`);
console.log(` Arguments:`, functionCall.functionCall?.args);
}
if (response.candidates[0].groundingMetadata) {
console.log('\n✓ Grounding was used');
const sources = response.candidates[0].groundingMetadata.webPages || [];
console.log(` Sources: ${sources.length} web pages`);
}
console.log('\n=== Grounding + Function Calling Complete ===');
}
async function checkingGroundingUsage() {
console.log('\n=== Checking Grounding Usage Example ===\n');
// Query that doesn't need search
console.log('Query 1: What is 2+2? (Should NOT need search)');
const response1 = await ai.models.generateContent({
model: 'gemini-2.5-flash',
contents: 'What is 2+2?',
config: {
tools: [{ googleSearch: {} }],
},
});
console.log(`Answer: ${response1.text}`);
console.log(
`Grounding used: ${response1.candidates[0].groundingMetadata ? 'YES' : 'NO'}\n`
);
// Query that needs current information
console.log('Query 2: What happened in the news today? (Should need search)');
const response2 = await ai.models.generateContent({
model: 'gemini-2.5-flash',
contents: 'What are the top news headlines today?',
config: {
tools: [{ googleSearch: {} }],
},
});
console.log(`Answer: ${response2.text}`);
console.log(`Grounding used: ${response2.candidates[0].groundingMetadata ? 'YES' : 'NO'}`);
if (response2.candidates[0].groundingMetadata) {
console.log('\nSearch queries performed:');
const queries = response2.candidates[0].groundingMetadata.searchQueries || [];
queries.forEach((q, i) => {
console.log(`${i + 1}. ${q.text || q}`);
});
}
console.log('\n=== Checking Grounding Usage Complete ===');
}
async function citationsExample() {
console.log('\n=== Citations Example ===\n');
const response = await ai.models.generateContent({
model: 'gemini-2.5-flash',
contents: 'Tell me about the recent Mars rover discoveries',
config: {
tools: [{ googleSearch: {} }],
},
});
console.log('Response:', response.text);
if (response.candidates[0].groundingMetadata) {
const metadata = response.candidates[0].groundingMetadata;
console.log('\n📚 Web Pages:');
const webPages = metadata.webPages || [];
webPages.forEach((page, i) => {
console.log(`\n${i + 1}. ${page.title}`);
console.log(` URL: ${page.url}`);
if (page.snippet) {
console.log(` Snippet: ${page.snippet.substring(0, 100)}...`);
}
});
console.log('\n🔗 Citations:');
const citations = metadata.citations || [];
citations.forEach((citation, i) => {
console.log(`\n${i + 1}. Position: ${citation.startIndex}-${citation.endIndex}`);
console.log(` Source: ${citation.uri}`);
});
}
console.log('\n=== Citations Example Complete ===');
}
// Run all examples
async function main() {
await basicGrounding();
await dynamicRetrievalExample();
await chatWithGrounding();
await groundingWithFunctionCalling();
await checkingGroundingUsage();
await citationsExample();
}
main().catch((error) => {
console.error('Error:', error.message);
if (error.message.includes('Google Cloud project')) {
console.error(
'\nNote: Grounding requires a Google Cloud project, not just an API key.'
);
}
process.exit(1);
});

View File

@@ -0,0 +1,117 @@
/**
* Multimodal Image Understanding with Gemini API
*
* Demonstrates:
* - Image analysis with vision capabilities
* - Base64 encoding of images
* - Combining text and image inputs
* - Multiple images in one request
*
* Prerequisites:
* - npm install @google/genai@1.27.0
* - export GEMINI_API_KEY="..."
*/
import { GoogleGenAI } from '@google/genai';
import fs from 'fs';
async function main() {
const ai = new GoogleGenAI({
apiKey: process.env.GEMINI_API_KEY,
});
try {
// Example 1: Analyze a single image
console.log('Example 1: Analyze Single Image\n');
// Load image from file
const imagePath = '/path/to/image.jpg'; // Replace with actual path
const imageData = fs.readFileSync(imagePath);
const base64Image = imageData.toString('base64');
const response1 = await ai.models.generateContent({
model: 'gemini-2.5-flash',
contents: [
{
parts: [
{ text: 'Describe this image in detail. What objects, people, or scenes do you see?' },
{
inlineData: {
data: base64Image,
mimeType: 'image/jpeg' // or 'image/png', 'image/webp', etc.
}
}
]
}
]
});
console.log(response1.text);
console.log('\n---\n');
// Example 2: Compare two images
console.log('Example 2: Compare Two Images\n');
const imagePath2 = '/path/to/image2.jpg'; // Replace with actual path
const imageData2 = fs.readFileSync(imagePath2);
const base64Image2 = imageData2.toString('base64');
const response2 = await ai.models.generateContent({
model: 'gemini-2.5-flash',
contents: [
{
parts: [
{ text: 'Compare these two images. What are the similarities and differences?' },
{ inlineData: { data: base64Image, mimeType: 'image/jpeg' } },
{ inlineData: { data: base64Image2, mimeType: 'image/jpeg' } }
]
}
]
});
console.log(response2.text);
console.log('\n---\n');
// Example 3: Specific questions about image
console.log('Example 3: Specific Questions\n');
const response3 = await ai.models.generateContent({
model: 'gemini-2.5-flash',
contents: [
{
parts: [
{ text: 'How many people are in this image? What are they wearing?' },
{ inlineData: { data: base64Image, mimeType: 'image/jpeg' } }
]
}
]
});
console.log(response3.text);
} catch (error: any) {
console.error('Error:', error.message);
if (error.message.includes('ENOENT')) {
console.error('\n⚠ Image file not found. Update the imagePath variable with a valid path.');
}
}
}
/**
* Supported image formats:
* - JPEG (.jpg, .jpeg)
* - PNG (.png)
* - WebP (.webp)
* - HEIC (.heic)
* - HEIF (.heif)
*
* Max size: 20MB per image
*
* Tips:
* - Use specific, detailed prompts for better results
* - You can analyze multiple images in one request
* - gemini-2.5-flash and gemini-2.5-pro both support vision
*/
main();

View File

@@ -0,0 +1,152 @@
/**
* Multimodal Video and Audio Understanding with Gemini API
*
* Demonstrates:
* - Video analysis (what happens in the video)
* - Audio transcription and understanding
* - PDF document parsing
* - Combining multiple modalities
*
* Prerequisites:
* - npm install @google/genai@1.27.0
* - export GEMINI_API_KEY="..."
*/
import { GoogleGenAI } from '@google/genai';
import fs from 'fs';
async function main() {
const ai = new GoogleGenAI({
apiKey: process.env.GEMINI_API_KEY,
});
try {
// Example 1: Analyze video
console.log('Example 1: Video Analysis\n');
const videoPath = '/path/to/video.mp4'; // Replace with actual path
const videoData = fs.readFileSync(videoPath);
const base64Video = videoData.toString('base64');
const response1 = await ai.models.generateContent({
model: 'gemini-2.5-flash',
contents: [
{
parts: [
{ text: 'Describe what happens in this video. Summarize the key events.' },
{
inlineData: {
data: base64Video,
mimeType: 'video/mp4'
}
}
]
}
]
});
console.log(response1.text);
console.log('\n---\n');
// Example 2: Transcribe and analyze audio
console.log('Example 2: Audio Transcription and Analysis\n');
const audioPath = '/path/to/audio.mp3'; // Replace with actual path
const audioData = fs.readFileSync(audioPath);
const base64Audio = audioData.toString('base64');
const response2 = await ai.models.generateContent({
model: 'gemini-2.5-flash',
contents: [
{
parts: [
{ text: 'Transcribe this audio and provide a summary of the main points discussed.' },
{
inlineData: {
data: base64Audio,
mimeType: 'audio/mp3'
}
}
]
}
]
});
console.log(response2.text);
console.log('\n---\n');
// Example 3: Parse PDF document
console.log('Example 3: PDF Document Parsing\n');
const pdfPath = '/path/to/document.pdf'; // Replace with actual path
const pdfData = fs.readFileSync(pdfPath);
const base64Pdf = pdfData.toString('base64');
const response3 = await ai.models.generateContent({
model: 'gemini-2.5-flash',
contents: [
{
parts: [
{ text: 'Summarize the key points in this PDF document. Extract any important data or conclusions.' },
{
inlineData: {
data: base64Pdf,
mimeType: 'application/pdf'
}
}
]
}
]
});
console.log(response3.text);
console.log('\n---\n');
// Example 4: Combine multiple modalities
console.log('Example 4: Multiple Modalities (Video + Text Questions)\n');
const response4 = await ai.models.generateContent({
model: 'gemini-2.5-flash',
contents: [
{
parts: [
{ text: 'Based on this video, answer these questions:\n1. How many people appear?\n2. What is the main activity?\n3. Where does this take place?' },
{ inlineData: { data: base64Video, mimeType: 'video/mp4' } }
]
}
]
});
console.log(response4.text);
} catch (error: any) {
console.error('Error:', error.message);
if (error.message.includes('ENOENT')) {
console.error('\n⚠ File not found. Update the file path variables with valid paths.');
}
}
}
/**
* Supported Video Formats:
* - MP4, MPEG, MOV, AVI, FLV, MPG, WebM, WMV
* Max size: 2GB (use File API for larger - Phase 2)
* Max length (inline): 2 minutes
*
* Supported Audio Formats:
* - MP3, WAV, FLAC, AAC, OGG, OPUS
* Max size: 20MB
*
* PDF:
* - Max size: 30MB
* - Text-based PDFs work best
* - Scanned images may have lower accuracy
*
* Tips:
* - For videos > 2 minutes, use the File API (Phase 2)
* - Specific prompts yield better results
* - You can combine text, images, video, audio, and PDFs in one request
*/
main();

22
templates/package.json Normal file
View File

@@ -0,0 +1,22 @@
{
"name": "gemini-api-example",
"version": "1.0.0",
"description": "Google Gemini API examples using @google/genai SDK",
"type": "module",
"scripts": {
"dev": "tsx watch src/index.ts",
"start": "node dist/index.js",
"build": "tsc"
},
"dependencies": {
"@google/genai": "^1.27.0"
},
"devDependencies": {
"@types/node": "^20.0.0",
"tsx": "^4.7.0",
"typescript": "^5.3.0"
},
"engines": {
"node": ">=18.0.0"
}
}

View File

@@ -0,0 +1,81 @@
/**
* Streaming Text Generation with Gemini API (Node.js SDK)
*
* Demonstrates:
* - Streaming responses with async iteration
* - Real-time token delivery for better UX
* - Handling partial chunks
* - Multi-turn chat with streaming
*
* Prerequisites:
* - npm install @google/genai@1.27.0
* - export GEMINI_API_KEY="..."
*/
import { GoogleGenAI } from '@google/genai';
async function main() {
const ai = new GoogleGenAI({
apiKey: process.env.GEMINI_API_KEY,
});
try {
// Example 1: Basic streaming
console.log('Example 1: Basic Streaming\n');
console.log('Prompt: Write a 200-word story about time travel\n');
const response = await ai.models.generateContentStream({
model: 'gemini-2.5-flash',
contents: 'Write a 200-word story about time travel'
});
// Stream chunks as they arrive
for await (const chunk of response) {
// Each chunk may contain partial text
if (chunk.text) {
process.stdout.write(chunk.text);
}
}
console.log('\n\n---\n');
// Example 2: Streaming with chat
console.log('Example 2: Streaming Chat\n');
const chat = await ai.models.createChat({
model: 'gemini-2.5-flash',
systemInstruction: 'You are a helpful coding assistant.'
});
// First turn with streaming
console.log('User: What is TypeScript?\n');
const response1 = await chat.sendMessageStream('What is TypeScript?');
console.log('Assistant: ');
for await (const chunk of response1) {
process.stdout.write(chunk.text);
}
console.log('\n\n');
// Second turn with streaming (context maintained)
console.log('User: How do I install it?\n');
const response2 = await chat.sendMessageStream('How do I install it?');
console.log('Assistant: ');
for await (const chunk of response2) {
process.stdout.write(chunk.text);
}
console.log('\n\n');
// Get full chat history
const history = chat.getHistory();
console.log(`Total messages in history: ${history.length}`);
} catch (error: any) {
console.error('Error:', error.message);
}
}
main();

View File

@@ -0,0 +1,190 @@
/**
* Streaming Text Generation with Gemini API (Fetch - SSE Parsing)
*
* Demonstrates:
* - Server-Sent Events (SSE) parsing with fetch
* - Manual stream handling for Cloudflare Workers or edge runtimes
* - Buffer management for incomplete chunks
* - Error handling during streaming
*
* Prerequisites:
* - Set GEMINI_API_KEY environment variable
*/
interface Env {
GEMINI_API_KEY: string;
}
/**
* Example for Cloudflare Workers with streaming response
*/
export default {
async fetch(request: Request, env: Env): Promise<Response> {
// Create a TransformStream to stream tokens to the client
const { readable, writable } = new TransformStream();
const writer = writable.getWriter();
const encoder = new TextEncoder();
// Start streaming in the background
(async () => {
try {
const response = await fetch(
`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:streamGenerateContent`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-goog-api-key': env.GEMINI_API_KEY,
},
body: JSON.stringify({
contents: [
{
parts: [
{ text: 'Write a 200-word story about time travel' }
]
}
]
}),
}
);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
if (!response.body) {
throw new Error('Response body is null');
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) {
break;
}
// Append new data to buffer
buffer += decoder.decode(value, { stream: true });
// Split by newlines (SSE format)
const lines = buffer.split('\n');
// Keep the last incomplete line in buffer
buffer = lines.pop() || '';
for (const line of lines) {
// Skip empty lines and metadata
if (line.trim() === '' || line.startsWith('data: [DONE]')) {
continue;
}
// Parse SSE format: "data: {json}"
if (!line.startsWith('data: ')) {
continue;
}
try {
const jsonData = JSON.parse(line.slice(6)); // Remove "data: " prefix
const text = jsonData.candidates[0]?.content?.parts[0]?.text;
if (text) {
// Write chunk to client
await writer.write(encoder.encode(text));
}
} catch (e) {
// Skip invalid JSON chunks
console.error('Failed to parse chunk:', e);
}
}
}
// Close the stream
await writer.close();
} catch (error: any) {
await writer.write(encoder.encode(`\n\nError: ${error.message}`));
await writer.close();
}
})();
// Return streaming response immediately
return new Response(readable, {
headers: {
'Content-Type': 'text/plain; charset=utf-8',
'Transfer-Encoding': 'chunked',
},
});
}
};
/**
* Example for Node.js/Standalone
*/
async function mainNodeJS() {
const GEMINI_API_KEY = process.env.GEMINI_API_KEY;
if (!GEMINI_API_KEY) {
throw new Error('GEMINI_API_KEY environment variable not set');
}
const response = await fetch(
`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:streamGenerateContent`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-goog-api-key': GEMINI_API_KEY,
},
body: JSON.stringify({
contents: [
{
parts: [
{ text: 'Write a 200-word story about time travel' }
]
}
]
}),
}
);
if (!response.body) {
throw new Error('Response body is null');
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (line.trim() === '' || line.startsWith('data: [DONE]')) continue;
if (!line.startsWith('data: ')) continue;
try {
const data = JSON.parse(line.slice(6));
const text = data.candidates[0]?.content?.parts[0]?.text;
if (text) {
process.stdout.write(text);
}
} catch (e) {
// Skip invalid JSON
}
}
}
console.log('\n');
}
// Uncomment to run in Node.js
// mainNodeJS();

View File

@@ -0,0 +1,61 @@
/**
* Basic Text Generation with Gemini API (Node.js SDK)
*
* Demonstrates:
* - Installing @google/genai (CORRECT SDK, NOT @google/generative-ai)
* - Basic text generation with gemini-2.5-flash
* - Accessing response text
* - Error handling
*
* Prerequisites:
* - npm install @google/genai@1.27.0
* - export GEMINI_API_KEY="..."
*/
import { GoogleGenAI } from '@google/genai';
async function main() {
// Initialize the Google GenAI client
// ⚠️ IMPORTANT: Use @google/genai, NOT @google/generative-ai (deprecated)
const ai = new GoogleGenAI({
apiKey: process.env.GEMINI_API_KEY,
});
try {
// Generate content with gemini-2.5-flash
// Models available: gemini-2.5-pro, gemini-2.5-flash, gemini-2.5-flash-lite
const response = await ai.models.generateContent({
model: 'gemini-2.5-flash',
contents: 'Explain quantum computing in simple terms for a 10-year-old'
});
// Access the generated text
console.log('Generated text:');
console.log(response.text);
// Access full response metadata
console.log('\nToken usage:');
console.log('- Prompt tokens:', response.usageMetadata.promptTokenCount);
console.log('- Response tokens:', response.usageMetadata.candidatesTokenCount);
console.log('- Total tokens:', response.usageMetadata.totalTokenCount);
// Check finish reason
console.log('\nFinish reason:', response.candidates[0].finishReason);
// Possible values: "STOP" (normal), "MAX_TOKENS", "SAFETY", "OTHER"
} catch (error: any) {
console.error('Error generating content:');
if (error.status === 401) {
console.error('❌ Invalid API key. Set GEMINI_API_KEY environment variable.');
} else if (error.status === 429) {
console.error('❌ Rate limit exceeded. Try again later or implement exponential backoff.');
} else if (error.status === 404) {
console.error('❌ Model not found. Use: gemini-2.5-pro, gemini-2.5-flash, or gemini-2.5-flash-lite');
} else {
console.error('❌', error.message);
}
}
}
main();

View File

@@ -0,0 +1,124 @@
/**
* Basic Text Generation with Gemini API (Fetch - Cloudflare Workers)
*
* Demonstrates:
* - Direct REST API calls using fetch (no SDK dependencies)
* - Perfect for Cloudflare Workers, Deno, Bun, or edge runtimes
* - Manual JSON parsing
* - Error handling with fetch
*
* Prerequisites:
* - Set GEMINI_API_KEY environment variable (or use env.GEMINI_API_KEY in Workers)
*/
/**
* Example for Cloudflare Workers
*/
interface Env {
GEMINI_API_KEY: string;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
try {
// Make direct API call to Gemini
const response = await fetch(
`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-goog-api-key': env.GEMINI_API_KEY,
},
body: JSON.stringify({
contents: [
{
parts: [
{
text: 'Explain quantum computing in simple terms for a 10-year-old'
}
]
}
]
}),
}
);
// Check for HTTP errors
if (!response.ok) {
const errorData = await response.json();
return new Response(
JSON.stringify({
error: errorData.error?.message || 'Unknown error',
status: response.status
}),
{ status: response.status, headers: { 'Content-Type': 'application/json' } }
);
}
// Parse response
const data = await response.json();
// Extract text from response structure
const generatedText = data.candidates[0]?.content?.parts[0]?.text;
const usageMetadata = data.usageMetadata;
const finishReason = data.candidates[0]?.finishReason;
return new Response(
JSON.stringify({
text: generatedText,
usage: {
promptTokens: usageMetadata.promptTokenCount,
responseTokens: usageMetadata.candidatesTokenCount,
totalTokens: usageMetadata.totalTokenCount
},
finishReason
}),
{ headers: { 'Content-Type': 'application/json' } }
);
} catch (error: any) {
return new Response(
JSON.stringify({ error: error.message }),
{ status: 500, headers: { 'Content-Type': 'application/json' } }
);
}
}
};
/**
* Example for Node.js/Standalone
*/
async function mainNodeJS() {
const GEMINI_API_KEY = process.env.GEMINI_API_KEY;
if (!GEMINI_API_KEY) {
throw new Error('GEMINI_API_KEY environment variable not set');
}
const response = await fetch(
`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-goog-api-key': GEMINI_API_KEY,
},
body: JSON.stringify({
contents: [
{
parts: [
{ text: 'Explain quantum computing in simple terms' }
]
}
]
}),
}
);
const data = await response.json();
console.log(data.candidates[0].content.parts[0].text);
}
// Uncomment to run in Node.js
// mainNodeJS();

137
templates/thinking-mode.ts Normal file
View File

@@ -0,0 +1,137 @@
/**
* Thinking Mode Configuration with Gemini API
*
* Demonstrates:
* - Thinking mode (enabled by default on Gemini 2.5 models)
* - Configuring thinking budget
* - When to use thinking mode
* - Impact on latency and quality
*
* Prerequisites:
* - npm install @google/genai@1.27.0
* - export GEMINI_API_KEY="..."
*
* Thinking mode is ALWAYS ENABLED on Gemini 2.5 models (cannot be disabled)
*/
import { GoogleGenAI } from '@google/genai';
async function main() {
const ai = new GoogleGenAI({
apiKey: process.env.GEMINI_API_KEY,
});
try {
// Example 1: Default thinking budget
console.log('Example 1: Default Thinking Budget\n');
console.log('Prompt: Solve this complex math problem:\n');
console.log('If a train travels 120 km in 1.5 hours, then slows down to 60 km/h for 45 minutes, how far has it traveled total?\n');
const response1 = await ai.models.generateContent({
model: 'gemini-2.5-flash',
contents: 'If a train travels 120 km in 1.5 hours, then slows down to 60 km/h for 45 minutes, how far has it traveled total?'
// No thinkingConfig = uses default budget
});
console.log('Answer:', response1.text);
console.log('\nToken usage:', response1.usageMetadata);
console.log('\n---\n');
// Example 2: Increased thinking budget for complex reasoning
console.log('Example 2: Increased Thinking Budget (8192 tokens)\n');
console.log('Prompt: Complex logic puzzle\n');
const response2 = await ai.models.generateContent({
model: 'gemini-2.5-flash',
contents: `
Three people (Alice, Bob, Carol) have different jobs (doctor, engineer, teacher).
Clues:
1. Alice is not a doctor
2. The engineer is older than Bob
3. Carol is younger than the teacher
4. The doctor is the youngest
Who has which job?
`,
config: {
thinkingConfig: {
thinkingBudget: 8192 // Increase budget for complex reasoning
}
}
});
console.log('Answer:', response2.text);
console.log('\nToken usage:', response2.usageMetadata);
console.log('\n---\n');
// Example 3: Comparison with gemini-2.5-pro (more thinking capability)
console.log('Example 3: Using gemini-2.5-pro for Advanced Reasoning\n');
console.log('Prompt: Multi-step code optimization problem\n');
const response3 = await ai.models.generateContent({
model: 'gemini-2.5-pro', // Pro model has better reasoning
contents: `
Optimize this Python code for better performance:
def find_duplicates(arr):
duplicates = []
for i in range(len(arr)):
for j in range(i + 1, len(arr)):
if arr[i] == arr[j] and arr[i] not in duplicates:
duplicates.append(arr[i])
return duplicates
Explain your optimization strategy step by step.
`,
config: {
thinkingConfig: {
thinkingBudget: 8192
}
}
});
console.log('Optimization:', response3.text);
console.log('\nToken usage:', response3.usageMetadata);
} catch (error: any) {
console.error('Error:', error.message);
}
}
/**
* Thinking Mode Guidelines:
*
* What is Thinking Mode?
* - Gemini 2.5 models "think" before responding, improving accuracy
* - The model internally reasons through the problem
* - This happens transparently (you don't see the thinking process)
*
* Thinking Budget:
* - Controls max tokens allocated for internal reasoning
* - Higher budget = more thorough reasoning (may increase latency)
* - Default budget is usually sufficient for most tasks
*
* When to Increase Budget:
* ✅ Complex math/logic problems
* ✅ Multi-step reasoning tasks
* ✅ Code optimization challenges
* ✅ Detailed analysis requiring careful consideration
*
* When Default is Fine:
* ⏺️ Simple factual questions
* ⏺️ Creative writing
* ⏺️ Translation
* ⏺️ Summarization
*
* Model Comparison:
* - gemini-2.5-pro: Best for complex reasoning, higher default thinking budget
* - gemini-2.5-flash: Good balance, suitable for most thinking tasks
* - gemini-2.5-flash-lite: Basic thinking, optimized for speed
*
* Important Notes:
* - You CANNOT disable thinking mode on 2.5 models (always on)
* - Thinking tokens count toward total usage
* - Higher thinking budget may increase latency slightly
*/
main();