Download app/page.tsx from matthoffner/chat-with-internet: direct link, hf CLI and curl.
- Browser
- Download file 5.24 kB
-
https://huggingface.co/spaces/matthoffner/chat-with-internet/resolve/08cc2d69bd56e4b73b39132082e6bf86ff6a016a/app/page.tsx
- Command line
-
hf download hf://spaces/matthoffner/chat-with-internet@08cc2d69bd56e4b73b39132082e6bf86ff6a016a/app/page.tsx
-
curl -L -o page.tsx https://huggingface.co/spaces/matthoffner/chat-with-internet/resolve/08cc2d69bd56e4b73b39132082e6bf86ff6a016a/app/page.tsx
5.24 kB
| "use client"; | |
| import styles from './page.module.css'; | |
| import { 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'; | |
| const Page: React.FC = () => { | |
| 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({ | |
| prompt: input, | |
| 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, | |
| onResponse: (response: { status: number; }) => { | |
| if (response.status === 429) { | |
| toast.error("You have reached your request limit for the day."); | |
| return; | |
| } else { | |
| console.log("chat initialized"); | |
| } | |
| }, | |
| 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}> | |
| <h1 className={styles.title}> | |
| π URL Surfer πββοΈ | |
| </h1> | |
| <div className={styles.messages}> | |
| {messages.length > 0 ? ( | |
| messages.map((message, i) => { | |
| const messageClass = `message ${message.role === 'user' ? 'message-user' : ''}`; | |
| return ( | |
| <div key={i} className={messageClass}> | |
| <div className="avatar"> | |
| {roleUIConfig[message.role].avatar} | |
| </div> | |
| {message.content === "" && message.function_call != undefined ? ( | |
| typeof message.function_call === "object" ? ( | |
| <div className="flex flex-col"> | |
| <div> | |
| Using{" "} | |
| <span className="font-bold"> | |
| {message.function_call.name} | |
| </span>{" "} | |
| ... | |
| </div> | |
| <div className=""> | |
| {message.function_call.arguments} | |
| </div> | |
| </div> | |
| ) : ( | |
| <div className="function-call">{message.function_call}</div> | |
| ) | |
| ) : ( | |
| roleUIConfig[message.role].dialogComponent(message) | |
| )} | |
| </div> | |
| ); | |
| })) : null} | |
| </div> | |
| <form onSubmit={handleSubmit} className={styles.form}> | |
| <input | |
| type="text" | |
| value={input} | |
| onChange={(e) => setInput(e.target.value)} | |
| placeholder="Chat with Internet" | |
| className={styles.input} | |
| /> | |
| <button type="submit" className={styles.button} disabled={isLoading}> | |
| {isLoading ? 'Loading...' : 'Send'} | |
| </button> | |
| </form> | |
| </main> | |
| ); | |
| }; | |
| export default Page; | |