feat: add chat name update functionality and improve AI response handling#1548
feat: add chat name update functionality and improve AI response handling#1548
Conversation
There was a problem hiding this comment.
Pull request overview
Adds automatic chat naming for new AI conversations and tightens AI JSON-output handling to reduce parsing failures.
Changes:
- Generate a chat title on first user message for newly created AI chats and persist it.
- Extend
IUserAiChatRepositorywithupdateChatNameand implement it in the repository extension. - Strengthen the AI prompt guidance and post-processing for JSON responses (remove comments / trailing commas).
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| backend/src/entities/ai/use-cases/request-info-from-table-with-ai-v7.use.case.ts | Triggers background chat-title generation for new chats and updates the persisted chat name. |
| backend/src/entities/ai/ai.service.ts | Updates the LLM prompt to emphasize strict JSON-only output with no comments. |
| backend/src/entities/ai/ai-conversation-history/user-ai-chat/repository/user-ai-chat-repository.interface.ts | Adds updateChatName to the repository interface. |
| backend/src/entities/ai/ai-conversation-history/user-ai-chat/repository/user-ai-chat-repository.extension.ts | Implements updateChatName via a TypeORM update query. |
| backend/src/ai-core/tools/query-validators.ts | Expands AI JSON cleaning to strip comment syntax and trailing commas before parsing. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| this.generateAndUpdateChatName(foundUserAiChat.id, user_message).catch((error) => { | ||
| Sentry.captureException(error); | ||
| }); |
There was a problem hiding this comment.
generateAndUpdateChatName() already catches and reports errors internally, so the additional .catch(...) here will never run and makes the async fire-and-forget behavior harder to reason about. Either remove the outer .catch and explicitly mark the call as fire-and-forget (e.g., void ...), or remove the internal try/catch and handle errors only at the call site (but avoid double-reporting).
| this.generateAndUpdateChatName(foundUserAiChat.id, user_message).catch((error) => { | |
| Sentry.captureException(error); | |
| }); | |
| void this.generateAndUpdateChatName(foundUserAiChat.id, user_message); |
| findChatByIdAndUserId(chatId: string, userId: string): Promise<UserAiChatEntity | null>; | ||
| findChatWithMessagesByIdAndUserId(chatId: string, userId: string): Promise<UserAiChatEntity | null>; | ||
| createChatForUser(userId: string, name?: string): Promise<UserAiChatEntity>; | ||
| updateChatName(chatId: string, name: string): Promise<void>; |
There was a problem hiding this comment.
updateChatName is defined without a userId parameter, unlike the other repository methods that scope operations by chatId + userId. To avoid accidental cross-tenant updates, consider changing this API to updateChatName(chatId: string, userId: string, name: string) (or similar) so implementations can enforce ownership at the data layer.
| updateChatName(chatId: string, name: string): Promise<void>; | |
| updateChatName(chatId: string, userId: string, name: string): Promise<void>; |
| async updateChatName(chatId: string, name: string): Promise<void> { | ||
| await this.createQueryBuilder() | ||
| .update(UserAiChatEntity) | ||
| .set({ name }) | ||
| .where('id = :chatId', { chatId }) |
There was a problem hiding this comment.
This update query scopes only by id. Since chats are user-owned (and other repo methods require userId), this should also include user_id = :userId (and accept userId as a parameter) to prevent updating another user's chat if a UUID is ever reused/leaked/passed incorrectly.
| async updateChatName(chatId: string, name: string): Promise<void> { | |
| await this.createQueryBuilder() | |
| .update(UserAiChatEntity) | |
| .set({ name }) | |
| .where('id = :chatId', { chatId }) | |
| async updateChatName(chatId: string, userId: string, name: string): Promise<void> { | |
| await this.createQueryBuilder() | |
| .update(UserAiChatEntity) | |
| .set({ name }) | |
| .where('id = :chatId AND user_id = :userId', { chatId, userId }) |
| cleanedResponse = cleanedResponse.replace(/^\s*\/\/.*$/gm, ''); | ||
| cleanedResponse = cleanedResponse.replace(/\/\*[\s\S]*?\*\//g, ''); | ||
| cleanedResponse = cleanedResponse.replace(/,(\s*[}\]])/g, '$1'); |
There was a problem hiding this comment.
The block-comment stripping regex (/\/\*[\s\S]*?\*\//g) will also remove /* ... */ sequences that appear inside valid JSON string values, mutating otherwise-correct AI responses (e.g., a description containing those characters). Consider using a comment-tolerant parser (e.g., JSON5) or implementing a small scanner that removes comments only when not inside quoted strings.
No description provided.