feat: add quick access model setting via dropdown model (#5104)
* feat: add quick access model setting via dropdown model * Update web-app/src/containers/DropdownModelProvider.tsx Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> --------- Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
This commit is contained in:
parent
83464b367f
commit
8afb962739
@ -33,9 +33,9 @@ const DropdownAssistant = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DropdownMenu open={dropdownOpen} onOpenChange={setDropdownOpen}>
|
<DropdownMenu open={dropdownOpen} onOpenChange={setDropdownOpen}>
|
||||||
<div className="flex items-center justify-between gap-1">
|
<div className="flex items-center justify-between gap-2 bg-main-view-fg/5 py-1 hover:bg-main-view-fg/8 px-2 rounded-sm">
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<button className="bg-main-view-fg/5 py-1 hover:bg-main-view-fg/8 px-2 rounded font-medium cursor-pointer flex items-center gap-1.5 relative z-20 max-w-40">
|
<button className="font-medium cursor-pointer flex items-center gap-1.5 relative z-20 max-w-40">
|
||||||
<div className="text-main-view-fg/80 flex items-center gap-1">
|
<div className="text-main-view-fg/80 flex items-center gap-1">
|
||||||
{selectedAssistant?.avatar && (
|
{selectedAssistant?.avatar && (
|
||||||
<span className="shrink-0 w-4 h-4 relative flex items-center justify-center">
|
<span className="shrink-0 w-4 h-4 relative flex items-center justify-center">
|
||||||
@ -75,10 +75,13 @@ const DropdownAssistant = () => {
|
|||||||
align="start"
|
align="start"
|
||||||
>
|
>
|
||||||
{assistants.map((assistant) => (
|
{assistants.map((assistant) => (
|
||||||
<div className="relative pr-6" key={assistant.id}>
|
<div
|
||||||
<DropdownMenuItem asChild>
|
className="relative pr-6 hover:bg-main-view-fg/4 rounded-sm"
|
||||||
|
key={assistant.id}
|
||||||
|
>
|
||||||
|
<DropdownMenuItem className="hover:bg-transparent">
|
||||||
<div
|
<div
|
||||||
className="text-main-view-fg/70 cursor-pointer"
|
className="text-main-view-fg/70 cursor-pointer flex gap-2"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setCurrentAssistant(assistant)
|
setCurrentAssistant(assistant)
|
||||||
updateCurrentThreadAssistant(assistant)
|
updateCurrentThreadAssistant(assistant)
|
||||||
|
|||||||
@ -14,14 +14,20 @@ import { IconSettings } from '@tabler/icons-react'
|
|||||||
import { useNavigate } from '@tanstack/react-router'
|
import { useNavigate } from '@tanstack/react-router'
|
||||||
import { route } from '@/constants/routes'
|
import { route } from '@/constants/routes'
|
||||||
import { useThreads } from '@/hooks/useThreads'
|
import { useThreads } from '@/hooks/useThreads'
|
||||||
|
import { ModelSetting } from '@/containers/ModelSetting'
|
||||||
|
|
||||||
type DropdownModelProviderProps = {
|
type DropdownModelProviderProps = {
|
||||||
model?: ThreadModel
|
model?: ThreadModel
|
||||||
}
|
}
|
||||||
|
|
||||||
const DropdownModelProvider = ({ model }: DropdownModelProviderProps) => {
|
const DropdownModelProvider = ({ model }: DropdownModelProviderProps) => {
|
||||||
const { providers, selectModelProvider, selectedProvider, selectedModel } =
|
const {
|
||||||
useModelProvider()
|
providers,
|
||||||
|
getProviderByName,
|
||||||
|
selectModelProvider,
|
||||||
|
selectedProvider,
|
||||||
|
selectedModel,
|
||||||
|
} = useModelProvider()
|
||||||
const [displayModel, setDisplayModel] = useState<string>('')
|
const [displayModel, setDisplayModel] = useState<string>('')
|
||||||
const { updateCurrentThreadModel } = useThreads()
|
const { updateCurrentThreadModel } = useThreads()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
@ -48,107 +54,123 @@ const DropdownModelProvider = ({ model }: DropdownModelProviderProps) => {
|
|||||||
|
|
||||||
if (!providers.length) return null
|
if (!providers.length) return null
|
||||||
|
|
||||||
|
|
||||||
|
const provider = getProviderByName(selectedProvider)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenu>
|
||||||
<button
|
<div className="bg-main-view-fg/5 hover:bg-main-view-fg/8 px-2 py-1 flex items-center gap-1.5 rounded-sm">
|
||||||
title={displayModel}
|
<DropdownMenuTrigger asChild>
|
||||||
className="bg-main-view-fg/5 hover:bg-main-view-fg/8 px-2 py-1 rounded font-medium cursor-pointer flex items-center gap-1.5 relative z-20 max-w-40"
|
<button
|
||||||
>
|
title={displayModel}
|
||||||
<img
|
className="font-medium cursor-pointer flex items-center gap-1.5 relative z-20 max-w-38"
|
||||||
src={getProviderLogo(selectedProvider as string)}
|
>
|
||||||
alt={`${selectedProvider} - Logo`}
|
<img
|
||||||
className="size-4"
|
src={getProviderLogo(selectedProvider as string)}
|
||||||
/>
|
alt={`${selectedProvider} - Logo`}
|
||||||
<span
|
className="size-4"
|
||||||
className={cn(
|
/>
|
||||||
'text-main-view-fg/80 truncate leading-normal',
|
<span
|
||||||
!selectedModel?.id && 'text-main-view-fg/50'
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{displayModel}
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent
|
|
||||||
className="w-60 max-h-[320px]"
|
|
||||||
side="bottom"
|
|
||||||
align="start"
|
|
||||||
>
|
|
||||||
<DropdownMenuGroup>
|
|
||||||
{providers.map((provider, index) => {
|
|
||||||
// Only show active providers
|
|
||||||
if (!provider.active) return null
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={cn(
|
className={cn(
|
||||||
'bg-main-view-fg/4 first:mt-0 rounded-sm my-1.5 first:mb-0 '
|
'text-main-view-fg/80 truncate leading-normal',
|
||||||
|
!selectedModel?.id && 'text-main-view-fg/50'
|
||||||
)}
|
)}
|
||||||
key={`provider-${index}`}
|
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between">
|
{displayModel}
|
||||||
<DropdownMenuLabel className="flex items-center gap-1.5">
|
</span>
|
||||||
<img
|
</button>
|
||||||
src={getProviderLogo(provider.provider)}
|
</DropdownMenuTrigger>
|
||||||
alt={`${provider.provider} - Logo`}
|
{selectedModel && (
|
||||||
className="size-4"
|
<ModelSetting
|
||||||
/>
|
model={selectedModel as Model}
|
||||||
<span className="capitalize truncate">
|
provider={provider as ProviderObject}
|
||||||
{getProviderTitle(provider.provider)}
|
/>
|
||||||
</span>
|
)}
|
||||||
</DropdownMenuLabel>
|
</div>
|
||||||
<div
|
<DropdownMenuContent
|
||||||
className="size-6 cursor-pointer flex items-center justify-center rounded hover:bg-main-view-fg/10 transition-all duration-200 ease-in-out mr-2"
|
className="w-60 max-h-[320px]"
|
||||||
onClick={() =>
|
side="bottom"
|
||||||
navigate({
|
align="start"
|
||||||
to: route.settings.providers,
|
>
|
||||||
params: { providerName: provider.provider },
|
<DropdownMenuGroup>
|
||||||
})
|
{providers.map((provider, index) => {
|
||||||
}
|
// Only show active providers
|
||||||
>
|
if (!provider.active) return null
|
||||||
<IconSettings size={18} className="text-main-view-fg/50" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{provider.models.map((model, modelIndex) => {
|
return (
|
||||||
const capabilities = model.capabilities || []
|
<div
|
||||||
|
className={cn(
|
||||||
return (
|
'bg-main-view-fg/4 first:mt-0 rounded-sm my-1.5 first:mb-0 '
|
||||||
<DropdownMenuItem
|
)}
|
||||||
className={cn(
|
key={`provider-${index}`}
|
||||||
'h-8 mx-1',
|
>
|
||||||
provider.provider !== 'llama.cpp' &&
|
<div className="flex items-center justify-between">
|
||||||
!provider.api_key?.length &&
|
<DropdownMenuLabel className="flex items-center gap-1.5">
|
||||||
'hidden'
|
<img
|
||||||
)}
|
src={getProviderLogo(provider.provider)}
|
||||||
title={model.id}
|
alt={`${provider.provider} - Logo`}
|
||||||
key={`model-${modelIndex}`}
|
className="size-4"
|
||||||
onClick={() => {
|
/>
|
||||||
selectModelProvider(provider.provider, model.id)
|
<span className="capitalize truncate text-sm">
|
||||||
updateCurrentThreadModel({
|
{getProviderTitle(provider.provider)}
|
||||||
id: model.id,
|
</span>
|
||||||
provider: provider.provider,
|
</DropdownMenuLabel>
|
||||||
|
<div
|
||||||
|
className="size-6 cursor-pointer flex items-center justify-center rounded hover:bg-main-view-fg/10 transition-all duration-200 ease-in-out mr-2"
|
||||||
|
onClick={() =>
|
||||||
|
navigate({
|
||||||
|
to: route.settings.providers,
|
||||||
|
params: { providerName: provider.provider },
|
||||||
})
|
})
|
||||||
}}
|
}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-1.5 w-full">
|
<IconSettings
|
||||||
<span className="truncate text-main-view-fg/70">
|
size={18}
|
||||||
{model.id}
|
className="text-main-view-fg/50"
|
||||||
</span>
|
/>
|
||||||
<div className="-mr-1.5">
|
</div>
|
||||||
<Capabilities capabilities={capabilities} />
|
</div>
|
||||||
|
|
||||||
|
{provider.models.map((model, modelIndex) => {
|
||||||
|
const capabilities = model.capabilities || []
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropdownMenuItem
|
||||||
|
className={cn(
|
||||||
|
'h-8 mx-1',
|
||||||
|
provider.provider !== 'llama.cpp' &&
|
||||||
|
!provider.api_key?.length &&
|
||||||
|
'hidden'
|
||||||
|
)}
|
||||||
|
title={model.id}
|
||||||
|
key={`model-${modelIndex}`}
|
||||||
|
onClick={() => {
|
||||||
|
selectModelProvider(provider.provider, model.id)
|
||||||
|
updateCurrentThreadModel({
|
||||||
|
id: model.id,
|
||||||
|
provider: provider.provider,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-1.5 w-full">
|
||||||
|
<span className="truncate text-main-view-fg/70">
|
||||||
|
{model.id}
|
||||||
|
</span>
|
||||||
|
<div className="-mr-1.5">
|
||||||
|
<Capabilities capabilities={capabilities} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuItem>
|
)
|
||||||
)
|
})}
|
||||||
})}
|
</div>
|
||||||
</div>
|
)
|
||||||
)
|
})}
|
||||||
})}
|
</DropdownMenuGroup>
|
||||||
</DropdownMenuGroup>
|
</DropdownMenuContent>
|
||||||
</DropdownMenuContent>
|
</DropdownMenu>
|
||||||
</DropdownMenu>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -13,12 +13,6 @@ import { useModelProvider } from '@/hooks/useModelProvider'
|
|||||||
import { updateModel } from '@/services/models'
|
import { updateModel } from '@/services/models'
|
||||||
import { ModelSettingParams } from '@janhq/core'
|
import { ModelSettingParams } from '@janhq/core'
|
||||||
|
|
||||||
// import {
|
|
||||||
// HoverCard,
|
|
||||||
// HoverCardContent,
|
|
||||||
// HoverCardTrigger,
|
|
||||||
// } from '@/components/ui/hover-card'
|
|
||||||
|
|
||||||
type ModelSettingProps = {
|
type ModelSettingProps = {
|
||||||
provider: ProviderObject
|
provider: ProviderObject
|
||||||
model: Model
|
model: Model
|
||||||
@ -91,10 +85,10 @@ export function ModelSetting({ model, provider }: ModelSettingProps) {
|
|||||||
const config = value as ProviderSetting
|
const config = value as ProviderSetting
|
||||||
return (
|
return (
|
||||||
<div key={key} className="space-y-2">
|
<div key={key} className="space-y-2">
|
||||||
<div className="flex flex-col">
|
<div className="flex items-start justify-between gap-8">
|
||||||
<div className="space-y-1 mb-2">
|
<div className="space-y-1 mb-2">
|
||||||
<h3 className="font-medium">{config.title}</h3>
|
<h3 className="font-medium">{config.title}</h3>
|
||||||
<p className="text-main-view-fg/60 text-xs">
|
<p className="text-main-view-fg/70 text-xs">
|
||||||
{config.description}
|
{config.description}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -109,38 +103,6 @@ export function ModelSetting({ model, provider }: ModelSettingProps) {
|
|||||||
}}
|
}}
|
||||||
onChange={(newValue) => handleSettingChange(key, newValue)}
|
onChange={(newValue) => handleSettingChange(key, newValue)}
|
||||||
/>
|
/>
|
||||||
{/* <div className="mt-2">
|
|
||||||
<HoverCard openDelay={200}>
|
|
||||||
<HoverCardTrigger asChild>
|
|
||||||
<div>
|
|
||||||
<div className="flex items-center justify-between mb-2">
|
|
||||||
<label htmlFor={config.key}>{config.title}</label>
|
|
||||||
</div>
|
|
||||||
<DynamicControllerSetting
|
|
||||||
key={config.key}
|
|
||||||
title={config.title}
|
|
||||||
description={config.description}
|
|
||||||
controllerType={config.controller_type}
|
|
||||||
controllerProps={{
|
|
||||||
...config.controller_props,
|
|
||||||
value: value,
|
|
||||||
}}
|
|
||||||
onChange={(newValue) =>
|
|
||||||
handleSettingChange(key, newValue)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</HoverCardTrigger>
|
|
||||||
<HoverCardContent
|
|
||||||
align="start"
|
|
||||||
className="w-[260px] text-sm"
|
|
||||||
side="left"
|
|
||||||
sideOffset={24}
|
|
||||||
>
|
|
||||||
{config.description}
|
|
||||||
</HoverCardContent>
|
|
||||||
</HoverCard>
|
|
||||||
</div> */}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -163,7 +163,7 @@ function Hub() {
|
|||||||
<div className="flex flex-col h-full w-full">
|
<div className="flex flex-col h-full w-full">
|
||||||
<HeaderPage>
|
<HeaderPage>
|
||||||
<div className="pr-4 py-3 border-b border-main-view-fg/5 h-10 w-full flex items-center justify-between relative z-20 ">
|
<div className="pr-4 py-3 border-b border-main-view-fg/5 h-10 w-full flex items-center justify-between relative z-20 ">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2 w-full pr-4">
|
||||||
<IconSearch className="text-main-view-fg/60" size={14} />
|
<IconSearch className="text-main-view-fg/60" size={14} />
|
||||||
<input
|
<input
|
||||||
placeholder="Search models..."
|
placeholder="Search models..."
|
||||||
|
|||||||
@ -300,7 +300,7 @@ function ProviderDetail() {
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
actions={
|
actions={
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-1">
|
||||||
<DialogEditModel
|
<DialogEditModel
|
||||||
provider={provider}
|
provider={provider}
|
||||||
modelId={model.id}
|
modelId={model.id}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user