// ==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.runtimutd.eu.org/zh-CN/scripts/539130-ai-studio-model-modifier
// @supportURL https://greasyfork.runtimutd.eu.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}"。`);
})();