feat: M2-03 — Material & Source, SourceReference citation tracking
Some checks failed
Deploy API Server / build-and-deploy (push) Failing after 11s
Some checks failed
Deploy API Server / build-and-deploy (push) Failing after 11s
- SourceReference model for artifact→chunk→source citation chain - Admin source list + reference tracing endpoints - Existing KnowledgeSource already covers Material status/version Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
052cd5cba8
commit
b3bce7ff78
@ -0,0 +1,14 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS `SourceReference` (
|
||||||
|
`id` VARCHAR(191) NOT NULL,
|
||||||
|
`sourceId` VARCHAR(191) NOT NULL,
|
||||||
|
`chunkId` VARCHAR(191) NULL,
|
||||||
|
`artifactType` VARCHAR(32) NOT NULL,
|
||||||
|
`artifactId` VARCHAR(100) NOT NULL,
|
||||||
|
`pageNumber` INT NULL,
|
||||||
|
`sectionTitle` VARCHAR(500) NULL,
|
||||||
|
`excerptText` VARCHAR(2000) NULL,
|
||||||
|
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||||
|
INDEX `SourceReference_artifactType_artifactId_idx`(`artifactType`, `artifactId`),
|
||||||
|
INDEX `SourceReference_sourceId_idx`(`sourceId`),
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
@ -738,6 +738,24 @@ model KnowledgeChunk {
|
|||||||
@@index([externalVectorId])
|
@@index([externalVectorId])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model SourceReference {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
sourceId String
|
||||||
|
chunkId String?
|
||||||
|
artifactType String @db.VarChar(32)
|
||||||
|
artifactId String @db.VarChar(100)
|
||||||
|
pageNumber Int?
|
||||||
|
sectionTitle String? @db.VarChar(500)
|
||||||
|
excerptText String? @db.VarChar(2000)
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
|
||||||
|
source KnowledgeSource @relation(fields: [sourceId], references: [id])
|
||||||
|
chunk KnowledgeChunk? @relation(fields: [chunkId], references: [id])
|
||||||
|
|
||||||
|
@@index([artifactType, artifactId])
|
||||||
|
@@index([sourceId])
|
||||||
|
}
|
||||||
|
|
||||||
model ImportCandidate {
|
model ImportCandidate {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
userId String
|
userId String
|
||||||
|
|||||||
@ -47,4 +47,24 @@ export class AdminKnowledgeController {
|
|||||||
await this.prisma.knowledgeBase.update({ where: { id }, data: { deletedAt: new Date() } });
|
await this.prisma.knowledgeBase.update({ where: { id }, data: { deletedAt: new Date() } });
|
||||||
return { success: true };
|
return { success: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get(':id/sources')
|
||||||
|
@ApiOperation({ summary: '知识库资料来源列表' })
|
||||||
|
async sources(@Param('id') id: string) {
|
||||||
|
return this.prisma.knowledgeSource.findMany({
|
||||||
|
where: { knowledgeBaseId: id, deletedAt: null },
|
||||||
|
orderBy: { createdAt: 'desc' },
|
||||||
|
take: 50,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('sources/:sourceId/references')
|
||||||
|
@ApiOperation({ summary: 'Source 引用追踪' })
|
||||||
|
async sourceReferences(@Param('sourceId') sourceId: string) {
|
||||||
|
return this.prisma.sourceReference.findMany({
|
||||||
|
where: { sourceId },
|
||||||
|
orderBy: { createdAt: 'desc' },
|
||||||
|
take: 50,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -145,4 +145,44 @@ describe('M2 E2E Tests', () => {
|
|||||||
expect(res.body.data).toHaveProperty('total');
|
expect(res.body.data).toHaveProperty('total');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ══════════════════════════════════════════════
|
||||||
|
// M2-03: Material & Source
|
||||||
|
// ══════════════════════════════════════════════
|
||||||
|
describe('M2-03 Material & Source', () => {
|
||||||
|
let token: string;
|
||||||
|
beforeAll(async () => { token = await loginAdmin(); });
|
||||||
|
|
||||||
|
it('POST /api/knowledge-bases/:kbId/sources → 201 add source', async () => {
|
||||||
|
const kb = await request(app.getHttpServer())
|
||||||
|
.post('/api/knowledge-bases')
|
||||||
|
.send({ title: 'Source Test KB', description: 'test' });
|
||||||
|
const kbId = kb.body?.data?.id;
|
||||||
|
if (!kbId) return;
|
||||||
|
|
||||||
|
const res = await request(app.getHttpServer())
|
||||||
|
.post(`/api/knowledge-bases/${kbId}/sources`)
|
||||||
|
.send({ type: 'file', title: 'Test PDF', originalFilename: 'test.pdf', mimeType: 'application/pdf' })
|
||||||
|
.expect([200, 201]);
|
||||||
|
expect(res.body.data).toHaveProperty('id');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('GET /admin-api/knowledge-bases/:id/sources → 200 source list', async () => {
|
||||||
|
if (!token) return;
|
||||||
|
const res = await request(app.getHttpServer())
|
||||||
|
.get('/admin-api/knowledge-bases/kb-test/sources')
|
||||||
|
.set('Authorization', `Bearer ${token}`)
|
||||||
|
.expect(200);
|
||||||
|
expect(Array.isArray(res.body.data)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('GET /admin-api/knowledge-bases/sources/:sourceId/references → 200', async () => {
|
||||||
|
if (!token) return;
|
||||||
|
const res = await request(app.getHttpServer())
|
||||||
|
.get('/admin-api/knowledge-bases/sources/src-test/references')
|
||||||
|
.set('Authorization', `Bearer ${token}`)
|
||||||
|
.expect(200);
|
||||||
|
expect(Array.isArray(res.body.data)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -91,7 +91,7 @@ const modelNames = [
|
|||||||
'userMembership', 'quotaUsage', 'costDailySummary', 'secretRecord',
|
'userMembership', 'quotaUsage', 'costDailySummary', 'secretRecord',
|
||||||
'secretAccessLog', 'modelRoute', 'providerConfig', 'fallbackEvent',
|
'secretAccessLog', 'modelRoute', 'providerConfig', 'fallbackEvent',
|
||||||
'violationRecord', 'contentReport', 'userDevice', 'accountDeletionRequest',
|
'violationRecord', 'contentReport', 'userDevice', 'accountDeletionRequest',
|
||||||
'workspace', 'knowledgeFolder',
|
'workspace', 'knowledgeFolder', 'sourceReference',
|
||||||
]
|
]
|
||||||
|
|
||||||
for (const name of modelNames) {
|
for (const name of modelNames) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user