Merge ffddff40ab into da71eaebd2
This commit is contained in:
commit
179df9284c
64
docs/providers/deepseek.md
Normal file
64
docs/providers/deepseek.md
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
---
|
||||||
|
summary: "Configure DeepSeek API (Chat + Reasoner models)"
|
||||||
|
read_when:
|
||||||
|
- You want to use DeepSeek models
|
||||||
|
- You need to configure DeepSeek API key
|
||||||
|
- You want cost-effective reasoning models
|
||||||
|
---
|
||||||
|
|
||||||
|
# DeepSeek
|
||||||
|
|
||||||
|
DeepSeek provides cost-effective AI models with OpenAI-compatible endpoints. Moltbot supports both the Chat and Reasoner models.
|
||||||
|
|
||||||
|
## Models
|
||||||
|
|
||||||
|
| Model ID | Name | Reasoning | Context |
|
||||||
|
|----------|------|-----------|---------|
|
||||||
|
| `deepseek-chat` | DeepSeek Chat | No | 64K |
|
||||||
|
| `deepseek-reasoner` | DeepSeek Reasoner | Yes | 64K |
|
||||||
|
|
||||||
|
## Quick start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
moltbot onboard --auth-choice deepseek-api-key
|
||||||
|
```
|
||||||
|
|
||||||
|
Or set the environment variable:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export DEEPSEEK_API_KEY="sk-..."
|
||||||
|
moltbot models set deepseek/deepseek-chat
|
||||||
|
```
|
||||||
|
|
||||||
|
## Config snippet
|
||||||
|
|
||||||
|
```json5
|
||||||
|
{
|
||||||
|
env: { DEEPSEEK_API_KEY: "sk-..." },
|
||||||
|
agents: {
|
||||||
|
defaults: {
|
||||||
|
model: { primary: "deepseek/deepseek-chat" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using the Reasoner model
|
||||||
|
|
||||||
|
For tasks requiring chain-of-thought reasoning:
|
||||||
|
|
||||||
|
```json5
|
||||||
|
{
|
||||||
|
agents: {
|
||||||
|
defaults: {
|
||||||
|
model: { primary: "deepseek/deepseek-reasoner" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- DeepSeek uses an OpenAI-compatible API at `https://api.deepseek.com`
|
||||||
|
- Get your API key from [platform.deepseek.com](https://platform.deepseek.com/)
|
||||||
|
- Pricing is very competitive compared to other providers
|
||||||
@ -39,6 +39,7 @@ See [Venice AI](/providers/venice).
|
|||||||
- [OpenRouter](/providers/openrouter)
|
- [OpenRouter](/providers/openrouter)
|
||||||
- [Vercel AI Gateway](/providers/vercel-ai-gateway)
|
- [Vercel AI Gateway](/providers/vercel-ai-gateway)
|
||||||
- [Moonshot AI (Kimi + Kimi Code)](/providers/moonshot)
|
- [Moonshot AI (Kimi + Kimi Code)](/providers/moonshot)
|
||||||
|
- [DeepSeek (Chat + Reasoner)](/providers/deepseek)
|
||||||
- [OpenCode Zen](/providers/opencode)
|
- [OpenCode Zen](/providers/opencode)
|
||||||
- [Amazon Bedrock](/bedrock)
|
- [Amazon Bedrock](/bedrock)
|
||||||
- [Z.AI](/providers/zai)
|
- [Z.AI](/providers/zai)
|
||||||
|
|||||||
@ -286,6 +286,7 @@ export function resolveEnvApiKey(provider: string): EnvApiKeyResult | null {
|
|||||||
venice: "VENICE_API_KEY",
|
venice: "VENICE_API_KEY",
|
||||||
mistral: "MISTRAL_API_KEY",
|
mistral: "MISTRAL_API_KEY",
|
||||||
opencode: "OPENCODE_API_KEY",
|
opencode: "OPENCODE_API_KEY",
|
||||||
|
deepseek: "DEEPSEEK_API_KEY",
|
||||||
};
|
};
|
||||||
const envVar = envMap[normalized];
|
const envVar = envMap[normalized];
|
||||||
if (!envVar) return null;
|
if (!envVar) return null;
|
||||||
|
|||||||
85
src/agents/models-config.providers.deepseek.test.ts
Normal file
85
src/agents/models-config.providers.deepseek.test.ts
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import { mkdtempSync } from "node:fs";
|
||||||
|
import { join } from "node:path";
|
||||||
|
import { tmpdir } from "node:os";
|
||||||
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import {
|
||||||
|
buildDeepSeekProvider,
|
||||||
|
DEEPSEEK_API_BASE_URL,
|
||||||
|
DEEPSEEK_CHAT_MODEL_ID,
|
||||||
|
DEEPSEEK_REASONER_MODEL_ID,
|
||||||
|
resolveImplicitProviders,
|
||||||
|
} from "./models-config.providers.js";
|
||||||
|
|
||||||
|
describe("DeepSeek provider", () => {
|
||||||
|
describe("buildDeepSeekProvider", () => {
|
||||||
|
it("returns correct provider configuration", () => {
|
||||||
|
const provider = buildDeepSeekProvider();
|
||||||
|
|
||||||
|
expect(provider.baseUrl).toBe(DEEPSEEK_API_BASE_URL);
|
||||||
|
expect(provider.api).toBe("openai-completions");
|
||||||
|
expect(provider.models).toHaveLength(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("includes deepseek-chat model with correct config", () => {
|
||||||
|
const provider = buildDeepSeekProvider();
|
||||||
|
const chatModel = provider.models.find((m) => m.id === DEEPSEEK_CHAT_MODEL_ID);
|
||||||
|
|
||||||
|
expect(chatModel).toBeDefined();
|
||||||
|
expect(chatModel?.name).toBe("DeepSeek Chat");
|
||||||
|
expect(chatModel?.reasoning).toBe(false);
|
||||||
|
expect(chatModel?.input).toEqual(["text"]);
|
||||||
|
expect(chatModel?.contextWindow).toBe(64000);
|
||||||
|
expect(chatModel?.maxTokens).toBe(8192);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("includes deepseek-reasoner model with reasoning enabled", () => {
|
||||||
|
const provider = buildDeepSeekProvider();
|
||||||
|
const reasonerModel = provider.models.find((m) => m.id === DEEPSEEK_REASONER_MODEL_ID);
|
||||||
|
|
||||||
|
expect(reasonerModel).toBeDefined();
|
||||||
|
expect(reasonerModel?.name).toBe("DeepSeek Reasoner");
|
||||||
|
expect(reasonerModel?.reasoning).toBe(true);
|
||||||
|
expect(reasonerModel?.input).toEqual(["text"]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("resolveImplicitProviders", () => {
|
||||||
|
let previousKey: string | undefined;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
previousKey = process.env.DEEPSEEK_API_KEY;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
if (previousKey === undefined) {
|
||||||
|
delete process.env.DEEPSEEK_API_KEY;
|
||||||
|
} else {
|
||||||
|
process.env.DEEPSEEK_API_KEY = previousKey;
|
||||||
|
}
|
||||||
|
vi.resetModules();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not include deepseek when no API key is configured", async () => {
|
||||||
|
delete process.env.DEEPSEEK_API_KEY;
|
||||||
|
const agentDir = mkdtempSync(join(tmpdir(), "clawd-test-deepseek-"));
|
||||||
|
const providers = await resolveImplicitProviders({ agentDir });
|
||||||
|
|
||||||
|
expect(providers?.deepseek).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("includes deepseek when DEEPSEEK_API_KEY env var is set", async () => {
|
||||||
|
process.env.DEEPSEEK_API_KEY = "sk-test-deepseek-key";
|
||||||
|
const agentDir = mkdtempSync(join(tmpdir(), "clawd-test-deepseek-"));
|
||||||
|
|
||||||
|
vi.resetModules();
|
||||||
|
const { resolveImplicitProviders: freshResolve } =
|
||||||
|
await import("./models-config.providers.js");
|
||||||
|
const providers = await freshResolve({ agentDir });
|
||||||
|
|
||||||
|
expect(providers?.deepseek).toBeDefined();
|
||||||
|
expect(providers?.deepseek?.baseUrl).toBe(DEEPSEEK_API_BASE_URL);
|
||||||
|
expect(providers?.deepseek?.apiKey).toBe("DEEPSEEK_API_KEY");
|
||||||
|
expect(providers?.deepseek?.models).toHaveLength(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -86,6 +86,18 @@ const OLLAMA_DEFAULT_COST = {
|
|||||||
cacheWrite: 0,
|
cacheWrite: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const DEEPSEEK_API_BASE_URL = "https://api.deepseek.com";
|
||||||
|
export const DEEPSEEK_CHAT_MODEL_ID = "deepseek-chat";
|
||||||
|
export const DEEPSEEK_REASONER_MODEL_ID = "deepseek-reasoner";
|
||||||
|
const DEEPSEEK_DEFAULT_CONTEXT_WINDOW = 64000;
|
||||||
|
const DEEPSEEK_DEFAULT_MAX_TOKENS = 8192;
|
||||||
|
const DEEPSEEK_DEFAULT_COST = {
|
||||||
|
input: 0,
|
||||||
|
output: 0,
|
||||||
|
cacheRead: 0,
|
||||||
|
cacheWrite: 0,
|
||||||
|
};
|
||||||
|
|
||||||
interface OllamaModel {
|
interface OllamaModel {
|
||||||
name: string;
|
name: string;
|
||||||
modified_at: string;
|
modified_at: string;
|
||||||
@ -388,6 +400,33 @@ async function buildOllamaProvider(): Promise<ProviderConfig> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function buildDeepSeekProvider(): ProviderConfig {
|
||||||
|
return {
|
||||||
|
baseUrl: DEEPSEEK_API_BASE_URL,
|
||||||
|
api: "openai-completions",
|
||||||
|
models: [
|
||||||
|
{
|
||||||
|
id: DEEPSEEK_CHAT_MODEL_ID,
|
||||||
|
name: "DeepSeek Chat",
|
||||||
|
reasoning: false,
|
||||||
|
input: ["text"],
|
||||||
|
cost: DEEPSEEK_DEFAULT_COST,
|
||||||
|
contextWindow: DEEPSEEK_DEFAULT_CONTEXT_WINDOW,
|
||||||
|
maxTokens: DEEPSEEK_DEFAULT_MAX_TOKENS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: DEEPSEEK_REASONER_MODEL_ID,
|
||||||
|
name: "DeepSeek Reasoner",
|
||||||
|
reasoning: true,
|
||||||
|
input: ["text"],
|
||||||
|
cost: DEEPSEEK_DEFAULT_COST,
|
||||||
|
contextWindow: DEEPSEEK_DEFAULT_CONTEXT_WINDOW,
|
||||||
|
maxTokens: DEEPSEEK_DEFAULT_MAX_TOKENS,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export async function resolveImplicitProviders(params: {
|
export async function resolveImplicitProviders(params: {
|
||||||
agentDir: string;
|
agentDir: string;
|
||||||
}): Promise<ModelsConfig["providers"]> {
|
}): Promise<ModelsConfig["providers"]> {
|
||||||
@ -454,6 +493,13 @@ export async function resolveImplicitProviders(params: {
|
|||||||
providers.ollama = { ...(await buildOllamaProvider()), apiKey: ollamaKey };
|
providers.ollama = { ...(await buildOllamaProvider()), apiKey: ollamaKey };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const deepseekKey =
|
||||||
|
resolveEnvApiKeyVarName("deepseek") ??
|
||||||
|
resolveApiKeyFromProfiles({ provider: "deepseek", store: authStore });
|
||||||
|
if (deepseekKey) {
|
||||||
|
providers.deepseek = { ...buildDeepSeekProvider(), apiKey: deepseekKey };
|
||||||
|
}
|
||||||
|
|
||||||
return providers;
|
return providers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user