factored-out `generateFromFact` into `fact-triggers` module

master
Avraham Sakal 2 months ago
parent 41e94feef6
commit 5e5549e85f

@ -3,7 +3,56 @@ import {
publicProcedure, publicProcedure,
createCallerFactory, createCallerFactory,
} from "../../trpc/server.js"; } from "../../trpc/server.js";
import { db } from "../../database/lowdb.js"; import { db, Fact } from "../../database/lowdb.js";
import type { DraftMessage } from "../../types.js";
import { openrouter } from "./provider.js";
import { generateObject, generateText, jsonSchema } from "ai";
const factTriggersSystemPrompt = ({
previousRunningSummary,
messagesSincePreviousRunningSummary,
mainResponseContent,
}: {
previousRunningSummary: string;
messagesSincePreviousRunningSummary: Array<DraftMessage>;
mainResponseContent: string;
}) => `You are an expert at idenitfying situations when facts are useful.
You will be given a summary of a conversation, and the messages exchanged since that summary was produced.
Then you will be given a fact that was extracted from that conversation, and you will need to identify a natural language phrase that describes a situation in which it would be useful to invoke the fact.
The facts will be used to enrich context of conversations with AI assistants: upon each turn, a semantic database will be searched to see whether the current situation in the conversation matches any situations that are deemed to render the fact useful, and the fact will be injected into the context of the conversation.
Your task is to produce a list of triggers for the fact.
* You should not extract any facts that are already in the summary.
* The user should be referred to as "the user" in the fact text.
* The user's pronouns should be either he or she, NOT "they" or "them", because these triggers will be read by an AI assistant to give it context; and excessive use of "they" or "them" will make what they refer to unclear or ambiguous.
* The assistant should be referred to as "I" or "me", because these triggers will be read by an AI assistant to give it context.
<running_summary>
${previousRunningSummary}
</running_summary>
${messagesSincePreviousRunningSummary.map(
(message) =>
`<${message.role}_message>${message.content}</${message.role}_message>`,
)}
<assistant_response>
${mainResponseContent}
</assistant_response>
`;
const factTriggersUserPrompt = ({
factContent,
}: {
factContent: string;
}) => `<fact_content>
${factContent}
</fact_content>
Generate a list of situations in which the fact is useful.`;
export const factTriggers = router({ export const factTriggers = router({
fetchByFactId: publicProcedure fetchByFactId: publicProcedure
@ -29,6 +78,50 @@ export const factTriggers = router({
db.write(); db.write();
return { ok: true }; return { ok: true };
}), }),
generateFromFact: publicProcedure
.input((x) => x as {
previousRunningSummary: string;
messagesSincePreviousRunningSummary: Array<DraftMessage>;
mainResponseContent: string;
fact: Fact;
})
.mutation(async ({ input: { previousRunningSummary, messagesSincePreviousRunningSummary, mainResponseContent, fact } }) => {
const factTriggers = await generateObject<{
factTriggers: Array<string>;
}>({
model: openrouter("mistralai/mistral-nemo"),
messages: [
{
role: "system" as const,
content: factTriggersSystemPrompt({
previousRunningSummary,
messagesSincePreviousRunningSummary,
mainResponseContent,
}),
},
{
role: "user" as const,
content: factTriggersUserPrompt({
factContent: fact.content,
}),
},
],
schema: jsonSchema({
type: "object",
properties: {
factTriggers: {
type: "array",
items: {
type: "string",
},
},
},
}),
// maxSteps: 3,
// tools: undefined,
});
return factTriggers;
})
}); });
export const createCaller = createCallerFactory(factTriggers); export const createCaller = createCallerFactory(factTriggers);

@ -20,10 +20,12 @@ import { conversations } from "./conversations.js";
import { messages } from "./messages.js"; import { messages } from "./messages.js";
import { facts, createCaller as createCallerFacts } from "./facts.js"; import { facts, createCaller as createCallerFacts } from "./facts.js";
import { createCaller as createCallerMessages } from "./messages.js"; import { createCaller as createCallerMessages } from "./messages.js";
import { createCaller as createCallerFactTriggers } from "./fact-triggers.js";
import { openrouter } from "./provider.js"; import { openrouter } from "./provider.js";
const factsCaller = createCallerFacts({}); const factsCaller = createCallerFacts({});
const messagesCaller = createCallerMessages({}); const messagesCaller = createCallerMessages({});
const factTriggerCaller = createCallerFactTriggers({});
const mainSystemPrompt = ({ const mainSystemPrompt = ({
systemPrompt, systemPrompt,
@ -36,51 +38,6 @@ ${previousRunningSummary}
</running_summary> </running_summary>
`; `;
const factTriggersSystemPrompt = ({
previousRunningSummary,
messagesSincePreviousRunningSummary,
mainResponseContent,
}: {
previousRunningSummary: string;
messagesSincePreviousRunningSummary: Array<DraftMessage>;
mainResponseContent: string;
}) => `You are an expert at idenitfying situations when facts are useful.
You will be given a summary of a conversation, and the messages exchanged since that summary was produced.
Then you will be given a fact that was extracted from that conversation, and you will need to identify a natural language phrase that describes a situation in which it would be useful to invoke the fact.
The facts will be used to enrich context of conversations with AI assistants: upon each turn, a semantic database will be searched to see whether the current situation in the conversation matches any situations that are deemed to render the fact useful, and the fact will be injected into the context of the conversation.
Your task is to produce a list of triggers for the fact.
* You should not extract any facts that are already in the summary.
* The user should be referred to as "the user" in the fact text.
* The user's pronouns should be either he or she, NOT "they" or "them", because these triggers will be read by an AI assistant to give it context; and excessive use of "they" or "them" will make what they refer to unclear or ambiguous.
* The assistant should be referred to as "I" or "me", because these triggers will be read by an AI assistant to give it context.
<running_summary>
${previousRunningSummary}
</running_summary>
${messagesSincePreviousRunningSummary.map(
(message) =>
`<${message.role}_message>${message.content}</${message.role}_message>`,
)}
<assistant_response>
${mainResponseContent}
</assistant_response>
`;
const factTriggersUserPrompt = ({
factContent,
}: {
factContent: string;
}) => `<fact_content>
${factContent}
</fact_content>
Generate a list of situations in which the fact is useful.`;
export const chat = router({ export const chat = router({
conversations, conversations,
@ -232,40 +189,11 @@ export const chat = router({
* phrase that describes a situation in which it would be useful to invoke * phrase that describes a situation in which it would be useful to invoke
* the Fact. (e.g., "When food preferences are discussed"). */ * the Fact. (e.g., "When food preferences are discussed"). */
for (const fact of insertedFacts) { for (const fact of insertedFacts) {
const factTriggers = await generateObject<{ const factTriggers = await factTriggerCaller.generateFromFact({
factTriggers: Array<string>; mainResponseContent: mainResponse.text,
}>({
model: openrouter("mistralai/mistral-nemo"),
messages: [
{
role: "system" as const,
content: factTriggersSystemPrompt({
previousRunningSummary, previousRunningSummary,
messagesSincePreviousRunningSummary, messagesSincePreviousRunningSummary,
mainResponseContent: mainResponse.text, fact
}),
},
{
role: "user" as const,
content: factTriggersUserPrompt({
factContent: fact.content,
}),
},
],
schema: jsonSchema({
type: "object",
properties: {
factTriggers: {
type: "array",
items: {
type: "string",
},
},
},
}),
maxSteps: 3,
tools: undefined,
...parameters,
}); });
const insertedFactTriggers: Array<FactTrigger> = const insertedFactTriggers: Array<FactTrigger> =
factTriggers.object.factTriggers.map((factTrigger) => ({ factTriggers.object.factTriggers.map((factTrigger) => ({

Loading…
Cancel
Save