perf: ery extension and fix filter same embedding result (#3833)

* perf: ery extension and fix filter same embedding result

* fix: extract node too long

* perf: ui

* perf: not chatId will auto save

* fix: laf

* fix: member load

* feat: add completions unstream error response

* feat: add completions unstream error response

* updat emodel provider
This commit is contained in:
Archer
2025-02-19 22:16:43 +08:00
committed by GitHub
parent 8604cbd021
commit 6762723b10
26 changed files with 317 additions and 257 deletions

View File

@@ -78,7 +78,7 @@ const Layout = ({ children }: { children: JSX.Element }) => {
isUpdateNotification &&
feConfigs?.bind_notification_method &&
feConfigs?.bind_notification_method.length > 0 &&
!userInfo?.team.notificationAccount &&
!userInfo?.contact &&
!!userInfo?.team.permission.isOwner;
useMount(() => {

View File

@@ -250,7 +250,7 @@ export const WholeResponseContent = ({
value={`${activeModule.queryExtensionResult.inputTokens}/${activeModule.queryExtensionResult.outputTokens}`}
/>
<Row
label={t('common:support.wallet.usage.Extension result')}
label={t('chat:query_extension_result')}
value={activeModule.queryExtensionResult.query}
/>
</>
@@ -259,10 +259,7 @@ export const WholeResponseContent = ({
label={t('common:core.chat.response.Extension model')}
value={activeModule?.extensionModel}
/>
<Row
label={t('common:support.wallet.usage.Extension result')}
value={`${activeModule?.extensionResult}`}
/>
<Row label={t('chat:query_extension_result')} value={`${activeModule?.extensionResult}`} />
{activeModule.quoteList && activeModule.quoteList.length > 0 && (
<Row
label={t('common:core.chat.response.module quoteList')}

View File

@@ -130,7 +130,7 @@ const QuoteItem = ({
<Box>
{t(SearchScoreTypeMap[score.primaryScore.type]?.label as any)}
{SearchScoreTypeMap[score.primaryScore.type]?.showScore
? ` ${score.primaryScore.value.toFixed(4)}`
? ` ${score.primaryScore.value?.toFixed(4)}`
: ''}
</Box>
</Flex>

View File

@@ -43,10 +43,10 @@ function MemberItemCard({
{isChecked !== undefined && <Checkbox isChecked={isChecked} pointerEvents="none" />}
<Avatar src={avatar} w="1.5rem" borderRadius={'50%'} />
<VStack w="full" gap={0}>
<Box w="full">{name}</Box>
<Box w="full">{orgs && orgs.length > 0 && <OrgTags orgs={orgs} />}</Box>
</VStack>
<Box w="full">
<Box fontSize={'sm'}>{name}</Box>
<Box lineHeight={1}>{orgs && orgs.length > 0 && <OrgTags orgs={orgs} />}</Box>
</Box>
{permission && <PermissionTags permission={permission} />}
{onDelete !== undefined && (
<MyIcon

View File

@@ -126,7 +126,7 @@ function MemberModal({
const [selectedMemberIdList, setSelectedMembers] = useState<string[]>([]);
const filterMembers = useMemo(() => {
if (searchText) {
return searchedData?.members;
return searchedData?.members || [];
}
if (!searchText && filterClass !== 'member' && filterClass !== 'org') return [];
if (currentOrg && filterClass === 'org') {
@@ -320,122 +320,124 @@ function MemberModal({
</Box>
)}
<ScrollData
flexDirection={'column'}
gap={1}
userSelect={'none'}
height={'fit-content'}
>
{filterOrgs?.map((org) => {
const onChange = () => {
setSelectedOrgIdList((state) => {
if (state.includes(org._id)) {
return state.filter((v) => v !== org._id);
}
return [...state, org._id];
});
};
const collaborator = collaboratorList?.find((v) => v.orgId === org._id);
return (
<HStack
justifyContent="space-between"
key={org._id}
py="2"
px="3"
borderRadius="sm"
alignItems="center"
_hover={HoverBoxStyle}
onClick={onChange}
>
<Checkbox
isChecked={selectedOrgIdList.includes(org._id)}
pointerEvents="none"
/>
<MyAvatar src={org.avatar} w="1.5rem" borderRadius={'50%'} />
<HStack ml="2" w="full" gap="5px">
<Text>{org.name}</Text>
{(filterClass === 'org' || filterClass === 'member') && (
<ScrollData
flexDirection={'column'}
gap={1}
userSelect={'none'}
height={'fit-content'}
>
{filterOrgs?.map((org) => {
const onChange = () => {
setSelectedOrgIdList((state) => {
if (state.includes(org._id)) {
return state.filter((v) => v !== org._id);
}
return [...state, org._id];
});
};
const collaborator = collaboratorList?.find((v) => v.orgId === org._id);
return (
<HStack
justifyContent="space-between"
key={org._id}
py="2"
px="3"
borderRadius="sm"
alignItems="center"
_hover={HoverBoxStyle}
onClick={onChange}
>
<Checkbox
isChecked={selectedOrgIdList.includes(org._id)}
pointerEvents="none"
/>
<MyAvatar src={org.avatar} w="1.5rem" borderRadius={'50%'} />
<HStack ml="2" w="full" gap="5px">
<Text>{org.name}</Text>
{org.count && (
<>
<Tag size="sm" my="auto">
{org.count}
</Tag>
</>
)}
</HStack>
<PermissionTags permission={collaborator?.permission.value} />
{org.count && (
<>
<Tag size="sm" my="auto">
{org.count}
</Tag>
</>
<MyIcon
name="core/chat/chevronRight"
w="16px"
p="4px"
rounded={'6px'}
_hover={{
bgColor: 'myGray.200'
}}
onClick={(e) => {
setParentPath(getOrgChildrenPath(org));
e.stopPropagation();
}}
/>
)}
</HStack>
<PermissionTags permission={collaborator?.permission.value} />
{org.count && (
<MyIcon
name="core/chat/chevronRight"
w="16px"
p="4px"
rounded={'6px'}
_hover={{
bgColor: 'myGray.200'
}}
onClick={(e) => {
setParentPath(getOrgChildrenPath(org));
e.stopPropagation();
}}
/>
)}
</HStack>
);
})}
{filterMembers?.map((member) => {
const onChange = () => {
setSelectedMembers((state) => {
if (state.includes(member.tmbId)) {
return state.filter((v) => v !== member.tmbId);
}
return [...state, member.tmbId];
});
};
const collaborator = collaboratorList?.find((v) => v.tmbId === member.tmbId);
const memberOrgs = orgs.filter((org) =>
org.members.find((v) => String(v.tmbId) === String(member.tmbId))
);
const memberPathIds = memberOrgs.map((org) =>
(org.path + '/' + org.pathId).split('/').slice(0)
);
const memberOrgNames = memberPathIds.map((pathIds) =>
pathIds.map((id) => orgs.find((v) => v.pathId === id)?.name).join('/')
);
return (
<MemberItemCard
avatar={member.avatar}
key={member.tmbId}
name={member.memberName}
permission={collaborator?.permission.value}
onChange={onChange}
isChecked={selectedMemberIdList.includes(member.tmbId)}
orgs={memberOrgNames}
/>
);
})}
{filterGroups?.map((group) => {
const onChange = () => {
setSelectedGroupIdList((state) => {
if (state.includes(group._id)) {
return state.filter((v) => v !== group._id);
}
return [...state, group._id];
});
};
const collaborator = collaboratorList?.find((v) => v.groupId === group._id);
return (
<MemberItemCard
avatar={group.avatar}
key={group._id}
name={
group.name === DefaultGroupName ? userInfo?.team.teamName ?? '' : group.name
}
permission={collaborator?.permission.value}
onChange={onChange}
isChecked={selectedGroupIdList.includes(group._id)}
/>
);
})}
</ScrollData>
);
})}
{filterMembers?.map((member) => {
const onChange = () => {
setSelectedMembers((state) => {
if (state.includes(member.tmbId)) {
return state.filter((v) => v !== member.tmbId);
}
return [...state, member.tmbId];
});
};
const collaborator = collaboratorList?.find((v) => v.tmbId === member.tmbId);
const memberOrgs = orgs.filter((org) =>
org.members.find((v) => String(v.tmbId) === String(member.tmbId))
);
const memberPathIds = memberOrgs.map((org) =>
(org.path + '/' + org.pathId).split('/').slice(0)
);
const memberOrgNames = memberPathIds.map((pathIds) =>
pathIds.map((id) => orgs.find((v) => v.pathId === id)?.name).join('/')
);
return (
<MemberItemCard
avatar={member.avatar}
key={member.tmbId}
name={member.memberName}
permission={collaborator?.permission.value}
onChange={onChange}
isChecked={selectedMemberIdList.includes(member.tmbId)}
orgs={memberOrgNames}
/>
);
})}
</ScrollData>
)}
{filterGroups?.map((group) => {
const onChange = () => {
setSelectedGroupIdList((state) => {
if (state.includes(group._id)) {
return state.filter((v) => v !== group._id);
}
return [...state, group._id];
});
};
const collaborator = collaboratorList?.find((v) => v.groupId === group._id);
return (
<MemberItemCard
avatar={group.avatar}
key={group._id}
name={
group.name === DefaultGroupName ? userInfo?.team.teamName ?? '' : group.name
}
permission={collaborator?.permission.value}
onChange={onChange}
isChecked={selectedGroupIdList.includes(group._id)}
/>
);
})}
</Flex>
</Flex>

View File

@@ -18,12 +18,18 @@ function OrgTags({ orgs, type = 'simple' }: { orgs: string[]; type?: 'simple' |
}
>
{type === 'simple' ? (
<Box fontSize="sm" fontWeight={400} w="full" color="myGray.500" whiteSpace={'nowrap'}>
<Box
className="textEllipsis"
fontSize="xs"
fontWeight={400}
w="full"
color="myGray.400"
whiteSpace={'nowrap'}
>
{orgs
.map((org) => org.split('/').pop())
.join(', ')
.slice(0, 30)}
{orgs.length > 1 && '...'}
</Box>
) : (
<Flex direction="row" gap="1" p="2" alignItems={'start'} wrap={'wrap'}>

View File

@@ -194,19 +194,7 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
{t('account_team:user_team_invite_member')}
</Button>
)}
{!userInfo?.team.permission.isOwner && (
<Button
variant={'whitePrimary'}
size="md"
borderRadius={'md'}
ml={3}
leftIcon={<MyIcon name={'support/account/loginoutLight'} w={'14px'} />}
onClick={() => openLeaveConfirm(onLeaveTeam)()}
>
{t('account_team:user_team_leave_team')}
</Button>
)}
{userInfo?.team.permission.hasManagePer && (
{userInfo?.team.permission.isOwner && isSyncMember && (
<Button
variant={'whitePrimary'}
size="md"
@@ -223,6 +211,18 @@ function MemberTable({ Tabs }: { Tabs: React.ReactNode }) {
{t('account_team:export_members')}
</Button>
)}
{!userInfo?.team.permission.isOwner && (
<Button
variant={'whitePrimary'}
size="md"
borderRadius={'md'}
ml={3}
leftIcon={<MyIcon name={'support/account/loginoutLight'} w={'14px'} />}
onClick={() => openLeaveConfirm(onLeaveTeam)()}
>
{t('account_team:user_team_leave_team')}
</Button>
)}
</HStack>
</Flex>

View File

@@ -87,7 +87,7 @@ function PermissionManage({
const groupList = collaboratorList.filter(
(item) =>
Object.keys(item).includes('groupId') &&
(!searchKey || searchResult?.groups.find((group) => group.groupId === item.groupId))
(!searchKey || searchResult?.groups.find((group) => group._id === item.groupId))
);
const orgList = collaboratorList.filter(
(item) =>

View File

@@ -96,6 +96,7 @@ const ExtractFieldModal = ({
<Input
bg={'myGray.50'}
placeholder="name/age/sql"
maxLength={20}
{...register('key', { required: true })}
/>
</Flex>

View File

@@ -35,6 +35,7 @@ import IOTitle from '../../components/IOTitle';
import { useContextSelector } from 'use-context-selector';
import { WorkflowContext } from '../../../context';
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
const NodeExtract = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
const { inputs, outputs, nodeId } = data;
@@ -71,7 +72,7 @@ const NodeExtract = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
</Button>
</Flex>
<TableContainer borderRadius={'md'} overflow={'hidden'} borderWidth={'1px'} mt={2}>
<TableContainer borderRadius={'md'} overflow={'auto'} borderWidth={'1px'} mt={2}>
<Table variant={'workflow'}>
<Thead>
<Tr>
@@ -85,19 +86,23 @@ const NodeExtract = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
{extractKeys.map((item, index) => (
<Tr key={index}>
<Td>
<Flex alignItems={'center'}>
<Flex alignItems={'center'} maxW={'300px'} className={'textEllipsis'}>
<MyIcon name={'checkCircle'} w={'14px'} mr={1} color={'myGray.600'} />
{item.key}
</Flex>
</Td>
<Td>{item.desc}</Td>
<Td>
<Box maxW={'300px'} whiteSpace={'pre-wrap'}>
{item.desc}
</Box>
</Td>
<Td>
{item.required ? (
<Flex alignItems={'center'}>
<MyIcon name={'check'} w={'16px'} color={'myGray.900'} mr={2} />
</Flex>
) : (
''
'-'
)}
</Td>
<Td>
@@ -197,7 +202,7 @@ const NodeExtract = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
const newOutput: FlowNodeOutputItemType = {
id: getNanoid(),
key: data.key,
label: `${t('common:extraction_results')}-${data.desc}`,
label: `${t('common:extraction_results')}-${data.key}`,
valueType: data.valueType || WorkflowIOValueTypeEnum.string,
type: FlowNodeOutputTypeEnum.static
};

View File

@@ -11,6 +11,9 @@ async function handler(req: NextApiRequest, _res: NextApiResponse) {
await authCert({ req, authRoot: true });
const users = await MongoUser.find();
const teams = await MongoTeam.find();
console.log('Total users:', users.length);
let success = 0;
for await (const user of users) {
try {
const team = teams.find((team) => String(team.ownerId) === String(user._id));
@@ -18,6 +21,7 @@ async function handler(req: NextApiRequest, _res: NextApiResponse) {
user.contact = team.notificationAccount;
}
await user.save();
console.log('Success:', ++success);
} catch (error) {
console.error(error);
}

View File

@@ -6,12 +6,12 @@ import { ApiRequestProps } from '@fastgpt/service/type/next';
import { syncCollection } from '@fastgpt/service/core/dataset/collection/utils';
/*
Collection sync
1. Check collection type: link, api dataset collection
2. Get collection and raw text
3. Check whether the original text is the same: skip if same
4. Create new collection
5. Delete old collection
Collection sync
1. Check collection type: link, api dataset collection
2. Get collection and raw text
3. Check whether the original text is the same: skip if same
4. Create new collection
5. Delete old collection
*/
export type CollectionSyncBody = {
collectionId: string;
@@ -27,6 +27,7 @@ async function handler(req: ApiRequestProps<CollectionSyncBody>) {
const { collection } = await authDatasetCollection({
req,
authToken: true,
authApiKey: true,
collectionId,
per: WritePermissionVal
});

View File

@@ -16,13 +16,13 @@ async function handler(
const user = await getUserDetail({ tmbId });
// Remove sensitive information
if (user.team.lafAccount) {
user.team.lafAccount = {
appid: user.team.lafAccount.appid,
token: '',
pat: ''
};
}
// if (user.team.lafAccount) {
// user.team.lafAccount = {
// appid: user.team.lafAccount.appid,
// token: '',
// pat: ''
// };
// }
if (user.team.openaiAccount) {
user.team.openaiAccount = {
key: '',

View File

@@ -302,65 +302,64 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
})();
// save chat
if (chatId) {
const isOwnerUse = !shareId && !spaceTeamId && String(tmbId) === String(app.tmbId);
const source = (() => {
if (shareId) {
return ChatSourceEnum.share;
}
if (authType === 'apikey') {
return ChatSourceEnum.api;
}
if (spaceTeamId) {
return ChatSourceEnum.team;
}
return ChatSourceEnum.online;
})();
const isInteractiveRequest = !!getLastInteractiveValue(histories);
const { text: userInteractiveVal } = chatValue2RuntimePrompt(userQuestion.value);
const newTitle = isPlugin
? variables.cTime ?? getSystemTime(timezone)
: getChatTitleFromChatMessage(userQuestion);
const aiResponse: AIChatItemType & { dataId?: string } = {
dataId: responseChatItemId,
obj: ChatRoleEnum.AI,
value: assistantResponses,
[DispatchNodeResponseKeyEnum.nodeResponse]: flowResponses
};
if (isInteractiveRequest) {
await updateInteractiveChat({
chatId,
appId: app._id,
userInteractiveVal,
aiResponse,
newVariables
});
} else {
await saveChat({
chatId,
appId: app._id,
teamId,
tmbId: tmbId,
nodes,
appChatConfig: chatConfig,
variables: newVariables,
isUpdateUseTime: isOwnerUse && source === ChatSourceEnum.online, // owner update use time
newTitle,
shareId,
outLinkUid: outLinkUserId,
source: source,
sourceName: sourceName || '',
content: [userQuestion, aiResponse],
metadata: {
originIp,
...metadata
}
});
const isOwnerUse = !shareId && !spaceTeamId && String(tmbId) === String(app.tmbId);
const source = (() => {
if (shareId) {
return ChatSourceEnum.share;
}
if (authType === 'apikey') {
return ChatSourceEnum.api;
}
if (spaceTeamId) {
return ChatSourceEnum.team;
}
return ChatSourceEnum.online;
})();
const isInteractiveRequest = !!getLastInteractiveValue(histories);
const { text: userInteractiveVal } = chatValue2RuntimePrompt(userQuestion.value);
const newTitle = isPlugin
? variables.cTime ?? getSystemTime(timezone)
: getChatTitleFromChatMessage(userQuestion);
const aiResponse: AIChatItemType & { dataId?: string } = {
dataId: responseChatItemId,
obj: ChatRoleEnum.AI,
value: assistantResponses,
[DispatchNodeResponseKeyEnum.nodeResponse]: flowResponses
};
const saveChatId = chatId || getNanoid(24);
if (isInteractiveRequest) {
await updateInteractiveChat({
chatId: saveChatId,
appId: app._id,
userInteractiveVal,
aiResponse,
newVariables
});
} else {
await saveChat({
chatId: saveChatId,
appId: app._id,
teamId,
tmbId: tmbId,
nodes,
appChatConfig: chatConfig,
variables: newVariables,
isUpdateUseTime: isOwnerUse && source === ChatSourceEnum.online, // owner update use time
newTitle,
shareId,
outLinkUid: outLinkUserId,
source,
sourceName: sourceName || '',
content: [userQuestion, aiResponse],
metadata: {
originIp,
...metadata
}
});
}
addLog.info(`completions running time: ${(Date.now() - startTime) / 1000}s`);
@@ -407,9 +406,11 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
return assistantResponses;
})();
const error = flowResponses[flowResponses.length - 1]?.error;
res.json({
...(detail ? { responseData: feResponseData, newVariables } : {}),
error,
id: chatId || '',
model: '',
usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 1 },