sanbo
commited on
Commit
·
31b509b
1
Parent(s):
98b8872
update sth. at 2025-01-11 21:14:37
Browse files- Dockerfile +11 -58
- degpt.py +173 -133
- more_core.py +30 -19
- requirements.txt +3 -10
Dockerfile
CHANGED
@@ -4,45 +4,19 @@ FROM python:3.11-slim-bullseye AS builder
|
|
4 |
ENV PYTHONDONTWRITEBYTECODE=1 \
|
5 |
PYTHONUNBUFFERED=1 \
|
6 |
PIP_NO_CACHE_DIR=1 \
|
7 |
-
DEBIAN_FRONTEND=noninteractive
|
8 |
-
PLAYWRIGHT_BROWSERS_PATH=/root/.cache/ms-playwright
|
9 |
|
10 |
WORKDIR /build
|
11 |
|
12 |
-
#
|
13 |
RUN apt-get update && apt-get install -y --no-install-recommends \
|
14 |
-
build-essential \
|
15 |
curl \
|
16 |
ca-certificates \
|
17 |
-
|
18 |
-
|
19 |
-
libnss3 \
|
20 |
-
libnspr4 \
|
21 |
-
libcups2 \
|
22 |
-
libglib2.0-0 \
|
23 |
-
libc6 \
|
24 |
-
libatk1.0-0 \
|
25 |
-
libatk-bridge2.0-0 \
|
26 |
-
libdrm2 \
|
27 |
-
libxkbcommon0 \
|
28 |
-
libxcomposite1 \
|
29 |
-
libxdamage1 \
|
30 |
-
libxfixes3 \
|
31 |
-
libxrandr2 \
|
32 |
-
libgbm1 \
|
33 |
-
libasound2 \
|
34 |
-
libpango-1.0-0 \
|
35 |
-
libpangocairo-1.0-0 \
|
36 |
-
libx11-6 \
|
37 |
-
libxcb1 \
|
38 |
-
&& rm -rf /var/lib/apt/lists/*
|
39 |
|
40 |
-
# 安装Python包和Playwright
|
41 |
COPY requirements.txt .
|
42 |
-
RUN pip install --
|
43 |
-
pip install --no-cache-dir -r requirements.txt && \
|
44 |
-
playwright install chromium --with-deps && \
|
45 |
-
ls -la /root/.cache/ms-playwright
|
46 |
|
47 |
# Runtime stage
|
48 |
FROM python:3.11-slim-bullseye
|
@@ -50,41 +24,20 @@ FROM python:3.11-slim-bullseye
|
|
50 |
ENV PYTHONDONTWRITEBYTECODE=1 \
|
51 |
PYTHONUNBUFFERED=1 \
|
52 |
PORT=7860 \
|
53 |
-
DEBUG=false
|
54 |
-
PLAYWRIGHT_BROWSERS_PATH=/root/.cache/ms-playwright
|
55 |
|
56 |
WORKDIR /app
|
57 |
|
58 |
-
#
|
|
|
|
|
|
|
|
|
59 |
RUN apt-get update && apt-get install -y --no-install-recommends \
|
60 |
-
libnss3 \
|
61 |
-
libnspr4 \
|
62 |
-
libcups2 \
|
63 |
-
libglib2.0-0 \
|
64 |
-
libc6 \
|
65 |
-
libatk1.0-0 \
|
66 |
-
libatk-bridge2.0-0 \
|
67 |
-
libdrm2 \
|
68 |
-
libxkbcommon0 \
|
69 |
-
libxcomposite1 \
|
70 |
-
libxdamage1 \
|
71 |
-
libxfixes3 \
|
72 |
-
libxrandr2 \
|
73 |
-
libgbm1 \
|
74 |
-
libasound2 \
|
75 |
-
libpango-1.0-0 \
|
76 |
-
libpangocairo-1.0-0 \
|
77 |
-
libx11-6 \
|
78 |
-
libxcb1 \
|
79 |
curl \
|
80 |
ca-certificates \
|
81 |
&& rm -rf /var/lib/apt/lists/*
|
82 |
|
83 |
-
# 复制必要文件
|
84 |
-
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
|
85 |
-
COPY --from=builder /root/.cache/ms-playwright /root/.cache/ms-playwright
|
86 |
-
COPY more_core.py degpt.py ./
|
87 |
-
|
88 |
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
89 |
CMD curl -f http://localhost:${PORT}/health || exit 1
|
90 |
|
|
|
4 |
ENV PYTHONDONTWRITEBYTECODE=1 \
|
5 |
PYTHONUNBUFFERED=1 \
|
6 |
PIP_NO_CACHE_DIR=1 \
|
7 |
+
DEBIAN_FRONTEND=noninteractive
|
|
|
8 |
|
9 |
WORKDIR /build
|
10 |
|
11 |
+
# Install minimal dependencies and Python packages
|
12 |
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
|
13 |
curl \
|
14 |
ca-certificates \
|
15 |
+
&& rm -rf /var/lib/apt/lists/* \
|
16 |
+
&& pip install --upgrade pip
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
|
|
|
18 |
COPY requirements.txt .
|
19 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
|
|
|
|
|
|
20 |
|
21 |
# Runtime stage
|
22 |
FROM python:3.11-slim-bullseye
|
|
|
24 |
ENV PYTHONDONTWRITEBYTECODE=1 \
|
25 |
PYTHONUNBUFFERED=1 \
|
26 |
PORT=7860 \
|
27 |
+
DEBUG=false
|
|
|
28 |
|
29 |
WORKDIR /app
|
30 |
|
31 |
+
# Copy Python packages and application files
|
32 |
+
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
|
33 |
+
COPY more_core.py degpt.py ./
|
34 |
+
|
35 |
+
# Install runtime dependencies
|
36 |
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
curl \
|
38 |
ca-certificates \
|
39 |
&& rm -rf /var/lib/apt/lists/*
|
40 |
|
|
|
|
|
|
|
|
|
|
|
41 |
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
42 |
CMD curl -f http://localhost:${PORT}/health || exit 1
|
43 |
|
degpt.py
CHANGED
@@ -2,19 +2,17 @@
|
|
2 |
update time: 2025.01.09
|
3 |
verson: 0.1.125
|
4 |
"""
|
5 |
-
import aiohttp
|
6 |
-
import time
|
7 |
-
import requests
|
8 |
-
import asyncio
|
9 |
-
|
10 |
-
from playwright.async_api import async_playwright
|
11 |
import json
|
12 |
-
import logging
|
13 |
import re
|
14 |
-
|
15 |
from datetime import datetime, timedelta
|
|
|
|
|
|
|
|
|
16 |
|
17 |
-
|
|
|
18 |
# 全局变量
|
19 |
last_request_time = 0 # 上次请求的时间戳
|
20 |
cache_duration = 14400 # 缓存有效期,单位:秒 (4小时)
|
@@ -42,7 +40,8 @@ base_addrs = [
|
|
42 |
'''基础域名'''
|
43 |
base_url = 'https://singapore-chat.degpt.ai/api'
|
44 |
|
45 |
-
|
|
|
46 |
# 全局变量:存储所有模型的统计信息
|
47 |
# 格式:{model_name: {"calls": 调用次数, "fails": 失败次数, "last_fail": 最后失败时间}}
|
48 |
MODEL_STATS: Dict[str, Dict] = {}
|
@@ -65,11 +64,11 @@ def record_call(model_name: str, success: bool = True) -> None:
|
|
65 |
stats["last_fail"] = datetime.now()
|
66 |
|
67 |
|
68 |
-
|
69 |
"""异步获取最优模型"""
|
70 |
try:
|
71 |
if not MODEL_STATS:
|
72 |
-
|
73 |
|
74 |
best_model = None
|
75 |
best_rate = -1.0
|
@@ -86,17 +85,17 @@ async def get_auto_model(cooldown_seconds: int = 300) -> str:
|
|
86 |
best_rate = success_rate
|
87 |
best_model = name
|
88 |
|
89 |
-
default_model = best_model or
|
90 |
if debug:
|
91 |
print(f"选择模型: {default_model}")
|
92 |
return default_model
|
93 |
except Exception as e:
|
94 |
if debug:
|
95 |
print(f"模型选择错误: {e}")
|
96 |
-
return
|
97 |
|
98 |
|
99 |
-
|
100 |
"""检查并更新系统状态
|
101 |
1. 如果模型数据为空,更新模型数据
|
102 |
2. 测试当前base_url是否可用,不可用则切换
|
@@ -108,11 +107,11 @@ async def reload_check():
|
|
108 |
if not cached_models["data"]:
|
109 |
if debug:
|
110 |
print("模型数据为空,开始更新...")
|
111 |
-
|
112 |
|
113 |
# 测试用例 - 平衡效率和功能验证
|
114 |
test_payload = {
|
115 |
-
"model":
|
116 |
"messages": [{
|
117 |
"role": "user",
|
118 |
"content": [{"type": "text", "text": "test"}]
|
@@ -130,10 +129,10 @@ async def reload_check():
|
|
130 |
'Content-Type': 'application/json'
|
131 |
}
|
132 |
|
133 |
-
|
134 |
# 测试当前URL
|
135 |
try:
|
136 |
-
|
137 |
f"{base_url}/v0/chat/completion/proxy",
|
138 |
headers=headers,
|
139 |
json=test_payload,
|
@@ -141,7 +140,7 @@ async def reload_check():
|
|
141 |
) as response:
|
142 |
if response.status == 200:
|
143 |
# 验证响应格式
|
144 |
-
if
|
145 |
if debug:
|
146 |
print(f"当前URL可用: {base_url}")
|
147 |
return
|
@@ -154,13 +153,13 @@ async def reload_check():
|
|
154 |
if url == base_url:
|
155 |
continue
|
156 |
try:
|
157 |
-
|
158 |
f"{url}/v0/chat/completion/proxy",
|
159 |
headers=headers,
|
160 |
json=test_payload,
|
161 |
timeout=5
|
162 |
) as response:
|
163 |
-
if response.status == 200 and
|
164 |
base_url = url
|
165 |
if debug:
|
166 |
print(f"切换到新URL: {base_url}")
|
@@ -177,11 +176,12 @@ async def reload_check():
|
|
177 |
if debug:
|
178 |
print(f"系统检查失败: {e}")
|
179 |
|
180 |
-
|
|
|
181 |
"""Thread-safe model fetching and cache updating"""
|
182 |
global cached_models
|
183 |
try:
|
184 |
-
|
185 |
except Exception as e:
|
186 |
print(f"{e}")
|
187 |
try:
|
@@ -189,17 +189,18 @@ async def _fetch_and_update_models():
|
|
189 |
except Exception as e:
|
190 |
print(f"{e}")
|
191 |
|
192 |
-
|
193 |
-
|
|
|
194 |
global cached_models, last_request_time
|
195 |
current_time = time.time()
|
196 |
if (current_time - last_request_time) > cache_duration:
|
197 |
try:
|
198 |
# Update timestamp before awaiting to prevent concurrent updates
|
199 |
last_request_time = current_time
|
200 |
-
|
201 |
except Exception as e:
|
202 |
-
print(e)
|
203 |
|
204 |
return json.dumps(cached_models)
|
205 |
|
@@ -259,11 +260,12 @@ def get_alive_models():
|
|
259 |
print(f"请求失败,状态码: {response.status_code}")
|
260 |
|
261 |
|
262 |
-
|
263 |
"""解析JS内容中的模型信息"""
|
264 |
try:
|
265 |
pattern = r'models\s*:\s*\[([^\]]+)\]'
|
266 |
match = re.search(pattern, js_content)
|
|
|
267 |
if match:
|
268 |
models_data = match.group(1)
|
269 |
models_data = re.sub(r'(\w+):', r'"\1":', models_data)
|
@@ -272,103 +274,138 @@ async def parse_models_from_js(js_content: str) -> List[Dict]:
|
|
272 |
|
273 |
try:
|
274 |
models = json.loads(models_data)
|
275 |
-
# print(f"成功解析到 {len(models)} 个模型信息")
|
276 |
return models
|
277 |
except json.JSONDecodeError as e:
|
278 |
-
print(f"JSON解析错误: {str(e)}")
|
279 |
return []
|
|
|
280 |
return []
|
|
|
281 |
except Exception as e:
|
282 |
-
print(f"解析模型信息时发生错误: {str(e)}")
|
283 |
return []
|
284 |
|
285 |
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
355 |
global cached_models
|
356 |
# 获取 JavaScript 文件内容
|
357 |
# url = "https://www.degpt.ai/_app/immutable/chunks/index.83d92b06.js"
|
358 |
-
url = "https://www.degpt.ai/_app/immutable/chunks/index.4aecf75a.js"
|
|
|
359 |
response = requests.get(url)
|
360 |
|
361 |
if response.status_code == 200:
|
362 |
js_content = response.text
|
363 |
-
models =
|
|
|
364 |
if models:
|
365 |
-
|
|
|
366 |
existing_ids = {m.get('id') for m in cached_models["data"]}
|
367 |
for model in models:
|
368 |
model_id = model.get('model', '').strip()
|
369 |
-
|
370 |
-
|
371 |
-
|
|
|
372 |
record_call(model_id)
|
373 |
if model_id and model_id not in existing_ids:
|
374 |
model_data = {
|
@@ -383,10 +420,11 @@ async def get_from_js():
|
|
383 |
"tip": model.get('tip', '')
|
384 |
}
|
385 |
cached_models["data"].append(model_data)
|
386 |
-
|
|
|
387 |
|
388 |
|
389 |
-
|
390 |
"""
|
391 |
判断模型是否在模型列表中且非最近失败的模型
|
392 |
|
@@ -405,7 +443,7 @@ async def is_model_available(model_id: str, cooldown_seconds: int = 300) -> bool
|
|
405 |
|
406 |
# 如果MODEL_STATS为空,加载模型数据
|
407 |
if not MODEL_STATS:
|
408 |
-
|
409 |
|
410 |
# 检查模型是否在统计信息中
|
411 |
if model_id not in MODEL_STATS:
|
@@ -421,7 +459,7 @@ async def is_model_available(model_id: str, cooldown_seconds: int = 300) -> bool
|
|
421 |
return True
|
422 |
|
423 |
|
424 |
-
|
425 |
"""
|
426 |
检查提供的model_id是否可用,如果不可用则返回成功率最高的模型
|
427 |
|
@@ -441,17 +479,17 @@ async def get_model_by_autoupdate(model_id: Optional[str] = None, cooldown_secon
|
|
441 |
|
442 |
# 如果MODEL_STATS为空,加载模型数据
|
443 |
if not MODEL_STATS:
|
444 |
-
|
445 |
|
446 |
# 如果提供了model_id且可用,直接返回
|
447 |
-
if model_id and
|
448 |
return model_id
|
449 |
|
450 |
# 否则返回成功率最高的可用模型
|
451 |
-
return
|
452 |
|
453 |
|
454 |
-
|
455 |
"""Check if the data is in the expected ChatGPT format"""
|
456 |
try:
|
457 |
# If the data is a string, try to parse it as JSON
|
@@ -473,12 +511,12 @@ async def is_chatgpt_format(data):
|
|
473 |
return False
|
474 |
|
475 |
|
476 |
-
|
477 |
user_prompt,
|
478 |
user_id: str = None,
|
479 |
session_id: str = None,
|
480 |
system_prompt="You are a helpful assistant.",
|
481 |
-
model=
|
482 |
project="DecentralGPT", stream=False,
|
483 |
temperature=0.3, max_tokens=1024, top_p=0.5,
|
484 |
frequency_penalty=0, presence_penalty=0):
|
@@ -487,20 +525,25 @@ async def chat_completion_message(
|
|
487 |
{"role": "system", "content": system_prompt},
|
488 |
{"role": "user", "content": user_prompt}
|
489 |
]
|
490 |
-
return
|
491 |
top_p, frequency_penalty,
|
492 |
presence_penalty)
|
493 |
|
494 |
|
495 |
-
|
496 |
messages,
|
497 |
-
model=
|
498 |
user_id: str = None,
|
499 |
session_id: str = None,
|
500 |
project="DecentralGPT", stream=False, temperature=0.3, max_tokens=1024, top_p=0.5,
|
501 |
frequency_penalty=0, presence_penalty=0):
|
502 |
-
#
|
503 |
-
|
|
|
|
|
|
|
|
|
|
|
504 |
headers = {
|
505 |
'sec-ch-ua-platform': '"macOS"',
|
506 |
'Referer': 'https://www.degpt.ai/',
|
@@ -510,13 +553,6 @@ async def chat_completion_messages(
|
|
510 |
'Content-Type': 'application/json',
|
511 |
'sec-ch-ua-mobile': '?0'
|
512 |
}
|
513 |
-
# 确保model有效
|
514 |
-
if not model or model == "auto":
|
515 |
-
model = await get_auto_model()
|
516 |
-
else:
|
517 |
-
model = await get_model_by_autoupdate(model)
|
518 |
-
if debug:
|
519 |
-
print(f"校准后的model: {model}")
|
520 |
payload = {
|
521 |
# make sure ok
|
522 |
"model": model,
|
@@ -532,23 +568,27 @@ async def chat_completion_messages(
|
|
532 |
}
|
533 |
# print(json.dumps(headers, indent=4))
|
534 |
# print(json.dumps(payload, indent=4))
|
535 |
-
return
|
536 |
|
537 |
|
538 |
-
|
539 |
"""处理用户请求并保留上下文"""
|
540 |
try:
|
|
|
541 |
response = requests.post(url, headers=headers, json=payload)
|
542 |
response.encoding = 'utf-8'
|
543 |
response.raise_for_status()
|
544 |
return response.json()
|
545 |
except requests.exceptions.RequestException as e:
|
546 |
print(f"请求失败: {e}")
|
547 |
-
await record_call(model, False)
|
548 |
return "请求失败,请检查网络或参数配置。"
|
549 |
except (KeyError, IndexError) as e:
|
550 |
print(f"解析响应时出错: {e}")
|
551 |
-
await record_call(model, False)
|
552 |
return "解析响应内容失败。"
|
553 |
return {}
|
554 |
|
|
|
|
|
|
|
|
|
|
|
|
2 |
update time: 2025.01.09
|
3 |
verson: 0.1.125
|
4 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
import json
|
|
|
6 |
import re
|
7 |
+
import time
|
8 |
from datetime import datetime, timedelta
|
9 |
+
from typing import List, Dict, Optional
|
10 |
+
|
11 |
+
import aiohttp
|
12 |
+
import requests
|
13 |
|
14 |
+
|
15 |
+
debug = True
|
16 |
# 全局变量
|
17 |
last_request_time = 0 # 上次请求的时间戳
|
18 |
cache_duration = 14400 # 缓存有效期,单位:秒 (4小时)
|
|
|
40 |
'''基础域名'''
|
41 |
base_url = 'https://singapore-chat.degpt.ai/api'
|
42 |
|
43 |
+
'''基础模型'''
|
44 |
+
base_model = "Pixtral-124B"
|
45 |
# 全局变量:存储所有模型的统计信息
|
46 |
# 格式:{model_name: {"calls": 调用次数, "fails": 失败次数, "last_fail": 最后失败时间}}
|
47 |
MODEL_STATS: Dict[str, Dict] = {}
|
|
|
64 |
stats["last_fail"] = datetime.now()
|
65 |
|
66 |
|
67 |
+
def get_auto_model(cooldown_seconds: int = 300) -> str:
|
68 |
"""异步获取最优模型"""
|
69 |
try:
|
70 |
if not MODEL_STATS:
|
71 |
+
get_models()
|
72 |
|
73 |
best_model = None
|
74 |
best_rate = -1.0
|
|
|
85 |
best_rate = success_rate
|
86 |
best_model = name
|
87 |
|
88 |
+
default_model = best_model or base_model
|
89 |
if debug:
|
90 |
print(f"选择模型: {default_model}")
|
91 |
return default_model
|
92 |
except Exception as e:
|
93 |
if debug:
|
94 |
print(f"模型选择错误: {e}")
|
95 |
+
return base_model
|
96 |
|
97 |
|
98 |
+
def reload_check():
|
99 |
"""检查并更新系统状态
|
100 |
1. 如果模型数据为空,更新模型数据
|
101 |
2. 测试当前base_url是否可用,不可用则切换
|
|
|
107 |
if not cached_models["data"]:
|
108 |
if debug:
|
109 |
print("模型数据为空,开始更新...")
|
110 |
+
get_models()
|
111 |
|
112 |
# 测试用例 - 平衡效率和功能验证
|
113 |
test_payload = {
|
114 |
+
"model": base_model,
|
115 |
"messages": [{
|
116 |
"role": "user",
|
117 |
"content": [{"type": "text", "text": "test"}]
|
|
|
129 |
'Content-Type': 'application/json'
|
130 |
}
|
131 |
|
132 |
+
with aiohttp.ClientSession() as session:
|
133 |
# 测试当前URL
|
134 |
try:
|
135 |
+
with session.post(
|
136 |
f"{base_url}/v0/chat/completion/proxy",
|
137 |
headers=headers,
|
138 |
json=test_payload,
|
|
|
140 |
) as response:
|
141 |
if response.status == 200:
|
142 |
# 验证响应格式
|
143 |
+
if response.read():
|
144 |
if debug:
|
145 |
print(f"当前URL可用: {base_url}")
|
146 |
return
|
|
|
153 |
if url == base_url:
|
154 |
continue
|
155 |
try:
|
156 |
+
with session.post(
|
157 |
f"{url}/v0/chat/completion/proxy",
|
158 |
headers=headers,
|
159 |
json=test_payload,
|
160 |
timeout=5
|
161 |
) as response:
|
162 |
+
if response.status == 200 and response.read():
|
163 |
base_url = url
|
164 |
if debug:
|
165 |
print(f"切换到新URL: {base_url}")
|
|
|
176 |
if debug:
|
177 |
print(f"系统检查失败: {e}")
|
178 |
|
179 |
+
|
180 |
+
def _fetch_and_update_models():
|
181 |
"""Thread-safe model fetching and cache updating"""
|
182 |
global cached_models
|
183 |
try:
|
184 |
+
get_from_js()
|
185 |
except Exception as e:
|
186 |
print(f"{e}")
|
187 |
try:
|
|
|
189 |
except Exception as e:
|
190 |
print(f"{e}")
|
191 |
|
192 |
+
|
193 |
+
def get_models():
|
194 |
+
"""model data retrieval with thread safety"""
|
195 |
global cached_models, last_request_time
|
196 |
current_time = time.time()
|
197 |
if (current_time - last_request_time) > cache_duration:
|
198 |
try:
|
199 |
# Update timestamp before awaiting to prevent concurrent updates
|
200 |
last_request_time = current_time
|
201 |
+
_fetch_and_update_models()
|
202 |
except Exception as e:
|
203 |
+
print(f"{e}")
|
204 |
|
205 |
return json.dumps(cached_models)
|
206 |
|
|
|
260 |
print(f"请求失败,状态码: {response.status_code}")
|
261 |
|
262 |
|
263 |
+
def parse_models_from_js(js_content: str) -> List[Dict]:
|
264 |
"""解析JS内容中的模型信息"""
|
265 |
try:
|
266 |
pattern = r'models\s*:\s*\[([^\]]+)\]'
|
267 |
match = re.search(pattern, js_content)
|
268 |
+
|
269 |
if match:
|
270 |
models_data = match.group(1)
|
271 |
models_data = re.sub(r'(\w+):', r'"\1":', models_data)
|
|
|
274 |
|
275 |
try:
|
276 |
models = json.loads(models_data)
|
|
|
277 |
return models
|
278 |
except json.JSONDecodeError as e:
|
|
|
279 |
return []
|
280 |
+
|
281 |
return []
|
282 |
+
|
283 |
except Exception as e:
|
|
|
284 |
return []
|
285 |
|
286 |
|
287 |
+
# def get_model_names_from_js(url="https://www.degpt.ai/", timeout: int = 60):
|
288 |
+
# global cached_models
|
289 |
+
# try:
|
290 |
+
# with async_playwright() as p:
|
291 |
+
# browser = p.chromium.launch(
|
292 |
+
# headless=True,
|
293 |
+
# args=['--no-sandbox']
|
294 |
+
# )
|
295 |
+
# context = browser.new_context(
|
296 |
+
# viewport={'width': 1920, 'height': 1080},
|
297 |
+
# user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/119.0.0.0 Safari/537.36'
|
298 |
+
# )
|
299 |
+
# page = context.new_page()
|
300 |
+
#
|
301 |
+
# def handle_response(response):
|
302 |
+
# try:
|
303 |
+
# if response.request.resource_type == "script":
|
304 |
+
# content_type = response.headers.get('content-type', '').lower()
|
305 |
+
# if 'javascript' in content_type:
|
306 |
+
# js_content = response.text()
|
307 |
+
# if 'models' in js_content:
|
308 |
+
# # print(f"找到包含模型信息的JS文件: {response.url}")
|
309 |
+
# models = parse_models_from_js(js_content)
|
310 |
+
# if models:
|
311 |
+
# # print("\n提取的模型列表:")
|
312 |
+
# existing_ids = {m.get('id') for m in cached_models["data"]}
|
313 |
+
# for model in models:
|
314 |
+
# model_id = model.get('model', '').strip()
|
315 |
+
# # print(f"- 名称: {model.get('name', '')}")
|
316 |
+
# # print(f" 模型: {model.get('model', '')}")
|
317 |
+
# # print(f" 描述: {model.get('desc', '')}")
|
318 |
+
# record_call(model_id)
|
319 |
+
# if model_id and model_id not in existing_ids:
|
320 |
+
# model_data = {
|
321 |
+
# "id": model_id,
|
322 |
+
# "object": "model",
|
323 |
+
# "model": model_id,
|
324 |
+
# "created": int(time.time()),
|
325 |
+
# "owned_by": model_id.split("-")[0] if "-" in model_id else "unknown",
|
326 |
+
# "name": model.get('name', ''),
|
327 |
+
# "description": model.get('desc', ''),
|
328 |
+
# "support": model.get('support', 'text'),
|
329 |
+
# "tip": model.get('tip', '')
|
330 |
+
# }
|
331 |
+
# cached_models["data"].append(model_data)
|
332 |
+
# # print(f"添加新模型: {model_id}")
|
333 |
+
# except Exception as e:
|
334 |
+
# print(f"处理响应时发生错误: {str(e)}")
|
335 |
+
# logging.error(f"Response处理异常: {e}", exc_info=True)
|
336 |
+
#
|
337 |
+
# page.on("response", handle_response)
|
338 |
+
#
|
339 |
+
# try:
|
340 |
+
# page.goto(url, timeout=timeout * 1000, wait_until='networkidle')
|
341 |
+
# page.wait_for_timeout(5000)
|
342 |
+
# except Exception as e:
|
343 |
+
# print(f"页面加载错误: {str(e)}")
|
344 |
+
# logging.error(f"页面加载异常: {e}", exc_info=True)
|
345 |
+
# finally:
|
346 |
+
# browser.close()
|
347 |
+
# except Exception as e:
|
348 |
+
# print(f"提取过程发生错误: {str(e)}")
|
349 |
+
# get_from_js()
|
350 |
+
# raise
|
351 |
+
|
352 |
+
|
353 |
+
# def parse_models_and_urls_from_js(js_content: str) -> Dict:
|
354 |
+
# """从JS内容中解析模型和URL信息"""
|
355 |
+
# result = {"models": [], "urls": []}
|
356 |
+
#
|
357 |
+
# try:
|
358 |
+
# # 提取模型信息
|
359 |
+
# model_pattern = r'models\s*:\s*\[([^\]]+)\]'
|
360 |
+
# model_match = re.search(model_pattern, js_content)
|
361 |
+
#
|
362 |
+
# if model_match:
|
363 |
+
# models_data = model_match.group(1)
|
364 |
+
# models_data = re.sub(r'(\w+):', r'"\1":', models_data)
|
365 |
+
# models_data = models_data.replace("'", '"')
|
366 |
+
# models_data = f"[{models_data}]"
|
367 |
+
#
|
368 |
+
# try:
|
369 |
+
# models = json.loads(models_data)
|
370 |
+
# result["models"] = models
|
371 |
+
# except json.JSONDecodeError as e:
|
372 |
+
# print(f"Error decoding models JSON: {e}")
|
373 |
+
#
|
374 |
+
# # 提取URL信息
|
375 |
+
# url_pattern = r'\{name\s*:\s*"([^"]+)"\s*,\s*url\s*:\s*"([^"]+)"'
|
376 |
+
# url_matches = re.findall(url_pattern, js_content)
|
377 |
+
#
|
378 |
+
# if url_matches:
|
379 |
+
# urls = [{"name": name, "url": url} for name, url in url_matches]
|
380 |
+
# result["urls"] = urls
|
381 |
+
#
|
382 |
+
# return result
|
383 |
+
#
|
384 |
+
# except Exception as e:
|
385 |
+
# print(f"Error parsing JS content: {e}")
|
386 |
+
# return result
|
387 |
+
def get_from_js():
|
388 |
global cached_models
|
389 |
# 获取 JavaScript 文件内容
|
390 |
# url = "https://www.degpt.ai/_app/immutable/chunks/index.83d92b06.js"
|
391 |
+
# url = "https://www.degpt.ai/_app/immutable/chunks/index.4aecf75a.js"
|
392 |
+
url = "https://www.degpt.ai/_app/immutable/chunks/index.e0d19999.js"
|
393 |
response = requests.get(url)
|
394 |
|
395 |
if response.status_code == 200:
|
396 |
js_content = response.text
|
397 |
+
models = parse_models_from_js(js_content)
|
398 |
+
# xx = parse_models_and_urls_from_js(js_content)
|
399 |
if models:
|
400 |
+
if debug:
|
401 |
+
print("get_from_js提取的模型列表:")
|
402 |
existing_ids = {m.get('id') for m in cached_models["data"]}
|
403 |
for model in models:
|
404 |
model_id = model.get('model', '').strip()
|
405 |
+
if debug:
|
406 |
+
print(f"get_from_js 名称: {model.get('name', '')}")
|
407 |
+
print(f"get_from_js 模型: {model.get('model', '')}")
|
408 |
+
print(f"get_from_js 描述: {model.get('desc', '')}")
|
409 |
record_call(model_id)
|
410 |
if model_id and model_id not in existing_ids:
|
411 |
model_data = {
|
|
|
420 |
"tip": model.get('tip', '')
|
421 |
}
|
422 |
cached_models["data"].append(model_data)
|
423 |
+
if debug:
|
424 |
+
print(f"get_from_js添加新模型: {model_id}")
|
425 |
|
426 |
|
427 |
+
def is_model_available(model_id: str, cooldown_seconds: int = 300) -> bool:
|
428 |
"""
|
429 |
判断模型是否在模型列表中且非最近失败的模型
|
430 |
|
|
|
443 |
|
444 |
# 如果MODEL_STATS为空,加载模型数据
|
445 |
if not MODEL_STATS:
|
446 |
+
get_models()
|
447 |
|
448 |
# 检查模型是否在统计信息中
|
449 |
if model_id not in MODEL_STATS:
|
|
|
459 |
return True
|
460 |
|
461 |
|
462 |
+
def get_model_by_autoupdate(model_id: Optional[str] = None, cooldown_seconds: int = 300) -> Optional[str]:
|
463 |
"""
|
464 |
检查提供的model_id是否可用,如果不可用则返回成功率最高的模型
|
465 |
|
|
|
479 |
|
480 |
# 如果MODEL_STATS为空,加载模型数据
|
481 |
if not MODEL_STATS:
|
482 |
+
get_models()
|
483 |
|
484 |
# 如果提供了model_id且可用,直接返回
|
485 |
+
if model_id and is_model_available(model_id, cooldown_seconds):
|
486 |
return model_id
|
487 |
|
488 |
# 否则返回成功率最高的可用模型
|
489 |
+
return get_auto_model(cooldown_seconds=cooldown_seconds)
|
490 |
|
491 |
|
492 |
+
def is_chatgpt_format(data):
|
493 |
"""Check if the data is in the expected ChatGPT format"""
|
494 |
try:
|
495 |
# If the data is a string, try to parse it as JSON
|
|
|
511 |
return False
|
512 |
|
513 |
|
514 |
+
def chat_completion_message(
|
515 |
user_prompt,
|
516 |
user_id: str = None,
|
517 |
session_id: str = None,
|
518 |
system_prompt="You are a helpful assistant.",
|
519 |
+
model=base_model,
|
520 |
project="DecentralGPT", stream=False,
|
521 |
temperature=0.3, max_tokens=1024, top_p=0.5,
|
522 |
frequency_penalty=0, presence_penalty=0):
|
|
|
525 |
{"role": "system", "content": system_prompt},
|
526 |
{"role": "user", "content": user_prompt}
|
527 |
]
|
528 |
+
return chat_completion_messages(messages, user_id, session_id, model, project, stream, temperature, max_tokens,
|
529 |
top_p, frequency_penalty,
|
530 |
presence_penalty)
|
531 |
|
532 |
|
533 |
+
def chat_completion_messages(
|
534 |
messages,
|
535 |
+
model=base_model,
|
536 |
user_id: str = None,
|
537 |
session_id: str = None,
|
538 |
project="DecentralGPT", stream=False, temperature=0.3, max_tokens=1024, top_p=0.5,
|
539 |
frequency_penalty=0, presence_penalty=0):
|
540 |
+
# 确保model有效
|
541 |
+
if not model or model == "auto":
|
542 |
+
model = get_auto_model()
|
543 |
+
else:
|
544 |
+
model = get_model_by_autoupdate(model)
|
545 |
+
if debug:
|
546 |
+
print(f"校准后的model: {model}")
|
547 |
headers = {
|
548 |
'sec-ch-ua-platform': '"macOS"',
|
549 |
'Referer': 'https://www.degpt.ai/',
|
|
|
553 |
'Content-Type': 'application/json',
|
554 |
'sec-ch-ua-mobile': '?0'
|
555 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
556 |
payload = {
|
557 |
# make sure ok
|
558 |
"model": model,
|
|
|
568 |
}
|
569 |
# print(json.dumps(headers, indent=4))
|
570 |
# print(json.dumps(payload, indent=4))
|
571 |
+
return chat_completion(headers, payload)
|
572 |
|
573 |
|
574 |
+
def chat_completion(headers, payload):
|
575 |
"""处理用户请求并保留上下文"""
|
576 |
try:
|
577 |
+
url = f'{base_url}/v0/chat/completion/proxy'
|
578 |
response = requests.post(url, headers=headers, json=payload)
|
579 |
response.encoding = 'utf-8'
|
580 |
response.raise_for_status()
|
581 |
return response.json()
|
582 |
except requests.exceptions.RequestException as e:
|
583 |
print(f"请求失败: {e}")
|
|
|
584 |
return "请求失败,请检查网络或参数配置。"
|
585 |
except (KeyError, IndexError) as e:
|
586 |
print(f"解析响应时出错: {e}")
|
|
|
587 |
return "解析响应内容失败。"
|
588 |
return {}
|
589 |
|
590 |
+
# if __name__ == '__main__':
|
591 |
+
# print("get_models: ",get_models())
|
592 |
+
# print("cached_models:",cached_models)
|
593 |
+
# print("base_url: ",base_url)
|
594 |
+
# print("MODEL_STATS:",MODEL_STATS)
|
more_core.py
CHANGED
@@ -1,5 +1,4 @@
|
|
1 |
import json
|
2 |
-
import json
|
3 |
import multiprocessing
|
4 |
import os
|
5 |
import random
|
@@ -17,7 +16,7 @@ from starlette.responses import HTMLResponse
|
|
17 |
import degpt as dg
|
18 |
|
19 |
# debug for Log
|
20 |
-
debug =
|
21 |
|
22 |
app = FastAPI(
|
23 |
title="ones",
|
@@ -38,27 +37,27 @@ class APIServer:
|
|
38 |
self.scheduler.start()
|
39 |
|
40 |
def _setup_routes(self) -> None:
|
41 |
-
"""Initialize API routes"""
|
42 |
|
43 |
# Static routes with names for filtering
|
44 |
@self.app.get("/", name="root", include_in_schema=False)
|
45 |
-
|
46 |
return HTMLResponse(content="<h1>hello. It's home page.</h1>")
|
47 |
|
48 |
@self.app.get("/web", name="web")
|
49 |
-
|
50 |
return HTMLResponse(content="<h1>hello. It's web page.</h1>")
|
51 |
|
52 |
@self.app.get("/health", name="health")
|
53 |
-
|
54 |
return JSONResponse(content={"status": "working"})
|
55 |
|
56 |
|
57 |
@self.app.get("/v1/models", name="models")
|
58 |
-
|
59 |
if debug:
|
60 |
print("Fetching models...")
|
61 |
-
models_str =
|
62 |
try:
|
63 |
models_json = json.loads(models_str)
|
64 |
return JSONResponse(content=models_json)
|
@@ -105,11 +104,13 @@ class APIServer:
|
|
105 |
|
106 |
async def chat_endpoint(request: Request) -> Dict[str, Any]:
|
107 |
try:
|
|
|
|
|
108 |
headers = dict(request.headers)
|
109 |
data = await request.json()
|
110 |
if debug:
|
111 |
print(f"Request received...\r\n\tHeaders: {headers},\r\n\tData: {data}")
|
112 |
-
return
|
113 |
except Exception as e:
|
114 |
if debug:
|
115 |
print(f"Request processing error: {e}")
|
@@ -165,7 +166,7 @@ class APIServer:
|
|
165 |
result['model'] = model # 根据需要设置model值
|
166 |
return result
|
167 |
|
168 |
-
|
169 |
"""Generate API response"""
|
170 |
global debug
|
171 |
if debug:
|
@@ -176,7 +177,7 @@ class APIServer:
|
|
176 |
# print(f"model: {model}")
|
177 |
# just auto will check
|
178 |
if "auto" == model:
|
179 |
-
model =
|
180 |
# else:
|
181 |
# if not dg.is_model_available(model):
|
182 |
# raise HTTPException(status_code=400, detail="Invalid Model")
|
@@ -195,19 +196,17 @@ class APIServer:
|
|
195 |
|
196 |
if debug:
|
197 |
print(f"request model: {model}")
|
198 |
-
|
|
|
199 |
print(f"request messages: {msgs}")
|
200 |
|
201 |
-
result =
|
202 |
messages=msgs,
|
203 |
model=model
|
204 |
)
|
205 |
if debug:
|
206 |
print(f"result: {result}---- {self.is_chatgpt_format(result)}")
|
207 |
|
208 |
-
# # Assuming this 'result' comes from your model or some other logic
|
209 |
-
# result = "This is a test result."
|
210 |
-
|
211 |
# If the request body data already matches ChatGPT format, return it directly
|
212 |
if self.is_chatgpt_format(result):
|
213 |
# If data already follows ChatGPT format, use it directly
|
@@ -246,7 +245,7 @@ class APIServer:
|
|
246 |
|
247 |
return response_data
|
248 |
except Exception as e:
|
249 |
-
|
250 |
if debug:
|
251 |
print(f"Response generation error: {e}")
|
252 |
raise HTTPException(status_code=500, detail=str(e)) from e
|
@@ -287,8 +286,20 @@ class APIServer:
|
|
287 |
server = uvicorn.Server(config)
|
288 |
server.run()
|
289 |
|
290 |
-
|
291 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
292 |
|
293 |
def _schedule_route_check(self) -> None:
|
294 |
"""
|
|
|
1 |
import json
|
|
|
2 |
import multiprocessing
|
3 |
import os
|
4 |
import random
|
|
|
16 |
import degpt as dg
|
17 |
|
18 |
# debug for Log
|
19 |
+
debug = True
|
20 |
|
21 |
app = FastAPI(
|
22 |
title="ones",
|
|
|
37 |
self.scheduler.start()
|
38 |
|
39 |
def _setup_routes(self) -> None:
|
40 |
+
self.routes = """Initialize API routes"""
|
41 |
|
42 |
# Static routes with names for filtering
|
43 |
@self.app.get("/", name="root", include_in_schema=False)
|
44 |
+
def root():
|
45 |
return HTMLResponse(content="<h1>hello. It's home page.</h1>")
|
46 |
|
47 |
@self.app.get("/web", name="web")
|
48 |
+
def web():
|
49 |
return HTMLResponse(content="<h1>hello. It's web page.</h1>")
|
50 |
|
51 |
@self.app.get("/health", name="health")
|
52 |
+
def health():
|
53 |
return JSONResponse(content={"status": "working"})
|
54 |
|
55 |
|
56 |
@self.app.get("/v1/models", name="models")
|
57 |
+
def models():
|
58 |
if debug:
|
59 |
print("Fetching models...")
|
60 |
+
models_str = dg.get_models()
|
61 |
try:
|
62 |
models_json = json.loads(models_str)
|
63 |
return JSONResponse(content=models_json)
|
|
|
104 |
|
105 |
async def chat_endpoint(request: Request) -> Dict[str, Any]:
|
106 |
try:
|
107 |
+
if debug:
|
108 |
+
print(f"Request chat_endpoint...")
|
109 |
headers = dict(request.headers)
|
110 |
data = await request.json()
|
111 |
if debug:
|
112 |
print(f"Request received...\r\n\tHeaders: {headers},\r\n\tData: {data}")
|
113 |
+
return self._generate_response(headers, data)
|
114 |
except Exception as e:
|
115 |
if debug:
|
116 |
print(f"Request processing error: {e}")
|
|
|
166 |
result['model'] = model # 根据需要设置model值
|
167 |
return result
|
168 |
|
169 |
+
def _generate_response(self, headers: Dict[str, str], data: Dict[str, Any]) -> Dict[str, Any]:
|
170 |
"""Generate API response"""
|
171 |
global debug
|
172 |
if debug:
|
|
|
177 |
# print(f"model: {model}")
|
178 |
# just auto will check
|
179 |
if "auto" == model:
|
180 |
+
model = dg.get_auto_model()
|
181 |
# else:
|
182 |
# if not dg.is_model_available(model):
|
183 |
# raise HTTPException(status_code=400, detail="Invalid Model")
|
|
|
196 |
|
197 |
if debug:
|
198 |
print(f"request model: {model}")
|
199 |
+
if token:
|
200 |
+
print(f"request token: {token}")
|
201 |
print(f"request messages: {msgs}")
|
202 |
|
203 |
+
result = dg.chat_completion_messages(
|
204 |
messages=msgs,
|
205 |
model=model
|
206 |
)
|
207 |
if debug:
|
208 |
print(f"result: {result}---- {self.is_chatgpt_format(result)}")
|
209 |
|
|
|
|
|
|
|
210 |
# If the request body data already matches ChatGPT format, return it directly
|
211 |
if self.is_chatgpt_format(result):
|
212 |
# If data already follows ChatGPT format, use it directly
|
|
|
245 |
|
246 |
return response_data
|
247 |
except Exception as e:
|
248 |
+
dg.record_call(model,False)
|
249 |
if debug:
|
250 |
print(f"Response generation error: {e}")
|
251 |
raise HTTPException(status_code=500, detail=str(e)) from e
|
|
|
286 |
server = uvicorn.Server(config)
|
287 |
server.run()
|
288 |
|
289 |
+
"""
|
290 |
+
异步方法 `_reload_check` 用于执行模块的热重载检查。
|
291 |
+
|
292 |
+
此方法调用 `dg.reload_check()` 来检测是否有代码更新,并在必要时重新加载模块。
|
293 |
+
这是一个内部实现细节,通常不需要外部调用。
|
294 |
+
|
295 |
+
参数:
|
296 |
+
无
|
297 |
+
|
298 |
+
返回:
|
299 |
+
None
|
300 |
+
"""
|
301 |
+
def _reload_check(self) -> None:
|
302 |
+
dg.reload_check()
|
303 |
|
304 |
def _schedule_route_check(self) -> None:
|
305 |
"""
|
requirements.txt
CHANGED
@@ -2,22 +2,15 @@
|
|
2 |
fastapi
|
3 |
uvicorn[standard]
|
4 |
tiktoken
|
5 |
-
playwright
|
6 |
requests
|
7 |
starlette
|
8 |
pydantic
|
9 |
|
10 |
-
#
|
11 |
uvloop
|
12 |
httptools
|
13 |
aiohttp
|
14 |
-
asyncio
|
15 |
|
16 |
-
# Utils
|
17 |
apscheduler
|
18 |
-
python-
|
19 |
-
typing-extensions
|
20 |
-
python-dotenv
|
21 |
-
|
22 |
-
# Optional performance optimization
|
23 |
-
ujson
|
|
|
2 |
fastapi
|
3 |
uvicorn[standard]
|
4 |
tiktoken
|
|
|
5 |
requests
|
6 |
starlette
|
7 |
pydantic
|
8 |
|
9 |
+
# Performance optimization
|
10 |
uvloop
|
11 |
httptools
|
12 |
aiohttp
|
|
|
13 |
|
14 |
+
# Scheduling & Utils
|
15 |
apscheduler
|
16 |
+
python-dotenv
|
|
|
|
|
|
|
|
|
|