add custom system prompt (#39)
This commit is contained in:
+23
-34
@@ -1,10 +1,11 @@
|
||||
import { Conversation, Message, OpenAIModel } from "@/types";
|
||||
import { Conversation, KeyValuePair, Message, OpenAIModel } from "@/types";
|
||||
import { FC, useEffect, useRef, useState } from "react";
|
||||
import { ChatInput } from "./ChatInput";
|
||||
import { ChatLoader } from "./ChatLoader";
|
||||
import { ChatMessage } from "./ChatMessage";
|
||||
import { ModelSelect } from "./ModelSelect";
|
||||
import { Regenerate } from "./Regenerate";
|
||||
import { SystemPrompt } from "./SystemPrompt";
|
||||
|
||||
interface Props {
|
||||
conversation: Conversation;
|
||||
@@ -15,20 +16,10 @@ interface Props {
|
||||
loading: boolean;
|
||||
lightMode: "light" | "dark";
|
||||
onSend: (message: Message, isResend: boolean) => void;
|
||||
onModelChange: (conversation: Conversation, model: OpenAIModel) => void;
|
||||
onUpdateConversation: (conversation: Conversation, data: KeyValuePair) => void;
|
||||
}
|
||||
|
||||
export const Chat: FC<Props> = ({
|
||||
conversation,
|
||||
models,
|
||||
messageIsStreaming,
|
||||
modelError,
|
||||
messageError,
|
||||
loading,
|
||||
lightMode,
|
||||
onSend,
|
||||
onModelChange,
|
||||
}) => {
|
||||
export const Chat: FC<Props> = ({ conversation, models, messageIsStreaming, modelError, messageError, loading, lightMode, onSend, onUpdateConversation }) => {
|
||||
const [currentMessage, setCurrentMessage] = useState<Message>();
|
||||
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||
@@ -46,38 +37,36 @@ export const Chat: FC<Props> = ({
|
||||
{modelError ? (
|
||||
<div className="flex flex-col justify-center mx-auto h-full w-[300px] sm:w-[500px] space-y-6">
|
||||
<div className="text-center text-red-500">Error fetching models.</div>
|
||||
<div className="text-center text-red-500">
|
||||
Make sure your OpenAI API key is set in the bottom left of the
|
||||
sidebar or in a .env.local file and refresh.
|
||||
</div>
|
||||
<div className="text-center text-red-500">
|
||||
If you completed this step, OpenAI may be experiencing issues.
|
||||
</div>
|
||||
<div className="text-center text-red-500">Make sure your OpenAI API key is set in the bottom left of the sidebar or in a .env.local file and refresh.</div>
|
||||
<div className="text-center text-red-500">If you completed this step, OpenAI may be experiencing issues.</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div>
|
||||
{conversation.messages.length === 0 ? (
|
||||
<>
|
||||
<div className="flex justify-center pt-8">
|
||||
<ModelSelect
|
||||
model={conversation.model}
|
||||
models={models}
|
||||
onModelChange={(model) =>
|
||||
onModelChange(conversation, model)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col mx-auto pt-12 space-y-10 w-[350px] sm:w-[600px]">
|
||||
<div className="text-4xl text-center text-neutral-600 dark:text-neutral-200">{models.length === 0 ? "Loading..." : "Chatbot UI"}</div>
|
||||
|
||||
<div className="text-4xl text-center text-neutral-600 dark:text-neutral-200 pt-[160px] sm:pt-[280px]">
|
||||
{models.length === 0 ? "Loading..." : "Chatbot UI"}
|
||||
{models.length > 0 && (
|
||||
<div className="flex flex-col h-full space-y-4 border p-4 rounded border-neutral-500">
|
||||
<ModelSelect
|
||||
model={conversation.model}
|
||||
models={models}
|
||||
onModelChange={(model) => onUpdateConversation(conversation, { key: "model", value: model })}
|
||||
/>
|
||||
|
||||
<SystemPrompt
|
||||
conversation={conversation}
|
||||
onChangePrompt={(prompt) => onUpdateConversation(conversation, { key: "prompt", value: prompt })}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex justify-center py-2 text-neutral-500 bg-neutral-100 dark:bg-[#444654] dark:text-neutral-200 text-sm border border-b-neutral-300 dark:border-none">
|
||||
Model: {conversation.model.name}
|
||||
</div>
|
||||
<div className="flex justify-center py-2 text-neutral-500 bg-neutral-100 dark:bg-[#444654] dark:text-neutral-200 text-sm border border-b-neutral-300 dark:border-none">Model: {conversation.model.name}</div>
|
||||
|
||||
{conversation.messages.map((message, index) => (
|
||||
<ChatMessage
|
||||
|
||||
@@ -45,10 +45,8 @@ export const ChatInput: FC<Props> = ({ onSend, messageIsStreaming, model }) => {
|
||||
};
|
||||
|
||||
const isMobile = () => {
|
||||
const userAgent =
|
||||
typeof window.navigator === "undefined" ? "" : navigator.userAgent;
|
||||
const mobileRegex =
|
||||
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|Mobile|mobile|CriOS/i;
|
||||
const userAgent = typeof window.navigator === "undefined" ? "" : navigator.userAgent;
|
||||
const mobileRegex = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|Mobile|mobile|CriOS/i;
|
||||
return mobileRegex.test(userAgent);
|
||||
};
|
||||
|
||||
@@ -72,12 +70,12 @@ export const ChatInput: FC<Props> = ({ onSend, messageIsStreaming, model }) => {
|
||||
<div className="fixed sm:absolute bottom-4 sm:bottom-8 w-full sm:w-1/2 px-2 left-0 sm:left-[280px] lg:left-[200px] right-0 ml-auto mr-auto">
|
||||
<textarea
|
||||
ref={textareaRef}
|
||||
className="rounded-lg pl-4 pr-8 py-3 w-full focus:outline-none max-h-[280px] dark:bg-[#40414F] dark:border-opacity-50 dark:border-neutral-800 dark:text-neutral-100 border border-neutral-300 shadow text-neutral-900"
|
||||
className="rounded-lg pl-4 pr-8 py-3 w-full focus:outline-none dark:bg-[#40414F] dark:border-opacity-50 dark:border-neutral-800 dark:text-neutral-100 border border-neutral-300 shadow text-neutral-900"
|
||||
style={{
|
||||
resize: "none",
|
||||
bottom: `${textareaRef?.current?.scrollHeight}px`,
|
||||
maxHeight: "400px",
|
||||
overflow: "auto",
|
||||
overflow: "auto"
|
||||
}}
|
||||
placeholder="Type a message..."
|
||||
value={content}
|
||||
|
||||
@@ -20,9 +20,7 @@ export const ChatMessage: FC<Props> = ({ message, lightMode }) => {
|
||||
|
||||
<div className="prose dark:prose-invert mt-[-2px]">
|
||||
{message.role === "user" ? (
|
||||
<div className="prose dark:prose-invert whitespace-pre-wrap">
|
||||
{message.content}
|
||||
</div>
|
||||
<div className="prose dark:prose-invert whitespace-pre-wrap">{message.content}</div>
|
||||
) : (
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[remarkGfm]}
|
||||
@@ -38,11 +36,23 @@ export const ChatMessage: FC<Props> = ({ message, lightMode }) => {
|
||||
{...props}
|
||||
/>
|
||||
) : (
|
||||
<code className={className} {...props}>
|
||||
<code
|
||||
className={className}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</code>
|
||||
);
|
||||
},
|
||||
table({ children }) {
|
||||
return <table className="border-collapse border border-black dark:border-white py-1 px-3">{children}</table>;
|
||||
},
|
||||
th({ children }) {
|
||||
return <th className="border border-black dark:border-white break-words py-1 px-3 bg-gray-500 text-white">{children}</th>;
|
||||
},
|
||||
td({ children }) {
|
||||
return <td className="border border-black dark:border-white break-words py-1 px-3">{children}</td>;
|
||||
}
|
||||
}}
|
||||
>
|
||||
{message.content}
|
||||
|
||||
@@ -12,7 +12,7 @@ export const ModelSelect: FC<Props> = ({ model, models, onModelChange }) => {
|
||||
<div className="flex flex-col">
|
||||
<label className="text-left mb-2 dark:text-neutral-400 text-neutral-700">Model</label>
|
||||
<select
|
||||
className="w-[300px] p-3 dark:text-white dark:bg-[#343541] border border-neutral-500 rounded-lg appearance-none focus:shadow-outline text-neutral-900 cursor-pointer"
|
||||
className="w-full p-3 dark:text-white dark:bg-[#343541] border border-neutral-500 rounded-lg appearance-none focus:shadow-outline text-neutral-900 cursor-pointer"
|
||||
placeholder="Select a model"
|
||||
value={model.id}
|
||||
onChange={(e) => {
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { Conversation } from "@/types";
|
||||
import { DEFAULT_SYSTEM_PROMPT } from "@/utils/app/const";
|
||||
import { FC, useEffect, useRef, useState } from "react";
|
||||
|
||||
interface Props {
|
||||
conversation: Conversation;
|
||||
onChangePrompt: (prompt: string) => void;
|
||||
}
|
||||
|
||||
export const SystemPrompt: FC<Props> = ({ conversation, onChangePrompt }) => {
|
||||
const [value, setValue] = useState<string>("");
|
||||
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
const value = e.target.value;
|
||||
const maxLength = 4000;
|
||||
|
||||
if (value.length > maxLength) {
|
||||
alert(`Prompt limit is ${maxLength} characters`);
|
||||
return;
|
||||
}
|
||||
|
||||
setValue(value);
|
||||
|
||||
if (value.length > 0) {
|
||||
onChangePrompt(value);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (textareaRef && textareaRef.current) {
|
||||
textareaRef.current.style.height = "inherit";
|
||||
textareaRef.current.style.height = `${textareaRef.current?.scrollHeight}px`;
|
||||
}
|
||||
}, [value]);
|
||||
|
||||
useEffect(() => {
|
||||
if (conversation.prompt) {
|
||||
setValue(conversation.prompt);
|
||||
} else {
|
||||
setValue(DEFAULT_SYSTEM_PROMPT);
|
||||
}
|
||||
}, [conversation]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
<label className="text-left dark:text-neutral-400 text-neutral-700 mb-2">System Prompt</label>
|
||||
<textarea
|
||||
ref={textareaRef}
|
||||
className="w-full rounded-lg px-4 py-2 focus:outline-none dark:bg-[#40414F] dark:border-opacity-50 dark:border-neutral-800 dark:text-neutral-100 border border-neutral-500 shadow text-neutral-900"
|
||||
style={{
|
||||
resize: "none",
|
||||
bottom: `${textareaRef?.current?.scrollHeight}px`,
|
||||
maxHeight: "300px",
|
||||
overflow: "auto"
|
||||
}}
|
||||
placeholder="Enter a prompt"
|
||||
value={value}
|
||||
rows={1}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user