Skip to content

Commit 66adf95

Browse files
authored
Merge pull request #1548 from rocket-admin/backend_ai_conversation_history
feat: add chat name update functionality and improve AI response handling
2 parents 86844da + 427c441 commit 66adf95

File tree

5 files changed

+55
-1
lines changed

5 files changed

+55
-1
lines changed

backend/src/ai-core/tools/query-validators.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,5 +107,11 @@ export function cleanAIJsonResponse(response: string): string {
107107
if (cleanedResponse.endsWith('```')) {
108108
cleanedResponse = cleanedResponse.slice(0, -3);
109109
}
110+
cleanedResponse = cleanedResponse.trim();
111+
112+
cleanedResponse = cleanedResponse.replace(/^\s*\/\/.*$/gm, '');
113+
cleanedResponse = cleanedResponse.replace(/\/\*[\s\S]*?\*\//g, '');
114+
cleanedResponse = cleanedResponse.replace(/,(\s*[}\]])/g, '$1');
115+
110116
return cleanedResponse.trim();
111117
}

backend/src/entities/ai/ai-conversation-history/user-ai-chat/repository/user-ai-chat-repository.extension.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,12 @@ export const userAiChatRepositoryExtension: IUserAiChatRepository = {
3232
});
3333
return await this.save(newChat);
3434
},
35+
36+
async updateChatName(chatId: string, name: string): Promise<void> {
37+
await this.createQueryBuilder()
38+
.update(UserAiChatEntity)
39+
.set({ name })
40+
.where('id = :chatId', { chatId })
41+
.execute();
42+
},
3543
};

backend/src/entities/ai/ai-conversation-history/user-ai-chat/repository/user-ai-chat-repository.interface.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ export interface IUserAiChatRepository {
55
findChatByIdAndUserId(chatId: string, userId: string): Promise<UserAiChatEntity | null>;
66
findChatWithMessagesByIdAndUserId(chatId: string, userId: string): Promise<UserAiChatEntity | null>;
77
createChatForUser(userId: string, name?: string): Promise<UserAiChatEntity>;
8+
updateChatName(chatId: string, name: string): Promise<void>;
89
}

backend/src/entities/ai/ai.service.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,10 @@ Respond ONLY with valid JSON in this exact format (no markdown, no explanations)
276276
]
277277
}
278278
279-
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.`;
279+
IMPORTANT:
280+
- 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.
281+
- Output ONLY valid JSON. Do NOT include any comments (no // or /* */ comments) in the JSON output.
282+
- All widget_params must be valid JSON objects without any comments or explanatory text.`;
280283
}
281284

282285
private parseAIResponse(aiResponse: string, tablesInformation: Array<TableInformation>): AIResponse {

backend/src/entities/ai/use-cases/request-info-from-table-with-ai-v7.use.case.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ export class RequestInfoFromTableWithAIUseCaseV7
6767

6868
let chatIdForHeader: string | null = null;
6969
let foundUserAiChat: UserAiChatEntity | null = null;
70+
let isNewChat = false;
7071

7172
if (ai_thread_id) {
7273
foundUserAiChat = await this._dbContext.userAiChatRepository.findChatByIdAndUserId(ai_thread_id, user_id);
@@ -78,6 +79,7 @@ export class RequestInfoFromTableWithAIUseCaseV7
7879
if (!foundUserAiChat) {
7980
foundUserAiChat = await this._dbContext.userAiChatRepository.createChatForUser(user_id);
8081
chatIdForHeader = foundUserAiChat.id;
82+
isNewChat = true;
8183
}
8284

8385
if (chatIdForHeader) {
@@ -86,6 +88,12 @@ export class RequestInfoFromTableWithAIUseCaseV7
8688

8789
await this._dbContext.aiChatMessageRepository.saveMessage(foundUserAiChat.id, user_message, MessageRole.user);
8890

91+
if (isNewChat) {
92+
this.generateAndUpdateChatName(foundUserAiChat.id, user_message).catch((error) => {
93+
Sentry.captureException(error);
94+
});
95+
}
96+
8997
const messages = new MessageBuilder().system(systemPrompt).human(user_message).build();
9098

9199
try {
@@ -338,4 +346,32 @@ export class RequestInfoFromTableWithAIUseCaseV7
338346

339347
return { foundConnection, dataAccessObject, databaseType, isMongoDb, userEmail };
340348
}
349+
350+
private async generateAndUpdateChatName(chatId: string, userMessage: string): Promise<void> {
351+
try {
352+
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.
353+
The title should capture the main topic or intent.
354+
Respond ONLY with the title, no quotes, no explanation.
355+
User question: `;
356+
const prompt = CHAT_NAME_GENERATION_PROMPT + userMessage;
357+
const messages = new MessageBuilder().human(prompt).build();
358+
359+
let generatedName = '';
360+
const stream = await this.aiCoreService.streamChatWithToolsAndProvider(this.aiProvider, messages, []);
361+
362+
for await (const chunk of stream) {
363+
if (chunk.type === 'text' && chunk.content) {
364+
generatedName += chunk.content;
365+
}
366+
}
367+
368+
generatedName = generatedName.trim().slice(0, 100);
369+
370+
if (generatedName) {
371+
await this._dbContext.userAiChatRepository.updateChatName(chatId, generatedName);
372+
}
373+
} catch (error) {
374+
Sentry.captureException(error);
375+
}
376+
}
341377
}

0 commit comments

Comments
 (0)