feat: M2-05 — Vector integration contracts + citation context assembler
All checks were successful
Deploy API Server / build-and-deploy (push) Successful in 38s

- integration-types.ts: IndexableChunk, CitationContext, RetrievalRequest/Response
- VectorService.buildCitationContexts() for RAG citation assembly
- Defines Ingestion↔Vector↔RAG interface contracts

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
WangDL 2026-05-24 13:28:05 +08:00
parent 9520d1f549
commit 68540b0d67
3 changed files with 94 additions and 0 deletions

View File

@ -0,0 +1,54 @@
// Shared types for Ingestion ↔ Vector ↔ RAG integration
/** Ingestion → Vector: chunk with embedding ready for upsert */
export interface IndexableChunk {
id: string;
sourceId: string;
knowledgeBaseId: string;
userId: string;
content: string;
chunkIndex: number;
pageNumber?: number;
sectionTitle?: string;
tokenCount: number;
embedding: number[];
}
/** Vector → RAG: search result with source info for citation */
export interface CitationContext {
chunkId: string;
sourceId: string;
sourceTitle: string;
content: string;
pageNumber?: number;
sectionTitle?: string;
score: number;
}
/** RAG → Vector: search request parameters */
export interface RetrievalRequest {
queryEmbedding: number[];
knowledgeBaseId?: string;
userId?: string;
topK?: number;
enableRerank?: boolean;
}
/** RAG → Vector: complete retrieval response */
export interface RetrievalResponse {
results: CitationContext[];
queryTimeMs: number;
}
/** Batch upsert result */
export interface BatchUpsertResult {
upserted: number;
failed: number;
errors?: string[];
}
/** Cleanup trigger when import fails or source is deleted */
export interface VectorCleanupRequest {
sourceId: string;
reason: 'import_failed' | 'source_deleted' | 'reparse';
}

View File

@ -2,6 +2,7 @@ import { Injectable, Logger, OnModuleInit, Optional } from '@nestjs/common';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { QdrantClient } from '@qdrant/js-client-rest'; import { QdrantClient } from '@qdrant/js-client-rest';
import { AiGatewayService } from '../ai/gateway/ai-gateway.service'; import { AiGatewayService } from '../ai/gateway/ai-gateway.service';
import type { CitationContext } from './integration-types';
export interface VectorPoint { export interface VectorPoint {
id: string; id: string;
@ -191,4 +192,17 @@ export class VectorService implements OnModuleInit {
const result = await this.client.count(COLLECTION_NAME); const result = await this.client.count(COLLECTION_NAME);
return result.count; return result.count;
} }
/** Build citation context from search results */
async buildCitationContexts(results: SearchResult[]): Promise<CitationContext[]> {
return results.map(r => ({
chunkId: r.id,
sourceId: r.payload?.sourceId || 'unknown',
sourceTitle: r.payload?.sourceTitle || 'Untitled',
content: r.payload?.text || '',
pageNumber: r.payload?.pageNumber,
sectionTitle: r.payload?.sectionTitle,
score: r.score,
}));
}
} }

View File

@ -233,4 +233,30 @@ describe('M2 E2E Tests', () => {
expect(res.body.success).toBe(true); expect(res.body.success).toBe(true);
}); });
}); });
// ══════════════════════════════════════════════
// M2-05: Vector & Retrieval 对接
// ══════════════════════════════════════════════
describe('M2-05 Vector Integration', () => {
let token: string;
beforeAll(async () => { token = await loginAdmin(); });
it('VectorService collection accessible', async () => {
if (!token) return;
const res = await request(app.getHttpServer())
.get('/admin-api/vector/collection')
.set('Authorization', `Bearer ${token}`)
.expect(200);
expect(res.body.data).toHaveProperty('name');
});
it('VectorService count accessible', async () => {
if (!token) return;
const res = await request(app.getHttpServer())
.get('/admin-api/vector/count')
.set('Authorization', `Bearer ${token}`)
.expect(200);
expect(res.body.data).toHaveProperty('count');
});
});
}); });