| |
| import os |
| import subprocess |
| import sys |
| import time |
| import json |
| from pathlib import Path |
| import signal |
| import threading |
| import shutil |
| import logging |
| import urllib.request |
| import urllib.error |
| import tempfile |
| import http.server |
| import socketserver |
| from typing import Dict, Any, Optional, Union |
|
|
| |
| logging.basicConfig(level=logging.INFO, |
| format='%(asctime)s [%(levelname)s] %(message)s', |
| datefmt='%Y-%m-%d %H:%M:%S') |
|
|
| logger = logging.getLogger('ten-agent') |
|
|
| |
| AGENTS_DIR = Path("/app/agents") |
| PROPERTY_JSON = AGENTS_DIR / "property.json" |
| MANIFEST_JSON = AGENTS_DIR / "manifest.json" |
| VOICE_AGENT_JSON = AGENTS_DIR / "voice_agent.json" |
| CHAT_AGENT_JSON = AGENTS_DIR / "chat_agent.json" |
| API_BINARY = Path("/app/server/bin/api") |
| PLAYGROUND_DIR = Path("/app/playground") |
| BACKUP_DIR = Path("/app/backup") |
|
|
| |
| PROXY_PORT = 9090 |
| API_PORT = 8080 |
|
|
| def ensure_directory_permissions(directory_path): |
| """Обеспечиваем правильные разрешения для директории""" |
| directory = Path(directory_path) |
| if not directory.exists(): |
| logger.info(f"Создание директории {directory}") |
| directory.mkdir(parents=True, exist_ok=True) |
| |
| |
| subprocess.run(["chmod", "-R", "777", str(directory)]) |
| logger.info(f"Права доступа для {directory} установлены") |
|
|
| def backup_file(filepath): |
| """Создает резервную копию файла""" |
| src_path = Path(filepath) |
| if not src_path.exists(): |
| logger.warning(f"Невозможно создать резервную копию: {filepath} не существует") |
| return |
| |
| BACKUP_DIR.mkdir(parents=True, exist_ok=True) |
| dest_path = BACKUP_DIR / f"{src_path.name}.bak" |
| |
| try: |
| shutil.copy2(src_path, dest_path) |
| logger.info(f"Резервная копия создана: {dest_path}") |
| except Exception as e: |
| logger.error(f"Ошибка при создании резервной копии {filepath}: {e}") |
|
|
| def update_property_json(): |
| """Проверяет существующий property.json и добавляет поле predefined_graphs, если его нет""" |
| if not PROPERTY_JSON.exists(): |
| logger.info(f"{PROPERTY_JSON} не существует, нет необходимости в обновлении") |
| return False |
| |
| try: |
| with open(PROPERTY_JSON, 'r') as f: |
| data = json.load(f) |
| |
| need_update = False |
| |
| |
| if "predefined_graphs" not in data: |
| logger.info(f"{PROPERTY_JSON} не содержит поле predefined_graphs") |
| need_update = True |
| elif not isinstance(data["predefined_graphs"], list): |
| logger.warning(f"{PROPERTY_JSON} содержит predefined_graphs, но это не массив") |
| need_update = True |
| |
| if not need_update: |
| logger.info(f"{PROPERTY_JSON} уже содержит корректное поле predefined_graphs") |
| return False |
| |
| |
| backup_file(PROPERTY_JSON) |
| |
| |
| property_data = { |
| "_ten": {}, |
| "name": "TEN Agent Example", |
| "version": "0.0.1", |
| "extensions": ["openai_chatgpt"], |
| "description": "A basic voice agent with OpenAI", |
| "predefined_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" |
| } |
| ] |
| } |
| |
| |
| for key, value in data.items(): |
| if key not in property_data and key != "predefined_graphs" and key != "graphs": |
| property_data[key] = value |
| |
| |
| property_data["graphs"] = property_data["predefined_graphs"] |
| |
| |
| with open(PROPERTY_JSON, 'w') as f: |
| json.dump(property_data, f, indent=2) |
| |
| logger.info(f"{PROPERTY_JSON} успешно обновлен с корректным полем predefined_graphs") |
| return True |
| except Exception as e: |
| logger.error(f"Ошибка при обновлении {PROPERTY_JSON}: {e}") |
| return False |
|
|
| def check_and_create_property_json(): |
| """Проверяет наличие property.json и создает его при необходимости""" |
| if not PROPERTY_JSON.exists(): |
| logger.warning(f"{PROPERTY_JSON} не найден, создаем файл...") |
| |
| property_data = { |
| "_ten": {}, |
| "name": "TEN Agent Example", |
| "version": "0.0.1", |
| "extensions": ["openai_chatgpt"], |
| "description": "A basic voice agent with OpenAI", |
| "predefined_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" |
| } |
| ], |
| "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" |
| } |
| ] |
| } |
| |
| |
| PROPERTY_JSON.parent.mkdir(parents=True, exist_ok=True) |
| |
| |
| with tempfile.NamedTemporaryFile(mode='w', delete=False) as temp_file: |
| json.dump(property_data, temp_file, indent=2) |
| temp_path = temp_file.name |
| |
| |
| try: |
| shutil.copy2(temp_path, PROPERTY_JSON) |
| os.chmod(PROPERTY_JSON, 0o666) |
| logger.info(f"Файл {PROPERTY_JSON} создан успешно") |
| except Exception as e: |
| logger.error(f"Ошибка при создании {PROPERTY_JSON}: {e}") |
| finally: |
| os.unlink(temp_path) |
|
|
| def check_and_create_agent_files(): |
| """Проверяет наличие всех необходимых файлов агентов и создает их при необходимости""" |
| |
| |
| if not MANIFEST_JSON.exists(): |
| manifest_data = { |
| "name": "default", |
| "agents": [ |
| { |
| "name": "voice_agent", |
| "description": "A simple voice agent" |
| }, |
| { |
| "name": "chat_agent", |
| "description": "A text chat agent" |
| } |
| ] |
| } |
| |
| with open(MANIFEST_JSON, 'w') as f: |
| json.dump(manifest_data, f, indent=2) |
| os.chmod(MANIFEST_JSON, 0o666) |
| logger.info(f"Файл {MANIFEST_JSON} создан") |
| |
| |
| if not VOICE_AGENT_JSON.exists(): |
| voice_agent_data = { |
| "nodes": [], |
| "edges": [], |
| "groups": [], |
| "templates": [], |
| "root": None |
| } |
| |
| with open(VOICE_AGENT_JSON, 'w') as f: |
| json.dump(voice_agent_data, f, indent=2) |
| os.chmod(VOICE_AGENT_JSON, 0o666) |
| logger.info(f"Файл {VOICE_AGENT_JSON} создан") |
| |
| |
| if not CHAT_AGENT_JSON.exists(): |
| chat_agent_data = { |
| "nodes": [], |
| "edges": [], |
| "groups": [], |
| "templates": [], |
| "root": None |
| } |
| |
| with open(CHAT_AGENT_JSON, 'w') as f: |
| json.dump(chat_agent_data, f, indent=2) |
| os.chmod(CHAT_AGENT_JSON, 0o666) |
| logger.info(f"Файл {CHAT_AGENT_JSON} создан") |
|
|
| def check_files(): |
| """Проверяет и выводит информацию о важных файлах""" |
| files_to_check = [ |
| PROPERTY_JSON, |
| MANIFEST_JSON, |
| VOICE_AGENT_JSON, |
| CHAT_AGENT_JSON, |
| API_BINARY |
| ] |
| |
| logger.info("=== Проверка критических файлов ===") |
| for file_path in files_to_check: |
| path = Path(file_path) |
| if path.exists(): |
| if path.is_file(): |
| size = path.stat().st_size |
| logger.info(f"✅ {file_path} (размер: {size} байт)") |
| |
| |
| if str(file_path).endswith('.json'): |
| try: |
| with open(file_path, 'r') as f: |
| content = json.load(f) |
| logger.info(f" Содержимое: {json.dumps(content, indent=2)}") |
| except Exception as e: |
| logger.error(f" Ошибка чтения JSON: {e}") |
| else: |
| logger.warning(f"❌ {file_path} (это директория, а не файл)") |
| else: |
| logger.error(f"❌ {file_path} (файл не найден)") |
| |
| logger.info("=== Проверка структуры директорий ===") |
| logger.info(f"Содержимое {AGENTS_DIR}:") |
| subprocess.run(["ls", "-la", str(AGENTS_DIR)]) |
| |
| logger.info("Проверка прав доступа:") |
| subprocess.run(["stat", str(AGENTS_DIR)]) |
| subprocess.run(["stat", str(PROPERTY_JSON)]) |
|
|
| def analyze_api_response(response_data): |
| """Анализирует ответ API и выводит диагностическую информацию""" |
| try: |
| |
| if not response_data or response_data.strip() == "": |
| logger.error("API вернул пустой ответ") |
| return None |
| |
| json_data = json.loads(response_data) |
| |
| |
| if isinstance(json_data, list): |
| logger.info(f"API вернул список с {len(json_data)} элементами") |
| if len(json_data) > 0: |
| logger.info(f"Структура первого элемента: {json.dumps(json_data[0], indent=2)}") |
| return json_data |
| elif isinstance(json_data, dict): |
| logger.info(f"API вернул словарь с ключами: {list(json_data.keys())}") |
| |
| if "code" in json_data: |
| logger.error(f"API вернул ошибку с кодом: {json_data['code']}") |
| if "msg" in json_data: |
| logger.error(f"Сообщение ошибки: {json_data['msg']}") |
| return json_data |
| else: |
| logger.warning(f"API вернул неожиданный тип данных: {type(json_data)}") |
| return json_data |
| except json.JSONDecodeError as e: |
| logger.error(f"Ошибка декодирования JSON: {e}") |
| logger.error(f"Сырые данные: {response_data}") |
| return None |
| except Exception as e: |
| logger.error(f"Ошибка при анализе ответа API: {e}") |
| return None |
|
|
| def test_api(): |
| """Делает запрос к API для получения списка графов""" |
| logger.info("=== Тестирование API ===") |
| try: |
| |
| time.sleep(3) |
| |
| |
| try: |
| proxy_url = f"http://localhost:{PROXY_PORT}/graphs" |
| logger.info(f"Проверка прокси-сервера по адресу: {proxy_url}") |
| with urllib.request.urlopen(proxy_url) as response: |
| data = response.read().decode('utf-8') |
| logger.info(f"Ответ прокси-сервера: {data}") |
| json_data = analyze_api_response(data) |
| if json_data and isinstance(json_data, list) and len(json_data) > 0: |
| logger.info("Прокси-сервер возвращает корректные данные о графах") |
| return |
| except Exception as e: |
| logger.warning(f"Ошибка при проверке прокси-сервера: {e}") |
| |
| |
| with urllib.request.urlopen("http://localhost:8080/graphs") as response: |
| data = response.read().decode('utf-8') |
| logger.info(f"Ответ /graphs: {data}") |
| |
| |
| json_data = analyze_api_response(data) |
| |
| |
| if json_data is None: |
| logger.error("Не удалось проанализировать ответ API") |
| |
| generate_fresh_property_json() |
| restart_api_server() |
| elif isinstance(json_data, list): |
| if len(json_data) > 0: |
| logger.info(f"API вернул {len(json_data)} графов") |
| else: |
| logger.warning("API вернул пустой список графов") |
| |
| if PROPERTY_JSON.exists(): |
| logger.info("Пробуем обновить существующий property.json...") |
| if update_property_json(): |
| restart_api_server() |
| elif isinstance(json_data, dict) and "code" in json_data: |
| logger.warning("API вернул ошибку, исправляем property.json") |
| generate_fresh_property_json() |
| restart_api_server() |
| except urllib.error.URLError as e: |
| logger.error(f"Ошибка запроса к API: {e}") |
| except Exception as e: |
| logger.error(f"Неизвестная ошибка при запросе к API: {e}") |
|
|
| def generate_fresh_property_json(): |
| """Создает полностью новый property.json с оптимальной структурой""" |
| logger.info("Создание нового property.json с оптимальной структурой") |
| |
| |
| if PROPERTY_JSON.exists(): |
| backup_file(PROPERTY_JSON) |
| |
| |
| property_data = { |
| "_ten": {}, |
| "name": "TEN Agent Example", |
| "version": "0.0.1", |
| "extensions": ["openai_chatgpt"], |
| "description": "A basic voice agent with OpenAI", |
| "predefined_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" |
| } |
| ], |
| "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" |
| } |
| ] |
| } |
| |
| |
| with tempfile.NamedTemporaryFile(mode='w', delete=False) as temp_file: |
| json.dump(property_data, temp_file, indent=2) |
| temp_path = temp_file.name |
| |
| |
| try: |
| shutil.copy2(temp_path, PROPERTY_JSON) |
| try: |
| os.chmod(PROPERTY_JSON, 0o666) |
| except Exception as e: |
| logger.warning(f"Невозможно изменить права доступа для {PROPERTY_JSON}: {e}") |
| logger.info(f"Новый файл {PROPERTY_JSON} создан успешно") |
| except Exception as e: |
| logger.error(f"Ошибка при создании {PROPERTY_JSON}: {e}") |
| finally: |
| os.unlink(temp_path) |
|
|
| def restart_api_server(): |
| """Перезапускает API сервер""" |
| logger.info("Перезапускаем API сервер...") |
| |
| |
| try: |
| subprocess.run(["pkill", "-f", str(API_BINARY)]) |
| time.sleep(1) |
| except Exception as e: |
| logger.warning(f"Ошибка при остановке API сервера: {e}") |
| |
| |
| try: |
| new_process = subprocess.Popen([str(API_BINARY)]) |
| logger.info("API сервер успешно перезапущен") |
| time.sleep(2) |
| return new_process |
| except Exception as e: |
| logger.error(f"Ошибка при запуске API сервера: {e}") |
| return None |
|
|
| def run_simple_proxy(): |
| """Запускает встроенный прокси-сервер""" |
| import http.server |
| import socketserver |
| import json |
| |
| |
| GRAPHS_DATA = [ |
| { |
| "name": "Voice Agent", |
| "description": "Voice Agent with OpenAI", |
| "file": "voice_agent.json", |
| "id": "voice_agent", |
| "package": "default" |
| }, |
| { |
| "name": "Chat Agent", |
| "description": "Chat Agent", |
| "file": "chat_agent.json", |
| "id": "chat_agent", |
| "package": "default" |
| } |
| ] |
| |
| |
| DESIGNER_DATA = { |
| "success": True, |
| "packages": [ |
| { |
| "name": "default", |
| "description": "Default package", |
| "graphs": [ |
| { |
| "name": "Voice Agent", |
| "description": "Voice Agent with OpenAI", |
| "file": "voice_agent.json", |
| "id": "voice_agent", |
| "package": "default" |
| }, |
| { |
| "name": "Chat Agent", |
| "description": "Chat Agent", |
| "file": "chat_agent.json", |
| "id": "chat_agent", |
| "package": "default" |
| } |
| ] |
| } |
| ] |
| } |
| |
| class SimpleProxyHandler(http.server.BaseHTTPRequestHandler): |
| def do_GET(self): |
| logger.info(f"PROXY: GET запрос: {self.path}") |
| |
| |
| if self.path == "/graphs": |
| logger.info("PROXY: Возвращаем заготовленные данные о графах") |
| self._send_response(200, json.dumps(GRAPHS_DATA)) |
| return |
| |
| |
| if self.path.startswith("/api/designer/") or self.path.startswith("/api/dev/"): |
| logger.info(f"PROXY: Обработка запроса к Designer API: {self.path}") |
| self._send_response(200, json.dumps(DESIGNER_DATA)) |
| return |
| |
| |
| try: |
| self._proxy_to_api("GET") |
| except Exception as e: |
| logger.error(f"PROXY: Ошибка при проксировании GET запроса: {e}") |
| |
| self._send_response(200, json.dumps({"success": True})) |
| |
| def do_POST(self): |
| logger.info(f"PROXY: POST запрос: {self.path}") |
| |
| |
| if self.path.startswith("/api/designer/") or self.path.startswith("/api/dev/"): |
| logger.info(f"PROXY: Обработка POST запроса к Designer API: {self.path}") |
| self._send_response(200, json.dumps({"success": True})) |
| return |
| |
| |
| try: |
| self._proxy_to_api("POST") |
| except Exception as e: |
| logger.error(f"PROXY: Ошибка при проксировании POST запроса: {e}") |
| |
| self._send_response(200, json.dumps({"success": True})) |
| |
| def do_OPTIONS(self): |
| logger.info(f"PROXY: OPTIONS запрос: {self.path}") |
| self.send_response(200) |
| 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') |
| self.end_headers() |
| |
| def _proxy_to_api(self, method): |
| """Проксирует запрос к API серверу""" |
| url = f"http://localhost:{API_PORT}{self.path}" |
| logger.info(f"PROXY: Проксирование запроса к API: {url}") |
| |
| req = urllib.request.Request(url, method=method) |
| |
| |
| for header, value in self.headers.items(): |
| if header.lower() not in ["host", "content-length"]: |
| req.add_header(header, value) |
| |
| |
| if method == "POST": |
| content_length = int(self.headers.get('Content-Length', 0)) |
| body = self.rfile.read(content_length) |
| req.data = body |
| |
| |
| with urllib.request.urlopen(req) as response: |
| |
| self.send_response(response.status) |
| |
| |
| for header, value in response.getheaders(): |
| if header.lower() != "transfer-encoding": |
| self.send_header(header, value) |
| |
| |
| self.send_header('Access-Control-Allow-Origin', '*') |
| self.end_headers() |
| |
| |
| self.wfile.write(response.read()) |
| |
| def _send_response(self, status_code, data): |
| """Отправляет ответ с указанным статусом и данными""" |
| self.send_response(status_code) |
| self.send_header('Content-Type', 'application/json') |
| 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') |
| self.end_headers() |
| |
| if isinstance(data, str): |
| self.wfile.write(data.encode('utf-8')) |
| else: |
| self.wfile.write(data) |
| |
| def log_message(self, format, *args): |
| """Перенаправляем логи сервера в наш логгер""" |
| logger.debug(f"PROXY: {self.address_string()} - {format % args}") |
| |
| |
| try: |
| |
| try: |
| server = socketserver.TCPServer(("", PROXY_PORT), SimpleProxyHandler) |
| logger.info(f"Встроенный прокси-сервер успешно запущен на порту {PROXY_PORT}") |
| server.serve_forever() |
| except OSError as e: |
| |
| alt_port = PROXY_PORT + 1 |
| logger.warning(f"Не удалось запустить прокси на порту {PROXY_PORT}, пробуем порт {alt_port}") |
| try: |
| server = socketserver.TCPServer(("", alt_port), SimpleProxyHandler) |
| logger.info(f"Встроенный прокси-сервер успешно запущен на порту {alt_port}") |
| |
| os.environ["NEXT_PUBLIC_DESIGNER_API_URL"] = f"http://localhost:{alt_port}" |
| os.environ["AGENT_SERVER_URL"] = f"http://localhost:{alt_port}" |
| server.serve_forever() |
| except Exception as inner_e: |
| logger.error(f"Не удалось запустить прокси-сервер даже на альтернативном порту: {inner_e}") |
| |
| try: |
| server = socketserver.TCPServer(("", 0), SimpleProxyHandler) |
| _, dynamic_port = server.server_address |
| logger.info(f"Встроенный прокси-сервер успешно запущен на динамическом порту {dynamic_port}") |
| |
| os.environ["NEXT_PUBLIC_DESIGNER_API_URL"] = f"http://localhost:{dynamic_port}" |
| os.environ["AGENT_SERVER_URL"] = f"http://localhost:{dynamic_port}" |
| server.serve_forever() |
| except Exception as last_e: |
| logger.error(f"Все попытки запустить прокси-сервер не удались: {last_e}") |
| |
| return |
| except Exception as e: |
| logger.error(f"Критическая ошибка в прокси-сервере: {e}") |
|
|
| def main(): |
| processes = [] |
| try: |
| |
| if not API_BINARY.exists(): |
| logger.error(f"API binary не найден: {API_BINARY}") |
| return 1 |
| |
| if not PLAYGROUND_DIR.exists(): |
| logger.error(f"Playground директория не найдена: {PLAYGROUND_DIR}") |
| return 1 |
| |
| |
| tmp_files_script = Path(__file__).parent / "create_tmp_agents.sh" |
| if tmp_files_script.exists(): |
| logger.info(f"Запуск скрипта создания временных файлов: {tmp_files_script}") |
| try: |
| os.chmod(tmp_files_script, 0o755) |
| subprocess.run(["bash", str(tmp_files_script)]) |
| logger.info("Скрипт создания временных файлов успешно выполнен") |
| except Exception as e: |
| logger.error(f"Ошибка при запуске скрипта создания временных файлов: {e}") |
| else: |
| logger.warning(f"Скрипт {tmp_files_script} не найден") |
| |
| |
| ensure_directory_permissions(AGENTS_DIR) |
| ensure_directory_permissions(BACKUP_DIR) |
| |
| |
| check_and_create_property_json() |
| |
| |
| update_property_json() |
| |
| |
| check_and_create_agent_files() |
| |
| |
| check_files() |
| |
| |
| logger.info("Запуск TEN-Agent API сервера на порту 8080...") |
| api_process = subprocess.Popen([str(API_BINARY)]) |
| processes.append(api_process) |
| |
| |
| test_thread = threading.Thread(target=test_api) |
| test_thread.daemon = True |
| test_thread.start() |
| |
| |
| proxy_script = Path(__file__).parent / "proxy_server.py" |
| logger.info(f"Проверка наличия прокси-сервера по пути: {proxy_script}") |
|
|
| |
| autonomous_proxy_script = Path(__file__).parent / "start_proxy.sh" |
| try: |
| if autonomous_proxy_script.exists(): |
| logger.info(f"Запуск автономного прокси-сервера через: {autonomous_proxy_script}") |
| os.chmod(autonomous_proxy_script, 0o755) |
| proxy_process = subprocess.Popen(["bash", str(autonomous_proxy_script)]) |
| processes.append(proxy_process) |
| logger.info("Автономный прокси-сервер запущен") |
| else: |
| logger.warning(f"Скрипт {autonomous_proxy_script} не найден") |
| except Exception as e: |
| logger.error(f"Ошибка при запуске автономного прокси-сервера: {e}") |
|
|
| |
| |
| if proxy_script.exists(): |
| logger.info(f"Найден файл прокси-сервера: {proxy_script}") |
| proxy_process = subprocess.Popen([sys.executable, str(proxy_script)], env=dict(os.environ, PROXY_PORT=str(PROXY_PORT))) |
| processes.append(proxy_process) |
| else: |
| |
| alt_path = Path("./proxy_server.py") |
| logger.info(f"Поиск альтернативного пути: {alt_path}") |
| if alt_path.exists(): |
| logger.info(f"Найден файл прокси-сервера по альтернативному пути: {alt_path}") |
| proxy_process = subprocess.Popen([sys.executable, str(alt_path)], env=dict(os.environ, PROXY_PORT=str(PROXY_PORT))) |
| processes.append(proxy_process) |
| else: |
| |
| try: |
| logger.info("Попытка запуска proxy_server.py через subprocess...") |
| proxy_process = subprocess.Popen([sys.executable, "-m", "proxy_server"], env=dict(os.environ, PROXY_PORT=str(PROXY_PORT))) |
| processes.append(proxy_process) |
| logger.info("Прокси-сервер запущен через модуль") |
| except Exception as e: |
| logger.warning(f"Не удалось запустить прокси-сервер через модуль: {e}") |
| |
| |
| logger.info("Создание встроенного прокси-сервера...") |
| |
| try: |
| |
| proxy_thread = threading.Thread(target=run_simple_proxy) |
| proxy_thread.daemon = True |
| proxy_thread.start() |
| logger.info(f"Встроенный прокси-сервер запущен на порту {PROXY_PORT}") |
| except Exception as e: |
| logger.error(f"Серьезная ошибка при запуске встроенного прокси-сервера: {e}") |
| |
| |
| |
| os.environ["PORT"] = "7860" |
| os.environ["AGENT_SERVER_URL"] = f"http://localhost:{PROXY_PORT}" |
| os.environ["NEXT_PUBLIC_EDIT_GRAPH_MODE"] = "true" |
| os.environ["NEXT_PUBLIC_DISABLE_CAMERA"] = "true" |
| |
| |
| os.environ["NEXT_PUBLIC_DEV_MODE"] = "false" |
| os.environ["NEXT_PUBLIC_API_BASE_URL"] = "/api/agents" |
| os.environ["NEXT_PUBLIC_DESIGNER_API_URL"] = f"http://localhost:{PROXY_PORT}" |
| |
| |
| playground_process = subprocess.Popen( |
| ["pnpm", "dev"], |
| cwd=str(PLAYGROUND_DIR), |
| env=os.environ |
| ) |
| processes.append(playground_process) |
| |
| |
| for proc in processes: |
| proc.wait() |
| |
| except KeyboardInterrupt: |
| logger.info("Завершение работы...") |
| except Exception as e: |
| logger.error(f"Ошибка: {e}") |
| finally: |
| |
| for proc in processes: |
| if proc and proc.poll() is None: |
| proc.terminate() |
| try: |
| proc.wait(timeout=5) |
| except subprocess.TimeoutExpired: |
| proc.kill() |
| |
| return 0 |
|
|
| if __name__ == "__main__": |
| |
| signal.signal(signal.SIGINT, lambda sig, frame: sys.exit(0)) |
| signal.signal(signal.SIGTERM, lambda sig, frame: sys.exit(0)) |
| |
| sys.exit(main()) |