perf: model test;perf: sidebar trigger (#4127)

* fix: import dataset step error;perf: ai proxy avatar (#4074)

* perf: pg config params

* perf: ai proxy avatar

* fix: import dataset step error

* feat: data input ux

* perf: app dataset rewite

* perf: model test

* perf: sidebar trigger

* lock

* update nanoid version

* fix: select component ux

* fix: ts

* fix: vitest

* remove test
This commit is contained in:
Archer 2025-03-12 21:11:43 +08:00 committed by archer
parent c131c2a7dc
commit f71ab0caeb
No known key found for this signature in database
GPG Key ID: 4446499B846D4A9E
64 changed files with 438 additions and 1356 deletions

View File

@ -30,3 +30,4 @@ weight: 799
4. 错误提示翻译缺失。 4. 错误提示翻译缺失。
5. 内容提取节点array 类型 schema 错误。 5. 内容提取节点array 类型 schema 错误。
6. 模型渠道测试时,实际未指定渠道测试。 6. 模型渠道测试时,实际未指定渠道测试。
7. 新增自定义模型时,会把默认模型字段也保存,导致默认模型误判。

View File

@ -9,7 +9,7 @@
"encoding": "^0.1.13", "encoding": "^0.1.13",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"jschardet": "3.1.1", "jschardet": "3.1.1",
"nanoid": "^4.0.1", "nanoid": "^5.1.3",
"next": "14.2.21", "next": "14.2.21",
"openai": "4.61.0", "openai": "4.61.0",
"openapi-types": "^12.1.3", "openapi-types": "^12.1.3",

View File

@ -300,6 +300,9 @@ export const readRawContentByFileBuffer = async ({
return systemParse(); return systemParse();
}; };
const start = Date.now();
addLog.debug(`Start parse file`, { extension });
let { rawText, formatText, imageList } = await (async () => { let { rawText, formatText, imageList } = await (async () => {
if (extension === 'pdf') { if (extension === 'pdf') {
return await pdfParseFn(); return await pdfParseFn();
@ -307,6 +310,8 @@ export const readRawContentByFileBuffer = async ({
return await systemParse(); return await systemParse();
})(); })();
addLog.debug(`Parse file success, time: ${Date.now() - start}ms. Uploading file image.`);
// markdown data format // markdown data format
if (imageList) { if (imageList) {
await batchRun(imageList, async (item) => { await batchRun(imageList, async (item) => {
@ -341,5 +346,7 @@ export const readRawContentByFileBuffer = async ({
} }
} }
addLog.debug(`Upload file image success, time: ${Date.now() - start}ms`);
return { rawText, formatText, imageList }; return { rawText, formatText, imageList };
}; };

View File

@ -76,6 +76,10 @@ export const createChatCompletion = async ({
timeout: formatTimeout timeout: formatTimeout
}); });
addLog.debug(`Start create chat completion`, {
model: body.model
});
const response = await ai.chat.completions.create(body, { const response = await ai.chat.completions.create(body, {
...options, ...options,
...(modelConstantsData.requestUrl ? { path: modelConstantsData.requestUrl } : {}), ...(modelConstantsData.requestUrl ? { path: modelConstantsData.requestUrl } : {}),

View File

@ -36,14 +36,12 @@ export async function getVectorsByText({ model, input, type, headers }: GetVecto
model.requestUrl model.requestUrl
? { ? {
path: model.requestUrl, path: model.requestUrl,
headers: model.requestAuth headers: {
? { ...(model.requestAuth ? { Authorization: `Bearer ${model.requestAuth}` } : {}),
Authorization: `Bearer ${model.requestAuth}`, ...headers
...headers }
}
: headers
} }
: {} : { headers }
) )
.then(async (res) => { .then(async (res) => {
if (!res.data) { if (!res.data) {

View File

@ -61,7 +61,6 @@ export async function rewriteAppWorkflowToDetail({
teamId: isRoot ? undefined : teamId, teamId: isRoot ? undefined : teamId,
datasetIdList: Array.from(datasetIdSet) datasetIdList: Array.from(datasetIdSet)
}); });
const datasetMap = new Map(datasetList.map((ds) => [String(ds.datasetId), ds])); const datasetMap = new Map(datasetList.map((ds) => [String(ds.datasetId), ds]));
// Rewrite dataset ids, add dataset info to nodes // Rewrite dataset ids, add dataset info to nodes

View File

@ -9,7 +9,7 @@
"axios": "^1.8.2", "axios": "^1.8.2",
"chalk": "^5.3.0", "chalk": "^5.3.0",
"cheerio": "1.0.0-rc.12", "cheerio": "1.0.0-rc.12",
"cookie": "^0.5.0", "cookie": "^0.7.1",
"date-fns": "2.30.0", "date-fns": "2.30.0",
"dayjs": "^1.11.7", "dayjs": "^1.11.7",
"decompress": "^4.2.1", "decompress": "^4.2.1",

View File

@ -9,6 +9,7 @@ type Props = FlexProps & {
onClick?: () => void; onClick?: () => void;
hoverColor?: string; hoverColor?: string;
tip?: string; tip?: string;
isLoading?: boolean;
}; };
const MyIconButton = ({ const MyIconButton = ({
@ -17,11 +18,13 @@ const MyIconButton = ({
hoverColor = 'primary.600', hoverColor = 'primary.600',
size = '1rem', size = '1rem',
tip, tip,
isLoading = false,
...props ...props
}: Props) => { }: Props) => {
return ( return (
<MyTooltip label={tip}> <MyTooltip label={tip}>
<Flex <Flex
position={'relative'}
p={1} p={1}
color={'myGray.500'} color={'myGray.500'}
rounded={'sm'} rounded={'sm'}
@ -33,11 +36,14 @@ const MyIconButton = ({
bg: 'myGray.05', bg: 'myGray.05',
color: hoverColor color: hoverColor
}} }}
onClick={onClick} onClick={() => {
if (isLoading) return;
onClick?.();
}}
sx={{ userSelect: 'none' }} sx={{ userSelect: 'none' }}
{...props} {...props}
> >
<MyIcon name={icon as any} w={size} /> <MyIcon name={isLoading ? 'common/loading' : (icon as any)} w={size} />
</Flex> </Flex>
</MyTooltip> </MyTooltip>
); );

View File

@ -24,6 +24,7 @@ import MyIcon from '../Icon';
import { useRequest2 } from '../../../hooks/useRequest'; import { useRequest2 } from '../../../hooks/useRequest';
import MyDivider from '../MyDivider'; import MyDivider from '../MyDivider';
import { useScrollPagination } from '../../../hooks/useScrollPagination'; import { useScrollPagination } from '../../../hooks/useScrollPagination';
import Avatar from '../Avatar';
/** Props /** Props
* value: 选中的值 * value: 选中的值
@ -32,20 +33,21 @@ import { useScrollPagination } from '../../../hooks/useScrollPagination';
* isLoading: 是否加载中 * isLoading: 是否加载中
* ScrollData: 分页滚动数据控制器 [useScrollPagination] * ScrollData: 分页滚动数据控制器 [useScrollPagination]
* */ * */
export type SelectProps<T = any> = ButtonProps & { export type SelectProps<T = any> = Omit<ButtonProps, 'onChange'> & {
value?: T; value?: T;
placeholder?: string; placeholder?: string;
isSearch?: boolean; isSearch?: boolean;
list: { list: {
alias?: string; alias?: string;
icon?: string; icon?: string;
iconSize?: string;
label: string | React.ReactNode; label: string | React.ReactNode;
description?: string; description?: string;
value: T; value: T;
showBorder?: boolean; showBorder?: boolean;
}[]; }[];
isLoading?: boolean; isLoading?: boolean;
onchange?: (val: T) => any | Promise<any>; onChange?: (val: T) => any | Promise<any>;
ScrollData?: ReturnType<typeof useScrollPagination>['ScrollData']; ScrollData?: ReturnType<typeof useScrollPagination>['ScrollData'];
}; };
@ -56,7 +58,7 @@ const MySelect = <T = any,>(
isSearch = false, isSearch = false,
width = '100%', width = '100%',
list = [], list = [],
onchange, onChange,
isLoading = false, isLoading = false,
ScrollData, ScrollData,
...props ...props
@ -115,7 +117,7 @@ const MySelect = <T = any,>(
} }
}, [isSearch, isOpen]); }, [isSearch, isOpen]);
const { runAsync: onChange, loading } = useRequest2((val: T) => onchange?.(val)); const { runAsync: onclickChange, loading } = useRequest2((val: T) => onChange?.(val));
const ListRender = useMemo(() => { const ListRender = useMemo(() => {
return ( return (
@ -135,16 +137,17 @@ const MySelect = <T = any,>(
color: 'myGray.900' color: 'myGray.900'
})} })}
onClick={() => { onClick={() => {
if (onChange && value !== item.value) { if (value !== item.value) {
onChange(item.value); onclickChange(item.value);
} }
}} }}
whiteSpace={'pre-wrap'} whiteSpace={'pre-wrap'}
fontSize={'sm'} fontSize={'sm'}
display={'block'} display={'block'}
mb={0.5}
> >
<Flex alignItems={'center'}> <Flex alignItems={'center'}>
{item.icon && <MyIcon mr={2} name={item.icon as any} w={'1rem'} />} {item.icon && <Avatar mr={2} src={item.icon as any} w={item.iconSize ?? '1rem'} />}
{item.label} {item.label}
</Flex> </Flex>
{item.description && ( {item.description && (
@ -224,7 +227,9 @@ const MySelect = <T = any,>(
/> />
) : ( ) : (
<> <>
{selectItem?.icon && <MyIcon mr={2} name={selectItem.icon as any} w={'1rem'} />} {selectItem?.icon && (
<Avatar mr={2} src={selectItem.icon as any} w={selectItem.iconSize ?? '1rem'} />
)}
{selectItem?.alias || selectItem?.label || placeholder} {selectItem?.alias || selectItem?.label || placeholder}
</> </>
)} )}

View File

@ -200,7 +200,7 @@ export function usePagination<DataT, ResT = {}>(
// Watch scroll position // Watch scroll position
useThrottleEffect( useThrottleEffect(
() => { () => {
if (!ref?.current || type !== 'scroll' || noMore) return; if (!ref?.current || type !== 'scroll' || noMore || isLoading) return;
const { scrollTop, scrollHeight, clientHeight } = ref.current; const { scrollTop, scrollHeight, clientHeight } = ref.current;
if ( if (
@ -211,7 +211,7 @@ export function usePagination<DataT, ResT = {}>(
fetchData(pageNum + 1, ref); fetchData(pageNum + 1, ref);
} }
}, },
[scroll], [scroll, isLoading],
{ wait: 50 } { wait: 50 }
); );

View File

@ -21,7 +21,6 @@
"edit_channel": "Channel configuration", "edit_channel": "Channel configuration",
"enable_channel": "Enable", "enable_channel": "Enable",
"forbid_channel": "Disabled", "forbid_channel": "Disabled",
"maxToken_tip": "The model max_tokens parameter, if left blank, means that the model does not support it.",
"key_type": "API key format:", "key_type": "API key format:",
"log": "Call log", "log": "Call log",
"log_detail": "Log details", "log_detail": "Log details",
@ -29,6 +28,7 @@
"log_status": "Status", "log_status": "Status",
"mapping": "Model Mapping", "mapping": "Model Mapping",
"mapping_tip": "A valid Json is required. \nThe model can be mapped when sending a request to the actual address. \nFor example:\n{\n \n \"gpt-4o\": \"gpt-4o-test\"\n\n}\n\nWhen FastGPT requests the gpt-4o model, the gpt-4o-test model is sent to the actual address, instead of gpt-4o.", "mapping_tip": "A valid Json is required. \nThe model can be mapped when sending a request to the actual address. \nFor example:\n{\n \n \"gpt-4o\": \"gpt-4o-test\"\n\n}\n\nWhen FastGPT requests the gpt-4o model, the gpt-4o-test model is sent to the actual address, instead of gpt-4o.",
"maxToken_tip": "The model max_tokens parameter, if left blank, means that the model does not support it.",
"max_temperature_tip": "If the model temperature parameter is not filled in, it means that the model does not support the temperature parameter.", "max_temperature_tip": "If the model temperature parameter is not filled in, it means that the model does not support the temperature parameter.",
"model": "Model", "model": "Model",
"model_name": "Model name", "model_name": "Model name",
@ -43,7 +43,7 @@
"select_model_placeholder": "Select the model available under this channel", "select_model_placeholder": "Select the model available under this channel",
"select_provider_placeholder": "Search for manufacturers", "select_provider_placeholder": "Search for manufacturers",
"selected_model_empty": "Choose at least one model", "selected_model_empty": "Choose at least one model",
"start_test": "Start testing {{num}} models", "start_test": "Batch test {{num}} models",
"test_failed": "There are {{num}} models that report errors", "test_failed": "There are {{num}} models that report errors",
"vlm_model": "Vlm", "vlm_model": "Vlm",
"vlm_model_tip": "Used to generate additional indexing of images in a document in the knowledge base", "vlm_model_tip": "Used to generate additional indexing of images in a document in the knowledge base",

View File

@ -21,7 +21,6 @@
"edit_channel": "渠道配置", "edit_channel": "渠道配置",
"enable_channel": "启用", "enable_channel": "启用",
"forbid_channel": "禁用", "forbid_channel": "禁用",
"maxToken_tip": "模型 max_tokens 参数,如果留空,则代表模型不支持该参数。",
"key_type": "API key 格式: ", "key_type": "API key 格式: ",
"log": "调用日志", "log": "调用日志",
"log_detail": "日志详情", "log_detail": "日志详情",
@ -29,6 +28,7 @@
"log_status": "状态", "log_status": "状态",
"mapping": "模型映射", "mapping": "模型映射",
"mapping_tip": "需填写一个有效 Json。可在向实际地址发送请求时对模型进行映射。例如\n{\n \"gpt-4o\": \"gpt-4o-test\"\n}\n当 FastGPT 请求 gpt-4o 模型时,会向实际地址发送 gpt-4o-test 的模型,而不是 gpt-4o。", "mapping_tip": "需填写一个有效 Json。可在向实际地址发送请求时对模型进行映射。例如\n{\n \"gpt-4o\": \"gpt-4o-test\"\n}\n当 FastGPT 请求 gpt-4o 模型时,会向实际地址发送 gpt-4o-test 的模型,而不是 gpt-4o。",
"maxToken_tip": "模型 max_tokens 参数,如果留空,则代表模型不支持该参数。",
"max_temperature_tip": "模型 temperature 参数,不填则代表模型不支持 temperature 参数。", "max_temperature_tip": "模型 temperature 参数,不填则代表模型不支持 temperature 参数。",
"model": "模型", "model": "模型",
"model_name": "模型名", "model_name": "模型名",
@ -43,7 +43,7 @@
"select_model_placeholder": "选择该渠道下可用的模型", "select_model_placeholder": "选择该渠道下可用的模型",
"select_provider_placeholder": "搜索厂商", "select_provider_placeholder": "搜索厂商",
"selected_model_empty": "至少选择一个模型", "selected_model_empty": "至少选择一个模型",
"start_test": "开始测试{{num}}个模型", "start_test": "批量测试{{num}}个模型",
"test_failed": "有{{num}}个模型报错", "test_failed": "有{{num}}个模型报错",
"vlm_model": "图片理解模型", "vlm_model": "图片理解模型",
"vlm_model_tip": "用于知识库中对文档中的图片进行额外的索引生成", "vlm_model_tip": "用于知识库中对文档中的图片进行额外的索引生成",

View File

@ -19,7 +19,6 @@
"edit_channel": "渠道配置", "edit_channel": "渠道配置",
"enable_channel": "啟用", "enable_channel": "啟用",
"forbid_channel": "禁用", "forbid_channel": "禁用",
"maxToken_tip": "模型 max_tokens 參數,如果留空,則代表模型不支持該參數。",
"key_type": "API key 格式:", "key_type": "API key 格式:",
"log": "調用日誌", "log": "調用日誌",
"log_detail": "日誌詳情", "log_detail": "日誌詳情",
@ -27,6 +26,7 @@
"log_status": "狀態", "log_status": "狀態",
"mapping": "模型映射", "mapping": "模型映射",
"mapping_tip": "需填寫一個有效 Json。\n可在向實際地址發送請求時對模型進行映射。\n例如\n{\n \n \"gpt-4o\": \"gpt-4o-test\"\n\n}\n\n當 FastGPT 請求 gpt-4o 模型時,會向實際地址發送 gpt-4o-test 的模型,而不是 gpt-4o。", "mapping_tip": "需填寫一個有效 Json。\n可在向實際地址發送請求時對模型進行映射。\n例如\n{\n \n \"gpt-4o\": \"gpt-4o-test\"\n\n}\n\n當 FastGPT 請求 gpt-4o 模型時,會向實際地址發送 gpt-4o-test 的模型,而不是 gpt-4o。",
"maxToken_tip": "模型 max_tokens 參數,如果留空,則代表模型不支持該參數。",
"max_temperature_tip": "模型 temperature 參數,不填則代表模型不支持 temperature 參數。", "max_temperature_tip": "模型 temperature 參數,不填則代表模型不支持 temperature 參數。",
"model": "模型", "model": "模型",
"model_name": "模型名", "model_name": "模型名",
@ -41,7 +41,7 @@
"select_model_placeholder": "選擇該渠道下可用的模型", "select_model_placeholder": "選擇該渠道下可用的模型",
"select_provider_placeholder": "搜索廠商", "select_provider_placeholder": "搜索廠商",
"selected_model_empty": "至少選擇一個模型", "selected_model_empty": "至少選擇一個模型",
"start_test": "開始測試{{num}}個模型", "start_test": "批量測試{{num}}個模型",
"test_failed": "有{{num}}個模型報錯", "test_failed": "有{{num}}個模型報錯",
"vlm_model": "圖片理解模型", "vlm_model": "圖片理解模型",
"vlm_model_tip": "用於知識庫中對文檔中的圖片進行額外的索引生成", "vlm_model_tip": "用於知識庫中對文檔中的圖片進行額外的索引生成",

1351
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -43,7 +43,7 @@
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mermaid": "^10.2.3", "mermaid": "^10.2.3",
"nanoid": "^4.0.1", "nanoid": "^5.1.3",
"next": "14.2.21", "next": "14.2.21",
"next-i18next": "15.3.0", "next-i18next": "15.3.0",
"nextjs-node-loader": "^1.1.5", "nextjs-node-loader": "^1.1.5",
@ -84,6 +84,7 @@
"eslint": "8.56.0", "eslint": "8.56.0",
"eslint-config-next": "14.2.3", "eslint-config-next": "14.2.3",
"nextjs-node-loader": "^1.1.5", "nextjs-node-loader": "^1.1.5",
"typescript": "^5.1.3" "typescript": "^5.1.3",
"vitest": "^3.0.2"
} }
} }

View File

@ -20,7 +20,7 @@ type Props = SelectProps & {
disableTip?: string; disableTip?: string;
}; };
const OneRowSelector = ({ list, onchange, disableTip, ...props }: Props) => { const OneRowSelector = ({ list, onChange, disableTip, ...props }: Props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { llmModelList, embeddingModelList, ttsModelList, sttModelList, reRankModelList } = const { llmModelList, embeddingModelList, ttsModelList, sttModelList, reRankModelList } =
useSystemStore(); useSystemStore();
@ -96,12 +96,12 @@ const OneRowSelector = ({ list, onchange, disableTip, ...props }: Props) => {
placeholder={t('common:not_model_config')} placeholder={t('common:not_model_config')}
h={'40px'} h={'40px'}
{...props} {...props}
onchange={(e) => { onChange={(e) => {
if (e === 'price') { if (e === 'price') {
onOpen(); onOpen();
return; return;
} }
return onchange?.(e); return onChange?.(e);
}} }}
/> />
)} )}
@ -110,7 +110,7 @@ const OneRowSelector = ({ list, onchange, disableTip, ...props }: Props) => {
</Box> </Box>
); );
}; };
const MultipleRowSelector = ({ list, onchange, disableTip, placeholder, ...props }: Props) => { const MultipleRowSelector = ({ list, onChange, disableTip, placeholder, ...props }: Props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { llmModelList, embeddingModelList, ttsModelList, sttModelList, reRankModelList } = const { llmModelList, embeddingModelList, ttsModelList, sttModelList, reRankModelList } =
useSystemStore(); useSystemStore();
@ -178,9 +178,9 @@ const MultipleRowSelector = ({ list, onchange, disableTip, placeholder, ...props
const onSelect = useCallback( const onSelect = useCallback(
(e: string[]) => { (e: string[]) => {
return onchange?.(e[1]); return onChange?.(e[1]);
}, },
[onchange] [onChange]
); );
const SelectedModel = useMemo(() => { const SelectedModel = useMemo(() => {

View File

@ -26,7 +26,7 @@ const I18nLngSelector = () => {
<MySelect <MySelect
value={i18n.language} value={i18n.language}
list={list} list={list}
onchange={(val: any) => { onChange={(val: any) => {
const lang = val; const lang = val;
onChangeLng(lang); onChangeLng(lang);
}} }}

View File

@ -1,27 +1,35 @@
import React from 'react'; import React, { useState, useEffect, useRef } from 'react';
import { Box, Flex } from '@chakra-ui/react'; import { Box, Flex } from '@chakra-ui/react';
import type { BoxProps } from '@chakra-ui/react'; import type { BoxProps } from '@chakra-ui/react';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
interface Props extends BoxProps { interface Props extends BoxProps {
isFolded?: boolean; externalTrigger?: Boolean;
onFoldChange?: (isFolded: boolean) => void;
} }
const SideBar = (e?: Props) => { const SideBar = (e?: Props) => {
const { const {
w = ['100%', '0 0 250px', '0 0 250px', '0 0 270px', '0 0 290px'], w = ['100%', '0 0 250px', '0 0 250px', '0 0 270px', '0 0 290px'],
children, children,
isFolded = false, externalTrigger,
onFoldChange,
...props ...props
} = e || {}; } = e || {};
const handleToggle = () => { const [isFolded, setIsFolded] = useState(false);
if (onFoldChange) {
onFoldChange(!isFolded); // 保存上一次折叠状态
const preFoledStatus = useRef<Boolean>(false);
useEffect(() => {
if (externalTrigger) {
setIsFolded(true);
preFoledStatus.current = isFolded;
} else {
// @ts-ignore
setIsFolded(preFoledStatus.current);
} }
}; // eslint-disable-next-line react-hooks/exhaustive-deps
}, [externalTrigger]);
return ( return (
<Box <Box
@ -58,7 +66,7 @@ const SideBar = (e?: Props) => {
visibility: 'hidden', visibility: 'hidden',
opacity: 0 opacity: 0
})} })}
onClick={handleToggle} onClick={() => setIsFolded(!isFolded)}
> >
<MyIcon <MyIcon
name={'common/backLight'} name={'common/backLight'}

View File

@ -154,7 +154,7 @@ const AIChatSettingsModal = ({
value: item.model, value: item.model,
label: item.name label: item.name
}))} }))}
onchange={onChangeModel} onChange={onChangeModel}
/> />
</Box> </Box>
</Flex> </Flex>
@ -385,7 +385,7 @@ const AIChatSettingsModal = ({
label: item label: item
}))} }))}
value={responseFormat} value={responseFormat}
onchange={(e) => { onChange={(e) => {
setValue(NodeInputKeyEnum.aiChatResponseFormat, e); setValue(NodeInputKeyEnum.aiChatResponseFormat, e);
}} }}
/> />

View File

@ -212,7 +212,7 @@ const ModelTable = () => {
w={'200px'} w={'200px'}
bg={'myGray.50'} bg={'myGray.50'}
value={provider} value={provider}
onchange={setProvider} onChange={setProvider}
list={filterProviderList} list={filterProviderList}
/> />
</HStack> </HStack>
@ -224,7 +224,7 @@ const ModelTable = () => {
w={'150px'} w={'150px'}
bg={'myGray.50'} bg={'myGray.50'}
value={modelType} value={modelType}
onchange={setModelType} onChange={setModelType}
list={selectModelTypeList.current} list={selectModelTypeList.current}
/> />
</HStack> </HStack>

View File

@ -77,7 +77,7 @@ const SettingLLMModel = ({
value: item.model, value: item.model,
label: item.name label: item.name
}))} }))}
onchange={(e) => { onChange={(e) => {
onChange({ onChange({
...defaultData, ...defaultData,
model: e model: e

View File

@ -290,7 +290,7 @@ const DatasetParamsModal = ({
width={'100%'} width={'100%'}
value={queryExtensionModel} value={queryExtensionModel}
list={chatModelSelectList} list={chatModelSelectList}
onchange={(val: any) => { onChange={(val: any) => {
setValue('datasetSearchExtensionModel', val); setValue('datasetSearchExtensionModel', val);
}} }}
/> />

View File

@ -125,7 +125,7 @@ const QGConfigModal = ({
value: item.model, value: item.model,
label: item.name label: item.name
}))} }))}
onchange={(e) => { onChange={(e) => {
onChange({ onChange({
...value, ...value,
model: e model: e

View File

@ -80,7 +80,7 @@ export const VariableInputItem = ({
value: item.value value: item.value
}))} }))}
value={value} value={value}
onchange={(e) => setValue(`variables.${item.key}`, e)} onChange={(e) => setValue(`variables.${item.key}`, e)}
/> />
); );
}} }}

View File

@ -162,7 +162,7 @@ const RenderPluginInput = ({
} }
if (inputType === FlowNodeInputTypeEnum.select && input.list) { if (inputType === FlowNodeInputTypeEnum.select && input.list) {
return ( return (
<MySelect list={input.list} value={value} onchange={onChange} isDisabled={isDisabled} /> <MySelect list={input.list} value={value} onChange={onChange} isDisabled={isDisabled} />
); );
} }
if (inputType === FlowNodeInputTypeEnum.fileSelect) { if (inputType === FlowNodeInputTypeEnum.fileSelect) {
@ -179,7 +179,7 @@ const RenderPluginInput = ({
value: item.model, value: item.model,
label: item.name label: item.name
}))} }))}
onchange={onChange} onChange={onChange}
/> />
); );
} }

View File

@ -321,7 +321,7 @@ const RenderUserFormInteractive = React.memo(function RenderFormInput({
list={input.list} list={input.list}
value={value} value={value}
isDisabled={interactive.params.submitted} isDisabled={interactive.params.submitted}
onchange={(e) => setValue(input.label, e)} onChange={(e) => setValue(input.label, e)}
/> />
); );
}} }}

View File

@ -164,7 +164,7 @@ const LafAccountModal = ({
} }
placeholder={t('common:plugin.App')} placeholder={t('common:plugin.App')}
value={watch('appid')} value={watch('appid')}
onchange={(e) => { onChange={(e) => {
setValue('appid', e); setValue('appid', e);
}} }}
{...(register('appid'), { required: true })} {...(register('appid'), { required: true })}

View File

@ -49,7 +49,7 @@ const DefaultPermissionList = ({
<MySelect <MySelect
list={defaultPermissionSelectList} list={defaultPermissionSelectList}
value={per} value={per}
onchange={(per) => { onChange={(per) => {
if (isInheritPermission && hasParent) { if (isInheritPermission && hasParent) {
openConfirm( openConfirm(
() => onRequestChange(per), () => onRequestChange(per),

View File

@ -46,48 +46,21 @@ const TeamSelector = ({
const teamList = useMemo(() => { const teamList = useMemo(() => {
return myTeams.map((team) => ({ return myTeams.map((team) => ({
label: ( icon: team.avatar,
<Flex iconSize: '1.25rem',
key={team.teamId} label: team.teamName,
alignItems={'center'}
borderRadius={'md'}
cursor={'default'}
gap={3}
onClick={() => onSwitchTeam(team.teamId)}
_hover={{
cursor: 'pointer'
}}
>
<Avatar src={team.avatar} w={['1.25rem', '1.375rem']} />
<Box flex={'1 0 0'} w={0} className="textEllipsis" fontSize={'sm'}>
{team.teamName}
</Box>
</Flex>
),
value: team.teamId value: team.teamId
})); }));
}, [myTeams, onSwitchTeam]); }, [myTeams]);
const formatTeamList = useMemo(() => { const formatTeamList = useMemo(() => {
return [ return [
...(showManage ...(showManage
? [ ? [
{ {
label: ( icon: 'common/setting',
<Flex iconSize: '1.25rem',
key={'manage'} label: t('user:manage_team'),
alignItems={'center'}
borderRadius={'md'}
cursor={'pointer'}
gap={3}
onClick={() => router.push('/account/team')}
>
<MyIcon name="common/setting" w={['1.25rem', '1.375rem']} />
<Box flex={'1 0 0'} w={0} className="textEllipsis" fontSize={'sm'}>
{t('user:manage_team')}
</Box>
</Flex>
),
value: 'manage', value: 'manage',
showBorder: true showBorder: true
} }
@ -95,11 +68,24 @@ const TeamSelector = ({
: []), : []),
...teamList ...teamList
]; ];
}, [showManage, t, teamList, router]); }, [showManage, t, teamList]);
const handleChange = (value: string) => {
if (value === 'manage') {
router.push('/account/team');
} else {
onSwitchTeam(value);
}
};
return ( return (
<Box w={'100%'}> <Box w={'100%'}>
<MySelect {...props} value={userInfo?.team?.teamId} list={formatTeamList} /> <MySelect
{...props}
value={userInfo?.team?.teamId}
list={formatTeamList}
onChange={handleChange}
/>
</Box> </Box>
); );
}; };

View File

@ -111,7 +111,7 @@ const BillTable = () => {
list={billTypeList} list={billTypeList}
value={billType} value={billType}
size={'sm'} size={'sm'}
onchange={(e) => { onChange={(e) => {
setBillType(e); setBillType(e);
}} }}
w={'130px'} w={'130px'}

View File

@ -213,7 +213,7 @@ export const ModelEditModal = ({
<Td textAlign={'right'}> <Td textAlign={'right'}>
<MySelect <MySelect
value={provider} value={provider}
onchange={(value) => setValue('provider', value)} onChange={(value) => setValue('provider', value)}
list={providerList.current} list={providerList.current}
{...InputStyles} {...InputStyles}
/> />

View File

@ -194,7 +194,7 @@ const EditChannelModal = ({
placeholder={t('account_model:select_provider_placeholder')} placeholder={t('account_model:select_provider_placeholder')}
value={providerType} value={providerType}
isSearch isSearch
onchange={(val) => { onChange={(val) => {
setValue('type', val); setValue('type', val);
}} }}
/> />
@ -333,6 +333,8 @@ const MultipleSelect = ({ value = [], list = [], onSelect }: SelectProps) => {
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
const { copyData } = useCopyData(); const { copyData } = useCopyData();
const [search, setSearch] = useState('');
const onclickItem = useCallback( const onclickItem = useCallback(
(val: string) => { (val: string) => {
if (value.includes(val)) { if (value.includes(val)) {
@ -343,12 +345,11 @@ const MultipleSelect = ({ value = [], list = [], onSelect }: SelectProps) => {
top: BoxRef.current.scrollHeight top: BoxRef.current.scrollHeight
}); });
} }
setSearch('');
}, },
[value, onSelect] [value, onSelect]
); );
const [search, setSearch] = useState('');
const filterUnSelected = useMemo(() => { const filterUnSelected = useMemo(() => {
return list return list
.filter((item) => !value.includes(item.value)) .filter((item) => !value.includes(item.value))

View File

@ -25,6 +25,7 @@ import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import { getErrText } from '@fastgpt/global/common/error/utils'; import { getErrText } from '@fastgpt/global/common/error/utils';
import { batchRun } from '@fastgpt/global/common/system/utils'; import { batchRun } from '@fastgpt/global/common/system/utils';
import { useToast } from '@fastgpt/web/hooks/useToast'; import { useToast } from '@fastgpt/web/hooks/useToast';
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
type ModelTestItem = { type ModelTestItem = {
label: React.ReactNode; label: React.ReactNode;
@ -143,13 +144,47 @@ const ModelTest = ({
refreshDeps: [testModelList] refreshDeps: [testModelList]
} }
); );
const { runAsync: onTestOneModel, loading: testingOneModel } = useRequest2(
async (model: string) => {
const start = Date.now();
setTestModelList((prev) =>
prev.map((item) =>
item.model === model ? { ...item, status: 'running', message: '' } : item
)
);
try {
await getTestModel({ model, channelId });
const duration = Date.now() - start;
setTestModelList((prev) =>
prev.map((item) =>
item.model === model ? { ...item, status: 'success', duration: duration / 1000 } : item
)
);
} catch (error) {
setTestModelList((prev) =>
prev.map((item) =>
item.model === model ? { ...item, status: 'error', message: getErrText(error) } : item
)
);
}
},
{
manual: true
}
);
const isTestLoading = testingOneModel || isTesting;
return ( return (
<MyModal <MyModal
iconSrc={'core/chat/sendLight'} iconSrc={'core/chat/sendLight'}
isLoading={loadingModels} isLoading={loadingModels}
title={t('account_model:model_test')} title={t('account_model:model_test')}
w={'600px'} w={'100%'}
maxW={['90vw', '1090px']}
isOpen isOpen
> >
<ModalBody> <ModalBody>
@ -157,8 +192,10 @@ const ModelTest = ({
<Table> <Table>
<Thead> <Thead>
<Tr> <Tr>
<Th>{t('account_model:model')}</Th> <Th>{t('account_model:model_name')}</Th>
<Th>{t('account:model.model_id')}</Th>
<Th>{t('account_model:channel_status')}</Th> <Th>{t('account_model:channel_status')}</Th>
<Th></Th>
</Tr> </Tr>
</Thead> </Thead>
<Tbody> <Tbody>
@ -167,6 +204,7 @@ const ModelTest = ({
return ( return (
<Tr key={item.model}> <Tr key={item.model}>
<Td>{item.label}</Td> <Td>{item.label}</Td>
<Td>{item.model}</Td>
<Td> <Td>
<Flex alignItems={'center'}> <Flex alignItems={'center'}>
<MyTag mr={1} type="borderSolid" colorSchema={data.colorSchema as any}> <MyTag mr={1} type="borderSolid" colorSchema={data.colorSchema as any}>
@ -182,6 +220,16 @@ const ModelTest = ({
)} )}
</Flex> </Flex>
</Td> </Td>
<Td>
<MyIconButton
isLoading={isTestLoading}
icon={'core/chat/sendLight'}
tip={t('account:model.test_model')}
onClick={() => {
onTestOneModel(item.model);
}}
/>
</Td>
</Tr> </Tr>
); );
})} })}
@ -193,7 +241,7 @@ const ModelTest = ({
<Button mr={4} variant={'whiteBase'} onClick={onClose}> <Button mr={4} variant={'whiteBase'} onClick={onClose}>
{t('common:common.Cancel')} {t('common:common.Cancel')}
</Button> </Button>
<Button isLoading={isTesting} variant={'primary'} onClick={onStartTest}> <Button isLoading={isTestLoading} variant={'primary'} onClick={onStartTest}>
{t('account_model:start_test', { num: testModelList.length })} {t('account_model:start_test', { num: testModelList.length })}
</Button> </Button>
</ModalFooter> </ModalFooter>

View File

@ -206,7 +206,7 @@ const ChannelLog = ({ Tab }: { Tab: React.ReactNode }) => {
list={channelList} list={channelList}
placeholder={t('account_model:select_channel')} placeholder={t('account_model:select_channel')}
value={filterProps.channelId} value={filterProps.channelId}
onchange={(val) => setFilterProps({ ...filterProps, channelId: val })} onChange={(val) => setFilterProps({ ...filterProps, channelId: val })}
/> />
</Box> </Box>
</HStack> </HStack>
@ -219,7 +219,7 @@ const ChannelLog = ({ Tab }: { Tab: React.ReactNode }) => {
list={modelList} list={modelList}
placeholder={t('account_model:select_model')} placeholder={t('account_model:select_model')}
value={filterProps.model} value={filterProps.model}
onchange={(val) => setFilterProps({ ...filterProps, model: val })} onChange={(val) => setFilterProps({ ...filterProps, model: val })}
/> />
</Box> </Box>
</HStack> </HStack>
@ -234,7 +234,7 @@ const ChannelLog = ({ Tab }: { Tab: React.ReactNode }) => {
{ label: t('common:common.failed'), value: 'error' } { label: t('common:common.failed'), value: 'error' }
]} ]}
value={filterProps.code_type} value={filterProps.code_type}
onchange={(val) => setFilterProps({ ...filterProps, code_type: val })} onChange={(val) => setFilterProps({ ...filterProps, code_type: val })}
/> />
</Box> </Box>
</HStack> </HStack>

View File

@ -326,7 +326,7 @@ const ModelTable = ({ Tab }: { Tab: React.ReactNode }) => {
w={'200px'} w={'200px'}
bg={'myGray.50'} bg={'myGray.50'}
value={provider} value={provider}
onchange={setProvider} onChange={setProvider}
list={filterProviderList} list={filterProviderList}
/> />
</HStack> </HStack>
@ -338,7 +338,7 @@ const ModelTable = ({ Tab }: { Tab: React.ReactNode }) => {
w={'150px'} w={'150px'}
bg={'myGray.50'} bg={'myGray.50'}
value={modelType} value={modelType}
onchange={setModelType} onChange={setModelType}
list={selectModelTypeList.current} list={selectModelTypeList.current}
/> />
</HStack> </HStack>
@ -436,7 +436,7 @@ const ModelTable = ({ Tab }: { Tab: React.ReactNode }) => {
<MyIconButton <MyIconButton
icon={'core/chat/sendLight'} icon={'core/chat/sendLight'}
tip={t('account:model.test_model')} tip={t('account:model.test_model')}
onClick={() => onTestModel(item.model)} onClick={() => onTestModel({ model: item.model })}
/> />
<MyIconButton <MyIconButton
icon={'common/settingLight'} icon={'common/settingLight'}
@ -597,7 +597,7 @@ const DefaultModelModal = ({
value: item.model, value: item.model,
label: item.name label: item.name
}))} }))}
onchange={(e) => { onChange={(e) => {
setDefaultData((state) => ({ setDefaultData((state) => ({
...state, ...state,
llm: llmModelList.find((item) => item.model === e) llm: llmModelList.find((item) => item.model === e)
@ -616,7 +616,7 @@ const DefaultModelModal = ({
value: item.model, value: item.model,
label: item.name label: item.name
}))} }))}
onchange={(e) => { onChange={(e) => {
setDefaultData((state) => ({ setDefaultData((state) => ({
...state, ...state,
embedding: embeddingModelList.find((item) => item.model === e) embedding: embeddingModelList.find((item) => item.model === e)
@ -635,7 +635,7 @@ const DefaultModelModal = ({
value: item.model, value: item.model,
label: item.name label: item.name
}))} }))}
onchange={(e) => { onChange={(e) => {
setDefaultData((state) => ({ setDefaultData((state) => ({
...state, ...state,
tts: ttsModelList.find((item) => item.model === e) tts: ttsModelList.find((item) => item.model === e)
@ -654,7 +654,7 @@ const DefaultModelModal = ({
value: item.model, value: item.model,
label: item.name label: item.name
}))} }))}
onchange={(e) => { onChange={(e) => {
setDefaultData((state) => ({ setDefaultData((state) => ({
...state, ...state,
stt: sttModelList.find((item) => item.model === e) stt: sttModelList.find((item) => item.model === e)
@ -673,7 +673,7 @@ const DefaultModelModal = ({
value: item.model, value: item.model,
label: item.name label: item.name
}))} }))}
onchange={(e) => { onChange={(e) => {
setDefaultData((state) => ({ setDefaultData((state) => ({
...state, ...state,
rerank: reRankModelList.find((item) => item.model === e) rerank: reRankModelList.find((item) => item.model === e)
@ -696,7 +696,7 @@ const DefaultModelModal = ({
value: item.model, value: item.model,
label: item.name label: item.name
}))} }))}
onchange={(e) => { onChange={(e) => {
setDefaultData((state) => ({ setDefaultData((state) => ({
...state, ...state,
datasetTextLLM: datasetModelList.find((item) => item.model === e) datasetTextLLM: datasetModelList.find((item) => item.model === e)
@ -718,7 +718,7 @@ const DefaultModelModal = ({
value: item.model, value: item.model,
label: item.name label: item.name
}))} }))}
onchange={(e) => { onChange={(e) => {
setDefaultData((state) => ({ setDefaultData((state) => ({
...state, ...state,
datasetImageLLM: vlmModelList.find((item) => item.model === e) datasetImageLLM: vlmModelList.find((item) => item.model === e)

View File

@ -71,7 +71,7 @@ function CreateInvitationModal({ onClose }: { onClose: () => void }) {
<MySelect <MySelect
list={expiresOptions} list={expiresOptions}
value={expires} value={expires}
onchange={(val) => setValue('expires', val)} onChange={(val) => setValue('expires', val)}
minW="120px" minW="120px"
/> />
</> </>

View File

@ -84,7 +84,7 @@ const ExtractFieldModal = ({
<MySelect<string> <MySelect<string>
list={toolValueTypeList} list={toolValueTypeList}
value={valueType} value={valueType}
onchange={(e) => { onChange={(e) => {
setValue('valueType', e as any); setValue('valueType', e as any);
}} }}
/> />

View File

@ -217,7 +217,7 @@ const RenderHttpMethodAndUrl = React.memo(function RenderHttpMethodAndUrl({
value: 'PATCH' value: 'PATCH'
} }
]} ]}
onchange={(e) => { onChange={(e) => {
onChangeNode({ onChangeNode({
nodeId, nodeId,
type: 'updateInput', type: 'updateInput',

View File

@ -396,7 +396,7 @@ const ConditionSelect = ({
w={'100%'} w={'100%'}
list={filterQuiredConditionList} list={filterQuiredConditionList}
value={condition} value={condition}
onchange={onSelect} onChange={onSelect}
placeholder={t('common:chose_condition')} placeholder={t('common:chose_condition')}
/> />
); );
@ -441,7 +441,7 @@ const ConditionValueInput = ({
{ label: 'True', value: 'true' }, { label: 'True', value: 'true' },
{ label: 'False', value: 'false' } { label: 'False', value: 'false' }
]} ]}
onchange={onChange} onChange={onChange}
value={value} value={value}
placeholder={workflowT('ifelse.Select value')} placeholder={workflowT('ifelse.Select value')}
isDisabled={ isDisabled={

View File

@ -235,7 +235,7 @@ const NodeLaf = (props: NodeProps<FlowNodeItemType>) => {
isLoading={isLoadingFunctions} isLoading={isLoadingFunctions}
list={lafFunctionSelectList} list={lafFunctionSelectList}
placeholder={t('common:core.module.laf.Select laf function')} placeholder={t('common:core.module.laf.Select laf function')}
onchange={(e) => { onChange={(e) => {
onChangeNode({ onChangeNode({
nodeId, nodeId,
type: 'updateInput', type: 'updateInput',

View File

@ -209,7 +209,7 @@ const InputTypeConfig = ({
(item) => item.value !== WorkflowIOValueTypeEnum.arrayAny (item) => item.value !== WorkflowIOValueTypeEnum.arrayAny
)} )}
value={valueType} value={valueType}
onchange={(e) => { onChange={(e) => {
setValue('valueType', e); setValue('valueType', e);
}} }}
/> />
@ -346,7 +346,7 @@ const InputTypeConfig = ({
? defaultValue ? defaultValue
: '' : ''
} }
onchange={(e) => { onChange={(e) => {
setValue('defaultValue', e); setValue('defaultValue', e);
}} }}
w={'200px'} w={'200px'}

View File

@ -144,7 +144,7 @@ const PluginOutputEditModal = ({
(item) => item.value !== WorkflowIOValueTypeEnum.arrayAny (item) => item.value !== WorkflowIOValueTypeEnum.arrayAny
)} )}
value={valueType} value={valueType}
onchange={(e) => { onChange={(e) => {
setValue('valueType', e); setValue('valueType', e);
}} }}
/> />

View File

@ -123,7 +123,7 @@ const ToolParamsEditModal = ({
<MySelect <MySelect
list={toolValueTypeList} list={toolValueTypeList}
value={valueType} value={valueType}
onchange={(e: any) => { onChange={(e: any) => {
setValue('valueType', e); setValue('valueType', e);
}} }}
/> />

View File

@ -137,7 +137,7 @@ const FieldModal = ({
(item) => item.value !== WorkflowIOValueTypeEnum.arrayAny (item) => item.value !== WorkflowIOValueTypeEnum.arrayAny
)} )}
value={valueType} value={valueType}
onchange={(e) => { onChange={(e) => {
setValue('valueType', e); setValue('valueType', e);
}} }}
/> />

View File

@ -14,7 +14,7 @@ const SelectRender = ({ item, nodeId }: RenderInputProps) => {
width={'100%'} width={'100%'}
value={item.value} value={item.value}
list={item.list || []} list={item.list || []}
onchange={(e) => { onChange={(e) => {
onChangeNode({ onChangeNode({
nodeId, nodeId,
type: 'updateInput', type: 'updateInput',

View File

@ -57,7 +57,7 @@ const SelectAiModelRender = ({ item, nodeId }: RenderInputProps) => {
value: item.model, value: item.model,
label: item.name label: item.name
}))} }))}
onchange={onChangeModel} onChange={onChangeModel}
/> />
); );
}, [item.value, modelList, onChangeModel]); }, [item.value, modelList, onChangeModel]);

View File

@ -201,7 +201,7 @@ const EditModal = ({ onClose, ...props }: RenderInputProps & { onClose: () => vo
description: t('workflow:dataset_quote_role_user_option_desc') description: t('workflow:dataset_quote_role_user_option_desc')
} }
]} ]}
onchange={(e) => { onChange={(e) => {
setValue('quoteRole', e); setValue('quoteRole', e);
}} }}
/> />

View File

@ -131,7 +131,7 @@ const FieldModal = ({
(item) => item.value !== WorkflowIOValueTypeEnum.arrayAny (item) => item.value !== WorkflowIOValueTypeEnum.arrayAny
)} )}
value={valueType} value={valueType}
onchange={(e) => { onChange={(e) => {
setValue('valueType', e); setValue('valueType', e);
}} }}
/> />

View File

@ -97,7 +97,7 @@ const EditFieldModal = ({
<MySelect <MySelect
list={toolValueTypeList} list={toolValueTypeList}
value={valueType} value={valueType}
onchange={(e: any) => { onChange={(e: any) => {
setValue('valueType', e); setValue('valueType', e);
}} }}
/> />

View File

@ -289,7 +289,7 @@ const TemplateMarketModal = ({
<MySelect<TemplateAppType> <MySelect<TemplateAppType>
h={'8'} h={'8'}
value={currentAppType} value={currentAppType}
onchange={(value) => { onChange={(value) => {
setCurrentAppType(value); setCurrentAppType(value);
}} }}
bg={'myGray.100'} bg={'myGray.100'}

View File

@ -333,7 +333,7 @@ function DataProcess() {
label: item.name, label: item.name,
value: item.model value: item.model
}))} }))}
onchange={(e) => { onChange={(e) => {
setValue('llmModel', e); setValue('llmModel', e);
}} }}
/> />
@ -349,7 +349,7 @@ function DataProcess() {
label: item.name, label: item.name,
value: item.model value: item.model
}))} }))}
onchange={(e) => { onChange={(e) => {
setValue('vlmModel', e); setValue('vlmModel', e);
}} }}
/> />

View File

@ -195,7 +195,7 @@ const Info = ({ datasetId }: { datasetId: string }) => {
label: item.name, label: item.name,
value: item.model value: item.model
}))} }))}
onchange={(e) => { onChange={(e) => {
const vectorModel = embeddingModelList.find((item) => item.model === e); const vectorModel = embeddingModelList.find((item) => item.model === e);
if (!vectorModel) return; if (!vectorModel) return;
return onOpenConfirmRebuild(async () => { return onOpenConfirmRebuild(async () => {
@ -220,7 +220,7 @@ const Info = ({ datasetId }: { datasetId: string }) => {
value: item.model value: item.model
}))} }))}
fontSize={'mini'} fontSize={'mini'}
onchange={(e) => { onChange={(e) => {
const agentModel = datasetModelList.find((item) => item.model === e); const agentModel = datasetModelList.find((item) => item.model === e);
if (!agentModel) return; if (!agentModel) return;
setValue('agentModel', agentModel); setValue('agentModel', agentModel);
@ -244,7 +244,7 @@ const Info = ({ datasetId }: { datasetId: string }) => {
value: item.model value: item.model
}))} }))}
fontSize={'mini'} fontSize={'mini'}
onchange={(e) => { onChange={(e) => {
const vlmModel = vllmModelList.find((item) => item.model === e); const vlmModel = vllmModelList.find((item) => item.model === e);
if (!vlmModel) return; if (!vlmModel) return;
setValue('vlmModel', vlmModel); setValue('vlmModel', vlmModel);

View File

@ -186,7 +186,7 @@ const Test = ({ datasetId }: { datasetId: string }) => {
// } // }
]} ]}
value={inputType} value={inputType}
onchange={(e) => setInputType(e)} onChange={(e) => setInputType(e)}
/> />
<Button <Button

View File

@ -205,7 +205,7 @@ const CreateModal = ({
label: item.name, label: item.name,
value: item.model value: item.model
}))} }))}
onchange={(e) => { onChange={(e) => {
setValue('vectorModel' as const, e); setValue('vectorModel' as const, e);
}} }}
/> />
@ -237,7 +237,7 @@ const CreateModal = ({
label: item.name, label: item.name,
value: item.model value: item.model
}))} }))}
onchange={(e) => { onChange={(e) => {
setValue('agentModel', e); setValue('agentModel', e);
}} }}
/> />
@ -269,7 +269,7 @@ const CreateModal = ({
label: item.name, label: item.name,
value: item.model value: item.model
}))} }))}
onchange={(e) => { onChange={(e) => {
setValue('vlmModel', e); setValue('vlmModel', e);
}} }}
/> />

View File

@ -1,4 +1,4 @@
import React, { useCallback, useMemo } from 'react'; import React, { useCallback, useEffect, useMemo } from 'react';
import { import {
Box, Box,
Flex, Flex,
@ -44,6 +44,7 @@ import { useRouter } from 'next/router';
import TeamSelector from '@/pageComponents/account/TeamSelector'; import TeamSelector from '@/pageComponents/account/TeamSelector';
import { getWorkorderURL } from '@/web/common/workorder/api'; import { getWorkorderURL } from '@/web/common/workorder/api';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { useMount } from 'ahooks';
const StandDetailModal = dynamic( const StandDetailModal = dynamic(
() => import('@/pageComponents/account/info/standardDetailModal'), () => import('@/pageComponents/account/info/standardDetailModal'),
@ -64,7 +65,9 @@ const Info = () => {
const standardPlan = teamPlanStatus?.standardConstants; const standardPlan = teamPlanStatus?.standardConstants;
const { isOpen: isOpenContact, onClose: onCloseContact, onOpen: onOpenContact } = useDisclosure(); const { isOpen: isOpenContact, onClose: onCloseContact, onOpen: onOpenContact } = useDisclosure();
useQuery(['init'], initUserInfo); useMount(() => {
initUserInfo();
});
return ( return (
<AccountContainer> <AccountContainer>

View File

@ -130,7 +130,7 @@ const UsageTable = () => {
{ label: t('account_usage:every_month'), value: 'month' } { label: t('account_usage:every_month'), value: 'month' }
]} ]}
value={unit} value={unit}
onchange={setUnit} onChange={setUnit}
/> />
)} */} )} */}
</Flex> </Flex>

View File

@ -132,14 +132,12 @@ const testTTSModel = async (model: TTSModelType, headers: Record<string, string>
model.requestUrl model.requestUrl
? { ? {
path: model.requestUrl, path: model.requestUrl,
headers: model.requestAuth headers: {
? { ...(model.requestAuth ? { Authorization: `Bearer ${model.requestAuth}` } : {}),
Authorization: `Bearer ${model.requestAuth}`, ...headers
...headers }
}
: headers
} }
: {} : { headers }
); );
}; };

View File

@ -27,6 +27,13 @@ async function handler(
const dbModel = await MongoSystemModel.findOne({ model }).lean(); const dbModel = await MongoSystemModel.findOne({ model }).lean();
const modelData = findModelFromAlldata(model); const modelData = findModelFromAlldata(model);
if (metadata) {
delete metadata.isActive;
delete metadata.isDefault;
delete metadata.isDefaultDatasetTextModel;
delete metadata.isDefaultDatasetImageModel;
}
const metadataConcat: Record<string, any> = { const metadataConcat: Record<string, any> = {
...modelData, // system config ...modelData, // system config
...dbModel?.metadata, // db config ...dbModel?.metadata, // db config

View File

@ -19,6 +19,7 @@ import { concatPer } from '@fastgpt/service/support/permission/controller';
import { getGroupsByTmbId } from '@fastgpt/service/support/permission/memberGroup/controllers'; import { getGroupsByTmbId } from '@fastgpt/service/support/permission/memberGroup/controllers';
import { getOrgIdSetWithParentByTmbId } from '@fastgpt/service/support/permission/org/controllers'; import { getOrgIdSetWithParentByTmbId } from '@fastgpt/service/support/permission/org/controllers';
import { addSourceMember } from '@fastgpt/service/support/user/utils'; import { addSourceMember } from '@fastgpt/service/support/user/utils';
import { getNanoid } from '../../../../../../../packages/global/common/string/tools';
export type ListAppBody = { export type ListAppBody = {
parentId?: ParentIdType; parentId?: ParentIdType;

View File

@ -65,14 +65,6 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords); const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
const totalRecordsCount = useContextSelector(ChatRecordContext, (v) => v.totalRecordsCount); const totalRecordsCount = useContextSelector(ChatRecordContext, (v) => v.totalRecordsCount);
const [sidebarFolded, setSidebarFolded] = useState(false);
useEffect(() => {
if (quoteData) {
setSidebarFolded(true);
}
}, [quoteData]);
// Load chat init data // Load chat init data
const { loading } = useRequest2( const { loading } = useRequest2(
async () => { async () => {
@ -156,9 +148,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
); );
return isPc || !appId ? ( return isPc || !appId ? (
<SideBar isFolded={sidebarFolded} onFoldChange={setSidebarFolded}> <SideBar externalTrigger={!!quoteData}>{Children}</SideBar>
{Children}
</SideBar>
) : ( ) : (
<Drawer <Drawer
isOpen={isOpenSlider} isOpen={isOpenSlider}
@ -171,7 +161,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
<DrawerContent maxWidth={'75vw'}>{Children}</DrawerContent> <DrawerContent maxWidth={'75vw'}>{Children}</DrawerContent>
</Drawer> </Drawer>
); );
}, [t, isPc, appId, isOpenSlider, onCloseSlider, sidebarFolded]); }, [t, isPc, appId, isOpenSlider, onCloseSlider, quoteData]);
return ( return (
<Flex h={'100%'}> <Flex h={'100%'}>

View File

@ -90,14 +90,6 @@ const OutLink = (props: Props) => {
const totalRecordsCount = useContextSelector(ChatRecordContext, (v) => v.totalRecordsCount); const totalRecordsCount = useContextSelector(ChatRecordContext, (v) => v.totalRecordsCount);
const isChatRecordsLoaded = useContextSelector(ChatRecordContext, (v) => v.isChatRecordsLoaded); const isChatRecordsLoaded = useContextSelector(ChatRecordContext, (v) => v.isChatRecordsLoaded);
const [sidebarFolded, setSidebarFolded] = useState(false);
useEffect(() => {
if (quoteData) {
setSidebarFolded(true);
}
}, [quoteData]);
const initSign = useRef(false); const initSign = useRef(false);
const { data, loading } = useRequest2( const { data, loading } = useRequest2(
async () => { async () => {
@ -229,9 +221,7 @@ const OutLink = (props: Props) => {
if (showHistory !== '1') return null; if (showHistory !== '1') return null;
return isPc ? ( return isPc ? (
<SideBar isFolded={sidebarFolded} onFoldChange={setSidebarFolded}> <SideBar externalTrigger={!!quoteData}>{Children}</SideBar>
{Children}
</SideBar>
) : ( ) : (
<Drawer <Drawer
isOpen={isOpenSlider} isOpen={isOpenSlider}
@ -246,7 +236,7 @@ const OutLink = (props: Props) => {
</DrawerContent> </DrawerContent>
</Drawer> </Drawer>
); );
}, [isOpenSlider, isPc, onCloseSlider, showHistory, t, sidebarFolded]); }, [isOpenSlider, isPc, onCloseSlider, quoteData, showHistory, t]);
return ( return (
<> <>

View File

@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react'; import React, { useCallback, useEffect, useMemo } from 'react';
import NextHead from '@/components/common/NextHead'; import NextHead from '@/components/common/NextHead';
import { getTeamChatInfo } from '@/web/core/chat/api'; import { getTeamChatInfo } from '@/web/core/chat/api';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
@ -70,14 +70,6 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords); const chatRecords = useContextSelector(ChatRecordContext, (v) => v.chatRecords);
const totalRecordsCount = useContextSelector(ChatRecordContext, (v) => v.totalRecordsCount); const totalRecordsCount = useContextSelector(ChatRecordContext, (v) => v.totalRecordsCount);
const [sidebarFolded, setSidebarFolded] = useState(false);
useEffect(() => {
if (quoteData) {
setSidebarFolded(true);
}
}, [quoteData]);
// get chat app info // get chat app info
const { loading } = useRequest2( const { loading } = useRequest2(
async () => { async () => {
@ -174,9 +166,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
); );
return isPc || !appId ? ( return isPc || !appId ? (
<SideBar isFolded={sidebarFolded} onFoldChange={setSidebarFolded}> <SideBar externalTrigger={!!quoteData}>{Children}</SideBar>
{Children}
</SideBar>
) : ( ) : (
<Drawer <Drawer
isOpen={isOpenSlider} isOpen={isOpenSlider}
@ -189,7 +179,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
<DrawerContent maxWidth={'75vw'}>{Children}</DrawerContent> <DrawerContent maxWidth={'75vw'}>{Children}</DrawerContent>
</Drawer> </Drawer>
); );
}, [appId, isOpenSlider, isPc, onCloseSlider, t, sidebarFolded]); }, [appId, isOpenSlider, isPc, onCloseSlider, quoteData, t]);
return ( return (
<Flex h={'100%'}> <Flex h={'100%'}>

View File

@ -7,6 +7,13 @@
"@test/*": ["../../test/*"] "@test/*": ["../../test/*"]
} }
}, },
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.d.ts", "../../packages/**/*.d.ts"], "include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
"**/*.d.ts",
"../../packages/**/*.d.ts",
"../../test/list.test.ts"
],
"exclude": ["**/*.test.ts"] "exclude": ["**/*.test.ts"]
} }

View File

@ -1,9 +1,12 @@
import { MongoApp } from '@fastgpt/service/core/app/schema'; import { MongoApp } from '@fastgpt/service/core/app/schema';
import { MongoAppVersion } from '@fastgpt/service/core/app/version/schema'; import { MongoAppVersion } from '@fastgpt/service/core/app/version/schema';
import { getRootUser } from '@test/datas/users'; import { getRootUser } from './datas/users';
import { Call } from '@test/utils/request'; import { Call } from './utils/request';
import { describe, expect, it } from 'vitest'; import { describe, expect, it } from 'vitest';
import handler, { type versionListBody, type versionListResponse } from './list'; import handler, {
type versionListBody,
type versionListResponse
} from '../projects/app/src/pages/api/core/app/version/list';
describe('app version list test', () => { describe('app version list test', () => {
it('should return app version list', async () => { it('should return app version list', async () => {