120 lines
4.4 KiB
Python
Raw Permalink Normal View History

import http.server
import socketserver
import json
import sys
import os
import asyncio
import threading
from pathlib import Path
2026-01-01 17:48:45 +08:00
# Add project root to sys.path
PROJECT_ROOT = Path(__file__).parent
if str(PROJECT_ROOT) not in sys.path:
sys.path.insert(0, str(PROJECT_ROOT))
2026-01-01 17:48:45 +08:00
from src.Module.llm.llm import StreamingLLM
from src.Module.tts.tts import StreamingTTS
from src.utils.prompts import get_trigger_prompt
from loguru import logger
# Configuration
PORT = 8000
class AgentRequestHandler(http.server.BaseHTTPRequestHandler):
def _set_headers(self, content_type='application/json'):
self.send_response(200)
self.send_header('Content-type', content_type)
self.end_headers()
def do_POST(self):
if self.path == '/abnormal_trigger':
content_length = int(self.headers['Content-Length'])
post_data = self.rfile.read(content_length)
try:
data = json.loads(post_data.decode('utf-8'))
logger.info(f"Received abnormal_trigger: {data}")
trigger_reason = data.get("trigger_reason")
# context_data = data.get("context_data") # Not used in prompt yet, but available
if not trigger_reason:
self.send_error(400, "Missing trigger_reason")
return
# Generate Audio
audio_data = self.generate_response_audio(trigger_reason)
if audio_data:
self._set_headers('application/octet-stream')
self.wfile.write(audio_data)
logger.info("Sent audio response")
else:
self.send_error(500, "Failed to generate audio")
except json.JSONDecodeError:
self.send_error(400, "Invalid JSON")
except Exception as e:
logger.error(f"Error processing request: {e}")
self.send_error(500, str(e))
else:
self.send_error(404, "Not Found")
def generate_response_audio(self, trigger_reason):
"""
Orchestrates LLM and TTS to produce audio bytes.
"""
system_prompt = get_trigger_prompt(trigger_reason)
logger.info(f"Generated prompt for {trigger_reason}")
# Run async logic synchronously
return asyncio.run(self._async_generate(system_prompt))
async def _async_generate(self, system_prompt):
llm = StreamingLLM()
# Ensure we use a voice that works. 'Cherry' is used in other handlers.
tts = StreamingTTS(voice='Cherry')
full_audio = bytearray()
# Generator for LLM text
def text_generator():
# We use chat with empty message because the system_prompt contains the instruction
# and the trigger implies "user just triggered this state".
# Or we can put a dummy user message like "." or "START".
# Looking at src/handlers/abnormal_trigger.py: it calls llm.chat(message="", system_prompt=system_prompt)
for chunk in llm.chat(message="(用户触发了异常状态,请直接根据系统提示开始说话)", system_prompt=system_prompt):
if chunk.error:
logger.error(f"LLM Error: {chunk.error}")
continue
if chunk.content:
# logger.debug(f"LLM Chunk: {chunk.content}")
yield chunk.content
# Stream TTS from LLM text generator
try:
for audio_chunk in tts.stream_from_generator(text_generator()):
if audio_chunk.error:
logger.error(f"TTS Error: {audio_chunk.error}")
continue
if audio_chunk.data:
full_audio.extend(audio_chunk.data)
return bytes(full_audio)
except Exception as e:
logger.error(f"Generation failed: {e}")
return None
def run(server_class=http.server.HTTPServer, handler_class=AgentRequestHandler, port=PORT):
server_address = ('0.0.0.0', port)
httpd = server_class(server_address, handler_class)
logger.info(f"Starting Agent HTTP Server on port {port}...")
try:
httpd.serve_forever()
except KeyboardInterrupt:
pass
httpd.server_close()
logger.info("Server stopped.")
if __name__ == '__main__':
run()