Completion Page
This commit is contained in:
		
							parent
							
								
									bcb38d991a
								
							
						
					
					
						commit
						c7ed4b07c2
					
				| @ -20,11 +20,11 @@ | |||||||
|   "Manage Models": "管理模型", |   "Manage Models": "管理模型", | ||||||
|   "Model": "模型", |   "Model": "模型", | ||||||
|   "Model Parameters": "模型参数", |   "Model Parameters": "模型参数", | ||||||
|   "Frequency Penalty *": "Frequency Penalty *", |   "Frequency Penalty": "Frequency Penalty", | ||||||
|   "Presence Penalty *": "Presence Penalty *", |   "Presence Penalty": "Presence Penalty", | ||||||
|   "Top_P *": "Top_P *", |   "Top_P": "Top_P", | ||||||
|   "Temperature *": "Temperature *", |   "Temperature": "Temperature", | ||||||
|   "Max Response Token *": "最大响应 Token *", |   "Max Response Token": "最大响应 Token", | ||||||
|   "API Port": "API 端口", |   "API Port": "API 端口", | ||||||
|   "Hover your mouse over the text to view a detailed description. Settings marked with * will take effect immediately after being saved.": "把鼠标悬停在文本上查看详细描述. 标记了星号 * 的设置在保存后会立即生效.", |   "Hover your mouse over the text to view a detailed description. Settings marked with * will take effect immediately after being saved.": "把鼠标悬停在文本上查看详细描述. 标记了星号 * 的设置在保存后会立即生效.", | ||||||
|   "Default API Parameters": "默认 API 参数", |   "Default API Parameters": "默认 API 参数", | ||||||
| @ -102,5 +102,17 @@ | |||||||
|   "Enabling this option can greatly improve inference speed, but there may be compatibility issues. If it fails to start, please turn off this option.": "开启这个选项能大大提升推理速度,但可能存在兼容性问题,如果启动失败,请关闭此选项", |   "Enabling this option can greatly improve inference speed, but there may be compatibility issues. If it fails to start, please turn off this option.": "开启这个选项能大大提升推理速度,但可能存在兼容性问题,如果启动失败,请关闭此选项", | ||||||
|   "Supported custom cuda file not found": "没有找到支持的自定义cuda文件", |   "Supported custom cuda file not found": "没有找到支持的自定义cuda文件", | ||||||
|   "Failed to copy custom cuda file": "自定义cuda文件复制失败", |   "Failed to copy custom cuda file": "自定义cuda文件复制失败", | ||||||
|   "Downloading update, please wait. If it is not completed, please manually download the program from GitHub and replace the original program.": "正在下载更新,请等待。如果一直未完成,请从Github手动下载并覆盖原程序" |   "Downloading update, please wait. If it is not completed, please manually download the program from GitHub and replace the original program.": "正在下载更新,请等待。如果一直未完成,请从Github手动下载并覆盖原程序", | ||||||
|  |   "Completion": "补全", | ||||||
|  |   "Parameters": "参数", | ||||||
|  |   "Stop Sequences": "停止词", | ||||||
|  |   "When this content appears in the response result, the generation will end.": "响应结果出现该内容时就结束生成", | ||||||
|  |   "Reset": "重置", | ||||||
|  |   "Generate": "生成", | ||||||
|  |   "Writer": "写作", | ||||||
|  |   "Translator": "翻译", | ||||||
|  |   "Catgirl": "猫娘", | ||||||
|  |   "Explain Code": "代码解释", | ||||||
|  |   "Werewolf": "狼人杀", | ||||||
|  |   "Blank": "空白" | ||||||
| } | } | ||||||
| @ -3,18 +3,25 @@ import { Label, Tooltip } from '@fluentui/react-components'; | |||||||
| import classnames from 'classnames'; | import classnames from 'classnames'; | ||||||
| 
 | 
 | ||||||
| export const Labeled: FC<{ | export const Labeled: FC<{ | ||||||
|   label: string; desc?: string | null, content: ReactElement, flex?: boolean, spaceBetween?: boolean |   label: string; | ||||||
|  |   desc?: string | null, | ||||||
|  |   content: ReactElement, | ||||||
|  |   flex?: boolean, | ||||||
|  |   spaceBetween?: boolean, | ||||||
|  |   breakline?: boolean | ||||||
| }> = ({ | }> = ({ | ||||||
|   label, |   label, | ||||||
|   desc, |   desc, | ||||||
|   content, |   content, | ||||||
|   flex, |   flex, | ||||||
|   spaceBetween |   spaceBetween, | ||||||
|  |   breakline | ||||||
| }) => { | }) => { | ||||||
|   return ( |   return ( | ||||||
|     <div className={classnames( |     <div className={classnames( | ||||||
|       'items-center', |       !breakline ? 'items-center' : '', | ||||||
|       flex ? 'flex' : 'grid grid-cols-2', |       flex ? 'flex' : 'grid grid-cols-2', | ||||||
|  |       breakline ? 'flex-col' : '', | ||||||
|       spaceBetween && 'justify-between') |       spaceBetween && 'justify-between') | ||||||
|     }> |     }> | ||||||
|       {desc ? |       {desc ? | ||||||
|  | |||||||
							
								
								
									
										46
									
								
								frontend/src/components/WorkHeader.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								frontend/src/components/WorkHeader.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,46 @@ | |||||||
|  | import React, { FC } from 'react'; | ||||||
|  | import { observer } from 'mobx-react-lite'; | ||||||
|  | import { Divider, PresenceBadge, Text } from '@fluentui/react-components'; | ||||||
|  | import commonStore, { ModelStatus } from '../stores/commonStore'; | ||||||
|  | import { ConfigSelector } from './ConfigSelector'; | ||||||
|  | import { RunButton } from './RunButton'; | ||||||
|  | import { PresenceBadgeStatus } from '@fluentui/react-badge'; | ||||||
|  | import { useTranslation } from 'react-i18next'; | ||||||
|  | 
 | ||||||
|  | const statusText = { | ||||||
|  |   [ModelStatus.Offline]: 'Offline', | ||||||
|  |   [ModelStatus.Starting]: 'Starting', | ||||||
|  |   [ModelStatus.Loading]: 'Loading', | ||||||
|  |   [ModelStatus.Working]: 'Working' | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const badgeStatus: { [modelStatus: number]: PresenceBadgeStatus } = { | ||||||
|  |   [ModelStatus.Offline]: 'unknown', | ||||||
|  |   [ModelStatus.Starting]: 'away', | ||||||
|  |   [ModelStatus.Loading]: 'away', | ||||||
|  |   [ModelStatus.Working]: 'available' | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export const WorkHeader: FC = observer(() => { | ||||||
|  |   const { t } = useTranslation(); | ||||||
|  |   const port = commonStore.getCurrentModelConfig().apiParameters.apiPort; | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <div className="flex flex-col gap-1"> | ||||||
|  |       <div className="flex justify-between items-center"> | ||||||
|  |         <div className="flex items-center gap-2"> | ||||||
|  |           <PresenceBadge status={badgeStatus[commonStore.status.modelStatus]} /> | ||||||
|  |           <Text size={100}>{t('Model Status') + ': ' + t(statusText[commonStore.status.modelStatus])}</Text> | ||||||
|  |         </div> | ||||||
|  |         <div className="flex items-center gap-2"> | ||||||
|  |           <ConfigSelector size="small" /> | ||||||
|  |           <RunButton iconMode /> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |       <Text size={100}> | ||||||
|  |         {t('This tool\'s API is compatible with OpenAI API. It can be used with any ChatGPT tool you like. Go to the settings of some ChatGPT tool, replace the \'https://api.openai.com\' part in the API address with \'') + `http://127.0.0.1:${port}` + '\'.'} | ||||||
|  |       </Text> | ||||||
|  |       <Divider style={{ flexGrow: 0 }} /> | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | }); | ||||||
| @ -1,11 +1,8 @@ | |||||||
| import React, { FC, useEffect, useRef, useState } from 'react'; | import React, { FC, useEffect, useRef, useState } from 'react'; | ||||||
| import { useTranslation } from 'react-i18next'; | import { useTranslation } from 'react-i18next'; | ||||||
| import { RunButton } from '../components/RunButton'; | import { Avatar, PresenceBadge, Textarea } from '@fluentui/react-components'; | ||||||
| import { Avatar, Divider, PresenceBadge, Text, Textarea } from '@fluentui/react-components'; |  | ||||||
| import commonStore, { ModelStatus } from '../stores/commonStore'; | import commonStore, { ModelStatus } from '../stores/commonStore'; | ||||||
| import { observer } from 'mobx-react-lite'; | import { observer } from 'mobx-react-lite'; | ||||||
| import { PresenceBadgeStatus } from '@fluentui/react-badge'; |  | ||||||
| import { ConfigSelector } from '../components/ConfigSelector'; |  | ||||||
| import { v4 as uuid } from 'uuid'; | import { v4 as uuid } from 'uuid'; | ||||||
| import classnames from 'classnames'; | import classnames from 'classnames'; | ||||||
| import { fetchEventSource } from '@microsoft/fetch-event-source'; | import { fetchEventSource } from '@microsoft/fetch-event-source'; | ||||||
| @ -17,6 +14,7 @@ import { ArrowCircleUp28Regular, Delete28Regular, RecordStop28Regular } from '@f | |||||||
| import { CopyButton } from '../components/CopyButton'; | import { CopyButton } from '../components/CopyButton'; | ||||||
| import { ReadButton } from '../components/ReadButton'; | import { ReadButton } from '../components/ReadButton'; | ||||||
| import { toast } from 'react-toastify'; | import { toast } from 'react-toastify'; | ||||||
|  | import { WorkHeader } from '../components/WorkHeader'; | ||||||
| 
 | 
 | ||||||
| export const userName = 'M E'; | export const userName = 'M E'; | ||||||
| export const botName = 'A I'; | export const botName = 'A I'; | ||||||
| @ -293,40 +291,10 @@ const ChatPanel: FC = observer(() => { | |||||||
|   ); |   ); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const statusText = { |  | ||||||
|   [ModelStatus.Offline]: 'Offline', |  | ||||||
|   [ModelStatus.Starting]: 'Starting', |  | ||||||
|   [ModelStatus.Loading]: 'Loading', |  | ||||||
|   [ModelStatus.Working]: 'Working' |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const badgeStatus: { [modelStatus: number]: PresenceBadgeStatus } = { |  | ||||||
|   [ModelStatus.Offline]: 'unknown', |  | ||||||
|   [ModelStatus.Starting]: 'away', |  | ||||||
|   [ModelStatus.Loading]: 'away', |  | ||||||
|   [ModelStatus.Working]: 'available' |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| export const Chat: FC = observer(() => { | export const Chat: FC = observer(() => { | ||||||
|   const { t } = useTranslation(); |  | ||||||
|   const port = commonStore.getCurrentModelConfig().apiParameters.apiPort; |  | ||||||
| 
 |  | ||||||
|   return ( |   return ( | ||||||
|     <div className="flex flex-col gap-1 p-2 h-full overflow-hidden"> |     <div className="flex flex-col gap-1 p-2 h-full overflow-hidden"> | ||||||
|       <div className="flex justify-between items-center"> |       <WorkHeader /> | ||||||
|         <div className="flex items-center gap-2"> |  | ||||||
|           <PresenceBadge status={badgeStatus[commonStore.status.modelStatus]} /> |  | ||||||
|           <Text size={100}>{t('Model Status') + ': ' + t(statusText[commonStore.status.modelStatus])}</Text> |  | ||||||
|         </div> |  | ||||||
|         <div className="flex items-center gap-2"> |  | ||||||
|           <ConfigSelector size="small" /> |  | ||||||
|           <RunButton iconMode /> |  | ||||||
|         </div> |  | ||||||
|       </div> |  | ||||||
|       <Text size={100}> |  | ||||||
|         {t('This tool\'s API is compatible with OpenAI API. It can be used with any ChatGPT tool you like. Go to the settings of some ChatGPT tool, replace the \'https://api.openai.com\' part in the API address with \'') + `http://127.0.0.1:${port}` + '\'.'} |  | ||||||
|       </Text> |  | ||||||
|       <Divider style={{ flexGrow: 0 }} /> |  | ||||||
|       <ChatPanel /> |       <ChatPanel /> | ||||||
|     </div> |     </div> | ||||||
|   ); |   ); | ||||||
|  | |||||||
							
								
								
									
										302
									
								
								frontend/src/pages/Completion.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										302
									
								
								frontend/src/pages/Completion.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,302 @@ | |||||||
|  | import React, { FC, useEffect, useRef } from 'react'; | ||||||
|  | import { observer } from 'mobx-react-lite'; | ||||||
|  | import { WorkHeader } from '../components/WorkHeader'; | ||||||
|  | import { Button, Dropdown, Input, Option, Textarea } from '@fluentui/react-components'; | ||||||
|  | import { Labeled } from '../components/Labeled'; | ||||||
|  | import { ValuedSlider } from '../components/ValuedSlider'; | ||||||
|  | import { useTranslation } from 'react-i18next'; | ||||||
|  | import { ApiParameters } from './Configs'; | ||||||
|  | import commonStore, { ModelStatus } from '../stores/commonStore'; | ||||||
|  | import { fetchEventSource } from '@microsoft/fetch-event-source'; | ||||||
|  | import { toast } from 'react-toastify'; | ||||||
|  | 
 | ||||||
|  | export type CompletionParams = Omit<ApiParameters, 'apiPort'> & { stop: string } | ||||||
|  | 
 | ||||||
|  | export type CompletionPreset = { | ||||||
|  |   name: string, | ||||||
|  |   prompt: string, | ||||||
|  |   params: CompletionParams | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const defaultPresets: CompletionPreset[] = [{ | ||||||
|  |   name: 'Writer', | ||||||
|  |   prompt: '以下是不朽的科幻史诗巨著,描写细腻,刻画了宏大的星际文明战争。\n第一章\n', | ||||||
|  |   params: { | ||||||
|  |     maxResponseToken: 4100, | ||||||
|  |     temperature: 1, | ||||||
|  |     topP: 0.5, | ||||||
|  |     presencePenalty: 0.4, | ||||||
|  |     frequencyPenalty: 0.4, | ||||||
|  |     stop: '' | ||||||
|  |   } | ||||||
|  | }, { | ||||||
|  |   name: 'Translator', | ||||||
|  |   prompt: '', | ||||||
|  |   params: { | ||||||
|  |     maxResponseToken: 4100, | ||||||
|  |     temperature: 1, | ||||||
|  |     topP: 0.5, | ||||||
|  |     presencePenalty: 0.4, | ||||||
|  |     frequencyPenalty: 0.4, | ||||||
|  |     stop: '' | ||||||
|  |   } | ||||||
|  | }, { | ||||||
|  |   name: 'Catgirl', | ||||||
|  |   prompt: '', | ||||||
|  |   params: { | ||||||
|  |     maxResponseToken: 4100, | ||||||
|  |     temperature: 1, | ||||||
|  |     topP: 0.5, | ||||||
|  |     presencePenalty: 0.4, | ||||||
|  |     frequencyPenalty: 0.4, | ||||||
|  |     stop: '' | ||||||
|  |   } | ||||||
|  | }, { | ||||||
|  |   name: 'Explain Code', | ||||||
|  |   prompt: '', | ||||||
|  |   params: { | ||||||
|  |     maxResponseToken: 4100, | ||||||
|  |     temperature: 1, | ||||||
|  |     topP: 0.5, | ||||||
|  |     presencePenalty: 0.4, | ||||||
|  |     frequencyPenalty: 0.4, | ||||||
|  |     stop: '' | ||||||
|  |   } | ||||||
|  | }, { | ||||||
|  |   name: 'Werewolf', | ||||||
|  |   prompt: '', | ||||||
|  |   params: { | ||||||
|  |     maxResponseToken: 4100, | ||||||
|  |     temperature: 1, | ||||||
|  |     topP: 0.5, | ||||||
|  |     presencePenalty: 0.4, | ||||||
|  |     frequencyPenalty: 0.4, | ||||||
|  |     stop: '' | ||||||
|  |   } | ||||||
|  | }, { | ||||||
|  |   name: 'Blank', | ||||||
|  |   prompt: '', | ||||||
|  |   params: { | ||||||
|  |     maxResponseToken: 4100, | ||||||
|  |     temperature: 1, | ||||||
|  |     topP: 0.5, | ||||||
|  |     presencePenalty: 0.4, | ||||||
|  |     frequencyPenalty: 0.4, | ||||||
|  |     stop: '' | ||||||
|  |   } | ||||||
|  | }]; | ||||||
|  | 
 | ||||||
|  | const CompletionPanel: FC = observer(() => { | ||||||
|  |   const { t } = useTranslation(); | ||||||
|  |   const inputRef = useRef<HTMLTextAreaElement>(null); | ||||||
|  |   const port = commonStore.getCurrentModelConfig().apiParameters.apiPort; | ||||||
|  |   const sseControllerRef = useRef<AbortController | null>(null); | ||||||
|  | 
 | ||||||
|  |   const scrollToBottom = () => { | ||||||
|  |     if (inputRef.current) | ||||||
|  |       inputRef.current.scrollTop = inputRef.current.scrollHeight; | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   useEffect(() => { | ||||||
|  |     if (inputRef.current) | ||||||
|  |       inputRef.current.style.height = '100%'; | ||||||
|  |     scrollToBottom(); | ||||||
|  |   }, []); | ||||||
|  | 
 | ||||||
|  |   if (!commonStore.completionPreset) | ||||||
|  |     commonStore.setCompletionPreset(defaultPresets[0]); | ||||||
|  | 
 | ||||||
|  |   const name = commonStore.completionPreset!.name; | ||||||
|  | 
 | ||||||
|  |   const prompt = commonStore.completionPreset!.prompt; | ||||||
|  |   const setPrompt = (prompt: string) => { | ||||||
|  |     commonStore.setCompletionPreset({ | ||||||
|  |       ...commonStore.completionPreset!, | ||||||
|  |       prompt | ||||||
|  |     }); | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   const params = commonStore.completionPreset!.params; | ||||||
|  |   const setParams = (newParams: Partial<CompletionParams>) => { | ||||||
|  |     commonStore.setCompletionPreset({ | ||||||
|  |       ...commonStore.completionPreset!, | ||||||
|  |       params: { | ||||||
|  |         ...commonStore.completionPreset!.params, | ||||||
|  |         ...newParams | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   const onSubmit = (prompt: string) => { | ||||||
|  |     if (commonStore.status.modelStatus === ModelStatus.Offline) { | ||||||
|  |       toast(t('Please click the button in the top right corner to start the model'), { type: 'warning' }); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     let answer = ''; | ||||||
|  |     sseControllerRef.current = new AbortController(); | ||||||
|  |     fetchEventSource(`http://127.0.0.1:${port}/completions`, // https://api.openai.com/v1/completions || http://127.0.0.1:${port}/completions
 | ||||||
|  |       { | ||||||
|  |         method: 'POST', | ||||||
|  |         headers: { | ||||||
|  |           'Content-Type': 'application/json', | ||||||
|  |           Authorization: `Bearer sk-` | ||||||
|  |         }, | ||||||
|  |         body: JSON.stringify({ | ||||||
|  |           prompt, | ||||||
|  |           stream: true, | ||||||
|  |           model: 'text-davinci-003', | ||||||
|  |           max_tokens: params.maxResponseToken, | ||||||
|  |           temperature: params.temperature, | ||||||
|  |           top_p: params.topP, | ||||||
|  |           presence_penalty: params.presencePenalty, | ||||||
|  |           frequency_penalty: params.frequencyPenalty, | ||||||
|  |           stop: params.stop || undefined | ||||||
|  |         }), | ||||||
|  |         signal: sseControllerRef.current?.signal, | ||||||
|  |         onmessage(e) { | ||||||
|  |           console.log('sse message', e); | ||||||
|  |           scrollToBottom(); | ||||||
|  |           if (e.data === '[DONE]') { | ||||||
|  |             commonStore.setCompletionGenerating(false); | ||||||
|  |             return; | ||||||
|  |           } | ||||||
|  |           let data; | ||||||
|  |           try { | ||||||
|  |             data = JSON.parse(e.data); | ||||||
|  |           } catch (error) { | ||||||
|  |             console.debug('json error', error); | ||||||
|  |             return; | ||||||
|  |           } | ||||||
|  |           if (data.choices && Array.isArray(data.choices) && data.choices.length > 0) { | ||||||
|  |             answer += data.choices[0].text; | ||||||
|  |             setPrompt(prompt + answer); | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         onclose() { | ||||||
|  |           console.log('Connection closed'); | ||||||
|  |         }, | ||||||
|  |         onerror(err) { | ||||||
|  |           commonStore.setCompletionGenerating(false); | ||||||
|  |           throw err; | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <div className="flex flex-col sm:flex-row gap-2 overflow-hidden grow"> | ||||||
|  |       <Textarea | ||||||
|  |         ref={inputRef} | ||||||
|  |         className="grow" | ||||||
|  |         value={prompt} | ||||||
|  |         onChange={(e) => setPrompt(e.target.value)} | ||||||
|  |       /> | ||||||
|  |       <div className="flex flex-col gap-1 max-h-48 sm:max-w-sm sm:max-h-full"> | ||||||
|  |         <Dropdown style={{ minWidth: 0 }} | ||||||
|  |           value={t(commonStore.completionPreset!.name)!} | ||||||
|  |           selectedOptions={[commonStore.completionPreset!.name]} | ||||||
|  |           onOptionSelect={(_, data) => { | ||||||
|  |             if (data.optionValue) { | ||||||
|  |               commonStore.setCompletionPreset(defaultPresets.find((preset) => preset.name === data.optionValue)!); | ||||||
|  |             } | ||||||
|  |           }}> | ||||||
|  |           { | ||||||
|  |             defaultPresets.map((preset) => | ||||||
|  |               <Option key={preset.name} value={preset.name}>{t(preset.name)!}</Option>) | ||||||
|  |           } | ||||||
|  |         </Dropdown> | ||||||
|  |         <div className="flex flex-col gap-1 overflow-x-hidden overflow-y-auto"> | ||||||
|  |           <Labeled flex breakline label={t('Max Response Token')} | ||||||
|  |             desc={t('By default, the maximum number of tokens that can be answered in a single response, it can be changed by the user by specifying API parameters.')} | ||||||
|  |             content={ | ||||||
|  |               <ValuedSlider value={params.maxResponseToken} min={100} max={8100} | ||||||
|  |                 step={400} | ||||||
|  |                 input | ||||||
|  |                 onChange={(e, data) => { | ||||||
|  |                   setParams({ | ||||||
|  |                     maxResponseToken: data.value | ||||||
|  |                   }); | ||||||
|  |                 }} /> | ||||||
|  |             } /> | ||||||
|  |           <Labeled flex breakline label={t('Temperature')} | ||||||
|  |             desc={t('Sampling temperature, the higher the stronger the randomness and creativity, while the lower, the more focused and deterministic it will be.')} | ||||||
|  |             content={ | ||||||
|  |               <ValuedSlider value={params.temperature} min={0} max={2} step={0.1} | ||||||
|  |                 input | ||||||
|  |                 onChange={(e, data) => { | ||||||
|  |                   setParams({ | ||||||
|  |                     temperature: data.value | ||||||
|  |                   }); | ||||||
|  |                 }} /> | ||||||
|  |             } /> | ||||||
|  |           <Labeled flex breakline label={t('Top_P')} | ||||||
|  |             desc={t('Consider the results of the top n% probability mass, 0.1 considers the top 10%, with higher quality but more conservative, 1 considers all results, with lower quality but more diverse.')} | ||||||
|  |             content={ | ||||||
|  |               <ValuedSlider value={params.topP} min={0} max={1} step={0.1} input | ||||||
|  |                 onChange={(e, data) => { | ||||||
|  |                   setParams({ | ||||||
|  |                     topP: data.value | ||||||
|  |                   }); | ||||||
|  |                 }} /> | ||||||
|  |             } /> | ||||||
|  |           <Labeled flex breakline label={t('Presence Penalty')} | ||||||
|  |             desc={t('Positive values penalize new tokens based on whether they appear in the text so far, increasing the model\'s likelihood to talk about new topics.')} | ||||||
|  |             content={ | ||||||
|  |               <ValuedSlider value={params.presencePenalty} min={-2} max={2} | ||||||
|  |                 step={0.1} input | ||||||
|  |                 onChange={(e, data) => { | ||||||
|  |                   setParams({ | ||||||
|  |                     presencePenalty: data.value | ||||||
|  |                   }); | ||||||
|  |                 }} /> | ||||||
|  |             } /> | ||||||
|  |           <Labeled flex breakline label={t('Frequency Penalty')} | ||||||
|  |             desc={t('Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model\'s likelihood to repeat the same line verbatim.')} | ||||||
|  |             content={ | ||||||
|  |               <ValuedSlider value={params.frequencyPenalty} min={-2} max={2} | ||||||
|  |                 step={0.1} input | ||||||
|  |                 onChange={(e, data) => { | ||||||
|  |                   setParams({ | ||||||
|  |                     frequencyPenalty: data.value | ||||||
|  |                   }); | ||||||
|  |                 }} /> | ||||||
|  |             } /> | ||||||
|  |           <Labeled flex breakline label={t('Stop Sequences')} | ||||||
|  |             desc={t('When this content appears in the response result, the generation will end.')} | ||||||
|  |             content={ | ||||||
|  |               <Input value={params.stop} | ||||||
|  |                 onChange={(e, data) => { | ||||||
|  |                   setParams({ | ||||||
|  |                     stop: data.value | ||||||
|  |                   }); | ||||||
|  |                 }} /> | ||||||
|  |             } /> | ||||||
|  |         </div> | ||||||
|  |         <div className="grow" /> | ||||||
|  |         <div className="flex justify-between gap-2"> | ||||||
|  |           <Button className="grow" onClick={() => { | ||||||
|  |             commonStore.setCompletionPreset(defaultPresets.find((preset) => preset.name === name)!); | ||||||
|  |           }}>{t('Reset')}</Button> | ||||||
|  |           <Button className="grow" appearance="primary" onClick={() => { | ||||||
|  |             if (commonStore.completionGenerating) { | ||||||
|  |               sseControllerRef.current?.abort(); | ||||||
|  |               commonStore.setCompletionGenerating(false); | ||||||
|  |             } else { | ||||||
|  |               commonStore.setCompletionGenerating(true); | ||||||
|  |               onSubmit(prompt); | ||||||
|  |             } | ||||||
|  |           }}>{!commonStore.completionGenerating ? t('Generate') : t('Stop')}</Button> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | export const Completion: FC = observer(() => { | ||||||
|  |   return ( | ||||||
|  |     <div className="flex flex-col gap-1 p-2 h-full overflow-hidden"> | ||||||
|  |       <WorkHeader /> | ||||||
|  |       <CompletionPanel /> | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | }); | ||||||
| @ -643,7 +643,7 @@ export const Configs: FC = observer(() => { | |||||||
|                         }); |                         }); | ||||||
|                       }} /> |                       }} /> | ||||||
|                   } /> |                   } /> | ||||||
|                 <Labeled label={t('Max Response Token *')} |                 <Labeled label={t('Max Response Token') + ' *'} | ||||||
|                   desc={t('By default, the maximum number of tokens that can be answered in a single response, it can be changed by the user by specifying API parameters.')} |                   desc={t('By default, the maximum number of tokens that can be answered in a single response, it can be changed by the user by specifying API parameters.')} | ||||||
|                   content={ |                   content={ | ||||||
|                     <ValuedSlider value={selectedConfig.apiParameters.maxResponseToken} min={100} max={8100} |                     <ValuedSlider value={selectedConfig.apiParameters.maxResponseToken} min={100} max={8100} | ||||||
| @ -655,7 +655,7 @@ export const Configs: FC = observer(() => { | |||||||
|                         }); |                         }); | ||||||
|                       }} /> |                       }} /> | ||||||
|                   } /> |                   } /> | ||||||
|                 <Labeled label={t('Temperature *')} |                 <Labeled label={t('Temperature') + ' *'} | ||||||
|                   desc={t('Sampling temperature, the higher the stronger the randomness and creativity, while the lower, the more focused and deterministic it will be.')} |                   desc={t('Sampling temperature, the higher the stronger the randomness and creativity, while the lower, the more focused and deterministic it will be.')} | ||||||
|                   content={ |                   content={ | ||||||
|                     <ValuedSlider value={selectedConfig.apiParameters.temperature} min={0} max={2} step={0.1} |                     <ValuedSlider value={selectedConfig.apiParameters.temperature} min={0} max={2} step={0.1} | ||||||
| @ -666,7 +666,7 @@ export const Configs: FC = observer(() => { | |||||||
|                         }); |                         }); | ||||||
|                       }} /> |                       }} /> | ||||||
|                   } /> |                   } /> | ||||||
|                 <Labeled label={t('Top_P *')} |                 <Labeled label={t('Top_P') + ' *'} | ||||||
|                   desc={t('Consider the results of the top n% probability mass, 0.1 considers the top 10%, with higher quality but more conservative, 1 considers all results, with lower quality but more diverse.')} |                   desc={t('Consider the results of the top n% probability mass, 0.1 considers the top 10%, with higher quality but more conservative, 1 considers all results, with lower quality but more diverse.')} | ||||||
|                   content={ |                   content={ | ||||||
|                     <ValuedSlider value={selectedConfig.apiParameters.topP} min={0} max={1} step={0.1} input |                     <ValuedSlider value={selectedConfig.apiParameters.topP} min={0} max={1} step={0.1} input | ||||||
| @ -676,7 +676,7 @@ export const Configs: FC = observer(() => { | |||||||
|                         }); |                         }); | ||||||
|                       }} /> |                       }} /> | ||||||
|                   } /> |                   } /> | ||||||
|                 <Labeled label={t('Presence Penalty *')} |                 <Labeled label={t('Presence Penalty') + ' *'} | ||||||
|                   desc={t('Positive values penalize new tokens based on whether they appear in the text so far, increasing the model\'s likelihood to talk about new topics.')} |                   desc={t('Positive values penalize new tokens based on whether they appear in the text so far, increasing the model\'s likelihood to talk about new topics.')} | ||||||
|                   content={ |                   content={ | ||||||
|                     <ValuedSlider value={selectedConfig.apiParameters.presencePenalty} min={-2} max={2} |                     <ValuedSlider value={selectedConfig.apiParameters.presencePenalty} min={-2} max={2} | ||||||
| @ -687,7 +687,7 @@ export const Configs: FC = observer(() => { | |||||||
|                         }); |                         }); | ||||||
|                       }} /> |                       }} /> | ||||||
|                   } /> |                   } /> | ||||||
|                 <Labeled label={t('Frequency Penalty *')} |                 <Labeled label={t('Frequency Penalty') + ' *'} | ||||||
|                   desc={t('Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model\'s likelihood to repeat the same line verbatim.')} |                   desc={t('Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model\'s likelihood to repeat the same line verbatim.')} | ||||||
|                   content={ |                   content={ | ||||||
|                     <ValuedSlider value={selectedConfig.apiParameters.frequencyPenalty} min={-2} max={2} |                     <ValuedSlider value={selectedConfig.apiParameters.frequencyPenalty} min={-2} max={2} | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ import { Configs } from './Configs'; | |||||||
| import { | import { | ||||||
|   ArrowDownload20Regular, |   ArrowDownload20Regular, | ||||||
|   Chat20Regular, |   Chat20Regular, | ||||||
|  |   ClipboardEdit20Regular, | ||||||
|   DataUsageSettings20Regular, |   DataUsageSettings20Regular, | ||||||
|   DocumentSettings20Regular, |   DocumentSettings20Regular, | ||||||
|   Home20Regular, |   Home20Regular, | ||||||
| @ -17,6 +18,7 @@ import { Train } from './Train'; | |||||||
| import { Settings } from './Settings'; | import { Settings } from './Settings'; | ||||||
| import { About } from './About'; | import { About } from './About'; | ||||||
| import { Downloads } from './Downloads'; | import { Downloads } from './Downloads'; | ||||||
|  | import { Completion } from './Completion'; | ||||||
| 
 | 
 | ||||||
| type NavigationItem = { | type NavigationItem = { | ||||||
|   label: string; |   label: string; | ||||||
| @ -41,6 +43,13 @@ export const pages: NavigationItem[] = [ | |||||||
|     element: <Chat />, |     element: <Chat />, | ||||||
|     top: true |     top: true | ||||||
|   }, |   }, | ||||||
|  |   { | ||||||
|  |     label: 'Completion', | ||||||
|  |     path: '/completion', | ||||||
|  |     icon: <ClipboardEdit20Regular />, | ||||||
|  |     element: <Completion />, | ||||||
|  |     top: true | ||||||
|  |   }, | ||||||
|   { |   { | ||||||
|     label: 'Configs', |     label: 'Configs', | ||||||
|     path: '/configs', |     path: '/configs', | ||||||
|  | |||||||
| @ -10,6 +10,7 @@ import { SettingsType } from '../pages/Settings'; | |||||||
| import { IntroductionContent } from '../pages/Home'; | import { IntroductionContent } from '../pages/Home'; | ||||||
| import { AboutContent } from '../pages/About'; | import { AboutContent } from '../pages/About'; | ||||||
| import i18n from 'i18next'; | import i18n from 'i18next'; | ||||||
|  | import { CompletionPreset } from '../pages/Completion'; | ||||||
| 
 | 
 | ||||||
| export enum ModelStatus { | export enum ModelStatus { | ||||||
|   Offline, |   Offline, | ||||||
| @ -37,6 +38,9 @@ class CommonStore { | |||||||
|   // chat
 |   // chat
 | ||||||
|   conversations: Conversations = {}; |   conversations: Conversations = {}; | ||||||
|   conversationsOrder: string[] = []; |   conversationsOrder: string[] = []; | ||||||
|  |   // completion
 | ||||||
|  |   completionPreset: CompletionPreset | null = null; | ||||||
|  |   completionGenerating: boolean = false; | ||||||
|   // configs
 |   // configs
 | ||||||
|   currentModelConfigIndex: number = 0; |   currentModelConfigIndex: number = 0; | ||||||
|   modelConfigs: ModelConfig[] = []; |   modelConfigs: ModelConfig[] = []; | ||||||
| @ -155,6 +159,14 @@ class CommonStore { | |||||||
|   setConversationsOrder = (value: string[]) => { |   setConversationsOrder = (value: string[]) => { | ||||||
|     this.conversationsOrder = value; |     this.conversationsOrder = value; | ||||||
|   }; |   }; | ||||||
|  | 
 | ||||||
|  |   setCompletionPreset(value: CompletionPreset) { | ||||||
|  |     this.completionPreset = value; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   setCompletionGenerating(value: boolean) { | ||||||
|  |     this.completionGenerating = value; | ||||||
|  |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export default new CommonStore(); | export default new CommonStore(); | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user