Z User commited on
Commit
c6b874d
·
1 Parent(s): 6a941e4

feat: add Pollinations.ai free image generation plugin

Browse files

- New plugin: plugins/image_gen/pollinations (no API key needed)
- Uses https://image.pollinations.ai for free image gen (Flux model)
- Supports landscape/square/portrait aspect ratios
- config.yaml: image_gen.provider = pollinations
- Dockerfile: copy plugin to /root/.hermes/plugins/

Dockerfile CHANGED
@@ -26,7 +26,7 @@ RUN mkdir -p /usr/share/fonts/truetype/noto && \
26
  fc-cache -f
27
 
28
  # Create hermes home
29
- RUN mkdir -p /root/.hermes
30
 
31
  # Copy config files
32
  COPY config.yaml /root/.hermes/config.yaml
@@ -34,6 +34,7 @@ COPY SOUL.md /root/.hermes/SOUL.md
34
  COPY .env /root/.hermes/.env
35
  COPY entry.py /app/entry.py
36
  COPY dashboard.html /app/dashboard.html
 
37
 
38
  RUN chmod 600 /root/.hermes/.env
39
 
 
26
  fc-cache -f
27
 
28
  # Create hermes home
29
+ RUN mkdir -p /root/.hermes/plugins/image_gen/pollinations
30
 
31
  # Copy config files
32
  COPY config.yaml /root/.hermes/config.yaml
 
34
  COPY .env /root/.hermes/.env
35
  COPY entry.py /app/entry.py
36
  COPY dashboard.html /app/dashboard.html
37
+ COPY plugins/pollinations/ /root/.hermes/plugins/image_gen/pollinations/
38
 
39
  RUN chmod 600 /root/.hermes/.env
40
 
config.yaml CHANGED
@@ -19,6 +19,8 @@ compress:
19
  threshold: 50
20
  target_ratio: 20
21
  protect_last: 20
 
 
22
  no_mcp: true
23
  terminal:
24
  backend: local
 
19
  threshold: 50
20
  target_ratio: 20
21
  protect_last: 20
22
+ image_gen:
23
+ provider: pollinations
24
  no_mcp: true
25
  terminal:
26
  backend: local
plugins/pollinations/__init__.py ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Pollinations.ai image generation backend — free, no API key required.
2
+
3
+ Generates images via https://image.pollinations.ai/prompt/{prompt}.
4
+ Downloads the result and saves to $HERMES_HOME/cache/images/.
5
+ Supports landscape/square/portrait aspect ratios.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import logging
11
+ import os
12
+ import urllib.parse
13
+ import urllib.request
14
+ from typing import Any, Dict, List
15
+
16
+ from agent.image_gen_provider import (
17
+ DEFAULT_ASPECT_RATIO,
18
+ ImageGenProvider,
19
+ error_response,
20
+ resolve_aspect_ratio,
21
+ success_response,
22
+ )
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+ _API_BASE = "https://image.pollinations.ai/prompt"
27
+
28
+ _SIZES = {
29
+ "landscape": "1344x768",
30
+ "square": "1024x1024",
31
+ "portrait": "768x1344",
32
+ }
33
+
34
+ DEFAULT_MODEL = "flux"
35
+
36
+
37
+ class PollinationsImageGenProvider(ImageGenProvider):
38
+ """Pollinations.ai free image generation backend."""
39
+
40
+ @property
41
+ def name(self) -> str:
42
+ return "pollinations"
43
+
44
+ @property
45
+ def display_name(self) -> str:
46
+ return "Pollinations (Free)"
47
+
48
+ def is_available(self) -> bool:
49
+ return True # no API key needed
50
+
51
+ def list_models(self) -> List[Dict[str, Any]]:
52
+ return [
53
+ {
54
+ "id": "flux",
55
+ "display": "Flux (Free)",
56
+ "speed": "~10-20s",
57
+ "strengths": "Free, no API key, good quality",
58
+ "price": "Free",
59
+ }
60
+ ]
61
+
62
+ def get_setup_schema(self) -> Dict[str, Any]:
63
+ return {
64
+ "name": "Pollinations.ai",
65
+ "badge": "free",
66
+ "tag": "Free image generation — no API key required",
67
+ "env_vars": [],
68
+ }
69
+
70
+ def default_model(self) -> str:
71
+ return DEFAULT_MODEL
72
+
73
+ def generate(
74
+ self,
75
+ prompt: str,
76
+ aspect_ratio: str = DEFAULT_ASPECT_RATIO,
77
+ **kwargs: Any,
78
+ ) -> Dict[str, Any]:
79
+ prompt = (prompt or "").strip()
80
+ aspect = resolve_aspect_ratio(aspect_ratio)
81
+
82
+ if not prompt:
83
+ return error_response(
84
+ error="Prompt is required",
85
+ error_type="invalid_argument",
86
+ provider="pollinations",
87
+ aspect_ratio=aspect,
88
+ )
89
+
90
+ width, height = _SIZES.get(aspect, _SIZES["square"]).split("x")
91
+ seed = os.urandom(4).hex()
92
+ url = f"{_API_BASE}/{urllib.parse.quote(prompt)}?width={width}&height={height}&seed={seed}&nologo=true&model=flux"
93
+
94
+ # Download image to cache
95
+ try:
96
+ from hermes_constants import get_hermes_home
97
+ import datetime
98
+ import uuid
99
+
100
+ cache_dir = get_hermes_home() / "cache" / "images"
101
+ cache_dir.mkdir(parents=True, exist_ok=True)
102
+ ts = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
103
+ short = uuid.uuid4().hex[:8]
104
+ save_path = cache_dir / f"pollinations_{ts}_{short}.png"
105
+
106
+ req = urllib.request.Request(url, headers={"User-Agent": "Hermes/1.0"})
107
+ with urllib.request.urlopen(req, timeout=60) as resp:
108
+ data = resp.read()
109
+
110
+ save_path.write_bytes(data)
111
+ image_ref = str(save_path)
112
+
113
+ except Exception as exc:
114
+ logger.warning("Pollinations image download failed: %s", exc)
115
+ # Fallback: return URL directly
116
+ image_ref = url
117
+
118
+ return success_response(
119
+ image=image_ref,
120
+ model=DEFAULT_MODEL,
121
+ prompt=prompt,
122
+ aspect_ratio=aspect,
123
+ provider="pollinations",
124
+ extra={"url": url},
125
+ )
126
+
127
+
128
+ def register(ctx) -> None:
129
+ ctx.register_image_gen_provider(PollinationsImageGenProvider())
plugins/pollinations/plugin.yaml ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ name: pollinations
2
+ version: 1.0.0
3
+ description: "Free image generation via Pollinations.ai — no API key required."
4
+ author: Hermes
5
+ kind: backend