From 6d6bd14622f838b371ab2d0da35e672c2eb62e48 Mon Sep 17 00:00:00 2001 From: Sean Hatfield Date: Tue, 22 Jul 2025 09:56:51 -0700 Subject: [PATCH] Moonshot AI LLM & agent provider (#4178) * add moonshot ai LLM & agent provider * fix moonshot agent calling * handle attachments/fix moonshot llm provider * update docs/example env * add moonshot to onboarding privacy * add moonshot to onboarding llm preference * update privacy for moonshot ai * update logo higher res * remove caching and use modelmap --- README.md | 1 + docker/.env.example | 4 + .../LLMSelection/MoonshotAiOptions/index.jsx | 117 ++++++++++++ frontend/src/media/llmprovider/moonshotai.png | Bin 0 -> 52334 bytes .../GeneralSettings/LLMPreference/index.jsx | 10 ++ .../Steps/DataHandling/index.jsx | 10 ++ .../Steps/LLMPreference/index.jsx | 9 + .../AgentConfig/AgentLLMSelection/index.jsx | 1 + server/.env.example | 4 + server/endpoints/utils.js | 3 + server/models/systemSettings.js | 5 + server/utils/AiProviders/modelMap/index.js | 1 + server/utils/AiProviders/moonshotAi/index.js | 169 ++++++++++++++++++ server/utils/agents/aibitat/index.js | 2 + .../agents/aibitat/providers/ai-provider.js | 9 +- .../utils/agents/aibitat/providers/index.js | 2 + .../agents/aibitat/providers/moonshotAi.js | 103 +++++++++++ server/utils/agents/index.js | 6 + server/utils/helpers/customModels.js | 28 +++ server/utils/helpers/index.js | 8 + server/utils/helpers/updateENV.js | 11 ++ 21 files changed, 502 insertions(+), 1 deletion(-) create mode 100644 frontend/src/components/LLMSelection/MoonshotAiOptions/index.jsx create mode 100644 frontend/src/media/llmprovider/moonshotai.png create mode 100644 server/utils/AiProviders/moonshotAi/index.js create mode 100644 server/utils/agents/aibitat/providers/moonshotAi.js diff --git a/README.md b/README.md index a8199403..65c529b1 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,7 @@ AnythingLLM divides your documents into objects called `workspaces`. A Workspace - [xAI](https://x.ai/) - [Novita AI (chat models)](https://novita.ai/model-api/product/llm-api?utm_source=github_anything-llm&utm_medium=github_readme&utm_campaign=link) - [PPIO](https://ppinfra.com?utm_source=github_anything-llm) +- [Moonshot AI](https://www.moonshot.ai/) **Embedder models:** diff --git a/docker/.env.example b/docker/.env.example index d0b6dbeb..6a5d8b33 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -133,6 +133,10 @@ GID='1000' # PPIO_API_KEY='your-ppio-api-key-here' # PPIO_MODEL_PREF=deepseek/deepseek-v3/community +# LLM_PROVIDER='moonshotai' +# MOONSHOT_AI_API_KEY='your-moonshot-api-key-here' +# MOONSHOT_AI_MODEL_PREF='moonshot-v1-32k' + ########################################### ######## Embedding API SElECTION ########## ########################################### diff --git a/frontend/src/components/LLMSelection/MoonshotAiOptions/index.jsx b/frontend/src/components/LLMSelection/MoonshotAiOptions/index.jsx new file mode 100644 index 00000000..4546fef0 --- /dev/null +++ b/frontend/src/components/LLMSelection/MoonshotAiOptions/index.jsx @@ -0,0 +1,117 @@ +import { useState, useEffect } from "react"; +import System from "@/models/system"; + +export default function MoonshotAiOptions({ settings }) { + const [inputValue, setInputValue] = useState(settings?.MoonshotAiApiKey); + const [moonshotAiKey, setMoonshotAiKey] = useState( + settings?.MoonshotAiApiKey + ); + + return ( +
+
+ + setInputValue(e.target.value)} + onBlur={() => setMoonshotAiKey(inputValue)} + /> +
+ {!settings?.credentialsOnly && ( + + )} +
+ ); +} + +function MoonshotAiModelSelection({ apiKey, settings }) { + const [models, setModels] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function findCustomModels() { + setLoading(true); + const { models: availableModels } = await System.customModels( + "moonshotai", + typeof apiKey === "boolean" ? null : apiKey + ); + + if (availableModels?.length > 0) { + setModels(availableModels); + } + + setLoading(false); + } + findCustomModels(); + }, [apiKey]); + + if (!apiKey) { + return ( +
+ + +
+ ); + } + + if (loading) { + return ( +
+ + +
+ ); + } + + return ( +
+ + +
+ ); +} diff --git a/frontend/src/media/llmprovider/moonshotai.png b/frontend/src/media/llmprovider/moonshotai.png new file mode 100644 index 0000000000000000000000000000000000000000..71f408054152b2c56d7d1f703d430da6266933ed GIT binary patch literal 52334 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 , + description: "Run Moonshot AI's powerful LLMs.", + requiredConfig: ["MoonshotAiApiKey"], + }, { name: "Generic OpenAI", value: "generic-openai", diff --git a/frontend/src/pages/OnboardingFlow/Steps/DataHandling/index.jsx b/frontend/src/pages/OnboardingFlow/Steps/DataHandling/index.jsx index bc48209d..67be19ce 100644 --- a/frontend/src/pages/OnboardingFlow/Steps/DataHandling/index.jsx +++ b/frontend/src/pages/OnboardingFlow/Steps/DataHandling/index.jsx @@ -38,6 +38,8 @@ import VoyageAiLogo from "@/media/embeddingprovider/voyageai.png"; import PPIOLogo from "@/media/llmprovider/ppio.png"; import PGVectorLogo from "@/media/vectordbs/pgvector.png"; import DPAISLogo from "@/media/llmprovider/dpais.png"; +import MoonshotAiLogo from "@/media/llmprovider/moonshotai.png"; + import React, { useState, useEffect } from "react"; import paths from "@/utils/paths"; import { useNavigate } from "react-router-dom"; @@ -242,6 +244,14 @@ export const LLM_SELECTION_PRIVACY = { ], logo: DPAISLogo, }, + moonshotai: { + name: "Moonshot AI", + description: [ + "Your chats may be used by Moonshot AI for training and model refinement", + "Your prompts and document text used in response creation are visible to Moonshot AI", + ], + logo: MoonshotAiLogo, + }, }; export const VECTOR_DB_PRIVACY = { diff --git a/frontend/src/pages/OnboardingFlow/Steps/LLMPreference/index.jsx b/frontend/src/pages/OnboardingFlow/Steps/LLMPreference/index.jsx index 02d97893..4ce2745d 100644 --- a/frontend/src/pages/OnboardingFlow/Steps/LLMPreference/index.jsx +++ b/frontend/src/pages/OnboardingFlow/Steps/LLMPreference/index.jsx @@ -27,6 +27,7 @@ import NvidiaNimLogo from "@/media/llmprovider/nvidia-nim.png"; import CohereLogo from "@/media/llmprovider/cohere.png"; import PPIOLogo from "@/media/llmprovider/ppio.png"; import DellProAiStudioLogo from "@/media/llmprovider/dpais.png"; +import MoonshotAiLogo from "@/media/llmprovider/moonshotai.png"; import OpenAiOptions from "@/components/LLMSelection/OpenAiOptions"; import GenericOpenAiOptions from "@/components/LLMSelection/GenericOpenAiOptions"; @@ -55,6 +56,7 @@ import XAILLMOptions from "@/components/LLMSelection/XAiLLMOptions"; import NvidiaNimOptions from "@/components/LLMSelection/NvidiaNimOptions"; import PPIOLLMOptions from "@/components/LLMSelection/PPIOLLMOptions"; import DellProAiStudioOptions from "@/components/LLMSelection/DPAISOptions"; +import MoonshotAiOptions from "@/components/LLMSelection/MoonshotAiOptions"; import LLMItem from "@/components/LLMSelection/LLMItem"; import System from "@/models/system"; @@ -263,6 +265,13 @@ const LLMS = [ options: (settings) => , description: "Run xAI's powerful LLMs like Grok-2 and more.", }, + { + name: "Moonshot AI", + value: "moonshotai", + logo: MoonshotAiLogo, + options: (settings) => , + description: "Run Moonshot AI's powerful LLMs.", + }, ]; export default function LLMPreference({ diff --git a/frontend/src/pages/WorkspaceSettings/AgentConfig/AgentLLMSelection/index.jsx b/frontend/src/pages/WorkspaceSettings/AgentConfig/AgentLLMSelection/index.jsx index 6baae1dd..31b7327b 100644 --- a/frontend/src/pages/WorkspaceSettings/AgentConfig/AgentLLMSelection/index.jsx +++ b/frontend/src/pages/WorkspaceSettings/AgentConfig/AgentLLMSelection/index.jsx @@ -31,6 +31,7 @@ const ENABLED_PROVIDERS = [ "xai", "nvidia-nim", "gemini", + "moonshotai", // TODO: More agent support. // "cohere", // Has tool calling and will need to build explicit support // "huggingface" // Can be done but already has issues with no-chat templated. Needs to be tested. diff --git a/server/.env.example b/server/.env.example index 12fa5ec2..65e97481 100644 --- a/server/.env.example +++ b/server/.env.example @@ -131,6 +131,10 @@ SIG_SALT='salt' # Please generate random string at least 32 chars long. # PPIO_API_KEY='your-ppio-api-key-here' # PPIO_MODEL_PREF='deepseek/deepseek-v3/community' +# LLM_PROVIDER='moonshotai' +# MOONSHOT_AI_API_KEY='your-moonshot-api-key-here' +# MOONSHOT_AI_MODEL_PREF='moonshot-v1-32k' + ########################################### ######## Embedding API SElECTION ########## ########################################### diff --git a/server/endpoints/utils.js b/server/endpoints/utils.js index cf418325..425d44f9 100644 --- a/server/endpoints/utils.js +++ b/server/endpoints/utils.js @@ -142,6 +142,9 @@ function getModelTag() { case "gemini": model = process.env.GEMINI_LLM_MODEL_PREF; break; + case "moonshotai": + model = process.env.MOONSHOT_AI_MODEL_PREF; + break; default: model = "--"; break; diff --git a/server/models/systemSettings.js b/server/models/systemSettings.js index 8de54f6c..df2eca13 100644 --- a/server/models/systemSettings.js +++ b/server/models/systemSettings.js @@ -544,6 +544,11 @@ const SystemSettings = { LiteLLMBasePath: process.env.LITE_LLM_BASE_PATH, LiteLLMApiKey: !!process.env.LITE_LLM_API_KEY, + // Moonshot AI Keys + MoonshotAiApiKey: !!process.env.MOONSHOT_AI_API_KEY, + MoonshotAiModelPref: + process.env.MOONSHOT_AI_MODEL_PREF || "moonshot-v1-32k", + // Generic OpenAI Keys GenericOpenAiBasePath: process.env.GENERIC_OPEN_AI_BASE_PATH, GenericOpenAiModelPref: process.env.GENERIC_OPEN_AI_MODEL_PREF, diff --git a/server/utils/AiProviders/modelMap/index.js b/server/utils/AiProviders/modelMap/index.js index 4f5740be..8ff713ee 100644 --- a/server/utils/AiProviders/modelMap/index.js +++ b/server/utils/AiProviders/modelMap/index.js @@ -18,6 +18,7 @@ class ContextWindowFinder { groq: "groq", xai: "xai", deepseek: "deepseek", + moonshot: "moonshot", }; static expiryMs = 1000 * 60 * 60 * 24 * 3; // 3 days static remoteUrl = diff --git a/server/utils/AiProviders/moonshotAi/index.js b/server/utils/AiProviders/moonshotAi/index.js new file mode 100644 index 00000000..c4bc7b65 --- /dev/null +++ b/server/utils/AiProviders/moonshotAi/index.js @@ -0,0 +1,169 @@ +const { NativeEmbedder } = require("../../EmbeddingEngines/native"); +const { + LLMPerformanceMonitor, +} = require("../../helpers/chat/LLMPerformanceMonitor"); +const { + handleDefaultStreamResponseV2, + formatChatHistory, +} = require("../../helpers/chat/responses"); +const { MODEL_MAP } = require("../modelMap"); + +class MoonshotAiLLM { + constructor(embedder = null, modelPreference = null) { + if (!process.env.MOONSHOT_AI_API_KEY) + throw new Error("No Moonshot AI API key was set."); + const { OpenAI: OpenAIApi } = require("openai"); + + this.openai = new OpenAIApi({ + baseURL: "https://api.moonshot.ai/v1", + apiKey: process.env.MOONSHOT_AI_API_KEY, + }); + this.model = + modelPreference || + process.env.MOONSHOT_AI_MODEL_PREF || + "moonshot-v1-32k"; + this.limits = { + history: this.promptWindowLimit() * 0.15, + system: this.promptWindowLimit() * 0.15, + user: this.promptWindowLimit() * 0.7, + }; + + this.embedder = embedder ?? new NativeEmbedder(); + this.defaultTemp = 0.7; + this.log( + `Initialized ${this.model} with context window ${this.promptWindowLimit()}` + ); + } + + log(text, ...args) { + console.log(`\x1b[36m[${this.constructor.name}]\x1b[0m ${text}`, ...args); + } + + #appendContext(contextTexts = []) { + if (!contextTexts || !contextTexts.length) return ""; + return ( + "\nContext:\n" + + contextTexts + .map((text, i) => { + return `[CONTEXT ${i}]:\n${text}\n[END CONTEXT ${i}]\n\n`; + }) + .join("") + ); + } + + /** + * Generates appropriate content array for a message + attachments. + * @param {{userPrompt:string, attachments: import("../../helpers").Attachment[]}} + * @returns {string|object[]} + */ + #generateContent({ userPrompt, attachments = [] }) { + if (!attachments.length) { + return userPrompt; + } + + const content = [{ type: "text", text: userPrompt }]; + for (let attachment of attachments) { + content.push({ + type: "image_url", + image_url: { + url: attachment.contentString, + }, + }); + } + return content.flat(); + } + + streamingEnabled() { + return true; + } + + promptWindowLimit() { + return MODEL_MAP.get("moonshot", this.model) ?? 8_192; + } + + constructPrompt({ + systemPrompt = "", + contextTexts = [], + chatHistory = [], + userPrompt = "", + attachments = [], + }) { + const prompt = { + role: "system", + content: `${systemPrompt}${this.#appendContext(contextTexts)}`, + }; + return [ + prompt, + ...formatChatHistory(chatHistory, this.#generateContent), + { + role: "user", + content: this.#generateContent({ userPrompt, attachments }), + }, + ]; + } + + async compressMessages(promptArgs = {}, rawHistory = []) { + const { messageArrayCompressor } = require("../../helpers/chat"); + const messageArray = this.constructPrompt(promptArgs); + return await messageArrayCompressor(this, messageArray, rawHistory); + } + + async getChatCompletion(messages = null, { temperature = 0.7 }) { + const result = await LLMPerformanceMonitor.measureAsyncFunction( + this.openai.chat.completions + .create({ + model: this.model, + messages, + temperature, + }) + .catch((e) => { + throw new Error(e.message); + }) + ); + + if ( + !Object.prototype.hasOwnProperty.call(result.output, "choices") || + result.output.choices.length === 0 + ) + return null; + + return { + textResponse: result.output.choices[0].message.content, + metrics: { + prompt_tokens: result.output.usage.prompt_tokens || 0, + completion_tokens: result.output.usage.completion_tokens || 0, + total_tokens: result.output.usage.total_tokens || 0, + outputTps: result.output.usage.completion_tokens / result.duration, + duration: result.duration, + }, + }; + } + + async streamGetChatCompletion(messages = null, { temperature = 0.7 }) { + const measuredStreamRequest = await LLMPerformanceMonitor.measureStream( + this.openai.chat.completions.create({ + model: this.model, + stream: true, + messages, + temperature, + }), + messages + ); + + return measuredStreamRequest; + } + + handleStream(response, stream, responseProps) { + return handleDefaultStreamResponseV2(response, stream, responseProps); + } + + // Simple wrapper for dynamic embedder & normalize interface for all LLM implementations + async embedTextInput(textInput) { + return await this.embedder.embedTextInput(textInput); + } + async embedChunks(textChunks = []) { + return await this.embedder.embedChunks(textChunks); + } +} + +module.exports = { MoonshotAiLLM }; diff --git a/server/utils/agents/aibitat/index.js b/server/utils/agents/aibitat/index.js index 6e069def..d6b22d3a 100644 --- a/server/utils/agents/aibitat/index.js +++ b/server/utils/agents/aibitat/index.js @@ -812,6 +812,8 @@ ${this.getHistory({ to: route.to }) return new Providers.FireworksAIProvider({ model: config.model }); case "nvidia-nim": return new Providers.NvidiaNimProvider({ model: config.model }); + case "moonshotai": + return new Providers.MoonshotAiProvider({ model: config.model }); case "deepseek": return new Providers.DeepSeekProvider({ model: config.model }); case "litellm": diff --git a/server/utils/agents/aibitat/providers/ai-provider.js b/server/utils/agents/aibitat/providers/ai-provider.js index 5085fdcf..07867e4c 100644 --- a/server/utils/agents/aibitat/providers/ai-provider.js +++ b/server/utils/agents/aibitat/providers/ai-provider.js @@ -184,7 +184,14 @@ class Provider { apiKey: process.env.GEMINI_API_KEY ?? null, ...config, }); - + case "moonshotai": + return new ChatOpenAI({ + configuration: { + baseURL: "https://api.moonshot.ai/v1", + }, + apiKey: process.env.MOONSHOT_AI_API_KEY ?? null, + ...config, + }); // OSS Model Runners // case "anythingllm_ollama": // return new ChatOllama({ diff --git a/server/utils/agents/aibitat/providers/index.js b/server/utils/agents/aibitat/providers/index.js index d8c17486..859ad9de 100644 --- a/server/utils/agents/aibitat/providers/index.js +++ b/server/utils/agents/aibitat/providers/index.js @@ -23,6 +23,7 @@ const NvidiaNimProvider = require("./nvidiaNim.js"); const PPIOProvider = require("./ppio.js"); const GeminiProvider = require("./gemini.js"); const DellProAiStudioProvider = require("./dellProAiStudio.js"); +const MoonshotAiProvider = require("./moonshotAi.js"); module.exports = { OpenAIProvider, @@ -50,4 +51,5 @@ module.exports = { PPIOProvider, GeminiProvider, DellProAiStudioProvider, + MoonshotAiProvider, }; diff --git a/server/utils/agents/aibitat/providers/moonshotAi.js b/server/utils/agents/aibitat/providers/moonshotAi.js new file mode 100644 index 00000000..b6bb3beb --- /dev/null +++ b/server/utils/agents/aibitat/providers/moonshotAi.js @@ -0,0 +1,103 @@ +const OpenAI = require("openai"); +const Provider = require("./ai-provider.js"); +const InheritMultiple = require("./helpers/classes.js"); +const UnTooled = require("./helpers/untooled.js"); + +class MoonshotAiProvider extends InheritMultiple([Provider, UnTooled]) { + model; + + constructor(config = {}) { + const { model = "moonshot-v1-32k" } = config; + super(); + const client = new OpenAI({ + baseURL: "https://api.moonshot.ai/v1", + apiKey: process.env.MOONSHOT_AI_API_KEY, + maxRetries: 3, + }); + + this._client = client; + this.model = model; + this.verbose = true; + } + + /** + * Create a completion based on the received messages. + * + * @param messages A list of messages to send to the API. + * @param functions + * @returns The completion. + */ + get client() { + return this._client; + } + + async #handleFunctionCallChat({ messages = [] }) { + return await this.client.chat.completions + .create({ + model: this.model, + temperature: 0, + messages, + }) + .then((result) => { + if (!result.hasOwnProperty("choices")) + throw new Error("Moonshot chat: No results!"); + if (result.choices.length === 0) + throw new Error("Moonshot chat: No results length!"); + return result.choices[0].message.content; + }) + .catch((_) => { + return null; + }); + } + + async complete(messages, functions = []) { + try { + let completion; + if (functions.length > 0) { + const { toolCall, text } = await this.functionCall( + messages, + functions, + this.#handleFunctionCallChat.bind(this) + ); + + if (toolCall !== null) { + this.providerLog(`Valid tool call found - running ${toolCall.name}.`); + this.deduplicator.trackRun(toolCall.name, toolCall.arguments); + return { + result: null, + functionCall: { + name: toolCall.name, + arguments: toolCall.arguments, + }, + cost: 0, + }; + } + completion = { content: text }; + } + + if (!completion?.content) { + this.providerLog( + "Will assume chat completion without tool call inputs." + ); + const response = await this.client.chat.completions.create({ + model: this.model, + messages: this.cleanMsgs(messages), + }); + completion = response.choices[0].message; + } + + // The UnTooled class inherited Deduplicator is mostly useful to prevent the agent + // from calling the exact same function over and over in a loop within a single chat exchange + // _but_ we should enable it to call previously used tools in a new chat interaction. + this.deduplicator.reset("runs"); + return { + result: completion.content, + cost: 0, + }; + } catch (error) { + throw error; + } + } +} + +module.exports = MoonshotAiProvider; diff --git a/server/utils/agents/index.js b/server/utils/agents/index.js index 915e5a59..4527ee78 100644 --- a/server/utils/agents/index.js +++ b/server/utils/agents/index.js @@ -199,6 +199,10 @@ class AgentHandler { "Dell Pro AI Studio model must be set to use agents." ); break; + case "moonshotai": + if (!process.env.MOONSHOT_AI_MODEL_PREF) + throw new Error("Moonshot AI model must be set to use agents."); + break; default: throw new Error( @@ -254,6 +258,8 @@ class AgentHandler { return process.env.DEEPSEEK_MODEL_PREF ?? "deepseek-chat"; case "litellm": return process.env.LITE_LLM_MODEL_PREF ?? null; + case "moonshotai": + return process.env.MOONSHOT_AI_MODEL_PREF ?? "moonshot-v1-32k"; case "apipie": return process.env.APIPIE_LLM_MODEL_PREF ?? null; case "xai": diff --git a/server/utils/helpers/customModels.js b/server/utils/helpers/customModels.js index cff97ff8..de855f0b 100644 --- a/server/utils/helpers/customModels.js +++ b/server/utils/helpers/customModels.js @@ -33,6 +33,7 @@ const SUPPORT_CUSTOM_MODELS = [ "gemini", "ppio", "dpais", + "moonshotai", ]; async function getCustomModels(provider = "", apiKey = null, basePath = null) { @@ -84,6 +85,8 @@ async function getCustomModels(provider = "", apiKey = null, basePath = null) { return await getPPIOModels(apiKey); case "dpais": return await getDellProAiStudioModels(basePath); + case "moonshotai": + return await getMoonshotAiModels(apiKey); default: return { models: [], error: "Invalid provider for custom models" }; } @@ -675,6 +678,31 @@ async function getDellProAiStudioModels(basePath = null) { } } +async function getMoonshotAiModels(_apiKey = null) { + const apiKey = + _apiKey === true + ? process.env.MOONSHOT_AI_API_KEY + : _apiKey || process.env.MOONSHOT_AI_API_KEY || null; + + const { OpenAI: OpenAIApi } = require("openai"); + const openai = new OpenAIApi({ + baseURL: "https://api.moonshot.ai/v1", + apiKey, + }); + const models = await openai.models + .list() + .then((results) => results.data) + .catch((e) => { + console.error(`MoonshotAi:listModels`, e.message); + return []; + }); + + // Api Key was successful so lets save it for future uses + if (models.length > 0) process.env.MOONSHOT_AI_API_KEY = apiKey; + return { models, error: null }; +} + module.exports = { getCustomModels, + SUPPORT_CUSTOM_MODELS, }; diff --git a/server/utils/helpers/index.js b/server/utils/helpers/index.js index 2017c618..9e101a2b 100644 --- a/server/utils/helpers/index.js +++ b/server/utils/helpers/index.js @@ -203,6 +203,9 @@ function getLLMProvider({ provider = null, model = null } = {}) { case "ppio": const { PPIOLLM } = require("../AiProviders/ppio"); return new PPIOLLM(embedder, model); + case "moonshotai": + const { MoonshotAiLLM } = require("../AiProviders/moonshotAi"); + return new MoonshotAiLLM(embedder, model); case "dpais": const { DellProAiStudioLLM } = require("../AiProviders/dellProAiStudio"); return new DellProAiStudioLLM(embedder, model); @@ -353,6 +356,9 @@ function getLLMProviderClass({ provider = null } = {}) { case "dpais": const { DellProAiStudioLLM } = require("../AiProviders/dellProAiStudio"); return DellProAiStudioLLM; + case "moonshotai": + const { MoonshotAiLLM } = require("../AiProviders/moonshotAi"); + return MoonshotAiLLM; default: return null; } @@ -419,6 +425,8 @@ function getBaseLLMProviderModel({ provider = null } = {}) { return process.env.PPIO_API_KEY; case "dpais": return process.env.DPAIS_LLM_MODEL_PREF; + case "moonshotai": + return process.env.MOONSHOT_AI_MODEL_PREF; default: return null; } diff --git a/server/utils/helpers/updateENV.js b/server/utils/helpers/updateENV.js index 87670830..ce28e73a 100644 --- a/server/utils/helpers/updateENV.js +++ b/server/utils/helpers/updateENV.js @@ -676,6 +676,16 @@ const KEY_MAPPING = { envKey: "PPIO_MODEL_PREF", checks: [isNotEmpty], }, + + // Moonshot AI Options + MoonshotAiApiKey: { + envKey: "MOONSHOT_AI_API_KEY", + checks: [isNotEmpty], + }, + MoonshotAiModelPref: { + envKey: "MOONSHOT_AI_MODEL_PREF", + checks: [isNotEmpty], + }, }; function isNotEmpty(input = "") { @@ -784,6 +794,7 @@ function supportedLLM(input = "") { "nvidia-nim", "ppio", "dpais", + "moonshotai", ].includes(input); return validSelection ? null : `${input} is not a valid LLM provider.`; }