Zai moonshot reverse proxy (#4923) * adding reverse proxy support * update * added handling for the image caption extension
Signed| @@ -2845,7 +2845,7 @@ | ||
| 2845 | 2845 | <option value="zai">Z.AI (GLM)</option> |
| 2846 | 2846 | </optgroup> |
| 2847 | 2847 | </select> |
| 2848 | 2848 | <div class="inline-drawer wide100p" data-source="openai,claude,mistralai,makersuite,vertexai,deepseek,xai,zai,moonshot"> |
| 2849 | 2849 | <div class="inline-drawer-toggle inline-drawer-header"> |
| 2850 | 2850 | <b data-i18n="Reverse Proxy">Reverse Proxy</b> |
| 2851 | 2851 | <div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div> |
| @@ -2909,7 +2909,7 @@ | ||
| 2909 | 2909 | </div> |
| 2910 | 2910 | </div> |
| 2911 | 2911 | </div> |
| 2912 | 2912 | <div id="ReverseProxyWarningMessage" data-source="openai,claude,mistralai,makersuite,vertexai,deepseek,xai,zai,moonshot"> |
| 2913 | 2913 | <div class="reverse_proxy_warning"> |
| 2914 | 2914 | <b> |
| 2915 | 2915 | <div data-i18n="Using a proxy that you're not running yourself is a risk to your data privacy."> |
| @@ -489,6 +489,8 @@ jQuery(async function () { | ||
| 489 | 489 | 'vertexai': SECRET_KEYS.VERTEXAI, |
| 490 | 490 | 'anthropic': SECRET_KEYS.CLAUDE, |
| 491 | 491 | 'xai': SECRET_KEYS.XAI, |
| 492 | + 'zai': SECRET_KEYS.ZAI, | |
| 493 | + 'moonshot': SECRET_KEYS.MOONSHOT, | |
| 492 | 494 | }; |
| 493 | 495 | |
| 494 | 496 | if (reverseProxyApis[api]) { |
| @@ -502,11 +504,9 @@ jQuery(async function () { | ||
| 502 | 504 | 'groq': SECRET_KEYS.GROQ, |
| 503 | 505 | 'cohere': SECRET_KEYS.COHERE, |
| 504 | 506 | 'aimlapi': SECRET_KEYS.AIMLAPI, |
| 505 | - 'moonshot': SECRET_KEYS.MOONSHOT, | |
| 506 | 507 | 'nanogpt': SECRET_KEYS.NANOGPT, |
| 507 | 508 | 'chutes': SECRET_KEYS.CHUTES, |
| 508 | 509 | 'electronhub': SECRET_KEYS.ELECTRONHUB, |
| 509 | - 'zai': SECRET_KEYS.ZAI, | |
| 510 | 510 | }; |
| 511 | 511 | |
| 512 | 512 | if (chatCompletionApis[api] && secret_state[chatCompletionApis[api]]) { |
| @@ -191,7 +191,7 @@ | ||
| 191 | 191 | <input id="caption_ollama_custom_model" class="text_pole" type="text" placeholder="e.g. gemma3:latest" /> |
| 192 | 192 | </div> |
| 193 | 193 | </div> |
| 194 | 194 | <label data-type="openai,anthropic,google,vertexai,mistral,xai,zai,moonshot" class="checkbox_label flexBasis100p" for="caption_allow_reverse_proxy" title="Allow using reverse proxy if defined and valid."> |
| 195 | 195 | <input id="caption_allow_reverse_proxy" type="checkbox" class="checkbox"> |
| 196 | 196 | <span data-i18n="Allow reverse proxy">Allow reverse proxy</span> |
| 197 | 197 | </label> |
| @@ -15,7 +15,7 @@ import { createThumbnail, isValidUrl } from '../utils.js'; | ||
| 15 | 15 | */ |
| 16 | 16 | export async function getMultimodalCaption(base64Img, prompt) { |
| 17 | 17 | const useReverseProxy = |
| 18 | 18 | (['openai', 'anthropic', 'google', 'mistral', 'vertexai', 'xai', 'zai', 'moonshot'].includes(extension_settings.caption.multimodal_api)) |
| 19 | 19 | && extension_settings.caption.allow_reverse_proxy |
| 20 | 20 | && oai_settings.reverse_proxy |
| 21 | 21 | && isValidUrl(oai_settings.reverse_proxy); |
| @@ -2486,6 +2486,8 @@ export async function createGenerationParameters(settings, model, type, messages | ||
| 2486 | 2486 | chat_completion_sources.VERTEXAI, |
| 2487 | 2487 | chat_completion_sources.DEEPSEEK, |
| 2488 | 2488 | chat_completion_sources.XAI, |
| 2489 | + chat_completion_sources.ZAI, | |
| 2490 | + chat_completion_sources.MOONSHOT, | |
| 2489 | 2491 | ]; |
| 2490 | 2492 | |
| 2491 | 2493 | // Sources that support logprobs |
| @@ -4129,6 +4131,8 @@ async function getStatusOpen() { | ||
| 4129 | 4131 | chat_completion_sources.VERTEXAI, |
| 4130 | 4132 | chat_completion_sources.DEEPSEEK, |
| 4131 | 4133 | chat_completion_sources.XAI, |
| 4134 | + chat_completion_sources.ZAI, | |
| 4135 | + chat_completion_sources.MOONSHOT, | |
| 4132 | 4136 | ]; |
| 4133 | 4137 | if (oai_settings.reverse_proxy && validateProxySources.includes(oai_settings.chat_completion_source)) { |
| 4134 | 4138 | await validateReverseProxy(); |
| @@ -5590,11 +5594,11 @@ async function onConnectButtonClick(e) { | ||
| 5590 | 5594 | [chat_completion_sources.DEEPSEEK]: { key: SECRET_KEYS.DEEPSEEK, selector: '#api_key_deepseek', proxy: true }, |
| 5591 | 5595 | [chat_completion_sources.XAI]: { key: SECRET_KEYS.XAI, selector: '#api_key_xai', proxy: true }, |
| 5592 | 5596 | [chat_completion_sources.AIMLAPI]: { key: SECRET_KEYS.AIMLAPI, selector: '#api_key_aimlapi', proxy: false }, |
| 5593 | 5597 | [chat_completion_sources.MOONSHOT]: { key: SECRET_KEYS.MOONSHOT, selector: '#api_key_moonshot', proxy: falsetrue }, |
| 5594 | 5598 | [chat_completion_sources.FIREWORKS]: { key: SECRET_KEYS.FIREWORKS, selector: '#api_key_fireworks', proxy: false }, |
| 5595 | 5599 | [chat_completion_sources.COMETAPI]: { key: SECRET_KEYS.COMETAPI, selector: '#api_key_cometapi', proxy: false }, |
| 5596 | 5600 | [chat_completion_sources.AZURE_OPENAI]: { key: SECRET_KEYS.AZURE_OPENAI, selector: '#api_key_azure_openai', proxy: false }, |
| 5597 | 5601 | [chat_completion_sources.ZAI]: { key: SECRET_KEYS.ZAI, selector: '#api_key_zai', proxy: falsetrue }, |
| 5598 | 5602 | [chat_completion_sources.CHUTES]: { key: SECRET_KEYS.CHUTES, selector: '#api_key_chutes', proxy: false }, |
| 5599 | 5603 | }; |
| 5600 | 5604 | |
| @@ -1701,8 +1701,8 @@ router.post('/status', async function (request, statusResponse) { | ||
| 1701 | 1701 | headers = {}; |
| 1702 | 1702 | throw new Error('This provider is temporarily disabled.'); |
| 1703 | 1703 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.MOONSHOT) { |
| 1704 | - apiUrl = API_MOONSHOT; | |
| 1704 | + apiUrl = new URL(request.body.reverse_proxy || API_MOONSHOT).toString(); | |
| 1705 | 1705 | apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MOONSHOT); |
| 1706 | 1706 | headers = {}; |
| 1707 | 1707 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.FIREWORKS) { |
| 1708 | 1708 | apiUrl = API_FIREWORKS; |
| @@ -2249,8 +2249,8 @@ router.post('/generate', async function (request, response) { | ||
| 2249 | 2249 | setJsonObjectFormat(bodyParams, request.body.messages, request.body.json_schema); |
| 2250 | 2250 | } |
| 2251 | 2251 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.MOONSHOT) { |
| 2252 | - apiUrl = API_MOONSHOT; | |
| 2252 | + apiUrl = new URL(request.body.reverse_proxy || API_MOONSHOT).toString(); | |
| 2253 | 2253 | apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MOONSHOT); |
| 2254 | 2254 | headers = {}; |
| 2255 | 2255 | bodyParams = {}; |
| 2256 | 2256 | request.body.json_schema |
| @@ -2265,8 +2265,9 @@ router.post('/generate', async function (request, response) { | ||
| 2265 | 2265 | }; |
| 2266 | 2266 | throw new Error('This provider is temporarily disabled.'); |
| 2267 | 2267 | } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.ZAI) { |
| 2268 | 2268 | apiUrlconst defaultApiUrl = request.body.zai_endpoint === ZAI_ENDPOINT.CODING ? API_ZAI_CODING : API_ZAI_COMMON; |
| 2269 | 2269 | apiKeyapiUrl = readSecretnew URL(request.userbody.directories,reverse_proxy SECRET_KEYS|| defaultApiUrl).ZAItoString(); |
| 2270 | + apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.ZAI); | |
| 2270 | 2271 | headers = { |
| 2271 | 2272 | 'Accept-Language': 'en-US,en', |
| 2272 | 2273 | }; |
| @@ -73,7 +73,7 @@ router.post('/caption-image', async (request, response) => { | ||
| 73 | 73 | key = readSecret(request.user.directories, SECRET_KEYS.COHERE); |
| 74 | 74 | } |
| 75 | 75 | |
| 76 | 76 | if (request.body.api === 'moonshot' && !request.body.reverse_proxy) { |
| 77 | 77 | key = readSecret(request.user.directories, SECRET_KEYS.MOONSHOT); |
| 78 | 78 | } |
| 79 | 79 | |
| @@ -89,8 +89,11 @@ router.post('/caption-image', async (request, response) => { | ||
| 89 | 89 | key = readSecret(request.user.directories, SECRET_KEYS.ELECTRONHUB); |
| 90 | 90 | } |
| 91 | 91 | |
| 92 | 92 | if (request.body.api === 'zai' && !request.body.reverse_proxy) { |
| 93 | 93 | key = readSecret(request.user.directories, SECRET_KEYS.ZAI); |
| 94 | + } | |
| 95 | + | |
| 96 | + if (request.body.api === 'zai') { | |
| 94 | 97 | bodyParams.max_tokens = 4096; // default is 1024 |
| 95 | 98 | } |
| 96 | 99 | |
| @@ -174,7 +177,7 @@ router.post('/caption-image', async (request, response) => { | ||
| 174 | 177 | apiUrl = 'https://text.pollinations.ai/openai/chat/completions'; |
| 175 | 178 | } |
| 176 | 179 | |
| 177 | 180 | if (request.body.api === 'moonshot' && !request.body.reverse_proxy) { |
| 178 | 181 | apiUrl = 'https://api.moonshot.ai/v1/chat/completions'; |
| 179 | 182 | } |
| 180 | 183 | |
| @@ -190,21 +193,21 @@ router.post('/caption-image', async (request, response) => { | ||
| 190 | 193 | apiUrl = 'https://api.electronhub.ai/v1/chat/completions'; |
| 191 | 194 | } |
| 192 | 195 | |
| 193 | 196 | if (request.body.api === 'zai' && !request.body.reverse_proxy) { |
| 194 | 197 | apiUrl = request.body.zai_endpoint === ZAI_ENDPOINT.CODING |
| 195 | 198 | ? 'https://api.z.ai/api/coding/paas/v4/chat/completions' |
| 196 | 199 | : 'https://api.z.ai/api/paas/v4/chat/completions'; |
| 200 | + } | |
| 197 | 201 | |
| 198 | 202 | // Handle video inlining for Z.AI |
| 199 | 203 | if (request.body.api === 'zai' && /data:video\/\w+;base64,/.test(request.body.image)) { |
| 200 | 204 | const message = body.messages.find(msg => Array.isArray(msg.content)); |
| 201 | 205 | if (message) { |
| 202 | 206 | const imgContent = message.content.find(c => c.type === 'image_url'); |
| 203 | 207 | if (imgContent) { |
| 204 | 208 | imgContent.type = 'video_url'; |
| 205 | 209 | imgContent.video_url = imgContent.image_url; |
| 206 | 210 | delete imgContent.image_url; |
| 207 | - } | |
| 208 | 211 | } |
| 209 | 212 | } |
| 210 | 213 | } |