All files / src/ai/providers gemini.ts

97.01% Statements 65/67
66.66% Branches 8/12
100% Functions 2/2
97.01% Lines 65/67

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 682x 2x 2x 2x 2x 2x 2x 2x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 1x 1x 1x 2x 2x 2x 2x 2x 2x 2x 2x 2x 3x 3x 1x 1x 1x 1x 1x     1x 1x 1x 1x 1x 3x 3x 3x 2x  
import { AIProvider, ChatOptions, EmptyContentError } from './base';
import { t } from '../../i18n/messages';
 
export class GeminiProvider extends AIProvider {
  id = 'gemini';
  name = 'Google Gemini';
 
  async chat(systemPrompt: string, userPrompt: string, options: ChatOptions): Promise<string> {
    const url = `${this.baseUrl}/models/${options.model}:generateContent?key=${this.apiKey}`;
 
    const body = JSON.stringify({
      systemInstruction: {
        parts: [{ text: systemPrompt }],
      },
      contents: [
        {
          parts: [{ text: userPrompt }],
        },
      ],
      generationConfig: {
        temperature: options.temperature,
        maxOutputTokens: options.maxTokens,
      },
    });
 
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), options.timeoutMs);
 
    try {
      const response = await fetch(url, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body,
        signal: controller.signal,
      });
 
      if (!response.ok) {
        const errorText = await response.text();
        throw new Error(`Gemini API 请求失败 (${response.status}): ${errorText}`);
      }
 
      const data = await response.json() as {
        candidates?: Array<{
          content?: { parts?: Array<{ text?: string }> };
        }>;
        promptFeedback?: { blockReason?: string };
        error?: { message?: string };
      };
 
      const text = data.candidates?.[0]?.content?.parts?.[0]?.text;
      if (text === undefined || text === null || text.trim() === '') {
        const parts = [
          `candidates=${data.candidates?.length ?? 0}`,
          `blockReason=${data.promptFeedback?.blockReason ?? 'none'}`,
        ];
        if (data.error?.message) {
          parts.push(data.error.message);
        }
        throw new EmptyContentError(t('adapter.emptyContent', { 0: parts.join(', ') }));
      }
 
      return text;
    } finally {
      clearTimeout(timeout);
    }
  }
}