From 0d44023bb82021f03a89146c871755c628fd08ca Mon Sep 17 00:00:00 2001 From: Tyler <68524461+TySP-Dev@users.noreply.github.com> Date: Fri, 15 May 2026 14:32:03 -0400 Subject: [PATCH] More plugin fixes --- ai_answers.py | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/ai_answers.py b/ai_answers.py index 0549b97..61083d6 100644 --- a/ai_answers.py +++ b/ai_answers.py @@ -1016,7 +1016,75 @@ class SXNGPlugin(Plugin): {numbered_instructions} """ - + + def call_ollama(): + conn = None + try: + conn, path = _get_streaming_connection(self.endpoint_url) + payload_dict = { + "model": effective_model, + "messages": [ + {"role": "system", "content": SYSTEM}, + {"role": "user", "content": prompt}, + {"role": "assistant", "content": ""}, + ], + "stream": False, + "max_tokens": self.max_tokens, + "temperature": self.temperature, + } + payload = json.dumps(payload_dict) + headers = { + "Content-Type": "application/json", + "Authorization": f"Bearer {self.api_key}", + } + conn.request("POST", path, body=payload.encode('utf-8'), headers=headers) + res = conn.getresponse() + if res.status != 200: + body = res.read(1024).decode('utf-8', errors='replace') + logger.error(f"{PLUGIN_NAME}: Ollama {res.status}: {body}") + return '', f"Ollama error {res.status}" + obj = json.loads(res.read().decode('utf-8', errors='replace')) + if 'error' in obj: + err = obj['error'] + msg = err.get('message', str(err)) if isinstance(err, dict) else str(err) + return '', msg + choices = obj.get('choices', []) + if not choices: + return '', "No choices in Ollama response." + message = choices[0].get('message', {}) + content = message.get('content') or '' + reasoning = message.get('reasoning') or message.get('reasoning_content') or '' + content = re.sub(r'.*?', '', content, flags=re.DOTALL).strip() + if not content and reasoning: + logger.warning(f"{PLUGIN_NAME}: content empty, extracting from reasoning field") + lines = reasoning.splitlines() + header_re = re.compile(r'^\s*\*?\*?[A-Z][^:]{0,40}:\*?\*?\s*$') + last_header_idx = -1 + for i, line in enumerate(lines): + if header_re.match(line): + last_header_idx = i + if last_header_idx >= 0 and last_header_idx < len(lines) - 1: + content = '\n'.join(lines[last_header_idx + 1:]).strip() + if not content: + paragraphs = [p.strip() for p in reasoning.split('\n\n') if p.strip()] + content = '\n\n'.join(paragraphs[-2:]) if len(paragraphs) >= 2 else paragraphs[-1] if paragraphs else '' + if reasoning and content: + full = (f"\n{reasoning}\n\n\n" if reasoning else "") + content + else: + full = content + full = re.sub(r'.*?', '', full, flags=re.DOTALL).strip() + return full, None + except Exception as e: + logger.error(f"{PLUGIN_NAME}: Ollama call error: {e}", exc_info=True) + return '', f"Connection Error: {e}" + finally: + if conn: + conn.close() + + text, error = call_ollama() + return jsonify({"text": text, "error": error}) + return True + def _assemble_context(self, clean_results, infoboxes, answers, offset=0) -> tuple[str, list]: """Builds context string from normalized search data. Returns (context_str, urls).""" context_parts = []