From 72ef176cd46c6246e2e2647795b04e527ccecdb3 Mon Sep 17 00:00:00 2001 From: Rady Date: Tue, 31 Dec 2024 21:06:55 +0800 Subject: [PATCH] Add DeepSeek Provider --- README.md | 25 ++++++++++ package-lock.json | 16 +++---- package.json | 3 +- src/api/index.ts | 3 ++ src/core/webview/ClineProvider.ts | 13 ++++++ src/shared/api.ts | 21 +++++++++ webview-ui/package-lock.json | 5 +- webview-ui/package.json | 4 +- .../src/components/settings/ApiOptions.tsx | 46 +++++++++++++++++++ webview-ui/src/index.tsx | 2 +- webview-ui/src/utils/validate.ts | 5 ++ 11 files changed, 129 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 6819183bd..a2bc76a4d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,30 @@ # Cline (prev. Claude Dev) – \#1 on OpenRouter +**Clone from Cline only with Deepseek Provider** + +Usage: + +1. Clone the repository _(Requires [git-lfs](https://git-lfs.com/))_: +```bash +git clone https://github.com/cline/cline.git +``` + +2. Install dependencies: +```bash +npm install +``` + +3. Build the extension: +```bash +npm run package && vsce package +``` + +

+ DeepSeek Provider +

+ +-------------------- +

diff --git a/package-lock.json b/package-lock.json index a80a79fbe..5723f20b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "claude-dev", - "version": "3.0.4", + "version": "3.0.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "claude-dev", - "version": "3.0.4", + "version": "3.0.9", "license": "Apache-2.0", "dependencies": { "@anthropic-ai/bedrock-sdk": "^0.10.2", @@ -5769,9 +5769,9 @@ "license": "MIT" }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -9038,9 +9038,9 @@ "license": "MIT" }, "node_modules/npm-run-all/node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 391ec12f7..272bf070a 100644 --- a/package.json +++ b/package.json @@ -145,7 +145,8 @@ "start:webview": "cd webview-ui && npm run start", "build:webview": "cd webview-ui && npm run build", "test:webview": "cd webview-ui && npm run test", - "publish:marketplace": "vsce publish && ovsx publish" + "publish:marketplace": "vsce publish && ovsx publish", + "vsce-package": "vsce package" }, "devDependencies": { "@types/diff": "^5.2.1", diff --git a/src/api/index.ts b/src/api/index.ts index ec35c2a2a..eb6e9bd29 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -9,6 +9,7 @@ import { OllamaHandler } from "./providers/ollama" import { LmStudioHandler } from "./providers/lmstudio" import { GeminiHandler } from "./providers/gemini" import { OpenAiNativeHandler } from "./providers/openai-native" +import { DeepSeekHandler } from "./providers/deepseek" import { ApiStream } from "./transform/stream" export interface ApiHandler { @@ -37,6 +38,8 @@ export function buildApiHandler(configuration: ApiConfiguration): ApiHandler { return new GeminiHandler(options) case "openai-native": return new OpenAiNativeHandler(options) + case "deepseek": + return new DeepSeekHandler(options) default: return new AnthropicHandler(options) } diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 8a556f936..abe7650b5 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -39,6 +39,7 @@ type SecretKey = | "openAiApiKey" | "geminiApiKey" | "openAiNativeApiKey" + | "deepSeekApiKey" type GlobalStateKey = | "apiProvider" | "apiModelId" @@ -59,6 +60,7 @@ type GlobalStateKey = | "azureApiVersion" | "openRouterModelId" | "openRouterModelInfo" + | "deepSeekBaseUrl" | "autoApprovalSettings" export const GlobalFileNames = { @@ -381,6 +383,8 @@ export class ClineProvider implements vscode.WebviewViewProvider { azureApiVersion, openRouterModelId, openRouterModelInfo, + deepSeekApiKey, + deepSeekBaseUrl, } = message.apiConfiguration await this.updateGlobalState("apiProvider", apiProvider) await this.updateGlobalState("apiModelId", apiModelId) @@ -406,6 +410,8 @@ export class ClineProvider implements vscode.WebviewViewProvider { await this.updateGlobalState("azureApiVersion", azureApiVersion) await this.updateGlobalState("openRouterModelId", openRouterModelId) await this.updateGlobalState("openRouterModelInfo", openRouterModelInfo) + await this.storeSecret("deepSeekApiKey", deepSeekApiKey) + await this.updateGlobalState("deepSeekBaseUrl", deepSeekBaseUrl) if (this.cline) { this.cline.api = buildApiHandler(message.apiConfiguration) } @@ -922,6 +928,8 @@ export class ClineProvider implements vscode.WebviewViewProvider { lastShownAnnouncementId, customInstructions, taskHistory, + deepSeekApiKey, + deepSeekBaseUrl, autoApprovalSettings, ] = await Promise.all([ this.getGlobalState("apiProvider") as Promise, @@ -951,6 +959,8 @@ export class ClineProvider implements vscode.WebviewViewProvider { this.getGlobalState("lastShownAnnouncementId") as Promise, this.getGlobalState("customInstructions") as Promise, this.getGlobalState("taskHistory") as Promise, + this.getSecret("deepSeekApiKey") as Promise, + this.getGlobalState("deepSeekBaseUrl") as Promise, this.getGlobalState("autoApprovalSettings") as Promise, ]) @@ -994,6 +1004,8 @@ export class ClineProvider implements vscode.WebviewViewProvider { azureApiVersion, openRouterModelId, openRouterModelInfo, + deepSeekApiKey, + deepSeekBaseUrl, }, lastShownAnnouncementId, customInstructions, @@ -1074,6 +1086,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { "openAiApiKey", "geminiApiKey", "openAiNativeApiKey", + "deepSeekApiKey", ] for (const key of secretKeys) { await this.storeSecret(key, undefined) diff --git a/src/shared/api.ts b/src/shared/api.ts index 868e9f06e..e4e1bddaf 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -8,6 +8,7 @@ export type ApiProvider = | "lmstudio" | "gemini" | "openai-native" + | "deepseek" export interface ApiHandlerOptions { apiModelId?: string @@ -33,6 +34,8 @@ export interface ApiHandlerOptions { geminiApiKey?: string openAiNativeApiKey?: string azureApiVersion?: string + deepSeekApiKey?: string + deepSeekBaseUrl?: string } export type ApiConfiguration = ApiHandlerOptions & { @@ -54,6 +57,24 @@ export interface ModelInfo { description?: string } +// DeepSeek +export type DeepSeekModelId = keyof typeof deepSeekModels +export const deepSeekDefaultModelId: DeepSeekModelId = "deepseek-chat" +export const deepSeekModels = { + "deepseek-chat": { + maxTokens: 4096, + contextWindow: 64_000, + supportsImages: false, + supportsComputerUse: false, + supportsPromptCache: true, + inputPrice: 0.14, + outputPrice: 0.28, + cacheWritesPrice: 0.14, + cacheReadsPrice: 0.014, + description: "DeepSeek V2.5 model with strong general capabilities and code abilities. Supports function calling and context caching.", + }, +} as const satisfies Record + // Anthropic // https://docs.anthropic.com/en/docs/about-claude/models export type AnthropicModelId = keyof typeof anthropicModels diff --git a/webview-ui/package-lock.json b/webview-ui/package-lock.json index ee8d460f5..d6d0de510 100644 --- a/webview-ui/package-lock.json +++ b/webview-ui/package-lock.json @@ -27,13 +27,13 @@ "react-use": "^17.5.1", "react-virtuoso": "^4.7.13", "rehype-highlight": "^7.0.0", - "rewire": "^7.0.0", "styled-components": "^6.1.13", "typescript": "^4.9.5", "web-vitals": "^2.1.4" }, "devDependencies": { - "@types/vscode-webview": "^1.57.5" + "@types/vscode-webview": "^1.57.5", + "rewire": "^7.0.0" } }, "node_modules/@adobe/css-tools": { @@ -17410,6 +17410,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/rewire/-/rewire-7.0.0.tgz", "integrity": "sha512-DyyNyzwMtGYgu0Zl/ya0PR/oaunM+VuCuBxCuhYJHHaV0V+YvYa3bBGxb5OZ71vndgmp1pYY8F4YOwQo1siRGw==", + "dev": true, "license": "MIT", "dependencies": { "eslint": "^8.47.0" diff --git a/webview-ui/package.json b/webview-ui/package.json index cc5beb639..82961d06b 100644 --- a/webview-ui/package.json +++ b/webview-ui/package.json @@ -22,7 +22,6 @@ "react-use": "^17.5.1", "react-virtuoso": "^4.7.13", "rehype-highlight": "^7.0.0", - "rewire": "^7.0.0", "styled-components": "^6.1.13", "typescript": "^4.9.5", "web-vitals": "^2.1.4" @@ -52,6 +51,7 @@ ] }, "devDependencies": { - "@types/vscode-webview": "^1.57.5" + "@types/vscode-webview": "^1.57.5", + "rewire": "^7.0.0" } } diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index e77c83b50..0a4c49279 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -17,6 +17,8 @@ import { azureOpenAiDefaultApiVersion, bedrockDefaultModelId, bedrockModels, + deepSeekDefaultModelId, + deepSeekModels, geminiDefaultModelId, geminiModels, openAiModelInfoSaneDefaults, @@ -128,6 +130,7 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage }: style={{ minWidth: 130, position: "relative", zIndex: OPENROUTER_MODEL_PICKER_Z_INDEX + 1 }}> OpenRouter Anthropic + DeepSeek Google Gemini GCP Vertex AI AWS Bedrock @@ -189,6 +192,46 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage }: )} + {selectedProvider === "deepseek" && ( +
+ + DeepSeek API Key + + + { + const isChecked = e.target.checked === true + setApiConfiguration({ + ...apiConfiguration, + deepSeekBaseUrl: isChecked ? "https://api.deepseek.com/beta" : "https://api.deepseek.com", + }) + }}> + Use beta endpoint (8K token limit) + + +

+ This key is stored locally and only used to make API requests from this extension. + {!apiConfiguration?.deepSeekApiKey && ( + + You can get a DeepSeek API key by signing up here. + + )} +

+
+ )} + {selectedProvider === "openai-native" && (