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

Исправление работы прокси-сервера в HF Space: всегда возвращаем заготовленные данные о графах, улучшена обработка ошибок и автоматический выбор порта

Browse files
Files changed (1) hide show
  1. app.py +91 -95
app.py CHANGED
@@ -453,7 +453,7 @@ def run_simple_proxy():
453
  import socketserver
454
  import json
455
 
456
- # Предварительно подготовленные данные для графов
457
  GRAPHS_DATA = [
458
  {
459
  "name": "Voice Agent",
@@ -502,29 +502,42 @@ def run_simple_proxy():
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}")
@@ -534,76 +547,40 @@ def run_simple_proxy():
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
  """Отправляет ответ с указанным статусом и данными"""
@@ -625,24 +602,39 @@ def run_simple_proxy():
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 = []
@@ -712,11 +704,15 @@ def main():
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"
 
453
  import socketserver
454
  import json
455
 
456
+ # Предварительно подготовленные данные для графов - формат правильный
457
  GRAPHS_DATA = [
458
  {
459
  "name": "Voice Agent",
 
502
  def do_GET(self):
503
  logger.info(f"PROXY: GET запрос: {self.path}")
504
 
505
+ # Для запросов к /graphs ВСЕГДА возвращаем заранее подготовленный ответ
506
  if self.path == "/graphs":
507
+ logger.info("PROXY: Возвращаем заготовленные данные о графах")
508
+ self._send_response(200, json.dumps(GRAPHS_DATA))
509
  return
510
 
511
+ # Для запросов к Designer API ВСЕГДА возвращаем заранее подготовленный ответ
512
  if self.path.startswith("/api/designer/") or self.path.startswith("/api/dev/"):
513
+ logger.info(f"PROXY: Обработка запроса к Designer API: {self.path}")
514
+ self._send_response(200, json.dumps(DESIGNER_DATA))
515
  return
516
 
517
  # Для других запросов пробуем проксировать на API сервер
518
+ try:
519
+ self._proxy_to_api("GET")
520
+ except Exception as e:
521
+ logger.error(f"PROXY: Ошибка при проксировании GET запроса: {e}")
522
+ # В случае ошибки возвращаем базовый успешный ответ
523
+ self._send_response(200, json.dumps({"success": True}))
524
 
525
  def do_POST(self):
526
  logger.info(f"PROXY: POST запрос: {self.path}")
527
 
528
+ # Для запросов к Designer API ВСЕГДА возвращаем заранее подготовленный ответ
529
  if self.path.startswith("/api/designer/") or self.path.startswith("/api/dev/"):
530
+ logger.info(f"PROXY: Обработка POST запроса к Designer API: {self.path}")
531
+ self._send_response(200, json.dumps({"success": True}))
532
  return
533
 
534
  # Для других запросов пробуем проксировать на API сервер
535
+ try:
536
+ self._proxy_to_api("POST")
537
+ except Exception as e:
538
+ logger.error(f"PROXY: Ошибка при проксировании POST запроса: {e}")
539
+ # В случае ошибки возвращаем базовый успешный ответ
540
+ self._send_response(200, json.dumps({"success": True}))
541
 
542
  def do_OPTIONS(self):
543
  logger.info(f"PROXY: OPTIONS запрос: {self.path}")
 
547
  self.send_header('Access-Control-Allow-Headers', 'Content-Type')
548
  self.end_headers()
549
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
550
  def _proxy_to_api(self, method):
551
  """Проксирует запрос к API серверу"""
552
+ url = f"http://localhost:{API_PORT}{self.path}"
553
+ logger.info(f"PROXY: Проксирование запроса к API: {url}")
554
+
555
+ req = urllib.request.Request(url, method=method)
556
+
557
+ # Копирование заголовков
558
+ for header, value in self.headers.items():
559
+ if header.lower() not in ["host", "content-length"]:
560
+ req.add_header(header, value)
561
+
562
+ # Для POST-запросов копируем тело
563
+ if method == "POST":
564
+ content_length = int(self.headers.get('Content-Length', 0))
565
+ body = self.rfile.read(content_length)
566
+ req.data = body
567
+
568
+ # Выполняем запрос к API серверу
569
+ with urllib.request.urlopen(req) as response:
570
+ # Отправляем ответ клиенту
571
+ self.send_response(response.status)
572
 
573
+ # Копируем заголовки ответа
574
+ for header, value in response.getheaders():
575
+ if header.lower() != "transfer-encoding":
576
+ self.send_header(header, value)
577
 
578
+ # Добавляем CORS заголовки
579
+ self.send_header('Access-Control-Allow-Origin', '*')
580
+ self.end_headers()
 
 
581
 
582
+ # Копируем тело ответа
583
+ self.wfile.write(response.read())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
584
 
585
  def _send_response(self, status_code, data):
586
  """Отправляет ответ с указанным статусом и данными"""
 
602
 
603
  # Запускаем прокси-сервер
604
  try:
605
+ # Пытаемся создать HTTP сервер на стандартном порту PROXY_PORT
606
+ try:
607
+ server = socketserver.TCPServer(("", PROXY_PORT), SimpleProxyHandler)
608
+ logger.info(f"Встроенный прокси-сервер успешно запущен на порту {PROXY_PORT}")
609
+ server.serve_forever()
610
+ except OSError as e:
611
+ # Если порт занят, пробуем порт PROXY_PORT+1
612
+ alt_port = PROXY_PORT + 1
613
+ logger.warning(f"Не удалось запустить прокси на порту {PROXY_PORT}, пробуем порт {alt_port}")
614
  try:
615
+ server = socketserver.TCPServer(("", alt_port), SimpleProxyHandler)
616
+ logger.info(f"Встроенный прокси-сервер успешно запущен на порту {alt_port}")
617
+ # Обновляем переменную окружения для UI
618
+ os.environ["NEXT_PUBLIC_DESIGNER_API_URL"] = f"http://localhost:{alt_port}"
619
+ os.environ["AGENT_SERVER_URL"] = f"http://localhost:{alt_port}"
620
+ server.serve_forever()
621
+ except Exception as inner_e:
622
+ logger.error(f"Не удалось запустить прокси-сервер даже на альтернативном порту: {inner_e}")
623
+ # Если и это не получилось, пробуем на порту 0 (любой свободный)
624
+ try:
625
+ server = socketserver.TCPServer(("", 0), SimpleProxyHandler)
626
+ _, dynamic_port = server.server_address
627
+ logger.info(f"Встроенный прокси-сервер успешно запущен на динамическом порту {dynamic_port}")
628
+ # Обновляем переменную окружения для UI
629
+ os.environ["NEXT_PUBLIC_DESIGNER_API_URL"] = f"http://localhost:{dynamic_port}"
630
+ os.environ["AGENT_SERVER_URL"] = f"http://localhost:{dynamic_port}"
631
+ server.serve_forever()
632
+ except Exception as last_e:
633
+ logger.error(f"Все попытки запустить прокси-сервер не удались: {last_e}")
634
+ # Сдаемся, нет смысла продолжать
635
+ return
636
  except Exception as e:
637
+ logger.error(f"Критическая ошибка в прокси-сервере: {e}")
638
 
639
  def main():
640
  processes = []
 
704
  # Вместо вложенного try-except используем новый блок
705
  logger.info("Создание встроенного прокси-сервера...")
706
 
707
+ try:
708
+ # Запускаем прокси-сервер в отдельном потоке - обязательный запуск
709
+ proxy_thread = threading.Thread(target=run_simple_proxy)
710
+ proxy_thread.daemon = True
711
+ proxy_thread.start()
712
+ logger.info(f"Встроенный прокси-сервер запущен на порту {PROXY_PORT}")
713
+ except Exception as e:
714
+ logger.error(f"Серьезная ошибка при запуске встроенного прокси-сервера: {e}")
715
+ # Даже если встроенный прокси не запустился, не сдаемся - используем директные URL
716
 
717
  # Настраиваем переменные окружения для Playground UI
718
  os.environ["PORT"] = "7860"