diff --git a/www/app/api/chat/honcho/route.ts b/www/app/api/chat/honcho/route.ts index 1c6843b..ce7331f 100644 --- a/www/app/api/chat/honcho/route.ts +++ b/www/app/api/chat/honcho/route.ts @@ -6,8 +6,18 @@ export const runtime = 'nodejs'; export const maxDuration = 100; export const dynamic = 'force-dynamic'; // always run dynamically + +function parseHonchoContent(str: string) { + try { + const match = str.match(/(.*?)<\/honcho>/s); + return match ? match[1].trim() : str; + } catch (error) { + return str; + } +} + export async function POST(req: NextRequest) { - const { message, conversationId } = await req.json(); + const { message, thought, conversationId } = await req.json(); const userData = await getUserData(); @@ -17,12 +27,19 @@ export async function POST(req: NextRequest) { const { appId, userId } = userData; + + + const query = `Given the following user message: ${message} I had the following message: ${parseHonchoContent(thought)}` + const dialecticQuery = await honcho.apps.users.sessions.chat( appId, userId, conversationId, - { queries: message } + { queries: query } ); + console.log('dialecticQuery:', query); + console.log('dialecticQuery Response:', dialecticQuery); + return NextResponse.json({ content: dialecticQuery.content }); } diff --git a/www/app/api/chat/response/route.ts b/www/app/api/chat/response/route.ts index e9a8756..550a384 100644 --- a/www/app/api/chat/response/route.ts +++ b/www/app/api/chat/response/route.ts @@ -1,4 +1,10 @@ -import { createStream, getUserData, Message } from '@/utils/ai'; +import { + assistant, + createStream, + getUserData, + Message, + user, +} from '@/utils/ai'; import { honcho } from '@/utils/honcho'; import { responsePrompt } from '@/utils/prompts/response'; import { NextRequest, NextResponse } from 'next/server'; @@ -8,7 +14,9 @@ export const maxDuration = 100; export const dynamic = 'force-dynamic'; // always run dynamically export async function POST(req: NextRequest) { - const { message, conversationId, honchoThought } = await req.json(); + const { message, conversationId, thought, honchoThought } = await req.json(); + + console.log('honchoThought', honchoThought); const userData = await getUserData(); if (!userData) { @@ -17,44 +25,110 @@ export async function POST(req: NextRequest) { const { appId, userId } = userData; - const history: Message[] = []; - const responseIter = await honcho.apps.users.sessions.metamessages.list( + const responseIter = await honcho.apps.users.sessions.messages.list( + appId, + userId, + conversationId, + {} + ); + + const responseHistory = Array.from(responseIter.items); + + const honchoIter = await honcho.apps.users.sessions.metamessages.list( appId, userId, conversationId, { - metamessage_type: 'response', + metamessage_type: 'honcho', } ); - for await (const metamessage of responseIter) { - const associatedMessage = await honcho.apps.users.sessions.messages.get( - appId, - userId, - conversationId, - metamessage.message_id - ); + const honchoHistory = Array.from(honchoIter.items); - history.push({ role: 'user', content: metamessage.content }); - history.push({ role: 'assistant', content: associatedMessage.content }); - } + console.log('honchoHistory', honchoHistory); + console.log('responseHistory', responseHistory); + + const getHonchoMessage = (id: string) => + honchoHistory.find((m) => m.message_id === id)?.content || + 'No Honcho Message'; + + const history = responseHistory.map((message, i) => { + if (message.is_user) { + return user`${getHonchoMessage(message.id)} + ${message.content}`; + } else { + return assistant`${message.content}`; + } + }); - const messages = [ - ...responsePrompt, - ...history, + const finalMessage = user`${honchoThought} + ${message}`; + + const prompt = [...responsePrompt, ...history, finalMessage]; + + console.log('responsePrompt', prompt); + + // Create logs directory if it doesn't exist + + const stream = await createStream( + prompt, { - role: 'user', - content: `${honchoThought}\n${message}`, + sessionId: conversationId, + userId, + type: 'response', }, - ] as Message[]; + async (response) => { + const newUserMessage = await honcho.apps.users.sessions.messages.create( + appId, + userId, + conversationId, + { + is_user: true, + content: message, + } + ); - const stream = await createStream('response', messages, { - appId, - userId, - sessionId: conversationId, - userInput: message, - honchoContent: honchoThought, - }); + // Execute all requests in parallel + await Promise.all([ + // Save the thought metamessage + honcho.apps.users.sessions.metamessages.create( + appId, + userId, + conversationId, + { + message_id: newUserMessage.id, + metamessage_type: 'thought', + content: thought || '', + metadata: { type: 'assistant' }, + } + ), + + // Save honcho metamessage + honcho.apps.users.sessions.metamessages.create( + appId, + userId, + conversationId, + { + message_id: newUserMessage.id, + metamessage_type: 'honcho', + content: honchoThought || '', + metadata: { type: 'assistant' }, + } + ), + + // Save assistant message + honcho.apps.users.sessions.messages.create( + appId, + userId, + conversationId, + { + is_user: false, + content: response.text, + } + ), + ]); + } + ); if (!stream) { throw new Error('Failed to get stream'); diff --git a/www/app/api/chat/thought/route.ts b/www/app/api/chat/thought/route.ts index 6e40636..de6d821 100644 --- a/www/app/api/chat/thought/route.ts +++ b/www/app/api/chat/thought/route.ts @@ -1,8 +1,6 @@ import { createStream, getUserData, - HistoryWithoutResponse, - Message, user, assistant, // parsePrompt, @@ -15,14 +13,13 @@ export const runtime = 'nodejs'; export const maxDuration = 100; export const dynamic = 'force-dynamic'; // always run dynamically -const CONTEXT_LIMIT = 10; - -interface Metadata { - summary?: string; +interface ThoughtCallProps { + message: string; + conversationId: string; } export async function POST(req: NextRequest) { - const { message, conversationId } = await req.json(); + const { message, conversationId } = (await req.json()) as ThoughtCallProps; const userData = await getUserData(); if (!userData) { @@ -30,61 +27,75 @@ export async function POST(req: NextRequest) { } const { appId, userId } = userData; + const messageIter = await honcho.apps.users.sessions.messages.list( + appId, + userId, + conversationId, + {} + ); + + const messageHistory = Array.from(messageIter.items); + const thoughtIter = await honcho.apps.users.sessions.metamessages.list( appId, userId, conversationId, { metamessage_type: 'thought', - filter: { type: 'user' }, } ); - const thoughtHistory = thoughtIter.items.map( - (metamessage) => user`${metamessage.content}` - ); + const thoughtHistory = Array.from(thoughtIter.items); - const recentResponseMeta = await honcho.apps.users.sessions.metamessages.list( + const honchoIter = await honcho.apps.users.sessions.metamessages.list( appId, userId, conversationId, { - metamessage_type: 'response', - filter: { type: 'user' }, - reverse: true, - size: 1, + metamessage_type: 'honcho', } ); - const [recentResponse] = recentResponseMeta.items; - const content = recentResponse?.content ?? ''; - - const honchoResponse = - content.match(/([^]*?)<\/honcho>/)?.[1]?.trim() ?? 'None'; - const bloomResponse = - content.match(/([^]*?)<\/tutor>/)?.[1]?.trim() ?? 'None'; + const honchoHistory = Array.from(honchoIter.items); + + const history = messageHistory.map((message, i) => { + if (message.is_user) { + if (i == 0) { + return user`${message.content}`; + } + const lastUserMessage = messageHistory[i - 2]; + const honchoResponse = honchoHistory.find( + (h) => h.message_id === lastUserMessage.id + ); + const tutorResponse = messageHistory[i - 1]; + + return user`${honchoResponse?.content || 'None'} + ${tutorResponse?.content || 'None'} + ${message.content}`; + } else { + const lastUserMessage = messageHistory[i - 1]; + const thoughtResponse = thoughtHistory.find( + (t) => t.message_id === lastUserMessage.id + ); + return assistant`${thoughtResponse?.content || 'None'}`; + } + }); - const prompt: Message[] = [ - ...thoughtPrompt, - ...thoughtHistory, - { - role: 'user', - content: `${honchoResponse}\n${bloomResponse}\n${message}`, - }, - ]; + const finalMessage = user`${honchoHistory[honchoHistory.length - 1]?.content || 'None'} + ${messageHistory[messageHistory.length - 1]?.content || 'None'} + ${message}`; - const honchoPayload: HistoryWithoutResponse = { - appId, - userId, - sessionId: conversationId, - userInput: message, - }; + const prompt = [...thoughtPrompt, ...history, finalMessage]; console.log('Messages:\n'); console.log(prompt); console.log('\n\n\n'); - const stream = await createStream('thought', prompt, honchoPayload); + const stream = await createStream(prompt, { + sessionId: conversationId, + userId, + type: 'thought', + }); if (!stream) { throw new Error('Failed to get stream'); diff --git a/www/utils/ai.ts b/www/utils/ai.ts index 5fd5a7d..2092896 100644 --- a/www/utils/ai.ts +++ b/www/utils/ai.ts @@ -32,95 +32,95 @@ const openrouter = createOpenRouter({ }, }); -export interface HistoryWithoutResponse { - appId: string; - userId: string; - sessionId: string; - userInput: string; - thought?: string; - honchoContent?: string; -} - -type History = HistoryWithoutResponse & { - aiResponse: string; -}; - -async function saveHistory({ - appId, - userId, - sessionId, - userInput, - thought, - honchoContent, - aiResponse, -}: History) { - try { - // Create user message - const newUserMessage = await honcho.apps.users.sessions.messages.create( - appId, - userId, - sessionId, - { - is_user: true, - content: userInput, - } - ); - - // Save thought metamessage for user message - const thoughtMetamessage = `${honchoContent}\n${aiResponse}\n${userInput}`; - await honcho.apps.users.sessions.metamessages.create( - appId, - userId, - sessionId, - { - message_id: newUserMessage.id, - metamessage_type: 'thought', - content: thoughtMetamessage, - metadata: { type: 'user' }, - } - ); - - // Create AI message - const newAiMessage = await honcho.apps.users.sessions.messages.create( - appId, - userId, - sessionId, - { - is_user: false, - content: aiResponse, - } - ); - - // Save thought metamessage for AI message - await honcho.apps.users.sessions.metamessages.create( - appId, - userId, - sessionId, - { - content: thought || '', - message_id: newAiMessage.id, - metamessage_type: 'thought', - metadata: { type: 'assistant' }, - } - ); - - // Save response metamessage - const responseMetamessage = `${honchoContent}\n${userInput}`; - await honcho.apps.users.sessions.metamessages.create( - appId, - userId, - sessionId, - { - message_id: newAiMessage.id, - metamessage_type: 'response', - content: responseMetamessage, - } - ); - } catch (error) { - Sentry.captureException(error); - throw error; // Re-throw to be handled by caller - } -} +// export interface HistoryWithoutResponse { +// appId: string; +// userId: string; +// sessionId: string; +// userInput: string; +// thought?: string; +// honchoContent?: string; +// } + +// type History = HistoryWithoutResponse & { +// aiResponse: string; +// }; + +// async function saveHistory({ +// appId, +// userId, +// sessionId, +// userInput, +// thought, +// honchoContent, +// aiResponse, +// }: History) { +// try { +// // Create user message +// const newUserMessage = await honcho.apps.users.sessions.messages.create( +// appId, +// userId, +// sessionId, +// { +// is_user: true, +// content: userInput, +// } +// ); + +// // Save thought metamessage for user message +// const thoughtMetamessage = `${honchoContent}\n${aiResponse}\n${userInput}`; +// await honcho.apps.users.sessions.metamessages.create( +// appId, +// userId, +// sessionId, +// { +// message_id: newUserMessage.id, +// metamessage_type: 'thought', +// content: thoughtMetamessage, +// metadata: { type: 'user' }, +// } +// ); + +// // Create AI message +// const newAiMessage = await honcho.apps.users.sessions.messages.create( +// appId, +// userId, +// sessionId, +// { +// is_user: false, +// content: aiResponse, +// } +// ); + +// // Save thought metamessage for AI message +// await honcho.apps.users.sessions.metamessages.create( +// appId, +// userId, +// sessionId, +// { +// content: thought || '', +// message_id: newAiMessage.id, +// metamessage_type: 'thought', +// metadata: { type: 'assistant' }, +// } +// ); + +// // Save response metamessage +// const responseMetamessage = `${honchoContent}\n${userInput}`; +// await honcho.apps.users.sessions.metamessages.create( +// appId, +// userId, +// sessionId, +// { +// message_id: newAiMessage.id, +// metamessage_type: 'response', +// content: responseMetamessage, +// } +// ); +// } catch (error) { +// Sentry.captureException(error); +// throw error; // Re-throw to be handled by caller +// } +// } export async function getUserData() { const supabase = createClient(); @@ -165,31 +165,28 @@ export const assistant = ( // role: 'system', // content: d(strings, ...values), // }); - export async function createStream( - type: string, messages: Message[], - payload: HistoryWithoutResponse + metadata: { + sessionId: string; + userId: string; + type: string; + }, + onFinish?: (response: { text: string }) => Promise ) { try { const result = streamText({ model: openrouter(MODEL), messages, - onFinish: async (response) => { - if (type === 'response') { - const aiResponse = response.text; - const finalPayload = { ...payload, aiResponse }; - await saveHistory(finalPayload); - } - }, + ...(onFinish && { onFinish }), experimental_telemetry: { isEnabled: true, metadata: { - sessionId: payload.sessionId, - userId: payload.userId, + sessionId: metadata.sessionId, + userId: metadata.userId, release: SENTRY_RELEASE, environment: SENTRY_ENVIRONMENT, - tags: [type], + tags: [metadata.type], }, }, });