AI Studio Model Modifier

拦截 aistudio.google.com 的 GenerateContent 请求修改模型,支持在官方、预览及内部测试模型(例如 Kingfall)间切换,并提供带分类的下拉菜单。

目前為 2025-06-11 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name:zh_cn   AI Studio 模型修改器 - 解锁 Kingfall 及更多隐藏模型
// @name         AI Studio Model Modifier
// @namespace    http://tampermonkey.net/
// @version      1.1.1
// @description  拦截 aistudio.google.com 的 GenerateContent 请求修改模型,支持在官方、预览及内部测试模型(例如 Kingfall)间切换,并提供带分类的下拉菜单。
// @description:en Modify the model for aistudio.google.com requests, allowing switching between official, preview, and internal test models such as kingfall with a categorized dropdown menu.
// @author       Z_06
// @match        *://aistudio.google.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=google.com
// @license      MIT
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @grant        GM_addStyle
// @homepageURL  https://greasyfork.org/zh-CN/scripts/539130-ai-studio-model-modifier
// @supportURL   https://greasyfork.org/zh-CN/scripts/539130-ai-studio-model-modifier
// ==/UserScript==

(function() {
    'use strict';

    // --- 配置区域 ---
    const SCRIPT_NAME = "[AI Studio] 模型修改器";
    const STORAGE_KEY = "aistudio_custom_model_name_v2";
    const TARGET_URL = "https://alkalimakersuite-pa.clients6.google.com/$rpc/google.internal.alkali.applications.makersuite.v1.MakerSuiteService/GenerateContent";
    const MODEL_SELECTOR_CONTAINER = 'div.settings-model-selector';

    // 带分类的可选模型列表
    const MODEL_OPTIONS = [
        {
            label: "内部测试模型",
            options: [
                { name: "Kingfall (内部测试)", value: "models/kingfall-ab-test" },
                { name: "Calmriver (内部测试)", value: "models/calmriver-ab-test" },
                { name: "Claybrook (内部测试)", value: "models/claybrook-ab-test" },
                { name: "Frostwind (内部测试)", value: "models/frostwind-ab-test" },
                { name: "Goldmane (内部测试)", value: "models/goldmane-ab-test" },
            ]
        },
        {
            label: "Gemini 2.5",
            options: [
                { name: "2.5 Pro 预览版 (06-05)", value: "models/gemini-2.5-pro-preview-06-05" },
                { name: "2.5 Flash 预览版 (05-20)", value: "models/gemini-2.5-flash-preview-05-20" },
                { name: "2.5 Pro 预览 (05-06)", value: "models/gemini-2.5-pro-preview-05-06" },
                { name: "2.5 Pro 预览 (03-25)", value: "models/gemini-2.5-pro-preview-03-25" },
                { name: "2.5 Pro 预览 (03-25 AB-Test)", value: "models/gemini-2.5-pro-preview-03-25-ab-test" },
                { name: "2.5 Pro EXP (03-25)", value: "models/gemini-2.5-pro-exp-03-25" },
                { name: "2.5 Flash 预览 (04-17)", value: "models/gemini-2.5-flash-preview-04-17" },
                { name: "2.5 Flash 预览 (04-17 Thinking)", value: "models/gemini-2.5-flash-preview-04-17-thinking" },
            ]
        },
        {
            label: "Gemini 2.0",
            options: [
                { name: "2.0 Flash", value: "models/gemini-2.0-flash" },
                { name: "2.0 Flash (图片生成)", value: "models/gemini-2.0-flash-preview-image-generation" },
                { name: "2.0 Flash-Lite", value: "models/gemini-2.0-flash-lite" },
            ]
        },
        {
            label: "Gemini 1.5",
            options: [
                { name: "1.5 Pro", value: "models/gemini-1.5-pro" },
                { name: "1.5 Flash", value: "models/gemini-1.5-flash" },
                { name: "1.5 Flash-8B", value: "models/gemini-1.5-flash-8b" },
            ]
        }
    ];
    // 默认模型
    const DEFAULT_MODEL = MODEL_OPTIONS[0].options[0].value;

    // --- 获取已保存的或默认的模型名称 ---
    let customModelName = GM_getValue(STORAGE_KEY, DEFAULT_MODEL);

    // --- 注入CSS样式 ---
    GM_addStyle(`
        ${MODEL_SELECTOR_CONTAINER} ms-model-selector-two-column { display: none !important; }
        #custom-model-selector {
            width: 100%; padding: 8px 12px; margin-top: 4px; border: 1px solid #5f6368;
            border-radius: 8px; color: #e2e2e5; background-color: #35373a;
            font-family: 'Google Sans', 'Roboto', sans-serif; font-size: 14px; font-weight: 500;
            box-sizing: border-box; cursor: pointer; -webkit-appearance: none; -moz-appearance: none; appearance: none;
            background-image: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23e2e2e5%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.4-5.4-13z%22%2F%3E%3C%2Fsvg%3E');
            background-repeat: no-repeat; background-position: right 12px center; background-size: 10px;
        }
        #custom-model-selector optgroup { font-weight: bold; color: #8ab4f8; }
    `);

    // --- 菜单和UI更新逻辑 ---

    /**
     * 更新或创建下拉菜单中的选项,并选中指定的模型
     * @param {string} modelValue - 要选中的模型的完整值
     */
    function updateAndSelectModel(modelValue) {
        const selector = document.getElementById('custom-model-selector');
        if (!selector) return;

        // 使用 querySelector 检查选项是否已存在于任何分组中
        if (!selector.querySelector(`option[value="${modelValue}"]`)) {
            // 选项不存在,动态创建它到 "自定义模型" 分组
            let customGroup = document.getElementById('custom-model-optgroup');
            if (!customGroup) {
                customGroup = document.createElement('optgroup');
                customGroup.id = 'custom-model-optgroup';
                customGroup.label = '自定义模型';
                selector.appendChild(customGroup);
            }

            const newOption = document.createElement('option');
            newOption.value = modelValue;
            newOption.textContent = `* ${modelValue.replace('models/', '')}`;
            customGroup.appendChild(newOption);
        }

        selector.value = modelValue;
    }

    /**
     * 创建并注入带分类的模型选择下拉菜单
     * @param {HTMLElement} container - 用于注入UI的父容器元素
     */
    function createModelSelectorUI(container) {
        console.log(`[${SCRIPT_NAME}] 发现容器,注入带分类的UI...`);

        const selector = document.createElement('select');
        selector.id = 'custom-model-selector';
        selector.title = "所有请求都将被强制使用此下拉框选中的模型";

        // 遍历分类和选项来创建 <optgroup> 和 <option>
        MODEL_OPTIONS.forEach(group => {
            const optgroup = document.createElement('optgroup');
            optgroup.label = group.label;
            group.options.forEach(opt => {
                const option = document.createElement('option');
                option.value = opt.value;
                option.textContent = opt.name;
                optgroup.appendChild(option);
            });
            selector.appendChild(optgroup);
        });

        selector.addEventListener('change', (event) => {
            const newModel = event.target.value;
            customModelName = newModel;
            GM_setValue(STORAGE_KEY, newModel);
            console.log(`[${SCRIPT_NAME}] 模型已切换并保存: ${newModel}`);
        });

        const injectionPoint = container.querySelector('.item-input-form-field');
        if (injectionPoint) {
            injectionPoint.appendChild(selector);
            // 初始化UI,确保它显示当前活动的模型(如果不在预设中,会动态添加)
            updateAndSelectModel(customModelName);
            console.log(`[${SCRIPT_NAME}] 自定义UI注入成功。`);
        }
    }

    // 注册油猴菜单命令
    GM_registerMenuCommand(`添加/设置自定义模型`, () => {
        const newModel = prompt("请输入要强制使用的完整模型名称:", customModelName);
        if (newModel && newModel.trim() !== "") {
            const trimmedModel = newModel.trim();
            customModelName = trimmedModel;
            GM_setValue(STORAGE_KEY, trimmedModel);
            alert(`模型已更新为:\n${trimmedModel}`);
            updateAndSelectModel(trimmedModel);
        }
    });

    // --- DOM 变动监听,用于注入UI元素 ---
    const observer = new MutationObserver((mutations, obs) => {
        const container = document.querySelector(MODEL_SELECTOR_CONTAINER);
        if (container && !document.getElementById('custom-model-selector')) {
            createModelSelectorUI(container);
        }
    });
    observer.observe(document.body, { childList: true, subtree: true });

    // --- 核心:拦截和修改 XHR 请求 ---
    const originalOpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function(method, url) {
        this._url = url;
        this._method = method;
        return originalOpen.apply(this, arguments);
    };

    const originalSend = XMLHttpRequest.prototype.send;
    XMLHttpRequest.prototype.send = function(data) {
        if (this._url === TARGET_URL && this._method.toUpperCase() === 'POST' && data) {
            try {
                let payload = JSON.parse(data);
                const originalModel = payload[0];
                if (typeof originalModel === 'string' && originalModel.startsWith('models/')) {
                    console.log(`[${SCRIPT_NAME}] 拦截请求。原始: ${originalModel} -> 修改为: ${customModelName}`);
                    payload[0] = customModelName;
                    const modifiedData = JSON.stringify(payload);
                    return originalSend.call(this, modifiedData);
                }
            } catch (e) {
                console.error(`[${SCRIPT_NAME}] 修改请求负载时出错:`, e);
            }
        }
        return originalSend.apply(this, arguments);
    };

    console.log(`[${SCRIPT_NAME}] 已加载。当前强制模型为 "${customModelName}"。`);
})();