CoolFace
Apppublic

apollor/GrokPythonService

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
app.py1261 linesDownload Raw Back to root
1import os2import json3import uuid4import time5import base646import sys7import inspect8import secrets9from loguru import logger10from dotenv import load_dotenv11 12import requests13from flask import Flask, request, Response, jsonify, stream_with_context, render_template, redirect, session14from curl_cffi import requests as curl_requests15from werkzeug.middleware.proxy_fix import ProxyFix16 17current_dir = os.path.dirname(os.path.abspath(__file__))18env_path = os.path.join(current_dir, '.env')19 20load_dotenv(env_path)21 22class Logger:23    def __init__(self, level="INFO", colorize=True, format=None):24        logger.remove()25 26        if format is None:27            format = (28                "<green>{time:YYYY-MM-DD HH:mm:ss}</green> | "29                "<level>{level: <8}</level> | "30                "<cyan>{extra[filename]}</cyan>:<cyan>{extra[function]}</cyan>:<cyan>{extra[lineno]}</cyan> | "31                "<level>{message}</level>"32            )33 34        logger.add(35            sys.stderr,36            level=level,37            format=format,38            colorize=colorize,39            backtrace=True,40            diagnose=True41        )42 43        self.logger = logger44 45    def _get_caller_info(self):46        frame = inspect.currentframe()47        try:48            caller_frame = frame.f_back.f_back49            full_path = caller_frame.f_code.co_filename50            function = caller_frame.f_code.co_name51            lineno = caller_frame.f_lineno52 53            filename = os.path.basename(full_path)54 55            return {56                'filename': filename,57                'function': function,58                'lineno': lineno59            }60        finally:61            del frame62 63    def info(self, message, source="API"):64        caller_info = self._get_caller_info()65        self.logger.bind(**caller_info).info(f"[{source}] {message}")66 67    def error(self, message, source="API"):68        caller_info = self._get_caller_info()69 70        if isinstance(message, Exception):71            self.logger.bind(**caller_info).exception(f"[{source}] {str(message)}")72        else:73            self.logger.bind(**caller_info).error(f"[{source}] {message}")74 75    def warning(self, message, source="API"):76        caller_info = self._get_caller_info()77        self.logger.bind(**caller_info).warning(f"[{source}] {message}")78 79    def debug(self, message, source="API"):80        caller_info = self._get_caller_info()81        self.logger.bind(**caller_info).debug(f"[{source}] {message}")82 83    async def request_logger(self, request):84        caller_info = self._get_caller_info()85        self.logger.bind(**caller_info).info(f"请求: {request.method} {request.path}", "Request")86 87logger = Logger(level="INFO")88 89 90CONFIG = {91    "MODELS": {92        'grok-2': 'grok-latest',93        'grok-2-imageGen': 'grok-latest',94        'grok-2-search': 'grok-latest',95        "grok-3": "grok-3",96        "grok-3-search": "grok-3",97        "grok-3-imageGen": "grok-3",98        "grok-3-deepsearch": "grok-3",99        "grok-3-reasoning": "grok-3"100    },101    "API": {102        "IS_TEMP_CONVERSATION": os.getenv("IS_TEMP_CONVERSATION", "true").lower() == "true",103        "IS_CUSTOM_SSO": os.getenv("IS_CUSTOM_SSO", "false").lower() == "true",104        "BASE_URL": "https://grok.com",105        "API_KEY": os.getenv("API_KEY", "sk-123456"),106        "SIGNATURE_COOKIE": None,107        "PICGO_KEY": os.getenv("PICGO_KEY") or None,108        "TUMY_KEY": os.getenv("TUMY_KEY") or None,109        "RETRY_TIME": 1000,110        "PROXY": os.getenv("PROXY") or None111    },112    "ADMIN": {113        "MANAGER_SWITCH": os.getenv("MANAGER_SWITCH") or None,114        "PASSWORD": os.getenv("ADMINPASSWORD") or None 115    },116    "SERVER": {117        "COOKIE": None,118        "CF_CLEARANCE":os.getenv("CF_CLEARANCE") or None,119        "PORT": int(os.getenv("PORT", 5200))120    },121    "RETRY": {122        "RETRYSWITCH": False,123        "MAX_ATTEMPTS": 2124    },125    "SHOW_THINKING": os.getenv("SHOW_THINKING") == "true",126    "IS_THINKING": False,127    "IS_IMG_GEN": False,128    "IS_IMG_GEN2": False,129    "ISSHOW_SEARCH_RESULTS": os.getenv("ISSHOW_SEARCH_RESULTS", "true").lower() == "true"130}131 132 133DEFAULT_HEADERS = {134    'Accept': '*/*',135    'Accept-Language': 'zh-CN,zh;q=0.9',136    'Accept-Encoding': 'gzip, deflate, br, zstd',137    'Content-Type': 'text/plain;charset=UTF-8',138    'Connection': 'keep-alive',139    'Origin': 'https://grok.com',140    'Priority': 'u=1, i',141    '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',142    'Sec-Ch-Ua': '"Not(A:Brand";v="99", "Google Chrome";v="133", "Chromium";v="133"',143    'Sec-Ch-Ua-Mobile': '?0',144    'Sec-Ch-Ua-Platform': '"macOS"',145    'Sec-Fetch-Dest': 'empty',146    'Sec-Fetch-Mode': 'cors',147    'Sec-Fetch-Site': 'same-origin',148    'Baggage': 'sentry-public_key=b311e0f2690c81f25e2c4cf6d4f7ce1c'149}150 151class AuthTokenManager:152    def __init__(self):153        self.token_model_map = {}154        self.expired_tokens = set()155        self.token_status_map = {}156 157        self.model_config = {158            "grok-2": {159                "RequestFrequency": 30,160                "ExpirationTime": 1 * 60 * 60 * 1000  # 1小时161            },162            "grok-3": {163                "RequestFrequency": 20,164                "ExpirationTime": 2 * 60 * 60 * 1000  # 2小时165            },166            "grok-3-deepsearch": {167                "RequestFrequency": 10,168                "ExpirationTime": 24 * 60 * 60 * 1000  # 24小时169            },170            "grok-3-reasoning": {171                "RequestFrequency": 10,172                "ExpirationTime": 24 * 60 * 60 * 1000  # 24小时173            }174        }175        self.token_reset_switch = False176        self.token_reset_timer = None177 178    def add_token(self, token):179        sso = token.split("sso=")[1].split(";")[0]180        for model in self.model_config.keys():181            if model not in self.token_model_map:182                self.token_model_map[model] = []183            if sso not in self.token_status_map:184                self.token_status_map[sso] = {}185 186            existing_token_entry = next((entry for entry in self.token_model_map[model] if entry["token"] == token), None)187 188            if not existing_token_entry:189                self.token_model_map[model].append({190                    "token": token,191                    "RequestCount": 0,192                    "AddedTime": int(time.time() * 1000),193                    "StartCallTime": None194                })195 196                if model not in self.token_status_map[sso]:197                    self.token_status_map[sso][model] = {198                        "isValid": True,199                        "invalidatedTime": None,200                        "totalRequestCount": 0201                    }202 203    def set_token(self, token):204        models = list(self.model_config.keys())205        self.token_model_map = {model: [{206            "token": token,207            "RequestCount": 0,208            "AddedTime": int(time.time() * 1000),209            "StartCallTime": None210        }] for model in models}211 212        sso = token.split("sso=")[1].split(";")[0]213        self.token_status_map[sso] = {model: {214            "isValid": True,215            "invalidatedTime": None,216            "totalRequestCount": 0217        } for model in models}218 219    def delete_token(self, token):220        try:221            sso = token.split("sso=")[1].split(";")[0]222            for model in self.token_model_map:223                self.token_model_map[model] = [entry for entry in self.token_model_map[model] if entry["token"] != token]224 225            if sso in self.token_status_map:226                del self.token_status_map[sso]227 228            logger.info(f"令牌已成功移除: {token}", "TokenManager")229            return True230        except Exception as error:231            logger.error(f"令牌删除失败: {str(error)}")232            return False233    def reduce_token_request_count(self, model_id, count):234        try:235            normalized_model = self.normalize_model_name(model_id)236            237            if normalized_model not in self.token_model_map:238                logger.error(f"模型 {normalized_model} 不存在", "TokenManager")239                return False240                241            if not self.token_model_map[normalized_model]:242                logger.error(f"模型 {normalized_model} 没有可用的token", "TokenManager")243                return False244                245            token_entry = self.token_model_map[normalized_model][0]246            247            # 确保RequestCount不会小于0248            new_count = max(0, token_entry["RequestCount"] - count)249            reduction = token_entry["RequestCount"] - new_count250            251            token_entry["RequestCount"] = new_count252            253            # 更新token状态254            if token_entry["token"]:255                sso = token_entry["token"].split("sso=")[1].split(";")[0]256                if sso in self.token_status_map and normalized_model in self.token_status_map[sso]:257                    self.token_status_map[sso][normalized_model]["totalRequestCount"] = max(258                        0, 259                        self.token_status_map[sso][normalized_model]["totalRequestCount"] - reduction260                    )261            return True262            263        except Exception as error:264            logger.error(f"重置校对token请求次数时发生错误: {str(error)}", "TokenManager")265            return False266    def get_next_token_for_model(self, model_id, is_return=False):267        normalized_model = self.normalize_model_name(model_id)268 269        if normalized_model not in self.token_model_map or not self.token_model_map[normalized_model]:270            return None271 272        token_entry = self.token_model_map[normalized_model][0]273        if is_return:274            return token_entry["token"]275 276        if token_entry:277            if token_entry["StartCallTime"] is None:278                token_entry["StartCallTime"] = int(time.time() * 1000)279 280            if not self.token_reset_switch:281                self.start_token_reset_process()282                self.token_reset_switch = True283 284            token_entry["RequestCount"] += 1285 286            if token_entry["RequestCount"] > self.model_config[normalized_model]["RequestFrequency"]:287                self.remove_token_from_model(normalized_model, token_entry["token"])288                next_token_entry = self.token_model_map[normalized_model][0] if self.token_model_map[normalized_model] else None289                return next_token_entry["token"] if next_token_entry else None290 291            sso = token_entry["token"].split("sso=")[1].split(";")[0]292            if sso in self.token_status_map and normalized_model in self.token_status_map[sso]:293                if token_entry["RequestCount"] == self.model_config[normalized_model]["RequestFrequency"]:294                    self.token_status_map[sso][normalized_model]["isValid"] = False295                    self.token_status_map[sso][normalized_model]["invalidatedTime"] = int(time.time() * 1000)296                self.token_status_map[sso][normalized_model]["totalRequestCount"] += 1297 298            return token_entry["token"]299 300        return None301 302    def remove_token_from_model(self, model_id, token):303        normalized_model = self.normalize_model_name(model_id)304 305        if normalized_model not in self.token_model_map:306            logger.error(f"模型 {normalized_model} 不存在", "TokenManager")307            return False308 309        model_tokens = self.token_model_map[normalized_model]310        token_index = next((i for i, entry in enumerate(model_tokens) if entry["token"] == token), -1)311 312        if token_index != -1:313            removed_token_entry = model_tokens.pop(token_index)314            self.expired_tokens.add((315                removed_token_entry["token"],316                normalized_model,317                int(time.time() * 1000)318            ))319 320            if not self.token_reset_switch:321                self.start_token_reset_process()322                self.token_reset_switch = True323 324            logger.info(f"模型{model_id}的令牌已失效,已成功移除令牌: {token}", "TokenManager")325            return True326 327        logger.error(f"在模型 {normalized_model} 中未找到 token: {token}", "TokenManager")328        return False329 330    def get_expired_tokens(self):331        return list(self.expired_tokens)332 333    def normalize_model_name(self, model):334        if model.startswith('grok-') and 'deepsearch' not in model and 'reasoning' not in model:335            return '-'.join(model.split('-')[:2])336        return model337 338    def get_token_count_for_model(self, model_id):339        normalized_model = self.normalize_model_name(model_id)340        return len(self.token_model_map.get(normalized_model, []))341 342    def get_remaining_token_request_capacity(self):343        remaining_capacity_map = {}344 345        for model in self.model_config.keys():346            model_tokens = self.token_model_map.get(model, [])347            model_request_frequency = self.model_config[model]["RequestFrequency"]348 349            total_used_requests = sum(token_entry.get("RequestCount", 0) for token_entry in model_tokens)350 351            remaining_capacity = (len(model_tokens) * model_request_frequency) - total_used_requests352            remaining_capacity_map[model] = max(0, remaining_capacity)353 354        return remaining_capacity_map355 356    def get_token_array_for_model(self, model_id):357        normalized_model = self.normalize_model_name(model_id)358        return self.token_model_map.get(normalized_model, [])359 360    def start_token_reset_process(self):361        def reset_expired_tokens():362            now = int(time.time() * 1000)363 364            tokens_to_remove = set()365            for token_info in self.expired_tokens:366                token, model, expired_time = token_info367                expiration_time = self.model_config[model]["ExpirationTime"]368 369                if now - expired_time >= expiration_time:370                    if not any(entry["token"] == token for entry in self.token_model_map.get(model, [])):371                        if model not in self.token_model_map:372                            self.token_model_map[model] = []373 374                        self.token_model_map[model].append({375                            "token": token,376                            "RequestCount": 0,377                            "AddedTime": now,378                            "StartCallTime": None379                        })380 381                    sso = token.split("sso=")[1].split(";")[0]382                    if sso in self.token_status_map and model in self.token_status_map[sso]:383                        self.token_status_map[sso][model]["isValid"] = True384                        self.token_status_map[sso][model]["invalidatedTime"] = None385                        self.token_status_map[sso][model]["totalRequestCount"] = 0386 387                    tokens_to_remove.add(token_info)388 389            self.expired_tokens -= tokens_to_remove390 391            for model in self.model_config.keys():392                if model not in self.token_model_map:393                    continue394 395                for token_entry in self.token_model_map[model]:396                    if not token_entry.get("StartCallTime"):397                        continue398 399                    expiration_time = self.model_config[model]["ExpirationTime"]400                    if now - token_entry["StartCallTime"] >= expiration_time:401                        sso = token_entry["token"].split("sso=")[1].split(";")[0]402                        if sso in self.token_status_map and model in self.token_status_map[sso]:403                            self.token_status_map[sso][model]["isValid"] = True404                            self.token_status_map[sso][model]["invalidatedTime"] = None405                            self.token_status_map[sso][model]["totalRequestCount"] = 0406 407                        token_entry["RequestCount"] = 0408                        token_entry["StartCallTime"] = None409 410        import threading411        # 启动一个线程执行定时任务,每小时执行一次412        def run_timer():413            while True:414                reset_expired_tokens()415                time.sleep(3600)416 417        timer_thread = threading.Thread(target=run_timer)418        timer_thread.daemon = True419        timer_thread.start()420 421    def get_all_tokens(self):422        all_tokens = set()423        for model_tokens in self.token_model_map.values():424            for entry in model_tokens:425                all_tokens.add(entry["token"])426        return list(all_tokens)427    def get_current_token(self, model_id):428        normalized_model = self.normalize_model_name(model_id)429 430        if normalized_model not in self.token_model_map or not self.token_model_map[normalized_model]:431            return None432 433        token_entry = self.token_model_map[normalized_model][0]434        return token_entry["token"]435 436    def get_token_status_map(self):437        return self.token_status_map438 439class Utils:440    @staticmethod441    def organize_search_results(search_results):442        if not search_results or 'results' not in search_results:443            return ''444 445        results = search_results['results']446        formatted_results = []447 448        for index, result in enumerate(results):449            title = result.get('title', '未知标题')450            url = result.get('url', '#')451            preview = result.get('preview', '无预览内容')452 453            formatted_result = f"\r\n<details><summary>资料[{index}]: {title}</summary>\r\n{preview}\r\n\n[Link]({url})\r\n</details>"454            formatted_results.append(formatted_result)455 456        return '\n\n'.join(formatted_results)457 458    @staticmethod459    def create_auth_headers(model, is_return=False):460        return token_manager.get_next_token_for_model(model, is_return)461 462    @staticmethod463    def get_proxy_options():464        proxy = CONFIG["API"]["PROXY"]465        proxy_options = {}466 467        if proxy:468            logger.info(f"使用代理: {proxy}", "Server")469            470            if proxy.startswith("socks5://"):471                proxy_options["proxy"] = proxy472            473                if '@' in proxy:474                    auth_part = proxy.split('@')[0].split('://')[1]475                    if ':' in auth_part:476                        username, password = auth_part.split(':')477                        proxy_options["proxy_auth"] = (username, password)478            else:479                proxy_options["proxies"] = {"https": proxy, "http": proxy}     480        return proxy_options481 482class GrokApiClient:483    def __init__(self, model_id):484        if model_id not in CONFIG["MODELS"]:485            raise ValueError(f"不支持的模型: {model_id}")486        self.model_id = CONFIG["MODELS"][model_id]487 488    def process_message_content(self, content):489        if isinstance(content, str):490            return content491        return None492 493    def get_image_type(self, base64_string):494        mime_type = 'image/jpeg'495        if 'data:image' in base64_string:496            import re497            matches = re.search(r'data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+);base64,', base64_string)498            if matches:499                mime_type = matches.group(1)500 501        extension = mime_type.split('/')[1]502        file_name = f"image.{extension}"503 504        return {505            "mimeType": mime_type,506            "fileName": file_name507        }508    def upload_base64_file(self, message, model):509        try:510            message_base64 = base64.b64encode(message.encode('utf-8')).decode('utf-8')511            upload_data = {512                "fileName": "message.txt",513                "fileMimeType": "text/plain",514                "content": message_base64515            }516 517            logger.info("发送文字文件请求", "Server")518            cookie = f"{Utils.create_auth_headers(model, True)};{CONFIG['SERVER']['CF_CLEARANCE']}" 519            proxy_options = Utils.get_proxy_options()520            response = curl_requests.post(521                "https://grok.com/rest/app-chat/upload-file",522                headers={523                    **DEFAULT_HEADERS,524                    "Cookie":cookie525                },526                json=upload_data,527                impersonate="chrome133a",528                verify=False,529                **proxy_options530            )531 532            if response.status_code != 200:533                logger.error(f"上传文件失败,状态码:{response.status_code}", "Server")534                raise Exception(f"上传文件失败,状态码:{response.status_code}")535 536            result = response.json()537            logger.info(f"上传文件成功: {result}", "Server")538            return result.get("fileMetadataId", "")539 540        except Exception as error:541            logger.error(str(error), "Server")542            raise Exception(f"上传文件失败,状态码:{response.status_code}")543    def upload_base64_image(self, base64_data, url):544        try:545            if 'data:image' in base64_data:546                image_buffer = base64_data.split(',')[1]547            else:548                image_buffer = base64_data549 550            image_info = self.get_image_type(base64_data)551            mime_type = image_info["mimeType"]552            file_name = image_info["fileName"]553 554            upload_data = {555                "rpc": "uploadFile",556                "req": {557                    "fileName": file_name,558                    "fileMimeType": mime_type,559                    "content": image_buffer560                }561            }562 563            logger.info("发送图片请求", "Server")564 565            proxy_options = Utils.get_proxy_options()566            response = curl_requests.post(567                url,568                headers={569                    **DEFAULT_HEADERS,570                    "Cookie":CONFIG["SERVER"]['COOKIE']571                },572                json=upload_data,573                impersonate="chrome133a",574                verify=False,575                **proxy_options576            )577 578            if response.status_code != 200:579                logger.error(f"上传图片失败,状态码:{response.status_code}", "Server")580                return ''581 582            result = response.json()583            logger.info(f"上传图片成功: {result}", "Server")584            return result.get("fileMetadataId", "")585 586        except Exception as error:587            logger.error(str(error), "Server")588            return ''589 590    def prepare_chat_request(self, request):591        if ((request["model"] == 'grok-2-imageGen' or request["model"] == 'grok-3-imageGen') and592            not CONFIG["API"]["PICGO_KEY"] and not CONFIG["API"]["TUMY_KEY"] and593            request.get("stream", False)):594            raise ValueError("该模型流式输出需要配置PICGO或者TUMY图床密钥!")595 596        todo_messages = request["messages"]597        if request["model"] in ['grok-2-imageGen', 'grok-3-imageGen', 'grok-3-deepsearch']:598            last_message = todo_messages[-1]599            if last_message["role"] != 'user':600                raise ValueError('此模型最后一条消息必须是用户消息!')601            todo_messages = [last_message]602        file_attachments = []603        messages = ''604        last_role = None605        last_content = ''606        message_length = 0607        convert_to_file = False608        last_message_content = ''609        search = request["model"] in ['grok-2-search', 'grok-3-search']610 611        # 移除<think>标签及其内容和base64图片612        def remove_think_tags(text):613            import re614            text = re.sub(r'<think>[\s\S]*?<\/think>', '', text).strip()615            text = re.sub(r'!\[image\]\(data:.*?base64,.*?\)', '[图片]', text)616            return text617 618        def process_content(content):619            if isinstance(content, list):620                text_content = ''621                for item in content:622                    if item["type"] == 'image_url':623                        text_content += ("[图片]" if not text_content else '\n[图片]')624                    elif item["type"] == 'text':625                        text_content += (remove_think_tags(item["text"]) if not text_content else '\n' + remove_think_tags(item["text"]))626                return text_content627            elif isinstance(content, dict) and content is not None:628                if content["type"] == 'image_url':629                    return "[图片]"630                elif content["type"] == 'text':631                    return remove_think_tags(content["text"])632            return remove_think_tags(self.process_message_content(content))633 634        for current in todo_messages:635            role = 'assistant' if current["role"] == 'assistant' else 'user'636            is_last_message = current == todo_messages[-1]637 638            if is_last_message and "content" in current:639                if isinstance(current["content"], list):640                    for item in current["content"]:641                        if item["type"] == 'image_url':642                            processed_image = self.upload_base64_image(643                                item["image_url"]["url"],644                                f"{CONFIG['API']['BASE_URL']}/api/rpc"645                            )646                            if processed_image:647                                file_attachments.append(processed_image)648                elif isinstance(current["content"], dict) and current["content"].get("type") == 'image_url':649                    processed_image = self.upload_base64_image(650                        current["content"]["image_url"]["url"],651                        f"{CONFIG['API']['BASE_URL']}/api/rpc"652                    )653                    if processed_image:654                        file_attachments.append(processed_image)655 656 657            text_content = process_content(current.get("content", ""))658            if is_last_message and convert_to_file:659                last_message_content = f"{role.upper()}: {text_content or '[图片]'}\n"660                continue661            if text_content or (is_last_message and file_attachments):662                if role == last_role and text_content:663                    last_content += '\n' + text_content664                    messages = messages[:messages.rindex(f"{role.upper()}: ")] + f"{role.upper()}: {last_content}\n"665                else:666                    messages += f"{role.upper()}: {text_content or '[图片]'}\n"667                    last_content = text_content668                    last_role = role669            message_length += len(messages)670            if message_length >= 40000:671                convert_to_file = True672               673        if convert_to_file:674            file_id = self.upload_base64_file(messages, request["model"])675            if file_id:676                file_attachments.insert(0, file_id)677            messages = last_message_content.strip()678        if messages.strip() == '':679            if convert_to_file:680                messages = '基于txt文件内容进行回复:'681            else:682                raise ValueError('消息内容为空!')683        return {684            "temporary": CONFIG["API"].get("IS_TEMP_CONVERSATION", False),685            "modelName": self.model_id,686            "message": messages.strip(),687            "fileAttachments": file_attachments[:4],688            "imageAttachments": [],689            "disableSearch": False,690            "enableImageGeneration": True,691            "returnImageBytes": False,692            "returnRawGrokInXaiRequest": False,693            "enableImageStreaming": False,694            "imageGenerationCount": 1,695            "forceConcise": False,696            "toolOverrides": {697                "imageGen": request["model"] in ['grok-2-imageGen', 'grok-3-imageGen'],698                "webSearch": search,699                "xSearch": search,700                "xMediaSearch": search,701                "trendsSearch": search,702                "xPostAnalyze": search703            },704            "enableSideBySide": True,705            "isPreset": False,706            "sendFinalMetadata": True,707            "customInstructions": "",708            "deepsearchPreset": "default" if request["model"] == 'grok-3-deepsearch' else "",709            "isReasoning": request["model"] == 'grok-3-reasoning'710        }711 712class MessageProcessor:713    @staticmethod714    def create_chat_response(message, model, is_stream=False):715        base_response = {716            "id": f"chatcmpl-{uuid.uuid4()}",717            "created": int(time.time()),718            "model": model719        }720 721        if is_stream:722            return {723                **base_response,724                "object": "chat.completion.chunk",725                "choices": [{726                    "index": 0,727                    "delta": {728                        "content": message729                    }730                }]731            }732 733        return {734            **base_response,735            "object": "chat.completion",736            "choices": [{737                "index": 0,738                "message": {739                    "role": "assistant",740                    "content": message741                },742                "finish_reason": "stop"743            }],744            "usage": None745        }746 747def process_model_response(response, model):748    result = {"token": None, "imageUrl": None}749 750    if CONFIG["IS_IMG_GEN"]:751        if response.get("cachedImageGenerationResponse") and not CONFIG["IS_IMG_GEN2"]:752            result["imageUrl"] = response["cachedImageGenerationResponse"]["imageUrl"]753        return result754 755    if model == 'grok-2':756        result["token"] = response.get("token")757    elif model in ['grok-2-search', 'grok-3-search']:758        if response.get("webSearchResults") and CONFIG["ISSHOW_SEARCH_RESULTS"]:759            result["token"] = f"\r\n<think>{Utils.organize_search_results(response['webSearchResults'])}</think>\r\n"760        else:761            result["token"] = response.get("token")762    elif model == 'grok-3':763        result["token"] = response.get("token")764    elif model == 'grok-3-deepsearch':765        if response.get("messageStepId") and not CONFIG["SHOW_THINKING"]:766            return result767        if response.get("messageStepId") and not CONFIG["IS_THINKING"]:768            result["token"] = "<think>" + response.get("token", "")769            CONFIG["IS_THINKING"] = True770        elif not response.get("messageStepId") and CONFIG["IS_THINKING"] and response.get("messageTag") == "final":771            result["token"] = "</think>" + response.get("token", "")772            CONFIG["IS_THINKING"] = False773        elif (response.get("messageStepId") and CONFIG["IS_THINKING"] and response.get("messageTag") == "assistant") or response.get("messageTag") == "final":774            result["token"] = response.get("token")775    elif model == 'grok-3-reasoning':776        if response.get("isThinking") and not CONFIG["SHOW_THINKING"]:777            return result778 779        if response.get("isThinking") and not CONFIG["IS_THINKING"]:780            result["token"] = "<think>" + response.get("token", "")781            CONFIG["IS_THINKING"] = True782        elif not response.get("isThinking") and CONFIG["IS_THINKING"]:783            result["token"] = "</think>" + response.get("token", "")784            CONFIG["IS_THINKING"] = False785        else:786            result["token"] = response.get("token")787 788    return result789 790def handle_image_response(image_url):791    max_retries = 2792    retry_count = 0793    image_base64_response = None794 795    while retry_count < max_retries:796        try:797            proxy_options = Utils.get_proxy_options()798            image_base64_response = curl_requests.get(799                f"https://assets.grok.com/{image_url}",800                headers={801                    **DEFAULT_HEADERS,802                    "Cookie":CONFIG["SERVER"]['COOKIE']803                },804                impersonate="chrome133a",805                **proxy_options806            )807 808            if image_base64_response.status_code == 200:809                break810 811            retry_count += 1812            if retry_count == max_retries:813                raise Exception(f"上游服务请求失败! status: {image_base64_response.status_code}")814 815            time.sleep(CONFIG["API"]["RETRY_TIME"] / 1000 * retry_count)816 817        except Exception as error:818            logger.error(str(error), "Server")819            retry_count += 1820            if retry_count == max_retries:821                raise822 823            time.sleep(CONFIG["API"]["RETRY_TIME"] / 1000 * retry_count)824 825    image_buffer = image_base64_response.content826 827    if not CONFIG["API"]["PICGO_KEY"] and not CONFIG["API"]["TUMY_KEY"]:828        base64_image = base64.b64encode(image_buffer).decode('utf-8')829        image_content_type = image_base64_response.headers.get('content-type', 'image/jpeg')830        return f"![image](data:{image_content_type};base64,{base64_image})"831 832    logger.info("开始上传图床", "Server")833 834    if CONFIG["API"]["PICGO_KEY"]:835        files = {'source': ('image.jpg', image_buffer, 'image/jpeg')}836        headers = {837            "X-API-Key": CONFIG["API"]["PICGO_KEY"]838        }839 840        response_url = requests.post(841            "https://www.picgo.net/api/1/upload",842            files=files,843            headers=headers844        )845 846        if response_url.status_code != 200:847            return "生图失败,请查看PICGO图床密钥是否设置正确"848        else:849            logger.info("生图成功", "Server")850            result = response_url.json()851            return f"![image]({result['image']['url']})"852 853 854    elif CONFIG["API"]["TUMY_KEY"]:855        files = {'file': ('image.jpg', image_buffer, 'image/jpeg')}856        headers = {857            "Accept": "application/json",858            'Authorization': f"Bearer {CONFIG['API']['TUMY_KEY']}"859        }860 861        response_url = requests.post(862            "https://tu.my/api/v1/upload",863            files=files,864            headers=headers865        )866 867        if response_url.status_code != 200:868            return "生图失败,请查看TUMY图床密钥是否设置正确"869        else:870            try:871                result = response_url.json()872                logger.info("生图成功", "Server")873                return f"![image]({result['data']['links']['url']})"874            except Exception as error:875                logger.error(str(error), "Server")876                return "生图失败,请查看TUMY图床密钥是否设置正确"877 878def handle_non_stream_response(response, model):879    try:880        logger.info("开始处理非流式响应", "Server")881 882        stream = response.iter_lines()883        full_response = ""884 885        CONFIG["IS_THINKING"] = False886        CONFIG["IS_IMG_GEN"] = False887        CONFIG["IS_IMG_GEN2"] = False888 889        for chunk in stream:890            if not chunk:891                continue892            try:893                line_json = json.loads(chunk.decode("utf-8").strip())894                if line_json.get("error"):895                    logger.error(json.dumps(line_json, indent=2), "Server")896                    return json.dumps({"error": "RateLimitError"}) + "\n\n"897 898                response_data = line_json.get("result", {}).get("response")899                if not response_data:900                    continue901 902                if response_data.get("doImgGen") or response_data.get("imageAttachmentInfo"):903                    CONFIG["IS_IMG_GEN"] = True904 905                result = process_model_response(response_data, model)906 907                if result["token"]:908                    full_response += result["token"]909 910                if result["imageUrl"]:911                    CONFIG["IS_IMG_GEN2"] = True912                    return handle_image_response(result["imageUrl"])913 914            except json.JSONDecodeError:915                continue916            except Exception as e:917                logger.error(f"处理流式响应行时出错: {str(e)}", "Server")918                continue919 920        return full_response921    except Exception as error:922        logger.error(str(error), "Server")923        raise924def handle_stream_response(response, model):925    def generate():926        logger.info("开始处理流式响应", "Server")927 928        stream = response.iter_lines()929        CONFIG["IS_THINKING"] = False930        CONFIG["IS_IMG_GEN"] = False931        CONFIG["IS_IMG_GEN2"] = False932 933        for chunk in stream:934            if not chunk:935                continue936            try:937                line_json = json.loads(chunk.decode("utf-8").strip())938                if line_json.get("error"):939                    logger.error(json.dumps(line_json, indent=2), "Server")940                    yield json.dumps({"error": "RateLimitError"}) + "\n\n"941                    return942 943                response_data = line_json.get("result", {}).get("response")944                if not response_data:945                    continue946 947                if response_data.get("doImgGen") or response_data.get("imageAttachmentInfo"):948                    CONFIG["IS_IMG_GEN"] = True949 950                result = process_model_response(response_data, model)951 952                if result["token"]:953                    yield f"data: {json.dumps(MessageProcessor.create_chat_response(result['token'], model, True))}\n\n"954 955                if result["imageUrl"]:956                    CONFIG["IS_IMG_GEN2"] = True957                    image_data = handle_image_response(result["imageUrl"])958                    yield f"data: {json.dumps(MessageProcessor.create_chat_response(image_data, model, True))}\n\n"959 960            except json.JSONDecodeError:961                continue962            except Exception as e:963                logger.error(f"处理流式响应行时出错: {str(e)}", "Server")964                continue965 966        yield "data: [DONE]\n\n"967    return generate()968 969def initialization():970    sso_array = os.getenv("SSO", "").split(',')971    logger.info("开始加载令牌", "Server")972    for sso in sso_array:973        if sso:974            token_manager.add_token(f"sso-rw={sso};sso={sso}")975 976    logger.info(f"成功加载令牌: {json.dumps(token_manager.get_all_tokens(), indent=2)}", "Server")977    logger.info(f"令牌加载完成,共加载: {len(token_manager.get_all_tokens())}个令牌", "Server")978 979    if CONFIG["API"]["PROXY"]:980        logger.info(f"代理已设置: {CONFIG['API']['PROXY']}", "Server")981 982logger.info("初始化完成", "Server")983 984 985app = Flask(__name__)986app.wsgi_app = ProxyFix(app.wsgi_app)987app.secret_key = os.getenv('FLASK_SECRET_KEY') or secrets.token_hex(16)988app.json.sort_keys = False989 990@app.route('/manager/login', methods=['GET', 'POST'])991def manager_login():992    if CONFIG["ADMIN"]["MANAGER_SWITCH"]:993        if request.method == 'POST':994            password = request.form.get('password')995            if password == CONFIG["ADMIN"]["PASSWORD"]:996                session['is_logged_in'] = True997                return redirect('/manager')998            return render_template('login.html', error=True)999        return render_template('login.html', error=False)1000    else:1001        return redirect('/')1002 1003def check_auth():1004    return session.get('is_logged_in', False)1005 1006@app.route('/manager')1007def manager():1008    if not check_auth():1009        return redirect('/manager/login')1010    return render_template('manager.html')1011 1012@app.route('/manager/api/get')1013def get_manager_tokens():1014    if not check_auth():1015        return jsonify({"error": "Unauthorized"}), 4011016    return jsonify(token_manager.get_token_status_map())1017 1018@app.route('/manager/api/add', methods=['POST'])1019def add_manager_token():1020    if not check_auth():1021        return jsonify({"error": "Unauthorized"}), 4011022    try:1023        sso = request.json.get('sso')1024        if not sso:1025            return jsonify({"error": "SSO token is required"}), 4001026        token_manager.add_token(f"sso-rw={sso};sso={sso}")1027        return jsonify({"success": True})1028    except Exception as e:1029        return jsonify({"error": str(e)}), 5001030 1031@app.route('/manager/api/delete', methods=['POST'])1032def delete_manager_token():1033    if not check_auth():1034        return jsonify({"error": "Unauthorized"}), 4011035    try:1036        sso = request.json.get('sso')1037        if not sso:1038            return jsonify({"error": "SSO token is required"}), 4001039        token_manager.delete_token(f"sso-rw={sso};sso={sso}")1040        return jsonify({"success": True})1041    except Exception as e:1042        return jsonify({"error": str(e)}), 5001043    1044@app.route('/manager/api/cf_clearance', methods=['POST'])   1045def setCf_Manager_clearance():1046    if not check_auth():1047        return jsonify({"error": "Unauthorized"}), 4011048    try:1049        cf_clearance = request.json.get('cf_clearance')1050        if not cf_clearance:1051            return jsonify({"error": "cf_clearance is required"}), 4001052        CONFIG["SERVER"]['CF_CLEARANCE'] = cf_clearance1053        return jsonify({"success": True})1054    except Exception as e:1055        return jsonify({"error": str(e)}), 5001056 1057 1058@app.route('/get/tokens', methods=['GET'])1059def get_tokens():1060    auth_token = request.headers.get('Authorization', '').replace('Bearer ', '')1061    if CONFIG["API"]["IS_CUSTOM_SSO"]:1062        return jsonify({"error": '自定义的SSO令牌模式无法获取轮询sso令牌状态'}), 4031063    elif auth_token != CONFIG["API"]["API_KEY"]:1064        return jsonify({"error": 'Unauthorized'}), 4011065    return jsonify(token_manager.get_token_status_map())1066 1067@app.route('/add/token', methods=['POST'])1068def add_token():1069    auth_token = request.headers.get('Authorization', '').replace('Bearer ', '')1070    if CONFIG["API"]["IS_CUSTOM_SSO"]:1071        return jsonify({"error": '自定义的SSO令牌模式无法添加sso令牌'}), 4031072    elif auth_token != CONFIG["API"]["API_KEY"]:1073        return jsonify({"error": 'Unauthorized'}), 4011074 1075    try:1076        sso = request.json.get('sso')1077        token_manager.add_token(f"sso-rw={sso};sso={sso}")1078        return jsonify(token_manager.get_token_status_map().get(sso, {})), 2001079    except Exception as error:1080        logger.error(str(error), "Server")1081        return jsonify({"error": '添加sso令牌失败'}), 5001082    1083@app.route('/set/cf_clearance', methods=['POST'])1084def setCf_clearance():1085    auth_token = request.headers.get('Authorization', '').replace('Bearer ', '')1086    if auth_token != CONFIG["API"]["API_KEY"]:1087        return jsonify({"error": 'Unauthorized'}), 4011088    try:1089        cf_clearance = request.json.get('cf_clearance')1090        CONFIG["SERVER"]['CF_CLEARANCE'] = cf_clearance1091        return jsonify({"message": '设置cf_clearance成功'}), 2001092    except Exception as error:1093        logger.error(str(error), "Server")1094        return jsonify({"error": '设置cf_clearance失败'}), 5001095    1096@app.route('/delete/token', methods=['POST'])1097def delete_token():1098    auth_token = request.headers.get('Authorization', '').replace('Bearer ', '')1099    if CONFIG["API"]["IS_CUSTOM_SSO"]:1100        return jsonify({"error": '自定义的SSO令牌模式无法删除sso令牌'}), 4031101    elif auth_token != CONFIG["API"]["API_KEY"]:1102        return jsonify({"error": 'Unauthorized'}), 4011103 1104    try:1105        sso = request.json.get('sso')1106        token_manager.delete_token(f"sso-rw={sso};sso={sso}")1107        return jsonify({"message": '删除sso令牌成功'}), 2001108    except Exception as error:1109        logger.error(str(error), "Server")1110        return jsonify({"error": '删除sso令牌失败'}), 5001111 1112@app.route('/v1/models', methods=['GET'])1113def get_models():1114    return jsonify({1115        "object": "list",1116        "data": [1117            {1118                "id": model,1119                "object": "model",1120                "created": int(time.time()),1121                "owned_by": "grok"1122            }1123            for model in CONFIG["MODELS"].keys()1124        ]1125    })1126 1127@app.route('/v1/chat/completions', methods=['POST'])1128def chat_completions():1129    response_status_code = 5001130    try:1131        auth_token = request.headers.get('Authorization',1132                                         '').replace('Bearer ', '')1133        if auth_token:1134            if CONFIG["API"]["IS_CUSTOM_SSO"]:1135                result = f"sso={auth_token};sso-rw={auth_token}"1136                token_manager.set_token(result)1137            elif auth_token != CONFIG["API"]["API_KEY"]:1138                return jsonify({"error": 'Unauthorized'}), 4011139        else:1140            return jsonify({"error": 'API_KEY缺失'}), 4011141 1142        data = request.json1143        model = data.get("model")1144        stream = data.get("stream", False)1145 1146        retry_count = 01147        grok_client = GrokApiClient(model)1148        request_payload = grok_client.prepare_chat_request(data)1149        1150 1151        while retry_count < CONFIG["RETRY"]["MAX_ATTEMPTS"]:1152            retry_count += 11153            CONFIG["API"]["SIGNATURE_COOKIE"] = Utils.create_auth_headers(model)1154 1155            if not CONFIG["API"]["SIGNATURE_COOKIE"]:1156                raise ValueError('该模型无可用令牌')1157 1158            logger.info(1159                f"当前令牌: {json.dumps(CONFIG['API']['SIGNATURE_COOKIE'], indent=2)}","Server")1160            logger.info(1161                f"当前可用模型的全部可用数量: {json.dumps(token_manager.get_remaining_token_request_capacity(), indent=2)}","Server")1162            1163            if CONFIG['SERVER']['CF_CLEARANCE']:1164                CONFIG["SERVER"]['COOKIE'] = f"{CONFIG['API']['SIGNATURE_COOKIE']};{CONFIG['SERVER']['CF_CLEARANCE']}" 1165            else:1166                CONFIG["SERVER"]['COOKIE'] = CONFIG['API']['SIGNATURE_COOKIE']1167            logger.info(json.dumps(request_payload,indent=2),"Server")1168            try:1169                proxy_options = Utils.get_proxy_options()1170                response = curl_requests.post(1171                    f"{CONFIG['API']['BASE_URL']}/rest/app-chat/conversations/new",1172                    headers={1173                        **DEFAULT_HEADERS, 1174                        "Cookie":CONFIG["SERVER"]['COOKIE']1175                    },1176                    json=request_payload,1177                    impersonate="chrome133a",1178                    verify=False,1179                    stream=True,1180                    **proxy_options)1181                logger.info(CONFIG["SERVER"]['COOKIE'],"Server")1182                if response.status_code == 200:1183                    response_status_code = 2001184                    logger.info("请求成功", "Server")1185                    logger.info(f"当前{model}剩余可用令牌数: {token_manager.get_token_count_for_model(model)}","Server")1186 1187                    try:1188                        if stream:1189                            return Response(stream_with_context(1190                                handle_stream_response(response, model)),content_type='text/event-stream')1191                        else:1192                            content = handle_non_stream_response(response, model)1193                            return jsonify(1194                                MessageProcessor.create_chat_response(content, model))1195 1196                    except Exception as error:1197                        logger.error(str(error), "Server")1198                        if CONFIG["API"]["IS_CUSTOM_SSO"]:1199                            raise ValueError(f"自定义SSO令牌当前模型{model}的请求次数已失效")1200                        token_manager.remove_token_from_model(model, CONFIG["API"]["SIGNATURE_COOKIE"])

Showing the first 1,200 of 1261 lines. Download the file for the rest.