mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-18 18:41:52 +08:00
feat(server): realtime handle & migration (#15487)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Retry failed Copilot transcription tasks in real time. * Retried tasks resume processing and report updated status. * Managed Copilot provider models can be omitted to use provider defaults. * **Bug Fixes** * Improved handling of incomplete BYOK profiles, including safe replacement of legacy records. * Duplicate profile creation now returns a clear validation error. * Improved transcript processing reliability by preventing duplicate or stale dispatches. * **Migration** * Consolidated legacy managed-provider settings while preserving existing profiles and defaults. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
+32
-15
@@ -1,5 +1,4 @@
|
||||
import {
|
||||
retryTranscriptTaskMutation,
|
||||
settleTranscriptTaskMutation,
|
||||
submitTranscriptTaskMutation,
|
||||
} from '@affine/graphql';
|
||||
@@ -22,7 +21,10 @@ function createStore(
|
||||
gql: ReturnType<typeof vi.fn>,
|
||||
getAudioTranscriptionInput: () => Promise<AudioTranscriptionInput> = async () => ({
|
||||
files: [],
|
||||
})
|
||||
}),
|
||||
realtimeRequest: ReturnType<typeof vi.fn> = vi
|
||||
.fn()
|
||||
.mockResolvedValue({ task: { id: 'task-2' } })
|
||||
) {
|
||||
const framework = new Framework();
|
||||
const server = {
|
||||
@@ -31,7 +33,7 @@ function createStore(
|
||||
},
|
||||
};
|
||||
const realtime = {
|
||||
request: vi.fn().mockResolvedValue({ task: { id: 'task-2' } }),
|
||||
request: realtimeRequest,
|
||||
subscribe: vi.fn(),
|
||||
};
|
||||
framework
|
||||
@@ -67,9 +69,16 @@ describe('AudioTranscriptionJobStore transcript task API', () => {
|
||||
const gql = vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce({ submitTranscriptTask: { id: 'task-1' } })
|
||||
.mockResolvedValueOnce({ retryTranscriptTask: { id: 'task-2' } })
|
||||
.mockResolvedValueOnce({ settleTranscriptTask: { id: 'task-2' } });
|
||||
const store = createStore(gql, async () => ({ files: [file] }));
|
||||
const realtimeRequest = vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce({ task: { id: 'task-2', status: 'running' } })
|
||||
.mockResolvedValueOnce({ task: { id: 'task-2' } });
|
||||
const store = createStore(
|
||||
gql,
|
||||
async () => ({ files: [file] }),
|
||||
realtimeRequest
|
||||
);
|
||||
|
||||
await store.submitTranscriptTask();
|
||||
await store.retryTranscriptTask('task-1');
|
||||
@@ -90,16 +99,6 @@ describe('AudioTranscriptionJobStore transcript task API', () => {
|
||||
);
|
||||
expect(gql).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
expect.objectContaining({
|
||||
query: retryTranscriptTaskMutation,
|
||||
variables: {
|
||||
workspaceId: 'workspace-1',
|
||||
taskId: 'task-1',
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(gql).toHaveBeenNthCalledWith(
|
||||
3,
|
||||
expect.objectContaining({
|
||||
query: settleTranscriptTaskMutation,
|
||||
variables: {
|
||||
@@ -108,5 +107,23 @@ describe('AudioTranscriptionJobStore transcript task API', () => {
|
||||
},
|
||||
})
|
||||
);
|
||||
expect(realtimeRequest).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
'copilot.transcript.task.retry',
|
||||
{
|
||||
workspaceId: 'workspace-1',
|
||||
taskId: 'task-1',
|
||||
}
|
||||
);
|
||||
expect(realtimeRequest).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
'copilot.transcript.task.get',
|
||||
{
|
||||
workspaceId: 'workspace-1',
|
||||
taskId: 'task-2',
|
||||
blobId: 'blob-1',
|
||||
},
|
||||
{ timeoutMs: 10000 }
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {
|
||||
retryTranscriptTaskMutation,
|
||||
settleTranscriptTaskMutation,
|
||||
submitTranscriptTaskMutation,
|
||||
type TranscriptionResultType,
|
||||
@@ -64,21 +63,14 @@ export class AudioTranscriptionJobStore extends Entity<{
|
||||
};
|
||||
|
||||
retryTranscriptTask = async (taskId: string) => {
|
||||
const graphqlService = this.graphqlService;
|
||||
if (!graphqlService) {
|
||||
throw new Error('No graphql service available');
|
||||
}
|
||||
const response = await graphqlService.gql({
|
||||
query: retryTranscriptTaskMutation,
|
||||
variables: {
|
||||
taskId,
|
||||
workspaceId: this.currentWorkspaceId,
|
||||
},
|
||||
});
|
||||
if (!response.retryTranscriptTask) {
|
||||
const response = await this.nbstoreService.realtime.request(
|
||||
'copilot.transcript.task.retry',
|
||||
{ taskId, workspaceId: this.currentWorkspaceId }
|
||||
);
|
||||
if (!response.task) {
|
||||
throw new Error('Failed to retry audio transcription');
|
||||
}
|
||||
return response.retryTranscriptTask;
|
||||
return response.task as TranscriptionResultType;
|
||||
};
|
||||
|
||||
getTranscriptTask = async (
|
||||
|
||||
Reference in New Issue
Block a user