Skip to content

Commit c339ce9

Browse files
authored
Merge pull request #1549 from rocket-admin/backend_ai_conversation_history
Backend ai conversation history
2 parents 1197d66 + dd34490 commit c339ce9

5 files changed

Lines changed: 94 additions & 5 deletions

File tree

backend/src/entities/ai/ai-conversation-history/ai-chat-messages/ai-chat-message.entity.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ export class AiChatMessageEntity {
2222
@Column({ nullable: true, default: null, type: 'enum', enum: MessageRole })
2323
role: MessageRole;
2424

25+
@Column({ nullable: true, default: null, type: 'varchar', length: 255 })
26+
response_id: string;
27+
2528
@CreateDateColumn({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
2629
created_at: Date;
2730

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@ export const aiChatMessageRepositoryExtension: IAiChatMessageRepository = {
1010
.getMany();
1111
},
1212

13+
async findLastAiMessageForChat(chatId: string): Promise<AiChatMessageEntity | null> {
14+
return await this.createQueryBuilder('ai_chat_message')
15+
.where('ai_chat_message.ai_chat_id = :chatId', { chatId })
16+
.andWhere('ai_chat_message.role = :role', { role: MessageRole.ai })
17+
.orderBy('ai_chat_message.created_at', 'DESC')
18+
.getOne();
19+
},
20+
1321
async deleteMessagesForChat(chatId: string): Promise<void> {
1422
await this.createQueryBuilder()
1523
.delete()
@@ -18,11 +26,17 @@ export const aiChatMessageRepositoryExtension: IAiChatMessageRepository = {
1826
.execute();
1927
},
2028

21-
async saveMessage(chatId: string, message: string, role: MessageRole): Promise<AiChatMessageEntity> {
29+
async saveMessage(
30+
chatId: string,
31+
message: string,
32+
role: MessageRole,
33+
responseId?: string,
34+
): Promise<AiChatMessageEntity> {
2235
const newMessage = this.create({
2336
ai_chat_id: chatId,
2437
message,
2538
role,
39+
response_id: responseId,
2640
});
2741
return await this.save(newMessage);
2842
},

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { MessageRole } from '../message-role.enum.js';
33

44
export interface IAiChatMessageRepository {
55
findMessagesForChat(chatId: string): Promise<AiChatMessageEntity[]>;
6+
findLastAiMessageForChat(chatId: string): Promise<AiChatMessageEntity | null>;
67
deleteMessagesForChat(chatId: string): Promise<void>;
7-
saveMessage(chatId: string, message: string, role: MessageRole): Promise<AiChatMessageEntity>;
8+
saveMessage(chatId: string, message: string, role: MessageRole, responseId?: string): Promise<AiChatMessageEntity>;
89
}

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

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
isValidMongoDbCommand,
2727
wrapQueryWithLimit,
2828
AIProviderType,
29+
AIProviderConfig,
2930
} from '../../../ai-core/index.js';
3031
import { UserAiChatEntity } from '../ai-conversation-history/user-ai-chat/user-ai-chat.entity.js';
3132
import { MessageRole } from '../ai-conversation-history/ai-chat-messages/message-role.enum.js';
@@ -94,24 +95,36 @@ export class RequestInfoFromTableWithAIUseCaseV7
9495
});
9596
}
9697

97-
const messages = new MessageBuilder().system(systemPrompt).human(user_message).build();
98+
const { messages, previousResponseId } = await this.buildMessagesWithHistory(
99+
systemPrompt,
100+
user_message,
101+
foundUserAiChat.id,
102+
isNewChat,
103+
);
98104

99105
try {
100-
const { accumulatedResponse } = await this.processWithToolLoop(
106+
const config: AIProviderConfig = {};
107+
if (this.aiProvider === AIProviderType.OPENAI && previousResponseId) {
108+
config.previousResponseId = previousResponseId;
109+
}
110+
111+
const { accumulatedResponse, lastResponseId } = await this.processWithToolLoop(
101112
messages,
102113
tools,
103114
response,
104115
dataAccessObject,
105116
tableName,
106117
userEmail,
107118
foundConnection,
119+
config,
108120
);
109121

110122
if (accumulatedResponse) {
111123
await this._dbContext.aiChatMessageRepository.saveMessage(
112124
foundUserAiChat.id,
113125
accumulatedResponse,
114126
MessageRole.ai,
127+
lastResponseId,
115128
);
116129
}
117130

@@ -133,15 +146,22 @@ export class RequestInfoFromTableWithAIUseCaseV7
133146
inputTableName: string,
134147
userEmail: string,
135148
foundConnection: ConnectionEntity,
149+
config: AIProviderConfig = {},
136150
): Promise<{ lastResponseId: string | null; accumulatedResponse: string }> {
137151
let currentMessages = [...messages];
138152
let lastResponseId: string | null = null;
139153
let depth = 0;
140154
let totalAccumulatedResponse = '';
155+
let currentConfig = { ...config };
141156

142157
while (depth < this.maxDepth) {
143158
try {
144-
const stream = await this.aiCoreService.streamChatWithToolsAndProvider(this.aiProvider, currentMessages, tools);
159+
const stream = await this.aiCoreService.streamChatWithToolsAndProvider(
160+
this.aiProvider,
161+
currentMessages,
162+
tools,
163+
currentConfig,
164+
);
145165

146166
let pendingToolCalls: AIToolCall[] = [];
147167
let accumulatedContent = '';
@@ -174,6 +194,10 @@ export class RequestInfoFromTableWithAIUseCaseV7
174194
foundConnection,
175195
);
176196

197+
if (this.aiProvider === AIProviderType.OPENAI && lastResponseId) {
198+
currentConfig = { ...currentConfig, previousResponseId: lastResponseId };
199+
}
200+
177201
const continuationBuilder = MessageBuilder.fromMessages(currentMessages);
178202
continuationBuilder.ai(accumulatedContent, pendingToolCalls);
179203
for (const result of toolResults) {
@@ -347,6 +371,40 @@ export class RequestInfoFromTableWithAIUseCaseV7
347371
return { foundConnection, dataAccessObject, databaseType, isMongoDb, userEmail };
348372
}
349373

374+
private async buildMessagesWithHistory(
375+
systemPrompt: string,
376+
userMessage: string,
377+
chatId: string,
378+
isNewChat: boolean,
379+
): Promise<{ messages: BaseMessage[]; previousResponseId: string | null }> {
380+
if (isNewChat) {
381+
const messages = new MessageBuilder().system(systemPrompt).human(userMessage).build();
382+
return { messages, previousResponseId: null };
383+
}
384+
385+
if (this.aiProvider === AIProviderType.OPENAI) {
386+
const lastAiMessage = await this._dbContext.aiChatMessageRepository.findLastAiMessageForChat(chatId);
387+
const previousResponseId = lastAiMessage?.response_id || null;
388+
const messages = new MessageBuilder().system(systemPrompt).human(userMessage).build();
389+
return { messages, previousResponseId };
390+
}
391+
392+
const previousMessages = await this._dbContext.aiChatMessageRepository.findMessagesForChat(chatId);
393+
const builder = new MessageBuilder().system(systemPrompt);
394+
395+
for (const msg of previousMessages) {
396+
if (msg.role === MessageRole.user) {
397+
builder.human(msg.message);
398+
} else if (msg.role === MessageRole.ai) {
399+
builder.ai(msg.message);
400+
}
401+
}
402+
403+
builder.human(userMessage);
404+
405+
return { messages: builder.build(), previousResponseId: null };
406+
}
407+
350408
private async generateAndUpdateChatName(chatId: string, userMessage: string): Promise<void> {
351409
try {
352410
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.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { MigrationInterface, QueryRunner } from 'typeorm';
2+
3+
export class AddResponseIdToAiChatMessage1769790101930 implements MigrationInterface {
4+
name = 'AddResponseIdToAiChatMessage1769790101930';
5+
6+
public async up(queryRunner: QueryRunner): Promise<void> {
7+
await queryRunner.query(`ALTER TABLE "ai_chat_message" ADD "response_id" character varying(255)`);
8+
}
9+
10+
public async down(queryRunner: QueryRunner): Promise<void> {
11+
await queryRunner.query(`ALTER TABLE "ai_chat_message" DROP COLUMN "response_id"`);
12+
}
13+
}

0 commit comments

Comments
 (0)