feat: add more share config (#3120)

* feat: add more share config

* add i18n en
This commit is contained in:
papapatrick 2024-11-12 10:09:02 +08:00 committed by archer
parent f4e0dfc9bd
commit 5892ded567
No known key found for this signature in database
GPG Key ID: 4446499B846D4A9E
30 changed files with 389 additions and 173 deletions

View File

@ -82,7 +82,7 @@ export const filterPublicNodeResponseData = ({
}: {
flowResponses?: ChatHistoryItemResType[];
}) => {
const filedList = ['quoteList', 'moduleType', 'pluginOutput'];
const filedList = ['quoteList', 'moduleType', 'pluginOutput', 'runningTime'];
const filterModuleTypeList: any[] = [
FlowNodeTypeEnum.pluginModule,
FlowNodeTypeEnum.datasetSearchNode,

View File

@ -10,3 +10,7 @@ export type AuthOutLinkLimitProps = AuthOutLinkChatProps & { outLink: OutLinkSch
export type AuthOutLinkResponse = {
uid: string;
};
export type AuthOutLinkProps = {
shareId?: string;
outLinkUid?: string;
};

View File

@ -51,6 +51,10 @@ export type OutLinkSchema<T extends OutlinkAppType = undefined> = {
// whether the response content is detailed
responseDetail: boolean;
// whether to hide the node status
showNodeStatus: boolean;
// whether to show the complete quote
showCompleteQuote: boolean;
// response when request
immediateResponse?: string;
@ -79,6 +83,8 @@ export type OutLinkEditType<T = undefined> = {
_id?: string;
name: string;
responseDetail?: OutLinkSchema<T>['responseDetail'];
showNodeStatus?: OutLinkSchema<T>['showNodeStatus'];
showCompleteQuote?: OutLinkSchema<T>['showCompleteQuote'];
// response when request
immediateResponse?: string;
// response when error or other situation

View File

@ -18,12 +18,14 @@ export const getWorkflowResponseWrite = ({
res,
detail,
streamResponse,
id = getNanoid(24)
id = getNanoid(24),
showNodeStatus = true
}: {
res?: NextApiResponse;
detail: boolean;
streamResponse: boolean;
id?: string;
showNodeStatus?: boolean;
}) => {
return ({
write,
@ -50,8 +52,18 @@ export const getWorkflowResponseWrite = ({
SseResponseEventEnum.toolResponse,
SseResponseEventEnum.updateVariables
];
if (!detail && detailEvent.includes(event)) return;
if (
!showNodeStatus &&
(event === SseResponseEventEnum.flowNodeStatus ||
event === SseResponseEventEnum.toolCall ||
event === SseResponseEventEnum.toolParams ||
event === SseResponseEventEnum.toolResponse)
)
return;
responseWrite({
res,
write,

View File

@ -46,6 +46,14 @@ const OutLinkSchema = new Schema({
type: Boolean,
default: false
},
showNodeStatus: {
type: Boolean,
default: false
},
showCompleteQuote: {
type: Boolean,
default: false
},
limit: {
maxUsagePoints: {
type: Number,

View File

@ -74,6 +74,7 @@
"module.type": "\"{{type}}\" type\n{{description}}",
"modules.Title is required": "Module name cannot be empty",
"month.unit": "Day",
"move.hint": "After moving, the selected application/folder will inherit the permission settings of the new folder, and the original permission settings will become invalid.",
"move_app": "Move Application",
"not_json_file": "Please select a JSON file",
"open_vision_function_tip": "Models with icon switches have image recognition capabilities. \nAfter being turned on, the model will parse the pictures in the file link and automatically parse the pictures in the user's question (user question ≤ 500 words).",

View File

@ -35,6 +35,7 @@
"not_select_file": "No file selected",
"plugins_output": "Plugin Output",
"question_tip": "From top to bottom, the response order of each module",
"response.child total points": "Sub-workflow point consumption",
"response.dataset_concat_length": "Combined total",
"response.node_inputs": "Node Inputs",
"select": "Select",

View File

@ -53,6 +53,7 @@
"code_error.error_code.502": "Gateway Error",
"code_error.error_code.503": "Server Overloaded or Under Maintenance",
"code_error.error_code.504": "Gateway Timeout",
"code_error.error_code[429]": "Requests are too frequent",
"code_error.error_message.403": "Credential Error",
"code_error.error_message.510": "Insufficient Account Balance",
"code_error.error_message.511": "Unauthorized to Operate This Model",
@ -79,6 +80,7 @@
"code_error.team_error.plugin_amount_not_enough": "Plugin Limit Reached",
"code_error.team_error.re_rank_not_enough": "Unauthorized to Use Re-Rank",
"code_error.team_error.un_auth": "Unauthorized to Operate This Team",
"code_error.team_error.user_not_active": "The user did not accept or has left the team",
"code_error.team_error.website_sync_not_enough": "Unauthorized to Use Website Sync",
"code_error.token_error_code.403": "Invalid Login Status, Please Re-login",
"code_error.user_error.balance_not_enough": "Insufficient Account Balance",
@ -120,6 +122,7 @@
"common.Documents": "Documents",
"common.Done": "Done",
"common.Edit": "Edit",
"common.Error": "Error",
"common.Exit": "Exit",
"common.Exit Directly": "Exit Directly",
"common.Expired Time": "Expiration Time",
@ -151,6 +154,7 @@
"common.Params": "Parameters",
"common.Password inconsistency": "Passwords Do Not Match",
"common.Permission": "Permission",
"common.Permission_tip": "Individual permissions are greater than group permissions",
"common.Please Input Name": "Please Enter a Name",
"common.Read document": "Read Document",
"common.Read intro": "Read Introduction",
@ -188,7 +192,6 @@
"common.Update Successful": "Updated Successfully",
"common.Username": "Username",
"common.Waiting": "Waiting",
"common.Error": "Error",
"common.Warning": "Warning",
"common.Website": "Website",
"common.all_result": "Full Results",
@ -546,6 +549,7 @@
"core.dataset.import.Chunk Range": "Range: {{min}}~{{max}}",
"core.dataset.import.Chunk Split": "Direct Segmentation",
"core.dataset.import.Chunk Split Tip": "Segment the text according to certain rules and convert it into a format that can be semantically searched. Suitable for most scenarios. No additional model processing is required, and the cost is low.",
"core.dataset.import.Continue upload": "Continue upload",
"core.dataset.import.Custom process": "Custom Rules",
"core.dataset.import.Custom process desc": "Customize segmentation and preprocessing rules",
"core.dataset.import.Custom prompt": "Custom Prompt",
@ -574,11 +578,10 @@
"core.dataset.import.Select source": "Select Source",
"core.dataset.import.Source name": "Source Name",
"core.dataset.import.Sources list": "Source List",
"core.dataset.import.Continue upload": "Continue upload",
"core.dataset.import.Upload complete": "Upload complete",
"core.dataset.import.Start upload": "Start Upload",
"core.dataset.import.Total files": "Total {{total}} Files",
"core.dataset.import.Training mode": "Training Mode",
"core.dataset.import.Upload complete": "Upload complete",
"core.dataset.import.Upload data": "Confirm Upload",
"core.dataset.import.Upload file progress": "File Upload Progress",
"core.dataset.import.Upload status": "Status",
@ -889,7 +892,9 @@
"is_using": "In Use",
"item_description": "Field Description",
"item_name": "Field Name",
"just_now": "just",
"key_repetition": "Key Repetition",
"move.confirm": "Confirm move",
"navbar.Account": "Account",
"navbar.Chat": "Chat",
"navbar.Datasets": "Datasets",
@ -970,6 +975,9 @@
"support.outlink.Usage points": "Points Consumption",
"support.outlink.share.Response Quote": "Return Quote",
"support.outlink.share.Response Quote tips": "Return quoted content in the share link, but do not allow users to download the original document",
"support.outlink.share.running_node": "Running node",
"support.outlink.share.show_complete_quote": "View original source",
"support.outlink.share.show_complete_quote_tips": "View and download the complete citation document, or jump to the citation website",
"support.permission.Permission": "Permission",
"support.standard.AI Bonus Points": "AI Points",
"support.standard.due_date": "Due Date",
@ -1200,6 +1208,8 @@
"user.team.member.waiting": "Pending Acceptance",
"user.team.role.Admin": "Admin",
"user.team.role.Owner": "Owner",
"user.team.role.Visitor": "visitor",
"user.team.role.writer": "writable member",
"user.type": "Type",
"verification": "Verification",
"xx_search_result": "{{key}} Search Results",

View File

@ -8,6 +8,7 @@
"confirm_to_rebuild_embedding_tip": "Are you sure you want to switch the index for the Dataset?\nSwitching the index is a significant operation that requires re-indexing all data in your Dataset, which may take a long time. Please ensure your account has sufficient remaining points.\n\nAdditionally, you need to update the applications that use this Dataset to avoid conflicts with other indexed model Datasets.",
"custom_data_process_params": "Custom",
"custom_data_process_params_desc": "Customize data processing rules",
"data.ideal_chunk_length": "ideal block length",
"data_process_params": "Processing parameters",
"data_process_setting": "Data processing configuration",
"dataset.no_collections": "No datasets available",
@ -25,6 +26,7 @@
"ideal_chunk_length_tips": "Segment according to the end symbol and combine multiple segments into one block. This value determines the estimated size of the block, if there is any fluctuation.",
"import.Auto mode Estimated Price Tips": "The text understanding model needs to be called, which requires more points: {{price}} points/1K tokens",
"import.Embedding Estimated Price Tips": "Only use the index model and consume a small amount of AI points: {{price}} points/1K tokens",
"move.hint": "After moving, the selected knowledge base/folder will inherit the permission settings of the new folder, and the original permission settings will become invalid.",
"permission.des.manage": "Can manage the entire knowledge base data and information",
"permission.des.read": "View knowledge base content",
"permission.des.write": "Ability to add and change knowledge base content",
@ -44,4 +46,4 @@
"training_mode": "Chunk mode",
"website_dataset": "Website Sync",
"website_dataset_desc": "Website sync allows you to build a Dataset directly using a web link."
}
}

View File

@ -1,6 +1,7 @@
{
"app_key_tips": "These keys are already linked to the current application. Check the documentation for detailed usage.",
"basic_info": "Basic Info",
"config": "Visibility configuration",
"copy_link_hint": "Copy the link below to the specified location",
"create_api_key": "Create New Key",
"create_link": "Create Link",
@ -22,7 +23,10 @@
"publish_name": "Name",
"qpm_is_empty": "QPM cannot be empty",
"qpm_tips": "Maximum number of queries per minute per IP",
"quote_content": "Quote content",
"request_address": "Request URL",
"show_node": "real-time running status",
"show_origin_content": "View original source",
"show_share_link_modal_title": "Get Started",
"token_auth": "Token Authentication",
"token_auth_tips": "Token authentication server URL. If provided, a request will be sent to the specified server for authentication before each conversation.",
@ -33,4 +37,4 @@
"wecom.create_modal_title": "Create WeCom Bot",
"wecom.edit_modal_title": "Edit WeCom Bot",
"wecom.title": "Publish to WeCom Bot"
}
}

View File

@ -22,6 +22,8 @@
"bind_inform_account_success": "Notification Account Bound Successfully",
"delete.admin_failed": "Failed to Delete Admin",
"delete.admin_success": "Admin Deleted Successfully",
"delete.failed": "Delete failed",
"delete.success": "Delete successfully",
"has_chosen": "Selected",
"individuation": "Individuation",
"login.error": "Login Error",
@ -32,6 +34,7 @@
"notification.Bind Notification Pipe Hint": "Please bind a notification receiving account to ensure you receive notifications such as plan expiration reminders, ensuring your service runs smoothly.",
"notification.remind_owner_bind": "Please remind the creator to bind a notification account",
"operations": "Actions",
"owner": "owner",
"password.code_required": "Verification Code Required",
"password.code_send_error": "Failed to Send Verification Code",
"password.code_sended": "Verification Code Sent",
@ -76,6 +79,32 @@
"synchronization.title": "Enter the sync tag link and click the sync button to synchronize",
"team.Add manager": "Add Admin",
"team.add_collaborator": "Add Collaborator",
"team.add_writer": "Add writable members",
"team.avatar_and_name": "avatar",
"team.belong_to_group": "Member group",
"team.group.avatar": "Group avatar",
"team.group.create": "Create group",
"team.group.create_failed": "Failed to create group",
"team.group.default_group": "Default group",
"team.group.delete_confirm": "Confirm to delete group?",
"team.group.edit": "Edit group",
"team.group.edit_info": "Edit information",
"team.group.group": "group",
"team.group.keep_admin": "Keep administrator rights",
"team.group.manage_member": "Managing members",
"team.group.manage_tip": "You can invite members, delete members, create groups, manage all groups, and assign permissions to groups and members",
"team.group.members": "member",
"team.group.name": "Group name",
"team.group.permission.manage": "administrator",
"team.group.permission.write": "Workbench/knowledge base creation",
"team.group.permission_tip": "Members with individually configured permissions will follow the individual permission configuration and will no longer be affected by group permissions.\n\nIf a member is in multiple permission groups, the member's permissions are combined.",
"team.group.role.admin": "administrator",
"team.group.role.member": "member",
"team.group.role.owner": "owner",
"team.group.search_placeholder": "Search member/group name",
"team.group.set_as_admin": "Set as administrator",
"team.group.toast.can_not_delete_owner": "Owner cannot be deleted, please transfer first",
"team.group.transfer_owner": "transfer owner",
"team.manage_collaborators": "Manage Collaborators",
"team.no_collaborators": "No Collaborators",
"team.write_role_member": "",
@ -84,4 +113,4 @@
"usage.share": "Share Link",
"usage.wecom": "WeCom",
"usage_record": "Usages"
}
}

View File

@ -29,6 +29,7 @@
"contains": "Contains",
"content_to_retrieve": "Content to Retrieve",
"content_to_search": "Content to Search",
"contextMenu.addComment": "Add comment",
"context_menu.add_comment": "Add comment",
"create_link_error": "Error creating link",
"custom_feedback": "Custom Feedback",
@ -177,6 +178,7 @@
"tool_params.params_description_placeholder": "Name/Age/SQL statement..",
"tool_params.params_name": "Name",
"tool_params.params_name_placeholder": "name/age/sql",
"tool_params.tool_params_result": "Parameter configuration results",
"trigger_after_application_completion": "Will be triggered after the application is fully completed",
"update_link_error": "Error updating link",
"update_specified_node_output_or_global_variable": "Can update the output value of a specified node or update global variables",

View File

@ -974,8 +974,11 @@
"support.outlink.Max usage points": "积分上限",
"support.outlink.Max usage points tip": "该链接最多允许使用多少积分,超出后将无法使用。-1 代表无限制。",
"support.outlink.Usage points": "积分消耗",
"support.outlink.share.Response Quote": "返回引用",
"support.outlink.share.Response Quote tips": "在分享链接中返回引用内容,但不会允许用户下载原文档",
"support.outlink.share.Response Quote": "引用内容",
"support.outlink.share.Response Quote tips": "查看知识库搜索的引用内容,不可查看完整引用文档或跳转引用网站",
"support.outlink.share.running_node": "运行节点",
"support.outlink.share.show_complete_quote": "查看来源原文",
"support.outlink.share.show_complete_quote_tips": "查看及下载完整引用文档,或跳转引用网站",
"support.permission.Permission": "权限",
"support.standard.AI Bonus Points": "AI 积分",
"support.standard.due_date": "到期时间",

View File

@ -1,6 +1,7 @@
{
"app_key_tips": "这些 key 已有当前应用标识,具体使用可参考文档",
"basic_info": "基本信息",
"config": "可见度配置",
"copy_link_hint": "将下面链接复制到指定位置",
"create_api_key": "创建新 key",
"create_link": "创建链接",
@ -25,12 +26,15 @@
"request_address": "请求地址",
"show_share_link_modal_title": "开始使用",
"token_auth": "身份验证",
"token_auth_tips": "身份校验服务器地址,如填写该值,每次对话前都会向指定服务器发送一个请求,进行身份校验",
"token_auth_tips": "身份校验服务器地址",
"token_auth_use_cases": "查看身份验证使用说明",
"wecom.api": "企微 API",
"wecom.bot": "企业微信机器人",
"wecom.bot_desc": "通过 API 直接接入企业微信机器人",
"wecom.create_modal_title": "创建企微机器人",
"wecom.edit_modal_title": "编辑企微机器人",
"wecom.title": "发布到企业微信机器人"
}
"wecom.title": "发布到企业微信机器人",
"show_node": "实时运行状态",
"quote_content": "引用内容",
"show_origin_content": "查看来源原文"
}

View File

@ -34,7 +34,7 @@ export type ChatProviderProps = OutLinkChatAuthProps & {
// not chat test params
chatId?: string;
chatType?: 'log' | 'chat';
chatType?: 'log' | 'chat' | 'share' | 'team';
};
type useChatStoreType = OutLinkChatAuthProps &

View File

@ -13,6 +13,9 @@ import { useSystem } from '@fastgpt/web/hooks/useSystem';
import { ChatSiteItemType } from '@fastgpt/global/core/chat/type';
import { addStatisticalDataToHistoryItem } from '@/global/core/chat/utils';
import { useSize } from 'ahooks';
import { ChatContext } from '@/web/core/chat/context/chatContext';
import { useContextSelector } from 'use-context-selector';
import { ChatBoxContext } from '../Provider';
const QuoteModal = dynamic(() => import('./QuoteModal'));
const ContextModal = dynamic(() => import('./ContextModal'));
@ -37,6 +40,7 @@ const ResponseTags = ({
totalRunningTime: runningTime = 0,
historyPreviewLength = 0
} = useMemo(() => addStatisticalDataToHistoryItem(historyItem), [historyItem]);
const [quoteModalData, setQuoteModalData] = useState<{
rawSearch: SearchDataResponseItemType[];
metadata?: {
@ -47,6 +51,13 @@ const ResponseTags = ({
}>();
const [quoteFolded, setQuoteFolded] = useState<boolean>(true);
const showCompleteQuote = useContextSelector(ChatContext, (v) => v.showCompleteQuote);
const { chatType } = useContextSelector(ChatBoxContext, (v) => v);
const showAllTag = useMemo(() => {
return chatType !== 'share' && chatType !== 'team';
}, [chatType]);
const {
isOpen: isOpenWholeModal,
onOpen: onOpenWholeModal,
@ -77,10 +88,10 @@ const ResponseTags = ({
sourceName: item.sourceName,
sourceId: item.sourceId,
icon: getSourceNameIcon({ sourceId: item.sourceId, sourceName: item.sourceName }),
canReadQuote: showDetail || strIsLink(item.sourceId),
canReadQuote: showCompleteQuote || strIsLink(item.sourceId),
collectionId: item.collectionId
}));
}, [quoteList, showDetail]);
}, [quoteList, showCompleteQuote]);
return !showTags ? null : (
<>
@ -176,49 +187,51 @@ const ResponseTags = ({
</Flex>
</>
)}
{showDetail && (
<Flex alignItems={'center'} mt={3} flexWrap={'wrap'} gap={2}>
{quoteList.length > 0 && (
<MyTooltip label={t('chat:view_citations')}>
<MyTag
colorSchema="blue"
type="borderSolid"
cursor={'pointer'}
onClick={() => setQuoteModalData({ rawSearch: quoteList })}
>
{t('chat:citations', { num: quoteList.length })}
</MyTag>
</MyTooltip>
)}
{llmModuleAccount === 1 && (
<>
{historyPreviewLength > 0 && (
<MyTooltip label={t('chat:click_contextual_preview')}>
<MyTag
colorSchema="green"
cursor={'pointer'}
type="borderSolid"
onClick={onOpenContextModal}
>
{t('chat:contextual', { num: historyPreviewLength })}
</MyTag>
</MyTooltip>
)}
</>
)}
{llmModuleAccount > 1 && (
<MyTag type="borderSolid" colorSchema="blue">
{t('chat:multiple_AI_conversations')}
</MyTag>
)}
{isPc && runningTime > 0 && (
<MyTooltip label={t('chat:module_runtime_and')}>
<MyTag colorSchema="purple" type="borderSolid" cursor={'default'}>
{runningTime}s
</MyTag>
</MyTooltip>
)}
<Flex alignItems={'center'} mt={3} flexWrap={'wrap'} gap={2}>
{quoteList.length > 0 && (
<MyTooltip label={t('chat:view_citations')}>
<MyTag
colorSchema="blue"
type="borderSolid"
cursor={'pointer'}
onClick={() => setQuoteModalData({ rawSearch: quoteList })}
>
{t('chat:citations', { num: quoteList.length })}
</MyTag>
</MyTooltip>
)}
{llmModuleAccount === 1 && showAllTag && (
<>
{historyPreviewLength > 0 && (
<MyTooltip label={t('chat:click_contextual_preview')}>
<MyTag
colorSchema="green"
cursor={'pointer'}
type="borderSolid"
onClick={onOpenContextModal}
>
{t('chat:contextual', { num: historyPreviewLength })}
</MyTag>
</MyTooltip>
)}
</>
)}
{llmModuleAccount > 1 && showAllTag && (
<MyTag type="borderSolid" colorSchema="blue">
{t('chat:multiple_AI_conversations')}
</MyTag>
)}
{isPc && runningTime > 0 && (
<MyTooltip label={t('chat:module_runtime_and')}>
<MyTag colorSchema="purple" type="borderSolid" cursor={'default'}>
{runningTime}s
</MyTag>
</MyTooltip>
)}
{showAllTag && (
<MyTooltip label={t('common:core.chat.response.Read complete response tips')}>
<MyTag
colorSchema="gray"
@ -229,18 +242,19 @@ const ResponseTags = ({
{t('common:core.chat.response.Read complete response')}
</MyTag>
</MyTooltip>
</Flex>
)}
)}
</Flex>
{!!quoteModalData && (
<QuoteModal
{...quoteModalData}
showDetail={showDetail}
showDetail={showCompleteQuote}
onClose={() => setQuoteModalData(undefined)}
/>
)}
{isOpenContextModal && <ContextModal dataId={dataId} onClose={onCloseContextModal} />}
{isOpenWholeModal && (
<WholeResponseModal dataId={dataId} showDetail={showDetail} onClose={onCloseWholeModal} />
<WholeResponseModal dataId={dataId} showDetail={true} onClose={onCloseWholeModal} />
)}
</>
);

View File

@ -9,6 +9,8 @@ import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import dynamic from 'next/dynamic';
import MyBox from '@fastgpt/web/components/common/MyBox';
import { SearchScoreTypeEnum, SearchScoreTypeMap } from '@fastgpt/global/core/dataset/constants';
import { useContextSelector } from 'use-context-selector';
import { ChatBoxContext } from '../chat/ChatContainer/ChatBox/Provider';
const InputDataModal = dynamic(() => import('@/pages/dataset/detail/components/InputDataModal'));
@ -54,6 +56,12 @@ const QuoteItem = ({
const { t } = useTranslation();
const [editInputData, setEditInputData] = useState<{ dataId: string; collectionId: string }>();
const { chatType } = useContextSelector(ChatBoxContext, (v) => v);
const canEdit = useMemo(() => {
return chatType !== 'share' && chatType !== 'team';
}, [chatType]);
const score = useMemo(() => {
if (!Array.isArray(quoteItem.score)) {
return {
@ -224,7 +232,7 @@ const QuoteItem = ({
canView={canViewSource}
/>
<Box flex={1} />
{quoteItem.id && (
{quoteItem.id && canEdit && (
<MyTooltip label={t('common:core.dataset.data.Edit')}>
<Box
className="hover-data"
@ -252,7 +260,7 @@ const QuoteItem = ({
</Box>
</MyTooltip>
)}
{linkToDataset && (
{linkToDataset && canEdit && (
<Link
as={NextLink}
className="hover-data"

View File

@ -6,6 +6,8 @@ import { getCollectionSourceAndOpen } from '@/web/core/dataset/hooks/readCollect
import { getSourceNameIcon } from '@fastgpt/global/core/dataset/utils';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useI18n } from '@/web/context/I18n';
import { ChatBoxContext } from '../chat/ChatContainer/ChatBox/Provider';
import { useContextSelector } from 'use-context-selector';
type Props = BoxProps & {
sourceName?: string;
@ -23,11 +25,19 @@ const RawSourceBox = ({
}: Props) => {
const { t } = useTranslation();
const { fileT } = useI18n();
const { shareId, outLinkUid, chatType } = useContextSelector(ChatBoxContext, (v) => v);
const canPreview = !!sourceId && canView;
const icon = useMemo(() => getSourceNameIcon({ sourceId, sourceName }), [sourceId, sourceName]);
const read = getCollectionSourceAndOpen(collectionId);
const read = getCollectionSourceAndOpen({
collectionId,
authProps: {
shareId,
outLinkUid
},
isShare: chatType === 'share'
});
return (
<MyTooltip

View File

@ -5,12 +5,15 @@ import { DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constant
import { createFileToken } from '@fastgpt/service/support/permission/controller';
import { BucketNameEnum, ReadFileBaseUrl } from '@fastgpt/global/common/file/constants';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import { AuthOutLinkProps } from '@fastgpt/global/support/outLink/api';
import { authOutLink } from '@/service/support/permission/auth/outLink';
export type readCollectionSourceQuery = {
export type readCollectionSourceQuery = {};
export type readCollectionSourceBody = {
collectionId: string;
};
export type readCollectionSourceBody = {};
isShare?: boolean;
} & AuthOutLinkProps;
export type readCollectionSourceResponse = {
type: 'url';
@ -20,11 +23,17 @@ export type readCollectionSourceResponse = {
async function handler(
req: ApiRequestProps<readCollectionSourceBody, readCollectionSourceQuery>
): Promise<readCollectionSourceResponse> {
const { isShare, outLinkUid, shareId } = req.body;
if (isShare) {
await authOutLink({ shareId, outLinkUid });
}
const { collection, teamId, tmbId } = await authDatasetCollection({
req,
authToken: true,
authApiKey: true,
collectionId: req.query.collectionId,
collectionId: req.body.collectionId,
per: ReadPermissionVal
});

View File

@ -24,7 +24,7 @@ export type OutLinkUpdateResponse = {};
async function handler(
req: ApiRequestProps<OutLinkUpdateBody, OutLinkUpdateQuery>
): Promise<OutLinkUpdateResponse> {
const { _id, name, responseDetail, limit, app } = req.body;
const { _id, name, responseDetail, limit, app, showCompleteQuote, showNodeStatus } = req.body;
if (!_id) {
return Promise.reject(CommonErrEnum.missingParams);
@ -35,6 +35,8 @@ async function handler(
await MongoOutLink.findByIdAndUpdate(_id, {
name,
responseDetail,
showCompleteQuote,
showNodeStatus,
limit,
app
});

View File

@ -83,6 +83,8 @@ type AuthResponseType = {
user: UserModelSchema;
app: AppSchema;
responseDetail?: boolean;
showNodeStatus?: boolean;
showCompleteQuote?: boolean;
authType: `${AuthUserTypeEnum}`;
apikey?: string;
canWrite: boolean;
@ -160,7 +162,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
sourceName,
apikey,
canWrite,
outLinkUserId = customUid
outLinkUserId = customUid,
showNodeStatus
} = await (async () => {
// share chat
if (shareId && outLinkUid) {
@ -256,7 +259,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
res,
detail,
streamResponse: stream,
id: chatId
id: chatId,
showNodeStatus
});
/* start flow controller */
@ -461,7 +465,7 @@ const authShareChat = async ({
shareId: string;
chatId?: string;
}): Promise<AuthResponseType> => {
const { teamId, tmbId, user, appId, authType, responseDetail, uid, sourceName } =
const { teamId, tmbId, user, appId, authType, responseDetail, showNodeStatus, uid, sourceName } =
await authOutLinkChatStart(data);
const app = await MongoApp.findById(appId).lean();
@ -485,7 +489,8 @@ const authShareChat = async ({
apikey: '',
authType,
canWrite: false,
outLinkUserId: uid
outLinkUserId: uid,
showNodeStatus
};
};
const authTeamSpaceChat = async ({

View File

@ -1,4 +1,4 @@
import React, { useMemo, useState } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import {
Flex,
Box,
@ -184,6 +184,8 @@ const Share = ({ appId }: { appId: string; type: PublishChannelEnum }) => {
_id: item._id,
name: item.name,
responseDetail: item.responseDetail,
showCompleteQuote: item.showCompleteQuote,
showNodeStatus: item.showNodeStatus,
limit: item.limit
})
},
@ -272,11 +274,27 @@ function EditLinkModal({
const {
register,
setValue,
watch,
handleSubmit: submitShareChat
} = useForm({
defaultValues: defaultData
});
const responseDetail = watch('responseDetail');
const showCompleteQuote = watch('showCompleteQuote');
useEffect(() => {
if (!responseDetail) {
setValue('showCompleteQuote', false);
}
}, [responseDetail, setValue]);
useEffect(() => {
if (showCompleteQuote) {
setValue('responseDetail', true);
}
}, [showCompleteQuote, setValue]);
const isEdit = useMemo(() => !!defaultData._id, [defaultData]);
const { mutate: onclickCreate, isLoading: creating } = useRequest({
@ -302,100 +320,130 @@ function EditLinkModal({
isOpen={true}
iconSrc="/imgs/modal/shareFill.svg"
title={isEdit ? publishT('edit_link') : publishT('create_link')}
w={'53.125rem'}
>
<ModalBody>
<Flex alignItems={'center'}>
<FormLabel flex={'0 0 90px'}>{t('common:Name')}</FormLabel>
<Input
placeholder={publishT('link_name')}
maxLength={20}
{...register('name', {
required: t('common:common.name_is_empty') || 'name_is_empty'
})}
/>
</Flex>
{feConfigs?.isPlus && (
<>
<ModalBody p={6}>
<Flex flexDir={['column', 'row']}>
<Box pr={[0, 6]} borderRight={['0px', '1px']} borderColor={['', 'myGray.150']}>
<Box fontSize={'sm'} fontWeight={'500'} color={'myGray.600'}>
{t('publish:basic_info')}
</Box>
<Flex alignItems={'center'} mt={4}>
<FormLabel flex={'0 0 90px'} alignItems={'center'}>
{t('common:common.Expired Time')}
</FormLabel>
<FormLabel flex={'0 0 90px'}>{t('common:Name')}</FormLabel>
<Input
type="datetime-local"
defaultValue={
defaultData.limit?.expiredTime
? dayjs(defaultData.limit?.expiredTime).format('YYYY-MM-DDTHH:mm')
: ''
}
onChange={(e) => {
setValue('limit.expiredTime', new Date(e.target.value));
}}
/>
</Flex>
<Flex alignItems={'center'} mt={4}>
<Flex flex={'0 0 90px'} alignItems={'center'}>
<FormLabel>QPM</FormLabel>
<QuestionTip ml={1} label={publishT('qpm_tips' || '')}></QuestionTip>
</Flex>
<Input
max={1000}
{...register('limit.QPM', {
min: 0,
max: 1000,
valueAsNumber: true,
required: publishT('qpm_is_empty') || ''
placeholder={publishT('link_name')}
maxLength={20}
{...register('name', {
required: t('common:common.name_is_empty') || 'name_is_empty'
})}
/>
</Flex>
<Flex alignItems={'center'} mt={4}>
<Flex flex={'0 0 90px'} alignItems={'center'}>
<FormLabel>{t('common:support.outlink.Max usage points')}</FormLabel>
{feConfigs?.isPlus && (
<>
<Flex alignItems={'center'} mt={4}>
<FormLabel flex={'0 0 90px'} alignItems={'center'}>
{t('common:common.Expired Time')}
</FormLabel>
<Input
type="datetime-local"
defaultValue={
defaultData.limit?.expiredTime
? dayjs(defaultData.limit?.expiredTime).format('YYYY-MM-DDTHH:mm')
: ''
}
onChange={(e) => {
setValue('limit.expiredTime', new Date(e.target.value));
}}
/>
</Flex>
<Flex alignItems={'center'} mt={4}>
<Flex flex={'0 0 90px'} alignItems={'center'}>
<FormLabel>QPM</FormLabel>
<QuestionTip ml={1} label={publishT('qpm_tips' || '')}></QuestionTip>
</Flex>
<Input
max={1000}
{...register('limit.QPM', {
min: 0,
max: 1000,
valueAsNumber: true,
required: publishT('qpm_is_empty') || ''
})}
/>
</Flex>
<Flex alignItems={'center'} mt={4}>
<Flex flex={'0 0 90px'} alignItems={'center'}>
<FormLabel>{t('common:support.outlink.Max usage points')}</FormLabel>
<QuestionTip
ml={1}
label={t('common:support.outlink.Max usage points tip')}
></QuestionTip>
</Flex>
<Input
{...register('limit.maxUsagePoints', {
min: -1,
max: 10000000,
valueAsNumber: true,
required: true
})}
/>
</Flex>
<Flex alignItems={'center'} mt={4}>
<Flex flex={'0 0 90px'} alignItems={'center'}>
<FormLabel>{publishT('token_auth')}</FormLabel>
<QuestionTip ml={1} label={publishT('token_auth_tips') || ''}></QuestionTip>
</Flex>
<Input
placeholder={publishT('token_auth_tips') || ''}
fontSize={'sm'}
{...register('limit.hookUrl')}
/>
</Flex>
<Link
href={getDocPath('/docs/development/openapi/share')}
target={'_blank'}
fontSize={'xs'}
color={'myGray.500'}
>
{publishT('token_auth_use_cases')}
</Link>
</>
)}
</Box>
<Box pl={[0, 6]} flexGrow={1} pt={[6, 0]}>
<Box fontSize={'sm'} fontWeight={'500'} color={'myGray.600'}>
{t('publish:config')}
</Box>
<Flex alignItems={'center'} mt={4} justify={'space-between'}>
<Flex alignItems={'center'}>
<FormLabel>{t('publish:show_node')}</FormLabel>
</Flex>
<Switch {...register('showNodeStatus')} />
</Flex>
<Flex alignItems={'center'} mt={4} justify={'space-between'}>
<Flex alignItems={'center'}>
<FormLabel>{t('common:support.outlink.share.Response Quote')}</FormLabel>
<QuestionTip
ml={1}
label={t('common:support.outlink.Max usage points tip')}
label={t('common:support.outlink.share.Response Quote tips' || '')}
></QuestionTip>
</Flex>
<Input
{...register('limit.maxUsagePoints', {
min: -1,
max: 10000000,
valueAsNumber: true,
required: true
})}
/>
<Switch {...register('responseDetail')} isChecked={responseDetail} />
</Flex>
<Flex alignItems={'center'} mt={4}>
<Flex flex={'0 0 90px'} alignItems={'center'}>
<FormLabel>{publishT('token_auth')}</FormLabel>
<QuestionTip ml={1} label={publishT('token_auth_tips') || ''}></QuestionTip>
<Flex alignItems={'center'} mt={4} justify={'space-between'}>
<Flex alignItems={'center'}>
<FormLabel>{t('common:support.outlink.share.show_complete_quote')}</FormLabel>
<QuestionTip
ml={1}
label={t('common:support.outlink.share.show_complete_quote_tips' || '')}
></QuestionTip>
</Flex>
<Input
placeholder={publishT('token_auth_tips') || ''}
fontSize={'sm'}
{...register('limit.hookUrl')}
/>
<Switch {...register('showCompleteQuote')} isChecked={showCompleteQuote} />
</Flex>
<Link
href={getDocPath('/docs/development/openapi/share')}
target={'_blank'}
fontSize={'xs'}
color={'myGray.500'}
>
{publishT('token_auth_use_cases')}
</Link>
</>
)}
<Flex alignItems={'center'} mt={4}>
<Flex flex={'0 0 90px'} alignItems={'center'}>
<FormLabel>{t('common:support.outlink.share.Response Quote')}</FormLabel>
<QuestionTip
ml={1}
label={t('support.outlink.share.Response Quote tips' || '')}
></QuestionTip>
</Flex>
<Switch {...register('responseDetail')} />
</Box>
</Flex>
</ModalBody>

View File

@ -42,6 +42,7 @@ type Props = {
shareId: string;
authToken: string;
customUid: string;
showCompleteQuote: boolean;
};
const OutLink = (
@ -364,6 +365,7 @@ const OutLink = (
chatId={chatId}
shareId={shareId}
outLinkUid={outLinkUid}
chatType="share"
/>
)}
</Box>
@ -375,13 +377,13 @@ const OutLink = (
};
const Render = (props: Props) => {
const { shareId, authToken, customUid } = props;
const { shareId, authToken, customUid, showCompleteQuote } = props;
const { localUId, loaded } = useShareChatStore();
const [isLoaded, setIsLoaded] = useState(false);
const contextParams = useMemo(() => {
return { shareId, outLinkUid: authToken || customUid || localUId };
}, [authToken, customUid, localUId, shareId]);
return { shareId, outLinkUid: authToken || localUId || customUid, showCompleteQuote };
}, [authToken, customUid, localUId, shareId, showCompleteQuote]);
useMount(() => {
setIsLoaded(true);
@ -415,7 +417,7 @@ export async function getServerSideProps(context: any) {
{
shareId
},
'appId'
'appId showCompleteQuote'
)
.populate('appId', 'name avatar intro')
.lean()) as OutLinkWithAppType;
@ -431,6 +433,7 @@ export async function getServerSideProps(context: any) {
appName: app?.appId?.name ?? 'AI',
appAvatar: app?.appId?.avatar ?? '',
appIntro: app?.appId?.intro ?? 'AI',
showCompleteQuote: app?.showCompleteQuote ?? false,
shareId: shareId ?? '',
authToken: authToken ?? '',
customUid,

View File

@ -296,6 +296,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
chatId={chatId}
teamId={teamId}
teamToken={teamToken}
chatType="team"
/>
)}
</Box>

View File

@ -10,6 +10,8 @@ import { formatTime2YMDHM } from '@fastgpt/global/common/string/time';
import { DatasetCollectionTypeMap, TrainingTypeMap } from '@fastgpt/global/core/dataset/constants';
import { getCollectionSourceAndOpen } from '@/web/core/dataset/hooks/readCollectionSource';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useContextSelector } from 'use-context-selector';
import { ChatBoxContext } from '@/components/core/chat/ChatContainer/ChatBox/Provider';
const MetaDataCard = ({ datasetId }: { datasetId: string }) => {
const { t } = useTranslation();
@ -18,7 +20,17 @@ const MetaDataCard = ({ datasetId }: { datasetId: string }) => {
collectionId: string;
datasetId: string;
};
const readSource = getCollectionSourceAndOpen(collectionId);
const { shareId, outLinkUid, chatType } = useContextSelector(ChatBoxContext, (v) => v);
const readSource = getCollectionSourceAndOpen({
collectionId,
authProps: {
shareId,
outLinkUid
},
isShare: chatType === 'share'
});
const { data: collection, loading: isLoading } = useRequest2(
() => getDatasetCollectionById(collectionId),
{

View File

@ -3,7 +3,8 @@ import type {
AuthOutLinkChatProps,
AuthOutLinkLimitProps,
AuthOutLinkInitProps,
AuthOutLinkResponse
AuthOutLinkResponse,
AuthOutLinkProps
} from '@fastgpt/global/support/outLink/api.d';
import { authOutLinkValid } from '@fastgpt/service/support/permission/publish/authLink';
import { getUserChatInfoAndAuthTeamPoints } from '@/service/support/permission/auth/team';
@ -23,10 +24,7 @@ export function authOutLinkChatLimit(data: AuthOutLinkLimitProps): Promise<AuthO
export const authOutLink = async ({
shareId,
outLinkUid
}: {
shareId?: string;
outLinkUid?: string;
}): Promise<{
}: AuthOutLinkProps): Promise<{
uid: string;
appId: string;
shareChat: OutLinkSchema;
@ -70,6 +68,7 @@ export async function authOutLinkChatStart({
tmbId: shareChat.tmbId,
authType: AuthUserTypeEnum.token,
responseDetail: shareChat.responseDetail,
showNodeStatus: shareChat.showNodeStatus,
user,
appId,
uid

View File

@ -23,6 +23,8 @@ export const defaultApp: AppDetailType = {
export const defaultOutLinkForm: OutLinkEditType = {
name: '',
responseDetail: false,
showNodeStatus: false,
showCompleteQuote: false,
limit: {
QPM: 100,
maxUsagePoints: -1

View File

@ -16,7 +16,7 @@ import { getNanoid } from '@fastgpt/global/common/string/tools';
import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination';
type ChatContextValueType = {
params: Record<string, string | number>;
params: Record<string, string | number | boolean>;
};
type ChatContextType = {
chatId: string;
@ -44,6 +44,7 @@ type ChatContextType = {
isLoading: boolean;
histories: ChatHistoryItemType[];
onUpdateHistoryTitle: ({ chatId, newTitle }: { chatId: string; newTitle: string }) => void;
showCompleteQuote: boolean;
};
export const ChatContext = createContext<ChatContextType>({
@ -85,7 +86,8 @@ export const ChatContext = createContext<ChatContextType>({
onChangeAppId: function (appId: string): void {
throw new Error('Function not implemented.');
},
isLoading: false
isLoading: false,
showCompleteQuote: true
});
const ChatContextProvider = ({
@ -94,6 +96,7 @@ const ChatContextProvider = ({
}: ChatContextValueType & { children: ReactNode }) => {
const router = useRouter();
const { chatId = '' } = router.query as { chatId: string };
const { showCompleteQuote }: { showCompleteQuote?: boolean } = params;
const forbidLoadChat = useRef(false);
@ -225,7 +228,8 @@ const ChatContextProvider = ({
ScrollData,
loadHistories,
histories,
onUpdateHistoryTitle
onUpdateHistoryTitle,
showCompleteQuote: showCompleteQuote ?? true
};
return <ChatContext.Provider value={contextValue}>{children}</ChatContext.Provider>;
};

View File

@ -56,6 +56,7 @@ import type { UpdateDatasetDataProps } from '@fastgpt/global/core/dataset/contro
import type { DatasetFolderCreateBody } from '@/pages/api/core/dataset/folder/create';
import type { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type';
import type { GetScrollCollectionsProps } from '@/pages/api/core/dataset/collection/scrollList';
import { AuthOutLinkProps } from '@fastgpt/global/support/outLink/api';
/* ======================== dataset ======================= */
export const getDatasets = (data: GetDatasetListBody) =>
@ -197,5 +198,6 @@ export const getPreviewChunks = (data: PostPreviewFilesChunksProps) =>
POST<PreviewChunksResponse>('/core/dataset/file/getPreviewChunks', data);
/* ================== read source ======================== */
export const getCollectionSource = (collectionId: string) =>
GET<readCollectionSourceResponse>('/core/dataset/collection/read', { collectionId });
export const getCollectionSource = (
data: { collectionId: string; isShare?: boolean } & AuthOutLinkProps
) => POST<readCollectionSourceResponse>('/core/dataset/collection/read', data);

View File

@ -1,10 +1,20 @@
import { authOutLink } from '@/service/support/permission/auth/outLink';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { getCollectionSource } from '@/web/core/dataset/api';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { AuthOutLinkProps } from '@fastgpt/global/support/outLink/api';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { useTranslation } from 'next-i18next';
export function getCollectionSourceAndOpen(collectionId: string) {
export function getCollectionSourceAndOpen({
collectionId,
authProps,
isShare
}: {
collectionId: string;
authProps: AuthOutLinkProps;
isShare?: boolean;
}) {
const { toast } = useToast();
const { t } = useTranslation();
const { setLoading } = useSystemStore();
@ -12,7 +22,8 @@ export function getCollectionSourceAndOpen(collectionId: string) {
return async () => {
try {
setLoading(true);
const { value: url } = await getCollectionSource(collectionId);
const { value: url } = await getCollectionSource({ collectionId, isShare, ...authProps });
if (!url) {
throw new Error('No file found');