4.6.8-alpha (#804)

* perf: redirect request and err log replace

perf: dataset openapi

feat: session

fix: retry input error

feat: 468 doc

sub page

feat: standard sub

perf: rerank tip

perf: rerank tip

perf: api sdk

perf: openapi

sub plan

perf: sub ui

fix: ts

* perf: init log

* fix: variable select

* sub page

* icon

* perf: llm model config

* perf: menu ux

* perf: system store

* perf: publish app name

* fix: init data

* perf: flow edit ux

* fix: value type format and ux

* fix prompt editor default value (#13)

* fix prompt editor default value

* fix prompt editor update when not focus

* add key with variable

---------

Co-authored-by: Archer <545436317@qq.com>

* fix: value type

* doc

* i18n

* import path

* home page

* perf: mongo session running

* fix: ts

* perf: use toast

* perf: flow edit

* perf: sse response

* slider ui

* fetch error

* fix prompt editor rerender when not focus by key defaultvalue (#14)

* perf: prompt editor

* feat: dataset search concat

* perf: doc

* fix:ts

* perf: doc

* fix json editor onblur value (#15)

* faq

* vector model default config

* ipv6

---------

Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com>
This commit is contained in:
Archer
2024-02-01 21:57:41 +08:00
committed by GitHub
parent fc19c4cf09
commit 34602b25df
285 changed files with 10345 additions and 11223 deletions

View File

@@ -1,16 +1,21 @@
import { sseResponseEventEnum } from '@fastgpt/service/common/response/constant';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { parseStreamChunk, SSEParseData } from '@/utils/sse';
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type.d';
import { StartChatFnProps } from '@/components/ChatBox';
import { getToken } from '@/web/support/user/auth';
import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
import dayjs from 'dayjs';
import {
// refer to https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web
EventStreamContentType,
fetchEventSource
} from '@fortaine/fetch-event-source';
type StreamFetchProps = {
url?: string;
data: Record<string, any>;
onMessage: StartChatFnProps['generatingMessage'];
abortSignal: AbortController;
abortCtrl: AbortController;
};
type StreamResponseType = {
responseText: string;
@@ -20,105 +25,158 @@ export const streamFetch = ({
url = '/api/v1/chat/completions',
data,
onMessage,
abortSignal
abortCtrl
}: StreamFetchProps) =>
new Promise<StreamResponseType>(async (resolve, reject) => {
const timeoutId = setTimeout(() => {
abortCtrl.abort('Time out');
}, 60000);
// response data
let responseText = '';
let remainText = '';
let errMsg = '';
let responseData: ChatHistoryItemResType[] = [];
let finished = false;
const finish = () => {
if (errMsg) {
return failedFinish();
}
return resolve({
responseText,
responseData
});
};
const failedFinish = (err?: any) => {
finished = true;
reject({
message: getErrText(err, errMsg || '响应过程出现异常~'),
responseText
});
};
// animate response to make it looks smooth
function animateResponseText() {
// abort message
if (abortCtrl.signal.aborted) {
onMessage({ text: remainText });
responseText += remainText;
return finish();
}
if (remainText) {
const fetchCount = Math.max(1, Math.round(remainText.length / 60));
const fetchText = remainText.slice(0, fetchCount);
onMessage({ text: fetchText });
responseText += fetchText;
remainText = remainText.slice(fetchCount);
}
if (finished && !remainText) {
return finish();
}
requestAnimationFrame(animateResponseText);
}
// start animation
animateResponseText();
try {
const response = await window.fetch(url, {
// auto complete variables
const variables = data?.variables || {};
variables.cTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
const requestData = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
token: getToken()
},
signal: abortSignal.signal,
signal: abortCtrl.signal,
body: JSON.stringify({
...data,
variables,
detail: true,
stream: true
})
});
if (!response?.body || !response?.ok) {
throw new Error('Request Error');
}
const reader = response.body?.getReader();
// response data
let responseText = '';
let errMsg = '';
let responseData: ChatHistoryItemResType[] = [];
const parseData = new SSEParseData();
const read = async () => {
try {
const { done, value } = await reader.read();
if (done) {
if (response.status === 200 && !errMsg) {
return resolve({
responseText,
responseData
});
} else {
return reject({
message: errMsg || '响应过程出现异常~',
responseText
});
}
}
const chunkResponse = parseStreamChunk(value);
chunkResponse.forEach((item) => {
// parse json data
const { eventName, data } = parseData.parse(item);
if (!eventName || !data) return;
if (eventName === sseResponseEventEnum.answer && data !== '[DONE]') {
const answer: string = data?.choices?.[0]?.delta?.content || '';
onMessage({ text: answer });
responseText += answer;
} else if (
eventName === sseResponseEventEnum.moduleStatus &&
data?.name &&
data?.status
) {
onMessage(data);
} else if (
eventName === sseResponseEventEnum.appStreamResponse &&
Array.isArray(data)
) {
responseData = data;
} else if (eventName === sseResponseEventEnum.error) {
errMsg = getErrText(data, '流响应错误');
}
});
read();
} catch (err: any) {
if (abortSignal.signal.aborted) {
return resolve({
responseText,
responseData
});
}
reject({
responseText,
message: getErrText(err, '请求异常')
});
}
};
read();
// send request
await fetchEventSource(url, {
...requestData,
async onopen(res) {
clearTimeout(timeoutId);
const contentType = res.headers.get('content-type');
// not stream
if (contentType?.startsWith('text/plain')) {
return failedFinish(await res.clone().text());
}
// failed stream
if (
!res.ok ||
!res.headers.get('content-type')?.startsWith(EventStreamContentType) ||
res.status !== 200
) {
try {
failedFinish(await res.clone().json());
} catch {
failedFinish(await res.clone().text());
}
}
},
onmessage({ event, data }) {
if (data === '[DONE]') {
return;
}
// parse text to json
const parseJson = (() => {
try {
return JSON.parse(data);
} catch (error) {
return {};
}
})();
if (event === sseResponseEventEnum.answer) {
const answer: string = parseJson?.choices?.[0]?.delta?.content || '';
remainText += answer;
} else if (
event === sseResponseEventEnum.moduleStatus &&
parseJson?.name &&
parseJson?.status
) {
onMessage(parseJson);
} else if (event === sseResponseEventEnum.appStreamResponse && Array.isArray(parseJson)) {
responseData = parseJson;
} else if (event === sseResponseEventEnum.error) {
errMsg = getErrText(parseJson, '流响应错误');
}
},
onclose() {
finished = true;
},
onerror(e) {
clearTimeout(timeoutId);
failedFinish(getErrText(e));
},
openWhenHidden: true
});
} catch (err: any) {
if (abortSignal.signal.aborted) {
return resolve({
responseText: '',
responseData: []
});
clearTimeout(timeoutId);
if (abortCtrl.signal.aborted) {
finished = true;
return;
}
console.log(err, 'fetch error');
reject(getErrText(err, '请求异常'));
failedFinish(err);
}
});