4.8.10 test (#2539)

* fix: i18n

* fix: null value

* fix: workflow refresh variables

* perf: copy data

* doc

* perf: run app code

* perf: variable store

* update doc

* perf: pay ui

* fix: log header ui

* fix: log header ui
This commit is contained in:
Archer
2024-08-27 19:48:42 +08:00
committed by GitHub
parent 454a479fd8
commit c9bb39d802
20 changed files with 87 additions and 118 deletions

View File

@@ -53,19 +53,27 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4810' \
4. 新增 - 工作流撤销和重做
5. 新增 - 工作流本次编辑记录,取代自动保存
6. 新增 - 工作流版本支持重命名
7. 商业版新增 - 飞书机器人接入
8. 商业版新增 - 公众号接入接入
9. 商业版新增 - 自助开票申请
10. 商业版新增 - SSO 定制
11. 优化 - 知识库集合禁用,目录禁用会递归修改其下所有 children 的禁用状态。
12. 优化 - 节点选择,避免切换 tab 时候path 加载报错
13. 优化 - 最新 React Markdown 组件,支持 Base64 图片
14. 优化 - 知识库列表 UI
15. 优化 - 支持无网络配置情况下运行
16. 修复 - Prompt 模式调用工具stream=false 模式下,会携带 0: 开头标记
17. 修复 - 对话日志鉴权问题:仅为 APP 管理员的用户,无法查看对话日志详情
18. 修复 - 选择 Milvus 部署时,无法导出知识库。
19. 修复 - 创建 APP 副本,无法复制系统配置
20. 修复 - 图片识别模式下,自动解析图片链接正则不够严谨问题
21. 修复 - 内容提取的数据类型与输出数据类型未一致
22. 修复 - 工作流运行时间统计错误
7. 新增 - 应用调用迁移成单独节点,同时可以传递全局变量和用户的文件。
8. 商业版新增 - 飞书机器人接入
9. 商业版新增 - 公众号接入接入
10. 商业版新增 - 自助开票申请
11. 商业版新增 - SSO 定制
12. 优化 - SSE 响应优化
13. 优化 - 无 SSL 证书情况下,优化复制
14. 优化 - 单选框打开后自动滚动到选中的位置
15. 优化 - 知识库集合禁用,目录禁用会递归修改其下所有 children 的禁用状态
16. 优化 - 节点选择,避免切换 tab 时候path 加载报错
17. 优化 - 最新 React Markdown 组件,支持 Base64 图片
18. 优化 - 知识库列表 UI。
19. 优化 - 支持无网络配置情况下运行
20. 优化 - 部分全局变量,增加数据类型约束
21. 修复 - 全局变量 key 可能重复
22. 修复 - Prompt 模式调用工具stream=false 模式下,会携带 0: 开头标记
23. 修复 - 对话日志鉴权问题:仅为 APP 管理员的用户,无法查看对话日志详情。
24. 修复 - 选择 Milvus 部署时,无法导出知识库。
25. 修复 - 创建 APP 副本,无法复制系统配置。
26. 修复 - 图片识别模式下,自动解析图片链接正则不够严谨问题。
27. 修复 - 内容提取的数据类型与输出数据类型未一致。
28. 修复 - 工作流运行时间统计错误。
29. 修复 - stream 模式下,工具调用有可能出现 undefined
30. 修复 - 全局变量在 API 中无法持久化。

View File

@@ -45,8 +45,7 @@ const systemNodes: FlowNodeTemplateType[] = [
LafModule,
IfElseNode,
VariableUpdateNode,
CodeNode,
RunAppModule
CodeNode
];
/* app flow module templates */
export const appSystemModuleTemplates: FlowNodeTemplateType[] = [
@@ -72,5 +71,6 @@ export const moduleTemplatesFlat: FlowNodeTemplateType[] = [
),
EmptyNode,
RunPluginModule,
RunAppPluginModule
RunAppPluginModule,
RunAppModule
];

View File

@@ -72,15 +72,3 @@ export const standardSubLevelMap = {
weight: 5
}
};
export enum PackageChangeStatusEnum {
buy = 'buy',
renewal = 'renewal',
upgrade = 'upgrade'
}
export const packagePayTextMap = {
[PackageChangeStatusEnum.buy]: i18nT('common:pay.package_tip.buy'),
[PackageChangeStatusEnum.renewal]: i18nT('common:pay.package_tip.renewal'),
[PackageChangeStatusEnum.upgrade]: i18nT('common:pay.package_tip.upgrade')
};

View File

@@ -5,7 +5,6 @@ import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/c
import { delFileByFileIdList, getGFSCollection } from '../../common/file/gridfs/controller';
import { BucketNameEnum } from '@fastgpt/global/common/file/constants';
import { MongoChat } from './chatSchema';
import { ChatSchema as ChatType } from '@fastgpt/global/core/chat/type.d';
export async function getChatItems({
appId,
@@ -37,24 +36,6 @@ export async function getChatItems({
return { histories };
}
export async function getChat({
appId,
chatId,
field
}: {
appId: string;
chatId?: string;
field: string;
}): Promise<{ chat: ChatType | null }> {
if (!chatId) {
return { chat: null };
}
const chat = await MongoChat.findOne({ appId, chatId }, field).lean();
return { chat };
}
/* Temporary adaptation for old conversation records */
export const adaptStringValue = (value: any): ChatItemValueItemType[] => {
if (typeof value === 'string') {

View File

@@ -357,7 +357,7 @@ async function streamResponse({
const responseChoice = part.choices?.[0]?.delta;
// console.log(responseChoice, '---===');
if (responseChoice.content) {
if (responseChoice?.content) {
const content = responseChoice?.content || '';
textAnswer += content;

View File

@@ -44,7 +44,7 @@ const MyMenu = ({
iconSize = '1rem',
Button,
menuList,
iconRadius = 'sm',
iconRadius,
placement = 'bottom-start'
}: Props) => {
const typeMapStyle: Record<MenuItemType, MenuItemProps> = {

View File

@@ -126,6 +126,7 @@
"Confirm to leave the page": "Confirm to leave the page?",
"Copy": "Copy",
"Copy Successful": "Copy Successful",
"Copy_failed": "Copy failed, please copy manually",
"Create Failed": "Create Failed",
"Create New": "Create New",
"Create Success": "Create Success",
@@ -1430,7 +1431,7 @@
"Quote Content Tip": "You can customize the structure of the quote content to better adapt to different scenarios. You can use some variables for template configuration:\n{{q}} - search content, {{a}} - expected content, {{source}} - source, {{sourceId}} - source file name, {{index}} - the nth quote, they are all optional, here are the default values:\n{{default}}",
"Quote Prompt Tip": "You can use {{quote}} to insert the quote content template, and use {{question}} to insert the question. Here are the default values:\n{{default}}"
},
"textarea_variable_picker_tip": "Input / to select variables",
"textarea_variable_picker_tip": "Input '/' to select variables",
"tool_field": "Tool field parameter configuration",
"undefined_var": "An undefined variable is referenced. Is it automatically added?",
"unit": {

View File

@@ -128,6 +128,7 @@
"Confirm to leave the page": "确认离开该页面?",
"Copy": "复制",
"Copy Successful": "复制成功",
"Copy_failed": "复制失败,请手动复制",
"Create Failed": "创建异常",
"Create New": "新建",
"Create Success": "创建成功",
@@ -637,7 +638,8 @@
"success": "开始同步"
}
},
"training": {}
"training": {
}
},
"data": {
"Auxiliary Data": "辅助数据",
@@ -1429,7 +1431,7 @@
"Quote Content Tip": "可以自定义引用内容的结构,以更好的适配不同场景。可以使用一些变量来进行模板配置:\n{{q}} - 检索内容,{{a}} - 预期内容,{{source}} - 来源,{{sourceId}} - 来源文件名,{{index}} - 第 n 个引用,他们都是可选的,下面是默认值:\n{{default}}",
"Quote Prompt Tip": "可以用 {{quote}} 来插入引用内容模板,使用 {{question}} 来插入问题。下面是默认值:\n{{default}}"
},
"textarea_variable_picker_tip": "输入 / 可选择变量",
"textarea_variable_picker_tip": "输入\"/\"可选择变量",
"tool_field": "工具字段参数配置",
"undefined_var": "引用了未定义的变量,是否自动添加?",
"unit": {

View File

@@ -58,6 +58,7 @@ const ApiKeyTable = ({ tips, appId }: { tips: string; appId?: string }) => {
const [baseUrl, setBaseUrl] = useState('https://fastgpt.in/api');
const [editData, setEditData] = useState<EditProps>();
const [apiKey, setApiKey] = useState('');
const { ConfirmModal, openConfirm } = useConfirm({
type: 'delete',
content: t('workflow:delete_api')
@@ -271,6 +272,7 @@ const ApiKeyTable = ({ tips, appId }: { tips: string; appId?: string }) => {
wordBreak={'break-all'}
cursor={'pointer'}
borderRadius={'md'}
userSelect={'all'}
onClick={() => copyData(apiKey)}
>
<Box flex={1}>{apiKey}</Box>

View File

@@ -75,7 +75,7 @@ const QRCodePayModal = ({
<MyModal isOpen title={t('common:user.Pay')} iconSrc="/imgs/modal/pay.svg">
<ModalBody textAlign={'center'} py={6} whiteSpace={'pre'}>
{tip && (
<Box textAlign={'left'} whiteSpace={'normal'} mb={3}>
<Box fontSize={'sm'} whiteSpace={'normal'} mb={3}>
{tip}
</Box>
)}

View File

@@ -43,7 +43,7 @@ const StandardPlanContentList = ({
}, [subPlans?.standard, level, mode]);
return planContent ? (
<Grid gap={4} fontSize={'sm'}>
<Grid gap={4} fontSize={'sm'} fontWeight={500}>
<Flex alignItems={'center'}>
<MyIcon name={'price/right'} w={'16px'} mr={3} />
<Box color={'myGray.600'}>

View File

@@ -1,5 +1,4 @@
import React from 'react';
import { useTranslation } from 'next-i18next';
import ApiKeyTable from '@/components/support/apikey/Table';
import { useI18n } from '@/web/context/I18n';
import { Box } from '@chakra-ui/react';

View File

@@ -16,7 +16,6 @@ import {
} from '@chakra-ui/react';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { useTranslation } from 'next-i18next';
import { useQuery } from '@tanstack/react-query';
import { useLoading } from '@fastgpt/web/hooks/useLoading';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { getTeamPlans } from '@/web/support/user/team/api';
@@ -25,7 +24,6 @@ import {
standardSubLevelMap,
SubTypeEnum
} from '@fastgpt/global/support/wallet/sub/constants';
import { TeamSubSchema } from '@fastgpt/global/support/wallet/sub/type';
import { formatTime2YMDHM } from '@fastgpt/global/common/string/time';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
@@ -71,7 +69,7 @@ const StandDetailModal = ({ onClose }: { onClose: () => void }) => {
isCentered
>
<ModalCloseButton onClick={onClose} />
<ModalBody px={'3.25rem'} py={'2rem'}>
<ModalBody px={[4, 8]} py={[2, 6]}>
<TableContainer mt={2} position={'relative'} minH={'300px'}>
<Table>
<Thead>
@@ -100,7 +98,6 @@ const StandDetailModal = ({ onClose }: { onClose: () => void }) => {
? subPlans?.standard?.[currentSubLevel]
: undefined;
const datasetSize = standardPlan?.maxDatasetSize || currentExtraDatasetSize;
const now = new Date();
return (
<Tr key={_id} fontWeight={500} fontSize={'mini'} color={'myGray.900'}>

View File

@@ -20,7 +20,7 @@ import {
textAdaptGptResponse
} from '@fastgpt/global/core/workflow/runtime/utils';
import { GPTMessages2Chats, chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
import { getChat, getChatItems } from '@fastgpt/service/core/chat/controller';
import { getChatItems } from '@fastgpt/service/core/chat/controller';
import { saveChat } from '@fastgpt/service/core/chat/saveChat';
import { responseWrite } from '@fastgpt/service/common/response';
import { pushChatUsage } from '@/service/support/wallet/usage/push';
@@ -216,11 +216,10 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
}
return latestHumanChat;
})();
const { text, files } = chatValue2RuntimePrompt(userQuestion.value);
// Get and concat history;
const limit = getMaxHistoryLimitFromNodes(app.modules);
const [{ histories }, { nodes, edges, chatConfig }, { chat }] = await Promise.all([
const [{ histories }, { nodes, edges, chatConfig }, chatDetail] = await Promise.all([
getChatItems({
appId: app._id,
chatId,
@@ -228,32 +227,27 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
field: `dataId obj value nodeOutputs`
}),
getAppLatestVersion(app._id, app),
getChat({
appId: app._id,
chatId,
field: 'source variableList variables'
})
MongoChat.findOne({ appId: app._id, chatId }, 'source variableList variables')
]);
// get chat histories
// Get chat histories
const newHistories = concatHistories(histories, chatMessages);
// get global variables
if (chat && chat.variables) {
// Get store variables(Api variable precedence)
if (chatDetail?.variables) {
variables = {
...chat.variables,
...chatDetail.variables,
...variables
};
}
// Get runtimeNodes
let runtimeNodes = storeNodes2RuntimeNodes(nodes, getWorkflowEntryNodeIds(nodes, newHistories));
if (isPlugin) {
// Rewrite plugin run params variables
variables = removePluginInputVariables(variables, runtimeNodes);
runtimeNodes = updatePluginInputByVariables(runtimeNodes, variables);
}
runtimeNodes = rewriteNodeOutputByHistories(newHistories, runtimeNodes);
const workflowResponseWrite = getWorkflowResponseWrite({

View File

@@ -1,10 +1,8 @@
import React from 'react';
import { Flex, Box, useTheme } from '@chakra-ui/react';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useTranslation } from 'next-i18next';
import { HUMAN_ICON } from '@fastgpt/global/common/system/constants';
import { getInitChatInfo } from '@/web/core/chat/api';
import MyTag from '@fastgpt/web/components/common/Tag/index';
import MyBox from '@fastgpt/web/components/common/MyBox';
import { getNanoid } from '@fastgpt/global/common/string/tools';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
@@ -17,6 +15,8 @@ import CloseIcon from '@fastgpt/web/components/common/Icon/close';
import ChatBox from '@/components/core/chat/ChatContainer/ChatBox';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
import { useQuery } from '@tanstack/react-query';
import { PcHeader } from '@/pages/chat/components/ChatHeader';
const PluginRunBox = dynamic(() => import('@/components/core/chat/ChatContainer/PluginRunBox'));
const DetailLogsModal = ({
@@ -124,26 +124,7 @@ const DetailLogsModal = ({
>
{isPc ? (
<>
<Box mr={3} color={'myGray.1000'}>
{title}
</Box>
{chatRecords.length > 0 && (
<>
<MyTag colorSchema="blue">
<MyIcon name={'history'} w={'14px'} />
<Box ml={1}>
{t('common:core.chat.History Amount', { amount: chatRecords.length })}
</Box>
</MyTag>
{!!chatModels && (
<MyTag ml={2} colorSchema={'green'}>
<MyIcon name={'core/chat/chatModelTag'} w={'14px'} />
<Box ml={1}>{chatModels.join(',')}</Box>
</MyTag>
)}
</>
)}
<PcHeader title={title || ''} history={chatRecords} chatModels={chatModels} />
<Box flex={1} />
</>
) : (

View File

@@ -408,7 +408,6 @@ const RenderList = React.memo(function RenderList({
templates.forEach((item) => {
const index = copy.findIndex((template) => template.type === item.templateType);
if (index === -1) return;
if (item.flowNodeType === FlowNodeTypeEnum.runApp) return;
copy[index].list.push(item);
});
return copy.filter((item) => item.list.length > 0);

View File

@@ -52,7 +52,7 @@ const NodeStart = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
label: item.label
};
});
}, [nodeList, t]);
}, [nodeList, appDetail.chatConfig, t]);
return (
<NodeCard

View File

@@ -248,7 +248,7 @@ const MobileHeader = ({
);
};
const PcHeader = ({
export const PcHeader = ({
title,
chatModels,
history
@@ -260,7 +260,7 @@ const PcHeader = ({
const { t } = useTranslation();
return (
<>
<Box mr={3} maxW={'160px'} className="textEllipsis" color={'myGray.1000'}>
<Box mr={3} maxW={'200px'} className="textEllipsis" color={'myGray.1000'}>
{title}
</Box>
<MyTag>

View File

@@ -2,12 +2,7 @@ import React, { useMemo, useState } from 'react';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { Box, Button, Flex, Grid, HStack } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
import {
StandardSubLevelEnum,
SubModeEnum,
PackageChangeStatusEnum,
packagePayTextMap
} from '@fastgpt/global/support/wallet/sub/constants';
import { StandardSubLevelEnum, SubModeEnum } from '@fastgpt/global/support/wallet/sub/constants';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { standardSubLevelMap } from '@fastgpt/global/support/wallet/sub/constants';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
@@ -16,8 +11,12 @@ import QRCodePayModal, { type QRPayProps } from '@/components/support/wallet/QRC
import { getWxPayQRCode } from '@/web/support/wallet/bill/api';
import { BillTypeEnum } from '@fastgpt/global/support/wallet/bill/constants';
import StandardPlanContentList from '@/components/support/wallet/StandardPlanContentList';
import { useRouter } from 'next/router';
import { useToast } from '@fastgpt/web/hooks/useToast';
export enum PackageChangeStatusEnum {
buy = 'buy',
renewal = 'renewal',
upgrade = 'upgrade'
}
const Standard = ({
standardPlan: myStandardPlan,
@@ -27,8 +26,13 @@ const Standard = ({
refetchTeamSubPlan: () => void;
}) => {
const { t } = useTranslation();
const router = useRouter();
const { toast } = useToast();
const packagePayTextMap = {
[PackageChangeStatusEnum.buy]: t('common:pay.package_tip.buy'),
[PackageChangeStatusEnum.renewal]: t('common:pay.package_tip.renewal'),
[PackageChangeStatusEnum.upgrade]: t('common:pay.package_tip.upgrade')
};
const [packageChange, setPackageChange] = useState<PackageChangeStatusEnum>();
const { subPlans, feConfigs } = useSystemStore();
const [selectSubMode, setSelectSubMode] = useState<`${SubModeEnum}`>(SubModeEnum.month);
@@ -177,7 +181,8 @@ const Standard = ({
boxShadow={'0'}
cursor={'default'}
w={'100%'}
variant={isCurrentPlan ? 'whiteBase' : 'solid'}
isDisabled
variant={'whiteBase'}
>
{t('common:free')}
</Button>
@@ -265,7 +270,7 @@ const Standard = ({
</Grid>
{!!qrPayData && packageChange && (
<QRCodePayModal tip={t(packagePayTextMap[packageChange])} {...qrPayData} />
<QRCodePayModal tip={packagePayTextMap[packageChange]} {...qrPayData} />
)}
</Flex>
<HStack mt={8} color={'blue.700'} ml={8}>

View File

@@ -2,6 +2,7 @@ import { useTranslation } from 'next-i18next';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { useCallback } from 'react';
import { hasHttps } from '@fastgpt/web/common/system/utils';
import { isProduction } from '@fastgpt/service/common/system/constants';
/**
* copy text data
@@ -17,20 +18,31 @@ export const useCopyData = () => {
duration = 1000
) => {
try {
if (hasHttps() && navigator.clipboard) {
if (hasHttps() && !isProduction && navigator.clipboard) {
await navigator.clipboard.writeText(data);
} else {
throw new Error('');
}
} catch (error) {
console.log(error);
// console.log(error);
const textarea = document.createElement('textarea');
textarea.value = data;
textarea.style.position = 'absolute';
textarea.style.opacity = '0';
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body?.removeChild(textarea);
const res = document.execCommand('copy');
document.body.removeChild(textarea);
if (!res) {
return toast({
title: t('common:common.Copy_failed'),
status: 'error',
duration
});
}
}
if (title) {