setup postgres and kyseley for most data instead of milvus
This commit is contained in:
+2
-2
@@ -10,8 +10,8 @@ export default {
|
||||
Layout,
|
||||
|
||||
// https://vike.dev/head-tags
|
||||
title: "My Vike App",
|
||||
description: "Demo showcasing Vike",
|
||||
title: "Trainable AI",
|
||||
description: "The Chatbot that Remembers",
|
||||
|
||||
passToClient: ["user"],
|
||||
extends: vikeReact,
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
import { JsonInput, Tabs, Textarea } from "@mantine/core";
|
||||
import { trpc } from "../../trpc/client";
|
||||
import { create } from "zustand";
|
||||
import type { Message as UIMessage } from "ai";
|
||||
import type { OtherParameters, Store } from "./types.js";
|
||||
|
||||
const defaultSystemPrompt = `You are a helpful assistant that answers questions based on the provided context. If you don't know the answer, just say that you don't know, don't try to make up an answer.`;
|
||||
const defaultParameters = {
|
||||
temperature: 0.5,
|
||||
max_tokens: 100,
|
||||
} as OtherParameters;
|
||||
|
||||
const useStore = create<Store>()((set) => ({
|
||||
messages: [],
|
||||
message: "",
|
||||
systemPrompt: defaultSystemPrompt,
|
||||
parameters: defaultParameters,
|
||||
loading: false,
|
||||
setMessages: (messages) => set({ messages }),
|
||||
setMessage: (message) => set({ message }),
|
||||
setSystemPrompt: (systemPrompt) => set({ systemPrompt }),
|
||||
setParameters: (parameters) => set({ parameters }),
|
||||
setLoading: (loading) => set({ loading }),
|
||||
}));
|
||||
|
||||
export default function ChatPage() {
|
||||
const messages = useStore((state) => state.messages);
|
||||
const message = useStore((state) => state.message);
|
||||
const systemPrompt = useStore((state) => state.systemPrompt);
|
||||
const parameters = useStore((state) => state.parameters);
|
||||
const loading = useStore((state) => state.loading);
|
||||
const setMessages = useStore((state) => state.setMessages);
|
||||
const setMessage = useStore((state) => state.setMessage);
|
||||
const setSystemPrompt = useStore((state) => state.setSystemPrompt);
|
||||
const setParameters = useStore((state) => state.setParameters);
|
||||
const setLoading = useStore((state) => state.setLoading);
|
||||
|
||||
return (
|
||||
<Tabs defaultValue="message">
|
||||
<Tabs.List>
|
||||
<Tabs.Tab value="message">Message</Tabs.Tab>
|
||||
<Tabs.Tab value="system-prompt">System Prompt</Tabs.Tab>
|
||||
<Tabs.Tab value="parameters">Parameters</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
<Tabs.Panel value="message">
|
||||
<Messages messages={messages} />
|
||||
<Textarea
|
||||
resize="vertical"
|
||||
placeholder="Type your message here..."
|
||||
value={message}
|
||||
disabled={loading}
|
||||
onChange={(e) => setMessage(e.target.value)}
|
||||
onKeyDown={async (e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
const messagesWithNewUserMessage = [
|
||||
...messages,
|
||||
{ role: "user" as const, content: message } as UIMessage,
|
||||
];
|
||||
setMessages(messagesWithNewUserMessage);
|
||||
setLoading(true);
|
||||
const response = await trpc.chat.sendMessage.query({
|
||||
messages: messagesWithNewUserMessage,
|
||||
systemPrompt,
|
||||
parameters,
|
||||
});
|
||||
const messagesWithAssistantMessage = [
|
||||
...messagesWithNewUserMessage,
|
||||
{ role: "assistant", content: response.text } as UIMessage,
|
||||
];
|
||||
setMessages(messagesWithAssistantMessage);
|
||||
setMessage("");
|
||||
setLoading(false);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Tabs.Panel>
|
||||
<Tabs.Panel value="system-prompt">
|
||||
<Textarea
|
||||
resize="vertical"
|
||||
placeholder={defaultSystemPrompt}
|
||||
value={systemPrompt}
|
||||
onChange={(e) => setSystemPrompt(e.target.value)}
|
||||
/>
|
||||
</Tabs.Panel>
|
||||
<Tabs.Panel value="parameters">
|
||||
<JsonInput
|
||||
resize="vertical"
|
||||
formatOnBlur
|
||||
placeholder={JSON.stringify(defaultParameters)}
|
||||
value={JSON.stringify(parameters)}
|
||||
onChange={(value) => setParameters(JSON.parse(value))}
|
||||
/>
|
||||
</Tabs.Panel>
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
|
||||
function Messages({
|
||||
messages,
|
||||
}: {
|
||||
messages: Array<UIMessage>;
|
||||
}) {
|
||||
return (
|
||||
<div>
|
||||
{messages.map((message, index) => (
|
||||
// biome-ignore lint/suspicious/noArrayIndexKey: <explanation>
|
||||
<div key={index}>{message.content}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
import { JsonInput, Tabs, Textarea } from "@mantine/core";
|
||||
import { trpc } from "../../../trpc/client";
|
||||
import type { Message as UIMessage } from "ai";
|
||||
import { useEffect } from "react";
|
||||
import {
|
||||
defaultParameters,
|
||||
defaultSystemPrompt,
|
||||
useStore,
|
||||
} from "../../../state";
|
||||
import { usePageContext } from "vike-react/usePageContext";
|
||||
import { useData } from "vike-react/useData";
|
||||
import type { Data } from "./+data";
|
||||
import type { ConversationsId } from "../../../database/generated/public/Conversations";
|
||||
|
||||
export default function ChatPage() {
|
||||
const pageContext = usePageContext();
|
||||
const conversationId = Number(pageContext.routeParams.id) as ConversationsId;
|
||||
const conversationTitle = useStore((state) => state.conversationTitle);
|
||||
const messages = useStore((state) => state.messages);
|
||||
const message = useStore((state) => state.message);
|
||||
const systemPrompt = useStore((state) => state.systemPrompt);
|
||||
const parameters = useStore((state) => state.parameters);
|
||||
const loading = useStore((state) => state.loading);
|
||||
const setConversationId = useStore((state) => state.setConversationId);
|
||||
const setConversationTitle = useStore((state) => state.setConversationTitle);
|
||||
const setMessages = useStore((state) => state.setMessages);
|
||||
const setMessage = useStore((state) => state.setMessage);
|
||||
const setSystemPrompt = useStore((state) => state.setSystemPrompt);
|
||||
const setParameters = useStore((state) => state.setParameters);
|
||||
const setLoading = useStore((state) => state.setLoading);
|
||||
|
||||
const conversation = useData<Data>();
|
||||
|
||||
useEffect(() => {
|
||||
setConversationId(conversationId);
|
||||
}, [conversationId, setConversationId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (conversation?.id && conversation?.title) {
|
||||
setConversationId(conversation.id);
|
||||
setConversationTitle(conversation.title);
|
||||
}
|
||||
}, [
|
||||
conversation?.id,
|
||||
conversation?.title,
|
||||
setConversationId,
|
||||
setConversationTitle,
|
||||
]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<span>Conversation #{conversationId} - </span>
|
||||
<input
|
||||
type="text"
|
||||
value={conversationTitle}
|
||||
onChange={(e) => {
|
||||
setConversationTitle(e.target.value);
|
||||
}}
|
||||
onBlur={(e) => {
|
||||
trpc.chat.updateConversationTitle.mutate({
|
||||
id: conversationId,
|
||||
title: e.target.value,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Tabs defaultValue="message">
|
||||
<Tabs.List>
|
||||
<Tabs.Tab value="message">Message</Tabs.Tab>
|
||||
<Tabs.Tab value="system-prompt">System Prompt</Tabs.Tab>
|
||||
<Tabs.Tab value="parameters">Parameters</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
<Tabs.Panel value="message">
|
||||
<Messages messages={messages} />
|
||||
<Textarea
|
||||
resize="vertical"
|
||||
placeholder="Type your message here..."
|
||||
value={message}
|
||||
disabled={loading}
|
||||
onChange={(e) => setMessage(e.target.value)}
|
||||
onKeyDown={async (e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
const messagesWithNewUserMessage = [
|
||||
...messages,
|
||||
{ role: "user" as const, content: message } as UIMessage,
|
||||
];
|
||||
setMessages(messagesWithNewUserMessage);
|
||||
setLoading(true);
|
||||
const response = await trpc.chat.sendMessage.query({
|
||||
messages: messagesWithNewUserMessage,
|
||||
systemPrompt,
|
||||
parameters,
|
||||
});
|
||||
const messagesWithAssistantMessage = [
|
||||
...messagesWithNewUserMessage,
|
||||
{ role: "assistant", content: response.text } as UIMessage,
|
||||
];
|
||||
setMessages(messagesWithAssistantMessage);
|
||||
setMessage("");
|
||||
setLoading(false);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Tabs.Panel>
|
||||
<Tabs.Panel value="system-prompt">
|
||||
<Textarea
|
||||
resize="vertical"
|
||||
placeholder={defaultSystemPrompt}
|
||||
value={systemPrompt}
|
||||
onChange={(e) => setSystemPrompt(e.target.value)}
|
||||
/>
|
||||
</Tabs.Panel>
|
||||
<Tabs.Panel value="parameters">
|
||||
<JsonInput
|
||||
resize="vertical"
|
||||
formatOnBlur
|
||||
placeholder={JSON.stringify(defaultParameters)}
|
||||
value={JSON.stringify(parameters)}
|
||||
onChange={(value) => setParameters(JSON.parse(value))}
|
||||
/>
|
||||
</Tabs.Panel>
|
||||
</Tabs>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function Messages({
|
||||
messages,
|
||||
}: {
|
||||
messages: Array<UIMessage>;
|
||||
}) {
|
||||
return (
|
||||
<div>
|
||||
{messages.map((message, index) => (
|
||||
// biome-ignore lint/suspicious/noArrayIndexKey: <explanation>
|
||||
<div key={index}>{message.content}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import type { PageContextServer } from "vike/types";
|
||||
import { createCaller } from "../trpc.js";
|
||||
|
||||
export type Data = Awaited<ReturnType<typeof data>>;
|
||||
|
||||
export const data = async (pageContext: PageContextServer) => {
|
||||
const { id } = pageContext.routeParams;
|
||||
const caller = createCaller({});
|
||||
const conversation = await caller.fetchConversation({
|
||||
id: Number(id),
|
||||
});
|
||||
return conversation;
|
||||
};
|
||||
@@ -3,9 +3,8 @@ The system begins with a generic system prompt.
|
||||
The user begins interacting with the model, perhaps introducing himself. Perhaps the initial UI should contain a pre-filled message as if it's from the model, saying "Hi, I'm {name}. Tell me about yourself or what you want me to do."
|
||||
|
||||
Every time the user submits a message, the backend should:
|
||||
* Save the message to the database under a conversation key for later lookup
|
||||
|
||||
* Generate a new message from the model, and add it to the database. The model should be
|
||||
* Save the message to the database under a conversation id for later lookup
|
||||
* Generate a new message from the model, and add it to the database. The model should be given the conversation summary thus far, and of course the user's latest message, unmodified. Invite the model to create any tools it needs. The tool needs to be implemented in a language which this system can execute; usually an interpretted language like Python or JavaScript.
|
||||
* Extract Facts from the user's message, and add them to the database, linking the Facts with the messages they came from. (Yes, this should be done *after* the model response, not before; because when we run a query to find Facts to inject into the context sent to the model, we don't want Facts from the user's current message to be candidates for 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.)
|
||||
* 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 FactTriggers and add them to the database, linking the FactTriggers with the Facts they came from. A FactTrigger is a natural language phrase that describes a situation in which it would be useful to invoke the Fact. (e.g., "When food preferences are discussed").
|
||||
|
||||
+64
-2
@@ -1,20 +1,80 @@
|
||||
import {
|
||||
router,
|
||||
publicProcedure,
|
||||
createCallerFactory,
|
||||
Validator,
|
||||
// Validator
|
||||
} from "../../trpc/server";
|
||||
// import { Type as T } from "@sinclair/typebox";
|
||||
import { createOpenRouter } from "@openrouter/ai-sdk-provider";
|
||||
import { generateText } from "ai";
|
||||
import type { Message as UIMessage } from "ai";
|
||||
import type { OtherParameters } from "./types.js";
|
||||
import type { OtherParameters } from "../../types.js";
|
||||
import { env } from "../../server/env.js";
|
||||
// import { client } from "../../database/milvus";
|
||||
// import {
|
||||
// ConsistencyLevelEnum,
|
||||
// type NumberArrayId,
|
||||
// } from "@zilliz/milvus2-sdk-node";
|
||||
import { db } from "../../database/postgres";
|
||||
import type { ConversationsId } from "../../database/generated/public/Conversations";
|
||||
import type { UsersId } from "../../database/generated/public/Users";
|
||||
|
||||
const openrouter = createOpenRouter({
|
||||
apiKey: env.OPENROUTER_API_KEY,
|
||||
});
|
||||
|
||||
export const chat = router({
|
||||
listConversations: publicProcedure.query(async () => {
|
||||
const rows = await db.selectFrom("conversations").selectAll().execute();
|
||||
return rows;
|
||||
}),
|
||||
fetchConversation: publicProcedure
|
||||
.input((x) => x as { id: number })
|
||||
.query(async ({ input: { id } }) => {
|
||||
const row = await db
|
||||
.selectFrom("conversations")
|
||||
.selectAll()
|
||||
.where("id", "=", id as ConversationsId)
|
||||
.executeTakeFirst();
|
||||
return row;
|
||||
}),
|
||||
createConversation: publicProcedure.mutation(async () => {
|
||||
const title = "New Conversation";
|
||||
const row = await db
|
||||
.insertInto("conversations")
|
||||
.values({
|
||||
title,
|
||||
user_id: 1 as UsersId,
|
||||
})
|
||||
.returningAll()
|
||||
.executeTakeFirst();
|
||||
return row;
|
||||
}),
|
||||
deleteConversation: publicProcedure
|
||||
.input((x) => x as { id: number })
|
||||
.mutation(async ({ input: { id } }) => {
|
||||
const result = await db
|
||||
.deleteFrom("conversations")
|
||||
.where("id", "=", id as ConversationsId)
|
||||
.execute();
|
||||
return result;
|
||||
}),
|
||||
updateConversationTitle: publicProcedure
|
||||
.input(
|
||||
(x) =>
|
||||
x as {
|
||||
id: number;
|
||||
title: string;
|
||||
},
|
||||
)
|
||||
.mutation(async ({ input: { id, title } }) => {
|
||||
const result = await db
|
||||
.updateTable("conversations")
|
||||
.set({ title })
|
||||
.where("id", "=", id as ConversationsId)
|
||||
.execute();
|
||||
return result[0];
|
||||
}),
|
||||
sendMessage: publicProcedure
|
||||
.input(
|
||||
(x) =>
|
||||
@@ -38,3 +98,5 @@ export const chat = router({
|
||||
return response;
|
||||
}),
|
||||
});
|
||||
|
||||
export const createCaller = createCallerFactory(chat);
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import type { Message as UIMessage } from "ai";
|
||||
import type { generateText } from "ai";
|
||||
|
||||
export type OtherParameters = Omit<
|
||||
Parameters<typeof generateText>[0],
|
||||
"model" | "messages" | "abortSignal"
|
||||
>;
|
||||
|
||||
export type Store = {
|
||||
messages: Array<UIMessage>;
|
||||
message: string;
|
||||
systemPrompt: string;
|
||||
parameters: OtherParameters;
|
||||
loading: boolean;
|
||||
setMessages: (messages: Array<UIMessage>) => void;
|
||||
setMessage: (message: string) => void;
|
||||
setSystemPrompt: (systemPrompt: string) => void;
|
||||
setParameters: (parameters: OtherParameters) => void;
|
||||
setLoading: (loading: boolean) => void;
|
||||
};
|
||||
Reference in New Issue
Block a user