#!/usr/bin/env python3 import http.server import json import os import sys import datetime import traceback from pathlib import Path # Путь к директории с агентами AGENT_DIR = os.environ.get("TEN_AGENT_DIR", "/tmp/ten_user/agents") class TENAgentHandler(http.server.BaseHTTPRequestHandler): def _set_headers(self, content_type="application/json"): self.send_response(200) self.send_header('Content-type', content_type) self.send_header('Access-Control-Allow-Origin', '*') self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS') self.send_header('Access-Control-Allow-Headers', 'Content-Type, X-Requested-With') self.end_headers() def do_OPTIONS(self): self._set_headers() def log_request(self, code='-', size='-'): # Переопределяем, чтобы не логировать каждый запрос в stderr # особенно health checks, которые могут забивать логи if self.path != '/health': super().log_request(code, size) def do_GET(self): try: print(f"GET request: {self.path}") # Базовые API эндпоинты if self.path in ["/graphs", "/api/graphs"]: # Чтение property.json для получения списка графов try: property_file = Path(AGENT_DIR) / "property.json" if not property_file.exists(): print("⚠️ Property file not found at", property_file) # Создаем базовый файл свойств на месте create_basic_property_file(property_file) with open(property_file, "r") as f: property_data = json.load(f) graphs = property_data.get("graphs", []) print(f"✅ Returning {len(graphs)} graphs from property.json") print(f"Graphs: {json.dumps(graphs, indent=2)}") self._set_headers() self.wfile.write(json.dumps(graphs).encode()) except Exception as e: print(f"Error reading property.json: {e}") traceback.print_exc() # Возвращаем резервный список графов fallback_graphs = [ { "name": "Voice Agent (Fallback)", "description": "Basic voice agent with OpenAI", "file": "voice_agent.json" } ] self._set_headers() self.wfile.write(json.dumps(fallback_graphs).encode()) elif self.path in ["/health", "/"]: # Просто возвращаем, что API сервер работает self._set_headers() self.wfile.write(json.dumps({ "status": "ok", "time": str(datetime.datetime.now()), "message": "TEN Agent API wrapper is running" }).encode()) elif self.path == "/list": # Возвращаем пустой список сессий self._set_headers() self.wfile.write(json.dumps([]).encode()) elif self.path.startswith("/dev-tmp/"): # Обработка всех запросов к /dev-tmp/ self._set_headers() self.wfile.write(json.dumps({}).encode()) elif self.path == "/vector/document/preset/list": # Возвращаем пустой список предустановок векторов self._set_headers() self.wfile.write(json.dumps([]).encode()) # Обработка запросов к API TEN Graph Designer elif self.path.startswith("/api/designer/") or self.path.startswith("/api/dev/"): if "/packages/reload" in self.path: # Возвращаем структурированный ответ с графами property_file = Path(AGENT_DIR) / "property.json" if property_file.exists(): with open(property_file, "r") as f: property_data = json.load(f) graphs = property_data.get("graphs", []) response_data = { "data": graphs, "status": 200, "message": "Success" } else: response_data = { "data": [], "status": 200, "message": "Success" } self._set_headers() self.wfile.write(json.dumps(response_data).encode()) else: self._set_headers() self.wfile.write(json.dumps({"data": [], "status": 200, "message": "Success"}).encode()) else: # Для всех остальных запросов возвращаем 404 self.send_error(404, "Not found") except Exception as e: print(f"Error handling GET request: {e}") traceback.print_exc() self.send_error(500, f"Internal server error: {e}") def do_POST(self): try: print(f"POST request: {self.path}") # Читаем тело запроса content_length = int(self.headers['Content-Length']) if 'Content-Length' in self.headers else 0 post_data = self.rfile.read(content_length) try: request_data = json.loads(post_data) if content_length > 0 else {} except json.JSONDecodeError: request_data = {} print(f"Request data: {request_data}") if self.path == "/ping": # Для ping запросов просто возвращаем успешный статус self._set_headers() self.wfile.write(json.dumps({"status": "ok"}).encode()) elif self.path == "/token/generate": # Для запросов на генерацию токена возвращаем простой токен # Это нужно для Agora SDK self._set_headers() response = { "token": "dummy_token_for_agora", "request_id": request_data.get("RequestId", ""), "channel_name": request_data.get("ChannelName", ""), "uid": request_data.get("Uid", 0) } self.wfile.write(json.dumps(response).encode()) elif self.path == "/start": # Для запросов на запуск сессии возвращаем успешный статус self._set_headers() # Возвращаем идентификатор сессии для совместимости session_id = "fallback-session-" + datetime.datetime.now().strftime("%Y%m%d%H%M%S") self.wfile.write(json.dumps({ "status": "ok", "session_id": session_id }).encode()) elif self.path == "/stop": # Для запросов на остановку сессии возвращаем успешный статус self._set_headers() self.wfile.write(json.dumps({"status": "ok"}).encode()) elif self.path.startswith("/vector/document/"): # Для запросов на обновление или загрузку документов возвращаем успешный статус self._set_headers() self.wfile.write(json.dumps({"status": "ok", "message": "Operation completed"}).encode()) # API для TEN Graph Designer elif self.path.startswith("/api/designer/") or self.path.startswith("/api/dev/"): self._set_headers() self.wfile.write(json.dumps({"data": {}, "status": 200, "message": "Success"}).encode()) else: # Для всех остальных запросов возвращаем 404 self.send_error(404, "Not found") except Exception as e: print(f"Error handling POST request: {e}") traceback.print_exc() self.send_error(500, f"Internal server error: {e}") def create_basic_property_file(filepath): """Создает базовый property.json файл""" print(f"Creating basic property.json file at {filepath}") # Создаем базовый property.json property_data = { "name": "TEN Agent Example", "version": "0.0.1", "extensions": ["openai_chatgpt"], "description": "A basic voice agent with OpenAI", "graphs": [ { "name": "Voice Agent", "description": "Basic voice agent with OpenAI", "file": "voice_agent.json" }, { "name": "Chat Agent", "description": "Simple chat agent", "file": "chat_agent.json" } ] } try: # Убедимся, что директория существует filepath.parent.mkdir(exist_ok=True, parents=True) with open(filepath, "w") as f: json.dump(property_data, f, indent=2) print(f"✅ Created property.json at {filepath}") # Также создаем базовые файлы графов create_basic_graph_files(filepath.parent) return True except Exception as e: print(f"❌ Error creating property.json: {e}") traceback.print_exc() return False def create_basic_graph_files(dir_path): """Создает базовые файлы графов""" print(f"Creating basic graph files in {dir_path}") # Создаем voice_agent.json voice_agent = { "_ten": {"version": "0.0.1"}, "nodes": [ { "id": "start", "type": "start", "data": {"x": 100, "y": 100} }, { "id": "openai_chatgpt", "type": "openai_chatgpt", "data": { "x": 300, "y": 200, "properties": { "model": "gpt-3.5-turbo", "temperature": 0.7, "system_prompt": "You are a helpful assistant." } } }, { "id": "end", "type": "end", "data": {"x": 500, "y": 100} } ], "edges": [ { "id": "start_to_chatgpt", "source": "start", "target": "openai_chatgpt" }, { "id": "chatgpt_to_end", "source": "openai_chatgpt", "target": "end" } ], "groups": [], "templates": [], "root": "start" } try: with open(dir_path / "voice_agent.json", "w") as f: json.dump(voice_agent, f, indent=2) print(f"✅ Created voice_agent.json") # Создаем chat_agent.json (аналогичный voice_agent.json) chat_agent = dict(voice_agent) chat_agent["nodes"][1]["data"]["properties"]["system_prompt"] = "You are a helpful chat assistant." with open(dir_path / "chat_agent.json", "w") as f: json.dump(chat_agent, f, indent=2) print(f"✅ Created chat_agent.json") # Создаем manifest.json manifest = { "_ten": {"version": "0.0.1"}, "name": "default", "agents": [ { "name": "voice_agent", "description": "A simple voice agent", "type": "voice" }, { "name": "chat_agent", "description": "A text chat agent", "type": "chat" } ] } with open(dir_path / "manifest.json", "w") as f: json.dump(manifest, f, indent=2) print(f"✅ Created manifest.json") return True except Exception as e: print(f"❌ Error creating graph files: {e}") traceback.print_exc() return False def run(server_class=http.server.HTTPServer, handler_class=TENAgentHandler, port=8080): server_address = ('', port) httpd = server_class(server_address, handler_class) print(f"Starting API server on port {port}...") print(f"Using agent directory: {AGENT_DIR}") # Проверяем, что директория с агентами существует agent_dir_path = Path(AGENT_DIR) if not agent_dir_path.exists(): print(f"WARNING: Agent directory {AGENT_DIR} does not exist, creating it...") agent_dir_path.mkdir(exist_ok=True, parents=True) # Проверка наличия необходимых файлов property_file = agent_dir_path / "property.json" if not property_file.exists(): print(f"WARNING: property.json not found in {AGENT_DIR}") create_basic_property_file(property_file) else: print(f"✅ Using existing property.json at {property_file}") # Выводим содержимое try: with open(property_file, "r") as f: property_data = json.load(f) print(f"Property.json content: {json.dumps(property_data, indent=2)}") except Exception as e: print(f"Error reading property.json: {e}") try: print(f"✅ API server is ready to receive requests!") httpd.serve_forever() except KeyboardInterrupt: print("Shutting down API server...") except Exception as e: print(f"Error in API server: {e}") traceback.print_exc() if __name__ == "__main__": port = int(os.environ.get("API_PORT", 8080)) run(port=port)