69 lines
1.9 KiB
Python
69 lines
1.9 KiB
Python
"""Router: mic/WebSocket -> brain -> TTS -> avatar -> RTMP streaming glue."""
|
|
import os, json, queue, threading, time
|
|
from pathlib import Path
|
|
from flask import Flask, request, jsonify
|
|
from flask_sock import Sock
|
|
import requests
|
|
|
|
app = Flask(__name__)
|
|
sock = Sock(app)
|
|
|
|
BRAIN = os.getenv("BRAIN_URL", "http://brain:11434")
|
|
TTS = os.getenv("TTS_URL", "http://tts:5002")
|
|
PERSONA = os.getenv("PERSONA", "/app/persona.yaml")
|
|
|
|
response_q = queue.Queue()
|
|
|
|
def build_prompt(user_text):
|
|
return user_text
|
|
|
|
def brain_reply(prompt):
|
|
try:
|
|
r = requests.post(f"{BRAIN}/api/generate", json={"model":"llama3.2:1b","prompt":prompt,"stream":False}, timeout=20)
|
|
return r.json().get("response","")
|
|
except Exception as e:
|
|
return f"*static from the aether* {e}"
|
|
|
|
def tts_synth(text):
|
|
try:
|
|
r = requests.post(f"{TTS}/synthesize", json={"text": text}, timeout=20)
|
|
return r.content
|
|
except Exception:
|
|
return b""
|
|
|
|
def run_job(text):
|
|
prompt = build_prompt(text)
|
|
reply = brain_reply(prompt)
|
|
audio = tts_synth(reply)
|
|
response_q.put({"text": reply, "audio": audio})
|
|
|
|
@app.post("/say")
|
|
def say():
|
|
data = request.get_json(force=True)
|
|
text = data.get("text", "")
|
|
if not text:
|
|
return jsonify({"error": "text required"}), 400
|
|
threading.Thread(target=run_job, args=(text,), daemon=True).start()
|
|
return jsonify({"queued": True})
|
|
|
|
@app.get("/health")
|
|
def health():
|
|
return {"status": "ok", "queued": response_q.qsize()}
|
|
|
|
@sock.route("/ws")
|
|
def ws(ws):
|
|
while True:
|
|
msg = ws.receive()
|
|
if msg is None:
|
|
break
|
|
try:
|
|
data = json.loads(msg)
|
|
except Exception:
|
|
continue
|
|
if data.get("type") == "text":
|
|
run_job(data.get("text",""))
|
|
result = response_q.get()
|
|
ws.send(json.dumps(result))
|
|
|
|
if __name__ == "__main__":
|
|
app.run(host="0.0.0.0", port=8787) |