mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 05:12:39 +00:00
4.8.12 test fix (#2988)
* perf: qps limit * perf: http response data * perf: json path check * fix: ts * loop support reference parent variable
This commit is contained in:
@@ -41,7 +41,7 @@ export enum ERROR_ENUM {
|
||||
unAuthModel = 'unAuthModel',
|
||||
unAuthApiKey = 'unAuthApiKey',
|
||||
unAuthFile = 'unAuthFile',
|
||||
QPSLimitExceed = 'QPSLimitExceed'
|
||||
tooManyRequest = 'tooManyRequest'
|
||||
}
|
||||
|
||||
export type ErrType<T> = Record<
|
||||
@@ -69,10 +69,10 @@ export const ERROR_RESPONSE: Record<
|
||||
message: i18nT('common:code_error.error_message.403'),
|
||||
data: null
|
||||
},
|
||||
[ERROR_ENUM.QPSLimitExceed]: {
|
||||
[ERROR_ENUM.tooManyRequest]: {
|
||||
code: 429,
|
||||
statusText: ERROR_ENUM.QPSLimitExceed,
|
||||
message: i18nT('common:code_error.error_code.429'),
|
||||
statusText: ERROR_ENUM.tooManyRequest,
|
||||
message: 'Too many request',
|
||||
data: null
|
||||
},
|
||||
[ERROR_ENUM.insufficientQuota]: {
|
||||
|
@@ -8,7 +8,7 @@
|
||||
"weight": 10,
|
||||
"courseUrl": "https://fael3z0zfze.feishu.cn/wiki/LsKAwOmtniA4vkkC259cmfxXnAc?fromScene=spaceOverview",
|
||||
"isTool": true,
|
||||
"templateType": "tools",
|
||||
"templateType": "search",
|
||||
|
||||
"workflow": {
|
||||
"nodes": [
|
||||
@@ -37,7 +37,7 @@
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"renderTypeList": ["reference"],
|
||||
"renderTypeList": ["input", "reference"],
|
||||
"selectedTypeIndex": 0,
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
|
@@ -19,8 +19,11 @@ export const NextEntry = ({ beforeCallback = [] }: { beforeCallback?: Promise<an
|
||||
await Promise.all([withNextCors(req, res), ...beforeCallback]);
|
||||
|
||||
let response = null;
|
||||
for (const handler of args) {
|
||||
for await (const handler of args) {
|
||||
response = await handler(req, res);
|
||||
if (res.writableFinished) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Get request duration
|
||||
|
@@ -1,26 +0,0 @@
|
||||
import { ApiRequestProps } from 'type/next';
|
||||
import requestIp from 'request-ip';
|
||||
import { ERROR_ENUM } from '@fastgpt/global/common/error/errorCode';
|
||||
import { authFrequencyLimit } from 'common/system/frequencyLimit/utils';
|
||||
import { addSeconds } from 'date-fns';
|
||||
|
||||
// unit: times/s
|
||||
// how to use?
|
||||
// export default NextAPI(useQPSLimit(10), handler); // limit 10 times per second for a ip
|
||||
export function useQPSLimit(limit: number) {
|
||||
return async (req: ApiRequestProps) => {
|
||||
const ip = requestIp.getClientIp(req);
|
||||
if (!ip) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await authFrequencyLimit({
|
||||
eventId: 'ip-qps-limit' + ip,
|
||||
maxAmount: limit,
|
||||
expiredTime: addSeconds(new Date(), 1)
|
||||
});
|
||||
} catch (_) {
|
||||
return Promise.reject(ERROR_ENUM.QPSLimitExceed);
|
||||
}
|
||||
};
|
||||
}
|
32
packages/service/common/middle/reqFrequencyLimit.ts
Normal file
32
packages/service/common/middle/reqFrequencyLimit.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { ApiRequestProps } from '../../type/next';
|
||||
import requestIp from 'request-ip';
|
||||
import { ERROR_ENUM } from '@fastgpt/global/common/error/errorCode';
|
||||
import { authFrequencyLimit } from '../system/frequencyLimit/utils';
|
||||
import { addSeconds } from 'date-fns';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { jsonRes } from '../response';
|
||||
|
||||
// unit: times/s
|
||||
// how to use?
|
||||
// export default NextAPI(useQPSLimit(10), handler); // limit 10 times per second for a ip
|
||||
export function useReqFrequencyLimit(seconds: number, limit: number) {
|
||||
return async (req: ApiRequestProps, res: NextApiResponse) => {
|
||||
const ip = requestIp.getClientIp(req);
|
||||
if (!ip && process.env.USE_IP_LIMIT !== 'true') {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await authFrequencyLimit({
|
||||
eventId: 'ip-qps-limit' + ip,
|
||||
maxAmount: limit,
|
||||
expiredTime: addSeconds(new Date(), seconds)
|
||||
});
|
||||
} catch (_) {
|
||||
res.status(429);
|
||||
jsonRes(res, {
|
||||
code: 429,
|
||||
message: ERROR_ENUM.tooManyRequest
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
@@ -17,7 +17,7 @@ const FrequencyLimitSchema = new Schema({
|
||||
});
|
||||
|
||||
try {
|
||||
FrequencyLimitSchema.index({ eventId: 1 }, { unique: true });
|
||||
FrequencyLimitSchema.index({ eventId: 1, expiredTime: 1 });
|
||||
FrequencyLimitSchema.index({ expiredTime: 1 }, { expireAfterSeconds: 0 });
|
||||
} catch (error) {}
|
||||
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import { AuthFrequencyLimitProps } from '@fastgpt/global/common/frequenctLimit/type';
|
||||
import { MongoFrequencyLimit } from './schema';
|
||||
import { readFromSecondary } from '../../mongo/utils';
|
||||
|
||||
export const authFrequencyLimit = async ({
|
||||
eventId,
|
||||
@@ -11,22 +10,24 @@ export const authFrequencyLimit = async ({
|
||||
// 对应 eventId 的 account+1, 不存在的话,则创建一个
|
||||
const result = await MongoFrequencyLimit.findOneAndUpdate(
|
||||
{
|
||||
eventId
|
||||
eventId,
|
||||
expiredTime: { $gte: new Date() }
|
||||
},
|
||||
{
|
||||
$inc: { amount: 1 },
|
||||
// If not exist, set the expiredTime
|
||||
$setOnInsert: { expiredTime }
|
||||
},
|
||||
{
|
||||
upsert: true,
|
||||
new: true,
|
||||
...readFromSecondary
|
||||
new: true
|
||||
}
|
||||
);
|
||||
|
||||
).lean();
|
||||
// 因为始终会返回+1的结果,所以这里不能直接等,需要多一个。
|
||||
if (result.amount > maxAmount) {
|
||||
return Promise.reject(result);
|
||||
}
|
||||
} catch (error) {}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
@@ -32,8 +32,6 @@ export const dispatchLoop = async (props: Props): Promise<Response> => {
|
||||
return Promise.reject('Input array length cannot be greater than 50');
|
||||
}
|
||||
|
||||
const runNodes = runtimeNodes.filter((node) => childrenNodeIdList.includes(node.nodeId));
|
||||
|
||||
const outputValueArr = [];
|
||||
const loopDetail: ChatHistoryItemResType[] = [];
|
||||
let assistantResponses: AIChatItemValueItemType[] = [];
|
||||
@@ -43,7 +41,7 @@ export const dispatchLoop = async (props: Props): Promise<Response> => {
|
||||
for await (const item of loopInputArray) {
|
||||
const response = await dispatchWorkFlow({
|
||||
...props,
|
||||
runtimeNodes: runNodes.map((node) =>
|
||||
runtimeNodes: runtimeNodes.map((node) =>
|
||||
node.flowNodeType === FlowNodeTypeEnum.loopStart
|
||||
? {
|
||||
...node,
|
||||
|
@@ -239,7 +239,15 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
||||
)
|
||||
.forEach((item) => {
|
||||
const key = item.key.startsWith('$') ? item.key : `$.${item.key}`;
|
||||
results[item.key] = JSONPath({ path: key, json: formatResponse })[0];
|
||||
results[item.key] = (() => {
|
||||
// 检查是否是简单的属性访问或单一索引访问
|
||||
if (/^\$(\.[a-zA-Z_][a-zA-Z0-9_]*)+$/.test(key) || /^\$(\[\d+\])+$/.test(key)) {
|
||||
return JSONPath({ path: key, json: formatResponse })[0];
|
||||
}
|
||||
|
||||
// 如果无法确定,默认返回数组
|
||||
return JSONPath({ path: key, json: formatResponse });
|
||||
})();
|
||||
});
|
||||
|
||||
if (typeof formatResponse[NodeOutputKeyEnum.answerText] === 'string') {
|
||||
@@ -252,6 +260,7 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
||||
}
|
||||
|
||||
return {
|
||||
...results,
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||
totalPoints: 0,
|
||||
params: Object.keys(params).length > 0 ? params : undefined,
|
||||
@@ -261,8 +270,7 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
||||
},
|
||||
[DispatchNodeResponseKeyEnum.toolResponses]:
|
||||
Object.keys(results).length > 0 ? results : rawResponse,
|
||||
[NodeOutputKeyEnum.httpRawResponse]: rawResponse,
|
||||
...results
|
||||
[NodeOutputKeyEnum.httpRawResponse]: rawResponse
|
||||
};
|
||||
} catch (error) {
|
||||
addLog.error('Http request error', error);
|
||||
|
Reference in New Issue
Block a user