mirror of
https://github.com/BerriAI/litellm.git
synced 2025-04-25 18:54:30 +00:00
refactor chat playground
This commit is contained in:
parent
353c882574
commit
e91dc1e00f
1 changed files with 174 additions and 195 deletions
|
@ -226,206 +226,185 @@ const ChatUI: React.FC<ChatUIProps> = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ width: "100%", position: "relative" }}>
|
<div style={{ width: "100%", position: "relative" }}>
|
||||||
<Grid className="gap-2 p-8 h-[80vh] w-full mt-2">
|
<div className="flex h-[80vh] w-full mt-2">
|
||||||
<Card>
|
{/* Left Sidebar with Controls */}
|
||||||
|
<div className="w-1/4 p-4 border-r">
|
||||||
|
<div className="mb-6">
|
||||||
|
<Text className="font-medium block mb-2">API Key Source</Text>
|
||||||
|
<Select
|
||||||
|
disabled={disabledPersonalKeyCreation}
|
||||||
|
defaultValue="session"
|
||||||
|
style={{ width: "100%" }}
|
||||||
|
onChange={(value) => setApiKeySource(value as "session" | "custom")}
|
||||||
|
options={[
|
||||||
|
{ value: 'session', label: 'Current UI Session' },
|
||||||
|
{ value: 'custom', label: 'Virtual Key' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
{apiKeySource === 'custom' && (
|
||||||
|
<TextInput
|
||||||
|
className="mt-2"
|
||||||
|
placeholder="Enter custom API key"
|
||||||
|
type="password"
|
||||||
|
onValueChange={setApiKey}
|
||||||
|
value={apiKey}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
<TabGroup>
|
<div className="mb-6">
|
||||||
<TabList>
|
<Text className="font-medium block mb-2">Select Model:</Text>
|
||||||
<Tab>Chat</Tab>
|
<Select
|
||||||
</TabList>
|
placeholder="Select a Model"
|
||||||
<TabPanels>
|
onChange={onModelChange}
|
||||||
<TabPanel>
|
options={[
|
||||||
<div className="sm:max-w-2xl">
|
...modelInfo.map((option) => ({
|
||||||
<Grid numItems={2}>
|
value: option.model_group,
|
||||||
<Col>
|
label: option.model_group
|
||||||
<Text>API Key Source</Text>
|
})),
|
||||||
<Select
|
{ value: 'custom', label: 'Enter custom model' }
|
||||||
disabled={disabledPersonalKeyCreation}
|
]}
|
||||||
defaultValue="session"
|
style={{ width: "100%" }}
|
||||||
style={{ width: "100%" }}
|
showSearch={true}
|
||||||
onChange={(value) => setApiKeySource(value as "session" | "custom")}
|
/>
|
||||||
options={[
|
{showCustomModelInput && (
|
||||||
{ value: 'session', label: 'Current UI Session' },
|
<TextInput
|
||||||
{ value: 'custom', label: 'Virtual Key' },
|
className="mt-2"
|
||||||
]}
|
placeholder="Enter custom model name"
|
||||||
|
onValueChange={(value) => {
|
||||||
|
// Using setTimeout to create a simple debounce effect
|
||||||
|
if (customModelTimeout.current) {
|
||||||
|
clearTimeout(customModelTimeout.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
customModelTimeout.current = setTimeout(() => {
|
||||||
|
setSelectedModel(value);
|
||||||
|
}, 500); // 500ms delay after typing stops
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mb-6">
|
||||||
|
<EndpointSelector
|
||||||
|
endpointType={endpointType}
|
||||||
|
onEndpointChange={handleEndpointChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
onClick={clearChatHistory}
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
Clear Chat
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Main Chat Area */}
|
||||||
|
<div className="w-3/4 flex flex-col">
|
||||||
|
<div className="flex-1 overflow-auto p-4">
|
||||||
|
{chatHistory.map((message, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className={`mb-4 ${message.role === "user" ? "text-right" : "text-left"}`}
|
||||||
|
>
|
||||||
|
<div style={{
|
||||||
|
display: 'inline-block',
|
||||||
|
maxWidth: '80%',
|
||||||
|
borderRadius: '8px',
|
||||||
|
padding: '10px 14px',
|
||||||
|
backgroundColor: message.role === "user" ? '#f0f0f0' : '#f9f9f9',
|
||||||
|
textAlign: 'left'
|
||||||
|
}}>
|
||||||
|
<div style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '8px',
|
||||||
|
marginBottom: '4px'
|
||||||
|
}}>
|
||||||
|
<strong>{message.role}</strong>
|
||||||
|
{message.role === "assistant" && message.model && (
|
||||||
|
<span style={{
|
||||||
|
fontSize: '12px',
|
||||||
|
color: '#666',
|
||||||
|
backgroundColor: '#f5f5f5',
|
||||||
|
padding: '2px 6px',
|
||||||
|
borderRadius: '4px',
|
||||||
|
fontWeight: 'normal'
|
||||||
|
}}>
|
||||||
|
{message.model}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div style={{
|
||||||
|
whiteSpace: "pre-wrap",
|
||||||
|
wordBreak: "break-word",
|
||||||
|
maxWidth: "100%"
|
||||||
|
}}>
|
||||||
|
{message.isImage ? (
|
||||||
|
<img
|
||||||
|
src={message.content}
|
||||||
|
alt="Generated image"
|
||||||
|
style={{ maxWidth: '100%', maxHeight: '500px' }}
|
||||||
/>
|
/>
|
||||||
{apiKeySource === 'custom' && (
|
) : (
|
||||||
<TextInput
|
<ReactMarkdown
|
||||||
className="mt-2"
|
components={{
|
||||||
placeholder="Enter custom API key"
|
code({node, inline, className, children, ...props}: React.ComponentPropsWithoutRef<'code'> & {
|
||||||
type="password"
|
inline?: boolean;
|
||||||
onValueChange={setApiKey}
|
node?: any;
|
||||||
value={apiKey}
|
}) {
|
||||||
/>
|
const match = /language-(\w+)/.exec(className || '');
|
||||||
)}
|
return !inline && match ? (
|
||||||
</Col>
|
<SyntaxHighlighter
|
||||||
<Col className="mx-2">
|
style={coy as any}
|
||||||
<Text>Select Model:</Text>
|
language={match[1]}
|
||||||
<Select
|
PreTag="div"
|
||||||
placeholder="Select a Model"
|
{...props}
|
||||||
onChange={onModelChange}
|
|
||||||
options={[
|
|
||||||
...modelInfo.map((option) => ({
|
|
||||||
value: option.model_group,
|
|
||||||
label: option.model_group
|
|
||||||
})),
|
|
||||||
{ value: 'custom', label: 'Enter custom model' }
|
|
||||||
]}
|
|
||||||
style={{ width: "350px" }}
|
|
||||||
showSearch={true}
|
|
||||||
/>
|
|
||||||
{showCustomModelInput && (
|
|
||||||
<TextInput
|
|
||||||
className="mt-2"
|
|
||||||
placeholder="Enter custom model name"
|
|
||||||
onValueChange={(value) => {
|
|
||||||
// Using setTimeout to create a simple debounce effect
|
|
||||||
if (customModelTimeout.current) {
|
|
||||||
clearTimeout(customModelTimeout.current);
|
|
||||||
}
|
|
||||||
|
|
||||||
customModelTimeout.current = setTimeout(() => {
|
|
||||||
setSelectedModel(value);
|
|
||||||
}, 500); // 500ms delay after typing stops
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<EndpointSelector
|
|
||||||
endpointType={endpointType}
|
|
||||||
onEndpointChange={handleEndpointChange}
|
|
||||||
className="mt-2"
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
{/* Clear Chat Button */}
|
|
||||||
<Button
|
|
||||||
onClick={clearChatHistory}
|
|
||||||
className="mt-4"
|
|
||||||
>
|
|
||||||
Clear Chat
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<Table
|
|
||||||
className="mt-5"
|
|
||||||
style={{
|
|
||||||
display: "block",
|
|
||||||
maxHeight: "60vh",
|
|
||||||
overflowY: "auto",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<TableHead>
|
|
||||||
<TableRow>
|
|
||||||
<TableCell>
|
|
||||||
{/* <Title>Chat</Title> */}
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
</TableHead>
|
|
||||||
<TableBody>
|
|
||||||
{chatHistory.map((message, index) => (
|
|
||||||
<TableRow key={index}>
|
|
||||||
<TableCell>
|
|
||||||
<div style={{
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: '8px',
|
|
||||||
marginBottom: '4px'
|
|
||||||
}}>
|
|
||||||
<strong>{message.role}</strong>
|
|
||||||
{message.role === "assistant" && message.model && (
|
|
||||||
<span style={{
|
|
||||||
fontSize: '12px',
|
|
||||||
color: '#666',
|
|
||||||
backgroundColor: '#f5f5f5',
|
|
||||||
padding: '2px 6px',
|
|
||||||
borderRadius: '4px',
|
|
||||||
fontWeight: 'normal'
|
|
||||||
}}>
|
|
||||||
{message.model}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div style={{
|
|
||||||
whiteSpace: "pre-wrap",
|
|
||||||
wordBreak: "break-word",
|
|
||||||
maxWidth: "100%"
|
|
||||||
}}>
|
|
||||||
{message.isImage ? (
|
|
||||||
<img
|
|
||||||
src={message.content}
|
|
||||||
alt="Generated image"
|
|
||||||
style={{ maxWidth: '100%', maxHeight: '500px' }}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<ReactMarkdown
|
|
||||||
components={{
|
|
||||||
code({node, inline, className, children, ...props}: React.ComponentPropsWithoutRef<'code'> & {
|
|
||||||
inline?: boolean;
|
|
||||||
node?: any;
|
|
||||||
}) {
|
|
||||||
const match = /language-(\w+)/.exec(className || '');
|
|
||||||
return !inline && match ? (
|
|
||||||
<SyntaxHighlighter
|
|
||||||
style={coy as any}
|
|
||||||
language={match[1]}
|
|
||||||
PreTag="div"
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
{String(children).replace(/\n$/, '')}
|
|
||||||
</SyntaxHighlighter>
|
|
||||||
) : (
|
|
||||||
<code className={className} {...props}>
|
|
||||||
{children}
|
|
||||||
</code>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{message.content}
|
{String(children).replace(/\n$/, '')}
|
||||||
</ReactMarkdown>
|
</SyntaxHighlighter>
|
||||||
)}
|
) : (
|
||||||
</div>
|
<code className={className} {...props}>
|
||||||
</TableCell>
|
{children}
|
||||||
</TableRow>
|
</code>
|
||||||
))}
|
);
|
||||||
<TableRow>
|
}
|
||||||
<TableCell>
|
}}
|
||||||
<div ref={chatEndRef} style={{ height: "1px" }} />
|
>
|
||||||
</TableCell>
|
{message.content}
|
||||||
</TableRow>
|
</ReactMarkdown>
|
||||||
</TableBody>
|
)}
|
||||||
</Table>
|
|
||||||
<div
|
|
||||||
className="mt-3"
|
|
||||||
style={{ position: "absolute", bottom: 5, width: "95%" }}
|
|
||||||
>
|
|
||||||
<div className="flex" style={{ marginTop: "16px" }}>
|
|
||||||
<TextInput
|
|
||||||
type="text"
|
|
||||||
value={inputMessage}
|
|
||||||
onChange={(e) => setInputMessage(e.target.value)}
|
|
||||||
onKeyDown={handleKeyDown}
|
|
||||||
placeholder={
|
|
||||||
endpointType === EndpointType.CHAT
|
|
||||||
? "Type your message..."
|
|
||||||
: "Describe the image you want to generate..."
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
onClick={handleSendMessage}
|
|
||||||
className="ml-2"
|
|
||||||
>
|
|
||||||
{endpointType === EndpointType.CHAT ? "Send" : "Generate"}
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</TabPanel>
|
</div>
|
||||||
|
))}
|
||||||
</TabPanels>
|
<div ref={chatEndRef} style={{ height: "1px" }} />
|
||||||
</TabGroup>
|
</div>
|
||||||
</Card>
|
|
||||||
</Grid>
|
<div className="p-4 border-t">
|
||||||
|
<div className="flex">
|
||||||
|
<TextInput
|
||||||
|
type="text"
|
||||||
|
value={inputMessage}
|
||||||
|
onChange={(e) => setInputMessage(e.target.value)}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
placeholder={
|
||||||
|
endpointType === EndpointType.CHAT
|
||||||
|
? "Type your message..."
|
||||||
|
: "Describe the image you want to generate..."
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
onClick={handleSendMessage}
|
||||||
|
className="ml-2"
|
||||||
|
>
|
||||||
|
{endpointType === EndpointType.CHAT ? "Send" : "Generate"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue