Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions src/utils/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,23 @@ const getUseModel = async (
return Router!.default;
};

const setReqModel = (req: any, model: string) => {
// If the model is in format "{provider},default", use the original request model
if (model && model.includes(",")) {
const [provider, modelName] = model.split(",");
if (modelName.toLowerCase() === "default") {
// Extract the original model from request, keep provider but use original model name
const originalModel = req.body.model;
req.log.info(`Using default model placeholder - keeping original model: ${originalModel} with provider: ${provider}`);
req.body.model = `${provider},${originalModel}`;
} else {
req.body.model = model;
}
} else {
req.body.model = model;
}
}

export const router = async (req: any, _res: any, context: any) => {
const { config, event } = context;
// Parse sessionId from metadata.user_id
Expand Down Expand Up @@ -221,10 +238,11 @@ export const router = async (req: any, _res: any, context: any) => {
if (!model) {
model = await getUseModel(req, tokenCount, config, lastMessageUsage);
}
req.body.model = model;
setReqModel(req, model);
} catch (error: any) {
req.log.error(`Error in router middleware: ${error.message}`);
req.body.model = config.Router!.default;
// Fallback to default model in case of error
setReqModel(req, config.Router!.default);
}
return;
};
Expand Down
15 changes: 9 additions & 6 deletions ui/src/components/Router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,21 +47,24 @@ export function Router() {

// Handle case where config.Providers might be null or undefined
const providers = Array.isArray(config.Providers) ? config.Providers : [];


// Create model options with provider grouping and add "default" option for each provider
const modelOptions = providers.flatMap((provider) => {
// Handle case where individual provider might be null or undefined
if (!provider) return [];

// Handle case where provider.models might be null or undefined
const models = Array.isArray(provider.models) ? provider.models : [];

// Handle case where provider.name might be null or undefined
const providerName = provider.name || "Unknown Provider";

return models.map((model) => ({
value: `${providerName},${model || "Unknown Model"}`,
label: `${providerName}, ${model || "Unknown Model"}`,
}));
label: `${model || "Unknown Model"}`,
displayLabel: `${providerName}, ${model || "Unknown Model"}`,
group: providerName,
}))
});

return (
Expand Down
84 changes: 59 additions & 25 deletions ui/src/components/ui/combobox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ import {
} from "@/components/ui/popover"

interface ComboboxProps {
options: { label: string; value: string }[];
options: {
label: string;
value: string;
group?: string;
displayLabel?: string;
}[];
value?: string;
onChange: (value: string) => void;
placeholder?: string;
Expand All @@ -40,6 +45,31 @@ export function Combobox({

const selectedOption = options.find((option) => option.value === value)

// Check if any options have groups
const hasGroups = React.useMemo(() => {
return options.some((option) => option.group !== undefined);
}, [options])

// Group options by their group property if groups exist
const groupedOptions = React.useMemo(() => {
if (!hasGroups) {
// If no groups, return all options in a single unnamed group
return [[undefined, options] as [undefined, typeof options]]
}

const groups = new Map<string, typeof options>()

options.forEach((option) => {
const groupName = option.group || "Other";
if (!groups.has(groupName)) {
groups.set(groupName, [])
}
groups.get(groupName)?.push(option)
});

return Array.from(groups.entries());
}, [options, hasGroups])

return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
Expand All @@ -49,7 +79,9 @@ export function Combobox({
aria-expanded={open}
className="w-full justify-between transition-all-ease hover:scale-[1.02] active:scale-[0.98]"
>
{selectedOption ? selectedOption.label : placeholder}
{selectedOption
? selectedOption.displayLabel || selectedOption.label
: placeholder}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50 transition-transform duration-200 group-data-[state=open]:rotate-180" />
</Button>
</PopoverTrigger>
Expand All @@ -58,30 +90,32 @@ export function Combobox({
<CommandInput placeholder={searchPlaceholder} />
<CommandList>
<CommandEmpty>{emptyPlaceholder}</CommandEmpty>
<CommandGroup>
{options.map((option) => (
<CommandItem
key={option.value}
value={option.value}
onSelect={(currentValue) => {
onChange(currentValue === value ? "" : currentValue)
setOpen(false)
}}
className="transition-all-ease hover:bg-accent hover:text-accent-foreground"
>
<Check
className={cn(
"mr-2 h-4 w-4 transition-opacity",
value === option.value ? "opacity-100" : "opacity-0"
)}
/>
{option.label}
</CommandItem>
))}
</CommandGroup>
{groupedOptions.map(([groupName, groupOptions]) => (
<CommandGroup key={groupName || "default"} heading={groupName}>
{groupOptions.map((option) => (
<CommandItem
key={option.value}
value={option.value}
onSelect={(currentValue) => {
onChange(currentValue === value ? "" : currentValue)
setOpen(false)
}}
className="transition-all-ease hover:bg-accent hover:text-accent-foreground"
>
<Check
className={cn(
"mr-2 h-4 w-4 transition-opacity",
value === option.value ? "opacity-100" : "opacity-0"
)}
/>
{option.label}
</CommandItem>
))}
</CommandGroup>
))}
</CommandList>
</Command>
</PopoverContent>
</Popover>
)
}
);
}