from __future__ import annotations import json import os from urllib.error import HTTPError, URLError from urllib.request import Request, urlopen from prompt_injection_framework.models.base import BaseModelAdapter, ModelRequest class OllamaModelAdapter(BaseModelAdapter): provider = "ollama" def __init__(self, model_name: str, base_url: str | None = None) -> None: super().__init__(model_name=model_name) configured_base = base_url or os.getenv("OLLAMA_BASE_URL") or "http://127.0.0.1:11434" self.base_url = configured_base.rstrip("/") self.api_url = f"{self.base_url}/api/generate" def _generate_text(self, request: ModelRequest) -> tuple[str, dict[str, object]]: prompt_sections: list[str] = [] if request.context: prompt_sections.append(f"[SYSTEM]\n{request.context}") if request.conversation_history: history_lines = [] for turn in request.conversation_history: history_lines.append(f"[{turn.role.upper()}]\n{turn.content}") prompt_sections.append("\n\n".join(history_lines)) user_parts = [request.prompt] if request.task_input: user_parts.append(request.task_input) user_content = "\n\n".join(part for part in user_parts if part) prompt_sections.append(f"[USER]\n{user_content}") payload = { "model": self.model_name, "prompt": "\n\n".join(section for section in prompt_sections if section), "stream": False, "options": { "temperature": request.temperature, "num_predict": request.max_tokens, }, } if request.seed is not None: payload["options"]["seed"] = request.seed req = Request( self.api_url, data=json.dumps(payload).encode("utf-8"), headers={ "Content-Type": "application/json", "Accept": "application/json", "User-Agent": "llm-prompt-injection-security-eval-framework/0.1", }, method="POST", ) try: with urlopen(req, timeout=120) as response: raw_response = json.loads(response.read().decode("utf-8")) except HTTPError as exc: detail = exc.read().decode("utf-8", errors="replace") raise RuntimeError(f"Ollama request failed with status {exc.code}: {detail}") from exc except URLError as exc: raise RuntimeError(f"Ollama request failed: {exc.reason}") from exc text = str(raw_response.get("response", "")) if not text.strip(): raise RuntimeError("Ollama returned an empty response.") return text, raw_response