AI Studio Model Modifier

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

当前为 2025-06-11 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name:zh_cn AI Studio 模型修改器 - 解锁 Kingfall 及更多隐藏模型
  3. // @name AI Studio Model Modifier
  4. // @namespace http://tampermonkey.net/
  5. // @version 1.1
  6. // @description 拦截 aistudio.google.com 的 GenerateContent 请求修改模型,支持在官方、预览及内部测试模型(例如 Kingfall)间切换,并提供带分类的下拉菜单。
  7. // @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.
  8. // @author Z_06
  9. // @match *://aistudio.google.com/*
  10. // @icon https://www.google.com/s2/favicons?sz=64&domain=google.com
  11. // @license MIT
  12. // @grant GM_setValue
  13. // @grant GM_getValue
  14. // @grant GM_registerMenuCommand
  15. // @grant GM_addStyle
  16. // ==/UserScript==
  17.  
  18. (function() {
  19. 'use strict';
  20.  
  21. // --- 配置区域 ---
  22. const SCRIPT_NAME = "[AI Studio] 模型修改器";
  23. const STORAGE_KEY = "aistudio_custom_model_name_v2";
  24. const TARGET_URL = "https://alkalimakersuite-pa.clients6.google.com/$rpc/google.internal.alkali.applications.makersuite.v1.MakerSuiteService/GenerateContent";
  25. const MODEL_SELECTOR_CONTAINER = 'div.settings-model-selector';
  26.  
  27. // 带分类的可选模型列表
  28. const MODEL_OPTIONS = [
  29. {
  30. label: "内部测试模型",
  31. options: [
  32. { name: "Kingfall (内部测试)", value: "models/kingfall-ab-test" },
  33. { name: "Calmriver (内部测试)", value: "models/calmriver-ab-test" },
  34. { name: "Claybrook (内部测试)", value: "models/claybrook-ab-test" },
  35. { name: "Frostwind (内部测试)", value: "models/frostwind-ab-test" },
  36. { name: "Goldmane (内部测试)", value: "models/goldmane-ab-test" },
  37. ]
  38. },
  39. {
  40. label: "Gemini 2.5",
  41. options: [
  42. { name: "2.5 Pro 预览版 (06-05)", value: "models/gemini-2.5-pro-preview-06-05" },
  43. { name: "2.5 Flash 预览版 (05-20)", value: "models/gemini-2.5-flash-preview-05-20" },
  44. { name: "2.5 Pro 预览 (05-06)", value: "models/gemini-2.5-pro-preview-05-06" },
  45. { name: "2.5 Pro 预览 (03-25)", value: "models/gemini-2.5-pro-preview-03-25" },
  46. { name: "2.5 Pro 预览 (03-25 AB-Test)", value: "models/gemini-2.5-pro-preview-03-25-ab-test" },
  47. { name: "2.5 Pro EXP (03-25)", value: "models/gemini-2.5-pro-exp-03-25" },
  48. { name: "2.5 Flash 预览 (04-17)", value: "models/gemini-2.5-flash-preview-04-17" },
  49. { name: "2.5 Flash 预览 (04-17 Thinking)", value: "models/gemini-2.5-flash-preview-04-17-thinking" },
  50. ]
  51. },
  52. {
  53. label: "Gemini 2.0",
  54. options: [
  55. { name: "2.0 Flash", value: "models/gemini-2.0-flash" },
  56. { name: "2.0 Flash (图片生成)", value: "models/gemini-2.0-flash-preview-image-generation" },
  57. { name: "2.0 Flash-Lite", value: "models/gemini-2.0-flash-lite" },
  58. ]
  59. },
  60. {
  61. label: "Gemini 1.5",
  62. options: [
  63. { name: "1.5 Pro", value: "models/gemini-1.5-pro" },
  64. { name: "1.5 Flash", value: "models/gemini-1.5-flash" },
  65. { name: "1.5 Flash-8B", value: "models/gemini-1.5-flash-8b" },
  66. ]
  67. }
  68. ];
  69. // 默认模型
  70. const DEFAULT_MODEL = MODEL_OPTIONS[0].options[0].value;
  71.  
  72. // --- 获取已保存的或默认的模型名称 ---
  73. let customModelName = GM_getValue(STORAGE_KEY, DEFAULT_MODEL);
  74.  
  75. // --- 注入CSS样式 ---
  76. GM_addStyle(`
  77. ${MODEL_SELECTOR_CONTAINER} ms-model-selector-two-column { display: none !important; }
  78. #custom-model-selector {
  79. width: 100%; padding: 8px 12px; margin-top: 4px; border: 1px solid #5f6368;
  80. border-radius: 8px; color: #e2e2e5; background-color: #35373a;
  81. font-family: 'Google Sans', 'Roboto', sans-serif; font-size: 14px; font-weight: 500;
  82. box-sizing: border-box; cursor: pointer; -webkit-appearance: none; -moz-appearance: none; appearance: none;
  83. 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');
  84. background-repeat: no-repeat; background-position: right 12px center; background-size: 10px;
  85. }
  86. #custom-model-selector optgroup { font-weight: bold; color: #8ab4f8; }
  87. `);
  88.  
  89. // --- 菜单和UI更新逻辑 ---
  90.  
  91. /**
  92. * 更新或创建下拉菜单中的选项,并选中指定的模型
  93. * @param {string} modelValue - 要选中的模型的完整值
  94. */
  95. function updateAndSelectModel(modelValue) {
  96. const selector = document.getElementById('custom-model-selector');
  97. if (!selector) return;
  98.  
  99. // 使用 querySelector 检查选项是否已存在于任何分组中
  100. if (!selector.querySelector(`option[value="${modelValue}"]`)) {
  101. // 选项不存在,动态创建它到 "自定义模型" 分组
  102. let customGroup = document.getElementById('custom-model-optgroup');
  103. if (!customGroup) {
  104. customGroup = document.createElement('optgroup');
  105. customGroup.id = 'custom-model-optgroup';
  106. customGroup.label = '自定义模型';
  107. selector.appendChild(customGroup);
  108. }
  109.  
  110. const newOption = document.createElement('option');
  111. newOption.value = modelValue;
  112. newOption.textContent = `* ${modelValue.replace('models/', '')}`;
  113. customGroup.appendChild(newOption);
  114. }
  115.  
  116. selector.value = modelValue;
  117. }
  118.  
  119. /**
  120. * 创建并注入带分类的模型选择下拉菜单
  121. * @param {HTMLElement} container - 用于注入UI的父容器元素
  122. */
  123. function createModelSelectorUI(container) {
  124. console.log(`[${SCRIPT_NAME}] 发现容器,注入带分类的UI...`);
  125.  
  126. const selector = document.createElement('select');
  127. selector.id = 'custom-model-selector';
  128. selector.title = "所有请求都将被强制使用此下拉框选中的模型";
  129.  
  130. // 遍历分类和选项来创建 <optgroup> 和 <option>
  131. MODEL_OPTIONS.forEach(group => {
  132. const optgroup = document.createElement('optgroup');
  133. optgroup.label = group.label;
  134. group.options.forEach(opt => {
  135. const option = document.createElement('option');
  136. option.value = opt.value;
  137. option.textContent = opt.name;
  138. optgroup.appendChild(option);
  139. });
  140. selector.appendChild(optgroup);
  141. });
  142.  
  143. selector.addEventListener('change', (event) => {
  144. const newModel = event.target.value;
  145. customModelName = newModel;
  146. GM_setValue(STORAGE_KEY, newModel);
  147. console.log(`[${SCRIPT_NAME}] 模型已切换并保存: ${newModel}`);
  148. });
  149.  
  150. const injectionPoint = container.querySelector('.item-input-form-field');
  151. if (injectionPoint) {
  152. injectionPoint.appendChild(selector);
  153. // 初始化UI,确保它显示当前活动的模型(如果不在预设中,会动态添加)
  154. updateAndSelectModel(customModelName);
  155. console.log(`[${SCRIPT_NAME}] 自定义UI注入成功。`);
  156. }
  157. }
  158.  
  159. // 注册油猴菜单命令
  160. GM_registerMenuCommand(`添加/设置自定义模型`, () => {
  161. const newModel = prompt("请输入要强制使用的完整模型名称:", customModelName);
  162. if (newModel && newModel.trim() !== "") {
  163. const trimmedModel = newModel.trim();
  164. customModelName = trimmedModel;
  165. GM_setValue(STORAGE_KEY, trimmedModel);
  166. alert(`模型已更新为:\n${trimmedModel}`);
  167. updateAndSelectModel(trimmedModel);
  168. }
  169. });
  170.  
  171. // --- DOM 变动监听,用于注入UI元素 ---
  172. const observer = new MutationObserver((mutations, obs) => {
  173. const container = document.querySelector(MODEL_SELECTOR_CONTAINER);
  174. if (container && !document.getElementById('custom-model-selector')) {
  175. createModelSelectorUI(container);
  176. }
  177. });
  178. observer.observe(document.body, { childList: true, subtree: true });
  179.  
  180. // --- 核心:拦截和修改 XHR 请求 ---
  181. const originalOpen = XMLHttpRequest.prototype.open;
  182. XMLHttpRequest.prototype.open = function(method, url) {
  183. this._url = url;
  184. this._method = method;
  185. return originalOpen.apply(this, arguments);
  186. };
  187.  
  188. const originalSend = XMLHttpRequest.prototype.send;
  189. XMLHttpRequest.prototype.send = function(data) {
  190. if (this._url === TARGET_URL && this._method.toUpperCase() === 'POST' && data) {
  191. try {
  192. let payload = JSON.parse(data);
  193. const originalModel = payload[0];
  194. if (typeof originalModel === 'string' && originalModel.startsWith('models/')) {
  195. console.log(`[${SCRIPT_NAME}] 拦截请求。原始: ${originalModel} -> 修改为: ${customModelName}`);
  196. payload[0] = customModelName;
  197. const modifiedData = JSON.stringify(payload);
  198. return originalSend.call(this, modifiedData);
  199. }
  200. } catch (e) {
  201. console.error(`[${SCRIPT_NAME}] 修改请求负载时出错:`, e);
  202. }
  203. }
  204. return originalSend.apply(this, arguments);
  205. };
  206.  
  207. console.log(`[${SCRIPT_NAME}] 已加载。当前强制模型为 "${customModelName}"。`);
  208. })();