diff --git a/cortex-js/package.json b/cortex-js/package.json index 1b84807d3..5a462cae8 100644 --- a/cortex-js/package.json +++ b/cortex-js/package.json @@ -45,6 +45,7 @@ "@nestjs/event-emitter": "^2.0.4", "@nestjs/mapped-types": "*", "@nestjs/platform-express": "^10.0.0", + "@nestjs/sequelize": "^10.0.1", "@nestjs/swagger": "^7.3.1", "@terascope/fetch-github-release": "^0.8.8", "axios": "^1.6.8", @@ -60,9 +61,10 @@ "readline": "^1.3.0", "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1", + "sequelize": "^6.37.3", + "sequelize-typescript": "^2.1.6", "sqlite3": "^5.1.7", "systeminformation": "^5.22.11", - "typeorm": "^0.3.20", "ulid": "^2.3.0", "uuid": "^9.0.1", "whatwg-url": "^14.0.0", @@ -72,13 +74,13 @@ "@nestjs/cli": "^10.0.0", "@nestjs/schematics": "^10.0.0", "@nestjs/testing": "^10.0.0", - "@nestjs/typeorm": "^10.0.2", "@types/cli-progress": "^3.11.5", "@types/decompress": "^4.2.7", "@types/express": "^4.17.17", "@types/jest": "^29.5.2", "@types/js-yaml": "^4.0.9", "@types/node": "^20.12.9", + "@types/sequelize": "^4.28.20", "@types/supertest": "^6.0.2", "@types/update-notifier": "^6.0.8", "@types/uuid": "^9.0.8", diff --git a/cortex-js/src/command.module.ts b/cortex-js/src/command.module.ts index ad27d9b5f..667e13934 100644 --- a/cortex-js/src/command.module.ts +++ b/cortex-js/src/command.module.ts @@ -20,7 +20,6 @@ import { ModelRemoveCommand } from './infrastructure/commanders/models/model-rem import { RunCommand } from './infrastructure/commanders/shortcuts/run.command'; import { ModelUpdateCommand } from './infrastructure/commanders/models/model-update.command'; import { AssistantsModule } from './usecases/assistants/assistants.module'; -import { CliUsecasesModule } from './infrastructure/commanders/usecases/cli.usecases.module'; import { MessagesModule } from './usecases/messages/messages.module'; import { FileManagerModule } from './infrastructure/services/file-manager/file-manager.module'; import { PSCommand } from './infrastructure/commanders/ps.command'; @@ -34,6 +33,7 @@ import { EventEmitterModule } from '@nestjs/event-emitter'; import { DownloadManagerModule } from './infrastructure/services/download-manager/download-manager.module'; import { ServeStopCommand } from './infrastructure/commanders/sub-commands/serve-stop.command'; import { ContextModule } from './infrastructure/services/context/context.module'; +import { CliUsecasesModule } from './infrastructure/commanders/usecases/cli.usecases.module'; import { ExtensionsModule } from './extensions/extensions.module'; import { ConfigsCommand } from './infrastructure/commanders/configs.command'; import { EnginesCommand } from './infrastructure/commanders/engines.command'; @@ -46,6 +46,7 @@ import { EnginesListCommand } from './infrastructure/commanders/engines/engines- import { EnginesGetCommand } from './infrastructure/commanders/engines/engines-get.command'; import { EnginesInitCommand } from './infrastructure/commanders/engines/engines-init.command'; + @Module({ imports: [ ConfigModule.forRoot({ diff --git a/cortex-js/src/command.ts b/cortex-js/src/command.ts index ac21b8c56..9540cea96 100644 --- a/cortex-js/src/command.ts +++ b/cortex-js/src/command.ts @@ -39,7 +39,7 @@ async function bootstrap() { contextService!.set('sessionId', anonymousData?.sessionId); telemetryUseCase!.sendActivationEvent(TelemetrySource.CLI); telemetryUseCase!.sendCrashReport(); - return CommandFactory.runApplication(app); + await CommandFactory.runApplication(app); }); } diff --git a/cortex-js/src/infrastructure/database/mysql-database.providers.ts b/cortex-js/src/infrastructure/database/mysql-database.providers.ts deleted file mode 100644 index ff756d5a8..000000000 --- a/cortex-js/src/infrastructure/database/mysql-database.providers.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { databaseName } from '@/infrastructure/constants/cortex'; -import { DataSource } from 'typeorm'; -import { ThreadEntity } from '../entities/thread.entity'; -import { AssistantEntity } from '../entities/assistant.entity'; -import { MessageEntity } from '../entities/message.entity'; - -export const mysqlDatabaseProviders = [ - { - provide: 'DATA_SOURCE', - useFactory: async () => { - const dataSource = new DataSource({ - type: 'mysql', - host: 'localhost', - port: 3306, - username: 'root', - password: '', - database: databaseName, - entities: [ThreadEntity, AssistantEntity, MessageEntity], - synchronize: process.env.NODE_ENV !== 'production', - }); - - return dataSource.initialize(); - }, - }, -]; diff --git a/cortex-js/src/infrastructure/database/providers/assistant.providers.ts b/cortex-js/src/infrastructure/database/providers/assistant.providers.ts index fcd341209..02ed0c90d 100644 --- a/cortex-js/src/infrastructure/database/providers/assistant.providers.ts +++ b/cortex-js/src/infrastructure/database/providers/assistant.providers.ts @@ -1,11 +1,12 @@ import { AssistantEntity } from '@/infrastructure/entities/assistant.entity'; -import { DataSource } from 'typeorm'; +import { Sequelize } from 'sequelize-typescript'; export const assistantProviders = [ { provide: 'ASSISTANT_REPOSITORY', - useFactory: (dataSource: DataSource) => - dataSource.getRepository(AssistantEntity), + useFactory: async(sequelize: Sequelize) =>{ + return sequelize.getRepository(AssistantEntity); + }, inject: ['DATA_SOURCE'], }, ]; diff --git a/cortex-js/src/infrastructure/database/providers/message.providers.ts b/cortex-js/src/infrastructure/database/providers/message.providers.ts index 51730e5dc..aa6cc9261 100644 --- a/cortex-js/src/infrastructure/database/providers/message.providers.ts +++ b/cortex-js/src/infrastructure/database/providers/message.providers.ts @@ -1,11 +1,12 @@ -import { MessageEntity } from '@/infrastructure/entities/message.entity'; -import { DataSource } from 'typeorm'; +import { MessageEntity } from "@/infrastructure/entities/message.entity"; +import { Sequelize } from "sequelize-typescript"; export const messageProviders = [ { provide: 'MESSAGE_REPOSITORY', - useFactory: (dataSource: DataSource) => - dataSource.getRepository(MessageEntity), + useFactory: async(sequelize: Sequelize) =>{ + return sequelize.getRepository(MessageEntity); + }, inject: ['DATA_SOURCE'], }, ]; diff --git a/cortex-js/src/infrastructure/database/providers/thread.providers.ts b/cortex-js/src/infrastructure/database/providers/thread.providers.ts index fbe0c4eeb..8f91cb9a7 100644 --- a/cortex-js/src/infrastructure/database/providers/thread.providers.ts +++ b/cortex-js/src/infrastructure/database/providers/thread.providers.ts @@ -1,11 +1,13 @@ import { ThreadEntity } from '@/infrastructure/entities/thread.entity'; -import { DataSource } from 'typeorm'; +import { Sequelize } from 'sequelize-typescript'; export const threadProviders = [ { provide: 'THREAD_REPOSITORY', - useFactory: (dataSource: DataSource) => - dataSource.getRepository(ThreadEntity), + useFactory: async(sequelize: Sequelize) =>{ + return sequelize.getRepository(ThreadEntity); + }, inject: ['DATA_SOURCE'], }, ]; + diff --git a/cortex-js/src/infrastructure/database/sqlite-database.providers.ts b/cortex-js/src/infrastructure/database/sqlite-database.providers.ts index e76b49b91..db33dc333 100644 --- a/cortex-js/src/infrastructure/database/sqlite-database.providers.ts +++ b/cortex-js/src/infrastructure/database/sqlite-database.providers.ts @@ -1,10 +1,10 @@ import { FileManagerService } from '@/infrastructure/services/file-manager/file-manager.service'; import { databaseFile } from '@/infrastructure/constants/cortex'; import { join } from 'path'; -import { DataSource } from 'typeorm'; import { ThreadEntity } from '../entities/thread.entity'; -import { AssistantEntity } from '../entities/assistant.entity'; import { MessageEntity } from '../entities/message.entity'; +import { AssistantEntity } from '../entities/assistant.entity'; +import { Sequelize } from 'sequelize-typescript'; export const sqliteDatabaseProviders = [ { @@ -13,14 +13,13 @@ export const sqliteDatabaseProviders = [ useFactory: async (fileManagerService: FileManagerService) => { const dataFolderPath = await fileManagerService.getDataFolderPath(); const sqlitePath = join(dataFolderPath, databaseFile); - const dataSource = new DataSource({ - type: 'sqlite', - database: sqlitePath, - synchronize: process.env.NODE_ENV !== 'production', - entities: [ThreadEntity, AssistantEntity, MessageEntity], + const sequelize = new Sequelize({ + dialect: 'sqlite', + storage: sqlitePath, + logging: false, }); - - return dataSource.initialize(); + sequelize.addModels([ThreadEntity, MessageEntity, AssistantEntity]); + return sequelize; }, }, ]; diff --git a/cortex-js/src/infrastructure/entities/assistant.entity.ts b/cortex-js/src/infrastructure/entities/assistant.entity.ts index 9eb5fcb15..f9ef78435 100644 --- a/cortex-js/src/infrastructure/entities/assistant.entity.ts +++ b/cortex-js/src/infrastructure/entities/assistant.entity.ts @@ -1,51 +1,90 @@ +import { Table, Column, Model, PrimaryKey, DataType } from 'sequelize-typescript'; import { Assistant } from '@/domain/models/assistant.interface'; import type { AssistantToolResources, AssistantResponseFormatOption, } from '@/domain/models/assistant.interface'; -import { Column, Entity, PrimaryColumn } from 'typeorm'; -@Entity('assistants') -export class AssistantEntity implements Assistant { - @PrimaryColumn({ type: String }) +@Table({ tableName: 'assistants', timestamps: false}) +export class AssistantEntity extends Model implements Assistant { + @PrimaryKey + @Column({ + type: DataType.STRING, + }) id: string; - @Column({ type: String, nullable: true }) + @Column({ + type: DataType.STRING, + allowNull: true, + }) avatar?: string; - @Column({ type: String }) + @Column({ + type: DataType.STRING, + defaultValue: 'assistant', + }) object: 'assistant'; - @Column({ type: Number }) + @Column({ + type: DataType.INTEGER, + }) created_at: number; - @Column({ type: String, nullable: true }) + @Column({ + type: DataType.STRING, + allowNull: true, + }) name: string | null; - @Column({ type: String, nullable: true }) + @Column({ + type: DataType.STRING, + allowNull: true, + }) description: string | null; - @Column({ type: String }) + @Column({ + type: DataType.STRING, + }) model: string; - @Column({ type: String, nullable: true }) + @Column({ + type: DataType.STRING, + allowNull: true, + }) instructions: string | null; - @Column({ type: 'simple-json' }) + @Column({ + type: DataType.JSON, + }) tools: any; - @Column({ type: 'simple-json', nullable: true }) + @Column({ + type: DataType.JSON, + allowNull: true, + }) metadata: any | null; - @Column({ type: Number, nullable: true }) + @Column({ + type: DataType.FLOAT, + allowNull: true, + }) top_p: number | null; - @Column({ type: Number, nullable: true }) + @Column({ + type: DataType.FLOAT, + allowNull: true, + }) temperature: number | null; - @Column({ type: 'simple-json', nullable: true }) + @Column({ + type: DataType.JSON, + allowNull: true, + }) response_format: AssistantResponseFormatOption | null; - @Column({ type: 'simple-json', nullable: true }) + @Column({ + type: DataType.JSON, + allowNull: true, + }) tool_resources: AssistantToolResources | null; } diff --git a/cortex-js/src/infrastructure/entities/message.entity.ts b/cortex-js/src/infrastructure/entities/message.entity.ts index 8e009d9c6..1fe6192a5 100644 --- a/cortex-js/src/infrastructure/entities/message.entity.ts +++ b/cortex-js/src/infrastructure/entities/message.entity.ts @@ -1,52 +1,88 @@ +import { Table, Column, Model, PrimaryKey, DataType } from 'sequelize-typescript'; import type { Message, MessageContent, MessageIncompleteDetails, MessageAttachment, } from '@/domain/models/message.interface'; -import { Column, Entity, PrimaryColumn } from 'typeorm'; -@Entity('messages') -export class MessageEntity implements Message { - @PrimaryColumn({ type: String }) +@Table({ tableName: 'messages', timestamps: false}) +export class MessageEntity extends Model implements Message { + @PrimaryKey + @Column({ + type: DataType.STRING, + }) id: string; - @Column({ type: String }) + @Column({ + type: DataType.STRING, + defaultValue: 'thread.message', + }) object: 'thread.message'; - @Column({ type: String }) + @Column({ + type: DataType.STRING, + }) thread_id: string; - @Column({ type: String, nullable: true }) + @Column({ + type: DataType.STRING, + allowNull: true, + }) assistant_id: string | null; - @Column({ type: String }) + @Column({ + type: DataType.STRING, + }) role: 'user' | 'assistant'; - @Column({ type: String }) + @Column({ + type: DataType.STRING, + }) status: 'in_progress' | 'incomplete' | 'completed'; - @Column({ type: 'simple-json', nullable: true }) + @Column({ + type: DataType.JSON, + allowNull: true, + }) metadata: any | null; - @Column({ type: String, nullable: true }) + @Column({ + type: DataType.STRING, + allowNull: true, + }) run_id: string | null; - @Column({ type: Number, nullable: true }) + @Column({ + type: DataType.INTEGER, + allowNull: true, + }) completed_at: number | null; - @Column({ type: 'simple-json' }) + @Column({ + type: DataType.JSON, + }) content: MessageContent[]; - @Column({ type: 'simple-json', nullable: true }) + @Column({ + type: DataType.JSON, + allowNull: true, + }) incomplete_details: MessageIncompleteDetails | null; - @Column({ type: Number }) + @Column({ + type: DataType.INTEGER, + }) created_at: number; - @Column({ type: 'simple-json' }) + @Column({ + type: DataType.JSON, + }) attachments: MessageAttachment[]; - @Column({ type: Number, nullable: true }) + @Column({ + type: DataType.INTEGER, + allowNull: true, + }) incomplete_at: number | null; } diff --git a/cortex-js/src/infrastructure/entities/thread.entity.ts b/cortex-js/src/infrastructure/entities/thread.entity.ts index c53f2559d..fabd11338 100644 --- a/cortex-js/src/infrastructure/entities/thread.entity.ts +++ b/cortex-js/src/infrastructure/entities/thread.entity.ts @@ -1,27 +1,45 @@ +import { Table, Column, Model, PrimaryKey, DataType } from 'sequelize-typescript'; import type { Thread, ThreadToolResources } from '@/domain/models/thread.interface'; -import { Entity, PrimaryColumn, Column } from 'typeorm'; import { AssistantEntity } from './assistant.entity'; -@Entity('threads') -export class ThreadEntity implements Thread { - @PrimaryColumn({ type: String }) +@Table({ tableName: 'threads', timestamps: false}) +export class ThreadEntity extends Model implements Thread { + @PrimaryKey + @Column({ + type: DataType.STRING, + }) id: string; - @Column({ type: String }) + @Column({ + type: DataType.STRING, + defaultValue: 'thread', + }) object: 'thread'; - @Column({ type: String, name: 'title' }) + @Column({ + type: DataType.STRING, + }) title: string; - @Column({ type: 'simple-json' }) + @Column({ + type: DataType.JSON, + }) assistants: AssistantEntity[]; - @Column({ type: Number }) + @Column({ + type: DataType.INTEGER, + }) created_at: number; - @Column({ type: 'simple-json', nullable: true }) + @Column({ + type: DataType.JSON, + allowNull: true, + }) tool_resources: ThreadToolResources | null; - @Column({ type: 'simple-json', nullable: true }) + @Column({ + type: DataType.JSON, + allowNull: true, + }) metadata: any | null; } diff --git a/cortex-js/src/usecases/assistants/assistants.usecases.ts b/cortex-js/src/usecases/assistants/assistants.usecases.ts index 2c9c1bb68..b3b425468 100644 --- a/cortex-js/src/usecases/assistants/assistants.usecases.ts +++ b/cortex-js/src/usecases/assistants/assistants.usecases.ts @@ -1,12 +1,12 @@ import { Inject, Injectable } from '@nestjs/common'; -import { AssistantEntity } from '@/infrastructure/entities/assistant.entity'; -import { QueryFailedError, Repository } from 'typeorm'; import { CreateAssistantDto } from '@/infrastructure/dtos/assistants/create-assistant.dto'; import { Assistant } from '@/domain/models/assistant.interface'; import { PageDto } from '@/infrastructure/dtos/page.dto'; import { ModelRepository } from '@/domain/repositories/model.interface'; import { ModelNotFoundException } from '@/infrastructure/exception/model-not-found.exception'; -import { DuplicateAssistantException } from '@/infrastructure/exception/duplicate-assistant.exception'; +import { Op } from 'sequelize'; +import { AssistantEntity } from '@/infrastructure/entities/assistant.entity'; +import { Repository } from 'sequelize-typescript'; @Injectable() export class AssistantsUsecases { @@ -25,7 +25,7 @@ export class AssistantsUsecases { } } - const assistant: AssistantEntity = { + const assistant: Partial = { ...createAssistantDto, object: 'assistant', created_at: Date.now(), @@ -36,17 +36,12 @@ export class AssistantsUsecases { }; try { - await this.assistantRepository.insert(assistant); + await this.assistantRepository.create(assistant); } catch (err) { - if (err instanceof QueryFailedError) { - if (err.driverError.code === 'SQLITE_CONSTRAINT') - throw new DuplicateAssistantException(id); - } - throw err; } - return this.findOne(assistant.id); + return this.findOne(id); } async listAssistants( @@ -55,20 +50,21 @@ export class AssistantsUsecases { after?: string, before?: string, ) { - const queryBuilder = this.assistantRepository.createQueryBuilder(); const normalizedOrder = order === 'asc' ? 'ASC' : 'DESC'; - queryBuilder.orderBy('created_at', normalizedOrder).take(limit + 1); - + const where: any = {}; if (after) { - queryBuilder.andWhere('id > :after', { after }); + where.id = { [Op.gt]: after }; } - if (before) { - queryBuilder.andWhere('id < :before', { before }); + where.id = { [Op.lt]: before }; } - const { entities: assistants } = await queryBuilder.getRawAndEntities(); + const assistants = await this.assistantRepository.findAll({ + where, + order: [['created_at', normalizedOrder]], + limit: limit + 1, + }); let hasMore = false; if (assistants.length > limit) { @@ -83,7 +79,7 @@ export class AssistantsUsecases { } async findAll(): Promise { - return this.assistantRepository.find(); + return this.assistantRepository.findAll(); } async findOne(id: string) { @@ -93,6 +89,8 @@ export class AssistantsUsecases { } async remove(id: string) { - return this.assistantRepository.delete(id); + return this.assistantRepository.destroy({ + where: { id }, + }); } } diff --git a/cortex-js/src/usecases/messages/messages.usecases.ts b/cortex-js/src/usecases/messages/messages.usecases.ts index 9ae758e88..13eaac7c4 100644 --- a/cortex-js/src/usecases/messages/messages.usecases.ts +++ b/cortex-js/src/usecases/messages/messages.usecases.ts @@ -1,20 +1,21 @@ import { Inject, Injectable } from '@nestjs/common'; import { CreateMessageDto } from '@/infrastructure/dtos/messages/create-message.dto'; import { UpdateMessageDto } from '@/infrastructure/dtos/messages/update-message.dto'; -import { Repository } from 'typeorm'; -import { MessageEntity } from '@/infrastructure/entities/message.entity'; import { ulid } from 'ulid'; +import { MessageEntity } from '@/infrastructure/entities/message.entity'; +import { Message } from '@/domain/models/message.interface'; +import { Repository } from 'sequelize-typescript'; @Injectable() export class MessagesUsecases { constructor( @Inject('MESSAGE_REPOSITORY') - private messageRepository: Repository, + private messageRepository: Repository, ) {} async create(createMessageDto: CreateMessageDto) { const { assistant_id } = createMessageDto; - const message: MessageEntity = { + const message: Partial = { ...createMessageDto, id: ulid(), created_at: Date.now(), @@ -27,14 +28,14 @@ export class MessagesUsecases { metadata: undefined, assistant_id: assistant_id ?? null, }; - this.messageRepository.insert(message); + return this.messageRepository.create(message); } - findAll() { - return this.messageRepository.find(); + async findAll() { + return this.messageRepository.findAll(); } - findOne(id: string) { + async findOne(id: string) { return this.messageRepository.findOne({ where: { id, @@ -42,26 +43,27 @@ export class MessagesUsecases { }); } - update(id: string, updateMessageDto: UpdateMessageDto) { - const updateEntity: Partial = { - ...updateMessageDto, - }; - return this.messageRepository.update(id, updateEntity); + async update(id: string, updateMessageDto: UpdateMessageDto) { + const [numberOfAffectedRows, [updatedMessage]] = await this.messageRepository.update(updateMessageDto, { + where: { id }, + returning: true, + }); + return { numberOfAffectedRows, updatedMessage }; } - remove(id: string) { - return this.messageRepository.delete(id); + async remove(id: string) { + return this.messageRepository.destroy({ + where: { id }, + }); } async getLastMessagesByThread(threadId: string, limit: number) { - return this.messageRepository.find({ + return this.messageRepository.findAll({ where: { thread_id: threadId, }, - order: { - created_at: 'DESC', - }, - take: limit, + order: [['created_at', 'DESC']], + limit: limit, }); } } diff --git a/cortex-js/src/usecases/threads/threads.usecases.ts b/cortex-js/src/usecases/threads/threads.usecases.ts index 0a06e971b..0ffcfa522 100644 --- a/cortex-js/src/usecases/threads/threads.usecases.ts +++ b/cortex-js/src/usecases/threads/threads.usecases.ts @@ -1,10 +1,7 @@ import { Inject, Injectable, NotFoundException } from '@nestjs/common'; import { CreateThreadDto } from '@/infrastructure/dtos/threads/create-thread.dto'; import { UpdateThreadDto } from '@/infrastructure/dtos/threads/update-thread.dto'; -import { ThreadEntity } from '@/infrastructure/entities/thread.entity'; -import { Repository } from 'typeorm'; import { v4 as uuidv4 } from 'uuid'; -import { MessageEntity } from '@/infrastructure/entities/message.entity'; import { PageDto } from '@/infrastructure/dtos/page.dto'; import { CreateMessageDto } from '@/infrastructure/dtos/threads/create-message.dto'; import { ulid } from 'ulid'; @@ -12,7 +9,11 @@ import { Message, MessageContent } from '@/domain/models/message.interface'; import { UpdateMessageDto } from '@/infrastructure/dtos/threads/update-message.dto'; import { Thread } from '@/domain/models/thread.interface'; import DeleteMessageDto from '@/infrastructure/dtos/threads/delete-message.dto'; -import { AssistantEntity } from '@/infrastructure/entities/assistant.entity'; +import { Assistant } from '@/domain/models/assistant.interface'; +import { Repository } from 'sequelize-typescript'; +import { ThreadEntity } from '@/infrastructure/entities/thread.entity'; +import { MessageEntity } from '@/infrastructure/entities/message.entity'; +import { Op } from 'sequelize'; @Injectable() export class ThreadsUsecases { @@ -20,14 +21,14 @@ export class ThreadsUsecases { @Inject('THREAD_REPOSITORY') private threadRepository: Repository, @Inject('MESSAGE_REPOSITORY') - private messageRepository: Repository, + private messageRepository: Repository, ) {} - async create(createThreadDto: CreateThreadDto): Promise { + async create(createThreadDto: CreateThreadDto): Promise { const id = uuidv4(); const { assistants } = createThreadDto; - const assistantEntity: AssistantEntity[] = assistants.map((assistant) => { - const entity: AssistantEntity = { + const assistantEntity: Assistant[] = assistants.map((assistant) => { + const entity: Assistant = { ...assistant, response_format: null, tool_resources: null, @@ -37,7 +38,7 @@ export class ThreadsUsecases { return entity; }); - const thread: ThreadEntity = { + const thread: Partial = { id, assistants: assistantEntity, object: 'thread', @@ -46,15 +47,14 @@ export class ThreadsUsecases { tool_resources: null, metadata: null, }; - await this.threadRepository.insert(thread); - return thread; + + return this.threadRepository.create(thread); } - async findAll(): Promise { - return this.threadRepository.find({ - order: { - created_at: 'DESC', - }, + async findAll(): Promise { + return this.threadRepository.findAll({ + include: [{ all: true }], + order: [['created_at', 'DESC']], }); } @@ -64,32 +64,23 @@ export class ThreadsUsecases { order: 'asc' | 'desc', after?: string, before?: string, - // eslint-disable-next-line @typescript-eslint/no-unused-vars runId?: string, ) { await this.getThreadOrThrow(threadId); - const queryBuilder = this.messageRepository.createQueryBuilder(); const normalizedOrder = order === 'asc' ? 'ASC' : 'DESC'; - queryBuilder - .where('thread_id = :id', { id: threadId }) - .orderBy('created_at', normalizedOrder) - .take(limit + 1); // Fetch one more record than the limit - - if (after) { - queryBuilder.andWhere('id > :after', { after }); - } - - if (before) { - queryBuilder.andWhere('id < :before', { before }); - } - - const { entities: messages } = await queryBuilder.getRawAndEntities(); + const messages = await this.messageRepository.findAll({ + where: { thread_id: threadId }, + order: [['created_at', normalizedOrder]], + limit: limit + 1, + ...(after && { where: { id: { [Op.gt]: after } } }), + ...(before && { where: { id: { [Op.lt]: before } } }), + }); let hasMore = false; if (messages.length > limit) { hasMore = true; - messages.pop(); // Remove the extra record + messages.pop(); } const firstId = messages[0]?.id ?? undefined; @@ -113,7 +104,7 @@ export class ThreadsUsecases { }, }; - const message: MessageEntity = { + const message: Partial = { id: ulid(), object: 'thread.message', thread_id: threadId, @@ -129,7 +120,8 @@ export class ThreadsUsecases { attachments: [], incomplete_at: null, }; - await this.messageRepository.insert(message); + + await this.messageRepository.create(message); return message; } @@ -139,12 +131,8 @@ export class ThreadsUsecases { updateMessageDto: UpdateMessageDto, ) { await this.getThreadOrThrow(threadId); - await this.messageRepository.update(messageId, updateMessageDto); - return this.messageRepository.findOne({ - where: { - id: messageId, - }, - }); + await this.messageRepository.update(updateMessageDto, { where: { id: messageId } }); + return this.messageRepository.findOne({ where: { id: messageId } }); } private async getThreadOrThrow(threadId: string): Promise { @@ -157,9 +145,7 @@ export class ThreadsUsecases { private async getMessageOrThrow(messageId: string): Promise { const message = await this.messageRepository.findOne({ - where: { - id: messageId, - }, + where: { id: messageId }, }); if (!message) { throw new NotFoundException(`Message with id ${messageId} not found`); @@ -172,9 +158,9 @@ export class ThreadsUsecases { } async update(id: string, updateThreadDto: UpdateThreadDto) { - const assistantEntities: AssistantEntity[] = + const assistantEntities: Assistant[] = updateThreadDto.assistants?.map((assistant) => { - const entity: AssistantEntity = { + const entity: Assistant = { ...assistant, name: assistant.name, response_format: null, @@ -185,24 +171,21 @@ export class ThreadsUsecases { return entity; }) ?? []; - const entity: Partial = { + const entity: Partial = { ...updateThreadDto, assistants: assistantEntities, }; - return this.threadRepository.update(id, entity); + + return this.threadRepository.update(entity, { where: { id } }); } - remove(id: string) { - this.threadRepository.delete(id); + async remove(id: string) { + await this.threadRepository.destroy({ where: { id } }); } - async deleteMessage( - _threadId: string, - messageId: string, - ): Promise { - // we still allow user to delete message even if the thread is not there + async deleteMessage(_threadId: string, messageId: string): Promise { await this.getMessageOrThrow(messageId); - await this.messageRepository.delete(messageId); + await this.messageRepository.destroy({ where: { id: messageId } }); return { id: messageId, @@ -212,12 +195,12 @@ export class ThreadsUsecases { } async retrieveMessage(_threadId: string, messageId: string) { - // we still allow user to delete message even if the thread is not there + // we still allow user to delete message even if the thread is not there return this.getMessageOrThrow(messageId); } async clean(threadId: string) { await this.getThreadOrThrow(threadId); - await this.messageRepository.delete({ thread_id: threadId }); + await this.messageRepository.destroy({ where: { thread_id: threadId } }); } }