CoolFace
Apppublic

Tamanskaya/abacus_chat_proxy

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
app.py1328 linesDownload Raw Back to root
1from flask import Flask, request, jsonify, Response, render_template_string, render_template, redirect, url_for, session as flask_session2import requests3import time4import json5import uuid6import random7import io8import re9from functools import wraps10import hashlib11import jwt  12import os13import threading14from datetime import datetime, timedelta15import tiktoken  # 导入tiktoken来计算token数量16 17app = Flask(__name__, template_folder='templates')18app.secret_key = os.environ.get("SECRET_KEY", "abacus_chat_proxy_secret_key")19app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7)20 21 22API_ENDPOINT_URL = "https://abacus.ai/api/v0/describeDeployment"23MODEL_LIST_URL = "https://abacus.ai/api/v0/listExternalApplications"24CHAT_URL = "https://apps.abacus.ai/api/_chatLLMSendMessageSSE"25USER_INFO_URL = "https://abacus.ai/api/v0/_getUserInfo"26COMPUTE_POINTS_URL = "https://apps.abacus.ai/api/_getOrganizationComputePoints"27COMPUTE_POINTS_LOG_URL = "https://abacus.ai/api/v0/_getOrganizationComputePointLog"28CREATE_CONVERSATION_URL = "https://apps.abacus.ai/api/createDeploymentConversation"29 30 31USER_AGENTS = [32    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36"33]34 35 36PASSWORD = None37USER_NUM = 038USER_DATA = []39CURRENT_USER = -140MODELS = set()41 42 43TRACE_ID = "3042e28b3abf475d8d973c7e904935af"44SENTRY_TRACE = f"{TRACE_ID}-80d9d2538b2682d0"45 46 47# 添加一个计数器记录健康检查次数48health_check_counter = 049 50 51# 添加统计变量52model_usage_stats = {}  # 模型使用次数统计53total_tokens = {54    "prompt": 0,       # 输入token统计55    "completion": 0,   # 输出token统计56    "total": 0         # 总token统计57}58 59# 模型调用记录60model_usage_records = []  # 每次调用详细记录61MODEL_USAGE_RECORDS_FILE = "/app/model_usage_records.json"  # 调用记录保存文件,使用/app目录确保HF中持久化62 63# 计算点信息64compute_points = {65    "left": 0,          # 剩余计算点66    "total": 0,         # 总计算点67    "used": 0,          # 已使用计算点68    "percentage": 0,    # 使用百分比69    "last_update": None # 最后更新时间70}71 72# 计算点使用日志73compute_points_log = {74    "columns": {},      # 列名75    "log": []           # 日志数据76}77 78# 多用户计算点信息79users_compute_points = []80 81# 记录启动时间82START_TIME = datetime.utcnow() + timedelta(hours=8)  # 北京时间83 84 85# 自定义JSON编码器,处理datetime对象86class DateTimeEncoder(json.JSONEncoder):87    def default(self, obj):88        if isinstance(obj, datetime):89            return obj.strftime('%Y-%m-%d %H:%M:%S')90        return super(DateTimeEncoder, self).default(obj)91 92 93# 加载模型调用记录94def load_model_usage_records():95    global model_usage_records96    try:97        if os.path.exists(MODEL_USAGE_RECORDS_FILE):98            with open(MODEL_USAGE_RECORDS_FILE, 'r', encoding='utf-8') as f:99                records = json.load(f)100                if isinstance(records, list):101                    model_usage_records = records102                    print(f"成功加载 {len(model_usage_records)} 条模型调用记录")103                else:104                    print("调用记录文件格式不正确,初始化为空列表")105    except Exception as e:106        print(f"加载模型调用记录失败: {e}")107        model_usage_records = []108 109# 保存模型调用记录110def save_model_usage_records():111    try:112        with open(MODEL_USAGE_RECORDS_FILE, 'w', encoding='utf-8') as f:113            json.dump(model_usage_records, f, ensure_ascii=False, indent=2, cls=DateTimeEncoder)114        print(f"成功保存 {len(model_usage_records)} 条模型调用记录")115    except Exception as e:116        print(f"保存模型调用记录失败: {e}")117 118 119def update_conversation_id(user_index, conversation_id):120    """更新用户的conversation_id并保存到配置文件"""121    try:122        with open("config.json", "r") as f:123            config = json.load(f)124        125        if "config" in config and user_index < len(config["config"]):126            config["config"][user_index]["conversation_id"] = conversation_id127            128            # 保存到配置文件129            with open("config.json", "w") as f:130                json.dump(config, f, indent=4)131                132            print(f"已将用户 {user_index+1} 的conversation_id更新为: {conversation_id}")133        else:134            print(f"更新conversation_id失败: 配置文件格式错误或用户索引越界")135    except Exception as e:136        print(f"更新conversation_id失败: {e}")137 138 139def resolve_config():140    # 从环境变量读取多组配置141    config_list = []142    i = 1143    while True:144        cookie = os.environ.get(f"cookie_{i}")145        if not cookie:146            break147        148        # 为每个cookie创建一个配置项,conversation_id初始为空149        config_list.append({150            "conversation_id": "",  # 初始为空,将通过get_or_create_conversation自动创建151            "cookies": cookie152        })153        i += 1154    155    # 如果环境变量存在配置,使用环境变量的配置156    if config_list:157        print(f"从环境变量加载了 {len(config_list)} 个配置")158        return config_list159    160    # 如果环境变量不存在,从文件读取161    try:162        with open("config.json", "r") as f:163            config = json.load(f)164        config_list = config.get("config")165        return config_list166    except FileNotFoundError:167        print("未找到config.json文件")168        return []169    except json.JSONDecodeError:170        print("config.json格式错误")171        return []172 173 174def get_password():175    global PASSWORD176    # 从环境变量读取密码177    env_password = os.environ.get("password")178    if env_password:179        PASSWORD = hashlib.sha256(env_password.encode()).hexdigest()180        return181 182    # 如果环境变量不存在,从文件读取183    try:184        with open("password.txt", "r") as f:185            PASSWORD = f.read().strip()186    except FileNotFoundError:187        with open("password.txt", "w") as f:188            PASSWORD = None189 190 191def require_auth(f):192    @wraps(f)193    def decorated(*args, **kwargs):194        if not PASSWORD:195            return f(*args, **kwargs)196        197        # 检查Flask会话是否已登录198        if flask_session.get('logged_in'):199            return f(*args, **kwargs)200            201        # 如果是API请求,检查Authorization头202        auth = request.authorization203        if not auth or not check_auth(auth.token):204            # 如果是浏览器请求,重定向到登录页面205            if request.headers.get('Accept', '').find('text/html') >= 0:206                return redirect(url_for('login'))207            return jsonify({"error": "Unauthorized access"}), 401208        return f(*args, **kwargs)209 210    return decorated211 212 213def check_auth(token):214    return hashlib.sha256(token.encode()).hexdigest() == PASSWORD215 216 217def is_token_expired(token):218    if not token:219        return True220    221    try:222        # Malkodi tokenon sen validigo de subskribo223        payload = jwt.decode(token, options={"verify_signature": False})224        # Akiru eksvalidiĝan tempon, konsideru eksvalidiĝinta 5 minutojn antaŭe225        return payload.get('exp', 0) - time.time() < 300226    except:227        return True228 229 230def refresh_token(session, cookies):231    """Uzu kuketon por refreŝigi session token, nur revenigu novan tokenon"""232    headers = {233        "accept": "application/json, text/plain, */*",234        "accept-language": "zh-CN,zh;q=0.9",235        "content-type": "application/json",236        "reai-ui": "1",237        "sec-ch-ua": "\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"",238        "sec-ch-ua-mobile": "?0",239        "sec-ch-ua-platform": "\"Windows\"",240        "sec-fetch-dest": "empty",241        "sec-fetch-mode": "cors",242        "sec-fetch-site": "same-site",243        "x-abacus-org-host": "apps",244        "user-agent": random.choice(USER_AGENTS),245        "origin": "https://apps.abacus.ai",246        "referer": "https://apps.abacus.ai/",247        "cookie": cookies248    }249    250    try:251        response = session.post(252            USER_INFO_URL,253            headers=headers,254            json={},255            cookies=None256        )257        258        if response.status_code == 200:259            response_data = response.json()260            if response_data.get('success') and 'sessionToken' in response_data.get('result', {}):261                return response_data['result']['sessionToken']262            else:263                print(f"刷新token失败: {response_data.get('error', '未知错误')}")264                return None265        else:266            print(f"刷新token失败,状态码: {response.status_code}")267            return None268    except Exception as e:269        print(f"刷新token异常: {e}")270        return None271 272 273def get_model_map(session, cookies, session_token):274    """Akiru disponeblan modelan liston kaj ĝiajn mapajn rilatojn"""275    headers = {276        "accept": "application/json, text/plain, */*",277        "accept-language": "zh-CN,zh;q=0.9",278        "content-type": "application/json",279        "reai-ui": "1",280        "sec-ch-ua": "\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"",281        "sec-ch-ua-mobile": "?0",282        "sec-ch-ua-platform": "\"Windows\"",283        "sec-fetch-dest": "empty",284        "sec-fetch-mode": "cors",285        "sec-fetch-site": "same-site",286        "x-abacus-org-host": "apps",287        "user-agent": random.choice(USER_AGENTS),288        "origin": "https://apps.abacus.ai",289        "referer": "https://apps.abacus.ai/",290        "cookie": cookies291    }292    293    if session_token:294        headers["session-token"] = session_token295    296    model_map = {}297    models_set = set()298    299    try:300        response = session.post(301            MODEL_LIST_URL,302            headers=headers,303            json={},304            cookies=None305        )306        307        if response.status_code != 200:308            print(f"获取模型列表失败,状态码: {response.status_code}")309            raise Exception("API请求失败")310        311        data = response.json()312        if not data.get('success'):313            print(f"获取模型列表失败: {data.get('error', '未知错误')}")314            raise Exception("API返回错误")315        316        applications = []317        if isinstance(data.get('result'), dict):318            applications = data.get('result', {}).get('externalApplications', [])319        elif isinstance(data.get('result'), list):320            applications = data.get('result', [])321        322        for app in applications:323            app_name = app.get('name', '')324            app_id = app.get('externalApplicationId', '')325            prediction_overrides = app.get('predictionOverrides', {})326            llm_name = prediction_overrides.get('llmName', '') if prediction_overrides else ''327            328            if not (app_name and app_id and llm_name):329                continue330                331            model_name = app_name332            model_map[model_name] = (app_id, llm_name)333            models_set.add(model_name)334        335        if not model_map:336            raise Exception("未找到任何可用模型")337        338        return model_map, models_set339    340    except Exception as e:341        print(f"获取模型列表异常: {e}")342        raise343 344 345def init_session():346    get_password()347    global USER_NUM, MODELS, USER_DATA348    config_list = resolve_config()349    user_num = len(config_list)350    all_models = set()351    352    for i in range(user_num):353        user = config_list[i]354        cookies = user.get("cookies")355        conversation_id = user.get("conversation_id")356        session = requests.Session()357        358        session_token = refresh_token(session, cookies)359        if not session_token:360            print(f"无法获取cookie {i+1}的token")361            continue362        363        try:364            model_map, models_set = get_model_map(session, cookies, session_token)365            all_models.update(models_set)366            USER_DATA.append((session, cookies, session_token, conversation_id, model_map, i))367        except Exception as e:368            print(f"配置用户 {i+1} 失败: {e}")369            continue370    371    USER_NUM = len(USER_DATA)372    if USER_NUM == 0:373        print("No user available, exiting...")374        exit(1)375    376    MODELS = all_models377    print(f"启动完成,共配置 {USER_NUM} 个用户")378 379 380def update_cookie(session, cookies):381    cookie_jar = {}382    for key, value in session.cookies.items():383        cookie_jar[key] = value384    cookie_dict = {}385    for item in cookies.split(";"):386        key, value = item.strip().split("=", 1)387        cookie_dict[key] = value388    cookie_dict.update(cookie_jar)389    cookies = "; ".join([f"{key}={value}" for key, value in cookie_dict.items()])390    return cookies391 392 393user_data = init_session()394 395 396@app.route("/v1/models", methods=["GET"])397@require_auth398def get_models():399    if len(MODELS) == 0:400        return jsonify({"error": "No models available"}), 500401    model_list = []402    for model in MODELS:403        model_list.append(404            {405                "id": model,406                "object": "model",407                "created": int(time.time()),408                "owned_by": "Elbert",409                "name": model,410            }411        )412    return jsonify({"object": "list", "data": model_list})413 414 415@app.route("/v1/chat/completions", methods=["POST"])416@require_auth417def chat_completions():418    openai_request = request.get_json()419    stream = openai_request.get("stream", False)420    messages = openai_request.get("messages")421    if messages is None:422        return jsonify({"error": "Messages is required", "status": 400}), 400423    model = openai_request.get("model")424    if model not in MODELS:425        return (426            jsonify(427                {428                    "error": "Model not available, check if it is configured properly",429                    "status": 404,430                }431            ),432            404,433        )434    message = format_message(messages)435    think = (436        openai_request.get("think", False) if model == "Claude Sonnet 3.7" else False437    )438    return (439        send_message(message, model, think)440        if stream441        else send_message_non_stream(message, model, think)442    )443 444 445def get_user_data():446    global CURRENT_USER447    CURRENT_USER = (CURRENT_USER + 1) % USER_NUM448    print(f"使用配置 {CURRENT_USER+1}")449    450    # Akiru uzantajn datumojn451    session, cookies, session_token, conversation_id, model_map, user_index = USER_DATA[CURRENT_USER]452    453    # Kontrolu ĉu la tokeno eksvalidiĝis, se jes, refreŝigu ĝin454    if is_token_expired(session_token):455        print(f"Cookie {CURRENT_USER+1}的token已过期或即将过期,正在刷新...")456        new_token = refresh_token(session, cookies)457        if new_token:458            # Ĝisdatigu la globale konservitan tokenon459            USER_DATA[CURRENT_USER] = (session, cookies, new_token, conversation_id, model_map, user_index)460            session_token = new_token461            print(f"成功更新token: {session_token[:15]}...{session_token[-15:]}")462        else:463            print(f"警告:无法刷新Cookie {CURRENT_USER+1}的token,继续使用当前token")464    465    return (session, cookies, session_token, conversation_id, model_map, user_index)466 467 468def create_conversation(session, cookies, session_token, external_application_id=None, deployment_id=None):469    """创建新的会话"""470    if not (external_application_id and deployment_id):471        print("无法创建新会话: 缺少必要参数")472        return None473    474    headers = {475        "accept": "application/json, text/plain, */*",476        "accept-language": "zh-CN,zh;q=0.9",477        "content-type": "application/json",478        "cookie": cookies,479        "user-agent": random.choice(USER_AGENTS),480        "x-abacus-org-host": "apps"481    }482    483    if session_token:484        headers["session-token"] = session_token485    486    create_payload = {487        "deploymentId": deployment_id,488        "name": "New Chat",489        "externalApplicationId": external_application_id490    }491    492    try:493        response = session.post(494            CREATE_CONVERSATION_URL,495            headers=headers,496            json=create_payload497        )498        499        if response.status_code == 200:500            data = response.json()501            if data.get("success", False):502                new_conversation_id = data.get("result", {}).get("deploymentConversationId")503                if new_conversation_id:504                    print(f"成功创建新的conversation: {new_conversation_id}")505                    return new_conversation_id506        507        print(f"创建会话失败: {response.status_code} - {response.text[:100]}")508        return None509    except Exception as e:510        print(f"创建会话时出错: {e}")511        return None512 513 514def is_conversation_valid(session, cookies, session_token, conversation_id, model_map, model):515    """检查会话ID是否有效"""516    if not conversation_id:517        return False518    519    # 如果没有这些信息,无法验证520    if not (model in model_map and len(model_map[model]) >= 2):521        return False522        523    external_app_id = model_map[model][0]524    525    # 尝试发送一个空消息来测试会话ID是否有效526    headers = {527        "accept": "text/event-stream",528        "content-type": "text/plain;charset=UTF-8",529        "cookie": cookies,530        "user-agent": random.choice(USER_AGENTS)531    }532    533    if session_token:534        headers["session-token"] = session_token535    536    payload = {537        "requestId": str(uuid.uuid4()),538        "deploymentConversationId": conversation_id,539        "message": "",  # 空消息540        "isDesktop": False,541        "externalApplicationId": external_app_id542    }543    544    try:545        response = session.post(546            CHAT_URL,547            headers=headers,548            data=json.dumps(payload),549            stream=False550        )551        552        # 即使返回错误,只要不是缺少ID的错误,也说明ID是有效的553        if response.status_code == 200:554            return True555        556        error_text = response.text557        if "Missing required parameter" in error_text:558            return False559            560        # 其他类型的错误,可能ID是有效的但有其他问题561        return True562    except:563        # 如果请求出错,无法确定,返回False让系统创建新ID564        return False565 566 567def get_or_create_conversation(session, cookies, session_token, conversation_id, model_map, model, user_index):568    """获取有效的会话ID,如果无效则创建新会话"""569    # 如果conversation_id为None或为空字符串,直接创建新会话570    if not conversation_id:571        print("会话ID为空,将创建新会话")572        need_create = True573    else:574        # 检查现有会话ID是否有效575        need_create = not is_conversation_valid(session, cookies, session_token, conversation_id, model_map, model)576        if need_create:577            print(f"会话ID {conversation_id} 无效,将创建新会话")578    579    # 如果需要创建新会话580    if need_create:581        if model in model_map and len(model_map[model]) >= 2:582            external_app_id = model_map[model][0]583            # 创建会话时需要deployment_id,我们先使用一个固定值584            # 在实际应用中应从API响应中获取585            deployment_id = "14b2a314cc"  # 这是从您提供的请求中获取的586            587            new_conversation_id = create_conversation(588                session, cookies, session_token, 589                external_application_id=external_app_id,590                deployment_id=deployment_id591            )592            593            if new_conversation_id:594                # 更新全局存储的会话ID595                global USER_DATA, CURRENT_USER596                session, cookies, session_token, _, model_map, _ = USER_DATA[CURRENT_USER]597                USER_DATA[CURRENT_USER] = (session, cookies, session_token, new_conversation_id, model_map, user_index)598                599                # 保存到配置文件600                update_conversation_id(user_index, new_conversation_id)601                602                return new_conversation_id603    604    # 如果无法创建,返回原始ID605    return conversation_id606 607 608def generate_trace_id():609    """Generu novan trace_id kaj sentry_trace"""610    trace_id = str(uuid.uuid4()).replace('-', '')611    sentry_trace = f"{trace_id}-{str(uuid.uuid4())[:16]}"612    return trace_id, sentry_trace613 614 615def send_message(message, model, think=False):616    """Flua traktado kaj plusendo de mesaĝoj"""617    (session, cookies, session_token, conversation_id, model_map, user_index) = get_user_data()618    619    # 确保有有效的会话ID620    conversation_id = get_or_create_conversation(session, cookies, session_token, conversation_id, model_map, model, user_index)621    622    trace_id, sentry_trace = generate_trace_id()623    624    # 计算输入token625    prompt_tokens = num_tokens_from_string(message)626    completion_buffer = io.StringIO()  # 收集所有输出用于计算token627    628    headers = {629        "accept": "text/event-stream",630        "accept-language": "zh-CN,zh;q=0.9",631        "baggage": f"sentry-environment=production,sentry-release=975eec6685013679c139fc88db2c48e123d5c604,sentry-public_key=3476ea6df1585dd10e92cdae3a66ff49,sentry-trace_id={trace_id}",632        "content-type": "text/plain;charset=UTF-8",633        "cookie": cookies,634        "sec-ch-ua": "\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"",635        "sec-ch-ua-mobile": "?0",636        "sec-ch-ua-platform": "\"Windows\"",637        "sec-fetch-dest": "empty",638        "sec-fetch-mode": "cors",639        "sec-fetch-site": "same-origin",640        "sentry-trace": sentry_trace,641        "user-agent": random.choice(USER_AGENTS)642    }643    644    if session_token:645        headers["session-token"] = session_token646    647    payload = {648        "requestId": str(uuid.uuid4()),649        "deploymentConversationId": conversation_id,650        "message": message,651        "isDesktop": False,652        "chatConfig": {653            "timezone": "Asia/Shanghai",654            "language": "zh-CN"655        },656        "llmName": model_map[model][1],657        "externalApplicationId": model_map[model][0],658        "regenerate": True,659        "editPrompt": True660    }661    662    if think:663        payload["useThinking"] = think664    665    try:666        response = session.post(667            CHAT_URL,668            headers=headers,669            data=json.dumps(payload),670            stream=True671        )672        673        response.raise_for_status()674        675        def extract_segment(line_data):676            try:677                data = json.loads(line_data)678                if "segment" in data:679                    if isinstance(data["segment"], str):680                        return data["segment"]681                    elif isinstance(data["segment"], dict) and "segment" in data["segment"]:682                        return data["segment"]["segment"]683                return ""684            except:685                return ""686        687        def generate():688            id = ""689            think_state = 2690            691            yield "data: " + json.dumps({"object": "chat.completion.chunk", "choices": [{"delta": {"role": "assistant"}}]}) + "\n\n"692            693            for line in response.iter_lines():694                if line:695                    decoded_line = line.decode("utf-8")696                    try:697                        if think:698                            data = json.loads(decoded_line)699                            if data.get("type") != "text":700                                continue701                            elif think_state == 2:702                                id = data.get("messageId")703                                segment = "<think>\n" + data.get("segment", "")704                                completion_buffer.write(segment)  # 收集输出705                                yield f"data: {json.dumps({'object': 'chat.completion.chunk', 'choices': [{'delta': {'content': segment}}]})}\n\n"706                                think_state = 1707                            elif think_state == 1:708                                if data.get("messageId") != id:709                                    segment = data.get("segment", "")710                                    completion_buffer.write(segment)  # 收集输出711                                    yield f"data: {json.dumps({'object': 'chat.completion.chunk', 'choices': [{'delta': {'content': segment}}]})}\n\n"712                                else:713                                    segment = "\n</think>\n" + data.get("segment", "")714                                    completion_buffer.write(segment)  # 收集输出715                                    yield f"data: {json.dumps({'object': 'chat.completion.chunk', 'choices': [{'delta': {'content': segment}}]})}\n\n"716                                    think_state = 0717                            else:718                                segment = data.get("segment", "")719                                completion_buffer.write(segment)  # 收集输出720                                yield f"data: {json.dumps({'object': 'chat.completion.chunk', 'choices': [{'delta': {'content': segment}}]})}\n\n"721                        else:722                            segment = extract_segment(decoded_line)723                            if segment:724                                completion_buffer.write(segment)  # 收集输出725                                yield f"data: {json.dumps({'object': 'chat.completion.chunk', 'choices': [{'delta': {'content': segment}}]})}\n\n"726                    except Exception as e:727                        print(f"处理响应出错: {e}")728            729            yield "data: " + json.dumps({"object": "chat.completion.chunk", "choices": [{"delta": {}, "finish_reason": "stop"}]}) + "\n\n"730            yield "data: [DONE]\n\n"731            732            # 在流式传输完成后计算token并更新统计733            completion_tokens = num_tokens_from_string(completion_buffer.getvalue())734            update_model_stats(model, prompt_tokens, completion_tokens)735        736        return Response(generate(), mimetype="text/event-stream")737    except requests.exceptions.RequestException as e:738        error_details = str(e)739        if hasattr(e, 'response') and e.response is not None:740            if hasattr(e.response, 'text'):741                error_details += f" - Response: {e.response.text[:200]}"742        print(f"发送消息失败: {error_details}")743        return jsonify({"error": f"Failed to send message: {error_details}"}), 500744 745 746def send_message_non_stream(message, model, think=False):747    """Ne-flua traktado de mesaĝoj"""748    (session, cookies, session_token, conversation_id, model_map, user_index) = get_user_data()749    750    # 确保有有效的会话ID751    conversation_id = get_or_create_conversation(session, cookies, session_token, conversation_id, model_map, model, user_index)752    753    trace_id, sentry_trace = generate_trace_id()754    755    # 计算输入token756    prompt_tokens = num_tokens_from_string(message)757    758    headers = {759        "accept": "text/event-stream",760        "accept-language": "zh-CN,zh;q=0.9",761        "baggage": f"sentry-environment=production,sentry-release=975eec6685013679c139fc88db2c48e123d5c604,sentry-public_key=3476ea6df1585dd10e92cdae3a66ff49,sentry-trace_id={trace_id}",762        "content-type": "text/plain;charset=UTF-8",763        "cookie": cookies,764        "sec-ch-ua": "\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"",765        "sec-ch-ua-mobile": "?0",766        "sec-ch-ua-platform": "\"Windows\"",767        "sec-fetch-dest": "empty",768        "sec-fetch-mode": "cors",769        "sec-fetch-site": "same-origin",770        "sentry-trace": sentry_trace,771        "user-agent": random.choice(USER_AGENTS)772    }773    774    if session_token:775        headers["session-token"] = session_token776    777    payload = {778        "requestId": str(uuid.uuid4()),779        "deploymentConversationId": conversation_id,780        "message": message,781        "isDesktop": False,782        "chatConfig": {783            "timezone": "Asia/Shanghai",784            "language": "zh-CN"785        },786        "llmName": model_map[model][1],787        "externalApplicationId": model_map[model][0],788        "regenerate": True,789        "editPrompt": True790    }791    792    if think:793        payload["useThinking"] = think794    795    try:796        response = session.post(797            CHAT_URL,798            headers=headers,799            data=json.dumps(payload),800            stream=True801        )802        803        response.raise_for_status()804        buffer = io.StringIO()805        806        def extract_segment(line_data):807            try:808                data = json.loads(line_data)809                if "segment" in data:810                    if isinstance(data["segment"], str):811                        return data["segment"]812                    elif isinstance(data["segment"], dict) and "segment" in data["segment"]:813                        return data["segment"]["segment"]814                return ""815            except:816                return ""817        818        if think:819            id = ""820            think_state = 2821            think_buffer = io.StringIO()822            content_buffer = io.StringIO()823            824            for line in response.iter_lines():825                if line:826                    decoded_line = line.decode("utf-8")827                    try:828                        data = json.loads(decoded_line)829                        if data.get("type") != "text":830                            continue831                        elif think_state == 2:832                            id = data.get("messageId")833                            segment = data.get("segment", "")834                            think_buffer.write(segment)835                            think_state = 1836                        elif think_state == 1:837                            if data.get("messageId") != id:838                                segment = data.get("segment", "")839                                content_buffer.write(segment)840                            else:841                                segment = data.get("segment", "")842                                think_buffer.write(segment)843                                think_state = 0844                        else:845                            segment = data.get("segment", "")846                            content_buffer.write(segment)847                    except Exception as e:848                        print(f"处理响应出错: {e}")849            850            think_content = think_buffer.getvalue()851            response_content = content_buffer.getvalue()852            853            # 计算输出token并更新统计信息854            completion_tokens = num_tokens_from_string(think_content + response_content)855            update_model_stats(model, prompt_tokens, completion_tokens)856            857            return jsonify({858                "id": f"chatcmpl-{str(uuid.uuid4())}",859                "object": "chat.completion",860                "created": int(time.time()),861                "model": model,862                "choices": [{863                    "index": 0,864                    "message": {865                        "role": "assistant",866                        "content": f"<think>\n{think_content}\n</think>\n{response_content}"867                    },868                    "finish_reason": "stop"869                }],870                "usage": {871                    "prompt_tokens": prompt_tokens,872                    "completion_tokens": completion_tokens,873                    "total_tokens": prompt_tokens + completion_tokens874                }875            })876        else:877            for line in response.iter_lines():878                if line:879                    decoded_line = line.decode("utf-8")880                    segment = extract_segment(decoded_line)881                    if segment:882                        buffer.write(segment)883            884            response_content = buffer.getvalue()885            886            # 计算输出token并更新统计信息887            completion_tokens = num_tokens_from_string(response_content)888            update_model_stats(model, prompt_tokens, completion_tokens)889            890            return jsonify({891                "id": f"chatcmpl-{str(uuid.uuid4())}",892                "object": "chat.completion",893                "created": int(time.time()),894                "model": model,895                "choices": [{896                    "index": 0,897                    "message": {898                        "role": "assistant",899                        "content": response_content900                    },901                    "finish_reason": "stop"902                }],903                "usage": {904                    "prompt_tokens": prompt_tokens,905                    "completion_tokens": completion_tokens,906                    "total_tokens": prompt_tokens + completion_tokens907                }908            })909    except requests.exceptions.RequestException as e:910        error_details = str(e)911        if hasattr(e, 'response') and e.response is not None:912            if hasattr(e.response, 'text'):913                error_details += f" - Response: {e.response.text[:200]}"914        print(f"发送消息失败: {error_details}")915        return jsonify({"error": f"Failed to send message: {error_details}"}), 500916 917 918def format_message(messages):919    buffer = io.StringIO()920    role_map, prefix, messages = extract_role(messages)921    for message in messages:922        role = message.get("role")923        role = "\b" + role_map[role] if prefix else role_map[role]924        content = message.get("content").replace("\\n", "\n")925        pattern = re.compile(r"<\|removeRole\|>\n")926        if pattern.match(content):927            content = pattern.sub("", content)928            buffer.write(f"{content}\n")929        else:930            buffer.write(f"{role}: {content}\n\n")931    formatted_message = buffer.getvalue()932    return formatted_message933 934 935def extract_role(messages):936    role_map = {"user": "Human", "assistant": "Assistant", "system": "System"}937    prefix = False938    first_message = messages[0]["content"]939    pattern = re.compile(940        r"""941        <roleInfo>\s*942        user:\s*(?P<user>[^\n]*)\s*943        assistant:\s*(?P<assistant>[^\n]*)\s*944        system:\s*(?P<system>[^\n]*)\s*945        prefix:\s*(?P<prefix>[^\n]*)\s*946        </roleInfo>\n947    """,948        re.VERBOSE,949    )950    match = pattern.search(first_message)951    if match:952        role_map = {953            "user": match.group("user"),954            "assistant": match.group("assistant"),955            "system": match.group("system"),956        }957        prefix = match.group("prefix") == "1"958        messages[0]["content"] = pattern.sub("", first_message)959        print(f"Extracted role map:")960        print(961            f"User: {role_map['user']}, Assistant: {role_map['assistant']}, System: {role_map['system']}"962        )963        print(f"Using prefix: {prefix}")964    return (role_map, prefix, messages)965 966 967@app.route("/health", methods=["GET"])968def health_check():969    global health_check_counter970    health_check_counter += 1971    return jsonify({972        "status": "healthy",973        "timestamp": datetime.now().isoformat(),974        "checks": health_check_counter975    })976 977 978def keep_alive():979    """每20分钟进行一次自我健康检查"""980    while True:981        try:982            requests.get("http://127.0.0.1:7860/health")983            time.sleep(1200)  # 20分钟984        except:985            pass  # 忽略错误,保持运行986 987 988@app.route("/", methods=["GET"])989def index():990    # 如果需要密码且用户未登录,重定向到登录页面991    if PASSWORD and not flask_session.get('logged_in'):992        return redirect(url_for('login'))993    994    # 否则重定向到仪表盘995    return redirect(url_for('dashboard'))996 997 998# 使用tiktoken计算字符串的token数量999def num_tokens_from_string(text):1000    """使用tiktoken计算字符串的token数量"""1001    try:1002        # 使用cl100k_base编码器,这是gpt-4和gpt-3.5-turbo使用的编码器1003        enc = tiktoken.get_encoding("cl100k_base")1004        tokens = enc.encode(text)1005        return len(tokens)1006    except Exception as e:1007        # 如果出错,使用字符长度作为粗略估计(大约每4个字符1个token)1008        print(f"计算token数量出错: {e},使用估算方法")1009        return len(text) // 41010 1011# 更新模型使用统计1012def update_model_stats(model, prompt_tokens, completion_tokens):1013    """更新模型使用统计数据"""1014    global model_usage_stats, total_tokens, model_usage_records1015    1016    # 获取北京时间1017    beijing_now = datetime.utcnow() + timedelta(hours=8)1018    call_time = beijing_now.strftime("%Y-%m-%d %H:%M:%S")1019    1020    # 首次使用该模型时初始化统计1021    if model not in model_usage_stats:1022        model_usage_stats[model] = {1023            "count": 0,1024            "prompt_tokens": 0,1025            "completion_tokens": 01026        }1027    1028    # 更新模型统计1029    model_usage_stats[model]["count"] += 11030    model_usage_stats[model]["prompt_tokens"] += prompt_tokens1031    model_usage_stats[model]["completion_tokens"] += completion_tokens1032    1033    # 更新总token统计1034    total_tokens["prompt"] += prompt_tokens1035    total_tokens["completion"] += completion_tokens1036    total_tokens["total"] += (prompt_tokens + completion_tokens)1037    1038    # 添加使用记录1039    usage_record = {1040        "call_time": call_time,1041        "model": model,1042        "prompt_tokens": prompt_tokens,1043        "completion_tokens": completion_tokens,1044        "total_tokens": prompt_tokens + completion_tokens,1045        "calculation_method": "tiktoken"1046    }1047    model_usage_records.append(usage_record)1048    1049    # 保存记录到文件,确保数据持久化1050    save_model_usage_records()1051    1052    print(f"模型使用记录: {model}, 输入: {prompt_tokens}, 输出: {completion_tokens}, 时间: {call_time}")1053 1054 1055# 获取计算点信息1056def get_compute_points():1057    global compute_points, USER_DATA, users_compute_points1058    1059    if USER_NUM == 0:1060        return1061    1062    # 清空用户计算点列表1063    users_compute_points = []1064    1065    # 累计总计算点1066    total_left = 01067    total_points = 01068    1069    # 获取每个用户的计算点信息1070    for i, user_data in enumerate(USER_DATA):1071        try:1072            session, cookies, session_token, _, _, _ = user_data1073            1074            # 检查token是否有效1075            if is_token_expired(session_token):1076                session_token = refresh_token(session, cookies)1077                if not session_token:1078                    print(f"用户{i+1}刷新token失败,无法获取计算点信息")1079                    continue1080                USER_DATA[i] = (session, cookies, session_token, user_data[3], user_data[4], i)1081            1082            headers = {1083                "accept": "application/json, text/plain, */*",1084                "accept-language": "zh-CN,zh;q=0.9",1085                "baggage": f"sentry-environment=production,sentry-release=93da8385541a6ce339b1f41b0c94428c70657e22,sentry-public_key=3476ea6df1585dd10e92cdae3a66ff49,sentry-trace_id={TRACE_ID}",1086                "reai-ui": "1",1087                "sec-ch-ua": "\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"",1088                "sec-ch-ua-mobile": "?0",1089                "sec-ch-ua-platform": "\"Windows\"",1090                "sec-fetch-dest": "empty",1091                "sec-fetch-mode": "cors",1092                "sec-fetch-site": "same-origin",1093                "sentry-trace": SENTRY_TRACE,1094                "session-token": session_token,1095                "x-abacus-org-host": "apps",1096                "cookie": cookies1097            }1098            1099            response = session.get(1100                COMPUTE_POINTS_URL,1101                headers=headers1102            )1103            1104            if response.status_code == 200:1105                result = response.json()1106                if result.get("success") and "result" in result:1107                    data = result["result"]1108                    left = data.get("computePointsLeft", 0)1109                    total = data.get("totalComputePoints", 0)1110                    used = total - left1111                    percentage = round((used / total) * 100, 2) if total > 0 else 01112                    1113                    # 获取北京时间1114                    beijing_now = datetime.utcnow() + timedelta(hours=8)1115                    1116                    # 添加到用户列表1117                    user_points = {1118                        "user_id": i + 1,  # 用户ID从1开始1119                        "left": left,1120                        "total": total,1121                        "used": used,1122                        "percentage": percentage,1123                        "last_update": beijing_now1124                    }1125                    users_compute_points.append(user_points)1126                    1127                    # 累计总数1128                    total_left += left1129                    total_points += total1130                    1131                    print(f"用户{i+1}计算点信息更新成功: 剩余 {left}, 总计 {total}")1132                    1133                    # 对于第一个用户,获取计算点使用日志1134                    if i == 0:1135                        get_compute_points_log(session, cookies, session_token)1136                else:1137                    print(f"获取用户{i+1}计算点信息失败: {result.get('error', '未知错误')}")1138            else:1139                print(f"获取用户{i+1}计算点信息失败,状态码: {response.status_code}")1140        except Exception as e:1141            print(f"获取用户{i+1}计算点信息异常: {e}")1142    1143    # 更新全局计算点信息(所有用户总和)1144    if users_compute_points:1145        compute_points["left"] = total_left1146        compute_points["total"] = total_points1147        compute_points["used"] = total_points - total_left1148        compute_points["percentage"] = round((compute_points["used"] / compute_points["total"]) * 100, 2) if compute_points["total"] > 0 else 01149        compute_points["last_update"] = datetime.utcnow() + timedelta(hours=8)  # 北京时间1150        print(f"所有用户计算点总计: 剩余 {total_left}, 总计 {total_points}")1151 1152# 获取计算点使用日志1153def get_compute_points_log(session, cookies, session_token):1154    global compute_points_log1155    1156    try:1157        headers = {1158            "accept": "application/json, text/plain, */*",1159            "accept-language": "zh-CN,zh;q=0.9",1160            "content-type": "application/json",1161            "reai-ui": "1",1162            "sec-ch-ua": "\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"",1163            "sec-ch-ua-mobile": "?0",1164            "sec-ch-ua-platform": "\"Windows\"",1165            "sec-fetch-dest": "empty",1166            "sec-fetch-mode": "cors",1167            "sec-fetch-site": "same-site",1168            "session-token": session_token,1169            "x-abacus-org-host": "apps",1170            "cookie": cookies1171        }1172        1173        response = session.post(1174            COMPUTE_POINTS_LOG_URL,1175            headers=headers,1176            json={"byLlm": True}1177        )1178        1179        if response.status_code == 200:1180            result = response.json()1181            if result.get("success") and "result" in result:1182                data = result["result"]1183                compute_points_log["columns"] = data.get("columns", {})1184                compute_points_log["log"] = data.get("log", [])1185                print(f"计算点使用日志更新成功,获取到 {len(compute_points_log['log'])} 条记录")1186            else:1187                print(f"获取计算点使用日志失败: {result.get('error', '未知错误')}")1188        else:1189            print(f"获取计算点使用日志失败,状态码: {response.status_code}")1190    except Exception as e:1191        print(f"获取计算点使用日志异常: {e}")1192 1193 1194# 添加登录相关路由1195@app.route("/login", methods=["GET", "POST"])1196def login():1197    error = None1198    if request.method == "POST":1199        password = request.form.get("password")1200        if password and hashlib.sha256(password.encode()).hexdigest() == PASSWORD:

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