Skip to content

Commit

Permalink
fix (thoughts): Get Dialectic Query and Add Backwards compatability f…
Browse files Browse the repository at this point in the history
…or old message storage format (#196)
  • Loading branch information
VVoruganti authored Dec 27, 2024
1 parent 6120bb8 commit 793f60d
Show file tree
Hide file tree
Showing 4 changed files with 251 additions and 113 deletions.
62 changes: 19 additions & 43 deletions www/app/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import { type Message } from '@/utils/types';
import { localStorageProvider } from '@/utils/swrCache';

import useAutoScroll from '@/hooks/autoscroll';
import MessageList from '@/components/MessageList';
import { MessageListRef } from '@/components/MessageList';

const Thoughts = dynamic(() => import('@/components/thoughts'), {
ssr: false,
Expand Down Expand Up @@ -130,6 +132,7 @@ export default function Chat({
const messageContainerRef = useRef<ElementRef<'section'>>(null);
const [, scrollToBottom] = useAutoScroll(messageContainerRef);

const messageListRef = useRef<MessageListRef>(null);
const firstChat = useMemo(() => {
return !initialConversations?.length ||
(initialConversations.length === 1 && !initialMessages?.length) ||
Expand Down Expand Up @@ -297,7 +300,7 @@ What\'s on your mind? Let\'s dive in. 🌱`,
},
];
await mutateMessages(newMessages, { revalidate: false });
scrollToBottom();
messageListRef.current?.scrollToBottom();

await new Promise((resolve) => setTimeout(resolve, 1000));

Expand Down Expand Up @@ -394,20 +397,20 @@ What\'s on your mind? Let\'s dive in. 🌱`,
{ revalidate: false }
);

scrollToBottom();
messageListRef.current?.scrollToBottom();
}

responseReader.releaseLock();
responseReader = null;

await mutateMessages();
scrollToBottom();
messageListRef.current?.scrollToBottom();
setCanSend(true);
} catch (error) {
console.error('Chat error:', error);
setCanSend(true);
await mutateMessages();
scrollToBottom();
messageListRef.current?.scrollToBottom();
} finally {
// Cleanup
if (thoughtReader) {
Expand Down Expand Up @@ -478,45 +481,18 @@ What\'s on your mind? Let\'s dive in. 🌱`,
</section>
)}
<div className="flex flex-col flex-grow overflow-hidden bg-secondary">
<section
className="flex-grow overflow-y-auto px-4 lg:px-5 dark:text-white"
ref={messageContainerRef}
>
{messages ? (
[defaultMessage, ...messages].map((message, i) => (
<MessageBox
key={message.id || i}
isUser={message.isUser}
userId={userId}
message={message}
loading={messagesLoading}
conversationId={conversationId}
setThought={setThought}
isThoughtOpen={openThoughtMessageId === message.id}
setIsThoughtsOpen={(isOpen) =>
setIsThoughtsOpen(isOpen, message.id)
}
onReactionAdded={handleReactionAdded}
/>
))
) : (
<MessageBox
isUser={false}
message={{
content: '',
id: '',
isUser: false,
metadata: { reaction: null },
}}
loading={true}
setThought={setThought}
setIsThoughtsOpen={setIsThoughtsOpen}
onReactionAdded={handleReactionAdded}
userId={userId}
conversationId={conversationId}
/>
)}
</section>
<MessageList
ref={messageListRef}
messages={messages}
defaultMessage={defaultMessage}
userId={userId}
conversationId={conversationId}
messagesLoading={messagesLoading}
handleReactionAdded={handleReactionAdded}
setThoughtParent={setThought}
openThoughtMessageId={openThoughtMessageId}
setIsThoughtsOpen={setIsThoughtsOpen}
/>
<div className="p-3 pb-0 lg:p-5 lg:pb-0">
<form
id="send"
Expand Down
49 changes: 37 additions & 12 deletions www/app/actions/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,43 @@ export async function getThought(conversationId: string, messageId: string) {
const honchoUser = await getHonchoUser(user.id);

try {
const thoughts = await honcho.apps.users.sessions.metamessages.list(
honchoApp.id,
honchoUser.id,
conversationId,
{
message_id: messageId,
metamessage_type: 'thought',
filter: { type: 'assistant' },
}
);

return thoughts.items[0]?.content || null;
const [thoughts, dialectic] = await Promise.all([
honcho.apps.users.sessions.metamessages.list(
honchoApp.id,
honchoUser.id,
conversationId,
{
message_id: messageId,
metamessage_type: 'thought',
filter: { type: 'assistant' },
}
),
honcho.apps.users.sessions.metamessages.list(
honchoApp.id,
honchoUser.id,
conversationId,
{
message_id: messageId,
metamessage_type: 'honcho',
filter: { type: 'assistant' },
}
)
]);

const thoughtText = thoughts.items[0]?.content;
const dialecticText = dialectic.items[0]?.content;

if (!thoughtText) {
return null;
}

let completeThought = thoughtText;

if (dialecticText) {
completeThought += '\n\nDialectic Response:\n\n' + dialecticText;
}

return completeThought;
} catch (error) {
console.error('Error in getThought:', error);
throw new Error('Internal server error');
Expand Down
139 changes: 139 additions & 0 deletions www/components/MessageList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
'use client';
import { useRef, useState, forwardRef, useImperativeHandle } from 'react';
import { Message } from '@/utils/types';
import MessageBox from '@/components/messagebox';
import { getThought } from '@/app/actions/messages';
import useAutoScroll from '@/hooks/autoscroll';

interface MessageListProps {
messages: Message[] | undefined;
defaultMessage: Message;
userId: string;
conversationId?: string;
messagesLoading: boolean;
handleReactionAdded: (messageId: string, reaction: any) => Promise<void>;
setThoughtParent: (thought: string) => void;
setIsThoughtsOpen: (isOpen: boolean, messageId?: string | null | undefined) => void;
openThoughtMessageId: string | null;
}

export interface MessageListRef {
scrollToBottom: () => void;
}

const MessageList = forwardRef<MessageListRef, MessageListProps>(({
messages,
defaultMessage,
userId,
conversationId,
messagesLoading,
handleReactionAdded,
setThoughtParent,
setIsThoughtsOpen,
openThoughtMessageId,
}, ref) => {
const [isThoughtLoading, setIsThoughtLoading] = useState<boolean>(false);
const [thoughtError, setThoughtError] = useState<{ messageId: string, error: string } | null>(null);

const messageContainerRef = useRef<HTMLElement>(null);
const [, scrollToBottom] = useAutoScroll(messageContainerRef);

// Expose scrollToBottom method to parent
useImperativeHandle(ref, () => ({
scrollToBottom: () => {
scrollToBottom();
},
}));

const handleGetThought = async (messageId: string) => {
if (!conversationId || !userId) return;

// Find the last user message before this AI message
const allMessages = [defaultMessage, ...(messages || [])];
const messageIndex = allMessages.findIndex(msg => msg.id === messageId);

// Look backwards for the last user message
let lastUserMessageId = null;
for (let i = messageIndex; i >= 0; i--) {
if (allMessages[i].isUser) {
lastUserMessageId = allMessages[i].id;
break;
}
}

try {
// Try with last user message first
if (lastUserMessageId) {
const thought = await getThought(conversationId, lastUserMessageId);
if (thought) {
setThoughtParent(thought);
setIsThoughtsOpen(true, messageId);
return;
}
}

// If that didn't work, try with current AI message
const thought = await getThought(conversationId, messageId);
if (thought) {
setThoughtParent(thought);
setIsThoughtsOpen(true, messageId);
return;
}

// If neither worked
setThoughtError({ messageId, error: 'No thought data available.' });
console.log(messageId, 'No thought data available.');
} catch (error) {
console.error('Failed to fetch thought:', error);
setThoughtError({ messageId, error: 'Failed to fetch thought.' });
} finally {
setIsThoughtLoading(false);
}
};

return (
<section
className="flex-grow overflow-y-auto px-4 lg:px-5 dark:text-white"
ref={messageContainerRef}
>
{messages ? (
[defaultMessage, ...messages].map((message, i) => (
<MessageBox
key={message.id || i}
isUser={message.isUser}
userId={userId}
message={message}
loading={messagesLoading}
conversationId={conversationId}
isThoughtOpen={openThoughtMessageId === message.id}
setIsThoughtsOpen={(isOpen) => setIsThoughtsOpen(isOpen, message.id)}
onReactionAdded={handleReactionAdded}
onGetThought={handleGetThought}
isThoughtLoading={isThoughtLoading && openThoughtMessageId === message.id}
thoughtError={thoughtError?.messageId === message.id ? thoughtError.error : null}
/>
))
) : (
<MessageBox
isUser={false}
message={{
content: '',
id: '',
isUser: false,
metadata: { reaction: null },
}}
loading={true}
setIsThoughtsOpen={setIsThoughtsOpen}
onReactionAdded={handleReactionAdded}
onGetThought={handleGetThought}
userId={userId}
conversationId={conversationId}
/>
)}
</section>
);
});

MessageList.displayName = 'MessageList';

export default MessageList;
Loading

0 comments on commit 793f60d

Please sign in to comment.