You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

113 lines
3.8 KiB
TypeScript

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>
);
}