Feat/moonshot api (#4330)

* moonshot

* Partial mode + JSON schema

* Add logo image

* Limit max temp to 1

* Add to captioning extension
This commit is contained in:
Cohee
2025-07-31 00:01:04 +03:00
committed by GitHub
parent dd4dae1dc6
commit 0e38bfbf05
16 changed files with 170 additions and 22 deletions
+1
View File
@@ -0,0 +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>

After

Width:  |  Height:  |  Size: 2.9 KiB

+36 -8
View File
@@ -652,7 +652,7 @@
<input type="number" id="openai_max_tokens" name="openai_max_tokens" class="text_pole" min="1" max="65536">
</div>
</div>
<div class="range-block" data-source="openai,custom,xai,aimlapi">
<div class="range-block" data-source="openai,custom,xai,aimlapi,moonshot">
<div class="range-block-title" data-i18n="Multiple swipes per generation">
Multiple swipes per generation
</div>
@@ -691,7 +691,7 @@
</span>
</div>
</div>
<div class="range-block" data-source="openai,claude,aimlapi,openrouter,ai21,makersuite,vertexai,mistralai,custom,cohere,perplexity,groq,nanogpt,deepseek,xai,pollinations">
<div class="range-block" data-source="openai,claude,aimlapi,openrouter,ai21,makersuite,vertexai,mistralai,custom,cohere,perplexity,groq,nanogpt,deepseek,xai,pollinations,moonshot">
<div class="range-block-title" data-i18n="Temperature">
Temperature
</div>
@@ -704,7 +704,7 @@
</div>
</div>
</div>
<div class="range-block" data-source="openai,aimlapi,openrouter,custom,cohere,perplexity,groq,mistralai,nanogpt,deepseek,xai,pollinations">
<div class="range-block" data-source="openai,aimlapi,openrouter,custom,cohere,perplexity,groq,mistralai,nanogpt,deepseek,xai,pollinations,moonshot">
<div class="range-block-title" data-i18n="Frequency Penalty">
Frequency Penalty
</div>
@@ -717,7 +717,7 @@
</div>
</div>
</div>
<div class="range-block" data-source="openai,aimlapi,openrouter,custom,cohere,perplexity,groq,mistralai,nanogpt,deepseek,xai,pollinations">
<div class="range-block" data-source="openai,aimlapi,openrouter,custom,cohere,perplexity,groq,mistralai,nanogpt,deepseek,xai,pollinations,moonshot">
<div class="range-block-title" data-i18n="Presence Penalty">
Presence Penalty
</div>
@@ -743,7 +743,7 @@
</div>
</div>
</div>
<div class="range-block" data-source="openai,claude,aimlapi,openrouter,ai21,makersuite,vertexai,mistralai,custom,cohere,perplexity,groq,nanogpt,deepseek,xai,pollinations">
<div class="range-block" data-source="openai,claude,aimlapi,openrouter,ai21,makersuite,vertexai,mistralai,custom,cohere,perplexity,groq,nanogpt,deepseek,xai,pollinations,moonshot">
<div class="range-block-title" data-i18n="Top P">
Top P
</div>
@@ -1984,7 +1984,7 @@
</b>
</div>
</div>
<div class="range-block" data-source="openai,cohere,mistralai,custom,claude,aimlapi,openrouter,groq,deepseek,makersuite,vertexai,ai21,xai,pollinations">
<div class="range-block" data-source="openai,cohere,mistralai,custom,claude,aimlapi,openrouter,groq,deepseek,makersuite,vertexai,ai21,xai,pollinations,moonshot">
<label for="openai_function_calling" class="checkbox_label flexWrap widthFreeExpand">
<input id="openai_function_calling" type="checkbox" />
<span data-i18n="Enable function calling">Enable function calling</span>
@@ -1995,7 +1995,7 @@
<strong data-i18n="enable_functions_desc_4">Not supported when Prompt Post-Processing with "no tools" is used!</strong>
</div>
</div>
<div class="range-block" data-source="openai,aimlapi,openrouter,mistralai,makersuite,vertexai,claude,custom,xai,pollinations">
<div class="range-block" data-source="openai,aimlapi,openrouter,mistralai,makersuite,vertexai,claude,custom,xai,pollinations,moonshot">
<label for="openai_image_inlining" class="checkbox_label flexWrap widthFreeExpand">
<input id="openai_image_inlining" type="checkbox" />
<span data-i18n="Send inline images">Send inline images</span>
@@ -2065,7 +2065,7 @@
</span>
</div>
</div>
<div class="range-block" data-source="deepseek,aimlapi,openrouter,custom,claude,xai,makersuite,vertexai,pollinations">
<div class="range-block" data-source="deepseek,aimlapi,openrouter,custom,claude,xai,makersuite,vertexai,pollinations,moonshot">
<label for="openai_show_thoughts" class="checkbox_label widthFreeExpand">
<input id="openai_show_thoughts" type="checkbox" />
<span data-i18n="Request model reasoning">Request model reasoning</span>
@@ -2797,6 +2797,7 @@
<option value="makersuite">Google AI Studio</option>
<option value="vertexai">Google Vertex AI</option>
<option value="mistralai">MistralAI</option>
<option value="moonshot">Moonshot AI</option>
<option value="nanogpt">NanoGPT</option>
<option value="openrouter">OpenRouter</option>
<option value="perplexity">Perplexity</option>
@@ -3627,6 +3628,33 @@
</span>
</div>
</div>
<div id="moonshot_form" data-source="moonshot">
<h4>
<a data-i18n="Moonshot AI API Key" href="https://platform.moonshot.ai/console/api-keys" target="_blank" rel="noopener noreferrer">
Moonshot AI API Key
</a>
</h4>
<div class="flex-container">
<input id="api_key_moonshot" name="api_key_moonshot" class="text_pole flex1" value="" type="text" autocomplete="off">
<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>
</div>
<div data-for="api_key_moonshot" class="neutral_warning" data-i18n="For privacy reasons, your API key will be hidden after you click 'Connect'.">
For privacy reasons, your API key will be hidden after you click 'Connect'.
</div>
<h4 data-i18n="Moonshot AI Model">Moonshot AI Model</h4>
<select id="model_moonshot_select">
<option value="kimi-k2-0711-preview">kimi-k2-0711-preview</option>
<option value="moonshot-v1-8k">moonshot-v1-8k</option>
<option value="moonshot-v1-32k">moonshot-v1-32k</option>
<option value="moonshot-v1-128k">moonshot-v1-128k</option>
<option value="moonshot-v1-auto">moonshot-v1-auto</option>
<option value="kimi-latest">kimi-latest</option>
<option value="moonshot-v1-8k-vision-preview">moonshot-v1-8k-vision-preview</option>
<option value="moonshot-v1-32k-vision-preview">moonshot-v1-32k-vision-preview</option>
<option value="moonshot-v1-128k-vision-preview">moonshot-v1-128k-vision-preview</option>
<option value="kimi-thinking-preview">kimi-thinking-preview</option>
</select>
</div>
<div id="prompt_post_processing_form">
<h4>
<span data-i18n="Prompt Post-Processing">
+1
View File
@@ -410,6 +410,7 @@ function RA_autoconnect(PrevApi) {
|| (secret_state[SECRET_KEYS.DEEPSEEK] && oai_settings.chat_completion_source == chat_completion_sources.DEEPSEEK)
|| (secret_state[SECRET_KEYS.XAI] && oai_settings.chat_completion_source == chat_completion_sources.XAI)
|| (secret_state[SECRET_KEYS.AIMLAPI] && oai_settings.chat_completion_source == chat_completion_sources.AIMLAPI)
|| (secret_state[SECRET_KEYS.MOONSHOT] && oai_settings.chat_completion_source == chat_completion_sources.MOONSHOT)
|| (oai_settings.chat_completion_source === chat_completion_sources.POLLINATIONS)
|| (isValidUrl(oai_settings.custom_url) && oai_settings.chat_completion_source == chat_completion_sources.CUSTOM)
) {
@@ -438,6 +438,7 @@ jQuery(async function () {
'groq': SECRET_KEYS.GROQ,
'cohere': SECRET_KEYS.COHERE,
'aimlapi': SECRET_KEYS.AIMLAPI,
'moonshot': SECRET_KEYS.MOONSHOT,
};
if (chatCompletionApis[api] && secret_state[chatCompletionApis[api]]) {
@@ -27,6 +27,7 @@
<option value="koboldcpp">KoboldCpp</option>
<option value="llamacpp">llama.cpp</option>
<option value="mistral">MistralAI</option>
<option value="moonshot">Moonshot AI</option>
<option value="ollama">Ollama</option>
<option value="openai">OpenAI</option>
<option value="openrouter">OpenRouter</option>
@@ -51,6 +52,9 @@
<option data-type="mistral" value="mistral-small-latest">mistral-small-latest</option>
<option data-type="mistral" value="mistral-medium-latest">mistral-medium-latest</option>
<option data-type="mistral" value="mistral-medium-2505">mistral-medium-2505</option>
<option data-type="moonshot" value="moonshot-v1-8k-vision-preview">moonshot-v1-8k-vision-preview</option>
<option data-type="moonshot" value="moonshot-v1-32k-vision-preview">moonshot-v1-32k-vision-preview</option>
<option data-type="moonshot" value="moonshot-v1-128k-vision-preview">moonshot-v1-128k-vision-preview</option>
<option data-type="openai" value="gpt-4.1">gpt-4.1</option>
<option data-type="openai" value="gpt-4.1-2025-04-14">gpt-4.1-2025-04-14</option>
<option data-type="openai" value="gpt-4.1-mini">gpt-4.1-mini</option>
+4
View File
@@ -238,6 +238,10 @@ function throwIfInvalidModel(useReverseProxy) {
if (multimodalApi === 'aimlapi' && !secret_state[SECRET_KEYS.AIMLAPI]) {
throw new Error('AI/ML API key is not set.');
}
if (multimodalApi === 'moonshot' && !secret_state[SECRET_KEYS.MOONSHOT]) {
throw new Error('Moonshot AI API key is not set.');
}
}
/**
+72 -2
View File
@@ -183,6 +183,7 @@ export const chat_completion_sources = {
AIMLAPI: 'aimlapi',
XAI: 'xai',
POLLINATIONS: 'pollinations',
MOONSHOT: 'moonshot',
};
const character_names_behavior = {
@@ -272,6 +273,7 @@ export const settingsToUpdate = {
aimlapi_model: ['#model_aimlapi_select', 'aimlapi_model', false, true],
xai_model: ['#model_xai_select', 'xai_model', false, true],
pollinations_model: ['#model_pollinations_select', 'pollinations_model', false, true],
moonshot_model: ['#model_moonshot_select', 'moonshot_model', false, true],
custom_model: ['#custom_model_id', 'custom_model', false, true],
custom_url: ['#custom_api_url_text', 'custom_url', false, true],
custom_include_body: ['#custom_include_body', 'custom_include_body', false, true],
@@ -367,6 +369,7 @@ const default_settings = {
aimlapi_model: 'gpt-4o-mini-2024-07-18',
xai_model: 'grok-3-beta',
pollinations_model: 'openai',
moonshot_model: 'kimi-latest',
custom_model: '',
custom_url: '',
custom_include_body: '',
@@ -453,6 +456,7 @@ const oai_settings = {
aimlapi_model: 'gpt-4-turbo',
xai_model: 'grok-3-beta',
pollinations_model: 'openai',
moonshot_model: 'kimi-latest',
custom_model: '',
custom_url: '',
custom_include_body: '',
@@ -1613,6 +1617,8 @@ export function getChatCompletionModel(source = null) {
return oai_settings.xai_model;
case chat_completion_sources.POLLINATIONS:
return oai_settings.pollinations_model;
case chat_completion_sources.MOONSHOT:
return oai_settings.moonshot_model;
default:
console.error(`Unknown chat completion source: ${activeSource}`);
return '';
@@ -2038,13 +2044,14 @@ async function sendOpenAIRequest(type, messages, signal, { jsonSchema = null } =
const isAimlapi = oai_settings.chat_completion_source == chat_completion_sources.AIMLAPI;
const isXAI = oai_settings.chat_completion_source == chat_completion_sources.XAI;
const isPollinations = oai_settings.chat_completion_source == chat_completion_sources.POLLINATIONS;
const isMoonshot = oai_settings.chat_completion_source == chat_completion_sources.MOONSHOT;
const isTextCompletion = isOAI && textCompletionModels.includes(oai_settings.openai_model);
const isQuiet = type === 'quiet';
const isImpersonate = type === 'impersonate';
const isContinue = type === 'continue';
const stream = oai_settings.stream_openai && !isQuiet && !(isOAI && ['o1-2024-12-17', 'o1'].includes(oai_settings.openai_model));
const useLogprobs = !!power_user.request_token_probabilities;
const canMultiSwipe = oai_settings.n > 1 && !isContinue && !isImpersonate && !isQuiet && (isOAI || isCustom || isXAI || isAimlapi);
const canMultiSwipe = oai_settings.n > 1 && !isContinue && !isImpersonate && !isQuiet && (isOAI || isCustom || isXAI || isAimlapi || isMoonshot);
const logitBiasSources = [chat_completion_sources.OPENAI, chat_completion_sources.OPENROUTER, chat_completion_sources.CUSTOM];
if (oai_settings.bias_preset_selected
@@ -2384,7 +2391,7 @@ export function getStreamingReply(data, state, { chatCompletionSource = null, ov
state.reasoning += (data.choices?.filter(x => x?.delta?.reasoning)?.[0]?.delta?.reasoning || '');
}
return data.choices?.[0]?.delta?.content ?? data.choices?.[0]?.message?.content ?? data.choices?.[0]?.text ?? '';
} else if ([chat_completion_sources.CUSTOM, chat_completion_sources.POLLINATIONS, chat_completion_sources.AIMLAPI].includes(chat_completion_source)) {
} else if ([chat_completion_sources.CUSTOM, chat_completion_sources.POLLINATIONS, chat_completion_sources.AIMLAPI, chat_completion_sources.MOONSHOT].includes(chat_completion_source)) {
if (show_thoughts) {
state.reasoning +=
data.choices?.filter(x => x?.delta?.reasoning_content)?.[0]?.delta?.reasoning_content ??
@@ -3345,6 +3352,7 @@ function loadOpenAISettings(data, settings) {
oai_settings.aimlapi_model = settings.aimlapi_model ?? default_settings.aimlapi_model;
oai_settings.xai_model = settings.xai_model ?? default_settings.xai_model;
oai_settings.pollinations_model = settings.pollinations_model ?? default_settings.pollinations_model;
oai_settings.moonshot_model = settings.moonshot_model ?? default_settings.moonshot_model;
oai_settings.custom_model = settings.custom_model ?? default_settings.custom_model;
oai_settings.custom_url = settings.custom_url ?? default_settings.custom_url;
oai_settings.custom_include_body = settings.custom_include_body ?? default_settings.custom_include_body;
@@ -3442,6 +3450,8 @@ function loadOpenAISettings(data, settings) {
$(`#model_xai_select option[value="${oai_settings.xai_model}"`).prop('selected', true);
$('#model_pollinations_select').val(oai_settings.pollinations_model);
$(`#model_pollinations_select option[value="${oai_settings.pollinations_model}"`).prop('selected', true);
$('#model_moonshot_select').val(oai_settings.moonshot_model);
$(`#model_moonshot_select option[value="${oai_settings.moonshot_model}"`).prop('selected', true);
$('#custom_model_id').val(oai_settings.custom_model);
$('#custom_api_url_text').val(oai_settings.custom_url);
$('#openai_max_context').val(oai_settings.openai_max_context);
@@ -3714,6 +3724,7 @@ async function saveOpenAIPreset(name, settings, triggerUi = true) {
xai_model: settings.xai_model,
pollinations_model: settings.pollinations_model,
aimlapi_model: settings.aimlapi_model,
moonshot_model: settings.moonshot_model,
custom_model: settings.custom_model,
custom_url: settings.custom_url,
custom_include_body: settings.custom_include_body,
@@ -4424,6 +4435,28 @@ function getGroqMaxContext(model, isUnlocked) {
return Object.entries(contextMap).find(([key]) => model.includes(key))?.[1] || max_128k;
}
function getMoonshotMaxContext(model, isUnlocked) {
if (isUnlocked) {
return unlocked_max;
}
const contextMap = {
'moonshot-v1-8k': max_8k,
'moonshot-v1-32k': max_32k,
'moonshot-v1-128k': max_128k,
'moonshot-v1-auto': max_128k,
'moonshot-v1-8k-vision-preview': max_8k,
'moonshot-v1-32k-vision-preview': max_32k,
'moonshot-v1-128k-vision-preview': max_128k,
'kimi-k2-0711-preview': max_32k,
'kimi-latest': max_32k,
'kimi-thinking-preview': max_32k,
};
// Return context size if model found, otherwise default to 32k
return Object.entries(contextMap).find(([key]) => model.includes(key))?.[1] || max_32k;
}
async function onModelChange() {
biasCache = undefined;
let value = String($(this).val() || '');
@@ -4559,6 +4592,11 @@ async function onModelChange() {
oai_settings.xai_model = value;
}
if (value && $(this).is('#model_moonshot_select')) {
console.log('Moonshot model changed to', value);
oai_settings.moonshot_model = value;
}
if ([chat_completion_sources.MAKERSUITE, chat_completion_sources.VERTEXAI].includes(oai_settings.chat_completion_source)) {
if (oai_settings.max_context_unlocked) {
$('#openai_max_context').attr('max', max_2mil);
@@ -4822,6 +4860,15 @@ async function onModelChange() {
$('#freq_pen_openai').attr('max', 2).attr('min', -2).val(oai_settings.freq_pen_openai).trigger('input');
}
if (oai_settings.chat_completion_source === chat_completion_sources.MOONSHOT) {
const maxContext = getMoonshotMaxContext(oai_settings.moonshot_model, oai_settings.max_context_unlocked);
$('#openai_max_context').attr('max', maxContext);
oai_settings.openai_max_context = Math.min(Number($('#openai_max_context').attr('max')), oai_settings.openai_max_context);
$('#openai_max_context').val(oai_settings.openai_max_context).trigger('input');
oai_settings.temp_openai = Math.min(claude_max_temp, oai_settings.temp_openai);
$('#temp_openai').attr('max', claude_max_temp).val(oai_settings.temp_openai).trigger('input');
}
$('#openai_max_context_counter').attr('max', Number($('#openai_max_context').attr('max')));
saveSettingsDebounced();
@@ -5053,6 +5100,19 @@ async function onConnectButtonClick(e) {
}
}
if (oai_settings.chat_completion_source == chat_completion_sources.MOONSHOT) {
const api_key_moonshot = String($('#api_key_moonshot').val()).trim();
if (api_key_moonshot.length) {
await writeSecret(SECRET_KEYS.MOONSHOT, api_key_moonshot);
}
if (!secret_state[SECRET_KEYS.MOONSHOT]) {
console.log('No secret key saved for Moonshot');
return;
}
}
startStatusLoading();
saveSettingsDebounced();
await getStatusOpen();
@@ -5114,6 +5174,9 @@ function toggleChatCompletionForms() {
else if (oai_settings.chat_completion_source == chat_completion_sources.POLLINATIONS) {
$('#model_pollinations_select').trigger('change');
}
else if (oai_settings.chat_completion_source == chat_completion_sources.MOONSHOT) {
$('#model_moonshot_select').trigger('change');
}
$('[data-source]').each(function () {
const validSources = $(this).data('source').split(',');
$(this).toggle(validSources.includes(oai_settings.chat_completion_source));
@@ -5221,6 +5284,10 @@ export function isImageInliningSupported() {
'grok-4',
'grok-2-vision',
'grok-vision',
// Moonshot
'moonshot-v1-8k-vision-preview',
'moonshot-v1-32k-vision-preview',
'moonshot-v1-128k-vision-preview',
];
switch (oai_settings.chat_completion_source) {
@@ -5249,6 +5316,8 @@ export function isImageInliningSupported() {
return visionSupportedModels.some(model => oai_settings.aimlapi_model.includes(model));
case chat_completion_sources.POLLINATIONS:
return (Array.isArray(model_list) && model_list.find(m => m.id === oai_settings.pollinations_model)?.vision);
case chat_completion_sources.MOONSHOT:
return visionSupportedModels.some(model => oai_settings.moonshot_model.includes(model));
default:
return false;
}
@@ -6037,6 +6106,7 @@ export function initOpenAI() {
$('#model_custom_select').on('change', onModelChange);
$('#model_xai_select').on('change', onModelChange);
$('#model_pollinations_select').on('change', onModelChange);
$('#model_moonshot_select').on('change', onModelChange);
$('#settings_preset_openai').on('change', onSettingsPresetChange);
$('#new_oai_preset').on('click', onNewPresetClick);
$('#delete_oai_preset').on('click', onDeletePresetClick);
+1
View File
@@ -120,6 +120,7 @@ export function extractReasoningFromData(data, {
return data?.content?.find(part => part.type === 'thinking')?.thinking ?? '';
case chat_completion_sources.AIMLAPI:
case chat_completion_sources.POLLINATIONS:
case chat_completion_sources.MOONSHOT:
case chat_completion_sources.CUSTOM: {
return data?.choices?.[0]?.message?.reasoning_content
?? data?.choices?.[0]?.message?.reasoning
+3
View File
@@ -63,6 +63,7 @@ export const SECRET_KEYS = {
VERTEXAI_SERVICE_ACCOUNT: 'vertexai_service_account_json',
MINIMAX: 'api_key_minimax',
MINIMAX_GROUP_ID: 'minimax_group_id',
MOONSHOT: 'api_key_moonshot',
};
const FRIENDLY_NAMES = {
@@ -114,6 +115,7 @@ const FRIENDLY_NAMES = {
[SECRET_KEYS.DEEPLX_URL]: 'DeepLX Endpoint (e.g. http://127.0.0.1:1188/translate)',
[SECRET_KEYS.MINIMAX]: 'MiniMax TTS',
[SECRET_KEYS.MINIMAX_GROUP_ID]: 'MiniMax Group ID',
[SECRET_KEYS.MOONSHOT]: 'Moonshot AI',
};
const INPUT_MAP = {
@@ -148,6 +150,7 @@ const INPUT_MAP = {
[SECRET_KEYS.AIMLAPI]: '#api_key_aimlapi',
[SECRET_KEYS.XAI]: '#api_key_xai',
[SECRET_KEYS.VERTEXAI_SERVICE_ACCOUNT]: '#vertexai_service_account_json',
[SECRET_KEYS.MOONSHOT]: '#api_key_moonshot',
};
const getLabel = () => moment().format('L LT');
+1
View File
@@ -4805,6 +4805,7 @@ function getModelOptions(quiet) {
{ id: 'model_aimlapi_select', api: 'openai', type: chat_completion_sources.AIMLAPI },
{ id: 'model_xai_select', api: 'openai', type: chat_completion_sources.XAI },
{ id: 'model_pollinations_select', api: 'openai', type: chat_completion_sources.POLLINATIONS },
{ id: 'model_moonshot_select', api: 'openai', type: chat_completion_sources.MOONSHOT },
{ id: 'model_novel_select', api: 'novel', type: null },
{ id: 'horde_model', api: 'koboldhorde', type: null },
];
+1
View File
@@ -620,6 +620,7 @@ export class ToolManager {
chat_completion_sources.AI21,
chat_completion_sources.XAI,
chat_completion_sources.POLLINATIONS,
chat_completion_sources.MOONSHOT,
];
return supportedSources.includes(oai_settings.chat_completion_source);
}
+1
View File
@@ -178,6 +178,7 @@ export const CHAT_COMPLETION_SOURCES = {
AIMLAPI: 'aimlapi',
XAI: 'xai',
POLLINATIONS: 'pollinations',
MOONSHOT: 'moonshot',
};
/**
+32 -10
View File
@@ -65,6 +65,7 @@ const API_DEEPSEEK = 'https://api.deepseek.com/beta';
const API_XAI = 'https://api.x.ai/v1';
const API_AIMLAPI = 'https://api.aimlapi.com/v1';
const API_POLLINATIONS = 'https://text.pollinations.ai/openai';
const API_MOONSHOT = 'https://api.moonshot.ai/v1';
/**
* Gets OpenRouter transforms based on the request.
@@ -97,6 +98,23 @@ function getOpenRouterPlugins(request) {
return plugins;
}
/**
* Hacky way to use JSON schema only if json_object format is supported.
* @param {object} bodyParams Additional body parameters
* @param {object[]} messages Array of messages
* @param {object} jsonSchema JSON schema object
*/
function setJsonObjectFormat(bodyParams, messages, jsonSchema) {
bodyParams['response_format'] = {
type: 'json_object',
};
const message = {
role: 'user',
content: `JSON schema for the response:\n${JSON.stringify(jsonSchema.value, null, 4)}`,
};
messages.push(message);
}
/**
* Sends a request to Claude API.
* @param {express.Request} request Express request
@@ -890,7 +908,7 @@ async function sendDeepSeekRequest(request, response) {
const postProcessType = String(request.body.model).endsWith('-reasoner')
? PROMPT_PROCESSING_TYPE.STRICT_TOOLS
: PROMPT_PROCESSING_TYPE.SEMI_TOOLS;
const processedMessages = addAssistantPrefix(postProcessPrompt(request.body.messages, postProcessType, getPromptNames(request)), bodyParams.tools);
const processedMessages = addAssistantPrefix(postProcessPrompt(request.body.messages, postProcessType, getPromptNames(request)), bodyParams.tools, 'prefix');
const requestBody = {
'messages': processedMessages,
@@ -1220,6 +1238,10 @@ router.post('/status', async function (request, statusResponse) {
apiUrl = API_GROQ;
apiKey = readSecret(request.user.directories, SECRET_KEYS.GROQ);
headers = {};
} else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.MOONSHOT) {
apiUrl = API_MOONSHOT;
apiKey = readSecret(request.user.directories, SECRET_KEYS.MOONSHOT);
headers = {};
} else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.MAKERSUITE) {
apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(request.user.directories, SECRET_KEYS.MAKERSUITE);
apiUrl = trimTrailingSlash(request.body.reverse_proxy || API_MAKERSUITE);
@@ -1598,17 +1620,17 @@ router.post('/generate', function (request, response) {
referrer: 'sillytavern',
seed: request.body.seed ?? Math.floor(Math.random() * 99999999),
};
// Hack to support JSON schema
if (request.body.json_schema) {
bodyParams['response_format'] = {
type: 'json_object',
};
const message = {
role: 'user',
content: `JSON schema for the response:\n${JSON.stringify(request.body.json_schema.value, null, 4)}`,
};
request.body.messages.push(message);
setJsonObjectFormat(bodyParams, request.body.messages, request.body.json_schema);
}
} else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.MOONSHOT) {
apiUrl = API_MOONSHOT;
apiKey = readSecret(request.user.directories, SECRET_KEYS.MOONSHOT);
headers = {};
bodyParams = {};
request.body.json_schema
? setJsonObjectFormat(bodyParams, request.body.messages, request.body.json_schema)
: addAssistantPrefix(request.body.messages, [], 'partial');
} else {
console.warn('This chat completion source is not supported yet.');
return response.status(400).send({ error: true });
+8
View File
@@ -73,6 +73,10 @@ router.post('/caption-image', async (request, response) => {
key = readSecret(request.user.directories, SECRET_KEYS.COHERE);
}
if (request.body.api === 'moonshot') {
key = readSecret(request.user.directories, SECRET_KEYS.MOONSHOT);
}
const noKeyTypes = ['custom', 'ooba', 'koboldcpp', 'vllm', 'llamacpp', 'pollinations'];
if (!key && !request.body.reverse_proxy && !noKeyTypes.includes(request.body.api)) {
console.warn('No key found for API', request.body.api);
@@ -153,6 +157,10 @@ router.post('/caption-image', async (request, response) => {
apiUrl = 'https://text.pollinations.ai/openai/chat/completions';
}
if (request.body.api === 'moonshot') {
apiUrl = 'https://api.moonshot.ai/v1/chat/completions';
}
if (['koboldcpp', 'vllm', 'llamacpp', 'ooba'].includes(request.body.api)) {
apiUrl = `${trimV1(request.body.server_url)}/v1/chat/completions`;
}
+1
View File
@@ -56,6 +56,7 @@ export const SECRET_KEYS = {
VERTEXAI_SERVICE_ACCOUNT: 'vertexai_service_account_json',
MINIMAX: 'api_key_minimax',
MINIMAX_GROUP_ID: 'minimax_group_id',
MOONSHOT: 'api_key_moonshot',
};
/**
+3 -2
View File
@@ -53,15 +53,16 @@ export function getPromptNames(request) {
* Adds an assistant prefix to the last message.
* @param {any[]} prompt Prompt messages array
* @param {any[]} tools Array of tool definitions
* @param {string} property The property to set the prefix on
* @returns {any[]} Transformed messages array
*/
export function addAssistantPrefix(prompt, tools) {
export function addAssistantPrefix(prompt, tools, property) {
if (!prompt.length) {
return prompt;
}
const hasAnyTools = (Array.isArray(tools) && tools.length > 0) || prompt.some(x => x.role === 'tool');
if (!hasAnyTools && prompt[prompt.length - 1].role === 'assistant') {
prompt[prompt.length - 1].prefix = true;
prompt[prompt.length - 1][property] = true;
}
return prompt;
}