mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-05 08:10:14 +00:00
feat: add moonshot provider (#6046)
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> Co-authored-by: CellenLee <99465814+CellenLee@users.noreply.github.com>
This commit is contained in:
parent
b1bc085aa6
commit
df6c57d293
32 changed files with 522 additions and 0 deletions
|
|
@ -159,6 +159,7 @@ export const SECRET_STATE_KEYS = [
|
|||
"geminiApiKey",
|
||||
"openAiNativeApiKey",
|
||||
"deepSeekApiKey",
|
||||
"moonshotApiKey",
|
||||
"mistralApiKey",
|
||||
"unboundApiKey",
|
||||
"requestyApiKey",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ export const providerNames = [
|
|||
"gemini-cli",
|
||||
"openai-native",
|
||||
"mistral",
|
||||
"moonshot",
|
||||
"deepseek",
|
||||
"unbound",
|
||||
"requesty",
|
||||
|
|
@ -187,6 +188,13 @@ const deepSeekSchema = apiModelIdProviderModelSchema.extend({
|
|||
deepSeekApiKey: z.string().optional(),
|
||||
})
|
||||
|
||||
const moonshotSchema = apiModelIdProviderModelSchema.extend({
|
||||
moonshotBaseUrl: z
|
||||
.union([z.literal("https://api.moonshot.ai/v1"), z.literal("https://api.moonshot.cn/v1")])
|
||||
.optional(),
|
||||
moonshotApiKey: z.string().optional(),
|
||||
})
|
||||
|
||||
const unboundSchema = baseProviderSettingsSchema.extend({
|
||||
unboundApiKey: z.string().optional(),
|
||||
unboundModelId: z.string().optional(),
|
||||
|
|
@ -241,6 +249,7 @@ export const providerSettingsSchemaDiscriminated = z.discriminatedUnion("apiProv
|
|||
openAiNativeSchema.merge(z.object({ apiProvider: z.literal("openai-native") })),
|
||||
mistralSchema.merge(z.object({ apiProvider: z.literal("mistral") })),
|
||||
deepSeekSchema.merge(z.object({ apiProvider: z.literal("deepseek") })),
|
||||
moonshotSchema.merge(z.object({ apiProvider: z.literal("moonshot") })),
|
||||
unboundSchema.merge(z.object({ apiProvider: z.literal("unbound") })),
|
||||
requestySchema.merge(z.object({ apiProvider: z.literal("requesty") })),
|
||||
humanRelaySchema.merge(z.object({ apiProvider: z.literal("human-relay") })),
|
||||
|
|
@ -269,6 +278,7 @@ export const providerSettingsSchema = z.object({
|
|||
...openAiNativeSchema.shape,
|
||||
...mistralSchema.shape,
|
||||
...deepSeekSchema.shape,
|
||||
...moonshotSchema.shape,
|
||||
...unboundSchema.shape,
|
||||
...requestySchema.shape,
|
||||
...humanRelaySchema.shape,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ export * from "./groq.js"
|
|||
export * from "./lite-llm.js"
|
||||
export * from "./lm-studio.js"
|
||||
export * from "./mistral.js"
|
||||
export * from "./moonshot.js"
|
||||
export * from "./ollama.js"
|
||||
export * from "./openai.js"
|
||||
export * from "./openrouter.js"
|
||||
|
|
|
|||
22
packages/types/src/providers/moonshot.ts
Normal file
22
packages/types/src/providers/moonshot.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import type { ModelInfo } from "../model.js"
|
||||
|
||||
// https://platform.moonshot.ai/
|
||||
export type MoonshotModelId = keyof typeof moonshotModels
|
||||
|
||||
export const moonshotDefaultModelId: MoonshotModelId = "kimi-k2-0711-preview"
|
||||
|
||||
export const moonshotModels = {
|
||||
"kimi-k2-0711-preview": {
|
||||
maxTokens: 32_000,
|
||||
contextWindow: 131_072,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: true,
|
||||
inputPrice: 0.6, // $0.60 per million tokens (cache miss)
|
||||
outputPrice: 2.5, // $2.50 per million tokens
|
||||
cacheWritesPrice: 0, // $0 per million tokens (cache miss)
|
||||
cacheReadsPrice: 0.15, // $0.15 per million tokens (cache hit)
|
||||
description: `Kimi K2 is a state-of-the-art mixture-of-experts (MoE) language model with 32 billion activated parameters and 1 trillion total parameters.`,
|
||||
},
|
||||
} as const satisfies Record<string, ModelInfo>
|
||||
|
||||
export const MOONSHOT_DEFAULT_TEMPERATURE = 0.6
|
||||
|
|
@ -17,6 +17,7 @@ import {
|
|||
GeminiHandler,
|
||||
OpenAiNativeHandler,
|
||||
DeepSeekHandler,
|
||||
MoonshotHandler,
|
||||
MistralHandler,
|
||||
VsCodeLmHandler,
|
||||
UnboundHandler,
|
||||
|
|
@ -89,6 +90,8 @@ export function buildApiHandler(configuration: ProviderSettings): ApiHandler {
|
|||
return new OpenAiNativeHandler(options)
|
||||
case "deepseek":
|
||||
return new DeepSeekHandler(options)
|
||||
case "moonshot":
|
||||
return new MoonshotHandler(options)
|
||||
case "vscode-lm":
|
||||
return new VsCodeLmHandler(options)
|
||||
case "mistral":
|
||||
|
|
@ -110,6 +113,7 @@ export function buildApiHandler(configuration: ProviderSettings): ApiHandler {
|
|||
case "litellm":
|
||||
return new LiteLLMHandler(options)
|
||||
default:
|
||||
apiProvider satisfies "gemini-cli" | undefined
|
||||
return new AnthropicHandler(options)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
297
src/api/providers/__tests__/moonshot.spec.ts
Normal file
297
src/api/providers/__tests__/moonshot.spec.ts
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
// Mocks must come first, before imports
|
||||
const mockCreate = vi.fn()
|
||||
vi.mock("openai", () => {
|
||||
return {
|
||||
__esModule: true,
|
||||
default: vi.fn().mockImplementation(() => ({
|
||||
chat: {
|
||||
completions: {
|
||||
create: mockCreate.mockImplementation(async (options) => {
|
||||
if (!options.stream) {
|
||||
return {
|
||||
id: "test-completion",
|
||||
choices: [
|
||||
{
|
||||
message: { role: "assistant", content: "Test response", refusal: null },
|
||||
finish_reason: "stop",
|
||||
index: 0,
|
||||
},
|
||||
],
|
||||
usage: {
|
||||
prompt_tokens: 10,
|
||||
completion_tokens: 5,
|
||||
total_tokens: 15,
|
||||
cached_tokens: 2,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Return async iterator for streaming
|
||||
return {
|
||||
[Symbol.asyncIterator]: async function* () {
|
||||
yield {
|
||||
choices: [
|
||||
{
|
||||
delta: { content: "Test response" },
|
||||
index: 0,
|
||||
},
|
||||
],
|
||||
usage: null,
|
||||
}
|
||||
yield {
|
||||
choices: [
|
||||
{
|
||||
delta: {},
|
||||
index: 0,
|
||||
},
|
||||
],
|
||||
usage: {
|
||||
prompt_tokens: 10,
|
||||
completion_tokens: 5,
|
||||
total_tokens: 15,
|
||||
cached_tokens: 2,
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}),
|
||||
},
|
||||
},
|
||||
})),
|
||||
}
|
||||
})
|
||||
|
||||
import OpenAI from "openai"
|
||||
import type { Anthropic } from "@anthropic-ai/sdk"
|
||||
|
||||
import { moonshotDefaultModelId } from "@roo-code/types"
|
||||
|
||||
import type { ApiHandlerOptions } from "../../../shared/api"
|
||||
|
||||
import { MoonshotHandler } from "../moonshot"
|
||||
|
||||
describe("MoonshotHandler", () => {
|
||||
let handler: MoonshotHandler
|
||||
let mockOptions: ApiHandlerOptions
|
||||
|
||||
beforeEach(() => {
|
||||
mockOptions = {
|
||||
moonshotApiKey: "test-api-key",
|
||||
apiModelId: "moonshot-chat",
|
||||
moonshotBaseUrl: "https://api.moonshot.ai/v1",
|
||||
}
|
||||
handler = new MoonshotHandler(mockOptions)
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe("constructor", () => {
|
||||
it("should initialize with provided options", () => {
|
||||
expect(handler).toBeInstanceOf(MoonshotHandler)
|
||||
expect(handler.getModel().id).toBe(mockOptions.apiModelId)
|
||||
})
|
||||
|
||||
it.skip("should throw error if API key is missing", () => {
|
||||
expect(() => {
|
||||
new MoonshotHandler({
|
||||
...mockOptions,
|
||||
moonshotApiKey: undefined,
|
||||
})
|
||||
}).toThrow("Moonshot API key is required")
|
||||
})
|
||||
|
||||
it("should use default model ID if not provided", () => {
|
||||
const handlerWithoutModel = new MoonshotHandler({
|
||||
...mockOptions,
|
||||
apiModelId: undefined,
|
||||
})
|
||||
expect(handlerWithoutModel.getModel().id).toBe(moonshotDefaultModelId)
|
||||
})
|
||||
|
||||
it("should use default base URL if not provided", () => {
|
||||
const handlerWithoutBaseUrl = new MoonshotHandler({
|
||||
...mockOptions,
|
||||
moonshotBaseUrl: undefined,
|
||||
})
|
||||
expect(handlerWithoutBaseUrl).toBeInstanceOf(MoonshotHandler)
|
||||
// The base URL is passed to OpenAI client internally
|
||||
expect(OpenAI).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
baseURL: "https://api.moonshot.ai/v1",
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it("should use chinese base URL if provided", () => {
|
||||
const customBaseUrl = "https://api.moonshot.cn/v1"
|
||||
const handlerWithCustomUrl = new MoonshotHandler({
|
||||
...mockOptions,
|
||||
moonshotBaseUrl: customBaseUrl,
|
||||
})
|
||||
expect(handlerWithCustomUrl).toBeInstanceOf(MoonshotHandler)
|
||||
// The custom base URL is passed to OpenAI client
|
||||
expect(OpenAI).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
baseURL: customBaseUrl,
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it("should set includeMaxTokens to true", () => {
|
||||
// Create a new handler and verify OpenAI client was called with includeMaxTokens
|
||||
const _handler = new MoonshotHandler(mockOptions)
|
||||
expect(OpenAI).toHaveBeenCalledWith(expect.objectContaining({ apiKey: mockOptions.moonshotApiKey }))
|
||||
})
|
||||
})
|
||||
|
||||
describe("getModel", () => {
|
||||
it("should return model info for valid model ID", () => {
|
||||
const model = handler.getModel()
|
||||
expect(model.id).toBe(mockOptions.apiModelId)
|
||||
expect(model.info).toBeDefined()
|
||||
expect(model.info.maxTokens).toBe(32_000)
|
||||
expect(model.info.contextWindow).toBe(131_072)
|
||||
expect(model.info.supportsImages).toBe(false)
|
||||
expect(model.info.supportsPromptCache).toBe(true) // Should be true now
|
||||
})
|
||||
|
||||
it("should return provided model ID with default model info if model does not exist", () => {
|
||||
const handlerWithInvalidModel = new MoonshotHandler({
|
||||
...mockOptions,
|
||||
apiModelId: "invalid-model",
|
||||
})
|
||||
const model = handlerWithInvalidModel.getModel()
|
||||
expect(model.id).toBe("invalid-model") // Returns provided ID
|
||||
expect(model.info).toBeDefined()
|
||||
// With the current implementation, it's the same object reference when using default model info
|
||||
expect(model.info).toBe(handler.getModel().info)
|
||||
// Should have the same base properties
|
||||
expect(model.info.contextWindow).toBe(handler.getModel().info.contextWindow)
|
||||
// And should have supportsPromptCache set to true
|
||||
expect(model.info.supportsPromptCache).toBe(true)
|
||||
})
|
||||
|
||||
it("should return default model if no model ID is provided", () => {
|
||||
const handlerWithoutModel = new MoonshotHandler({
|
||||
...mockOptions,
|
||||
apiModelId: undefined,
|
||||
})
|
||||
const model = handlerWithoutModel.getModel()
|
||||
expect(model.id).toBe(moonshotDefaultModelId)
|
||||
expect(model.info).toBeDefined()
|
||||
expect(model.info.supportsPromptCache).toBe(true)
|
||||
})
|
||||
|
||||
it("should include model parameters from getModelParams", () => {
|
||||
const model = handler.getModel()
|
||||
expect(model).toHaveProperty("temperature")
|
||||
expect(model).toHaveProperty("maxTokens")
|
||||
})
|
||||
})
|
||||
|
||||
describe("createMessage", () => {
|
||||
const systemPrompt = "You are a helpful assistant."
|
||||
const messages: Anthropic.Messages.MessageParam[] = [
|
||||
{
|
||||
role: "user",
|
||||
content: [
|
||||
{
|
||||
type: "text" as const,
|
||||
text: "Hello!",
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
it("should handle streaming responses", async () => {
|
||||
const stream = handler.createMessage(systemPrompt, messages)
|
||||
const chunks: any[] = []
|
||||
for await (const chunk of stream) {
|
||||
chunks.push(chunk)
|
||||
}
|
||||
|
||||
expect(chunks.length).toBeGreaterThan(0)
|
||||
const textChunks = chunks.filter((chunk) => chunk.type === "text")
|
||||
expect(textChunks).toHaveLength(1)
|
||||
expect(textChunks[0].text).toBe("Test response")
|
||||
})
|
||||
|
||||
it("should include usage information", async () => {
|
||||
const stream = handler.createMessage(systemPrompt, messages)
|
||||
const chunks: any[] = []
|
||||
for await (const chunk of stream) {
|
||||
chunks.push(chunk)
|
||||
}
|
||||
|
||||
const usageChunks = chunks.filter((chunk) => chunk.type === "usage")
|
||||
expect(usageChunks.length).toBeGreaterThan(0)
|
||||
expect(usageChunks[0].inputTokens).toBe(10)
|
||||
expect(usageChunks[0].outputTokens).toBe(5)
|
||||
})
|
||||
|
||||
it("should include cache metrics in usage information", async () => {
|
||||
const stream = handler.createMessage(systemPrompt, messages)
|
||||
const chunks: any[] = []
|
||||
for await (const chunk of stream) {
|
||||
chunks.push(chunk)
|
||||
}
|
||||
|
||||
const usageChunks = chunks.filter((chunk) => chunk.type === "usage")
|
||||
expect(usageChunks.length).toBeGreaterThan(0)
|
||||
expect(usageChunks[0].cacheWriteTokens).toBe(0)
|
||||
expect(usageChunks[0].cacheReadTokens).toBe(2)
|
||||
})
|
||||
})
|
||||
|
||||
describe("processUsageMetrics", () => {
|
||||
it("should correctly process usage metrics including cache information", () => {
|
||||
// We need to access the protected method, so we'll create a test subclass
|
||||
class TestMoonshotHandler extends MoonshotHandler {
|
||||
public testProcessUsageMetrics(usage: any) {
|
||||
return this.processUsageMetrics(usage)
|
||||
}
|
||||
}
|
||||
|
||||
const testHandler = new TestMoonshotHandler(mockOptions)
|
||||
|
||||
const usage = {
|
||||
prompt_tokens: 100,
|
||||
completion_tokens: 50,
|
||||
total_tokens: 150,
|
||||
cached_tokens: 20,
|
||||
}
|
||||
|
||||
const result = testHandler.testProcessUsageMetrics(usage)
|
||||
|
||||
expect(result.type).toBe("usage")
|
||||
expect(result.inputTokens).toBe(100)
|
||||
expect(result.outputTokens).toBe(50)
|
||||
expect(result.cacheWriteTokens).toBe(0)
|
||||
expect(result.cacheReadTokens).toBe(20)
|
||||
})
|
||||
|
||||
it("should handle missing cache metrics gracefully", () => {
|
||||
class TestMoonshotHandler extends MoonshotHandler {
|
||||
public testProcessUsageMetrics(usage: any) {
|
||||
return this.processUsageMetrics(usage)
|
||||
}
|
||||
}
|
||||
|
||||
const testHandler = new TestMoonshotHandler(mockOptions)
|
||||
|
||||
const usage = {
|
||||
prompt_tokens: 100,
|
||||
completion_tokens: 50,
|
||||
total_tokens: 150,
|
||||
// No cached_tokens
|
||||
}
|
||||
|
||||
const result = testHandler.testProcessUsageMetrics(usage)
|
||||
|
||||
expect(result.type).toBe("usage")
|
||||
expect(result.inputTokens).toBe(100)
|
||||
expect(result.outputTokens).toBe(50)
|
||||
expect(result.cacheWriteTokens).toBe(0)
|
||||
expect(result.cacheReadTokens).toBeUndefined()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -4,6 +4,7 @@ export { AwsBedrockHandler } from "./bedrock"
|
|||
export { ChutesHandler } from "./chutes"
|
||||
export { ClaudeCodeHandler } from "./claude-code"
|
||||
export { DeepSeekHandler } from "./deepseek"
|
||||
export { MoonshotHandler } from "./moonshot"
|
||||
export { FakeAIHandler } from "./fake-ai"
|
||||
export { GeminiHandler } from "./gemini"
|
||||
export { GlamaHandler } from "./glama"
|
||||
|
|
|
|||
39
src/api/providers/moonshot.ts
Normal file
39
src/api/providers/moonshot.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import { moonshotModels, moonshotDefaultModelId } from "@roo-code/types"
|
||||
|
||||
import type { ApiHandlerOptions } from "../../shared/api"
|
||||
|
||||
import type { ApiStreamUsageChunk } from "../transform/stream"
|
||||
import { getModelParams } from "../transform/model-params"
|
||||
|
||||
import { OpenAiHandler } from "./openai"
|
||||
|
||||
export class MoonshotHandler extends OpenAiHandler {
|
||||
constructor(options: ApiHandlerOptions) {
|
||||
super({
|
||||
...options,
|
||||
openAiApiKey: options.moonshotApiKey ?? "not-provided",
|
||||
openAiModelId: options.apiModelId ?? moonshotDefaultModelId,
|
||||
openAiBaseUrl: options.moonshotBaseUrl ?? "https://api.moonshot.ai/v1",
|
||||
openAiStreamingEnabled: true,
|
||||
includeMaxTokens: true,
|
||||
})
|
||||
}
|
||||
|
||||
override getModel() {
|
||||
const id = this.options.apiModelId ?? moonshotDefaultModelId
|
||||
const info = moonshotModels[id as keyof typeof moonshotModels] || moonshotModels[moonshotDefaultModelId]
|
||||
const params = getModelParams({ format: "openai", modelId: id, model: info, settings: this.options })
|
||||
return { id, info, ...params }
|
||||
}
|
||||
|
||||
// Override to handle Moonshot's usage metrics, including caching.
|
||||
protected override processUsageMetrics(usage: any): ApiStreamUsageChunk {
|
||||
return {
|
||||
type: "usage",
|
||||
inputTokens: usage?.prompt_tokens || 0,
|
||||
outputTokens: usage?.completion_tokens || 0,
|
||||
cacheWriteTokens: 0,
|
||||
cacheReadTokens: usage?.cached_tokens,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -53,6 +53,7 @@ describe("checkExistKey", () => {
|
|||
geminiApiKey: undefined,
|
||||
openAiNativeApiKey: undefined,
|
||||
deepSeekApiKey: undefined,
|
||||
moonshotApiKey: undefined,
|
||||
mistralApiKey: undefined,
|
||||
vsCodeLmModelSelector: undefined,
|
||||
requestyApiKey: undefined,
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import {
|
|||
claudeCodeDefaultModelId,
|
||||
geminiDefaultModelId,
|
||||
deepSeekDefaultModelId,
|
||||
moonshotDefaultModelId,
|
||||
mistralDefaultModelId,
|
||||
xaiDefaultModelId,
|
||||
groqDefaultModelId,
|
||||
|
|
@ -61,6 +62,7 @@ import {
|
|||
LMStudio,
|
||||
LiteLLM,
|
||||
Mistral,
|
||||
Moonshot,
|
||||
Ollama,
|
||||
OpenAI,
|
||||
OpenAICompatible,
|
||||
|
|
@ -287,6 +289,7 @@ const ApiOptions = ({
|
|||
"openai-native": { field: "apiModelId", default: openAiNativeDefaultModelId },
|
||||
gemini: { field: "apiModelId", default: geminiDefaultModelId },
|
||||
deepseek: { field: "apiModelId", default: deepSeekDefaultModelId },
|
||||
moonshot: { field: "apiModelId", default: moonshotDefaultModelId },
|
||||
mistral: { field: "apiModelId", default: mistralDefaultModelId },
|
||||
xai: { field: "apiModelId", default: xaiDefaultModelId },
|
||||
groq: { field: "apiModelId", default: groqDefaultModelId },
|
||||
|
|
@ -464,6 +467,10 @@ const ApiOptions = ({
|
|||
<DeepSeek apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} />
|
||||
)}
|
||||
|
||||
{selectedProvider === "moonshot" && (
|
||||
<Moonshot apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} />
|
||||
)}
|
||||
|
||||
{selectedProvider === "vscode-lm" && (
|
||||
<VSCodeLM apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} />
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import {
|
|||
bedrockModels,
|
||||
claudeCodeModels,
|
||||
deepSeekModels,
|
||||
moonshotModels,
|
||||
geminiModels,
|
||||
mistralModels,
|
||||
openAiNativeModels,
|
||||
|
|
@ -19,6 +20,7 @@ export const MODELS_BY_PROVIDER: Partial<Record<ProviderName, Record<string, Mod
|
|||
"claude-code": claudeCodeModels,
|
||||
bedrock: bedrockModels,
|
||||
deepseek: deepSeekModels,
|
||||
moonshot: moonshotModels,
|
||||
gemini: geminiModels,
|
||||
mistral: mistralModels,
|
||||
"openai-native": openAiNativeModels,
|
||||
|
|
@ -34,6 +36,7 @@ export const PROVIDERS = [
|
|||
{ value: "claude-code", label: "Claude Code" },
|
||||
{ value: "gemini", label: "Google Gemini" },
|
||||
{ value: "deepseek", label: "DeepSeek" },
|
||||
{ value: "moonshot", label: "Moonshot" },
|
||||
{ value: "openai-native", label: "OpenAI" },
|
||||
{ value: "openai", label: "OpenAI Compatible" },
|
||||
{ value: "vertex", label: "GCP Vertex AI" },
|
||||
|
|
|
|||
73
webview-ui/src/components/settings/providers/Moonshot.tsx
Normal file
73
webview-ui/src/components/settings/providers/Moonshot.tsx
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import { useCallback } from "react"
|
||||
import { VSCodeTextField, VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react"
|
||||
|
||||
import type { ProviderSettings } from "@roo-code/types"
|
||||
|
||||
import { useAppTranslation } from "@src/i18n/TranslationContext"
|
||||
import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
|
||||
|
||||
import { inputEventTransform } from "../transforms"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
type MoonshotProps = {
|
||||
apiConfiguration: ProviderSettings
|
||||
setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
|
||||
}
|
||||
|
||||
export const Moonshot = ({ apiConfiguration, setApiConfigurationField }: MoonshotProps) => {
|
||||
const { t } = useAppTranslation()
|
||||
|
||||
const handleInputChange = useCallback(
|
||||
<K extends keyof ProviderSettings, E>(
|
||||
field: K,
|
||||
transform: (event: E) => ProviderSettings[K] = inputEventTransform,
|
||||
) =>
|
||||
(event: E | Event) => {
|
||||
setApiConfigurationField(field, transform(event as E))
|
||||
},
|
||||
[setApiConfigurationField],
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<label className="block font-medium mb-1">{t("settings:providers.moonshotBaseUrl")}</label>
|
||||
<VSCodeDropdown
|
||||
value={apiConfiguration.moonshotBaseUrl}
|
||||
onChange={handleInputChange("moonshotBaseUrl")}
|
||||
className={cn("w-full")}>
|
||||
<VSCodeOption value="https://api.moonshot.ai/v1" className="p-2">
|
||||
api.moonshot.ai
|
||||
</VSCodeOption>
|
||||
<VSCodeOption value="https://api.moonshot.cn/v1" className="p-2">
|
||||
api.moonshot.cn
|
||||
</VSCodeOption>
|
||||
</VSCodeDropdown>
|
||||
</div>
|
||||
<div>
|
||||
<VSCodeTextField
|
||||
value={apiConfiguration?.moonshotApiKey || ""}
|
||||
type="password"
|
||||
onInput={handleInputChange("moonshotApiKey")}
|
||||
placeholder={t("settings:placeholders.apiKey")}
|
||||
className="w-full">
|
||||
<label className="block font-medium mb-1">{t("settings:providers.moonshotApiKey")}</label>
|
||||
</VSCodeTextField>
|
||||
<div className="text-sm text-vscode-descriptionForeground">
|
||||
{t("settings:providers.apiKeyStorageNotice")}
|
||||
</div>
|
||||
{!apiConfiguration?.moonshotApiKey && (
|
||||
<VSCodeButtonLink
|
||||
href={
|
||||
apiConfiguration.moonshotBaseUrl === "https://api.moonshot.cn/v1"
|
||||
? "https://platform.moonshot.cn/console/api-keys"
|
||||
: "https://platform.moonshot.ai/console/api-keys"
|
||||
}
|
||||
appearance="secondary">
|
||||
{t("settings:providers.getMoonshotApiKey")}
|
||||
</VSCodeButtonLink>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ export { Glama } from "./Glama"
|
|||
export { Groq } from "./Groq"
|
||||
export { LMStudio } from "./LMStudio"
|
||||
export { Mistral } from "./Mistral"
|
||||
export { Moonshot } from "./Moonshot"
|
||||
export { Ollama } from "./Ollama"
|
||||
export { OpenAI } from "./OpenAI"
|
||||
export { OpenAICompatible } from "./OpenAICompatible"
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import {
|
|||
bedrockModels,
|
||||
deepSeekDefaultModelId,
|
||||
deepSeekModels,
|
||||
moonshotDefaultModelId,
|
||||
moonshotModels,
|
||||
geminiDefaultModelId,
|
||||
geminiModels,
|
||||
mistralDefaultModelId,
|
||||
|
|
@ -162,6 +164,11 @@ function getSelectedModel({
|
|||
const info = deepSeekModels[id as keyof typeof deepSeekModels]
|
||||
return { id, info }
|
||||
}
|
||||
case "moonshot": {
|
||||
const id = apiConfiguration.apiModelId ?? moonshotDefaultModelId
|
||||
const info = moonshotModels[id as keyof typeof moonshotModels]
|
||||
return { id, info }
|
||||
}
|
||||
case "openai-native": {
|
||||
const id = apiConfiguration.apiModelId ?? openAiNativeDefaultModelId
|
||||
const info = openAiNativeModels[id as keyof typeof openAiNativeModels]
|
||||
|
|
@ -211,6 +218,7 @@ function getSelectedModel({
|
|||
// case "human-relay":
|
||||
// case "fake-ai":
|
||||
default: {
|
||||
provider satisfies "anthropic" | "gemini-cli" | "human-relay" | "fake-ai"
|
||||
const id = apiConfiguration.apiModelId ?? anthropicDefaultModelId
|
||||
const info = anthropicModels[id as keyof typeof anthropicModels]
|
||||
return { id, info }
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/ca/settings.json
generated
3
webview-ui/src/i18n/locales/ca/settings.json
generated
|
|
@ -253,6 +253,9 @@
|
|||
"getChutesApiKey": "Obtenir clau API de Chutes",
|
||||
"deepSeekApiKey": "Clau API de DeepSeek",
|
||||
"getDeepSeekApiKey": "Obtenir clau API de DeepSeek",
|
||||
"moonshotApiKey": "Clau API de Moonshot",
|
||||
"getMoonshotApiKey": "Obtenir clau API de Moonshot",
|
||||
"moonshotBaseUrl": "Punt d'entrada de Moonshot",
|
||||
"geminiApiKey": "Clau API de Gemini",
|
||||
"getGroqApiKey": "Obtenir clau API de Groq",
|
||||
"groqApiKey": "Clau API de Groq",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/de/settings.json
generated
3
webview-ui/src/i18n/locales/de/settings.json
generated
|
|
@ -253,6 +253,9 @@
|
|||
"getChutesApiKey": "Chutes API-Schlüssel erhalten",
|
||||
"deepSeekApiKey": "DeepSeek API-Schlüssel",
|
||||
"getDeepSeekApiKey": "DeepSeek API-Schlüssel erhalten",
|
||||
"moonshotApiKey": "Moonshot API-Schlüssel",
|
||||
"getMoonshotApiKey": "Moonshot API-Schlüssel erhalten",
|
||||
"moonshotBaseUrl": "Moonshot-Einstiegspunkt",
|
||||
"geminiApiKey": "Gemini API-Schlüssel",
|
||||
"getGroqApiKey": "Groq API-Schlüssel erhalten",
|
||||
"groqApiKey": "Groq API-Schlüssel",
|
||||
|
|
|
|||
|
|
@ -253,6 +253,9 @@
|
|||
"getChutesApiKey": "Get Chutes API Key",
|
||||
"deepSeekApiKey": "DeepSeek API Key",
|
||||
"getDeepSeekApiKey": "Get DeepSeek API Key",
|
||||
"moonshotApiKey": "Moonshot API Key",
|
||||
"getMoonshotApiKey": "Get Moonshot API Key",
|
||||
"moonshotBaseUrl": "Moonshot Entrypoint",
|
||||
"geminiApiKey": "Gemini API Key",
|
||||
"getGroqApiKey": "Get Groq API Key",
|
||||
"groqApiKey": "Groq API Key",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/es/settings.json
generated
3
webview-ui/src/i18n/locales/es/settings.json
generated
|
|
@ -253,6 +253,9 @@
|
|||
"getChutesApiKey": "Obtener clave API de Chutes",
|
||||
"deepSeekApiKey": "Clave API de DeepSeek",
|
||||
"getDeepSeekApiKey": "Obtener clave API de DeepSeek",
|
||||
"moonshotApiKey": "Clave API de Moonshot",
|
||||
"getMoonshotApiKey": "Obtener clave API de Moonshot",
|
||||
"moonshotBaseUrl": "Punto de entrada de Moonshot",
|
||||
"geminiApiKey": "Clave API de Gemini",
|
||||
"getGroqApiKey": "Obtener clave API de Groq",
|
||||
"groqApiKey": "Clave API de Groq",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/fr/settings.json
generated
3
webview-ui/src/i18n/locales/fr/settings.json
generated
|
|
@ -253,6 +253,9 @@
|
|||
"getChutesApiKey": "Obtenir la clé API Chutes",
|
||||
"deepSeekApiKey": "Clé API DeepSeek",
|
||||
"getDeepSeekApiKey": "Obtenir la clé API DeepSeek",
|
||||
"moonshotApiKey": "Clé API Moonshot",
|
||||
"getMoonshotApiKey": "Obtenir la clé API Moonshot",
|
||||
"moonshotBaseUrl": "Point d'entrée Moonshot",
|
||||
"geminiApiKey": "Clé API Gemini",
|
||||
"getGroqApiKey": "Obtenir la clé API Groq",
|
||||
"groqApiKey": "Clé API Groq",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/hi/settings.json
generated
3
webview-ui/src/i18n/locales/hi/settings.json
generated
|
|
@ -253,6 +253,9 @@
|
|||
"getChutesApiKey": "Chutes API कुंजी प्राप्त करें",
|
||||
"deepSeekApiKey": "DeepSeek API कुंजी",
|
||||
"getDeepSeekApiKey": "DeepSeek API कुंजी प्राप्त करें",
|
||||
"moonshotApiKey": "Moonshot API कुंजी",
|
||||
"getMoonshotApiKey": "Moonshot API कुंजी प्राप्त करें",
|
||||
"moonshotBaseUrl": "Moonshot प्रवेश बिंदु",
|
||||
"geminiApiKey": "Gemini API कुंजी",
|
||||
"getGroqApiKey": "Groq API कुंजी प्राप्त करें",
|
||||
"groqApiKey": "Groq API कुंजी",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/id/settings.json
generated
3
webview-ui/src/i18n/locales/id/settings.json
generated
|
|
@ -257,6 +257,9 @@
|
|||
"getChutesApiKey": "Dapatkan Chutes API Key",
|
||||
"deepSeekApiKey": "DeepSeek API Key",
|
||||
"getDeepSeekApiKey": "Dapatkan DeepSeek API Key",
|
||||
"moonshotApiKey": "Kunci API Moonshot",
|
||||
"getMoonshotApiKey": "Dapatkan Kunci API Moonshot",
|
||||
"moonshotBaseUrl": "Titik Masuk Moonshot",
|
||||
"geminiApiKey": "Gemini API Key",
|
||||
"getGroqApiKey": "Dapatkan Groq API Key",
|
||||
"groqApiKey": "Groq API Key",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/it/settings.json
generated
3
webview-ui/src/i18n/locales/it/settings.json
generated
|
|
@ -253,6 +253,9 @@
|
|||
"getChutesApiKey": "Ottieni chiave API Chutes",
|
||||
"deepSeekApiKey": "Chiave API DeepSeek",
|
||||
"getDeepSeekApiKey": "Ottieni chiave API DeepSeek",
|
||||
"moonshotApiKey": "Chiave API Moonshot",
|
||||
"getMoonshotApiKey": "Ottieni chiave API Moonshot",
|
||||
"moonshotBaseUrl": "Punto di ingresso Moonshot",
|
||||
"geminiApiKey": "Chiave API Gemini",
|
||||
"getGroqApiKey": "Ottieni chiave API Groq",
|
||||
"groqApiKey": "Chiave API Groq",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/ja/settings.json
generated
3
webview-ui/src/i18n/locales/ja/settings.json
generated
|
|
@ -253,6 +253,9 @@
|
|||
"getChutesApiKey": "Chutes APIキーを取得",
|
||||
"deepSeekApiKey": "DeepSeek APIキー",
|
||||
"getDeepSeekApiKey": "DeepSeek APIキーを取得",
|
||||
"moonshotApiKey": "Moonshot APIキー",
|
||||
"getMoonshotApiKey": "Moonshot APIキーを取得",
|
||||
"moonshotBaseUrl": "Moonshot エントリーポイント",
|
||||
"geminiApiKey": "Gemini APIキー",
|
||||
"getGroqApiKey": "Groq APIキーを取得",
|
||||
"groqApiKey": "Groq APIキー",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/ko/settings.json
generated
3
webview-ui/src/i18n/locales/ko/settings.json
generated
|
|
@ -253,6 +253,9 @@
|
|||
"getChutesApiKey": "Chutes API 키 받기",
|
||||
"deepSeekApiKey": "DeepSeek API 키",
|
||||
"getDeepSeekApiKey": "DeepSeek API 키 받기",
|
||||
"moonshotApiKey": "Moonshot API 키",
|
||||
"getMoonshotApiKey": "Moonshot API 키 받기",
|
||||
"moonshotBaseUrl": "Moonshot 엔트리포인트",
|
||||
"geminiApiKey": "Gemini API 키",
|
||||
"getGroqApiKey": "Groq API 키 받기",
|
||||
"groqApiKey": "Groq API 키",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/nl/settings.json
generated
3
webview-ui/src/i18n/locales/nl/settings.json
generated
|
|
@ -253,6 +253,9 @@
|
|||
"getChutesApiKey": "Chutes API-sleutel ophalen",
|
||||
"deepSeekApiKey": "DeepSeek API-sleutel",
|
||||
"getDeepSeekApiKey": "DeepSeek API-sleutel ophalen",
|
||||
"moonshotApiKey": "Moonshot API-sleutel",
|
||||
"getMoonshotApiKey": "Moonshot API-sleutel ophalen",
|
||||
"moonshotBaseUrl": "Moonshot-ingangspunt",
|
||||
"geminiApiKey": "Gemini API-sleutel",
|
||||
"getGroqApiKey": "Groq API-sleutel ophalen",
|
||||
"groqApiKey": "Groq API-sleutel",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/pl/settings.json
generated
3
webview-ui/src/i18n/locales/pl/settings.json
generated
|
|
@ -253,6 +253,9 @@
|
|||
"getChutesApiKey": "Uzyskaj klucz API Chutes",
|
||||
"deepSeekApiKey": "Klucz API DeepSeek",
|
||||
"getDeepSeekApiKey": "Uzyskaj klucz API DeepSeek",
|
||||
"moonshotApiKey": "Klucz API Moonshot",
|
||||
"getMoonshotApiKey": "Uzyskaj klucz API Moonshot",
|
||||
"moonshotBaseUrl": "Punkt wejścia Moonshot",
|
||||
"geminiApiKey": "Klucz API Gemini",
|
||||
"getGroqApiKey": "Uzyskaj klucz API Groq",
|
||||
"groqApiKey": "Klucz API Groq",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/pt-BR/settings.json
generated
3
webview-ui/src/i18n/locales/pt-BR/settings.json
generated
|
|
@ -253,6 +253,9 @@
|
|||
"getChutesApiKey": "Obter chave de API Chutes",
|
||||
"deepSeekApiKey": "Chave de API DeepSeek",
|
||||
"getDeepSeekApiKey": "Obter chave de API DeepSeek",
|
||||
"moonshotApiKey": "Chave de API Moonshot",
|
||||
"getMoonshotApiKey": "Obter chave de API Moonshot",
|
||||
"moonshotBaseUrl": "Ponto de entrada Moonshot",
|
||||
"geminiApiKey": "Chave de API Gemini",
|
||||
"getGroqApiKey": "Obter chave de API Groq",
|
||||
"groqApiKey": "Chave de API Groq",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/ru/settings.json
generated
3
webview-ui/src/i18n/locales/ru/settings.json
generated
|
|
@ -253,6 +253,9 @@
|
|||
"getChutesApiKey": "Получить Chutes API-ключ",
|
||||
"deepSeekApiKey": "DeepSeek API-ключ",
|
||||
"getDeepSeekApiKey": "Получить DeepSeek API-ключ",
|
||||
"moonshotApiKey": "Moonshot API-ключ",
|
||||
"getMoonshotApiKey": "Получить Moonshot API-ключ",
|
||||
"moonshotBaseUrl": "Точка входа Moonshot",
|
||||
"geminiApiKey": "Gemini API-ключ",
|
||||
"getGroqApiKey": "Получить Groq API-ключ",
|
||||
"groqApiKey": "Groq API-ключ",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/tr/settings.json
generated
3
webview-ui/src/i18n/locales/tr/settings.json
generated
|
|
@ -253,6 +253,9 @@
|
|||
"getChutesApiKey": "Chutes API Anahtarı Al",
|
||||
"deepSeekApiKey": "DeepSeek API Anahtarı",
|
||||
"getDeepSeekApiKey": "DeepSeek API Anahtarı Al",
|
||||
"moonshotApiKey": "Moonshot API Anahtarı",
|
||||
"getMoonshotApiKey": "Moonshot API Anahtarı Al",
|
||||
"moonshotBaseUrl": "Moonshot Giriş Noktası",
|
||||
"geminiApiKey": "Gemini API Anahtarı",
|
||||
"getGroqApiKey": "Groq API Anahtarı Al",
|
||||
"groqApiKey": "Groq API Anahtarı",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/vi/settings.json
generated
3
webview-ui/src/i18n/locales/vi/settings.json
generated
|
|
@ -253,6 +253,9 @@
|
|||
"getChutesApiKey": "Lấy khóa API Chutes",
|
||||
"deepSeekApiKey": "Khóa API DeepSeek",
|
||||
"getDeepSeekApiKey": "Lấy khóa API DeepSeek",
|
||||
"moonshotApiKey": "Khóa API Moonshot",
|
||||
"getMoonshotApiKey": "Lấy khóa API Moonshot",
|
||||
"moonshotBaseUrl": "Điểm vào Moonshot",
|
||||
"geminiApiKey": "Khóa API Gemini",
|
||||
"getGroqApiKey": "Lấy khóa API Groq",
|
||||
"groqApiKey": "Khóa API Groq",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/zh-CN/settings.json
generated
3
webview-ui/src/i18n/locales/zh-CN/settings.json
generated
|
|
@ -253,6 +253,9 @@
|
|||
"getChutesApiKey": "获取 Chutes API 密钥",
|
||||
"deepSeekApiKey": "DeepSeek API 密钥",
|
||||
"getDeepSeekApiKey": "获取 DeepSeek API 密钥",
|
||||
"moonshotApiKey": "Moonshot API 密钥",
|
||||
"getMoonshotApiKey": "获取 Moonshot API 密钥",
|
||||
"moonshotBaseUrl": "Moonshot 服务站点",
|
||||
"geminiApiKey": "Gemini API 密钥",
|
||||
"getGroqApiKey": "获取 Groq API 密钥",
|
||||
"groqApiKey": "Groq API 密钥",
|
||||
|
|
|
|||
3
webview-ui/src/i18n/locales/zh-TW/settings.json
generated
3
webview-ui/src/i18n/locales/zh-TW/settings.json
generated
|
|
@ -253,6 +253,9 @@
|
|||
"getChutesApiKey": "取得 Chutes API 金鑰",
|
||||
"deepSeekApiKey": "DeepSeek API 金鑰",
|
||||
"getDeepSeekApiKey": "取得 DeepSeek API 金鑰",
|
||||
"moonshotApiKey": "Moonshot API 金鑰",
|
||||
"getMoonshotApiKey": "取得 Moonshot API 金鑰",
|
||||
"moonshotBaseUrl": "Moonshot 服務站點",
|
||||
"geminiApiKey": "Gemini API 金鑰",
|
||||
"getGroqApiKey": "取得 Groq API 金鑰",
|
||||
"groqApiKey": "Groq API 金鑰",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue