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
a1674a5f
authored
Jun 16, 2026
by
Archer
Committed by
GitHub
Jun 16, 2026
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: action (#7128)
* fix: action * perf: modal * fix: openapi
parent
4f30121d
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
171 additions
and
137 deletions
+171
-137
.github/workflows/build-fastgpt-ide-agent.yml
+20
-2
packages/global/openapi/core/app/tool/api.ts
+16
-0
packages/global/openapi/core/app/tool/index.ts
+2
-2
packages/global/test/core/app/tool/openapi.test.ts
+5
-0
projects/app/src/components/core/chat/ChatContainer/ChatBox/components/SelectMarkCollection.tsx
+59
-58
projects/app/src/components/core/dataset/SelectModal.tsx
+3
-6
projects/app/src/web/core/dataset/components/SelectCollections.tsx
+66
-69
No files found.
.github/workflows/build-fastgpt-ide-agent.yml
View file @
a1674a5f
...
...
@@ -119,6 +119,13 @@ jobs:
registry
:
ghcr.io
username
:
${{ github.repository_owner }}
password
:
${{ secrets.GITHUB_TOKEN }}
-
name
:
Login to Ali Hub
if
:
matrix.image == 'fastgpt-agent-sandbox-proxy'
uses
:
docker/login-action@v3
with
:
registry
:
registry.cn-hangzhou.aliyuncs.com
username
:
${{ secrets.FASTGPT_ALI_IMAGE_USER }}
password
:
${{ secrets.FASTGPT_ALI_IMAGE_PSW }}
-
name
:
Download digests
uses
:
actions/download-artifact@v4
with
:
...
...
@@ -132,9 +139,20 @@ jobs:
-
name
:
Set image name and tag
run
:
|
echo "Git_Tag=ghcr.io/${{ github.repository_owner }}/${{ matrix.image }}:${{ matrix.tag }}" >> $GITHUB_ENV
if [[ "${{ matrix.image }}" == "fastgpt-agent-sandbox-proxy" ]]; then
echo "Ali_Tag=${{ secrets.FASTGPT_ALI_IMAGE_PREFIX }}/${{ matrix.image }}:${{ matrix.tag }}" >> $GITHUB_ENV
fi
-
name
:
Create manifest list and push
working-directory
:
${{ runner.temp }}/digests
run
:
|
docker buildx imagetools create -t "${Git_Tag}" \
$(printf 'ghcr.io/${{ github.repository_owner }}/${{ matrix.image }}@sha256:%s ' *)
TAGS="${Git_Tag}"
if [[ -n "${Ali_Tag:-}" ]]; then
TAGS="$(echo -e "${TAGS}\n${Ali_Tag}")"
fi
for TAG in $TAGS; do
docker buildx imagetools create -t "${TAG}" \
$(printf 'ghcr.io/${{ github.repository_owner }}/${{ matrix.image }}@sha256:%s ' *)
sleep 5
done
packages/global/openapi/core/app/tool/api.ts
View file @
a1674a5f
...
...
@@ -129,5 +129,21 @@ export const GetPreviewNodeQuerySchema = z.union([
export
type
GetPreviewNodeQuery
=
z
.
infer
<
typeof
GetPreviewNodeQuerySchema
>
;
export
const
GetPreviewNodeQueryOpenAPISchema
=
GetPreviewNodeBaseQuerySchema
.
extend
({
versionId
:
z
.
string
().
optional
().
meta
({
example
:
'68ad85a7463006c963799a05'
,
description
:
'工具版本 ID,与 getLatestVersion 必须二选一。传空字符串时返回最新版节点数据,但响应中的 version 为空'
}),
getLatestVersion
:
BoolSchema
.
optional
().
meta
({
example
:
true
,
description
:
'是否获取最新版本 ID,与 versionId 必须二选一。只能传 true,表示返回最新版节点数据并带上具体 version'
})
}).
meta
({
description
:
'OpenAPI 文档参数模型。运行时仍由 GetPreviewNodeQuerySchema 校验 versionId/getLatestVersion 的二选一约束。'
});
export
const
GetPreviewNodeResponseSchema
=
ToolPreviewNodeResponseSchema
;
export
type
GetPreviewNodeResponse
=
z
.
infer
<
typeof
GetPreviewNodeResponseSchema
>
;
packages/global/openapi/core/app/tool/index.ts
View file @
a1674a5f
import
type
{
OpenAPIPath
}
from
'../../../type'
;
import
{
TagsMap
}
from
'../../../tag'
;
import
{
GetPreviewNodeQuerySchema
,
GetPreviewNodeQuery
OpenAPI
Schema
,
GetPreviewNodeResponseSchema
,
GetSystemToolTemplatesBodySchema
,
GetSystemToolTemplatesResponseSchema
,
...
...
@@ -61,7 +61,7 @@ export const ToolPath: OpenAPIPath = {
'根据工具 ID 和版本配置生成可插入工作流画布的工具节点模板,支持系统工具和我的工具(MCP、HTTP、工作流工具)'
,
tags
:
[
TagsMap
.
appSystemTool
,
TagsMap
.
httpTools
,
TagsMap
.
mcpTools
,
TagsMap
.
pluginTeam
],
requestParams
:
{
query
:
GetPreviewNodeQuerySchema
query
:
GetPreviewNodeQuery
OpenAPI
Schema
},
responses
:
{
200
:
{
...
...
packages/global/test/core/app/tool/openapi.test.ts
View file @
a1674a5f
import
{
describe
,
expect
,
it
}
from
'vitest'
;
import
{
openAPIDocument
}
from
'@fastgpt/global/openapi'
;
import
{
GetPreviewNodeQuerySchema
}
from
'@fastgpt/global/openapi/core/app/tool/api'
;
describe
(
'GetPreviewNodeQuerySchema'
,
()
=>
{
...
...
@@ -51,4 +52,8 @@ describe('GetPreviewNodeQuerySchema', () => {
}).
success
).
toBe
(
false
);
});
it
(
'keeps full OpenAPI document generation compatible with query params'
,
()
=>
{
expect
(
openAPIDocument
.
paths
[
'/core/app/tool/getPreviewNode'
]?.
get
).
toBeDefined
();
});
});
projects/app/src/components/core/chat/ChatContainer/ChatBox/components/SelectMarkCollection.tsx
View file @
a1674a5f
import
React
from
'react'
;
import
{
ModalBody
,
useTheme
,
ModalFooter
,
Button
,
Box
,
Card
,
Flex
,
Grid
}
from
'@chakra-ui/react'
;
import
{
useTheme
,
ModalFooter
,
Button
,
Box
,
Card
,
Flex
,
Grid
}
from
'@chakra-ui/react'
;
import
{
useTranslation
}
from
'next-i18next'
;
import
Avatar
from
'@fastgpt/web/components/common/Avatar'
;
import
MyIcon
from
'@fastgpt/web/components/common/Icon'
;
...
...
@@ -9,6 +9,7 @@ import dynamic from 'next/dynamic';
import
{
type
AdminFbkType
}
from
'@fastgpt/global/core/chat/type'
;
import
SelectCollections
from
'@/web/core/dataset/components/SelectCollections'
;
import
EmptyTip
from
'@fastgpt/web/components/common/EmptyTip'
;
import
MyTooltip
from
'@fastgpt/web/components/common/MyTooltip'
;
const
InputDataModal
=
dynamic
(
()
=>
import
(
'@/pageComponents/dataset/detail/components/InputDataModal'
)
...
...
@@ -49,49 +50,51 @@ const SelectMarkCollection = ({
isLoading=
{
isFetching
}
tips=
{
t
(
'common:core.chat.Select dataset Desc'
)
}
>
<
ModalBody
flex=
{
'1 0 0'
}
overflowY=
{
'auto'
}
>
<
Grid
display=
{
'grid'
}
gridTemplateColumns=
{
[
'repeat(1,1fr)'
,
'repeat(2,1fr)'
,
'repeat(3,1fr)'
]
}
gridGap=
{
3
}
userSelect=
{
'none'
}
>
{
datasets
.
map
((
item
)
=>
(()
=>
{
return
(
<
Card
key=
{
item
.
_id
}
p=
{
3
}
border=
{
theme
.
borders
.
base
}
boxShadow=
{
'sm'
}
h=
{
'80px'
}
cursor=
{
'pointer'
}
_hover=
{
{
boxShadow
:
'md'
}
}
onClick=
{
()
=>
{
if
(
item
.
type
===
DatasetTypeEnum
.
folder
)
{
setParentId
(
item
.
_id
);
}
else
{
setAdminMarkData
({
...
adminMarkData
,
datasetId
:
item
.
_id
});
}
}
}
>
<
Flex
alignItems=
{
'center'
}
h=
{
'38px'
}
>
<
Avatar
src=
{
item
.
avatar
}
w=
{
'2rem'
}
borderRadius=
{
'sm'
}
></
Avatar
>
<
Box
ml=
{
3
}
>
{
item
.
name
}
</
Box
>
</
Flex
>
<
Flex
justifyContent=
{
'flex-end'
}
alignItems=
{
'center'
}
fontSize=
{
'sm'
}
>
<
MyIcon
mr=
{
1
}
name=
"kbTest"
w=
{
'12px'
}
/>
<
Box
color=
{
'myGray.500'
}
>
{
item
.
vectorModel
.
name
}
</
Box
>
</
Flex
>
</
Card
>
);
})()
)
}
</
Grid
>
{
datasets
.
length
===
0
&&
<
EmptyTip
text=
{
t
(
'chat:empty_directory'
)
}
></
EmptyTip
>
}
</
ModalBody
>
{
datasets
.
length
===
0
&&
<
EmptyTip
text=
{
t
(
'chat:empty_directory'
)
}
></
EmptyTip
>
}
<
Grid
display=
{
'grid'
}
gridTemplateColumns=
{
[
'repeat(1,1fr)'
,
'repeat(2,1fr)'
,
'repeat(3,1fr)'
]
}
gridGap=
{
3
}
userSelect=
{
'none'
}
>
{
datasets
.
map
((
item
)
=>
(()
=>
{
return
(
<
Card
key=
{
item
.
_id
}
p=
{
3
}
border=
{
theme
.
borders
.
base
}
boxShadow=
{
'sm'
}
h=
{
'80px'
}
cursor=
{
'pointer'
}
_hover=
{
{
boxShadow
:
'md'
}
}
onClick=
{
()
=>
{
if
(
item
.
type
===
DatasetTypeEnum
.
folder
)
{
setParentId
(
item
.
_id
);
}
else
{
setAdminMarkData
({
...
adminMarkData
,
datasetId
:
item
.
_id
});
}
}
}
>
<
Flex
alignItems=
{
'center'
}
h=
{
'38px'
}
>
<
Avatar
src=
{
item
.
avatar
}
w=
{
'2rem'
}
borderRadius=
{
'sm'
}
></
Avatar
>
<
MyTooltip
label=
{
item
.
name
}
showOnlyWhenOverflow
>
<
Box
ml=
{
3
}
className=
"textEllipsis"
>
{
item
.
name
}
</
Box
>
</
MyTooltip
>
</
Flex
>
<
Flex
justifyContent=
{
'flex-end'
}
alignItems=
{
'center'
}
fontSize=
{
'sm'
}
>
<
MyIcon
mr=
{
1
}
name=
"kbTest"
w=
{
'12px'
}
/>
<
Box
color=
{
'myGray.500'
}
>
{
item
.
vectorModel
.
name
}
</
Box
>
</
Flex
>
</
Card
>
);
})()
)
}
</
Grid
>
</
DatasetSelectModal
>
)
}
...
...
@@ -109,20 +112,18 @@ const SelectMarkCollection = ({
});
}
}
CustomFooter=
{
<
ModalFooter
>
<
Button
variant=
{
'whiteBase'
}
mr=
{
2
}
onClick=
{
()
=>
{
setAdminMarkData
({
...
adminMarkData
,
datasetId
:
undefined
});
}
}
>
{
t
(
'common:last_step'
)
}
</
Button
>
</
ModalFooter
>
<
Button
variant=
{
'whiteBase'
}
mr=
{
2
}
onClick=
{
()
=>
{
setAdminMarkData
({
...
adminMarkData
,
datasetId
:
undefined
});
}
}
>
{
t
(
'common:last_step'
)
}
</
Button
>
}
/>
)
}
...
...
projects/app/src/components/core/dataset/SelectModal.tsx
View file @
a1674a5f
import
{
getDatasets
,
getDatasetPaths
}
from
'@/web/core/dataset/api'
;
import
MyModal
from
'@fastgpt/web/components/common/MyModal'
;
import
MyModal
from
'@fastgpt/web/components/
v2/
common/MyModal'
;
import
React
,
{
type
Dispatch
,
useState
}
from
'react'
;
import
{
useTranslation
}
from
'next-i18next'
;
import
{
Box
}
from
'@chakra-ui/react'
;
import
FolderPath
from
'@/components/common/folder/Path'
;
import
MyBox
from
'@fastgpt/web/components/common/MyBox'
;
import
{
useRequest
}
from
'@fastgpt/web/hooks/useRequest'
;
import
type
{
ParentIdType
,
...
...
@@ -32,7 +31,6 @@ const DatasetSelectContainer = ({
return
(
<
MyModal
iconSrc=
"/imgs/workflow/db.png"
title=
{
<
Box
fontWeight=
{
'normal'
}
>
<
FolderPath
...
...
@@ -58,10 +56,9 @@ const DatasetSelectContainer = ({
w=
{
'100%'
}
maxW=
{
[
'90vw'
,
'900px'
]
}
isCentered
isLoading=
{
isLoading
}
>
<
MyBox
isLoading=
{
isLoading
}
h=
{
'100%'
}
>
{
children
}
</
MyBox
>
{
children
}
</
MyModal
>
);
};
...
...
projects/app/src/web/core/dataset/components/SelectCollections.tsx
View file @
a1674a5f
import
MyIcon
from
'@fastgpt/web/components/common/Icon'
;
import
MyModal
from
'@fastgpt/web/components/common/MyModal'
;
import
MyModal
from
'@fastgpt/web/components/
v2/
common/MyModal'
;
import
FolderPath
from
'@/components/common/folder/Path'
;
import
{
useRequest
}
from
'@fastgpt/web/hooks/useRequest'
;
import
{
...
...
@@ -118,7 +118,7 @@ const SelectCollections = ({
w=
{
'100%'
}
h=
{
[
'90vh'
,
'80vh'
]
}
isCentered
i
conSrc=
"/imgs/modal/move.svg"
i
sLoading=
{
isLoading
}
title=
{
<
Box
>
<
FolderPath
...
...
@@ -148,72 +148,10 @@ const SelectCollections = ({
/>
</
Box
>
}
>
<
ModalBody
flex=
{
'1 0 0'
}
overflow=
{
'auto'
}
>
<
Grid
gridTemplateColumns=
{
[
'repeat(1,1fr)'
,
'repeat(2,1fr)'
]
}
gridGap=
{
3
}
userSelect=
{
'none'
}
mt=
{
2
}
>
{
collections
.
map
((
item
)
=>
(()
=>
{
const
selected
=
selectedDatasetCollectionIds
.
includes
(
item
.
_id
);
return
(
<
Card
key=
{
item
.
_id
}
p=
{
3
}
border=
{
theme
.
borders
.
base
}
boxShadow=
{
'sm'
}
cursor=
{
'pointer'
}
_hover=
{
{
bg
:
'primary.50'
,
borderColor
:
'primary.300'
}
}
{
...
(
selected
?
{
bg
:
'
primary
.
200'
}
:
{})}
onClick=
{
()
=>
{
if
(
item
.
type
===
DatasetCollectionTypeEnum
.
folder
)
{
setParentId
(
item
.
_id
);
}
else
{
let
result
:
string
[]
=
[];
if
(
max
===
1
)
{
result
=
[
item
.
_id
];
}
else
if
(
selected
)
{
result
=
selectedDatasetCollectionIds
.
filter
((
id
)
=>
id
!==
item
.
_id
);
}
else
if
(
selectedDatasetCollectionIds
.
length
<
max
)
{
result
=
[...
selectedDatasetCollectionIds
,
item
.
_id
];
}
setSelectedDatasetCollectionIds
(
result
);
onChange
?.({
parentId
,
collectionIds
:
result
});
}
}
}
>
<
Flex
alignItems=
{
'center'
}
h=
{
'38px'
}
minW=
{
0
}
>
<
MyIcon
name=
{
item
.
icon
as
any
}
w=
{
'18px'
}
flexShrink=
{
0
}
/>
<
Box
ml=
{
3
}
fontSize=
{
'sm'
}
minW=
{
0
}
flex=
{
1
}
>
<
MyTooltip
label=
{
item
.
name
}
showOnlyWhenOverflow
>
<
Box
className=
"textEllipsis"
>
{
item
.
name
}
</
Box
>
</
MyTooltip
>
</
Box
>
</
Flex
>
</
Card
>
);
})()
)
}
</
Grid
>
{
collections
.
length
===
0
&&
(
<
EmptyTip
pt=
{
'20vh'
}
text=
{
t
(
'common:no_child_folder'
)
}
></
EmptyTip
>
)
}
<
Loading
loading=
{
isLoading
}
fixed=
{
false
}
/>
</
ModalBody
>
{
CustomFooter
?
(
<>
{
CustomFooter
}
</>
)
:
(
<
ModalFooter
>
footer=
{
CustomFooter
?
(
<>
{
CustomFooter
}
</>
)
:
(
<
Button
isLoading=
{
isResponding
}
isDisabled=
{
type
===
'collection'
&&
selectedDatasetCollectionIds
.
length
===
0
}
...
...
@@ -221,7 +159,66 @@ const SelectCollections = ({
>
{
type
===
'folder'
?
t
(
'common:confirm_move'
)
:
t
(
'common:Confirm'
)
}
</
Button
>
</
ModalFooter
>
)
}
>
<
Grid
gridTemplateColumns=
{
[
'repeat(1,1fr)'
,
'repeat(2,1fr)'
]
}
gridGap=
{
3
}
userSelect=
{
'none'
}
mt=
{
2
}
>
{
collections
.
map
((
item
)
=>
(()
=>
{
const
selected
=
selectedDatasetCollectionIds
.
includes
(
item
.
_id
);
return
(
<
Card
key=
{
item
.
_id
}
p=
{
3
}
border=
{
theme
.
borders
.
base
}
boxShadow=
{
'sm'
}
cursor=
{
'pointer'
}
_hover=
{
{
bg
:
'primary.50'
,
borderColor
:
'primary.300'
}
}
{
...
(
selected
?
{
bg
:
'
primary
.
200'
}
:
{})}
onClick=
{
()
=>
{
if
(
item
.
type
===
DatasetCollectionTypeEnum
.
folder
)
{
setParentId
(
item
.
_id
);
}
else
{
let
result
:
string
[]
=
[];
if
(
max
===
1
)
{
result
=
[
item
.
_id
];
}
else
if
(
selected
)
{
result
=
selectedDatasetCollectionIds
.
filter
((
id
)
=>
id
!==
item
.
_id
);
}
else
if
(
selectedDatasetCollectionIds
.
length
<
max
)
{
result
=
[...
selectedDatasetCollectionIds
,
item
.
_id
];
}
setSelectedDatasetCollectionIds
(
result
);
onChange
?.({
parentId
,
collectionIds
:
result
});
}
}
}
>
<
Flex
alignItems=
{
'center'
}
h=
{
'38px'
}
minW=
{
0
}
>
<
MyIcon
name=
{
item
.
icon
as
any
}
w=
{
'18px'
}
flexShrink=
{
0
}
/>
<
Box
ml=
{
3
}
fontSize=
{
'sm'
}
minW=
{
0
}
flex=
{
1
}
>
<
MyTooltip
label=
{
item
.
name
}
showOnlyWhenOverflow
>
<
Box
className=
"textEllipsis"
>
{
item
.
name
}
</
Box
>
</
MyTooltip
>
</
Box
>
</
Flex
>
</
Card
>
);
})()
)
}
</
Grid
>
{
collections
.
length
===
0
&&
(
<
EmptyTip
pt=
{
'20vh'
}
text=
{
t
(
'common:no_child_folder'
)
}
></
EmptyTip
>
)
}
</
MyModal
>
);
...
...
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