From a10cd8b5d46130161bb5f4abcd008be253f4f6f0 Mon Sep 17 00:00:00 2001 From: spdustin <1342542+spdustin@users.noreply.github.com> Date: Tue, 12 Dec 2023 10:47:11 -0600 Subject: [PATCH] merge conflicts --- autoExpertChatGPTDebugHelper.user.js | 79 +++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/autoExpertChatGPTDebugHelper.user.js b/autoExpertChatGPTDebugHelper.user.js index 1f5e4fa..59aef21 100644 --- a/autoExpertChatGPTDebugHelper.user.js +++ b/autoExpertChatGPTDebugHelper.user.js @@ -4,11 +4,17 @@ // @namespace https://spdustin.substack.com // @version 1.2.1 // @description Adds some helpful debugging tools to the ChatGPT UI -// @run-at document-idle +// @run-at document-end // @match https://chat.openai.com/* // @grant none // ==/UserScript== +const BASE_URL = "https://chat.openai.com"; +const GPT4_LIMIT_ENDPOINT = "/public-api/conversation_limit"; +const DEBUG_TRANSCRIPT_FILENAME = "autoexpert_debugger_transcript.json"; +const DEBUG_CUSTOM_INSTRUCTIONS_FILENAME = "user_system_messages.json"; +const CI_MODAL_TRIGGER = new KeyboardEvent("keydown", { + key: "I", const BASE_URL = "https://chat.openai.com"; const GPT4_LIMIT_ENDPOINT = "/public-api/conversation_limit"; const DEBUG_TRANSCRIPT_FILENAME = "autoexpert_debugger_transcript.json"; @@ -27,10 +33,16 @@ const DEBUG_CONTENT_LABEL_COLORS = { Result: "bg-green-100 text-green-700", Reply: "bg-gray-100 text-gray-700", Multi: "bg-blue-100 text-blue-700", + Text: "bg-yellow-100 text-yellow-700", + Result: "bg-green-100 text-green-700", + Reply: "bg-gray-100 text-gray-700", + Multi: "bg-blue-100 text-blue-700", }; const DEBUG_STATUS_LABEL_COLORS = { in_progress: "bg-yellow-100 text-yellow-700", finished_successfully: "bg-green-100 text-green-700", + in_progress: "bg-yellow-100 text-yellow-700", + finished_successfully: "bg-green-100 text-green-700", }; const DEBUG_MESSAGE_PROP_PATHS = [ @@ -78,8 +90,10 @@ class AEDataService { static RETRY_DELAY = 2000; + static TOKEN_ENDPOINT = "/api/auth/session"; static TOKEN_ENDPOINT = "/api/auth/session"; + static CI_ENDPOINT = "/backend-api/user_system_messages"; static CI_ENDPOINT = "/backend-api/user_system_messages"; constructor(baseUrl) { @@ -88,6 +102,7 @@ class AEDataService { this.tokenExpiration = Date.now(); this.defaultHeaders = { "Content-Type": "application/json", + "Content-Type": "application/json", }; } @@ -104,6 +119,8 @@ class AEDataService { throw new Error(`HTTP error! status: ${response.status}`); } const contentType = response.headers.get("Content-Type"); + if (contentType && contentType.includes("application/json")) { + const contentType = response.headers.get("Content-Type"); if (contentType && contentType.includes("application/json")) { return response.json(); } @@ -111,6 +128,10 @@ class AEDataService { } async fetchToken() { + if ( + Date.now() + AEDataService.TOKEN_EXPIRATION_BUFFER < + this.tokenExpiration + ) { if ( Date.now() + AEDataService.TOKEN_EXPIRATION_BUFFER < this.tokenExpiration @@ -120,6 +141,9 @@ class AEDataService { for (let retries = 0; retries < AEDataService.MAX_RETRIES; retries++) { try { + const response = await fetch( + `${this.baseUrl}${AEDataService.TOKEN_ENDPOINT}` + ); const response = await fetch( `${this.baseUrl}${AEDataService.TOKEN_ENDPOINT}` ); @@ -128,6 +152,11 @@ class AEDataService { } const tokenData = await response.json(); + if ( + tokenData && + tokenData.accessToken && + typeof tokenData.expires === "string" + ) { if ( tokenData && tokenData.accessToken && @@ -138,7 +167,9 @@ class AEDataService { return this.cachedToken; } throw new Error("Token data is missing the accessToken property"); + throw new Error("Token data is missing the accessToken property"); } catch (error) { + this.handleError("fetchToken", error); this.handleError("fetchToken", error); if (retries === AEDataService.MAX_RETRIES - 1) { throw error; @@ -156,9 +187,15 @@ class AEDataService { options, true ); + const userCustomInstructions = await this.fetchData( + AEDataService.CI_ENDPOINT, + options, + true + ); return userCustomInstructions; } catch (error) { handleError("getUserCustomInstructions", error); + handleError("getUserCustomInstructions", error); } } @@ -167,20 +204,26 @@ class AEDataService { const response = await this.fetchData( AEDataService.CI_ENDPOINT, { + method: "POST", method: "POST", headers: this.defaultHeaders, body: JSON.stringify(data), }, true + true ); if (response.error) { throw new Error( `Failed to update custom instructions: ${response.error}` ); + throw new Error( + `Failed to update custom instructions: ${response.error}` + ); } return response; } catch (error) { handleError("updateCustomInstructions", error); + handleError("updateCustomInstructions", error); } } @@ -194,6 +237,7 @@ class AEDataService { const aeDataService = new AEDataService(BASE_URL); function createDomElementFromHTML(htmlString) { + const tempDiv = document.createElement("div"); const tempDiv = document.createElement("div"); tempDiv.innerHTML = htmlString; return tempDiv.firstElementChild; @@ -202,12 +246,16 @@ function createDomElementFromHTML(htmlString) { async function setup() { const userCustomInstructions = await aeDataService.getUserCustomInstructions(); + const userCustomInstructions = + await aeDataService.getUserCustomInstructions(); function downloadJson(variable, filename) { const jsonString = JSON.stringify(variable, null, 2); const blob = new Blob([jsonString], { type: "application/json" }); + const blob = new Blob([jsonString], { type: "application/json" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); + const a = document.createElement("a"); a.href = url; a.download = filename; document.body.appendChild(a); @@ -252,10 +300,12 @@ async function setup() { const button = createDomElementFromHTML(buttonHtml); button.addEventListener("click", buttonData.handler, true); + button.addEventListener("click", buttonData.handler, true); return button; } async function updateLimitText() { + if (document.getElementById("prompt-textarea")) { if (document.getElementById("prompt-textarea")) { try { const options = {}; @@ -264,12 +314,18 @@ async function setup() { options, true ); + const data = await aeDataService.fetchData( + GPT4_LIMIT_ENDPOINT, + options, + true + ); if (data) { const limitText = document.forms[0].nextElementSibling.firstChild; limitText.innerText = data.message_disclaimer.textarea; } } catch (error) { handleError("updateLimitText", error); + handleError("updateLimitText", error); } } } @@ -299,6 +355,7 @@ async function setup() { window.location.reload(true); } catch (error) { handleError("handleToggleClick", error); + handleError("handleToggleClick", error); } }, }, @@ -314,8 +371,13 @@ async function setup() { userCustomInstructions, DEBUG_CUSTOM_INSTRUCTIONS_FILENAME ); + downloadJson( + userCustomInstructions, + DEBUG_CUSTOM_INSTRUCTIONS_FILENAME + ); } catch (error) { handleError("handleDownloadDataClick", error); + handleError("handleDownloadDataClick", error); } }, }, @@ -326,6 +388,7 @@ async function setup() { '', hideForGPTs: false, handler: () => { + debugPanel.innerHTML = ""; debugPanel.innerHTML = ""; messages = new Map(); }, @@ -458,6 +521,11 @@ function render(key, value) { let label; let value; switch (content.content_type) { + case "tether_quote": + label = "Quote"; + value = `${content.title} (${content.url})\n${escapeHtml( + content.text + )}`; case "tether_quote": label = "Quote"; value = `${content.title} (${content.url})\n${escapeHtml( @@ -468,6 +536,8 @@ function render(key, value) { label = "Browser"; value = content.results; break; + case "text": + label = "Reply"; case "text": label = "Reply"; value = escapeHtml(part); @@ -503,6 +573,8 @@ function render(key, value) { async function decodeEventStream(event) { const events = new TextDecoder().decode(event); events + .replace(/^data: /, "") + .split("\n") .replace(/^data: /, "") .split("\n") .filter(Boolean) @@ -543,6 +615,9 @@ async function logEventStream(response) { const originalFetch = window.fetch; window.fetch = async (...args) => { const response = await originalFetch(...args); + if ( + response.headers.get("content-type") === "text/event-stream; charset=utf-8" + ) { if ( response.headers.get("content-type") === "text/event-stream; charset=utf-8" ) { @@ -552,3 +627,5 @@ window.fetch = async (...args) => { }; setup(); + +document.messages = messages; \ No newline at end of file