Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 

306 řádky
10 KiB

  1. /*
  2. Copyright (C) 2025 QuantumNous
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <https://www.gnu.org/licenses/>.
  13. For commercial licensing, please contact support@quantumnous.com
  14. */
  15. import React from 'react';
  16. import { Input, Slider, Typography, Button, Tag } from '@douyinfe/semi-ui';
  17. import { useTranslation } from 'react-i18next';
  18. import {
  19. Hash,
  20. Thermometer,
  21. Target,
  22. Repeat,
  23. Ban,
  24. Shuffle,
  25. Check,
  26. X,
  27. } from 'lucide-react';
  28. const ParameterControl = ({
  29. inputs,
  30. parameterEnabled,
  31. onInputChange,
  32. onParameterToggle,
  33. disabled = false,
  34. mutualExclusive = false,
  35. }) => {
  36. const { t } = useTranslation();
  37. return (
  38. <>
  39. {/* Temperature */}
  40. {mutualExclusive && (
  41. <div className='mb-3 px-2 py-1.5 bg-amber-50 border border-amber-200 rounded-lg'>
  42. <Typography.Text className='text-xs text-amber-600'>
  43. {t('此模型的 Temperature 和 Top P 互斥,只能启用一个')}
  44. </Typography.Text>
  45. </div>
  46. )}
  47. <div
  48. className={`transition-opacity duration-200 mb-4 ${!parameterEnabled.temperature || disabled ? 'opacity-50' : ''}`}
  49. >
  50. <div className='flex items-center justify-between mb-2'>
  51. <div className='flex items-center gap-2'>
  52. <Thermometer size={16} className='text-gray-500' />
  53. <Typography.Text strong className='text-sm'>
  54. Temperature
  55. </Typography.Text>
  56. <Tag size='small' shape='circle'>
  57. {inputs.temperature}
  58. </Tag>
  59. </div>
  60. <Button
  61. theme={parameterEnabled.temperature ? 'solid' : 'borderless'}
  62. type={parameterEnabled.temperature ? 'primary' : 'tertiary'}
  63. size='small'
  64. icon={
  65. parameterEnabled.temperature ? (
  66. <Check size={10} />
  67. ) : (
  68. <X size={10} />
  69. )
  70. }
  71. onClick={() => onParameterToggle('temperature')}
  72. className='!rounded-full !w-4 !h-4 !p-0 !min-w-0'
  73. disabled={disabled}
  74. />
  75. </div>
  76. <Typography.Text className='text-xs text-gray-500 mb-2'>
  77. {t('控制输出的随机性和创造性')}
  78. </Typography.Text>
  79. <Slider
  80. step={0.1}
  81. min={0.1}
  82. max={1}
  83. value={inputs.temperature}
  84. onChange={(value) => onInputChange('temperature', value)}
  85. className='mt-2'
  86. disabled={!parameterEnabled.temperature || disabled}
  87. />
  88. </div>
  89. {/* Top P */}
  90. <div
  91. className={`transition-opacity duration-200 mb-4 ${!parameterEnabled.top_p || disabled ? 'opacity-50' : ''}`}
  92. >
  93. <div className='flex items-center justify-between mb-2'>
  94. <div className='flex items-center gap-2'>
  95. <Target size={16} className='text-gray-500' />
  96. <Typography.Text strong className='text-sm'>
  97. Top P
  98. </Typography.Text>
  99. <Tag size='small' shape='circle'>
  100. {inputs.top_p}
  101. </Tag>
  102. </div>
  103. <Button
  104. theme={parameterEnabled.top_p ? 'solid' : 'borderless'}
  105. type={parameterEnabled.top_p ? 'primary' : 'tertiary'}
  106. size='small'
  107. icon={
  108. parameterEnabled.top_p ? <Check size={10} /> : <X size={10} />
  109. }
  110. onClick={() => onParameterToggle('top_p')}
  111. className='!rounded-full !w-4 !h-4 !p-0 !min-w-0'
  112. disabled={disabled}
  113. />
  114. </div>
  115. <Typography.Text className='text-xs text-gray-500 mb-2'>
  116. {t('核采样,控制词汇选择的多样性')}
  117. </Typography.Text>
  118. <Slider
  119. step={0.1}
  120. min={0.1}
  121. max={1}
  122. value={inputs.top_p}
  123. onChange={(value) => onInputChange('top_p', value)}
  124. className='mt-2'
  125. disabled={!parameterEnabled.top_p || disabled}
  126. />
  127. </div>
  128. {/* Frequency Penalty */}
  129. <div
  130. className={`transition-opacity duration-200 mb-4 ${!parameterEnabled.frequency_penalty || disabled ? 'opacity-50' : ''}`}
  131. >
  132. <div className='flex items-center justify-between mb-2'>
  133. <div className='flex items-center gap-2'>
  134. <Repeat size={16} className='text-gray-500' />
  135. <Typography.Text strong className='text-sm'>
  136. Frequency Penalty
  137. </Typography.Text>
  138. <Tag size='small' shape='circle'>
  139. {inputs.frequency_penalty}
  140. </Tag>
  141. </div>
  142. <Button
  143. theme={parameterEnabled.frequency_penalty ? 'solid' : 'borderless'}
  144. type={parameterEnabled.frequency_penalty ? 'primary' : 'tertiary'}
  145. size='small'
  146. icon={
  147. parameterEnabled.frequency_penalty ? (
  148. <Check size={10} />
  149. ) : (
  150. <X size={10} />
  151. )
  152. }
  153. onClick={() => onParameterToggle('frequency_penalty')}
  154. className='!rounded-full !w-4 !h-4 !p-0 !min-w-0'
  155. disabled={disabled}
  156. />
  157. </div>
  158. <Typography.Text className='text-xs text-gray-500 mb-2'>
  159. {t('频率惩罚,减少重复词汇的出现')}
  160. </Typography.Text>
  161. <Slider
  162. step={0.1}
  163. min={-2}
  164. max={2}
  165. value={inputs.frequency_penalty}
  166. onChange={(value) => onInputChange('frequency_penalty', value)}
  167. className='mt-2'
  168. disabled={!parameterEnabled.frequency_penalty || disabled}
  169. />
  170. </div>
  171. {/* Presence Penalty */}
  172. <div
  173. className={`transition-opacity duration-200 mb-4 ${!parameterEnabled.presence_penalty || disabled ? 'opacity-50' : ''}`}
  174. >
  175. <div className='flex items-center justify-between mb-2'>
  176. <div className='flex items-center gap-2'>
  177. <Ban size={16} className='text-gray-500' />
  178. <Typography.Text strong className='text-sm'>
  179. Presence Penalty
  180. </Typography.Text>
  181. <Tag size='small' shape='circle'>
  182. {inputs.presence_penalty}
  183. </Tag>
  184. </div>
  185. <Button
  186. theme={parameterEnabled.presence_penalty ? 'solid' : 'borderless'}
  187. type={parameterEnabled.presence_penalty ? 'primary' : 'tertiary'}
  188. size='small'
  189. icon={
  190. parameterEnabled.presence_penalty ? (
  191. <Check size={10} />
  192. ) : (
  193. <X size={10} />
  194. )
  195. }
  196. onClick={() => onParameterToggle('presence_penalty')}
  197. className='!rounded-full !w-4 !h-4 !p-0 !min-w-0'
  198. disabled={disabled}
  199. />
  200. </div>
  201. <Typography.Text className='text-xs text-gray-500 mb-2'>
  202. {t('存在惩罚,鼓励讨论新话题')}
  203. </Typography.Text>
  204. <Slider
  205. step={0.1}
  206. min={-2}
  207. max={2}
  208. value={inputs.presence_penalty}
  209. onChange={(value) => onInputChange('presence_penalty', value)}
  210. className='mt-2'
  211. disabled={!parameterEnabled.presence_penalty || disabled}
  212. />
  213. </div>
  214. {/* MaxTokens */}
  215. <div
  216. className={`transition-opacity duration-200 mb-4 ${!parameterEnabled.max_tokens || disabled ? 'opacity-50' : ''}`}
  217. >
  218. <div className='flex items-center justify-between mb-2'>
  219. <div className='flex items-center gap-2'>
  220. <Hash size={16} className='text-gray-500' />
  221. <Typography.Text strong className='text-sm'>
  222. Max Tokens
  223. </Typography.Text>
  224. </div>
  225. <Button
  226. theme={parameterEnabled.max_tokens ? 'solid' : 'borderless'}
  227. type={parameterEnabled.max_tokens ? 'primary' : 'tertiary'}
  228. size='small'
  229. icon={
  230. parameterEnabled.max_tokens ? (
  231. <Check size={10} />
  232. ) : (
  233. <X size={10} />
  234. )
  235. }
  236. onClick={() => onParameterToggle('max_tokens')}
  237. className='!rounded-full !w-4 !h-4 !p-0 !min-w-0'
  238. disabled={disabled}
  239. />
  240. </div>
  241. <Input
  242. placeholder='MaxTokens'
  243. name='max_tokens'
  244. required
  245. autoComplete='new-password'
  246. defaultValue={0}
  247. value={inputs.max_tokens}
  248. onChange={(value) => onInputChange('max_tokens', value)}
  249. className='!rounded-lg'
  250. disabled={!parameterEnabled.max_tokens || disabled}
  251. />
  252. </div>
  253. {/* Seed */}
  254. <div
  255. className={`transition-opacity duration-200 mb-4 ${!parameterEnabled.seed || disabled ? 'opacity-50' : ''}`}
  256. >
  257. <div className='flex items-center justify-between mb-2'>
  258. <div className='flex items-center gap-2'>
  259. <Shuffle size={16} className='text-gray-500' />
  260. <Typography.Text strong className='text-sm'>
  261. Seed
  262. </Typography.Text>
  263. <Typography.Text className='text-xs text-gray-400'>
  264. ({t('可选,用于复现结果')})
  265. </Typography.Text>
  266. </div>
  267. <Button
  268. theme={parameterEnabled.seed ? 'solid' : 'borderless'}
  269. type={parameterEnabled.seed ? 'primary' : 'tertiary'}
  270. size='small'
  271. icon={parameterEnabled.seed ? <Check size={10} /> : <X size={10} />}
  272. onClick={() => onParameterToggle('seed')}
  273. className='!rounded-full !w-4 !h-4 !p-0 !min-w-0'
  274. disabled={disabled}
  275. />
  276. </div>
  277. <Input
  278. placeholder={t('随机种子 (留空为随机)')}
  279. name='seed'
  280. autoComplete='new-password'
  281. value={inputs.seed || ''}
  282. onChange={(value) =>
  283. onInputChange('seed', value === '' ? null : value)
  284. }
  285. className='!rounded-lg'
  286. disabled={!parameterEnabled.seed || disabled}
  287. />
  288. </div>
  289. </>
  290. );
  291. };
  292. export default ParameterControl;