Unverified Commit c06e766d authored by zxhlyh's avatar zxhlyh Committed by GitHub

feat: model parameter prefefined (#1917)

parent 4a3d15b6
'use client'
import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import cn from 'classnames'
import type { ModelModeType } from '@/types/app'
type Props = {
className?: string
type: ModelModeType
isHighlight?: boolean
}
const ModelModeTypeLabel: FC<Props> = ({
className,
type,
isHighlight,
}) => {
const { t } = useTranslation()
return (
<div
className={cn(className, isHighlight ? 'border-indigo-300 text-indigo-600' : 'border-gray-300 text-gray-500', 'flex items-center h-4 px-1 border rounded text-xs font-semibold uppercase text-ellipsis overflow-hidden whitespace-nowrap')}
>
{t(`appDebug.modelConfig.modeType.${type}`)}
</div>
)
}
export default React.memo(ModelModeTypeLabel)
'use client'
import type { FC } from 'react'
import React from 'react'
export type IModelNameProps = {
modelId: string
modelDisplayName?: string
}
export const supportI18nModelName = [
'gpt-3.5-turbo', 'gpt-3.5-turbo-16k',
'gpt-4', 'gpt-4-32k',
'text-davinci-003', 'text-embedding-ada-002', 'whisper-1',
'claude-instant-1', 'claude-2',
]
const ModelName: FC<IModelNameProps> = ({
modelDisplayName,
}) => {
return (
<span className='text-ellipsis overflow-hidden whitespace-nowrap' title={modelDisplayName}>
{modelDisplayName}
</span>
)
}
export default React.memo(ModelName)
'use client'
import type { FC } from 'react'
import React, { useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import Tooltip from '@/app/components/base/tooltip'
import Slider from '@/app/components/base/slider'
import TagInput from '@/app/components/base/tag-input'
export const getFitPrecisionValue = (num: number, precision: number | null) => {
if (!precision || !(`${num}`).includes('.'))
return num
const currNumPrecision = (`${num}`).split('.')[1].length
if (currNumPrecision > precision)
return parseFloat(num.toFixed(precision))
return num
}
export type IParamIteProps = {
id: string
name: string
tip: string
value: number | string[]
step?: number
min?: number
max: number
precision: number | null
onChange: (key: string, value: number | string[]) => void
inputType?: 'inputTag' | 'slider'
}
const TIMES_TEMPLATE = '1000000000000'
const ParamItem: FC<IParamIteProps> = ({ id, name, tip, step = 0.1, min = 0, max, precision, value, inputType, onChange }) => {
const { t } = useTranslation()
const getToIntTimes = (num: number) => {
if (precision)
return parseInt(TIMES_TEMPLATE.slice(0, precision + 1), 10)
if (num < 5)
return 10
return 1
}
const times = getToIntTimes(max)
useEffect(() => {
if (precision)
onChange(id, getFitPrecisionValue(value, precision))
}, [value, precision])
return (
<div className="flex items-center justify-between flex-wrap gap-y-2">
<div className="flex flex-col flex-shrink-0">
<div className="flex items-center">
<span className="mr-[6px] text-gray-500 text-[13px] font-medium">{name}</span>
{/* Give tooltip different tip to avoiding hide bug */}
<Tooltip htmlContent={<div className="w-[200px] whitespace-pre-wrap">{tip}</div>} position='top' selector={`param-name-tooltip-${id}`}>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.66667 10.6667H8V8H7.33333M8 5.33333H8.00667M14 8C14 8.78793 13.8448 9.56815 13.5433 10.2961C13.2417 11.0241 12.7998 11.6855 12.2426 12.2426C11.6855 12.7998 11.0241 13.2417 10.2961 13.5433C9.56815 13.8448 8.78793 14 8 14C7.21207 14 6.43185 13.8448 5.7039 13.5433C4.97595 13.2417 4.31451 12.7998 3.75736 12.2426C3.20021 11.6855 2.75825 11.0241 2.45672 10.2961C2.15519 9.56815 2 8.78793 2 8C2 6.4087 2.63214 4.88258 3.75736 3.75736C4.88258 2.63214 6.4087 2 8 2C9.5913 2 11.1174 2.63214 12.2426 3.75736C13.3679 4.88258 14 6.4087 14 8Z" stroke="#9CA3AF" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</Tooltip>
</div>
{inputType === 'inputTag' && <div className="text-gray-400 text-xs font-normal">{t('common.model.params.stop_sequencesPlaceholder')}</div>}
</div>
<div className="flex items-center">
{inputType === 'inputTag'
? <TagInput
items={(value ?? []) as string[]}
onChange={newSequences => onChange(id, newSequences)}
customizedConfirmKey='Tab'
/>
: (
<>
<div className="mr-4 w-[120px]">
<Slider value={value * times} min={min * times} max={max * times} onChange={(value) => {
onChange(id, value / times)
}} />
</div>
<input type="number" min={min} max={max} step={step} className="block w-[64px] h-9 leading-9 rounded-lg border-0 pl-1 pl py-1.5 bg-gray-50 text-gray-900 placeholder:text-gray-400 focus:ring-1 focus:ring-inset focus:ring-primary-600" value={value} onChange={(e) => {
let value = getFitPrecisionValue(isNaN(parseFloat(e.target.value)) ? min : parseFloat(e.target.value), precision)
if (value < min)
value = min
if (value > max)
value = max
onChange(id, value)
}} />
</>
)
}
</div>
</div>
)
}
export default React.memo(ParamItem)
'use client'
import type { FC } from 'react'
import React from 'react'
export type IProviderNameProps = {
provideName: string
}
const ProviderName: FC<IProviderNameProps> = ({
provideName,
}) => {
return (
<span>
{provideName}
</span>
)
}
export default React.memo(ProviderName)
......@@ -28,7 +28,6 @@ import type { ExternalDataTool } from '@/models/common'
import type { DataSet } from '@/models/datasets'
import type { ModelConfig as BackendModelConfig, VisionSettings } from '@/types/app'
import ConfigContext from '@/context/debug-configuration'
// import ConfigModel from '@/app/components/app/configuration/config-model'
import Config from '@/app/components/app/configuration/config'
import Debug from '@/app/components/app/configuration/debug'
import Confirm from '@/app/components/base/confirm'
......
import type { FC } from 'react'
import { useEffect, useMemo, useState } from 'react'
import useSWR from 'swr'
import cn from 'classnames'
import { useTranslation } from 'react-i18next'
import type {
DefaultModel,
......@@ -32,6 +33,13 @@ import { fetchModelParameterRules } from '@/service/common'
import Loading from '@/app/components/base/loading'
import { useProviderContext } from '@/context/provider-context'
import TooltipPlus from '@/app/components/base/tooltip-plus'
import Radio from '@/app/components/base/radio'
import { TONE_LIST } from '@/config'
import { Brush01 } from '@/app/components/base/icons/src/vender/solid/editor'
import { Scales02 } from '@/app/components/base/icons/src/vender/solid/FinanceAndECommerce'
import { Target04 } from '@/app/components/base/icons/src/vender/solid/general'
import { Sliders02 } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
type ModelParameterModalProps = {
isAdvancedMode: boolean
......@@ -71,6 +79,8 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
const { t } = useTranslation()
const language = useLanguage()
const { hasSettedApiKey, modelProviders } = useProviderContext()
const media = useBreakpoints()
const isMobile = media === MediaType.mobile
const [open, setOpen] = useState(false)
const { data: parameterRulesData, isLoading } = useSWR(`/workspaces/current/model-providers/${provider}/models/parameter-rules?model=${modelId}`, fetchModelParameterRules)
const {
......@@ -89,6 +99,44 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
return parameterRulesData?.data || []
}, [parameterRulesData])
// only openai support this
function matchToneId(completionParams: FormValue): number {
const remvoedCustomeTone = TONE_LIST.slice(0, -1)
const CUSTOM_TONE_ID = 4
const tone = remvoedCustomeTone.find((tone) => {
return tone.config?.temperature === completionParams.temperature
&& tone.config?.top_p === completionParams.top_p
&& tone.config?.presence_penalty === completionParams.presence_penalty
&& tone.config?.frequency_penalty === completionParams.frequency_penalty
})
return tone ? tone.id : CUSTOM_TONE_ID
}
// tone is a preset of completionParams.
const [toneId, setToneId] = useState(matchToneId(completionParams)) // default is Balanced
const toneTabBgClassName = ({
1: 'bg-[#F5F8FF]',
2: 'bg-[#F4F3FF]',
3: 'bg-[#F6FEFC]',
})[toneId] || ''
// set completionParams by toneId
const handleToneChange = (id: number) => {
if (id === 4)
return // custom tone
const tone = TONE_LIST.find(tone => tone.id === id)
if (tone) {
setToneId(id)
onCompletionParamsChange({
...tone.config,
max_tokens: completionParams.max_tokens,
})
}
}
useEffect(() => {
setToneId(matchToneId(completionParams))
}, [completionParams])
const handleParamChange = (key: string, value: ParameterValue) => {
onCompletionParamsChange({
...completionParams,
......@@ -138,6 +186,17 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
handleInitialParams()
}, [parameterRules])
const getToneIcon = (toneId: number) => {
const className = 'w-[14px] h-[14px]'
const res = ({
1: <Brush01 className={className} />,
2: <Scales02 className={className} />,
3: <Target04 className={className} />,
4: <Sliders02 className={className} />,
})[toneId]
return res
}
return (
<PortalToFollowElem
open={open}
......@@ -241,6 +300,47 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
<div className='mt-5'><Loading /></div>
)
}
{['openai', 'azure_openai'].includes(provider) && !isLoading && !!parameterRules.length && (
<div className='mt-5 mb-4'>
<div className="mb-3 text-sm text-gray-900">{t('appDebug.modelConfig.setTone')}</div>
<Radio.Group className={cn('!rounded-lg', toneTabBgClassName)} value={toneId} onChange={handleToneChange}>
<>
{TONE_LIST.slice(0, 3).map(tone => (
<div className='grow flex items-center' key={tone.id}>
<Radio
value={tone.id}
className={cn(tone.id === toneId && 'rounded-md border border-gray-200 shadow-md', '!mr-0 grow !px-1 sm:!px-2 !justify-center text-[13px] font-medium')}
labelClassName={cn(tone.id === toneId
? ({
1: 'text-[#6938EF]',
2: 'text-[#444CE7]',
3: 'text-[#107569]',
})[toneId]
: 'text-[#667085]', 'flex items-center space-x-2')}
>
<>
{getToneIcon(tone.id)}
{!isMobile && <div>{t(`common.model.tone.${tone.name}`) as string}</div>}
<div className=""></div>
</>
</Radio>
{tone.id !== toneId && tone.id + 1 !== toneId && (<div className='h-5 border-r border-gray-200'></div>)}
</div>
))}
</>
<Radio
value={TONE_LIST[3].id}
className={cn(toneId === 4 && 'rounded-md border border-gray-200 shadow-md', '!mr-0 grow !px-1 sm:!px-2 !justify-center text-[13px] font-medium')}
labelClassName={cn('flex items-center space-x-2 ', toneId === 4 ? 'text-[#155EEF]' : 'text-[#667085]')}
>
<>
{getToneIcon(TONE_LIST[3].id)}
{!isMobile && <div>{t(`common.model.tone.${TONE_LIST[3].name}`) as string}</div>}
</>
</Radio>
</Radio.Group>
</div>
)}
{
!isLoading && !!parameterRules.length && (
[
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment