| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import React from 'react'; |
| import { Input, Slider, Typography, Button, Tag } from '@douyinfe/semi-ui'; |
| import { |
| Hash, |
| Thermometer, |
| Target, |
| Repeat, |
| Ban, |
| Shuffle, |
| Check, |
| X, |
| } from 'lucide-react'; |
|
|
| const ParameterControl = ({ |
| inputs, |
| parameterEnabled, |
| onInputChange, |
| onParameterToggle, |
| disabled = false, |
| }) => { |
| return ( |
| <> |
| {/* Temperature */} |
| <div |
| className={`transition-opacity duration-200 mb-4 ${!parameterEnabled.temperature || disabled ? 'opacity-50' : ''}`} |
| > |
| <div className='flex items-center justify-between mb-2'> |
| <div className='flex items-center gap-2'> |
| <Thermometer size={16} className='text-gray-500' /> |
| <Typography.Text strong className='text-sm'> |
| Temperature |
| </Typography.Text> |
| <Tag size='small' shape='circle'> |
| {inputs.temperature} |
| </Tag> |
| </div> |
| <Button |
| theme={parameterEnabled.temperature ? 'solid' : 'borderless'} |
| type={parameterEnabled.temperature ? 'primary' : 'tertiary'} |
| size='small' |
| icon={ |
| parameterEnabled.temperature ? ( |
| <Check size={10} /> |
| ) : ( |
| <X size={10} /> |
| ) |
| } |
| onClick={() => onParameterToggle('temperature')} |
| className='!rounded-full !w-4 !h-4 !p-0 !min-w-0' |
| disabled={disabled} |
| /> |
| </div> |
| <Typography.Text className='text-xs text-gray-500 mb-2'> |
| 控制输出的随机性和创造性 |
| </Typography.Text> |
| <Slider |
| step={0.1} |
| min={0.1} |
| max={1} |
| value={inputs.temperature} |
| onChange={(value) => onInputChange('temperature', value)} |
| className='mt-2' |
| disabled={!parameterEnabled.temperature || disabled} |
| /> |
| </div> |
| |
| {/* Top P */} |
| <div |
| className={`transition-opacity duration-200 mb-4 ${!parameterEnabled.top_p || disabled ? 'opacity-50' : ''}`} |
| > |
| <div className='flex items-center justify-between mb-2'> |
| <div className='flex items-center gap-2'> |
| <Target size={16} className='text-gray-500' /> |
| <Typography.Text strong className='text-sm'> |
| Top P |
| </Typography.Text> |
| <Tag size='small' shape='circle'> |
| {inputs.top_p} |
| </Tag> |
| </div> |
| <Button |
| theme={parameterEnabled.top_p ? 'solid' : 'borderless'} |
| type={parameterEnabled.top_p ? 'primary' : 'tertiary'} |
| size='small' |
| icon={ |
| parameterEnabled.top_p ? <Check size={10} /> : <X size={10} /> |
| } |
| onClick={() => onParameterToggle('top_p')} |
| className='!rounded-full !w-4 !h-4 !p-0 !min-w-0' |
| disabled={disabled} |
| /> |
| </div> |
| <Typography.Text className='text-xs text-gray-500 mb-2'> |
| 核采样,控制词汇选择的多样性 |
| </Typography.Text> |
| <Slider |
| step={0.1} |
| min={0.1} |
| max={1} |
| value={inputs.top_p} |
| onChange={(value) => onInputChange('top_p', value)} |
| className='mt-2' |
| disabled={!parameterEnabled.top_p || disabled} |
| /> |
| </div> |
| |
| {/* Frequency Penalty */} |
| <div |
| className={`transition-opacity duration-200 mb-4 ${!parameterEnabled.frequency_penalty || disabled ? 'opacity-50' : ''}`} |
| > |
| <div className='flex items-center justify-between mb-2'> |
| <div className='flex items-center gap-2'> |
| <Repeat size={16} className='text-gray-500' /> |
| <Typography.Text strong className='text-sm'> |
| Frequency Penalty |
| </Typography.Text> |
| <Tag size='small' shape='circle'> |
| {inputs.frequency_penalty} |
| </Tag> |
| </div> |
| <Button |
| theme={parameterEnabled.frequency_penalty ? 'solid' : 'borderless'} |
| type={parameterEnabled.frequency_penalty ? 'primary' : 'tertiary'} |
| size='small' |
| icon={ |
| parameterEnabled.frequency_penalty ? ( |
| <Check size={10} /> |
| ) : ( |
| <X size={10} /> |
| ) |
| } |
| onClick={() => onParameterToggle('frequency_penalty')} |
| className='!rounded-full !w-4 !h-4 !p-0 !min-w-0' |
| disabled={disabled} |
| /> |
| </div> |
| <Typography.Text className='text-xs text-gray-500 mb-2'> |
| 频率惩罚,减少重复词汇的出现 |
| </Typography.Text> |
| <Slider |
| step={0.1} |
| min={-2} |
| max={2} |
| value={inputs.frequency_penalty} |
| onChange={(value) => onInputChange('frequency_penalty', value)} |
| className='mt-2' |
| disabled={!parameterEnabled.frequency_penalty || disabled} |
| /> |
| </div> |
| |
| {/* Presence Penalty */} |
| <div |
| className={`transition-opacity duration-200 mb-4 ${!parameterEnabled.presence_penalty || disabled ? 'opacity-50' : ''}`} |
| > |
| <div className='flex items-center justify-between mb-2'> |
| <div className='flex items-center gap-2'> |
| <Ban size={16} className='text-gray-500' /> |
| <Typography.Text strong className='text-sm'> |
| Presence Penalty |
| </Typography.Text> |
| <Tag size='small' shape='circle'> |
| {inputs.presence_penalty} |
| </Tag> |
| </div> |
| <Button |
| theme={parameterEnabled.presence_penalty ? 'solid' : 'borderless'} |
| type={parameterEnabled.presence_penalty ? 'primary' : 'tertiary'} |
| size='small' |
| icon={ |
| parameterEnabled.presence_penalty ? ( |
| <Check size={10} /> |
| ) : ( |
| <X size={10} /> |
| ) |
| } |
| onClick={() => onParameterToggle('presence_penalty')} |
| className='!rounded-full !w-4 !h-4 !p-0 !min-w-0' |
| disabled={disabled} |
| /> |
| </div> |
| <Typography.Text className='text-xs text-gray-500 mb-2'> |
| 存在惩罚,鼓励讨论新话题 |
| </Typography.Text> |
| <Slider |
| step={0.1} |
| min={-2} |
| max={2} |
| value={inputs.presence_penalty} |
| onChange={(value) => onInputChange('presence_penalty', value)} |
| className='mt-2' |
| disabled={!parameterEnabled.presence_penalty || disabled} |
| /> |
| </div> |
| |
| {/* MaxTokens */} |
| <div |
| className={`transition-opacity duration-200 mb-4 ${!parameterEnabled.max_tokens || disabled ? 'opacity-50' : ''}`} |
| > |
| <div className='flex items-center justify-between mb-2'> |
| <div className='flex items-center gap-2'> |
| <Hash size={16} className='text-gray-500' /> |
| <Typography.Text strong className='text-sm'> |
| Max Tokens |
| </Typography.Text> |
| </div> |
| <Button |
| theme={parameterEnabled.max_tokens ? 'solid' : 'borderless'} |
| type={parameterEnabled.max_tokens ? 'primary' : 'tertiary'} |
| size='small' |
| icon={ |
| parameterEnabled.max_tokens ? ( |
| <Check size={10} /> |
| ) : ( |
| <X size={10} /> |
| ) |
| } |
| onClick={() => onParameterToggle('max_tokens')} |
| className='!rounded-full !w-4 !h-4 !p-0 !min-w-0' |
| disabled={disabled} |
| /> |
| </div> |
| <Input |
| placeholder='MaxTokens' |
| name='max_tokens' |
| required |
| autoComplete='new-password' |
| defaultValue={0} |
| value={inputs.max_tokens} |
| onChange={(value) => onInputChange('max_tokens', value)} |
| className='!rounded-lg' |
| disabled={!parameterEnabled.max_tokens || disabled} |
| /> |
| </div> |
| |
| {/* Seed */} |
| <div |
| className={`transition-opacity duration-200 mb-4 ${!parameterEnabled.seed || disabled ? 'opacity-50' : ''}`} |
| > |
| <div className='flex items-center justify-between mb-2'> |
| <div className='flex items-center gap-2'> |
| <Shuffle size={16} className='text-gray-500' /> |
| <Typography.Text strong className='text-sm'> |
| Seed |
| </Typography.Text> |
| <Typography.Text className='text-xs text-gray-400'> |
| (可选,用于复现结果) |
| </Typography.Text> |
| </div> |
| <Button |
| theme={parameterEnabled.seed ? 'solid' : 'borderless'} |
| type={parameterEnabled.seed ? 'primary' : 'tertiary'} |
| size='small' |
| icon={parameterEnabled.seed ? <Check size={10} /> : <X size={10} />} |
| onClick={() => onParameterToggle('seed')} |
| className='!rounded-full !w-4 !h-4 !p-0 !min-w-0' |
| disabled={disabled} |
| /> |
| </div> |
| <Input |
| placeholder='随机种子 (留空为随机)' |
| name='seed' |
| autoComplete='new-password' |
| value={inputs.seed || ''} |
| onChange={(value) => |
| onInputChange('seed', value === '' ? null : value) |
| } |
| className='!rounded-lg' |
| disabled={!parameterEnabled.seed || disabled} |
| /> |
| </div> |
| </> |
| ); |
| }; |
|
|
| export default ParameterControl; |
|
|