Merge pull request #483 from MODSetter/dev

feat(model-config): enhance model selection UI
This commit is contained in:
Rohan Verma 2025-11-13 12:48:33 -08:00 committed by GitHub
commit bdd8cb9bbc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -3,7 +3,9 @@
import { import {
AlertCircle, AlertCircle,
Bot, Bot,
Check,
CheckCircle, CheckCircle,
ChevronsUpDown,
Clock, Clock,
Edit3, Edit3,
Eye, Eye,
@ -21,6 +23,14 @@ import { Alert, AlertDescription } from "@/components/ui/alert";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card"; import { Card, CardContent } from "@/components/ui/card";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command";
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
@ -30,6 +40,7 @@ import {
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { import {
Select, Select,
SelectContent, SelectContent,
@ -41,6 +52,7 @@ import { LANGUAGES } from "@/contracts/enums/languages";
import { getModelsByProvider } from "@/contracts/enums/llm-models"; import { getModelsByProvider } from "@/contracts/enums/llm-models";
import { LLM_PROVIDERS } from "@/contracts/enums/llm-providers"; import { LLM_PROVIDERS } from "@/contracts/enums/llm-providers";
import { type CreateLLMConfig, type LLMConfig, useLLMConfigs } from "@/hooks/use-llm-configs"; import { type CreateLLMConfig, type LLMConfig, useLLMConfigs } from "@/hooks/use-llm-configs";
import { cn } from "@/lib/utils";
import InferenceParamsEditor from "../inference-params-editor"; import InferenceParamsEditor from "../inference-params-editor";
interface ModelConfigManagerProps { interface ModelConfigManagerProps {
@ -72,6 +84,7 @@ export function ModelConfigManager({ searchSpaceId }: ModelConfigManagerProps) {
search_space_id: searchSpaceId, search_space_id: searchSpaceId,
}); });
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const [modelComboboxOpen, setModelComboboxOpen] = useState(false);
// Populate form when editing // Populate form when editing
useEffect(() => { useEffect(() => {
@ -533,51 +546,91 @@ export function ModelConfigManager({ searchSpaceId }: ModelConfigManagerProps) {
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="model_name">Model Name *</Label> <Label htmlFor="model_name">Model Name *</Label>
{availableModels.length > 0 ? ( <Popover open={modelComboboxOpen} onOpenChange={setModelComboboxOpen}>
<> <PopoverTrigger asChild>
<Select <Button
variant="outline"
role="combobox"
aria-expanded={modelComboboxOpen}
className="w-full justify-between font-normal"
>
<span className={cn(!formData.model_name && "text-muted-foreground")}>
{formData.model_name || "Select or type model name..."}
</span>
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-full p-0" align="start" side="bottom">
<Command shouldFilter={false}>
<CommandInput
placeholder={selectedProvider?.example || "Type model name..."}
value={formData.model_name} value={formData.model_name}
onValueChange={(value) => handleInputChange("model_name", value)} onValueChange={(value) => handleInputChange("model_name", value)}
/>
<CommandList>
<CommandEmpty>
<div className="py-2 text-center text-sm text-muted-foreground">
{formData.model_name
? `Using custom model: "${formData.model_name}"`
: "Type your model name above"}
</div>
</CommandEmpty>
{availableModels.length > 0 && (
<CommandGroup heading="Suggested Models">
{availableModels
.filter(
(model) =>
!formData.model_name ||
model.value
.toLowerCase()
.includes(formData.model_name.toLowerCase()) ||
model.label
.toLowerCase()
.includes(formData.model_name.toLowerCase())
)
.map((model) => (
<CommandItem
key={model.value}
value={model.value}
onSelect={(currentValue) => {
handleInputChange("model_name", currentValue);
setModelComboboxOpen(false);
}}
className="flex flex-col items-start py-3"
> >
<SelectTrigger> <div className="flex w-full items-center">
<SelectValue placeholder="Select a model" /> <Check
</SelectTrigger> className={cn(
<SelectContent className="max-h-[400px]"> "mr-2 h-4 w-4 shrink-0",
{availableModels.map((model) => ( formData.model_name === model.value
<SelectItem key={model.value} value={model.value}> ? "opacity-100"
<div className="flex flex-col py-1"> : "opacity-0"
<span className="font-medium">{model.label}</span> )}
/>
<div className="flex-1">
<div className="font-medium">{model.label}</div>
{model.contextWindow && ( {model.contextWindow && (
<span className="text-xs text-muted-foreground"> <div className="text-xs text-muted-foreground">
Context: {model.contextWindow} Context: {model.contextWindow}
</span> </div>
)} )}
</div> </div>
</SelectItem> </div>
</CommandItem>
))} ))}
</SelectContent> </CommandGroup>
</Select>
<p className="text-xs text-muted-foreground">
{availableModels.length} model{availableModels.length !== 1 ? "s" : ""}{" "}
available
</p>
</>
) : (
<>
<Input
id="model_name"
placeholder={selectedProvider?.example || "e.g., gpt-4"}
value={formData.model_name}
onChange={(e) => handleInputChange("model_name", e.target.value)}
required
/>
{selectedProvider && (
<p className="text-xs text-muted-foreground">
Examples: {selectedProvider.example}
</p>
)}
</>
)} )}
</CommandList>
</Command>
</PopoverContent>
</Popover>
<p className="text-xs text-muted-foreground">
{availableModels.length > 0
? `Type freely or select from ${availableModels.length} model suggestions`
: selectedProvider?.example
? `Examples: ${selectedProvider.example}`
: "Type your model name freely"}
</p>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">