Spaces:
Running
Running
"use client"; | |
import styles from './page.module.css'; | |
import { useEffect, useState } from 'react'; | |
import { useChat } from 'ai/react'; | |
import { FunctionCallHandler, Message, nanoid } from 'ai'; | |
import ReactMarkdown from "react-markdown"; | |
import { Bot, User } from "lucide-react"; | |
import { toast } from 'sonner'; | |
import { FunctionIcon } from './icons'; | |
import { updateBackground } from './util'; | |
import Input from './input'; | |
const Page: React.FC = () => { | |
useEffect(() => { | |
updateBackground(); | |
const interval = setInterval(updateBackground, 600); | |
return () => clearInterval(interval); | |
}, []); | |
const functionCallHandler: FunctionCallHandler = async ( | |
chatMessages, | |
functionCall, | |
) => { | |
let result; | |
const { name, arguments: args } = functionCall; | |
const response = await fetch("/api/functions", { | |
method: "POST", | |
headers: { | |
"Content-Type": "application/json", | |
}, | |
body: JSON.stringify({ | |
args: args, | |
name: name | |
}) | |
} as any); | |
if (!response.ok) { | |
const errorText = await response.text(); | |
toast.error(`Something went wrong: ${errorText}`); | |
return; | |
} | |
result = await response.text(); | |
return { | |
messages: [ | |
...chatMessages, | |
{ | |
id: nanoid(), | |
name: functionCall.name, | |
role: "function" as const, | |
content: result, | |
}, | |
], | |
}; | |
}; | |
const { messages, input, setInput, handleSubmit, isLoading } = useChat({ | |
experimental_onFunctionCall: functionCallHandler, | |
onError: (error: any) => { | |
console.log(error); | |
}, | |
}); | |
const [isExpanded, setIsExpanded] = useState(false); | |
const toggleExpand = () => { | |
setIsExpanded(!isExpanded); | |
}; | |
const roleUIConfig: { | |
[key: string]: { | |
avatar: JSX.Element; | |
bgColor: string; | |
avatarColor: string; | |
// eslint-disable-next-line no-unused-vars | |
dialogComponent: (message: Message) => JSX.Element; | |
}; | |
} = { | |
user: { | |
avatar: <User width={20} />, | |
bgColor: "bg-white", | |
avatarColor: "bg-black", | |
dialogComponent: (message: Message) => ( | |
<ReactMarkdown | |
className="" | |
components={{ | |
a: (props) => ( | |
<a {...props} target="_blank" rel="noopener noreferrer" /> | |
), | |
}} | |
> | |
{message.content} | |
</ReactMarkdown> | |
), | |
}, | |
assistant: { | |
avatar: <Bot width={20} />, | |
bgColor: "bg-gray-100", | |
avatarColor: "bg-green-500", | |
dialogComponent: (message: Message) => ( | |
<ReactMarkdown | |
className="" | |
components={{ | |
a: (props) => ( | |
<a {...props} target="_blank" rel="noopener noreferrer" /> | |
), | |
}} | |
> | |
{message.content} | |
</ReactMarkdown> | |
), | |
}, | |
function: { | |
avatar: <div className="cursor-pointer" onClick={toggleExpand}><FunctionIcon /></div>, | |
bgColor: "bg-gray-200", | |
avatarColor: "bg-blue-500", | |
dialogComponent: (message: Message) => { | |
return ( | |
<div className="flex flex-col"> | |
{isExpanded && ( | |
<div className="py-1">{message.content}</div> | |
)} | |
</div> | |
); | |
}, | |
} | |
}; | |
return ( | |
<main className={styles.main}> | |
<div id="bg" className={styles.background}></div> | |
<div className={styles.messages}> | |
{messages.length > 0 ? ( | |
messages.map((message, i) => { | |
const messageClass = `${styles.message} ${message.role === 'user' ? styles['message-user'] : ''}`; | |
return ( | |
<div key={i} className={messageClass} style={{ display: 'flex', alignItems: 'center' }}> | |
<div className={styles.avatar}> | |
{roleUIConfig[message.role].avatar} | |
</div> | |
<div style={{ flex: 1 }}> | |
{message.content === "" && message.function_call != undefined ? ( | |
typeof message.function_call === "object" ? ( | |
<div style={{ display: 'flex', flexDirection: 'column' }}> | |
<div> | |
Using{" "} | |
<span className="font-bold"> | |
{message.function_call.name} | |
</span>{" "} | |
... | |
</div> | |
<div> | |
{message.function_call.arguments} | |
</div> | |
</div> | |
) : ( | |
<div className="function-call">{message.function_call}</div> | |
) | |
) : ( | |
roleUIConfig[message.role].dialogComponent(message) | |
)} | |
</div> | |
</div> | |
); | |
}) | |
) : null} | |
</div> | |
<Input handleSubmit={handleSubmit as any} setInput={setInput} input={input} /> | |
</main> | |
); | |
} | |
export default Page; | |