diff --git a/Dockerfile b/Dockerfile index f7541c892..53720164a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # Install dependencies only when needed FROM node:18.15-alpine AS deps # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. -RUN apk add --no-cache libc6-compat && npm install -g pnpm +RUN apk add libc6-compat && npm install -g pnpm WORKDIR /app ARG name diff --git a/docSite/content/docs/custom-models/chatglm2.md b/docSite/content/docs/custom-models/chatglm2.md index 384a66f9a..d3909dab4 100644 --- a/docSite/content/docs/custom-models/chatglm2.md +++ b/docSite/content/docs/custom-models/chatglm2.md @@ -99,7 +99,7 @@ Authorization 为 sk-aaabbbcccdddeeefffggghhhiiijjjkkk。model 为刚刚在 One ## 接入 FastGPT -修改 config.json 配置文件,在 VectorModels 中加入 chatglm2 模型: +修改 config.json 配置文件,在 ChatModels 中加入 chatglm2 模型: ```json "ChatModels": [ @@ -107,10 +107,11 @@ Authorization 为 sk-aaabbbcccdddeeefffggghhhiiijjjkkk。model 为刚刚在 One { "model": "chatglm2", "name": "chatglm2", - "maxToken": 8000, - "price": 0, - "quoteMaxToken": 4000, - "maxTemperature": 1.2, + "maxContext": 4000, + "maxResponse": 4000, + "quoteMaxToken": 2000, + "maxTemperature": 1, + "vision": false, "defaultSystemChatPrompt": "" } ] diff --git a/docSite/content/docs/development/openapi/dataset.md b/docSite/content/docs/development/openapi/dataset.md index 66a12f106..154428175 100644 --- a/docSite/content/docs/development/openapi/dataset.md +++ b/docSite/content/docs/development/openapi/dataset.md @@ -21,7 +21,9 @@ weight: 563 curl --location --request POST 'https://fastgpt.run/api/support/wallet/bill/createTrainingBill' \ --header 'Authorization: Bearer {{apikey}}' \ --header 'Content-Type: application/json' \ ---data-raw '' +--data-raw '{ + "name": "可选,自定义订单名称,例如:文档训练-fastgpt.docx" +}' ``` **响应结果** diff --git a/docSite/content/docs/installation/docker.md b/docSite/content/docs/installation/docker.md index f7d45bdfb..c25926e6f 100644 --- a/docSite/content/docs/installation/docker.md +++ b/docSite/content/docs/installation/docker.md @@ -86,7 +86,7 @@ curl -O https://raw.githubusercontent.com/labring/FastGPT/main/projects/app/data ## 三、启动容器 -修改`docker-compose.yml`中的`OPENAI_BASE_URL`和`CHAT_API_KEY`即可,对应为 API 的地址和 key。 +修改`docker-compose.yml`中的`OPENAI_BASE_URL`和`CHAT_API_KEY`即可,对应为 API 的地址(别忘记加/v1)和 key。 ```bash # 在 docker-compose.yml 同级目录下执行 diff --git a/packages/global/common/system/types/index.d.ts b/packages/global/common/system/types/index.d.ts index 7715c9328..00f918276 100644 --- a/packages/global/common/system/types/index.d.ts +++ b/packages/global/common/system/types/index.d.ts @@ -2,16 +2,15 @@ export type FeConfigsType = { show_emptyChat?: boolean; show_register?: boolean; show_appStore?: boolean; - show_contact?: boolean; show_git?: boolean; show_pay?: boolean; show_openai_account?: boolean; show_promotion?: boolean; hide_app_flow?: boolean; + concatMd?: string; docUrl?: string; openAPIDocUrl?: string; systemTitle?: string; - authorText?: string; googleClientVerKey?: string; isPlus?: boolean; oauth?: { diff --git a/packages/global/core/ai/model.d.ts b/packages/global/core/ai/model.d.ts index 123d98868..0fb7e90d3 100644 --- a/packages/global/core/ai/model.d.ts +++ b/packages/global/core/ai/model.d.ts @@ -26,6 +26,14 @@ export type VectorModelItemType = { maxToken: number; }; +export type ReRankModelItemType = { + model: string; + name: string; + price: number; + requestUrl?: string; + requestAuth?: string; +}; + export type AudioSpeechModelType = { model: string; name: string; diff --git a/packages/global/core/ai/model.ts b/packages/global/core/ai/model.ts index f074be10c..d3040cec4 100644 --- a/packages/global/core/ai/model.ts +++ b/packages/global/core/ai/model.ts @@ -4,7 +4,8 @@ import type { FunctionModelItemType, VectorModelItemType, AudioSpeechModelType, - WhisperModelType + WhisperModelType, + ReRankModelItemType } from './model.d'; export const defaultChatModels: ChatModelItemType[] = [ @@ -117,6 +118,8 @@ export const defaultVectorModels: VectorModelItemType[] = [ } ]; +export const defaultReRankModels: ReRankModelItemType[] = []; + export const defaultAudioSpeechModels: AudioSpeechModelType[] = [ { model: 'tts-1', diff --git a/packages/global/core/app/type.d.ts b/packages/global/core/app/type.d.ts index 40613ffc0..9851b4703 100644 --- a/packages/global/core/app/type.d.ts +++ b/packages/global/core/app/type.d.ts @@ -4,6 +4,7 @@ import { PermissionTypeEnum } from '../../support/permission/constant'; import type { AIChatModuleProps, DatasetModuleProps } from '../module/node/type.d'; import { VariableInputEnum } from '../module/constants'; import { SelectedDatasetType } from '../module/api'; +import { DatasetSearchModeEnum } from '../dataset/constant'; export interface AppSchema { _id: string; @@ -18,6 +19,7 @@ export interface AppSchema { updateTime: number; modules: ModuleItemType[]; permission: `${PermissionTypeEnum}`; + inited?: boolean; } export type AppListItemType = { @@ -62,7 +64,7 @@ export type AppSimpleEditFormType = { datasets: SelectedDatasetType; similarity: number; limit: number; - rerank: boolean; + searchMode: `${DatasetSearchModeEnum}`; searchEmptyText: string; }; userGuide: { @@ -106,7 +108,7 @@ export type AppSimpleEditConfigTemplateType = { datasets?: boolean; similarity?: boolean; limit?: boolean; - rerank?: boolean; + searchMode: `${DatasetSearchModeEnum}`; searchEmptyText?: boolean; }; userGuide?: { diff --git a/packages/global/core/app/utils.ts b/packages/global/core/app/utils.ts index 0e8d01465..e663ae20b 100644 --- a/packages/global/core/app/utils.ts +++ b/packages/global/core/app/utils.ts @@ -5,6 +5,7 @@ import type { FlowNodeInputItemType } from '../module/node/type.d'; import { getGuideModule, splitGuideModule } from '../module/utils'; import { defaultChatModels } from '../ai/model'; import { ModuleItemType } from '../module/type.d'; +import { DatasetSearchModeEnum } from '../dataset/constant'; export const getDefaultAppForm = (templateId = 'fastgpt-universal'): AppSimpleEditFormType => { const defaultChatModel = defaultChatModels[0]; @@ -25,7 +26,7 @@ export const getDefaultAppForm = (templateId = 'fastgpt-universal'): AppSimpleEd similarity: 0.4, limit: 5, searchEmptyText: '', - rerank: false + searchMode: DatasetSearchModeEnum.embedding }, userGuide: { welcomeText: '', @@ -91,10 +92,9 @@ export const appModules2Form = ({ module.inputs, ModuleInputKeyEnum.datasetLimit ); - defaultAppForm.dataset.rerank = findInputValueByKey( - module.inputs, - ModuleInputKeyEnum.datasetStartReRank - ); + defaultAppForm.dataset.searchMode = + findInputValueByKey(module.inputs, ModuleInputKeyEnum.datasetSearchMode) || + DatasetSearchModeEnum.embedding; // empty text const emptyOutputs = diff --git a/packages/global/core/dataset/constant.ts b/packages/global/core/dataset/constant.ts index 9de474444..2f12a9728 100644 --- a/packages/global/core/dataset/constant.ts +++ b/packages/global/core/dataset/constant.ts @@ -86,4 +86,31 @@ export const TrainingTypeMap = { // } }; +export enum DatasetSearchModeEnum { + embedding = 'embedding', + embeddingReRank = 'embeddingReRank', + embFullTextReRank = 'embFullTextReRank' +} + +export const DatasetSearchModeMap = { + [DatasetSearchModeEnum.embedding]: { + icon: 'core/dataset/modeEmbedding', + title: 'core.dataset.search.mode.embedding', + desc: 'core.dataset.search.mode.embedding desc', + value: DatasetSearchModeEnum.embedding + }, + [DatasetSearchModeEnum.embeddingReRank]: { + icon: 'core/dataset/modeEmbeddingRerank', + title: 'core.dataset.search.mode.embeddingReRank', + desc: 'core.dataset.search.mode.embeddingReRank desc', + value: DatasetSearchModeEnum.embeddingReRank + }, + [DatasetSearchModeEnum.embFullTextReRank]: { + icon: 'core/dataset/modeEmbFTRerank', + title: 'core.dataset.search.mode.embFullTextReRank', + desc: 'core.dataset.search.mode.embFullTextReRank desc', + value: DatasetSearchModeEnum.embFullTextReRank + } +}; + export const FolderAvatarSrc = '/imgs/files/folder.svg'; diff --git a/packages/global/core/module/constants.ts b/packages/global/core/module/constants.ts index b0502ac94..00ec4ddb8 100644 --- a/packages/global/core/module/constants.ts +++ b/packages/global/core/module/constants.ts @@ -61,7 +61,8 @@ export enum ModuleInputKeyEnum { datasetSelectList = 'datasets', datasetSimilarity = 'similarity', datasetLimit = 'limit', - datasetStartReRank = 'rerank', + datasetSearchMode = 'searchMode', + datasetParamsModal = 'datasetParamsModal', // context extract contextExtractInput = 'content', @@ -98,5 +99,6 @@ export enum ModuleOutputKeyEnum { export enum VariableInputEnum { input = 'input', + textarea = 'textarea', select = 'select' } diff --git a/packages/global/core/module/node/constant.ts b/packages/global/core/module/node/constant.ts index 4252322b9..ea2c0c8c1 100644 --- a/packages/global/core/module/node/constant.ts +++ b/packages/global/core/module/node/constant.ts @@ -16,6 +16,8 @@ export enum FlowNodeInputTypeEnum { selectChatModel = 'selectChatModel', // dataset special input selectDataset = 'selectDataset', + selectDatasetParamsModal = 'selectDatasetParamsModal', + hidden = 'hidden' } diff --git a/packages/global/core/module/template/system/classifyQuestion.ts b/packages/global/core/module/template/system/classifyQuestion.ts index d642eddc4..c8eadf0c3 100644 --- a/packages/global/core/module/template/system/classifyQuestion.ts +++ b/packages/global/core/module/template/system/classifyQuestion.ts @@ -17,8 +17,11 @@ export const ClassifyQuestionModule: FlowModuleTemplateType = { flowType: FlowNodeTypeEnum.classifyQuestion, avatar: '/imgs/module/cq.png', name: '问题分类', - intro: - '根据用户的历史记录和当前问题判断该次提问的类型。可以添加多组问题类型,下面是一个模板例子:\n类型1: 打招呼\n类型2: 关于 laf 通用问题\n类型3: 关于 laf 代码问题\n类型4: 其他问题', + intro: `根据用户的历史记录和当前问题判断该次提问的类型。可以添加多组问题类型,下面是一个模板例子: +类型1: 打招呼 +类型2: 关于商品“使用”问题 +类型3: 关于商品“购买”问题 +类型4: 其他问题`, showStatus: true, inputs: [ Input_Template_TFSwitch, diff --git a/packages/global/core/module/template/system/datasetSearch.ts b/packages/global/core/module/template/system/datasetSearch.ts index ffabb1e78..c876b2c1e 100644 --- a/packages/global/core/module/template/system/datasetSearch.ts +++ b/packages/global/core/module/template/system/datasetSearch.ts @@ -12,6 +12,7 @@ import { } from '../../constants'; import { Input_Template_TFSwitch, Input_Template_UserChatInput } from '../input'; import { Output_Template_Finish } from '../output'; +import { DatasetSearchModeEnum } from '../../../dataset/constant'; export const DatasetSearchModule: FlowModuleTemplateType = { id: FlowNodeTypeEnum.datasetSearchNode, @@ -36,7 +37,7 @@ export const DatasetSearchModule: FlowModuleTemplateType = { }, { key: ModuleInputKeyEnum.datasetSimilarity, - type: FlowNodeInputTypeEnum.slider, + type: FlowNodeInputTypeEnum.hidden, label: '最低相关性', value: 0.4, valueType: ModuleDataTypeEnum.number, @@ -52,7 +53,7 @@ export const DatasetSearchModule: FlowModuleTemplateType = { }, { key: ModuleInputKeyEnum.datasetLimit, - type: FlowNodeInputTypeEnum.slider, + type: FlowNodeInputTypeEnum.hidden, label: '单次搜索上限', description: '最多取 n 条记录作为本次问题引用', value: 5, @@ -68,13 +69,20 @@ export const DatasetSearchModule: FlowModuleTemplateType = { showTargetInPlugin: false }, { - key: ModuleInputKeyEnum.datasetStartReRank, - type: FlowNodeInputTypeEnum.switch, - label: '结果重排', - description: '将召回的结果进行进一步重排,可增加召回率', - plusField: true, - value: false, - valueType: ModuleDataTypeEnum.boolean, + key: ModuleInputKeyEnum.datasetSearchMode, + type: FlowNodeInputTypeEnum.hidden, + label: 'core.dataset.search.Mode', + valueType: ModuleDataTypeEnum.string, + showTargetInApp: false, + showTargetInPlugin: false, + value: DatasetSearchModeEnum.embedding + }, + { + key: ModuleInputKeyEnum.datasetParamsModal, + type: FlowNodeInputTypeEnum.selectDatasetParamsModal, + label: '', + connected: false, + valueType: ModuleDataTypeEnum.any, showTargetInApp: false, showTargetInPlugin: false }, diff --git a/packages/global/core/module/template/system/history.ts b/packages/global/core/module/template/system/history.ts index fa4903058..9774de471 100644 --- a/packages/global/core/module/template/system/history.ts +++ b/packages/global/core/module/template/system/history.ts @@ -18,10 +18,12 @@ export const HistoryModule: FlowModuleTemplateType = { key: ModuleInputKeyEnum.historyMaxAmount, type: FlowNodeInputTypeEnum.numberInput, label: '最长记录数', + description: + '该记录数不代表模型可接收这么多的历史记录,具体可接收多少历史记录,取决于模型的能力,通常建议不要超过20条。', value: 6, valueType: ModuleDataTypeEnum.number, min: 0, - max: 50, + max: 100, showTargetInApp: false, showTargetInPlugin: false }, diff --git a/packages/service/core/dataset/data/schema.ts b/packages/service/core/dataset/data/schema.ts index e77a024f8..d79dfbbff 100644 --- a/packages/service/core/dataset/data/schema.ts +++ b/packages/service/core/dataset/data/schema.ts @@ -45,7 +45,7 @@ const DatasetDataSchema = new Schema({ }, fullTextToken: { type: String, - required: true + default: '' }, indexes: { type: [ diff --git a/packages/service/support/permission/controller.ts b/packages/service/support/permission/controller.ts index 1e92419d1..8c03bfbe0 100644 --- a/packages/service/support/permission/controller.ts +++ b/packages/service/support/permission/controller.ts @@ -105,14 +105,13 @@ export async function parseHeaderCert({ }; } // root user - async function parseRootKey(rootKey?: string, userId = '') { + async function parseRootKey(rootKey?: string) { if (!rootKey || !process.env.ROOT_KEY || rootKey !== process.env.ROOT_KEY) { return Promise.reject(ERROR_ENUM.unAuthorization); } - return userId; } - const { cookie, token, apikey, rootkey, userid, authorization } = (req.headers || + const { cookie, token, apikey, rootkey, authorization } = (req.headers || {}) as ReqHeaderAuthType; const { uid, teamId, tmbId, appId, openApiKey, authType } = await (async () => { @@ -129,9 +128,10 @@ export async function parseHeaderCert({ }; } if (authRoot && rootkey) { + await parseRootKey(rootkey); // root user return { - uid: await parseRootKey(rootkey, userid), + uid: '', teamId: '', tmbId: '', appId: '', diff --git a/projects/app/data/config.json b/projects/app/data/config.json index af0f8d168..62c639095 100644 --- a/projects/app/data/config.json +++ b/projects/app/data/config.json @@ -113,6 +113,7 @@ "maxToken": 3000 } ], + "ReRankModels": [], "AudioSpeechModels": [ { "model": "tts-1", diff --git a/projects/app/public/locales/en/common.json b/projects/app/public/locales/en/common.json index e62b94580..245229255 100644 --- a/projects/app/public/locales/en/common.json +++ b/projects/app/public/locales/en/common.json @@ -170,6 +170,7 @@ "Rename Success": "Rename Success", "Request Error": "Request Error", "Require Input": "Required", + "Save": "Save", "Save Failed": "Save Failed", "Save Success": "Save Success", "Search": "Search", @@ -216,11 +217,15 @@ "Prompt": "Prompt" }, "app": { + "App params config": "App Config", "Next Step Guide": "Next step guide", "Question Guide Tip": "At the end of the conversation, three leading questions will be asked.", + "Save and preview": "Save", "Select TTS": "Select TTS", + "Simple Config Tip": "Only basic functions are included. For complex agent functions, use advanced orchestration.", "TTS": "Audio Speech", "TTS Tip": "After this function is enabled, the voice playback function can be used after each conversation. Use of this feature may incur additional charges.", + "Welcome Text": "Welcome Text", "create app": "Create App", "setting": "App Setting", "simple": { @@ -270,6 +275,22 @@ "Ideal chunk length": "Ideal chunk length", "Ideal chunk length Tips": "Segment by end symbol. We recommend that your document should be properly punctuated to ensure that each complete sentence length does not exceed this value \n Chinese document recommended 400~1000\n English document recommended 600~1200" }, + "search": { + "Empty result response": "Empty Response", + "Empty result response Tips": "If you fill in the content, if no suitable content is found, you will directly reply to the content.", + "Min Similarity": "Min Similarity", + "Min Similarity Tips": "The similarity of different index models is different, please use the search test to select the appropriate value", + "Params Setting": "Params Setting", + "Top K": "Top K", + "mode": { + "embFullTextReRank": "Hybrid search ", + "embFullTextReRank desc": "Reordering with a mixture of vector search and full-text search results by Rerank usually works best", + "embedding": "Vector search", + "embedding desc": "Direct vector topk correlation query ", + "embeddingReRank": "Enhanced semantic retrieval ", + "embeddingReRank desc": "Sort using Rerank after overperforming vector topk queries " + } + }, "test": { "Test": "Start", "Test Result": "Results", @@ -327,18 +348,20 @@ }, "variable": { "add option": "Add Option", + "input type": "Text", "key": "Key", "key is required": "variable key is required", "select type": "Select", "text max length": "Max Length", - "text type": "Text", + "textarea type": "Textarea", "variable key is required": "", "variable name": "Name", "variable name is required": "variable name is required", "variable option is required": "Variable option is required", "variable option is value is required": "Variable option is value is required", "variable options": "Options" - } + }, + "variable add option": "Add Option" } }, "dataset": { @@ -412,9 +435,6 @@ "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!", "import csv tip": "Ensure that the CSV is in UTF-8 format; otherwise, garbled characters will be displayed", - "recall": { - "rerank": "Rerank" - }, "test": { "noResult": "Search results are empty" } @@ -674,7 +694,8 @@ "bill": { "Audio Speech": "Audio Speech", "Whisper": "Whisper", - "bill username": "User" + "bill username": "User", + "ReRank": "ReRank" } } } diff --git a/projects/app/public/locales/zh/common.json b/projects/app/public/locales/zh/common.json index a53fd8447..2f8af1f69 100644 --- a/projects/app/public/locales/zh/common.json +++ b/projects/app/public/locales/zh/common.json @@ -170,6 +170,7 @@ "Rename Success": "重命名成功", "Request Error": "请求异常", "Require Input": "必填", + "Save": "保存", "Save Failed": "保存失败", "Save Success": "保存成功", "Search": "搜索", @@ -216,11 +217,15 @@ "Prompt": "提示词" }, "app": { + "App params config": "应用配置", "Next Step Guide": "下一步指引", "Question Guide Tip": "对话结束后,会为生成 3 个引导性问题。", + "Save and preview": "保存并预览", "Select TTS": "选择语音播放模式", + "Simple Config Tip": "仅包含基础功能,复杂 agent 功能请使用高级编排。", "TTS": "语音播报", "TTS Tip": "开启后,每次对话后可使用语音播放功能。使用该功能可能产生额外费用。", + "Welcome Text": "对话开场白", "create app": "创建属于你的 AI 应用", "setting": "应用信息设置", "simple": { @@ -270,6 +275,22 @@ "Ideal chunk length": "理想分块长度", "Ideal chunk length Tips": "按结束符号进行分段。我们建议您的文档应合理的使用标点符号,以确保每个完整的句子长度不要超过该值\n中文文档建议400~1000\n英文文档建议600~1200" }, + "search": { + "Empty result response": "空搜索回复", + "Empty result response Tips": "若填写该内容,没有搜索到合适内容时,将直接回复填写的内容。", + "Min Similarity": "最低相似度", + "Min Similarity Tips": "不同索引模型的相似度有区别,请通过搜索测试来选择合适的数值", + "Params Setting": "搜索参数设置", + "Top K": "单次搜索上限", + "mode": { + "embFullTextReRank": "混合检索", + "embFullTextReRank desc": "使用向量检索与全文检索混合结果进行 Rerank 进行重排,通常效果最佳", + "embedding": "语义检索", + "embedding desc": "直接进行向量 topk 相关性查询", + "embeddingReRank": "增强语义检索", + "embeddingReRank desc": "超额进行向量 topk 查询后再使用 Rerank 进行排序" + } + }, "test": { "Test": "测试", "Test Result": "测试结果", @@ -327,18 +348,20 @@ }, "variable": { "add option": "添加选项", + "input type": "文本", "key": "变量 key", - "key is required": "", + "key is required": "变量key是必须的", "select type": "下拉单选", "text max length": "最大长度", - "text type": "文本", + "textarea type": "段落", "variable key is required": "变量 key 不能为空", "variable name": "变量名", "variable name is required": "变量名不能为空", "variable option is required": "选项不能全空", "variable option is value is required": "选项内容不能为空", "variable options": "选项" - } + }, + "variable add option": "添加选项" } }, "dataset": { @@ -412,9 +435,6 @@ "deleteDatasetTips": "确认删除该知识库?删除后数据无法恢复,请确认!", "deleteFolderTips": "确认删除该文件夹及其包含的所有知识库?删除后数据无法恢复,请确认!", "import csv tip": "请确保CSV为UTF-8格式,否则会乱码", - "recall": { - "rerank": "结果重排" - }, "test": { "noResult": "搜索结果为空" } @@ -674,7 +694,8 @@ "bill": { "Audio Speech": "语音播报", "Whisper": "语音输入", - "bill username": "用户" + "bill username": "用户", + "ReRank": "结果重排" } } } diff --git a/projects/app/public/simpleTemplates/fastgpt-simple.json b/projects/app/public/simpleTemplates/fastgpt-simple.json index 813705f15..65736ae4d 100644 --- a/projects/app/public/simpleTemplates/fastgpt-simple.json +++ b/projects/app/public/simpleTemplates/fastgpt-simple.json @@ -14,7 +14,7 @@ "datasets": true, "similarity": false, "limit": false, - "rerank": false, + "searchMode": "embedding", "searchEmptyText": false }, "userGuide": { diff --git a/projects/app/src/components/ChatBox/QuoteModal.tsx b/projects/app/src/components/ChatBox/QuoteModal.tsx index 58d90f925..95b363430 100644 --- a/projects/app/src/components/ChatBox/QuoteModal.tsx +++ b/projects/app/src/components/ChatBox/QuoteModal.tsx @@ -122,7 +122,7 @@ const QuoteModal = ({ {isPc && ( - + # {item.id} diff --git a/projects/app/src/components/ChatBox/WholeResponseModal.tsx b/projects/app/src/components/ChatBox/WholeResponseModal.tsx index 16397e44d..9ec503293 100644 --- a/projects/app/src/components/ChatBox/WholeResponseModal.tsx +++ b/projects/app/src/components/ChatBox/WholeResponseModal.tsx @@ -116,7 +116,7 @@ const WholeResponseModal = ({ if (!activeModule?.historyPreview) return ''; return activeModule.historyPreview .map((item, i) => `**${item.obj}**\n${item.value}`) - .join('\n---\n'); + .join('\n\n---\n\n'); })()} /> {activeModule.quoteList && activeModule.quoteList.length > 0 && ( diff --git a/projects/app/src/components/ChatBox/index.tsx b/projects/app/src/components/ChatBox/index.tsx index 2942cd6e0..98301138b 100644 --- a/projects/app/src/components/ChatBox/index.tsx +++ b/projects/app/src/components/ChatBox/index.tsx @@ -26,7 +26,8 @@ import { useTheme, BoxProps, FlexProps, - Image + Image, + Textarea } from '@chakra-ui/react'; import { feConfigs } from '@/web/common/system/staticData'; import { eventBus } from '@/web/common/utils/eventbus'; @@ -558,11 +559,23 @@ const ChatBox = ( {item.type === VariableInputEnum.input && ( )} + {item.type === VariableInputEnum.textarea && ( + + + + )} + + + + + + + ); +}; + +export default DatasetParamsModal; diff --git a/projects/app/src/components/core/module/DatasetSelectModal.tsx b/projects/app/src/components/core/module/DatasetSelectModal.tsx index 6976f4de3..9ed61857c 100644 --- a/projects/app/src/components/core/module/DatasetSelectModal.tsx +++ b/projects/app/src/components/core/module/DatasetSelectModal.tsx @@ -22,7 +22,7 @@ import MySlider from '@/components/Slider'; import MyTooltip from '@/components/MyTooltip'; import MyModal from '@/components/MyModal'; import MyIcon from '@/components/Icon'; -import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constant'; +import { DatasetSearchModeEnum, DatasetTypeEnum } from '@fastgpt/global/core/dataset/constant'; import { useTranslation } from 'next-i18next'; import { useDatasetStore } from '@/web/core/dataset/store/dataset'; import { feConfigs } from '@/web/common/system/staticData'; @@ -30,9 +30,6 @@ import DatasetSelectContainer, { useDatasetSelect } from '@/components/core/data import { useLoading } from '@/web/common/hooks/useLoading'; import EmptyTip from '@/components/EmptyTip'; import { AppSimpleEditFormType } from '@fastgpt/global/core/app/type'; -import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants'; - -type DatasetParamsProps = AppSimpleEditFormType['dataset']; export const DatasetSelectModal = ({ isOpen, @@ -222,127 +219,4 @@ export const DatasetSelectModal = ({ ); }; -export const DatasetParamsModal = ({ - searchEmptyText, - limit, - similarity, - rerank, - onClose, - onChange -}: DatasetParamsProps & { onClose: () => void; onChange: (e: DatasetParamsProps) => void }) => { - const [refresh, setRefresh] = useState(false); - const { register, setValue, getValues, handleSubmit } = useForm({ - defaultValues: { - searchEmptyText, - limit, - similarity, - rerank - } - }); - - return ( - - - - {feConfigs?.isPlus && ( - - - 结果重排 - - - - - { - setValue(ModuleInputKeyEnum.datasetStartReRank, e.target.checked); - setRefresh(!refresh); - }} - /> - - )} - - - 相似度 - - - - - { - setValue(ModuleInputKeyEnum.datasetSimilarity, val); - setRefresh(!refresh); - }} - /> - - - - 单次搜索数量 - - - { - setValue(ModuleInputKeyEnum.datasetLimit, val); - setRefresh(!refresh); - }} - /> - - - - - 空搜索回复 - - - - - - - - - - - - - ); -}; - export default DatasetSelectModal; diff --git a/projects/app/src/components/core/module/Flow/components/modules/VariableEdit.tsx b/projects/app/src/components/core/module/Flow/components/modules/VariableEdit.tsx index a7b96ee9c..1a695ce61 100644 --- a/projects/app/src/components/core/module/Flow/components/modules/VariableEdit.tsx +++ b/projects/app/src/components/core/module/Flow/components/modules/VariableEdit.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { Box, Button, @@ -54,13 +54,18 @@ const VariableEdit = ({ const VariableTypeList = [ { - label: t('core.module.variable.text type'), - icon: 'settingLight', + label: t('core.module.variable.input type'), + icon: 'core/app/variable/input', key: VariableInputEnum.input }, + { + label: t('core.module.variable.textarea type'), + icon: 'core/app/variable/textarea', + key: VariableInputEnum.textarea + }, { label: t('core.module.variable.select type'), - icon: 'settingLight', + icon: 'core/app/variable/select', key: VariableInputEnum.select } ]; @@ -94,6 +99,13 @@ const VariableEdit = ({ } }; + const formatVariables = useMemo(() => { + return variables.map((item) => ({ + ...item, + icon: VariableTypeList.find((type) => type.key === item.type)?.icon + })); + }, [variables]); + return ( @@ -114,12 +126,13 @@ const VariableEdit = ({ + {t('common.Add New')} - {variables.length > 0 && ( + {formatVariables.length > 0 && ( + @@ -127,9 +140,12 @@ const VariableEdit = ({ - {variables.map((item, index) => ( + {formatVariables.map((item) => ( - + +
{t('core.module.variable.variable name')} {t('core.module.variable.key')} {t('common.Require Input')}
{item.label} + + {item.label} {item.key} {item.required ? '✔' : ''} @@ -190,18 +206,21 @@ const VariableEdit = ({ {t('core.module.Field Type')} - + {VariableTypeList.map((item) => ( - {item.label} + {item.label} ))} @@ -225,14 +244,14 @@ const VariableEdit = ({ {t('core.module.variable.text max length')} - + @@ -258,16 +277,18 @@ const VariableEdit = ({ })} /> - removeEnums(i)} - /> + {selectEnums.length > 1 && ( + removeEnums(i)} + /> + )} ))} diff --git a/projects/app/src/components/core/module/Flow/components/render/RenderInput.tsx b/projects/app/src/components/core/module/Flow/components/render/RenderInput.tsx index 80d463ee6..c485764ce 100644 --- a/projects/app/src/components/core/module/Flow/components/render/RenderInput.tsx +++ b/projects/app/src/components/core/module/Flow/components/render/RenderInput.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import type { SelectAppItemType } from '@fastgpt/global/core/module/type'; import type { FlowNodeInputItemType } from '@fastgpt/global/core/module/node/type'; import { @@ -36,11 +36,13 @@ import type { SelectedDatasetType } from '@fastgpt/global/core/module/api.d'; import { useQuery } from '@tanstack/react-query'; import type { EditFieldModeType, EditFieldType } from '../modules/FieldEditModal'; import { feConfigs } from '@/web/common/system/staticData'; +import { DatasetSearchModeEnum } from '@fastgpt/global/core/dataset/constant'; const FieldEditModal = dynamic(() => import('../modules/FieldEditModal')); const SelectAppModal = dynamic(() => import('../../SelectAppModal')); const AIChatSettingsModal = dynamic(() => import('../../../AIChatSettingsModal')); const DatasetSelectModal = dynamic(() => import('../../../DatasetSelectModal')); +const DatasetParamsModal = dynamic(() => import('../../../DatasetParamsModal')); export const Label = React.memo(function Label({ moduleId, @@ -232,6 +234,9 @@ const RenderInput = ({ {item.type === FlowNodeInputTypeEnum.selectDataset && ( )} + {item.type === FlowNodeInputTypeEnum.selectDatasetParamsModal && ( + + )} {item.type === FlowNodeInputTypeEnum.custom && CustomComponent[item.key] && ( <>{CustomComponent[item.key]({ ...item })} )} @@ -251,7 +256,7 @@ type RenderProps = { moduleId: string; }; -var NumberInputRender = React.memo(function NumberInputRender({ item, moduleId }: RenderProps) { +const NumberInputRender = React.memo(function NumberInputRender({ item, moduleId }: RenderProps) { return ( { const obj: Record = {}; @@ -440,15 +445,15 @@ var AISetting = React.memo(function AISetting({ inputs = [], moduleId }: RenderP ); }); -var SelectChatModelRender = React.memo(function SelectChatModelRender({ +const SelectChatModelRender = React.memo(function SelectChatModelRender({ inputs = [], item, moduleId }: RenderProps) { const modelList = chatModelList || []; - function onChangeModel(e: string) { - { + const onChangeModel = useCallback( + (e: string) => { onChangeNode({ moduleId, type: 'updateInput', @@ -477,8 +482,9 @@ var SelectChatModelRender = React.memo(function SelectChatModelRender({ value: model.maxResponse / 2 } }); - } - } + }, + [inputs, item, modelList, moduleId] + ); const list = modelList.map((item) => { const priceStr = `(${formatPrice(item.price, 1000)}元/1k Tokens)`; @@ -489,9 +495,11 @@ var SelectChatModelRender = React.memo(function SelectChatModelRender({ }; }); - if (!item.value && list.length > 0) { - onChangeModel(list[0].value); - } + useEffect(() => { + if (!item.value && list.length > 0) { + onChangeModel(list[0].value); + } + }, [item.value, list, onChangeModel]); return ( ); }); + +const SelectDatasetParamsRender = React.memo(function SelectDatasetParamsRender({ + inputs = [], + moduleId +}: RenderProps) { + const { t } = useTranslation(); + const [data, setData] = useState({ + searchMode: DatasetSearchModeEnum.embedding, + limit: 5, + similarity: 0.5 + }); + + const { isOpen, onOpen, onClose } = useDisclosure(); + + useEffect(() => { + inputs.forEach((input) => { + // @ts-ignore + if (data[input.key] !== undefined) { + setData((state) => ({ + ...state, + [input.key]: input.value + })); + } + }); + }, [inputs]); + + return ( + <> + + {isOpen && ( + { + for (let key in e) { + const item = inputs.find((input) => input.key === key); + if (!item) continue; + onChangeNode({ + moduleId, + type: 'updateInput', + key, + value: { + ...item, + //@ts-ignore + value: e[key] + } + }); + } + }} + /> + )} + + ); +}); diff --git a/projects/app/src/components/support/apikey/Table.tsx b/projects/app/src/components/support/apikey/Table.tsx index 63f48c467..2bfd452b6 100644 --- a/projects/app/src/components/support/apikey/Table.tsx +++ b/projects/app/src/components/support/apikey/Table.tsx @@ -82,14 +82,16 @@ const ApiKeyTable = ({ tips, appId }: { tips: string; appId?: string }) => { API 秘钥管理 - - 查看文档 - + {feConfigs.docUrl && ( + + 查看文档 + + )} {tips} diff --git a/projects/app/src/global/common/api/systemRes.d.ts b/projects/app/src/global/common/api/systemRes.d.ts index 69c68d6f4..30a567f17 100644 --- a/projects/app/src/global/common/api/systemRes.d.ts +++ b/projects/app/src/global/common/api/systemRes.d.ts @@ -4,7 +4,8 @@ import type { LLMModelItemType, VectorModelItemType, AudioSpeechModels, - WhisperModelType + WhisperModelType, + ReRankModelItemType } from '@fastgpt/global/core/ai/model.d'; import type { FeConfigsType } from '@fastgpt/global/common/system/types/index.d'; @@ -19,6 +20,7 @@ export type ConfigFileType = { ExtractModels: FunctionModelItemType[]; QGModels: LLMModelItemType[]; VectorModels: VectorModelItemType[]; + ReRankModels: ReRankModelItemType[]; AudioSpeechModels: AudioSpeechModelType[]; WhisperModel: WhisperModelType; }; @@ -29,6 +31,7 @@ export type InitDateResponse = { extractModels: FunctionModelItemType[]; vectorModels: VectorModelItemType[]; audioSpeechModels: AudioSpeechModels[]; + reRankModels: ReRankModelItemType[]; feConfigs: FeConfigsType; priceMd: string; systemVersion: string; diff --git a/projects/app/src/global/core/app/constants.ts b/projects/app/src/global/core/app/constants.ts index 8a844461f..47ca29e51 100644 --- a/projects/app/src/global/core/app/constants.ts +++ b/projects/app/src/global/core/app/constants.ts @@ -1,4 +1,5 @@ import { AppSimpleEditConfigTemplateType } from '@fastgpt/global/core/app/type.d'; +import { DatasetSearchModeEnum } from '@fastgpt/global/core/dataset/constant'; export const SimpleModeTemplate_FastGPT_Universal: AppSimpleEditConfigTemplateType = { id: 'fastgpt-universal', @@ -17,7 +18,7 @@ export const SimpleModeTemplate_FastGPT_Universal: AppSimpleEditConfigTemplateTy datasets: true, similarity: true, limit: true, - rerank: true, + searchMode: DatasetSearchModeEnum.embedding, searchEmptyText: true }, userGuide: { diff --git a/projects/app/src/global/core/dataset/api.d.ts b/projects/app/src/global/core/dataset/api.d.ts index 6c25dbeb8..1c9c0d9be 100644 --- a/projects/app/src/global/core/dataset/api.d.ts +++ b/projects/app/src/global/core/dataset/api.d.ts @@ -1,5 +1,5 @@ import { PushDatasetDataChunkProps } from '@fastgpt/global/core/dataset/api'; -import { TrainingModeEnum } from '@fastgpt/global/core/dataset/constant'; +import { DatasetSearchModeEnum, TrainingModeEnum } from '@fastgpt/global/core/dataset/constant'; import { DatasetDataIndexItemType, SearchDataResponseItemType @@ -43,7 +43,7 @@ export type SearchTestProps = { datasetId: string; text: string; limit?: number; - rerank?: boolean; + searchMode?: `${DatasetSearchModeEnum}`; }; export type SearchTestResponse = { list: SearchDataResponseItemType[]; diff --git a/projects/app/src/pages/_app.tsx b/projects/app/src/pages/_app.tsx index 98a154e61..29dc4ab61 100644 --- a/projects/app/src/pages/_app.tsx +++ b/projects/app/src/pages/_app.tsx @@ -90,13 +90,15 @@ function App({ Component, pageProps }: AppProps) { hiId && localStorage.setItem('inviterId', hiId); }, [hiId]); + const title = feConfigs?.systemTitle || process.env.SYSTEM_NAME || ''; + return ( <> - {feConfigs?.systemTitle || process.env.SYSTEM_NAME || ''} + {title} - {process.env.SYSTEM_NAME || 'FastGPT'}
diff --git a/projects/app/src/pages/api/admin/initv46-2.ts b/projects/app/src/pages/api/admin/initv46-2.ts index 77d79943d..cbb1f83ab 100644 --- a/projects/app/src/pages/api/admin/initv46-2.ts +++ b/projects/app/src/pages/api/admin/initv46-2.ts @@ -152,6 +152,7 @@ async function init(limit: number): Promise { collectionId: data.collection_id, q: data.q, a: data.a, + fullTextToken: '', indexes: [ { defaultIndex: !data.a, diff --git a/projects/app/src/pages/api/admin/initv462-2.ts b/projects/app/src/pages/api/admin/initv462-2.ts new file mode 100644 index 000000000..8212c0d04 --- /dev/null +++ b/projects/app/src/pages/api/admin/initv462-2.ts @@ -0,0 +1,80 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import { jsonRes } from '@fastgpt/service/common/response'; +import { connectToDatabase } from '@/service/mongo'; +import { delay } from '@/utils/tools'; +import { authCert } from '@fastgpt/service/support/permission/auth/common'; +import { MongoApp } from '@fastgpt/service/core/app/schema'; +import { FlowNodeInputTypeEnum, FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant'; +import { DatasetSearchModeEnum } from '@fastgpt/global/core/dataset/constant'; +import { ModuleDataTypeEnum, ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants'; +import { ModuleItemType } from '@fastgpt/global/core/module/type'; + +let success = 0; +/* pg 中的数据搬到 mongo dataset.datas 中,并做映射 */ +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + try { + const { limit = 50 } = req.body as { limit: number }; + await authCert({ req, authRoot: true }); + await connectToDatabase(); + success = 0; + + console.log('total', await MongoApp.countDocuments()); + + await initApp(limit); + + jsonRes(res, { + message: 'success' + }); + } catch (error) { + console.log(error); + + jsonRes(res, { + code: 500, + error + }); + } +} +export async function initApp(limit = 50): Promise { + try { + const apps = await MongoApp.find({ inited: false }).limit(limit); + if (apps.length === 0) return; + + const result = await Promise.allSettled( + apps.map(async (app) => { + // 遍历app的modules,找到 datasetSearch, 如果 rerank=true, searchMode = embFullTextReRank, 否则等于embedding + const modules = JSON.parse(JSON.stringify(app.modules)) as ModuleItemType[]; + modules.forEach((module) => { + if (module.flowType === FlowNodeTypeEnum.datasetSearchNode) { + module.inputs.forEach((input, i) => { + if (input.key === 'rerank') { + const val = !!input.value as boolean; + module.inputs.splice(i, 1, { + key: ModuleInputKeyEnum.datasetSearchMode, + type: FlowNodeInputTypeEnum.hidden, + label: 'core.dataset.search.Mode', + valueType: ModuleDataTypeEnum.string, + showTargetInApp: false, + showTargetInPlugin: false, + value: val + ? DatasetSearchModeEnum.embFullTextReRank + : DatasetSearchModeEnum.embedding + }); + } + }); + } + }); + app.modules = modules; + app.inited = true; + await app.save(); + }) + ); + + success += result.filter((item) => item.status === 'fulfilled').length; + console.log(`success: ${success}`); + return initApp(limit); + } catch (error) { + console.log(error); + await delay(1000); + return initApp(limit); + } +} diff --git a/projects/app/src/pages/api/core/app/form2Modules/fastgpt-simple.ts b/projects/app/src/pages/api/core/app/form2Modules/fastgpt-simple.ts index 5ee44bcf2..ca65f345f 100644 --- a/projects/app/src/pages/api/core/app/form2Modules/fastgpt-simple.ts +++ b/projects/app/src/pages/api/core/app/form2Modules/fastgpt-simple.ts @@ -8,6 +8,7 @@ import type { AppSimpleEditFormType } from '@fastgpt/global/core/app/type.d'; import type { ModuleItemType } from '@fastgpt/global/core/module/type'; import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/module/node/constant'; import { FormatForm2ModulesProps } from '@fastgpt/global/core/app/api'; +import { DatasetSearchModeEnum } from '@fastgpt/global/core/dataset/constant'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -378,7 +379,7 @@ function datasetTemplate({ }, { key: 'similarity', - value: 0.5, + value: 0.4, type: FlowNodeInputTypeEnum.slider, label: '相似度', connected: true @@ -403,13 +404,23 @@ function datasetTemplate({ connected: true }, { - key: 'rerank', - type: FlowNodeInputTypeEnum.switch, - label: '结果重排', - description: '将召回的结果进行进一步重排,可增加召回率', - plusField: true, - connected: true, - value: true + key: 'searchMode', + type: 'hidden', + label: 'core.dataset.search.Mode', + valueType: 'string', + showTargetInApp: false, + showTargetInPlugin: false, + value: DatasetSearchModeEnum.embFullTextReRank, + connected: false + }, + { + key: 'datasetParamsModal', + type: 'selectDatasetParamsModal', + label: '', + connected: false, + valueType: 'any', + showTargetInApp: false, + showTargetInPlugin: false } ], outputs: [ diff --git a/projects/app/src/pages/api/core/app/form2Modules/fastgpt-universal.ts b/projects/app/src/pages/api/core/app/form2Modules/fastgpt-universal.ts index 9e3b0e89c..1f157f974 100644 --- a/projects/app/src/pages/api/core/app/form2Modules/fastgpt-universal.ts +++ b/projects/app/src/pages/api/core/app/form2Modules/fastgpt-universal.ts @@ -312,13 +312,23 @@ function datasetTemplate(formData: AppSimpleEditFormType): ModuleItemType[] { connected: true }, { - key: 'rerank', - type: FlowNodeInputTypeEnum.switch, - label: '结果重排', - description: '将召回的结果进行进一步重排,可增加召回率', - plusField: true, + key: 'searchMode', + type: 'hidden', + label: 'core.dataset.search.Mode', + valueType: 'string', + showTargetInApp: false, + showTargetInPlugin: false, + value: formData.dataset.searchMode, + connected: false + }, + { + key: 'datasetParamsModal', + type: 'selectDatasetParamsModal', + label: '', connected: false, - value: formData.dataset.rerank + valueType: 'any', + showTargetInApp: false, + showTargetInPlugin: false } ], outputs: [ diff --git a/projects/app/src/pages/api/core/app/update.ts b/projects/app/src/pages/api/core/app/update.ts index 7d2f121f3..c2526305a 100644 --- a/projects/app/src/pages/api/core/app/update.ts +++ b/projects/app/src/pages/api/core/app/update.ts @@ -4,7 +4,6 @@ import { connectToDatabase } from '@/service/mongo'; import { MongoApp } from '@fastgpt/service/core/app/schema'; import type { AppUpdateParams } from '@fastgpt/global/core/app/api'; import { authApp } from '@fastgpt/service/support/permission/auth/app'; -import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants'; /* 获取我的模型 */ export default async function handler(req: NextApiRequest, res: NextApiResponse) { diff --git a/projects/app/src/pages/api/core/dataset/exportAll.ts b/projects/app/src/pages/api/core/dataset/exportAll.ts index e05ddc8a4..12b3030a1 100644 --- a/projects/app/src/pages/api/core/dataset/exportAll.ts +++ b/projects/app/src/pages/api/core/dataset/exportAll.ts @@ -67,8 +67,15 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< 'limit.exportKbTime': new Date() }); }); + + cursor.on('error', (err) => { + addLog.error(`export dataset error`, err); + res.status(500); + res.end(); + }); } catch (err) { res.status(500); + addLog.error(`export dataset error`, err); jsonRes(res, { code: 500, error: err diff --git a/projects/app/src/pages/api/core/dataset/searchTest.ts b/projects/app/src/pages/api/core/dataset/searchTest.ts index aae2a02d6..1d4322c09 100644 --- a/projects/app/src/pages/api/core/dataset/searchTest.ts +++ b/projects/app/src/pages/api/core/dataset/searchTest.ts @@ -15,7 +15,7 @@ import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants'; export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) { try { await connectToDatabase(); - const { datasetId, text, limit = 20, rerank } = req.body as SearchTestProps; + const { datasetId, text, limit = 20, searchMode } = req.body as SearchTestProps; if (!datasetId || !text) { throw new Error('缺少参数'); @@ -40,7 +40,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex model: dataset.vectorModel, limit: Math.min(limit, 50), datasetIds: [datasetId], - rerank + searchMode }); // push bill diff --git a/projects/app/src/pages/api/system/getInitData.ts b/projects/app/src/pages/api/system/getInitData.ts index 135515de8..d149540ce 100644 --- a/projects/app/src/pages/api/system/getInitData.ts +++ b/projects/app/src/pages/api/system/getInitData.ts @@ -14,7 +14,8 @@ import { defaultQGModels, defaultVectorModels, defaultAudioSpeechModels, - defaultWhisperModel + defaultWhisperModel, + defaultReRankModels } from '@fastgpt/global/core/ai/model'; import { SimpleModeTemplate_FastGPT_Universal } from '@/global/core/app/constants'; import { getSimpleTemplatesFromPlus } from '@/service/core/app/utils'; @@ -31,11 +32,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) cqModels: global.cqModels, extractModels: global.extractModels, vectorModels: global.vectorModels, - audioSpeechModels: global.audioSpeechModels.map((item) => ({ + reRankModels: global.reRankModels.map((item) => ({ ...item, - baseUrl: undefined, - key: undefined + requestUrl: undefined, + requestAuth: undefined })), + audioSpeechModels: global.audioSpeechModels, priceMd: global.priceMd, systemVersion: global.systemVersion || '0.0.0', simpleModeTemplates: global.simpleModeTemplates @@ -50,12 +52,11 @@ const defaultSystemEnv: SystemEnvType = { }; const defaultFeConfigs: FeConfigsType = { show_emptyChat: true, - show_contact: true, show_git: true, - docUrl: 'https://docs.fastgpt.in', + show_register: false, + docUrl: 'https://doc.fastgpt.in', openAPIDocUrl: 'https://doc.fastgpt.in/docs/development/openapi', systemTitle: 'FastGPT', - authorText: 'Made by FastGPT Team.', limit: { exportLimitMinutes: 0 }, @@ -99,7 +100,14 @@ export function setDefaultData(res?: ConfigFileType) { ? { ...defaultSystemEnv, ...res.SystemParams } : defaultSystemEnv; global.feConfigs = res?.FeConfig - ? { ...defaultFeConfigs, ...res.FeConfig, isPlus: !!res.SystemParams?.pluginBaseUrl } + ? { + concatMd: res?.FeConfig?.show_git + ? '* 项目开源地址: [FastGPT GitHub](https://github.com/labring/FastGPT)\n* 交流群: ![](https://doc.fastgpt.in/wechat-fastgpt.webp)' + : '', + ...defaultFeConfigs, + ...res.FeConfig, + isPlus: !!res.SystemParams?.pluginBaseUrl + } : defaultFeConfigs; global.chatModels = res?.ChatModels || defaultChatModels; @@ -110,6 +118,8 @@ export function setDefaultData(res?: ConfigFileType) { global.vectorModels = res?.VectorModels || defaultVectorModels; + global.reRankModels = res?.ReRankModels || defaultReRankModels; + global.audioSpeechModels = res?.AudioSpeechModels || defaultAudioSpeechModels; global.whisperModel = res?.WhisperModel || defaultWhisperModel; diff --git a/projects/app/src/pages/api/v1/embeddings.ts b/projects/app/src/pages/api/v1/embeddings.ts index 157a8d91c..8ddec5e3d 100644 --- a/projects/app/src/pages/api/v1/embeddings.ts +++ b/projects/app/src/pages/api/v1/embeddings.ts @@ -15,7 +15,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex try { let { input, model, billId } = req.body as Props; await connectToDatabase(); - const { teamId, tmbId } = await authCert({ req, authToken: true }); + const { teamId, tmbId } = await authCert({ req, authToken: true, authApiKey: true }); if (!Array.isArray(input) || typeof input !== 'string') { throw new Error('input is nor array or string'); diff --git a/projects/app/src/pages/api/v1/rerank.ts b/projects/app/src/pages/api/v1/rerank.ts new file mode 100644 index 000000000..03d097254 --- /dev/null +++ b/projects/app/src/pages/api/v1/rerank.ts @@ -0,0 +1,42 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import { jsonRes } from '@fastgpt/service/common/response'; +import { authCert } from '@fastgpt/service/support/permission/auth/common'; +import { withNextCors } from '@fastgpt/service/common/middle/cors'; +import { pushReRankBill } from '@/service/support/wallet/bill/push'; +import { connectToDatabase } from '@/service/mongo'; +import { authTeamBalance } from '@/service/support/permission/auth/bill'; +import { PostReRankProps } from '@fastgpt/global/core/ai/api'; +import { reRankRecall } from '@/service/core/ai/rerank'; + +export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) { + try { + let { query, inputs } = req.body as PostReRankProps; + await connectToDatabase(); + const { teamId, tmbId } = await authCert({ + req, + authApiKey: true + }); + await authTeamBalance(teamId); + + // max 150 length + inputs = inputs.slice(0, 150); + + const result = await reRankRecall({ query, inputs }); + + pushReRankBill({ + teamId, + tmbId, + source: 'api' + }); + + jsonRes(res, { + data: result + }); + } catch (err) { + console.log(err); + jsonRes(res, { + code: 500, + error: err + }); + } +}); diff --git a/projects/app/src/pages/app/detail/components/SimpleEdit/index.tsx b/projects/app/src/pages/app/detail/components/SimpleEdit/index.tsx index 2c3be635f..ac8691e41 100644 --- a/projects/app/src/pages/app/detail/components/SimpleEdit/index.tsx +++ b/projects/app/src/pages/app/detail/components/SimpleEdit/index.tsx @@ -33,7 +33,6 @@ import { AppSchema } from '@fastgpt/global/core/app/type.d'; import { delModelById } from '@/web/core/app/api'; import { useTranslation } from 'next-i18next'; import { getGuideModule } from '@fastgpt/global/core/module/utils'; -import { DatasetParamsModal } from '@/components/core/module/DatasetSelectModal'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { useDatasetStore } from '@/web/core/dataset/store/dataset'; import { useAppStore } from '@/web/core/app/store/useAppStore'; @@ -56,6 +55,7 @@ import VariableEdit from '@/components/core/module/Flow/components/modules/Varia const InfoModal = dynamic(() => import('../InfoModal')); const DatasetSelectModal = dynamic(() => import('@/components/core/module/DatasetSelectModal')); +const DatasetParamsModal = dynamic(() => import('@/components/core/module/DatasetParamsModal')); const AIChatSettingsModal = dynamic(() => import('@/components/core/module/AIChatSettingsModal')); function ConfigForm({ @@ -72,7 +72,6 @@ function ConfigForm({ const { appDetail, updateAppDetail } = useAppStore(); const { loadAllDatasets, allDatasets } = useDatasetStore(); const { isPc } = useSystemStore(); - const [editVariable, setEditVariable] = useState(); const [refresh, setRefresh] = useState(false); const { register, setValue, getValues, reset, handleSubmit, control } = @@ -197,8 +196,8 @@ function ConfigForm({ })} > - 应用配置 - + {t('core.app.App params config')} + @@ -215,7 +214,7 @@ function ConfigForm({ } }} > - {isPc ? '保存并预览' : '保存'} + {isPc ? t('core.app.Save and preview') : t('common.Save')} @@ -248,7 +247,7 @@ function ConfigForm({ {''} - 对话开场白 + {t('core.app.Welcome Text')} @@ -351,7 +350,7 @@ function ConfigForm({ )} {(selectSimpleTemplate.systemForm.dataset.limit || - selectSimpleTemplate.systemForm.dataset.rerank || + selectSimpleTemplate.systemForm.dataset.searchMode || selectSimpleTemplate.systemForm.dataset.searchEmptyText || selectSimpleTemplate.systemForm.dataset.similarity) && ( @@ -459,7 +458,7 @@ function ConfigForm({ { + onSuccess={(e) => { setValue('dataset', { ...getValues('dataset'), ...e diff --git a/projects/app/src/pages/app/list/index.tsx b/projects/app/src/pages/app/list/index.tsx index 2bcaea6bf..a8cecb55b 100644 --- a/projects/app/src/pages/app/list/index.tsx +++ b/projects/app/src/pages/app/list/index.tsx @@ -66,12 +66,12 @@ const MyApps = () => { ); /* 加载模型 */ - useQuery(['loadApps'], () => loadMyApps(true), { + const { isFetching } = useQuery(['loadApps'], () => loadMyApps(true), { refetchOnMount: true }); return ( - + {''} diff --git a/projects/app/src/pages/components/Navbar.tsx b/projects/app/src/pages/components/Navbar.tsx index 68fc51a7d..92a993a0d 100644 --- a/projects/app/src/pages/components/Navbar.tsx +++ b/projects/app/src/pages/components/Navbar.tsx @@ -20,7 +20,7 @@ const Navbar = () => { const { isOpen: isOpenMenu, onOpen: onOpenMenu, onClose: onCloseMenu } = useDisclosure(); const { isPc } = useSystemStore(); const menuList = [ - ...(feConfigs?.show_contact + ...(feConfigs?.concatMd ? [ { label: t('home.Commercial'), diff --git a/projects/app/src/pages/dataset/detail/components/Import/Csv.tsx b/projects/app/src/pages/dataset/detail/components/Import/Csv.tsx index 9ff7a8a39..76ee37018 100644 --- a/projects/app/src/pages/dataset/detail/components/Import/Csv.tsx +++ b/projects/app/src/pages/dataset/detail/components/Import/Csv.tsx @@ -5,7 +5,10 @@ import { useImportStore, SelectorContainer, PreviewFileOrChunk } from './Provide import { useTranslation } from 'next-i18next'; const fileExtension = '.csv'; -const csvTemplate = `index,content\n"被索引的内容","对应的答案。CSV 中请注意内容不能包含双引号,双引号是列分割符号"\n"什么是 laf","laf 是一个云函数开发平台……",""\n"什么是 sealos","Sealos 是以 kubernetes 为内核的云操作系统发行版,可以……"`; +const csvTemplate = `index,content +"必填内容","可选内容。CSV 中请注意内容不能包含双引号,双引号是列分割符号" +"结合人工智能的演进历程,AIGC的发展大致可以分为三个阶段,即:早期萌芽阶段(20世纪50年代至90年代中期)、沉淀积累阶段(20世纪90年代中期至21世纪10年代中期),以及快速发展展阶段(21世纪10年代中期至今)。","" +"AIGC发展分为几个阶段?","早期萌芽阶段(20世纪50年代至90年代中期)、沉淀积累阶段(20世纪90年代中期至21世纪10年代中期)、快速发展展阶段(21世纪10年代中期至今)"`; const CsvImport = () => { const { t } = useTranslation(); diff --git a/projects/app/src/pages/dataset/detail/components/Test.tsx b/projects/app/src/pages/dataset/detail/components/Test.tsx index 9224d41d3..69a5d9c3a 100644 --- a/projects/app/src/pages/dataset/detail/components/Test.tsx +++ b/projects/app/src/pages/dataset/detail/components/Test.tsx @@ -1,5 +1,15 @@ import React, { useEffect, useMemo, useState } from 'react'; -import { Box, Textarea, Button, Flex, useTheme, Grid, Progress, Switch } from '@chakra-ui/react'; +import { + Box, + Textarea, + Button, + Flex, + useTheme, + Grid, + Progress, + Switch, + useDisclosure +} from '@chakra-ui/react'; import { useDatasetStore } from '@/web/core/dataset/store/dataset'; import { useSearchTestStore, SearchTestStoreItemType } from '@/web/core/dataset/store/searchTest'; import { getDatasetDataItemById, postSearchText } from '@/web/core/dataset/api'; @@ -13,12 +23,14 @@ import { useToast } from '@/web/common/hooks/useToast'; import { customAlphabet } from 'nanoid'; import MyTooltip from '@/components/MyTooltip'; import { QuestionOutlineIcon } from '@chakra-ui/icons'; -import { SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type'; import { useTranslation } from 'next-i18next'; -import { feConfigs } from '@/web/common/system/staticData'; -import { SearchTestResponse } from '../../../../global/core/dataset/api'; +import { SearchTestResponse } from '@/global/core/dataset/api'; +import { DatasetSearchModeEnum, DatasetSearchModeMap } from '@fastgpt/global/core/dataset/constant'; +import dynamic from 'next/dynamic'; const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12); +const DatasetParamsModal = dynamic(() => import('@/components/core/module/DatasetParamsModal')); + const Test = ({ datasetId }: { datasetId: string }) => { const { t } = useTranslation(); const theme = useTheme(); @@ -30,15 +42,24 @@ const Test = ({ datasetId }: { datasetId: string }) => { const [inputText, setInputText] = useState(''); const [datasetTestItem, setDatasetTestItem] = useState(); const [editInputData, setEditInputData] = useState(); - const [rerank, setRerank] = useState(false); + const [searchMode, setSearchMode] = useState<`${DatasetSearchModeEnum}`>( + DatasetSearchModeEnum.embedding + ); + const searchModeData = DatasetSearchModeMap[searchMode]; - const kbTestHistory = useMemo( + const { + isOpen: isOpenSelectMode, + onOpen: onOpenSelectMode, + onClose: onCloseSelectMode + } = useDisclosure(); + + const testHistories = useMemo( () => datasetTestList.filter((item) => item.datasetId === datasetId), [datasetId, datasetTestList] ); const { mutate, isLoading } = useRequest({ - mutationFn: () => postSearchText({ datasetId, text: inputText.trim(), rerank, limit: 30 }), + mutationFn: () => postSearchText({ datasetId, text: inputText.trim(), searchMode, limit: 30 }), onSuccess(res: SearchTestResponse) { if (!res || res.list.length === 0) { return toast({ @@ -71,6 +92,7 @@ const Test = ({ datasetId }: { datasetId: string }) => { return ( + {/* input */} { {t('core.dataset.test.Test Text')} - {feConfigs?.isPlus && ( - - {t('dataset.recall.rerank')} - setRerank(e.target.checked)} /> - - )} +