import json import urllib.error import urllib.request from typing import Optional from .generator import Clause, Command, random_command from .lexicon import COLORS, LOCATIONS, OBJECTS, VERBS _SYSTEM_PROMPT = ( "Return ONLY a JSON object — no prose, no markdown, no code fences.\n" "Schema: {{\"clauses\":[{{\"verb\":\"\",\"object\":\"\"," "\"location\":\"\",\"color\":\"\"}}],\"level\":}}\n" "Allowed verb IDs: {verbs}\n" "Allowed object IDs: {objects}\n" "Allowed location IDs: {locations}\n" "Allowed color IDs: {colors}" ) def _system_prompt() -> str: return _SYSTEM_PROMPT.format( verbs=", ".join(VERBS), objects=", ".join(OBJECTS), locations=", ".join(LOCATIONS), colors=", ".join(COLORS), ) def _validate(data: dict, level: int) -> Optional[Command]: clauses_raw = data.get("clauses") if not isinstance(clauses_raw, list) or not clauses_raw: return None clauses = [] for c in clauses_raw: v = c.get("verb") o = c.get("object") loc = c.get("location") col = c.get("color") if v not in VERBS or o not in OBJECTS or loc not in LOCATIONS: return None if col is not None and col not in COLORS: return None clauses.append(Clause(verb_id=v, object_id=o, location_id=loc, color_id=col)) return Command(clauses=clauses, level=level) def llm_command(level: int, endpoint: str, model: str) -> Command: payload = json.dumps({ "model": model, "messages": [ {"role": "system", "content": _system_prompt()}, {"role": "user", "content": f"Generate a level {level} aphasia therapy command."}, ], "temperature": 0.8, }).encode() try: req = urllib.request.Request( endpoint, data=payload, headers={"Content-Type": "application/json"}, method="POST", ) with urllib.request.urlopen(req, timeout=10) as resp: body = json.loads(resp.read()) content = body["choices"][0]["message"]["content"] start = content.find("{") end = content.rfind("}") + 1 if start == -1 or end == 0: raise ValueError("brak JSON w odpowiedzi") data = json.loads(content[start:end]) cmd = _validate(data, level) if cmd is None: raise ValueError("nieznane identyfikatory w odpowiedzi LLM") return cmd except Exception: return random_command(level)