mirror of
https://github.com/labring/FastGPT.git
synced 2025-08-01 03:48:24 +00:00
V4.6.5-alpha (#609)
This commit is contained in:
@@ -40,17 +40,19 @@ export const dispatchClassifyQuestion = async (props: Props): Promise<CQResponse
|
||||
|
||||
const cqModel = getCQModel(model);
|
||||
|
||||
const chatHistories = getHistories(history, histories);
|
||||
|
||||
const { arg, tokens } = await (async () => {
|
||||
if (cqModel.functionCall) {
|
||||
return functionCall({
|
||||
...props,
|
||||
histories: getHistories(history, histories),
|
||||
histories: chatHistories,
|
||||
cqModel
|
||||
});
|
||||
}
|
||||
return completions({
|
||||
...props,
|
||||
histories: getHistories(history, histories),
|
||||
histories: chatHistories,
|
||||
cqModel
|
||||
});
|
||||
})();
|
||||
@@ -65,7 +67,8 @@ export const dispatchClassifyQuestion = async (props: Props): Promise<CQResponse
|
||||
query: userChatInput,
|
||||
tokens,
|
||||
cqList: agents,
|
||||
cqResult: result.value
|
||||
cqResult: result.value,
|
||||
contextTotalLen: chatHistories.length + 2
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -115,7 +118,7 @@ ${systemPrompt}
|
||||
required: ['type']
|
||||
}
|
||||
};
|
||||
const ai = getAIApi(user.openaiAccount, 48000);
|
||||
const ai = getAIApi(user.openaiAccount, 480000);
|
||||
|
||||
const response = await ai.chat.completions.create({
|
||||
model: cqModel.model,
|
||||
|
@@ -38,18 +38,19 @@ export async function dispatchContentExtract(props: Props): Promise<Response> {
|
||||
}
|
||||
|
||||
const extractModel = global.extractModels[0];
|
||||
const chatHistories = getHistories(history, histories);
|
||||
|
||||
const { arg, tokens } = await (async () => {
|
||||
if (extractModel.functionCall) {
|
||||
return functionCall({
|
||||
...props,
|
||||
histories: getHistories(history, histories),
|
||||
histories: chatHistories,
|
||||
extractModel
|
||||
});
|
||||
}
|
||||
return completions({
|
||||
...props,
|
||||
histories: getHistories(history, histories),
|
||||
histories: chatHistories,
|
||||
extractModel
|
||||
});
|
||||
})();
|
||||
@@ -84,7 +85,8 @@ export async function dispatchContentExtract(props: Props): Promise<Response> {
|
||||
query: content,
|
||||
tokens,
|
||||
extractDescription: description,
|
||||
extractResult: arg
|
||||
extractResult: arg,
|
||||
contextTotalLen: chatHistories.length + 2
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -100,11 +102,11 @@ async function functionCall({
|
||||
{
|
||||
obj: ChatRoleEnum.Human,
|
||||
value: `<任务描述>
|
||||
${description || '根据用户要求提取适当的 JSON 字符串。'}
|
||||
${description || '根据用户要求获取适当的 JSON 字符串。'}
|
||||
|
||||
- 如果字段为空,你返回空字符串。
|
||||
- 不要换行。
|
||||
- 结合历史记录和文本进行提取。
|
||||
- 结合历史记录和文本进行获取。
|
||||
</任务描述>
|
||||
|
||||
<文本>
|
||||
@@ -128,7 +130,8 @@ ${content}
|
||||
extractKeys.forEach((item) => {
|
||||
properties[item.key] = {
|
||||
type: 'string',
|
||||
description: item.desc
|
||||
description: item.desc,
|
||||
...(item.enum ? { enum: item.enum.split('\n') } : {})
|
||||
};
|
||||
});
|
||||
|
||||
@@ -192,7 +195,9 @@ async function completions({
|
||||
json: extractKeys
|
||||
.map(
|
||||
(item) =>
|
||||
`{"key":"${item.key}", "description":"${item.required}", "required":${item.required}}}`
|
||||
`{"key":"${item.key}", "description":"${item.required}", "required":${item.required}${
|
||||
item.enum ? `, "enum":"[${item.enum.split('\n')}]"` : ''
|
||||
}}`
|
||||
)
|
||||
.join('\n'),
|
||||
text: `${histories.map((item) => `${item.obj}:${item.value}`).join('\n')}
|
||||
|
@@ -202,7 +202,8 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
|
||||
query: userChatInput,
|
||||
maxToken: max_tokens,
|
||||
quoteList: filterQuoteQA,
|
||||
historyPreview: getHistoryPreview(completeMessages)
|
||||
historyPreview: getHistoryPreview(completeMessages),
|
||||
contextTotalLen: completeMessages.length
|
||||
},
|
||||
history: completeMessages
|
||||
};
|
||||
|
@@ -26,6 +26,21 @@ import { dispatchRunPlugin } from './plugin/run';
|
||||
import { dispatchPluginInput } from './plugin/runInput';
|
||||
import { dispatchPluginOutput } from './plugin/runOutput';
|
||||
|
||||
const callbackMap: Record<string, Function> = {
|
||||
[FlowNodeTypeEnum.historyNode]: dispatchHistory,
|
||||
[FlowNodeTypeEnum.questionInput]: dispatchChatInput,
|
||||
[FlowNodeTypeEnum.answerNode]: dispatchAnswer,
|
||||
[FlowNodeTypeEnum.chatNode]: dispatchChatCompletion,
|
||||
[FlowNodeTypeEnum.datasetSearchNode]: dispatchDatasetSearch,
|
||||
[FlowNodeTypeEnum.classifyQuestion]: dispatchClassifyQuestion,
|
||||
[FlowNodeTypeEnum.contentExtract]: dispatchContentExtract,
|
||||
[FlowNodeTypeEnum.httpRequest]: dispatchHttpRequest,
|
||||
[FlowNodeTypeEnum.runApp]: dispatchAppRequest,
|
||||
[FlowNodeTypeEnum.pluginModule]: dispatchRunPlugin,
|
||||
[FlowNodeTypeEnum.pluginInput]: dispatchPluginInput,
|
||||
[FlowNodeTypeEnum.pluginOutput]: dispatchPluginOutput
|
||||
};
|
||||
|
||||
/* running */
|
||||
export async function dispatchModules({
|
||||
res,
|
||||
@@ -111,6 +126,7 @@ export async function dispatchModules({
|
||||
Object.entries(data).map(([key, val]: any) => {
|
||||
updateInputValue(key, val);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
function moduleOutput(
|
||||
@@ -119,6 +135,7 @@ export async function dispatchModules({
|
||||
): Promise<any> {
|
||||
pushStore(module, result);
|
||||
|
||||
//
|
||||
const nextRunModules: RunningModuleItemType[] = [];
|
||||
|
||||
// Assign the output value to the next module
|
||||
@@ -141,18 +158,19 @@ export async function dispatchModules({
|
||||
});
|
||||
});
|
||||
|
||||
return checkModulesCanRun(nextRunModules);
|
||||
}
|
||||
function checkModulesCanRun(modules: RunningModuleItemType[] = []) {
|
||||
// Ensure the uniqueness of running modules
|
||||
const set = new Set<string>();
|
||||
const filterModules = modules.filter((module) => {
|
||||
const filterModules = nextRunModules.filter((module) => {
|
||||
if (set.has(module.moduleId)) return false;
|
||||
set.add(module.moduleId);
|
||||
return true;
|
||||
});
|
||||
|
||||
return checkModulesCanRun(filterModules);
|
||||
}
|
||||
function checkModulesCanRun(modules: RunningModuleItemType[] = []) {
|
||||
return Promise.all(
|
||||
filterModules.map((module) => {
|
||||
modules.map((module) => {
|
||||
if (!module.inputs.find((item: any) => item.value === undefined)) {
|
||||
moduleInput(module, { [ModuleInputKeyEnum.switch]: undefined });
|
||||
return moduleRun(module);
|
||||
@@ -192,20 +210,6 @@ export async function dispatchModules({
|
||||
};
|
||||
|
||||
const dispatchRes: Record<string, any> = await (async () => {
|
||||
const callbackMap: Record<string, Function> = {
|
||||
[FlowNodeTypeEnum.historyNode]: dispatchHistory,
|
||||
[FlowNodeTypeEnum.questionInput]: dispatchChatInput,
|
||||
[FlowNodeTypeEnum.answerNode]: dispatchAnswer,
|
||||
[FlowNodeTypeEnum.chatNode]: dispatchChatCompletion,
|
||||
[FlowNodeTypeEnum.datasetSearchNode]: dispatchDatasetSearch,
|
||||
[FlowNodeTypeEnum.classifyQuestion]: dispatchClassifyQuestion,
|
||||
[FlowNodeTypeEnum.contentExtract]: dispatchContentExtract,
|
||||
[FlowNodeTypeEnum.httpRequest]: dispatchHttpRequest,
|
||||
[FlowNodeTypeEnum.runApp]: dispatchAppRequest,
|
||||
[FlowNodeTypeEnum.pluginModule]: dispatchRunPlugin,
|
||||
[FlowNodeTypeEnum.pluginInput]: dispatchPluginInput,
|
||||
[FlowNodeTypeEnum.pluginOutput]: dispatchPluginOutput
|
||||
};
|
||||
if (callbackMap[module.flowType]) {
|
||||
return callbackMap[module.flowType](props);
|
||||
}
|
||||
@@ -232,6 +236,11 @@ export async function dispatchModules({
|
||||
|
||||
// start process width initInput
|
||||
const initModules = runningModules.filter((item) => initRunningModuleType[item.flowType]);
|
||||
|
||||
// runningModules.forEach((item) => {
|
||||
// console.log(item);
|
||||
// });
|
||||
|
||||
initModules.map((module) =>
|
||||
moduleInput(module, {
|
||||
...startParams,
|
||||
|
@@ -1,7 +1,11 @@
|
||||
import type { ModuleDispatchProps } from '@/types/core/chat/type';
|
||||
import { dispatchModules } from '../index';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
|
||||
import { ModuleInputKeyEnum, ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
|
||||
import {
|
||||
DYNAMIC_INPUT_KEY,
|
||||
ModuleInputKeyEnum,
|
||||
ModuleOutputKeyEnum
|
||||
} from '@fastgpt/global/core/module/constants';
|
||||
import type { moduleDispatchResType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { getPluginRuntimeById } from '@fastgpt/service/core/plugin/controller';
|
||||
import { authPluginCanUse } from '@fastgpt/service/support/permission/auth/plugin';
|
||||
@@ -29,13 +33,37 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
|
||||
await authPluginCanUse({ id: pluginId, teamId, tmbId });
|
||||
const plugin = await getPluginRuntimeById(pluginId);
|
||||
|
||||
// concat dynamic inputs
|
||||
const inputModule = plugin.modules.find((item) => item.flowType === FlowNodeTypeEnum.pluginInput);
|
||||
if (!inputModule) return Promise.reject('Plugin error, It has no set input.');
|
||||
const hasDynamicInput = inputModule.inputs.find((input) => input.key === DYNAMIC_INPUT_KEY);
|
||||
|
||||
const startParams: Record<string, any> = (() => {
|
||||
if (!hasDynamicInput) return data;
|
||||
|
||||
const params: Record<string, any> = {
|
||||
[DYNAMIC_INPUT_KEY]: {}
|
||||
};
|
||||
|
||||
for (const key in data) {
|
||||
const input = inputModule.inputs.find((input) => input.key === key);
|
||||
if (input) {
|
||||
params[key] = data[key];
|
||||
} else {
|
||||
params[DYNAMIC_INPUT_KEY][key] = data[key];
|
||||
}
|
||||
}
|
||||
|
||||
return params;
|
||||
})();
|
||||
|
||||
const { responseData, answerText } = await dispatchModules({
|
||||
...props,
|
||||
modules: plugin.modules.map((module) => ({
|
||||
...module,
|
||||
showStatus: false
|
||||
})),
|
||||
startParams: data
|
||||
startParams
|
||||
});
|
||||
|
||||
const output = responseData.find((item) => item.moduleType === FlowNodeTypeEnum.pluginOutput);
|
||||
@@ -46,6 +74,7 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
|
||||
|
||||
return {
|
||||
answerText,
|
||||
// responseData, // debug
|
||||
responseData: {
|
||||
moduleLogo: plugin.avatar,
|
||||
price: responseData.reduce((sum, item) => sum + (item.price || 0), 0),
|
||||
|
@@ -1,9 +1,14 @@
|
||||
import type { moduleDispatchResType } from '@fastgpt/global/core/chat/type.d';
|
||||
import type { ModuleDispatchProps } from '@/types/core/chat/type';
|
||||
import { ModuleInputKeyEnum, ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
|
||||
import axios from 'axios';
|
||||
import { flatDynamicParams } from '../utils';
|
||||
|
||||
export type HttpRequestProps = ModuleDispatchProps<{
|
||||
[ModuleInputKeyEnum.httpUrl]: string;
|
||||
[ModuleInputKeyEnum.abandon_httpUrl]: string;
|
||||
[ModuleInputKeyEnum.httpMethod]: string;
|
||||
[ModuleInputKeyEnum.httpReqUrl]: string;
|
||||
[ModuleInputKeyEnum.httpHeader]: string;
|
||||
[key: string]: any;
|
||||
}>;
|
||||
export type HttpResponse = {
|
||||
@@ -12,39 +17,98 @@ export type HttpResponse = {
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
export const dispatchHttpRequest = async (props: Record<string, any>): Promise<HttpResponse> => {
|
||||
const {
|
||||
export const dispatchHttpRequest = async (props: HttpRequestProps): Promise<HttpResponse> => {
|
||||
let {
|
||||
appId,
|
||||
chatId,
|
||||
variables,
|
||||
inputs: { url, ...body }
|
||||
} = props as HttpRequestProps;
|
||||
inputs: {
|
||||
system_httpMethod: httpMethod,
|
||||
url: abandonUrl,
|
||||
system_httpReqUrl: httpReqUrl,
|
||||
system_httpHeader: httpHeader,
|
||||
...body
|
||||
}
|
||||
} = props;
|
||||
|
||||
const requestBody = {
|
||||
...body,
|
||||
chatId,
|
||||
variables
|
||||
};
|
||||
body = flatDynamicParams(body);
|
||||
|
||||
const { requestMethod, requestUrl, requestHeader, requestBody, requestQuery } = await (() => {
|
||||
// 2024-2-12 clear
|
||||
if (abandonUrl) {
|
||||
return {
|
||||
requestMethod: 'POST',
|
||||
requestUrl: abandonUrl,
|
||||
requestHeader: httpHeader,
|
||||
requestBody: {
|
||||
...body,
|
||||
appId,
|
||||
chatId,
|
||||
variables
|
||||
},
|
||||
requestQuery: {}
|
||||
};
|
||||
}
|
||||
if (httpReqUrl) {
|
||||
return {
|
||||
requestMethod: httpMethod,
|
||||
requestUrl: httpReqUrl,
|
||||
requestHeader: httpHeader,
|
||||
requestBody: {
|
||||
appId,
|
||||
chatId,
|
||||
variables,
|
||||
data: body
|
||||
},
|
||||
requestQuery: {
|
||||
appId,
|
||||
chatId,
|
||||
...variables,
|
||||
...body
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return Promise.reject('url is empty');
|
||||
})();
|
||||
|
||||
const formatBody = transformFlatJson({ ...requestBody });
|
||||
|
||||
// parse header
|
||||
const headers = await (() => {
|
||||
try {
|
||||
if (!requestHeader) return {};
|
||||
return JSON.parse(requestHeader);
|
||||
} catch (error) {
|
||||
return Promise.reject('Header 为非法 JSON 格式');
|
||||
}
|
||||
})();
|
||||
|
||||
try {
|
||||
const response = await fetchData({
|
||||
url,
|
||||
body: requestBody
|
||||
method: requestMethod,
|
||||
url: requestUrl,
|
||||
headers,
|
||||
body: formatBody,
|
||||
query: requestQuery
|
||||
});
|
||||
|
||||
return {
|
||||
responseData: {
|
||||
price: 0,
|
||||
body: requestBody,
|
||||
body: formatBody,
|
||||
httpResult: response
|
||||
},
|
||||
...response
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
return {
|
||||
[ModuleOutputKeyEnum.failed]: true,
|
||||
responseData: {
|
||||
price: 0,
|
||||
body: requestBody,
|
||||
body: formatBody,
|
||||
httpResult: { error }
|
||||
}
|
||||
};
|
||||
@@ -52,19 +116,97 @@ export const dispatchHttpRequest = async (props: Record<string, any>): Promise<H
|
||||
};
|
||||
|
||||
async function fetchData({
|
||||
method,
|
||||
url,
|
||||
body
|
||||
headers,
|
||||
body,
|
||||
query
|
||||
}: {
|
||||
method: string;
|
||||
url: string;
|
||||
headers: Record<string, any>;
|
||||
body: Record<string, any>;
|
||||
query: Record<string, any>;
|
||||
}): Promise<Record<string, any>> {
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
const { data: response } = await axios<Record<string, any>>({
|
||||
method,
|
||||
baseURL: `http://${process.env.HOSTNAME || 'localhost'}:${process.env.PORT || 3000}`,
|
||||
url,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
'Content-Type': 'application/json',
|
||||
...headers
|
||||
},
|
||||
body: JSON.stringify(body)
|
||||
}).then((res) => res.json());
|
||||
params: method === 'GET' ? query : {},
|
||||
data: method === 'POST' ? body : {}
|
||||
});
|
||||
|
||||
return response;
|
||||
/*
|
||||
parse the json:
|
||||
{
|
||||
user: {
|
||||
name: 'xxx',
|
||||
age: 12
|
||||
}
|
||||
psw: 'xxx'
|
||||
}
|
||||
|
||||
result: {
|
||||
'user': {
|
||||
name: 'xxx',
|
||||
age: 12
|
||||
},
|
||||
'user.name': 'xxx',
|
||||
'user.age': 12,
|
||||
'psw': 'xxx'
|
||||
}
|
||||
*/
|
||||
const parseJson = (obj: Record<string, any>, prefix = '') => {
|
||||
let result: Record<string, any> = {};
|
||||
for (const key in obj) {
|
||||
if (typeof obj[key] === 'object') {
|
||||
result[key] = obj[key];
|
||||
result = {
|
||||
...result,
|
||||
...parseJson(obj[key], `${prefix}${key}.`)
|
||||
};
|
||||
} else {
|
||||
result[`${prefix}${key}`] = obj[key];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
return parseJson(response);
|
||||
}
|
||||
|
||||
function transformFlatJson(obj: Record<string, any>) {
|
||||
for (let key in obj) {
|
||||
if (typeof obj[key] === 'object') {
|
||||
transformFlatJson(obj[key]);
|
||||
}
|
||||
if (key.includes('.')) {
|
||||
let parts = key.split('.');
|
||||
if (parts.length <= 1) continue;
|
||||
|
||||
const firstKey = parts.shift();
|
||||
|
||||
if (!firstKey) continue;
|
||||
|
||||
const lastKey = parts.join('.');
|
||||
|
||||
if (obj[firstKey]) {
|
||||
obj[firstKey] = {
|
||||
...obj[firstKey],
|
||||
[lastKey]: obj[key]
|
||||
};
|
||||
} else {
|
||||
obj[firstKey] = { [lastKey]: obj[key] };
|
||||
}
|
||||
|
||||
transformFlatJson(obj[firstKey]);
|
||||
|
||||
delete obj[key];
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@ import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { sseResponseEventEnum } from '@fastgpt/service/common/response/constant';
|
||||
import { textAdaptGptResponse } from '@/utils/adapt';
|
||||
import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
|
||||
import { getHistories } from '../utils';
|
||||
|
||||
type Props = ModuleDispatchProps<{
|
||||
userChatInput: string;
|
||||
@@ -26,6 +27,7 @@ export const dispatchAppRequest = async (props: Props): Promise<Response> => {
|
||||
user,
|
||||
stream,
|
||||
detail,
|
||||
histories,
|
||||
inputs: { userChatInput, history = [], app }
|
||||
} = props;
|
||||
|
||||
@@ -52,17 +54,19 @@ export const dispatchAppRequest = async (props: Props): Promise<Response> => {
|
||||
});
|
||||
}
|
||||
|
||||
const chatHistories = getHistories(history, histories);
|
||||
|
||||
const { responseData, answerText } = await dispatchModules({
|
||||
...props,
|
||||
appId: app.id,
|
||||
modules: appData.modules,
|
||||
histories: history,
|
||||
histories: chatHistories,
|
||||
startParams: {
|
||||
userChatInput
|
||||
}
|
||||
});
|
||||
|
||||
const completeMessages = history.concat([
|
||||
const completeMessages = chatHistories.concat([
|
||||
{
|
||||
obj: ChatRoleEnum.Human,
|
||||
value: userChatInput
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { DYNAMIC_INPUT_KEY } from '@fastgpt/global/core/module/constants';
|
||||
|
||||
export const getHistories = (history?: ChatItemType[] | number, histories: ChatItemType[] = []) => {
|
||||
if (!history) return [];
|
||||
@@ -7,3 +8,13 @@ export const getHistories = (history?: ChatItemType[] | number, histories: ChatI
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
export const flatDynamicParams = (params: Record<string, any>) => {
|
||||
const dynamicParams = params[DYNAMIC_INPUT_KEY];
|
||||
if (!dynamicParams) return params;
|
||||
return {
|
||||
...params,
|
||||
...dynamicParams,
|
||||
[DYNAMIC_INPUT_KEY]: undefined
|
||||
};
|
||||
};
|
||||
|
@@ -6,6 +6,7 @@ import { addLog } from '@fastgpt/service/common/system/log';
|
||||
import type { ConcatBillProps, CreateBillProps } from '@fastgpt/global/support/wallet/bill/api.d';
|
||||
import { defaultQGModels } from '@fastgpt/global/core/ai/model';
|
||||
import { POST } from '@fastgpt/service/common/api/plusRequest';
|
||||
import { PostReRankProps } from '@fastgpt/global/core/ai/api';
|
||||
|
||||
export function createBill(data: CreateBillProps) {
|
||||
if (!global.systemEnv?.pluginBaseUrl) return;
|
||||
@@ -247,16 +248,21 @@ export function pushWhisperBill({
|
||||
export function pushReRankBill({
|
||||
teamId,
|
||||
tmbId,
|
||||
source
|
||||
source,
|
||||
inputs
|
||||
}: {
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
source: `${BillSourceEnum}`;
|
||||
inputs: PostReRankProps['inputs'];
|
||||
}) {
|
||||
const model = global.reRankModels[0];
|
||||
if (!model) return { total: 0 };
|
||||
|
||||
const total = model.price * PRICE_SCALE;
|
||||
const textLength = inputs.reduce((sum, item) => sum + item.text.length, 0);
|
||||
const ratio = Math.ceil(textLength / 1000);
|
||||
|
||||
const total = model.price * PRICE_SCALE * ratio;
|
||||
const name = 'wallet.bill.ReRank';
|
||||
|
||||
createBill({
|
||||
|
Reference in New Issue
Block a user