ballagb19 commited on
Commit
4ad7943
·
verified ·
1 Parent(s): 9299116

Upload captcha_solver/engines/ollama_engine.py with huggingface_hub

Browse files
captcha_solver/engines/ollama_engine.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Ollama HTTP client (optional upgrade).
2
+
3
+ If ollama is enabled and running, this engine provides better quality
4
+ text + vision inference by routing to local ollama models. The HF
5
+ engines remain the default for fully-offline operation.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import base64
11
+ import json
12
+ import urllib.error
13
+ import urllib.request
14
+ from typing import Any, Optional
15
+
16
+ from captcha_solver.engines.base import BaseEngine
17
+ from captcha_solver.config import get_settings
18
+
19
+
20
+ class OllamaEngine(BaseEngine):
21
+ name = "ollama"
22
+
23
+ def __init__(self) -> None:
24
+ super().__init__()
25
+ self._enabled = False
26
+ self._host = ""
27
+
28
+ def _do_load(self) -> None:
29
+ s = get_settings()
30
+ self._enabled = s.ollama_enabled
31
+ self._host = s.ollama_host.rstrip("/")
32
+ if not self._enabled:
33
+ raise RuntimeError("ollama disabled (ollama_enabled=false in config)")
34
+ try:
35
+ with urllib.request.urlopen(f"{self._host}/api/tags", timeout=3) as r:
36
+ if r.status != 200:
37
+ raise RuntimeError(f"ollama returned {r.status}")
38
+ except Exception as exc:
39
+ self._enabled = False
40
+ raise RuntimeError(f"ollama not reachable at {self._host}: {exc}") from exc
41
+
42
+ def _do_unload(self) -> None:
43
+ self._enabled = False
44
+
45
+ @property
46
+ def enabled(self) -> bool:
47
+ return self._enabled and self._loaded
48
+
49
+ def _post(self, path: str, payload: dict, timeout: int = 30) -> dict:
50
+ data = json.dumps(payload).encode("utf-8")
51
+ req = urllib.request.Request(
52
+ f"{self._host}{path}",
53
+ data=data,
54
+ headers={"Content-Type": "application/json"},
55
+ method="POST",
56
+ )
57
+ with urllib.request.urlopen(req, timeout=timeout) as r:
58
+ return json.loads(r.read().decode("utf-8"))
59
+
60
+ def generate_text(
61
+ self,
62
+ prompt: str,
63
+ system: Optional[str] = None,
64
+ model: Optional[str] = None,
65
+ max_tokens: int = 64,
66
+ ) -> str:
67
+ if not self.enabled:
68
+ raise RuntimeError("ollama not enabled")
69
+ s = get_settings()
70
+ payload: dict[str, Any] = {
71
+ "model": model or s.ollama_text_model,
72
+ "prompt": prompt,
73
+ "stream": False,
74
+ "options": {"num_predict": max_tokens, "temperature": 0.0},
75
+ }
76
+ if system:
77
+ payload["system"] = system
78
+ return self._post("/api/generate", payload, s.ollama_timeout).get("response", "").strip()
79
+
80
+ def describe_image(self, pil_image, prompt: str, model: Optional[str] = None) -> str:
81
+ if not self.enabled:
82
+ raise RuntimeError("ollama not enabled")
83
+ s = get_settings()
84
+ import io
85
+
86
+ buf = io.BytesIO()
87
+ pil_image.save(buf, format="PNG")
88
+ b64 = base64.b64encode(buf.getvalue()).decode("ascii")
89
+ payload = {
90
+ "model": model or s.ollama_vision_model,
91
+ "prompt": prompt,
92
+ "images": [b64],
93
+ "stream": False,
94
+ "options": {"num_predict": 200, "temperature": 0.0},
95
+ }
96
+ return self._post("/api/generate", payload, s.ollama_timeout).get("response", "").strip()