better handling for black box messages

This commit is contained in:
Nate Kelley 2025-03-06 11:17:53 -07:00
parent 7423fe31a3
commit 3fedad0866
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
4 changed files with 58 additions and 24 deletions

View File

@ -522,7 +522,8 @@ describe('updateReasoningMessage', () => {
}); });
it('should handle multiple updates and append text to existing reasoning message', () => { it('should handle multiple updates and append text to existing reasoning message', () => {
const reasoning: BusterChatMessageReasoning_text = { const messageId = 'test-message-id';
let reasoning: BusterChatMessageReasoning_text = {
id: 'reasoning-1', id: 'reasoning-1',
type: 'text', type: 'text',
message: '', message: '',
@ -533,23 +534,46 @@ describe('updateReasoningMessage', () => {
}; };
// First update with "Hello" // First update with "Hello"
let result = updateReasoningMessage('test-message-id', undefined, reasoning); let result = updateReasoningMessage(messageId, undefined, reasoning);
expect( expect(
(result.reasoning_messages['reasoning-1'] as BusterChatMessageReasoning_text).message (result.reasoning_messages['reasoning-1'] as BusterChatMessageReasoning_text).message
).toBe('Hello'); ).toBe('Hello');
// Second update with ", how" // Second update with ", how"
reasoning.message_chunk = ', how'; reasoning = {
result = updateReasoningMessage('test-message-id', result, reasoning); ...reasoning,
message_chunk: ', how'
};
result = updateReasoningMessage(messageId, result, reasoning);
expect( expect(
(result.reasoning_messages['reasoning-1'] as BusterChatMessageReasoning_text).message (result.reasoning_messages['reasoning-1'] as BusterChatMessageReasoning_text).message
).toBe('Hello, how'); ).toBe('Hello, how');
// Third update with " are you doing today?" // Third update with " are you doing today?"
reasoning.message_chunk = ' are you doing today?'; reasoning = {
result = updateReasoningMessage('test-message-id', result, reasoning); ...reasoning,
message_chunk: ' are you doing today?'
};
result = updateReasoningMessage(messageId, result, reasoning);
expect( expect(
(result.reasoning_messages['reasoning-1'] as BusterChatMessageReasoning_text).message (result.reasoning_messages['reasoning-1'] as BusterChatMessageReasoning_text).message
).toBe('Hello, how are you doing today?'); ).toBe('Hello, how are you doing today?');
let reasoning2: BusterChatMessageReasoning_text = {
...reasoning,
message_chunk: 'new reasoning message baby',
id: 'reasoning-2'
};
result = updateReasoningMessage(messageId, result, reasoning2);
expect(
(result.reasoning_messages['reasoning-1'] as BusterChatMessageReasoning_text).message
).toBe('Hello, how are you doing today?');
expect(result.reasoning_message_ids).toContain('reasoning-2');
expect(result.reasoning_message_ids.length).toBe(2);
expect(
(result.reasoning_messages['reasoning-2'] as BusterChatMessageReasoning_text).message
).toBe('new reasoning message baby');
}); });
}); });

View File

@ -16,55 +16,63 @@ export const useBlackBoxMessage = () => {
const getChatMessageMemoized = useBusterChatContextSelector((x) => x.getChatMessageMemoized); const getChatMessageMemoized = useBusterChatContextSelector((x) => x.getChatMessageMemoized);
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const removeAutoThought = useMemoizedFn(({ messageId }: { messageId: string }) => { const clearTimeoutRef = useMemoizedFn((messageId: string) => {
console.log('removeAutoThought', messageId);
if (timeoutRef.current[messageId]) { if (timeoutRef.current[messageId]) {
clearTimeout(timeoutRef.current[messageId]); clearTimeout(timeoutRef.current[messageId]);
delete timeoutRef.current[messageId]; delete timeoutRef.current[messageId];
} }
});
const removeBlackBoxMessage = useMemoizedFn(({ messageId }: { messageId: string }) => {
console.log('removeBlackBoxMessage', messageId);
clearTimeoutRef(messageId);
const options = queryKeys.chatsBlackBoxMessages(messageId); const options = queryKeys.chatsBlackBoxMessages(messageId);
queryClient.setQueryData(options.queryKey, null); queryClient.setQueryData(options.queryKey, null);
}); });
const addAutoThought = useMemoizedFn(({ messageId }: { messageId: string }) => { const addBlackBoxMessage = useMemoizedFn(({ messageId }: { messageId: string }) => {
const randomThought = getRandomThought(); const randomThought = getRandomThought();
console.log(messageId, randomThought); console.log(messageId, randomThought);
const options = queryKeys.chatsBlackBoxMessages(messageId); const options = queryKeys.chatsBlackBoxMessages(messageId);
queryClient.setQueryData(options.queryKey, randomThought); queryClient.setQueryData(options.queryKey, randomThought);
}); });
const checkAutoThought = useMemoizedFn( const checkBlackBoxMessage = useMemoizedFn(
(message: IBusterChatMessage, event: ChatEvent_GeneratingReasoningMessage) => { (message: IBusterChatMessage, event: ChatEvent_GeneratingReasoningMessage) => {
const isFinishedReasoningMessage = event.reasoning.status !== 'loading'; const isFinishedReasoningMessage = event.reasoning.status !== 'loading';
if (isFinishedReasoningMessage) { if (isFinishedReasoningMessage) {
addAutoThought({ messageId: message.id }); clearTimeoutRef(message.id);
addBlackBoxMessage({ messageId: message.id });
_loopAutoThought({ messageId: message.id }); _loopAutoThought({ messageId: message.id });
} else { } else {
removeAutoThought({ messageId: message.id }); removeBlackBoxMessage({ messageId: message.id });
} }
} }
); );
const _loopAutoThought = useMemoizedFn(async ({ messageId }: { messageId: string }) => { const _loopAutoThought = useMemoizedFn(async ({ messageId }: { messageId: string }) => {
const randomDelay = random(3000, 5000); const randomDelay = random(5000, 5000);
timeoutRef.current[messageId] = setTimeout(() => { timeoutRef.current[messageId] = setTimeout(() => {
const message = getChatMessageMemoized(messageId); const message = getChatMessageMemoized(messageId);
console.log('loopAutoThought', messageId, !!message);
if (!message) return; if (!message) return;
if (!timeoutRef.current[messageId]) return;
console.log('loopAutoThought', messageId, !!message);
const isMessageCompletedStream = !!message?.isCompletedStream; const isMessageCompletedStream = !!message?.isCompletedStream;
const lastReasoningMessageId = last(message?.reasoning_message_ids) || ''; const lastReasoningMessageId = last(message?.reasoning_message_ids) || '';
const lastReasoningMessage = message?.reasoning_messages[lastReasoningMessageId]; const lastReasoningMessage = message?.reasoning_messages[lastReasoningMessageId];
const isLastReasoningMessageCompleted = lastReasoningMessage?.status === 'completed'; const isLastReasoningMessageCompleted = lastReasoningMessage?.status === 'completed';
if (!isMessageCompletedStream && isLastReasoningMessageCompleted) { if (!isMessageCompletedStream && isLastReasoningMessageCompleted) {
addAutoThought({ messageId }); addBlackBoxMessage({ messageId });
_loopAutoThought({ messageId }); _loopAutoThought({ messageId });
} }
}, randomDelay); }, randomDelay);
}); });
return { checkAutoThought, removeAutoThought }; return { checkBlackBoxMessage, removeBlackBoxMessage };
}; };
const getRandomThought = (currentThought?: string): string => { const getRandomThought = (currentThought?: string): string => {

View File

@ -32,7 +32,7 @@ export const useChatStreamMessage = () => {
const chatRefMessages = useRef<Record<string, IBusterChatMessage>>({}); const chatRefMessages = useRef<Record<string, IBusterChatMessage>>({});
const [isPending, startTransition] = useTransition(); const [isPending, startTransition] = useTransition();
const { checkAutoThought, removeAutoThought } = useBlackBoxMessage(); const { checkBlackBoxMessage, removeBlackBoxMessage } = useBlackBoxMessage();
const onUpdateChatMessageTransition = useMemoizedFn( const onUpdateChatMessageTransition = useMemoizedFn(
(chatMessage: Parameters<typeof onUpdateChatMessage>[0]) => { (chatMessage: Parameters<typeof onUpdateChatMessage>[0]) => {
@ -40,6 +40,7 @@ export const useChatStreamMessage = () => {
const iChatMessage: IBusterChatMessage = create(currentChatMessage, (draft) => { const iChatMessage: IBusterChatMessage = create(currentChatMessage, (draft) => {
Object.assign(draft || {}, chatMessage); Object.assign(draft || {}, chatMessage);
})!; })!;
chatRefMessages.current[chatMessage.id] = iChatMessage;
onUpdateChatMessage(iChatMessage!); onUpdateChatMessage(iChatMessage!);
@ -65,7 +66,7 @@ export const useChatStreamMessage = () => {
chatRef.current[iChat.id] = iChat; chatRef.current[iChat.id] = iChat;
normalizeChatMessage(iChatMessages); normalizeChatMessage(iChatMessages);
onUpdateChat(iChat); onUpdateChat(iChat);
removeAutoThought({ messageId: iChat.message_ids[iChat.message_ids.length - 1] }); removeBlackBoxMessage({ messageId: iChat.message_ids[iChat.message_ids.length - 1] });
}); });
const stopChatCallback = useMemoizedFn((chatId: string) => { const stopChatCallback = useMemoizedFn((chatId: string) => {
@ -127,12 +128,13 @@ export const useChatStreamMessage = () => {
const _generatingReasoningMessageCallback = useMemoizedFn( const _generatingReasoningMessageCallback = useMemoizedFn(
(_: null, d: ChatEvent_GeneratingReasoningMessage) => { (_: null, d: ChatEvent_GeneratingReasoningMessage) => {
const { message_id, reasoning } = d; const { message_id, reasoning } = d;
const currentMessage = chatRefMessages.current[message_id]; const updatedMessage = updateReasoningMessage(
const updatedMessage = updateReasoningMessage(message_id, currentMessage, reasoning); message_id,
chatRefMessages.current[message_id],
reasoning
);
checkAutoThought(updatedMessage, d); checkBlackBoxMessage(updatedMessage, d); //we only trigger black box message if the reasoning message is completed
//TRIGGER
onUpdateChatMessageTransition({ onUpdateChatMessageTransition({
id: message_id, id: message_id,

View File

@ -23,7 +23,7 @@ export const BlackBoxMessage: React.FC<{ messageId: string }> = React.memo(({ me
</BarContainer>; </BarContainer>;
} }
return null; return <span>no black box message?</span>;
}); });
BlackBoxMessage.displayName = 'BlackBoxMessage'; BlackBoxMessage.displayName = 'BlackBoxMessage';