Skip to content

Commit

Permalink
Add DeepSeek Provider
Browse files Browse the repository at this point in the history
  • Loading branch information
Rady committed Dec 31, 2024
1 parent e0dcd21 commit 72ef176
Show file tree
Hide file tree
Showing 11 changed files with 129 additions and 14 deletions.
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
```

<p align="center">
<img src="assets/docs/DeepSeek-Provider.png" alt="DeepSeek Provider" width="100%" />
</p>

--------------------

<p align="center">
<img src="https://media.githubusercontent.com/media/cline/cline/main/assets/docs/demo.gif" width="100%" />
</p>
Expand Down
16 changes: 8 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
3 changes: 3 additions & 0 deletions src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
}
Expand Down
13 changes: 13 additions & 0 deletions src/core/webview/ClineProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type SecretKey =
| "openAiApiKey"
| "geminiApiKey"
| "openAiNativeApiKey"
| "deepSeekApiKey"
type GlobalStateKey =
| "apiProvider"
| "apiModelId"
Expand All @@ -59,6 +60,7 @@ type GlobalStateKey =
| "azureApiVersion"
| "openRouterModelId"
| "openRouterModelInfo"
| "deepSeekBaseUrl"
| "autoApprovalSettings"

export const GlobalFileNames = {
Expand Down Expand Up @@ -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)
Expand All @@ -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)
}
Expand Down Expand Up @@ -922,6 +928,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
lastShownAnnouncementId,
customInstructions,
taskHistory,
deepSeekApiKey,
deepSeekBaseUrl,
autoApprovalSettings,
] = await Promise.all([
this.getGlobalState("apiProvider") as Promise<ApiProvider | undefined>,
Expand Down Expand Up @@ -951,6 +959,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
this.getGlobalState("lastShownAnnouncementId") as Promise<string | undefined>,
this.getGlobalState("customInstructions") as Promise<string | undefined>,
this.getGlobalState("taskHistory") as Promise<HistoryItem[] | undefined>,
this.getSecret("deepSeekApiKey") as Promise<string | undefined>,
this.getGlobalState("deepSeekBaseUrl") as Promise<string | undefined>,
this.getGlobalState("autoApprovalSettings") as Promise<AutoApprovalSettings | undefined>,
])

Expand Down Expand Up @@ -994,6 +1004,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
azureApiVersion,
openRouterModelId,
openRouterModelInfo,
deepSeekApiKey,
deepSeekBaseUrl,
},
lastShownAnnouncementId,
customInstructions,
Expand Down Expand Up @@ -1074,6 +1086,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
"openAiApiKey",
"geminiApiKey",
"openAiNativeApiKey",
"deepSeekApiKey",
]
for (const key of secretKeys) {
await this.storeSecret(key, undefined)
Expand Down
21 changes: 21 additions & 0 deletions src/shared/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export type ApiProvider =
| "lmstudio"
| "gemini"
| "openai-native"
| "deepseek"

export interface ApiHandlerOptions {
apiModelId?: string
Expand All @@ -33,6 +34,8 @@ export interface ApiHandlerOptions {
geminiApiKey?: string
openAiNativeApiKey?: string
azureApiVersion?: string
deepSeekApiKey?: string
deepSeekBaseUrl?: string
}

export type ApiConfiguration = ApiHandlerOptions & {
Expand All @@ -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<string, ModelInfo>

// Anthropic
// https://docs.anthropic.com/en/docs/about-claude/models
export type AnthropicModelId = keyof typeof anthropicModels
Expand Down
5 changes: 3 additions & 2 deletions webview-ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions webview-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -52,6 +51,7 @@
]
},
"devDependencies": {
"@types/vscode-webview": "^1.57.5"
"@types/vscode-webview": "^1.57.5",
"rewire": "^7.0.0"
}
}
46 changes: 46 additions & 0 deletions webview-ui/src/components/settings/ApiOptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import {
azureOpenAiDefaultApiVersion,
bedrockDefaultModelId,
bedrockModels,
deepSeekDefaultModelId,
deepSeekModels,
geminiDefaultModelId,
geminiModels,
openAiModelInfoSaneDefaults,
Expand Down Expand Up @@ -128,6 +130,7 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage }:
style={{ minWidth: 130, position: "relative", zIndex: OPENROUTER_MODEL_PICKER_Z_INDEX + 1 }}>
<VSCodeOption value="openrouter">OpenRouter</VSCodeOption>
<VSCodeOption value="anthropic">Anthropic</VSCodeOption>
<VSCodeOption value="deepseek">DeepSeek</VSCodeOption>
<VSCodeOption value="gemini">Google Gemini</VSCodeOption>
<VSCodeOption value="vertex">GCP Vertex AI</VSCodeOption>
<VSCodeOption value="bedrock">AWS Bedrock</VSCodeOption>
Expand Down Expand Up @@ -189,6 +192,46 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage }:
</div>
)}

{selectedProvider === "deepseek" && (
<div>
<VSCodeTextField
value={apiConfiguration?.deepSeekApiKey || ""}
style={{ width: "100%" }}
type="password"
onInput={handleInputChange("deepSeekApiKey")}
placeholder="Enter API Key...">
<span style={{ fontWeight: 500 }}>DeepSeek API Key</span>
</VSCodeTextField>

<VSCodeCheckbox
checked={apiConfiguration?.deepSeekBaseUrl?.includes("/beta") || false}
onChange={(e: any) => {
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)
</VSCodeCheckbox>

<p style={{
fontSize: "12px",
marginTop: 3,
color: "var(--vscode-descriptionForeground)",
}}>
This key is stored locally and only used to make API requests from this extension.
{!apiConfiguration?.deepSeekApiKey && (
<VSCodeLink
href="https://platform.deepseek.com"
style={{ display: "inline", fontSize: "inherit" }}>
You can get a DeepSeek API key by signing up here.
</VSCodeLink>
)}
</p>
</div>
)}

{selectedProvider === "openai-native" && (
<div>
<VSCodeTextField
Expand Down Expand Up @@ -632,6 +675,7 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage }:
{selectedProvider === "vertex" && createDropdown(vertexModels)}
{selectedProvider === "gemini" && createDropdown(geminiModels)}
{selectedProvider === "openai-native" && createDropdown(openAiNativeModels)}
{selectedProvider === "deepseek" && createDropdown(deepSeekModels)}
</div>

<ModelInfoView
Expand Down Expand Up @@ -818,6 +862,8 @@ export function normalizeApiConfiguration(apiConfiguration?: ApiConfiguration) {
return getProviderData(geminiModels, geminiDefaultModelId)
case "openai-native":
return getProviderData(openAiNativeModels, openAiNativeDefaultModelId)
case "deepseek":
return getProviderData(deepSeekModels, deepSeekDefaultModelId)
case "openrouter":
return {
selectedProvider: provider,
Expand Down
2 changes: 1 addition & 1 deletion webview-ui/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import ReactDOM from "react-dom/client"
import "./index.css"
import App from "./App"
import reportWebVitals from "./reportWebVitals"
import "../../node_modules/@vscode/codicons/dist/codicon.css"
import "./codicon.css"

const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement)
root.render(
Expand Down
5 changes: 5 additions & 0 deletions webview-ui/src/utils/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ export function validateApiConfiguration(apiConfiguration?: ApiConfiguration): s
return "You must provide a valid model ID."
}
break
case "deepseek":
if (!apiConfiguration.deepSeekApiKey) {
return "You must provide a valid API key or choose a different provider."
}
break
}
}
return undefined
Expand Down

0 comments on commit 72ef176

Please sign in to comment.