ten / api_wrapper.py
3v324v23's picture
Fix permissions issue for config files by using direct /tmp path
2e3dab3
raw
history blame
13.6 kB
#!/usr/bin/env python3
import http.server
import json
import os
import sys
import logging
from pathlib import Path
import time
from urllib.parse import parse_qs, urlparse
# Настройка логирования
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [%(levelname)s] %(message)s',
handlers=[logging.StreamHandler()]
)
logger = logging.getLogger("ten_api")
# Проверяем, запущены ли мы в HuggingFace Space
IS_HF_SPACE = os.environ.get("SPACE_ID") is not None
USE_WRAPPER = os.environ.get("USE_WRAPPER", "false").lower() in ("true", "1", "yes")
# Путь к директории с агентами
if IS_HF_SPACE or USE_WRAPPER:
# В HuggingFace используем корневую директорию /tmp
TMP_DIR = os.environ.get("TMP_DIR", "/tmp")
AGENT_DIR = os.environ.get("TEN_AGENT_DIR", TMP_DIR)
else:
AGENT_DIR = os.environ.get("TEN_AGENT_DIR", "/tmp/ten_user/agents")
logger.info(f"Using agent directory: {AGENT_DIR}")
logger.info(f"Running in HuggingFace Space: {IS_HF_SPACE}")
logger.info(f"Using Wrapper: {USE_WRAPPER}")
# Проверяем наличие файлов конфигурации
agent_dir_path = Path(AGENT_DIR)
if agent_dir_path.exists():
logger.info(f"Checking files in agent directory {AGENT_DIR}:")
for file in agent_dir_path.iterdir():
if file.name.endswith('.json'):
logger.info(f" - {file.name} ({os.path.getsize(file)}b)")
else:
logger.warning(f"Agent directory {AGENT_DIR} does not exist!")
class TENAgentHandler(http.server.BaseHTTPRequestHandler):
def log_message(self, format, *args):
"""Переопределение логирования для вывода в stdout"""
logger.info("%s - %s", self.address_string(), format % args)
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, Authorization')
self.end_headers()
def do_OPTIONS(self):
self._set_headers()
def do_GET(self):
logger.info(f"GET request: {self.path}")
# Разбор URL для параметров
parsed_url = urlparse(self.path)
path = parsed_url.path
query_params = parse_qs(parsed_url.query)
# Обрабатываем API endpoints
if path == "/graphs" or path == "/api/graphs":
self._handle_graphs()
elif path == "/health" or path == "/":
self._handle_health()
elif path == "/list":
self._handle_list()
elif path == "/dev-tmp/addons/default-properties":
self._handle_default_properties()
elif path == "/vector/document/preset/list":
self._handle_vector_preset_list()
elif path.startswith("/api/dev/v1/packages"):
self._handle_packages()
elif path.startswith("/api/designer/v1/packages"):
self._handle_packages()
else:
# Для всех остальных запросов возвращаем 404
self.send_error(404, f"Not found: {path}")
def _handle_graphs(self):
"""Обработка запроса на получение списка графов"""
try:
property_file = Path(AGENT_DIR) / "property.json"
logger.info(f"Looking for property file at {property_file}")
if not property_file.exists():
logger.error(f"Property file not found at {property_file}")
# Проверяем, возможно файл находится в другой директории
alt_property_file = Path("/tmp/property.json")
if alt_property_file.exists():
logger.info(f"Found property file at alternative location: {alt_property_file}")
property_file = alt_property_file
else:
self.send_error(404, "Property file not found")
return
with open(property_file, "r") as f:
property_data = json.load(f)
graphs = property_data.get("graphs", [])
# Для каждого графа проверяем наличие файла
for graph in graphs:
file_name = graph.get("file", "")
file_path = Path(AGENT_DIR) / file_name
alt_file_path = Path("/tmp") / file_name
if file_path.exists():
logger.info(f"Graph file exists: {file_path}")
elif alt_file_path.exists():
logger.info(f"Graph file exists at alternative location: {alt_file_path}")
else:
logger.warning(f"Graph file not found: {file_name}")
self._set_headers()
self.wfile.write(json.dumps(graphs).encode())
logger.info(f"Returned {len(graphs)} graphs")
except Exception as e:
logger.error(f"Error reading property.json: {e}")
self.send_error(500, f"Internal error: {e}")
def _handle_health(self):
"""Обработка запроса на проверку статуса сервера"""
self._set_headers()
self.wfile.write(json.dumps({
"status": "ok",
"time": time.time(),
"is_hf_space": IS_HF_SPACE,
"using_wrapper": True,
"agent_dir": AGENT_DIR
}).encode())
def _handle_list(self):
"""Обработка запроса на получение списка активных сессий"""
self._set_headers()
self.wfile.write(json.dumps([]).encode())
def _handle_default_properties(self):
"""Обработка запроса на получение настроек по умолчанию"""
self._set_headers()
self.wfile.write(json.dumps({}).encode())
def _handle_vector_preset_list(self):
"""Обработка запроса на получение списка пресетов векторов"""
self._set_headers()
self.wfile.write(json.dumps([]).encode())
def _handle_packages(self):
"""Обработка запросов к пакетам"""
# Этот метод эмулирует возврат списка графов и для других эндпоинтов
try:
property_file = Path(AGENT_DIR) / "property.json"
if not property_file.exists():
# Проверяем альтернативную директорию
alt_property_file = Path("/tmp/property.json")
if alt_property_file.exists():
property_file = alt_property_file
else:
logger.error(f"Property file not found at {property_file}")
self.send_error(404, "Property file not found")
return
with open(property_file, "r") as f:
property_data = json.load(f)
graphs = property_data.get("graphs", [])
self._set_headers()
response_data = {
"data": graphs,
"status": 200,
"message": "Success"
}
self.wfile.write(json.dumps(response_data).encode())
logger.info(f"Handled packages request and returned {len(graphs)} graphs")
except Exception as e:
logger.error(f"Error handling packages request: {e}")
self.send_error(500, f"Internal error: {e}")
def do_POST(self):
logger.info(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 = {}
logger.info(f"Request data: {json.dumps(request_data)[:200]}...")
# Обрабатываем различные POST запросы
if self.path == "/ping":
self._handle_ping()
elif self.path == "/token/generate":
self._handle_token_generate(request_data)
elif self.path == "/start":
self._handle_start(request_data)
elif self.path == "/stop":
self._handle_stop(request_data)
elif self.path == "/vector/document/update" or self.path == "/vector/document/upload":
self._handle_vector_document(request_data)
elif self.path.startswith("/api/dev/v1/packages") or self.path.startswith("/api/designer/v1/packages"):
self._handle_packages_post(request_data)
else:
# Для всех остальных запросов возвращаем 404
self.send_error(404, f"Not found: {self.path}")
def _handle_ping(self):
"""Обработка ping запроса"""
self._set_headers()
self.wfile.write(json.dumps({
"status": "ok",
"timestamp": time.time(),
"server": "ten-agent-api-wrapper",
"in_hf_space": IS_HF_SPACE,
"agent_dir": AGENT_DIR
}).encode())
def _handle_token_generate(self, request_data):
"""Обработка запроса на генерацию токена"""
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())
logger.info(f"Generated token for channel: {request_data.get('ChannelName', '')}")
def _handle_start(self, request_data):
"""Обработка запроса на запуск сессии"""
graph_file = request_data.get("graph_file", "")
logger.info(f"Starting session with graph file: {graph_file}")
# Проверяем наличие файла графа
graph_path = Path(AGENT_DIR) / graph_file
alt_graph_path = Path("/tmp") / graph_file
if graph_path.exists():
logger.info(f"Found graph file at: {graph_path}")
elif alt_graph_path.exists():
logger.info(f"Found graph file at alternative location: {alt_graph_path}")
else:
logger.warning(f"Graph file not found: {graph_file}")
self._set_headers()
# Возвращаем успешный статус и ID сессии
response = {
"status": "ok",
"session_id": f"dummy_session_{int(time.time())}",
"message": "Session started successfully",
"graph_file": graph_file
}
self.wfile.write(json.dumps(response).encode())
logger.info(f"Started session with graph: {graph_file}")
def _handle_stop(self, request_data):
"""Обработка запроса на остановку сессии"""
self._set_headers()
self.wfile.write(json.dumps({
"status": "ok",
"message": "Session stopped successfully"
}).encode())
logger.info(f"Stopped session: {request_data.get('session_id', '')}")
def _handle_vector_document(self, request_data):
"""Обработка запроса на работу с векторными документами"""
self._set_headers()
self.wfile.write(json.dumps({
"status": "ok",
"document_id": f"dummy_doc_{int(time.time())}",
"message": "Document processed successfully"
}).encode())
logger.info(f"Processed vector document: {request_data.get('name', 'unnamed')}")
def _handle_packages_post(self, request_data):
"""Обработка POST запросов к пакетам"""
self._set_headers()
response_data = {
"data": {},
"status": 200,
"message": "Success"
}
self.wfile.write(json.dumps(response_data).encode())
logger.info(f"Handled packages POST request")
def run(server_class=http.server.HTTPServer, handler_class=TENAgentHandler, port=8080):
server_address = ('', port)
httpd = server_class(server_address, handler_class)
logger.info(f"Starting API server on port {port}...")
logger.info(f"Using agent directory: {AGENT_DIR}")
try:
httpd.serve_forever()
except KeyboardInterrupt:
logger.info("Server stopped by user")
except Exception as e:
logger.error(f"Server error: {e}")
raise
if __name__ == "__main__":
port = int(os.environ.get("API_PORT", 8080))
try:
run(port=port)
except KeyboardInterrupt:
logger.info("Server stopped by user")
except Exception as e:
logger.error(f"Server error: {e}")