generate and save facts

This commit is contained in:
Avraham Sakal
2025-07-27 10:53:25 -04:00
parent d7716bd451
commit ee9e9424ab
7 changed files with 114 additions and 18 deletions
+22 -3
View File
@@ -2,6 +2,7 @@ import {
Box,
Group,
JsonInput,
List,
Stack,
Tabs,
Textarea,
@@ -30,6 +31,7 @@ export default function ChatPage() {
const message = useStore((state) => state.message);
const systemPrompt = useStore((state) => state.systemPrompt);
const parameters = useStore((state) => state.parameters);
const facts = useStore((state) => state.facts);
const loading = useStore((state) => state.loading);
const setConversationId = useStore((state) => state.setConversationId);
const setConversationTitle = useStore((state) => state.setConversationTitle);
@@ -37,9 +39,14 @@ export default function ChatPage() {
const setMessage = useStore((state) => state.setMessage);
const setSystemPrompt = useStore((state) => state.setSystemPrompt);
const setParameters = useStore((state) => state.setParameters);
const setFacts = useStore((state) => state.setFacts);
const setLoading = useStore((state) => state.setLoading);
const { conversation, messages: initialMessages } = useData<Data>();
const {
conversation,
messages: initialMessages,
facts: initialFacts,
} = useData<Data>();
useEffect(() => {
setConversationId(conversationId);
@@ -61,6 +68,10 @@ export default function ChatPage() {
setMessages(initialMessages);
}, [initialMessages, setMessages]);
useEffect(() => {
setFacts(initialFacts);
}, [initialFacts, setFacts]);
return (
<>
<div>
@@ -84,6 +95,7 @@ export default function ChatPage() {
<Tabs.Tab value="message">Message</Tabs.Tab>
<Tabs.Tab value="system-prompt">System Prompt</Tabs.Tab>
<Tabs.Tab value="parameters">Parameters</Tabs.Tab>
<Tabs.Tab value="facts">Facts</Tabs.Tab>
</Tabs.List>
<Tabs.Panel value="message">
<Messages messages={messages} />
@@ -131,6 +143,7 @@ export default function ChatPage() {
];
setMessages(messagesWithAssistantMessage);
setMessage("");
setFacts(response.insertedFacts);
setLoading(false);
}
}}
@@ -153,6 +166,13 @@ export default function ChatPage() {
onChange={(value) => setParameters(JSON.parse(value))}
/>
</Tabs.Panel>
<Tabs.Panel value="facts">
<List>
{facts.map((fact) => (
<List.Item key={fact.id}>{fact.content}</List.Item>
))}
</List>
</Tabs.Panel>
</Tabs>
</>
);
@@ -164,7 +184,6 @@ function Messages({
messages: Array<DraftMessage | CommittedMessage>;
}) {
const theme = useMantineTheme();
console.log("messages", messages);
return (
<Stack gap="md" justify="flex-start">
{messages.map((message, index) => (
@@ -188,7 +207,7 @@ function Messages({
</div>
<Markdown>{message.content}</Markdown>
</Box>
{"runningSummary" in message && (
{"runningSummary" in message && message.runningSummary && (
<Box w="75%" bd="dotted" p="md" bdrs="md">
<div>
<strong>Running Summary:</strong>
+1 -1
View File
@@ -12,5 +12,5 @@ export const data = async (pageContext: PageContextServer) => {
const messages = await caller.fetchMessages({
conversationId: id,
});
return { conversation, messages };
return { conversation, messages, facts: [] };
};
+67 -9
View File
@@ -4,7 +4,7 @@ import {
createCallerFactory,
} from "../../trpc/server";
import { createOpenRouter } from "@openrouter/ai-sdk-provider";
import { generateText } from "ai";
import { generateObject, generateText, jsonSchema } from "ai";
import type { Message as UIMessage } from "ai";
import type {
OtherParameters,
@@ -17,7 +17,7 @@ import { env } from "../../server/env.js";
// ConsistencyLevelEnum,
// type NumberArrayId,
// } from "@zilliz/milvus2-sdk-node";
import { db } from "../../database/lowdb";
import { db, type Fact } from "../../database/lowdb";
import { nanoid } from "nanoid";
const mainSystemPrompt = ({
@@ -30,6 +30,15 @@ This is a summary of the conversation so far, from your point-of-view (so "I" an
${previousRunningSummary}
</running_summary>
`;
const factsFromUserMessageSystemPrompt = ({
previousRunningSummary,
}: {
previousRunningSummary: string;
}) => `Given the following summary of a conversation, coupled with the messages exchanged since that summary was produced, extract new facts that can be gleaned from the conversation.
<running_summary>
${previousRunningSummary}
</running_summary>
`;
const runningSummarySystemPrompt = ({
previousRunningSummary,
}: {
@@ -69,10 +78,19 @@ export const chat = router({
deleteConversation: publicProcedure
.input((x) => x as { id: string })
.mutation(async ({ input: { id } }) => {
await db.data.conversations.splice(
db.data.conversations.splice(
db.data.conversations.findIndex((c) => c.id === id),
1,
);
const deletedMessageIds = db.data.messages
.filter((m) => m.conversationId === id)
.map((m) => m.id);
db.data.messages = db.data.messages.filter(
(m) => m.conversationId !== id,
);
db.data.facts = db.data.facts.filter(
(fact) => !deletedMessageIds.includes(fact.sourceMessageId),
);
db.write();
return { ok: true };
}),
@@ -136,7 +154,7 @@ export const chat = router({
index: messages.length - 1,
createdAt: new Date().toISOString(),
};
await db.data.messages.push(insertedUserMessage);
db.data.messages.push(insertedUserMessage);
// do not db.write() until the end
/** Generate a new message from the model, but hold-off on adding it to
@@ -172,6 +190,43 @@ export const chat = router({
* injection, because we're sending the user's message unadulterated to
* the model; there's no reason to inject the same Facts that the model is
* already using to generate its response.) */
const factsFromUserMessageResponse = await generateObject<{
facts: Array<string>;
}>({
model: openrouter("mistralai/mistral-nemo"),
messages: [
{
role: "system" as const,
content: factsFromUserMessageSystemPrompt({
previousRunningSummary,
}),
},
...messages.slice(previousRunningSummaryIndex + 1),
],
schema: jsonSchema({
type: "object",
properties: {
facts: {
type: "array",
items: {
type: "string",
},
},
},
}),
maxSteps: 3,
tools: undefined,
...parameters,
});
const insertedFacts: Array<Fact> =
factsFromUserMessageResponse.object.facts.map((fact) => ({
id: nanoid(),
userId: "1",
sourceMessageId: insertedUserMessage.id,
content: fact,
createdAt: new Date().toISOString(),
}));
db.data.facts.push(...insertedFacts);
/** Extract Facts from the model's response, and add them to the database,
* linking the Facts with the messages they came from. */
/** For each Fact produced in the two fact-extraction steps, generate
@@ -246,12 +301,15 @@ export const chat = router({
index: messages.length,
createdAt: new Date().toISOString(),
};
await db.data.messages.push(insertedAssistantMessage);
db.data.messages.push(insertedAssistantMessage);
await db.write();
/** TODO: notify the caller, somehow, that some messages were saved to
* the database and/or were outfitted with runningSummaries, so the
* caller can update its UI state. */
return { insertedAssistantMessage, insertedUserMessage };
return {
insertedAssistantMessage,
insertedUserMessage,
insertedFacts,
};
},
),
});