malt666 commited on
Commit
33d4059
·
verified ·
1 Parent(s): 049292d

Upload 7 files

Browse files
Files changed (4) hide show
  1. README .md +119 -0
  2. app.py +199 -112
  3. config_editor.py +72 -0
  4. 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
+ [![Deploy to Hugging Face Spaces](https://huggingface.co/datasets/huggingface/badges/raw/main/deploy-to-spaces-lg.svg)](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 resolve_config():
140
- # 从环境变量读取多组配置
141
- config_list = []
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
- with open("config.json", "r") as f:
 
163
  config = json.load(f)
164
- config_list = config.get("config")
165
- return config_list
166
- except FileNotFoundError:
167
- print("未找到config.json文件")
168
- return []
169
- except json.JSONDecodeError:
170
- print("config.json格式错误")
171
- return []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
 
173
 
174
  def get_password():
@@ -248,11 +232,10 @@ def refresh_token(session, cookies):
248
  }
249
 
250
  try:
251
- response = session.post(
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 = session.post(
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 = session.post(
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
- response = session.post(
 
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
- # 如果conversation_id为None或为空字符串,直接创建新会话
570
- if not conversation_id:
571
- print("会话ID为空,将创建新会话")
572
- need_create = True
573
- else:
574
- # 检查现有会话ID是否有效
575
- need_create = not is_conversation_valid(session, cookies, session_token, conversation_id, model_map, model)
576
- if need_create:
577
- print(f"会话ID {conversation_id} 无效,将创建新会话")
578
-
579
- # 如果需要创建新会话
580
- if need_create:
581
- if model in model_map and len(model_map[model]) >= 2:
582
- external_app_id = model_map[model][0]
583
- # 创建会话时需要deployment_id,我们先使用一个固定值
584
- # 在实际应用中应从API响应中获取
585
- deployment_id = "14b2a314cc" # 这是从您提供的请求中获取的
 
 
 
586
 
587
- new_conversation_id = create_conversation(
588
- session, cookies, session_token,
589
- external_application_id=external_app_id,
590
- deployment_id=deployment_id
591
- )
592
 
593
- if new_conversation_id:
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
- # 如果无法创建,返回原始ID
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
- # 确保有有效的会话ID
620
- conversation_id = get_or_create_conversation(session, cookies, session_token, conversation_id, model_map, model, user_index)
 
 
 
 
 
 
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": conversation_id,
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 = session.post(
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
- # 确保有有效的会话ID
751
- conversation_id = get_or_create_conversation(session, cookies, session_token, conversation_id, model_map, model, user_index)
 
 
 
 
 
 
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": conversation_id,
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 = session.post(
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
- requests.get("http://127.0.0.1:7860/health")
 
 
 
983
  time.sleep(1200) # 20分钟
984
  except:
985
- pass # 忽略错误,保持运行
 
986
 
987
 
988
  @app.route("/", methods=["GET"])
@@ -1096,7 +1141,7 @@ def get_compute_points():
1096
  "cookie": cookies
1097
  }
1098
 
1099
- response = session.get(
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 = session.post(
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 Proxy - 仪表盘</title>
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 Proxy</span>
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
- <span class="card-icon">📊</span>
530
- 系统状态
531
  </h2>
532
  </div>
533
- <div class="status-item">
534
- <span class="status-label">服务状态</span>
535
- <span class="status-value success">运行中</span>
536
- </div>
537
- <div class="status-item">
538
- <span class="status-label">运行时间</span>
539
- <span class="status-value">{{ uptime }}</span>
540
- </div>
541
- <div class="status-item">
542
- <span class="status-label">健康检查次数</span>
543
- <span class="status-value">{{ health_checks }}</span>
544
- </div>
545
- <div class="status-item">
546
- <span class="status-label">已配置用户数</span>
547
- <span class="status-value">{{ user_count }}</span>
548
- </div>
549
- <div class="status-item">
550
- <span class="status-label">可用模型</span>
551
- <div class="models-list">
552
- {% for model in models %}
553
- <span class="model-tag">{{ model }}</span>
554
- {% endfor %}
 
 
 
 
 
 
 
 
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>* 以上数据仅统计通过本代理使用的token数量,不包含在Abacus官网直接使用的token。数值为粗略估计,可能与实际计费有差异。</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 Proxy. 保持简单,保持可靠。</p>
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');