mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-12 05:30:48 +08:00
重构主控编排与运行时预热链路,统一工作区提示词/专家调度协议并补齐 wiki 记忆注入与写回闭环。
同时收敛启动与运维脚本默认行为(含 wiki worker)、更新 Admin 可观测性与相关测试,降低首轮时延并提高运行稳定性。 Made-with: Cursor
This commit is contained in:
parent
4a23b715a2
commit
dbbe3add6a
14438 changed files with 2693620 additions and 2546 deletions
6
openclaw/extensions/deepseek/api.ts
Normal file
6
openclaw/extensions/deepseek/api.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
export {
|
||||
buildDeepSeekModelDefinition,
|
||||
DEEPSEEK_BASE_URL,
|
||||
DEEPSEEK_MODEL_CATALOG,
|
||||
} from "./models.js";
|
||||
export { buildDeepSeekProvider } from "./provider-catalog.js";
|
||||
88
openclaw/extensions/deepseek/index.test.ts
Normal file
88
openclaw/extensions/deepseek/index.test.ts
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { resolveProviderPluginChoice } from "../../src/plugins/provider-auth-choice.runtime.js";
|
||||
import { registerSingleProviderPlugin } from "../../test/helpers/plugins/plugin-registration.js";
|
||||
import deepseekPlugin from "./index.js";
|
||||
|
||||
describe("deepseek provider plugin", () => {
|
||||
it("registers DeepSeek with api-key auth wizard metadata", async () => {
|
||||
const provider = await registerSingleProviderPlugin(deepseekPlugin);
|
||||
const resolved = resolveProviderPluginChoice({
|
||||
providers: [provider],
|
||||
choice: "deepseek-api-key",
|
||||
});
|
||||
|
||||
expect(provider.id).toBe("deepseek");
|
||||
expect(provider.label).toBe("DeepSeek");
|
||||
expect(provider.envVars).toEqual(["DEEPSEEK_API_KEY"]);
|
||||
expect(provider.auth).toHaveLength(1);
|
||||
expect(resolved).not.toBeNull();
|
||||
expect(resolved?.provider.id).toBe("deepseek");
|
||||
expect(resolved?.method.id).toBe("api-key");
|
||||
});
|
||||
|
||||
it("builds the static DeepSeek model catalog", async () => {
|
||||
const provider = await registerSingleProviderPlugin(deepseekPlugin);
|
||||
expect(provider.catalog).toBeDefined();
|
||||
|
||||
const catalog = await provider.catalog!.run({
|
||||
config: {},
|
||||
env: {},
|
||||
resolveProviderApiKey: () => ({ apiKey: "test-key" }),
|
||||
resolveProviderAuth: () => ({
|
||||
apiKey: "test-key",
|
||||
mode: "api_key",
|
||||
source: "env",
|
||||
}),
|
||||
} as never);
|
||||
|
||||
expect(catalog && "provider" in catalog).toBe(true);
|
||||
if (!catalog || !("provider" in catalog)) {
|
||||
throw new Error("expected single-provider catalog");
|
||||
}
|
||||
|
||||
expect(catalog.provider.api).toBe("openai-completions");
|
||||
expect(catalog.provider.baseUrl).toBe("https://api.deepseek.com");
|
||||
expect(catalog.provider.models?.map((model) => model.id)).toEqual([
|
||||
"deepseek-chat",
|
||||
"deepseek-reasoner",
|
||||
]);
|
||||
expect(
|
||||
catalog.provider.models?.find((model) => model.id === "deepseek-reasoner")?.reasoning,
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("publishes configured DeepSeek models through plugin-owned catalog augmentation", async () => {
|
||||
const provider = await registerSingleProviderPlugin(deepseekPlugin);
|
||||
|
||||
expect(
|
||||
provider.augmentModelCatalog?.({
|
||||
config: {
|
||||
models: {
|
||||
providers: {
|
||||
deepseek: {
|
||||
models: [
|
||||
{
|
||||
id: "deepseek-chat",
|
||||
name: "DeepSeek Chat",
|
||||
input: ["text"],
|
||||
reasoning: false,
|
||||
contextWindow: 65536,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as never),
|
||||
).toEqual([
|
||||
{
|
||||
provider: "deepseek",
|
||||
id: "deepseek-chat",
|
||||
name: "DeepSeek Chat",
|
||||
input: ["text"],
|
||||
reasoning: false,
|
||||
contextWindow: 65536,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
46
openclaw/extensions/deepseek/index.ts
Normal file
46
openclaw/extensions/deepseek/index.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
import { readConfiguredProviderCatalogEntries } from "openclaw/plugin-sdk/provider-catalog-shared";
|
||||
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
|
||||
import { applyDeepSeekConfig, DEEPSEEK_DEFAULT_MODEL_REF } from "./onboard.js";
|
||||
import { buildDeepSeekProvider } from "./provider-catalog.js";
|
||||
|
||||
const PROVIDER_ID = "deepseek";
|
||||
|
||||
export default defineSingleProviderPluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "DeepSeek Provider",
|
||||
description: "Bundled DeepSeek provider plugin",
|
||||
provider: {
|
||||
label: "DeepSeek",
|
||||
docsPath: "/providers/deepseek",
|
||||
auth: [
|
||||
{
|
||||
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,
|
||||
applyConfig: (cfg) => applyDeepSeekConfig(cfg),
|
||||
wizard: {
|
||||
choiceId: "deepseek-api-key",
|
||||
choiceLabel: "DeepSeek API key",
|
||||
groupId: "deepseek",
|
||||
groupLabel: "DeepSeek",
|
||||
groupHint: "API key",
|
||||
},
|
||||
},
|
||||
],
|
||||
catalog: {
|
||||
buildProvider: buildDeepSeekProvider,
|
||||
},
|
||||
augmentModelCatalog: ({ config }) =>
|
||||
readConfiguredProviderCatalogEntries({
|
||||
config,
|
||||
providerId: PROVIDER_ID,
|
||||
}),
|
||||
matchesContextOverflowError: ({ errorMessage }) =>
|
||||
/\bdeepseek\b.*(?:input.*too long|context.*exceed)/i.test(errorMessage),
|
||||
},
|
||||
});
|
||||
44
openclaw/extensions/deepseek/models.ts
Normal file
44
openclaw/extensions/deepseek/models.ts
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import type { ModelDefinitionConfig } from "openclaw/plugin-sdk/provider-model-shared";
|
||||
|
||||
export const DEEPSEEK_BASE_URL = "https://api.deepseek.com";
|
||||
|
||||
// DeepSeek V3.2 API pricing (per 1M tokens)
|
||||
// https://api-docs.deepseek.com/quick_start/pricing
|
||||
const DEEPSEEK_V3_2_COST = {
|
||||
input: 0.28,
|
||||
output: 0.42,
|
||||
cacheRead: 0.028,
|
||||
cacheWrite: 0,
|
||||
};
|
||||
|
||||
export const DEEPSEEK_MODEL_CATALOG: ModelDefinitionConfig[] = [
|
||||
{
|
||||
id: "deepseek-chat",
|
||||
name: "DeepSeek Chat",
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
contextWindow: 131072,
|
||||
maxTokens: 8192,
|
||||
cost: DEEPSEEK_V3_2_COST,
|
||||
compat: { supportsUsageInStreaming: true },
|
||||
},
|
||||
{
|
||||
id: "deepseek-reasoner",
|
||||
name: "DeepSeek Reasoner",
|
||||
reasoning: true,
|
||||
input: ["text"],
|
||||
contextWindow: 131072,
|
||||
maxTokens: 65536,
|
||||
cost: DEEPSEEK_V3_2_COST,
|
||||
compat: { supportsUsageInStreaming: true },
|
||||
},
|
||||
];
|
||||
|
||||
export function buildDeepSeekModelDefinition(
|
||||
model: (typeof DEEPSEEK_MODEL_CATALOG)[number],
|
||||
): ModelDefinitionConfig {
|
||||
return {
|
||||
...model,
|
||||
api: "openai-completions",
|
||||
};
|
||||
}
|
||||
31
openclaw/extensions/deepseek/onboard.ts
Normal file
31
openclaw/extensions/deepseek/onboard.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import {
|
||||
applyAgentDefaultModelPrimary,
|
||||
applyProviderConfigWithModelCatalog,
|
||||
type OpenClawConfig,
|
||||
} from "openclaw/plugin-sdk/provider-onboard";
|
||||
import { buildDeepSeekModelDefinition, DEEPSEEK_BASE_URL, DEEPSEEK_MODEL_CATALOG } from "./api.js";
|
||||
|
||||
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,
|
||||
);
|
||||
}
|
||||
28
openclaw/extensions/deepseek/openclaw.plugin.json
Normal file
28
openclaw/extensions/deepseek/openclaw.plugin.json
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"id": "deepseek",
|
||||
"enabledByDefault": true,
|
||||
"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": {}
|
||||
}
|
||||
}
|
||||
15
openclaw/extensions/deepseek/package.json
Normal file
15
openclaw/extensions/deepseek/package.json
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"name": "@openclaw/deepseek-provider",
|
||||
"version": "2026.4.20",
|
||||
"private": true,
|
||||
"description": "OpenClaw DeepSeek provider plugin",
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@openclaw/plugin-sdk": "workspace:*"
|
||||
},
|
||||
"openclaw": {
|
||||
"extensions": [
|
||||
"./index.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
14
openclaw/extensions/deepseek/provider-catalog.ts
Normal file
14
openclaw/extensions/deepseek/provider-catalog.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import type { ModelProviderConfig } from "openclaw/plugin-sdk/provider-model-shared";
|
||||
import {
|
||||
buildDeepSeekModelDefinition,
|
||||
DEEPSEEK_BASE_URL,
|
||||
DEEPSEEK_MODEL_CATALOG,
|
||||
} from "./models.js";
|
||||
|
||||
export function buildDeepSeekProvider(): ModelProviderConfig {
|
||||
return {
|
||||
baseUrl: DEEPSEEK_BASE_URL,
|
||||
api: "openai-completions",
|
||||
models: DEEPSEEK_MODEL_CATALOG.map(buildDeepSeekModelDefinition),
|
||||
};
|
||||
}
|
||||
16
openclaw/extensions/deepseek/tsconfig.json
Normal file
16
openclaw/extensions/deepseek/tsconfig.json
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"extends": "../tsconfig.package-boundary.base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "."
|
||||
},
|
||||
"include": ["./*.ts", "./src/**/*.ts"],
|
||||
"exclude": [
|
||||
"./**/*.test.ts",
|
||||
"./dist/**",
|
||||
"./node_modules/**",
|
||||
"./src/test-support/**",
|
||||
"./src/**/*test-helpers.ts",
|
||||
"./src/**/*test-harness.ts",
|
||||
"./src/**/*test-support.ts"
|
||||
]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue