yxmiler commited on
Commit
f6c6cd4
·
verified ·
1 Parent(s): d517a37

Upload 2 files

Browse files
Files changed (2) hide show
  1. Dockerfile +12 -0
  2. app.py +1113 -0
Dockerfile ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+
3
+ WORKDIR /app
4
+
5
+ RUN pip install --no-cache-dir flask requests curl_cffi werkzeug loguru
6
+
7
+ COPY . .
8
+
9
+ ENV PORT=5200
10
+ EXPOSE 5200
11
+
12
+ CMD ["python", "app.py"]
app.py ADDED
@@ -0,0 +1,1113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import uuid
4
+ import time
5
+ import base64
6
+ import sys
7
+ import inspect
8
+ from loguru import logger
9
+
10
+ import requests
11
+ from flask import Flask, request, Response, jsonify, stream_with_context
12
+ from curl_cffi import requests as curl_requests
13
+ from werkzeug.middleware.proxy_fix import ProxyFix
14
+
15
+
16
+ class Logger:
17
+ def __init__(self, level="INFO", colorize=True, format=None):
18
+ logger.remove()
19
+
20
+ if format is None:
21
+ format = (
22
+ "<green>{time:YYYY-MM-DD HH:mm:ss}</green> | "
23
+ "<level>{level: <8}</level> | "
24
+ "<cyan>{extra[filename]}</cyan>:<cyan>{extra[function]}</cyan>:<cyan>{extra[lineno]}</cyan> | "
25
+ "<level>{message}</level>"
26
+ )
27
+
28
+ logger.add(
29
+ sys.stderr,
30
+ level=level,
31
+ format=format,
32
+ colorize=colorize,
33
+ backtrace=True,
34
+ diagnose=True
35
+ )
36
+
37
+ self.logger = logger
38
+
39
+ def _get_caller_info(self):
40
+ frame = inspect.currentframe()
41
+ try:
42
+ caller_frame = frame.f_back.f_back
43
+ full_path = caller_frame.f_code.co_filename
44
+ function = caller_frame.f_code.co_name
45
+ lineno = caller_frame.f_lineno
46
+
47
+ filename = os.path.basename(full_path)
48
+
49
+ return {
50
+ 'filename': filename,
51
+ 'function': function,
52
+ 'lineno': lineno
53
+ }
54
+ finally:
55
+ del frame
56
+
57
+ def info(self, message, source="API"):
58
+ caller_info = self._get_caller_info()
59
+ self.logger.bind(**caller_info).info(f"[{source}] {message}")
60
+
61
+ def error(self, message, source="API"):
62
+ caller_info = self._get_caller_info()
63
+
64
+ if isinstance(message, Exception):
65
+ self.logger.bind(**caller_info).exception(f"[{source}] {str(message)}")
66
+ else:
67
+ self.logger.bind(**caller_info).error(f"[{source}] {message}")
68
+
69
+ def warning(self, message, source="API"):
70
+ caller_info = self._get_caller_info()
71
+ self.logger.bind(**caller_info).warning(f"[{source}] {message}")
72
+
73
+ def debug(self, message, source="API"):
74
+ caller_info = self._get_caller_info()
75
+ self.logger.bind(**caller_info).debug(f"[{source}] {message}")
76
+
77
+ async def request_logger(self, request):
78
+ caller_info = self._get_caller_info()
79
+ self.logger.bind(**caller_info).info(f"请求: {request.method} {request.path}", "Request")
80
+
81
+ logger = Logger(level="INFO")
82
+
83
+
84
+ CONFIG = {
85
+ "MODELS": {
86
+ 'grok-2': 'grok-latest',
87
+ 'grok-2-imageGen': 'grok-latest',
88
+ 'grok-2-search': 'grok-latest',
89
+ "grok-3": "grok-3",
90
+ "grok-3-search": "grok-3",
91
+ "grok-3-imageGen": "grok-3",
92
+ "grok-3-deepsearch": "grok-3",
93
+ "grok-3-reasoning": "grok-3"
94
+ },
95
+ "API": {
96
+ "IS_TEMP_CONVERSATION": os.environ.get("IS_TEMP_CONVERSATION", "true").lower() == "true",
97
+ "IS_CUSTOM_SSO": os.environ.get("IS_CUSTOM_SSO", "false").lower() == "true",
98
+ "BASE_URL": "https://grok.com",
99
+ "API_KEY": os.environ.get("API_KEY", "sk-123456"),
100
+ "SIGNATURE_COOKIE": None,
101
+ "PICGO_KEY": os.environ.get("PICGO_KEY") or None,
102
+ "TUMY_KEY": os.environ.get("TUMY_KEY") or None,
103
+ "RETRY_TIME": 1000,
104
+ "PROXY": os.environ.get("PROXY") or None
105
+ },
106
+ "SERVER": {
107
+ "PORT": int(os.environ.get("PORT", 5200))
108
+ },
109
+ "RETRY": {
110
+ "MAX_ATTEMPTS": 2
111
+ },
112
+ "SHOW_THINKING": os.environ.get("SHOW_THINKING") == "true",
113
+ "IS_THINKING": False,
114
+ "IS_IMG_GEN": False,
115
+ "IS_IMG_GEN2": False,
116
+ "ISSHOW_SEARCH_RESULTS": os.environ.get("ISSHOW_SEARCH_RESULTS", "true").lower() == "true"
117
+ }
118
+
119
+
120
+ DEFAULT_HEADERS = {
121
+ 'Accept': '*/*',
122
+ 'Accept-Language': 'zh-CN,zh;q=0.9',
123
+ 'Accept-Encoding': 'gzip, deflate, br, zstd',
124
+ 'Content-Type': 'text/plain;charset=UTF-8',
125
+ 'Connection': 'keep-alive',
126
+ 'Origin': 'https://grok.com',
127
+ 'Priority': 'u=1, i',
128
+ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36',
129
+ 'Sec-Ch-Ua': '"Not(A:Brand";v="99", "Google Chrome";v="133", "Chromium";v="133"',
130
+ 'Sec-Ch-Ua-Mobile': '?0',
131
+ 'Sec-Ch-Ua-Platform': '"macOS"',
132
+ 'Sec-Fetch-Dest': 'empty',
133
+ 'Sec-Fetch-Mode': 'cors',
134
+ 'Sec-Fetch-Site': 'same-origin',
135
+ 'Baggage': 'sentry-public_key=b311e0f2690c81f25e2c4cf6d4f7ce1c'
136
+ }
137
+
138
+ class AuthTokenManager:
139
+ def __init__(self):
140
+ self.token_model_map = {}
141
+ self.expired_tokens = set()
142
+ self.token_status_map = {}
143
+
144
+ self.model_config = {
145
+ "grok-2": {
146
+ "RequestFrequency": 30,
147
+ "ExpirationTime": 1 * 60 * 60 * 1000 # 1小时
148
+ },
149
+ "grok-3": {
150
+ "RequestFrequency": 20,
151
+ "ExpirationTime": 2 * 60 * 60 * 1000 # 2小时
152
+ },
153
+ "grok-3-deepsearch": {
154
+ "RequestFrequency": 10,
155
+ "ExpirationTime": 24 * 60 * 60 * 1000 # 24小时
156
+ },
157
+ "grok-3-reasoning": {
158
+ "RequestFrequency": 10,
159
+ "ExpirationTime": 24 * 60 * 60 * 1000 # 24小时
160
+ }
161
+ }
162
+ self.token_reset_switch = False
163
+ self.token_reset_timer = None
164
+
165
+ def add_token(self, token):
166
+ sso = token.split("sso=")[1].split(";")[0]
167
+ for model in self.model_config.keys():
168
+ if model not in self.token_model_map:
169
+ self.token_model_map[model] = []
170
+ if sso not in self.token_status_map:
171
+ self.token_status_map[sso] = {}
172
+
173
+ existing_token_entry = next((entry for entry in self.token_model_map[model] if entry["token"] == token), None)
174
+
175
+ if not existing_token_entry:
176
+ self.token_model_map[model].append({
177
+ "token": token,
178
+ "RequestCount": 0,
179
+ "AddedTime": int(time.time() * 1000),
180
+ "StartCallTime": None
181
+ })
182
+
183
+ if model not in self.token_status_map[sso]:
184
+ self.token_status_map[sso][model] = {
185
+ "isValid": True,
186
+ "invalidatedTime": None,
187
+ "totalRequestCount": 0
188
+ }
189
+
190
+ def set_token(self, token):
191
+ models = list(self.model_config.keys())
192
+ self.token_model_map = {model: [{
193
+ "token": token,
194
+ "RequestCount": 0,
195
+ "AddedTime": int(time.time() * 1000),
196
+ "StartCallTime": None
197
+ }] for model in models}
198
+
199
+ sso = token.split("sso=")[1].split(";")[0]
200
+ self.token_status_map[sso] = {model: {
201
+ "isValid": True,
202
+ "invalidatedTime": None,
203
+ "totalRequestCount": 0
204
+ } for model in models}
205
+
206
+ def delete_token(self, token):
207
+ try:
208
+ sso = token.split("sso=")[1].split(";")[0]
209
+ for model in self.token_model_map:
210
+ self.token_model_map[model] = [entry for entry in self.token_model_map[model] if entry["token"] != token]
211
+
212
+ if sso in self.token_status_map:
213
+ del self.token_status_map[sso]
214
+
215
+ logger.info(f"令牌已成功移除: {token}", "TokenManager")
216
+ return True
217
+ except Exception as error:
218
+ logger.error(f"令牌删除失败: {str(error)}")
219
+ return False
220
+ def reduce_token_request_count(self, model_id, count):
221
+ try:
222
+ normalized_model = self.normalize_model_name(model_id)
223
+
224
+ if normalized_model not in self.token_model_map:
225
+ logger.error(f"模型 {normalized_model} 不存在", "TokenManager")
226
+ return False
227
+
228
+ if not self.token_model_map[normalized_model]:
229
+ logger.error(f"模型 {normalized_model} 没有可用的token", "TokenManager")
230
+ return False
231
+
232
+ token_entry = self.token_model_map[normalized_model][0]
233
+
234
+ # 确保RequestCount不会小于0
235
+ new_count = max(0, token_entry["RequestCount"] - count)
236
+ reduction = token_entry["RequestCount"] - new_count
237
+
238
+ token_entry["RequestCount"] = new_count
239
+
240
+ # 更新token状态
241
+ if token_entry["token"]:
242
+ sso = token_entry["token"].split("sso=")[1].split(";")[0]
243
+ if sso in self.token_status_map and normalized_model in self.token_status_map[sso]:
244
+ self.token_status_map[sso][normalized_model]["totalRequestCount"] = max(
245
+ 0,
246
+ self.token_status_map[sso][normalized_model]["totalRequestCount"] - reduction
247
+ )
248
+ return True
249
+
250
+ except Exception as error:
251
+ logger.error(f"重置校对token请求次数时发生错误: {str(error)}", "TokenManager")
252
+ return False
253
+ def get_next_token_for_model(self, model_id):
254
+ normalized_model = self.normalize_model_name(model_id)
255
+
256
+ if normalized_model not in self.token_model_map or not self.token_model_map[normalized_model]:
257
+ return None
258
+
259
+ token_entry = self.token_model_map[normalized_model][0]
260
+
261
+ if token_entry:
262
+ if token_entry["StartCallTime"] is None:
263
+ token_entry["StartCallTime"] = int(time.time() * 1000)
264
+
265
+ if not self.token_reset_switch:
266
+ self.start_token_reset_process()
267
+ self.token_reset_switch = True
268
+
269
+ token_entry["RequestCount"] += 1
270
+
271
+ if token_entry["RequestCount"] > self.model_config[normalized_model]["RequestFrequency"]:
272
+ self.remove_token_from_model(normalized_model, token_entry["token"])
273
+ next_token_entry = self.token_model_map[normalized_model][0] if self.token_model_map[normalized_model] else None
274
+ return next_token_entry["token"] if next_token_entry else None
275
+
276
+ sso = token_entry["token"].split("sso=")[1].split(";")[0]
277
+ if sso in self.token_status_map and normalized_model in self.token_status_map[sso]:
278
+ if token_entry["RequestCount"] == self.model_config[normalized_model]["RequestFrequency"]:
279
+ self.token_status_map[sso][normalized_model]["isValid"] = False
280
+ self.token_status_map[sso][normalized_model]["invalidatedTime"] = int(time.time() * 1000)
281
+ self.token_status_map[sso][normalized_model]["totalRequestCount"] += 1
282
+
283
+ return token_entry["token"]
284
+
285
+ return None
286
+
287
+ def remove_token_from_model(self, model_id, token):
288
+ normalized_model = self.normalize_model_name(model_id)
289
+
290
+ if normalized_model not in self.token_model_map:
291
+ logger.error(f"模型 {normalized_model} 不存在", "TokenManager")
292
+ return False
293
+
294
+ model_tokens = self.token_model_map[normalized_model]
295
+ token_index = next((i for i, entry in enumerate(model_tokens) if entry["token"] == token), -1)
296
+
297
+ if token_index != -1:
298
+ removed_token_entry = model_tokens.pop(token_index)
299
+ self.expired_tokens.add((
300
+ removed_token_entry["token"],
301
+ normalized_model,
302
+ int(time.time() * 1000)
303
+ ))
304
+
305
+ if not self.token_reset_switch:
306
+ self.start_token_reset_process()
307
+ self.token_reset_switch = True
308
+
309
+ logger.info(f"模型{model_id}的令牌已失效,已成功移除令牌: {token}", "TokenManager")
310
+ return True
311
+
312
+ logger.error(f"在模型 {normalized_model} 中未找到 token: {token}", "TokenManager")
313
+ return False
314
+
315
+ def get_expired_tokens(self):
316
+ return list(self.expired_tokens)
317
+
318
+ def normalize_model_name(self, model):
319
+ if model.startswith('grok-') and 'deepsearch' not in model and 'reasoning' not in model:
320
+ return '-'.join(model.split('-')[:2])
321
+ return model
322
+
323
+ def get_token_count_for_model(self, model_id):
324
+ normalized_model = self.normalize_model_name(model_id)
325
+ return len(self.token_model_map.get(normalized_model, []))
326
+
327
+ def get_remaining_token_request_capacity(self):
328
+ remaining_capacity_map = {}
329
+
330
+ for model in self.model_config.keys():
331
+ model_tokens = self.token_model_map.get(model, [])
332
+ model_request_frequency = self.model_config[model]["RequestFrequency"]
333
+
334
+ total_used_requests = sum(token_entry.get("RequestCount", 0) for token_entry in model_tokens)
335
+
336
+ remaining_capacity = (len(model_tokens) * model_request_frequency) - total_used_requests
337
+ remaining_capacity_map[model] = max(0, remaining_capacity)
338
+
339
+ return remaining_capacity_map
340
+
341
+ def get_token_array_for_model(self, model_id):
342
+ normalized_model = self.normalize_model_name(model_id)
343
+ return self.token_model_map.get(normalized_model, [])
344
+
345
+ def start_token_reset_process(self):
346
+ def reset_expired_tokens():
347
+ now = int(time.time() * 1000)
348
+
349
+ tokens_to_remove = set()
350
+ for token_info in self.expired_tokens:
351
+ token, model, expired_time = token_info
352
+ expiration_time = self.model_config[model]["ExpirationTime"]
353
+
354
+ if now - expired_time >= expiration_time:
355
+ if not any(entry["token"] == token for entry in self.token_model_map.get(model, [])):
356
+ if model not in self.token_model_map:
357
+ self.token_model_map[model] = []
358
+
359
+ self.token_model_map[model].append({
360
+ "token": token,
361
+ "RequestCount": 0,
362
+ "AddedTime": now,
363
+ "StartCallTime": None
364
+ })
365
+
366
+ sso = token.split("sso=")[1].split(";")[0]
367
+ if sso in self.token_status_map and model in self.token_status_map[sso]:
368
+ self.token_status_map[sso][model]["isValid"] = True
369
+ self.token_status_map[sso][model]["invalidatedTime"] = None
370
+ self.token_status_map[sso][model]["totalRequestCount"] = 0
371
+
372
+ tokens_to_remove.add(token_info)
373
+
374
+ self.expired_tokens -= tokens_to_remove
375
+
376
+ for model in self.model_config.keys():
377
+ if model not in self.token_model_map:
378
+ continue
379
+
380
+ for token_entry in self.token_model_map[model]:
381
+ if not token_entry.get("StartCallTime"):
382
+ continue
383
+
384
+ expiration_time = self.model_config[model]["ExpirationTime"]
385
+ if now - token_entry["StartCallTime"] >= expiration_time:
386
+ sso = token_entry["token"].split("sso=")[1].split(";")[0]
387
+ if sso in self.token_status_map and model in self.token_status_map[sso]:
388
+ self.token_status_map[sso][model]["isValid"] = True
389
+ self.token_status_map[sso][model]["invalidatedTime"] = None
390
+ self.token_status_map[sso][model]["totalRequestCount"] = 0
391
+
392
+ token_entry["RequestCount"] = 0
393
+ token_entry["StartCallTime"] = None
394
+
395
+ import threading
396
+ # 启动一个线程执行定时任务,每小时执行一次
397
+ def run_timer():
398
+ while True:
399
+ reset_expired_tokens()
400
+ time.sleep(3600)
401
+
402
+ timer_thread = threading.Thread(target=run_timer)
403
+ timer_thread.daemon = True
404
+ timer_thread.start()
405
+
406
+ def get_all_tokens(self):
407
+ all_tokens = set()
408
+ for model_tokens in self.token_model_map.values():
409
+ for entry in model_tokens:
410
+ all_tokens.add(entry["token"])
411
+ return list(all_tokens)
412
+
413
+ def get_token_status_map(self):
414
+ return self.token_status_map
415
+
416
+ class Utils:
417
+ @staticmethod
418
+ def organize_search_results(search_results):
419
+ if not search_results or 'results' not in search_results:
420
+ return ''
421
+
422
+ results = search_results['results']
423
+ formatted_results = []
424
+
425
+ for index, result in enumerate(results):
426
+ title = result.get('title', '未知标题')
427
+ url = result.get('url', '#')
428
+ preview = result.get('preview', '无预览内容')
429
+
430
+ formatted_result = f"\r\n<details><summary>资料[{index}]: {title}</summary>\r\n{preview}\r\n\n[Link]({url})\r\n</details>"
431
+ formatted_results.append(formatted_result)
432
+
433
+ return '\n\n'.join(formatted_results)
434
+
435
+ @staticmethod
436
+ def create_auth_headers(model):
437
+ return token_manager.get_next_token_for_model(model)
438
+
439
+ @staticmethod
440
+ def get_proxy_options():
441
+ proxy = CONFIG["API"]["PROXY"]
442
+ proxy_options = {}
443
+
444
+ if proxy:
445
+ logger.info(f"使用代理: {proxy}", "Server")
446
+ if proxy.startswith("socks5://"):
447
+ proxy_options["proxy"] = proxy
448
+
449
+ if '@' in proxy:
450
+ auth_part = proxy.split('@')[0].split('://')[1]
451
+ if ':' in auth_part:
452
+ username, password = auth_part.split(':')
453
+ proxy_options["proxy_auth"] = (username, password)
454
+ else:
455
+ proxy_options["proxies"] = {"https": proxy, "http": proxy}
456
+ print(proxy_options)
457
+ return proxy_options
458
+
459
+ class GrokApiClient:
460
+ def __init__(self, model_id):
461
+ if model_id not in CONFIG["MODELS"]:
462
+ raise ValueError(f"不支持的模型: {model_id}")
463
+ self.model_id = CONFIG["MODELS"][model_id]
464
+
465
+ def process_message_content(self, content):
466
+ if isinstance(content, str):
467
+ return content
468
+ return None
469
+
470
+ def get_image_type(self, base64_string):
471
+ mime_type = 'image/jpeg'
472
+ if 'data:image' in base64_string:
473
+ import re
474
+ matches = re.search(r'data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+);base64,', base64_string)
475
+ if matches:
476
+ mime_type = matches.group(1)
477
+
478
+ extension = mime_type.split('/')[1]
479
+ file_name = f"image.{extension}"
480
+
481
+ return {
482
+ "mimeType": mime_type,
483
+ "fileName": file_name
484
+ }
485
+
486
+ def upload_base64_image(self, base64_data, url):
487
+ try:
488
+ if 'data:image' in base64_data:
489
+ image_buffer = base64_data.split(',')[1]
490
+ else:
491
+ image_buffer = base64_data
492
+
493
+ image_info = self.get_image_type(base64_data)
494
+ mime_type = image_info["mimeType"]
495
+ file_name = image_info["fileName"]
496
+
497
+ upload_data = {
498
+ "rpc": "uploadFile",
499
+ "req": {
500
+ "fileName": file_name,
501
+ "fileMimeType": mime_type,
502
+ "content": image_buffer
503
+ }
504
+ }
505
+
506
+ logger.info("发送图片请求", "Server")
507
+
508
+ proxy_options = Utils.get_proxy_options()
509
+ response = curl_requests.post(
510
+ url,
511
+ headers={
512
+ **DEFAULT_HEADERS,
513
+ "Cookie": CONFIG["API"]["SIGNATURE_COOKIE"]
514
+ },
515
+ json=upload_data,
516
+ impersonate="chrome133a",
517
+ **proxy_options
518
+ )
519
+
520
+ if response.status_code != 200:
521
+ logger.error(f"上传图片失败,状态码:{response.status_code}", "Server")
522
+ return ''
523
+
524
+ result = response.json()
525
+ logger.info(f"上传图片成功: {result}", "Server")
526
+ return result.get("fileMetadataId", "")
527
+
528
+ except Exception as error:
529
+ logger.error(str(error), "Server")
530
+ return ''
531
+
532
+ def prepare_chat_request(self, request):
533
+ if ((request["model"] == 'grok-2-imageGen' or request["model"] == 'grok-3-imageGen') and
534
+ not CONFIG["API"]["PICGO_KEY"] and not CONFIG["API"]["TUMY_KEY"] and
535
+ request.get("stream", False)):
536
+ raise ValueError("该模型流式输出需要配置PICGO或者TUMY图床密钥!")
537
+
538
+ todo_messages = request["messages"]
539
+ if request["model"] in ['grok-2-imageGen', 'grok-3-imageGen', 'grok-3-deepsearch']:
540
+ last_message = todo_messages[-1]
541
+ if last_message["role"] != 'user':
542
+ raise ValueError('此模型最后一条消息必须是用户消息!')
543
+ todo_messages = [last_message]
544
+
545
+ file_attachments = []
546
+ messages = ''
547
+ last_role = None
548
+ last_content = ''
549
+ search = request["model"] in ['grok-2-search', 'grok-3-search']
550
+
551
+ # 移除<think>标签及其内容和base64图片
552
+ def remove_think_tags(text):
553
+ import re
554
+ text = re.sub(r'<think>[\s\S]*?<\/think>', '', text).strip()
555
+ text = re.sub(r'!\[image\]\(data:.*?base64,.*?\)', '[图片]', text)
556
+ return text
557
+
558
+ def process_content(content):
559
+ if isinstance(content, list):
560
+ text_content = ''
561
+ for item in content:
562
+ if item["type"] == 'image_url':
563
+ text_content += ("[图片]" if not text_content else '\n[图片]')
564
+ elif item["type"] == 'text':
565
+ text_content += (remove_think_tags(item["text"]) if not text_content else '\n' + remove_think_tags(item["text"]))
566
+ return text_content
567
+ elif isinstance(content, dict) and content is not None:
568
+ if content["type"] == 'image_url':
569
+ return "[图片]"
570
+ elif content["type"] == 'text':
571
+ return remove_think_tags(content["text"])
572
+ return remove_think_tags(self.process_message_content(content))
573
+
574
+ for current in todo_messages:
575
+ role = 'assistant' if current["role"] == 'assistant' else 'user'
576
+ is_last_message = current == todo_messages[-1]
577
+
578
+ if is_last_message and "content" in current:
579
+ if isinstance(current["content"], list):
580
+ for item in current["content"]:
581
+ if item["type"] == 'image_url':
582
+ processed_image = self.upload_base64_image(
583
+ item["image_url"]["url"],
584
+ f"{CONFIG['API']['BASE_URL']}/api/rpc"
585
+ )
586
+ if processed_image:
587
+ file_attachments.append(processed_image)
588
+ elif isinstance(current["content"], dict) and current["content"].get("type") == 'image_url':
589
+ processed_image = self.upload_base64_image(
590
+ current["content"]["image_url"]["url"],
591
+ f"{CONFIG['API']['BASE_URL']}/api/rpc"
592
+ )
593
+ if processed_image:
594
+ file_attachments.append(processed_image)
595
+
596
+
597
+ text_content = process_content(current.get("content", ""))
598
+
599
+ if text_content or (is_last_message and file_attachments):
600
+ if role == last_role and text_content:
601
+ last_content += '\n' + text_content
602
+ messages = messages[:messages.rindex(f"{role.upper()}: ")] + f"{role.upper()}: {last_content}\n"
603
+ else:
604
+ messages += f"{role.upper()}: {text_content or '[图片]'}\n"
605
+ last_content = text_content
606
+ last_role = role
607
+
608
+ return {
609
+ "temporary": CONFIG["API"].get("IS_TEMP_CONVERSATION", False),
610
+ "modelName": self.model_id,
611
+ "message": messages.strip(),
612
+ "fileAttachments": file_attachments[:4],
613
+ "imageAttachments": [],
614
+ "disableSearch": False,
615
+ "enableImageGeneration": True,
616
+ "returnImageBytes": False,
617
+ "returnRawGrokInXaiRequest": False,
618
+ "enableImageStreaming": False,
619
+ "imageGenerationCount": 1,
620
+ "forceConcise": False,
621
+ "toolOverrides": {
622
+ "imageGen": request["model"] in ['grok-2-imageGen', 'grok-3-imageGen'],
623
+ "webSearch": search,
624
+ "xSearch": search,
625
+ "xMediaSearch": search,
626
+ "trendsSearch": search,
627
+ "xPostAnalyze": search
628
+ },
629
+ "enableSideBySide": True,
630
+ "isPreset": False,
631
+ "sendFinalMetadata": True,
632
+ "customInstructions": "",
633
+ "deepsearchPreset": "default" if request["model"] == 'grok-3-deepsearch' else "",
634
+ "isReasoning": request["model"] == 'grok-3-reasoning'
635
+ }
636
+
637
+ class MessageProcessor:
638
+ @staticmethod
639
+ def create_chat_response(message, model, is_stream=False):
640
+ base_response = {
641
+ "id": f"chatcmpl-{uuid.uuid4()}",
642
+ "created": int(time.time()),
643
+ "model": model
644
+ }
645
+
646
+ if is_stream:
647
+ return {
648
+ **base_response,
649
+ "object": "chat.completion.chunk",
650
+ "choices": [{
651
+ "index": 0,
652
+ "delta": {
653
+ "content": message
654
+ }
655
+ }]
656
+ }
657
+
658
+ return {
659
+ **base_response,
660
+ "object": "chat.completion",
661
+ "choices": [{
662
+ "index": 0,
663
+ "message": {
664
+ "role": "assistant",
665
+ "content": message
666
+ },
667
+ "finish_reason": "stop"
668
+ }],
669
+ "usage": None
670
+ }
671
+
672
+ def process_model_response(response, model):
673
+ result = {"token": None, "imageUrl": None}
674
+
675
+ if CONFIG["IS_IMG_GEN"]:
676
+ if response.get("cachedImageGenerationResponse") and not CONFIG["IS_IMG_GEN2"]:
677
+ result["imageUrl"] = response["cachedImageGenerationResponse"]["imageUrl"]
678
+ return result
679
+
680
+ if model == 'grok-2':
681
+ result["token"] = response.get("token")
682
+ elif model in ['grok-2-search', 'grok-3-search']:
683
+ if response.get("webSearchResults") and CONFIG["ISSHOW_SEARCH_RESULTS"]:
684
+ result["token"] = f"\r\n<think>{Utils.organize_search_results(response['webSearchResults'])}</think>\r\n"
685
+ else:
686
+ result["token"] = response.get("token")
687
+ elif model == 'grok-3':
688
+ result["token"] = response.get("token")
689
+ elif model == 'grok-3-deepsearch':
690
+ if response.get("messageStepId") and not CONFIG["SHOW_THINKING"]:
691
+ return result
692
+ if response.get("messageStepId") and not CONFIG["IS_THINKING"]:
693
+ result["token"] = "<think>" + response.get("token", "")
694
+ CONFIG["IS_THINKING"] = True
695
+ elif not response.get("messageStepId") and CONFIG["IS_THINKING"] and response.get("messageTag") == "final":
696
+ result["token"] = "</think>" + response.get("token", "")
697
+ CONFIG["IS_THINKING"] = False
698
+ elif (response.get("messageStepId") and CONFIG["IS_THINKING"] and response.get("messageTag") == "assistant") or response.get("messageTag") == "final":
699
+ result["token"] = response.get("token")
700
+ elif model == 'grok-3-reasoning':
701
+ if response.get("isThinking") and not CONFIG["SHOW_THINKING"]:
702
+ return result
703
+
704
+ if response.get("isThinking") and not CONFIG["IS_THINKING"]:
705
+ result["token"] = "<think>" + response.get("token", "")
706
+ CONFIG["IS_THINKING"] = True
707
+ elif not response.get("isThinking") and CONFIG["IS_THINKING"]:
708
+ result["token"] = "</think>" + response.get("token", "")
709
+ CONFIG["IS_THINKING"] = False
710
+ else:
711
+ result["token"] = response.get("token")
712
+
713
+ return result
714
+
715
+ def handle_image_response(image_url):
716
+ max_retries = 2
717
+ retry_count = 0
718
+ image_base64_response = None
719
+
720
+ while retry_count < max_retries:
721
+ try:
722
+ proxy_options = Utils.get_proxy_options()
723
+ image_base64_response = curl_requests.get(
724
+ f"https://assets.grok.com/{image_url}",
725
+ headers={
726
+ **DEFAULT_HEADERS,
727
+ "Cookie": CONFIG["API"]["SIGNATURE_COOKIE"]
728
+ },
729
+ impersonate="chrome120",
730
+ **proxy_options
731
+ )
732
+
733
+ if image_base64_response.status_code == 200:
734
+ break
735
+
736
+ retry_count += 1
737
+ if retry_count == max_retries:
738
+ raise Exception(f"上游服务请求失败! status: {image_base64_response.status_code}")
739
+
740
+ time.sleep(CONFIG["API"]["RETRY_TIME"] / 1000 * retry_count)
741
+
742
+ except Exception as error:
743
+ logger.error(str(error), "Server")
744
+ retry_count += 1
745
+ if retry_count == max_retries:
746
+ raise
747
+
748
+ time.sleep(CONFIG["API"]["RETRY_TIME"] / 1000 * retry_count)
749
+
750
+ image_buffer = image_base64_response.content
751
+
752
+ if not CONFIG["API"]["PICGO_KEY"] and not CONFIG["API"]["TUMY_KEY"]:
753
+ base64_image = base64.b64encode(image_buffer).decode('utf-8')
754
+ image_content_type = image_base64_response.headers.get('content-type', 'image/jpeg')
755
+ return f"![image](data:{image_content_type};base64,{base64_image})"
756
+
757
+ logger.info("开始上传图床", "Server")
758
+
759
+ if CONFIG["API"]["PICGO_KEY"]:
760
+ files = {'source': ('image.jpg', image_buffer, 'image/jpeg')}
761
+ headers = {
762
+ "X-API-Key": CONFIG["API"]["PICGO_KEY"]
763
+ }
764
+
765
+ response_url = requests.post(
766
+ "https://www.picgo.net/api/1/upload",
767
+ files=files,
768
+ headers=headers
769
+ )
770
+
771
+ if response_url.status_code != 200:
772
+ return "生图失败,请查看PICGO图床密钥是否设置正确"
773
+ else:
774
+ logger.info("生图成功", "Server")
775
+ result = response_url.json()
776
+ return f"![image]({result['image']['url']})"
777
+
778
+
779
+ elif CONFIG["API"]["TUMY_KEY"]:
780
+ files = {'file': ('image.jpg', image_buffer, 'image/jpeg')}
781
+ headers = {
782
+ "Accept": "application/json",
783
+ 'Authorization': f"Bearer {CONFIG['API']['TUMY_KEY']}"
784
+ }
785
+
786
+ response_url = requests.post(
787
+ "https://tu.my/api/v1/upload",
788
+ files=files,
789
+ headers=headers
790
+ )
791
+
792
+ if response_url.status_code != 200:
793
+ return "生图失败,请查看TUMY图床密钥是否设置正确"
794
+ else:
795
+ try:
796
+ result = response_url.json()
797
+ logger.info("生图成功", "Server")
798
+ return f"![image]({result['data']['links']['url']})"
799
+ except Exception as error:
800
+ logger.error(str(error), "Server")
801
+ return "生图失败,请查看TUMY图床密钥是否设置正确"
802
+
803
+ def handle_non_stream_response(response, model):
804
+ try:
805
+ logger.info("开始处理非流式响应", "Server")
806
+
807
+ stream = response.iter_lines()
808
+ full_response = ""
809
+
810
+ CONFIG["IS_THINKING"] = False
811
+ CONFIG["IS_IMG_GEN"] = False
812
+ CONFIG["IS_IMG_GEN2"] = False
813
+
814
+ for chunk in stream:
815
+ if not chunk:
816
+ continue
817
+ try:
818
+ line_json = json.loads(chunk.decode("utf-8").strip())
819
+ if line_json.get("error"):
820
+ logger.error(json.dumps(line_json, indent=2), "Server")
821
+ return json.dumps({"error": "RateLimitError"}) + "\n\n"
822
+
823
+ response_data = line_json.get("result", {}).get("response")
824
+ if not response_data:
825
+ continue
826
+
827
+ if response_data.get("doImgGen") or response_data.get("imageAttachmentInfo"):
828
+ CONFIG["IS_IMG_GEN"] = True
829
+
830
+ result = process_model_response(response_data, model)
831
+
832
+ if result["token"]:
833
+ full_response += result["token"]
834
+
835
+ if result["imageUrl"]:
836
+ CONFIG["IS_IMG_GEN2"] = True
837
+ return handle_image_response(result["imageUrl"])
838
+
839
+ except json.JSONDecodeError:
840
+ continue
841
+ except Exception as e:
842
+ logger.error(f"处理流式响应行时出错: {str(e)}", "Server")
843
+ continue
844
+
845
+ return full_response
846
+ except Exception as error:
847
+ logger.error(str(error), "Server")
848
+ raise
849
+ def handle_stream_response(response, model):
850
+ def generate():
851
+ logger.info("开始处理流式响应", "Server")
852
+
853
+ stream = response.iter_lines()
854
+ CONFIG["IS_THINKING"] = False
855
+ CONFIG["IS_IMG_GEN"] = False
856
+ CONFIG["IS_IMG_GEN2"] = False
857
+
858
+ for chunk in stream:
859
+ if not chunk:
860
+ continue
861
+ try:
862
+ line_json = json.loads(chunk.decode("utf-8").strip())
863
+ if line_json.get("error"):
864
+ logger.error(json.dumps(line_json, indent=2), "Server")
865
+ yield json.dumps({"error": "RateLimitError"}) + "\n\n"
866
+ return
867
+
868
+ response_data = line_json.get("result", {}).get("response")
869
+ if not response_data:
870
+ continue
871
+
872
+ if response_data.get("doImgGen") or response_data.get("imageAttachmentInfo"):
873
+ CONFIG["IS_IMG_GEN"] = True
874
+
875
+ result = process_model_response(response_data, model)
876
+
877
+ if result["token"]:
878
+ yield f"data: {json.dumps(MessageProcessor.create_chat_response(result['token'], model, True))}\n\n"
879
+
880
+ if result["imageUrl"]:
881
+ CONFIG["IS_IMG_GEN2"] = True
882
+ image_data = handle_image_response(result["imageUrl"])
883
+ yield f"data: {json.dumps(MessageProcessor.create_chat_response(image_data, model, True))}\n\n"
884
+
885
+ except json.JSONDecodeError:
886
+ continue
887
+ except Exception as e:
888
+ logger.error(f"处理流式响应行时出错: {str(e)}", "Server")
889
+ continue
890
+
891
+ yield "data: [DONE]\n\n"
892
+ return generate()
893
+
894
+ def initialization():
895
+ sso_array = os.environ.get("SSO", "").split(',')
896
+ logger.info("开始加载令牌", "Server")
897
+ for sso in sso_array:
898
+ if sso:
899
+ token_manager.add_token(f"sso-rw={sso};sso={sso}")
900
+
901
+ logger.info(f"成功加载令牌: {json.dumps(token_manager.get_all_tokens(), indent=2)}", "Server")
902
+ logger.info(f"令牌加载完成,共加载: {len(token_manager.get_all_tokens())}个令牌", "Server")
903
+
904
+ if CONFIG["API"]["PROXY"]:
905
+ logger.info(f"代理已设置: {CONFIG['API']['PROXY']}", "Server")
906
+
907
+ logger.info("初始化完成", "Server")
908
+
909
+
910
+ app = Flask(__name__)
911
+ app.wsgi_app = ProxyFix(app.wsgi_app)
912
+
913
+
914
+ @app.before_request
915
+ def log_request_info():
916
+ logger.info(f"{request.method} {request.path}", "Request")
917
+
918
+ @app.route('/get/tokens', methods=['GET'])
919
+ def get_tokens():
920
+ auth_token = request.headers.get('Authorization', '').replace('Bearer ', '')
921
+ if CONFIG["API"]["IS_CUSTOM_SSO"]:
922
+ return jsonify({"error": '自定义的SSO令牌模式无法获取轮询sso令牌状态'}), 403
923
+ elif auth_token != CONFIG["API"]["API_KEY"]:
924
+ return jsonify({"error": 'Unauthorized'}), 401
925
+
926
+ return jsonify(token_manager.get_token_status_map())
927
+
928
+ @app.route('/add/token', methods=['POST'])
929
+ def add_token():
930
+ auth_token = request.headers.get('Authorization', '').replace('Bearer ', '')
931
+ if CONFIG["API"]["IS_CUSTOM_SSO"]:
932
+ return jsonify({"error": '自定义的SSO令牌模式无法添加sso令牌'}), 403
933
+ elif auth_token != CONFIG["API"]["API_KEY"]:
934
+ return jsonify({"error": 'Unauthorized'}), 401
935
+
936
+ try:
937
+ sso = request.json.get('sso')
938
+ token_manager.add_token(f"sso-rw={sso};sso={sso}")
939
+ return jsonify(token_manager.get_token_status_map().get(sso, {})), 200
940
+ except Exception as error:
941
+ logger.error(str(error), "Server")
942
+ return jsonify({"error": '添加sso令牌失败'}), 500
943
+
944
+ @app.route('/delete/token', methods=['POST'])
945
+ def delete_token():
946
+ auth_token = request.headers.get('Authorization', '').replace('Bearer ', '')
947
+ if CONFIG["API"]["IS_CUSTOM_SSO"]:
948
+ return jsonify({"error": '自定义的SSO令牌模式无法删除sso令牌'}), 403
949
+ elif auth_token != CONFIG["API"]["API_KEY"]:
950
+ return jsonify({"error": 'Unauthorized'}), 401
951
+
952
+ try:
953
+ sso = request.json.get('sso')
954
+ token_manager.delete_token(f"sso-rw={sso};sso={sso}")
955
+ return jsonify({"message": '删除sso令牌成功'}), 200
956
+ except Exception as error:
957
+ logger.error(str(error), "Server")
958
+ return jsonify({"error": '删除sso令牌失败'}), 500
959
+
960
+ @app.route('/v1/models', methods=['GET'])
961
+ def get_models():
962
+ return jsonify({
963
+ "object": "list",
964
+ "data": [
965
+ {
966
+ "id": model,
967
+ "object": "model",
968
+ "created": int(time.time()),
969
+ "owned_by": "grok"
970
+ }
971
+ for model in CONFIG["MODELS"].keys()
972
+ ]
973
+ })
974
+
975
+ @app.route('/v1/chat/completions', methods=['POST'])
976
+ def chat_completions():
977
+ try:
978
+ auth_token = request.headers.get('Authorization',
979
+ '').replace('Bearer ', '')
980
+ if auth_token:
981
+ if CONFIG["API"]["IS_CUSTOM_SSO"]:
982
+ result = f"sso={auth_token};sso-rw={auth_token}"
983
+ token_manager.set_token(result)
984
+ elif auth_token != CONFIG["API"]["API_KEY"]:
985
+ return jsonify({"error": 'Unauthorized'}), 401
986
+ else:
987
+ return jsonify({"error": 'API_KEY缺失'}), 401
988
+
989
+ data = request.json
990
+ model = data.get("model")
991
+ stream = data.get("stream", False)
992
+
993
+ retry_count = 0
994
+ grok_client = GrokApiClient(model)
995
+ request_payload = grok_client.prepare_chat_request(data)
996
+ response_status_code = 500
997
+
998
+ while retry_count < CONFIG["RETRY"]["MAX_ATTEMPTS"]:
999
+ retry_count += 1
1000
+ CONFIG["API"]["SIGNATURE_COOKIE"] = Utils.create_auth_headers(
1001
+ model)
1002
+
1003
+ if not CONFIG["API"]["SIGNATURE_COOKIE"]:
1004
+ raise ValueError('该模型无可用令牌')
1005
+
1006
+ logger.info(
1007
+ f"当前令牌: {json.dumps(CONFIG['API']['SIGNATURE_COOKIE'], indent=2)}",
1008
+ "Server")
1009
+ logger.info(
1010
+ f"当前可用模型的全部可用数量: {json.dumps(token_manager.get_remaining_token_request_capacity(), indent=2)}",
1011
+ "Server")
1012
+
1013
+ try:
1014
+ proxy_options = Utils.get_proxy_options()
1015
+ response = curl_requests.post(
1016
+ f"{CONFIG['API']['BASE_URL']}/rest/app-chat/conversations/new",
1017
+ headers={
1018
+ **DEFAULT_HEADERS, "Cookie":
1019
+ CONFIG["API"]["SIGNATURE_COOKIE"]
1020
+ },
1021
+ data=json.dumps(request_payload),
1022
+ impersonate="chrome133a",
1023
+ stream=True,
1024
+ **proxy_options)
1025
+ if response.status_code == 200:
1026
+ response_status_code = 200
1027
+ logger.info("请求成功", "Server")
1028
+ logger.info(
1029
+ f"当前{model}剩余可用令牌数: {token_manager.get_token_count_for_model(model)}",
1030
+ "Server")
1031
+
1032
+ try:
1033
+ if stream:
1034
+ return Response(stream_with_context(
1035
+ handle_stream_response(response, model)),
1036
+ content_type='text/event-stream')
1037
+ else:
1038
+ content = handle_non_stream_response(
1039
+ response, model)
1040
+ return jsonify(
1041
+ MessageProcessor.create_chat_response(
1042
+ content, model))
1043
+
1044
+ except Exception as error:
1045
+ logger.error(str(error), "Server")
1046
+ if CONFIG["API"]["IS_CUSTOM_SSO"]:
1047
+ raise ValueError(f"自定义SSO令牌当前模型{model}的请求次数已失效")
1048
+
1049
+ token_manager.remove_token_from_model(
1050
+ model, CONFIG["API"]["SIGNATURE_COOKIE"])
1051
+ if token_manager.get_token_count_for_model(model) == 0:
1052
+ raise ValueError(f"{model} 次数已达上限,请切换其他模型或者重新对话")
1053
+ elif response.status_code == 403:
1054
+ response_status_code = 403
1055
+ token_manager.reduce_token_request_count(model,1)#重置去除当前因为错误未成功请求的次数,确保不会因为错误未成功请求的次数导致次数上限
1056
+ if token_manager.get_token_count_for_model(model) == 0:
1057
+ raise ValueError(f"{model} 次数已达上限,请切换其他模型或者重新对话")
1058
+ raise ValueError(f"IP暂时被封黑无法破盾,请稍后重试或者更换ip")
1059
+ elif response.status_code == 429:
1060
+ response_status_code = 429
1061
+ token_manager.reduce_token_request_count(model,1)
1062
+ if CONFIG["API"]["IS_CUSTOM_SSO"]:
1063
+ raise ValueError(f"自定义SSO令牌当前模型{model}的请求次数已失效")
1064
+
1065
+ token_manager.remove_token_from_model(
1066
+ model, CONFIG["API"]["SIGNATURE_COOKIE"])
1067
+ if token_manager.get_token_count_for_model(model) == 0:
1068
+ raise ValueError(f"{model} 次数已达上限,请切换其他模型或者重新对话")
1069
+
1070
+ else:
1071
+ if CONFIG["API"]["IS_CUSTOM_SSO"]:
1072
+ raise ValueError(f"自定义SSO令牌当前模型{model}的请求次数已失效")
1073
+
1074
+ logger.error(f"令牌异常错误状态!status: {response.status_code}",
1075
+ "Server")
1076
+ token_manager.remove_token_from_model(
1077
+ model, CONFIG["API"]["SIGNATURE_COOKIE"])
1078
+ logger.info(
1079
+ f"当前{model}剩余可用令牌数: {token_manager.get_token_count_for_model(model)}",
1080
+ "Server")
1081
+
1082
+ except Exception as e:
1083
+ logger.error(f"请求处理异常: {str(e)}", "Server")
1084
+ if CONFIG["API"]["IS_CUSTOM_SSO"]:
1085
+ raise
1086
+ continue
1087
+ if response_status_code == 403:
1088
+ raise ValueError('IP暂时被封黑无法破盾,请稍后重试或者更换ip')
1089
+ elif response_status_code == 500:
1090
+ raise ValueError('当前模型所有令牌暂无可用,请稍后重试')
1091
+
1092
+ except Exception as error:
1093
+ logger.error(str(error), "ChatAPI")
1094
+ return jsonify(
1095
+ {"error": {
1096
+ "message": str(error),
1097
+ "type": "server_error"
1098
+ }}), response_status_code
1099
+
1100
+ @app.route('/', defaults={'path': ''})
1101
+ @app.route('/<path:path>')
1102
+ def catch_all(path):
1103
+ return 'api运行正常', 200
1104
+
1105
+ if __name__ == '__main__':
1106
+ token_manager = AuthTokenManager()
1107
+ initialization()
1108
+
1109
+ app.run(
1110
+ host='0.0.0.0',
1111
+ port=CONFIG["SERVER"]["PORT"],
1112
+ debug=False
1113
+ )