diff --git a/backend/src/ai-core/tools/query-validators.ts b/backend/src/ai-core/tools/query-validators.ts index 82406698f..bed85dd49 100644 --- a/backend/src/ai-core/tools/query-validators.ts +++ b/backend/src/ai-core/tools/query-validators.ts @@ -107,5 +107,11 @@ export function cleanAIJsonResponse(response: string): string { if (cleanedResponse.endsWith('```')) { cleanedResponse = cleanedResponse.slice(0, -3); } + cleanedResponse = cleanedResponse.trim(); + + cleanedResponse = cleanedResponse.replace(/^\s*\/\/.*$/gm, ''); + cleanedResponse = cleanedResponse.replace(/\/\*[\s\S]*?\*\//g, ''); + cleanedResponse = cleanedResponse.replace(/,(\s*[}\]])/g, '$1'); + return cleanedResponse.trim(); } diff --git a/backend/src/entities/ai/ai-conversation-history/user-ai-chat/repository/user-ai-chat-repository.extension.ts b/backend/src/entities/ai/ai-conversation-history/user-ai-chat/repository/user-ai-chat-repository.extension.ts index 28271768c..5cfb32f34 100644 --- a/backend/src/entities/ai/ai-conversation-history/user-ai-chat/repository/user-ai-chat-repository.extension.ts +++ b/backend/src/entities/ai/ai-conversation-history/user-ai-chat/repository/user-ai-chat-repository.extension.ts @@ -32,4 +32,12 @@ export const userAiChatRepositoryExtension: IUserAiChatRepository = { }); return await this.save(newChat); }, + + async updateChatName(chatId: string, name: string): Promise { + await this.createQueryBuilder() + .update(UserAiChatEntity) + .set({ name }) + .where('id = :chatId', { chatId }) + .execute(); + }, }; diff --git a/backend/src/entities/ai/ai-conversation-history/user-ai-chat/repository/user-ai-chat-repository.interface.ts b/backend/src/entities/ai/ai-conversation-history/user-ai-chat/repository/user-ai-chat-repository.interface.ts index badc9c92d..56bfafd1d 100644 --- a/backend/src/entities/ai/ai-conversation-history/user-ai-chat/repository/user-ai-chat-repository.interface.ts +++ b/backend/src/entities/ai/ai-conversation-history/user-ai-chat/repository/user-ai-chat-repository.interface.ts @@ -5,4 +5,5 @@ export interface IUserAiChatRepository { findChatByIdAndUserId(chatId: string, userId: string): Promise; findChatWithMessagesByIdAndUserId(chatId: string, userId: string): Promise; createChatForUser(userId: string, name?: string): Promise; + updateChatName(chatId: string, name: string): Promise; } diff --git a/backend/src/entities/ai/ai.service.ts b/backend/src/entities/ai/ai.service.ts index 22bc8012d..cdd67ac99 100644 --- a/backend/src/entities/ai/ai.service.ts +++ b/backend/src/entities/ai/ai.service.ts @@ -276,7 +276,10 @@ Respond ONLY with valid JSON in this exact format (no markdown, no explanations) ] } -IMPORTANT: For each widget, include appropriate widget_params based on the column name, type, and semantics. Use empty {} for widgets that don't need special configuration.`; +IMPORTANT: +- For each widget, include appropriate widget_params based on the column name, type, and semantics. Use empty {} for widgets that don't need special configuration. +- Output ONLY valid JSON. Do NOT include any comments (no // or /* */ comments) in the JSON output. +- All widget_params must be valid JSON objects without any comments or explanatory text.`; } private parseAIResponse(aiResponse: string, tablesInformation: Array): AIResponse { diff --git a/backend/src/entities/ai/use-cases/request-info-from-table-with-ai-v7.use.case.ts b/backend/src/entities/ai/use-cases/request-info-from-table-with-ai-v7.use.case.ts index 78cf46cc2..93e830326 100644 --- a/backend/src/entities/ai/use-cases/request-info-from-table-with-ai-v7.use.case.ts +++ b/backend/src/entities/ai/use-cases/request-info-from-table-with-ai-v7.use.case.ts @@ -67,6 +67,7 @@ export class RequestInfoFromTableWithAIUseCaseV7 let chatIdForHeader: string | null = null; let foundUserAiChat: UserAiChatEntity | null = null; + let isNewChat = false; if (ai_thread_id) { foundUserAiChat = await this._dbContext.userAiChatRepository.findChatByIdAndUserId(ai_thread_id, user_id); @@ -78,6 +79,7 @@ export class RequestInfoFromTableWithAIUseCaseV7 if (!foundUserAiChat) { foundUserAiChat = await this._dbContext.userAiChatRepository.createChatForUser(user_id); chatIdForHeader = foundUserAiChat.id; + isNewChat = true; } if (chatIdForHeader) { @@ -86,6 +88,12 @@ export class RequestInfoFromTableWithAIUseCaseV7 await this._dbContext.aiChatMessageRepository.saveMessage(foundUserAiChat.id, user_message, MessageRole.user); + if (isNewChat) { + this.generateAndUpdateChatName(foundUserAiChat.id, user_message).catch((error) => { + Sentry.captureException(error); + }); + } + const messages = new MessageBuilder().system(systemPrompt).human(user_message).build(); try { @@ -338,4 +346,32 @@ export class RequestInfoFromTableWithAIUseCaseV7 return { foundConnection, dataAccessObject, databaseType, isMongoDb, userEmail }; } + + private async generateAndUpdateChatName(chatId: string, userMessage: string): Promise { + try { + const CHAT_NAME_GENERATION_PROMPT = `Generate a very short, concise title (max 5-6 words) for a chat conversation based on the user's first question. +The title should capture the main topic or intent. +Respond ONLY with the title, no quotes, no explanation. +User question: `; + const prompt = CHAT_NAME_GENERATION_PROMPT + userMessage; + const messages = new MessageBuilder().human(prompt).build(); + + let generatedName = ''; + const stream = await this.aiCoreService.streamChatWithToolsAndProvider(this.aiProvider, messages, []); + + for await (const chunk of stream) { + if (chunk.type === 'text' && chunk.content) { + generatedName += chunk.content; + } + } + + generatedName = generatedName.trim().slice(0, 100); + + if (generatedName) { + await this._dbContext.userAiChatRepository.updateChatName(chatId, generatedName); + } + } catch (error) { + Sentry.captureException(error); + } + } }