File size: 12,450 Bytes
58cec34
 
2839144
 
d7f0eff
 
3c7d3b0
86f3bf2
eeea7be
 
 
 
58cec34
d7f0eff
3c7d3b0
d7f0eff
3c7d3b0
d7f0eff
3c7d3b0
d7f0eff
3c7d3b0
d7f0eff
 
 
 
3c7d3b0
 
 
74e8d5b
eeea7be
 
 
 
 
2455e56
11ddd3d
2455e56
74e8d5b
eeea7be
74e8d5b
11ddd3d
74e8d5b
2455e56
 
 
eeea7be
3c7d3b0
eeea7be
2455e56
 
 
eeea7be
11ddd3d
74e8d5b
11ddd3d
2455e56
11ddd3d
eeea7be
2455e56
 
 
 
74e8d5b
 
 
eeea7be
 
 
 
 
 
 
 
 
 
 
d7f0eff
eeea7be
 
 
 
2455e56
eeea7be
 
74e8d5b
eeea7be
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d7f0eff
eeea7be
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11ddd3d
eeea7be
 
 
 
 
 
 
d7f0eff
2839144
d7f0eff
2839144
3c7d3b0
 
 
eeea7be
 
 
3c7d3b0
eeea7be
 
 
 
 
 
d7f0eff
 
 
 
3c7d3b0
 
 
 
d7f0eff
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
#!/usr/bin/env python3
import os
import subprocess
import sys
import time
from pathlib import Path
import signal
import shutil
import http.client
import socketserver
from http.server import HTTPServer, BaseHTTPRequestHandler
import threading

def main():
    processes = []
    try:
        # Пути к исполняемым файлам
        api_binary = Path("/app/server/bin/api")
        playground_dir = Path("/app/playground")
        
        # Проверяем существование файлов
        if not api_binary.exists():
            print(f"ERROR: API binary not found at {api_binary}", file=sys.stderr)
            return 1
        
        if not playground_dir.exists():
            print(f"ERROR: Playground directory not found at {playground_dir}", file=sys.stderr)
            return 1
        
        # Создаем директорию для логов с правами доступа
        log_dir = "/tmp/ten_agent"
        os.makedirs(log_dir, exist_ok=True)
        # Даем всем права на запись в директорию логов
        os.chmod(log_dir, 0o777)
            
        # Запускаем API сервер с правильными переменными окружения
        print("Starting TEN-Agent API server on port 8080...")
        api_server_env = os.environ.copy()
        api_server_env["LOG_PATH"] = log_dir
        api_server_env["LOG_STDOUT"] = "true"
        api_server_env["WORKERS_MAX"] = "4"  # Устанавливаем значение для WORKERS_MAX
        api_server_process = subprocess.Popen([str(api_binary)], env=api_server_env)
        processes.append(api_server_process)
        
        # Даем время API серверу запуститься
        time.sleep(3)
        
        # Запускаем Playground UI на порту 3000
        print("Starting Playground UI on port 3000...")
        playground_env = os.environ.copy()
        playground_env["AGENT_SERVER_URL"] = "http://localhost:8080"  # Подключаемся к локальному API серверу
        playground_env["NODE_ENV"] = "production"  # Убедимся, что запускаем в production режиме
        playground_env["PORT"] = "3000"  # Указываем порт через переменную окружения
        
        # Запускаем Next.js с правильными параметрами (без -- --port)
        playground_process = subprocess.Popen(
            "cd /app/playground && pnpm start",  # Удаляем лишние параметры
            shell=True,
            env=playground_env
        )
        processes.append(playground_process)
        
        # Даем время Playground UI запуститься
        time.sleep(5)
        
        # Создаем эффективный прокси-сервер
        class ProxyHandler(BaseHTTPRequestHandler):
            # Отключаем логирование каждого запроса
            def log_message(self, format, *args):
                if args and args[0].startswith('GET /?logs=container'):
                    return  # Игнорируем логи для запросов логов контейнера
                sys.stderr.write("%s - - [%s] %s\n" %
                                (self.client_address[0],
                                  self.log_date_time_string(),
                                  format % args))
            
            def do_GET(self):
                self._handle_request('GET')
                
            def do_POST(self):
                self._handle_request('POST')
                
            def do_PUT(self):
                self._handle_request('PUT')
                
            def do_DELETE(self):
                self._handle_request('DELETE')
                
            def do_OPTIONS(self):
                self._handle_request('OPTIONS')
                
            def _handle_request(self, method):
                try:
                    # Определяем, какой сервер должен обработать запрос
                    target_host = 'localhost'
                    
                    # API endpoints идут на порт 8080 (API сервер)
                    if self.path.startswith('/health') or \
                       self.path.startswith('/list') or \
                       self.path.startswith('/graphs') or \
                       self.path.startswith('/start') or \
                       self.path.startswith('/stop') or \
                       self.path.startswith('/ping') or \
                       self.path.startswith('/token') or \
                       self.path.startswith('/dev-tmp') or \
                       self.path.startswith('/vector'):
                        target_port = 8080
                    else:
                        # Все остальные запросы (включая / и UI assets) идут на порт 3000 (Playground)
                        target_port = 3000
                        
                    # Пробуем подключиться к целевому серверу
                    conn = http.client.HTTPConnection(target_host, target_port, timeout=30)
                    
                    # Получаем данные запроса для POST/PUT
                    body = None
                    if method in ['POST', 'PUT']:
                        content_length = int(self.headers.get('Content-Length', 0))
                        body = self.rfile.read(content_length) if content_length > 0 else None
                    
                    # Копируем все заголовки запроса
                    headers = {k: v for k, v in self.headers.items()}
                    
                    # Исправляем Host заголовок
                    headers['Host'] = f"{target_host}:{target_port}"
                    
                    # Отправляем запрос на правильный сервер
                    conn.request(method, self.path, body=body, headers=headers)
                    
                    # Получаем ответ
                    response = conn.getresponse()
                    
                    # Отправляем статус ответа
                    self.send_response(response.status)
                    
                    # Копируем все заголовки ответа
                    for header, value in response.getheaders():
                        if header.lower() != 'transfer-encoding':  # Исключаем заголовок transfer-encoding
                            self.send_header(header, value)
                    
                    # Завершаем заголовки
                    self.end_headers()
                    
                    # Отправляем тело ответа
                    chunk_size = 8192
                    while True:
                        chunk = response.read(chunk_size)
                        if not chunk:
                            break
                        self.wfile.write(chunk)
                    
                    # Закрываем соединение
                    conn.close()
                    
                except Exception as e:
                    print(f"Proxy error for {method} {self.path}: {str(e)}", file=sys.stderr)
                    
                    # Для запросов мониторинга не показываем ошибку
                    if self.path == '/?logs=container':
                        self.send_response(200)
                        self.send_header('Content-type', 'text/plain')
                        self.end_headers()
                        self.wfile.write(b"OK")
                        return
                    
                    # Отправляем страницу с ошибкой и разъяснением
                    self.send_response(500)
                    self.send_header('Content-type', 'text/html; charset=utf-8')
                    self.end_headers()
                    
                    error_message = f"""
                    <!DOCTYPE html>
                    <html>
                    <head>
                        <title>TEN Agent - Error</title>
                        <meta charset="utf-8">
                        <style>
                            body {{ font-family: Arial, sans-serif; line-height: 1.6; max-width: 800px; margin: 0 auto; padding: 20px; }}
                            h1 {{ color: #dc3545; }}
                            .error {{ background: #f8d7da; border-left: 4px solid #dc3545; padding: 15px; margin-bottom: 20px; }}
                            pre {{ background: #f8f9fa; padding: 10px; border-radius: 5px; overflow-x: auto; }}
                        </style>
                    </head>
                    <body>
                        <h1>Произошла ошибка при обработке запроса</h1>
                        <div class="error">
                            <p><strong>Детали ошибки:</strong> {str(e)}</p>
                            <p>Целевой порт: {target_port}</p>
                        </div>
                        <p>Система пытается запустить все компоненты. Попробуйте обновить страницу через минуту.</p>
                    </body>
                    </html>
                    """
                    
                    self.wfile.write(error_message.encode('utf-8'))
        
        # Запускаем HTTP прокси-сервер
        port = 7860  # Hugging Face Space ожидает сервер на порту 7860
        print(f"Starting proxy server on port {port}...")
        
        # Разрешаем повторное использование адреса и порта
        class ReuseAddressServer(socketserver.ThreadingTCPServer):
            allow_reuse_address = True
            daemon_threads = True
            
        server = ReuseAddressServer(('0.0.0.0', port), ProxyHandler)
        
        # Запускаем сервер
        server_thread = threading.Thread(target=server.serve_forever)
        server_thread.daemon = True
        server_thread.start()
        
        # Продолжаем выполнение, чтобы можно было обработать сигналы остановки
        while True:
            # Проверяем, что все процессы еще живы
            if not api_server_process.poll() is None:
                print("API server has stopped, restarting...")
                api_server_process = subprocess.Popen([str(api_binary)], env=api_server_env)
                processes = [p for p in processes if p != api_server_process]
                processes.append(api_server_process)
            
            if not playground_process.poll() is None:
                print("Playground UI has stopped, restarting...")
                playground_process = subprocess.Popen(
                    "cd /app/playground && pnpm start",
                    shell=True,
                    env=playground_env
                )
                processes = [p for p in processes if p != playground_process]
                processes.append(playground_process)
            
            time.sleep(10)
    
    except KeyboardInterrupt:
        print("Shutting down...")
    finally:
        # Завершаем все процессы при выходе
        for proc in processes:
            try:
                if proc and proc.poll() is None:
                    proc.terminate()
                    proc.wait(timeout=5)
            except:
                if proc and proc.poll() is None:
                    proc.kill()
        
        # Останавливаем сервер
        if 'server' in locals():
            server.shutdown()
    
    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())