ChatGPT Model Switcher: Toggle on/off GPT-4o-mini

Injects a button allowing you to toggle on/off GPT-4o-mini during the chat

目前为 2025-03-09 提交的版本。查看 最新版本

// ==UserScript==
// @name         ChatGPT Model Switcher: Toggle on/off GPT-4o-mini
// @namespace    http://tampermonkey.net/
// @version      0.26
// @description  Injects a button allowing you to toggle on/off GPT-4o-mini during the chat
// @match        *://chat.openai.com/*
// @grant        unsafeWindow
// @grant        GM.setValue
// @grant        GM.getValue
// @run-at       document-idle
// ==/UserScript==

(async function() {
  'use strict';

  class ModelSwitcher {
    constructor(useMini = true) {
      this.useMini = useMini;
      this.containerSelector = 'div[class*="relative"]'; // More flexible selector
    }

    hookFetch() {
      const originalFetch = unsafeWindow.fetch;
      unsafeWindow.fetch = async (resource, config = {}) => {
        if (
          resource === 'https://chat.openai.com/backend-api/conversation' &&
          config.method === 'POST' &&
          config.headers &&
          config.headers['Content-Type'] === 'application/json' &&
          config.body
        ) {
          if (this.useMini) {
            const body = JSON.parse(config.body);
            body.model = 'gpt-4o-mini'; // Assuming the model is still called gpt-4o-mini
            config.body = JSON.stringify(body);
          }
        }
        return originalFetch(resource, config);
      };
    }

    injectToggleButtonStyle() {
      if (!document.getElementById('toggleCss')) {
        const styleNode = document.createElement('style');
        styleNode.id = 'toggleCss';
        styleNode.type = 'text/css';
        styleNode.textContent = `.toggle {
  position: relative;
  display: inline-block;
  width: 2.5rem;
  height: 1.5rem;
  background-color: hsl(0deg 0% 40%);
  border-radius: 25px;
  cursor: pointer;
  transition: background-color 0.2s ease-in;
}
.toggle::after {
  content: '';
  position: absolute;
  width: 1.4rem;
  left: 0.1rem;
  height: calc(1.5rem - 2px);
  top: 1px;
  background-color: white;
  border-radius: 50%;
  transition: all 0.2s ease-out;
}
#cb-toggle:checked + .toggle {
  background-color: hsl(102, 58%, 39%);
}
#cb-toggle:checked + .toggle::after {
  transform: translateX(1rem);
}
.hide-me {
  opacity: 0;
  height: 0;
  width: 0;
}`;
        document.head.appendChild(styleNode);
      }
    }

    getContainer() {
      return document.querySelector(this.containerSelector);
    }

    injectToggleButton(container = null) {
      if (!container) container = this.getContainer();
      if (!container) {
        console.error('Container not found!');
        return;
      }

      // Check if toggle already exists
      if (container.querySelector('#cb-toggle')) {
        return;
      }

      container.classList.add('items-center');

      // <input id="cb-toggle" type="checkbox" class="hide-me">
      const checkbox = document.createElement('input');
      checkbox.id = 'cb-toggle';
      checkbox.type = 'checkbox';
      checkbox.className = 'hide-me';
      checkbox.checked = this.useMini;

      // <label for="cb-toggle" class="toggle" title="Toggle GPT-4o Mini"></label>
      const label = document.createElement('label');
      label.htmlFor = 'cb-toggle';
      label.className = 'toggle';
      label.title = `Using model: ${this.useMini ? 'GPT-4o mini' : 'original'}`;

      container.appendChild(checkbox);
      container.appendChild(label);

      const cb = document.querySelector('#cb-toggle');
      cb.addEventListener(
        'click',
        async () => {
          this.useMini = cb.checked;
          await GM.setValue('useMini', this.useMini);
          label.title = `Using model: ${this.useMini ? 'GPT-4o mini' : 'original'}`;
        },
        false
      );
    }

    monitorChild(nodeSelector, callback) {
      const node = document.querySelector(nodeSelector);
      if (!node) {
        console.log(`${nodeSelector} not found!`);
        return;
      }
      const observer = new MutationObserver(mutationsList => {
        for (const mutation of mutationsList) {
          callback(observer, mutation);
          break;
        }
      });
      observer.observe(node, { childList: true });
    }

    monitorNodesAndInject() {
      this.monitorChild('body main', () => {
        this.injectToggleButton();

        this.monitorChild('main div:first-child div:first-child', (observer, mutation) => {
          observer.disconnect();
          this.injectToggleButton();
        });
      });

      this.monitorChild(this.containerSelector, (observer, mutation) => {
        observer.disconnect();
        setTimeout(() => this.injectToggleButton(), 500);
      });
    }
  }

  const useMini = await GM.getValue('useMini', true);
  const switcher = new ModelSwitcher(useMini);
  switcher.hookFetch();
  switcher.injectToggleButtonStyle();
  switcher.monitorNodesAndInject();
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址