| @@ -408,6 +408,8 @@ jQuery(async function () { | |||
| 408 | // Handle multimodal sources | 408 | // Handle multimodal sources |
| 409 | if (settings.source === 'multimodal') { | 409 | if (settings.source === 'multimodal') { |
| 410 | const api = settings.multimodal_api; | 410 | const api = settings.multimodal_api; |
| 411 | const altEndpointEnabled = settings.alt_endpoint_enabled; | ||
| 412 | const altEndpointUrl = settings.alt_endpoint_url; | ||
| 411 | 413 | ||
| 412 | // APIs that support reverse proxy | 414 | // APIs that support reverse proxy |
| 413 | const reverseProxyApis = { | 415 | const reverseProxyApis = { |
| @@ -444,7 +446,11 @@ jQuery(async function () { | |||
| 444 | 'vllm': textgen_types.VLLM, | 446 | 'vllm': textgen_types.VLLM, |
| 445 | }; | 447 | }; |
| 446 | 448 | ||
| 447 | if (textCompletionApis[api] && textgenerationwebui_settings.server_urls[textCompletionApis[api]]) { | 449 | if (textCompletionApis[api] && altEndpointEnabled && altEndpointUrl) { |
| 450 | return true; | ||
| 451 | } | ||
| 452 | |||
| 453 | if (textCompletionApis[api] && !altEndpointEnabled && textgenerationwebui_settings.server_urls[textCompletionApis[api]]) { | ||
| 448 | return true; | 454 | return true; |
| 449 | } | 455 | } |
| 450 | 456 | ||
| @@ -580,6 +586,14 @@ jQuery(async function () { | |||
| 580 | extension_settings.caption.multimodal_model = String($('#caption_multimodal_model').val()); | 586 | extension_settings.caption.multimodal_model = String($('#caption_multimodal_model').val()); |
| 581 | saveSettingsDebounced(); | 587 | saveSettingsDebounced(); |
| 582 | }); | 588 | }); |
| 589 | $('#caption_altEndpoint_url').val(extension_settings.caption.alt_endpoint_url).on('input', () => { | ||
| 590 | extension_settings.caption.alt_endpoint_url = String($('#caption_altEndpoint_url').val()); | ||
| 591 | saveSettingsDebounced(); | ||
| 592 | }); | ||
| 593 | $('#caption_altEndpoint_enabled').prop('checked', !!(extension_settings.caption.alt_endpoint_enabled)).on('input', () => { | ||
| 594 | extension_settings.caption.alt_endpoint_enabled = !!$('#caption_altEndpoint_enabled').prop('checked'); | ||
| 595 | saveSettingsDebounced(); | ||
| 596 | }); | ||
| 583 | 597 | ||
| 584 | const onMessageEvent = async (index) => { | 598 | const onMessageEvent = async (index) => { |
| 585 | if (!extension_settings.caption.auto_mode) { | 599 | if (!extension_settings.caption.auto_mode) { |
| @@ -189,6 +189,16 @@ | |||
| 189 | <small><b data-i18n="Hint:">Hint:</b> <span data-i18n="Set your API keys and endpoints in the 'API Connections' tab first.">Set your API keys and endpoints in the 'API Connections' tab first.</span></small> | 189 | <small><b data-i18n="Hint:">Hint:</b> <span data-i18n="Set your API keys and endpoints in the 'API Connections' tab first.">Set your API keys and endpoints in the 'API Connections' tab first.</span></small> |
| 190 | </div> | 190 | </div> |
| 191 | </div> | 191 | </div> |
| 192 | <div data-type="koboldcpp,ollama,vllm,llamacpp,ooba" class="flex-container flexFlowColumn"> | ||
| 193 | <label for="caption_altEndpoint_enabled" class="checkbox_label"> | ||
| 194 | <input id="caption_altEndpoint_enabled" type="checkbox"> | ||
| 195 | <span data-i18n="Use secondary URL">Use secondary URL</span> | ||
| 196 | </label> | ||
| 197 | <label for="caption_altEndpoint_url" data-i18n="Secondary captioning endpoint URL"> | ||
| 198 | Secondary captioning endpoint URL | ||
| 199 | </label> | ||
| 200 | <input id="caption_altEndpoint_url" class="text_pole" type="text" placeholder="e.g. http://localhost:5001" /> | ||
| 201 | </div> | ||
| 192 | <div id="caption_prompt_block"> | 202 | <div id="caption_prompt_block"> |
| 193 | <label for="caption_prompt" data-i18n="Caption Prompt">Caption Prompt</label> | 203 | <label for="caption_prompt" data-i18n="Caption Prompt">Caption Prompt</label> |
| 194 | <textarea id="caption_prompt" class="text_pole" rows="1" placeholder="< Use default >">{{PROMPT_DEFAULT}}</textarea> | 204 | <textarea id="caption_prompt" class="text_pole" rows="1" placeholder="< Use default >">{{PROMPT_DEFAULT}}</textarea> |
| @@ -61,7 +61,9 @@ export async function getMultimodalCaption(base64Img, prompt) { | |||
| 61 | requestBody.model = textgenerationwebui_settings.ollama_model; | 61 | requestBody.model = textgenerationwebui_settings.ollama_model; |
| 62 | } | 62 | } |
| 63 | 63 | ||
| 64 | requestBody.server_url = textgenerationwebui_settings.server_urls[textgen_types.OLLAMA]; | 64 | requestBody.server_url = extension_settings.caption.alt_endpoint_enabled |
| 65 | ? extension_settings.caption.alt_endpoint_url | ||
| 66 | : textgenerationwebui_settings.server_urls[textgen_types.OLLAMA]; | ||
| 65 | } | 67 | } |
| 66 | 68 | ||
| 67 | if (isVllm) { | 69 | if (isVllm) { |
| @@ -69,19 +71,27 @@ export async function getMultimodalCaption(base64Img, prompt) { | |||
| 69 | requestBody.model = textgenerationwebui_settings.vllm_model; | 71 | requestBody.model = textgenerationwebui_settings.vllm_model; |
| 70 | } | 72 | } |
| 71 | 73 | ||
| 72 | requestBody.server_url = textgenerationwebui_settings.server_urls[textgen_types.VLLM]; | 74 | requestBody.server_url = extension_settings.caption.alt_endpoint_enabled |
| 75 | ? extension_settings.caption.alt_endpoint_url | ||
| 76 | : textgenerationwebui_settings.server_urls[textgen_types.VLLM]; | ||
| 73 | } | 77 | } |
| 74 | 78 | ||
| 75 | if (isLlamaCpp) { | 79 | if (isLlamaCpp) { |
| 76 | requestBody.server_url = textgenerationwebui_settings.server_urls[textgen_types.LLAMACPP]; | 80 | requestBody.server_url = extension_settings.caption.alt_endpoint_enabled |
| 81 | ? extension_settings.caption.alt_endpoint_url | ||
| 82 | : textgenerationwebui_settings.server_urls[textgen_types.LLAMACPP]; | ||
| 77 | } | 83 | } |
| 78 | 84 | ||
| 79 | if (isOoba) { | 85 | if (isOoba) { |
| 80 | requestBody.server_url = textgenerationwebui_settings.server_urls[textgen_types.OOBA]; | 86 | requestBody.server_url = extension_settings.caption.alt_endpoint_enabled |
| 87 | ? extension_settings.caption.alt_endpoint_url | ||
| 88 | : textgenerationwebui_settings.server_urls[textgen_types.OOBA]; | ||
| 81 | } | 89 | } |
| 82 | 90 | ||
| 83 | if (isKoboldCpp) { | 91 | if (isKoboldCpp) { |
| 84 | requestBody.server_url = textgenerationwebui_settings.server_urls[textgen_types.KOBOLDCPP]; | 92 | requestBody.server_url = extension_settings.caption.alt_endpoint_enabled |
| 93 | ? extension_settings.caption.alt_endpoint_url | ||
| 94 | : textgenerationwebui_settings.server_urls[textgen_types.KOBOLDCPP]; | ||
| 85 | } | 95 | } |
| 86 | 96 | ||
| 87 | if (isCustom) { | 97 | if (isCustom) { |
| @@ -121,75 +131,84 @@ export async function getMultimodalCaption(base64Img, prompt) { | |||
| 121 | } | 131 | } |
| 122 | 132 | ||
| 123 | function throwIfInvalidModel(useReverseProxy) { | 133 | function throwIfInvalidModel(useReverseProxy) { |
| 124 | if (extension_settings.caption.multimodal_api === 'openai' && !secret_state[SECRET_KEYS.OPENAI] && !useReverseProxy) { | 134 | const altEndpointEnabled = extension_settings.caption.alt_endpoint_enabled; |
| 135 | const altEndpointUrl = extension_settings.caption.alt_endpoint_url; | ||
| 136 | const multimodalModel = extension_settings.caption.multimodal_model; | ||
| 137 | const multimodalApi = extension_settings.caption.multimodal_api; | ||
| 138 | |||
| 139 | if (altEndpointEnabled && ['llamacpp', 'ooba', 'koboldcpp', 'vllm', 'ollama'].includes(multimodalApi) && !altEndpointUrl) { | ||
| 140 | throw new Error('Secondary endpoint URL is not set.'); | ||
| 141 | } | ||
| 142 | |||
| 143 | if (multimodalApi === 'openai' && !secret_state[SECRET_KEYS.OPENAI] && !useReverseProxy) { | ||
| 125 | throw new Error('OpenAI API key is not set.'); | 144 | throw new Error('OpenAI API key is not set.'); |
| 126 | } | 145 | } |
| 127 | 146 | ||
| 128 | if (extension_settings.caption.multimodal_api === 'openrouter' && !secret_state[SECRET_KEYS.OPENROUTER]) { | 147 | if (multimodalApi === 'openrouter' && !secret_state[SECRET_KEYS.OPENROUTER]) { |
| 129 | throw new Error('OpenRouter API key is not set.'); | 148 | throw new Error('OpenRouter API key is not set.'); |
| 130 | } | 149 | } |
| 131 | 150 | ||
| 132 | if (extension_settings.caption.multimodal_api === 'anthropic' && !secret_state[SECRET_KEYS.CLAUDE] && !useReverseProxy) { | 151 | if (multimodalApi === 'anthropic' && !secret_state[SECRET_KEYS.CLAUDE] && !useReverseProxy) { |
| 133 | throw new Error('Anthropic (Claude) API key is not set.'); | 152 | throw new Error('Anthropic (Claude) API key is not set.'); |
| 134 | } | 153 | } |
| 135 | 154 | ||
| 136 | if (extension_settings.caption.multimodal_api === 'zerooneai' && !secret_state[SECRET_KEYS.ZEROONEAI]) { | 155 | if (multimodalApi === 'zerooneai' && !secret_state[SECRET_KEYS.ZEROONEAI]) { |
| 137 | throw new Error('01.AI API key is not set.'); | 156 | throw new Error('01.AI API key is not set.'); |
| 138 | } | 157 | } |
| 139 | 158 | ||
| 140 | if (extension_settings.caption.multimodal_api === 'groq' && !secret_state[SECRET_KEYS.GROQ]) { | 159 | if (multimodalApi === 'groq' && !secret_state[SECRET_KEYS.GROQ]) { |
| 141 | throw new Error('Groq API key is not set.'); | 160 | throw new Error('Groq API key is not set.'); |
| 142 | } | 161 | } |
| 143 | 162 | ||
| 144 | if (extension_settings.caption.multimodal_api === 'google' && !secret_state[SECRET_KEYS.MAKERSUITE] && !useReverseProxy) { | 163 | if (multimodalApi === 'google' && !secret_state[SECRET_KEYS.MAKERSUITE] && !useReverseProxy) { |
| 145 | throw new Error('Google AI Studio API key is not set.'); | 164 | throw new Error('Google AI Studio API key is not set.'); |
| 146 | } | 165 | } |
| 147 | 166 | ||
| 148 | if (extension_settings.caption.multimodal_api === 'vertexai' && !secret_state[SECRET_KEYS.VERTEXAI] && !useReverseProxy) { | 167 | if (multimodalApi === 'vertexai' && !secret_state[SECRET_KEYS.VERTEXAI] && !useReverseProxy) { |
| 149 | throw new Error('Google Vertex AI API key is not set.'); | 168 | throw new Error('Google Vertex AI API key is not set.'); |
| 150 | } | 169 | } |
| 151 | 170 | ||
| 152 | if (extension_settings.caption.multimodal_api === 'mistral' && !secret_state[SECRET_KEYS.MISTRALAI] && !useReverseProxy) { | 171 | if (multimodalApi === 'mistral' && !secret_state[SECRET_KEYS.MISTRALAI] && !useReverseProxy) { |
| 153 | throw new Error('Mistral AI API key is not set.'); | 172 | throw new Error('Mistral AI API key is not set.'); |
| 154 | } | 173 | } |
| 155 | 174 | ||
| 156 | if (extension_settings.caption.multimodal_api === 'cohere' && !secret_state[SECRET_KEYS.COHERE]) { | 175 | if (multimodalApi === 'cohere' && !secret_state[SECRET_KEYS.COHERE]) { |
| 157 | throw new Error('Cohere API key is not set.'); | 176 | throw new Error('Cohere API key is not set.'); |
| 158 | } | 177 | } |
| 159 | 178 | ||
| 160 | if (extension_settings.caption.multimodal_api === 'xai' && !secret_state[SECRET_KEYS.XAI] && !useReverseProxy) { | 179 | if (multimodalApi === 'xai' && !secret_state[SECRET_KEYS.XAI] && !useReverseProxy) { |
| 161 | throw new Error('xAI API key is not set.'); | 180 | throw new Error('xAI API key is not set.'); |
| 162 | } | 181 | } |
| 163 | 182 | ||
| 164 | if (extension_settings.caption.multimodal_api === 'ollama' && !textgenerationwebui_settings.server_urls[textgen_types.OLLAMA]) { | 183 | if (multimodalApi === 'ollama' && !textgenerationwebui_settings.server_urls[textgen_types.OLLAMA] && !altEndpointEnabled) { |
| 165 | throw new Error('Ollama server URL is not set.'); | 184 | throw new Error('Ollama server URL is not set.'); |
| 166 | } | 185 | } |
| 167 | 186 | ||
| 168 | if (extension_settings.caption.multimodal_api === 'ollama' && extension_settings.caption.multimodal_model === 'ollama_current' && !textgenerationwebui_settings.ollama_model) { | 187 | if (multimodalApi === 'ollama' && multimodalModel === 'ollama_current' && !textgenerationwebui_settings.ollama_model) { |
| 169 | throw new Error('Ollama model is not set.'); | 188 | throw new Error('Ollama model is not set.'); |
| 170 | } | 189 | } |
| 171 | 190 | ||
| 172 | if (extension_settings.caption.multimodal_api === 'llamacpp' && !textgenerationwebui_settings.server_urls[textgen_types.LLAMACPP]) { | 191 | if (multimodalApi === 'llamacpp' && !textgenerationwebui_settings.server_urls[textgen_types.LLAMACPP] && !altEndpointEnabled) { |
| 173 | throw new Error('LlamaCPP server URL is not set.'); | 192 | throw new Error('LlamaCPP server URL is not set.'); |
| 174 | } | 193 | } |
| 175 | 194 | ||
| 176 | if (extension_settings.caption.multimodal_api === 'ooba' && !textgenerationwebui_settings.server_urls[textgen_types.OOBA]) { | 195 | if (multimodalApi === 'ooba' && !textgenerationwebui_settings.server_urls[textgen_types.OOBA] && !altEndpointEnabled) { |
| 177 | throw new Error('Text Generation WebUI server URL is not set.'); | 196 | throw new Error('Text Generation WebUI server URL is not set.'); |
| 178 | } | 197 | } |
| 179 | 198 | ||
| 180 | if (extension_settings.caption.multimodal_api === 'koboldcpp' && !textgenerationwebui_settings.server_urls[textgen_types.KOBOLDCPP]) { | 199 | if (multimodalApi === 'koboldcpp' && !textgenerationwebui_settings.server_urls[textgen_types.KOBOLDCPP] && !altEndpointEnabled) { |
| 181 | throw new Error('KoboldCpp server URL is not set.'); | 200 | throw new Error('KoboldCpp server URL is not set.'); |
| 182 | } | 201 | } |
| 183 | 202 | ||
| 184 | if (extension_settings.caption.multimodal_api === 'vllm' && !textgenerationwebui_settings.server_urls[textgen_types.VLLM]) { | 203 | if (multimodalApi === 'vllm' && !textgenerationwebui_settings.server_urls[textgen_types.VLLM] && !altEndpointEnabled) { |
| 185 | throw new Error('vLLM server URL is not set.'); | 204 | throw new Error('vLLM server URL is not set.'); |
| 186 | } | 205 | } |
| 187 | 206 | ||
| 188 | if (extension_settings.caption.multimodal_api === 'vllm' && extension_settings.caption.multimodal_model === 'vllm_current' && !textgenerationwebui_settings.vllm_model) { | 207 | if (multimodalApi === 'vllm' && multimodalModel === 'vllm_current' && !textgenerationwebui_settings.vllm_model) { |
| 189 | throw new Error('vLLM model is not set.'); | 208 | throw new Error('vLLM model is not set.'); |
| 190 | } | 209 | } |
| 191 | 210 | ||
| 192 | if (extension_settings.caption.multimodal_api === 'custom' && !oai_settings.custom_url) { | 211 | if (multimodalApi === 'custom' && !oai_settings.custom_url) { |
| 193 | throw new Error('Custom API URL is not set.'); | 212 | throw new Error('Custom API URL is not set.'); |
| 194 | } | 213 | } |
| 195 | } | 214 | } |
| @@ -151,8 +151,11 @@ router.post('/caption-image', async (request, response) => { | |||
| 151 | apiUrl = 'https://text.pollinations.ai/openai/chat/completions'; | 151 | apiUrl = 'https://text.pollinations.ai/openai/chat/completions'; |
| 152 | } | 152 | } |
| 153 | 153 | ||
| 154 | if (request.body.api === 'ooba') { | 154 | if (['koboldcpp', 'vllm', 'llamacpp', 'ooba'].includes(request.body.api)) { |
| 155 | apiUrl = `${trimV1(request.body.server_url)}/v1/chat/completions`; | 155 | apiUrl = `${trimV1(request.body.server_url)}/v1/chat/completions`; |
| 156 | } | ||
| 157 | |||
| 158 | if (request.body.api === 'ooba') { | ||
| 156 | const imgMessage = body.messages.pop(); | 159 | const imgMessage = body.messages.pop(); |
| 157 | body.messages.push({ | 160 | body.messages.push({ |
| 158 | role: 'user', | 161 | role: 'user', |
| @@ -165,10 +168,6 @@ router.post('/caption-image', async (request, response) => { | |||
| 165 | }); | 168 | }); |
| 166 | } | 169 | } |
| 167 | 170 | ||
| 168 | if (['koboldcpp', 'vllm', 'llamacpp'].includes(request.body.api)) { | ||
| 169 | apiUrl = `${trimV1(request.body.server_url)}/v1/chat/completions`; | ||
| 170 | } | ||
| 171 | |||
| 172 | setAdditionalHeaders(request, { headers }, apiUrl); | 171 | setAdditionalHeaders(request, { headers }, apiUrl); |
| 173 | console.debug('Multimodal captioning request', body); | 172 | console.debug('Multimodal captioning request', body); |
| 174 | 173 | ||