Skip to content

Commit 9e9911a

Browse files
lishengzxcclaude
andcommitted
feat(dsh): add bailian-cli-dsh plugin bundle for DeepSeek Harness
Expose Bailian capabilities to dsh through its service seams as one package with six subpath plugin entries and a `dsh.bundle` patch: - TokenPlan as an LLM provider - bailian_vision_describe / bailian_image_generate tools over `bl` - knowledge-base retrieval as a WebSearchProvider (`bailian-kb`) - cross-session memory: tools, pre-step recall, turn-close persist - managed-agent as a SubagentProvider TokenPlan configures the base bundle's existing pi-ai row rather than mounting a second `dsh-llm-pi-ai` instance. A second instance cannot work: pi-ai re-declares its entire built-in provider catalog to `registerConfigurableProviders`, and that directory is global, so boot fails with a duplicate on `amazon-bedrock`. Routes and vision support were probed against the live gateway. qwen3.8-max, qwen3.7-plus, qwen3.6-flash and glm-5.2 read images; qwen3.7-max rejects them with HTTP 400; the DeepSeek routes accept image content without erroring yet stay blind. The DeepSeek entries therefore do not declare image input — claiming it would turn a clean refusal into a silently wrong answer — and `bailian_vision_describe` serves them by returning text instead. Also fix `bl memory` against the v2 API, each verified live: - `profile get` used /profiles, which returns HTTP 500. The documented and working endpoint is /user_profile. - `add` read `response.memory_ids`, which the service never returns. It returns `memory_nodes`, so text output always printed "IDs: none". - `MemoryNode.created_at`/`updated_at` are unix seconds, not strings, and `UserProfileResponse.profile` did not match the wire shape. - Add the missing request parameters: --meta-data, --project-id, --project-ids, --min-score, --enable-rerank, --plan-version, --enable-judge, --enable-rewrite, --timestamp. - Add `memory profile list|detail|update|delete`, covering the four v2 profile-schema operations the CLI was missing. `plan_version: lite` is ignored by the service and still bills pro; `enable_rerank: false` is what actually selects lite, which is ~50x cheaper per search. The CLI flag and the memory plugin both send the parameter that works. Disable pnpm's autoInstallPeers: the @deepseek-ai/dsh-* rc line peers on three packages that were never published to npm, which 404s the whole workspace install. Verified the existing packages still build. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 2389681 commit 9e9911a

32 files changed

Lines changed: 4408 additions & 410 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,6 @@ packages/cli/scene/**/outputs/
5252

5353
# Local scratch / plan drafts (never commit)
5454
.scratch/
55+
56+
# pnpm pack output
57+
*.tgz

packages/cli/src/commands.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ import {
3030
memoryDelete,
3131
memoryProfileCreate,
3232
memoryProfileGet,
33+
memoryProfileList,
34+
memoryProfileDetail,
35+
memoryProfileUpdate,
36+
memoryProfileDelete,
3337
knowledgeRetrieve,
3438
knowledgeSearch,
3539
knowledgeChat,
@@ -149,6 +153,10 @@ export const commands: Record<string, AnyCommand> = {
149153
"memory delete": memoryDelete,
150154
"memory profile create": memoryProfileCreate,
151155
"memory profile get": memoryProfileGet,
156+
"memory profile list": memoryProfileList,
157+
"memory profile detail": memoryProfileDetail,
158+
"memory profile update": memoryProfileUpdate,
159+
"memory profile delete": memoryProfileDelete,
152160
"knowledge retrieve": knowledgeRetrieve,
153161
"knowledge search": knowledgeSearch,
154162
"knowledge chat": knowledgeChat,

packages/commands/src/commands/memory/add.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,16 @@ const ADD_FLAGS = {
2828
valueHint: "<id>",
2929
description: "Memory library ID (isolate memory space)",
3030
},
31+
projectId: {
32+
type: "string",
33+
valueHint: "<id>",
34+
description: "Memory extraction rule ID (defaults to the library's default rule)",
35+
},
36+
metaData: {
37+
type: "string",
38+
valueHint: "<json>",
39+
description: 'Custom metadata JSON object: {"location":"Beijing"}',
40+
},
3141
} satisfies FlagsDef;
3242
type AddFlags = ParsedFlags<typeof ADD_FLAGS>;
3343

@@ -40,6 +50,7 @@ export default defineCommand({
4050
'--user-id user1 --content "The user likes Python programming"',
4151
'--user-id user1 --messages \'[{"role":"user","content":"I like traveling"}]\'',
4252
'--user-id user1 --content "Lives in Beijing" --profile-schema schema_xxx',
53+
'--user-id user1 --content "Lives in Beijing" --meta-data \'{"source":"onboarding"}\'',
4354
],
4455
validate: (f: AddFlags) =>
4556
!f.messages && !f.content ? "Provide --messages or --content." : undefined,
@@ -63,6 +74,15 @@ export default defineCommand({
6374

6475
if (flags.profileSchema) body.profile_schema = flags.profileSchema;
6576
if (flags.memoryLibraryId) body.memory_library_id = flags.memoryLibraryId;
77+
if (flags.projectId) body.project_id = flags.projectId;
78+
79+
if (flags.metaData) {
80+
try {
81+
body.meta_data = JSON.parse(flags.metaData);
82+
} catch {
83+
throw new UsageError("--meta-data must be valid JSON object");
84+
}
85+
}
6686

6787
const format = detectOutputFormat(settings.output);
6888

@@ -78,8 +98,14 @@ export default defineCommand({
7898
});
7999

80100
if (settings.quiet || format === "text") {
81-
const ids = response.memory_ids?.join(", ") || "none";
82-
emitBare(`Memory added. IDs: ${ids}`);
101+
const nodes = response.memory_nodes ?? [];
102+
if (nodes.length === 0) {
103+
emitBare("No memory fragments were extracted.");
104+
} else {
105+
for (const node of nodes) {
106+
emitBare(`[${node.event ?? "ADD"}] ${node.memory_node_id} ${node.content}`);
107+
}
108+
}
83109
} else {
84110
emitResult(response, format);
85111
}

packages/commands/src/commands/memory/list.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ export default defineCommand({
2424
},
2525
page: { type: "number", valueHint: "<n>", description: "Page number (default: 1)" },
2626
memoryLibraryId: { type: "string", valueHint: "<id>", description: "Memory library ID" },
27+
projectId: {
28+
type: "string",
29+
valueHint: "<id>",
30+
description: "Memory extraction rule ID (defaults to the library's default rule)",
31+
},
2732
},
2833
exampleArgs: ["--user-id user1", "--user-id user1 --page-size 20 --page 2"],
2934
async run(ctx) {
@@ -36,6 +41,7 @@ export default defineCommand({
3641
if (flags.pageSize !== undefined) params.set("page_size", String(flags.pageSize));
3742
if (flags.page !== undefined) params.set("page_num", String(flags.page));
3843
if (flags.memoryLibraryId) params.set("memory_library_id", flags.memoryLibraryId);
44+
if (flags.projectId) params.set("project_id", flags.projectId);
3945

4046
const path = `${memoryListPath()}?${params.toString()}`;
4147

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { defineCommand, profileSchemaItemPath, detectOutputFormat } from "bailian-cli-core";
2+
import { emitResult, emitBare } from "bailian-cli-runtime";
3+
4+
export default defineCommand({
5+
description: "Delete a profile schema",
6+
auth: "apiKey",
7+
usageArgs: "--schema-id <id> [flags]",
8+
flags: {
9+
schemaId: {
10+
type: "string",
11+
valueHint: "<id>",
12+
description: "Profile schema ID (required)",
13+
required: true,
14+
},
15+
memoryLibraryId: { type: "string", valueHint: "<id>", description: "Memory library ID" },
16+
},
17+
exampleArgs: ["--schema-id schema_xxx"],
18+
async run(ctx) {
19+
const { settings, flags } = ctx;
20+
const format = detectOutputFormat(settings.output);
21+
22+
const params = new URLSearchParams();
23+
if (flags.memoryLibraryId) params.set("memory_library_id", flags.memoryLibraryId);
24+
const query = params.toString();
25+
const base = profileSchemaItemPath(flags.schemaId);
26+
const path = query ? `${base}?${query}` : base;
27+
28+
if (settings.dryRun) {
29+
emitResult({ endpoint: ctx.client.url(path), method: "DELETE" }, format);
30+
return;
31+
}
32+
33+
const response = await ctx.client.requestJson<{ request_id: string }>({
34+
path,
35+
method: "DELETE",
36+
});
37+
38+
if (settings.quiet || format === "text") {
39+
emitBare(`Profile schema ${flags.schemaId} deleted.`);
40+
} else {
41+
emitResult(response, format);
42+
}
43+
},
44+
});
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import {
2+
defineCommand,
3+
profileSchemaItemPath,
4+
detectOutputFormat,
5+
type ProfileSchemaGetResponse,
6+
} from "bailian-cli-core";
7+
import { emitResult, emitBare } from "bailian-cli-runtime";
8+
9+
export default defineCommand({
10+
description: "Show a profile schema and its attribute IDs",
11+
auth: "apiKey",
12+
usageArgs: "--schema-id <id> [flags]",
13+
flags: {
14+
schemaId: {
15+
type: "string",
16+
valueHint: "<id>",
17+
description: "Profile schema ID (required)",
18+
required: true,
19+
},
20+
memoryLibraryId: { type: "string", valueHint: "<id>", description: "Memory library ID" },
21+
},
22+
exampleArgs: ["--schema-id schema_xxx"],
23+
async run(ctx) {
24+
const { settings, flags } = ctx;
25+
const format = detectOutputFormat(settings.output);
26+
27+
const params = new URLSearchParams();
28+
if (flags.memoryLibraryId) params.set("memory_library_id", flags.memoryLibraryId);
29+
const query = params.toString();
30+
const base = profileSchemaItemPath(flags.schemaId);
31+
const path = query ? `${base}?${query}` : base;
32+
33+
if (settings.dryRun) {
34+
emitResult({ endpoint: ctx.client.url(path), method: "GET" }, format);
35+
return;
36+
}
37+
38+
const response = await ctx.client.requestJson<ProfileSchemaGetResponse>({
39+
path,
40+
method: "GET",
41+
});
42+
43+
if (settings.quiet || format === "text") {
44+
emitBare(`${response.name}${response.description ? ` — ${response.description}` : ""}`);
45+
for (const attribute of response.attributes ?? []) {
46+
emitBare(` [${attribute.attribute_id}] ${attribute.name}`);
47+
}
48+
} else {
49+
emitResult(response, format);
50+
}
51+
},
52+
});
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import {
2+
defineCommand,
3+
profileSchemaPath,
4+
detectOutputFormat,
5+
type ProfileSchemaListResponse,
6+
} from "bailian-cli-core";
7+
import { emitResult, emitBare } from "bailian-cli-runtime";
8+
9+
export default defineCommand({
10+
description: "List profile schemas",
11+
auth: "apiKey",
12+
usageArgs: "[flags]",
13+
flags: {
14+
memoryLibraryId: { type: "string", valueHint: "<id>", description: "Memory library ID" },
15+
pageSize: { type: "number", valueHint: "<n>", description: "Results per page (default: 10)" },
16+
page: { type: "number", valueHint: "<n>", description: "Page number (default: 1)" },
17+
},
18+
exampleArgs: ["", "--page-size 20 --page 2"],
19+
async run(ctx) {
20+
const { settings, flags } = ctx;
21+
const format = detectOutputFormat(settings.output);
22+
23+
const params = new URLSearchParams();
24+
if (flags.memoryLibraryId) params.set("memory_library_id", flags.memoryLibraryId);
25+
if (flags.pageSize !== undefined) params.set("page_size", String(flags.pageSize));
26+
if (flags.page !== undefined) params.set("page_num", String(flags.page));
27+
28+
const query = params.toString();
29+
const path = query ? `${profileSchemaPath()}?${query}` : profileSchemaPath();
30+
31+
if (settings.dryRun) {
32+
emitResult({ endpoint: ctx.client.url(path), method: "GET" }, format);
33+
return;
34+
}
35+
36+
const response = await ctx.client.requestJson<ProfileSchemaListResponse>({
37+
path,
38+
method: "GET",
39+
});
40+
41+
if (settings.quiet || format === "text") {
42+
const schemas = response.profile_schemas ?? [];
43+
if (schemas.length === 0) {
44+
emitBare("No profile schemas found.");
45+
} else {
46+
for (const schema of schemas) {
47+
emitBare(`[${schema.profile_schema_id}] ${schema.name}`);
48+
}
49+
if (response.total !== undefined) emitBare(`\nTotal: ${response.total}`);
50+
}
51+
} else {
52+
emitResult(response, format);
53+
}
54+
},
55+
});
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import {
2+
defineCommand,
3+
UsageError,
4+
profileSchemaItemPath,
5+
detectOutputFormat,
6+
type ProfileSchemaUpdateRequest,
7+
} from "bailian-cli-core";
8+
import { emitResult, emitBare } from "bailian-cli-runtime";
9+
import type { FlagsDef, ParsedFlags } from "bailian-cli-core";
10+
11+
const UPDATE_FLAGS = {
12+
schemaId: {
13+
type: "string",
14+
valueHint: "<id>",
15+
description: "Profile schema ID (required)",
16+
required: true,
17+
},
18+
name: { type: "string", valueHint: "<name>", description: "New schema name" },
19+
description: { type: "string", valueHint: "<text>", description: "New schema description" },
20+
attributeOps: {
21+
type: "string",
22+
valueHint: "<json>",
23+
description:
24+
'Attribute operations JSON array: [{"op":"add","name":"plan"},{"op":"delete","attribute_id":"attr_1"}]',
25+
},
26+
memoryLibraryId: { type: "string", valueHint: "<id>", description: "Memory library ID" },
27+
} satisfies FlagsDef;
28+
type UpdateFlags = ParsedFlags<typeof UPDATE_FLAGS>;
29+
30+
export default defineCommand({
31+
description: "Update a profile schema's name, description, or attributes",
32+
auth: "apiKey",
33+
usageArgs: "--schema-id <id> [--name <name>] [--attribute-ops <json>] [flags]",
34+
flags: UPDATE_FLAGS,
35+
notes: ["Attribute IDs for update/delete operations come from `memory profile detail`."],
36+
exampleArgs: [
37+
'--schema-id schema_xxx --name "user_basic_v2"',
38+
'--schema-id schema_xxx --attribute-ops \'[{"op":"add","name":"plan","description":"subscription plan"}]\'',
39+
'--schema-id schema_xxx --attribute-ops \'[{"op":"delete","attribute_id":"attr_1"}]\'',
40+
],
41+
validate: (f: UpdateFlags) =>
42+
!f.name && !f.description && !f.attributeOps
43+
? "Provide --name, --description, or --attribute-ops."
44+
: undefined,
45+
async run(ctx) {
46+
const { settings, flags } = ctx;
47+
const format = detectOutputFormat(settings.output);
48+
49+
const body: ProfileSchemaUpdateRequest = {};
50+
if (flags.name) body.name = flags.name;
51+
if (flags.description) body.description = flags.description;
52+
if (flags.memoryLibraryId) body.memory_library_id = flags.memoryLibraryId;
53+
54+
if (flags.attributeOps) {
55+
try {
56+
body.attributes_operations = JSON.parse(flags.attributeOps);
57+
} catch {
58+
throw new UsageError("--attribute-ops must be valid JSON array");
59+
}
60+
}
61+
62+
const path = profileSchemaItemPath(flags.schemaId);
63+
64+
if (settings.dryRun) {
65+
emitResult({ endpoint: ctx.client.url(path), method: "PATCH", request: body }, format);
66+
return;
67+
}
68+
69+
const response = await ctx.client.requestJson<{ request_id: string }>({
70+
path,
71+
method: "PATCH",
72+
body,
73+
});
74+
75+
if (settings.quiet || format === "text") {
76+
emitBare(`Profile schema ${flags.schemaId} updated.`);
77+
} else {
78+
emitResult(response, format);
79+
}
80+
},
81+
});

0 commit comments

Comments
 (0)