mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-20 03:26:47 +08:00
feat(core): migrate more pull to realtime (#14936)
#### PR Dependency Tree * **PR #14936** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Consolidated realtime subscription patterns for consistent, more reliable live updates across comments, notifications, transcription tasks, and embedding progress. * Standardized realtime room naming and subscription keys for deterministic delivery. * **New Features** * Introduced a reusable live-query mechanism powering realtime snapshot + event workflows used by comments, notifications, transcript tasks, and embedding progress. * **Tests** * Added tests covering live-query behavior and deterministic subscription key generation. [](https://app.coderabbit.ai/change-stack/toeverything/AFFiNE/pull/14936) <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -3,15 +3,17 @@ import { z } from 'zod';
|
||||
|
||||
import { OnEvent } from '../../../base';
|
||||
import { AccessController } from '../../../core/permission';
|
||||
import type {
|
||||
import {
|
||||
RealtimePublisher,
|
||||
RealtimeRegistry,
|
||||
realtimeWorkspaceEmbeddingProgressRoom,
|
||||
registerRealtimeLiveQuery,
|
||||
} from '../../../core/realtime';
|
||||
import { Models } from '../../../models';
|
||||
import { CopilotContextService } from './service';
|
||||
|
||||
export function workspaceEmbeddingRoom(workspaceId: string) {
|
||||
return `workspace:${workspaceId}:embedding-progress`;
|
||||
return realtimeWorkspaceEmbeddingProgressRoom(workspaceId);
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
@@ -27,27 +29,28 @@ export class CopilotEmbeddingRealtimeProvider implements OnModuleInit {
|
||||
onModuleInit() {
|
||||
const input = z.object({ workspaceId: z.string() });
|
||||
|
||||
this.registry?.registerRequest({
|
||||
name: 'workspace.embedding.progress.get',
|
||||
input,
|
||||
handle: async (user, payload) => {
|
||||
await this.assertCopilot(user.id, payload.workspaceId);
|
||||
if (!this.context.canEmbedding) {
|
||||
return { total: 0, embedded: 0 };
|
||||
}
|
||||
return await this.models.copilotWorkspace.getEmbeddingStatus(
|
||||
payload.workspaceId
|
||||
);
|
||||
registerRealtimeLiveQuery(this.registry, {
|
||||
request: {
|
||||
name: 'workspace.embedding.progress.get',
|
||||
input,
|
||||
handle: async (user, payload) => {
|
||||
await this.assertCopilot(user.id, payload.workspaceId);
|
||||
if (!this.context.canEmbedding) {
|
||||
return { total: 0, embedded: 0 };
|
||||
}
|
||||
return await this.models.copilotWorkspace.getEmbeddingStatus(
|
||||
payload.workspaceId
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
this.registry?.registerTopic({
|
||||
name: 'workspace.embedding.progress.changed',
|
||||
input,
|
||||
authorize: async (user, payload) => {
|
||||
await this.assertCopilot(user.id, payload.workspaceId);
|
||||
topic: {
|
||||
name: 'workspace.embedding.progress.changed',
|
||||
input,
|
||||
authorize: async (user, payload) => {
|
||||
await this.assertCopilot(user.id, payload.workspaceId);
|
||||
},
|
||||
room: (_user, payload) => workspaceEmbeddingRoom(payload.workspaceId),
|
||||
},
|
||||
room: (_user, payload) => workspaceEmbeddingRoom(payload.workspaceId),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,12 @@ import { z } from 'zod';
|
||||
|
||||
import { CopilotTranscriptionJobNotFound } from '../../../base';
|
||||
import { AccessController } from '../../../core/permission';
|
||||
import type { RealtimeRegistry } from '../../../core/realtime';
|
||||
import { CopilotTranscriptionService, transcriptTaskRoom } from './service';
|
||||
import {
|
||||
type RealtimeRegistry,
|
||||
realtimeTranscriptTaskRoom,
|
||||
registerRealtimeLiveQuery,
|
||||
} from '../../../core/realtime';
|
||||
import { CopilotTranscriptionService } from './service';
|
||||
|
||||
@Injectable()
|
||||
export class CopilotTranscriptRealtimeProvider implements OnModuleInit {
|
||||
@@ -15,47 +19,51 @@ export class CopilotTranscriptRealtimeProvider implements OnModuleInit {
|
||||
) {}
|
||||
|
||||
onModuleInit() {
|
||||
this.registry?.registerRequest({
|
||||
name: 'copilot.transcript.task.get',
|
||||
input: z
|
||||
.object({
|
||||
workspaceId: z.string(),
|
||||
blobId: z.string().optional(),
|
||||
taskId: z.string().optional(),
|
||||
})
|
||||
.refine(input => input.blobId || input.taskId),
|
||||
handle: async (user, input) => {
|
||||
await this.assertCopilot(user.id, input.workspaceId);
|
||||
return {
|
||||
task: await this.transcript.queryTask(
|
||||
user.id,
|
||||
input.workspaceId,
|
||||
input.taskId,
|
||||
input.blobId
|
||||
),
|
||||
};
|
||||
},
|
||||
const requestInput = z
|
||||
.object({
|
||||
workspaceId: z.string(),
|
||||
blobId: z.string().optional(),
|
||||
taskId: z.string().optional(),
|
||||
})
|
||||
.refine(input => input.blobId || input.taskId);
|
||||
const topicInput = z.object({
|
||||
workspaceId: z.string(),
|
||||
taskId: z.string(),
|
||||
});
|
||||
|
||||
this.registry?.registerTopic({
|
||||
name: 'copilot.transcript.task.changed',
|
||||
input: z.object({
|
||||
workspaceId: z.string(),
|
||||
taskId: z.string(),
|
||||
}),
|
||||
authorize: async (user, input) => {
|
||||
await this.assertCopilot(user.id, input.workspaceId);
|
||||
const task = await this.transcript.queryTask(
|
||||
user.id,
|
||||
input.workspaceId,
|
||||
input.taskId
|
||||
);
|
||||
if (!task) {
|
||||
throw new CopilotTranscriptionJobNotFound();
|
||||
}
|
||||
registerRealtimeLiveQuery(this.registry, {
|
||||
request: {
|
||||
name: 'copilot.transcript.task.get',
|
||||
input: requestInput,
|
||||
handle: async (user, input) => {
|
||||
await this.assertCopilot(user.id, input.workspaceId);
|
||||
return {
|
||||
task: await this.transcript.queryTask(
|
||||
user.id,
|
||||
input.workspaceId,
|
||||
input.taskId,
|
||||
input.blobId
|
||||
),
|
||||
};
|
||||
},
|
||||
},
|
||||
topic: {
|
||||
name: 'copilot.transcript.task.changed',
|
||||
input: topicInput,
|
||||
authorize: async (user, input) => {
|
||||
await this.assertCopilot(user.id, input.workspaceId);
|
||||
const task = await this.transcript.queryTask(
|
||||
user.id,
|
||||
input.workspaceId,
|
||||
input.taskId
|
||||
);
|
||||
if (!task) {
|
||||
throw new CopilotTranscriptionJobNotFound();
|
||||
}
|
||||
},
|
||||
room: (_user, input) =>
|
||||
realtimeTranscriptTaskRoom(input.workspaceId, input.taskId),
|
||||
},
|
||||
room: (_user, input) =>
|
||||
transcriptTaskRoom(input.workspaceId, input.taskId),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,10 @@ import {
|
||||
OnJob,
|
||||
sniffMime,
|
||||
} from '../../../base';
|
||||
import type { RealtimePublisher } from '../../../core/realtime';
|
||||
import {
|
||||
type RealtimePublisher,
|
||||
realtimeTranscriptTaskRoom,
|
||||
} from '../../../core/realtime';
|
||||
import { Models } from '../../../models';
|
||||
import { CopilotAccessPolicy } from '../access';
|
||||
import { PromptService } from '../prompt';
|
||||
@@ -33,10 +36,6 @@ const TRANSCRIPT_ACTION_ID = 'transcript.audio.gemini';
|
||||
const TRANSCRIPT_ACTION_VERSION = 'v1';
|
||||
const TRANSCRIPT_STRATEGY = 'gemini';
|
||||
|
||||
export function transcriptTaskRoom(workspaceId: string, taskId: string) {
|
||||
return `copilot:transcript:${workspaceId}:${taskId}`;
|
||||
}
|
||||
|
||||
export type TranscriptionJob = {
|
||||
id: string;
|
||||
status: AiJobStatus;
|
||||
@@ -468,7 +467,7 @@ export class CopilotTranscriptionService {
|
||||
'copilot.transcript.task.changed',
|
||||
{ workspaceId, taskId },
|
||||
{ taskId, status, error },
|
||||
{ room: transcriptTaskRoom(workspaceId, taskId) }
|
||||
{ room: realtimeTranscriptTaskRoom(workspaceId, taskId) }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user