Initial commit
This commit is contained in:
245
templates/cloudflare-worker.ts
Normal file
245
templates/cloudflare-worker.ts
Normal 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
206
templates/code-execution.ts
Normal 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);
|
||||
});
|
||||
278
templates/combined-advanced.ts
Normal file
278
templates/combined-advanced.ts
Normal 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);
|
||||
});
|
||||
154
templates/context-caching.ts
Normal file
154
templates/context-caching.ts
Normal 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);
|
||||
});
|
||||
156
templates/function-calling-basic.ts
Normal file
156
templates/function-calling-basic.ts
Normal 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();
|
||||
177
templates/function-calling-parallel.ts
Normal file
177
templates/function-calling-parallel.ts
Normal 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();
|
||||
271
templates/grounding-search.ts
Normal file
271
templates/grounding-search.ts
Normal 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);
|
||||
});
|
||||
117
templates/multimodal-image.ts
Normal file
117
templates/multimodal-image.ts
Normal 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();
|
||||
152
templates/multimodal-video-audio.ts
Normal file
152
templates/multimodal-video-audio.ts
Normal 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
22
templates/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
81
templates/streaming-chat.ts
Normal file
81
templates/streaming-chat.ts
Normal 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();
|
||||
190
templates/streaming-fetch.ts
Normal file
190
templates/streaming-fetch.ts
Normal 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();
|
||||
61
templates/text-generation-basic.ts
Normal file
61
templates/text-generation-basic.ts
Normal 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();
|
||||
124
templates/text-generation-fetch.ts
Normal file
124
templates/text-generation-fetch.ts
Normal 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
137
templates/thinking-mode.ts
Normal 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();
|
||||
Reference in New Issue
Block a user