This commit is contained in:
Archer
2023-10-22 23:54:04 +08:00
committed by GitHub
parent 3091a90df6
commit a3534407bf
365 changed files with 7266 additions and 6055 deletions

View File

@@ -2,7 +2,7 @@
"SystemParams": {
"vectorMaxProcess": 15,
"qaMaxProcess": 15,
"pgHNSWEfSearch ": 40
"pgHNSWEfSearch ": 60
},
"ChatModels": [
{

View File

@@ -1,6 +1,6 @@
{
"name": "app",
"version": "4.5.0",
"version": "4.5.1",
"private": false,
"scripts": {
"dev": "next dev",
@@ -16,9 +16,9 @@
"@chakra-ui/system": "^2.5.8",
"@emotion/react": "^11.10.6",
"@emotion/styled": "^11.10.6",
"@fastgpt/common": "workspace:*",
"@fastgpt/core": "workspace:*",
"@fastgpt/support": "workspace:*",
"@fastgpt/global": "workspace:*",
"@fastgpt/service": "workspace:*",
"@fastgpt/web": "workspace:*",
"@mozilla/readability": "^0.4.4",
"@tanstack/react-query": "^4.24.10",
"@types/nprogress": "^0.2.0",
@@ -63,7 +63,6 @@
"remark-math": "^5.1.1",
"request-ip": "^3.3.0",
"sass": "^1.58.3",
"timezones-list": "^3.0.2",
"zustand": "^4.3.5"
},
"devDependencies": {

View File

@@ -1,10 +1,9 @@
### Fast GPT V4.5.0
### Fast GPT V4.5.1
1. 新增 - 升级 PgVector 插件,引入 HNSW 索引,极大加快的知识库搜索速度。
2. 新增 - AI对话模块增加【返回AI内容】选项可控制 AI 的内容不直接返回浏览器
3. 优化 - TextSplitter采用递归拆解法
4. 优化 - 高级编排 UX 性能
5. 优化数据集管理,区分手动录入和标注,可追数据至某个文件,保留链接读取的原始链接。
6. [使用文档](https://doc.fastgpt.run/docs/intro/)
7. [点击查看高级编排介绍文档](https://doc.fastgpt.run/docs/workflow)
8. [点击查看商业版](https://doc.fastgpt.run/docs/commercial/)
1. 新增 - 知识库目录结构,更方便进行分类
2. 新增 - 升级 PgVector 插件,引入 HNSW 索引,极大加快的知识库搜索速度
3. 新增 - AI对话模块增加【返回AI内容】选项可控制 AI 的内容不直接返回浏览器
4. 优化 - TextSplitter采用递归拆解法。
5. [使用文档](https://doc.fastgpt.run/docs/intro/)
6. [点击查看高级编排介绍文档](https://doc.fastgpt.run/docs/workflow)
7. [点击查看商业版](https://doc.fastgpt.run/docs/commercial/)

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1697612159198" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6941" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M512 928c-125.44 0-224-182.72-224-416s98.56-416 224-416 224 182.72 224 416-98.56 416-224 416z m0-768c-75.84 0-160 144.64-160 352s84.16 352 160 352 160-144.64 160-352-84.16-352-160-352z" fill="#C0C5D3" p-id="6942"></path><path d="M699.52 797.76a623.04 623.04 0 0 1-299.52-91.84C198.08 589.44 88.96 412.8 151.68 304s270.4-102.72 472.32 14.08 311.04 293.12 248.32 401.92c-30.08 51.84-93.12 77.76-172.8 77.76zM324.16 290.56c-57.6 0-100.16 16.32-117.12 45.44-37.76 65.6 45.44 210.88 224 314.56s346.88 103.04 384 37.44-45.44-210.88-224-314.56a560.32 560.32 0 0 0-266.88-82.88z" fill="#C0C5D3" p-id="6943"></path><path d="M324.48 797.76c-79.68 0-142.72-25.92-172.8-77.76-64-108.8 46.4-285.44 248.32-401.92S809.6 195.2 872.32 304s-46.4 285.44-248.32 401.92a623.04 623.04 0 0 1-299.52 91.84zM699.84 290.56a560.32 560.32 0 0 0-267.84 82.88c-179.52 103.68-262.72 248.96-224 314.56s205.44 66.24 384-37.44 262.72-248.96 224-314.56c-16-29.12-58.56-45.44-116.16-45.44z" fill="#C0C5D3" p-id="6944"></path><path d="M512 512m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z" fill="#C0C5D3" p-id="6945"></path><path d="M512 592a80 80 0 1 1 80-80 80 80 0 0 1-80 80z m0-128a48 48 0 1 0 48 48 48 48 0 0 0-48-48z" fill="#C0C5D3" p-id="6946"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1694327751771" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4992" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M0 0h1024v1024H0V0z" fill="#202425" opacity=".01" p-id="4993"></path><path d="M136.533333 68.266667a68.266667 68.266667 0 0 0-68.266666 68.266666v428.305067a17.066667 17.066667 0 0 0 28.842666 12.356267l237.738667-226.440534a34.133333 34.133333 0 0 1 42.496-3.6864l268.288 178.858667a17.066667 17.066667 0 0 0 22.766933-3.447467L951.978667 171.4176A17.066667 17.066667 0 0 0 955.733333 160.699733V136.533333a68.266667 68.266667 0 0 0-68.266666-68.266666H136.533333z m819.2 255.3856a17.066667 17.066667 0 0 0-30.344533-10.717867l-221.866667 274.705067a17.066667 17.066667 0 0 0-3.7888 10.717866v340.309334a17.066667 17.066667 0 0 0 17.066667 17.066666h170.666667a68.266667 68.266667 0 0 0 68.266666-68.266666V323.652267zM614.4 955.733333a17.066667 17.066667 0 0 0 17.066667-17.066666v-330.990934a17.066667 17.066667 0 0 0-7.611734-14.199466l-204.8-136.533334a17.066667 17.066667 0 0 0-26.5216 14.199467V938.666667a17.066667 17.066667 0 0 0 17.066667 17.066666h204.8z m-307.2 0a17.066667 17.066667 0 0 0 17.066667-17.066666v-443.733334a17.066667 17.066667 0 0 0-28.842667-12.356266l-221.866667 211.285333a17.066667 17.066667 0 0 0-5.290666 12.3904V887.466667a68.266667 68.266667 0 0 0 68.266666 68.266666h170.666667z" fill="#FFAA44" p-id="4994"></path><path d="M73.557333 693.8624a17.066667 17.066667 0 0 0-5.290666 12.3904V887.466667a68.266667 68.266667 0 0 0 68.266666 68.266666h170.666667a17.066667 17.066667 0 0 0 17.066667-17.066666v-443.733334a17.066667 17.066667 0 0 0-28.842667-12.356266l-221.866667 211.285333zM392.533333 938.666667a17.066667 17.066667 0 0 0 17.066667 17.066666h204.8a17.066667 17.066667 0 0 0 17.066667-17.066666v-330.990934a17.066667 17.066667 0 0 0-7.611734-14.199466l-204.8-136.533334a17.066667 17.066667 0 0 0-26.5216 14.199467V938.666667z m307.2 0a17.066667 17.066667 0 0 0 17.066667 17.066666h170.666667a68.266667 68.266667 0 0 0 68.266666-68.266666V323.6864a17.066667 17.066667 0 0 0-30.344533-10.752l-221.866667 274.705067a17.066667 17.066667 0 0 0-3.7888 10.717866v340.309334z" fill="#11AA66" p-id="4995"></path></svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1697622173220" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2398" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M722.36246914 1024.8960632h-415.54678519c-120.26260543 0-218.12969876-97.86709333-218.12969877-218.12969875v-586.42583704c0-120.26260543 97.86709333-218.12969876 218.12969877-218.12969877h415.54678519c120.26260543 0 218.12969876 97.86709333 218.12969876 218.12969877v586.42583704c0 120.26260543-97.86709333 218.12969876-218.12969876 218.12969875z m-415.54678519-898.28010667c-51.65207703 0-93.72457086 42.07249383-93.72457086 93.72457088v586.42583704c0 51.65207703 42.07249383 93.72457086 93.72457086 93.72457086h415.54678519c51.65207703 0 93.72457086-42.07249383 93.72457086-93.72457086v-586.42583704c0-51.65207703-42.07249383-93.72457086-93.72457086-93.72457088h-415.54678519z" fill="#BDD2EF" p-id="2399"></path><path d="M684.0441363 599.76969482H338.27296395c-42.07249383 0-76.11885037-34.04635653-76.11885037-76.11885037s34.04635653-76.11885037 76.11885037-76.11885037h345.77117235c42.07249383 0 76.11885037 34.04635653 76.11885037 76.11885037s-34.17581037 76.11885037-76.11885037 76.11885037zM465.00826075 360.66847605h-126.7352968c-42.07249383 0-76.11885037-34.04635653-76.11885037-76.11885036s34.04635653-76.11885037 76.11885037-76.11885038h126.7352968c42.07249383 0 76.11885037 34.04635653 76.11885036 76.11885038s-34.04635653 76.11885037-76.11885036 76.11885036zM684.0441363 360.66847605h-25.37295013c-42.07249383 0-76.11885037-34.04635653-76.11885036-76.11885036s34.04635653-76.11885037 76.11885036-76.11885038h25.37295013c42.07249383 0 76.11885037 34.04635653 76.11885037 76.11885038s-34.17581037 76.11885037-76.11885037 76.11885036zM684.0441363 838.87091358H338.27296395c-42.07249383 0-76.11885037-34.04635653-76.11885037-76.11885038s34.04635653-76.11885037 76.11885037-76.11885037h345.77117235c42.07249383 0 76.11885037 34.04635653 76.11885037 76.11885037s-34.17581037 76.11885037-76.11885037 76.11885038z" fill="#2867CE" p-id="2400"></path></svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1692418843591" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4084" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M511.5 82c-236.6 0-429 192.4-429 429 0 236.5 192.5 429 429 429 236.6 0 429-192.4 429-429 0-236.5-192.4-429-429-429z m377.6 403.8H734.3c-4-139.9-41.4-259.9-97.5-331.9C776.5 203 879 332 889.1 485.8z m-402.8-349v349h-147c5.5-175.5 68.6-322.6 147-349z m0 399.4v349c-78.4-26.4-141.4-173.5-147-349h147z m50.5 349v-349h147c-5.6 175.5-68.6 322.6-147 349z m0-399.4v-349c78.4 26.4 141.4 173.5 147 349h-147zM386.3 153.9c-56.1 72-93.5 192-97.5 331.9H133.9C144.1 332 246.5 203 386.3 153.9zM133.9 536.2h154.8c4 139.9 41.4 259.9 97.5 331.9C246.5 819 144.1 690 133.9 536.2z m502.8 331.9c56.1-72 93.5-192 97.5-331.9H889C879 690 776.5 819 636.7 868.1z" fill="#5F9BEB" p-id="4085"></path></svg>

Before

Width:  |  Height:  |  Size: 1006 B

View File

@@ -14,6 +14,7 @@
"UnKnow": "UnKnow",
"Warning": "Warning",
"app": {
"AI Settings": "AI Settings",
"Advance App TestTip": "The current application is advanced editing mode \n. If you need to switch to [simple mode], please click the save button on the left",
"App Detail": "App Detail",
"Chat Logs Tips": "Logs record the app's online, shared, and API(chatId is existing) conversations",
@@ -39,7 +40,6 @@
"My Apps": "My Apps",
"Output Field Settings": "Output Field Settings",
"Paste Config": "Paste Config",
"AI Settings": "AI Settings",
"Variable Key Repeat Tip": "Variable Key Repeat",
"module": {
"Custom Title Tip": "The title name is displayed during the conversation"
@@ -108,9 +108,11 @@
"Add": "Add",
"Close": "Clow",
"Collect": "Collect",
"Confirm Move": "Move here",
"Copy": "Copy",
"Copy Successful": "Copy Successful",
"Course": "",
"Create Virtual File Failed": "Create Virtual File Failed",
"Custom Title": "Custom Title",
"Delete": "Delete",
"Delete Failed": "Delete Failed",
@@ -118,29 +120,92 @@
"Delete Warning": "Warning",
"Edit": "Edit",
"Expired Time": "Expired",
"File": "File",
"Filed is repeat": "Filed is repeated",
"Filed is repeated": "",
"Input": "Input",
"Last Step": "Last",
"Max credit": "Credit",
"Max credit tips": "What is the maximum amount of money that can be consumed by the link? If the link is exceeded, it will be banned. -1 indicates no limit.",
"Name": "Name",
"Name is empty": "Name is empty",
"Next Step": "Next",
"Output": "Output",
"Password inconsistency": "Password inconsistency",
"Rename": "Rename",
"Rename Failed": "Rename Failed",
"Rename Success": "Rename Success",
"Request Error": "Request Error",
"Search": "Search",
"Select One Folder": "Select a folder",
"Status": "Status",
"Time": "Time",
"Unknow": "Unknow",
"Unknow Source": "UnKnow Source",
"Update Successful": "Update Successful",
"export": ""
"Update Time": "Update Time",
"error": {
"unKnow": "There was an accident"
},
"export": "",
"folder": {
"Drag Tip": "Click and move",
"Move Success": "Move Success",
"No Folder": "No Folder",
"Root Path": "Root Folder"
}
},
"dataset": {
"Chunk Length": "Chunk Length",
"Confirm move the folder": "Confirm Move",
"Confirm to delete the data": "Confirm to delete the data?",
"Confirm to delete the file": "Are you sure to delete the file and all its data?",
"Create Folder": "Create Folder",
"Create Virtual File": "Virtual File",
"Delete Dataset Error": "Delete dataset failed",
"Edit Folder": "Edit Folder",
"Export": "Export",
"File Input": "Import File",
"File Size": "File Size",
"Filename": "Filename",
"Files": "{{total}} Files",
"Folder Name": "Input folder name",
"Insert Data": "Insert",
"Manual Data": "Manual Data",
"Manual Input": "Manual Input",
"Manual Mark": "Manual Mark",
"Mark Data": "Mark Data",
"Move Failed": "Move Failed",
"My Dataset": "My Dataset",
"Queue Desc": "This data refers to the current amount of training for the entire system. FastGPT uses queued training, and if you have too much data to train, you may need to wait for a while",
"Select Dataset": "Select Dataset",
"Select Folder": "Enter folder",
"System Data Queue": "Data Queue",
"Training Name": "Dataset Training"
"Training Name": "Dataset Training",
"Upload Time": "Upload Time",
"Virtual File Tip": "Virtual files allow you to create a custom container to hold data",
"collections": {
"Click to view file": "View File Data",
"Click to view folder": "To Folder",
"Collection Embedding": "{{total}}Embedding",
"Confirm to delete the folder": "Are you sure to delete this folder and all its contents?",
"Create Training Data": "Training-{{filename}}",
"Create Virtual File Success": "Create Virtual File Success",
"Data Amount": "Data Amount",
"Ready": "Ready",
"Select Collection": "Select Collection",
"Select One Collection To Store": "Select the collection to store"
},
"data": {
"Delete Tip": "Confirm to delete the data?",
"File import": "File Import",
"Input Data": "Import Data",
"Input Success Tip": "Succeeded in importing data",
"Update Data": "Update Data",
"Update Success Tip": "Update data successfully"
},
"deleteDatasetTips": "Are you sure to delete the knowledge base? Data cannot be recovered after deletion, please confirm!",
"deleteFolderTips": "Are you sure to delete this folder and all the knowledge bases it contains? Data cannot be recovered after deletion, please confirm!"
},
"file": {
"Click to download CSV template": "Click to download CSV template",
@@ -148,7 +213,6 @@
"Create File": "Create File",
"Create file": "Create file",
"Drag and drop": "Drag and drop files here",
"Embedding": "Embedding",
"Fetch Url": "Fetch Url",
"If the imported file is garbled, please convert CSV to UTF-8 encoding format": "If the imported file is garbled, please convert CSV to UTF-8 encoding format",
"Parse": "{{name}} Parsing...",
@@ -207,31 +271,6 @@
"desc": "AI knowledge base question and answer platform based on LLM large model",
"slogan": "Let the AI know more about you"
},
"kb": {
"Chunk Length": "Chunk Length",
"Confirm move the folder": "Confirm Move",
"Confirm to delete the file": "Are you sure to delete the file and all its data?",
"Create Folder": "Create Folder",
"Delete Dataset Error": "Delete dataset failed",
"Edit Folder": "Edit Folder",
"File Size": "File Size",
"Filename": "Filename",
"Files": "{{total}} Files",
"Folder Name": "Input folder name",
"Insert Data": "Insert",
"Manual Data": "Manual Data",
"Manual Input": "Manual Input",
"Manual Mark": "Manual Mark",
"Mark Data": "Mark Data",
"Move Failed": "Move Failed",
"My Dataset": "My Dataset",
"No Folder": "No Folder",
"Select Dataset": "Select Dataset",
"Select Folder": "Enter folder",
"Upload Time": "Upload Time",
"deleteDatasetTips": "Are you sure to delete the knowledge base? Data cannot be recovered after deletion, please confirm!",
"deleteFolderTips": "Are you sure to delete this folder and all the knowledge bases it contains? Data cannot be recovered after deletion, please confirm!"
},
"navbar": {
"Account": "Account",
"Apps": "Apps",

View File

@@ -14,6 +14,7 @@
"UnKnow": "未知",
"Warning": "提示",
"app": {
"AI Settings": "AI 高级配置",
"Advance App TestTip": "当前应用为高级编排模式\n如需切换为【简易模式】请点击左侧保存按键",
"App Detail": "应用详情",
"Chat Logs Tips": "日志会记录该应用的在线、分享和 API(需填写 chatId) 对话记录",
@@ -39,7 +40,6 @@
"My Apps": "我的应用",
"Output Field Settings": "输出字段编辑",
"Paste Config": "粘贴配置",
"AI Settings": "AI 高级配置",
"Variable Key Repeat Tip": "变量 key 重复",
"module": {
"Custom Title Tip": "该标题名字会展示在对话过程中"
@@ -108,9 +108,11 @@
"Add": "添加",
"Close": "关闭",
"Collect": "收藏",
"Confirm Move": "移动到这",
"Copy": "复制",
"Copy Successful": "复制成功",
"Course": "",
"Create Virtual File Failed": "创建虚拟文件失败",
"Custom Title": "自定义标题",
"Delete": "删除",
"Delete Failed": "删除失败",
@@ -118,29 +120,92 @@
"Delete Warning": "删除警告",
"Edit": "编辑",
"Expired Time": "过期时间",
"File": "文件",
"Filed is repeat": "",
"Filed is repeated": "字段重复了",
"Input": "输入",
"Last Step": "上一步",
"Max credit": "最大金额",
"Max credit tips": "该链接最大可消耗多少金额,超出后链接将被禁止使用。-1 代表无限制。",
"Name": "名称",
"Name is empty": "名称不能为空",
"Next Step": "下一步",
"Output": "输出",
"Password inconsistency": "两次密码不一致",
"Rename": "重命名",
"Rename Failed": "重命名失败",
"Rename Success": "重命名成功",
"Request Error": "请求异常",
"Search": "搜索",
"Select One Folder": "选择一个目录",
"Status": "状态",
"Time": "时间",
"Unknow": "未知",
"Unknow Source": "未知来源",
"Update Successful": "更新成功",
"export": ""
"Update Time": "更新时间",
"error": {
"unKnow": "出现了点意外~"
},
"export": "",
"folder": {
"Drag Tip": "点我可拖动",
"Move Success": "移动成功",
"No Folder": "这个目录空空的~",
"Root Path": "根目录"
}
},
"dataset": {
"Chunk Length": "数据总量",
"Confirm move the folder": "确认移动到该目录",
"Confirm to delete the data": "确认删除该数据?",
"Confirm to delete the file": "确认删除该文件及其所有数据?",
"Create Folder": "创建文件夹",
"Create Virtual File": "创建虚拟文件",
"Delete Dataset Error": "删除知识库异常",
"Edit Folder": "编辑文件夹",
"Export": "导出",
"File Input": "文件导入",
"File Size": "文件大小",
"Filename": "文件名",
"Files": "文件: {{total}}个",
"Folder Name": "输入文件夹名称",
"Insert Data": "插入",
"Manual Data": "手动录入",
"Manual Input": "手动录入",
"Manual Mark": "手动标注",
"Mark Data": "标注数据",
"Move Failed": "移动出现错误~",
"My Dataset": "我的知识库",
"Queue Desc": "该数据是指整个系统当前待训练的数量。{{title}} 采用排队训练的方式,如果待训练的数据过多,可能需要等待一段时间",
"Select Dataset": "选择该知识库",
"Select Folder": "进入文件夹",
"System Data Queue": "排队长度",
"Training Name": "数据训练"
"Training Name": "数据训练",
"Upload Time": "上传时间",
"Virtual File Tip": "虚拟文件允许创建一个自定义的容器装入数据",
"collections": {
"Click to view file": "点击查看文件详情",
"Click to view folder": "进入目录",
"Collection Embedding": "{{total}}组索引中",
"Confirm to delete the folder": "确认删除该文件夹及里面所有内容?",
"Create Training Data": "文件训练-{{filename}}",
"Create Virtual File Success": "创建虚拟文件成功",
"Data Amount": "数据总量",
"Ready": "已就绪",
"Select Collection": "选择文件",
"Select One Collection To Store": "选择一个文件进行存储"
},
"data": {
"Delete Tip": "确认删除该条数据?",
"File import": "文件导入",
"Input Data": "导入数据",
"Input Success Tip": "导入数据成功",
"Update Data": "更新数据",
"Update Success Tip": "更新数据成功"
},
"deleteDatasetTips": "确认删除该知识库?删除后数据无法恢复,请确认!",
"deleteFolderTips": "确认删除该文件夹及其包含的所有知识库?删除后数据无法恢复,请确认!"
},
"file": {
"Click to download CSV template": "点击下载 CSV 模板",
@@ -148,11 +213,10 @@
"Create File": "创建新文件",
"Create file": "创建文件",
"Drag and drop": "拖拽文件至此",
"Embedding": "索引中",
"Fetch Url": "链接读取",
"If the imported file is garbled, please convert CSV to UTF-8 encoding format": "如果导入文件乱码,请将 CSV 转成 UTF-8 编码格式",
"Parse": "{{name}} 解析中...",
"Ready": "可用",
"Ready": "",
"Release the mouse to upload the file": "松开鼠标上传文件",
"Select a maximum of 10 files": "最多选择10个文件",
"Uploading": "正在上传 {{name}},进度: {{percent}}%",
@@ -207,31 +271,6 @@
"desc": "基于 LLM 大模型的 AI 知识库问答平台",
"slogan": "让 AI 更懂你的知识"
},
"kb": {
"Chunk Length": "数据总量",
"Confirm move the folder": "确认移动到该目录",
"Confirm to delete the file": "确认删除该文件及其所有数据?",
"Create Folder": "创建文件夹",
"Delete Dataset Error": "删除知识库异常",
"Edit Folder": "编辑文件夹",
"File Size": "文件大小",
"Filename": "文件名",
"Files": "文件: {{total}}个",
"Folder Name": "输入文件夹名称",
"Insert Data": "插入",
"Manual Data": "手动录入",
"Manual Input": "手动录入",
"Manual Mark": "手动标注",
"Mark Data": "标注数据",
"Move Failed": "移动出现错误~",
"My Dataset": "我的知识库",
"No Folder": "没有子目录了~",
"Select Dataset": "选择该知识库",
"Select Folder": "进入文件夹",
"Upload Time": "上传时间",
"deleteDatasetTips": "确认删除该知识库?删除后数据无法恢复,请确认!",
"deleteFolderTips": "确认删除该文件夹及其包含的所有知识库?删除后数据无法恢复,请确认!"
},
"navbar": {
"Account": "账号",
"Apps": "应用",

View File

@@ -3,7 +3,7 @@ import { ModalBody, Textarea, ModalFooter, Button } from '@chakra-ui/react';
import MyModal from '../MyModal';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useTranslation } from 'next-i18next';
import { userUpdateChatFeedback } from '@/web/core/api/chat';
import { userUpdateChatFeedback } from '@/web/core/chat/api';
const FeedbackModal = ({
chatItemId,

View File

@@ -0,0 +1,16 @@
import React, { useState } from 'react';
const MarkModal = () => {
const [adminMarkData, setAdminMarkData] = useState<{
chatItemId: string;
dataId?: string;
datasetId?: string;
collectionId?: string;
q: string;
a: string;
}>();
return <div>MarkModal</div>;
};
export default MarkModal;

View File

@@ -1,23 +1,24 @@
import React, { useCallback, useMemo, useState } from 'react';
import { ModalBody, Box, useTheme, Flex, Progress } from '@chakra-ui/react';
import { getDatasetDataItemById } from '@/web/core/api/dataset';
import { getDatasetDataItemById } from '@/web/core/dataset/api';
import { useLoading } from '@/web/common/hooks/useLoading';
import { useToast } from '@/web/common/hooks/useToast';
import { getErrText } from '@/utils/tools';
import { QuoteItemType } from '@/types/chat';
import { getErrText } from '@fastgpt/global/common/error/utils';
import MyIcon from '@/components/Icon';
import InputDataModal, { RawFileText } from '@/pages/kb/detail/components/InputDataModal';
import InputDataModal, {
RawSourceText,
type InputDataType
} from '@/pages/dataset/detail/components/InputDataModal';
import MyModal from '../MyModal';
import { useTranslation } from 'react-i18next';
import { useRouter } from 'next/router';
import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
const QuoteModal = ({
onUpdateQuote,
rawSearch = [],
onClose
}: {
onUpdateQuote: (quoteId: string, sourceText?: string) => Promise<void>;
rawSearch: QuoteItemType[];
rawSearch: SearchDataResponseItemType[];
onClose: () => void;
}) => {
const { t } = useTranslation();
@@ -25,7 +26,7 @@ const QuoteModal = ({
const router = useRouter();
const { toast } = useToast();
const { setIsLoading, Loading } = useLoading();
const [editDataItem, setEditDataItem] = useState<QuoteItemType>();
const [editInputData, setEditInputData] = useState<InputDataType>();
const isShare = useMemo(() => router.pathname === '/chat/share', [router.pathname]);
@@ -33,18 +34,17 @@ const QuoteModal = ({
* click edit, get new kbDataItem
*/
const onclickEdit = useCallback(
async (item: QuoteItemType) => {
async (item: InputDataType) => {
if (!item.id) return;
try {
setIsLoading(true);
const data = await getDatasetDataItemById(item.id);
if (!data) {
onUpdateQuote(item.id, '已删除');
throw new Error('该数据已被删除');
}
setEditDataItem(data);
setEditInputData(data);
} catch (err) {
toast({
status: 'warning',
@@ -53,7 +53,7 @@ const QuoteModal = ({
}
setIsLoading(false);
},
[setIsLoading, toast, onUpdateQuote]
[setIsLoading, toast]
);
return (
@@ -94,10 +94,7 @@ const QuoteModal = ({
>
{!isShare && (
<Flex alignItems={'center'} mb={1}>
<RawFileText
filename={item.source || t('common.Unknow') || 'Unknow'}
fileId={item.file_id}
/>
<RawSourceText sourceName={item.sourceName} sourceId={item.sourceId} />
<Box flex={'1'} />
{item.score && (
<>
@@ -150,16 +147,17 @@ const QuoteModal = ({
</ModalBody>
<Loading fixed={false} />
</MyModal>
{editDataItem && (
{editInputData && editInputData.id && (
<InputDataModal
onClose={() => setEditDataItem(undefined)}
onSuccess={() => onUpdateQuote(editDataItem.id)}
onDelete={() => onUpdateQuote(editDataItem.id, '已删除')}
kbId={editDataItem.kb_id}
defaultValues={{
...editDataItem,
dataId: editDataItem.id
onClose={() => setEditInputData(undefined)}
onSuccess={() => {
console.log('更新引用成功');
}}
onDelete={() => {
console.log('删除引用成功');
}}
datasetId={editInputData.datasetId}
defaultValues={editInputData}
/>
)}
</>

View File

@@ -3,7 +3,7 @@ import { ModalBody, ModalFooter, Button } from '@chakra-ui/react';
import MyModal from '../MyModal';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useTranslation } from 'next-i18next';
import { userUpdateChatFeedback } from '@/web/core/api/chat';
import { userUpdateChatFeedback } from '@/web/core/chat/api';
const ReadFeedbackModal = ({
chatItemId,
@@ -39,14 +39,14 @@ const ReadFeedbackModal = ({
<MyModal isOpen={true} onClose={onClose} title={t('chat.Feedback Modal')}>
<ModalBody>{content}</ModalBody>
<ModalFooter>
<Button mr={2} isLoading={isLoading} variant={'base'} onClick={mutate}>
{t('chat.Feedback Close')}
</Button>
{!isMarked && (
<Button variant={'base'} mr={2} onClick={onMark}>
<Button mr={2} onClick={onMark}>
{t('chat.Feedback Mark')}
</Button>
)}
<Button isLoading={isLoading} onClick={mutate}>
{t('chat.Feedback Close')}
</Button>
</ModalFooter>
</MyModal>
);

View File

@@ -1,8 +1,9 @@
import React, { useCallback, useMemo, useState } from 'react';
import { ChatHistoryItemResType, ChatItemType, QuoteItemType } from '@/types/chat';
import React, { useMemo, useState } from 'react';
import { ChatHistoryItemResType, ChatItemType } from '@/types/chat';
import { Flex, BoxProps, useDisclosure } from '@chakra-ui/react';
import { useTranslation } from 'react-i18next';
import { useGlobalStore } from '@/web/common/store/global';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import type { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
import dynamic from 'next/dynamic';
import Tag from '../Tag';
import MyTooltip from '../MyTooltip';
@@ -13,9 +14,9 @@ const ContextModal = dynamic(() => import('./ContextModal'), { ssr: false });
const WholeResponseModal = dynamic(() => import('./WholeResponseModal'), { ssr: false });
const ResponseTags = ({ responseData = [] }: { responseData?: ChatHistoryItemResType[] }) => {
const { isPc } = useGlobalStore();
const { isPc } = useSystemStore();
const { t } = useTranslation();
const [quoteModalData, setQuoteModalData] = useState<QuoteItemType[]>();
const [quoteModalData, setQuoteModalData] = useState<SearchDataResponseItemType[]>();
const [contextModalData, setContextModalData] = useState<ChatItemType[]>();
const {
isOpen: isOpenWholeModal,
@@ -37,14 +38,12 @@ const ResponseTags = ({ responseData = [] }: { responseData?: ChatHistoryItemRes
.filter((item) => item.moduleType === FlowModuleTypeEnum.chatNode)
.map((item) => item.quoteList)
.flat()
.filter((item) => item) as QuoteItemType[],
.filter((item) => item) as SearchDataResponseItemType[],
historyPreview: chatData?.historyPreview,
runningTime: +responseData.reduce((sum, item) => sum + (item.runningTime || 0), 0).toFixed(2)
};
}, [responseData]);
const updateQuote = useCallback(async (quoteId: string, sourceText?: string) => {}, []);
const TagStyles: BoxProps = {
mr: 2,
bg: 'transparent'
@@ -100,11 +99,7 @@ const ResponseTags = ({ responseData = [] }: { responseData?: ChatHistoryItemRes
</MyTooltip>
{!!quoteModalData && (
<QuoteModal
rawSearch={quoteModalData}
onUpdateQuote={updateQuote}
onClose={() => setQuoteModalData(undefined)}
/>
<QuoteModal rawSearch={quoteModalData} onClose={() => setQuoteModalData(undefined)} />
)}
{!!contextModalData && (
<ContextModal context={contextModalData} onClose={() => setContextModalData(undefined)} />

View File

@@ -1,114 +0,0 @@
import React, { useState } from 'react';
import { ModalBody, useTheme, ModalFooter, Button, Box, Card, Flex, Grid } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
import { useToast } from '@/web/common/hooks/useToast';
import Avatar from '../Avatar';
import MyIcon from '@/components/Icon';
import { DatasetTypeEnum } from '@fastgpt/core/dataset/constant';
import DatasetSelectModal, { useDatasetSelect } from '@/components/core/dataset/SelectModal';
const SelectDataset = ({
isOpen,
onSuccess,
onClose
}: {
isOpen: boolean;
onSuccess: (kbId: string) => void;
onClose: () => void;
}) => {
const { t } = useTranslation();
const theme = useTheme();
const { toast } = useToast();
const [selectedId, setSelectedId] = useState<string>();
const { paths, parentId, setParentId, datasets } = useDatasetSelect();
return (
<DatasetSelectModal
isOpen={isOpen}
paths={paths}
onClose={onClose}
parentId={parentId}
setParentId={setParentId}
tips={t('chat.Select Mark Kb Desc')}
>
<ModalBody flex={'1 0 0'} overflowY={'auto'}>
<Grid
gridTemplateColumns={['repeat(1,1fr)', 'repeat(2,1fr)', 'repeat(3,1fr)']}
gridGap={3}
userSelect={'none'}
>
{datasets.map((item) =>
(() => {
const selected = selectedId === item._id;
return (
<Card
key={item._id}
p={3}
border={theme.borders.base}
boxShadow={'sm'}
h={'80px'}
cursor={'pointer'}
_hover={{
boxShadow: 'md'
}}
{...(selected
? {
bg: 'myBlue.300'
}
: {})}
onClick={() => {
if (item.type === DatasetTypeEnum.folder) {
setParentId(item._id);
} else {
setSelectedId(item._id);
}
}}
>
<Flex alignItems={'center'} h={'38px'}>
<Avatar src={item.avatar} w={['24px', '28px', '32px']}></Avatar>
<Box ml={3} fontWeight={'bold'} fontSize={['md', 'lg', 'xl']}>
{item.name}
</Box>
</Flex>
<Flex justifyContent={'flex-end'} alignItems={'center'} fontSize={'sm'}>
<MyIcon mr={1} name="kbTest" w={'12px'} />
<Box color={'myGray.500'}>{item.vectorModel.name}</Box>
</Flex>
</Card>
);
})()
)}
</Grid>
{datasets.length === 0 && (
<Flex mt={5} flexDirection={'column'} alignItems={'center'}>
<MyIcon name="empty" w={'48px'} h={'48px'} color={'transparent'} />
<Box mt={2} color={'myGray.500'}>
西~
</Box>
</Flex>
)}
</ModalBody>
<ModalFooter>
<Button variant={'base'} mr={2} onClick={onClose}>
{t('Cancel')}
</Button>
<Button
onClick={() => {
if (!selectedId) {
return toast({
status: 'warning',
title: t('Select value is empty')
});
}
onSuccess(selectedId);
}}
>
{t('Confirm')}
</Button>
</ModalFooter>
</DatasetSelectModal>
);
};
export default SelectDataset;

View File

@@ -0,0 +1,195 @@
import React, { useState } from 'react';
import { ModalBody, useTheme, ModalFooter, Button, Box, Card, Flex, Grid } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
import Avatar from '../Avatar';
import MyIcon from '@/components/Icon';
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constant';
import DatasetSelectModal, { useDatasetSelect } from '@/components/core/dataset/SelectModal';
import dynamic from 'next/dynamic';
import { MarkDataType } from '@/global/core/dataset/type';
import SelectCollections from '@/web/core/dataset/components/SelectCollections';
const InputDataModal = dynamic(() => import('@/pages/dataset/detail/components/InputDataModal'));
export type AdminMarkType = {
dataId?: string;
datasetId?: string;
collectionId?: string;
q: string;
a?: string;
};
const SelectMarkCollection = ({
adminMarkData,
setAdminMarkData,
onSuccess,
onClose
}: {
adminMarkData: AdminMarkType;
setAdminMarkData: (e: AdminMarkType) => void;
onClose: () => void;
onSuccess: (adminFeedback: MarkDataType) => void;
}) => {
const { t } = useTranslation();
const theme = useTheme();
const [selectedDatasetId, setSelectedDatasetId] = useState<string>();
const [selectedDatasetCollectionIds, setSelectedDatasetCollectionIds] = useState<string[]>([]);
const { paths, parentId, setParentId, datasets, isLoading } = useDatasetSelect();
return (
<>
{/* select dataset */}
{!adminMarkData.datasetId && (
<DatasetSelectModal
isOpen
paths={paths}
onClose={onClose}
parentId={parentId}
setParentId={setParentId}
tips={t('chat.Select Mark Kb Desc')}
>
<ModalBody flex={'1 0 0'} overflowY={'auto'}>
<Grid
gridTemplateColumns={['repeat(1,1fr)', 'repeat(2,1fr)', 'repeat(3,1fr)']}
gridGap={3}
userSelect={'none'}
>
{datasets.map((item) =>
(() => {
const selected = selectedDatasetId === item._id;
return (
<Card
key={item._id}
p={3}
border={theme.borders.base}
boxShadow={'sm'}
h={'80px'}
cursor={'pointer'}
_hover={{
boxShadow: 'md'
}}
{...(selected
? {
bg: 'myBlue.300'
}
: {})}
onClick={() => {
if (item.type === DatasetTypeEnum.folder) {
setParentId(item._id);
} else {
setSelectedDatasetId(item._id);
}
}}
>
<Flex alignItems={'center'} h={'38px'}>
<Avatar src={item.avatar} w={['24px', '28px', '32px']}></Avatar>
<Box ml={3} fontWeight={'bold'} fontSize={['md', 'lg', 'xl']}>
{item.name}
</Box>
</Flex>
<Flex justifyContent={'flex-end'} alignItems={'center'} fontSize={'sm'}>
<MyIcon mr={1} name="kbTest" w={'12px'} />
<Box color={'myGray.500'}>{item.vectorModel.name}</Box>
</Flex>
</Card>
);
})()
)}
</Grid>
{datasets.length === 0 && (
<Flex mt={'10vh'} flexDirection={'column'} alignItems={'center'}>
<MyIcon name="empty" w={'48px'} h={'48px'} color={'transparent'} />
<Box mt={2} color={'myGray.500'}>
西~
</Box>
</Flex>
)}
</ModalBody>
<ModalFooter>
<Button
isLoading={isLoading}
isDisabled={!selectedDatasetId}
onClick={() => {
setAdminMarkData({ ...adminMarkData, datasetId: selectedDatasetId });
}}
>
{t('common.Next Step')}
</Button>
</ModalFooter>
</DatasetSelectModal>
)}
{/* select collection */}
{adminMarkData.datasetId && !adminMarkData.collectionId && (
<SelectCollections
datasetId={adminMarkData.datasetId}
type={'collection'}
title={t('dataset.collections.Select One Collection To Store')}
onClose={onClose}
onChange={({ collectionIds }) => {
setSelectedDatasetCollectionIds(collectionIds);
}}
CustomFooter={
<ModalFooter>
<Button
variant={'base'}
mr={2}
onClick={() => {
setAdminMarkData({
...adminMarkData,
datasetId: undefined
});
}}
>
{t('common.Last Step')}
</Button>
<Button
isDisabled={selectedDatasetCollectionIds.length === 0}
onClick={() => {
setAdminMarkData({
...adminMarkData,
collectionId: selectedDatasetCollectionIds[0]
});
}}
>
{t('common.Next Step')}
</Button>
</ModalFooter>
}
/>
)}
{/* input data */}
{adminMarkData.datasetId && adminMarkData.collectionId && (
<InputDataModal
onClose={onClose}
datasetId={adminMarkData.datasetId}
defaultValues={{
id: adminMarkData.dataId,
datasetId: adminMarkData.datasetId,
collectionId: adminMarkData.collectionId,
sourceName: '手动标注',
q: adminMarkData.q,
a: adminMarkData.a
}}
onSuccess={(data) => {
if (!data.q || !adminMarkData.datasetId || !adminMarkData.collectionId || !data.id) {
return onClose();
}
onSuccess({
dataId: data.id,
datasetId: adminMarkData.datasetId,
collectionId: adminMarkData.collectionId,
q: data.q,
a: data.a
});
onClose();
}}
/>
)}
</>
);
};
export default React.memo(SelectMarkCollection);

View File

@@ -8,7 +8,7 @@ import Tabs from '../Tabs';
import MyModal from '../MyModal';
import MyTooltip from '../MyTooltip';
import { QuestionOutlineIcon } from '@chakra-ui/icons';
import { formatPrice } from '@fastgpt/common/bill/index';
import { formatPrice } from '@fastgpt/global/common/bill/tools';
function Row({ label, value }: { label: string; value?: string | number | React.ReactNode }) {
const theme = useTheme();

View File

@@ -10,25 +10,6 @@
}
}
.newChat {
.modelListContainer {
height: 0;
overflow: hidden;
}
.modelList {
border-radius: 6px;
}
&:hover {
.modelListContainer {
height: 60vh;
}
.modelList {
box-shadow: 0 0 5px rgba($color: #000000, $alpha: 0.05);
border: 1px solid #dee0e2;
}
}
}
.statusAnimation {
animation: statusBox 0.8s linear infinite alternate;
}

View File

@@ -8,6 +8,7 @@ import React, {
ForwardedRef,
useEffect
} from 'react';
import Script from 'next/script';
import { throttle } from 'lodash';
import {
ChatHistoryItemResType,
@@ -17,7 +18,7 @@ import {
} from '@/types/chat';
import { useToast } from '@/web/common/hooks/useToast';
import { useAudioPlay } from '@/web/common/utils/voice';
import { getErrText } from '@/utils/tools';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { useCopyData } from '@/web/common/hooks/useCopyData';
import {
Box,
@@ -30,7 +31,7 @@ import {
BoxProps,
FlexProps
} from '@chakra-ui/react';
import { feConfigs } from '@/web/common/store/static';
import { feConfigs } from '@/web/common/system/staticData';
import { eventBus } from '@/web/common/utils/eventbus';
import { adaptChat2GptMessages } from '@/utils/common/adapt/message';
import { useMarkdown } from '@/web/common/hooks/useMarkdown';
@@ -38,14 +39,15 @@ import { AppModuleItemType } from '@/types/app';
import { VariableInputEnum } from '@/constants/app';
import { useForm } from 'react-hook-form';
import type { MessageItemType } from '@/types/core/chat/type';
import { fileDownload } from '@/web/common/utils/file';
import { fileDownload } from '@/web/common/file/utils';
import { htmlTemplate } from '@/constants/common';
import { useRouter } from 'next/router';
import { useGlobalStore } from '@/web/common/store/global';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { TaskResponseKeyEnum } from '@/constants/chat';
import { useTranslation } from 'react-i18next';
import { customAlphabet } from 'nanoid';
import { userUpdateChatFeedback, adminUpdateChatFeedback } from '@/web/core/api/chat';
import { adminUpdateChatFeedback, userUpdateChatFeedback } from '@/web/core/chat/api';
import type { AdminMarkType } from './SelectMarkCollection';
import MyIcon from '@/components/Icon';
import Avatar from '@/components/Avatar';
@@ -56,14 +58,11 @@ import dynamic from 'next/dynamic';
const ResponseTags = dynamic(() => import('./ResponseTags'));
const FeedbackModal = dynamic(() => import('./FeedbackModal'));
const ReadFeedbackModal = dynamic(() => import('./ReadFeedbackModal'));
const SelectDataset = dynamic(() => import('./SelectDataset'));
const InputDataModal = dynamic(() => import('@/pages/kb/detail/components/InputDataModal'));
const SelectMarkCollection = dynamic(() => import('./SelectMarkCollection'));
import styles from './index.module.scss';
import Script from 'next/script';
import { postQuestionGuide } from '@/web/core/api/ai';
import { splitGuideModule } from './utils';
import { DatasetSpecialIdEnum } from '@fastgpt/core/dataset/constant';
import { postQuestionGuide } from '@/web/core/ai/api';
import { splitGuideModule } from '@/global/core/app/modules/utils';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 24);
@@ -131,7 +130,7 @@ const ChatBox = (
const router = useRouter();
const { t } = useTranslation();
const { toast } = useToast();
const { isPc } = useGlobalStore();
const { isPc } = useSystemStore();
const TextareaDom = useRef<HTMLTextAreaElement>(null);
const chatController = useRef(new AbortController());
const questionGuideController = useRef(new AbortController());
@@ -147,14 +146,7 @@ const ChatBox = (
content: string;
isMarked: boolean;
}>();
const [adminMarkData, setAdminMarkData] = useState<{
// mark modal data
kbId?: string;
chatItemId: string;
dataId?: string;
q: string;
a: string;
}>();
const [adminMarkData, setAdminMarkData] = useState<AdminMarkType & { chatItemId: string }>();
const [questionGuides, setQuestionGuide] = useState<string[]>([]);
const isChatting = useMemo(
@@ -674,10 +666,11 @@ const ChatBox = (
if (item.adminFeedback) {
setAdminMarkData({
chatItemId: item.dataId,
kbId: item.adminFeedback.kbId,
datasetId: item.adminFeedback.datasetId,
collectionId: item.adminFeedback.collectionId,
dataId: item.adminFeedback.dataId,
q: chatHistory[index - 1]?.value || '',
a: item.adminFeedback.content
q: item.adminFeedback.q || chatHistory[index - 1]?.value || '',
a: item.adminFeedback.a
});
} else {
setAdminMarkData({
@@ -798,7 +791,9 @@ const ChatBox = (
</Box>
<Box h={'1px'} bg={'myGray.300'} flex={'1'} />
</Flex>
<Box>{item.adminFeedback.content}</Box>
<Box whiteSpace={'pre'}>{`${item.adminFeedback.q || ''}${
item.adminFeedback.a ? `\n${item.adminFeedback.a}` : ''
}`}</Box>
</Box>
)}
</Card>
@@ -940,75 +935,44 @@ const ChatBox = (
/>
)}
{/* admin mark data */}
{showMarkIcon && (
<>
{/* select one dataset to insert markData */}
<SelectDataset
isOpen={!!adminMarkData && !adminMarkData.kbId}
onClose={() => setAdminMarkData(undefined)}
// @ts-ignore
onSuccess={(kbId) => setAdminMarkData((state) => ({ ...state, kbId }))}
/>
{!!adminMarkData && (
<SelectMarkCollection
adminMarkData={adminMarkData}
setAdminMarkData={(e) => setAdminMarkData({ ...e, chatItemId: adminMarkData.chatItemId })}
onClose={() => setAdminMarkData(undefined)}
onSuccess={(adminFeedback) => {
adminUpdateChatFeedback({
chatItemId: adminMarkData.chatItemId,
...adminFeedback
});
// update dom
setChatHistory((state) =>
state.map((chatItem) =>
chatItem.dataId === adminMarkData.chatItemId
? {
...chatItem,
adminFeedback
}
: chatItem
)
);
{/* edit markData modal */}
{adminMarkData && adminMarkData.kbId && (
<InputDataModal
onClose={() => setAdminMarkData(undefined)}
onSuccess={async (data) => {
if (!adminMarkData.kbId || !data.dataId) {
return setAdminMarkData(undefined);
}
const adminFeedback = {
kbId: adminMarkData.kbId,
dataId: data.dataId,
content: data.a
};
// update dom
setChatHistory((state) =>
state.map((chatItem) =>
chatItem.dataId === adminMarkData.chatItemId
? {
...chatItem,
adminFeedback
}
: chatItem
)
);
// request to update adminFeedback
try {
adminUpdateChatFeedback({
chatItemId: adminMarkData.chatItemId,
...adminFeedback
});
if (readFeedbackData) {
userUpdateChatFeedback({
chatItemId: readFeedbackData.chatItemId,
userFeedback: undefined
});
setChatHistory((state) =>
state.map((chatItem) =>
chatItem.dataId === readFeedbackData.chatItemId
? { ...chatItem, userFeedback: undefined }
: chatItem
)
);
setReadFeedbackData(undefined);
}
} catch (error) {}
setAdminMarkData(undefined);
}}
kbId={adminMarkData.kbId}
defaultValues={{
dataId: adminMarkData.dataId,
q: adminMarkData.q,
a: adminMarkData.a,
file_id: DatasetSpecialIdEnum.mark
}}
/>
)}
</>
if (readFeedbackData) {
userUpdateChatFeedback({
chatItemId: readFeedbackData.chatItemId,
userFeedback: undefined
});
setChatHistory((state) =>
state.map((chatItem) =>
chatItem.dataId === readFeedbackData.chatItemId
? { ...chatItem, userFeedback: undefined }
: chatItem
)
);
setReadFeedbackData(undefined);
}
}}
/>
)}
</Flex>
);

View File

@@ -1,4 +0,0 @@
.datePicker {
--rdp-background-color: #d6e8ff;
--rdp-accent-color: #0000ff;
}

View File

@@ -4,7 +4,6 @@ import { addDays, format } from 'date-fns';
import { type DateRange, DayPicker } from 'react-day-picker';
import MyIcon from '../Icon';
import 'react-day-picker/dist/style.css';
import styles from './index.module.scss';
import zhCN from 'date-fns/locale/zh-CN';
const DateRangePicker = ({
@@ -59,6 +58,10 @@ const DateRangePicker = ({
<Card
position={'absolute'}
zIndex={1}
css={{
'--rdp-background-color': '#d6e8ff',
' --rdp-accent-color': '#0000ff'
}}
{...(position === 'top'
? {
bottom: '40px'
@@ -69,7 +72,6 @@ const DateRangePicker = ({
locale={zhCN}
id="test"
mode="range"
className={styles.datePicker}
defaultMonth={defaultDate.to}
selected={range}
disabled={[

View File

@@ -0,0 +1,20 @@
import React from 'react';
import { Flex, Box, FlexProps } from '@chakra-ui/react';
import MyIcon from '../Icon';
type Props = FlexProps & {
text?: string | null;
};
const EmptyTip = ({ text, ...props }: Props) => {
return (
<Flex mt={5} flexDirection={'column'} alignItems={'center'} pt={'10vh'} {...props}>
<MyIcon name="empty" w={'48px'} h={'48px'} color={'transparent'} />
<Box mt={2} color={'myGray.500'}>
{text || '没有什么数据噢~'}
</Box>
</Flex>
);
};
export default EmptyTip;

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { useRouter } from 'next/router';
import { useToast } from '@chakra-ui/react';
import { useUserStore } from '@/web/support/store/user';
import { useUserStore } from '@/web/support/user/useUserStore';
import { useQuery } from '@tanstack/react-query';
const unAuthPage: { [key: string]: boolean } = {

View File

@@ -2,14 +2,14 @@ import React, { useEffect, useMemo } from 'react';
import { Box, useColorMode, Flex } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import { useLoading } from '@/web/common/hooks/useLoading';
import { useGlobalStore } from '@/web/common/store/global';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { throttle } from 'lodash';
import Auth from './auth';
import Navbar from './navbar';
import NavbarPhone from './navbarPhone';
import { useQuery } from '@tanstack/react-query';
import { useUserStore } from '@/web/support/store/user';
import { getUnreadCount } from '@/web/support/api/user';
import { useUserStore } from '@/web/support/user/useUserStore';
import { getUnreadCount } from '@/web/support/user/api';
const pcUnShowLayoutRoute: Record<string, boolean> = {
'/': true,
@@ -30,7 +30,7 @@ const Layout = ({ children }: { children: JSX.Element }) => {
const router = useRouter();
const { colorMode, setColorMode } = useColorMode();
const { Loading } = useLoading();
const { loading, setScreenWidth, isPc, loadGitStar } = useGlobalStore();
const { loading, setScreenWidth, isPc, loadGitStar } = useSystemStore();
const { userInfo } = useUserStore();
const isChatPage = useMemo(

View File

@@ -1,16 +1,16 @@
import React, { useMemo } from 'react';
import { Box, Flex, Link } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import { useUserStore } from '@/web/support/store/user';
import { useChatStore } from '@/web/core/store/chat';
import { useUserStore } from '@/web/support/user/useUserStore';
import { useChatStore } from '@/web/core/chat/storeChat';
import { HUMAN_ICON } from '@/constants/chat';
import { feConfigs } from '@/web/common/store/static';
import { feConfigs } from '@/web/common/system/staticData';
import NextLink from 'next/link';
import Badge from '../Badge';
import Avatar from '../Avatar';
import MyIcon from '../Icon';
import { useTranslation } from 'next-i18next';
import { useGlobalStore } from '@/web/common/store/global';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import MyTooltip from '../MyTooltip';
export enum NavbarTypeEnum {
@@ -22,7 +22,7 @@ const Navbar = ({ unread }: { unread: number }) => {
const { t } = useTranslation();
const router = useRouter();
const { userInfo } = useUserStore();
const { gitStar } = useGlobalStore();
const { gitStar } = useSystemStore();
const { lastChatAppId, lastChatId } = useChatStore();
const navbarList = useMemo(
() => [
@@ -44,8 +44,8 @@ const Navbar = ({ unread }: { unread: number }) => {
label: t('navbar.Datasets'),
icon: 'dbLight',
activeIcon: 'dbFill',
link: `/kb/list`,
activeLink: ['/kb/list', '/kb/detail']
link: `/dataset/list`,
activeLink: ['/dataset/list', '/dataset/detail']
},
...(feConfigs?.show_appStore
? [

View File

@@ -1,7 +1,7 @@
import React, { useMemo } from 'react';
import { useRouter } from 'next/router';
import { Flex, Box } from '@chakra-ui/react';
import { useChatStore } from '@/web/core/store/chat';
import { useChatStore } from '@/web/core/chat/storeChat';
import { useTranslation } from 'react-i18next';
import Badge from '../Badge';
import MyIcon from '../Icon';

View File

@@ -1,8 +1,8 @@
import React, { useMemo } from 'react';
import { Box, useTheme } from '@chakra-ui/react';
import { getFileAndOpen } from '@/web/common/utils/file';
import { getFileAndOpen } from '@/web/common/file/utils';
import { useToast } from '@/web/common/hooks/useToast';
import { getErrText } from '@/utils/tools';
import { getErrText } from '@fastgpt/global/common/error/utils';
type QuoteItemType = {
file_id?: string;

View File

@@ -22,7 +22,7 @@ const MyModal = ({
title,
children,
isCentered,
w = 'auto',
w = '100%',
maxW = ['90vw', '600px'],
...props
}: Props) => {
@@ -44,7 +44,12 @@ const MyModal = ({
>
{!!title && <ModalHeader>{title}</ModalHeader>}
{onClose && <ModalCloseButton />}
<Box overflow={'overlay'} h={'100%'} display={'flex'} flexDirection={'column'}>
<Box
overflow={props.overflow || 'overlay'}
h={'100%'}
display={'flex'}
flexDirection={'column'}
>
{children}
</Box>
</ModalContent>

View File

@@ -1,13 +1,13 @@
import React from 'react';
import { Tooltip, TooltipProps } from '@chakra-ui/react';
import { useGlobalStore } from '@/web/common/store/global';
import { useSystemStore } from '@/web/common/system/useSystemStore';
interface Props extends TooltipProps {
forceShow?: boolean;
}
const MyTooltip = ({ children, forceShow = false, shouldWrapChildren = true, ...props }: Props) => {
const { isPc } = useGlobalStore();
const { isPc } = useSystemStore();
return isPc || forceShow ? (
<Tooltip
bg={'white'}

View File

@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import MyModal from '../MyModal';
import { Box, Button, Grid, useTheme } from '@chakra-ui/react';
import { PromptTemplateItem } from '@fastgpt/core/ai/type';
import { PromptTemplateItem } from '@fastgpt/global/core/ai/type.d';
import { ModalBody, ModalFooter } from '@chakra-ui/react';
const PromptTemplate = ({

View File

@@ -0,0 +1,62 @@
import MyIcon from '@/components/Icon';
import { Box, Flex } from '@chakra-ui/react';
import { ParentTreePathItemType } from '@fastgpt/global/common/parentFolder/type';
import React, { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
const ParentPaths = (props: {
paths?: ParentTreePathItemType[];
rootName?: string;
FirstPathDom?: React.ReactNode;
onClick: (parentId: string) => void;
}) => {
const { t } = useTranslation();
const { paths = [], rootName = t('common.folder.Root Path'), FirstPathDom, onClick } = props;
const concatPaths = useMemo(
() => [
{
parentId: '',
parentName: rootName
},
...paths
],
[rootName, paths]
);
return paths.length === 0 && !!FirstPathDom ? (
<>{FirstPathDom}</>
) : (
<Flex flex={1}>
{concatPaths.map((item, i) => (
<Flex key={item.parentId} alignItems={'center'}>
<Box
fontSize={['md', 'lg']}
py={1}
px={[0, 2]}
borderRadius={'md'}
{...(i === concatPaths.length - 1
? {
cursor: 'default'
}
: {
cursor: 'pointer',
_hover: {
bg: 'myGray.100'
},
onClick: () => {
onClick(item.parentId);
}
})}
>
{item.parentName}
</Box>
{i !== concatPaths.length - 1 && (
<MyIcon name={'rightArrowLight'} color={'myGray.500'} w={['18px', '24px']} />
)}
</Flex>
))}
</Flex>
);
};
export default React.memo(ParentPaths);

View File

@@ -1,9 +1,9 @@
import { getDatasets, getDatasetPaths } from '@/web/core/api/dataset';
import { getDatasets, getDatasetPaths } from '@/web/core/dataset/api';
import MyModal from '@/components/MyModal';
import { useQuery } from '@tanstack/react-query';
import React, { Dispatch, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useGlobalStore } from '@/web/common/store/global';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { Box, Flex, ModalHeader } from '@chakra-ui/react';
import MyIcon from '@/components/Icon';
@@ -30,7 +30,7 @@ const DatasetSelectContainer = ({
children: React.ReactNode;
}) => {
const { t } = useTranslation();
const { isPc } = useGlobalStore();
const { isPc } = useSystemStore();
return (
<MyModal isOpen={isOpen} onClose={onClose} w={'100%'} maxW={['90vw', '900px']} isCentered>
@@ -86,11 +86,11 @@ const DatasetSelectContainer = ({
);
};
export const useDatasetSelect = () => {
export function useDatasetSelect() {
const { t } = useTranslation();
const [parentId, setParentId] = useState<string>();
const { data } = useQuery(['loadDatasetData', parentId], () =>
const { data, isLoading } = useQuery(['loadDatasetData', parentId], () =>
Promise.all([getDatasets({ parentId }), getDatasetPaths(parentId)])
);
@@ -98,7 +98,7 @@ export const useDatasetSelect = () => {
() => [
{
parentId: '',
parentName: t('kb.My Dataset')
parentName: t('dataset.My Dataset')
},
...(data?.[1] || [])
],
@@ -109,8 +109,9 @@ export const useDatasetSelect = () => {
parentId,
setParentId,
datasets: data?.[0] || [],
paths
paths,
isLoading
};
};
}
export default DatasetSelectContainer;

View File

@@ -25,14 +25,14 @@ import {
createAOpenApiKey,
delOpenApiById,
putOpenApiKey
} from '@/web/support/api/openapi';
} from '@/web/support/openapi/api';
import type { EditApiKeyProps } from '@/global/support/api/openapiReq';
import { useQuery, useMutation } from '@tanstack/react-query';
import { useLoading } from '@/web/common/hooks/useLoading';
import dayjs from 'dayjs';
import { AddIcon, QuestionOutlineIcon } from '@chakra-ui/icons';
import { useCopyData } from '@/web/common/hooks/useCopyData';
import { feConfigs } from '@/web/common/store/static';
import { feConfigs } from '@/web/common/system/staticData';
import { useTranslation } from 'react-i18next';
import MyIcon from '@/components/Icon';
import MyModal from '@/components/MyModal';

View File

@@ -9,7 +9,7 @@ import {
} from './index';
import type { AppItemType } from '@/types/app';
import type { FlowModuleTemplateType } from '@/types/core/app/flow';
import { chatModelList, cqModelList } from '@/web/common/store/static';
import { chatModelList, cqModelList } from '@/web/common/system/staticData';
import {
Input_Template_History,
Input_Template_TFSwitch,
@@ -240,7 +240,7 @@ export const ChatModule: FlowModuleTemplateType = {
};
export const KBSearchModule: FlowModuleTemplateType = {
flowType: FlowModuleTypeEnum.kbSearchNode,
flowType: FlowModuleTypeEnum.datasetSearchNode,
logo: '/imgs/module/db.png',
name: '知识库搜索',
intro: '去知识库中搜索对应的答案。可作为 AI 对话引用参考。',
@@ -248,7 +248,7 @@ export const KBSearchModule: FlowModuleTemplateType = {
inputs: [
Input_Template_TFSwitch,
{
key: 'kbList',
key: 'datasets',
type: FlowInputItemTypeEnum.selectDataset,
label: '关联的知识库',
value: [],
@@ -900,7 +900,7 @@ export const appTemplates: (AppItemType & {
{
moduleId: 'kbSearch',
name: '知识库搜索',
flowType: 'kbSearchNode',
flowType: 'datasetSearchNode',
showStatus: true,
position: {
x: 956.0838440206068,
@@ -908,7 +908,7 @@ export const appTemplates: (AppItemType & {
},
inputs: [
{
key: 'kbList',
key: 'datasets',
type: 'custom',
label: '关联的知识库',
value: [],
@@ -1952,7 +1952,7 @@ export const appTemplates: (AppItemType & {
{
moduleId: 'fljhzy',
name: '知识库搜索',
flowType: 'kbSearchNode',
flowType: 'datasetSearchNode',
showStatus: true,
position: {
x: 1305.5374262228029,
@@ -1960,7 +1960,7 @@ export const appTemplates: (AppItemType & {
},
inputs: [
{
key: 'kbList',
key: 'datasets',
type: 'custom',
label: '关联的知识库',
value: [],

View File

@@ -34,7 +34,7 @@ export enum FlowModuleTypeEnum {
questionInput = 'questionInput',
historyNode = 'historyNode',
chatNode = 'chatNode',
kbSearchNode = 'kbSearchNode',
datasetSearchNode = 'datasetSearchNode',
tfSwitchNode = 'tfSwitchNode',
answerNode = 'answerNode',
classifyQuestion = 'classifyQuestion',

View File

@@ -1,5 +1,5 @@
import type { AppSchema } from '@/types/mongoSchema';
import type { OutLinkEditType } from '@fastgpt/support/outLink/type.d';
import type { OutLinkEditType } from '@fastgpt/global/support/outLink/type.d';
import type {
LLMModelItemType,
ChatModelItemType,

View File

@@ -1,10 +1 @@
export enum TrainingModeEnum {
'qa' = 'qa',
'index' = 'index'
}
export const TrainingTypeMap = {
[TrainingModeEnum.qa]: 'qa',
[TrainingModeEnum.index]: 'index'
};
export const PgDatasetTableName = 'modeldata';

View File

@@ -1,3 +0,0 @@
export type CreateTrainingBillType = {
name: string;
};

View File

@@ -1,4 +0,0 @@
export type FetchResultItem = {
url: string;
content: string;
};

View File

@@ -4,7 +4,7 @@ import type {
LLMModelItemType,
VectorModelItemType
} from '@/types/model';
import type { FeConfigsType } from '@fastgpt/common/type/index.d';
import type { FeConfigsType } from '@fastgpt/global/common/system/types/index.d';
export type InitDateResponse = {
chatModels: ChatModelItemType[];

View File

@@ -1,5 +1,18 @@
import { getErrText } from './tools';
import { countPromptTokens } from './common/tiktoken';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { countPromptTokens } from '@/global/common/tiktoken';
/*
replace {{variable}} to value
*/
export function replaceVariable(text: string, obj: Record<string, string | number>) {
for (const key in obj) {
const val = obj[key];
if (typeof val !== 'string') continue;
text = text.replace(new RegExp(`{{(${key})}}`, 'g'), val);
}
return text || '';
}
/**
* text split into chunks

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +1,8 @@
/* Only the token of gpt-3.5-turbo is used */
import { ChatItemType } from '@/types/chat';
import { Tiktoken } from 'js-tiktoken/lite';
import { adaptChat2GptMessages } from '../adapt/message';
import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/core/ai/constant';
import { adaptChat2GptMessages } from '@/utils/common/adapt/message';
import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/global/core/ai/constant';
import encodingJson from './cl100k_base.json';
/* init tikToken obj */
@@ -27,7 +27,10 @@ export function getTikTokenEnc() {
}
/* count one prompt tokens */
export function countPromptTokens(prompt = '', role: `${ChatCompletionRequestMessageRoleEnum}`) {
export function countPromptTokens(
prompt = '',
role: '' | `${ChatCompletionRequestMessageRoleEnum}` = ''
) {
const enc = getTikTokenEnc();
const text = `${role}\n${prompt}`;
try {

View File

@@ -1,4 +1,4 @@
import { ChatCompletionRequestMessage } from '@fastgpt/core/ai/type';
import { ChatCompletionRequestMessage } from '@fastgpt/global/core/ai/type.d';
export type CreateQuestionGuideParams = {
messages: ChatCompletionRequestMessage[];

View File

@@ -1,6 +1,5 @@
export type AdminUpdateFeedbackParams = {
import { MarkDataType } from '../dataset/type';
export type AdminUpdateFeedbackParams = MarkDataType & {
chatItemId: string;
kbId: string;
dataId: string;
content: string;
};

View File

@@ -1,8 +1,9 @@
import { DatasetTypeEnum } from '@fastgpt/core/dataset/constant';
import { DatasetCollectionTypeEnum, DatasetTypeEnum } from '@fastgpt/global/core/dataset/constant';
import type { RequestPaging } from '@/types';
import { TrainingModeEnum } from '@/constants/plugin';
import { TrainingModeEnum } from '@fastgpt/global/core/dataset/constant';
import type { SearchTestItemType } from '@/types/core/dataset';
import { DatasetDataItemType } from '@/types/core/dataset/data';
import { DatasetChunkItemType, UploadChunkItemType } from '@fastgpt/global/core/dataset/type';
import { DatasetCollectionSchemaType } from '@fastgpt/global/core/dataset/type';
/* ===== dataset ===== */
export type DatasetUpdateParams = {
@@ -22,38 +23,50 @@ export type CreateDatasetParams = {
};
export type SearchTestProps = {
kbId: string;
datasetId: string;
text: string;
};
/* ======= file =========== */
export type GetFileListProps = RequestPaging & {
kbId: string;
searchText: string;
/* ======= collections =========== */
export type GetDatasetCollectionsProps = RequestPaging & {
datasetId: string;
parentId?: string;
searchText?: string;
simple?: boolean;
selectFolder?: boolean;
};
export type CreateDatasetCollectionParams = {
datasetId: string;
parentId?: string;
name: string;
type: `${DatasetCollectionTypeEnum}`;
metadata?: DatasetCollectionSchemaType['metadata'];
updateTime?: string;
};
export type UpdateDatasetCollectionParams = {
id: string;
parentId?: string;
name?: string;
metadata?: DatasetCollectionSchemaType['metadata'];
};
export type UpdateFileProps = { id: string; name?: string; datasetUsed?: boolean };
export type MarkFileUsedProps = { fileIds: string[] };
/* ==== data ===== */
export type SetOneDatasetDataProps = {
id?: string;
datasetId: string;
collectionId: string;
q?: string; // embedding content
a?: string; // bonus content
};
export type PushDataProps = {
kbId: string;
data: DatasetDataItemType[];
collectionId: string;
data: DatasetChunkItemType[];
mode: `${TrainingModeEnum}`;
prompt?: string;
billId?: string;
};
export type UpdateDatasetDataPrams = {
dataId: string;
kbId: string;
a?: string;
q?: string;
};
export type GetDatasetDataListProps = RequestPaging & {
kbId: string;
searchText: string;
fileId: string;
searchText?: string;
collectionId: string;
};

View File

@@ -2,11 +2,11 @@ import type { RequestPaging } from '@/types';
import { TrainingModeEnum } from '@/constants/plugin';
import type { SearchTestItemType } from '@/types/core/dataset';
import { DatasetDataItemType } from '@/types/core/dataset/data';
import { DatasetCollectionSchemaType } from '@fastgpt/global/core/dataset/type';
/* ===== dataset ===== */
export type SearchTestResponseType = SearchTestItemType['results'];
/* ======= file =========== */
/* ======= collection =========== */
/* ==== data ===== */
export type PushDataResponse = {

View File

@@ -0,0 +1,5 @@
/* ================= dataset ===================== */
/* ================= collection ===================== */
/* ================= data ===================== */

View File

@@ -0,0 +1,23 @@
import { ParentTreePathItemType } from '@fastgpt/global/common/parentFolder/type';
import { DatasetCollectionSchemaType } from '@fastgpt/global/core/dataset/type.d';
/* ================= dataset ===================== */
/* ================= collection ===================== */
export type DatasetCollectionsListItemType = {
_id: string;
parentId?: string;
name: string;
type: DatasetCollectionSchemaType['type'];
updateTime: Date;
dataAmount?: number;
trainingAmount: number;
metadata: DatasetCollectionSchemaType['metadata'];
};
/* ================= data ===================== */
export type DatasetDataListItemType = {
id: string;
q: string; // embedding content
a: string; // bonus content
};

View File

@@ -0,0 +1,7 @@
export type MarkDataType = {
dataId: string;
datasetId: string;
collectionId: string;
q: string;
a?: string;
};

View File

@@ -1,4 +1,4 @@
import { PromptTemplateItem } from '@fastgpt/core/ai/type.d';
import { PromptTemplateItem } from '@fastgpt/global/core/ai/type.d';
export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
{
@@ -9,7 +9,7 @@ export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [
{
title: '全部变量',
desc: '包含 q 和 a 两个变量的标准模板',
value: `{instruction:"{{q}}",output:"{{a}}",source:"{{source}}",file_id:"{{file_id}}",index:"{{index}}"}`
value: `{instruction:"{{q}}",output:"{{a}}",source:"{{source}}",sourceId:"{{sourceId}}",index:"{{index}}"}`
}
];
@@ -24,7 +24,7 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [
对话要求:
1. 背景知识是最新的,其中 instruction 是相关介绍output 是预期回答或补充。
2. 使用背景知识回答问题。
3. 背景知识无法满足问题时,你需严谨的回答问题
3. 使用对话的风格回答我的问题,答案要和背景知识表述一致
我的问题是:"{{question}}"`
},
{

View File

@@ -1,4 +1,4 @@
import type { OpenApiSchema } from '@fastgpt/support/openapi/type.d';
import type { OpenApiSchema } from '@fastgpt/global/support/openapi/type';
export type GetApiKeyProps = {
appId?: string;

View File

@@ -8,12 +8,12 @@ import { theme } from '@/web/styles/theme';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import NProgress from 'nprogress'; //nprogress module
import Router from 'next/router';
import { clientInitData, feConfigs } from '@/web/common/store/static';
import { clientInitData, feConfigs } from '@/web/common/system/staticData';
import { appWithTranslation, useTranslation } from 'next-i18next';
import { getLangStore, setLangStore } from '@/web/common/utils/i18n';
import { useRouter } from 'next/router';
import { useGlobalStore } from '@/web/common/store/global';
import type { FeConfigsType } from '@fastgpt/common/type/index.d';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import type { FeConfigsType } from '@fastgpt/global/common/system/types/index.d';
import 'nprogress/nprogress.css';
import '@/web/styles/reset.scss';
@@ -38,7 +38,7 @@ function App({ Component, pageProps }: AppProps) {
const router = useRouter();
const { hiId } = router.query as { hiId?: string };
const { i18n } = useTranslation();
const { setLastRoute } = useGlobalStore();
const { setLastRoute } = useSystemStore();
const [scripts, setScripts] = useState<FeConfigsType['scripts']>([]);
useEffect(() => {

View File

@@ -1,11 +1,11 @@
import { useEffect } from 'react';
import { useRouter } from 'next/router';
import { serviceSideProps } from '@/web/common/utils/i18n';
import { useGlobalStore } from '@/web/common/store/global';
import { useSystemStore } from '@/web/common/system/useSystemStore';
function Error() {
const router = useRouter();
const { lastRoute } = useGlobalStore();
const { lastRoute } = useSystemStore();
useEffect(() => {
setTimeout(() => {

View File

@@ -14,7 +14,7 @@ import {
import { UserBillType } from '@/types/user';
import dayjs from 'dayjs';
import { BillSourceMap } from '@/constants/user';
import { formatPrice } from '@fastgpt/common/bill/index';
import { formatPrice } from '@fastgpt/global/common/bill/tools';
import MyModal from '@/components/MyModal';
import { useTranslation } from 'react-i18next';

View File

@@ -12,7 +12,7 @@ import {
Button
} from '@chakra-ui/react';
import { BillSourceMap } from '@/constants/user';
import { getUserBills } from '@/web/common/api/bill';
import { getUserBills } from '@/web/common/bill/api';
import type { UserBillType } from '@/types/user';
import { usePagination } from '@/web/common/hooks/usePagination';
import { useLoading } from '@/web/common/hooks/useLoading';
@@ -21,7 +21,7 @@ import MyIcon from '@/components/Icon';
import DateRangePicker, { type DateRangeType } from '@/components/DateRangePicker';
import { addDays } from 'date-fns';
import dynamic from 'next/dynamic';
import { useGlobalStore } from '@/web/common/store/global';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useTranslation } from 'next-i18next';
const BillDetail = dynamic(() => import('./BillDetail'));
@@ -32,7 +32,7 @@ const BillTable = () => {
from: addDays(new Date(), -7),
to: new Date()
});
const { isPc } = useGlobalStore();
const { isPc } = useSystemStore();
const {
data: bills,

View File

@@ -15,15 +15,15 @@ import {
import { useForm } from 'react-hook-form';
import { UserUpdateParams } from '@/types/user';
import { useToast } from '@/web/common/hooks/useToast';
import { useUserStore } from '@/web/support/store/user';
import { useUserStore } from '@/web/support/user/useUserStore';
import { UserType } from '@/types/user';
import { useQuery } from '@tanstack/react-query';
import dynamic from 'next/dynamic';
import { useSelectFile } from '@/web/common/hooks/useSelectFile';
import { compressImg } from '@/web/common/utils/file';
import { feConfigs, systemVersion } from '@/web/common/store/static';
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
import { compressImg } from '@/web/common/file/utils';
import { feConfigs, systemVersion } from '@/web/common/system/staticData';
import { useTranslation } from 'next-i18next';
import { timezoneList } from '@/utils/user';
import { timezoneList } from '@fastgpt/global/common/time/timezone';
import Loading from '@/components/Loading';
import Avatar from '@/components/Avatar';
import MyIcon from '@/components/Icon';

View File

@@ -1,17 +1,17 @@
import React from 'react';
import { Box, Flex, useTheme } from '@chakra-ui/react';
import { getInforms, readInform } from '@/web/support/api/user';
import { getInforms, readInform } from '@/web/support/user/api';
import { usePagination } from '@/web/common/hooks/usePagination';
import { useLoading } from '@/web/common/hooks/useLoading';
import type { informSchema } from '@/types/mongoSchema';
import { formatTimeToChatTime } from '@/utils/tools';
import { useGlobalStore } from '@/web/common/store/global';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import MyIcon from '@/components/Icon';
const BillTable = () => {
const theme = useTheme();
const { Loading } = useLoading();
const { isPc } = useGlobalStore();
const { isPc } = useSystemStore();
const {
data: informs,
isLoading,

View File

@@ -1,14 +1,14 @@
import React, { useState, useCallback } from 'react';
import { ModalFooter, ModalBody, Button, Input, Box, Grid } from '@chakra-ui/react';
import { getPayCode, checkPayResult } from '@/web/common/api/bill';
import { getPayCode, checkPayResult } from '@/web/common/bill/api';
import { useToast } from '@/web/common/hooks/useToast';
import { useQuery } from '@tanstack/react-query';
import { useRouter } from 'next/router';
import { getErrText } from '@/utils/tools';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { useTranslation } from 'react-i18next';
import Markdown from '@/components/Markdown';
import MyModal from '@/components/MyModal';
import { priceMd } from '@/web/common/store/static';
import { priceMd } from '@/web/common/system/staticData';
const PayModal = ({ onClose }: { onClose: () => void }) => {
const router = useRouter();
@@ -69,12 +69,7 @@ const PayModal = ({ onClose }: { onClose: () => void }) => {
title={t('user.Pay')}
isCentered={!payId}
>
<ModalBody
p={0}
h={payId ? 'auto' : ['auto', '70vh']}
display={'flex'}
flexDirection={'column'}
>
<ModalBody p={0} minH={payId ? 'auto' : '70vh'} display={'flex'} flexDirection={'column'}>
{!payId && (
<>
<Grid gridTemplateColumns={'repeat(4,1fr)'} gridGap={5} mb={4} px={6}>

View File

@@ -11,11 +11,11 @@ import {
Flex,
Box
} from '@chakra-ui/react';
import { getPayOrders, checkPayResult } from '@/web/common/api/bill';
import { getPayOrders, checkPayResult } from '@/web/common/bill/api';
import { PaySchema } from '@/types/mongoSchema';
import dayjs from 'dayjs';
import { useQuery } from '@tanstack/react-query';
import { formatPrice } from '@fastgpt/common/bill/index';
import { formatPrice } from '@fastgpt/global/common/bill/tools';
import { useToast } from '@/web/common/hooks/useToast';
import { useLoading } from '@/web/common/hooks/useLoading';
import MyIcon from '@/components/Icon';
@@ -58,7 +58,7 @@ const PayRecordTable = () => {
);
return (
<Box position={'relative'} h={'100%'}>
<Box position={'relative'} h={'100%'} overflow={'overlay'}>
{!isInitialLoading && payOrders.length === 0 ? (
<Flex h={'100%'} flexDirection={'column'} alignItems={'center'} justifyContent={'center'}>
<MyIcon name="empty" w={'48px'} h={'48px'} color={'transparent'} />
@@ -67,7 +67,7 @@ const PayRecordTable = () => {
</Box>
</Flex>
) : (
<TableContainer py={[0, 5]} px={[3, 8]} h={'100%'} overflow={'overlay'}>
<TableContainer py={[0, 5]} px={[3, 8]}>
<Table>
<Thead>
<Tr>

View File

@@ -16,8 +16,8 @@ import {
} from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
import { useQuery } from '@tanstack/react-query';
import { getPromotionInitData, getPromotionRecords } from '@/web/support/api/user';
import { useUserStore } from '@/web/support/store/user';
import { getPromotionInitData, getPromotionRecords } from '@/web/support/user/api';
import { useUserStore } from '@/web/support/user/useUserStore';
import { useLoading } from '@/web/common/hooks/useLoading';
import MyTooltip from '@/components/MyTooltip';

View File

@@ -4,7 +4,7 @@ import MyModal from '@/components/MyModal';
import { useTranslation } from 'react-i18next';
import { useForm } from 'react-hook-form';
import { useRequest } from '@/web/common/hooks/useRequest';
import { updatePasswordByOld } from '@/web/support/api/user';
import { updatePasswordByOld } from '@/web/support/user/api';
type FormType = {
oldPsw: string;

View File

@@ -1,17 +1,17 @@
import React, { useCallback, useRef } from 'react';
import { Box, Flex, useTheme } from '@chakra-ui/react';
import { useGlobalStore } from '@/web/common/store/global';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useRouter } from 'next/router';
import dynamic from 'next/dynamic';
import { clearToken } from '@/utils/user';
import { useUserStore } from '@/web/support/store/user';
import { clearToken } from '@/web/support/user/auth';
import { useUserStore } from '@/web/support/user/useUserStore';
import { useConfirm } from '@/web/common/hooks/useConfirm';
import PageContainer from '@/components/PageContainer';
import SideTabs from '@/components/SideTabs';
import Tabs from '@/components/Tabs';
import UserInfo from './components/Info';
import { serviceSideProps } from '@/web/common/utils/i18n';
import { feConfigs } from '@/web/common/store/static';
import { feConfigs } from '@/web/common/system/staticData';
import { useTranslation } from 'react-i18next';
import Script from 'next/script';
@@ -86,7 +86,7 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
const router = useRouter();
const theme = useTheme();
const { isPc } = useGlobalStore();
const { isPc } = useSystemStore();
const { setUserInfo } = useUserStore();
const setCurrentTab = useCallback(

View File

@@ -1,35 +0,0 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { authUser } from '@fastgpt/support/user/auth';
import { PgClient } from '@/service/pg';
import { PgDatasetTableName } from '@/constants/plugin';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await authUser({ req, authRoot: true });
const { rowCount } = await PgClient.query(`SELECT 1
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = '${PgDatasetTableName}'
AND column_name = 'file_id'`);
if (rowCount > 0) {
return jsonRes(res, {
data: '已经存在file_id字段'
});
}
jsonRes(res, {
data: await PgClient.query(
`ALTER TABLE ${PgDatasetTableName} ADD COLUMN file_id VARCHAR(100)`
)
});
} catch (error) {
jsonRes(res, {
code: 500,
error
});
}
}

View File

@@ -1,10 +1,10 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { authUser } from '@fastgpt/support/user/auth';
import { authUser } from '@fastgpt/service/support/user/auth';
import { connectToDatabase } from '@/service/mongo';
import { MongoDataset } from '@fastgpt/core/dataset/schema';
import { DatasetTypeEnum } from '@fastgpt/core/dataset/constant';
import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constant';
import { PgClient } from '@/service/pg';
import { PgDatasetTableName } from '@/constants/plugin';

View File

@@ -1,8 +1,8 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { authUser } from '@fastgpt/support/user/auth';
import { authUser } from '@fastgpt/service/support/user/auth';
import { connectToDatabase } from '@/service/mongo';
import mongoose from '@fastgpt/common/mongo';
import mongoose from '@fastgpt/service/common/mongo';
import { PgClient } from '@/service/pg';
import { PgDatasetTableName } from '@/constants/plugin';

View File

@@ -1,6 +1,6 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { authUser } from '@fastgpt/support/user/auth';
import { authUser } from '@fastgpt/service/support/user/auth';
import { connectToDatabase, Bill } from '@/service/mongo';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {

View File

@@ -1,6 +1,6 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { authUser } from '@fastgpt/support/user/auth';
import { authUser } from '@fastgpt/service/support/user/auth';
import { connectToDatabase, App } from '@/service/mongo';
import { FlowInputItemTypeEnum, FlowModuleTypeEnum } from '@/constants/flow';
import { SystemInputEnum } from '@/constants/app';

View File

@@ -1,11 +1,11 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { authUser } from '@fastgpt/support/user/auth';
import { authUser } from '@fastgpt/service/support/user/auth';
import { connectToDatabase } from '@/service/mongo';
import { PgClient } from '@/service/pg';
import { PgDatasetTableName } from '@/constants/plugin';
import { DatasetSpecialIdEnum } from '@fastgpt/core/dataset/constant';
import { Types, connectionMongo } from '@fastgpt/common/mongo';
import { DatasetSpecialIdEnum } from '@fastgpt/global/core/dataset/constant';
import { Types, connectionMongo } from '@fastgpt/service/common/mongo';
import { delay } from '@/utils/tools';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {

View File

@@ -0,0 +1,344 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { App, connectToDatabase } from '@/service/mongo';
import { PgClient } from '@/service/pg';
import { connectionMongo } from '@fastgpt/service/common/mongo';
import { PgDatasetTableName } from '@/constants/plugin';
import { FlowModuleTypeEnum } from '@/constants/flow';
import { delay } from '@/utils/tools';
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
import { DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constant';
import { strIsLink } from '@fastgpt/global/common/string/tools';
import { GridFSStorage } from '@/service/lib/gridfs';
import { Types } from 'mongoose';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
const { limit = 50 } = req.body as { limit: number };
await connectToDatabase();
console.log('rename');
await rename();
console.log('init mongo data');
await initMongo(limit);
console.log('create collection');
await createCollection();
console.log('update pg collectionId');
await updatePgCollection();
console.log('init done');
jsonRes(res, {
data: {}
});
} catch (error) {
console.log(error);
jsonRes(res, {
code: 500,
error
});
}
}
async function rename() {
// rename mongo kbs -> datasets
try {
const collections = await connectionMongo.connection.db
.listCollections({ name: 'kbs' })
.toArray();
if (collections.length > 0) {
const kbCollection = connectionMongo.connection.db.collection('kbs');
await kbCollection.rename('datasets', { dropTarget: true });
console.log('success rename kbs -> datasets');
}
} catch (error) {
console.log('error rename kbs -> datasets', error);
}
// rename pg: kb_id -> dataset_id
try {
const { rows } = await PgClient.query(`SELECT EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_name = '${PgDatasetTableName}'
AND column_name = 'kb_id'
);`);
if (rows[0].exists) {
await PgClient.query(`ALTER TABLE ${PgDatasetTableName} RENAME COLUMN kb_id TO dataset_id`);
console.log('success rename kb_id -> dataset_id');
}
} catch (error) {
console.log('error rename kb_id -> dataset_id', error);
}
// rename pg: file_id -> collection_id
try {
const { rows } = await PgClient.query(`SELECT EXISTS (
SELECT 1
FROM information_schema.columns
WHERE table_name = '${PgDatasetTableName}'
AND column_name = 'file_id'
);`);
if (rows[0].exists) {
await PgClient.query(
`ALTER TABLE ${PgDatasetTableName} RENAME COLUMN file_id TO collection_id`
);
console.log('success rename file_id -> collection_id');
}
} catch (error) {
console.log('error rename file_id -> collection_id', error);
}
}
async function initMongo(limit: number) {
let success = 0;
async function initApp(limit = 100): Promise<any> {
// 遍历所有 app更新 app modules 里的 FlowModuleTypeEnum.kbSearchNode
const apps = await App.find({ inited: false }).limit(limit);
if (apps.length === 0) return;
try {
await Promise.all(
apps.map(async (app) => {
const modules = app.toObject().modules;
// @ts-ignore
app.inited = true;
modules.forEach((module) => {
// @ts-ignore
if (module.flowType === 'kbSearchNode') {
module.flowType = FlowModuleTypeEnum.datasetSearchNode;
module.inputs.forEach((input) => {
if (input.key === 'kbList') {
input.key = 'datasets';
input.value?.forEach((item: any) => {
item.datasetId = item.kbId;
});
}
});
}
});
app.modules = JSON.parse(JSON.stringify(modules));
await app.save();
})
);
success += limit;
console.log('mongo init:', success);
return initApp(limit);
} catch (error) {
return initApp(limit);
}
}
// init app
await App.updateMany(
{},
{
$set: {
inited: false
}
}
);
const totalApp = await App.countDocuments();
console.log(`total app: ${totalApp}`);
await delay(2000);
console.log('start init app');
await initApp(limit);
console.log('init mongo success');
}
type RowType = { user_id: string; dataset_id: string; collection_id: string };
async function createCollection() {
let success = 0;
const { rows, rowCount } = await PgClient.query(`SELECT user_id,dataset_id,collection_id
FROM ${PgDatasetTableName}
GROUP BY user_id,collection_id, dataset_id
ORDER BY dataset_id`);
if (rowCount === 0) {
console.log('pg done');
return;
}
// init dataset collection
console.log(`total collection: ${rowCount}`);
// collectionId 的类型manual, mark, httpLink, fileId
async function initCollection(row: RowType): Promise<any> {
try {
{
const userId = row.user_id;
const datasetId = row.dataset_id;
const collectionId = row.collection_id;
const count = await MongoDatasetCollection.countDocuments({
datasetId,
userId,
['metadata.pgCollectionId']: collectionId
});
if (count > 0) {
console.log('collection already exist');
return;
}
if (collectionId === 'manual') {
await MongoDatasetCollection.create({
parentId: null,
datasetId,
userId,
name: '手动录入',
type: DatasetCollectionTypeEnum.virtual,
updateTime: new Date('2099'),
metadata: {
pgCollectionId: collectionId
}
});
} else if (collectionId === 'mark') {
await MongoDatasetCollection.create({
parentId: null,
datasetId,
userId,
name: '手动标注',
type: DatasetCollectionTypeEnum.virtual,
updateTime: new Date('2099'),
metadata: {
pgCollectionId: collectionId
}
});
} else if (strIsLink(collectionId)) {
await MongoDatasetCollection.create({
parentId: null,
datasetId,
userId,
name: collectionId,
type: DatasetCollectionTypeEnum.link,
metadata: {
rawLink: collectionId,
pgCollectionId: collectionId
}
});
} else {
// find file
const gridFs = new GridFSStorage('dataset', userId);
const collection = gridFs.Collection();
const file = await collection.findOne({
_id: new Types.ObjectId(collectionId)
});
if (file) {
await MongoDatasetCollection.create({
parentId: null,
datasetId,
userId,
name: file.filename,
type: DatasetCollectionTypeEnum.file,
metadata: {
fileId: file._id,
pgCollectionId: collectionId
}
});
} else {
// no file
await MongoDatasetCollection.create({
parentId: null,
datasetId,
userId,
name: '未知文件',
type: DatasetCollectionTypeEnum.virtual,
metadata: {
pgCollectionId: collectionId
}
});
}
}
console.log('create collection success');
}
} catch (error) {
console.log(error);
await delay(2000);
return initCollection(row);
}
}
for await (const row of rows) {
await initCollection(row);
console.log('init collection success: ', ++success);
}
}
async function updatePgCollection(): Promise<any> {
let success = 0;
const limit = 10;
const collections = await MongoDatasetCollection.find({
'metadata.pgCollectionId': { $exists: true, $ne: '' }
}).lean();
console.log('total:', collections.length);
async function update(i: number): Promise<any> {
const item = collections[i];
if (!item) return;
try {
console.log('start', item.name, item.datasetId, item.metadata.pgCollectionId);
const time = Date.now();
if (item.metadata.pgCollectionId) {
const { rows } = await PgClient.select(PgDatasetTableName, {
fields: ['id'],
where: [
['dataset_id', String(item.datasetId)],
'AND',
['collection_id', String(item.metadata.pgCollectionId)]
],
limit: 999999
});
console.log('update date total', rows.length, 'time:', Date.now() - time);
await PgClient.query(`
update ${PgDatasetTableName} set collection_id = '${item._id}' where dataset_id = '${String(
item.datasetId
)}' AND collection_id = '${String(item.metadata.pgCollectionId)}'
`);
console.log('pg update time', Date.now() - time);
}
// 更新 file id
if (item.type === 'file' && item.metadata.fileId) {
const collection = connectionMongo.connection.db.collection(`dataset.files`);
await collection.findOneAndUpdate({ _id: new Types.ObjectId(item.metadata.fileId) }, [
{
$set: {
'metadata.datasetId': item.datasetId,
'metadata.collectionId': item._id
}
}
]);
}
await MongoDatasetCollection.findByIdAndUpdate(item._id, {
$unset: { 'metadata.pgCollectionId': '' }
});
console.log('success', ++success);
return update(i + limit);
} catch (error) {
console.log(error);
await delay(5000);
return update(i);
}
}
const arr = new Array(limit).fill(0);
return Promise.all(arr.map((_, i) => update(i)));
}

View File

@@ -1,45 +0,0 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { authUser } from '@fastgpt/support/user/auth';
import { connectToDatabase } from '@/service/mongo';
import { PgClient } from '@/service/pg';
import { PgDatasetTableName } from '@/constants/plugin';
import { DatasetSpecialIdEnum } from '@fastgpt/core/dataset/constant';
import { Types, connectionMongo } from '@fastgpt/common/mongo';
import { delay } from '@/utils/tools';
import { replaceVariable } from '@/utils/common/tools/text';
import { getVector } from '../openapi/plugin/vector';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
// await connectToDatabase();
// const { text, analyze, sql } = req.body as {
// userId: string;
// text: string;
// analyze?: boolean;
// sql: string;
// };
// await authUser({ req, authRoot: true });
// const vectorModel = global.vectorModels[0];
// const { vectors } = await getVector({
// model: vectorModel.model,
// input: [text]
// });
// const start = Date.now();
// const result: any = await PgClient.query(sql.replace(/\[vector\]/g, `[${vectors[0]}]`));
jsonRes(res, {
data: {
// rows: result?.[2]?.rows,
// time: Date.now() - start
}
});
} catch (error) {
jsonRes(res, {
code: 500,
error
});
}
}

View File

@@ -2,7 +2,7 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase } from '@/service/mongo';
import { authUser } from '@fastgpt/support/user/auth';
import { authUser } from '@fastgpt/service/support/user/auth';
import { App } from '@/service/models/app';
import type { CreateAppParams } from '@/types/app';
import { AppTypeEnum } from '@/constants/app';

View File

@@ -1,8 +1,8 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase, Bill } from '@/service/mongo';
import { authUser } from '@fastgpt/support/user/auth';
import { Types } from '@fastgpt/common/mongo';
import { authUser } from '@fastgpt/service/support/user/auth';
import { Types } from '@fastgpt/service/common/mongo';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {

View File

@@ -1,8 +1,8 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { Chat, App, connectToDatabase, Collection } from '@/service/mongo';
import { MongoOutLink } from '@fastgpt/support/outLink/schema';
import { authUser } from '@fastgpt/support/user/auth';
import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
import { authUser } from '@fastgpt/service/support/user/auth';
import { authApp } from '@/service/utils/auth';
/* 获取我的模型 */

View File

@@ -1,7 +1,7 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase } from '@/service/mongo';
import { authUser } from '@fastgpt/support/user/auth';
import { authUser } from '@fastgpt/service/support/user/auth';
import { authApp } from '@/service/utils/auth';
/* 获取我的模型 */

View File

@@ -1,10 +1,10 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { Chat, connectToDatabase } from '@/service/mongo';
import { authUser } from '@fastgpt/support/user/auth';
import { authUser } from '@fastgpt/service/support/user/auth';
import type { PagingData } from '@/types';
import { AppLogsListItemType } from '@/types/app';
import { Types } from '@fastgpt/common/mongo';
import { Types } from '@fastgpt/service/common/mongo';
import { addDays } from 'date-fns';
import type { GetAppChatLogsParams } from '@/global/core/api/appReq.d';

View File

@@ -1,7 +1,7 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase, App } from '@/service/mongo';
import { authUser } from '@fastgpt/support/user/auth';
import { authUser } from '@fastgpt/service/support/user/auth';
import { AppListItemType } from '@/types/app';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {

View File

@@ -1,7 +1,7 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase, Collection, App } from '@/service/mongo';
import { authUser } from '@fastgpt/support/user/auth';
import { authUser } from '@fastgpt/service/support/user/auth';
/* 模型收藏切换 */
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {

View File

@@ -3,8 +3,8 @@ import { jsonRes } from '@/service/response';
import { connectToDatabase, App } from '@/service/mongo';
import type { PagingData } from '@/types';
import type { ShareAppItem } from '@/types/app';
import { authUser } from '@fastgpt/support/user/auth';
import { Types } from '@fastgpt/common/mongo';
import { authUser } from '@fastgpt/service/support/user/auth';
import { Types } from '@fastgpt/service/common/mongo';
/* 获取模型列表 */
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {

View File

@@ -1,7 +1,7 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase } from '@/service/mongo';
import { authUser } from '@fastgpt/support/user/auth';
import { authUser } from '@fastgpt/service/support/user/auth';
import { App } from '@/service/models/app';
import type { AppUpdateParams } from '@/types/app';
import { authApp } from '@/service/utils/auth';

View File

@@ -1,9 +1,9 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { connectToDatabase } from '@/service/mongo';
import { authUser } from '@fastgpt/support/user/auth';
import { authUser } from '@fastgpt/service/support/user/auth';
import { sseErrRes } from '@/service/response';
import { sseResponseEventEnum } from '@/constants/chat';
import { responseWrite } from '@fastgpt/common/tools/stream';
import { responseWrite } from '@fastgpt/service/common/response';
import { AppModuleItemType } from '@/types/app';
import { dispatchModules } from '@/pages/api/v1/chat/completions';
import { pushChatBill } from '@/service/common/bill/push';

View File

@@ -1,7 +1,7 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase, ChatItem } from '@/service/mongo';
import { authUser } from '@fastgpt/support/user/auth';
import { authUser } from '@fastgpt/service/support/user/auth';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {

View File

@@ -2,15 +2,15 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase, ChatItem } from '@/service/mongo';
import type { AdminUpdateFeedbackParams } from '@/global/core/api/chatReq.d';
import { authUser } from '@fastgpt/support/user/auth';
import { authUser } from '@fastgpt/service/support/user/auth';
/* 初始化我的聊天框,需要身份验证 */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await connectToDatabase();
const { chatItemId, kbId, dataId, content = undefined } = req.body as AdminUpdateFeedbackParams;
const { chatItemId, datasetId, dataId, q, a } = req.body as AdminUpdateFeedbackParams;
if (!chatItemId || !kbId || !dataId || !content) {
if (!chatItemId || !datasetId || !dataId || !q) {
throw new Error('missing parameter');
}
@@ -23,9 +23,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
},
{
adminFeedback: {
kbId,
datasetId,
dataId,
content
q,
a
}
}
);

View File

@@ -1,7 +1,7 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase, Chat } from '@/service/mongo';
import { authUser } from '@fastgpt/support/user/auth';
import { authUser } from '@fastgpt/service/support/user/auth';
import type { ChatHistoryItemType } from '@/types/chat';
import { ChatSourceEnum } from '@/constants/chat';

View File

@@ -1,7 +1,7 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase, Chat } from '@/service/mongo';
import { authUser } from '@fastgpt/support/user/auth';
import { authUser } from '@fastgpt/service/support/user/auth';
export type Props = {
chatId: string;

View File

@@ -2,11 +2,11 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { Chat, ChatItem, connectToDatabase } from '@/service/mongo';
import type { InitChatResponse } from '@/global/core/api/chatRes.d';
import { authUser } from '@fastgpt/support/user/auth';
import { authUser } from '@fastgpt/service/support/user/auth';
import { ChatItemType } from '@/types/chat';
import { authApp } from '@/service/utils/auth';
import type { ChatSchema } from '@/types/mongoSchema';
import { getGuideModule } from '@/components/ChatBox/utils';
import { getGuideModule } from '@/global/core/app/modules/utils';
import { getChatModelNameListByModules } from '@/service/core/app/module';
import { TaskResponseKeyEnum } from '@/constants/chat';

View File

@@ -1,7 +1,7 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase, Chat, ChatItem } from '@/service/mongo';
import { authUser } from '@fastgpt/support/user/auth';
import { authUser } from '@fastgpt/service/support/user/auth';
import { ChatSourceEnum } from '@/constants/chat';
type Props = {

View File

@@ -1,9 +1,9 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase, Bill } from '@/service/mongo';
import { authUser } from '@fastgpt/support/user/auth';
import { authUser } from '@fastgpt/service/support/user/auth';
import { BillSourceEnum } from '@/constants/user';
import { CreateTrainingBillType } from '@/global/common/api/billReq.d';
import { CreateTrainingBillType } from '@fastgpt/global/common/bill/types/billReq.d';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {

View File

@@ -1,10 +1,10 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase } from '@/service/mongo';
import { authUser } from '@fastgpt/support/user/auth';
import { authUser } from '@fastgpt/service/support/user/auth';
import type { CreateQuestionGuideParams } from '@/global/core/api/aiReq.d';
import { pushQuestionGuideBill } from '@/service/common/bill/push';
import { createQuestionGuide } from '@fastgpt/core/ai/functions/createQuestionGuide';
import { createQuestionGuide } from '@fastgpt/service/core/ai/functions/createQuestionGuide';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {

View File

@@ -1,8 +1,8 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase } from '@/service/mongo';
import { MongoDataset } from '@fastgpt/core/dataset/schema';
import { authUser } from '@fastgpt/support/user/auth';
import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
import { authUser } from '@fastgpt/service/support/user/auth';
import { getVectorModel } from '@/service/core/ai/model';
import type { DatasetsItemType } from '@/types/core/dataset';
@@ -12,12 +12,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
// 凭证校验
const { userId } = await authUser({ req, authToken: true });
const kbList = await MongoDataset.find({
const datasets = await MongoDataset.find({
userId,
type: 'dataset'
});
const data = kbList.map((item) => ({
const data = datasets.map((item) => ({
...item.toJSON(),
vectorModel: getVectorModel(item.vectorModel)
}));

View File

@@ -0,0 +1,87 @@
/*
Create one dataset collection
*/
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase } from '@/service/mongo';
import { authUser } from '@fastgpt/service/support/user/auth';
import type { CreateDatasetCollectionParams } from '@/global/core/api/datasetReq.d';
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
import { DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constant';
import { getCollectionUpdateTime } from '@fastgpt/service/core/dataset/collection/utils';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
await connectToDatabase();
const { userId } = await authUser({ req, authToken: true });
const body = req.body || {};
jsonRes(res, {
data: await createOneCollection({
...body,
userId
})
});
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
}
}
export async function createOneCollection({
name,
parentId,
datasetId,
type,
metadata = {},
userId
}: CreateDatasetCollectionParams & { userId: string }) {
const { _id } = await MongoDatasetCollection.create({
name,
userId,
datasetId,
parentId: parentId || null,
type,
metadata,
updateTime: getCollectionUpdateTime({ name })
});
// create default collection
if (type === DatasetCollectionTypeEnum.folder) {
await createDefaultCollection({
datasetId,
parentId: _id,
userId
});
}
return _id;
}
// create default collection
export function createDefaultCollection({
name = '手动录入',
datasetId,
parentId,
userId
}: {
name?: '手动录入' | '手动标注';
datasetId: string;
parentId?: string;
userId: string;
}) {
return MongoDatasetCollection.create({
name,
userId,
datasetId,
parentId,
type: DatasetCollectionTypeEnum.virtual,
updateTime: new Date('2000'),
metadata: {}
});
}

View File

@@ -0,0 +1,61 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase } from '@/service/mongo';
import { MongoDatasetTraining } from '@fastgpt/service/core/dataset/training/schema';
import { authUser } from '@fastgpt/service/support/user/auth';
import { findCollectionAndChild } from '@fastgpt/service/core/dataset/collection/utils';
import { delDataByCollectionId } from '@/service/core/dataset/data/utils';
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
import { GridFSStorage } from '@/service/lib/gridfs';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
await connectToDatabase();
const { collectionId } = req.query as { collectionId: string };
if (!collectionId) {
throw new Error('CollectionIdId is required');
}
// 凭证校验
const { userId } = await authUser({ req, authToken: true });
// find all delete id
const collections = await findCollectionAndChild(collectionId, '_id metadata');
const delIdList = collections.map((item) => item._id);
// delete pg data
await delDataByCollectionId({ userId, collectionIds: delIdList });
// delete training data
await MongoDatasetTraining.deleteMany({
datasetCollectionId: { $in: delIdList },
userId
});
// delete file
const gridFs = new GridFSStorage('dataset', userId);
const fileCollection = gridFs.Collection();
await Promise.all(
collections.map(
(item) =>
//@ts-ignore
item.metadata?.fileId && fileCollection.findOneAndDelete({ _id: item.metadata.fileId })
)
);
// delete collection
await MongoDatasetCollection.deleteMany({
_id: { $in: delIdList },
userId
});
jsonRes(res);
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
}
}

View File

@@ -0,0 +1,37 @@
/*
Get one dataset collection detail
*/
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase } from '@/service/mongo';
import { authUser } from '@fastgpt/service/support/user/auth';
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
await connectToDatabase();
const { id } = req.query as { id: string };
if (!id) {
throw new Error('Id is required');
}
// 凭证校验
const { userId } = await authUser({ req, authToken: true });
const collection = await MongoDatasetCollection.findOne({ _id: id, userId }).lean();
if (!collection) {
throw new Error('Collection not found');
}
jsonRes(res, {
data: collection
});
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
}
}

View File

@@ -0,0 +1,134 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase } from '@/service/mongo';
import { DatasetTrainingCollectionName } from '@fastgpt/service/core/dataset/training/schema';
import { authUser } from '@fastgpt/service/support/user/auth';
import { Types } from '@fastgpt/service/common/mongo';
import type { DatasetCollectionsListItemType } from '@/global/core/dataset/response';
import type { GetDatasetCollectionsProps } from '@/global/core/api/datasetReq';
import { PagingData } from '@/types';
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
import { countCollectionData } from '@/service/core/dataset/data/utils';
import { DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constant';
import { startQueue } from '@/service/utils/tools';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
await connectToDatabase();
let {
pageNum = 1,
pageSize = 10,
datasetId,
parentId = null,
searchText = '',
selectFolder = false,
simple = false
} = req.body as GetDatasetCollectionsProps;
searchText = searchText?.replace(/'/g, '');
// 凭证校验
const { userId } = await authUser({ req, authToken: true });
const match = {
userId: new Types.ObjectId(userId),
datasetId: new Types.ObjectId(datasetId),
parentId: parentId ? new Types.ObjectId(parentId) : null,
...(selectFolder ? { type: DatasetCollectionTypeEnum.folder } : {}),
...(searchText
? {
name: new RegExp(searchText, 'i')
}
: {})
};
if (simple) {
const collections = await MongoDatasetCollection.find(match, '_id name type parentId')
.sort({
updateTime: -1
})
.lean();
return jsonRes<PagingData<DatasetCollectionsListItemType>>(res, {
data: {
pageNum,
pageSize,
data: await Promise.all(
collections.map(async (item) => ({
...item,
dataAmount: 0,
trainingAmount: 0
}))
),
total: await MongoDatasetCollection.countDocuments(match)
}
});
}
const collections = await MongoDatasetCollection.aggregate([
{
$match: match
},
{
$lookup: {
from: DatasetTrainingCollectionName,
localField: '_id',
foreignField: 'datasetCollectionId',
as: 'trainings_amount'
}
},
// 统计子集合的数量和子训练的数量
{
$project: {
_id: 1,
parentId: 1,
fileId: 1,
name: 1,
type: 1,
updateTime: 1,
trainingAmount: { $size: '$trainings_amount' }
}
},
{
$sort: { updateTime: -1 }
},
{
$skip: (pageNum - 1) * pageSize
},
{
$limit: pageSize
}
]);
const counts = await countCollectionData({
collectionIds: collections.map((item) => item._id),
datasetId
});
const data = await Promise.all(
collections.map(async (item, i) => ({
...item,
dataAmount: item.type === DatasetCollectionTypeEnum.folder ? undefined : counts[i]
}))
);
if (data.find((item) => item.trainingAmount > 0)) {
startQueue(1);
}
// count collections
jsonRes<PagingData<DatasetCollectionsListItemType>>(res, {
data: {
pageNum,
pageSize,
data,
total: await MongoDatasetCollection.countDocuments(match)
}
});
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
}
}

View File

@@ -0,0 +1,29 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase } from '@/service/mongo';
import type { DatasetPathItemType } from '@/types/core/dataset';
import { getDatasetCollectionPaths } from '@fastgpt/service/core/dataset/collection/utils';
import { authUser } from '@fastgpt/service/support/user/auth';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
await connectToDatabase();
const { parentId } = req.query as { parentId: string };
const { userId } = await authUser({ req, authToken: true });
const paths = await getDatasetCollectionPaths({
parentId,
userId
});
jsonRes<DatasetPathItemType[]>(res, {
data: paths
});
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
}
}

View File

@@ -0,0 +1,48 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase } from '@/service/mongo';
import { authUser } from '@fastgpt/service/support/user/auth';
import type { UpdateDatasetCollectionParams } from '@/global/core/api/datasetReq.d';
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
import { getCollectionUpdateTime } from '@fastgpt/service/core/dataset/collection/utils';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
await connectToDatabase();
const { id, parentId, name, metadata = {} } = req.body as UpdateDatasetCollectionParams;
if (!id) {
throw new Error('缺少参数');
}
// 凭证校验
const { userId } = await authUser({ req, authToken: true });
const updateFields: Record<string, any> = {
...(parentId !== undefined && { parentId: parentId || null }),
...(name && { name, updateTime: getCollectionUpdateTime({ name }) })
};
// 将metadata的每个字段添加到updateFields中
for (const [key, value] of Object.entries(metadata)) {
updateFields[`metadata.${key}`] = value;
}
await MongoDatasetCollection.findOneAndUpdate(
{
_id: id,
userId
},
{
$set: updateFields
}
);
jsonRes(res);
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
}
}

View File

@@ -1,9 +1,10 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase } from '@/service/mongo';
import { MongoDataset } from '@fastgpt/core/dataset/schema';
import { authUser } from '@fastgpt/support/user/auth';
import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
import { authUser } from '@fastgpt/service/support/user/auth';
import type { CreateDatasetParams } from '@/global/core/api/datasetReq.d';
import { createDefaultCollection } from './collection/create';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
@@ -23,6 +24,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
type
});
await createDefaultCollection({
datasetId: _id,
userId
});
jsonRes(res, { data: _id });
} catch (err) {
jsonRes(res, {

View File

@@ -1,20 +1,20 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { authUser } from '@fastgpt/support/user/auth';
import { authUser } from '@fastgpt/service/support/user/auth';
import { PgClient } from '@/service/pg';
import { withNextCors } from '@fastgpt/common/tools/nextjs';
import { withNextCors } from '@fastgpt/service/common/middle/cors';
import { PgDatasetTableName } from '@/constants/plugin';
import { connectToDatabase } from '@/service/mongo';
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
await connectToDatabase();
let { dataId } = req.query as {
const { dataId } = req.query as {
dataId: string;
};
if (!dataId) {
throw new Error('缺少参数');
throw new Error('dataId is required');
}
// 凭证校验

View File

@@ -1,30 +1,30 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase } from '@/service/mongo';
import { MongoUser } from '@fastgpt/support/user/schema';
import { authUser } from '@fastgpt/support/user/auth';
import { MongoUser } from '@fastgpt/service/support/user/schema';
import { authUser } from '@fastgpt/service/support/user/auth';
import { PgDatasetTableName } from '@/constants/plugin';
import { findAllChildrenIds } from '../delete';
import QueryStream from 'pg-query-stream';
import { PgClient } from '@/service/pg';
import { addLog } from '@/service/utils/tools';
import { responseWriteController } from '@fastgpt/common/tools/stream';
import { responseWriteController } from '@fastgpt/service/common/response';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
await connectToDatabase();
let { kbId } = req.query as {
kbId: string;
let { datasetId } = req.query as {
datasetId: string;
};
if (!kbId || !global.pgClient) {
if (!datasetId || !global.pgClient) {
throw new Error('缺少参数');
}
// 凭证校验
const { userId } = await authUser({ req, authToken: true });
const exportIds = [kbId, ...(await findAllChildrenIds(kbId))];
const exportIds = [datasetId, ...(await findAllChildrenIds(datasetId))];
const limitMinutesAgo = new Date(
Date.now() - (global.feConfigs?.limit?.exportLimitMinutes || 0) * 60 * 1000
@@ -48,7 +48,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
}
const { rows } = await PgClient.query(
`SELECT count(id) FROM ${PgDatasetTableName} where user_id='${userId}' AND kb_id IN (${exportIds
`SELECT count(id) FROM ${PgDatasetTableName} where user_id='${userId}' AND dataset_id IN (${exportIds
.map((id) => `'${id}'`)
.join(',')})`
);
@@ -67,10 +67,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
res.end('Error connecting to database');
return;
}
if (!client) return;
// create pg select stream
const query = new QueryStream(
`SELECT q, a, source FROM ${PgDatasetTableName} where user_id='${userId}' AND kb_id IN (${exportIds
`SELECT q, a FROM ${PgDatasetTableName} where user_id='${userId}' AND dataset_id IN (${exportIds
.map((id) => `'${id}'`)
.join(',')})`
);
@@ -84,18 +85,18 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
readStream: stream
});
write('index,content,source');
write('index,content');
// parse data every row
stream.on('data', ({ q, a, source }: { q: string; a: string; source?: string }) => {
stream.on('data', ({ q, a }: { q: string; a: string }) => {
if (res.closed) {
return stream.destroy();
}
q = q.replace(/"/g, '""');
a = a.replace(/"/g, '""');
source = source?.replace(/"/g, '""');
// source = source?.replace(/"/g, '""');
write(`\n"${q}","${a || ''}","${source || ''}"`);
write(`\n"${q}","${a || ''}"`);
});
// finish
stream.on('end', async () => {

View File

@@ -1,10 +1,11 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { connectToDatabase } from '@/service/mongo';
import { authUser } from '@fastgpt/support/user/auth';
import { authUser } from '@fastgpt/service/support/user/auth';
import { PgClient } from '@/service/pg';
import { PgDatasetTableName } from '@/constants/plugin';
import type { PgDataItemType } from '@/types/core/dataset/data';
import type { DatasetDataItemType, PgDataItemType } from '@fastgpt/global/core/dataset/type';
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
export type Response = {
id: string;
@@ -26,16 +27,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
// 凭证校验
const { userId } = await authUser({ req, authToken: true });
const where: any = [['user_id', userId], 'AND', ['id', dataId]];
const searchRes = await PgClient.select<PgDataItemType>(PgDatasetTableName, {
fields: ['kb_id', 'id', 'q', 'a', 'source', 'file_id'],
where,
limit: 1
});
jsonRes(res, {
data: searchRes.rows[0]
data: await getDatasetDataById({ userId, id: dataId })
});
} catch (err) {
jsonRes(res, {
@@ -44,3 +37,70 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
});
}
}
export async function getDatasetDataById({
id,
userId
}: {
id: string;
userId: string;
}): Promise<DatasetDataItemType> {
const where: any = [['user_id', userId], 'AND', ['id', id]];
const searchRes = await PgClient.select<PgDataItemType>(PgDatasetTableName, {
fields: ['id', 'q', 'a', 'dataset_id', 'collection_id'],
where,
limit: 1
});
const data = searchRes?.rows?.[0];
if (!data) {
return Promise.reject('Data not found');
}
// find source
const collection = (await getDatasetDataItemInfo({ pgDataList: [data] }))[0];
if (!collection) {
return Promise.reject('Data Collection not found');
}
return {
id: data.id,
q: data.q,
a: data.a,
datasetId: data.dataset_id,
collectionId: data.collection_id,
sourceName: collection.sourceName,
sourceId: collection.sourceId
};
}
export async function getDatasetDataItemInfo({
pgDataList
}: {
pgDataList: PgDataItemType[];
}): Promise<DatasetDataItemType[]> {
const collections = await MongoDatasetCollection.find(
{
_id: { $in: pgDataList.map((item) => item.collection_id) }
},
'_id name datasetId metadata'
).lean();
return pgDataList.map((item) => {
const collection = collections.find(
(collection) => String(collection._id) === item.collection_id
);
return {
id: item.id,
q: item.q,
a: item.a,
datasetId: collection?.datasetId || '',
collectionId: item.collection_id,
sourceName: collection?.name || '',
sourceId: collection?.metadata?.fileId || collection?.metadata?.rawLink
};
});
}

Some files were not shown because too many files have changed in this diff Show More