mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-24 00:11:31 +00:00
feat: add deepseek provider
This commit is contained in:
4
.github/labeler.yml
vendored
4
.github/labeler.yml
vendored
@@ -234,6 +234,10 @@
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/byteplus/**"
|
||||
"extensions: deepseek":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "extensions/deepseek/**"
|
||||
"extensions: anthropic":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
|
||||
@@ -1106,6 +1106,7 @@
|
||||
"providers/cloudflare-ai-gateway",
|
||||
"providers/claude-max-api-proxy",
|
||||
"providers/deepgram",
|
||||
"providers/deepseek",
|
||||
"providers/github-copilot",
|
||||
"providers/huggingface",
|
||||
"providers/kilocode",
|
||||
|
||||
63
docs/providers/deepseek.md
Normal file
63
docs/providers/deepseek.md
Normal file
@@ -0,0 +1,63 @@
|
||||
---
|
||||
summary: "DeepSeek setup (auth + model selection)"
|
||||
read_when:
|
||||
- You want to use DeepSeek with OpenClaw
|
||||
- You need the API key env var or CLI auth choice
|
||||
---
|
||||
|
||||
# DeepSeek
|
||||
|
||||
[DeepSeek](https://www.deepseek.com) provides powerful AI models with an OpenAI-compatible API.
|
||||
|
||||
- Provider: `deepseek`
|
||||
- Auth: `DEEPSEEK_API_KEY`
|
||||
- API: OpenAI-compatible
|
||||
|
||||
## Quick start
|
||||
|
||||
1. Set the API key (recommended: store it for the Gateway):
|
||||
|
||||
```bash
|
||||
openclaw setup --wizard --auth-choice deepseek-api-key
|
||||
```
|
||||
|
||||
2. Set a default model:
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
model: { primary: "deepseek/deepseek-chat" },
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## Non-interactive example
|
||||
|
||||
```bash
|
||||
openclaw setup --wizard --non-interactive \
|
||||
--mode local \
|
||||
--auth-choice deepseek-api-key \
|
||||
--deepseek-api-key "$DEEPSEEK_API_KEY"
|
||||
```
|
||||
|
||||
This will set `deepseek/deepseek-chat` as the default model.
|
||||
|
||||
## Environment note
|
||||
|
||||
If the Gateway runs as a daemon (launchd/systemd), make sure `DEEPSEEK_API_KEY`
|
||||
is available to that process (for example, in `~/.openclaw/.env` or via
|
||||
`env.shellEnv`).
|
||||
|
||||
## Available models
|
||||
|
||||
| Model ID | Name | Type | Context |
|
||||
| ------------------- | ------------------------ | --------- | ------- |
|
||||
| `deepseek-chat` | DeepSeek Chat (V3.2) | General | 128K |
|
||||
| `deepseek-reasoner` | DeepSeek Reasoner (V3.2) | Reasoning | 128K |
|
||||
|
||||
- **deepseek-chat** corresponds to DeepSeek-V3.2 in non-thinking mode.
|
||||
- **deepseek-reasoner** corresponds to DeepSeek-V3.2 in thinking mode with chain-of-thought reasoning.
|
||||
|
||||
Get your API key at [platform.deepseek.com](https://platform.deepseek.com/api_keys).
|
||||
55
extensions/deepseek/index.ts
Normal file
55
extensions/deepseek/index.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { emptyPluginConfigSchema, type OpenClawPluginApi } from "openclaw/plugin-sdk/core";
|
||||
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth";
|
||||
import { buildSingleProviderApiKeyCatalog } from "openclaw/plugin-sdk/provider-catalog";
|
||||
import { applyDeepSeekConfig, DEEPSEEK_DEFAULT_MODEL_REF } from "./onboard.js";
|
||||
import { buildDeepSeekProvider } from "./provider-catalog.js";
|
||||
|
||||
const PROVIDER_ID = "deepseek";
|
||||
|
||||
const deepseekPlugin = {
|
||||
id: PROVIDER_ID,
|
||||
name: "DeepSeek Provider",
|
||||
description: "Bundled DeepSeek provider plugin",
|
||||
configSchema: emptyPluginConfigSchema(),
|
||||
register(api: OpenClawPluginApi) {
|
||||
api.registerProvider({
|
||||
id: PROVIDER_ID,
|
||||
label: "DeepSeek",
|
||||
docsPath: "/providers/deepseek",
|
||||
envVars: ["DEEPSEEK_API_KEY"],
|
||||
auth: [
|
||||
createProviderApiKeyAuthMethod({
|
||||
providerId: PROVIDER_ID,
|
||||
methodId: "api-key",
|
||||
label: "DeepSeek API key",
|
||||
hint: "API key",
|
||||
optionKey: "deepseekApiKey",
|
||||
flagName: "--deepseek-api-key",
|
||||
envVar: "DEEPSEEK_API_KEY",
|
||||
promptMessage: "Enter DeepSeek API key",
|
||||
defaultModel: DEEPSEEK_DEFAULT_MODEL_REF,
|
||||
expectedProviders: ["deepseek"],
|
||||
applyConfig: (cfg) => applyDeepSeekConfig(cfg),
|
||||
wizard: {
|
||||
choiceId: "deepseek-api-key",
|
||||
choiceLabel: "DeepSeek API key",
|
||||
groupId: "deepseek",
|
||||
groupLabel: "DeepSeek",
|
||||
groupHint: "API key",
|
||||
},
|
||||
}),
|
||||
],
|
||||
catalog: {
|
||||
order: "simple",
|
||||
run: (ctx) =>
|
||||
buildSingleProviderApiKeyCatalog({
|
||||
ctx,
|
||||
providerId: PROVIDER_ID,
|
||||
buildProvider: buildDeepSeekProvider,
|
||||
}),
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default deepseekPlugin;
|
||||
35
extensions/deepseek/onboard.ts
Normal file
35
extensions/deepseek/onboard.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import {
|
||||
buildDeepSeekModelDefinition,
|
||||
DEEPSEEK_BASE_URL,
|
||||
DEEPSEEK_MODEL_CATALOG,
|
||||
} from "openclaw/plugin-sdk/provider-models";
|
||||
import {
|
||||
applyAgentDefaultModelPrimary,
|
||||
applyProviderConfigWithModelCatalog,
|
||||
type OpenClawConfig,
|
||||
} from "openclaw/plugin-sdk/provider-onboard";
|
||||
|
||||
export const DEEPSEEK_DEFAULT_MODEL_REF = "deepseek/deepseek-chat";
|
||||
|
||||
export function applyDeepSeekProviderConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
const models = { ...cfg.agents?.defaults?.models };
|
||||
models[DEEPSEEK_DEFAULT_MODEL_REF] = {
|
||||
...models[DEEPSEEK_DEFAULT_MODEL_REF],
|
||||
alias: models[DEEPSEEK_DEFAULT_MODEL_REF]?.alias ?? "DeepSeek",
|
||||
};
|
||||
|
||||
return applyProviderConfigWithModelCatalog(cfg, {
|
||||
agentModels: models,
|
||||
providerId: "deepseek",
|
||||
api: "openai-completions",
|
||||
baseUrl: DEEPSEEK_BASE_URL,
|
||||
catalogModels: DEEPSEEK_MODEL_CATALOG.map(buildDeepSeekModelDefinition),
|
||||
});
|
||||
}
|
||||
|
||||
export function applyDeepSeekConfig(cfg: OpenClawConfig): OpenClawConfig {
|
||||
return applyAgentDefaultModelPrimary(
|
||||
applyDeepSeekProviderConfig(cfg),
|
||||
DEEPSEEK_DEFAULT_MODEL_REF,
|
||||
);
|
||||
}
|
||||
27
extensions/deepseek/openclaw.plugin.json
Normal file
27
extensions/deepseek/openclaw.plugin.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"id": "deepseek",
|
||||
"providers": ["deepseek"],
|
||||
"providerAuthEnvVars": {
|
||||
"deepseek": ["DEEPSEEK_API_KEY"]
|
||||
},
|
||||
"providerAuthChoices": [
|
||||
{
|
||||
"provider": "deepseek",
|
||||
"method": "api-key",
|
||||
"choiceId": "deepseek-api-key",
|
||||
"choiceLabel": "DeepSeek API key",
|
||||
"groupId": "deepseek",
|
||||
"groupLabel": "DeepSeek",
|
||||
"groupHint": "API key",
|
||||
"optionKey": "deepseekApiKey",
|
||||
"cliFlag": "--deepseek-api-key",
|
||||
"cliOption": "--deepseek-api-key <key>",
|
||||
"cliDescription": "DeepSeek API key"
|
||||
}
|
||||
],
|
||||
"configSchema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {}
|
||||
}
|
||||
}
|
||||
12
extensions/deepseek/package.json
Normal file
12
extensions/deepseek/package.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "@openclaw/deepseek-provider",
|
||||
"version": "2026.3.14",
|
||||
"private": true,
|
||||
"description": "OpenClaw DeepSeek provider plugin",
|
||||
"type": "module",
|
||||
"openclaw": {
|
||||
"extensions": [
|
||||
"./index.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
14
extensions/deepseek/provider-catalog.ts
Normal file
14
extensions/deepseek/provider-catalog.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import {
|
||||
buildDeepSeekModelDefinition,
|
||||
DEEPSEEK_BASE_URL,
|
||||
DEEPSEEK_MODEL_CATALOG,
|
||||
type ModelProviderConfig,
|
||||
} from "openclaw/plugin-sdk/provider-models";
|
||||
|
||||
export function buildDeepSeekProvider(): ModelProviderConfig {
|
||||
return {
|
||||
baseUrl: DEEPSEEK_BASE_URL,
|
||||
api: "openai-completions",
|
||||
models: DEEPSEEK_MODEL_CATALOG.map(buildDeepSeekModelDefinition),
|
||||
};
|
||||
}
|
||||
46
src/agents/deepseek-models.ts
Normal file
46
src/agents/deepseek-models.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import type { ModelDefinitionConfig } from "../config/types.models.js";
|
||||
|
||||
export const DEEPSEEK_BASE_URL = "https://api.deepseek.com";
|
||||
|
||||
const DEEPSEEK_DEFAULT_COST = {
|
||||
input: 0,
|
||||
output: 0,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
};
|
||||
|
||||
export const DEEPSEEK_MODEL_CATALOG: ModelDefinitionConfig[] = [
|
||||
{
|
||||
id: "deepseek-chat",
|
||||
name: "DeepSeek Chat",
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
contextWindow: 131072,
|
||||
maxTokens: 8192,
|
||||
cost: DEEPSEEK_DEFAULT_COST,
|
||||
},
|
||||
{
|
||||
id: "deepseek-reasoner",
|
||||
name: "DeepSeek Reasoner",
|
||||
reasoning: true,
|
||||
input: ["text"],
|
||||
contextWindow: 131072,
|
||||
maxTokens: 8192,
|
||||
cost: DEEPSEEK_DEFAULT_COST,
|
||||
},
|
||||
];
|
||||
|
||||
export function buildDeepSeekModelDefinition(
|
||||
model: (typeof DEEPSEEK_MODEL_CATALOG)[number],
|
||||
): ModelDefinitionConfig {
|
||||
return {
|
||||
id: model.id,
|
||||
name: model.name,
|
||||
api: "openai-completions",
|
||||
reasoning: model.reasoning,
|
||||
input: model.input,
|
||||
cost: model.cost,
|
||||
contextWindow: model.contextWindow,
|
||||
maxTokens: model.maxTokens,
|
||||
};
|
||||
}
|
||||
27
src/agents/models-config.providers.deepseek.test.ts
Normal file
27
src/agents/models-config.providers.deepseek.test.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { buildDeepSeekProvider } from "./models-config.providers.static.js";
|
||||
|
||||
describe("DeepSeek provider", () => {
|
||||
it("should build deepseek provider with correct configuration", () => {
|
||||
const provider = buildDeepSeekProvider();
|
||||
expect(provider.baseUrl).toBe("https://api.deepseek.com");
|
||||
expect(provider.api).toBe("openai-completions");
|
||||
expect(provider.models).toBeDefined();
|
||||
expect(provider.models.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("should include both deepseek-chat and deepseek-reasoner models", () => {
|
||||
const provider = buildDeepSeekProvider();
|
||||
const modelIds = provider.models.map((m) => m.id);
|
||||
expect(modelIds).toContain("deepseek-chat");
|
||||
expect(modelIds).toContain("deepseek-reasoner");
|
||||
});
|
||||
|
||||
it("should mark deepseek-reasoner as a reasoning model", () => {
|
||||
const provider = buildDeepSeekProvider();
|
||||
const reasoner = provider.models.find((m) => m.id === "deepseek-reasoner");
|
||||
const chat = provider.models.find((m) => m.id === "deepseek-chat");
|
||||
expect(reasoner?.reasoning).toBe(true);
|
||||
expect(chat?.reasoning).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -2,6 +2,7 @@ export {
|
||||
buildBytePlusCodingProvider,
|
||||
buildBytePlusProvider,
|
||||
} from "../../extensions/byteplus/provider-catalog.js";
|
||||
export { buildDeepSeekProvider } from "../../extensions/deepseek/provider-catalog.js";
|
||||
export { buildKimiCodingProvider } from "../../extensions/kimi-coding/provider-catalog.js";
|
||||
export { buildKilocodeProvider } from "../../extensions/kilocode/provider-catalog.js";
|
||||
export {
|
||||
|
||||
@@ -10,6 +10,7 @@ export type BuiltInAuthChoice =
|
||||
| "claude-cli"
|
||||
| "token"
|
||||
| "chutes"
|
||||
| "deepseek-api-key"
|
||||
| "openai-codex"
|
||||
| "openai-api-key"
|
||||
| "openrouter-api-key"
|
||||
@@ -58,6 +59,7 @@ export type BuiltInAuthChoiceGroupId =
|
||||
| "openai"
|
||||
| "anthropic"
|
||||
| "chutes"
|
||||
| "deepseek"
|
||||
| "google"
|
||||
| "copilot"
|
||||
| "openrouter"
|
||||
@@ -115,6 +117,7 @@ export type OnboardOptions = {
|
||||
/** API key persistence mode for setup flows (default: plaintext). */
|
||||
secretInputMode?: SecretInputMode;
|
||||
anthropicApiKey?: string;
|
||||
deepseekApiKey?: string;
|
||||
openaiApiKey?: string;
|
||||
mistralApiKey?: string;
|
||||
openrouterApiKey?: string;
|
||||
|
||||
@@ -41,6 +41,11 @@ export {
|
||||
SYNTHETIC_DEFAULT_MODEL_REF,
|
||||
SYNTHETIC_MODEL_CATALOG,
|
||||
} from "../agents/synthetic-models.js";
|
||||
export {
|
||||
buildDeepSeekModelDefinition,
|
||||
DEEPSEEK_BASE_URL,
|
||||
DEEPSEEK_MODEL_CATALOG,
|
||||
} from "../agents/deepseek-models.js";
|
||||
export {
|
||||
buildTogetherModelDefinition,
|
||||
TOGETHER_BASE_URL,
|
||||
|
||||
@@ -2,6 +2,7 @@ import ANTHROPIC_MANIFEST from "../../extensions/anthropic/openclaw.plugin.json"
|
||||
import BYTEPLUS_MANIFEST from "../../extensions/byteplus/openclaw.plugin.json" with { type: "json" };
|
||||
import CLOUDFLARE_AI_GATEWAY_MANIFEST from "../../extensions/cloudflare-ai-gateway/openclaw.plugin.json" with { type: "json" };
|
||||
import COPILOT_PROXY_MANIFEST from "../../extensions/copilot-proxy/openclaw.plugin.json" with { type: "json" };
|
||||
import DEEPSEEK_MANIFEST from "../../extensions/deepseek/openclaw.plugin.json" with { type: "json" };
|
||||
import GITHUB_COPILOT_MANIFEST from "../../extensions/github-copilot/openclaw.plugin.json" with { type: "json" };
|
||||
import GOOGLE_MANIFEST from "../../extensions/google/openclaw.plugin.json" with { type: "json" };
|
||||
import HUGGINGFACE_MANIFEST from "../../extensions/huggingface/openclaw.plugin.json" with { type: "json" };
|
||||
@@ -63,6 +64,7 @@ export const BUNDLED_PROVIDER_AUTH_ENV_VAR_CANDIDATES = collectBundledProviderAu
|
||||
BYTEPLUS_MANIFEST,
|
||||
CLOUDFLARE_AI_GATEWAY_MANIFEST,
|
||||
COPILOT_PROXY_MANIFEST,
|
||||
DEEPSEEK_MANIFEST,
|
||||
GITHUB_COPILOT_MANIFEST,
|
||||
GOOGLE_MANIFEST,
|
||||
HUGGINGFACE_MANIFEST,
|
||||
|
||||
Reference in New Issue
Block a user