Skip to content

Commit 8ee2c37

Browse files
committed
test(knowledge): 添加多模态与表格型仓库 E2E 测试套件支持
- 新增多模态问答及检索服务的 live E2E 测试用例,支持基于图像参数的功能验证 - 补充表格库的 chunk add/list/delete 闭环测试,验证了 field channel 的必填项和读写一致性 - 增加图片库 chunk list 的 metadata 验证,确保 image_url 数组和可见性标志存在 - 实现带覆盖重导功能的 doc import-oss 测试,确认覆盖后 fileId 变更及旧文件失效 - 编写自有 OSS Bucket 的幂等复用集合创建和获取测试,确保服务端的 tag-based 访问控制支持 - 在 gating 中添加对各类长驻知识库及服务环境变量的就绪检测函数
1 parent 9fc6434 commit 8ee2c37

12 files changed

Lines changed: 481 additions & 19 deletions

File tree

packages/commands/src/commands/knowledge/chunk-add.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ const CHUNK_ADD_FLAGS = {
2222
docId: {
2323
type: "string",
2424
valueHint: "<id>",
25-
description: "Attach the chunk to this document (document-type knowledge bases)",
25+
description:
26+
"Owning document ID; required for table/image knowledge bases (the server rejects field-channel chunks without it), optional for document-type",
2627
},
2728
content: {
2829
type: "string",
@@ -73,6 +74,7 @@ export default defineCommand({
7374
flags: CHUNK_ADD_FLAGS,
7475
notes: [
7576
"Document / table / image knowledge bases are supported; audio-video ones are not.",
77+
"Table/image knowledge bases require --doc-id — verified live: the server returns HTTP 500 (dataId不能为空) without it. Use the document-level id from the doc list command; the per-row doc_id in chunk list metadata is rejected (Index.InvalidParameter).",
7678
"The API is idempotent but rate-limited to 10 calls per second — throttle batch scripts.",
7779
"The response carries no chunk id; list chunks afterwards to find the new one.",
7880
"For table/image knowledge bases use --field with Excel column headers as keys; values are passed through as strings.",

packages/commands/src/commands/knowledge/collection-create.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export default defineCommand({
4747
flags: COLLECTION_CREATE_FLAGS,
4848
notes: [
4949
"Store type defaults to platform (managed storage); custom uses your authorized OSS bucket.",
50+
"Custom buckets must carry the bucket tag bailian-connector-access=ReadAndWrite (Bailian's tag-based access control); without it the server rejects creation with a misleading 'setBucketCORS failed' error.",
5051
"There is no collection delete API — create collections deliberately.",
5152
],
5253
exampleArgs: [
@@ -71,15 +72,17 @@ export default defineCommand({
7172
const format = detectOutputFormat(settings.output);
7273

7374
const storeType = (flags.storeType ?? "platform").toUpperCase();
74-
// The server contract still uses connector* fields; only the CLI-facing term is collection
75+
// The server contract still uses connector* fields; only the CLI-facing term is collection.
76+
// CUSTOM fields are regionId/bucketName per api/connector/add-connector.md (live-verified;
77+
// the earlier ossRegionId/ossBucket naming was an implementation error, rejected with InvalidParameter).
7578
const body = {
7679
connectorType: "FILE",
7780
connectorName: flags.name,
7881
description: flags.description,
7982
fileConnectorConfig: {
8083
storeType,
8184
...(storeType === "CUSTOM"
82-
? { ossRegionId: flags.ossRegion, ossBucket: flags.ossBucket }
85+
? { regionId: flags.ossRegion, bucketName: flags.ossBucket }
8386
: {}),
8487
},
8588
};

packages/commands/src/commands/knowledge/collection-get.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,11 @@ export default defineCommand({
6969
const storeType = collection?.fileConnectorConfig?.storeType;
7070
emitBare(`storeType: ${storeType ?? "-"}`);
7171
if (storeType === "CUSTOM") {
72-
emitBare(` ossRegion: ${collection?.fileConnectorConfig?.ossRegionId ?? "-"}`);
73-
emitBare(` ossBucket: ${collection?.fileConnectorConfig?.ossBucket ?? "-"}`);
72+
// Defensive branch: live-verified getConnector responses do NOT echo
73+
// fileConnectorConfig at all (storeType prints "-"); kept in case the
74+
// server starts returning it. Field names follow the create contract.
75+
emitBare(` ossRegion: ${collection?.fileConnectorConfig?.regionId ?? "-"}`);
76+
emitBare(` ossBucket: ${collection?.fileConnectorConfig?.bucketName ?? "-"}`);
7477
}
7578
return;
7679
}

packages/commands/src/commands/knowledge/doc-import-oss.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export default defineCommand({
5454
notes: [
5555
"The bucket must be authorized to the platform service role beforehand; permission errors from the server are passed through with a pointer to check AliyunServiceRoleForBailian in the RAM console.",
5656
"File names are derived from the OSS key basename.",
57+
"--overwrite replaces the previously imported file and issues a NEW fileId (the old one becomes invalid) — verified live.",
5758
],
5859
exampleArgs: [
5960
"--bucket my-bucket --region cn-beijing --oss-key docs/a.pdf --workspace-id ws-xxx",
@@ -94,14 +95,21 @@ export default defineCommand({
9495
body,
9596
});
9697

97-
const fileIds = response.data?.fileIds ?? [];
98+
// Live-verified shape: results come back as addFileResultList (the docs' flat
99+
// fileIds field is not returned); per-file status is SUCCESS on success
100+
const results = response.data?.addFileResultList ?? [];
101+
const fileIds = results
102+
.map((result) => result.fileId)
103+
.filter((fileId): fileId is string => !!fileId);
98104
if (settings.quiet) {
99105
for (const fileId of fileIds) emitBare(fileId);
100106
return;
101107
}
102108
if (format === "text") {
103109
emitBare(`imported: ${fileIds.length} file(s)`);
104-
for (const fileId of fileIds) emitBare(` ${fileId}`);
110+
for (const result of results) {
111+
emitBare(` ${result.fileId ?? "-"} ${result.status ?? "-"} ${result.ossKey ?? ""}`);
112+
}
105113
return;
106114
}
107115
emitResult(response, format);

packages/commands/tests/e2e/helpers.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,15 @@ export {
2929
isConnectorE2EReady,
3030
isConsoleE2EReady,
3131
isDashScopeE2EReady,
32+
isImageKbE2EReady,
3233
isKbAdminE2EReady,
34+
isMultimodalChatE2EReady,
35+
isMultimodalSearchE2EReady,
3336
isOpenApiE2EReady,
37+
isOssImportE2EReady,
3438
isSearchE2EReady,
39+
isTableKbE2EReady,
40+
isTableSearchE2EReady,
3541
} from "e2e/gating";
3642

3743
const e2eDir = dirname(fileURLToPath(import.meta.url));

packages/commands/tests/e2e/knowledge/knowledge-chat.e2e.test.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { describe, expect, test } from "vite-plus/test";
2-
import { isChatE2EReady, parseStdoutJson, runCommandE2e } from "../helpers.ts";
2+
import {
3+
isChatE2EReady,
4+
isMultimodalChatE2EReady,
5+
parseStdoutJson,
6+
runCommandE2e,
7+
} from "../helpers.ts";
38
import { KNOWLEDGE_CHAT_ROUTES } from "../topic-routes.ts";
49

510
interface ContentPart {
@@ -389,3 +394,34 @@ describe.skipIf(!isChatE2EReady())("e2e: knowledge chat (live)", () => {
389394
expect(stderr).toBeTruthy();
390395
});
391396
});
397+
398+
// Long-lived console-created fixture (multimodal Q&A services cannot be created
399+
// via the CLI — see .env BAILIAN_E2E_IMAGE_CHAT_AGENT_ID)
400+
describe.skipIf(!isMultimodalChatE2EReady())(
401+
"e2e: knowledge chat --image live (常驻 fixture)",
402+
() => {
403+
const workspaceId = process.env.BAILIAN_WORKSPACE_ID!;
404+
const imageChatAgentId = process.env.BAILIAN_E2E_IMAGE_CHAT_AGENT_ID!;
405+
406+
test("多模态问答接受 --image 并返回非空回答", async () => {
407+
const { stdout, stderr, exitCode } = await runCommandE2e(KNOWLEDGE_CHAT_ROUTES, [
408+
"knowledge",
409+
"chat",
410+
"--message",
411+
"图里有什么",
412+
"--image",
413+
"https://dashscope.oss-cn-beijing.aliyuncs.com/images/dog_and_girl.jpeg",
414+
"--agent-id",
415+
imageChatAgentId,
416+
"--workspace-id",
417+
workspaceId,
418+
"--output",
419+
"json",
420+
]);
421+
expect(exitCode, stderr).toBe(0);
422+
const data = parseStdoutJson<ChatJsonResult>(stdout);
423+
expect(data.answer).toBeTruthy();
424+
expect(data.answer.length).toBeGreaterThan(0);
425+
}, 120_000);
426+
},
427+
);

0 commit comments

Comments
 (0)