3v324v23 commited on
Commit
f6e2895
·
1 Parent(s): a128108

Полное исправление проблемы с property.json и добавление надежного встроенного прокси-сервера

Browse files
Files changed (1) hide show
  1. app.py +349 -150
app.py CHANGED
@@ -33,6 +33,10 @@ API_BINARY = Path("/app/server/bin/api")
33
  PLAYGROUND_DIR = Path("/app/playground")
34
  BACKUP_DIR = Path("/app/backup")
35
 
 
 
 
 
36
  def ensure_directory_permissions(directory_path):
37
  """Обеспечиваем правильные разрешения для директории"""
38
  directory = Path(directory_path)
@@ -70,20 +74,31 @@ def update_property_json():
70
  with open(PROPERTY_JSON, 'r') as f:
71
  data = json.load(f)
72
 
73
- if "predefined_graphs" in data:
74
- logger.info(f"{PROPERTY_JSON} уже содержит поле predefined_graphs")
 
 
 
 
 
 
 
 
 
 
75
  return False
76
 
77
  # Создаем резервную копию перед изменением
78
  backup_file(PROPERTY_JSON)
79
 
80
- # Добавляем поле predefined_graphs на основе поля graphs или создаем его
81
- if "graphs" in data:
82
- data["predefined_graphs"] = data["graphs"]
83
- logger.info(f"Добавлено поле predefined_graphs на основе поля graphs")
84
- else:
85
- # Создаем стандартные графы
86
- data["predefined_graphs"] = [
 
87
  {
88
  "name": "Voice Agent",
89
  "description": "Basic voice agent with OpenAI",
@@ -95,13 +110,21 @@ def update_property_json():
95
  "file": "chat_agent.json"
96
  }
97
  ]
98
- logger.info(f"Добавлено стандартное поле predefined_graphs")
 
 
 
 
 
 
 
 
99
 
100
  # Сохраняем обновленный файл
101
  with open(PROPERTY_JSON, 'w') as f:
102
- json.dump(data, f, indent=2)
103
 
104
- logger.info(f"{PROPERTY_JSON} успешно обновлен с добавлением поля predefined_graphs")
105
  return True
106
  except Exception as e:
107
  logger.error(f"Ошибка при обновлении {PROPERTY_JSON}: {e}")
@@ -296,6 +319,22 @@ def test_api():
296
  try:
297
  # Даем серверу время запуститься
298
  time.sleep(3)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
  with urllib.request.urlopen("http://localhost:8080/graphs") as response:
300
  data = response.read().decode('utf-8')
301
  logger.info(f"Ответ /graphs: {data}")
@@ -306,6 +345,9 @@ def test_api():
306
  # Проверяем структуру ответа и реагируем на проблемы
307
  if json_data is None:
308
  logger.error("Не удалось проанализировать ответ API")
 
 
 
309
  elif isinstance(json_data, list):
310
  if len(json_data) > 0:
311
  logger.info(f"API вернул {len(json_data)} графов")
@@ -315,28 +357,293 @@ def test_api():
315
  if PROPERTY_JSON.exists():
316
  logger.info("Пробуем обновить существующий property.json...")
317
  if update_property_json():
318
- # Перезапускаем API сервер после обновления
319
- logger.info("Перезапускаем API сервер после обновления property.json...")
320
- subprocess.run(["pkill", "-f", str(API_BINARY)])
321
- time.sleep(1)
322
- subprocess.Popen([str(API_BINARY)])
323
  elif isinstance(json_data, dict) and "code" in json_data:
324
  logger.warning("API вернул ошибку, исправляем property.json")
325
- backup_file(PROPERTY_JSON)
326
- check_and_create_property_json()
327
- update_property_json() # Добавляем вызов функции обновления
328
- check_and_create_agent_files()
329
- ensure_directory_permissions(AGENTS_DIR)
330
- # Перезапускаем API сервер
331
- logger.info("Перезапускаем API сервер...")
332
- subprocess.run(["pkill", "-f", str(API_BINARY)])
333
- time.sleep(1)
334
- subprocess.Popen([str(API_BINARY)])
335
  except urllib.error.URLError as e:
336
  logger.error(f"Ошибка запроса к API: {e}")
337
  except Exception as e:
338
  logger.error(f"Неизвестная ошибка при запросе к API: {e}")
339
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
340
  def main():
341
  processes = []
342
  try:
@@ -376,14 +683,13 @@ def main():
376
  test_thread.start()
377
 
378
  # Запускаем прокси-сервер
379
- proxy_port = 9090
380
  proxy_script = Path(__file__).parent / "proxy_server.py"
381
  logger.info(f"Проверка наличия прокси-сервера по пути: {proxy_script}")
382
 
383
  # Проверяем наличие файла разными способами для диагностики
384
  if proxy_script.exists():
385
  logger.info(f"Найден файл прокси-сервера: {proxy_script}")
386
- proxy_process = subprocess.Popen([sys.executable, str(proxy_script)], env=dict(os.environ, PROXY_PORT=str(proxy_port)))
387
  processes.append(proxy_process)
388
  else:
389
  # Пробуем искать файл в текущем каталоге
@@ -391,144 +697,37 @@ def main():
391
  logger.info(f"Поиск альтернативного пути: {alt_path}")
392
  if alt_path.exists():
393
  logger.info(f"Найден файл прокси-сервера по альтернативному пути: {alt_path}")
394
- proxy_process = subprocess.Popen([sys.executable, str(alt_path)], env=dict(os.environ, PROXY_PORT=str(proxy_port)))
395
  processes.append(proxy_process)
396
  else:
397
  # Пробуем запустить напрямую через Python
398
  try:
399
  logger.info("Попытка запуска proxy_server.py через subprocess...")
400
- proxy_process = subprocess.Popen([sys.executable, "-m", "proxy_server"], env=dict(os.environ, PROXY_PORT=str(proxy_port)))
401
  processes.append(proxy_process)
402
  logger.info("Прокси-сервер запущен через модуль")
403
  except Exception as e:
404
  logger.warning(f"Не удалось запустить прокси-сервер через модуль: {e}")
405
 
406
- # Пробуем создать и запустить очень простой прокси-сервер
407
- try:
408
- # Создаем упрощенный прокси-сервер
409
- logger.info("Создание встроенного прокси-сервера...")
410
-
411
- def run_simple_proxy():
412
- """Запускает встроенный прокси-сервер"""
413
- import http.server
414
- import socketserver
415
-
416
- class SimpleProxyHandler(http.server.BaseHTTPRequestHandler):
417
- def do_GET(self):
418
- # Для запросов к /graphs возвращаем заранее подготовленный ответ
419
- if self.path == "/graphs":
420
- self.send_response(200)
421
- self.send_header('Content-Type', 'application/json')
422
- self.send_header('Access-Control-Allow-Origin', '*')
423
- self.end_headers()
424
-
425
- # Готовый ответ с графами
426
- graphs_data = json.dumps([
427
- {
428
- "name": "Voice Agent",
429
- "description": "Voice Agent with OpenAI",
430
- "file": "voice_agent.json",
431
- "id": "voice_agent",
432
- "package": "default"
433
- },
434
- {
435
- "name": "Chat Agent",
436
- "description": "Chat Agent",
437
- "file": "chat_agent.json",
438
- "id": "chat_agent",
439
- "package": "default"
440
- }
441
- ])
442
- self.wfile.write(graphs_data.encode('utf-8'))
443
- return
444
-
445
- # Для запросов к Designer API возвращаем заранее подготовленный ответ
446
- if self.path.startswith("/api/designer/") or self.path.startswith("/api/dev/"):
447
- self.send_response(200)
448
- self.send_header('Content-Type', 'application/json')
449
- self.send_header('Access-Control-Allow-Origin', '*')
450
- self.end_headers()
451
-
452
- # Готовый ответ для Designer API
453
- designer_data = json.dumps({
454
- "success": True,
455
- "packages": [
456
- {
457
- "name": "default",
458
- "description": "Default package",
459
- "graphs": [
460
- {
461
- "name": "Voice Agent",
462
- "description": "Voice Agent with OpenAI",
463
- "file": "voice_agent.json",
464
- "id": "voice_agent",
465
- "package": "default"
466
- },
467
- {
468
- "name": "Chat Agent",
469
- "description": "Chat Agent",
470
- "file": "chat_agent.json",
471
- "id": "chat_agent",
472
- "package": "default"
473
- }
474
- ]
475
- }
476
- ]
477
- })
478
- self.wfile.write(designer_data.encode('utf-8'))
479
- return
480
-
481
- def do_POST(self):
482
- # Для запросов к Designer API возвращаем заранее подготовленный ответ
483
- if self.path.startswith("/api/designer/") or self.path.startswith("/api/dev/"):
484
- self.send_response(200)
485
- self.send_header('Content-Type', 'application/json')
486
- self.send_header('Access-Control-Allow-Origin', '*')
487
- self.end_headers()
488
-
489
- # Готовый ответ для Designer API
490
- self.wfile.write(json.dumps({"success": True}).encode('utf-8'))
491
- return
492
-
493
- def do_OPTIONS(self):
494
- self.send_response(200)
495
- self.send_header('Access-Control-Allow-Origin', '*')
496
- self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
497
- self.send_header('Access-Control-Allow-Headers', 'Content-Type')
498
- self.end_headers()
499
-
500
- def log_message(self, format, *args):
501
- logger.info(f"PROXY: {self.address_string()} - {format % args}")
502
-
503
- # Запускаем сервер
504
- try:
505
- port = int(os.environ.get("PROXY_PORT", "9090"))
506
- with socketserver.TCPServer(("", port), SimpleProxyHandler) as httpd:
507
- logger.info(f"Запуск встроенного прокси-сервера на порту {port}")
508
- httpd.serve_forever()
509
- except Exception as e:
510
- logger.error(f"Ошибка при запуске встроенного прокси-сервера: {e}")
511
-
512
- # Запускаем прокси-сервер в отдельном потоке
513
- proxy_thread = threading.Thread(target=run_simple_proxy)
514
- proxy_thread.daemon = True
515
- proxy_thread.start()
516
- logger.info(f"Встроенный прокси-сервер запущен на порту {proxy_port}")
517
- except Exception as e:
518
- logger.warning(f"Все попытки запустить прокси-сервер не удались: {e}, будет использовано прямое подключение к API")
519
- proxy_port = 8080 # Fallback на порт API сервера
520
-
521
- # Запускаем Playground UI в режиме dev на порту 7860 (порт Hugging Face)
522
- logger.info("Запуск Playground UI в режиме разработки на порту 7860...")
523
  os.environ["PORT"] = "7860"
524
- os.environ["AGENT_SERVER_URL"] = f"http://localhost:{proxy_port}" # Используем прокси вместо прямого подключения
525
  os.environ["NEXT_PUBLIC_EDIT_GRAPH_MODE"] = "true" # Включаем расширенный режим редактирования
526
  os.environ["NEXT_PUBLIC_DISABLE_CAMERA"] = "true" # Отключаем запрос на использование камеры
527
 
528
  # Важные переменные для отключения запросов к дизайнеру
529
  os.environ["NEXT_PUBLIC_DEV_MODE"] = "false"
530
  os.environ["NEXT_PUBLIC_API_BASE_URL"] = "/api/agents"
531
- os.environ["NEXT_PUBLIC_DESIGNER_API_URL"] = f"http://localhost:{proxy_port}" # Используем прокси для запросов дизайнера
532
 
533
  # Запускаем Playground UI
534
  playground_process = subprocess.Popen(
 
33
  PLAYGROUND_DIR = Path("/app/playground")
34
  BACKUP_DIR = Path("/app/backup")
35
 
36
+ # Глобальные переменные
37
+ PROXY_PORT = 9090 # Порт для прокси-сервера
38
+ API_PORT = 8080 # Порт для API сервера
39
+
40
  def ensure_directory_permissions(directory_path):
41
  """Обеспечиваем правильные разрешения для директории"""
42
  directory = Path(directory_path)
 
74
  with open(PROPERTY_JSON, 'r') as f:
75
  data = json.load(f)
76
 
77
+ need_update = False
78
+
79
+ # Проверяем наличие predefined_graphs и его формат
80
+ if "predefined_graphs" not in data:
81
+ logger.info(f"{PROPERTY_JSON} не содержит поле predefined_graphs")
82
+ need_update = True
83
+ elif not isinstance(data["predefined_graphs"], list):
84
+ logger.warning(f"{PROPERTY_JSON} содержит predefined_graphs, но это не массив")
85
+ need_update = True
86
+
87
+ if not need_update:
88
+ logger.info(f"{PROPERTY_JSON} уже содержит корректное поле predefined_graphs")
89
  return False
90
 
91
  # Создаем резервную копию перед изменением
92
  backup_file(PROPERTY_JSON)
93
 
94
+ # Создаем полностью новый property.json с правильной структурой
95
+ property_data = {
96
+ "_ten": {},
97
+ "name": "TEN Agent Example",
98
+ "version": "0.0.1",
99
+ "extensions": ["openai_chatgpt"],
100
+ "description": "A basic voice agent with OpenAI",
101
+ "predefined_graphs": [
102
  {
103
  "name": "Voice Agent",
104
  "description": "Basic voice agent with OpenAI",
 
110
  "file": "chat_agent.json"
111
  }
112
  ]
113
+ }
114
+
115
+ # Если есть другие поля в оригинальном файле, сохраняем их
116
+ for key, value in data.items():
117
+ if key not in property_data and key != "predefined_graphs" and key != "graphs":
118
+ property_data[key] = value
119
+
120
+ # Добавляем поле graphs в конец для совместимости
121
+ property_data["graphs"] = property_data["predefined_graphs"]
122
 
123
  # Сохраняем обновленный файл
124
  with open(PROPERTY_JSON, 'w') as f:
125
+ json.dump(property_data, f, indent=2)
126
 
127
+ logger.info(f"{PROPERTY_JSON} успешно обновлен с корректным полем predefined_graphs")
128
  return True
129
  except Exception as e:
130
  logger.error(f"Ошибка при обновлении {PROPERTY_JSON}: {e}")
 
319
  try:
320
  # Даем серверу время запуститься
321
  time.sleep(3)
322
+
323
+ # Сначала проверяем прокси-сервер
324
+ try:
325
+ proxy_url = f"http://localhost:{PROXY_PORT}/graphs"
326
+ logger.info(f"Проверка прокси-сервера по адресу: {proxy_url}")
327
+ with urllib.request.urlopen(proxy_url) as response:
328
+ data = response.read().decode('utf-8')
329
+ logger.info(f"Ответ прокси-сервера: {data}")
330
+ json_data = analyze_api_response(data)
331
+ if json_data and isinstance(json_data, list) and len(json_data) > 0:
332
+ logger.info("Прокси-сервер возвращает корректные данные о графах")
333
+ return
334
+ except Exception as e:
335
+ logger.warning(f"Ошибка при проверке прокси-сервера: {e}")
336
+
337
+ # Затем проверяем основной API
338
  with urllib.request.urlopen("http://localhost:8080/graphs") as response:
339
  data = response.read().decode('utf-8')
340
  logger.info(f"Ответ /graphs: {data}")
 
345
  # Проверяем структуру ответа и реагируем на проблемы
346
  if json_data is None:
347
  logger.error("Не удалось проанализировать ответ API")
348
+ # Исправляем property.json и перезапускаем API
349
+ generate_fresh_property_json()
350
+ restart_api_server()
351
  elif isinstance(json_data, list):
352
  if len(json_data) > 0:
353
  logger.info(f"API вернул {len(json_data)} графов")
 
357
  if PROPERTY_JSON.exists():
358
  logger.info("Пробуем обновить существующий property.json...")
359
  if update_property_json():
360
+ restart_api_server()
 
 
 
 
361
  elif isinstance(json_data, dict) and "code" in json_data:
362
  logger.warning("API вернул ошибку, исправляем property.json")
363
+ generate_fresh_property_json()
364
+ restart_api_server()
 
 
 
 
 
 
 
 
365
  except urllib.error.URLError as e:
366
  logger.error(f"Ошибка запроса к API: {e}")
367
  except Exception as e:
368
  logger.error(f"Неизвестная ошибка при запросе к API: {e}")
369
 
370
+ def generate_fresh_property_json():
371
+ """Создает полностью новый property.json с оптимальной структурой"""
372
+ logger.info("Создание нового property.json с оптимальной структурой")
373
+
374
+ # Создаем резервную копию существующего файла, если он есть
375
+ if PROPERTY_JSON.exists():
376
+ backup_file(PROPERTY_JSON)
377
+
378
+ # Создаем новый property.json с правильным порядком полей
379
+ property_data = {
380
+ "_ten": {},
381
+ "name": "TEN Agent Example",
382
+ "version": "0.0.1",
383
+ "extensions": ["openai_chatgpt"],
384
+ "description": "A basic voice agent with OpenAI",
385
+ "predefined_graphs": [
386
+ {
387
+ "name": "Voice Agent",
388
+ "description": "Basic voice agent with OpenAI",
389
+ "file": "voice_agent.json"
390
+ },
391
+ {
392
+ "name": "Chat Agent",
393
+ "description": "Simple chat agent",
394
+ "file": "chat_agent.json"
395
+ }
396
+ ],
397
+ "graphs": [
398
+ {
399
+ "name": "Voice Agent",
400
+ "description": "Basic voice agent with OpenAI",
401
+ "file": "voice_agent.json"
402
+ },
403
+ {
404
+ "name": "Chat Agent",
405
+ "description": "Simple chat agent",
406
+ "file": "chat_agent.json"
407
+ }
408
+ ]
409
+ }
410
+
411
+ # Создаем временный файл и затем перемещаем его
412
+ with tempfile.NamedTemporaryFile(mode='w', delete=False) as temp_file:
413
+ json.dump(property_data, temp_file, indent=2)
414
+ temp_path = temp_file.name
415
+
416
+ # Копируем временный файл в целевой
417
+ try:
418
+ shutil.copy2(temp_path, PROPERTY_JSON)
419
+ try:
420
+ os.chmod(PROPERTY_JSON, 0o666) # Устанавливаем права доступа rw-rw-rw-
421
+ except Exception as e:
422
+ logger.warning(f"Невозможно изменить права доступа для {PROPERTY_JSON}: {e}")
423
+ logger.info(f"Новый файл {PROPERTY_JSON} создан успешно")
424
+ except Exception as e:
425
+ logger.error(f"Ошибка при создании {PROPERTY_JSON}: {e}")
426
+ finally:
427
+ os.unlink(temp_path) # Удаляем временный файл
428
+
429
+ def restart_api_server():
430
+ """Перезапускает API сервер"""
431
+ logger.info("Перезапускаем API сервер...")
432
+
433
+ # Останавливаем текущий процесс
434
+ try:
435
+ subprocess.run(["pkill", "-f", str(API_BINARY)])
436
+ time.sleep(1)
437
+ except Exception as e:
438
+ logger.warning(f"Ошибка при остановке API сервера: {e}")
439
+
440
+ # Запускаем новый процесс
441
+ try:
442
+ new_process = subprocess.Popen([str(API_BINARY)])
443
+ logger.info("API сервер успешно перезапущен")
444
+ time.sleep(2) # Даем время для запуска
445
+ return new_process
446
+ except Exception as e:
447
+ logger.error(f"Ошибка при запуске API сервера: {e}")
448
+ return None
449
+
450
+ def run_simple_proxy():
451
+ """Запускает встроенный прокси-сервер"""
452
+ import http.server
453
+ import socketserver
454
+ import json
455
+
456
+ # Предварительно подготовленные данные для графов
457
+ GRAPHS_DATA = [
458
+ {
459
+ "name": "Voice Agent",
460
+ "description": "Voice Agent with OpenAI",
461
+ "file": "voice_agent.json",
462
+ "id": "voice_agent",
463
+ "package": "default"
464
+ },
465
+ {
466
+ "name": "Chat Agent",
467
+ "description": "Chat Agent",
468
+ "file": "chat_agent.json",
469
+ "id": "chat_agent",
470
+ "package": "default"
471
+ }
472
+ ]
473
+
474
+ # Предварительно подготовленные данные для API дизайнера
475
+ DESIGNER_DATA = {
476
+ "success": True,
477
+ "packages": [
478
+ {
479
+ "name": "default",
480
+ "description": "Default package",
481
+ "graphs": [
482
+ {
483
+ "name": "Voice Agent",
484
+ "description": "Voice Agent with OpenAI",
485
+ "file": "voice_agent.json",
486
+ "id": "voice_agent",
487
+ "package": "default"
488
+ },
489
+ {
490
+ "name": "Chat Agent",
491
+ "description": "Chat Agent",
492
+ "file": "chat_agent.json",
493
+ "id": "chat_agent",
494
+ "package": "default"
495
+ }
496
+ ]
497
+ }
498
+ ]
499
+ }
500
+
501
+ class SimpleProxyHandler(http.server.BaseHTTPRequestHandler):
502
+ def do_GET(self):
503
+ logger.info(f"PROXY: GET запрос: {self.path}")
504
+
505
+ # Для запросов к /graphs возвращаем заранее подготовленный ответ
506
+ if self.path == "/graphs":
507
+ self._handle_graphs_request()
508
+ return
509
+
510
+ # Для запросов к Designer API возвращаем заранее подготовленный ответ
511
+ if self.path.startswith("/api/designer/") or self.path.startswith("/api/dev/"):
512
+ self._handle_designer_request()
513
+ return
514
+
515
+ # Для других запросов пробуем проксировать на API сервер
516
+ self._proxy_to_api("GET")
517
+
518
+ def do_POST(self):
519
+ logger.info(f"PROXY: POST запрос: {self.path}")
520
+
521
+ # Для запросов к Designer API возвращаем заранее подготовленный ответ
522
+ if self.path.startswith("/api/designer/") or self.path.startswith("/api/dev/"):
523
+ self._handle_designer_request()
524
+ return
525
+
526
+ # Для других запросов пробуем проксировать на API сервер
527
+ self._proxy_to_api("POST")
528
+
529
+ def do_OPTIONS(self):
530
+ logger.info(f"PROXY: OPTIONS запрос: {self.path}")
531
+ self.send_response(200)
532
+ self.send_header('Access-Control-Allow-Origin', '*')
533
+ self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
534
+ self.send_header('Access-Control-Allow-Headers', 'Content-Type')
535
+ self.end_headers()
536
+
537
+ def _handle_graphs_request(self):
538
+ """Обрабатывает запросы к /graphs"""
539
+ logger.info("PROXY: Обработка запроса к /graphs")
540
+
541
+ # Сначала пробуем получить данные от API сервера
542
+ try:
543
+ with urllib.request.urlopen(f"http://localhost:{API_PORT}/graphs") as response:
544
+ data = response.read().decode('utf-8')
545
+ try:
546
+ json_data = json.loads(data)
547
+ # Если API вернул непустой список, используем его
548
+ if isinstance(json_data, list) and len(json_data) > 0:
549
+ logger.info("PROXY: API вернул непустой список графов, используем его")
550
+ self._send_response(200, data)
551
+ return
552
+ except Exception:
553
+ # Если ошибка парсинга JSON, используем заготовленные данные
554
+ pass
555
+ except Exception:
556
+ # Если ошибка подключения к API, используем заготовленные данные
557
+ pass
558
+
559
+ # Если API недоступен или вернул некорректные данные, используем заготовленные данные
560
+ logger.info("PROXY: Возвращаем заготовленные данные о графах")
561
+ self._send_response(200, json.dumps(GRAPHS_DATA))
562
+
563
+ def _handle_designer_request(self):
564
+ """Обрабатывает запросы к Designer API"""
565
+ logger.info(f"PROXY: Обработка запроса к Designer API: {self.path}")
566
+ self._send_response(200, json.dumps(DESIGNER_DATA))
567
+
568
+ def _proxy_to_api(self, method):
569
+ """Проксирует запрос к API серверу"""
570
+ try:
571
+ url = f"http://localhost:{API_PORT}{self.path}"
572
+ logger.info(f"PROXY: Проксирование запроса к API: {url}")
573
+
574
+ req = urllib.request.Request(url, method=method)
575
+
576
+ # Копирование заголовков
577
+ for header, value in self.headers.items():
578
+ if header.lower() not in ["host", "content-length"]:
579
+ req.add_header(header, value)
580
+
581
+ # Для POST-запросов копируем тело
582
+ if method == "POST":
583
+ content_length = int(self.headers.get('Content-Length', 0))
584
+ body = self.rfile.read(content_length)
585
+ req.data = body
586
+
587
+ # Выполняем запрос к API серверу
588
+ with urllib.request.urlopen(req) as response:
589
+ # Отправляем ответ клиенту
590
+ self.send_response(response.status)
591
+
592
+ # Копируем заголовки ответа
593
+ for header, value in response.getheaders():
594
+ if header.lower() != "transfer-encoding":
595
+ self.send_header(header, value)
596
+
597
+ # Добавляем CORS заголовки
598
+ self.send_header('Access-Control-Allow-Origin', '*')
599
+ self.end_headers()
600
+
601
+ # Копируем тело ответа
602
+ self.wfile.write(response.read())
603
+ except Exception as e:
604
+ logger.error(f"PROXY: Ошибка при проксировании запроса: {e}")
605
+ # В случае ошибки возвращаем пустой успешный ответ
606
+ self._send_response(200, json.dumps({"success": True}))
607
+
608
+ def _send_response(self, status_code, data):
609
+ """Отправляет ответ с указанным статусом и данными"""
610
+ self.send_response(status_code)
611
+ self.send_header('Content-Type', 'application/json')
612
+ self.send_header('Access-Control-Allow-Origin', '*')
613
+ self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
614
+ self.send_header('Access-Control-Allow-Headers', 'Content-Type')
615
+ self.end_headers()
616
+
617
+ if isinstance(data, str):
618
+ self.wfile.write(data.encode('utf-8'))
619
+ else:
620
+ self.wfile.write(data)
621
+
622
+ def log_message(self, format, *args):
623
+ """Перенаправляем логи сервера в наш логгер"""
624
+ logger.debug(f"PROXY: {self.address_string()} - {format % args}")
625
+
626
+ # Запускаем прокси-сервер
627
+ try:
628
+ port = PROXY_PORT
629
+ logger.info(f"Запуск встроенного прокси-сервера на порту {port}")
630
+
631
+ # Создаем сервер с обработкой возможной занятости порта
632
+ for attempt in range(3):
633
+ try:
634
+ with socketserver.TCPServer(("", port), SimpleProxyHandler) as httpd:
635
+ logger.info(f"Встроенный прокси-сервер успешно запущен на порту {port}")
636
+ httpd.serve_forever()
637
+ break
638
+ except OSError as e:
639
+ if e.errno == 98: # Address already in use
640
+ logger.warning(f"Порт {port} уже занят, пробуем порт {port+1}")
641
+ port += 1
642
+ else:
643
+ raise
644
+ except Exception as e:
645
+ logger.error(f"Ошибка при запуске встроенного прокси-сервера: {e}")
646
+
647
  def main():
648
  processes = []
649
  try:
 
683
  test_thread.start()
684
 
685
  # Запускаем прокси-сервер
 
686
  proxy_script = Path(__file__).parent / "proxy_server.py"
687
  logger.info(f"Проверка наличия прокси-сервера по пути: {proxy_script}")
688
 
689
  # Проверяем наличие файла разными способами для диагностики
690
  if proxy_script.exists():
691
  logger.info(f"Найден файл прокси-сервера: {proxy_script}")
692
+ proxy_process = subprocess.Popen([sys.executable, str(proxy_script)], env=dict(os.environ, PROXY_PORT=str(PROXY_PORT)))
693
  processes.append(proxy_process)
694
  else:
695
  # Пробуем искать файл в текущем каталоге
 
697
  logger.info(f"Поиск альтернативного пути: {alt_path}")
698
  if alt_path.exists():
699
  logger.info(f"Найден файл прокси-сервера по альтернативному пути: {alt_path}")
700
+ proxy_process = subprocess.Popen([sys.executable, str(alt_path)], env=dict(os.environ, PROXY_PORT=str(PROXY_PORT)))
701
  processes.append(proxy_process)
702
  else:
703
  # Пробуем запустить напрямую через Python
704
  try:
705
  logger.info("Попытка запуска proxy_server.py через subprocess...")
706
+ proxy_process = subprocess.Popen([sys.executable, "-m", "proxy_server"], env=dict(os.environ, PROXY_PORT=str(PROXY_PORT)))
707
  processes.append(proxy_process)
708
  logger.info("Прокси-сервер запущен через модуль")
709
  except Exception as e:
710
  logger.warning(f"Не удалось запустить прокси-сервер через модуль: {e}")
711
 
712
+ # Вместо вложенного try-except используем новый блок
713
+ logger.info("Создание встроенного прокси-сервера...")
714
+
715
+ # Запускаем прокси-сервер в отдельном потоке
716
+ proxy_thread = threading.Thread(target=run_simple_proxy)
717
+ proxy_thread.daemon = True
718
+ proxy_thread.start()
719
+ logger.info(f"Встроенный прокси-сервер запущен на порту {PROXY_PORT}")
720
+
721
+ # Настраиваем переменные окружения для Playground UI
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
722
  os.environ["PORT"] = "7860"
723
+ os.environ["AGENT_SERVER_URL"] = f"http://localhost:{PROXY_PORT}" # Используем прокси вместо прямого подключения
724
  os.environ["NEXT_PUBLIC_EDIT_GRAPH_MODE"] = "true" # Включаем расширенный режим редактирования
725
  os.environ["NEXT_PUBLIC_DISABLE_CAMERA"] = "true" # Отключаем запрос на использование камеры
726
 
727
  # Важные переменные для отключения запросов к дизайнеру
728
  os.environ["NEXT_PUBLIC_DEV_MODE"] = "false"
729
  os.environ["NEXT_PUBLIC_API_BASE_URL"] = "/api/agents"
730
+ os.environ["NEXT_PUBLIC_DESIGNER_API_URL"] = f"http://localhost:{PROXY_PORT}" # Используем прокси для запросов дизайнера
731
 
732
  # Запускаем Playground UI
733
  playground_process = subprocess.Popen(