CoolFace
Apppublic

cvener/GrokPythonService

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
app.py1315 linesDownload Raw Back to root
1import os2import json3import uuid4import time5import base646import sys7import inspect8import secrets9from loguru import logger10from pathlib import Path11 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 17class Logger:18    def __init__(self, level="INFO", colorize=True, format=None):19        logger.remove()20 21        if format is None:22            format = (23                "<green>{time:YYYY-MM-DD HH:mm:ss}</green> | "24                "<level>{level: <8}</level> | "25                "<cyan>{extra[filename]}</cyan>:<cyan>{extra[function]}</cyan>:<cyan>{extra[lineno]}</cyan> | "26                "<level>{message}</level>"27            )28 29        logger.add(30            sys.stderr,31            level=level,32            format=format,33            colorize=colorize,34            backtrace=True,35            diagnose=True36        )37 38        self.logger = logger39 40    def _get_caller_info(self):41        frame = inspect.currentframe()42        try:43            caller_frame = frame.f_back.f_back44            full_path = caller_frame.f_code.co_filename45            function = caller_frame.f_code.co_name46            lineno = caller_frame.f_lineno47 48            filename = os.path.basename(full_path)49 50            return {51                'filename': filename,52                'function': function,53                'lineno': lineno54            }55        finally:56            del frame57 58    def info(self, message, source="API"):59        caller_info = self._get_caller_info()60        self.logger.bind(**caller_info).info(f"[{source}] {message}")61 62    def error(self, message, source="API"):63        caller_info = self._get_caller_info()64 65        if isinstance(message, Exception):66            self.logger.bind(**caller_info).exception(f"[{source}] {str(message)}")67        else:68            self.logger.bind(**caller_info).error(f"[{source}] {message}")69 70    def warning(self, message, source="API"):71        caller_info = self._get_caller_info()72        self.logger.bind(**caller_info).warning(f"[{source}] {message}")73 74    def debug(self, message, source="API"):75        caller_info = self._get_caller_info()76        self.logger.bind(**caller_info).debug(f"[{source}] {message}")77 78    async def request_logger(self, request):79        caller_info = self._get_caller_info()80        self.logger.bind(**caller_info).info(f"请求: {request.method} {request.path}", "Request")81 82logger = Logger(level="INFO")83 84CONFIG = {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-deepersearch": "grok-3",94        "grok-3-reasoning": "grok-3"95    },96    "API": {97        "IS_TEMP_CONVERSATION": os.environ.get("IS_TEMP_CONVERSATION", "true").lower() == "true",98        "IS_CUSTOM_SSO": os.environ.get("IS_CUSTOM_SSO", "false").lower() == "true",99        "BASE_URL": "https://grok.com",100        "API_KEY": os.environ.get("API_KEY", "sk-123456"),101        "SIGNATURE_COOKIE": None,102        "PICGO_KEY": os.environ.get("PICGO_KEY") or None,103        "TUMY_KEY": os.environ.get("TUMY_KEY") or None,104        "RETRY_TIME": 1000,105        "PROXY": os.environ.get("PROXY") or None106    },107    "ADMIN": {108        "MANAGER_SWITCH": os.environ.get("MANAGER_SWITCH") or None,109        "PASSWORD": os.environ.get("ADMINPASSWORD") or None 110    },111    "SERVER": {112        "COOKIE": None,113        "CF_CLEARANCE":os.environ.get("CF_CLEARANCE") or None,114        "PORT": int(os.environ.get("PORT", 5200))115    },116    "RETRY": {117        "RETRYSWITCH": False,118        "MAX_ATTEMPTS": 2119    },120    "SHOW_THINKING": os.environ.get("SHOW_THINKING") == "true",121    "IS_THINKING": False,122    "IS_IMG_GEN": False,123    "IS_IMG_GEN2": False,124    "ISSHOW_SEARCH_RESULTS": os.environ.get("ISSHOW_SEARCH_RESULTS", "true").lower() == "true"125}126 127 128DEFAULT_HEADERS = {129    'Accept': '*/*',130    'Accept-Language': 'zh-CN,zh;q=0.9',131    'Accept-Encoding': 'gzip, deflate, br, zstd',132    'Content-Type': 'text/plain;charset=UTF-8',133    'Connection': 'keep-alive',134    'Origin': 'https://grok.com',135    'Priority': 'u=1, i',136    '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',137    'Sec-Ch-Ua': '"Not(A:Brand";v="99", "Google Chrome";v="133", "Chromium";v="133"',138    'Sec-Ch-Ua-Mobile': '?0',139    'Sec-Ch-Ua-Platform': '"macOS"',140    'Sec-Fetch-Dest': 'empty',141    'Sec-Fetch-Mode': 'cors',142    'Sec-Fetch-Site': 'same-origin',143    'Baggage': 'sentry-public_key=b311e0f2690c81f25e2c4cf6d4f7ce1c',144    'x-statsig-id': 'ZTpUeXBlRXJyb3I6IENhbm5vdCByZWFkIHByb3BlcnRpZXMgb2YgbnVsbCAocmVhZGluZyAndGl0bGVbMV0nKQ=='# 暂用固定x-statsig-id,失效再看145}146 147class AuthTokenManager:148    def __init__(self):149        self.token_model_map = {}150        self.expired_tokens = set()151        self.token_status_map = {}152 153        self.model_config = {154            "grok-2": {155                "RequestFrequency": 30,156                "ExpirationTime": 1 * 60 * 60 * 1000  # 1小时157            },158            "grok-3": {159                "RequestFrequency": 20,160                "ExpirationTime": 2 * 60 * 60 * 1000  # 2小时161            },162            "grok-3-deepsearch": {163                "RequestFrequency": 10,164                "ExpirationTime": 24 * 60 * 60 * 1000  # 24小时165            },166            "grok-3-deepersearch": {167                "RequestFrequency": 3,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        self.load_token_status() # 加载令牌状态178    def save_token_status(self):179        try:        180            with open(CONFIG["TOKEN_STATUS_FILE"], 'w', encoding='utf-8') as f:181                json.dump(self.token_status_map, f, indent=2, ensure_ascii=False)182            logger.info("令牌状态已保存到配置文件", "TokenManager")183        except Exception as error:184            logger.error(f"保存令牌状态失败: {str(error)}", "TokenManager")185            186    def load_token_status(self):187        try:188            token_status_file = Path(CONFIG["TOKEN_STATUS_FILE"])189            if token_status_file.exists():190                with open(token_status_file, 'r', encoding='utf-8') as f:191                    self.token_status_map = json.load(f)192                logger.info("已从配置文件加载令牌状态", "TokenManager")193        except Exception as error:194            logger.error(f"加载令牌状态失败: {str(error)}", "TokenManager")195    def add_token(self, token,isinitialization=False):196        sso = token.split("sso=")[1].split(";")[0]197        for model in self.model_config.keys():198            if model not in self.token_model_map:199                self.token_model_map[model] = []200            if sso not in self.token_status_map:201                self.token_status_map[sso] = {}202 203            existing_token_entry = next((entry for entry in self.token_model_map[model] if entry["token"] == token), None)204 205            if not existing_token_entry:206                self.token_model_map[model].append({207                    "token": token,208                    "RequestCount": 0,209                    "AddedTime": int(time.time() * 1000),210                    "StartCallTime": None211                })212 213                if model not in self.token_status_map[sso]:214                    self.token_status_map[sso][model] = {215                        "isValid": True,216                        "invalidatedTime": None,217                        "totalRequestCount": 0218                    }219        if not isinitialization:220            self.save_token_status()221 222    def set_token(self, token):223        models = list(self.model_config.keys())224        self.token_model_map = {model: [{225            "token": token,226            "RequestCount": 0,227            "AddedTime": int(time.time() * 1000),228            "StartCallTime": None229        }] for model in models}230 231        sso = token.split("sso=")[1].split(";")[0]232        self.token_status_map[sso] = {model: {233            "isValid": True,234            "invalidatedTime": None,235            "totalRequestCount": 0236        } for model in models}237 238    def delete_token(self, token):239        try:240            sso = token.split("sso=")[1].split(";")[0]241            for model in self.token_model_map:242                self.token_model_map[model] = [entry for entry in self.token_model_map[model] if entry["token"] != token]243 244            if sso in self.token_status_map:245                del self.token_status_map[sso]246            247            self.save_token_status()248 249            logger.info(f"令牌已成功移除: {token}", "TokenManager")250            return True251        except Exception as error:252            logger.error(f"令牌删除失败: {str(error)}")253            return False254    def reduce_token_request_count(self, model_id, count):255        try:256            normalized_model = self.normalize_model_name(model_id)257            258            if normalized_model not in self.token_model_map:259                logger.error(f"模型 {normalized_model} 不存在", "TokenManager")260                return False261                262            if not self.token_model_map[normalized_model]:263                logger.error(f"模型 {normalized_model} 没有可用的token", "TokenManager")264                return False265                266            token_entry = self.token_model_map[normalized_model][0]267            268            # 确保RequestCount不会小于0269            new_count = max(0, token_entry["RequestCount"] - count)270            reduction = token_entry["RequestCount"] - new_count271            272            token_entry["RequestCount"] = new_count273            274            # 更新token状态275            if token_entry["token"]: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                    self.token_status_map[sso][normalized_model]["totalRequestCount"] = max(279                        0, 280                        self.token_status_map[sso][normalized_model]["totalRequestCount"] - reduction281                    )282            return True283            284        except Exception as error:285            logger.error(f"重置校对token请求次数时发生错误: {str(error)}", "TokenManager")286            return False287    def get_next_token_for_model(self, model_id, is_return=False):288        normalized_model = self.normalize_model_name(model_id)289 290        if normalized_model not in self.token_model_map or not self.token_model_map[normalized_model]:291            return None292 293        token_entry = self.token_model_map[normalized_model][0]294        if is_return:295            return token_entry["token"]296 297        if token_entry:298            if token_entry["StartCallTime"] is None:299                token_entry["StartCallTime"] = int(time.time() * 1000)300 301            if not self.token_reset_switch:302                self.start_token_reset_process()303                self.token_reset_switch = True304 305            token_entry["RequestCount"] += 1306 307            if token_entry["RequestCount"] > self.model_config[normalized_model]["RequestFrequency"]:308                self.remove_token_from_model(normalized_model, token_entry["token"])309                next_token_entry = self.token_model_map[normalized_model][0] if self.token_model_map[normalized_model] else None310                return next_token_entry["token"] if next_token_entry else None311 312            sso = token_entry["token"].split("sso=")[1].split(";")[0]313            if sso in self.token_status_map and normalized_model in self.token_status_map[sso]:314                if token_entry["RequestCount"] == self.model_config[normalized_model]["RequestFrequency"]:315                    self.token_status_map[sso][normalized_model]["isValid"] = False316                    self.token_status_map[sso][normalized_model]["invalidatedTime"] = int(time.time() * 1000)317                self.token_status_map[sso][normalized_model]["totalRequestCount"] += 1318 319                self.save_token_status()320 321            return token_entry["token"]322 323        return None324 325    def remove_token_from_model(self, model_id, token):326        normalized_model = self.normalize_model_name(model_id)327 328        if normalized_model not in self.token_model_map:329            logger.error(f"模型 {normalized_model} 不存在", "TokenManager")330            return False331 332        model_tokens = self.token_model_map[normalized_model]333        token_index = next((i for i, entry in enumerate(model_tokens) if entry["token"] == token), -1)334 335        if token_index != -1:336            removed_token_entry = model_tokens.pop(token_index)337            self.expired_tokens.add((338                removed_token_entry["token"],339                normalized_model,340                int(time.time() * 1000)341            ))342 343            if not self.token_reset_switch:344                self.start_token_reset_process()345                self.token_reset_switch = True346 347            logger.info(f"模型{model_id}的令牌已失效,已成功移除令牌: {token}", "TokenManager")348            return True349 350        logger.error(f"在模型 {normalized_model} 中未找到 token: {token}", "TokenManager")351        return False352 353    def get_expired_tokens(self):354        return list(self.expired_tokens)355 356    def normalize_model_name(self, model):357        if model.startswith('grok-') and 'deepsearch' not in model and 'reasoning' not in model:358            return '-'.join(model.split('-')[:2])359        return model360 361    def get_token_count_for_model(self, model_id):362        normalized_model = self.normalize_model_name(model_id)363        return len(self.token_model_map.get(normalized_model, []))364 365    def get_remaining_token_request_capacity(self):366        remaining_capacity_map = {}367 368        for model in self.model_config.keys():369            model_tokens = self.token_model_map.get(model, [])370            model_request_frequency = self.model_config[model]["RequestFrequency"]371 372            total_used_requests = sum(token_entry.get("RequestCount", 0) for token_entry in model_tokens)373 374            remaining_capacity = (len(model_tokens) * model_request_frequency) - total_used_requests375            remaining_capacity_map[model] = max(0, remaining_capacity)376 377        return remaining_capacity_map378 379    def get_token_array_for_model(self, model_id):380        normalized_model = self.normalize_model_name(model_id)381        return self.token_model_map.get(normalized_model, [])382 383    def start_token_reset_process(self):384        def reset_expired_tokens():385            now = int(time.time() * 1000)386 387            tokens_to_remove = set()388            for token_info in self.expired_tokens:389                token, model, expired_time = token_info390                expiration_time = self.model_config[model]["ExpirationTime"]391 392                if now - expired_time >= expiration_time:393                    if not any(entry["token"] == token for entry in self.token_model_map.get(model, [])):394                        if model not in self.token_model_map:395                            self.token_model_map[model] = []396 397                        self.token_model_map[model].append({398                            "token": token,399                            "RequestCount": 0,400                            "AddedTime": now,401                            "StartCallTime": None402                        })403 404                    sso = token.split("sso=")[1].split(";")[0]405                    if sso in self.token_status_map and model in self.token_status_map[sso]:406                        self.token_status_map[sso][model]["isValid"] = True407                        self.token_status_map[sso][model]["invalidatedTime"] = None408                        self.token_status_map[sso][model]["totalRequestCount"] = 0409 410                    tokens_to_remove.add(token_info)411 412            self.expired_tokens -= tokens_to_remove413 414            for model in self.model_config.keys():415                if model not in self.token_model_map:416                    continue417 418                for token_entry in self.token_model_map[model]:419                    if not token_entry.get("StartCallTime"):420                        continue421 422                    expiration_time = self.model_config[model]["ExpirationTime"]423                    if now - token_entry["StartCallTime"] >= expiration_time:424                        sso = token_entry["token"].split("sso=")[1].split(";")[0]425                        if sso in self.token_status_map and model in self.token_status_map[sso]:426                            self.token_status_map[sso][model]["isValid"] = True427                            self.token_status_map[sso][model]["invalidatedTime"] = None428                            self.token_status_map[sso][model]["totalRequestCount"] = 0429 430                        token_entry["RequestCount"] = 0431                        token_entry["StartCallTime"] = None432 433        import threading434        # 启动一个线程执行定时任务,每小时执行一次435        def run_timer():436            while True:437                reset_expired_tokens()438                time.sleep(3600)439 440        timer_thread = threading.Thread(target=run_timer)441        timer_thread.daemon = True442        timer_thread.start()443 444    def get_all_tokens(self):445        all_tokens = set()446        for model_tokens in self.token_model_map.values():447            for entry in model_tokens:448                all_tokens.add(entry["token"])449        return list(all_tokens)450    def get_current_token(self, model_id):451        normalized_model = self.normalize_model_name(model_id)452 453        if normalized_model not in self.token_model_map or not self.token_model_map[normalized_model]:454            return None455 456        token_entry = self.token_model_map[normalized_model][0]457        return token_entry["token"]458 459    def get_token_status_map(self):460        return self.token_status_map461 462class Utils:463    @staticmethod464    def organize_search_results(search_results):465        if not search_results or 'results' not in search_results:466            return ''467 468        results = search_results['results']469        formatted_results = []470 471        for index, result in enumerate(results):472            title = result.get('title', '未知标题')473            url = result.get('url', '#')474            preview = result.get('preview', '无预览内容')475 476            formatted_result = f"\r\n<details><summary>资料[{index}]: {title}</summary>\r\n{preview}\r\n\n[Link]({url})\r\n</details>"477            formatted_results.append(formatted_result)478 479        return '\n\n'.join(formatted_results)480 481    @staticmethod482    def create_auth_headers(model, is_return=False):483        return token_manager.get_next_token_for_model(model, is_return)484 485    @staticmethod486    def get_proxy_options():487        proxy = CONFIG["API"]["PROXY"]488        proxy_options = {}489 490        if proxy:491            logger.info(f"使用代理: {proxy}", "Server")492            493            if proxy.startswith("socks5://"):494                proxy_options["proxy"] = proxy495            496                if '@' in proxy:497                    auth_part = proxy.split('@')[0].split('://')[1]498                    if ':' in auth_part:499                        username, password = auth_part.split(':')500                        proxy_options["proxy_auth"] = (username, password)501            else:502                proxy_options["proxies"] = {"https": proxy, "http": proxy}     503        return proxy_options504 505class GrokApiClient:506    def __init__(self, model_id):507        if model_id not in CONFIG["MODELS"]:508            raise ValueError(f"不支持的模型: {model_id}")509        self.model_id = CONFIG["MODELS"][model_id]510 511    def process_message_content(self, content):512        if isinstance(content, str):513            return content514        return None515 516    def get_image_type(self, base64_string):517        mime_type = 'image/jpeg'518        if 'data:image' in base64_string:519            import re520            matches = re.search(r'data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+);base64,', base64_string)521            if matches:522                mime_type = matches.group(1)523 524        extension = mime_type.split('/')[1]525        file_name = f"image.{extension}"526 527        return {528            "mimeType": mime_type,529            "fileName": file_name530        }531    def upload_base64_file(self, message, model):532        try:533            message_base64 = base64.b64encode(message.encode('utf-8')).decode('utf-8')534            upload_data = {535                "fileName": "message.txt",536                "fileMimeType": "text/plain",537                "content": message_base64538            }539 540            logger.info("发送文字文件请求", "Server")541            cookie = f"{Utils.create_auth_headers(model, True)};{CONFIG['SERVER']['CF_CLEARANCE']}" 542            proxy_options = Utils.get_proxy_options()543            response = curl_requests.post(544                "https://grok.com/rest/app-chat/upload-file",545                headers={546                    **DEFAULT_HEADERS,547                    "Cookie":cookie548                },549                json=upload_data,550                impersonate="chrome133a",551                **proxy_options552            )553 554            if response.status_code != 200:555                logger.error(f"上传文件失败,状态码:{response.status_code}", "Server")556                raise Exception(f"上传文件失败,状态码:{response.status_code}")557 558            result = response.json()559            logger.info(f"上传文件成功: {result}", "Server")560            return result.get("fileMetadataId", "")561 562        except Exception as error:563            logger.error(str(error), "Server")564            raise Exception(f"上传文件失败,状态码:{response.status_code}")565    def upload_base64_image(self, base64_data, url):566        try:567            if 'data:image' in base64_data:568                image_buffer = base64_data.split(',')[1]569            else:570                image_buffer = base64_data571 572            image_info = self.get_image_type(base64_data)573            mime_type = image_info["mimeType"]574            file_name = image_info["fileName"]575 576            upload_data = {577                "rpc": "uploadFile",578                "req": {579                    "fileName": file_name,580                    "fileMimeType": mime_type,581                    "content": image_buffer582                }583            }584 585            logger.info("发送图片请求", "Server")586 587            proxy_options = Utils.get_proxy_options()588            response = curl_requests.post(589                url,590                headers={591                    **DEFAULT_HEADERS,592                    "Cookie":CONFIG["SERVER"]['COOKIE']593                },594                json=upload_data,595                impersonate="chrome133a",596                **proxy_options597            )598 599            if response.status_code != 200:600                logger.error(f"上传图片失败,状态码:{response.status_code}", "Server")601                return ''602 603            result = response.json()604            logger.info(f"上传图片成功: {result}", "Server")605            return result.get("fileMetadataId", "")606 607        except Exception as error:608            logger.error(str(error), "Server")609            return ''610    # def convert_system_messages(self, messages):611    #     try:612    #         system_prompt = []613    #         i = 0614    #         while i < len(messages):615    #             if messages[i].get('role') != 'system':616    #                 break617 618    #             system_prompt.append(self.process_message_content(messages[i].get('content')))619    #             i += 1620 621    #         messages = messages[i:]622    #         system_prompt = '\n'.join(system_prompt)623 624    #         if not messages:625    #             raise ValueError("没有找到用户或者AI消息")626    #         return {"system_prompt":system_prompt,"messages":messages}627    #     except Exception as error:628    #         logger.error(str(error), "Server")629    #         raise ValueError(error)630    def prepare_chat_request(self, request):631        if ((request["model"] == 'grok-2-imageGen' or request["model"] == 'grok-3-imageGen') and632            not CONFIG["API"]["PICGO_KEY"] and not CONFIG["API"]["TUMY_KEY"] and633            request.get("stream", False)):634            raise ValueError("该模型流式输出需要配置PICGO或者TUMY图床密钥!")635 636        # system_message, todo_messages = self.convert_system_messages(request["messages"]).values()637        todo_messages = request["messages"]638        if request["model"] in ['grok-2-imageGen', 'grok-3-imageGen', 'grok-3-deepsearch']:639            last_message = todo_messages[-1]640            if last_message["role"] != 'user':641                raise ValueError('此模型最后一条消息必须是用户消息!')642            todo_messages = [last_message]643        file_attachments = []644        messages = ''645        last_role = None646        last_content = ''647        message_length = 0648        convert_to_file = False649        last_message_content = ''650        search = request["model"] in ['grok-2-search', 'grok-3-search']651        deepsearchPreset = ''652        if request["model"] == 'grok-3-deepsearch':653            deepsearchPreset = 'default'654        elif request["model"] == 'grok-3-deepersearch':655            deepsearchPreset = 'deeper'656 657        # 移除<think>标签及其内容和base64图片658        def remove_think_tags(text):659            import re660            text = re.sub(r'<think>[\s\S]*?<\/think>', '', text).strip()661            text = re.sub(r'!\[image\]\(data:.*?base64,.*?\)', '[图片]', text)662            return text663 664        def process_content(content):665            if isinstance(content, list):666                text_content = ''667                for item in content:668                    if item["type"] == 'image_url':669                        text_content += ("[图片]" if not text_content else '\n[图片]')670                    elif item["type"] == 'text':671                        text_content += (remove_think_tags(item["text"]) if not text_content else '\n' + remove_think_tags(item["text"]))672                return text_content673            elif isinstance(content, dict) and content is not None:674                if content["type"] == 'image_url':675                    return "[图片]"676                elif content["type"] == 'text':677                    return remove_think_tags(content["text"])678            return remove_think_tags(self.process_message_content(content))679        for current in todo_messages:680            role = 'assistant' if current["role"] == 'assistant' else 'user'681            is_last_message = current == todo_messages[-1]682 683            if is_last_message and "content" in current:684                if isinstance(current["content"], list):685                    for item in current["content"]:686                        if item["type"] == 'image_url':687                            processed_image = self.upload_base64_image(688                                item["image_url"]["url"],689                                f"{CONFIG['API']['BASE_URL']}/api/rpc"690                            )691                            if processed_image:692                                file_attachments.append(processed_image)693                elif isinstance(current["content"], dict) and current["content"].get("type") == 'image_url':694                    processed_image = self.upload_base64_image(695                        current["content"]["image_url"]["url"],696                        f"{CONFIG['API']['BASE_URL']}/api/rpc"697                    )698                    if processed_image:699                        file_attachments.append(processed_image)700 701 702            text_content = process_content(current.get("content", ""))703            if is_last_message and convert_to_file:704                last_message_content = f"{role.upper()}: {text_content or '[图片]'}\n"705                continue706            if text_content or (is_last_message and file_attachments):707                if role == last_role and text_content:708                    last_content += '\n' + text_content709                    messages = messages[:messages.rindex(f"{role.upper()}: ")] + f"{role.upper()}: {last_content}\n"710                else:711                    messages += f"{role.upper()}: {text_content or '[图片]'}\n"712                    last_content = text_content713                    last_role = role714            message_length += len(messages)715            if message_length >= 40000:716                convert_to_file = True717               718        if convert_to_file:719            file_id = self.upload_base64_file(messages, request["model"])720            if file_id:721                file_attachments.insert(0, file_id)722            messages = last_message_content.strip()723        if messages.strip() == '':724            if convert_to_file:725                messages = '基于txt文件内容进行回复:'726            else:727                raise ValueError('消息内容为空!')728        return {729            "temporary": CONFIG["API"].get("IS_TEMP_CONVERSATION", False),730            "modelName": self.model_id,731            "message": messages.strip(),732            "fileAttachments": file_attachments[:4],733            "imageAttachments": [],734            "disableSearch": False,735            "enableImageGeneration": True,736            "returnImageBytes": False,737            "returnRawGrokInXaiRequest": False,738            "enableImageStreaming": False,739            "imageGenerationCount": 1,740            "forceConcise": False,741            "toolOverrides": {742                "imageGen": request["model"] in ['grok-2-imageGen', 'grok-3-imageGen'],743                "webSearch": search,744                "xSearch": search,745                "xMediaSearch": search,746                "trendsSearch": search,747                "xPostAnalyze": search748            },749            "enableSideBySide": True,750            "sendFinalMetadata": True,751            "customPersonality": "",752            "deepsearchPreset": deepsearchPreset,753            "isReasoning": request["model"] == 'grok-3-reasoning',754            "disableTextFollowUps": True755        }756 757class MessageProcessor:758    @staticmethod759    def create_chat_response(message, model, is_stream=False):760        base_response = {761            "id": f"chatcmpl-{uuid.uuid4()}",762            "created": int(time.time()),763            "model": model764        }765 766        if is_stream:767            return {768                **base_response,769                "object": "chat.completion.chunk",770                "choices": [{771                    "index": 0,772                    "delta": {773                        "content": message774                    }775                }]776            }777 778        return {779            **base_response,780            "object": "chat.completion",781            "choices": [{782                "index": 0,783                "message": {784                    "role": "assistant",785                    "content": message786                },787                "finish_reason": "stop"788            }],789            "usage": None790        }791 792def process_model_response(response, model):793    result = {"token": None, "imageUrl": None}794 795    if CONFIG["IS_IMG_GEN"]:796        if response.get("cachedImageGenerationResponse") and not CONFIG["IS_IMG_GEN2"]:797            result["imageUrl"] = response["cachedImageGenerationResponse"]["imageUrl"]798        return result799 800    if model == 'grok-2':801        result["token"] = response.get("token")802    elif model in ['grok-2-search', 'grok-3-search']:803        if response.get("webSearchResults") and CONFIG["ISSHOW_SEARCH_RESULTS"]:804            result["token"] = f"\r\n<think>{Utils.organize_search_results(response['webSearchResults'])}</think>\r\n"805        else:806            result["token"] = response.get("token")807    elif model == 'grok-3':808        result["token"] = response.get("token")809    elif model in ['grok-3-deepsearch', 'grok-3-deepersearch']:810        if response.get("messageStepId") and not CONFIG["SHOW_THINKING"]:811            return result812        if response.get("messageStepId") and not CONFIG["IS_THINKING"]:813            result["token"] = "<think>" + response.get("token", "")814            CONFIG["IS_THINKING"] = True815        elif not response.get("messageStepId") and CONFIG["IS_THINKING"] and response.get("messageTag") == "final":816            result["token"] = "</think>" + response.get("token", "")817            CONFIG["IS_THINKING"] = False818        elif (response.get("messageStepId") and CONFIG["IS_THINKING"] and response.get("messageTag") == "assistant") or response.get("messageTag") == "final":819            result["token"] = response.get("token","")820        elif (CONFIG["IS_THINKING"] and response.get("token","").get("action","") == "webSearch"):821            result["token"] = response.get("token","").get("action_input","").get("query","")            822        elif (CONFIG["IS_THINKING"] and response.get("webSearchResults")):823            result["token"] = Utils.organize_search_results(response['webSearchResults'])824    elif model == 'grok-3-reasoning':825        if response.get("isThinking") and not CONFIG["SHOW_THINKING"]:826            return result827 828        if response.get("isThinking") and not CONFIG["IS_THINKING"]:829            result["token"] = "<think>" + response.get("token", "")830            CONFIG["IS_THINKING"] = True831        elif not response.get("isThinking") and CONFIG["IS_THINKING"]:832            result["token"] = "</think>" + response.get("token", "")833            CONFIG["IS_THINKING"] = False834        else:835            result["token"] = response.get("token")836 837    return result838 839def handle_image_response(image_url):840    max_retries = 2841    retry_count = 0842    image_base64_response = None843 844    while retry_count < max_retries:845        try:846            proxy_options = Utils.get_proxy_options()847            image_base64_response = curl_requests.get(848                f"https://assets.grok.com/{image_url}",849                headers={850                    **DEFAULT_HEADERS,851                    "Cookie":CONFIG["SERVER"]['COOKIE']852                },853                impersonate="chrome133a",854                **proxy_options855            )856 857            if image_base64_response.status_code == 200:858                break859 860            retry_count += 1861            if retry_count == max_retries:862                raise Exception(f"上游服务请求失败! status: {image_base64_response.status_code}")863 864            time.sleep(CONFIG["API"]["RETRY_TIME"] / 1000 * retry_count)865 866        except Exception as error:867            logger.error(str(error), "Server")868            retry_count += 1869            if retry_count == max_retries:870                raise871 872            time.sleep(CONFIG["API"]["RETRY_TIME"] / 1000 * retry_count)873 874    image_buffer = image_base64_response.content875 876    if not CONFIG["API"]["PICGO_KEY"] and not CONFIG["API"]["TUMY_KEY"]:877        base64_image = base64.b64encode(image_buffer).decode('utf-8')878        image_content_type = image_base64_response.headers.get('content-type', 'image/jpeg')879        return f"![image](data:{image_content_type};base64,{base64_image})"880 881    logger.info("开始上传图床", "Server")882 883    if CONFIG["API"]["PICGO_KEY"]:884        files = {'source': ('image.jpg', image_buffer, 'image/jpeg')}885        headers = {886            "X-API-Key": CONFIG["API"]["PICGO_KEY"]887        }888 889        response_url = requests.post(890            "https://www.picgo.net/api/1/upload",891            files=files,892            headers=headers893        )894 895        if response_url.status_code != 200:896            return "生图失败,请查看PICGO图床密钥是否设置正确"897        else:898            logger.info("生图成功", "Server")899            result = response_url.json()900            return f"![image]({result['image']['url']})"901 902 903    elif CONFIG["API"]["TUMY_KEY"]:904        files = {'file': ('image.jpg', image_buffer, 'image/jpeg')}905        headers = {906            "Accept": "application/json",907            'Authorization': f"Bearer {CONFIG['API']['TUMY_KEY']}"908        }909 910        response_url = requests.post(911            "https://tu.my/api/v1/upload",912            files=files,913            headers=headers914        )915 916        if response_url.status_code != 200:917            return "生图失败,请查看TUMY图床密钥是否设置正确"918        else:919            try:920                result = response_url.json()921                logger.info("生图成功", "Server")922                return f"![image]({result['data']['links']['url']})"923            except Exception as error:924                logger.error(str(error), "Server")925                return "生图失败,请查看TUMY图床密钥是否设置正确"926 927def handle_non_stream_response(response, model):928    try:929        logger.info("开始处理非流式响应", "Server")930 931        stream = response.iter_lines()932        full_response = ""933 934        CONFIG["IS_THINKING"] = False935        CONFIG["IS_IMG_GEN"] = False936        CONFIG["IS_IMG_GEN2"] = False937 938        for chunk in stream:939            if not chunk:940                continue941            try:942                line_json = json.loads(chunk.decode("utf-8").strip())943                if line_json.get("error"):944                    logger.error(json.dumps(line_json, indent=2), "Server")945                    return json.dumps({"error": "RateLimitError"}) + "\n\n"946 947                response_data = line_json.get("result", {}).get("response")948                if not response_data:949                    continue950 951                if response_data.get("doImgGen") or response_data.get("imageAttachmentInfo"):952                    CONFIG["IS_IMG_GEN"] = True953 954                result = process_model_response(response_data, model)955 956                if result["token"]:957                    full_response += result["token"]958 959                if result["imageUrl"]:960                    CONFIG["IS_IMG_GEN2"] = True961                    return handle_image_response(result["imageUrl"])962 963            except json.JSONDecodeError:964                continue965            except Exception as e:966                logger.error(f"处理流式响应行时出错: {str(e)}", "Server")967                continue968 969        return full_response970    except Exception as error:971        logger.error(str(error), "Server")972        raise973def handle_stream_response(response, model):974    def generate():975        logger.info("开始处理流式响应", "Server")976 977        stream = response.iter_lines()978        CONFIG["IS_THINKING"] = False979        CONFIG["IS_IMG_GEN"] = False980        CONFIG["IS_IMG_GEN2"] = False981 982        for chunk in stream:983            if not chunk:984                continue985            try:986                line_json = json.loads(chunk.decode("utf-8").strip())987                print(line_json)988                if line_json.get("error"):989                    logger.error(json.dumps(line_json, indent=2), "Server")990                    yield json.dumps({"error": "RateLimitError"}) + "\n\n"991                    return992 993                response_data = line_json.get("result", {}).get("response")994                if not response_data:995                    continue996 997                if response_data.get("doImgGen") or response_data.get("imageAttachmentInfo"):998                    CONFIG["IS_IMG_GEN"] = True999 1000                result = process_model_response(response_data, model)1001 1002                if result["token"]:1003                    yield f"data: {json.dumps(MessageProcessor.create_chat_response(result['token'], model, True))}\n\n"1004 1005                if result["imageUrl"]:1006                    CONFIG["IS_IMG_GEN2"] = True1007                    image_data = handle_image_response(result["imageUrl"])1008                    yield f"data: {json.dumps(MessageProcessor.create_chat_response(image_data, model, True))}\n\n"1009 1010            except json.JSONDecodeError:1011                continue1012            except Exception as e:1013                logger.error(f"处理流式响应行时出错: {str(e)}", "Server")1014                continue1015 1016        yield "data: [DONE]\n\n"1017    return generate()1018 1019def initialization():1020    sso_array = os.environ.get("SSO", "").split(',')1021    logger.info("开始加载令牌", "Server")1022    token_manager.load_token_status()1023    for sso in sso_array:1024        if sso:1025            token_manager.add_token(f"sso-rw={sso};sso={sso}",True)1026    token_manager.save_token_status()1027 1028    logger.info(f"成功加载令牌: {json.dumps(token_manager.get_all_tokens(), indent=2)}", "Server")1029    logger.info(f"令牌加载完成,共加载: {len(token_manager.get_all_tokens())}个令牌", "Server")1030 1031    if CONFIG["API"]["PROXY"]:1032        logger.info(f"代理已设置: {CONFIG['API']['PROXY']}", "Server")1033 1034logger.info("初始化完成", "Server")1035 1036 1037app = Flask(__name__)1038app.wsgi_app = ProxyFix(app.wsgi_app)1039app.secret_key = os.environ.get('FLASK_SECRET_KEY') or secrets.token_hex(16)1040app.json.sort_keys = False1041 1042@app.route('/manager/login', methods=['GET', 'POST'])1043def manager_login():1044    if CONFIG["ADMIN"]["MANAGER_SWITCH"]:1045        if request.method == 'POST':1046            password = request.form.get('password')1047            if password == CONFIG["ADMIN"]["PASSWORD"]:1048                session['is_logged_in'] = True1049                return redirect('/manager')1050            return render_template('login.html', error=True)1051        return render_template('login.html', error=False)1052    else:1053        return redirect('/')1054 1055def check_auth():1056    return session.get('is_logged_in', False)1057 1058@app.route('/manager')1059def manager():1060    if not check_auth():1061        return redirect('/manager/login')1062    return render_template('manager.html')1063 1064@app.route('/manager/api/get')1065def get_manager_tokens():1066    if not check_auth():1067        return jsonify({"error": "Unauthorized"}), 4011068    return jsonify(token_manager.get_token_status_map())1069 1070@app.route('/manager/api/add', methods=['POST'])1071def add_manager_token():1072    if not check_auth():1073        return jsonify({"error": "Unauthorized"}), 4011074    try:1075        sso = request.json.get('sso')1076        if not sso:1077            return jsonify({"error": "SSO token is required"}), 4001078        token_manager.add_token(f"sso-rw={sso};sso={sso}")1079        return jsonify({"success": True})1080    except Exception as e:1081        return jsonify({"error": str(e)}), 5001082 1083@app.route('/manager/api/delete', methods=['POST'])1084def delete_manager_token():1085    if not check_auth():1086        return jsonify({"error": "Unauthorized"}), 4011087    try:1088        sso = request.json.get('sso')1089        if not sso:1090            return jsonify({"error": "SSO token is required"}), 4001091        token_manager.delete_token(f"sso-rw={sso};sso={sso}")1092        return jsonify({"success": True})1093    except Exception as e:1094        return jsonify({"error": str(e)}), 5001095    1096@app.route('/manager/api/cf_clearance', methods=['POST'])   1097def setCf_Manager_clearance():1098    if not check_auth():1099        return jsonify({"error": "Unauthorized"}), 4011100    try:1101        cf_clearance = request.json.get('cf_clearance')1102        if not cf_clearance:1103            return jsonify({"error": "cf_clearance is required"}), 4001104        CONFIG["SERVER"]['CF_CLEARANCE'] = cf_clearance1105        return jsonify({"success": True})1106    except Exception as e:1107        return jsonify({"error": str(e)}), 5001108 1109 1110@app.route('/get/tokens', methods=['GET'])1111def get_tokens():1112    auth_token = request.headers.get('Authorization', '').replace('Bearer ', '')1113    if CONFIG["API"]["IS_CUSTOM_SSO"]:1114        return jsonify({"error": '自定义的SSO令牌模式无法获取轮询sso令牌状态'}), 4031115    elif auth_token != CONFIG["API"]["API_KEY"]:1116        return jsonify({"error": 'Unauthorized'}), 4011117    return jsonify(token_manager.get_token_status_map())1118 1119@app.route('/add/token', methods=['POST'])1120def add_token():1121    auth_token = request.headers.get('Authorization', '').replace('Bearer ', '')1122    if CONFIG["API"]["IS_CUSTOM_SSO"]:1123        return jsonify({"error": '自定义的SSO令牌模式无法添加sso令牌'}), 4031124    elif auth_token != CONFIG["API"]["API_KEY"]:1125        return jsonify({"error": 'Unauthorized'}), 4011126 1127    try:1128        sso = request.json.get('sso')1129        token_manager.add_token(f"sso-rw={sso};sso={sso}")1130        return jsonify(token_manager.get_token_status_map().get(sso, {})), 2001131    except Exception as error:1132        logger.error(str(error), "Server")1133        return jsonify({"error": '添加sso令牌失败'}), 5001134    1135@app.route('/set/cf_clearance', methods=['POST'])1136def setCf_clearance():1137    auth_token = request.headers.get('Authorization', '').replace('Bearer ', '')1138    if auth_token != CONFIG["API"]["API_KEY"]:1139        return jsonify({"error": 'Unauthorized'}), 4011140    try:1141        cf_clearance = request.json.get('cf_clearance')1142        CONFIG["SERVER"]['CF_CLEARANCE'] = cf_clearance1143        return jsonify({"message": '设置cf_clearance成功'}), 2001144    except Exception as error:1145        logger.error(str(error), "Server")1146        return jsonify({"error": '设置cf_clearance失败'}), 5001147    1148@app.route('/delete/token', methods=['POST'])1149def delete_token():1150    auth_token = request.headers.get('Authorization', '').replace('Bearer ', '')1151    if CONFIG["API"]["IS_CUSTOM_SSO"]:1152        return jsonify({"error": '自定义的SSO令牌模式无法删除sso令牌'}), 4031153    elif auth_token != CONFIG["API"]["API_KEY"]:1154        return jsonify({"error": 'Unauthorized'}), 4011155 1156    try:1157        sso = request.json.get('sso')1158        token_manager.delete_token(f"sso-rw={sso};sso={sso}")1159        return jsonify({"message": '删除sso令牌成功'}), 2001160    except Exception as error:1161        logger.error(str(error), "Server")1162        return jsonify({"error": '删除sso令牌失败'}), 5001163 1164@app.route('/v1/models', methods=['GET'])1165def get_models():1166    return jsonify({1167        "object": "list",1168        "data": [1169            {1170                "id": model,1171                "object": "model",1172                "created": int(time.time()),1173                "owned_by": "grok"1174            }1175            for model in CONFIG["MODELS"].keys()1176        ]1177    })1178 1179@app.route('/v1/chat/completions', methods=['POST'])1180def chat_completions():1181    response_status_code = 5001182    try:1183        auth_token = request.headers.get('Authorization',1184                                         '').replace('Bearer ', '')1185        if auth_token:1186            if CONFIG["API"]["IS_CUSTOM_SSO"]:1187                result = f"sso={auth_token};sso-rw={auth_token}"1188                token_manager.set_token(result)1189            elif auth_token != CONFIG["API"]["API_KEY"]:1190                return jsonify({"error": 'Unauthorized'}), 4011191        else:1192            return jsonify({"error": 'API_KEY缺失'}), 4011193 1194        data = request.json1195        model = data.get("model")1196        stream = data.get("stream", False)1197 1198        retry_count = 01199        grok_client = GrokApiClient(model)1200        request_payload = grok_client.prepare_chat_request(data)

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