Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
赵月辉
/
fastgpt-migrated
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Unverified
Commit
e02e1545
authored
Jul 08, 2026
by
YeYuheng
Committed by
GitHub
Jul 08, 2026
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix(ai): avoid stack overflow saving base64 request records (#7280)
parent
b3200482
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
50 additions
and
15 deletions
+50
-15
packages/service/core/ai/record/controller.ts
+19
-7
packages/service/test/core/ai/record/controller.test.ts
+31
-8
No files found.
packages/service/core/ai/record/controller.ts
View file @
e02e1545
...
@@ -8,21 +8,33 @@ export const createLLMRequestId = () => {
...
@@ -8,21 +8,33 @@ export const createLLMRequestId = () => {
};
};
const
base64OmittedPlaceholder
=
'[base64 omitted]'
;
const
base64OmittedPlaceholder
=
'[base64 omitted]'
;
const
dataUrlBase64Regex
=
/data:
([^
,
]
*;base64,
)([
A-Za-z0-9+
/
=_-
]{32,})
/g
;
const
fullBase64Regex
=
/^
[
A-Za-z0-9+
/
=_-
]
+$/
;
const
base64LikeKeySet
=
new
Set
([
'base64'
,
'data'
]);
const
base64LikeKeySet
=
new
Set
([
'base64'
,
'data'
]);
const
fullBase64Regex
=
/^
[
A-Za-z0-9+
/
=_-
]
+$/
;
const
dataUrlPrefix
=
'data:'
;
const
base64UrlMarker
=
';base64,'
;
const
rawBase64OmitLength
=
256
;
const
isBase64LikeString
=
(
value
:
string
)
=>
{
const
isBase64LikeString
=
(
value
:
string
)
=>
{
const
text
=
value
.
trim
();
const
text
=
value
.
trim
();
return
text
.
length
>=
256
&&
fullBase64Regex
.
test
(
text
);
return
text
.
length
>=
rawBase64OmitLength
&&
fullBase64Regex
.
test
(
text
);
};
/**
* 清洗整个字段值形式的 data URL。
* 大视频会生成超长 data URL,使用正则 replace 容易触发 V8 栈溢出。
*/
const
sanitizeDataUrlBase64
=
(
value
:
string
)
=>
{
if
(
!
value
.
startsWith
(
dataUrlPrefix
))
return
value
;
const
markerIndex
=
value
.
indexOf
(
base64UrlMarker
);
if
(
markerIndex
===
-
1
)
return
value
;
return
`
${
value
.
slice
(
0
,
markerIndex
+
base64UrlMarker
.
length
)}${
base64OmittedPlaceholder
}
`
;
};
};
const
sanitizeString
=
(
value
:
string
,
key
?:
string
)
=>
{
const
sanitizeString
=
(
value
:
string
,
key
?:
string
)
=>
{
const
sanitizedDataUrl
=
value
.
replace
(
const
sanitizedDataUrl
=
sanitizeDataUrlBase64
(
value
);
dataUrlBase64Regex
,
(
_match
,
prefix
:
string
)
=>
`data:
${
prefix
}${
base64OmittedPlaceholder
}
`
);
if
(
base64LikeKeySet
.
has
(
key
?.
toLowerCase
()
??
''
)
&&
isBase64LikeString
(
sanitizedDataUrl
))
{
if
(
base64LikeKeySet
.
has
(
key
?.
toLowerCase
()
??
''
)
&&
isBase64LikeString
(
sanitizedDataUrl
))
{
return
base64OmittedPlaceholder
;
return
base64OmittedPlaceholder
;
...
...
packages/service/test/core/ai/record/controller.test.ts
View file @
e02e1545
...
@@ -72,19 +72,42 @@ describe('sanitizeLLMRequestRecordPayload', () => {
...
@@ -72,19 +72,42 @@ describe('sanitizeLLMRequestRecordPayload', () => {
text
:
'plain text should stay'
text
:
'plain text should stay'
});
});
});
});
});
it
(
'redacts base64 embedded in a longer string'
,
()
=>
{
describe
(
'LLM request record team isolation'
,
()
=>
{
const
payload
=
{
it
(
'saves records with very large video data urls after redacting base64 payloads'
,
async
()
=>
{
markdown
:
`}
)`
const
largeVideoBase64
=
createBase64
(
10
_000_000
);
};
expect
(
sanitizeLLMRequestRecordPayload
(
payload
)).
toEqual
({
await
saveLLMRequestRecord
({
markdown
:
''
teamId
:
'507f1f77bcf86cd799439011'
,
requestId
:
'large_video_request'
,
body
:
{
messages
:
[
{
role
:
'user'
,
content
:
[
{
type
:
'text'
,
text
:
'analyze this video'
},
{
type
:
'video_url'
,
video_url
:
{
url
:
`data:video/mp4;base64,
${
largeVideoBase64
}
`
}
}
]
}
]
},
response
:
{
answerText
:
'done'
}
});
});
const
record
=
await
getLLMRequestRecord
(
'large_video_request'
,
'507f1f77bcf86cd799439011'
);
expect
(
record
?.
body
.
messages
[
0
].
content
[
1
].
video_url
.
url
).
toBe
(
'data:video/mp4;base64,[base64 omitted]'
);
expect
(
record
?.
response
).
toEqual
({
answerText
:
'done'
});
});
});
});
describe
(
'LLM request record team isolation'
,
()
=>
{
it
(
'saves records with teamId and only reads them from the same team'
,
async
()
=>
{
it
(
'saves records with teamId and only reads them from the same team'
,
async
()
=>
{
await
saveLLMRequestRecord
({
await
saveLLMRequestRecord
({
teamId
:
'507f1f77bcf86cd799439011'
,
teamId
:
'507f1f77bcf86cd799439011'
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment