diff --git a/docSite/content/docs/course/data_search.md b/docSite/content/docs/course/data_search.md index baa74cd13..64fcc5bd2 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 60eafc8ed..93bc62579 100644 --- a/docSite/content/docs/development/configuration.md +++ b/docSite/content/docs/development/configuration.md @@ -13,7 +13,163 @@ weight: 708 这个配置文件中包含了系统级参数、AI 对话的模型、function 模型等…… -## 4.6.8+ 版本新配置文件 +## 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 新配置文件 llm模型全部合并 @@ -33,10 +189,11 @@ llm模型全部合并 "maxResponse": 4000, // 最大回复 "quoteMaxToken": 13000, // 最大引用内容 "maxTemperature": 1.2, // 最大温度 - "charsPointsPrice": 0, + "inputPrice": 0, + "outputPrice": 0, "censor": false, "vision": false, // 是否支持图片输入 - "datasetProcess": false, // 是否设置为知识库处理模型(QA),务必保证至少有一个为true,否则知识库会报错 + "datasetProcess": false, // 是否设置为知识库处理模型 "toolChoice": true, // 是否支持工具选择 "functionCall": false, // 是否支持函数调用 "customCQPrompt": "", // 自定义文本分类提示词(不支持工具和函数调用的模型 @@ -51,7 +208,8 @@ llm模型全部合并 "maxResponse": 16000, "quoteMaxToken": 13000, "maxTemperature": 1.2, - "charsPointsPrice": 0, + "inputPrice": 0, + "outputPrice": 0, "censor": false, "vision": false, "datasetProcess": true, @@ -69,7 +227,8 @@ llm模型全部合并 "maxResponse": 4000, "quoteMaxToken": 100000, "maxTemperature": 1.2, - "charsPointsPrice": 0, + "inputPrice": 0, + "outputPrice": 0, "censor": false, "vision": false, "datasetProcess": false, @@ -87,9 +246,10 @@ llm模型全部合并 "maxResponse": 4000, "quoteMaxToken": 100000, "maxTemperature": 1.2, - "charsPointsPrice": 0, + "inputPrice": 0, + "outputPrice": 0, "censor": false, - "vision": true, + "vision": false, "datasetProcess": false, "toolChoice": true, "functionCall": false, @@ -103,7 +263,8 @@ llm模型全部合并 { "model": "text-embedding-ada-002", "name": "Embedding-2", - "charsPointsPrice": 0, + "inputPrice": 0, + "outputPrice": 0, "defaultToken": 700, "maxToken": 3000, "weight": 100, @@ -115,7 +276,8 @@ llm模型全部合并 { "model": "tts-1", "name": "OpenAI TTS1", - "charsPointsPrice": 0, + "inputPrice": 0, + "outputPrice": 0, "voices": [ { "label": "Alloy", "value": "alloy", "bufferId": "openai-Alloy" }, { "label": "Echo", "value": "echo", "bufferId": "openai-Echo" }, @@ -129,7 +291,8 @@ llm模型全部合并 "whisperModel": { "model": "whisper-1", "name": "Whisper1", - "charsPointsPrice": 0 + "inputPrice": 0, + "outputPrice": 0 } } ``` @@ -150,7 +313,7 @@ llm模型全部合并 { "model": "bge-reranker-base", // 随意 "name": "检索重排-base", // 随意 - "charsPointsPrice": 0, + "inputPrice": 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 b4c95b613..8eaed68eb 100644 --- a/docSite/content/docs/development/docker.md +++ b/docSite/content/docs/development/docker.md @@ -110,7 +110,6 @@ 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 ce597d432..fe0a98cbd 100644 --- a/docSite/content/docs/development/one-api.md +++ b/docSite/content/docs/development/one-api.md @@ -116,7 +116,8 @@ CHAT_API_KEY=sk-xxxxxx "maxResponse": 4000, // 最大回复 "quoteMaxToken": 13000, // 最大引用内容 "maxTemperature": 1.2, // 最大温度 - "charsPointsPrice": 0, + "inputPrice": 0, + "outputPrice": 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 2de562041..8fe90fb7d 100644 --- a/docSite/content/docs/development/openapi/dataset.md +++ b/docSite/content/docs/development/openapi/dataset.md @@ -13,25 +13,12 @@ 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}}' \ @@ -167,7 +154,7 @@ curl --location --request GET 'http://localhost:3000/api/core/dataset/list?paren "vectorModel": { "model": "text-embedding-ada-002", "name": "Embedding-2", - "charsPointsPrice": 0, + "inputPrice": 0, "defaultToken": 512, "maxToken": 8000, "weight": 100 @@ -226,7 +213,7 @@ curl --location --request GET 'http://localhost:3000/api/core/dataset/detail?id= "vectorModel": { "model": "text-embedding-ada-002", "name": "Embedding-2", - "charsPointsPrice": 0, + "inputPrice": 0, "defaultToken": 512, "maxToken": 8000, "weight": 100 @@ -236,7 +223,8 @@ curl --location --request GET 'http://localhost:3000/api/core/dataset/detail?id= "name": "FastAI-16k", "maxContext": 16000, "maxResponse": 16000, - "charsPointsPrice": 0 + "inputPrice": 0, + "outputPrice": 0 }, "intro": "", "permission": "private", @@ -812,33 +800,6 @@ 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 组数据。 @@ -864,14 +825,11 @@ curl --location --request POST 'https://api.fastgpt.in/api/core/dataset/data/pus { "q": "你会什么?", "a": "我什么都会", - "indexes": [ - { - "text":"自定义索引1" - }, - { - "text":"自定义索引2" - } - ] + "indexes": [{ + "defaultIndex": false, + "type":"custom", + "text":"自定义索引,不使用默认索引" + }] } ] }' @@ -892,7 +850,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 >}} @@ -908,6 +866,7 @@ curl --location --request POST 'https://api.fastgpt.in/api/core/dataset/data/pus "data": { "insertLen": 1, // 最终插入成功的数量 "overToken": [], // 超出 token 的 + "repeat": [], // 重复的数量 "error": [] // 其他错误 } @@ -1091,16 +1050,7 @@ curl --location --request PUT 'http://localhost:3000/api/core/dataset/data/updat "id":"65abd4b29d1448617cba61db", "q":"测试111", "a":"sss", - "indexes":[ - { - "dataId": "xxx", - "defaultIndex":false, - "text":"自定义索引1" - }, - { - "text":"修改后的自定义索引2。(会删除原来的自定义索引2,并插入新的自定义索引2)" - } - ] + "indexes":[] }' ``` @@ -1114,7 +1064,7 @@ curl --location --request PUT 'http://localhost:3000/api/core/dataset/data/updat - id: 数据的id - q: 主要数据(选填) - a: 辅助数据(选填) -- indexes: 自定义索引(选填),类型参考`为集合批量添加添加数据`。如果创建时候有自定义索引, +- indexes: 自定义索引(选填),类型参考`为集合批量添加添加数据`,建议直接不传。更新q,a后,如果有默认索引,则会直接更新默认索引。 {{% /alert %}} {{< /markdownify >}} diff --git a/docSite/content/docs/development/openapi/share.md b/docSite/content/docs/development/openapi/share.md index 8ae7c9588..4040b3077 100644 --- a/docSite/content/docs/development/openapi/share.md +++ b/docSite/content/docs/development/openapi/share.md @@ -169,6 +169,8 @@ 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' \ @@ -176,117 +178,72 @@ curl --location --request POST '{{host}}/shareAuth/finish' \ "token": "{{authToken}}", "responseData": [ { - "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": "KB Search", + "price": 1.2000000000000002, + "model": "Embedding-2", + "tokens": 6, + "similarity": 0.61, + "limit": 3 }, { - "moduleName": "AI 对话", - "moduleType": "chatNode", - "totalPoints": 0.593, + "moduleName": "AI Chat", + "price": 454.5, "model": "FastAI-4k", - "charsLength": 593, - "query": "导演是谁", - "maxToken": 2000, + "tokens": 303, + "question": "导演是谁", + "answer": "电影《铃芽之旅》的导演是新海诚。", + "maxToken": 2050, "quoteList": [ { - "id": "65bb346a53698398479a8854", - "q": "导演是谁?", + "dataset_id": "646627f4f7b896cfd8910e38", + "id": "8099", + "q": "本作的主人公是谁?", + "a": "本作的主人公是名叫铃芽的少女。", + "source": "手动修改" + }, + { + "dataset_id": "646627f4f7b896cfd8910e38", + "id": "8686", + "q": "电影《铃芽之旅》男主角是谁?", + "a": "电影《铃芽之旅》男主角是宗像草太,由松村北斗配音。", + "source": "" + }, + { + "dataset_id": "646627f4f7b896cfd8910e38", + "id": "19339", + "q": "电影《铃芽之旅》的导演是谁?22", "a": "电影《铃芽之旅》的导演是新海诚。", - "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 - } - ] + "source": "手动修改" } ], - "historyPreview": [ + "completeMessages": [ + { + "obj": "System", + "value": "下面是知识库内容:\n1. [本作的主人公是谁?\n本作的主人公是名叫铃芽的少女。]\n2. [电影《铃芽之旅》男主角是谁?\n电影《铃芽之旅》男主角是宗像草太,由松村北斗配音。]\n3. [电影《铃芽之旅》的导演是谁?22\n电影《铃芽之旅》的导演是新海诚。]\n" + }, + { + "obj": "System", + "value": "1.请记住,你的身份是百度的下一代知识增强语言模型,能够完全根据知识库提供的内容回答问题。\n\n2. 你忘记了关于电影《铃芽之旅》以外的内容。" + }, + { + "obj": "System", + "value": "你仅回答关于电影《玲芽之旅》的问题,其余问题直接回复: 我不清楚。" + }, { "obj": "Human", - "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问题:\"\"\"导演是谁\"\"\"" + "value": "导演是谁" }, { "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 9353384e9..28127db26 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 92b06088f..973bf5397 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/initv467 +1. https://xxxxx/api/admin/initv464 ```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 ad5c9d9b1..1acd9a9e4 100644 --- a/docSite/content/docs/development/upgrading/468.md +++ b/docSite/content/docs/development/upgrading/468.md @@ -36,7 +36,6 @@ 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 deleted file mode 100644 index 3446bfb92..000000000 --- a/docSite/content/docs/development/upgrading/469.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -title: 'V4.6.9(进行中)' -description: 'FastGPT V4.6.9更新说明' -icon: 'upgrade' -draft: false -toc: true -weight: 827 ---- - - -## V4.6.9 更新说明 - -1. 优化 - 重写了计量模式 \ No newline at end of file diff --git a/docSite/content/docs/workflow/examples/google_search.md b/docSite/content/docs/workflow/examples/google_search.md index 77096db85..ef58d2a65 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": "selectLLMModel", + "type": "selectExtractModel", "valueType": "string", "label": "core.module.input.label.LLM", "required": true, @@ -264,7 +264,7 @@ export default async function (ctx: FunctionContext) { }, { "key": "model", - "type": "selectLLMModel", + "type": "selectChatModel", "label": "core.module.input.label.aiModel", "required": true, "valueType": "string", @@ -635,7 +635,7 @@ export default async function (ctx: FunctionContext) { }, { "key": "model", - "type": "selectLLMModel", + "type": "selectChatModel", "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 6d593fb6c..d6614b106 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": "selectLLMModel", + "type": "selectExtractModel", "valueType": "string", "label": "core.module.input.label.LLM", "required": true, @@ -401,7 +401,7 @@ HTTP 模块允许你调用任意 GET/POST 类型的 HTTP 接口,从而实现 }, { "key": "model", - "type": "selectLLMModel", + "type": "selectCQModel", "valueType": "string", "label": "core.module.input.label.Classify model", "required": true, @@ -614,7 +614,7 @@ HTTP 模块允许你调用任意 GET/POST 类型的 HTTP 接口,从而实现 }, { "key": "model", - "type": "selectLLMModel", + "type": "selectChatModel", "label": "core.module.input.label.aiModel", "required": true, "valueType": "string", @@ -835,7 +835,7 @@ HTTP 模块允许你调用任意 GET/POST 类型的 HTTP 接口,从而实现 }, { "key": "model", - "type": "selectLLMModel", + "type": "selectExtractModel", "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 f146ba7dc..c4451eb3d 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 be1ca7d52..5922ed198 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 3ba187c82..315c9abf1 100644 --- a/packages/global/common/error/code/team.ts +++ b/packages/global/common/error/code/team.ts @@ -3,25 +3,11 @@ import { ErrType } from '../errorCode'; /* team: 500000 */ export enum TeamErrEnum { teamOverSize = 'teamOverSize', - unAuthTeam = 'unAuthTeam', - aiPointsNotEnough = 'aiPointsNotEnough', - datasetSizeNotEnough = 'datasetSizeNotEnough', - datasetAmountNotEnough = 'datasetAmountNotEnough', - appAmountNotEnough = 'appAmountNotEnough', - pluginAmountNotEnough = 'pluginAmountNotEnough', - websiteSyncNotEnough = 'websiteSyncNotEnough', - reRankNotEnough = 'reRankNotEnough' + unAuthTeam = 'unAuthTeam' } const teamErr = [ { statusText: TeamErrEnum.teamOverSize, message: 'error.team.overSize' }, - { 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: '无权使用检索重排~' } + { statusText: TeamErrEnum.unAuthTeam, message: '无权操作该团队' } ]; export default teamErr.reduce((acc, cur, index) => { return { diff --git a/packages/global/common/math/tools.ts b/packages/global/common/math/tools.ts deleted file mode 100644 index ece67f496..000000000 --- a/packages/global/common/math/tools.ts +++ /dev/null @@ -1,4 +0,0 @@ -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/core/ai/model.d.ts b/packages/global/core/ai/model.d.ts index e5b783a92..7203201ac 100644 --- a/packages/global/core/ai/model.d.ts +++ b/packages/global/core/ai/model.d.ts @@ -6,7 +6,8 @@ export type LLMModelItemType = { quoteMaxToken: number; maxTemperature: number; - charsPointsPrice: number; // 1k chars=n points + inputPrice: number; + outputPrice: number; censor?: boolean; vision?: boolean; @@ -26,7 +27,8 @@ export type VectorModelItemType = { model: string; name: string; defaultToken: number; - charsPointsPrice: number; + inputPrice: number; + outputPrice: number; maxToken: number; weight: number; hidden?: boolean; @@ -36,7 +38,8 @@ export type VectorModelItemType = { export type ReRankModelItemType = { model: string; name: string; - charsPointsPrice: number; + inputPrice: number; + outputPrice?: number; requestUrl?: string; requestAuth?: string; }; @@ -44,12 +47,14 @@ export type ReRankModelItemType = { export type AudioSpeechModelType = { model: string; name: string; - charsPointsPrice: number; + inputPrice: number; + outputPrice?: number; voices: { label: string; value: string; bufferId: string }[]; }; export type WhisperModelType = { model: string; name: string; - charsPointsPrice: number; // 60s = n points + inputPrice: number; + outputPrice?: number; }; diff --git a/packages/global/core/ai/model.ts b/packages/global/core/ai/model.ts index 8d2dfe156..ad7a67c69 100644 --- a/packages/global/core/ai/model.ts +++ b/packages/global/core/ai/model.ts @@ -8,7 +8,8 @@ export const defaultQAModels: LLMModelItemType[] = [ maxResponse: 16000, quoteMaxToken: 13000, maxTemperature: 1.2, - charsPointsPrice: 0, + inputPrice: 0, + outputPrice: 0, censor: false, vision: false, datasetProcess: true, @@ -25,7 +26,8 @@ export const defaultVectorModels: VectorModelItemType[] = [ { model: 'text-embedding-ada-002', name: 'Embedding-2', - charsPointsPrice: 0, + inputPrice: 0, + outputPrice: 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 e7917f37f..7a9d526b3 100644 --- a/packages/global/core/app/api.d.ts +++ b/packages/global/core/app/api.d.ts @@ -17,7 +17,6 @@ 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 8f0df617e..3a17f24d1 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,7 +20,6 @@ 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 a43b62387..43f650dc8 100644 --- a/packages/global/core/chat/constants.ts +++ b/packages/global/core/chat/constants.ts @@ -27,8 +27,7 @@ export enum ChatSourceEnum { test = 'test', online = 'online', share = 'share', - api = 'api', - team = 'team' + api = 'api' } export const ChatSourceMap = { [ChatSourceEnum.test]: { @@ -42,9 +41,6 @@ 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 bf1d76bbe..6b32fb7cd 100644 --- a/packages/global/core/chat/type.d.ts +++ b/packages/global/core/chat/type.d.ts @@ -4,7 +4,6 @@ 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 = { @@ -26,22 +25,6 @@ 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: Array, - teamInfo: teamInfoSchema -} - export type ChatWithAppSchema = Omit & { appId: AppSchema; }; @@ -105,15 +88,15 @@ export type ChatHistoryItemType = HistoryItemType & { export type moduleDispatchResType = { // common moduleLogo?: string; + price?: number; runningTime?: number; - query?: string; - textOutput?: string; - - // bill + inputTokens?: number; + outputTokens?: number; charsLength?: number; model?: string; + query?: string; contextTotalLen?: number; - totalPoints?: number; + textOutput?: string; // chat temperature?: number; @@ -128,7 +111,6 @@ 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 4afb5f744..005fcf155 100644 --- a/packages/global/core/dataset/constants.ts +++ b/packages/global/core/dataset/constants.ts @@ -71,6 +71,30 @@ 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 3b84a1f7f..bac51bd41 100644 --- a/packages/global/core/dataset/type.d.ts +++ b/packages/global/core/dataset/type.d.ts @@ -3,6 +3,7 @@ import { PermissionTypeEnum } from '../../support/permission/constant'; import { PushDatasetDataChunkProps } from './api'; import { DatasetCollectionTypeEnum, + DatasetDataIndexTypeEnum, DatasetStatusEnum, DatasetTypeEnum, SearchScoreTypeEnum, @@ -63,6 +64,7 @@ export type DatasetCollectionSchemaType = { export type DatasetDataIndexItemType = { defaultIndex: boolean; dataId: string; // pg data id + type: `${DatasetDataIndexTypeEnum}`; text: string; }; export type DatasetDataSchemaType = { @@ -140,7 +142,6 @@ export type DatasetCollectionItemType = CollectionWithDatasetType & { /* ================= data ===================== */ export type DatasetDataItemType = { id: string; - teamId: string; datasetId: string; collectionId: string; sourceName: string; @@ -172,7 +173,7 @@ export type DatasetFileSchema = { /* ============= search =============== */ export type SearchDataResponseItemType = Omit< DatasetDataItemType, - 'teamId' | 'indexes' | 'isOwner' | 'canWrite' + '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 d9ccb6480..53809965c 100644 --- a/packages/global/core/dataset/utils.ts +++ b/packages/global/core/dataset/utils.ts @@ -1,4 +1,4 @@ -import { TrainingModeEnum, DatasetCollectionTypeEnum } from './constants'; +import { TrainingModeEnum, DatasetCollectionTypeEnum, DatasetDataIndexTypeEnum } from './constants'; import { getFileIcon } from '../../common/file/icon'; import { strIsLink } from '../../common/string/tools'; @@ -41,6 +41,7 @@ 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 0eaa7e76f..db24dc8d7 100644 --- a/packages/global/core/module/constants.ts +++ b/packages/global/core/module/constants.ts @@ -89,10 +89,9 @@ 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 4b285437f..dddc78a16 100644 --- a/packages/global/core/module/node/constant.ts +++ b/packages/global/core/module/node/constant.ts @@ -20,7 +20,9 @@ export enum FlowNodeInputTypeEnum { aiSettings = 'aiSettings', // ai model select - selectLLMModel = 'selectLLMModel', + selectChatModel = 'selectChatModel', + selectCQModel = 'selectCQModel', + selectExtractModel = 'selectExtractModel', // dataset special input selectDataset = 'selectDataset', @@ -56,7 +58,7 @@ export enum FlowNodeTypeEnum { pluginModule = 'pluginModule', pluginInput = 'pluginInput', pluginOutput = 'pluginOutput', - queryExtension = 'cfr' + cfr = 'cfr' // abandon } diff --git a/packages/global/core/module/template/system/aiChat.ts b/packages/global/core/module/template/system/aiChat.ts index f11ec5c09..dd4074f2f 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.selectLLMModel, + type: FlowNodeInputTypeEnum.selectChatModel, 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 fff8826d0..4e0d1a141 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.selectLLMModel, + type: FlowNodeInputTypeEnum.selectCQModel, 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 ef85f78f2..5616ffa40 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.selectLLMModel, + type: FlowNodeInputTypeEnum.selectExtractModel, valueType: ModuleIOValueTypeEnum.string, label: 'core.module.input.label.LLM', required: true, diff --git a/packages/global/core/module/template/system/queryExtension.ts b/packages/global/core/module/template/system/coreferenceResolution.ts similarity index 76% rename from packages/global/core/module/template/system/queryExtension.ts rename to packages/global/core/module/template/system/coreferenceResolution.ts index dc598190f..daabad15f 100644 --- a/packages/global/core/module/template/system/queryExtension.ts +++ b/packages/global/core/module/template/system/coreferenceResolution.ts @@ -3,7 +3,7 @@ import { FlowNodeOutputTypeEnum, FlowNodeTypeEnum } from '../../node/constant'; -import { FlowModuleTemplateType } from '../../type'; +import { FlowModuleTemplateType } from '../../type.d'; import { ModuleIOValueTypeEnum, ModuleInputKeyEnum, @@ -17,19 +17,19 @@ import { } from '../input'; import { Output_Template_UserChatInput } from '../output'; -export const AiQueryExtension: FlowModuleTemplateType = { +export const AiCFR: FlowModuleTemplateType = { id: FlowNodeTypeEnum.chatNode, templateType: ModuleTemplateTypeEnum.other, - flowType: FlowNodeTypeEnum.queryExtension, + flowType: FlowNodeTypeEnum.cfr, avatar: '/imgs/module/cfr.svg', name: 'core.module.template.Query extension', - intro: 'core.module.template.Query extension intro', + intro: '该模块已合并到知识库搜索参数中,无需单独使用。模块将于2024/3/31弃用,请尽快修改。', showStatus: true, inputs: [ Input_Template_Switch, { key: ModuleInputKeyEnum.aiModel, - type: FlowNodeInputTypeEnum.selectLLMModel, + type: FlowNodeInputTypeEnum.selectExtractModel, label: 'core.module.input.label.aiModel', required: true, valueType: ModuleIOValueTypeEnum.string, @@ -39,7 +39,7 @@ export const AiQueryExtension: FlowModuleTemplateType = { { key: ModuleInputKeyEnum.aiSystemPrompt, type: FlowNodeInputTypeEnum.textarea, - label: 'core.app.edit.Query extension background prompt', + label: 'core.module.input.label.Background', max: 300, valueType: ModuleIOValueTypeEnum.string, description: 'core.app.edit.Query extension background tip', @@ -54,8 +54,7 @@ export const AiQueryExtension: FlowModuleTemplateType = { Output_Template_UserChatInput, { key: ModuleOutputKeyEnum.text, - label: 'core.module.output.label.query extension result', - description: 'core.module.output.description.query extension result', + label: 'core.module.output.label.cfr 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 02acd6074..231747aed 100644 --- a/packages/global/core/module/type.d.ts +++ b/packages/global/core/module/type.d.ts @@ -1,14 +1,6 @@ import { FlowNodeTypeEnum } from './node/constant'; -import { - ModuleIOValueTypeEnum, - ModuleOutputKeyEnum, - ModuleTemplateTypeEnum, - VariableInputEnum -} from './constants'; +import { ModuleIOValueTypeEnum, 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 @@ -113,7 +105,7 @@ export type ChatDispatchProps = { mode: 'test' | 'chat'; teamId: string; tmbId: string; - user: UserModelSchema; + user: UserType; appId: string; chatId?: string; responseChatItemId?: string; @@ -124,10 +116,7 @@ export type ChatDispatchProps = { }; export type ModuleDispatchProps = ChatDispatchProps & { - module: RunningModuleItemType; + outputs: RunningModuleItemType['outputs']; + inputs: RunningModuleItemType['inputs']; 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 0f5dca701..66dd1426f 100644 --- a/packages/global/support/openapi/type.d.ts +++ b/packages/global/support/openapi/type.d.ts @@ -1,5 +1,6 @@ export type OpenApiSchema = { _id: string; + userId: string; teamId: string; tmbId: string; createTime: Date; @@ -7,9 +8,9 @@ export type OpenApiSchema = { apiKey: string; appId?: string; name: string; - usagePoints: number; + usage: number; limit?: { expiredTime?: Date; - maxUsagePoints: number; + credit?: number; }; }; diff --git a/packages/global/support/outLink/api.d.ts b/packages/global/support/outLink/api.d.ts index 20099288a..c53532bae 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 './type.d'; +import { OutLinkSchema } from '@fastgpt/global/support/outLink/type'; export type AuthOutLinkInitProps = { outLinkUid: string; diff --git a/packages/global/support/outLink/type.d.ts b/packages/global/support/outLink/type.d.ts index 812f854a2..e6e19c5c5 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; - usagePoints: number; + total: number; lastTime: Date; type: `${OutLinkTypeEnum}`; responseDetail: boolean; limit?: { expiredTime?: Date; QPM: number; - maxUsagePoints: number; + credit: number; hookUrl?: string; }; }; diff --git a/packages/global/support/permission/type.d.ts b/packages/global/support/permission/type.d.ts index f0ad8f48e..eac0ce9ff 100644 --- a/packages/global/support/permission/type.d.ts +++ b/packages/global/support/permission/type.d.ts @@ -1,6 +1,7 @@ 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 024e869c9..b9840c2d0 100644 --- a/packages/global/support/user/team/constant.ts +++ b/packages/global/support/user/team/constant.ts @@ -1,6 +1,5 @@ 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 121de43bd..663d9afab 100644 --- a/packages/global/support/user/team/controller.d.ts +++ b/packages/global/support/user/team/controller.d.ts @@ -15,7 +15,6 @@ 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 af34a8ea4..4fff5930a 100644 --- a/packages/global/support/user/team/type.d.ts +++ b/packages/global/support/user/team/type.d.ts @@ -9,23 +9,11 @@ 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; @@ -38,13 +26,13 @@ export type TeamMemberSchema = { defaultTeam: boolean; }; -export type TeamMemberWithUserSchema = Omit & { +export type TeamMemberWithUserSchema = TeamMemberSchema & { userId: UserModelSchema; }; -export type TeamMemberWithTeamSchema = Omit & { +export type TeamMemberWithTeamSchema = TeamMemberSchema & { teamId: TeamSchema; }; -export type TeamMemberWithTeamAndUserSchema = Omit & { +export type TeamMemberWithTeamAndUserSchema = TeamMemberWithTeamSchema & { userId: UserModelSchema; }; diff --git a/packages/global/support/user/type.d.ts b/packages/global/support/user/type.d.ts index 3a3d56ea0..3cb978810 100644 --- a/packages/global/support/user/type.d.ts +++ b/packages/global/support/user/type.d.ts @@ -29,5 +29,4 @@ 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 034b5e368..dcc421ecf 100644 --- a/packages/global/support/wallet/bill/api.d.ts +++ b/packages/global/support/wallet/bill/api.d.ts @@ -1,18 +1,25 @@ -import { BillTypeEnum } from './constants'; +import { BillSourceEnum } from './constants'; +import { BillListItemCountType, BillListItemType } from './type'; + +export type CreateTrainingBillProps = { + name: string; + datasetId: string; +}; + +export type ConcatBillProps = BillListItemCountType & { + teamId: string; + tmbId: string; + billId?: string; + total: number; + listIndex?: number; +}; export type CreateBillProps = { - type: `${BillTypeEnum}`; - - // balance - balance?: number; // read - - month?: number; - // extra dataset size - extraDatasetSize?: number; // 1k - extraPoints?: number; // 100w -}; -export type CreateBillResponse = { - billId: string; - codeUrl: string; - readPrice: number; + teamId: string; + tmbId: string; + appName: string; + appId?: string; + total: number; + source: `${BillSourceEnum}`; + list: BillListItemType[]; }; diff --git a/packages/global/support/wallet/bill/constants.ts b/packages/global/support/wallet/bill/constants.ts index 03edfabf6..87832de4c 100644 --- a/packages/global/support/wallet/bill/constants.ts +++ b/packages/global/support/wallet/bill/constants.ts @@ -1,57 +1,34 @@ -export enum BillTypeEnum { - balance = 'balance', +// model price: xxx/1k tokens +// ¥1 = 100000. +export const PRICE_SCALE = 100000; + +export enum BillSourceEnum { + fastgpt = 'fastgpt', + api = 'api', + shareLink = 'shareLink', + training = 'training', + standSubPlan = 'standSubPlan', - extraDatasetSub = 'extraDatasetSub', - extraPoints = 'extraPoints' + extraDatasetSub = 'extraDatasetSub' } -export const billTypeMap = { - [BillTypeEnum.balance]: { - label: 'support.wallet.subscription.type.balance' + +export const BillSourceMap = { + [BillSourceEnum.fastgpt]: { + label: '在线使用' }, - [BillTypeEnum.standSubPlan]: { + [BillSourceEnum.api]: { + label: 'Api' + }, + [BillSourceEnum.shareLink]: { + label: '免登录链接' + }, + [BillSourceEnum.training]: { + label: 'dataset.Training Name' + }, + [BillSourceEnum.standSubPlan]: { label: 'support.wallet.subscription.type.standard' }, - [BillTypeEnum.extraDatasetSub]: { + [BillSourceEnum.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' - }, - [BillStatusEnum.REFUND]: { - label: 'support.wallet.bill.status.refund' - }, - [BillStatusEnum.NOTPAY]: { - label: 'support.wallet.bill.status.notpay' - }, - [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' - }, - [BillPayWayEnum.wx]: { - label: 'support.wallet.bill.payWay.wx' - } -}; - -export const SUB_DATASET_SIZE_RATE = 1000; -export const SUB_EXTRA_POINT_RATE = 1000000; diff --git a/packages/global/support/wallet/bill/tools.ts b/packages/global/support/wallet/bill/tools.ts new file mode 100644 index 000000000..300875e94 --- /dev/null +++ b/packages/global/support/wallet/bill/tools.ts @@ -0,0 +1,26 @@ +/* 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 e317e4656..1b7e00843 100644 --- a/packages/global/support/wallet/bill/type.d.ts +++ b/packages/global/support/wallet/bill/type.d.ts @@ -1,29 +1,35 @@ -import { StandardSubLevelEnum, SubModeEnum, SubTypeEnum } from '../sub/constants'; -import { BillPayWayEnum, BillTypeEnum } from './constants'; +import { CreateBillProps } from './api'; +import { BillSourceEnum } from './constants'; -export type BillSchemaType = { - _id: string; - 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 BillListItemCountType = { + inputTokens?: number; + outputTokens?: number; + charsLength?: number; + duration?: number; + + // sub + datasetSize?: number; + + // abandon + tokenLen?: number; }; - -export type ChatModuleBillType = { - totalPoints: number; +export type BillListItemType = BillListItemCountType & { moduleName: string; - model: string; - charsLength: number; + amount: number; + model?: string; +}; + +export type BillSchema = CreateBillProps & { + _id: string; + time: Date; +}; + +export type BillItemType = { + id: string; + // memberName: string; + time: Date; + appName: string; + source: BillSchema['source']; + total: number; + list: BillSchema['list']; }; diff --git a/packages/global/support/wallet/constants.ts b/packages/global/support/wallet/constants.ts deleted file mode 100644 index b6dc8e005..000000000 --- a/packages/global/support/wallet/constants.ts +++ /dev/null @@ -1,3 +0,0 @@ -// 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 new file mode 100644 index 000000000..41f7fd6d8 --- /dev/null +++ b/packages/global/support/wallet/pay/constants.ts @@ -0,0 +1,41 @@ +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 new file mode 100644 index 000000000..6a2ea3436 --- /dev/null +++ b/packages/global/support/wallet/pay/type.d.ts @@ -0,0 +1,18 @@ +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 2db4b6d0d..8813e0f99 100644 --- a/packages/global/support/wallet/sub/api.d.ts +++ b/packages/global/support/wallet/sub/api.d.ts @@ -1,14 +1,26 @@ 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 9f32cc7f5..6951f9dbe 100644 --- a/packages/global/support/wallet/sub/constants.ts +++ b/packages/global/support/wallet/sub/constants.ts @@ -1,3 +1,5 @@ +export const POINTS_SCALE = 10000; + export enum SubTypeEnum { standard = 'standard', extraDatasetSize = 'extraDatasetSize', @@ -17,16 +19,20 @@ export const subTypeMap = { export enum SubStatusEnum { active = 'active', - expired = 'expired' + canceled = 'canceled' } export const subStatusMap = { [SubStatusEnum.active]: { label: 'support.wallet.subscription.status.active' }, - [SubStatusEnum.expired]: { + [SubStatusEnum.canceled]: { label: 'support.wallet.subscription.status.canceled' } }; +export const subSelectMap = { + true: SubStatusEnum.active, + false: SubStatusEnum.canceled +}; export enum SubModeEnum { month = 'month', @@ -34,11 +40,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 } }; @@ -57,7 +63,7 @@ export const standardSubLevelMap = { }, [StandardSubLevelEnum.experience]: { label: 'support.wallet.subscription.standardSubLevel.experience', - desc: '' + desc: 'support.wallet.subscription.standardSubLevel.experience 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 d1f049db4..30fe6f39b 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 / month - pointPrice: number; // read price/ one thousand - totalPoints: number; // n + price: number; // read price + pointPrice: number; // read price/ one ten thousand 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 - permissionCustomApiKey: boolean; - permissionCustomCopyright: boolean; // feature - permissionWebsiteSync: boolean; - permissionReRank: boolean; + reRankWeight: number; // 1~4 + totalPoints: number; // n ten thousand }; export type StandSubPlanLevelMapType = Record< @@ -27,9 +27,6 @@ export type SubPlanType = { [SubTypeEnum.extraDatasetSize]: { price: number; }; - [SubTypeEnum.extraPoints]: { - price: number; - }; }; export type TeamSubSchema = { @@ -37,32 +34,40 @@ 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; - surplusPoints: number; currentExtraDatasetSize: number; + nextExtraDatasetSize: number; + + currentExtraPoints: number; + nextExtraPoints: number; + + surplusPoints: number; + + // abandon + datasetStoreAmount?: number; + renew?: boolean; }; -export type FeTeamPlanStatusType = { +export type FeTeamSubType = { [SubTypeEnum.standard]?: TeamSubSchema; - standardConstants?: TeamStandardSubPlanItemType; + [SubTypeEnum.extraDatasetSize]?: TeamSubSchema; + [SubTypeEnum.extraPoints]?: TeamSubSchema; + standardMaxDatasetSize: number; totalPoints: number; usedPoints: number; - standardMaxDatasetSize?: number; - standardMaxPoints?: number; - // standard + extra + standardMaxPoints: number; datasetMaxSize: number; usedDatasetSize: number; }; diff --git a/packages/global/support/wallet/usage/api.d.ts b/packages/global/support/wallet/usage/api.d.ts deleted file mode 100644 index f87fd4e6c..000000000 --- a/packages/global/support/wallet/usage/api.d.ts +++ /dev/null @@ -1,26 +0,0 @@ -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 deleted file mode 100644 index 13dae786e..000000000 --- a/packages/global/support/wallet/usage/constants.ts +++ /dev/null @@ -1,21 +0,0 @@ -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 deleted file mode 100644 index ad1bfa240..000000000 --- a/packages/global/support/wallet/usage/tools.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* 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 deleted file mode 100644 index 8f686b607..000000000 --- a/packages/global/support/wallet/usage/type.d.ts +++ /dev/null @@ -1,26 +0,0 @@ -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/vectorStore/controller.ts b/packages/service/common/vectorStore/controller.ts index 47fdfa36b..a9a4c4a6b 100644 --- a/packages/service/common/vectorStore/controller.ts +++ b/packages/service/common/vectorStore/controller.ts @@ -38,22 +38,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/controller.ts b/packages/service/common/vectorStore/pg/controller.ts index 4dd6f6225..828dc983b 100644 --- a/packages/service/common/vectorStore/pg/controller.ts +++ b/packages/service/common/vectorStore/pg/controller.ts @@ -25,18 +25,6 @@ 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) { diff --git a/packages/service/core/ai/config.ts b/packages/service/core/ai/config.ts index 17aff89aa..68f3227fc 100644 --- a/packages/service/core/ai/config.ts +++ b/packages/service/core/ai/config.ts @@ -11,7 +11,6 @@ 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/functions/cfr.ts b/packages/service/core/ai/functions/cfr.ts new file mode 100644 index 000000000..8315997d9 --- /dev/null +++ b/packages/service/core/ai/functions/cfr.ts @@ -0,0 +1,159 @@ +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 e84e457b8..6bf9d194a 100644 --- a/packages/service/core/ai/functions/createQuestionGuide.ts +++ b/packages/service/core/ai/functions/createQuestionGuide.ts @@ -1,6 +1,5 @@ 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"]`; @@ -11,13 +10,6 @@ export async function createQuestionGuide({ messages: ChatMessageItemType[]; model: string; }) { - const concatMessages: ChatMessageItemType[] = [ - ...messages, - { - role: 'user', - content: Prompt_QuestionGuide - } - ]; const ai = getAIApi({ timeout: 480000 }); @@ -25,21 +17,28 @@ export async function createQuestionGuide({ model: model, temperature: 0.1, max_tokens: 200, - messages: concatMessages, + messages: [ + ...messages, + { + role: 'user', + content: Prompt_QuestionGuide + } + ], 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: [], - charsLength: 0 + inputTokens, + outputTokens }; } @@ -51,12 +50,14 @@ export async function createQuestionGuide({ try { return { result: JSON.parse(jsonStr), - charsLength + inputTokens, + outputTokens }; } catch (error) { return { result: [], - charsLength: 0 + inputTokens, + outputTokens }; } } diff --git a/packages/service/core/ai/functions/queryExtension.ts b/packages/service/core/ai/functions/queryExtension.ts index 48c5a50b8..a72820c62 100644 --- a/packages/service/core/ai/functions/queryExtension.ts +++ b/packages/service/core/ai/functions/queryExtension.ts @@ -1,19 +1,18 @@ 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 = `作为一个向量检索助手,你的任务是结合历史记录,从不同角度,为“原问题”生成个不同版本的“检索词”,从而提高向量检索的语义丰富度,提高向量检索的精度。生成的问题要求指向对象清晰明确。例如: 历史记录: """ """ 原问题: 介绍下剧情。 -检索词: ["介绍下故事的背景和主要人物。","故事的主题是什么?","剧情是是如何发展的?"] +检索词: ["发生了什么故事?","故事梗概是什么?","讲述了什么故事?"] ---------------- 历史记录: """ @@ -21,7 +20,7 @@ Q: 对话背景。 A: 当前对话是关于 FatGPT 的介绍和使用等。 """ 原问题: 怎么下载 -检索词: ["FastGPT 如何下载?","下载 FastGPT 需要什么条件?","有哪些渠道可以下载 FastGPT?"] +检索词: ["FastGPT 怎么下载?","下载 FastGPT 需要什么条件?","有哪些渠道可以下载 FastGPT?"] ---------------- 历史记录: """ @@ -31,15 +30,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。 """ -原问题: Tell me about him -检索词: ["Introduce labring, the author of FastGPT." ," Background information on author labring." "," Why does labring do FastGPT?"] +原问题: 介绍下他 +检索词: ["介绍下 FastGPT 的作者 labring。","作者 labring 的背景信息。","labring 为什么要做 FastGPT?"] ---------------- 历史记录: """ @@ -106,7 +105,8 @@ export const queryExtension = async ({ rawQuery: string; extensionQueries: string[]; model: string; - charsLength: number; + inputTokens: number; + outputTokens: number; }> => { const systemFewShot = chatBg ? `Q: 对话背景。 @@ -125,20 +125,18 @@ 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, - // @ts-ignore - messages, + messages: [ + { + role: 'user', + content: replaceVariable(defaultPrompt, { + query: `${query}`, + histories: concatFewShot + }) + } + ], stream: false }); @@ -148,7 +146,8 @@ A: ${chatBg} rawQuery: query, extensionQueries: [], model, - charsLength: 0 + inputTokens: 0, + outputTokens: 0 }; } @@ -161,7 +160,8 @@ A: ${chatBg} rawQuery: query, extensionQueries: queries, model, - charsLength: countGptMessagesChars(messages) + inputTokens: result.usage?.prompt_tokens || 0, + outputTokens: result.usage?.completion_tokens || 0 }; } catch (error) { console.log(error); @@ -169,7 +169,8 @@ A: ${chatBg} rawQuery: query, extensionQueries: [], model, - charsLength: 0 + inputTokens: 0, + outputTokens: 0 }; } }; diff --git a/packages/service/core/app/schema.ts b/packages/service/core/app/schema.ts index 0dba217b7..551e487e0 100644 --- a/packages/service/core/app/schema.ts +++ b/packages/service/core/app/schema.ts @@ -61,9 +61,6 @@ 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 cd1919af8..1a9e3b9f2 100644 --- a/packages/service/core/chat/chatItemSchema.ts +++ b/packages/service/core/chat/chatItemSchema.ts @@ -92,8 +92,6 @@ 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 5483b3e88..727608e0c 100644 --- a/packages/service/core/chat/chatSchema.ts +++ b/packages/service/core/chat/chatSchema.ts @@ -83,9 +83,6 @@ 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 769be3f47..91886749f 100644 --- a/packages/service/core/chat/utils.ts +++ b/packages/service/core/chat/utils.ts @@ -2,10 +2,7 @@ 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, - ChatMessageItemType -} from '@fastgpt/global/core/ai/type.d'; +import type { ChatCompletionContentPart } from '@fastgpt/global/core/ai/type.d'; import axios from 'axios'; /* slice chat context by tokens */ @@ -59,12 +56,6 @@ export function ChatContextFilter({ return [...systemPrompts, ...chats]; } -export const countMessagesChars = (messages: ChatItemType[]) => { - return messages.reduce((sum, item) => sum + item.value.length, 0); -}; -export const countGptMessagesChars = (messages: ChatMessageItemType[]) => - messages.reduce((sum, item) => sum + 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 9a5513050..791531abd 100644 --- a/packages/service/core/dataset/collection/controller.ts +++ b/packages/service/core/dataset/collection/controller.ts @@ -147,6 +147,8 @@ 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 d0779fbb3..3ba222c61 100644 --- a/packages/service/core/dataset/controller.ts +++ b/packages/service/core/dataset/controller.ts @@ -66,11 +66,6 @@ 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 35f5b05aa..46442b159 100644 --- a/packages/service/core/dataset/data/schema.ts +++ b/packages/service/core/dataset/data/schema.ts @@ -7,6 +7,10 @@ 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'; @@ -50,6 +54,11 @@ 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 4332423cf..5689e5afb 100644 --- a/packages/service/core/dataset/search/utils.ts +++ b/packages/service/core/dataset/search/utils.ts @@ -14,54 +14,22 @@ export const datasetSearchQueryExtension = async ({ extensionBg?: string; histories?: ChatItemType[]; }) => { - 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 - }; - } - })(); + // concat query + let queries = [query]; + let rewriteQuery = + histories.length > 0 + ? `${histories + .map((item) => { + return `${item.obj}: ${item.value}`; + }) + .join('\n')} + Human: ${query} + ` + : query; // ai extension const aiExtensionResult = await (async () => { - if (!extensionModel || alreadyExtension) return; + if (!extensionModel) return; const result = await queryExtension({ chatBg: extensionBg, query, @@ -71,13 +39,23 @@ export const datasetSearchQueryExtension = async ({ if (result.extensionQueries?.length === 0) return; return result; })(); + if (aiExtensionResult) { - queries = filterSamQuery(queries.concat(aiExtensionResult.extensionQueries)); + queries = 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: queries, + concatQueries: filterSameQueries, rewriteQuery, aiExtensionResult }; diff --git a/packages/service/core/dataset/training/controller.ts b/packages/service/core/dataset/training/controller.ts index 90eefe043..382ec9d93 100644 --- a/packages/service/core/dataset/training/controller.ts +++ b/packages/service/core/dataset/training/controller.ts @@ -57,7 +57,7 @@ export async function pushDataListToTrainingQueue({ if (trainingMode === TrainingModeEnum.chunk) { const vectorModelData = vectorModelList?.find((item) => item.model === vectorModel); if (!vectorModelData) { - return Promise.reject(`File model ${vectorModel} is inValid`); + return Promise.reject(`Model ${vectorModel} is inValid`); } return { @@ -70,7 +70,7 @@ export async function pushDataListToTrainingQueue({ if (trainingMode === TrainingModeEnum.qa) { const qaModelData = datasetModelList?.find((item) => item.model === agentModel); if (!qaModelData) { - return Promise.reject(`Vector model ${agentModel} is inValid`); + return Promise.reject(`Model ${agentModel} is inValid`); } return { maxToken: qaModelData.maxContext * 0.8, diff --git a/packages/service/core/dataset/training/schema.ts b/packages/service/core/dataset/training/schema.ts index 66662da88..e9c746563 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 { TrainingTypeMap } from '@fastgpt/global/core/dataset/constants'; +import { DatasetDataIndexTypeMap, TrainingTypeMap } from '@fastgpt/global/core/dataset/constants'; import { DatasetColCollectionName } from '../collection/schema'; import { DatasetCollectionName } from '../schema'; import { @@ -86,6 +86,11 @@ 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 8b85872f3..aff31afd5 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 a66c684b7..cfce33e3d 100644 --- a/packages/service/support/openapi/auth.ts +++ b/packages/service/support/openapi/auth.ts @@ -19,15 +19,14 @@ export async function authOpenApiKey({ apikey }: { apikey: string }) { // auth limit // @ts-ignore if (global.feConfigs?.isPlus) { - await POST('/support/openapi/authLimit', { - openApi: openApi.toObject() - } as AuthOpenApiLimitProps); + await POST('/support/openapi/authLimit', { openApi } 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 dff9af5a4..6eca9c95f 100644 --- a/packages/service/support/openapi/schema.ts +++ b/packages/service/support/openapi/schema.ts @@ -1,6 +1,8 @@ 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 @@ -8,6 +10,10 @@ import { const OpenApiSchema = new Schema( { + userId: { + type: Schema.Types.ObjectId, + ref: 'user' + }, teamId: { type: Schema.Types.ObjectId, ref: TeamCollectionName, @@ -38,17 +44,22 @@ const OpenApiSchema = new Schema( type: String, default: 'Api Key' }, - usagePoints: { + usage: { + // total usage. value from bill total type: Number, - default: 0 + default: 0, + get: (val: number) => formatStorePrice2Read(val) }, limit: { expiredTime: { type: Date }, - maxUsagePoints: { + credit: { + // value from user settings type: Number, - default: -1 + default: -1, + set: (val: number) => val * PRICE_SCALE, + get: (val: number) => formatStorePrice2Read(val) } } }, diff --git a/packages/service/support/openapi/tools.ts b/packages/service/support/openapi/tools.ts index d9e0946e8..b6bedee1e 100644 --- a/packages/service/support/openapi/tools.ts +++ b/packages/service/support/openapi/tools.ts @@ -8,21 +8,15 @@ export function updateApiKeyUsedTime(id: string) { }); } -export function updateApiKeyUsage({ - apikey, - totalPoints -}: { - apikey: string; - totalPoints: number; -}) { +export function updateApiKeyUsage({ apikey, usage }: { apikey: string; usage: number }) { MongoOpenApi.findOneAndUpdate( { apiKey: apikey }, { $inc: { - usagePoints: totalPoints + usage } } ).catch((err) => { - console.log('update apiKey totalPoints error', err); + console.log('update apiKey usage error', err); }); } diff --git a/packages/service/support/outLink/schema.ts b/packages/service/support/outLink/schema.ts index 3d6d18482..acb7526ab 100644 --- a/packages/service/support/outLink/schema.ts +++ b/packages/service/support/outLink/schema.ts @@ -35,7 +35,8 @@ const OutLinkSchema = new Schema({ type: String, required: true }, - usagePoints: { + total: { + // total amount type: Number, default: 0 }, @@ -47,10 +48,6 @@ const OutLinkSchema = new Schema({ default: false }, limit: { - maxUsagePoints: { - type: Number, - default: -1 - }, expiredTime: { type: Date }, @@ -58,18 +55,16 @@ 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 878234c63..3b9afa32d 100644 --- a/packages/service/support/outLink/tools.ts +++ b/packages/service/support/outLink/tools.ts @@ -1,19 +1,18 @@ import axios from 'axios'; import { MongoOutLink } from './schema'; import { FastGPTProUrl } from '../../common/system/constants'; -import { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type'; -export const addOutLinkUsage = async ({ +export const updateOutLinkUsage = async ({ shareId, - totalPoints + total }: { shareId: string; - totalPoints: number; + total: number; }) => { MongoOutLink.findOneAndUpdate( { shareId }, { - $inc: { usagePoints: totalPoints }, + $inc: { total }, lastTime: new Date() } ).catch((err) => { @@ -24,13 +23,11 @@ export const addOutLinkUsage = async ({ export const pushResult2Remote = async ({ outLinkUid, shareId, - appName, responseData }: { outLinkUid?: string; // raw id, not parse shareId?: string; - appName: string; - responseData?: ChatHistoryItemResType[]; + responseData?: any[]; }) => { if (!shareId || !outLinkUid || !FastGPTProUrl) return; try { @@ -45,7 +42,6 @@ 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 e55ed064d..c793714eb 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 { teamId, tmbId } = await parseHeaderCert(props); + const { userId, teamId, tmbId } = await parseHeaderCert(props); const { role } = await getTmbInfoByTmbId({ tmbId }); const { collection, isOwner, canWrite } = await (async () => { @@ -143,6 +143,7 @@ export async function authDatasetCollection({ })(); return { + userId, teamId, tmbId, collection, @@ -162,7 +163,7 @@ export async function authDatasetFile({ file: DatasetFileSchema; } > { - const { teamId, tmbId } = await parseHeaderCert(props); + const { userId, teamId, tmbId } = await parseHeaderCert(props); const [file, collection] = await Promise.all([ getFileById({ bucketName: BucketNameEnum.dataset, fileId }), @@ -189,6 +190,7 @@ export async function authDatasetFile({ }); return { + userId, teamId, tmbId, file, @@ -198,4 +200,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 1eb506f3f..e8da32072 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 { teamId, tmbId } = await parseHeaderCert(props); + const { userId, teamId, tmbId } = await parseHeaderCert(props); const team = await getTmbInfoByTmbId({ tmbId }); if (team.role === TeamMemberRoleEnum.visitor) { @@ -20,6 +20,7 @@ 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 fb73b7107..56afce1ac 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 { teamId, tmbId, appId: apiKeyAppId = '' } = await authOpenApiKey({ apikey }); + const { userId, teamId, tmbId, appId: apiKeyAppId = '' } = await authOpenApiKey({ apikey }); return { - uid: '', + uid: userId, 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 new file mode 100644 index 000000000..b6e8a18c9 --- /dev/null +++ b/packages/service/support/permission/limit/dataset.ts @@ -0,0 +1,23 @@ +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/user/controller.ts b/packages/service/support/user/controller.ts index 5765df3a7..361a9029b 100644 --- a/packages/service/support/user/controller.ts +++ b/packages/service/support/user/controller.ts @@ -2,6 +2,7 @@ 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) { @@ -46,3 +47,22 @@ 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 496726860..15e75c491 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/constants'; +import { PRICE_SCALE } from '@fastgpt/global/support/wallet/bill/constants'; import type { UserModelSchema } from '@fastgpt/global/support/user/type'; import { UserStatusEnum, userStatusMap } from '@fastgpt/global/support/user/constant'; @@ -63,8 +63,6 @@ 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 17e27ab5f..042cd2388 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(String(tmbId)), + _id: new Types.ObjectId(tmbId), status: notLeaveStatus }); } diff --git a/packages/service/support/user/team/teamSchema.ts b/packages/service/support/user/team/teamSchema.ts index 5a358b409..3121b92ae 100644 --- a/packages/service/support/user/team/teamSchema.ts +++ b/packages/service/support/user/team/teamSchema.ts @@ -27,10 +27,7 @@ const TeamSchema = new Schema({ }, maxSize: { type: Number, - default: 1 - }, - tagsUrl: { - type: String + default: 3 }, limit: { lastExportDatasetTime: { diff --git a/packages/service/support/user/team/teamTagsSchema.ts b/packages/service/support/user/team/teamTagsSchema.ts deleted file mode 100644 index f1c1044e6..000000000 --- a/packages/service/support/user/team/teamTagsSchema.ts +++ /dev/null @@ -1,35 +0,0 @@ -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/usage/controller.ts b/packages/service/support/wallet/bill/controller.ts similarity index 62% rename from packages/service/support/wallet/usage/controller.ts rename to packages/service/support/wallet/bill/controller.ts index 43e9534ae..3528159a0 100644 --- a/packages/service/support/wallet/usage/controller.ts +++ b/packages/service/support/wallet/bill/controller.ts @@ -1,8 +1,8 @@ -import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants'; -import { MongoUsage } from './schema'; +import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants'; +import { MongoBill } from './schema'; import { ClientSession } from '../../../common/mongo'; -export const createTrainingUsage = async ({ +export const createTrainingBill = async ({ teamId, tmbId, appName, @@ -14,33 +14,33 @@ export const createTrainingUsage = async ({ teamId: string; tmbId: string; appName: string; - billSource: `${UsageSourceEnum}`; + billSource: `${BillSourceEnum}`; vectorModel: string; agentModel: string; session?: ClientSession; }) => { - const [{ _id }] = await MongoUsage.create( + const [{ _id }] = await MongoBill.create( [ { teamId, tmbId, appName, source: billSource, - totalPoints: 0, list: [ { - moduleName: 'support.wallet.moduleName.index', + moduleName: 'wallet.moduleName.index', model: vectorModel, charsLength: 0, amount: 0 }, { - moduleName: 'support.wallet.moduleName.qa', + moduleName: 'wallet.moduleName.qa', model: agentModel, charsLength: 0, amount: 0 } - ] + ], + total: 0 } ], { session } diff --git a/packages/service/support/wallet/usage/schema.ts b/packages/service/support/wallet/bill/schema.ts similarity index 50% rename from packages/service/support/wallet/usage/schema.ts rename to packages/service/support/wallet/bill/schema.ts index e6c39355d..c4d7bef38 100644 --- a/packages/service/support/wallet/usage/schema.ts +++ b/packages/service/support/wallet/bill/schema.ts @@ -1,15 +1,13 @@ import { connectionMongo, type Model } from '../../../common/mongo'; const { Schema, model, models } = connectionMongo; -import { UsageSchemaType } from '@fastgpt/global/support/wallet/usage/type'; -import { UsageSourceMap } from '@fastgpt/global/support/wallet/usage/constants'; +import { BillSchema as BillType } from '@fastgpt/global/support/wallet/bill/type'; +import { BillSourceMap } from '@fastgpt/global/support/wallet/bill/constants'; import { TeamCollectionName, TeamMemberCollectionName } from '@fastgpt/global/support/user/team/constant'; -export const UsageCollectionName = 'usages'; - -const UsageSchema = new Schema({ +const BillSchema = new Schema({ teamId: { type: Schema.Types.ObjectId, ref: TeamCollectionName, @@ -20,11 +18,6 @@ const UsageSchema = new Schema({ ref: TeamMemberCollectionName, required: true }, - source: { - type: String, - enum: Object.keys(UsageSourceMap), - required: true - }, appName: { type: String, default: '' @@ -38,16 +31,16 @@ const UsageSchema = new Schema({ type: Date, default: () => new Date() }, - totalPoints: { - // total points + total: { + // 1 * PRICE_SCALE type: Number, required: true }, - // total: { - // // total points - // type: Number, - // required: true - // }, + source: { + type: String, + enum: Object.keys(BillSourceMap), + required: true + }, list: { type: Array, default: [] @@ -55,15 +48,11 @@ const UsageSchema = new Schema({ }); try { - 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 }); + BillSchema.index({ teamId: 1, tmbId: 1, source: 1, time: -1 }, { background: true }); + BillSchema.index({ time: 1 }, { expireAfterSeconds: 180 * 24 * 60 * 60 }); } catch (error) { console.log(error); } -export const MongoUsage: Model = - models[UsageCollectionName] || model(UsageCollectionName, UsageSchema); -MongoUsage.syncIndexes(); +export const MongoBill: Model = models['bill'] || model('bill', BillSchema); +MongoBill.syncIndexes(); diff --git a/packages/service/support/wallet/sub/schema.ts b/packages/service/support/wallet/sub/schema.ts index 11316878e..2d2258070 100644 --- a/packages/service/support/wallet/sub/schema.ts +++ b/packages/service/support/wallet/sub/schema.ts @@ -1,8 +1,3 @@ -/* - 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'; @@ -28,10 +23,25 @@ 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() @@ -45,16 +55,12 @@ const SubSchema = new Schema({ type: Number, required: true }, + pointPrice: { + // stand level point total price + type: Number + }, - // standard sub - currentMode: { - type: String, - enum: Object.keys(subModeMap) - }, - nextMode: { - type: String, - enum: Object.keys(subModeMap) - }, + // sub content currentSubLevel: { type: String, enum: Object.keys(standardSubLevelMap) @@ -63,34 +69,79 @@ const SubSchema = new Schema({ type: String, enum: Object.keys(standardSubLevelMap) }, - - // stand sub and extra points sub. Plan total points totalPoints: { type: Number }, - pointPrice: { - // stand level point total price + + currentExtraDatasetSize: { type: Number }, - surplusPoints: { - // plan surplus points + nextExtraDatasetSize: { type: Number }, - // extra dataset size - currentExtraDatasetSize: { + currentExtraPoints: { type: Number - } + }, + nextExtraPoints: { + 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 + type: Number + }, + + // abandon + renew: Boolean, //决定是否续费 + datasetStoreAmount: Number }); try { - // get team plan - SubSchema.index({ teamId: 1, type: 1, expiredTime: -1 }); - - // timer task. check expired plan; update standard plan; - SubSchema.index({ type: 1, expiredTime: -1 }); - // timer task. clear dead team - SubSchema.index({ type: 1, currentSubLevel: 1, nextSubLevel: 1 }); + SubSchema.index({ teamId: 1 }); + SubSchema.index({ status: 1 }); + SubSchema.index({ type: 1 }); + SubSchema.index({ 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 760ad6716..feb29c8ff 100644 --- a/packages/service/support/wallet/sub/utils.ts +++ b/packages/service/support/wallet/sub/utils.ts @@ -1,70 +1,87 @@ import { SubTypeEnum } from '@fastgpt/global/support/wallet/sub/constants'; import { MongoTeamSub } from './schema'; -import { - FeTeamPlanStatusType, - StandSubPlanLevelMapType -} from '@fastgpt/global/support/wallet/sub/type.d'; +import { addHours } from 'date-fns'; +import { FeTeamSubType, StandSubPlanLevelMapType } from '@fastgpt/global/support/wallet/sub/type.d'; import { getVectorCountByTeamId } from '../../../common/vectorStore/controller'; -export const getTeamSubPlans = async ({ - teamId, - standardPlans -}: { - teamId: string; - standardPlans?: StandSubPlanLevelMapType; -}): Promise => { - const [plans, usedDatasetSize] = await Promise.all([ - MongoTeamSub.find({ teamId }).lean(), - getVectorCountByTeamId(teamId) - ]); - - const standard = plans.find((plan) => plan.type === SubTypeEnum.standard); - const extraDatasetSize = plans.filter((plan) => plan.type === SubTypeEnum.extraDatasetSize); - const extraPoints = plans.filter((plan) => plan.type === SubTypeEnum.extraPoints); - - const totalPoints = - (standard?.totalPoints || 0) + - extraPoints.reduce((acc, cur) => acc + (cur.totalPoints || 0), 0); - 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.reduce((acc, cur) => acc + (cur.currentExtraDatasetSize || 0), 0); - - return { - [SubTypeEnum.standard]: standard, - standardConstants: - standard?.currentSubLevel && standardPlans - ? standardPlans[standard.currentSubLevel] - : undefined, - - totalPoints, - usedPoints: totalPoints - surplusPoints, - - datasetMaxSize: totalDatasetSize, - usedDatasetSize - }; -}; -export const getTeamStandPlan = async ({ +/* get team dataset max size */ +export const getTeamDatasetMaxSize = async ({ teamId, standardPlans }: { teamId: string; standardPlans?: StandSubPlanLevelMapType; }) => { - const standard = await MongoTeamSub.findOne({ teamId, type: SubTypeEnum.standard }).lean(); + if (!standardPlans) { + return { + maxSize: Infinity, + sub: null + }; + } + + const plans = await MongoTeamSub.find({ + teamId, + expiredTime: { $gte: addHours(new Date(), -3) } + }).lean(); + + const standard = plans.find((plan) => plan.type === SubTypeEnum.standard); + const extraDatasetSize = plans.find((plan) => plan.type === SubTypeEnum.extraDatasetSize); + + const standardMaxDatasetSize = + standard?.currentSubLevel && standardPlans + ? standardPlans[standard.currentSubLevel]?.maxDatasetSize || Infinity + : Infinity; + const totalDatasetSize = + standardMaxDatasetSize + (extraDatasetSize?.currentExtraDatasetSize || 0); + + return { + maxSize: totalDatasetSize, + sub: extraDatasetSize + }; +}; + +export const getTeamSubPlanStatus = async ({ + teamId, + standardPlans +}: { + teamId: string; + standardPlans?: StandSubPlanLevelMapType; +}): Promise => { + 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 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); return { [SubTypeEnum.standard]: standard, - standardConstants: - standard?.currentSubLevel && standardPlans - ? standardPlans[standard.currentSubLevel] - : undefined + [SubTypeEnum.extraDatasetSize]: extraDatasetSize, + [SubTypeEnum.extraPoints]: extraPoints, + + standardMaxDatasetSize, + datasetMaxSize: totalDatasetSize, + usedDatasetSize, + + standardMaxPoints, + totalPoints, + usedPoints: totalPoints - surplusPoints }; }; diff --git a/packages/web/components/common/Icon/constants.ts b/packages/web/components/common/Icon/constants.ts index 09ae20777..4642d8e38 100644 --- a/packages/web/components/common/Icon/constants.ts +++ b/packages/web/components/common/Icon/constants.ts @@ -5,13 +5,6 @@ export const iconPaths = { closeSolid: () => import('./icons/closeSolid.svg'), collectionLight: () => import('./icons/collectionLight.svg'), collectionSolid: () => import('./icons/collectionSolid.svg'), - 'acount/cube': () => import('./icons/acount/cube.svg'), - 'acount/personalized': () => import('./icons/acount/personalized.svg'), - 'acount/user': () => import('./icons/acount/user.svg'), - 'acount/plans': () => import('./icons/acount/plans.svg'), - 'acount/check': () => import('./icons/acount/check.svg'), - 'acount/arrowRight': () => import('./icons/acount/arrowRight.svg'), - 'acount/plansBlue': () => import('./icons/acount/plansBlue.svg'), 'common/addCircleLight': () => import('./icons/common/addCircleLight.svg'), 'common/backFill': () => import('./icons/common/backFill.svg'), 'common/backLight': () => import('./icons/common/backLight.svg'), @@ -135,7 +128,6 @@ 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'), @@ -151,20 +143,19 @@ export const iconPaths = { stop: () => import('./icons/stop.svg'), 'support/account/loginoutLight': () => import('./icons/support/account/loginoutLight.svg'), 'support/account/promotionLight': () => import('./icons/support/account/promotionLight.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/bill/billRecordLight': () => import('./icons/support/bill/billRecordLight.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/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/acount/arrowRight.svg b/packages/web/components/common/Icon/icons/acount/arrowRight.svg deleted file mode 100644 index 461a2f530..000000000 --- a/packages/web/components/common/Icon/icons/acount/arrowRight.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/packages/web/components/common/Icon/icons/acount/check.svg b/packages/web/components/common/Icon/icons/acount/check.svg deleted file mode 100644 index b063a333d..000000000 --- a/packages/web/components/common/Icon/icons/acount/check.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/packages/web/components/common/Icon/icons/acount/cube.svg b/packages/web/components/common/Icon/icons/acount/cube.svg deleted file mode 100644 index cab560dc5..000000000 --- a/packages/web/components/common/Icon/icons/acount/cube.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/packages/web/components/common/Icon/icons/acount/personalized.svg b/packages/web/components/common/Icon/icons/acount/personalized.svg deleted file mode 100644 index 8b9565b08..000000000 --- a/packages/web/components/common/Icon/icons/acount/personalized.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/packages/web/components/common/Icon/icons/acount/plans.svg b/packages/web/components/common/Icon/icons/acount/plans.svg deleted file mode 100644 index e8aa85672..000000000 --- a/packages/web/components/common/Icon/icons/acount/plans.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/packages/web/components/common/Icon/icons/acount/plansBlue.svg b/packages/web/components/common/Icon/icons/acount/plansBlue.svg deleted file mode 100644 index 7d620cef6..000000000 --- a/packages/web/components/common/Icon/icons/acount/plansBlue.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/packages/web/components/common/Icon/icons/acount/user.svg b/packages/web/components/common/Icon/icons/acount/user.svg deleted file mode 100644 index 7c8faf3fd..000000000 --- a/packages/web/components/common/Icon/icons/acount/user.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/packages/web/components/common/Icon/icons/modal/confirmPay.svg b/packages/web/components/common/Icon/icons/modal/confirmPay.svg deleted file mode 100644 index bc4f40f6e..000000000 --- a/packages/web/components/common/Icon/icons/modal/confirmPay.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - \ No newline at end of file diff --git a/packages/web/components/common/Icon/icons/support/usage/usageRecordLight.svg b/packages/web/components/common/Icon/icons/support/bill/billRecordLight.svg similarity index 100% rename from packages/web/components/common/Icon/icons/support/usage/usageRecordLight.svg rename to packages/web/components/common/Icon/icons/support/bill/billRecordLight.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 deleted file mode 100644 index c701b2576..000000000 --- a/packages/web/components/common/Icon/icons/support/bill/shoppingCart.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - \ No newline at end of file diff --git a/packages/web/components/common/Icon/icons/support/bill/extraDatasetsize.svg b/packages/web/components/common/Icon/icons/support/pay/extraDatasetsize.svg similarity index 100% rename from packages/web/components/common/Icon/icons/support/bill/extraDatasetsize.svg rename to packages/web/components/common/Icon/icons/support/pay/extraDatasetsize.svg diff --git a/packages/web/components/common/Icon/icons/support/bill/extraPoints.svg b/packages/web/components/common/Icon/icons/support/pay/extraPoints.svg similarity index 100% rename from packages/web/components/common/Icon/icons/support/bill/extraPoints.svg rename to packages/web/components/common/Icon/icons/support/pay/extraPoints.svg diff --git a/packages/web/components/common/Icon/icons/support/bill/payRecordLight.svg b/packages/web/components/common/Icon/icons/support/pay/payRecordLight.svg similarity index 100% rename from packages/web/components/common/Icon/icons/support/bill/payRecordLight.svg rename to packages/web/components/common/Icon/icons/support/pay/payRecordLight.svg diff --git a/packages/web/components/common/Icon/icons/support/bill/priceLight.svg b/packages/web/components/common/Icon/icons/support/pay/priceLight.svg similarity index 100% rename from packages/web/components/common/Icon/icons/support/bill/priceLight.svg rename to packages/web/components/common/Icon/icons/support/pay/priceLight.svg diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 23807b50e..f094cca27 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '6.1' +lockfileVersion: '6.0' 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 @@ -3789,6 +3789,7 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] requiresBuild: true optional: true @@ -3797,6 +3798,7 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] requiresBuild: true optional: true @@ -3805,6 +3807,7 @@ packages: engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] requiresBuild: true optional: true @@ -3813,6 +3816,7 @@ packages: engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] requiresBuild: true optional: true @@ -3899,6 +3903,7 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] requiresBuild: true dev: false optional: true @@ -3908,6 +3913,7 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] requiresBuild: true dev: false optional: true @@ -3917,6 +3923,7 @@ packages: engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] requiresBuild: true dev: false optional: true @@ -3926,6 +3933,7 @@ packages: engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] requiresBuild: true dev: false optional: true @@ -7919,9 +7927,6 @@ packages: /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - requiresBuild: true - dev: false - optional: true /is-fullwidth-code-point@4.0.0: resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} @@ -10680,13 +10685,10 @@ 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 strip-ansi: 6.0.1 - dev: false - optional: true /string-width@5.1.2: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} diff --git a/projects/app/data/config.json b/projects/app/data/config.json index 21cc58981..6eeeb82ee 100644 --- a/projects/app/data/config.json +++ b/projects/app/data/config.json @@ -13,7 +13,8 @@ "maxResponse": 4000, "quoteMaxToken": 13000, "maxTemperature": 1.2, - "charsPointsPrice": 0, + "inputPrice": 0, + "outputPrice": 0, "censor": false, "vision": false, "datasetProcess": false, @@ -31,7 +32,8 @@ "maxResponse": 16000, "quoteMaxToken": 13000, "maxTemperature": 1.2, - "charsPointsPrice": 0, + "inputPrice": 0, + "outputPrice": 0, "censor": false, "vision": false, "datasetProcess": true, @@ -49,7 +51,8 @@ "maxResponse": 4000, "quoteMaxToken": 100000, "maxTemperature": 1.2, - "charsPointsPrice": 0, + "inputPrice": 0, + "outputPrice": 0, "censor": false, "vision": false, "datasetProcess": false, @@ -67,9 +70,10 @@ "maxResponse": 4000, "quoteMaxToken": 100000, "maxTemperature": 1.2, - "charsPointsPrice": 0, + "inputPrice": 0, + "outputPrice": 0, "censor": false, - "vision": true, + "vision": false, "datasetProcess": false, "toolChoice": true, "functionCall": false, @@ -83,7 +87,8 @@ { "model": "text-embedding-ada-002", "name": "Embedding-2", - "charsPointsPrice": 0, + "inputPrice": 0, + "outputPrice": 0, "defaultToken": 700, "maxToken": 3000, "weight": 100 @@ -94,44 +99,22 @@ { "model": "tts-1", "name": "OpenAI TTS1", - "charsPointsPrice": 0, + "inputPrice": 0, + "outputPrice": 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", - "charsPointsPrice": 0 + "inputPrice": 0, + "outputPrice": 0 } } diff --git a/projects/app/next.config.js b/projects/app/next.config.js index ef9310e53..ad9a7d860 100644 --- a/projects/app/next.config.js +++ b/projects/app/next.config.js @@ -45,7 +45,13 @@ const nextConfig = { }, transpilePackages: ['@fastgpt/*'], experimental: { - serverComponentsExternalPackages: ['mongoose', 'pg'], + serverComponentsExternalPackages: [ + 'mongoose', + 'pg', + 'react', + '@chakra-ui/react', + '@lexical/react' + ], outputFileTracingRoot: path.join(__dirname, '../../') } }; diff --git a/projects/app/package.json b/projects/app/package.json index c50beb88e..5358239a6 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", @@ -74,4 +74,4 @@ "eslint-config-next": "13.1.6", "typescript": "4.9.5" } -} \ No newline at end of file +} diff --git a/projects/app/public/docs/versionIntro.md b/projects/app/public/docs/versionIntro.md index 5f1cd9eb7..f740a75e2 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 d2e12ef14..22e3d2444 100644 --- a/projects/app/public/locales/en/common.json +++ b/projects/app/public/locales/en/common.json @@ -15,7 +15,6 @@ "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", @@ -84,7 +83,6 @@ "Delete Success": "Delete Successful", "Delete Tip": "Delete Confirm", "Delete Warning": "Warning", - "Detail": "Detail", "Done": "Done", "Edit": "Edit", "Exit": "Exit", @@ -100,6 +98,8 @@ "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,7 +110,6 @@ "Number of words": "{{amount}} words", "OK": "OK", "Opened": "Opened", - "Other": "Other", "Output": "Output", "Params": "Params", "Password inconsistency": "Password inconsistency", @@ -133,20 +132,17 @@ "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", @@ -504,7 +500,6 @@ "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}}", @@ -614,8 +609,7 @@ "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: 1k chars={{charsPointsPrice}} points", - "Estimated points": "About {{points}} points", + "Estimated Price Tips": "QA charges\nInput: {{inputPrice}}/1k tokens\nOutput: {{outputPrice}}/1k tokens", "Fetch Error": "Get link failed", "Fetch Url": "Url", "Fetch url placeholder": "Up to 10 links, one per line.", @@ -685,7 +679,7 @@ "Source name": "Source", "Top K": "Top K", "Using cfr": "Open query extension", - "Using query extension": "Open query extension", + "Using query extension": "", "mode": { "embedding": "Vector recall", "embedding desc": "Use vectors for text correlation queries", @@ -849,8 +843,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" @@ -862,7 +856,6 @@ "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": { @@ -871,7 +864,7 @@ "Quote": "Quote", "Search result empty": "Search result empty", "Search result not empty": "Search result not empty", - "query extension result": "Response text", + "cfr result": "Response text", "result false": "False", "result true": "True", "running done": "done", @@ -1110,7 +1103,6 @@ "navbar": { "Account": "Account", "Apps": "App", - "Apps Share": "", "Chat": "Chat", "Datasets": "DataSet", "Module": "Module", @@ -1175,9 +1167,6 @@ "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." @@ -1186,16 +1175,9 @@ "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" }, @@ -1210,10 +1192,7 @@ } }, "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", @@ -1221,60 +1200,32 @@ "bill": { "AI Model": "AI Model", "AI Type": "AI Type", - "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" - } + "Price": "Price(¥)" }, - "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 ai points": "AI Points", - "Update extra dataset size": "Dataset size", - "Upgrade plan": "Upgrade plan", + "Update extra dataset size": "Update size", "function": { "History store": "", "Max app": "", @@ -1285,7 +1236,6 @@ }, "mode": { "Month": "Monthly", - "Period": "Period", "Year": "Yearly", "Year sale": "2 months free" }, @@ -1298,38 +1248,9 @@ "team": "" }, "type": { - "balance": "Add Balance", "extraDatasetSize": "Extra dataset size", - "extraPoints": "AI Points", - "standard": "Sub plan" + "standard": "" } - }, - "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" } } }, @@ -1371,7 +1292,6 @@ "Set OpenAI Account Failed": "Set OpenAI account failed", "Sign Out": "Sign Out", "Source": "Source", - "Standard Detail": "", "Team": "Team", "Time": "Time", "Timezone": "Timezone", @@ -1419,11 +1339,7 @@ "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?", @@ -1448,5 +1364,37 @@ "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 1accdb4db..e12c9d08c 100644 --- a/projects/app/public/locales/zh/common.json +++ b/projects/app/public/locales/zh/common.json @@ -15,7 +15,6 @@ "AI Settings": "AI 配置", "Advance App TestTip": "当前应用可能为高级编排模式\n如需切换为【简易模式】请点击左侧保存按键", "App Detail": "应用详情", - "Apps Share": "应用分享", "Basic Settings": "基本信息", "Chat Debug": "调试预览", "Chat Logs Tips": "日志会记录该应用的在线、分享和 API(需填写 chatId) 对话记录", @@ -84,7 +83,6 @@ "Delete Success": "删除成功", "Delete Tip": "删除提示", "Delete Warning": "删除警告", - "Detail": "详情", "Done": "完成", "Edit": "编辑", "Exit": "退出", @@ -100,6 +98,8 @@ "Last use time": "最后使用时间", "Load Failed": "加载失败", "Loading": "加载中...", + "Max credit": "最大金额", + "Max credit tips": "该链接最大可消耗多少金额,超出后链接将被禁止使用。-1 代表无限制。", "More settings": "更多设置", "Name": "名称", "Name Can": "名称不能为空", @@ -110,7 +110,6 @@ "Number of words": "{{amount}}字", "OK": "好的", "Opened": "已开启", - "Other": "其他", "Output": "输出", "Params": "参数", "Password inconsistency": "两次密码不一致", @@ -133,20 +132,17 @@ "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": "更新成功", @@ -406,7 +402,7 @@ "Stop Speak": "停止录音", "Type a message": "输入问题", "Unpin": "取消置顶", - "You need to a chat app": "鉴权失败,暂无权限访问应用", + "You need to a chat app": "你需要创建一个应用", "error": { "Chat error": "对话出现异常", "Messages empty": "接口内容为空,可能文本超长了~", @@ -438,7 +434,7 @@ }, "response": { "Complete Response": "完整响应", - "Extension model": "问题优化模型", + "Extension model": "问题补全模型", "Plugin Resonse Detail": "插件详情", "Read complete response": "查看详情", "Read complete response tips": "点击查看详细流程", @@ -504,7 +500,6 @@ "Manual collection": "手动数据集", "My Dataset": "我的知识库", "Name": "知识库名称", - "Query extension intro": "开启问题优化功能,可以提高提高连续对话时,知识库搜索的精度。开启该功能后,在进行知识库搜索时,会根据对话记录,利用 AI 补全问题缺失的信息。", "Quote Length": "引用内容长度", "Read Dataset": "查看知识库详情", "Search score tip": "{{scoreText}}下面是详细排名和得分情况:\n----\n{{detailScore}}", @@ -556,8 +551,7 @@ "success": "开始同步" } }, - "training": { - } + "training": {} }, "data": { "Auxiliary Data": "辅助数据", @@ -566,7 +560,7 @@ "Data Content": "相关数据内容", "Data Content Placeholder": "该输入框是必填项,该内容通常是对于知识点的描述,也可以是用户的问题,最多 {{maxToken}} 字。", "Data Content Tip": "该输入框是必填项\n该内容通常是对于知识点的描述,也可以是用户的问题。", - "Default Index Tip": "无法编辑,默认索引会使用【相关数据内容】与【辅助数据】的文本直接生成索引。", + "Default Index Tip": "无法编辑,默认索引会使用【相关数据内容】与【辅助数据】的文本直接生成索引,如不需要默认索引,可删除。 每条数据必须保证有一个以上索引,所有索引被删除后,会自动生成默认索引。", "Edit": "编辑数据", "Empty Tip": "这个集合还没有数据~", "Main Content": "主要内容", @@ -614,10 +608,9 @@ "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输入: 1k字符 = {{charsPointsPrice}}积分", - "Estimated points": "预估消耗 {{points}} 积分", + "Estimated Price Tips": "QA计费为\n输入: {{inputPrice}}/1k tokens\n输出: {{outputPrice}}/1k tokens", "Fetch Error": "获取链接失败", "Fetch Url": "网络链接", "Fetch url placeholder": "最多10个链接,每行一个。", @@ -637,7 +630,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": "重新生成预览", @@ -687,7 +680,7 @@ "Source name": "引用来源名", "Top K": "单次搜索上限", "Using cfr": "", - "Using query extension": "使用问题优化", + "Using query extension": "使用问题补全", "mode": { "embedding": "语义检索", "embedding desc": "使用向量进行文本相关性查询", @@ -851,8 +844,8 @@ "dynamicTargetInput": "动态外部数据", "input": "输入框", "selectApp": "应用选择", + "selectChatModel": "对话模型选择", "selectDataset": "知识库选择", - "selectLLMModel": "对话模型选择", "switch": "开关", "target": "外部数据", "textarea": "段落输入" @@ -864,7 +857,6 @@ "Ai response content": "将在 stream 回复完毕后触发", "New context": "将本次回复内容拼接上历史记录,作为新的上下文返回", "Quote": "始终返回数组,如果希望搜索结果为空时执行额外操作,需要用到上面的两个输入以及目标模块的触发器", - "query extension result": "以字符串数组的形式输出,可将该结果直接连接到“知识库搜索”的“用户问题”中,建议不要连接到“AI对话”的“用户问题”中", "running done": "模块调用结束时触发" }, "label": { @@ -873,7 +865,7 @@ "Quote": "引用内容", "Search result empty": "搜索结果为空", "Search result not empty": "搜索结果不为空", - "query extension result": "优化结果", + "cfr result": "补全结果", "result false": "False", "result true": "True", "running done": "模块调用结束", @@ -900,8 +892,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": "可以选择一个其他应用进行调用", @@ -1112,7 +1104,6 @@ "navbar": { "Account": "账号", "Apps": "应用", - "Apps Share": "应用分享", "Chat": "聊天", "Datasets": "知识库", "Module": "模块", @@ -1177,9 +1168,6 @@ "Usage": "已用额度(¥)" }, "outlink": { - "Max usage points": "积分上限", - "Max usage points tip": "该链接最多允许使用多少积分,超出后将无法使用。-1 代表无限制。", - "Usage points": "积分消耗", "share": { "Response Quote": "返回引用", "Response Quote tips": "在分享链接中返回引用内容,但不会允许用户下载原文档" @@ -1188,16 +1176,9 @@ "subscription": { "Cancel subscription": "取消订阅" }, - "team": { - "limit": { - "No permission rerank": "无权使用结果重排,请升级您的套餐" - } - }, "user": { - "Avatar": "头像", "Need to login": "请先登录", "Price": "计费标准", - "User self info": "个人信息", "auth": { "Sending Code": "正在发送" }, @@ -1212,10 +1193,7 @@ } }, "wallet": { - "Amount": "金额", "Balance not enough tip": "余额不足,请先到账号页充值", - "Bills": "账单", - "Buy": "购买", "Buy more": "扩容", "Confirm pay": "支付确认", "Pay error": "支付失败", @@ -1223,115 +1201,57 @@ "bill": { "AI Model": "AI 模型", "AI Type": "AI 类型", - "Number": "订单号", - "Price": "价格(¥)", - "Status": "状态", - "Type": "订单类型", - "payWay": { - "Way": "支付方式", - "balance": "余额支付", - "wx": "微信支付" - }, - "status": { - "closed": "已关闭", - "notpay": "未支付", - "refund": "已退款", - "success": "支付成功" - } + "Price": "价格(¥)" }, - "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 extra ai points": "当前额外积分", - "Current plan": "当前套餐", + "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": "常见问题", - "Month amount": "月数", - "Next extra ai points": "下次额外积分", - "Next plan": "未来套餐", + "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 ai points": "额外AI积分", - "Update extra dataset size": "额外存储量", - "Upgrade plan": "升级套餐", + "Update extra dataset size": "调整知识库额外容量", "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": "", + "experience desc": "可使用 {{title}} 的完整功能", "free": "免费版", - "free desc": "每月均可免费使用基础功能,15天不活跃时,将会清除知识库", + "free desc": "每月均可免费使用 {{title}} 的基础功能", "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": "用户" } } }, @@ -1373,7 +1293,6 @@ "Set OpenAI Account Failed": "设置 OpenAI 账号异常", "Sign Out": "登出", "Source": "来源", - "Standard Detail": "套餐详情", "Team": "团队", "Time": "时间", "Timezone": "时区", @@ -1421,11 +1340,7 @@ "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": "确认加入该团队?", @@ -1450,5 +1365,37 @@ "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/WholeResponseModal.tsx b/projects/app/src/components/ChatBox/WholeResponseModal.tsx index 5fc6e4508..6a57f74ef 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?.totalPoints !== undefined && ( + {activeModule?.price !== undefined && ( )} + + diff --git a/projects/app/src/components/Layout/auth.tsx b/projects/app/src/components/Layout/auth.tsx index 4129aa956..09c28cad0 100644 --- a/projects/app/src/components/Layout/auth.tsx +++ b/projects/app/src/components/Layout/auth.tsx @@ -11,7 +11,6 @@ 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 2762c5678..5561c25f9 100644 --- a/projects/app/src/components/Layout/index.tsx +++ b/projects/app/src/components/Layout/index.tsx @@ -23,7 +23,6 @@ const pcUnShowLayoutRoute: Record = { '/login/provider': true, '/login/fastlogin': true, '/chat/share': true, - '/chat/team': true, '/app/edit': true, '/chat': true, '/tools/price': true, @@ -35,7 +34,6 @@ const phoneUnShowLayoutRoute: Record = { '/login/provider': true, '/login/fastlogin': true, '/chat/share': true, - '/chat/team': true, '/tools/price': true, '/price': true }; @@ -116,10 +114,9 @@ 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 e8eb3b67f..58f156a8b 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, { singleTilde: false }], RemarkBreaks]} + remarkPlugins={[RemarkMath, RemarkGfm, 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 328128f22..854c7b51c 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'; -import { useRouter } from 'next/router'; -import { AI_POINT_USAGE_CARD_ROUTE } from '@/web/support/wallet/sub/constants'; + +const PriceBox = dynamic(() => import('@/components/support/wallet/Price')); const SelectAiModel = ({ list, ...props }: SelectProps) => { const { t } = useTranslation(); const { feConfigs } = useSystemStore(); - const router = useRouter(); - const expandList = useMemo(() => { return feConfigs.show_pay ? list.concat({ @@ -20,6 +20,12 @@ 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') { - router.push(AI_POINT_USAGE_CARD_ROUTE); + onOpenPriceBox(); return; } props.onchange?.(e); }} /> + {isOpenPriceBox && } ); }; diff --git a/projects/app/src/components/TagEdit/index.tsx b/projects/app/src/components/TagEdit/index.tsx deleted file mode 100644 index 7f9a21809..000000000 --- a/projects/app/src/components/TagEdit/index.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import React, { useEffect, useMemo, useState } from 'react'; -import { - Menu, - MenuButton, - MenuList, - MenuItemOption, - MenuOptionGroup, - Flex, - TagLabel, - TagCloseButton, - HStack, - Tag, - Input -} from '@chakra-ui/react'; -import type { TeamTagsSchema } from '@fastgpt/global/support/user/team/type'; -const TagEdit = ({ - defaultValues, - teamsTags, - setSelectedTags -}: { - defaultValues: []; - teamsTags: Array; - 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 8b7c61e56..d00a26df4 100644 --- a/projects/app/src/components/core/module/DatasetParamsModal.tsx +++ b/projects/app/src/components/core/module/DatasetParamsModal.tsx @@ -27,8 +27,6 @@ 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}`; @@ -63,8 +61,6 @@ 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); @@ -75,7 +71,7 @@ const DatasetParamsModal = ({ limit, similarity, searchMode, - usingReRank: !!usingReRank && !!teamPlanStatus?.standardConstants?.permissionReRank, + usingReRank, datasetSearchUsingExtensionQuery, datasetSearchExtensionModel: datasetSearchExtensionModel ?? llmModelList[0]?.model, datasetSearchExtensionBg @@ -109,10 +105,6 @@ const DatasetParamsModal = ({ return true; }, [getValues, similarity]); - const showReRank = useMemo(() => { - return usingReRank !== undefined && reRankModelList.length > 0; - }, [reRankModelList.length, usingReRank]); - return ( - {showReRank && ( + {usingReRank !== undefined && reRankModelList.length > 0 && ( <> { - if (!teamPlanStatus?.standardConstants?.permissionReRank) { - return toast({ - status: 'warning', - title: t('support.team.limit.No permission rerank') - }); - } setValue('usingReRank', !getValues('usingReRank')); setRefresh((state) => !state); }} @@ -287,7 +273,7 @@ const DatasetParamsModal = ({ {currentTabType === SearchSettingTabEnum.queryExtension && ( - {t('core.dataset.Query extension intro')} + {t('core.module.template.Query extension intro')} {t('core.dataset.search.Using query extension')} diff --git a/projects/app/src/components/core/module/Flow/components/render/RenderInput/index.tsx b/projects/app/src/components/core/module/Flow/components/render/RenderInput/index.tsx index d2a3a09be..17af1b58b 100644 --- a/projects/app/src/components/core/module/Flow/components/render/RenderInput/index.tsx +++ b/projects/app/src/components/core/module/Flow/components/render/RenderInput/index.tsx @@ -46,7 +46,11 @@ const RenderList: { Component: dynamic(() => import('./templates/AiSetting')) }, { - types: [FlowNodeInputTypeEnum.selectLLMModel], + types: [ + FlowNodeInputTypeEnum.selectChatModel, + FlowNodeInputTypeEnum.selectCQModel, + FlowNodeInputTypeEnum.selectExtractModel + ], Component: dynamic(() => import('./templates/SelectAiModel')) }, { diff --git a/projects/app/src/components/core/module/Flow/index.tsx b/projects/app/src/components/core/module/Flow/index.tsx index 46f139818..d78b20cc1 100644 --- a/projects/app/src/components/core/module/Flow/index.tsx +++ b/projects/app/src/components/core/module/Flow/index.tsx @@ -33,7 +33,7 @@ const nodeTypes: Record<`${FlowNodeTypeEnum}`, any> = { [FlowNodeTypeEnum.pluginInput]: dynamic(() => import('./components/nodes/NodePluginInput')), [FlowNodeTypeEnum.pluginOutput]: dynamic(() => import('./components/nodes/NodePluginOutput')), [FlowNodeTypeEnum.pluginModule]: NodeSimple, - [FlowNodeTypeEnum.queryExtension]: NodeSimple + [FlowNodeTypeEnum.cfr]: NodeSimple }; const edgeTypes = { [EDGE_TYPE]: ButtonEdge diff --git a/projects/app/src/components/support/apikey/Table.tsx b/projects/app/src/components/support/apikey/Table.tsx index 19fdcb818..f3f5c7ef9 100644 --- a/projects/app/src/components/support/apikey/Table.tsx +++ b/projects/app/src/components/support/apikey/Table.tsx @@ -46,7 +46,7 @@ type EditProps = EditApiKeyProps & { _id?: string }; const defaultEditData: EditProps = { name: '', limit: { - maxUsagePoints: -1 + credit: -1 } }; @@ -153,16 +153,16 @@ const ApiKeyTable = ({ tips, appId }: { tips: string; appId?: string }) => { - {apiKeys.map(({ _id, name, usagePoints, limit, apiKey, createTime, lastUsedTime }) => ( + {apiKeys.map(({ _id, name, usage, limit, apiKey, createTime, lastUsedTime }) => ( {name} {apiKey} - {Math.round(usagePoints)} + {usage} {feConfigs?.isPlus && ( <> - {limit?.maxUsagePoints && limit?.maxUsagePoints > -1 - ? `${limit?.maxUsagePoints}` + {limit?.credit && limit?.credit > -1 + ? `${limit?.credit}` : t('common.Unlimited')} @@ -334,15 +334,15 @@ function EditKeyModal({ <> - {t('support.outlink.Max usage points')}: - + {t('common.Max credit')}: + import('./EditModal')); const InviteModal = dynamic(() => import('./InviteModal')); -const TeamTagsAsync = dynamic(() => import('../TeamTagsAsync')); const TeamManageModal = ({ onClose }: { onClose: () => void }) => { const theme = useTheme(); const { t } = useTranslation(); const { Loading } = useLoading(); const { toast } = useToast(); - const [teamsTags, setTeamTags] = useState(); const { ConfirmModal: ConfirmRemoveMemberModal, openConfirm: openRemoveMember } = useConfirm(); const { ConfirmModal: ConfirmLeaveTeamModal, openConfirm: openLeaveConfirm } = useConfirm({ @@ -69,11 +61,6 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => { const { userInfo, initUserInfo } = useUserStore(); const [editTeamData, setEditTeamData] = useState(); const { isOpen: isOpenInvite, onOpen: onOpenInvite, onClose: onCloseInvite } = useDisclosure(); - const { - isOpen: isOpenTeamTagsAsync, - onOpen: onOpenTeamTagsAsync, - onClose: onCloseTeamTagsAsync - } = useDisclosure(); const { data: myTeams = [], @@ -89,8 +76,6 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => { mutationFn: async (teamId: string) => { const token = await putSwitchTeam(teamId); token && setToken(token); - // get team tags - await getTeamsTags(teamId); return initUserInfo(); }, errorToast: t('user.team.Switch Team Failed') @@ -101,11 +86,6 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => { ['getMembers', userInfo?.team?.teamId], () => { if (!userInfo?.team?.teamId) return []; - // get team tags - getTeamsTags(userInfo.team.teamId).then((res: any) => { - setTeamTags(res); - }); - return getTeamMembers(userInfo.team.teamId); } ); @@ -128,9 +108,7 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => { mutationFn: async (teamId?: string) => { if (!teamId) return; // change to personal team - // get members await onSwitchTeam(defaultTeam.teamId); - return delLeaveTeam(teamId); }, onSuccess() { @@ -206,7 +184,6 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => { bg: 'myGray.100' } })} - onClick={() => onSwitchTeam(team.teamId)} > void }) => { : {})} > {team.teamName} - {/* {userInfo?.team?.teamId === team.teamId && ( - - {teamsTags.slice(0, 3).map((item: any, index) => { - return ( - - {item.label} - - ); - })} - - )} */} {userInfo?.team?.teamId === team.teamId ? ( @@ -263,7 +229,7 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => { borderBottomColor={'myGray.100'} mb={3} > - + {userInfo.team.teamName} {userInfo.team.role === TeamMemberRoleEnum.owner && ( @@ -313,27 +279,6 @@ const TeamManageModal = ({ onClose }: { onClose: () => void }) => { {t('user.team.Invite Member')} )} - {userInfo.team.role === TeamMemberRoleEnum.owner && ( - - )} {userInfo.team.role !== TeamMemberRoleEnum.owner && ( - - - - - - - - - ); -}; - -export default React.memo(TeamTagsAsync); diff --git a/projects/app/src/components/support/wallet/Price.tsx b/projects/app/src/components/support/wallet/Price.tsx new file mode 100644 index 000000000..f102ec2a2 --- /dev/null +++ b/projects/app/src/components/support/wallet/Price.tsx @@ -0,0 +1,97 @@ +import React from 'react'; +import { Box, CloseButton } from '@chakra-ui/react'; +import { useSystemStore } from '@/web/common/system/useSystemStore'; +import ReactDOM from 'react-dom'; + +import Markdown from '@/components/Markdown'; + +const Price = ({ onClose }: { onClose: () => void }) => { + const { llmModelList, vectorModelList, audioSpeechModelList, whisperModel } = useSystemStore(); + + const list = [ + { + title: 'AI语言模型', + describe: '', + md: ` +| 模型 | 输入价格(¥) | 输出价格(¥) | +| --- | --- | --- | +${llmModelList + ?.map((item) => `| ${item.name} | ${item.inputPrice}/1k tokens | ${item.outputPrice}/1k tokens |`) + .join('\n')}` + }, + { + title: '索引模型(文档训练 & 文档检索)', + describe: '', + md: ` +| 模型 | 价格(¥) | +| --- | --- | +${vectorModelList?.map((item) => `| ${item.name} | ${item.inputPrice}/1k 字符 |`).join('\n')} + ` + }, + { + title: '语音播放', + describe: '', + md: ` +| 模型 | 价格(¥) | +| --- | --- | +${audioSpeechModelList + ?.map((item) => `| ${item.name} | ${item.inputPrice}/1k 字符 | - |`) + .join('\n')}` + }, + ...(whisperModel + ? [ + { + title: '语音输入', + describe: '', + md: ` +| 模型 | 价格(¥) | +| --- | --- | +| ${whisperModel.name} | ${whisperModel.inputPrice}/分钟 | - |` + } + ] + : []) + ]; + + return ReactDOM.createPortal( + + + + + {list.map((item) => ( + + + {item.title} + + + + + + ))} + + + , + // @ts-ignore + document.querySelector('body') + ); +}; + +export default Price; diff --git a/projects/app/src/components/support/wallet/QRCodePayModal.tsx b/projects/app/src/components/support/wallet/QRCodePayModal.tsx deleted file mode 100644 index 521479154..000000000 --- a/projects/app/src/components/support/wallet/QRCodePayModal.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import MyModal from '@/components/MyModal'; -import React, { useEffect } from 'react'; -import { useTranslation } from 'next-i18next'; -import { Box, ModalBody, ModalFooter } from '@chakra-ui/react'; -import { useQuery } from '@tanstack/react-query'; -import { checkBalancePayResult } from '@/web/support/wallet/bill/api'; -import { useToast } from '@fastgpt/web/hooks/useToast'; -import { useRouter } from 'next/router'; -import { getErrText } from '@fastgpt/global/common/error/utils'; - -export type QRPayProps = { - readPrice: number; - codeUrl: string; - billId: string; -}; - -const QRCodePayModal = ({ - readPrice, - codeUrl, - billId, - onSuccess -}: QRPayProps & { onSuccess?: () => any }) => { - const router = useRouter(); - const { t } = useTranslation(); - const { toast } = useToast(); - const dom = document.getElementById('payQRCode'); - - useEffect(() => { - if (dom && window.QRCode) { - new window.QRCode(dom, { - text: codeUrl, - width: 128, - height: 128, - colorDark: '#000000', - colorLight: '#ffffff', - correctLevel: window.QRCode.CorrectLevel.H - }); - } - }, [dom]); - - useQuery( - [billId], - () => { - if (!billId) return null; - return checkBalancePayResult(billId); - }, - { - enabled: !!billId, - refetchInterval: 3000, - onSuccess: async (res) => { - if (!res) return; - - try { - await onSuccess?.(); - toast({ - title: res, - status: 'success' - }); - } catch (error) { - toast({ - title: getErrText(error), - status: 'error' - }); - } - - setTimeout(() => { - router.reload(); - }, 1000); - } - } - ); - - return ( - - - 请微信扫码支付: {readPrice}元,请勿关闭页面 - - - - - ); -}; - -export default React.memo(QRCodePayModal); diff --git a/projects/app/src/components/support/wallet/StandardPlanContentList.tsx b/projects/app/src/components/support/wallet/StandardPlanContentList.tsx deleted file mode 100644 index 6a5eda487..000000000 --- a/projects/app/src/components/support/wallet/StandardPlanContentList.tsx +++ /dev/null @@ -1,116 +0,0 @@ -import { useSystemStore } from '@/web/common/system/useSystemStore'; -import { StandardSubLevelEnum, SubModeEnum } from '@fastgpt/global/support/wallet/sub/constants'; -import React, { useMemo } from 'react'; -import { standardSubLevelMap } from '@fastgpt/global/support/wallet/sub/constants'; -import { Box, Flex, Grid } from '@chakra-ui/react'; -import MyIcon from '@fastgpt/web/components/common/Icon'; -import { useTranslation } from 'next-i18next'; - -const StandardPlanContentList = ({ - level, - mode -}: { - level: `${StandardSubLevelEnum}`; - mode: `${SubModeEnum}`; -}) => { - const { t } = useTranslation(); - const { subPlans } = useSystemStore(); - - const planContent = useMemo(() => { - const plan = subPlans?.standard?.[level]; - if (!plan) return; - return { - price: plan.price * (mode === SubModeEnum.month ? 1 : 10), - level: level as `${StandardSubLevelEnum}`, - ...standardSubLevelMap[level as `${StandardSubLevelEnum}`], - maxTeamMember: plan.maxTeamMember, - maxAppAmount: plan.maxAppAmount, - maxDatasetAmount: plan.maxDatasetAmount, - chatHistoryStoreDuration: plan.chatHistoryStoreDuration, - maxDatasetSize: plan.maxDatasetSize, - permissionCustomApiKey: plan.permissionCustomApiKey, - permissionCustomCopyright: plan.permissionCustomCopyright, - trainingWeight: plan.trainingWeight, - permissionReRank: plan.permissionReRank, - totalPoints: plan.totalPoints * (mode === SubModeEnum.month ? 1 : 12), - permissionWebsiteSync: plan.permissionWebsiteSync - }; - }, [subPlans?.standard, level, mode]); - - return planContent ? ( - - - - - {t('support.wallet.subscription.function.Max members', { - amount: planContent.maxTeamMember - })} - - - - - - {t('support.wallet.subscription.function.Max app', { - amount: planContent.maxAppAmount - })} - - - - - - {t('support.wallet.subscription.function.Max dataset', { - amount: planContent.maxDatasetAmount - })} - - - - - - {t('support.wallet.subscription.function.History store', { - amount: planContent.chatHistoryStoreDuration - })} - - - - - - {t('support.wallet.subscription.function.Max dataset size', { - amount: planContent.maxDatasetSize - })} - - - - - - - {t('support.wallet.subscription.function.Points', { - amount: planContent.totalPoints - })} - - - - - - - {t('support.wallet.subscription.Training weight', { - weight: planContent.trainingWeight - })} - - - {!!planContent.permissionReRank && ( - - - 检索结果重排 - - )} - {!!planContent.permissionWebsiteSync && ( - - - Web站点同步 - - )} - - ) : null; -}; - -export default StandardPlanContentList; diff --git a/projects/app/src/components/support/wallet/SubDatasetModal.tsx b/projects/app/src/components/support/wallet/SubDatasetModal.tsx new file mode 100644 index 000000000..dcc100b10 --- /dev/null +++ b/projects/app/src/components/support/wallet/SubDatasetModal.tsx @@ -0,0 +1,240 @@ +import React, { useState } from 'react'; +import MyModal from '@/components/MyModal'; +import { useTranslation } from 'next-i18next'; +import { + Box, + Flex, + ModalBody, + NumberInput, + NumberInputField, + NumberInputStepper, + NumberIncrementStepper, + NumberDecrementStepper, + ModalFooter, + Button +} from '@chakra-ui/react'; +import { useQuery } from '@tanstack/react-query'; +import { + getTeamDatasetValidSub, + posCheckTeamDatasetSizeSub, + postUpdateTeamDatasetSizeSub, + putTeamDatasetSubStatus +} from '@/web/support/wallet/sub/api'; +import Markdown from '@/components/Markdown'; +import MyTooltip from '@/components/MyTooltip'; +import { QuestionOutlineIcon } from '@chakra-ui/icons'; +import { useConfirm } from '@/web/common/hooks/useConfirm'; +import { useRequest } from '@/web/common/hooks/useRequest'; +import { useRouter } from 'next/router'; +import { useSystemStore } from '@/web/common/system/useSystemStore'; +import { formatTime2YMDHM } from '@fastgpt/global/common/string/time'; +import MySelect from '@/components/Select'; +import { + SubStatusEnum, + SubTypeEnum, + subSelectMap +} from '@fastgpt/global/support/wallet/sub/constants'; +import { SubDatasetSizePreviewCheckResponse } from '@fastgpt/global/support/wallet/sub/api.d'; +import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools'; +import { useUserStore } from '@/web/support/user/useUserStore'; + +const SubDatasetModal = ({ onClose }: { onClose: () => void }) => { + const { subPlans } = useSystemStore(); + const datasetStorePrice = subPlans?.extraDatasetSize?.price || 0; + + const { t } = useTranslation(); + const router = useRouter(); + const { ConfirmModal, openConfirm } = useConfirm({}); + const { userInfo } = useUserStore(); + const [datasetSize, setDatasetSize] = useState(0); + const [isRenew, setIsRenew] = useState('false'); + + const { data: teamSubPlan } = useQuery(['getTeamDatasetValidSub'], getTeamDatasetValidSub, { + onSuccess(res) { + setIsRenew(res?.extraDatasetSize?.status === SubStatusEnum.active ? 'true' : 'false'); + setDatasetSize((res?.extraDatasetSize?.nextExtraDatasetSize || 0) / 1000); + } + }); + + const { mutate: onClickUpdateSub, isLoading: isPaying } = useRequest({ + mutationFn: () => postUpdateTeamDatasetSizeSub({ size: datasetSize }), + onSuccess() { + setTimeout(() => { + router.reload(); + }, 100); + }, + successToast: t('common.Update success'), + errorToast: t('common.error.Update error') + }); + + const { mutate: onClickPreviewCheck, isLoading: isFetchingPreviewCheck } = useRequest({ + mutationFn: () => + posCheckTeamDatasetSizeSub({ + size: datasetSize + }), + onSuccess(res: SubDatasetSizePreviewCheckResponse) { + if (!res.payForNewSub) { + onClickUpdateSub(''); + return; + } else { + openConfirm( + () => { + if (!res.balanceEnough) return; + onClickUpdateSub(''); + }, + undefined, + + + 当前额外容量: + {teamSubPlan?.extraDatasetSize?.currentExtraDatasetSize || 0}条 + + + 新的额外容量: + {res.newSubSize}条 + + + 新套餐价格: + {formatStorePrice2Read(res.newPlanPrice)}元 + + + 本次需支付: + {formatStorePrice2Read(res.payPrice)}元 + + + 有效时长: + 30天 + + + 账号余额: + {formatStorePrice2Read(userInfo?.team?.balance).toFixed(3)}元 + + {!res.balanceEnough && ( + + 账号余额不足,请先充值余额再购买额外容量。 + + )} + + )(); + } + }, + errorToast: t('common.error.Update error') + }); + const { mutate: onUpdateStatus } = useRequest({ + mutationFn: (e: 'true' | 'false') => { + setIsRenew(e); + return putTeamDatasetSubStatus({ + status: subSelectMap[e], + type: SubTypeEnum.extraDatasetSize + }); + }, + successToast: t('common.Update success'), + errorToast: t('common.error.Update error') + }); + + const isLoading = isPaying || isFetchingPreviewCheck; + + return ( + + + <> + + {t('support.user.Price')} + + + + + + + + {t('support.wallet.subscription.Current dataset store')}: + + {teamSubPlan?.extraDatasetSize?.currentExtraDatasetSize || 0} + {t('core.dataset.data.unit')} + + + {teamSubPlan?.extraDatasetSize?.nextExtraDatasetSize !== undefined && ( + + {t('support.wallet.subscription.Next sub dataset size')}: + + {teamSubPlan?.extraDatasetSize?.nextExtraDatasetSize || 0} + {t('core.dataset.data.unit')} + + + )} + {!!teamSubPlan?.extraDatasetSize?.startTime && ( + + 订阅开始时间: + {formatTime2YMDHM(teamSubPlan?.extraDatasetSize?.startTime)} + + )} + {!!teamSubPlan?.extraDatasetSize?.expiredTime && ( + + 订阅到期时间: + {formatTime2YMDHM(teamSubPlan?.extraDatasetSize?.expiredTime)} + + )} + + 是否自动续费: + + + + {t('support.wallet.subscription.Update extra dataset size')} + + { + setDatasetSize(Number(e)); + }} + > + + + + + + + 000{t('core.dataset.data.unit')} + + + + + + {datasetSize * 1000 !== teamSubPlan?.extraDatasetSize?.nextExtraDatasetSize && ( + + )} + + + + + ); +}; + +export default SubDatasetModal; diff --git a/projects/app/src/constants/app.ts b/projects/app/src/constants/app.ts index de53fbab3..53651df43 100644 --- a/projects/app/src/constants/app.ts +++ b/projects/app/src/constants/app.ts @@ -15,8 +15,7 @@ export const defaultApp: AppDetailType = { tmbId: '', permission: 'private', isOwner: false, - canWrite: false, - teamTags: [''] + canWrite: false }; export const defaultOutLinkForm: OutLinkEditType = { @@ -24,7 +23,7 @@ export const defaultOutLinkForm: OutLinkEditType = { responseDetail: false, limit: { QPM: 100, - maxUsagePoints: -1 + credit: -1 } }; diff --git a/projects/app/src/global/core/chat/api.d.ts b/projects/app/src/global/core/chat/api.d.ts index 654005c81..db20bed4d 100644 --- a/projects/app/src/global/core/chat/api.d.ts +++ b/projects/app/src/global/core/chat/api.d.ts @@ -14,12 +14,6 @@ export type InitChatProps = { chatId?: string; loadCustomFeedbacks?: boolean; }; -/* ---------- chat ----------- */ -export type chatByTeamProps = { - teamId?: string; - appId?: string; - outLinkUid?: string; -}; export type InitOutLinkChatProps = { chatId?: string; shareId?: string; @@ -45,7 +39,6 @@ export type InitChatResponse = { /* ---------- history ----------- */ export type getHistoriesProps = { appId?: string; - authToken?: string; // share chat shareId?: string; outLinkUid?: string; // authToken/uid diff --git a/projects/app/src/global/core/prompt/AIChat.ts b/projects/app/src/global/core/prompt/AIChat.ts index ea2a18f80..a89f3cd87 100644 --- a/projects/app/src/global/core/prompt/AIChat.ts +++ b/projects/app/src/global/core/prompt/AIChat.ts @@ -4,34 +4,42 @@ export const Prompt_QuoteTemplateList: PromptTemplateItem[] = [ { title: '标准模板', desc: '标准提示词,用于结构不固定的知识库。', - value: `{{q}} -{{a}}` + value: ` +{{q}} +{{a}} +` }, { title: '问答模板', desc: '适合 QA 问答结构的知识库,可以让AI较为严格的按预设内容回答', - value: ` + value: ` +<问题> {{q}} - - + +<答案> {{a}} -` + +` }, { title: '标准严格模板', desc: '在标准模板基础上,对模型的回答做更严格的要求。', - value: `{{q}} -{{a}}` + value: ` +{{q}} +{{a}} +` }, { title: '严格问答模板', desc: '在问答模板基础上,对模型的回答做更严格的要求。', - value: ` + value: ` +<问题> {{q}} - - + +<答案> {{a}} -` + +` } ]; @@ -39,16 +47,14 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [ { title: '标准模板', desc: '', - value: `使用 标记中的内容作为你的知识: + value: `使用 标记中的内容作为你的知识: - {{quote}} - 回答要求: - 如果你不清楚答案,你需要澄清。 -- 避免提及你是从 获取的知识。 -- 保持答案与 中描述的一致。 +- 避免提及你是从 获取的知识。 +- 保持答案与 中描述的一致。 - 使用 Markdown 语法优化回答格式。 - 使用与问题相同的语言回答。 @@ -59,9 +65,7 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [ desc: '', value: `使用 标记中的问答对进行回答。 - {{quote}} - 回答要求: - 选择其中一个或多个问答对进行回答。 @@ -74,20 +78,18 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [ { title: '标准严格模板', desc: '', - value: `忘记你已有的知识,仅使用 标记中的内容作为你的知识: + value: `忘记你已有的知识,仅使用 标记中的内容作为你的知识: - {{quote}} - 思考流程: -1. 判断问题是否与 标记中的内容有关。 +1. 判断问题是否与 标记中的内容有关。 2. 如果有关,你按下面的要求回答。 3. 如果无关,你直接拒绝回答本次问题。 回答要求: -- 避免提及你是从 获取的知识。 -- 保持答案与 中描述的一致。 +- 避免提及你是从 获取的知识。 +- 保持答案与 中描述的一致。 - 使用 Markdown 语法优化回答格式。 - 使用与问题相同的语言回答。 @@ -98,9 +100,7 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [ desc: '', value: `忘记你已有的知识,仅使用 标记中的问答对进行回答。 - {{quote}} -} 思考流程: 1. 判断问题是否与 标记中的内容有关。 diff --git a/projects/app/src/pages/_error.tsx b/projects/app/src/pages/_error.tsx index f2acb97a9..1c119ad0a 100644 --- a/projects/app/src/pages/_error.tsx +++ b/projects/app/src/pages/_error.tsx @@ -2,7 +2,6 @@ import { useEffect } from 'react'; import { useRouter } from 'next/router'; import { serviceSideProps } from '@/web/common/utils/i18n'; import { useSystemStore } from '@/web/common/system/useSystemStore'; -import { Box } from '@chakra-ui/react'; function Error() { const router = useRouter(); @@ -25,12 +24,11 @@ function Error() { }, []); return ( - - {`出现未捕获的异常。 -1. 私有部署用户,90%由于配置文件不正确导致。 -2. 部分系统不兼容相关API。大部分是苹果的safari 浏览器导致,可以尝试更换 chrome。 -3. 请关闭浏览器翻译功能,部分翻译导致页面崩溃。`} - +

+ 部分系统不兼容,导致页面崩溃。如果可以,请联系作者,反馈下具体操作和页面。 大部分是 苹果 的 + safari 浏览器导致,可以尝试更换 chrome + 浏览器。或者是因为开了中文翻译导致,请检查并关闭中文翻译。 +

); } diff --git a/projects/app/src/pages/account/components/BillDetail.tsx b/projects/app/src/pages/account/components/BillDetail.tsx new file mode 100644 index 000000000..6d089c2cf --- /dev/null +++ b/projects/app/src/pages/account/components/BillDetail.tsx @@ -0,0 +1,160 @@ +import React, { useMemo } from 'react'; +import { + ModalBody, + Flex, + Box, + Table, + Thead, + Tbody, + Tr, + Th, + Td, + TableContainer +} from '@chakra-ui/react'; +import { BillItemType } from '@fastgpt/global/support/wallet/bill/type.d'; +import dayjs from 'dayjs'; +import { BillSourceMap } from '@fastgpt/global/support/wallet/bill/constants'; +import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools'; +import MyModal from '@/components/MyModal'; +import { useTranslation } from 'next-i18next'; + +const BillDetail = ({ bill, onClose }: { bill: BillItemType; onClose: () => void }) => { + const { t } = useTranslation(); + const filterBillList = useMemo( + () => bill.list.filter((item) => item && item.moduleName), + [bill.list] + ); + + const { + hasModel, + hasTokens, + hasInputTokens, + hasOutputTokens, + hasCharsLen, + hasDuration, + hasDataLen, + hasDatasetSize + } = useMemo(() => { + let hasModel = false; + let hasTokens = false; + let hasInputTokens = false; + let hasOutputTokens = false; + let hasCharsLen = false; + let hasDuration = false; + let hasDataLen = false; + let hasDatasetSize = false; + + bill.list.forEach((item) => { + if (item.model !== undefined) { + hasModel = true; + } + if (typeof item.tokenLen === 'number') { + hasTokens = true; + } + if (typeof item.inputTokens === 'number') { + hasInputTokens = true; + } + if (typeof item.outputTokens === 'number') { + hasOutputTokens = true; + } + if (typeof item.charsLength === 'number') { + hasCharsLen = true; + } + if (typeof item.duration === 'number') { + hasDuration = true; + } + if (typeof item.datasetSize === 'number') { + hasDatasetSize = true; + } + }); + + return { + hasModel, + hasTokens, + hasInputTokens, + hasOutputTokens, + hasCharsLen, + hasDuration, + hasDataLen, + hasDatasetSize + }; + }, [bill.list]); + + return ( + + + {/* + {t('wallet.bill.bill username')}: + {t(bill.memberName)} + */} + + {t('wallet.bill.Number')}: + {bill.id} + + + {t('wallet.bill.Time')}: + {dayjs(bill.time).format('YYYY/MM/DD HH:mm:ss')} + + + {t('wallet.bill.App name')}: + {t(bill.appName) || '-'} + + + {t('wallet.bill.Source')}: + {t(BillSourceMap[bill.source]?.label)} + + + {t('wallet.bill.Total')}: + {bill.total}元 + + + + {t('wallet.bill.Bill Module')} + + + + + + + {hasModel && } + {hasTokens && } + {hasInputTokens && } + {hasOutputTokens && } + {hasCharsLen && } + {hasDuration && } + {hasDatasetSize && ( + + )} + + + + + {filterBillList.map((item, i) => ( + + + {hasModel && } + {hasTokens && } + {hasInputTokens && } + {hasOutputTokens && } + {hasCharsLen && } + {hasDuration && } + {hasDatasetSize && } + + + ))} + +
{t('wallet.bill.Module name')}{t('wallet.bill.Ai model')}{t('wallet.bill.Token Length')}{t('wallet.bill.Input Token Length')}{t('wallet.bill.Output Token Length')}{t('wallet.bill.Text Length')}{t('wallet.bill.Duration')}{t('support.wallet.subscription.type.extraDatasetSize')}费用(¥)
{t(item.moduleName)}{item.model ?? '-'}{item.tokenLen ?? '-'}{item.inputTokens ?? '-'}{item.outputTokens ?? '-'}{item.charsLength ?? '-'}{item.duration ?? '-'}{item.datasetSize ?? '-'}{formatStorePrice2Read(item.amount)}
+
+
+
+
+ ); +}; + +export default BillDetail; diff --git a/projects/app/src/pages/account/components/BillTable.tsx b/projects/app/src/pages/account/components/BillTable.tsx index eb382a952..8ea7eddd5 100644 --- a/projects/app/src/pages/account/components/BillTable.tsx +++ b/projects/app/src/pages/account/components/BillTable.tsx @@ -1,6 +1,5 @@ -import React, { useState, useCallback, useMemo, useEffect } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { - Button, Table, Thead, Tbody, @@ -10,38 +9,43 @@ import { TableContainer, Flex, Box, - ModalBody + Button } from '@chakra-ui/react'; -import { getBills, checkBalancePayResult } from '@/web/support/wallet/bill/api'; -import type { BillSchemaType } from '@fastgpt/global/support/wallet/bill/type.d'; +import { BillSourceEnum, BillSourceMap } from '@fastgpt/global/support/wallet/bill/constants'; +import { getUserBills } from '@/web/support/wallet/bill/api'; +import type { BillItemType } from '@fastgpt/global/support/wallet/bill/type'; +import { usePagination } from '@/web/common/hooks/usePagination'; +import { useLoading } from '@/web/common/hooks/useLoading'; import dayjs from 'dayjs'; -import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/usage/tools'; -import { useToast } from '@fastgpt/web/hooks/useToast'; import MyIcon from '@fastgpt/web/components/common/Icon'; +import DateRangePicker, { type DateRangeType } from '@/components/DateRangePicker'; +import { addDays } from 'date-fns'; +import dynamic from 'next/dynamic'; +import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useTranslation } from 'next-i18next'; import MySelect from '@/components/Select'; -import { - BillTypeEnum, - billPayWayMap, - billStatusMap, - billTypeMap -} from '@fastgpt/global/support/wallet/bill/constants'; -import { usePagination } from '@/web/common/hooks/usePagination'; -import MyBox from '@/components/common/MyBox'; -import { useRequest } from '@/web/common/hooks/useRequest'; -import MyModal from '@/components/MyModal'; -import { standardSubLevelMap, subModeMap } from '@fastgpt/global/support/wallet/sub/constants'; +import { useQuery } from '@tanstack/react-query'; +import { useUserStore } from '@/web/support/user/useUserStore'; +import { getTeamMembers } from '@/web/support/user/team/api'; +import Avatar from '@/components/Avatar'; +const BillDetail = dynamic(() => import('./BillDetail')); const BillTable = () => { const { t } = useTranslation(); - const { toast } = useToast(); - const [billType, setBillType] = useState<`${BillTypeEnum}` | ''>(''); - const [billDetail, setBillDetail] = useState(); + const { Loading } = useLoading(); + const [dateRange, setDateRange] = useState({ + from: addDays(new Date(), -7), + to: new Date() + }); + const [billSource, setBillSource] = useState<`${BillSourceEnum}` | ''>(''); + const { isPc } = useSystemStore(); + const { userInfo } = useUserStore(); + const [billDetail, setBillDetail] = useState(); - const billTypeList = useMemo( + const sourceList = useMemo( () => [ { label: t('common.All'), value: '' }, - ...Object.entries(billTypeMap).map(([key, value]) => ({ + ...Object.entries(BillSourceMap).map(([key, value]) => ({ label: t(value.label), value: key })) @@ -49,193 +53,134 @@ const BillTable = () => { [t] ); + const [selectTmbId, setSelectTmbId] = useState(userInfo?.team?.tmbId); + const { data: members = [] } = useQuery(['getMembers', userInfo?.team?.teamId], () => { + if (!userInfo?.team?.teamId) return []; + return getTeamMembers(userInfo.team.teamId); + }); + const tmbList = useMemo( + () => + members.map((item) => ({ + label: ( + + + {item.memberName} + + ), + value: item.tmbId + })), + [members] + ); + const { data: bills, isLoading, Pagination, - getData, - total - } = usePagination({ - api: getBills, - pageSize: 20, + getData + } = usePagination({ + api: getUserBills, + pageSize: isPc ? 20 : 10, params: { - type: billType + dateStart: dateRange.from || new Date(), + dateEnd: addDays(dateRange.to || new Date(), 1), + source: billSource, + teamMemberId: selectTmbId }, defaultRequest: false }); - const { mutate: handleRefreshPayOrder, isLoading: isRefreshing } = useRequest({ - mutationFn: async (payId: string) => { - try { - const data = await checkBalancePayResult(payId); - toast({ - title: data, - status: 'success' - }); - } catch (error: any) { - toast({ - title: error?.message, - status: 'warning' - }); - console.log(error); - } - try { - getData(1); - } catch (error) {} - } - }); - useEffect(() => { getData(1); - }, [billType]); + }, [billSource, selectTmbId]); return ( - - + + + {tmbList.length > 1 && userInfo?.team?.canWrite && ( + + + {t('support.user.team.member')} + + + + )} + + + getData(1)} + /> + + + + - + {/* */} + - - - + + - {bills.map((item, i) => ( - - - + {bills.map((item) => ( + + {/* */} + + + + - - - ))}
#{t('user.team.Member Name')}{t('user.Time')} { - setBillType(e); + setBillSource(e); }} w={'130px'} > {t('user.Time')}{t('support.wallet.Amount')}{t('support.wallet.bill.Status')}{t('user.Application Name')}{t('user.Total Amount')}
{i + 1}{t(billTypeMap[item.type]?.label)}
{item.memberName}{dayjs(item.time).format('YYYY/MM/DD HH:mm:ss')}{t(BillSourceMap[item.source]?.label)}{t(item.appName) || '-'}{item.total}元 - {item.createTime ? dayjs(item.createTime).format('YYYY/MM/DD HH:mm:ss') : '-'} - {formatStorePrice2Read(item.price)}元{t(billStatusMap[item.status]?.label)} - {item.status === 'NOTPAY' && ( - - )} -
- {total >= 20 && ( - - - - )} - {!isLoading && bills.length === 0 && ( - - - - {t('support.wallet.noBill')} - - - )}
- {!!billDetail && ( - setBillDetail(undefined)} /> + {!isLoading && bills.length === 0 && ( + + + + 无使用记录~ + + )} -
+ + + {!!billDetail && setBillDetail(undefined)} />} + ); }; -export default BillTable; - -function BillDetailModal({ bill, onClose }: { bill: BillSchemaType; onClose: () => void }) { - const { t } = useTranslation(); - - return ( - - - - {t('support.wallet.bill.Number')}: - {bill.orderId} - - - {t('support.wallet.usage.Time')}: - {dayjs(bill.createTime).format('YYYY/MM/DD HH:mm:ss')} - - - {t('support.wallet.bill.Status')}: - {t(billStatusMap[bill.status]?.label)} - - {!!bill.metadata?.payWay && ( - - {t('support.wallet.bill.payWay.Way')}: - {t(billPayWayMap[bill.metadata.payWay]?.label)} - - )} - - {t('support.wallet.Amount')}: - {formatStorePrice2Read(bill.price)}元 - - - {t('support.wallet.bill.Type')}: - {t(billTypeMap[bill.type]?.label)} - - {!!bill.metadata?.subMode && ( - - {t('support.wallet.subscription.mode.Period')}: - {t(subModeMap[bill.metadata.subMode]?.label)} - - )} - {!!bill.metadata?.standSubLevel && ( - - {t('support.wallet.subscription.Stand plan level')}: - {t(standardSubLevelMap[bill.metadata.standSubLevel]?.label)} - - )} - {bill.metadata?.datasetSize !== undefined && ( - - {t('support.wallet.subscription.Extra dataset size')}: - {bill.metadata?.datasetSize} - - )} - {bill.metadata?.extraPoints !== undefined && ( - - {t('support.wallet.subscription.Extra ai points')}: - {bill.metadata.extraPoints} - - )} - - - ); -} +export default React.memo(BillTable); diff --git a/projects/app/src/pages/account/components/Info.tsx b/projects/app/src/pages/account/components/Info.tsx index 6f6d45a75..a957475be 100644 --- a/projects/app/src/pages/account/components/Info.tsx +++ b/projects/app/src/pages/account/components/Info.tsx @@ -1,14 +1,15 @@ -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback, useMemo, useRef } from 'react'; import { Box, Flex, Button, useDisclosure, useTheme, + Divider, + Select, Input, Link, - Progress, - Grid + Progress } from '@chakra-ui/react'; import { useForm } from 'react-hook-form'; import { UserUpdateParams } from '@/types/user'; @@ -21,68 +22,35 @@ import { useSelectFile } from '@/web/common/file/hooks/useSelectFile'; import { compressImgFileAndUpload } from '@/web/common/file/controller'; import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useTranslation } from 'next-i18next'; +import { timezoneList } from '@fastgpt/global/common/time/timezone'; import Avatar from '@/components/Avatar'; import MyIcon from '@fastgpt/web/components/common/Icon'; import MyTooltip from '@/components/MyTooltip'; +import { langMap, setLngStore } from '@/web/common/utils/i18n'; import { useRouter } from 'next/router'; -import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/usage/tools'; +import MySelect from '@/components/Select'; +import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools'; import { putUpdateMemberName } from '@/web/support/user/team/api'; import { getDocPath } from '@/web/common/system/doc'; +import { getTeamDatasetValidSub } from '@/web/support/wallet/sub/api'; import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants'; -import { standardSubLevelMap } from '@fastgpt/global/support/wallet/sub/constants'; -import { formatTime2YMDHM } from '@fastgpt/global/common/string/time'; -import { AI_POINT_USAGE_CARD_ROUTE } from '@/web/support/wallet/sub/constants'; -import StandardPlanContentList from '@/components/support/wallet/StandardPlanContentList'; -const StandDetailModal = dynamic(() => import('./standardDetailModal')); const TeamMenu = dynamic(() => import('@/components/support/user/team/TeamMenu')); const PayModal = dynamic(() => import('./PayModal')); const UpdatePswModal = dynamic(() => import('./UpdatePswModal')); const OpenAIAccountModal = dynamic(() => import('./OpenAIAccountModal')); +const SubDatasetModal = dynamic(() => import('@/components/support/wallet/SubDatasetModal')); -const Account = () => { - const { isPc } = useSystemStore(); - - const { initUserInfo } = useUserStore(); - - useQuery(['init'], initUserInfo); - - return ( - - {isPc ? ( - - - - - - - - - - - - ) : ( - <> - - - - - )} - - ); -}; - -export default React.memo(Account); - -const MyInfo = () => { +const UserInfo = () => { const theme = useTheme(); - const { feConfigs } = useSystemStore(); - const { t } = useTranslation(); - const { userInfo, updateUserInfo } = useUserStore(); + const router = useRouter(); + const { feConfigs, systemVersion } = useSystemStore(); + const { t, i18n } = useTranslation(); + const { userInfo, updateUserInfo, initUserInfo } = useUserStore(); + const timezones = useRef(timezoneList()); const { reset } = useForm({ defaultValues: userInfo as UserType }); - const { isPc } = useSystemStore(); const { toast } = useToast(); const { @@ -95,11 +63,13 @@ const MyInfo = () => { onClose: onCloseUpdatePsw, onOpen: onOpenUpdatePsw } = useDisclosure(); + const { isOpen: isOpenOpenai, onClose: onCloseOpenai, onOpen: onOpenOpenai } = useDisclosure(); const { - isOpen: isOpenStandardModal, - onClose: onCloseStandardModal, - onOpen: onOpenStandardModal + isOpen: isOpenSubDatasetModal, + onClose: onCloseSubDatasetModal, + onOpen: onOpenSubDatasetModal } = useDisclosure(); + const { File, onOpen: onOpenSelectFile } = useSelectFile({ fileType: '.jpg,.png', multiple: false @@ -147,73 +117,81 @@ const MyInfo = () => { [onclickSave, t, toast, userInfo] ); + useQuery(['init'], initUserInfo, { + onSuccess(res) { + reset(res); + } + }); + + const { + data: teamSubPlan = { totalPoints: 0, usedPoints: 0, datasetMaxSize: 800, usedDatasetSize: 0 } + } = useQuery(['getTeamDatasetValidSub'], getTeamDatasetValidSub); + const datasetUsageMap = useMemo(() => { + const rate = teamSubPlan.usedDatasetSize / teamSubPlan.datasetMaxSize; + + const colorScheme = (() => { + if (rate < 0.5) return 'green'; + if (rate < 0.8) return 'yellow'; + return 'red'; + })(); + + return { + colorScheme, + value: rate * 100, + maxSize: teamSubPlan.datasetMaxSize || t('common.Unlimited'), + usedSize: teamSubPlan.usedDatasetSize + }; + }, [teamSubPlan.usedDatasetSize, teamSubPlan.datasetMaxSize, t]); + return ( - - {/* user info */} - {isPc && ( - - - {t('support.user.User self info')} - - )} - - - {isPc ? ( - - {t('support.user.Avatar')}:  - - - - - - - - ) : ( - + + + - - - - - + + + - - - {t('user.Replace')} - - - )} + + + {t('user.Replace')} + + + {feConfigs.isPlus && ( - + {t('user.Member Name')}:  { @@ -226,265 +204,109 @@ const MyInfo = () => { /> )} - + {t('user.Account')}:  {userInfo?.username} - {feConfigs.isPlus && ( - - {t('user.Password')}:  - ***** - - - )} - + {t('user.Team')}:  - {feConfigs.isPlus && ( - - - - {t('user.team.Balance')}:  - - - {formatStorePrice2Read(userInfo?.team?.balance).toFixed(3)} 元 - - {feConfigs?.show_pay && userInfo?.team?.canWrite && ( - - )} - - - )} - - {isOpenPayModal && } - {isOpenUpdatePsw && } - - - ); -}; -const PlanUsage = () => { - const { isPc } = useSystemStore(); - const router = useRouter(); - const { t } = useTranslation(); - const { userInfo, initUserInfo, teamPlanStatus } = useUserStore(); - const { reset } = useForm({ - defaultValues: userInfo as UserType - }); - - const planName = useMemo(() => { - if (!teamPlanStatus?.standard?.currentSubLevel) return ''; - return standardSubLevelMap[teamPlanStatus.standard.currentSubLevel].label; - }, [teamPlanStatus?.standard?.currentSubLevel]); - const standardPlan = teamPlanStatus?.standard; - - useQuery(['init'], initUserInfo, { - onSuccess(res) { - reset(res); - } - }); - - const datasetUsageMap = useMemo(() => { - if (!teamPlanStatus) { - return { - colorScheme: 'green', - value: 0, - maxSize: t('common.Unlimited'), - usedSize: 0 - }; - } - const rate = teamPlanStatus.usedDatasetSize / teamPlanStatus.datasetMaxSize; - - const colorScheme = (() => { - if (rate < 0.5) return 'green'; - if (rate < 0.8) return 'yellow'; - return 'red'; - })(); - - return { - colorScheme, - value: rate * 100, - maxSize: teamPlanStatus.datasetMaxSize || t('common.Unlimited'), - usedSize: teamPlanStatus.usedDatasetSize - }; - }, [teamPlanStatus, t]); - const aiPointsUsageMap = useMemo(() => { - if (!teamPlanStatus) { - return { - colorScheme: 'green', - value: 0, - maxSize: t('common.Unlimited'), - usedSize: 0 - }; - } - - const rate = teamPlanStatus.usedPoints / teamPlanStatus.totalPoints; - - const colorScheme = (() => { - if (rate < 0.5) return 'green'; - if (rate < 0.8) return 'yellow'; - return 'red'; - })(); - - return { - colorScheme, - value: rate * 100, - maxSize: teamPlanStatus.totalPoints || t('common.Unlimited'), - usedSize: teamPlanStatus.usedPoints - }; - }, [teamPlanStatus, t]); - - return standardPlan ? ( - - - - - {t('support.wallet.subscription.Team plan and usage')} - - - {/* */} - - - + + {t('user.Language')}:  - - {t('support.wallet.subscription.Current plan')} - - - {t(planName)} - - - {t('common.Expired Time')}: - {formatTime2YMDHM(standardPlan?.expiredTime)} - + ({ + label: lang.label, + value: key + }))} + onchange={(val: any) => { + const lang = val; + setLngStore(lang); + router.replace(router.basePath, router.asPath, { locale: lang }); + }} + /> - - - - - - - - - - - - {t('support.user.team.Dataset usage')} - - {datasetUsageMap.usedSize}/{datasetUsageMap.maxSize} + {feConfigs.isPlus && ( + <> + + + + {t('user.team.Balance')}:  + + + {formatStorePrice2Read(userInfo?.team?.balance).toFixed(3)} 元 + + {feConfigs?.show_pay && userInfo?.team?.canWrite && ( + + )} + + + {feConfigs?.show_pay && ( + + + + {t('support.user.team.Dataset usage')}: {datasetUsageMap.usedSize}/ + {datasetUsageMap.maxSize} + + {userInfo?.team?.canWrite && ( + + )} + + + + - - - - - - - - - - {t('support.wallet.subscription.AI points')} - - {Math.round(teamPlanStatus.usedPoints)}/{teamPlanStatus.totalPoints} - - - - - - - - - - - ) : null; -}; -const Other = () => { - const theme = useTheme(); - const { toast } = useToast(); - const { feConfigs, systemVersion } = useSystemStore(); - const { t } = useTranslation(); - const { userInfo, updateUserInfo, initUserInfo, teamPlanStatus } = useUserStore(); - const { reset } = useForm({ - defaultValues: userInfo as UserType - }); + )} + + )} - const { isOpen: isOpenOpenai, onClose: onCloseOpenai, onOpen: onOpenOpenai } = useDisclosure(); - - const onclickSave = useCallback( - async (data: UserType) => { - await updateUserInfo({ - avatar: data.avatar, - timezone: data.timezone, - openaiAccount: data.openaiAccount - }); - reset(data); - toast({ - title: '更新数据成功', - status: 'success' - }); - }, - [reset, toast, updateUserInfo] - ); - - return ( - - {feConfigs?.docUrl && ( { )} - - - - {t('common.system.Help Chatbot')} - - - - {feConfigs?.show_openai_account && ( - - + - OpenAI/OneAPI 账号 + {t('common.system.Help Chatbot')} - - + )} - + {feConfigs?.show_openai_account && ( + <> + + + + + + OpenAI/OneAPI 账号 + + + + + + )} + + + {isOpenPayModal && } + {isOpenUpdatePsw && } {isOpenOpenai && userInfo && ( { onClose={onCloseOpenai} /> )} + {isOpenSubDatasetModal && } + ); }; + +export default React.memo(UserInfo); diff --git a/projects/app/src/pages/account/components/InfoOld.tsx b/projects/app/src/pages/account/components/InfoOld.tsx deleted file mode 100644 index 83e16173f..000000000 --- a/projects/app/src/pages/account/components/InfoOld.tsx +++ /dev/null @@ -1,439 +0,0 @@ -import React, { useCallback, useMemo, useRef } from 'react'; -import { - Box, - Flex, - Button, - useDisclosure, - useTheme, - Divider, - Select, - Input, - Link, - Progress -} from '@chakra-ui/react'; -import { useForm } from 'react-hook-form'; -import { UserUpdateParams } from '@/types/user'; -import { useToast } from '@fastgpt/web/hooks/useToast'; -import { useUserStore } from '@/web/support/user/useUserStore'; -import type { UserType } from '@fastgpt/global/support/user/type.d'; -import { useQuery } from '@tanstack/react-query'; -import dynamic from 'next/dynamic'; -import { useSelectFile } from '@/web/common/file/hooks/useSelectFile'; -import { compressImgFileAndUpload } from '@/web/common/file/controller'; -import { useSystemStore } from '@/web/common/system/useSystemStore'; -import { useTranslation } from 'next-i18next'; -import { timezoneList } from '@fastgpt/global/common/time/timezone'; -import Avatar from '@/components/Avatar'; -import MyIcon from '@fastgpt/web/components/common/Icon'; -import MyTooltip from '@/components/MyTooltip'; -import { langMap, setLngStore } from '@/web/common/utils/i18n'; -import { useRouter } from 'next/router'; -import MySelect from '@/components/Select'; -import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/usage/tools'; -import { putUpdateMemberName } from '@/web/support/user/team/api'; -import { getDocPath } from '@/web/common/system/doc'; -import { getTeamPlanStatus } from '@/web/support/wallet/sub/api'; -import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants'; - -const TeamMenu = dynamic(() => import('@/components/support/user/team/TeamMenu')); -const PayModal = dynamic(() => import('./PayModal')); -const UpdatePswModal = dynamic(() => import('./UpdatePswModal')); -const OpenAIAccountModal = dynamic(() => import('./OpenAIAccountModal')); - -const UserInfo = () => { - const theme = useTheme(); - const router = useRouter(); - const { feConfigs, systemVersion } = useSystemStore(); - const { t, i18n } = useTranslation(); - const { userInfo, updateUserInfo, initUserInfo } = useUserStore(); - const timezones = useRef(timezoneList()); - const { reset } = useForm({ - defaultValues: userInfo as UserType - }); - - const { toast } = useToast(); - const { - isOpen: isOpenPayModal, - onClose: onClosePayModal, - onOpen: onOpenPayModal - } = useDisclosure(); - const { - isOpen: isOpenUpdatePsw, - onClose: onCloseUpdatePsw, - onOpen: onOpenUpdatePsw - } = useDisclosure(); - const { isOpen: isOpenOpenai, onClose: onCloseOpenai, onOpen: onOpenOpenai } = useDisclosure(); - - const { File, onOpen: onOpenSelectFile } = useSelectFile({ - fileType: '.jpg,.png', - multiple: false - }); - - const onclickSave = useCallback( - async (data: UserType) => { - await updateUserInfo({ - avatar: data.avatar, - timezone: data.timezone, - openaiAccount: data.openaiAccount - }); - reset(data); - toast({ - title: '更新数据成功', - status: 'success' - }); - }, - [reset, toast, updateUserInfo] - ); - - const onSelectFile = useCallback( - async (e: File[]) => { - const file = e[0]; - if (!file || !userInfo) return; - try { - const src = await compressImgFileAndUpload({ - type: MongoImageTypeEnum.userAvatar, - file, - maxW: 300, - maxH: 300 - }); - - onclickSave({ - ...userInfo, - avatar: src - }); - } catch (err: any) { - toast({ - title: typeof err === 'string' ? err : t('common.error.Select avatar failed'), - status: 'warning' - }); - } - }, - [onclickSave, t, toast, userInfo] - ); - - useQuery(['init'], initUserInfo, { - onSuccess(res) { - reset(res); - } - }); - - const { - data: teamSubPlan = { - totalPoints: 0, - usedPoints: 0, - datasetMaxSize: 800, - usedDatasetSize: 0 - } - } = useQuery(['getTeamPlanStatus'], getTeamPlanStatus); - const datasetUsageMap = useMemo(() => { - const rate = teamSubPlan.usedDatasetSize / teamSubPlan.datasetMaxSize; - - const colorScheme = (() => { - if (rate < 0.5) return 'green'; - if (rate < 0.8) return 'yellow'; - return 'red'; - })(); - - return { - colorScheme, - value: rate * 100, - maxSize: teamSubPlan.datasetMaxSize || t('common.Unlimited'), - usedSize: teamSubPlan.usedDatasetSize - }; - }, [teamSubPlan.usedDatasetSize, teamSubPlan.datasetMaxSize, t]); - const aiPointsUsageMap = useMemo(() => { - const rate = teamSubPlan.usedPoints / teamSubPlan.totalPoints; - - const colorScheme = (() => { - if (rate < 0.5) return 'green'; - if (rate < 0.8) return 'yellow'; - return 'red'; - })(); - - return { - colorScheme, - value: rate * 100, - maxSize: teamSubPlan.totalPoints || t('common.Unlimited'), - usedSize: teamSubPlan.usedPoints - }; - }, [teamSubPlan.usedPoints, teamSubPlan.totalPoints, t]); - - return ( - - - - - - - - - - - {t('user.Replace')} - - - - {feConfigs.isPlus && ( - - {t('user.Member Name')}:  - { - const val = e.target.value; - if (val === userInfo?.team?.memberName) return; - try { - putUpdateMemberName(val); - } catch (error) {} - }} - /> - - )} - - {t('user.Account')}:  - {userInfo?.username} - - - {t('user.Team')}:  - - - - - - {t('user.Language')}:  - - ({ - label: lang.label, - value: key - }))} - onchange={(val: any) => { - const lang = val; - setLngStore(lang); - router.replace(router.basePath, router.asPath, { locale: lang }); - }} - /> - - - - {t('user.Timezone')}:  - - - - {t('user.Password')}:  - ***** - - - {feConfigs.isPlus && ( - <> - - - - {t('user.team.Balance')}:  - - - {formatStorePrice2Read(userInfo?.team?.balance).toFixed(3)} 元 - - {feConfigs?.show_pay && userInfo?.team?.canWrite && ( - - )} - - - {feConfigs?.show_pay && ( - <> - - - - {t('support.user.team.Dataset usage')}: {datasetUsageMap.usedSize}/ - {datasetUsageMap.maxSize} - - {userInfo?.team?.canWrite && ( - - )} - - - - - - - - - AI积分: {Math.round(teamSubPlan.usedPoints)}/{teamSubPlan.totalPoints} - - - - - - - - )} - - )} - - {feConfigs?.docUrl && ( - - - - {t('system.Help Document')} - - - - V{systemVersion} - - - )} - {feConfigs?.chatbotUrl && ( - - - - {t('common.system.Help Chatbot')} - - - )} - {feConfigs?.show_openai_account && ( - <> - - - - - - - OpenAI/OneAPI 账号 - - - - - - )} - - - {isOpenPayModal && } - {isOpenUpdatePsw && } - {isOpenOpenai && userInfo && ( - - onclickSave({ - ...userInfo, - openaiAccount: data - }) - } - onClose={onCloseOpenai} - /> - )} - - - ); -}; - -export default React.memo(UserInfo); diff --git a/projects/app/src/pages/account/components/PayModal.tsx b/projects/app/src/pages/account/components/PayModal.tsx index cbcb8d23d..a397f409f 100644 --- a/projects/app/src/pages/account/components/PayModal.tsx +++ b/projects/app/src/pages/account/components/PayModal.tsx @@ -1,45 +1,37 @@ import React, { useState, useCallback } from 'react'; import { ModalFooter, ModalBody, Button, Input, Box, Grid } from '@chakra-ui/react'; -import { getWxPayQRCode } from '@/web/support/wallet/bill/api'; +import { getPayCode, checkPayResult } from '@/web/support/wallet/pay/api'; import { useToast } from '@fastgpt/web/hooks/useToast'; +import { useQuery } from '@tanstack/react-query'; import { useRouter } from 'next/router'; import { getErrText } from '@fastgpt/global/common/error/utils'; import { useTranslation } from 'next-i18next'; +import Markdown from '@/components/Markdown'; import MyModal from '@/components/MyModal'; -import { BillTypeEnum } from '@fastgpt/global/support/wallet/bill/constants'; -import QRCodePayModal, { type QRPayProps } from '@/components/support/wallet/QRCodePayModal'; - -const PayModal = ({ - onClose, - defaultValue, - onSuccess -}: { - defaultValue?: number; - onClose: () => void; - onSuccess?: () => any; -}) => { +const PayModal = ({ onClose }: { onClose: () => void }) => { const router = useRouter(); const { t } = useTranslation(); const { toast } = useToast(); - const [inputVal, setInputVal] = useState(defaultValue); + const [inputVal, setInputVal] = useState(''); const [loading, setLoading] = useState(false); - const [qrPayData, setQRPayData] = useState(); + const [payId, setPayId] = useState(''); const handleClickPay = useCallback(async () => { if (!inputVal || inputVal <= 0 || isNaN(+inputVal)) return; setLoading(true); try { // 获取支付二维码 - const res = await getWxPayQRCode({ - type: BillTypeEnum.balance, - balance: inputVal - }); - setQRPayData({ - readPrice: res.readPrice, - codeUrl: res.codeUrl, - billId: res.billId + const res = await getPayCode(inputVal); + new window.QRCode(document.getElementById('payQRCode'), { + text: res.codeUrl, + width: 128, + height: 128, + colorDark: '#000000', + colorLight: '#ffffff', + correctLevel: window.QRCode.CorrectLevel.H }); + setPayId(res.payId); } catch (err) { toast({ title: getErrText(err), @@ -49,48 +41,84 @@ const PayModal = ({ setLoading(false); }, [inputVal, toast]); + useQuery( + [payId], + () => { + if (!payId) return null; + return checkPayResult(payId); + }, + { + enabled: !!payId, + refetchInterval: 3000, + onSuccess(res) { + if (!res) return; + toast({ + title: res, + status: 'success' + }); + router.reload(); + } + } + ); + return ( - + - - {[10, 20, 50, 100, 200, 500].map((item) => ( - - ))} - - - { - setInputVal(Math.floor(+e.target.value)); - }} - > + {!payId && ( + <> + + {[10, 20, 50, 100, 200, 500].map((item) => ( + + ))} + + + { + setInputVal(Math.floor(+e.target.value)); + }} + > + + + )} + {/* 付费二维码 */} + + {payId && 请微信扫码支付: {inputVal}元,请勿关闭页面} + - - + {!payId && ( + <> + + + + )} - - {!!qrPayData && } ); }; diff --git a/projects/app/src/pages/account/components/PayRecordTable.tsx b/projects/app/src/pages/account/components/PayRecordTable.tsx new file mode 100644 index 000000000..e5567764b --- /dev/null +++ b/projects/app/src/pages/account/components/PayRecordTable.tsx @@ -0,0 +1,108 @@ +import React, { useState, useCallback } from 'react'; +import { + Button, + Table, + Thead, + Tbody, + Tr, + Th, + Td, + TableContainer, + Flex, + Box +} from '@chakra-ui/react'; +import { getPayOrders, checkPayResult } from '@/web/support/wallet/pay/api'; +import type { PaySchema } from '@fastgpt/global/support/wallet/pay/type.d'; +import dayjs from 'dayjs'; +import { useQuery } from '@tanstack/react-query'; +import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools'; +import { useToast } from '@fastgpt/web/hooks/useToast'; +import { useLoading } from '@/web/common/hooks/useLoading'; +import MyIcon from '@fastgpt/web/components/common/Icon'; + +const PayRecordTable = () => { + const { Loading, setIsLoading } = useLoading(); + const [payOrders, setPayOrders] = useState([]); + const { toast } = useToast(); + + const { isInitialLoading, refetch } = useQuery(['initPayOrder'], getPayOrders, { + onSuccess(res) { + setPayOrders(res); + } + }); + + const handleRefreshPayOrder = useCallback( + async (payId: string) => { + setIsLoading(true); + + try { + const data = await checkPayResult(payId); + toast({ + title: data, + status: 'success' + }); + } catch (error: any) { + toast({ + title: error?.message, + status: 'warning' + }); + console.log(error); + } + try { + refetch(); + } catch (error) {} + + setIsLoading(false); + }, + [refetch, setIsLoading, toast] + ); + + return ( + + {!isInitialLoading && payOrders.length === 0 ? ( + + + + 无支付记录~ + + + ) : ( + + + + + + + + + + + + + {payOrders.map((item) => ( + + + + + + + + ))} + +
订单号时间金额状态
{item.orderId} + {item.createTime ? dayjs(item.createTime).format('YYYY/MM/DD HH:mm:ss') : '-'} + {formatStorePrice2Read(item.price)}元{item.status} + {item.status === 'NOTPAY' && ( + + )} +
+
+ )} + +
+ ); +}; + +export default PayRecordTable; diff --git a/projects/app/src/pages/account/components/UsageDetail.tsx b/projects/app/src/pages/account/components/UsageDetail.tsx deleted file mode 100644 index d75cd5c2c..000000000 --- a/projects/app/src/pages/account/components/UsageDetail.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import React, { useMemo } from 'react'; -import { - ModalBody, - Flex, - Box, - Table, - Thead, - Tbody, - Tr, - Th, - Td, - TableContainer -} from '@chakra-ui/react'; -import { UsageItemType } from '@fastgpt/global/support/wallet/usage/type.d'; -import dayjs from 'dayjs'; -import { UsageSourceMap } from '@fastgpt/global/support/wallet/usage/constants'; -import MyModal from '@/components/MyModal'; -import { useTranslation } from 'next-i18next'; -import { formatNumber } from '@fastgpt/global/common/math/tools'; - -const UsageDetail = ({ usage, onClose }: { usage: UsageItemType; onClose: () => void }) => { - const { t } = useTranslation(); - const filterBillList = useMemo( - () => usage.list.filter((item) => item && item.moduleName), - [usage.list] - ); - - const { hasModel, hasCharsLen, hasDuration } = useMemo(() => { - let hasModel = false; - let hasCharsLen = false; - let hasDuration = false; - let hasDataLen = false; - - usage.list.forEach((item) => { - if (item.model !== undefined) { - hasModel = true; - } - - if (typeof item.charsLength === 'number') { - hasCharsLen = true; - } - if (typeof item.duration === 'number') { - hasDuration = true; - } - }); - - return { - hasModel, - hasCharsLen, - hasDuration, - hasDataLen - }; - }, [usage.list]); - - return ( - - - - {t('support.wallet.bill.Number')}: - {usage.id} - - - {t('support.wallet.usage.Time')}: - {dayjs(usage.time).format('YYYY/MM/DD HH:mm:ss')} - - - {t('support.wallet.usage.App name')}: - {t(usage.appName) || '-'} - - - {t('support.wallet.usage.Source')}: - {t(UsageSourceMap[usage.source]?.label)} - - - {t('support.wallet.usage.Total points')}: - {formatNumber(usage.totalPoints)} - - - - {t('support.wallet.usage.Bill Module')} - - - - - - - {hasModel && } - {hasCharsLen && } - {hasDuration && } - - - - - - {filterBillList.map((item, i) => ( - - - {hasModel && } - {hasCharsLen && } - {hasDuration && } - - - ))} - -
{t('support.wallet.usage.Module name')}{t('support.wallet.usage.Ai model')}{t('support.wallet.usage.Text Length')}{t('support.wallet.usage.Duration')}{t('support.wallet.usage.Total points')}
{t(item.moduleName)}{item.model ?? '-'}{item.charsLength ?? '-'}{item.duration ?? '-'}{formatNumber(item.amount)}
-
-
-
-
- ); -}; - -export default UsageDetail; diff --git a/projects/app/src/pages/account/components/UsageTable.tsx b/projects/app/src/pages/account/components/UsageTable.tsx deleted file mode 100644 index d73f779de..000000000 --- a/projects/app/src/pages/account/components/UsageTable.tsx +++ /dev/null @@ -1,189 +0,0 @@ -import React, { useEffect, useMemo, useState } from 'react'; -import { - Table, - Thead, - Tbody, - Tr, - Th, - Td, - TableContainer, - Flex, - Box, - Button -} from '@chakra-ui/react'; -import { UsageSourceEnum, UsageSourceMap } from '@fastgpt/global/support/wallet/usage/constants'; -import { getUserUsages } from '@/web/support/wallet/usage/api'; -import type { UsageItemType } from '@fastgpt/global/support/wallet/usage/type'; -import { usePagination } from '@/web/common/hooks/usePagination'; -import { useLoading } from '@/web/common/hooks/useLoading'; -import dayjs from 'dayjs'; -import MyIcon from '@fastgpt/web/components/common/Icon'; -import DateRangePicker, { type DateRangeType } from '@/components/DateRangePicker'; -import { addDays } from 'date-fns'; -import dynamic from 'next/dynamic'; -import { useSystemStore } from '@/web/common/system/useSystemStore'; -import { useTranslation } from 'next-i18next'; -import MySelect from '@/components/Select'; -import { useQuery } from '@tanstack/react-query'; -import { useUserStore } from '@/web/support/user/useUserStore'; -import { getTeamMembers } from '@/web/support/user/team/api'; -import Avatar from '@/components/Avatar'; -import { formatNumber } from '../../../../../../packages/global/common/math/tools'; -const UsageDetail = dynamic(() => import('./UsageDetail')); - -const UsageTable = () => { - const { t } = useTranslation(); - const { Loading } = useLoading(); - const [dateRange, setDateRange] = useState({ - from: addDays(new Date(), -7), - to: new Date() - }); - const [usageSource, setUsageSource] = useState<`${UsageSourceEnum}` | ''>(''); - const { isPc } = useSystemStore(); - const { userInfo } = useUserStore(); - const [usageDetail, setUsageDetail] = useState(); - - const sourceList = useMemo( - () => [ - { label: t('common.All'), value: '' }, - ...Object.entries(UsageSourceMap).map(([key, value]) => ({ - label: t(value.label), - value: key - })) - ], - [t] - ); - - const [selectTmbId, setSelectTmbId] = useState(userInfo?.team?.tmbId); - const { data: members = [] } = useQuery(['getMembers', userInfo?.team?.teamId], () => { - if (!userInfo?.team?.teamId) return []; - return getTeamMembers(userInfo.team.teamId); - }); - const tmbList = useMemo( - () => - members.map((item) => ({ - label: ( - - - {item.memberName} - - ), - value: item.tmbId - })), - [members] - ); - - const { - data: usages, - isLoading, - Pagination, - getData - } = usePagination({ - api: getUserUsages, - pageSize: isPc ? 20 : 10, - params: { - dateStart: dateRange.from || new Date(), - dateEnd: addDays(dateRange.to || new Date(), 1), - source: usageSource, - teamMemberId: selectTmbId - }, - defaultRequest: false - }); - - useEffect(() => { - getData(1); - }, [usageSource, selectTmbId]); - - return ( - - - {tmbList.length > 1 && userInfo?.team?.canWrite && ( - - - {t('support.user.team.member')} - - - - )} - - - getData(1)} - /> - - - - - - - - {/* */} - - - - - - - - - {usages.map((item) => ( - - {/* */} - - - - - - - ))} - -
{t('user.team.Member Name')}{t('user.Time')} - { - setUsageSource(e); - }} - w={'130px'} - > - {t('user.Application Name')}{t('support.wallet.usage.Total points')}
{item.memberName}{dayjs(item.time).format('YYYY/MM/DD HH:mm:ss')}{t(UsageSourceMap[item.source]?.label) || '-'}{t(item.appName) || '-'}{formatNumber(item.totalPoints) || 0} - -
-
- - {!isLoading && usages.length === 0 && ( - - - - 无使用记录~ - - - )} - - - {!!usageDetail && ( - setUsageDetail(undefined)} /> - )} -
- ); -}; - -export default React.memo(UsageTable); diff --git a/projects/app/src/pages/account/components/standardDetailModal.tsx b/projects/app/src/pages/account/components/standardDetailModal.tsx deleted file mode 100644 index 2c313980b..000000000 --- a/projects/app/src/pages/account/components/standardDetailModal.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react'; -import { ModalBody, Box, Flex, Input, ModalFooter, Button } from '@chakra-ui/react'; -import MyModal from '@/components/MyModal'; -import { useTranslation } from 'next-i18next'; -import { useForm } from 'react-hook-form'; -import { useRequest } from '@/web/common/hooks/useRequest'; -import type { UserType } from '@fastgpt/global/support/user/type.d'; - -const StandDetailModal = ({ onClose }: { onClose: () => void }) => { - const { t } = useTranslation(); - - return ( - - - - - ); -}; - -export default StandDetailModal; diff --git a/projects/app/src/pages/account/index.tsx b/projects/app/src/pages/account/index.tsx index 49ce40a7a..774a41562 100644 --- a/projects/app/src/pages/account/index.tsx +++ b/projects/app/src/pages/account/index.tsx @@ -14,16 +14,18 @@ import { useTranslation } from 'next-i18next'; import Script from 'next/script'; const Promotion = dynamic(() => import('./components/Promotion')); -const UsageTable = dynamic(() => import('./components/UsageTable')); const BillTable = dynamic(() => import('./components/BillTable')); +const PayRecordTable = dynamic(() => import('./components/PayRecordTable')); const InformTable = dynamic(() => import('./components/InformTable')); const ApiKeyTable = dynamic(() => import('./components/ApiKeyTable')); +const PriceBox = dynamic(() => import('@/components/support/wallet/Price')); enum TabEnum { 'info' = 'info', 'promotion' = 'promotion', - 'usage' = 'usage', 'bill' = 'bill', + 'price' = 'price', + 'pay' = 'pay', 'inform' = 'inform', 'apikey' = 'apikey', 'loginout' = 'loginout' @@ -43,18 +45,27 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => { ...(feConfigs?.isPlus ? [ { - icon: 'support/usage/usageRecordLight', + icon: 'support/bill/billRecordLight', label: t('user.Usage Record'), - id: TabEnum.usage + id: TabEnum.bill } ] : []), ...(feConfigs?.show_pay && userInfo?.team.canWrite ? [ { - icon: 'support/bill/payRecordLight', - label: t('support.wallet.Bills'), - id: TabEnum.bill + icon: 'support/pay/payRecordLight', + label: t('user.Recharge Record'), + id: TabEnum.pay + } + ] + : []), + ...(feConfigs?.show_pay + ? [ + { + icon: 'support/pay/priceLight', + label: t('support.user.Price'), + id: TabEnum.price } ] : []), @@ -97,6 +108,11 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => { const { openConfirm, ConfirmModal } = useConfirm({ content: '确认退出登录?' }); + const { + isOpen: isOpenPriceBox, + onOpen: onOpenPriceBox, + onClose: onClosePriceBox + } = useDisclosure(); const router = useRouter(); const theme = useTheme(); @@ -108,6 +124,8 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => { setUserInfo(null); router.replace('/login'); })(); + } else if (tab === TabEnum.price) { + onOpenPriceBox(); } else { router.replace({ query: { @@ -116,7 +134,7 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => { }); } }, - [openConfirm, router, setUserInfo] + [onOpenPriceBox, openConfirm, router, setUserInfo] ); return ( @@ -160,14 +178,16 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => { {currentTab === TabEnum.info && } {currentTab === TabEnum.promotion && } - {currentTab === TabEnum.usage && } {currentTab === TabEnum.bill && } + {currentTab === TabEnum.pay && } {currentTab === TabEnum.inform && } {currentTab === TabEnum.apikey && } + + {isOpenPriceBox && } ); }; diff --git a/projects/app/src/pages/api/admin/initv468.ts b/projects/app/src/pages/api/admin/initv468.ts deleted file mode 100644 index 02caa11f1..000000000 --- a/projects/app/src/pages/api/admin/initv468.ts +++ /dev/null @@ -1,99 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from 'next'; -import { jsonRes } from '@fastgpt/service/common/response'; -import { connectToDatabase } from '@/service/mongo'; -import { authCert } from '@fastgpt/service/support/permission/auth/common'; -import { PgClient } from '@fastgpt/service/common/vectorStore/pg'; -import { PgDatasetTableName } from '@fastgpt/global/common/vectorStore/constants'; -import { MongoImage } from '@fastgpt/service/common/file/image/schema'; -import { MongoImageSchemaType } from '@fastgpt/global/common/file/image/type'; -import { delay } from '@fastgpt/global/common/system/utils'; -import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema'; -import { getNanoid } from '@fastgpt/global/common/string/tools'; -import { MongoApp } from '@fastgpt/service/core/app/schema'; -import { ModuleItemType } from '@fastgpt/global/core/module/type'; -import { DYNAMIC_INPUT_KEY, ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants'; - -let success = 0; -let deleteImg = 0; - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - try { - // 设置所有app为 inited = false - const result = await MongoApp.updateMany({}, { $set: { inited: false } }); - console.log(result); - - await initApp(); - - jsonRes(res, { - message: 'success' - }); - } catch (error) { - console.log(error); - - jsonRes(res, { - code: 500, - error - }); - } -} - -const systemKeys: string[] = [ - ModuleInputKeyEnum.switch, - ModuleInputKeyEnum.httpMethod, - ModuleInputKeyEnum.httpReqUrl, - ModuleInputKeyEnum.httpHeaders, - DYNAMIC_INPUT_KEY, - ModuleInputKeyEnum.addInputParam -]; -const initApp = async (): Promise => { - const app = await MongoApp.findOne({ inited: false }).sort({ updateTime: -1 }); - if (!app) { - return; - } - - try { - const modules = JSON.parse(JSON.stringify(app.modules)) as ModuleItemType[]; - let update = false; - // 找到http模块 - modules.forEach((module) => { - if (module.flowType === 'httpRequest') { - const method = module.inputs.find((input) => input.key === ModuleInputKeyEnum.httpMethod); - if (method?.value === 'POST') { - module.inputs.forEach((input) => { - // 更新非系统字段的key - if (!systemKeys.includes(input.key)) { - // 更新output的target - modules.forEach((item) => { - item.outputs.forEach((output) => { - output.targets.forEach((target) => { - if (target.moduleId === module.moduleId && target.key === input.key) { - target.key = `data.${input.key}`; - } - }); - }); - }); - // 更新key - input.key = `data.${input.key}`; - update = true; - } - }); - } - } - }); - - if (update) { - console.log('update http app'); - app.modules = modules; - } - app.inited = true; - await app.save(); - - console.log(++success); - return initApp(); - } catch (error) { - console.log(error); - - await delay(1000); - return initApp(); - } -}; diff --git a/projects/app/src/pages/api/admin/initv469.ts b/projects/app/src/pages/api/admin/initv469.ts deleted file mode 100644 index 54bf086ca..000000000 --- a/projects/app/src/pages/api/admin/initv469.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from 'next'; -import { jsonRes } from '@fastgpt/service/common/response'; -import { connectToDatabase } from '@/service/mongo'; -import { authCert } from '@fastgpt/service/support/permission/auth/common'; -import { MongoUsage } from '@fastgpt/service/support/wallet/usage/schema'; -import { connectionMongo } from '@fastgpt/service/common/mongo'; - -/* pg 中的数据搬到 mongo dataset.datas 中,并做映射 */ -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - try { - await connectToDatabase(); - await authCert({ req, authRoot: true }); - - // 检查 usage 是否有记录 - const totalUsages = await MongoUsage.countDocuments(); - if (totalUsages === 0) { - // 重命名 bills 集合成 usages - await connectionMongo.connection.db.renameCollection('bills', 'usages', { - // 强制 - dropTarget: true - }); - } - - jsonRes(res, { - message: 'success' - }); - } catch (error) { - console.log(error); - - jsonRes(res, { - code: 500, - error - }); - } -} diff --git a/projects/app/src/pages/api/common/file/upload.ts b/projects/app/src/pages/api/common/file/upload.ts index efa9b2ad7..3f3d135e5 100644 --- a/projects/app/src/pages/api/common/file/upload.ts +++ b/projects/app/src/pages/api/common/file/upload.ts @@ -16,7 +16,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< let filePaths: string[] = []; try { - const { teamId, tmbId } = await authCert({ req, authToken: true }); + const { userId, teamId, tmbId } = await authCert({ req, authToken: true }); const { file, bucketName, metadata } = await upload.doUpload(req, res); diff --git a/projects/app/src/pages/api/core/ai/agent/createQuestionGuide.ts b/projects/app/src/pages/api/core/ai/agent/createQuestionGuide.ts index a51329085..722c4983d 100644 --- a/projects/app/src/pages/api/core/ai/agent/createQuestionGuide.ts +++ b/projects/app/src/pages/api/core/ai/agent/createQuestionGuide.ts @@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@fastgpt/service/common/response'; import { connectToDatabase } from '@/service/mongo'; import type { CreateQuestionGuideParams } from '@/global/core/ai/api.d'; -import { pushQuestionGuideUsage } from '@/service/support/wallet/usage/push'; +import { pushQuestionGuideBill } from '@/service/support/wallet/bill/push'; import { createQuestionGuide } from '@fastgpt/service/core/ai/functions/createQuestionGuide'; import { authCertOrShareId } from '@fastgpt/service/support/permission/auth/common'; @@ -19,7 +19,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< const qgModel = global.llmModels[0]; - const { result, charsLength } = await createQuestionGuide({ + const { result, inputTokens, outputTokens } = await createQuestionGuide({ messages, model: qgModel.model }); @@ -28,8 +28,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< data: result }); - pushQuestionGuideUsage({ - charsLength, + pushQuestionGuideBill({ + inputTokens, + outputTokens, teamId, tmbId }); diff --git a/projects/app/src/pages/api/core/app/create.ts b/projects/app/src/pages/api/core/app/create.ts index dadfbedb7..26e3bfe24 100644 --- a/projects/app/src/pages/api/core/app/create.ts +++ b/projects/app/src/pages/api/core/app/create.ts @@ -6,7 +6,6 @@ import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { MongoApp } from '@fastgpt/service/core/app/schema'; import { authUserNotVisitor } from '@fastgpt/service/support/permission/auth/user'; import { SimpleModeTemplate_FastGPT_Universal } from '@/global/core/app/constants'; -import { checkTeamAppLimit } from '@/service/support/permission/teamLimit'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -26,7 +25,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< const { teamId, tmbId } = await authUserNotVisitor({ req, authToken: true }); // 上限校验 - await checkTeamAppLimit(teamId); + const authCount = await MongoApp.countDocuments({ + teamId + }); + if (authCount >= 50) { + throw new Error('每个团队上限 50 个应用'); + } // 创建模型 const response = await MongoApp.create({ diff --git a/projects/app/src/pages/api/core/app/data/totalUsage.ts b/projects/app/src/pages/api/core/app/data/totalUsage.ts new file mode 100644 index 000000000..137c2edaa --- /dev/null +++ b/projects/app/src/pages/api/core/app/data/totalUsage.ts @@ -0,0 +1,51 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import { jsonRes } from '@fastgpt/service/common/response'; +import { connectToDatabase } from '@/service/mongo'; +import { authCert } from '@fastgpt/service/support/permission/auth/common'; +import { Types } from '@fastgpt/service/common/mongo'; +import { MongoBill } from '@fastgpt/service/support/wallet/bill/schema'; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + try { + await connectToDatabase(); + const { appId, start, end } = req.body as { appId: string; start: number; end: number }; + const { userId } = await authCert({ req, authToken: true }); + + const result = await MongoBill.aggregate([ + { + $match: { + appId: new Types.ObjectId(appId), + userId: new Types.ObjectId(userId), + time: { $gte: new Date(start) } + } + }, + { + $group: { + _id: { + year: { $year: '$time' }, + month: { $month: '$time' }, + day: { $dayOfMonth: '$time' } + }, + total: { $sum: '$total' } + } + }, + { + $project: { + _id: 0, + date: { $dateFromParts: { year: '$_id.year', month: '$_id.month', day: '$_id.day' } }, + total: 1 + } + }, + { $sort: { date: 1 } } + ]); + + jsonRes(res, { + data: result + }); + } catch (err) { + jsonRes(res, { + code: 500, + error: err + }); + } +} diff --git a/projects/app/src/pages/api/core/app/form2Modules/fastgpt-simple.ts b/projects/app/src/pages/api/core/app/form2Modules/fastgpt-simple.ts index 320a12b69..3a23b4842 100644 --- a/projects/app/src/pages/api/core/app/form2Modules/fastgpt-simple.ts +++ b/projects/app/src/pages/api/core/app/form2Modules/fastgpt-simple.ts @@ -92,7 +92,7 @@ function simpleChatTemplate({ formData, maxToken }: Props): ModuleItemType[] { }, { key: 'model', - type: 'selectLLMModel', + type: 'selectChatModel', label: 'core.module.input.label.aiModel', required: true, valueType: 'string', @@ -471,7 +471,7 @@ function datasetTemplate({ formData, maxToken }: Props): ModuleItemType[] { }, { key: 'model', - type: 'selectLLMModel', + type: 'selectChatModel', label: 'core.module.input.label.aiModel', required: true, valueType: 'string', diff --git a/projects/app/src/pages/api/core/app/form2Modules/fastgpt-universal.ts b/projects/app/src/pages/api/core/app/form2Modules/fastgpt-universal.ts index 9f4c706cb..8b9a18263 100644 --- a/projects/app/src/pages/api/core/app/form2Modules/fastgpt-universal.ts +++ b/projects/app/src/pages/api/core/app/form2Modules/fastgpt-universal.ts @@ -88,7 +88,7 @@ function simpleChatTemplate(formData: AppSimpleEditFormType): ModuleItemType[] { }, { key: 'model', - type: 'selectLLMModel', + type: 'selectChatModel', label: 'core.module.input.label.aiModel', required: true, valueType: 'string', @@ -498,7 +498,7 @@ function datasetTemplate(formData: AppSimpleEditFormType): ModuleItemType[] { }, { key: 'model', - type: 'selectLLMModel', + type: 'selectChatModel', label: 'core.module.input.label.aiModel', required: true, valueType: 'string', diff --git a/projects/app/src/pages/api/core/app/update.ts b/projects/app/src/pages/api/core/app/update.ts index 01498ab5f..d8c00fb3f 100644 --- a/projects/app/src/pages/api/core/app/update.ts +++ b/projects/app/src/pages/api/core/app/update.ts @@ -12,7 +12,7 @@ import { getLLMModel } from '@/service/core/ai/model'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { await connectToDatabase(); - const { name, avatar, type, simpleTemplateId, intro, modules, permission, teamTags } = + const { name, avatar, type, simpleTemplateId, intro, modules, permission } = req.body as AppUpdateParams; const { appId } = req.query as { appId: string }; @@ -65,7 +65,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< avatar, intro, permission, - teamTags: teamTags, ...(modules && { modules }) diff --git a/projects/app/src/pages/api/core/app/updateTeamTasg.ts b/projects/app/src/pages/api/core/app/updateTeamTasg.ts deleted file mode 100644 index 8b3a750b9..000000000 --- a/projects/app/src/pages/api/core/app/updateTeamTasg.ts +++ /dev/null @@ -1,82 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from 'next'; -import { jsonRes } from '@fastgpt/service/common/response'; -import { connectToDatabase } from '@/service/mongo'; -import { MongoApp } from '@fastgpt/service/core/app/schema'; -import type { AppUpdateParams } from '@fastgpt/global/core/app/api'; -import { authApp } from '@fastgpt/service/support/permission/auth/app'; -import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant'; -import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants'; -import { getLLMModel } from '@/service/core/ai/model'; - -/* 获取我的模型 */ -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - try { - await connectToDatabase(); - const { name, avatar, type, simpleTemplateId, intro, modules, permission, teamTags } = - req.body as AppUpdateParams; - const { appId } = req.query as { appId: string }; - - if (!appId) { - throw new Error('appId is empty'); - } - - // 凭证校验 - await authApp({ req, authToken: true, appId, per: permission ? 'owner' : 'w' }); - - // check modules - // 1. dataset search limit, less than model quoteMaxToken - if (modules) { - let maxTokens = 3000; - - modules.forEach((item) => { - if (item.flowType === FlowNodeTypeEnum.chatNode) { - const model = - item.inputs.find((item) => item.key === ModuleInputKeyEnum.aiModel)?.value || ''; - const chatModel = getLLMModel(model); - const quoteMaxToken = chatModel.quoteMaxToken || 3000; - - maxTokens = Math.max(maxTokens, quoteMaxToken); - } - }); - - modules.forEach((item) => { - if (item.flowType === FlowNodeTypeEnum.datasetSearchNode) { - item.inputs.forEach((input) => { - if (input.key === ModuleInputKeyEnum.datasetMaxTokens) { - const val = input.value as number; - if (val > maxTokens) { - input.value = maxTokens; - } - } - }); - } - }); - } - - // 更新模型 - await MongoApp.findOneAndUpdate( - { - _id: appId - }, - { - name, - type, - simpleTemplateId, - avatar, - intro, - permission, - teamTags: teamTags, - ...(modules && { - modules - }) - } - ); - - jsonRes(res); - } catch (err) { - jsonRes(res, { - code: 500, - error: err - }); - } -} diff --git a/projects/app/src/pages/api/core/chat/chatTest.ts b/projects/app/src/pages/api/core/chat/chatTest.ts index bee667a46..f9b7aeddc 100644 --- a/projects/app/src/pages/api/core/chat/chatTest.ts +++ b/projects/app/src/pages/api/core/chat/chatTest.ts @@ -4,13 +4,13 @@ import { sseErrRes } from '@fastgpt/service/common/response'; import { sseResponseEventEnum } from '@fastgpt/service/common/response/constant'; import { responseWrite } from '@fastgpt/service/common/response'; import type { ModuleItemType } from '@fastgpt/global/core/module/type.d'; -import { pushChatUsage } from '@/service/support/wallet/usage/push'; -import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants'; +import { pushChatBill } from '@/service/support/wallet/bill/push'; +import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants'; import type { ChatItemType } from '@fastgpt/global/core/chat/type'; import { authApp } from '@fastgpt/service/support/permission/auth/app'; import { dispatchModules } from '@/service/moduleDispatch'; import { authCert } from '@fastgpt/service/support/permission/auth/common'; -import { getUserChatInfoAndAuthTeamPoints } from '@/service/support/permission/auth/team'; +import { getUserAndAuthBalance } from '@fastgpt/service/support/user/controller'; export type Props = { history: ChatItemType[]; @@ -50,10 +50,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) ]); // auth balance - const { user } = await getUserChatInfoAndAuthTeamPoints(tmbId); + const user = await getUserAndAuthBalance({ + tmbId, + minBalance: 0 + }); /* start process */ - const { responseData, moduleDispatchBills } = await dispatchModules({ + const { responseData } = await dispatchModules({ res, mode: 'test', teamId, @@ -82,13 +85,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) }); res.end(); - pushChatUsage({ + pushChatBill({ appName, appId, teamId, tmbId, - source: UsageSourceEnum.fastgpt, - moduleDispatchBills + source: BillSourceEnum.fastgpt, + response: responseData }); } catch (err: any) { res.status(500); diff --git a/projects/app/src/pages/api/core/chat/clearHistories.ts b/projects/app/src/pages/api/core/chat/clearHistories.ts index 8a5424053..99947245f 100644 --- a/projects/app/src/pages/api/core/chat/clearHistories.ts +++ b/projects/app/src/pages/api/core/chat/clearHistories.ts @@ -14,13 +14,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) await connectToDatabase(); const { appId, shareId, outLinkUid } = req.query as ClearHistoriesProps; - let chatAppId = appId; - const match = await (async () => { if (shareId && outLinkUid) { - const { appId, uid } = await authOutLink({ shareId, outLinkUid }); + const { uid } = await authOutLink({ shareId, outLinkUid }); - chatAppId = appId; return { shareId, outLinkUid: uid @@ -44,11 +41,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) const idList = list.map((item) => item.chatId); await MongoChatItem.deleteMany({ - appId: chatAppId, + appId, chatId: { $in: idList } }); await MongoChat.deleteMany({ - appId: chatAppId, + appId, chatId: { $in: idList } }); diff --git a/projects/app/src/pages/api/core/chat/getHistories.ts b/projects/app/src/pages/api/core/chat/getHistories.ts index 97ca87fc6..c81f8cae6 100644 --- a/projects/app/src/pages/api/core/chat/getHistories.ts +++ b/projects/app/src/pages/api/core/chat/getHistories.ts @@ -28,13 +28,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) } }; } - if (appId && outLinkUid) { - return { - shareId, - outLinkUid: outLinkUid, - source: ChatSourceEnum.team - }; - } if (appId) { const { tmbId } = await authCert({ req, authToken: true }); return { @@ -43,7 +36,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) source: ChatSourceEnum.online }; } - return Promise.reject('Params are error'); })(); diff --git a/projects/app/src/pages/api/core/chat/item/getSpeech.ts b/projects/app/src/pages/api/core/chat/item/getSpeech.ts index eb7db258c..17d9a647b 100644 --- a/projects/app/src/pages/api/core/chat/item/getSpeech.ts +++ b/projects/app/src/pages/api/core/chat/item/getSpeech.ts @@ -3,9 +3,9 @@ import { jsonRes } from '@fastgpt/service/common/response'; import { connectToDatabase } from '@/service/mongo'; import { GetChatSpeechProps } from '@/global/core/chat/api.d'; import { text2Speech } from '@fastgpt/service/core/ai/audio/speech'; -import { pushAudioSpeechUsage } from '@/service/support/wallet/usage/push'; +import { pushAudioSpeechBill } from '@/service/support/wallet/bill/push'; import { authCertOrShareId } from '@fastgpt/service/support/permission/auth/common'; -import { authType2UsageSource } from '@/service/support/wallet/usage/utils'; +import { authType2BillSource } from '@/service/support/wallet/bill/utils'; import { getAudioSpeechModel } from '@/service/core/ai/model'; import { MongoTTSBuffer } from '@fastgpt/service/common/buffer/tts/schema'; @@ -54,12 +54,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) speed: ttsConfig.speed, onSuccess: async ({ model, buffer }) => { try { - pushAudioSpeechUsage({ + pushAudioSpeechBill({ model: model, charsLength: input.length, tmbId, teamId, - source: authType2UsageSource({ authType }) + source: authType2BillSource({ authType }) }); await MongoTTSBuffer.create({ diff --git a/projects/app/src/pages/api/core/chat/outLink/getInforByTeamId.ts b/projects/app/src/pages/api/core/chat/outLink/getInforByTeamId.ts deleted file mode 100644 index 224d9a074..000000000 --- a/projects/app/src/pages/api/core/chat/outLink/getInforByTeamId.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from 'next'; -import { jsonRes } from '@fastgpt/service/common/response'; -import { connectToDatabase } from '@/service/mongo'; -import type { chatByTeamProps } from '@/global/core/chat/api.d'; -import axios from 'axios'; -import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema'; -import { getChatItems } from '@fastgpt/service/core/chat/controller'; -import { selectShareResponse } from '@/utils/service/core/chat'; -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - try { - await connectToDatabase(); - - let { teamId, appId, outLinkUid } = req.query as chatByTeamProps; - - const history = await MongoChatItem.find({ - appId: appId, - outLinkUid: outLinkUid, - teamId: teamId - }); - - jsonRes(res, { - data: history - }); - } catch (err) { - jsonRes(res, { - code: 500, - data: req.query, - error: err - }); - } -} - -export const config = { - api: { - responseLimit: '10mb' - } -}; diff --git a/projects/app/src/pages/api/core/chat/team/init.ts b/projects/app/src/pages/api/core/chat/team/init.ts deleted file mode 100644 index 47b5f0e6f..000000000 --- a/projects/app/src/pages/api/core/chat/team/init.ts +++ /dev/null @@ -1,91 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from 'next'; -import { jsonRes } from '@fastgpt/service/common/response'; -import { connectToDatabase } from '@/service/mongo'; -import { getGuideModule } from '@fastgpt/global/core/module/utils'; -import { getChatModelNameListByModules } from '@/service/core/app/module'; -import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants'; -import type { InitChatProps, InitChatResponse } from '@/global/core/chat/api.d'; -import { MongoChat } from '@fastgpt/service/core/chat/chatSchema'; -import { MongoApp } from '@fastgpt/service/core/app/schema'; -import { getChatItems } from '@fastgpt/service/core/chat/controller'; -import { AppErrEnum } from '@fastgpt/global/common/error/code/app'; - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - try { - await connectToDatabase(); - - let { appId, chatId, outLinkUid } = req.query as { - chatId?: string; - appId?: string; - outLinkUid?: string; - }; - - if (!appId) { - return jsonRes(res, { - code: 501, - message: "You don't have an app yet" - }); - } - - // auth app permission - const [chat, app] = await Promise.all([ - // authApp({ - // req, - // authToken: false, - // appId, - // per: 'r' - // }), - chatId ? MongoChat.findOne({ appId, chatId }) : undefined, - MongoApp.findById(appId).lean() - ]); - if (!app) { - throw new Error(AppErrEnum.unExist); - } - - // auth chat permission - // if (chat && chat.outLinkUid !== outLinkUid) { - // throw new Error(ChatErrEnum.unAuthChat); - // } - // // auth chat permission - // if (chat && !app.canWrite && String(tmbId) !== String(chat?.tmbId)) { - // throw new Error(ChatErrEnum.unAuthChat); - // } - - // get app and history - const { history } = await getChatItems({ - appId, - chatId, - limit: 30, - field: `dataId obj value adminFeedback userBadFeedback userGoodFeedback ${ModuleOutputKeyEnum.responseData}` - }); - - jsonRes(res, { - data: { - chatId, - appId, - title: chat?.title || '新对话', - userAvatar: undefined, - variables: chat?.variables || {}, - history, - app: { - userGuideModule: getGuideModule(app.modules), - chatModels: getChatModelNameListByModules(app.modules), - name: app.name, - avatar: app.avatar, - intro: app.intro - } - } - }); - } catch (err) { - jsonRes(res, { - code: 500, - error: err - }); - } -} - -export const config = { - api: { - responseLimit: '10mb' - } -}; diff --git a/projects/app/src/pages/api/core/chat/teamInit.ts b/projects/app/src/pages/api/core/chat/teamInit.ts deleted file mode 100644 index a3087e15c..000000000 --- a/projects/app/src/pages/api/core/chat/teamInit.ts +++ /dev/null @@ -1,81 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from 'next'; -import { jsonRes } from '@fastgpt/service/common/response'; -import { connectToDatabase } from '@/service/mongo'; -import { authApp } from '@fastgpt/service/support/permission/auth/app'; -import { getGuideModule } from '@fastgpt/global/core/module/utils'; -import { getChatModelNameListByModules } from '@/service/core/app/module'; -import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants'; -import type { InitChatProps, InitChatResponse } from '@/global/core/chat/api.d'; -import { MongoChat } from '@fastgpt/service/core/chat/chatSchema'; -import { getChatItems } from '@fastgpt/service/core/chat/controller'; -import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat'; - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - try { - await connectToDatabase(); - - let { appId, chatId, loadCustomFeedbacks } = req.query as InitChatProps; - - if (!appId) { - return jsonRes(res, { - code: 501, - message: "You don't have an app yet" - }); - } - - // auth app permission - const [{ app, tmbId }, chat] = await Promise.all([ - authApp({ - req, - authToken: true, - appId, - per: 'r' - }), - chatId ? MongoChat.findOne({ appId, chatId }) : undefined - ]); - - // // auth chat permission - // if (chat && !app.canWrite && String(tmbId) !== String(chat?.tmbId)) { - // throw new Error(ChatErrEnum.unAuthChat); - // } - - // get app and history - const { history } = await getChatItems({ - appId, - chatId, - limit: 30, - field: `dataId obj value adminFeedback userBadFeedback userGoodFeedback ${ - ModuleOutputKeyEnum.responseData - } ${loadCustomFeedbacks ? 'customFeedbacks' : ''}` - }); - - jsonRes(res, { - data: { - chatId, - appId, - title: chat?.title || '新对话', - userAvatar: undefined, - variables: chat?.variables || {}, - history, - app: { - userGuideModule: getGuideModule(app.modules), - chatModels: getChatModelNameListByModules(app.modules), - name: app.name, - avatar: app.avatar, - intro: app.intro - } - } - }); - } catch (err) { - jsonRes(res, { - code: 500, - error: err - }); - } -} - -export const config = { - api: { - responseLimit: '10mb' - } -}; diff --git a/projects/app/src/pages/api/core/chat/updateHistory.ts b/projects/app/src/pages/api/core/chat/updateHistory.ts index 634604894..634336c9a 100644 --- a/projects/app/src/pages/api/core/chat/updateHistory.ts +++ b/projects/app/src/pages/api/core/chat/updateHistory.ts @@ -24,7 +24,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) await MongoChat.findOneAndUpdate( { appId, chatId }, { - updateTime: new Date(), ...(customTitle !== undefined && { customTitle }), ...(top !== undefined && { top }) } diff --git a/projects/app/src/pages/api/core/dataset/collection/create/link.ts b/projects/app/src/pages/api/core/dataset/collection/create/link.ts index 317bd3fa5..15f0ac26a 100644 --- a/projects/app/src/pages/api/core/dataset/collection/create/link.ts +++ b/projects/app/src/pages/api/core/dataset/collection/create/link.ts @@ -11,12 +11,13 @@ import { TrainingModeEnum, DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constants'; -import { checkDatasetLimit } from '@/service/support/permission/teamLimit'; +import { checkDatasetLimit } from '@fastgpt/service/support/permission/limit/dataset'; import { predictDataLimitLength } from '@fastgpt/global/core/dataset/utils'; -import { createTrainingUsage } from '@fastgpt/service/support/wallet/usage/controller'; -import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants'; +import { createTrainingBill } from '@fastgpt/service/support/wallet/bill/controller'; +import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants'; import { getLLMModel, getVectorModel } from '@/service/core/ai/model'; import { reloadCollectionChunks } from '@fastgpt/service/core/dataset/collection/utils'; +import { getStandardSubPlan } from '@/service/support/wallet/sub/utils'; import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { @@ -42,7 +43,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< // 1. check dataset limit await checkDatasetLimit({ teamId, - insertLen: predictDataLimitLength(trainingType, new Array(10)) + insertLen: predictDataLimitLength(trainingType, new Array(10)), + standardPlans: getStandardSubPlan() }); const { _id: collectionId } = await mongoSessionRun(async (session) => { @@ -64,11 +66,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< }); // 3. create bill and start sync - const { billId } = await createTrainingUsage({ + const { billId } = await createTrainingBill({ teamId, tmbId, appName: 'core.dataset.collection.Sync Collection', - billSource: UsageSourceEnum.training, + billSource: BillSourceEnum.training, vectorModel: getVectorModel(dataset.vectorModel).name, agentModel: getLLMModel(dataset.agentModel).name, session diff --git a/projects/app/src/pages/api/core/dataset/collection/create/text.ts b/projects/app/src/pages/api/core/dataset/collection/create/text.ts index 891baf297..b24a1724c 100644 --- a/projects/app/src/pages/api/core/dataset/collection/create/text.ts +++ b/projects/app/src/pages/api/core/dataset/collection/create/text.ts @@ -12,13 +12,14 @@ import { DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constants'; import { splitText2Chunks } from '@fastgpt/global/common/string/textSplitter'; -import { checkDatasetLimit } from '@/service/support/permission/teamLimit'; +import { checkDatasetLimit } from '@fastgpt/service/support/permission/limit/dataset'; import { predictDataLimitLength } from '@fastgpt/global/core/dataset/utils'; import { pushDataToTrainingQueue } from '@/service/core/dataset/data/controller'; import { hashStr } from '@fastgpt/global/common/string/tools'; -import { createTrainingUsage } from '@fastgpt/service/support/wallet/usage/controller'; -import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants'; +import { createTrainingBill } from '@fastgpt/service/support/wallet/bill/controller'; +import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants'; import { getLLMModel, getVectorModel } from '@/service/core/ai/model'; +import { getStandardSubPlan } from '@/service/support/wallet/sub/utils'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -52,7 +53,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< // 2. check dataset limit await checkDatasetLimit({ teamId, - insertLen: predictDataLimitLength(trainingType, chunks) + insertLen: predictDataLimitLength(trainingType, chunks), + standardPlans: getStandardSubPlan() }); // 3. create collection and training bill @@ -72,11 +74,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< hashRawText: hashStr(text), rawTextLength: text.length }), - createTrainingUsage({ + createTrainingBill({ teamId, tmbId, appName: name, - billSource: UsageSourceEnum.training, + billSource: BillSourceEnum.training, vectorModel: getVectorModel(dataset.vectorModel)?.name, agentModel: getLLMModel(dataset.agentModel)?.name }) diff --git a/projects/app/src/pages/api/core/dataset/collection/sync/link.ts b/projects/app/src/pages/api/core/dataset/collection/sync/link.ts index 0e93e43bd..7c0104c99 100644 --- a/projects/app/src/pages/api/core/dataset/collection/sync/link.ts +++ b/projects/app/src/pages/api/core/dataset/collection/sync/link.ts @@ -12,8 +12,8 @@ import { DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constants'; import { DatasetErrEnum } from '@fastgpt/global/common/error/code/dataset'; -import { createTrainingUsage } from '@fastgpt/service/support/wallet/usage/controller'; -import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants'; +import { createTrainingBill } from '@fastgpt/service/support/wallet/bill/controller'; +import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants'; import { getLLMModel, getVectorModel } from '@/service/core/ai/model'; import { createOneCollection } from '@fastgpt/service/core/dataset/collection/controller'; import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun'; @@ -56,11 +56,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< await mongoSessionRun(async (session) => { // create training bill - const { billId } = await createTrainingUsage({ + const { billId } = await createTrainingBill({ teamId: collection.teamId, tmbId, appName: 'core.dataset.collection.Sync Collection', - billSource: UsageSourceEnum.training, + billSource: BillSourceEnum.training, vectorModel: vectorModelData.name, agentModel: agentModelData.name, session diff --git a/projects/app/src/pages/api/core/dataset/create.ts b/projects/app/src/pages/api/core/dataset/create.ts index eef14e167..c1dccb8c1 100644 --- a/projects/app/src/pages/api/core/dataset/create.ts +++ b/projects/app/src/pages/api/core/dataset/create.ts @@ -7,7 +7,6 @@ import { createDefaultCollection } from '@fastgpt/service/core/dataset/collectio import { authUserNotVisitor } from '@fastgpt/service/support/permission/auth/user'; import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants'; import { getLLMModel, getVectorModel, getDatasetModel } from '@/service/core/ai/model'; -import { checkTeamDatasetLimit } from '@/service/support/permission/teamLimit'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -32,7 +31,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< } // check limit - await checkTeamDatasetLimit(teamId); + const authCount = await MongoDataset.countDocuments({ + teamId, + type: DatasetTypeEnum.dataset + }); + if (authCount >= 50) { + throw new Error('每个团队上限 50 个知识库'); + } const { _id } = await MongoDataset.create({ name, diff --git a/projects/app/src/pages/api/core/dataset/data/delete.ts b/projects/app/src/pages/api/core/dataset/data/delete.ts index 708f21ada..b945d3e56 100644 --- a/projects/app/src/pages/api/core/dataset/data/delete.ts +++ b/projects/app/src/pages/api/core/dataset/data/delete.ts @@ -3,7 +3,8 @@ import { jsonRes } from '@fastgpt/service/common/response'; import { withNextCors } from '@fastgpt/service/common/middle/cors'; import { connectToDatabase } from '@/service/mongo'; import { authDatasetData } from '@/service/support/permission/auth/dataset'; -import { deleteDatasetData } from '@/service/core/dataset/data/controller'; +import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema'; +import { deleteDatasetDataVector } from '@fastgpt/service/common/vectorStore/controller'; export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -25,7 +26,19 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex per: 'w' }); - await deleteDatasetData(datasetData); + // update mongo data update time + await MongoDatasetData.findByIdAndUpdate(dataId, { + updateTime: new Date() + }); + + // delete vector data + await deleteDatasetDataVector({ + teamId, + idList: datasetData.indexes.map((item) => item.dataId) + }); + + // delete mongo data + await MongoDatasetData.findByIdAndDelete(dataId); jsonRes(res, { data: 'success' diff --git a/projects/app/src/pages/api/core/dataset/data/insertData.ts b/projects/app/src/pages/api/core/dataset/data/insertData.ts index 7ca9773ea..a8ec06bf1 100644 --- a/projects/app/src/pages/api/core/dataset/data/insertData.ts +++ b/projects/app/src/pages/api/core/dataset/data/insertData.ts @@ -12,10 +12,12 @@ import { hasSameValue } from '@/service/core/dataset/data/utils'; import { insertData2Dataset } from '@/service/core/dataset/data/controller'; import { authDatasetCollection } from '@fastgpt/service/support/permission/auth/dataset'; import { getCollectionWithDataset } from '@fastgpt/service/core/dataset/controller'; -import { pushGenerateVectorUsage } from '@/service/support/wallet/usage/push'; +import { authTeamBalance } from '@/service/support/permission/auth/bill'; +import { pushGenerateVectorBill } from '@/service/support/wallet/bill/push'; import { InsertOneDatasetDataProps } from '@/global/core/dataset/api'; import { simpleText } from '@fastgpt/global/common/string/tools'; -import { checkDatasetLimit } from '@/service/support/permission/teamLimit'; +import { checkDatasetLimit } from '@fastgpt/service/support/permission/limit/dataset'; +import { getStandardSubPlan } from '@/service/support/wallet/sub/utils'; export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -41,7 +43,8 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex await checkDatasetLimit({ teamId, - insertLen: 1 + insertLen: 1, + standardPlans: getStandardSubPlan() }); // auth collection and get dataset @@ -49,7 +52,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex { datasetId: { _id: datasetId, vectorModel } } - ] = await Promise.all([getCollectionWithDataset(collectionId)]); + ] = await Promise.all([getCollectionWithDataset(collectionId), authTeamBalance(teamId)]); // format data const formatQ = simpleText(q); @@ -87,7 +90,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex indexes: formatIndexes }); - pushGenerateVectorUsage({ + pushGenerateVectorBill({ teamId, tmbId, charsLength, diff --git a/projects/app/src/pages/api/core/dataset/data/pushData.ts b/projects/app/src/pages/api/core/dataset/data/pushData.ts index 476cd4532..8e50ef333 100644 --- a/projects/app/src/pages/api/core/dataset/data/pushData.ts +++ b/projects/app/src/pages/api/core/dataset/data/pushData.ts @@ -8,9 +8,10 @@ import type { PushDatasetDataResponse } from '@fastgpt/global/core/dataset/api.d'; import { authDatasetCollection } from '@fastgpt/service/support/permission/auth/dataset'; -import { checkDatasetLimit } from '@/service/support/permission/teamLimit'; +import { checkDatasetLimit } from '@fastgpt/service/support/permission/limit/dataset'; import { predictDataLimitLength } from '@fastgpt/global/core/dataset/utils'; import { pushDataToTrainingQueue } from '@/service/core/dataset/data/controller'; +import { getStandardSubPlan } from '@/service/support/wallet/sub/utils'; export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -37,7 +38,8 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex // auth dataset limit await checkDatasetLimit({ teamId, - insertLen: predictDataLimitLength(collection.trainingType, data) + insertLen: predictDataLimitLength(collection.trainingType, data), + standardPlans: getStandardSubPlan() }); jsonRes(res, { diff --git a/projects/app/src/pages/api/core/dataset/data/update.ts b/projects/app/src/pages/api/core/dataset/data/update.ts index 4cd8f5bfe..7f7102d4b 100644 --- a/projects/app/src/pages/api/core/dataset/data/update.ts +++ b/projects/app/src/pages/api/core/dataset/data/update.ts @@ -4,9 +4,9 @@ import { withNextCors } from '@fastgpt/service/common/middle/cors'; import { connectToDatabase } from '@/service/mongo'; import { updateData2Dataset } from '@/service/core/dataset/data/controller'; import { authDatasetData } from '@/service/support/permission/auth/dataset'; -import { pushGenerateVectorUsage } from '@/service/support/wallet/usage/push'; +import { authTeamBalance } from '@/service/support/permission/auth/bill'; +import { pushGenerateVectorBill } from '@/service/support/wallet/bill/push'; import { UpdateDatasetDataProps } from '@/global/core/dataset/api'; -import { checkDatasetLimit } from '@/service/support/permission/teamLimit'; export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -29,10 +29,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex }); // auth team balance - await checkDatasetLimit({ - teamId, - insertLen: 1 - }); + await authTeamBalance(teamId); const { charsLength } = await updateData2Dataset({ dataId: id, @@ -42,7 +39,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex model: vectorModel }); - pushGenerateVectorUsage({ + pushGenerateVectorBill({ teamId, tmbId, charsLength, diff --git a/projects/app/src/pages/api/core/dataset/searchTest.ts b/projects/app/src/pages/api/core/dataset/searchTest.ts index cce91385c..6358ba381 100644 --- a/projects/app/src/pages/api/core/dataset/searchTest.ts +++ b/projects/app/src/pages/api/core/dataset/searchTest.ts @@ -4,16 +4,14 @@ import { withNextCors } from '@fastgpt/service/common/middle/cors'; import type { SearchTestProps, SearchTestResponse } from '@/global/core/dataset/api.d'; import { connectToDatabase } from '@/service/mongo'; import { authDataset } from '@fastgpt/service/support/permission/auth/dataset'; -import { pushGenerateVectorUsage } from '@/service/support/wallet/usage/push'; +import { authTeamBalance } from '@/service/support/permission/auth/bill'; +import { pushGenerateVectorBill } from '@/service/support/wallet/bill/push'; import { searchDatasetData } from '@/service/core/dataset/data/controller'; import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools'; -import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants'; +import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants'; import { getLLMModel } from '@/service/core/ai/model'; +import { queryExtension } from '@fastgpt/service/core/ai/functions/queryExtension'; import { datasetSearchQueryExtension } from '@fastgpt/service/core/dataset/search/utils'; -import { - checkTeamAIPoints, - checkTeamReRankPermission -} from '@/service/support/permission/teamLimit'; export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -45,7 +43,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex per: 'r' }); // auth balance - await checkTeamAIPoints(teamId); + await authTeamBalance(teamId); // query extension const extensionModel = @@ -67,27 +65,28 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex similarity, datasetIds: [datasetId], searchMode, - usingReRank: usingReRank && (await checkTeamReRankPermission(teamId)) + usingReRank }); // push bill - const { totalPoints } = pushGenerateVectorUsage({ + const { total } = pushGenerateVectorBill({ teamId, tmbId, charsLength, model: dataset.vectorModel, - source: apikey ? UsageSourceEnum.api : UsageSourceEnum.fastgpt, + source: apikey ? BillSourceEnum.api : BillSourceEnum.fastgpt, ...(aiExtensionResult && extensionModel && { extensionModel: extensionModel.name, - extensionCharsLength: aiExtensionResult.charsLength + extensionInputTokens: aiExtensionResult.inputTokens, + extensionOutputTokens: aiExtensionResult.outputTokens }) }); if (apikey) { updateApiKeyUsage({ apikey, - totalPoints: totalPoints + usage: total }); } diff --git a/projects/app/src/pages/api/core/plugin/create.ts b/projects/app/src/pages/api/core/plugin/create.ts index b5fe5c26d..c0f4ab035 100644 --- a/projects/app/src/pages/api/core/plugin/create.ts +++ b/projects/app/src/pages/api/core/plugin/create.ts @@ -4,7 +4,6 @@ import { connectToDatabase } from '@/service/mongo'; import type { CreateOnePluginParams } from '@fastgpt/global/core/plugin/controller'; import { authUserNotVisitor } from '@fastgpt/service/support/permission/auth/user'; import { MongoPlugin } from '@fastgpt/service/core/plugin/schema'; -import { checkTeamPluginLimit } from '@/service/support/permission/teamLimit'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -12,8 +11,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< const { teamId, tmbId } = await authUserNotVisitor({ req, authToken: true }); const body = req.body as CreateOnePluginParams; - await checkTeamPluginLimit(teamId); - const { _id } = await MongoPlugin.create({ ...body, teamId, diff --git a/projects/app/src/pages/api/plugins/textEditor/index.ts b/projects/app/src/pages/api/plugins/textEditor/index.ts index 35ad8de80..c7aa2c06a 100644 --- a/projects/app/src/pages/api/plugins/textEditor/index.ts +++ b/projects/app/src/pages/api/plugins/textEditor/index.ts @@ -34,6 +34,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< }); const textResult = replaceVariable(text, obj); + res.json({ text: textResult }); diff --git a/projects/app/src/pages/api/support/user/account/update.ts b/projects/app/src/pages/api/support/user/account/update.ts index af55c441d..7b29daad3 100644 --- a/projects/app/src/pages/api/support/user/account/update.ts +++ b/projects/app/src/pages/api/support/user/account/update.ts @@ -5,7 +5,6 @@ import { authCert } from '@fastgpt/service/support/permission/auth/common'; import { UserUpdateParams } from '@/types/user'; import { getAIApi, openaiBaseUrl } from '@fastgpt/service/core/ai/config'; import { connectToDatabase } from '@/service/mongo'; -import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema'; /* update user info */ export default async function handler(req: NextApiRequest, res: NextApiResponse) { @@ -13,12 +12,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< await connectToDatabase(); const { avatar, timezone, openaiAccount } = req.body as UserUpdateParams; - const { tmbId } = await authCert({ req, authToken: true }); - const tmb = await MongoTeamMember.findById(tmbId); - if (!tmb) { - throw new Error('can not find it'); - } - const userId = tmb.userId; + const { userId } = await authCert({ req, authToken: true }); + // auth key if (openaiAccount?.key) { console.log('auth user openai key', openaiAccount?.key); diff --git a/projects/app/src/pages/api/support/user/account/updatePasswordByOld.ts b/projects/app/src/pages/api/support/user/account/updatePasswordByOld.ts index 86712d749..ad4eec2bf 100644 --- a/projects/app/src/pages/api/support/user/account/updatePasswordByOld.ts +++ b/projects/app/src/pages/api/support/user/account/updatePasswordByOld.ts @@ -3,7 +3,6 @@ import { jsonRes } from '@fastgpt/service/common/response'; import { authCert } from '@fastgpt/service/support/permission/auth/common'; import { MongoUser } from '@fastgpt/service/support/user/schema'; import { connectToDatabase } from '@/service/mongo'; -import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -14,12 +13,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< throw new Error('Params is missing'); } - const { tmbId } = await authCert({ req, authToken: true }); - const tmb = await MongoTeamMember.findById(tmbId); - if (!tmb) { - throw new Error('can not find it'); - } - const userId = tmb.userId; + const { userId } = await authCert({ req, authToken: true }); + // auth old password const user = await MongoUser.findOne({ _id: userId, diff --git a/projects/app/src/pages/api/support/user/team/limit/datasetSizeLimit.ts b/projects/app/src/pages/api/support/user/team/limit/datasetSizeLimit.ts index e7f9856f2..4d22ffec3 100644 --- a/projects/app/src/pages/api/support/user/team/limit/datasetSizeLimit.ts +++ b/projects/app/src/pages/api/support/user/team/limit/datasetSizeLimit.ts @@ -2,7 +2,8 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@fastgpt/service/common/response'; import { connectToDatabase } from '@/service/mongo'; import { authCert } from '@fastgpt/service/support/permission/auth/common'; -import { checkDatasetLimit } from '@/service/support/permission/teamLimit'; +import { checkDatasetLimit } from '@fastgpt/service/support/permission/limit/dataset'; +import { getStandardSubPlan } from '@/service/support/wallet/sub/utils'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -22,7 +23,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< await checkDatasetLimit({ teamId, - insertLen: numberSize + insertLen: numberSize, + standardPlans: getStandardSubPlan() }); jsonRes(res); diff --git a/projects/app/src/pages/api/support/wallet/usage/createTrainingUsage.ts b/projects/app/src/pages/api/support/wallet/bill/createTrainingBill.ts similarity index 68% rename from projects/app/src/pages/api/support/wallet/usage/createTrainingUsage.ts rename to projects/app/src/pages/api/support/wallet/bill/createTrainingBill.ts index 2cbc3d63b..fec583d69 100644 --- a/projects/app/src/pages/api/support/wallet/usage/createTrainingUsage.ts +++ b/projects/app/src/pages/api/support/wallet/bill/createTrainingBill.ts @@ -1,16 +1,16 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@fastgpt/service/common/response'; import { connectToDatabase } from '@/service/mongo'; -import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants'; -import { CreateTrainingUsageProps } from '@fastgpt/global/support/wallet/usage/api.d'; +import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants'; +import { CreateTrainingBillProps } from '@fastgpt/global/support/wallet/bill/api.d'; import { getLLMModel, getVectorModel } from '@/service/core/ai/model'; -import { createTrainingUsage } from '@fastgpt/service/support/wallet/usage/controller'; +import { createTrainingBill } from '@fastgpt/service/support/wallet/bill/controller'; import { authDataset } from '@fastgpt/service/support/permission/auth/dataset'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { await connectToDatabase(); - const { name, datasetId } = req.body as CreateTrainingUsageProps; + const { name, datasetId } = req.body as CreateTrainingBillProps; const { teamId, tmbId, dataset } = await authDataset({ req, @@ -20,11 +20,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) per: 'w' }); - const { billId } = await createTrainingUsage({ + const { billId } = await createTrainingBill({ teamId, tmbId, appName: name, - billSource: UsageSourceEnum.training, + billSource: BillSourceEnum.training, vectorModel: getVectorModel(dataset.vectorModel).name, agentModel: getLLMModel(dataset.agentModel).name }); diff --git a/projects/app/src/pages/api/support/wallet/sub/getTeamSubStatus.ts b/projects/app/src/pages/api/support/wallet/sub/getTeamSubStatus.ts index 074bedf7e..83eb283fd 100644 --- a/projects/app/src/pages/api/support/wallet/sub/getTeamSubStatus.ts +++ b/projects/app/src/pages/api/support/wallet/sub/getTeamSubStatus.ts @@ -2,21 +2,22 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@fastgpt/service/common/response'; import { connectToDatabase } from '@/service/mongo'; import { authCert } from '@fastgpt/service/support/permission/auth/common'; -import { getTeamSubPlans } from '@fastgpt/service/support/wallet/sub/utils'; +import { getTeamSubPlanStatus } from '@fastgpt/service/support/wallet/sub/utils'; import { getStandardSubPlan } from '@/service/support/wallet/sub/utils'; -import { FeTeamPlanStatusType } from '@fastgpt/global/support/wallet/sub/type'; +import { FeTeamSubType } from '@fastgpt/global/support/wallet/sub/type'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { await connectToDatabase(); + // 凭证校验 const { teamId } = await authCert({ req, authToken: true }); - jsonRes(res, { - data: await getTeamSubPlans({ + jsonRes(res, { + data: await getTeamSubPlanStatus({ teamId, standardPlans: getStandardSubPlan() }) diff --git a/projects/app/src/pages/api/v1/audio/transcriptions.ts b/projects/app/src/pages/api/v1/audio/transcriptions.ts index 1c6997d51..189ab13c4 100644 --- a/projects/app/src/pages/api/v1/audio/transcriptions.ts +++ b/projects/app/src/pages/api/v1/audio/transcriptions.ts @@ -6,7 +6,7 @@ import { getUploadModel } from '@fastgpt/service/common/file/multer'; import { removeFilesByPaths } from '@fastgpt/service/common/file/utils'; import fs from 'fs'; import { getAIApi } from '@fastgpt/service/core/ai/config'; -import { pushWhisperUsage } from '@/service/support/wallet/usage/push'; +import { pushWhisperBill } from '@/service/support/wallet/bill/push'; const upload = getUploadModel({ maxSize: 2 @@ -40,7 +40,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex model: global.whisperModel.model }); - pushWhisperUsage({ + pushWhisperBill({ teamId, tmbId, duration diff --git a/projects/app/src/pages/api/v1/chat/completions.ts b/projects/app/src/pages/api/v1/chat/completions.ts index 9f06f9bd3..f703072ab 100644 --- a/projects/app/src/pages/api/v1/chat/completions.ts +++ b/projects/app/src/pages/api/v1/chat/completions.ts @@ -13,16 +13,16 @@ import { gptMessage2ChatType, textAdaptGptResponse } from '@/utils/adapt'; import { getChatItems } from '@fastgpt/service/core/chat/controller'; import { saveChat } from '@/service/utils/chat/saveChat'; import { responseWrite } from '@fastgpt/service/common/response'; -import { pushChatUsage } from '@/service/support/wallet/usage/push'; +import { pushChatBill } from '@/service/support/wallet/bill/push'; import { authOutLinkChatStart } from '@/service/support/permission/auth/outLink'; -import { pushResult2Remote, addOutLinkUsage } from '@fastgpt/service/support/outLink/tools'; +import { pushResult2Remote, updateOutLinkUsage } from '@fastgpt/service/support/outLink/tools'; import requestIp from 'request-ip'; -import { getUsageSourceByAuthType } from '@fastgpt/global/support/wallet/usage/tools'; +import { getBillSourceByAuthType } from '@fastgpt/global/support/wallet/bill/tools'; import { selectShareResponse } from '@/utils/service/core/chat'; import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools'; import { connectToDatabase } from '@/service/mongo'; -import { getUserChatInfoAndAuthTeamPoints } from '@/service/support/permission/auth/team'; +import { getUserAndAuthBalance } from '@fastgpt/service/support/user/controller'; import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant'; import { MongoApp } from '@fastgpt/service/core/app/schema'; import { autChatCrud } from '@/service/support/permission/auth/chat'; @@ -35,14 +35,9 @@ type FastGptShareChatProps = { shareId?: string; outLinkUid?: string; }; -type FastGptTeamShareChatProps = { - teamId?: string; - outLinkUid?: string; -}; export type Props = ChatCompletionCreateParams & FastGptWebChatProps & - FastGptShareChatProps & - FastGptTeamShareChatProps & { + FastGptShareChatProps & { messages: ChatMessageItemType[]; stream?: boolean; detail?: boolean; @@ -65,7 +60,6 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex const { chatId, appId, - teamId, shareId, outLinkUid, stream = false, @@ -103,96 +97,90 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex } /* auth app permission */ - const { teamId, tmbId, user, app, responseDetail, authType, apikey, canWrite, outLinkUserId } = - await (async () => { - if (shareId && outLinkUid) { - const { teamId, tmbId, user, appId, authType, responseDetail, uid } = - await authOutLinkChatStart({ - shareId, - ip: originIp, - outLinkUid, - question: question.value - }); - const app = await MongoApp.findById(appId); - - if (!app) { - return Promise.reject('app is empty'); - } - - return { - teamId, - tmbId, - user, - app, - responseDetail, - apikey: '', - authType, - canWrite: false, - outLinkUserId: uid - }; - } - - const { - appId: apiKeyAppId, - teamId, - tmbId, - authType, - apikey - } = await authCert({ - req, - authToken: true, - authApiKey: true + const { user, app, responseDetail, authType, apikey, canWrite, uid } = await (async () => { + if (shareId && outLinkUid) { + const { user, appId, authType, responseDetail, uid } = await authOutLinkChatStart({ + shareId, + ip: originIp, + outLinkUid, + question: question.value }); + const app = await MongoApp.findById(appId); - const user = await getUserChatInfoAndAuthTeamPoints(tmbId); - - // openapi key - if (authType === AuthUserTypeEnum.apikey) { - if (!apiKeyAppId) { - return Promise.reject( - 'Key is error. You need to use the app key rather than the account key.' - ); - } - const app = await MongoApp.findById(apiKeyAppId); - - if (!app) { - return Promise.reject('app is empty'); - } - - return { - teamId, - tmbId, - user, - app, - responseDetail: detail, - apikey, - authType, - canWrite: true - }; + if (!app) { + return Promise.reject('app is empty'); + } + + return { + user, + app, + responseDetail, + apikey: '', + authType, + canWrite: false, + uid + }; + } + + const { + appId: apiKeyAppId, + tmbId, + authType, + apikey + } = await authCert({ + req, + authToken: true, + authApiKey: true + }); + + const user = await getUserAndAuthBalance({ + tmbId, + minBalance: 0 + }); + + // openapi key + if (authType === AuthUserTypeEnum.apikey) { + if (!apiKeyAppId) { + return Promise.reject( + 'Key is error. You need to use the app key rather than the account key.' + ); + } + const app = await MongoApp.findById(apiKeyAppId); + + if (!app) { + return Promise.reject('app is empty'); } - // token auth - if (!appId) { - return Promise.reject('appId is empty'); - } - const { app, canWrite } = await authApp({ - req, - authToken: true, - appId, - per: 'r' - }); - return { - teamId, - tmbId, user, app, responseDetail: detail, apikey, authType, - canWrite: canWrite || false + canWrite: true }; - })(); + } + + // token auth + if (!appId) { + return Promise.reject('appId is empty'); + } + const { app, canWrite } = await authApp({ + req, + authToken: true, + appId, + per: 'r' + }); + + return { + user, + app, + responseDetail: detail, + apikey, + authType, + canWrite: canWrite || false + }; + })(); // auth chat permission await autChatCrud({ @@ -213,17 +201,16 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex limit: 30, field: `dataId obj value` }); - const concatHistories = history.concat(chatMessages); const responseChatItemId: string | undefined = messages[messages.length - 1].dataId; /* start flow controller */ - const { responseData, moduleDispatchBills, answerText } = await dispatchModules({ + const { responseData, answerText } = await dispatchModules({ res, mode: 'chat', user, - teamId: String(teamId), - tmbId: String(tmbId), + teamId: String(user.team.teamId), + tmbId: String(user.team.tmbId), appId: String(app._id), chatId, responseChatItemId, @@ -236,19 +223,18 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex stream, detail }); - console.log('af'); // save chat if (chatId) { await saveChat({ chatId, appId: app._id, - teamId: teamId, - tmbId: tmbId, + teamId: user.team.teamId, + tmbId: user.team.tmbId, variables, - updateUseTime: !shareId && String(tmbId) === String(app.tmbId), // owner update use time + updateUseTime: !shareId && String(user.team.tmbId) === String(app.tmbId), // owner update use time shareId, - outLinkUid: outLinkUserId, + outLinkUid: uid, source: (() => { if (shareId) { return ChatSourceEnum.share; @@ -319,29 +305,29 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex } // add record - const { totalPoints } = pushChatUsage({ + const { total } = pushChatBill({ appName: app.name, appId: app._id, - teamId: teamId, - tmbId: tmbId, - source: getUsageSourceByAuthType({ shareId, authType }), - moduleDispatchBills + teamId: user.team.teamId, + tmbId: user.team.tmbId, + source: getBillSourceByAuthType({ shareId, authType }), + response: responseData }); if (shareId) { - pushResult2Remote({ outLinkUid, shareId, appName: app.name, responseData }); - addOutLinkUsage({ + pushResult2Remote({ outLinkUid, shareId, responseData }); + updateOutLinkUsage({ shareId, - totalPoints + total }); } if (apikey) { updateApiKeyUsage({ apikey, - totalPoints + usage: total }); } - } catch (err) { + } catch (err: any) { if (stream) { sseErrRes(res, err); res.end(); diff --git a/projects/app/src/pages/api/v1/embeddings.ts b/projects/app/src/pages/api/v1/embeddings.ts index 3f82ecf87..a363ecb78 100644 --- a/projects/app/src/pages/api/v1/embeddings.ts +++ b/projects/app/src/pages/api/v1/embeddings.ts @@ -2,13 +2,13 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@fastgpt/service/common/response'; import { authCert } from '@fastgpt/service/support/permission/auth/common'; import { withNextCors } from '@fastgpt/service/common/middle/cors'; -import { pushGenerateVectorUsage } from '@/service/support/wallet/usage/push'; +import { pushGenerateVectorBill } from '@/service/support/wallet/bill/push'; import { connectToDatabase } from '@/service/mongo'; +import { authTeamBalance } from '@/service/support/permission/auth/bill'; import { getVectorsByText } from '@fastgpt/service/core/ai/embedding'; import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools'; -import { getUsageSourceByAuthType } from '@fastgpt/global/support/wallet/usage/tools'; +import { getBillSourceByAuthType } from '@fastgpt/global/support/wallet/bill/tools'; import { getVectorModel } from '@/service/core/ai/model'; -import { checkTeamAIPoints } from '@/service/support/permission/teamLimit'; type Props = { input: string | string[]; @@ -34,7 +34,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex authApiKey: true }); - await checkTeamAIPoints(teamId); + await authTeamBalance(teamId); const { charsLength, vectors } = await getVectorsByText({ input: query, @@ -55,19 +55,19 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex } }); - const { totalPoints } = pushGenerateVectorUsage({ + const { total } = pushGenerateVectorBill({ teamId, tmbId, charsLength, model, billId, - source: getUsageSourceByAuthType({ authType }) + source: getBillSourceByAuthType({ authType }) }); if (apikey) { updateApiKeyUsage({ apikey, - totalPoints: totalPoints + usage: total }); } } catch (err) { diff --git a/projects/app/src/pages/api/v1/rerank.ts b/projects/app/src/pages/api/v1/rerank.ts new file mode 100644 index 000000000..c73ab0b6c --- /dev/null +++ b/projects/app/src/pages/api/v1/rerank.ts @@ -0,0 +1,50 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import { jsonRes } from '@fastgpt/service/common/response'; +import { authCert } from '@fastgpt/service/support/permission/auth/common'; +import { withNextCors } from '@fastgpt/service/common/middle/cors'; +import { pushReRankBill } from '@/service/support/wallet/bill/push'; +import { connectToDatabase } from '@/service/mongo'; +import { authTeamBalance } from '@/service/support/permission/auth/bill'; +import { PostReRankProps, PostReRankResponse } from '@fastgpt/global/core/ai/api'; +import { reRankRecall } from '@/service/core/ai/rerank'; +import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools'; + +export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) { + let { query, inputs } = req.body as PostReRankProps; + try { + await connectToDatabase(); + const { teamId, tmbId, apikey } = await authCert({ + req, + authApiKey: true + }); + await authTeamBalance(teamId); + + // max 150 length + inputs = inputs.slice(0, 150); + + const result = await reRankRecall({ query, inputs }); + + const { total } = pushReRankBill({ + teamId, + tmbId, + source: 'api', + inputs + }); + + if (apikey) { + updateApiKeyUsage({ + apikey, + usage: total + }); + } + + jsonRes(res, { + data: result + }); + } catch (err) { + jsonRes(res, { + code: 500, + error: err + }); + } +}); diff --git a/projects/app/src/pages/app/detail/components/Charts/TotalUsage.tsx b/projects/app/src/pages/app/detail/components/Charts/TotalUsage.tsx new file mode 100644 index 000000000..8822483ef --- /dev/null +++ b/projects/app/src/pages/app/detail/components/Charts/TotalUsage.tsx @@ -0,0 +1,201 @@ +import React, { useEffect, useMemo, useRef } from 'react'; +import * as echarts from 'echarts'; +import { useSystemStore } from '@/web/common/system/useSystemStore'; +import { getAppTotalUsage } from '@/web/core/app/api'; +import { useQuery } from '@tanstack/react-query'; +import dayjs from 'dayjs'; +import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools'; +import Loading from '@/components/Loading'; +import { Box } from '@chakra-ui/react'; + +const map = { + blue: { + backgroundColor: { + type: 'linear', + x: 0, + y: 0, + x2: 0, + y2: 1, + colorStops: [ + { + offset: 0, + color: 'rgba(3, 190, 232, 0.42)' // 0% 处的颜色 + }, + { + offset: 1, + color: 'rgba(0, 182, 240, 0)' + } + ], + global: false // 缺省为 false + }, + lineColor: '#36ADEF' + }, + deepBlue: { + backgroundColor: { + type: 'linear', + x: 0, + y: 0, + x2: 0, + y2: 1, + colorStops: [ + { + offset: 0, + color: 'rgba(47, 112, 237, 0.42)' // 0% 处的颜色 + }, + { + offset: 1, + color: 'rgba(94, 159, 235, 0)' + } + ], + global: false + }, + lineColor: '#3293EC' + }, + purple: { + backgroundColor: { + type: 'linear', + x: 0, + y: 0, + x2: 0, + y2: 1, + colorStops: [ + { + offset: 0, + color: 'rgba(211, 190, 255, 0.42)' // 0% 处的颜色 + }, + { + offset: 1, + color: 'rgba(52, 60, 255, 0)' + } + ], + global: false // 缺省为 false + }, + lineColor: '#8172D8' + }, + green: { + backgroundColor: { + type: 'linear', + x: 0, + y: 0, + x2: 0, + y2: 1, + colorStops: [ + { + offset: 0, + color: 'rgba(4, 209, 148, 0.42)' // 0% 处的颜色 + }, + { + offset: 1, + color: 'rgba(19, 217, 181, 0)' + } + ], + global: false // 缺省为 false + }, + lineColor: '#00A9A6', + max: 100 + } +}; + +const TokenUsage = ({ appId }: { appId: string }) => { + const { screenWidth } = useSystemStore(); + + const Dom = useRef(null); + const myChart = useRef(); + const { data = [] } = useQuery(['init'], () => getAppTotalUsage({ appId })); + + const option = useMemo( + () => ({ + xAxis: { + type: 'category', + show: false, + boundaryGap: false, + data: data.map((item) => item.date) + }, + yAxis: { + type: 'value', + splitNumber: 3, + min: 0 + }, + grid: { + show: false, + left: 5, + right: 5, + top: 0, + bottom: 5 + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'line' + }, + formatter: (e: any[]) => { + const data = e[0]; + if (!data) return ''; + + return ` +
+
${dayjs(data.axisValue).format('YYYY/MM/DD')}
+
${formatStorePrice2Read(e[0]?.value || 0)}元
+
+`; + } + }, + series: [ + { + data: data.map((item) => item.total), + type: 'line', + showSymbol: true, + animationDuration: 1000, + animationEasingUpdate: 'linear', + areaStyle: { + color: map['blue'].backgroundColor + }, + lineStyle: { + width: '1', + color: map['blue'].lineColor + }, + itemStyle: { + width: 1.5, + color: map['blue'].lineColor + }, + emphasis: { + // highlight + disabled: true + } + } + ] + }), + [data] + ); + + // init chart + useEffect(() => { + if (!Dom.current || myChart?.current?.getOption()) return; + myChart.current = echarts.init(Dom.current); + myChart.current && myChart.current.setOption(option); + + setTimeout(() => { + myChart.current?.resize(); + }, 500); + }, []); + + // data changed, update + useEffect(() => { + if (!myChart.current || !myChart?.current?.getOption()) return; + myChart.current.setOption(option); + }, [data, option]); + + // resize chart + useEffect(() => { + if (!myChart.current || !myChart.current.getOption()) return; + myChart.current.resize(); + }, [screenWidth]); + + return ( + + + + ); +}; + +export default React.memo(TokenUsage); diff --git a/projects/app/src/pages/app/detail/components/OutLink/Share.tsx b/projects/app/src/pages/app/detail/components/OutLink/Share.tsx index 048028f33..ee7980ea3 100644 --- a/projects/app/src/pages/app/detail/components/OutLink/Share.tsx +++ b/projects/app/src/pages/app/detail/components/OutLink/Share.tsx @@ -36,6 +36,7 @@ import { useForm } from 'react-hook-form'; import { defaultOutLinkForm } from '@/constants/app'; import type { OutLinkEditType, OutLinkSchema } from '@fastgpt/global/support/outLink/type.d'; import { useRequest } from '@/web/common/hooks/useRequest'; +import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools'; import { OutLinkTypeEnum } from '@fastgpt/global/support/outLink/constant'; import { useTranslation } from 'next-i18next'; import { useToast } from '@fastgpt/web/hooks/useToast'; @@ -93,7 +94,7 @@ const Share = ({ appId }: { appId: string }) => { {t('common.Name')} - {t('support.outlink.Usage points')} + {t('common.Price used')} {t('core.app.share.Is response quote')} {feConfigs?.isPlus && ( <> @@ -111,11 +112,11 @@ const Share = ({ appId }: { appId: string }) => { {item.name} - {Math.round(item.usagePoints)} + {formatStorePrice2Read(item.total)} {feConfigs?.isPlus ? `${ - item.limit?.maxUsagePoints && item.limit.maxUsagePoints > -1 - ? ` / ${item.limit.maxUsagePoints}` + item.limit && item.limit.credit > -1 + ? ` / ¥${item.limit.credit}` : ` / ${t('common.Unlimited')}` }` : ''} @@ -314,15 +315,15 @@ function EditLinkModal({ - {t('support.outlink.Max usage points')} - + {t('common.Max credit')} + import('../InfoModal')); const AppCard = ({ appId }: { appId: string }) => { @@ -22,7 +20,6 @@ const AppCard = ({ appId }: { appId: string }) => { const { toast } = useToast(); const { appDetail } = useAppStore(); const [settingAppInfo, setSettingAppInfo] = useState(); - const [TeamTagsSet, setTeamTagsSet] = useState(); const { openConfirm: openConfirmDel, ConfirmModal: ConfirmDelModal } = useConfirm({ content: t('app.Confirm Del App Tip') @@ -126,17 +123,6 @@ const AppCard = ({ appId }: { appId: string }) => { > {t('core.app.navbar.Publish')} - {appDetail.isOwner && ( - - )} {appDetail.isOwner && ( - - - -
- ); -}; -export default TagsEditModal; diff --git a/projects/app/src/pages/app/list/component/CreateModal.tsx b/projects/app/src/pages/app/list/component/CreateModal.tsx index fa3039bd6..e0b107357 100644 --- a/projects/app/src/pages/app/list/component/CreateModal.tsx +++ b/projects/app/src/pages/app/list/component/CreateModal.tsx @@ -8,12 +8,8 @@ import { Input, Grid, useTheme, - Card, - Text, - HStack, - Tag + Card } from '@chakra-ui/react'; -import { AddIcon } from '@chakra-ui/icons'; import { useSelectFile } from '@/web/common/file/hooks/useSelectFile'; import { useForm } from 'react-hook-form'; import { compressImgFileAndUpload } from '@/web/common/file/controller'; diff --git a/projects/app/src/pages/app/list/index.tsx b/projects/app/src/pages/app/list/index.tsx index ace56978c..88db3bac8 100644 --- a/projects/app/src/pages/app/list/index.tsx +++ b/projects/app/src/pages/app/list/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState, useEffect } from 'react'; +import React, { useCallback } from 'react'; import { Box, Grid, Flex, IconButton, Button, useDisclosure } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import { useQuery } from '@tanstack/react-query'; @@ -8,6 +8,7 @@ import { useToast } from '@fastgpt/web/hooks/useToast'; import { useConfirm } from '@/web/common/hooks/useConfirm'; import { serviceSideProps } from '@/web/common/utils/i18n'; import { useTranslation } from 'next-i18next'; + import MyIcon from '@fastgpt/web/components/common/Icon'; import PageContainer from '@/components/PageContainer'; import Avatar from '@/components/Avatar'; @@ -23,7 +24,6 @@ const MyApps = () => { const router = useRouter(); const { userInfo } = useUserStore(); const { myApps, loadMyApps } = useAppStore(); - const [teamsTags, setTeamTags] = useState([]); const { openConfirm, ConfirmModal } = useConfirm({ title: '删除提示', content: '确认删除该应用所有信息?' @@ -65,9 +65,11 @@ const MyApps = () => { {t('app.My Apps')} - + {userInfo?.team?.canWrite && ( + + )} { ))} - {/* ( - - ) */} - {myApps.length === 0 && ( diff --git a/projects/app/src/pages/chat/team.tsx b/projects/app/src/pages/chat/team.tsx deleted file mode 100644 index b5fe8a3a0..000000000 --- a/projects/app/src/pages/chat/team.tsx +++ /dev/null @@ -1,521 +0,0 @@ -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import Head from 'next/head'; -import { getTeamChatInfo } from '@/web/core/chat/api'; -import { useRouter } from 'next/router'; -import { - Box, - Flex, - useDisclosure, - Drawer, - DrawerOverlay, - DrawerContent, - useTheme -} from '@chakra-ui/react'; -import Avatar from '@/components/Avatar'; -import { useToast } from '@fastgpt/web/hooks/useToast'; -import { useQuery } from '@tanstack/react-query'; -import { useSystemStore } from '@/web/common/system/useSystemStore'; -import SideBar from '@/components/SideBar'; -import PageContainer from '@/components/PageContainer'; -import { getChatListById } from '@/web/core/chat/api'; -import ChatHistorySlider from './components/ChatHistorySlider'; -import ChatHeader from './components/ChatHeader'; -import { serviceSideProps } from '@/web/common/utils/i18n'; -import { useTranslation } from 'next-i18next'; -import { checkChatSupportSelectFileByChatModels } from '@/web/core/chat/utils'; -import { useChatStore } from '@/web/core/chat/storeChat'; -import { customAlphabet } from 'nanoid'; -import { useLoading } from '@/web/common/hooks/useLoading'; -const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12); -import ChatBox, { type ComponentRef, type StartChatFnProps } from '@/components/ChatBox'; -import { streamFetch } from '@/web/common/api/fetch'; -import { useTeamShareChatStore } from '@/web/core/chat/storeTeamChat'; -import type { - ChatHistoryItemType, - chatAppListSchema, - teamInfoType -} from '@fastgpt/global/core/chat/type.d'; -import { chatContentReplaceBlock } from '@fastgpt/global/core/chat/utils'; -import { ChatStatusEnum } from '@fastgpt/global/core/chat/constants'; -import { POST } from '@/web/common/api/request'; -const OutLink = ({ - teamId, - appId, - chatId, - authToken -}: { - teamId: string; - appId: string; - chatId: string; - authToken: string; -}) => { - const { t } = useTranslation(); - const router = useRouter(); - const { toast } = useToast(); - const theme = useTheme(); - const [myApps, setMyApps] = useState>([]); - const { isPc } = useSystemStore(); - const ChatBoxRef = useRef(null); - const [teamInfo, setTeamInfo] = useState(); - const { Loading, setIsLoading } = useLoading(); - const forbidRefresh = useRef(false); - - const { isOpen: isOpenSlider, onClose: onCloseSlider, onOpen: onOpenSlider } = useDisclosure(); - const { - histories, - loadHistories, - lastChatAppId, - setLastChatAppId, - lastChatId, - setLastChatId, - pushHistory, - updateHistory, - delOneHistory, - chatData, - setChatData, - delOneHistoryItem, - clearHistories - } = useChatStore(); - const { - localUId, - teamShareChatHistory, // abandon - clearLocalHistory // abandon - } = useTeamShareChatStore(); - - const outLinkUid: string = authToken || localUId; - - // 纯网络获取流程 - const loadApps = useCallback(async () => { - try { - if (!teamId) { - toast({ - status: 'error', - title: t('core.chat.You need to a chat app') - }); - return; - } - // 根据teamId 获取教研token以及用户tags,然后通过是否为 - // 根据获取历史记录列表 - const res = await getChatListById({ teamId, authToken }); - const { apps = [], teamInfo } = res; - setMyApps(apps); - setTeamInfo(teamInfo); - if (apps.length <= 0) { - toast({ - status: 'error', - title: t('core.chat.You need to a chat app') - }); - } - // - return null; - } catch (error: any) { - toast({ - status: 'warning', - title: error?.message - }); - } - }, [outLinkUid, router, t, toast]); - - const startChat = useCallback( - async ({ messages, controller, generatingMessage, variables }: StartChatFnProps) => { - console.log('res', 13); - const prompts = messages.slice(-2); - const completionChatId = chatId ? chatId : nanoid(); - - const { responseText, responseData } = await streamFetch({ - data: { - messages: prompts, - variables, - appId, - teamId: teamId, - outLinkUid: outLinkUid, - chatId: completionChatId - }, - onMessage: generatingMessage, - abortCtrl: controller - }); - - const newTitle = - chatContentReplaceBlock(prompts[0].content).slice(0, 20) || - prompts[1]?.value?.slice(0, 20) || - t('core.chat.New Chat'); - - // new chat - if (completionChatId !== chatId) { - const newHistory: ChatHistoryItemType = { - chatId: completionChatId, - updateTime: new Date(), - title: newTitle, - appId, - top: false - }; - pushHistory(newHistory); - if (controller.signal.reason !== 'leave') { - forbidRefresh.current = true; - router.replace({ - query: { - chatId: completionChatId, - appId, - teamId: teamId, - authToken: authToken - } - }); - } - } else { - // update chat - const currentChat = histories.find((item) => item.chatId === chatId); - currentChat && - updateHistory({ - ...currentChat, - updateTime: new Date(), - title: newTitle - }); - } - // update chat window - setChatData((state) => ({ - ...state, - title: newTitle, - history: ChatBoxRef.current?.getChatHistories() || state.history - })); - - return { responseText, responseData, isNewChat: forbidRefresh.current }; - }, - [appId, chatId, histories, pushHistory, router, setChatData, updateHistory] - ); - - const { isFetching } = useQuery(['init', appId, teamId], async () => { - console.log('res', 3); - if (!teamId) { - toast({ - status: 'error', - title: t('core.chat.You need to a chat app') - }); - return; - } - return teamId && loadApps(); - }); - - useQuery(['loadHistories', appId], () => { - console.log('res', 1); - teamId && appId ? loadHistories({ appId, outLinkUid }) : null; - }); - // 初始化聊天框 - useQuery(['init', { appId, chatId }], () => { - if (!teamId) { - toast({ - status: 'error', - title: t('core.chat.You need to a chat app') - }); - return; - } - // pc: redirect to latest model chat - if (!appId && lastChatAppId) { - return router.replace({ - query: { - appId: lastChatAppId, - chatId: lastChatId, - teamId: teamId, - authToken: authToken - } - }); - } - if (!appId && myApps[0]) { - return router.replace({ - query: { - appId: myApps[0]._id, - chatId: lastChatId, - teamId: teamId, - authToken: authToken - } - }); - } - if (!appId) { - (async () => { - const { apps = [] } = await getChatListById({ teamId, authToken }); - setMyApps(apps); - if (apps.length === 0) { - toast({ - status: 'error', - title: t('core.chat.You need to a chat app') - }); - } else { - router.replace({ - query: { - appId: apps[0]._id, - chatId: lastChatId, - teamId: teamId, - authToken: authToken - } - }); - } - })(); - return; - } - - // store id - appId && setLastChatAppId(appId); - setLastChatId(chatId); - return loadChatInfo({ - appId, - chatId, - loading: appId !== chatData.appId - }); - }); - - // get chat app info - const loadChatInfo = useCallback( - async ({ - appId, - chatId, - loading = false - }: { - appId: string; - chatId: string; - loading?: boolean; - }) => { - try { - if (!teamId) { - toast({ - status: 'error', - title: t('core.chat.You need to a chat app') - }); - return; - } - loading && setIsLoading(true); - const res = await getTeamChatInfo({ appId, chatId, outLinkUid }); - console.log('res', res); - const history = res.history.map((item) => ({ - ...item, - status: ChatStatusEnum.finish - })); - - setChatData({ - ...res, - history - }); - - // have records. - ChatBoxRef.current?.resetHistory(history); - ChatBoxRef.current?.resetVariables(res.variables); - if (res.history.length > 0) { - setTimeout(() => { - ChatBoxRef.current?.scrollToBottom('auto'); - }, 500); - } - } catch (e: any) { - // reset all chat tore - setLastChatAppId(''); - setLastChatId(''); - toast({ - title: t('core.chat.Failed to initialize chat'), - status: 'error' - }); - if (e?.code === 501) { - //router.replace('/app/list'); - } else if (chatId) { - router.replace({ - query: { - ...router.query, - chatId: '' - } - }); - } - } - setIsLoading(false); - return null; - }, - [setIsLoading, setChatData, router, setLastChatAppId, setLastChatId, toast] - ); - // 监测路由改变 - useEffect(() => { - const activeHistory = teamShareChatHistory.filter((item) => !item.delete); - if (!localUId || !teamId || activeHistory.length === 0) return; - (async () => { - try { - await POST('/core/chat/initLocalShareHistoryV464', { - outLinkUid: localUId, - chatIds: teamShareChatHistory.map((item) => item.chatId) - }); - clearLocalHistory(); - // router.reload(); - } catch (error) { - toast({ - status: 'warning', - title: t('core.shareChat.Init Error') - }); - } - })(); - }, [clearLocalHistory, localUId, router, teamShareChatHistory, teamId, t, toast]); - - return ( - - {/* pc show myself apps */} - - - - {myApps.map((item) => ( - { - router.replace({ - query: { - appId: item._id, - teamId: teamId, - authToken: authToken - } - }); - } - })} - > - - - {item.name} - - - ))} - - - - - - {((children: React.ReactNode) => { - return isPc || !appId ? ( - {children} - ) : ( - - - {children} - - ); - })( - ({ - id: item.chatId, - title: item.title, - customTitle: item.customTitle, - top: item.top - }))} - onChangeChat={(chatId) => { - router.replace({ - query: { - chatId: chatId || '', - appId, - teamId: teamId, - authToken: authToken - } - }); - if (!isPc) { - onCloseSlider(); - } - }} - onDelHistory={(e) => delOneHistory({ ...e, appId })} - onClearHistory={() => { - clearHistories({ appId }); - router.replace({ - query: { - appId, - teamId: teamId, - authToken: authToken - } - }); - }} - onSetHistoryTop={(e) => { - updateHistory({ ...e, appId }); - }} - onSetCustomTitle={async (e) => { - updateHistory({ - appId, - chatId: e.chatId, - title: e.title, - customTitle: e.title - }); - }} - /> - )} - {/* chat container */} - - {/* header */} - - {/* chat box */} - - {}} - onStartChat={startChat} - onDelMessage={(e) => - delOneHistoryItem({ ...e, appId: chatData.appId, chatId, outLinkUid }) - } - appId={chatData.appId} - chatId={chatId} - outLinkUid={outLinkUid} - /> - - - - - - ); -}; - -export async function getServerSideProps(context: any) { - const teamId = context?.query?.teamId || ''; - const appId = context?.query?.appId || ''; - const chatId = context?.query?.chatId || ''; - const authToken: string = context?.query?.authToken || ''; - - return { - props: { - teamId, - appId, - chatId, - authToken, - ...(await serviceSideProps(context)) - } - }; -} - -export default OutLink; diff --git a/projects/app/src/pages/dataset/detail/components/Import/Provider.tsx b/projects/app/src/pages/dataset/detail/components/Import/Provider.tsx index fb6c5525c..00a8d479d 100644 --- a/projects/app/src/pages/dataset/detail/components/Import/Provider.tsx +++ b/projects/app/src/pages/dataset/detail/components/Import/Provider.tsx @@ -1,5 +1,6 @@ import React, { useContext, useCallback, createContext, useState, useMemo, useEffect } from 'react'; +import { formatModelPrice2Read } from '@fastgpt/global/support/wallet/bill/tools'; import { splitText2Chunks } from '@fastgpt/global/common/string/textSplitter'; import { TrainingModeEnum } from '@fastgpt/global/core/dataset/constants'; import { useTranslation } from 'next-i18next'; @@ -33,7 +34,7 @@ type useImportStoreType = { totalChunkChars: number; totalChunks: number; chunkSize: number; - predictPoints: number; + predictPrice: number; priceTip: string; uploadRate: number; splitSources2Chunks: () => void; @@ -53,7 +54,7 @@ const StateContext = createContext({ totalChunkChars: 0, totalChunks: 0, chunkSize: 0, - predictPoints: 0, + predictPrice: 0, priceTip: '', uploadRate: 50, splitSources2Chunks: () => {} @@ -104,9 +105,10 @@ const Provider = ({ chunkSize: embeddingChunkSize, showChunkInput: true, showPromptInput: false, - charsPointsPrice: vectorModel.charsPointsPrice, + inputPrice: vectorModel.inputPrice, + outputPrice: 0, priceTip: t('core.dataset.import.Embedding Estimated Price Tips', { - price: vectorModel.charsPointsPrice + price: vectorModel.inputPrice }), uploadRate: 150 }, @@ -118,9 +120,10 @@ const Provider = ({ chunkSize: agentModel.maxContext * 0.55 || 6000, showChunkInput: false, showPromptInput: true, - charsPointsPrice: agentModel.charsPointsPrice, + inputPrice: agentModel.inputPrice, + outputPrice: agentModel.outputPrice, priceTip: t('core.dataset.import.QA Estimated Price Tips', { - price: agentModel?.charsPointsPrice + price: agentModel?.inputPrice }), uploadRate: 30 } @@ -148,12 +151,15 @@ const Provider = ({ () => sources.reduce((sum, file) => sum + file.chunkChars, 0), [sources] ); - const predictPoints = useMemo(() => { + const predictPrice = useMemo(() => { if (mode === TrainingModeEnum.qa) { - return +(((totalChunkChars * 1.5) / 1000) * agentModel.charsPointsPrice).toFixed(2); + const inputTotal = totalChunkChars * selectModelStaticParam.inputPrice; + const outputTotal = totalChunkChars * 0.5 * selectModelStaticParam.inputPrice; + + return formatModelPrice2Read(inputTotal + outputTotal); } - return +((totalChunkChars / 1000) * vectorModel.charsPointsPrice).toFixed(2); - }, [agentModel.charsPointsPrice, mode, totalChunkChars, vectorModel.charsPointsPrice]); + return formatModelPrice2Read(totalChunkChars * selectModelStaticParam.inputPrice); + }, [mode, selectModelStaticParam.inputPrice, totalChunkChars]); const totalChunks = useMemo( () => sources.reduce((sum, file) => sum + file.chunks.length, 0), [sources] @@ -172,8 +178,7 @@ const Provider = ({ return { ...file, chunkChars: chars, - chunks: chunks.map((chunk, i) => ({ - chunkIndex: i, + chunks: chunks.map((chunk) => ({ q: chunk, a: '' })) @@ -193,7 +198,7 @@ const Provider = ({ totalChunkChars, totalChunks, chunkSize, - predictPoints, + predictPrice, splitSources2Chunks }; return {children}; diff --git a/projects/app/src/pages/dataset/detail/components/Import/commonProgress/DataProcess.tsx b/projects/app/src/pages/dataset/detail/components/Import/commonProgress/DataProcess.tsx index d34e536a2..2f3eef5cf 100644 --- a/projects/app/src/pages/dataset/detail/components/Import/commonProgress/DataProcess.tsx +++ b/projects/app/src/pages/dataset/detail/components/Import/commonProgress/DataProcess.tsx @@ -46,7 +46,7 @@ function DataProcess({ maxChunkSize, totalChunkChars, totalChunks, - predictPoints, + predictPrice, showRePreview, splitSources2Chunks, priceTip @@ -275,7 +275,7 @@ function DataProcess({ {feConfigs?.show_pay && ( - {t('core.dataset.import.Estimated points', { points: predictPoints })} + {t('core.dataset.import.Estimated Price', { amount: predictPrice, unit: '元' })} )} diff --git a/projects/app/src/pages/dataset/detail/components/Import/commonProgress/Upload.tsx b/projects/app/src/pages/dataset/detail/components/Import/commonProgress/Upload.tsx index c3bd68733..f7e0475d6 100644 --- a/projects/app/src/pages/dataset/detail/components/Import/commonProgress/Upload.tsx +++ b/projects/app/src/pages/dataset/detail/components/Import/commonProgress/Upload.tsx @@ -16,7 +16,7 @@ import { useImportStore, type FormType } from '../Provider'; import { useTranslation } from 'next-i18next'; import MyIcon from '@fastgpt/web/components/common/Icon'; import { useRequest } from '@/web/common/hooks/useRequest'; -import { postCreateTrainingUsage } from '@/web/support/wallet/usage/api'; +import { postCreateTrainingBill } from '@/web/support/wallet/bill/api'; import { useDatasetStore } from '@/web/core/dataset/store/dataset'; import { chunksUpload, fileCollectionCreate } from '@/web/core/dataset/utils'; import { ImportSourceItemType } from '@/web/core/dataset/type'; @@ -54,7 +54,7 @@ const Upload = ({ showPreviewChunks }: { showPreviewChunks: boolean }) => { // Batch create collection and upload chunks for await (const item of uploadList) { - const billId = await postCreateTrainingUsage({ + const billId = await postCreateTrainingBill({ name: item.sourceName, datasetId: datasetDetail._id }); diff --git a/projects/app/src/pages/dataset/detail/components/InputDataModal.tsx b/projects/app/src/pages/dataset/detail/components/InputDataModal.tsx index ab7e133e0..14bfb5ddb 100644 --- a/projects/app/src/pages/dataset/detail/components/InputDataModal.tsx +++ b/projects/app/src/pages/dataset/detail/components/InputDataModal.tsx @@ -19,6 +19,7 @@ import { useRequest } from '@/web/common/hooks/useRequest'; import { countPromptTokens } from '@fastgpt/global/common/string/tiktoken'; import { useConfirm } from '@/web/common/hooks/useConfirm'; import { getDefaultIndex } from '@fastgpt/global/core/dataset/utils'; +import { DatasetDataIndexTypeEnum } from '@fastgpt/global/core/dataset/constants'; import { DatasetDataIndexItemType } from '@fastgpt/global/core/dataset/type'; import SideTabs from '@/components/SideTabs'; import DeleteIcon from '@fastgpt/web/components/common/Icon/delete'; @@ -161,10 +162,9 @@ const InputDataModal = ({ q: e.q, a: e.a, // remove dataId - indexes: e.indexes.map((index) => ({ - ...index, - dataId: undefined - })) + indexes: e.indexes.map((index) => + index.defaultIndex ? getDefaultIndex({ q: e.q, a: e.a }) : index + ) }); return { @@ -195,7 +195,7 @@ const InputDataModal = ({ id: dataId, ...e, indexes: e.indexes.map((index) => - index.defaultIndex ? getDefaultIndex({ q: e.q, a: e.a, dataId: index.dataId }) : index + index.defaultIndex ? getDefaultIndex({ q: e.q, a: e.a }) : index ) }); @@ -278,7 +278,7 @@ const InputDataModal = ({ bg={i % 2 !== 0 ? 'myWhite.400' : ''} _hover={{ '& .delete': { - display: index.defaultIndex ? 'none' : 'block' + display: index.defaultIndex && indexes.length === 1 ? 'none' : 'block' } }} > @@ -331,6 +331,7 @@ const InputDataModal = ({ onClick={() => appendIndexes({ defaultIndex: false, + type: DatasetDataIndexTypeEnum.chunk, text: '', dataId: `${Date.now()}` }) @@ -382,47 +383,45 @@ const InputTab = ({ const [inputType, setInputType] = useState(InputTypeEnum.q); return ( - - - - - * - - {t('core.dataset.data.Main Content')} - - - - - ), - value: InputTypeEnum.q - }, - { - label: ( - - {t('core.dataset.data.Auxiliary Data')} - - - - - ), - value: InputTypeEnum.a - } - ]} - value={inputType} - onChange={(e) => setInputType(e as InputTypeEnum)} - /> -
+ + + + * + + {t('core.dataset.data.Main Content')} + + + + + ), + value: InputTypeEnum.q + }, + { + label: ( + + {t('core.dataset.data.Auxiliary Data')} + + + + + ), + value: InputTypeEnum.a + } + ]} + value={inputType} + onChange={(e) => setInputType(e as InputTypeEnum)} + /> - + {inputType === InputTypeEnum.q && (