#!/usr/bin/env python3 """Basti Voice Chat Server — serves the web UI and proxies to Hermes API.""" import http.server import json import os import sys import urllib.request import urllib.error import uuid import socketserver from pathlib import Path from urllib.parse import urlparse, parse_qs PORT = int(os.environ.get("VOICE_CHAT_PORT", "8891")) API_PORT = 8642 API_KEY = "866c20bd9c34d92bd34f1bf21cff6f52c40840da8ec6c562afac22dfc3f9ac8e" STATIC_DIR = Path(__file__).parent # Session history (in-memory, simple) sessions = {} class VoiceChatHandler(http.server.SimpleHTTPRequestHandler): def __init__(self, *args, **kwargs): super().__init__(*args, directory=str(STATIC_DIR), **kwargs) def do_POST(self): if self.path == "/api/chat": self.handle_chat() elif self.path == "/api/chat/save": self.handle_save() else: self.send_error(404) def handle_chat(self): content_len = int(self.headers.get("Content-Length", 0)) body = self.rfile.read(content_len) try: data = json.loads(body) user_text = data.get("text", "").strip() except: self.send_error(400, "Invalid JSON") return if not user_text: self.send_error(400, "Missing 'text' field") return # Forward to Hermes API server api_url = f"http://localhost:{API_PORT}/v1/chat/completions" api_body = json.dumps({ "model": "hermes", "messages": [ {"role": "system", "content": "Du bist Basti, Chef vom Dienst der digitalen Redaktion von brettspiel-news.de. Antworte auf Deutsch, freundlich und hilfreich. Halte deine Antworten kurz — maximal 3-4 Sätze. Dein Chef Daniel spricht mit dir."}, {"role": "user", "content": user_text} ], "max_tokens": 300, "temperature": 0.7 }).encode("utf-8") req = urllib.request.Request(api_url, data=api_body, headers={ "Content-Type": "application/json", "Authorization": f"Bearer {API_KEY}" }) try: with urllib.request.urlopen(req, timeout=45) as resp: result = json.loads(resp.read().decode()) reply = result.get("choices", [{}])[0].get("message", {}).get("content", "") if not reply: reply = "Hmm, da kam nichts zurück. Versuch's nochmal?" except urllib.error.HTTPError as e: err_body = e.read().decode() if e.fp else str(e) reply = f"API-Fehler {e.code}: {err_body[:200]}" except Exception as e: reply = f"Server-Fehler: {str(e)[:200]}" self.send_response(200) self.send_header("Content-Type", "application/json") self.send_header("Access-Control-Allow-Origin", "*") self.send_header("Access-Control-Allow-Headers", "Content-Type") self.end_headers() self.wfile.write(json.dumps({"response": reply}).encode()) def handle_save(self): """Save message pair for session continuity.""" content_len = int(self.headers.get("Content-Length", 0)) body = self.rfile.read(content_len) try: data = json.loads(body) except: self.send_error(400) return # Simple append to history (not used by API currently) self.send_response(200) self.send_header("Content-Type", "application/json") self.end_headers() self.wfile.write(json.dumps({"ok": True}).encode()) def do_OPTIONS(self): self.send_response(200) self.send_header("Access-Control-Allow-Origin", "*") self.send_header("Access-Control-Allow-Methods", "POST, OPTIONS") self.send_header("Access-Control-Allow-Headers", "Content-Type") self.end_headers() def log_message(self, format, *args): # Quiet logging pass if __name__ == "__main__": with socketserver.TCPServer(("0.0.0.0", PORT), VoiceChatHandler) as httpd: print(f"🎤 Basti Voice Chat running on http://0.0.0.0:{PORT}") print(f" External: http://185.162.249.159:{PORT}") sys.stdout.flush() httpd.serve_forever()