fix(server): query workspace embed files (#11982)

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
	- Expanded file chunk matching to include both context and workspace file embeddings, providing broader and more relevant search results.
- **Improvements**
	- Enhanced result ranking by introducing a re-ranking step for combined embedding matches, improving the relevance of returned file chunks.
	- Adjusted file count reporting to reflect the total number of workspace files instead of ignored documents for more accurate workspace file statistics.
	- Renamed and streamlined workspace file management methods for clearer and more consistent API usage.
- **Bug Fixes**
	- Prevented embedding similarity queries when embedding is disabled for a workspace, improving system behavior consistency.
- **Tests**
	- Added comprehensive tests to verify workspace embedding management, including enabling, matching, and disabling embedding functionality.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
darkskygit
2025-04-25 08:32:32 +00:00
parent 0abe65653b
commit 49c57ca649
13 changed files with 220 additions and 112 deletions
@@ -135,7 +135,7 @@ export class CopilotWorkspaceEmbeddingConfigResolver {
@Parent() config: CopilotWorkspaceConfigType,
@Args('pagination', PaginationInput.decode) pagination: PaginationInput
): Promise<PaginatedCopilotWorkspaceFileType> {
const [files, totalCount] = await this.copilotWorkspace.listWorkspaceFiles(
const [files, totalCount] = await this.copilotWorkspace.listFiles(
config.workspaceId,
pagination
);
@@ -177,12 +177,12 @@ export class CopilotWorkspaceEmbeddingConfigResolver {
}
try {
const { blobId, file } = await this.copilotWorkspace.addWorkspaceFile(
const { blobId, file } = await this.copilotWorkspace.addFile(
user.id,
workspaceId,
content
);
await this.copilotWorkspace.addWorkspaceFileEmbeddingQueue({
await this.copilotWorkspace.queueFileEmbedding({
userId: user.id,
workspaceId,
blobId,
@@ -219,6 +219,6 @@ export class CopilotWorkspaceEmbeddingConfigResolver {
.workspace(workspaceId)
.assert('Workspace.Settings.Update');
return await this.copilotWorkspace.removeWorkspaceFile(workspaceId, fileId);
return await this.copilotWorkspace.removeFile(workspaceId, fileId);
}
}
@@ -53,11 +53,7 @@ export class CopilotWorkspaceService implements OnApplicationBootstrap {
]);
}
async addWorkspaceFile(
userId: string,
workspaceId: string,
content: FileUpload
) {
async addFile(userId: string, workspaceId: string, content: FileUpload) {
const fileName = content.filename;
const buffer = await readStream(content.createReadStream());
const blobId = createHash('sha256').update(buffer).digest('base64url');
@@ -70,29 +66,25 @@ export class CopilotWorkspaceService implements OnApplicationBootstrap {
return { blobId, file };
}
async getWorkspaceFile(workspaceId: string, fileId: string) {
async getFile(workspaceId: string, fileId: string) {
return await this.models.copilotWorkspace.getFile(workspaceId, fileId);
}
async listWorkspaceFiles(
async listFiles(
workspaceId: string,
pagination?: {
includeRead?: boolean;
} & PaginationInput
) {
return await Promise.all([
this.models.copilotWorkspace.listWorkspaceFiles(workspaceId, pagination),
this.models.copilotWorkspace.countIgnoredDocs(workspaceId),
this.models.copilotWorkspace.listFiles(workspaceId, pagination),
this.models.copilotWorkspace.countFiles(workspaceId),
]);
}
async addWorkspaceFileEmbeddingQueue(
file: Jobs['copilot.workspace.embedding.files']
) {
if (!this.supportEmbedding) return;
async queueFileEmbedding(file: Jobs['copilot.embedding.files']) {
const { userId, workspaceId, blobId, fileId, fileName } = file;
await this.queue.add('copilot.workspace.embedding.files', {
await this.queue.add('copilot.embedding.files', {
userId,
workspaceId,
blobId,
@@ -101,10 +93,7 @@ export class CopilotWorkspaceService implements OnApplicationBootstrap {
});
}
async removeWorkspaceFile(workspaceId: string, fileId: string) {
return await this.models.copilotWorkspace.removeWorkspaceFile(
workspaceId,
fileId
);
async removeFile(workspaceId: string, fileId: string) {
return await this.models.copilotWorkspace.removeFile(workspaceId, fileId);
}
}
@@ -13,15 +13,6 @@ declare global {
jobId: string;
};
}
interface Jobs {
'copilot.workspace.embedding.files': {
userId: string;
workspaceId: string;
blobId: string;
fileId: string;
fileName: string;
};
}
}
@ObjectType('CopilotWorkspaceIgnoredDoc')