@ -4,7 +4,7 @@ import {
createCallerFactory ,
} from "../../trpc/server" ;
import { createOpenRouter } from "@openrouter/ai-sdk-provider" ;
import { generate Text } from "ai" ;
import { generate Object, generate Text, 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
/ * * G e n e r a t e a n e w m e s s a g e f r o m t h e m o d e l , b u t h o l d - o f f o n a d d i n g i t t o
@ -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 ) ;
/ * * E x t r a c t F a c t s f r o m t h e m o d e l ' s r e s p o n s e , a n d a d d t h e m t o t h e d a t a b a s e ,
* linking the Facts with the messages they came from . * /
/ * * F o r e a c h F a c t p r o d u c e d i n t h e t w o f a c t - e x t r a c t i o n s t e p s , g e n e r a t e
@ -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 ( ) ;
/ * * T O D O : n o t i f y t h e c a l l e r , s o m e h o w , t h a t s o m e m e s s a g e s w e r e s a v e d t o
* the database and / or were outfitted with runningSummaries , so the
* caller can update its UI state . * /
return { insertedAssistantMessage , insertedUserMessage } ;
return {
insertedAssistantMessage ,
insertedUserMessage ,
insertedFacts ,
} ;
} ,
) ,
} ) ;