Merge pull request #3839 from SillyTavern/gork-ai xAI grok
Signed| @@ -0,0 +1,46 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
| 2 | <!-- Generator: Adobe Illustrator 27.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> | ||
| 3 | |||
| 4 | <svg | ||
| 5 | version="1.1" | ||
| 6 | id="katman_1" | ||
| 7 | x="0px" | ||
| 8 | y="0px" | ||
| 9 | viewBox="0 0 438.67001 481.44999" | ||
| 10 | xml:space="preserve" | ||
| 11 | sodipodi:docname="XAI_Logo.svg" | ||
| 12 | width="438.67001" | ||
| 13 | height="481.45001" | ||
| 14 | inkscape:version="1.3 (0e150ed, 2023-07-21)" | ||
| 15 | xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||
| 16 | xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||
| 17 | xmlns="http://www.w3.org/2000/svg" | ||
| 18 | xmlns:svg="http://www.w3.org/2000/svg"><defs | ||
| 19 | id="defs4" /><sodipodi:namedview | ||
| 20 | id="namedview4" | ||
| 21 | pagecolor="#ffffff" | ||
| 22 | bordercolor="#000000" | ||
| 23 | borderopacity="0.25" | ||
| 24 | inkscape:showpageshadow="2" | ||
| 25 | inkscape:pageopacity="0.0" | ||
| 26 | inkscape:pagecheckerboard="0" | ||
| 27 | inkscape:deskcolor="#d1d1d1" | ||
| 28 | inkscape:zoom="0.39645207" | ||
| 29 | inkscape:cx="219.44645" | ||
| 30 | inkscape:cy="238.36425" | ||
| 31 | inkscape:window-width="1512" | ||
| 32 | inkscape:window-height="856" | ||
| 33 | inkscape:window-x="0" | ||
| 34 | inkscape:window-y="38" | ||
| 35 | inkscape:window-maximized="1" | ||
| 36 | inkscape:current-layer="katman_1" /> <g | ||
| 37 | id="g4" | ||
| 38 | transform="translate(-201.61,-56.91)"> <polygon | ||
| 39 | points="631.96,538.36 640.28,93.18 557.09,211.99 565.4,538.36 " | ||
| 40 | id="polygon1" /> <polygon | ||
| 41 | points="379.35,284.53 430.13,357.05 640.28,56.91 538.72,56.91 " | ||
| 42 | id="polygon2" /> <polygon | ||
| 43 | points="353.96,465.84 303.17,393.31 201.61,538.36 303.17,538.36 " | ||
| 44 | id="polygon3" /> <polygon | ||
| 45 | points="531.69,538.36 303.17,211.99 201.61,211.99 430.13,538.36 " | ||
| 46 | id="polygon4" /> </g> </svg> | ||
| @@ -685,7 +685,7 @@ | |||
| 685 | </span> | 685 | </span> |
| 686 | </div> | 686 | </div> |
| 687 | </div> | 687 | </div> |
| 688 | <div class="range-block" data-source="openai,claude,windowai,openrouter,ai21,scale,makersuite,mistralai,custom,cohere,perplexity,groq,01ai,nanogpt,deepseek"> | 688 | <div class="range-block" data-source="openai,claude,windowai,openrouter,ai21,scale,makersuite,mistralai,custom,cohere,perplexity,groq,01ai,nanogpt,deepseek,xai"> |
| 689 | <div class="range-block-title" data-i18n="Temperature"> | 689 | <div class="range-block-title" data-i18n="Temperature"> |
| 690 | Temperature | 690 | Temperature |
| 691 | </div> | 691 | </div> |
| @@ -698,7 +698,7 @@ | |||
| 698 | </div> | 698 | </div> |
| 699 | </div> | 699 | </div> |
| 700 | </div> | 700 | </div> |
| 701 | <div class="range-block" data-source="openai,openrouter,custom,cohere,perplexity,groq,mistralai,nanogpt,deepseek"> | 701 | <div class="range-block" data-source="openai,openrouter,custom,cohere,perplexity,groq,mistralai,nanogpt,deepseek,xai"> |
| 702 | <div class="range-block-title" data-i18n="Frequency Penalty"> | 702 | <div class="range-block-title" data-i18n="Frequency Penalty"> |
| 703 | Frequency Penalty | 703 | Frequency Penalty |
| 704 | </div> | 704 | </div> |
| @@ -711,7 +711,7 @@ | |||
| 711 | </div> | 711 | </div> |
| 712 | </div> | 712 | </div> |
| 713 | </div> | 713 | </div> |
| 714 | <div class="range-block" data-source="openai,openrouter,custom,cohere,perplexity,groq,mistralai,nanogpt,deepseek"> | 714 | <div class="range-block" data-source="openai,openrouter,custom,cohere,perplexity,groq,mistralai,nanogpt,deepseek,xai"> |
| 715 | <div class="range-block-title" data-i18n="Presence Penalty"> | 715 | <div class="range-block-title" data-i18n="Presence Penalty"> |
| 716 | Presence Penalty | 716 | Presence Penalty |
| 717 | </div> | 717 | </div> |
| @@ -737,7 +737,7 @@ | |||
| 737 | </div> | 737 | </div> |
| 738 | </div> | 738 | </div> |
| 739 | </div> | 739 | </div> |
| 740 | <div class="range-block" data-source="openai,claude,openrouter,ai21,scale,makersuite,mistralai,custom,cohere,perplexity,groq,01ai,nanogpt,deepseek"> | 740 | <div class="range-block" data-source="openai,claude,openrouter,ai21,scale,makersuite,mistralai,custom,cohere,perplexity,groq,01ai,nanogpt,deepseek,xai"> |
| 741 | <div class="range-block-title" data-i18n="Top P"> | 741 | <div class="range-block-title" data-i18n="Top P"> |
| 742 | Top P | 742 | Top P |
| 743 | </div> | 743 | </div> |
| @@ -974,7 +974,7 @@ | |||
| 974 | </div> | 974 | </div> |
| 975 | </div> | 975 | </div> |
| 976 | </div> | 976 | </div> |
| 977 | <div class="range-block" data-source="openai,openrouter,mistralai,custom,cohere,groq,nanogpt"> | 977 | <div class="range-block" data-source="openai,openrouter,mistralai,custom,cohere,groq,nanogpt,xai"> |
| 978 | <div class="range-block-title justifyLeft" data-i18n="Seed"> | 978 | <div class="range-block-title justifyLeft" data-i18n="Seed"> |
| 979 | Seed | 979 | Seed |
| 980 | </div> | 980 | </div> |
| @@ -1965,7 +1965,7 @@ | |||
| 1965 | </span> | 1965 | </span> |
| 1966 | </div> | 1966 | </div> |
| 1967 | </div> | 1967 | </div> |
| 1968 | <div class="range-block" data-source="openai,cohere,mistralai,custom,claude,openrouter,groq,deepseek,makersuite,ai21"> | 1968 | <div class="range-block" data-source="openai,cohere,mistralai,custom,claude,openrouter,groq,deepseek,makersuite,ai21,xai"> |
| 1969 | <label for="openai_function_calling" class="checkbox_label flexWrap widthFreeExpand"> | 1969 | <label for="openai_function_calling" class="checkbox_label flexWrap widthFreeExpand"> |
| 1970 | <input id="openai_function_calling" type="checkbox" /> | 1970 | <input id="openai_function_calling" type="checkbox" /> |
| 1971 | <span data-i18n="Enable function calling">Enable function calling</span> | 1971 | <span data-i18n="Enable function calling">Enable function calling</span> |
| @@ -1975,7 +1975,7 @@ | |||
| 1975 | <span data-i18n="enable_functions_desc_3">Can be utilized by various extensions to provide additional functionality.</span> | 1975 | <span data-i18n="enable_functions_desc_3">Can be utilized by various extensions to provide additional functionality.</span> |
| 1976 | </div> | 1976 | </div> |
| 1977 | </div> | 1977 | </div> |
| 1978 | <div class="range-block" data-source="openai,openrouter,mistralai,makersuite,claude,custom,01ai"> | 1978 | <div class="range-block" data-source="openai,openrouter,mistralai,makersuite,claude,custom,01ai,xai"> |
| 1979 | <label for="openai_image_inlining" class="checkbox_label flexWrap widthFreeExpand"> | 1979 | <label for="openai_image_inlining" class="checkbox_label flexWrap widthFreeExpand"> |
| 1980 | <input id="openai_image_inlining" type="checkbox" /> | 1980 | <input id="openai_image_inlining" type="checkbox" /> |
| 1981 | <span data-i18n="Send inline images">Send inline images</span> | 1981 | <span data-i18n="Send inline images">Send inline images</span> |
| @@ -1987,7 +1987,7 @@ | |||
| 1987 | <code><i class="fa-solid fa-wand-magic-sparkles"></i></code> | 1987 | <code><i class="fa-solid fa-wand-magic-sparkles"></i></code> |
| 1988 | <span data-i18n="image_inlining_hint_3">menu to attach an image file to the chat.</span> | 1988 | <span data-i18n="image_inlining_hint_3">menu to attach an image file to the chat.</span> |
| 1989 | </div> | 1989 | </div> |
| 1990 | <div class="flex-container flexFlowColumn wide100p textAlignCenter marginTop10" data-source="openai,custom"> | 1990 | <div class="flex-container flexFlowColumn wide100p textAlignCenter marginTop10" data-source="openai,custom,xai"> |
| 1991 | <div class="flex-container oneline-dropdown"> | 1991 | <div class="flex-container oneline-dropdown"> |
| 1992 | <label for="openai_inline_image_quality" data-i18n="Inline Image Quality"> | 1992 | <label for="openai_inline_image_quality" data-i18n="Inline Image Quality"> |
| 1993 | Inline Image Quality | 1993 | Inline Image Quality |
| @@ -2031,7 +2031,7 @@ | |||
| 2031 | </span> | 2031 | </span> |
| 2032 | </div> | 2032 | </div> |
| 2033 | </div> | 2033 | </div> |
| 2034 | <div class="range-block" data-source="deepseek,openrouter,custom,claude"> | 2034 | <div class="range-block" data-source="deepseek,openrouter,custom,claude,xai"> |
| 2035 | <label for="openai_show_thoughts" class="checkbox_label widthFreeExpand"> | 2035 | <label for="openai_show_thoughts" class="checkbox_label widthFreeExpand"> |
| 2036 | <input id="openai_show_thoughts" type="checkbox" /> | 2036 | <input id="openai_show_thoughts" type="checkbox" /> |
| 2037 | <span> | 2037 | <span> |
| @@ -2045,7 +2045,7 @@ | |||
| 2045 | </span> | 2045 | </span> |
| 2046 | </div> | 2046 | </div> |
| 2047 | </div> | 2047 | </div> |
| 2048 | <div class="flex-container flexFlowColumn wide100p textAlignCenter marginTop10" data-source="openai,custom,claude"> | 2048 | <div class="flex-container flexFlowColumn wide100p textAlignCenter marginTop10" data-source="openai,custom,claude,xai"> |
| 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."> | 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 | <label for="openai_reasoning_effort"> | 2050 | <label for="openai_reasoning_effort"> |
| 2051 | <span data-i18n="Reasoning Effort">Reasoning Effort</span> | 2051 | <span data-i18n="Reasoning Effort">Reasoning Effort</span> |
| @@ -2756,6 +2756,7 @@ | |||
| 2756 | <option value="perplexity">Perplexity</option> | 2756 | <option value="perplexity">Perplexity</option> |
| 2757 | <option value="scale">Scale</option> | 2757 | <option value="scale">Scale</option> |
| 2758 | <option value="windowai">Window AI</option> | 2758 | <option value="windowai">Window AI</option> |
| 2759 | <option value="xai">xAI (Grok)</option> | ||
| 2759 | </optgroup> | 2760 | </optgroup> |
| 2760 | </select> | 2761 | </select> |
| 2761 | <div class="inline-drawer wide100p" data-source="openai,claude,mistralai,makersuite,deepseek"> | 2762 | <div class="inline-drawer wide100p" data-source="openai,claude,mistralai,makersuite,deepseek"> |
| @@ -3424,6 +3425,31 @@ | |||
| 3424 | <select id="model_01ai_select"> | 3425 | <select id="model_01ai_select"> |
| 3425 | </select> | 3426 | </select> |
| 3426 | </div> | 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_xai" 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 | <div id="prompt_post_porcessing_form" data-source="custom,openrouter"> | 3453 | <div id="prompt_post_porcessing_form" data-source="custom,openrouter"> |
| 3428 | <h4 data-i18n="Prompt Post-Processing">Prompt Post-Processing</h4> | 3454 | <h4 data-i18n="Prompt Post-Processing">Prompt Post-Processing</h4> |
| 3429 | <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."> | 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 | || (secret_state[SECRET_KEYS.ZEROONEAI] && oai_settings.chat_completion_source == chat_completion_sources.ZEROONEAI) | 409 | || (secret_state[SECRET_KEYS.ZEROONEAI] && oai_settings.chat_completion_source == chat_completion_sources.ZEROONEAI) |
| 410 | || (secret_state[SECRET_KEYS.NANOGPT] && oai_settings.chat_completion_source == chat_completion_sources.NANOGPT) | 410 | || (secret_state[SECRET_KEYS.NANOGPT] && oai_settings.chat_completion_source == chat_completion_sources.NANOGPT) |
| 411 | || (secret_state[SECRET_KEYS.DEEPSEEK] && oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK) | 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 | || (isValidUrl(oai_settings.custom_url) && oai_settings.chat_completion_source == chat_completion_sources.CUSTOM) | 413 | || (isValidUrl(oai_settings.custom_url) && oai_settings.chat_completion_source == chat_completion_sources.CUSTOM) |
| 413 | ) { | 414 | ) { |
| 414 | $('#api_button_openai').trigger('click'); | 415 | $('#api_button_openai').trigger('click'); |
| @@ -428,6 +428,7 @@ jQuery(async function () { | |||
| 428 | 'zerooneai': SECRET_KEYS.ZEROONEAI, | 428 | 'zerooneai': SECRET_KEYS.ZEROONEAI, |
| 429 | 'groq': SECRET_KEYS.GROQ, | 429 | 'groq': SECRET_KEYS.GROQ, |
| 430 | 'cohere': SECRET_KEYS.COHERE, | 430 | 'cohere': SECRET_KEYS.COHERE, |
| 431 | 'xai': SECRET_KEYS.XAI, | ||
| 431 | }; | 432 | }; |
| 432 | 433 | ||
| 433 | if (chatCompletionApis[api] && secret_state[chatCompletionApis[api]]) { | 434 | if (chatCompletionApis[api] && secret_state[chatCompletionApis[api]]) { |
| @@ -31,6 +31,7 @@ | |||
| 31 | <option value="openrouter">OpenRouter</option> | 31 | <option value="openrouter">OpenRouter</option> |
| 32 | <option value="ooba" data-i18n="Text Generation WebUI (oobabooga)">Text Generation WebUI (oobabooga)</option> | 32 | <option value="ooba" data-i18n="Text Generation WebUI (oobabooga)">Text Generation WebUI (oobabooga)</option> |
| 33 | <option value="vllm">vLLM</option> | 33 | <option value="vllm">vLLM</option> |
| 34 | <option value="xai">xAI (Grok)</option> | ||
| 34 | </select> | 35 | </select> |
| 35 | </div> | 36 | </div> |
| 36 | <div class="flex1 flex-container flexFlowColumn flexNoGap"> | 37 | <div class="flex1 flex-container flexFlowColumn flexNoGap"> |
| @@ -134,6 +135,8 @@ | |||
| 134 | <option data-type="koboldcpp" value="koboldcpp_current" data-i18n="currently_loaded">[Currently loaded]</option> | 135 | <option data-type="koboldcpp" value="koboldcpp_current" data-i18n="currently_loaded">[Currently loaded]</option> |
| 135 | <option data-type="vllm" value="vllm_current" data-i18n="currently_selected">[Currently selected]</option> | 136 | <option data-type="vllm" value="vllm_current" data-i18n="currently_selected">[Currently selected]</option> |
| 136 | <option data-type="custom" value="custom_current" data-i18n="currently_selected">[Currently selected]</option> | 137 | <option data-type="custom" value="custom_current" data-i18n="currently_selected">[Currently selected]</option> |
| 138 | <option data-type="xai" value="grok-2-vision-1212">grok-2-vision-1212</option> | ||
| 139 | <option data-type="xai" value="grok-vision-beta">grok-vision-beta</option> | ||
| 137 | </select> | 140 | </select> |
| 138 | </div> | 141 | </div> |
| 139 | <div data-type="ollama"> | 142 | <div data-type="ollama"> |
| @@ -153,6 +153,10 @@ function throwIfInvalidModel(useReverseProxy) { | |||
| 153 | throw new Error('Cohere API key is not set.'); | 153 | throw new Error('Cohere API key is not set.'); |
| 154 | } | 154 | } |
| 155 | 155 | ||
| 156 | if (extension_settings.caption.multimodal_api === 'xai' && !secret_state[SECRET_KEYS.XAI]) { | ||
| 157 | throw new Error('xAI API key is not set.'); | ||
| 158 | } | ||
| 159 | |||
| 156 | if (extension_settings.caption.multimodal_api === 'ollama' && !textgenerationwebui_settings.server_urls[textgen_types.OLLAMA]) { | 160 | if (extension_settings.caption.multimodal_api === 'ollama' && !textgenerationwebui_settings.server_urls[textgen_types.OLLAMA]) { |
| 157 | throw new Error('Ollama server URL is not set.'); | 161 | throw new Error('Ollama server URL is not set.'); |
| 158 | } | 162 | } |
| @@ -81,6 +81,7 @@ const sources = { | |||
| 81 | nanogpt: 'nanogpt', | 81 | nanogpt: 'nanogpt', |
| 82 | bfl: 'bfl', | 82 | bfl: 'bfl', |
| 83 | falai: 'falai', | 83 | falai: 'falai', |
| 84 | xai: 'xai', | ||
| 84 | }; | 85 | }; |
| 85 | 86 | ||
| 86 | const initiators = { | 87 | const initiators = { |
| @@ -1303,6 +1304,7 @@ async function onModelChange() { | |||
| 1303 | sources.nanogpt, | 1304 | sources.nanogpt, |
| 1304 | sources.bfl, | 1305 | sources.bfl, |
| 1305 | sources.falai, | 1306 | sources.falai, |
| 1307 | sources.xai, | ||
| 1306 | ]; | 1308 | ]; |
| 1307 | 1309 | ||
| 1308 | if (cloudSources.includes(extension_settings.sd.source)) { | 1310 | if (cloudSources.includes(extension_settings.sd.source)) { |
| @@ -1518,6 +1520,9 @@ async function loadSamplers() { | |||
| 1518 | case sources.bfl: | 1520 | case sources.bfl: |
| 1519 | samplers = ['N/A']; | 1521 | samplers = ['N/A']; |
| 1520 | break; | 1522 | break; |
| 1523 | case sources.xai: | ||
| 1524 | samplers = ['N/A']; | ||
| 1525 | break; | ||
| 1521 | } | 1526 | } |
| 1522 | 1527 | ||
| 1523 | for (const sampler of samplers) { | 1528 | for (const sampler of samplers) { |
| @@ -1708,6 +1713,9 @@ async function loadModels() { | |||
| 1708 | case sources.falai: | 1713 | case sources.falai: |
| 1709 | models = await loadFalaiModels(); | 1714 | models = await loadFalaiModels(); |
| 1710 | break; | 1715 | break; |
| 1716 | case sources.xai: | ||
| 1717 | models = await loadXAIModels(); | ||
| 1718 | break; | ||
| 1711 | } | 1719 | } |
| 1712 | 1720 | ||
| 1713 | for (const model of models) { | 1721 | for (const model of models) { |
| @@ -1760,6 +1768,12 @@ async function loadFalaiModels() { | |||
| 1760 | return []; | 1768 | return []; |
| 1761 | } | 1769 | } |
| 1762 | 1770 | ||
| 1771 | async function loadXAIModels() { | ||
| 1772 | return [ | ||
| 1773 | { value: 'grok-2-image-1212', text: 'grok-2-image-1212' }, | ||
| 1774 | ]; | ||
| 1775 | } | ||
| 1776 | |||
| 1763 | async function loadPollinationsModels() { | 1777 | async function loadPollinationsModels() { |
| 1764 | const result = await fetch('/api/sd/pollinations/models', { | 1778 | const result = await fetch('/api/sd/pollinations/models', { |
| 1765 | method: 'POST', | 1779 | method: 'POST', |
| @@ -2081,6 +2095,9 @@ async function loadSchedulers() { | |||
| 2081 | case sources.falai: | 2095 | case sources.falai: |
| 2082 | schedulers = ['N/A']; | 2096 | schedulers = ['N/A']; |
| 2083 | break; | 2097 | break; |
| 2098 | case sources.xai: | ||
| 2099 | schedulers = ['N/A']; | ||
| 2100 | break; | ||
| 2084 | } | 2101 | } |
| 2085 | 2102 | ||
| 2086 | for (const scheduler of schedulers) { | 2103 | for (const scheduler of schedulers) { |
| @@ -2166,6 +2183,12 @@ async function loadVaes() { | |||
| 2166 | case sources.bfl: | 2183 | case sources.bfl: |
| 2167 | vaes = ['N/A']; | 2184 | vaes = ['N/A']; |
| 2168 | break; | 2185 | break; |
| 2186 | case sources.falai: | ||
| 2187 | vaes = ['N/A']; | ||
| 2188 | break; | ||
| 2189 | case sources.xai: | ||
| 2190 | vaes = ['N/A']; | ||
| 2191 | break; | ||
| 2169 | } | 2192 | } |
| 2170 | 2193 | ||
| 2171 | for (const vae of vaes) { | 2194 | for (const vae of vaes) { |
| @@ -2735,6 +2758,9 @@ async function sendGenerationRequest(generationType, prompt, additionalNegativeP | |||
| 2735 | case sources.falai: | 2758 | case sources.falai: |
| 2736 | result = await generateFalaiImage(prefixedPrompt, negativePrompt, signal); | 2759 | result = await generateFalaiImage(prefixedPrompt, negativePrompt, signal); |
| 2737 | break; | 2760 | break; |
| 2761 | case sources.xai: | ||
| 2762 | result = await generateXAIImage(prefixedPrompt, negativePrompt, signal); | ||
| 2763 | break; | ||
| 2738 | } | 2764 | } |
| 2739 | 2765 | ||
| 2740 | if (!result.data) { | 2766 | if (!result.data) { |
| @@ -3464,6 +3490,33 @@ async function generateBflImage(prompt, signal) { | |||
| 3464 | } | 3490 | } |
| 3465 | 3491 | ||
| 3466 | /** | 3492 | /** |
| 3493 | * Generates an image using the xAI API. | ||
| 3494 | * @param {string} prompt The main instruction used to guide the image generation. | ||
| 3495 | * @param {string} _negativePrompt Negative prompt is not used in this API | ||
| 3496 | * @param {AbortSignal} signal An AbortSignal object that can be used to cancel the request. | ||
| 3497 | * @returns {Promise<{format: string, data: string}>} A promise that resolves when the image generation and processing are complete. | ||
| 3498 | */ | ||
| 3499 | async function generateXAIImage(prompt, _negativePrompt, signal) { | ||
| 3500 | const result = await fetch('/api/sd/xai/generate', { | ||
| 3501 | method: 'POST', | ||
| 3502 | headers: getRequestHeaders(), | ||
| 3503 | signal: signal, | ||
| 3504 | body: JSON.stringify({ | ||
| 3505 | prompt: prompt, | ||
| 3506 | model: extension_settings.sd.model, | ||
| 3507 | }), | ||
| 3508 | }); | ||
| 3509 | |||
| 3510 | if (result.ok) { | ||
| 3511 | const data = await result.json(); | ||
| 3512 | return { format: 'jpg', data: data.image }; | ||
| 3513 | } else { | ||
| 3514 | const text = await result.text(); | ||
| 3515 | throw new Error(text); | ||
| 3516 | } | ||
| 3517 | } | ||
| 3518 | |||
| 3519 | /** | ||
| 3467 | * Generates an image using the FAL.AI API. | 3520 | * Generates an image using the FAL.AI API. |
| 3468 | * @param {string} prompt - The main instruction used to guide the image generation. | 3521 | * @param {string} prompt - The main instruction used to guide the image generation. |
| 3469 | * @param {string} negativePrompt - The negative prompt used to guide the image generation. | 3522 | * @param {string} negativePrompt - The negative prompt used to guide the image generation. |
| @@ -3782,6 +3835,8 @@ function isValidState() { | |||
| 3782 | return secret_state[SECRET_KEYS.BFL]; | 3835 | return secret_state[SECRET_KEYS.BFL]; |
| 3783 | case sources.falai: | 3836 | case sources.falai: |
| 3784 | return secret_state[SECRET_KEYS.FALAI]; | 3837 | return secret_state[SECRET_KEYS.FALAI]; |
| 3838 | case sources.xai: | ||
| 3839 | return secret_state[SECRET_KEYS.XAI]; | ||
| 3785 | } | 3840 | } |
| 3786 | } | 3841 | } |
| 3787 | 3842 | ||
| @@ -52,6 +52,7 @@ | |||
| 52 | <option value="auto">Stable Diffusion Web UI (AUTOMATIC1111)</option> | 52 | <option value="auto">Stable Diffusion Web UI (AUTOMATIC1111)</option> |
| 53 | <option value="horde">Stable Horde</option> | 53 | <option value="horde">Stable Horde</option> |
| 54 | <option value="togetherai">TogetherAI</option> | 54 | <option value="togetherai">TogetherAI</option> |
| 55 | <option value="xai">xAI (Grok)</option> | ||
| 55 | </select> | 56 | </select> |
| 56 | <div data-sd-source="auto"> | 57 | <div data-sd-source="auto"> |
| 57 | <label for="sd_auto_url">SD Web UI URL</label> | 58 | <label for="sd_auto_url">SD Web UI URL</label> |
| @@ -184,6 +184,7 @@ export const chat_completion_sources = { | |||
| 184 | ZEROONEAI: '01ai', | 184 | ZEROONEAI: '01ai', |
| 185 | NANOGPT: 'nanogpt', | 185 | NANOGPT: 'nanogpt', |
| 186 | DEEPSEEK: 'deepseek', | 186 | DEEPSEEK: 'deepseek', |
| 187 | XAI: 'xai', | ||
| 187 | }; | 188 | }; |
| 188 | 189 | ||
| 189 | const character_names_behavior = { | 190 | const character_names_behavior = { |
| @@ -257,6 +258,7 @@ export const settingsToUpdate = { | |||
| 257 | nanogpt_model: ['#model_nanogpt_select', 'nanogpt_model', false], | 258 | nanogpt_model: ['#model_nanogpt_select', 'nanogpt_model', false], |
| 258 | deepseek_model: ['#model_deepseek_select', 'deepseek_model', false], | 259 | deepseek_model: ['#model_deepseek_select', 'deepseek_model', false], |
| 259 | zerooneai_model: ['#model_01ai_select', 'zerooneai_model', false], | 260 | zerooneai_model: ['#model_01ai_select', 'zerooneai_model', false], |
| 261 | xai_model: ['#model_xai_select', 'xai_model', false], | ||
| 260 | custom_model: ['#custom_model_id', 'custom_model', false], | 262 | custom_model: ['#custom_model_id', 'custom_model', false], |
| 261 | custom_url: ['#custom_api_url_text', 'custom_url', false], | 263 | custom_url: ['#custom_api_url_text', 'custom_url', false], |
| 262 | custom_include_body: ['#custom_include_body', 'custom_include_body', false], | 264 | custom_include_body: ['#custom_include_body', 'custom_include_body', false], |
| @@ -345,6 +347,7 @@ const default_settings = { | |||
| 345 | nanogpt_model: 'gpt-4o-mini', | 347 | nanogpt_model: 'gpt-4o-mini', |
| 346 | zerooneai_model: 'yi-large', | 348 | zerooneai_model: 'yi-large', |
| 347 | deepseek_model: 'deepseek-chat', | 349 | deepseek_model: 'deepseek-chat', |
| 350 | xai_model: 'grok-3-beta', | ||
| 348 | custom_model: '', | 351 | custom_model: '', |
| 349 | custom_url: '', | 352 | custom_url: '', |
| 350 | custom_include_body: '', | 353 | custom_include_body: '', |
| @@ -425,6 +428,7 @@ const oai_settings = { | |||
| 425 | nanogpt_model: 'gpt-4o-mini', | 428 | nanogpt_model: 'gpt-4o-mini', |
| 426 | zerooneai_model: 'yi-large', | 429 | zerooneai_model: 'yi-large', |
| 427 | deepseek_model: 'deepseek-chat', | 430 | deepseek_model: 'deepseek-chat', |
| 431 | xai_model: 'grok-3-beta', | ||
| 428 | custom_model: '', | 432 | custom_model: '', |
| 429 | custom_url: '', | 433 | custom_url: '', |
| 430 | custom_include_body: '', | 434 | custom_include_body: '', |
| @@ -1644,6 +1648,8 @@ export function getChatCompletionModel(source = null) { | |||
| 1644 | return oai_settings.nanogpt_model; | 1648 | return oai_settings.nanogpt_model; |
| 1645 | case chat_completion_sources.DEEPSEEK: | 1649 | case chat_completion_sources.DEEPSEEK: |
| 1646 | return oai_settings.deepseek_model; | 1650 | return oai_settings.deepseek_model; |
| 1651 | case chat_completion_sources.XAI: | ||
| 1652 | return oai_settings.xai_model; | ||
| 1647 | default: | 1653 | default: |
| 1648 | throw new Error(`Unknown chat completion source: ${activeSource}`); | 1654 | throw new Error(`Unknown chat completion source: ${activeSource}`); |
| 1649 | } | 1655 | } |
| @@ -1961,6 +1967,7 @@ async function sendOpenAIRequest(type, messages, signal) { | |||
| 1961 | const is01AI = oai_settings.chat_completion_source == chat_completion_sources.ZEROONEAI; | 1967 | const is01AI = oai_settings.chat_completion_source == chat_completion_sources.ZEROONEAI; |
| 1962 | const isNano = oai_settings.chat_completion_source == chat_completion_sources.NANOGPT; | 1968 | const isNano = oai_settings.chat_completion_source == chat_completion_sources.NANOGPT; |
| 1963 | const isDeepSeek = oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK; | 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 | const isTextCompletion = isOAI && textCompletionModels.includes(oai_settings.openai_model); | 1971 | const isTextCompletion = isOAI && textCompletionModels.includes(oai_settings.openai_model); |
| 1965 | const isQuiet = type === 'quiet'; | 1972 | const isQuiet = type === 'quiet'; |
| 1966 | const isImpersonate = type === 'impersonate'; | 1973 | const isImpersonate = type === 'impersonate'; |
| @@ -2033,7 +2040,7 @@ async function sendOpenAIRequest(type, messages, signal) { | |||
| 2033 | } | 2040 | } |
| 2034 | 2041 | ||
| 2035 | // Add logprobs request (currently OpenAI only, max 5 on their side) | 2042 | // Add logprobs request (currently OpenAI only, max 5 on their side) |
| 2036 | if (useLogprobs && (isOAI || isCustom || isDeepSeek)) { | 2043 | if (useLogprobs && (isOAI || isCustom || isDeepSeek || isXAI)) { |
| 2037 | generate_data['logprobs'] = 5; | 2044 | generate_data['logprobs'] = 5; |
| 2038 | } | 2045 | } |
| 2039 | 2046 | ||
| @@ -2152,7 +2159,18 @@ async function sendOpenAIRequest(type, messages, signal) { | |||
| 2152 | } | 2159 | } |
| 2153 | } | 2160 | } |
| 2154 | 2161 | ||
| 2155 | if ((isOAI || isOpenRouter || isMistral || isCustom || isCohere || isNano) && oai_settings.seed >= 0) { | 2162 | if (isXAI) { |
| 2163 | if (generate_data.model.includes('grok-3-mini')) { | ||
| 2164 | delete generate_data.presence_penalty; | ||
| 2165 | delete generate_data.frequency_penalty; | ||
| 2166 | } | ||
| 2167 | if (generate_data.model.includes('grok-vision')) { | ||
| 2168 | delete generate_data.tools; | ||
| 2169 | delete generate_data.tool_choice; | ||
| 2170 | } | ||
| 2171 | } | ||
| 2172 | |||
| 2173 | if ((isOAI || isOpenRouter || isMistral || isCustom || isCohere || isNano || isXAI) && oai_settings.seed >= 0) { | ||
| 2156 | generate_data['seed'] = oai_settings.seed; | 2174 | generate_data['seed'] = oai_settings.seed; |
| 2157 | } | 2175 | } |
| 2158 | 2176 | ||
| @@ -2278,6 +2296,11 @@ export function getStreamingReply(data, state, { chatCompletionSource = null, ov | |||
| 2278 | state.reasoning += (data.choices?.filter(x => x?.delta?.reasoning_content)?.[0]?.delta?.reasoning_content || ''); | 2296 | state.reasoning += (data.choices?.filter(x => x?.delta?.reasoning_content)?.[0]?.delta?.reasoning_content || ''); |
| 2279 | } | 2297 | } |
| 2280 | return data.choices?.[0]?.delta?.content || ''; | 2298 | return data.choices?.[0]?.delta?.content || ''; |
| 2299 | } else if (chat_completion_source === chat_completion_sources.XAI) { | ||
| 2300 | if (show_thoughts) { | ||
| 2301 | state.reasoning += (data.choices?.filter(x => x?.delta?.reasoning_content)?.[0]?.delta?.reasoning_content || ''); | ||
| 2302 | } | ||
| 2303 | return data.choices?.[0]?.delta?.content || ''; | ||
| 2281 | } else if (chat_completion_source === chat_completion_sources.OPENROUTER) { | 2304 | } else if (chat_completion_source === chat_completion_sources.OPENROUTER) { |
| 2282 | if (show_thoughts) { | 2305 | if (show_thoughts) { |
| 2283 | state.reasoning += (data.choices?.filter(x => x?.delta?.reasoning)?.[0]?.delta?.reasoning || ''); | 2306 | state.reasoning += (data.choices?.filter(x => x?.delta?.reasoning)?.[0]?.delta?.reasoning || ''); |
| @@ -2310,6 +2333,7 @@ function parseChatCompletionLogprobs(data) { | |||
| 2310 | switch (oai_settings.chat_completion_source) { | 2333 | switch (oai_settings.chat_completion_source) { |
| 2311 | case chat_completion_sources.OPENAI: | 2334 | case chat_completion_sources.OPENAI: |
| 2312 | case chat_completion_sources.DEEPSEEK: | 2335 | case chat_completion_sources.DEEPSEEK: |
| 2336 | case chat_completion_sources.XAI: | ||
| 2313 | case chat_completion_sources.CUSTOM: | 2337 | case chat_completion_sources.CUSTOM: |
| 2314 | if (!data.choices?.length) { | 2338 | if (!data.choices?.length) { |
| 2315 | return null; | 2339 | return null; |
| @@ -3231,6 +3255,7 @@ function loadOpenAISettings(data, settings) { | |||
| 3231 | oai_settings.nanogpt_model = settings.nanogpt_model ?? default_settings.nanogpt_model; | 3255 | oai_settings.nanogpt_model = settings.nanogpt_model ?? default_settings.nanogpt_model; |
| 3232 | oai_settings.deepseek_model = settings.deepseek_model ?? default_settings.deepseek_model; | 3256 | oai_settings.deepseek_model = settings.deepseek_model ?? default_settings.deepseek_model; |
| 3233 | oai_settings.zerooneai_model = settings.zerooneai_model ?? default_settings.zerooneai_model; | 3257 | oai_settings.zerooneai_model = settings.zerooneai_model ?? default_settings.zerooneai_model; |
| 3258 | oai_settings.xai_model = settings.xai_model ?? default_settings.xai_model; | ||
| 3234 | oai_settings.custom_model = settings.custom_model ?? default_settings.custom_model; | 3259 | oai_settings.custom_model = settings.custom_model ?? default_settings.custom_model; |
| 3235 | oai_settings.custom_url = settings.custom_url ?? default_settings.custom_url; | 3260 | oai_settings.custom_url = settings.custom_url ?? default_settings.custom_url; |
| 3236 | oai_settings.custom_include_body = settings.custom_include_body ?? default_settings.custom_include_body; | 3261 | oai_settings.custom_include_body = settings.custom_include_body ?? default_settings.custom_include_body; |
| @@ -3316,6 +3341,8 @@ function loadOpenAISettings(data, settings) { | |||
| 3316 | $('#model_deepseek_select').val(oai_settings.deepseek_model); | 3341 | $('#model_deepseek_select').val(oai_settings.deepseek_model); |
| 3317 | $(`#model_deepseek_select option[value="${oai_settings.deepseek_model}"`).prop('selected', true); | 3342 | $(`#model_deepseek_select option[value="${oai_settings.deepseek_model}"`).prop('selected', true); |
| 3318 | $('#model_01ai_select').val(oai_settings.zerooneai_model); | 3343 | $('#model_01ai_select').val(oai_settings.zerooneai_model); |
| 3344 | $('#model_xai_select').val(oai_settings.xai_model); | ||
| 3345 | $(`#model_xai_select option[value="${oai_settings.xai_model}"`).attr('selected', true); | ||
| 3319 | $('#custom_model_id').val(oai_settings.custom_model); | 3346 | $('#custom_model_id').val(oai_settings.custom_model); |
| 3320 | $('#custom_api_url_text').val(oai_settings.custom_url); | 3347 | $('#custom_api_url_text').val(oai_settings.custom_url); |
| 3321 | $('#openai_max_context').val(oai_settings.openai_max_context); | 3348 | $('#openai_max_context').val(oai_settings.openai_max_context); |
| @@ -4386,6 +4413,11 @@ async function onModelChange() { | |||
| 4386 | $('#custom_model_id').val(value).trigger('input'); | 4413 | $('#custom_model_id').val(value).trigger('input'); |
| 4387 | } | 4414 | } |
| 4388 | 4415 | ||
| 4416 | if ($(this).is('#model_xai_select')) { | ||
| 4417 | console.log('XAI model changed to', value); | ||
| 4418 | oai_settings.xai_model = value; | ||
| 4419 | } | ||
| 4420 | |||
| 4389 | if (oai_settings.chat_completion_source == chat_completion_sources.SCALE) { | 4421 | if (oai_settings.chat_completion_source == chat_completion_sources.SCALE) { |
| 4390 | if (oai_settings.max_context_unlocked) { | 4422 | if (oai_settings.max_context_unlocked) { |
| 4391 | $('#openai_max_context').attr('max', unlocked_max); | 4423 | $('#openai_max_context').attr('max', unlocked_max); |
| @@ -4641,6 +4673,22 @@ async function onModelChange() { | |||
| 4641 | $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input'); | 4673 | $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input'); |
| 4642 | } | 4674 | } |
| 4643 | 4675 | ||
| 4676 | if (oai_settings.chat_completion_source === chat_completion_sources.XAI) { | ||
| 4677 | if (oai_settings.max_context_unlocked) { | ||
| 4678 | $('#openai_max_context').attr('max', unlocked_max); | ||
| 4679 | } else if (oai_settings.xai_model.includes('grok-2-vision')) { | ||
| 4680 | $('#openai_max_context').attr('max', max_32k); | ||
| 4681 | } else if (oai_settings.xai_model.includes('grok-vision')) { | ||
| 4682 | $('#openai_max_context').attr('max', max_8k); | ||
| 4683 | } else { | ||
| 4684 | $('#openai_max_context').attr('max', max_128k); | ||
| 4685 | } | ||
| 4686 | |||
| 4687 | oai_settings.openai_max_context = Math.min(Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context); | ||
| 4688 | $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input'); | ||
| 4689 | $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input'); | ||
| 4690 | } | ||
| 4691 | |||
| 4644 | if (oai_settings.chat_completion_source === chat_completion_sources.COHERE) { | 4692 | if (oai_settings.chat_completion_source === chat_completion_sources.COHERE) { |
| 4645 | oai_settings.pres_pen_openai = Math.min(Math.max(0, oai_settings.pres_pen_openai), 1); | 4693 | oai_settings.pres_pen_openai = Math.min(Math.max(0, oai_settings.pres_pen_openai), 1); |
| 4646 | $('#pres_pen_openai').attr('max', 1).attr('min', 0).val(oai_settings.pres_pen_openai).trigger('input'); | 4694 | $('#pres_pen_openai').attr('max', 1).attr('min', 0).val(oai_settings.pres_pen_openai).trigger('input'); |
| @@ -4879,6 +4927,19 @@ async function onConnectButtonClick(e) { | |||
| 4879 | } | 4927 | } |
| 4880 | } | 4928 | } |
| 4881 | 4929 | ||
| 4930 | if (oai_settings.chat_completion_source === chat_completion_sources.XAI) { | ||
| 4931 | const api_key_xai = String($('#api_key_xai').val()).trim(); | ||
| 4932 | |||
| 4933 | if (api_key_xai.length) { | ||
| 4934 | await writeSecret(SECRET_KEYS.XAI, api_key_xai); | ||
| 4935 | } | ||
| 4936 | |||
| 4937 | if (!secret_state[SECRET_KEYS.XAI]) { | ||
| 4938 | console.log('No secret key saved for XAI'); | ||
| 4939 | return; | ||
| 4940 | } | ||
| 4941 | } | ||
| 4942 | |||
| 4882 | startStatusLoading(); | 4943 | startStatusLoading(); |
| 4883 | saveSettingsDebounced(); | 4944 | saveSettingsDebounced(); |
| 4884 | await getStatusOpen(); | 4945 | await getStatusOpen(); |
| @@ -4935,6 +4996,9 @@ function toggleChatCompletionForms() { | |||
| 4935 | else if (oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK) { | 4996 | else if (oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK) { |
| 4936 | $('#model_deepseek_select').trigger('change'); | 4997 | $('#model_deepseek_select').trigger('change'); |
| 4937 | } | 4998 | } |
| 4999 | else if (oai_settings.chat_completion_source == chat_completion_sources.XAI) { | ||
| 5000 | $('#model_xai_select').trigger('change'); | ||
| 5001 | } | ||
| 4938 | $('[data-source]').each(function () { | 5002 | $('[data-source]').each(function () { |
| 4939 | const validSources = $(this).data('source').split(','); | 5003 | const validSources = $(this).data('source').split(','); |
| 4940 | $(this).toggle(validSources.includes(oai_settings.chat_completion_source)); | 5004 | $(this).toggle(validSources.includes(oai_settings.chat_completion_source)); |
| @@ -5073,6 +5137,8 @@ export function isImageInliningSupported() { | |||
| 5073 | 'pixtral-large-2411', | 5137 | 'pixtral-large-2411', |
| 5074 | 'c4ai-aya-vision-8b', | 5138 | 'c4ai-aya-vision-8b', |
| 5075 | 'c4ai-aya-vision-32b', | 5139 | 'c4ai-aya-vision-32b', |
| 5140 | 'grok-2-vision', | ||
| 5141 | 'grok-vision', | ||
| 5076 | ]; | 5142 | ]; |
| 5077 | 5143 | ||
| 5078 | switch (oai_settings.chat_completion_source) { | 5144 | switch (oai_settings.chat_completion_source) { |
| @@ -5092,6 +5158,8 @@ export function isImageInliningSupported() { | |||
| 5092 | return visionSupportedModels.some(model => oai_settings.mistralai_model.includes(model)); | 5158 | return visionSupportedModels.some(model => oai_settings.mistralai_model.includes(model)); |
| 5093 | case chat_completion_sources.COHERE: | 5159 | case chat_completion_sources.COHERE: |
| 5094 | return visionSupportedModels.some(model => oai_settings.cohere_model.includes(model)); | 5160 | return visionSupportedModels.some(model => oai_settings.cohere_model.includes(model)); |
| 5161 | case chat_completion_sources.XAI: | ||
| 5162 | return visionSupportedModels.some(model => oai_settings.xai_model.includes(model)); | ||
| 5095 | default: | 5163 | default: |
| 5096 | return false; | 5164 | return false; |
| 5097 | } | 5165 | } |
| @@ -5688,6 +5756,7 @@ export function initOpenAI() { | |||
| 5688 | $('#model_deepseek_select').on('change', onModelChange); | 5756 | $('#model_deepseek_select').on('change', onModelChange); |
| 5689 | $('#model_01ai_select').on('change', onModelChange); | 5757 | $('#model_01ai_select').on('change', onModelChange); |
| 5690 | $('#model_custom_select').on('change', onModelChange); | 5758 | $('#model_custom_select').on('change', onModelChange); |
| 5759 | $('#model_xai_select').on('change', onModelChange); | ||
| 5691 | $('#settings_preset_openai').on('change', onSettingsPresetChange); | 5760 | $('#settings_preset_openai').on('change', onSettingsPresetChange); |
| 5692 | $('#new_oai_preset').on('click', onNewPresetClick); | 5761 | $('#new_oai_preset').on('click', onNewPresetClick); |
| 5693 | $('#delete_oai_preset').on('click', onDeletePresetClick); | 5762 | $('#delete_oai_preset').on('click', onDeletePresetClick); |
| @@ -109,6 +109,8 @@ export function extractReasoningFromData(data, { | |||
| 109 | switch (chatCompletionSource ?? oai_settings.chat_completion_source) { | 109 | switch (chatCompletionSource ?? oai_settings.chat_completion_source) { |
| 110 | case chat_completion_sources.DEEPSEEK: | 110 | case chat_completion_sources.DEEPSEEK: |
| 111 | return data?.choices?.[0]?.message?.reasoning_content ?? ''; | 111 | return data?.choices?.[0]?.message?.reasoning_content ?? ''; |
| 112 | case chat_completion_sources.XAI: | ||
| 113 | return data?.choices?.[0]?.message?.reasoning_content ?? ''; | ||
| 112 | case chat_completion_sources.OPENROUTER: | 114 | case chat_completion_sources.OPENROUTER: |
| 113 | return data?.choices?.[0]?.message?.reasoning ?? ''; | 115 | return data?.choices?.[0]?.message?.reasoning ?? ''; |
| 114 | case chat_completion_sources.MAKERSUITE: | 116 | case chat_completion_sources.MAKERSUITE: |
| @@ -42,6 +42,7 @@ export const SECRET_KEYS = { | |||
| 42 | DEEPSEEK: 'api_key_deepseek', | 42 | DEEPSEEK: 'api_key_deepseek', |
| 43 | SERPER: 'api_key_serper', | 43 | SERPER: 'api_key_serper', |
| 44 | FALAI: 'api_key_falai', | 44 | FALAI: 'api_key_falai', |
| 45 | XAI: 'api_key_xai', | ||
| 45 | }; | 46 | }; |
| 46 | 47 | ||
| 47 | const INPUT_MAP = { | 48 | const INPUT_MAP = { |
| @@ -76,6 +77,7 @@ const INPUT_MAP = { | |||
| 76 | [SECRET_KEYS.NANOGPT]: '#api_key_nanogpt', | 77 | [SECRET_KEYS.NANOGPT]: '#api_key_nanogpt', |
| 77 | [SECRET_KEYS.GENERIC]: '#api_key_generic', | 78 | [SECRET_KEYS.GENERIC]: '#api_key_generic', |
| 78 | [SECRET_KEYS.DEEPSEEK]: '#api_key_deepseek', | 79 | [SECRET_KEYS.DEEPSEEK]: '#api_key_deepseek', |
| 80 | [SECRET_KEYS.XAI]: '#api_key_xai', | ||
| 79 | }; | 81 | }; |
| 80 | 82 | ||
| 81 | async function clearSecret() { | 83 | async function clearSecret() { |
| @@ -3942,6 +3942,7 @@ function getModelOptions(quiet) { | |||
| 3942 | { id: 'model_nanogpt_select', api: 'openai', type: chat_completion_sources.NANOGPT }, | 3942 | { id: 'model_nanogpt_select', api: 'openai', type: chat_completion_sources.NANOGPT }, |
| 3943 | { id: 'model_01ai_select', api: 'openai', type: chat_completion_sources.ZEROONEAI }, | 3943 | { id: 'model_01ai_select', api: 'openai', type: chat_completion_sources.ZEROONEAI }, |
| 3944 | { id: 'model_deepseek_select', api: 'openai', type: chat_completion_sources.DEEPSEEK }, | 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 | { id: 'model_novel_select', api: 'novel', type: null }, | 3946 | { id: 'model_novel_select', api: 'novel', type: null }, |
| 3946 | { id: 'horde_model', api: 'koboldhorde', type: null }, | 3947 | { id: 'horde_model', api: 'koboldhorde', type: null }, |
| 3947 | ]; | 3948 | ]; |
| @@ -586,6 +586,7 @@ export class ToolManager { | |||
| 586 | chat_completion_sources.DEEPSEEK, | 586 | chat_completion_sources.DEEPSEEK, |
| 587 | chat_completion_sources.MAKERSUITE, | 587 | chat_completion_sources.MAKERSUITE, |
| 588 | chat_completion_sources.AI21, | 588 | chat_completion_sources.AI21, |
| 589 | chat_completion_sources.XAI, | ||
| 589 | ]; | 590 | ]; |
| 590 | return supportedSources.includes(oai_settings.chat_completion_source); | 591 | return supportedSources.includes(oai_settings.chat_completion_source); |
| 591 | } | 592 | } |
| @@ -176,6 +176,7 @@ export const CHAT_COMPLETION_SOURCES = { | |||
| 176 | ZEROONEAI: '01ai', | 176 | ZEROONEAI: '01ai', |
| 177 | NANOGPT: 'nanogpt', | 177 | NANOGPT: 'nanogpt', |
| 178 | DEEPSEEK: 'deepseek', | 178 | DEEPSEEK: 'deepseek', |
| 179 | XAI: 'xai', | ||
| 179 | }; | 180 | }; |
| 180 | 181 | ||
| 181 | /** | 182 | /** |
| @@ -23,6 +23,7 @@ import { | |||
| 23 | convertCohereMessages, | 23 | convertCohereMessages, |
| 24 | convertMistralMessages, | 24 | convertMistralMessages, |
| 25 | convertAI21Messages, | 25 | convertAI21Messages, |
| 26 | convertXAIMessages, | ||
| 26 | mergeMessages, | 27 | mergeMessages, |
| 27 | cachingAtDepthForOpenRouterClaude, | 28 | cachingAtDepthForOpenRouterClaude, |
| 28 | cachingAtDepthForClaude, | 29 | cachingAtDepthForClaude, |
| @@ -53,6 +54,7 @@ const API_01AI = 'https://api.lingyiwanwu.com/v1'; | |||
| 53 | const API_AI21 = 'https://api.ai21.com/studio/v1'; | 54 | const API_AI21 = 'https://api.ai21.com/studio/v1'; |
| 54 | const API_NANOGPT = 'https://nano-gpt.com/api/v1'; | 55 | const API_NANOGPT = 'https://nano-gpt.com/api/v1'; |
| 55 | const API_DEEPSEEK = 'https://api.deepseek.com/beta'; | 56 | const API_DEEPSEEK = 'https://api.deepseek.com/beta'; |
| 57 | const API_XAI = 'https://api.x.ai/v1'; | ||
| 56 | 58 | ||
| 57 | /** | 59 | /** |
| 58 | * Applies a post-processing step to the generated messages. | 60 | * Applies a post-processing step to the generated messages. |
| @@ -872,6 +874,9 @@ router.post('/status', async function (request, response_getstatus_openai) { | |||
| 872 | api_url = new URL(request.body.reverse_proxy || API_DEEPSEEK.replace('/beta', '')); | 874 | api_url = new URL(request.body.reverse_proxy || API_DEEPSEEK.replace('/beta', '')); |
| 873 | api_key_openai = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.DEEPSEEK); | 875 | api_key_openai = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.DEEPSEEK); |
| 874 | headers = {}; | 876 | headers = {}; |
| 877 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.XAI) { | ||
| 878 | api_url = API_XAI; | ||
| 879 | api_key_openai = readSecret(request.user.directories, SECRET_KEYS.XAI); | ||
| 875 | } else { | 880 | } else { |
| 876 | console.warn('This chat completion source is not supported yet.'); | 881 | console.warn('This chat completion source is not supported yet.'); |
| 877 | return response_getstatus_openai.status(400).send({ error: true }); | 882 | return response_getstatus_openai.status(400).send({ error: true }); |
| @@ -1150,6 +1155,12 @@ router.post('/generate', function (request, response) { | |||
| 1150 | apiKey = readSecret(request.user.directories, SECRET_KEYS.ZEROONEAI); | 1155 | apiKey = readSecret(request.user.directories, SECRET_KEYS.ZEROONEAI); |
| 1151 | headers = {}; | 1156 | headers = {}; |
| 1152 | bodyParams = {}; | 1157 | bodyParams = {}; |
| 1158 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.XAI) { | ||
| 1159 | apiUrl = API_XAI; | ||
| 1160 | apiKey = readSecret(request.user.directories, SECRET_KEYS.XAI); | ||
| 1161 | headers = {}; | ||
| 1162 | bodyParams = {}; | ||
| 1163 | request.body.messages = convertXAIMessages(request.body.messages, getPromptNames(request)); | ||
| 1153 | } else { | 1164 | } else { |
| 1154 | console.warn('This chat completion source is not supported yet.'); | 1165 | console.warn('This chat completion source is not supported yet.'); |
| 1155 | return response.status(400).send({ error: true }); | 1166 | return response.status(400).send({ error: true }); |
| @@ -1162,6 +1173,12 @@ router.post('/generate', function (request, response) { | |||
| 1162 | } | 1173 | } |
| 1163 | } | 1174 | } |
| 1164 | 1175 | ||
| 1176 | if ([CHAT_COMPLETION_SOURCES.XAI].includes(request.body.chat_completion_source)) { | ||
| 1177 | if (['grok-3-mini-beta', 'grok-3-mini-fast-beta'].includes(request.body.model)) { | ||
| 1178 | bodyParams['reasoning_effort'] = request.body.reasoning_effort === 'high' ? 'high' : 'low'; | ||
| 1179 | } | ||
| 1180 | } | ||
| 1181 | |||
| 1165 | if (!apiKey && !request.body.reverse_proxy && request.body.chat_completion_source !== CHAT_COMPLETION_SOURCES.CUSTOM) { | 1182 | if (!apiKey && !request.body.reverse_proxy && request.body.chat_completion_source !== CHAT_COMPLETION_SOURCES.CUSTOM) { |
| 1166 | console.warn('OpenAI API key is missing.'); | 1183 | console.warn('OpenAI API key is missing.'); |
| 1167 | return response.status(400).send({ error: true }); | 1184 | return response.status(400).send({ error: true }); |
| @@ -65,6 +65,10 @@ router.post('/caption-image', async (request, response) => { | |||
| 65 | key = readSecret(request.user.directories, SECRET_KEYS.COHERE); | 65 | key = readSecret(request.user.directories, SECRET_KEYS.COHERE); |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | if (request.body.api === 'xai') { | ||
| 69 | key = readSecret(request.user.directories, SECRET_KEYS.XAI); | ||
| 70 | } | ||
| 71 | |||
| 68 | if (!key && !request.body.reverse_proxy && ['custom', 'ooba', 'koboldcpp', 'vllm'].includes(request.body.api) === false) { | 72 | if (!key && !request.body.reverse_proxy && ['custom', 'ooba', 'koboldcpp', 'vllm'].includes(request.body.api) === false) { |
| 69 | console.warn('No key found for API', request.body.api); | 73 | console.warn('No key found for API', request.body.api); |
| 70 | return response.sendStatus(400); | 74 | return response.sendStatus(400); |
| @@ -134,6 +138,10 @@ router.post('/caption-image', async (request, response) => { | |||
| 134 | apiUrl = 'https://api.cohere.ai/v2/chat'; | 138 | apiUrl = 'https://api.cohere.ai/v2/chat'; |
| 135 | } | 139 | } |
| 136 | 140 | ||
| 141 | if (request.body.api === 'xai') { | ||
| 142 | apiUrl = 'https://api.x.ai/v1/chat/completions'; | ||
| 143 | } | ||
| 144 | |||
| 137 | if (request.body.api === 'ooba') { | 145 | if (request.body.api === 'ooba') { |
| 138 | apiUrl = `${trimV1(request.body.server_url)}/v1/chat/completions`; | 146 | apiUrl = `${trimV1(request.body.server_url)}/v1/chat/completions`; |
| 139 | const imgMessage = body.messages.pop(); | 147 | const imgMessage = body.messages.pop(); |
| @@ -52,6 +52,7 @@ export const SECRET_KEYS = { | |||
| 52 | GENERIC: 'api_key_generic', | 52 | GENERIC: 'api_key_generic', |
| 53 | DEEPSEEK: 'api_key_deepseek', | 53 | DEEPSEEK: 'api_key_deepseek', |
| 54 | SERPER: 'api_key_serper', | 54 | SERPER: 'api_key_serper', |
| 55 | XAI: 'api_key_xai', | ||
| 55 | }; | 56 | }; |
| 56 | 57 | ||
| 57 | // These are the keys that are safe to expose, even if allowKeysExposure is false | 58 | // These are the keys that are safe to expose, even if allowKeysExposure is false |
| @@ -627,8 +627,8 @@ together.post('/models', async (request, response) => { | |||
| 627 | } | 627 | } |
| 628 | 628 | ||
| 629 | const models = data | 629 | const models = data |
| 630 | .filter(x => x.display_type === 'image') | 630 | .filter(x => x.type === 'image') |
| 631 | .map(x => ({ value: x.name, text: x.display_name })); | 631 | .map(x => ({ value: x.id, text: x.display_name })); |
| 632 | 632 | ||
| 633 | return response.send(models); | 633 | return response.send(models); |
| 634 | } catch (error) { | 634 | } catch (error) { |
| @@ -1246,6 +1246,7 @@ falai.post('/generate', async (request, response) => { | |||
| 1246 | 'Authorization': `Key ${key}`, | 1246 | 'Authorization': `Key ${key}`, |
| 1247 | }, | 1247 | }, |
| 1248 | }); | 1248 | }); |
| 1249 | /** @type {any} */ | ||
| 1249 | const resultData = await resultFetch.json(); | 1250 | const resultData = await resultFetch.json(); |
| 1250 | 1251 | ||
| 1251 | if (resultData.detail !== null && resultData.detail !== undefined) { | 1252 | if (resultData.detail !== null && resultData.detail !== undefined) { |
| @@ -1271,6 +1272,56 @@ falai.post('/generate', async (request, response) => { | |||
| 1271 | } | 1272 | } |
| 1272 | }); | 1273 | }); |
| 1273 | 1274 | ||
| 1275 | const xai = express.Router(); | ||
| 1276 | |||
| 1277 | xai.post('/generate', async (request, response) => { | ||
| 1278 | try { | ||
| 1279 | const key = readSecret(request.user.directories, SECRET_KEYS.XAI); | ||
| 1280 | |||
| 1281 | if (!key) { | ||
| 1282 | console.warn('xAI key not found.'); | ||
| 1283 | return response.sendStatus(400); | ||
| 1284 | } | ||
| 1285 | |||
| 1286 | const requestBody = { | ||
| 1287 | prompt: request.body.prompt, | ||
| 1288 | model: request.body.model, | ||
| 1289 | response_format: 'b64_json', | ||
| 1290 | }; | ||
| 1291 | |||
| 1292 | console.debug('xAI request:', requestBody); | ||
| 1293 | |||
| 1294 | const result = await fetch('https://api.x.ai/v1/images/generations', { | ||
| 1295 | method: 'POST', | ||
| 1296 | body: JSON.stringify(requestBody), | ||
| 1297 | headers: { | ||
| 1298 | 'Content-Type': 'application/json', | ||
| 1299 | 'Authorization': `Bearer ${key}`, | ||
| 1300 | }, | ||
| 1301 | }); | ||
| 1302 | |||
| 1303 | if (!result.ok) { | ||
| 1304 | const text = await result.text(); | ||
| 1305 | console.warn('xAI returned an error.', text); | ||
| 1306 | return response.sendStatus(500); | ||
| 1307 | } | ||
| 1308 | |||
| 1309 | /** @type {any} */ | ||
| 1310 | const data = await result.json(); | ||
| 1311 | |||
| 1312 | const image = data?.data?.[0]?.b64_json; | ||
| 1313 | if (!image) { | ||
| 1314 | console.warn('xAI returned invalid data.'); | ||
| 1315 | return response.sendStatus(500); | ||
| 1316 | } | ||
| 1317 | |||
| 1318 | return response.send({ image }); | ||
| 1319 | } catch (error) { | ||
| 1320 | console.error('Error communicating with xAI', error); | ||
| 1321 | return response.sendStatus(500); | ||
| 1322 | } | ||
| 1323 | }); | ||
| 1324 | |||
| 1274 | router.use('/comfy', comfy); | 1325 | router.use('/comfy', comfy); |
| 1275 | router.use('/together', together); | 1326 | router.use('/together', together); |
| 1276 | router.use('/drawthings', drawthings); | 1327 | router.use('/drawthings', drawthings); |
| @@ -1280,3 +1331,4 @@ router.use('/huggingface', huggingface); | |||
| 1280 | router.use('/nanogpt', nanogpt); | 1331 | router.use('/nanogpt', nanogpt); |
| 1281 | router.use('/bfl', bfl); | 1332 | router.use('/bfl', bfl); |
| 1282 | router.use('/falai', falai); | 1333 | router.use('/falai', falai); |
| 1334 | router.use('/xai', xai); | ||
| @@ -413,7 +413,7 @@ export function convertGooglePrompt(messages, model, useSysPrompt, names) { | |||
| 413 | } | 413 | } |
| 414 | } | 414 | } |
| 415 | 415 | ||
| 416 | const system_instruction = { parts: [{ text: sys_prompt.trim() }]}; | 416 | const system_instruction = { parts: [{ text: sys_prompt.trim() }] }; |
| 417 | const toolNameMap = {}; | 417 | const toolNameMap = {}; |
| 418 | 418 | ||
| 419 | const contents = []; | 419 | const contents = []; |
| @@ -680,6 +680,43 @@ export function convertMistralMessages(messages, names) { | |||
| 680 | } | 680 | } |
| 681 | 681 | ||
| 682 | /** | 682 | /** |
| 683 | * Convert a prompt from the messages objects to the format used by xAI. | ||
| 684 | * @param {object[]} messages Array of messages | ||
| 685 | * @param {PromptNames} names Prompt names | ||
| 686 | * @returns {object[]} Prompt for xAI | ||
| 687 | */ | ||
| 688 | export function convertXAIMessages(messages, names) { | ||
| 689 | if (!Array.isArray(messages)) { | ||
| 690 | return []; | ||
| 691 | } | ||
| 692 | |||
| 693 | messages.forEach(msg => { | ||
| 694 | if (!msg.name || msg.role === 'user') { | ||
| 695 | return; | ||
| 696 | } | ||
| 697 | |||
| 698 | const needsCharNamePrefix = [ | ||
| 699 | { role: 'assistant', condition: names.charName && !msg.content.startsWith(`${names.charName}: `) && !names.startsWithGroupName(msg.content) }, | ||
| 700 | { role: 'system', name: 'example_assistant', condition: names.charName && !msg.content.startsWith(`${names.charName}: `) && !names.startsWithGroupName(msg.content) }, | ||
| 701 | { role: 'system', name: 'example_user', condition: names.userName && !msg.content.startsWith(`${names.userName}: `) }, | ||
| 702 | ]; | ||
| 703 | |||
| 704 | const matchingRule = needsCharNamePrefix.find(rule => | ||
| 705 | msg.role === rule.role && (!rule.name || msg.name === rule.name) && rule.condition, | ||
| 706 | ); | ||
| 707 | |||
| 708 | if (matchingRule) { | ||
| 709 | const prefix = msg.role === 'system' && msg.name === 'example_user' ? names.userName : names.charName; | ||
| 710 | msg.content = `${prefix}: ${msg.content}`; | ||
| 711 | } | ||
| 712 | |||
| 713 | delete msg.name; | ||
| 714 | }); | ||
| 715 | |||
| 716 | return messages; | ||
| 717 | } | ||
| 718 | |||
| 719 | /** | ||
| 683 | * Merge messages with the same consecutive role, removing names if they exist. | 720 | * Merge messages with the same consecutive role, removing names if they exist. |
| 684 | * @param {any[]} messages Messages to merge | 721 | * @param {any[]} messages Messages to merge |
| 685 | * @param {PromptNames} names Prompt names | 722 | * @param {PromptNames} names Prompt names |