feat(server): improve context error handle (#11342)

This commit is contained in:
darkskygit
2025-04-01 05:38:32 +00:00
parent 6ed9c33c33
commit dc67614d6d
5 changed files with 73 additions and 23 deletions
@@ -73,11 +73,18 @@ export class CopilotContextDocJob {
} }
@OnEvent('workspace.doc.embedding') @OnEvent('workspace.doc.embedding')
async addDocEmbeddingQueue(docs: Events['workspace.doc.embedding']) { async addDocEmbeddingQueue(
docs: Events['workspace.doc.embedding'],
contextId?: string
) {
if (!this.supportEmbedding) return; if (!this.supportEmbedding) return;
for (const { workspaceId, docId } of docs) { for (const { workspaceId, docId } of docs) {
await this.queue.add('doc.embedPendingDocs', { workspaceId, docId }); await this.queue.add('doc.embedPendingDocs', {
contextId,
workspaceId,
docId,
});
} }
} }
@@ -130,23 +137,24 @@ export class CopilotContextDocJob {
fileId, fileId,
chunkSize: total, chunkSize: total,
}); });
} catch (e: any) { } catch (error: any) {
const error = mapAnyError(e);
error.log('CopilotJob', {
workspaceId,
fileId,
});
this.event.emit('workspace.file.embed.failed', { this.event.emit('workspace.file.embed.failed', {
contextId, contextId,
fileId, fileId,
error: e.toString(), error: mapAnyError(error).message,
}); });
// passthrough error to job queue
throw error;
} }
} }
@OnJob('doc.embedPendingDocs') @OnJob('doc.embedPendingDocs')
async embedPendingDocs({ workspaceId, docId }: Jobs['doc.embedPendingDocs']) { async embedPendingDocs({
contextId,
workspaceId,
docId,
}: Jobs['doc.embedPendingDocs']) {
if (!this.supportEmbedding) return; if (!this.supportEmbedding) return;
try { try {
@@ -165,11 +173,16 @@ export class CopilotContextDocJob {
); );
} }
} }
} catch (e: any) { } catch (error: any) {
this.logger.error( if (contextId) {
`Failed to embed pending doc: ${workspaceId}::${docId}`, this.event.emit('workspace.doc.embed.failed', {
e contextId,
); docId,
});
}
// passthrough error to job queue
throw error;
} }
} }
} }
@@ -464,7 +464,8 @@ export class CopilotContextResolver {
options.docs.map(docId => ({ options.docs.map(docId => ({
workspaceId: session.workspaceId, workspaceId: session.workspaceId,
docId, docId,
})) })),
session.id
); );
} }
@@ -523,12 +524,10 @@ export class CopilotContextResolver {
try { try {
const record = await session.addDocRecord(options.docId); const record = await session.addDocRecord(options.docId);
await this.jobs.addDocEmbeddingQueue([ await this.jobs.addDocEmbeddingQueue(
{ [{ workspaceId: session.workspaceId, docId: options.docId }],
workspaceId: session.workspaceId, session.id
docId: options.docId, );
},
]);
return { ...record, status: record.status || null }; return { ...record, status: record.status || null };
} catch (e: any) { } catch (e: any) {
@@ -11,6 +11,7 @@ import {
import { import {
ContextConfig, ContextConfig,
ContextConfigSchema, ContextConfigSchema,
ContextDoc,
ContextEmbedStatus, ContextEmbedStatus,
ContextFile, ContextFile,
Models, Models,
@@ -148,6 +149,18 @@ export class CopilotContextService implements OnApplicationBootstrap {
return null; return null;
} }
@OnEvent('workspace.doc.embed.failed')
async onDocEmbedFailed({
contextId,
docId,
}: Events['workspace.doc.embed.failed']) {
const context = await this.get(contextId);
await context.saveDocRecord(docId, doc => ({
...(doc as ContextDoc),
status: ContextEmbedStatus.failed,
}));
}
@OnEvent('workspace.file.embed.finished') @OnEvent('workspace.file.embed.finished')
async onFileEmbedFinish({ async onFileEmbedFinish({
contextId, contextId,
@@ -217,6 +217,23 @@ export class ContextSession implements AsyncDisposable {
); );
} }
async saveDocRecord(
docId: string,
cb: (
record: Pick<ContextDoc, 'id' | 'status'> &
Partial<Omit<ContextDoc, 'id' | 'status'>>
) => ContextDoc
) {
const docs = [this.config.docs, ...this.config.categories.map(c => c.docs)]
.flat()
.filter(d => d.id === docId);
for (const doc of docs) {
Object.assign(doc, cb({ ...doc }));
}
await this.save();
}
async saveFileRecord( async saveFileRecord(
fileId: string, fileId: string,
cb: ( cb: (
@@ -10,11 +10,18 @@ declare global {
workspaceId: string; workspaceId: string;
docId: string; docId: string;
}>; }>;
'workspace.doc.embed.failed': {
contextId: string;
docId: string;
};
'workspace.file.embed.finished': { 'workspace.file.embed.finished': {
contextId: string; contextId: string;
fileId: string; fileId: string;
chunkSize: number; chunkSize: number;
}; };
'workspace.file.embed.failed': { 'workspace.file.embed.failed': {
contextId: string; contextId: string;
fileId: string; fileId: string;
@@ -23,6 +30,7 @@ declare global {
} }
interface Jobs { interface Jobs {
'doc.embedPendingDocs': { 'doc.embedPendingDocs': {
contextId?: string;
workspaceId: string; workspaceId: string;
docId: string; docId: string;
}; };