Skip to content

Commit

Permalink
add auto chat naming (#204)
Browse files Browse the repository at this point in the history
* add auto chat naming

* feat: Rename old untitled chats

Co-authored-by: Ben Lopata <[email protected]>

* chore: change setCanSend Order

* chore: Prettify

---------

Co-authored-by: Vineeth Voruganti <[email protected]>
Co-authored-by: Ben Lopata <[email protected]>
  • Loading branch information
3 people authored Jan 14, 2025
1 parent 623deb2 commit a1bb41d
Show file tree
Hide file tree
Showing 10 changed files with 255 additions and 135 deletions.
275 changes: 161 additions & 114 deletions www/app/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@ import { Reaction } from '@/components/messagebox';
import { FiMenu } from 'react-icons/fi';
import Link from 'next/link';
import { getFreeMessageCount, useFreeTrial } from '@/utils/supabase/actions';
import { getConversations, createConversation } from './actions/conversations';
import {
getConversations,
createConversation,
updateConversation,
} from './actions/conversations';
import { getMessages, addOrRemoveReaction } from './actions/messages';
import { type Message } from '@/utils/types';
import { Conversation, Message } from '@/utils/types';
import { localStorageProvider } from '@/utils/swrCache';

import useAutoScroll from '@/hooks/autoscroll';
Expand All @@ -28,9 +32,6 @@ const Thoughts = dynamic(() => import('@/components/thoughts'), {
ssr: false,
});

const MessageBox = dynamic(() => import('@/components/messagebox'), {
ssr: false,
});
const Sidebar = dynamic(() => import('@/components/sidebar'), {
ssr: false,
});
Expand Down Expand Up @@ -88,13 +89,13 @@ async function fetchStream(
interface ChatProps {
initialUserId: string;
initialEmail: string | undefined;
initialConversations: any[];
initialConversations: Conversation[];
initialChatAccess: {
isSubscribed: boolean;
freeMessages: number;
canChat: boolean;
};
initialMessages: any[];
initialMessages: Message[];
initialConversationId: string | null | undefined;
}

Expand All @@ -111,9 +112,7 @@ export default function Chat({
initialChatAccess,
}: ChatProps) {
const [userId] = useState(initialUserId);
const [isSubscribed, setIsSubscribed] = useState(
initialChatAccess.isSubscribed
);
const [isSubscribed] = useState(initialChatAccess.isSubscribed);
const [freeMessages, setFreeMessages] = useState(
initialChatAccess.freeMessages
);
Expand All @@ -134,7 +133,7 @@ export default function Chat({
const posthog = usePostHog();
const input = useRef<ElementRef<'textarea'>>(null);
const messageContainerRef = useRef<ElementRef<'section'>>(null);
const [, scrollToBottom] = useAutoScroll(messageContainerRef);
useAutoScroll(messageContainerRef);

const messageListRef = useRef<MessageListRef>(null);
const firstChat = useMemo(() => {
Expand All @@ -157,7 +156,7 @@ export default function Chat({
The more we chat, the more I learn about you as a person. That helps me adapt to your interests and needs.
What\'s on your mind? Let\'s dive in. 🌱`,
What's on your mind? Let's dive in. 🌱`,
isUser: false,
id: '',
metadata: {},
Expand Down Expand Up @@ -245,7 +244,7 @@ What\'s on your mind? Let\'s dive in. 🌱`,
revalidateOnFocus: false,
revalidateOnReconnect: false,
dedupingInterval: 60000,
onSuccess: (data) => {
onSuccess: () => {
if (conversationId?.startsWith('temp-')) {
mutateMessages([], false);
}
Expand Down Expand Up @@ -282,101 +281,84 @@ What\'s on your mind? Let\'s dive in. 🌱`,
}
};

async function chat(message?: string) {
const rawMessage = message || input.current?.value;
if (!userId || !rawMessage) return;

// Process message to have double newline for markdown
const messageToSend = rawMessage.replace(/\n/g, '\n\n');

if (input.current) input.current.value = '';

setCanSend(false);

const newMessages = [
...messages!,
{
content: messageToSend,
isUser: true,
id: '',
metadata: {},
},
{
content: '',
isUser: false,
id: '',
metadata: {},
},
];
await mutateMessages(newMessages, { revalidate: false });
messageListRef.current?.scrollToBottom();

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

let thoughtReader: ReadableStreamDefaultReader | null = null;
let responseReader: ReadableStreamDefaultReader | null = null;

try {
// Get thought stream
const thoughtStream = await fetchStream(
'thought',
messageToSend,
conversationId!
);
async function processThought(messageToSend: string, conversationId: string) {
// Get thought stream
const thoughtStream = await fetchStream(
'thought',
messageToSend,
conversationId
);

if (!thoughtStream) {
throw new Error('Failed to get thought stream');
}
if (!thoughtStream) {
throw new Error('Failed to get thought stream');
}

thoughtReader = thoughtStream.getReader();
let thoughtText = '';
setThought('');
const thoughtReader = thoughtStream.getReader();
let thoughtText = '';
setThought('');

// Process thought stream
while (true) {
const { done, value } = await thoughtReader.read();
if (done) break;
// Process thought stream
while (true) {
const { done, value } = await thoughtReader.read();
if (done) break;

thoughtText += new TextDecoder().decode(value);
setThought(thoughtText);
}
thoughtText += new TextDecoder().decode(value);
setThought(thoughtText);
}

// Cleanup thought stream
thoughtReader.releaseLock();
thoughtReader = null;
// Cleanup thought stream
thoughtReader.releaseLock();

const honchoResponse = await fetchStream(
'honcho',
messageToSend,
conversationId!,
thoughtText
);
return thoughtText;
}

const honchoContent = (await new Response(
honchoResponse
).json()) as HonchoResponse;
async function processHoncho(
messageToSend: string,
conversationId: string,
thoughtText: string
) {
// Get honcho response
const honchoResponse = await fetchStream(
'honcho',
messageToSend,
conversationId,
thoughtText
);

const pureThought = thoughtText;
const honchoContent = (await new Response(
honchoResponse
).json()) as HonchoResponse;

thoughtText +=
'\n\nHoncho Dialectic Response:\n\n' + honchoContent.content;
setThought(thoughtText);
const updatedThought =
thoughtText +
'\n\nHoncho Dialectic Response:\n\n' +
honchoContent.content;
setThought(updatedThought);

await new Promise((resolve) => setTimeout(resolve, 2000));
return honchoContent;
}

// Get response stream using the thought and dialectic response
const responseStream = await fetchStream(
'response',
messageToSend,
conversationId!,
pureThought,
honchoContent.content
);
if (!responseStream) throw new Error('Failed to get response stream');
async function processResponse(
messageToSend: string,
conversationId: string,
thoughtText: string,
honchoContent: string,
newMessages: Message[],
isSubscribed: boolean
) {
const responseStream = await fetchStream(
'response',
messageToSend,
conversationId,
thoughtText,
honchoContent
);
if (!responseStream) throw new Error('Failed to get response stream');

responseReader = responseStream.getReader();
let currentModelOutput = '';
const responseReader = responseStream.getReader();
let currentModelOutput = '';

try {
// Process response stream
while (true) {
const { done, value } = await responseReader.read();
Expand Down Expand Up @@ -408,34 +390,99 @@ What\'s on your mind? Let\'s dive in. 🌱`,

messageListRef.current?.scrollToBottom();
}

} finally {
responseReader.releaseLock();
responseReader = null;
}
}

async function processSummary(messageToSend: string, conversationId: string) {
const summaryResponse = await fetch('/api/chat/summary', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
message: messageToSend,
}),
});

if (summaryResponse.ok) {
const { summary } = await summaryResponse.json();
await updateConversation(conversationId, summary);
await mutateConversations();
}
}

async function chat(message?: string) {
const rawMessage = message || input.current?.value;
if (!userId || !rawMessage) return;

// Process message to have double newline for markdown
const messageToSend = rawMessage.replace(/\n/g, '\n\n');

if (input.current) input.current.value = '';

setCanSend(false);

const newMessages = [
...messages!,
{
content: messageToSend,
isUser: true,
id: '',
metadata: {},
},
{
content: '',
isUser: false,
id: '',
metadata: {},
},
];
await mutateMessages(newMessages, { revalidate: false });
messageListRef.current?.scrollToBottom();

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

try {
// Process thought and summary in parallel if this is the first message
const isFirstChat = messages?.length === 0;
const isUntitledConversation =
conversations?.find((c) => c.conversationId === conversationId)
?.name === 'Untitled';
const shouldGenerateSummary = isFirstChat || isUntitledConversation;
const [thoughtText] = await Promise.all([
processThought(messageToSend, conversationId!),
...(shouldGenerateSummary
? [processSummary(messageToSend, conversationId!)]
: []),
]);

// Process honcho response
const honchoContent = await processHoncho(
messageToSend,
conversationId!,
thoughtText
);

// Process response
await processResponse(
messageToSend,
conversationId!,
thoughtText,
honchoContent.content,
newMessages,
isSubscribed
);

await mutateMessages();
messageListRef.current?.scrollToBottom();
setCanSend(true);
} catch (error) {
console.error('Chat error:', error);
setCanSend(true);
await mutateMessages();
messageListRef.current?.scrollToBottom();
} finally {
// Cleanup
if (thoughtReader) {
try {
thoughtReader.releaseLock();
} catch (e) {
console.error('Error releasing thought reader:', e);
}
}
if (responseReader) {
try {
responseReader.releaseLock();
} catch (e) {
console.error('Error releasing response reader:', e);
}
}
setCanSend(true);
}
}

Expand Down
6 changes: 1 addition & 5 deletions www/app/actions/conversations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,10 @@
import { createClient } from '@/utils/supabase/server';
import { honcho, getHonchoApp, getHonchoUser } from '@/utils/honcho';
import * as Sentry from '@sentry/nextjs';
import { Conversation } from '@/utils/types';

// TODO add proper authorization check

type Conversation = {
conversationId: string;
name: string;
};

export async function getConversations() {
return Sentry.startSpan(
{ name: 'server-action.getConversations', op: 'server.action' },
Expand Down
Loading

0 comments on commit a1bb41d

Please sign in to comment.