MistralAI: Add Pixtral to models and captioning

60df924bec5b32a6c089c6c19764d41a1e9ef14b

Cohee <18619528+Cohee1207@users.noreply.github.com>

6 files changed, +29 -4Showing whitespace changes
public/index.html+3 -0
@@ -2896,6 +2896,7 @@
28962896 <option value="mistral-large-latest">mistral-large-latest</option>
28972897 <option value="codestral-latest">codestral-latest</option>
28982898 <option value="codestral-mamba-latest">codestral-mamba-latest</option>
2899+ <option value="pixtral-latest">pixtral-latest</option>
28992900 </optgroup>
29002901 <optgroup label="Sub-versions">
29012902 <option value="open-mistral-nemo-2407">open-mistral-nemo-2407</option>
@@ -2903,11 +2904,13 @@
29032904 <option value="mistral-tiny-2312">mistral-tiny-2312</option>
29042905 <option value="mistral-small-2312">mistral-small-2312</option>
29052906 <option value="mistral-small-2402">mistral-small-2402</option>
2907+ <option value="mistral-small-2409">mistral-small-2409</option>
29062908 <option value="mistral-medium-2312">mistral-medium-2312</option>
29072909 <option value="mistral-large-2402">mistral-large-2402</option>
29082910 <option value="mistral-large-2407">mistral-large-2407</option>
29092911 <option value="codestral-2405">codestral-2405</option>
29102912 <option value="codestral-mamba-2407">codestral-mamba-2407</option>
2913+ <option value="pixtral-12b-2409">pixtral-12b-2409</option>
29112914 </optgroup>
29122915 </select>
29132916 </div>
public/scripts/extensions/caption/index.js+1 -0
@@ -403,6 +403,7 @@ jQuery(async function () {
403403 (extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'openai' && (secret_state[SECRET_KEYS.OPENAI] || extension_settings.caption.allow_reverse_proxy)) ||
404404 (extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'openrouter' && secret_state[SECRET_KEYS.OPENROUTER]) ||
405405 (extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'zerooneai' && secret_state[SECRET_KEYS.ZEROONEAI]) ||
406+ (extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'mistral' && (secret_state[SECRET_KEYS.MISTRALAI] || extension_settings.caption.allow_reverse_proxy)) ||
406407 (extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'google' && (secret_state[SECRET_KEYS.MAKERSUITE] || extension_settings.caption.allow_reverse_proxy)) ||
407408 (extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'anthropic' && (secret_state[SECRET_KEYS.CLAUDE] || extension_settings.caption.allow_reverse_proxy)) ||
408409 (extension_settings.caption.source === 'multimodal' && extension_settings.caption.multimodal_api === 'ollama' && textgenerationwebui_settings.server_urls[textgen_types.OLLAMA]) ||
public/scripts/extensions/caption/settings.html+4 -1
@@ -23,6 +23,7 @@
2323 <option value="google">Google AI Studio</option>
2424 <option value="koboldcpp">KoboldCpp</option>
2525 <option value="llamacpp">llama.cpp</option>
26+ <option value="mistral">MistralAI</option>
2627 <option value="ollama">Ollama</option>
2728 <option value="openai">OpenAI</option>
2829 <option value="openrouter">OpenRouter</option>
@@ -33,6 +34,8 @@
3334 <div class="flex1 flex-container flexFlowColumn flexNoGap">
3435 <label for="caption_multimodal_model" data-i18n="Model">Model</label>
3536 <select id="caption_multimodal_model" class="flex1 text_pole">
37+ <option data-type="mistral" value="pixtral-latest">pixtral-latest</option>
38+ <option data-type="mistral" value="pixtral-12b-2409">pixtral-12b-2409</option>
3639 <option data-type="zerooneai" value="yi-vision">yi-vision</option>
3740 <option data-type="openai" value="gpt-4-vision-preview">gpt-4-vision-preview</option>
3841 <option data-type="openai" value="gpt-4-turbo">gpt-4-turbo</option>
@@ -96,7 +99,7 @@
9699 <div data-type="ollama">
97100 The model must be downloaded first! Do it with the <code>ollama pull</code> command or <a href="#" id="caption_ollama_pull">click here</a>.
98101 </div>
99102 <label data-type="openai,anthropic,google,mistral" class="checkbox_label flexBasis100p" for="caption_allow_reverse_proxy" title="Allow using reverse proxy if defined and valid.">
100103 <input id="caption_allow_reverse_proxy" type="checkbox" class="checkbox">
101104 <span data-i18n="Allow reverse proxy">Allow reverse proxy</span>
102105 </label>
public/scripts/extensions/shared.js+6 -2
@@ -13,7 +13,7 @@ import { createThumbnail, isValidUrl } from '../utils.js';
1313 */
1414export async function getMultimodalCaption(base64Img, prompt) {
1515 const useReverseProxy =
1616 (['openai', 'anthropic', 'google', 'mistral'].includes(extension_settings.caption.multimodal_api))
1717 && extension_settings.caption.allow_reverse_proxy
1818 && oai_settings.reverse_proxy
1919 && isValidUrl(oai_settings.reverse_proxy);
@@ -36,7 +36,7 @@ export async function getMultimodalCaption(base64Img, prompt) {
3636 const isVllm = extension_settings.caption.multimodal_api === 'vllm';
3737 const base64Bytes = base64Img.length * 0.75;
3838 const compressionLimit = 2 * 1024 * 1024;
3939 if ((['google', 'openrouter', 'mistral'].includes(extension_settings.caption.multimodal_api) && base64Bytes > compressionLimit) || isOoba || isKoboldCpp) {
4040 const maxSide = 1024;
4141 base64Img = await createThumbnail(base64Img, maxSide, maxSide, 'image/jpeg');
4242 }
@@ -139,6 +139,10 @@ function throwIfInvalidModel(useReverseProxy) {
139139 throw new Error('Google AI Studio API key is not set.');
140140 }
141141
142+ if (extension_settings.caption.multi_modal_api === 'mistral' && !secret_state[SECRET_KEYS.MISTRALAI] && !useReverseProxy) {
143+ throw new Error('Mistral AI API key is not set.');
144+ }
145+
142146 if (extension_settings.caption.multimodal_api === 'ollama' && !textgenerationwebui_settings.server_urls[textgen_types.OLLAMA]) {
143147 throw new Error('Ollama server URL is not set.');
144148 }
public/scripts/openai.js+7 -1
@@ -2490,7 +2490,7 @@ class Message {
24902490 * @returns {Promise<string>} Compressed image as a Data URL.
24912491 */
24922492 async compressImage(image) {
24932493 if ([chat_completion_sources.OPENROUTER, chat_completion_sources.MAKERSUITE, chat_completion_sources.MISTRALAI].includes(oai_settings.chat_completion_source)) {
24942494 const sizeThreshold = 2 * 1024 * 1024;
24952495 const dataSize = image.length * 0.75;
24962496 const maxSide = 1024;
@@ -4221,6 +4221,8 @@ async function onModelChange() {
42214221 $('#openai_max_context').attr('max', max_128k);
42224222 } else if (oai_settings.mistralai_model.includes('mixtral-8x22b')) {
42234223 $('#openai_max_context').attr('max', max_64k);
4224+ } else if (oai_settings.mistralai_model.includes('pixtral')) {
4225+ $('#openai_max_context').attr('max', max_128k);
42244226 } else {
42254227 $('#openai_max_context').attr('max', max_32k);
42264228 }
@@ -4770,6 +4772,8 @@ export function isImageInliningSupported() {
47704772 'gpt-4o-mini',
47714773 'chatgpt-4o-latest',
47724774 'yi-vision',
4775+ 'pixtral-latest',
4776+ 'pixtral-12b-2409',
47734777 ];
47744778
47754779 switch (oai_settings.chat_completion_source) {
@@ -4785,6 +4789,8 @@ export function isImageInliningSupported() {
47854789 return true;
47864790 case chat_completion_sources.ZEROONEAI:
47874791 return visionSupportedModels.some(model => oai_settings.zerooneai_model.includes(model));
4792+ case chat_completion_sources.MISTRALAI:
4793+ return visionSupportedModels.some(model => oai_settings.mistralai_model.includes(model));
47884794 default:
47894795 return false;
47904796 }
src/endpoints/openai.js+8 -0
@@ -51,6 +51,10 @@ router.post('/caption-image', jsonParser, async (request, response) => {
5151 key = readSecret(request.user.directories, SECRET_KEYS.ZEROONEAI);
5252 }
5353
54+ if (request.body.api === 'mistral') {
55+ key = readSecret(request.user.directories, SECRET_KEYS.MISTRALAI);
56+ }
57+
5458 if (!key && !request.body.reverse_proxy && ['custom', 'ooba', 'koboldcpp', 'vllm'].includes(request.body.api) === false) {
5559 console.log('No key found for API', request.body.api);
5660 return response.sendStatus(400);
@@ -107,6 +111,10 @@ router.post('/caption-image', jsonParser, async (request, response) => {
107111 apiUrl = 'https://api.01.ai/v1/chat/completions';
108112 }
109113
114+ if (request.body.api === 'mistral') {
115+ apiUrl = 'https://api.mistral.ai/v1/chat/completions';
116+ }
117+
110118 if (request.body.api === 'ooba') {
111119 apiUrl = `${trimV1(request.body.server_url)}/v1/chat/completions`;
112120 const imgMessage = body.messages.pop();