3v324v23 commited on
Commit
dae7932
·
1 Parent(s): 2d96520

Исправление прокси-сервера: добавлена обработка ошибок и хардкодные ответы

Browse files
Files changed (1) hide show
  1. app.py +375 -680
app.py CHANGED
@@ -12,714 +12,409 @@ import http.server
12
  import socketserver
13
  import urllib.request
14
  import urllib.error
15
- import gradio as gr
16
 
17
- # Проверяем, запущены ли мы в HuggingFace Space
18
- IS_HF_SPACE = os.environ.get("SPACE_ID") is not None
19
- print(f"Running in HuggingFace Space: {IS_HF_SPACE}")
20
-
21
- # Конфигурация путей
22
- TMP_DIR = Path("/tmp/ten_user")
23
- AGENTS_DIR = TMP_DIR / "agents"
24
- LOGS_DIR = TMP_DIR / "logs"
25
- NEXTJS_PORT = int(os.environ.get("UI_PORT", 3000))
26
- API_PORT = int(os.environ.get("API_PORT", 8080))
27
- GRADIO_PORT = int(os.environ.get("GRADIO_PORT", 7860))
28
-
29
- # Адрес для подключения к UI в iframe
30
- if IS_HF_SPACE:
31
- # В HuggingFace Space нужно использовать внутренний URL
32
- UI_URL = f"https://{os.environ.get('SPACE_ID', 'unknown-space')}.hf.space/_next/iframe/3000"
33
- INTERNAL_HOST = "0.0.0.0"
34
- else:
35
- # В локальном окружении используем localhost
36
- UI_URL = f"http://localhost:{NEXTJS_PORT}"
37
- INTERNAL_HOST = "localhost"
38
-
39
- def create_directories():
40
- """Создает необходимые директории"""
41
- print("Создание директорий...")
42
-
43
- # Создаем только директории, без попытки создания файлов
44
- TMP_DIR.mkdir(exist_ok=True, parents=True)
45
- AGENTS_DIR.mkdir(exist_ok=True, parents=True)
46
- LOGS_DIR.mkdir(exist_ok=True, parents=True)
47
-
48
- # Не создаем файл server.log, т.к. он вызывает ошибку прав доступа
49
- # (LOGS_DIR / "server.log").touch()
50
-
51
- print(f"Директории созданы в {TMP_DIR}")
52
-
53
- # Выводим информацию о правах доступа для отладки
54
- print(f"Права доступа для {TMP_DIR}: {os.stat(TMP_DIR).st_mode}")
55
- if LOGS_DIR.exists():
56
- print(f"Директория логов создана: {LOGS_DIR}")
57
- print(f"Права доступа для {LOGS_DIR}: {os.stat(LOGS_DIR).st_mode}")
58
- else:
59
- print(f"Директория логов не создана: {LOGS_DIR}")
60
-
61
- # Пробуем записать лог в /tmp напрямую, где должны быть права на запись
62
- try:
63
- tmp_log_file = Path("/tmp/ten_server.log")
64
- tmp_log_file.touch()
65
- print(f"Создан файл лога в /tmp: {tmp_log_file}")
66
- except Exception as e:
67
- print(f"Не удалось создать файл лога в /tmp: {e}")
68
- # Это не критическая ошибка, продолжаем работу
69
-
70
- def create_config_files():
71
- """Создает базовые файлы конфигурации"""
72
- print("Создание конфигурационных файлов...")
73
-
74
- # Используем директорию в /tmp напрямую, без вложенных директорий
75
- config_dir = Path("/tmp")
76
- property_file_path = config_dir / "property.json"
77
- voice_agent_path = config_dir / "voice_agent.json"
78
- chat_agent_path = config_dir / "chat_agent.json"
79
-
80
- # Создаем property.json с графами
81
- property_data = {
82
- "name": "TEN Agent Demo",
83
- "version": "0.0.1",
84
- "extensions": ["openai_chatgpt", "elevenlabs_tts", "deepgram_asr"],
85
- "description": "TEN Agent on Hugging Face Space",
86
- "graphs": [
87
- {
88
- "name": "Voice Agent",
89
- "description": "Basic voice agent with OpenAI and ElevenLabs",
90
- "file": "voice_agent.json"
91
- },
92
- {
93
- "name": "Chat Agent",
94
- "description": "Simple chat agent with OpenAI",
95
- "file": "chat_agent.json"
96
- }
97
- ]
98
- }
99
-
100
- try:
101
- with open(property_file_path, "w") as f:
102
- json.dump(property_data, f, indent=2)
103
- print(f"Файл {property_file_path} создан успешно")
104
- except Exception as e:
105
- print(f"Ошибка при создании {property_file_path}: {e}")
106
- return False
107
-
108
- # Создаем voice_agent.json
109
- voice_agent = {
110
- "_ten": {"version": "0.0.1"},
111
- "nodes": [
112
- {
113
- "id": "start",
114
- "type": "start",
115
- "data": {"x": 100, "y": 100}
116
- },
117
- {
118
- "id": "openai_chatgpt",
119
- "type": "openai_chatgpt",
120
- "data": {
121
- "x": 300,
122
- "y": 200,
123
- "properties": {
124
- "model": "gpt-3.5-turbo",
125
- "temperature": 0.7,
126
- "system_prompt": "You are a helpful assistant."
127
- }
128
- }
129
- },
130
- {
131
- "id": "elevenlabs_tts",
132
- "type": "elevenlabs_tts",
133
- "data": {
134
- "x": 500,
135
- "y": 200,
136
- "properties": {
137
- "voice_id": "21m00Tcm4TlvDq8ikWAM"
138
- }
139
- }
140
- },
141
- {
142
- "id": "deepgram_asr",
143
- "type": "deepgram_asr",
144
- "data": {
145
- "x": 300,
146
- "y": 300,
147
- "properties": {
148
- "language": "ru"
149
- }
150
- }
151
- },
152
- {
153
- "id": "end",
154
- "type": "end",
155
- "data": {"x": 700, "y": 100}
156
- }
157
- ],
158
- "edges": [
159
- {"id": "start_to_chatgpt", "source": "start", "target": "openai_chatgpt"},
160
- {"id": "chatgpt_to_tts", "source": "openai_chatgpt", "target": "elevenlabs_tts"},
161
- {"id": "tts_to_end", "source": "elevenlabs_tts", "target": "end"},
162
- {"id": "asr_to_chatgpt", "source": "deepgram_asr", "target": "openai_chatgpt"}
163
- ],
164
- "groups": [],
165
- "templates": [],
166
- "root": "start"
167
- }
168
-
169
- try:
170
- with open(voice_agent_path, "w") as f:
171
- json.dump(voice_agent, f, indent=2)
172
- print(f"Файл {voice_agent_path} создан успешно")
173
- except Exception as e:
174
- print(f"Ошибка при создании {voice_agent_path}: {e}")
175
- return False
176
-
177
- # Создаем chat_agent.json (упрощенная версия)
178
- chat_agent = {
179
- "_ten": {"version": "0.0.1"},
180
- "nodes": [
181
- {
182
- "id": "start",
183
- "type": "start",
184
- "data": {"x": 100, "y": 100}
185
- },
186
- {
187
- "id": "openai_chatgpt",
188
- "type": "openai_chatgpt",
189
- "data": {
190
- "x": 300,
191
- "y": 200,
192
- "properties": {
193
- "model": "gpt-3.5-turbo",
194
- "temperature": 0.7,
195
- "system_prompt": "You are a helpful chat assistant."
196
- }
197
- }
198
- },
199
- {
200
- "id": "end",
201
- "type": "end",
202
- "data": {"x": 500, "y": 100}
203
- }
204
- ],
205
- "edges": [
206
- {"id": "start_to_chatgpt", "source": "start", "target": "openai_chatgpt"},
207
- {"id": "chatgpt_to_end", "source": "openai_chatgpt", "target": "end"}
208
- ],
209
- "groups": [],
210
- "templates": [],
211
- "root": "start"
212
- }
213
-
214
- try:
215
- with open(chat_agent_path, "w") as f:
216
- json.dump(chat_agent, f, indent=2)
217
- print(f"Файл {chat_agent_path} создан успешно")
218
- except Exception as e:
219
- print(f"Ошибка при создании {chat_agent_path}: {e}")
220
- return False
221
-
222
- # Обновляем глобальную переменную AGENTS_DIR для использования нового пути
223
- global AGENTS_DIR
224
- AGENTS_DIR = config_dir
225
- print(f"Конфигурационные файлы созданы успешно в директории {config_dir}")
226
- return True
227
-
228
- def start_api_server():
229
- """Запускает API сервер"""
230
- print("Запуск API сервера...")
231
-
232
- # Устанавливаем переменные окружения
233
- api_env = os.environ.copy()
234
- api_env["TEN_AGENT_DIR"] = str(AGENTS_DIR)
235
- api_env["API_PORT"] = str(API_PORT)
236
-
237
- # Выводим информацию о директории с агентами для отладки
238
- print(f"Директория с агентами: {AGENTS_DIR}")
239
- print(f"Файлы в директории агентов:")
240
- if AGENTS_DIR.exists():
241
- for file in AGENTS_DIR.iterdir():
242
- if file.name.endswith('.json'):
243
- print(f" - {file.name} ({os.path.getsize(file)}b)")
244
-
245
- # В HuggingFace Space нужны дополнительные настройки
246
- if IS_HF_SPACE:
247
- print("Configuring API server for HuggingFace Space environment...")
248
- # Указываем серверу специально использовать API wrapper
249
- api_env["USE_WRAPPER"] = "true"
250
- # Отключаем логирование в файл
251
- api_env["TEN_LOG_DISABLE_FILE"] = "true"
252
- # Указываем путь для временных файлов
253
- api_env["TMP_DIR"] = "/tmp"
254
-
255
- # Запускаем Python API wrapper
256
- api_cmd = ["python", "api_wrapper.py"]
257
- print(f"Running API command: {' '.join(api_cmd)}")
258
- api_process = subprocess.Popen(
259
- api_cmd,
260
- env=api_env,
261
- stdout=subprocess.PIPE,
262
- stderr=subprocess.PIPE
263
- )
264
-
265
- # Ждем запуска сервера
266
- time.sleep(2)
267
-
268
- # Проверяем, что процесс не упал
269
- if api_process.poll() is not None:
270
- stdout, stderr = api_process.communicate()
271
- print(f"API сервер не запустился!")
272
- print(f"STDOUT: {stdout.decode()}")
273
- print(f"STDERR: {stderr.decode()}")
274
- return None
275
 
276
- print(f"API server started and listening on port {API_PORT}")
277
-
278
- # Запускаем поток для вывода логов
279
- def log_output(process, prefix):
280
- for line in iter(process.stdout.readline, b''):
281
- print(f"[{prefix}] {line.decode().strip()}")
282
- for line in iter(process.stderr.readline, b''):
283
- print(f"[{prefix} ERROR] {line.decode().strip()}")
284
-
285
- log_thread = threading.Thread(target=log_output, args=(api_process, "API"))
286
- log_thread.daemon = True
287
- log_thread.start()
288
-
289
- return api_process
290
-
291
- def start_playground():
292
- """Запускает Playground UI через Next.js"""
293
- print("Запуск Playground UI...")
294
-
295
- # Создаем директорию для Next.js в /tmp, где у нас есть права на запись
296
- tmp_playground_dir = Path("/tmp/ten_playground")
297
- if not tmp_playground_dir.exists():
298
- print(f"Создаем временную директорию для Next.js: {tmp_playground_dir}")
299
- tmp_playground_dir.mkdir(exist_ok=True, parents=True)
300
-
301
- # Копируем необходимые файлы из /app/playground в /tmp/ten_playground
302
- print("Копируем файлы Next.js приложения во временную директорию...")
303
- try:
304
- os.system(f"cp -r /app/playground/app /tmp/ten_playground/")
305
- os.system(f"cp -r /app/playground/public /tmp/ten_playground/")
306
- os.system(f"cp /app/playground/package.json /tmp/ten_playground/")
307
- os.system(f"cp /app/playground/next.config.mjs /tmp/ten_playground/")
308
- os.system(f"cp /app/playground/tailwind.config.js /tmp/ten_playground/")
309
- os.system(f"cp /app/playground/postcss.config.js /tmp/ten_playground/")
310
 
311
- # Проверяем, что файлы скопировались
312
- if not (tmp_playground_dir / "app").exists():
313
- print("Не удалось скопировать файлы Next.js. Создаем простое приложение...")
314
- create_simple_next_app(tmp_playground_dir)
315
- except Exception as e:
316
- print(f"Ошибка при копировании файлов: {e}")
317
- print("Создаем простое приложение Next.js...")
318
- create_simple_next_app(tmp_playground_dir)
319
-
320
- # Устанавливаем переменные окружения
321
- ui_env = os.environ.copy()
322
- ui_env["PORT"] = str(NEXTJS_PORT)
323
- ui_env["AGENT_SERVER_URL"] = f"http://{INTERNAL_HOST}:{API_PORT}"
324
- ui_env["NEXT_PUBLIC_EDIT_GRAPH_MODE"] = "true"
325
- ui_env["NEXT_PUBLIC_DISABLE_CAMERA"] = "false"
326
-
327
- # В HuggingFace Space нужны дополнительные настройки
328
- if IS_HF_SPACE:
329
- print("Configuring for HuggingFace Space environment...")
330
- ui_env["NEXT_PUBLIC_IS_HF_SPACE"] = "true"
331
- # Отключаем строгие проверки CORS для работы в iframe
332
- ui_env["NEXT_PUBLIC_DISABLE_CORS"] = "true"
333
-
334
- # Запускаем UI из временной директории, где у нас есть права на запись
335
- ui_cmd = f"cd {tmp_playground_dir} && npx next dev"
336
- print(f"Running UI command: {ui_cmd}")
337
- ui_process = subprocess.Popen(
338
- ui_cmd,
339
- env=ui_env,
340
- shell=True,
341
- stdout=subprocess.PIPE,
342
- stderr=subprocess.PIPE
343
- )
344
-
345
- # Ждем запуска UI
346
- time.sleep(5)
347
-
348
- # Проверяем, что процесс не упал
349
- if ui_process.poll() is not None:
350
- stdout, stderr = ui_process.communicate()
351
- print(f"Playground UI не запустился!")
352
- print(f"STDOUT: {stdout.decode()}")
353
- print(f"STDERR: {stderr.decode()}")
354
- return None
355
-
356
- # Запускаем поток для вывода логов
357
- def log_output(process, prefix):
358
- for line in iter(process.stdout.readline, b''):
359
- print(f"[{prefix}] {line.decode().strip()}")
360
- for line in iter(process.stderr.readline, b''):
361
- print(f"[{prefix} ERROR] {line.decode().strip()}")
362
-
363
- log_thread = threading.Thread(target=log_output, args=(ui_process, "UI"))
364
- log_thread.daemon = True
365
- log_thread.start()
366
-
367
- return ui_process
368
-
369
- def create_interface():
370
- """Создает Gradio интерфейс для редиректа"""
371
- with gr.Blocks() as demo:
372
- gr.Markdown("# TEN Agent на Hugging Face Space")
373
- gr.Markdown("## Управление и мониторинг")
374
 
375
- # Статус серверов
376
- status_md = gr.Markdown("### Статус: Инициализация...")
377
 
378
- with gr.Row():
379
- col1, col2 = gr.Column(), gr.Column()
 
380
 
381
- with col1:
382
- # Информация об API сервере
383
- gr.Markdown(f"""
384
- ### API сервер
385
-
386
- API сервер работает по адресу: http://{INTERNAL_HOST}:{API_PORT}
387
-
388
- Доступные эндпоинты:
389
- - `/graphs` - Список доступных графов
390
- - `/health` - Статус API сервера
391
- """)
392
-
393
- # Кнопка для проверки API
394
- check_api_btn = gr.Button("Проверить API сервер")
395
- api_result = gr.JSON(label="Результат запроса к API")
 
 
 
 
396
 
397
- def check_api():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
398
  try:
399
- import requests
400
- response = requests.get(f"http://{INTERNAL_HOST}:{API_PORT}/health")
401
- return response.json()
402
  except Exception as e:
403
- return {"status": "error", "message": str(e)}
404
-
405
- check_api_btn.click(check_api, outputs=api_result)
406
-
407
- with col2:
408
- # Информация о UI сервере
409
- gr.Markdown(f"""
410
- ### UI сервер
411
-
412
- UI сервер доступен по адресу: {UI_URL}
413
 
414
- <a href="{UI_URL}" target="_blank" style="display: inline-block; padding: 10px 15px; background-color: #4CAF50; color: white; text-decoration: none; border-radius: 4px; margin: 10px 0;">Открыть UI в новой вкладке</a>
415
- """)
 
 
 
416
 
417
- # Функция для открытия UI в iframe
418
- iframe_btn = gr.Button("Показать UI в iframe")
 
 
 
419
 
420
- def show_iframe():
421
- return f"""
422
- <div style="border: 1px solid #ccc; padding: 10px; border-radius: 5px;">
423
- <iframe src="{UI_URL}" width="100%" height="500px" frameborder="0"></iframe>
424
- </div>
425
- """
426
 
427
- iframe_area = gr.HTML()
428
- iframe_btn.click(show_iframe, outputs=iframe_area)
429
-
430
- # Ссылки на документацию и важные настройки
431
- with gr.Accordion("Инструкции", open=False):
432
- gr.Markdown("""
433
- ### Важные настройки
434
-
435
- Для полноценной работы необходимо настроить следующие API ключи:
436
-
437
- 1. **OpenAI API Key** - для работы с языковыми моделями
438
- 2. **Deepgram API Key** - для распознавания речи
439
- 3. **ElevenLabs API Key** - для синтеза речи
440
- 4. **Agora App ID и Certificate** - для работы с RTC
441
 
442
- ### Доступные графы
443
-
444
- 1. **Voice Agent** - Голосовой агент с OpenAI и ElevenLabs
445
- 2. **Chat Agent** - Текстовый чат с OpenAI
446
- """)
 
 
 
 
447
 
448
- # Статус серверов и логи
449
- with gr.Accordion("Статус системы", open=False):
450
- api_status = gr.Textbox(label="Статус API сервера", value="Проверка...", interactive=False)
451
- ui_status = gr.Textbox(label="Статус UI сервера", value="Проверка...", interactive=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
452
 
453
- # Функция обновления статуса
454
- def update_status():
455
- api_status_msg = "✅ Активен" if is_port_in_use(API_PORT) else "❌ Не активен"
456
- ui_status_msg = "✅ Активен" if is_port_in_use(NEXTJS_PORT) else "❌ Не активен"
457
- status_md_msg = f"### Статус: {'✅ Все системы работают' if is_port_in_use(API_PORT) and is_port_in_use(NEXTJS_PORT) else '⚠️ Есть проблемы'}"
458
- return [api_status_msg, ui_status_msg, status_md_msg]
459
 
460
- status_btn = gr.Button("Обновить статус")
461
- status_btn.click(update_status, outputs=[api_status, ui_status, status_md])
462
 
463
- # Обновляем статус при загрузке
464
- demo.load(update_status, outputs=[api_status, ui_status, status_md])
465
-
466
- return demo
 
 
 
 
 
467
 
468
- # Вспомогательная функция для проверки, используется ли порт
469
- def is_port_in_use(port):
470
- import socket
471
- with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
472
- try:
473
- s.connect(('localhost', port))
474
- return True
475
- except:
476
- return False
477
 
478
- def create_simple_next_app(target_dir):
479
- """Создает простое Next.js приложение в указанной директории"""
480
- print(f"Создание простого Next.js приложения в {target_dir}")
481
-
482
- # Создаем базовую структуру Next.js приложения
483
- app_dir = target_dir / "app"
484
- app_dir.mkdir(exist_ok=True, parents=True)
485
-
486
- # Создаем простой package.json
487
- package_json = {
488
- "name": "ten-agent-simple-ui",
489
- "version": "0.1.0",
490
- "private": True,
491
- "scripts": {
492
- "dev": "next dev",
493
- "build": "next build",
494
- "start": "next start"
495
- },
496
- "dependencies": {
497
- "next": "latest",
498
- "react": "latest",
499
- "react-dom": "latest"
500
- }
501
- }
502
-
503
- with open(target_dir / "package.json", "w") as f:
504
- json.dump(package_json, f, indent=2)
505
-
506
- # Создаем простой next.config.js
507
- next_config = """/** @type {import('next').NextConfig} */
508
- const nextConfig = {
509
- reactStrictMode: true,
510
- }
511
-
512
- module.exports = nextConfig
513
- """
514
-
515
- with open(target_dir / "next.config.js", "w") as f:
516
- f.write(next_config)
517
-
518
- # Создаем простую страницу
519
- page_content = """export default function Home() {
520
- return (
521
- <div style={{ padding: '20px', fontFamily: 'Arial, sans-serif' }}>
522
- <h1>TEN Agent UI</h1>
523
- <p>API server is running at: <a href="http://localhost:8080">http://localhost:8080</a></p>
524
-
525
- <div style={{ marginTop: '20px', padding: '10px', backgroundColor: '#f0f0f0', borderRadius: '5px' }}>
526
- <p>API endpoints:</p>
527
- <ul>
528
- <li><a href="http://localhost:8080/graphs">/graphs</a> - Available graphs</li>
529
- <li><a href="http://localhost:8080/health">/health</a> - API server status</li>
530
- </ul>
531
- </div>
532
-
533
- <div style={{ marginTop: '20px' }}>
534
- <button
535
- style={{
536
- padding: '10px 15px',
537
- backgroundColor: '#4CAF50',
538
- color: 'white',
539
- border: 'none',
540
- borderRadius: '5px',
541
- cursor: 'pointer'
542
- }}
543
- onClick={() => window.location.href = 'http://localhost:8080/graphs'}
544
- >
545
- Go to API
546
- </button>
547
- </div>
548
- </div>
549
- );
550
- }
551
- """
552
-
553
- with open(app_dir / "page.js", "w") as f:
554
- f.write(page_content)
555
-
556
- # Создаем простой layout.js
557
- layout_content = """export const metadata = {
558
- title: 'TEN Agent',
559
- description: 'Simple UI for TEN Agent',
560
- }
561
 
562
- export default function RootLayout({ children }) {
563
- return (
564
- <html lang="en">
565
- <body>{children}</body>
566
- </html>
567
- )
568
- }
569
- """
570
 
571
- with open(app_dir / "layout.js", "w") as f:
572
- f.write(layout_content)
573
-
574
- print(f"Простое Next.js приложение создано в {target_dir}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
575
 
576
- def start_simple_ui():
577
- """Запускает простой HTTP сервер для UI"""
578
- print("Запуск простого HTTP сервера...")
579
-
580
- # Создаем директорию для UI
581
- simple_ui_dir = Path("/tmp/ten_ui")
582
- simple_ui_dir.mkdir(exist_ok=True, parents=True)
583
-
584
- # Создаем простую HTML страницу
585
- html_content = """<!DOCTYPE html>
586
- <html lang="en">
587
- <head>
588
- <meta charset="UTF-8">
589
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
590
- <title>TEN Agent Simple UI</title>
591
- <style>
592
- body {
593
- font-family: Arial, sans-serif;
594
- max-width: 800px;
595
- margin: 0 auto;
596
- padding: 20px;
597
- }
598
- .card {
599
- background-color: #f5f5f5;
600
- border-radius: 8px;
601
- padding: 16px;
602
- margin-bottom: 16px;
603
- }
604
- button {
605
- background-color: #4CAF50;
606
- border: none;
607
- color: white;
608
- padding: 10px 15px;
609
- text-align: center;
610
- text-decoration: none;
611
- display: inline-block;
612
- font-size: 16px;
613
- margin: 4px 2px;
614
- cursor: pointer;
615
- border-radius: 4px;
616
- }
617
- a {
618
- color: #0066cc;
619
- }
620
- </style>
621
- </head>
622
- <body>
623
- <h1>TEN Agent UI</h1>
624
- <div class="card">
625
- <h2>API Server</h2>
626
- <p>API сервер работает по адресу: <a href="http://localhost:8080" target="_blank">http://localhost:8080</a></p>
627
- <p>Доступные эндпоинты:</p>
628
- <ul>
629
- <li><a href="http://localhost:8080/graphs" target="_blank">/graphs</a> - Список доступных графов</li>
630
- <li><a href="http://localhost:8080/health" target="_blank">/health</a> - Статус API сервера</li>
631
- </ul>
632
- </div>
633
-
634
- <div class="card">
635
- <h2>Запуск сессии</h2>
636
- <p>Для запуска сессии можно использовать API вручную:</p>
637
- <pre>curl -X POST http://localhost:8080/start -H "Content-Type: application/json" -d '{"graph_file":"voice_agent.json"}'</pre>
638
- </div>
639
- </body>
640
- </html>
641
- """
642
-
643
- with open(simple_ui_dir / "index.html", "w") as f:
644
- f.write(html_content)
645
-
646
- # Запускаем простой HTTP сервер на порту 3000
647
- server_cmd = f"cd {simple_ui_dir} && python -m http.server {NEXTJS_PORT}"
648
- print(f"Running simple HTTP server: {server_cmd}")
649
-
650
- server_process = subprocess.Popen(
651
- server_cmd,
652
- shell=True,
653
- stdout=subprocess.PIPE,
654
- stderr=subprocess.PIPE
655
- )
656
-
657
- # Ждем запуска сервера
658
- time.sleep(2)
659
-
660
- # Проверяем, что процесс не упал
661
- if server_process.poll() is not None:
662
- stdout, stderr = server_process.communicate()
663
- print(f"Простой HTTP сервер не запустился!")
664
- print(f"STDOUT: {stdout.decode()}")
665
- print(f"STDERR: {stderr.decode()}")
666
- return None
667
-
668
- print(f"Простой HTTP сервер запущен и слушает на порту {NEXTJS_PORT}")
669
- return server_process
670
 
671
  def main():
672
- # Создаем директории и файлы конфигурации
673
- create_directories()
674
-
675
- # Пытаемся создать конфигурационные файлы
676
- if not create_config_files():
677
- print("ОШИБКА: Не удалось создать конфигурационные файлы!")
678
- return 1
679
-
680
- # Запускаем API сервер
681
- api_process = start_api_server()
682
- if not api_process:
683
- print("Не удалось запустить API сервер")
684
- return 1
685
-
686
- # Пробуем запустить Playground UI через Next.js
687
- ui_process = start_playground()
688
-
689
- # Если запуск через Next.js не удался, пробуем запустить простой HTTP сервер
690
- if not ui_process:
691
- print("Не удалось запустить Playground UI через Next.js, пробуем простой HTTP сервер...")
692
- ui_process = start_simple_ui()
693
-
694
- if not ui_process:
695
- print("Не удалось запустить ни один UI сервер. Продолжаем только с API сервером.")
696
-
697
- # Создаем Gradio интерфейс
698
- demo = create_interface()
699
-
700
- # Запускаем Gradio
701
- demo.launch(server_port=GRADIO_PORT, server_name=INTERNAL_HOST, share=False)
702
-
703
- # Остаемся в цикле до завершения процессов
704
  try:
705
- while True:
706
- if api_process.poll() is not None:
707
- print("API сервер остановлен")
708
- if ui_process and ui_process.poll() is None:
709
- ui_process.terminate()
710
- break
711
-
712
- if ui_process and ui_process.poll() is not None:
713
- print("UI сервер остановлен, пробуем перезапустить...")
714
- # Пробуем запустить простой HTTP сервер, если UI процесс упал
715
- ui_process = start_simple_ui()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
716
 
717
- time.sleep(1)
718
  except KeyboardInterrupt:
719
- print("Принудительная остановка...")
720
- api_process.terminate()
721
- if ui_process:
722
- ui_process.terminate()
 
 
 
 
 
 
 
 
723
 
724
  return 0
725
 
 
12
  import socketserver
13
  import urllib.request
14
  import urllib.error
 
15
 
16
+ def check_and_create_property_json():
17
+ """Проверяет наличие property.json и создает его при необходимости"""
18
+ property_path = Path("/app/agents/property.json")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
+ if not property_path.exists():
21
+ print(f"WARNING: {property_path} не найден, создаем файл...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
+ property_data = {
24
+ "name": "TEN Agent Example",
25
+ "version": "0.0.1",
26
+ "extensions": ["openai_chatgpt"],
27
+ "description": "A basic voice agent with OpenAI",
28
+ "graphs": [
29
+ {
30
+ "name": "Voice Agent",
31
+ "description": "Basic voice agent with OpenAI",
32
+ "file": "voice_agent.json"
33
+ },
34
+ {
35
+ "name": "Chat Agent",
36
+ "description": "Simple chat agent",
37
+ "file": "chat_agent.json"
38
+ }
39
+ ]
40
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
+ # Проверяем и создаем директории
43
+ property_path.parent.mkdir(parents=True, exist_ok=True)
44
 
45
+ # Записываем файл
46
+ with open(property_path, 'w') as f:
47
+ json.dump(property_data, f, indent=2)
48
 
49
+ print(f"Файл {property_path} создан успешно")
50
+
51
+ # Проверяем наличие _ten секции во всех JSON файлах
52
+ check_and_fix_ten_section()
53
+
54
+ def check_and_fix_ten_section():
55
+ """Проверяет наличие _ten секции во всех JSON файлах и добавляет её если нужно"""
56
+ json_files = [
57
+ "/app/agents/voice_agent.json",
58
+ "/app/agents/chat_agent.json",
59
+ "/app/agents/manifest.json"
60
+ ]
61
+
62
+ for file_path in json_files:
63
+ path = Path(file_path)
64
+ if path.exists():
65
+ try:
66
+ with open(path, 'r') as f:
67
+ data = json.load(f)
68
 
69
+ # Проверяем наличие _ten секции
70
+ if '_ten' not in data:
71
+ print(f"Добавляем _ten секцию в {file_path}")
72
+ data['_ten'] = {"version": "0.0.1"}
73
+
74
+ # Сохраняем файл
75
+ with open(path, 'w') as f:
76
+ json.dump(data, f, indent=2)
77
+
78
+ print(f"Файл {file_path} обновлен")
79
+ except Exception as e:
80
+ print(f"Ошибка при обработке файла {file_path}: {e}")
81
+
82
+ class ProxyHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
83
+ def do_POST(self):
84
+ print(f"Получен запрос: {self.path}")
85
+
86
+ # Перенаправляем запрос /api/dev/v1/packages/reload на /graphs
87
+ if self.path.startswith('/api/dev/v1/packages/reload') or self.path.startswith('/api/designer/v1/packages/reload'):
88
+ try:
89
+ print("Перенаправление на /graphs")
90
+ with urllib.request.urlopen("http://localhost:8080/graphs") as response:
91
+ data = response.read().decode('utf-8')
92
+
93
+ # Если сервер вернул пустой ответ или ошибку, создаем свой собственный ответ
94
+ if not data or "Invalid format" in data:
95
+ print("Сервер вернул ошибку или пустой ответ, создаем свой ответ")
96
+ # Создаем хардкодный список графов из property.json
97
+ property_path = Path("/app/agents/property.json")
98
+ if property_path.exists():
99
+ try:
100
+ with open(property_path, 'r') as f:
101
+ property_data = json.load(f)
102
+ graphs = property_data.get("graphs", [])
103
+ except Exception as e:
104
+ print(f"Ошибка чтения property.json: {e}")
105
+ graphs = []
106
+ else:
107
+ graphs = []
108
+
109
+ # Добавляем обязательные поля для каждого графа
110
+ for graph in graphs:
111
+ graph["id"] = graph.get("name", "").lower().replace(" ", "_")
112
+ if "file" in graph:
113
+ graph["file"] = graph["file"]
114
+ else:
115
+ try:
116
+ # Пытаемся разобрать JSON из ответа
117
+ graphs = json.loads(data)
118
+ except json.JSONDecodeError:
119
+ print(f"Ошибка разбора JSON из ответа сервера: {data}")
120
+ graphs = []
121
+
122
+ # Форматируем ответ в нужном формате для фронтенда
123
+ formatted_response = {
124
+ "data": graphs,
125
+ "status": 200,
126
+ "message": "Success"
127
+ }
128
+
129
+ response_data = json.dumps(formatted_response).encode('utf-8')
130
+
131
+ # Отправляем ответ
132
+ self.send_response(200)
133
+ self.send_header('Content-Type', 'application/json')
134
+ self.send_header('Content-Length', len(response_data))
135
+ self.send_header('Access-Control-Allow-Origin', '*')
136
+ self.end_headers()
137
+ self.wfile.write(response_data)
138
+ print(f"Отправлен ответ: {response_data.decode('utf-8')}")
139
+ except urllib.error.URLError as e:
140
+ print(f"Ошибка при перенаправлении: {e}")
141
+ # В случае ошибки, отправляем хардкодный ответ
142
+ property_path = Path("/app/agents/property.json")
143
+ if property_path.exists():
144
  try:
145
+ with open(property_path, 'r') as f:
146
+ property_data = json.load(f)
147
+ graphs = property_data.get("graphs", [])
148
  except Exception as e:
149
+ print(f"Ошибка чтения property.json: {e}")
150
+ graphs = []
151
+ else:
152
+ graphs = []
 
 
 
 
 
 
153
 
154
+ # Добавляем обязательные поля для каждого графа
155
+ for graph in graphs:
156
+ graph["id"] = graph.get("name", "").lower().replace(" ", "_")
157
+ if "file" in graph:
158
+ graph["file"] = graph["file"]
159
 
160
+ formatted_response = {
161
+ "data": graphs,
162
+ "status": 200,
163
+ "message": "Success"
164
+ }
165
 
166
+ response_data = json.dumps(formatted_response).encode('utf-8')
 
 
 
 
 
167
 
168
+ self.send_response(200)
169
+ self.send_header('Content-Type', 'application/json')
170
+ self.send_header('Content-Length', len(response_data))
171
+ self.send_header('Access-Control-Allow-Origin', '*')
172
+ self.end_headers()
173
+ self.wfile.write(response_data)
174
+ print(f"Отправлен хардкодный ответ: {response_data.decode('utf-8')}")
175
+ else:
176
+ self.send_error(404, "Not Found")
 
 
 
 
 
177
 
178
+ def do_OPTIONS(self):
179
+ self.send_response(200)
180
+ self.send_header('Access-Control-Allow-Origin', '*')
181
+ self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
182
+ self.send_header('Access-Control-Allow-Headers', 'Content-Type')
183
+ self.end_headers()
184
+
185
+ def do_GET(self):
186
+ print(f"Получен GET запрос: {self.path}")
187
 
188
+ # Перенаправляем запрос /api/designer/v1/addons/extensions
189
+ if self.path.startswith('/api/designer/v1/addons/extensions'):
190
+ # Отправляем хардкодный ответ со списком расширений
191
+ extensions = [
192
+ {
193
+ "id": "openai_chatgpt",
194
+ "name": "OpenAI ChatGPT",
195
+ "version": "0.0.1",
196
+ "description": "Integration with OpenAI ChatGPT API",
197
+ "nodes": [
198
+ {
199
+ "id": "openai_chatgpt",
200
+ "name": "OpenAI ChatGPT",
201
+ "category": "AI",
202
+ "description": "Sends message to OpenAI ChatGPT API"
203
+ }
204
+ ]
205
+ }
206
+ ]
207
 
208
+ formatted_response = {
209
+ "data": extensions,
210
+ "status": 200,
211
+ "message": "Success"
212
+ }
 
213
 
214
+ response_data = json.dumps(formatted_response).encode('utf-8')
 
215
 
216
+ self.send_response(200)
217
+ self.send_header('Content-Type', 'application/json')
218
+ self.send_header('Content-Length', len(response_data))
219
+ self.send_header('Access-Control-Allow-Origin', '*')
220
+ self.end_headers()
221
+ self.wfile.write(response_data)
222
+ print(f"Отправлен ответ для extensions: {response_data.decode('utf-8')}")
223
+ else:
224
+ self.send_error(404, "Not Found")
225
 
226
+ def run_proxy_server():
227
+ """Запускает прокси-сервер на порту 49483"""
228
+ handler = ProxyHTTPRequestHandler
229
+ httpd = socketserver.TCPServer(("", 49483), handler)
230
+ print("Прокси-сервер запущен на порту 49483")
231
+ httpd.serve_forever()
 
 
 
232
 
233
+ def check_files():
234
+ """Проверяет и выводит информацию о важных файлах"""
235
+ files_to_check = [
236
+ "/app/agents/property.json",
237
+ "/app/agents/manifest.json",
238
+ "/app/agents/voice_agent.json",
239
+ "/app/agents/chat_agent.json",
240
+ "/app/server/bin/api"
241
+ ]
242
+
243
+ print("\n=== Проверка критических файлов ===")
244
+ for file_path in files_to_check:
245
+ path = Path(file_path)
246
+ if path.exists():
247
+ if path.is_file():
248
+ size = path.stat().st_size
249
+ print(f" {file_path} (размер: {size} байт)")
250
+
251
+ # Если это JSON файл, выводим его содержимое
252
+ if file_path.endswith('.json'):
253
+ try:
254
+ with open(file_path, 'r') as f:
255
+ content = json.load(f)
256
+ print(f" Содержимое: {json.dumps(content, indent=2)}")
257
+ except Exception as e:
258
+ print(f" Ошибка чтения JSON: {e}")
259
+ else:
260
+ print(f"❌ {file_path} (это директория, а не файл)")
261
+ else:
262
+ print(f"❌ {file_path} (файл не найден)")
263
+
264
+ print("\n=== Проверка структуры директорий ===")
265
+ print("Содержимое /app/agents:")
266
+ subprocess.run(["ls", "-la", "/app/agents"])
267
+
268
+ print("\nПроверка прав доступа:")
269
+ subprocess.run(["stat", "/app/agents"])
270
+ subprocess.run(["stat", "/app/agents/property.json"])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
271
 
272
+ def test_api():
273
+ """Делает запрос к API для получения списка графов"""
274
+ import urllib.request
275
+ import urllib.error
 
 
 
 
276
 
277
+ print("\n=== Тестирование API ===")
278
+ try:
279
+ # Даем серверу время запуститься
280
+ time.sleep(3)
281
+ with urllib.request.urlopen("http://localhost:8080/graphs") as response:
282
+ data = response.read().decode('utf-8')
283
+ print(f"Ответ /graphs: {data}")
284
+
285
+ # Пробуем сделать запрос к нашему прокси
286
+ try:
287
+ with urllib.request.urlopen("http://localhost:49483/api/designer/v1/packages/reload") as proxy_response:
288
+ proxy_data = proxy_response.read().decode('utf-8')
289
+ print(f"Ответ от прокси: {proxy_data}")
290
+ except urllib.error.URLError as e:
291
+ print(f"Ошибка запроса к прокси: {e}")
292
+
293
+ except urllib.error.URLError as e:
294
+ print(f"Ошибка запроса к API: {e}")
295
+ except Exception as e:
296
+ print(f"Неизвестная ошибка при запросе к API: {e}")
297
 
298
+ def move_json_files_to_right_places():
299
+ """Копирует JSON файлы в нужные директории для работы с TEN-Agent"""
300
+ # Создаем структуру директорий, похожую на локальную установку
301
+ print("Создаем правильную структуру директорий для TEN-Agent...")
302
+
303
+ target_dirs = [
304
+ "/app/agents",
305
+ "/app/agents/examples",
306
+ "/app/agents/examples/default",
307
+ "/app/agents/extensions"
308
+ ]
309
+
310
+ for dir_path in target_dirs:
311
+ os.makedirs(dir_path, exist_ok=True)
312
+
313
+ # Копируем voice_agent.json в директорию examples
314
+ source_file = "/app/agents/voice_agent.json"
315
+ target_file = "/app/agents/examples/voice_agent.json"
316
+ if os.path.exists(source_file):
317
+ shutil.copy2(source_file, target_file)
318
+ print(f"Файл скопирован: {source_file} -> {target_file}")
319
+
320
+ # Копируем chat_agent.json в директорию examples
321
+ source_file = "/app/agents/chat_agent.json"
322
+ target_file = "/app/agents/examples/chat_agent.json"
323
+ if os.path.exists(source_file):
324
+ shutil.copy2(source_file, target_file)
325
+ print(f"Файл скопирован: {source_file} -> {target_file}")
326
+
327
+ # Обновляем property.json с путями к файлам
328
+ property_path = "/app/agents/property.json"
329
+ if os.path.exists(property_path):
330
+ try:
331
+ with open(property_path, 'r') as f:
332
+ property_data = json.load(f)
333
+
334
+ # Обновляем пути к файлам графов
335
+ for graph in property_data.get("graphs", []):
336
+ if "file" in graph:
337
+ graph["file"] = f"examples/{graph['file']}"
338
+
339
+ # Сохраняем обновленный файл
340
+ with open(property_path, 'w') as f:
341
+ json.dump(property_data, f, indent=2)
342
+
343
+ print(f"Файл {property_path} обновлен с правильными путями")
344
+ except Exception as e:
345
+ print(f"Ошибка при обновлении {property_path}: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
346
 
347
  def main():
348
+ processes = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
  try:
350
+ # Пути к исполняемым файлам
351
+ api_binary = Path("/app/server/bin/api")
352
+ playground_dir = Path("/app/playground")
353
+
354
+ # Проверяем существование файлов
355
+ if not api_binary.exists():
356
+ print(f"ERROR: API binary not found at {api_binary}", file=sys.stderr)
357
+ return 1
358
+
359
+ if not playground_dir.exists():
360
+ print(f"ERROR: Playground directory not found at {playground_dir}", file=sys.stderr)
361
+ return 1
362
+
363
+ # Проверяем и создаем property.json
364
+ check_and_create_property_json()
365
+
366
+ # Перемещаем файлы в нужные места
367
+ move_json_files_to_right_places()
368
+
369
+ # Проверка файлов перед запуском
370
+ check_files()
371
+
372
+ # Запускаем API сервер
373
+ print("Starting TEN-Agent API server on port 8080...")
374
+ api_process = subprocess.Popen([str(api_binary)])
375
+ processes.append(api_process)
376
+
377
+ # Тестируем API
378
+ test_thread = threading.Thread(target=test_api)
379
+ test_thread.daemon = True
380
+ test_thread.start()
381
+
382
+ # Запускаем прокси-сервер на порту 49483
383
+ proxy_thread = threading.Thread(target=run_proxy_server)
384
+ proxy_thread.daemon = True
385
+ proxy_thread.start()
386
+
387
+ # Запускаем Playground UI в режиме dev на порту 7860 (порт Hugging Face)
388
+ print("Starting Playground UI in development mode on port 7860...")
389
+ os.environ["PORT"] = "7860"
390
+ os.environ["AGENT_SERVER_URL"] = "http://localhost:8080"
391
+ os.environ["NEXT_PUBLIC_EDIT_GRAPH_MODE"] = "true" # Включаем расширенный режим редактирования
392
+ os.environ["NEXT_PUBLIC_DISABLE_CAMERA"] = "true" # Отключаем запрос на использование камеры
393
+
394
+ playground_process = subprocess.Popen(
395
+ ["pnpm", "dev"],
396
+ cwd=str(playground_dir),
397
+ env=os.environ
398
+ )
399
+ processes.append(playground_process)
400
+
401
+ # Ожидаем завершения процессов
402
+ for proc in processes:
403
+ proc.wait()
404
 
 
405
  except KeyboardInterrupt:
406
+ print("Shutting down...")
407
+ except Exception as e:
408
+ print(f"Error: {e}", file=sys.stderr)
409
+ finally:
410
+ # Завершение процессов
411
+ for proc in processes:
412
+ if proc and proc.poll() is None:
413
+ proc.terminate()
414
+ try:
415
+ proc.wait(timeout=5)
416
+ except subprocess.TimeoutExpired:
417
+ proc.kill()
418
 
419
  return 0
420