-
-
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
Conversation
…ases - Introduced UserAiChatEntity and AiChatMessageEntity to manage AI chat sessions and messages. - Implemented IUserAiChatRepository and IAiChatMessageRepository interfaces for data access. - Created repository extensions for user AI chat and message functionalities. - Developed use cases for finding, deleting, and retrieving user AI chats and messages. - Added UserAiChatController to handle API requests related to user AI chats. - Updated global database context to include new repositories. - Created migration to set up database tables and relationships for AI chat entities.
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.
Pull request overview
This pull request implements AI conversation history functionality to enable users to maintain and manage persistent chat sessions with the AI assistant. The feature allows users to continue conversations across multiple requests and manage their chat history.
Changes:
- Added database tables and entities for storing AI chat conversations and messages with user relationships
- Implemented CRUD API endpoints for managing AI chats (list, get, delete)
- Created a new v4 AI request endpoint that saves conversation messages to the database
- Added comprehensive E2E tests for both SaaS and non-SaaS environments
Reviewed changes
Copilot reviewed 27 out of 27 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| backend/src/migrations/1769759553633-AddChatWithAiAndChatMessageEntities.ts | Database migration creating user_ai_chat and ai_chat_message tables with foreign key relationships |
| backend/src/entities/ai/ai-conversation-history/user-ai-chat/user-ai-chat.entity.ts | Entity definition for user AI chat sessions |
| backend/src/entities/ai/ai-conversation-history/ai-chat-messages/ai-chat-message.entity.ts | Entity definition for individual chat messages with role (user/ai/system) |
| backend/src/entities/ai/ai-conversation-history/user-ai-chat/repository/*.ts | Repository interface and implementation for chat CRUD operations |
| backend/src/entities/ai/ai-conversation-history/ai-chat-messages/repository/*.ts | Repository interface and implementation for message operations |
| backend/src/entities/ai/ai-conversation-history/use-cases/*.ts | Use cases for finding and deleting user AI chats |
| backend/src/entities/ai/ai-conversation-history/user-ai-chat.controller.ts | REST controller exposing chat management endpoints |
| backend/src/entities/ai/use-cases/request-info-from-table-with-ai-v7.use.case.ts | New v7 use case integrating conversation history with AI requests |
| backend/src/entities/ai/user-ai-requests-v2.controller.ts | Updated controller with v4 endpoint supporting conversation threads |
| backend/src/entities/ai/ai.module.ts | Module configuration registering new providers and applying authentication middleware |
| backend/src/entities/user/user.entity.ts | Added relationship to user AI chats |
| backend/src/common/application/global-database-context.ts | Added repositories for chat and message entities |
| backend/src/common/data-injection.tokens.ts | Added use case tokens for dependency injection |
| backend/src/decorators/slug-uuid.decorator.ts | Added 'chatId' parameter support |
| backend/src/exceptions/text/messages.ts | Added AI_CHAT_NOT_FOUND error message |
| backend/test/ava-tests/saas-tests/ai-chat-e2e.test.ts | Comprehensive E2E tests for AI chat functionality in SaaS mode |
| backend/test/ava-tests/non-saas-tests/non-saas-ai-chat-e2e.test.ts | Comprehensive E2E tests for AI chat functionality in non-SaaS mode |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| `ALTER TABLE "ai_chat_message" ADD CONSTRAINT "FK_03bc49058afd5262d6a503bf123" FOREIGN KEY ("ai_chat_id") REFERENCES "user_ai_chat"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, | ||
| ); | ||
| await queryRunner.query( | ||
| `ALTER TABLE "user_ai_chat" ADD CONSTRAINT "FK_0f95dbd767d42e637345636cb5d" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, |
Copilot
AI
Jan 30, 2026
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 foreign key constraints use "ON DELETE NO ACTION" which is inconsistent with the entity definitions and codebase conventions. Looking at the entity definitions, UserAiChatEntity and AiChatMessageEntity don't specify onDelete options, but similar user-owned entities throughout the codebase (like AiResponsesToUserEntity, UserApiKeyEntity, SignInAuditEntity) use CASCADE deletion. When a user is deleted, their AI chats should also be deleted. Similarly, when a chat is deleted, its messages should be deleted. Change both foreign key constraints to use "ON DELETE CASCADE" to match the expected behavior and prevent orphaned records.
| `ALTER TABLE "ai_chat_message" ADD CONSTRAINT "FK_03bc49058afd5262d6a503bf123" FOREIGN KEY ("ai_chat_id") REFERENCES "user_ai_chat"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, | |
| ); | |
| await queryRunner.query( | |
| `ALTER TABLE "user_ai_chat" ADD CONSTRAINT "FK_0f95dbd767d42e637345636cb5d" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, | |
| `ALTER TABLE "ai_chat_message" ADD CONSTRAINT "FK_03bc49058afd5262d6a503bf123" FOREIGN KEY ("ai_chat_id") REFERENCES "user_ai_chat"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, | |
| ); | |
| await queryRunner.query( | |
| `ALTER TABLE "user_ai_chat" ADD CONSTRAINT "FK_0f95dbd767d42e637345636cb5d" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, |
| await this._dbContext.aiChatMessageRepository.saveMessage(foundUserAiChat.id, user_message, MessageRole.user); | ||
|
|
||
| const messages = new MessageBuilder().system(systemPrompt).human(user_message).build(); | ||
|
|
Copilot
AI
Jan 30, 2026
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 v7 use case saves conversation messages to the database but does not retrieve and include previous messages when sending requests to the AI. At line 89, it only builds messages with the system prompt and the current user message, completely ignoring the conversation history stored in the database. This defeats the purpose of maintaining conversation history since the AI model won't have context from previous exchanges. To fix this, after retrieving or creating the chat entity, load the existing messages from the database and include them in the MessageBuilder chain before adding the new user message. You should load messages from the aiChatMessageRepository, convert them to the appropriate format for the MessageBuilder, and add them in chronological order.
| await this._dbContext.aiChatMessageRepository.saveMessage(foundUserAiChat.id, user_message, MessageRole.user); | |
| const messages = new MessageBuilder().system(systemPrompt).human(user_message).build(); | |
| // Load existing conversation history for this chat and build the message list in chronological order. | |
| const previousMessages = | |
| await this._dbContext.aiChatMessageRepository.findMessagesByChatId(foundUserAiChat.id); | |
| // Persist the current user message to the conversation history. | |
| await this._dbContext.aiChatMessageRepository.saveMessage( | |
| foundUserAiChat.id, | |
| user_message, | |
| MessageRole.user, | |
| ); | |
| const messageBuilder = new MessageBuilder().system(systemPrompt); | |
| for (const message of previousMessages) { | |
| if (message.role === MessageRole.user) { | |
| messageBuilder.human(message.content); | |
| } else if (message.role === MessageRole.ai) { | |
| messageBuilder.ai(message.content); | |
| } | |
| } | |
| // Append the current user message as the latest turn in the conversation. | |
| const messages = messageBuilder.human(user_message).build(); |
|
|
||
| @ManyToOne( | ||
| () => UserEntity, | ||
| (user) => user.ai_chats, |
Copilot
AI
Jan 30, 2026
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 UserEntity should specify the onDelete behavior to match the migration and follow codebase conventions. Similar user-owned entities like AiResponsesToUserEntity specify onDelete: 'CASCADE' in their decorators. Add the onDelete option to ensure TypeORM properly handles cascade deletion when generating migrations in the future.
| (user) => user.ai_chats, | |
| (user) => user.ai_chats, | |
| { onDelete: 'CASCADE' }, |
|
|
||
| @ManyToOne( | ||
| () => UserAiChatEntity, | ||
| (ai_chat) => ai_chat.messages, |
Copilot
AI
Jan 30, 2026
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.
| (ai_chat) => ai_chat.messages, | |
| (ai_chat) => ai_chat.messages, | |
| { onDelete: 'CASCADE' }, |
| throw new NotFoundException(Messages.AI_CHAT_NOT_FOUND); | ||
| } | ||
|
|
||
| await this._dbContext.aiChatMessageRepository.deleteMessagesForChat(chatId); |
Copilot
AI
Jan 30, 2026
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 manual deletion of messages at line 30 is unnecessary if the database foreign key constraint is properly set to CASCADE. Once the migration is fixed to use ON DELETE CASCADE, this explicit deletion can be removed since the database will automatically delete related messages when the chat is deleted. This would simplify the code and ensure atomicity of the deletion operation.
| await this._dbContext.aiChatMessageRepository.deleteMessagesForChat(chatId); |
| 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 { |
Copilot
AI
Jan 30, 2026
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 response object classes are missing @ApiProperty decorators which are consistently used throughout the codebase for Swagger documentation. Looking at similar DTOs like FoundUserDto or FoundConnectionPropertiesDs, all properties should have @ApiProperty() decorators to generate proper API documentation. Add these decorators to all properties in AiChatMessageRO, UserAiChatRO, and UserAiChatWithMessagesRO classes.
| 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] }) |
No description provided.