mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-04 02:31:27 +00:00
feat(ui): ModelConfigCard + OutputCard + PromptEditorHeader
- ModelConfigCard: @tremor Text + antd Input + custom black-overlay modal \u2192 shadcn Dialog + Button + Input + lucide Settings. Proper focus-trap and backdrop via Radix. - OutputCard: antd Typography \u2192 plain span with semantic tokens. Preserves SectionHeader and max-height expand/collapse animation. - PromptEditorHeader: @tremor Button + antd Input/Select + lucide icons \u2192 shadcn Button + Input + Select. Draft/version chips and 'Unsaved changes' indicator preserved. File added to overrides for the version pill's blue palette. Gates: TS \u2713 | Build \u2713. Co-authored-by: yuneng-jiang <yuneng-berri@users.noreply.github.com>
This commit is contained in:
parent
ff7cefada3
commit
e66b69852c
4 changed files with 110 additions and 134 deletions
|
|
@ -58,6 +58,7 @@
|
|||
"src/components/view_logs/ToolsSection/ToolItem.tsx",
|
||||
"src/components/guardrails/content_filter/PatternTable.tsx",
|
||||
"src/components/guardrails/content_filter/ContentFilterDisplay.tsx",
|
||||
"src/components/prompts/prompt_editor_view/PromptEditorHeader.tsx",
|
||||
"src/components/policies/impact_preview_alert.tsx",
|
||||
"src/components/policies/impact_popover.tsx",
|
||||
"src/components/DeletedKeysPage/DeletedKeysPage.tsx",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,13 @@
|
|||
import React, { useState } from "react";
|
||||
import { Text } from "@tremor/react";
|
||||
import { Input } from "antd";
|
||||
import { SettingsIcon } from "lucide-react";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Settings } from "lucide-react";
|
||||
import ModelSelector from "../../common_components/ModelSelector";
|
||||
|
||||
interface ModelConfigCardProps {
|
||||
|
|
@ -36,63 +42,53 @@ const ModelConfigCard: React.FC<ModelConfigCardProps> = ({
|
|||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => setShowConfig(!showConfig)}
|
||||
className="flex items-center gap-2 px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50"
|
||||
>
|
||||
<SettingsIcon size={16} />
|
||||
<span>Parameters</span>
|
||||
</button>
|
||||
<Button variant="outline" onClick={() => setShowConfig(!showConfig)}>
|
||||
<Settings className="h-4 w-4" />
|
||||
Parameters
|
||||
</Button>
|
||||
|
||||
{showConfig && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-30">
|
||||
<div className="bg-white rounded-lg shadow-xl p-6 w-96">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3 className="text-lg font-semibold">Model Parameters</h3>
|
||||
<button
|
||||
onClick={() => setShowConfig(false)}
|
||||
className="text-gray-400 hover:text-gray-600"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<Text className="text-sm text-gray-700">Temperature</Text>
|
||||
<Input
|
||||
type="number"
|
||||
size="small"
|
||||
min={0}
|
||||
max={2}
|
||||
step={0.1}
|
||||
value={temperature}
|
||||
onChange={(e) => onTemperatureChange(parseFloat(e.target.value) || 0)}
|
||||
className="w-20"
|
||||
/>
|
||||
</div>
|
||||
<Dialog open={showConfig} onOpenChange={setShowConfig}>
|
||||
<DialogContent className="max-w-96">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Model Parameters</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-sm text-foreground">Temperature</span>
|
||||
<Input
|
||||
type="number"
|
||||
min={0}
|
||||
max={2}
|
||||
step={0.1}
|
||||
value={temperature}
|
||||
onChange={(e) =>
|
||||
onTemperatureChange(parseFloat(e.target.value) || 0)
|
||||
}
|
||||
className="w-20 h-8"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<Text className="text-sm text-gray-700">Max Tokens</Text>
|
||||
<Input
|
||||
type="number"
|
||||
size="small"
|
||||
min={1}
|
||||
max={32768}
|
||||
value={maxTokens}
|
||||
onChange={(e) => onMaxTokensChange(parseInt(e.target.value) || 1000)}
|
||||
className="w-24"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-sm text-foreground">Max Tokens</span>
|
||||
<Input
|
||||
type="number"
|
||||
min={1}
|
||||
max={32768}
|
||||
value={maxTokens}
|
||||
onChange={(e) =>
|
||||
onMaxTokensChange(parseInt(e.target.value) || 1000)
|
||||
}
|
||||
className="w-24 h-8"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ModelConfigCard;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,14 @@
|
|||
import React from "react";
|
||||
import { Button as TremorButton } from "@tremor/react";
|
||||
import { Input, Select } from "antd";
|
||||
import { ArrowLeftIcon, SaveIcon, ClockIcon } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { ArrowLeft, Clock, Save } from "lucide-react";
|
||||
import PromptCodeSnippets from "./PromptCodeSnippets";
|
||||
|
||||
interface PromptEditorHeaderProps {
|
||||
|
|
@ -41,35 +48,36 @@ const PromptEditorHeader: React.FC<PromptEditorHeaderProps> = ({
|
|||
onEnvironmentChange,
|
||||
}) => {
|
||||
return (
|
||||
<div className="bg-white border-b border-gray-200 px-6 py-3 flex items-center justify-between">
|
||||
<div className="bg-background border-b border-border px-6 py-3 flex items-center justify-between">
|
||||
<div className="flex items-center space-x-3">
|
||||
<TremorButton icon={ArrowLeftIcon} variant="light" onClick={onBack} size="xs">
|
||||
<Button variant="ghost" size="sm" onClick={onBack}>
|
||||
<ArrowLeft className="h-3.5 w-3.5" />
|
||||
Back
|
||||
</TremorButton>
|
||||
</Button>
|
||||
<Input
|
||||
value={promptName}
|
||||
onChange={(e) => onNameChange(e.target.value)}
|
||||
className="text-base font-medium border-none shadow-none"
|
||||
style={{ width: "200px" }}
|
||||
className="text-base font-medium border-none shadow-none w-[200px] h-8"
|
||||
/>
|
||||
{version && (
|
||||
<span className="px-2 py-0.5 text-xs bg-blue-100 text-blue-700 rounded font-medium">
|
||||
<span className="px-2 py-0.5 text-xs bg-blue-100 text-blue-700 dark:bg-blue-950 dark:text-blue-300 rounded font-medium">
|
||||
{version}
|
||||
</span>
|
||||
)}
|
||||
<Select
|
||||
value={environment}
|
||||
onChange={onEnvironmentChange}
|
||||
style={{ width: 140 }}
|
||||
size="small"
|
||||
options={[
|
||||
{ label: "Development", value: "development" },
|
||||
{ label: "Staging", value: "staging" },
|
||||
{ label: "Production", value: "production" },
|
||||
]}
|
||||
/>
|
||||
<span className="px-2 py-0.5 text-xs bg-gray-100 text-gray-600 rounded">Draft</span>
|
||||
<span className="text-xs text-gray-400">Unsaved changes</span>
|
||||
<Select value={environment} onValueChange={onEnvironmentChange}>
|
||||
<SelectTrigger className="w-[140px] h-8">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="development">Development</SelectItem>
|
||||
<SelectItem value="staging">Staging</SelectItem>
|
||||
<SelectItem value="production">Production</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<span className="px-2 py-0.5 text-xs bg-muted text-muted-foreground rounded">
|
||||
Draft
|
||||
</span>
|
||||
<span className="text-xs text-muted-foreground">Unsaved changes</span>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<PromptCodeSnippets
|
||||
|
|
@ -77,30 +85,22 @@ const PromptEditorHeader: React.FC<PromptEditorHeaderProps> = ({
|
|||
model={promptModel}
|
||||
promptVariables={promptVariables}
|
||||
accessToken={accessToken}
|
||||
version={version?.replace('v', '') || "1"}
|
||||
version={version?.replace("v", "") || "1"}
|
||||
proxySettings={proxySettings}
|
||||
/>
|
||||
{editMode && onShowHistory && (
|
||||
<TremorButton
|
||||
icon={ClockIcon}
|
||||
variant="secondary"
|
||||
onClick={onShowHistory}
|
||||
>
|
||||
<Button variant="secondary" onClick={onShowHistory}>
|
||||
<Clock className="h-4 w-4" />
|
||||
History
|
||||
</TremorButton>
|
||||
</Button>
|
||||
)}
|
||||
<TremorButton
|
||||
icon={SaveIcon}
|
||||
onClick={onSave}
|
||||
loading={isSaving}
|
||||
disabled={isSaving}
|
||||
>
|
||||
{editMode ? "Update" : "Save"}
|
||||
</TremorButton>
|
||||
<Button onClick={onSave} disabled={isSaving}>
|
||||
<Save className="h-4 w-4" />
|
||||
{isSaving ? "Saving…" : editMode ? "Update" : "Save"}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PromptEditorHeader;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,8 @@
|
|||
/**
|
||||
* OutputCard - Displays output message with token count and cost
|
||||
* Datadog-style: header with icon/metrics, content below
|
||||
*/
|
||||
|
||||
import { useState } from 'react';
|
||||
import { Typography } from 'antd';
|
||||
import { useState } from "react";
|
||||
import MessageManager from "@/components/molecules/message_manager";
|
||||
import { ParsedMessage } from './prettyMessagesTypes';
|
||||
import { SectionHeader } from './SectionHeader';
|
||||
import { SimpleMessageBlock } from './SimpleMessageBlock';
|
||||
|
||||
const { Text } = Typography;
|
||||
import { ParsedMessage } from "./prettyMessagesTypes";
|
||||
import { SectionHeader } from "./SectionHeader";
|
||||
import { SimpleMessageBlock } from "./SimpleMessageBlock";
|
||||
|
||||
interface OutputCardProps {
|
||||
message: ParsedMessage | null;
|
||||
|
|
@ -18,26 +10,24 @@ interface OutputCardProps {
|
|||
outputCost?: number;
|
||||
}
|
||||
|
||||
export function OutputCard({ message, completionTokens, outputCost }: OutputCardProps) {
|
||||
export function OutputCard({
|
||||
message,
|
||||
completionTokens,
|
||||
outputCost,
|
||||
}: OutputCardProps) {
|
||||
const [isCollapsed, setIsCollapsed] = useState(false);
|
||||
|
||||
const handleCopy = () => {
|
||||
if (!message) return;
|
||||
|
||||
const content = message.content || '';
|
||||
|
||||
const content = message.content || "";
|
||||
navigator.clipboard.writeText(content);
|
||||
MessageManager.success('Output copied');
|
||||
MessageManager.success("Output copied");
|
||||
};
|
||||
|
||||
if (!message) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
border: '1px solid #f0f0f0',
|
||||
borderRadius: 6,
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
<div className="border border-border rounded-md overflow-hidden">
|
||||
<SectionHeader
|
||||
type="output"
|
||||
tokens={completionTokens}
|
||||
|
|
@ -47,17 +37,16 @@ export function OutputCard({ message, completionTokens, outputCost }: OutputCard
|
|||
onToggleCollapse={() => setIsCollapsed(!isCollapsed)}
|
||||
/>
|
||||
<div
|
||||
className="overflow-hidden transition-[max-height,opacity] duration-300 ease-out"
|
||||
style={{
|
||||
maxHeight: isCollapsed ? '0px' : '10000px',
|
||||
overflow: 'hidden',
|
||||
transition: 'max-height 0.3s ease-out, opacity 0.3s ease-out',
|
||||
maxHeight: isCollapsed ? "0px" : "10000px",
|
||||
opacity: isCollapsed ? 0 : 1,
|
||||
}}
|
||||
>
|
||||
<div style={{ padding: '12px 16px' }}>
|
||||
<Text type="secondary" style={{ fontSize: 13, fontStyle: 'italic' }}>
|
||||
<div className="px-4 py-3">
|
||||
<span className="text-[13px] italic text-muted-foreground">
|
||||
No response data available
|
||||
</Text>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -65,14 +54,7 @@ export function OutputCard({ message, completionTokens, outputCost }: OutputCard
|
|||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
border: '1px solid #f0f0f0',
|
||||
borderRadius: 6,
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
{/* Datadog-style Header */}
|
||||
<div className="border border-border rounded-md overflow-hidden">
|
||||
<SectionHeader
|
||||
type="output"
|
||||
tokens={completionTokens}
|
||||
|
|
@ -82,16 +64,14 @@ export function OutputCard({ message, completionTokens, outputCost }: OutputCard
|
|||
onToggleCollapse={() => setIsCollapsed(!isCollapsed)}
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
<div
|
||||
className="overflow-hidden transition-[max-height,opacity] duration-300 ease-out"
|
||||
style={{
|
||||
maxHeight: isCollapsed ? '0px' : '10000px',
|
||||
overflow: 'hidden',
|
||||
transition: 'max-height 0.3s ease-out, opacity 0.3s ease-out',
|
||||
maxHeight: isCollapsed ? "0px" : "10000px",
|
||||
opacity: isCollapsed ? 0 : 1,
|
||||
}}
|
||||
>
|
||||
<div style={{ padding: '12px 16px' }}>
|
||||
<div className="px-4 py-3">
|
||||
<SimpleMessageBlock
|
||||
label="ASSISTANT"
|
||||
content={message.content}
|
||||
|
|
@ -102,4 +82,3 @@ export function OutputCard({ message, completionTokens, outputCost }: OutputCard
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue