mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-16 17:46:18 +08:00
feat(server): attachment embedding (#13348)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added support for managing "blobs" in Copilot context, including adding and removing blobs via new GraphQL mutations and UI fields. * Introduced tracking and querying of blob embeddings within workspaces, enabling search and similarity matching for blob content. * Extended Copilot context and workspace APIs, schema, and UI to display and manage blobs alongside existing documents and files. * **Bug Fixes** * Updated context and embedding status logic to handle blobs, ensuring accurate status reporting and embedding management. * **Tests** * Added and updated test cases and snapshots to cover blob embedding insertion, matching, and removal scenarios. * **Documentation** * Updated GraphQL schema and TypeScript types to reflect new blob-related fields and mutations. * **Chores** * Refactored and cleaned up code to support new blob entity and embedding logic, including renaming and updating internal methods and types. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -65,15 +65,14 @@ export class CopilotEmbeddingJob {
|
||||
async addFileEmbeddingQueue(file: Jobs['copilot.embedding.files']) {
|
||||
if (!this.supportEmbedding) return;
|
||||
|
||||
const { userId, workspaceId, contextId, blobId, fileId, fileName } = file;
|
||||
await this.queue.add('copilot.embedding.files', {
|
||||
userId,
|
||||
workspaceId,
|
||||
contextId,
|
||||
blobId,
|
||||
fileId,
|
||||
fileName,
|
||||
});
|
||||
await this.queue.add('copilot.embedding.files', file);
|
||||
}
|
||||
|
||||
@CallMetric('ai', 'addBlobEmbeddingQueue')
|
||||
async addBlobEmbeddingQueue(blob: Jobs['copilot.embedding.blobs']) {
|
||||
if (!this.supportEmbedding) return;
|
||||
|
||||
await this.queue.add('copilot.embedding.blobs', blob);
|
||||
}
|
||||
|
||||
@OnEvent('workspace.doc.embedding')
|
||||
@@ -288,6 +287,55 @@ export class CopilotEmbeddingJob {
|
||||
}
|
||||
}
|
||||
|
||||
@OnJob('copilot.embedding.blobs')
|
||||
async embedPendingBlob({
|
||||
userId,
|
||||
workspaceId,
|
||||
contextId,
|
||||
blobId,
|
||||
}: Jobs['copilot.embedding.blobs']) {
|
||||
if (!this.supportEmbedding || !this.embeddingClient) return;
|
||||
|
||||
try {
|
||||
const file = await this.readCopilotBlob(
|
||||
userId,
|
||||
workspaceId,
|
||||
blobId,
|
||||
'blob'
|
||||
);
|
||||
|
||||
const chunks = await this.embeddingClient.getFileChunks(file);
|
||||
const total = chunks.reduce((acc, c) => acc + c.length, 0);
|
||||
|
||||
for (const chunk of chunks) {
|
||||
const embeddings = await this.embeddingClient.generateEmbeddings(chunk);
|
||||
await this.models.copilotWorkspace.insertBlobEmbeddings(
|
||||
workspaceId,
|
||||
blobId,
|
||||
embeddings
|
||||
);
|
||||
}
|
||||
|
||||
if (contextId) {
|
||||
this.event.emit('workspace.blob.embed.finished', {
|
||||
contextId,
|
||||
blobId,
|
||||
chunkSize: total,
|
||||
});
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (contextId) {
|
||||
this.event.emit('workspace.blob.embed.failed', {
|
||||
contextId,
|
||||
blobId,
|
||||
error: mapAnyError(error).message,
|
||||
});
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
private async getDocFragment(
|
||||
workspaceId: string,
|
||||
docId: string
|
||||
@@ -465,7 +513,7 @@ export class CopilotEmbeddingJob {
|
||||
|
||||
const docIdsInWorkspace = readAllDocIdsFromWorkspaceSnapshot(snapshot.blob);
|
||||
const docIdsInEmbedding =
|
||||
await this.models.copilotContext.listWorkspaceEmbedding(workspaceId);
|
||||
await this.models.copilotContext.listWorkspaceDocEmbedding(workspaceId);
|
||||
const docIdsInWorkspaceSet = new Set(docIdsInWorkspace);
|
||||
|
||||
const deletedDocIds = docIdsInEmbedding.filter(
|
||||
|
||||
@@ -14,6 +14,18 @@ declare global {
|
||||
enableDocEmbedding?: boolean;
|
||||
};
|
||||
|
||||
'workspace.blob.embed.finished': {
|
||||
contextId: string;
|
||||
blobId: string;
|
||||
chunkSize: number;
|
||||
};
|
||||
|
||||
'workspace.blob.embed.failed': {
|
||||
contextId: string;
|
||||
blobId: string;
|
||||
error: string;
|
||||
};
|
||||
|
||||
'workspace.doc.embedding': Array<{
|
||||
workspaceId: string;
|
||||
docId: string;
|
||||
@@ -62,6 +74,13 @@ declare global {
|
||||
fileName: string;
|
||||
};
|
||||
|
||||
'copilot.embedding.blobs': {
|
||||
contextId?: string;
|
||||
userId: string;
|
||||
workspaceId: string;
|
||||
blobId: string;
|
||||
};
|
||||
|
||||
'copilot.embedding.cleanupTrashedDocEmbeddings': {
|
||||
workspaceId: string;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user