-
-
Notifications
You must be signed in to change notification settings - Fork 18
Backend ai conversation_history #1545
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5902822
ba204a0
34d8972
b77b6f8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| import { | ||
| Column, | ||
| CreateDateColumn, | ||
| Entity, | ||
| JoinColumn, | ||
| ManyToOne, | ||
| PrimaryGeneratedColumn, | ||
| Relation, | ||
| UpdateDateColumn, | ||
| } from 'typeorm'; | ||
| import { MessageRole } from './message-role.enum.js'; | ||
| import { UserAiChatEntity } from '../user-ai-chat/user-ai-chat.entity.js'; | ||
|
|
||
| @Entity('ai_chat_message') | ||
| export class AiChatMessageEntity { | ||
| @PrimaryGeneratedColumn('uuid') | ||
| id: string; | ||
|
|
||
| @Column({ default: null, type: 'text' }) | ||
| message: string; | ||
|
|
||
| @Column({ nullable: true, default: null, type: 'enum', enum: MessageRole }) | ||
| role: MessageRole; | ||
|
|
||
| @CreateDateColumn({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' }) | ||
| created_at: Date; | ||
|
|
||
| @UpdateDateColumn({ type: 'timestamp', nullable: true, default: null }) | ||
| updated_at: Date; | ||
|
|
||
| @ManyToOne( | ||
| () => UserAiChatEntity, | ||
| (ai_chat) => ai_chat.messages, | ||
| ) | ||
| @JoinColumn({ name: 'ai_chat_id' }) | ||
| ai_chat: Relation<UserAiChatEntity>; | ||
|
|
||
| @Column() | ||
| ai_chat_id: string; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| export enum MessageRole { | ||
| user = 'user', | ||
| ai = 'ai', | ||
| system = 'system', | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| import { IAiChatMessageRepository } from './ai-chat-message-repository.interface.js'; | ||
| import { AiChatMessageEntity } from '../ai-chat-message.entity.js'; | ||
| import { MessageRole } from '../message-role.enum.js'; | ||
|
|
||
| export const aiChatMessageRepositoryExtension: IAiChatMessageRepository = { | ||
| async findMessagesForChat(chatId: string): Promise<AiChatMessageEntity[]> { | ||
| return await this.createQueryBuilder('ai_chat_message') | ||
| .where('ai_chat_message.ai_chat_id = :chatId', { chatId }) | ||
| .orderBy('ai_chat_message.created_at', 'ASC') | ||
| .getMany(); | ||
| }, | ||
|
|
||
| async deleteMessagesForChat(chatId: string): Promise<void> { | ||
| await this.createQueryBuilder() | ||
| .delete() | ||
| .from('ai_chat_message') | ||
| .where('ai_chat_id = :chatId', { chatId }) | ||
| .execute(); | ||
| }, | ||
|
|
||
| async saveMessage(chatId: string, message: string, role: MessageRole): Promise<AiChatMessageEntity> { | ||
| const newMessage = this.create({ | ||
| ai_chat_id: chatId, | ||
| message, | ||
| role, | ||
| }); | ||
| return await this.save(newMessage); | ||
| }, | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| import { AiChatMessageEntity } from '../ai-chat-message.entity.js'; | ||
| import { MessageRole } from '../message-role.enum.js'; | ||
|
|
||
| export interface IAiChatMessageRepository { | ||
| findMessagesForChat(chatId: string): Promise<AiChatMessageEntity[]>; | ||
| deleteMessagesForChat(chatId: string): Promise<void>; | ||
| saveMessage(chatId: string, message: string, role: MessageRole): Promise<AiChatMessageEntity>; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| export class FindUserAiChatsDs { | ||
| userId: string; | ||
| } | ||
|
|
||
| export class FindUserAiChatByIdDs { | ||
| userId: string; | ||
| chatId: string; | ||
| } | ||
|
|
||
| export class DeleteUserAiChatDs { | ||
| userId: string; | ||
| chatId: string; | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,19 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { MessageRole } from '../../ai-chat-messages/message-role.enum.js'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export class AiChatMessageRO { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| role: MessageRole; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| created_at: Date; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export class UserAiChatRO { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| created_at: Date; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| updated_at: Date; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export class UserAiChatWithMessagesRO extends UserAiChatRO { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1
to
+17
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { MessageRole } from '../../ai-chat-messages/message-role.enum.js'; | |
| export class AiChatMessageRO { | |
| id: string; | |
| message: string; | |
| role: MessageRole; | |
| created_at: Date; | |
| } | |
| export class UserAiChatRO { | |
| id: string; | |
| name: string; | |
| created_at: Date; | |
| updated_at: Date; | |
| } | |
| export class UserAiChatWithMessagesRO extends UserAiChatRO { | |
| import { MessageRole } from '../../ai-chat-messages/message-role.enum.js'; | |
| import { ApiProperty } from '@nestjs/swagger'; | |
| export class AiChatMessageRO { | |
| @ApiProperty() | |
| id: string; | |
| @ApiProperty() | |
| message: string; | |
| @ApiProperty({ enum: MessageRole }) | |
| role: MessageRole; | |
| @ApiProperty() | |
| created_at: Date; | |
| } | |
| export class UserAiChatRO { | |
| @ApiProperty() | |
| id: string; | |
| @ApiProperty() | |
| name: string; | |
| @ApiProperty() | |
| created_at: Date; | |
| @ApiProperty() | |
| updated_at: Date; | |
| } | |
| export class UserAiChatWithMessagesRO extends UserAiChatRO { | |
| @ApiProperty({ type: () => [AiChatMessageRO] }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| import { AiChatMessageEntity } from '../../ai-chat-messages/ai-chat-message.entity.js'; | ||
| import { UserAiChatEntity } from '../../user-ai-chat/user-ai-chat.entity.js'; | ||
| import { AiChatMessageRO, UserAiChatRO, UserAiChatWithMessagesRO } from '../response-objects/user-ai-chat.ro.js'; | ||
|
|
||
| export function buildUserAiChatRO(chat: UserAiChatEntity): UserAiChatRO { | ||
| return { | ||
| id: chat.id, | ||
| name: chat.name, | ||
| created_at: chat.created_at, | ||
| updated_at: chat.updated_at, | ||
| }; | ||
| } | ||
|
|
||
| export function buildAiChatMessageRO(message: AiChatMessageEntity): AiChatMessageRO { | ||
| return { | ||
| id: message.id, | ||
| message: message.message, | ||
| role: message.role, | ||
| created_at: message.created_at, | ||
| }; | ||
| } | ||
|
|
||
| export function buildUserAiChatWithMessagesRO(chat: UserAiChatEntity): UserAiChatWithMessagesRO { | ||
| return { | ||
| id: chat.id, | ||
| name: chat.name, | ||
| created_at: chat.created_at, | ||
| updated_at: chat.updated_at, | ||
| messages: chat.messages?.map((message) => buildAiChatMessageRO(message)) ?? [], | ||
| }; | ||
| } |
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
| @@ -0,0 +1,35 @@ | ||||
| import { Inject, Injectable, NotFoundException, Scope } from '@nestjs/common'; | ||||
| import AbstractUseCase from '../../../../common/abstract-use.case.js'; | ||||
| import { IGlobalDatabaseContext } from '../../../../common/application/global-database-context.interface.js'; | ||||
| import { BaseType } from '../../../../common/data-injection.tokens.js'; | ||||
| import { Messages } from '../../../../exceptions/text/messages.js'; | ||||
| import { SuccessResponse } from '../../../../microservices/saas-microservice/data-structures/common-responce.ds.js'; | ||||
| import { DeleteUserAiChatDs } from '../application/data-structures/user-ai-chat.ds.js'; | ||||
| import { IDeleteUserAiChat } from './user-ai-chat-use-cases.interface.js'; | ||||
|
|
||||
| @Injectable({ scope: Scope.REQUEST }) | ||||
| export class DeleteUserAiChatUseCase | ||||
| extends AbstractUseCase<DeleteUserAiChatDs, SuccessResponse> | ||||
| implements IDeleteUserAiChat | ||||
| { | ||||
| constructor( | ||||
| @Inject(BaseType.GLOBAL_DB_CONTEXT) | ||||
| protected _dbContext: IGlobalDatabaseContext, | ||||
| ) { | ||||
| super(); | ||||
| } | ||||
|
|
||||
| protected async implementation(inputData: DeleteUserAiChatDs): Promise<SuccessResponse> { | ||||
| const { userId, chatId } = inputData; | ||||
| const foundChat = await this._dbContext.userAiChatRepository.findChatByIdAndUserId(chatId, userId); | ||||
|
|
||||
| if (!foundChat) { | ||||
| throw new NotFoundException(Messages.AI_CHAT_NOT_FOUND); | ||||
| } | ||||
|
|
||||
| await this._dbContext.aiChatMessageRepository.deleteMessagesForChat(chatId); | ||||
|
||||
| await this._dbContext.aiChatMessageRepository.deleteMessagesForChat(chatId); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| import { Inject, Injectable, NotFoundException } from '@nestjs/common'; | ||
| import AbstractUseCase from '../../../../common/abstract-use.case.js'; | ||
| import { IGlobalDatabaseContext } from '../../../../common/application/global-database-context.interface.js'; | ||
| import { BaseType } from '../../../../common/data-injection.tokens.js'; | ||
| import { Messages } from '../../../../exceptions/text/messages.js'; | ||
| import { FindUserAiChatByIdDs } from '../application/data-structures/user-ai-chat.ds.js'; | ||
| import { UserAiChatWithMessagesRO } from '../application/response-objects/user-ai-chat.ro.js'; | ||
| import { buildUserAiChatWithMessagesRO } from '../application/utils/build-user-ai-chat-ro.util.js'; | ||
| import { IFindUserAiChatById } from './user-ai-chat-use-cases.interface.js'; | ||
|
|
||
| @Injectable() | ||
| export class FindUserAiChatByIdUseCase | ||
| extends AbstractUseCase<FindUserAiChatByIdDs, UserAiChatWithMessagesRO> | ||
| implements IFindUserAiChatById | ||
| { | ||
| constructor( | ||
| @Inject(BaseType.GLOBAL_DB_CONTEXT) | ||
| protected _dbContext: IGlobalDatabaseContext, | ||
| ) { | ||
| super(); | ||
| } | ||
|
|
||
| protected async implementation(inputData: FindUserAiChatByIdDs): Promise<UserAiChatWithMessagesRO> { | ||
| const { userId, chatId } = inputData; | ||
| const foundChat = await this._dbContext.userAiChatRepository.findChatWithMessagesByIdAndUserId(chatId, userId); | ||
|
|
||
| if (!foundChat) { | ||
| throw new NotFoundException(Messages.AI_CHAT_NOT_FOUND); | ||
| } | ||
|
|
||
| return buildUserAiChatWithMessagesRO(foundChat); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| import { Inject, Injectable } from '@nestjs/common'; | ||
| import AbstractUseCase from '../../../../common/abstract-use.case.js'; | ||
| import { IGlobalDatabaseContext } from '../../../../common/application/global-database-context.interface.js'; | ||
| import { BaseType } from '../../../../common/data-injection.tokens.js'; | ||
| import { FindUserAiChatsDs } from '../application/data-structures/user-ai-chat.ds.js'; | ||
| import { UserAiChatRO } from '../application/response-objects/user-ai-chat.ro.js'; | ||
| import { buildUserAiChatRO } from '../application/utils/build-user-ai-chat-ro.util.js'; | ||
| import { IFindUserAiChats } from './user-ai-chat-use-cases.interface.js'; | ||
|
|
||
| @Injectable() | ||
| export class FindUserAiChatsUseCase | ||
| extends AbstractUseCase<FindUserAiChatsDs, UserAiChatRO[]> | ||
| implements IFindUserAiChats | ||
| { | ||
| constructor( | ||
| @Inject(BaseType.GLOBAL_DB_CONTEXT) | ||
| protected _dbContext: IGlobalDatabaseContext, | ||
| ) { | ||
| super(); | ||
| } | ||
|
|
||
| protected async implementation(inputData: FindUserAiChatsDs): Promise<UserAiChatRO[]> { | ||
| const { userId } = inputData; | ||
| const foundChats = await this._dbContext.userAiChatRepository.findAllChatsForUser(userId); | ||
| return foundChats.map((chat) => buildUserAiChatRO(chat)); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| import { InTransactionEnum } from '../../../../enums/in-transaction.enum.js'; | ||
| import { SuccessResponse } from '../../../../microservices/saas-microservice/data-structures/common-responce.ds.js'; | ||
| import { | ||
| DeleteUserAiChatDs, | ||
| FindUserAiChatByIdDs, | ||
| FindUserAiChatsDs, | ||
| } from '../application/data-structures/user-ai-chat.ds.js'; | ||
| import { UserAiChatRO, UserAiChatWithMessagesRO } from '../application/response-objects/user-ai-chat.ro.js'; | ||
|
|
||
| export interface IFindUserAiChats { | ||
| execute(inputData: FindUserAiChatsDs, inTransaction: InTransactionEnum): Promise<UserAiChatRO[]>; | ||
| } | ||
|
|
||
| export interface IFindUserAiChatById { | ||
| execute(inputData: FindUserAiChatByIdDs, inTransaction: InTransactionEnum): Promise<UserAiChatWithMessagesRO>; | ||
| } | ||
|
|
||
| export interface IDeleteUserAiChat { | ||
| execute(inputData: DeleteUserAiChatDs, inTransaction: InTransactionEnum): Promise<SuccessResponse>; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ManyToOne relationship with UserAiChatEntity should specify the onDelete behavior to ensure messages are deleted when their parent chat is deleted. Add onDelete: 'CASCADE' to the decorator options to match the database migration and maintain referential integrity.