Fix AI chat streaming persistence when command menu closes (#14854)
Closes [#1583](https://github.com/twentyhq/core-team-issues/issues/1583) - Removed `messageId` column from `File` table and its references in code (unrelated to this PR) - Updated AI chat to use React Context API with persistent provider in `CommandMenuContainer` - Converted all AI chat component states to regular Recoil atoms (no instance context needed) --------- Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
-32
@@ -1,32 +0,0 @@
|
||||
import { Command, CommandRunner } from 'nest-commander';
|
||||
|
||||
import {
|
||||
CLEANUP_ORPHANED_FILES_CRON_PATTERN,
|
||||
CleanupOrphanedFilesCronJob,
|
||||
} from 'src/engine/core-modules/file/crons/jobs/cleanup-orphaned-files.cron.job';
|
||||
import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decorators/message-queue.decorator';
|
||||
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
||||
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
|
||||
|
||||
@Command({
|
||||
name: 'cron:file:cleanup-orphaned-files',
|
||||
description: 'Starts a cron job to clean up orphaned files (no messageId)',
|
||||
})
|
||||
export class CleanupOrphanedFilesCronCommand extends CommandRunner {
|
||||
constructor(
|
||||
@InjectMessageQueue(MessageQueue.cronQueue)
|
||||
private readonly messageQueueService: MessageQueueService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
await this.messageQueueService.addCron<undefined>({
|
||||
jobName: CleanupOrphanedFilesCronJob.name,
|
||||
data: undefined,
|
||||
options: {
|
||||
repeat: { pattern: CLEANUP_ORPHANED_FILES_CRON_PATTERN },
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
|
||||
import { IsNull, LessThan, Repository } from 'typeorm';
|
||||
|
||||
import { SentryCronMonitor } from 'src/engine/core-modules/cron/sentry-cron-monitor.decorator';
|
||||
import { FileEntity } from 'src/engine/core-modules/file/entities/file.entity';
|
||||
import { FileMetadataService } from 'src/engine/core-modules/file/services/file-metadata.service';
|
||||
import { Process } from 'src/engine/core-modules/message-queue/decorators/process.decorator';
|
||||
import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator';
|
||||
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
|
||||
export const CLEANUP_ORPHANED_FILES_CRON_PATTERN = '0 2 * * *';
|
||||
|
||||
@Processor(MessageQueue.cronQueue)
|
||||
export class CleanupOrphanedFilesCronJob {
|
||||
constructor(
|
||||
@InjectRepository(Workspace)
|
||||
private readonly workspaceRepository: Repository<Workspace>,
|
||||
@InjectRepository(FileEntity)
|
||||
private readonly fileRepository: Repository<FileEntity>,
|
||||
private readonly fileMetadataService: FileMetadataService,
|
||||
) {}
|
||||
|
||||
@Process(CleanupOrphanedFilesCronJob.name)
|
||||
@SentryCronMonitor(
|
||||
CleanupOrphanedFilesCronJob.name,
|
||||
CLEANUP_ORPHANED_FILES_CRON_PATTERN,
|
||||
)
|
||||
async handle(): Promise<void> {
|
||||
const activeWorkspaces = await this.workspaceRepository.find({
|
||||
where: {
|
||||
activationStatus: WorkspaceActivationStatus.ACTIVE,
|
||||
},
|
||||
select: ['id'],
|
||||
});
|
||||
|
||||
if (activeWorkspaces.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000);
|
||||
|
||||
const orphanedFiles = await this.fileRepository.find({
|
||||
select: ['id', 'workspaceId', 'fullPath'],
|
||||
where: {
|
||||
messageId: IsNull(),
|
||||
createdAt: LessThan(oneHourAgo),
|
||||
},
|
||||
});
|
||||
|
||||
if (orphanedFiles.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const file of orphanedFiles) {
|
||||
await this.fileMetadataService
|
||||
.deleteFileById(file.id, file.workspaceId)
|
||||
.catch((error) => {
|
||||
throw new Error(
|
||||
`[${CleanupOrphanedFilesCronJob.name}] Cannot delete orphaned file - ${file.fullPath}: ${error.message}`,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,9 +19,6 @@ export class FileDTO {
|
||||
@Field()
|
||||
type: string;
|
||||
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
messageId?: string;
|
||||
|
||||
@Field(() => Date, { nullable: false })
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
} from 'typeorm';
|
||||
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AgentChatMessageEntity } from 'src/engine/metadata-modules/agent/agent-chat-message.entity';
|
||||
|
||||
@Entity('file')
|
||||
@Index('IDX_FILE_WORKSPACE_ID', ['workspaceId'])
|
||||
@@ -39,15 +38,6 @@ export class FileEntity {
|
||||
@JoinColumn({ name: 'workspaceId' })
|
||||
workspace: Relation<Workspace>;
|
||||
|
||||
@Column({ nullable: true, type: 'uuid' })
|
||||
messageId: string;
|
||||
|
||||
@ManyToOne(() => AgentChatMessageEntity, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn({ name: 'messageId' })
|
||||
message: Relation<AgentChatMessageEntity>;
|
||||
|
||||
@CreateDateColumn({ type: 'timestamptz' })
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
@@ -11,8 +11,6 @@ import { JwtModule } from 'src/engine/core-modules/jwt/jwt.module';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
|
||||
import { FileController } from './controllers/file.controller';
|
||||
import { CleanupOrphanedFilesCronCommand } from './crons/commands/cleanup-orphaned-files.cron.command';
|
||||
import { CleanupOrphanedFilesCronJob } from './crons/jobs/cleanup-orphaned-files.cron.job';
|
||||
import { FileEntity } from './entities/file.entity';
|
||||
import { FileUploadService } from './file-upload/services/file-upload.service';
|
||||
import { FileResolver } from './resolvers/file.resolver';
|
||||
@@ -34,11 +32,9 @@ import { FileService } from './services/file.service';
|
||||
FileWorkspaceMemberListener,
|
||||
FileWorkspaceFolderDeletionJob,
|
||||
FileDeletionJob,
|
||||
CleanupOrphanedFilesCronJob,
|
||||
CleanupOrphanedFilesCronCommand,
|
||||
FileUploadService,
|
||||
],
|
||||
exports: [FileService, FileMetadataService, CleanupOrphanedFilesCronCommand],
|
||||
exports: [FileService, FileMetadataService],
|
||||
controllers: [FileController],
|
||||
})
|
||||
export class FileModule {}
|
||||
|
||||
Reference in New Issue
Block a user