ChatGPT Model Switcher

Switch ChatGPT model mid-chat

K instalaci tototo skriptu si budete muset nainstalovat rozšíření jako Tampermonkey, Greasemonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Userscripts.

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

K instalaci tohoto skriptu si budete muset nainstalovat manažer uživatelských skriptů.

(Už mám manažer uživatelských skriptů, nechte mě ho nainstalovat!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Už mám manažer uživatelských stylů, nechte mě ho nainstalovat!)

// ==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 });

})();