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