From 059fa8c62b06ce05fd2124ac11eeef1a83023d8b Mon Sep 17 00:00:00 2001 From: mic Date: Tue, 20 May 2025 21:54:14 +0200 Subject: [PATCH] Anthropic options added to the prefs page. see #349 --- _locales/en/messages.json | 30 +++++++++- options/mzta-options.css | 13 +++++ options/mzta-options.html | 46 +++++++++++++++ options/mzta-options.js | 119 +++++++++++++++++++++++++++++++++++++- 4 files changed, 204 insertions(+), 4 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 61c51288..51330cbf 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1438,5 +1438,33 @@ "prefs_OptionText_Project_No_temporary_chat_warn": { "message": "If a Project is specified in the options or in a prompt, the temporary chat will not be used.", "description": "" - } + }, + "prefs_Anthropic_API_Key": { + "message": "Anthropic API Key", + "description": "" + }, + "prefs_Connection_type_Anthropic_API": { + "message": "Anthropic API", + "description": "" + }, + "Anthropic_Models": { + "message": "Anthropic Models", + "description": "" + }, + "Anthropic_Models_Fetch": { + "message": "Update Anthropic Models list", + "description": "" + }, + "Anthropic_Models_Error_fetching": { + "message": "Error trying to fetch Anthropic models", + "description": "" + }, + "Anthropic_Version": { + "message": "Anthropic API Version", + "description": "" + }, + "Anthropic_Version_Info": { + "message": "REQUIRED. Do not change this value unless you know what you're doing. More info at: ", + "description": "" + } } \ No newline at end of file diff --git a/options/mzta-options.css b/options/mzta-options.css index 47554130..0029bc67 100644 --- a/options/mzta-options.css +++ b/options/mzta-options.css @@ -120,6 +120,10 @@ tr.conntype_google_gemini_api, tr.conntype_google_gemini_api2{ background-color: rgb(233, 238, 169); } +tr.conntype_anthropic_api, tr.conntype_anthropic_api2{ + background-color: rgb(255, 168, 204); +} + #chatgpt_model_fetch_loading{ display: none; font-style: italic; @@ -178,6 +182,11 @@ input.option-input[type="text"]{ font-style: italic; } +#anthropic_model_fetch_loading{ + display: none; + font-style: italic; +} + #owl_warning{ display: none; } @@ -265,6 +274,10 @@ input.option-input[type="text"]{ background-color: rgb(72, 77, 3); } + tr.conntype_anthropic_api, tr.conntype_anthropic_api2{ + background-color: rgb(83, 0, 35); + } + .api_key-container .toggle-icon img { filter: invert(1); } diff --git a/options/mzta-options.html b/options/mzta-options.html index a3dffc18..187fd401 100644 --- a/options/mzta-options.html +++ b/options/mzta-options.html @@ -124,6 +124,7 @@ + @@ -384,6 +385,51 @@ + + + +
+ + +
+ + + + + + + + __MSG_Loading__
+ + + + + + + + + + + + + + + + __MSG_prefs_OptionText_max_prompt_length__ diff --git a/options/mzta-options.js b/options/mzta-options.js index 67d73003..d9786cc8 100644 --- a/options/mzta-options.js +++ b/options/mzta-options.js @@ -22,6 +22,7 @@ import { OpenAI } from '../js/api/openai.js'; import { Ollama } from '../js/api/ollama.js'; import { OpenAIComp } from '../js/api/openai_comp.js' import { GoogleGemini } from '../js/api/google_gemini.js'; +import { Anthropic } from '../js/api/anthropic.js'; import { ChatGPTWeb_models, checkSparksPresence, isThunderbird128OrGreater, openTab, sanitizeChatGPTModelData, sanitizeChatGPTWebCustomData, validateCustomData_ChatGPTWeb, getChatGPTWebModelsList_HTML } from '../js/mzta-utils.js'; let taLog = new taLogger("mzta-options",true); @@ -60,7 +61,7 @@ function saveOptions(e) { async function restoreOptions() { function setCurrentChoice(result) { document.querySelectorAll(".option-input").forEach(element => { - taLog.log("Options restoring " + element.id + " = " + (element.id=="chatgpt_api_key" || element.id=="openai_comp_api_key" || element.id=="google_gemini_api_key" ? "****************" : result[element.id])); + taLog.log("Options restoring " + element.id + " = " + (element.id=="chatgpt_api_key" || element.id=="openai_comp_api_key" || element.id=="google_gemini_api_key" || element.id=="anthropic_api_key" ? "****************" : result[element.id])); switch (element.type) { case 'checkbox': element.checked = result[element.id] || false; @@ -109,6 +110,7 @@ function showConnectionOptions() { let ollama_api_display = 'none'; let openai_comp_api_display = 'none'; let google_gemini_api_display = 'none'; + let anthropic_api_display = 'none'; let conntype_select = document.getElementById("connection_type"); let parent = conntype_select.parentElement.parentElement.parentElement; parent.classList.toggle("conntype_chatgpt_web", (conntype_select.value === "chatgpt_web")); @@ -116,6 +118,7 @@ function showConnectionOptions() { parent.classList.toggle("conntype_ollama_api", (conntype_select.value === "ollama_api")); parent.classList.toggle("conntype_openai_comp_api", (conntype_select.value === "openai_comp_api")); parent.classList.toggle("conntype_google_gemini_api", (conntype_select.value === "google_gemini_api")); + parent.classList.toggle("conntype_anthropic_api", (conntype_select.value === "anthropic_api")); if (conntype_select.value === "chatgpt_web") { chatgpt_web_display = 'table-row'; }else{ @@ -141,6 +144,11 @@ function showConnectionOptions() { }else{ google_gemini_api_display = 'none'; } + if (conntype_select.value === "anthropic_api") { + anthropic_api_display = 'table-row'; + }else{ + anthropic_api_display = 'none'; + } document.querySelectorAll(".conntype_chatgpt_web").forEach(element => { element.style.display = chatgpt_web_display; }); @@ -156,6 +164,9 @@ function showConnectionOptions() { document.querySelectorAll(".conntype_google_gemini_api").forEach(element => { element.style.display = google_gemini_api_display; }); + document.querySelectorAll(".conntype_anthropic_api").forEach(element => { + element.style.display = anthropic_api_display; + }); if (permission_all_urls) { document.getElementById('openai_comp_api_cors_warning').style.display = 'none'; document.getElementById('ollama_api_cors_warning').style.display = 'none'; @@ -250,6 +261,50 @@ function warn_OpenAIComp_HostEmpty() { } } +function warn_Anthropic_APIKeyEmpty() { + let apiKeyInput = document.getElementById('anthropic_api_key'); + let btnFetchAnthropicModels = document.getElementById('btnUpdateAnthropicModels'); + let modelAnthropic = document.getElementById('anthropic_model'); + if(apiKeyInput.value === ''){ + apiKeyInput.style.border = '2px solid red'; + btnFetchAnthropicModels.disabled = true; + modelAnthropic.disabled = true; + modelAnthropic.selectedIndex = -1; + modelAnthropic.style.border = ''; + }else{ + apiKeyInput.style.border = ''; + btnFetchAnthropicModels.disabled = false; + modelAnthropic.disabled = false; + if((modelAnthropic.selectedIndex === -1)||(modelAnthropic.value === '')){ + modelAnthropic.style.border = '2px solid red'; + }else{ + modelAnthropic.style.border = ''; + } + } +} + +function warn_Anthropic_VersionEmpty() { + let versionInput = document.getElementById('anthropic_version'); + let btnFetchAnthropicModels = document.getElementById('btnUpdateAnthropicModels'); + let modelAnthropic = document.getElementById('anthropic_model'); + if(versionInput.value === ''){ + versionInput.style.border = '2px solid red'; + btnFetchAnthropicModels.disabled = true; + modelAnthropic.disabled = true; + modelAnthropic.selectedIndex = -1; + modelAnthropic.style.border = ''; + }else{ + versionInput.style.border = ''; + btnFetchAnthropicModels.disabled = false; + modelAnthropic.disabled = false; + if((modelAnthropic.selectedIndex === -1)||(modelAnthropic.value === '')){ + modelAnthropic.style.border = '2px solid red'; + }else{ + modelAnthropic.style.border = ''; + } + } +} + function disable_MaxPromptLength(){ let maxPromptLength = document.getElementById('max_prompt_length'); let conntype_select = document.getElementById("connection_type"); @@ -450,6 +505,8 @@ document.addEventListener('DOMContentLoaded', async () => { conntype_select.addEventListener("change", warn_Ollama_HostEmpty); conntype_select.addEventListener("change", warn_OpenAIComp_HostEmpty); conntype_select.addEventListener("change", warn_GoogleGemini_APIKeyEmpty); + conntype_select.addEventListener("change", warn_Anthropic_APIKeyEmpty); + conntype_select.addEventListener("change", warn_Anthropic_VersionEmpty); conntype_select.addEventListener("change", disable_AddTags); conntype_select.addEventListener("change", disable_SpamFilter); conntype_select.addEventListener("change", disable_GetCalendarEvent); @@ -459,8 +516,10 @@ document.addEventListener('DOMContentLoaded', async () => { document.getElementById("google_gemini_api_key").addEventListener("change", warn_GoogleGemini_APIKeyEmpty); document.getElementById("chatgpt_web_project").addEventListener("input", validateCustomData_ChatGPTWeb); document.getElementById("chatgpt_web_custom_gpt").addEventListener("input", validateCustomData_ChatGPTWeb); + document.getElementById("anthropic_api_key").addEventListener("change", warn_Anthropic_APIKeyEmpty); + document.getElementById("anthropic_version").addEventListener("change", warn_Anthropic_VersionEmpty); - let prefs = await browser.storage.sync.get({chatgpt_web_model: '', chatgpt_model: '', ollama_model: '', openai_comp_model: '', google_gemini_model: '', chatgpt_win_height: 0, chatgpt_win_width: 0 }); + let prefs = await browser.storage.sync.get({chatgpt_web_model: '', chatgpt_model: '', ollama_model: '', openai_comp_model: '', google_gemini_model: '', anthropic_model: '', anthropic_version: '', chatgpt_win_height: 0, chatgpt_win_width: 0 }); // OpenAI API ChatGPT model fetching let select_chatgpt_model = document.getElementById('chatgpt_model'); @@ -502,7 +561,7 @@ document.addEventListener('DOMContentLoaded', async () => { warn_ChatGPT_APIKeyEmpty(); }); - // Google Gemini API ChatGPT model fetching + // Google Gemini API model fetching let select_google_gemini_model = document.getElementById('google_gemini_model'); const google_gemini_option = document.createElement('option'); google_gemini_option.value = prefs.google_gemini_model; @@ -640,11 +699,54 @@ document.addEventListener('DOMContentLoaded', async () => { warn_OpenAIComp_HostEmpty(); }); + // Anthropic API model fetching + let select_anthropic_model = document.getElementById('anthropic_model'); + const anthropic_option = document.createElement('option'); + anthropic_option.value = prefs.anthropic_model; + anthropic_option.text = prefs.anthropic_model; + select_anthropic_model.appendChild(anthropic_option); + select_anthropic_model.addEventListener("change", warn_Anthropic_APIKeyEmpty); + select_anthropic_model.addEventListener("change", warn_Anthropic_VersionEmpty); + + document.getElementById('btnUpdateAnthropicModels').addEventListener('click', async () => { + document.getElementById('anthropic_model_fetch_loading').style.display = 'inline'; + let anthropic = new Anthropic(document.getElementById("anthropic_api_key").value, document.getElementById("anthropic_version").value, '', true); + anthropic.fetchModels().then((data) => { + if(!data.ok){ + let errorDetail; + try { + errorDetail = JSON.parse(data.error); + errorDetail = errorDetail.error.message; + } catch (e) { + errorDetail = data.error; + } + document.getElementById('anthropic_model_fetch_loading').style.display = 'none'; + console.error("[ThunderAI] " + browser.i18n.getMessage("Anthropic_Models_Error_fetching")); + alert(browser.i18n.getMessage("Anthropic_Models_Error_fetching")+": " + errorDetail); + return; + } + taLog.log("Anthropic models: " + JSON.stringify(data)); + data.response.forEach(model => { + if (!Array.from(select_anthropic_model.options).some(option => option.value === model.id)) { + const option = document.createElement('option'); + option.value = model.id; + option.text = model.display_name + " (" + model.id + ")"; + select_anthropic_model.appendChild(option); + } + }); + document.getElementById('anthropic_model_fetch_loading').style.display = 'none'; + }); + + warn_Anthropic_APIKeyEmpty(); + }); + showConnectionOptions(); warn_ChatGPT_APIKeyEmpty(); warn_Ollama_HostEmpty(); warn_OpenAIComp_HostEmpty(); warn_GoogleGemini_APIKeyEmpty(); + warn_Anthropic_APIKeyEmpty(); + warn_Anthropic_VersionEmpty(); disable_MaxPromptLength(); disable_AddTags(); disable_SpamFilter(); @@ -683,6 +785,17 @@ document.addEventListener('DOMContentLoaded', async () => { icon_img_openai_comp_api_key.src = type === 'password' ? "../images/pwd-show.png" : "../images/pwd-hide.png"; }); + const passwordField_anthropic_api_key = document.getElementById('anthropic_api_key'); + const toggleIcon_anthropic_api_key = document.getElementById('toggle_anthropic_api_key'); + const icon_img_anthropic_api_key = document.getElementById('pwd-icon_anthropic_api_key'); + + toggleIcon_anthropic_api_key.addEventListener('click', () => { + const type = passwordField_anthropic_api_key.getAttribute('type') === 'password' ? 'text' : 'password'; + passwordField_anthropic_api_key.setAttribute('type', type); + + icon_img_anthropic_api_key.src = type === 'password' ? "../images/pwd-show.png" : "../images/pwd-hide.png"; + }); + const btnChatGPTWeb_Tab = document.getElementById('btnChatGPTWeb_Tab'); btnChatGPTWeb_Tab.addEventListener('click', async () => { let prefs_mod = await browser.storage.sync.get({chatgpt_web_model: prefs_default.chatgpt_web_model, chatgpt_web_project: prefs_default.chatgpt_web_project, chatgpt_web_custom_gpt: prefs_default.chatgpt_web_custom_gpt});