Add official GLM API as CC provider (#4678) * Add UI elements * Add support for model configuration * fix: update API request parameters for improved handling * Add logo img * Fix tool calling with negative index * Include tool calls into 'last in context' calculation * feat: add support for captioning

4add4f0090dc41d194c75c52684e9df6468b698f

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

Signed
16 files changed, +158 -12Showing whitespace changes
public/img/zai.svg+3 -0
@@ -0,0 +1,3 @@
1+<svg version="1.1" x="0" y="0" xml:space="preserve" id="svg6" width="18.60156" height="15.82031" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18.60156 15.82031">
2+ <path id="path2" d="M.4707 0v2.33008h7.09961c.36 0 .7004-.1807.9004-.4707L9.77147.00977S.4607.01.4707 0zm10.68946.00977L0 15.8203h7.4414L18.60157.00977Zm-.11914 13.48046c-.36 0-.7004.1807-.9004.4707l-1.31054 1.85938h9.30078v-2.33008z"/>
3+</svg>
public/index.html+32 -6
@@ -693,7 +693,7 @@
693693 </span>
694694 </div>
695695 </div>
696696 <div class="range-block" data-source="openai,claude,aimlapi,openrouter,ai21,makersuite,vertexai,mistralai,custom,cohere,perplexity,groq,electronhub,nanogpt,deepseek,xai,pollinations,moonshot,fireworks,cometapi,azure_openai,zai">
697697 <div class="range-block-title" data-i18n="Temperature">
698698 Temperature
699699 </div>
@@ -745,7 +745,7 @@
745745 </div>
746746 </div>
747747 </div>
748748 <div class="range-block" data-source="openai,claude,aimlapi,openrouter,ai21,makersuite,vertexai,mistralai,custom,cohere,perplexity,groq,electronhub,nanogpt,deepseek,xai,pollinations,moonshot,fireworks,cometapi,azure_openai,zai">
749749 <div class="range-block-title" data-i18n="Top P">
750750 Top P
751751 </div>
@@ -1965,7 +1965,7 @@
19651965 </b>
19661966 </div>
19671967 </div>
19681968 <div class="range-block" data-source="openai,cohere,mistralai,custom,claude,aimlapi,openrouter,groq,deepseek,makersuite,vertexai,ai21,xai,pollinations,moonshot,fireworks,cometapi,electronhub,azure_openai,zai">
19691969 <label for="openai_function_calling" class="checkbox_label flexWrap widthFreeExpand">
19701970 <input id="openai_function_calling" type="checkbox" />
19711971 <span data-i18n="Enable function calling">Enable function calling</span>
@@ -1980,7 +1980,7 @@
19801980 <strong data-i18n="enable_functions_desc_4">Not supported when Prompt Post-Processing with "no tools" is used!</strong>
19811981 </div>
19821982 </div>
19831983 <div class="range-block" data-source="openai,aimlapi,openrouter,mistralai,makersuite,vertexai,claude,custom,xai,pollinations,moonshot,cohere,cometapi,nanogpt,electronhub,azure_openai,zai">
19841984 <label for="openai_image_inlining" class="checkbox_label flexWrap widthFreeExpand">
19851985 <input id="openai_image_inlining" type="checkbox" />
19861986 <span data-i18n="Send inline images">Send inline images</span>
@@ -1996,7 +1996,7 @@
19961996 <code><i class="fa-solid fa-wand-magic-sparkles"></i></code>
19971997 <span data-i18n="image_inlining_hint_3">menu to attach an image file to the chat.</span>
19981998 </div>
19991999 <div class="flex-container flexFlowColumn wide100p textAlignCenter marginTop10" data-source="openai,custom,xai,pollinations,cohere,cometapi,nanogpt,moonshot,aimlapi,openrouter,mistralai,electronhub,azure_openai,zai">
20002000 <div class="flex-container oneline-dropdown">
20012001 <label for="openai_inline_image_quality" data-i18n="Inline Image Quality">
20022002 Inline Image Quality
@@ -2058,7 +2058,7 @@
20582058 </span>
20592059 </div>
20602060 </div>
20612061 <div class="range-block" data-source="deepseek,aimlapi,openrouter,custom,claude,xai,makersuite,vertexai,pollinations,moonshot,mistralai,fireworks,cometapi,electronhub,azure_openai,nanogpt,zai">
20622062 <label for="openai_show_thoughts" class="checkbox_label widthFreeExpand">
20632063 <input id="openai_show_thoughts" type="checkbox" />
20642064 <span data-i18n="Request model reasoning">Request model reasoning</span>
@@ -2801,6 +2801,7 @@
28012801 <option value="perplexity">Perplexity</option>
28022802 <option value="pollinations">Pollinations</option>
28032803 <option value="xai">xAI (Grok)</option>
2804+ <option value="zai">Z.AI (GLM)</option>
28042805 </optgroup>
28052806 </select>
28062807 <div class="inline-drawer wide100p" data-source="openai,claude,mistralai,makersuite,vertexai,deepseek,xai">
@@ -3674,6 +3675,31 @@
36743675 <option value="kimi-thinking-preview">kimi-thinking-preview</option>
36753676 </select>
36763677 </div>
3678+ <div id="zai_form" data-source="zai">
3679+ <h4>
3680+ <a data-i18n="Z.AI API Key" href="https://z.ai/manage-apikey/apikey-list" target="_blank" rel="noopener noreferrer">
3681+ Z.AI API Key
3682+ </a>
3683+ </h4>
3684+ <div class="flex-container">
3685+ <input id="api_key_zai" name="api_key_zai" class="text_pole flex1" value="" type="text" autocomplete="off">
3686+ <div title="Manage API keys" data-i18n="[title]Manage API keys" class="menu_button fa-solid fa-key fa-fw manage-api-keys" data-key="api_key_zai"></div>
3687+ </div>
3688+ <div data-for="api_key_zai" class="neutral_warning" data-i18n="For privacy reasons, your API key will be hidden after you click 'Connect'.">
3689+ For privacy reasons, your API key will be hidden after you click 'Connect'.
3690+ </div>
3691+ <h4 data-i18n="Z.AI Model">Z.AI Model</h4>
3692+ <select id="model_zai_select">
3693+ <option value="glm-4.6">glm-4.6</option>
3694+ <option value="glm-4.5v">glm-4.5v</option>
3695+ <option value="glm-4.5">glm-4.5</option>
3696+ <option value="glm-4.5-air">glm-4.5-air</option>
3697+ <option value="glm-4.5-x">glm-4.5-x</option>
3698+ <option value="glm-4.5-airx">glm-4.5-airx</option>
3699+ <option value="glm-4.5-flash">glm-4.5-flash</option>
3700+ <option value="glm-4-32b-0414-128k">glm-4-32b-0414-128k</option>
3701+ </select>
3702+ </div>
36773703 <div id="azure_openai_settings" data-source="azure_openai">
36783704 <!-- Azure Base URL -->
36793705 <h4><span data-i18n="Azure Base URL">Azure Base URL</span></h4>
public/script.js+2 -1
@@ -5264,7 +5264,7 @@ function setInContextMessages(msgInContextCount, type) {
52645264 msgInContextCount++;
52655265 }
52665266
52675267 const lastMessageBlock = chatElement.find('.mes:not([is_system="true"]), .mes.toolCall').eq(-msgInContextCount);
52685268 lastMessageBlock.addClass('lastInContext');
52695269
52705270 if (lastMessageBlock.length === 0) {
@@ -5520,6 +5520,7 @@ export function extractJsonFromData(data, { mainApi = null, chatCompletionSource
55205520 case chat_completion_sources.XAI:
55215521 case chat_completion_sources.ELECTRONHUB:
55225522 case chat_completion_sources.AZURE_OPENAI:
5523+ case chat_completion_sources.ZAI:
55235524 default:
55245525 result = tryParse(text);
55255526 break;
public/scripts/RossAscends-mods.js+1 -0
@@ -411,6 +411,7 @@ function RA_autoconnect(PrevApi) {
411411 || (secret_state[SECRET_KEYS.MOONSHOT] && oai_settings.chat_completion_source == chat_completion_sources.MOONSHOT)
412412 || (secret_state[SECRET_KEYS.FIREWORKS] && oai_settings.chat_completion_source == chat_completion_sources.FIREWORKS)
413413 || (secret_state[SECRET_KEYS.COMETAPI] && oai_settings.chat_completion_source == chat_completion_sources.COMETAPI)
414+ || (secret_state[SECRET_KEYS.ZAI] && oai_settings.chat_completion_source == chat_completion_sources.ZAI)
414415 || (oai_settings.chat_completion_source === chat_completion_sources.POLLINATIONS)
415416 || (isValidUrl(oai_settings.custom_url) && oai_settings.chat_completion_source == chat_completion_sources.CUSTOM)
416417 || (secret_state[SECRET_KEYS.AZURE_OPENAI] && oai_settings.chat_completion_source == chat_completion_sources.AZURE_OPENAI)
public/scripts/extensions/caption/index.js+1 -0
@@ -442,6 +442,7 @@ jQuery(async function () {
442442 'moonshot': SECRET_KEYS.MOONSHOT,
443443 'nanogpt': SECRET_KEYS.NANOGPT,
444444 'electronhub': SECRET_KEYS.ELECTRONHUB,
445+ 'zai': SECRET_KEYS.ZAI,
445446 };
446447
447448 if (chatCompletionApis[api] && secret_state[chatCompletionApis[api]]) {
public/scripts/extensions/caption/settings.html+2 -0
@@ -37,6 +37,7 @@
3737 <option value="pollinations">Pollinations</option>
3838 <option value="vllm">vLLM</option>
3939 <option value="xai">xAI (Grok)</option>
40+ <option value="zai">Z.AI (GLM)</option>
4041 </select>
4142 </div>
4243 <div class="flex1 flex-container flexFlowColumn flexNoGap">
@@ -151,6 +152,7 @@
151152 <option data-type="ollama" value="mistral-small3.2">mistral-small3.2</option>
152153 <option data-type="ollama" value="llama3.2-vision">llama3.2-vision</option>
153154 <option data-type="ollama" value="llama4">llama4</option>
155+ <option data-type="zai" value="glm-4.5v">glm-4.5v</option>
154156 <option data-type="llamacpp" value="llamacpp_current" data-i18n="currently_loaded">[Currently loaded]</option>
155157 <option data-type="ooba" value="ooba_current" data-i18n="currently_loaded">[Currently loaded]</option>
156158 <option data-type="koboldcpp" value="koboldcpp_current" data-i18n="currently_loaded">[Currently loaded]</option>
public/scripts/extensions/shared.js+4 -0
@@ -255,6 +255,10 @@ function throwIfInvalidModel(useReverseProxy) {
255255 if (multimodalApi === 'electronhub' && !secret_state[SECRET_KEYS.ELECTRONHUB]) {
256256 throw new Error('Electron Hub API key is not set.');
257257 }
258+
259+ if (multimodalApi === 'zai' && !secret_state[SECRET_KEYS.ZAI]) {
260+ throw new Error('Z.AI API key is not set.');
261+ }
258262}
259263
260264/**
public/scripts/openai.js+80 -3
@@ -189,6 +189,7 @@ export const chat_completion_sources = {
189189 FIREWORKS: 'fireworks',
190190 COMETAPI: 'cometapi',
191191 AZURE_OPENAI: 'azure_openai',
192+ ZAI: 'zai',
192193};
193194
194195const character_names_behavior = {
@@ -294,6 +295,7 @@ export const settingsToUpdate = {
294295 custom_prompt_post_processing: ['#custom_prompt_post_processing', 'custom_prompt_post_processing', false, true],
295296 google_model: ['#model_google_select', 'google_model', false, true],
296297 vertexai_model: ['#model_vertexai_select', 'vertexai_model', false, true],
298+ zai_model: ['#model_zai_select', 'zai_model', false, true],
297299 openai_max_context: ['#openai_max_context', 'openai_max_context', false, false],
298300 openai_max_tokens: ['#openai_max_tokens', 'openai_max_tokens', false, false],
299301 wrap_in_quotes: ['#wrap_in_quotes', 'wrap_in_quotes', true, false],
@@ -391,6 +393,7 @@ const default_settings = {
391393 cometapi_model: 'gpt-4o',
392394 moonshot_model: 'kimi-latest',
393395 fireworks_model: 'accounts/fireworks/models/kimi-k2-instruct',
396+ zai_model: 'glm-4.6',
394397 azure_base_url: '',
395398 azure_deployment_name: '',
396399 azure_api_version: '2024-02-15-preview',
@@ -487,6 +490,7 @@ const oai_settings = {
487490 cometapi_model: 'gpt-4o',
488491 moonshot_model: 'kimi-latest',
489492 fireworks_model: 'accounts/fireworks/models/kimi-k2-instruct',
493+ zai_model: 'glm-4.6',
490494 azure_base_url: '',
491495 azure_deployment_name: '',
492496 azure_api_version: '2024-02-15-preview',
@@ -1532,7 +1536,7 @@ export async function prepareOpenAIMessages({
15321536 const eventData = { chat, dryRun };
15331537 await eventSource.emit(event_types.CHAT_COMPLETION_PROMPT_READY, eventData);
15341538
15351539 openai_messages_count = chat.filter(x => !x?.tool_calls && (x?.role === ['user', ||'assistant', 'tool'].includes(x?.role === 'assistant'))?.length || 0;
15361540
15371541 return [chat, promptManager.tokenHandler.counts];
15381542}
@@ -1664,6 +1668,8 @@ export function getChatCompletionModel(source = null) {
16641668 return oai_settings.fireworks_model;
16651669 case chat_completion_sources.AZURE_OPENAI:
16661670 return oai_settings.azure_openai_model;
1671+ case chat_completion_sources.ZAI:
1672+ return oai_settings.zai_model;
16671673 default:
16681674 console.error(`Unknown chat completion source: ${activeSource}`);
16691675 return '';
@@ -2299,7 +2305,8 @@ async function sendOpenAIRequest(type, messages, signal, { jsonSchema = null } =
22992305 const isXAI = oai_settings.chat_completion_source == chat_completion_sources.XAI;
23002306 const isPollinations = oai_settings.chat_completion_source == chat_completion_sources.POLLINATIONS;
23012307 const isMoonshot = oai_settings.chat_completion_source == chat_completion_sources.MOONSHOT;
23022308 const isAzureOpenAI = oai_settings.chat_completion_source == chat_completion_sources.AZURE_OPENAI; // Add this line
2309+ const isZai = oai_settings.chat_completion_source == chat_completion_sources.ZAI;
23032310 const isTextCompletion = isOAI && textCompletionModels.includes(oai_settings.openai_model);
23042311 const isQuiet = type === 'quiet';
23052312 const isImpersonate = type === 'impersonate';
@@ -2500,6 +2507,14 @@ async function sendOpenAIRequest(type, messages, signal, { jsonSchema = null } =
25002507 generate_data['top_k'] = Number(oai_settings.top_k_openai);
25012508 }
25022509
2510+ // https://docs.z.ai/api-reference/llm/chat-completion
2511+ if (isZai) {
2512+ generate_data['top_p'] = generate_data.top_p || 0.01;
2513+ generate_data['stop'] = getCustomStoppingStrings(1);
2514+ delete generate_data.presence_penalty;
2515+ delete generate_data.frequency_penalty;
2516+ }
2517+
25032518 const seedSupportedSources = [
25042519 chat_completion_sources.OPENAI,
25052520 chat_completion_sources.AZURE_OPENAI,
@@ -2681,7 +2696,7 @@ export function getStreamingReply(data, state, { chatCompletionSource = null, ov
26812696 state.reasoning += (data.choices?.filter(x => x?.delta?.reasoning)?.[0]?.delta?.reasoning || '');
26822697 }
26832698 return data.choices?.[0]?.delta?.content ?? data.choices?.[0]?.message?.content ?? data.choices?.[0]?.text ?? '';
26842699 } else if ([chat_completion_sources.CUSTOM, chat_completion_sources.POLLINATIONS, chat_completion_sources.AIMLAPI, chat_completion_sources.MOONSHOT, chat_completion_sources.COMETAPI, chat_completion_sources.ELECTRONHUB, chat_completion_sources.NANOGPT, chat_completion_sources.ZAI].includes(chat_completion_source)) {
26852700 if (show_thoughts) {
26862701 state.reasoning +=
26872702 data.choices?.filter(x => x?.delta?.reasoning_content)?.[0]?.delta?.reasoning_content ??
@@ -3655,6 +3670,7 @@ function loadOpenAISettings(data, settings) {
36553670 oai_settings.cometapi_model = settings.cometapi_model ?? default_settings.cometapi_model;
36563671 oai_settings.moonshot_model = settings.moonshot_model ?? default_settings.moonshot_model;
36573672 oai_settings.fireworks_model = settings.fireworks_model ?? default_settings.fireworks_model;
3673+ oai_settings.zai_model = settings.zai_model ?? default_settings.zai_model;
36583674 oai_settings.custom_model = settings.custom_model ?? default_settings.custom_model;
36593675 oai_settings.custom_url = settings.custom_url ?? default_settings.custom_url;
36603676 oai_settings.custom_include_body = settings.custom_include_body ?? default_settings.custom_include_body;
@@ -3760,6 +3776,8 @@ function loadOpenAISettings(data, settings) {
37603776 $(`#model_pollinations_select option[value="${oai_settings.pollinations_model}"`).prop('selected', true);
37613777 $('#model_moonshot_select').val(oai_settings.moonshot_model);
37623778 $(`#model_moonshot_select option[value="${oai_settings.moonshot_model}"`).prop('selected', true);
3779+ $('#model_zai_select').val(oai_settings.zai_model);
3780+ $(`#model_zai_select option[value="${oai_settings.zai_model}"`).prop('selected', true);
37633781 $('#custom_model_id').val(oai_settings.custom_model);
37643782 $('#custom_api_url_text').val(oai_settings.custom_url);
37653783 $('#azure_base_url').val(oai_settings.azure_base_url);
@@ -3933,6 +3951,7 @@ async function getStatusOpen() {
39333951 chat_completion_sources.AI21,
39343952 chat_completion_sources.VERTEXAI,
39353953 chat_completion_sources.PERPLEXITY,
3954+ chat_completion_sources.ZAI,
39363955 ];
39373956 if (noValidateSources.includes(oai_settings.chat_completion_source)) {
39383957 let status = t`Key saved; press \"Test Message\" to verify.`;
@@ -4059,6 +4078,7 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
40594078 moonshot_model: settings.moonshot_model,
40604079 fireworks_model: settings.fireworks_model,
40614080 cometapi_model: settings.cometapi_model,
4081+ zai_model: settings.zai_model,
40624082 custom_model: settings.custom_model,
40634083 custom_url: settings.custom_url,
40644084 custom_include_body: settings.custom_include_body,
@@ -4713,6 +4733,29 @@ function getGroqMaxContext(model, isUnlocked) {
47134733 return Object.entries(contextMap).find(([key]) => model.includes(key))?.[1] || max_128k;
47144734}
47154735
4736+/**
4737+ * Get the maximum context size for the Z.AI model
4738+ * @param {string} model Model identifier
4739+ * @param {boolean} isUnlocked If context limits are unlocked
4740+ * @returns {number} Maximum context size in tokens
4741+ */
4742+function getZaiMaxContext(model, isUnlocked) {
4743+ if (isUnlocked) {
4744+ return unlocked_max;
4745+ }
4746+
4747+ const contextMap = {
4748+ 'glm-4.6': max_200k,
4749+ 'glm-4.5': max_128k,
4750+ 'glm-4-32b-0414-128k': max_128k,
4751+ 'glm-4.5-air': max_128k,
4752+ 'glm-4.5v': max_64k,
4753+ };
4754+
4755+ // Return context size if model found, otherwise default to 128k
4756+ return Object.entries(contextMap).find(([key]) => model.includes(key))?.[1] || max_128k;
4757+}
4758+
47164759function getMoonshotMaxContext(model, isUnlocked) {
47174760 if (isUnlocked) {
47184761 return unlocked_max;
@@ -4980,6 +5023,11 @@ async function onModelChange() {
49805023 oai_settings.azure_openai_model = value;
49815024 }
49825025
5026+ if ($(this).is('#model_zai_select')) {
5027+ console.log('ZAI model changed to', value);
5028+ oai_settings.zai_model = value;
5029+ }
5030+
49835031 if ([chat_completion_sources.MAKERSUITE, chat_completion_sources.VERTEXAI].includes(oai_settings.chat_completion_source)) {
49845032 if (oai_settings.max_context_unlocked) {
49855033 $('#openai_max_context').attr('max', max_2mil);
@@ -5282,6 +5330,15 @@ async function onModelChange() {
52825330 $('#temp_openai').attr('max', oai_max_temp).val(oai_settings.temp_openai).trigger('input');
52835331 }
52845332
5333+ if (oai_settings.chat_completion_source == chat_completion_sources.ZAI) {
5334+ const maxContext = getZaiMaxContext(oai_settings.zai_model, oai_settings.max_context_unlocked);
5335+ $('#openai_max_context').attr('max', maxContext);
5336+ oai_settings.openai_max_context = Math.min(Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context);
5337+ $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');
5338+ oai_settings.temp_openai = Math.min(claude_max_temp, oai_settings.temp_openai);
5339+ $('#temp_openai').attr('max', claude_max_temp).val(oai_settings.temp_openai).trigger('input');
5340+ }
5341+
52855342 $('#openai_max_context_counter').attr('max', Number($('#openai_max_context').attr('max')));
52865343
52875344 saveSettingsDebounced();
@@ -5583,6 +5640,18 @@ async function onConnectButtonClick(e) {
55835640 }
55845641 }
55855642
5643+ if (oai_settings.chat_completion_source == chat_completion_sources.ZAI) {
5644+ const api_key_zai = String($('#api_key_zai').val()).trim();
5645+
5646+ if (api_key_zai.length) {
5647+ await writeSecret(SECRET_KEYS.ZAI, api_key_zai);
5648+ }
5649+
5650+ if (!api_key_zai && !secret_state[SECRET_KEYS.ZAI]) {
5651+ console.log('No secret key saved for ZAI');
5652+ return;
5653+ }
5654+ }
55865655
55875656 startStatusLoading();
55885657 saveSettingsDebounced();
@@ -5660,6 +5729,9 @@ function toggleChatCompletionForms() {
56605729 else if (oai_settings.chat_completion_source == chat_completion_sources.AZURE_OPENAI) {
56615730 $('#azure_openai_model').trigger('change');
56625731 }
5732+ else if (oai_settings.chat_completion_source == chat_completion_sources.ZAI) {
5733+ $('#model_zai_select').trigger('change');
5734+ }
56635735
56645736 $('[data-source]').each(function () {
56655737 const validSources = $(this).data('source').split(',');
@@ -5774,6 +5846,8 @@ export function isImageInliningSupported() {
57745846 'moonshot-v1-8k-vision-preview',
57755847 'moonshot-v1-32k-vision-preview',
57765848 'moonshot-v1-128k-vision-preview',
5849+ // Z.AI (GLM)
5850+ 'glm-4.5v',
57775851 ];
57785852
57795853 switch (oai_settings.chat_completion_source) {
@@ -5816,6 +5890,8 @@ export function isImageInliningSupported() {
58165890 return visionSupportedModels.some(model => oai_settings.moonshot_model.includes(model));
58175891 case chat_completion_sources.NANOGPT:
58185892 return (Array.isArray(model_list) && model_list.find(m => m.id === oai_settings.nanogpt_model)?.capabilities?.vision);
5893+ case chat_completion_sources.ZAI:
5894+ return visionSupportedModels.some(model => oai_settings.zai_model.includes(model));
58195895 default:
58205896 return false;
58215897 }
@@ -6664,6 +6740,7 @@ export function initOpenAI() {
66646740 $('#model_moonshot_select').on('change', onModelChange);
66656741 $('#model_fireworks_select').on('change', onModelChange);
66666742 $('#azure_openai_model').on('change', onModelChange);
6743+ $('#model_zai_select').on('change', onModelChange);
66676744 $('#settings_preset_openai').on('change', onSettingsPresetChange);
66686745 $('#new_oai_preset').on('click', onNewPresetClick);
66696746 $('#delete_oai_preset').on('click', onDeletePresetClick);
public/scripts/reasoning.js+1 -0
@@ -127,6 +127,7 @@ export function extractReasoningFromData(data, {
127127 case chat_completion_sources.COMETAPI:
128128 case chat_completion_sources.ELECTRONHUB:
129129 case chat_completion_sources.NANOGPT:
130+ case chat_completion_sources.ZAI:
130131 case chat_completion_sources.CUSTOM: {
131132 return data?.choices?.[0]?.message?.reasoning_content
132133 ?? data?.choices?.[0]?.message?.reasoning
public/scripts/secrets.js+3 -0
@@ -68,6 +68,7 @@ export const SECRET_KEYS = {
6868 MINIMAX_GROUP_ID: 'minimax_group_id',
6969 MOONSHOT: 'api_key_moonshot',
7070 COMETAPI: 'api_key_cometapi',
71+ ZAI: 'api_key_zai',
7172};
7273
7374const FRIENDLY_NAMES = {
@@ -124,6 +125,7 @@ const FRIENDLY_NAMES = {
124125 [SECRET_KEYS.MOONSHOT]: 'Moonshot AI',
125126 [SECRET_KEYS.COMETAPI]: 'CometAPI',
126127 [SECRET_KEYS.AZURE_OPENAI]: 'Azure OpenAI',
128+ [SECRET_KEYS.ZAI]: 'Z.AI',
127129};
128130
129131const INPUT_MAP = {
@@ -163,6 +165,7 @@ const INPUT_MAP = {
163165 [SECRET_KEYS.FIREWORKS]: '#api_key_fireworks',
164166 [SECRET_KEYS.COMETAPI]: '#api_key_cometapi',
165167 [SECRET_KEYS.AZURE_OPENAI]: '#api_key_azure_openai',
168+ [SECRET_KEYS.ZAI]: '#api_key_zai',
166169};
167170
168171const getLabel = () => moment().format('L LT');
public/scripts/slash-commands.js+1 -0
@@ -4858,6 +4858,7 @@ function getModelOptions(quiet) {
48584858 { id: 'model_moonshot_select', api: 'openai', type: chat_completion_sources.MOONSHOT },
48594859 { id: 'model_fireworks_select', api: 'openai', type: chat_completion_sources.FIREWORKS },
48604860 { id: 'model_cometapi_select', api: 'openai', type: chat_completion_sources.COMETAPI },
4861+ { id: 'model_zai_select', api: 'openai', type: chat_completion_sources.ZAI },
48614862 { id: 'model_novel_select', api: 'novel', type: null },
48624863 { id: 'horde_model', api: 'koboldhorde', type: null },
48634864 ];
public/scripts/tool-calling.js+3 -2
@@ -444,9 +444,9 @@ export class ToolManager {
444444 }
445445
446446 for (const toolCallDelta of toolCallDeltas) {
447447 const toolCallIndex = (typeof toolCallDelta?.index ==>= 'number')0 ? toolCallDelta.index : toolCallDeltas.indexOf(toolCallDelta);
448448
449449 if (isNaN(toolCallIndex) || toolCallIndex < 0) {
450450 continue;
451451 }
452452
@@ -661,6 +661,7 @@ export class ToolManager {
661661 chat_completion_sources.COMETAPI,
662662 chat_completion_sources.ELECTRONHUB,
663663 chat_completion_sources.AZURE_OPENAI,
664+ chat_completion_sources.ZAI,
664665 ];
665666 return supportedSources.includes(oai_settings.chat_completion_source);
666667 }
src/constants.js+1 -0
@@ -183,6 +183,7 @@ export const CHAT_COMPLETION_SOURCES = {
183183 FIREWORKS: 'fireworks',
184184 COMETAPI: 'cometapi',
185185 AZURE_OPENAI: 'azure_openai',
186+ ZAI: 'zai',
186187};
187188
188189/**
src/endpoints/backends/chat-completions.js+15 -0
@@ -73,6 +73,7 @@ const API_POLLINATIONS = 'https://text.pollinations.ai/openai';
7373const API_MOONSHOT = 'https://api.moonshot.ai/v1';
7474const API_FIREWORKS = 'https://api.fireworks.ai/inference/v1';
7575const API_COMETAPI = 'https://api.cometapi.com/v1';
76+const API_ZAI = 'https://api.z.ai/api/paas/v4';
7677
7778/**
7879 * Gets OpenRouter transforms based on the request.
@@ -1958,6 +1959,20 @@ router.post('/generate', function (request, response) {
19581959 reasoning_effort: request.body.reasoning_effort,
19591960 };
19601961 throw new Error('This provider is temporarily disabled.');
1962+ } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.ZAI) {
1963+ apiUrl = API_ZAI;
1964+ apiKey = readSecret(request.user.directories, SECRET_KEYS.ZAI);
1965+ headers = {
1966+ 'Accept-Language': 'en-US,en',
1967+ };
1968+ bodyParams = {
1969+ thinking: {
1970+ type: request.body.include_reasoning ? 'enabled' : 'disabled',
1971+ },
1972+ };
1973+ if (request.body.json_schema) {
1974+ setJsonObjectFormat(bodyParams, request.body.messages, request.body.json_schema);
1975+ }
19611976 } else {
19621977 console.warn('This chat completion source is not supported yet.');
19631978 return response.status(400).send({ error: true });
src/endpoints/openai.js+8 -0
@@ -85,6 +85,10 @@ router.post('/caption-image', async (request, response) => {
8585 key = readSecret(request.user.directories, SECRET_KEYS.ELECTRONHUB);
8686 }
8787
88+ if (request.body.api === 'zai') {
89+ key = readSecret(request.user.directories, SECRET_KEYS.ZAI);
90+ }
91+
8892 const noKeyTypes = ['custom', 'ooba', 'koboldcpp', 'vllm', 'llamacpp', 'pollinations'];
8993 if (!key && !request.body.reverse_proxy && !noKeyTypes.includes(request.body.api)) {
9094 console.warn('No key found for API', request.body.api);
@@ -177,6 +181,10 @@ router.post('/caption-image', async (request, response) => {
177181 apiUrl = 'https://api.electronhub.ai/v1/chat/completions';
178182 }
179183
184+ if (request.body.api === 'zai') {
185+ apiUrl = 'https://api.z.ai/api/paas/v4/chat/completions';
186+ }
187+
180188 if (['koboldcpp', 'vllm', 'llamacpp', 'ooba'].includes(request.body.api)) {
181189 apiUrl = `${trimV1(request.body.server_url)}/v1/chat/completions`;
182190 }
src/endpoints/secrets.js+1 -0
@@ -61,6 +61,7 @@ export const SECRET_KEYS = {
6161 MOONSHOT: 'api_key_moonshot',
6262 COMETAPI: 'api_key_cometapi',
6363 AZURE_OPENAI: 'api_key_azure_openai',
64+ ZAI: 'api_key_zai',
6465};
6566
6667/**