mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-06 03:49:56 +08:00
fix(server): realtime loading (#14959)
#### PR Dependency Tree * **PR #14959** 👈 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** * Rewired realtime and copilot services to require their runtime dependencies, improving reliability and removing nullable/optional runtime paths. * **Tests** * Centralized service creation in tests with helper factories and added checks ensuring realtime dependency injection is configured as expected. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/toeverything/AFFiNE/pull/14959) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -137,6 +137,21 @@ function createSuccessfulTranscriptBridge(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createCopilotTranscriptionService(...deps: unknown[]) {
|
||||||
|
return new CopilotTranscriptionService(
|
||||||
|
deps[0] as never,
|
||||||
|
deps[1] as never,
|
||||||
|
deps[2] as never,
|
||||||
|
deps[3] as never,
|
||||||
|
deps[4] as never,
|
||||||
|
deps[5] as never,
|
||||||
|
(deps[6] ?? {
|
||||||
|
assertQuotaOrByok: Sinon.stub().resolves(undefined),
|
||||||
|
}) as never,
|
||||||
|
(deps[7] ?? { publish: Sinon.stub() }) as never
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
test('queryTask hides ready transcript task result until settlement', async t => {
|
test('queryTask hides ready transcript task result until settlement', async t => {
|
||||||
const payload = TranscriptPayloadSchema.parse({
|
const payload = TranscriptPayloadSchema.parse({
|
||||||
infos: [
|
infos: [
|
||||||
@@ -148,7 +163,7 @@ test('queryTask hides ready transcript task result until settlement', async t =>
|
|||||||
],
|
],
|
||||||
normalizedTranscript: '00:00:05 A: Kickoff',
|
normalizedTranscript: '00:00:05 A: Kickoff',
|
||||||
});
|
});
|
||||||
const service = new CopilotTranscriptionService(
|
const service = createCopilotTranscriptionService(
|
||||||
{
|
{
|
||||||
copilotTranscriptTask: {
|
copilotTranscriptTask: {
|
||||||
getWithUser: Sinon.stub().resolves({
|
getWithUser: Sinon.stub().resolves({
|
||||||
@@ -181,7 +196,7 @@ test('settleTask unlocks ready transcript task result idempotently', async t =>
|
|||||||
status: 'settled',
|
status: 'settled',
|
||||||
protectedResult: payload,
|
protectedResult: payload,
|
||||||
});
|
});
|
||||||
const service = new CopilotTranscriptionService(
|
const service = createCopilotTranscriptionService(
|
||||||
{
|
{
|
||||||
copilotTranscriptTask: {
|
copilotTranscriptTask: {
|
||||||
getWithUser: Sinon.stub().resolves({
|
getWithUser: Sinon.stub().resolves({
|
||||||
@@ -216,7 +231,7 @@ test('settleTask checks copilot quota before unlocking ready task', async t => {
|
|||||||
protectedResult: payload,
|
protectedResult: payload,
|
||||||
});
|
});
|
||||||
const assertQuotaOrByok = Sinon.stub().rejects(new Error('quota exceeded'));
|
const assertQuotaOrByok = Sinon.stub().rejects(new Error('quota exceeded'));
|
||||||
const service = new CopilotTranscriptionService(
|
const service = createCopilotTranscriptionService(
|
||||||
{
|
{
|
||||||
copilotTranscriptTask: {
|
copilotTranscriptTask: {
|
||||||
getWithUser: Sinon.stub().resolves({
|
getWithUser: Sinon.stub().resolves({
|
||||||
@@ -248,7 +263,7 @@ test('settleTask checks copilot quota before unlocking ready task', async t => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('retryTask rejects ready transcript tasks', async t => {
|
test('retryTask rejects ready transcript tasks', async t => {
|
||||||
const service = new CopilotTranscriptionService(
|
const service = createCopilotTranscriptionService(
|
||||||
{
|
{
|
||||||
copilotTranscriptTask: {
|
copilotTranscriptTask: {
|
||||||
getWithUser: Sinon.stub().resolves({
|
getWithUser: Sinon.stub().resolves({
|
||||||
@@ -272,7 +287,7 @@ test('retryTask rejects ready transcript tasks', async t => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('retryTask rejects settled transcript tasks', async t => {
|
test('retryTask rejects settled transcript tasks', async t => {
|
||||||
const service = new CopilotTranscriptionService(
|
const service = createCopilotTranscriptionService(
|
||||||
{
|
{
|
||||||
copilotTranscriptTask: {
|
copilotTranscriptTask: {
|
||||||
getWithUser: Sinon.stub().resolves({
|
getWithUser: Sinon.stub().resolves({
|
||||||
@@ -306,7 +321,7 @@ test('retryTask reuses failed task and queues a new action attempt', async t =>
|
|||||||
summaryJson: null,
|
summaryJson: null,
|
||||||
providerMeta: { provider: 'gemini', model: 'gemini-2.5-flash' },
|
providerMeta: { provider: 'gemini', model: 'gemini-2.5-flash' },
|
||||||
});
|
});
|
||||||
const service = new CopilotTranscriptionService(
|
const service = createCopilotTranscriptionService(
|
||||||
{
|
{
|
||||||
copilotTranscriptTask: {
|
copilotTranscriptTask: {
|
||||||
getWithUser: Sinon.stub().resolves({
|
getWithUser: Sinon.stub().resolves({
|
||||||
@@ -352,7 +367,7 @@ test('retryTask prechecks quota or BYOK before queueing provider work', async t
|
|||||||
const payload = TranscriptPayloadSchema.parse({
|
const payload = TranscriptPayloadSchema.parse({
|
||||||
normalizedTranscript: '00:00:05 A: Kickoff',
|
normalizedTranscript: '00:00:05 A: Kickoff',
|
||||||
});
|
});
|
||||||
const service = new CopilotTranscriptionService(
|
const service = createCopilotTranscriptionService(
|
||||||
{
|
{
|
||||||
copilotTranscriptTask: {
|
copilotTranscriptTask: {
|
||||||
getWithUser: Sinon.stub().resolves({
|
getWithUser: Sinon.stub().resolves({
|
||||||
@@ -391,7 +406,7 @@ for (const status of ['ready', 'settled']) {
|
|||||||
test(`submitTask allows a new task for the same blob after ${status} task`, async t => {
|
test(`submitTask allows a new task for the same blob after ${status} task`, async t => {
|
||||||
const createdTasks: unknown[] = [];
|
const createdTasks: unknown[] = [];
|
||||||
const queuedJobs: unknown[] = [];
|
const queuedJobs: unknown[] = [];
|
||||||
const service = new CopilotTranscriptionService(
|
const service = createCopilotTranscriptionService(
|
||||||
{
|
{
|
||||||
copilotTranscriptTask: {
|
copilotTranscriptTask: {
|
||||||
getWithUser: Sinon.stub().resolves({
|
getWithUser: Sinon.stub().resolves({
|
||||||
@@ -439,7 +454,7 @@ for (const status of ['ready', 'settled']) {
|
|||||||
test('submitTask prechecks quota or BYOK before persisting uploads', async t => {
|
test('submitTask prechecks quota or BYOK before persisting uploads', async t => {
|
||||||
const assertQuotaOrByok = Sinon.stub().rejects(new Error('quota exceeded'));
|
const assertQuotaOrByok = Sinon.stub().rejects(new Error('quota exceeded'));
|
||||||
const resolveTranscriptionModel = Sinon.stub().resolves('gemini-2.5-flash');
|
const resolveTranscriptionModel = Sinon.stub().resolves('gemini-2.5-flash');
|
||||||
const service = new CopilotTranscriptionService(
|
const service = createCopilotTranscriptionService(
|
||||||
{
|
{
|
||||||
copilotTranscriptTask: {
|
copilotTranscriptTask: {
|
||||||
getWithUser: Sinon.stub().resolves(null),
|
getWithUser: Sinon.stub().resolves(null),
|
||||||
@@ -468,7 +483,7 @@ test('submitTask prechecks quota or BYOK before persisting uploads', async t =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('submitTask rejects unavailable transcript strategy', async t => {
|
test('submitTask rejects unavailable transcript strategy', async t => {
|
||||||
const service = new CopilotTranscriptionService(
|
const service = createCopilotTranscriptionService(
|
||||||
{
|
{
|
||||||
copilotTranscriptTask: {
|
copilotTranscriptTask: {
|
||||||
getWithUser: Sinon.stub().resolves(null),
|
getWithUser: Sinon.stub().resolves(null),
|
||||||
@@ -515,7 +530,7 @@ test('transcriptTask runs native transcript recipe through action bridge when av
|
|||||||
const bridgeInputs: unknown[] = [];
|
const bridgeInputs: unknown[] = [];
|
||||||
const markRunning = Sinon.stub().resolves({ id: 'task-1' });
|
const markRunning = Sinon.stub().resolves({ id: 'task-1' });
|
||||||
const complete = Sinon.stub().resolves({ id: 'task-1', status: 'ready' });
|
const complete = Sinon.stub().resolves({ id: 'task-1', status: 'ready' });
|
||||||
const service = new CopilotTranscriptionService(
|
const service = createCopilotTranscriptionService(
|
||||||
{
|
{
|
||||||
copilotTranscriptTask: {
|
copilotTranscriptTask: {
|
||||||
get: Sinon.stub().resolves({
|
get: Sinon.stub().resolves({
|
||||||
@@ -586,7 +601,7 @@ test('transcriptTask fails task when native action bridge reports an error event
|
|||||||
normalizedTranscript: '00:00:05 A: Kickoff',
|
normalizedTranscript: '00:00:05 A: Kickoff',
|
||||||
});
|
});
|
||||||
const complete = Sinon.stub().resolves({ id: 'task-1', status: 'failed' });
|
const complete = Sinon.stub().resolves({ id: 'task-1', status: 'failed' });
|
||||||
const service = new CopilotTranscriptionService(
|
const service = createCopilotTranscriptionService(
|
||||||
{
|
{
|
||||||
copilotTranscriptTask: {
|
copilotTranscriptTask: {
|
||||||
get: Sinon.stub().resolves({
|
get: Sinon.stub().resolves({
|
||||||
|
|||||||
@@ -185,7 +185,10 @@ export function buildAppModule(env: Env) {
|
|||||||
.useIf(
|
.useIf(
|
||||||
() => env.flavors.sync || env.flavors.front,
|
() => env.flavors.sync || env.flavors.front,
|
||||||
SyncModule,
|
SyncModule,
|
||||||
TelemetryModule,
|
TelemetryModule
|
||||||
|
)
|
||||||
|
.useIf(
|
||||||
|
() => !env.flavors.graphql && (env.flavors.sync || env.flavors.front),
|
||||||
CopilotRealtimeModule
|
CopilotRealtimeModule
|
||||||
)
|
)
|
||||||
// graphql server only
|
// graphql server only
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { Injectable, OnModuleInit, Optional } from '@nestjs/common';
|
import { Injectable, OnModuleInit } from '@nestjs/common';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { decodeWithJson, encodeWithJson } from '../../base/graphql';
|
import { decodeWithJson, encodeWithJson } from '../../base/graphql';
|
||||||
import { AccessController } from '../permission';
|
import { AccessController } from '../permission';
|
||||||
import {
|
import {
|
||||||
realtimeCommentRoom,
|
realtimeCommentRoom,
|
||||||
type RealtimePublisher,
|
RealtimePublisher,
|
||||||
type RealtimeRegistry,
|
RealtimeRegistry,
|
||||||
registerRealtimeLiveQuery,
|
registerRealtimeLiveQuery,
|
||||||
} from '../realtime';
|
} from '../realtime';
|
||||||
import type { CommentCursor } from './resolver';
|
import type { CommentCursor } from './resolver';
|
||||||
@@ -21,7 +21,7 @@ export class CommentRealtimeProvider implements OnModuleInit {
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly service: CommentService,
|
private readonly service: CommentService,
|
||||||
private readonly ac: AccessController,
|
private readonly ac: AccessController,
|
||||||
@Optional() private readonly registry?: RealtimeRegistry
|
private readonly registry: RealtimeRegistry
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
onModuleInit() {
|
onModuleInit() {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { randomUUID } from 'node:crypto';
|
import { randomUUID } from 'node:crypto';
|
||||||
|
|
||||||
import { Optional } from '@nestjs/common';
|
|
||||||
import {
|
import {
|
||||||
Args,
|
Args,
|
||||||
Mutation,
|
Mutation,
|
||||||
@@ -27,7 +26,7 @@ import { Comment, DocMode, Models, Reply } from '../../models';
|
|||||||
import { CurrentUser } from '../auth/session';
|
import { CurrentUser } from '../auth/session';
|
||||||
import { ServerFeature, ServerService } from '../config';
|
import { ServerFeature, ServerService } from '../config';
|
||||||
import { AccessController, DocAction } from '../permission';
|
import { AccessController, DocAction } from '../permission';
|
||||||
import type { RealtimePublisher } from '../realtime';
|
import { RealtimePublisher } from '../realtime';
|
||||||
import { CommentAttachmentStorage } from '../storage';
|
import { CommentAttachmentStorage } from '../storage';
|
||||||
import { UserType } from '../user';
|
import { UserType } from '../user';
|
||||||
import { WorkspaceType } from '../workspaces';
|
import { WorkspaceType } from '../workspaces';
|
||||||
@@ -60,7 +59,7 @@ export class CommentResolver {
|
|||||||
private readonly queue: JobQueue,
|
private readonly queue: JobQueue,
|
||||||
private readonly models: Models,
|
private readonly models: Models,
|
||||||
private readonly server: ServerService,
|
private readonly server: ServerService,
|
||||||
@Optional() private readonly realtime?: RealtimePublisher
|
private readonly realtime: RealtimePublisher
|
||||||
) {
|
) {
|
||||||
// enable comment feature by default
|
// enable comment feature by default
|
||||||
this.server.enableFeature(ServerFeature.Comment);
|
this.server.enableFeature(ServerFeature.Comment);
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { Injectable, OnModuleInit, Optional } from '@nestjs/common';
|
import { Injectable, OnModuleInit } from '@nestjs/common';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
realtimeNotificationRoom,
|
realtimeNotificationRoom,
|
||||||
type RealtimeRegistry,
|
RealtimeRegistry,
|
||||||
registerRealtimeLiveQuery,
|
registerRealtimeLiveQuery,
|
||||||
} from '../realtime';
|
} from '../realtime';
|
||||||
import { NotificationService } from './service';
|
import { NotificationService } from './service';
|
||||||
@@ -12,7 +12,7 @@ import { NotificationService } from './service';
|
|||||||
export class NotificationRealtimeProvider implements OnModuleInit {
|
export class NotificationRealtimeProvider implements OnModuleInit {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly service: NotificationService,
|
private readonly service: NotificationService,
|
||||||
@Optional() private readonly registry?: RealtimeRegistry
|
private readonly registry: RealtimeRegistry
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
onModuleInit() {
|
onModuleInit() {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Injectable, Logger, Optional } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
import { Prisma } from '@prisma/client';
|
import { Prisma } from '@prisma/client';
|
||||||
|
|
||||||
import { NotificationNotFound, PaginationInput, URLHelper } from '../../base';
|
import { NotificationNotFound, PaginationInput, URLHelper } from '../../base';
|
||||||
@@ -17,8 +17,7 @@ import {
|
|||||||
} from '../../models';
|
} from '../../models';
|
||||||
import { DocReader } from '../doc';
|
import { DocReader } from '../doc';
|
||||||
import { Mailer } from '../mail';
|
import { Mailer } from '../mail';
|
||||||
import type { RealtimePublisher } from '../realtime';
|
import { realtimeNotificationRoom, RealtimePublisher } from '../realtime';
|
||||||
import { realtimeNotificationRoom } from '../realtime';
|
|
||||||
import { generateDocPath } from '../utils/doc';
|
import { generateDocPath } from '../utils/doc';
|
||||||
import {
|
import {
|
||||||
generateWorkspaceSettingsPath,
|
generateWorkspaceSettingsPath,
|
||||||
@@ -34,7 +33,7 @@ export class NotificationService {
|
|||||||
private readonly docReader: DocReader,
|
private readonly docReader: DocReader,
|
||||||
private readonly mailer: Mailer,
|
private readonly mailer: Mailer,
|
||||||
private readonly url: URLHelper,
|
private readonly url: URLHelper,
|
||||||
@Optional() private readonly realtime?: RealtimePublisher
|
private readonly realtime: RealtimePublisher
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async cleanExpiredNotifications() {
|
async cleanExpiredNotifications() {
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import { z } from 'zod';
|
|||||||
import type { CopilotTranscriptionReader } from '../../../plugins/copilot/transcript';
|
import type { CopilotTranscriptionReader } from '../../../plugins/copilot/transcript';
|
||||||
import { CopilotTranscriptRealtimeProvider } from '../../../plugins/copilot/transcript';
|
import { CopilotTranscriptRealtimeProvider } from '../../../plugins/copilot/transcript';
|
||||||
import type { CurrentUser } from '../../auth';
|
import type { CurrentUser } from '../../auth';
|
||||||
|
import { CommentRealtimeProvider } from '../../comment/realtime';
|
||||||
|
import { NotificationRealtimeProvider } from '../../notification/realtime';
|
||||||
import type { AccessController } from '../../permission';
|
import type { AccessController } from '../../permission';
|
||||||
import { RealtimeGateway } from '../gateway';
|
import { RealtimeGateway } from '../gateway';
|
||||||
import {
|
import {
|
||||||
@@ -194,6 +196,26 @@ test('registerRealtimeLiveQuery registers paired request and topic handlers', as
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('realtime providers expose runtime injection metadata for registry dependencies', t => {
|
||||||
|
t.true(
|
||||||
|
Reflect.getMetadata(
|
||||||
|
'design:paramtypes',
|
||||||
|
NotificationRealtimeProvider
|
||||||
|
).includes(RealtimeRegistry)
|
||||||
|
);
|
||||||
|
t.true(
|
||||||
|
Reflect.getMetadata('design:paramtypes', CommentRealtimeProvider).includes(
|
||||||
|
RealtimeRegistry
|
||||||
|
)
|
||||||
|
);
|
||||||
|
t.true(
|
||||||
|
Reflect.getMetadata(
|
||||||
|
'design:paramtypes',
|
||||||
|
CopilotTranscriptRealtimeProvider
|
||||||
|
).includes(RealtimeRegistry)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test('copilot transcript realtime provider registers task live query handlers', async t => {
|
test('copilot transcript realtime provider registers task live query handlers', async t => {
|
||||||
const registry = new RealtimeRegistry();
|
const registry = new RealtimeRegistry();
|
||||||
const assertions: unknown[] = [];
|
const assertions: unknown[] = [];
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { RealtimeRequestName, RealtimeTopicName } from '@affine/realtime';
|
import type { RealtimeRequestName, RealtimeTopicName } from '@affine/realtime';
|
||||||
|
|
||||||
import type { RealtimeRegistry } from './registry';
|
import { RealtimeRegistry } from './registry';
|
||||||
import type { RealtimeRequestHandler, RealtimeTopicHandler } from './types';
|
import type { RealtimeRequestHandler, RealtimeTopicHandler } from './types';
|
||||||
|
|
||||||
export type RealtimeLiveQueryDefinition<
|
export type RealtimeLiveQueryDefinition<
|
||||||
@@ -15,9 +15,9 @@ export function registerRealtimeLiveQuery<
|
|||||||
Request extends RealtimeRequestName,
|
Request extends RealtimeRequestName,
|
||||||
Topic extends RealtimeTopicName,
|
Topic extends RealtimeTopicName,
|
||||||
>(
|
>(
|
||||||
registry: RealtimeRegistry | undefined,
|
registry: RealtimeRegistry,
|
||||||
definition: RealtimeLiveQueryDefinition<Request, Topic>
|
definition: RealtimeLiveQueryDefinition<Request, Topic>
|
||||||
) {
|
) {
|
||||||
registry?.registerRequest(definition.request);
|
registry.registerRequest(definition.request);
|
||||||
registry?.registerTopic(definition.topic);
|
registry.registerTopic(definition.topic);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Injectable, OnModuleInit, Optional } from '@nestjs/common';
|
import { Injectable, OnModuleInit } from '@nestjs/common';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { OnEvent } from '../../../base';
|
import { OnEvent } from '../../../base';
|
||||||
@@ -22,8 +22,8 @@ export class CopilotEmbeddingRealtimeProvider implements OnModuleInit {
|
|||||||
private readonly ac: AccessController,
|
private readonly ac: AccessController,
|
||||||
private readonly models: Models,
|
private readonly models: Models,
|
||||||
private readonly context: CopilotContextService,
|
private readonly context: CopilotContextService,
|
||||||
@Optional() private readonly registry?: RealtimeRegistry,
|
private readonly registry: RealtimeRegistry,
|
||||||
@Optional() private readonly publisher?: RealtimePublisher
|
private readonly publisher: RealtimePublisher
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
onModuleInit() {
|
onModuleInit() {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { Injectable, OnModuleInit, Optional } from '@nestjs/common';
|
import { Injectable, OnModuleInit } from '@nestjs/common';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { CopilotTranscriptionJobNotFound } from '../../../base';
|
import { CopilotTranscriptionJobNotFound } from '../../../base';
|
||||||
import { AccessController } from '../../../core/permission';
|
import { AccessController } from '../../../core/permission';
|
||||||
import {
|
import {
|
||||||
type RealtimeRegistry,
|
RealtimeRegistry,
|
||||||
realtimeTranscriptTaskRoom,
|
realtimeTranscriptTaskRoom,
|
||||||
registerRealtimeLiveQuery,
|
registerRealtimeLiveQuery,
|
||||||
} from '../../../core/realtime';
|
} from '../../../core/realtime';
|
||||||
@@ -15,7 +15,7 @@ export class CopilotTranscriptRealtimeProvider implements OnModuleInit {
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly ac: AccessController,
|
private readonly ac: AccessController,
|
||||||
private readonly transcript: CopilotTranscriptionReader,
|
private readonly transcript: CopilotTranscriptionReader,
|
||||||
@Optional() private readonly registry?: RealtimeRegistry
|
private readonly registry: RealtimeRegistry
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
onModuleInit() {
|
onModuleInit() {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { BadRequestException, Injectable, Optional } from '@nestjs/common';
|
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||||
import { AiJobStatus } from '@prisma/client';
|
import { AiJobStatus } from '@prisma/client';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
sniffMime,
|
sniffMime,
|
||||||
} from '../../../base';
|
} from '../../../base';
|
||||||
import {
|
import {
|
||||||
type RealtimePublisher,
|
RealtimePublisher,
|
||||||
realtimeTranscriptTaskRoom,
|
realtimeTranscriptTaskRoom,
|
||||||
} from '../../../core/realtime';
|
} from '../../../core/realtime';
|
||||||
import { Models } from '../../../models';
|
import { Models } from '../../../models';
|
||||||
@@ -45,8 +45,8 @@ export class CopilotTranscriptionService {
|
|||||||
private readonly tasks: TaskPolicy,
|
private readonly tasks: TaskPolicy,
|
||||||
private readonly prompts: PromptService,
|
private readonly prompts: PromptService,
|
||||||
private readonly actionBridge: ActionRuntimeBridge,
|
private readonly actionBridge: ActionRuntimeBridge,
|
||||||
@Optional() private readonly access?: CopilotAccessPolicy,
|
private readonly access: CopilotAccessPolicy,
|
||||||
@Optional() private readonly realtime?: RealtimePublisher
|
private readonly realtime: RealtimePublisher
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
private parseTaskPayload(payload: unknown): TranscriptionPayloadV2 {
|
private parseTaskPayload(payload: unknown): TranscriptionPayloadV2 {
|
||||||
@@ -180,7 +180,7 @@ export class CopilotTranscriptionService {
|
|||||||
throw new CopilotTranscriptionJobExists();
|
throw new CopilotTranscriptionJobExists();
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.access?.assertQuotaOrByok({
|
await this.access.assertQuotaOrByok({
|
||||||
userId,
|
userId,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
featureKind: 'transcript',
|
featureKind: 'transcript',
|
||||||
@@ -234,7 +234,7 @@ export class CopilotTranscriptionService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.access?.assertQuotaOrByok({
|
await this.access.assertQuotaOrByok({
|
||||||
userId,
|
userId,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
featureKind: 'transcript',
|
featureKind: 'transcript',
|
||||||
@@ -282,7 +282,7 @@ export class CopilotTranscriptionService {
|
|||||||
return taskToJob(task);
|
return taskToJob(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.access?.assertQuotaOrByok({
|
await this.access.assertQuotaOrByok({
|
||||||
userId,
|
userId,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
featureKind: 'transcript',
|
featureKind: 'transcript',
|
||||||
@@ -412,7 +412,7 @@ export class CopilotTranscriptionService {
|
|||||||
status: AiJobStatus,
|
status: AiJobStatus,
|
||||||
error?: string
|
error?: string
|
||||||
) {
|
) {
|
||||||
this.realtime?.publish(
|
this.realtime.publish(
|
||||||
'copilot.transcript.task.changed',
|
'copilot.transcript.task.changed',
|
||||||
{ workspaceId, taskId },
|
{ workspaceId, taskId },
|
||||||
{ taskId, status, error },
|
{ taskId, status, error },
|
||||||
|
|||||||
Reference in New Issue
Block a user