Spaces:
Running
Running
| """Pollinations.ai image generation backend — free, no API key required. | |
| Generates images via https://image.pollinations.ai/prompt/{prompt}. | |
| Downloads the result and saves to $HERMES_HOME/cache/images/. | |
| Supports landscape/square/portrait aspect ratios. | |
| """ | |
| from __future__ import annotations | |
| import logging | |
| import os | |
| import urllib.parse | |
| import urllib.request | |
| from typing import Any, Dict, List | |
| from agent.image_gen_provider import ( | |
| DEFAULT_ASPECT_RATIO, | |
| ImageGenProvider, | |
| error_response, | |
| resolve_aspect_ratio, | |
| success_response, | |
| ) | |
| logger = logging.getLogger(__name__) | |
| _API_BASE = "https://image.pollinations.ai/prompt" | |
| _SIZES = { | |
| "landscape": "1344x768", | |
| "square": "1024x1024", | |
| "portrait": "768x1344", | |
| } | |
| DEFAULT_MODEL = "flux" | |
| class PollinationsImageGenProvider(ImageGenProvider): | |
| """Pollinations.ai free image generation backend.""" | |
| def name(self) -> str: | |
| return "pollinations" | |
| def display_name(self) -> str: | |
| return "Pollinations (Free)" | |
| def is_available(self) -> bool: | |
| return True # no API key needed | |
| def list_models(self) -> List[Dict[str, Any]]: | |
| return [ | |
| { | |
| "id": "flux", | |
| "display": "Flux (Free)", | |
| "speed": "~10-20s", | |
| "strengths": "Free, no API key, good quality", | |
| "price": "Free", | |
| } | |
| ] | |
| def get_setup_schema(self) -> Dict[str, Any]: | |
| return { | |
| "name": "Pollinations.ai", | |
| "badge": "free", | |
| "tag": "Free image generation — no API key required", | |
| "env_vars": [], | |
| } | |
| def default_model(self) -> str: | |
| return DEFAULT_MODEL | |
| def generate( | |
| self, | |
| prompt: str, | |
| aspect_ratio: str = DEFAULT_ASPECT_RATIO, | |
| **kwargs: Any, | |
| ) -> Dict[str, Any]: | |
| prompt = (prompt or "").strip() | |
| aspect = resolve_aspect_ratio(aspect_ratio) | |
| if not prompt: | |
| return error_response( | |
| error="Prompt is required", | |
| error_type="invalid_argument", | |
| provider="pollinations", | |
| aspect_ratio=aspect, | |
| ) | |
| width, height = _SIZES.get(aspect, _SIZES["square"]).split("x") | |
| seed = os.urandom(4).hex() | |
| url = f"{_API_BASE}/{urllib.parse.quote(prompt)}?width={width}&height={height}&seed={seed}&nologo=true&model=flux" | |
| # Download image to cache | |
| try: | |
| from hermes_constants import get_hermes_home | |
| import datetime | |
| import uuid | |
| cache_dir = get_hermes_home() / "cache" / "images" | |
| cache_dir.mkdir(parents=True, exist_ok=True) | |
| ts = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") | |
| short = uuid.uuid4().hex[:8] | |
| save_path = cache_dir / f"pollinations_{ts}_{short}.png" | |
| req = urllib.request.Request(url, headers={"User-Agent": "Hermes/1.0"}) | |
| with urllib.request.urlopen(req, timeout=60) as resp: | |
| data = resp.read() | |
| save_path.write_bytes(data) | |
| image_ref = str(save_path) | |
| except Exception as exc: | |
| logger.warning("Pollinations image download failed: %s", exc) | |
| # Fallback: return URL directly | |
| image_ref = url | |
| return success_response( | |
| image=image_ref, | |
| model=DEFAULT_MODEL, | |
| prompt=prompt, | |
| aspect_ratio=aspect, | |
| provider="pollinations", | |
| extra={"url": url}, | |
| ) | |
| def register(ctx) -> None: | |
| ctx.register_image_gen_provider(PollinationsImageGenProvider()) | |