Claude AI Response Model Modifier

Modifies 'model' field via Tampermonkey menu, with performance improvements.

// ==UserScript==
// @name         Claude AI Response Model Modifier
// @namespace    http://tampermonkey.net/
// @version      2.1.3 (Optimized and Cleaned Logs)
// @description  Modifies 'model' field via Tampermonkey menu, with performance improvements.
// @author       Your Name
// @match        https://claude.ai/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @grant        unsafeWindow
// @run-at       document-start
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    const PREDEFINED_MODELS = [
        'claude-opus-4-20250514-claude-ai-pro', 'claude-3-7-sonnet-20250219', 'claude-opus-4-20250514',
    ];

    const CONFIG_KEY_ENABLE_MOD = 'ClaudeMod_EnableModification_v2';
    const CONFIG_KEY_TARGET_MODEL = 'ClaudeMod_TargetModelString_v2';

    let currentEnableModification;
    let currentTargetModelString;

    function loadConfig() {
        currentEnableModification = GM_getValue(CONFIG_KEY_ENABLE_MOD, true);
        currentTargetModelString = GM_getValue(CONFIG_KEY_TARGET_MODEL, PREDEFINED_MODELS[0]);
    }

    function registerMenuItems() {
        const toggleStatusText = '功能开关: 模型修改 (当前: ' + (currentEnableModification ? '已开启' : '已关闭') + ')';
        let modelDisplayText = currentTargetModelString || "N/A"; // Handle undefined case
        if (modelDisplayText.length > 30) {
            modelDisplayText = modelDisplayText.substring(0, 27) + '...';
        }
        const selectModelText = '选择预定义模型 (当前: ' + modelDisplayText + ')';

        GM_registerMenuCommand(toggleStatusText, toggleEnableModification, 't');
        GM_registerMenuCommand(selectModelText, selectTargetModelFromList, 's');
        GM_registerMenuCommand('设置自定义模型字符串', setCustomTargetModel, 'c');
    }

    function toggleEnableModification() {
        currentEnableModification = !currentEnableModification;
        GM_setValue(CONFIG_KEY_ENABLE_MOD, currentEnableModification);
        alert('模型修改功能现在已 ' + (currentEnableModification ? '开启 (ON)' : '关闭 (OFF)') + '.');
        registerMenuItems();
    }

    function selectTargetModelFromList() {
        let promptMessage = "请选择要使用的模型 (输入序号):\n\n";
        PREDEFINED_MODELS.forEach((model, index) => { promptMessage += (index + 1) + '. ' + model + '\n'; });
        promptMessage += '\n当前已选 (若修改将保存): ' + (currentTargetModelString || "N/A");
        const selection = prompt(promptMessage);
        if (selection === null) return;
        const selectedIndex = parseInt(selection, 10) - 1;
        if (selectedIndex >= 0 && selectedIndex < PREDEFINED_MODELS.length) {
            currentTargetModelString = PREDEFINED_MODELS[selectedIndex];
            GM_setValue(CONFIG_KEY_TARGET_MODEL, currentTargetModelString);
            alert('目标模型已设置为: ' + currentTargetModelString);
            registerMenuItems();
        } else { alert("无效的选择。"); }
    }

    function setCustomTargetModel() {
        const customModel = prompt("请输入自定义的模型字符串:", currentTargetModelString || PREDEFINED_MODELS[0]);
        if (customModel === null) return;
        if (customModel.trim() !== "") {
            currentTargetModelString = customModel.trim();
            GM_setValue(CONFIG_KEY_TARGET_MODEL, currentTargetModelString);
            alert('目标模型已自定义为: ' + currentTargetModelString);
            registerMenuItems();
        } else { alert("自定义模型字符串不能为空。"); }
    }

    let originalFetch;
    let fetchTargetWindow;

    if (typeof unsafeWindow !== 'undefined' && typeof unsafeWindow.fetch === 'function') {
        fetchTargetWindow = unsafeWindow;
        originalFetch = unsafeWindow.fetch;
    } else if (typeof window.fetch === 'function') {
        fetchTargetWindow = window;
        originalFetch = window.fetch;
    } else {
        console.error('[Claude Mod v2.1.3] CRITICAL ERROR: Fetch function not found!');
    }

    const TARGET_URL_REGEX = /^\/api\/organizations\/[^\/]+\/chat_conversations\/[^\/]+\?tree=True&rendering_mode=messages&render_all_tools=true$/;

    if (typeof originalFetch === 'function') {
        fetchTargetWindow.fetch = function(input, init) {
            let urlToTest;
            if (typeof input === 'string') { urlToTest = input; }
            else if (input && typeof input.url === 'string') { urlToTest = input.url; }
            else {
                // console.warn('[Claude Mod v2.1.3] Could not determine URL from fetch input. Passing through.'); // 保留此警告,但设为可选
                return originalFetch.apply(fetchTargetWindow, arguments);
            }

            // --- 性能优化:前置快速检查 ---
            // 仅当功能开启且 URL 初步匹配时,才进行后续的正则匹配
            if (!currentEnableModification || !urlToTest.startsWith('/api/organizations/')) {
                return originalFetch.apply(fetchTargetWindow, arguments);
            }

            // --- 对可能的目标 URL 进行正则匹配 ---
            const isMatch = TARGET_URL_REGEX.test(urlToTest);

            if (isMatch) { // 无需再次检查 currentEnableModification,前面已检查
                console.log('[Claude Mod v2.1.3] Intercepted:', urlToTest.substring(0, 120) + (urlToTest.length > 120 ? '...' : ''));
                return originalFetch.apply(fetchTargetWindow, arguments)
                    .then(async (response) => {
                        if (!response.ok) {
                             console.warn('[Claude Mod v2.1.3] Original response not OK for intercepted URL:', response.status, response.statusText, urlToTest);
                             return response;
                        }
                        const responseBodyText = await response.text();
                        let jsonData;
                        try {
                            jsonData = JSON.parse(responseBodyText);
                        } catch (e) {
                            console.error('[Claude Mod v2.1.3] Failed to parse JSON for:', urlToTest.substring(0,120), e);
                            return new Response(responseBodyText, { status: response.status, statusText: response.statusText, headers: response.headers });
                        }

                        console.log('[Claude Mod v2.1.3] Original model:', jsonData.model); // 保留这些关键操作日志
                        jsonData.model = currentTargetModelString;
                        console.log('[Claude Mod v2.1.3] Modified model to:', currentTargetModelString);

                        const modifiedResponseBody = JSON.stringify(jsonData);
                        const modifiedResponse = new Response(modifiedResponseBody, { status: response.status, statusText: response.statusText, headers: response.headers });
                        modifiedResponse.headers.set('Content-Type', 'application/json');
                        return modifiedResponse;
                    })
                    .catch(error => {
                        console.error('[Claude Mod v2.1.3] Error during fetch modification for:', urlToTest.substring(0,120), error);
                        throw error;
                    });
            }
            // 如果 URL 以 /api/organizations/ 开头,但不完全匹配 TARGET_URL_REGEX
            return originalFetch.apply(fetchTargetWindow, arguments);
        };
    }

    function init() {
        loadConfig();
        registerMenuItems();
        let fetchTargetName = "N/A";
        if (fetchTargetWindow === unsafeWindow) fetchTargetName = "unsafeWindow";
        else if (fetchTargetWindow === window) fetchTargetName = "window";

        console.log('[Claude Mod v2.1.3] Script loaded. UI active. Fetch override targeting: ' + fetchTargetName);
        console.log('[Claude Mod v2.1.3] Settings: Mod ' + (currentEnableModification ? 'ON' : 'OFF') + ', Model: ' + (currentTargetModelString || "N/A"));
    }
    init();

})();
// End of script. Ensure NO characters, including spaces or newlines, exist after this line.