( apiKey: string, body: ImageToolBody, requestId: string, logger: ReturnType<typeof createLogger> )
| 607 | } |
| 608 | |
| 609 | async function generateWithGemini( |
| 610 | apiKey: string, |
| 611 | body: ImageToolBody, |
| 612 | requestId: string, |
| 613 | logger: ReturnType<typeof createLogger> |
| 614 | ): Promise<GeneratedImageResult> { |
| 615 | const model = pickAllowed(body.model, GEMINI_IMAGE_MODELS, 'gemini-3.1-flash-image-preview') |
| 616 | const aspectRatios = |
| 617 | model === 'gemini-3.1-flash-image-preview' |
| 618 | ? [...GEMINI_BASE_ASPECT_RATIOS, ...GEMINI_EXTREME_ASPECT_RATIOS] |
| 619 | : GEMINI_BASE_ASPECT_RATIOS |
| 620 | const imageConfig: Record<string, string> = {} |
| 621 | |
| 622 | if (body.aspectRatio) { |
| 623 | imageConfig.aspectRatio = pickAllowed(body.aspectRatio, aspectRatios, '1:1') |
| 624 | } |
| 625 | |
| 626 | if (model === 'gemini-3.1-flash-image-preview' && body.resolution) { |
| 627 | imageConfig.imageSize = pickAllowed(body.resolution, GEMINI_IMAGE_SIZES, '1K') |
| 628 | } else if (model === 'gemini-3-pro-image-preview' && body.resolution) { |
| 629 | imageConfig.imageSize = pickAllowed(body.resolution, GEMINI_PRO_IMAGE_SIZES, '1K') |
| 630 | } |
| 631 | |
| 632 | const requestBody: Record<string, unknown> = { |
| 633 | contents: [ |
| 634 | { |
| 635 | parts: [{ text: body.prompt }], |
| 636 | }, |
| 637 | ], |
| 638 | } |
| 639 | |
| 640 | requestBody.generationConfig = { |
| 641 | responseModalities: ['TEXT', 'IMAGE'], |
| 642 | ...(Object.keys(imageConfig).length > 0 && { imageConfig }), |
| 643 | } |
| 644 | |
| 645 | const geminiResponse = await fetch( |
| 646 | `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent`, |
| 647 | { |
| 648 | method: 'POST', |
| 649 | headers: { |
| 650 | 'x-goog-api-key': apiKey, |
| 651 | 'Content-Type': 'application/json', |
| 652 | }, |
| 653 | body: JSON.stringify(requestBody), |
| 654 | } |
| 655 | ) |
| 656 | |
| 657 | if (!geminiResponse.ok) { |
| 658 | const error = await readResponseTextWithLimit(geminiResponse, { |
| 659 | maxBytes: DEFAULT_MAX_ERROR_BODY_BYTES, |
| 660 | label: 'Gemini image error response', |
| 661 | }) |
| 662 | throw new Error(`Gemini API error: ${geminiResponse.status} - ${error}`) |
| 663 | } |
| 664 | |
| 665 | const data = await readResponseJsonWithLimit(geminiResponse, { |
| 666 | maxBytes: MAX_IMAGE_JSON_BYTES, |
no test coverage detected