Spaces:
Sleeping
Sleeping
Upload 7 files
Browse files- README .md +119 -0
- app.py +199 -112
- config_editor.py +72 -0
- templates/dashboard.html +129 -28
README .md
ADDED
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
title: Abacus Chat Proxy
|
3 |
+
emoji: 🤖
|
4 |
+
colorFrom: blue
|
5 |
+
colorTo: purple
|
6 |
+
sdk: docker
|
7 |
+
sdk_version: "3.9"
|
8 |
+
app_file: app.py
|
9 |
+
pinned: false
|
10 |
+
---
|
11 |
+
|
12 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
13 |
+
|
14 |
+
# Abacus Chat Proxy
|
15 |
+
|
16 |
+
> 📢 本项目基于 [orbitoo/abacus_chat_proxy](https://github.com/orbitoo/abacus_chat_proxy) 改进
|
17 |
+
>
|
18 |
+
> 特别感谢 orbitoo 大佬提供的原始项目!
|
19 |
+
>
|
20 |
+
> 本项目增加了:Docker部署支持、Hugging Face一键部署、自动保活功能等
|
21 |
+
|
22 |
+
一个用于中转API请求的代理服务器。
|
23 |
+
|
24 |
+
#### 注意创建space的时候把private改为public,因为不改为public导致的错误别来找我
|
25 |
+
|
26 |
+
[](https://huggingface.co/spaces/malt666/abacus_chat_proxy?duplicate=true)
|
27 |
+
|
28 |
+
## ⚠️ 警告
|
29 |
+
|
30 |
+
**本地部署方式已失效!**为了适配hugging face,本项目的本地部署方式已不再可用。目前只能通过Hugging Face Spaces部署来使用本代理服务。请使用下方的Hugging Face一键部署方法。
|
31 |
+
|
32 |
+
## 🚀 快速开始
|
33 |
+
|
34 |
+
### Hugging Face一键部署
|
35 |
+
|
36 |
+
#### 注意创建space的时候把private改为public,因为不改为public导致的错误别来找我
|
37 |
+
|
38 |
+
1. 点击上方的"Deploy to Hugging Face Spaces"按钮
|
39 |
+
2. 登录你的Hugging Face账号(如果还没有,需要注册一个)
|
40 |
+
3. 在弹出的页面中设置你的Space名称,注意创建space的时候把private改为public,因为不改为为public导致的错误别来找我
|
41 |
+
4. 创建完Space后,在Space的Settings -> Repository Secrets中添加以下配置:
|
42 |
+
- `cookie_1`: 你的cookies字符串
|
43 |
+
- `password`: (必填,最好超过8位数,不然被盗用api导致点数用光,损失自负)api调用密码,也是dashboard登录密码
|
44 |
+
5. 等待自动部署完成即可
|
45 |
+
6. 登录dashboard查看使用情况,space里面是没办法登录的,点击弹出的登录页面的小窗口提示“请点击 https://xyz-abacus-chat-proxy.hf.space 来登录并查看使用情况”的链接来登录,登录密码是你设置的password
|
46 |
+
7. **获取API链接**:部署成功后,网络的登录页面会显示api接口链接;或者你点击右上角的三个点按钮,在弹出的选项卡里面点击"Embed this Space",然后在弹出的"Embed this Space"界面里的"Direct URL"就是你的访问链接,你可以用这个链接调用API和查看使用情况
|
47 |
+
8. api调用:api调用的时候注意密钥填写你的password,不然没办法调用
|
48 |
+
### 本地运行(已失效)
|
49 |
+
|
50 |
+
> ⚠️ 以下本地运行方法已失效,仅作参考。请使用Hugging Face部署方式。
|
51 |
+
|
52 |
+
#### Windows用户
|
53 |
+
|
54 |
+
1. 双击运行 `start.bat`
|
55 |
+
2. 首次运行选择 `0` 进行配置
|
56 |
+
3. 配置完成后选择 `Y` 直接启动,或 `N` 返回菜单
|
57 |
+
4. 之后可直接选择 `1` 启动代理
|
58 |
+
5. 代理服务器默认运行在 `http://127.0.0.1:9876/`
|
59 |
+
|
60 |
+
#### Linux/macOS用户
|
61 |
+
|
62 |
+
```bash
|
63 |
+
# 赋予脚本执行权限
|
64 |
+
chmod +x start.sh
|
65 |
+
|
66 |
+
# 运行脚本
|
67 |
+
./start.sh
|
68 |
+
```
|
69 |
+
|
70 |
+
选项说明同Windows。
|
71 |
+
|
72 |
+
### 🌐 Hugging Face部署
|
73 |
+
|
74 |
+
1. Fork本仓库到你的GitHub账号
|
75 |
+
2. 在Hugging Face上创建新的Space(选择Docker类型)
|
76 |
+
3. 在Space的设置中连接你的GitHub仓库
|
77 |
+
4. 在Space的设置中添加以下Secrets:
|
78 |
+
- 第1组配置:
|
79 |
+
- `cookie_1`: 第1个cookies字符串
|
80 |
+
- 第2组配置(如果需要):
|
81 |
+
- `cookie_2`: 第2个cookies字符串
|
82 |
+
- 更多配置以此类推(`cookie_3`...)
|
83 |
+
- `password`: (可选)访问密码
|
84 |
+
5. Space会自动部署,服务将在 `https://你的空间名-你的用户名.hf.space` 上运行
|
85 |
+
|
86 |
+
## ⚙️ 环境要求
|
87 |
+
|
88 |
+
- Python 3.8+
|
89 |
+
- pip
|
90 |
+
|
91 |
+
## 📦 依赖
|
92 |
+
|
93 |
+
```bash
|
94 |
+
Flask==3.1.0
|
95 |
+
requests==2.32.3
|
96 |
+
PyJWT==2.8.0
|
97 |
+
```
|
98 |
+
|
99 |
+
## 📝 配置说明
|
100 |
+
|
101 |
+
### 本地配置
|
102 |
+
|
103 |
+
首次运行时,请选择 `0` 进行配置,按照提示填写相关信息。配置文件将保存在 `config.json` 中。
|
104 |
+
|
105 |
+
### 环境变量配置
|
106 |
+
|
107 |
+
在Docker或云平台部署时,需要配置以下环境变量:
|
108 |
+
|
109 |
+
- 必需的配置(至少需要一组):
|
110 |
+
- `cookie_1`: 第1组配置
|
111 |
+
- `cookie_2`: 第2组配置(可选)
|
112 |
+
- 以此类推...
|
113 |
+
- 可选配置:
|
114 |
+
- `password`: 访问密码
|
115 |
+
|
116 |
+
## 🔒 安全说明
|
117 |
+
|
118 |
+
- 建议在部署到Hugging Face时设置访问密码
|
119 |
+
- 在Hugging Face上配置时,请使用Secrets来存储敏感信息
|
app.py
CHANGED
@@ -26,6 +26,7 @@ USER_INFO_URL = "https://abacus.ai/api/v0/_getUserInfo"
|
|
26 |
COMPUTE_POINTS_URL = "https://apps.abacus.ai/api/_getOrganizationComputePoints"
|
27 |
COMPUTE_POINTS_LOG_URL = "https://abacus.ai/api/v0/_getOrganizationComputePointLog"
|
28 |
CREATE_CONVERSATION_URL = "https://apps.abacus.ai/api/createDeploymentConversation"
|
|
|
29 |
|
30 |
|
31 |
USER_AGENTS = [
|
@@ -38,6 +39,9 @@ USER_NUM = 0
|
|
38 |
USER_DATA = []
|
39 |
CURRENT_USER = -1
|
40 |
MODELS = set()
|
|
|
|
|
|
|
41 |
|
42 |
|
43 |
TRACE_ID = "3042e28b3abf475d8d973c7e904935af"
|
@@ -81,7 +85,6 @@ users_compute_points = []
|
|
81 |
# 记录启动时间
|
82 |
START_TIME = datetime.utcnow() + timedelta(hours=8) # 北京时间
|
83 |
|
84 |
-
|
85 |
# 自定义JSON编码器,处理datetime对象
|
86 |
class DateTimeEncoder(json.JSONEncoder):
|
87 |
def default(self, obj):
|
@@ -90,32 +93,6 @@ class DateTimeEncoder(json.JSONEncoder):
|
|
90 |
return super(DateTimeEncoder, self).default(obj)
|
91 |
|
92 |
|
93 |
-
# 加载模型调用记录
|
94 |
-
def load_model_usage_records():
|
95 |
-
global model_usage_records
|
96 |
-
try:
|
97 |
-
if os.path.exists(MODEL_USAGE_RECORDS_FILE):
|
98 |
-
with open(MODEL_USAGE_RECORDS_FILE, 'r', encoding='utf-8') as f:
|
99 |
-
records = json.load(f)
|
100 |
-
if isinstance(records, list):
|
101 |
-
model_usage_records = records
|
102 |
-
print(f"成功加载 {len(model_usage_records)} 条模型调用记录")
|
103 |
-
else:
|
104 |
-
print("调用记录文件格式不正确,初始化为空列表")
|
105 |
-
except Exception as e:
|
106 |
-
print(f"加载模型调用记录失败: {e}")
|
107 |
-
model_usage_records = []
|
108 |
-
|
109 |
-
# 保存模型调用记录
|
110 |
-
def save_model_usage_records():
|
111 |
-
try:
|
112 |
-
with open(MODEL_USAGE_RECORDS_FILE, 'w', encoding='utf-8') as f:
|
113 |
-
json.dump(model_usage_records, f, ensure_ascii=False, indent=2, cls=DateTimeEncoder)
|
114 |
-
print(f"成功保存 {len(model_usage_records)} 条模型调用记录")
|
115 |
-
except Exception as e:
|
116 |
-
print(f"保存模型调用记录失败: {e}")
|
117 |
-
|
118 |
-
|
119 |
def update_conversation_id(user_index, conversation_id):
|
120 |
"""更新用户的conversation_id并保存到配置文件"""
|
121 |
try:
|
@@ -136,39 +113,46 @@ def update_conversation_id(user_index, conversation_id):
|
|
136 |
print(f"更新conversation_id失败: {e}")
|
137 |
|
138 |
|
139 |
-
def
|
140 |
-
|
141 |
-
|
142 |
-
i = 1
|
143 |
-
while True:
|
144 |
-
cookie = os.environ.get(f"cookie_{i}")
|
145 |
-
if not cookie:
|
146 |
-
break
|
147 |
-
|
148 |
-
# 为每个cookie创建一个配置项,conversation_id初始为空
|
149 |
-
config_list.append({
|
150 |
-
"conversation_id": "", # 初始为空,将通过get_or_create_conversation自动创建
|
151 |
-
"cookies": cookie
|
152 |
-
})
|
153 |
-
i += 1
|
154 |
-
|
155 |
-
# 如果环境变量存在配置,使用环境变量的配置
|
156 |
-
if config_list:
|
157 |
-
print(f"从环境变量加载了 {len(config_list)} 个配置")
|
158 |
-
return config_list
|
159 |
-
|
160 |
-
# 如果环境变量不存在,从文件读取
|
161 |
try:
|
162 |
-
|
|
|
163 |
config = json.load(f)
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
172 |
|
173 |
|
174 |
def get_password():
|
@@ -248,11 +232,10 @@ def refresh_token(session, cookies):
|
|
248 |
}
|
249 |
|
250 |
try:
|
251 |
-
response =
|
252 |
USER_INFO_URL,
|
253 |
headers=headers,
|
254 |
-
json={}
|
255 |
-
cookies=None
|
256 |
)
|
257 |
|
258 |
if response.status_code == 200:
|
@@ -297,11 +280,10 @@ def get_model_map(session, cookies, session_token):
|
|
297 |
models_set = set()
|
298 |
|
299 |
try:
|
300 |
-
response =
|
301 |
MODEL_LIST_URL,
|
302 |
headers=headers,
|
303 |
-
json={}
|
304 |
-
cookies=None
|
305 |
)
|
306 |
|
307 |
if response.status_code != 200:
|
@@ -490,7 +472,7 @@ def create_conversation(session, cookies, session_token, external_application_id
|
|
490 |
}
|
491 |
|
492 |
try:
|
493 |
-
response =
|
494 |
CREATE_CONVERSATION_URL,
|
495 |
headers=headers,
|
496 |
json=create_payload
|
@@ -511,6 +493,49 @@ def create_conversation(session, cookies, session_token, external_application_id
|
|
511 |
return None
|
512 |
|
513 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
514 |
def is_conversation_valid(session, cookies, session_token, conversation_id, model_map, model):
|
515 |
"""检查会话ID是否有效"""
|
516 |
if not conversation_id:
|
@@ -542,7 +567,8 @@ def is_conversation_valid(session, cookies, session_token, conversation_id, mode
|
|
542 |
}
|
543 |
|
544 |
try:
|
545 |
-
|
|
|
546 |
CHAT_URL,
|
547 |
headers=headers,
|
548 |
data=json.dumps(payload),
|
@@ -566,42 +592,33 @@ def is_conversation_valid(session, cookies, session_token, conversation_id, mode
|
|
566 |
|
567 |
def get_or_create_conversation(session, cookies, session_token, conversation_id, model_map, model, user_index):
|
568 |
"""获取有效的会话ID,如果无效则创建新会话"""
|
569 |
-
#
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
#
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
|
|
|
|
|
|
586 |
|
587 |
-
|
588 |
-
|
589 |
-
external_application_id=external_app_id,
|
590 |
-
deployment_id=deployment_id
|
591 |
-
)
|
592 |
|
593 |
-
|
594 |
-
# 更新全局存储的会话ID
|
595 |
-
global USER_DATA, CURRENT_USER
|
596 |
-
session, cookies, session_token, _, model_map, _ = USER_DATA[CURRENT_USER]
|
597 |
-
USER_DATA[CURRENT_USER] = (session, cookies, session_token, new_conversation_id, model_map, user_index)
|
598 |
-
|
599 |
-
# 保存到配置文件
|
600 |
-
update_conversation_id(user_index, new_conversation_id)
|
601 |
-
|
602 |
-
return new_conversation_id
|
603 |
|
604 |
-
#
|
605 |
return conversation_id
|
606 |
|
607 |
|
@@ -616,8 +633,14 @@ def send_message(message, model, think=False):
|
|
616 |
"""Flua traktado kaj plusendo de mesaĝoj"""
|
617 |
(session, cookies, session_token, conversation_id, model_map, user_index) = get_user_data()
|
618 |
|
619 |
-
#
|
620 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
621 |
|
622 |
trace_id, sentry_trace = generate_trace_id()
|
623 |
|
@@ -646,7 +669,7 @@ def send_message(message, model, think=False):
|
|
646 |
|
647 |
payload = {
|
648 |
"requestId": str(uuid.uuid4()),
|
649 |
-
"deploymentConversationId":
|
650 |
"message": message,
|
651 |
"isDesktop": False,
|
652 |
"chatConfig": {
|
@@ -663,7 +686,7 @@ def send_message(message, model, think=False):
|
|
663 |
payload["useThinking"] = think
|
664 |
|
665 |
try:
|
666 |
-
response =
|
667 |
CHAT_URL,
|
668 |
headers=headers,
|
669 |
data=json.dumps(payload),
|
@@ -732,6 +755,10 @@ def send_message(message, model, think=False):
|
|
732 |
# 在流式传输完成后计算token并更新统计
|
733 |
completion_tokens = num_tokens_from_string(completion_buffer.getvalue())
|
734 |
update_model_stats(model, prompt_tokens, completion_tokens)
|
|
|
|
|
|
|
|
|
735 |
|
736 |
return Response(generate(), mimetype="text/event-stream")
|
737 |
except requests.exceptions.RequestException as e:
|
@@ -747,8 +774,14 @@ def send_message_non_stream(message, model, think=False):
|
|
747 |
"""Ne-flua traktado de mesaĝoj"""
|
748 |
(session, cookies, session_token, conversation_id, model_map, user_index) = get_user_data()
|
749 |
|
750 |
-
#
|
751 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
752 |
|
753 |
trace_id, sentry_trace = generate_trace_id()
|
754 |
|
@@ -776,7 +809,7 @@ def send_message_non_stream(message, model, think=False):
|
|
776 |
|
777 |
payload = {
|
778 |
"requestId": str(uuid.uuid4()),
|
779 |
-
"deploymentConversationId":
|
780 |
"message": message,
|
781 |
"isDesktop": False,
|
782 |
"chatConfig": {
|
@@ -793,7 +826,7 @@ def send_message_non_stream(message, model, think=False):
|
|
793 |
payload["useThinking"] = think
|
794 |
|
795 |
try:
|
796 |
-
response =
|
797 |
CHAT_URL,
|
798 |
headers=headers,
|
799 |
data=json.dumps(payload),
|
@@ -854,6 +887,10 @@ def send_message_non_stream(message, model, think=False):
|
|
854 |
completion_tokens = num_tokens_from_string(think_content + response_content)
|
855 |
update_model_stats(model, prompt_tokens, completion_tokens)
|
856 |
|
|
|
|
|
|
|
|
|
857 |
return jsonify({
|
858 |
"id": f"chatcmpl-{str(uuid.uuid4())}",
|
859 |
"object": "chat.completion",
|
@@ -887,6 +924,10 @@ def send_message_non_stream(message, model, think=False):
|
|
887 |
completion_tokens = num_tokens_from_string(response_content)
|
888 |
update_model_stats(model, prompt_tokens, completion_tokens)
|
889 |
|
|
|
|
|
|
|
|
|
890 |
return jsonify({
|
891 |
"id": f"chatcmpl-{str(uuid.uuid4())}",
|
892 |
"object": "chat.completion",
|
@@ -979,10 +1020,14 @@ def keep_alive():
|
|
979 |
"""每20分钟进行一次自我健康检查"""
|
980 |
while True:
|
981 |
try:
|
982 |
-
|
|
|
|
|
|
|
983 |
time.sleep(1200) # 20分钟
|
984 |
except:
|
985 |
-
|
|
|
986 |
|
987 |
|
988 |
@app.route("/", methods=["GET"])
|
@@ -1096,7 +1141,7 @@ def get_compute_points():
|
|
1096 |
"cookie": cookies
|
1097 |
}
|
1098 |
|
1099 |
-
response =
|
1100 |
COMPUTE_POINTS_URL,
|
1101 |
headers=headers
|
1102 |
)
|
@@ -1170,7 +1215,7 @@ def get_compute_points_log(session, cookies, session_token):
|
|
1170 |
"cookie": cookies
|
1171 |
}
|
1172 |
|
1173 |
-
response =
|
1174 |
COMPUTE_POINTS_LOG_URL,
|
1175 |
headers=headers,
|
1176 |
json={"byLlm": True}
|
@@ -1251,10 +1296,26 @@ def dashboard():
|
|
1251 |
compute_points_log=compute_points_log,
|
1252 |
space_url=SPACE_URL, # 传递空间URL
|
1253 |
users_compute_points=users_compute_points, # 传递用户计算点信息
|
1254 |
-
model_usage_records=model_usage_records # 传递模型使用记录
|
|
|
1255 |
)
|
1256 |
|
1257 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1258 |
# 获取Hugging Face Space URL
|
1259 |
def get_space_url():
|
1260 |
# 尝试从环境变量获取
|
@@ -1310,6 +1371,32 @@ def auto_save_stats():
|
|
1310 |
print(f"自动保存/加载模型使用记录出错: {e}")
|
1311 |
|
1312 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1313 |
if __name__ == "__main__":
|
1314 |
# 启动保活线程
|
1315 |
threading.Thread(target=keep_alive, daemon=True).start()
|
|
|
26 |
COMPUTE_POINTS_URL = "https://apps.abacus.ai/api/_getOrganizationComputePoints"
|
27 |
COMPUTE_POINTS_LOG_URL = "https://abacus.ai/api/v0/_getOrganizationComputePointLog"
|
28 |
CREATE_CONVERSATION_URL = "https://apps.abacus.ai/api/createDeploymentConversation"
|
29 |
+
DELETE_CONVERSATION_URL = "https://apps.abacus.ai/api/deleteDeploymentConversation"
|
30 |
|
31 |
|
32 |
USER_AGENTS = [
|
|
|
39 |
USER_DATA = []
|
40 |
CURRENT_USER = -1
|
41 |
MODELS = set()
|
42 |
+
# 添加变量跟踪是否删除上一个对话以及上一个对话ID
|
43 |
+
DELETE_CHAT = True # 默认启用删除功能
|
44 |
+
PREVIOUS_CONVERSATION_IDS = {} # 用于跟踪每个用户的上一个conversation_id
|
45 |
|
46 |
|
47 |
TRACE_ID = "3042e28b3abf475d8d973c7e904935af"
|
|
|
85 |
# 记录启动时间
|
86 |
START_TIME = datetime.utcnow() + timedelta(hours=8) # 北京时间
|
87 |
|
|
|
88 |
# 自定义JSON编码器,处理datetime对象
|
89 |
class DateTimeEncoder(json.JSONEncoder):
|
90 |
def default(self, obj):
|
|
|
93 |
return super(DateTimeEncoder, self).default(obj)
|
94 |
|
95 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
96 |
def update_conversation_id(user_index, conversation_id):
|
97 |
"""更新用户的conversation_id并保存到配置文件"""
|
98 |
try:
|
|
|
113 |
print(f"更新conversation_id失败: {e}")
|
114 |
|
115 |
|
116 |
+
def load_config():
|
117 |
+
"""从配置文件加载配置"""
|
118 |
+
global USER_DATA, CURRENT_USER
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
119 |
try:
|
120 |
+
# 读取配置文件
|
121 |
+
with open('config.json', 'r') as f:
|
122 |
config = json.load(f)
|
123 |
+
|
124 |
+
# 确保USER_DATA为空列表
|
125 |
+
USER_DATA = []
|
126 |
+
|
127 |
+
# 如果配置文件中有配置项,则加载
|
128 |
+
if "config" in config and isinstance(config["config"], list):
|
129 |
+
for i, user_config in enumerate(config["config"]):
|
130 |
+
# 获取必要的配置项
|
131 |
+
cookies = user_config.get("cookies", "")
|
132 |
+
session_token = user_config.get("session_token", "")
|
133 |
+
models = user_config.get("models", {})
|
134 |
+
|
135 |
+
# 不再从配置文件读取conversation_id,确保每次启动都使用新会话
|
136 |
+
conversation_id = None
|
137 |
+
|
138 |
+
# 创建会话对象
|
139 |
+
session = requests.Session()
|
140 |
+
|
141 |
+
# 添加到USER_DATA
|
142 |
+
USER_DATA.append((session, cookies, session_token, conversation_id, models, i))
|
143 |
+
|
144 |
+
print(f"成功加载了 {len(USER_DATA)} 个用户配置")
|
145 |
+
|
146 |
+
# 设置当前用户为第一个用户
|
147 |
+
CURRENT_USER = 0
|
148 |
+
|
149 |
+
return True
|
150 |
+
else:
|
151 |
+
print("配置文件格式错误")
|
152 |
+
return False
|
153 |
+
except Exception as e:
|
154 |
+
print(f"加载配置失败: {e}")
|
155 |
+
return False
|
156 |
|
157 |
|
158 |
def get_password():
|
|
|
232 |
}
|
233 |
|
234 |
try:
|
235 |
+
response = requests.post(
|
236 |
USER_INFO_URL,
|
237 |
headers=headers,
|
238 |
+
json={}
|
|
|
239 |
)
|
240 |
|
241 |
if response.status_code == 200:
|
|
|
280 |
models_set = set()
|
281 |
|
282 |
try:
|
283 |
+
response = requests.post(
|
284 |
MODEL_LIST_URL,
|
285 |
headers=headers,
|
286 |
+
json={}
|
|
|
287 |
)
|
288 |
|
289 |
if response.status_code != 200:
|
|
|
472 |
}
|
473 |
|
474 |
try:
|
475 |
+
response = requests.post(
|
476 |
CREATE_CONVERSATION_URL,
|
477 |
headers=headers,
|
478 |
json=create_payload
|
|
|
493 |
return None
|
494 |
|
495 |
|
496 |
+
def delete_conversation(session, cookies, session_token, conversation_id):
|
497 |
+
"""删除指定的会话"""
|
498 |
+
if not conversation_id:
|
499 |
+
print("无法删除会话: conversation_id为空")
|
500 |
+
return False
|
501 |
+
|
502 |
+
headers = {
|
503 |
+
"accept": "application/json, text/plain, */*",
|
504 |
+
"accept-language": "zh-CN,zh;q=0.9",
|
505 |
+
"content-type": "application/json",
|
506 |
+
"cookie": cookies,
|
507 |
+
"user-agent": random.choice(USER_AGENTS),
|
508 |
+
"x-abacus-org-host": "apps"
|
509 |
+
}
|
510 |
+
|
511 |
+
if session_token:
|
512 |
+
headers["session-token"] = session_token
|
513 |
+
|
514 |
+
delete_payload = {
|
515 |
+
"deploymentId": "14b2a314cc",
|
516 |
+
"deploymentConversationId": conversation_id
|
517 |
+
}
|
518 |
+
|
519 |
+
try:
|
520 |
+
response = requests.post(
|
521 |
+
DELETE_CONVERSATION_URL,
|
522 |
+
headers=headers,
|
523 |
+
json=delete_payload
|
524 |
+
)
|
525 |
+
|
526 |
+
if response.status_code == 200:
|
527 |
+
data = response.json()
|
528 |
+
if data.get("success", False):
|
529 |
+
print(f"成功删除conversation: {conversation_id}")
|
530 |
+
return True
|
531 |
+
|
532 |
+
print(f"删除会话失败: {response.status_code} - {response.text[:100]}")
|
533 |
+
return False
|
534 |
+
except Exception as e:
|
535 |
+
print(f"删除会话时出错: {e}")
|
536 |
+
return False
|
537 |
+
|
538 |
+
|
539 |
def is_conversation_valid(session, cookies, session_token, conversation_id, model_map, model):
|
540 |
"""检查会话ID是否有效"""
|
541 |
if not conversation_id:
|
|
|
567 |
}
|
568 |
|
569 |
try:
|
570 |
+
# 直接发送请求
|
571 |
+
response = requests.post(
|
572 |
CHAT_URL,
|
573 |
headers=headers,
|
574 |
data=json.dumps(payload),
|
|
|
592 |
|
593 |
def get_or_create_conversation(session, cookies, session_token, conversation_id, model_map, model, user_index):
|
594 |
"""获取有效的会话ID,如果无效则创建新会话"""
|
595 |
+
# 始终创建新会话,不再检查现有会话是否有效
|
596 |
+
print("正在创建新会话...")
|
597 |
+
|
598 |
+
if model in model_map and len(model_map[model]) >= 2:
|
599 |
+
external_app_id = model_map[model][0]
|
600 |
+
# 创建会话时需要deployment_id,我们先使用一个固定值
|
601 |
+
# 在实际应用中应从API响应中获取
|
602 |
+
deployment_id = "14b2a314cc" # 这是从您提供的请求中获取的
|
603 |
+
|
604 |
+
new_conversation_id = create_conversation(
|
605 |
+
session, cookies, session_token,
|
606 |
+
external_application_id=external_app_id,
|
607 |
+
deployment_id=deployment_id
|
608 |
+
)
|
609 |
+
|
610 |
+
if new_conversation_id:
|
611 |
+
# 更新全局存储的会话ID
|
612 |
+
global USER_DATA, CURRENT_USER
|
613 |
+
session, cookies, session_token, _, model_map, _ = USER_DATA[CURRENT_USER]
|
614 |
+
USER_DATA[CURRENT_USER] = (session, cookies, session_token, new_conversation_id, model_map, user_index)
|
615 |
|
616 |
+
# 不再保存到配置文件,确保每次都创建新会话
|
617 |
+
# update_conversation_id(user_index, new_conversation_id)
|
|
|
|
|
|
|
618 |
|
619 |
+
return new_conversation_id
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
620 |
|
621 |
+
# 如果创建失败,返回原始ID
|
622 |
return conversation_id
|
623 |
|
624 |
|
|
|
633 |
"""Flua traktado kaj plusendo de mesaĝoj"""
|
634 |
(session, cookies, session_token, conversation_id, model_map, user_index) = get_user_data()
|
635 |
|
636 |
+
# 保存当前用户的上一个对话ID用于稍后删除
|
637 |
+
previous_conversation_id = PREVIOUS_CONVERSATION_IDS.get(user_index)
|
638 |
+
|
639 |
+
# 确保有有效的会话ID,现在总是会创建新的
|
640 |
+
new_conversation_id = get_or_create_conversation(session, cookies, session_token, conversation_id, model_map, model, user_index)
|
641 |
+
|
642 |
+
# 更新当前用户的会话ID记录
|
643 |
+
PREVIOUS_CONVERSATION_IDS[user_index] = new_conversation_id
|
644 |
|
645 |
trace_id, sentry_trace = generate_trace_id()
|
646 |
|
|
|
669 |
|
670 |
payload = {
|
671 |
"requestId": str(uuid.uuid4()),
|
672 |
+
"deploymentConversationId": new_conversation_id,
|
673 |
"message": message,
|
674 |
"isDesktop": False,
|
675 |
"chatConfig": {
|
|
|
686 |
payload["useThinking"] = think
|
687 |
|
688 |
try:
|
689 |
+
response = requests.post(
|
690 |
CHAT_URL,
|
691 |
headers=headers,
|
692 |
data=json.dumps(payload),
|
|
|
755 |
# 在流式传输完成后计算token并更新统计
|
756 |
completion_tokens = num_tokens_from_string(completion_buffer.getvalue())
|
757 |
update_model_stats(model, prompt_tokens, completion_tokens)
|
758 |
+
|
759 |
+
# 在流式传输完成后删除上一个对话(如果存在且启用了删除功能)
|
760 |
+
if DELETE_CHAT and previous_conversation_id and previous_conversation_id != new_conversation_id:
|
761 |
+
delete_conversation(session, cookies, session_token, previous_conversation_id)
|
762 |
|
763 |
return Response(generate(), mimetype="text/event-stream")
|
764 |
except requests.exceptions.RequestException as e:
|
|
|
774 |
"""Ne-flua traktado de mesaĝoj"""
|
775 |
(session, cookies, session_token, conversation_id, model_map, user_index) = get_user_data()
|
776 |
|
777 |
+
# 保存当前用户的上一个对话ID用于稍后删除
|
778 |
+
previous_conversation_id = PREVIOUS_CONVERSATION_IDS.get(user_index)
|
779 |
+
|
780 |
+
# 确保有有效的会话ID,现在总是会创建新的
|
781 |
+
new_conversation_id = get_or_create_conversation(session, cookies, session_token, conversation_id, model_map, model, user_index)
|
782 |
+
|
783 |
+
# 更新当前用户的会话ID记录
|
784 |
+
PREVIOUS_CONVERSATION_IDS[user_index] = new_conversation_id
|
785 |
|
786 |
trace_id, sentry_trace = generate_trace_id()
|
787 |
|
|
|
809 |
|
810 |
payload = {
|
811 |
"requestId": str(uuid.uuid4()),
|
812 |
+
"deploymentConversationId": new_conversation_id,
|
813 |
"message": message,
|
814 |
"isDesktop": False,
|
815 |
"chatConfig": {
|
|
|
826 |
payload["useThinking"] = think
|
827 |
|
828 |
try:
|
829 |
+
response = requests.post(
|
830 |
CHAT_URL,
|
831 |
headers=headers,
|
832 |
data=json.dumps(payload),
|
|
|
887 |
completion_tokens = num_tokens_from_string(think_content + response_content)
|
888 |
update_model_stats(model, prompt_tokens, completion_tokens)
|
889 |
|
890 |
+
# 删除上一个对话(如果存在且启用了删除功能)
|
891 |
+
if DELETE_CHAT and previous_conversation_id and previous_conversation_id != new_conversation_id:
|
892 |
+
delete_conversation(session, cookies, session_token, previous_conversation_id)
|
893 |
+
|
894 |
return jsonify({
|
895 |
"id": f"chatcmpl-{str(uuid.uuid4())}",
|
896 |
"object": "chat.completion",
|
|
|
924 |
completion_tokens = num_tokens_from_string(response_content)
|
925 |
update_model_stats(model, prompt_tokens, completion_tokens)
|
926 |
|
927 |
+
# 删除上一个对话(如果存在且启用了删除功能)
|
928 |
+
if DELETE_CHAT and previous_conversation_id and previous_conversation_id != new_conversation_id:
|
929 |
+
delete_conversation(session, cookies, session_token, previous_conversation_id)
|
930 |
+
|
931 |
return jsonify({
|
932 |
"id": f"chatcmpl-{str(uuid.uuid4())}",
|
933 |
"object": "chat.completion",
|
|
|
1020 |
"""每20分钟进行一次自我健康检查"""
|
1021 |
while True:
|
1022 |
try:
|
1023 |
+
# 获取当前端口
|
1024 |
+
port = int(os.environ.get("PORT", 9876))
|
1025 |
+
# 请求本地健康检查端点
|
1026 |
+
requests.get(f"http://127.0.0.1:{port}/health")
|
1027 |
time.sleep(1200) # 20分钟
|
1028 |
except:
|
1029 |
+
# 忽略错误,保持运行
|
1030 |
+
time.sleep(1200) # 即使失败也等待20分钟
|
1031 |
|
1032 |
|
1033 |
@app.route("/", methods=["GET"])
|
|
|
1141 |
"cookie": cookies
|
1142 |
}
|
1143 |
|
1144 |
+
response = requests.get(
|
1145 |
COMPUTE_POINTS_URL,
|
1146 |
headers=headers
|
1147 |
)
|
|
|
1215 |
"cookie": cookies
|
1216 |
}
|
1217 |
|
1218 |
+
response = requests.post(
|
1219 |
COMPUTE_POINTS_LOG_URL,
|
1220 |
headers=headers,
|
1221 |
json={"byLlm": True}
|
|
|
1296 |
compute_points_log=compute_points_log,
|
1297 |
space_url=SPACE_URL, # 传递空间URL
|
1298 |
users_compute_points=users_compute_points, # 传递用户计算点信息
|
1299 |
+
model_usage_records=model_usage_records, # 传递模型使用记录
|
1300 |
+
delete_chat=DELETE_CHAT # 传递删除对话开关状态
|
1301 |
)
|
1302 |
|
1303 |
|
1304 |
+
@app.route("/api/set_delete_chat", methods=["POST"])
|
1305 |
+
@require_auth
|
1306 |
+
def set_delete_chat():
|
1307 |
+
"""设置是否启用自动删除对话功能"""
|
1308 |
+
global DELETE_CHAT
|
1309 |
+
try:
|
1310 |
+
data = request.get_json()
|
1311 |
+
if "delete_chat" in data:
|
1312 |
+
DELETE_CHAT = bool(data["delete_chat"])
|
1313 |
+
return jsonify({"success": True, "delete_chat": DELETE_CHAT})
|
1314 |
+
return jsonify({"success": False, "error": "Missing delete_chat parameter"}), 400
|
1315 |
+
except Exception as e:
|
1316 |
+
return jsonify({"success": False, "error": str(e)}), 500
|
1317 |
+
|
1318 |
+
|
1319 |
# 获取Hugging Face Space URL
|
1320 |
def get_space_url():
|
1321 |
# 尝试从环境变量获取
|
|
|
1371 |
print(f"自动保存/加载模型使用记录出错: {e}")
|
1372 |
|
1373 |
|
1374 |
+
# 加载模型调用记录
|
1375 |
+
def load_model_usage_records():
|
1376 |
+
global model_usage_records
|
1377 |
+
try:
|
1378 |
+
if os.path.exists(MODEL_USAGE_RECORDS_FILE):
|
1379 |
+
with open(MODEL_USAGE_RECORDS_FILE, 'r', encoding='utf-8') as f:
|
1380 |
+
records = json.load(f)
|
1381 |
+
if isinstance(records, list):
|
1382 |
+
model_usage_records = records
|
1383 |
+
print(f"成功加载 {len(model_usage_records)} 条模型调用记录")
|
1384 |
+
else:
|
1385 |
+
print("调用记录文件格式不正确,初始化为空列表")
|
1386 |
+
except Exception as e:
|
1387 |
+
print(f"加载模型调用记录失败: {e}")
|
1388 |
+
model_usage_records = []
|
1389 |
+
|
1390 |
+
# 保存模型调用记录
|
1391 |
+
def save_model_usage_records():
|
1392 |
+
try:
|
1393 |
+
with open(MODEL_USAGE_RECORDS_FILE, 'w', encoding='utf-8') as f:
|
1394 |
+
json.dump(model_usage_records, f, ensure_ascii=False, indent=2, cls=DateTimeEncoder)
|
1395 |
+
print(f"成功保存 {len(model_usage_records)} 条模型调用记录")
|
1396 |
+
except Exception as e:
|
1397 |
+
print(f"保存模型调用记录失败: {e}")
|
1398 |
+
|
1399 |
+
|
1400 |
if __name__ == "__main__":
|
1401 |
# 启动保活线程
|
1402 |
threading.Thread(target=keep_alive, daemon=True).start()
|
config_editor.py
ADDED
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
import os
|
3 |
+
import hashlib
|
4 |
+
|
5 |
+
if __name__ == "__main__":
|
6 |
+
config = None
|
7 |
+
path = os.path.dirname(os.path.realpath(__file__))
|
8 |
+
os.chdir(path)
|
9 |
+
if os.path.exists("config.json"):
|
10 |
+
with open("config.json", "r") as f:
|
11 |
+
config = json.load(f)
|
12 |
+
try:
|
13 |
+
config["config"]
|
14 |
+
except KeyError:
|
15 |
+
config = None
|
16 |
+
if config is None:
|
17 |
+
print(f"配置文件不存在或为空,创建新配置...")
|
18 |
+
config = {"config": []}
|
19 |
+
print(f"输入会话ID: ")
|
20 |
+
user_data = {"conversation_id": input()}
|
21 |
+
print(f"输入cookies: ")
|
22 |
+
user_data["cookies"] = input()
|
23 |
+
config["config"].append(user_data)
|
24 |
+
|
25 |
+
again = True
|
26 |
+
while True:
|
27 |
+
if again:
|
28 |
+
num = len(config["config"])
|
29 |
+
print(f"\n当前有 {num} 个配置。")
|
30 |
+
print("----------")
|
31 |
+
print(f"1. 添加新配置")
|
32 |
+
print(f"2. 删除所有配置")
|
33 |
+
print(f"3. 设置密码")
|
34 |
+
print(f"4. 保存并退出")
|
35 |
+
choice = input()
|
36 |
+
|
37 |
+
if choice == "1":
|
38 |
+
print(f"输入会话ID: ")
|
39 |
+
user_data = {"conversation_id": input()}
|
40 |
+
print(f"输入cookies: ")
|
41 |
+
user_data["cookies"] = input()
|
42 |
+
config["config"].append(user_data)
|
43 |
+
print("\n成功添加配置!")
|
44 |
+
again = True
|
45 |
+
|
46 |
+
elif choice == "2":
|
47 |
+
print("确定要删除所有配置吗? (y/n)")
|
48 |
+
if input().lower() == 'y':
|
49 |
+
config["config"] = []
|
50 |
+
print("已删除所有配置")
|
51 |
+
again = True
|
52 |
+
|
53 |
+
elif choice == "3":
|
54 |
+
print(f"输入新密码(留空则删除密码): ")
|
55 |
+
password = input()
|
56 |
+
with open("password.txt", "w") as f:
|
57 |
+
if password != "":
|
58 |
+
f.write(hashlib.sha256(password.encode()).hexdigest())
|
59 |
+
print(f"密码已设置")
|
60 |
+
else:
|
61 |
+
f.write("")
|
62 |
+
print(f"密码已删除")
|
63 |
+
|
64 |
+
elif choice == "4":
|
65 |
+
with open("config.json", "w") as f:
|
66 |
+
json.dump(config, f, indent=4)
|
67 |
+
print("配置已保存")
|
68 |
+
break
|
69 |
+
|
70 |
+
else:
|
71 |
+
print(f"无效的选择")
|
72 |
+
again = False
|
templates/dashboard.html
CHANGED
@@ -3,7 +3,7 @@
|
|
3 |
<head>
|
4 |
<meta charset="UTF-8">
|
5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
-
<title>Abacus Chat
|
7 |
<style>
|
8 |
:root {
|
9 |
--primary-color: #6f42c1;
|
@@ -504,6 +504,64 @@
|
|
504 |
grid-template-columns: 1fr;
|
505 |
}
|
506 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
507 |
</style>
|
508 |
</head>
|
509 |
<body>
|
@@ -512,7 +570,7 @@
|
|
512 |
<nav class="navbar">
|
513 |
<a href="/" class="navbar-brand">
|
514 |
<span class="navbar-logo">🤖</span>
|
515 |
-
<span class="navbar-title">Abacus Chat
|
516 |
</a>
|
517 |
<div class="navbar-actions">
|
518 |
<a href="/logout" class="btn-logout">
|
@@ -526,32 +584,40 @@
|
|
526 |
<div class="card">
|
527 |
<div class="card-header">
|
528 |
<h2 class="card-title">
|
529 |
-
<
|
530 |
-
|
531 |
</h2>
|
532 |
</div>
|
533 |
-
<div
|
534 |
-
<
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
<
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
<
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
<
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
<
|
551 |
-
|
552 |
-
{
|
553 |
-
|
554 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
555 |
</div>
|
556 |
</div>
|
557 |
</div>
|
@@ -611,7 +677,7 @@
|
|
611 |
<span class="status-value token-count">{{ total_tokens.completion|int }}</span>
|
612 |
</div>
|
613 |
<div class="token-note">
|
614 |
-
<small>*
|
615 |
</div>
|
616 |
<div class="table-container">
|
617 |
<table class="data-table token-model-table">
|
@@ -788,7 +854,7 @@
|
|
788 |
</div>
|
789 |
|
790 |
<div class="footer">
|
791 |
-
<p>© {{ year }} Abacus Chat
|
792 |
</div>
|
793 |
</div>
|
794 |
|
@@ -814,6 +880,41 @@
|
|
814 |
// 初始化隐藏回到顶部按钮
|
815 |
document.querySelector('.float-btn').style.opacity = '0';
|
816 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
817 |
// 模型统计折叠功能
|
818 |
const toggleBtn = document.getElementById('toggleModelStats');
|
819 |
const hiddenModels = document.querySelectorAll('.hidden-model');
|
|
|
3 |
<head>
|
4 |
<meta charset="UTF-8">
|
5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
+
<title>Abacus Chat Service - 仪表盘</title>
|
7 |
<style>
|
8 |
:root {
|
9 |
--primary-color: #6f42c1;
|
|
|
504 |
grid-template-columns: 1fr;
|
505 |
}
|
506 |
}
|
507 |
+
|
508 |
+
/* 开关样式 */
|
509 |
+
.switch {
|
510 |
+
position: relative;
|
511 |
+
display: inline-block;
|
512 |
+
width: 50px;
|
513 |
+
height: 24px;
|
514 |
+
margin-right: 10px;
|
515 |
+
vertical-align: middle;
|
516 |
+
}
|
517 |
+
|
518 |
+
.switch input {
|
519 |
+
opacity: 0;
|
520 |
+
width: 0;
|
521 |
+
height: 0;
|
522 |
+
}
|
523 |
+
|
524 |
+
.slider {
|
525 |
+
position: absolute;
|
526 |
+
cursor: pointer;
|
527 |
+
top: 0;
|
528 |
+
left: 0;
|
529 |
+
right: 0;
|
530 |
+
bottom: 0;
|
531 |
+
background-color: rgba(255, 255, 255, 0.2);
|
532 |
+
transition: .4s;
|
533 |
+
}
|
534 |
+
|
535 |
+
.slider:before {
|
536 |
+
position: absolute;
|
537 |
+
content: "";
|
538 |
+
height: 18px;
|
539 |
+
width: 18px;
|
540 |
+
left: 3px;
|
541 |
+
bottom: 3px;
|
542 |
+
background-color: white;
|
543 |
+
transition: .4s;
|
544 |
+
}
|
545 |
+
|
546 |
+
input:checked + .slider {
|
547 |
+
background-color: var(--primary-color);
|
548 |
+
}
|
549 |
+
|
550 |
+
input:focus + .slider {
|
551 |
+
box-shadow: 0 0 1px var(--primary-color);
|
552 |
+
}
|
553 |
+
|
554 |
+
input:checked + .slider:before {
|
555 |
+
transform: translateX(26px);
|
556 |
+
}
|
557 |
+
|
558 |
+
.slider.round {
|
559 |
+
border-radius: 24px;
|
560 |
+
}
|
561 |
+
|
562 |
+
.slider.round:before {
|
563 |
+
border-radius: 50%;
|
564 |
+
}
|
565 |
</style>
|
566 |
</head>
|
567 |
<body>
|
|
|
570 |
<nav class="navbar">
|
571 |
<a href="/" class="navbar-brand">
|
572 |
<span class="navbar-logo">🤖</span>
|
573 |
+
<span class="navbar-title">Abacus Chat Service</span>
|
574 |
</a>
|
575 |
<div class="navbar-actions">
|
576 |
<a href="/logout" class="btn-logout">
|
|
|
584 |
<div class="card">
|
585 |
<div class="card-header">
|
586 |
<h2 class="card-title">
|
587 |
+
<div class="card-icon">🖥️</div>
|
588 |
+
系统信息
|
589 |
</h2>
|
590 |
</div>
|
591 |
+
<div>
|
592 |
+
<div class="status-item">
|
593 |
+
<div class="status-label">系统状态</div>
|
594 |
+
<div class="status-value success">运行中</div>
|
595 |
+
</div>
|
596 |
+
<div class="status-item">
|
597 |
+
<div class="status-label">运行时间</div>
|
598 |
+
<div class="status-value">{{ uptime }}</div>
|
599 |
+
</div>
|
600 |
+
<div class="status-item">
|
601 |
+
<div class="status-label">健康检查次数</div>
|
602 |
+
<div class="status-value">{{ health_checks }}</div>
|
603 |
+
</div>
|
604 |
+
<div class="status-item">
|
605 |
+
<div class="status-label">已配置用户数量</div>
|
606 |
+
<div class="status-value">{{ user_count }}</div>
|
607 |
+
</div>
|
608 |
+
<div class="status-item">
|
609 |
+
<div class="status-label">可用模型数量</div>
|
610 |
+
<div class="status-value">{{ models|length }}</div>
|
611 |
+
</div>
|
612 |
+
<div class="status-item">
|
613 |
+
<div class="status-label">自动删除对话</div>
|
614 |
+
<div class="status-value">
|
615 |
+
<label class="switch">
|
616 |
+
<input type="checkbox" id="deleteChat" {% if delete_chat %}checked{% endif %}>
|
617 |
+
<span class="slider round"></span>
|
618 |
+
</label>
|
619 |
+
<span id="deleteChatStatus">{{ "已启用" if delete_chat else "已禁用" }}</span>
|
620 |
+
</div>
|
621 |
</div>
|
622 |
</div>
|
623 |
</div>
|
|
|
677 |
<span class="status-value token-count">{{ total_tokens.completion|int }}</span>
|
678 |
</div>
|
679 |
<div class="token-note">
|
680 |
+
<small>* 以上数据仅统计通过本服务使用的token数量,不包含在Abacus官网直接使用的token。数值为粗略估计,可能与实际计费有差异。</small>
|
681 |
</div>
|
682 |
<div class="table-container">
|
683 |
<table class="data-table token-model-table">
|
|
|
854 |
</div>
|
855 |
|
856 |
<div class="footer">
|
857 |
+
<p>© {{ year }} Abacus Chat Service. 保持简单,保持可靠。</p>
|
858 |
</div>
|
859 |
</div>
|
860 |
|
|
|
880 |
// 初始化隐藏回到顶部按钮
|
881 |
document.querySelector('.float-btn').style.opacity = '0';
|
882 |
|
883 |
+
// 自动删除对话开关功能
|
884 |
+
const deleteChatSwitch = document.getElementById('deleteChat');
|
885 |
+
const deleteChatStatus = document.getElementById('deleteChatStatus');
|
886 |
+
|
887 |
+
if (deleteChatSwitch) {
|
888 |
+
deleteChatSwitch.addEventListener('change', () => {
|
889 |
+
// 发送API请求更新设置
|
890 |
+
fetch('/api/set_delete_chat', {
|
891 |
+
method: 'POST',
|
892 |
+
headers: {
|
893 |
+
'Content-Type': 'application/json'
|
894 |
+
},
|
895 |
+
body: JSON.stringify({
|
896 |
+
delete_chat: deleteChatSwitch.checked
|
897 |
+
})
|
898 |
+
})
|
899 |
+
.then(response => response.json())
|
900 |
+
.then(data => {
|
901 |
+
if (data.success) {
|
902 |
+
deleteChatStatus.textContent = deleteChatSwitch.checked ? '已启用' : '已禁用';
|
903 |
+
} else {
|
904 |
+
alert('更新设置失败: ' + (data.error || '未知错误'));
|
905 |
+
// 回滚UI状态
|
906 |
+
deleteChatSwitch.checked = !deleteChatSwitch.checked;
|
907 |
+
}
|
908 |
+
})
|
909 |
+
.catch(error => {
|
910 |
+
console.error('更新设置出错:', error);
|
911 |
+
alert('更新设置出错,请查看控制台');
|
912 |
+
// 回滚UI状态
|
913 |
+
deleteChatSwitch.checked = !deleteChatSwitch.checked;
|
914 |
+
});
|
915 |
+
});
|
916 |
+
}
|
917 |
+
|
918 |
// 模型统计折叠功能
|
919 |
const toggleBtn = document.getElementById('toggleModelStats');
|
920 |
const hiddenModels = document.querySelectorAll('.hidden-model');
|