Добавление автономного прокси-сервера и создание файлов во временной директории для обхода проблем с правами доступа в HF Space
Browse files- app.py +28 -0
- create_tmp_agents.sh +58 -0
- start_proxy.sh +8 -0
- temp_property_json.py +65 -0
- temp_proxy_server.py +188 -0
app.py
CHANGED
@@ -648,6 +648,19 @@ def main():
|
|
648 |
logger.error(f"Playground директория не найдена: {PLAYGROUND_DIR}")
|
649 |
return 1
|
650 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
651 |
# Создаем директории и устанавливаем права
|
652 |
ensure_directory_permissions(AGENTS_DIR)
|
653 |
ensure_directory_permissions(BACKUP_DIR)
|
@@ -678,6 +691,21 @@ def main():
|
|
678 |
proxy_script = Path(__file__).parent / "proxy_server.py"
|
679 |
logger.info(f"Проверка наличия прокси-сервера по пути: {proxy_script}")
|
680 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
681 |
# Проверяем наличие файла разными способами для диагностики
|
682 |
if proxy_script.exists():
|
683 |
logger.info(f"Найден файл прокси-сервера: {proxy_script}")
|
|
|
648 |
logger.error(f"Playground директория не найдена: {PLAYGROUND_DIR}")
|
649 |
return 1
|
650 |
|
651 |
+
# Запуск скрипта для создания временных файлов в /tmp
|
652 |
+
tmp_files_script = Path(__file__).parent / "create_tmp_agents.sh"
|
653 |
+
if tmp_files_script.exists():
|
654 |
+
logger.info(f"Запуск скрипта создания временных файлов: {tmp_files_script}")
|
655 |
+
try:
|
656 |
+
os.chmod(tmp_files_script, 0o755) # Даем права на выполнение
|
657 |
+
subprocess.run(["bash", str(tmp_files_script)])
|
658 |
+
logger.info("Скрипт создания временных файлов успешно выполнен")
|
659 |
+
except Exception as e:
|
660 |
+
logger.error(f"Ошибка при запуске скрипта создания временных файлов: {e}")
|
661 |
+
else:
|
662 |
+
logger.warning(f"Скрипт {tmp_files_script} не найден")
|
663 |
+
|
664 |
# Создаем директории и устанавливаем права
|
665 |
ensure_directory_permissions(AGENTS_DIR)
|
666 |
ensure_directory_permissions(BACKUP_DIR)
|
|
|
691 |
proxy_script = Path(__file__).parent / "proxy_server.py"
|
692 |
logger.info(f"Проверка наличия прокси-сервера по пути: {proxy_script}")
|
693 |
|
694 |
+
# Запуск автономного прокси-сервера через скрипт
|
695 |
+
autonomous_proxy_script = Path(__file__).parent / "start_proxy.sh"
|
696 |
+
try:
|
697 |
+
if autonomous_proxy_script.exists():
|
698 |
+
logger.info(f"Запуск автономного прокси-сервера через: {autonomous_proxy_script}")
|
699 |
+
os.chmod(autonomous_proxy_script, 0o755) # Даем права на выполнение
|
700 |
+
proxy_process = subprocess.Popen(["bash", str(autonomous_proxy_script)])
|
701 |
+
processes.append(proxy_process)
|
702 |
+
logger.info("Автономный прокси-сервер запущен")
|
703 |
+
else:
|
704 |
+
logger.warning(f"Скрипт {autonomous_proxy_script} не найден")
|
705 |
+
except Exception as e:
|
706 |
+
logger.error(f"Ошибка при запуске автономного прокси-сервера: {e}")
|
707 |
+
|
708 |
+
# Запускаем встроенный прокси-сервер как резервный вариант
|
709 |
# Проверяем наличие файла разными способами для диагностики
|
710 |
if proxy_script.exists():
|
711 |
logger.info(f"Найден файл прокси-сервера: {proxy_script}")
|
create_tmp_agents.sh
ADDED
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/bin/bash
|
2 |
+
|
3 |
+
TMP_DIR="/tmp/ten-agent"
|
4 |
+
mkdir -p $TMP_DIR
|
5 |
+
|
6 |
+
echo "Creating temporary agent files in $TMP_DIR..."
|
7 |
+
|
8 |
+
# Create voice_agent.json
|
9 |
+
cat > $TMP_DIR/voice_agent.json << 'EOL'
|
10 |
+
{
|
11 |
+
"nodes": [],
|
12 |
+
"edges": [],
|
13 |
+
"groups": [],
|
14 |
+
"templates": [],
|
15 |
+
"root": null
|
16 |
+
}
|
17 |
+
EOL
|
18 |
+
|
19 |
+
# Create chat_agent.json
|
20 |
+
cat > $TMP_DIR/chat_agent.json << 'EOL'
|
21 |
+
{
|
22 |
+
"nodes": [],
|
23 |
+
"edges": [],
|
24 |
+
"groups": [],
|
25 |
+
"templates": [],
|
26 |
+
"root": null
|
27 |
+
}
|
28 |
+
EOL
|
29 |
+
|
30 |
+
# Create manifest.json
|
31 |
+
cat > $TMP_DIR/manifest.json << 'EOL'
|
32 |
+
{
|
33 |
+
"name": "default",
|
34 |
+
"agents": [
|
35 |
+
{
|
36 |
+
"name": "voice_agent",
|
37 |
+
"description": "A simple voice agent"
|
38 |
+
},
|
39 |
+
{
|
40 |
+
"name": "chat_agent",
|
41 |
+
"description": "A text chat agent"
|
42 |
+
}
|
43 |
+
]
|
44 |
+
}
|
45 |
+
EOL
|
46 |
+
|
47 |
+
# Run the Python script to create property.json
|
48 |
+
python3 temp_property_json.py
|
49 |
+
|
50 |
+
echo "All temporary agent files created successfully."
|
51 |
+
echo "Files created:"
|
52 |
+
ls -la $TMP_DIR
|
53 |
+
|
54 |
+
# Create symbolic links to make API find these files
|
55 |
+
ln -sf $TMP_DIR/property.json /app/agents/property.json.tmp || echo "Failed to create symlink for property.json"
|
56 |
+
ln -sf $TMP_DIR/voice_agent.json /app/agents/voice_agent.json.tmp || echo "Failed to create symlink for voice_agent.json"
|
57 |
+
ln -sf $TMP_DIR/chat_agent.json /app/agents/chat_agent.json.tmp || echo "Failed to create symlink for chat_agent.json"
|
58 |
+
ln -sf $TMP_DIR/manifest.json /app/agents/manifest.json.tmp || echo "Failed to create symlink for manifest.json"
|
start_proxy.sh
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/bin/bash
|
2 |
+
|
3 |
+
echo "Starting autonomous proxy server..."
|
4 |
+
python3 temp_proxy_server.py &
|
5 |
+
echo "Proxy server started in background with PID $!"
|
6 |
+
|
7 |
+
# Сохраняем PID в файл для возможности дальнейшего управления
|
8 |
+
echo $! > /tmp/proxy_server.pid
|
temp_property_json.py
ADDED
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
import os
|
3 |
+
import json
|
4 |
+
import logging
|
5 |
+
from pathlib import Path
|
6 |
+
|
7 |
+
# Настройка логирования
|
8 |
+
logging.basicConfig(level=logging.INFO,
|
9 |
+
format='%(asctime)s [%(levelname)s] %(message)s',
|
10 |
+
datefmt='%Y-%m-%d %H:%M:%S')
|
11 |
+
|
12 |
+
logger = logging.getLogger('temp-property')
|
13 |
+
|
14 |
+
# Создаем временную директорию для файлов
|
15 |
+
TMP_DIR = Path("/tmp/ten-agent")
|
16 |
+
TMP_DIR.mkdir(exist_ok=True, parents=True)
|
17 |
+
|
18 |
+
# Путь к временному property.json
|
19 |
+
TEMP_PROPERTY_JSON = TMP_DIR / "property.json"
|
20 |
+
|
21 |
+
# Создаем property.json с правильной структурой
|
22 |
+
property_data = {
|
23 |
+
"_ten": {},
|
24 |
+
"name": "TEN Agent Example",
|
25 |
+
"version": "0.0.1",
|
26 |
+
"extensions": ["openai_chatgpt"],
|
27 |
+
"description": "A basic voice agent with OpenAI",
|
28 |
+
"predefined_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 |
+
"graphs": [
|
41 |
+
{
|
42 |
+
"name": "Voice Agent",
|
43 |
+
"description": "Basic voice agent with OpenAI",
|
44 |
+
"file": "voice_agent.json"
|
45 |
+
},
|
46 |
+
{
|
47 |
+
"name": "Chat Agent",
|
48 |
+
"description": "Simple chat agent",
|
49 |
+
"file": "chat_agent.json"
|
50 |
+
}
|
51 |
+
]
|
52 |
+
}
|
53 |
+
|
54 |
+
# Записываем JSON
|
55 |
+
with open(TEMP_PROPERTY_JSON, 'w') as f:
|
56 |
+
json.dump(property_data, f, indent=2)
|
57 |
+
|
58 |
+
logger.info(f"Создан временный property.json: {TEMP_PROPERTY_JSON}")
|
59 |
+
|
60 |
+
# Устанавливаем переменную окружения для API
|
61 |
+
# Записываем путь в файл для последующего использования
|
62 |
+
with open("/tmp/ten-agent/property_path.txt", 'w') as f:
|
63 |
+
f.write(str(TEMP_PROPERTY_JSON))
|
64 |
+
|
65 |
+
logger.info("Путь к property.json сохранен в /tmp/ten-agent/property_path.txt")
|
temp_proxy_server.py
ADDED
@@ -0,0 +1,188 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
import http.server
|
3 |
+
import socketserver
|
4 |
+
import json
|
5 |
+
import logging
|
6 |
+
import sys
|
7 |
+
import os
|
8 |
+
import urllib.request
|
9 |
+
|
10 |
+
# Настройка логирования
|
11 |
+
logging.basicConfig(level=logging.INFO,
|
12 |
+
format='%(asctime)s [%(levelname)s] %(message)s',
|
13 |
+
datefmt='%Y-%m-%d %H:%M:%S')
|
14 |
+
|
15 |
+
logger = logging.getLogger('ten-proxy')
|
16 |
+
|
17 |
+
# Порт для прокси-сервера (по умолчанию)
|
18 |
+
PROXY_PORT = int(os.environ.get('PROXY_PORT', '9090'))
|
19 |
+
API_PORT = 8080
|
20 |
+
|
21 |
+
# Предопределенные данные для графов
|
22 |
+
GRAPHS_DATA = [
|
23 |
+
{
|
24 |
+
"name": "Voice Agent",
|
25 |
+
"description": "Voice Agent with OpenAI",
|
26 |
+
"file": "voice_agent.json",
|
27 |
+
"id": "voice_agent",
|
28 |
+
"package": "default"
|
29 |
+
},
|
30 |
+
{
|
31 |
+
"name": "Chat Agent",
|
32 |
+
"description": "Chat Agent",
|
33 |
+
"file": "chat_agent.json",
|
34 |
+
"id": "chat_agent",
|
35 |
+
"package": "default"
|
36 |
+
}
|
37 |
+
]
|
38 |
+
|
39 |
+
# Данные для API дизайнера
|
40 |
+
DESIGNER_DATA = {
|
41 |
+
"success": True,
|
42 |
+
"packages": [
|
43 |
+
{
|
44 |
+
"name": "default",
|
45 |
+
"description": "Default package",
|
46 |
+
"graphs": [
|
47 |
+
{
|
48 |
+
"name": "Voice Agent",
|
49 |
+
"description": "Voice Agent with OpenAI",
|
50 |
+
"file": "voice_agent.json",
|
51 |
+
"id": "voice_agent",
|
52 |
+
"package": "default"
|
53 |
+
},
|
54 |
+
{
|
55 |
+
"name": "Chat Agent",
|
56 |
+
"description": "Chat Agent",
|
57 |
+
"file": "chat_agent.json",
|
58 |
+
"id": "chat_agent",
|
59 |
+
"package": "default"
|
60 |
+
}
|
61 |
+
]
|
62 |
+
}
|
63 |
+
]
|
64 |
+
}
|
65 |
+
|
66 |
+
class SimpleProxyHandler(http.server.BaseHTTPRequestHandler):
|
67 |
+
def do_GET(self):
|
68 |
+
logger.info(f"PROXY: GET запрос: {self.path}")
|
69 |
+
|
70 |
+
# Для запросов к /graphs ВСЕГДА возвращаем заранее подготовленный ответ
|
71 |
+
if self.path == "/graphs":
|
72 |
+
logger.info("PROXY: Возвращаем заготовленные данные о графах")
|
73 |
+
self._send_response(200, json.dumps(GRAPHS_DATA))
|
74 |
+
return
|
75 |
+
|
76 |
+
# Для запросов к Designer API ВСЕГДА возвращаем заранее подготовленный ответ
|
77 |
+
if self.path.startswith("/api/designer/") or self.path.startswith("/api/dev/"):
|
78 |
+
logger.info(f"PROXY: Обработка запроса к Designer API: {self.path}")
|
79 |
+
self._send_response(200, json.dumps(DESIGNER_DATA))
|
80 |
+
return
|
81 |
+
|
82 |
+
# Для других запросов пробуем проксировать на API сервер
|
83 |
+
try:
|
84 |
+
self._proxy_to_api("GET")
|
85 |
+
except Exception as e:
|
86 |
+
logger.error(f"PROXY: Ошибка при проксировании GET запроса: {e}")
|
87 |
+
self._send_response(200, json.dumps({"success": True}))
|
88 |
+
|
89 |
+
def do_POST(self):
|
90 |
+
logger.info(f"PROXY: POST запрос: {self.path}")
|
91 |
+
|
92 |
+
# Для запросов к Designer API ВСЕГДА возвращаем заранее подготовленный ответ
|
93 |
+
if self.path.startswith("/api/designer/") or self.path.startswith("/api/dev/"):
|
94 |
+
logger.info(f"PROXY: Обработка POST запроса к Designer API: {self.path}")
|
95 |
+
self._send_response(200, json.dumps({"success": True}))
|
96 |
+
return
|
97 |
+
|
98 |
+
# Для других запросов пробуем проксировать на API сервер
|
99 |
+
try:
|
100 |
+
self._proxy_to_api("POST")
|
101 |
+
except Exception as e:
|
102 |
+
logger.error(f"PROXY: Ошибка при проксировании POST запроса: {e}")
|
103 |
+
self._send_response(200, json.dumps({"success": True}))
|
104 |
+
|
105 |
+
def do_OPTIONS(self):
|
106 |
+
logger.info(f"PROXY: OPTIONS запрос: {self.path}")
|
107 |
+
self.send_response(200)
|
108 |
+
self.send_header('Access-Control-Allow-Origin', '*')
|
109 |
+
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
|
110 |
+
self.send_header('Access-Control-Allow-Headers', 'Content-Type')
|
111 |
+
self.end_headers()
|
112 |
+
|
113 |
+
def _proxy_to_api(self, method):
|
114 |
+
"""Проксирует запрос к API серверу"""
|
115 |
+
url = f"http://localhost:{API_PORT}{self.path}"
|
116 |
+
logger.info(f"PROXY: Проксирование запроса к API: {url}")
|
117 |
+
|
118 |
+
req = urllib.request.Request(url, method=method)
|
119 |
+
|
120 |
+
# Копирование заголовков
|
121 |
+
for header, value in self.headers.items():
|
122 |
+
if header.lower() not in ["host", "content-length"]:
|
123 |
+
req.add_header(header, value)
|
124 |
+
|
125 |
+
# Для POST-запросов копируем тело
|
126 |
+
if method == "POST":
|
127 |
+
content_length = int(self.headers.get('Content-Length', 0))
|
128 |
+
body = self.rfile.read(content_length)
|
129 |
+
req.data = body
|
130 |
+
|
131 |
+
# Выполняем запрос к API серверу
|
132 |
+
with urllib.request.urlopen(req) as response:
|
133 |
+
# Отправляем ответ клиенту
|
134 |
+
self.send_response(response.status)
|
135 |
+
|
136 |
+
# Копируем заголовки ответа
|
137 |
+
for header, value in response.getheaders():
|
138 |
+
if header.lower() != "transfer-encoding":
|
139 |
+
self.send_header(header, value)
|
140 |
+
|
141 |
+
# Добавляем CORS заголовки
|
142 |
+
self.send_header('Access-Control-Allow-Origin', '*')
|
143 |
+
self.end_headers()
|
144 |
+
|
145 |
+
# Копируем тело ответа
|
146 |
+
self.wfile.write(response.read())
|
147 |
+
|
148 |
+
def _send_response(self, status_code, data):
|
149 |
+
"""Отправляет ответ с указанным статусом и данными"""
|
150 |
+
self.send_response(status_code)
|
151 |
+
self.send_header('Content-Type', 'application/json')
|
152 |
+
self.send_header('Access-Control-Allow-Origin', '*')
|
153 |
+
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
|
154 |
+
self.send_header('Access-Control-Allow-Headers', 'Content-Type')
|
155 |
+
self.end_headers()
|
156 |
+
|
157 |
+
if isinstance(data, str):
|
158 |
+
self.wfile.write(data.encode('utf-8'))
|
159 |
+
else:
|
160 |
+
self.wfile.write(data)
|
161 |
+
|
162 |
+
def log_message(self, format, *args):
|
163 |
+
"""Перенаправляем логи сервера в наш логгер"""
|
164 |
+
logger.debug(f"PROXY: {self.address_string()} - {format % args}")
|
165 |
+
|
166 |
+
# Запускаем сервер
|
167 |
+
def main():
|
168 |
+
port = PROXY_PORT
|
169 |
+
|
170 |
+
logger.info(f"Запуск автономного прокси-сервера на порту {port}")
|
171 |
+
|
172 |
+
# Пробуем разные порты, если основной занят
|
173 |
+
for attempt in range(3):
|
174 |
+
try:
|
175 |
+
httpd = socketserver.TCPServer(("", port), SimpleProxyHandler)
|
176 |
+
logger.info(f"Автономный прокси-сервер успешно запущен на порту {port}")
|
177 |
+
httpd.serve_forever()
|
178 |
+
break
|
179 |
+
except OSError as e:
|
180 |
+
if e.errno == 98: # Address already in use
|
181 |
+
logger.warning(f"Порт {port} уже занят, пробуем порт {port+1}")
|
182 |
+
port += 1
|
183 |
+
else:
|
184 |
+
logger.error(f"Ошибка при запуске сервера: {e}")
|
185 |
+
raise
|
186 |
+
|
187 |
+
if __name__ == "__main__":
|
188 |
+
main()
|