upgrade ai sdk to v5
This commit is contained in:
Vendored
+19
@@ -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
|
||||||
|
|||||||
+3
-2
@@ -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>;
|
||||||
};
|
};
|
||||||
|
|||||||
+55
-30
@@ -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,10 +126,10 @@ 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,12 +139,19 @@ 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>;
|
||||||
}),
|
}),
|
||||||
|
|||||||
+17
-13
@@ -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;
|
||||||
},
|
}
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+19
-15
@@ -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;
|
||||||
},
|
}
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+26
-12
@@ -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";
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user