From d053e7c16ca1186a460d44dc7111f57f2b5323e9 Mon Sep 17 00:00:00 2001 From: chris Date: Sat, 25 Jul 2026 08:06:29 +0000 Subject: [PATCH] chore: update emma/emma_server.py via opax-mcp --- emma/emma_server.py | 138 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 emma/emma_server.py diff --git a/emma/emma_server.py b/emma/emma_server.py new file mode 100644 index 0000000..f0a6529 --- /dev/null +++ b/emma/emma_server.py @@ -0,0 +1,138 @@ +""" +emma_server.py — HTTP-wrapper rundt Emma-agenten. + +Start: cd /home/chris_christiansen/OSVauco && python -m emma.emma_server +Port: 8765 + +Endepunkter: + POST /ask {"prompt": "...", "history": [...]} → {"response": "..."} + GET /health → {"status": "ok", "model": "...", "memory_patterns": N} + GET /stats → Flynn-statistikk + +Modell-prioritet (iht. llm-stack.md): + 1. qwen2.5-coder:7b (kode/repo-arbeid, tools) + 2. qwen2.5:7b (generell, tools) + 3. gemma3:4b (fallback, ingen tools) + +Regler (iht. emma_identity.py + HANDOFF.md): + - Emma rapporterer kun til Chris Christiansen + - Guardrails kan ikke deaktiveres + - Aldri deploy uten eksplisitt GO fra Chris + - Gitea er primær Git +""" + +import sys +import json +import logging +from pathlib import Path +from http.server import BaseHTTPRequestHandler, HTTPServer + +sys.path.insert(0, str(Path(__file__).parent.parent)) + +from emma.emma_backend_ollama import OllamaBackend +from emma.emma_resonance_persistent import PersistentMorphicMemory +from emma.emma_mcot import EmmaMCoTAgent +from emma.emma_guardrails import EmmaGuardrails +from emma.emma_flynn import FlynnTracker + +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s [emma-server] %(levelname)s %(message)s", +) +log = logging.getLogger("emma-server") + +# Modell-prioritet iht. llm-stack.md +DEFAULT_MODEL = "qwen2.5-coder:7b" +DEFAULT_EMBED = "qwen2.5:3b" # brukes til morfisk resonans-embedding +PORT = 8765 + + +def _build_agent(model: str = DEFAULT_MODEL) -> tuple: + llm = OllamaBackend(model=model, embed_model=DEFAULT_EMBED) + memory = PersistentMorphicMemory() + guardrails = EmmaGuardrails() + agent = EmmaMCoTAgent(llm=llm, memory=memory) + return agent, guardrails, memory + + +# Bygg agent ved oppstart +log.info(f"Laster Emma med modell={DEFAULT_MODEL} ...") +_agent, _guardrails, _memory = _build_agent() +log.info(f"Emma klar | {len(_memory)} morfiske m\u00f8nstre lastet") + + +class EmmaHandler(BaseHTTPRequestHandler): + def log_message(self, format, *args): # noqa: A002 + log.info(f"{self.address_string()} {format % args}") + + def _send_json(self, code: int, data: dict): + body = json.dumps(data, ensure_ascii=False).encode() + self.send_response(code) + self.send_header("Content-Type", "application/json; charset=utf-8") + self.send_header("Content-Length", str(len(body))) + self.end_headers() + self.wfile.write(body) + + def _read_body(self) -> dict: + length = int(self.headers.get("Content-Length", 0)) + raw = self.rfile.read(length) if length else b"{}" + try: + return json.loads(raw) + except json.JSONDecodeError: + return {} + + def do_GET(self): + if self.path == "/health": + self._send_json(200, { + "status": "ok", + "model": DEFAULT_MODEL, + "memory_patterns": len(_memory), + "agent": "EmmaMCoTAgent", + }) + elif self.path == "/stats": + stats = FlynnTracker().summary() or {} + self._send_json(200, stats) + else: + self._send_json(404, {"error": "not found"}) + + def do_POST(self): + if self.path != "/ask": + self._send_json(404, {"error": "not found"}) + return + + body = self._read_body() + prompt = (body.get("prompt") or "").strip() + history = body.get("history") or [] + + if not prompt: + self._send_json(400, {"error": "prompt er p\u00e5krevd"}) + return + + # Guardrail-sjekk + ok, reason = _guardrails.can_execute(prompt) + if not ok: + log.warning(f"Guardrail blokkerte: {reason}") + self._send_json(403, {"error": reason, "blocked": True}) + return + + log.info(f"Sp\u00f8rsm\u00e5l: {prompt[:80]}") + try: + response = _agent.run(prompt, history) + self._send_json(200, {"response": response, "model": DEFAULT_MODEL}) + except Exception as e: + log.error(f"Agent-feil: {e}") + self._send_json(500, {"error": str(e)}) + + +def main(): + server = HTTPServer(("0.0.0.0", PORT), EmmaHandler) + log.info(f"Emma HTTP-server lytter p\u00e5 port {PORT}") + try: + server.serve_forever() + except KeyboardInterrupt: + log.info("Emma server stoppet") + server.server_close() + + +if __name__ == "__main__": + main()