Spaces:
Sleeping
Sleeping
| """Google Gemini engine — free API tier, no self-hosting. | |
| Uses Gemini's generous free tier (no credit card). Strong on Korean / CJK and | |
| low-resource prose. Available whenever ``GEMINI_API_KEY`` is set. Text is sent to | |
| Google, so this engine is not private. | |
| """ | |
| from __future__ import annotations | |
| import os | |
| import httpx | |
| from languages import name_for | |
| from providers.base import TranslationProvider | |
| MODEL = os.environ.get("GEMINI_MODEL", "gemini-2.5-flash-lite") | |
| ENDPOINT = "https://generativelanguage.googleapis.com/v1beta/models" | |
| class GeminiProvider(TranslationProvider): | |
| id = "gemini" | |
| name = "Gemini 2.5 Flash-Lite" | |
| kind = "api" | |
| description = "Google Gemini · free API tier · excellent for Korean/CJK" | |
| private = False | |
| key_field = "gemini" | |
| setup_hint = "Add a Gemini key in Settings (free at aistudio.google.com/apikey)" | |
| def is_available(self) -> bool: | |
| return bool(os.environ.get("GEMINI_API_KEY")) | |
| def translate(self, text: str, src: str, tgt: str, api_key: str | None = None) -> str: | |
| key = api_key or os.environ.get("GEMINI_API_KEY") | |
| if not key: | |
| raise ValueError("No Gemini API key provided.") | |
| prompt = ( | |
| f"Translate the following text from {name_for(src)} to " | |
| f"{name_for(tgt)}. Output ONLY the translation, with no notes, " | |
| f"quotes, or explanations.\n\n{text}" | |
| ) | |
| url = f"{ENDPOINT}/{MODEL}:generateContent?key={key}" | |
| resp = httpx.post( | |
| url, | |
| json={ | |
| "contents": [{"parts": [{"text": prompt}]}], | |
| "generationConfig": {"temperature": 0.2}, | |
| }, | |
| timeout=30.0, | |
| ) | |
| resp.raise_for_status() | |
| data = resp.json() | |
| try: | |
| return data["candidates"][0]["content"]["parts"][0]["text"].strip() | |
| except (KeyError, IndexError) as exc: | |
| raise RuntimeError(f"Unexpected Gemini response: {data}") from exc | |