mirror of
https://github.com/cline/cline.git
synced 2026-09-11 16:42:40 +08:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f3ac183f7 | ||
|
|
a8cc02317d |
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
Add Moonshot AI provider
|
||||
@@ -122,6 +122,7 @@ enum ApiProvider {
|
||||
CEREBRAS = 23;
|
||||
SAPAICORE = 24;
|
||||
CLAUDE_CODE = 25;
|
||||
MOONSHOT = 26;
|
||||
}
|
||||
|
||||
// Model info for OpenAI-compatible models
|
||||
@@ -237,4 +238,5 @@ message ModelsApiConfiguration {
|
||||
optional string claude_code_path = 73;
|
||||
optional string aws_authentication = 74;
|
||||
optional string aws_bedrock_api_key = 75;
|
||||
optional string moonshot_api_key = 76;
|
||||
}
|
||||
|
||||
@@ -236,4 +236,7 @@ message ApiConfiguration {
|
||||
// Extension fields for Bedrock Api Keys
|
||||
optional string aws_authentication = 78;
|
||||
optional string aws_bedrock_api_key = 79;
|
||||
|
||||
// Moonshot
|
||||
optional string moonshot_api_key = 80;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import { SambanovaHandler } from "./providers/sambanova"
|
||||
import { CerebrasHandler } from "./providers/cerebras"
|
||||
import { SapAiCoreHandler } from "./providers/sapaicore"
|
||||
import { ClaudeCodeHandler } from "./providers/claude-code"
|
||||
import { MoonshotHandler } from "./providers/moonshot"
|
||||
|
||||
export interface ApiHandler {
|
||||
createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream
|
||||
@@ -187,6 +188,11 @@ function createHandlerForProvider(apiProvider: string | undefined, options: Omit
|
||||
liteLlmUsePromptCache: options.liteLlmUsePromptCache,
|
||||
taskId: options.taskId,
|
||||
})
|
||||
case "moonshot":
|
||||
return new MoonshotHandler({
|
||||
moonshotApiKey: options.moonshotApiKey,
|
||||
apiModelId: options.apiModelId,
|
||||
})
|
||||
case "nebius":
|
||||
return new NebiusHandler({
|
||||
nebiusApiKey: options.nebiusApiKey,
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
import { Anthropic } from "@anthropic-ai/sdk"
|
||||
import OpenAI from "openai"
|
||||
import { withRetry } from "../retry"
|
||||
import { ApiHandler } from "../index"
|
||||
import { convertToOpenAiMessages } from "../transform/openai-format"
|
||||
import { ApiStream } from "../transform/stream"
|
||||
import { ModelInfo, MoonshotModelId, moonshotModels, moonshotDefaultModelId } from "@/shared/api"
|
||||
|
||||
interface MoonshotHandlerOptions {
|
||||
moonshotApiKey?: string
|
||||
apiModelId?: string
|
||||
}
|
||||
|
||||
export class MoonshotHandler implements ApiHandler {
|
||||
private client: OpenAI | undefined
|
||||
|
||||
constructor(private readonly options: MoonshotHandlerOptions) {}
|
||||
|
||||
private ensureClient(): OpenAI {
|
||||
if (!this.client) {
|
||||
if (!this.options.moonshotApiKey) {
|
||||
throw new Error("Moonshot API key is required")
|
||||
}
|
||||
try {
|
||||
this.client = new OpenAI({
|
||||
baseURL: "https://api.moonshot.ai/v1",
|
||||
apiKey: this.options.moonshotApiKey,
|
||||
})
|
||||
} catch (error) {
|
||||
throw new Error(`Error creating Moonshot client: ${error.message}`)
|
||||
}
|
||||
}
|
||||
return this.client
|
||||
}
|
||||
|
||||
@withRetry()
|
||||
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
|
||||
const client = this.ensureClient()
|
||||
const model = this.getModel()
|
||||
|
||||
const openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [
|
||||
{ role: "system", content: systemPrompt },
|
||||
...convertToOpenAiMessages(messages),
|
||||
]
|
||||
|
||||
const stream = await client.chat.completions.create({
|
||||
model: model.id,
|
||||
messages: openAiMessages,
|
||||
temperature: 0,
|
||||
stream: true,
|
||||
stream_options: { include_usage: true },
|
||||
})
|
||||
for await (const chunk of stream) {
|
||||
const delta = chunk.choices[0]?.delta
|
||||
if (delta?.content) {
|
||||
yield {
|
||||
type: "text",
|
||||
text: delta.content,
|
||||
}
|
||||
}
|
||||
|
||||
if (delta && "reasoning_content" in delta && delta.reasoning_content) {
|
||||
yield {
|
||||
type: "reasoning",
|
||||
reasoning: (delta.reasoning_content as string | undefined) || "",
|
||||
}
|
||||
}
|
||||
|
||||
if (chunk.usage) {
|
||||
yield {
|
||||
type: "usage",
|
||||
inputTokens: chunk.usage.prompt_tokens || 0,
|
||||
outputTokens: chunk.usage.completion_tokens || 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getModel(): { id: MoonshotModelId; info: ModelInfo } {
|
||||
const modelId = this.options.apiModelId
|
||||
|
||||
if (modelId && modelId in moonshotModels) {
|
||||
const id = modelId as MoonshotModelId
|
||||
return { id, info: moonshotModels[id] }
|
||||
}
|
||||
return { id: moonshotDefaultModelId, info: moonshotModels[moonshotDefaultModelId] }
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ export type SecretKey =
|
||||
| "authNonce"
|
||||
| "asksageApiKey"
|
||||
| "xaiApiKey"
|
||||
| "moonshotApiKey"
|
||||
| "nebiusApiKey"
|
||||
| "sambanovaApiKey"
|
||||
| "cerebrasApiKey"
|
||||
|
||||
@@ -166,6 +166,7 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
|
||||
xaiApiKey,
|
||||
sambanovaApiKey,
|
||||
cerebrasApiKey,
|
||||
moonshotApiKey,
|
||||
nebiusApiKey,
|
||||
planActSeparateModelsSettingRaw,
|
||||
favoritedModelIds,
|
||||
@@ -241,6 +242,7 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
|
||||
getSecret(context, "xaiApiKey") as Promise<string | undefined>,
|
||||
getSecret(context, "sambanovaApiKey") as Promise<string | undefined>,
|
||||
getSecret(context, "cerebrasApiKey") as Promise<string | undefined>,
|
||||
getSecret(context, "moonshotApiKey") as Promise<string | undefined>,
|
||||
getSecret(context, "nebiusApiKey") as Promise<string | undefined>,
|
||||
getGlobalState(context, "planActSeparateModelsSetting") as Promise<boolean | undefined>,
|
||||
getGlobalState(context, "favoritedModelIds") as Promise<string[] | undefined>,
|
||||
@@ -437,6 +439,7 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
|
||||
xaiApiKey,
|
||||
sambanovaApiKey,
|
||||
cerebrasApiKey,
|
||||
moonshotApiKey,
|
||||
nebiusApiKey,
|
||||
favoritedModelIds,
|
||||
requestTimeoutMs,
|
||||
@@ -547,6 +550,7 @@ export async function updateApiConfiguration(context: vscode.ExtensionContext, a
|
||||
clineAccountId,
|
||||
sambanovaApiKey,
|
||||
cerebrasApiKey,
|
||||
moonshotApiKey,
|
||||
nebiusApiKey,
|
||||
favoritedModelIds,
|
||||
fireworksApiKey,
|
||||
@@ -643,6 +647,7 @@ export async function updateApiConfiguration(context: vscode.ExtensionContext, a
|
||||
xaiApiKey,
|
||||
sambanovaApiKey,
|
||||
cerebrasApiKey,
|
||||
moonshotApiKey,
|
||||
nebiusApiKey,
|
||||
sapAiCoreClientId,
|
||||
sapAiCoreClientSecret,
|
||||
@@ -686,6 +691,7 @@ export async function resetGlobalState(context: vscode.ExtensionContext) {
|
||||
"xaiApiKey",
|
||||
"sambanovaApiKey",
|
||||
"cerebrasApiKey",
|
||||
"moonshotApiKey",
|
||||
"nebiusApiKey",
|
||||
]
|
||||
for (const key of secretKeys) {
|
||||
|
||||
@@ -20,6 +20,7 @@ export type ApiProvider =
|
||||
| "vscode-lm"
|
||||
| "cline"
|
||||
| "litellm"
|
||||
| "moonshot"
|
||||
| "nebius"
|
||||
| "fireworks"
|
||||
| "asksage"
|
||||
@@ -88,6 +89,7 @@ export interface ApiHandlerOptions {
|
||||
azureApiVersion?: string
|
||||
vsCodeLmModelSelector?: LanguageModelChatSelector
|
||||
qwenApiLine?: string
|
||||
moonshotApiKey?: string
|
||||
nebiusApiKey?: string
|
||||
asksageApiUrl?: string
|
||||
asksageApiKey?: string
|
||||
@@ -2552,3 +2554,34 @@ export const sapAiCoreModels = {
|
||||
description: sapAiCoreModelDescription,
|
||||
},
|
||||
} as const satisfies Record<string, ModelInfo>
|
||||
|
||||
// Moonshot AI Studio
|
||||
// https://platform.moonshot.ai/docs/pricing/chat
|
||||
export const moonshotModels = {
|
||||
"kimi-k2-0711-preview": {
|
||||
maxTokens: 131_072,
|
||||
contextWindow: 131_072,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
inputPrice: 0.6,
|
||||
outputPrice: 2.5,
|
||||
},
|
||||
"moonshot-v1-128k-vision-preview": {
|
||||
maxTokens: 131_072,
|
||||
contextWindow: 131_072,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
inputPrice: 2,
|
||||
outputPrice: 5,
|
||||
},
|
||||
"kimi-thinking-preview": {
|
||||
maxTokens: 131_072,
|
||||
contextWindow: 131_072,
|
||||
supportsImages: false,
|
||||
supportsPromptCache: false,
|
||||
inputPrice: 30,
|
||||
outputPrice: 30,
|
||||
},
|
||||
} as const satisfies Record<string, ModelInfo>
|
||||
export type MoonshotModelId = keyof typeof moonshotModels
|
||||
export const moonshotDefaultModelId = "kimi-k2-0711-preview" satisfies MoonshotModelId
|
||||
|
||||
@@ -222,6 +222,8 @@ function convertApiProviderToProto(provider: string | undefined): ProtoApiProvid
|
||||
return ProtoApiProvider.CLINE
|
||||
case "litellm":
|
||||
return ProtoApiProvider.LITELLM
|
||||
case "moonshot":
|
||||
return ProtoApiProvider.MOONSHOT
|
||||
case "nebius":
|
||||
return ProtoApiProvider.NEBIUS
|
||||
case "fireworks":
|
||||
@@ -282,6 +284,8 @@ function convertProtoToApiProvider(provider: ProtoApiProvider): ApiProvider {
|
||||
return "cline"
|
||||
case ProtoApiProvider.LITELLM:
|
||||
return "litellm"
|
||||
case ProtoApiProvider.MOONSHOT:
|
||||
return "moonshot"
|
||||
case ProtoApiProvider.NEBIUS:
|
||||
return "nebius"
|
||||
case ProtoApiProvider.FIREWORKS:
|
||||
@@ -364,6 +368,7 @@ export function convertApiConfigurationToProto(config: ApiConfiguration): ProtoA
|
||||
azureApiVersion: config.azureApiVersion,
|
||||
vsCodeLmModelSelector: config.vsCodeLmModelSelector,
|
||||
qwenApiLine: config.qwenApiLine,
|
||||
moonshotApiKey: config.moonshotApiKey,
|
||||
nebiusApiKey: config.nebiusApiKey,
|
||||
asksageApiUrl: config.asksageApiUrl,
|
||||
asksageApiKey: config.asksageApiKey,
|
||||
@@ -445,6 +450,7 @@ export function convertProtoToApiConfiguration(protoConfig: ProtoApiConfiguratio
|
||||
azureApiVersion: protoConfig.azureApiVersion,
|
||||
vsCodeLmModelSelector: protoConfig.vsCodeLmModelSelector,
|
||||
qwenApiLine: protoConfig.qwenApiLine,
|
||||
moonshotApiKey: protoConfig.moonshotApiKey,
|
||||
nebiusApiKey: protoConfig.nebiusApiKey,
|
||||
asksageApiUrl: protoConfig.asksageApiUrl,
|
||||
asksageApiKey: protoConfig.asksageApiKey,
|
||||
|
||||
@@ -30,6 +30,7 @@ export function convertApiConfigurationToProtoApiConfiguration(config: ApiConfig
|
||||
qwenApiKey: config.qwenApiKey,
|
||||
doubaoApiKey: config.doubaoApiKey,
|
||||
mistralApiKey: config.mistralApiKey,
|
||||
moonshotApiKey: config.moonshotApiKey,
|
||||
nebiusApiKey: config.nebiusApiKey,
|
||||
asksageApiKey: config.asksageApiKey,
|
||||
xaiApiKey: config.xaiApiKey,
|
||||
@@ -152,6 +153,7 @@ export function convertProtoApiConfigurationToApiConfiguration(protoConfig: Prot
|
||||
qwenApiKey: protoConfig.qwenApiKey,
|
||||
doubaoApiKey: protoConfig.doubaoApiKey,
|
||||
mistralApiKey: protoConfig.mistralApiKey,
|
||||
moonshotApiKey: protoConfig.moonshotApiKey,
|
||||
nebiusApiKey: protoConfig.nebiusApiKey,
|
||||
asksageApiKey: protoConfig.asksageApiKey,
|
||||
xaiApiKey: protoConfig.xaiApiKey,
|
||||
|
||||
@@ -31,6 +31,7 @@ import { OllamaProvider } from "./providers/OllamaProvider"
|
||||
import { ClaudeCodeProvider } from "./providers/ClaudeCodeProvider"
|
||||
import { SapAiCoreProvider } from "./providers/SapAiCoreProvider"
|
||||
import { BedrockProvider } from "./providers/BedrockProvider"
|
||||
import { MoonshotProvider } from "./providers/MoonshotProvider"
|
||||
import { NebiusProvider } from "./providers/NebiusProvider"
|
||||
import { LiteLlmProvider } from "./providers/LiteLlmProvider"
|
||||
import { VSCodeLmProvider } from "./providers/VSCodeLmProvider"
|
||||
@@ -146,6 +147,7 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage, is
|
||||
<VSCodeOption value="lmstudio">LM Studio</VSCodeOption>
|
||||
<VSCodeOption value="ollama">Ollama</VSCodeOption>
|
||||
<VSCodeOption value="litellm">LiteLLM</VSCodeOption>
|
||||
<VSCodeOption value="moonshot">Moonshot AI</VSCodeOption>
|
||||
<VSCodeOption value="nebius">Nebius AI Studio</VSCodeOption>
|
||||
<VSCodeOption value="asksage">AskSage</VSCodeOption>
|
||||
<VSCodeOption value="xai">xAI</VSCodeOption>
|
||||
@@ -241,6 +243,10 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage, is
|
||||
<OllamaProvider showModelOptions={showModelOptions} isPopup={isPopup} />
|
||||
)}
|
||||
|
||||
{apiConfiguration && selectedProvider === "moonshot" && (
|
||||
<MoonshotProvider showModelOptions={showModelOptions} isPopup={isPopup} />
|
||||
)}
|
||||
|
||||
{apiConfiguration && selectedProvider === "nebius" && (
|
||||
<NebiusProvider showModelOptions={showModelOptions} isPopup={isPopup} />
|
||||
)}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import { moonshotModels } from "@shared/api"
|
||||
import { ApiKeyField } from "../common/ApiKeyField"
|
||||
import { ModelSelector } from "../common/ModelSelector"
|
||||
import { ModelInfoView } from "../common/ModelInfoView"
|
||||
import { normalizeApiConfiguration } from "../utils/providerUtils"
|
||||
import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
|
||||
import { useExtensionState } from "@/context/ExtensionStateContext"
|
||||
|
||||
/**
|
||||
* Props for the MoonshotProvider component
|
||||
*/
|
||||
interface MoonshotProviderProps {
|
||||
showModelOptions: boolean
|
||||
isPopup?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* The Moonshot AI Studio provider configuration component
|
||||
*/
|
||||
export const MoonshotProvider = ({ showModelOptions, isPopup }: MoonshotProviderProps) => {
|
||||
const { apiConfiguration } = useExtensionState()
|
||||
const { handleFieldChange } = useApiConfigurationHandlers()
|
||||
|
||||
// Get the normalized configuration
|
||||
const { selectedModelId, selectedModelInfo } = normalizeApiConfiguration(apiConfiguration)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ApiKeyField
|
||||
initialValue={apiConfiguration?.moonshotApiKey || ""}
|
||||
onChange={(value) => handleFieldChange("moonshotApiKey", value)}
|
||||
providerName="Moonshot"
|
||||
signupUrl="https://platform.moonshot.ai/console/api-keys"
|
||||
helpText="This key is stored locally and only used to make API requests from this extension."
|
||||
/>
|
||||
|
||||
{showModelOptions && (
|
||||
<>
|
||||
<ModelSelector
|
||||
models={moonshotModels}
|
||||
selectedModelId={selectedModelId}
|
||||
onChange={(e: any) => handleFieldChange("apiModelId", e.target.value)}
|
||||
label="Model"
|
||||
/>
|
||||
|
||||
<ModelInfoView selectedModelId={selectedModelId} modelInfo={selectedModelInfo} isPopup={isPopup} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -34,6 +34,8 @@ import {
|
||||
doubaoModels,
|
||||
doubaoDefaultModelId,
|
||||
liteLlmModelInfoSaneDefaults,
|
||||
moonshotModels,
|
||||
moonshotDefaultModelId,
|
||||
nebiusModels,
|
||||
nebiusDefaultModelId,
|
||||
cerebrasModels,
|
||||
@@ -168,6 +170,8 @@ export function normalizeApiConfiguration(apiConfiguration?: ApiConfiguration):
|
||||
}
|
||||
case "xai":
|
||||
return getProviderData(xaiModels, xaiDefaultModelId)
|
||||
case "moonshot":
|
||||
return getProviderData(moonshotModels, moonshotDefaultModelId)
|
||||
case "nebius":
|
||||
return getProviderData(nebiusModels, nebiusDefaultModelId)
|
||||
case "sambanova":
|
||||
|
||||
@@ -98,6 +98,11 @@ export function validateApiConfiguration(apiConfiguration?: ApiConfiguration): s
|
||||
return "You must provide a valid model selector."
|
||||
}
|
||||
break
|
||||
case "moonshot":
|
||||
if (!apiConfiguration.moonshotApiKey) {
|
||||
return "You must provide a valid API key or choose a different provider."
|
||||
}
|
||||
break
|
||||
case "nebius":
|
||||
if (!apiConfiguration.nebiusApiKey) {
|
||||
return "You must provide a valid API key or choose a different provider."
|
||||
|
||||
Reference in New Issue
Block a user