dify
This commit is contained in:
121
dify/web/app/components/base/param-item/index.stories.tsx
Normal file
121
dify/web/app/components/base/param-item/index.stories.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
import type { Meta, StoryObj } from '@storybook/nextjs'
|
||||
import { useState } from 'react'
|
||||
import ParamItem from '.'
|
||||
|
||||
type ParamConfig = {
|
||||
id: string
|
||||
name: string
|
||||
tip: string
|
||||
value: number
|
||||
min: number
|
||||
max: number
|
||||
step: number
|
||||
allowToggle?: boolean
|
||||
}
|
||||
|
||||
const PARAMS: ParamConfig[] = [
|
||||
{
|
||||
id: 'temperature',
|
||||
name: 'Temperature',
|
||||
tip: 'Controls randomness. Lower values make the model more deterministic, higher values encourage creativity.',
|
||||
value: 0.7,
|
||||
min: 0,
|
||||
max: 2,
|
||||
step: 0.1,
|
||||
allowToggle: true,
|
||||
},
|
||||
{
|
||||
id: 'top_p',
|
||||
name: 'Top P',
|
||||
tip: 'Nucleus sampling keeps only the most probable tokens whose cumulative probability exceeds this threshold.',
|
||||
value: 0.9,
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.05,
|
||||
},
|
||||
{
|
||||
id: 'frequency_penalty',
|
||||
name: 'Frequency Penalty',
|
||||
tip: 'Discourages repeating tokens. Increase to reduce repetition.',
|
||||
value: 0.2,
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.05,
|
||||
},
|
||||
]
|
||||
|
||||
const ParamItemPlayground = () => {
|
||||
const [state, setState] = useState<Record<string, { value: number; enabled: boolean }>>(() => {
|
||||
return PARAMS.reduce((acc, item) => {
|
||||
acc[item.id] = { value: item.value, enabled: true }
|
||||
return acc
|
||||
}, {} as Record<string, { value: number; enabled: boolean }>)
|
||||
})
|
||||
|
||||
const handleChange = (id: string, value: number) => {
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
[id]: {
|
||||
...prev[id],
|
||||
value: Number.parseFloat(value.toFixed(3)),
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
const handleToggle = (id: string, enabled: boolean) => {
|
||||
setState(prev => ({
|
||||
...prev,
|
||||
[id]: {
|
||||
...prev[id],
|
||||
enabled,
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex w-full max-w-2xl flex-col gap-5 rounded-2xl border border-divider-subtle bg-components-panel-bg p-6">
|
||||
<div className="flex items-center justify-between text-xs uppercase tracking-[0.18em] text-text-tertiary">
|
||||
<span>Generation parameters</span>
|
||||
<code className="rounded-md border border-divider-subtle bg-background-default px-2 py-1 text-[11px] text-text-tertiary">
|
||||
{JSON.stringify(state, null, 0)}
|
||||
</code>
|
||||
</div>
|
||||
{PARAMS.map(param => (
|
||||
<ParamItem
|
||||
key={param.id}
|
||||
className="rounded-xl border border-transparent px-3 py-2 hover:border-divider-subtle hover:bg-background-default-subtle"
|
||||
id={param.id}
|
||||
name={param.name}
|
||||
tip={param.tip}
|
||||
value={state[param.id].value}
|
||||
enable={state[param.id].enabled}
|
||||
min={param.min}
|
||||
max={param.max}
|
||||
step={param.step}
|
||||
hasSwitch={param.allowToggle}
|
||||
onChange={handleChange}
|
||||
onSwitchChange={handleToggle}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const meta = {
|
||||
title: 'Base/Data Entry/ParamItem',
|
||||
component: ParamItemPlayground,
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
docs: {
|
||||
description: {
|
||||
component: 'Slider + numeric input pairing used for model parameter tuning. Supports optional enable toggles per parameter.',
|
||||
},
|
||||
},
|
||||
},
|
||||
tags: ['autodocs'],
|
||||
} satisfies Meta<typeof ParamItemPlayground>
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
export const Playground: Story = {}
|
||||
79
dify/web/app/components/base/param-item/index.tsx
Normal file
79
dify/web/app/components/base/param-item/index.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import { InputNumber } from '../input-number'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import Slider from '@/app/components/base/slider'
|
||||
import Switch from '@/app/components/base/switch'
|
||||
|
||||
type Props = {
|
||||
className?: string
|
||||
id: string
|
||||
name: string
|
||||
noTooltip?: boolean
|
||||
tip?: string
|
||||
value: number
|
||||
enable: boolean
|
||||
step?: number
|
||||
min?: number
|
||||
max: number
|
||||
onChange: (key: string, value: number) => void
|
||||
hasSwitch?: boolean
|
||||
onSwitchChange?: (key: string, enable: boolean) => void
|
||||
}
|
||||
|
||||
const ParamItem: FC<Props> = ({ className, id, name, noTooltip, tip, step = 0.1, min = 0, max, value, enable, onChange, hasSwitch, onSwitchChange }) => {
|
||||
return (
|
||||
<div className={className}>
|
||||
<div className='flex items-center justify-between'>
|
||||
<div className='flex h-6 items-center'>
|
||||
{hasSwitch && (
|
||||
<Switch
|
||||
size='md'
|
||||
className='mr-2'
|
||||
defaultValue={enable}
|
||||
onChange={async (val) => {
|
||||
onSwitchChange?.(id, val)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<span className='system-sm-semibold mr-1 text-text-secondary'>{name}</span>
|
||||
{!noTooltip && (
|
||||
<Tooltip
|
||||
triggerClassName='w-4 h-4 shrink-0'
|
||||
popupContent={<div className='w-[200px]'>{tip}</div>}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className='mt-1 flex items-center'>
|
||||
<div className='mr-3 flex shrink-0 items-center'>
|
||||
<InputNumber
|
||||
disabled={!enable}
|
||||
type='number'
|
||||
min={min}
|
||||
max={max}
|
||||
step={step}
|
||||
amount={step}
|
||||
size='regular'
|
||||
value={value}
|
||||
onChange={(value) => {
|
||||
onChange(id, value)
|
||||
}}
|
||||
className='w-[72px]'
|
||||
/>
|
||||
</div>
|
||||
<div className='flex grow items-center'>
|
||||
<Slider
|
||||
className='w-full'
|
||||
disabled={!enable}
|
||||
value={max < 5 ? value * 100 : value}
|
||||
min={min < 1 ? min * 100 : min}
|
||||
max={max < 5 ? max * 100 : max}
|
||||
onChange={value => onChange(id, value / (max < 5 ? 100 : 1))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default ParamItem
|
||||
@@ -0,0 +1,53 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import ParamItem from '.'
|
||||
|
||||
type Props = {
|
||||
className?: string
|
||||
value: number
|
||||
onChange: (key: string, value: number) => void
|
||||
enable: boolean
|
||||
hasSwitch?: boolean
|
||||
onSwitchChange?: (key: string, enable: boolean) => void
|
||||
}
|
||||
|
||||
const VALUE_LIMIT = {
|
||||
default: 0.7,
|
||||
step: 0.01,
|
||||
min: 0,
|
||||
max: 1,
|
||||
}
|
||||
|
||||
const ScoreThresholdItem: FC<Props> = ({
|
||||
className,
|
||||
value,
|
||||
enable,
|
||||
onChange,
|
||||
hasSwitch,
|
||||
onSwitchChange,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const handleParamChange = (key: string, value: number) => {
|
||||
let notOutRangeValue = Number.parseFloat(value.toFixed(2))
|
||||
notOutRangeValue = Math.max(VALUE_LIMIT.min, notOutRangeValue)
|
||||
notOutRangeValue = Math.min(VALUE_LIMIT.max, notOutRangeValue)
|
||||
onChange(key, notOutRangeValue)
|
||||
}
|
||||
return (
|
||||
<ParamItem
|
||||
className={className}
|
||||
id='score_threshold'
|
||||
name={t('appDebug.datasetConfig.score_threshold')}
|
||||
tip={t('appDebug.datasetConfig.score_thresholdTip') as string}
|
||||
{...VALUE_LIMIT}
|
||||
value={value}
|
||||
enable={enable}
|
||||
onChange={handleParamChange}
|
||||
hasSwitch={hasSwitch}
|
||||
onSwitchChange={onSwitchChange}
|
||||
/>
|
||||
)
|
||||
}
|
||||
export default React.memo(ScoreThresholdItem)
|
||||
53
dify/web/app/components/base/param-item/top-k-item.tsx
Normal file
53
dify/web/app/components/base/param-item/top-k-item.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import ParamItem from '.'
|
||||
|
||||
type Props = {
|
||||
className?: string
|
||||
value: number
|
||||
onChange: (key: string, value: number) => void
|
||||
enable: boolean
|
||||
}
|
||||
|
||||
const maxTopK = (() => {
|
||||
const configValue = Number.parseInt(globalThis.document?.body?.getAttribute('data-public-top-k-max-value') || '', 10)
|
||||
if (configValue && !isNaN(configValue))
|
||||
return configValue
|
||||
return 10
|
||||
})()
|
||||
const VALUE_LIMIT = {
|
||||
default: 2,
|
||||
step: 1,
|
||||
min: 1,
|
||||
max: maxTopK,
|
||||
}
|
||||
|
||||
const TopKItem: FC<Props> = ({
|
||||
className,
|
||||
value,
|
||||
enable,
|
||||
onChange,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const handleParamChange = (key: string, value: number) => {
|
||||
let notOutRangeValue = Number.parseInt(value.toFixed(0))
|
||||
notOutRangeValue = Math.max(VALUE_LIMIT.min, notOutRangeValue)
|
||||
notOutRangeValue = Math.min(VALUE_LIMIT.max, notOutRangeValue)
|
||||
onChange(key, notOutRangeValue)
|
||||
}
|
||||
return (
|
||||
<ParamItem
|
||||
className={className}
|
||||
id='top_k'
|
||||
name={t('appDebug.datasetConfig.top_k')}
|
||||
tip={t('appDebug.datasetConfig.top_kTip') as string}
|
||||
{...VALUE_LIMIT}
|
||||
value={value}
|
||||
enable={enable}
|
||||
onChange={handleParamChange}
|
||||
/>
|
||||
)
|
||||
}
|
||||
export default React.memo(TopKItem)
|
||||
Reference in New Issue
Block a user