Some checks are pending
Check Python Version Consistency / Check Python Version (push) Waiting to run
139 lines
4.3 KiB
Python
139 lines
4.3 KiB
Python
"""
|
|
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()
|