4.8.12 test (#3006)

* perf: oneapi error tip

* fix: qps limit condition error

* perf: Plan tip

* fix: permission modal ui

* perf: dataset slider ui

* perf: api key auth tmbId problem

* perf: dataset upload i18n

* fix: http json path check
This commit is contained in:
Archer
2024-10-28 22:47:45 +08:00
committed by GitHub
parent b712a821f8
commit e06d72e86e
29 changed files with 270 additions and 294 deletions

View File

@@ -12,7 +12,7 @@ import { jsonRes } from '../response';
export function useReqFrequencyLimit(seconds: number, limit: number) {
return async (req: ApiRequestProps, res: NextApiResponse) => {
const ip = requestIp.getClientIp(req);
if (!ip && process.env.USE_IP_LIMIT !== 'true') {
if (!ip || process.env.USE_IP_LIMIT !== 'true') {
return;
}
try {

View File

@@ -45,6 +45,7 @@ import { computedMaxToken, llmCompletionsBodyFormat } from '../../../ai/utils';
import { WorkflowResponseType } from '../type';
import { formatTime2YMDHM } from '@fastgpt/global/common/string/time';
import { AiChatQuoteRoleType } from '@fastgpt/global/core/workflow/template/system/aiChat/type';
import { getErrText } from '@fastgpt/global/common/error/utils';
export type ChatProps = ModuleDispatchProps<
AIChatNodeProps & {
@@ -262,7 +263,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
});
if (user.openaiAccount?.baseUrl) {
return Promise.reject(`您的 OpenAI key 出错了: ${JSON.stringify(requestBody)}`);
return Promise.reject(`您的 OpenAI key 出错了: ${getErrText(error)}`);
}
return Promise.reject(error);

View File

@@ -240,13 +240,22 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
.forEach((item) => {
const key = item.key.startsWith('$') ? item.key : `$.${item.key}`;
results[item.key] = (() => {
// 检查是否是简单的属性访问或单一索引访问
if (/^\$(\.[a-zA-Z_][a-zA-Z0-9_]*)+$/.test(key) || /^\$(\[\d+\])+$/.test(key)) {
return JSONPath({ path: key, json: formatResponse })[0];
const result = JSONPath({ path: key, json: formatResponse });
// 如果结果为空,返回 undefined
if (!result || result.length === 0) {
return undefined;
}
// 如果无法确定,默认返回数组
return JSONPath({ path: key, json: formatResponse });
// 以下情况返回数组:
// 1. 使用通配符 *
// 2. 使用数组切片 [start:end]
// 3. 使用过滤表达式 [?(...)]
// 4. 使用递归下降 ..
// 5. 使用多个结果运算符 ,
const needArrayResult = /[*]|[\[][:?]|\.\.|\,/.test(key);
return needArrayResult ? result : result[0];
})();
});

View File

@@ -19,7 +19,7 @@ const colorMap: Record<
}
> = {
white: {
borderColor: 'myGray.400',
borderColor: 'myGray.200',
bg: 'white',
color: 'myGray.700'
},

View File

@@ -75,15 +75,22 @@ export const useConfirm = (props?: {
const [requesting, setRequesting] = useState(false);
useEffect(() => {
timer.current = setInterval(() => {
setCountDownAmount((val) => {
if (val <= 0) {
clearInterval(timer.current);
}
return val - 1;
});
}, 1000);
}, []);
if (isOpen) {
setCountDownAmount(countDown);
timer.current = setInterval(() => {
setCountDownAmount((val) => {
if (val <= 0) {
clearInterval(timer.current);
}
return val - 1;
});
}, 1000);
return () => {
clearInterval(timer.current);
};
}
}, [isOpen]);
return (
<MyModal isOpen={isOpen} iconSrc={iconSrc} title={title} maxW={['90vw', '400px']}>

View File

@@ -27,7 +27,6 @@
"cron.every_month": "Run Monthly",
"cron.every_week": "Run Weekly",
"cron.interval": "Run at Intervals",
"current_settings": "Current Configuration",
"day": "Day",
"document_quote": "Document Reference",
"document_quote_tip": "Usually used to accept user-uploaded document content (requires document parsing), and can also be used to reference other string data.",
@@ -160,4 +159,4 @@
"workflow.user_file_input_desc": "Links to documents and images uploaded by users.",
"workflow.user_select": "User Selection",
"workflow.user_select_tip": "This module can configure multiple options for selection during the dialogue. Different options can lead to different workflow branches."
}
}

View File

@@ -624,7 +624,7 @@
"core.dataset.test.test result placeholder": "Test results will be displayed here",
"core.dataset.test.test result tip": "Sort based on the similarity between the Dataset content and the test text. You can adjust the corresponding text based on the test results.\nNote: The data in the test records may have been modified. Clicking on a test data will display the latest data.",
"core.dataset.training.Agent queue": "QA Training Queue",
"core.dataset.training.Auto mode": "Enhanced Processing (Experimental)",
"core.dataset.training.Auto mode": "Auto Processing",
"core.dataset.training.Auto mode Tip": "Increase the semantic richness of data blocks by generating related questions and summaries through sub-indexes and calling models, making it more conducive to retrieval. Requires more storage space and increases AI call times.",
"core.dataset.training.Chunk mode": "Direct Segmentation",
"core.dataset.training.Full": "Estimated Over 5 Minutes",
@@ -1198,4 +1198,4 @@
"verification": "Verification",
"xx_search_result": "{{key}} Search Results",
"yes": "Yes"
}
}

View File

@@ -6,6 +6,10 @@
"common_dataset": "General Dataset",
"common_dataset_desc": "Build a Dataset by importing files, web links, or manual input.",
"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_process_params": "Processing parameters",
"data_process_setting": "Data processing configuration",
"dataset.no_collections": "No datasets available",
"dataset.no_tags": "No tags available",
"external_file": "External File Library",
@@ -17,6 +21,10 @@
"file_model_function_tip": "Enhances indexing and QA generation",
"filename": "Filename",
"folder_dataset": "Folder",
"ideal_chunk_length": "ideal block length",
"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",
"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",
@@ -33,6 +41,7 @@
"tag.tags": "Tags",
"tag.total_tags": "Total {{total}} tags",
"the_knowledge_base_has_indexes_that_are_being_trained_or_being_rebuilt": "The Dataset has indexes that are being trained or rebuilt",
"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

@@ -16,4 +16,4 @@
"agree": "agree",
"cookies_tip": " This website uses cookies to provide a better service experience. By continuing to use the site, you agree to our Cookie Policy.",
"privacy_policy": "Privacy Policy"
}
}

View File

@@ -54,7 +54,6 @@
"field_description_placeholder": "Describe the function of this input field. If it is a tool call parameter, this description will affect the quality of the model generation.",
"field_name_already_exists": "Field name already exists",
"field_required": "Required",
"field_used_as_reference": "Support reference",
"field_used_as_tool_input": "Used as Tool Call Parameter",
"filter_description": "Currently supports filtering by tags and creation time. Fill in the format as follows:\n{\n \"tags\": {\n \"$and\": [\"Tag 1\",\"Tag 2\"],\n \"$or\": [\"When there are $and tags, and is effective, or is not effective\"]\n },\n \"createTime\": {\n \"$gte\": \"YYYY-MM-DD HH:mm format, collection creation time greater than this time\",\n \"$lte\": \"YYYY-MM-DD HH:mm format, collection creation time less than this time, can be used with $gte\"\n }\n}",
"form_input_result": "User complete input result",
@@ -175,7 +174,6 @@
"tool_params.params_description_placeholder": "Name/Age/SQL statement..",
"tool_params.params_name": "Name",
"tool_params.params_name_placeholder": "name/age/sql",
"tool_params_config": "Tool params config",
"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",
@@ -185,7 +183,6 @@
"user_form_input_name": "Name",
"user_question": "User Question",
"user_question_tool_desc": "User input questions (questions need to be improved)",
"value_type": "Value type",
"variable_description": "Variable description",
"variable_picker_tips": "Type node name or variable name to search",
"variable_update": "Variable Update",
@@ -193,4 +190,4 @@
"workflow.Switch_success": "Switch Successful",
"workflow.Team cloud": "Team Cloud",
"workflow.exit_tips": "Your changes have not been saved. 'Exit directly' will not save your edits."
}
}

View File

@@ -27,7 +27,6 @@
"cron.every_month": "每月执行",
"cron.every_week": "每周执行",
"cron.interval": "间隔执行",
"current_settings": "当前配置",
"day": "日",
"document_quote": "文档引用",
"document_quote_tip": "通常用于接受用户上传的文档内容(这需要文档解析),也可以用于引用其他字符串数据。",
@@ -161,4 +160,4 @@
"workflow.user_file_input_desc": "用户上传的文档和图片链接",
"workflow.user_select": "用户选择",
"workflow.user_select_tip": "该模块可配置多个选项,以供对话时选择。不同选项可导向不同工作流支线"
}
}

View File

@@ -41,4 +41,4 @@
"upload": "上传",
"view_citations": "查看引用",
"web_site_sync": "Web站点同步"
}
}

View File

@@ -20,7 +20,6 @@
"Folder": "文件夹",
"Login": "登录",
"Move": "移动",
"move.confirm": "确认移动",
"Name": "名称",
"None": "无",
"Rename": "重命名",
@@ -83,8 +82,6 @@
"code_error.team_error.un_auth": "无权操作该团队",
"code_error.team_error.user_not_active": "用户未接受或已离开团队",
"code_error.team_error.website_sync_not_enough": "无权使用Web站点同步~",
"code_error.team_error.group_name_duplicate": "群组名称重复",
"code_error.team_error.user_not_active": "用户未接受或已离开团队",
"code_error.token_error_code.403": "登录状态无效,请重新登录",
"code_error.user_error.balance_not_enough": "账号余额不足~",
"code_error.user_error.bin_visitor": "您的身份校验未通过",
@@ -544,14 +541,14 @@
"core.dataset.externalFile": "外部文件库",
"core.dataset.file": "文件",
"core.dataset.folder": "目录",
"core.dataset.import.Auto mode Estimated Price Tips": "需调用文本理解模型,需要消耗较多 tokens{{price}} 积分/1K tokens",
"core.dataset.import.Auto mode Estimated Price Tips": "需调用文本理解模型,需要消耗较多AI 积分{{price}} 积分/1K tokens",
"core.dataset.import.Auto process": "自动",
"core.dataset.import.Auto process desc": "自动设置分割和预处理规则",
"core.dataset.import.Chunk Range": "范围:{{min}}~{{max}}",
"core.dataset.import.Chunk Split": "直接分段",
"core.dataset.import.Chunk Split Tip": "将文本按一定的规则进行分段处理后,转成可进行语义搜索的格式,适合绝大多数场景。不需要调用模型额外处理,成本低。",
"core.dataset.import.Custom process": "自定义规则",
"core.dataset.import.Custom process desc": "自定义设置分制和预处理规则",
"core.dataset.import.Custom process desc": "自定义设置数据处理规则",
"core.dataset.import.Custom prompt": "自定义提示词",
"core.dataset.import.Custom split char": "自定义分隔符",
"core.dataset.import.Custom split char Tips": "允许你根据自定义的分隔符进行分块。通常用于已处理好的数据,使用特定的分隔符来精确分块。",
@@ -633,7 +630,7 @@
"core.dataset.test.test result placeholder": "测试结果将在这里展示",
"core.dataset.test.test result tip": "根据知识库内容与测试文本的相似度进行排序,你可以根据测试结果调整对应的文本。\n注意测试记录中的数据可能已经被修改过点击某条测试数据后将展示最新的数据。",
"core.dataset.training.Agent queue": "QA 训练排队",
"core.dataset.training.Auto mode": "增强处理(实验)",
"core.dataset.training.Auto mode": "增强处理",
"core.dataset.training.Auto mode Tip": "通过子索引以及调用模型生成相关问题与摘要,来增加数据块的语义丰富度,更利于检索。需要消耗更多的存储空间和增加 AI 调用次数。",
"core.dataset.training.Chunk mode": "直接分段",
"core.dataset.training.Full": "预计 5 分钟以上",
@@ -892,6 +889,7 @@
"item_description": "字段描述",
"item_name": "字段名",
"key_repetition": "key 重复",
"move.confirm": "确认移动",
"navbar.Account": "账号",
"navbar.Chat": "聊天",
"navbar.Datasets": "知识库",
@@ -1209,4 +1207,4 @@
"verification": "验证",
"xx_search_result": "{{key}} 的搜索结果",
"yes": "是"
}
}

View File

@@ -6,6 +6,11 @@
"common_dataset": "通用知识库",
"common_dataset_desc": "可通过导入文件、网页链接或手动录入形式构建知识库",
"confirm_to_rebuild_embedding_tip": "确认为知识库切换索引?\n切换索引是一个非常重量的操作需要对您知识库内所有数据进行重新索引时间可能较长请确保账号内剩余积分充足。\n\n此外你还需要注意修改选择该知识库的应用避免它们与其他索引模型知识库混用。",
"custom_data_process_params": "自定义",
"custom_data_process_params_desc": "自定义设置数据处理规则",
"data.ideal_chunk_length": "理想分块长度",
"data_process_params": "处理参数",
"data_process_setting": "数据处理配置",
"dataset.no_collections": "暂无数据集",
"dataset.no_tags": "暂无标签",
"external_file": "外部文件库",
@@ -17,6 +22,14 @@
"file_model_function_tip": "用于增强索引和 QA 生成",
"filename": "文件名",
"folder_dataset": "文件夹",
"ideal_chunk_length": "理想分块长度",
"ideal_chunk_length_tips": "按结束符号进行分段,并将多个分段组成一个分块,该值决定了分块的预估大小,如果会有上下浮动。",
"import.Auto mode Estimated Price Tips": "需调用文本理解模型需要消耗较多AI 积分:{{price}} 积分/1K tokens",
"import.Embedding Estimated Price Tips": "仅使用索引模型,消耗少量 AI 积分:{{price}} 积分/1K tokens",
"move.hint": "移动后,所选知识库/文件夹将继承新文件夹的权限设置,原先的权限设置失效。",
"permission.des.manage": "可管理整个知识库数据和信息",
"permission.des.read": "可查看知识库内容",
"permission.des.write": "可增加和变更知识库内容",
"rebuild_embedding_start_tip": "切换索引模型任务已开始",
"rebuilding_index_count": "重建中索引数量:{{count}}",
"tag.Add New": "新建",
@@ -30,10 +43,7 @@
"tag.tags": "标签",
"tag.total_tags": "共{{total}}个标签",
"the_knowledge_base_has_indexes_that_are_being_trained_or_being_rebuilt": "知识库有训练中或正在重建的索引",
"training_mode": "处理方式",
"website_dataset": "Web 站点同步",
"website_dataset_desc": "Web 站点同步允许你直接使用一个网页链接构建知识库",
"permission.des.read": "可查看知识库内容",
"permission.des.write": "可增加和变更知识库内容",
"permission.des.manage": "可管理整个知识库数据和信息",
"move.hint": "移动后,所选知识库/文件夹将继承新文件夹的权限设置,原先的权限设置失效。"
}
"website_dataset_desc": "Web 站点同步允许你直接使用一个网页链接构建知识库"
}

View File

@@ -16,4 +16,4 @@
"agree": "同意",
"cookies_tip": "本网站使用 cookies 提供更好的服务体验。继续使用即表示您同意我们的 Cookie 政策。",
"privacy_policy": "隐私政策"
}
}

View File

@@ -55,7 +55,6 @@
"field_description_placeholder": "描述该输入字段的功能,如果为工具调用参数,则该描述会影响模型生成的质量",
"field_name_already_exists": "字段名已经存在",
"field_required": "必填",
"field_used_as_reference": "支持变量引用",
"field_used_as_tool_input": "作为工具调用参数",
"filter_description": "目前支持标签和创建时间过滤,需按照以下格式填写:\n{\n \"tags\": {\n \"$and\": [\"标签 1\",\"标签 2\"],\n \"$or\": [\"有 $and 标签时and 生效or 不生效\"]\n },\n \"createTime\": {\n \"$gte\": \"YYYY-MM-DD HH:mm 格式即可,集合的创建时间大于该时间\",\n \"$lte\": \"YYYY-MM-DD HH:mm 格式即可,集合的创建时间小于该时间,可和 $gte 共同使用\"\n }\n}",
"form_input_result": "用户完整输入结果",
@@ -186,7 +185,6 @@
"user_form_input_name": "标题",
"user_question": "用户问题",
"user_question_tool_desc": "用户输入的问题(问题需要完善)",
"value_type": "数据类型",
"variable_description": "变量描述",
"variable_picker_tips": "可输入节点名或变量名搜索",
"variable_update": "变量更新",
@@ -194,4 +192,4 @@
"workflow.Switch_success": "切换成功",
"workflow.Team cloud": "团队云端",
"workflow.exit_tips": "您的更改尚未保存,「直接退出」将不会保存您的编辑记录。"
}
}

View File

@@ -126,29 +126,11 @@ const FolderSlideCard = ({
<MyDivider my={6} />
<Box>
<FormLabel>{t('common:support.permission.Permission')}</FormLabel>
{!isInheritPermission && (
<Box mt={2}>
<ResumeInherit onResume={() => resumeInheritPermission?.().then(refetchResource)} />
</Box>
)}
{managePer.permission.hasManagePer && !!defaultPer && (
<Box mt={5}>
<Box fontSize={'sm'} color={'myGray.500'}>
{t('common:permission.Default permission')}
</Box>
<DefaultPermissionList
mt="1"
per={defaultPer.value}
defaultPer={defaultPer.defaultValue}
isInheritPermission={isInheritPermission}
onChange={(v) => defaultPer.onChange(v)}
hasParent={hasParent}
/>
</Box>
)}
<Box mt={6}>
<CollaboratorContextProvider
{...managePer}
@@ -190,8 +172,8 @@ const FolderSlideCard = ({
<MemberListCard
mt={2}
tagStyle={{
type: 'borderSolid',
colorSchema: 'gray'
type: 'fill',
colorSchema: 'white'
}}
/>
</>

View File

@@ -114,6 +114,35 @@ const ChatInput = ({
renderAudioGraph,
stream
} = useSpeech({ appId, ...outLinkAuthData });
const onWhisperRecord = useCallback(() => {
const finishWhisperTranscription = (text: string) => {
if (!text) return;
if (whisperConfig?.autoSend) {
onSendMessage({
text,
files: fileList,
autoTTSResponse
});
replaceFiles([]);
} else {
resetInputVal({ text });
}
};
if (isSpeaking) {
return stopSpeak();
}
startSpeak(finishWhisperTranscription);
}, [
autoTTSResponse,
fileList,
isSpeaking,
onSendMessage,
replaceFiles,
resetInputVal,
startSpeak,
stopSpeak,
whisperConfig?.autoSend
]);
useEffect(() => {
if (!stream) {
return;
@@ -131,28 +160,6 @@ const ChatInput = ({
};
renderCurve();
}, [renderAudioGraph, stream]);
const finishWhisperTranscription = useCallback(
(text: string) => {
if (!text) return;
if (whisperConfig?.autoSend) {
onSendMessage({
text,
files: fileList,
autoTTSResponse
});
replaceFiles([]);
} else {
resetInputVal({ text });
}
},
[autoTTSResponse, fileList, onSendMessage, replaceFiles, resetInputVal, whisperConfig?.autoSend]
);
const onWhisperRecord = useCallback(() => {
if (isSpeaking) {
return stopSpeak();
}
startSpeak(finishWhisperTranscription);
}, [finishWhisperTranscription, isSpeaking, startSpeak, stopSpeak]);
const RenderTranslateLoading = useMemo(
() => (

View File

@@ -95,23 +95,19 @@ function AddMemberModal({ onClose, mode = 'member' }: AddModalPropsType) {
iconSrc="modal/AddClb"
title={t('user:team.add_collaborator')}
minW="800px"
h={'100%'}
isCentered
isLoading={loadingMembersAndGroups}
>
<ModalBody>
<ModalBody flex={'1'}>
<Grid
border="1px solid"
borderColor="myGray.200"
borderRadius="0.5rem"
gridTemplateColumns="1fr 1fr"
h={'100%'}
>
<Flex
flexDirection="column"
borderRight="1px solid"
borderColor="myGray.200"
p="4"
minH="200px"
>
<Flex flexDirection="column" borderRight="1px solid" borderColor="myGray.200" p="4">
<SearchInput
placeholder={t('user:search_user')}
bgColor="myGray.50"

View File

@@ -325,6 +325,7 @@ const MyInfo = ({ onOpenContact }: { onOpenContact: () => void }) => {
</Box>
);
};
const PlanUsage = () => {
const router = useRouter();
const { t } = useTranslation();

View File

@@ -527,15 +527,14 @@ const authHeaderRequest = async ({
teamId,
tmbId,
authType,
apikey,
canWrite: apiKeyCanWrite
apikey
} = await authCert({
req,
authToken: true,
authApiKey: true
});
const { app, canWrite } = await (async () => {
const { app } = await (async () => {
if (authType === AuthUserTypeEnum.apikey) {
if (!apiKeyAppId) {
return Promise.reject(
@@ -551,16 +550,14 @@ const authHeaderRequest = async ({
appId = String(app._id);
return {
app,
canWrite: apiKeyCanWrite
app
};
} else {
// token_auth
if (!appId) {
return Promise.reject('appId is empty');
}
const { app, permission } = await authApp({
const { app } = await authApp({
req,
authToken: true,
appId,
@@ -568,8 +565,7 @@ const authHeaderRequest = async ({
});
return {
app,
canWrite: permission.hasReadPer
app
};
}
})();
@@ -579,7 +575,12 @@ const authHeaderRequest = async ({
MongoChat.findOne({ appId, chatId }).lean()
]);
if (chat && (String(chat.teamId) !== teamId || String(chat.tmbId) !== tmbId)) {
if (
chat &&
(String(chat.teamId) !== teamId ||
// There's no need to distinguish who created it if it's apiKey auth
(authType === AuthUserTypeEnum.token && String(chat.tmbId) !== tmbId))
) {
return Promise.reject(ChatErrEnum.unAuthChat);
}
@@ -591,7 +592,7 @@ const authHeaderRequest = async ({
responseDetail: true,
apikey,
authType,
canWrite
canWrite: true
};
};

View File

@@ -386,7 +386,7 @@ const Render = (props: Props) => {
return (
<>
<NextHead title={props.appName} desc={props.appIntro} icon={props.appAvatar} />
<NextHead title={props.appName || 'AI'} desc={props.appIntro} icon={props.appAvatar} />
{systemLoaded && (
<ChatContextProvider params={contextParams}>
<OutLink {...props} outLinkUid={contextParams.outLinkUid} />;

View File

@@ -15,11 +15,11 @@ function MemberManager({ managePer }: { managePer: MemberManagerInputPropsType }
return (
<>
<Flex alignItems="center" flexDirection="row" justifyContent="space-between" w="full">
<Box>
<FormLabel fontSize={'mini'}>{t('common:permission.Collaborator')}</FormLabel>
<Box color={'myGray.900'} fontSize={'mini'} fontWeight={'bold'}>
{t('common:permission.Collaborator')}
</Box>
<Flex gap={0.5}>
<Box p={1}>
<Flex gap={2}>
<Box>
<MyIcon
onClick={onOpenManageModal}
name="common/setting"
@@ -30,7 +30,7 @@ function MemberManager({ managePer }: { managePer: MemberManagerInputPropsType }
_hover={{ color: 'primary.500' }}
/>
</Box>
<Box p={1}>
<Box>
<MyIcon
cursor={'pointer'}
onClick={onOpenAddMember}

View File

@@ -182,7 +182,7 @@ const DatasetImportContextProvider = ({ children }: { children: React.ReactNode
showChunkInput: false,
showPromptInput: false,
charsPointsPrice: agentModel.charsPointsPrice,
priceTip: t('common:core.dataset.import.Auto mode Estimated Price Tips', {
priceTip: t('dataset:import.Auto mode Estimated Price Tips', {
price: agentModel.charsPointsPrice
}),
uploadRate: 100
@@ -197,7 +197,7 @@ const DatasetImportContextProvider = ({ children }: { children: React.ReactNode
showChunkInput: true,
showPromptInput: false,
charsPointsPrice: vectorModel.charsPointsPrice,
priceTip: t('common:core.dataset.import.Embedding Estimated Price Tips', {
priceTip: t('dataset:import.Embedding Estimated Price Tips', {
price: vectorModel.charsPointsPrice
}),
uploadRate: 150
@@ -212,8 +212,8 @@ const DatasetImportContextProvider = ({ children }: { children: React.ReactNode
showChunkInput: true,
showPromptInput: true,
charsPointsPrice: agentModel.charsPointsPrice,
priceTip: t('common:core.dataset.import.QA Estimated Price Tips', {
price: agentModel?.charsPointsPrice
priceTip: t('dataset:import.Auto mode Estimated Price Tips', {
price: agentModel.charsPointsPrice
}),
uploadRate: 30
}

View File

@@ -78,7 +78,7 @@ function DataProcess({ showPreviewChunks = true }: { showPreviewChunks: boolean
<Box h={'100%'} display={['block', 'flex']} fontSize={'sm'}>
<Box
flex={'1 0 0'}
minW={['auto', '540px']}
minW={['auto', '500px']}
maxW={'600px'}
h={['auto', '100%']}
overflow={'auto'}
@@ -86,11 +86,11 @@ function DataProcess({ showPreviewChunks = true }: { showPreviewChunks: boolean
>
<Flex alignItems={'center'}>
<MyIcon name={'common/settingLight'} w={'20px'} />
<Box fontSize={'md'}>{t('common:core.dataset.import.Data process params')}</Box>
<Box fontSize={'md'}>{t('dataset:data_process_setting')}</Box>
</Flex>
<Box display={['block', 'flex']} mt={4} alignItems={'center'}>
<FormLabel flex={'0 0 100px'}>{t('common:core.dataset.import.Training mode')}</FormLabel>
<FormLabel flex={'0 0 100px'}>{t('dataset:training_mode')}</FormLabel>
<LeftRadio
list={trainingModeList.map(([key, value]) => ({
title: t(value.label as any),
@@ -107,8 +107,9 @@ function DataProcess({ showPreviewChunks = true }: { showPreviewChunks: boolean
flexWrap={'wrap'}
/>
</Box>
<Box display={['block', 'flex']} mt={5}>
<FormLabel flex={'0 0 100px'}>{t('common:core.dataset.import.Process way')}</FormLabel>
<FormLabel flex={'0 0 100px'}>{t('dataset:data_process_params')}</FormLabel>
<LeftRadio
list={[
{
@@ -117,18 +118,16 @@ function DataProcess({ showPreviewChunks = true }: { showPreviewChunks: boolean
value: ImportProcessWayEnum.auto
},
{
title: t('common:core.dataset.import.Custom process'),
desc: t('common:core.dataset.import.Custom process desc'),
title: t('dataset:custom_data_process_params'),
desc: t('dataset:custom_data_process_params_desc'),
value: ImportProcessWayEnum.custom,
children: way === ImportProcessWayEnum.custom && (
<Box mt={5}>
{showChunkInput && chunkSizeField && (
<Box>
<Flex alignItems={'center'}>
<Box>{t('common:core.dataset.import.Ideal chunk length')}</Box>
<MyTooltip
label={t('common:core.dataset.import.Ideal chunk length Tips')}
>
<Box>{t('dataset:ideal_chunk_length')}</Box>
<MyTooltip label={t('dataset:ideal_chunk_length_tips')}>
<MyIcon
name={'common/questionLight'}
ml={1}
@@ -269,15 +268,15 @@ function DataProcess({ showPreviewChunks = true }: { showPreviewChunks: boolean
}}
></LeftRadio>
</Box>
<Box mt={5} pl={[0, '100px']} gap={3}>
{feConfigs?.show_pay && (
<MyTooltip label={priceTip}>
<MyTag colorSchema={'gray'} py={1.5} borderRadius={'md'} px={3} whiteSpace={'wrap'}>
{priceTip}
</MyTag>
</MyTooltip>
)}
</Box>
{feConfigs?.show_pay && (
<Box mt={5} pl={[0, '100px']} gap={3}>
<MyTag colorSchema={'gray'} py={1.5} borderRadius={'md'} px={3} whiteSpace={'wrap'}>
{priceTip}
</MyTag>
</Box>
)}
<Flex mt={5} gap={3} justifyContent={'flex-end'}>
<Button
onClick={() => {

View File

@@ -1,5 +1,5 @@
import React, { useState } from 'react';
import { Box, Flex, IconButton } from '@chakra-ui/react';
import { Box, Flex, Grid, IconButton } from '@chakra-ui/react';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useTranslation } from 'next-i18next';
@@ -19,68 +19,69 @@ const Preview = ({ showPreviewChunks }: { showPreviewChunks: boolean }) => {
const [previewChunkSource, setPreviewChunkSource] = useState<ImportSourceItemType>();
return (
<Box h={'100%'} display={['block', 'flex']} flexDirection={'column'}>
<Box h={'100%'} w={'100%'} display={['block', 'flex']} flexDirection={'column'}>
<Flex alignItems={'center'}>
<MyIcon name={'core/dataset/fileCollection'} w={'20px'} />
<Box fontSize={'md'}>{t('common:core.dataset.import.Sources list')}</Box>
</Flex>
<Box mt={3} flex={'1 0 0'} width={'100%'} overflow={'auto'}>
{sources.map((source) => (
<Flex
key={source.id}
bg={'white'}
p={4}
borderRadius={'md'}
borderWidth={'1px'}
borderColor={'borderColor.low'}
boxShadow={'2'}
mb={3}
alignItems={'center'}
>
<MyIcon name={source.icon as any} w={'16px'} />
<Box mx={1} flex={'1 0 0'} w={0} className="textEllipsis">
{source.sourceName}
</Box>
{showPreviewChunks && (
<Box fontSize={'xs'} color={'myGray.600'}>
<MyMenu
Button={
<IconButton
icon={<MyIcon name={'common/viewLight'} w={'14px'} p={2} />}
aria-label={''}
size={'sm'}
variant={'whitePrimary'}
/>
}
menuList={[
{
children: [
{
label: (
<Flex alignItems={'center'}>
<MyIcon name={'core/dataset/fileCollection'} w={'14px'} mr={2} />
{t('common:core.dataset.import.Preview raw text')}
</Flex>
),
onClick: () => setPreviewRawTextSource(source)
},
{
label: (
<Flex alignItems={'center'}>
<MyIcon name={'core/dataset/splitLight'} w={'14px'} mr={2} />
{t('common:core.dataset.import.Preview chunks')}
</Flex>
),
onClick: () => setPreviewChunkSource(source)
}
]
}
]}
/>
<Box mt={3} flex={'1 0 0'} width={'100%'} overflowY={'auto'}>
<Grid w={'100%'} gap={3} gridTemplateColumns={['1fr', '1fr', '1fr', '1fr', '1fr 1fr']}>
{sources.map((source) => (
<Flex
key={source.id}
bg={'white'}
p={4}
borderRadius={'md'}
borderWidth={'1px'}
borderColor={'borderColor.low'}
boxShadow={'2'}
alignItems={'center'}
>
<MyIcon name={source.icon as any} w={['1rem', '1.25rem']} />
<Box mx={1} flex={'1 0 0'} wordBreak={'break-all'} fontSize={'sm'}>
{source.sourceName}
</Box>
)}
</Flex>
))}
{showPreviewChunks && (
<Box fontSize={'xs'} color={'myGray.600'}>
<MyMenu
Button={
<IconButton
icon={<MyIcon name={'common/viewLight'} w={'14px'} p={2} />}
aria-label={''}
size={'sm'}
variant={'whitePrimary'}
/>
}
menuList={[
{
children: [
{
label: (
<Flex alignItems={'center'}>
<MyIcon name={'core/dataset/fileCollection'} w={'14px'} mr={2} />
{t('common:core.dataset.import.Preview raw text')}
</Flex>
),
onClick: () => setPreviewRawTextSource(source)
},
{
label: (
<Flex alignItems={'center'}>
<MyIcon name={'core/dataset/splitLight'} w={'14px'} mr={2} />
{t('common:core.dataset.import.Preview chunks')}
</Flex>
),
onClick: () => setPreviewChunkSource(source)
}
]
}
]}
/>
</Box>
)}
</Flex>
))}
</Grid>
</Box>
{!!previewRawTextSource && (
<PreviewRawText

View File

@@ -33,8 +33,6 @@ import { EditResourceInfoFormType } from '@/components/common/Modal/EditResource
const EditResourceModal = dynamic(() => import('@/components/common/Modal/EditResourceModal'));
const Info = ({ datasetId }: { datasetId: string }) => {
const [openBaseConfig, setOpenBaseConfig] = useState(true);
const [openPermissionConfig, setOpenPermissionConfig] = useState(true);
const { t } = useTranslation();
const { datasetDetail, loadDatasetDetail, updateDataset, rebuildingCount, trainingCount } =
useContextSelector(DatasetPageContext, (v) => v);
@@ -175,23 +173,13 @@ const Info = ({ datasetId }: { datasetId: string }) => {
<MyDivider my={4} h={'2px'} maxW={'500px'} />
<Box overflow={'hidden'} h={openBaseConfig ? 'auto' : '24px'}>
<Box overflow={'hidden'}>
<Flex justify={'space-between'} alignItems={'center'} fontSize={'mini'} h={'24px'}>
<Box fontWeight={'500'} color={'myGray.900'} userSelect={'none'}>
{t('common:common.base_config')}
</Box>
<MyIcon
w={'16px'}
_hover={{ color: 'primary.500', cursor: 'pointer' }}
color={'myGray.500'}
name={openBaseConfig ? 'core/chat/chevronUp' : 'core/chat/chevronDown'}
onClick={(e) => {
e.preventDefault();
setOpenBaseConfig(!openBaseConfig);
}}
/>
</Flex>
<Flex mt={3} w={'100%'} flexDir={'column'} userSelect={'none'}>
<Flex mt={3} w={'100%'} flexDir={'column'}>
<FormLabel fontSize={'mini'} fontWeight={'500'}>
{t('common:core.dataset.Dataset ID')}
</FormLabel>
@@ -202,7 +190,7 @@ const Info = ({ datasetId }: { datasetId: string }) => {
<FormLabel fontSize={'mini'} fontWeight={'500'}>
{t('common:core.ai.model.Vector Model')}
</FormLabel>
<Box pt={2} flex={[1, '0 0 320px']}>
<Box pt={2}>
<AIModelSelector
w={'100%'}
value={vectorModel.model}
@@ -231,12 +219,10 @@ const Info = ({ datasetId }: { datasetId: string }) => {
</Box>
<Flex mt={2} w={'100%'} alignItems={'center'}>
<FormLabel flex={['0 0 90px', '0 0 160px']} fontSize={'mini'} w={0} fontWeight={'500'}>
<FormLabel flex={1} fontSize={'mini'} w={0} fontWeight={'500'}>
{t('common:core.Max Token')}
</FormLabel>
<Box flex={[1, '0 0 320px']} fontSize={'mini'}>
{vectorModel.maxToken}
</Box>
<Box fontSize={'mini'}>{vectorModel.maxToken}</Box>
</Flex>
<Box pt={5}>
@@ -286,54 +272,33 @@ const Info = ({ datasetId }: { datasetId: string }) => {
{datasetDetail.permission.hasManagePer && (
<>
<MyDivider my={4} h={'2px'} maxW={'500px'} />
<Box overflow={'hidden'} h={openPermissionConfig ? 'auto' : '24px'}>
<Flex justify={'space-between'} alignItems={'center'} fontSize={'mini'} h={'24px'}>
<Box fontWeight={'500'} color={'myGray.900'} userSelect={'none'}>
{t('common:permission.Permission config')}
</Box>
<MyIcon
w={'16px'}
_hover={{ color: 'primary.500', cursor: 'pointer' }}
color={'myGray.500'}
name={openPermissionConfig ? 'core/chat/chevronUp' : 'core/chat/chevronDown'}
onClick={() => setOpenPermissionConfig(!openPermissionConfig)}
/>
</Flex>
<Box mt={3}>
<FormLabel fontWeight={'500'} fontSize={'mini'} pb={3} userSelect={'none'}>
{t('common:permission.Default permission')}
</FormLabel>
</Box>
<Box py={4}>
<MemberManager
managePer={{
mode: 'all',
permission: datasetDetail.permission,
onGetCollaboratorList: () => getCollaboratorList(datasetId),
permissionList: DatasetPermissionList,
onUpdateCollaborators: (body) =>
postUpdateDatasetCollaborators({
...body,
datasetId
}),
onDelOneCollaborator: async ({ groupId, tmbId }) => {
if (tmbId) {
return deleteDatasetCollaborators({
datasetId,
tmbId
});
} else if (groupId) {
return deleteDatasetCollaborators({
datasetId,
groupId
});
}
<Box>
<MemberManager
managePer={{
mode: 'all',
permission: datasetDetail.permission,
onGetCollaboratorList: () => getCollaboratorList(datasetId),
permissionList: DatasetPermissionList,
onUpdateCollaborators: (body) =>
postUpdateDatasetCollaborators({
...body,
datasetId
}),
onDelOneCollaborator: async ({ groupId, tmbId }) => {
if (tmbId) {
return deleteDatasetCollaborators({
datasetId,
tmbId
});
} else if (groupId) {
return deleteDatasetCollaborators({
datasetId,
groupId
});
}
}}
/>
</Box>
}
}}
/>
</Box>
</>
)}

View File

@@ -13,7 +13,6 @@ import { useTranslation } from 'next-i18next';
import React, { useCallback, useState } from 'react';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useRouter } from 'next/router';
import { useForm } from 'react-hook-form';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { getErrText } from '@fastgpt/global/common/error/utils';
@@ -272,31 +271,15 @@ const ExtraPlan = () => {
<MyIcon mr={2} name={'support/bill/shoppingCart'} w={'16px'} color={'primary.600'} />
{t('common:support.wallet.buy_resource')}
</Flex>
{/* <Flex mt={4} alignItems={'center'}>
<Box flex={['0 0 100px', '1 0 0']}>
<Flex mt={4} alignItems={'center'}>
<Box flex={['0 0 100px', '1 0 0']} color={'myGray.600'}>
{t('common:support.wallet.subscription.Month amount')}
</Box>
<Flex alignItems={'center'} mt={1} w={'180px'} position={'relative'}>
<NumberInput size={'sm'} flex={1} step={1} min={1} max={12} position={'relative'}>
<NumberInputField
pr={'30px'}
{...registerExtraPoints('month', {
required: true,
min: 1,
max: 12,
valueAsNumber: true
})}
/>
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
<Box position={'absolute'} right={'20px'} color={'myGray.500'} fontSize={'xs'}>
{t('common:common.month')}
</Box>
<Box>1</Box>
<Box color={'myGray.600'}>{t('common:common.month')}</Box>
</Flex>
</Flex> */}
</Flex>
<Flex mt={4} alignItems={'center'}>
<Box flex={['0 0 100px', '1 0 0']} color={'myGray.600'}>
{t('common:support.wallet.subscription.Update extra ai points')}

View File

@@ -84,21 +84,35 @@ export const useSpeech = (props?: OutLinkChatAuthProps & { appId?: string }) =>
mediaRecorder.current.onstop = async () => {
if (!cancelWhisperSignal.current) {
const formData = new FormData();
let options = {};
const { options, filename } = (() => {
if (MediaRecorder.isTypeSupported('video/webm; codecs=vp9')) {
return {
options: { mimeType: 'video/webm; codecs=vp9' },
filename: 'recording.mp3'
};
}
if (MediaRecorder.isTypeSupported('video/webm')) {
return {
options: { type: 'video/webm' },
filename: 'recording.mp3'
};
}
if (MediaRecorder.isTypeSupported('video/mp4')) {
return {
options: { mimeType: 'video/mp4', videoBitsPerSecond: 100000 },
filename: 'recording.mp4'
};
}
return {
options: { type: 'video/webm' },
filename: 'recording.mp3'
};
})();
if (MediaRecorder.isTypeSupported('video/webm; codecs=vp9')) {
options = { mimeType: 'video/webm; codecs=vp9' };
} else if (MediaRecorder.isTypeSupported('video/webm')) {
options = { type: 'video/webm' };
} else if (MediaRecorder.isTypeSupported('video/mp4')) {
options = { mimeType: 'video/mp4', videoBitsPerSecond: 100000 };
} else {
console.error('no suitable mimetype found for this device');
}
const blob = new Blob(chunks, options);
const duration = Math.round((Date.now() - startTimestamp.current) / 1000);
formData.append('file', blob, 'recording.mp3');
console.log(options, filename, '=-=-');
formData.append('file', blob, filename);
formData.append(
'data',
JSON.stringify({