Add new Grok models (#4541) * Add new Grok models * Fix xAI request fields filtering * Fetch xAI model lists from API
Signed| @@ -3624,19 +3624,13 @@ | ||
| 3624 | 3624 | <div data-for="api_key_xai" class="neutral_warning" data-i18n="For privacy reasons, your API key will be hidden after you click 'Connect'."> |
| 3625 | 3625 | For privacy reasons, your API key will be hidden after you click 'Connect'. |
| 3626 | 3626 | </div> |
| 3627 | + <div> | |
| 3627 | 3628 | <h4 data-i18n="xAI Model">xAI Model</h4> |
| 3628 | 3629 | <select id="model_xai_select"> |
| 3629 | - <option value="grok-4-0709">grok-4-0709</option> | |
| 3630 | + <option value="" data-i18n="-- Connect to the API --">-- Connect to the API --</option> | |
| 3630 | - <option value="grok-3-beta">grok-3-beta</option> | |
| 3631 | - <option value="grok-3-fast-beta">grok-3-fast-beta</option> | |
| 3632 | - <option value="grok-3-mini-beta">grok-3-mini-beta</option> | |
| 3633 | - <option value="grok-3-mini-fast-beta">grok-3-mini-fast-beta</option> | |
| 3634 | - <option value="grok-2-vision-1212">grok-2-vision-1212</option> | |
| 3635 | - <option value="grok-2-1212">grok-2-1212</option> | |
| 3636 | - <option value="grok-vision-beta">grok-vision-beta</option> | |
| 3637 | - <option value="grok-beta">grok-beta</option> | |
| 3638 | 3631 | </select> |
| 3639 | 3632 | </div> |
| 3633 | + </div> | |
| 3640 | 3634 | <div id="aimlapi_form" data-source="aimlapi"> |
| 3641 | 3635 | <h4> |
| 3642 | 3636 | <span data-i18n="AI/ML API Key">AI/ML API Key</span> |
| @@ -550,6 +550,7 @@ jQuery(async function () { | ||
| 550 | 550 | await processEndpoint('nanogpt', '/api/backends/chat-completions/multimodal-models/nanogpt'); |
| 551 | 551 | await processEndpoint('electronhub', '/api/backends/chat-completions/multimodal-models/electronhub'); |
| 552 | 552 | await processEndpoint('mistral', '/api/backends/chat-completions/multimodal-models/mistral'); |
| 553 | + await processEndpoint('xai', '/api/backends/chat-completions/multimodal-models/xai'); | |
| 553 | 554 | } |
| 554 | 555 | |
| 555 | 556 | await addSettings(); |
| @@ -47,7 +47,7 @@ | ||
| 47 | 47 | </div> |
| 48 | 48 | </label> |
| 49 | 49 | <select id="caption_multimodal_model" class="flex1 text_pole"> |
| 50 | 50 | <!-- AI/ML API, OpenRouter, Pollinations, NanoGPT, Mistral, xAI are added externally by JavaScript --> |
| 51 | 51 | <option data-type="cohere" value="c4ai-aya-vision-8b">c4ai-aya-vision-8b</option> |
| 52 | 52 | <option data-type="cohere" value="c4ai-aya-vision-32b">c4ai-aya-vision-32b</option> |
| 53 | 53 | <option data-type="cohere" value="command-a-vision-07-2025">command-a-vision-07-2025</option> |
| @@ -168,9 +168,6 @@ | ||
| 168 | 168 | <option data-type="koboldcpp" value="koboldcpp_current" data-i18n="currently_loaded">[Currently loaded]</option> |
| 169 | 169 | <option data-type="vllm" value="vllm_current" data-i18n="currently_selected">[Currently selected]</option> |
| 170 | 170 | <option data-type="custom" value="custom_current" data-i18n="currently_selected">[Currently selected]</option> |
| 171 | - <option data-type="xai" value="grok-4-0709">grok-4-0709</option> | |
| 172 | - <option data-type="xai" value="grok-2-vision-1212">grok-2-vision-1212</option> | |
| 173 | - <option data-type="xai" value="grok-vision-beta">grok-vision-beta</option> | |
| 174 | 171 | </select> |
| 175 | 172 | </div> |
| 176 | 173 | <div data-type="ollama"> |
| @@ -2028,6 +2028,24 @@ function saveModelList(data) { | ||
| 2028 | 2028 | .append(new Option(modelId || 'None', modelId || '', true, true)) |
| 2029 | 2029 | .trigger('change'); |
| 2030 | 2030 | } |
| 2031 | + | |
| 2032 | + if (oai_settings.chat_completion_source == chat_completion_sources.XAI) { | |
| 2033 | + $('#model_xai_select').empty(); | |
| 2034 | + model_list.forEach((model) => { | |
| 2035 | + $('#model_xai_select').append( | |
| 2036 | + $('<option>', { | |
| 2037 | + value: model.id, | |
| 2038 | + text: model.id, | |
| 2039 | + })); | |
| 2040 | + }); | |
| 2041 | + | |
| 2042 | + const selectedModel = model_list.find(model => model.id === oai_settings.xai_model); | |
| 2043 | + if (model_list.length > 0 && (!selectedModel || !oai_settings.xai_model)) { | |
| 2044 | + oai_settings.xai_model = model_list[0].id; | |
| 2045 | + } | |
| 2046 | + | |
| 2047 | + $('#model_xai_select').val(oai_settings.xai_model).trigger('change'); | |
| 2048 | + } | |
| 2031 | 2049 | } |
| 2032 | 2050 | |
| 2033 | 2051 | function appendOpenRouterOptions(model_list, groupModels = false, sort = false) { |
| @@ -2452,19 +2470,24 @@ async function sendOpenAIRequest(type, messages, signal, { jsonSchema = null } = | ||
| 2452 | 2470 | } |
| 2453 | 2471 | |
| 2454 | 2472 | if (isXAI) { |
| 2455 | - if (generate_data.model.includes('grok-4')) { | |
| 2473 | + const model = generate_data.model; | |
| 2474 | + if (model.includes('grok-3-mini')) { | |
| 2456 | 2475 | delete generate_data.presence_penalty; |
| 2457 | 2476 | delete generate_data.frequency_penalty; |
| 2458 | 2477 | delete generate_data.stop; |
| 2478 | + } else { | |
| 2479 | + // As of 2025/09/21, only grok-3-mini accepts reasoning_effort | |
| 2459 | 2480 | delete generate_data.reasoning_effort; |
| 2460 | 2481 | } |
| 2461 | - if (generate_data.model.includes('grok-3-mini')) { | |
| 2482 | + | |
| 2483 | + if (model.includes('grok-4') || model.includes('grok-code')) { | |
| 2462 | 2484 | delete generate_data.presence_penalty; |
| 2463 | 2485 | delete generate_data.frequency_penalty; |
| 2486 | + | |
| 2487 | + // grok-4-fast-non-reasoning accepts stop | |
| 2488 | + if (!model.includes('grok-4-fast-non-reasoning')) { | |
| 2489 | + delete generate_data.stop; | |
| 2464 | 2490 | } |
| 2465 | - if (generate_data.model.includes('grok-vision')) { | |
| 2466 | - delete generate_data.tools; | |
| 2467 | - delete generate_data.tool_choice; | |
| 2468 | 2491 | } |
| 2469 | 2492 | } |
| 2470 | 2493 | |
| @@ -4916,6 +4939,10 @@ async function onModelChange() { | ||
| 4916 | 4939 | } |
| 4917 | 4940 | |
| 4918 | 4941 | if ($(this).is('#model_xai_select')) { |
| 4942 | + if (!value) { | |
| 4943 | + console.debug('Null XAI model selected. Ignoring.'); | |
| 4944 | + return; | |
| 4945 | + } | |
| 4919 | 4946 | console.log('XAI model changed to', value); |
| 4920 | 4947 | oai_settings.xai_model = value; |
| 4921 | 4948 | } |
| @@ -5184,11 +5211,14 @@ async function onModelChange() { | ||
| 5184 | 5211 | $('#openai_max_context').attr('max', unlocked_max); |
| 5185 | 5212 | } else if (oai_settings.xai_model.includes('grok-2-vision')) { |
| 5186 | 5213 | $('#openai_max_context').attr('max', max_32k); |
| 5187 | 5214 | } else if (oai_settings.xai_model.includes('grok-vision4-fast')) { |
| 5188 | 5215 | $('#openai_max_context').attr('max', max_8kmax_2mil); |
| 5189 | 5216 | } else if (oai_settings.xai_model.includes('grok-4')) { |
| 5190 | 5217 | $('#openai_max_context').attr('max', max_256k); |
| 5218 | + } else if (oai_settings.xai_model.includes('grok-code')) { | |
| 5219 | + $('#openai_max_context').attr('max', max_256k); | |
| 5191 | 5220 | } else { |
| 5221 | + // grok 2 and grok 3 | |
| 5192 | 5222 | $('#openai_max_context').attr('max', max_128k); |
| 5193 | 5223 | } |
| 5194 | 5224 | |
| @@ -5737,7 +5767,6 @@ export function isImageInliningSupported() { | ||
| 5737 | 5767 | // xAI (Grok) |
| 5738 | 5768 | 'grok-4', |
| 5739 | 5769 | 'grok-2-vision', |
| 5740 | - 'grok-vision', | |
| 5741 | 5770 | // Moonshot |
| 5742 | 5771 | 'moonshot-v1-8k-vision-preview', |
| 5743 | 5772 | 'moonshot-v1-32k-vision-preview', |
| @@ -5770,6 +5799,7 @@ export function isImageInliningSupported() { | ||
| 5770 | 5799 | case chat_completion_sources.COHERE: |
| 5771 | 5800 | return visionSupportedModels.some(model => oai_settings.cohere_model.includes(model)); |
| 5772 | 5801 | case chat_completion_sources.XAI: |
| 5802 | + // TODO: xAI's /models endpoint doesn't return modality info | |
| 5773 | 5803 | return visionSupportedModels.some(model => oai_settings.xai_model.includes(model)); |
| 5774 | 5804 | case chat_completion_sources.AIMLAPI: |
| 5775 | 5805 | return (Array.isArray(model_list) && model_list.find(m => m.id === oai_settings.aimlapi_model)?.features?.includes('openai/chat-completion.vision')); |
| @@ -1012,7 +1012,7 @@ async function sendXaiRequest(request, response) { | ||
| 1012 | 1012 | bodyParams['stop'] = request.body.stop; |
| 1013 | 1013 | } |
| 1014 | 1014 | |
| 1015 | - if (request.body.reasoning_effort && ['grok-3-mini-beta', 'grok-3-mini-fast-beta'].includes(request.body.model)) { | |
| 1015 | + if (request.body.reasoning_effort) { | |
| 1016 | 1016 | bodyParams['reasoning_effort'] = request.body.reasoning_effort === 'high' ? 'high' : 'low'; |
| 1017 | 1017 | } |
| 1018 | 1018 | |
| @@ -2238,4 +2238,37 @@ multimodalModels.post('/mistral', async (req, res) => { | ||
| 2238 | 2238 | } |
| 2239 | 2239 | }); |
| 2240 | 2240 | |
| 2241 | +multimodalModels.post('/xai', async (req, res) => { | |
| 2242 | + try { | |
| 2243 | + const key = readSecret(req.user.directories, SECRET_KEYS.XAI); | |
| 2244 | + | |
| 2245 | + if (!key) { | |
| 2246 | + return res.json([]); | |
| 2247 | + } | |
| 2248 | + | |
| 2249 | + // xAI's /models endpoint doesn't return modality info, so we must use /language-models instead | |
| 2250 | + const response = await fetch('https://api.x.ai/v1/language-models', { | |
| 2251 | + headers: { | |
| 2252 | + 'Authorization': `Bearer ${key}`, | |
| 2253 | + }, | |
| 2254 | + }); | |
| 2255 | + | |
| 2256 | + if (!response.ok) { | |
| 2257 | + return res.json([]); | |
| 2258 | + } | |
| 2259 | + | |
| 2260 | + /** @type {any} */ | |
| 2261 | + const data = await response.json(); | |
| 2262 | + const multimodalModels = data.models.filter(m => m.input_modalities?.includes('image')).map(m => m.id); | |
| 2263 | + if (!multimodalModels.includes('grok-4-0709')) { | |
| 2264 | + // The endpoint says it doesn't support images, but it does | |
| 2265 | + multimodalModels.push('grok-4-0709'); | |
| 2266 | + } | |
| 2267 | + return res.json(multimodalModels); | |
| 2268 | + } catch (error) { | |
| 2269 | + console.error(error); | |
| 2270 | + return res.sendStatus(500); | |
| 2271 | + } | |
| 2272 | +}); | |
| 2273 | + | |
| 2241 | 2274 | router.use('/multimodal-models', multimodalModels); |