3v324v23 commited on
Commit
588adc0
·
1 Parent(s): 6b8158b

Fix TEN-Agent for HuggingFace Space: Replace Go server with Python wrapper

Browse files
Files changed (10) hide show
  1. .env.example +6 -2
  2. .space/README.md +16 -0
  3. .space/hf-space.sh +41 -0
  4. Dockerfile +38 -66
  5. README.md +60 -10
  6. api_wrapper.py +264 -0
  7. app.py +375 -199
  8. proxy.py +130 -0
  9. requirements.txt +7 -0
  10. start.sh +40 -18
.env.example CHANGED
@@ -1,7 +1,6 @@
1
  # Server Configuration
2
  LOG_PATH=/tmp/ten_agent
3
  LOG_STDOUT=true
4
- SERVER_PORT=7860
5
 
6
  # Agora App ID and App Certificate
7
  AGORA_APP_ID=
@@ -16,4 +15,9 @@ OPENAI_MODEL=gpt-4o
16
  DEEPGRAM_API_KEY=
17
 
18
  # Text to Speech
19
- ELEVENLABS_TTS_KEY=
 
 
 
 
 
 
1
  # Server Configuration
2
  LOG_PATH=/tmp/ten_agent
3
  LOG_STDOUT=true
 
4
 
5
  # Agora App ID and App Certificate
6
  AGORA_APP_ID=
 
15
  DEEPGRAM_API_KEY=
16
 
17
  # Text to Speech
18
+ ELEVENLABS_API_KEY=
19
+
20
+ # Ports configuration
21
+ API_PORT=8080
22
+ UI_PORT=3000
23
+ GRADIO_PORT=7860
.space/README.md ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # TEN-Agent на Hugging Face Space
2
+
3
+ Этот файл содержит конфигурацию для запуска TEN-Agent на Hugging Face Space.
4
+
5
+ ## Особенности
6
+
7
+ - Использует Python API Wrapper вместо оригинального Go-сервера
8
+ - Хранит все временные файлы в директории `/tmp/ten_user`
9
+ - Запускает UI через Gradio
10
+
11
+ ## Запуск
12
+
13
+ Для запуска используется скрипт `hf-space.sh`, который:
14
+ 1. Создает необходимые директории
15
+ 2. Проверяет наличие файлов
16
+ 3. Запускает приложение через `app.py`
.space/hf-space.sh ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ echo "===== Starting TEN-Agent in Python Wrapper Mode ====="
4
+ echo "$(date)"
5
+ echo "Current directory: $(pwd)"
6
+
7
+ echo "===== Environment Information ====="
8
+ echo "User: $(whoami || echo 'Unknown')"
9
+ echo "Groups: $(groups || echo 'Unknown')"
10
+ echo "Home directory: $HOME"
11
+
12
+ # Создаем необходимые директории
13
+ echo "Creating temporary directories in /tmp..."
14
+ mkdir -p /tmp/ten_user/agents
15
+ mkdir -p /tmp/ten_user/logs
16
+ chmod -R 777 /tmp/ten_user
17
+
18
+ # Проверяем наличие файлов
19
+ echo "Checking necessary files..."
20
+ if [ -f "api_wrapper.py" ]; then
21
+ echo "✅ api_wrapper.py found"
22
+ else
23
+ echo "❌ api_wrapper.py missing!"
24
+ exit 1
25
+ fi
26
+
27
+ if [ -f "app.py" ]; then
28
+ echo "✅ app.py found"
29
+ else
30
+ echo "❌ app.py missing!"
31
+ exit 1
32
+ fi
33
+
34
+ # Проверяем python и зависимости
35
+ echo "Checking Python..."
36
+ python3 --version
37
+ python3 -c "import gradio; import fastapi; print('✅ All dependencies installed')" || echo "❌ Dependencies missing"
38
+
39
+ # Запускаем приложение через Python wrapper
40
+ echo "Starting TEN-Agent via Python wrapper (app.py)..."
41
+ python3 app.py
Dockerfile CHANGED
@@ -1,82 +1,54 @@
1
- FROM ubuntu:22.04
2
 
3
- # Установка базовых пакетов
 
 
4
  RUN apt-get update && apt-get install -y \
5
- curl \
6
- git \
7
- wget \
8
- make \
9
- gcc \
10
- build-essential \
11
- libasound2 \
12
- libgstreamer1.0-dev \
13
- libunwind-dev \
14
- libc++1 \
15
- libssl-dev \
16
  python3 \
17
- python3-venv \
18
  python3-pip \
19
- python3-dev \
20
- unzip \
21
- jq \
22
- vim \
23
- ca-certificates \
24
- && apt-get clean && rm -rf /var/lib/apt/lists/* && rm -rf /tmp/*
25
-
26
- # Установка Go
27
- RUN wget https://golang.org/dl/go1.21.0.linux-amd64.tar.gz && \
28
- tar -C /usr/local -xzf go1.21.0.linux-amd64.tar.gz && \
29
- rm go1.21.0.linux-amd64.tar.gz
30
 
31
- # Настройка окружения Go
32
- ENV PATH=$PATH:/usr/local/go/bin
33
- ENV GOPATH=/go
34
- ENV PATH=$PATH:$GOPATH/bin
35
- RUN mkdir -p /go && chmod 777 /go
36
 
37
- # Создание директории для кэша Go
38
- ENV GOCACHE=/tmp/go-cache
39
- RUN mkdir -p /tmp/go-cache && chmod 777 /tmp/go-cache
40
 
41
- # Установка Node.js и pnpm
42
- RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
43
- apt-get install -y nodejs && \
44
- npm install -g pnpm
45
 
46
- # Клонирование репозитория TEN-Agent
47
- WORKDIR /app
48
- RUN git clone --depth 1 https://github.com/TEN-framework/TEN-Agent.git /app
49
 
50
- # Создание пользовательских директорий для временных файлов
51
- RUN mkdir -p /tmp/ten_user && chmod 777 /tmp/ten_user
52
- RUN mkdir -p /app/fallback && chmod 777 /app/fallback
 
53
 
54
- # Компиляция API сервера
55
- RUN mkdir -p /app/server/bin && \
56
- cd /app/server && \
57
- GOCACHE=/tmp/go-cache go build -o bin/api main.go && \
58
- chmod +x bin/api && \
59
- cp bin/api /app/fallback/api && \
60
- chmod 777 /app/fallback/api
61
 
62
- # Проверяем, что API сервер скомпилирован
63
- RUN ls -la /app/server/bin/api /app/fallback/api
 
64
 
65
- # Создание .env файла из окружения Hugging Face Space
66
- # Используем простой формат без пробелов для предотвращения ошибок
67
- RUN echo "AGORA_APP_ID=${AGORA_APP_ID}\nAGORA_APP_CERTIFICATE=${AGORA_APP_CERTIFICATE}\nAZURE_STT_KEY=${AZURE_STT_KEY}\nAZURE_STT_REGION=${AZURE_STT_REGION}\nAZURE_TTS_KEY=${AZURE_TTS_KEY}\nAZURE_TTS_REGION=${AZURE_TTS_REGION}\nOPENAI_API_KEY=${OPENAI_API_KEY}" > /app/.env
68
-
69
- # Предварительная установка зависимостей для playground
70
- # Делаем это отдельно, чтобы не пересобирать при каждом изменении
71
- RUN cd /app/playground && pnpm install
72
 
73
- # Копируем стартовые скрипты
74
- COPY start.sh /app/start.sh
75
- COPY fallback.py /app/fallback.py
76
- RUN chmod +x /app/start.sh /app/fallback.py
77
 
78
- # Открываем порты (7860 для HF Space, 8080 для API)
79
- EXPOSE 7860 8080
80
 
81
- # Запускаем TEN-Agent через fallback скрипт
 
 
82
  CMD ["/app/start.sh"]
 
1
+ FROM node:18
2
 
3
+ WORKDIR /app
4
+
5
+ # Устанавливаем Python
6
  RUN apt-get update && apt-get install -y \
 
 
 
 
 
 
 
 
 
 
 
7
  python3 \
 
8
  python3-pip \
9
+ git \
10
+ curl \
11
+ && rm -rf /var/lib/apt/lists/*
 
 
 
 
 
 
 
 
12
 
13
+ # Копируем файлы API wrapper и конфигурации
14
+ COPY api_wrapper.py /app/
15
+ COPY app.py /app/
16
+ COPY proxy.py /app/
17
+ COPY .space/hf-space.sh /app/
18
 
19
+ # Создаем директории
20
+ RUN mkdir -p /tmp/ten_user/agents /tmp/ten_user/logs /app/backup
21
+ RUN chmod -R 777 /tmp /app/backup
22
 
23
+ # Устанавливаем Python зависимости
24
+ COPY requirements.txt /app/
25
+ RUN pip3 install -r requirements.txt
 
26
 
27
+ # Клонируем репозиторий TEN-Agent
28
+ RUN git clone --depth 1 https://github.com/TEN-framework/TEN-Agent.git /tmp/ten-agent
 
29
 
30
+ # Копируем только необходимые файлы из репозитория
31
+ RUN mkdir -p /app/playground
32
+ RUN cp -r /tmp/ten-agent/playground/* /app/playground/
33
+ RUN rm -rf /tmp/ten-agent
34
 
35
+ # Устанавливаем pnpm
36
+ RUN npm install -g pnpm@8
 
 
 
 
 
37
 
38
+ # Устанавливаем dependencies для playground
39
+ WORKDIR /app/playground
40
+ RUN pnpm install
41
 
42
+ # Возвращаемся в основную директорию
43
+ WORKDIR /app
 
 
 
 
 
44
 
45
+ # Делаем скрипт запуска исполняемым
46
+ RUN chmod +x /app/hf-space.sh
 
 
47
 
48
+ # Экспортируем порт
49
+ EXPOSE 7860 3000 8080
50
 
51
+ # Запускаем приложение через скрипт
52
+ COPY start.sh /app/
53
+ RUN chmod +x /app/start.sh
54
  CMD ["/app/start.sh"]
README.md CHANGED
@@ -5,6 +5,8 @@ colorFrom: blue
5
  colorTo: purple
6
  sdk: docker
7
  sdk_version: "latest"
 
 
8
  pinned: false
9
  ---
10
 
@@ -21,17 +23,63 @@ pinned: false
21
 
22
  # TEN-Agent на Hugging Face Space
23
 
24
- Это официальная версия [TEN-Agent](https://github.com/TEN-framework/TEN-Agent), развернутая на Hugging Face Space. Приложение позволяет взаимодействовать с голосовыми и чат-агентами, используя различные языковые модели и расширения.
25
 
26
- ## Что такое TEN-Agent?
27
 
28
- TEN Agent - это конверсационный голосовой ИИ-агент, работающий на платформе TEN, интегрирующий Deepseek, Gemini, OpenAI, технологии RTC, и даже аппаратные возможности ESP32. Он обеспечивает возможности ИИ в реальном времени, такие как зрение, слух и речь, и полностью совместим с платформами Dify и Coze.
29
 
30
- ## Как использовать?
31
 
32
- 1. Выберите тип графа (например, Voice Agent, Chat Agent)
33
- 2. Выберите соответствующий модуль
34
- 3. Выберите расширение и настройте параметры API-ключей
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
 
36
  ## Доступные функции
37
 
@@ -42,9 +90,11 @@ TEN Agent - это конверсационный голосовой ИИ-аге
42
 
43
  ## Технические детали
44
 
45
- Этот Space полностью воспроизводит официальный контейнер TEN-Agent, используя систему сборки `task` с командами:
46
- - `task use` - для сборки агента
47
- - `task run` - для запуска сервера
 
 
48
 
49
  ## Ссылки
50
 
 
5
  colorTo: purple
6
  sdk: docker
7
  sdk_version: "latest"
8
+ app_port: 7860
9
+ app_file: app.py
10
  pinned: false
11
  ---
12
 
 
23
 
24
  # TEN-Agent на Hugging Face Space
25
 
26
+ Это адаптированная версия [TEN-Agent](https://github.com/TEN-framework/TEN-Agent) для работы на платформе Hugging Face Spaces.
27
 
28
+ ## Описание
29
 
30
+ TEN-Agent - это разговорный голосовой AI-агент, работающий на базе фреймворка TEN. Он интегрирует различные LLM модели, включая Deepseek, Gemini, OpenAI, технологии RTC и другие инструменты.
31
 
32
+ ## Особенности
33
 
34
+ - 🗣️ **Голосовое общение**: Интеграция с ASR (распознавание речи) и TTS (синтез речи)
35
+ - 👀 **Зрение**: Обработка и анализ изображений с камеры
36
+ - 🧠 **Различные LLM**: Поддержка OpenAI, Gemini, DeepSeek и других
37
+ - 🔗 **Расширения**: Модульная архитектура с возможностью добавления новых функций
38
+
39
+ ## Метод запуска (ВАЖНО!)
40
+
41
+ Для решения проблем с запуском в Hugging Face Space, мы используем:
42
+
43
+ 1. **Python API Wrapper** вместо оригинального Go-сервера
44
+ 2. **Хранение всех файлов** в директории `/tmp/ten_user`
45
+ 3. **Запуск через app.py**, а не через оригинальные скрипты
46
+
47
+ > **Эта версия специально адаптирована для Hugging Face Space и решает проблемы с правами доступа!**
48
+
49
+ ## Настройка
50
+
51
+ В интерфейсе необходимо настроить следующие API ключи:
52
+
53
+ 1. **OpenAI API Key**: Для текстовой обработки
54
+ 2. **Deepgram API Key**: Для распознавания речи
55
+ 3. **ElevenLabs API Key**: Для генерации голоса
56
+ 4. **Agora App ID и App Certificate**: Для работы с RTC
57
+
58
+ ## Графы
59
+
60
+ В системе предустановлены два графа:
61
+
62
+ 1. **Voice Agent**: Голосовой агент с OpenAI и ElevenLabs
63
+ 2. **Chat Agent**: Текстовый чат с OpenAI
64
+
65
+ ## Как использовать
66
+
67
+ 1. Дождитесь полной загрузки интерфейса
68
+ 2. Нажмите на кнопку "Открыть TEN Agent UI"
69
+ 3. В новой вкладке настройте API ключи
70
+ 4. Выберите нужный график и начните общение с агентом
71
+
72
+ ## Ограничения
73
+
74
+ В текущей версии на Hugging Face Space имеются следующие ограничения:
75
+
76
+ - Ограниченная производительность веб-интерфейса
77
+ - Отсутствие постоянного хранилища для загружаемых файлов
78
+ - Некоторые функции могут работать медленнее, чем в локальной установке
79
+
80
+ ## Запуск локально
81
+
82
+ Для локального запуска полной версии следуйте инструкциям в [официальном репозитории](https://github.com/TEN-framework/TEN-Agent).
83
 
84
  ## Доступные функции
85
 
 
90
 
91
  ## Технические детали
92
 
93
+ Этот Space использует свой собственный метод запуска через Python-обертку:
94
+
95
+ ```python
96
+ python3 app.py
97
+ ```
98
 
99
  ## Ссылки
100
 
api_wrapper.py ADDED
@@ -0,0 +1,264 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ import http.server
3
+ import json
4
+ import os
5
+ import sys
6
+ import logging
7
+ from pathlib import Path
8
+ import time
9
+ from urllib.parse import parse_qs, urlparse
10
+
11
+ # Настройка логирования
12
+ logging.basicConfig(
13
+ level=logging.INFO,
14
+ format='%(asctime)s [%(levelname)s] %(message)s',
15
+ handlers=[logging.StreamHandler()]
16
+ )
17
+ logger = logging.getLogger("ten_api")
18
+
19
+ # Проверяем, запущены ли мы в HuggingFace Space
20
+ IS_HF_SPACE = os.environ.get("SPACE_ID") is not None
21
+ USE_WRAPPER = os.environ.get("USE_WRAPPER", "false").lower() in ("true", "1", "yes")
22
+
23
+ # Путь к директории с агентами
24
+ if IS_HF_SPACE or USE_WRAPPER:
25
+ TMP_DIR = os.environ.get("TMP_DIR", "/tmp/ten_user")
26
+ AGENT_DIR = os.environ.get("TEN_AGENT_DIR", f"{TMP_DIR}/agents")
27
+ else:
28
+ AGENT_DIR = os.environ.get("TEN_AGENT_DIR", "/tmp/ten_user/agents")
29
+
30
+ logger.info(f"Using agent directory: {AGENT_DIR}")
31
+ logger.info(f"Running in HuggingFace Space: {IS_HF_SPACE}")
32
+ logger.info(f"Using Wrapper: {USE_WRAPPER}")
33
+
34
+ class TENAgentHandler(http.server.BaseHTTPRequestHandler):
35
+ def log_message(self, format, *args):
36
+ """Переопределение логирования для вывода в stdout"""
37
+ logger.info("%s - %s", self.address_string(), format % args)
38
+
39
+ def _set_headers(self, content_type="application/json"):
40
+ self.send_response(200)
41
+ self.send_header('Content-type', content_type)
42
+ self.send_header('Access-Control-Allow-Origin', '*')
43
+ self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
44
+ self.send_header('Access-Control-Allow-Headers', 'Content-Type, Authorization')
45
+ self.end_headers()
46
+
47
+ def do_OPTIONS(self):
48
+ self._set_headers()
49
+
50
+ def do_GET(self):
51
+ logger.info(f"GET request: {self.path}")
52
+
53
+ # Разбор URL для параметров
54
+ parsed_url = urlparse(self.path)
55
+ path = parsed_url.path
56
+ query_params = parse_qs(parsed_url.query)
57
+
58
+ # Обрабатываем API endpoints
59
+ if path == "/graphs" or path == "/api/graphs":
60
+ self._handle_graphs()
61
+ elif path == "/health" or path == "/":
62
+ self._handle_health()
63
+ elif path == "/list":
64
+ self._handle_list()
65
+ elif path == "/dev-tmp/addons/default-properties":
66
+ self._handle_default_properties()
67
+ elif path == "/vector/document/preset/list":
68
+ self._handle_vector_preset_list()
69
+ elif path.startswith("/api/dev/v1/packages"):
70
+ self._handle_packages()
71
+ elif path.startswith("/api/designer/v1/packages"):
72
+ self._handle_packages()
73
+ else:
74
+ # Для всех остальных запросов возвращаем 404
75
+ self.send_error(404, f"Not found: {path}")
76
+
77
+ def _handle_graphs(self):
78
+ """Обработка запроса на получение списка графов"""
79
+ try:
80
+ property_file = Path(AGENT_DIR) / "property.json"
81
+ if not property_file.exists():
82
+ logger.error(f"Property file not found at {property_file}")
83
+ self.send_error(404, "Property file not found")
84
+ return
85
+
86
+ with open(property_file, "r") as f:
87
+ property_data = json.load(f)
88
+
89
+ graphs = property_data.get("graphs", [])
90
+ self._set_headers()
91
+ self.wfile.write(json.dumps(graphs).encode())
92
+ logger.info(f"Returned {len(graphs)} graphs")
93
+ except Exception as e:
94
+ logger.error(f"Error reading property.json: {e}")
95
+ self.send_error(500, f"Internal error: {e}")
96
+
97
+ def _handle_health(self):
98
+ """Обработка запроса на проверку статуса сервера"""
99
+ self._set_headers()
100
+ self.wfile.write(json.dumps({
101
+ "status": "ok",
102
+ "time": time.time(),
103
+ "is_hf_space": IS_HF_SPACE,
104
+ "using_wrapper": True
105
+ }).encode())
106
+
107
+ def _handle_list(self):
108
+ """Обработка запроса на получение списка активных сессий"""
109
+ self._set_headers()
110
+ self.wfile.write(json.dumps([]).encode())
111
+
112
+ def _handle_default_properties(self):
113
+ """Обработка запроса на получение настроек по умолчанию"""
114
+ self._set_headers()
115
+ self.wfile.write(json.dumps({}).encode())
116
+
117
+ def _handle_vector_preset_list(self):
118
+ """Обработка запроса на получение списка пресетов векторов"""
119
+ self._set_headers()
120
+ self.wfile.write(json.dumps([]).encode())
121
+
122
+ def _handle_packages(self):
123
+ """Обработка запросов к пакетам"""
124
+ # Этот м��тод эмулирует возврат списка графов и для других эндпоинтов
125
+ try:
126
+ property_file = Path(AGENT_DIR) / "property.json"
127
+ if not property_file.exists():
128
+ logger.error(f"Property file not found at {property_file}")
129
+ self.send_error(404, "Property file not found")
130
+ return
131
+
132
+ with open(property_file, "r") as f:
133
+ property_data = json.load(f)
134
+
135
+ graphs = property_data.get("graphs", [])
136
+ self._set_headers()
137
+ response_data = {
138
+ "data": graphs,
139
+ "status": 200,
140
+ "message": "Success"
141
+ }
142
+ self.wfile.write(json.dumps(response_data).encode())
143
+ logger.info(f"Handled packages request and returned {len(graphs)} graphs")
144
+ except Exception as e:
145
+ logger.error(f"Error handling packages request: {e}")
146
+ self.send_error(500, f"Internal error: {e}")
147
+
148
+ def do_POST(self):
149
+ logger.info(f"POST request: {self.path}")
150
+
151
+ # Читаем тело запроса
152
+ content_length = int(self.headers['Content-Length']) if 'Content-Length' in self.headers else 0
153
+ post_data = self.rfile.read(content_length)
154
+
155
+ try:
156
+ request_data = json.loads(post_data) if content_length > 0 else {}
157
+ except json.JSONDecodeError:
158
+ request_data = {}
159
+
160
+ logger.info(f"Request data: {json.dumps(request_data)[:200]}...")
161
+
162
+ # Обрабатываем различные POST запросы
163
+ if self.path == "/ping":
164
+ self._handle_ping()
165
+ elif self.path == "/token/generate":
166
+ self._handle_token_generate(request_data)
167
+ elif self.path == "/start":
168
+ self._handle_start(request_data)
169
+ elif self.path == "/stop":
170
+ self._handle_stop(request_data)
171
+ elif self.path == "/vector/document/update" or self.path == "/vector/document/upload":
172
+ self._handle_vector_document(request_data)
173
+ elif self.path.startswith("/api/dev/v1/packages") or self.path.startswith("/api/designer/v1/packages"):
174
+ self._handle_packages_post(request_data)
175
+ else:
176
+ # Для всех остальных запросов возвращаем 404
177
+ self.send_error(404, f"Not found: {self.path}")
178
+
179
+ def _handle_ping(self):
180
+ """Обработка ping запроса"""
181
+ self._set_headers()
182
+ self.wfile.write(json.dumps({
183
+ "status": "ok",
184
+ "timestamp": time.time(),
185
+ "server": "ten-agent-api-wrapper",
186
+ "in_hf_space": IS_HF_SPACE
187
+ }).encode())
188
+
189
+ def _handle_token_generate(self, request_data):
190
+ """Обработка запроса на генерацию токена"""
191
+ self._set_headers()
192
+ response = {
193
+ "token": "dummy_token_for_agora",
194
+ "request_id": request_data.get("RequestId", ""),
195
+ "channel_name": request_data.get("ChannelName", ""),
196
+ "uid": request_data.get("Uid", 0)
197
+ }
198
+ self.wfile.write(json.dumps(response).encode())
199
+ logger.info(f"Generated token for channel: {request_data.get('ChannelName', '')}")
200
+
201
+ def _handle_start(self, request_data):
202
+ """Обработка запроса на запуск сессии"""
203
+ self._set_headers()
204
+ # Возвращаем успешный статус и ID сессии
205
+ response = {
206
+ "status": "ok",
207
+ "session_id": f"dummy_session_{int(time.time())}",
208
+ "message": "Session started successfully",
209
+ "graph_file": request_data.get("graph_file", "unknown")
210
+ }
211
+ self.wfile.write(json.dumps(response).encode())
212
+ logger.info(f"Started session with graph: {request_data.get('graph_file', '')}")
213
+
214
+ def _handle_stop(self, request_data):
215
+ """Обработка запроса на остановку сессии"""
216
+ self._set_headers()
217
+ self.wfile.write(json.dumps({
218
+ "status": "ok",
219
+ "message": "Session stopped successfully"
220
+ }).encode())
221
+ logger.info(f"Stopped session: {request_data.get('session_id', '')}")
222
+
223
+ def _handle_vector_document(self, request_data):
224
+ """Обработка запроса на работу с векторными документами"""
225
+ self._set_headers()
226
+ self.wfile.write(json.dumps({
227
+ "status": "ok",
228
+ "document_id": f"dummy_doc_{int(time.time())}",
229
+ "message": "Document processed successfully"
230
+ }).encode())
231
+ logger.info(f"Processed vector document: {request_data.get('name', 'unnamed')}")
232
+
233
+ def _handle_packages_post(self, request_data):
234
+ """Обработка POST запросов к пакетам"""
235
+ self._set_headers()
236
+ response_data = {
237
+ "data": {},
238
+ "status": 200,
239
+ "message": "Success"
240
+ }
241
+ self.wfile.write(json.dumps(response_data).encode())
242
+ logger.info(f"Handled packages POST request")
243
+
244
+ def run(server_class=http.server.HTTPServer, handler_class=TENAgentHandler, port=8080):
245
+ server_address = ('', port)
246
+ httpd = server_class(server_address, handler_class)
247
+ logger.info(f"Starting API server on port {port}...")
248
+ logger.info(f"Using agent directory: {AGENT_DIR}")
249
+ try:
250
+ httpd.serve_forever()
251
+ except KeyboardInterrupt:
252
+ logger.info("Server stopped by user")
253
+ except Exception as e:
254
+ logger.error(f"Server error: {e}")
255
+ raise
256
+
257
+ if __name__ == "__main__":
258
+ port = int(os.environ.get("API_PORT", 8080))
259
+ try:
260
+ run(port=port)
261
+ except KeyboardInterrupt:
262
+ logger.info("Server stopped by user")
263
+ except Exception as e:
264
+ logger.error(f"Server error: {e}")
app.py CHANGED
@@ -12,219 +12,395 @@ import http.server
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
- class ProxyHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
52
- def do_POST(self):
53
- print(f"Получен запрос: {self.path}")
54
-
55
- # Перенаправляем запрос /api/dev/v1/packages/reload на /graphs
56
- if self.path.startswith('/api/dev/v1/packages/reload') or self.path.startswith('/api/designer/v1/packages/reload'):
57
- try:
58
- print("Перенаправление на /graphs")
59
- with urllib.request.urlopen("http://localhost:8080/graphs") as response:
60
- data = response.read()
61
-
62
- # Преобразуем данные для нужного формата
63
- try:
64
- graphs = json.loads(data)
65
- formatted_response = {
66
- "data": graphs,
67
- "status": 200,
68
- "message": "Success"
69
- }
70
- response_data = json.dumps(formatted_response).encode('utf-8')
71
- except:
72
- response_data = data
73
-
74
- # Отправляем ответ
75
- self.send_response(200)
76
- self.send_header('Content-Type', 'application/json')
77
- self.send_header('Content-Length', len(response_data))
78
- self.send_header('Access-Control-Allow-Origin', '*')
79
- self.end_headers()
80
- self.wfile.write(response_data)
81
- print(f"Отправлен ответ: {response_data.decode('utf-8')}")
82
- except urllib.error.URLError as e:
83
- print(f"Ошибка при перенаправлении: {e}")
84
- self.send_error(500, f"Proxy Error: {e}")
85
- else:
86
- self.send_error(404, "Not Found")
87
 
88
- def do_OPTIONS(self):
89
- self.send_response(200)
90
- self.send_header('Access-Control-Allow-Origin', '*')
91
- self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
92
- self.send_header('Access-Control-Allow-Headers', 'Content-Type')
93
- self.end_headers()
94
-
95
- def run_proxy_server():
96
- """Запускает прокси-сервер на порту 49483"""
97
- handler = ProxyHTTPRequestHandler
98
- httpd = socketserver.TCPServer(("", 49483), handler)
99
- print("Прокси-сервер запущен на порту 49483")
100
- httpd.serve_forever()
101
-
102
- def check_files():
103
- """Проверяет и выводит информацию о важных файлах"""
104
- files_to_check = [
105
- "/app/agents/property.json",
106
- "/app/agents/manifest.json",
107
- "/app/agents/voice_agent.json",
108
- "/app/agents/chat_agent.json",
109
- "/app/server/bin/api"
110
- ]
111
-
112
- print("\n=== Проверка критических файлов ===")
113
- for file_path in files_to_check:
114
- path = Path(file_path)
115
- if path.exists():
116
- if path.is_file():
117
- size = path.stat().st_size
118
- print(f"✅ {file_path} (размер: {size} байт)")
119
 
120
- # Если это JSON файл, выводим его содержимое
121
- if file_path.endswith('.json'):
122
- try:
123
- with open(file_path, 'r') as f:
124
- content = json.load(f)
125
- print(f" Содержимое: {json.dumps(content, indent=2)}")
126
- except Exception as e:
127
- print(f" Ошибка чтения JSON: {e}")
128
- else:
129
- print(f"❌ {file_path} (это директория, а не файл)")
130
- else:
131
- print(f"❌ {file_path} (файл не найден)")
132
-
133
- print("\n=== Проверка структуры директорий ===")
134
- print("Содержимое /app/agents:")
135
- subprocess.run(["ls", "-la", "/app/agents"])
136
-
137
- print("\nПроверка прав доступа:")
138
- subprocess.run(["stat", "/app/agents"])
139
- subprocess.run(["stat", "/app/agents/property.json"])
140
-
141
- def test_api():
142
- """Делает запрос к API для получения списка графов"""
143
- import urllib.request
144
- import urllib.error
145
 
146
- print("\n=== Тестирование API ===")
147
- try:
148
- # Даем серверу время запуститься
149
- time.sleep(3)
150
- with urllib.request.urlopen("http://localhost:8080/graphs") as response:
151
- data = response.read().decode('utf-8')
152
- print(f"Ответ /graphs: {data}")
153
- except urllib.error.URLError as e:
154
- print(f"Ошибка запроса к API: {e}")
155
- except Exception as e:
156
- print(f"Неизвестная ошибка при запросе к API: {e}")
157
 
158
  def main():
159
- processes = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  try:
161
- # Пути к исполняемым файлам
162
- api_binary = Path("/app/server/bin/api")
163
- playground_dir = Path("/app/playground")
164
-
165
- # Проверяем существование файлов
166
- if not api_binary.exists():
167
- print(f"ERROR: API binary not found at {api_binary}", file=sys.stderr)
168
- return 1
169
-
170
- if not playground_dir.exists():
171
- print(f"ERROR: Playground directory not found at {playground_dir}", file=sys.stderr)
172
- return 1
173
-
174
- # Проверяем и создаем property.json
175
- check_and_create_property_json()
176
-
177
- # Проверка файлов перед запуском
178
- check_files()
179
-
180
- # Запускаем API сервер
181
- print("Starting TEN-Agent API server on port 8080...")
182
- api_process = subprocess.Popen([str(api_binary)])
183
- processes.append(api_process)
184
-
185
- # Тестируем API
186
- test_thread = threading.Thread(target=test_api)
187
- test_thread.daemon = True
188
- test_thread.start()
189
-
190
- # Запускаем прокси-сервер на порту 49483
191
- proxy_thread = threading.Thread(target=run_proxy_server)
192
- proxy_thread.daemon = True
193
- proxy_thread.start()
194
-
195
- # Запускаем Playground UI в режиме dev на порту 7860 (порт Hugging Face)
196
- print("Starting Playground UI in development mode on port 7860...")
197
- os.environ["PORT"] = "7860"
198
- os.environ["AGENT_SERVER_URL"] = "http://localhost:8080"
199
- os.environ["NEXT_PUBLIC_EDIT_GRAPH_MODE"] = "true" # Включаем расширенный режим редактирования
200
- os.environ["NEXT_PUBLIC_DISABLE_CAMERA"] = "true" # Отключаем запрос на использование камеры
201
-
202
- playground_process = subprocess.Popen(
203
- ["pnpm", "dev"],
204
- cwd=str(playground_dir),
205
- env=os.environ
206
- )
207
- processes.append(playground_process)
208
-
209
- # Ожидаем завершения процессов
210
- for proc in processes:
211
- proc.wait()
212
 
 
213
  except KeyboardInterrupt:
214
- print("Shutting down...")
215
- except Exception as e:
216
- print(f"Error: {e}", file=sys.stderr)
217
- finally:
218
- # Завершение процессов
219
- for proc in processes:
220
- if proc and proc.poll() is None:
221
- proc.terminate()
222
- try:
223
- proc.wait(timeout=5)
224
- except subprocess.TimeoutExpired:
225
- proc.kill()
226
-
227
- return 0
228
 
229
  if __name__ == "__main__":
230
  # Корректная обработка сигналов
 
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
+ TMP_DIR.mkdir(exist_ok=True, parents=True)
44
+ AGENTS_DIR.mkdir(exist_ok=True, parents=True)
45
+ LOGS_DIR.mkdir(exist_ok=True, parents=True)
46
+
47
+ # Создаем пустой файл логов
48
+ (LOGS_DIR / "server.log").touch()
49
+ print(f"Директории созданы в {TMP_DIR}")
50
+
51
+ def create_config_files():
52
+ """Создает базовые файлы конфигурации"""
53
+ print("Создание конфигурационных файлов...")
54
+
55
+ # Создаем property.json с графами
56
+ property_data = {
57
+ "name": "TEN Agent Demo",
58
+ "version": "0.0.1",
59
+ "extensions": ["openai_chatgpt", "elevenlabs_tts", "deepgram_asr"],
60
+ "description": "TEN Agent on Hugging Face Space",
61
+ "graphs": [
62
+ {
63
+ "name": "Voice Agent",
64
+ "description": "Basic voice agent with OpenAI and ElevenLabs",
65
+ "file": "voice_agent.json"
66
+ },
67
+ {
68
+ "name": "Chat Agent",
69
+ "description": "Simple chat agent with OpenAI",
70
+ "file": "chat_agent.json"
71
+ }
72
+ ]
73
+ }
74
+
75
+ with open(AGENTS_DIR / "property.json", "w") as f:
76
+ json.dump(property_data, f, indent=2)
77
+
78
+ # Создаем voice_agent.json
79
+ voice_agent = {
80
+ "_ten": {"version": "0.0.1"},
81
+ "nodes": [
82
+ {
83
+ "id": "start",
84
+ "type": "start",
85
+ "data": {"x": 100, "y": 100}
86
+ },
87
+ {
88
+ "id": "openai_chatgpt",
89
+ "type": "openai_chatgpt",
90
+ "data": {
91
+ "x": 300,
92
+ "y": 200,
93
+ "properties": {
94
+ "model": "gpt-3.5-turbo",
95
+ "temperature": 0.7,
96
+ "system_prompt": "You are a helpful assistant."
97
+ }
98
  }
99
+ },
100
+ {
101
+ "id": "elevenlabs_tts",
102
+ "type": "elevenlabs_tts",
103
+ "data": {
104
+ "x": 500,
105
+ "y": 200,
106
+ "properties": {
107
+ "voice_id": "21m00Tcm4TlvDq8ikWAM"
108
+ }
109
+ }
110
+ },
111
+ {
112
+ "id": "deepgram_asr",
113
+ "type": "deepgram_asr",
114
+ "data": {
115
+ "x": 300,
116
+ "y": 300,
117
+ "properties": {
118
+ "language": "ru"
119
+ }
120
+ }
121
+ },
122
+ {
123
+ "id": "end",
124
+ "type": "end",
125
+ "data": {"x": 700, "y": 100}
126
+ }
127
+ ],
128
+ "edges": [
129
+ {"id": "start_to_chatgpt", "source": "start", "target": "openai_chatgpt"},
130
+ {"id": "chatgpt_to_tts", "source": "openai_chatgpt", "target": "elevenlabs_tts"},
131
+ {"id": "tts_to_end", "source": "elevenlabs_tts", "target": "end"},
132
+ {"id": "asr_to_chatgpt", "source": "deepgram_asr", "target": "openai_chatgpt"}
133
+ ],
134
+ "groups": [],
135
+ "templates": [],
136
+ "root": "start"
137
+ }
138
+
139
+ with open(AGENTS_DIR / "voice_agent.json", "w") as f:
140
+ json.dump(voice_agent, f, indent=2)
141
+
142
+ # Создаем chat_agent.json (упрощенная версия)
143
+ chat_agent = {
144
+ "_ten": {"version": "0.0.1"},
145
+ "nodes": [
146
+ {
147
+ "id": "start",
148
+ "type": "start",
149
+ "data": {"x": 100, "y": 100}
150
+ },
151
+ {
152
+ "id": "openai_chatgpt",
153
+ "type": "openai_chatgpt",
154
+ "data": {
155
+ "x": 300,
156
+ "y": 200,
157
+ "properties": {
158
+ "model": "gpt-3.5-turbo",
159
+ "temperature": 0.7,
160
+ "system_prompt": "You are a helpful chat assistant."
161
+ }
162
+ }
163
+ },
164
+ {
165
+ "id": "end",
166
+ "type": "end",
167
+ "data": {"x": 500, "y": 100}
168
+ }
169
+ ],
170
+ "edges": [
171
+ {"id": "start_to_chatgpt", "source": "start", "target": "openai_chatgpt"},
172
+ {"id": "chatgpt_to_end", "source": "openai_chatgpt", "target": "end"}
173
+ ],
174
+ "groups": [],
175
+ "templates": [],
176
+ "root": "start"
177
+ }
178
+
179
+ with open(AGENTS_DIR / "chat_agent.json", "w") as f:
180
+ json.dump(chat_agent, f, indent=2)
181
+
182
+ print("Конфигурационные файлы созданы успешно")
183
+
184
+ def start_api_server():
185
+ """Запускает API сервер"""
186
+ print("Запуск API сервера...")
187
+
188
+ # Устанавливаем переменные окружения
189
+ api_env = os.environ.copy()
190
+ api_env["TEN_AGENT_DIR"] = str(AGENTS_DIR)
191
+ api_env["API_PORT"] = str(API_PORT)
192
+
193
+ # В HuggingFace Space нужны дополнительные настройки
194
+ if IS_HF_SPACE:
195
+ print("Configuring API server for HuggingFace Space environment...")
196
+ # Указываем серверу специально использовать API wrapper
197
+ api_env["USE_WRAPPER"] = "true"
198
+ # Отключаем логирование в файл
199
+ api_env["TEN_LOG_DISABLE_FILE"] = "true"
200
+ # Указываем путь для временных файлов
201
+ api_env["TMP_DIR"] = str(TMP_DIR)
202
+
203
+ # Запускаем Python API wrapper
204
+ api_cmd = ["python", "api_wrapper.py"]
205
+ print(f"Running API command: {' '.join(api_cmd)}")
206
+ api_process = subprocess.Popen(
207
+ api_cmd,
208
+ env=api_env,
209
+ stdout=subprocess.PIPE,
210
+ stderr=subprocess.PIPE
211
+ )
212
+
213
+ # Ждем запуска сервера
214
+ time.sleep(2)
215
+
216
+ # Проверяем, что процесс не упал
217
+ if api_process.poll() is not None:
218
+ stdout, stderr = api_process.communicate()
219
+ print(f"API сервер не запустился!")
220
+ print(f"STDOUT: {stdout.decode()}")
221
+ print(f"STDERR: {stderr.decode()}")
222
+ return None
223
+
224
+ print(f"API server started and listening on port {API_PORT}")
225
+
226
+ # Запускаем поток для вывода логов
227
+ def log_output(process, prefix):
228
+ for line in iter(process.stdout.readline, b''):
229
+ print(f"[{prefix}] {line.decode().strip()}")
230
+ for line in iter(process.stderr.readline, b''):
231
+ print(f"[{prefix} ERROR] {line.decode().strip()}")
232
+
233
+ log_thread = threading.Thread(target=log_output, args=(api_process, "API"))
234
+ log_thread.daemon = True
235
+ log_thread.start()
236
+
237
+ return api_process
238
+
239
+ def start_playground():
240
+ """Запускает Playground UI через Next.js"""
241
+ print("Запуск Playground UI...")
242
+
243
+ # Устанавливаем переменные окружения
244
+ ui_env = os.environ.copy()
245
+ ui_env["PORT"] = str(NEXTJS_PORT)
246
+ ui_env["AGENT_SERVER_URL"] = f"http://{INTERNAL_HOST}:{API_PORT}"
247
+ ui_env["NEXT_PUBLIC_EDIT_GRAPH_MODE"] = "true"
248
+ ui_env["NEXT_PUBLIC_DISABLE_CAMERA"] = "false"
249
+
250
+ # В HuggingFace Space нужны дополнительные настройки
251
+ if IS_HF_SPACE:
252
+ print("Configuring for HuggingFace Space environment...")
253
+ ui_env["NEXT_PUBLIC_IS_HF_SPACE"] = "true"
254
+ # Отключаем строгие проверки CORS для работы в iframe
255
+ ui_env["NEXT_PUBLIC_DISABLE_CORS"] = "true"
256
+
257
+ # Запускаем UI
258
+ ui_cmd = "cd playground && npm run dev"
259
+ print(f"Running UI command: {ui_cmd}")
260
+ ui_process = subprocess.Popen(
261
+ ui_cmd,
262
+ env=ui_env,
263
+ shell=True,
264
+ stdout=subprocess.PIPE,
265
+ stderr=subprocess.PIPE
266
+ )
267
+
268
+ # Ждем запуска UI
269
+ time.sleep(5)
270
+
271
+ # Проверяем, что процесс не упал
272
+ if ui_process.poll() is not None:
273
+ stdout, stderr = ui_process.communicate()
274
+ print(f"Playground UI не запустился!")
275
+ print(f"STDOUT: {stdout.decode()}")
276
+ print(f"STDERR: {stderr.decode()}")
277
+ return None
278
+
279
+ # Запускаем поток для вывода логов
280
+ def log_output(process, prefix):
281
+ for line in iter(process.stdout.readline, b''):
282
+ print(f"[{prefix}] {line.decode().strip()}")
283
+ for line in iter(process.stderr.readline, b''):
284
+ print(f"[{prefix} ERROR] {line.decode().strip()}")
285
+
286
+ log_thread = threading.Thread(target=log_output, args=(ui_process, "UI"))
287
+ log_thread.daemon = True
288
+ log_thread.start()
289
+
290
+ return ui_process
291
+
292
+ def create_interface():
293
+ """Создает Gradio интерфейс для редиректа"""
294
+ with gr.Blocks() as demo:
295
+ gr.Markdown("# TEN Agent на Hugging Face Space")
296
+ gr.Markdown("## Загрузка приложения...")
297
 
298
+ # Статус серверов
299
+ status_md = gr.Markdown("### Статус: Инициализация...")
300
 
301
+ with gr.Row():
302
+ col1, col2 = gr.Column(), gr.Column()
 
303
 
304
+ with col1:
305
+ # Кнопка для открытия UI в iframe
306
+ open_iframe = gr.Button("Показать TEN Agent в iframe")
307
+
308
+ # Функция для открытия UI в iframe
309
+ def show_iframe():
310
+ return f"""
311
+ <div style="border: 1px solid #ccc; padding: 10px; border-radius: 5px;">
312
+ <iframe src="{UI_URL}" width="100%" height="600px" frameborder="0"></iframe>
313
+ </div>
314
+ """
315
+
316
+ iframe_area = gr.HTML()
317
+ open_iframe.click(show_iframe, outputs=iframe_area)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
318
 
319
+ with col2:
320
+ # Ссылка на UI
321
+ gr.Markdown(f"""
322
+ ### Открыть TEN Agent в новой вкладке:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
323
 
324
+ <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;">Открыть TEN Agent UI</a>
325
+
326
+ ### ВАЖНО:
327
+
328
+ Настройте API ключи в интерфейсе для полноценной работы:
329
+ - OpenAI API Key
330
+ - ElevenLabs API Key
331
+ - Deepgram API Key
332
+ - Agora App ID и Certificate
333
+ """)
334
+
335
+ # Статус серверов и логи
336
+ with gr.Accordion("Системная информация", open=False):
337
+ api_status = gr.Textbox(label="Статус API сервера", value="Запускается...", interactive=False)
338
+ ui_status = gr.Textbox(label="Статус UI сервера", value="Запускается...", interactive=False)
339
+
340
+ # Функция обновления статуса
341
+ def update_status():
342
+ api_status_msg = "✅ Активен" if is_port_in_use(API_PORT) else "❌ Не активен"
343
+ ui_status_msg = "✅ Активен" if is_port_in_use(NEXTJS_PORT) else "❌ Не активен"
344
+ status_md_msg = f"### Статус: {'✅ Все системы работают' if is_port_in_use(API_PORT) and is_port_in_use(NEXTJS_PORT) else '⚠️ Есть проблемы'}"
345
+ return [api_status_msg, ui_status_msg, status_md_msg]
346
+
347
+ status_btn = gr.Button("Обновить статус")
348
+ status_btn.click(update_status, outputs=[api_status, ui_status, status_md])
349
 
350
+ return demo
351
+
352
+ # Вспомогательная функция для проверки, используется ли порт
353
+ def is_port_in_use(port):
354
+ import socket
355
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
356
+ try:
357
+ s.connect(('localhost', port))
358
+ return True
359
+ except:
360
+ return False
361
 
362
  def main():
363
+ # Создаем директории и файлы конфигурации
364
+ create_directories()
365
+ create_config_files()
366
+
367
+ # Запускаем API сервер
368
+ api_process = start_api_server()
369
+ if not api_process:
370
+ print("Не у��алось запустить API сервер")
371
+ return
372
+
373
+ # Запускаем Playground UI
374
+ ui_process = start_playground()
375
+ if not ui_process:
376
+ print("Не удалось запустить Playground UI")
377
+ api_process.terminate()
378
+ return
379
+
380
+ # Создаем Gradio интерфейс
381
+ demo = create_interface()
382
+
383
+ # Запускаем Gradio
384
+ demo.launch(server_port=GRADIO_PORT, server_name=INTERNAL_HOST, share=False)
385
+
386
+ # Остаемся в цикле до завершения процессов
387
  try:
388
+ while True:
389
+ if api_process.poll() is not None:
390
+ print("API сервер остановлен")
391
+ ui_process.terminate()
392
+ break
393
+
394
+ if ui_process.poll() is not None:
395
+ print("UI остановлен")
396
+ api_process.terminate()
397
+ break
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
398
 
399
+ time.sleep(1)
400
  except KeyboardInterrupt:
401
+ print("Принудительная остановка...")
402
+ api_process.terminate()
403
+ ui_process.terminate()
 
 
 
 
 
 
 
 
 
 
 
404
 
405
  if __name__ == "__main__":
406
  # Корректная обработка сигналов
proxy.py ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ import http.server
3
+ import socketserver
4
+ import urllib.request
5
+ import urllib.error
6
+ import json
7
+ import os
8
+ import sys
9
+ from pathlib import Path
10
+
11
+ # Порты для сервисов
12
+ API_PORT = int(os.environ.get("API_PORT", 8080))
13
+ UI_PORT = int(os.environ.get("UI_PORT", 3000))
14
+ PROXY_PORT = int(os.environ.get("PROXY_PORT", 49483))
15
+
16
+ class ProxyHandler(http.server.SimpleHTTPRequestHandler):
17
+ def log_message(self, format, *args):
18
+ """Переопределение логирования для вывода в stdout"""
19
+ print("%s - %s" % (self.address_string(), format % args))
20
+
21
+ def do_GET(self):
22
+ """Обработка GET запросов с перенаправлением"""
23
+ print(f"GET request: {self.path}")
24
+
25
+ # Если путь начинается с /api, перенаправляем на API сервер
26
+ if self.path.startswith('/api/'):
27
+ self._proxy_request("GET", API_PORT)
28
+ else:
29
+ self.send_error(404, "Not Found")
30
+
31
+ def do_POST(self):
32
+ """Обработка POST запросов с перенаправлением"""
33
+ print(f"POST request: {self.path}")
34
+
35
+ # Если путь начинается с /api, перенаправляем на API сервер
36
+ if self.path.startswith('/api/'):
37
+ self._proxy_request("POST", API_PORT)
38
+ elif self.path.startswith('/api/dev/v1/packages') or self.path.startswith('/api/designer/v1/packages'):
39
+ self._handle_packages_request()
40
+ else:
41
+ self.send_error(404, "Not Found")
42
+
43
+ def do_OPTIONS(self):
44
+ """Обработка OPTIONS запросов"""
45
+ self.send_response(200)
46
+ self.send_header('Access-Control-Allow-Origin', '*')
47
+ self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
48
+ self.send_header('Access-Control-Allow-Headers', 'Content-Type, Authorization')
49
+ self.end_headers()
50
+
51
+ def _proxy_request(self, method, port):
52
+ """Прокси запрос на указанный порт"""
53
+ target_url = f"http://localhost:{port}{self.path}"
54
+
55
+ try:
56
+ # Читаем тело запроса, если есть
57
+ content_length = int(self.headers['Content-Length']) if 'Content-Length' in self.headers else 0
58
+ body = self.rfile.read(content_length) if content_length > 0 else None
59
+
60
+ # Создаем запрос
61
+ req = urllib.request.Request(
62
+ target_url,
63
+ data=body,
64
+ headers={k: v for k, v in self.headers.items() if k.lower() not in ('host', 'content-length')},
65
+ method=method
66
+ )
67
+
68
+ # Выполняем запрос
69
+ with urllib.request.urlopen(req) as response:
70
+ # Копируем заголовки ответа
71
+ self.send_response(response.status)
72
+ for key, val in response.getheaders():
73
+ if key.lower() not in ('transfer-encoding', 'content-encoding'):
74
+ self.send_header(key, val)
75
+ self.send_header('Access-Control-Allow-Origin', '*')
76
+ self.end_headers()
77
+
78
+ # Копируем тело ответа
79
+ self.wfile.write(response.read())
80
+
81
+ except urllib.error.HTTPError as e:
82
+ self.send_response(e.code)
83
+ for key, val in e.headers.items():
84
+ if key.lower() not in ('transfer-encoding', 'content-encoding'):
85
+ self.send_header(key, val)
86
+ self.send_header('Access-Control-Allow-Origin', '*')
87
+ self.end_headers()
88
+ self.wfile.write(e.read())
89
+ except Exception as e:
90
+ self.send_error(500, f"Proxy Error: {str(e)}")
91
+
92
+ def _handle_packages_request(self):
93
+ """Обрабатывает запросы к пакетам"""
94
+ try:
95
+ # Читаем содержимое property.json
96
+ property_file = Path("/tmp/ten_user/agents/property.json")
97
+ if property_file.exists():
98
+ with open(property_file, 'r') as f:
99
+ property_data = json.load(f)
100
+
101
+ graphs = property_data.get("graphs", [])
102
+
103
+ # Формируем ответ
104
+ response = {
105
+ "data": graphs,
106
+ "status": 200,
107
+ "message": "Success"
108
+ }
109
+
110
+ # Отправляем ответ
111
+ self.send_response(200)
112
+ self.send_header('Content-Type', 'application/json')
113
+ self.send_header('Access-Control-Allow-Origin', '*')
114
+ self.end_headers()
115
+
116
+ self.wfile.write(json.dumps(response).encode('utf-8'))
117
+ else:
118
+ self.send_error(404, "Property file not found")
119
+ except Exception as e:
120
+ self.send_error(500, f"Error handling packages request: {str(e)}")
121
+
122
+ def run_proxy_server():
123
+ """Запускает прокси-сервер на указанном порту"""
124
+ handler = ProxyHandler
125
+ with socketserver.TCPServer(("", PROXY_PORT), handler) as httpd:
126
+ print(f"Starting proxy server on port {PROXY_PORT}...")
127
+ httpd.serve_forever()
128
+
129
+ if __name__ == "__main__":
130
+ run_proxy_server()
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ gradio>=4.0
2
+ requests>=2.28.0
3
+ fastapi>=0.100.0
4
+ python-dotenv>=1.0.0
5
+ uvicorn>=0.25.0
6
+ numpy>=1.24.0
7
+ websockets>=12.0
start.sh CHANGED
@@ -1,28 +1,50 @@
1
  #!/bin/bash
2
 
3
- # Вывод информации о запуске
4
- echo "===== Starting TEN-Agent on HuggingFace Space ====="
5
  echo "$(date)"
6
  echo "Current directory: $(pwd)"
7
 
8
- # Вывод информации о пользователе и его правах
9
- echo "===== Environment Information ====="
10
- echo "User: $(whoami)"
11
- echo "Groups: $(groups)"
12
- echo "Home directory: $HOME"
 
 
 
 
 
 
 
 
 
13
 
14
- # Проверяем наличие .env файла
15
- if [ -f .env ]; then
16
- echo "✅ .env file found"
17
- cat .env | grep -v "KEY\|CERTIFICATE" | sed 's/=.*/=***/'
18
  else
19
- echo "⚠️ Warning: .env file not found, will use environment variables"
 
20
  fi
21
 
22
- # Запускаем приложение напрямую через fallback скрипт
23
- echo "===== Starting TEN-Agent via fallback script ====="
24
- echo "Due to permission issues in Hugging Face Space, we'll use the fallback script"
25
- echo "This will create necessary files in /tmp where we have write access"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
- # Выполняем Python скрипт напрямую
28
- exec python3 /app/fallback.py
 
 
1
  #!/bin/bash
2
 
3
+ echo "===== Starting TEN-Agent in Python Mode ====="
 
4
  echo "$(date)"
5
  echo "Current directory: $(pwd)"
6
 
7
+ # Создаем необходимые директории
8
+ echo "Creating temporary directories in /tmp..."
9
+ mkdir -p /tmp/ten_user/agents
10
+ mkdir -p /tmp/ten_user/logs
11
+ chmod -R 777 /tmp/ten_user
12
+
13
+ # Проверяем наличие файлов
14
+ echo "Checking necessary files..."
15
+ if [ -f "api_wrapper.py" ]; then
16
+ echo "✅ api_wrapper.py found"
17
+ else
18
+ echo "❌ api_wrapper.py missing!"
19
+ exit 1
20
+ fi
21
 
22
+ if [ -f "app.py" ]; then
23
+ echo "✅ app.py found"
 
 
24
  else
25
+ echo " app.py missing!"
26
+ exit 1
27
  fi
28
 
29
+ # Убеждаемся, что fallback.py не будет использован
30
+ if [ -f "fallback.py" ]; then
31
+ echo "⚠️ Renaming fallback.py to fallback.py.bak to prevent conflicts"
32
+ mv fallback.py fallback.py.bak
33
+ fi
34
+
35
+ # Выводим информацию об окружении
36
+ echo "===== Environment Information ====="
37
+ echo "User: $(whoami || echo 'Unknown')"
38
+ echo "Home directory: $HOME"
39
+ echo "Python version: $(python3 --version)"
40
+ echo "Node version: $(node --version)"
41
+
42
+ # Запускаем прокси-сервер в фоновом режиме
43
+ echo "Starting proxy server..."
44
+ python3 proxy.py &
45
+ PROXY_PID=$!
46
+ echo "Proxy server started with PID: $PROXY_PID"
47
 
48
+ # Запускаем приложение через Python-обертку
49
+ echo "Starting TEN-Agent via Python wrapper (app.py)..."
50
+ exec python3 app.py