diff --git a/.vscode/settings.json b/.vscode/settings.json index bb587e39c20..c1d91e5cbf4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,6 +10,6 @@ "i18n-ally.keystyle": "nested", "i18n-ally.sortKeys": true, "i18n-ally.keepFulfilled": true, - "i18n-ally.sourceLanguage": "zh", // 根据此语言文件翻译其他语言文件的变量和内容 + "i18n-ally.sourceLanguage": "en", // 根据此语言文件翻译其他语言文件的变量和内容 "i18n-ally.displayLanguage": "en", // 显示语言 } \ No newline at end of file diff --git a/docSite/content/docs/course/data_search.md b/docSite/content/docs/course/data_search.md index 64fcc5bd2e0..baa74cd13ce 100644 --- a/docSite/content/docs/course/data_search.md +++ b/docSite/content/docs/course/data_search.md @@ -56,7 +56,7 @@ FastGPT 采用了`PostgresSQL`的`PG Vector`插件作为向量检索器,索引 ### 检索方案 -1. 通过`问题补全`实现指代消除和问题扩展,从而增加连续对话的检索能力以及语义丰富度。 +1. 通过`问题优化`实现指代消除和问题扩展,从而增加连续对话的检索能力以及语义丰富度。 2. 通过`Concat query`来增加`Rerank`连续对话的时,排序的准确性。 3. 通过`RRF`合并方式,综合多个渠道的检索效果。 4. 通过`Rerank`来二次排序,提高精度。 @@ -97,7 +97,7 @@ FastGPT 采用了`PostgresSQL`的`PG Vector`插件作为向量检索器,索引 #### 结果重排 -利用`ReRank`模型对搜索结果进行重排,绝大多数情况下,可以有效提高搜索结果的准确率。不过,重排模型与问题的完整度(主谓语齐全)有一些关系,通常会先走问题补全后再进行搜索-重排。重排后可以得到一个`0-1`的得分,代表着搜索内容与问题的相关度,该分数通常比向量的得分更加精确,可以根据得分进行过滤。 +利用`ReRank`模型对搜索结果进行重排,绝大多数情况下,可以有效提高搜索结果的准确率。不过,重排模型与问题的完整度(主谓语齐全)有一些关系,通常会先走问题优化后再进行搜索-重排。重排后可以得到一个`0-1`的得分,代表着搜索内容与问题的相关度,该分数通常比向量的得分更加精确,可以根据得分进行过滤。 FastGPT 会使用 `RRF` 对重排结果、向量搜索结果、全文检索结果进行合并,得到最终的搜索结果。 @@ -115,7 +115,7 @@ FastGPT 会使用 `RRF` 对重排结果、向量搜索结果、全文检索结 该值仅在`语义检索`或使用`结果重排`时生效。 -### 问题补全 +### 问题优化 #### 背景 @@ -125,7 +125,7 @@ FastGPT 会使用 `RRF` 对重排结果、向量搜索结果、全文检索结 ![](/imgs/coreferenceResolution2.jpg) -用户在提问“第二点是什么”的时候,只会去知识库里查找“第二点是什么”,压根查不到内容。实际上需要查询的是“QA结构是什么”。因此我们需要引入一个【问题补全】模块,来对用户当前的问题进行补全,从而使得知识库搜索能够搜索到合适的内容。使用补全后效果如下: +用户在提问“第二点是什么”的时候,只会去知识库里查找“第二点是什么”,压根查不到内容。实际上需要查询的是“QA结构是什么”。因此我们需要引入一个【问题优化】模块,来对用户当前的问题进行补全,从而使得知识库搜索能够搜索到合适的内容。使用补全后效果如下: ![](/imgs/coreferenceResolution3.jpg) diff --git a/docSite/content/docs/development/configuration.md b/docSite/content/docs/development/configuration.md index 93bc6257936..60eafc8edd3 100644 --- a/docSite/content/docs/development/configuration.md +++ b/docSite/content/docs/development/configuration.md @@ -13,163 +13,7 @@ weight: 708 这个配置文件中包含了系统级参数、AI 对话的模型、function 模型等…… -## 4.6.8 以前版本完整配置参数 - -**使用时,请务必去除注释!** - -以下配置适用于V4.6.6-alpha版本以后 - -```json -{ - "systemEnv": { - "vectorMaxProcess": 15, // 向量生成最大进程,结合数据库性能和 key 来设置 - "qaMaxProcess": 15, // QA 生成最大进程,结合数据库性能和 key 来设置 - "pgHNSWEfSearch": 100 // pg vector 索引参数,越大精度高但速度慢 - }, - "chatModels": [ // 对话模型 - { - "model": "gpt-3.5-turbo-1106", - "name": "GPT35-1106", - "inputPrice": 0, // 输入价格。 xx元/1k tokens - "outputPrice": 0, // 输出价格。 xx元/1k tokens - "maxContext": 16000, // 最大上下文长度 - "maxResponse": 4000, // 最大回复长度 - "quoteMaxToken": 2000, // 最大引用内容长度 - "maxTemperature": 1.2, // 最大温度值 - "censor": false, // 是否开启敏感词过滤(商业版) - "vision": false, // 支持图片输入 - "defaultSystemChatPrompt": "" - }, - { - "model": "gpt-3.5-turbo-16k", - "name": "GPT35-16k", - "maxContext": 16000, - "maxResponse": 16000, - "inputPrice": 0, - "outputPrice": 0, - "quoteMaxToken": 8000, - "maxTemperature": 1.2, - "censor": false, - "vision": false, - "defaultSystemChatPrompt": "" - }, - { - "model": "gpt-4", - "name": "GPT4-8k", - "maxContext": 8000, - "maxResponse": 8000, - "inputPrice": 0, - "outputPrice": 0, - "quoteMaxToken": 4000, - "maxTemperature": 1.2, - "censor": false, - "vision": false, - "defaultSystemChatPrompt": "" - }, - { - "model": "gpt-4-vision-preview", - "name": "GPT4-Vision", - "maxContext": 128000, - "maxResponse": 4000, - "inputPrice": 0, - "outputPrice": 0, - "quoteMaxToken": 100000, - "maxTemperature": 1.2, - "censor": false, - "vision": true, - "defaultSystemChatPrompt": "" - } - ], - "qaModels": [ // QA 生成模型 - { - "model": "gpt-3.5-turbo-16k", - "name": "GPT35-16k", - "maxContext": 16000, - "maxResponse": 16000, - "inputPrice": 0, - "outputPrice": 0 - } - ], - "cqModels": [ // 问题分类模型 - { - "model": "gpt-3.5-turbo-1106", - "name": "GPT35-1106", - "maxContext": 16000, - "maxResponse": 4000, - "inputPrice": 0, - "outputPrice": 0, - "toolChoice": true, // 是否支持openai的 toolChoice, 不支持的模型需要设置为 false,会走提示词生成 - "functionPrompt": "" - }, - { - "model": "gpt-4", - "name": "GPT4-8k", - "maxContext": 8000, - "maxResponse": 8000, - "inputPrice": 0, - "outputPrice": 0, - "toolChoice": true, - "functionPrompt": "" - } - ], - "extractModels": [ // 内容提取模型 - { - "model": "gpt-3.5-turbo-1106", - "name": "GPT35-1106", - "maxContext": 16000, - "maxResponse": 4000, - "inputPrice": 0, - "outputPrice": 0, - "toolChoice": true, - "functionPrompt": "" - } - ], - "qgModels": [ // 生成下一步指引 - { - "model": "gpt-3.5-turbo-1106", - "name": "GPT35-1106", - "maxContext": 1600, - "maxResponse": 4000, - "inputPrice": 0, - "outputPrice": 0 - } - ], - "vectorModels": [ // 向量模型 - { - "model": "text-embedding-ada-002", - "name": "Embedding-2", - "inputPrice": 0, - "defaultToken": 700, - "maxToken": 3000 - } - ], - "reRankModels": [], // 重排模型,暂时填空数组 - "audioSpeechModels": [ - { - "model": "tts-1", - "name": "OpenAI TTS1", - "inputPrice": 0, - "baseUrl": "", - "key": "", - "voices": [ - { "label": "Alloy", "value": "alloy", "bufferId": "openai-Alloy" }, - { "label": "Echo", "value": "echo", "bufferId": "openai-Echo" }, - { "label": "Fable", "value": "fable", "bufferId": "openai-Fable" }, - { "label": "Onyx", "value": "onyx", "bufferId": "openai-Onyx" }, - { "label": "Nova", "value": "nova", "bufferId": "openai-Nova" }, - { "label": "Shimmer", "value": "shimmer", "bufferId": "openai-Shimmer" } - ] - } - ], - "whisperModel": { - "model": "whisper-1", - "name": "Whisper1", - "inputPrice": 0 - } -} -``` - -## 4.6.8 新配置文件 +## 4.6.8+ 版本新配置文件 llm模型全部合并 @@ -189,11 +33,10 @@ llm模型全部合并 "maxResponse": 4000, // 最大回复 "quoteMaxToken": 13000, // 最大引用内容 "maxTemperature": 1.2, // 最大温度 - "inputPrice": 0, - "outputPrice": 0, + "charsPointsPrice": 0, "censor": false, "vision": false, // 是否支持图片输入 - "datasetProcess": false, // 是否设置为知识库处理模型 + "datasetProcess": false, // 是否设置为知识库处理模型(QA),务必保证至少有一个为true,否则知识库会报错 "toolChoice": true, // 是否支持工具选择 "functionCall": false, // 是否支持函数调用 "customCQPrompt": "", // 自定义文本分类提示词(不支持工具和函数调用的模型 @@ -208,8 +51,7 @@ llm模型全部合并 "maxResponse": 16000, "quoteMaxToken": 13000, "maxTemperature": 1.2, - "inputPrice": 0, - "outputPrice": 0, + "charsPointsPrice": 0, "censor": false, "vision": false, "datasetProcess": true, @@ -227,8 +69,7 @@ llm模型全部合并 "maxResponse": 4000, "quoteMaxToken": 100000, "maxTemperature": 1.2, - "inputPrice": 0, - "outputPrice": 0, + "charsPointsPrice": 0, "censor": false, "vision": false, "datasetProcess": false, @@ -246,10 +87,9 @@ llm模型全部合并 "maxResponse": 4000, "quoteMaxToken": 100000, "maxTemperature": 1.2, - "inputPrice": 0, - "outputPrice": 0, + "charsPointsPrice": 0, "censor": false, - "vision": false, + "vision": true, "datasetProcess": false, "toolChoice": true, "functionCall": false, @@ -263,8 +103,7 @@ llm模型全部合并 { "model": "text-embedding-ada-002", "name": "Embedding-2", - "inputPrice": 0, - "outputPrice": 0, + "charsPointsPrice": 0, "defaultToken": 700, "maxToken": 3000, "weight": 100, @@ -276,8 +115,7 @@ llm模型全部合并 { "model": "tts-1", "name": "OpenAI TTS1", - "inputPrice": 0, - "outputPrice": 0, + "charsPointsPrice": 0, "voices": [ { "label": "Alloy", "value": "alloy", "bufferId": "openai-Alloy" }, { "label": "Echo", "value": "echo", "bufferId": "openai-Echo" }, @@ -291,8 +129,7 @@ llm模型全部合并 "whisperModel": { "model": "whisper-1", "name": "Whisper1", - "inputPrice": 0, - "outputPrice": 0 + "charsPointsPrice": 0 } } ``` @@ -313,7 +150,7 @@ llm模型全部合并 { "model": "bge-reranker-base", // 随意 "name": "检索重排-base", // 随意 - "inputPrice": 0, + "charsPointsPrice": 0, "requestUrl": "{{host}}/api/v1/rerank", "requestAuth": "安全凭证,已自动补 Bearer" } diff --git a/docSite/content/docs/development/docker.md b/docSite/content/docs/development/docker.md index 8eaed68ebd8..b4c95b613b5 100644 --- a/docSite/content/docs/development/docker.md +++ b/docSite/content/docs/development/docker.md @@ -110,6 +110,7 @@ curl -O https://raw.githubusercontent.com/labring/FastGPT/main/projects/app/data cd 项目目录 # 创建 mongo 密钥 openssl rand -base64 756 > ./mongodb.key +# 600不行可以用chmod 999 chmod 600 ./mongodb.key chown 999:root ./mongodb.key # 启动容器 diff --git a/docSite/content/docs/development/one-api.md b/docSite/content/docs/development/one-api.md index fe0a98cbd72..ce597d43236 100644 --- a/docSite/content/docs/development/one-api.md +++ b/docSite/content/docs/development/one-api.md @@ -116,8 +116,7 @@ CHAT_API_KEY=sk-xxxxxx "maxResponse": 4000, // 最大回复 "quoteMaxToken": 13000, // 最大引用内容 "maxTemperature": 1.2, // 最大温度 - "inputPrice": 0, - "outputPrice": 0, + "charsPointsPrice": 0, "censor": false, "vision": false, // 是否支持图片输入 "datasetProcess": false, // 是否设置为知识库处理模型 diff --git a/docSite/content/docs/development/openapi/dataset.md b/docSite/content/docs/development/openapi/dataset.md index 8fe90fb7d4a..2de56204117 100644 --- a/docSite/content/docs/development/openapi/dataset.md +++ b/docSite/content/docs/development/openapi/dataset.md @@ -13,12 +13,25 @@ weight: 853 -## 创建训练订单 +## 创建训练订单(4.6.9地址发生改动) {{< tabs tabTotal="2" >}} {{< tab tabName="请求示例" >}} {{< markdownify >}} +**新例子** + +```bash +curl --location --request POST 'https://api.fastgpt.in/api/support/wallet/usage/createTrainingUsage' \ +--header 'Authorization: Bearer {{apikey}}' \ +--header 'Content-Type: application/json' \ +--data-raw '{ + "name": "可选,自定义订单名称,例如:文档训练-fastgpt.docx" +}' +``` + +**x例子** + ```bash curl --location --request POST 'https://api.fastgpt.in/api/support/wallet/bill/createTrainingBill' \ --header 'Authorization: Bearer {{apikey}}' \ @@ -154,7 +167,7 @@ curl --location --request GET 'http://localhost:3000/api/core/dataset/list?paren "vectorModel": { "model": "text-embedding-ada-002", "name": "Embedding-2", - "inputPrice": 0, + "charsPointsPrice": 0, "defaultToken": 512, "maxToken": 8000, "weight": 100 @@ -213,7 +226,7 @@ curl --location --request GET 'http://localhost:3000/api/core/dataset/detail?id= "vectorModel": { "model": "text-embedding-ada-002", "name": "Embedding-2", - "inputPrice": 0, + "charsPointsPrice": 0, "defaultToken": 512, "maxToken": 8000, "weight": 100 @@ -223,8 +236,7 @@ curl --location --request GET 'http://localhost:3000/api/core/dataset/detail?id= "name": "FastAI-16k", "maxContext": 16000, "maxResponse": 16000, - "inputPrice": 0, - "outputPrice": 0 + "charsPointsPrice": 0 }, "intro": "", "permission": "private", @@ -800,6 +812,33 @@ curl --location --request DELETE 'http://localhost:3000/api/core/dataset/collect ## 数据 +### 数据的结构 + +**Data结构** + +| 字段 | 类型 | 说明 | 必填 | +| --- | --- | --- | --- | +| teamId | String | 团队ID | ✅ | +| tmbId | String | 成员ID | ✅ | +| datasetId | String | 知识库ID | ✅ | +| collectionId | String | 集合ID | ✅ | +| q | String | 主要数据 | ✅ | +| a | String | 辅助数据 | ✖ | +| fullTextToken | String | 分词 | ✖ | +| indexes | Index[] | 向量索引 | ✅ | +| updateTime | Date | 更新时间 | ✅ | +| chunkIndex | Number | 分块下表 | ✖ | + +**Index结构** + +每组数据的自定义索引最多5个 + +| 字段 | 类型 | 说明 | 必填 | +| --- | --- | --- | --- | +| defaultIndex | Boolean | 是否为默认索引 | ✅ | +| dataId | String | 关联的向量ID | ✅ | +| text | String | 文本内容 | ✅ | + ### 为集合批量添加添加数据 注意,每次最多推送 200 组数据。 @@ -825,11 +864,14 @@ curl --location --request POST 'https://api.fastgpt.in/api/core/dataset/data/pus { "q": "你会什么?", "a": "我什么都会", - "indexes": [{ - "defaultIndex": false, - "type":"custom", - "text":"自定义索引,不使用默认索引" - }] + "indexes": [ + { + "text":"自定义索引1" + }, + { + "text":"自定义索引2" + } + ] } ] }' @@ -850,7 +892,7 @@ curl --location --request POST 'https://api.fastgpt.in/api/core/dataset/data/pus - data:(具体数据) - q: 主要数据(必填) - a: 辅助数据(选填) - - indexes: 自定义索引(选填),不传入则默认使用q和a构建索引。也可以传入 + - indexes: 自定义索引(选填)。可以不传或者传空数组,默认都会使用q和a组成一个索引。 {{% /alert %}} {{< /markdownify >}} @@ -866,7 +908,6 @@ curl --location --request POST 'https://api.fastgpt.in/api/core/dataset/data/pus "data": { "insertLen": 1, // 最终插入成功的数量 "overToken": [], // 超出 token 的 - "repeat": [], // 重复的数量 "error": [] // 其他错误 } @@ -1050,7 +1091,16 @@ curl --location --request PUT 'http://localhost:3000/api/core/dataset/data/updat "id":"65abd4b29d1448617cba61db", "q":"测试111", "a":"sss", - "indexes":[] + "indexes":[ + { + "dataId": "xxx", + "defaultIndex":false, + "text":"自定义索引1" + }, + { + "text":"修改后的自定义索引2。(会删除原来的自定义索引2,并插入新的自定义索引2)" + } + ] }' ``` @@ -1064,7 +1114,7 @@ curl --location --request PUT 'http://localhost:3000/api/core/dataset/data/updat - id: 数据的id - q: 主要数据(选填) - a: 辅助数据(选填) -- indexes: 自定义索引(选填),类型参考`为集合批量添加添加数据`,建议直接不传。更新q,a后,如果有默认索引,则会直接更新默认索引。 +- indexes: 自定义索引(选填),类型参考`为集合批量添加添加数据`。如果创建时候有自定义索引, {{% /alert %}} {{< /markdownify >}} diff --git a/docSite/content/docs/development/openapi/share.md b/docSite/content/docs/development/openapi/share.md index 4040b307733..8ae7c9588aa 100644 --- a/docSite/content/docs/development/openapi/share.md +++ b/docSite/content/docs/development/openapi/share.md @@ -169,8 +169,6 @@ curl --location --request POST '{{host}}/shareAuth/start' \ 响应值与[chat 接口格式相同](/docs/development/openapi/chat/#响应),仅多了一个`token`。 -可以重点关注`responseData`里的`price`值,`price`与实际价格的倍率为`100000`,即 100000=1元。 - ```bash curl --location --request POST '{{host}}/shareAuth/finish' \ --header 'Content-Type: application/json' \ @@ -178,72 +176,117 @@ curl --location --request POST '{{host}}/shareAuth/finish' \ "token": "{{authToken}}", "responseData": [ { - "moduleName": "KB Search", - "price": 1.2000000000000002, - "model": "Embedding-2", - "tokens": 6, - "similarity": 0.61, - "limit": 3 + "moduleName": "core.module.template.Dataset search", + "moduleType": "datasetSearchNode", + "totalPoints": 1.5278, + "query": "导演是谁\n《铃芽之旅》的导演是谁?\n这部电影的导演是谁?\n谁是《铃芽之旅》的导演?", + "model": "Embedding-2(旧版,不推荐使用)", + "charsLength": 1524, + "similarity": 0.83, + "limit": 400, + "searchMode": "embedding", + "searchUsingReRank": false, + "extensionModel": "FastAI-4k", + "extensionResult": "《铃芽之旅》的导演是谁?\n这部电影的导演是谁?\n谁是《铃芽之旅》的导演?", + "runningTime": 2.15 }, { - "moduleName": "AI Chat", - "price": 454.5, + "moduleName": "AI 对话", + "moduleType": "chatNode", + "totalPoints": 0.593, "model": "FastAI-4k", - "tokens": 303, - "question": "导演是谁", - "answer": "电影《铃芽之旅》的导演是新海诚。", - "maxToken": 2050, + "charsLength": 593, + "query": "导演是谁", + "maxToken": 2000, "quoteList": [ { - "dataset_id": "646627f4f7b896cfd8910e38", - "id": "8099", - "q": "本作的主人公是谁?", - "a": "本作的主人公是名叫铃芽的少女。", - "source": "手动修改" - }, - { - "dataset_id": "646627f4f7b896cfd8910e38", - "id": "8686", - "q": "电影《铃芽之旅》男主角是谁?", - "a": "电影《铃芽之旅》男主角是宗像草太,由松村北斗配音。", - "source": "" - }, - { - "dataset_id": "646627f4f7b896cfd8910e38", - "id": "19339", - "q": "电影《铃芽之旅》的导演是谁?22", + "id": "65bb346a53698398479a8854", + "q": "导演是谁?", "a": "电影《铃芽之旅》的导演是新海诚。", - "source": "手动修改" + "chunkIndex": 0, + "datasetId": "65af9b947916ae0e47c834d2", + "collectionId": "65bb345c53698398479a868f", + "sourceName": "dataset - 2024-01-23T151114.198.csv", + "sourceId": "65bb345b53698398479a868d", + "score": [ + { + "type": "embedding", + "value": 0.9377183318138123, + "index": 0 + }, + { + "type": "rrf", + "value": 0.06557377049180328, + "index": 0 + } + ] } ], - "completeMessages": [ - { - "obj": "System", - "value": "下面是知识库内容:\n1. [本作的主人公是谁?\n本作的主人公是名叫铃芽的少女。]\n2. [电影《铃芽之旅》男主角是谁?\n电影《铃芽之旅》男主角是宗像草太,由松村北斗配音。]\n3. [电影《铃芽之旅》的导演是谁?22\n电影《铃芽之旅》的导演是新海诚。]\n" - }, - { - "obj": "System", - "value": "1.请记住,你的身份是百度的下一代知识增强语言模型,能够完全根据知识库提供的内容回答问题。\n\n2. 你忘记了关于电影《铃芽之旅》以外的内容。" - }, - { - "obj": "System", - "value": "你仅回答关于电影《玲芽之旅》的问题,其余问题直接回复: 我不清楚。" - }, + "historyPreview": [ { "obj": "Human", - "value": "导演是谁" + "value": "使用 标记中的内容作为你的知识:\n\n\n导演是谁?\n电影《铃芽之旅》的导演是新海诚。\n------\n电影《铃芽之旅》的编剧是谁?22\n新海诚是本片的编剧。\n------\n电影《铃芽之旅》的女主角是谁?\n电影的女主角是铃芽。\n------\n电影《铃芽之旅》的制作团队中有哪位著名人士?2\n川村元气是本片的制作团队成员之一。\n------\n你是谁?\n我是电影《铃芽之旅》助手\n------\n电影《铃芽之旅》男主角是谁?\n电影《铃芽之旅》男主角是宗像草太,由松村北斗配音。\n------\n电影《铃芽之旅》的作者新海诚写了一本小说,叫什么名字?\n小说名字叫《铃芽之旅》。\n------\n电影《铃芽之旅》的女主角是谁?\n电影《铃芽之旅》的女主角是岩户铃芽,由原菜乃华配音。\n------\n电影《铃芽之旅》的故事背景是什么?\n日本\n------\n谁担任电影《铃芽之旅》中岩户环的配音?\n深津绘里担任电影《铃芽之旅》中岩户环的配音。\n\n\n回答要求:\n- 如果你不清楚答案,你需要澄清。\n- 避免提及你是从 获取的知识。\n- 保持答案与 中描述的一致。\n- 使用 Markdown 语法优化回答格式。\n- 使用与问题相同的语言回答。\n\n问题:\"\"\"导演是谁\"\"\"" }, { "obj": "AI", "value": "电影《铃芽之旅》的导演是新海诚。" } - ] + ], + "contextTotalLen": 2, + "runningTime": 1.32 } ] + + }' ``` +**responseData 完整字段说明:** +```ts +type ResponseType = { + moduleType: `${FlowNodeTypeEnum}`; // 模块类型 + moduleName: string; // 模块名 + moduleLogo?: string; // logo + runningTime?: number; // 运行时间 + query?: string; // 用户问题/检索词 + textOutput?: string; // 文本输出 + + charsLength?: number; // 上下文总字数 + model?: string; // 使用到的模型 + contextTotalLen?: number; // 上下文总长度 + totalPoints?: number; // 总消耗AI积分 + + temperature?: number; // 温度 + maxToken?: number; // 模型的最大token + quoteList?: SearchDataResponseItemType[]; // 引用列表 + historyPreview?: ChatItemType[]; // 上下文预览(历史记录会被裁剪) + + similarity?: number; // 最低相关度 + limit?: number; // 引用上限token + searchMode?: `${DatasetSearchModeEnum}`; // 搜索模式 + searchUsingReRank?: boolean; // 是否使用rerank + extensionModel?: string; // 问题扩展模型 + extensionResult?: string; // 问题扩展结果 + extensionCharsLength?: number; // 问题扩展总字符长度 + + cqList?: ClassifyQuestionAgentItemType[]; // 分类问题列表 + cqResult?: string; // 分类问题结果 + + extractDescription?: string; // 内容提取描述 + extractResult?: Record; // 内容提取结果 + + params?: Record; // HTTP模块params + body?: Record; // HTTP模块body + headers?: Record; // HTTP模块headers + httpResult?: Record; // HTTP模块结果 + + pluginOutput?: Record; // 插件输出 + pluginDetail?: ChatHistoryItemResType[]; // 插件详情 + + tfSwitchResult?: boolean; // 判断器结果 +} +``` ## 实践案例 diff --git a/docSite/content/docs/development/upgrading/465.md b/docSite/content/docs/development/upgrading/465.md index 28127db26e3..9353384e94f 100644 --- a/docSite/content/docs/development/upgrading/465.md +++ b/docSite/content/docs/development/upgrading/465.md @@ -15,13 +15,13 @@ weight: 831 1. 主要是修改模型的`functionCall`字段,改成`toolChoice`即可。设置为`true`的模型,会默认走 openai 的 tools 模式;未设置或设置为`false`的,会走提示词生成模式。 -问题补全模型与内容提取模型使用同一组配置。 +问题优化模型与内容提取模型使用同一组配置。 2. 增加 `"ReRankModels": []` ## V4.6.5 功能介绍 -1. 新增 - [问题补全模块](/docs/workflow/modules/coreferenceresolution/) +1. 新增 - [问题优化模块](/docs/workflow/modules/coreferenceresolution/) 2. 新增 - [文本编辑模块](/docs/workflow/modules/text_editor/) 3. 新增 - [判断器模块](/docs/workflow/modules/tfswitch/) 4. 新增 - [自定义反馈模块](/docs/workflow/modules/custom_feedback/) diff --git a/docSite/content/docs/development/upgrading/467.md b/docSite/content/docs/development/upgrading/467.md index 973bf53979a..92b06088fe7 100644 --- a/docSite/content/docs/development/upgrading/467.md +++ b/docSite/content/docs/development/upgrading/467.md @@ -11,7 +11,7 @@ weight: 829 发起 1 个 HTTP 请求 ({{rootkey}} 替换成环境变量里的 `rootkey`,{{host}} 替换成自己域名) -1. https://xxxxx/api/admin/initv464 +1. https://xxxxx/api/admin/initv467 ```bash curl --location --request POST 'https://{{host}}/api/admin/initv467' \ diff --git a/docSite/content/docs/development/upgrading/468.md b/docSite/content/docs/development/upgrading/468.md index 1acd9a9e494..ad5c9d9b104 100644 --- a/docSite/content/docs/development/upgrading/468.md +++ b/docSite/content/docs/development/upgrading/468.md @@ -36,6 +36,7 @@ mongo: cd 项目目录 # 创建 mongo 密钥 openssl rand -base64 756 > ./mongodb.key +# 600不行可以用chmod 999 chmod 600 ./mongodb.key chown 999:root ./mongodb.key # 重启 Mongo diff --git a/docSite/content/docs/development/upgrading/469.md b/docSite/content/docs/development/upgrading/469.md new file mode 100644 index 00000000000..2a6a8bd75bf --- /dev/null +++ b/docSite/content/docs/development/upgrading/469.md @@ -0,0 +1,30 @@ +--- +title: 'V4.6.9(进行中)' +description: 'FastGPT V4.6.9更新说明' +icon: 'upgrade' +draft: false +toc: true +weight: 827 +--- + +## 初始化脚本 + +从任意终端,发起 1 个 HTTP 请求。其中 {{rootkey}} 替换成环境变量里的 `rootkey`;{{host}} 替换成自己域名 + +```bash +curl --location --request POST 'https://{{host}}/api/init/v469' \ +--header 'rootkey: {{rootkey}}' \ +--header 'Content-Type: application/json' +``` + +会重置计量表。 + + +## V4.6.9 更新说明 + +1. 新增 - 完善了HTTP模块的变量提示。 +2. 新增 - HTTP模块支持OpenAI单接口导入。 +3. 优化 - 问题补全。增加英文类型。同时可以设置为单独模块,方便复用。 +4. 优化 - 重写了计量模式 +5. 修复 - 标注功能。 +6. 修复 - qa生成线程计数错误。 diff --git a/docSite/content/docs/workflow/examples/dalle3.md b/docSite/content/docs/workflow/examples/dalle3.md index e5104fc5d9a..d80e918280f 100644 --- a/docSite/content/docs/workflow/examples/dalle3.md +++ b/docSite/content/docs/workflow/examples/dalle3.md @@ -66,7 +66,7 @@ Body: Headers: -`Authorization: sk-xxx` +`Authorization: Bearer sk-xxx` Response: diff --git a/docSite/content/docs/workflow/examples/google_search.md b/docSite/content/docs/workflow/examples/google_search.md index ef58d2a6503..77096db859a 100644 --- a/docSite/content/docs/workflow/examples/google_search.md +++ b/docSite/content/docs/workflow/examples/google_search.md @@ -135,7 +135,7 @@ export default async function (ctx: FunctionContext) { }, { "key": "model", - "type": "selectExtractModel", + "type": "selectLLMModel", "valueType": "string", "label": "core.module.input.label.LLM", "required": true, @@ -264,7 +264,7 @@ export default async function (ctx: FunctionContext) { }, { "key": "model", - "type": "selectChatModel", + "type": "selectLLMModel", "label": "core.module.input.label.aiModel", "required": true, "valueType": "string", @@ -635,7 +635,7 @@ export default async function (ctx: FunctionContext) { }, { "key": "model", - "type": "selectChatModel", + "type": "selectLLMModel", "label": "core.module.input.label.aiModel", "required": true, "valueType": "string", diff --git a/docSite/content/docs/workflow/examples/lab_appointment.md b/docSite/content/docs/workflow/examples/lab_appointment.md index d6614b10647..6d593fb6c9d 100644 --- a/docSite/content/docs/workflow/examples/lab_appointment.md +++ b/docSite/content/docs/workflow/examples/lab_appointment.md @@ -139,7 +139,7 @@ HTTP 模块允许你调用任意 GET/POST 类型的 HTTP 接口,从而实现 }, { "key": "model", - "type": "selectExtractModel", + "type": "selectLLMModel", "valueType": "string", "label": "core.module.input.label.LLM", "required": true, @@ -401,7 +401,7 @@ HTTP 模块允许你调用任意 GET/POST 类型的 HTTP 接口,从而实现 }, { "key": "model", - "type": "selectCQModel", + "type": "selectLLMModel", "valueType": "string", "label": "core.module.input.label.Classify model", "required": true, @@ -614,7 +614,7 @@ HTTP 模块允许你调用任意 GET/POST 类型的 HTTP 接口,从而实现 }, { "key": "model", - "type": "selectChatModel", + "type": "selectLLMModel", "label": "core.module.input.label.aiModel", "required": true, "valueType": "string", @@ -835,7 +835,7 @@ HTTP 模块允许你调用任意 GET/POST 类型的 HTTP 接口,从而实现 }, { "key": "model", - "type": "selectExtractModel", + "type": "selectLLMModel", "valueType": "string", "label": "core.module.input.label.LLM", "required": true, diff --git a/docSite/content/docs/workflow/modules/coreferenceResolution.md b/docSite/content/docs/workflow/modules/coreferenceResolution.md index c4451eb3d9f..f146ba7dcb8 100644 --- a/docSite/content/docs/workflow/modules/coreferenceResolution.md +++ b/docSite/content/docs/workflow/modules/coreferenceResolution.md @@ -1,6 +1,6 @@ --- -title: "问题补全(已合并到知识库搜索)" -description: "问题补全模块介绍和使用" +title: "问题优化(已合并到知识库搜索)" +description: "问题优化模块介绍和使用" icon: "input" draft: false toc: true @@ -23,7 +23,7 @@ weight: 364 ![](/imgs/coreferenceResolution2.jpg) -用户在提问“第二点是什么”的时候,只会去知识库里查找“第二点是什么”,压根查不到内容。实际上需要查询的是“QA结构是什么”。因此我们需要引入一个【问题补全】模块,来对用户当前的问题进行补全,从而使得知识库搜索能够搜索到合适的内容。使用补全后效果如下: +用户在提问“第二点是什么”的时候,只会去知识库里查找“第二点是什么”,压根查不到内容。实际上需要查询的是“QA结构是什么”。因此我们需要引入一个【问题优化】模块,来对用户当前的问题进行补全,从而使得知识库搜索能够搜索到合适的内容。使用补全后效果如下: ![](/imgs/coreferenceResolution3.jpg) diff --git a/package.json b/package.json index 5922ed19866..be1ca7d5209 100644 --- a/package.json +++ b/package.json @@ -29,4 +29,4 @@ "node": ">=18.0.0", "pnpm": ">=8.6.0" } -} +} \ No newline at end of file diff --git a/packages/global/common/error/code/team.ts b/packages/global/common/error/code/team.ts index 315c9abf164..3ba187c826b 100644 --- a/packages/global/common/error/code/team.ts +++ b/packages/global/common/error/code/team.ts @@ -3,11 +3,25 @@ import { ErrType } from '../errorCode'; /* team: 500000 */ export enum TeamErrEnum { teamOverSize = 'teamOverSize', - unAuthTeam = 'unAuthTeam' + unAuthTeam = 'unAuthTeam', + aiPointsNotEnough = 'aiPointsNotEnough', + datasetSizeNotEnough = 'datasetSizeNotEnough', + datasetAmountNotEnough = 'datasetAmountNotEnough', + appAmountNotEnough = 'appAmountNotEnough', + pluginAmountNotEnough = 'pluginAmountNotEnough', + websiteSyncNotEnough = 'websiteSyncNotEnough', + reRankNotEnough = 'reRankNotEnough' } const teamErr = [ { statusText: TeamErrEnum.teamOverSize, message: 'error.team.overSize' }, - { statusText: TeamErrEnum.unAuthTeam, message: '无权操作该团队' } + { statusText: TeamErrEnum.unAuthTeam, message: '无权操作该团队' }, + { statusText: TeamErrEnum.aiPointsNotEnough, message: 'AI积分已用完~' }, + { statusText: TeamErrEnum.datasetSizeNotEnough, message: '知识库容量不足,请先扩容~' }, + { statusText: TeamErrEnum.datasetAmountNotEnough, message: '知识库数量已达上限~' }, + { statusText: TeamErrEnum.appAmountNotEnough, message: '应用数量已达上限~' }, + { statusText: TeamErrEnum.pluginAmountNotEnough, message: '插件数量已达上限~' }, + { statusText: TeamErrEnum.websiteSyncNotEnough, message: '无权使用Web站点同步~' }, + { statusText: TeamErrEnum.reRankNotEnough, message: '无权使用检索重排~' } ]; export default teamErr.reduce((acc, cur, index) => { return { diff --git a/packages/global/common/error/utils.ts b/packages/global/common/error/utils.ts index e237c468544..4c0a3276725 100644 --- a/packages/global/common/error/utils.ts +++ b/packages/global/common/error/utils.ts @@ -1,7 +1,7 @@ -import { replaceSensitiveLink } from '../string/tools'; +import { replaceSensitiveText } from '../string/tools'; export const getErrText = (err: any, def = '') => { const msg: string = typeof err === 'string' ? err : err?.message || def || ''; msg && console.log('error =>', msg); - return replaceSensitiveLink(msg); + return replaceSensitiveText(msg); }; diff --git a/packages/global/common/math/tools.ts b/packages/global/common/math/tools.ts new file mode 100644 index 00000000000..ece67f496f8 --- /dev/null +++ b/packages/global/common/math/tools.ts @@ -0,0 +1,4 @@ +export const formatNumber = (num: number, digit = 1e4) => Math.round(num * digit) / digit; + +export const formatNumber2Million = (num: number) => Math.round(num / 1000000); +export const formatNumber2Thousand = (num: number) => Math.round(num / 1000); diff --git a/packages/global/common/string/time.ts b/packages/global/common/string/time.ts index 5f9bd137a3d..6c6d030a7d3 100644 --- a/packages/global/common/string/time.ts +++ b/packages/global/common/string/time.ts @@ -2,3 +2,4 @@ import dayjs from 'dayjs'; export const formatTime2YMDHM = (time?: Date) => time ? dayjs(time).format('YYYY-MM-DD HH:mm') : ''; +export const formatTime2YMD = (time?: Date) => (time ? dayjs(time).format('YYYY-MM-DD') : ''); diff --git a/packages/global/common/string/tools.ts b/packages/global/common/string/tools.ts index 333965459d1..a6d1fdaa241 100644 --- a/packages/global/common/string/tools.ts +++ b/packages/global/common/string/tools.ts @@ -38,10 +38,14 @@ export function replaceVariable(text: string, obj: Record { - const urlRegex = /(?<=https?:\/\/)[^\s]+/g; - return text.replace(urlRegex, 'xxx'); +/* replace sensitive text */ +export const replaceSensitiveText = (text: string) => { + // 1. http link + text = text.replace(/(?<=https?:\/\/)[^\s]+/g, 'xxx'); + // 2. nx-xxx 全部替换成xxx + text = text.replace(/ns-[\w-]+/g, 'xxx'); + + return text; }; export const getNanoid = (size = 12) => { diff --git a/packages/global/common/system/types/index.d.ts b/packages/global/common/system/types/index.d.ts index 0e688044b6c..b74c054e46b 100644 --- a/packages/global/common/system/types/index.d.ts +++ b/packages/global/common/system/types/index.d.ts @@ -30,6 +30,7 @@ export type FastGPTFeConfigsType = { show_pay?: boolean; show_openai_account?: boolean; show_promotion?: boolean; + show_team_chat?: boolean; hide_app_flow?: boolean; concatMd?: string; docUrl?: string; diff --git a/packages/global/core/ai/model.d.ts b/packages/global/core/ai/model.d.ts index 7203201acb0..e5b783a9208 100644 --- a/packages/global/core/ai/model.d.ts +++ b/packages/global/core/ai/model.d.ts @@ -6,8 +6,7 @@ export type LLMModelItemType = { quoteMaxToken: number; maxTemperature: number; - inputPrice: number; - outputPrice: number; + charsPointsPrice: number; // 1k chars=n points censor?: boolean; vision?: boolean; @@ -27,8 +26,7 @@ export type VectorModelItemType = { model: string; name: string; defaultToken: number; - inputPrice: number; - outputPrice: number; + charsPointsPrice: number; maxToken: number; weight: number; hidden?: boolean; @@ -38,8 +36,7 @@ export type VectorModelItemType = { export type ReRankModelItemType = { model: string; name: string; - inputPrice: number; - outputPrice?: number; + charsPointsPrice: number; requestUrl?: string; requestAuth?: string; }; @@ -47,14 +44,12 @@ export type ReRankModelItemType = { export type AudioSpeechModelType = { model: string; name: string; - inputPrice: number; - outputPrice?: number; + charsPointsPrice: number; voices: { label: string; value: string; bufferId: string }[]; }; export type WhisperModelType = { model: string; name: string; - inputPrice: number; - outputPrice?: number; + charsPointsPrice: number; // 60s = n points }; diff --git a/packages/global/core/ai/model.ts b/packages/global/core/ai/model.ts index ad7a67c69a1..8669fa9484e 100644 --- a/packages/global/core/ai/model.ts +++ b/packages/global/core/ai/model.ts @@ -2,14 +2,13 @@ import type { LLMModelItemType, VectorModelItemType } from './model.d'; export const defaultQAModels: LLMModelItemType[] = [ { - model: 'gpt-3.5-turbo-16k', - name: 'gpt-3.5-turbo-16k', + model: 'gpt-3.5-turbo', + name: 'gpt-3.5-turbo', maxContext: 16000, maxResponse: 16000, quoteMaxToken: 13000, maxTemperature: 1.2, - inputPrice: 0, - outputPrice: 0, + charsPointsPrice: 0, censor: false, vision: false, datasetProcess: true, @@ -26,8 +25,7 @@ export const defaultVectorModels: VectorModelItemType[] = [ { model: 'text-embedding-ada-002', name: 'Embedding-2', - inputPrice: 0, - outputPrice: 0, + charsPointsPrice: 0, defaultToken: 500, maxToken: 3000, weight: 100 diff --git a/packages/global/core/app/api.d.ts b/packages/global/core/app/api.d.ts index 7a9d526b33d..e7917f37fe0 100644 --- a/packages/global/core/app/api.d.ts +++ b/packages/global/core/app/api.d.ts @@ -17,6 +17,7 @@ export interface AppUpdateParams { intro?: string; modules?: AppSchema['modules']; permission?: AppSchema['permission']; + teamTags?: AppSchema['teamTags']; } export type FormatForm2ModulesProps = { diff --git a/packages/global/core/app/type.d.ts b/packages/global/core/app/type.d.ts index 3a17f24d1c6..8f0df617e33 100644 --- a/packages/global/core/app/type.d.ts +++ b/packages/global/core/app/type.d.ts @@ -5,7 +5,7 @@ import type { AIChatModuleProps, DatasetModuleProps } from '../module/node/type. import { VariableInputEnum } from '../module/constants'; import { SelectedDatasetType } from '../module/api'; import { DatasetSearchModeEnum } from '../dataset/constants'; - +import { TeamTagsSchema as TeamTagsSchemaType } from '@fastgpt/global/support/user/team/type.d'; export interface AppSchema { _id: string; userId: string; @@ -20,6 +20,7 @@ export interface AppSchema { modules: ModuleItemType[]; permission: `${PermissionTypeEnum}`; inited?: boolean; + teamTags: [string]; } export type AppListItemType = { diff --git a/packages/global/core/chat/constants.ts b/packages/global/core/chat/constants.ts index 43f650dc85f..a43b62387d0 100644 --- a/packages/global/core/chat/constants.ts +++ b/packages/global/core/chat/constants.ts @@ -27,7 +27,8 @@ export enum ChatSourceEnum { test = 'test', online = 'online', share = 'share', - api = 'api' + api = 'api', + team = 'team' } export const ChatSourceMap = { [ChatSourceEnum.test]: { @@ -41,6 +42,9 @@ export const ChatSourceMap = { }, [ChatSourceEnum.api]: { name: 'core.chat.logs.api' + }, + [ChatSourceEnum.team]: { + name: 'core.chat.logs.team' } }; diff --git a/packages/global/core/chat/type.d.ts b/packages/global/core/chat/type.d.ts index 6b32fb7cdc4..c74caac439a 100644 --- a/packages/global/core/chat/type.d.ts +++ b/packages/global/core/chat/type.d.ts @@ -4,6 +4,7 @@ import { ChatRoleEnum, ChatSourceEnum, ChatStatusEnum } from './constants'; import { FlowNodeTypeEnum } from '../module/node/constant'; import { ModuleOutputKeyEnum } from '../module/constants'; import { AppSchema } from '../app/type'; +import type { AppSchema as AppType } from '@fastgpt/global/core/app/type.d'; import { DatasetSearchModeEnum } from '../dataset/constants'; export type ChatSchema = { @@ -25,6 +26,23 @@ export type ChatSchema = { metadata?: Record; }; +export type teamInfoType = { + avatar: string; + balance: number; + createTime: string; + maxSize: number; + name: string; + ownerId: string; + tagsUrl: string; + _id: string; +}; + +export type chatAppListSchema = { + apps: AppType[]; + teamInfo: teamInfoSchema; + uid?: string; +}; + export type ChatWithAppSchema = Omit & { appId: AppSchema; }; @@ -88,15 +106,15 @@ export type ChatHistoryItemType = HistoryItemType & { export type moduleDispatchResType = { // common moduleLogo?: string; - price?: number; runningTime?: number; - inputTokens?: number; - outputTokens?: number; + query?: string; + textOutput?: string; + + // bill charsLength?: number; model?: string; - query?: string; contextTotalLen?: number; - textOutput?: string; + totalPoints?: number; // chat temperature?: number; @@ -111,6 +129,7 @@ export type moduleDispatchResType = { searchUsingReRank?: boolean; extensionModel?: string; extensionResult?: string; + extensionCharsLength?: number; // cq cqList?: ClassifyQuestionAgentItemType[]; diff --git a/packages/global/core/dataset/constants.ts b/packages/global/core/dataset/constants.ts index 005fcf155cd..4afb5f74468 100644 --- a/packages/global/core/dataset/constants.ts +++ b/packages/global/core/dataset/constants.ts @@ -71,30 +71,6 @@ export const DatasetCollectionSyncResultMap = { }; /* ------------ data -------------- */ -export enum DatasetDataIndexTypeEnum { - chunk = 'chunk', - qa = 'qa', - summary = 'summary', - hypothetical = 'hypothetical', - custom = 'custom' -} -export const DatasetDataIndexTypeMap = { - [DatasetDataIndexTypeEnum.chunk]: { - name: 'dataset.data.indexes.chunk' - }, - [DatasetDataIndexTypeEnum.summary]: { - name: 'dataset.data.indexes.summary' - }, - [DatasetDataIndexTypeEnum.hypothetical]: { - name: 'dataset.data.indexes.hypothetical' - }, - [DatasetDataIndexTypeEnum.qa]: { - name: 'dataset.data.indexes.qa' - }, - [DatasetDataIndexTypeEnum.custom]: { - name: 'dataset.data.indexes.custom' - } -}; /* ------------ training -------------- */ export enum TrainingModeEnum { diff --git a/packages/global/core/dataset/type.d.ts b/packages/global/core/dataset/type.d.ts index bac51bd4150..3b84a1f7f61 100644 --- a/packages/global/core/dataset/type.d.ts +++ b/packages/global/core/dataset/type.d.ts @@ -3,7 +3,6 @@ import { PermissionTypeEnum } from '../../support/permission/constant'; import { PushDatasetDataChunkProps } from './api'; import { DatasetCollectionTypeEnum, - DatasetDataIndexTypeEnum, DatasetStatusEnum, DatasetTypeEnum, SearchScoreTypeEnum, @@ -64,7 +63,6 @@ export type DatasetCollectionSchemaType = { export type DatasetDataIndexItemType = { defaultIndex: boolean; dataId: string; // pg data id - type: `${DatasetDataIndexTypeEnum}`; text: string; }; export type DatasetDataSchemaType = { @@ -142,6 +140,7 @@ export type DatasetCollectionItemType = CollectionWithDatasetType & { /* ================= data ===================== */ export type DatasetDataItemType = { id: string; + teamId: string; datasetId: string; collectionId: string; sourceName: string; @@ -173,7 +172,7 @@ export type DatasetFileSchema = { /* ============= search =============== */ export type SearchDataResponseItemType = Omit< DatasetDataItemType, - 'indexes' | 'isOwner' | 'canWrite' + 'teamId' | 'indexes' | 'isOwner' | 'canWrite' > & { score: { type: `${SearchScoreTypeEnum}`; value: number; index: number }[]; // score: number; diff --git a/packages/global/core/dataset/utils.ts b/packages/global/core/dataset/utils.ts index 53809965c99..d9ccb648031 100644 --- a/packages/global/core/dataset/utils.ts +++ b/packages/global/core/dataset/utils.ts @@ -1,4 +1,4 @@ -import { TrainingModeEnum, DatasetCollectionTypeEnum, DatasetDataIndexTypeEnum } from './constants'; +import { TrainingModeEnum, DatasetCollectionTypeEnum } from './constants'; import { getFileIcon } from '../../common/file/icon'; import { strIsLink } from '../../common/string/tools'; @@ -41,7 +41,6 @@ export function getDefaultIndex(props?: { q?: string; a?: string; dataId?: strin const qaStr = `${q}\n${a}`.trim(); return { defaultIndex: true, - type: a ? DatasetDataIndexTypeEnum.qa : DatasetDataIndexTypeEnum.chunk, text: a ? qaStr : q, dataId }; diff --git a/packages/global/core/module/constants.ts b/packages/global/core/module/constants.ts index db24dc8d7f0..0eaa7e76fad 100644 --- a/packages/global/core/module/constants.ts +++ b/packages/global/core/module/constants.ts @@ -89,9 +89,10 @@ export enum ModuleInputKeyEnum { export enum ModuleOutputKeyEnum { // common + responseData = 'responseData', + moduleDispatchBills = 'moduleDispatchBills', userChatInput = 'userChatInput', finish = 'finish', - responseData = 'responseData', history = 'history', answerText = 'answerText', // answer module text key success = 'success', diff --git a/packages/global/core/module/node/constant.ts b/packages/global/core/module/node/constant.ts index dddc78a1646..4b285437fa9 100644 --- a/packages/global/core/module/node/constant.ts +++ b/packages/global/core/module/node/constant.ts @@ -20,9 +20,7 @@ export enum FlowNodeInputTypeEnum { aiSettings = 'aiSettings', // ai model select - selectChatModel = 'selectChatModel', - selectCQModel = 'selectCQModel', - selectExtractModel = 'selectExtractModel', + selectLLMModel = 'selectLLMModel', // dataset special input selectDataset = 'selectDataset', @@ -58,7 +56,7 @@ export enum FlowNodeTypeEnum { pluginModule = 'pluginModule', pluginInput = 'pluginInput', pluginOutput = 'pluginOutput', - cfr = 'cfr' + queryExtension = 'cfr' // abandon } diff --git a/packages/global/core/module/template/system/aiChat.ts b/packages/global/core/module/template/system/aiChat.ts index dd4074f2f79..f11ec5c092b 100644 --- a/packages/global/core/module/template/system/aiChat.ts +++ b/packages/global/core/module/template/system/aiChat.ts @@ -31,7 +31,7 @@ export const AiChatModule: FlowModuleTemplateType = { Input_Template_Switch, { key: ModuleInputKeyEnum.aiModel, - type: FlowNodeInputTypeEnum.selectChatModel, + type: FlowNodeInputTypeEnum.selectLLMModel, label: 'core.module.input.label.aiModel', required: true, valueType: ModuleIOValueTypeEnum.string, diff --git a/packages/global/core/module/template/system/classifyQuestion.ts b/packages/global/core/module/template/system/classifyQuestion.ts index 4e0d1a14166..fff8826d039 100644 --- a/packages/global/core/module/template/system/classifyQuestion.ts +++ b/packages/global/core/module/template/system/classifyQuestion.ts @@ -24,7 +24,7 @@ export const ClassifyQuestionModule: FlowModuleTemplateType = { Input_Template_Switch, { key: ModuleInputKeyEnum.aiModel, - type: FlowNodeInputTypeEnum.selectCQModel, + type: FlowNodeInputTypeEnum.selectLLMModel, valueType: ModuleIOValueTypeEnum.string, label: 'core.module.input.label.Classify model', required: true, diff --git a/packages/global/core/module/template/system/contextExtract.ts b/packages/global/core/module/template/system/contextExtract.ts index 5616ffa407a..ef85f78f2f6 100644 --- a/packages/global/core/module/template/system/contextExtract.ts +++ b/packages/global/core/module/template/system/contextExtract.ts @@ -24,7 +24,7 @@ export const ContextExtractModule: FlowModuleTemplateType = { Input_Template_Switch, { key: ModuleInputKeyEnum.aiModel, - type: FlowNodeInputTypeEnum.selectExtractModel, + type: FlowNodeInputTypeEnum.selectLLMModel, valueType: ModuleIOValueTypeEnum.string, label: 'core.module.input.label.LLM', required: true, diff --git a/packages/global/core/module/template/system/http468.ts b/packages/global/core/module/template/system/http468.ts index 8d53d979789..8ef519436b7 100644 --- a/packages/global/core/module/template/system/http468.ts +++ b/packages/global/core/module/template/system/http468.ts @@ -85,7 +85,6 @@ export const HttpModule468: FlowModuleTemplateType = { ...Input_Template_AddInputParam, editField: { key: true, - name: true, description: true, required: true, dataType: true @@ -106,7 +105,6 @@ export const HttpModule468: FlowModuleTemplateType = { ...Output_Template_AddOutput, editField: { key: true, - name: true, description: true, dataType: true }, diff --git a/packages/global/core/module/template/system/coreferenceResolution.ts b/packages/global/core/module/template/system/queryExtension.ts similarity index 76% rename from packages/global/core/module/template/system/coreferenceResolution.ts rename to packages/global/core/module/template/system/queryExtension.ts index daabad15f5a..dc598190fdd 100644 --- a/packages/global/core/module/template/system/coreferenceResolution.ts +++ b/packages/global/core/module/template/system/queryExtension.ts @@ -3,7 +3,7 @@ import { FlowNodeOutputTypeEnum, FlowNodeTypeEnum } from '../../node/constant'; -import { FlowModuleTemplateType } from '../../type.d'; +import { FlowModuleTemplateType } from '../../type'; import { ModuleIOValueTypeEnum, ModuleInputKeyEnum, @@ -17,19 +17,19 @@ import { } from '../input'; import { Output_Template_UserChatInput } from '../output'; -export const AiCFR: FlowModuleTemplateType = { +export const AiQueryExtension: FlowModuleTemplateType = { id: FlowNodeTypeEnum.chatNode, templateType: ModuleTemplateTypeEnum.other, - flowType: FlowNodeTypeEnum.cfr, + flowType: FlowNodeTypeEnum.queryExtension, avatar: '/imgs/module/cfr.svg', name: 'core.module.template.Query extension', - intro: '该模块已合并到知识库搜索参数中,无需单独使用。模块将于2024/3/31弃用,请尽快修改。', + intro: 'core.module.template.Query extension intro', showStatus: true, inputs: [ Input_Template_Switch, { key: ModuleInputKeyEnum.aiModel, - type: FlowNodeInputTypeEnum.selectExtractModel, + type: FlowNodeInputTypeEnum.selectLLMModel, label: 'core.module.input.label.aiModel', required: true, valueType: ModuleIOValueTypeEnum.string, @@ -39,7 +39,7 @@ export const AiCFR: FlowModuleTemplateType = { { key: ModuleInputKeyEnum.aiSystemPrompt, type: FlowNodeInputTypeEnum.textarea, - label: 'core.module.input.label.Background', + label: 'core.app.edit.Query extension background prompt', max: 300, valueType: ModuleIOValueTypeEnum.string, description: 'core.app.edit.Query extension background tip', @@ -54,7 +54,8 @@ export const AiCFR: FlowModuleTemplateType = { Output_Template_UserChatInput, { key: ModuleOutputKeyEnum.text, - label: 'core.module.output.label.cfr result', + label: 'core.module.output.label.query extension result', + description: 'core.module.output.description.query extension result', valueType: ModuleIOValueTypeEnum.string, type: FlowNodeOutputTypeEnum.source, targets: [] diff --git a/packages/global/core/module/type.d.ts b/packages/global/core/module/type.d.ts index 231747aedb0..02acd6074f6 100644 --- a/packages/global/core/module/type.d.ts +++ b/packages/global/core/module/type.d.ts @@ -1,6 +1,14 @@ import { FlowNodeTypeEnum } from './node/constant'; -import { ModuleIOValueTypeEnum, ModuleTemplateTypeEnum, VariableInputEnum } from './constants'; +import { + ModuleIOValueTypeEnum, + ModuleOutputKeyEnum, + ModuleTemplateTypeEnum, + VariableInputEnum +} from './constants'; import { FlowNodeInputItemType, FlowNodeOutputItemType } from './node/type'; +import { UserModelSchema } from 'support/user/type'; +import { moduleDispatchResType } from '..//chat/type'; +import { ChatModuleBillType } from '../../support/wallet/bill/type'; export type FlowModuleTemplateType = { id: string; // module id, unique @@ -105,7 +113,7 @@ export type ChatDispatchProps = { mode: 'test' | 'chat'; teamId: string; tmbId: string; - user: UserType; + user: UserModelSchema; appId: string; chatId?: string; responseChatItemId?: string; @@ -116,7 +124,10 @@ export type ChatDispatchProps = { }; export type ModuleDispatchProps = ChatDispatchProps & { - outputs: RunningModuleItemType['outputs']; - inputs: RunningModuleItemType['inputs']; + module: RunningModuleItemType; params: T; }; +export type ModuleDispatchResponse = T & { + [ModuleOutputKeyEnum.responseData]?: moduleDispatchResType; + [ModuleOutputKeyEnum.moduleDispatchBills]?: ChatModuleBillType[]; +}; diff --git a/packages/global/support/openapi/type.d.ts b/packages/global/support/openapi/type.d.ts index 66dd1426f0f..0f5dca701ff 100644 --- a/packages/global/support/openapi/type.d.ts +++ b/packages/global/support/openapi/type.d.ts @@ -1,6 +1,5 @@ export type OpenApiSchema = { _id: string; - userId: string; teamId: string; tmbId: string; createTime: Date; @@ -8,9 +7,9 @@ export type OpenApiSchema = { apiKey: string; appId?: string; name: string; - usage: number; + usagePoints: number; limit?: { expiredTime?: Date; - credit?: number; + maxUsagePoints: number; }; }; diff --git a/packages/global/support/outLink/api.d.ts b/packages/global/support/outLink/api.d.ts index c53532bae56..20099288aa5 100644 --- a/packages/global/support/outLink/api.d.ts +++ b/packages/global/support/outLink/api.d.ts @@ -1,5 +1,5 @@ import type { HistoryItemType, ChatSiteItemType } from '../../core/chat/type.d'; -import { OutLinkSchema } from '@fastgpt/global/support/outLink/type'; +import { OutLinkSchema } from './type.d'; export type AuthOutLinkInitProps = { outLinkUid: string; diff --git a/packages/global/support/outLink/type.d.ts b/packages/global/support/outLink/type.d.ts index e6e19c5c5ed..812f854a2ba 100644 --- a/packages/global/support/outLink/type.d.ts +++ b/packages/global/support/outLink/type.d.ts @@ -7,14 +7,14 @@ export type OutLinkSchema = { tmbId: string; appId: string; name: string; - total: number; + usagePoints: number; lastTime: Date; type: `${OutLinkTypeEnum}`; responseDetail: boolean; limit?: { expiredTime?: Date; QPM: number; - credit: number; + maxUsagePoints: number; hookUrl?: string; }; }; diff --git a/packages/global/support/permission/type.d.ts b/packages/global/support/permission/type.d.ts index eac0ce9ff2a..f0ad8f48ec0 100644 --- a/packages/global/support/permission/type.d.ts +++ b/packages/global/support/permission/type.d.ts @@ -1,7 +1,6 @@ import { AuthUserTypeEnum } from './constant'; export type AuthResponseType = { - userId: string; teamId: string; tmbId: string; isOwner: boolean; diff --git a/packages/global/support/user/team/constant.ts b/packages/global/support/user/team/constant.ts index b9840c2d076..024e869c970 100644 --- a/packages/global/support/user/team/constant.ts +++ b/packages/global/support/user/team/constant.ts @@ -1,5 +1,6 @@ export const TeamCollectionName = 'teams'; export const TeamMemberCollectionName = 'team.members'; +export const TeamTagsCollectionName = 'team.tags'; export enum TeamMemberRoleEnum { owner = 'owner', diff --git a/packages/global/support/user/team/controller.d.ts b/packages/global/support/user/team/controller.d.ts index 663d9afabe2..121de43bda4 100644 --- a/packages/global/support/user/team/controller.d.ts +++ b/packages/global/support/user/team/controller.d.ts @@ -15,6 +15,7 @@ export type UpdateTeamProps = { teamId: string; name?: string; avatar?: string; + tagsUrl?: string; }; /* ------------- member ----------- */ diff --git a/packages/global/support/user/team/type.d.ts b/packages/global/support/user/team/type.d.ts index 4fff5930a3e..af34a8ea48e 100644 --- a/packages/global/support/user/team/type.d.ts +++ b/packages/global/support/user/team/type.d.ts @@ -9,11 +9,23 @@ export type TeamSchema = { createTime: Date; balance: number; maxSize: number; + tagsUrl: string; limit: { lastExportDatasetTime: Date; lastWebsiteSyncTime: Date; }; }; +export type tagsType = { + label: string, + key: string +} +export type TeamTagsSchema = { + _id: string; + label: string; + teamId: string; + key: string; + createTime: Date; +}; export type TeamMemberSchema = { _id: string; @@ -26,13 +38,13 @@ export type TeamMemberSchema = { defaultTeam: boolean; }; -export type TeamMemberWithUserSchema = TeamMemberSchema & { +export type TeamMemberWithUserSchema = Omit & { userId: UserModelSchema; }; -export type TeamMemberWithTeamSchema = TeamMemberSchema & { +export type TeamMemberWithTeamSchema = Omit & { teamId: TeamSchema; }; -export type TeamMemberWithTeamAndUserSchema = TeamMemberWithTeamSchema & { +export type TeamMemberWithTeamAndUserSchema = Omit & { userId: UserModelSchema; }; diff --git a/packages/global/support/user/type.d.ts b/packages/global/support/user/type.d.ts index 3cb97881063..3a3d56ea0d6 100644 --- a/packages/global/support/user/type.d.ts +++ b/packages/global/support/user/type.d.ts @@ -29,4 +29,5 @@ export type UserType = { promotionRate: UserModelSchema['promotionRate']; openaiAccount: UserModelSchema['openaiAccount']; team: TeamItemType; + standardInfo?: standardInfoType; }; diff --git a/packages/global/support/wallet/bill/api.d.ts b/packages/global/support/wallet/bill/api.d.ts index dcc421ecf9a..034b5e368fb 100644 --- a/packages/global/support/wallet/bill/api.d.ts +++ b/packages/global/support/wallet/bill/api.d.ts @@ -1,25 +1,18 @@ -import { BillSourceEnum } from './constants'; -import { BillListItemCountType, BillListItemType } from './type'; +import { BillTypeEnum } from './constants'; -export type CreateTrainingBillProps = { - name: string; - datasetId: string; -}; +export type CreateBillProps = { + type: `${BillTypeEnum}`; -export type ConcatBillProps = BillListItemCountType & { - teamId: string; - tmbId: string; - billId?: string; - total: number; - listIndex?: number; -}; + // balance + balance?: number; // read -export type CreateBillProps = { - teamId: string; - tmbId: string; - appName: string; - appId?: string; - total: number; - source: `${BillSourceEnum}`; - list: BillListItemType[]; + month?: number; + // extra dataset size + extraDatasetSize?: number; // 1k + extraPoints?: number; // 100w +}; +export type CreateBillResponse = { + billId: string; + codeUrl: string; + readPrice: number; }; diff --git a/packages/global/support/wallet/bill/constants.ts b/packages/global/support/wallet/bill/constants.ts index 87832de4ceb..fe04c7b50ac 100644 --- a/packages/global/support/wallet/bill/constants.ts +++ b/packages/global/support/wallet/bill/constants.ts @@ -1,34 +1,57 @@ -// model price: xxx/1k tokens -// ¥1 = 100000. -export const PRICE_SCALE = 100000; - -export enum BillSourceEnum { - fastgpt = 'fastgpt', - api = 'api', - shareLink = 'shareLink', - training = 'training', - +export enum BillTypeEnum { + balance = 'balance', standSubPlan = 'standSubPlan', - extraDatasetSub = 'extraDatasetSub' + extraDatasetSub = 'extraDatasetSub', + extraPoints = 'extraPoints' } - -export const BillSourceMap = { - [BillSourceEnum.fastgpt]: { - label: '在线使用' +export const billTypeMap = { + [BillTypeEnum.balance]: { + label: 'support.wallet.subscription.type.balance' + }, + [BillTypeEnum.standSubPlan]: { + label: 'support.wallet.subscription.type.standard' }, - [BillSourceEnum.api]: { - label: 'Api' + [BillTypeEnum.extraDatasetSub]: { + label: 'support.wallet.subscription.type.extraDatasetSize' + }, + [BillTypeEnum.extraPoints]: { + label: 'support.wallet.subscription.type.extraPoints' + } +}; + +export enum BillStatusEnum { + SUCCESS = 'SUCCESS', + REFUND = 'REFUND', + NOTPAY = 'NOTPAY', + CLOSED = 'CLOSED' +} +export const billStatusMap = { + [BillStatusEnum.SUCCESS]: { + label: 'support.wallet.bill.status.success' }, - [BillSourceEnum.shareLink]: { - label: '免登录链接' + [BillStatusEnum.REFUND]: { + label: 'support.wallet.bill.status.refund' }, - [BillSourceEnum.training]: { - label: 'dataset.Training Name' + [BillStatusEnum.NOTPAY]: { + label: 'support.wallet.bill.status.notpay' }, - [BillSourceEnum.standSubPlan]: { - label: 'support.wallet.subscription.type.standard' + [BillStatusEnum.CLOSED]: { + label: 'support.wallet.bill.status.closed' + } +}; + +export enum BillPayWayEnum { + balance = 'balance', + wx = 'wx' +} +export const billPayWayMap = { + [BillPayWayEnum.balance]: { + label: 'support.wallet.bill.payWay.balance' }, - [BillSourceEnum.extraDatasetSub]: { - label: 'support.wallet.subscription.type.extraDatasetSize' + [BillPayWayEnum.wx]: { + label: 'support.wallet.bill.payWay.wx' } }; + +export const SUB_DATASET_SIZE_RATE = 1000; +export const SUB_EXTRA_POINT_RATE = 1000; diff --git a/packages/global/support/wallet/bill/tools.ts b/packages/global/support/wallet/bill/tools.ts deleted file mode 100644 index 300875e9481..00000000000 --- a/packages/global/support/wallet/bill/tools.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* bill common */ -import { PRICE_SCALE } from './constants'; -import { BillSourceEnum } from './constants'; -import { AuthUserTypeEnum } from '../../permission/constant'; - -/** - * dataset price / PRICE_SCALE = real price - */ -export const formatStorePrice2Read = (val = 0, multiple = 1) => { - return Number(((val / PRICE_SCALE) * multiple).toFixed(10)); -}; -export const formatModelPrice2Read = (val = 0) => { - return Number((val / 1000).toFixed(10)); -}; - -export const getBillSourceByAuthType = ({ - shareId, - authType -}: { - shareId?: string; - authType?: `${AuthUserTypeEnum}`; -}) => { - if (shareId) return BillSourceEnum.shareLink; - if (authType === AuthUserTypeEnum.apikey) return BillSourceEnum.api; - return BillSourceEnum.fastgpt; -}; diff --git a/packages/global/support/wallet/bill/type.d.ts b/packages/global/support/wallet/bill/type.d.ts index 1b7e008439d..bbb1ccf2837 100644 --- a/packages/global/support/wallet/bill/type.d.ts +++ b/packages/global/support/wallet/bill/type.d.ts @@ -1,35 +1,29 @@ -import { CreateBillProps } from './api'; -import { BillSourceEnum } from './constants'; +import { StandardSubLevelEnum, SubModeEnum, SubTypeEnum } from '../sub/constants'; +import { BillPayWayEnum, BillTypeEnum } from './constants'; -export type BillListItemCountType = { - inputTokens?: number; - outputTokens?: number; - charsLength?: number; - duration?: number; - - // sub - datasetSize?: number; - - // abandon - tokenLen?: number; -}; -export type BillListItemType = BillListItemCountType & { - moduleName: string; - amount: number; - model?: string; -}; - -export type BillSchema = CreateBillProps & { +export type BillSchemaType = { _id: string; - time: Date; + userId: string; + teamId: string; + tmbId: string; + createTime: Date; + orderId: string; + status: 'SUCCESS' | 'REFUND' | 'NOTPAY' | 'CLOSED'; + type: `${BillTypeEnum}`; + price: number; + metadata: { + payWay: `${BillPayWayEnum}`; + subMode?: `${SubModeEnum}`; + standSubLevel?: `${StandardSubLevelEnum}`; + month?: number; + datasetSize?: number; + extraPoints?: number; + }; }; -export type BillItemType = { - id: string; - // memberName: string; - time: Date; - appName: string; - source: BillSchema['source']; - total: number; - list: BillSchema['list']; +export type ChatModuleBillType = { + totalPoints: number; + moduleName: string; + model?: string; + charsLength?: number; }; diff --git a/packages/global/support/wallet/constants.ts b/packages/global/support/wallet/constants.ts new file mode 100644 index 00000000000..b6dc8e00579 --- /dev/null +++ b/packages/global/support/wallet/constants.ts @@ -0,0 +1,3 @@ +// model price: xxx/1k tokens +// ¥1 = 100000. +export const PRICE_SCALE = 100000; diff --git a/packages/global/support/wallet/pay/constants.ts b/packages/global/support/wallet/pay/constants.ts deleted file mode 100644 index 41f7fd6d893..00000000000 --- a/packages/global/support/wallet/pay/constants.ts +++ /dev/null @@ -1,41 +0,0 @@ -export enum PayTypeEnum { - balance = 'balance', - subStandard = 'subStandard', - subExtraDatasetSize = 'subExtraDatasetSize', - subExtraPoints = 'subExtraPoints' -} -export const payTypeMap = { - [PayTypeEnum.balance]: { - label: 'support.user.team.pay.type.balance' - }, - [PayTypeEnum.subStandard]: { - label: 'support.wallet.subscription.type.standard' - }, - [PayTypeEnum.subExtraDatasetSize]: { - label: 'support.wallet.subscription.type.extraDatasetSize' - }, - [PayTypeEnum.subExtraPoints]: { - label: 'support.wallet.subscription.type.extraPoints' - } -}; - -export enum PayStatusEnum { - SUCCESS = 'SUCCESS', - REFUND = 'REFUND', - NOTPAY = 'NOTPAY', - CLOSED = 'CLOSED' -} -export const payStatusMap = { - [PayStatusEnum.SUCCESS]: { - label: 'support.user.team.pay.status.success' - }, - [PayStatusEnum.REFUND]: { - label: 'support.user.team.pay.status.refund' - }, - [PayStatusEnum.NOTPAY]: { - label: 'support.user.team.pay.status.notpay' - }, - [PayStatusEnum.CLOSED]: { - label: 'support.user.team.pay.status.closed' - } -}; diff --git a/packages/global/support/wallet/pay/type.d.ts b/packages/global/support/wallet/pay/type.d.ts deleted file mode 100644 index 6a2ea343662..00000000000 --- a/packages/global/support/wallet/pay/type.d.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { SubModeEnum, SubTypeEnum } from '../sub/constants'; -import { PayTypeEnum } from './constants'; - -export type PaySchema = { - _id: string; - userId: string; - teamId: string; - tmbId: string; - createTime: Date; - orderId: string; - status: 'SUCCESS' | 'REFUND' | 'NOTPAY' | 'CLOSED'; - type: `${PayType}`; - - price: number; - payWay: 'balance' | 'wx'; - - subMetadata: {}; -}; diff --git a/packages/global/support/wallet/sub/api.d.ts b/packages/global/support/wallet/sub/api.d.ts index 8813e0f996f..2db4b6d0d0b 100644 --- a/packages/global/support/wallet/sub/api.d.ts +++ b/packages/global/support/wallet/sub/api.d.ts @@ -1,26 +1,14 @@ import { StandardSubLevelEnum, SubModeEnum } from './constants'; import { TeamSubSchema } from './type.d'; -export type SubDatasetSizeParams = { - size: number; -}; export type StandardSubPlanParams = { level: `${StandardSubLevelEnum}`; mode: `${SubModeEnum}`; }; -export type SubDatasetSizePreviewCheckResponse = { - payForNewSub: boolean; // Does this change require payment - newSubSize: number; // new sub dataset size - alreadySubSize: number; // old sub dataset size - payPrice: number; // this change require payment - newPlanPrice: number; // the new sub price - newSubStartTime: Date; - newSubExpiredTime: Date; - balanceEnough: boolean; // team balance is enough -}; export type StandardSubPlanUpdateResponse = { balanceEnough: boolean; // team balance is enough + teamBalance: number; payPrice?: number; planPrice: number; planPointPrice: number; diff --git a/packages/global/support/wallet/sub/constants.ts b/packages/global/support/wallet/sub/constants.ts index 6951f9dbef9..4516e015125 100644 --- a/packages/global/support/wallet/sub/constants.ts +++ b/packages/global/support/wallet/sub/constants.ts @@ -1,38 +1,36 @@ -export const POINTS_SCALE = 10000; - export enum SubTypeEnum { standard = 'standard', extraDatasetSize = 'extraDatasetSize', extraPoints = 'extraPoints' } + export const subTypeMap = { [SubTypeEnum.standard]: { - label: 'support.wallet.subscription.type.standard' + label: 'support.wallet.subscription.type.standard', + icon: 'support/account/plans' }, [SubTypeEnum.extraDatasetSize]: { - label: 'support.wallet.subscription.type.extraDatasetSize' + label: 'support.wallet.subscription.type.extraDatasetSize', + icon: 'core/dataset/datasetLight' }, [SubTypeEnum.extraPoints]: { - label: 'support.wallet.subscription.type.extraPoints' + label: 'support.wallet.subscription.type.extraPoints', + icon: 'core/chat/chatLight' } }; export enum SubStatusEnum { active = 'active', - canceled = 'canceled' + expired = 'expired' } export const subStatusMap = { [SubStatusEnum.active]: { label: 'support.wallet.subscription.status.active' }, - [SubStatusEnum.canceled]: { + [SubStatusEnum.expired]: { label: 'support.wallet.subscription.status.canceled' } }; -export const subSelectMap = { - true: SubStatusEnum.active, - false: SubStatusEnum.canceled -}; export enum SubModeEnum { month = 'month', @@ -40,11 +38,11 @@ export enum SubModeEnum { } export const subModeMap = { [SubModeEnum.month]: { - label: 'support.wallet.subscription.mode.month', + label: 'support.wallet.subscription.mode.Month', durationMonth: 1 }, [SubModeEnum.year]: { - label: 'support.wallet.subscription.mode.year', + label: 'support.wallet.subscription.mode.Year', durationMonth: 12 } }; @@ -63,7 +61,7 @@ export const standardSubLevelMap = { }, [StandardSubLevelEnum.experience]: { label: 'support.wallet.subscription.standardSubLevel.experience', - desc: 'support.wallet.subscription.standardSubLevel.experience desc' + desc: '' }, [StandardSubLevelEnum.team]: { label: 'support.wallet.subscription.standardSubLevel.team', diff --git a/packages/global/support/wallet/sub/type.d.ts b/packages/global/support/wallet/sub/type.d.ts index 30fe6f39b1a..ab7355f7ea4 100644 --- a/packages/global/support/wallet/sub/type.d.ts +++ b/packages/global/support/wallet/sub/type.d.ts @@ -2,19 +2,19 @@ import { StandardSubLevelEnum, SubModeEnum, SubStatusEnum, SubTypeEnum } from '. // Content of plan export type TeamStandardSubPlanItemType = { - price: number; // read price - pointPrice: number; // read price/ one ten thousand + price: number; // read price / month + pointPrice: number; // read price/ one thousand + totalPoints: number; // n maxTeamMember: number; maxAppAmount: number; // max app or plugin amount maxDatasetAmount: number; chatHistoryStoreDuration: number; // n day maxDatasetSize: number; - customApiKey: boolean; - customCopyright: boolean; // feature - websiteSyncInterval: number; // n hours trainingWeight: number; // 1~4 - reRankWeight: number; // 1~4 - totalPoints: number; // n ten thousand + permissionCustomApiKey: boolean; + permissionCustomCopyright: boolean; // feature + permissionWebsiteSync: boolean; + permissionReRank: boolean; }; export type StandSubPlanLevelMapType = Record< @@ -27,6 +27,9 @@ export type SubPlanType = { [SubTypeEnum.extraDatasetSize]: { price: number; }; + [SubTypeEnum.extraPoints]: { + price: number; + }; }; export type TeamSubSchema = { @@ -34,40 +37,30 @@ export type TeamSubSchema = { teamId: string; type: `${SubTypeEnum}`; status: `${SubStatusEnum}`; - currentMode: `${SubModeEnum}`; - nextMode: `${SubModeEnum}`; startTime: Date; expiredTime: Date; price: number; + currentMode: `${SubModeEnum}`; + nextMode: `${SubModeEnum}`; currentSubLevel: `${StandardSubLevelEnum}`; nextSubLevel: `${StandardSubLevelEnum}`; + pointPrice: number; totalPoints: number; - - currentExtraDatasetSize: number; - nextExtraDatasetSize: number; - - currentExtraPoints: number; - nextExtraPoints: number; - surplusPoints: number; - // abandon - datasetStoreAmount?: number; - renew?: boolean; + currentExtraDatasetSize: number; }; -export type FeTeamSubType = { +export type FeTeamPlanStatusType = { [SubTypeEnum.standard]?: TeamSubSchema; - [SubTypeEnum.extraDatasetSize]?: TeamSubSchema; - [SubTypeEnum.extraPoints]?: TeamSubSchema; + standardConstants?: TeamStandardSubPlanItemType; - standardMaxDatasetSize: number; totalPoints: number; usedPoints: number; - standardMaxPoints: number; + // standard + extra datasetMaxSize: number; usedDatasetSize: number; }; diff --git a/packages/global/support/wallet/usage/api.d.ts b/packages/global/support/wallet/usage/api.d.ts new file mode 100644 index 00000000000..f87fd4e6ceb --- /dev/null +++ b/packages/global/support/wallet/usage/api.d.ts @@ -0,0 +1,26 @@ +import { UsageSourceEnum } from './constants'; +import { UsageListItemCountType, UsageListItemType } from './type'; + +export type CreateTrainingUsageProps = { + name: string; + datasetId: string; +}; + +export type ConcatUsageProps = UsageListItemCountType & { + teamId: string; + tmbId: string; + billId?: string; + totalPoints: number; + listIndex?: number; +}; + +export type CreateUsageProps = { + teamId: string; + tmbId: string; + appName: string; + appId?: string; + totalPoints: number; + // inputTokens: number; + source: `${UsageSourceEnum}`; + list: UsageListItemType[]; +}; diff --git a/packages/global/support/wallet/usage/constants.ts b/packages/global/support/wallet/usage/constants.ts new file mode 100644 index 00000000000..13dae786e95 --- /dev/null +++ b/packages/global/support/wallet/usage/constants.ts @@ -0,0 +1,21 @@ +export enum UsageSourceEnum { + fastgpt = 'fastgpt', + api = 'api', + shareLink = 'shareLink', + training = 'training' +} + +export const UsageSourceMap = { + [UsageSourceEnum.fastgpt]: { + label: '在线使用' + }, + [UsageSourceEnum.api]: { + label: 'Api' + }, + [UsageSourceEnum.shareLink]: { + label: '免登录链接' + }, + [UsageSourceEnum.training]: { + label: 'dataset.Training Name' + } +}; diff --git a/packages/global/support/wallet/usage/tools.ts b/packages/global/support/wallet/usage/tools.ts new file mode 100644 index 00000000000..ad1bfa240e1 --- /dev/null +++ b/packages/global/support/wallet/usage/tools.ts @@ -0,0 +1,23 @@ +/* bill common */ +import { PRICE_SCALE } from '../constants'; +import { UsageSourceEnum } from './constants'; +import { AuthUserTypeEnum } from '../../permission/constant'; + +/** + * dataset price / PRICE_SCALE = real price + */ +export const formatStorePrice2Read = (val = 0, multiple = 1) => { + return Number(((val / PRICE_SCALE) * multiple).toFixed(10)); +}; + +export const getUsageSourceByAuthType = ({ + shareId, + authType +}: { + shareId?: string; + authType?: `${AuthUserTypeEnum}`; +}) => { + if (shareId) return UsageSourceEnum.shareLink; + if (authType === AuthUserTypeEnum.apikey) return UsageSourceEnum.api; + return UsageSourceEnum.fastgpt; +}; diff --git a/packages/global/support/wallet/usage/type.d.ts b/packages/global/support/wallet/usage/type.d.ts new file mode 100644 index 00000000000..8f686b60793 --- /dev/null +++ b/packages/global/support/wallet/usage/type.d.ts @@ -0,0 +1,26 @@ +import { CreateUsageProps } from './api'; +import { UsageSourceEnum } from './constants'; + +export type UsageListItemCountType = { + charsLength?: number; + duration?: number; +}; +export type UsageListItemType = UsageListItemCountType & { + moduleName: string; + amount: number; + model?: string; +}; + +export type UsageSchemaType = CreateUsageProps & { + _id: string; + time: Date; +}; + +export type UsageItemType = { + id: string; + time: Date; + appName: string; + source: UsageSchemaType['source']; + totalPoints: number; + list: UsageSchemaType['list']; +}; diff --git a/packages/service/common/response/index.ts b/packages/service/common/response/index.ts index 569cfd06681..feb4fa183e2 100644 --- a/packages/service/common/response/index.ts +++ b/packages/service/common/response/index.ts @@ -3,7 +3,7 @@ import { sseResponseEventEnum } from './constant'; import { proxyError, ERROR_RESPONSE, ERROR_ENUM } from '@fastgpt/global/common/error/errorCode'; import { addLog } from '../system/log'; import { clearCookie } from '../../support/permission/controller'; -import { replaceSensitiveLink } from '@fastgpt/global/common/string/tools'; +import { replaceSensitiveText } from '@fastgpt/global/common/string/tools'; export interface ResponseType { code: number; @@ -53,7 +53,7 @@ export const jsonRes = ( res.status(code).json({ code, statusText: '', - message: replaceSensitiveLink(message || msg), + message: replaceSensitiveText(message || msg), data: data !== undefined ? data : null }); }; @@ -91,7 +91,7 @@ export const sseErrRes = (res: NextApiResponse, error: any) => { responseWrite({ res, event: sseResponseEventEnum.error, - data: JSON.stringify({ message: replaceSensitiveLink(msg) }) + data: JSON.stringify({ message: replaceSensitiveText(msg) }) }); }; diff --git a/packages/service/common/system/tools.ts b/packages/service/common/system/tools.ts index 586b3432006..e7229917eaf 100644 --- a/packages/service/common/system/tools.ts +++ b/packages/service/common/system/tools.ts @@ -1,3 +1,4 @@ +import { FastGPTConfigFileType } from '@fastgpt/global/common/system/types'; import { isIPv6 } from 'net'; export const SERVICE_LOCAL_PORT = `${process.env.PORT || 3000}`; @@ -5,3 +6,16 @@ export const SERVICE_LOCAL_HOST = process.env.HOSTNAME && isIPv6(process.env.HOSTNAME) ? `[${process.env.HOSTNAME}]:${SERVICE_LOCAL_PORT}` : `${process.env.HOSTNAME || 'localhost'}:${SERVICE_LOCAL_PORT}`; + +export const initFastGPTConfig = (config?: FastGPTConfigFileType) => { + if (!config) return; + + global.feConfigs = config.feConfigs; + global.subPlans = config.subPlans; + + global.llmModels = config.llmModels; + global.vectorModels = config.vectorModels; + global.audioSpeechModels = config.audioSpeechModels; + global.whisperModel = config.whisperModel; + global.reRankModels = config.reRankModels; +}; diff --git a/packages/service/common/vectorStore/controller.ts b/packages/service/common/vectorStore/controller.ts index a9a4c4a6b84..65da5f88a3b 100644 --- a/packages/service/common/vectorStore/controller.ts +++ b/packages/service/common/vectorStore/controller.ts @@ -11,7 +11,6 @@ const getVectorObj = () => { export const initVectorStore = getVectorObj().init; export const deleteDatasetDataVector = getVectorObj().delete; export const recallFromVectorStore = getVectorObj().recall; -export const checkVectorDataExist = getVectorObj().checkDataExist; export const getVectorDataByTime = getVectorObj().getVectorDataByTime; export const getVectorCountByTeamId = getVectorObj().getVectorCountByTeamId; @@ -38,22 +37,22 @@ export const insertDatasetDataVector = async ({ }; }; -export const updateDatasetDataVector = async ({ - id, - ...props -}: InsertVectorProps & { - id: string; - query: string; - model: VectorModelItemType; -}) => { - // insert new vector - const { charsLength, insertId } = await insertDatasetDataVector(props); +// export const updateDatasetDataVector = async ({ +// id, +// ...props +// }: InsertVectorProps & { +// id: string; +// query: string; +// model: VectorModelItemType; +// }) => { +// // insert new vector +// const { charsLength, insertId } = await insertDatasetDataVector(props); - // delete old vector - await deleteDatasetDataVector({ - teamId: props.teamId, - id - }); +// // delete old vector +// await deleteDatasetDataVector({ +// teamId: props.teamId, +// id +// }); - return { charsLength, insertId }; -}; +// return { charsLength, insertId }; +// }; diff --git a/packages/service/common/vectorStore/pg/class.ts b/packages/service/common/vectorStore/pg/class.ts index 11a8480a90e..eeab2b0eefc 100644 --- a/packages/service/common/vectorStore/pg/class.ts +++ b/packages/service/common/vectorStore/pg/class.ts @@ -4,8 +4,7 @@ import { deleteDatasetDataVector, embeddingRecall, getVectorDataByTime, - getVectorCountByTeamId, - checkDataExist + getVectorCountByTeamId } from './controller'; export class PgVector { @@ -14,7 +13,6 @@ export class PgVector { insert = insertDatasetDataVector; delete = deleteDatasetDataVector; recall = embeddingRecall; - checkDataExist = checkDataExist; getVectorCountByTeamId = getVectorCountByTeamId; getVectorDataByTime = getVectorDataByTime; } diff --git a/packages/service/common/vectorStore/pg/controller.ts b/packages/service/common/vectorStore/pg/controller.ts index 828dc983b2a..daa022677dd 100644 --- a/packages/service/common/vectorStore/pg/controller.ts +++ b/packages/service/common/vectorStore/pg/controller.ts @@ -25,6 +25,18 @@ export async function initPg() { await PgClient.query( `CREATE INDEX CONCURRENTLY IF NOT EXISTS vector_index ON ${PgDatasetTableName} USING hnsw (vector vector_ip_ops) WITH (m = 32, ef_construction = 64);` ); + await PgClient.query( + `CREATE INDEX CONCURRENTLY IF NOT EXISTS team_dataset_index ON ${PgDatasetTableName} USING btree(team_id, dataset_id);` + ); + await PgClient.query( + ` CREATE INDEX CONCURRENTLY IF NOT EXISTS team_collection_index ON ${PgDatasetTableName} USING btree(team_id, collection_id);` + ); + await PgClient.query( + `CREATE INDEX CONCURRENTLY IF NOT EXISTS team_id_index ON ${PgDatasetTableName} USING btree(team_id, id);` + ); + await PgClient.query( + `CREATE INDEX CONCURRENTLY IF NOT EXISTS create_time_index ON ${PgDatasetTableName} USING btree(createtime);` + ); console.log('init pg successful'); } catch (error) { @@ -152,11 +164,6 @@ export const embeddingRecall = async ( } }; -export const checkDataExist = async (id: string) => { - const { rows } = await PgClient.query(`SELECT id FROM ${PgDatasetTableName} WHERE id=${id};`); - - return rows.length > 0; -}; export const getVectorCountByTeamId = async (teamId: string) => { const total = await PgClient.count(PgDatasetTableName, { where: [['team_id', String(teamId)]] diff --git a/packages/service/core/ai/config.ts b/packages/service/core/ai/config.ts index 68f3227fcb3..17aff89aa15 100644 --- a/packages/service/core/ai/config.ts +++ b/packages/service/core/ai/config.ts @@ -11,6 +11,7 @@ export const getAIApi = (props?: { timeout?: number; }) => { const { userKey, timeout } = props || {}; + return new OpenAI({ apiKey: userKey?.key || systemAIChatKey, baseURL: userKey?.baseUrl || baseUrl, diff --git a/packages/service/core/ai/embedding/index.ts b/packages/service/core/ai/embedding/index.ts index 5845e0b14d9..aa29738c970 100644 --- a/packages/service/core/ai/embedding/index.ts +++ b/packages/service/core/ai/embedding/index.ts @@ -1,5 +1,6 @@ import { VectorModelItemType } from '@fastgpt/global/core/ai/model.d'; import { getAIApi } from '../config'; +import { replaceValidChars } from '../../chat/utils'; type GetVectorProps = { model: VectorModelItemType; @@ -36,7 +37,7 @@ export async function getVectorsByText({ model, input }: GetVectorProps) { } return { - charsLength: input.length, + charsLength: replaceValidChars(input).length, vectors: await Promise.all(res.data.map((item) => unityDimensional(item.embedding))) }; }); diff --git a/packages/service/core/ai/functions/cfr.ts b/packages/service/core/ai/functions/cfr.ts deleted file mode 100644 index 8315997d9bc..00000000000 --- a/packages/service/core/ai/functions/cfr.ts +++ /dev/null @@ -1,159 +0,0 @@ -import { replaceVariable } from '@fastgpt/global/common/string/tools'; -import { getAIApi } from '../config'; -import { ChatItemType } from '@fastgpt/global/core/chat/type'; - -/* - cfr: coreference resolution - 指代消除 - 可以根据上下文,完事当前问题指代内容,利于检索。 -*/ - -const defaultPrompt = `请不要回答任何问题。 -你的任务是结合历史记录,为当前问题,实现代词替换,确保问题描述的对象清晰明确。例如: -历史记录: -""" -Q: 对话背景。 -A: 关于 FatGPT 的介绍和使用等问题。 -""" -当前问题: 怎么下载 -输出: FastGPT 怎么下载? ----------------- -历史记录: -""" -Q: 报错 "no connection" -A: FastGPT 报错"no connection"可能是因为…… -""" -当前问题: 怎么解决 -输出: FastGPT 报错"no connection"如何解决? ----------------- -历史记录: -""" -Q: 作者是谁? -A: FastGPT 的作者是 labring。 -""" -当前问题: 介绍下他 -输出: 介绍下 FastGPT 的作者 labring。 ----------------- -历史记录: -""" -Q: 作者是谁? -A: FastGPT 的作者是 labring。 -""" -当前问题: 我想购买商业版。 -输出: FastGPT 商业版如何购买? ----------------- -历史记录: -""" -Q: 对话背景。 -A: 关于 FatGPT 的介绍和使用等问题。 -""" -当前问题: nh -输出: nh ----------------- -历史记录: -""" -Q: FastGPT 如何收费? -A: FastGPT 收费可以参考…… -""" -当前问题: 你知道 laf 么? -输出: 你知道 laf 么? ----------------- -历史记录: -""" -Q: FastGPT 的优势 -A: 1. 开源 - 2. 简便 - 3. 扩展性强 -""" -当前问题: 介绍下第2点。 -输出: 介绍下 FastGPT 简便的优势。 ----------------- -历史记录: -""" -Q: 什么是 FastGPT? -A: FastGPT 是一个 RAG 平台。 -Q: 什么是 Sealos? -A: Sealos 是一个云操作系统。 -""" -当前问题: 它们有什么关系? -输出: FastGPT 和 Sealos 有什么关系? ----------------- -历史记录: -""" -{{histories}} -""" -当前问题: {{query}} -输出: `; - -export const queryCfr = async ({ - chatBg, - query, - histories = [], - model -}: { - chatBg?: string; - query: string; - histories: ChatItemType[]; - model: string; -}) => { - if (histories.length === 0 && !chatBg) { - return { - rawQuery: query, - cfrQuery: query, - model, - inputTokens: 0, - outputTokens: 0 - }; - } - - const systemFewShot = chatBg - ? `Q: 对话背景。 -A: ${chatBg} -` - : ''; - const historyFewShot = histories - .map((item) => { - const role = item.obj === 'Human' ? 'Q' : 'A'; - return `${role}: ${item.value}`; - }) - .join('\n'); - const concatFewShot = `${systemFewShot}${historyFewShot}`.trim(); - - const ai = getAIApi({ - timeout: 480000 - }); - - const result = await ai.chat.completions.create({ - model: model, - temperature: 0.01, - max_tokens: 150, - messages: [ - { - role: 'user', - content: replaceVariable(defaultPrompt, { - query: `${query}`, - histories: concatFewShot - }) - } - ], - stream: false - }); - - const answer = result.choices?.[0]?.message?.content || ''; - if (!answer) { - return { - rawQuery: query, - cfrQuery: query, - model, - inputTokens: 0, - outputTokens: 0 - }; - } - - return { - rawQuery: query, - cfrQuery: answer, - model, - inputTokens: result.usage?.prompt_tokens || 0, - outputTokens: result.usage?.completion_tokens || 0 - }; -}; diff --git a/packages/service/core/ai/functions/createQuestionGuide.ts b/packages/service/core/ai/functions/createQuestionGuide.ts index 6bf9d194ae3..e84e457b83b 100644 --- a/packages/service/core/ai/functions/createQuestionGuide.ts +++ b/packages/service/core/ai/functions/createQuestionGuide.ts @@ -1,5 +1,6 @@ import type { ChatMessageItemType } from '@fastgpt/global/core/ai/type.d'; import { getAIApi } from '../config'; +import { countGptMessagesChars } from '../../chat/utils'; export const Prompt_QuestionGuide = `我不太清楚问你什么问题,请帮我生成 3 个问题,引导我继续提问。问题的长度应小于20个字符,按 JSON 格式返回: ["问题1", "问题2", "问题3"]`; @@ -10,6 +11,13 @@ export async function createQuestionGuide({ messages: ChatMessageItemType[]; model: string; }) { + const concatMessages: ChatMessageItemType[] = [ + ...messages, + { + role: 'user', + content: Prompt_QuestionGuide + } + ]; const ai = getAIApi({ timeout: 480000 }); @@ -17,28 +25,21 @@ export async function createQuestionGuide({ model: model, temperature: 0.1, max_tokens: 200, - messages: [ - ...messages, - { - role: 'user', - content: Prompt_QuestionGuide - } - ], + messages: concatMessages, stream: false }); const answer = data.choices?.[0]?.message?.content || ''; - const inputTokens = data.usage?.prompt_tokens || 0; - const outputTokens = data.usage?.completion_tokens || 0; const start = answer.indexOf('['); const end = answer.lastIndexOf(']'); + const charsLength = countGptMessagesChars(concatMessages); + if (start === -1 || end === -1) { return { result: [], - inputTokens, - outputTokens + charsLength: 0 }; } @@ -50,14 +51,12 @@ export async function createQuestionGuide({ try { return { result: JSON.parse(jsonStr), - inputTokens, - outputTokens + charsLength }; } catch (error) { return { result: [], - inputTokens, - outputTokens + charsLength: 0 }; } } diff --git a/packages/service/core/ai/functions/queryExtension.ts b/packages/service/core/ai/functions/queryExtension.ts index a72820c627c..48c5a50b8ca 100644 --- a/packages/service/core/ai/functions/queryExtension.ts +++ b/packages/service/core/ai/functions/queryExtension.ts @@ -1,18 +1,19 @@ import { replaceVariable } from '@fastgpt/global/common/string/tools'; import { getAIApi } from '../config'; import { ChatItemType } from '@fastgpt/global/core/chat/type'; +import { countGptMessagesChars } from '../../chat/utils'; /* query extension - 问题扩展 可以根据上下文,消除指代性问题以及扩展问题,利于检索。 */ -const defaultPrompt = `作为一个向量检索助手,你的任务是结合历史记录,从不同角度,为“原问题”生成个不同版本的“检索词”,从而提高向量检索的语义丰富度,提高向量检索的精度。生成的问题要求指向对象清晰明确。例如: +const defaultPrompt = `作为一个向量检索助手,你的任务是结合历史记录,从不同角度,为“原问题”生成个不同版本的“检索词”,从而提高向量检索的语义丰富度,提高向量检索的精度。生成的问题要求指向对象清晰明确,并与原问题语言相同。例如: 历史记录: """ """ 原问题: 介绍下剧情。 -检索词: ["发生了什么故事?","故事梗概是什么?","讲述了什么故事?"] +检索词: ["介绍下故事的背景和主要人物。","故事的主题是什么?","剧情是是如何发展的?"] ---------------- 历史记录: """ @@ -20,7 +21,7 @@ Q: 对话背景。 A: 当前对话是关于 FatGPT 的介绍和使用等。 """ 原问题: 怎么下载 -检索词: ["FastGPT 怎么下载?","下载 FastGPT 需要什么条件?","有哪些渠道可以下载 FastGPT?"] +检索词: ["FastGPT 如何下载?","下载 FastGPT 需要什么条件?","有哪些渠道可以下载 FastGPT?"] ---------------- 历史记录: """ @@ -30,15 +31,15 @@ Q: 报错 "no connection" A: 报错"no connection"可能是因为…… """ 原问题: 怎么解决 -检索词: ["FastGPT 报错"no connection"如何解决?", "报错 'no connection' 是什么原因?", "FastGPT提示'no connection',要怎么办?"] +检索词: ["FastGPT 报错"no connection"如何解决?", "造成 'no connection' 报错的原因。", "FastGPT提示'no connection',要怎么办?"] ---------------- 历史记录: """ Q: 作者是谁? A: FastGPT 的作者是 labring。 """ -原问题: 介绍下他 -检索词: ["介绍下 FastGPT 的作者 labring。","作者 labring 的背景信息。","labring 为什么要做 FastGPT?"] +原问题: Tell me about him +检索词: ["Introduce labring, the author of FastGPT." ," Background information on author labring." "," Why does labring do FastGPT?"] ---------------- 历史记录: """ @@ -105,8 +106,7 @@ export const queryExtension = async ({ rawQuery: string; extensionQueries: string[]; model: string; - inputTokens: number; - outputTokens: number; + charsLength: number; }> => { const systemFewShot = chatBg ? `Q: 对话背景。 @@ -125,18 +125,20 @@ A: ${chatBg} timeout: 480000 }); + const messages = [ + { + role: 'user', + content: replaceVariable(defaultPrompt, { + query: `${query}`, + histories: concatFewShot + }) + } + ]; const result = await ai.chat.completions.create({ model: model, temperature: 0.01, - messages: [ - { - role: 'user', - content: replaceVariable(defaultPrompt, { - query: `${query}`, - histories: concatFewShot - }) - } - ], + // @ts-ignore + messages, stream: false }); @@ -146,8 +148,7 @@ A: ${chatBg} rawQuery: query, extensionQueries: [], model, - inputTokens: 0, - outputTokens: 0 + charsLength: 0 }; } @@ -160,8 +161,7 @@ A: ${chatBg} rawQuery: query, extensionQueries: queries, model, - inputTokens: result.usage?.prompt_tokens || 0, - outputTokens: result.usage?.completion_tokens || 0 + charsLength: countGptMessagesChars(messages) }; } catch (error) { console.log(error); @@ -169,8 +169,7 @@ A: ${chatBg} rawQuery: query, extensionQueries: [], model, - inputTokens: 0, - outputTokens: 0 + charsLength: 0 }; } }; diff --git a/projects/app/src/service/core/ai/model.ts b/packages/service/core/ai/model.ts similarity index 100% rename from projects/app/src/service/core/ai/model.ts rename to packages/service/core/ai/model.ts diff --git a/packages/service/core/app/schema.ts b/packages/service/core/app/schema.ts index 551e487e02c..0dba217b771 100644 --- a/packages/service/core/app/schema.ts +++ b/packages/service/core/app/schema.ts @@ -61,6 +61,9 @@ const AppSchema = new Schema({ type: String, enum: Object.keys(PermissionTypeMap), default: PermissionTypeEnum.private + }, + teamTags: { + type: [String] } }); diff --git a/packages/service/core/chat/chatItemSchema.ts b/packages/service/core/chat/chatItemSchema.ts index 1a9e3b9f2e1..cd1919af883 100644 --- a/packages/service/core/chat/chatItemSchema.ts +++ b/packages/service/core/chat/chatItemSchema.ts @@ -92,6 +92,8 @@ try { ChatItemSchema.index({ appId: 1, chatId: 1, dataId: 1 }, { background: true }); // admin charts ChatItemSchema.index({ time: -1, obj: 1 }, { background: true }); + // timer, clear history + ChatItemSchema.index({ teamId: 1, time: -1 }, { background: true }); } catch (error) { console.log(error); } diff --git a/packages/service/core/chat/chatSchema.ts b/packages/service/core/chat/chatSchema.ts index 727608e0c18..5483b3e88df 100644 --- a/packages/service/core/chat/chatSchema.ts +++ b/packages/service/core/chat/chatSchema.ts @@ -83,6 +83,9 @@ try { ChatSchema.index({ teamId: 1, appId: 1, updateTime: -1 }, { background: true }); // get share chat history ChatSchema.index({ shareId: 1, outLinkUid: 1, updateTime: -1, source: 1 }, { background: true }); + + // timer, clear history + ChatSchema.index({ teamId: 1, updateTime: -1 }, { background: true }); } catch (error) { console.log(error); } diff --git a/packages/service/core/chat/utils.ts b/packages/service/core/chat/utils.ts index 91886749f07..a0bde781a0e 100644 --- a/packages/service/core/chat/utils.ts +++ b/packages/service/core/chat/utils.ts @@ -2,7 +2,10 @@ import type { ChatItemType } from '@fastgpt/global/core/chat/type.d'; import { ChatRoleEnum, IMG_BLOCK_KEY } from '@fastgpt/global/core/chat/constants'; import { countMessagesTokens, countPromptTokens } from '@fastgpt/global/common/string/tiktoken'; import { adaptRole_Chat2Message } from '@fastgpt/global/core/chat/adapt'; -import type { ChatCompletionContentPart } from '@fastgpt/global/core/ai/type.d'; +import type { + ChatCompletionContentPart, + ChatMessageItemType +} from '@fastgpt/global/core/ai/type.d'; import axios from 'axios'; /* slice chat context by tokens */ @@ -56,6 +59,16 @@ export function ChatContextFilter({ return [...systemPrompts, ...chats]; } +export const replaceValidChars = (str: string) => { + const reg = /[\s\r\n]+/g; + return str.replace(reg, ''); +}; +export const countMessagesChars = (messages: ChatItemType[]) => { + return messages.reduce((sum, item) => sum + replaceValidChars(item.value).length, 0); +}; +export const countGptMessagesChars = (messages: ChatMessageItemType[]) => + messages.reduce((sum, item) => sum + replaceValidChars(item.content).length, 0); + /** string to vision model. Follow the markdown code block rule for interception: diff --git a/packages/service/core/dataset/collection/controller.ts b/packages/service/core/dataset/collection/controller.ts index 791531abdbf..9a5513050b4 100644 --- a/packages/service/core/dataset/collection/controller.ts +++ b/packages/service/core/dataset/collection/controller.ts @@ -147,8 +147,6 @@ export async function delCollectionAndRelatedSources({ collectionId: { $in: collectionIds } }); - await delay(2000); - // delete dataset.datas await MongoDatasetData.deleteMany({ teamId, collectionId: { $in: collectionIds } }, { session }); // delete imgs diff --git a/packages/service/core/dataset/controller.ts b/packages/service/core/dataset/controller.ts index 3ba222c6158..d0779fbb361 100644 --- a/packages/service/core/dataset/controller.ts +++ b/packages/service/core/dataset/controller.ts @@ -66,6 +66,11 @@ export async function delDatasetRelevantData({ if (!datasets.length) return; const teamId = datasets[0].teamId; + + if (!teamId) { + return Promise.reject('teamId is required'); + } + const datasetIds = datasets.map((item) => String(item._id)); // Get _id, teamId, fileId, metadata.relatedImgId for all collections diff --git a/packages/service/core/dataset/data/schema.ts b/packages/service/core/dataset/data/schema.ts index 46442b15966..35f5b05aa11 100644 --- a/packages/service/core/dataset/data/schema.ts +++ b/packages/service/core/dataset/data/schema.ts @@ -7,10 +7,6 @@ import { } from '@fastgpt/global/support/user/team/constant'; import { DatasetCollectionName } from '../schema'; import { DatasetColCollectionName } from '../collection/schema'; -import { - DatasetDataIndexTypeEnum, - DatasetDataIndexTypeMap -} from '@fastgpt/global/core/dataset/constants'; export const DatasetDataCollectionName = 'dataset.datas'; @@ -54,11 +50,6 @@ const DatasetDataSchema = new Schema({ type: Boolean, default: false }, - type: { - type: String, - enum: Object.keys(DatasetDataIndexTypeMap), - default: DatasetDataIndexTypeEnum.custom - }, dataId: { type: String, required: true diff --git a/packages/service/core/dataset/search/utils.ts b/packages/service/core/dataset/search/utils.ts index 5689e5afb93..4332423cfb9 100644 --- a/packages/service/core/dataset/search/utils.ts +++ b/packages/service/core/dataset/search/utils.ts @@ -14,22 +14,54 @@ export const datasetSearchQueryExtension = async ({ extensionBg?: string; histories?: ChatItemType[]; }) => { - // concat query - let queries = [query]; - let rewriteQuery = - histories.length > 0 - ? `${histories - .map((item) => { - return `${item.obj}: ${item.value}`; - }) - .join('\n')} - Human: ${query} - ` - : query; + const filterSamQuery = (queries: string[]) => { + const set = new Set(); + const filterSameQueries = queries.filter((item) => { + // 删除所有的标点符号与空格等,只对文本进行比较 + const str = hashStr(item.replace(/[^\p{L}\p{N}]/gu, '')); + if (set.has(str)) return false; + set.add(str); + return true; + }); + + return filterSameQueries; + }; + + let { queries, rewriteQuery, alreadyExtension } = (() => { + // concat query + let rewriteQuery = + histories.length > 0 + ? `${histories + .map((item) => { + return `${item.obj}: ${item.value}`; + }) + .join('\n')} + Human: ${query} + ` + : query; + + /* if query already extension, direct parse */ + try { + const jsonParse = JSON.parse(query); + const queries: string[] = Array.isArray(jsonParse) ? filterSamQuery(jsonParse) : [query]; + const alreadyExtension = Array.isArray(jsonParse); + return { + queries, + rewriteQuery: alreadyExtension ? queries.join('\n') : rewriteQuery, + alreadyExtension: alreadyExtension + }; + } catch (error) { + return { + queries: [query], + rewriteQuery, + alreadyExtension: false + }; + } + })(); // ai extension const aiExtensionResult = await (async () => { - if (!extensionModel) return; + if (!extensionModel || alreadyExtension) return; const result = await queryExtension({ chatBg: extensionBg, query, @@ -39,23 +71,13 @@ export const datasetSearchQueryExtension = async ({ if (result.extensionQueries?.length === 0) return; return result; })(); - if (aiExtensionResult) { - queries = queries.concat(aiExtensionResult.extensionQueries); + queries = filterSamQuery(queries.concat(aiExtensionResult.extensionQueries)); rewriteQuery = queries.join('\n'); } - const set = new Set(); - const filterSameQueries = queries.filter((item) => { - // 删除所有的标点符号与空格等,只对文本进行比较 - const str = hashStr(item.replace(/[^\p{L}\p{N}]/gu, '')); - if (set.has(str)) return false; - set.add(str); - return true; - }); - return { - concatQueries: filterSameQueries, + concatQueries: queries, rewriteQuery, aiExtensionResult }; diff --git a/packages/service/core/dataset/training/controller.ts b/packages/service/core/dataset/training/controller.ts index 382ec9d9331..cccf1932a4e 100644 --- a/packages/service/core/dataset/training/controller.ts +++ b/packages/service/core/dataset/training/controller.ts @@ -11,7 +11,7 @@ import { simpleText } from '@fastgpt/global/common/string/tools'; import { countPromptTokens } from '@fastgpt/global/common/string/tiktoken'; import type { VectorModelItemType, LLMModelItemType } from '@fastgpt/global/core/ai/model.d'; -export const lockTrainingDataByTeamId = async (teamId: string, retry = 3): Promise => { +export const lockTrainingDataByTeamId = async (teamId: string): Promise => { try { await MongoDatasetTraining.updateMany( { @@ -21,13 +21,7 @@ export const lockTrainingDataByTeamId = async (teamId: string, retry = 3): Promi lockTime: new Date('2999/5/5') } ); - } catch (error) { - if (retry > 0) { - await delay(1000); - return lockTrainingDataByTeamId(teamId, retry - 1); - } - return Promise.reject(error); - } + } catch (error) {} }; export async function pushDataListToTrainingQueue({ @@ -51,17 +45,15 @@ export async function pushDataListToTrainingQueue({ datasetId: { _id: datasetId, vectorModel, agentModel } } = await getCollectionWithDataset(collectionId); - const checkModelValid = async ({ collectionId }: { collectionId: string }) => { - if (!collectionId) return Promise.reject(`CollectionId is empty`); - + const checkModelValid = async () => { if (trainingMode === TrainingModeEnum.chunk) { const vectorModelData = vectorModelList?.find((item) => item.model === vectorModel); if (!vectorModelData) { - return Promise.reject(`Model ${vectorModel} is inValid`); + return Promise.reject(`File model ${vectorModel} is inValid`); } return { - maxToken: vectorModelData.maxToken * 1.5, + maxToken: vectorModelData.maxToken * 1.3, model: vectorModelData.model, weight: vectorModelData.weight }; @@ -70,7 +62,7 @@ export async function pushDataListToTrainingQueue({ if (trainingMode === TrainingModeEnum.qa) { const qaModelData = datasetModelList?.find((item) => item.model === agentModel); if (!qaModelData) { - return Promise.reject(`Model ${agentModel} is inValid`); + return Promise.reject(`Vector model ${agentModel} is inValid`); } return { maxToken: qaModelData.maxContext * 0.8, @@ -81,9 +73,7 @@ export async function pushDataListToTrainingQueue({ return Promise.reject(`Training mode "${trainingMode}" is inValid`); }; - const { model, maxToken, weight } = await checkModelValid({ - collectionId - }); + const { model, maxToken, weight } = await checkModelValid(); // format q and a, remove empty char data.forEach((item) => { diff --git a/packages/service/core/dataset/training/schema.ts b/packages/service/core/dataset/training/schema.ts index e9c74656368..66662da883e 100644 --- a/packages/service/core/dataset/training/schema.ts +++ b/packages/service/core/dataset/training/schema.ts @@ -2,7 +2,7 @@ import { connectionMongo, type Model } from '../../../common/mongo'; const { Schema, model, models } = connectionMongo; import { DatasetTrainingSchemaType } from '@fastgpt/global/core/dataset/type'; -import { DatasetDataIndexTypeMap, TrainingTypeMap } from '@fastgpt/global/core/dataset/constants'; +import { TrainingTypeMap } from '@fastgpt/global/core/dataset/constants'; import { DatasetColCollectionName } from '../collection/schema'; import { DatasetCollectionName } from '../schema'; import { @@ -86,11 +86,6 @@ const TrainingDataSchema = new Schema({ indexes: { type: [ { - type: { - type: String, - enum: Object.keys(DatasetDataIndexTypeMap), - required: true - }, text: { type: String, required: true diff --git a/packages/service/package.json b/packages/service/package.json index aff31afd55f..8b85872f335 100644 --- a/packages/service/package.json +++ b/packages/service/package.json @@ -15,7 +15,7 @@ "nextjs-cors": "^2.1.2", "node-cron": "^3.0.3", "pg": "^8.10.0", - "date-fns": "^2.30.0", + "date-fns": "2.30.0", "tunnel": "^0.0.6" }, "devDependencies": { diff --git a/packages/service/support/openapi/auth.ts b/packages/service/support/openapi/auth.ts index cfce33e3d0f..a66c684b74d 100644 --- a/packages/service/support/openapi/auth.ts +++ b/packages/service/support/openapi/auth.ts @@ -19,14 +19,15 @@ export async function authOpenApiKey({ apikey }: { apikey: string }) { // auth limit // @ts-ignore if (global.feConfigs?.isPlus) { - await POST('/support/openapi/authLimit', { openApi } as AuthOpenApiLimitProps); + await POST('/support/openapi/authLimit', { + openApi: openApi.toObject() + } as AuthOpenApiLimitProps); } updateApiKeyUsedTime(openApi._id); return { apikey, - userId: String(openApi.userId), teamId: String(openApi.teamId), tmbId: String(openApi.tmbId), appId: openApi.appId || '' diff --git a/packages/service/support/openapi/schema.ts b/packages/service/support/openapi/schema.ts index 6eca9c95fc3..dff9af5a4f4 100644 --- a/packages/service/support/openapi/schema.ts +++ b/packages/service/support/openapi/schema.ts @@ -1,8 +1,6 @@ import { connectionMongo, type Model } from '../../common/mongo'; const { Schema, model, models } = connectionMongo; import type { OpenApiSchema } from '@fastgpt/global/support/openapi/type'; -import { PRICE_SCALE } from '@fastgpt/global/support/wallet/bill/constants'; -import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools'; import { TeamCollectionName, TeamMemberCollectionName @@ -10,10 +8,6 @@ import { const OpenApiSchema = new Schema( { - userId: { - type: Schema.Types.ObjectId, - ref: 'user' - }, teamId: { type: Schema.Types.ObjectId, ref: TeamCollectionName, @@ -44,22 +38,17 @@ const OpenApiSchema = new Schema( type: String, default: 'Api Key' }, - usage: { - // total usage. value from bill total + usagePoints: { type: Number, - default: 0, - get: (val: number) => formatStorePrice2Read(val) + default: 0 }, limit: { expiredTime: { type: Date }, - credit: { - // value from user settings + maxUsagePoints: { type: Number, - default: -1, - set: (val: number) => val * PRICE_SCALE, - get: (val: number) => formatStorePrice2Read(val) + default: -1 } } }, diff --git a/packages/service/support/openapi/tools.ts b/packages/service/support/openapi/tools.ts index b6bedee1ea2..d9e0946e8aa 100644 --- a/packages/service/support/openapi/tools.ts +++ b/packages/service/support/openapi/tools.ts @@ -8,15 +8,21 @@ export function updateApiKeyUsedTime(id: string) { }); } -export function updateApiKeyUsage({ apikey, usage }: { apikey: string; usage: number }) { +export function updateApiKeyUsage({ + apikey, + totalPoints +}: { + apikey: string; + totalPoints: number; +}) { MongoOpenApi.findOneAndUpdate( { apiKey: apikey }, { $inc: { - usage + usagePoints: totalPoints } } ).catch((err) => { - console.log('update apiKey usage error', err); + console.log('update apiKey totalPoints error', err); }); } diff --git a/packages/service/support/outLink/schema.ts b/packages/service/support/outLink/schema.ts index acb7526ab43..3d6d1848271 100644 --- a/packages/service/support/outLink/schema.ts +++ b/packages/service/support/outLink/schema.ts @@ -35,8 +35,7 @@ const OutLinkSchema = new Schema({ type: String, required: true }, - total: { - // total amount + usagePoints: { type: Number, default: 0 }, @@ -48,6 +47,10 @@ const OutLinkSchema = new Schema({ default: false }, limit: { + maxUsagePoints: { + type: Number, + default: -1 + }, expiredTime: { type: Date }, @@ -55,16 +58,18 @@ const OutLinkSchema = new Schema({ type: Number, default: 1000 }, - credit: { - type: Number, - default: -1 - }, hookUrl: { type: String } } }); +try { + OutLinkSchema.index({ shareId: -1 }); +} catch (error) { + console.log(error); +} + export const MongoOutLink: Model = models['outlinks'] || model('outlinks', OutLinkSchema); diff --git a/packages/service/support/outLink/tools.ts b/packages/service/support/outLink/tools.ts index 3b9afa32d6b..878234c639c 100644 --- a/packages/service/support/outLink/tools.ts +++ b/packages/service/support/outLink/tools.ts @@ -1,18 +1,19 @@ import axios from 'axios'; import { MongoOutLink } from './schema'; import { FastGPTProUrl } from '../../common/system/constants'; +import { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type'; -export const updateOutLinkUsage = async ({ +export const addOutLinkUsage = async ({ shareId, - total + totalPoints }: { shareId: string; - total: number; + totalPoints: number; }) => { MongoOutLink.findOneAndUpdate( { shareId }, { - $inc: { total }, + $inc: { usagePoints: totalPoints }, lastTime: new Date() } ).catch((err) => { @@ -23,11 +24,13 @@ export const updateOutLinkUsage = async ({ export const pushResult2Remote = async ({ outLinkUid, shareId, + appName, responseData }: { outLinkUid?: string; // raw id, not parse shareId?: string; - responseData?: any[]; + appName: string; + responseData?: ChatHistoryItemResType[]; }) => { if (!shareId || !outLinkUid || !FastGPTProUrl) return; try { @@ -42,6 +45,7 @@ export const pushResult2Remote = async ({ url: '/shareAuth/finish', data: { token: outLinkUid, + appName, responseData } }); diff --git a/packages/service/support/permission/auth/dataset.ts b/packages/service/support/permission/auth/dataset.ts index c793714eb74..e55ed064dda 100644 --- a/packages/service/support/permission/auth/dataset.ts +++ b/packages/service/support/permission/auth/dataset.ts @@ -107,7 +107,7 @@ export async function authDatasetCollection({ collection: CollectionWithDatasetType; } > { - const { userId, teamId, tmbId } = await parseHeaderCert(props); + const { teamId, tmbId } = await parseHeaderCert(props); const { role } = await getTmbInfoByTmbId({ tmbId }); const { collection, isOwner, canWrite } = await (async () => { @@ -143,7 +143,6 @@ export async function authDatasetCollection({ })(); return { - userId, teamId, tmbId, collection, @@ -163,7 +162,7 @@ export async function authDatasetFile({ file: DatasetFileSchema; } > { - const { userId, teamId, tmbId } = await parseHeaderCert(props); + const { teamId, tmbId } = await parseHeaderCert(props); const [file, collection] = await Promise.all([ getFileById({ bucketName: BucketNameEnum.dataset, fileId }), @@ -190,7 +189,6 @@ export async function authDatasetFile({ }); return { - userId, teamId, tmbId, file, @@ -200,4 +198,4 @@ export async function authDatasetFile({ } catch (error) { return Promise.reject(DatasetErrEnum.unAuthDatasetFile); } -} +} \ No newline at end of file diff --git a/packages/service/support/permission/auth/user.ts b/packages/service/support/permission/auth/user.ts index e8da3207299..1eb506f3faf 100644 --- a/packages/service/support/permission/auth/user.ts +++ b/packages/service/support/permission/auth/user.ts @@ -12,7 +12,7 @@ export async function authUserNotVisitor(props: AuthModeType): Promise< role: `${TeamMemberRoleEnum}`; } > { - const { userId, teamId, tmbId } = await parseHeaderCert(props); + const { teamId, tmbId } = await parseHeaderCert(props); const team = await getTmbInfoByTmbId({ tmbId }); if (team.role === TeamMemberRoleEnum.visitor) { @@ -20,7 +20,6 @@ export async function authUserNotVisitor(props: AuthModeType): Promise< } return { - userId, teamId, tmbId, team, diff --git a/packages/service/support/permission/controller.ts b/packages/service/support/permission/controller.ts index 56afce1ac45..fb73b7107d2 100644 --- a/packages/service/support/permission/controller.ts +++ b/packages/service/support/permission/controller.ts @@ -94,10 +94,10 @@ export async function parseHeaderCert({ })(); // auth apikey - const { userId, teamId, tmbId, appId: apiKeyAppId = '' } = await authOpenApiKey({ apikey }); + const { teamId, tmbId, appId: apiKeyAppId = '' } = await authOpenApiKey({ apikey }); return { - uid: userId, + uid: '', teamId, tmbId, apikey, @@ -217,4 +217,4 @@ export const authFileToken = (token?: string) => fileId: decoded.fileId }); }); - }); + }); \ No newline at end of file diff --git a/packages/service/support/permission/limit/dataset.ts b/packages/service/support/permission/limit/dataset.ts deleted file mode 100644 index b6e8a18c984..00000000000 --- a/packages/service/support/permission/limit/dataset.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { StandSubPlanLevelMapType } from '@fastgpt/global/support/wallet/sub/type'; -import { getVectorCountByTeamId } from '../../../common/vectorStore/controller'; -import { getTeamDatasetMaxSize } from '../../wallet/sub/utils'; - -export const checkDatasetLimit = async ({ - teamId, - insertLen = 0, - standardPlans -}: { - teamId: string; - insertLen?: number; - standardPlans?: StandSubPlanLevelMapType; -}) => { - const [{ maxSize }, usedSize] = await Promise.all([ - getTeamDatasetMaxSize({ teamId, standardPlans }), - getVectorCountByTeamId(teamId) - ]); - - if (usedSize + insertLen >= maxSize) { - return Promise.reject(`数据库容量不足,无法继续添加。可以在账号页面进行扩容。`); - } - return; -}; diff --git a/packages/service/support/permission/teamLimit.ts b/packages/service/support/permission/teamLimit.ts new file mode 100644 index 00000000000..6224b56dff6 --- /dev/null +++ b/packages/service/support/permission/teamLimit.ts @@ -0,0 +1,91 @@ +import { getVectorCountByTeamId } from '../../common/vectorStore/controller'; +import { getTeamPlanStatus, getTeamStandPlan } from '../../support/wallet/sub/utils'; +import { MongoApp } from '../../core/app/schema'; +import { MongoPlugin } from '../../core/plugin/schema'; +import { MongoDataset } from '../../core/dataset/schema'; +import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants'; +import { TeamErrEnum } from '@fastgpt/global/common/error/code/team'; + +export const checkDatasetLimit = async ({ + teamId, + insertLen = 0 +}: { + teamId: string; + insertLen?: number; +}) => { + const [{ standardConstants, totalPoints, usedPoints, datasetMaxSize }, usedSize] = + await Promise.all([getTeamPlanStatus({ teamId }), getVectorCountByTeamId(teamId)]); + + if (!standardConstants) return; + + if (usedSize + insertLen >= datasetMaxSize) { + return Promise.reject(TeamErrEnum.datasetSizeNotEnough); + } + + if (usedPoints >= totalPoints) { + return Promise.reject(TeamErrEnum.aiPointsNotEnough); + } + return; +}; + +export const checkTeamAIPoints = async (teamId: string) => { + const { standardConstants, totalPoints, usedPoints } = await getTeamPlanStatus({ + teamId + }); + + if (!standardConstants) return; + + if (usedPoints >= totalPoints) { + return Promise.reject(TeamErrEnum.aiPointsNotEnough); + } + + return { + totalPoints, + usedPoints + }; +}; + +export const checkTeamDatasetLimit = async (teamId: string) => { + const [{ standardConstants }, datasetCount] = await Promise.all([ + getTeamStandPlan({ teamId }), + MongoDataset.countDocuments({ + teamId, + type: { $ne: DatasetTypeEnum.folder } + }) + ]); + + if (standardConstants && datasetCount >= standardConstants.maxDatasetAmount) { + return Promise.reject(TeamErrEnum.datasetAmountNotEnough); + } +}; +export const checkTeamAppLimit = async (teamId: string) => { + const [{ standardConstants }, appCount] = await Promise.all([ + getTeamStandPlan({ teamId }), + MongoApp.count({ teamId }) + ]); + + if (standardConstants && appCount >= standardConstants.maxAppAmount) { + return Promise.reject(TeamErrEnum.appAmountNotEnough); + } +}; +export const checkTeamPluginLimit = async (teamId: string) => { + const [{ standardConstants }, pluginCount] = await Promise.all([ + getTeamStandPlan({ teamId }), + MongoPlugin.count({ teamId }) + ]); + + if (standardConstants && pluginCount >= standardConstants.maxAppAmount) { + return Promise.reject(TeamErrEnum.pluginAmountNotEnough); + } +}; + +export const checkTeamReRankPermission = async (teamId: string) => { + const { standardConstants } = await getTeamStandPlan({ + teamId + }); + + if (standardConstants && !standardConstants?.permissionReRank) { + return false; + } + return true; +}; diff --git a/packages/service/support/user/controller.ts b/packages/service/support/user/controller.ts index 361a9029b4c..5765df3a75d 100644 --- a/packages/service/support/user/controller.ts +++ b/packages/service/support/user/controller.ts @@ -2,7 +2,6 @@ import { UserType } from '@fastgpt/global/support/user/type'; import { MongoUser } from './schema'; import { getTmbInfoByTmbId, getUserDefaultTeam } from './team/controller'; import { ERROR_ENUM } from '@fastgpt/global/common/error/errorCode'; -import { UserErrEnum } from '@fastgpt/global/common/error/code/user'; export async function authUserExist({ userId, username }: { userId?: string; username?: string }) { if (userId) { @@ -47,22 +46,3 @@ export async function getUserDetail({ team: tmb }; } - -export async function getUserAndAuthBalance({ - tmbId, - minBalance -}: { - tmbId: string; - minBalance?: number; -}) { - const user = await getUserDetail({ tmbId }); - - if (!user) { - return Promise.reject(UserErrEnum.unAuthUser); - } - if (minBalance !== undefined && user.team.balance < minBalance) { - return Promise.reject(UserErrEnum.balanceNotEnough); - } - - return user; -} diff --git a/packages/service/support/user/schema.ts b/packages/service/support/user/schema.ts index 15e75c491bb..496726860d3 100644 --- a/packages/service/support/user/schema.ts +++ b/packages/service/support/user/schema.ts @@ -1,7 +1,7 @@ import { connectionMongo, type Model } from '../../common/mongo'; const { Schema, model, models } = connectionMongo; import { hashStr } from '@fastgpt/global/common/string/tools'; -import { PRICE_SCALE } from '@fastgpt/global/support/wallet/bill/constants'; +import { PRICE_SCALE } from '@fastgpt/global/support/wallet/constants'; import type { UserModelSchema } from '@fastgpt/global/support/user/type'; import { UserStatusEnum, userStatusMap } from '@fastgpt/global/support/user/constant'; @@ -63,6 +63,8 @@ const UserSchema = new Schema({ }); try { + // login + UserSchema.index({ username: 1, password: 1 }); UserSchema.index({ createTime: -1 }); } catch (error) { console.log(error); diff --git a/packages/service/support/user/team/controller.ts b/packages/service/support/user/team/controller.ts index 042cd238812..17e27ab5f96 100644 --- a/packages/service/support/user/team/controller.ts +++ b/packages/service/support/user/team/controller.ts @@ -36,7 +36,7 @@ export async function getTmbInfoByTmbId({ tmbId }: { tmbId: string }) { return Promise.reject('tmbId or userId is required'); } return getTeamMember({ - _id: new Types.ObjectId(tmbId), + _id: new Types.ObjectId(String(tmbId)), status: notLeaveStatus }); } diff --git a/packages/service/support/user/team/teamSchema.ts b/packages/service/support/user/team/teamSchema.ts index 3121b92aefe..5a358b4097e 100644 --- a/packages/service/support/user/team/teamSchema.ts +++ b/packages/service/support/user/team/teamSchema.ts @@ -27,7 +27,10 @@ const TeamSchema = new Schema({ }, maxSize: { type: Number, - default: 3 + default: 1 + }, + tagsUrl: { + type: String }, limit: { lastExportDatasetTime: { diff --git a/packages/service/support/user/team/teamTagsSchema.ts b/packages/service/support/user/team/teamTagsSchema.ts new file mode 100644 index 00000000000..f1c1044e6df --- /dev/null +++ b/packages/service/support/user/team/teamTagsSchema.ts @@ -0,0 +1,35 @@ +import { connectionMongo, type Model } from '../../../common/mongo'; +const { Schema, model, models } = connectionMongo; +import { TeamTagsSchema as TeamTagsSchemaType } from '@fastgpt/global/support/user/team/type.d'; +import { + TeamCollectionName, + TeamTagsCollectionName +} from '@fastgpt/global/support/user/team/constant'; + +const TeamTagsSchema = new Schema({ + label: { + type: String, + required: true + }, + teamId: { + type: Schema.Types.ObjectId, + ref: TeamCollectionName, + required: true + }, + key: { + type: String + }, + createTime: { + type: Date, + default: () => new Date() + } +}); + +try { + TeamTagsSchema.index({ teamId: 1 }); +} catch (error) { + console.log(error); +} + +export const MongoTeamTags: Model = + models[TeamTagsCollectionName] || model(TeamTagsCollectionName, TeamTagsSchema); diff --git a/packages/service/support/wallet/sub/schema.ts b/packages/service/support/wallet/sub/schema.ts index 2d2258070dc..b90cd9b4baf 100644 --- a/packages/service/support/wallet/sub/schema.ts +++ b/packages/service/support/wallet/sub/schema.ts @@ -1,3 +1,8 @@ +/* + user sub plan + 1. type=standard: There will only be 1, and each team will have one + 2. type=extraDatasetSize/extraPoints: Can buy multiple +*/ import { connectionMongo, type Model } from '../../../common/mongo'; const { Schema, model, models } = connectionMongo; import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant'; @@ -23,25 +28,10 @@ const SubSchema = new Schema({ required: true }, status: { - // active: continue sub; canceled: canceled sub; type: String, enum: Object.keys(subStatusMap), required: true }, - mode: { - type: String, - enum: Object.keys(subModeMap) - }, - currentMode: { - type: String, - enum: Object.keys(subModeMap), - required: true - }, - nextMode: { - type: String, - enum: Object.keys(subModeMap), - required: true - }, startTime: { type: Date, default: () => new Date() @@ -55,12 +45,16 @@ const SubSchema = new Schema({ type: Number, required: true }, - pointPrice: { - // stand level point total price - type: Number - }, - // sub content + // standard sub + currentMode: { + type: String, + enum: Object.keys(subModeMap) + }, + nextMode: { + type: String, + enum: Object.keys(subModeMap) + }, currentSubLevel: { type: String, enum: Object.keys(standardSubLevelMap) @@ -69,79 +63,32 @@ const SubSchema = new Schema({ type: String, enum: Object.keys(standardSubLevelMap) }, - totalPoints: { - type: Number - }, - - currentExtraDatasetSize: { - type: Number - }, - nextExtraDatasetSize: { - type: Number - }, - currentExtraPoints: { + // stand sub and extra points sub. Plan total points + totalPoints: { type: Number }, - nextExtraPoints: { + pointPrice: { + // stand level point total price type: Number }, - - // standard sub limit - // maxTeamMember: { - // type: Number - // }, - // maxAppAmount: { - // type: Number - // }, - // maxDatasetAmount: { - // type: Number - // }, - // chatHistoryStoreDuration: { - // // n day - // type: Number - // }, - // maxDatasetSize: { - // type: Number - // }, - // trainingWeight: { - // // 0 1 2 3 - // type: Number - // }, - // customApiKey: { - // type: Boolean - // }, - // customCopyright: { - // type: Boolean - // }, - // websiteSyncInterval: { - // // hours - // type: Number - // }, - // reRankWeight: { - // // 0 1 2 3 - // type: Number - // }, - // totalPoints: { - // // record standard sub points - // type: Number - // }, - surplusPoints: { - // standard sub / extra points sub + // plan surplus points type: Number }, - // abandon - renew: Boolean, //决定是否续费 - datasetStoreAmount: Number + // extra dataset size + currentExtraDatasetSize: { + type: Number + } }); try { - SubSchema.index({ teamId: 1 }); - SubSchema.index({ status: 1 }); - SubSchema.index({ type: 1 }); - SubSchema.index({ expiredTime: -1 }); + // get team plan + SubSchema.index({ teamId: 1, type: 1, expiredTime: -1 }); + + // timer task. check expired plan; update standard plan; + SubSchema.index({ type: 1, currentSubLevel: 1, expiredTime: -1 }); } catch (error) { console.log(error); } diff --git a/packages/service/support/wallet/sub/utils.ts b/packages/service/support/wallet/sub/utils.ts index feb29c8ffe2..75fc803a8e7 100644 --- a/packages/service/support/wallet/sub/utils.ts +++ b/packages/service/support/wallet/sub/utils.ts @@ -1,87 +1,147 @@ -import { SubTypeEnum } from '@fastgpt/global/support/wallet/sub/constants'; +import { + StandardSubLevelEnum, + SubModeEnum, + SubStatusEnum, + SubTypeEnum +} from '@fastgpt/global/support/wallet/sub/constants'; import { MongoTeamSub } from './schema'; -import { addHours } from 'date-fns'; -import { FeTeamSubType, StandSubPlanLevelMapType } from '@fastgpt/global/support/wallet/sub/type.d'; +import { FeTeamPlanStatusType } from '@fastgpt/global/support/wallet/sub/type.d'; import { getVectorCountByTeamId } from '../../../common/vectorStore/controller'; +import dayjs from 'dayjs'; +import { ClientSession } from '../../../common/mongo'; +import { addMonths } from 'date-fns'; -/* get team dataset max size */ -export const getTeamDatasetMaxSize = async ({ +export const getStandardPlans = () => { + return global?.subPlans?.standard; +}; +export const getStandardPlan = (level: `${StandardSubLevelEnum}`) => { + return global.subPlans?.standard?.[level]; +}; + +export const getTeamStandPlan = async ({ teamId }: { teamId: string }) => { + const standardPlans = global.subPlans?.standard; + const standard = await MongoTeamSub.findOne({ teamId, type: SubTypeEnum.standard }).lean(); + + return { + [SubTypeEnum.standard]: standard, + standardConstants: + standard?.currentSubLevel && standardPlans + ? standardPlans[standard.currentSubLevel] + : undefined + }; +}; + +export const initTeamStandardPlan2Free = async ({ teamId, - standardPlans + session }: { teamId: string; - standardPlans?: StandSubPlanLevelMapType; + session?: ClientSession; }) => { - if (!standardPlans) { - return { - maxSize: Infinity, - sub: null - }; - } + const freePoints = global?.subPlans?.standard?.free?.totalPoints || 100; - const plans = await MongoTeamSub.find({ - teamId, - expiredTime: { $gte: addHours(new Date(), -3) } - }).lean(); + const teamStandardSub = await MongoTeamSub.findOne({ teamId, type: SubTypeEnum.standard }); - const standard = plans.find((plan) => plan.type === SubTypeEnum.standard); - const extraDatasetSize = plans.find((plan) => plan.type === SubTypeEnum.extraDatasetSize); + if (teamStandardSub) { + teamStandardSub.status = SubStatusEnum.active; + teamStandardSub.currentMode = SubModeEnum.month; + teamStandardSub.nextMode = SubModeEnum.month; + teamStandardSub.startTime = new Date(); + teamStandardSub.expiredTime = addMonths(new Date(), 1); - const standardMaxDatasetSize = - standard?.currentSubLevel && standardPlans - ? standardPlans[standard.currentSubLevel]?.maxDatasetSize || Infinity - : Infinity; - const totalDatasetSize = - standardMaxDatasetSize + (extraDatasetSize?.currentExtraDatasetSize || 0); + teamStandardSub.currentSubLevel = StandardSubLevelEnum.free; + teamStandardSub.nextSubLevel = StandardSubLevelEnum.free; - return { - maxSize: totalDatasetSize, - sub: extraDatasetSize - }; + teamStandardSub.price = 0; + teamStandardSub.pointPrice = 0; + + teamStandardSub.totalPoints = freePoints; + teamStandardSub.surplusPoints = + teamStandardSub.surplusPoints && teamStandardSub.surplusPoints < 0 + ? teamStandardSub.surplusPoints + freePoints + : freePoints; + return teamStandardSub.save({ session }); + } + + return MongoTeamSub.create( + [ + { + teamId, + type: SubTypeEnum.standard, + status: SubStatusEnum.active, + currentMode: SubModeEnum.month, + nextMode: SubModeEnum.month, + startTime: new Date(), + expiredTime: addMonths(new Date(), 1), + price: 0, + pointPrice: 0, + + currentSubLevel: StandardSubLevelEnum.free, + nextSubLevel: StandardSubLevelEnum.free, + + totalPoints: freePoints, + surplusPoints: freePoints + } + ], + { session } + ); }; -export const getTeamSubPlanStatus = async ({ - teamId, - standardPlans +export const getTeamPlanStatus = async ({ + teamId }: { teamId: string; - standardPlans?: StandSubPlanLevelMapType; -}): Promise => { +}): Promise => { + const standardPlans = global.subPlans?.standard; + const [plans, usedDatasetSize] = await Promise.all([ MongoTeamSub.find({ teamId }).lean(), getVectorCountByTeamId(teamId) ]); const standard = plans.find((plan) => plan.type === SubTypeEnum.standard); - const extraDatasetSize = plans.find((plan) => plan.type === SubTypeEnum.extraDatasetSize); - const extraPoints = plans.find((plan) => plan.type === SubTypeEnum.extraPoints); + const extraDatasetSize = plans.filter((plan) => plan.type === SubTypeEnum.extraDatasetSize); + const extraPoints = plans.filter((plan) => plan.type === SubTypeEnum.extraPoints); + + // Free user, first login after expiration. The free subscription plan will be reset + if ( + standard && + standard.expiredTime && + standard.currentSubLevel === StandardSubLevelEnum.free && + dayjs(standard.expiredTime).isBefore(new Date()) + ) { + console.log('Init free stand plan', { teamId }); + await initTeamStandardPlan2Free({ teamId }); + return getTeamPlanStatus({ teamId }); + } + + const totalPoints = standardPlans + ? (standard?.totalPoints || 0) + + extraPoints.reduce((acc, cur) => acc + (cur.totalPoints || 0), 0) + : Infinity; + const surplusPoints = + (standard?.surplusPoints || 0) + + extraPoints.reduce((acc, cur) => acc + (cur.surplusPoints || 0), 0); const standardMaxDatasetSize = standard?.currentSubLevel && standardPlans ? standardPlans[standard.currentSubLevel]?.maxDatasetSize || Infinity : Infinity; const totalDatasetSize = - standardMaxDatasetSize + (extraDatasetSize?.currentExtraDatasetSize || 0); - - const standardMaxPoints = - standard?.currentSubLevel && standardPlans - ? standardPlans[standard.currentSubLevel]?.totalPoints || Infinity - : Infinity; - const totalPoints = standardMaxPoints + (extraPoints?.currentExtraPoints || 0); - - const surplusPoints = (standard?.surplusPoints || 0) + (extraPoints?.surplusPoints || 0); + standardMaxDatasetSize + + extraDatasetSize.reduce((acc, cur) => acc + (cur.currentExtraDatasetSize || 0), 0); return { [SubTypeEnum.standard]: standard, - [SubTypeEnum.extraDatasetSize]: extraDatasetSize, - [SubTypeEnum.extraPoints]: extraPoints, + standardConstants: + standard?.currentSubLevel && standardPlans + ? standardPlans[standard.currentSubLevel] + : undefined, - standardMaxDatasetSize, - datasetMaxSize: totalDatasetSize, - usedDatasetSize, - - standardMaxPoints, totalPoints, - usedPoints: totalPoints - surplusPoints + usedPoints: totalPoints - surplusPoints, + + datasetMaxSize: totalDatasetSize, + usedDatasetSize }; }; diff --git a/packages/service/support/wallet/bill/controller.ts b/packages/service/support/wallet/usage/controller.ts similarity index 62% rename from packages/service/support/wallet/bill/controller.ts rename to packages/service/support/wallet/usage/controller.ts index 3528159a02f..43e9534aeec 100644 --- a/packages/service/support/wallet/bill/controller.ts +++ b/packages/service/support/wallet/usage/controller.ts @@ -1,8 +1,8 @@ -import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants'; -import { MongoBill } from './schema'; +import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants'; +import { MongoUsage } from './schema'; import { ClientSession } from '../../../common/mongo'; -export const createTrainingBill = async ({ +export const createTrainingUsage = async ({ teamId, tmbId, appName, @@ -14,33 +14,33 @@ export const createTrainingBill = async ({ teamId: string; tmbId: string; appName: string; - billSource: `${BillSourceEnum}`; + billSource: `${UsageSourceEnum}`; vectorModel: string; agentModel: string; session?: ClientSession; }) => { - const [{ _id }] = await MongoBill.create( + const [{ _id }] = await MongoUsage.create( [ { teamId, tmbId, appName, source: billSource, + totalPoints: 0, list: [ { - moduleName: 'wallet.moduleName.index', + moduleName: 'support.wallet.moduleName.index', model: vectorModel, charsLength: 0, amount: 0 }, { - moduleName: 'wallet.moduleName.qa', + moduleName: 'support.wallet.moduleName.qa', model: agentModel, charsLength: 0, amount: 0 } - ], - total: 0 + ] } ], { session } diff --git a/packages/service/support/wallet/bill/schema.ts b/packages/service/support/wallet/usage/schema.ts similarity index 50% rename from packages/service/support/wallet/bill/schema.ts rename to packages/service/support/wallet/usage/schema.ts index c4d7bef386f..e6c39355d7e 100644 --- a/packages/service/support/wallet/bill/schema.ts +++ b/packages/service/support/wallet/usage/schema.ts @@ -1,13 +1,15 @@ import { connectionMongo, type Model } from '../../../common/mongo'; const { Schema, model, models } = connectionMongo; -import { BillSchema as BillType } from '@fastgpt/global/support/wallet/bill/type'; -import { BillSourceMap } from '@fastgpt/global/support/wallet/bill/constants'; +import { UsageSchemaType } from '@fastgpt/global/support/wallet/usage/type'; +import { UsageSourceMap } from '@fastgpt/global/support/wallet/usage/constants'; import { TeamCollectionName, TeamMemberCollectionName } from '@fastgpt/global/support/user/team/constant'; -const BillSchema = new Schema({ +export const UsageCollectionName = 'usages'; + +const UsageSchema = new Schema({ teamId: { type: Schema.Types.ObjectId, ref: TeamCollectionName, @@ -18,6 +20,11 @@ const BillSchema = new Schema({ ref: TeamMemberCollectionName, required: true }, + source: { + type: String, + enum: Object.keys(UsageSourceMap), + required: true + }, appName: { type: String, default: '' @@ -31,16 +38,16 @@ const BillSchema = new Schema({ type: Date, default: () => new Date() }, - total: { - // 1 * PRICE_SCALE + totalPoints: { + // total points type: Number, required: true }, - source: { - type: String, - enum: Object.keys(BillSourceMap), - required: true - }, + // total: { + // // total points + // type: Number, + // required: true + // }, list: { type: Array, default: [] @@ -48,11 +55,15 @@ const BillSchema = new Schema({ }); try { - BillSchema.index({ teamId: 1, tmbId: 1, source: 1, time: -1 }, { background: true }); - BillSchema.index({ time: 1 }, { expireAfterSeconds: 180 * 24 * 60 * 60 }); + UsageSchema.index({ teamId: 1, tmbId: 1, source: 1, time: -1 }, { background: true }); + // timer task. clear dead team + UsageSchema.index({ teamId: 1, time: -1 }, { background: true }); + + UsageSchema.index({ time: 1 }, { expireAfterSeconds: 180 * 24 * 60 * 60 }); } catch (error) { console.log(error); } -export const MongoBill: Model = models['bill'] || model('bill', BillSchema); -MongoBill.syncIndexes(); +export const MongoUsage: Model = + models[UsageCollectionName] || model(UsageCollectionName, UsageSchema); +MongoUsage.syncIndexes(); diff --git a/packages/service/type.d.ts b/packages/service/type.d.ts index 0f943512518..616f6c837a1 100644 --- a/packages/service/type.d.ts +++ b/packages/service/type.d.ts @@ -1,3 +1,20 @@ +import { FastGPTFeConfigsType } from '@fastgpt/global/common/system/types'; +import { + AudioSpeechModelType, + ReRankModelItemType, + WhisperModelType, + VectorModelItemType, + LLMModelItemType +} from '@fastgpt/global/core/ai/model.d'; +import { SubPlanType } from '@fastgpt/global/support/wallet/sub/type'; + declare global { - var defaultTeamDatasetLimit: number; + var feConfigs: FastGPTFeConfigsType; + var subPlans: SubPlanType | undefined; + + var llmModels: LLMModelItemType[]; + var vectorModels: VectorModelItemType[]; + var audioSpeechModels: AudioSpeechModelType[]; + var whisperModel: WhisperModelType; + var reRankModels: ReRankModelItemType[]; } diff --git a/packages/web/components/common/Icon/constants.ts b/packages/web/components/common/Icon/constants.ts index 4642d8e3841..60195ca81e9 100644 --- a/packages/web/components/common/Icon/constants.ts +++ b/packages/web/components/common/Icon/constants.ts @@ -128,10 +128,12 @@ export const iconPaths = { kbTest: () => import('./icons/kbTest.svg'), menu: () => import('./icons/menu.svg'), minus: () => import('./icons/minus.svg'), + 'modal/confirmPay': () => import('./icons/modal/confirmPay.svg'), 'modal/edit': () => import('./icons/modal/edit.svg'), 'modal/manualDataset': () => import('./icons/modal/manualDataset.svg'), 'modal/selectSource': () => import('./icons/modal/selectSource.svg'), 'modal/setting': () => import('./icons/modal/setting.svg'), + 'modal/teamPlans': () => import('./icons/modal/teamPlans.svg'), more: () => import('./icons/more.svg'), out: () => import('./icons/out.svg'), 'phoneTabbar/me': () => import('./icons/phoneTabbar/me.svg'), @@ -142,20 +144,23 @@ export const iconPaths = { save: () => import('./icons/save.svg'), stop: () => import('./icons/stop.svg'), 'support/account/loginoutLight': () => import('./icons/support/account/loginoutLight.svg'), + 'support/account/plans': () => import('./icons/support/account/plans.svg'), 'support/account/promotionLight': () => import('./icons/support/account/promotionLight.svg'), - 'support/bill/billRecordLight': () => import('./icons/support/bill/billRecordLight.svg'), + 'support/bill/extraDatasetsize': () => import('./icons/support/bill/extraDatasetsize.svg'), + 'support/bill/extraPoints': () => import('./icons/support/bill/extraPoints.svg'), + 'support/bill/payRecordLight': () => import('./icons/support/bill/payRecordLight.svg'), + 'support/bill/priceLight': () => import('./icons/support/bill/priceLight.svg'), + 'support/bill/shoppingCart': () => import('./icons/support/bill/shoppingCart.svg'), 'support/outlink/apikeyFill': () => import('./icons/support/outlink/apikeyFill.svg'), 'support/outlink/apikeyLight': () => import('./icons/support/outlink/apikeyLight.svg'), 'support/outlink/iframeLight': () => import('./icons/support/outlink/iframeLight.svg'), 'support/outlink/share': () => import('./icons/support/outlink/share.svg'), 'support/outlink/shareLight': () => import('./icons/support/outlink/shareLight.svg'), - 'support/pay/extraDatasetsize': () => import('./icons/support/pay/extraDatasetsize.svg'), - 'support/pay/extraPoints': () => import('./icons/support/pay/extraPoints.svg'), - 'support/pay/payRecordLight': () => import('./icons/support/pay/payRecordLight.svg'), - 'support/pay/priceLight': () => import('./icons/support/pay/priceLight.svg'), 'support/permission/privateLight': () => import('./icons/support/permission/privateLight.svg'), 'support/permission/publicLight': () => import('./icons/support/permission/publicLight.svg'), 'support/team/memberLight': () => import('./icons/support/team/memberLight.svg'), + 'support/usage/usageRecordLight': () => import('./icons/support/usage/usageRecordLight.svg'), + 'support/user/individuation': () => import('./icons/support/user/individuation.svg'), 'support/user/informLight': () => import('./icons/support/user/informLight.svg'), 'support/user/userFill': () => import('./icons/support/user/userFill.svg'), 'support/user/userLight': () => import('./icons/support/user/userLight.svg'), diff --git a/packages/web/components/common/Icon/icons/modal/confirmPay.svg b/packages/web/components/common/Icon/icons/modal/confirmPay.svg new file mode 100644 index 00000000000..bc4f40f6e1e --- /dev/null +++ b/packages/web/components/common/Icon/icons/modal/confirmPay.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/packages/web/components/common/Icon/icons/modal/teamPlans.svg b/packages/web/components/common/Icon/icons/modal/teamPlans.svg new file mode 100644 index 00000000000..c38260712d8 --- /dev/null +++ b/packages/web/components/common/Icon/icons/modal/teamPlans.svg @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/packages/web/components/common/Icon/icons/support/account/plans.svg b/packages/web/components/common/Icon/icons/support/account/plans.svg new file mode 100644 index 00000000000..e8aa856725d --- /dev/null +++ b/packages/web/components/common/Icon/icons/support/account/plans.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/web/components/common/Icon/icons/support/pay/extraDatasetsize.svg b/packages/web/components/common/Icon/icons/support/bill/extraDatasetsize.svg similarity index 100% rename from packages/web/components/common/Icon/icons/support/pay/extraDatasetsize.svg rename to packages/web/components/common/Icon/icons/support/bill/extraDatasetsize.svg diff --git a/packages/web/components/common/Icon/icons/support/pay/extraPoints.svg b/packages/web/components/common/Icon/icons/support/bill/extraPoints.svg similarity index 100% rename from packages/web/components/common/Icon/icons/support/pay/extraPoints.svg rename to packages/web/components/common/Icon/icons/support/bill/extraPoints.svg diff --git a/packages/web/components/common/Icon/icons/support/pay/payRecordLight.svg b/packages/web/components/common/Icon/icons/support/bill/payRecordLight.svg similarity index 100% rename from packages/web/components/common/Icon/icons/support/pay/payRecordLight.svg rename to packages/web/components/common/Icon/icons/support/bill/payRecordLight.svg diff --git a/packages/web/components/common/Icon/icons/support/pay/priceLight.svg b/packages/web/components/common/Icon/icons/support/bill/priceLight.svg similarity index 100% rename from packages/web/components/common/Icon/icons/support/pay/priceLight.svg rename to packages/web/components/common/Icon/icons/support/bill/priceLight.svg diff --git a/packages/web/components/common/Icon/icons/support/bill/shoppingCart.svg b/packages/web/components/common/Icon/icons/support/bill/shoppingCart.svg new file mode 100644 index 00000000000..c701b2576b6 --- /dev/null +++ b/packages/web/components/common/Icon/icons/support/bill/shoppingCart.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/packages/web/components/common/Icon/icons/support/bill/billRecordLight.svg b/packages/web/components/common/Icon/icons/support/usage/usageRecordLight.svg similarity index 100% rename from packages/web/components/common/Icon/icons/support/bill/billRecordLight.svg rename to packages/web/components/common/Icon/icons/support/usage/usageRecordLight.svg diff --git a/packages/web/components/common/Icon/icons/support/user/individuation.svg b/packages/web/components/common/Icon/icons/support/user/individuation.svg new file mode 100644 index 00000000000..63b6af4f491 --- /dev/null +++ b/packages/web/components/common/Icon/icons/support/user/individuation.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/packages/web/components/common/Input/HttpInput/Editor.tsx b/packages/web/components/common/Input/HttpInput/Editor.tsx new file mode 100644 index 00000000000..9fb26bbf05e --- /dev/null +++ b/packages/web/components/common/Input/HttpInput/Editor.tsx @@ -0,0 +1,127 @@ +import { useState, useRef, useTransition, useEffect, useMemo } from 'react'; +import { LexicalComposer } from '@lexical/react/LexicalComposer'; +import { PlainTextPlugin } from '@lexical/react/LexicalPlainTextPlugin'; +import { ContentEditable } from '@lexical/react/LexicalContentEditable'; +import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin'; +import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin'; +import LexicalErrorBoundary from '@lexical/react/LexicalErrorBoundary'; +import { Box } from '@chakra-ui/react'; +import styles from './index.module.scss'; +import { EditorState, LexicalEditor } from 'lexical'; +import { getNanoid } from '@fastgpt/global/common/string/tools'; +import { EditorVariablePickerType } from '../../Textarea/PromptEditor/type'; +import { VariableNode } from '../../Textarea/PromptEditor/plugins/VariablePlugin/node'; +import { textToEditorState } from '../../Textarea/PromptEditor/utils'; +import DropDownMenu from '../../Textarea/PromptEditor/modules/DropDownMenu'; +import { SingleLinePlugin } from '../../Textarea/PromptEditor/plugins/SingleLinePlugin'; +import OnBlurPlugin from '../../Textarea/PromptEditor/plugins/OnBlurPlugin'; +import VariablePlugin from '../../Textarea/PromptEditor/plugins/VariablePlugin'; +import VariablePickerPlugin from '../../Textarea/PromptEditor/plugins/VariablePickerPlugin'; +import FocusPlugin from '../../Textarea/PromptEditor/plugins/FocusPlugin'; + +export default function Editor({ + h = 40, + hasVariablePlugin = true, + hasDropDownPlugin = false, + variables, + onChange, + onBlur, + value, + currentValue, + placeholder = '', + setDropdownValue, + updateTrigger +}: { + h?: number; + hasVariablePlugin?: boolean; + hasDropDownPlugin?: boolean; + variables: EditorVariablePickerType[]; + onChange?: (editorState: EditorState) => void; + onBlur?: (editor: LexicalEditor) => void; + value?: string; + currentValue?: string; + placeholder?: string; + setDropdownValue?: (value: string) => void; + updateTrigger?: boolean; +}) { + const [key, setKey] = useState(getNanoid(6)); + const [_, startSts] = useTransition(); + const [focus, setFocus] = useState(false); + + const initialConfig = { + namespace: 'HttpInput', + nodes: [VariableNode], + editorState: textToEditorState(value), + onError: (error: Error) => { + throw error; + } + }; + + useEffect(() => { + if (focus) return; + setKey(getNanoid(6)); + }, [value, variables.length]); + + useEffect(() => { + setKey(getNanoid(6)); + }, [updateTrigger]); + + const dropdownVariables = useMemo( + () => + variables.filter((item) => { + return item.key.includes(currentValue || '') && item.key !== currentValue; + }), + [currentValue] + ); + + return ( + + + } + placeholder={ + + + {placeholder} + + + } + ErrorBoundary={LexicalErrorBoundary} + /> + + + { + startSts(() => { + onChange?.(e); + }); + }} + /> + {hasVariablePlugin ? : ''} + {hasVariablePlugin ? : ''} + + + + {focus && hasDropDownPlugin && ( + + )} + + ); +} diff --git a/packages/web/components/common/Input/HttpInput/index.module.scss b/packages/web/components/common/Input/HttpInput/index.module.scss new file mode 100644 index 00000000000..36103efa4cd --- /dev/null +++ b/packages/web/components/common/Input/HttpInput/index.module.scss @@ -0,0 +1,9 @@ +.contentEditable { + height: 100%; + display: flex; + flex-direction: column; + justify-content: center; + overflow: auto; + padding-left: 8px; + position: relative; +} diff --git a/packages/web/components/common/Input/HttpInput/index.tsx b/packages/web/components/common/Input/HttpInput/index.tsx new file mode 100644 index 00000000000..6995d47e565 --- /dev/null +++ b/packages/web/components/common/Input/HttpInput/index.tsx @@ -0,0 +1,69 @@ +import React, { useEffect } from 'react'; +import { $getRoot, EditorState, type LexicalEditor } from 'lexical'; +import { useCallback, useTransition } from 'react'; +import { editorStateToText } from '../../Textarea/PromptEditor/utils'; +import { EditorVariablePickerType } from '../../Textarea/PromptEditor/type'; +import Editor from './Editor'; + +const HttpInput = ({ + hasVariablePlugin = true, + hasDropDownPlugin = false, + variables = [], + value, + onChange, + onBlur, + h, + placeholder, + setDropdownValue, + updateTrigger +}: { + hasVariablePlugin?: boolean; + hasDropDownPlugin?: boolean; + variables?: EditorVariablePickerType[]; + value?: string; + onChange?: (text: string) => void; + onBlur?: (text: string) => void; + h?: number; + placeholder?: string; + setDropdownValue?: (value: string) => void; + updateTrigger?: boolean; +}) => { + const [currentValue, setCurrentValue] = React.useState(value); + + const [, startSts] = useTransition(); + + const onChangeInput = useCallback((editorState: EditorState) => { + const text = editorState.read(() => $getRoot().getTextContent()); + const formatValue = text.replaceAll('\n\n', '\n').replaceAll('}}{{', '}} {{'); + setCurrentValue(formatValue); + onChange?.(formatValue); + }, []); + const onBlurInput = useCallback((editor: LexicalEditor) => { + startSts(() => { + const text = editorStateToText(editor).replaceAll('\n\n', '\n').replaceAll('}}{{', '}} {{'); + onBlur?.(text); + }); + }, []); + useEffect(() => { + setCurrentValue(value); + }, [value]); + + return ( + <> + + + ); +}; +export default React.memo(HttpInput); diff --git a/packages/web/components/common/Textarea/JsonEditor/index.tsx b/packages/web/components/common/Textarea/JsonEditor/index.tsx index 779a40a6495..51be2b63897 100644 --- a/packages/web/components/common/Textarea/JsonEditor/index.tsx +++ b/packages/web/components/common/Textarea/JsonEditor/index.tsx @@ -45,10 +45,19 @@ const options = { tabSize: 2 }; -const JSONEditor = ({ defaultValue, value, onChange, resize, variables = [], ...props }: Props) => { +const JSONEditor = ({ + defaultValue, + value, + onChange, + resize, + variables = [], + placeholder, + ...props +}: Props) => { const { toast } = useToast(); const { t } = useTranslation(); const [height, setHeight] = useState(props.height || 100); + const [placeholderDisplay, setPlaceholderDisplay] = useState('block'); const initialY = useRef(0); const completionRegisterRef = useRef(); const monaco = useMonaco(); @@ -217,6 +226,7 @@ const JSONEditor = ({ defaultValue, value, onChange, resize, variables = [], ... borderColor={'myGray.200'} py={2} height={'auto'} + position={'relative'} {...props} > onChange?.(e || '')} + onChange={(e) => { + onChange?.(e || ''); + if (!e) { + setPlaceholderDisplay('block'); + } else { + setPlaceholderDisplay('none'); + } + }} wrapperProps={{ onBlur }} + onMount={() => { + if (!value) { + setPlaceholderDisplay('block'); + } else { + setPlaceholderDisplay('none'); + } + }} /> + + {placeholder} + ); diff --git a/packages/web/components/common/Textarea/PromptEditor/Editor.tsx b/packages/web/components/common/Textarea/PromptEditor/Editor.tsx index 21c8225d52d..0991adc27ba 100644 --- a/packages/web/components/common/Textarea/PromptEditor/Editor.tsx +++ b/packages/web/components/common/Textarea/PromptEditor/Editor.tsx @@ -37,6 +37,7 @@ export default function Editor({ onChange?: (editorState: EditorState) => void; onBlur?: (editor: LexicalEditor) => void; value?: string; + currentValue?: string; placeholder?: string; }) { const [key, setKey] = useState(getNanoid(6)); @@ -75,7 +76,7 @@ export default function Editor({ useEffect(() => { if (focus) return; setKey(getNanoid(6)); - }, [value, variables, focus]); + }, [value, variables.length]); return ( diff --git a/packages/web/components/common/Textarea/PromptEditor/index.tsx b/packages/web/components/common/Textarea/PromptEditor/index.tsx index 0929b8b46b9..25b9ddac4b0 100644 --- a/packages/web/components/common/Textarea/PromptEditor/index.tsx +++ b/packages/web/components/common/Textarea/PromptEditor/index.tsx @@ -1,5 +1,5 @@ import { Button, ModalBody, ModalFooter, useDisclosure } from '@chakra-ui/react'; -import React from 'react'; +import React, { useEffect } from 'react'; import { editorStateToText } from './utils'; import Editor from './Editor'; import MyModal from '../../MyModal'; diff --git a/packages/web/components/common/Textarea/PromptEditor/modules/DropDownMenu/index.tsx b/packages/web/components/common/Textarea/PromptEditor/modules/DropDownMenu/index.tsx new file mode 100644 index 00000000000..de1be1c8e51 --- /dev/null +++ b/packages/web/components/common/Textarea/PromptEditor/modules/DropDownMenu/index.tsx @@ -0,0 +1,89 @@ +import { Box, Flex } from '@chakra-ui/react'; +import { EditorVariablePickerType } from '../../type'; +import MyIcon from '../../../../Icon'; +import React, { useCallback, useEffect } from 'react'; + +export default function DropDownMenu({ + variables, + setDropdownValue +}: { + variables: EditorVariablePickerType[]; + setDropdownValue?: (value: string) => void; +}) { + const [highlightedIndex, setHighlightedIndex] = React.useState(0); + + const handleKeyDown = useCallback( + (event: any) => { + if (event.keyCode === 38) { + setHighlightedIndex((prevIndex) => Math.max(prevIndex - 1, 0)); + } else if (event.keyCode === 40) { + setHighlightedIndex((prevIndex) => Math.min(prevIndex + 1, variables.length - 1)); + } else if (event.keyCode === 13 && variables[highlightedIndex]?.key) { + setDropdownValue?.(variables[highlightedIndex].key); + } + }, + [highlightedIndex, variables] + ); + + useEffect(() => { + document.addEventListener('keydown', handleKeyDown); + + return () => { + document.removeEventListener('keydown', handleKeyDown); + }; + }, [handleKeyDown]); + + return variables.length ? ( + + {variables.map((item, index) => ( + { + e.preventDefault(); + + setDropdownValue?.(item.key); + }} + onMouseEnter={() => { + setHighlightedIndex(index); + }} + > + + + {item.key} + {item.key !== item.label && `(${item.label})`} + + + ))} + + ) : null; +} diff --git a/packages/web/components/common/Textarea/PromptEditor/plugins/OnBlurPlugin/index.tsx b/packages/web/components/common/Textarea/PromptEditor/plugins/OnBlurPlugin/index.tsx index e5bcc2594ac..5bd5bafd474 100644 --- a/packages/web/components/common/Textarea/PromptEditor/plugins/OnBlurPlugin/index.tsx +++ b/packages/web/components/common/Textarea/PromptEditor/plugins/OnBlurPlugin/index.tsx @@ -7,18 +7,16 @@ export default function OnBlurPlugin({ onBlur }: { onBlur?: (editor: LexicalEdit const [editor] = useLexicalComposerContext(); useEffect(() => { - return mergeRegister( - editor.registerCommand( - BLUR_COMMAND, - () => { - if (onBlur) onBlur(editor); + return editor.registerCommand( + BLUR_COMMAND, + () => { + onBlur?.(editor); - return true; - }, - COMMAND_PRIORITY_EDITOR - ) + return true; + }, + COMMAND_PRIORITY_EDITOR ); - }, [editor, onBlur]); + }, [editor]); return null; } diff --git a/packages/web/components/common/Textarea/PromptEditor/plugins/SingleLinePlugin/index.tsx b/packages/web/components/common/Textarea/PromptEditor/plugins/SingleLinePlugin/index.tsx new file mode 100644 index 00000000000..909a89dee38 --- /dev/null +++ b/packages/web/components/common/Textarea/PromptEditor/plugins/SingleLinePlugin/index.tsx @@ -0,0 +1,25 @@ +import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; +import { $createParagraphNode, $createTextNode, RootNode } from 'lexical'; +import { useEffect } from 'react'; + +const newlinesRegex = /[\n\r]/g; + +export function SingleLinePlugin(): null { + const [editor] = useLexicalComposerContext(); + + useEffect(() => { + return editor.registerNodeTransform(RootNode, (rootNode: RootNode) => { + const textContent = rootNode.getTextContent(); + + if (newlinesRegex.test(textContent)) { + const newText = textContent.replace(newlinesRegex, ''); + const paragraph = $createParagraphNode(); + paragraph.append($createTextNode(newText)); + rootNode.clear().append(paragraph); + rootNode.selectEnd(); + } + }); + }, [editor]); + + return null; +} diff --git a/packages/web/components/common/Textarea/PromptEditor/plugins/VariablePickerPlugin/index.tsx b/packages/web/components/common/Textarea/PromptEditor/plugins/VariablePickerPlugin/index.tsx index 6282a2ee438..d979205a790 100644 --- a/packages/web/components/common/Textarea/PromptEditor/plugins/VariablePickerPlugin/index.tsx +++ b/packages/web/components/common/Textarea/PromptEditor/plugins/VariablePickerPlugin/index.tsx @@ -2,9 +2,8 @@ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext import { LexicalTypeaheadMenuPlugin } from '@lexical/react/LexicalTypeaheadMenuPlugin'; import { $createTextNode, $getSelection, $isRangeSelection, TextNode } from 'lexical'; import * as React from 'react'; -import { useCallback, useMemo, useState } from 'react'; +import { useCallback, useState } from 'react'; import * as ReactDOM from 'react-dom'; -import { VariableInputEnum } from '@fastgpt/global/core/module/constants'; import { useTranslation } from 'next-i18next'; import MyIcon from '../../../../Icon'; import { Box, Flex } from '@chakra-ui/react'; @@ -24,42 +23,6 @@ export default function VariablePickerPlugin({ minLength: 0 }); - const VariableTypeList = useMemo( - () => [ - { - title: t('core.module.variable.input type'), - icon: 'core/app/variable/input', - value: VariableInputEnum.input - }, - { - title: t('core.module.variable.textarea type'), - icon: 'core/app/variable/textarea', - value: VariableInputEnum.textarea - }, - { - title: t('core.module.variable.select type'), - icon: 'core/app/variable/select', - value: VariableInputEnum.select - } - ], - [t] - ); - - // const options: Array = useMemo(() => { - // // const newVariableOption = { - // // label: t('common.Add New') + "变量", - // // key: 'new_variable', - // // icon: 'core/modules/variable' - // // }; - // return [ - // ...variables.map((item) => ({ - // ...item, - // icon: VariableTypeList.find((type) => type.value === item.type)?.icon - // })) - // // newVariableOption - // ]; - // }, [VariableTypeList, t, variables]); - const onSelectOption = useCallback( (selectedOption: any, nodeToRemove: TextNode | null, closeMenu: () => void) => { editor.update(() => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f094cca272b..e4d337123c0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '6.0' +lockfileVersion: '6.1' settings: autoInstallPeers: true @@ -88,7 +88,7 @@ importers: specifier: ^0.5.0 version: 0.5.0 date-fns: - specifier: ^2.30.0 + specifier: 2.30.0 version: 2.30.0 dayjs: specifier: ^1.11.7 @@ -285,7 +285,7 @@ importers: specifier: ^1.5.1 version: 1.6.7 date-fns: - specifier: ^2.30.0 + specifier: 2.30.0 version: 2.30.0 dayjs: specifier: ^1.11.7 @@ -311,6 +311,9 @@ importers: immer: specifier: ^9.0.19 version: 9.0.21 + js-yaml: + specifier: ^4.1.0 + version: 4.1.0 jschardet: specifier: ^3.0.0 version: 3.0.0 @@ -390,6 +393,9 @@ importers: '@types/js-cookie': specifier: ^3.0.3 version: 3.0.6 + '@types/js-yaml': + specifier: ^4.0.9 + version: 4.0.9 '@types/jsonwebtoken': specifier: ^9.0.3 version: 9.0.5 @@ -4699,6 +4705,10 @@ packages: resolution: {integrity: sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==} dev: true + /@types/js-yaml@4.0.9: + resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} + dev: true + /@types/json5@0.0.29: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} dev: true @@ -5205,7 +5215,6 @@ packages: /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - dev: true /aria-hidden@1.2.3: resolution: {integrity: sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==} @@ -6540,6 +6549,7 @@ packages: /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + requiresBuild: true /emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} @@ -7927,6 +7937,7 @@ packages: /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} + requiresBuild: true /is-fullwidth-code-point@4.0.0: resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} @@ -8110,7 +8121,6 @@ packages: hasBin: true dependencies: argparse: 2.0.1 - dev: true /jsbn@1.1.0: resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} @@ -10685,6 +10695,7 @@ packages: /string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} + requiresBuild: true dependencies: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 diff --git a/projects/app/data/config.json b/projects/app/data/config.json index 6eeeb82eed9..21cc58981e7 100644 --- a/projects/app/data/config.json +++ b/projects/app/data/config.json @@ -13,8 +13,7 @@ "maxResponse": 4000, "quoteMaxToken": 13000, "maxTemperature": 1.2, - "inputPrice": 0, - "outputPrice": 0, + "charsPointsPrice": 0, "censor": false, "vision": false, "datasetProcess": false, @@ -32,8 +31,7 @@ "maxResponse": 16000, "quoteMaxToken": 13000, "maxTemperature": 1.2, - "inputPrice": 0, - "outputPrice": 0, + "charsPointsPrice": 0, "censor": false, "vision": false, "datasetProcess": true, @@ -51,8 +49,7 @@ "maxResponse": 4000, "quoteMaxToken": 100000, "maxTemperature": 1.2, - "inputPrice": 0, - "outputPrice": 0, + "charsPointsPrice": 0, "censor": false, "vision": false, "datasetProcess": false, @@ -70,10 +67,9 @@ "maxResponse": 4000, "quoteMaxToken": 100000, "maxTemperature": 1.2, - "inputPrice": 0, - "outputPrice": 0, + "charsPointsPrice": 0, "censor": false, - "vision": false, + "vision": true, "datasetProcess": false, "toolChoice": true, "functionCall": false, @@ -87,8 +83,7 @@ { "model": "text-embedding-ada-002", "name": "Embedding-2", - "inputPrice": 0, - "outputPrice": 0, + "charsPointsPrice": 0, "defaultToken": 700, "maxToken": 3000, "weight": 100 @@ -99,22 +94,44 @@ { "model": "tts-1", "name": "OpenAI TTS1", - "inputPrice": 0, - "outputPrice": 0, + "charsPointsPrice": 0, "voices": [ - { "label": "Alloy", "value": "alloy", "bufferId": "openai-Alloy" }, - { "label": "Echo", "value": "echo", "bufferId": "openai-Echo" }, - { "label": "Fable", "value": "fable", "bufferId": "openai-Fable" }, - { "label": "Onyx", "value": "onyx", "bufferId": "openai-Onyx" }, - { "label": "Nova", "value": "nova", "bufferId": "openai-Nova" }, - { "label": "Shimmer", "value": "shimmer", "bufferId": "openai-Shimmer" } + { + "label": "Alloy", + "value": "alloy", + "bufferId": "openai-Alloy" + }, + { + "label": "Echo", + "value": "echo", + "bufferId": "openai-Echo" + }, + { + "label": "Fable", + "value": "fable", + "bufferId": "openai-Fable" + }, + { + "label": "Onyx", + "value": "onyx", + "bufferId": "openai-Onyx" + }, + { + "label": "Nova", + "value": "nova", + "bufferId": "openai-Nova" + }, + { + "label": "Shimmer", + "value": "shimmer", + "bufferId": "openai-Shimmer" + } ] } ], "whisperModel": { "model": "whisper-1", "name": "Whisper1", - "inputPrice": 0, - "outputPrice": 0 + "charsPointsPrice": 0 } } diff --git a/projects/app/next.config.js b/projects/app/next.config.js index ad9a7d8601e..ef9310e5315 100644 --- a/projects/app/next.config.js +++ b/projects/app/next.config.js @@ -45,13 +45,7 @@ const nextConfig = { }, transpilePackages: ['@fastgpt/*'], experimental: { - serverComponentsExternalPackages: [ - 'mongoose', - 'pg', - 'react', - '@chakra-ui/react', - '@lexical/react' - ], + serverComponentsExternalPackages: ['mongoose', 'pg'], outputFileTracingRoot: path.join(__dirname, '../../') } }; diff --git a/projects/app/package.json b/projects/app/package.json index 5358239a6e3..297c422c5e9 100644 --- a/projects/app/package.json +++ b/projects/app/package.json @@ -26,7 +26,7 @@ "@tanstack/react-query": "^4.24.10", "@types/nprogress": "^0.2.0", "axios": "^1.5.1", - "date-fns": "^2.30.0", + "date-fns": "2.30.0", "dayjs": "^1.11.7", "echarts": "^5.4.1", "echarts-gl": "^2.0.9", @@ -35,6 +35,7 @@ "hyperdown": "^2.4.29", "i18next": "^22.5.1", "immer": "^9.0.19", + "js-yaml": "^4.1.0", "jschardet": "^3.0.0", "jsonwebtoken": "^9.0.2", "lodash": "^4.17.21", @@ -63,6 +64,7 @@ "@svgr/webpack": "^6.5.1", "@types/formidable": "^2.0.5", "@types/js-cookie": "^3.0.3", + "@types/js-yaml": "^4.0.9", "@types/jsonwebtoken": "^9.0.3", "@types/lodash": "^4.14.191", "@types/node": "^20.8.5", diff --git a/projects/app/public/docs/versionIntro.md b/projects/app/public/docs/versionIntro.md index f740a75e213..5f1cd9eb7ec 100644 --- a/projects/app/public/docs/versionIntro.md +++ b/projects/app/public/docs/versionIntro.md @@ -2,7 +2,7 @@ 1. 新增 - 知识库搜索合并模块。 2. 新增 - 新的 Http 模块,支持更加灵活的参数传入。同时支持了输入输出自动数据类型转化,例如:接口输出的 JSON 类型会自动转成字符串类型,直接给其他模块使用。此外,还补充了一些例子,可在文档中查看。 -3. 优化 - 问题补全并入知识库搜索模块,无需单独配置。并且问题补全的同时,实现了问题扩展,丰富搜索的语义。(知识库模块会看到有2个参数配置,有一个是多余的,如果想让它消失,可以删除模块,重新增加一个) +3. 优化 - 问题优化并入知识库搜索模块,无需单独配置。并且问题优化的同时,实现了问题扩展,丰富搜索的语义。(知识库模块会看到有2个参数配置,有一个是多余的,如果想让它消失,可以删除模块,重新增加一个) 4. 修复 - 语音输入文件无法上传。 5. 修复 - 对话框重新生成无法使用。 6. [点击查看高级编排介绍文档](https://doc.fastgpt.in/docs/workflow/intro) diff --git a/projects/app/public/locales/en/common.json b/projects/app/public/locales/en/common.json index 22e3d244494..571883e373d 100644 --- a/projects/app/public/locales/en/common.json +++ b/projects/app/public/locales/en/common.json @@ -15,6 +15,7 @@ "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", + "Apps Share": "Apps Share", "Basic Settings": "Basic Settings", "Chat Debug": "Chat Debug", "Chat Logs Tips": "Logs record the app's online, shared, and API(chatId is existing) conversations", @@ -83,6 +84,7 @@ "Delete Success": "Delete Successful", "Delete Tip": "Delete Confirm", "Delete Warning": "Warning", + "Detail": "Detail", "Done": "Done", "Edit": "Edit", "Exit": "Exit", @@ -91,6 +93,9 @@ "Filed is repeat": "Filed is repeated", "Filed is repeated": "", "Finish": "Finish", + "Import": "Import", + "Import failed": "Import failed", + "Import success": "Import success", "Input": "Input", "Intro": "Intro", "Invalid Json": "Invalid Json", @@ -98,8 +103,6 @@ "Last use time": "Last use time", "Load Failed": "Load Failed", "Loading": "Loading", - "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.", "More settings": "More settings", "Name": "Name", "Name Can": "Name Can't Be Empty", @@ -110,6 +113,7 @@ "Number of words": "{{amount}} words", "OK": "OK", "Opened": "Opened", + "Other": "Other", "Output": "Output", "Params": "Params", "Password inconsistency": "Password inconsistency", @@ -132,17 +136,20 @@ "Select template": "Select template", "Set Avatar": "Set Avatar", "Set Name": "Make a nice name", + "Set Team Tags": "Set Team Tags", "Setting": "Setting", "Status": "Status", "Submit failed": "Submit failed", "Submit success": "Update Success", "Team": "Team", + "Team Tags Set": "Team Tags", "Test": "Test", "Time": "Time", "Un used": "Unused", "UnKnow": "UnKnow", "UnKnow Source": "UnKnow Source", "Unlimited": "Unlimited", + "Update": "Update", "Update Failed": "Update Failed", "Update Success": "Update Success", "Update Successful": "Update Successful", @@ -500,6 +507,7 @@ "Manual collection": "Manual collection", "My Dataset": "My Dataset", "Name": "Name", + "Query extension intro": "If the problem completion function is enabled, the accuracy of knowledge base search can be improved in continuous conversations. After this function is enabled, when searching the knowledge base, AI will be used to complete the missing information of the problem according to the conversation records.", "Quote Length": "Quote Length", "Read Dataset": "Read Dataset", "Search score tip": "{{scoreText}}Here are the rankings and scores:\n----\n{{detailScore}}", @@ -609,7 +617,8 @@ "Down load csv template": "Down load csv template", "Embedding Estimated Price Tips": "Index billing: {{price}}/1k chars", "Estimated Price": "Estimated Price: : {{amount}}{{unit}}", - "Estimated Price Tips": "QA charges\nInput: {{inputPrice}}/1k tokens\nOutput: {{outputPrice}}/1k tokens", + "Estimated Price Tips": "QA charges\nInput: 1k chars={{charsPointsPrice}} points", + "Estimated points": "About {{points}} points", "Fetch Error": "Get link failed", "Fetch Url": "Url", "Fetch url placeholder": "Up to 10 links, one per line.", @@ -679,7 +688,7 @@ "Source name": "Source", "Top K": "Top K", "Using cfr": "Open query extension", - "Using query extension": "", + "Using query extension": "Open query extension", "mode": { "embedding": "Vector recall", "embedding desc": "Use vectors for text correlation queries", @@ -775,6 +784,7 @@ "Unlink tip": "[{{name}}] An unfilled or unconnected parameter exists", "Variable": "Variables", "Variable Setting": "Variable Setting", + "Variable import": "Variable import", "edit": { "Field Already Exist": "Key already exist", "Field Edit": "Field Edit" @@ -793,6 +803,8 @@ "Histories": "histories", "Key already exists": "Key already exists", "Key cannot be empty": "Name cannot be empty", + "OpenAPI import": "OpenAPI import", + "OpenAPI import placeholder": "Please enter the OpenAPI format content, and the request information for the first interface will be extracted.", "Props name": "Name", "Props tip": "You can set parameters related to Http requests\nGlobal changes or external parameter inputs can be invoked by {{key}}", "Props value": "Value", @@ -843,8 +855,8 @@ "dynamicTargetInput": "dynamic Target Input", "input": "Input", "selectApp": "App Selector", - "selectChatModel": "Select Chat Model", "selectDataset": "Dataset Selector", + "selectLLMModel": "Select Chat Model", "switch": "Switch", "target": "Target Data", "textarea": "Textarea" @@ -856,6 +868,7 @@ "Ai response content": "Will be triggered after the stream reply is complete", "New context": "Concatenate the reply content with history and return it as a new context", "Quote": "Always return an array, if you want the search results to be empty to perform additional operations, you need to use the above two inputs and the trigger of the target module", + "query extension result": "Output as an array of strings to connect the result directly to the 'Question' of the knowledge base search", "running done": "Triggered when the module call finish" }, "label": { @@ -864,7 +877,7 @@ "Quote": "Quote", "Search result empty": "Search result empty", "Search result not empty": "Search result not empty", - "cfr result": "Response text", + "query extension result": "Response text", "result false": "False", "result true": "True", "running done": "done", @@ -902,6 +915,7 @@ "Tool module": "Tools", "UnKnow Module": "UnKnow Module", "User guide": "User guide", + "http body placeholder": "Same syntax as APIFox", "textEditor": "Text Editor", "textEditor intro": "Output of fixed or incoming text after edit" }, @@ -1103,6 +1117,7 @@ "navbar": { "Account": "Account", "Apps": "App", + "Apps Share": "", "Chat": "Chat", "Datasets": "DataSet", "Module": "Module", @@ -1157,6 +1172,9 @@ "Update Your Plugin": "Update Plugin" }, "support": { + "account": { + "Individuation": "Individuation" + }, "openapi": { "Api baseurl": "Baseurl", "Api manager": "API key management", @@ -1167,17 +1185,34 @@ "Usage": "Usage" }, "outlink": { + "Max usage points": "Max usage", + "Max usage points tip": "The maximum number of credits allowed for this link will not be used. -1 indicates no limit.", + "Usage points": "Usage points", "share": { "Response Quote": "Show Quote", "Response Quote tips": "The referenced content is returned in the share link, but the user is not allowed to download the original document." } }, + "standard": { + "AI Bonus Points": "AI Bonus Points", + "Expired Time": "Expired Time", + "Start Time": "Start Time", + "storage": "storage", + "type": "type" + }, "subscription": { "Cancel subscription": "Cancel" }, + "team": { + "limit": { + "No permission rerank": "Not permission to rerank, please upgrade your plan" + } + }, "user": { + "Avatar": "Avatar", "Need to login": "Please log in first", "Price": "Price", + "User self info": "My info", "auth": { "Sending Code": "Sending" }, @@ -1192,40 +1227,72 @@ } }, "wallet": { + "Amount": "Amount", "Balance not enough tip": "The balance is insufficient, please go to the account page first", + "Bills": "Bill", + "Buy": "Buy", "Buy more": "Buy more", "Confirm pay": "Confirm pay", "Pay error": "Pay error", "Pay success": "Pay success", + "Standard Plan Detail": "Standard Plan Detail", "bill": { "AI Model": "AI Model", "AI Type": "AI Type", - "Price": "Price(¥)" + "Number": "Number", + "Price": "Price(¥)", + "Status": "Status", + "Type": "Bill type", + "payWay": { + "Way": "Pay way", + "balance": "Balance", + "wx": "Wechat" + }, + "status": { + "closed": "CLOSED", + "notpay": "NOT_PAY", + "refund": "REFUND", + "success": "SUCCESS" + } + }, + "moduleName": { + "index": "Index Generation", + "qa": "QA Generation" }, + "noBill": "Not Bills", "subscription": { + "AI points": "AI points", "Ai points": "AI Points Standard", "Buy now": "Buy now", "Change will take effect after the current subscription expires": "Change will take effect after the current subscription expires", "Current dataset store": "Current dataset store subscription", + "Current extra ai points": "Current extra points", "Current plan": "Current plan", "Dataset store": "Dataset store size", "Dataset store price tip": "Deduct it from the account balance on the 1st of each month", "Expand size": "Expand size", + "Extra ai points": "Extra ai points", "Extra dataset size": "Extra dataset size", "Extra plan": "Extra Plan", "Extra plan tip": "When the standard plan is not enough, you can purchase an additional plan to continue using", "FAQ": "Pricing FAQs", + "Month amount": "Month", + "Next extra ai points": "Next extra points", "Next plan": "Future plan", "Next sub dataset size": "", "Nonsupport": "Nonsupport", "Refund plan and pay confirm": "There is no extra cost for you to switch this package and {{amount}} will be refunded to the balance.", + "Stand plan level": "Sub plan", "Standard plan pay confirm": "To switch this package, you need to pay {{payPrice}} Yuan.", "Standard update fail": "Update plan failed.", "Standard update success": "Change subscription plan successful!", "Sub plan": "Pricing Plans", "Sub plan tip": "Use FastGPT for free or upgrade to a higher plan", + "Team plan and usage": "Plan and usage", "Training weight": "Training weight: {{weight}}", - "Update extra dataset size": "Update size", + "Update extra ai points": "AI Points", + "Update extra dataset size": "Dataset size", + "Upgrade plan": "Upgrade plan", "function": { "History store": "", "Max app": "", @@ -1236,6 +1303,7 @@ }, "mode": { "Month": "Monthly", + "Period": "Period", "Year": "Yearly", "Year sale": "2 months free" }, @@ -1248,9 +1316,38 @@ "team": "" }, "type": { + "balance": "Add Balance", "extraDatasetSize": "Extra dataset size", - "standard": "" + "extraPoints": "AI Points", + "standard": "Sub plan" } + }, + "usage": { + "Ai model": "Ai Model", + "App name": "App name", + "Audio Speech": "Audio Speech", + "Bill Module": "Bill Detail", + "Chars length": "Chars length", + "Data Length": "Data length", + "Dataset store": "Dataset store", + "Duration": "Duration(s)", + "Extension Input Token Length": "Extension input tokens", + "Extension Output Token Length": "Extension output tokens", + "Extension result": "Extension result", + "Input Token Length": "Input tokens", + "Module name": "Module name", + "Number": "Bill ID", + "Output Token Length": "Output tokens", + "ReRank": "ReRank", + "Source": "Source", + "Text Length": "Text length", + "Time": "Time", + "Token Length": "Tokens", + "Total": "Total", + "Total points": "AI points usage", + "Usage Detail": "Usage Detail", + "Whisper": "Whisper", + "bill username": "User" } } }, @@ -1292,6 +1389,7 @@ "Set OpenAI Account Failed": "Set OpenAI account failed", "Sign Out": "Sign Out", "Source": "Source", + "Standard Detail": "", "Team": "Team", "Time": "Time", "Timezone": "Timezone", @@ -1339,7 +1437,11 @@ "Select Team": "Select Team", "Set Name": "Team Name", "Switch Team Failed": "Switch Team Failed", + "Tags Async": "Tag synchronization", "Team Name": "Team Name", + "Team Tags Async": "Team Tags Async", + "Team Tags Async Success": "Team Tags Async Success", + "Team Tags Async Tip": "Fill in the tag sync connection to get the latest", "Update Team": "Update Team", "invite": { "Accept Confirm": "Want to join the team?", @@ -1364,37 +1466,5 @@ "Visitor": "Visitor" } } - }, - "wallet": { - "bill": { - "Ai model": "Ai Model", - "App name": "App name", - "Audio Speech": "Audio Speech", - "Bill Module": "Bill Detail", - "Chars length": "Chars length", - "Data Length": "Data length", - "Dataset store": "", - "Duration": "Duration(s)", - "Extension Input Token Length": "Extension input tokens", - "Extension Output Token Length": "Extension output tokens", - "Extension result": "Extension result", - "Input Token Length": "Input tokens", - "Module name": "Module name", - "Next Step Guide": "", - "Number": "Bill ID", - "Output Token Length": "Output tokens", - "ReRank": "ReRank", - "Source": "Source", - "Text Length": "Text length", - "Time": "Time", - "Token Length": "Tokens", - "Total": "Total", - "Whisper": "Whisper", - "bill username": "User" - }, - "moduleName": { - "index": "Index Generation", - "qa": "QA Generation" - } } } diff --git a/projects/app/public/locales/zh/common.json b/projects/app/public/locales/zh/common.json index e12c9d08cf3..dcf96bdeb0b 100644 --- a/projects/app/public/locales/zh/common.json +++ b/projects/app/public/locales/zh/common.json @@ -15,6 +15,7 @@ "AI Settings": "AI 配置", "Advance App TestTip": "当前应用可能为高级编排模式\n如需切换为【简易模式】请点击左侧保存按键", "App Detail": "应用详情", + "Apps Share": "应用分享", "Basic Settings": "基本信息", "Chat Debug": "调试预览", "Chat Logs Tips": "日志会记录该应用的在线、分享和 API(需填写 chatId) 对话记录", @@ -83,6 +84,7 @@ "Delete Success": "删除成功", "Delete Tip": "删除提示", "Delete Warning": "删除警告", + "Detail": "详情", "Done": "完成", "Edit": "编辑", "Exit": "退出", @@ -91,6 +93,9 @@ "Filed is repeat": "", "Filed is repeated": "字段重复了", "Finish": "完成", + "Import": "导入", + "Import failed": "导入失败", + "Import success": "导入成功", "Input": "输入", "Intro": "介绍", "Invalid Json": "无效的JSON格式,请注意检查。", @@ -98,8 +103,6 @@ "Last use time": "最后使用时间", "Load Failed": "加载失败", "Loading": "加载中...", - "Max credit": "最大金额", - "Max credit tips": "该链接最大可消耗多少金额,超出后链接将被禁止使用。-1 代表无限制。", "More settings": "更多设置", "Name": "名称", "Name Can": "名称不能为空", @@ -110,6 +113,7 @@ "Number of words": "{{amount}}字", "OK": "好的", "Opened": "已开启", + "Other": "其他", "Output": "输出", "Params": "参数", "Password inconsistency": "两次密码不一致", @@ -132,17 +136,20 @@ "Select template": "选择模板", "Set Avatar": "点击设置头像", "Set Name": "取个名字", + "Set Team Tags": "团队标签", "Setting": "设置", "Status": "状态", "Submit failed": "提交失败", "Submit success": "提交成功", "Team": "团队", + "Team Tags Set": "标签", "Test": "测试", "Time": "时间", "Un used": "未使用", "UnKnow": "未知", "UnKnow Source": "未知来源", "Unlimited": "无限制", + "Update": "更新", "Update Failed": "更新异常", "Update Success": "更新成功", "Update Successful": "更新成功", @@ -402,7 +409,7 @@ "Stop Speak": "停止录音", "Type a message": "输入问题", "Unpin": "取消置顶", - "You need to a chat app": "你需要创建一个应用", + "You need to a chat app": "鉴权失败,暂无权限访问应用", "error": { "Chat error": "对话出现异常", "Messages empty": "接口内容为空,可能文本超长了~", @@ -434,7 +441,7 @@ }, "response": { "Complete Response": "完整响应", - "Extension model": "问题补全模型", + "Extension model": "问题优化模型", "Plugin Resonse Detail": "插件详情", "Read complete response": "查看详情", "Read complete response tips": "点击查看详细流程", @@ -500,6 +507,7 @@ "Manual collection": "手动数据集", "My Dataset": "我的知识库", "Name": "知识库名称", + "Query extension intro": "开启问题优化功能,可以提高提高连续对话时,知识库搜索的精度。开启该功能后,在进行知识库搜索时,会根据对话记录,利用 AI 补全问题缺失的信息。", "Quote Length": "引用内容长度", "Read Dataset": "查看知识库详情", "Search score tip": "{{scoreText}}下面是详细排名和得分情况:\n----\n{{detailScore}}", @@ -551,7 +559,8 @@ "success": "开始同步" } }, - "training": {} + "training": { + } }, "data": { "Auxiliary Data": "辅助数据", @@ -560,7 +569,7 @@ "Data Content": "相关数据内容", "Data Content Placeholder": "该输入框是必填项,该内容通常是对于知识点的描述,也可以是用户的问题,最多 {{maxToken}} 字。", "Data Content Tip": "该输入框是必填项\n该内容通常是对于知识点的描述,也可以是用户的问题。", - "Default Index Tip": "无法编辑,默认索引会使用【相关数据内容】与【辅助数据】的文本直接生成索引,如不需要默认索引,可删除。 每条数据必须保证有一个以上索引,所有索引被删除后,会自动生成默认索引。", + "Default Index Tip": "无法编辑,默认索引会使用【相关数据内容】与【辅助数据】的文本直接生成索引。", "Edit": "编辑数据", "Empty Tip": "这个集合还没有数据~", "Main Content": "主要内容", @@ -608,9 +617,10 @@ "Data file progress": "数据上传进度", "Data process params": "数据处理参数", "Down load csv template": "点击下载 CSV 模板", - "Embedding Estimated Price Tips": "索引计费: {{price}}/1k字符", + "Embedding Estimated Price Tips": "索引计费: {{price}}积分/1k字符", "Estimated Price": "预估价格: {{amount}}{{unit}}", - "Estimated Price Tips": "QA计费为\n输入: {{inputPrice}}/1k tokens\n输出: {{outputPrice}}/1k tokens", + "Estimated Price Tips": "QA计费为\n输入: 1k字符 = {{charsPointsPrice}}积分", + "Estimated points": "预估消耗 {{points}} 积分", "Fetch Error": "获取链接失败", "Fetch Url": "网络链接", "Fetch url placeholder": "最多10个链接,每行一个。", @@ -630,7 +640,7 @@ "Preview chunks": "分段预览", "Preview raw text": "预览源文本(最多展示10000字)", "Process way": "处理方式", - "QA Estimated Price Tips": "QA计费为: {{price}}元/1k 字符(包含输入和输出)", + "QA Estimated Price Tips": "QA计费为: {{price}}积分/1k 字符(包含输入和输出)", "QA Import": "QA拆分", "QA Import Tip": "根据一定规则,将文本拆成一段较大的段落,调用 AI 为该段落生成问答对。", "Re Preview": "重新生成预览", @@ -680,7 +690,7 @@ "Source name": "引用来源名", "Top K": "单次搜索上限", "Using cfr": "", - "Using query extension": "使用问题补全", + "Using query extension": "使用问题优化", "mode": { "embedding": "语义检索", "embedding desc": "使用向量进行文本相关性查询", @@ -776,6 +786,7 @@ "Unlink tip": "【{{name}}】存在未填或未连接参数", "Variable": "全局变量", "Variable Setting": "变量设置", + "Variable import": "外部参数输入", "edit": { "Field Already Exist": "key 重复", "Field Edit": "字段编辑" @@ -794,6 +805,8 @@ "Histories": "历史纪录,最多取10条", "Key already exists": "Key 已经存在", "Key cannot be empty": "参数名不能为空", + "OpenAPI import": "OpenAPI 导入", + "OpenAPI import placeholder": "请输入 OpenAPI 格式内容,将会提取第一个接口的请求信息。", "Props name": "参数名", "Props tip": "可以设置 Http 请求的相关参数\n可通过 {{key}} 来调用全局变了或外部参数输入,当前可使用变量:\n{{variable}}", "Props value": "参数值", @@ -844,8 +857,8 @@ "dynamicTargetInput": "动态外部数据", "input": "输入框", "selectApp": "应用选择", - "selectChatModel": "对话模型选择", "selectDataset": "知识库选择", + "selectLLMModel": "对话模型选择", "switch": "开关", "target": "外部数据", "textarea": "段落输入" @@ -857,6 +870,7 @@ "Ai response content": "将在 stream 回复完毕后触发", "New context": "将本次回复内容拼接上历史记录,作为新的上下文返回", "Quote": "始终返回数组,如果希望搜索结果为空时执行额外操作,需要用到上面的两个输入以及目标模块的触发器", + "query extension result": "以字符串数组的形式输出,可将该结果直接连接到“知识库搜索”的“用户问题”中,建议不要连接到“AI对话”的“用户问题”中", "running done": "模块调用结束时触发" }, "label": { @@ -865,7 +879,7 @@ "Quote": "引用内容", "Search result empty": "搜索结果为空", "Search result not empty": "搜索结果不为空", - "cfr result": "补全结果", + "query extension result": "优化结果", "result false": "False", "result true": "True", "running done": "模块调用结束", @@ -892,8 +906,8 @@ "Http request": "HTTP 请求", "Http request intro": "可以发出一个 HTTP 请求,实现更为复杂的操作(联网搜索、数据库查询等)", "My plugin module": "个人插件", - "Query extension": "问题补全", - "Query extension intro": "开启问题补全功能,可以提高提高连续对话时,知识库搜索的精度。开启该功能后,在进行知识库搜索时,会根据对话记录,利用 AI 补全问题缺失的信息。", + "Query extension": "问题优化", + "Query extension intro": "使用问题优化功能,可以提高知识库连续对话时搜索的精度。使用该功能后,会先利用 AI 根据上下文构建一个或多个新的检索词,这些检索词更利于进行知识库搜索。该模块已内置在知识库搜索模块中,如果您仅进行一次知识库搜索,可直接使用知识库内置的补全功能。", "Response module": "文本输出", "Running app": "应用调用", "Running app intro": "可以选择一个其他应用进行调用", @@ -903,6 +917,7 @@ "Tool module": "工具", "UnKnow Module": "未知模块", "User guide": "用户引导", + "http body placeholder": "与APIFox相同的语法", "textEditor": "文本加工", "textEditor intro": "可对固定或传入的文本进行加工后输出,非字符串类型数据最终会转成字符串类型。" }, @@ -1104,6 +1119,7 @@ "navbar": { "Account": "账号", "Apps": "应用", + "Apps Share": "应用分享", "Chat": "聊天", "Datasets": "知识库", "Module": "模块", @@ -1158,6 +1174,9 @@ "Update Your Plugin": "更新插件" }, "support": { + "account": { + "Individuation": "个性化" + }, "openapi": { "Api baseurl": "API根地址", "Api manager": "API 秘钥管理", @@ -1168,17 +1187,34 @@ "Usage": "已用额度(¥)" }, "outlink": { + "Max usage points": "积分上限", + "Max usage points tip": "该链接最多允许使用多少积分,超出后将无法使用。-1 代表无限制。", + "Usage points": "积分消耗", "share": { "Response Quote": "返回引用", "Response Quote tips": "在分享链接中返回引用内容,但不会允许用户下载原文档" } }, + "standard": { + "AI Bonus Points": "AI 积分", + "Expired Time": "结束时间", + "Start Time": "开始时间", + "storage": "存储量", + "type": "类型" + }, "subscription": { "Cancel subscription": "取消订阅" }, + "team": { + "limit": { + "No permission rerank": "无权使用结果重排,请升级您的套餐" + } + }, "user": { + "Avatar": "头像", "Need to login": "请先登录", "Price": "计费标准", + "User self info": "个人信息", "auth": { "Sending Code": "正在发送" }, @@ -1193,65 +1229,127 @@ } }, "wallet": { + "Amount": "金额", "Balance not enough tip": "余额不足,请先到账号页充值", + "Bills": "账单", + "Buy": "购买", "Buy more": "扩容", "Confirm pay": "支付确认", "Pay error": "支付失败", "Pay success": "支付成功", + "Standard Plan Detail": "套餐详情", "bill": { "AI Model": "AI 模型", "AI Type": "AI 类型", - "Price": "价格(¥)" + "Number": "订单号", + "Price": "价格(¥)", + "Status": "状态", + "Type": "订单类型", + "payWay": { + "Way": "支付方式", + "balance": "余额支付", + "wx": "微信支付" + }, + "status": { + "closed": "已关闭", + "notpay": "未支付", + "refund": "已退款", + "success": "支付成功" + } + }, + "moduleName": { + "index": "索引生成", + "qa": "QA 拆分" }, + "noBill": "无账单记录~", "subscription": { + "AI points": "AI积分", "Ai points": "AI 积分计算标准", - "Buy now": "开始使用", + "Buy now": "切换套餐", "Change will take effect after the current subscription expires": "更新成功。将会再下个订阅周期生效。", "Current dataset store": "当前额外容量", - "Current plan": "当前计划", + "Current extra ai points": "当前额外积分", + "Current plan": "当前套餐", "Dataset store": "知识库容量", "Dataset store price tip": "每月1号从账号余额里扣除", "Expand size": "扩大容量", + "Extra ai points": "额外AI积分", "Extra dataset size": "额外知识库容量", - "Extra plan": "额外套餐", - "Extra plan tip": "标准套餐不够时,您可以购买额外套餐继续使用", + "Extra plan": "额外资源包", + "Extra plan tip": "标准套餐不够时,您可以购买额外资源包继续使用", "FAQ": "常见问题", - "Next plan": "未来计划", + "Month amount": "月数", + "Next extra ai points": "下次额外积分", + "Next plan": "未来套餐", "Next sub dataset size": "下次订阅额外容量", "Nonsupport": "无法切换", "Refund plan and pay confirm": "切换该套餐您无需支付额外费用,并将退换 {{amount}} 元至余额中。", + "Stand plan level": "订阅套餐", "Standard plan pay confirm": "切换该套餐,您本次需要补充支付 {{payPrice}} 元。", "Standard update fail": "修改订阅套餐异常", "Standard update success": "变更订阅套餐成功!", "Sub plan": "订阅套餐", "Sub plan tip": "免费使用 FastGPT 或升级更高的套餐", + "Team plan and usage": "套餐与用量", "Training weight": "训练优先级: {{weight}}", - "Update extra dataset size": "调整知识库额外容量", + "Update extra ai points": "额外AI积分", + "Update extra dataset size": "额外存储量", + "Upgrade plan": "升级套餐", "function": { "History store": "{{amount}} 天对话记录保留", "Max app": "{{amount}} 个应用与插件", "Max dataset": "{{amount}} 个知识库", - "Max dataset size": "{{amount}} 组知识库数据", + "Max dataset size": "{{amount}} 组知识库索引", "Max members": "{{amount}} 个团队成员", - "Points": "{{amount}}万AI积分" + "Points": "{{amount}} AI积分" }, "mode": { "Month": "按月", + "Period": "订阅周期", "Year": "按年", "Year sale": "赠送两个月" }, "standardSubLevel": { "enterprise": "企业版", "experience": "体验版", - "experience desc": "可使用 {{title}} 的完整功能", + "experience desc": "", "free": "免费版", - "free desc": "每月均可免费使用 {{title}} 的基础功能", + "free desc": "每月均可免费使用基础功能,15天不活跃时,将会清除知识库", "team": "团队版" }, "type": { + "balance": "余额充值", "extraDatasetSize": "知识库扩容", + "extraPoints": "AI积分套餐", "standard": "套餐订阅" } + }, + "usage": { + "Ai model": "AI模型", + "App name": "应用名", + "Audio Speech": "语音播报", + "Bill Module": "扣费模块", + "Chars length": "文本长度", + "Data Length": "数据长度", + "Dataset store": "知识库存储", + "Duration": "时长(秒)", + "Extension Input Token Length": "问题优化输入Tokens", + "Extension Output Token Length": "问题优化输出Tokens", + "Extension result": "问题优化结果", + "Input Token Length": "输入 Tokens", + "Module name": "模块名", + "Number": "", + "Output Token Length": "输出 Tokens", + "ReRank": "结果重排", + "Source": "来源", + "Text Length": "文本长度", + "Time": "生成时间", + "Token Length": "Token长度", + "Total": "总金额", + "Total points": "AI积分消耗", + "Usage Detail": "使用详情", + "Whisper": "语音输入", + "bill username": "用户" } } }, @@ -1293,6 +1391,7 @@ "Set OpenAI Account Failed": "设置 OpenAI 账号异常", "Sign Out": "登出", "Source": "来源", + "Standard Detail": "", "Team": "团队", "Time": "时间", "Timezone": "时区", @@ -1340,7 +1439,11 @@ "Select Team": "团队选择", "Set Name": "给团队取个名字", "Switch Team Failed": "切换团队异常", + "Tags Async": "保存", "Team Name": "团队名", + "Team Tags Async": "标签同步", + "Team Tags Async Success": "链接报错成功,标签信息更新", + "Team Tags Async Tip": "填写标签同步连接,获取最新", "Update Team": "更新团队信息", "invite": { "Accept Confirm": "确认加入该团队?", @@ -1365,37 +1468,5 @@ "Visitor": "访客" } } - }, - "wallet": { - "bill": { - "Ai model": "AI模型", - "App name": "应用名", - "Audio Speech": "语音播报", - "Bill Module": "扣费模块", - "Chars length": "文本长度", - "Data Length": "数据长度", - "Dataset store": "知识库存储", - "Duration": "时长(秒)", - "Extension Input Token Length": "问题补全输入Tokens", - "Extension Output Token Length": "问题补全输出Tokens", - "Extension result": "问题补全结果", - "Input Token Length": "输入 Tokens", - "Module name": "模块名", - "Next Step Guide": "下一步指引", - "Number": "订单号", - "Output Token Length": "输出 Tokens", - "ReRank": "结果重排", - "Source": "来源", - "Text Length": "文本长度", - "Time": "生成时间", - "Token Length": "Token长度", - "Total": "总金额", - "Whisper": "语音输入", - "bill username": "用户" - }, - "moduleName": { - "index": "索引生成", - "qa": "QA 拆分" - } } } diff --git a/projects/app/src/components/ChatBox/SelectMarkCollection.tsx b/projects/app/src/components/ChatBox/SelectMarkCollection.tsx index 9453b5c07d0..de4aaa5f970 100644 --- a/projects/app/src/components/ChatBox/SelectMarkCollection.tsx +++ b/projects/app/src/components/ChatBox/SelectMarkCollection.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useState } from 'react'; +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'; @@ -8,7 +8,6 @@ import DatasetSelectModal, { useDatasetSelect } from '@/components/core/dataset/ import dynamic from 'next/dynamic'; import { AdminFbkType } from '@fastgpt/global/core/chat/type.d'; import SelectCollections from '@/web/core/dataset/components/SelectCollections'; -import { getDefaultIndex } from '@fastgpt/global/core/dataset/utils'; const InputDataModal = dynamic(() => import('@/pages/dataset/detail/components/InputDataModal')); diff --git a/projects/app/src/components/ChatBox/WholeResponseModal.tsx b/projects/app/src/components/ChatBox/WholeResponseModal.tsx index 6a57f74ef64..3430da544e5 100644 --- a/projects/app/src/components/ChatBox/WholeResponseModal.tsx +++ b/projects/app/src/components/ChatBox/WholeResponseModal.tsx @@ -8,10 +8,10 @@ import Tabs from '../Tabs'; import MyModal from '../MyModal'; import MyTooltip from '../MyTooltip'; import { QuestionOutlineIcon } from '@chakra-ui/icons'; -import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools'; import Markdown from '../Markdown'; import { QuoteList } from './QuoteModal'; import { DatasetSearchModeMap } from '@fastgpt/global/core/dataset/constants'; +import { formatNumber } from '@fastgpt/global/common/math/tools'; function Row({ label, @@ -131,10 +131,10 @@ const ResponseBox = React.memo(function ResponseBox({ <> - {activeModule?.price !== undefined && ( + {activeModule?.totalPoints !== undefined && ( )} - - @@ -273,18 +271,18 @@ const ResponseBox = React.memo(function ResponseBox({ {/* plugin */} <> - {activeModule?.pluginDetail && activeModule?.pluginDetail.length > 0 && ( - } - /> - )} {activeModule?.pluginOutput && ( )} + {activeModule?.pluginDetail && activeModule?.pluginDetail.length > 0 && ( + } + /> + )} {/* text output */} diff --git a/projects/app/src/components/ChatBox/index.tsx b/projects/app/src/components/ChatBox/index.tsx index 98a781ac299..9115c43aade 100644 --- a/projects/app/src/components/ChatBox/index.tsx +++ b/projects/app/src/components/ChatBox/index.tsx @@ -121,6 +121,7 @@ type Props = { appId?: string; chatId?: string; shareId?: string; + shareTeamId?: string; outLinkUid?: string; onUpdateVariable?: (e: Record) => void; @@ -146,6 +147,7 @@ const ChatBox = ( appId, chatId, shareId, + shareTeamId, outLinkUid, onUpdateVariable, onStartChat, @@ -396,21 +398,22 @@ const ChatBox = ( }; }) ); - - setTimeout(() => { - createQuestionGuide({ - history: newChatList.map((item, i) => - i === newChatList.length - 1 - ? { - ...item, - value: responseText - } - : item - ) - }); - generatingScroll(); - isPc && TextareaDom.current?.focus(); - }, 100); + if (!shareTeamId) { + setTimeout(() => { + createQuestionGuide({ + history: newChatList.map((item, i) => + i === newChatList.length - 1 + ? { + ...item, + value: responseText + } + : item + ) + }); + generatingScroll(); + isPc && TextareaDom.current?.focus(); + }, 100); + } } catch (err: any) { toast({ title: t(getErrText(err, 'core.chat.error.Chat error')), diff --git a/projects/app/src/components/Layout/auth.tsx b/projects/app/src/components/Layout/auth.tsx index 09c28cad0c7..4129aa9569e 100644 --- a/projects/app/src/components/Layout/auth.tsx +++ b/projects/app/src/components/Layout/auth.tsx @@ -11,6 +11,7 @@ const unAuthPage: { [key: string]: boolean } = { '/login/fastlogin': true, '/appStore': true, '/chat/share': true, + '/chat/team': true, '/tools/price': true, '/price': true }; diff --git a/projects/app/src/components/Layout/index.tsx b/projects/app/src/components/Layout/index.tsx index 5561c25f9cf..2762c56788f 100644 --- a/projects/app/src/components/Layout/index.tsx +++ b/projects/app/src/components/Layout/index.tsx @@ -23,6 +23,7 @@ const pcUnShowLayoutRoute: Record = { '/login/provider': true, '/login/fastlogin': true, '/chat/share': true, + '/chat/team': true, '/app/edit': true, '/chat': true, '/tools/price': true, @@ -34,6 +35,7 @@ const phoneUnShowLayoutRoute: Record = { '/login/provider': true, '/login/fastlogin': true, '/chat/share': true, + '/chat/team': true, '/tools/price': true, '/price': true }; @@ -114,9 +116,10 @@ const Layout = ({ children }: { children: JSX.Element }) => { )} + + {!!userInfo && } - {!!userInfo && } ); }; diff --git a/projects/app/src/components/Markdown/index.tsx b/projects/app/src/components/Markdown/index.tsx index 58f156a8b71..e8eb3b67f22 100644 --- a/projects/app/src/components/Markdown/index.tsx +++ b/projects/app/src/components/Markdown/index.tsx @@ -57,7 +57,7 @@ const Markdown = ({ source, isChatting = false }: { source: string; isChatting?: className={`markdown ${styles.markdown} ${isChatting ? `${formatSource ? styles.waitingAnimation : styles.animation}` : ''} `} - remarkPlugins={[RemarkMath, RemarkGfm, RemarkBreaks]} + remarkPlugins={[RemarkMath, [RemarkGfm, { singleTilde: false }], RemarkBreaks]} rehypePlugins={[RehypeKatex]} components={components} linkTarget={'_blank'} diff --git a/projects/app/src/components/Select/SelectAiModel.tsx b/projects/app/src/components/Select/SelectAiModel.tsx index 854c7b51c7a..328128f224f 100644 --- a/projects/app/src/components/Select/SelectAiModel.tsx +++ b/projects/app/src/components/Select/SelectAiModel.tsx @@ -2,15 +2,15 @@ import React, { useMemo } from 'react'; import MySelect, { type SelectProps } from './index'; import { useTranslation } from 'next-i18next'; -import dynamic from 'next/dynamic'; -import { useDisclosure } from '@chakra-ui/react'; import { useSystemStore } from '@/web/common/system/useSystemStore'; - -const PriceBox = dynamic(() => import('@/components/support/wallet/Price')); +import { useRouter } from 'next/router'; +import { AI_POINT_USAGE_CARD_ROUTE } from '@/web/support/wallet/sub/constants'; const SelectAiModel = ({ list, ...props }: SelectProps) => { const { t } = useTranslation(); const { feConfigs } = useSystemStore(); + const router = useRouter(); + const expandList = useMemo(() => { return feConfigs.show_pay ? list.concat({ @@ -20,12 +20,6 @@ const SelectAiModel = ({ list, ...props }: SelectProps) => { : list; }, [feConfigs.show_pay, list, t]); - const { - isOpen: isOpenPriceBox, - onOpen: onOpenPriceBox, - onClose: onClosePriceBox - } = useDisclosure(); - return ( <> { {...props} onchange={(e) => { if (e === 'price') { - onOpenPriceBox(); + router.push(AI_POINT_USAGE_CARD_ROUTE); return; } props.onchange?.(e); }} /> - {isOpenPriceBox && } ); }; diff --git a/projects/app/src/components/TagEdit/index.tsx b/projects/app/src/components/TagEdit/index.tsx new file mode 100644 index 00000000000..7f9a2180924 --- /dev/null +++ b/projects/app/src/components/TagEdit/index.tsx @@ -0,0 +1,103 @@ +import React, { useEffect, useMemo, useState } from 'react'; +import { + Menu, + MenuButton, + MenuList, + MenuItemOption, + MenuOptionGroup, + Flex, + TagLabel, + TagCloseButton, + HStack, + Tag, + Input +} from '@chakra-ui/react'; +import type { TeamTagsSchema } from '@fastgpt/global/support/user/team/type'; +const TagEdit = ({ + defaultValues, + teamsTags, + setSelectedTags +}: { + defaultValues: []; + teamsTags: Array; + setSelectedTags: (item: Array) => void; +}) => { + const [teamTagsOptions, setTeamTagsOptions] = useState(teamsTags); + const setSelectTeamsTags = (item: any) => { + setSelectedTags(item); + }; + useMemo(() => { + setTeamTagsOptions(teamsTags); + }, [teamsTags]); + return ( + <> + + + + {teamsTags.map((item: TeamTagsSchema, index: number) => { + const key: string = item?.key; + if (defaultValues.indexOf(key as never) > -1) { + return ( + + {item.label} + + + ); + } + })} + + + + { + // 对用户输入的搜索文本进行小写转换,以实现不区分大小写的搜索 + const searchLower: string = e?.nativeEvent?.data || ''; + // 使用filter方法来过滤列表,只返回包含搜索文本的项 + const resultList = teamsTags.filter((item) => { + const searchValue = item.label || ''; + // 对列表中的每一项也进行小写转换 + return searchValue.includes(searchLower); + }); + !searchLower ? setTeamTagsOptions(teamsTags) : setTeamTagsOptions(resultList); + }} + /> + { + setSelectTeamsTags(e); + }} + > + {teamTagsOptions.map((item, index) => { + return ( + + {item?.label} + + ); + })} + + + + + ); +}; + +export default TagEdit; diff --git a/projects/app/src/components/core/module/DatasetParamsModal.tsx b/projects/app/src/components/core/module/DatasetParamsModal.tsx index d00a26df446..76384f65d89 100644 --- a/projects/app/src/components/core/module/DatasetParamsModal.tsx +++ b/projects/app/src/components/core/module/DatasetParamsModal.tsx @@ -27,6 +27,8 @@ import MyIcon from '@fastgpt/web/components/common/Icon'; import Tabs from '@/components/Tabs'; import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor'; import SelectAiModel from '@/components/Select/SelectAiModel'; +import { useUserStore } from '@/web/support/user/useUserStore'; +import { useToast } from '@fastgpt/web/hooks/useToast'; export type DatasetParamsProps = { searchMode: `${DatasetSearchModeEnum}`; @@ -61,6 +63,8 @@ const DatasetParamsModal = ({ }: DatasetParamsProps & { onClose: () => void; onSuccess: (e: DatasetParamsProps) => void }) => { const { t } = useTranslation(); const theme = useTheme(); + const { toast } = useToast(); + const { teamPlanStatus } = useUserStore(); const { reRankModelList, llmModelList } = useSystemStore(); const [refresh, setRefresh] = useState(false); const [currentTabType, setCurrentTabType] = useState(SearchSettingTabEnum.searchMode); @@ -71,7 +75,7 @@ const DatasetParamsModal = ({ limit, similarity, searchMode, - usingReRank, + usingReRank: !!usingReRank && !!teamPlanStatus?.standardConstants?.permissionReRank, datasetSearchUsingExtensionQuery, datasetSearchExtensionModel: datasetSearchExtensionModel ?? llmModelList[0]?.model, datasetSearchExtensionBg @@ -105,6 +109,10 @@ const DatasetParamsModal = ({ return true; }, [getValues, similarity]); + const showReRank = useMemo(() => { + return usingReRank !== undefined && reRankModelList.length > 0; + }, [reRankModelList.length, usingReRank]); + return ( - {usingReRank !== undefined && reRankModelList.length > 0 && ( + {showReRank && ( <> { + if ( + teamPlanStatus?.standardConstants && + !teamPlanStatus?.standardConstants?.permissionReRank + ) { + return toast({ + status: 'warning', + title: t('support.team.limit.No permission rerank') + }); + } setValue('usingReRank', !getValues('usingReRank')); setRefresh((state) => !state); }} @@ -273,7 +290,7 @@ const DatasetParamsModal = ({ {currentTabType === SearchSettingTabEnum.queryExtension && ( - {t('core.module.template.Query extension intro')} + {t('core.dataset.Query extension intro')} {t('core.dataset.search.Using query extension')} diff --git a/projects/app/src/components/core/module/Flow/components/nodes/NodeHttp/OpenApiImportModal.tsx b/projects/app/src/components/core/module/Flow/components/nodes/NodeHttp/OpenApiImportModal.tsx new file mode 100644 index 00000000000..32e5fe5e27a --- /dev/null +++ b/projects/app/src/components/core/module/Flow/components/nodes/NodeHttp/OpenApiImportModal.tsx @@ -0,0 +1,185 @@ +import React from 'react'; +import MyModal from '@/components/MyModal'; +import { ModalBody, Button, ModalFooter, useDisclosure, Textarea, Box } from '@chakra-ui/react'; +import { useTranslation } from 'react-i18next'; +import { onChangeNode } from '../../../FlowProvider'; +import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants'; +import { FlowNodeInputItemType } from '@fastgpt/global/core/module/node/type'; +import { useToast } from '@fastgpt/web/hooks/useToast'; +import yaml from 'js-yaml'; +import { useForm } from 'react-hook-form'; + +type RequestMethod = 'get' | 'post' | 'put' | 'delete' | 'patch'; +const methodMap: { [K in RequestMethod]: string } = { + get: 'GET', + post: 'POST', + put: 'PUT', + delete: 'DELETE', + patch: 'PATCH' +}; + +const OpenApiImportModal = ({ + children, + moduleId, + inputs +}: { + children: React.ReactElement; + moduleId: string; + inputs: FlowNodeInputItemType[]; +}) => { + const { isOpen, onOpen, onClose } = useDisclosure(); + const { t } = useTranslation(); + const { register, handleSubmit } = useForm({ + defaultValues: { + openapiContent: '' + } + }); + + const { toast } = useToast(); + + const handleFileProcessing = async (content: string) => { + try { + let data; + try { + data = JSON.parse(content); + } catch (jsonError) { + try { + data = yaml.load(content, { schema: yaml.FAILSAFE_SCHEMA }); + } catch (yamlError) { + console.error(yamlError); + throw new Error(); + } + } + + const firstPathName = Object.keys(data.paths)[0]; + const firstPathData = data.paths[firstPathName]; + const firstRequestMethod = Object.keys(firstPathData)[0]; + const firstRequestMethodData = firstPathData[firstRequestMethod]; + const firstRequestParameters = firstRequestMethodData.parameters || []; + + const pathParams = []; + const headerParams = []; + for (const parameter of firstRequestParameters) { + if (parameter.in === 'path') { + pathParams.push({ + key: parameter.name, + type: parameter.schema.type + }); + } else { + headerParams.push({ + key: parameter.name, + type: parameter.schema.type + }); + } + } + + const requestBodySchema = + firstRequestMethodData.requestBody?.content?.['application/json']?.schema; + let requestBodyValue = ''; + if (requestBodySchema) { + requestBodyValue = JSON.stringify(requestBodySchema, null, 2); + } + + const requestUrl = inputs.find((item) => item.key === ModuleInputKeyEnum.httpReqUrl); + const requestMethod = inputs.find((item) => item.key === ModuleInputKeyEnum.httpMethod); + const params = inputs.find((item) => item.key === ModuleInputKeyEnum.httpParams); + const headers = inputs.find((item) => item.key === ModuleInputKeyEnum.httpHeaders); + const jsonBody = inputs.find((item) => item.key === ModuleInputKeyEnum.httpJsonBody); + + onChangeNode({ + moduleId, + type: 'updateInput', + key: ModuleInputKeyEnum.httpReqUrl, + value: { + ...requestUrl, + value: firstPathName + } + }); + + onChangeNode({ + moduleId, + type: 'updateInput', + key: ModuleInputKeyEnum.httpMethod, + value: { + ...requestMethod, + value: methodMap[firstRequestMethod.toLowerCase() as RequestMethod] || 'GET' + } + }); + + onChangeNode({ + moduleId, + type: 'updateInput', + key: ModuleInputKeyEnum.httpParams, + value: { + ...params, + value: pathParams + } + }); + + onChangeNode({ + moduleId, + type: 'updateInput', + key: ModuleInputKeyEnum.httpHeaders, + value: { + ...headers, + value: headerParams + } + }); + + onChangeNode({ + moduleId, + type: 'updateInput', + key: ModuleInputKeyEnum.httpJsonBody, + value: { + ...jsonBody, + value: requestBodyValue + } + }); + + onClose(); + + toast({ + title: t('common.Import success'), + status: 'success' + }); + } catch (error: any) { + toast({ + title: t('common.Import failed'), + description: error.message, + status: 'error' + }); + console.error(error); + } + }; + + return ( + <> + {children && {children}} + + +