diff --git a/CHANGELOG.md b/CHANGELOG.md index f9eb4f9c..a2413584 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@
  • [ChatGPT Web] Added a message to explain to click on "Force completion" if the ChatGPT job is not done after 7 seconds [#419].
  • [All APIs] The prompt id and name are now shown in the information header in the AI API chat [#436].
  • [Google Gemini API]Support for the thinkingBudget parameter has been added [#494].
  • +
  • Various code improvements and minor bugs fixed.
  • ...
  • Version 3.6.1 - 23/08/2025

    diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 48cd1b80..b32e6d7f 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1140,7 +1140,7 @@ "description": "" }, "prompt_add_tags_use_list": { - "message": "Use only these tags, that are comma separated in the list", + "message": "Use only the tags in this comma separated list", "description": "" }, "prefs_OptionText_add_tags_auto_only_inbox": { @@ -1151,6 +1151,10 @@ "message": "If checked, the AI will only add tags to emails received in the inbox folder.", "description": "" }, + "prefs_OptionText_add_tags_use_specific_integration_Info": { + "message": "If checked, the Model and API specified below will be used for adding tags to emails, regardless the one choosen in the ThunderAI options page.", + "description": "" + }, "placeholder_thunderai_def_sign": { "message": "Default signature as defined in ThunderAI options.", "description": "" @@ -1203,6 +1207,14 @@ "message": "Spam Filter Options", "description": "" }, + "prefs_OptionText_use_specific_integration": { + "message": "Use specific Model and API", + "description": "" + }, + "prefs_OptionText_spamfilter_use_specific_integration_Info": { + "message": "If checked, the Model and API specified below will be used for the spam filter, regardless the one chosen in the ThunderAI options page.", + "description": "" + }, "prefs_OptionText_spamfilter_threshold": { "message": "Spam threshold", "description": "" @@ -1652,5 +1664,17 @@ "chatgpt_click_force_completion": { "message": "It seems that it's not possible to get if ChatGPT has finished. Click here to force the completion of the job.", "description": "" + }, + "warn_API_needed": { + "message": "To use this feature, you need an API integration rather than the ChatGPT Web Integration. You can define a specific API in the feature settings page by first checking the checkbox above and then clicking the button on the left.", + "description": "" + }, + "prefs_google_gemini_thinking_budget": { + "message": "Thinking Budget", + "description": "" + }, + "prefs_google_gemini_thinking_budget_Info": { + "message": "Define the number of tokens to be used for thinking. Leave this field blank if the selected model does not support thinking or if you want to use the default method. Enter 0 to disable thinking, or -1 to enable dynamic thinking.", + "description": "" } -} \ No newline at end of file +} diff --git a/api_webchat/controller.js b/api_webchat/controller.js index ccec08d4..ef4050c1 100644 --- a/api_webchat/controller.js +++ b/api_webchat/controller.js @@ -95,7 +95,7 @@ switch (llm) { chatgpt_developer_messages: prefs_api.chatgpt_developer_messages, chatgpt_api_store: prefs_api.chatgpt_api_store, do_debug: prefs_api.do_debug, - i18nStrings: i18nStrings + i18nStrings: i18nStrings, }); let additional_text_elements = []; additional_text_elements.push({label: 'OpenAI Store', value: (prefs_api.chatgpt_api_store ? 'Yes' : 'No')}); @@ -140,7 +140,7 @@ switch (llm) { google_gemini_system_instruction: prefs_api.google_gemini_system_instruction, google_gemini_thinking_budget: prefs_api.google_gemini_thinking_budget, do_debug: prefs_api.do_debug, - i18nStrings: i18nStrings + i18nStrings: i18nStrings, }); messagesArea.appendUserMessage(getAPIsInitMessageString({ api_string: "Google Gemini API", @@ -210,7 +210,7 @@ switch (llm) { openai_comp_api_key: prefs_api.openai_comp_api_key, openai_comp_use_v1: prefs_api.openai_comp_use_v1, do_debug: prefs_api.do_debug, - i18nStrings: i18nStrings + i18nStrings: i18nStrings, }); let additional_text_elements = []; additional_text_elements.push({label: "Prompt", value: '[' + prompt_id + '] ' + decodeURIComponent(prompt_name)}); @@ -246,7 +246,7 @@ switch (llm) { anthropic_version: prefs_api.anthropic_version, anthropic_max_tokens: prefs_api.anthropic_max_tokens, do_debug: prefs_api.do_debug, - i18nStrings: i18nStrings + i18nStrings: i18nStrings, }); let additional_text_elements = []; additional_text_elements.push({label: "Prompt", value: '[' + prompt_id + '] ' + decodeURIComponent(prompt_name)}); diff --git a/js/mzta-menus.js b/js/mzta-menus.js index e58be3aa..771cb1d7 100644 --- a/js/mzta-menus.js +++ b/js/mzta-menus.js @@ -20,7 +20,19 @@ import { getPrompts } from './mzta-prompts.js'; import { prefs_default } from '../options/mzta-options-default.js'; -import { getLanguageDisplayName, getMenuContextCompose, getMenuContextDisplay, i18nConditionalGet, getMailSubject, getTagsList, extractJsonObject, convertNewlinesToBr, cleanupNewlines, checkIfTagLabelExists } from './mzta-utils.js' +import { + getLanguageDisplayName, + getMenuContextCompose, + getMenuContextDisplay, + i18nConditionalGet, + getMailSubject, + getTagsList, + extractJsonObject, + convertNewlinesToBr, + cleanupNewlines, + checkIfTagLabelExists, + getConnectionType, + } from './mzta-utils.js' import { taPromptUtils } from './mzta-utils-prompt.js'; import { taLogger } from './mzta-logger.js'; import { placeholdersUtils } from './mzta-placeholders.js'; @@ -191,8 +203,9 @@ export class mzta_Menus { default_chatgpt_lang: prefs_default.default_chatgpt_lang, do_debug: prefs_default.do_debug, }); - if((prefs_at.connection_type === '')||(prefs_at.connection_type === null)||(prefs_at.connection_type === undefined)||(prefs_at.connection_type === 'chatgpt_web')){ - console.error("[ThunderAI | AddTags] Invalid connection type: " + prefs_at.connection_type); + let def_conntype = getConnectionType(prefs_at.connection_type, curr_prompt); + if((def_conntype === '')||(def_conntype === null)||(def_conntype === undefined)||(def_conntype === 'chatgpt_web')){ + console.error("[ThunderAI | AddTags] Invalid connection type: " + def_conntype); taWorkingStatus.stopWorking(); return {ok:'0'}; } @@ -200,10 +213,17 @@ export class mzta_Menus { this.logger.log("fullPrompt: " + fullPrompt); let create_new_tags = !prefs_at.add_tags_auto_force_existing; let all_tags_list = tags_full_list[1]; - // TODO: use the current API, abort if using chatgpt web // COMMENTED TO DO TESTS // tags_current_email = "recipients, TEST, home, work, CAR, light"; - let cmd_addTags = new mzta_specialCommand(fullPrompt,prefs_at.connection_type,prefs_at.do_debug); + console.log(">>>>>>>>>>>>> curr_prompt: " + JSON.stringify(curr_prompt)); + console.log(">>>>>>>>>>>>>> prefs_at.connection_type: " + JSON.stringify(prefs_at.connection_type)); + console.log(">>>>>>>>>>>>>> def_conntype: " + JSON.stringify(def_conntype)); + let cmd_addTags = new mzta_specialCommand({ + prompt: fullPrompt, + llm: def_conntype, + custom_model: curr_prompt.model ? curr_prompt.model : '', + do_debug: prefs_at.do_debug + }); await cmd_addTags.initWorker(); try{ tags_current_email = taPromptUtils.getTagsFromResponse(await cmd_addTags.sendPrompt()); @@ -255,7 +275,11 @@ export class mzta_Menus { */ fullPrompt = taPromptUtils.finalizePrompt_get_calendar_event(fullPrompt); this.logger.log("fullPrompt: " + fullPrompt); - let cmd_GetCalendarEvent = new mzta_specialCommand(fullPrompt,prefs_at.connection_type,true); + let cmd_GetCalendarEvent = new mzta_specialCommand({ + prompt: fullPrompt, + llm: prefs_at.connection_type, + do_debug: true + }); await cmd_GetCalendarEvent.initWorker(); try{ calendar_event_data = await cmd_GetCalendarEvent.sendPrompt(); @@ -323,7 +347,11 @@ export class mzta_Menus { * } */ this.logger.log("fullPrompt: " + fullPrompt); - let cmd_GetTask = new mzta_specialCommand(fullPrompt,prefs_at.connection_type,true); + let cmd_GetTask = new mzta_specialCommand({ + prompt: fullPrompt, + llm: prefs_at.connection_type, + do_debug: true + }); await cmd_GetTask.initWorker(); try{ task_data = await cmd_GetTask.sendPrompt(); diff --git a/js/mzta-prompts.js b/js/mzta-prompts.js index f1fbce97..3fa64d79 100644 --- a/js/mzta-prompts.js +++ b/js/mzta-prompts.js @@ -72,6 +72,12 @@ ChatGPT Web Custom GPT (chatgpt_web_custom_gpt attribute): : custom gpt id url + API Connection Type + : api type + + API Model + : api model + */ const defaultPrompts = [ @@ -89,6 +95,8 @@ const defaultPrompts = [ chatgpt_web_model: '', chatgpt_web_project: '', chatgpt_web_custom_gpt: '', + api_type: '', + api_model: '', is_default: "1", is_special: "0", }, @@ -106,6 +114,8 @@ const defaultPrompts = [ chatgpt_web_model: '', chatgpt_web_project: '', chatgpt_web_custom_gpt: '', + api_type: '', + api_model: '', is_default: "1", is_special: "0", }, @@ -123,6 +133,8 @@ const defaultPrompts = [ chatgpt_web_model: '', chatgpt_web_project: '', chatgpt_web_custom_gpt: '', + api_type: '', + api_model: '', is_default: "1", is_special: "0", }, @@ -140,6 +152,8 @@ const defaultPrompts = [ chatgpt_web_model: '', chatgpt_web_project: '', chatgpt_web_custom_gpt: '', + api_type: '', + api_model: '', is_default: "1", is_special: "0", }, @@ -157,6 +171,8 @@ const defaultPrompts = [ chatgpt_web_model: '', chatgpt_web_project: '', chatgpt_web_custom_gpt: '', + api_type: '', + api_model: '', is_default: "1", is_special: "0", }, @@ -174,6 +190,8 @@ const defaultPrompts = [ chatgpt_web_model: '', chatgpt_web_project: '', chatgpt_web_custom_gpt: '', + api_type: '', + api_model: '', is_default: "1", is_special: "0", }, @@ -191,6 +209,8 @@ const defaultPrompts = [ chatgpt_web_model: '', chatgpt_web_project: '', chatgpt_web_custom_gpt: '', + api_type: '', + api_model: '', is_default: "1", is_special: "0", }, @@ -208,6 +228,8 @@ const defaultPrompts = [ chatgpt_web_model: '', chatgpt_web_project: '', chatgpt_web_custom_gpt: '', + api_type: '', + api_model: '', is_default: "1", is_special: "0", }, @@ -225,6 +247,8 @@ const defaultPrompts = [ chatgpt_web_model: '', chatgpt_web_project: '', chatgpt_web_custom_gpt: '', + api_type: '', + api_model: '', is_default: "1", is_special: "0", }, @@ -242,6 +266,8 @@ const defaultPrompts = [ chatgpt_web_model: '', chatgpt_web_project: '', chatgpt_web_custom_gpt: '', + api_type: '', + api_model: '', is_default: "1", is_special: "0", }, @@ -259,6 +285,8 @@ const specialPrompts = [ need_custom_text: "0", define_response_lang: "0", use_diff_viewer: "0", + api_type: '', + api_model: '', is_default: "1", is_special: "1", }, @@ -273,6 +301,8 @@ const specialPrompts = [ need_custom_text: "0", define_response_lang: "0", use_diff_viewer: "0", + api_type: '', + api_model: '', is_default: "1", is_special: "1", }, @@ -287,6 +317,8 @@ const specialPrompts = [ need_custom_text: "0", define_response_lang: "0", use_diff_viewer: "0", + api_type: '', + api_model: '', is_default: "1", is_special: "1", }, @@ -301,24 +333,26 @@ const specialPrompts = [ need_custom_text: "0", define_response_lang: "0", use_diff_viewer: "0", + api_type: '', + api_model: '', is_default: "1", is_special: "1", }, ]; -export async function getPrompts(onlyEnabled = false, includeSpecial = []){ // includeSpecial is an array of active special prompts ids +export async function getPrompts(onlyEnabled = false, includeSpecial = [], allSpecial = false){ // includeSpecial is an array of active special prompts ids const _defaultPrompts = await getDefaultPrompts_withProps(); // console.log('>>>>>>>>>>>> getPrompts _defaultPrompts: ' + JSON.stringify(_defaultPrompts)); const customPrompts = await getCustomPrompts(); // console.log('>>>>>>>>>>>> getPrompts customPrompts: ' + JSON.stringify(customPrompts)); const specialPrompts = await getSpecialPrompts(); let output = specialPrompts.concat(_defaultPrompts).concat(customPrompts); - if(includeSpecial.length == 0){ + if((includeSpecial.length == 0) && !allSpecial){ output = output.filter(obj => obj.is_special != 1); // we do not want special prompts }else{ // console.log(">>>>>>>>>> getPrompts includeSpecial: " + JSON.stringify(includeSpecial)); - output = output.filter(obj => includeSpecial.includes(obj.id) || obj.is_special != 1); + output = output.filter(obj => includeSpecial.includes(obj.id) || obj.is_special != 1 || allSpecial); // output = output.filter(obj => { // const isIncluded = includeSpecial.includes(obj.id); // const isNotSpecial = obj.is_special != 1; @@ -402,6 +436,11 @@ async function getDefaultPrompts_withProps() { prompt.position_display = prefs._default_prompts_properties[prompt.id].position_display; prompt.enabled = prefs._default_prompts_properties[prompt.id].enabled; prompt.need_custom_text = prefs._default_prompts_properties[prompt.id].need_custom_text; + prompt.chatgpt_web_model = prefs._default_prompts_properties[prompt.id].chatgpt_web_model; + prompt.chatgpt_web_project = prefs._default_prompts_properties[prompt.id].chatgpt_web_project; + prompt.chatgpt_web_custom_gpt = prefs._default_prompts_properties[prompt.id].chatgpt_web_custom_gpt; + prompt.api_type = prefs._default_prompts_properties[prompt.id].api_type; + prompt.api_model = prefs._default_prompts_properties[prompt.id].api_model; }else{ prompt.position_display = pos; prompt.position_compose = pos; @@ -433,6 +472,12 @@ async function getCustomPrompts() { if(prompt.chatgpt_web_custom_gpt === undefined){ prompt.chatgpt_web_custom_gpt = ""; } + if(prompt.api_type === undefined){ + prompt.api_type = ""; + } + if(prompt.api_model === undefined){ + prompt.api_model = ""; + } }); return prefs._custom_prompt; } @@ -441,7 +486,17 @@ async function getCustomPrompts() { export async function setDefaultPromptsProperties(prompts) { let default_prompts_properties = {}; prompts.forEach((prompt) => { - default_prompts_properties[prompt.id] = {position_compose: prompt.position_compose, position_display: prompt.position_display, enabled: prompt.enabled, need_custom_text: prompt.need_custom_text}; + default_prompts_properties[prompt.id] = { + position_compose: prompt.position_compose, + position_display: prompt.position_display, + enabled: prompt.enabled, + need_custom_text: prompt.need_custom_text, + chatgpt_web_model: prompt.chatgpt_web_model, + chatgpt_web_project: prompt.chatgpt_web_project, + chatgpt_web_custom_gpt: prompt.chatgpt_web_custom_gpt, + api_type: prompt.api_type, + api_model: prompt.api_model + }; }); //console.log('>>>>>>>>>>>>>> default_prompts_properties: ' + JSON.stringify(default_prompts_properties)); await browser.storage.local.set({_default_prompts_properties: default_prompts_properties}); @@ -486,4 +541,56 @@ export async function setSpecialPrompts(prompts) { export async function getSpamFilterPrompt(){ return (await getSpecialPrompts()).find(prompt => prompt.id == 'prompt_spamfilter'); +} + +export async function loadPrompt(id) { + let allPrompts = await getPrompts(false,[],true); + // console.log(">>>>>>>>>>>> loadPrompt id: " + id + " - allPrompts: " + JSON.stringify(allPrompts)); + return allPrompts.find(prompt => prompt.id === id); +} + +export async function savePrompt(prompt) { + // console.log(">>>>>>>>>>>>> savePrompt prompt: " + JSON.stringify(prompt)); + if (prompt.id === undefined) { + throw new Error("Invalid prompt: " + JSON.stringify(prompt)); + } + // Special Prompt + if (prompt.is_special === "1") { + let specialPrompts = await getSpecialPrompts(); + let index = specialPrompts.findIndex(p => p.id === prompt.id); + if (index === -1) { + specialPrompts.push(prompt); + } else { + specialPrompts[index] = prompt; + } + await setSpecialPrompts(specialPrompts); + return; + } + // Custom Prompt + if (prompt.is_default === "0") { + let customPrompts = await getCustomPrompts(); + let index = customPrompts.findIndex(p => p.id === prompt.id); + if (index === -1) { + customPrompts.push(prompt); + } else { + customPrompts[index] = prompt; + } + await setCustomPrompts(customPrompts); + } else { // Default Prompt + let defaultPrompts = getDefaultPrompts_withProps(); + let index = defaultPrompts.findIndex(p => p.id === prompt.id); + if (index === -1) { + defaultPrompts.push(prompt); + } else { + defaultPrompts[index] = prompt; + } + await setDefaultPromptsProperties(defaultPrompts); + } +} + +export async function clearPromptAPI(id){ + let _prompt = await loadPrompt(id); + _prompt.api = ""; + _prompt.model = ""; + await savePrompt(_prompt); } \ No newline at end of file diff --git a/js/mzta-special-commands.js b/js/mzta-special-commands.js index 2734f2ce..e23618da 100644 --- a/js/mzta-special-commands.js +++ b/js/mzta-special-commands.js @@ -17,7 +17,7 @@ */ // Call the API to use a special prompt - + import { prefs_default } from "../options/mzta-options-default.js"; import { taLogger } from './mzta-logger.js'; import { prefs_default } from '../options/mzta-options-default.js'; @@ -27,13 +27,21 @@ prompt = ""; worker = null; llm = ""; + custom_model = ""; full_message = ""; logger = null; do_debug = false; - constructor(prompt, llm, do_debug = false) { + constructor(args = {}) { + let { + prompt = '', + llm = '', + custom_model = '', + do_debug = false + } = args; this.prompt = prompt; this.llm = llm; + this.custom_model = custom_model; this.logger = new taLogger('mzta_specialCommand', do_debug); this.do_debug = do_debug; switch (this.llm) { @@ -59,6 +67,7 @@ } async initWorker() { + // console.log((">>>>>>>>>>>> this.custom_model: " + this.custom_model)); switch (this.llm) { case "chatgpt_api": { let prefs_api = await browser.storage.sync.get({ @@ -69,7 +78,7 @@ this.worker.postMessage({ type: 'init', chatgpt_api_key: prefs_api.chatgpt_api_key, - chatgpt_model: prefs_api.chatgpt_model, + chatgpt_model: this.custom_model != '' ? this.custom_model : prefs_api.chatgpt_model, chatgpt_developer_messages: prefs_api.chatgpt_developer_messages, do_debug: this.do_debug, i18nStrings: '' @@ -86,7 +95,7 @@ this.worker.postMessage({ type: 'init', google_gemini_api_key: prefs_api.google_gemini_api_key, - google_gemini_model: prefs_api.google_gemini_model, + google_gemini_model: this.custom_model != '' ? this.custom_model : prefs_api.google_gemini_model, google_gemini_system_instruction: prefs_api.google_gemini_system_instruction, google_gemini_thinking_budget: prefs_api.google_gemini_thinking_budget, do_debug: this.do_debug, @@ -102,7 +111,7 @@ this.worker.postMessage({ type: 'init', ollama_host: prefs_api.ollama_host, - ollama_model: prefs_api.ollama_model, + ollama_model: this.custom_model != '' ? this.custom_model : prefs_api.ollama_model, do_debug: this.do_debug, i18nStrings: '' }); @@ -120,7 +129,7 @@ this.worker.postMessage({ type: 'init', openai_comp_host: prefs_api.openai_comp_host, - openai_comp_model: prefs_api.openai_comp_model, + openai_comp_model: this.custom_model != '' ? this.custom_model : prefs_api.openai_comp_model, openai_comp_api_key: prefs_api.openai_comp_api_key, openai_comp_use_v1: prefs_api.openai_comp_use_v1, do_debug: this.do_debug, @@ -138,7 +147,7 @@ this.worker.postMessage({ type: 'init', anthropic_api_key: prefs_api.anthropic_api_key, - anthropic_model: prefs_api.anthropic_model, + anthropic_model: this.custom_model != '' ? this.custom_model : prefs_api.anthropic_model, anthropic_version: prefs_api.anthropic_version, anthropic_max_tokens: prefs_api.anthropic_max_tokens, do_debug: this.do_debug, diff --git a/js/mzta-utils-prompt.js b/js/mzta-utils-prompt.js index 03f29c53..896f6406 100644 --- a/js/mzta-utils-prompt.js +++ b/js/mzta-utils-prompt.js @@ -89,7 +89,7 @@ export const taPromptUtils = { fullPrompt += " \n" + browser.i18n.getMessage("prompt_add_tags_force_lang") + " " + default_chatgpt_lang + "."; } if(add_tags_auto_uselist && add_tags_auto_uselist_list && add_tags_auto_uselist_list.length > 0){ - fullPrompt += " \n" + browser.i18n.getMessage("prompt_add_tags_use_list") + " " + add_tags_auto_uselist_list + "."; + fullPrompt += " \n" + browser.i18n.getMessage("prompt_add_tags_use_list") + ": " + add_tags_auto_uselist_list + "."; } return fullPrompt; diff --git a/js/mzta-utils.js b/js/mzta-utils.js index af6e82fc..b486f343 100644 --- a/js/mzta-utils.js +++ b/js/mzta-utils.js @@ -549,10 +549,21 @@ export function getAPIsInitMessageString(args = {}) { } export function getActiveSpecialPromptsIDs(args = {}) { - const { addtags = false, get_calendar_event = false, get_task = false, is_chatgpt_web = false } = args; + const { + addtags = false, + addtags_api = false, + get_calendar_event = false, + get_task = false, + is_chatgpt_web = false + } = args; + // The Antispam filter is not here, because this method is used only + // to reload the ThunderAI button menu, not the context menu let output = []; // console.log(">>>>>>>>>> getActiveSpecialPromptsIDs args: " + JSON.stringify(args)); if (is_chatgpt_web) { + if (addtags_api && addtags) { + output.push('prompt_add_tags'); + } return output; } if (addtags) { @@ -568,6 +579,10 @@ export function getActiveSpecialPromptsIDs(args = {}) { return output; } +export function checkSpecificIntegration(use, conntype){ + return use && (conntype != null) && (conntype !== ''); +} + export function extractJsonObject(inputString) { try { const jsonMatch = inputString.match(/\{[\s\S]*\}/); @@ -587,6 +602,22 @@ export function extractJsonObject(inputString) { } } +export function isAPIKeyValue(id){ + return id=="chatgpt_api_key" || id=="openai_comp_api_key" || id=="google_gemini_api_key" || id=="anthropic_api_key"; +} + +export function getConnectionType(conntype, prompt, use_promptspecific_api = true) { + if(!use_promptspecific_api) { + return conntype; + } + console.log(">>>>>>>>>>> getConnectionType conntype: " + conntype + " prompt: " + JSON.stringify(prompt)); + if (prompt?.api != null && prompt.api !== '') { + return prompt.api; + } else { + return conntype; + } +} + export async function checkSparksPresence() { try { let sparks_current = await browser.runtime.sendMessage('thunderai-sparks@micz.it',{action: "checkPresence"}); diff --git a/js/workers/model-worker-anthropic.js b/js/workers/model-worker-anthropic.js index 9f311e7d..39cc136f 100644 --- a/js/workers/model-worker-anthropic.js +++ b/js/workers/model-worker-anthropic.js @@ -36,6 +36,7 @@ let assistantResponseAccumulator = ''; self.onmessage = async function(event) { if (event.data.type === 'init') { + // console.log(">>>>>>>>>>>>>> event.data: " + JSON.stringify(event.data)); anthropic_api_key = event.data.anthropic_api_key; anthropic_model = event.data.anthropic_model; anthropic = new Anthropic({ diff --git a/mzta-background.js b/mzta-background.js index c3c9c290..e5f0f42c 100644 --- a/mzta-background.js +++ b/mzta-background.js @@ -20,7 +20,35 @@ import { mzta_script } from './js/mzta-chatgpt.js'; import { prefs_default } from './options/mzta-options-default.js'; import { mzta_Menus } from './js/mzta-menus.js'; import { taLogger } from './js/mzta-logger.js'; -import { getCurrentIdentity, getOriginalBody, replaceBody, setBody, i18nConditionalGet, generateCallID, migrateCustomPromptsStorage, migrateDefaultPromptsPropStorage, getGPTWebModelString, getTagsList, createTag, assignTagsToMessage, checkIfTagLabelExists, getActiveSpecialPromptsIDs, checkSparksPresence, getMessages, getMailBody, extractJsonObject, contextMenuID_AddTags, contextMenuID_Spamfilter, sanitizeChatGPTModelData, sanitizeChatGPTWebCustomData, stripHtmlKeepLines, htmlBodyToPlainText, convertNewlinesToParagraphs } from './js/mzta-utils.js'; +import { + getCurrentIdentity, + getOriginalBody, + replaceBody, + setBody, + i18nConditionalGet, + generateCallID, + migrateCustomPromptsStorage, + migrateDefaultPromptsPropStorage, + getGPTWebModelString, + getTagsList, + createTag, + assignTagsToMessage, + checkIfTagLabelExists, + getActiveSpecialPromptsIDs, + checkSparksPresence, + getMessages, + getMailBody, + extractJsonObject, + contextMenuID_AddTags, + contextMenuID_Spamfilter, + sanitizeChatGPTModelData, + sanitizeChatGPTWebCustomData, + stripHtmlKeepLines, + htmlBodyToPlainText, + convertNewlinesToParagraphs, + getConnectionType, + checkSpecificIntegration, + } from './js/mzta-utils.js'; import { taPromptUtils } from './js/mzta-utils-prompt.js'; import { mzta_specialCommand } from './js/mzta-special-commands.js'; import { getSpamFilterPrompt } from './js/mzta-prompts.js'; @@ -56,6 +84,7 @@ taWorkingStatus.taLog = taLog; let special_prompts_ids = getActiveSpecialPromptsIDs({ addtags: prefs_init.add_tags, + addtags_api: checkSpecificIntegration(prefs_init.add_tags_use_specific_integration, prefs_init.add_tags_connection_type), get_calendar_event: doGetSparkFeature(prefs_init.get_calendar_event), get_task: doGetSparkFeature(prefs_init.get_task), is_chatgpt_web: (prefs_init.connection_type === "chatgpt_web") @@ -146,6 +175,7 @@ async function _reload_menus() { let getTask = doGetSparkFeature(prefs_reload.get_task); const special_prompts_ids = getActiveSpecialPromptsIDs({ addtags: prefs_reload.add_tags, + addtags_api: checkSpecificIntegration(prefs_init.add_tags_use_specific_integration, prefs_init.add_tags_connection_type), get_calendar_event: getCalendarEvent, get_task: getTask, is_chatgpt_web: (prefs_reload.connection_type === "chatgpt_web") @@ -749,7 +779,25 @@ function doGetSparkFeature(spark_feature_active) { } async function reload_pref_init(){ - prefs_init = await browser.storage.sync.get({do_debug: prefs_default.do_debug, add_tags: prefs_default.add_tags, get_calendar_event: prefs_default.get_calendar_event, get_task: prefs_default.get_task, connection_type: prefs_default.connection_type, add_tags_auto: prefs_default.add_tags_auto, add_tags_auto_force_existing: prefs_default.add_tags_auto_force_existing, add_tags_auto_only_inbox: prefs_default.add_tags_auto_only_inbox, spamfilter: prefs_default.spamfilter, spamfilter_threshold: prefs_default.spamfilter_threshold, dynamic_menu_force_enter: prefs_default.dynamic_menu_force_enter, add_tags_context_menu: prefs_default.add_tags_context_menu, spamfilter_context_menu: prefs_default.spamfilter_context_menu}); + prefs_init = await browser.storage.sync.get({ + do_debug: prefs_default.do_debug, + add_tags: prefs_default.add_tags, + get_calendar_event: prefs_default.get_calendar_event, + get_task: prefs_default.get_task, + connection_type: prefs_default.connection_type, + add_tags_auto: prefs_default.add_tags_auto, + add_tags_auto_force_existing: prefs_default.add_tags_auto_force_existing, + add_tags_auto_only_inbox: prefs_default.add_tags_auto_only_inbox, + spamfilter: prefs_default.spamfilter, + spamfilter_threshold: prefs_default.spamfilter_threshold, + dynamic_menu_force_enter: prefs_default.dynamic_menu_force_enter, + add_tags_context_menu: prefs_default.add_tags_context_menu, + spamfilter_context_menu: prefs_default.spamfilter_context_menu, + add_tags_use_specific_integration: prefs_default.add_tags_use_specific_integration, + add_tags_connection_type: prefs_default.add_tags_connection_type, + spamfilter_use_specific_integration: prefs_default.spamfilter_use_specific_integration, + spamfilter_connection_type: prefs_default.spamfilter_connection_type + }); _process_incoming = prefs_init.add_tags_auto || prefs_init.spamfilter; _sparks_presence = await checkSparksPresence(); } @@ -767,6 +815,7 @@ function setupStorageChangeListener() { let getTask = doGetSparkFeature(prefs_init.get_task); const special_prompts_ids = getActiveSpecialPromptsIDs({ addtags: newTags, + addtags_api: checkSpecificIntegration(prefs_init.add_tags_use_specific_integration, prefs_init.add_tags_connection_type), get_calendar_event: getCalendarEvent, get_task: getTask, is_chatgpt_web: (prefs_init.connection_type === "chatgpt_web") @@ -781,6 +830,7 @@ function setupStorageChangeListener() { let getTask = doGetSparkFeature(prefs_init.get_task); const special_prompts_ids = getActiveSpecialPromptsIDs({ addtags: prefs_init.add_tags, + addtags_api: checkSpecificIntegration(prefs_init.add_tags_use_specific_integration, prefs_init.add_tags_connection_type), get_calendar_event: getCalendarEvent, get_task: getTask, is_chatgpt_web: (prefs_init.connection_type === "chatgpt_web") @@ -795,6 +845,7 @@ function setupStorageChangeListener() { let getTask = doGetSparkFeature(newTask); const special_prompts_ids = getActiveSpecialPromptsIDs({ addtags: prefs_init.add_tags, + addtags_api: checkSpecificIntegration(prefs_init.add_tags_use_specific_integration, prefs_init.add_tags_connection_type), get_calendar_event: getCalendarEvent, get_task: getTask, is_chatgpt_web: (prefs_init.connection_type === "chatgpt_web") @@ -809,6 +860,7 @@ function setupStorageChangeListener() { let getTask = doGetSparkFeature(prefs_init.get_task); const special_prompts_ids = getActiveSpecialPromptsIDs({ addtags: prefs_init.add_tags, + addtags_api: checkSpecificIntegration(prefs_init.add_tags_use_specific_integration, prefs_init.add_tags_connection_type), get_calendar_event: getCalendarEvent, get_task: getTask, is_chatgpt_web: (newConnectionType === "chatgpt_web") @@ -895,6 +947,7 @@ menus.loadMenus(special_prompts_ids); // Context Menus function addContextMenu(menu_id) { + browser.menus.remove(menu_id); browser.menus.create({ id: menu_id, title: browser.i18n.getMessage("context_menu_" + menu_id), @@ -909,12 +962,12 @@ function removeContextMenu(menu_id) { } // Add Context menu: Add tags -if(prefs_init.add_tags && prefs_init.add_tags_context_menu && (prefs_init.connection_type !== "chatgpt_web")){ +if(prefs_init.add_tags && prefs_init.add_tags_context_menu && ((prefs_init.connection_type !== "chatgpt_web")||checkSpecificIntegration(prefs_init.add_tags_use_specific_integration,prefs_init.add_tags_connection_type))){ addContextMenu(contextMenuID_AddTags); } // Add Context menu: Spamfilter -if(prefs_init.spamfilter && prefs_init.spamfilter_context_menu && (prefs_init.connection_type !== "chatgpt_web")){ +if(prefs_init.spamfilter && prefs_init.spamfilter_context_menu && ((prefs_init.connection_type !== "chatgpt_web")||checkSpecificIntegration(prefs_init.spamfilter_use_specific_integration,prefs_init.spamfilter_connection_type))){ addContextMenu(contextMenuID_Spamfilter); } @@ -976,6 +1029,9 @@ async function processEmails(messages, addTagsAuto, spamFilter) { add_tags_auto_uselist: prefs_default.add_tags_auto_uselist, add_tags_auto_uselist_list: prefs_default.add_tags_auto_uselist_list, spamfilter_enabled_accounts: prefs_default.spamfilter_enabled_accounts, + add_tags_use_specific_integration: prefs_default.add_tags_use_specific_integration, + spamfilter_use_specific_integration: prefs_default.spamfilter_use_specific_integration, + do_debug: prefs_default.do_debug, }); for await (let message of messages) { @@ -1005,7 +1061,7 @@ async function processEmails(messages, addTagsAuto, spamFilter) { let specialFullPrompt_add_tags = ''; let curr_prompt_add_tags = menus.allPrompts.find(p => p.id === 'prompt_add_tags'); let tags_full_list = await getTagsList(); - // console.log(">>>>>>>>>>>>> curr_prompt_add_tags: " + curr_prompt_add_tags); + // console.log(">>>>>>>>>>>>> curr_prompt_add_tags: " + JSON.stringify(curr_prompt_add_tags)); let chatgpt_lang = await taPromptUtils.getDefaultLang(curr_prompt_add_tags); specialFullPrompt_add_tags = await taPromptUtils.preparePrompt({ curr_prompt: curr_prompt_add_tags, @@ -1018,7 +1074,13 @@ async function processEmails(messages, addTagsAuto, spamFilter) { }); specialFullPrompt_add_tags = taPromptUtils.finalizePrompt_add_tags(specialFullPrompt_add_tags, prefs_aats.add_tags_maxnum, prefs_aats.add_tags_force_lang, prefs_aats.default_chatgpt_lang, prefs_aats.add_tags_auto_uselist, prefs_aats.add_tags_auto_uselist_list); taLog.log("Special prompt: " + specialFullPrompt_add_tags); - let cmd_addTags = new mzta_specialCommand(specialFullPrompt_add_tags, prefs_aats.connection_type, prefs_init.do_debug); + // console.log(">>>>>>>>>> curr_prompt_add_tags.model: " + curr_prompt_add_tags.model); + let cmd_addTags = new mzta_specialCommand({ + prompt: specialFullPrompt_add_tags, + llm: getConnectionType(prefs_aats.connection_type, curr_prompt_add_tags, prefs_aats.add_tags_use_specific_integration), + custom_model: curr_prompt_add_tags.model ? curr_prompt_add_tags.model : '', + do_debug: prefs_aats.do_debug + }); await cmd_addTags.initWorker(); let tags_current_email = []; try { @@ -1052,7 +1114,12 @@ async function processEmails(messages, addTagsAuto, spamFilter) { }); taLog.log("Special prompt: " + specialFullPrompt_spamfilter); // console.log(">>>>>>>> Special prompt for spamfilter: " + specialFullPrompt_spamfilter); - let cmd_spamfilter = new mzta_specialCommand(specialFullPrompt_spamfilter, prefs_init.connection_type, prefs_init.do_debug); + let cmd_spamfilter = new mzta_specialCommand({ + prompt: specialFullPrompt_spamfilter, + llm: getConnectionType(prefs_aats.connection_type, curr_prompt_spamfilter, prefs_aats.spamfilter_use_specific_integration), + custom_model: curr_prompt_spamfilter.model ? curr_prompt_spamfilter.model : '', + do_debug: prefs_aats.do_debug + }); await cmd_spamfilter.initWorker(); let spamfilter_result = ''; taLog.log("Sending the prompt..."); diff --git a/options/mzta-options-default.js b/options/mzta-options-default.js index 94d7ebec..6efc2cfa 100644 --- a/options/mzta-options-default.js +++ b/options/mzta-options-default.js @@ -67,6 +67,13 @@ export const prefs_default = { add_tags_auto_uselist_list: '', add_tags_context_menu: true, add_tags_enabled_accounts: [], + add_tags_use_specific_integration: false, + add_tags_connection_type: 'chatgpt_api', + add_tags_chatgpt_model: '', + add_tags_ollama_model: '', + add_tags_openai_comp_model: '', + add_tags_google_gemini_model: '', + add_tags_anthropic_model: '', get_calendar_event: true, get_task: true, calendar_enforce_timezone: false, @@ -75,4 +82,11 @@ export const prefs_default = { spamfilter_threshold: 70, spamfilter_context_menu: true, spamfilter_enabled_accounts: [], -} \ No newline at end of file + spamfilter_use_specific_integration: false, + spamfilter_connection_type: 'chatgpt_api', + spamfilter_chatgpt_model: '', + spamfilter_ollama_model: '', + spamfilter_openai_comp_model: '', + spamfilter_google_gemini_model: '', + spamfilter_anthropic_model: '', +} diff --git a/options/mzta-options.css b/options/mzta-options.css index 280c5bcf..f0c21d66 100644 --- a/options/mzta-options.css +++ b/options/mzta-options.css @@ -82,10 +82,6 @@ span.dims_label{ display: inline-block; } -span.opt_title{ - font-weight: bold; -} - #btn_custom_prompts{ text-align: center; width: 100%; @@ -104,38 +100,9 @@ div#miczTranslate{ font-size:smaller; } -tr.conntype_chatgpt_api, tr.conntype_chatgpt_api2{ - background-color: rgb(185, 220, 252); -} - -tr.conntype_chatgpt_web, tr.conntype_chatgpt_web2{ - background-color: rgb(255, 209, 183); -} - -tr.conntype_ollama_api, tr.conntype_ollama_api2{ - background-color: rgb(177, 238, 169); -} - -tr.conntype_openai_comp_api, tr.conntype_openai_comp_api2{ - background-color: rgb(213, 169, 238); -} - -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{ +.warn_API_needed{ display: none; - font-style: italic; -} - -#google_gemini_model_fetch_loading{ - display: none; - font-style: italic; + font-weight: bold; } #no_sparks td{ @@ -144,53 +111,6 @@ tr.conntype_anthropic_api, tr.conntype_anthropic_api2{ background: #bbf7f1; } -textarea.option-textarea{ - width: -moz-available; - height: 10em; -} - -.api_key-container { - position: relative; - width: 100%; -} - -.api_key-container input { - width: -moz-available; - margin-right: 25px; -} - -input[type="text"], input[type="password"]{ - border-radius: 4px; - padding: 2px; -} - -input.option-input[type="text"]{ - width: -moz-available; -} - -.api_key-container .toggle-icon { - position: absolute; - right: 0px; - top: 50%; - transform: translateY(-50%); - cursor: pointer; -} - -#ollama_model_fetch_loading{ - display: none; - font-style: italic; -} - -#openai_comp_model_fetch_loading{ - display: none; - font-style: italic; -} - -#anthropic_model_fetch_loading{ - display: none; - font-style: italic; -} - #owl_warning{ display: none; } @@ -235,10 +155,6 @@ input.option-input[type="text"]{ color: #FF6600; } -.conntype_chatgpt_web_option{ - cursor: pointer; -} - .btn_small{ font-size: 0.8em; } diff --git a/options/mzta-options.html b/options/mzta-options.html index f8c12e4b..0e24f172 100644 --- a/options/mzta-options.html +++ b/options/mzta-options.html @@ -3,6 +3,7 @@ + @@ -113,375 +114,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - __MSG_OpenChatGPTTab_Info__ -

    -

    __MSG_OpenChatGPTTab_Info2__ - - - - - -
    - - -
    - - - - - - - - __MSG_Loading__
    - - - - - - - - - - - - - - - - - - - - - - -
    - - -
    - - - - - - - - __MSG_Loading__
    - - - - - - - - - - - - - - - - - - - - __MSG_remember_CORS__ [__MSG_more_info_string__] -

    __MSG_CORS_alternative_1__ -
    __MSG_CORS_alternative_2__ -

    - - - - - - - - __MSG_Loading__
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - __MSG_maybe_CORS_openai_comp__ [__MSG_more_info_string__] -

    __MSG_CORS_alternative_1__ -
    __MSG_CORS_alternative_2__ -

    - - - - - - - - - - - -
    - - -
    - - - - - - - - __MSG_Loading__
    - - - - - - - - - - - - -
    - - -
    - - - - - - - - __MSG_Loading__
    - - - - - - - - - - - - - __MSG_prefs_OptionText_anthropic_max_tokens__ - - - - + __MSG_prefs_OptionText_max_prompt_length__ @@ -499,6 +132,7 @@ @@ -509,6 +143,7 @@ diff --git a/options/mzta-options.js b/options/mzta-options.js index 21e42863..aeb16f64 100644 --- a/options/mzta-options.js +++ b/options/mzta-options.js @@ -18,17 +18,24 @@ import { prefs_default } from './mzta-options-default.js'; import { taLogger } from '../js/mzta-logger.js'; -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'; -import { openAICompConfigs } from '../js/api/openai_comp_configs.js'; +import { + ChatGPTWeb_models, + checkSparksPresence, + isThunderbird128OrGreater, + openTab, + getChatGPTWebModelsList_HTML, + isAPIKeyValue, + getConnectionType, +} from '../js/mzta-utils.js'; +import { + injectConnectionUI, + varConnectionUI, + showConnectionOptions, + updateWarnings +} from '../pages/_lib/connection-ui.js'; let taLog = new taLogger("mzta-options",true); let _isThunderbird128OrGreater = true; -let permission_all_urls = false; function saveOptions(e) { e.preventDefault(); @@ -70,7 +77,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" || element.id=="anthropic_api_key" ? "****************" : result[element.id])); + taLog.log("Options restoring " + element.id + " = " + (isAPIKeyValue(element.id) ? "****************" : result[element.id])); switch (element.type) { case 'checkbox': element.checked = result[element.id] || false; @@ -109,211 +116,6 @@ async function restoreOptions() { setCurrentChoice(getting); } -function showConnectionOptions() { - disable_MaxPromptLength(); - disable_AddTags(); - disable_SpamFilter(); - disable_GetCalendarEvent(); - let chatgpt_web_display = 'table-row'; - let chatgpt_api_display = 'none'; - 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")); - parent.classList.toggle("conntype_chatgpt_api", (conntype_select.value === "chatgpt_api")); - 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{ - chatgpt_web_display = 'none'; - } - if (conntype_select.value === "chatgpt_api") { - chatgpt_api_display = 'table-row'; - }else{ - chatgpt_api_display = 'none'; - } - if (conntype_select.value === "ollama_api") { - ollama_api_display = 'table-row'; - }else{ - ollama_api_display = 'none'; - } - if (conntype_select.value === "openai_comp_api") { - openai_comp_api_display = 'table-row'; - }else{ - openai_comp_api_display = 'none'; - } - if (conntype_select.value === "google_gemini_api") { - google_gemini_api_display = 'table-row'; - }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; - }); - document.querySelectorAll(".conntype_chatgpt_api").forEach(element => { - element.style.display = chatgpt_api_display; - }); - document.querySelectorAll(".conntype_ollama_api").forEach(element => { - element.style.display = ollama_api_display; - }); - document.querySelectorAll(".conntype_openai_comp_api").forEach(element => { - element.style.display = openai_comp_api_display; - }); - 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'; - } -} - -function warn_ChatGPT_APIKeyEmpty() { - let apiKeyInput = document.getElementById('chatgpt_api_key'); - let btnFetchChatGPTModels = document.getElementById('btnUpdateChatGPTModels'); - let modelChatGPT = document.getElementById('chatgpt_model'); - if(apiKeyInput.value === ''){ - apiKeyInput.style.border = '2px solid red'; - btnFetchChatGPTModels.disabled = true; - modelChatGPT.disabled = true; - modelChatGPT.selectedIndex = -1; - modelChatGPT.style.border = ''; - }else{ - apiKeyInput.style.border = ''; - btnFetchChatGPTModels.disabled = false; - modelChatGPT.disabled = false; - if((modelChatGPT.selectedIndex === -1)||(modelChatGPT.value === '')){ - modelChatGPT.style.border = '2px solid red'; - }else{ - modelChatGPT.style.border = ''; - } - } -} - -function warn_GoogleGemini_APIKeyEmpty() { - let apiKeyInput = document.getElementById('google_gemini_api_key'); - let btnFetchGoogleGeminiModels = document.getElementById('btnUpdateGoogleGeminiModels'); - let modelGoogleGemini = document.getElementById('google_gemini_model'); - if(apiKeyInput.value === ''){ - apiKeyInput.style.border = '2px solid red'; - btnFetchGoogleGeminiModels.disabled = true; - modelGoogleGemini.disabled = true; - modelGoogleGemini.selectedIndex = -1; - modelGoogleGemini.style.border = ''; - }else{ - apiKeyInput.style.border = ''; - btnFetchGoogleGeminiModels.disabled = false; - modelGoogleGemini.disabled = false; - if((modelGoogleGemini.selectedIndex === -1)||(modelGoogleGemini.value === '')){ - modelGoogleGemini.style.border = '2px solid red'; - }else{ - modelGoogleGemini.style.border = ''; - } - } -} - -function warn_Ollama_HostEmpty() { - let hostInput = document.getElementById('ollama_host'); - let btnFetchOllamaModels = document.getElementById('btnUpdateOllamaModels'); - let modelOllama = document.getElementById('ollama_model'); - if(hostInput.value === ''){ - hostInput.style.border = '2px solid red'; - btnFetchOllamaModels.disabled = true; - modelOllama.disabled = true; - modelOllama.selectedIndex = -1; - modelOllama.style.border = ''; - }else{ - hostInput.style.border = ''; - btnFetchOllamaModels.disabled = false; - modelOllama.disabled = false; - if((modelOllama.selectedIndex === -1)||(modelOllama.value === '')){ - modelOllama.style.border = '2px solid red'; - }else{ - modelOllama.style.border = ''; - } - } -} - -function warn_OpenAIComp_HostEmpty() { - let hostInput = document.getElementById('openai_comp_host'); - let btnUpdateOpenAICompModels = document.getElementById('btnUpdateOpenAICompModels'); - let modelOpenAIComp = document.getElementById('openai_comp_model'); - if(hostInput.value === ''){ - hostInput.style.border = '2px solid red'; - btnUpdateOpenAICompModels.disabled = true; - modelOpenAIComp.disabled = true; - modelOpenAIComp.selectedIndex = -1; - modelOpenAIComp.style.border = ''; - }else{ - hostInput.style.border = ''; - btnUpdateOpenAICompModels.disabled = false; - modelOpenAIComp.disabled = false; - if((modelOpenAIComp.selectedIndex === -1)||(modelOpenAIComp.value === '')){ - modelOpenAIComp.style.border = '2px solid red'; - }else{ - modelOpenAIComp.style.border = ''; - } - } -} - -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"); @@ -322,39 +124,36 @@ function disable_MaxPromptLength(){ maxPromptLength_tr.style.display = (maxPromptLength.disabled) ? 'none' : 'table-row'; } -function disable_AddTags(){ +function disable_AddTags(prefs_opt){ let add_tags = document.getElementById('add_tags'); let conntype_select = document.getElementById("connection_type"); - add_tags.disabled = (conntype_select.value === "chatgpt_web"); - add_tags.checked = add_tags.disabled ? false : add_tags.checked; + let add_tags_disabled = (getConnectionType(conntype_select.value, {api: prefs_opt.add_tags_use_specific_integration ? prefs_opt.add_tags_connection_type : ''}) === "chatgpt_web"); + console.log('>>>>>>>>>>>>> add_tags_disabled: ' + add_tags_disabled); + add_tags.checked = add_tags_disabled ? false : add_tags.checked; let add_tags_checked_original = add_tags.checked; if(!add_tags.checked){ let add_tags_info_btn = document.getElementById('btnManageTagsInfo'); add_tags_info_btn.disabled = 'disabled'; } - let add_tags_tr_elements = document.querySelectorAll('.add_tags_tr'); - add_tags_tr_elements.forEach(add_tags_tr => { - add_tags_tr.style.display = (add_tags.disabled) ? 'none' : 'table-row'; - }); + let add_tags_warn_API_needed = document.getElementById('add_tags_warn_API_needed'); + add_tags_warn_API_needed.style.display = (add_tags_disabled) ? 'inline-block' : 'none'; if(add_tags_checked_original != add_tags.checked){ browser.storage.sync.set({add_tags: add_tags.checked}); } } -function disable_SpamFilter(){ +function disable_SpamFilter(prefs_opt){ let spamfilter = document.getElementById('spamfilter'); let conntype_select = document.getElementById("connection_type"); - spamfilter.disabled = (conntype_select.value === "chatgpt_web"); + let spamfilter_disabled = (getConnectionType(conntype_select.value, {api: prefs_opt.spamfilter_use_specific_integration ? prefs_opt.spamfilter_connection_type : ''}) === "chatgpt_web");; let spamfilter_checked_original = spamfilter.checked; - spamfilter.checked = spamfilter.disabled ? false : spamfilter.checked; + spamfilter.checked = spamfilter_disabled ? false : spamfilter.checked; if(!spamfilter.checked){ let spamfilter_info_btn = document.getElementById('btnManageSpamFilterInfo'); spamfilter_info_btn.disabled = 'disabled'; } - let spamfilter_tr_elements = document.querySelectorAll('.spamfilter_tr'); - spamfilter_tr_elements.forEach(spamfilter_tr => { - spamfilter_tr.style.display = (spamfilter.disabled) ? 'none' : 'table-row'; - }); + let spamfilter_warn_API_needed = document.getElementById('spamfilter_warn_API_needed'); + spamfilter_warn_API_needed.style.display = (spamfilter_disabled) ? 'inline-block' : 'none'; if(spamfilter_checked_original != spamfilter.checked){ browser.storage.sync.set({spamfilter: spamfilter.checked}); } @@ -383,21 +182,6 @@ async function disable_GetCalendarEvent(){ wrong_sparks_text.style.display = (is_spark_present == 0) ? 'inline' : 'none'; } -function loadOpenAICompConfigs(){ - let select_openai_comp_model = document.getElementById('openai_comp_services_shortcut'); - openAICompConfigs.forEach(config => { - const option = document.createElement('option'); - option.value = config.id; - option.text = config.name; - select_openai_comp_model.appendChild(option); - }); -} - -function resetOpenAICompConfigs(){ - let select_openai_comp_model = document.getElementById('openai_comp_services_shortcut'); - select_openai_comp_model.value = 'custom'; -} - function resetMaxPromptLength(){ let maxPromptLength = document.getElementById('max_prompt_length'); maxPromptLength.value = prefs_default.max_prompt_length; @@ -405,9 +189,15 @@ function resetMaxPromptLength(){ } document.addEventListener('DOMContentLoaded', async () => { + await injectConnectionUI({ + afterTrId: 'connection_ui_anchor', + selectId: 'connection_type', + taLog: taLog + }); + await restoreOptions(); - permission_all_urls = await messenger.permissions.contains({ origins: [""] }) + varConnectionUI.permission_all_urls = await messenger.permissions.contains({ origins: [""] }) _isThunderbird128OrGreater = await isThunderbird128OrGreater(); @@ -502,39 +292,6 @@ document.addEventListener('DOMContentLoaded', async () => { openTab('/pages/get-task/mzta-get-task.html'); }); - document.getElementById('btnOpenAICompForceModel').addEventListener('click', () => { - let modelName = prompt(browser.i18n.getMessage('OpenAIComp_force_model_ask')).trim(); - if ((modelName !== null) && (modelName !== undefined) && (modelName !== '')) { - let select_openai_comp_model = document.getElementById('openai_comp_model'); - let option = document.createElement('option'); - option.value = modelName; - option.text = modelName; - select_openai_comp_model.appendChild(option); - select_openai_comp_model.value = modelName; - select_openai_comp_model.dispatchEvent(new Event('change', { bubbles: true })); - } - }); - - document.getElementById('btnOpenAICompClearModelsList').addEventListener('click', () => { - if (!confirm(browser.i18n.getMessage('OpenAIComp_ClearModelsList_Confirm'))) { - return; - } - let select_openai_comp_model = document.getElementById('openai_comp_model'); - while (select_openai_comp_model.options.length > 0) { - select_openai_comp_model.remove(0); - } - select_openai_comp_model.value = ''; - select_openai_comp_model.dispatchEvent(new Event('change', { bubbles: true })); - }); - - document.getElementById('btnGiveAllUrlsPermission_ollama_api').addEventListener('click', async () => { - permission_all_urls = await messenger.permissions.request({ origins: [""] }); - }); - - document.getElementById('btnGiveAllUrlsPermission_openai_comp_api').addEventListener('click', async () => { - permission_all_urls = await messenger.permissions.request({ origins: [""] }); - }); - getChatGPTWebModelsList_HTML(ChatGPTWeb_models, 'chatgpt_web_models_list'); document.querySelectorAll(".conntype_chatgpt_web_option").forEach(element => { element.addEventListener("click", () => { @@ -544,348 +301,26 @@ document.addEventListener('DOMContentLoaded', async () => { }); }); + let prefs_opt = await browser.storage.sync.get({ + add_tags_use_specific_integration: prefs_default.add_tags_use_specific_integration, + add_tags_connection_type: prefs_default.add_tags_connection_type, + spamfilter_use_specific_integration: prefs_default.spamfilter_use_specific_integration, + spamfilter_connection_type: prefs_default.spamfilter_connection_type, + }); + let conntype_select = document.getElementById("connection_type"); - conntype_select.addEventListener("change", showConnectionOptions); - conntype_select.addEventListener("change", warn_ChatGPT_APIKeyEmpty); - 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_MaxPromptLength); + conntype_select.addEventListener("change", () => disable_AddTags(prefs_opt)); + conntype_select.addEventListener("change", () => disable_SpamFilter(prefs_opt)); conntype_select.addEventListener("change", disable_GetCalendarEvent); - document.getElementById("chatgpt_api_key").addEventListener("change", warn_ChatGPT_APIKeyEmpty); - document.getElementById("ollama_host").addEventListener("change", warn_Ollama_HostEmpty); - document.getElementById("openai_comp_host").addEventListener("change", warn_OpenAIComp_HostEmpty); - 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); - document.getElementById("openai_comp_host").addEventListener("input", resetOpenAICompConfigs); - document.getElementById("openai_comp_chat_name").addEventListener("input", resetOpenAICompConfigs); - document.getElementById("openai_comp_use_v1").addEventListener("input", resetOpenAICompConfigs); - - 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'); - const chatgpt_option = document.createElement('option'); - chatgpt_option.value = prefs.chatgpt_model; - chatgpt_option.text = prefs.chatgpt_model; - select_chatgpt_model.appendChild(chatgpt_option); - select_chatgpt_model.addEventListener("change", warn_ChatGPT_APIKeyEmpty); - - document.getElementById('btnUpdateChatGPTModels').addEventListener('click', async () => { - document.getElementById('chatgpt_model_fetch_loading').style.display = 'inline'; - let openai = new OpenAI({ - apiKey: document.getElementById("chatgpt_api_key").value, - stream: true - }); - openai.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('chatgpt_model_fetch_loading').style.display = 'none'; - console.error("[ThunderAI] " + browser.i18n.getMessage("ChatGPT_Models_Error_fetching")); - alert(browser.i18n.getMessage("ChatGPT_Models_Error_fetching")+": " + errorDetail); - return; - } - taLog.log("ChatGPT models: " + JSON.stringify(data)); - data.response.forEach(model => { - if (!Array.from(select_chatgpt_model.options).some(option => option.value === model.id)) { - const option = document.createElement('option'); - option.value = model.id; - option.text = model.id; - select_chatgpt_model.appendChild(option); - } - }); - document.getElementById('chatgpt_model_fetch_loading').style.display = 'none'; - }); - - warn_ChatGPT_APIKeyEmpty(); - }); - - // 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; - google_gemini_option.text = prefs.google_gemini_model; - select_google_gemini_model.appendChild(google_gemini_option); - select_google_gemini_model.addEventListener("change", warn_GoogleGemini_APIKeyEmpty); - - document.getElementById('btnUpdateGoogleGeminiModels').addEventListener('click', async () => { - document.getElementById('google_gemini_model_fetch_loading').style.display = 'inline'; - let google_gemini = new GoogleGemini({ - apiKey: document.getElementById("google_gemini_api_key").value, - stream: true - }); - google_gemini.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('google_gemini_model_fetch_loading').style.display = 'none'; - console.error("[ThunderAI] " + browser.i18n.getMessage("GoogleGemini_Models_Error_fetching")); - alert(browser.i18n.getMessage("GoogleGemini_Models_Error_fetching")+": " + errorDetail); - return; - } - taLog.log("GoogleGemini models: " + JSON.stringify(data)); - data.response.forEach(model => { - if (!Array.from(select_google_gemini_model.options).some(option => option.value === model.name.substring(model.name.lastIndexOf("/") + 1))) { - const option = document.createElement('option'); - option.value = model.name.substring(model.name.lastIndexOf("/") + 1); - option.text = model.displayName; - select_google_gemini_model.appendChild(option); - } - }); - document.getElementById('google_gemini_model_fetch_loading').style.display = 'none'; - }); - - warn_GoogleGemini_APIKeyEmpty(); - }); - - // Ollama API Model fetching - let select_ollama_model = document.getElementById('ollama_model'); - const ollama_option = document.createElement('option'); - ollama_option.value = prefs.ollama_model; - ollama_option.text = prefs.ollama_model; - select_ollama_model.appendChild(ollama_option); - select_ollama_model.addEventListener("change", warn_Ollama_HostEmpty); - - document.getElementById('btnUpdateOllamaModels').addEventListener('click', async () => { - document.getElementById('ollama_model_fetch_loading').style.display = 'inline'; - let ollama = new Ollama({ - host: document.getElementById("ollama_host").value, - stream: true - }); - try { - let data = await ollama.fetchModels(); - if(!data){ - document.getElementById('ollama_model_fetch_loading').style.display = 'none'; - console.error("[ThunderAI] " + browser.i18n.getMessage("Ollama_Models_Error_fetching")); - alert(browser.i18n.getMessage("Ollama_Models_Error_fetching")); - return; - } - if(!data.ok){ - let errorDetail; - try { - errorDetail = JSON.parse(data.error); - errorDetail = errorDetail.error.message; - } catch (e) { - errorDetail = data.error; - } - document.getElementById('ollama_model_fetch_loading').style.display = 'none'; - console.error("[ThunderAI] " + browser.i18n.getMessage("Ollama_Models_Error_fetching")); - alert(browser.i18n.getMessage("Ollama_Models_Error_fetching")+": " + errorDetail); - return; - } - if(data.response.models.length == 0){ - document.getElementById('ollama_model_fetch_loading').style.display = 'none'; - console.error("[ThunderAI] " + browser.i18n.getMessage("Ollama_Models_Error_fetching")); - alert(browser.i18n.getMessage("Ollama_Models_Error_fetching")+": " + browser.i18n.getMessage("API_Models_Error_NoModels")); - return; - } - taLog.log("Ollama models: " + JSON.stringify(data)); - data.response.models.forEach(model => { - if (!Array.from(select_ollama_model.options).some(option => option.value === model.model)) { - const option = document.createElement('option'); - option.value = model.model; - option.text = model.name + " (" + model.model + ")"; - select_ollama_model.appendChild(option); - } - }); - document.getElementById('ollama_model_fetch_loading').style.display = 'none'; - } catch (error) { - document.getElementById('ollama_model_fetch_loading').style.display = 'none'; - taLog.error(browser.i18n.getMessage("Ollama_Models_Error_fetching")); - alert(browser.i18n.getMessage("Ollama_Models_Error_fetching")+": " + error.message); - } - - warn_Ollama_HostEmpty(); - }); - - - // OpenAI Comp API Model fetching - let select_openai_comp_model = document.getElementById('openai_comp_model'); - const openai_comp_option = document.createElement('option'); - openai_comp_option.value = prefs.openai_comp_model; - openai_comp_option.text = prefs.openai_comp_model; - select_openai_comp_model.appendChild(openai_comp_option); - select_openai_comp_model.addEventListener("change", warn_OpenAIComp_HostEmpty); - - document.getElementById('btnUpdateOpenAICompModels').addEventListener('click', async () => { - document.getElementById('openai_comp_model_fetch_loading').style.display = 'inline'; - let openai_comp = new OpenAIComp({ - host: document.getElementById("openai_comp_host").value, - model: null, - apiKey: document.getElementById("openai_comp_api_key").value, - stream: true, - use_v1: document.getElementById("openai_comp_use_v1").checked - }); - openai_comp.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('openai_comp_model_fetch_loading').style.display = 'none'; - console.error("[ThunderAI] " + browser.i18n.getMessage("OpenAIComp_Models_Error_fetching")); - alert(browser.i18n.getMessage("OpenAIComp_Models_Error_fetching")+": " + errorDetail); - return; - } - taLog.log("OpenAIComp models: " + JSON.stringify(data)); - data.response.forEach(model => { - if (!Array.from(select_openai_comp_model.options).some(option => option.value === model.id)) { - const option = document.createElement('option'); - option.value = model.id; - option.text = model.id; - select_openai_comp_model.appendChild(option); - } - }); - document.getElementById('openai_comp_model_fetch_loading').style.display = 'none'; - }); - - 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({ - apiKey: document.getElementById("anthropic_api_key").value, - version: document.getElementById("anthropic_version").value - }); - 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 => { - const existingOption = Array.from(select_anthropic_model.options).find(option => option.value === model.id); - if (existingOption) { - existingOption.text = model.display_name + " (" + model.id + ")"; - } else { - 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(); + showConnectionOptions(conntype_select); disable_MaxPromptLength(); - disable_AddTags(); - disable_SpamFilter(); + disable_AddTags(prefs_opt); + disable_SpamFilter(prefs_opt); disable_GetCalendarEvent(); - const passwordField_chatgpt_api_key = document.getElementById('chatgpt_api_key'); - const toggleIcon_chatgpt_api_key = document.getElementById('toggle_chatgpt_api_key'); - const icon_img_chatgpt_api_key = document.getElementById('pwd-icon_chatgpt_api_key'); - - toggleIcon_chatgpt_api_key.addEventListener('click', () => { - const type = passwordField_chatgpt_api_key.getAttribute('type') === 'password' ? 'text' : 'password'; - passwordField_chatgpt_api_key.setAttribute('type', type); - - icon_img_chatgpt_api_key.src = type === 'password' ? "../images/pwd-show.png" : "../images/pwd-hide.png"; - }); - - const passwordField_google_gemini_api_key = document.getElementById('google_gemini_api_key'); - const toggleIcon_google_gemini_api_key = document.getElementById('toggle_google_gemini_api_key'); - const icon_img_google_gemini_api_key = document.getElementById('pwd-icon_google_gemini_api_key'); - - toggleIcon_google_gemini_api_key.addEventListener('click', () => { - const type = passwordField_google_gemini_api_key.getAttribute('type') === 'password' ? 'text' : 'password'; - passwordField_google_gemini_api_key.setAttribute('type', type); - - icon_img_google_gemini_api_key.src = type === 'password' ? "../images/pwd-show.png" : "../images/pwd-hide.png"; - }); - - const passwordField_openai_comp_api_key = document.getElementById('openai_comp_api_key'); - const toggleIcon_openai_comp_api_key = document.getElementById('toggle_openai_comp_api_key'); - const icon_img_openai_comp_api_key = document.getElementById('pwd-icon_openai_comp_api_key'); - - toggleIcon_openai_comp_api_key.addEventListener('click', () => { - const type = passwordField_openai_comp_api_key.getAttribute('type') === 'password' ? 'text' : 'password'; - passwordField_openai_comp_api_key.setAttribute('type', type); - - 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}); - - let base_url = 'https://chatgpt.com'; - let model_opt = ''; - let webproject_set = false; - - if((prefs_mod.chatgpt_web_model != '') && (prefs_mod.chatgpt_web_model != undefined)){ - model_opt = '?model=' + sanitizeChatGPTModelData(prefs_mod.chatgpt_web_model); - } - if((prefs_mod.chatgpt_web_project != '') && (prefs_mod.chatgpt_web_project != undefined)){ - base_url += sanitizeChatGPTWebCustomData(prefs_mod.chatgpt_web_project); - webproject_set = true; - } - if(!webproject_set && (prefs_mod.chatgpt_web_custom_gpt != '') && (prefs_mod.chatgpt_web_custom_gpt != undefined)){ - base_url += sanitizeChatGPTWebCustomData(prefs_mod.chatgpt_web_custom_gpt); - } - browser.tabs.create({ url: base_url + model_opt }); - }); + document.getElementById('reset_max_prompt_length').addEventListener('click', resetMaxPromptLength); browser.runtime.getPlatformInfo().then(info => { taLog.log("OS: " + info.os); @@ -894,32 +329,6 @@ document.addEventListener('DOMContentLoaded', async () => { } }); - loadOpenAICompConfigs(); - let select_openai_comp_services_shortcut = document.getElementById('openai_comp_services_shortcut'); - select_openai_comp_services_shortcut.addEventListener("change", () => { - let selectedOption = select_openai_comp_services_shortcut.options[select_openai_comp_services_shortcut.selectedIndex]; - const config = openAICompConfigs.find(cfg => cfg.id === selectedOption.value); - if (config) { - if (!confirm(browser.i18n.getMessage('OpenAIComp_Configs_ConfirmApply', config.name))) { - return; - } - document.getElementById('openai_comp_host').value = config.host || ''; - // Clear all options from the select except the first (placeholder) one - const openaiCompModelSelect = document.getElementById('openai_comp_model'); - openaiCompModelSelect.value = ''; - while (openaiCompModelSelect.options.length > 0) { - openaiCompModelSelect.remove(0); - } - document.getElementById('openai_comp_use_v1').checked = !!config.use_v1; - document.getElementById('openai_comp_chat_name').value = config.chat_name || ''; - // Trigger change events if needed - document.getElementById('openai_comp_host').dispatchEvent(new Event('change', { bubbles: true })); - document.getElementById('openai_comp_model').dispatchEvent(new Event('change', { bubbles: true })); - document.getElementById('openai_comp_use_v1').dispatchEvent(new Event('change', { bubbles: true })); - document.getElementById('openai_comp_chat_name').dispatchEvent(new Event('change', { bubbles: true })); - } - }); - - document.getElementById('reset_max_prompt_length').addEventListener('click', resetMaxPromptLength); + updateWarnings(); }, { once: true }); diff --git a/options/mzta-release-notes.html b/options/mzta-release-notes.html index f883c3ff..151b870b 100644 --- a/options/mzta-release-notes.html +++ b/options/mzta-release-notes.html @@ -13,6 +13,7 @@
  • [ChatGPT Web] Added a message to explain to click on "Force completion" if the ChatGPT job is not done after 7 seconds [#419].
  • [All APIs] The prompt id and name are now shown in the information header in the AI API chat [#436].
  • [Google Gemini API]Support for the thinkingBudget parameter has been added [#494].
  • +
  • Various code improvements and minor bugs fixed.
  • ...
  • Version 3.6.1 - 23/08/2025

    diff --git a/pages/_lib/connection-ui.css b/pages/_lib/connection-ui.css new file mode 100644 index 00000000..5b63569d --- /dev/null +++ b/pages/_lib/connection-ui.css @@ -0,0 +1,118 @@ +tr.conntype_chatgpt_api, tr.conntype_chatgpt_api2{ + background-color: rgb(185, 220, 252); +} + +tr.conntype_chatgpt_web, tr.conntype_chatgpt_web2{ + background-color: rgb(255, 209, 183); +} + +tr.conntype_ollama_api, tr.conntype_ollama_api2{ + background-color: rgb(177, 238, 169); +} + +tr.conntype_openai_comp_api, tr.conntype_openai_comp_api2{ + background-color: rgb(213, 169, 238); +} + +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; +} + +#google_gemini_model_fetch_loading{ + display: none; + font-style: italic; +} + +textarea.option-textarea{ + width: -moz-available; + height: 10em; +} + +.api_key-container { + position: relative; + width: 100%; +} + +.api_key-container input { + width: -moz-available; + margin-right: 25px; +} + +input[type="text"], input[type="password"]{ + border-radius: 4px; + padding: 2px; +} + +input.option-input[type="text"]{ + width: -moz-available; +} + +.api_key-container .toggle-icon { + position: absolute; + right: 0px; + top: 50%; + transform: translateY(-50%); + cursor: pointer; +} + +#ollama_model_fetch_loading{ + display: none; + font-style: italic; +} + +#openai_comp_model_fetch_loading{ + display: none; + font-style: italic; +} + +#anthropic_model_fetch_loading{ + display: none; + font-style: italic; +} + +.conntype_chatgpt_web_option{ + cursor: pointer; +} + +span.opt_title{ + font-weight: bold; +} + +@media (prefers-color-scheme: dark) { + tr.conntype_chatgpt_api, tr.conntype_chatgpt_api2{ + background-color: rgb(0, 0, 58); + } + + tr.conntype_chatgpt_web,tr.conntype_chatgpt_web2{ + background-color: rgb(39, 11, 0); + } + + tr.conntype_ollama_api, tr.conntype_ollama_api2{ + background-color: rgb(7, 58, 0); + } + + tr.conntype_openai_comp_api, tr.conntype_openai_comp_api2{ + background-color: rgb(89, 20, 129); + } + + tr.conntype_google_gemini_api, tr.conntype_google_gemini_api2{ + 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); + } +} \ No newline at end of file diff --git a/pages/_lib/connection-ui.js b/pages/_lib/connection-ui.js new file mode 100644 index 00000000..eaee61bf --- /dev/null +++ b/pages/_lib/connection-ui.js @@ -0,0 +1,1103 @@ +/* + * ThunderAI [https://micz.it/thunderbird-addon-thunderai/] + * Copyright (C) 2024 - 2025 Mic (m@micz.it) + + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +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 { + validateCustomData_ChatGPTWeb, + sanitizeChatGPTModelData, + sanitizeChatGPTWebCustomData +} from '../../js/mzta-utils.js'; +import { openAICompConfigs } from '../../js/api/openai_comp_configs.js'; + +export const varConnectionUI = { + permission_all_urls: false +} + +export async function injectConnectionUI({ + afterTrId = '', + selectId = '', + modelId_prefix = '', + no_chatgpt_web = false, + defaultType = '', + tr_class = '', + taLog = console + } = {}) { + + const anchorTr = document.getElementById(afterTrId); + if (!anchorTr) { + console.error(`[ThuderAI | injectConnectionUI] Can't find tr#${afterTrId}`); + return null; + } + + let tpl = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + __MSG_OpenChatGPTTab_Info__ +

    +

    __MSG_OpenChatGPTTab_Info2__ + + + + + +
    + + +
    + + + + + + + + __MSG_Loading__
    + + + + + + + + + + + + + + + + + + + + + + +
    + + +
    + + + + + + + + __MSG_Loading__
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + __MSG_remember_CORS__ [__MSG_more_info_string__] +

    __MSG_CORS_alternative_1__ +
    __MSG_CORS_alternative_2__ +

    + + + + + + + + __MSG_Loading__
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + __MSG_maybe_CORS_openai_comp__ [__MSG_more_info_string__] +

    __MSG_CORS_alternative_1__ +
    __MSG_CORS_alternative_2__ +

    + + + + + + + + + + + +
    + + +
    + + + + + + + + __MSG_Loading__
    + + + + + + + + + + + + +
    + + +
    + + + + + + + + __MSG_Loading__
    + + + + + + + + + + + + + __MSG_prefs_OptionText_anthropic_max_tokens__ + + + + + `; + + const template = document.createElement('template'); + template.innerHTML = tpl.trim(); + const frag = template.content; + + const parent = anchorTr.parentElement; + const nodes = Array.from(frag.childNodes); + let last = anchorTr; + nodes.forEach(node => { + if (node.nodeType === Node.ELEMENT_NODE) { + parent.insertBefore(node, last.nextSibling); + last = node; + } + }); + + // Bindings + // const bindClick = (id, cb) => { const el = document.getElementById(id); if (el && typeof cb === 'function') el.addEventListener('click', cb); }; + // const bindChange = (id, cb) => { const el = document.getElementById(id); if (el && typeof cb === 'function') el.addEventListener('change', cb); }; + // const bindInput = (id, cb) => { const el = document.getElementById(id); if (el && typeof cb === 'function') el.addEventListener('input', cb); }; + + populateConnectionTypeOptions(selectId, no_chatgpt_web); + + let conntype_select = document.getElementById(selectId); + + if (!conntype_select) { + console.error('[ThuderAI | injectConnectionUI] Select not found after insertion.'); + } + + conntype_select.addEventListener("change", () => showConnectionOptions(conntype_select)); + conntype_select.addEventListener("change", (ev) => warn_ChatGPT_APIKeyEmpty(modelId_prefix)); + conntype_select.addEventListener("change", (ev) => warn_Ollama_HostEmpty(modelId_prefix)); + conntype_select.addEventListener("change", (ev) => warn_OpenAIComp_HostEmpty(modelId_prefix)); + conntype_select.addEventListener("change", (ev) => warn_GoogleGemini_APIKeyEmpty(modelId_prefix)); + conntype_select.addEventListener("change", (ev) => warn_Anthropic_APIKeyEmpty(modelId_prefix)); + conntype_select.addEventListener("change", (ev) => warn_Anthropic_VersionEmpty(modelId_prefix)); + document.getElementById("chatgpt_web_project").addEventListener("input", validateCustomData_ChatGPTWeb); + document.getElementById("chatgpt_web_custom_gpt").addEventListener("input", validateCustomData_ChatGPTWeb); + document.getElementById("chatgpt_api_key").addEventListener("change", (ev) => warn_ChatGPT_APIKeyEmpty(modelId_prefix)); + document.getElementById("ollama_host").addEventListener("change", (ev) => warn_Ollama_HostEmpty(modelId_prefix)); + document.getElementById("openai_comp_host").addEventListener("change", (ev) => warn_OpenAIComp_HostEmpty(modelId_prefix)); + document.getElementById("google_gemini_api_key").addEventListener("change", (ev) => warn_GoogleGemini_APIKeyEmpty(modelId_prefix)); + document.getElementById("anthropic_api_key").addEventListener("change", (ev) => warn_Anthropic_APIKeyEmpty(modelId_prefix)); + document.getElementById("anthropic_version").addEventListener("change", (ev) => warn_Anthropic_VersionEmpty(modelId_prefix)); + document.getElementById("openai_comp_host").addEventListener("input", resetOpenAICompConfigs); + document.getElementById("openai_comp_chat_name").addEventListener("input", resetOpenAICompConfigs); + document.getElementById("openai_comp_use_v1").addEventListener("input", resetOpenAICompConfigs); + + showConnectionOptions(conntype_select); + loadOpenAICompConfigs(); + warn_ChatGPT_APIKeyEmpty(modelId_prefix); + warn_Ollama_HostEmpty(modelId_prefix); + warn_OpenAIComp_HostEmpty(modelId_prefix); + warn_GoogleGemini_APIKeyEmpty(modelId_prefix); + warn_Anthropic_APIKeyEmpty(modelId_prefix); + warn_Anthropic_VersionEmpty(modelId_prefix); + + const passwordField_chatgpt_api_key = document.getElementById('chatgpt_api_key'); + const toggleIcon_chatgpt_api_key = document.getElementById('toggle_chatgpt_api_key'); + const icon_img_chatgpt_api_key = document.getElementById('pwd-icon_chatgpt_api_key'); + + toggleIcon_chatgpt_api_key.addEventListener('click', () => { + const type = passwordField_chatgpt_api_key.getAttribute('type') === 'password' ? 'text' : 'password'; + passwordField_chatgpt_api_key.setAttribute('type', type); + + icon_img_chatgpt_api_key.src = type === 'password' ? "/images/pwd-show.png" : "/images/pwd-hide.png"; + }); + + const passwordField_google_gemini_api_key = document.getElementById('google_gemini_api_key'); + const toggleIcon_google_gemini_api_key = document.getElementById('toggle_google_gemini_api_key'); + const icon_img_google_gemini_api_key = document.getElementById('pwd-icon_google_gemini_api_key'); + + toggleIcon_google_gemini_api_key.addEventListener('click', () => { + const type = passwordField_google_gemini_api_key.getAttribute('type') === 'password' ? 'text' : 'password'; + passwordField_google_gemini_api_key.setAttribute('type', type); + + icon_img_google_gemini_api_key.src = type === 'password' ? "/images/pwd-show.png" : "/images/pwd-hide.png"; + }); + + const passwordField_openai_comp_api_key = document.getElementById('openai_comp_api_key'); + const toggleIcon_openai_comp_api_key = document.getElementById('toggle_openai_comp_api_key'); + const icon_img_openai_comp_api_key = document.getElementById('pwd-icon_openai_comp_api_key'); + + toggleIcon_openai_comp_api_key.addEventListener('click', () => { + const type = passwordField_openai_comp_api_key.getAttribute('type') === 'password' ? 'text' : 'password'; + passwordField_openai_comp_api_key.setAttribute('type', type); + + 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}); + + let base_url = 'https://chatgpt.com'; + let model_opt = ''; + let webproject_set = false; + + if((prefs_mod.chatgpt_web_model != '') && (prefs_mod.chatgpt_web_model != undefined)){ + model_opt = '?model=' + sanitizeChatGPTModelData(prefs_mod.chatgpt_web_model); + } + if((prefs_mod.chatgpt_web_project != '') && (prefs_mod.chatgpt_web_project != undefined)){ + base_url += sanitizeChatGPTWebCustomData(prefs_mod.chatgpt_web_project); + webproject_set = true; + } + if(!webproject_set && (prefs_mod.chatgpt_web_custom_gpt != '') && (prefs_mod.chatgpt_web_custom_gpt != undefined)){ + base_url += sanitizeChatGPTWebCustomData(prefs_mod.chatgpt_web_custom_gpt); + } + browser.tabs.create({ url: base_url + model_opt }); + }); + + let select_openai_comp_services_shortcut = document.getElementById('openai_comp_services_shortcut'); + select_openai_comp_services_shortcut.addEventListener("change", () => { + let selectedOption = select_openai_comp_services_shortcut.options[select_openai_comp_services_shortcut.selectedIndex]; + const config = openAICompConfigs.find(cfg => cfg.id === selectedOption.value); + if (config) { + if (!confirm(browser.i18n.getMessage('OpenAIComp_Configs_ConfirmApply', config.name))) { + return; + } + document.getElementById('openai_comp_host').value = config.host || ''; + // Clear all options from the select except the first (placeholder) one + const openaiCompModelSelect = getModelEl('openai_comp_model', modelId_prefix); + openaiCompModelSelect.value = ''; + while (openaiCompModelSelect.options.length > 0) { + openaiCompModelSelect.remove(0); + } + document.getElementById('openai_comp_use_v1').checked = !!config.use_v1; + document.getElementById('openai_comp_chat_name').value = config.chat_name || ''; + // Trigger change events if needed + document.getElementById('openai_comp_host').dispatchEvent(new Event('change', { bubbles: true })); + getModelEl('openai_comp_model', modelId_prefix).dispatchEvent(new Event('change', { bubbles: true })); + document.getElementById('openai_comp_use_v1').dispatchEvent(new Event('change', { bubbles: true })); + document.getElementById('openai_comp_chat_name').dispatchEvent(new Event('change', { bubbles: true })); + } + }); + + 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 = getModelEl('chatgpt_model', modelId_prefix); + const chatgpt_option = document.createElement('option'); + chatgpt_option.value = prefs.chatgpt_model; + chatgpt_option.text = prefs.chatgpt_model; + select_chatgpt_model.appendChild(chatgpt_option); + select_chatgpt_model.addEventListener("change", () => warn_ChatGPT_APIKeyEmpty(modelId_prefix)); + + document.getElementById('btnUpdateChatGPTModels').addEventListener('click', async () => { + document.getElementById('chatgpt_model_fetch_loading').style.display = 'inline'; + let openai = new OpenAI(document.getElementById("chatgpt_api_key").value, '', true); + openai.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('chatgpt_model_fetch_loading').style.display = 'none'; + console.error("[ThunderAI] " + browser.i18n.getMessage("ChatGPT_Models_Error_fetching")); + alert(browser.i18n.getMessage("ChatGPT_Models_Error_fetching")+": " + errorDetail); + return; + } + taLog.log("ChatGPT models: " + JSON.stringify(data)); + data.response.forEach(model => { + if (!Array.from(select_chatgpt_model.options).some(option => option.value === model.id)) { + const option = document.createElement('option'); + option.value = model.id; + option.text = model.id; + select_chatgpt_model.appendChild(option); + } + }); + document.getElementById('chatgpt_model_fetch_loading').style.display = 'none'; + }); + + warn_ChatGPT_APIKeyEmpty(modelId_prefix); + }); + + // Google Gemini API model fetching + let select_google_gemini_model = getModelEl('google_gemini_model', modelId_prefix); + const google_gemini_option = document.createElement('option'); + google_gemini_option.value = prefs.google_gemini_model; + google_gemini_option.text = prefs.google_gemini_model; + select_google_gemini_model.appendChild(google_gemini_option); + select_google_gemini_model.addEventListener("change", () => warn_GoogleGemini_APIKeyEmpty(modelId_prefix)); + + document.getElementById('btnUpdateGoogleGeminiModels').addEventListener('click', async () => { + document.getElementById('google_gemini_model_fetch_loading').style.display = 'inline'; + let google_gemini = new GoogleGemini(document.getElementById("google_gemini_api_key").value, '', true); + google_gemini.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('google_gemini_model_fetch_loading').style.display = 'none'; + console.error("[ThunderAI] " + browser.i18n.getMessage("GoogleGemini_Models_Error_fetching")); + alert(browser.i18n.getMessage("GoogleGemini_Models_Error_fetching")+": " + errorDetail); + return; + } + taLog.log("GoogleGemini models: " + JSON.stringify(data)); + data.response.forEach(model => { + if (!Array.from(select_google_gemini_model.options).some(option => option.value === model.name.substring(model.name.lastIndexOf("/") + 1))) { + const option = document.createElement('option'); + option.value = model.name.substring(model.name.lastIndexOf("/") + 1); + option.text = model.displayName; + select_google_gemini_model.appendChild(option); + } + }); + document.getElementById('google_gemini_model_fetch_loading').style.display = 'none'; + }); + + warn_GoogleGemini_APIKeyEmpty(modelId_prefix); + }); + + // Ollama API Model fetching + let select_ollama_model = getModelEl('ollama_model', modelId_prefix); + const ollama_option = document.createElement('option'); + ollama_option.value = prefs.ollama_model; + ollama_option.text = prefs.ollama_model; + select_ollama_model.appendChild(ollama_option); + select_ollama_model.addEventListener("change", () => warn_Ollama_HostEmpty(modelId_prefix)); + + document.getElementById('btnUpdateOllamaModels').addEventListener('click', async () => { + document.getElementById('ollama_model_fetch_loading').style.display = 'inline'; + let ollama = new Ollama(document.getElementById("ollama_host").value, true); + try { + let data = await ollama.fetchModels(); + if(!data){ + document.getElementById('ollama_model_fetch_loading').style.display = 'none'; + console.error("[ThunderAI] " + browser.i18n.getMessage("Ollama_Models_Error_fetching")); + alert(browser.i18n.getMessage("Ollama_Models_Error_fetching")); + return; + } + if(!data.ok){ + let errorDetail; + try { + errorDetail = JSON.parse(data.error); + errorDetail = errorDetail.error.message; + } catch (e) { + errorDetail = data.error; + } + document.getElementById('ollama_model_fetch_loading').style.display = 'none'; + console.error("[ThunderAI] " + browser.i18n.getMessage("Ollama_Models_Error_fetching")); + alert(browser.i18n.getMessage("Ollama_Models_Error_fetching")+": " + errorDetail); + return; + } + if(data.response.models.length == 0){ + document.getElementById('ollama_model_fetch_loading').style.display = 'none'; + console.error("[ThunderAI] " + browser.i18n.getMessage("Ollama_Models_Error_fetching")); + alert(browser.i18n.getMessage("Ollama_Models_Error_fetching")+": " + browser.i18n.getMessage("API_Models_Error_NoModels")); + return; + } + taLog.log("Ollama models: " + JSON.stringify(data)); + data.response.models.forEach(model => { + if (!Array.from(select_ollama_model.options).some(option => option.value === model.model)) { + const option = document.createElement('option'); + option.value = model.model; + option.text = model.name + " (" + model.model + ")"; + select_ollama_model.appendChild(option); + } + }); + document.getElementById('ollama_model_fetch_loading').style.display = 'none'; + } catch (error) { + document.getElementById('ollama_model_fetch_loading').style.display = 'none'; + taLog.error(browser.i18n.getMessage("Ollama_Models_Error_fetching")); + alert(browser.i18n.getMessage("Ollama_Models_Error_fetching")+": " + error.message); + } + + warn_Ollama_HostEmpty(modelId_prefix); + }); + + // OpenAI Comp API Model fetching + let select_openai_comp_model = getModelEl('openai_comp_model', modelId_prefix); + const openai_comp_option = document.createElement('option'); + openai_comp_option.value = prefs.openai_comp_model; + openai_comp_option.text = prefs.openai_comp_model; + select_openai_comp_model.appendChild(openai_comp_option); + select_openai_comp_model.addEventListener("change", () => warn_OpenAIComp_HostEmpty(modelId_prefix)); + + document.getElementById('btnUpdateOpenAICompModels').addEventListener('click', async () => { + document.getElementById('openai_comp_model_fetch_loading').style.display = 'inline'; + let openai_comp = new OpenAIComp(document.getElementById("openai_comp_host").value , null, document.getElementById("openai_comp_api_key").value, true, document.getElementById("openai_comp_use_v1").checked); + openai_comp.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('openai_comp_model_fetch_loading').style.display = 'none'; + console.error("[ThunderAI] " + browser.i18n.getMessage("OpenAIComp_Models_Error_fetching")); + alert(browser.i18n.getMessage("OpenAIComp_Models_Error_fetching")+": " + errorDetail); + return; + } + taLog.log("OpenAIComp models: " + JSON.stringify(data)); + data.response.forEach(model => { + if (!Array.from(select_openai_comp_model.options).some(option => option.value === model.id)) { + const option = document.createElement('option'); + option.value = model.id; + option.text = model.id; + select_openai_comp_model.appendChild(option); + } + }); + document.getElementById('openai_comp_model_fetch_loading').style.display = 'none'; + }); + + warn_OpenAIComp_HostEmpty(modelId_prefix); + }); + + // Anthropic API model fetching + let select_anthropic_model = getModelEl('anthropic_model', modelId_prefix); + 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(modelId_prefix)); + select_anthropic_model.addEventListener("change", () => warn_Anthropic_VersionEmpty(modelId_prefix)); + + 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, ''); + 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 => { + const existingOption = Array.from(select_anthropic_model.options).find(option => option.value === model.id); + if (existingOption) { + existingOption.text = model.display_name + " (" + model.id + ")"; + } else { + 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(modelId_prefix); + }); + + document.getElementById('btnOpenAICompForceModel').addEventListener('click', () => { + let modelName = prompt(browser.i18n.getMessage('OpenAIComp_force_model_ask')).trim(); + if ((modelName !== null) && (modelName !== undefined) && (modelName !== '')) { + let select_openai_comp_model = getModelEl('openai_comp_model', modelId_prefix); + let option = document.createElement('option'); + option.value = modelName; + option.text = modelName; + select_openai_comp_model.appendChild(option); + select_openai_comp_model.value = modelName; + select_openai_comp_model.dispatchEvent(new Event('change', { bubbles: true })); + } + }); + + document.getElementById('btnOpenAICompClearModelsList').addEventListener('click', () => { + if (!confirm(browser.i18n.getMessage('OpenAIComp_ClearModelsList_Confirm'))) { + return; + } + let select_openai_comp_model = getModelEl('openai_comp_model', modelId_prefix); + while (select_openai_comp_model.options.length > 0) { + select_openai_comp_model.remove(0); + } + select_openai_comp_model.value = ''; + select_openai_comp_model.dispatchEvent(new Event('change', { bubbles: true })); + }); + + document.getElementById('btnGiveAllUrlsPermission_ollama_api').addEventListener('click', async () => { + permission_all_urls = await messenger.permissions.request({ origins: [""] }); + }); + + document.getElementById('btnGiveAllUrlsPermission_openai_comp_api').addEventListener('click', async () => { + permission_all_urls = await messenger.permissions.request({ origins: [""] }); + }); + + warn_ChatGPT_APIKeyEmpty(modelId_prefix); + warn_Ollama_HostEmpty(modelId_prefix); + warn_OpenAIComp_HostEmpty(modelId_prefix); + warn_GoogleGemini_APIKeyEmpty(modelId_prefix); + warn_Anthropic_APIKeyEmpty(modelId_prefix); + warn_Anthropic_VersionEmpty(modelId_prefix); + + return { + select: conntype_select, + onTypeChange: (fn) => { onTypeChange = fn; fn(conntype_select.value, { select: conntype_select }); } + }; +} + + +// From here there are exported functions + +export function updateWarnings(modelId_prefix = '') { + warn_ChatGPT_APIKeyEmpty(modelId_prefix); + warn_Ollama_HostEmpty(modelId_prefix); + warn_OpenAIComp_HostEmpty(modelId_prefix); + warn_GoogleGemini_APIKeyEmpty(modelId_prefix); + warn_Anthropic_APIKeyEmpty(modelId_prefix); + warn_Anthropic_VersionEmpty(modelId_prefix); +} + +export function changeConnTypeRowColor(conntype_row, conntype_select) { + conntype_row.classList.toggle("conntype_chatgpt_web", (conntype_select.value === "chatgpt_web")); + conntype_row.classList.toggle("conntype_chatgpt_api", (conntype_select.value === "chatgpt_api")); + conntype_row.classList.toggle("conntype_ollama_api", (conntype_select.value === "ollama_api")); + conntype_row.classList.toggle("conntype_openai_comp_api", (conntype_select.value === "openai_comp_api")); + conntype_row.classList.toggle("conntype_google_gemini_api", (conntype_select.value === "google_gemini_api")); + conntype_row.classList.toggle("conntype_anthropic_api", (conntype_select.value === "anthropic_api")); +} + +export function showConnectionOptions(conntype_select) { + let chatgpt_web_display = 'table-row'; + let chatgpt_api_display = 'none'; + let ollama_api_display = 'none'; + let openai_comp_api_display = 'none'; + let google_gemini_api_display = 'none'; + let anthropic_api_display = 'none'; + let parent = conntype_select.parentElement.parentElement.parentElement; + changeConnTypeRowColor(parent, conntype_select); + if (conntype_select.value === "chatgpt_web") { + chatgpt_web_display = 'table-row'; + }else{ + chatgpt_web_display = 'none'; + } + if (conntype_select.value === "chatgpt_api") { + chatgpt_api_display = 'table-row'; + }else{ + chatgpt_api_display = 'none'; + } + if (conntype_select.value === "ollama_api") { + ollama_api_display = 'table-row'; + }else{ + ollama_api_display = 'none'; + } + if (conntype_select.value === "openai_comp_api") { + openai_comp_api_display = 'table-row'; + }else{ + openai_comp_api_display = 'none'; + } + if (conntype_select.value === "google_gemini_api") { + google_gemini_api_display = 'table-row'; + }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; + }); + document.querySelectorAll(".conntype_chatgpt_api").forEach(element => { + element.style.display = chatgpt_api_display; + }); + document.querySelectorAll(".conntype_ollama_api").forEach(element => { + element.style.display = ollama_api_display; + }); + document.querySelectorAll(".conntype_openai_comp_api").forEach(element => { + element.style.display = openai_comp_api_display; + }); + 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 (varConnectionUI.permission_all_urls) { + document.getElementById('openai_comp_api_cors_warning').style.display = 'none'; + document.getElementById('ollama_api_cors_warning').style.display = 'none'; + } +} + + +// From here there are internal functions + +function getModelEl(model, modelId_prefix) { + return document.getElementById((modelId_prefix ? modelId_prefix : '') + model); +} + +function populateConnectionTypeOptions(selectId, no_chatgpt_web = false) { + const conntype_select = document.getElementById(selectId); + if (!conntype_select) return; + + const prevValue = conntype_select.value; + + const options = [ + { value: 'chatgpt_web', msgKey: 'prefs_Connection_type_ChatGPT_Web' }, + { value: 'chatgpt_api', msgKey: 'prefs_Connection_type_ChatGPT_API' }, + { value: 'google_gemini_api', msgKey: 'prefs_Connection_type_Google_Gemini_API' }, + { value: 'anthropic_api', msgKey: 'prefs_Connection_type_Anthropic_API' }, + { value: 'ollama_api', msgKey: 'prefs_Connection_type_Ollama_API' }, + { value: 'openai_comp_api', msgKey: 'prefs_Connection_type_OpenAI_Comp_API' } + ]; + + conntype_select.innerHTML = ''; + + for (const opt of options.filter(o => !(no_chatgpt_web && o.value === 'chatgpt_web'))) { + const optionEl = document.createElement('option'); + optionEl.value = opt.value; + optionEl.textContent = browser.i18n.getMessage(opt.msgKey) || opt.msgKey; + conntype_select.appendChild(optionEl); + } + + if (options.some(o => o.value === prevValue)) { + conntype_select.value = prevValue; + } +} + +function warn_ChatGPT_APIKeyEmpty(modelId_prefix) { + let apiKeyInput = document.getElementById('chatgpt_api_key'); + let btnFetchChatGPTModels = document.getElementById('btnUpdateChatGPTModels'); + let modelChatGPT = getModelEl('chatgpt_model', modelId_prefix); + if(apiKeyInput.value === ''){ + apiKeyInput.style.border = '2px solid red'; + btnFetchChatGPTModels.disabled = true; + modelChatGPT.disabled = true; + modelChatGPT.selectedIndex = -1; + modelChatGPT.style.border = ''; + }else{ + apiKeyInput.style.border = ''; + btnFetchChatGPTModels.disabled = false; + modelChatGPT.disabled = false; + if((modelChatGPT.selectedIndex === -1)||(modelChatGPT.value === '')){ + modelChatGPT.style.border = '2px solid red'; + }else{ + modelChatGPT.style.border = ''; + } + } +} + +function warn_GoogleGemini_APIKeyEmpty(modelId_prefix) { + let apiKeyInput = document.getElementById('google_gemini_api_key'); + let btnFetchGoogleGeminiModels = document.getElementById('btnUpdateGoogleGeminiModels'); + let modelGoogleGemini = getModelEl('google_gemini_model', modelId_prefix); + if(apiKeyInput.value === ''){ + apiKeyInput.style.border = '2px solid red'; + btnFetchGoogleGeminiModels.disabled = true; + modelGoogleGemini.disabled = true; + modelGoogleGemini.selectedIndex = -1; + modelGoogleGemini.style.border = ''; + }else{ + apiKeyInput.style.border = ''; + btnFetchGoogleGeminiModels.disabled = false; + modelGoogleGemini.disabled = false; + if((modelGoogleGemini.selectedIndex === -1)||(modelGoogleGemini.value === '')){ + modelGoogleGemini.style.border = '2px solid red'; + }else{ + modelGoogleGemini.style.border = ''; + } + } +} + +function warn_Ollama_HostEmpty(modelId_prefix) { + let hostInput = document.getElementById('ollama_host'); + let btnFetchOllamaModels = document.getElementById('btnUpdateOllamaModels'); + let modelOllama = getModelEl('ollama_model', modelId_prefix); + if(hostInput.value === ''){ + hostInput.style.border = '2px solid red'; + btnFetchOllamaModels.disabled = true; + modelOllama.disabled = true; + modelOllama.selectedIndex = -1; + modelOllama.style.border = ''; + }else{ + hostInput.style.border = ''; + btnFetchOllamaModels.disabled = false; + modelOllama.disabled = false; + if((modelOllama.selectedIndex === -1)||(modelOllama.value === '')){ + modelOllama.style.border = '2px solid red'; + }else{ + modelOllama.style.border = ''; + } + } +} + +function warn_OpenAIComp_HostEmpty(modelId_prefix) { + let hostInput = document.getElementById('openai_comp_host'); + let btnUpdateOpenAICompModels = document.getElementById('btnUpdateOpenAICompModels'); + let modelOpenAIComp = getModelEl('openai_comp_model', modelId_prefix); + if(hostInput.value === ''){ + hostInput.style.border = '2px solid red'; + btnUpdateOpenAICompModels.disabled = true; + modelOpenAIComp.disabled = true; + modelOpenAIComp.selectedIndex = -1; + modelOpenAIComp.style.border = ''; + }else{ + hostInput.style.border = ''; + btnUpdateOpenAICompModels.disabled = false; + modelOpenAIComp.disabled = false; + if((modelOpenAIComp.selectedIndex === -1)||(modelOpenAIComp.value === '')){ + modelOpenAIComp.style.border = '2px solid red'; + }else{ + modelOpenAIComp.style.border = ''; + } + } +} + +function warn_Anthropic_APIKeyEmpty(modelId_prefix) { + let apiKeyInput = document.getElementById('anthropic_api_key'); + let btnFetchAnthropicModels = document.getElementById('btnUpdateAnthropicModels'); + let modelAnthropic = getModelEl('anthropic_model', modelId_prefix); + 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(modelId_prefix) { + let versionInput = document.getElementById('anthropic_version'); + let btnFetchAnthropicModels = document.getElementById('btnUpdateAnthropicModels'); + let modelAnthropic = getModelEl('anthropic_model', modelId_prefix); + 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 resetOpenAICompConfigs(){ + let select_openai_comp_model = document.getElementById('openai_comp_services_shortcut'); + select_openai_comp_model.value = 'custom'; +} + +function loadOpenAICompConfigs(){ + let select_openai_comp_model = document.getElementById('openai_comp_services_shortcut'); + openAICompConfigs.forEach(config => { + const option = document.createElement('option'); + option.value = config.id; + option.text = config.name; + select_openai_comp_model.appendChild(option); + }); +} \ No newline at end of file diff --git a/pages/addtags/mzta-add-tags.css b/pages/addtags/mzta-add-tags.css index 070f3bdf..9b02a45c 100644 --- a/pages/addtags/mzta-add-tags.css +++ b/pages/addtags/mzta-add-tags.css @@ -43,10 +43,46 @@ background-color: #e9ffdf; } -tr.add_tags_auto_sub td{ +.specific_integration{ + background-color: #dfeaff; +} + +tr.add_tags_auto_sub td, +tr.specific_integration_sub td{ padding-left: 0.8em !important; } +.specific_integration_sub{ + display: none; +} + +table { + border-collapse: separate; + border-spacing: 0; +} + +.group td { + border-top: none; + border-bottom: none; +} + +.specific_integration_sub td:first-child { + border-left: 10px solid #dfeaff; +} + +.specific_integration_sub td:last-child { + border-right: 10px solid #dfeaff; +} + +#connection_ui_end td{ + height: 6px; + background-color: #dfeaff; +} + +#connection_ui_end{ + display: none; +} + .btn_div{ width: 100%; display: flex; @@ -72,6 +108,7 @@ table#miczPrefs { .section_title{ font-weight: bold; + font-size: 1.2em; } table#miczPrefs td{ @@ -159,4 +196,20 @@ table#miczPrefs tr:last-child td { .add_tags_auto{ background-color: #415c35; } + + .specific_integration{ + background-color: #2E3A4F; + } + + .specific_integration_sub td:first-child { + border-left: 10px solid #2E3A4F; + } + + .specific_integration_sub td:last-child { + border-right: 10px solid #2E3A4F; + } + + #connection_ui_end td{ + background-color: #2E3A4F; + } } \ No newline at end of file diff --git a/pages/addtags/mzta-add-tags.html b/pages/addtags/mzta-add-tags.html index bf9d8c53..0c27b53f 100644 --- a/pages/addtags/mzta-add-tags.html +++ b/pages/addtags/mzta-add-tags.html @@ -4,6 +4,7 @@ ThunderAI - __MSG_AddTags_PageTitle__ + @@ -13,8 +14,20 @@
    __MSG_AddTags_prompt_prefs_title__
    + + + + + + - + - + - + - + - + - - + - + - + - +
    __MSG_prefs_OptionText_use_specific_integration__ + + +
    __MSG_prefs_OptionText_add_tags_maxnum____MSG_prefs_OptionText_add_tags_maxnum__
    __MSG_prefs_OptionText_add_tags_exclusions_exact_match____MSG_prefs_OptionText_add_tags_exclusions_exact_match__
    __MSG_prefs_OptionText_add_tags_hide_exclusions____MSG_prefs_OptionText_add_tags_hide_exclusions__
    __MSG_prefs_OptionText_add_tags_first_uppercase____MSG_prefs_OptionText_add_tags_first_uppercase__
    __MSG_prefs_OptionText_add_tags_force_lang____MSG_prefs_OptionText_add_tags_force_lang__
    __MSG_prefs_OptionText_add_tags_auto__ + __MSG_prefs_OptionText_add_tags_auto__
    __MSG_prefs_OptionText_add_tags_auto_Info2__
    __MSG_prefs_OptionText_add_tags_auto_only_inbox____MSG_prefs_OptionText_add_tags_auto_only_inbox__
    __MSG_prefs_OptionText_add_tags_auto_uselist____MSG_prefs_OptionText_add_tags_auto_uselist__
    __MSG_prefs_OptionText_add_tags_context_menu____MSG_prefs_OptionText_add_tags_context_menu__
    __MSG_prefs_OptionText_add_tags_auto_force_existing____MSG_prefs_OptionText_add_tags_auto_force_existing__