10M-LLM / client /src /App.jsx
abancp's picture
openwebbook2
82f9e44
import { useState } from 'react'
import './App.css'
import Dropdown from 'react-dropdown';
import 'react-dropdown/style.css';
function App() {
const [prompt, setPrompt] = useState('')
const [isLoading, setIsLoading] = useState(false)
const [modelType,setModelType] = useState("pre")
const [chat, setChat] = useState([])
// Function to parse text and identify code blocks
const parseTextWithCodeBlocks = (text) => {
const codeBlockRegex = /```(\w+)?\s*([\s\S]+?)\s*```/g;
const parts = [];
let lastIndex = 0;
let match;
while ((match = codeBlockRegex.exec(text)) !== null) {
// Add text before the code block
if (match.index > lastIndex) {
parts.push({
type: 'text',
content: text.slice(lastIndex, match.index)
});
}
// Add the code block
parts.push({
type: 'code',
language: match[1] || '',
content: match[2]
});
lastIndex = match.index + match[0].length;
}
// Add remaining text after last code block
if (lastIndex < text.length) {
parts.push({
type: 'text',
content: text.slice(lastIndex)
});
}
return parts;
}
const handleSubmit = async () => {
chat.push({ src: "USER", "text": prompt })
setIsLoading(true)
let system_prompt = " ### "
const res = await fetch('http://127.0.0.1:8000/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ prompt:modelType === "fine" ? prompt + system_prompt : prompt }),
})
const reader = res.body.getReader()
const decoder = new TextDecoder('utf-8')
while (true) {
const { value, done } = await reader.read()
if (done) break
const chunk = decoder.decode(value)
if (chat[chat.length - 1].src === "AI") {
chat[chat.length - 1].text += chunk
} else {
chat.push({ src: "AI", text: chunk })
chat[chat.length - 1].text = chunk
}
setChat([...chat])
}
setIsLoading(false)
setPrompt("")
}
return (
<div className='w-full flex-col gap-6 h-screen bg-white flex justify-center items-center p-4'>
<header className='fixed top-0 left-0 p-4 flex justify-between'>
<div className='flex gap-4'>
<svg onClick={() => setChat([])} xmlns="http://www.w3.org/2000/svg" width="26" height="26" fill="currentColor" class="bi text-gray-700 bi-pencil-square" viewBox="0 0 16 16">
<path d="M15.502 1.94a.5.5 0 0 1 0 .706L14.459 3.69l-2-2L13.502.646a.5.5 0 0 1 .707 0l1.293 1.293zm-1.75 2.456-2-2L4.939 9.21a.5.5 0 0 0-.121.196l-.805 2.414a.25.25 0 0 0 .316.316l2.414-.805a.5.5 0 0 0 .196-.12l6.813-6.814z" />
<path fill-rule="evenodd" d="M1 13.5A1.5 1.5 0 0 0 2.5 15h11a1.5 1.5 0 0 0 1.5-1.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5v-11a.5.5 0 0 1 .5-.5H9a.5.5 0 0 0 0-1H2.5A1.5 1.5 0 0 0 1 2.5z" />
</svg>
<select value={modelType} onChange={(e)=>setModelType(e.target.value)} className='font-semibold focus:outline-none focus:border-0 text-lg text-gray-700'>
<option value={"pre"} selected>pre trained</option>
<option value={"fine"} selected>fine tuned</option>
</select>
</div>
</header>
{chat.length === 0 && <h1 className='text-3xl font-bold'>What can I Help With?</h1>}
{chat.length !== 0 &&
<div
className='w-screen h-[calc(100vh-6rem)] items-center justify-start flex flex-col pt-4 overflow-y-scroll'
>
{
<div className='h-full w-[44rem] flex flex-col gap-3'>
{
chat.map((msg, index) => (
<div key={index} className={`${msg.src === "USER" ? 'justify-end' : 'justify-start'} flex`}>
<div className={`${msg.src === "USER" ? 'bg-gray-200 font-mono rounded-[2rem] w-fit px-4 py-2' : ''} p-4`}>
{msg.src === "AI" ? (
<div className="whitespace-pre-wrap">
{parseTextWithCodeBlocks(msg.text).map((part, i) => (
part.type === 'code' ? (
<div key={i} className="my-2">
<div className="bg-gray-800 text-gray-100 px-2 py-1 text-sm rounded-t-md">
{part.language || 'code'}
</div>
<pre className="bg-gray-100 p-3 rounded-b-md overflow-x-auto text-sm">
<code>{part.content}</code>
</pre>
</div>
) : (
<span key={i}>{part.content}</span>
)
))}
</div>
) : (
<pre>{msg.text}</pre>
)}
</div>
</div>
))
}
</div>
}
</div>
}
<div className='w-[50rem] p-4 h-fit shadow-[-2px_4px_6px_0px_rgba(0,_0,_0,_0.1)] rounded-[2rem] border border-gray-300'>
<textarea onKeyDown={(e)=>e.key === "Enter" && handleSubmit()}
placeholder='Ask anything...'
className='max-h-[12rem] min-h-[2rem] w-full focus:outline-none'
value={prompt}
onChange={e => setPrompt(e.target.value)}
></textarea>
<div className='w-full flex justify-end mt-2'>
{isLoading
?
<div className='cursor-pointer rounded-full w-[34px] h-[34px] flex justify-center items-center p-2 bg-black'>
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" fill="currentColor" class="bi bi-square-fill text-white" viewBox="0 0 16 16">
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2z" />
</svg>
</div>
:
<svg onClick={handleSubmit} xmlns="http://www.w3.org/2000/svg" width="34" height="34" fill="currentColor" className="cursor-pointer bi bi-arrow-up-circle-fill" viewBox="0 0 16 16">
<path d="M16 8A8 8 0 1 0 0 8a8 8 0 0 0 16 0m-7.5 3.5a.5.5 0 0 1-1 0V5.707L5.354 7.854a.5.5 0 1 1-.708-.708l3-3a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 5.707z" />
</svg>}
</div>
</div>
</div>
)
}
export default App