Faisal Amir e8e5c8c5f4
fix: disabled native resize textarea when textarea autogrowing (#4326)
* fix: disabled native resize textarea when textarea autogrowing

* fix: disabled auto resize on instruction field
2024-12-23 20:39:22 +07:00

313 lines
9.6 KiB
TypeScript

import { memo, useCallback, useMemo } from 'react'
import {
InferenceEngine,
SettingComponentProps,
SliderComponentProps,
extractInferenceParams,
extractModelLoadParams,
} from '@janhq/core'
import {
Tabs,
TabsContent,
TextArea,
Accordion,
AccordionItem,
} from '@janhq/joi'
import { useAtom, useAtomValue, useSetAtom } from 'jotai'
import { useDebouncedCallback } from 'use-debounce'
import CopyOverInstruction from '@/containers/CopyInstruction'
import EngineSetting from '@/containers/EngineSetting'
import ModelDropdown from '@/containers/ModelDropdown'
import ModelSetting from '@/containers/ModelSetting'
import RightPanelContainer from '@/containers/RightPanelContainer'
import { useActiveModel } from '@/hooks/useActiveModel'
import { useCreateNewThread } from '@/hooks/useCreateNewThread'
import useUpdateModelParameters from '@/hooks/useUpdateModelParameters'
import { getConfigurationsData } from '@/utils/componentSettings'
import { isLocalEngine } from '@/utils/modelEngine'
import PromptTemplateSetting from './PromptTemplateSetting'
import Tools from './Tools'
import { experimentalFeatureEnabledAtom } from '@/helpers/atoms/AppConfig.atom'
import { activeAssistantAtom } from '@/helpers/atoms/Assistant.atom'
import { selectedModelAtom } from '@/helpers/atoms/Model.atom'
import {
activeThreadAtom,
engineParamsUpdateAtom,
getActiveThreadModelParamsAtom,
} from '@/helpers/atoms/Thread.atom'
import { activeTabThreadRightPanelAtom } from '@/helpers/atoms/ThreadRightPanel.atom'
const INFERENCE_SETTINGS = 'Inference Settings'
const MODEL_SETTINGS = 'Model Settings'
const ENGINE_SETTINGS = 'Engine Settings'
const ThreadRightPanel = () => {
const activeThread = useAtomValue(activeThreadAtom)
const activeAssistant = useAtomValue(activeAssistantAtom)
const activeModelParams = useAtomValue(getActiveThreadModelParamsAtom)
const selectedModel = useAtomValue(selectedModelAtom)
const [activeTabThreadRightPanel, setActiveTabThreadRightPanel] = useAtom(
activeTabThreadRightPanelAtom
)
const { updateThreadMetadata } = useCreateNewThread()
const experimentalFeature = useAtomValue(experimentalFeatureEnabledAtom)
const isModelSupportRagAndTools =
selectedModel?.engine === InferenceEngine.openai ||
isLocalEngine(selectedModel?.engine as InferenceEngine)
const setEngineParamsUpdate = useSetAtom(engineParamsUpdateAtom)
const { stopModel } = useActiveModel()
const { updateModelParameter } = useUpdateModelParameters()
const settings = useMemo(() => {
// runtime setting
const modelRuntimeParams = extractInferenceParams(
{
...selectedModel?.parameters,
...activeModelParams,
},
selectedModel?.parameters
)
const componentDataRuntimeSetting = getConfigurationsData(
modelRuntimeParams,
selectedModel
).filter((x) => x.key !== 'prompt_template')
// engine setting
const modelEngineParams = extractModelLoadParams(
{
...selectedModel?.settings,
...activeModelParams,
},
selectedModel?.settings
)
const componentDataEngineSetting = getConfigurationsData(
modelEngineParams,
selectedModel
).filter((x) => x.key !== 'prompt_template' && x.key !== 'embedding')
// the max value of max token has to follow context length
const maxTokens = componentDataRuntimeSetting.find(
(x) => x.key === 'max_tokens'
)
const contextLength = componentDataEngineSetting.find(
(x) => x.key === 'ctx_len'
)
if (maxTokens && contextLength) {
// replace maxToken to componentDataRuntimeSetting
const updatedComponentDataRuntimeSetting: SettingComponentProps[] =
componentDataRuntimeSetting.map((settingComponentProps) => {
if (settingComponentProps.key !== 'max_tokens')
return settingComponentProps
const contextLengthValue = Number(contextLength.controllerProps.value)
const maxTokenValue = Number(
settingComponentProps.controllerProps.value
)
const controllerProps =
settingComponentProps.controllerProps as SliderComponentProps
const sliderProps: SliderComponentProps = {
...controllerProps,
max: contextLengthValue,
value: Math.min(maxTokenValue, contextLengthValue),
}
const updatedSettingProps: SettingComponentProps = {
...settingComponentProps,
controllerProps: sliderProps,
}
return updatedSettingProps
})
return {
runtimeSettings: updatedComponentDataRuntimeSetting,
engineSettings: componentDataEngineSetting,
}
}
return {
runtimeSettings: componentDataRuntimeSetting,
engineSettings: componentDataEngineSetting,
}
}, [activeModelParams, selectedModel])
const promptTemplateSettings = useMemo(() => {
const modelEngineParams = extractModelLoadParams({
...selectedModel?.settings,
...activeModelParams,
})
const componentDataEngineSetting = getConfigurationsData(
modelEngineParams,
selectedModel
)
return componentDataEngineSetting.filter((x) => x.key === 'prompt_template')
}, [activeModelParams, selectedModel])
const onAssistantInstructionChanged = useCallback(
(e: React.ChangeEvent<HTMLTextAreaElement>) => {
if (activeThread && activeAssistant)
updateThreadMetadata({
...activeThread,
assistants: [
{
...activeAssistant,
instructions: e.target.value || '',
},
],
})
},
[activeAssistant, activeThread, updateThreadMetadata]
)
const resetModel = useDebouncedCallback(() => {
stopModel()
}, 300)
const onValueChanged = useCallback(
(key: string, value: string | number | boolean | string[]) => {
if (!activeThread || !activeAssistant) return
setEngineParamsUpdate(true)
resetModel()
updateModelParameter(activeThread, {
params: { [key]: value },
})
if (
activeAssistant.model.parameters?.max_tokens &&
activeAssistant.model.settings?.ctx_len
) {
if (
key === 'max_tokens' &&
Number(value) > activeAssistant.model.settings.ctx_len
) {
updateModelParameter(activeThread, {
params: {
max_tokens: activeAssistant.model.settings.ctx_len,
},
})
}
if (
key === 'ctx_len' &&
Number(value) < activeAssistant.model.parameters.max_tokens
) {
updateModelParameter(activeThread, {
params: {
max_tokens: activeAssistant.model.settings.ctx_len,
},
})
}
}
},
[
activeAssistant,
activeThread,
resetModel,
setEngineParamsUpdate,
updateModelParameter,
]
)
if (!activeThread) {
return null
}
return (
<RightPanelContainer>
<Tabs
options={[
{ name: 'Assistant', value: 'assistant' },
{ name: 'Model', value: 'model' },
...(experimentalFeature
? [
{
name: 'Tools',
value: 'tools',
disabled: !isModelSupportRagAndTools,
tooltipContent: 'Not supported for this model',
},
]
: []),
]}
value={activeTabThreadRightPanel as string}
onValueChange={(value) => setActiveTabThreadRightPanel(value)}
>
<TabsContent value="assistant">
<div className="flex flex-col space-y-4 p-4">
<div>
<label
id="assistant-instructions"
className="mb-2 inline-block font-bold"
>
Instructions
</label>
<TextArea
id="assistant-instructions"
placeholder="Eg. You are a helpful assistant."
value={activeAssistant?.instructions ?? ''}
// autoResize
onChange={onAssistantInstructionChanged}
/>
</div>
<CopyOverInstruction />
</div>
</TabsContent>
<TabsContent value="model">
<div className="flex flex-col gap-4 px-2 py-4">
<ModelDropdown />
</div>
{selectedModel && (
<Accordion defaultValue={[]}>
{settings.runtimeSettings.length !== 0 && (
<AccordionItem
title={INFERENCE_SETTINGS}
value={INFERENCE_SETTINGS}
>
<ModelSetting
componentProps={settings.runtimeSettings}
onValueChanged={onValueChanged}
/>
</AccordionItem>
)}
{promptTemplateSettings.length !== 0 && (
<AccordionItem title={MODEL_SETTINGS} value={MODEL_SETTINGS}>
<PromptTemplateSetting
componentData={promptTemplateSettings}
/>
</AccordionItem>
)}
{settings.engineSettings.length !== 0 && (
<AccordionItem title={ENGINE_SETTINGS} value={ENGINE_SETTINGS}>
<EngineSetting
componentData={settings.engineSettings}
onValueChanged={onValueChanged}
/>
</AccordionItem>
)}
</Accordion>
)}
</TabsContent>
<TabsContent value="tools">
<Tools />
</TabsContent>
</Tabs>
</RightPanelContainer>
)
}
export default memo(ThreadRightPanel)