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
a5364a45
authored
Jun 11, 2026
by
Archer
Committed by
GitHub
Jun 11, 2026
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update marketplace (#7097)
* fix: svg * doc * perf: sync website * perf: create qs guide
parent
8784b622
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
297 additions
and
21 deletions
+297
-21
.github/workflows/build-mcp-server.yml
+8
-10
.github/workflows/build-sso-service-image.yml
+8
-8
packages/service/core/ai/functions/createQuestionGuide.ts
+4
-1
packages/service/core/workflow/dispatch/ai/extract.ts
+2
-1
packages/service/support/user/utils.ts
+17
-0
packages/service/test/core/ai/functions/createQuestionGuide.test.ts
+60
-0
packages/service/test/core/workflow/dispatch/ai/extract.test.ts
+164
-0
packages/service/test/support/user/utils.test.ts
+33
-0
pro
+1
-1
No files found.
.github/workflows/build-mcp-server.yml
View file @
a5364a45
name
:
Build fastgpt-mcp-server images
name
:
Build fastgpt-mcp-server images
on
:
on
:
workflow_dispatch
:
workflow_dispatch
:
push
:
inputs
:
paths
:
version
:
-
'
projects/mcp_server/**
'
description
:
'
Image
version
tag
(e.g.
v1.0.0)
'
tags
:
required
:
true
-
'
v*'
type
:
string
jobs
:
jobs
:
validate-version
:
validate-version
:
runs-on
:
ubuntu-24.04
runs-on
:
ubuntu-24.04
...
@@ -15,12 +15,10 @@ jobs:
...
@@ -15,12 +15,10 @@ jobs:
-
name
:
Validate release version
-
name
:
Validate release version
id
:
version
id
:
version
env
:
env
:
VERSION
:
${{ github.ref_name }}
VERSION
:
${{ github.event.inputs.version }}
REF_TYPE
:
${{ github.ref_type }}
CURRENT_REF
:
${{ github.ref }}
run
:
|
run
:
|
if [[
"$REF_TYPE" != "tag" ||
! "$VERSION" =~ ^v ]]; then
if [[ ! "$VERSION" =~ ^v ]]; then
echo "::error::
Release workflow must run on a tag starting with v. Current ref: ${CURRENT_REF
}"
echo "::error::
Image version must start with v. Current value: ${VERSION
}"
exit 1
exit 1
fi
fi
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
...
...
.github/workflows/build-sso-service-image.yml
View file @
a5364a45
name
:
Build fastgpt-sso-service images
name
:
Build fastgpt-sso-service images
on
:
on
:
workflow_dispatch
:
workflow_dispatch
:
push
:
inputs
:
tags
:
version
:
-
'
v*'
description
:
'
Image
version
tag
(e.g.
v1.0.0)'
required
:
true
type
:
string
permissions
:
permissions
:
contents
:
read
contents
:
read
...
@@ -20,12 +22,10 @@ jobs:
...
@@ -20,12 +22,10 @@ jobs:
-
name
:
Validate release version
-
name
:
Validate release version
id
:
version
id
:
version
env
:
env
:
VERSION
:
${{ github.ref_name }}
VERSION
:
${{ github.event.inputs.version }}
REF_TYPE
:
${{ github.ref_type }}
CURRENT_REF
:
${{ github.ref }}
run
:
|
run
:
|
if [[
"$REF_TYPE" != "tag" ||
! "$VERSION" =~ ^v ]]; then
if [[ ! "$VERSION" =~ ^v ]]; then
echo "::error::
Release workflow must run on a tag starting with v. Current ref: ${CURRENT_REF
}"
echo "::error::
Image version must start with v. Current value: ${VERSION
}"
exit 1
exit 1
fi
fi
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
...
...
packages/service/core/ai/functions/createQuestionGuide.ts
View file @
a5364a45
...
@@ -6,6 +6,7 @@ import {
...
@@ -6,6 +6,7 @@ import {
import
json5
from
'json5'
;
import
json5
from
'json5'
;
import
{
createLLMResponse
}
from
'../llm/request'
;
import
{
createLLMResponse
}
from
'../llm/request'
;
import
{
getLogger
,
LogCategories
}
from
'../../../common/logger'
;
import
{
getLogger
,
LogCategories
}
from
'../../../common/logger'
;
import
{
getLLMModel
}
from
'../model'
;
const
logger
=
getLogger
(
LogCategories
.
MODULE
.
AI
.
FUNCTIONS
);
const
logger
=
getLogger
(
LogCategories
.
MODULE
.
AI
.
FUNCTIONS
);
...
@@ -22,6 +23,7 @@ export async function createQuestionGuide({
...
@@ -22,6 +23,7 @@ export async function createQuestionGuide({
inputTokens
:
number
;
inputTokens
:
number
;
outputTokens
:
number
;
outputTokens
:
number
;
}
>
{
}
>
{
const
questionGuideModel
=
getLLMModel
(
model
);
const
concatMessages
:
ChatCompletionMessageParam
[]
=
[
const
concatMessages
:
ChatCompletionMessageParam
[]
=
[
...
messages
,
...
messages
,
{
{
...
@@ -39,7 +41,8 @@ export async function createQuestionGuide({
...
@@ -39,7 +41,8 @@ export async function createQuestionGuide({
temperature
:
0.1
,
temperature
:
0.1
,
max_tokens
:
200
,
max_tokens
:
200
,
messages
:
concatMessages
,
messages
:
concatMessages
,
stream
:
true
stream
:
true
,
...(
questionGuideModel
?.
reasoning
?
{
reasoning_effort
:
'none'
as
const
}
:
{})
}
}
});
});
...
...
packages/service/core/workflow/dispatch/ai/extract.ts
View file @
a5364a45
...
@@ -243,7 +243,8 @@ const toolChoice = async (props: ActionProps) => {
...
@@ -243,7 +243,8 @@ const toolChoice = async (props: ActionProps) => {
messages
:
filterMessages
,
messages
:
filterMessages
,
tools
,
tools
,
tool_choice
:
{
type
:
'function'
,
function
:
{
name
:
agentFunName
}
},
tool_choice
:
{
type
:
'function'
,
function
:
{
name
:
agentFunName
}
},
toolCallMode
:
'toolChoice'
toolCallMode
:
'toolChoice'
,
...(
extractModel
.
reasoning
?
{
reasoning_effort
:
'none'
as
const
}
:
{})
}
as
const
;
}
as
const
;
const
{
const
{
...
...
packages/service/support/user/utils.ts
View file @
a5364a45
...
@@ -46,6 +46,23 @@ export const updateWebSyncLimit = async (teamId: string) => {
...
@@ -46,6 +46,23 @@ export const updateWebSyncLimit = async (teamId: string) => {
});
});
}
catch
{}
}
catch
{}
};
};
/**
* 清除团队站点同步冷却时间。
*
* 站点同步任务入队时会先写入 `limit.lastWebsiteSyncTime` 做触发频率限制;
* 如果 worker 最终没有成功同步任何页面,这次空同步不应占用团队后续手动同步机会。
*/
export
const
clearWebSyncLimit
=
async
(
teamId
:
string
)
=>
{
try
{
await
MongoTeam
.
findByIdAndUpdate
(
teamId
,
{
$unset
:
{
'limit.lastWebsiteSyncTime'
:
1
}
});
}
catch
{}
};
export
const
checkWebSyncLimit
=
async
({
export
const
checkWebSyncLimit
=
async
({
teamId
,
teamId
,
limitMinutes
=
0
limitMinutes
=
0
...
...
packages/service/test/core/ai/functions/createQuestionGuide.test.ts
0 → 100644
View file @
a5364a45
import
{
beforeEach
,
describe
,
expect
,
it
,
vi
}
from
'vitest'
;
const
{
createLLMResponseMock
,
getLLMModelMock
}
=
vi
.
hoisted
(()
=>
({
createLLMResponseMock
:
vi
.
fn
(),
getLLMModelMock
:
vi
.
fn
()
}));
vi
.
mock
(
'@fastgpt/service/core/ai/llm/request'
,
()
=>
({
createLLMResponse
:
createLLMResponseMock
}));
vi
.
mock
(
'@fastgpt/service/core/ai/model'
,
()
=>
({
getLLMModel
:
getLLMModelMock
}));
import
{
createQuestionGuide
}
from
'@fastgpt/service/core/ai/functions/createQuestionGuide'
;
describe
(
'createQuestionGuide'
,
()
=>
{
beforeEach
(()
=>
{
vi
.
clearAllMocks
();
createLLMResponseMock
.
mockResolvedValue
({
answerText
:
'["问题 1","问题 2","问题 3"]'
,
usage
:
{
inputTokens
:
10
,
outputTokens
:
5
}
});
});
it
(
'forces reasoning models to disable reasoning for question guide generation'
,
async
()
=>
{
getLLMModelMock
.
mockReturnValue
({
model
:
'deepseek-r1'
,
reasoning
:
true
});
await
createQuestionGuide
({
messages
:
[],
model
:
'deepseek-r1'
});
expect
(
createLLMResponseMock
.
mock
.
calls
[
0
][
0
].
body
).
toMatchObject
({
model
:
'deepseek-r1'
,
reasoning_effort
:
'none'
});
});
it
(
'does not set reasoning effort for non-reasoning models'
,
async
()
=>
{
getLLMModelMock
.
mockReturnValue
({
model
:
'gpt-4o'
,
reasoning
:
false
});
await
createQuestionGuide
({
messages
:
[],
model
:
'gpt-4o'
});
expect
(
createLLMResponseMock
.
mock
.
calls
[
0
][
0
].
body
).
not
.
toHaveProperty
(
'reasoning_effort'
);
});
});
packages/service/test/core/workflow/dispatch/ai/extract.test.ts
0 → 100644
View file @
a5364a45
import
{
beforeEach
,
describe
,
expect
,
it
,
vi
}
from
'vitest'
;
import
{
ChatRoleEnum
}
from
'@fastgpt/global/core/chat/constants'
;
import
{
NodeInputKeyEnum
}
from
'@fastgpt/global/core/workflow/constants'
;
import
{
FlowNodeTypeEnum
}
from
'@fastgpt/global/core/workflow/node/constant'
;
const
{
createLLMResponseMock
,
filterGPTMessageByMaxContextMock
,
getLLMModelMock
,
formatModelChars2PointsMock
}
=
vi
.
hoisted
(()
=>
({
createLLMResponseMock
:
vi
.
fn
(),
filterGPTMessageByMaxContextMock
:
vi
.
fn
(),
getLLMModelMock
:
vi
.
fn
(),
formatModelChars2PointsMock
:
vi
.
fn
()
}));
vi
.
mock
(
'@fastgpt/service/core/ai/llm/request'
,
()
=>
({
createLLMResponse
:
createLLMResponseMock
}));
vi
.
mock
(
'@fastgpt/service/core/ai/llm/utils'
,
()
=>
({
filterGPTMessageByMaxContext
:
filterGPTMessageByMaxContextMock
}));
vi
.
mock
(
'@fastgpt/service/core/ai/model'
,
()
=>
({
getLLMModel
:
getLLMModelMock
}));
vi
.
mock
(
'@fastgpt/service/support/wallet/usage/utils'
,
()
=>
({
formatModelChars2Points
:
formatModelChars2PointsMock
}));
import
{
dispatchContentExtract
}
from
'@fastgpt/service/core/workflow/dispatch/ai/extract'
;
const
createProps
=
()
=>
({
runningAppInfo
:
{
id
:
'app_1'
},
node
:
{
nodeId
:
'extract_node'
,
name
:
'内容提取'
,
flowNodeType
:
FlowNodeTypeEnum
.
contentExtract
},
histories
:
[
{
obj
:
ChatRoleEnum
.
Human
,
value
:
[
{
text
:
{
content
:
'历史问题'
}
}
]
}
],
externalProvider
:
{},
usagePush
:
vi
.
fn
(),
params
:
{
content
:
'张三来自杭州'
,
history
:
6
,
model
:
'deepseek-r1'
,
description
:
'提取姓名'
,
extractKeys
:
[
{
key
:
'name'
,
desc
:
'姓名'
,
required
:
true
}
]
}
})
as
any
;
describe
(
'dispatchContentExtract'
,
()
=>
{
beforeEach
(()
=>
{
vi
.
clearAllMocks
();
filterGPTMessageByMaxContextMock
.
mockImplementation
(
async
({
messages
})
=>
messages
);
formatModelChars2PointsMock
.
mockReturnValue
({
totalPoints
:
1
,
modelName
:
'DeepSeek R1'
});
});
it
(
'forces reasoning models to disable reasoning in tool choice extraction'
,
async
()
=>
{
getLLMModelMock
.
mockReturnValue
({
model
:
'deepseek-r1'
,
name
:
'DeepSeek R1'
,
maxContext
:
128000
,
reasoning
:
true
,
toolChoice
:
true
});
createLLMResponseMock
.
mockResolvedValue
({
answerText
:
''
,
toolCalls
:
[
{
function
:
{
arguments
:
'{"name":"张三"}'
}
}
],
usage
:
{
inputTokens
:
10
,
outputTokens
:
5
,
usedUserOpenAIKey
:
false
}
});
await
dispatchContentExtract
(
createProps
());
expect
(
createLLMResponseMock
.
mock
.
calls
[
0
][
0
].
body
).
toMatchObject
({
model
:
'deepseek-r1'
,
reasoning_effort
:
'none'
,
toolCallMode
:
'toolChoice'
});
});
it
(
'does not set reasoning effort in completion extraction'
,
async
()
=>
{
getLLMModelMock
.
mockReturnValue
({
model
:
'deepseek-r1'
,
name
:
'DeepSeek R1'
,
maxContext
:
128000
,
reasoning
:
true
,
toolChoice
:
false
});
createLLMResponseMock
.
mockResolvedValue
({
answerText
:
'{"name":"张三"}'
,
usage
:
{
inputTokens
:
10
,
outputTokens
:
5
,
usedUserOpenAIKey
:
false
}
});
await
dispatchContentExtract
(
createProps
());
expect
(
createLLMResponseMock
.
mock
.
calls
[
0
][
0
].
body
).
toMatchObject
({
model
:
'deepseek-r1'
});
expect
(
createLLMResponseMock
.
mock
.
calls
[
0
][
0
].
body
).
not
.
toHaveProperty
(
'reasoning_effort'
);
});
it
(
'does not set reasoning effort for non-reasoning extraction models'
,
async
()
=>
{
getLLMModelMock
.
mockReturnValue
({
model
:
'gpt-4o'
,
name
:
'GPT-4o'
,
maxContext
:
128000
,
reasoning
:
false
,
toolChoice
:
false
});
createLLMResponseMock
.
mockResolvedValue
({
answerText
:
'{"name":"张三"}'
,
usage
:
{
inputTokens
:
10
,
outputTokens
:
5
,
usedUserOpenAIKey
:
false
}
});
await
dispatchContentExtract
(
createProps
());
expect
(
createLLMResponseMock
.
mock
.
calls
[
0
][
0
].
body
).
not
.
toHaveProperty
(
'reasoning_effort'
);
});
});
packages/service/test/support/user/utils.test.ts
0 → 100644
View file @
a5364a45
import
{
beforeEach
,
describe
,
expect
,
it
,
vi
}
from
'vitest'
;
import
{
clearWebSyncLimit
}
from
'../../../support/user/utils'
;
import
{
MongoTeam
}
from
'../../../support/user/team/teamSchema'
;
vi
.
mock
(
'../../../support/user/team/teamSchema'
,
()
=>
({
MongoTeam
:
{
findByIdAndUpdate
:
vi
.
fn
()
}
}));
vi
.
mock
(
'../../../support/user/team/teamMemberSchema'
,
()
=>
({
MongoTeamMember
:
{
find
:
vi
.
fn
()
}
}));
describe
(
'support user utils'
,
()
=>
{
beforeEach
(()
=>
{
vi
.
clearAllMocks
();
});
it
(
'clears website sync limit timestamp'
,
async
()
=>
{
const
teamId
=
'team-id'
;
await
clearWebSyncLimit
(
teamId
);
expect
(
MongoTeam
.
findByIdAndUpdate
).
toHaveBeenCalledWith
(
teamId
,
{
$unset
:
{
'limit.lastWebsiteSyncTime'
:
1
}
});
});
});
pro
@
6e8c027b
Subproject commit
ce87008bf0da48e31e1e74036d1c37e903ea64c9
Subproject commit
6e8c027b70695879580f2f52f554d2d09e55599c
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