yxmiler commited on
Commit
e1602e9
·
verified ·
1 Parent(s): a7c853d

Delete grok2api.py

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