fix:agent eval and doc file (#6158)

* agent eval

* eval auth

* html transofrm size

* fix: test

---------

Co-authored-by: xxyyh <2289112474@qq>
Co-authored-by: archer <545436317@qq.com>
This commit is contained in:
YeYuheng
2025-12-30 11:20:55 +08:00
committed by GitHub
parent 5ff4cc11b0
commit 84570bda6f
7 changed files with 54 additions and 22 deletions

View File

@@ -163,6 +163,8 @@ ${{vec.db}}
CHAT_FILE_EXPIRE_TIME: 7 CHAT_FILE_EXPIRE_TIME: 7
# 服务器接收请求,最大大小,单位 MB # 服务器接收请求,最大大小,单位 MB
SERVICE_REQUEST_MAX_CONTENT_LENGTH: 10 SERVICE_REQUEST_MAX_CONTENT_LENGTH: 10
# HTML 转换最大字符数
MAX_HTML_TRANSFORM_CHARS: 1000000
volumes: volumes:
- ./config.json:/app/data/config.json - ./config.json:/app/data/config.json
sandbox: sandbox:

View File

@@ -7,6 +7,7 @@ import type { EvaluationSchemaType } from '@fastgpt/global/core/app/evaluation/t
import type { AuthModeType } from '../type'; import type { AuthModeType } from '../type';
import { MongoEvaluation } from '../../../core/app/evaluation/evalSchema'; import { MongoEvaluation } from '../../../core/app/evaluation/evalSchema';
import { parseHeaderCert } from '../auth/common'; import { parseHeaderCert } from '../auth/common';
import { AppErrEnum } from '@fastgpt/global/common/error/code/app';
export const authEval = async ({ export const authEval = async ({
evalId, evalId,
@@ -21,7 +22,13 @@ export const authEval = async ({
}> => { }> => {
const { teamId, tmbId, isRoot } = await parseHeaderCert(props); const { teamId, tmbId, isRoot } = await parseHeaderCert(props);
const evaluation = await MongoEvaluation.findById(evalId, 'tmbId').lean(); const evaluation = await MongoEvaluation.findOne(
{
_id: evalId,
teamId
},
'tmbId appId'
).lean();
if (!evaluation) { if (!evaluation) {
return Promise.reject('Evaluation not found'); return Promise.reject('Evaluation not found');
} }
@@ -34,28 +41,37 @@ export const authEval = async ({
}; };
} }
// App read per try {
if (per === ReadPermissionVal) { // App read per
if (per === ReadPermissionVal) {
await authAppByTmbId({
tmbId,
appId: evaluation.appId,
per: ReadPermissionVal,
isRoot
});
return {
teamId,
tmbId,
evaluation
};
}
// Write per
await authAppByTmbId({ await authAppByTmbId({
tmbId, tmbId,
appId: evaluation.appId, appId: evaluation.appId,
per: ReadPermissionVal, per: ManagePermissionVal,
isRoot isRoot
}); });
return { } catch (error) {
teamId, // If app does not exist, allow operation (app was deleted, allow eval cleanup)
tmbId, if (error !== AppErrEnum.unExist) {
evaluation throw error;
}; }
} }
// Write per
await authAppByTmbId({
tmbId,
appId: evaluation.appId,
per: ManagePermissionVal,
isRoot
});
return { return {
teamId, teamId,
tmbId, tmbId,

View File

@@ -48,6 +48,8 @@ declare global {
CHAT_LOG_SOURCE_ID_PREFIX?: string; CHAT_LOG_SOURCE_ID_PREFIX?: string;
NEXT_PUBLIC_BASE_URL: string; NEXT_PUBLIC_BASE_URL: string;
MAX_HTML_TRANSFORM_CHARS: string;
} }
} }
} }

View File

@@ -5,7 +5,7 @@ import { simpleMarkdownText } from '@fastgpt/global/common/string/markdown';
// @ts-ignore // @ts-ignore
const turndownPluginGfm = require('joplin-turndown-plugin-gfm'); const turndownPluginGfm = require('joplin-turndown-plugin-gfm');
const MAX_HTML_SIZE = 100 * 1000; // 100k characters limit const MAX_HTML_SIZE = Number(process.env.MAX_HTML_TRANSFORM_CHARS || 1000000);
const processBase64Images = (htmlContent: string) => { const processBase64Images = (htmlContent: string) => {
// 优化后的正则: // 优化后的正则:
@@ -70,9 +70,9 @@ export const html2md = (
// Base64 img to id, otherwise it will occupy memory when going to md // Base64 img to id, otherwise it will occupy memory when going to md
const { processedHtml, images } = processBase64Images(html); const { processedHtml, images } = processBase64Images(html);
// if html is too large, return the original html // if html is too large, return the original html (but preserve image list)
if (processedHtml.length > MAX_HTML_SIZE) { if (processedHtml.length > MAX_HTML_SIZE) {
return { rawText: processedHtml, imageList: [] }; return { rawText: processedHtml, imageList: images };
} }
const md = turndownService.turndown(processedHtml); const md = turndownService.turndown(processedHtml);

View File

@@ -12,6 +12,7 @@ export enum WorkerNameEnum {
export const getSafeEnv = () => { export const getSafeEnv = () => {
return { return {
MAX_HTML_TRANSFORM_CHARS: process.env.MAX_HTML_TRANSFORM_CHARS,
LOG_LEVEL: process.env.LOG_LEVEL, LOG_LEVEL: process.env.LOG_LEVEL,
STORE_LOG_LEVEL: process.env.STORE_LOG_LEVEL, STORE_LOG_LEVEL: process.env.STORE_LOG_LEVEL,
NODE_ENV: process.env.NODE_ENV, NODE_ENV: process.env.NODE_ENV,

View File

@@ -113,6 +113,8 @@ SHOW_COUPON=false
SHOW_DISCOUNT_COUPON=false SHOW_DISCOUNT_COUPON=false
# 自定义 config.json 路径 # 自定义 config.json 路径
CONFIG_JSON_PATH= CONFIG_JSON_PATH=
# HTML 转 Markdown 最大字符数(超过字符数不执行转化)
MAX_HTML_TRANSFORM_CHARS=
# 对话日志推送服务 # 对话日志推送服务
# # 日志服务地址 # # 日志服务地址

View File

@@ -108,7 +108,7 @@ describe('html2md 性能和功能测试', () => {
it('大型 base64 图片性能(~1MB)', () => { it('大型 base64 图片性能(~1MB)', () => {
// 生成约 1MB 的 base64 数据 // 生成约 1MB 的 base64 数据
const base64Data = 'A'.repeat(1024 * 1024); const base64Data = 'A'.repeat(1000000);
const html = `<img src="data:image/png;base64,${base64Data}">`; const html = `<img src="data:image/png;base64,${base64Data}">`;
const start = Date.now(); const start = Date.now();
@@ -160,14 +160,23 @@ describe('html2md 性能和功能测试', () => {
}); });
describe('防御性功能', () => { describe('防御性功能', () => {
it('应该拒绝超大 HTML 文档', () => { it('应该拒绝超大 HTML 文档(>1MB)', () => {
const hugeHtml = 'x'.repeat(100 * 1000 + 1); const hugeHtml = 'x'.repeat(1000000 + 1);
const result = html2md(hugeHtml); const result = html2md(hugeHtml);
expect(result.rawText).toBe(hugeHtml); expect(result.rawText).toBe(hugeHtml);
expect(result.imageList).toHaveLength(0); expect(result.imageList).toHaveLength(0);
}); });
it('应该正常处理大型 HTML 文档(<1MB)', () => {
const largeHtml = 'x'.repeat(1000000 - 1);
const result = html2md(largeHtml);
// 即使很大,但在限制内,应该正常处理
expect(result.rawText).toBeTruthy();
expect(result.rawText.length).toBeGreaterThan(0);
});
it('应该处理空 HTML', () => { it('应该处理空 HTML', () => {
const result = html2md(''); const result = html2md('');