Commit a1674a5f by Archer Committed by GitHub

fix: action (#7128)

* fix: action

* perf: modal

* fix: openapi
parent 4f30121d
...@@ -119,6 +119,13 @@ jobs: ...@@ -119,6 +119,13 @@ jobs:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }} 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 - name: Download digests
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
with: with:
...@@ -132,9 +139,20 @@ jobs: ...@@ -132,9 +139,20 @@ jobs:
- name: Set image name and tag - name: Set image name and tag
run: | run: |
echo "Git_Tag=ghcr.io/${{ github.repository_owner }}/${{ matrix.image }}:${{ matrix.tag }}" >> $GITHUB_ENV 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 - name: Create manifest list and push
working-directory: ${{ runner.temp }}/digests working-directory: ${{ runner.temp }}/digests
run: | run: |
docker buildx imagetools create -t "${Git_Tag}" \ TAGS="${Git_Tag}"
$(printf 'ghcr.io/${{ github.repository_owner }}/${{ matrix.image }}@sha256:%s ' *) 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
...@@ -129,5 +129,21 @@ export const GetPreviewNodeQuerySchema = z.union([ ...@@ -129,5 +129,21 @@ export const GetPreviewNodeQuerySchema = z.union([
export type GetPreviewNodeQuery = z.infer<typeof GetPreviewNodeQuerySchema>; 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 const GetPreviewNodeResponseSchema = ToolPreviewNodeResponseSchema;
export type GetPreviewNodeResponse = z.infer<typeof GetPreviewNodeResponseSchema>; export type GetPreviewNodeResponse = z.infer<typeof GetPreviewNodeResponseSchema>;
import type { OpenAPIPath } from '../../../type'; import type { OpenAPIPath } from '../../../type';
import { TagsMap } from '../../../tag'; import { TagsMap } from '../../../tag';
import { import {
GetPreviewNodeQuerySchema, GetPreviewNodeQueryOpenAPISchema,
GetPreviewNodeResponseSchema, GetPreviewNodeResponseSchema,
GetSystemToolTemplatesBodySchema, GetSystemToolTemplatesBodySchema,
GetSystemToolTemplatesResponseSchema, GetSystemToolTemplatesResponseSchema,
...@@ -61,7 +61,7 @@ export const ToolPath: OpenAPIPath = { ...@@ -61,7 +61,7 @@ export const ToolPath: OpenAPIPath = {
'根据工具 ID 和版本配置生成可插入工作流画布的工具节点模板,支持系统工具和我的工具(MCP、HTTP、工作流工具)', '根据工具 ID 和版本配置生成可插入工作流画布的工具节点模板,支持系统工具和我的工具(MCP、HTTP、工作流工具)',
tags: [TagsMap.appSystemTool, TagsMap.httpTools, TagsMap.mcpTools, TagsMap.pluginTeam], tags: [TagsMap.appSystemTool, TagsMap.httpTools, TagsMap.mcpTools, TagsMap.pluginTeam],
requestParams: { requestParams: {
query: GetPreviewNodeQuerySchema query: GetPreviewNodeQueryOpenAPISchema
}, },
responses: { responses: {
200: { 200: {
......
import { describe, expect, it } from 'vitest'; import { describe, expect, it } from 'vitest';
import { openAPIDocument } from '@fastgpt/global/openapi';
import { GetPreviewNodeQuerySchema } from '@fastgpt/global/openapi/core/app/tool/api'; import { GetPreviewNodeQuerySchema } from '@fastgpt/global/openapi/core/app/tool/api';
describe('GetPreviewNodeQuerySchema', () => { describe('GetPreviewNodeQuerySchema', () => {
...@@ -51,4 +52,8 @@ describe('GetPreviewNodeQuerySchema', () => { ...@@ -51,4 +52,8 @@ describe('GetPreviewNodeQuerySchema', () => {
}).success }).success
).toBe(false); ).toBe(false);
}); });
it('keeps full OpenAPI document generation compatible with query params', () => {
expect(openAPIDocument.paths['/core/app/tool/getPreviewNode']?.get).toBeDefined();
});
}); });
import React from 'react'; 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 { useTranslation } from 'next-i18next';
import Avatar from '@fastgpt/web/components/common/Avatar'; import Avatar from '@fastgpt/web/components/common/Avatar';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
...@@ -9,6 +9,7 @@ import dynamic from 'next/dynamic'; ...@@ -9,6 +9,7 @@ import dynamic from 'next/dynamic';
import { type AdminFbkType } from '@fastgpt/global/core/chat/type'; import { type AdminFbkType } from '@fastgpt/global/core/chat/type';
import SelectCollections from '@/web/core/dataset/components/SelectCollections'; import SelectCollections from '@/web/core/dataset/components/SelectCollections';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip'; import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
const InputDataModal = dynamic( const InputDataModal = dynamic(
() => import('@/pageComponents/dataset/detail/components/InputDataModal') () => import('@/pageComponents/dataset/detail/components/InputDataModal')
...@@ -49,49 +50,51 @@ const SelectMarkCollection = ({ ...@@ -49,49 +50,51 @@ const SelectMarkCollection = ({
isLoading={isFetching} isLoading={isFetching}
tips={t('common:core.chat.Select dataset Desc')} tips={t('common:core.chat.Select dataset Desc')}
> >
<ModalBody flex={'1 0 0'} overflowY={'auto'}> {datasets.length === 0 && <EmptyTip text={t('chat:empty_directory')}></EmptyTip>}
<Grid <Grid
display={'grid'} display={'grid'}
gridTemplateColumns={['repeat(1,1fr)', 'repeat(2,1fr)', 'repeat(3,1fr)']} gridTemplateColumns={['repeat(1,1fr)', 'repeat(2,1fr)', 'repeat(3,1fr)']}
gridGap={3} gridGap={3}
userSelect={'none'} userSelect={'none'}
> >
{datasets.map((item) => {datasets.map((item) =>
(() => { (() => {
return ( return (
<Card <Card
key={item._id} key={item._id}
p={3} p={3}
border={theme.borders.base} border={theme.borders.base}
boxShadow={'sm'} boxShadow={'sm'}
h={'80px'} h={'80px'}
cursor={'pointer'} cursor={'pointer'}
_hover={{ _hover={{
boxShadow: 'md' boxShadow: 'md'
}} }}
onClick={() => { onClick={() => {
if (item.type === DatasetTypeEnum.folder) { if (item.type === DatasetTypeEnum.folder) {
setParentId(item._id); setParentId(item._id);
} else { } else {
setAdminMarkData({ ...adminMarkData, datasetId: item._id }); setAdminMarkData({ ...adminMarkData, datasetId: item._id });
} }
}} }}
> >
<Flex alignItems={'center'} h={'38px'}> <Flex alignItems={'center'} h={'38px'}>
<Avatar src={item.avatar} w={'2rem'} borderRadius={'sm'}></Avatar> <Avatar src={item.avatar} w={'2rem'} borderRadius={'sm'}></Avatar>
<Box ml={3}>{item.name}</Box> <MyTooltip label={item.name} showOnlyWhenOverflow>
</Flex> <Box ml={3} className="textEllipsis">
<Flex justifyContent={'flex-end'} alignItems={'center'} fontSize={'sm'}> {item.name}
<MyIcon mr={1} name="kbTest" w={'12px'} /> </Box>
<Box color={'myGray.500'}>{item.vectorModel.name}</Box> </MyTooltip>
</Flex> </Flex>
</Card> <Flex justifyContent={'flex-end'} alignItems={'center'} fontSize={'sm'}>
); <MyIcon mr={1} name="kbTest" w={'12px'} />
})() <Box color={'myGray.500'}>{item.vectorModel.name}</Box>
)} </Flex>
</Grid> </Card>
{datasets.length === 0 && <EmptyTip text={t('chat:empty_directory')}></EmptyTip>} );
</ModalBody> })()
)}
</Grid>
</DatasetSelectModal> </DatasetSelectModal>
)} )}
...@@ -109,20 +112,18 @@ const SelectMarkCollection = ({ ...@@ -109,20 +112,18 @@ const SelectMarkCollection = ({
}); });
}} }}
CustomFooter={ CustomFooter={
<ModalFooter> <Button
<Button variant={'whiteBase'}
variant={'whiteBase'} mr={2}
mr={2} onClick={() => {
onClick={() => { setAdminMarkData({
setAdminMarkData({ ...adminMarkData,
...adminMarkData, datasetId: undefined
datasetId: undefined });
}); }}
}} >
> {t('common:last_step')}
{t('common:last_step')} </Button>
</Button>
</ModalFooter>
} }
/> />
)} )}
......
import { getDatasets, getDatasetPaths } from '@/web/core/dataset/api'; 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 React, { type Dispatch, useState } from 'react';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { Box } from '@chakra-ui/react'; import { Box } from '@chakra-ui/react';
import FolderPath from '@/components/common/folder/Path'; import FolderPath from '@/components/common/folder/Path';
import MyBox from '@fastgpt/web/components/common/MyBox';
import { useRequest } from '@fastgpt/web/hooks/useRequest'; import { useRequest } from '@fastgpt/web/hooks/useRequest';
import type { import type {
ParentIdType, ParentIdType,
...@@ -32,7 +31,6 @@ const DatasetSelectContainer = ({ ...@@ -32,7 +31,6 @@ const DatasetSelectContainer = ({
return ( return (
<MyModal <MyModal
iconSrc="/imgs/workflow/db.png"
title={ title={
<Box fontWeight={'normal'}> <Box fontWeight={'normal'}>
<FolderPath <FolderPath
...@@ -58,10 +56,9 @@ const DatasetSelectContainer = ({ ...@@ -58,10 +56,9 @@ const DatasetSelectContainer = ({
w={'100%'} w={'100%'}
maxW={['90vw', '900px']} maxW={['90vw', '900px']}
isCentered isCentered
isLoading={isLoading}
> >
<MyBox isLoading={isLoading} h={'100%'}> {children}
{children}
</MyBox>
</MyModal> </MyModal>
); );
}; };
......
import MyIcon from '@fastgpt/web/components/common/Icon'; 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 FolderPath from '@/components/common/folder/Path';
import { useRequest } from '@fastgpt/web/hooks/useRequest'; import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { import {
...@@ -118,7 +118,7 @@ const SelectCollections = ({ ...@@ -118,7 +118,7 @@ const SelectCollections = ({
w={'100%'} w={'100%'}
h={['90vh', '80vh']} h={['90vh', '80vh']}
isCentered isCentered
iconSrc="/imgs/modal/move.svg" isLoading={isLoading}
title={ title={
<Box> <Box>
<FolderPath <FolderPath
...@@ -148,72 +148,10 @@ const SelectCollections = ({ ...@@ -148,72 +148,10 @@ const SelectCollections = ({
/> />
</Box> </Box>
} }
> footer={
<ModalBody flex={'1 0 0'} overflow={'auto'}> CustomFooter ? (
<Grid <>{CustomFooter}</>
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>
<Button <Button
isLoading={isResponding} isLoading={isResponding}
isDisabled={type === 'collection' && selectedDatasetCollectionIds.length === 0} isDisabled={type === 'collection' && selectedDatasetCollectionIds.length === 0}
...@@ -221,7 +159,66 @@ const SelectCollections = ({ ...@@ -221,7 +159,66 @@ const SelectCollections = ({
> >
{type === 'folder' ? t('common:confirm_move') : t('common:Confirm')} {type === 'folder' ? t('common:confirm_move') : t('common:Confirm')}
</Button> </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> </MyModal>
); );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment