Files
AFFiNE-Mirror/packages/backend/server/src/plugins/copilot/cron.ts
T
DarkSky 06b3d020fa 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 -->
2026-08-16 08:04:19 +08:00

83 lines
2.2 KiB
TypeScript

import { Injectable, Logger } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import { JobQueue, OneDay, OnJob } from '../../base';
import { Models } from '../../models';
import { CopilotTranscriptionRetryService } from './transcript/retry';
const BACKGROUND_COPILOT_JOB_PRIORITY = 100;
declare global {
interface Jobs {
'copilot.session.cleanupEmptySessions': {};
'copilot.session.generateMissingTitles': {};
}
}
@Injectable()
export class CopilotCronJobs {
private readonly logger = new Logger(CopilotCronJobs.name);
constructor(
private readonly models: Models,
private readonly jobs: JobQueue,
private readonly transcriptRetry: CopilotTranscriptionRetryService
) {}
@Cron(CronExpression.EVERY_MINUTE)
async reconcileTranscriptDispatches() {
await this.transcriptRetry.reconcileDispatches();
}
@Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT)
async dailyCleanupJob() {
await this.jobs.add(
'copilot.session.cleanupEmptySessions',
{},
{ jobId: 'daily-copilot-cleanup-empty-sessions' }
);
await this.jobs.add(
'copilot.session.generateMissingTitles',
{},
{ jobId: 'daily-copilot-generate-missing-titles' }
);
}
async triggerGenerateMissingTitles() {
await this.jobs.add(
'copilot.session.generateMissingTitles',
{},
{ jobId: 'trigger-copilot-generate-missing-titles' }
);
}
@OnJob('copilot.session.cleanupEmptySessions')
async cleanupEmptySessions() {
const { removed, cleaned } =
await this.models.copilotSession.cleanupEmptySessions(
new Date(Date.now() - OneDay)
);
this.logger.log(
`Cleanup completed: ${removed} sessions deleted, ${cleaned} sessions marked as deleted`
);
}
@OnJob('copilot.session.generateMissingTitles')
async generateMissingTitles() {
const sessions = await this.models.copilotSession.toBeGenerateTitle();
for (const session of sessions) {
await this.jobs.add(
'copilot.session.generateTitle',
{ sessionId: session.id },
{ priority: BACKGROUND_COPILOT_JOB_PRIORITY }
);
}
this.logger.log(
`Scheduled title generation for ${sessions.length} sessions`
);
}
}