diff --git a/ai_answers.py b/ai_answers.py index 6f2cd07..4bbeb3e 100644 --- a/ai_answers.py +++ b/ai_answers.py @@ -26,11 +26,11 @@ class SXNGPlugin(Plugin): self.api_key = os.getenv('OPENROUTER_API_KEY') if self.provider == 'openrouter' else os.getenv('GEMINI_API_KEY') self.model = os.getenv('GEMINI_MODEL', 'gemma-3-27b-it') if self.provider == 'gemini' else os.getenv('OPENROUTER_MODEL', 'google/gemma-3-27b-it:free') try: - self.max_tokens = int(os.getenv('GEMINI_MAX_TOKENS', 500)) + self.max_tokens = int(os.getenv('RESPONSE_MAX_TOKENS', 500)) except ValueError: self.max_tokens = 500 try: - self.temperature = float(os.getenv('GEMINI_TEMPERATURE', 0.2)) + self.temperature = float(os.getenv('RESPONSE_TEMPERATURE', 0.2)) except ValueError: self.temperature = 0.2 self.base_url = os.getenv('OPENROUTER_BASE_URL', 'openrouter.ai') @@ -74,6 +74,7 @@ class SXNGPlugin(Plugin): def generate_gemini(): host = "generativelanguage.googleapis.com" path = f"/v1/models/{self.model}:streamGenerateContent?key={self.api_key}" + conn = None try: conn = http.client.HTTPSConnection(host, timeout=CONNECTION_TIMEOUT_SEC, context=ssl.create_default_context()) payload = {"contents": [{"parts": [{"text": prompt}]}], "generationConfig": {"maxOutputTokens": self.max_tokens, "temperature": self.temperature}} @@ -103,12 +104,20 @@ class SXNGPlugin(Plugin): if text: yield text buffer = buffer[idx:] except json.JSONDecodeError: break - conn.close() - except Exception as e: logger.error(f"Gemini Stream Exception: {e}") + except Exception as e: + logger.error(f"Gemini Stream Exception: {e}") + finally: + if conn: conn.close() def generate_openrouter(): + conn = None try: - conn = http.client.HTTPSConnection(self.base_url, timeout=CONNECTION_TIMEOUT_SEC, context=ssl.create_default_context()) + # Support HTTP for localhost/Ollama + is_local = self.base_url.startswith('localhost') or self.base_url.startswith('127.') + if is_local: + conn = http.client.HTTPConnection(self.base_url, timeout=CONNECTION_TIMEOUT_SEC) + else: + conn = http.client.HTTPSConnection(self.base_url, timeout=CONNECTION_TIMEOUT_SEC, context=ssl.create_default_context()) payload = { "model": self.model, "messages": [{"role": "user", "content": prompt}], @@ -122,7 +131,9 @@ class SXNGPlugin(Plugin): "HTTP-Referer": "https://github.com/searxng/searxng", "X-Title": "SearXNG LLM Plugin" } - conn.request("POST", "/api/v1/chat/completions", body=json.dumps(payload), headers=headers) + # Ollama uses /v1/... while OpenRouter uses /api/v1/... + api_path = "/v1/chat/completions" if is_local else "/api/v1/chat/completions" + conn.request("POST", api_path, body=json.dumps(payload), headers=headers) res = conn.getresponse() if res.status != 200: logger.error(f"OpenRouter API Error {res.status}: {res.read().decode('utf-8')}") @@ -145,8 +156,10 @@ class SXNGPlugin(Plugin): if content: yield content except json.JSONDecodeError: pass - conn.close() - except Exception as e: logger.error(f"OpenRouter Stream Exception: {e}") + except Exception as e: + logger.error(f"OpenRouter Stream Exception: {e}") + finally: + if conn: conn.close() generator = generate_openrouter if self.provider == 'openrouter' else generate_gemini return Response(generator(), mimetype='text/event-stream', headers={ @@ -180,12 +193,18 @@ class SXNGPlugin(Plugin):