Fix permissions issues on HuggingFace Space: Add fallback UI options
Browse files- Dockerfile +4 -8
- app.py +298 -35
- simple_next_app.js +34 -0
- start.sh +8 -2
Dockerfile
CHANGED
@@ -18,8 +18,8 @@ COPY proxy.py /app/
|
|
18 |
COPY .space/hf-space.sh /app/
|
19 |
|
20 |
# Создаем директории
|
21 |
-
RUN mkdir -p /tmp/ten_user/agents /tmp/ten_user/logs /app/backup
|
22 |
-
RUN chmod -R 777 /tmp
|
23 |
|
24 |
# Создаем и активируем виртуальную среду Python
|
25 |
RUN python3 -m venv /app/venv
|
@@ -38,12 +38,8 @@ RUN mkdir -p /app/playground
|
|
38 |
RUN cp -r /tmp/ten-agent/playground/* /app/playground/
|
39 |
RUN rm -rf /tmp/ten-agent
|
40 |
|
41 |
-
# Устанавливаем pnpm
|
42 |
-
RUN npm install -g pnpm@8
|
43 |
-
|
44 |
-
# Устанавливаем dependencies для playground
|
45 |
-
WORKDIR /app/playground
|
46 |
-
RUN pnpm install
|
47 |
|
48 |
# Возвращаемся в основную директорию
|
49 |
WORKDIR /app
|
|
|
18 |
COPY .space/hf-space.sh /app/
|
19 |
|
20 |
# Создаем директории
|
21 |
+
RUN mkdir -p /tmp/ten_user/agents /tmp/ten_user/logs /app/backup /tmp/ten_playground
|
22 |
+
RUN chmod -R 777 /tmp/ten_playground
|
23 |
|
24 |
# Создаем и активируем виртуальную среду Python
|
25 |
RUN python3 -m venv /app/venv
|
|
|
38 |
RUN cp -r /tmp/ten-agent/playground/* /app/playground/
|
39 |
RUN rm -rf /tmp/ten-agent
|
40 |
|
41 |
+
# Устанавливаем pnpm и next глобально для использования с npx
|
42 |
+
RUN npm install -g pnpm@8 next@latest
|
|
|
|
|
|
|
|
|
43 |
|
44 |
# Возвращаемся в основную директорию
|
45 |
WORKDIR /app
|
app.py
CHANGED
@@ -240,6 +240,31 @@ 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)
|
@@ -254,8 +279,8 @@ def start_playground():
|
|
254 |
# Отключаем строгие проверки CORS для работы в iframe
|
255 |
ui_env["NEXT_PUBLIC_DISABLE_CORS"] = "true"
|
256 |
|
257 |
-
# Запускаем UI
|
258 |
-
ui_cmd = "cd
|
259 |
print(f"Running UI command: {ui_cmd}")
|
260 |
ui_process = subprocess.Popen(
|
261 |
ui_cmd,
|
@@ -293,7 +318,7 @@ 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("### Статус: Инициализация...")
|
@@ -302,40 +327,76 @@ def create_interface():
|
|
302 |
col1, col2 = gr.Column(), gr.Column()
|
303 |
|
304 |
with col1:
|
305 |
-
#
|
306 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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="
|
313 |
</div>
|
314 |
"""
|
315 |
|
316 |
iframe_area = gr.HTML()
|
317 |
-
|
|
|
|
|
|
|
|
|
|
|
318 |
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
- Deepgram API Key
|
332 |
-
- Agora App ID и Certificate
|
333 |
-
""")
|
334 |
|
335 |
# Статус серверов и логи
|
336 |
-
with gr.Accordion("
|
337 |
-
api_status = gr.Textbox(label="Статус API сервера", value="
|
338 |
-
ui_status = gr.Textbox(label="Статус UI сервера", value="
|
339 |
|
340 |
# Функция обновления статуса
|
341 |
def update_status():
|
@@ -346,6 +407,9 @@ def create_interface():
|
|
346 |
|
347 |
status_btn = gr.Button("Обновить статус")
|
348 |
status_btn.click(update_status, outputs=[api_status, ui_status, status_md])
|
|
|
|
|
|
|
349 |
|
350 |
return demo
|
351 |
|
@@ -359,6 +423,199 @@ def is_port_in_use(port):
|
|
359 |
except:
|
360 |
return False
|
361 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
362 |
def main():
|
363 |
# Создаем директории и файлы конфигурации
|
364 |
create_directories()
|
@@ -370,12 +627,16 @@ def main():
|
|
370 |
print("Не удалось запустить API сервер")
|
371 |
return
|
372 |
|
373 |
-
#
|
374 |
ui_process = start_playground()
|
|
|
|
|
375 |
if not ui_process:
|
376 |
-
print("Не удалось запустить Playground UI")
|
377 |
-
|
378 |
-
|
|
|
|
|
379 |
|
380 |
# Создаем Gradio интерфейс
|
381 |
demo = create_interface()
|
@@ -388,19 +649,21 @@ def main():
|
|
388 |
while True:
|
389 |
if api_process.poll() is not None:
|
390 |
print("API сервер остановлен")
|
391 |
-
ui_process.
|
|
|
392 |
break
|
393 |
|
394 |
-
if ui_process.poll() is not None:
|
395 |
-
print("UI
|
396 |
-
|
397 |
-
|
398 |
|
399 |
time.sleep(1)
|
400 |
except KeyboardInterrupt:
|
401 |
print("Принудительная остановка...")
|
402 |
api_process.terminate()
|
403 |
-
ui_process
|
|
|
404 |
|
405 |
if __name__ == "__main__":
|
406 |
# Корректная обработка сигналов
|
|
|
240 |
"""Запускает Playground UI через Next.js"""
|
241 |
print("Запуск Playground UI...")
|
242 |
|
243 |
+
# Создаем директорию для Next.js в /tmp, где у нас есть права на запись
|
244 |
+
tmp_playground_dir = Path("/tmp/ten_playground")
|
245 |
+
if not tmp_playground_dir.exists():
|
246 |
+
print(f"Создаем временную директорию для Next.js: {tmp_playground_dir}")
|
247 |
+
tmp_playground_dir.mkdir(exist_ok=True, parents=True)
|
248 |
+
|
249 |
+
# Копируем необходимые файлы из /app/playground в /tmp/ten_playground
|
250 |
+
print("Копируем файлы Next.js приложения во временную директорию...")
|
251 |
+
try:
|
252 |
+
os.system(f"cp -r /app/playground/app /tmp/ten_playground/")
|
253 |
+
os.system(f"cp -r /app/playground/public /tmp/ten_playground/")
|
254 |
+
os.system(f"cp /app/playground/package.json /tmp/ten_playground/")
|
255 |
+
os.system(f"cp /app/playground/next.config.mjs /tmp/ten_playground/")
|
256 |
+
os.system(f"cp /app/playground/tailwind.config.js /tmp/ten_playground/")
|
257 |
+
os.system(f"cp /app/playground/postcss.config.js /tmp/ten_playground/")
|
258 |
+
|
259 |
+
# Проверяем, что файлы скопировались
|
260 |
+
if not (tmp_playground_dir / "app").exists():
|
261 |
+
print("Не удалось скопировать файлы Next.js. Создаем простое приложение...")
|
262 |
+
create_simple_next_app(tmp_playground_dir)
|
263 |
+
except Exception as e:
|
264 |
+
print(f"Ошибка при копировании файлов: {e}")
|
265 |
+
print("Создаем простое приложение Next.js...")
|
266 |
+
create_simple_next_app(tmp_playground_dir)
|
267 |
+
|
268 |
# Устанавливаем переменные окружения
|
269 |
ui_env = os.environ.copy()
|
270 |
ui_env["PORT"] = str(NEXTJS_PORT)
|
|
|
279 |
# Отключаем строгие проверки CORS для работы в iframe
|
280 |
ui_env["NEXT_PUBLIC_DISABLE_CORS"] = "true"
|
281 |
|
282 |
+
# Запускаем UI из временной директории, где у нас есть права на запись
|
283 |
+
ui_cmd = f"cd {tmp_playground_dir} && npx next dev"
|
284 |
print(f"Running UI command: {ui_cmd}")
|
285 |
ui_process = subprocess.Popen(
|
286 |
ui_cmd,
|
|
|
318 |
"""Создает Gradio интерфейс для редиректа"""
|
319 |
with gr.Blocks() as demo:
|
320 |
gr.Markdown("# TEN Agent на Hugging Face Space")
|
321 |
+
gr.Markdown("## Управление и мониторинг")
|
322 |
|
323 |
# Статус серверов
|
324 |
status_md = gr.Markdown("### Статус: Инициализация...")
|
|
|
327 |
col1, col2 = gr.Column(), gr.Column()
|
328 |
|
329 |
with col1:
|
330 |
+
# Информация об API сервере
|
331 |
+
gr.Markdown(f"""
|
332 |
+
### API сервер
|
333 |
+
|
334 |
+
API сервер работает по адресу: http://{INTERNAL_HOST}:{API_PORT}
|
335 |
+
|
336 |
+
Доступные эндпоинты:
|
337 |
+
- `/graphs` - Список доступных графов
|
338 |
+
- `/health` - Статус API сервера
|
339 |
+
""")
|
340 |
+
|
341 |
+
# Кнопка для проверки API
|
342 |
+
check_api_btn = gr.Button("Проверить API сервер")
|
343 |
+
api_result = gr.JSON(label="Результат запроса к API")
|
344 |
+
|
345 |
+
def check_api():
|
346 |
+
try:
|
347 |
+
import requests
|
348 |
+
response = requests.get(f"http://{INTERNAL_HOST}:{API_PORT}/health")
|
349 |
+
return response.json()
|
350 |
+
except Exception as e:
|
351 |
+
return {"status": "error", "message": str(e)}
|
352 |
+
|
353 |
+
check_api_btn.click(check_api, outputs=api_result)
|
354 |
+
|
355 |
+
with col2:
|
356 |
+
# Информация о UI сервере
|
357 |
+
gr.Markdown(f"""
|
358 |
+
### UI сервер
|
359 |
+
|
360 |
+
UI сервер доступен по адресу: {UI_URL}
|
361 |
+
|
362 |
+
<a href="{UI_URL}" target="_blank" style="display: inline-block; padding: 10px 15px; background-color: #4CAF50; color: white; text-decoration: none; border-radius: 4px; margin: 10px 0;">Открыть UI в новой вкладке</a>
|
363 |
+
""")
|
364 |
|
365 |
# Функция для открытия UI в iframe
|
366 |
+
iframe_btn = gr.Button("Показать UI в iframe")
|
367 |
+
|
368 |
def show_iframe():
|
369 |
return f"""
|
370 |
<div style="border: 1px solid #ccc; padding: 10px; border-radius: 5px;">
|
371 |
+
<iframe src="{UI_URL}" width="100%" height="500px" frameborder="0"></iframe>
|
372 |
</div>
|
373 |
"""
|
374 |
|
375 |
iframe_area = gr.HTML()
|
376 |
+
iframe_btn.click(show_iframe, outputs=iframe_area)
|
377 |
+
|
378 |
+
# Ссылки на документацию и важные настройки
|
379 |
+
with gr.Accordion("Инструкции", open=False):
|
380 |
+
gr.Markdown("""
|
381 |
+
### Важные настройки
|
382 |
|
383 |
+
Для полноценной работы необходимо настроить следующие API ключи:
|
384 |
+
|
385 |
+
1. **OpenAI API Key** - для работы с языковыми моделями
|
386 |
+
2. **Deepgram API Key** - для распознавания речи
|
387 |
+
3. **ElevenLabs API Key** - для синтеза речи
|
388 |
+
4. **Agora App ID и Certificate** - для работы с RTC
|
389 |
+
|
390 |
+
### Доступные графы
|
391 |
+
|
392 |
+
1. **Voice Agent** - Голосовой агент с OpenAI и ElevenLabs
|
393 |
+
2. **Chat Agent** - Текстовый чат с OpenAI
|
394 |
+
""")
|
|
|
|
|
|
|
395 |
|
396 |
# Статус серверов и логи
|
397 |
+
with gr.Accordion("Статус системы", open=False):
|
398 |
+
api_status = gr.Textbox(label="Статус API сервера", value="Проверка...", interactive=False)
|
399 |
+
ui_status = gr.Textbox(label="Статус UI сервера", value="Проверка...", interactive=False)
|
400 |
|
401 |
# Функция обновления статуса
|
402 |
def update_status():
|
|
|
407 |
|
408 |
status_btn = gr.Button("Обновить статус")
|
409 |
status_btn.click(update_status, outputs=[api_status, ui_status, status_md])
|
410 |
+
|
411 |
+
# Обновляем статус при загрузке
|
412 |
+
demo.load(update_status, outputs=[api_status, ui_status, status_md])
|
413 |
|
414 |
return demo
|
415 |
|
|
|
423 |
except:
|
424 |
return False
|
425 |
|
426 |
+
def create_simple_next_app(target_dir):
|
427 |
+
"""Создает простое Next.js приложение в указанной директории"""
|
428 |
+
print(f"Создание простого Next.js приложения в {target_dir}")
|
429 |
+
|
430 |
+
# Создаем базовую структуру Next.js приложения
|
431 |
+
app_dir = target_dir / "app"
|
432 |
+
app_dir.mkdir(exist_ok=True, parents=True)
|
433 |
+
|
434 |
+
# Создаем простой package.json
|
435 |
+
package_json = {
|
436 |
+
"name": "ten-agent-simple-ui",
|
437 |
+
"version": "0.1.0",
|
438 |
+
"private": True,
|
439 |
+
"scripts": {
|
440 |
+
"dev": "next dev",
|
441 |
+
"build": "next build",
|
442 |
+
"start": "next start"
|
443 |
+
},
|
444 |
+
"dependencies": {
|
445 |
+
"next": "latest",
|
446 |
+
"react": "latest",
|
447 |
+
"react-dom": "latest"
|
448 |
+
}
|
449 |
+
}
|
450 |
+
|
451 |
+
with open(target_dir / "package.json", "w") as f:
|
452 |
+
json.dump(package_json, f, indent=2)
|
453 |
+
|
454 |
+
# Создаем простой next.config.js
|
455 |
+
next_config = """/** @type {import('next').NextConfig} */
|
456 |
+
const nextConfig = {
|
457 |
+
reactStrictMode: true,
|
458 |
+
}
|
459 |
+
|
460 |
+
module.exports = nextConfig
|
461 |
+
"""
|
462 |
+
|
463 |
+
with open(target_dir / "next.config.js", "w") as f:
|
464 |
+
f.write(next_config)
|
465 |
+
|
466 |
+
# Создаем простую страницу
|
467 |
+
page_content = """export default function Home() {
|
468 |
+
return (
|
469 |
+
<div style={{ padding: '20px', fontFamily: 'Arial, sans-serif' }}>
|
470 |
+
<h1>TEN Agent UI</h1>
|
471 |
+
<p>API server is running at: <a href="http://localhost:8080">http://localhost:8080</a></p>
|
472 |
+
|
473 |
+
<div style={{ marginTop: '20px', padding: '10px', backgroundColor: '#f0f0f0', borderRadius: '5px' }}>
|
474 |
+
<p>API endpoints:</p>
|
475 |
+
<ul>
|
476 |
+
<li><a href="http://localhost:8080/graphs">/graphs</a> - Available graphs</li>
|
477 |
+
<li><a href="http://localhost:8080/health">/health</a> - API server status</li>
|
478 |
+
</ul>
|
479 |
+
</div>
|
480 |
+
|
481 |
+
<div style={{ marginTop: '20px' }}>
|
482 |
+
<button
|
483 |
+
style={{
|
484 |
+
padding: '10px 15px',
|
485 |
+
backgroundColor: '#4CAF50',
|
486 |
+
color: 'white',
|
487 |
+
border: 'none',
|
488 |
+
borderRadius: '5px',
|
489 |
+
cursor: 'pointer'
|
490 |
+
}}
|
491 |
+
onClick={() => window.location.href = 'http://localhost:8080/graphs'}
|
492 |
+
>
|
493 |
+
Go to API
|
494 |
+
</button>
|
495 |
+
</div>
|
496 |
+
</div>
|
497 |
+
);
|
498 |
+
}
|
499 |
+
"""
|
500 |
+
|
501 |
+
with open(app_dir / "page.js", "w") as f:
|
502 |
+
f.write(page_content)
|
503 |
+
|
504 |
+
# Создаем простой layout.js
|
505 |
+
layout_content = """export const metadata = {
|
506 |
+
title: 'TEN Agent',
|
507 |
+
description: 'Simple UI for TEN Agent',
|
508 |
+
}
|
509 |
+
|
510 |
+
export default function RootLayout({ children }) {
|
511 |
+
return (
|
512 |
+
<html lang="en">
|
513 |
+
<body>{children}</body>
|
514 |
+
</html>
|
515 |
+
)
|
516 |
+
}
|
517 |
+
"""
|
518 |
+
|
519 |
+
with open(app_dir / "layout.js", "w") as f:
|
520 |
+
f.write(layout_content)
|
521 |
+
|
522 |
+
print(f"Простое Next.js приложение создано в {target_dir}")
|
523 |
+
|
524 |
+
def start_simple_ui():
|
525 |
+
"""Запускает простой HTTP сервер для UI"""
|
526 |
+
print("Запуск простого HTTP сервера...")
|
527 |
+
|
528 |
+
# Создаем директорию для UI
|
529 |
+
simple_ui_dir = Path("/tmp/ten_ui")
|
530 |
+
simple_ui_dir.mkdir(exist_ok=True, parents=True)
|
531 |
+
|
532 |
+
# Создаем простую HTML страницу
|
533 |
+
html_content = """<!DOCTYPE html>
|
534 |
+
<html lang="en">
|
535 |
+
<head>
|
536 |
+
<meta charset="UTF-8">
|
537 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
538 |
+
<title>TEN Agent Simple UI</title>
|
539 |
+
<style>
|
540 |
+
body {
|
541 |
+
font-family: Arial, sans-serif;
|
542 |
+
max-width: 800px;
|
543 |
+
margin: 0 auto;
|
544 |
+
padding: 20px;
|
545 |
+
}
|
546 |
+
.card {
|
547 |
+
background-color: #f5f5f5;
|
548 |
+
border-radius: 8px;
|
549 |
+
padding: 16px;
|
550 |
+
margin-bottom: 16px;
|
551 |
+
}
|
552 |
+
button {
|
553 |
+
background-color: #4CAF50;
|
554 |
+
border: none;
|
555 |
+
color: white;
|
556 |
+
padding: 10px 15px;
|
557 |
+
text-align: center;
|
558 |
+
text-decoration: none;
|
559 |
+
display: inline-block;
|
560 |
+
font-size: 16px;
|
561 |
+
margin: 4px 2px;
|
562 |
+
cursor: pointer;
|
563 |
+
border-radius: 4px;
|
564 |
+
}
|
565 |
+
a {
|
566 |
+
color: #0066cc;
|
567 |
+
}
|
568 |
+
</style>
|
569 |
+
</head>
|
570 |
+
<body>
|
571 |
+
<h1>TEN Agent UI</h1>
|
572 |
+
<div class="card">
|
573 |
+
<h2>API Server</h2>
|
574 |
+
<p>API сервер работает по адресу: <a href="http://localhost:8080" target="_blank">http://localhost:8080</a></p>
|
575 |
+
<p>Доступные эндпоинты:</p>
|
576 |
+
<ul>
|
577 |
+
<li><a href="http://localhost:8080/graphs" target="_blank">/graphs</a> - Список доступных графов</li>
|
578 |
+
<li><a href="http://localhost:8080/health" target="_blank">/health</a> - Статус API сервера</li>
|
579 |
+
</ul>
|
580 |
+
</div>
|
581 |
+
|
582 |
+
<div class="card">
|
583 |
+
<h2>Запуск сессии</h2>
|
584 |
+
<p>Для запуска сессии можно использовать API вручную:</p>
|
585 |
+
<pre>curl -X POST http://localhost:8080/start -H "Content-Type: application/json" -d '{"graph_file":"voice_agent.json"}'</pre>
|
586 |
+
</div>
|
587 |
+
</body>
|
588 |
+
</html>
|
589 |
+
"""
|
590 |
+
|
591 |
+
with open(simple_ui_dir / "index.html", "w") as f:
|
592 |
+
f.write(html_content)
|
593 |
+
|
594 |
+
# Запускаем простой HTTP сервер на порту 3000
|
595 |
+
server_cmd = f"cd {simple_ui_dir} && python -m http.server {NEXTJS_PORT}"
|
596 |
+
print(f"Running simple HTTP server: {server_cmd}")
|
597 |
+
|
598 |
+
server_process = subprocess.Popen(
|
599 |
+
server_cmd,
|
600 |
+
shell=True,
|
601 |
+
stdout=subprocess.PIPE,
|
602 |
+
stderr=subprocess.PIPE
|
603 |
+
)
|
604 |
+
|
605 |
+
# Ждем запуска сервера
|
606 |
+
time.sleep(2)
|
607 |
+
|
608 |
+
# Проверяем, что процесс не упал
|
609 |
+
if server_process.poll() is not None:
|
610 |
+
stdout, stderr = server_process.communicate()
|
611 |
+
print(f"Простой HTTP сервер не запустился!")
|
612 |
+
print(f"STDOUT: {stdout.decode()}")
|
613 |
+
print(f"STDERR: {stderr.decode()}")
|
614 |
+
return None
|
615 |
+
|
616 |
+
print(f"Простой HTTP сервер запущен и слушает на порту {NEXTJS_PORT}")
|
617 |
+
return server_process
|
618 |
+
|
619 |
def main():
|
620 |
# Создаем директории и файлы конфигурации
|
621 |
create_directories()
|
|
|
627 |
print("Не удалось запустить API сервер")
|
628 |
return
|
629 |
|
630 |
+
# Пробуем запустить Playground UI через Next.js
|
631 |
ui_process = start_playground()
|
632 |
+
|
633 |
+
# Если запуск через Next.js не удался, пробуем запустить простой HTTP сервер
|
634 |
if not ui_process:
|
635 |
+
print("Не удалось запустить Playground UI через Next.js, пробуем простой HTTP сервер...")
|
636 |
+
ui_process = start_simple_ui()
|
637 |
+
|
638 |
+
if not ui_process:
|
639 |
+
print("Не удалось запустить ни один UI сервер. Продолжаем только с API сервером.")
|
640 |
|
641 |
# Создаем Gradio интерфейс
|
642 |
demo = create_interface()
|
|
|
649 |
while True:
|
650 |
if api_process.poll() is not None:
|
651 |
print("API сервер остановлен")
|
652 |
+
if ui_process and ui_process.poll() is None:
|
653 |
+
ui_process.terminate()
|
654 |
break
|
655 |
|
656 |
+
if ui_process and ui_process.poll() is not None:
|
657 |
+
print("UI сервер остановлен, пробуем перезапустить...")
|
658 |
+
# Пробуем запустить простой HTTP сервер, если UI процесс упал
|
659 |
+
ui_process = start_simple_ui()
|
660 |
|
661 |
time.sleep(1)
|
662 |
except KeyboardInterrupt:
|
663 |
print("Принудительная остановка...")
|
664 |
api_process.terminate()
|
665 |
+
if ui_process:
|
666 |
+
ui_process.terminate()
|
667 |
|
668 |
if __name__ == "__main__":
|
669 |
# Корректная обработка сигналов
|
simple_next_app.js
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// File: /tmp/ten_playground/app/page.js
|
2 |
+
// Simple Next.js app that redirects to API server
|
3 |
+
export default function Home() {
|
4 |
+
return (
|
5 |
+
<div style={{ padding: '20px', fontFamily: 'Arial, sans-serif' }}>
|
6 |
+
<h1>TEN Agent UI Loader</h1>
|
7 |
+
<p>API server is running at: <a href="http://localhost:8080">http://localhost:8080</a></p>
|
8 |
+
|
9 |
+
<div style={{ marginTop: '20px', padding: '10px', backgroundColor: '#f0f0f0', borderRadius: '5px' }}>
|
10 |
+
<p>Используйте API напрямую:</p>
|
11 |
+
<ul>
|
12 |
+
<li><a href="http://localhost:8080/graphs">/graphs</a> - Список доступных графов</li>
|
13 |
+
<li><a href="http://localhost:8080/health">/health</a> - Статус API сервера</li>
|
14 |
+
</ul>
|
15 |
+
</div>
|
16 |
+
|
17 |
+
<div style={{ marginTop: '20px' }}>
|
18 |
+
<button
|
19 |
+
style={{
|
20 |
+
padding: '10px 15px',
|
21 |
+
backgroundColor: '#4CAF50',
|
22 |
+
color: 'white',
|
23 |
+
border: 'none',
|
24 |
+
borderRadius: '5px',
|
25 |
+
cursor: 'pointer'
|
26 |
+
}}
|
27 |
+
onClick={() => window.location.href = 'http://localhost:8080/graphs'}
|
28 |
+
>
|
29 |
+
Перейти к API
|
30 |
+
</button>
|
31 |
+
</div>
|
32 |
+
</div>
|
33 |
+
);
|
34 |
+
}
|
start.sh
CHANGED
@@ -14,11 +14,17 @@ else
|
|
14 |
PYTHON_CMD="python3"
|
15 |
fi
|
16 |
|
17 |
-
# Создаем необходимые директории
|
18 |
echo "Creating temporary directories in /tmp..."
|
19 |
mkdir -p /tmp/ten_user/agents
|
20 |
mkdir -p /tmp/ten_user/logs
|
21 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
22 |
|
23 |
# Проверяем наличие файлов
|
24 |
echo "Checking necessary files..."
|
|
|
14 |
PYTHON_CMD="python3"
|
15 |
fi
|
16 |
|
17 |
+
# Создаем необходимые директории без изменения прав
|
18 |
echo "Creating temporary directories in /tmp..."
|
19 |
mkdir -p /tmp/ten_user/agents
|
20 |
mkdir -p /tmp/ten_user/logs
|
21 |
+
mkdir -p /tmp/ten_playground
|
22 |
+
|
23 |
+
# Проверяем, что директории созданы
|
24 |
+
echo "Checking directories..."
|
25 |
+
if [ -d "/tmp/ten_user" ]; then
|
26 |
+
echo "✅ /tmp/ten_user exists"
|
27 |
+
fi
|
28 |
|
29 |
# Проверяем наличие файлов
|
30 |
echo "Checking necessary files..."
|