diff --git a/CHANGELOG.md b/CHANGELOG.md index abc12f4e..4dbd3aa3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@
  • Added the {%mail_attachments_info%} placeholder to retrieve the name, type and file size of the mail attachments [#446].
  • [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.
  • [All APIs] It's now possibile to define a list of tags to be used when autotagging received emails [#436]. The tags are are now shown in the information header in the AI API chat [#289].
  • [OpenAI Comp API] Added DeepSeek configuration [#486].
  • ...
  • diff --git a/README.md b/README.md index e866075c..0bcaa8e1 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,8 @@ _The language status represents the percentage of translated strings in the late - loading.io for the loading SVGs - [Fluent Design System](https://www.iconfinder.com/fluent-designsystem) for the Custom Prompts table sorting icons - [JessiGue](https://www.flaticon.com/authors/jessigue) for the show/hide icon for api key fields +- [Iconka.com](https://www.iconarchive.com/artist/iconka.html) for the autotag context menu icon +- [Icojam](https://www.iconarchive.com/artist/icojam.html) for the spam filter context menu icon
    diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 50599c1b..f73006c0 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": "" @@ -1653,6 +1665,16 @@ "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.", "SelectAll": { "message": "Select All", "description": "" @@ -1661,4 +1683,4 @@ "message": "Deselect All", "description": "" } -} \ No newline at end of file +} diff --git a/api_webchat/controller.js b/api_webchat/controller.js index ef2b4f77..ef4050c1 100644 --- a/api_webchat/controller.js +++ b/api_webchat/controller.js @@ -20,6 +20,7 @@ * The original code has been released under the Apache License, Version 2.0. */ +import { prefs_default } from '../options/mzta-options-default.js'; import { placeholdersUtils } from '../js/mzta-placeholders.js'; import { getAPIsInitMessageString, convertNewlinesToBr } from '../js/mzta-utils.js'; @@ -75,13 +76,27 @@ messageInput.setMessagesArea(messagesArea); switch (llm) { case "chatgpt_api": { - let prefs_api = await browser.storage.sync.get({chatgpt_api_key: '', chatgpt_model: '', chatgpt_developer_messages:'', chatgpt_api_store: false, do_debug: false}); + let prefs_api = await browser.storage.sync.get({ + chatgpt_api_key: prefs_default.chatgpt_api_key, + chatgpt_model: prefs_default.chatgpt_model, + chatgpt_developer_messages: prefs_default.chatgpt_developer_messages, + chatgpt_api_store: prefs_default.chatgpt_api_store, + do_debug: prefs_default.do_debug, + }); let i18nStrings = {}; i18nStrings["chatgpt_api_request_failed"] = browser.i18n.getMessage('chatgpt_api_request_failed'); i18nStrings["error_connection_interrupted"] = browser.i18n.getMessage('error_connection_interrupted'); messageInput.setModel(prefs_api.chatgpt_model); messagesArea.setLLMName("ChatGPT"); - worker.postMessage({ type: 'init', chatgpt_api_key: prefs_api.chatgpt_api_key, chatgpt_model: prefs_api.chatgpt_model, chatgpt_developer_messages: prefs_api.chatgpt_developer_messages, chatgpt_api_store: prefs_api.chatgpt_api_store, do_debug: prefs_api.do_debug, i18nStrings: i18nStrings}); + worker.postMessage({ + type: 'init', + chatgpt_api_key: prefs_api.chatgpt_api_key, + chatgpt_model: prefs_api.chatgpt_model, + chatgpt_developer_messages: prefs_api.chatgpt_developer_messages, + chatgpt_api_store: prefs_api.chatgpt_api_store, + do_debug: prefs_api.do_debug, + i18nStrings: i18nStrings, + }); let additional_text_elements = []; additional_text_elements.push({label: 'OpenAI Store', value: (prefs_api.chatgpt_api_store ? 'Yes' : 'No')}); if(prefs_api.chatgpt_developer_messages && prefs_api.chatgpt_developer_messages.length > 0) { @@ -93,11 +108,20 @@ switch (llm) { model_string: prefs_api.chatgpt_model, additional_messages: additional_text_elements }), "info"); - browser.runtime.sendMessage({command: "openai_api_ready_" + call_id, window_id: (await browser.windows.getCurrent()).id}); + browser.runtime.sendMessage({ + command: "openai_api_ready_" + call_id, + window_id: (await browser.windows.getCurrent()).id + }); break; } case "google_gemini_api": { - let prefs_api = await browser.storage.sync.get({google_gemini_api_key: '', google_gemini_model: '', google_gemini_system_instruction: '', do_debug: false}); + let prefs_api = await browser.storage.sync.get({ + google_gemini_api_key: prefs_default.google_gemini_api_key, + google_gemini_model: prefs_default.google_gemini_model, + google_gemini_system_instruction: prefs_default.google_gemini_system_instruction, + google_gemini_thinking_budget: prefs_default.google_gemini_thinking_budget, + do_debug: prefs_default.do_debug, + }); let i18nStrings = {}; i18nStrings["google_gemini_api_request_failed"] = browser.i18n.getMessage('google_gemini_api_request_failed'); i18nStrings["error_connection_interrupted"] = browser.i18n.getMessage('error_connection_interrupted'); @@ -107,25 +131,54 @@ switch (llm) { if(prefs_api.google_gemini_system_instruction && prefs_api.google_gemini_system_instruction.length > 0) { additional_text_elements.push({label: browser.i18n.getMessage("GoogleGemini_SystemInstruction"), value: prefs_api.google_gemini_system_instruction}); } + additional_text_elements.push({label: 'Thinking Budget', value: prefs_api.google_gemini_thinking_budget}); additional_text_elements.push({label: "Prompt", value: '[' + prompt_id + '] ' + decodeURIComponent(prompt_name)}); - worker.postMessage({ type: 'init', google_gemini_api_key: prefs_api.google_gemini_api_key, google_gemini_model: prefs_api.google_gemini_model, google_gemini_system_instruction: prefs_api.google_gemini_system_instruction, do_debug: prefs_api.do_debug, i18nStrings: i18nStrings}); + worker.postMessage({ + type: 'init', + google_gemini_api_key: prefs_api.google_gemini_api_key, + google_gemini_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: prefs_api.do_debug, + i18nStrings: i18nStrings, + }); messagesArea.appendUserMessage(getAPIsInitMessageString({ api_string: "Google Gemini API", model_string: prefs_api.google_gemini_model, additional_messages: additional_text_elements }), "info"); - browser.runtime.sendMessage({command: "google_gemini_api_ready_" + call_id, window_id: (await browser.windows.getCurrent()).id}); + browser.runtime.sendMessage({ + command: "google_gemini_api_ready_" + call_id, + window_id: (await browser.windows.getCurrent()).id + }); break; } case "ollama_api": { - let prefs_api = await browser.storage.sync.get({ollama_host: '', ollama_model: '', ollama_num_ctx: 0, ollama_think: false, do_debug: false}); + let prefs_api = await browser.storage.sync.get({ + ollama_host: prefs_default.ollama_host, + ollama_model: prefs_default.ollama_model, + ollama_num_ctx: prefs_default.ollama_num_ctx, + ollama_think: prefs_default.ollama_think, + do_debug: prefs_default.do_debug, + }); let i18nStrings = {}; i18nStrings["ollama_api_request_failed"] = browser.i18n.getMessage('ollama_api_request_failed'); i18nStrings["error_connection_interrupted"] = browser.i18n.getMessage('error_connection_interrupted'); messageInput.setModel(prefs_api.ollama_model); messagesArea.setLLMName("Ollama Local"); - worker.postMessage({ type: 'init', ollama_host: prefs_api.ollama_host, ollama_model: prefs_api.ollama_model, ollama_num_ctx: prefs_api.ollama_num_ctx, ollama_think: prefs_api.ollama_think, do_debug: prefs_api.do_debug, i18nStrings: i18nStrings}); - browser.runtime.sendMessage({command: "ollama_api_ready_" + call_id, window_id: (await browser.windows.getCurrent()).id}); + worker.postMessage({ + type: 'init', + ollama_host: prefs_api.ollama_host, + ollama_model: prefs_api.ollama_model, + ollama_num_ctx: prefs_api.ollama_num_ctx, + ollama_think: prefs_api.ollama_think, + do_debug: prefs_api.do_debug, + i18nStrings: i18nStrings + }); + browser.runtime.sendMessage({ + command: "ollama_api_ready_" + call_id, + window_id: (await browser.windows.getCurrent()).id + }); let additional_text_elements = []; additional_text_elements.push({label: "Prompt", value: '[' + prompt_id + '] ' + decodeURIComponent(prompt_name)}); messagesArea.appendUserMessage(getAPIsInitMessageString({ @@ -137,13 +190,28 @@ switch (llm) { break; } case "openai_comp_api": { - let prefs_api = await browser.storage.sync.get({openai_comp_host: '', openai_comp_model: '', openai_comp_api_key: '', openai_comp_use_v1: true, openai_comp_chat_name: '', do_debug: false}); + let prefs_api = await browser.storage.sync.get({ + openai_comp_host: prefs_default.openai_comp_host, + openai_comp_model: prefs_default.openai_comp_model, + openai_comp_api_key: prefs_default.openai_comp_api_key, + openai_comp_use_v1: prefs_default.openai_comp_use_v1, + openai_comp_chat_name: prefs_default.openai_comp_chat_name, + do_debug: prefs_default.do_debug, + }); let i18nStrings = {}; i18nStrings["OpenAIComp_api_request_failed"] = browser.i18n.getMessage('OpenAIComp_api_request_failed'); i18nStrings["error_connection_interrupted"] = browser.i18n.getMessage('error_connection_interrupted'); messageInput.setModel(prefs_api.openai_comp_model); messagesArea.setLLMName(prefs_api.openai_comp_chat_name); - worker.postMessage({ type: 'init', openai_comp_host: prefs_api.openai_comp_host, openai_comp_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: prefs_api.do_debug, i18nStrings: i18nStrings}); + worker.postMessage({ + type: 'init', + openai_comp_host: prefs_api.openai_comp_host, + openai_comp_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: prefs_api.do_debug, + i18nStrings: i18nStrings, + }); let additional_text_elements = []; additional_text_elements.push({label: "Prompt", value: '[' + prompt_id + '] ' + decodeURIComponent(prompt_name)}); messagesArea.appendUserMessage(getAPIsInitMessageString({ @@ -152,17 +220,34 @@ switch (llm) { host_string: prefs_api.openai_comp_host, additional_messages: additional_text_elements }), "info"); - browser.runtime.sendMessage({command: "openai_comp_api_ready_" + call_id, window_id: (await browser.windows.getCurrent()).id}); + browser.runtime.sendMessage({ + command: "openai_comp_api_ready_" + call_id, + window_id: (await browser.windows.getCurrent()).id + }); break; } case "anthropic_api": { - let prefs_api = await browser.storage.sync.get({anthropic_api_key: '', anthropic_model: '', anthropic_version: '2023-06-01', anthropic_max_tokens: 4096, do_debug: false}); + let prefs_api = await browser.storage.sync.get({ + anthropic_api_key: prefs_default.anthropic_api_key, + anthropic_model: prefs_default.anthropic_model, + anthropic_version: prefs_default.anthropic_version, + anthropic_max_tokens: prefs_default.anthropic_max_tokens, + do_debug: prefs_default.do_debug, + }); let i18nStrings = {}; i18nStrings["anthropic_api_request_failed"] = browser.i18n.getMessage('anthropic_api_request_failed'); i18nStrings["error_connection_interrupted"] = browser.i18n.getMessage('error_connection_interrupted'); messageInput.setModel(prefs_api.anthropic_model); messagesArea.setLLMName("Anthropic"); - worker.postMessage({ type: 'init', anthropic_api_key: prefs_api.anthropic_api_key, anthropic_model: prefs_api.anthropic_model, anthropic_version: prefs_api.anthropic_version, anthropic_max_tokens: prefs_api.anthropic_max_tokens, do_debug: prefs_api.do_debug, i18nStrings: i18nStrings}); + worker.postMessage({ + type: 'init', + anthropic_api_key: prefs_api.anthropic_api_key, + anthropic_model: prefs_api.anthropic_model, + anthropic_version: prefs_api.anthropic_version, + anthropic_max_tokens: prefs_api.anthropic_max_tokens, + do_debug: prefs_api.do_debug, + i18nStrings: i18nStrings, + }); let additional_text_elements = []; additional_text_elements.push({label: "Prompt", value: '[' + prompt_id + '] ' + decodeURIComponent(prompt_name)}); messagesArea.appendUserMessage(getAPIsInitMessageString({ @@ -171,7 +256,10 @@ switch (llm) { version_string: prefs_api.anthropic_version, additional_messages: additional_text_elements }), "info"); - browser.runtime.sendMessage({command: "anthropic_api_ready_" + call_id, window_id: (await browser.windows.getCurrent()).id}); + browser.runtime.sendMessage({ + command: "anthropic_api_ready_" + call_id, + window_id: (await browser.windows.getCurrent()).id + }); break; } } @@ -243,4 +331,4 @@ browser.runtime.onMessage.addListener((message, sender, sendResponse) => { function sendPrompt(message){ messageInput._setMessageInputValue(convertNewlinesToBr(message.prompt)); messageInput._handleNewChatMessage(); -} \ No newline at end of file +} diff --git a/api_webchat/messagesArea.js b/api_webchat/messagesArea.js index eb61eca0..3696dbe2 100644 --- a/api_webchat/messagesArea.js +++ b/api_webchat/messagesArea.js @@ -20,6 +20,7 @@ * The original code has been released under the Apache License, Version 2.0. */ +import { prefs_default } from '../options/mzta-options-default.js'; const messagesAreaTemplate = document.createElement('template'); const messagesAreaStyle = document.createElement('style'); @@ -383,7 +384,7 @@ class MessagesArea extends HTMLElement { splitButton.appendChild(actionButton); const fullTextHTMLAtAssignment = this.fullTextHTML.trim().replace(/^"|"$/g, '').replace(/^

    "/, '

    ').replace(/"<\/p>$/, '

    '); // strip quotation marks //console.log(">>>>>>>>>>>> fullTextHTMLAtAssignment: " + fullTextHTMLAtAssignment); - let reply_type_pref = await browser.storage.sync.get({reply_type: 'reply_all'}); + let reply_type_pref = await browser.storage.sync.get({ reply_type: prefs_default.reply_type }); if((promptData.action == "1") && (promptData.mailMessageId != -1)) { const actionButton_line2 = document.createElement('span'); actionButton_line2.classList.add('action_btn_info'); @@ -613,4 +614,4 @@ function removeAloneBRs(htmlString) { }); return doc.body.innerHTML; -} \ No newline at end of file +} diff --git a/images/autotags.png b/images/autotags.png new file mode 100644 index 00000000..7490e48e Binary files /dev/null and b/images/autotags.png differ diff --git a/images/spamfilter.png b/images/spamfilter.png new file mode 100644 index 00000000..a068ef4e Binary files /dev/null and b/images/spamfilter.png differ diff --git a/js/api/anthropic.js b/js/api/anthropic.js index 662b45df..724a092b 100644 --- a/js/api/anthropic.js +++ b/js/api/anthropic.js @@ -27,7 +27,13 @@ export class Anthropic { max_tokens = 4096; stream = false; - constructor(apiKey, version, model, max_tokens = 4096, stream = false) { + constructor({ + apiKey = '', + version = '', + model = '', + max_tokens = 4096, + stream = false, + } = {}) { this.apiKey = apiKey; this.version = version; this.model = model; @@ -104,4 +110,4 @@ export class Anthropic { } } -} \ No newline at end of file +} diff --git a/js/api/google_gemini.js b/js/api/google_gemini.js index d0d8a055..f2e2eb67 100644 --- a/js/api/google_gemini.js +++ b/js/api/google_gemini.js @@ -24,12 +24,26 @@ export class GoogleGemini { model = ''; system_instruction = ''; stream = false; + thinking_budget = ''; // Model default - constructor(apiKey, model, system_instruction, stream) { + constructor({ + apiKey = '', + model = '', + system_instruction = '', + stream = false, + thinking_budget = '', + } = {}) { this.apiKey = apiKey; this.model = model; this.system_instruction = system_instruction; this.stream = stream; + this.thinking_budget = String(thinking_budget ?? '').trim(); + /* Info from: https://ai.google.dev/gemini-api/docs/thinking?#set-budget + # Turn on thinking with a specific token limit: "thinking_budget": 1024 + # Thinking off: "thinking_budget": 0 + # Turn on dynamic thinking: "thinking_budget": -1 + # Keep model default thinking: "thinking_budget": "" + */ } @@ -83,7 +97,15 @@ export class GoogleGemini { parts:{ text: this.system_instruction } - } + }; + } + + if(this.thinking_budget !== '') { + google_gemini_body.generationConfig = { + thinkingConfig: { + thinking_budget: this.thinking_budget, + } + }; } // console.log("[ThunderAI] Google Gemini API request: " + JSON.stringify(google_gemini_body)); diff --git a/js/api/ollama.js b/js/api/ollama.js index 5c10763a..9a4be836 100644 --- a/js/api/ollama.js +++ b/js/api/ollama.js @@ -24,8 +24,14 @@ export class Ollama { num_ctx = 0; think = false; - constructor(host, model, stream = false, num_ctx = 0, think = false) { - this.host = host.trim().replace(/\/+$/, ""); + constructor({ + host = '', + model = '', + stream = false, + num_ctx = 0, + think = false, + } = {}) { + this.host = (host || '').trim().replace(/\/+$/, ""); this.model = model; this.stream = stream; this.num_ctx = num_ctx; @@ -123,4 +129,4 @@ export class Ollama { // return output; // } // } -} \ No newline at end of file +} diff --git a/js/api/openai.js b/js/api/openai.js index 11884e7e..6d589c4c 100644 --- a/js/api/openai.js +++ b/js/api/openai.js @@ -27,7 +27,13 @@ export class OpenAI { stream = false; store = false; - constructor(apiKey, model, developer_messages, stream, store) { + constructor({ + apiKey = '', + model = '', + developer_messages = '', + stream = false, + store = false + } = {}) { this.apiKey = apiKey; this.model = model; this.developer_messages = developer_messages; @@ -120,4 +126,4 @@ export class OpenAI { return data.token_count; } -} \ No newline at end of file +} diff --git a/js/api/openai_comp.js b/js/api/openai_comp.js index a0ba14ec..3e975b62 100644 --- a/js/api/openai_comp.js +++ b/js/api/openai_comp.js @@ -27,8 +27,14 @@ export class OpenAIComp { use_v1 = true; stream = false; - constructor(host, model, apiKey = '', stream = false, use_v1 = true) { - this.host = host.trim().replace(/\/+$/, ""); + constructor({ + host = '', + model = '', + apiKey = '', + stream = false, + use_v1 = true, + } = {}) { + this.host = (host || '').trim().replace(/\/+$/, ""); this.model = model; this.stream = stream; this.apiKey = apiKey; @@ -107,4 +113,4 @@ export class OpenAIComp { } } -} \ No newline at end of file +} diff --git a/js/mzta-menus.js b/js/mzta-menus.js index c836acde..0ecec436 100644 --- a/js/mzta-menus.js +++ b/js/mzta-menus.js @@ -19,7 +19,20 @@ // Some original methods are derived from https://github.com/ali-raheem/Aify/blob/cfadf52f576b7be3720b5b73af7c8d3129c054da/plugin/html/actions.js import { getPrompts } from './mzta-prompts.js'; -import { getLanguageDisplayName, getMenuContextCompose, getMenuContextDisplay, i18nConditionalGet, getMailSubject, getTagsList, extractJsonObject, convertNewlinesToBr, cleanupNewlines, checkIfTagLabelExists } from './mzta-utils.js' +import { prefs_default } from '../options/mzta-options-default.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'; @@ -182,9 +195,17 @@ export class mzta_Menus { case 'prompt_add_tags': { // Add tags to the email let tags_current_email = []; let tags_current_email_final = []; - let prefs_at = await browser.storage.sync.get({add_tags_maxnum: 3, connection_type: '', add_tags_force_lang: true, add_tags_auto_force_existing: false, default_chatgpt_lang: '', do_debug: false}); - 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 prefs_at = await browser.storage.sync.get({ + add_tags_maxnum: prefs_default.add_tags_maxnum, + connection_type: prefs_default.connection_type, + add_tags_force_lang: prefs_default.add_tags_force_lang, + add_tags_auto_force_existing: prefs_default.add_tags_auto_force_existing, + default_chatgpt_lang: prefs_default.default_chatgpt_lang, + do_debug: prefs_default.do_debug, + }); + 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'}; } @@ -192,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()); @@ -226,7 +254,11 @@ export class mzta_Menus { } case 'prompt_get_calendar_event': { // Get a calendar event info let calendar_event_data = ''; - let prefs_at = await browser.storage.sync.get({connection_type: '', calendar_enforce_timezone: false, calendar_timezone: '',}); + let prefs_at = await browser.storage.sync.get({ + connection_type: prefs_default.connection_type, + calendar_enforce_timezone: prefs_default.calendar_enforce_timezone, + calendar_timezone: prefs_default.calendar_timezone, + }); if((prefs_at.connection_type === '')||(prefs_at.connection_type === null)||(prefs_at.connection_type === undefined)||(prefs_at.connection_type === 'chatgpt_web')){ console.error("[ThunderAI | GetCalendarEvent] Invalid connection type: " + prefs_at.connection_type); taWorkingStatus.stopWorking(); @@ -243,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(); @@ -311,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(); @@ -377,6 +417,7 @@ export class mzta_Menus { } }else{ // Classic prompts for the API webchat this.logger.log("fullPrompt: " + fullPrompt); + this.logger.log("curr_prompt: " + JSON.stringify(curr_prompt)); this.openChatGPT(fullPrompt, curr_prompt.action, tabs[0].id, curr_prompt.name, curr_prompt.need_custom_text, curr_prompt); taWorkingStatus.stopWorking(); return {ok:'1'}; @@ -500,4 +541,4 @@ export class mzta_Menus { return false; } -} \ No newline at end of file +} diff --git a/js/mzta-placeholders.js b/js/mzta-placeholders.js index fb08a387..0eb6f926 100644 --- a/js/mzta-placeholders.js +++ b/js/mzta-placeholders.js @@ -16,6 +16,8 @@ * along with this program. If not, see . */ +import { prefs_default } from '../options/mzta-options-default.js'; + /* ================= PLACEHOLDERS PROPERTIES ======================================== ================ BASE PROPERTIES @@ -480,11 +482,11 @@ export const placeholdersUtils = { finalSubs['tags_full_list'] = placeholdersUtils.failSafePlaceholders(tags_full_list[0]); break; case 'thunderai_def_sign': - let prefs_def_sign = await browser.storage.sync.get({default_sign_name: ''}); + let prefs_def_sign = await browser.storage.sync.get({ default_sign_name: prefs_default.default_sign_name }); finalSubs['thunderai_def_sign'] = placeholdersUtils.failSafePlaceholders(prefs_def_sign.default_sign_name); break; case 'thunderai_def_lang': - let prefs_def_lang = await browser.storage.sync.get({default_chatgpt_lang: ''}); + let prefs_def_lang = await browser.storage.sync.get({ default_chatgpt_lang: prefs_default.default_chatgpt_lang }); finalSubs['thunderai_def_lang'] = placeholdersUtils.failSafePlaceholders(prefs_def_lang.default_chatgpt_lang); break; case 'mail_attachments_info': @@ -516,4 +518,4 @@ export const placeholdersUtils = { return element; } -} \ No newline at end of file +} diff --git a/js/mzta-prompts.js b/js/mzta-prompts.js index f1fbce97..23182c84 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 === undefined || prompt.position_compose === "undefined") ? "" : prompt.position_compose, + position_display: (prompt.position_display === undefined || prompt.position_display === "undefined") ? "" : prompt.position_display, + enabled: (prompt.enabled === undefined || prompt.enabled === "undefined") ? "" : prompt.enabled, + need_custom_text: (prompt.need_custom_text === undefined || prompt.need_custom_text === "undefined") ? "" : prompt.need_custom_text, + chatgpt_web_model: (prompt.chatgpt_web_model === undefined || prompt.chatgpt_web_model === "undefined") ? "" : prompt.chatgpt_web_model, + chatgpt_web_project: (prompt.chatgpt_web_project === undefined || prompt.chatgpt_web_project === "undefined") ? "" : prompt.chatgpt_web_project, + chatgpt_web_custom_gpt: (prompt.chatgpt_web_custom_gpt === undefined || prompt.chatgpt_web_custom_gpt === "undefined") ? "" : prompt.chatgpt_web_custom_gpt, + api_type: (prompt.api_type === undefined || prompt.api_type === "undefined") ? "" : prompt.api_type, + api_model: (prompt.api_model === undefined || prompt.api_model === "undefined") ? "" : 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 2ac7ac38..0bbab812 100644 --- a/js/mzta-special-commands.js +++ b/js/mzta-special-commands.js @@ -17,22 +17,29 @@ */ // Call the API to use a special prompt - + import { prefs_default } from "../options/mzta-options-default.js"; import { taLogger } from './mzta-logger.js'; - export class mzta_specialCommand { 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) { @@ -58,30 +65,92 @@ } async initWorker() { + // console.log((">>>>>>>>>>>> this.custom_model: " + this.custom_model)); switch (this.llm) { case "chatgpt_api": { - let prefs_api = await browser.storage.sync.get({chatgpt_api_key: '', chatgpt_model: '', chatgpt_developer_messages: ''}); - this.worker.postMessage({ type: 'init', chatgpt_api_key: prefs_api.chatgpt_api_key, chatgpt_model: prefs_api.chatgpt_model, chatgpt_developer_messages: prefs_api.chatgpt_developer_messages, do_debug: this.do_debug, i18nStrings: ''}); + let prefs_api = await browser.storage.sync.get({ + chatgpt_api_key: prefs_default.chatgpt_api_key, + chatgpt_model: prefs_default.chatgpt_model, + chatgpt_developer_messages: prefs_default.chatgpt_developer_messages, + }); + this.worker.postMessage({ + type: 'init', + chatgpt_api_key: prefs_api.chatgpt_api_key, + 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: '' + }); break; } case "google_gemini_api": { - let prefs_api = await browser.storage.sync.get({google_gemini_api_key: '', google_gemini_model: '', google_gemini_system_instruction: ''}); - 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_system_instruction: prefs_api.google_gemini_system_instruction, do_debug: this.do_debug, i18nStrings: ''}); + let prefs_api = await browser.storage.sync.get({ + google_gemini_api_key: prefs_default.google_gemini_api_key, + google_gemini_model: prefs_default.google_gemini_model, + google_gemini_system_instruction: prefs_default.google_gemini_system_instruction, + google_gemini_thinking_budget: prefs_default.google_gemini_thinking_budget, + }); + this.worker.postMessage({ + type: 'init', + google_gemini_api_key: prefs_api.google_gemini_api_key, + 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, + i18nStrings: '' + }); break; } case "ollama_api": { - let prefs_api = await browser.storage.sync.get({ollama_host: '', ollama_model: ''}); - this.worker.postMessage({ type: 'init', ollama_host: prefs_api.ollama_host, ollama_model: prefs_api.ollama_model, do_debug: this.do_debug, i18nStrings: ''}); + let prefs_api = await browser.storage.sync.get({ + ollama_host: prefs_default.ollama_host, + ollama_model: prefs_default.ollama_model, + }); + this.worker.postMessage({ + type: 'init', + ollama_host: prefs_api.ollama_host, + ollama_model: this.custom_model != '' ? this.custom_model : prefs_api.ollama_model, + do_debug: this.do_debug, + i18nStrings: '' + }); break; } case "openai_comp_api": { - let prefs_api = await browser.storage.sync.get({openai_comp_host: '', openai_comp_model: '', openai_comp_api_key: '', openai_comp_use_v1: true, openai_comp_chat_name: '', do_debug: false}); - this.worker.postMessage({ type: 'init', openai_comp_host: prefs_api.openai_comp_host, openai_comp_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, i18nStrings: ''}); + let prefs_api = await browser.storage.sync.get({ + openai_comp_host: prefs_default.openai_comp_host, + openai_comp_model: prefs_default.openai_comp_model, + openai_comp_api_key: prefs_default.openai_comp_api_key, + openai_comp_use_v1: prefs_default.openai_comp_use_v1, + openai_comp_chat_name: prefs_default.openai_comp_chat_name, + do_debug: prefs_default.do_debug, + }); + this.worker.postMessage({ + type: 'init', + openai_comp_host: prefs_api.openai_comp_host, + 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, + i18nStrings: '' + }); break; } case "anthropic_api": { - let prefs_api = await browser.storage.sync.get({anthropic_api_key: '', anthropic_model: '', anthropic_version: '2023-06-01', anthropic_max_tokens: 4096}); - this.worker.postMessage({ type: 'init', anthropic_api_key: prefs_api.anthropic_api_key, anthropic_model: prefs_api.anthropic_model, anthropic_version: prefs_api.anthropic_version, anthropic_max_tokens: prefs_api.anthropic_max_tokens, do_debug: this.do_debug, i18nStrings: ''}); + let prefs_api = await browser.storage.sync.get({ + anthropic_api_key: prefs_default.anthropic_api_key, + anthropic_model: prefs_default.anthropic_model, + anthropic_version: prefs_default.anthropic_version, + anthropic_max_tokens: prefs_default.anthropic_max_tokens, + }); + this.worker.postMessage({ + type: 'init', + anthropic_api_key: prefs_api.anthropic_api_key, + 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, + i18nStrings: '' + }); break; } } @@ -131,4 +200,4 @@ } }); } - } \ No newline at end of file + } diff --git a/js/mzta-utils-prompt.js b/js/mzta-utils-prompt.js index 57098416..896f6406 100644 --- a/js/mzta-utils-prompt.js +++ b/js/mzta-utils-prompt.js @@ -18,11 +18,12 @@ import { placeholdersUtils } from './mzta-placeholders.js'; import { extractJsonObject } from './mzta-utils.js'; +import { prefs_default } from '../options/mzta-options-default.js'; export const taPromptUtils = { async getDefaultSignature(){ - let prefs = await browser.storage.sync.get({default_sign_name: ''}); + let prefs = await browser.storage.sync.get({ default_sign_name: prefs_default.default_sign_name }); if(prefs.default_sign_name===''){ return ''; }else{ @@ -68,7 +69,7 @@ export const taPromptUtils = { selection_html: selection_html, tags_full_list: tags_full_list }); - let prefs_ph = await browser.storage.sync.get({placeholders_use_default_value: false}); + let prefs_ph = await browser.storage.sync.get({ placeholders_use_default_value: prefs_default.placeholders_use_default_value }); fullPrompt = (placeholdersUtils.replacePlaceholders({ text: curr_prompt.text, replacements: finalSubs, @@ -88,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; @@ -104,7 +105,7 @@ export const taPromptUtils = { async getDefaultLang(curr_prompt){ let chatgpt_lang = ''; if(String(curr_prompt.define_response_lang) == "1"){ - let prefs = await browser.storage.sync.get({default_chatgpt_lang: ''}); + let prefs = await browser.storage.sync.get({ default_chatgpt_lang: prefs_default.default_chatgpt_lang }); chatgpt_lang = prefs.default_chatgpt_lang; if(chatgpt_lang === ''){ chatgpt_lang = browser.i18n.getMessage("reply_same_lang"); @@ -152,4 +153,4 @@ export const taPromptUtils = { } return tags; } -}; \ No newline at end of file +}; diff --git a/js/mzta-utils.js b/js/mzta-utils.js index bf41319a..81e7ac9f 100644 --- a/js/mzta-utils.js +++ b/js/mzta-utils.js @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +import { prefs_default } from '../options/mzta-options-default.js'; const sparks_min = '1.2.0'; // Minimum version of ThunderAI-Sparks required for the add-on to work export const ChatGPTWeb_models = ['gpt-5','gpt-5-instant','gpt-5-t-mini','gpt-5-thinking']; // List of models available in ChatGPT Web @@ -24,6 +25,10 @@ export const getMenuContextDisplay = () => 'message_display_action_menu'; export const contextMenuID_AddTags = 'mzta-add-tags'; export const contextMenuID_Spamfilter = 'mzta-spamfilter'; +export const contextMenuIconsPath = { + [contextMenuID_AddTags]: 'moz-extension:images/autotags.png', + [contextMenuID_Spamfilter]: 'moz-extension:images/spamfilter.png', +}; export function getLanguageDisplayName(languageCode) { const languageDisplay = new Intl.DisplayNames([languageCode], {type: 'language'}); @@ -250,6 +255,7 @@ export function convertNewlinesToParagraphs(input) { // This method is used to convert the model string id used in the URL // to the model string used in the webpage export function getGPTWebModelString(model) { + if (!model) return ''; model = model.toLowerCase().trim(); switch (model) { case 'gpt-5': @@ -388,7 +394,7 @@ export async function getTagsList(){ } export async function createTag(tag) { - let prefs_tag = await browser.storage.sync.get({add_tags_first_uppercase: true}); + let prefs_tag = await browser.storage.sync.get({ add_tags_first_uppercase: prefs_default.add_tags_first_uppercase }); if(prefs_tag.add_tags_first_uppercase) tag = tag.toLowerCase().charAt(0).toUpperCase() + tag.toLowerCase().slice(1); try { if(await isThunderbird128OrGreater()) { @@ -548,10 +554,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) { @@ -567,6 +584,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]*\}/); @@ -586,6 +607,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"}); @@ -607,10 +644,12 @@ export function validateChatGPTWebCustomData(data) { } export function sanitizeChatGPTModelData(input) { + if(!input) return ''; return encodeURIComponent(input).toLowerCase() } export function sanitizeChatGPTWebCustomData(input) { + if(!input) return ''; // Removes all characters that are not letters, numbers, dashes, or slashes return input.replace(/[^\p{L}\p{N}\/-]+/gu, ''); } @@ -753,4 +792,4 @@ export async function* getMessages(list) { yield message; } } -} \ No newline at end of file +} diff --git a/js/workers/model-worker-anthropic.js b/js/workers/model-worker-anthropic.js index a16578c9..39cc136f 100644 --- a/js/workers/model-worker-anthropic.js +++ b/js/workers/model-worker-anthropic.js @@ -36,9 +36,16 @@ 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(anthropic_api_key, event.data.anthropic_version, anthropic_model, event.data.anthropic_max_tokens, true); + anthropic = new Anthropic({ + apiKey: anthropic_api_key, + version: event.data.anthropic_version, + model: anthropic_model, + max_tokens: event.data.anthropic_max_tokens, + stream: true + }); do_debug = event.data.do_debug; i18nStrings = event.data.i18nStrings; taLog = new taLogger('model-worker-anthropic', do_debug); diff --git a/js/workers/model-worker-google_gemini.js b/js/workers/model-worker-google_gemini.js index e4557f52..cb961a66 100644 --- a/js/workers/model-worker-google_gemini.js +++ b/js/workers/model-worker-google_gemini.js @@ -38,7 +38,13 @@ self.onmessage = async function(event) { if (event.data.type === 'init') { google_gemini_api_key = event.data.google_gemini_api_key; google_gemini_model = event.data.google_gemini_model; - google_gemini = new GoogleGemini(google_gemini_api_key, google_gemini_model, event.data.google_gemini_system_instruction, true); + google_gemini = new GoogleGemini({ + apiKey: google_gemini_api_key, + model: google_gemini_model, + system_instruction: event.data.google_gemini_system_instruction, + thinking_budget: event.data.google_gemini_thinking_budget, + stream: true + }); do_debug = event.data.do_debug; i18nStrings = event.data.i18nStrings; taLog = new taLogger('model-worker-google_gemini', do_debug); diff --git a/js/workers/model-worker-ollama.js b/js/workers/model-worker-ollama.js index f29bab62..c04f0ef6 100644 --- a/js/workers/model-worker-ollama.js +++ b/js/workers/model-worker-ollama.js @@ -42,7 +42,12 @@ self.onmessage = async function(event) { ollama_model = event.data.ollama_model; ollama_num_ctx = event.data.ollama_num_ctx; //console.log(">>>>>>>>>>> ollama_host: " + ollama_host); - ollama = new Ollama(ollama_host, ollama_model, true, ollama_num_ctx); + ollama = new Ollama({ + host: ollama_host, + model: ollama_model, + stream: true, + num_ctx: ollama_num_ctx + }); do_debug = event.data.do_debug; i18nStrings = event.data.i18nStrings; taLog = new taLogger('model-worker-ollama', do_debug); @@ -138,4 +143,4 @@ self.onmessage = async function(event) { stopStreaming = true; break; //stop } -}; \ No newline at end of file +}; diff --git a/js/workers/model-worker-openai.js b/js/workers/model-worker-openai.js index 61e4bc22..f09d06d4 100644 --- a/js/workers/model-worker-openai.js +++ b/js/workers/model-worker-openai.js @@ -38,7 +38,13 @@ self.onmessage = async function(event) { if (event.data.type === 'init') { chatgpt_api_key = event.data.chatgpt_api_key; chatgpt_model = event.data.chatgpt_model; - openai = new OpenAI(chatgpt_api_key, chatgpt_model, event.data.chatgpt_developer_messages, true, event.data.chatgpt_api_store); + openai = new OpenAI({ + apiKey: chatgpt_api_key, + model: chatgpt_model, + developer_messages: event.data.chatgpt_developer_messages, + stream: true, + store: event.data.chatgpt_api_store + }); do_debug = event.data.do_debug; i18nStrings = event.data.i18nStrings; taLog = new taLogger('model-worker-openai', do_debug); diff --git a/js/workers/model-worker-openai_comp.js b/js/workers/model-worker-openai_comp.js index 4882800f..26346caf 100644 --- a/js/workers/model-worker-openai_comp.js +++ b/js/workers/model-worker-openai_comp.js @@ -42,7 +42,13 @@ self.onmessage = async function(event) { openai_comp_model = event.data.openai_comp_model; openai_comp_api_key = event.data.openai_comp_api_key; openai_comp_use_v1 = event.data.openai_comp_use_v1; - openai_comp = new OpenAIComp(openai_comp_host, openai_comp_model, openai_comp_api_key, true, openai_comp_use_v1); + openai_comp = new OpenAIComp({ + host: openai_comp_host, + model: openai_comp_model, + apiKey: openai_comp_api_key, + stream: true, + use_v1: openai_comp_use_v1 + }); do_debug = event.data.do_debug; i18nStrings = event.data.i18nStrings; taLog = new taLogger('model-worker-openai_comp', do_debug); diff --git a/mzta-background.js b/mzta-background.js index 4046b52a..c8ca8bfe 100644 --- a/mzta-background.js +++ b/mzta-background.js @@ -20,7 +20,36 @@ 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, + contextMenuIconsPath, + 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 +85,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 +176,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") @@ -414,7 +445,7 @@ async function openChatGPT(promptText, action, curr_tabId, prompt_name = '', do_ if((originalText == null) || (originalText == "")) { originalText = prompt_info.body_text; } - let reply_type_pref = await browser.storage.sync.get({reply_type: 'reply_all'}); + let reply_type_pref = await browser.storage.sync.get({ reply_type: prefs_default.reply_type }); //console.log(">>>>>>>>>> prompt_info: " + JSON.stringify(prompt_info)); let pre_script = `let mztaWinId = `+ createdTab.windowId +`; let mztaStatusPageDesc="`+ browser.i18n.getMessage("prefs_status_page") +`"; @@ -430,6 +461,7 @@ async function openChatGPT(promptText, action, curr_tabId, prompt_name = '', do_ let mztaReplyType="`+ reply_type_pref.reply_type + `"; `; + taLog.log("pre_script: " + pre_script); taLog.log("Waiting " + _wait_time + " millisec"); await new Promise(resolve => setTimeout(resolve, _wait_time)); taLog.log("Waiting " + _wait_time + " millisec done"); @@ -492,7 +524,7 @@ async function openChatGPT(promptText, action, curr_tabId, prompt_name = '', do_ browser.runtime.onMessage.addListener(listener2); let win_options2 = { - url: browser.runtime.getURL('api_webchat/index.html?llm='+prefs.connection_type+'&call_id='+rand_call_id5+'&ph_def_val='+(prefs.placeholders_use_default_value?'1':'0')+'&prompt_id='+encodeURIComponent(prompt_info.id) + '&prompt_name=' + encodeURIComponent(i18nConditionalGet(prompt_info.name))), + url: browser.runtime.getURL('api_webchat/index.html?llm='+prefs.connection_type+'&call_id='+rand_call_id2+'&ph_def_val='+(prefs.placeholders_use_default_value?'1':'0')+'&prompt_id='+encodeURIComponent(prompt_info.id) + '&prompt_name=' + encodeURIComponent(i18nConditionalGet(prompt_info.name))), type: "popup", } @@ -601,7 +633,7 @@ async function openChatGPT(promptText, action, curr_tabId, prompt_name = '', do_ browser.runtime.onMessage.addListener(listener3); let win_options3 = { - url: browser.runtime.getURL('api_webchat/index.html?llm='+prefs.connection_type+'&call_id='+rand_call_id5+'&ph_def_val='+(prefs.placeholders_use_default_value?'1':'0')+'&prompt_id='+encodeURIComponent(prompt_info.id) + '&prompt_name=' + encodeURIComponent(i18nConditionalGet(prompt_info.name))), + url: browser.runtime.getURL('api_webchat/index.html?llm='+prefs.connection_type+'&call_id='+rand_call_id3+'&ph_def_val='+(prefs.placeholders_use_default_value?'1':'0')+'&prompt_id='+encodeURIComponent(prompt_info.id) + '&prompt_name=' + encodeURIComponent(i18nConditionalGet(prompt_info.name))), type: "popup", } @@ -654,7 +686,7 @@ async function openChatGPT(promptText, action, curr_tabId, prompt_name = '', do_ browser.runtime.onMessage.addListener(listener4); let win_options4 = { - url: browser.runtime.getURL('api_webchat/index.html?llm='+prefs.connection_type+'&call_id='+rand_call_id5+'&ph_def_val='+(prefs.placeholders_use_default_value?'1':'0')+'&prompt_id='+encodeURIComponent(prompt_info.id) + '&prompt_name=' + encodeURIComponent(i18nConditionalGet(prompt_info.name))), + url: browser.runtime.getURL('api_webchat/index.html?llm='+prefs.connection_type+'&call_id='+rand_call_id4+'&ph_def_val='+(prefs.placeholders_use_default_value?'1':'0')+'&prompt_id='+encodeURIComponent(prompt_info.id) + '&prompt_name=' + encodeURIComponent(i18nConditionalGet(prompt_info.name))), type: "popup", } @@ -749,7 +781,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 +817,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 +832,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 +847,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 +862,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") @@ -818,14 +872,8 @@ function setupStorageChangeListener() { if(newConnectionType === "chatgpt_web"){ removeContextMenu(contextMenuID_AddTags); removeContextMenu(contextMenuID_Spamfilter); - }else{ - if(prefs_init.add_tags && prefs_init.add_tags_context_menu){ - addContextMenu(contextMenuID_AddTags); - } - if(prefs_init.spamfilter && prefs_init.spamfilter_context_menu){ - addContextMenu(contextMenuID_Spamfilter); - } } + addContextMenuItems(); } // context menu changes for add_tags and spamfilter @@ -895,12 +943,15 @@ 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), - contexts: ["message_list"] + contexts: ["message_list"], + icons: contextMenuIconsPath[menu_id], }); taLog.log("Context menu added: " + menu_id); + console.log(">>>>>>> contextMenuIconsPath[menu_id]: " + contextMenuIconsPath[menu_id]); } function removeContextMenu(menu_id) { @@ -908,16 +959,19 @@ function removeContextMenu(menu_id) { taLog.log("Context menu removed: " + menu_id); } -// Add Context menu: Add tags -if(prefs_init.add_tags && prefs_init.add_tags_context_menu && (prefs_init.connection_type !== "chatgpt_web")){ - addContextMenu(contextMenuID_AddTags); -} - -// Add Context menu: Spamfilter -if(prefs_init.spamfilter && prefs_init.spamfilter_context_menu && (prefs_init.connection_type !== "chatgpt_web")){ - addContextMenu(contextMenuID_Spamfilter); +function addContextMenuItems() { + // Add Context menu: Add tags + 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")||checkSpecificIntegration(prefs_init.spamfilter_use_specific_integration,prefs_init.spamfilter_connection_type))){ + addContextMenu(contextMenuID_Spamfilter); + } } +addContextMenuItems(); // Listen for context menu item clicks browser.menus.onClicked.addListener( (info, tab) => { @@ -976,6 +1030,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 +1062,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 +1075,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 { @@ -1040,7 +1103,7 @@ async function processEmails(messages, addTagsAuto, spamFilter) { } } let curr_prompt_spamfilter = await getSpamFilterPrompt(); - // console.log(">>>>>>>>>>>>> curr_prompt_spamfilter: " + curr_prompt_spamfilter); + // console.log(">>>>>>>>>>>>> curr_prompt_spamfilter: " + JSON.stringify(curr_prompt_spamfilter)); let chatgpt_lang = await taPromptUtils.getDefaultLang(curr_prompt_spamfilter); let specialFullPrompt_spamfilter = await taPromptUtils.preparePrompt({ curr_prompt: curr_prompt_spamfilter, @@ -1052,7 +1115,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 e1aeb908..6efc2cfa 100644 --- a/options/mzta-options-default.js +++ b/options/mzta-options-default.js @@ -45,6 +45,7 @@ export const prefs_default = { google_gemini_api_key: '', google_gemini_model: '', google_gemini_system_instruction: '', + google_gemini_thinking_budget: '', anthropic_api_key: '', anthropic_model: '', anthropic_version: '2023-06-01', @@ -66,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, @@ -74,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 13604510..92d2989a 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,330 +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(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(); - }); - - // 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(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(); - }); - - // 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(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(); - }); - - - // 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(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(); - }); - - // Anthropic API model fetching - let select_anthropic_model = document.getElementById('anthropic_model'); - const anthropic_option = document.createElement('option'); - anthropic_option.value = prefs.anthropic_model; - anthropic_option.text = prefs.anthropic_model; - select_anthropic_model.appendChild(anthropic_option); - select_anthropic_model.addEventListener("change", warn_Anthropic_APIKeyEmpty); - select_anthropic_model.addEventListener("change", warn_Anthropic_VersionEmpty); - - document.getElementById('btnUpdateAnthropicModels').addEventListener('click', async () => { - document.getElementById('anthropic_model_fetch_loading').style.display = 'inline'; - let anthropic = new Anthropic(document.getElementById("anthropic_api_key").value, document.getElementById("anthropic_version").value, ''); - 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); @@ -876,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 ce725495..854a9bff 100644 --- a/options/mzta-release-notes.html +++ b/options/mzta-release-notes.html @@ -12,6 +12,8 @@
  • Added the {%mail_attachments_info%} placeholder to retrieve the name, type and file size of the mail attachments [#446].
  • [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.
  • [All APIs] It's now possibile to define a list of tags to be used when autotagging received emails [#436]. The tags are are now shown in the information header in the AI API chat [#289].
  • [OpenAI Comp API] Added DeepSeek configuration [#486].
  • ...
  • 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..2513d118 --- /dev/null +++ b/pages/_lib/connection-ui.js @@ -0,0 +1,1120 @@ +/* + * 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 { prefs_default } from '../../options/mzta-options-default.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 { + 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({ + apiKey: document.getElementById("chatgpt_api_key").value, + }); + 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({ + apiKey: document.getElementById("google_gemini_api_key").value, + }); + 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({ + host: document.getElementById("ollama_host").value, + }); + 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({ + host: document.getElementById("openai_comp_host").value, + apiKey: document.getElementById("openai_comp_api_key").value, + 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(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({ + 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(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); + }); +} 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 d0d45231..b6c35fd6 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__