Feat/moonshot api (#4330) * moonshot * Partial mode + JSON schema * Add logo image * Limit max temp to 1 * Add to captioning extension

0e38bfbf0583ff8f09c282e5c9bd82fa2f6c8a64

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

Signed
16 files changed, +170 -22Showing whitespace changes
public/img/moonshot.svg+1 -0
@@ -0,0 +1 @@
1+<svg xmlns="http://www.w3.org/2000/svg" width="556" height="557.333" viewBox="0 0 417 418" preserveAspectRatio="xMidYMid meet" xmlns:v="https://vecta.io/nano"><path d="M203.162.45L195.5.601c-17.9 1.5-42.2 6.599-56 11.899a214.62 214.62 0 0 1-8.7 3.201c-3.3 1-1.8 1.9 6.5 4 4.5 1.1 16.801 4.399 27.201 7.299L191 34l24.501 6.5 35 9.5 28 7.5 30 7.9L329 71c12.3 3.6 44 12 45.3 12 2.5 0-16.6-20.899-28.6-31.299-36.3-31.6-82.7-49.6-131.2-51.1L203.162.45zM109.5 27.3c-1.1-.2-4.8 1.3-8.7 3.5-6.5 3.9-24.7 16.901-28 20.101-14.6 14.1-22.2 22.199-28 29.899-3.9 5.1-6.899 9.401-6.699 9.501.4.4 8.2 2.5 19.9 5.6l15 3.999c4.1 1.2 15.2 4.201 24.5 6.601l27.5 7.4 30 8 30.501 8.2L215 138l46.5 12.5c2.8.7 10.2 2.7 16.5 4.4l41.001 11c4.1 1.2 11.999 3.301 17.499 4.601L353.5 175c8.5 2.5 25 6.9 45 12 8.3 2.1 15.9 4.2 16.9 4.6 1.6.6 1.7.2 1.1-5.2-.7-6-3.001-17.7-6.101-31.6-1.9-8.4-2.099-8.7-11.399-10.8-2.5-.5-7.6-1.9-11.5-2.9L337 127.5l-29.5-7.9-31.701-8.6c-2.7-.7-4.799-1.5-4.799-1.9 0-2.5 19.4-22.3 26.3-26.9 2-1.3 3.7-2.8 3.7-3.3 0-.4-1.901-1.199-4.201-1.699-3.9-.9-20.199-5.2-36.299-9.7L239 61.8 217 56l-29.5-7.9-32-8.6-46-12.199zM31.1 104c-1.1 0-2.8 1.501-4.1 3.501C20.8 117.5 10.8 142.1 7.6 155c-2.1 8.5-4.6 20.8-4.6 22.4 0 .8 1.899 1.8 4.299 2.5l61.901 16.6 21 5.5C101.001 204.7 120.5 210 141 215.7l13 3.4c1.9.4 8.2 2 14 3.7l26 7.1L217 236l24.5 6.601 44 11.9L302.5 259c6.1 1.5 19.6 5.1 30 8l30 8 17 4.5 14.7 3.901 8.7 2.2 1-2.6c3.7-9.7 5.4-15.3 7.1-23.3l2.5-11.301c.6-2.5-.301-2.899-23.5-8.799L365 233 352 229.4c-3-.9-11.301-3.1-18.501-4.9L314.5 219.5 247 201.4c-16.9-4.3-28.201-7.9-28.701-8.9-.5-1.6 3.2-8.799 12.7-24.199 2.7-4.6 5-8.701 5-9.201s-1.899-1.4-4.299-1.9c-5.7-1.3-30.201-7.7-40.701-10.6L144.5 134l-11-3L117 126.7 69.5 114C51.8 108.9 32.8 104 31.1 104zM1.935 193.44c-.703.309-.986 1.885-1.436 6.16-1.5 14 1.1 38.9 6.4 59.9 4.5 18.1 4.901 18.799 11.801 20.799L62 292 99.5 302c1.7.4 9.1 2.4 16.5 4.4l28.5 7.6 28.5 7.601 22 5.899c4.7 1.4 17.1 4.7 27.5 7.5L275 349.1l24 6.4 29.101 7.701c11.6 3.1 16.9 4.1 17.6 3.4 14.6-14.1 19.599-19.401 24.599-25.601 5.2-6.5 5.7-7.499 4.1-8.099-4.7-1.8-36.599-10.501-52.699-14.401-3.7-.9-8.6-2.3-11-3.1-2.3-.8-7.101-2.2-10.701-3-3.6-.7-9.6-2.3-13.5-3.4-3.8-1.1-13.7-3.8-22-6l-30-8L204.601 287c-8.1-2.1-14.901-4-15.101-4.2-.2-.3 3.901-18.6 7.301-32.6.7-3.2.7-4.399-.1-4.699-.7-.2-7.501-2-15.201-4s-15.8-4.3-18-5-7.1-2.1-11-2.9c-8.5-2-17-4.2-25-6.7-3.3-1-9.6-2.7-14-3.8L54.5 207.4 23 199c-10.2-2.6-19.2-5.1-20.2-5.5-.35-.125-.63-.163-.864-.06zm18.563 102.62c-.316.053-.498.165-.498.34 0 1 4 8.8 7.8 15.3 2.3 3.9 4.2 7.4 4.2 7.8 0 2.1 21.4 28.6 29.6 36.7 12.2 12 12.501 12.3 24.901 21.3 12.5 9 35.5 21.999 43.5 24.599 1.4.4 6.3 2.1 11 3.8 31.5 11.5 69.099 14.601 103.299 8.701C257.199 412.301 282 405.4 282 404c0-.3-7.3-2.399-16.2-4.799l-59.3-15.801-17.101-4.5c-4.9-1.2-5.1-1.7-5.9-19.9-.9-17.8-1.2-19.699-3.8-20.399-1.2-.4-7.999-2.3-15.199-4.2l-28-7.5L109 319.6 81 312l-34-9-21.299-5.9c-2.1-.825-4.256-1.199-5.203-1.04z"/></svg>
1 \ No newline at end of file
public/index.html+36 -8
@@ -652,7 +652,7 @@
652652 <input type="number" id="openai_max_tokens" name="openai_max_tokens" class="text_pole" min="1" max="65536">
653653 </div>
654654 </div>
655655 <div class="range-block" data-source="openai,custom,xai,aimlapi,moonshot">
656656 <div class="range-block-title" data-i18n="Multiple swipes per generation">
657657 Multiple swipes per generation
658658 </div>
@@ -691,7 +691,7 @@
691691 </span>
692692 </div>
693693 </div>
694694 <div class="range-block" data-source="openai,claude,aimlapi,openrouter,ai21,makersuite,vertexai,mistralai,custom,cohere,perplexity,groq,nanogpt,deepseek,xai,pollinations,moonshot">
695695 <div class="range-block-title" data-i18n="Temperature">
696696 Temperature
697697 </div>
@@ -704,7 +704,7 @@
704704 </div>
705705 </div>
706706 </div>
707707 <div class="range-block" data-source="openai,aimlapi,openrouter,custom,cohere,perplexity,groq,mistralai,nanogpt,deepseek,xai,pollinations,moonshot">
708708 <div class="range-block-title" data-i18n="Frequency Penalty">
709709 Frequency Penalty
710710 </div>
@@ -717,7 +717,7 @@
717717 </div>
718718 </div>
719719 </div>
720720 <div class="range-block" data-source="openai,aimlapi,openrouter,custom,cohere,perplexity,groq,mistralai,nanogpt,deepseek,xai,pollinations,moonshot">
721721 <div class="range-block-title" data-i18n="Presence Penalty">
722722 Presence Penalty
723723 </div>
@@ -743,7 +743,7 @@
743743 </div>
744744 </div>
745745 </div>
746746 <div class="range-block" data-source="openai,claude,aimlapi,openrouter,ai21,makersuite,vertexai,mistralai,custom,cohere,perplexity,groq,nanogpt,deepseek,xai,pollinations,moonshot">
747747 <div class="range-block-title" data-i18n="Top P">
748748 Top P
749749 </div>
@@ -1984,7 +1984,7 @@
19841984 </b>
19851985 </div>
19861986 </div>
19871987 <div class="range-block" data-source="openai,cohere,mistralai,custom,claude,aimlapi,openrouter,groq,deepseek,makersuite,vertexai,ai21,xai,pollinations,moonshot">
19881988 <label for="openai_function_calling" class="checkbox_label flexWrap widthFreeExpand">
19891989 <input id="openai_function_calling" type="checkbox" />
19901990 <span data-i18n="Enable function calling">Enable function calling</span>
@@ -1995,7 +1995,7 @@
19951995 <strong data-i18n="enable_functions_desc_4">Not supported when Prompt Post-Processing with "no tools" is used!</strong>
19961996 </div>
19971997 </div>
19981998 <div class="range-block" data-source="openai,aimlapi,openrouter,mistralai,makersuite,vertexai,claude,custom,xai,pollinations,moonshot">
19991999 <label for="openai_image_inlining" class="checkbox_label flexWrap widthFreeExpand">
20002000 <input id="openai_image_inlining" type="checkbox" />
20012001 <span data-i18n="Send inline images">Send inline images</span>
@@ -2065,7 +2065,7 @@
20652065 </span>
20662066 </div>
20672067 </div>
20682068 <div class="range-block" data-source="deepseek,aimlapi,openrouter,custom,claude,xai,makersuite,vertexai,pollinations,moonshot">
20692069 <label for="openai_show_thoughts" class="checkbox_label widthFreeExpand">
20702070 <input id="openai_show_thoughts" type="checkbox" />
20712071 <span data-i18n="Request model reasoning">Request model reasoning</span>
@@ -2797,6 +2797,7 @@
27972797 <option value="makersuite">Google AI Studio</option>
27982798 <option value="vertexai">Google Vertex AI</option>
27992799 <option value="mistralai">MistralAI</option>
2800+ <option value="moonshot">Moonshot AI</option>
28002801 <option value="nanogpt">NanoGPT</option>
28012802 <option value="openrouter">OpenRouter</option>
28022803 <option value="perplexity">Perplexity</option>
@@ -3627,6 +3628,33 @@
36273628 </span>
36283629 </div>
36293630 </div>
3631+ <div id="moonshot_form" data-source="moonshot">
3632+ <h4>
3633+ <a data-i18n="Moonshot AI API Key" href="https://platform.moonshot.ai/console/api-keys" target="_blank" rel="noopener noreferrer">
3634+ Moonshot AI API Key
3635+ </a>
3636+ </h4>
3637+ <div class="flex-container">
3638+ <input id="api_key_moonshot" name="api_key_moonshot" class="text_pole flex1" value="" type="text" autocomplete="off">
3639+ <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_moonshot"></div>
3640+ </div>
3641+ <div data-for="api_key_moonshot" class="neutral_warning" data-i18n="For privacy reasons, your API key will be hidden after you click 'Connect'.">
3642+ For privacy reasons, your API key will be hidden after you click 'Connect'.
3643+ </div>
3644+ <h4 data-i18n="Moonshot AI Model">Moonshot AI Model</h4>
3645+ <select id="model_moonshot_select">
3646+ <option value="kimi-k2-0711-preview">kimi-k2-0711-preview</option>
3647+ <option value="moonshot-v1-8k">moonshot-v1-8k</option>
3648+ <option value="moonshot-v1-32k">moonshot-v1-32k</option>
3649+ <option value="moonshot-v1-128k">moonshot-v1-128k</option>
3650+ <option value="moonshot-v1-auto">moonshot-v1-auto</option>
3651+ <option value="kimi-latest">kimi-latest</option>
3652+ <option value="moonshot-v1-8k-vision-preview">moonshot-v1-8k-vision-preview</option>
3653+ <option value="moonshot-v1-32k-vision-preview">moonshot-v1-32k-vision-preview</option>
3654+ <option value="moonshot-v1-128k-vision-preview">moonshot-v1-128k-vision-preview</option>
3655+ <option value="kimi-thinking-preview">kimi-thinking-preview</option>
3656+ </select>
3657+ </div>
36303658 <div id="prompt_post_processing_form">
36313659 <h4>
36323660 <span data-i18n="Prompt Post-Processing">
public/scripts/RossAscends-mods.js+1 -0
@@ -410,6 +410,7 @@ function RA_autoconnect(PrevApi) {
410410 || (secret_state[SECRET_KEYS.DEEPSEEK] && oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK)
411411 || (secret_state[SECRET_KEYS.XAI] && oai_settings.chat_completion_source == chat_completion_sources.XAI)
412412 || (secret_state[SECRET_KEYS.AIMLAPI] && oai_settings.chat_completion_source == chat_completion_sources.AIMLAPI)
413+ || (secret_state[SECRET_KEYS.MOONSHOT] && oai_settings.chat_completion_source == chat_completion_sources.MOONSHOT)
413414 || (oai_settings.chat_completion_source === chat_completion_sources.POLLINATIONS)
414415 || (isValidUrl(oai_settings.custom_url) && oai_settings.chat_completion_source == chat_completion_sources.CUSTOM)
415416 ) {
public/scripts/extensions/caption/index.js+1 -0
@@ -438,6 +438,7 @@ jQuery(async function () {
438438 'groq': SECRET_KEYS.GROQ,
439439 'cohere': SECRET_KEYS.COHERE,
440440 'aimlapi': SECRET_KEYS.AIMLAPI,
441+ 'moonshot': SECRET_KEYS.MOONSHOT,
441442 };
442443
443444 if (chatCompletionApis[api] && secret_state[chatCompletionApis[api]]) {
public/scripts/extensions/caption/settings.html+4 -0
@@ -27,6 +27,7 @@
2727 <option value="koboldcpp">KoboldCpp</option>
2828 <option value="llamacpp">llama.cpp</option>
2929 <option value="mistral">MistralAI</option>
30+ <option value="moonshot">Moonshot AI</option>
3031 <option value="ollama">Ollama</option>
3132 <option value="openai">OpenAI</option>
3233 <option value="openrouter">OpenRouter</option>
@@ -51,6 +52,9 @@
5152 <option data-type="mistral" value="mistral-small-latest">mistral-small-latest</option>
5253 <option data-type="mistral" value="mistral-medium-latest">mistral-medium-latest</option>
5354 <option data-type="mistral" value="mistral-medium-2505">mistral-medium-2505</option>
55+ <option data-type="moonshot" value="moonshot-v1-8k-vision-preview">moonshot-v1-8k-vision-preview</option>
56+ <option data-type="moonshot" value="moonshot-v1-32k-vision-preview">moonshot-v1-32k-vision-preview</option>
57+ <option data-type="moonshot" value="moonshot-v1-128k-vision-preview">moonshot-v1-128k-vision-preview</option>
5458 <option data-type="openai" value="gpt-4.1">gpt-4.1</option>
5559 <option data-type="openai" value="gpt-4.1-2025-04-14">gpt-4.1-2025-04-14</option>
5660 <option data-type="openai" value="gpt-4.1-mini">gpt-4.1-mini</option>
public/scripts/extensions/shared.js+4 -0
@@ -238,6 +238,10 @@ function throwIfInvalidModel(useReverseProxy) {
238238 if (multimodalApi === 'aimlapi' && !secret_state[SECRET_KEYS.AIMLAPI]) {
239239 throw new Error('AI/ML API key is not set.');
240240 }
241+
242+ if (multimodalApi === 'moonshot' && !secret_state[SECRET_KEYS.MOONSHOT]) {
243+ throw new Error('Moonshot AI API key is not set.');
244+ }
241245}
242246
243247/**
public/scripts/openai.js+72 -2
@@ -183,6 +183,7 @@ export const chat_completion_sources = {
183183 AIMLAPI: 'aimlapi',
184184 XAI: 'xai',
185185 POLLINATIONS: 'pollinations',
186+ MOONSHOT: 'moonshot',
186187};
187188
188189const character_names_behavior = {
@@ -272,6 +273,7 @@ export const settingsToUpdate = {
272273 aimlapi_model: ['#model_aimlapi_select', 'aimlapi_model', false, true],
273274 xai_model: ['#model_xai_select', 'xai_model', false, true],
274275 pollinations_model: ['#model_pollinations_select', 'pollinations_model', false, true],
276+ moonshot_model: ['#model_moonshot_select', 'moonshot_model', false, true],
275277 custom_model: ['#custom_model_id', 'custom_model', false, true],
276278 custom_url: ['#custom_api_url_text', 'custom_url', false, true],
277279 custom_include_body: ['#custom_include_body', 'custom_include_body', false, true],
@@ -367,6 +369,7 @@ const default_settings = {
367369 aimlapi_model: 'gpt-4o-mini-2024-07-18',
368370 xai_model: 'grok-3-beta',
369371 pollinations_model: 'openai',
372+ moonshot_model: 'kimi-latest',
370373 custom_model: '',
371374 custom_url: '',
372375 custom_include_body: '',
@@ -453,6 +456,7 @@ const oai_settings = {
453456 aimlapi_model: 'gpt-4-turbo',
454457 xai_model: 'grok-3-beta',
455458 pollinations_model: 'openai',
459+ moonshot_model: 'kimi-latest',
456460 custom_model: '',
457461 custom_url: '',
458462 custom_include_body: '',
@@ -1613,6 +1617,8 @@ export function getChatCompletionModel(source = null) {
16131617 return oai_settings.xai_model;
16141618 case chat_completion_sources.POLLINATIONS:
16151619 return oai_settings.pollinations_model;
1620+ case chat_completion_sources.MOONSHOT:
1621+ return oai_settings.moonshot_model;
16161622 default:
16171623 console.error(`Unknown chat completion source: ${activeSource}`);
16181624 return '';
@@ -2038,13 +2044,14 @@ async function sendOpenAIRequest(type, messages, signal, { jsonSchema = null } =
20382044 const isAimlapi = oai_settings.chat_completion_source == chat_completion_sources.AIMLAPI;
20392045 const isXAI = oai_settings.chat_completion_source == chat_completion_sources.XAI;
20402046 const isPollinations = oai_settings.chat_completion_source == chat_completion_sources.POLLINATIONS;
2047+ const isMoonshot = oai_settings.chat_completion_source == chat_completion_sources.MOONSHOT;
20412048 const isTextCompletion = isOAI && textCompletionModels.includes(oai_settings.openai_model);
20422049 const isQuiet = type === 'quiet';
20432050 const isImpersonate = type === 'impersonate';
20442051 const isContinue = type === 'continue';
20452052 const stream = oai_settings.stream_openai && !isQuiet && !(isOAI && ['o1-2024-12-17', 'o1'].includes(oai_settings.openai_model));
20462053 const useLogprobs = !!power_user.request_token_probabilities;
20472054 const canMultiSwipe = oai_settings.n > 1 && !isContinue && !isImpersonate && !isQuiet && (isOAI || isCustom || isXAI || isAimlapi || isMoonshot);
20482055
20492056 const logitBiasSources = [chat_completion_sources.OPENAI, chat_completion_sources.OPENROUTER, chat_completion_sources.CUSTOM];
20502057 if (oai_settings.bias_preset_selected
@@ -2384,7 +2391,7 @@ export function getStreamingReply(data, state, { chatCompletionSource = null, ov
23842391 state.reasoning += (data.choices?.filter(x => x?.delta?.reasoning)?.[0]?.delta?.reasoning || '');
23852392 }
23862393 return data.choices?.[0]?.delta?.content ?? data.choices?.[0]?.message?.content ?? data.choices?.[0]?.text ?? '';
23872394 } else if ([chat_completion_sources.CUSTOM, chat_completion_sources.POLLINATIONS, chat_completion_sources.AIMLAPI, chat_completion_sources.MOONSHOT].includes(chat_completion_source)) {
23882395 if (show_thoughts) {
23892396 state.reasoning +=
23902397 data.choices?.filter(x => x?.delta?.reasoning_content)?.[0]?.delta?.reasoning_content ??
@@ -3345,6 +3352,7 @@ function loadOpenAISettings(data, settings) {
33453352 oai_settings.aimlapi_model = settings.aimlapi_model ?? default_settings.aimlapi_model;
33463353 oai_settings.xai_model = settings.xai_model ?? default_settings.xai_model;
33473354 oai_settings.pollinations_model = settings.pollinations_model ?? default_settings.pollinations_model;
3355+ oai_settings.moonshot_model = settings.moonshot_model ?? default_settings.moonshot_model;
33483356 oai_settings.custom_model = settings.custom_model ?? default_settings.custom_model;
33493357 oai_settings.custom_url = settings.custom_url ?? default_settings.custom_url;
33503358 oai_settings.custom_include_body = settings.custom_include_body ?? default_settings.custom_include_body;
@@ -3442,6 +3450,8 @@ function loadOpenAISettings(data, settings) {
34423450 $(`#model_xai_select option[value="${oai_settings.xai_model}"`).prop('selected', true);
34433451 $('#model_pollinations_select').val(oai_settings.pollinations_model);
34443452 $(`#model_pollinations_select option[value="${oai_settings.pollinations_model}"`).prop('selected', true);
3453+ $('#model_moonshot_select').val(oai_settings.moonshot_model);
3454+ $(`#model_moonshot_select option[value="${oai_settings.moonshot_model}"`).prop('selected', true);
34453455 $('#custom_model_id').val(oai_settings.custom_model);
34463456 $('#custom_api_url_text').val(oai_settings.custom_url);
34473457 $('#openai_max_context').val(oai_settings.openai_max_context);
@@ -3714,6 +3724,7 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
37143724 xai_model: settings.xai_model,
37153725 pollinations_model: settings.pollinations_model,
37163726 aimlapi_model: settings.aimlapi_model,
3727+ moonshot_model: settings.moonshot_model,
37173728 custom_model: settings.custom_model,
37183729 custom_url: settings.custom_url,
37193730 custom_include_body: settings.custom_include_body,
@@ -4424,6 +4435,28 @@ function getGroqMaxContext(model, isUnlocked) {
44244435 return Object.entries(contextMap).find(([key]) => model.includes(key))?.[1] || max_128k;
44254436}
44264437
4438+function getMoonshotMaxContext(model, isUnlocked) {
4439+ if (isUnlocked) {
4440+ return unlocked_max;
4441+ }
4442+
4443+ const contextMap = {
4444+ 'moonshot-v1-8k': max_8k,
4445+ 'moonshot-v1-32k': max_32k,
4446+ 'moonshot-v1-128k': max_128k,
4447+ 'moonshot-v1-auto': max_128k,
4448+ 'moonshot-v1-8k-vision-preview': max_8k,
4449+ 'moonshot-v1-32k-vision-preview': max_32k,
4450+ 'moonshot-v1-128k-vision-preview': max_128k,
4451+ 'kimi-k2-0711-preview': max_32k,
4452+ 'kimi-latest': max_32k,
4453+ 'kimi-thinking-preview': max_32k,
4454+ };
4455+
4456+ // Return context size if model found, otherwise default to 32k
4457+ return Object.entries(contextMap).find(([key]) => model.includes(key))?.[1] || max_32k;
4458+}
4459+
44274460async function onModelChange() {
44284461 biasCache = undefined;
44294462 let value = String($(this).val() || '');
@@ -4559,6 +4592,11 @@ async function onModelChange() {
45594592 oai_settings.xai_model = value;
45604593 }
45614594
4595+ if (value && $(this).is('#model_moonshot_select')) {
4596+ console.log('Moonshot model changed to', value);
4597+ oai_settings.moonshot_model = value;
4598+ }
4599+
45624600 if ([chat_completion_sources.MAKERSUITE, chat_completion_sources.VERTEXAI].includes(oai_settings.chat_completion_source)) {
45634601 if (oai_settings.max_context_unlocked) {
45644602 $('#openai_max_context').attr('max', max_2mil);
@@ -4822,6 +4860,15 @@ async function onModelChange() {
48224860 $('#freq_pen_openai').attr('max', 2).attr('min', -2).val(oai_settings.freq_pen_openai).trigger('input');
48234861 }
48244862
4863+ if (oai_settings.chat_completion_source === chat_completion_sources.MOONSHOT) {
4864+ const maxContext = getMoonshotMaxContext(oai_settings.moonshot_model, oai_settings.max_context_unlocked);
4865+ $('#openai_max_context').attr('max', maxContext);
4866+ oai_settings.openai_max_context = Math.min(Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context);
4867+ $('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');
4868+ oai_settings.temp_openai = Math.min(claude_max_temp, oai_settings.temp_openai);
4869+ $('#temp_openai').attr('max', claude_max_temp).val(oai_settings.temp_openai).trigger('input');
4870+ }
4871+
48254872 $('#openai_max_context_counter').attr('max', Number($('#openai_max_context').attr('max')));
48264873
48274874 saveSettingsDebounced();
@@ -5053,6 +5100,19 @@ async function onConnectButtonClick(e) {
50535100 }
50545101 }
50555102
5103+ if (oai_settings.chat_completion_source == chat_completion_sources.MOONSHOT) {
5104+ const api_key_moonshot = String($('#api_key_moonshot').val()).trim();
5105+
5106+ if (api_key_moonshot.length) {
5107+ await writeSecret(SECRET_KEYS.MOONSHOT, api_key_moonshot);
5108+ }
5109+
5110+ if (!secret_state[SECRET_KEYS.MOONSHOT]) {
5111+ console.log('No secret key saved for Moonshot');
5112+ return;
5113+ }
5114+ }
5115+
50565116 startStatusLoading();
50575117 saveSettingsDebounced();
50585118 await getStatusOpen();
@@ -5114,6 +5174,9 @@ function toggleChatCompletionForms() {
51145174 else if (oai_settings.chat_completion_source == chat_completion_sources.POLLINATIONS) {
51155175 $('#model_pollinations_select').trigger('change');
51165176 }
5177+ else if (oai_settings.chat_completion_source == chat_completion_sources.MOONSHOT) {
5178+ $('#model_moonshot_select').trigger('change');
5179+ }
51175180 $('[data-source]').each(function () {
51185181 const validSources = $(this).data('source').split(',');
51195182 $(this).toggle(validSources.includes(oai_settings.chat_completion_source));
@@ -5221,6 +5284,10 @@ export function isImageInliningSupported() {
52215284 'grok-4',
52225285 'grok-2-vision',
52235286 'grok-vision',
5287+ // Moonshot
5288+ 'moonshot-v1-8k-vision-preview',
5289+ 'moonshot-v1-32k-vision-preview',
5290+ 'moonshot-v1-128k-vision-preview',
52245291 ];
52255292
52265293 switch (oai_settings.chat_completion_source) {
@@ -5249,6 +5316,8 @@ export function isImageInliningSupported() {
52495316 return visionSupportedModels.some(model => oai_settings.aimlapi_model.includes(model));
52505317 case chat_completion_sources.POLLINATIONS:
52515318 return (Array.isArray(model_list) && model_list.find(m => m.id === oai_settings.pollinations_model)?.vision);
5319+ case chat_completion_sources.MOONSHOT:
5320+ return visionSupportedModels.some(model => oai_settings.moonshot_model.includes(model));
52525321 default:
52535322 return false;
52545323 }
@@ -6037,6 +6106,7 @@ export function initOpenAI() {
60376106 $('#model_custom_select').on('change', onModelChange);
60386107 $('#model_xai_select').on('change', onModelChange);
60396108 $('#model_pollinations_select').on('change', onModelChange);
6109+ $('#model_moonshot_select').on('change', onModelChange);
60406110 $('#settings_preset_openai').on('change', onSettingsPresetChange);
60416111 $('#new_oai_preset').on('click', onNewPresetClick);
60426112 $('#delete_oai_preset').on('click', onDeletePresetClick);
public/scripts/reasoning.js+1 -0
@@ -120,6 +120,7 @@ export function extractReasoningFromData(data, {
120120 return data?.content?.find(part => part.type === 'thinking')?.thinking ?? '';
121121 case chat_completion_sources.AIMLAPI:
122122 case chat_completion_sources.POLLINATIONS:
123+ case chat_completion_sources.MOONSHOT:
123124 case chat_completion_sources.CUSTOM: {
124125 return data?.choices?.[0]?.message?.reasoning_content
125126 ?? data?.choices?.[0]?.message?.reasoning
public/scripts/secrets.js+3 -0
@@ -63,6 +63,7 @@ export const SECRET_KEYS = {
6363 VERTEXAI_SERVICE_ACCOUNT: 'vertexai_service_account_json',
6464 MINIMAX: 'api_key_minimax',
6565 MINIMAX_GROUP_ID: 'minimax_group_id',
66+ MOONSHOT: 'api_key_moonshot',
6667};
6768
6869const FRIENDLY_NAMES = {
@@ -114,6 +115,7 @@ const FRIENDLY_NAMES = {
114115 [SECRET_KEYS.DEEPLX_URL]: 'DeepLX Endpoint (e.g. http://127.0.0.1:1188/translate)',
115116 [SECRET_KEYS.MINIMAX]: 'MiniMax TTS',
116117 [SECRET_KEYS.MINIMAX_GROUP_ID]: 'MiniMax Group ID',
118+ [SECRET_KEYS.MOONSHOT]: 'Moonshot AI',
117119};
118120
119121const INPUT_MAP = {
@@ -148,6 +150,7 @@ const INPUT_MAP = {
148150 [SECRET_KEYS.AIMLAPI]: '#api_key_aimlapi',
149151 [SECRET_KEYS.XAI]: '#api_key_xai',
150152 [SECRET_KEYS.VERTEXAI_SERVICE_ACCOUNT]: '#vertexai_service_account_json',
153+ [SECRET_KEYS.MOONSHOT]: '#api_key_moonshot',
151154};
152155
153156const getLabel = () => moment().format('L LT');
public/scripts/slash-commands.js+1 -0
@@ -4805,6 +4805,7 @@ function getModelOptions(quiet) {
48054805 { id: 'model_aimlapi_select', api: 'openai', type: chat_completion_sources.AIMLAPI },
48064806 { id: 'model_xai_select', api: 'openai', type: chat_completion_sources.XAI },
48074807 { id: 'model_pollinations_select', api: 'openai', type: chat_completion_sources.POLLINATIONS },
4808+ { id: 'model_moonshot_select', api: 'openai', type: chat_completion_sources.MOONSHOT },
48084809 { id: 'model_novel_select', api: 'novel', type: null },
48094810 { id: 'horde_model', api: 'koboldhorde', type: null },
48104811 ];
public/scripts/tool-calling.js+1 -0
@@ -620,6 +620,7 @@ export class ToolManager {
620620 chat_completion_sources.AI21,
621621 chat_completion_sources.XAI,
622622 chat_completion_sources.POLLINATIONS,
623+ chat_completion_sources.MOONSHOT,
623624 ];
624625 return supportedSources.includes(oai_settings.chat_completion_source);
625626 }
src/constants.js+1 -0
@@ -178,6 +178,7 @@ export const CHAT_COMPLETION_SOURCES = {
178178 AIMLAPI: 'aimlapi',
179179 XAI: 'xai',
180180 POLLINATIONS: 'pollinations',
181+ MOONSHOT: 'moonshot',
181182};
182183
183184/**
src/endpoints/backends/chat-completions.js+32 -10
@@ -65,6 +65,7 @@ const API_DEEPSEEK = 'https://api.deepseek.com/beta';
6565const API_XAI = 'https://api.x.ai/v1';
6666const API_AIMLAPI = 'https://api.aimlapi.com/v1';
6767const API_POLLINATIONS = 'https://text.pollinations.ai/openai';
68+const API_MOONSHOT = 'https://api.moonshot.ai/v1';
6869
6970/**
7071 * Gets OpenRouter transforms based on the request.
@@ -98,6 +99,23 @@ function getOpenRouterPlugins(request) {
9899}
99100
100101/**
102+ * Hacky way to use JSON schema only if json_object format is supported.
103+ * @param {object} bodyParams Additional body parameters
104+ * @param {object[]} messages Array of messages
105+ * @param {object} jsonSchema JSON schema object
106+ */
107+function setJsonObjectFormat(bodyParams, messages, jsonSchema) {
108+ bodyParams['response_format'] = {
109+ type: 'json_object',
110+ };
111+ const message = {
112+ role: 'user',
113+ content: `JSON schema for the response:\n${JSON.stringify(jsonSchema.value, null, 4)}`,
114+ };
115+ messages.push(message);
116+}
117+
118+/**
101119 * Sends a request to Claude API.
102120 * @param {express.Request} request Express request
103121 * @param {express.Response} response Express response
@@ -890,7 +908,7 @@ async function sendDeepSeekRequest(request, response) {
890908 const postProcessType = String(request.body.model).endsWith('-reasoner')
891909 ? PROMPT_PROCESSING_TYPE.STRICT_TOOLS
892910 : PROMPT_PROCESSING_TYPE.SEMI_TOOLS;
893911 const processedMessages = addAssistantPrefix(postProcessPrompt(request.body.messages, postProcessType, getPromptNames(request)), bodyParams.tools, 'prefix');
894912
895913 const requestBody = {
896914 'messages': processedMessages,
@@ -1220,6 +1238,10 @@ router.post('/status', async function (request, statusResponse) {
12201238 apiUrl = API_GROQ;
12211239 apiKey = readSecret(request.user.directories, SECRET_KEYS.GROQ);
12221240 headers = {};
1241+ } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.MOONSHOT) {
1242+ apiUrl = API_MOONSHOT;
1243+ apiKey = readSecret(request.user.directories, SECRET_KEYS.MOONSHOT);
1244+ headers = {};
12231245 } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.MAKERSUITE) {
12241246 apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MAKERSUITE);
12251247 apiUrl = trimTrailingSlash(request.body.reverse_proxy || API_MAKERSUITE);
@@ -1598,17 +1620,17 @@ router.post('/generate', function (request, response) {
15981620 referrer: 'sillytavern',
15991621 seed: request.body.seed ?? Math.floor(Math.random() * 99999999),
16001622 };
1601- // Hack to support JSON schema
16021623 if (request.body.json_schema) {
1603- bodyParams['response_format'] = {
1624+ setJsonObjectFormat(bodyParams, request.body.messages, request.body.json_schema);
1604- type: 'json_object',
1605- };
1606- const message = {
1607- role: 'user',
1608- content: `JSON schema for the response:\n${JSON.stringify(request.body.json_schema.value, null, 4)}`,
1609- };
1610- request.body.messages.push(message);
16111625 }
1626+ } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.MOONSHOT) {
1627+ apiUrl = API_MOONSHOT;
1628+ apiKey = readSecret(request.user.directories, SECRET_KEYS.MOONSHOT);
1629+ headers = {};
1630+ bodyParams = {};
1631+ request.body.json_schema
1632+ ? setJsonObjectFormat(bodyParams, request.body.messages, request.body.json_schema)
1633+ : addAssistantPrefix(request.body.messages, [], 'partial');
16121634 } else {
16131635 console.warn('This chat completion source is not supported yet.');
16141636 return response.status(400).send({ error: true });
src/endpoints/openai.js+8 -0
@@ -73,6 +73,10 @@ router.post('/caption-image', async (request, response) => {
7373 key = readSecret(request.user.directories, SECRET_KEYS.COHERE);
7474 }
7575
76+ if (request.body.api === 'moonshot') {
77+ key = readSecret(request.user.directories, SECRET_KEYS.MOONSHOT);
78+ }
79+
7680 const noKeyTypes = ['custom', 'ooba', 'koboldcpp', 'vllm', 'llamacpp', 'pollinations'];
7781 if (!key && !request.body.reverse_proxy && !noKeyTypes.includes(request.body.api)) {
7882 console.warn('No key found for API', request.body.api);
@@ -153,6 +157,10 @@ router.post('/caption-image', async (request, response) => {
153157 apiUrl = 'https://text.pollinations.ai/openai/chat/completions';
154158 }
155159
160+ if (request.body.api === 'moonshot') {
161+ apiUrl = 'https://api.moonshot.ai/v1/chat/completions';
162+ }
163+
156164 if (['koboldcpp', 'vllm', 'llamacpp', 'ooba'].includes(request.body.api)) {
157165 apiUrl = `${trimV1(request.body.server_url)}/v1/chat/completions`;
158166 }
src/endpoints/secrets.js+1 -0
@@ -56,6 +56,7 @@ export const SECRET_KEYS = {
5656 VERTEXAI_SERVICE_ACCOUNT: 'vertexai_service_account_json',
5757 MINIMAX: 'api_key_minimax',
5858 MINIMAX_GROUP_ID: 'minimax_group_id',
59+ MOONSHOT: 'api_key_moonshot',
5960};
6061
6162/**
src/prompt-converters.js+3 -2
@@ -53,15 +53,16 @@ export function getPromptNames(request) {
5353 * Adds an assistant prefix to the last message.
5454 * @param {any[]} prompt Prompt messages array
5555 * @param {any[]} tools Array of tool definitions
56+ * @param {string} property The property to set the prefix on
5657 * @returns {any[]} Transformed messages array
5758 */
5859export function addAssistantPrefix(prompt, tools, property) {
5960 if (!prompt.length) {
6061 return prompt;
6162 }
6263 const hasAnyTools = (Array.isArray(tools) && tools.length > 0) || prompt.some(x => x.role === 'tool');
6364 if (!hasAnyTools && prompt[prompt.length - 1].role === 'assistant') {
6465 prompt[prompt.length - 1].prefix[property] = true;
6566 }
6667 return prompt;
6768}