ChatGPT Model Switcher

Switch ChatGPT model mid-chat

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==UserScript==
// @name         ChatGPT Model Switcher
// @namespace    https://ucm.dev/
// @version      1.0
// @description  Switch ChatGPT model mid-chat
// @author       GPT-4, GPT-3.5, Copilot, and Sam Watkins <[email protected]>
// @match        *://chat.openai.com/*
// @grant        none
// @license      MIT
// ==/UserScript==

// This user script adds a model switcher to the OpenAI chat interface,
// allowing users to switch between different AI models when conversing.

(function () {

	"use strict";

	// Define various constants, such as available models,
	// URLs for API calls, messages, and selectors for DOM elements.

	const MODELS = [
		{
			"name": "text-davinci-002-render-sha",
			"label": "Default (GPT-3.5)"
		},
		{
			"name": "text-davinci-002-render-paid",
			"label": "Legacy (GPT-3.5)"
		},
		{
			"name": "gpt-4",
			"label": "GPT-4"
		}
	];

	const URLS = {
		conversation: "https://chat.openai.com/backend-api/conversation",
	};

	const MESSAGES = {
		modelUnavailable: "The previous model used in this conversation is unavailable. " +
		"We've switched you to the latest default model",
		modelChanged: "Now using ",
	};

	const SELECTORS = {
		buttons: "#__next main form > div div:nth-of-type(1)",
		messagesContainer: ".flex-col.items-center",
		modelUnavailableMessage: ".flex-col.items-center .text-center",
		chatGPTModelSelect: "#__next button[data-headlessui-state]",
	};

	const IDS = {
		switcher: "chatgpt-model-switcher",
	};

	// Utility functions
	const $ = (selector) => document.querySelector(selector);
	const $$ = (selector) => document.querySelectorAll(selector);
	const $id = (id) => document.getElementById(id);
	const $create = (element) => document.createElement(element);

	// Update the model parameter in the request body to match the
	// selected model in the UI.
	function updateModelParameter(originalRequest) {
		// Parse the request body
		const requestData = JSON.parse(originalRequest.body);

		// Make sure the request has a model parameter
		if (!requestData.model) return originalRequest;

		// Make sure the model switcher exists in the UI
		const modelSwitcher = $id(IDS.switcher);
		if (!modelSwitcher) return originalRequest;

		// Update the model parameter based on the selected option in the UI
		requestData.model = modelSwitcher.value;

		// Modify the request to include the updated model parameter
		const updatedRequest = {
			...originalRequest,
			body: JSON.stringify(requestData),
		};

		return updatedRequest;
	}

	// Check if the request is a conversation request.
	function isConversationRequest(requestArgs) {
		return	requestArgs[0] &&
		requestArgs[0] === URLS.conversation &&
		requestArgs[1] &&
		requestArgs[1].method === "POST" &&
		requestArgs[1].body;
	}

	// Replace the original fetch function with a new function that
	// updates the model parameter in the request before sending it.
	const originalFetch = window.fetch;
	window.fetch = async function () {
		if (isConversationRequest(arguments)) {
			arguments[1] = updateModelParameter(arguments[1]);
		}
		return await originalFetch.apply(this, arguments);
	};

	// Replace the model unavailable message with a message indicating
	// that the model has been changed.
	function replaceModelUnavailableMessage() {
		for (const element of $$(SELECTORS.modelUnavailableMessage)) {
			if (element.textContent !== MESSAGES.modelUnavailable) continue;
			const newModel = $id(IDS.switcher).value;
			const newModelLabel = MODELS.find((model) => model.name === newModel).label;
			element.textContent = MESSAGES.modelChanged + newModelLabel;
		}
	}

	// Synchronize the custom model switcher with the original model selector.
	// Update the custom model switcher's value to match the currently selected
	// model in the original model selector.
	function syncCustomModelSwitcher() {
		const chatGPTModelSelect = $(SELECTORS.chatGPTModelSelect);
		const modelLabel = chatGPTModelSelect.innerText.split("\n")[1];
		const customModelSwitcher = $id(IDS.switcher);

		for (const model of MODELS) {
			if (model.label !== modelLabel) continue;
			customModelSwitcher.value = model.name;
			break;
		}
	}

	// Initialize the model switcher: Add it to the chat interface, watch the
	// original model selector, and watch for the model unavailable message.
	function initModelSwitcher() {
		if ($id(IDS.switcher)) return;

		const buttons = $(SELECTORS.buttons);

		// Create the model switcher
		const modelSwitcher = $create("select");
		modelSwitcher.id = IDS.switcher;
		modelSwitcher.classList.add("btn", "flex", "gap-2", "justify-center", "btn-neutral");
		for (const model of MODELS) {
			const option = $create("option");
			option.value = model.name;
			option.textContent = model.label;
			modelSwitcher.appendChild(option);
		}

		// Add the model switcher to the button bar
		buttons.appendChild(modelSwitcher);

		// Initialize a MutationObserver to watch the messages container
		for (const element of $$(SELECTORS.messagesContainer)) {
			new MutationObserver(replaceModelUnavailableMessage).observe(element, { childList: true, subtree: true });
		}
	}

	let chatGPTModelSelectObserver = null;

	// Watch ChatGPT's model selector
	function watchChatGPTModelSelect() {
		if (chatGPTModelSelectObserver) return;
		const chatGPTModelSelect = $(SELECTORS.chatGPTModelSelect);
		chatGPTModelSelectObserver = new MutationObserver(syncCustomModelSwitcher);
		chatGPTModelSelectObserver.observe(chatGPTModelSelect, { childList: true, characterData: true, subtree: true });
	}

	// Check if the chat interface has been loaded, and call the
	// initModelSwitcher function when it is detected.
	function chatInterfaceChanged() {
		if ($(SELECTORS.buttons) && $(SELECTORS.messagesContainer)) {
			initModelSwitcher();
		}
		if ($(SELECTORS.chatGPTModelSelect)) {
			watchChatGPTModelSelect();
		}
	}

	// Observe mutations to the body element, and call the
	// initModelSwitcher function when the chat interface is detected.
	new MutationObserver(chatInterfaceChanged).observe(document.body, { childList: true, subtree: true });

})();