Z.AI: Video inlining and 'coding' captions Closes #4899

c92939e56cdc3634d26fcfb07bf83e39500e5e5a

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

6 files changed, +30 -9Showing whitespace changes
public/index.html+1 -1
@@ -1998,7 +1998,7 @@
19981998 <i class="icon-supported fa-solid fa-image" title="Supported by the current model" data-i18n="[title]Supported by the current model"></i>
19991999 <i class="icon-unsupported fa-solid fa-image" title="Unsupported by the current model" data-i18n="[title]Unsupported by the current model"></i>
20002000 </div>
20012001 <div id="openai_video_inlining_supported" data-source="makersuite,vertexai,openrouter,zai">
20022002 <i class="icon-supported fa-solid fa-film" title="Supported by the current model" data-i18n="[title]Supported by the current model"></i>
20032003 <i class="icon-unsupported fa-solid fa-film" title="Unsupported by the current model" data-i18n="[title]Unsupported by the current model"></i>
20042004 </div>
public/scripts/extensions/caption/index.js+1 -1
@@ -455,7 +455,7 @@ function isVideoCaptioningAvailable() {
455455 return false;
456456 }
457457
458458 return ['google', 'vertexai', 'zai'].includes(extension_settings.caption.multimodal_api);
459459}
460460
461461jQuery(async function () {
public/scripts/extensions/caption/settings.html+0 -3
@@ -177,9 +177,6 @@
177177 <option data-type="custom" value="custom_current" data-i18n="currently_selected">[Currently selected]</option>
178178 </select>
179179 </div>
180- <div data-type="zai">
181- <b>Will use Common API. Coding API is not supported!</b>
182- </div>
183180 <div data-type="ollama">
184181 <div>
185182 The model must be downloaded first! Do it with the <code>ollama pull</code> command or <a href="#" id="caption_ollama_pull">click here</a>.
public/scripts/extensions/shared.js+5 -1
@@ -1,7 +1,7 @@
11import { CONNECT_API_MAP, getRequestHeaders } from '../../script.js';
22import { extension_settings, openThirdPartyExtensionMenu } from '../extensions.js';
33import { t } from '../i18n.js';
44import { oai_settings, proxies, ZAI_ENDPOINT } from '../openai.js';
55import { SECRET_KEYS, secret_state } from '../secrets.js';
66import { textgen_types, textgenerationwebui_settings } from '../textgen-settings.js';
77import { getTokenCountAsync } from '../tokenizers.js';
@@ -115,6 +115,10 @@ export async function getMultimodalCaption(base64Img, prompt) {
115115 requestBody.custom_exclude_body = oai_settings.custom_exclude_body;
116116 }
117117
118+ if (extension_settings.caption.multimodal_api === 'zai') {
119+ requestBody.zai_endpoint = oai_settings.zai_endpoint || ZAI_ENDPOINT.COMMON;
120+ }
121+
118122 function getEndpointUrl() {
119123 switch (extension_settings.caption.multimodal_api) {
120124 case 'google':
public/scripts/openai.js+6 -1
@@ -5870,12 +5870,15 @@ export function isVideoInliningSupported() {
58705870 return false;
58715871 }
58725872
5873- // Only Gemini models support video for now
58745873 const videoSupportedModels = [
5874+ // Gemini
58755875 'gemini-2.0',
58765876 'gemini-2.5',
58775877 'gemini-exp-1206',
58785878 'gemini-3',
5879+ // Z.AI (GLM)
5880+ 'glm-4.5v',
5881+ 'glm-4.6v',
58795882 ];
58805883
58815884 switch (oai_settings.chat_completion_source) {
@@ -5885,6 +5888,8 @@ export function isVideoInliningSupported() {
58855888 return videoSupportedModels.some(model => oai_settings.vertexai_model.includes(model));
58865889 case chat_completion_sources.OPENROUTER:
58875890 return (Array.isArray(model_list) && model_list.find(m => m.id === oai_settings.openrouter_model)?.architecture?.input_modalities?.includes('video'));
5891+ case chat_completion_sources.ZAI:
5892+ return videoSupportedModels.some(model => oai_settings.zai_model.includes(model));
58885893 default:
58895894 return false;
58905895 }
src/endpoints/openai.js+17 -2
@@ -8,7 +8,7 @@ import express from 'express';
88import { getConfigValue, mergeObjectWithYaml, excludeKeysByYaml, trimV1, delay } from '../util.js';
99import { setAdditionalHeaders } from '../additional-headers.js';
1010import { readSecret, SECRET_KEYS } from './secrets.js';
1111import { AIMLAPI_HEADERS, OPENROUTER_HEADERS, ZAI_ENDPOINT } from '../constants.js';
1212
1313export const router = express.Router();
1414
@@ -191,7 +191,22 @@ router.post('/caption-image', async (request, response) => {
191191 }
192192
193193 if (request.body.api === 'zai') {
194- apiUrl = 'https://api.z.ai/api/paas/v4/chat/completions';
194+ apiUrl = request.body.zai_endpoint === ZAI_ENDPOINT.CODING
195+ ? 'https://api.z.ai/api/coding/paas/v4/chat/completions'
196+ : 'https://api.z.ai/api/paas/v4/chat/completions';
197+
198+ // Handle video inlining for Z.AI
199+ if (/data:video\/\w+;base64,/.test(request.body.image)) {
200+ const message = body.messages.find(msg => Array.isArray(msg.content));
201+ if (message) {
202+ const imgContent = message.content.find(c => c.type === 'image_url');
203+ if (imgContent) {
204+ imgContent.type = 'video_url';
205+ imgContent.video_url = imgContent.image_url;
206+ delete imgContent.image_url;
207+ }
208+ }
209+ }
195210 }
196211
197212 if (['koboldcpp', 'vllm', 'llamacpp', 'ooba'].includes(request.body.api)) {