/** * Gemini Embeddings with Fetch (Cloudflare Workers) * * Demonstrates embedding generation using fetch API instead of SDK. * Perfect for Cloudflare Workers and edge environments. * * Setup: * 1. Add GEMINI_API_KEY to wrangler.jsonc secrets * 2. npx wrangler secret put GEMINI_API_KEY * 3. Deploy: npx wrangler deploy * * Usage: * GET /?text=your+text+here */ interface Env { GEMINI_API_KEY: string; } interface EmbeddingRequest { content: { parts: Array<{ text: string }>; }; taskType?: string; outputDimensionality?: number; } interface EmbeddingResponse { embedding: { values: number[]; }; } export default { async fetch(request: Request, env: Env): Promise { // CORS headers for browser access const corsHeaders = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type' }; // Handle CORS preflight if (request.method === 'OPTIONS') { return new Response(null, { headers: corsHeaders }); } try { // Get text from query param or request body const url = new URL(request.url); let text: string; if (request.method === 'POST') { const body = await request.json<{ text: string }>(); text = body.text; } else { text = url.searchParams.get('text') || 'What is the meaning of life?'; } console.log(`Generating embedding for: "${text}"`); // Prepare request const embeddingRequest: EmbeddingRequest = { content: { parts: [{ text }] }, taskType: 'SEMANTIC_SIMILARITY', outputDimensionality: 768 }; // Call Gemini API const response = await fetch( 'https://generativelanguage.googleapis.com/v1beta/models/gemini-embedding-001:embedContent', { method: 'POST', headers: { 'x-goog-api-key': env.GEMINI_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify(embeddingRequest) } ); if (!response.ok) { const error = await response.text(); throw new Error(`Gemini API error: ${response.status} - ${error}`); } const data = await response.json(); const embedding = data.embedding.values; // Calculate vector magnitude const magnitude = Math.sqrt( embedding.reduce((sum, v) => sum + v * v, 0) ); // Return formatted response return new Response(JSON.stringify({ success: true, text, embedding: { dimensions: embedding.length, magnitude: magnitude.toFixed(4), firstValues: embedding.slice(0, 10).map(v => parseFloat(v.toFixed(4))), fullVector: embedding } }, null, 2), { headers: { 'Content-Type': 'application/json', ...corsHeaders } }); } catch (error: any) { console.error('Error:', error.message); return new Response(JSON.stringify({ success: false, error: error.message, hint: error.message.includes('401') ? 'Check GEMINI_API_KEY secret is set' : error.message.includes('429') ? 'Rate limit exceeded (Free tier: 100 RPM)' : 'Check error message for details' }, null, 2), { status: 500, headers: { 'Content-Type': 'application/json', ...corsHeaders } }); } } }; /** * Example wrangler.jsonc configuration: * * { * "name": "gemini-embeddings-worker", * "main": "src/index.ts", * "compatibility_date": "2025-10-25", * "vars": { * "ENVIRONMENT": "production" * } * } * * Set secret: * npx wrangler secret put GEMINI_API_KEY * * Test locally: * npx wrangler dev * curl "http://localhost:8787/?text=Hello+world" * * Deploy: * npx wrangler deploy */