feat: finished new UI thread setting pannel

This commit is contained in:
Faisal Amir 2024-01-01 15:08:17 +07:00
parent 2332c4e1d2
commit 590fa67244
10 changed files with 216 additions and 105 deletions

View File

@ -8,6 +8,8 @@ const TooltipProvider = TooltipPrimitive.Provider
const Tooltip = TooltipPrimitive.Root const Tooltip = TooltipPrimitive.Root
const TooltipPortal = TooltipPrimitive.Portal
const TooltipTrigger = TooltipPrimitive.Trigger const TooltipTrigger = TooltipPrimitive.Trigger
const TooltipContent = React.forwardRef< const TooltipContent = React.forwardRef<
@ -37,4 +39,5 @@ export {
TooltipContent, TooltipContent,
TooltipProvider, TooltipProvider,
TooltipArrow, TooltipArrow,
TooltipPortal,
} }

View File

@ -13,14 +13,16 @@ import { useClickOutside } from '@/hooks/useClickOutside'
interface Props { interface Props {
children: ReactNode children: ReactNode
title: string title: string
onRevealInFinderClick: (type: string) => void onRevealInFinderClick?: (type: string) => void
onViewJsonClick: (type: string) => void onViewJsonClick?: (type: string) => void
asChild?: boolean
} }
export default function CardSidebar({ export default function CardSidebar({
children, children,
title, title,
onRevealInFinderClick, onRevealInFinderClick,
onViewJsonClick, onViewJsonClick,
asChild,
}: Props) { }: Props) {
const [show, setShow] = useState(true) const [show, setShow] = useState(true)
const [more, setMore] = useState(false) const [more, setMore] = useState(false)
@ -39,7 +41,8 @@ export default function CardSidebar({
return ( return (
<div <div
className={twMerge( className={twMerge(
'flex w-full flex-col overflow-hidden border-t border-border bg-zinc-200 dark:bg-zinc-600/10' 'flex w-full flex-col border-t border-border bg-zinc-100 dark:bg-zinc-600/10',
asChild ? 'rounded-lg border' : 'border-t'
)} )}
> >
<div <div
@ -50,16 +53,18 @@ export default function CardSidebar({
> >
<span className="font-bold">{title}</span> <span className="font-bold">{title}</span>
<div className="flex"> <div className="flex">
{!asChild && (
<div <div
ref={setToggle} ref={setToggle}
className="cursor-pointer rounded-md bg-zinc-200 p-2 pr-0 dark:bg-zinc-600/10" className="cursor-pointer rounded-lg bg-zinc-100 p-2 pr-0 dark:bg-zinc-600/10"
onClick={() => setMore(!more)} onClick={() => setMore(!more)}
> >
<MoreVerticalIcon className="h-5 w-5" /> <MoreVerticalIcon className="h-5 w-5" />
</div> </div>
)}
<button <button
onClick={() => setShow(!show)} onClick={() => setShow(!show)}
className="flex w-full flex-1 items-center space-x-2 bg-zinc-200 px-3 py-2 dark:bg-zinc-600/10" className="flex w-full flex-1 items-center space-x-2 rounded-lg bg-zinc-100 px-3 py-2 dark:bg-zinc-600/10"
> >
<ChevronDownIcon <ChevronDownIcon
className={twMerge( className={twMerge(
@ -72,38 +77,76 @@ export default function CardSidebar({
{more && ( {more && (
<div <div
className="absolute right-0 top-8 z-20 w-52 overflow-hidden rounded-lg border border-border bg-background shadow-lg" className="absolute right-4 top-8 z-20 w-64 rounded-lg border border-border bg-background shadow-lg"
ref={setMenu} ref={setMenu}
> >
<div <div
className="flex cursor-pointer items-center space-x-2 px-4 py-2 hover:bg-secondary" className={twMerge(
'flex cursor-pointer space-x-2 px-4 py-2 hover:bg-secondary',
title === 'Model' ? 'items-start' : 'items-center'
)}
onClick={() => { onClick={() => {
onRevealInFinderClick(title) onRevealInFinderClick && onRevealInFinderClick(title)
setMore(false) setMore(false)
}} }}
> >
<FolderOpenIcon size={16} className="text-muted-foreground" /> <FolderOpenIcon
size={16}
className={twMerge(
'flex-shrink-0 text-muted-foreground',
title === 'Model' && 'mt-1'
)}
/>
<>
{title === 'Model' ? (
<div className="flex flex-col">
<span className="font-medium text-black dark:text-muted-foreground">
{openFolderTitle}
</span>
<span className="mt-1 text-muted-foreground">
Opens thread.json. Changes affect this thread only.
</span>
</div>
) : (
<span className="text-bold text-black dark:text-muted-foreground"> <span className="text-bold text-black dark:text-muted-foreground">
{openFolderTitle} {openFolderTitle}
</span> </span>
)}
</>
</div> </div>
<div <div
className="flex cursor-pointer items-center space-x-2 px-4 py-2 hover:bg-secondary" className="flex cursor-pointer items-start space-x-2 px-4 py-2 hover:bg-secondary"
onClick={() => { onClick={() => {
onViewJsonClick(title) onViewJsonClick && onViewJsonClick(title)
setMore(false) setMore(false)
}} }}
> >
<Code2Icon size={16} className="text-muted-foreground" /> <Code2Icon
<span className="text-bold text-black dark:text-muted-foreground"> size={16}
className="mt-0.5 flex-shrink-0 text-muted-foreground"
/>
<>
<div className="flex flex-col">
<span className="font-medium text-black dark:text-muted-foreground">
View as JSON View as JSON
</span> </span>
<span className="mt-1 text-muted-foreground">
Opens <span className="lowercase">{title}.json.</span>&nbsp;
Changes affect all new threads.
</span>
</div>
</>
</div> </div>
</div> </div>
)} )}
</div> </div>
{show && ( {show && (
<div className="flex flex-col gap-2 bg-white p-2 dark:bg-background"> <div
className={twMerge(
'flex flex-col gap-2 bg-white p-2 dark:bg-background',
asChild && 'rounded-b-lg'
)}
>
{children} {children}
</div> </div>
)} )}

View File

@ -1,9 +1,18 @@
import React from 'react' import React from 'react'
import { Switch } from '@janhq/uikit' import {
Switch,
Tooltip,
TooltipArrow,
TooltipContent,
TooltipPortal,
TooltipTrigger,
} from '@janhq/uikit'
import { useAtomValue } from 'jotai' import { useAtomValue } from 'jotai'
import { InfoIcon } from 'lucide-react'
import useUpdateModelParameters from '@/hooks/useUpdateModelParameters' import useUpdateModelParameters from '@/hooks/useUpdateModelParameters'
import { getActiveThreadIdAtom } from '@/helpers/atoms/Thread.atom' import { getActiveThreadIdAtom } from '@/helpers/atoms/Thread.atom'
@ -11,10 +20,11 @@ import { getActiveThreadIdAtom } from '@/helpers/atoms/Thread.atom'
type Props = { type Props = {
name: string name: string
title: string title: string
description: string
checked: boolean checked: boolean
} }
const Checkbox: React.FC<Props> = ({ name, title, checked }) => { const Checkbox: React.FC<Props> = ({ name, title, checked, description }) => {
const { updateModelParameter } = useUpdateModelParameters() const { updateModelParameter } = useUpdateModelParameters()
const threadId = useAtomValue(getActiveThreadIdAtom) const threadId = useAtomValue(getActiveThreadIdAtom)
@ -26,7 +36,21 @@ const Checkbox: React.FC<Props> = ({ name, title, checked }) => {
return ( return (
<div className="flex justify-between"> <div className="flex justify-between">
<p className="mb-2 text-sm font-semibold text-gray-600">{title}</p> <div className="mb-1 flex items-center gap-x-2">
<p className="text-sm font-semibold text-gray-600">{title}</p>
<Tooltip>
<TooltipTrigger asChild>
<InfoIcon size={16} className="flex-shrink-0" />
</TooltipTrigger>
<TooltipPortal>
<TooltipContent side="top" className="max-w-[240px]">
<span>{description}</span>
<TooltipArrow />
</TooltipContent>
</TooltipPortal>
</Tooltip>
</div>
<Switch checked={checked} onCheckedChange={onCheckedChange} /> <Switch checked={checked} onCheckedChange={onCheckedChange} />
</div> </div>
) )

View File

@ -177,7 +177,7 @@ const TopBar = () => {
> >
<Code2Icon <Code2Icon
size={16} size={16}
className="mt-1 flex-shrink-0 text-muted-foreground" className="mt-0.5 flex-shrink-0 text-muted-foreground"
/> />
<div className="flex flex-col"> <div className="flex flex-col">
<span className="font-medium text-black dark:text-muted-foreground"> <span className="font-medium text-black dark:text-muted-foreground">

View File

@ -1,7 +1,16 @@
import { Textarea } from '@janhq/uikit' import {
Textarea,
Tooltip,
TooltipPortal,
TooltipArrow,
TooltipContent,
TooltipTrigger,
} from '@janhq/uikit'
import { useAtomValue } from 'jotai' import { useAtomValue } from 'jotai'
import { InfoIcon } from 'lucide-react'
import useUpdateModelParameters from '@/hooks/useUpdateModelParameters' import useUpdateModelParameters from '@/hooks/useUpdateModelParameters'
import { getActiveThreadIdAtom } from '@/helpers/atoms/Thread.atom' import { getActiveThreadIdAtom } from '@/helpers/atoms/Thread.atom'
@ -9,6 +18,7 @@ import { getActiveThreadIdAtom } from '@/helpers/atoms/Thread.atom'
type Props = { type Props = {
title: string title: string
name: string name: string
description: string
placeholder: string placeholder: string
value: string value: string
} }
@ -17,6 +27,7 @@ const ModelConfigInput: React.FC<Props> = ({
title, title,
name, name,
value, value,
description,
placeholder, placeholder,
}) => { }) => {
const { updateModelParameter } = useUpdateModelParameters() const { updateModelParameter } = useUpdateModelParameters()
@ -30,7 +41,20 @@ const ModelConfigInput: React.FC<Props> = ({
return ( return (
<div className="flex flex-col"> <div className="flex flex-col">
<p className="mb-2 text-sm font-semibold text-gray-600">{title}</p> <div className="mb-4 flex items-center gap-x-2">
<p className="text-sm font-semibold text-gray-600">{title}</p>
<Tooltip>
<TooltipTrigger asChild>
<InfoIcon size={16} className="flex-shrink-0" />
</TooltipTrigger>
<TooltipPortal>
<TooltipContent side="top" className="max-w-[240px]">
<span>{description}</span>
<TooltipArrow />
</TooltipContent>
</TooltipPortal>
</Tooltip>
</div>
<Textarea <Textarea
placeholder={placeholder} placeholder={placeholder}
onChange={onValueChanged} onChange={onValueChanged}

View File

@ -1,7 +1,14 @@
import React from 'react' import React from 'react'
import { Slider, Input } from '@janhq/uikit' import { Slider, Input, TooltipPortal } from '@janhq/uikit'
import {
Tooltip,
TooltipContent,
TooltipTrigger,
TooltipArrow,
} from '@janhq/uikit'
import { useAtomValue } from 'jotai' import { useAtomValue } from 'jotai'
import { InfoIcon } from 'lucide-react'
import useUpdateModelParameters from '@/hooks/useUpdateModelParameters' import useUpdateModelParameters from '@/hooks/useUpdateModelParameters'
@ -9,6 +16,7 @@ import { getActiveThreadIdAtom } from '@/helpers/atoms/Thread.atom'
type Props = { type Props = {
name: string name: string
description: string
title: string title: string
min: number min: number
max: number max: number
@ -21,6 +29,7 @@ const SliderRightPanel: React.FC<Props> = ({
title, title,
min, min,
max, max,
description,
step, step,
value, value,
}) => { }) => {
@ -29,13 +38,25 @@ const SliderRightPanel: React.FC<Props> = ({
const onValueChanged = (e: number[]) => { const onValueChanged = (e: number[]) => {
if (!threadId) return if (!threadId) return
updateModelParameter(threadId, name, e[0]) updateModelParameter(threadId, name, e[0])
} }
return ( return (
<div className="flex flex-col"> <div className="flex flex-col">
<p className="mb-2 text-sm font-semibold text-gray-600">{title}</p> <div className="mb-4 flex items-center gap-x-2">
<p className="text-sm font-semibold text-gray-600">{title}</p>
<Tooltip>
<TooltipTrigger asChild>
<InfoIcon size={16} className="flex-shrink-0" />
</TooltipTrigger>
<TooltipPortal>
<TooltipContent side="top" className="max-w-[240px]">
<span>{description}</span>
<TooltipArrow />
</TooltipContent>
</TooltipPortal>
</Tooltip>
</div>
<div className="flex items-center gap-x-4"> <div className="flex items-center gap-x-4">
<div className="relative w-full"> <div className="relative w-full">
<Slider <Slider

View File

@ -4,7 +4,7 @@ export const presetConfiguration: Record<string, SettingComponentData> = {
prompt_template: { prompt_template: {
name: 'prompt_template', name: 'prompt_template',
title: 'Prompt template', title: 'Prompt template',
description: 'Prompt template', description: 'The prompt to use for internal configuration.',
controllerType: 'input', controllerType: 'input',
controllerData: { controllerData: {
placeholder: 'Prompt template', placeholder: 'Prompt template',
@ -14,7 +14,8 @@ export const presetConfiguration: Record<string, SettingComponentData> = {
stop: { stop: {
name: 'stop', name: 'stop',
title: 'Stop', title: 'Stop',
description: 'Stop', description:
'Defines specific tokens or phrases at which the model will stop generating further output. ',
controllerType: 'input', controllerType: 'input',
controllerData: { controllerData: {
placeholder: 'Stop', placeholder: 'Stop',
@ -24,7 +25,8 @@ export const presetConfiguration: Record<string, SettingComponentData> = {
ctx_len: { ctx_len: {
name: 'ctx_len', name: 'ctx_len',
title: 'Context Length', title: 'Context Length',
description: 'Context Length', description:
'The context length for model operations varies; the maximum depends on the specific model used.',
controllerType: 'slider', controllerType: 'slider',
controllerData: { controllerData: {
min: 0, min: 0,
@ -40,7 +42,7 @@ export const presetConfiguration: Record<string, SettingComponentData> = {
'The maximum number of tokens the model will generate in a single response.', 'The maximum number of tokens the model will generate in a single response.',
controllerType: 'slider', controllerType: 'slider',
controllerData: { controllerData: {
min: 0, min: 128,
max: 4096, max: 4096,
step: 128, step: 128,
value: 2048, value: 2048,
@ -48,8 +50,8 @@ export const presetConfiguration: Record<string, SettingComponentData> = {
}, },
ngl: { ngl: {
name: 'ngl', name: 'ngl',
title: 'NGL', title: 'Number of GPU layers (ngl)',
description: 'Number of layers in the neural network.', description: 'The number of layers to load onto the GPU for acceleration.',
controllerType: 'slider', controllerType: 'slider',
controllerData: { controllerData: {
min: 1, min: 1,
@ -61,7 +63,7 @@ export const presetConfiguration: Record<string, SettingComponentData> = {
embedding: { embedding: {
name: 'embedding', name: 'embedding',
title: 'Embedding', title: 'Embedding',
description: 'Indicates if embedding layers are used.', description: 'Whether to enable embedding.',
controllerType: 'checkbox', controllerType: 'checkbox',
controllerData: { controllerData: {
checked: true, checked: true,
@ -79,8 +81,7 @@ export const presetConfiguration: Record<string, SettingComponentData> = {
temperature: { temperature: {
name: 'temperature', name: 'temperature',
title: 'Temperature', title: 'Temperature',
description: description: 'Controls the randomness of the models output.',
"Controls randomness in model's responses. Higher values lead to more random responses.",
controllerType: 'slider', controllerType: 'slider',
controllerData: { controllerData: {
min: 0, min: 0,
@ -92,7 +93,8 @@ export const presetConfiguration: Record<string, SettingComponentData> = {
frequency_penalty: { frequency_penalty: {
name: 'frequency_penalty', name: 'frequency_penalty',
title: 'Frequency Penalty', title: 'Frequency Penalty',
description: 'Frequency Penalty', description:
'Adjusts the likelihood of the model repeating words or phrases in its output. ',
controllerType: 'slider', controllerType: 'slider',
controllerData: { controllerData: {
min: 0, min: 0,
@ -104,7 +106,8 @@ export const presetConfiguration: Record<string, SettingComponentData> = {
presence_penalty: { presence_penalty: {
name: 'presence_penalty', name: 'presence_penalty',
title: 'Presence Penalty', title: 'Presence Penalty',
description: 'Presence Penalty', description:
'Influences the generation of new and varied concepts in the models output. ',
controllerType: 'slider', controllerType: 'slider',
controllerData: { controllerData: {
min: 0, min: 0,
@ -116,7 +119,7 @@ export const presetConfiguration: Record<string, SettingComponentData> = {
top_p: { top_p: {
name: 'top_p', name: 'top_p',
title: 'Top P', title: 'Top P',
description: 'Top P', description: 'Set probability threshold for more relevant outputs.',
controllerType: 'slider', controllerType: 'slider',
controllerData: { controllerData: {
min: 0, min: 0,
@ -128,10 +131,11 @@ export const presetConfiguration: Record<string, SettingComponentData> = {
n_parallel: { n_parallel: {
name: 'n_parallel', name: 'n_parallel',
title: 'N Parallel', title: 'N Parallel',
description: 'N Parallel', description:
'The number of parallel operations. Only set when enable continuous batching. ',
controllerType: 'slider', controllerType: 'slider',
controllerData: { controllerData: {
min: 1, min: 0,
max: 4, max: 4,
step: 1, step: 1,
value: 1, value: 1,

View File

@ -38,6 +38,7 @@ const settingComponentBuilder = (componentData: SettingComponentData[]) => {
<Slider <Slider
key={data.name} key={data.name}
title={data.title} title={data.title}
description={data.description}
min={min} min={min}
max={max} max={max}
step={step} step={step}
@ -53,6 +54,7 @@ const settingComponentBuilder = (componentData: SettingComponentData[]) => {
title={data.title} title={data.title}
key={data.name} key={data.name}
name={data.name} name={data.name}
description={data.description}
placeholder={placeholder} placeholder={placeholder}
value={textValue} value={textValue}
/> />
@ -63,6 +65,7 @@ const settingComponentBuilder = (componentData: SettingComponentData[]) => {
<Checkbox <Checkbox
key={data.name} key={data.name}
name={data.name} name={data.name}
description={data.description}
title={data.title} title={data.title}
checked={checked} checked={checked}
/> />

View File

@ -10,6 +10,7 @@ import { twMerge } from 'tailwind-merge'
import LogoMark from '@/containers/Brand/Logo/Mark' import LogoMark from '@/containers/Brand/Logo/Mark'
import CardSidebar from '@/containers/CardSidebar' import CardSidebar from '@/containers/CardSidebar'
import DropdownListSidebar, { import DropdownListSidebar, {
selectedModelAtom, selectedModelAtom,
} from '@/containers/DropdownListSidebar' } from '@/containers/DropdownListSidebar'
@ -127,7 +128,7 @@ const Sidebar: React.FC = () => {
<div> <div>
<label <label
id="thread-title" id="thread-title"
className="mb-2 inline-block font-bold text-gray-600 dark:text-gray-300" className="mb-2 inline-block font-bold text-zinc-500 dark:text-gray-300"
> >
Title Title
</label> </label>
@ -146,7 +147,7 @@ const Sidebar: React.FC = () => {
<div className="flex flex-col"> <div className="flex flex-col">
<label <label
id="thread-title" id="thread-title"
className="mb-2 inline-block font-bold text-gray-600 dark:text-gray-300" className="mb-2 inline-block font-bold text-zinc-500 dark:text-gray-300"
> >
Threads ID Threads ID
</label> </label>
@ -156,46 +157,7 @@ const Sidebar: React.FC = () => {
</div> </div>
</div> </div>
{/* <CardSidebar <CardSidebar
title="Thread"
onRevealInFinderClick={onReviewInFinderClick}
onViewJsonClick={onViewJsonClick}
>
<div className="flex flex-col space-y-4 p-2">
<div>
<label
id="thread-title"
className="mb-2 inline-block font-bold text-gray-600 dark:text-gray-300"
>
Title
</label>
<Input
id="thread-title"
value={activeThread?.title}
onChange={(e) => {
if (activeThread)
updateThreadMetadata({
...activeThread,
title: e.target.value || '',
})
}}
/>
</div>
<div className="flex flex-col">
<label
id="thread-title"
className="mb-2 inline-block font-bold text-gray-600 dark:text-gray-300"
>
Threads ID
</label>
<span className="text-xs text-muted-foreground">
{activeThread?.id || '-'}
</span>
</div>
</div>
</CardSidebar> */}
{/* <CardSidebar
title="Assistant" title="Assistant"
onRevealInFinderClick={onReviewInFinderClick} onRevealInFinderClick={onReviewInFinderClick}
onViewJsonClick={onViewJsonClick} onViewJsonClick={onViewJsonClick}
@ -210,7 +172,7 @@ const Sidebar: React.FC = () => {
<div> <div>
<label <label
id="thread-title" id="thread-title"
className="mb-2 inline-block font-bold text-gray-600 dark:text-gray-300" className="mb-2 inline-block font-bold text-zinc-500 dark:text-gray-300"
> >
Instructions Instructions
</label> </label>
@ -232,34 +194,62 @@ const Sidebar: React.FC = () => {
}} }}
/> />
</div> </div>
{/* <div>
<label
id="tool-title"
className="mb-2 inline-block font-bold text-zinc-500 dark:text-gray-300"
>
Tools
</label>
<div className="flex items-center justify-between">
<label className="font-medium text-zinc-500 dark:text-gray-300">
Retrieval
</label>
<Switch name="retrieval" />
</div> </div>
</CardSidebar> */} </div> */}
</div>
</CardSidebar>
{/* <CardSidebar <CardSidebar
title="Model" title="Model"
onRevealInFinderClick={onReviewInFinderClick} onRevealInFinderClick={onReviewInFinderClick}
onViewJsonClick={onViewJsonClick} onViewJsonClick={onViewJsonClick}
> >
<div className="p-2"> <div className="p-2">
<DropdownListSidebar /> <DropdownListSidebar />
<div className="mt-4">
<div className="mt-6">
<CardSidebar title="Inference Parameters" asChild>
<div className="p-2">
<ModelSetting /> <ModelSetting />
</div> </div>
</CardSidebar>
</div>
<div className="mt-4">
<CardSidebar title="Model Parameters" asChild>
<div className="p-2">{/* <ModelSetting /> */}</div>
</CardSidebar>
</div>
{experimentalFeatureEnabed && {experimentalFeatureEnabed &&
Object.keys(modelSettingParams).length ? ( Object.keys(modelSettingParams).length ? (
<div className="mt-4">
<CardSidebar <CardSidebar
title="Engine" title="Engine Parameters"
onRevealInFinderClick={onReviewInFinderClick} onRevealInFinderClick={onReviewInFinderClick}
onViewJsonClick={onViewJsonClick} onViewJsonClick={onViewJsonClick}
asChild
> >
<div className="p-2"> <div className="p-2">
<EngineSetting /> <EngineSetting />
</div> </div>
</CardSidebar> </CardSidebar>
</div>
) : null} ) : null}
</div> </div>
</CardSidebar> */} </CardSidebar>
</div> </div>
</div> </div>
) )

View File

@ -39,7 +39,6 @@ export const toSettingParams = (
embedding: undefined, embedding: undefined,
n_parallel: undefined, n_parallel: undefined,
cpu_threads: undefined, cpu_threads: undefined,
prompt_template: undefined,
} }
const settingParams: ModelSettingParams = {} const settingParams: ModelSettingParams = {}