mirror of https://github.com/buster-so/buster.git
make response messages a helper function
This commit is contained in:
parent
db90ed13f4
commit
f73385aa87
|
@ -1,5 +1,14 @@
|
||||||
import { initializeOrUpdateMessage, updateChatTitle } from './chatStreamMessageHelper';
|
import {
|
||||||
|
initializeOrUpdateMessage,
|
||||||
|
updateChatTitle,
|
||||||
|
updateResponseMessage
|
||||||
|
} from './chatStreamMessageHelper';
|
||||||
import { IBusterChatMessage, IBusterChat } from '../interfaces';
|
import { IBusterChatMessage, IBusterChat } from '../interfaces';
|
||||||
|
import { ChatEvent_GeneratingResponseMessage } from '@/api/buster_socket/chats';
|
||||||
|
import {
|
||||||
|
BusterChatResponseMessage_file,
|
||||||
|
BusterChatResponseMessage_text
|
||||||
|
} from '@/api/asset_interfaces';
|
||||||
|
|
||||||
describe('initializeOrUpdateMessage', () => {
|
describe('initializeOrUpdateMessage', () => {
|
||||||
it('should initialize a new message when currentMessage is undefined', () => {
|
it('should initialize a new message when currentMessage is undefined', () => {
|
||||||
|
@ -119,3 +128,163 @@ describe('updateChatTitle', () => {
|
||||||
expect(result.title).toBe('Initial Title');
|
expect(result.title).toBe('Initial Title');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('updateResponseMessage', () => {
|
||||||
|
it('should create a new message when currentMessage is undefined', () => {
|
||||||
|
const mockEvent: ChatEvent_GeneratingResponseMessage = {
|
||||||
|
chat_id: 'test-chat-id',
|
||||||
|
message_id: 'test-message-id',
|
||||||
|
response_message: {
|
||||||
|
id: 'response-1',
|
||||||
|
type: 'text',
|
||||||
|
message: '',
|
||||||
|
message_chunk: 'Hello'
|
||||||
|
} as BusterChatResponseMessage_text,
|
||||||
|
progress: 'in_progress' as const
|
||||||
|
};
|
||||||
|
const result = updateResponseMessage('test-message-id', undefined, mockEvent);
|
||||||
|
|
||||||
|
expect(result.id).toBe('test-message-id');
|
||||||
|
expect(result.response_message_ids).toContain('response-1');
|
||||||
|
expect(
|
||||||
|
(result.response_messages['response-1'] as BusterChatResponseMessage_text).message
|
||||||
|
).toEqual('Hello');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update existing message with new response', () => {
|
||||||
|
const currentMessage: IBusterChatMessage = {
|
||||||
|
id: 'test-message-id',
|
||||||
|
isCompletedStream: false,
|
||||||
|
request_message: {
|
||||||
|
request: 'test request',
|
||||||
|
sender_id: 'user1',
|
||||||
|
sender_name: 'Test User',
|
||||||
|
sender_avatar: null
|
||||||
|
},
|
||||||
|
response_message_ids: ['response-1'],
|
||||||
|
reasoning_message_ids: [],
|
||||||
|
response_messages: {
|
||||||
|
'response-1': {
|
||||||
|
id: 'response-1',
|
||||||
|
type: 'text',
|
||||||
|
message: 'Hello',
|
||||||
|
message_chunk: undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reasoning_messages: {},
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
final_reasoning_message: null
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockEvent: ChatEvent_GeneratingResponseMessage = {
|
||||||
|
chat_id: 'test-chat-id',
|
||||||
|
message_id: 'test-message-id',
|
||||||
|
response_message: {
|
||||||
|
id: 'response-1',
|
||||||
|
type: 'text',
|
||||||
|
message: '',
|
||||||
|
message_chunk: ' World'
|
||||||
|
} as BusterChatResponseMessage_text,
|
||||||
|
progress: 'in_progress' as const
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = updateResponseMessage('test-message-id', currentMessage, mockEvent);
|
||||||
|
|
||||||
|
expect(result.response_message_ids).toContain('response-1');
|
||||||
|
expect(
|
||||||
|
(result.response_messages['response-1'] as BusterChatResponseMessage_text).message
|
||||||
|
).toEqual('Hello World');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle large message chunks correctly', () => {
|
||||||
|
const currentMessage: IBusterChatMessage = {
|
||||||
|
id: 'test-message-id',
|
||||||
|
isCompletedStream: false,
|
||||||
|
request_message: {
|
||||||
|
request: 'test request',
|
||||||
|
sender_id: 'user1',
|
||||||
|
sender_name: 'Test User',
|
||||||
|
sender_avatar: null
|
||||||
|
},
|
||||||
|
response_message_ids: ['response-1'],
|
||||||
|
reasoning_message_ids: [],
|
||||||
|
response_messages: {
|
||||||
|
'response-1': {
|
||||||
|
id: 'response-1',
|
||||||
|
type: 'text',
|
||||||
|
message: 'Initial message',
|
||||||
|
message_chunk: undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reasoning_messages: {},
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
final_reasoning_message: null
|
||||||
|
};
|
||||||
|
|
||||||
|
const largeChunk =
|
||||||
|
' '.repeat(1000) +
|
||||||
|
'This is a very large message chunk that should be appended correctly. '.repeat(10);
|
||||||
|
|
||||||
|
const mockEvent: ChatEvent_GeneratingResponseMessage = {
|
||||||
|
chat_id: 'test-chat-id',
|
||||||
|
message_id: 'test-message-id',
|
||||||
|
response_message: {
|
||||||
|
id: 'response-1',
|
||||||
|
type: 'text',
|
||||||
|
message: '',
|
||||||
|
message_chunk: largeChunk
|
||||||
|
} as BusterChatResponseMessage_text,
|
||||||
|
progress: 'in_progress' as const
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = updateResponseMessage('test-message-id', currentMessage, mockEvent);
|
||||||
|
|
||||||
|
expect(result.response_message_ids).toContain('response-1');
|
||||||
|
expect(
|
||||||
|
(result.response_messages['response-1'] as BusterChatResponseMessage_text).message
|
||||||
|
).toEqual('Initial message' + largeChunk);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle file type response messages', () => {
|
||||||
|
const responseMessageFile: BusterChatResponseMessage_file = {
|
||||||
|
id: 'response-1',
|
||||||
|
type: 'file',
|
||||||
|
file_name: 'initial.txt',
|
||||||
|
file_type: 'metric',
|
||||||
|
version_number: 1,
|
||||||
|
version_id: '1',
|
||||||
|
filter_version_id: '1'
|
||||||
|
};
|
||||||
|
|
||||||
|
const currentMessage: IBusterChatMessage = {
|
||||||
|
id: 'test-message-id',
|
||||||
|
isCompletedStream: false,
|
||||||
|
request_message: {
|
||||||
|
request: 'test request',
|
||||||
|
sender_id: 'user1',
|
||||||
|
sender_name: 'Test User',
|
||||||
|
sender_avatar: null
|
||||||
|
},
|
||||||
|
response_message_ids: ['response-1'],
|
||||||
|
reasoning_message_ids: [],
|
||||||
|
response_messages: {
|
||||||
|
'response-1': responseMessageFile
|
||||||
|
},
|
||||||
|
reasoning_messages: {},
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
final_reasoning_message: null
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockEvent: ChatEvent_GeneratingResponseMessage = {
|
||||||
|
chat_id: 'test-chat-id',
|
||||||
|
message_id: 'test-message-id',
|
||||||
|
response_message: { ...responseMessageFile, id: 'response-2' },
|
||||||
|
progress: 'in_progress' as const
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = updateResponseMessage('test-message-id', currentMessage, mockEvent);
|
||||||
|
|
||||||
|
expect(result.response_message_ids).toContain('response-2');
|
||||||
|
expect(result.response_messages['response-2']).toEqual(mockEvent.response_message);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
import { create } from 'mutative';
|
import { create } from 'mutative';
|
||||||
import { IBusterChat, IBusterChatMessage } from '../interfaces';
|
import { IBusterChat, IBusterChatMessage } from '../interfaces';
|
||||||
import { ChatEvent_GeneratingTitle } from '@/api/buster_socket/chats';
|
import {
|
||||||
|
ChatEvent_GeneratingTitle,
|
||||||
|
ChatEvent_GeneratingResponseMessage
|
||||||
|
} from '@/api/buster_socket/chats';
|
||||||
|
import { BusterChatResponseMessage_text } from '@/api/asset_interfaces';
|
||||||
|
|
||||||
const createInitialMessage = (messageId: string): IBusterChatMessage => ({
|
const createInitialMessage = (messageId: string): IBusterChatMessage => ({
|
||||||
id: messageId,
|
id: messageId,
|
||||||
|
@ -41,3 +45,55 @@ export const updateChatTitle = (
|
||||||
if (newTitle) draft.title = newTitle;
|
if (newTitle) draft.title = newTitle;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const updateResponseMessage = (
|
||||||
|
messageId: string,
|
||||||
|
currentMessage: IBusterChatMessage | undefined,
|
||||||
|
event: ChatEvent_GeneratingResponseMessage
|
||||||
|
): IBusterChatMessage => {
|
||||||
|
const { response_message } = event;
|
||||||
|
|
||||||
|
if (!response_message?.id) {
|
||||||
|
return currentMessage || createInitialMessage(messageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const responseMessageId = response_message.id;
|
||||||
|
const existingResponseMessage = currentMessage?.response_messages?.[responseMessageId];
|
||||||
|
const isNewResponseMessage = !existingResponseMessage;
|
||||||
|
|
||||||
|
let updatedMessage = currentMessage || createInitialMessage(messageId);
|
||||||
|
|
||||||
|
if (isNewResponseMessage) {
|
||||||
|
updatedMessage = initializeOrUpdateMessage(messageId, updatedMessage, (draft) => {
|
||||||
|
if (!draft.response_messages) {
|
||||||
|
draft.response_messages = {};
|
||||||
|
}
|
||||||
|
draft.response_messages[responseMessageId] = response_message;
|
||||||
|
if (!draft.response_message_ids) {
|
||||||
|
draft.response_message_ids = [];
|
||||||
|
}
|
||||||
|
draft.response_message_ids.push(responseMessageId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response_message.type === 'text') {
|
||||||
|
const existingResponseMessageText = existingResponseMessage as BusterChatResponseMessage_text;
|
||||||
|
const isStreaming =
|
||||||
|
response_message.message_chunk !== undefined && response_message.message_chunk !== null;
|
||||||
|
|
||||||
|
updatedMessage = initializeOrUpdateMessage(messageId, updatedMessage, (draft) => {
|
||||||
|
const responseMessage = draft.response_messages?.[responseMessageId];
|
||||||
|
if (!responseMessage) return;
|
||||||
|
const messageText = responseMessage as BusterChatResponseMessage_text;
|
||||||
|
Object.assign(messageText, {
|
||||||
|
...existingResponseMessageText,
|
||||||
|
...response_message,
|
||||||
|
message: isStreaming
|
||||||
|
? (existingResponseMessageText?.message || '') + (response_message.message_chunk || '')
|
||||||
|
: response_message.message
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedMessage;
|
||||||
|
};
|
||||||
|
|
|
@ -22,7 +22,11 @@ import { IBusterChat, IBusterChatMessage } from '../interfaces';
|
||||||
import { queryKeys } from '@/api/query_keys';
|
import { queryKeys } from '@/api/query_keys';
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
import { create } from 'mutative';
|
import { create } from 'mutative';
|
||||||
import { initializeOrUpdateMessage, updateChatTitle } from './chatStreamMessageHelper';
|
import {
|
||||||
|
initializeOrUpdateMessage,
|
||||||
|
updateChatTitle,
|
||||||
|
updateResponseMessage
|
||||||
|
} from './chatStreamMessageHelper';
|
||||||
|
|
||||||
export const useChatStreamMessage = () => {
|
export const useChatStreamMessage = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
@ -116,55 +120,18 @@ export const useChatStreamMessage = () => {
|
||||||
|
|
||||||
const _generatingResponseMessageCallback = useMemoizedFn(
|
const _generatingResponseMessageCallback = useMemoizedFn(
|
||||||
(_: null, d: ChatEvent_GeneratingResponseMessage) => {
|
(_: null, d: ChatEvent_GeneratingResponseMessage) => {
|
||||||
const { message_id, response_message } = d;
|
const { message_id } = d;
|
||||||
|
|
||||||
if (!response_message?.id) return;
|
const updatedMessage = updateResponseMessage(
|
||||||
|
message_id,
|
||||||
const responseMessageId = response_message.id;
|
chatRefMessages.current[message_id],
|
||||||
const existingResponseMessage =
|
d
|
||||||
chatRefMessages.current[message_id]?.response_messages?.[responseMessageId];
|
);
|
||||||
const isNewResponseMessage = !existingResponseMessage;
|
|
||||||
|
|
||||||
let currentMessage = chatRefMessages.current[message_id];
|
|
||||||
|
|
||||||
if (isNewResponseMessage) {
|
|
||||||
currentMessage = initializeOrUpdateMessage(message_id, currentMessage, (draft) => {
|
|
||||||
if (!draft.response_messages) {
|
|
||||||
draft.response_messages = {};
|
|
||||||
}
|
|
||||||
draft.response_messages[responseMessageId] = response_message;
|
|
||||||
if (!draft.response_message_ids) {
|
|
||||||
draft.response_message_ids = [];
|
|
||||||
}
|
|
||||||
draft.response_message_ids.push(responseMessageId);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response_message.type === 'text') {
|
|
||||||
const existingResponseMessageText =
|
|
||||||
existingResponseMessage as BusterChatResponseMessage_text;
|
|
||||||
const isStreaming =
|
|
||||||
response_message.message_chunk !== undefined && response_message.message_chunk !== null;
|
|
||||||
|
|
||||||
currentMessage = initializeOrUpdateMessage(message_id, currentMessage, (draft) => {
|
|
||||||
const responseMessage = draft.response_messages?.[responseMessageId];
|
|
||||||
if (!responseMessage) return;
|
|
||||||
const messageText = responseMessage as BusterChatMessageReasoning_text;
|
|
||||||
Object.assign(messageText, {
|
|
||||||
...existingResponseMessageText,
|
|
||||||
...response_message,
|
|
||||||
message: isStreaming
|
|
||||||
? (existingResponseMessageText?.message || '') +
|
|
||||||
(response_message.message_chunk || '')
|
|
||||||
: response_message.message
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onUpdateChatMessageTransition({
|
onUpdateChatMessageTransition({
|
||||||
id: message_id,
|
id: message_id,
|
||||||
response_messages: currentMessage?.response_messages,
|
response_messages: updatedMessage?.response_messages,
|
||||||
response_message_ids: currentMessage?.response_message_ids
|
response_message_ids: updatedMessage?.response_message_ids
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue