upgrade `ai` sdk to v5

master
Avraham Sakal 2 months ago
parent 3aa252e248
commit 341cf5bff1

@ -0,0 +1,19 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Vite: debug full stack",
"type": "node-terminal",
"request": "launch",
"command": "pnpm dev",
"serverReadyAction": {
"pattern": "Local: http://localhost:3000/",
"uriFormat": "http://localhost:%s/",
"action": "debugWithChrome"
}
}
]
}

@ -1,7 +1,3 @@
+ Break-out functionality in `sendMessage` into separate procedures,
namespaced by domain (e.g. `messages`, `facts`, `factTriggers`).
+ Make a `withDbWrite` function to wrap the calls to these procedures, so
as to separate the generation of data from persisting it.
+ Somehow subscribe the UI to events from the `sendMessage` procedure, + Somehow subscribe the UI to events from the `sendMessage` procedure,
so the user knows what's going on. so the user knows what's going on.
+ Parallelize the generation of data, so that the UI doesn't freeze for + Parallelize the generation of data, so that the UI doesn't freeze for

@ -1,5 +1,6 @@
import { Low } from "lowdb"; import { Low } from "lowdb";
import { JSONFile } from "lowdb/node"; import { JSONFile } from "lowdb/node";
import type { CommittedMessage } from "../types";
export type Conversation = { export type Conversation = {
id: string; id: string;
@ -27,7 +28,7 @@ export type FactTrigger = {
type DB = { type DB = {
conversations: Array<Conversation>; conversations: Array<Conversation>;
messages: Array<{ messages: Array</*{
id: string; id: string;
conversationId: string; conversationId: string;
content: string; content: string;
@ -35,7 +36,7 @@ type DB = {
index: number; index: number;
createdAt: string; createdAt: string;
runningSummary?: string; runningSummary?: string;
}>; }*/ CommittedMessage>;
facts: Array<Fact>; facts: Array<Fact>;
factTriggers: Array<FactTrigger>; factTriggers: Array<FactTrigger>;
}; };

@ -28,7 +28,7 @@ export default function ChatPage() {
const pageContext = usePageContext(); const pageContext = usePageContext();
const conversationId = pageContext.routeParams.id; const conversationId = pageContext.routeParams.id;
const conversationTitle = useStore( const conversationTitle = useStore(
(state) => state.conversations.find((c) => c.id === conversationId)?.title, (state) => state.conversations.find((c) => c.id === conversationId)?.title
); );
const messages = useStore((state) => state.messages); const messages = useStore((state) => state.messages);
const message = useStore((state) => state.message); const message = useStore((state) => state.message);
@ -54,30 +54,33 @@ export default function ChatPage() {
const [editingFactContent, setEditingFactContent] = useState(""); const [editingFactContent, setEditingFactContent] = useState("");
// State for editing fact triggers // State for editing fact triggers
const [editingFactTriggerId, setEditingFactTriggerId] = useState<string | null>(null); const [editingFactTriggerId, setEditingFactTriggerId] = useState<
const [editingFactTriggerContent, setEditingFactTriggerContent] = useState(""); string | null
>(null);
const [editingFactTriggerContent, setEditingFactTriggerContent] =
useState("");
// Handle clicking outside to cancel editing // Handle clicking outside to cancel editing
useEffect(() => { useEffect(() => {
function handleClickOutside(event: MouseEvent) { function handleClickOutside(event: MouseEvent) {
if (editingFactId && event.target instanceof Element) { if (editingFactId && event.target instanceof Element) {
const editingElement = event.target.closest('.editing-fact'); const editingElement = event.target.closest(".editing-fact");
if (!editingElement) { if (!editingElement) {
setEditingFactId(null); setEditingFactId(null);
} }
} }
if (editingFactTriggerId && event.target instanceof Element) { if (editingFactTriggerId && event.target instanceof Element) {
const editingElement = event.target.closest('.editing-fact-trigger'); const editingElement = event.target.closest(".editing-fact-trigger");
if (!editingElement) { if (!editingElement) {
setEditingFactTriggerId(null); setEditingFactTriggerId(null);
} }
} }
} }
document.addEventListener('mousedown', handleClickOutside); document.addEventListener("mousedown", handleClickOutside);
return () => { return () => {
document.removeEventListener('mousedown', handleClickOutside); document.removeEventListener("mousedown", handleClickOutside);
}; };
}, [editingFactId, editingFactTriggerId]); }, [editingFactId, editingFactTriggerId]);
@ -123,9 +126,9 @@ export default function ChatPage() {
async function handleUpdateFact(factId: string, content: string) { async function handleUpdateFact(factId: string, content: string) {
// Update the local state first // Update the local state first
setFacts(facts.map(fact => setFacts(
fact.id === factId ? { ...fact, content } : fact facts.map((fact) => (fact.id === factId ? { ...fact, content } : fact))
)); );
// Then update the database // Then update the database
await trpc.chat.facts.update.mutate({ factId, content }); await trpc.chat.facts.update.mutate({ factId, content });
@ -136,11 +139,18 @@ export default function ChatPage() {
await trpc.chat.factTriggers.deleteOne.mutate({ factTriggerId }); await trpc.chat.factTriggers.deleteOne.mutate({ factTriggerId });
} }
async function handleUpdateFactTrigger(factTriggerId: string, content: string) { async function handleUpdateFactTrigger(
factTriggerId: string,
content: string
) {
// Update the local state first // Update the local state first
setFactTriggers(factTriggers.map(factTrigger => setFactTriggers(
factTrigger.id === factTriggerId ? { ...factTrigger, content } : factTrigger factTriggers.map((factTrigger) =>
)); factTrigger.id === factTriggerId
? { ...factTrigger, content }
: factTrigger
)
);
// Then update the database // Then update the database
await trpc.chat.factTriggers.update.mutate({ factTriggerId, content }); await trpc.chat.factTriggers.update.mutate({ factTriggerId, content });
@ -185,7 +195,10 @@ export default function ChatPage() {
e.preventDefault(); e.preventDefault();
const messagesWithNewUserMessage = [ const messagesWithNewUserMessage = [
...messages, ...messages,
{ role: "user" as const, content: message } as DraftMessage, {
role: "user" as const,
parts: [{ type: "text", text: message }],
} as DraftMessage,
]; ];
setMessages(messagesWithNewUserMessage); setMessages(messagesWithNewUserMessage);
setLoading(true); setLoading(true);
@ -201,7 +214,8 @@ export default function ChatPage() {
id: response.insertedUserMessage?.id, id: response.insertedUserMessage?.id,
conversationId, conversationId,
role: "user" as const, role: "user" as const,
content: message, // content: message,
parts: [{ type: "text", text: message }],
index: response.insertedUserMessage?.index, index: response.insertedUserMessage?.index,
runningSummary: undefined, runningSummary: undefined,
} as CommittedMessage, } as CommittedMessage,
@ -209,7 +223,9 @@ export default function ChatPage() {
id: response.insertedAssistantMessage?.id, id: response.insertedAssistantMessage?.id,
conversationId, conversationId,
role: "assistant" as const, role: "assistant" as const,
content: response.insertedAssistantMessage?.content, // content: response.insertedAssistantMessage?.content,
// parts: [{ type: "text", text: response.insertedAssistantMessage?.content }],
parts: response.insertedAssistantMessage?.parts,
index: response.insertedAssistantMessage?.index, index: response.insertedAssistantMessage?.index,
runningSummary: runningSummary:
response.insertedAssistantMessage?.runningSummary || response.insertedAssistantMessage?.runningSummary ||
@ -270,9 +286,7 @@ export default function ChatPage() {
> >
<IconCheck size={16} /> <IconCheck size={16} />
</ActionIcon> </ActionIcon>
<ActionIcon <ActionIcon onClick={() => setEditingFactId(null)}>
onClick={() => setEditingFactId(null)}
>
<IconX size={16} /> <IconX size={16} />
</ActionIcon> </ActionIcon>
</Group> </Group>
@ -310,11 +324,16 @@ export default function ChatPage() {
<Group wrap="nowrap" className="editing-fact-trigger"> <Group wrap="nowrap" className="editing-fact-trigger">
<Textarea <Textarea
value={editingFactTriggerContent} value={editingFactTriggerContent}
onChange={(e) => setEditingFactTriggerContent(e.target.value)} onChange={(e) =>
setEditingFactTriggerContent(e.target.value)
}
onKeyDown={(e) => { onKeyDown={(e) => {
if (e.key === "Enter") { if (e.key === "Enter") {
e.preventDefault(); e.preventDefault();
handleUpdateFactTrigger(factTrigger.id, editingFactTriggerContent); handleUpdateFactTrigger(
factTrigger.id,
editingFactTriggerContent
);
setEditingFactTriggerId(null); setEditingFactTriggerId(null);
} else if (e.key === "Escape") { } else if (e.key === "Escape") {
setEditingFactTriggerId(null); setEditingFactTriggerId(null);
@ -325,15 +344,16 @@ export default function ChatPage() {
/> />
<ActionIcon <ActionIcon
onClick={() => { onClick={() => {
handleUpdateFactTrigger(factTrigger.id, editingFactTriggerContent); handleUpdateFactTrigger(
factTrigger.id,
editingFactTriggerContent
);
setEditingFactTriggerId(null); setEditingFactTriggerId(null);
}} }}
> >
<IconCheck size={16} /> <IconCheck size={16} />
</ActionIcon> </ActionIcon>
<ActionIcon <ActionIcon onClick={() => setEditingFactTriggerId(null)}>
onClick={() => setEditingFactTriggerId(null)}
>
<IconX size={16} /> <IconX size={16} />
</ActionIcon> </ActionIcon>
</Group> </Group>
@ -396,7 +416,12 @@ function Messages({
p="md" p="md"
bdrs="md" bdrs="md"
> >
<Markdown>{message.content}</Markdown> <Markdown>
{message.parts
.filter((p) => p.type === "text")
.map((p) => p.text)
.join("\n")}
</Markdown>
</Box> </Box>
</HoverCard.Target> </HoverCard.Target>
<HoverCard.Dropdown> <HoverCard.Dropdown>

@ -34,16 +34,22 @@ export const conversations = router({
.mutation(async ({ input: { id } }) => { .mutation(async ({ input: { id } }) => {
db.data.conversations.splice( db.data.conversations.splice(
db.data.conversations.findIndex((c) => c.id === id), db.data.conversations.findIndex((c) => c.id === id),
1, 1
); );
const deletedMessageIds = db.data.messages const deletedMessageIds = db.data.messages
.filter((m) => m.conversationId === id) .filter((m) => m.conversationId === id)
.map((m) => m.id); .map((m) => m.id);
db.data.messages = db.data.messages.filter( db.data.messages = db.data.messages.filter(
(m) => m.conversationId !== id, (m) => m.conversationId !== id
); );
const deletedFactIds = db.data.facts
.filter((fact) => deletedMessageIds.includes(fact.sourceMessageId))
.map((fact) => fact.id);
db.data.facts = db.data.facts.filter( db.data.facts = db.data.facts.filter(
(fact) => !deletedMessageIds.includes(fact.sourceMessageId), (fact) => !deletedFactIds.includes(fact.id)
);
db.data.factTriggers = db.data.factTriggers.filter(
(factTrigger) => !deletedFactIds.includes(factTrigger.sourceFactId)
); );
db.write(); db.write();
return { ok: true }; return { ok: true };
@ -54,7 +60,7 @@ export const conversations = router({
x as { x as {
id: string; id: string;
title: string; title: string;
}, }
) )
.mutation(async ({ input: { id, title } }) => { .mutation(async ({ input: { id, title } }) => {
const conversation = await db.data.conversations.find((c) => c.id === id); const conversation = await db.data.conversations.find((c) => c.id === id);
@ -67,7 +73,7 @@ export const conversations = router({
.input((x) => x as { conversationId: string }) .input((x) => x as { conversationId: string })
.query(async ({ input: { conversationId } }) => { .query(async ({ input: { conversationId } }) => {
const rows = await db.data.messages.filter( const rows = await db.data.messages.filter(
(m) => m.conversationId === conversationId, (m) => m.conversationId === conversationId
); );
return rows as Array<CommittedMessage>; return rows as Array<CommittedMessage>;
}), }),

@ -37,7 +37,10 @@ ${previousRunningSummary}
${messagesSincePreviousRunningSummary.map( ${messagesSincePreviousRunningSummary.map(
(message) => (message) =>
`<${message.role}_message>${message.content}</${message.role}_message>`, `<${message.role}_message>${message.parts
.filter((p) => p.type === "text")
.map((p) => p.text)
.join("\n")}</${message.role}_message>`
)} )}
<assistant_response> <assistant_response>
${mainResponseContent} ${mainResponseContent}
@ -59,7 +62,7 @@ export const factTriggers = router({
.input((x) => x as { factId: string }) .input((x) => x as { factId: string })
.query(async ({ input: { factId } }) => { .query(async ({ input: { factId } }) => {
return db.data.factTriggers.filter( return db.data.factTriggers.filter(
(factTrigger) => factTrigger.sourceFactId === factId, (factTrigger) => factTrigger.sourceFactId === factId
); );
}), }),
deleteOne: publicProcedure deleteOne: publicProcedure
@ -67,13 +70,14 @@ export const factTriggers = router({
(x) => (x) =>
x as { x as {
factTriggerId: string; factTriggerId: string;
}, }
) )
.mutation(async ({ input: { factTriggerId } }) => { .mutation(async ({ input: { factTriggerId } }) => {
const deletedFactTriggerIndex = db.data.factTriggers.findIndex( const deletedFactTriggerIndex = db.data.factTriggers.findIndex(
(factTrigger) => factTrigger.id === factTriggerId, (factTrigger) => factTrigger.id === factTriggerId
); );
if (deletedFactTriggerIndex === -1) throw new Error("Fact trigger not found"); if (deletedFactTriggerIndex === -1)
throw new Error("Fact trigger not found");
db.data.factTriggers.splice(deletedFactTriggerIndex, 1); db.data.factTriggers.splice(deletedFactTriggerIndex, 1);
await db.write(); await db.write();
return { ok: true }; return { ok: true };
@ -84,11 +88,11 @@ export const factTriggers = router({
x as { x as {
factTriggerId: string; factTriggerId: string;
content: string; content: string;
}, }
) )
.mutation(async ({ input: { factTriggerId, content } }) => { .mutation(async ({ input: { factTriggerId, content } }) => {
const factTriggerIndex = db.data.factTriggers.findIndex( const factTriggerIndex = db.data.factTriggers.findIndex(
(factTrigger) => factTrigger.id === factTriggerId, (factTrigger) => factTrigger.id === factTriggerId
); );
if (factTriggerIndex === -1) throw new Error("Fact trigger not found"); if (factTriggerIndex === -1) throw new Error("Fact trigger not found");
db.data.factTriggers[factTriggerIndex].content = content; db.data.factTriggers[factTriggerIndex].content = content;
@ -103,7 +107,7 @@ export const factTriggers = router({
messagesSincePreviousRunningSummary: Array<DraftMessage>; messagesSincePreviousRunningSummary: Array<DraftMessage>;
mainResponseContent: string; mainResponseContent: string;
fact: Fact; fact: Fact;
}, }
) )
.mutation( .mutation(
async ({ async ({
@ -114,9 +118,7 @@ export const factTriggers = router({
fact, fact,
}, },
}) => { }) => {
const factTriggers = await generateObject<{ const factTriggers = await generateObject({
factTriggers: Array<string>;
}>({
model: openrouter("mistralai/mistral-nemo"), model: openrouter("mistralai/mistral-nemo"),
messages: [ messages: [
{ {
@ -134,7 +136,9 @@ export const factTriggers = router({
}), }),
}, },
], ],
schema: jsonSchema({ schema: jsonSchema<{
factTriggers: Array<string>;
}>({
type: "object", type: "object",
properties: { properties: {
factTriggers: { factTriggers: {
@ -149,7 +153,7 @@ export const factTriggers = router({
// tools: undefined, // tools: undefined,
}); });
return factTriggers; return factTriggers;
}, }
), ),
}); });

@ -31,7 +31,10 @@ Your task is to extract *new* facts that can be gleaned from the *new* messages
${messagesSincePreviousRunningSummary.map( ${messagesSincePreviousRunningSummary.map(
(message) => (message) =>
`<${message.role}_message>${message.content}</${message.role}_message>`, `<${message.role}_message>${message.parts
.filter((p) => p.type === "text")
.map((p) => p.text)
.join("\n")}</${message.role}_message>`
)} )}
`; `;
@ -42,7 +45,10 @@ const factsFromNewMessagesUserPrompt = ({
}) => }) =>
`${newMessages.map( `${newMessages.map(
(message) => (message) =>
`<${message.role}_message>${message.content}</${message.role}_message>`, `<${message.role}_message>${message.parts
.filter((p) => p.type === "text")
.map((p) => p.text)
.join("\n")}</${message.role}_message>`
)} )}
Extract new facts from these messages.`; Extract new facts from these messages.`;
@ -55,7 +61,7 @@ export const facts = router({
.filter((m) => m.conversationId === conversationId) .filter((m) => m.conversationId === conversationId)
.map((m) => m.id); .map((m) => m.id);
const rows = await db.data.facts.filter((f) => const rows = await db.data.facts.filter((f) =>
conversationMessageIds.includes(f.sourceMessageId), conversationMessageIds.includes(f.sourceMessageId)
); );
return rows as Array<Fact>; return rows as Array<Fact>;
}), }),
@ -64,11 +70,11 @@ export const facts = router({
(x) => (x) =>
x as { x as {
factId: string; factId: string;
}, }
) )
.mutation(async ({ input: { factId } }) => { .mutation(async ({ input: { factId } }) => {
const deletedFactId = db.data.facts.findIndex( const deletedFactId = db.data.facts.findIndex(
(fact) => fact.id === factId, (fact) => fact.id === factId
); );
if (deletedFactId === -1) throw new Error("Fact not found"); if (deletedFactId === -1) throw new Error("Fact not found");
db.data.facts.splice(deletedFactId, 1); db.data.facts.splice(deletedFactId, 1);
@ -81,12 +87,10 @@ export const facts = router({
x as { x as {
factId: string; factId: string;
content: string; content: string;
}, }
) )
.mutation(async ({ input: { factId, content } }) => { .mutation(async ({ input: { factId, content } }) => {
const factIndex = db.data.facts.findIndex( const factIndex = db.data.facts.findIndex((fact) => fact.id === factId);
(fact) => fact.id === factId,
);
if (factIndex === -1) throw new Error("Fact not found"); if (factIndex === -1) throw new Error("Fact not found");
db.data.facts[factIndex].content = content; db.data.facts[factIndex].content = content;
await db.write(); await db.write();
@ -101,7 +105,7 @@ export const facts = router({
messagesSincePreviousRunningSummary: Array<DraftMessage>; messagesSincePreviousRunningSummary: Array<DraftMessage>;
/** *will* have facts extracted */ /** *will* have facts extracted */
newMessages: Array<DraftMessage>; newMessages: Array<DraftMessage>;
}, }
) )
.query( .query(
async ({ async ({
@ -111,9 +115,7 @@ export const facts = router({
newMessages, newMessages,
}, },
}) => { }) => {
const factsFromUserMessageResponse = await generateObject<{ const factsFromUserMessageResponse = await generateObject({
facts: Array<string>;
}>({
model: openrouter("mistralai/mistral-nemo"), model: openrouter("mistralai/mistral-nemo"),
messages: [ messages: [
{ {
@ -130,7 +132,9 @@ export const facts = router({
}), }),
}, },
], ],
schema: jsonSchema({ schema: jsonSchema<{
facts: Array<string>;
}>({
type: "object", type: "object",
properties: { properties: {
facts: { facts: {
@ -144,7 +148,7 @@ export const facts = router({
temperature: 0.4, temperature: 0.4,
}); });
return factsFromUserMessageResponse; return factsFromUserMessageResponse;
}, }
), ),
}); });

@ -37,7 +37,10 @@ const runningSummaryUserPrompt = ({
}) => }) =>
`${messagesSincePreviousRunningSummary.map( `${messagesSincePreviousRunningSummary.map(
(message) => (message) =>
`<${message.role}_message>${message.content}</${message.role}_message>`, `<${message.role}_message>${message.parts
.filter((p) => p.type === "text")
.map((p) => p.text)
.join("\n")}</${message.role}_message>`
)} )}
<assistant_response> <assistant_response>
${mainResponseContent} ${mainResponseContent}
@ -61,7 +64,7 @@ export const messages = router({
previousRunningSummary: string; previousRunningSummary: string;
messagesSincePreviousRunningSummary: Array<DraftMessage>; messagesSincePreviousRunningSummary: Array<DraftMessage>;
mainResponseContent: string; mainResponseContent: string;
}, }
) )
.mutation( .mutation(
async ({ async ({
@ -88,11 +91,10 @@ export const messages = router({
}), }),
}, },
], ],
maxSteps: 3,
tools: undefined, tools: undefined,
}); });
return runningSummaryResponse; return runningSummaryResponse;
}, }
), ),
}); });

@ -31,7 +31,10 @@ const factTriggerCaller = createCallerFactTriggers({});
const mainSystemPrompt = ({ const mainSystemPrompt = ({
systemPrompt, systemPrompt,
previousRunningSummary, previousRunningSummary,
}: { systemPrompt: string; previousRunningSummary: string }) => `${systemPrompt} }: {
systemPrompt: string;
previousRunningSummary: string;
}) => `${systemPrompt}
This is a summary of the conversation so far, from your point-of-view (so "I" and "me" refer to you): This is a summary of the conversation so far, from your point-of-view (so "I" and "me" refer to you):
<running_summary> <running_summary>
@ -52,7 +55,7 @@ export const chat = router({
messages: Array<DraftMessage | CommittedMessage>; messages: Array<DraftMessage | CommittedMessage>;
systemPrompt: string; systemPrompt: string;
parameters: OtherParameters; parameters: OtherParameters;
}, }
) )
.mutation( .mutation(
async ({ async ({
@ -65,7 +68,7 @@ export const chat = router({
* anyone can freely do. */ * anyone can freely do. */
const previousRunningSummaryIndex = messages.findLastIndex( const previousRunningSummaryIndex = messages.findLastIndex(
(message) => (message) =>
typeof (message as CommittedMessage).runningSummary !== "undefined", typeof (message as CommittedMessage).runningSummary !== "undefined"
); );
const previousRunningSummary = const previousRunningSummary =
previousRunningSummaryIndex >= 0 previousRunningSummaryIndex >= 0
@ -73,14 +76,15 @@ export const chat = router({
.runningSummary as string) .runningSummary as string)
: ""; : "";
const messagesSincePreviousRunningSummary = messages.slice( const messagesSincePreviousRunningSummary = messages.slice(
previousRunningSummaryIndex + 1, previousRunningSummaryIndex + 1
); );
/** Save the incoming message to the database. */ /** Save the incoming message to the database. */
const insertedUserMessage: CommittedMessage = { const insertedUserMessage: CommittedMessage = {
id: nanoid(), id: nanoid(),
conversationId, conversationId,
content: messages[messages.length - 1].content, // content: messages[messages.length - 1].content,
role: "user" as const, // role: "user" as const,
...messages[messages.length - 1],
index: messages.length - 1, index: messages.length - 1,
createdAt: new Date().toISOString(), createdAt: new Date().toISOString(),
}; };
@ -98,7 +102,10 @@ export const chat = router({
model: openrouter("mistralai/mistral-nemo"), model: openrouter("mistralai/mistral-nemo"),
messages: [ messages: [
previousRunningSummary === "" previousRunningSummary === ""
? { role: "system" as const, content: systemPrompt } ? {
role: "system" as const,
content: systemPrompt,
}
: { : {
role: "system" as const, role: "system" as const,
content: mainSystemPrompt({ content: mainSystemPrompt({
@ -106,9 +113,14 @@ export const chat = router({
previousRunningSummary, previousRunningSummary,
}), }),
}, },
...messagesSincePreviousRunningSummary, ...messagesSincePreviousRunningSummary.map((m) => ({
role: m.role,
content: m.parts
.filter((p) => p.type === "text")
.map((p) => p.text)
.join(""),
})),
], ],
maxSteps: 3,
tools: undefined, tools: undefined,
...parameters, ...parameters,
}); });
@ -149,7 +161,8 @@ export const chat = router({
const insertedAssistantMessage: CommittedMessage = { const insertedAssistantMessage: CommittedMessage = {
id: nanoid(), id: nanoid(),
conversationId, conversationId,
content: mainResponse.text, // content: mainResponse.text,
parts: [{ type: "text", text: mainResponse.text }],
runningSummary: runningSummaryResponse.text, runningSummary: runningSummaryResponse.text,
role: "assistant" as const, role: "assistant" as const,
index: messages.length, index: messages.length,
@ -165,7 +178,8 @@ export const chat = router({
newMessages: [ newMessages: [
{ {
role: "assistant" as const, role: "assistant" as const,
content: mainResponse.text, // content: mainResponse.text,
parts: [{ type: "text", text: mainResponse.text }],
}, },
], ],
}); });
@ -217,7 +231,7 @@ export const chat = router({
insertedUserMessage, insertedUserMessage,
insertedFacts, insertedFacts,
}; };
}, }
), ),
}); });

@ -1,4 +1,4 @@
import type { Message as UIMessage } from "ai"; import type { UIMessage } from "ai";
import type { generateText } from "ai"; import type { generateText } from "ai";
import type { Conversation, Fact, FactTrigger } from "./database/lowdb.js"; import type { Conversation, Fact, FactTrigger } from "./database/lowdb.js";

Loading…
Cancel
Save