| 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" className="!rounded-full"> | |
| {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" className="!rounded-full"> | |
| {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" className="!rounded-full"> | |
| {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" className="!rounded-full"> | |
| {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; |