v4.6.9-alpha (#918)
Co-authored-by: Mufei <327958099@qq.com> Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "app",
|
||||
"version": "4.6.8",
|
||||
"version": "4.6.9",
|
||||
"private": false,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
@@ -33,7 +33,7 @@
|
||||
"formidable": "^2.1.1",
|
||||
"framer-motion": "^9.0.6",
|
||||
"hyperdown": "^2.4.29",
|
||||
"i18next": "^22.5.1",
|
||||
"i18next": "23.10.0",
|
||||
"immer": "^9.0.19",
|
||||
"js-yaml": "^4.1.0",
|
||||
"jschardet": "^3.0.0",
|
||||
@@ -42,13 +42,13 @@
|
||||
"mermaid": "^10.2.3",
|
||||
"nanoid": "^4.0.1",
|
||||
"next": "13.5.2",
|
||||
"next-i18next": "^13.3.0",
|
||||
"next-i18next": "15.2.0",
|
||||
"nprogress": "^0.2.0",
|
||||
"react": "18.2.0",
|
||||
"react-day-picker": "^8.7.1",
|
||||
"react-dom": "18.2.0",
|
||||
"react-hook-form": "^7.43.1",
|
||||
"react-i18next": "^12.3.1",
|
||||
"react-i18next": "13.5.0",
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"reactflow": "^11.7.4",
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
### Fast GPT V4.6.8
|
||||
|
||||
1. 新增 - 知识库搜索合并模块。
|
||||
2. 新增 - 新的 Http 模块,支持更加灵活的参数传入。同时支持了输入输出自动数据类型转化,例如:接口输出的 JSON 类型会自动转成字符串类型,直接给其他模块使用。此外,还补充了一些例子,可在文档中查看。
|
||||
3. 优化 - 问题优化并入知识库搜索模块,无需单独配置。并且问题优化的同时,实现了问题扩展,丰富搜索的语义。(知识库模块会看到有2个参数配置,有一个是多余的,如果想让它消失,可以删除模块,重新增加一个)
|
||||
4. 修复 - 语音输入文件无法上传。
|
||||
5. 修复 - 对话框重新生成无法使用。
|
||||
6. [点击查看高级编排介绍文档](https://doc.fastgpt.in/docs/workflow/intro)
|
||||
7. [使用文档](https://doc.fastgpt.in/docs/intro/)
|
||||
8. [点击查看商业版](https://doc.fastgpt.in/docs/commercial/)
|
||||
1. 新增 - 知识库新增“增强处理”训练模式,可生成更多类型索引。
|
||||
2. 新增 - 完善了HTTP模块的变量提示。
|
||||
3. 新增 - HTTP模块支持OpenAI单接口导入。
|
||||
4. 优化 - 问题补全。增加英文类型。同时可以设置为单独模块,方便复用。
|
||||
5. [点击查看高级编排介绍文档](https://doc.fastgpt.in/docs/workflow/intro)
|
||||
6. [使用文档](https://doc.fastgpt.in/docs/intro/)
|
||||
7. [点击查看商业版](https://doc.fastgpt.in/docs/commercial/)
|
||||
@@ -268,6 +268,7 @@
|
||||
"Quote templates": "Quote templates",
|
||||
"Random": "Random",
|
||||
"Save and preview": "Save",
|
||||
"Search team tags": "Search tags",
|
||||
"Select TTS": "Select TTS",
|
||||
"Select app from template": "Select from the template",
|
||||
"Select quote template": "Select quote template",
|
||||
@@ -278,6 +279,7 @@
|
||||
"Simple Config Tip": "Only basic functions are included. For complex agent functions, use advanced orchestration.",
|
||||
"TTS": "Audio Speech",
|
||||
"TTS Tip": "After this function is enabled, the voice playback function can be used after each conversation. Use of this feature may incur additional charges.",
|
||||
"Team tags": "Team tags",
|
||||
"Temperature": "Temperature",
|
||||
"Welcome Text": "Welcome Text",
|
||||
"create app": "Create App",
|
||||
@@ -409,7 +411,7 @@
|
||||
"Stop Speak": "Stop Speak",
|
||||
"Type a message": "Input problem",
|
||||
"Unpin": "Unpin",
|
||||
"You need to a chat app": "You need to a chat app",
|
||||
"You need to a chat app": "You don't have apps available",
|
||||
"error": {
|
||||
"Chat error": "Chat error",
|
||||
"Messages empty": "Interface content is empty, maybe the text is too long ~",
|
||||
@@ -468,7 +470,7 @@
|
||||
"module similarity": "Similarity",
|
||||
"module temperature": "Temperature",
|
||||
"module time": "Running Time",
|
||||
"module tokens": "Tokens",
|
||||
"module tokens": "Total Tokens",
|
||||
"plugin output": "Plugin Output",
|
||||
"search using reRank": "ReRank",
|
||||
"text output": "Text Output"
|
||||
@@ -594,6 +596,7 @@
|
||||
"file": "File",
|
||||
"folder": "Folder",
|
||||
"import": {
|
||||
"Auto mode Estimated Price Tips": "Enhanced processing calls the file processing model: {{price}} integral /1k Tokens",
|
||||
"Auto process": "Auto",
|
||||
"Auto process desc": "Automatically set segmentation and preprocessing rules",
|
||||
"CSV Import": "CSV QA Import",
|
||||
@@ -615,9 +618,9 @@
|
||||
"Data file progress": "Data upload progress",
|
||||
"Data process params": "Data process params",
|
||||
"Down load csv template": "Down load csv template",
|
||||
"Embedding Estimated Price Tips": "Index billing: {{price}}/1k chars",
|
||||
"Embedding Estimated Price Tips": "Index billing: {{price}}/1k Tokens",
|
||||
"Estimated Price": "Estimated Price: : {{amount}}{{unit}}",
|
||||
"Estimated Price Tips": "QA charges\nInput: 1k chars={{charsPointsPrice}} points",
|
||||
"Estimated Price Tips": "QA charges: {{charsPointsPrice}} points/1k Tokens",
|
||||
"Estimated points": "About {{points}} points",
|
||||
"Fetch Error": "Get link failed",
|
||||
"Fetch Url": "Url",
|
||||
@@ -638,7 +641,7 @@
|
||||
"Preview chunks": "Chunks",
|
||||
"Preview raw text": "Preview file text (max show 10000 words)",
|
||||
"Process way": "Process way",
|
||||
"QA Estimated Price Tips": "QA billing: {{price}}/1k characters (including input and output)",
|
||||
"QA Estimated Price Tips": "QA billing: {{price}}/1k Tokens (including input and output)",
|
||||
"QA Import": "QA Split",
|
||||
"QA Import Tip": "According to certain rules, the text is broken into a larger paragraph, and the AI is invoked to generate a question and answer pair for the paragraph.",
|
||||
"Re Preview": "RePreview",
|
||||
@@ -737,6 +740,8 @@
|
||||
},
|
||||
"training": {
|
||||
"Agent queue": "QA wait list",
|
||||
"Auto mode": "Enhancement process",
|
||||
"Auto mode Tip": "Subindex and call model are used to generate relevant questions and abstracts to increase the semantic richness of data blocks and facilitate retrieval. It consumes more storage space and increases the number of AI calls.",
|
||||
"Chunk mode": "Chunk split",
|
||||
"Full": "Expect more than 5 minutes",
|
||||
"Leisure": "Leisure",
|
||||
@@ -1217,13 +1222,33 @@
|
||||
"Sending Code": "Sending"
|
||||
},
|
||||
"login": {
|
||||
"And": "&",
|
||||
"Email": "Email",
|
||||
"Forget Password": "Forget Password?",
|
||||
"Github": "Github",
|
||||
"Google": "Google",
|
||||
"Provider error": "Login exception, please try again"
|
||||
"Password": "Password",
|
||||
"Password login": "Password login",
|
||||
"Phone": "Phone Login",
|
||||
"Phone number": "Phone",
|
||||
"Policy tip": "By using it, you agree with us",
|
||||
"Privacy": "Privacy",
|
||||
"Provider error": "Login exception, please try again",
|
||||
"Register": "Register",
|
||||
"Root login": "Log in as the root user",
|
||||
"Root password placeholder": "root password is the environment variable you set",
|
||||
"Terms": "Terms",
|
||||
"Username": "Username",
|
||||
"Wechat": "Wechat",
|
||||
"Wx qr login": "Wechat scan code login"
|
||||
},
|
||||
"team": {
|
||||
"Dataset usage": "Dataset usage",
|
||||
"member": "Member"
|
||||
"Team Tags Async Success": "Team async success",
|
||||
"member": "Member",
|
||||
"tag": {
|
||||
"Have not opened": "Team chat is not enabled"
|
||||
}
|
||||
}
|
||||
},
|
||||
"wallet": {
|
||||
|
||||
@@ -268,6 +268,7 @@
|
||||
"Quote templates": "引用内容模板",
|
||||
"Random": "发散",
|
||||
"Save and preview": "保存并预览",
|
||||
"Search team tags": "搜索标签",
|
||||
"Select TTS": "选择语音播放模式",
|
||||
"Select app from template": "从模板中选择",
|
||||
"Select quote template": "选择引用提示模板",
|
||||
@@ -278,6 +279,7 @@
|
||||
"Simple Config Tip": "仅包含基础功能,复杂 agent 功能请使用高级编排。",
|
||||
"TTS": "语音播报",
|
||||
"TTS Tip": "开启后,每次对话后可使用语音播放功能。使用该功能可能产生额外费用。",
|
||||
"Team tags": "团队标签",
|
||||
"Temperature": "温度",
|
||||
"Welcome Text": "对话开场白",
|
||||
"create app": "创建属于你的 AI 应用",
|
||||
@@ -409,7 +411,7 @@
|
||||
"Stop Speak": "停止录音",
|
||||
"Type a message": "输入问题",
|
||||
"Unpin": "取消置顶",
|
||||
"You need to a chat app": "鉴权失败,暂无权限访问应用",
|
||||
"You need to a chat app": "你没有可用的应用",
|
||||
"error": {
|
||||
"Chat error": "对话出现异常",
|
||||
"Messages empty": "接口内容为空,可能文本超长了~",
|
||||
@@ -468,7 +470,7 @@
|
||||
"module similarity": "相似度",
|
||||
"module temperature": "温度",
|
||||
"module time": "运行时长",
|
||||
"module tokens": "Tokens",
|
||||
"module tokens": "总Tokens",
|
||||
"plugin output": "插件输出值",
|
||||
"search using reRank": "结果重排",
|
||||
"text output": "文本输出"
|
||||
@@ -596,13 +598,14 @@
|
||||
"file": "文件",
|
||||
"folder": "目录",
|
||||
"import": {
|
||||
"Auto mode Estimated Price Tips": "增强处理需调用文件处理模型: {{price}}积分/1k Tokens",
|
||||
"Auto process": "自动",
|
||||
"Auto process desc": "自动设置分割和预处理规则",
|
||||
"CSV Import": "CSV 导入",
|
||||
"CSV Import Tip": "通过批量导入问答对,要求提前整理好数据",
|
||||
"Chunk Range": "范围: {{min}}~{{max}}",
|
||||
"Chunk Split": "直接分段",
|
||||
"Chunk Split Tip": "将文本按一定的规则进行分段处理后,转成可进行语义搜索的格式,适合绝大多数场景。",
|
||||
"Chunk Split Tip": "将文本按一定的规则进行分段处理后,转成可进行语义搜索的格式,适合绝大多数场景。不需要调用模型额外处理,成本低。",
|
||||
"Chunk length": "分块总量",
|
||||
"Csv format error": "csv 文件格式有误,请确保 index 和 content 两列",
|
||||
"Custom file": "自定义文本",
|
||||
@@ -617,9 +620,9 @@
|
||||
"Data file progress": "数据上传进度",
|
||||
"Data process params": "数据处理参数",
|
||||
"Down load csv template": "点击下载 CSV 模板",
|
||||
"Embedding Estimated Price Tips": "索引计费: {{price}}积分/1k字符",
|
||||
"Embedding Estimated Price Tips": "索引计费: {{price}}积分/1k Tokens",
|
||||
"Estimated Price": "预估价格: {{amount}}{{unit}}",
|
||||
"Estimated Price Tips": "QA计费为\n输入: 1k字符 = {{charsPointsPrice}}积分",
|
||||
"Estimated Price Tips": "QA计费为\n输入: {{charsPointsPrice}}积分/1k Tokens",
|
||||
"Estimated points": "预估消耗 {{points}} 积分",
|
||||
"Fetch Error": "获取链接失败",
|
||||
"Fetch Url": "网络链接",
|
||||
@@ -640,9 +643,9 @@
|
||||
"Preview chunks": "分段预览",
|
||||
"Preview raw text": "预览源文本(最多展示10000字)",
|
||||
"Process way": "处理方式",
|
||||
"QA Estimated Price Tips": "QA计费为: {{price}}积分/1k 字符(包含输入和输出)",
|
||||
"QA Estimated Price Tips": "QA计费为: {{price}}积分/1k Tokens(包含输入和输出)",
|
||||
"QA Import": "QA拆分",
|
||||
"QA Import Tip": "根据一定规则,将文本拆成一段较大的段落,调用 AI 为该段落生成问答对。",
|
||||
"QA Import Tip": "根据一定规则,将文本拆成一段较大的段落,调用 AI 为该段落生成问答对。有非常高的检索精度,但是会丢失很多内容细节。",
|
||||
"Re Preview": "重新生成预览",
|
||||
"Select file": "选择文件",
|
||||
"Select source": "选择来源",
|
||||
@@ -739,6 +742,8 @@
|
||||
},
|
||||
"training": {
|
||||
"Agent queue": "QA训练排队",
|
||||
"Auto mode": "增强处理(实验)",
|
||||
"Auto mode Tip": "通过子索引以及调用模型生成相关问题与摘要,来增加数据块的语义丰富度,更利于检索。需要消耗更多的存储空间和增加AI调用次数。",
|
||||
"Chunk mode": "直接分段",
|
||||
"Full": "预计5分钟以上",
|
||||
"Leisure": "空闲",
|
||||
@@ -1219,13 +1224,33 @@
|
||||
"Sending Code": "正在发送"
|
||||
},
|
||||
"login": {
|
||||
"And": "和",
|
||||
"Email": "邮箱",
|
||||
"Forget Password": "忘记密码?",
|
||||
"Github": "Github 登录",
|
||||
"Google": "Google 登录",
|
||||
"Provider error": "登录异常,请重试"
|
||||
"Password": "密码",
|
||||
"Password login": "密码登录",
|
||||
"Phone": "手机号登录",
|
||||
"Phone number": "手机号",
|
||||
"Policy tip": "使用即代表你同意我们的",
|
||||
"Privacy": "隐私政策",
|
||||
"Provider error": "登录异常,请重试",
|
||||
"Register": "注册账号",
|
||||
"Root login": "使用root用户登录",
|
||||
"Root password placeholder": "root密码为你设置的环境变量",
|
||||
"Terms": "服务协议",
|
||||
"Username": "用户名",
|
||||
"Wechat": "微信登录",
|
||||
"Wx qr login": "微信扫码登录"
|
||||
},
|
||||
"team": {
|
||||
"Dataset usage": "知识库容量",
|
||||
"member": "成员"
|
||||
"Team Tags Async Success": "同步完成",
|
||||
"member": "成员",
|
||||
"tag": {
|
||||
"Have not opened": "未开通团队聊天功能"
|
||||
}
|
||||
}
|
||||
},
|
||||
"wallet": {
|
||||
|
||||
@@ -13,6 +13,7 @@ import { IMG_BLOCK_KEY } from '@fastgpt/global/core/chat/constants';
|
||||
import { addDays } from 'date-fns';
|
||||
import { useRequest } from '@/web/common/hooks/useRequest';
|
||||
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
|
||||
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6);
|
||||
|
||||
enum FileTypeEnum {
|
||||
@@ -35,8 +36,12 @@ const MessageInput = ({
|
||||
isChatting,
|
||||
TextareaDom,
|
||||
showFileSelector = false,
|
||||
resetInputVal
|
||||
}: {
|
||||
resetInputVal,
|
||||
shareId,
|
||||
outLinkUid,
|
||||
teamId,
|
||||
teamToken
|
||||
}: OutLinkChatAuthProps & {
|
||||
onChange?: (e: string) => void;
|
||||
onSendMessage: (e: string) => void;
|
||||
onStop: () => void;
|
||||
@@ -47,7 +52,6 @@ const MessageInput = ({
|
||||
}) => {
|
||||
const [, startSts] = useTransition();
|
||||
|
||||
const { shareId } = useRouter().query as { shareId?: string };
|
||||
const {
|
||||
isSpeaking,
|
||||
isTransCription,
|
||||
@@ -56,7 +60,7 @@ const MessageInput = ({
|
||||
speakingTimeString,
|
||||
renderAudioGraph,
|
||||
stream
|
||||
} = useSpeech({ shareId });
|
||||
} = useSpeech({ shareId, outLinkUid, teamId, teamToken });
|
||||
const { isPc } = useSystemStore();
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
const { t } = useTranslation();
|
||||
@@ -82,7 +86,10 @@ const MessageInput = ({
|
||||
maxSize: 1024 * 1024 * 5,
|
||||
// 30 day expired.
|
||||
expiredTime: addDays(new Date(), 7),
|
||||
shareId
|
||||
shareId,
|
||||
outLinkUid,
|
||||
teamId,
|
||||
teamToken
|
||||
});
|
||||
setFileList((state) =>
|
||||
state.map((item) =>
|
||||
@@ -320,7 +327,7 @@ ${images.map((img) => JSON.stringify({ src: img.src })).join('\n')}
|
||||
rows={1}
|
||||
height={'22px'}
|
||||
lineHeight={'22px'}
|
||||
maxHeight={'150px'}
|
||||
maxHeight={'50vh'}
|
||||
maxLength={-1}
|
||||
overflowY={'auto'}
|
||||
whiteSpace={'pre-wrap'}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { ModalBody, Box, useTheme } from '@chakra-ui/react';
|
||||
|
||||
import MyModal from '../MyModal';
|
||||
@@ -10,12 +10,12 @@ import RawSourceBox from '../core/dataset/RawSourceBox';
|
||||
const QuoteModal = ({
|
||||
rawSearch = [],
|
||||
onClose,
|
||||
isShare,
|
||||
showDetail,
|
||||
metadata
|
||||
}: {
|
||||
rawSearch: SearchDataResponseItemType[];
|
||||
onClose: () => void;
|
||||
isShare: boolean;
|
||||
showDetail: boolean;
|
||||
metadata?: {
|
||||
collectionId: string;
|
||||
sourceId?: string;
|
||||
@@ -57,7 +57,7 @@ const QuoteModal = ({
|
||||
}
|
||||
>
|
||||
<ModalBody>
|
||||
<QuoteList rawSearch={filterResults} isShare={isShare} />
|
||||
<QuoteList rawSearch={filterResults} showDetail={showDetail} />
|
||||
</ModalBody>
|
||||
</MyModal>
|
||||
</>
|
||||
@@ -68,10 +68,10 @@ export default QuoteModal;
|
||||
|
||||
export const QuoteList = React.memo(function QuoteList({
|
||||
rawSearch = [],
|
||||
isShare
|
||||
showDetail
|
||||
}: {
|
||||
rawSearch: SearchDataResponseItemType[];
|
||||
isShare: boolean;
|
||||
showDetail: boolean;
|
||||
}) {
|
||||
const theme = useTheme();
|
||||
|
||||
@@ -88,7 +88,7 @@ export const QuoteList = React.memo(function QuoteList({
|
||||
_hover={{ '& .hover-data': { display: 'flex' } }}
|
||||
bg={i % 2 === 0 ? 'white' : 'myWhite.500'}
|
||||
>
|
||||
<QuoteItem quoteItem={item} canViewSource={!isShare} linkToDataset={!isShare} />
|
||||
<QuoteItem quoteItem={item} canViewSource={showDetail} linkToDataset={showDetail} />
|
||||
</Box>
|
||||
))}
|
||||
</>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type.d';
|
||||
import type { ChatItemType } from '@fastgpt/global/core/chat/type';
|
||||
import { Flex, BoxProps, useDisclosure, Image, useTheme, Box } from '@chakra-ui/react';
|
||||
import { Flex, BoxProps, useDisclosure, useTheme, Box } from '@chakra-ui/react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
|
||||
@@ -20,10 +20,10 @@ const WholeResponseModal = dynamic(() => import('./WholeResponseModal'), { ssr:
|
||||
|
||||
const ResponseTags = ({
|
||||
responseData = [],
|
||||
isShare
|
||||
showDetail
|
||||
}: {
|
||||
responseData?: ChatHistoryItemResType[];
|
||||
isShare: boolean;
|
||||
showDetail: boolean;
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const { isPc } = useSystemStore();
|
||||
@@ -76,13 +76,13 @@ const ResponseTags = ({
|
||||
sourceName: item.sourceName,
|
||||
sourceId: item.sourceId,
|
||||
icon: getSourceNameIcon({ sourceId: item.sourceId, sourceName: item.sourceName }),
|
||||
canReadQuote: !isShare || strIsLink(item.sourceId),
|
||||
canReadQuote: showDetail || strIsLink(item.sourceId),
|
||||
collectionId: item.collectionId
|
||||
})),
|
||||
historyPreview: chatData?.historyPreview,
|
||||
runningTime: +responseData.reduce((sum, item) => sum + (item.runningTime || 0), 0).toFixed(2)
|
||||
};
|
||||
}, [isShare, responseData]);
|
||||
}, [showDetail, responseData]);
|
||||
|
||||
const TagStyles: BoxProps = {
|
||||
mr: 2,
|
||||
@@ -134,7 +134,7 @@ const ResponseTags = ({
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
{!isShare && (
|
||||
{showDetail && (
|
||||
<Flex alignItems={'center'} mt={3} flexWrap={'wrap'}>
|
||||
{quoteList.length > 0 && (
|
||||
<MyTooltip label="查看引用">
|
||||
@@ -187,7 +187,7 @@ const ResponseTags = ({
|
||||
{!!quoteModalData && (
|
||||
<QuoteModal
|
||||
{...quoteModalData}
|
||||
isShare={isShare}
|
||||
showDetail={showDetail}
|
||||
onClose={() => setQuoteModalData(undefined)}
|
||||
/>
|
||||
)}
|
||||
@@ -195,7 +195,11 @@ const ResponseTags = ({
|
||||
<ContextModal context={contextModalData} onClose={() => setContextModalData(undefined)} />
|
||||
)}
|
||||
{isOpenWholeModal && (
|
||||
<WholeResponseModal response={responseData} isShare={isShare} onClose={onCloseWholeModal} />
|
||||
<WholeResponseModal
|
||||
response={responseData}
|
||||
showDetail={showDetail}
|
||||
onClose={onCloseWholeModal}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -51,11 +51,11 @@ function Row({
|
||||
|
||||
const WholeResponseModal = ({
|
||||
response,
|
||||
isShare,
|
||||
showDetail,
|
||||
onClose
|
||||
}: {
|
||||
response: ChatHistoryItemResType[];
|
||||
isShare: boolean;
|
||||
showDetail: boolean;
|
||||
onClose: () => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -78,7 +78,7 @@ const WholeResponseModal = ({
|
||||
}
|
||||
>
|
||||
<Flex h={'100%'} flexDirection={'column'}>
|
||||
<ResponseBox response={response} isShare={isShare} />
|
||||
<ResponseBox response={response} showDetail={showDetail} />
|
||||
</Flex>
|
||||
</MyModal>
|
||||
);
|
||||
@@ -88,10 +88,10 @@ export default WholeResponseModal;
|
||||
|
||||
const ResponseBox = React.memo(function ResponseBox({
|
||||
response,
|
||||
isShare
|
||||
showDetail
|
||||
}: {
|
||||
response: ChatHistoryItemResType[];
|
||||
isShare: boolean;
|
||||
showDetail: boolean;
|
||||
}) {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
@@ -142,10 +142,7 @@ const ResponseBox = React.memo(function ResponseBox({
|
||||
value={`${activeModule?.runningTime || 0}s`}
|
||||
/>
|
||||
<Row label={t('core.chat.response.module model')} value={activeModule?.model} />
|
||||
<Row
|
||||
label={t('support.wallet.usage.Chars length')}
|
||||
value={`${activeModule?.charsLength}`}
|
||||
/>
|
||||
<Row label={t('core.chat.response.module tokens')} value={`${activeModule?.tokens}`} />
|
||||
<Row label={t('core.chat.response.module query')} value={activeModule?.query} />
|
||||
<Row
|
||||
label={t('core.chat.response.context total length')}
|
||||
@@ -188,7 +185,7 @@ const ResponseBox = React.memo(function ResponseBox({
|
||||
{activeModule.quoteList && activeModule.quoteList.length > 0 && (
|
||||
<Row
|
||||
label={t('core.chat.response.module quoteList')}
|
||||
rawDom={<QuoteList isShare={isShare} rawSearch={activeModule.quoteList} />}
|
||||
rawDom={<QuoteList showDetail={showDetail} rawSearch={activeModule.quoteList} />}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
@@ -280,7 +277,7 @@ const ResponseBox = React.memo(function ResponseBox({
|
||||
{activeModule?.pluginDetail && activeModule?.pluginDetail.length > 0 && (
|
||||
<Row
|
||||
label={t('core.chat.response.Plugin Resonse Detail')}
|
||||
rawDom={<ResponseBox response={activeModule.pluginDetail} isShare={isShare} />}
|
||||
rawDom={<ResponseBox response={activeModule.pluginDetail} showDetail={showDetail} />}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -68,6 +68,7 @@ import type { AppTTSConfigType, VariableItemType } from '@fastgpt/global/core/mo
|
||||
import MessageInput from './MessageInput';
|
||||
import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
|
||||
import ChatBoxDivider from '../core/chat/Divider';
|
||||
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
|
||||
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 24);
|
||||
|
||||
@@ -106,7 +107,7 @@ const MessageCardStyle: BoxProps = {
|
||||
maxW: ['calc(100% - 25px)', 'calc(100% - 40px)']
|
||||
};
|
||||
|
||||
type Props = {
|
||||
type Props = OutLinkChatAuthProps & {
|
||||
feedbackType?: `${FeedbackTypeEnum}`;
|
||||
showMarkIcon?: boolean; // admin mark dataset
|
||||
showVoiceIcon?: boolean;
|
||||
@@ -120,9 +121,6 @@ type Props = {
|
||||
// not chat test params
|
||||
appId?: string;
|
||||
chatId?: string;
|
||||
shareId?: string;
|
||||
shareTeamId?: string;
|
||||
outLinkUid?: string;
|
||||
|
||||
onUpdateVariable?: (e: Record<string, any>) => void;
|
||||
onStartChat?: (e: StartChatFnProps) => Promise<{
|
||||
@@ -147,8 +145,9 @@ const ChatBox = (
|
||||
appId,
|
||||
chatId,
|
||||
shareId,
|
||||
shareTeamId,
|
||||
outLinkUid,
|
||||
teamId,
|
||||
teamToken,
|
||||
onUpdateVariable,
|
||||
onStartChat,
|
||||
onDelMessage
|
||||
@@ -288,7 +287,10 @@ const ChatBox = (
|
||||
const result = await postQuestionGuide(
|
||||
{
|
||||
messages: adaptChat2GptMessages({ messages: history, reserveId: false }).slice(-6),
|
||||
shareId
|
||||
shareId,
|
||||
outLinkUid,
|
||||
teamId,
|
||||
teamToken
|
||||
},
|
||||
abortSignal
|
||||
);
|
||||
@@ -300,7 +302,7 @@ const ChatBox = (
|
||||
}
|
||||
} catch (error) {}
|
||||
},
|
||||
[questionGuide, shareId]
|
||||
[questionGuide, shareId, outLinkUid, teamId, teamToken]
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -398,22 +400,20 @@ const ChatBox = (
|
||||
};
|
||||
})
|
||||
);
|
||||
if (!shareTeamId) {
|
||||
setTimeout(() => {
|
||||
createQuestionGuide({
|
||||
history: newChatList.map((item, i) =>
|
||||
i === newChatList.length - 1
|
||||
? {
|
||||
...item,
|
||||
value: responseText
|
||||
}
|
||||
: item
|
||||
)
|
||||
});
|
||||
generatingScroll();
|
||||
isPc && TextareaDom.current?.focus();
|
||||
}, 100);
|
||||
}
|
||||
setTimeout(() => {
|
||||
createQuestionGuide({
|
||||
history: newChatList.map((item, i) =>
|
||||
i === newChatList.length - 1
|
||||
? {
|
||||
...item,
|
||||
value: responseText
|
||||
}
|
||||
: item
|
||||
)
|
||||
});
|
||||
generatingScroll();
|
||||
isPc && TextareaDom.current?.focus();
|
||||
}, 100);
|
||||
} catch (err: any) {
|
||||
toast({
|
||||
title: t(getErrText(err, 'core.chat.error.Chat error')),
|
||||
@@ -622,6 +622,7 @@ const ChatBox = (
|
||||
{/* control icon */}
|
||||
<Flex w={'100%'} alignItems={'center'} justifyContent={'flex-end'}>
|
||||
<ChatControllerComponent
|
||||
isChatting={isChatting}
|
||||
chat={item}
|
||||
onDelete={
|
||||
onDelMessage
|
||||
@@ -654,12 +655,17 @@ const ChatBox = (
|
||||
<ChatAvatar src={appAvatar} type={'AI'} />
|
||||
{/* control icon */}
|
||||
<ChatControllerComponent
|
||||
isChatting={isChatting}
|
||||
ml={2}
|
||||
chat={item}
|
||||
setChatHistory={setChatHistory}
|
||||
display={index === chatHistory.length - 1 && isChatting ? 'none' : 'flex'}
|
||||
showVoiceIcon={showVoiceIcon}
|
||||
ttsConfig={ttsConfig}
|
||||
shareId={shareId}
|
||||
outLinkUid={outLinkUid}
|
||||
teamId={teamId}
|
||||
teamToken={teamToken}
|
||||
onDelete={
|
||||
onDelMessage
|
||||
? () => {
|
||||
@@ -829,7 +835,10 @@ const ChatBox = (
|
||||
isChatting={index === chatHistory.length - 1 && isChatting}
|
||||
/>
|
||||
|
||||
<ResponseTags responseData={item.responseData} isShare={!!shareId} />
|
||||
<ResponseTags
|
||||
responseData={item.responseData}
|
||||
showDetail={!shareId && !teamId}
|
||||
/>
|
||||
|
||||
{/* custom feedback */}
|
||||
{item.customFeedbacks && item.customFeedbacks.length > 0 && (
|
||||
@@ -909,6 +918,10 @@ const ChatBox = (
|
||||
TextareaDom={TextareaDom}
|
||||
resetInputVal={resetInputVal}
|
||||
showFileSelector={showFileSelector}
|
||||
shareId={shareId}
|
||||
outLinkUid={outLinkUid}
|
||||
teamId={teamId}
|
||||
teamToken={teamToken}
|
||||
/>
|
||||
)}
|
||||
{/* user feedback modal */}
|
||||
@@ -1236,6 +1249,7 @@ function Empty() {
|
||||
}
|
||||
|
||||
const ChatControllerComponent = React.memo(function ChatControllerComponent({
|
||||
isChatting,
|
||||
chat,
|
||||
setChatHistory,
|
||||
display,
|
||||
@@ -1249,8 +1263,13 @@ const ChatControllerComponent = React.memo(function ChatControllerComponent({
|
||||
onAddUserDislike,
|
||||
onAddUserLike,
|
||||
ml,
|
||||
mr
|
||||
}: {
|
||||
mr,
|
||||
shareId,
|
||||
outLinkUid,
|
||||
teamId,
|
||||
teamToken
|
||||
}: OutLinkChatAuthProps & {
|
||||
isChatting: boolean;
|
||||
chat: ChatSiteItemType;
|
||||
setChatHistory?: React.Dispatch<React.SetStateAction<ChatSiteItemType[]>>;
|
||||
showVoiceIcon?: boolean;
|
||||
@@ -1267,7 +1286,11 @@ const ChatControllerComponent = React.memo(function ChatControllerComponent({
|
||||
const { t } = useTranslation();
|
||||
const { copyData } = useCopyData();
|
||||
const { audioLoading, audioPlaying, hasAudio, playAudio, cancelAudio } = useAudioPlay({
|
||||
ttsConfig
|
||||
ttsConfig,
|
||||
shareId,
|
||||
outLinkUid,
|
||||
teamId,
|
||||
teamToken
|
||||
});
|
||||
const controlIconStyle = {
|
||||
w: '14px',
|
||||
@@ -1296,7 +1319,7 @@ const ChatControllerComponent = React.memo(function ChatControllerComponent({
|
||||
onClick={() => copyData(chat.value)}
|
||||
/>
|
||||
</MyTooltip>
|
||||
{!!onDelete && (
|
||||
{!!onDelete && !isChatting && (
|
||||
<>
|
||||
{onRetry && (
|
||||
<MyTooltip label={t('core.chat.retry')}>
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuList,
|
||||
MenuItemOption,
|
||||
MenuOptionGroup,
|
||||
Flex,
|
||||
TagLabel,
|
||||
TagCloseButton,
|
||||
HStack,
|
||||
Tag,
|
||||
Input
|
||||
} from '@chakra-ui/react';
|
||||
import type { TeamTagsSchema } from '@fastgpt/global/support/user/team/type';
|
||||
const TagEdit = ({
|
||||
defaultValues,
|
||||
teamsTags,
|
||||
setSelectedTags
|
||||
}: {
|
||||
defaultValues: [];
|
||||
teamsTags: Array<TeamTagsSchema>;
|
||||
setSelectedTags: (item: Array<string>) => void;
|
||||
}) => {
|
||||
const [teamTagsOptions, setTeamTagsOptions] = useState(teamsTags);
|
||||
const setSelectTeamsTags = (item: any) => {
|
||||
setSelectedTags(item);
|
||||
};
|
||||
useMemo(() => {
|
||||
setTeamTagsOptions(teamsTags);
|
||||
}, [teamsTags]);
|
||||
return (
|
||||
<>
|
||||
<Menu closeOnSelect={false}>
|
||||
<MenuButton className="menu-btn" maxHeight={'250'} minWidth={'80%'}>
|
||||
<HStack
|
||||
style={{
|
||||
border: 'solid 2px #f3f3f3',
|
||||
borderRadius: '5px',
|
||||
padding: '3px',
|
||||
|
||||
flexWrap: 'wrap',
|
||||
minHeight: '40px'
|
||||
}}
|
||||
>
|
||||
{teamsTags.map((item: TeamTagsSchema, index: number) => {
|
||||
const key: string = item?.key;
|
||||
if (defaultValues.indexOf(key as never) > -1) {
|
||||
return (
|
||||
<Tag
|
||||
key={index}
|
||||
size={'md'}
|
||||
colorScheme="red"
|
||||
// maxWidth={"100px"}
|
||||
borderRadius="full"
|
||||
>
|
||||
<TagLabel> {item.label}</TagLabel>
|
||||
<TagCloseButton />
|
||||
</Tag>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</HStack>
|
||||
</MenuButton>
|
||||
<MenuList style={{ height: '300px', overflow: 'scroll' }}>
|
||||
<Input
|
||||
style={{ border: 'none', borderBottom: 'solid 1px #f6f6f6' }}
|
||||
placeholder="pleace "
|
||||
onChange={(e: any) => {
|
||||
// 对用户输入的搜索文本进行小写转换,以实现不区分大小写的搜索
|
||||
const searchLower: string = e?.nativeEvent?.data || '';
|
||||
// 使用filter方法来过滤列表,只返回包含搜索文本的项
|
||||
const resultList = teamsTags.filter((item) => {
|
||||
const searchValue = item.label || '';
|
||||
// 对列表中的每一项也进行小写转换
|
||||
return searchValue.includes(searchLower);
|
||||
});
|
||||
!searchLower ? setTeamTagsOptions(teamsTags) : setTeamTagsOptions(resultList);
|
||||
}}
|
||||
/>
|
||||
<MenuOptionGroup
|
||||
defaultValue={defaultValues}
|
||||
type="checkbox"
|
||||
style={{ height: '300px', overflow: 'scroll' }}
|
||||
onChange={(e) => {
|
||||
setSelectTeamsTags(e);
|
||||
}}
|
||||
>
|
||||
{teamTagsOptions.map((item, index) => {
|
||||
return (
|
||||
<MenuItemOption key={index} value={item.key}>
|
||||
{item?.label}
|
||||
</MenuItemOption>
|
||||
);
|
||||
})}
|
||||
</MenuOptionGroup>
|
||||
</MenuList>
|
||||
</Menu>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default TagEdit;
|
||||
@@ -8,7 +8,7 @@ import { DatasetTypeMap } from '@fastgpt/global/core/dataset/constants';
|
||||
const DatasetTypeTag = ({ type, ...props }: { type: `${DatasetTypeEnum}` } & FlexProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const item = DatasetTypeMap[type];
|
||||
const item = DatasetTypeMap[type] || DatasetTypeMap['dataset'];
|
||||
|
||||
return (
|
||||
<Flex
|
||||
|
||||
@@ -301,10 +301,22 @@ function RenderHttpProps({
|
||||
headers &&
|
||||
jsonBody &&
|
||||
{
|
||||
[TabEnum.params]: <RenderForm moduleId={moduleId} input={params} variables={variables} />,
|
||||
[TabEnum.params]: (
|
||||
<RenderForm
|
||||
moduleId={moduleId}
|
||||
input={params}
|
||||
variables={variables}
|
||||
tabType={TabEnum.params}
|
||||
/>
|
||||
),
|
||||
[TabEnum.body]: <RenderJson moduleId={moduleId} variables={variables} input={jsonBody} />,
|
||||
[TabEnum.headers]: (
|
||||
<RenderForm moduleId={moduleId} input={headers} variables={variables} />
|
||||
<RenderForm
|
||||
moduleId={moduleId}
|
||||
input={headers}
|
||||
variables={variables}
|
||||
tabType={TabEnum.headers}
|
||||
/>
|
||||
)
|
||||
}[selectedTab]}
|
||||
</Box>
|
||||
@@ -313,11 +325,13 @@ function RenderHttpProps({
|
||||
const RenderForm = ({
|
||||
moduleId,
|
||||
input,
|
||||
variables
|
||||
variables,
|
||||
tabType
|
||||
}: {
|
||||
moduleId: string;
|
||||
input: FlowNodeInputItemType;
|
||||
variables: EditorVariablePickerType[];
|
||||
tabType?: TabEnum;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
@@ -327,11 +341,52 @@ const RenderForm = ({
|
||||
const [shouldUpdateNode, setShouldUpdateNode] = useState(false);
|
||||
|
||||
const leftVariables = useMemo(() => {
|
||||
return variables.filter((variable) => {
|
||||
const HttpHeaders = [
|
||||
{ key: 'A-IM', label: 'A-IM' },
|
||||
{ key: 'Accept', label: 'Accept' },
|
||||
{ key: 'Accept-Charset', label: 'Accept-Charset' },
|
||||
{ key: 'Accept-Encoding', label: 'Accept-Encoding' },
|
||||
{ key: 'Accept-Language', label: 'Accept-Language' },
|
||||
{ key: 'Accept-Datetime', label: 'Accept-Datetime' },
|
||||
{ key: 'Access-Control-Request-Method', label: 'Access-Control-Request-Method' },
|
||||
{ key: 'Access-Control-Request-Headers', label: 'Access-Control-Request-Headers' },
|
||||
{ key: 'Authorization', label: 'Authorization' },
|
||||
{ key: 'Cache-Control', label: 'Cache-Control' },
|
||||
{ key: 'Connection', label: 'Connection' },
|
||||
{ key: 'Content-Length', label: 'Content-Length' },
|
||||
{ key: 'Content-Type', label: 'Content-Type' },
|
||||
{ key: 'Cookie', label: 'Cookie' },
|
||||
{ key: 'Date', label: 'Date' },
|
||||
{ key: 'Expect', label: 'Expect' },
|
||||
{ key: 'Forwarded', label: 'Forwarded' },
|
||||
{ key: 'From', label: 'From' },
|
||||
{ key: 'Host', label: 'Host' },
|
||||
{ key: 'If-Match', label: 'If-Match' },
|
||||
{ key: 'If-Modified-Since', label: 'If-Modified-Since' },
|
||||
{ key: 'If-None-Match', label: 'If-None-Match' },
|
||||
{ key: 'If-Range', label: 'If-Range' },
|
||||
{ key: 'If-Unmodified-Since', label: 'If-Unmodified-Since' },
|
||||
{ key: 'Max-Forwards', label: 'Max-Forwards' },
|
||||
{ key: 'Origin', label: 'Origin' },
|
||||
{ key: 'Pragma', label: 'Pragma' },
|
||||
{ key: 'Proxy-Authorization', label: 'Proxy-Authorization' },
|
||||
{ key: 'Range', label: 'Range' },
|
||||
{ key: 'Referer', label: 'Referer' },
|
||||
{ key: 'TE', label: 'TE' },
|
||||
{ key: 'User-Agent', label: 'User-Agent' },
|
||||
{ key: 'Upgrade', label: 'Upgrade' },
|
||||
{ key: 'Via', label: 'Via' },
|
||||
{ key: 'Warning', label: 'Warning' },
|
||||
{ key: 'Dnt', label: 'Dnt' },
|
||||
{ key: 'X-Requested-With', label: 'X-Requested-With' },
|
||||
{ key: 'X-CSRF-Token', label: 'X-CSRF-Token' }
|
||||
];
|
||||
|
||||
return (tabType === TabEnum.headers ? HttpHeaders : variables).filter((variable) => {
|
||||
const existVariables = list.map((item) => item.key);
|
||||
return !existVariables.includes(variable.key);
|
||||
});
|
||||
}, [list, variables]);
|
||||
}, [list, tabType, variables]);
|
||||
|
||||
useEffect(() => {
|
||||
setList(input.value || []);
|
||||
@@ -378,16 +433,23 @@ const RenderForm = ({
|
||||
};
|
||||
|
||||
const handleAddNewProps = (key: string, value: string = '') => {
|
||||
const checkExist = list.find((item) => item.key === key);
|
||||
if (checkExist) {
|
||||
return toast({
|
||||
status: 'warning',
|
||||
title: t('core.module.http.Key already exists')
|
||||
});
|
||||
}
|
||||
if (!key) return;
|
||||
setList((prevList) => {
|
||||
if (!key) {
|
||||
return prevList;
|
||||
}
|
||||
|
||||
const checkExist = prevList.find((item) => item.key === key);
|
||||
if (checkExist) {
|
||||
setUpdateTrigger((prev) => !prev);
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: t('core.module.http.Key already exists')
|
||||
});
|
||||
return prevList;
|
||||
}
|
||||
return [...prevList, { key, type: 'string', value }];
|
||||
});
|
||||
|
||||
setList((prevList) => [...prevList, { key, type: 'string', value }]);
|
||||
setShouldUpdateNode(true);
|
||||
};
|
||||
|
||||
@@ -406,7 +468,7 @@ const RenderForm = ({
|
||||
<Td p={0} w={'150px'}>
|
||||
<HttpInput
|
||||
hasVariablePlugin={false}
|
||||
hasDropDownPlugin={true}
|
||||
hasDropDownPlugin={tabType === TabEnum.headers}
|
||||
setDropdownValue={(value) => {
|
||||
handleKeyChange(index, value);
|
||||
setUpdateTrigger((prev) => !prev);
|
||||
@@ -450,16 +512,19 @@ const RenderForm = ({
|
||||
<Tr>
|
||||
<Td p={0} w={'150px'}>
|
||||
<HttpInput
|
||||
hasDropDownPlugin={true}
|
||||
hasVariablePlugin={false}
|
||||
hasDropDownPlugin={tabType === TabEnum.headers}
|
||||
setDropdownValue={(val) => {
|
||||
handleAddNewProps(val);
|
||||
setUpdateTrigger((prev) => !prev);
|
||||
}}
|
||||
placeholder={t('core.module.http.Add props')}
|
||||
value={''}
|
||||
h={40}
|
||||
variables={leftVariables}
|
||||
updateTrigger={updateTrigger}
|
||||
onBlur={(val) => {
|
||||
handleAddNewProps(val);
|
||||
setUpdateTrigger((prev) => !prev);
|
||||
}}
|
||||
/>
|
||||
</Td>
|
||||
@@ -490,7 +555,7 @@ const RenderJson = ({
|
||||
<Box mt={1}>
|
||||
<JSONEditor
|
||||
bg={'myGray.50'}
|
||||
height={200}
|
||||
defaultHeight={200}
|
||||
resize
|
||||
value={input.value}
|
||||
placeholder={t('core.module.template.http body placeholder')}
|
||||
|
||||
@@ -9,9 +9,7 @@ import {
|
||||
putSwitchTeam,
|
||||
putUpdateMember,
|
||||
delRemoveMember,
|
||||
delLeaveTeam,
|
||||
getTeamsTags,
|
||||
insertTeamsTags
|
||||
delLeaveTeam
|
||||
} from '@/web/support/user/team/api';
|
||||
import {
|
||||
Box,
|
||||
@@ -49,7 +47,7 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
|
||||
const EditModal = dynamic(() => import('./EditModal'));
|
||||
const InviteModal = dynamic(() => import('./InviteModal'));
|
||||
const TeamTagsAsync = dynamic(() => import('../TeamTagsAsync'));
|
||||
const TeamTagModal = dynamic(() => import('../TeamTagModal'));
|
||||
|
||||
const TeamManageModal = ({ onClose }: { onClose: () => void }) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -57,7 +55,6 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => {
|
||||
const { toast } = useToast();
|
||||
const { teamPlanStatus } = useUserStore();
|
||||
const { feConfigs } = useSystemStore();
|
||||
const [teamsTags, setTeamTags] = useState<any>();
|
||||
|
||||
const { ConfirmModal: ConfirmRemoveMemberModal, openConfirm: openRemoveMember } = useConfirm();
|
||||
const { ConfirmModal: ConfirmLeaveTeamModal, openConfirm: openLeaveConfirm } = useConfirm({
|
||||
@@ -87,8 +84,6 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => {
|
||||
mutationFn: async (teamId: string) => {
|
||||
const token = await putSwitchTeam(teamId);
|
||||
token && setToken(token);
|
||||
// get team tags
|
||||
await getTeamsTags(teamId);
|
||||
return initUserInfo();
|
||||
},
|
||||
errorToast: t('user.team.Switch Team Failed')
|
||||
@@ -99,11 +94,6 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => {
|
||||
['getMembers', userInfo?.team?.teamId],
|
||||
() => {
|
||||
if (!userInfo?.team?.teamId) return [];
|
||||
// get team tags
|
||||
getTeamsTags(userInfo.team.teamId).then((res: any) => {
|
||||
setTeamTags(res);
|
||||
});
|
||||
|
||||
return getTeamMembers(userInfo.team.teamId);
|
||||
}
|
||||
);
|
||||
@@ -217,17 +207,6 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => {
|
||||
: {})}
|
||||
>
|
||||
{team.teamName}
|
||||
{/* {userInfo?.team?.teamId === team.teamId && (
|
||||
<HStack spacing={1}>
|
||||
{teamsTags.slice(0, 3).map((item: any, index) => {
|
||||
return (
|
||||
<Tag key={index} size={'sm'} variant="outline" colorScheme="blue">
|
||||
{item.label}
|
||||
</Tag>
|
||||
);
|
||||
})}
|
||||
</HStack>
|
||||
)} */}
|
||||
</Box>
|
||||
{userInfo?.team?.teamId === team.teamId ? (
|
||||
<MyIcon name={'common/tickFill'} w={'16px'} color={'primary.500'} />
|
||||
@@ -290,31 +269,32 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => {
|
||||
<Box ml={2} bg={'myGray.100'} borderRadius={'20px'} px={3} fontSize={'xs'}>
|
||||
{members.length}
|
||||
</Box>
|
||||
{userInfo.team.role === TeamMemberRoleEnum.owner &&
|
||||
teamPlanStatus?.standardConstants &&
|
||||
teamPlanStatus.standardConstants.maxTeamMember > members.length && (
|
||||
<Button
|
||||
variant={'whitePrimary'}
|
||||
size="sm"
|
||||
borderRadius={'md'}
|
||||
ml={3}
|
||||
leftIcon={
|
||||
<MyIcon name={'common/inviteLight'} w={'14px'} color={'primary.500'} />
|
||||
{userInfo.team.role === TeamMemberRoleEnum.owner && (
|
||||
<Button
|
||||
variant={'whitePrimary'}
|
||||
size="sm"
|
||||
borderRadius={'md'}
|
||||
ml={3}
|
||||
leftIcon={<MyIcon name={'common/inviteLight'} w={'14px'} color={'primary.500'} />}
|
||||
onClick={() => {
|
||||
if (
|
||||
teamPlanStatus?.standardConstants?.maxTeamMember &&
|
||||
teamPlanStatus.standardConstants.maxTeamMember <= members.length
|
||||
) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: t('user.team.Over Max Member Tip', {
|
||||
max: teamPlanStatus.standardConstants.maxTeamMember
|
||||
})
|
||||
});
|
||||
} else {
|
||||
onOpenInvite();
|
||||
}
|
||||
onClick={() => {
|
||||
if (userInfo.team.maxSize <= members.length) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: t('user.team.Over Max Member Tip', { max: userInfo.team.maxSize })
|
||||
});
|
||||
} else {
|
||||
onOpenInvite();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t('user.team.Invite Member')}
|
||||
</Button>
|
||||
)}
|
||||
}}
|
||||
>
|
||||
{t('user.team.Invite Member')}
|
||||
</Button>
|
||||
)}
|
||||
{userInfo.team.role === TeamMemberRoleEnum.owner && feConfigs?.show_team_chat && (
|
||||
<Button
|
||||
variant={'whitePrimary'}
|
||||
@@ -323,14 +303,7 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => {
|
||||
ml={3}
|
||||
leftIcon={<DragHandleIcon w={'14px'} color={'primary.500'} />}
|
||||
onClick={() => {
|
||||
if (userInfo.team.maxSize <= members.length) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: t('user.team.Team Tags Async', { max: userInfo.team.maxSize })
|
||||
});
|
||||
} else {
|
||||
onOpenTeamTagsAsync();
|
||||
}
|
||||
onOpenTeamTagsAsync();
|
||||
}}
|
||||
>
|
||||
{t('user.team.Team Tags Async')}
|
||||
@@ -492,13 +465,7 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => {
|
||||
onSuccess={refetchMembers}
|
||||
/>
|
||||
)}
|
||||
{isOpenTeamTagsAsync && (
|
||||
<TeamTagsAsync
|
||||
teamInfo={teamsTags?.tagsUrl}
|
||||
teamsTags={teamsTags?.list || []}
|
||||
onClose={onCloseTeamTagsAsync}
|
||||
/>
|
||||
)}
|
||||
{isOpenTeamTagsAsync && <TeamTagModal onClose={onCloseTeamTagsAsync} />}
|
||||
<ConfirmRemoveMemberModal />
|
||||
<ConfirmLeaveTeamModal />
|
||||
</>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import React from 'react';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import {
|
||||
Box,
|
||||
@@ -11,61 +11,74 @@ import {
|
||||
HStack,
|
||||
Avatar
|
||||
} from '@chakra-ui/react';
|
||||
import { AttachmentIcon, CopyIcon, DragHandleIcon } from '@chakra-ui/icons';
|
||||
import { putUpdateTeamTags, updateTags } from '@/web/support/user/team/api';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { putUpdateTeam } from '@/web/support/user/team/api';
|
||||
import { useFieldArray, useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import type { TeamTagsSchema } from '@fastgpt/global/support/user/team/type';
|
||||
import type { TeamTagItemType } from '@fastgpt/global/support/user/team/type';
|
||||
import { useRequest } from '@/web/common/hooks/useRequest';
|
||||
import { RepeatIcon } from '@chakra-ui/icons';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { useCopyData } from '@/web/common/hooks/useCopyData';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { getTeamsTags, loadTeamTagsByDomain } from '@/web/support/user/team/api';
|
||||
|
||||
const TeamTagsAsync = ({
|
||||
teamsTags,
|
||||
teamInfo,
|
||||
onClose
|
||||
}: {
|
||||
teamsTags: Array<TeamTagsSchema>;
|
||||
teamInfo: any;
|
||||
onClose: () => void;
|
||||
}) => {
|
||||
type FormType = {
|
||||
teamDomain: string;
|
||||
tags: TeamTagItemType[];
|
||||
};
|
||||
|
||||
const TeamTagsAsync = ({ onClose }: { onClose: () => void }) => {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const [_teamsTags, setTeamTags] = useState<Array<TeamTagsSchema>>(teamsTags);
|
||||
|
||||
const { register, setValue, getValues, handleSubmit } = useForm<any>({
|
||||
defaultValues: { ...teamInfo }
|
||||
});
|
||||
const { userInfo, initUserInfo } = useUserStore();
|
||||
const { copyData } = useCopyData();
|
||||
|
||||
const teamInfo = userInfo?.team;
|
||||
|
||||
if (!teamInfo) {
|
||||
onClose();
|
||||
return null;
|
||||
}
|
||||
|
||||
const { register, control, handleSubmit } = useForm<FormType>({
|
||||
defaultValues: {
|
||||
teamDomain: teamInfo.teamDomain,
|
||||
tags: []
|
||||
}
|
||||
});
|
||||
const { fields: teamTags, replace: replaceTeamTags } = useFieldArray({
|
||||
control,
|
||||
name: 'tags'
|
||||
});
|
||||
|
||||
const baseUrl = global.feConfigs?.customSharePageDomain || location?.origin;
|
||||
const linkUrl = `${baseUrl}/chat/team?shareTeamId=${teamInfo?._id}${
|
||||
getValues('showHistory') ? '' : '&showHistory=0'
|
||||
}`;
|
||||
const linkUrl = `${baseUrl}/chat/team?teamId=${teamInfo.teamId}&teamToken=`;
|
||||
|
||||
// tags Async
|
||||
const { mutate: onclickAsync, isLoading: creating } = useRequest({
|
||||
mutationFn: async (data: any) => {
|
||||
return putUpdateTeamTags({ tagsUrl: data.tagsUrl, teamId: teamInfo?._id });
|
||||
const { mutate: onclickUpdate, isLoading: isUpdating } = useRequest({
|
||||
mutationFn: async (data: FormType) => {
|
||||
return putUpdateTeam({ teamDomain: data.teamDomain, teamId: teamInfo?.teamId });
|
||||
},
|
||||
onSuccess(id: string) {
|
||||
onSuccess() {
|
||||
initUserInfo();
|
||||
onClose();
|
||||
},
|
||||
successToast: t('user.team.Team Tags Async Success'),
|
||||
errorToast: t('common.Create Failed')
|
||||
});
|
||||
const asyncTags = async () => {
|
||||
console.log('getValues', getValues());
|
||||
const res: Array<TeamTagsSchema> = await updateTags(teamInfo?._id, getValues().tagsUrl);
|
||||
setTeamTags(res);
|
||||
toast({ status: 'success', title: '团队标签同步成功' });
|
||||
};
|
||||
useEffect(() => {
|
||||
console.log('teamInfo', teamInfo);
|
||||
}, []);
|
||||
const { mutate: onclickTagAsync, isLoading: isSyncing } = useRequest({
|
||||
mutationFn: (data: FormType) => loadTeamTagsByDomain(data.teamDomain),
|
||||
onSuccess(res) {
|
||||
replaceTeamTags(res);
|
||||
},
|
||||
successToast: t('support.user.team.Team Tags Async Success')
|
||||
});
|
||||
|
||||
useQuery(['getTeamsTags'], getTeamsTags, {
|
||||
onSuccess: (data) => {
|
||||
replaceTeamTags(data);
|
||||
}
|
||||
});
|
||||
|
||||
// 获取
|
||||
return (
|
||||
<>
|
||||
<MyModal
|
||||
@@ -80,7 +93,7 @@ const TeamTagsAsync = ({
|
||||
overflow={'hidden'}
|
||||
title={
|
||||
<Box>
|
||||
<Box>{teamInfo?.name}</Box>
|
||||
<Box>{teamInfo?.teamName}</Box>
|
||||
<Box color={'myGray.500'} fontSize={'xs'} fontWeight={'normal'}>
|
||||
{'填写标签同步链接,点击同步按钮即可同步'}
|
||||
</Box>
|
||||
@@ -98,8 +111,8 @@ const TeamTagsAsync = ({
|
||||
autoFocus
|
||||
bg={'myWhite.600'}
|
||||
placeholder="请输入同步标签"
|
||||
{...register('tagsUrl', {
|
||||
required: t('core.app.error.App name can not be empty')
|
||||
{...register('teamDomain', {
|
||||
required: true
|
||||
})}
|
||||
/>
|
||||
</Flex>
|
||||
@@ -146,7 +159,7 @@ const TeamTagsAsync = ({
|
||||
}}
|
||||
spacing={1}
|
||||
>
|
||||
{_teamsTags.map((item, index) => {
|
||||
{teamTags.map((item, index) => {
|
||||
return (
|
||||
<Tag key={index} mt={2} size={'md'} colorScheme="red" borderRadius="full">
|
||||
<Avatar
|
||||
@@ -161,7 +174,13 @@ const TeamTagsAsync = ({
|
||||
);
|
||||
})}
|
||||
</HStack>
|
||||
<Button ml={4} size="md" leftIcon={<RepeatIcon />} onClick={asyncTags}>
|
||||
<Button
|
||||
isLoading={isSyncing}
|
||||
ml={4}
|
||||
size="md"
|
||||
leftIcon={<RepeatIcon />}
|
||||
onClick={handleSubmit((data) => onclickTagAsync(data))}
|
||||
>
|
||||
立即同步
|
||||
</Button>
|
||||
</Flex>
|
||||
@@ -170,7 +189,7 @@ const TeamTagsAsync = ({
|
||||
<Button variant={'whiteBase'} mr={3} onClick={onClose}>
|
||||
{t('common.Close')}
|
||||
</Button>
|
||||
<Button isLoading={creating} onClick={handleSubmit((data) => onclickAsync(data))}>
|
||||
<Button isLoading={isUpdating} onClick={handleSubmit((data) => onclickUpdate(data))}>
|
||||
{t('user.team.Tags Async')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
@@ -1,7 +1,8 @@
|
||||
export enum PageTypeEnum {
|
||||
login = 'login',
|
||||
export enum LoginPageTypeEnum {
|
||||
passwordLogin = 'passwordLogin',
|
||||
register = 'register',
|
||||
forgetPassword = 'forgetPassword'
|
||||
forgetPassword = 'forgetPassword',
|
||||
wechat = 'wechat'
|
||||
}
|
||||
|
||||
export enum PromotionEnum {
|
||||
|
||||
4
projects/app/src/global/core/ai/api.d.ts
vendored
4
projects/app/src/global/core/ai/api.d.ts
vendored
@@ -1,6 +1,6 @@
|
||||
import { ChatMessageItemType } from '@fastgpt/global/core/ai/type.d';
|
||||
import type { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat.d';
|
||||
|
||||
export type CreateQuestionGuideParams = {
|
||||
export type CreateQuestionGuideParams = OutLinkChatAuthProps & {
|
||||
messages: ChatMessageItemType[];
|
||||
shareId?: string;
|
||||
};
|
||||
|
||||
39
projects/app/src/global/core/chat/api.d.ts
vendored
39
projects/app/src/global/core/chat/api.d.ts
vendored
@@ -1,6 +1,7 @@
|
||||
import type { AppTTSConfigType } from '@fastgpt/global/core/module/type.d';
|
||||
import { ModuleItemType } from '../module/type';
|
||||
import { AdminFbkType, ChatItemType, moduleDispatchResType } from '@fastgpt/global/core/chat/type';
|
||||
import type { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat.d';
|
||||
|
||||
export type GetChatSpeechProps = {
|
||||
ttsConfig: AppTTSConfigType;
|
||||
@@ -14,16 +15,16 @@ export type InitChatProps = {
|
||||
chatId?: string;
|
||||
loadCustomFeedbacks?: boolean;
|
||||
};
|
||||
/* ---------- chat ----------- */
|
||||
export type chatByTeamProps = {
|
||||
teamId?: string;
|
||||
appId?: string;
|
||||
outLinkUid?: string;
|
||||
};
|
||||
export type InitOutLinkChatProps = {
|
||||
chatId?: string;
|
||||
shareId?: string;
|
||||
outLinkUid?: string;
|
||||
shareId: string;
|
||||
outLinkUid: string;
|
||||
};
|
||||
export type InitTeamChatProps = {
|
||||
teamId: string;
|
||||
appId: string;
|
||||
chatId?: string;
|
||||
teamToken: string;
|
||||
};
|
||||
export type InitChatResponse = {
|
||||
chatId?: string;
|
||||
@@ -43,42 +44,30 @@ export type InitChatResponse = {
|
||||
};
|
||||
|
||||
/* ---------- history ----------- */
|
||||
export type getHistoriesProps = {
|
||||
export type GetHistoriesProps = OutLinkChatAuthProps & {
|
||||
appId?: string;
|
||||
authToken?: string;
|
||||
// share chat
|
||||
shareId?: string;
|
||||
outLinkUid?: string; // authToken/uid
|
||||
};
|
||||
|
||||
export type UpdateHistoryProps = {
|
||||
export type UpdateHistoryProps = OutLinkChatAuthProps & {
|
||||
appId: string;
|
||||
chatId: string;
|
||||
customTitle?: string;
|
||||
top?: boolean;
|
||||
shareId?: string;
|
||||
outLinkUid?: string;
|
||||
};
|
||||
|
||||
export type DelHistoryProps = {
|
||||
export type DelHistoryProps = OutLinkChatAuthProps & {
|
||||
appId: string;
|
||||
chatId: string;
|
||||
shareId?: string;
|
||||
outLinkUid?: string;
|
||||
};
|
||||
export type ClearHistoriesProps = {
|
||||
export type ClearHistoriesProps = OutLinkChatAuthProps & {
|
||||
appId?: string;
|
||||
shareId?: string;
|
||||
outLinkUid?: string;
|
||||
};
|
||||
|
||||
/* -------- chat item ---------- */
|
||||
export type DeleteChatItemProps = {
|
||||
export type DeleteChatItemProps = OutLinkChatAuthProps & {
|
||||
appId: string;
|
||||
chatId: string;
|
||||
contentId?: string;
|
||||
shareId?: string;
|
||||
outLinkUid?: string;
|
||||
};
|
||||
|
||||
export type AdminUpdateFeedbackParams = AdminFbkType & {
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
export const Prompt_AgentQA = {
|
||||
description: `<context></context> 标记中是一段文本,学习和分析它,并整理学习成果:
|
||||
description: `<Context></Context> 标记中是一段文本,学习和分析它,并整理学习成果:
|
||||
- 提出问题并给出每个问题的答案。
|
||||
- 答案需详细完整,给出相关原文描述。
|
||||
- 答案可以包含普通文字、链接、代码、表格、公示、媒体链接等 markdown 元素。
|
||||
- 答案需详细完整,尽可能保留原文描述。
|
||||
- 答案可以包含普通文字、链接、代码、表格、公示、媒体链接等 Markdown 元素。
|
||||
- 最多提出 30 个问题。
|
||||
`,
|
||||
fixedText: `最后,你需要按下面的格式返回多个问题和答案:
|
||||
fixedText: `请按以下格式整理学习成果:
|
||||
<Context>
|
||||
文本
|
||||
</Context>
|
||||
Q1: 问题。
|
||||
A1: 答案。
|
||||
Q2:
|
||||
A2:
|
||||
……
|
||||
|
||||
<context>
|
||||
------
|
||||
|
||||
我们开始吧!
|
||||
|
||||
<Context>
|
||||
{{text}}
|
||||
<context/>
|
||||
<Context/>
|
||||
`
|
||||
};
|
||||
|
||||
|
||||
@@ -56,8 +56,8 @@ const UpdatePswModal = ({ onClose }: { onClose: () => void }) => {
|
||||
{...register('newPsw', {
|
||||
required: true,
|
||||
maxLength: {
|
||||
value: 20,
|
||||
message: '密码最少 4 位最多 20 位'
|
||||
value: 60,
|
||||
message: '密码最少 4 位最多 60 位'
|
||||
}
|
||||
})}
|
||||
></Input>
|
||||
@@ -70,8 +70,8 @@ const UpdatePswModal = ({ onClose }: { onClose: () => void }) => {
|
||||
{...register('confirmPsw', {
|
||||
required: true,
|
||||
maxLength: {
|
||||
value: 20,
|
||||
message: '密码最少 4 位最多 20 位'
|
||||
value: 60,
|
||||
message: '密码最少 4 位最多 60 位'
|
||||
}
|
||||
})}
|
||||
></Input>
|
||||
|
||||
@@ -25,8 +25,9 @@ const UsageDetail = ({ usage, onClose }: { usage: UsageItemType; onClose: () =>
|
||||
[usage.list]
|
||||
);
|
||||
|
||||
const { hasModel, hasCharsLen, hasDuration } = useMemo(() => {
|
||||
const { hasModel, hasToken, hasCharsLen, hasDuration } = useMemo(() => {
|
||||
let hasModel = false;
|
||||
let hasToken = false;
|
||||
let hasCharsLen = false;
|
||||
let hasDuration = false;
|
||||
let hasDataLen = false;
|
||||
@@ -36,6 +37,9 @@ const UsageDetail = ({ usage, onClose }: { usage: UsageItemType; onClose: () =>
|
||||
hasModel = true;
|
||||
}
|
||||
|
||||
if (typeof item.tokens === 'number') {
|
||||
hasToken = true;
|
||||
}
|
||||
if (typeof item.charsLength === 'number') {
|
||||
hasCharsLen = true;
|
||||
}
|
||||
@@ -46,6 +50,7 @@ const UsageDetail = ({ usage, onClose }: { usage: UsageItemType; onClose: () =>
|
||||
|
||||
return {
|
||||
hasModel,
|
||||
hasToken,
|
||||
hasCharsLen,
|
||||
hasDuration,
|
||||
hasDataLen
|
||||
@@ -91,9 +96,9 @@ const UsageDetail = ({ usage, onClose }: { usage: UsageItemType; onClose: () =>
|
||||
<Tr>
|
||||
<Th>{t('support.wallet.usage.Module name')}</Th>
|
||||
{hasModel && <Th>{t('support.wallet.usage.Ai model')}</Th>}
|
||||
{hasToken && <Th>{t('support.wallet.usage.Token Length')}</Th>}
|
||||
{hasCharsLen && <Th>{t('support.wallet.usage.Text Length')}</Th>}
|
||||
{hasDuration && <Th>{t('support.wallet.usage.Duration')}</Th>}
|
||||
|
||||
<Th>{t('support.wallet.usage.Total points')}</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
@@ -102,6 +107,7 @@ const UsageDetail = ({ usage, onClose }: { usage: UsageItemType; onClose: () =>
|
||||
<Tr key={i}>
|
||||
<Td>{t(item.moduleName)}</Td>
|
||||
{hasModel && <Td>{item.model ?? '-'}</Td>}
|
||||
{hasToken && <Td>{item.tokens ?? '-'}</Td>}
|
||||
{hasCharsLen && <Td>{item.charsLength ?? '-'}</Td>}
|
||||
{hasDuration && <Td>{item.duration ?? '-'}</Td>}
|
||||
<Td>{formatNumber(item.amount)}</Td>
|
||||
|
||||
@@ -4,6 +4,11 @@ import { connectToDatabase } from '@/service/mongo';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { MongoUsage } from '@fastgpt/service/support/wallet/usage/schema';
|
||||
import { connectionMongo } from '@fastgpt/service/common/mongo';
|
||||
import { checkFiles } from '../timerTask/dataset/checkInValidDatasetFiles';
|
||||
import { addHours } from 'date-fns';
|
||||
import { checkInvalid as checkInvalidImg } from '../timerTask/dataset/checkInvalidDatasetImage';
|
||||
import { checkInvalidCollection } from '../timerTask/dataset/checkInvalidMongoCollection';
|
||||
import { checkInvalidVector } from '../timerTask/dataset/checkInvalidVector';
|
||||
|
||||
/* pg 中的数据搬到 mongo dataset.datas 中,并做映射 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
@@ -21,6 +26,21 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
});
|
||||
}
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
console.log('执行脏数据清理任务');
|
||||
const end = addHours(new Date(), -1);
|
||||
const start = addHours(new Date(), -360 * 24);
|
||||
await checkFiles(start, end);
|
||||
await checkInvalidImg(start, end);
|
||||
await checkInvalidCollection(start, end);
|
||||
await checkInvalidVector(start, end);
|
||||
console.log('执行脏数据清理任务完毕');
|
||||
} catch (error) {
|
||||
console.log('执行脏数据清理任务出错了');
|
||||
}
|
||||
})();
|
||||
|
||||
jsonRes(res, {
|
||||
message: 'success'
|
||||
});
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authCertOrShareId } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { authChatCert } from '@/service/support/permission/auth/chat';
|
||||
import { uploadMongoImg } from '@fastgpt/service/common/file/image/controller';
|
||||
import { UploadImgProps } from '@fastgpt/global/common/file/api';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { shareId, ...body } = req.body as UploadImgProps;
|
||||
const body = req.body as UploadImgProps;
|
||||
|
||||
const { teamId } = await authCertOrShareId({ req, shareId, authToken: true });
|
||||
const { teamId } = await authChatCert({ req, authToken: true });
|
||||
|
||||
const data = await uploadMongoImg({
|
||||
teamId,
|
||||
|
||||
@@ -4,8 +4,6 @@ import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { readFileSync, readdirSync } from 'fs';
|
||||
import type { InitDateResponse } from '@/global/common/api/systemRes';
|
||||
import type { FastGPTConfigFileType } from '@fastgpt/global/common/system/types/index.d';
|
||||
import { getTikTokenEnc } from '@fastgpt/global/common/string/tiktoken';
|
||||
import { initHttpAgent } from '@fastgpt/service/common/middle/httpAgent';
|
||||
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
|
||||
import { getFastGPTConfigFromDB } from '@fastgpt/service/common/system/config/controller';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
@@ -63,7 +61,6 @@ export async function getInitConfig() {
|
||||
await connectToDatabase();
|
||||
|
||||
await Promise.all([
|
||||
initGlobal(),
|
||||
initSystemConfig(),
|
||||
// getSimpleModeTemplates(),
|
||||
getSystemVersion(),
|
||||
@@ -84,18 +81,6 @@ export async function getInitConfig() {
|
||||
}
|
||||
}
|
||||
|
||||
export function initGlobal() {
|
||||
if (global.communityPlugins) return;
|
||||
|
||||
global.communityPlugins = [];
|
||||
global.simpleModeTemplates = [];
|
||||
global.qaQueueLen = global.qaQueueLen ?? 0;
|
||||
global.vectorQueueLen = global.vectorQueueLen ?? 0;
|
||||
// init tikToken
|
||||
getTikTokenEnc();
|
||||
initHttpAgent();
|
||||
}
|
||||
|
||||
export async function initSystemConfig() {
|
||||
// load config
|
||||
const [dbConfig, fileConfig] = await Promise.all([
|
||||
@@ -125,7 +110,6 @@ export async function initSystemConfig() {
|
||||
|
||||
// set config
|
||||
initFastGPTConfig(config);
|
||||
global.systemEnv = config.systemEnv;
|
||||
|
||||
console.log({
|
||||
feConfigs: global.feConfigs,
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { startQueue } from '@/service/utils/tools';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { startTrainingQueue } from '@/service/core/dataset/training/utils';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
await authCert({ req, authToken: true });
|
||||
startQueue();
|
||||
startTrainingQueue();
|
||||
} catch (error) {}
|
||||
jsonRes(res);
|
||||
}
|
||||
|
||||
@@ -4,22 +4,21 @@ import { connectToDatabase } from '@/service/mongo';
|
||||
import type { CreateQuestionGuideParams } from '@/global/core/ai/api.d';
|
||||
import { pushQuestionGuideUsage } from '@/service/support/wallet/usage/push';
|
||||
import { createQuestionGuide } from '@fastgpt/service/core/ai/functions/createQuestionGuide';
|
||||
import { authCertOrShareId } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { authChatCert } from '@/service/support/permission/auth/chat';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { messages, shareId } = req.body as CreateQuestionGuideParams;
|
||||
const { messages } = req.body as CreateQuestionGuideParams;
|
||||
|
||||
const { tmbId, teamId } = await authCertOrShareId({
|
||||
const { tmbId, teamId } = await authChatCert({
|
||||
req,
|
||||
authToken: true,
|
||||
shareId
|
||||
authToken: true
|
||||
});
|
||||
|
||||
const qgModel = global.llmModels[0];
|
||||
|
||||
const { result, charsLength } = await createQuestionGuide({
|
||||
const { result, tokens } = await createQuestionGuide({
|
||||
messages,
|
||||
model: qgModel.model
|
||||
});
|
||||
@@ -29,7 +28,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
});
|
||||
|
||||
pushQuestionGuideUsage({
|
||||
charsLength,
|
||||
tokens,
|
||||
teamId,
|
||||
tmbId
|
||||
});
|
||||
|
||||
@@ -7,12 +7,13 @@ import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
||||
import { ClearHistoriesProps } from '@/global/core/chat/api';
|
||||
import { authOutLink } from '@/service/support/permission/auth/outLink';
|
||||
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { authTeamSpaceToken } from '@/service/support/permission/auth/team';
|
||||
|
||||
/* clear chat history */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { appId, shareId, outLinkUid } = req.query as ClearHistoriesProps;
|
||||
const { appId, shareId, outLinkUid, teamId, teamToken } = req.query as ClearHistoriesProps;
|
||||
|
||||
let chatAppId = appId;
|
||||
|
||||
@@ -26,6 +27,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
outLinkUid: uid
|
||||
};
|
||||
}
|
||||
if (teamId && teamToken) {
|
||||
const { uid } = await authTeamSpaceToken({ teamId, teamToken });
|
||||
return {
|
||||
teamId,
|
||||
appId,
|
||||
outLinkUid: uid
|
||||
};
|
||||
}
|
||||
if (appId) {
|
||||
const { tmbId } = await authCert({ req, authToken: true });
|
||||
|
||||
|
||||
@@ -4,14 +4,15 @@ import { connectToDatabase } from '@/service/mongo';
|
||||
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||
import type { ChatHistoryItemType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { getHistoriesProps } from '@/global/core/chat/api';
|
||||
import { GetHistoriesProps } from '@/global/core/chat/api';
|
||||
import { authOutLink } from '@/service/support/permission/auth/outLink';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { authTeamSpaceToken } from '@/service/support/permission/auth/team';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { appId, shareId, outLinkUid } = req.body as getHistoriesProps;
|
||||
const { appId, shareId, outLinkUid, teamId, teamToken } = req.body as GetHistoriesProps;
|
||||
|
||||
const limit = shareId && outLinkUid ? 20 : 30;
|
||||
|
||||
@@ -28,10 +29,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
}
|
||||
};
|
||||
}
|
||||
if (appId && outLinkUid) {
|
||||
if (appId && teamId && teamToken) {
|
||||
const { uid } = await authTeamSpaceToken({ teamId, teamToken });
|
||||
return {
|
||||
shareId,
|
||||
outLinkUid: outLinkUid,
|
||||
teamId,
|
||||
appId,
|
||||
outLinkUid: uid,
|
||||
source: ChatSourceEnum.team
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { connectToDatabase } from '@/service/mongo';
|
||||
import { GetChatSpeechProps } from '@/global/core/chat/api.d';
|
||||
import { text2Speech } from '@fastgpt/service/core/ai/audio/speech';
|
||||
import { pushAudioSpeechUsage } from '@/service/support/wallet/usage/push';
|
||||
import { authCertOrShareId } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { authChatCert } from '@/service/support/permission/auth/chat';
|
||||
import { authType2UsageSource } from '@/service/support/wallet/usage/utils';
|
||||
import { getAudioSpeechModel } from '@fastgpt/service/core/ai/model';
|
||||
import { MongoTTSBuffer } from '@fastgpt/service/common/buffer/tts/schema';
|
||||
@@ -19,13 +19,13 @@ import { MongoTTSBuffer } from '@fastgpt/service/common/buffer/tts/schema';
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { ttsConfig, input, shareId } = req.body as GetChatSpeechProps;
|
||||
const { ttsConfig, input } = req.body as GetChatSpeechProps;
|
||||
|
||||
if (!ttsConfig.model || !ttsConfig.voice) {
|
||||
throw new Error('model or voice not found');
|
||||
}
|
||||
|
||||
const { teamId, tmbId, authType } = await authCertOrShareId({ req, authToken: true, shareId });
|
||||
const { teamId, tmbId, authType } = await authChatCert({ req, authToken: true });
|
||||
|
||||
const ttsModel = getAudioSpeechModel(ttsConfig.model);
|
||||
const voiceData = ttsModel.voices?.find((item) => item.value === ttsConfig.voice);
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import type { chatByTeamProps } from '@/global/core/chat/api.d';
|
||||
import axios from 'axios';
|
||||
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
||||
import { getChatItems } from '@fastgpt/service/core/chat/controller';
|
||||
import { selectShareResponse } from '@/utils/service/core/chat';
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
|
||||
let { teamId, appId, outLinkUid } = req.query as chatByTeamProps;
|
||||
|
||||
const history = await MongoChatItem.find({
|
||||
appId: appId,
|
||||
outLinkUid: outLinkUid,
|
||||
teamId: teamId
|
||||
});
|
||||
|
||||
jsonRes(res, {
|
||||
data: history
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
data: req.query,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const config = {
|
||||
api: {
|
||||
responseLimit: '10mb'
|
||||
}
|
||||
};
|
||||
@@ -9,7 +9,7 @@ import { getChatItems } from '@fastgpt/service/core/chat/controller';
|
||||
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
|
||||
import { authOutLink } from '@/service/support/permission/auth/outLink';
|
||||
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||
import { selectShareResponse } from '@/utils/service/core/chat';
|
||||
import { selectSimpleChatResponse } from '@/utils/service/core/chat';
|
||||
import { AppErrEnum } from '@fastgpt/global/common/error/code/app';
|
||||
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
|
||||
@@ -50,7 +50,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
|
||||
// pick share response field
|
||||
history.forEach((item) => {
|
||||
item.responseData = selectShareResponse({ responseData: item.responseData });
|
||||
item.responseData = selectSimpleChatResponse({ responseData: item.responseData });
|
||||
});
|
||||
|
||||
jsonRes<InitChatResponse>(res, {
|
||||
|
||||
@@ -4,59 +4,57 @@ import { connectToDatabase } from '@/service/mongo';
|
||||
import { getGuideModule } from '@fastgpt/global/core/module/utils';
|
||||
import { getChatModelNameListByModules } from '@/service/core/app/module';
|
||||
import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
|
||||
import type { InitChatProps, InitChatResponse } from '@/global/core/chat/api.d';
|
||||
import type { InitChatResponse, InitTeamChatProps } from '@/global/core/chat/api.d';
|
||||
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||
import { getChatItems } from '@fastgpt/service/core/chat/controller';
|
||||
import { AppErrEnum } from '@fastgpt/global/common/error/code/app';
|
||||
import { authTeamSpaceToken } from '@/service/support/permission/auth/team';
|
||||
import { MongoTeam } from '@fastgpt/service/support/user/team/teamSchema';
|
||||
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
|
||||
import { selectSimpleChatResponse } from '@/utils/service/core/chat';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
|
||||
let { appId, chatId, outLinkUid } = req.query as {
|
||||
chatId?: string;
|
||||
appId?: string;
|
||||
outLinkUid?: string;
|
||||
};
|
||||
let { teamId, appId, chatId, teamToken } = req.query as InitTeamChatProps;
|
||||
|
||||
if (!appId) {
|
||||
return jsonRes(res, {
|
||||
code: 501,
|
||||
message: "You don't have an app yet"
|
||||
});
|
||||
if (!teamId || !appId || !teamToken) {
|
||||
throw new Error('teamId, appId, teamToken are required');
|
||||
}
|
||||
|
||||
// auth app permission
|
||||
const [chat, app] = await Promise.all([
|
||||
// authApp({
|
||||
// req,
|
||||
// authToken: false,
|
||||
// appId,
|
||||
// per: 'r'
|
||||
// }),
|
||||
chatId ? MongoChat.findOne({ appId, chatId }) : undefined,
|
||||
const { uid } = await authTeamSpaceToken({
|
||||
teamId,
|
||||
teamToken
|
||||
});
|
||||
|
||||
const [team, chat, app] = await Promise.all([
|
||||
MongoTeam.findById(teamId, 'name avatar').lean(),
|
||||
MongoChat.findOne({ teamId, appId, chatId }).lean(),
|
||||
MongoApp.findById(appId).lean()
|
||||
]);
|
||||
|
||||
if (!app) {
|
||||
throw new Error(AppErrEnum.unExist);
|
||||
}
|
||||
|
||||
// auth chat permission
|
||||
// if (chat && chat.outLinkUid !== outLinkUid) {
|
||||
// throw new Error(ChatErrEnum.unAuthChat);
|
||||
// }
|
||||
// // auth chat permission
|
||||
// if (chat && !app.canWrite && String(tmbId) !== String(chat?.tmbId)) {
|
||||
// throw new Error(ChatErrEnum.unAuthChat);
|
||||
// }
|
||||
if (chat && chat.outLinkUid !== uid) {
|
||||
throw new Error(ChatErrEnum.unAuthChat);
|
||||
}
|
||||
|
||||
// get app and history
|
||||
const { history } = await getChatItems({
|
||||
appId,
|
||||
chatId,
|
||||
limit: 30,
|
||||
field: `dataId obj value adminFeedback userBadFeedback userGoodFeedback ${ModuleOutputKeyEnum.responseData}`
|
||||
field: `dataId obj value userGoodFeedback userBadFeedback adminFeedback ${ModuleOutputKeyEnum.responseData}`
|
||||
});
|
||||
|
||||
// pick share response field
|
||||
history.forEach((item) => {
|
||||
item.responseData = selectSimpleChatResponse({ responseData: item.responseData });
|
||||
});
|
||||
|
||||
jsonRes<InitChatResponse>(res, {
|
||||
@@ -64,7 +62,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
chatId,
|
||||
appId,
|
||||
title: chat?.title || '新对话',
|
||||
userAvatar: undefined,
|
||||
userAvatar: team?.avatar,
|
||||
variables: chat?.variables || {},
|
||||
history,
|
||||
app: {
|
||||
|
||||
@@ -8,9 +8,9 @@ import type { GetDatasetCollectionsProps } from '@/global/core/api/datasetReq';
|
||||
import { PagingData } from '@/types';
|
||||
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
|
||||
import { DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { startQueue } from '@/service/utils/tools';
|
||||
import { authDataset } from '@fastgpt/service/support/permission/auth/dataset';
|
||||
import { DatasetDataCollectionName } from '@fastgpt/service/core/dataset/data/schema';
|
||||
import { startTrainingQueue } from '@/service/core/dataset/training/utils';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
@@ -158,7 +158,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
);
|
||||
|
||||
if (data.find((item) => item.trainingAmount > 0)) {
|
||||
startQueue();
|
||||
startTrainingQueue();
|
||||
}
|
||||
|
||||
// count collections
|
||||
|
||||
@@ -75,7 +75,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
a: formatA
|
||||
});
|
||||
|
||||
const { insertId, charsLength } = await insertData2Dataset({
|
||||
const { insertId, tokens } = await insertData2Dataset({
|
||||
teamId,
|
||||
tmbId,
|
||||
datasetId,
|
||||
@@ -90,7 +90,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
pushGenerateVectorUsage({
|
||||
teamId,
|
||||
tmbId,
|
||||
charsLength,
|
||||
tokens,
|
||||
model: vectorModelData.model
|
||||
});
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
insertLen: 1
|
||||
});
|
||||
|
||||
const { charsLength } = await updateData2Dataset({
|
||||
const { tokens } = await updateData2Dataset({
|
||||
dataId: id,
|
||||
q,
|
||||
a,
|
||||
@@ -45,7 +45,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
pushGenerateVectorUsage({
|
||||
teamId,
|
||||
tmbId,
|
||||
charsLength,
|
||||
tokens,
|
||||
model: vectorModel
|
||||
});
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
extensionBg: datasetSearchExtensionBg
|
||||
});
|
||||
|
||||
const { searchRes, charsLength, ...result } = await searchDatasetData({
|
||||
const { searchRes, tokens, ...result } = await searchDatasetData({
|
||||
teamId,
|
||||
reRankQuery: rewriteQuery,
|
||||
queries: concatQueries,
|
||||
@@ -74,14 +74,14 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
const { totalPoints } = pushGenerateVectorUsage({
|
||||
teamId,
|
||||
tmbId,
|
||||
charsLength,
|
||||
tokens,
|
||||
model: dataset.vectorModel,
|
||||
source: apikey ? UsageSourceEnum.api : UsageSourceEnum.fastgpt,
|
||||
|
||||
...(aiExtensionResult &&
|
||||
extensionModel && {
|
||||
extensionModel: extensionModel.name,
|
||||
extensionCharsLength: aiExtensionResult.charsLength
|
||||
extensionTokens: aiExtensionResult.tokens
|
||||
})
|
||||
});
|
||||
if (apikey) {
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import {
|
||||
delFileByFileIdList,
|
||||
getGFSCollection
|
||||
} from '@fastgpt/service/common/file/gridfs/controller';
|
||||
import { addLog } from '@fastgpt/service/common/system/log';
|
||||
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
|
||||
import { addHours } from 'date-fns';
|
||||
|
||||
/*
|
||||
check dataset.files data. If there is no match in dataset.collections, delete it
|
||||
可能异常情况
|
||||
1. 上传了文件,未成功创建集合
|
||||
*/
|
||||
let deleteFileAmount = 0;
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const { startHour = 24, endHour = 1 } = req.body as {
|
||||
startHour?: number;
|
||||
endHour?: number;
|
||||
limit?: number;
|
||||
};
|
||||
await authCert({ req, authRoot: true });
|
||||
await connectToDatabase();
|
||||
|
||||
// start: now - maxDay, end: now - 3 day
|
||||
const start = addHours(new Date(), -startHour);
|
||||
const end = addHours(new Date(), -endHour);
|
||||
deleteFileAmount = 0;
|
||||
console.log(start, end);
|
||||
|
||||
await checkFiles(start, end);
|
||||
|
||||
jsonRes(res, {
|
||||
data: deleteFileAmount,
|
||||
message: 'success'
|
||||
});
|
||||
} catch (error) {
|
||||
addLog.error(`check valid dataset files error`, error);
|
||||
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function checkFiles(start: Date, end: Date) {
|
||||
const collection = getGFSCollection('dataset');
|
||||
const where = {
|
||||
uploadDate: { $gte: start, $lte: end }
|
||||
};
|
||||
|
||||
// 1. get all file _id
|
||||
const files = await collection
|
||||
.find(where, {
|
||||
projection: {
|
||||
metadata: 1,
|
||||
_id: 1
|
||||
}
|
||||
})
|
||||
.toArray();
|
||||
console.log('total files', files.length);
|
||||
|
||||
let index = 0;
|
||||
for await (const file of files) {
|
||||
try {
|
||||
// 2. find fileId in dataset.collections
|
||||
const hasCollection = await MongoDatasetCollection.countDocuments({
|
||||
teamId: file.metadata.teamId,
|
||||
fileId: file._id
|
||||
});
|
||||
|
||||
// 3. if not found, delete file
|
||||
if (hasCollection === 0) {
|
||||
await delFileByFileIdList({ bucketName: 'dataset', fileIdList: [String(file._id)] });
|
||||
console.log('delete file', file._id);
|
||||
deleteFileAmount++;
|
||||
}
|
||||
index++;
|
||||
index % 100 === 0 && console.log(index);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
console.log(`检测完成,共删除 ${deleteFileAmount} 个无效文件`);
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { addLog } from '@fastgpt/service/common/system/log';
|
||||
import { addHours } from 'date-fns';
|
||||
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
|
||||
import { MongoImage } from '@fastgpt/service/common/file/image/schema';
|
||||
|
||||
/*
|
||||
检测无效的数据集图片
|
||||
|
||||
可能异常情况:
|
||||
1. 上传文件过程中,上传了图片,但是最终没有创建数据集。
|
||||
*/
|
||||
|
||||
let deleteImageAmount = 0;
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const {
|
||||
startHour = 72,
|
||||
endHour = 24,
|
||||
limit = 10
|
||||
} = req.body as { startHour?: number; endHour?: number; limit?: number };
|
||||
await authCert({ req, authRoot: true });
|
||||
await connectToDatabase();
|
||||
|
||||
// start: now - maxDay, end: now - 3 day
|
||||
const start = addHours(new Date(), -startHour);
|
||||
const end = addHours(new Date(), -endHour);
|
||||
deleteImageAmount = 0;
|
||||
|
||||
await checkInvalid(start, end, limit);
|
||||
|
||||
jsonRes(res, {
|
||||
data: deleteImageAmount
|
||||
});
|
||||
} catch (error) {
|
||||
addLog.error(`check Invalid user error`, error);
|
||||
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function checkInvalid(start: Date, end: Date, limit = 50) {
|
||||
const images = await MongoImage.find(
|
||||
{
|
||||
createTime: {
|
||||
$gte: start,
|
||||
$lte: end
|
||||
},
|
||||
'metadata.relatedId': { $exists: true }
|
||||
},
|
||||
'_id teamId metadata'
|
||||
);
|
||||
console.log('total images', images.length);
|
||||
let index = 0;
|
||||
|
||||
for await (const image of images) {
|
||||
try {
|
||||
// 1. 检测是否有对应的集合
|
||||
const collection = await MongoDatasetCollection.findOne(
|
||||
{
|
||||
teamId: image.teamId,
|
||||
'metadata.relatedImgId': image.metadata?.relatedId
|
||||
},
|
||||
'_id'
|
||||
);
|
||||
|
||||
if (!collection) {
|
||||
await image.deleteOne();
|
||||
deleteImageAmount++;
|
||||
}
|
||||
|
||||
index++;
|
||||
|
||||
index % 100 === 0 && console.log(index);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`检测完成,共删除 ${deleteImageAmount} 个无效图片`);
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { addLog } from '@fastgpt/service/common/system/log';
|
||||
import { deleteDatasetDataVector } from '@fastgpt/service/common/vectorStore/controller';
|
||||
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
|
||||
import { addHours } from 'date-fns';
|
||||
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
|
||||
import { MongoDatasetTraining } from '@fastgpt/service/core/dataset/training/schema';
|
||||
|
||||
/*
|
||||
检测无效的 Mongo 数据
|
||||
异常情况:
|
||||
1. 训练过程删除知识库,可能导致还会有新的数据插入,导致无效。
|
||||
*/
|
||||
|
||||
let deleteAmount = 0;
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const { startHour = 3, endHour = 1 } = req.body as { startHour?: number; endHour?: number };
|
||||
await authCert({ req, authRoot: true });
|
||||
await connectToDatabase();
|
||||
|
||||
// start: now - maxDay, end: now - endHour
|
||||
const start = addHours(new Date(), -startHour);
|
||||
const end = addHours(new Date(), -endHour);
|
||||
deleteAmount = 0;
|
||||
|
||||
await checkInvalidCollection(start, end);
|
||||
|
||||
jsonRes(res, {
|
||||
data: deleteAmount,
|
||||
message: 'success'
|
||||
});
|
||||
} catch (error) {
|
||||
addLog.error(`check Invalid user error`, error);
|
||||
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function checkInvalidCollection(start: Date, end: Date) {
|
||||
// 1. 获取时间范围的所有data
|
||||
const rows = await MongoDatasetData.find(
|
||||
{
|
||||
updateTime: {
|
||||
$gte: start,
|
||||
$lte: end
|
||||
}
|
||||
},
|
||||
'_id teamId collectionId'
|
||||
).lean();
|
||||
|
||||
// 2. 合并所有的collectionId
|
||||
const map = new Map<string, { teamId: string; collectionId: string }>();
|
||||
for (const item of rows) {
|
||||
const collectionId = String(item.collectionId);
|
||||
if (!map.has(collectionId)) {
|
||||
map.set(collectionId, { teamId: item.teamId, collectionId });
|
||||
}
|
||||
}
|
||||
const list = Array.from(map.values());
|
||||
console.log('total collections', list.length);
|
||||
let index = 0;
|
||||
|
||||
for await (const item of list) {
|
||||
try {
|
||||
// 3. 查看该collection是否存在,不存在,则删除对应的数据
|
||||
const collection = await MongoDatasetCollection.findOne({ _id: item.collectionId });
|
||||
if (!collection) {
|
||||
const result = await Promise.all([
|
||||
MongoDatasetTraining.deleteMany({
|
||||
teamId: item.teamId,
|
||||
collectionId: item.collectionId
|
||||
}),
|
||||
MongoDatasetData.deleteMany({
|
||||
teamId: item.teamId,
|
||||
collectionId: item.collectionId
|
||||
}),
|
||||
deleteDatasetDataVector({
|
||||
teamId: item.teamId,
|
||||
collectionIds: [String(item.collectionId)]
|
||||
})
|
||||
]);
|
||||
console.log(result);
|
||||
console.log('collection is not found', item);
|
||||
continue;
|
||||
}
|
||||
} catch (error) {}
|
||||
console.log(++index);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { addLog } from '@fastgpt/service/common/system/log';
|
||||
import {
|
||||
deleteDatasetDataVector,
|
||||
getVectorDataByTime
|
||||
} from '@fastgpt/service/common/vectorStore/controller';
|
||||
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
|
||||
import { addHours } from 'date-fns';
|
||||
|
||||
/*
|
||||
检测无效的 Vector 数据.
|
||||
异常情况:
|
||||
1. 插入数据时,vector成功,mongo失败
|
||||
2. 更新数据,也会有插入 vector
|
||||
*/
|
||||
|
||||
let deletedVectorAmount = 0;
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const { startHour = 5, endHour = 1 } = req.body as { startHour?: number; endHour?: number };
|
||||
await authCert({ req, authRoot: true });
|
||||
await connectToDatabase();
|
||||
|
||||
// start: now - maxDay, end: now - endHour
|
||||
const start = addHours(new Date(), -startHour);
|
||||
const end = addHours(new Date(), -endHour);
|
||||
deletedVectorAmount = 0;
|
||||
|
||||
await checkInvalidVector(start, end);
|
||||
|
||||
jsonRes(res, {
|
||||
data: deletedVectorAmount,
|
||||
message: 'success'
|
||||
});
|
||||
} catch (error) {
|
||||
addLog.error(`check Invalid user error`, error);
|
||||
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function checkInvalidVector(start: Date, end: Date) {
|
||||
// 1. get all vector data
|
||||
const rows = await getVectorDataByTime(start, end);
|
||||
console.log('total data', rows.length);
|
||||
|
||||
let index = 0;
|
||||
|
||||
for await (const item of rows) {
|
||||
if (!item.teamId || !item.datasetId || !item.id) {
|
||||
console.log('error data', item);
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
// 2. find dataset.data
|
||||
const hasData = await MongoDatasetData.countDocuments({
|
||||
teamId: item.teamId,
|
||||
datasetId: item.datasetId,
|
||||
'indexes.dataId': item.id
|
||||
});
|
||||
|
||||
// 3. if not found, delete vector
|
||||
if (hasData === 0) {
|
||||
await deleteDatasetDataVector({
|
||||
teamId: item.teamId,
|
||||
id: item.id
|
||||
});
|
||||
console.log('delete vector data', item.id);
|
||||
deletedVectorAmount++;
|
||||
}
|
||||
|
||||
index++;
|
||||
index % 100 === 0 && console.log(index);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`检测完成,共删除 ${deletedVectorAmount} 个无效 向量 数据`);
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { withNextCors } from '@fastgpt/service/common/middle/cors';
|
||||
import { getUploadModel } from '@fastgpt/service/common/file/multer';
|
||||
import { removeFilesByPaths } from '@fastgpt/service/common/file/utils';
|
||||
import fs from 'fs';
|
||||
import { getAIApi } from '@fastgpt/service/core/ai/config';
|
||||
import { pushWhisperUsage } from '@/service/support/wallet/usage/push';
|
||||
import { authChatCert } from '@/service/support/permission/auth/chat';
|
||||
|
||||
const upload = getUploadModel({
|
||||
maxSize: 2
|
||||
@@ -18,12 +18,20 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
try {
|
||||
const {
|
||||
file,
|
||||
data: { duration }
|
||||
} = await upload.doUpload<{ duration: number; shareId?: string }>(req, res);
|
||||
data: { duration, teamId: spaceTeamId, teamToken }
|
||||
} = await upload.doUpload<{
|
||||
duration: number;
|
||||
shareId?: string;
|
||||
teamId?: string;
|
||||
teamToken?: string;
|
||||
}>(req, res);
|
||||
|
||||
req.body.teamId = spaceTeamId;
|
||||
req.body.teamToken = teamToken;
|
||||
|
||||
filePaths = [file.path];
|
||||
|
||||
const { teamId, tmbId } = await authCert({ req, authToken: true });
|
||||
const { teamId, tmbId } = await authChatCert({ req, authToken: true });
|
||||
|
||||
if (!global.whisperModel) {
|
||||
throw new Error('whisper model not found');
|
||||
|
||||
@@ -18,31 +18,28 @@ import { authOutLinkChatStart } from '@/service/support/permission/auth/outLink'
|
||||
import { pushResult2Remote, addOutLinkUsage } from '@fastgpt/service/support/outLink/tools';
|
||||
import requestIp from 'request-ip';
|
||||
import { getUsageSourceByAuthType } from '@fastgpt/global/support/wallet/usage/tools';
|
||||
import { authTeamShareChatStart } from '@/service/support/permission/auth/teamChat';
|
||||
import { selectShareResponse } from '@/utils/service/core/chat';
|
||||
import { authTeamSpaceToken } from '@/service/support/permission/auth/team';
|
||||
import { selectSimpleChatResponse } from '@/utils/service/core/chat';
|
||||
import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { getUserChatInfoAndAuthTeamPoints } from '@/service/support/permission/auth/team';
|
||||
import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||
import { autChatCrud } from '@/service/support/permission/auth/chat';
|
||||
import { UserModelSchema } from '@fastgpt/global/support/user/type';
|
||||
import { AppSchema } from '@fastgpt/global/core/app/type';
|
||||
import { AuthOutLinkChatProps } from '@fastgpt/global/support/outLink/api';
|
||||
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
|
||||
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
|
||||
|
||||
type FastGptWebChatProps = {
|
||||
chatId?: string; // undefined: nonuse history, '': new chat, 'xxxxx': use history
|
||||
appId?: string;
|
||||
};
|
||||
type FastGptShareChatProps = {
|
||||
shareId?: string;
|
||||
outLinkUid?: string;
|
||||
};
|
||||
type FastGptTeamShareChatProps = {
|
||||
shareTeamId?: string;
|
||||
outLinkUid?: string;
|
||||
};
|
||||
|
||||
export type Props = ChatCompletionCreateParams &
|
||||
FastGptWebChatProps &
|
||||
FastGptShareChatProps &
|
||||
FastGptTeamShareChatProps & {
|
||||
OutLinkChatAuthProps & {
|
||||
messages: ChatMessageItemType[];
|
||||
stream?: boolean;
|
||||
detail?: boolean;
|
||||
@@ -53,6 +50,18 @@ export type ChatResponseType = {
|
||||
quoteLen?: number;
|
||||
};
|
||||
|
||||
type AuthResponseType = {
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
user: UserModelSchema;
|
||||
app: AppSchema;
|
||||
responseDetail?: boolean;
|
||||
authType: `${AuthUserTypeEnum}`;
|
||||
apikey?: string;
|
||||
canWrite: boolean;
|
||||
outLinkUserId?: string;
|
||||
};
|
||||
|
||||
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
res.on('close', () => {
|
||||
res.end();
|
||||
@@ -65,9 +74,12 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
const {
|
||||
chatId,
|
||||
appId,
|
||||
shareTeamId,
|
||||
// share chat
|
||||
shareId,
|
||||
outLinkUid,
|
||||
// team chat
|
||||
teamId: spaceTeamId,
|
||||
teamToken,
|
||||
stream = false,
|
||||
detail = false,
|
||||
messages = [],
|
||||
@@ -100,136 +112,44 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
if (!question) {
|
||||
throw new Error('Question is empty');
|
||||
}
|
||||
/* auth app permission */
|
||||
|
||||
/*
|
||||
1. auth app permission
|
||||
2. auth balance
|
||||
3. get app
|
||||
4. parse outLink token
|
||||
*/
|
||||
const { teamId, tmbId, user, app, responseDetail, authType, apikey, canWrite, outLinkUserId } =
|
||||
await (async () => {
|
||||
// share chat
|
||||
if (shareId && outLinkUid) {
|
||||
const { teamId, tmbId, user, appId, authType, responseDetail, uid } =
|
||||
await authOutLinkChatStart({
|
||||
shareId,
|
||||
ip: originIp,
|
||||
outLinkUid,
|
||||
question: question.value
|
||||
});
|
||||
const app = await MongoApp.findById(appId);
|
||||
|
||||
if (!app) {
|
||||
return Promise.reject('app is empty');
|
||||
}
|
||||
|
||||
return {
|
||||
teamId,
|
||||
tmbId,
|
||||
user,
|
||||
app,
|
||||
responseDetail,
|
||||
apikey: '',
|
||||
authType,
|
||||
canWrite: false,
|
||||
outLinkUserId: uid
|
||||
};
|
||||
}
|
||||
// team Apps share
|
||||
if (shareTeamId && appId && outLinkUid) {
|
||||
const { user, uid, tmbId } = await authTeamShareChatStart({
|
||||
teamId: shareTeamId,
|
||||
ip: originIp,
|
||||
return authShareChat({
|
||||
shareId,
|
||||
outLinkUid,
|
||||
chatId,
|
||||
ip: originIp,
|
||||
question: question.value
|
||||
});
|
||||
const app = await MongoApp.findById(appId);
|
||||
if (!app) {
|
||||
return Promise.reject('app is empty');
|
||||
}
|
||||
|
||||
return {
|
||||
teamId: shareTeamId,
|
||||
tmbId,
|
||||
user,
|
||||
app,
|
||||
responseDetail: detail,
|
||||
authType: AuthUserTypeEnum.token,
|
||||
apikey: '',
|
||||
canWrite: false,
|
||||
outLinkUserId: uid
|
||||
};
|
||||
}
|
||||
// team space chat
|
||||
if (spaceTeamId && appId && teamToken) {
|
||||
return authTeamSpaceChat({
|
||||
teamId: spaceTeamId,
|
||||
teamToken,
|
||||
appId,
|
||||
chatId
|
||||
});
|
||||
}
|
||||
|
||||
const {
|
||||
appId: apiKeyAppId,
|
||||
teamId,
|
||||
tmbId,
|
||||
authType,
|
||||
apikey
|
||||
} = await authCert({
|
||||
/* parse req: api or token */
|
||||
return authHeaderRequest({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true
|
||||
});
|
||||
|
||||
const { user } = await getUserChatInfoAndAuthTeamPoints(tmbId);
|
||||
|
||||
// openapi key
|
||||
if (authType === AuthUserTypeEnum.apikey) {
|
||||
if (!apiKeyAppId) {
|
||||
return Promise.reject(
|
||||
'Key is error. You need to use the app key rather than the account key.'
|
||||
);
|
||||
}
|
||||
const app = await MongoApp.findById(apiKeyAppId);
|
||||
|
||||
if (!app) {
|
||||
return Promise.reject('app is empty');
|
||||
}
|
||||
|
||||
return {
|
||||
teamId,
|
||||
tmbId,
|
||||
user,
|
||||
app,
|
||||
responseDetail: detail,
|
||||
apikey,
|
||||
authType,
|
||||
canWrite: true
|
||||
};
|
||||
}
|
||||
|
||||
// token auth
|
||||
if (!appId) {
|
||||
return Promise.reject('appId is empty');
|
||||
}
|
||||
const { app, canWrite } = await authApp({
|
||||
req,
|
||||
authToken: true,
|
||||
appId,
|
||||
per: 'r'
|
||||
chatId,
|
||||
detail
|
||||
});
|
||||
|
||||
return {
|
||||
teamId,
|
||||
tmbId,
|
||||
user,
|
||||
app,
|
||||
responseDetail: detail,
|
||||
apikey,
|
||||
authType,
|
||||
canWrite: canWrite || false
|
||||
};
|
||||
})();
|
||||
|
||||
// auth chat permission
|
||||
await autChatCrud({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true,
|
||||
appId: app._id,
|
||||
chatId,
|
||||
shareId,
|
||||
shareTeamId,
|
||||
outLinkUid,
|
||||
per: 'w'
|
||||
});
|
||||
|
||||
// get and concat history
|
||||
const { history } = await getChatItems({
|
||||
appId: app._id,
|
||||
@@ -237,7 +157,6 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
limit: 30,
|
||||
field: `dataId obj value`
|
||||
});
|
||||
|
||||
const concatHistories = history.concat(chatMessages);
|
||||
const responseChatItemId: string | undefined = messages[messages.length - 1].dataId;
|
||||
|
||||
@@ -263,13 +182,14 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
|
||||
// save chat
|
||||
if (chatId) {
|
||||
const isOwnerUse = !shareId && !spaceTeamId && String(tmbId) === String(app.tmbId);
|
||||
await saveChat({
|
||||
chatId,
|
||||
appId: app._id,
|
||||
teamId,
|
||||
tmbId: tmbId,
|
||||
variables,
|
||||
updateUseTime: !shareId && String(tmbId) === String(app.tmbId), // owner update use time
|
||||
updateUseTime: isOwnerUse, // owner update use time
|
||||
shareId,
|
||||
outLinkUid: outLinkUserId,
|
||||
source: (() => {
|
||||
@@ -279,6 +199,9 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
if (authType === 'apikey') {
|
||||
return ChatSourceEnum.api;
|
||||
}
|
||||
if (spaceTeamId) {
|
||||
return ChatSourceEnum.team;
|
||||
}
|
||||
return ChatSourceEnum.online;
|
||||
})(),
|
||||
content: [
|
||||
@@ -299,7 +222,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
addLog.info(`completions running time: ${(Date.now() - startTime) / 1000}s`);
|
||||
|
||||
/* select fe response field */
|
||||
const feResponseData = canWrite ? responseData : selectShareResponse({ responseData });
|
||||
const feResponseData = canWrite ? responseData : selectSimpleChatResponse({ responseData });
|
||||
|
||||
if (stream) {
|
||||
responseWrite({
|
||||
@@ -382,3 +305,162 @@ export const config = {
|
||||
responseLimit: '20mb'
|
||||
}
|
||||
};
|
||||
|
||||
const authShareChat = async ({
|
||||
chatId,
|
||||
...data
|
||||
}: AuthOutLinkChatProps & {
|
||||
shareId: string;
|
||||
chatId?: string;
|
||||
}): Promise<AuthResponseType> => {
|
||||
const { teamId, tmbId, user, appId, authType, responseDetail, uid } =
|
||||
await authOutLinkChatStart(data);
|
||||
const app = await MongoApp.findById(appId).lean();
|
||||
|
||||
if (!app) {
|
||||
return Promise.reject('app is empty');
|
||||
}
|
||||
|
||||
// get chat
|
||||
const chat = await MongoChat.findOne({ appId, chatId }).lean();
|
||||
if (chat && (chat.shareId !== data.shareId || chat.outLinkUid !== uid)) {
|
||||
return Promise.reject(ChatErrEnum.unAuthChat);
|
||||
}
|
||||
|
||||
return {
|
||||
teamId,
|
||||
tmbId,
|
||||
user,
|
||||
app,
|
||||
responseDetail,
|
||||
apikey: '',
|
||||
authType,
|
||||
canWrite: false,
|
||||
outLinkUserId: uid
|
||||
};
|
||||
};
|
||||
const authTeamSpaceChat = async ({
|
||||
appId,
|
||||
teamId,
|
||||
teamToken,
|
||||
chatId
|
||||
}: {
|
||||
appId: string;
|
||||
teamId: string;
|
||||
teamToken: string;
|
||||
chatId?: string;
|
||||
}): Promise<AuthResponseType> => {
|
||||
const { uid } = await authTeamSpaceToken({
|
||||
teamId,
|
||||
teamToken
|
||||
});
|
||||
|
||||
const app = await MongoApp.findById(appId).lean();
|
||||
if (!app) {
|
||||
return Promise.reject('app is empty');
|
||||
}
|
||||
|
||||
const [chat, { user }] = await Promise.all([
|
||||
MongoChat.findOne({ appId, chatId }).lean(),
|
||||
getUserChatInfoAndAuthTeamPoints(app.tmbId)
|
||||
]);
|
||||
|
||||
if (chat && (String(chat.teamId) !== teamId || chat.outLinkUid !== uid)) {
|
||||
return Promise.reject(ChatErrEnum.unAuthChat);
|
||||
}
|
||||
|
||||
return {
|
||||
teamId,
|
||||
tmbId: app.tmbId,
|
||||
user,
|
||||
app,
|
||||
responseDetail: true,
|
||||
authType: AuthUserTypeEnum.outLink,
|
||||
apikey: '',
|
||||
canWrite: false,
|
||||
outLinkUserId: uid
|
||||
};
|
||||
};
|
||||
const authHeaderRequest = async ({
|
||||
req,
|
||||
appId,
|
||||
chatId,
|
||||
detail
|
||||
}: {
|
||||
req: NextApiRequest;
|
||||
appId?: string;
|
||||
chatId?: string;
|
||||
detail?: boolean;
|
||||
}): Promise<AuthResponseType> => {
|
||||
const {
|
||||
appId: apiKeyAppId,
|
||||
teamId,
|
||||
tmbId,
|
||||
authType,
|
||||
apikey,
|
||||
canWrite: apiKeyCanWrite
|
||||
} = await authCert({
|
||||
req,
|
||||
authToken: true,
|
||||
authApiKey: true
|
||||
});
|
||||
|
||||
const { app, canWrite } = await (async () => {
|
||||
if (authType === AuthUserTypeEnum.apikey) {
|
||||
if (!apiKeyAppId) {
|
||||
return Promise.reject(
|
||||
'Key is error. You need to use the app key rather than the account key.'
|
||||
);
|
||||
}
|
||||
const app = await MongoApp.findById(apiKeyAppId);
|
||||
|
||||
if (!app) {
|
||||
return Promise.reject('app is empty');
|
||||
}
|
||||
|
||||
appId = String(app._id);
|
||||
|
||||
return {
|
||||
app,
|
||||
canWrite: apiKeyCanWrite
|
||||
};
|
||||
} else {
|
||||
// token auth
|
||||
if (!appId) {
|
||||
return Promise.reject('appId is empty');
|
||||
}
|
||||
const { app, canWrite } = await authApp({
|
||||
req,
|
||||
authToken: true,
|
||||
appId,
|
||||
per: 'r'
|
||||
});
|
||||
|
||||
return {
|
||||
app,
|
||||
|
||||
canWrite: canWrite
|
||||
};
|
||||
}
|
||||
})();
|
||||
|
||||
const [{ user }, chat] = await Promise.all([
|
||||
getUserChatInfoAndAuthTeamPoints(tmbId),
|
||||
MongoChat.findOne({ appId, chatId }).lean()
|
||||
]);
|
||||
|
||||
if (chat && (String(chat.teamId) !== teamId || String(chat.tmbId) !== tmbId)) {
|
||||
return Promise.reject(ChatErrEnum.unAuthChat);
|
||||
}
|
||||
|
||||
return {
|
||||
teamId,
|
||||
tmbId,
|
||||
user,
|
||||
app,
|
||||
responseDetail: detail,
|
||||
apikey,
|
||||
authType,
|
||||
canWrite
|
||||
};
|
||||
};
|
||||
|
||||
@@ -36,7 +36,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
|
||||
await checkTeamAIPoints(teamId);
|
||||
|
||||
const { charsLength, vectors } = await getVectorsByText({
|
||||
const { tokens, vectors } = await getVectorsByText({
|
||||
input: query,
|
||||
model: getVectorModel(model)
|
||||
});
|
||||
@@ -50,15 +50,15 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
})),
|
||||
model,
|
||||
usage: {
|
||||
prompt_tokens: charsLength,
|
||||
total_tokens: charsLength
|
||||
prompt_tokens: tokens,
|
||||
total_tokens: tokens
|
||||
}
|
||||
});
|
||||
|
||||
const { totalPoints } = pushGenerateVectorUsage({
|
||||
teamId,
|
||||
tmbId,
|
||||
charsLength,
|
||||
tokens,
|
||||
model,
|
||||
billId,
|
||||
source: getUsageSourceByAuthType({ authType })
|
||||
|
||||
@@ -136,7 +136,7 @@ const SelectUsingWayModal = ({ share, onClose }: { share: OutLinkSchema; onClose
|
||||
/>
|
||||
|
||||
{/* config */}
|
||||
<Grid gridTemplateColumns={['repeat(3,1fr)']} gridGap={4} my={5}>
|
||||
<Grid gridTemplateColumns={['repeat(2,1fr)', 'repeat(3,1fr)']} gridGap={4} my={5}>
|
||||
<Flex {...gridItemStyle}>
|
||||
<Box flex={1}>{t('core.app.outLink.Show History')}</Box>
|
||||
<Switch {...register('showHistory')} />
|
||||
|
||||
@@ -14,10 +14,6 @@ import {
|
||||
ModalBody,
|
||||
Input,
|
||||
Switch,
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuList,
|
||||
MenuItem,
|
||||
Link
|
||||
} from '@chakra-ui/react';
|
||||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||
|
||||
@@ -13,7 +13,7 @@ import PermissionIconText from '@/components/support/permission/IconText';
|
||||
import dynamic from 'next/dynamic';
|
||||
import Avatar from '@/components/Avatar';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import TagsEditModal from './tagsEditModal';
|
||||
import TagsEditModal from './TagsEditModal';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
const InfoModal = dynamic(() => import('../InfoModal'));
|
||||
|
||||
@@ -156,9 +156,7 @@ const AppCard = ({ appId }: { appId: string }) => {
|
||||
{settingAppInfo && (
|
||||
<InfoModal defaultApp={settingAppInfo} onClose={() => setSettingAppInfo(undefined)} />
|
||||
)}
|
||||
{TeamTagsSet && (
|
||||
<TagsEditModal appDetail={appDetail} onClose={() => setTeamTagsSet(undefined)} />
|
||||
)}
|
||||
{TeamTagsSet && <TagsEditModal onClose={() => setTeamTagsSet(undefined)} />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
import React, { useState } from 'react';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import {
|
||||
Button,
|
||||
Flex,
|
||||
Box,
|
||||
ModalFooter,
|
||||
ModalBody,
|
||||
Menu,
|
||||
MenuButton,
|
||||
HStack,
|
||||
Tag,
|
||||
TagCloseButton,
|
||||
MenuList,
|
||||
Input,
|
||||
MenuOptionGroup,
|
||||
MenuItemOption,
|
||||
TagLabel
|
||||
} from '@chakra-ui/react';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { useAppStore } from '@/web/core/app/store/useAppStore';
|
||||
import { useRequest } from '@/web/common/hooks/useRequest';
|
||||
import { getTeamsTags } from '@/web/support/user/team/api';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
const TagsEditModal = ({ onClose }: { onClose: () => void }) => {
|
||||
const { t } = useTranslation();
|
||||
const { appDetail } = useAppStore();
|
||||
const { toast } = useToast();
|
||||
const { replaceAppDetail } = useAppStore();
|
||||
const [selectedTags, setSelectedTags] = useState<string[]>(appDetail?.teamTags || []);
|
||||
|
||||
// submit config
|
||||
const { mutate: saveSubmitSuccess, isLoading: btnLoading } = useRequest({
|
||||
mutationFn: async () => {
|
||||
await replaceAppDetail(appDetail._id, {
|
||||
teamTags: selectedTags
|
||||
});
|
||||
},
|
||||
onSuccess() {
|
||||
onClose();
|
||||
toast({
|
||||
title: t('common.Update Success'),
|
||||
status: 'success'
|
||||
});
|
||||
},
|
||||
errorToast: t('common.Update Failed')
|
||||
});
|
||||
|
||||
const { data: teamTags = [] } = useQuery(['getTeamsTags'], getTeamsTags);
|
||||
const [searchKey, setSearchKey] = useState('');
|
||||
const filterTeamTags = teamTags.filter((item) => {
|
||||
return item.label.includes(searchKey);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<MyModal
|
||||
style={{ width: '900px' }}
|
||||
isOpen
|
||||
onClose={onClose}
|
||||
iconSrc="/imgs/module/ai.svg"
|
||||
title={t('core.app.Team tags')}
|
||||
>
|
||||
<ModalBody>
|
||||
<Box mb={3} fontWeight="semibold">
|
||||
{t('团队标签')}
|
||||
</Box>
|
||||
<Menu closeOnSelect={false}>
|
||||
<MenuButton className="menu-btn" maxHeight={'250'} w={'100%'}>
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
borderWidth={'1px'}
|
||||
borderColor={'borderColor.base'}
|
||||
borderRadius={'md'}
|
||||
px={3}
|
||||
py={2}
|
||||
flexWrap={'wrap'}
|
||||
minH={'50px'}
|
||||
gap={3}
|
||||
>
|
||||
{teamTags.map((item, index) => {
|
||||
const key: string = item?.key;
|
||||
if (selectedTags.indexOf(key as never) > -1) {
|
||||
return (
|
||||
<Tag key={index} size={'md'} colorScheme="blue" borderRadius="full">
|
||||
<TagLabel>{item.label}</TagLabel>
|
||||
<TagCloseButton />
|
||||
</Tag>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</Flex>
|
||||
</MenuButton>
|
||||
<MenuList>
|
||||
<Box px={2}>
|
||||
<Input
|
||||
m={'auto'}
|
||||
placeholder={t('core.app.Search team tags')}
|
||||
value={searchKey}
|
||||
onChange={(e) => {
|
||||
setSearchKey(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<Box maxH={'300px'} overflow={'auto'} mt={1}>
|
||||
<MenuOptionGroup
|
||||
defaultValue={selectedTags}
|
||||
type="checkbox"
|
||||
onChange={(e) => {
|
||||
//@ts-ignore
|
||||
setSelectedTags(e);
|
||||
}}
|
||||
>
|
||||
{filterTeamTags.map((item) => {
|
||||
return (
|
||||
<MenuItemOption
|
||||
key={item.key}
|
||||
value={item.key}
|
||||
borderRadius={'md'}
|
||||
_hover={{ bg: 'myGray.100' }}
|
||||
>
|
||||
{item?.label}
|
||||
</MenuItemOption>
|
||||
);
|
||||
})}
|
||||
</MenuOptionGroup>
|
||||
</Box>
|
||||
</MenuList>
|
||||
</Menu>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant={'whiteBase'} mr={3} onClick={onClose}>
|
||||
{t('common.Close')}
|
||||
</Button>
|
||||
<Button isLoading={btnLoading} onClick={(e) => saveSubmitSuccess(e)}>
|
||||
{t('common.Save')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
export default TagsEditModal;
|
||||
@@ -1,103 +0,0 @@
|
||||
import React, { useCallback, useState, useEffect } from 'react';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { Button, Flex, Box, ModalFooter, ModalBody } from '@chakra-ui/react';
|
||||
import TagsEdit from '@/components/TagEdit';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { AppSchema } from '@fastgpt/global/core/app/type.d';
|
||||
import { TeamTagsSchema } from '@fastgpt/global/support/user/team/type';
|
||||
import { useAppStore } from '@/web/core/app/store/useAppStore';
|
||||
import { useRequest } from '@/web/common/hooks/useRequest';
|
||||
import { getTeamsTags } from '@/web/support/user/team/api';
|
||||
const TagsEditModal = ({ appDetail, onClose }: { appDetail?: any; onClose: () => void }) => {
|
||||
const { t } = useTranslation();
|
||||
const [teamsTags, setTeamTags] = useState<Array<TeamTagsSchema>>([]);
|
||||
const [selectedTags, setSelectedTags] = useState(appDetail?.teamTags);
|
||||
const { toast } = useToast();
|
||||
const { replaceAppDetail } = useAppStore();
|
||||
|
||||
// submit config
|
||||
const { mutate: saveSubmitSuccess, isLoading: btnLoading } = useRequest({
|
||||
mutationFn: async () => {
|
||||
await replaceAppDetail(appDetail._id, {
|
||||
teamTags: selectedTags
|
||||
});
|
||||
},
|
||||
onSuccess() {
|
||||
onClose();
|
||||
toast({
|
||||
title: t('common.Update Success'),
|
||||
status: 'success'
|
||||
});
|
||||
},
|
||||
errorToast: t('common.Update Failed')
|
||||
});
|
||||
//
|
||||
|
||||
// // 点击选择标签
|
||||
// const clickTag = (tagId :Number) => {
|
||||
// const index = selectedTags.indexOf(tagId);
|
||||
// if (index === -1) {
|
||||
// // 如果 num 不在数组 arr 中,添加它
|
||||
// setSelectedTags([tagId,...selectedTags])
|
||||
// } else {
|
||||
// const _selectedTags = [...selectedTags];
|
||||
// _selectedTags.splice(index, 1);
|
||||
// console.log('_selectedTags',_selectedTags);
|
||||
// // 如果 num 已经在数组 arr 中,移除它
|
||||
// setSelectedTags(_selectedTags);
|
||||
// }
|
||||
// }
|
||||
|
||||
useEffect(() => {
|
||||
// get team tags
|
||||
getTeamsTags(appDetail?.teamId).then((res: any) => {
|
||||
setTeamTags(res?.list);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<MyModal
|
||||
style={{ width: '900px' }}
|
||||
isOpen
|
||||
onClose={onClose}
|
||||
iconSrc="/imgs/module/ai.svg"
|
||||
title={'标签管理'}
|
||||
>
|
||||
<ModalBody>
|
||||
{/* <HStack spacing={2}>
|
||||
{teamsTags.map((item,index) => {
|
||||
return <Tag
|
||||
key={index}
|
||||
size={'md'}
|
||||
variant='outline'
|
||||
colorScheme={selectedTags.indexOf(item._id) > -1 ? 'green':'blue' }
|
||||
onClick={() => clickTag(item._id)}
|
||||
>
|
||||
{item.label}
|
||||
</Tag>
|
||||
})}
|
||||
</HStack> */}
|
||||
<Flex width={'100%'} alignItems={'center'}>
|
||||
<Box mb={3} mr={3} fontWeight="semibold">
|
||||
{t('团队标签')}
|
||||
</Box>
|
||||
<TagsEdit
|
||||
defaultValues={selectedTags}
|
||||
teamsTags={teamsTags}
|
||||
setSelectedTags={(item: Array<string>) => setSelectedTags(item)}
|
||||
/>
|
||||
</Flex>
|
||||
<ModalFooter>
|
||||
<Button variant={'whiteBase'} mr={3} onClick={onClose}>
|
||||
{t('common.Close')}
|
||||
</Button>
|
||||
<Button isLoading={btnLoading} onClick={(e) => saveSubmitSuccess(e)}>
|
||||
{t('common.Save')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalBody>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
export default TagsEditModal;
|
||||
@@ -1,45 +1,52 @@
|
||||
import React from 'react';
|
||||
import { Flex, Box, IconButton } from '@chakra-ui/react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import Avatar from '@/components/Avatar';
|
||||
import { useAppStore } from '@/web/core/app/store/useAppStore';
|
||||
import { AppListItemType } from '@fastgpt/global/core/app/type';
|
||||
|
||||
const SliderApps = ({ appId }: { appId: string }) => {
|
||||
const SliderApps = ({
|
||||
showExist = true,
|
||||
apps,
|
||||
activeAppId
|
||||
}: {
|
||||
showExist?: boolean;
|
||||
apps: AppListItemType[];
|
||||
activeAppId: string;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
const { myApps, loadMyApps } = useAppStore();
|
||||
|
||||
useQuery(['loadModels'], () => loadMyApps(false));
|
||||
|
||||
return (
|
||||
<Flex flexDirection={'column'} h={'100%'}>
|
||||
<Box px={5} py={4}>
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
cursor={'pointer'}
|
||||
py={2}
|
||||
px={3}
|
||||
borderRadius={'md'}
|
||||
_hover={{ bg: 'myGray.200' }}
|
||||
onClick={() => router.push('/app/list')}
|
||||
>
|
||||
<IconButton
|
||||
mr={3}
|
||||
icon={<MyIcon name={'common/backFill'} w={'18px'} color={'primary.500'} />}
|
||||
bg={'white'}
|
||||
boxShadow={'1px 1px 9px rgba(0,0,0,0.15)'}
|
||||
size={'smSquare'}
|
||||
borderRadius={'50%'}
|
||||
aria-label={''}
|
||||
/>
|
||||
{t('core.chat.Exit Chat')}
|
||||
</Flex>
|
||||
{showExist && (
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
cursor={'pointer'}
|
||||
py={2}
|
||||
px={3}
|
||||
borderRadius={'md'}
|
||||
_hover={{ bg: 'myGray.200' }}
|
||||
onClick={() => router.push('/app/list')}
|
||||
>
|
||||
<IconButton
|
||||
mr={3}
|
||||
icon={<MyIcon name={'common/backFill'} w={'18px'} color={'primary.500'} />}
|
||||
bg={'white'}
|
||||
boxShadow={'1px 1px 9px rgba(0,0,0,0.15)'}
|
||||
size={'smSquare'}
|
||||
borderRadius={'50%'}
|
||||
aria-label={''}
|
||||
/>
|
||||
{t('core.chat.Exit Chat')}
|
||||
</Flex>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Box flex={'1 0 0'} h={0} px={5} overflow={'overlay'}>
|
||||
{myApps.map((item) => (
|
||||
{apps.map((item) => (
|
||||
<Flex
|
||||
key={item._id}
|
||||
py={2}
|
||||
@@ -48,7 +55,7 @@ const SliderApps = ({ appId }: { appId: string }) => {
|
||||
cursor={'pointer'}
|
||||
borderRadius={'md'}
|
||||
alignItems={'center'}
|
||||
{...(item._id === appId
|
||||
{...(item._id === activeAppId
|
||||
? {
|
||||
bg: 'white',
|
||||
boxShadow: 'md'
|
||||
@@ -60,6 +67,8 @@ const SliderApps = ({ appId }: { appId: string }) => {
|
||||
onClick: () => {
|
||||
router.replace({
|
||||
query: {
|
||||
...router.query,
|
||||
chatId: '',
|
||||
appId: item._id
|
||||
}
|
||||
});
|
||||
|
||||
@@ -129,6 +129,8 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
||||
[appId, chatId, histories, pushHistory, router, setChatData, t, updateHistory]
|
||||
);
|
||||
|
||||
useQuery(['loadModels'], () => loadMyApps(false));
|
||||
|
||||
// get chat app info
|
||||
const loadChatInfo = useCallback(
|
||||
async ({
|
||||
@@ -251,7 +253,7 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
||||
{/* pc show myself apps */}
|
||||
{isPc && (
|
||||
<Box borderRight={theme.borders.base} w={'220px'} flexShrink={0}>
|
||||
<SliderApps appId={appId} />
|
||||
<SliderApps apps={myApps} activeAppId={appId} />
|
||||
</Box>
|
||||
)}
|
||||
|
||||
|
||||
@@ -22,22 +22,29 @@ import { serviceSideProps } from '@/web/common/utils/i18n';
|
||||
import { checkChatSupportSelectFileByChatModels } from '@/web/core/chat/utils';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { getInitOutLinkChatInfo } from '@/web/core/chat/api';
|
||||
import { POST } from '@/web/common/api/request';
|
||||
import { chatContentReplaceBlock } from '@fastgpt/global/core/chat/utils';
|
||||
import { useChatStore } from '@/web/core/chat/storeChat';
|
||||
import { ChatStatusEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import MyBox from '@/components/common/MyBox';
|
||||
import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
|
||||
import { OutLinkWithAppType } from '@fastgpt/global/support/outLink/type';
|
||||
|
||||
const OutLink = ({
|
||||
shareId,
|
||||
chatId,
|
||||
showHistory,
|
||||
authToken
|
||||
authToken,
|
||||
appName,
|
||||
appIntro,
|
||||
appAvatar
|
||||
}: {
|
||||
shareId: string;
|
||||
chatId: string;
|
||||
showHistory: '0' | '1';
|
||||
authToken?: string;
|
||||
appName?: string;
|
||||
appIntro?: string;
|
||||
appAvatar?: string;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
@@ -150,7 +157,18 @@ const OutLink = ({
|
||||
|
||||
return { responseText, responseData, isNewChat: forbidRefresh.current };
|
||||
},
|
||||
[chatId, shareId, outLinkUid, setChatData, appId, pushHistory, router, histories, updateHistory]
|
||||
[
|
||||
chatId,
|
||||
shareId,
|
||||
outLinkUid,
|
||||
t,
|
||||
setChatData,
|
||||
appId,
|
||||
pushHistory,
|
||||
router,
|
||||
histories,
|
||||
updateHistory
|
||||
]
|
||||
);
|
||||
|
||||
const loadChatInfo = useCallback(
|
||||
@@ -235,28 +253,6 @@ const OutLink = ({
|
||||
setIdEmbed(window !== top);
|
||||
}, []);
|
||||
|
||||
// todo:4.6.4 init: update local chat history, add outLinkUid
|
||||
useEffect(() => {
|
||||
const activeHistory = shareChatHistory.filter((item) => !item.delete);
|
||||
if (!localUId || !shareId || activeHistory.length === 0) return;
|
||||
(async () => {
|
||||
try {
|
||||
await POST('/core/chat/initLocalShareHistoryV464', {
|
||||
shareId,
|
||||
outLinkUid: localUId,
|
||||
chatIds: shareChatHistory.map((item) => item.chatId)
|
||||
});
|
||||
clearLocalHistory();
|
||||
// router.reload();
|
||||
} catch (error) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: getErrText(error, t('core.shareChat.Init Error'))
|
||||
});
|
||||
}
|
||||
})();
|
||||
}, [clearLocalHistory, localUId, router, shareChatHistory, shareId, t, toast]);
|
||||
|
||||
return (
|
||||
<PageContainer
|
||||
{...(isEmbed
|
||||
@@ -264,7 +260,9 @@ const OutLink = ({
|
||||
: { p: [0, 5] })}
|
||||
>
|
||||
<Head>
|
||||
<title>{chatData.app.name}</title>
|
||||
<title>{appName || chatData.app?.name}</title>
|
||||
<meta name="description" content={appIntro} />
|
||||
<link rel="icon" href={appAvatar || chatData.app?.avatar} />
|
||||
</Head>
|
||||
<MyBox
|
||||
isLoading={isFetching}
|
||||
@@ -397,12 +395,31 @@ export async function getServerSideProps(context: any) {
|
||||
const showHistory = context?.query?.showHistory || '1';
|
||||
const authToken = context?.query?.authToken || '';
|
||||
|
||||
const app = await (async () => {
|
||||
try {
|
||||
const app = (await MongoOutLink.findOne(
|
||||
{
|
||||
shareId
|
||||
},
|
||||
'appId'
|
||||
)
|
||||
.populate('appId', 'name avatar intro')
|
||||
.lean()) as OutLinkWithAppType;
|
||||
return app;
|
||||
} catch (error) {
|
||||
return undefined;
|
||||
}
|
||||
})();
|
||||
|
||||
return {
|
||||
props: {
|
||||
shareId,
|
||||
chatId,
|
||||
showHistory,
|
||||
authToken,
|
||||
appName: app?.appId?.name,
|
||||
appAvatar: app?.appId?.avatar,
|
||||
appIntro: app?.appId?.intro,
|
||||
...(await serviceSideProps(context))
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useRef } from 'react';
|
||||
import Head from 'next/head';
|
||||
import { getTeamChatInfo } from '@/web/core/chat/api';
|
||||
import { useRouter } from 'next/router';
|
||||
@@ -11,13 +11,12 @@ import {
|
||||
DrawerContent,
|
||||
useTheme
|
||||
} from '@chakra-ui/react';
|
||||
import Avatar from '@/components/Avatar';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import SideBar from '@/components/SideBar';
|
||||
import PageContainer from '@/components/PageContainer';
|
||||
import { getChatListById } from '@/web/core/chat/api';
|
||||
import { getMyTokensApps } from '@/web/core/chat/api';
|
||||
import ChatHistorySlider from './components/ChatHistorySlider';
|
||||
import ChatHeader from './components/ChatHeader';
|
||||
import { serviceSideProps } from '@/web/common/utils/i18n';
|
||||
@@ -25,112 +24,49 @@ import { useTranslation } from 'next-i18next';
|
||||
import { checkChatSupportSelectFileByChatModels } from '@/web/core/chat/utils';
|
||||
import { useChatStore } from '@/web/core/chat/storeChat';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
import { useLoading } from '@/web/common/hooks/useLoading';
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
|
||||
import ChatBox, { type ComponentRef, type StartChatFnProps } from '@/components/ChatBox';
|
||||
import { streamFetch } from '@/web/common/api/fetch';
|
||||
import { useTeamShareChatStore } from '@/web/core/chat/storeTeamChat';
|
||||
import type {
|
||||
ChatHistoryItemType,
|
||||
chatAppListSchema,
|
||||
teamInfoType
|
||||
} from '@fastgpt/global/core/chat/type.d';
|
||||
import type { ChatHistoryItemType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { chatContentReplaceBlock } from '@fastgpt/global/core/chat/utils';
|
||||
import { ChatStatusEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { POST } from '@/web/common/api/request';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import MyBox from '@/components/common/MyBox';
|
||||
import SliderApps from './components/SliderApps';
|
||||
|
||||
const OutLink = ({
|
||||
shareTeamId,
|
||||
teamId,
|
||||
appId,
|
||||
chatId,
|
||||
authToken
|
||||
teamToken
|
||||
}: {
|
||||
shareTeamId: string;
|
||||
teamId: string;
|
||||
appId: string;
|
||||
chatId: string;
|
||||
authToken: string;
|
||||
teamToken: string;
|
||||
}) => {
|
||||
type routerQueryType = {
|
||||
chatId?: string;
|
||||
appId?: string;
|
||||
shareTeamId: string;
|
||||
authToken?: string;
|
||||
};
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
const { toast } = useToast();
|
||||
const theme = useTheme();
|
||||
const [myApps, setMyApps] = useState<Array<any>>([]);
|
||||
const { isPc } = useSystemStore();
|
||||
const ChatBoxRef = useRef<ComponentRef>(null);
|
||||
const [teamInfo, setTeamInfo] = useState<teamInfoType>();
|
||||
const { Loading, setIsLoading } = useLoading();
|
||||
const forbidRefresh = useRef(false);
|
||||
|
||||
const { isOpen: isOpenSlider, onClose: onCloseSlider, onOpen: onOpenSlider } = useDisclosure();
|
||||
const {
|
||||
chatData,
|
||||
setChatData,
|
||||
histories,
|
||||
loadHistories,
|
||||
lastChatAppId,
|
||||
setLastChatAppId,
|
||||
lastChatId,
|
||||
setLastChatId,
|
||||
pushHistory,
|
||||
updateHistory,
|
||||
delOneHistory,
|
||||
chatData,
|
||||
setChatData,
|
||||
delOneHistoryItem,
|
||||
clearHistories
|
||||
} = useChatStore();
|
||||
const {
|
||||
localUId,
|
||||
teamShareChatHistory, // abandon
|
||||
clearLocalHistory // abandon
|
||||
} = useTeamShareChatStore();
|
||||
|
||||
const outLinkUid: string = authToken || localUId;
|
||||
|
||||
// 纯网络获取流程
|
||||
const loadApps = useCallback(async () => {
|
||||
try {
|
||||
if (!shareTeamId) {
|
||||
toast({
|
||||
status: 'error',
|
||||
title: t('core.chat.You need to a chat app')
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 根据获取历史记录列表
|
||||
const res = await getChatListById({ shareTeamId, authToken });
|
||||
const { apps, teamInfo } = res;
|
||||
setMyApps(apps);
|
||||
setTeamInfo(teamInfo);
|
||||
if (apps.length <= 0) {
|
||||
return toast({
|
||||
status: 'error',
|
||||
title: t('core.chat.You need to a chat app')
|
||||
});
|
||||
}
|
||||
if (!apps.find((obj) => obj._id === appId)) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: 'you do not have this App'
|
||||
});
|
||||
router.replace({
|
||||
query: {
|
||||
appId: apps[0]?._id,
|
||||
shareTeamId,
|
||||
authToken: authToken
|
||||
} as routerQueryType
|
||||
});
|
||||
}
|
||||
} catch (error: any) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: error?.message
|
||||
});
|
||||
}
|
||||
}, [appId, authToken, router, shareTeamId, t, toast]);
|
||||
|
||||
const startChat = useCallback(
|
||||
async ({ messages, controller, generatingMessage, variables }: StartChatFnProps) => {
|
||||
@@ -142,14 +78,14 @@ const OutLink = ({
|
||||
messages: prompts,
|
||||
variables,
|
||||
appId,
|
||||
shareTeamId,
|
||||
outLinkUid: outLinkUid,
|
||||
teamId,
|
||||
teamToken,
|
||||
chatId: completionChatId
|
||||
},
|
||||
onMessage: generatingMessage,
|
||||
abortCtrl: controller
|
||||
});
|
||||
|
||||
console.log(responseData);
|
||||
const newTitle =
|
||||
chatContentReplaceBlock(prompts[0].content).slice(0, 20) ||
|
||||
prompts[1]?.value?.slice(0, 20) ||
|
||||
@@ -169,11 +105,9 @@ const OutLink = ({
|
||||
forbidRefresh.current = true;
|
||||
router.replace({
|
||||
query: {
|
||||
chatId: completionChatId,
|
||||
appId,
|
||||
shareTeamId,
|
||||
authToken: authToken
|
||||
} as routerQueryType
|
||||
...router.query,
|
||||
chatId: completionChatId
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@@ -183,7 +117,9 @@ const OutLink = ({
|
||||
updateHistory({
|
||||
...currentChat,
|
||||
updateTime: new Date(),
|
||||
title: newTitle
|
||||
title: newTitle,
|
||||
teamId,
|
||||
teamToken
|
||||
});
|
||||
}
|
||||
// update chat window
|
||||
@@ -195,227 +131,146 @@ const OutLink = ({
|
||||
|
||||
return { responseText, responseData, isNewChat: forbidRefresh.current };
|
||||
},
|
||||
[appId, chatId, histories, pushHistory, router, setChatData, updateHistory]
|
||||
[
|
||||
appId,
|
||||
teamToken,
|
||||
chatId,
|
||||
histories,
|
||||
pushHistory,
|
||||
router,
|
||||
setChatData,
|
||||
teamId,
|
||||
t,
|
||||
updateHistory
|
||||
]
|
||||
);
|
||||
|
||||
const { isFetching } = useQuery(['init', appId, shareTeamId], async () => {
|
||||
console.log('res', 3);
|
||||
if (!shareTeamId) {
|
||||
/* replace router query to last chat */
|
||||
useEffect(() => {
|
||||
if ((!chatId || !appId) && (lastChatId || lastChatAppId)) {
|
||||
router.replace({
|
||||
query: {
|
||||
...router.query,
|
||||
chatId: chatId || lastChatId,
|
||||
appId: appId || lastChatAppId
|
||||
}
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
// get chat app list
|
||||
const loadApps = useCallback(async () => {
|
||||
try {
|
||||
const apps = await getMyTokensApps({ teamId, teamToken });
|
||||
|
||||
if (apps.length <= 0) {
|
||||
toast({
|
||||
status: 'error',
|
||||
title: t('core.chat.You need to a chat app')
|
||||
});
|
||||
return [];
|
||||
}
|
||||
|
||||
// if app id not exist, redirect to first app
|
||||
if (!appId || !apps.find((item) => item._id === appId)) {
|
||||
router.replace({
|
||||
query: {
|
||||
...router.query,
|
||||
appId: apps[0]?._id
|
||||
}
|
||||
});
|
||||
}
|
||||
return apps;
|
||||
} catch (error: any) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: getErrText(error)
|
||||
});
|
||||
}
|
||||
return [];
|
||||
}, [appId, teamToken, router, teamId, t, toast]);
|
||||
const { data: myApps = [], isLoading: isLoadingApps } = useQuery(['initApps', teamId], () => {
|
||||
if (!teamId) {
|
||||
toast({
|
||||
status: 'error',
|
||||
title: t('core.chat.You need to a chat app')
|
||||
title: t('support.user.team.tag.Have not opened')
|
||||
});
|
||||
return;
|
||||
}
|
||||
return shareTeamId && loadApps();
|
||||
return loadApps();
|
||||
});
|
||||
|
||||
// load histories
|
||||
useQuery(['loadHistories', appId], () => {
|
||||
if (shareTeamId && appId) {
|
||||
return loadHistories({ appId, outLinkUid });
|
||||
if (teamId && appId) {
|
||||
return loadHistories({ teamId, appId, teamToken: teamToken });
|
||||
}
|
||||
return;
|
||||
});
|
||||
// 初始化聊天框
|
||||
useQuery(['init', { appId, chatId }], () => {
|
||||
if (!shareTeamId) {
|
||||
toast({
|
||||
status: 'error',
|
||||
title: t('core.chat.You need to a chat app')
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (myApps.length > 0 && myApps.findIndex((obj) => obj._id === appId) === -1) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: 'you do not have this App'
|
||||
});
|
||||
return;
|
||||
}
|
||||
// pc: redirect to latest model chat
|
||||
if (!appId && lastChatAppId) {
|
||||
return router.replace({
|
||||
query: {
|
||||
appId: lastChatAppId,
|
||||
chatId: lastChatId,
|
||||
shareTeamId,
|
||||
authToken: authToken
|
||||
} as routerQueryType
|
||||
});
|
||||
}
|
||||
if (!appId && myApps[0]) {
|
||||
return router.replace({
|
||||
query: {
|
||||
appId: myApps[0]._id,
|
||||
chatId: lastChatId,
|
||||
shareTeamId,
|
||||
authToken: authToken
|
||||
} as routerQueryType
|
||||
});
|
||||
}
|
||||
if (!appId) {
|
||||
(async () => {
|
||||
const { apps = [] } = await getChatListById({ shareTeamId, authToken });
|
||||
setMyApps(apps);
|
||||
if (apps.length === 0) {
|
||||
toast({
|
||||
status: 'error',
|
||||
title: t('core.chat.You need to a chat app')
|
||||
});
|
||||
} else {
|
||||
router.replace({
|
||||
query: {
|
||||
appId: apps[0]._id,
|
||||
chatId: lastChatId,
|
||||
shareTeamId,
|
||||
authToken: authToken
|
||||
} as routerQueryType
|
||||
});
|
||||
}
|
||||
})();
|
||||
return;
|
||||
}
|
||||
|
||||
// store id
|
||||
appId && setLastChatAppId(appId);
|
||||
setLastChatId(chatId);
|
||||
return loadChatInfo({
|
||||
appId,
|
||||
chatId,
|
||||
loading: appId !== chatData.appId
|
||||
});
|
||||
});
|
||||
|
||||
// get chat app info
|
||||
const loadChatInfo = useCallback(
|
||||
async ({
|
||||
appId,
|
||||
chatId,
|
||||
loading = false
|
||||
}: {
|
||||
appId: string;
|
||||
chatId: string;
|
||||
loading?: boolean;
|
||||
}) => {
|
||||
try {
|
||||
if (!shareTeamId) {
|
||||
toast({
|
||||
status: 'error',
|
||||
title: t('core.chat.You need to a chat app')
|
||||
});
|
||||
return;
|
||||
}
|
||||
loading && setIsLoading(true);
|
||||
const res = await getTeamChatInfo({ appId, chatId, outLinkUid });
|
||||
console.log('res', res);
|
||||
const history = res.history.map((item) => ({
|
||||
...item,
|
||||
status: ChatStatusEnum.finish
|
||||
}));
|
||||
const loadChatInfo = useCallback(async () => {
|
||||
try {
|
||||
const res = await getTeamChatInfo({ teamId, appId, chatId, teamToken: teamToken });
|
||||
|
||||
setChatData({
|
||||
...res,
|
||||
history
|
||||
});
|
||||
const history = res.history.map((item) => ({
|
||||
...item,
|
||||
status: ChatStatusEnum.finish
|
||||
}));
|
||||
|
||||
// have records.
|
||||
ChatBoxRef.current?.resetHistory(history);
|
||||
ChatBoxRef.current?.resetVariables(res.variables);
|
||||
if (res.history.length > 0) {
|
||||
setTimeout(() => {
|
||||
ChatBoxRef.current?.scrollToBottom('auto');
|
||||
}, 500);
|
||||
}
|
||||
} catch (e: any) {
|
||||
// reset all chat tore
|
||||
setLastChatAppId('');
|
||||
setLastChatId('');
|
||||
toast({
|
||||
title: t('core.chat.Failed to initialize chat'),
|
||||
status: 'error'
|
||||
});
|
||||
if (e?.code === 501) {
|
||||
//router.replace('/app/list');
|
||||
} else if (chatId) {
|
||||
router.replace({
|
||||
query: {
|
||||
...router.query,
|
||||
chatId: ''
|
||||
} as routerQueryType
|
||||
});
|
||||
}
|
||||
setChatData({
|
||||
...res,
|
||||
history
|
||||
});
|
||||
|
||||
// have records.
|
||||
ChatBoxRef.current?.resetHistory(history);
|
||||
ChatBoxRef.current?.resetVariables(res.variables);
|
||||
|
||||
if (res.history.length > 0) {
|
||||
setTimeout(() => {
|
||||
ChatBoxRef.current?.scrollToBottom('auto');
|
||||
}, 500);
|
||||
}
|
||||
setIsLoading(false);
|
||||
} catch (e: any) {
|
||||
toast({
|
||||
title: t('core.chat.Failed to initialize chat'),
|
||||
status: 'error'
|
||||
});
|
||||
if (chatId) {
|
||||
router.replace({
|
||||
query: {
|
||||
...router.query,
|
||||
chatId: ''
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}, [teamId, appId, chatId, teamToken, setChatData, toast, t, router]);
|
||||
const { isFetching } = useQuery(['init', teamId, appId, chatId], () => {
|
||||
if (forbidRefresh.current) {
|
||||
forbidRefresh.current = false;
|
||||
return null;
|
||||
},
|
||||
[setIsLoading, setChatData, router, setLastChatAppId, setLastChatId, toast]
|
||||
);
|
||||
// 监测路由改变
|
||||
useEffect(() => {
|
||||
const activeHistory = teamShareChatHistory.filter((item) => !item.delete);
|
||||
if (!localUId || !shareTeamId || activeHistory.length === 0) return;
|
||||
(async () => {
|
||||
try {
|
||||
await POST('/core/chat/initLocalShareHistoryV464', {
|
||||
outLinkUid: localUId,
|
||||
chatIds: teamShareChatHistory.map((item) => item.chatId)
|
||||
});
|
||||
clearLocalHistory();
|
||||
// router.reload();
|
||||
} catch (error) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: t('core.shareChat.Init Error')
|
||||
});
|
||||
}
|
||||
})();
|
||||
}, [clearLocalHistory, localUId, router, teamShareChatHistory, shareTeamId, t, toast]);
|
||||
}
|
||||
if (teamId && appId) {
|
||||
return loadChatInfo();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
return (
|
||||
<Flex h={'100%'}>
|
||||
<MyBox display={'flex'} h={'100%'} isLoading={isLoadingApps || isFetching}>
|
||||
<Head>
|
||||
<title>{chatData.app.name}</title>
|
||||
</Head>
|
||||
{/* pc show myself apps */}
|
||||
<Box borderRight={theme.borders.base} w={'220px'} flexShrink={0}>
|
||||
<Flex flexDirection={'column'} h={'100%'}>
|
||||
<Box flex={'1 0 0'} h={0} px={5} py={4} overflow={'overlay'}>
|
||||
{myApps &&
|
||||
myApps.map((item) => (
|
||||
<Flex
|
||||
key={item._id}
|
||||
py={2}
|
||||
px={3}
|
||||
mb={3}
|
||||
cursor={'pointer'}
|
||||
borderRadius={'md'}
|
||||
alignItems={'center'}
|
||||
{...(item._id === appId
|
||||
? {
|
||||
bg: 'white',
|
||||
boxShadow: 'md'
|
||||
}
|
||||
: {
|
||||
_hover: {
|
||||
bg: 'myGray.200'
|
||||
},
|
||||
onClick: () => {
|
||||
router.replace({
|
||||
query: {
|
||||
appId: item._id,
|
||||
shareTeamId,
|
||||
authToken: authToken
|
||||
} as routerQueryType
|
||||
});
|
||||
}
|
||||
})}
|
||||
>
|
||||
<Avatar src={item.avatar} w={'24px'} />
|
||||
<Box ml={2} className={'textEllipsis'}>
|
||||
{item.name}
|
||||
</Box>
|
||||
</Flex>
|
||||
))}
|
||||
</Box>
|
||||
</Flex>
|
||||
</Box>
|
||||
{isPc && (
|
||||
<Box borderRight={theme.borders.base} w={'220px'} flexShrink={0}>
|
||||
<SliderApps showExist={false} apps={myApps} activeAppId={appId} />
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<PageContainer flex={'1 0 0'} w={0} p={[0, '16px']} position={'relative'}>
|
||||
<Flex h={'100%'} flexDirection={['column', 'row']} bg={'white'}>
|
||||
{((children: React.ReactNode) => {
|
||||
@@ -449,36 +304,35 @@ const OutLink = ({
|
||||
onChangeChat={(chatId) => {
|
||||
router.replace({
|
||||
query: {
|
||||
chatId: chatId || '',
|
||||
appId,
|
||||
shareTeamId,
|
||||
authToken: authToken
|
||||
} as routerQueryType
|
||||
...router.query,
|
||||
chatId: chatId || ''
|
||||
}
|
||||
});
|
||||
if (!isPc) {
|
||||
onCloseSlider();
|
||||
}
|
||||
}}
|
||||
onDelHistory={(e) => delOneHistory({ ...e, appId })}
|
||||
onDelHistory={(e) => delOneHistory({ ...e, appId, teamId, teamToken })}
|
||||
onClearHistory={() => {
|
||||
clearHistories({ appId });
|
||||
clearHistories({ appId, teamId, teamToken });
|
||||
router.replace({
|
||||
query: {
|
||||
appId,
|
||||
shareTeamId,
|
||||
authToken: authToken
|
||||
} as routerQueryType
|
||||
...router.query,
|
||||
chatId: ''
|
||||
}
|
||||
});
|
||||
}}
|
||||
onSetHistoryTop={(e) => {
|
||||
updateHistory({ ...e, appId });
|
||||
updateHistory({ ...e, teamId, teamToken, appId });
|
||||
}}
|
||||
onSetCustomTitle={async (e) => {
|
||||
updateHistory({
|
||||
appId,
|
||||
chatId: e.chatId,
|
||||
title: e.title,
|
||||
customTitle: e.title
|
||||
customTitle: e.title,
|
||||
teamId,
|
||||
teamToken
|
||||
});
|
||||
}}
|
||||
/>
|
||||
@@ -496,8 +350,8 @@ const OutLink = ({
|
||||
appAvatar={chatData.app.avatar}
|
||||
appName={chatData.app.name}
|
||||
history={chatData.history}
|
||||
showHistory={true}
|
||||
onOpenSlider={onOpenSlider}
|
||||
showHistory
|
||||
/>
|
||||
{/* chat box */}
|
||||
<Box flex={1}>
|
||||
@@ -512,33 +366,33 @@ const OutLink = ({
|
||||
onUpdateVariable={(e) => {}}
|
||||
onStartChat={startChat}
|
||||
onDelMessage={(e) =>
|
||||
delOneHistoryItem({ ...e, appId: chatData.appId, chatId, outLinkUid })
|
||||
delOneHistoryItem({ ...e, appId: chatData.appId, chatId, teamId, teamToken })
|
||||
}
|
||||
appId={chatData.appId}
|
||||
shareTeamId={shareTeamId}
|
||||
chatId={chatId}
|
||||
outLinkUid={outLinkUid}
|
||||
teamId={teamId}
|
||||
teamToken={teamToken}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</PageContainer>
|
||||
</Flex>
|
||||
</MyBox>
|
||||
);
|
||||
};
|
||||
|
||||
export async function getServerSideProps(context: any) {
|
||||
const shareTeamId = context?.query?.shareTeamId || '';
|
||||
const teamId = context?.query?.teamId || '';
|
||||
const appId = context?.query?.appId || '';
|
||||
const chatId = context?.query?.chatId || '';
|
||||
const authToken: string = context?.query?.authToken || '';
|
||||
const teamToken: string = context?.query?.teamToken || '';
|
||||
|
||||
return {
|
||||
props: {
|
||||
shareTeamId,
|
||||
teamId,
|
||||
appId,
|
||||
chatId,
|
||||
authToken,
|
||||
teamToken,
|
||||
...(await serviceSideProps(context))
|
||||
}
|
||||
};
|
||||
|
||||
@@ -95,6 +95,20 @@ const Provider = ({
|
||||
const customSplitChar = processParamsForm.watch('customSplitChar');
|
||||
|
||||
const modeStaticParams = {
|
||||
[TrainingModeEnum.auto]: {
|
||||
chunkOverlapRatio: 0.2,
|
||||
maxChunkSize: 2048,
|
||||
minChunkSize: 100,
|
||||
autoChunkSize: vectorModel?.defaultToken ? vectorModel?.defaultToken * 2 : 1024,
|
||||
chunkSize: vectorModel?.defaultToken ? vectorModel?.defaultToken * 2 : 1024,
|
||||
showChunkInput: false,
|
||||
showPromptInput: false,
|
||||
charsPointsPrice: agentModel.charsPointsPrice,
|
||||
priceTip: t('core.dataset.import.Auto mode Estimated Price Tips', {
|
||||
price: agentModel.charsPointsPrice
|
||||
}),
|
||||
uploadRate: 100
|
||||
},
|
||||
[TrainingModeEnum.chunk]: {
|
||||
chunkSizeField: 'embeddingChunkSize' as ChunkSizeFieldType,
|
||||
chunkOverlapRatio: 0.2,
|
||||
@@ -149,10 +163,17 @@ const Provider = ({
|
||||
[sources]
|
||||
);
|
||||
const predictPoints = useMemo(() => {
|
||||
if (mode === TrainingModeEnum.qa) {
|
||||
return +(((totalChunkChars * 1.5) / 1000) * agentModel.charsPointsPrice).toFixed(2);
|
||||
const totalTokensPredict = totalChunkChars / 1000;
|
||||
if (mode === TrainingModeEnum.auto) {
|
||||
const price = totalTokensPredict * 1.3 * agentModel.charsPointsPrice;
|
||||
return +price.toFixed(2);
|
||||
}
|
||||
return +((totalChunkChars / 1000) * vectorModel.charsPointsPrice).toFixed(2);
|
||||
if (mode === TrainingModeEnum.qa) {
|
||||
const price = totalTokensPredict * 1.2 * agentModel.charsPointsPrice;
|
||||
return +price.toFixed(2);
|
||||
}
|
||||
|
||||
return +(totalTokensPredict * vectorModel.charsPointsPrice).toFixed(2);
|
||||
}, [agentModel.charsPointsPrice, mode, totalChunkChars, vectorModel.charsPointsPrice]);
|
||||
const totalChunks = useMemo(
|
||||
() => sources.reduce((sum, file) => sum + file.chunks.length, 0),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Flex,
|
||||
@@ -60,6 +60,15 @@ function DataProcess({
|
||||
onClose: onCloseCustomPrompt
|
||||
} = useDisclosure();
|
||||
|
||||
const trainingModeList = useMemo(() => {
|
||||
const list = Object.entries(TrainingTypeMap);
|
||||
|
||||
return list.filter(([key, value]) => {
|
||||
if (feConfigs?.isPlus) return true;
|
||||
return value.isPlus;
|
||||
});
|
||||
}, [feConfigs?.isPlus]);
|
||||
|
||||
useEffect(() => {
|
||||
if (showPreviewChunks) {
|
||||
splitSources2Chunks();
|
||||
@@ -79,7 +88,7 @@ function DataProcess({
|
||||
{t('core.dataset.import.Training mode')}
|
||||
</Box>
|
||||
<LeftRadio
|
||||
list={Object.entries(TrainingTypeMap).map(([key, value]) => ({
|
||||
list={trainingModeList.map(([key, value]) => ({
|
||||
title: t(value.label),
|
||||
value: key,
|
||||
tooltip: t(value.tooltip)
|
||||
@@ -91,7 +100,7 @@ function DataProcess({
|
||||
setValue('mode', e);
|
||||
setRefresh(!refresh);
|
||||
}}
|
||||
gridTemplateColumns={'1fr 1fr'}
|
||||
gridTemplateColumns={'repeat(3,1fr)'}
|
||||
defaultBg="white"
|
||||
activeBg="white"
|
||||
/>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState, Dispatch, useCallback } from 'react';
|
||||
import { FormControl, Box, Input, Button } from '@chakra-ui/react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { PageTypeEnum } from '@/constants/user';
|
||||
import { LoginPageTypeEnum } from '@/constants/user';
|
||||
import { postFindPassword } from '@/web/support/user/api';
|
||||
import { useSendCode } from '@/web/support/user/hooks/useSendCode';
|
||||
import type { ResLogin } from '@/global/support/api/userRes.d';
|
||||
@@ -9,7 +9,7 @@ import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
|
||||
interface Props {
|
||||
setPageType: Dispatch<`${PageTypeEnum}`>;
|
||||
setPageType: Dispatch<`${LoginPageTypeEnum}`>;
|
||||
loginSuccess: (e: ResLogin) => void;
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
color={'primary.700'}
|
||||
cursor={'pointer'}
|
||||
_hover={{ textDecoration: 'underline' }}
|
||||
onClick={() => setPageType('login')}
|
||||
onClick={() => setPageType(LoginPageTypeEnum.passwordLogin)}
|
||||
>
|
||||
去登录
|
||||
</Box>
|
||||
|
||||
@@ -1,251 +0,0 @@
|
||||
import React, { useState, Dispatch, useCallback, useRef } from 'react';
|
||||
import {
|
||||
FormControl,
|
||||
Flex,
|
||||
Input,
|
||||
Button,
|
||||
Divider,
|
||||
AbsoluteCenter,
|
||||
Box,
|
||||
Link,
|
||||
useTheme
|
||||
} from '@chakra-ui/react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useRouter } from 'next/router';
|
||||
import { PageTypeEnum } from '@/constants/user';
|
||||
import { OAuthEnum } from '@fastgpt/global/support/user/constant';
|
||||
import { postLogin } from '@/web/support/user/api';
|
||||
import type { ResLogin } from '@/global/support/api/userRes';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
import { getDocPath } from '@/web/common/system/doc';
|
||||
import Avatar from '@/components/Avatar';
|
||||
import { LOGO_ICON } from '@fastgpt/global/common/system/constants';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 8);
|
||||
|
||||
interface Props {
|
||||
setPageType: Dispatch<`${PageTypeEnum}`>;
|
||||
loginSuccess: (e: ResLogin) => void;
|
||||
}
|
||||
|
||||
interface LoginFormType {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
const LoginForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
const theme = useTheme();
|
||||
const { lastRoute = '/app/list' } = router.query as { lastRoute: string };
|
||||
const { toast } = useToast();
|
||||
const { setLoginStore, feConfigs } = useSystemStore();
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors }
|
||||
} = useForm<LoginFormType>();
|
||||
|
||||
const [requesting, setRequesting] = useState(false);
|
||||
|
||||
const onclickLogin = useCallback(
|
||||
async ({ username, password }: LoginFormType) => {
|
||||
setRequesting(true);
|
||||
try {
|
||||
loginSuccess(
|
||||
await postLogin({
|
||||
username,
|
||||
password
|
||||
})
|
||||
);
|
||||
toast({
|
||||
title: '登录成功',
|
||||
status: 'success'
|
||||
});
|
||||
} catch (error: any) {
|
||||
toast({
|
||||
title: error.message || '登录异常',
|
||||
status: 'error'
|
||||
});
|
||||
}
|
||||
setRequesting(false);
|
||||
},
|
||||
[loginSuccess, toast]
|
||||
);
|
||||
|
||||
const redirectUri = `${location.origin}/login/provider`;
|
||||
const state = useRef(nanoid());
|
||||
|
||||
const oAuthList = [
|
||||
...(feConfigs?.oauth?.github
|
||||
? [
|
||||
{
|
||||
label: t('support.user.login.Github'),
|
||||
provider: OAuthEnum.github,
|
||||
icon: 'common/gitFill',
|
||||
redirectUrl: `https://github.com/login/oauth/authorize?client_id=${feConfigs?.oauth?.github}&redirect_uri=${redirectUri}&state=${state.current}&scope=user:email%20read:user`
|
||||
}
|
||||
]
|
||||
: []),
|
||||
...(feConfigs?.oauth?.google
|
||||
? [
|
||||
{
|
||||
label: t('support.user.login.Google'),
|
||||
provider: OAuthEnum.google,
|
||||
icon: 'common/googleFill',
|
||||
redirectUrl: `https://accounts.google.com/o/oauth2/v2/auth?client_id=${feConfigs?.oauth?.google}&redirect_uri=${redirectUri}&state=${state.current}&response_type=code&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email%20openid&include_granted_scopes=true`
|
||||
}
|
||||
]
|
||||
: [])
|
||||
];
|
||||
|
||||
const isCommunityVersion = feConfigs?.show_register === false && feConfigs?.show_git;
|
||||
|
||||
return (
|
||||
<Flex flexDirection={'column'} h={'100%'}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Flex
|
||||
w={['48px', '56px']}
|
||||
h={['48px', '56px']}
|
||||
bg={'myGray.25'}
|
||||
borderRadius={'xl'}
|
||||
borderWidth={'1.5px'}
|
||||
borderColor={'borderColor.base'}
|
||||
alignItems={'center'}
|
||||
justifyContent={'center'}
|
||||
>
|
||||
<Avatar src={LOGO_ICON} w={'30px'} />
|
||||
</Flex>
|
||||
<Box ml={3} fontSize={['2xl', '3xl']} fontWeight={'bold'}>
|
||||
{feConfigs?.systemTitle}
|
||||
</Box>
|
||||
</Flex>
|
||||
<Box
|
||||
mt={'42px'}
|
||||
onKeyDown={(e) => {
|
||||
if (e.keyCode === 13 && !e.shiftKey && !requesting) {
|
||||
handleSubmit(onclickLogin)();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<FormControl isInvalid={!!errors.username}>
|
||||
<Input
|
||||
bg={'myGray.50'}
|
||||
placeholder={isCommunityVersion ? '使用root用户登录' : '邮箱/手机号/用户名'}
|
||||
{...register('username', {
|
||||
required: '邮箱/手机号/用户名不能为空'
|
||||
})}
|
||||
></Input>
|
||||
</FormControl>
|
||||
<FormControl mt={6} isInvalid={!!errors.password}>
|
||||
<Input
|
||||
bg={'myGray.50'}
|
||||
type={'password'}
|
||||
placeholder={isCommunityVersion ? 'root密码为你设置的环境变量' : '密码'}
|
||||
{...register('password', {
|
||||
required: '密码不能为空',
|
||||
maxLength: {
|
||||
value: 20,
|
||||
message: '密码最多 20 位'
|
||||
}
|
||||
})}
|
||||
></Input>
|
||||
</FormControl>
|
||||
{feConfigs?.docUrl && (
|
||||
<Box mt={7} fontSize={'sm'}>
|
||||
使用即代表你同意我们的{' '}
|
||||
<Link
|
||||
href={getDocPath('/docs/agreement/disclaimer/')}
|
||||
target={'_blank'}
|
||||
color={'primary.500'}
|
||||
>
|
||||
免责声明
|
||||
</Link>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
my={6}
|
||||
w={'100%'}
|
||||
size={['md', 'lg']}
|
||||
colorScheme="blue"
|
||||
isLoading={requesting}
|
||||
onClick={handleSubmit(onclickLogin)}
|
||||
>
|
||||
{t('home.Login')}
|
||||
</Button>
|
||||
|
||||
{feConfigs?.show_register && (
|
||||
<>
|
||||
<Flex align={'center'} justifyContent={'flex-end'} color={'primary.700'}>
|
||||
<Box
|
||||
cursor={'pointer'}
|
||||
_hover={{ textDecoration: 'underline' }}
|
||||
onClick={() => setPageType('forgetPassword')}
|
||||
fontSize="sm"
|
||||
>
|
||||
忘记密码?
|
||||
</Box>
|
||||
<Box mx={3} h={'16px'} w={'1.5px'} bg={'myGray.250'}></Box>
|
||||
<Box
|
||||
cursor={'pointer'}
|
||||
_hover={{ textDecoration: 'underline' }}
|
||||
onClick={() => setPageType('register')}
|
||||
fontSize="sm"
|
||||
>
|
||||
注册账号
|
||||
</Box>
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
<Box flex={1} />
|
||||
{/* oauth */}
|
||||
{feConfigs?.show_register && oAuthList.length > 0 && (
|
||||
<>
|
||||
<Box position={'relative'}>
|
||||
<Divider />
|
||||
<AbsoluteCenter bg="white" px="4" color={'myGray.500'}>
|
||||
or
|
||||
</AbsoluteCenter>
|
||||
</Box>
|
||||
<Box mt={8}>
|
||||
{oAuthList.map((item) => (
|
||||
<Box key={item.provider} _notFirst={{ mt: 4 }}>
|
||||
<Button
|
||||
variant={'whitePrimary'}
|
||||
w={'100%'}
|
||||
h={'42px'}
|
||||
leftIcon={
|
||||
<MyIcon
|
||||
name={item.icon as any}
|
||||
w={'20px'}
|
||||
cursor={'pointer'}
|
||||
color={'myGray.800'}
|
||||
/>
|
||||
}
|
||||
onClick={() => {
|
||||
setLoginStore({
|
||||
provider: item.provider,
|
||||
lastRoute,
|
||||
state: state.current
|
||||
});
|
||||
router.replace(item.redirectUrl, '_self');
|
||||
}}
|
||||
>
|
||||
{item.label}
|
||||
</Button>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoginForm;
|
||||
171
projects/app/src/pages/login/components/LoginForm/LoginForm.tsx
Normal file
171
projects/app/src/pages/login/components/LoginForm/LoginForm.tsx
Normal file
@@ -0,0 +1,171 @@
|
||||
import React, { useState, Dispatch, useCallback } from 'react';
|
||||
import { FormControl, Flex, Input, Button, Box, Link } from '@chakra-ui/react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { LoginPageTypeEnum } from '@/constants/user';
|
||||
import { postLogin } from '@/web/support/user/api';
|
||||
import type { ResLogin } from '@/global/support/api/userRes';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { getDocPath } from '@/web/common/system/doc';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import FormLayout from './components/FormLayout';
|
||||
|
||||
interface Props {
|
||||
setPageType: Dispatch<`${LoginPageTypeEnum}`>;
|
||||
loginSuccess: (e: ResLogin) => void;
|
||||
}
|
||||
|
||||
interface LoginFormType {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
const LoginForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const { feConfigs } = useSystemStore();
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors }
|
||||
} = useForm<LoginFormType>();
|
||||
|
||||
const [requesting, setRequesting] = useState(false);
|
||||
|
||||
const onclickLogin = useCallback(
|
||||
async ({ username, password }: LoginFormType) => {
|
||||
setRequesting(true);
|
||||
try {
|
||||
loginSuccess(
|
||||
await postLogin({
|
||||
username,
|
||||
password
|
||||
})
|
||||
);
|
||||
toast({
|
||||
title: '登录成功',
|
||||
status: 'success'
|
||||
});
|
||||
} catch (error: any) {
|
||||
toast({
|
||||
title: error.message || '登录异常',
|
||||
status: 'error'
|
||||
});
|
||||
}
|
||||
setRequesting(false);
|
||||
},
|
||||
[loginSuccess, toast]
|
||||
);
|
||||
|
||||
const isCommunityVersion = feConfigs?.show_register === false && !feConfigs?.isPlus;
|
||||
|
||||
const loginOptions = [
|
||||
feConfigs?.show_phoneLogin ? t('support.user.login.Phone number') : '',
|
||||
feConfigs?.show_emailLogin ? t('support.user.login.Email') : '',
|
||||
t('support.user.login.Username')
|
||||
].filter(Boolean);
|
||||
|
||||
const placeholder = isCommunityVersion
|
||||
? t('support.user.login.Root login')
|
||||
: loginOptions.join('/');
|
||||
|
||||
return (
|
||||
<FormLayout setPageType={setPageType} pageType={LoginPageTypeEnum.passwordLogin}>
|
||||
<Box
|
||||
mt={'42px'}
|
||||
onKeyDown={(e) => {
|
||||
if (e.keyCode === 13 && !e.shiftKey && !requesting) {
|
||||
handleSubmit(onclickLogin)();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<FormControl isInvalid={!!errors.username}>
|
||||
<Input
|
||||
bg={'myGray.50'}
|
||||
placeholder={placeholder}
|
||||
{...register('username', {
|
||||
required: true
|
||||
})}
|
||||
></Input>
|
||||
</FormControl>
|
||||
<FormControl mt={6} isInvalid={!!errors.password}>
|
||||
<Input
|
||||
bg={'myGray.50'}
|
||||
type={'password'}
|
||||
placeholder={
|
||||
isCommunityVersion
|
||||
? t('support.user.login.Root password placeholder')
|
||||
: t('support.user.login.Password')
|
||||
}
|
||||
{...register('password', {
|
||||
required: true,
|
||||
maxLength: {
|
||||
value: 60,
|
||||
message: '密码最多 60 位'
|
||||
}
|
||||
})}
|
||||
></Input>
|
||||
</FormControl>
|
||||
{feConfigs?.docUrl && (
|
||||
<Flex alignItems={'center'} mt={7} fontSize={'sm'}>
|
||||
{t('support.user.login.Policy tip')}
|
||||
<Link
|
||||
ml={1}
|
||||
href={getDocPath('/docs/agreement/terms/')}
|
||||
target={'_blank'}
|
||||
color={'primary.500'}
|
||||
>
|
||||
{t('support.user.login.Terms')}
|
||||
</Link>
|
||||
<Box mx={1}>{t('support.user.login.And')}</Box>
|
||||
<Link
|
||||
href={getDocPath('/docs/agreement/privacy/')}
|
||||
target={'_blank'}
|
||||
color={'primary.500'}
|
||||
>
|
||||
{t('support.user.login.Privacy')}
|
||||
</Link>
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
my={6}
|
||||
w={'100%'}
|
||||
size={['md', 'lg']}
|
||||
colorScheme="blue"
|
||||
isLoading={requesting}
|
||||
onClick={handleSubmit(onclickLogin)}
|
||||
>
|
||||
{t('home.Login')}
|
||||
</Button>
|
||||
|
||||
{feConfigs?.show_register && (
|
||||
<>
|
||||
<Flex align={'center'} justifyContent={'flex-end'} color={'primary.700'}>
|
||||
<Box
|
||||
cursor={'pointer'}
|
||||
_hover={{ textDecoration: 'underline' }}
|
||||
onClick={() => setPageType('forgetPassword')}
|
||||
fontSize="sm"
|
||||
>
|
||||
{t('support.user.login.Forget Password')}
|
||||
</Box>
|
||||
<Box mx={3} h={'16px'} w={'1.5px'} bg={'myGray.250'}></Box>
|
||||
<Box
|
||||
cursor={'pointer'}
|
||||
_hover={{ textDecoration: 'underline' }}
|
||||
onClick={() => setPageType('register')}
|
||||
fontSize="sm"
|
||||
>
|
||||
{t('support.user.login.Register')}
|
||||
</Box>
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</FormLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoginForm;
|
||||
@@ -0,0 +1,60 @@
|
||||
import React, { Dispatch } from 'react';
|
||||
import { LoginPageTypeEnum } from '@/constants/user';
|
||||
import type { ResLogin } from '@/global/support/api/userRes';
|
||||
import { Box, Center, Image } from '@chakra-ui/react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { getWXLoginQR, getWXLoginResult } from '@/web/support/user/api';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import FormLayout from './components/FormLayout';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import Loading from '@/components/Loading';
|
||||
|
||||
interface Props {
|
||||
loginSuccess: (e: ResLogin) => void;
|
||||
setPageType: Dispatch<`${LoginPageTypeEnum}`>;
|
||||
}
|
||||
|
||||
const WechatForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
|
||||
const { data: wechatInfo } = useQuery(['getWXLoginQR'], getWXLoginQR, {
|
||||
onError(err) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: getErrText(err, '获取二维码失败')
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
useQuery(['getWXLoginResult', wechatInfo?.code], () => getWXLoginResult(wechatInfo?.code || ''), {
|
||||
refetchInterval: 3 * 1000,
|
||||
enabled: !!wechatInfo?.code,
|
||||
onSuccess(data: ResLogin) {
|
||||
loginSuccess(data);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<FormLayout setPageType={setPageType} pageType={LoginPageTypeEnum.wechat}>
|
||||
<Box>
|
||||
<Box w={'full'} textAlign={'center'} pt={5}>
|
||||
{t('support.user.login.Wx qr login')}
|
||||
</Box>
|
||||
<Box p={5} display={'flex'} w={'full'} justifyContent={'center'}>
|
||||
{wechatInfo?.codeUrl ? (
|
||||
<Image w="200px" src={wechatInfo?.codeUrl} alt="qrcode"></Image>
|
||||
) : (
|
||||
<Center w={200} h={200} position={'relative'}>
|
||||
<Loading fixed={false} />
|
||||
</Center>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
</FormLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default WechatForm;
|
||||
@@ -0,0 +1,136 @@
|
||||
import Divider from '@/components/core/module/Flow/components/modules/Divider';
|
||||
import { LoginPageTypeEnum } from '@/constants/user';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { AbsoluteCenter, Box, Button, Flex, Image } from '@chakra-ui/react';
|
||||
import { LOGO_ICON } from '@fastgpt/global/common/system/constants';
|
||||
import { OAuthEnum } from '@fastgpt/global/support/user/constant';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
import { useRouter } from 'next/router';
|
||||
import { Dispatch, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 8);
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode;
|
||||
setPageType: Dispatch<`${LoginPageTypeEnum}`>;
|
||||
pageType: `${LoginPageTypeEnum}`;
|
||||
}
|
||||
|
||||
const FormLayout = ({ children, setPageType, pageType }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
const { setLoginStore, feConfigs } = useSystemStore();
|
||||
const { lastRoute = '/app/list' } = router.query as { lastRoute: string };
|
||||
const state = useRef(nanoid());
|
||||
const redirectUri = `${location.origin}/login/provider`;
|
||||
|
||||
const oAuthList = [
|
||||
...(feConfigs?.oauth?.github
|
||||
? [
|
||||
{
|
||||
label: t('support.user.login.Github'),
|
||||
provider: OAuthEnum.github,
|
||||
icon: 'common/gitFill',
|
||||
redirectUrl: `https://github.com/login/oauth/authorize?client_id=${feConfigs?.oauth?.github}&redirect_uri=${redirectUri}&state=${state.current}&scope=user:email%20read:user`
|
||||
}
|
||||
]
|
||||
: []),
|
||||
...(feConfigs?.oauth?.google
|
||||
? [
|
||||
{
|
||||
label: t('support.user.login.Google'),
|
||||
provider: OAuthEnum.google,
|
||||
icon: 'common/googleFill',
|
||||
redirectUrl: `https://accounts.google.com/o/oauth2/v2/auth?client_id=${feConfigs?.oauth?.google}&redirect_uri=${redirectUri}&state=${state.current}&response_type=code&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email%20openid&include_granted_scopes=true`
|
||||
}
|
||||
]
|
||||
: []),
|
||||
...(feConfigs?.oauth?.wechat && pageType !== LoginPageTypeEnum.wechat
|
||||
? [
|
||||
{
|
||||
label: t('support.user.login.Wechat'),
|
||||
provider: OAuthEnum.wechat,
|
||||
icon: 'common/wechatFill',
|
||||
pageType: LoginPageTypeEnum.wechat
|
||||
}
|
||||
]
|
||||
: []),
|
||||
...(pageType !== LoginPageTypeEnum.passwordLogin
|
||||
? [
|
||||
{
|
||||
label: t('support.user.login.Password login'),
|
||||
provider: LoginPageTypeEnum.passwordLogin,
|
||||
icon: 'support/account/passwordLogin',
|
||||
pageType: LoginPageTypeEnum.passwordLogin
|
||||
}
|
||||
]
|
||||
: [])
|
||||
];
|
||||
return (
|
||||
<Flex flexDirection={'column'} h={'100%'}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Flex
|
||||
w={['48px', '56px']}
|
||||
h={['48px', '56px']}
|
||||
bg={'myGray.25'}
|
||||
borderRadius={'xl'}
|
||||
borderWidth={'1.5px'}
|
||||
borderColor={'borderColor.base'}
|
||||
alignItems={'center'}
|
||||
justifyContent={'center'}
|
||||
>
|
||||
<Image src={LOGO_ICON} w={'24px'} alt={'icon'} />
|
||||
</Flex>
|
||||
<Box ml={3} fontSize={['2xl', '3xl']} fontWeight={'bold'}>
|
||||
{feConfigs?.systemTitle}
|
||||
</Box>
|
||||
</Flex>
|
||||
{children}
|
||||
<Box flex={1} />
|
||||
{feConfigs?.show_register && oAuthList.length > 0 && (
|
||||
<>
|
||||
<Box position={'relative'}>
|
||||
<Divider />
|
||||
<AbsoluteCenter bg="white" px="4" color={'myGray.500'}>
|
||||
or
|
||||
</AbsoluteCenter>
|
||||
</Box>
|
||||
<Box mt={8}>
|
||||
{oAuthList.map((item) => (
|
||||
<Box key={item.provider} _notFirst={{ mt: 4 }}>
|
||||
<Button
|
||||
variant={'whitePrimary'}
|
||||
w={'100%'}
|
||||
h={'42px'}
|
||||
leftIcon={
|
||||
<MyIcon
|
||||
name={item.icon as any}
|
||||
w={'20px'}
|
||||
cursor={'pointer'}
|
||||
color={'myGray.800'}
|
||||
/>
|
||||
}
|
||||
onClick={() => {
|
||||
item.redirectUrl &&
|
||||
setLoginStore({
|
||||
provider: item.provider,
|
||||
lastRoute,
|
||||
state: state.current
|
||||
});
|
||||
item.redirectUrl && router.replace(item.redirectUrl, '_self');
|
||||
item.pageType && setPageType(item.pageType);
|
||||
}}
|
||||
>
|
||||
{item.label}
|
||||
</Button>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default FormLayout;
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState, Dispatch, useCallback } from 'react';
|
||||
import { FormControl, Box, Input, Button } from '@chakra-ui/react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { PageTypeEnum } from '@/constants/user';
|
||||
import { LoginPageTypeEnum } from '@/constants/user';
|
||||
import { postRegister } from '@/web/support/user/api';
|
||||
import { useSendCode } from '@/web/support/user/hooks/useSendCode';
|
||||
import type { ResLogin } from '@/global/support/api/userRes';
|
||||
@@ -13,7 +13,7 @@ import { useTranslation } from 'next-i18next';
|
||||
|
||||
interface Props {
|
||||
loginSuccess: (e: ResLogin) => void;
|
||||
setPageType: Dispatch<`${PageTypeEnum}`>;
|
||||
setPageType: Dispatch<`${LoginPageTypeEnum}`>;
|
||||
}
|
||||
|
||||
interface RegisterType {
|
||||
@@ -196,7 +196,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
color={'primary.700'}
|
||||
cursor={'pointer'}
|
||||
_hover={{ textDecoration: 'underline' }}
|
||||
onClick={() => setPageType('login')}
|
||||
onClick={() => setPageType(LoginPageTypeEnum.passwordLogin)}
|
||||
>
|
||||
已有账号,去登录
|
||||
</Box>
|
||||
|
||||
@@ -1,25 +1,28 @@
|
||||
import React, { useState, useCallback, useEffect } from 'react';
|
||||
import { Box, Flex, Image, useDisclosure } from '@chakra-ui/react';
|
||||
import { PageTypeEnum } from '@/constants/user';
|
||||
import { Box, Center, Flex, useDisclosure } from '@chakra-ui/react';
|
||||
import { LoginPageTypeEnum } from '@/constants/user';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import type { ResLogin } from '@/global/support/api/userRes.d';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import { useChatStore } from '@/web/core/chat/storeChat';
|
||||
import LoginForm from './components/LoginForm';
|
||||
import LoginForm from './components/LoginForm/LoginForm';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { serviceSideProps } from '@/web/common/utils/i18n';
|
||||
import { clearToken, setToken } from '@/web/support/user/auth';
|
||||
import CommunityModal from '@/components/CommunityModal';
|
||||
import Script from 'next/script';
|
||||
import Loading from '@/components/Loading';
|
||||
|
||||
const RegisterForm = dynamic(() => import('./components/RegisterForm'));
|
||||
const ForgetPasswordForm = dynamic(() => import('./components/ForgetPasswordForm'));
|
||||
const WechatForm = dynamic(() => import('./components/LoginForm/WechatForm'));
|
||||
|
||||
const Login = () => {
|
||||
const router = useRouter();
|
||||
const { lastRoute = '' } = router.query as { lastRoute: string };
|
||||
const { feConfigs } = useSystemStore();
|
||||
const [pageType, setPageType] = useState<`${PageTypeEnum}`>(PageTypeEnum.login);
|
||||
const [pageType, setPageType] = useState<`${LoginPageTypeEnum}`>();
|
||||
const { setUserInfo } = useUserStore();
|
||||
const { setLastChatId, setLastChatAppId } = useChatStore();
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
@@ -39,11 +42,12 @@ const Login = () => {
|
||||
[lastRoute, router, setLastChatId, setLastChatAppId, setUserInfo]
|
||||
);
|
||||
|
||||
function DynamicComponent({ type }: { type: `${PageTypeEnum}` }) {
|
||||
function DynamicComponent({ type }: { type: `${LoginPageTypeEnum}` }) {
|
||||
const TypeMap = {
|
||||
[PageTypeEnum.login]: LoginForm,
|
||||
[PageTypeEnum.register]: RegisterForm,
|
||||
[PageTypeEnum.forgetPassword]: ForgetPasswordForm
|
||||
[LoginPageTypeEnum.passwordLogin]: LoginForm,
|
||||
[LoginPageTypeEnum.register]: RegisterForm,
|
||||
[LoginPageTypeEnum.forgetPassword]: ForgetPasswordForm,
|
||||
[LoginPageTypeEnum.wechat]: WechatForm
|
||||
};
|
||||
|
||||
const Component = TypeMap[type];
|
||||
@@ -51,6 +55,13 @@ const Login = () => {
|
||||
return <Component setPageType={setPageType} loginSuccess={loginSuccess} />;
|
||||
}
|
||||
|
||||
/* default login type */
|
||||
useEffect(() => {
|
||||
if (!feConfigs.oauth) return;
|
||||
setPageType(
|
||||
feConfigs.oauth?.wechat ? LoginPageTypeEnum.wechat : LoginPageTypeEnum.passwordLogin
|
||||
);
|
||||
}, [feConfigs.oauth, feConfigs.oauth?.wechat]);
|
||||
useEffect(() => {
|
||||
clearToken();
|
||||
router.prefetch('/app/list');
|
||||
@@ -87,7 +98,13 @@ const Login = () => {
|
||||
]}
|
||||
>
|
||||
<Box w={['100%', '380px']} flex={'1 0 0'}>
|
||||
<DynamicComponent type={pageType} />
|
||||
{pageType ? (
|
||||
<DynamicComponent type={pageType} />
|
||||
) : (
|
||||
<Center w={'full'} h={'full'} position={'relative'}>
|
||||
<Loading fixed={false} />
|
||||
</Center>
|
||||
)}
|
||||
</Box>
|
||||
{feConfigs?.concatMd && (
|
||||
<Box
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Box, Button, Flex, Grid } from '@chakra-ui/react';
|
||||
import { Box, Flex, Grid } from '@chakra-ui/react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
const FAQ = () => {
|
||||
@@ -15,7 +15,7 @@ const FAQ = () => {
|
||||
},
|
||||
{
|
||||
title: '什么是AI积分?',
|
||||
desc: '每次调用AI模型时,都会消耗一定的AI积分。具体的计算标准可参考上方的“AI 积分计算标准”。\n1 字符=1中英文字符和标点符号,会去掉换行和空格符号,计算字符时包含对话上下文与知识库引用。'
|
||||
desc: '每次调用AI模型时,都会消耗一定的AI积分。具体的计算标准可参考上方的“AI 积分计算标准”。\nToken计算采用GPT3.5相同公式,1Token≈0.7中文字符≈0.9英文单词,连续出现的字符可能被认为是1个Tokens。'
|
||||
},
|
||||
{
|
||||
title: 'AI积分会过期么?',
|
||||
|
||||
@@ -42,7 +42,7 @@ const Points = () => {
|
||||
{llmModelList?.map((item, i) => (
|
||||
<Flex key={item.model} py={4} bg={i % 2 !== 0 ? 'myGray.50' : ''}>
|
||||
<Box flex={'1 0 0'}>{item.name}</Box>
|
||||
<Box flex={'1 0 0'}>{item.charsPointsPrice}积分 / 1000字符</Box>
|
||||
<Box flex={'1 0 0'}>{item.charsPointsPrice}积分 / 1000 Tokens</Box>
|
||||
</Flex>
|
||||
))}
|
||||
</Box>
|
||||
@@ -67,7 +67,7 @@ const Points = () => {
|
||||
{vectorModelList?.map((item, i) => (
|
||||
<Flex key={item.model} py={4} bg={i % 2 !== 0 ? 'myGray.50' : ''}>
|
||||
<Box flex={'1 0 0'}>{item.name}</Box>
|
||||
<Box flex={'1 0 0'}>{item.charsPointsPrice}积分 / 1000字符</Box>
|
||||
<Box flex={'1 0 0'}>{item.charsPointsPrice}积分 / 1000 Tokens</Box>
|
||||
</Flex>
|
||||
))}
|
||||
</Box>
|
||||
|
||||
@@ -1,21 +1,12 @@
|
||||
import { initSystemConfig } from '@/pages/api/common/system/getInitData';
|
||||
import { startQueue } from '@/service/utils/tools';
|
||||
import { setCron } from '@fastgpt/service/common/system/cron';
|
||||
import { startTrainingQueue } from '@/service/core/dataset/training/utils';
|
||||
|
||||
export const startCron = () => {
|
||||
setUpdateSystemConfigCron();
|
||||
setTrainingQueueCron();
|
||||
};
|
||||
|
||||
export const setUpdateSystemConfigCron = () => {
|
||||
setCron('*/5 * * * *', () => {
|
||||
initSystemConfig();
|
||||
console.log('refresh system config');
|
||||
});
|
||||
};
|
||||
|
||||
export const setTrainingQueueCron = () => {
|
||||
setCron('*/1 * * * *', () => {
|
||||
startQueue();
|
||||
startTrainingQueue();
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { getTikTokenEnc } from '@fastgpt/global/common/string/tiktoken';
|
||||
import { initHttpAgent } from '@fastgpt/service/common/middle/httpAgent';
|
||||
import { existsSync, readFileSync } from 'fs';
|
||||
|
||||
export const readConfigData = (name: string) => {
|
||||
@@ -23,3 +25,15 @@ export const readConfigData = (name: string) => {
|
||||
|
||||
return content;
|
||||
};
|
||||
|
||||
export function initGlobal() {
|
||||
if (global.communityPlugins) return;
|
||||
|
||||
global.communityPlugins = [];
|
||||
global.simpleModeTemplates = [];
|
||||
global.qaQueueLen = global.qaQueueLen ?? 0;
|
||||
global.vectorQueueLen = global.vectorQueueLen ?? 0;
|
||||
// init tikToken
|
||||
getTikTokenEnc();
|
||||
initHttpAgent();
|
||||
}
|
||||
|
||||
21
projects/app/src/service/common/system/volumnMongoWatch.ts
Normal file
21
projects/app/src/service/common/system/volumnMongoWatch.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { initSystemConfig } from '@/pages/api/common/system/getInitData';
|
||||
import { createDatasetTrainingMongoWatch } from '@/service/core/dataset/training/utils';
|
||||
import { MongoSystemConfigs } from '@fastgpt/service/common/system/config/schema';
|
||||
|
||||
export const startMongoWatch = async () => {
|
||||
reloadConfigWatch();
|
||||
createDatasetTrainingMongoWatch();
|
||||
};
|
||||
|
||||
const reloadConfigWatch = () => {
|
||||
const changeStream = MongoSystemConfigs.watch();
|
||||
|
||||
changeStream.on('change', async (change) => {
|
||||
try {
|
||||
if (change.operationType === 'insert') {
|
||||
await initSystemConfig();
|
||||
console.log('refresh system config');
|
||||
}
|
||||
} catch (error) {}
|
||||
});
|
||||
};
|
||||
@@ -35,7 +35,6 @@ import type {
|
||||
import { pushDataListToTrainingQueue } from '@fastgpt/service/core/dataset/training/controller';
|
||||
import { getVectorModel } from '@fastgpt/service/core/ai/model';
|
||||
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
||||
import { startQueue } from '@/service/utils/tools';
|
||||
|
||||
export async function pushDataToTrainingQueue(
|
||||
props: {
|
||||
@@ -49,8 +48,6 @@ export async function pushDataToTrainingQueue(
|
||||
datasetModelList: global.llmModels
|
||||
});
|
||||
|
||||
startQueue();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -129,7 +126,7 @@ export async function insertData2Dataset({
|
||||
|
||||
return {
|
||||
insertId: _id,
|
||||
charsLength: result.reduce((acc, cur) => acc + cur.charsLength, 0)
|
||||
tokens: result.reduce((acc, cur) => acc + cur.tokens, 0)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -240,11 +237,11 @@ export async function updateData2Dataset({
|
||||
return result;
|
||||
}
|
||||
return {
|
||||
charsLength: 0
|
||||
tokens: 0
|
||||
};
|
||||
})
|
||||
);
|
||||
const charsLength = insertResult.reduce((acc, cur) => acc + cur.charsLength, 0);
|
||||
const tokens = insertResult.reduce((acc, cur) => acc + cur.tokens, 0);
|
||||
// console.log(clonePatchResult2Insert);
|
||||
await mongoSessionRun(async (session) => {
|
||||
// update mongo
|
||||
@@ -273,7 +270,7 @@ export async function updateData2Dataset({
|
||||
});
|
||||
|
||||
return {
|
||||
charsLength
|
||||
tokens
|
||||
};
|
||||
}
|
||||
|
||||
@@ -343,7 +340,7 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
|
||||
};
|
||||
};
|
||||
const embeddingRecall = async ({ query, limit }: { query: string; limit: number }) => {
|
||||
const { vectors, charsLength } = await getVectorsByText({
|
||||
const { vectors, tokens } = await getVectorsByText({
|
||||
model: getVectorModel(model),
|
||||
input: query
|
||||
});
|
||||
@@ -407,7 +404,7 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
|
||||
|
||||
return {
|
||||
embeddingRecallResults: formatResult,
|
||||
charsLength
|
||||
tokens
|
||||
};
|
||||
};
|
||||
const fullTextRecall = async ({
|
||||
@@ -552,22 +549,21 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
|
||||
// multi query recall
|
||||
const embeddingRecallResList: SearchDataResponseItemType[][] = [];
|
||||
const fullTextRecallResList: SearchDataResponseItemType[][] = [];
|
||||
let totalCharsLength = 0;
|
||||
let totalTokens = 0;
|
||||
|
||||
await Promise.all(
|
||||
queries.map(async (query) => {
|
||||
const [{ charsLength, embeddingRecallResults }, { fullTextRecallResults }] =
|
||||
await Promise.all([
|
||||
embeddingRecall({
|
||||
query,
|
||||
limit: embeddingLimit
|
||||
}),
|
||||
fullTextRecall({
|
||||
query,
|
||||
limit: fullTextLimit
|
||||
})
|
||||
]);
|
||||
totalCharsLength += charsLength;
|
||||
const [{ tokens, embeddingRecallResults }, { fullTextRecallResults }] = await Promise.all([
|
||||
embeddingRecall({
|
||||
query,
|
||||
limit: embeddingLimit
|
||||
}),
|
||||
fullTextRecall({
|
||||
query,
|
||||
limit: fullTextLimit
|
||||
})
|
||||
]);
|
||||
totalTokens += tokens;
|
||||
|
||||
embeddingRecallResList.push(embeddingRecallResults);
|
||||
fullTextRecallResList.push(fullTextRecallResults);
|
||||
@@ -583,7 +579,7 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
|
||||
).slice(0, fullTextLimit);
|
||||
|
||||
return {
|
||||
charsLength: totalCharsLength,
|
||||
tokens: totalTokens,
|
||||
embeddingRecallResults: rrfEmbRecall,
|
||||
fullTextRecallResults: rrfFTRecall
|
||||
};
|
||||
@@ -594,7 +590,7 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
|
||||
const { embeddingLimit, fullTextLimit } = countRecallLimit();
|
||||
|
||||
// recall
|
||||
const { embeddingRecallResults, fullTextRecallResults, charsLength } = await multiQueryRecall({
|
||||
const { embeddingRecallResults, fullTextRecallResults, tokens } = await multiQueryRecall({
|
||||
embeddingLimit,
|
||||
fullTextLimit
|
||||
});
|
||||
@@ -666,7 +662,7 @@ export async function searchDatasetData(props: SearchDatasetDataProps) {
|
||||
|
||||
return {
|
||||
searchRes: filterResultsByMaxTokens(scoreFilter, maxTokens),
|
||||
charsLength,
|
||||
tokens,
|
||||
searchMode,
|
||||
limit: maxTokens,
|
||||
similarity,
|
||||
|
||||
31
projects/app/src/service/core/dataset/training/utils.ts
Normal file
31
projects/app/src/service/core/dataset/training/utils.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { generateQA } from '@/service/events/generateQA';
|
||||
import { generateVector } from '@/service/events/generateVector';
|
||||
import { TrainingModeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { DatasetTrainingSchemaType } from '@fastgpt/global/core/dataset/type';
|
||||
import { MongoDatasetTraining } from '@fastgpt/service/core/dataset/training/schema';
|
||||
|
||||
export const createDatasetTrainingMongoWatch = () => {
|
||||
const changeStream = MongoDatasetTraining.watch();
|
||||
|
||||
changeStream.on('change', async (change) => {
|
||||
try {
|
||||
if (change.operationType === 'insert') {
|
||||
const fullDocument = change.fullDocument as DatasetTrainingSchemaType;
|
||||
const { mode } = fullDocument;
|
||||
if (mode === TrainingModeEnum.qa) {
|
||||
generateQA();
|
||||
} else if (mode === TrainingModeEnum.chunk) {
|
||||
generateVector();
|
||||
}
|
||||
}
|
||||
} catch (error) {}
|
||||
});
|
||||
};
|
||||
|
||||
export const startTrainingQueue = (fast?: boolean) => {
|
||||
const max = global.systemEnv?.qaMaxProcess || 10;
|
||||
for (let i = 0; i < max; i++) {
|
||||
generateQA();
|
||||
generateVector();
|
||||
}
|
||||
};
|
||||
@@ -10,8 +10,10 @@ import { Prompt_AgentQA } from '@/global/core/prompt/agent';
|
||||
import type { PushDatasetDataChunkProps } from '@fastgpt/global/core/dataset/api.d';
|
||||
import { pushDataToTrainingQueue } from '@/service/core/dataset/data/controller';
|
||||
import { getLLMModel } from '@fastgpt/service/core/ai/model';
|
||||
import { checkInvalidChunkAndLock, checkTeamAiPointsAndLock } from './utils';
|
||||
import { countGptMessagesChars } from '@fastgpt/service/core/chat/utils';
|
||||
import { checkTeamAiPointsAndLock } from './utils';
|
||||
import { checkInvalidChunkAndLock } from '@fastgpt/service/core/dataset/training/utils';
|
||||
import { addMinutes } from 'date-fns';
|
||||
import { countGptMessagesTokens } from '@fastgpt/global/common/string/tiktoken';
|
||||
|
||||
const reduceQueue = () => {
|
||||
global.qaQueueLen = global.qaQueueLen > 0 ? global.qaQueueLen - 1 : 0;
|
||||
@@ -20,9 +22,11 @@ const reduceQueue = () => {
|
||||
};
|
||||
|
||||
export async function generateQA(): Promise<any> {
|
||||
if (global.qaQueueLen >= global.systemEnv.qaMaxProcess) return;
|
||||
const max = global.systemEnv?.qaMaxProcess || 10;
|
||||
if (global.qaQueueLen >= max) return;
|
||||
global.qaQueueLen++;
|
||||
|
||||
const startTime = Date.now();
|
||||
// get training data
|
||||
const {
|
||||
data,
|
||||
@@ -33,7 +37,7 @@ export async function generateQA(): Promise<any> {
|
||||
try {
|
||||
const data = await MongoDatasetTraining.findOneAndUpdate(
|
||||
{
|
||||
lockTime: { $lte: new Date(Date.now() - 6 * 60 * 1000) },
|
||||
lockTime: { $lte: addMinutes(new Date(), -6) },
|
||||
mode: TrainingModeEnum.qa
|
||||
},
|
||||
{
|
||||
@@ -66,7 +70,7 @@ export async function generateQA(): Promise<any> {
|
||||
text: data.q
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(`Get Training Data error`, error);
|
||||
addLog.error(`[QA Queue] Error`, error);
|
||||
return {
|
||||
error: true
|
||||
};
|
||||
@@ -75,7 +79,7 @@ export async function generateQA(): Promise<any> {
|
||||
|
||||
if (done || !data) {
|
||||
if (reduceQueue()) {
|
||||
console.log(`【QA】Task Done`);
|
||||
addLog.info(`[QA Queue] Done`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -83,17 +87,15 @@ export async function generateQA(): Promise<any> {
|
||||
reduceQueue();
|
||||
return generateQA();
|
||||
}
|
||||
console.log('Start QA Training');
|
||||
|
||||
// auth balance
|
||||
if (!(await checkTeamAiPointsAndLock(data.teamId, data.tmbId))) {
|
||||
console.log('balance not enough');
|
||||
reduceQueue();
|
||||
return generateQA();
|
||||
}
|
||||
addLog.info(`[QA Queue] Start`);
|
||||
|
||||
try {
|
||||
const startTime = Date.now();
|
||||
const model = getLLMModel(data.model)?.model;
|
||||
const prompt = `${data.prompt || Prompt_AgentQA.description}
|
||||
${replaceVariable(Prompt_AgentQA.fixedText, { text })}`;
|
||||
@@ -119,8 +121,8 @@ ${replaceVariable(Prompt_AgentQA.fixedText, { text })}`;
|
||||
|
||||
const qaArr = formatSplitText(answer, text); // 格式化后的QA对
|
||||
|
||||
addLog.info(`QA Training Finish`, {
|
||||
time: `${(Date.now() - startTime) / 1000}s`,
|
||||
addLog.info(`[QA Queue] Finish`, {
|
||||
time: Date.now() - startTime,
|
||||
splitLength: qaArr.length,
|
||||
usage: chatResponse.usage
|
||||
});
|
||||
@@ -146,7 +148,7 @@ ${replaceVariable(Prompt_AgentQA.fixedText, { text })}`;
|
||||
pushQAUsage({
|
||||
teamId: data.teamId,
|
||||
tmbId: data.tmbId,
|
||||
charsLength: countGptMessagesChars(messages).length,
|
||||
tokens: countGptMessagesTokens(messages),
|
||||
billId: data.billId,
|
||||
model
|
||||
});
|
||||
|
||||
@@ -2,8 +2,10 @@ import { insertData2Dataset } from '@/service/core/dataset/data/controller';
|
||||
import { MongoDatasetTraining } from '@fastgpt/service/core/dataset/training/schema';
|
||||
import { TrainingModeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { pushGenerateVectorUsage } from '@/service/support/wallet/usage/push';
|
||||
import { checkInvalidChunkAndLock, checkTeamAiPointsAndLock } from './utils';
|
||||
import { delay } from '@fastgpt/global/common/system/utils';
|
||||
import { checkTeamAiPointsAndLock } from './utils';
|
||||
import { checkInvalidChunkAndLock } from '@fastgpt/service/core/dataset/training/utils';
|
||||
import { addMinutes } from 'date-fns';
|
||||
import { addLog } from '@fastgpt/service/common/system/log';
|
||||
|
||||
const reduceQueue = () => {
|
||||
global.vectorQueueLen = global.vectorQueueLen > 0 ? global.vectorQueueLen - 1 : 0;
|
||||
@@ -13,7 +15,8 @@ const reduceQueue = () => {
|
||||
|
||||
/* 索引生成队列。每导入一次,就是一个单独的线程 */
|
||||
export async function generateVector(): Promise<any> {
|
||||
if (global.vectorQueueLen >= global.systemEnv.vectorMaxProcess) return;
|
||||
const max = global.systemEnv?.vectorMaxProcess || 10;
|
||||
if (global.vectorQueueLen >= max) return;
|
||||
global.vectorQueueLen++;
|
||||
const start = Date.now();
|
||||
|
||||
@@ -27,7 +30,7 @@ export async function generateVector(): Promise<any> {
|
||||
try {
|
||||
const data = await MongoDatasetTraining.findOneAndUpdate(
|
||||
{
|
||||
lockTime: { $lte: new Date(Date.now() - 1 * 60 * 1000) },
|
||||
lockTime: { $lte: addMinutes(new Date(), -1) },
|
||||
mode: TrainingModeEnum.chunk
|
||||
},
|
||||
{
|
||||
@@ -68,7 +71,7 @@ export async function generateVector(): Promise<any> {
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(`Get Training Data error`, error);
|
||||
addLog.error(`Get Training Data error`, error);
|
||||
return {
|
||||
error: true
|
||||
};
|
||||
@@ -77,11 +80,12 @@ export async function generateVector(): Promise<any> {
|
||||
|
||||
if (done || !data) {
|
||||
if (reduceQueue()) {
|
||||
console.log(`【index】Task done`);
|
||||
addLog.info(`[Vector Queue] Done`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (error) {
|
||||
addLog.error(`[Vector Queue] Error`, { error });
|
||||
reduceQueue();
|
||||
return generateVector();
|
||||
}
|
||||
@@ -92,6 +96,8 @@ export async function generateVector(): Promise<any> {
|
||||
return generateVector();
|
||||
}
|
||||
|
||||
addLog.info(`[Vector Queue] Start`);
|
||||
|
||||
// create vector and insert
|
||||
try {
|
||||
// invalid data
|
||||
@@ -103,7 +109,7 @@ export async function generateVector(): Promise<any> {
|
||||
}
|
||||
|
||||
// insert to dataset
|
||||
const { charsLength } = await insertData2Dataset({
|
||||
const { tokens } = await insertData2Dataset({
|
||||
teamId: data.teamId,
|
||||
tmbId: data.tmbId,
|
||||
datasetId: data.datasetId,
|
||||
@@ -119,7 +125,7 @@ export async function generateVector(): Promise<any> {
|
||||
pushGenerateVectorUsage({
|
||||
teamId: data.teamId,
|
||||
tmbId: data.tmbId,
|
||||
charsLength,
|
||||
tokens,
|
||||
model: data.model,
|
||||
billId: data.billId
|
||||
});
|
||||
@@ -129,7 +135,9 @@ export async function generateVector(): Promise<any> {
|
||||
reduceQueue();
|
||||
generateVector();
|
||||
|
||||
console.log(`embedding finished, time: ${Date.now() - start}ms`);
|
||||
addLog.info(`[Vector Queue] Finish`, {
|
||||
time: Date.now() - start
|
||||
});
|
||||
} catch (err: any) {
|
||||
reduceQueue();
|
||||
|
||||
|
||||
@@ -2,10 +2,6 @@ import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
|
||||
import { checkTeamAIPoints } from '@fastgpt/service/support/permission/teamLimit';
|
||||
import { sendOneInform } from '../support/user/inform/api';
|
||||
import { lockTrainingDataByTeamId } from '@fastgpt/service/core/dataset/training/controller';
|
||||
import { DatasetTrainingSchemaType } from '@fastgpt/global/core/dataset/type';
|
||||
import { addLog } from '@fastgpt/service/common/system/log';
|
||||
import { MongoDatasetTraining } from '@fastgpt/service/core/dataset/training/schema';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
|
||||
export const checkTeamAiPointsAndLock = async (teamId: string, tmbId: string) => {
|
||||
try {
|
||||
@@ -29,41 +25,3 @@ export const checkTeamAiPointsAndLock = async (teamId: string, tmbId: string) =>
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export const checkInvalidChunkAndLock = async ({
|
||||
err,
|
||||
errText,
|
||||
data
|
||||
}: {
|
||||
err: any;
|
||||
errText: string;
|
||||
data: DatasetTrainingSchemaType;
|
||||
}) => {
|
||||
if (err?.response) {
|
||||
addLog.info(`openai error: ${errText}`, {
|
||||
status: err.response?.status,
|
||||
stateusText: err.response?.statusText,
|
||||
data: err.response?.data
|
||||
});
|
||||
} else {
|
||||
console.log(err);
|
||||
addLog.error(getErrText(err, errText));
|
||||
}
|
||||
|
||||
if (
|
||||
err?.message === 'invalid message format' ||
|
||||
err?.type === 'invalid_request_error' ||
|
||||
err?.code === 500
|
||||
) {
|
||||
addLog.info('Lock training data');
|
||||
console.log(err);
|
||||
|
||||
try {
|
||||
await MongoDatasetTraining.findByIdAndUpdate(data._id, {
|
||||
lockTime: new Date('2998/5/5')
|
||||
});
|
||||
} catch (error) {}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { adaptChat2GptMessages } from '@fastgpt/global/core/chat/adapt';
|
||||
import { ChatContextFilter, countMessagesChars } from '@fastgpt/service/core/chat/utils';
|
||||
import type { moduleDispatchResType, ChatItemType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { ChatContextFilter } from '@fastgpt/service/core/chat/utils';
|
||||
import { countMessagesTokens } from '@fastgpt/global/common/string/tiktoken';
|
||||
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { getAIApi } from '@fastgpt/service/core/ai/config';
|
||||
import type {
|
||||
@@ -14,7 +15,7 @@ import { Prompt_CQJson } from '@/global/core/prompt/agent';
|
||||
import { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
|
||||
import { ModelTypeEnum, getLLMModel } from '@fastgpt/service/core/ai/model';
|
||||
import { getHistories } from '../utils';
|
||||
import { formatModelChars2Points } from '@/service/support/wallet/usage/utils';
|
||||
import { formatModelChars2Points } from '@fastgpt/service/support/wallet/usage/utils';
|
||||
|
||||
type Props = ModuleDispatchProps<{
|
||||
[ModuleInputKeyEnum.aiModel]: string;
|
||||
@@ -46,7 +47,7 @@ export const dispatchClassifyQuestion = async (props: Props): Promise<CQResponse
|
||||
|
||||
const chatHistories = getHistories(history, histories);
|
||||
|
||||
const { arg, charsLength } = await (async () => {
|
||||
const { arg, tokens } = await (async () => {
|
||||
if (cqModel.toolChoice) {
|
||||
return toolChoice({
|
||||
...props,
|
||||
@@ -65,7 +66,7 @@ export const dispatchClassifyQuestion = async (props: Props): Promise<CQResponse
|
||||
|
||||
const { totalPoints, modelName } = formatModelChars2Points({
|
||||
model: cqModel.model,
|
||||
charsLength,
|
||||
tokens,
|
||||
modelType: ModelTypeEnum.llm
|
||||
});
|
||||
|
||||
@@ -75,7 +76,7 @@ export const dispatchClassifyQuestion = async (props: Props): Promise<CQResponse
|
||||
totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
|
||||
model: modelName,
|
||||
query: userChatInput,
|
||||
charsLength,
|
||||
tokens,
|
||||
cqList: agents,
|
||||
cqResult: result.value,
|
||||
contextTotalLen: chatHistories.length + 2
|
||||
@@ -85,7 +86,7 @@ export const dispatchClassifyQuestion = async (props: Props): Promise<CQResponse
|
||||
moduleName: name,
|
||||
totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
|
||||
model: modelName,
|
||||
charsLength
|
||||
tokens
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -136,6 +137,13 @@ ${systemPrompt}
|
||||
required: ['type']
|
||||
}
|
||||
};
|
||||
const tools: any = [
|
||||
{
|
||||
type: 'function',
|
||||
function: agentFunction
|
||||
}
|
||||
];
|
||||
|
||||
const ai = getAIApi({
|
||||
userKey: user.openaiAccount,
|
||||
timeout: 480000
|
||||
@@ -144,13 +152,8 @@ ${systemPrompt}
|
||||
const response = await ai.chat.completions.create({
|
||||
model: cqModel.model,
|
||||
temperature: 0,
|
||||
messages: [...adaptMessages],
|
||||
tools: [
|
||||
{
|
||||
type: 'function',
|
||||
function: agentFunction
|
||||
}
|
||||
],
|
||||
messages: adaptMessages,
|
||||
tools,
|
||||
tool_choice: { type: 'function', function: { name: agentFunName } }
|
||||
});
|
||||
|
||||
@@ -158,13 +161,10 @@ ${systemPrompt}
|
||||
const arg = JSON.parse(
|
||||
response?.choices?.[0]?.message?.tool_calls?.[0]?.function?.arguments || ''
|
||||
);
|
||||
const functionChars =
|
||||
agentFunction.description.length +
|
||||
agentFunction.parameters.properties.type.description.length;
|
||||
|
||||
return {
|
||||
arg,
|
||||
charsLength: countMessagesChars(messages) + functionChars
|
||||
tokens: countMessagesTokens(messages, tools)
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(agentFunction.parameters);
|
||||
@@ -174,7 +174,7 @@ ${systemPrompt}
|
||||
|
||||
return {
|
||||
arg: {},
|
||||
charsLength: 0
|
||||
tokens: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -216,7 +216,7 @@ async function completions({
|
||||
agents.find((item) => answer.includes(item.key) || answer.includes(item.value))?.key || '';
|
||||
|
||||
return {
|
||||
charsLength: countMessagesChars(messages),
|
||||
tokens: countMessagesTokens(messages),
|
||||
arg: { type: id }
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { adaptChat2GptMessages } from '@fastgpt/global/core/chat/adapt';
|
||||
import { ChatContextFilter, countMessagesChars } from '@fastgpt/service/core/chat/utils';
|
||||
import type { moduleDispatchResType, ChatItemType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { ChatContextFilter } from '@fastgpt/service/core/chat/utils';
|
||||
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { countMessagesTokens } from '@fastgpt/global/common/string/tiktoken';
|
||||
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { getAIApi } from '@fastgpt/service/core/ai/config';
|
||||
import type {
|
||||
@@ -14,7 +15,7 @@ import { replaceVariable } from '@fastgpt/global/common/string/tools';
|
||||
import { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
|
||||
import { getHistories } from '../utils';
|
||||
import { ModelTypeEnum, getLLMModel } from '@fastgpt/service/core/ai/model';
|
||||
import { formatModelChars2Points } from '@/service/support/wallet/usage/utils';
|
||||
import { formatModelChars2Points } from '@fastgpt/service/support/wallet/usage/utils';
|
||||
|
||||
type Props = ModuleDispatchProps<{
|
||||
[ModuleInputKeyEnum.history]?: ChatItemType[];
|
||||
@@ -46,7 +47,7 @@ export async function dispatchContentExtract(props: Props): Promise<Response> {
|
||||
const extractModel = getLLMModel(model);
|
||||
const chatHistories = getHistories(history, histories);
|
||||
|
||||
const { arg, charsLength } = await (async () => {
|
||||
const { arg, tokens } = await (async () => {
|
||||
if (extractModel.toolChoice) {
|
||||
return toolChoice({
|
||||
...props,
|
||||
@@ -85,7 +86,7 @@ export async function dispatchContentExtract(props: Props): Promise<Response> {
|
||||
|
||||
const { totalPoints, modelName } = formatModelChars2Points({
|
||||
model: extractModel.model,
|
||||
charsLength,
|
||||
tokens,
|
||||
modelType: ModelTypeEnum.llm
|
||||
});
|
||||
|
||||
@@ -98,7 +99,7 @@ export async function dispatchContentExtract(props: Props): Promise<Response> {
|
||||
totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
|
||||
model: modelName,
|
||||
query: content,
|
||||
charsLength,
|
||||
tokens,
|
||||
extractDescription: description,
|
||||
extractResult: arg,
|
||||
contextTotalLen: chatHistories.length + 2
|
||||
@@ -108,7 +109,7 @@ export async function dispatchContentExtract(props: Props): Promise<Response> {
|
||||
moduleName: name,
|
||||
totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
|
||||
model: modelName,
|
||||
charsLength
|
||||
tokens
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -170,6 +171,12 @@ ${description || '根据用户要求获取适当的 JSON 字符串。'}
|
||||
required: extractKeys.filter((item) => item.required).map((item) => item.key)
|
||||
}
|
||||
};
|
||||
const tools: any = [
|
||||
{
|
||||
type: 'function',
|
||||
function: agentFunction
|
||||
}
|
||||
];
|
||||
|
||||
const ai = getAIApi({
|
||||
userKey: user.openaiAccount,
|
||||
@@ -180,12 +187,7 @@ ${description || '根据用户要求获取适当的 JSON 字符串。'}
|
||||
model: extractModel.model,
|
||||
temperature: 0,
|
||||
messages: [...adaptMessages],
|
||||
tools: [
|
||||
{
|
||||
type: 'function',
|
||||
function: agentFunction
|
||||
}
|
||||
],
|
||||
tools,
|
||||
tool_choice: { type: 'function', function: { name: agentFunName } }
|
||||
});
|
||||
|
||||
@@ -202,12 +204,9 @@ ${description || '根据用户要求获取适当的 JSON 字符串。'}
|
||||
}
|
||||
})();
|
||||
|
||||
const functionChars =
|
||||
description.length + extractKeys.reduce((sum, item) => sum + item.desc.length, 0);
|
||||
|
||||
return {
|
||||
rawResponse: response?.choices?.[0]?.message?.tool_calls?.[0]?.function?.arguments || '',
|
||||
charsLength: countMessagesChars(messages) + functionChars,
|
||||
tokens: countMessagesTokens(messages, tools),
|
||||
arg
|
||||
};
|
||||
}
|
||||
@@ -257,7 +256,7 @@ Human: ${content}`
|
||||
if (start === -1 || end === -1)
|
||||
return {
|
||||
rawResponse: answer,
|
||||
charsLength: countMessagesChars(messages),
|
||||
tokens: countMessagesTokens(messages),
|
||||
arg: {}
|
||||
};
|
||||
|
||||
@@ -269,14 +268,14 @@ Human: ${content}`
|
||||
try {
|
||||
return {
|
||||
rawResponse: answer,
|
||||
charsLength: countMessagesChars(messages),
|
||||
tokens: countMessagesTokens(messages),
|
||||
|
||||
arg: JSON.parse(jsonStr) as Record<string, any>
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
rawResponse: answer,
|
||||
charsLength: countMessagesChars(messages),
|
||||
tokens: countMessagesTokens(messages),
|
||||
arg: {}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import type { NextApiResponse } from 'next';
|
||||
import { ChatContextFilter, countMessagesChars } from '@fastgpt/service/core/chat/utils';
|
||||
import type { moduleDispatchResType, ChatItemType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { ChatContextFilter } from '@fastgpt/service/core/chat/utils';
|
||||
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { sseResponseEventEnum } from '@fastgpt/service/common/response/constant';
|
||||
import { textAdaptGptResponse } from '@/utils/adapt';
|
||||
import { getAIApi } from '@fastgpt/service/core/ai/config';
|
||||
import type { ChatCompletion, StreamChatType } from '@fastgpt/global/core/ai/type.d';
|
||||
import { formatModelChars2Points } from '@/service/support/wallet/usage/utils';
|
||||
import { formatModelChars2Points } from '@fastgpt/service/support/wallet/usage/utils';
|
||||
import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
|
||||
import { postTextCensor } from '@/service/common/censor';
|
||||
import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/global/core/ai/constant';
|
||||
import type { ModuleDispatchResponse, ModuleItemType } from '@fastgpt/global/core/module/type.d';
|
||||
import { countMessagesTokens, sliceMessagesTB } from '@fastgpt/global/common/string/tiktoken';
|
||||
import { countMessagesTokens } from '@fastgpt/global/common/string/tiktoken';
|
||||
import { adaptChat2GptMessages } from '@fastgpt/global/core/chat/adapt';
|
||||
import { Prompt_QuotePromptList, Prompt_QuoteTemplateList } from '@/global/core/prompt/AIChat';
|
||||
import type { AIChatModuleProps } from '@fastgpt/global/core/module/node/type.d';
|
||||
@@ -98,7 +98,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
|
||||
userChatInput,
|
||||
systemPrompt
|
||||
});
|
||||
const { max_tokens } = getMaxTokens({
|
||||
const { max_tokens } = await getMaxTokens({
|
||||
model: modelConstantsData,
|
||||
maxToken,
|
||||
filterMessages
|
||||
@@ -137,8 +137,6 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
|
||||
|
||||
const response = await ai.chat.completions.create(
|
||||
{
|
||||
presence_penalty: 0,
|
||||
frequency_penalty: 0,
|
||||
...modelConstantsData?.defaultConfig,
|
||||
model: modelConstantsData.model,
|
||||
temperature,
|
||||
@@ -189,10 +187,10 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
|
||||
}
|
||||
})();
|
||||
|
||||
const charsLength = countMessagesChars(completeMessages);
|
||||
const tokens = countMessagesTokens(completeMessages);
|
||||
const { totalPoints, modelName } = formatModelChars2Points({
|
||||
model,
|
||||
charsLength,
|
||||
tokens,
|
||||
modelType: ModelTypeEnum.llm
|
||||
});
|
||||
|
||||
@@ -201,7 +199,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
|
||||
[ModuleOutputKeyEnum.responseData]: {
|
||||
totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
|
||||
model: modelName,
|
||||
charsLength,
|
||||
tokens,
|
||||
query: `${userChatInput}`,
|
||||
maxToken: max_tokens,
|
||||
quoteList: filterQuoteQA,
|
||||
@@ -213,7 +211,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
|
||||
moduleName: name,
|
||||
totalPoints: user.openaiAccount?.key ? 0 : totalPoints,
|
||||
model: modelName,
|
||||
charsLength
|
||||
tokens
|
||||
}
|
||||
],
|
||||
history: completeMessages
|
||||
@@ -292,7 +290,7 @@ function getChatMessages({
|
||||
|
||||
const filterMessages = ChatContextFilter({
|
||||
messages,
|
||||
maxTokens: Math.ceil(model.maxContext - 300) // filter token. not response maxToken
|
||||
maxTokens: model.maxContext - 300 // filter token. not response maxToken
|
||||
});
|
||||
|
||||
const adaptMessages = adaptChat2GptMessages({ messages: filterMessages, reserveId: false });
|
||||
@@ -315,11 +313,12 @@ function getMaxTokens({
|
||||
const tokensLimit = model.maxContext;
|
||||
|
||||
/* count response max token */
|
||||
const promptsToken = countMessagesTokens({
|
||||
messages: filterMessages
|
||||
});
|
||||
const promptsToken = countMessagesTokens(filterMessages);
|
||||
maxToken = promptsToken + maxToken > tokensLimit ? tokensLimit - promptsToken : maxToken;
|
||||
|
||||
if (maxToken <= 0) {
|
||||
return Promise.reject('Over max token');
|
||||
}
|
||||
return {
|
||||
max_tokens: maxToken
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { moduleDispatchResType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { formatModelChars2Points } from '@/service/support/wallet/usage/utils';
|
||||
import { formatModelChars2Points } from '@fastgpt/service/support/wallet/usage/utils';
|
||||
import type { SelectedDatasetType } from '@fastgpt/global/core/module/api.d';
|
||||
import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
|
||||
import type {
|
||||
@@ -12,7 +12,7 @@ import { ModuleInputKeyEnum, ModuleOutputKeyEnum } from '@fastgpt/global/core/mo
|
||||
import { DatasetSearchModeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { getHistories } from '../utils';
|
||||
import { datasetSearchQueryExtension } from '@fastgpt/service/core/dataset/search/utils';
|
||||
import { ChatModuleBillType } from '@fastgpt/global/support/wallet/bill/type';
|
||||
import { ChatModuleUsageType } from '@fastgpt/global/support/wallet/bill/type';
|
||||
import { checkTeamReRankPermission } from '@fastgpt/service/support/permission/teamLimit';
|
||||
|
||||
type DatasetSearchProps = ModuleDispatchProps<{
|
||||
@@ -85,7 +85,7 @@ export async function dispatchDatasetSearch(
|
||||
// start search
|
||||
const {
|
||||
searchRes,
|
||||
charsLength,
|
||||
tokens,
|
||||
usingSimilarityFilter,
|
||||
usingReRank: searchUsingReRank
|
||||
} = await searchDatasetData({
|
||||
@@ -104,37 +104,37 @@ export async function dispatchDatasetSearch(
|
||||
// vector
|
||||
const { totalPoints, modelName } = formatModelChars2Points({
|
||||
model: vectorModel.model,
|
||||
charsLength,
|
||||
tokens,
|
||||
modelType: ModelTypeEnum.vector
|
||||
});
|
||||
const responseData: moduleDispatchResType & { totalPoints: number } = {
|
||||
totalPoints,
|
||||
query: concatQueries.join('\n'),
|
||||
model: modelName,
|
||||
charsLength,
|
||||
tokens,
|
||||
similarity: usingSimilarityFilter ? similarity : undefined,
|
||||
limit,
|
||||
searchMode,
|
||||
searchUsingReRank: searchUsingReRank
|
||||
};
|
||||
const moduleDispatchBills: ChatModuleBillType[] = [
|
||||
const moduleDispatchBills: ChatModuleUsageType[] = [
|
||||
{
|
||||
totalPoints,
|
||||
moduleName: module.name,
|
||||
model: modelName,
|
||||
charsLength
|
||||
tokens
|
||||
}
|
||||
];
|
||||
|
||||
if (aiExtensionResult) {
|
||||
const { totalPoints, modelName } = formatModelChars2Points({
|
||||
model: aiExtensionResult.model,
|
||||
charsLength: aiExtensionResult.charsLength,
|
||||
tokens: aiExtensionResult.tokens,
|
||||
modelType: ModelTypeEnum.llm
|
||||
});
|
||||
|
||||
responseData.totalPoints += totalPoints;
|
||||
responseData.charsLength = aiExtensionResult.charsLength;
|
||||
responseData.tokens = aiExtensionResult.tokens;
|
||||
responseData.extensionModel = modelName;
|
||||
responseData.extensionResult =
|
||||
aiExtensionResult.extensionQueries?.join('\n') ||
|
||||
@@ -144,7 +144,7 @@ export async function dispatchDatasetSearch(
|
||||
totalPoints,
|
||||
moduleName: 'core.module.template.Query extension',
|
||||
model: modelName,
|
||||
charsLength: aiExtensionResult.charsLength
|
||||
tokens: aiExtensionResult.tokens
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ import { dispatchRunPlugin } from './plugin/run';
|
||||
import { dispatchPluginInput } from './plugin/runInput';
|
||||
import { dispatchPluginOutput } from './plugin/runOutput';
|
||||
import { valueTypeFormat } from './utils';
|
||||
import { ChatModuleBillType } from '@fastgpt/global/support/wallet/bill/type';
|
||||
import { ChatModuleUsageType } from '@fastgpt/global/support/wallet/bill/type';
|
||||
|
||||
const callbackMap: Record<`${FlowNodeTypeEnum}`, Function> = {
|
||||
[FlowNodeTypeEnum.historyNode]: dispatchHistory,
|
||||
@@ -83,7 +83,7 @@ export async function dispatchModules({
|
||||
// let storeData: Record<string, any> = {}; // after module used
|
||||
let chatResponse: ChatHistoryItemResType[] = []; // response request and save to database
|
||||
let chatAnswerText = ''; // AI answer
|
||||
let chatModuleBills: ChatModuleBillType[] = [];
|
||||
let chatModuleBills: ChatModuleUsageType[] = [];
|
||||
let runningTime = Date.now();
|
||||
|
||||
function pushStore(
|
||||
@@ -95,7 +95,7 @@ export async function dispatchModules({
|
||||
}: {
|
||||
answerText?: string;
|
||||
responseData?: ChatHistoryItemResType | ChatHistoryItemResType[];
|
||||
moduleDispatchBills?: ChatModuleBillType[];
|
||||
moduleDispatchBills?: ChatModuleUsageType[];
|
||||
}
|
||||
) {
|
||||
const time = Date.now();
|
||||
@@ -165,7 +165,6 @@ export async function dispatchModules({
|
||||
const filterModules = nextRunModules.filter((module) => {
|
||||
if (set.has(module.moduleId)) return false;
|
||||
set.add(module.moduleId);
|
||||
``;
|
||||
return true;
|
||||
});
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
|
||||
moduleName: plugin.name,
|
||||
totalPoints: moduleDispatchBills.reduce((sum, item) => sum + (item.totalPoints || 0), 0),
|
||||
model: plugin.name,
|
||||
charsLength: 0
|
||||
tokens: 0
|
||||
}
|
||||
],
|
||||
...(output ? output.pluginOutput : {})
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
import axios from 'axios';
|
||||
import { valueTypeFormat } from '../utils';
|
||||
import { SERVICE_LOCAL_HOST } from '@fastgpt/service/common/system/tools';
|
||||
import { addLog } from '@fastgpt/service/common/system/log';
|
||||
|
||||
type PropsArrType = {
|
||||
key: string;
|
||||
@@ -130,7 +131,7 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
||||
...results
|
||||
};
|
||||
} catch (error) {
|
||||
const err = httpRequestErrorResponseData(error)
|
||||
addLog.error('Http request error', error);
|
||||
return {
|
||||
[ModuleOutputKeyEnum.failed]: true,
|
||||
[ModuleOutputKeyEnum.responseData]: {
|
||||
@@ -138,7 +139,7 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
||||
params: Object.keys(params).length > 0 ? params : undefined,
|
||||
body: Object.keys(requestBody).length > 0 ? requestBody : undefined,
|
||||
headers: Object.keys(headers).length > 0 ? headers : undefined,
|
||||
httpResult: { error: err }
|
||||
httpResult: { error: formatHttpError(error) }
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -280,21 +281,14 @@ function removeUndefinedSign(obj: Record<string, any>) {
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
function httpRequestErrorResponseData(error: any) {
|
||||
try {
|
||||
return {
|
||||
message: error?.message || undefined,
|
||||
name: error?.name || undefined,
|
||||
method: error?.config?.method || undefined,
|
||||
baseURL: error?.config?.baseURL || undefined,
|
||||
url: error?.config?.url || undefined,
|
||||
code: error?.code || undefined,
|
||||
status: error?.status || undefined
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
message: 'Request Failed',
|
||||
name: "AxiosError",
|
||||
};
|
||||
}
|
||||
function formatHttpError(error: any) {
|
||||
return {
|
||||
message: error?.message,
|
||||
name: error?.name,
|
||||
method: error?.config?.method,
|
||||
baseURL: error?.config?.baseURL,
|
||||
url: error?.config?.url,
|
||||
code: error?.code,
|
||||
status: error?.status
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import type {
|
||||
} from '@fastgpt/global/core/module/type.d';
|
||||
import { ModuleInputKeyEnum, ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
|
||||
import { ModelTypeEnum, getLLMModel } from '@fastgpt/service/core/ai/model';
|
||||
import { formatModelChars2Points } from '@/service/support/wallet/usage/utils';
|
||||
import { formatModelChars2Points } from '@fastgpt/service/support/wallet/usage/utils';
|
||||
import { queryExtension } from '@fastgpt/service/core/ai/functions/queryExtension';
|
||||
import { getHistories } from '../utils';
|
||||
import { hashStr } from '@fastgpt/global/common/string/tools';
|
||||
@@ -32,7 +32,7 @@ export const dispatchQueryExtension = async ({
|
||||
const queryExtensionModel = getLLMModel(model);
|
||||
const chatHistories = getHistories(history, histories);
|
||||
|
||||
const { extensionQueries, charsLength } = await queryExtension({
|
||||
const { extensionQueries, tokens } = await queryExtension({
|
||||
chatBg: systemPrompt,
|
||||
query: userChatInput,
|
||||
histories: chatHistories,
|
||||
@@ -43,7 +43,7 @@ export const dispatchQueryExtension = async ({
|
||||
|
||||
const { totalPoints, modelName } = formatModelChars2Points({
|
||||
model: queryExtensionModel.model,
|
||||
charsLength,
|
||||
tokens,
|
||||
modelType: ModelTypeEnum.llm
|
||||
});
|
||||
|
||||
@@ -60,7 +60,7 @@ export const dispatchQueryExtension = async ({
|
||||
[ModuleOutputKeyEnum.responseData]: {
|
||||
totalPoints,
|
||||
model: modelName,
|
||||
charsLength,
|
||||
tokens,
|
||||
query: userChatInput,
|
||||
textOutput: JSON.stringify(filterSameQueries)
|
||||
},
|
||||
@@ -69,7 +69,7 @@ export const dispatchQueryExtension = async ({
|
||||
moduleName: module.name,
|
||||
totalPoints,
|
||||
model: modelName,
|
||||
charsLength
|
||||
tokens
|
||||
}
|
||||
],
|
||||
[ModuleOutputKeyEnum.text]: JSON.stringify(filterSameQueries)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { startQueue } from './utils/tools';
|
||||
import { PRICE_SCALE } from '@fastgpt/global/support/wallet/constants';
|
||||
import { MongoUser } from '@fastgpt/service/support/user/schema';
|
||||
import { connectMongo } from '@fastgpt/service/common/mongo/init';
|
||||
@@ -9,22 +8,29 @@ import { initVectorStore } from '@fastgpt/service/common/vectorStore/controller'
|
||||
import { getInitConfig } from '@/pages/api/common/system/getInitData';
|
||||
import { startCron } from './common/system/cron';
|
||||
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
||||
import { initGlobal } from './common/system';
|
||||
import { startMongoWatch } from './common/system/volumnMongoWatch';
|
||||
import { startTrainingQueue } from './core/dataset/training/utils';
|
||||
|
||||
/**
|
||||
* connect MongoDB and init data
|
||||
*/
|
||||
export function connectToDatabase(): Promise<void> {
|
||||
return connectMongo({
|
||||
beforeHook: () => {},
|
||||
beforeHook: () => {
|
||||
initGlobal();
|
||||
},
|
||||
afterHook: async () => {
|
||||
initVectorStore();
|
||||
// start queue
|
||||
startQueue();
|
||||
startMongoWatch();
|
||||
// cron
|
||||
startCron();
|
||||
// init system config
|
||||
getInitConfig();
|
||||
|
||||
// cron
|
||||
startCron();
|
||||
// init vector database
|
||||
await initVectorStore();
|
||||
// start queue
|
||||
startTrainingQueue(true);
|
||||
|
||||
initRootUser();
|
||||
}
|
||||
@@ -62,7 +68,7 @@ async function initRootUser() {
|
||||
rootId = _id;
|
||||
}
|
||||
// init root team
|
||||
await createDefaultTeam({ userId: rootId, maxSize: 1, balance: 9999 * PRICE_SCALE, session });
|
||||
await createDefaultTeam({ userId: rootId, balance: 9999 * PRICE_SCALE, session });
|
||||
});
|
||||
|
||||
console.log(`root user init:`, {
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import { ChatSchema } from '@fastgpt/global/core/chat/type';
|
||||
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||
import { AuthModeType } from '@fastgpt/service/support/permission/type';
|
||||
import { authOutLink } from './outLink';
|
||||
import { authOutLink, authOutLinkInit } from './outLink';
|
||||
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
|
||||
import { authUserRole } from '@fastgpt/service/support/permission/auth/user';
|
||||
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
|
||||
import { AuthResponseType } from '@fastgpt/global/support/permission/type';
|
||||
import { authTeamSpaceToken } from './team';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { authOutLinkValid } from '@fastgpt/service/support/permission/auth/outLink';
|
||||
import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
|
||||
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
|
||||
/*
|
||||
outLink: Must be the owner
|
||||
token: team owner and chat owner have all permissions
|
||||
@@ -14,46 +19,51 @@ export async function autChatCrud({
|
||||
appId,
|
||||
chatId,
|
||||
shareId,
|
||||
shareTeamId,
|
||||
outLinkUid,
|
||||
|
||||
teamId: spaceTeamId,
|
||||
teamToken,
|
||||
per = 'owner',
|
||||
...props
|
||||
}: AuthModeType & {
|
||||
appId: string;
|
||||
chatId?: string;
|
||||
shareTeamId?: string;
|
||||
shareId?: string;
|
||||
outLinkUid?: string;
|
||||
|
||||
teamId?: string;
|
||||
teamToken?: string;
|
||||
}): Promise<{
|
||||
chat?: ChatSchema;
|
||||
isOutLink: boolean;
|
||||
uid?: string;
|
||||
}> {
|
||||
const isOutLink = Boolean((shareId || shareTeamId) && outLinkUid);
|
||||
const isOutLink = Boolean((shareId || spaceTeamId) && outLinkUid);
|
||||
if (!chatId) return { isOutLink, uid: outLinkUid };
|
||||
|
||||
const chat = await MongoChat.findOne({ appId, chatId }).lean();
|
||||
|
||||
if (!chat) return { isOutLink, uid: outLinkUid };
|
||||
|
||||
const { uid } = await (async () => {
|
||||
// outLink Auth
|
||||
if (shareId && outLinkUid) {
|
||||
const { uid } = await authOutLink({ shareId, outLinkUid });
|
||||
|
||||
// auth outLinkUid
|
||||
if (chat.shareId === shareId && chat.outLinkUid === uid) {
|
||||
if (!chat || (chat.shareId === shareId && chat.outLinkUid === uid)) {
|
||||
return { uid };
|
||||
}
|
||||
return Promise.reject(ChatErrEnum.unAuthChat);
|
||||
}
|
||||
if (shareTeamId && outLinkUid) {
|
||||
if (chat.teamId == shareTeamId && chat.outLinkUid === outLinkUid) {
|
||||
return { uid: outLinkUid };
|
||||
// auth team space chat
|
||||
if (spaceTeamId && teamToken) {
|
||||
const { uid } = await authTeamSpaceToken({ teamId: spaceTeamId, teamToken });
|
||||
if (!chat || (String(chat.teamId) === String(spaceTeamId) && chat.outLinkUid === uid)) {
|
||||
return { uid };
|
||||
}
|
||||
return Promise.reject(ChatErrEnum.unAuthChat);
|
||||
}
|
||||
|
||||
// req auth
|
||||
if (!chat) return { id: outLinkUid };
|
||||
|
||||
// auth req
|
||||
const { teamId, tmbId, role } = await authUserRole(props);
|
||||
|
||||
if (String(teamId) !== String(chat.teamId)) return Promise.reject(ChatErrEnum.unAuthChat);
|
||||
@@ -67,9 +77,61 @@ export async function autChatCrud({
|
||||
return Promise.reject(ChatErrEnum.unAuthChat);
|
||||
})();
|
||||
|
||||
if (!chat) return { isOutLink, uid };
|
||||
|
||||
return {
|
||||
chat,
|
||||
isOutLink,
|
||||
uid
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
Different chat source
|
||||
1. token (header)
|
||||
2. apikey (header)
|
||||
3. share page (body: shareId outLinkUid)
|
||||
4. team chat page (body: teamId teamToken)
|
||||
*/
|
||||
export async function authChatCert(props: AuthModeType) {
|
||||
const { teamId, teamToken, shareId, outLinkUid } = props.req.body as OutLinkChatAuthProps;
|
||||
|
||||
if (shareId && outLinkUid) {
|
||||
const { shareChat } = await authOutLinkValid({ shareId });
|
||||
const { uid } = await authOutLinkInit({
|
||||
outLinkUid,
|
||||
tokenUrl: shareChat.limit?.hookUrl
|
||||
});
|
||||
|
||||
return {
|
||||
teamId: String(shareChat.teamId),
|
||||
tmbId: String(shareChat.tmbId),
|
||||
authType: AuthUserTypeEnum.outLink,
|
||||
apikey: '',
|
||||
isOwner: false,
|
||||
canWrite: false,
|
||||
outLinkUid: uid
|
||||
};
|
||||
}
|
||||
if (teamId && teamToken) {
|
||||
const { uid } = await authTeamSpaceToken({ teamId, teamToken });
|
||||
const tmb = await MongoTeamMember.findOne(
|
||||
{ teamId, role: TeamMemberRoleEnum.owner },
|
||||
'tmbId'
|
||||
).lean();
|
||||
|
||||
if (!tmb) return Promise.reject(ChatErrEnum.unAuthChat);
|
||||
|
||||
return {
|
||||
teamId,
|
||||
tmbId: String(tmb._id),
|
||||
authType: AuthUserTypeEnum.teamDomain,
|
||||
apikey: '',
|
||||
isOwner: false,
|
||||
canWrite: false,
|
||||
outLinkUid: uid
|
||||
};
|
||||
}
|
||||
|
||||
return authCert(props);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { UserErrEnum } from '@fastgpt/global/common/error/code/user';
|
||||
import { TeamMemberWithUserSchema } from '@fastgpt/global/support/user/team/type';
|
||||
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
|
||||
import { MongoTeam } from '@fastgpt/service/support/user/team/teamSchema';
|
||||
import { checkTeamAIPoints } from '@fastgpt/service/support/permission/teamLimit';
|
||||
import axios from 'axios';
|
||||
import { GET } from '@fastgpt/service/common/api/plusRequest';
|
||||
import {
|
||||
AuthTeamTagTokenProps,
|
||||
AuthTokenFromTeamDomainResponse
|
||||
} from '@fastgpt/global/support/user/team/tag';
|
||||
|
||||
export async function getUserChatInfoAndAuthTeamPoints(tmbId: string) {
|
||||
const tmb = (await MongoTeamMember.findById(tmbId, 'teamId userId').populate(
|
||||
@@ -19,25 +22,21 @@ export async function getUserChatInfoAndAuthTeamPoints(tmbId: string) {
|
||||
};
|
||||
}
|
||||
|
||||
type UserInfoType = {
|
||||
data: {
|
||||
uid: string;
|
||||
tags: string[];
|
||||
};
|
||||
};
|
||||
|
||||
export async function getShareTeamUid(shareTeamId: string, authToken: string) {
|
||||
try {
|
||||
const teamInfo = await MongoTeam.findById(shareTeamId);
|
||||
const tagsUrl = teamInfo?.tagsUrl;
|
||||
const { data: userInfo } = await axios.post(tagsUrl + `/getUserInfo`, { autoken: authToken });
|
||||
|
||||
const uid = userInfo?.data?.uid;
|
||||
if (uid) {
|
||||
throw new Error('uid null');
|
||||
}
|
||||
return uid;
|
||||
} catch (err) {
|
||||
return '';
|
||||
}
|
||||
export function authTeamTagToken(data: AuthTeamTagTokenProps) {
|
||||
return GET<AuthTokenFromTeamDomainResponse['data']>('/support/user/team/tag/authTeamToken', data);
|
||||
}
|
||||
export async function authTeamSpaceToken({
|
||||
teamId,
|
||||
teamToken
|
||||
}: {
|
||||
teamId: string;
|
||||
teamToken: string;
|
||||
}) {
|
||||
// get outLink and app
|
||||
const data = await authTeamTagToken({ teamId, teamToken });
|
||||
const uid = data.uid;
|
||||
|
||||
return {
|
||||
uid
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
import { POST } from '@fastgpt/service/common/api/plusRequest';
|
||||
import type { AuthOutLinkChatProps } from '@fastgpt/global/support/outLink/api.d';
|
||||
import type { chatAppListSchema } from '@fastgpt/global/core/chat/type.d';
|
||||
import { getUserChatInfoAndAuthTeamPoints } from './team';
|
||||
import { MongoTeam } from '@fastgpt/service/support/user/team/teamSchema';
|
||||
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
|
||||
|
||||
export function authChatTeamInfo(data: { shareTeamId: string; authToken: string }) {
|
||||
return POST<chatAppListSchema>('/core/chat/init', data);
|
||||
}
|
||||
|
||||
export async function authTeamShareChatStart({
|
||||
teamId,
|
||||
ip,
|
||||
outLinkUid,
|
||||
question
|
||||
}: AuthOutLinkChatProps & {
|
||||
teamId: string;
|
||||
}) {
|
||||
// get outLink and app
|
||||
const { teamInfo, uid } = await authChatTeamInfo({ shareTeamId: teamId, authToken: outLinkUid });
|
||||
// check balance and chat limit
|
||||
const tmb = await MongoTeamMember.findOne({ teamId, userId: String(teamInfo.ownerId) });
|
||||
|
||||
if (!tmb) {
|
||||
throw new Error('can not find it');
|
||||
}
|
||||
|
||||
const { user } = await getUserChatInfoAndAuthTeamPoints(String(tmb._id));
|
||||
|
||||
return {
|
||||
user,
|
||||
tmbId: String(tmb._id),
|
||||
uid: uid
|
||||
};
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants';
|
||||
import { ModelTypeEnum } from '@fastgpt/service/core/ai/model';
|
||||
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { addLog } from '@fastgpt/service/common/system/log';
|
||||
import { createUsage, concatUsage } from './controller';
|
||||
import { formatModelChars2Points } from '@/service/support/wallet/usage/utils';
|
||||
import { ChatModuleBillType } from '@fastgpt/global/support/wallet/bill/type';
|
||||
import { formatModelChars2Points } from '@fastgpt/service/support/wallet/usage/utils';
|
||||
import { ChatModuleUsageType } from '@fastgpt/global/support/wallet/bill/type';
|
||||
|
||||
export const pushChatUsage = ({
|
||||
appName,
|
||||
@@ -19,7 +18,7 @@ export const pushChatUsage = ({
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
source: `${UsageSourceEnum}`;
|
||||
moduleDispatchBills: ChatModuleBillType[];
|
||||
moduleDispatchBills: ChatModuleUsageType[];
|
||||
}) => {
|
||||
const totalPoints = moduleDispatchBills.reduce((sum, item) => sum + (item.totalPoints || 0), 0);
|
||||
|
||||
@@ -34,7 +33,7 @@ export const pushChatUsage = ({
|
||||
moduleName: item.moduleName,
|
||||
amount: item.totalPoints || 0,
|
||||
model: item.model,
|
||||
charsLength: item.charsLength
|
||||
tokens: item.tokens
|
||||
}))
|
||||
});
|
||||
addLog.info(`finish completions`, {
|
||||
@@ -50,20 +49,20 @@ export const pushQAUsage = async ({
|
||||
teamId,
|
||||
tmbId,
|
||||
model,
|
||||
charsLength,
|
||||
tokens,
|
||||
billId
|
||||
}: {
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
model: string;
|
||||
charsLength: number;
|
||||
tokens: number;
|
||||
billId: string;
|
||||
}) => {
|
||||
// 计算价格
|
||||
const { totalPoints } = formatModelChars2Points({
|
||||
model,
|
||||
modelType: ModelTypeEnum.llm,
|
||||
charsLength
|
||||
tokens
|
||||
});
|
||||
|
||||
concatUsage({
|
||||
@@ -71,7 +70,7 @@ export const pushQAUsage = async ({
|
||||
teamId,
|
||||
tmbId,
|
||||
totalPoints,
|
||||
charsLength,
|
||||
tokens,
|
||||
listIndex: 1
|
||||
});
|
||||
|
||||
@@ -82,30 +81,30 @@ export const pushGenerateVectorUsage = ({
|
||||
billId,
|
||||
teamId,
|
||||
tmbId,
|
||||
charsLength,
|
||||
tokens,
|
||||
model,
|
||||
source = UsageSourceEnum.fastgpt,
|
||||
extensionModel,
|
||||
extensionCharsLength
|
||||
extensionTokens
|
||||
}: {
|
||||
billId?: string;
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
charsLength: number;
|
||||
tokens: number;
|
||||
model: string;
|
||||
source?: `${UsageSourceEnum}`;
|
||||
|
||||
extensionModel?: string;
|
||||
extensionCharsLength?: number;
|
||||
extensionTokens?: number;
|
||||
}) => {
|
||||
const { totalPoints: totalVector, modelName: vectorModelName } = formatModelChars2Points({
|
||||
modelType: ModelTypeEnum.vector,
|
||||
model,
|
||||
charsLength
|
||||
tokens
|
||||
});
|
||||
|
||||
const { extensionTotalPoints, extensionModelName } = (() => {
|
||||
if (!extensionModel || !extensionCharsLength)
|
||||
if (!extensionModel || !extensionTokens)
|
||||
return {
|
||||
extensionTotalPoints: 0,
|
||||
extensionModelName: ''
|
||||
@@ -113,7 +112,7 @@ export const pushGenerateVectorUsage = ({
|
||||
const { totalPoints, modelName } = formatModelChars2Points({
|
||||
modelType: ModelTypeEnum.llm,
|
||||
model: extensionModel,
|
||||
charsLength: extensionCharsLength
|
||||
tokens: extensionTokens
|
||||
});
|
||||
return {
|
||||
extensionTotalPoints: totalPoints,
|
||||
@@ -130,7 +129,7 @@ export const pushGenerateVectorUsage = ({
|
||||
tmbId,
|
||||
totalPoints,
|
||||
billId,
|
||||
charsLength,
|
||||
tokens,
|
||||
listIndex: 0
|
||||
});
|
||||
} else {
|
||||
@@ -145,7 +144,7 @@ export const pushGenerateVectorUsage = ({
|
||||
moduleName: 'support.wallet.moduleName.index',
|
||||
amount: totalVector,
|
||||
model: vectorModelName,
|
||||
charsLength
|
||||
tokens
|
||||
},
|
||||
...(extensionModel !== undefined
|
||||
? [
|
||||
@@ -153,7 +152,7 @@ export const pushGenerateVectorUsage = ({
|
||||
moduleName: 'core.module.template.Query extension',
|
||||
amount: extensionTotalPoints,
|
||||
model: extensionModelName,
|
||||
charsLength: extensionCharsLength
|
||||
tokens: extensionTokens
|
||||
}
|
||||
]
|
||||
: [])
|
||||
@@ -164,17 +163,17 @@ export const pushGenerateVectorUsage = ({
|
||||
};
|
||||
|
||||
export const pushQuestionGuideUsage = ({
|
||||
charsLength,
|
||||
tokens,
|
||||
teamId,
|
||||
tmbId
|
||||
}: {
|
||||
charsLength: number;
|
||||
tokens: number;
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
}) => {
|
||||
const qgModel = global.llmModels[0];
|
||||
const { totalPoints, modelName } = formatModelChars2Points({
|
||||
charsLength,
|
||||
tokens,
|
||||
model: qgModel.model,
|
||||
modelType: ModelTypeEnum.llm
|
||||
});
|
||||
@@ -190,14 +189,14 @@ export const pushQuestionGuideUsage = ({
|
||||
moduleName: 'core.app.Next Step Guide',
|
||||
amount: totalPoints,
|
||||
model: modelName,
|
||||
charsLength
|
||||
tokens
|
||||
}
|
||||
]
|
||||
});
|
||||
};
|
||||
|
||||
export function pushAudioSpeechUsage({
|
||||
appName = 'support.wallet.bill.Audio Speech',
|
||||
appName = 'support.wallet.usage.Audio Speech',
|
||||
model,
|
||||
charsLength,
|
||||
teamId,
|
||||
@@ -213,7 +212,7 @@ export function pushAudioSpeechUsage({
|
||||
}) {
|
||||
const { totalPoints, modelName } = formatModelChars2Points({
|
||||
model,
|
||||
charsLength,
|
||||
tokens: charsLength,
|
||||
modelType: ModelTypeEnum.audioSpeech
|
||||
});
|
||||
|
||||
@@ -249,12 +248,12 @@ export function pushWhisperUsage({
|
||||
|
||||
const { totalPoints, modelName } = formatModelChars2Points({
|
||||
model: whisperModel.model,
|
||||
charsLength: duration,
|
||||
tokens: duration,
|
||||
modelType: ModelTypeEnum.whisper,
|
||||
multiple: 60
|
||||
});
|
||||
|
||||
const name = 'support.wallet.bill.Whisper';
|
||||
const name = 'support.wallet.usage.Whisper';
|
||||
|
||||
createUsage({
|
||||
teamId,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { ModelTypeEnum, getModelMap } from '@fastgpt/service/core/ai/model';
|
||||
import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants';
|
||||
|
||||
@@ -16,29 +15,3 @@ export function authType2UsageSource({
|
||||
if (authType === AuthUserTypeEnum.apikey) return UsageSourceEnum.api;
|
||||
return UsageSourceEnum.fastgpt;
|
||||
}
|
||||
|
||||
export const formatModelChars2Points = ({
|
||||
model,
|
||||
charsLength = 0,
|
||||
modelType,
|
||||
multiple = 1000
|
||||
}: {
|
||||
model: string;
|
||||
charsLength: number;
|
||||
modelType: `${ModelTypeEnum}`;
|
||||
multiple?: number;
|
||||
}) => {
|
||||
const modelData = getModelMap?.[modelType]?.(model);
|
||||
if (!modelData)
|
||||
return {
|
||||
totalPoints: 0,
|
||||
modelName: ''
|
||||
};
|
||||
|
||||
const totalPoints = (modelData.charsPointsPrice || 0) * (charsLength / multiple);
|
||||
|
||||
return {
|
||||
modelName: modelData.name,
|
||||
totalPoints
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
import { generateQA } from '../events/generateQA';
|
||||
import { generateVector } from '../events/generateVector';
|
||||
|
||||
/* start task */
|
||||
export const startQueue = () => {
|
||||
if (!global.systemEnv) return;
|
||||
|
||||
generateQA();
|
||||
generateVector();
|
||||
};
|
||||
2
projects/app/src/types/index.d.ts
vendored
2
projects/app/src/types/index.d.ts
vendored
@@ -9,7 +9,6 @@ import {
|
||||
} from '@fastgpt/global/core/ai/model.d';
|
||||
import { TrackEventName } from '@/constants/common';
|
||||
import { AppSimpleEditConfigTemplateType } from '@fastgpt/global/core/app/type';
|
||||
import { FastGPTFeConfigsType, SystemEnvType } from '@fastgpt/global/common/system/types';
|
||||
import { SubPlanType } from '@fastgpt/global/support/wallet/sub/type';
|
||||
|
||||
export type PagingData<T> = {
|
||||
@@ -22,7 +21,6 @@ export type PagingData<T> = {
|
||||
export type RequestPaging = { pageNum: number; pageSize: number; [key]: any };
|
||||
|
||||
declare global {
|
||||
var systemEnv: SystemEnvType;
|
||||
var systemInitd: boolean;
|
||||
|
||||
var qaQueueLen: number;
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
|
||||
|
||||
export const selectShareResponse = ({
|
||||
export const selectSimpleChatResponse = ({
|
||||
responseData = []
|
||||
}: {
|
||||
responseData?: ChatHistoryItemResType[];
|
||||
}) => {
|
||||
const filedList = ['quoteList', 'moduleType'];
|
||||
const filterModuleTypeList: any[] = [FlowNodeTypeEnum.chatNode];
|
||||
|
||||
return responseData
|
||||
.filter((item) => filterModuleTypeList.includes(item.moduleType))
|
||||
.map((item) => {
|
||||
|
||||
@@ -3,9 +3,9 @@ import { POST } from '../api/request';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
|
||||
|
||||
export const useSpeech = (props?: { shareId?: string }) => {
|
||||
const { shareId } = props || {};
|
||||
export const useSpeech = (props?: OutLinkChatAuthProps) => {
|
||||
const { t } = useTranslation();
|
||||
const mediaRecorder = useRef<MediaRecorder>();
|
||||
// const mediaStream = useRef<MediaStream>();
|
||||
@@ -79,7 +79,13 @@ export const useSpeech = (props?: { shareId?: string }) => {
|
||||
const duration = Math.round((Date.now() - startTimestamp.current) / 1000);
|
||||
|
||||
formData.append('file', blob, 'recording.webm');
|
||||
formData.append('metadata', JSON.stringify({ duration, shareId }));
|
||||
formData.append(
|
||||
'data',
|
||||
JSON.stringify({
|
||||
...props,
|
||||
duration
|
||||
})
|
||||
);
|
||||
|
||||
setIsTransCription(true);
|
||||
try {
|
||||
|
||||
@@ -3,6 +3,6 @@ export const getDocPath = (path: string) => {
|
||||
const feConfigs = useSystemStore.getState().feConfigs;
|
||||
|
||||
if (!feConfigs?.docUrl) return '';
|
||||
if (feConfigs.docUrl.endsWith('/')) return feConfigs.docUrl;
|
||||
if (feConfigs.docUrl.endsWith('/')) return feConfigs.docUrl.slice(0, -1);
|
||||
return feConfigs.docUrl + path;
|
||||
};
|
||||
|
||||
@@ -4,12 +4,11 @@ import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import type { AppTTSConfigType } from '@fastgpt/global/core/module/type.d';
|
||||
import { TTSTypeEnum } from '@/constants/app';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useRouter } from 'next/router';
|
||||
import type { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat.d';
|
||||
|
||||
export const useAudioPlay = (props?: { ttsConfig?: AppTTSConfigType }) => {
|
||||
export const useAudioPlay = (props?: OutLinkChatAuthProps & { ttsConfig?: AppTTSConfigType }) => {
|
||||
const { t } = useTranslation();
|
||||
const { shareId } = useRouter().query as { shareId?: string };
|
||||
const { ttsConfig } = props || {};
|
||||
const { ttsConfig, shareId, outLinkUid, teamId, teamToken } = props || {};
|
||||
const { toast } = useToast();
|
||||
const [audio, setAudio] = useState<HTMLAudioElement>();
|
||||
const [audioLoading, setAudioLoading] = useState(false);
|
||||
@@ -63,7 +62,10 @@ export const useAudioPlay = (props?: { ttsConfig?: AppTTSConfigType }) => {
|
||||
chatItemId,
|
||||
ttsConfig,
|
||||
input: text,
|
||||
shareId
|
||||
shareId,
|
||||
outLinkUid,
|
||||
teamId,
|
||||
teamToken
|
||||
})
|
||||
});
|
||||
setAudioLoading(false);
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { GET, POST, DELETE, PUT } from '@/web/common/api/request';
|
||||
import type { ChatHistoryItemType, chatAppListSchema } from '@fastgpt/global/core/chat/type.d';
|
||||
import type { ChatHistoryItemType, ChatAppListSchema } from '@fastgpt/global/core/chat/type.d';
|
||||
|
||||
import type {
|
||||
CloseCustomFeedbackParams,
|
||||
InitChatProps,
|
||||
InitChatResponse,
|
||||
InitOutLinkChatProps,
|
||||
getHistoriesProps
|
||||
GetHistoriesProps,
|
||||
InitTeamChatProps
|
||||
} from '@/global/core/chat/api.d';
|
||||
import type {
|
||||
AdminUpdateFeedbackParams,
|
||||
@@ -16,37 +17,23 @@ import type {
|
||||
UpdateHistoryProps
|
||||
} from '@/global/core/chat/api.d';
|
||||
import { UpdateChatFeedbackProps } from '@fastgpt/global/core/chat/api';
|
||||
|
||||
/**
|
||||
* 根据队伍ID和获取
|
||||
*/
|
||||
export const getChatListById = (data: { shareTeamId: string; authToken: string }) =>
|
||||
POST<chatAppListSchema>(`/proApi/core/chat/init`, data);
|
||||
|
||||
/**
|
||||
* 获取团队分享的对话列表 initTeamChat
|
||||
* @param data
|
||||
* @returns
|
||||
*/
|
||||
export const getinitTeamChat = (data: { teamId: string; authToken: string; appId: string }) =>
|
||||
GET(`/proApi/core/chat/initTeamChat`, data);
|
||||
import { AuthTeamTagTokenProps } from '@fastgpt/global/support/user/team/tag';
|
||||
import { AppListItemType } from '@fastgpt/global/core/app/type';
|
||||
|
||||
/**
|
||||
* 获取初始化聊天内容
|
||||
*/
|
||||
export const getInitChatInfo = (data: InitChatProps) =>
|
||||
GET<InitChatResponse>(`/core/chat/init`, data);
|
||||
export const getInitChatInfoTeam = (data: InitChatProps) =>
|
||||
GET<InitChatResponse>(`/core/chat/init`, data);
|
||||
export const getInitOutLinkChatInfo = (data: InitOutLinkChatProps) =>
|
||||
GET<InitChatResponse>(`/core/chat/outLink/init`, data);
|
||||
export const getTeamChatInfo = (data: { appId: string; chatId: string; outLinkUid?: string }) =>
|
||||
export const getTeamChatInfo = (data: InitTeamChatProps) =>
|
||||
GET<InitChatResponse>(`/core/chat/team/init`, data);
|
||||
|
||||
/**
|
||||
* get current window history(appid or shareId)
|
||||
*/
|
||||
export const getChatHistories = (data: getHistoriesProps) =>
|
||||
export const getChatHistories = (data: GetHistoriesProps) =>
|
||||
POST<ChatHistoryItemType[]>('/core/chat/getHistories', data);
|
||||
|
||||
/**
|
||||
@@ -79,3 +66,18 @@ export const updateChatAdminFeedback = (data: AdminUpdateFeedbackParams) =>
|
||||
|
||||
export const closeCustomFeedback = (data: CloseCustomFeedbackParams) =>
|
||||
POST('/core/chat/feedback/closeCustom', data).catch();
|
||||
|
||||
/* team chat */
|
||||
/**
|
||||
* Get the app that can be used with this token
|
||||
*/
|
||||
export const getMyTokensApps = (data: AuthTeamTagTokenProps) =>
|
||||
GET<AppListItemType[]>(`/proApi/support/user/team/tag/getAppsByTeamTokens`, data);
|
||||
|
||||
/**
|
||||
* 获取团队分享的对话列表 initTeamChat
|
||||
* @param data
|
||||
* @returns
|
||||
*/
|
||||
export const getinitTeamChat = (data: { teamId: string; authToken: string; appId: string }) =>
|
||||
GET(`/proApi/core/chat/initTeamChat`, data);
|
||||
|
||||
@@ -4,7 +4,7 @@ import { immer } from 'zustand/middleware/immer';
|
||||
import type { ChatHistoryItemType } from '@fastgpt/global/core/chat/type.d';
|
||||
import type {
|
||||
InitChatResponse,
|
||||
getHistoriesProps,
|
||||
GetHistoriesProps,
|
||||
ClearHistoriesProps,
|
||||
DelHistoryProps,
|
||||
UpdateHistoryProps,
|
||||
@@ -21,7 +21,7 @@ import { defaultChatData } from '@/global/core/chat/constants';
|
||||
|
||||
type State = {
|
||||
histories: ChatHistoryItemType[];
|
||||
loadHistories: (data: getHistoriesProps) => Promise<null>;
|
||||
loadHistories: (data: GetHistoriesProps) => Promise<null>;
|
||||
delOneHistory(data: DelHistoryProps): Promise<void>;
|
||||
clearHistories(data: ClearHistoriesProps): Promise<void>;
|
||||
pushHistory: (history: ChatHistoryItemType) => void;
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
import { create } from 'zustand';
|
||||
import { devtools, persist } from 'zustand/middleware';
|
||||
import { immer } from 'zustand/middleware/immer';
|
||||
import type { ChatHistoryItemType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
const nanoid = customAlphabet(
|
||||
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWSYZ1234567890_',
|
||||
24
|
||||
);
|
||||
|
||||
type State = {
|
||||
localUId: string;
|
||||
teamShareChatHistory: (ChatHistoryItemType & { delete?: boolean })[];
|
||||
clearLocalHistory: (shareId?: string) => void;
|
||||
};
|
||||
|
||||
export const useTeamShareChatStore = create<State>()(
|
||||
devtools(
|
||||
persist(
|
||||
immer((set, get) => ({
|
||||
localUId: `shareChat-${Date.now()}-${nanoid()}`,
|
||||
teamShareChatHistory: [], // old version field
|
||||
clearLocalHistory() {
|
||||
// abandon
|
||||
set((state) => {
|
||||
state.teamShareChatHistory = state.teamShareChatHistory.map((item) => ({
|
||||
...item,
|
||||
delete: true
|
||||
}));
|
||||
});
|
||||
}
|
||||
})),
|
||||
{
|
||||
name: 'shareChatStore',
|
||||
partialize: (state) => ({
|
||||
localUId: state.localUId,
|
||||
shareChatHistory: state.teamShareChatHistory
|
||||
})
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -1,7 +1,7 @@
|
||||
import { GET, POST, PUT } from '@/web/common/api/request';
|
||||
import { hashStr } from '@fastgpt/global/common/string/tools';
|
||||
import type { ResLogin } from '@/global/support/api/userRes.d';
|
||||
import { UserAuthTypeEnum } from '@fastgpt/global/support/user/constant';
|
||||
import { UserAuthTypeEnum } from '@fastgpt/global/support/user/auth/constants';
|
||||
import { UserUpdateParams } from '@/types/user';
|
||||
import { UserType } from '@fastgpt/global/support/user/type.d';
|
||||
import type {
|
||||
@@ -9,6 +9,7 @@ import type {
|
||||
OauthLoginProps,
|
||||
PostLoginProps
|
||||
} from '@fastgpt/global/support/user/api.d';
|
||||
import { GetWXLoginQRResponse } from '@fastgpt/global/support/user/login/api.d';
|
||||
|
||||
export const sendAuthCode = (data: {
|
||||
username: string;
|
||||
@@ -71,3 +72,9 @@ export const postLogin = ({ password, ...props }: PostLoginProps) =>
|
||||
export const loginOut = () => GET('/support/user/account/loginout');
|
||||
|
||||
export const putUserInfo = (data: UserUpdateParams) => PUT('/support/user/account/update', data);
|
||||
|
||||
export const getWXLoginQR = () =>
|
||||
GET<GetWXLoginQRResponse>('/proApi/support/user/account/login/wx/getQR');
|
||||
|
||||
export const getWXLoginResult = (code: string) =>
|
||||
GET<ResLogin>(`/proApi/support/user/account/login/wx/getResult`, { code });
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useMemo, useCallback } from 'react';
|
||||
import { sendAuthCode } from '@/web/support/user/api';
|
||||
import { UserAuthTypeEnum } from '@fastgpt/global/support/user/constant';
|
||||
import { UserAuthTypeEnum } from '@fastgpt/global/support/user/auth/constants';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
UpdateTeamMemberProps,
|
||||
UpdateTeamProps
|
||||
} from '@fastgpt/global/support/user/team/controller.d';
|
||||
import type { TeamTagsSchema } from '@fastgpt/global/support/user/team/type';
|
||||
import type { TeamTagItemType, TeamTagSchema } from '@fastgpt/global/support/user/team/type';
|
||||
import {
|
||||
TeamItemType,
|
||||
TeamMemberItemType,
|
||||
@@ -25,14 +25,6 @@ export const putUpdateTeam = (data: UpdateTeamProps) =>
|
||||
PUT(`/proApi/support/user/team/update`, data);
|
||||
export const putSwitchTeam = (teamId: string) =>
|
||||
PUT<string>(`/proApi/support/user/team/switch`, { teamId });
|
||||
export const updateTags = (teamId: string, tagsUrl: string) =>
|
||||
POST<TeamTagsSchema[]>(`/proApi/support/user/team/tags/asyncTags`, { teamId, tagsUrl });
|
||||
export const getTeamsTags = (teamId: string) =>
|
||||
GET(`/proApi/support/user/team/tags/list`, { teamId });
|
||||
export const putUpdateTeamTags = (data: any) =>
|
||||
PUT(`/proApi/support/user/team/tags/updateUrl`, data);
|
||||
export const insertTeamsTags = (tags: Array<any>) =>
|
||||
POST(`/proApi/support/user/team/tags/create`, tags);
|
||||
|
||||
/* --------------- team member ---------------- */
|
||||
export const getTeamMembers = (teamId: string) =>
|
||||
@@ -50,6 +42,11 @@ export const updateInviteResult = (data: UpdateInviteProps) =>
|
||||
export const delLeaveTeam = (teamId: string) =>
|
||||
DELETE('/proApi/support/user/team/member/leave', { teamId });
|
||||
|
||||
/* --------------- team tags ---------------- */
|
||||
export const getTeamsTags = () => GET<TeamTagSchema[]>(`/proApi/support/user/team/tag/list`);
|
||||
export const loadTeamTagsByDomain = (domain: string) =>
|
||||
GET<TeamTagItemType[]>(`/proApi/support/user/team/tag/async`, { domain });
|
||||
|
||||
/* team limit */
|
||||
export const checkTeamExportDatasetLimit = (datasetId: string) =>
|
||||
GET(`/support/user/team/limit/exportDatasetLimit`, { datasetId });
|
||||
|
||||
Reference in New Issue
Block a user