Исправлена проблема с правами Go при сборке и прокси для работы Playground UI
Browse files- Dockerfile +11 -8
- app.py +73 -66
Dockerfile
CHANGED
@@ -22,22 +22,25 @@ RUN apt-get clean && apt-get update && apt-get install -y --no-install-recommend
|
|
22 |
ca-certificates \
|
23 |
&& apt-get clean && rm -rf /var/lib/apt/lists/* && rm -rf /tmp/*
|
24 |
|
25 |
-
#
|
|
|
|
|
|
|
26 |
RUN wget https://golang.org/dl/go1.21.0.linux-amd64.tar.gz && \
|
27 |
tar -C /usr/local -xzf go1.21.0.linux-amd64.tar.gz && \
|
28 |
-
rm go1.21.0.linux-amd64.tar.gz
|
|
|
|
|
|
|
29 |
ENV PATH=$PATH:/usr/local/go/bin
|
30 |
-
ENV GOPATH=/go
|
31 |
ENV PATH=$PATH:$GOPATH/bin
|
32 |
|
33 |
-
# Установка Node.js 20.x
|
34 |
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
|
35 |
apt-get install -y nodejs && \
|
36 |
npm install -g pnpm
|
37 |
|
38 |
-
# Создаем пользователя для запуска приложения с правильными правами
|
39 |
-
RUN useradd -m -s /bin/bash tenuser
|
40 |
-
|
41 |
WORKDIR /app
|
42 |
|
43 |
# Клонирование репозитория TEN Agent (основная ветка)
|
@@ -70,7 +73,7 @@ RUN cd /app/server && \
|
|
70 |
|
71 |
# Сборка Playground UI с правами пользователя
|
72 |
WORKDIR /app/playground
|
73 |
-
ENV PNPM_HOME="/
|
74 |
ENV PATH="$PNPM_HOME:$PATH"
|
75 |
RUN pnpm install --no-frozen-lockfile && \
|
76 |
NEXT_PUBLIC_EDIT_GRAPH_MODE=false pnpm build
|
|
|
22 |
ca-certificates \
|
23 |
&& apt-get clean && rm -rf /var/lib/apt/lists/* && rm -rf /tmp/*
|
24 |
|
25 |
+
# Создаем пользователя для запуска приложения с правильными правами
|
26 |
+
RUN useradd -m -s /bin/bash tenuser
|
27 |
+
|
28 |
+
# Установка Go 1.21 с правильными правами доступа
|
29 |
RUN wget https://golang.org/dl/go1.21.0.linux-amd64.tar.gz && \
|
30 |
tar -C /usr/local -xzf go1.21.0.linux-amd64.tar.gz && \
|
31 |
+
rm go1.21.0.linux-amd64.tar.gz && \
|
32 |
+
mkdir -p /home/tenuser/go && \
|
33 |
+
chown -R tenuser:tenuser /home/tenuser/go
|
34 |
+
|
35 |
ENV PATH=$PATH:/usr/local/go/bin
|
36 |
+
ENV GOPATH=/home/tenuser/go
|
37 |
ENV PATH=$PATH:$GOPATH/bin
|
38 |
|
39 |
+
# Установка Node.js 20.x и pnpm для Playground
|
40 |
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
|
41 |
apt-get install -y nodejs && \
|
42 |
npm install -g pnpm
|
43 |
|
|
|
|
|
|
|
44 |
WORKDIR /app
|
45 |
|
46 |
# Клонирование репозитория TEN Agent (основная ветка)
|
|
|
73 |
|
74 |
# Сборка Playground UI с правами пользователя
|
75 |
WORKDIR /app/playground
|
76 |
+
ENV PNPM_HOME="/home/tenuser/.pnpm-store"
|
77 |
ENV PATH="$PNPM_HOME:$PATH"
|
78 |
RUN pnpm install --no-frozen-lockfile && \
|
79 |
NEXT_PUBLIC_EDIT_GRAPH_MODE=false pnpm build
|
app.py
CHANGED
@@ -22,83 +22,90 @@ def main():
|
|
22 |
if not playground_dir.exists():
|
23 |
print(f"ERROR: Playground directory not found at {playground_dir}", file=sys.stderr)
|
24 |
return 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
|
26 |
-
# Запускаем
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
from http.server import HTTPServer, SimpleHTTPRequestHandler
|
|
|
|
|
28 |
|
29 |
-
class
|
30 |
def do_GET(self):
|
31 |
-
|
32 |
-
|
33 |
-
self.
|
34 |
-
|
|
|
|
|
|
|
|
|
|
|
35 |
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
h1 { color: #333; }
|
45 |
-
.info { background: #f8f9fa; border-left: 4px solid #28a745; padding: 15px; margin-bottom: 20px; }
|
46 |
-
.endpoint { background: #e9ecef; padding: 10px; border-radius: 5px; font-family: monospace; }
|
47 |
-
.api { margin-top: 30px; }
|
48 |
-
</style>
|
49 |
-
</head>
|
50 |
-
<body>
|
51 |
-
<h1>TEN Agent запущен успешно!</h1>
|
52 |
-
<div class="info">
|
53 |
-
<p>TEN Agent API сервер работает на порту 8080.</p>
|
54 |
-
<p>В связи с ограничениями Hugging Face Space, полноценный интерфейс Playground UI не может быть запущен напрямую.</p>
|
55 |
-
</div>
|
56 |
-
|
57 |
-
<div class="api">
|
58 |
-
<h2>API эндпоинты:</h2>
|
59 |
-
<ul>
|
60 |
-
<li><span class="endpoint">GET /health</span> - проверка состояния сервера</li>
|
61 |
-
<li><span class="endpoint">GET /list</span> - список запущенных агентов</li>
|
62 |
-
<li><span class="endpoint">GET /graphs</span> - доступные графы</li>
|
63 |
-
</ul>
|
64 |
-
</div>
|
65 |
-
|
66 |
-
<h2>Инструкция по локальному использованию</h2>
|
67 |
-
<p>Для использования полного интерфейса, подключите локальный Playground к этому API:</p>
|
68 |
-
<ol>
|
69 |
-
<li>Клонируйте репозиторий: <code>git clone https://github.com/TEN-framework/TEN-Agent.git</code></li>
|
70 |
-
<li>Перейдите в директорию playground: <code>cd TEN-Agent/playground</code></li>
|
71 |
-
<li>Установите зависимости: <code>pnpm install</code></li>
|
72 |
-
<li>Запустите Playground с подключением к API: <code>AGENT_SERVER_URL=https://nitrox-ten.hf.space pnpm dev</code></li>
|
73 |
-
<li>Откройте в браузере: <code>http://localhost:3000</code></li>
|
74 |
-
</ol>
|
75 |
-
|
76 |
-
<p>См. <a href="https://github.com/TEN-framework/TEN-Agent" target="_blank">документацию TEN Agent</a> для получения дополнительной информации.</p>
|
77 |
-
</body>
|
78 |
-
</html>
|
79 |
-
"""
|
80 |
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
86 |
self.end_headers()
|
87 |
-
|
88 |
-
|
|
|
|
|
|
|
|
|
|
|
89 |
self.send_header('Content-type', 'text/plain; charset=utf-8')
|
90 |
self.end_headers()
|
91 |
-
self.wfile.write(
|
92 |
|
93 |
-
# Запускаем
|
94 |
-
|
95 |
-
|
96 |
-
processes.append(api_server_process)
|
97 |
|
98 |
-
#
|
99 |
-
|
100 |
-
|
101 |
-
|
|
|
102 |
httpd.serve_forever()
|
103 |
|
104 |
except KeyboardInterrupt:
|
|
|
22 |
if not playground_dir.exists():
|
23 |
print(f"ERROR: Playground directory not found at {playground_dir}", file=sys.stderr)
|
24 |
return 1
|
25 |
+
|
26 |
+
# Запускаем API сервер
|
27 |
+
print("Starting TEN-Agent API server on port 8080...")
|
28 |
+
api_server_process = subprocess.Popen([str(api_binary)])
|
29 |
+
processes.append(api_server_process)
|
30 |
+
|
31 |
+
# Даем время API серверу запуститься
|
32 |
+
time.sleep(3)
|
33 |
|
34 |
+
# Запускаем Playground UI
|
35 |
+
print("Starting Playground UI on port 3000...")
|
36 |
+
playground_env = os.environ.copy()
|
37 |
+
playground_env["AGENT_SERVER_URL"] = "http://localhost:8080" # Подключаемся к локальному API серверу
|
38 |
+
playground_process = subprocess.Popen(
|
39 |
+
["pnpm", "start", "--", "-p", "3000"],
|
40 |
+
cwd=str(playground_dir),
|
41 |
+
env=playground_env
|
42 |
+
)
|
43 |
+
processes.append(playground_process)
|
44 |
+
|
45 |
+
# Запускаем прокси для Hugging Face
|
46 |
from http.server import HTTPServer, SimpleHTTPRequestHandler
|
47 |
+
import http.client
|
48 |
+
import socketserver
|
49 |
|
50 |
+
class ProxyHandler(SimpleHTTPRequestHandler):
|
51 |
def do_GET(self):
|
52 |
+
# Направляем запросы к Playground UI
|
53 |
+
if self.path == '/' or self.path.startswith('/about') or self.path.startswith('/_next') or self.path.startswith('/favicon.ico'):
|
54 |
+
self._proxy_request('localhost', 3000, self.path)
|
55 |
+
# Направляем API запросы к API серверу
|
56 |
+
elif self.path.startswith('/health') or self.path.startswith('/list') or self.path.startswith('/graphs'):
|
57 |
+
self._proxy_request('localhost', 8080, self.path)
|
58 |
+
else:
|
59 |
+
# По умолчанию отправляем на Playground
|
60 |
+
self._proxy_request('localhost', 3000, self.path)
|
61 |
|
62 |
+
def do_POST(self):
|
63 |
+
# Все POST запросы направляем в API
|
64 |
+
self._proxy_request('localhost', 8080, self.path)
|
65 |
+
|
66 |
+
def _proxy_request(self, host, port, path):
|
67 |
+
try:
|
68 |
+
# Создаем соединение с целевым сервером
|
69 |
+
conn = http.client.HTTPConnection(host, port)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
70 |
|
71 |
+
# Копируем заголовки запроса
|
72 |
+
headers = {}
|
73 |
+
for header in self.headers:
|
74 |
+
headers[header] = self.headers[header]
|
75 |
+
|
76 |
+
# Получаем данные запроса
|
77 |
+
content_length = int(self.headers.get('Content-Length', 0))
|
78 |
+
body = self.rfile.read(content_length) if content_length > 0 else None
|
79 |
+
|
80 |
+
# Отправляем запрос на целевой сервер
|
81 |
+
conn.request(self.command, path, body=body, headers=headers)
|
82 |
+
resp = conn.getresponse()
|
83 |
+
|
84 |
+
# Отправляем ответ клиенту
|
85 |
+
self.send_response(resp.status)
|
86 |
+
for header in resp.headers:
|
87 |
+
self.send_header(header, resp.headers[header])
|
88 |
self.end_headers()
|
89 |
+
|
90 |
+
# Отправляем тело ответа
|
91 |
+
self.wfile.write(resp.read())
|
92 |
+
conn.close()
|
93 |
+
except Exception as e:
|
94 |
+
print(f"Proxy error: {e}", file=sys.stderr)
|
95 |
+
self.send_response(500)
|
96 |
self.send_header('Content-type', 'text/plain; charset=utf-8')
|
97 |
self.end_headers()
|
98 |
+
self.wfile.write(f"Proxy error: {e}".encode('utf-8'))
|
99 |
|
100 |
+
# Запускаем HTTP прокси-сервер
|
101 |
+
port = 7860 # Hugging Face Space ожидает сервер на этом порту
|
102 |
+
print(f"Starting proxy server on port {port}, forwarding to Playground UI and API...")
|
|
|
103 |
|
104 |
+
# Разрешаем повторное использование адреса и порта
|
105 |
+
class ReuseAddressServer(socketserver.TCPServer):
|
106 |
+
allow_reuse_address = True
|
107 |
+
|
108 |
+
httpd = ReuseAddressServer(('0.0.0.0', port), ProxyHandler)
|
109 |
httpd.serve_forever()
|
110 |
|
111 |
except KeyboardInterrupt:
|