Skip to main content
Glama
gemini-client.ts6.32 kB
/** * Gemini API Client for Nano Banana image generation */ import { GeminiRequest, GeminiResponse, GeminiContent, ModelName, AspectRatio, Resolution, MODELS, } from "../types.js"; const BASE_URL = "https://generativelanguage.googleapis.com/v1beta/models"; /** * Get API key from environment variable */ export function getApiKey(): string { const apiKey = process.env.GEMINI_API_KEY; if (!apiKey) { throw new Error( "GEMINI_API_KEY environment variable is required. " + "Get your API key from https://aistudio.google.com/apikey" ); } return apiKey; } /** * Make a request to the Gemini API */ async function makeGeminiRequest( model: ModelName, request: GeminiRequest ): Promise<GeminiResponse> { const apiKey = getApiKey(); const url = `${BASE_URL}/${model}:generateContent`; const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json", "x-goog-api-key": apiKey, }, body: JSON.stringify(request), }); if (!response.ok) { const errorText = await response.text(); let errorMessage: string; try { const errorJson = JSON.parse(errorText); errorMessage = errorJson.error?.message || errorText; } catch { errorMessage = errorText; } throw new Error(`Gemini API error (${response.status}): ${errorMessage}`); } return (await response.json()) as GeminiResponse; } /** * Extract image data and text from Gemini response */ function extractResponseContent(response: GeminiResponse): { imageData?: string; mimeType?: string; text?: string; } { const result: { imageData?: string; mimeType?: string; text?: string; } = {}; if (!response.candidates || response.candidates.length === 0) { throw new Error("No response candidates returned from API"); } const parts = response.candidates[0].content.parts; const textParts: string[] = []; for (const part of parts) { // Skip thought parts (internal reasoning) if (part.thought) continue; if (part.text) { textParts.push(part.text); } else if (part.inlineData) { result.imageData = part.inlineData.data; result.mimeType = part.inlineData.mimeType; } } if (textParts.length > 0) { result.text = textParts.join("\n"); } return result; } /** * Generate an image from a text prompt */ export async function generateImage(options: { prompt: string; model?: ModelName; aspectRatio?: AspectRatio; resolution?: Resolution; useGoogleSearch?: boolean; }): Promise<{ imageData?: string; mimeType?: string; text?: string; }> { const { prompt, model = MODELS.NANO_BANANA_PRO, aspectRatio, resolution, useGoogleSearch = false, } = options; const request: GeminiRequest = { contents: [ { parts: [{ text: prompt }], }, ], generationConfig: { responseModalities: ["TEXT", "IMAGE"], }, }; // Add image config if aspect ratio or resolution specified if (aspectRatio || resolution) { request.generationConfig!.imageConfig = {}; if (aspectRatio) { request.generationConfig!.imageConfig.aspectRatio = aspectRatio; } if (resolution && model === MODELS.NANO_BANANA_PRO) { request.generationConfig!.imageConfig.imageSize = resolution; } } // Add Google Search tool for grounding if requested if (useGoogleSearch) { request.tools = [{ google_search: {} }]; } const response = await makeGeminiRequest(model, request); return extractResponseContent(response); } /** * Edit an image using a text prompt */ export async function editImage(options: { prompt: string; imageBase64: string; imageMimeType: string; model?: ModelName; aspectRatio?: AspectRatio; resolution?: Resolution; }): Promise<{ imageData?: string; mimeType?: string; text?: string; }> { const { prompt, imageBase64, imageMimeType, model = MODELS.NANO_BANANA_PRO, aspectRatio, resolution, } = options; const contents: GeminiContent[] = [ { parts: [ { text: prompt }, { inline_data: { mime_type: imageMimeType, data: imageBase64, }, }, ], }, ]; const request: GeminiRequest = { contents, generationConfig: { responseModalities: ["TEXT", "IMAGE"], }, }; // Add image config if aspect ratio or resolution specified if (aspectRatio || resolution) { request.generationConfig!.imageConfig = {}; if (aspectRatio) { request.generationConfig!.imageConfig.aspectRatio = aspectRatio; } if (resolution && model === MODELS.NANO_BANANA_PRO) { request.generationConfig!.imageConfig.imageSize = resolution; } } const response = await makeGeminiRequest(model, request); return extractResponseContent(response); } /** * Edit an image using multiple reference images */ export async function editImageWithReferences(options: { prompt: string; images: Array<{ base64: string; mimeType: string }>; model?: ModelName; aspectRatio?: AspectRatio; resolution?: Resolution; }): Promise<{ imageData?: string; mimeType?: string; text?: string; }> { const { prompt, images, model = MODELS.NANO_BANANA_PRO, aspectRatio, resolution, } = options; if (images.length > 14) { throw new Error("Maximum of 14 reference images allowed"); } const parts: Array<{ text?: string; inline_data?: { mime_type: string; data: string } }> = [ { text: prompt }, ]; for (const img of images) { parts.push({ inline_data: { mime_type: img.mimeType, data: img.base64, }, }); } const request: GeminiRequest = { contents: [{ parts }], generationConfig: { responseModalities: ["TEXT", "IMAGE"], }, }; // Add image config if (aspectRatio || resolution) { request.generationConfig!.imageConfig = {}; if (aspectRatio) { request.generationConfig!.imageConfig.aspectRatio = aspectRatio; } if (resolution && model === MODELS.NANO_BANANA_PRO) { request.generationConfig!.imageConfig.imageSize = resolution; } } const response = await makeGeminiRequest(model, request); return extractResponseContent(response); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mikeroussell/nano-banana-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server