Add xAI as chat completion source
| @@ -685,7 +685,7 @@ | ||
| 685 | 685 | </span> |
| 686 | 686 | </div> |
| 687 | 687 | </div> |
| 688 | 688 | <div class="range-block" data-source="openai,claude,windowai,openrouter,ai21,scale,makersuite,mistralai,custom,cohere,perplexity,groq,01ai,nanogpt,deepseek,xai"> |
| 689 | 689 | <div class="range-block-title" data-i18n="Temperature"> |
| 690 | 690 | Temperature |
| 691 | 691 | </div> |
| @@ -698,7 +698,7 @@ | ||
| 698 | 698 | </div> |
| 699 | 699 | </div> |
| 700 | 700 | </div> |
| 701 | 701 | <div class="range-block" data-source="openai,openrouter,custom,cohere,perplexity,groq,mistralai,nanogpt,deepseek,xai"> |
| 702 | 702 | <div class="range-block-title" data-i18n="Frequency Penalty"> |
| 703 | 703 | Frequency Penalty |
| 704 | 704 | </div> |
| @@ -711,7 +711,7 @@ | ||
| 711 | 711 | </div> |
| 712 | 712 | </div> |
| 713 | 713 | </div> |
| 714 | 714 | <div class="range-block" data-source="openai,openrouter,custom,cohere,perplexity,groq,mistralai,nanogpt,deepseek,xai"> |
| 715 | 715 | <div class="range-block-title" data-i18n="Presence Penalty"> |
| 716 | 716 | Presence Penalty |
| 717 | 717 | </div> |
| @@ -737,7 +737,7 @@ | ||
| 737 | 737 | </div> |
| 738 | 738 | </div> |
| 739 | 739 | </div> |
| 740 | 740 | <div class="range-block" data-source="openai,claude,openrouter,ai21,scale,makersuite,mistralai,custom,cohere,perplexity,groq,01ai,nanogpt,deepseek,xai"> |
| 741 | 741 | <div class="range-block-title" data-i18n="Top P"> |
| 742 | 742 | Top P |
| 743 | 743 | </div> |
| @@ -974,7 +974,7 @@ | ||
| 974 | 974 | </div> |
| 975 | 975 | </div> |
| 976 | 976 | </div> |
| 977 | 977 | <div class="range-block" data-source="openai,openrouter,mistralai,custom,cohere,groq,nanogpt,xai"> |
| 978 | 978 | <div class="range-block-title justifyLeft" data-i18n="Seed"> |
| 979 | 979 | Seed |
| 980 | 980 | </div> |
| @@ -1965,7 +1965,7 @@ | ||
| 1965 | 1965 | </span> |
| 1966 | 1966 | </div> |
| 1967 | 1967 | </div> |
| 1968 | 1968 | <div class="range-block" data-source="openai,cohere,mistralai,custom,claude,openrouter,groq,deepseek,makersuite,ai21,xai"> |
| 1969 | 1969 | <label for="openai_function_calling" class="checkbox_label flexWrap widthFreeExpand"> |
| 1970 | 1970 | <input id="openai_function_calling" type="checkbox" /> |
| 1971 | 1971 | <span data-i18n="Enable function calling">Enable function calling</span> |
| @@ -1975,7 +1975,7 @@ | ||
| 1975 | 1975 | <span data-i18n="enable_functions_desc_3">Can be utilized by various extensions to provide additional functionality.</span> |
| 1976 | 1976 | </div> |
| 1977 | 1977 | </div> |
| 1978 | 1978 | <div class="range-block" data-source="openai,openrouter,makersuite,claude,custom,01ai,xai"> |
| 1979 | 1979 | <label for="openai_image_inlining" class="checkbox_label flexWrap widthFreeExpand"> |
| 1980 | 1980 | <input id="openai_image_inlining" type="checkbox" /> |
| 1981 | 1981 | <span data-i18n="Send inline images">Send inline images</span> |
| @@ -2031,7 +2031,7 @@ | ||
| 2031 | 2031 | </span> |
| 2032 | 2032 | </div> |
| 2033 | 2033 | </div> |
| 2034 | 2034 | <div class="range-block" data-source="deepseek,openrouter,custom,claude,xai"> |
| 2035 | 2035 | <label for="openai_show_thoughts" class="checkbox_label widthFreeExpand"> |
| 2036 | 2036 | <input id="openai_show_thoughts" type="checkbox" /> |
| 2037 | 2037 | <span> |
| @@ -2045,7 +2045,7 @@ | ||
| 2045 | 2045 | </span> |
| 2046 | 2046 | </div> |
| 2047 | 2047 | </div> |
| 2048 | 2048 | <div class="flex-container flexFlowColumn wide100p textAlignCenter marginTop10" data-source="openai,custom,claude,xai"> |
| 2049 | 2049 | <div class="flex-container oneline-dropdown" title="Constrains effort on reasoning for reasoning models. Currently supported values are low, medium, and high. Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response." data-i18n="[title]Constrains effort on reasoning for reasoning models."> |
| 2050 | 2050 | <label for="openai_reasoning_effort"> |
| 2051 | 2051 | <span data-i18n="Reasoning Effort">Reasoning Effort</span> |
| @@ -2756,6 +2756,7 @@ | ||
| 2756 | 2756 | <option value="perplexity">Perplexity</option> |
| 2757 | 2757 | <option value="scale">Scale</option> |
| 2758 | 2758 | <option value="windowai">Window AI</option> |
| 2759 | + <option value="xai">xAI (Grok)</option> | |
| 2759 | 2760 | </optgroup> |
| 2760 | 2761 | </select> |
| 2761 | 2762 | <div class="inline-drawer wide100p" data-source="openai,claude,mistralai,makersuite,deepseek"> |
| @@ -3424,6 +3425,31 @@ | ||
| 3424 | 3425 | <select id="model_01ai_select"> |
| 3425 | 3426 | </select> |
| 3426 | 3427 | </div> |
| 3428 | + <div id="xai_form" data-source="xai"> | |
| 3429 | + <h4> | |
| 3430 | + <a data-i18n="xAI API Key" href="https://console.x.ai/" target="_blank" rel="noopener noreferrer"> | |
| 3431 | + xAI API Key | |
| 3432 | + </a> | |
| 3433 | + </h4> | |
| 3434 | + <div class="flex-container"> | |
| 3435 | + <input id="api_key_xai" name="api_key_xai" class="text_pole flex1" value="" type="text" autocomplete="off"> | |
| 3436 | + <div title="Clear your API key" data-i18n="[title]Clear your API key" class="menu_button fa-solid fa-circle-xmark clear-api-key" data-key="api_key_xai"></div> | |
| 3437 | + </div> | |
| 3438 | + <div data-for="api_key_01ai" class="neutral_warning" data-i18n="For privacy reasons, your API key will be hidden after you reload the page."> | |
| 3439 | + For privacy reasons, your API key will be hidden after you reload the page. | |
| 3440 | + </div> | |
| 3441 | + <h4 data-i18n="xAI Model">xAI Model</h4> | |
| 3442 | + <select id="model_xai_select"> | |
| 3443 | + <option value="grok-3-beta">grok-3-beta</option> | |
| 3444 | + <option value="grok-3-fast-beta">grok-3-fast-beta</option> | |
| 3445 | + <option value="grok-3-mini-beta">grok-3-mini-beta</option> | |
| 3446 | + <option value="grok-3-mini-fast-beta">grok-3-mini-fast-beta</option> | |
| 3447 | + <option value="grok-2-vision-1212">grok-2-vision-1212</option> | |
| 3448 | + <option value="grok-2-1212">grok-2-1212</option> | |
| 3449 | + <option value="grok-vision-beta">grok-vision-beta</option> | |
| 3450 | + <option value="grok-beta">grok-beta</option> | |
| 3451 | + </select> | |
| 3452 | + </div> | |
| 3427 | 3453 | <div id="prompt_post_porcessing_form" data-source="custom,openrouter"> |
| 3428 | 3454 | <h4 data-i18n="Prompt Post-Processing">Prompt Post-Processing</h4> |
| 3429 | 3455 | <select id="custom_prompt_post_processing" class="text_pole" title="Applies additional processing to the prompt before sending it to the API." data-i18n="[title]Applies additional processing to the prompt before sending it to the API."> |
| @@ -409,6 +409,7 @@ function RA_autoconnect(PrevApi) { | ||
| 409 | 409 | || (secret_state[SECRET_KEYS.ZEROONEAI] && oai_settings.chat_completion_source == chat_completion_sources.ZEROONEAI) |
| 410 | 410 | || (secret_state[SECRET_KEYS.NANOGPT] && oai_settings.chat_completion_source == chat_completion_sources.NANOGPT) |
| 411 | 411 | || (secret_state[SECRET_KEYS.DEEPSEEK] && oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK) |
| 412 | + || (secret_state[SECRET_KEYS.XAI] && oai_settings.chat_completion_source == chat_completion_sources.XAI) | |
| 412 | 413 | || (isValidUrl(oai_settings.custom_url) && oai_settings.chat_completion_source == chat_completion_sources.CUSTOM) |
| 413 | 414 | ) { |
| 414 | 415 | $('#api_button_openai').trigger('click'); |
| @@ -184,6 +184,7 @@ export const chat_completion_sources = { | ||
| 184 | 184 | ZEROONEAI: '01ai', |
| 185 | 185 | NANOGPT: 'nanogpt', |
| 186 | 186 | DEEPSEEK: 'deepseek', |
| 187 | + XAI: 'xai', | |
| 187 | 188 | }; |
| 188 | 189 | |
| 189 | 190 | const character_names_behavior = { |
| @@ -257,6 +258,7 @@ export const settingsToUpdate = { | ||
| 257 | 258 | nanogpt_model: ['#model_nanogpt_select', 'nanogpt_model', false], |
| 258 | 259 | deepseek_model: ['#model_deepseek_select', 'deepseek_model', false], |
| 259 | 260 | zerooneai_model: ['#model_01ai_select', 'zerooneai_model', false], |
| 261 | + xai_model: ['#model_xai_select', 'xai_model', false], | |
| 260 | 262 | custom_model: ['#custom_model_id', 'custom_model', false], |
| 261 | 263 | custom_url: ['#custom_api_url_text', 'custom_url', false], |
| 262 | 264 | custom_include_body: ['#custom_include_body', 'custom_include_body', false], |
| @@ -345,6 +347,7 @@ const default_settings = { | ||
| 345 | 347 | nanogpt_model: 'gpt-4o-mini', |
| 346 | 348 | zerooneai_model: 'yi-large', |
| 347 | 349 | deepseek_model: 'deepseek-chat', |
| 350 | + xai_model: 'grok-3-beta', | |
| 348 | 351 | custom_model: '', |
| 349 | 352 | custom_url: '', |
| 350 | 353 | custom_include_body: '', |
| @@ -425,6 +428,7 @@ const oai_settings = { | ||
| 425 | 428 | nanogpt_model: 'gpt-4o-mini', |
| 426 | 429 | zerooneai_model: 'yi-large', |
| 427 | 430 | deepseek_model: 'deepseek-chat', |
| 431 | + xai_model: 'grok-3-beta', | |
| 428 | 432 | custom_model: '', |
| 429 | 433 | custom_url: '', |
| 430 | 434 | custom_include_body: '', |
| @@ -1644,6 +1648,8 @@ export function getChatCompletionModel(source = null) { | ||
| 1644 | 1648 | return oai_settings.nanogpt_model; |
| 1645 | 1649 | case chat_completion_sources.DEEPSEEK: |
| 1646 | 1650 | return oai_settings.deepseek_model; |
| 1651 | + case chat_completion_sources.XAI: | |
| 1652 | + return oai_settings.xai_model; | |
| 1647 | 1653 | default: |
| 1648 | 1654 | throw new Error(`Unknown chat completion source: ${activeSource}`); |
| 1649 | 1655 | } |
| @@ -1961,6 +1967,7 @@ async function sendOpenAIRequest(type, messages, signal) { | ||
| 1961 | 1967 | const is01AI = oai_settings.chat_completion_source == chat_completion_sources.ZEROONEAI; |
| 1962 | 1968 | const isNano = oai_settings.chat_completion_source == chat_completion_sources.NANOGPT; |
| 1963 | 1969 | const isDeepSeek = oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK; |
| 1970 | + const isXAI = oai_settings.chat_completion_source == chat_completion_sources.XAI; | |
| 1964 | 1971 | const isTextCompletion = isOAI && textCompletionModels.includes(oai_settings.openai_model); |
| 1965 | 1972 | const isQuiet = type === 'quiet'; |
| 1966 | 1973 | const isImpersonate = type === 'impersonate'; |
| @@ -2033,7 +2040,7 @@ async function sendOpenAIRequest(type, messages, signal) { | ||
| 2033 | 2040 | } |
| 2034 | 2041 | |
| 2035 | 2042 | // Add logprobs request (currently OpenAI only, max 5 on their side) |
| 2036 | 2043 | if (useLogprobs && (isOAI || isCustom || isDeepSeek || isXAI)) { |
| 2037 | 2044 | generate_data['logprobs'] = 5; |
| 2038 | 2045 | } |
| 2039 | 2046 | |
| @@ -2152,7 +2159,7 @@ async function sendOpenAIRequest(type, messages, signal) { | ||
| 2152 | 2159 | } |
| 2153 | 2160 | } |
| 2154 | 2161 | |
| 2155 | 2162 | if ((isOAI || isOpenRouter || isMistral || isCustom || isCohere || isNano || isXAI) && oai_settings.seed >= 0) { |
| 2156 | 2163 | generate_data['seed'] = oai_settings.seed; |
| 2157 | 2164 | } |
| 2158 | 2165 | |
| @@ -2278,6 +2285,11 @@ export function getStreamingReply(data, state, { chatCompletionSource = null, ov | ||
| 2278 | 2285 | state.reasoning += (data.choices?.filter(x => x?.delta?.reasoning_content)?.[0]?.delta?.reasoning_content || ''); |
| 2279 | 2286 | } |
| 2280 | 2287 | return data.choices?.[0]?.delta?.content || ''; |
| 2288 | + } else if (chat_completion_source === chat_completion_sources.XAI) { | |
| 2289 | + if (show_thoughts) { | |
| 2290 | + state.reasoning += (data.choices?.filter(x => x?.delta?.reasoning_content)?.[0]?.delta?.reasoning_content || ''); | |
| 2291 | + } | |
| 2292 | + return data.choices?.[0]?.delta?.content || ''; | |
| 2281 | 2293 | } else if (chat_completion_source === chat_completion_sources.OPENROUTER) { |
| 2282 | 2294 | if (show_thoughts) { |
| 2283 | 2295 | state.reasoning += (data.choices?.filter(x => x?.delta?.reasoning)?.[0]?.delta?.reasoning || ''); |
| @@ -2310,6 +2322,7 @@ function parseChatCompletionLogprobs(data) { | ||
| 2310 | 2322 | switch (oai_settings.chat_completion_source) { |
| 2311 | 2323 | case chat_completion_sources.OPENAI: |
| 2312 | 2324 | case chat_completion_sources.DEEPSEEK: |
| 2325 | + case chat_completion_sources.XAI: | |
| 2313 | 2326 | case chat_completion_sources.CUSTOM: |
| 2314 | 2327 | if (!data.choices?.length) { |
| 2315 | 2328 | return null; |
| @@ -3231,6 +3244,7 @@ function loadOpenAISettings(data, settings) { | ||
| 3231 | 3244 | oai_settings.nanogpt_model = settings.nanogpt_model ?? default_settings.nanogpt_model; |
| 3232 | 3245 | oai_settings.deepseek_model = settings.deepseek_model ?? default_settings.deepseek_model; |
| 3233 | 3246 | oai_settings.zerooneai_model = settings.zerooneai_model ?? default_settings.zerooneai_model; |
| 3247 | + oai_settings.xai_model = settings.xai_model ?? default_settings.xai_model; | |
| 3234 | 3248 | oai_settings.custom_model = settings.custom_model ?? default_settings.custom_model; |
| 3235 | 3249 | oai_settings.custom_url = settings.custom_url ?? default_settings.custom_url; |
| 3236 | 3250 | oai_settings.custom_include_body = settings.custom_include_body ?? default_settings.custom_include_body; |
| @@ -3316,6 +3330,8 @@ function loadOpenAISettings(data, settings) { | ||
| 3316 | 3330 | $('#model_deepseek_select').val(oai_settings.deepseek_model); |
| 3317 | 3331 | $(`#model_deepseek_select option[value="${oai_settings.deepseek_model}"`).prop('selected', true); |
| 3318 | 3332 | $('#model_01ai_select').val(oai_settings.zerooneai_model); |
| 3333 | + $('#model_xai_select').val(oai_settings.xai_model); | |
| 3334 | + $(`#model_xai_select option[value="${oai_settings.xai_model}"`).attr('selected', true); | |
| 3319 | 3335 | $('#custom_model_id').val(oai_settings.custom_model); |
| 3320 | 3336 | $('#custom_api_url_text').val(oai_settings.custom_url); |
| 3321 | 3337 | $('#openai_max_context').val(oai_settings.openai_max_context); |
| @@ -4312,6 +4328,11 @@ async function onModelChange() { | ||
| 4312 | 4328 | $('#custom_model_id').val(value).trigger('input'); |
| 4313 | 4329 | } |
| 4314 | 4330 | |
| 4331 | + if ($(this).is('#model_xai_select')) { | |
| 4332 | + console.log('XAI model changed to', value); | |
| 4333 | + oai_settings.xai_model = value; | |
| 4334 | + } | |
| 4335 | + | |
| 4315 | 4336 | if (oai_settings.chat_completion_source == chat_completion_sources.SCALE) { |
| 4316 | 4337 | if (oai_settings.max_context_unlocked) { |
| 4317 | 4338 | $('#openai_max_context').attr('max', unlocked_max); |
| @@ -4584,6 +4605,22 @@ async function onModelChange() { | ||
| 4584 | 4605 | $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input'); |
| 4585 | 4606 | } |
| 4586 | 4607 | |
| 4608 | + if (oai_settings.chat_completion_source === chat_completion_sources.XAI) { | |
| 4609 | + if (oai_settings.max_context_unlocked) { | |
| 4610 | + $('#openai_max_context').attr('max', unlocked_max); | |
| 4611 | + } else if (oai_settings.xai_model.includes('grok-2-vision')) { | |
| 4612 | + $('#openai_max_context').attr('max', max_32k); | |
| 4613 | + } else if (oai_settings.xai_model.includes('grok-vision')) { | |
| 4614 | + $('#openai_max_context').attr('max', max_8k); | |
| 4615 | + } else { | |
| 4616 | + $('#openai_max_context').attr('max', max_128k); | |
| 4617 | + } | |
| 4618 | + | |
| 4619 | + oai_settings.openai_max_context = Math.min(Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context); | |
| 4620 | + $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input'); | |
| 4621 | + $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input'); | |
| 4622 | + } | |
| 4623 | + | |
| 4587 | 4624 | if (oai_settings.chat_completion_source === chat_completion_sources.COHERE) { |
| 4588 | 4625 | oai_settings.pres_pen_openai = Math.min(Math.max(0, oai_settings.pres_pen_openai), 1); |
| 4589 | 4626 | $('#pres_pen_openai').attr('max', 1).attr('min', 0).val(oai_settings.pres_pen_openai).trigger('input'); |
| @@ -4822,6 +4859,19 @@ async function onConnectButtonClick(e) { | ||
| 4822 | 4859 | } |
| 4823 | 4860 | } |
| 4824 | 4861 | |
| 4862 | + if (oai_settings.chat_completion_source === chat_completion_sources.XAI) { | |
| 4863 | + const api_key_xai = String($('#api_key_xai').val()).trim(); | |
| 4864 | + | |
| 4865 | + if (api_key_xai.length) { | |
| 4866 | + await writeSecret(SECRET_KEYS.XAI, api_key_xai); | |
| 4867 | + } | |
| 4868 | + | |
| 4869 | + if (!secret_state[SECRET_KEYS.XAI]) { | |
| 4870 | + console.log('No secret key saved for XAI'); | |
| 4871 | + return; | |
| 4872 | + } | |
| 4873 | + } | |
| 4874 | + | |
| 4825 | 4875 | startStatusLoading(); |
| 4826 | 4876 | saveSettingsDebounced(); |
| 4827 | 4877 | await getStatusOpen(); |
| @@ -4878,6 +4928,9 @@ function toggleChatCompletionForms() { | ||
| 4878 | 4928 | else if (oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK) { |
| 4879 | 4929 | $('#model_deepseek_select').trigger('change'); |
| 4880 | 4930 | } |
| 4931 | + else if (oai_settings.chat_completion_source == chat_completion_sources.XAI) { | |
| 4932 | + $('#model_xai_select').trigger('change'); | |
| 4933 | + } | |
| 4881 | 4934 | $('[data-source]').each(function () { |
| 4882 | 4935 | const validSources = $(this).data('source').split(','); |
| 4883 | 4936 | $(this).toggle(validSources.includes(oai_settings.chat_completion_source)); |
| @@ -5014,6 +5067,8 @@ export function isImageInliningSupported() { | ||
| 5014 | 5067 | 'pixtral-large-2411', |
| 5015 | 5068 | 'c4ai-aya-vision-8b', |
| 5016 | 5069 | 'c4ai-aya-vision-32b', |
| 5070 | + 'grok-2-vision', | |
| 5071 | + 'grok-vision', | |
| 5017 | 5072 | ]; |
| 5018 | 5073 | |
| 5019 | 5074 | switch (oai_settings.chat_completion_source) { |
| @@ -5033,6 +5088,8 @@ export function isImageInliningSupported() { | ||
| 5033 | 5088 | return visionSupportedModels.some(model => oai_settings.mistralai_model.includes(model)); |
| 5034 | 5089 | case chat_completion_sources.COHERE: |
| 5035 | 5090 | return visionSupportedModels.some(model => oai_settings.cohere_model.includes(model)); |
| 5091 | + case chat_completion_sources.XAI: | |
| 5092 | + return visionSupportedModels.some(model => oai_settings.xai_model.includes(model)); | |
| 5036 | 5093 | default: |
| 5037 | 5094 | return false; |
| 5038 | 5095 | } |
| @@ -5629,6 +5686,7 @@ export function initOpenAI() { | ||
| 5629 | 5686 | $('#model_deepseek_select').on('change', onModelChange); |
| 5630 | 5687 | $('#model_01ai_select').on('change', onModelChange); |
| 5631 | 5688 | $('#model_custom_select').on('change', onModelChange); |
| 5689 | + $('#model_xai_select').on('change', onModelChange); | |
| 5632 | 5690 | $('#settings_preset_openai').on('change', onSettingsPresetChange); |
| 5633 | 5691 | $('#new_oai_preset').on('click', onNewPresetClick); |
| 5634 | 5692 | $('#delete_oai_preset').on('click', onDeletePresetClick); |
| @@ -109,6 +109,8 @@ export function extractReasoningFromData(data, { | ||
| 109 | 109 | switch (chatCompletionSource ?? oai_settings.chat_completion_source) { |
| 110 | 110 | case chat_completion_sources.DEEPSEEK: |
| 111 | 111 | return data?.choices?.[0]?.message?.reasoning_content ?? ''; |
| 112 | + case chat_completion_sources.XAI: | |
| 113 | + return data?.choices?.[0]?.message?.reasoning_content ?? ''; | |
| 112 | 114 | case chat_completion_sources.OPENROUTER: |
| 113 | 115 | return data?.choices?.[0]?.message?.reasoning ?? ''; |
| 114 | 116 | case chat_completion_sources.MAKERSUITE: |
| @@ -42,6 +42,7 @@ export const SECRET_KEYS = { | ||
| 42 | 42 | DEEPSEEK: 'api_key_deepseek', |
| 43 | 43 | SERPER: 'api_key_serper', |
| 44 | 44 | FALAI: 'api_key_falai', |
| 45 | + XAI: 'api_key_xai', | |
| 45 | 46 | }; |
| 46 | 47 | |
| 47 | 48 | const INPUT_MAP = { |
| @@ -76,6 +77,7 @@ const INPUT_MAP = { | ||
| 76 | 77 | [SECRET_KEYS.NANOGPT]: '#api_key_nanogpt', |
| 77 | 78 | [SECRET_KEYS.GENERIC]: '#api_key_generic', |
| 78 | 79 | [SECRET_KEYS.DEEPSEEK]: '#api_key_deepseek', |
| 80 | + [SECRET_KEYS.XAI]: '#api_key_xai', | |
| 79 | 81 | }; |
| 80 | 82 | |
| 81 | 83 | async function clearSecret() { |
| @@ -3942,6 +3942,7 @@ function getModelOptions(quiet) { | ||
| 3942 | 3942 | { id: 'model_nanogpt_select', api: 'openai', type: chat_completion_sources.NANOGPT }, |
| 3943 | 3943 | { id: 'model_01ai_select', api: 'openai', type: chat_completion_sources.ZEROONEAI }, |
| 3944 | 3944 | { id: 'model_deepseek_select', api: 'openai', type: chat_completion_sources.DEEPSEEK }, |
| 3945 | + { id: 'model_xai_select', api: 'openai', type: chat_completion_sources.XAI }, | |
| 3945 | 3946 | { id: 'model_novel_select', api: 'novel', type: null }, |
| 3946 | 3947 | { id: 'horde_model', api: 'koboldhorde', type: null }, |
| 3947 | 3948 | ]; |
| @@ -586,6 +586,7 @@ export class ToolManager { | ||
| 586 | 586 | chat_completion_sources.DEEPSEEK, |
| 587 | 587 | chat_completion_sources.MAKERSUITE, |
| 588 | 588 | chat_completion_sources.AI21, |
| 589 | + chat_completion_sources.XAI, | |
| 589 | 590 | ]; |
| 590 | 591 | return supportedSources.includes(oai_settings.chat_completion_source); |
| 591 | 592 | } |
| @@ -176,6 +176,7 @@ export const CHAT_COMPLETION_SOURCES = { | ||
| 176 | 176 | ZEROONEAI: '01ai', |
| 177 | 177 | NANOGPT: 'nanogpt', |
| 178 | 178 | DEEPSEEK: 'deepseek', |
| 179 | + XAI: 'xai', | |
| 179 | 180 | }; |
| 180 | 181 | |
| 181 | 182 | /** |
| @@ -53,6 +53,7 @@ const API_01AI = 'https://api.lingyiwanwu.com/v1'; | ||
| 53 | 53 | const API_AI21 = 'https://api.ai21.com/studio/v1'; |
| 54 | 54 | const API_NANOGPT = 'https://nano-gpt.com/api/v1'; |
| 55 | 55 | const API_DEEPSEEK = 'https://api.deepseek.com/beta'; |
| 56 | +const API_XAI = 'https://api.x.ai/v1'; | |
| 56 | 57 | |
| 57 | 58 | /** |
| 58 | 59 | * Applies a post-processing step to the generated messages. |
| @@ -872,6 +873,9 @@ router.post('/status', async function (request, response_getstatus_openai) { | ||
| 872 | 873 | api_url = new URL(request.body.reverse_proxy || API_DEEPSEEK.replace('/beta', '')); |
| 873 | 874 | api_key_openai = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.DEEPSEEK); |
| 874 | 875 | headers = {}; |
| 876 | + } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.XAI) { | |
| 877 | + api_url = API_XAI; | |
| 878 | + api_key_openai = readSecret(request.user.directories, SECRET_KEYS.XAI); | |
| 875 | 879 | } else { |
| 876 | 880 | console.warn('This chat completion source is not supported yet.'); |
| 877 | 881 | return response_getstatus_openai.status(400).send({ error: true }); |
| @@ -1150,6 +1154,11 @@ router.post('/generate', function (request, response) { | ||
| 1150 | 1154 | apiKey = readSecret(request.user.directories, SECRET_KEYS.ZEROONEAI); |
| 1151 | 1155 | headers = {}; |
| 1152 | 1156 | bodyParams = {}; |
| 1157 | + } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.XAI) { | |
| 1158 | + apiUrl = API_XAI; | |
| 1159 | + apiKey = readSecret(request.user.directories, SECRET_KEYS.XAI); | |
| 1160 | + headers = {}; | |
| 1161 | + bodyParams = {}; | |
| 1153 | 1162 | } else { |
| 1154 | 1163 | console.warn('This chat completion source is not supported yet.'); |
| 1155 | 1164 | return response.status(400).send({ error: true }); |
| @@ -1162,6 +1171,12 @@ router.post('/generate', function (request, response) { | ||
| 1162 | 1171 | } |
| 1163 | 1172 | } |
| 1164 | 1173 | |
| 1174 | + if ([CHAT_COMPLETION_SOURCES.XAI].includes(request.body.chat_completion_source)) { | |
| 1175 | + if (['grok-3-mini-beta', 'grok-3-mini-fast-beta'].includes(request.body.model)) { | |
| 1176 | + bodyParams['reasoning_effort'] = request.body.reasoning_effort === 'high' ? 'high' : 'low'; | |
| 1177 | + } | |
| 1178 | + } | |
| 1179 | + | |
| 1165 | 1180 | if (!apiKey && !request.body.reverse_proxy && request.body.chat_completion_source !== CHAT_COMPLETION_SOURCES.CUSTOM) { |
| 1166 | 1181 | console.warn('OpenAI API key is missing.'); |
| 1167 | 1182 | return response.status(400).send({ error: true }); |
| @@ -52,6 +52,7 @@ export const SECRET_KEYS = { | ||
| 52 | 52 | GENERIC: 'api_key_generic', |
| 53 | 53 | DEEPSEEK: 'api_key_deepseek', |
| 54 | 54 | SERPER: 'api_key_serper', |
| 55 | + XAI: 'api_key_xai', | |
| 55 | 56 | }; |
| 56 | 57 | |
| 57 | 58 | // These are the keys that are safe to expose, even if allowKeysExposure is false |