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
value={formData.model_name} variant="outline"
onValueChange={(value) => handleInputChange("model_name", value)} role="combobox"
aria-expanded={modelComboboxOpen}
className="w-full justify-between font-normal"
> >
<SelectTrigger> <span className={cn(!formData.model_name && "text-muted-foreground")}>
<SelectValue placeholder="Select a model" /> {formData.model_name || "Select or type model name..."}
</SelectTrigger> </span>
<SelectContent className="max-h-[400px]"> <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
{availableModels.map((model) => ( </Button>
<SelectItem key={model.value} value={model.value}> </PopoverTrigger>
<div className="flex flex-col py-1"> <PopoverContent className="w-full p-0" align="start" side="bottom">
<span className="font-medium">{model.label}</span> <Command shouldFilter={false}>
{model.contextWindow && ( <CommandInput
<span className="text-xs text-muted-foreground"> placeholder={selectedProvider?.example || "Type model name..."}
Context: {model.contextWindow} value={formData.model_name}
</span> onValueChange={(value) => handleInputChange("model_name", value)}
)} />
</div> <CommandList>
</SelectItem> <CommandEmpty>
))} <div className="py-2 text-center text-sm text-muted-foreground">
</SelectContent> {formData.model_name
</Select> ? `Using custom model: "${formData.model_name}"`
<p className="text-xs text-muted-foreground"> : "Type your model name above"}
{availableModels.length} model{availableModels.length !== 1 ? "s" : ""}{" "} </div>
available </CommandEmpty>
</p> {availableModels.length > 0 && (
</> <CommandGroup heading="Suggested Models">
) : ( {availableModels
<> .filter(
<Input (model) =>
id="model_name" !formData.model_name ||
placeholder={selectedProvider?.example || "e.g., gpt-4"} model.value
value={formData.model_name} .toLowerCase()
onChange={(e) => handleInputChange("model_name", e.target.value)} .includes(formData.model_name.toLowerCase()) ||
required model.label
/> .toLowerCase()
{selectedProvider && ( .includes(formData.model_name.toLowerCase())
<p className="text-xs text-muted-foreground"> )
Examples: {selectedProvider.example} .map((model) => (
</p> <CommandItem
)} key={model.value}
</> value={model.value}
)} onSelect={(currentValue) => {
handleInputChange("model_name", currentValue);
setModelComboboxOpen(false);
}}
className="flex flex-col items-start py-3"
>
<div className="flex w-full items-center">
<Check
className={cn(
"mr-2 h-4 w-4 shrink-0",
formData.model_name === model.value
? "opacity-100"
: "opacity-0"
)}
/>
<div className="flex-1">
<div className="font-medium">{model.label}</div>
{model.contextWindow && (
<div className="text-xs text-muted-foreground">
Context: {model.contextWindow}
</div>
)}
</div>
</div>
</CommandItem>
))}
</CommandGroup>
)}
</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">