V4.8.17 feature (#3485)

* feat: add third party account config (#3443)

* temp

* editor workflow variable style

* add team to dispatch

* i18n

* delete console

* change openai account position

* fix

* fix

* fix

* fix

* fix

* 4.8.17 test (#3461)

* perf: external provider config

* perf: ui

* feat: add template config (#3434)

* change template position

* template config

* delete console

* delete

* fix

* fix

* perf: Mongo visutal field (#3464)

* remve invalid code

* perf: team member visutal code

* perf: virtual search; perf: search test data

* fix: ts

* fix: image response headers

* perf: template code

* perf: auth layout;perf: auto save (#3472)

* perf: auth layout

* perf: auto save

* perf: auto save

* fix: template guide display & http input support external variables (#3475)

* fix: template guide display

* http editor support external workflow variables

* perf: auto save;fix: ifelse checker line break; (#3478)

* perf: auto save

* perf: auto save

* fix: ifelse checker line break

* perf: doc

* perf: doc

* fix: update var type error

* 4.8.17 test (#3479)

* perf: auto save

* perf: auto save

* perf: template code

* 4.8.17 test (#3480)

* perf: auto save

* perf: auto save

* perf: model price model

* feat: add react memo

* perf: model provider filter

* fix: ts (#3481)

* perf: auto save

* perf: auto save

* fix: ts

* simple app tool select (#3473)

* workflow plugin userguide & simple tool ui

* simple tool filter

* reuse component

* change component to hook

* fix

* perf: too selector modal (#3484)

* perf: auto save

* perf: auto save

* perf: markdown render

* perf: too selector

* fix: app version require tmbId

* perf: templates refresh

* perf: templates refresh

* hide auto save error tip

* perf: toolkit guide

---------

Co-authored-by: heheer <heheer@sealos.io>
This commit is contained in:
Archer
2024-12-27 20:05:12 +08:00
committed by GitHub
parent a209856d48
commit b520988c64
207 changed files with 2943 additions and 1378 deletions

View File

@@ -16,7 +16,7 @@ OPENAI_BASE_URL=https://api.openai.com/v1
# 通用key。可以是 openai 的也可以是 oneapi 的。
# 此处逻辑:优先走 ONEAPI_URL如果填写了 ONEAPI_URLkey 也需要是 ONEAPI 的 key
CHAT_API_KEY=sk-xxxx
# 是否将图片转成 base64 传递给模型,本地开发和内网环境使用共有模型时候需要设置为 true
# 强制将图片转成 base64 传递给模型
MULTIPLE_DATA_TO_BASE64=true
# mongo 数据库连接参数,本地开发连接远程数据库时,可能需要增加 directConnection=true 参数,才能连接上。
@@ -26,8 +26,8 @@ MONGODB_URI=mongodb://username:password@0.0.0.0:27017/fastgpt?authSource=admin
# PG 向量库连接参数
PG_URL=postgresql://username:password@host:port/postgres
# milvus 向量库连接参数
MILVUS_ADDRESS=https://in03-78bd7f60e6e2a7c.api.gcp-us-west1.zillizcloud.com
MILVUS_TOKEN=133964348b00b4b4e4b51bef680a61350950385c8c64a3ec16b1ab92d3c67dcc4e0370fb9dd15791bcd6dadaf765e98a98735d0d
MILVUS_ADDRESS=
MILVUS_TOKEN=
# code sandbox url
SANDBOX_URL=http://localhost:3001

View File

@@ -1,6 +1,6 @@
{
"name": "app",
"version": "4.8.16",
"version": "4.8.17",
"private": false,
"scripts": {
"dev": "next dev",
@@ -23,6 +23,7 @@
"@fastgpt/plugins": "workspace:*",
"@fastgpt/service": "workspace:*",
"@fastgpt/web": "workspace:*",
"@fastgpt/templates": "workspace:*",
"@fortaine/fetch-event-source": "^3.0.6",
"@node-rs/jieba": "1.10.0",
"@tanstack/react-query": "^4.24.10",

View File

@@ -1,411 +0,0 @@
{
"name": "问题分类 + 知识库",
"intro": "先对用户的问题进行分类,再根据不同类型问题,执行不同的操作",
"author": "",
"avatar": "core/workflow/template/questionClassify",
"tags": ["office-services"],
"type": "advanced",
"workflow": {
"nodes": [
{
"nodeId": "userGuide",
"name": "系统配置",
"intro": "可以配置应用的系统参数",
"avatar": "core/workflow/template/systemConfig",
"flowNodeType": "userGuide",
"position": {
"x": 531.2422736065552,
"y": -486.7611729549753
},
"version": "481",
"inputs": [],
"outputs": []
},
{
"nodeId": "workflowStartNodeId",
"name": "流程开始",
"intro": "",
"avatar": "core/workflow/template/workflowStart",
"flowNodeType": "workflowStart",
"position": {
"x": 558.4082376415505,
"y": 123.72387429194112
},
"version": "481",
"inputs": [
{
"key": "userChatInput",
"renderTypeList": ["reference", "textarea"],
"valueType": "string",
"label": "用户问题",
"required": true,
"toolDescription": "用户问题"
}
],
"outputs": [
{
"id": "userChatInput",
"key": "userChatInput",
"label": "core.module.input.label.user question",
"valueType": "string",
"type": "FlowNodeOutputTypeEnum.static"
}
]
},
{
"nodeId": "7BdojPlukIQw",
"name": "AI 对话",
"intro": "AI 大模型对话",
"avatar": "core/workflow/template/aiChat",
"flowNodeType": "chatNode",
"showStatus": true,
"position": {
"x": 2701.1267277679685,
"y": -767.8956312653042
},
"version": "481",
"inputs": [
{
"key": "model",
"renderTypeList": ["settingLLMModel", "reference"],
"label": "core.module.input.label.aiModel",
"valueType": "string",
"value": "gpt-4o-mini"
},
{
"key": "temperature",
"renderTypeList": ["hidden"],
"label": "",
"value": 3,
"valueType": "number",
"min": 0,
"max": 10,
"step": 1
},
{
"key": "maxToken",
"renderTypeList": ["hidden"],
"label": "",
"value": 1950,
"valueType": "number",
"min": 100,
"max": 4000,
"step": 50
},
{
"key": "isResponseAnswerText",
"renderTypeList": ["hidden"],
"label": "",
"value": true,
"valueType": "boolean"
},
{
"key": "quoteTemplate",
"renderTypeList": ["hidden"],
"label": "",
"valueType": "string"
},
{
"key": "quotePrompt",
"renderTypeList": ["hidden"],
"label": "",
"valueType": "string"
},
{
"key": "systemPrompt",
"renderTypeList": ["textarea", "reference"],
"max": 3000,
"valueType": "string",
"label": "core.ai.Prompt",
"description": "core.app.tip.systemPromptTip",
"placeholder": "core.app.tip.chatNodeSystemPromptTip",
"value": ""
},
{
"key": "history",
"renderTypeList": ["numberInput", "reference"],
"valueType": "chatHistory",
"label": "core.module.input.label.chat history",
"required": true,
"min": 0,
"max": 30,
"value": 6
},
{
"key": "userChatInput",
"renderTypeList": ["reference", "textarea"],
"valueType": "string",
"label": "用户问题",
"required": true,
"toolDescription": "用户问题",
"value": ["workflowStartNodeId", "userChatInput"]
},
{
"key": "quoteQA",
"renderTypeList": ["settingDatasetQuotePrompt"],
"label": "",
"debugLabel": "知识库引用",
"description": "",
"valueType": "datasetQuote",
"value": ["MNMMMIjjWyMU", "quoteQA"]
}
],
"outputs": [
{
"id": "history",
"key": "history",
"label": "core.module.output.label.New context",
"description": "core.module.output.description.New context",
"valueType": "chatHistory",
"type": "FlowNodeOutputTypeEnum.static"
},
{
"id": "answerText",
"key": "answerText",
"label": "core.module.output.label.Ai response content",
"description": "core.module.output.description.Ai response content",
"valueType": "string",
"type": "FlowNodeOutputTypeEnum.static"
}
]
},
{
"nodeId": "rvbo634w3AYj",
"name": "问题分类",
"intro": "根据用户的历史记录和当前问题判断该次提问的类型。可以添加多组问题类型,下面是一个模板例子:\n类型1: 打招呼\n类型2: 关于商品\"使用\"问题\n类型3: 关于商品\"购买\"问题\n类型4: 其他问题",
"avatar": "core/workflow/template/questionClassify",
"flowNodeType": "classifyQuestion",
"showStatus": true,
"position": {
"x": 1020.9667229609946,
"y": -385.0060974413916
},
"version": "481",
"inputs": [
{
"key": "model",
"renderTypeList": ["selectLLMModel", "reference"],
"label": "core.module.input.label.aiModel",
"required": true,
"valueType": "string",
"llmModelType": "classify",
"value": "gpt-4o-mini"
},
{
"key": "systemPrompt",
"renderTypeList": ["textarea", "reference"],
"max": 3000,
"valueType": "string",
"label": "core.module.input.label.Background",
"description": "core.module.input.description.Background",
"placeholder": "core.module.input.placeholder.Classify background",
"value": ""
},
{
"key": "history",
"renderTypeList": ["numberInput", "reference"],
"valueType": "chatHistory",
"label": "core.module.input.label.chat history",
"required": true,
"min": 0,
"max": 30,
"value": 6
},
{
"key": "userChatInput",
"renderTypeList": ["reference", "textarea"],
"valueType": "string",
"label": "用户问题",
"required": true,
"value": ["workflowStartNodeId", "userChatInput"]
},
{
"key": "agents",
"renderTypeList": ["custom"],
"valueType": "any",
"label": "",
"value": [
{
"value": "关于电影《星际穿越》的问题",
"key": "wqre"
},
{
"value": "打招呼、问候等问题",
"key": "sdfa"
},
{
"value": "其他问题",
"key": "agex"
}
]
}
],
"outputs": [
{
"id": "cqResult",
"key": "cqResult",
"label": "分类结果",
"valueType": "string",
"type": "FlowNodeOutputTypeEnum.static"
}
]
},
{
"nodeId": "7kwgL1dVlwG6",
"name": "指定回复",
"intro": "该模块可以直接回复一段指定的内容。常用于引导、提示。非字符串内容传入时,会转成字符串进行输出。",
"avatar": "core/workflow/template/reply",
"flowNodeType": "answerNode",
"position": {
"x": 1874.9167551056487,
"y": 434.98431875888207
},
"version": "481",
"inputs": [
{
"key": "text",
"renderTypeList": ["textarea", "reference"],
"valueType": "string",
"label": "core.module.input.label.Response content",
"description": "core.module.input.description.Response content",
"placeholder": "core.module.input.description.Response content",
"selectedTypeIndex": 1,
"value": ["rvbo634w3AYj", "cqResult"]
}
],
"outputs": []
},
{
"nodeId": "MNMMMIjjWyMU",
"name": "知识库搜索",
"intro": "调用\"语义检索\"和\"全文检索\"能力,从\"知识库\"中查找可能与问题相关的参考内容",
"avatar": "core/workflow/template/datasetSearch",
"flowNodeType": "datasetSearchNode",
"showStatus": true,
"position": {
"x": 1851.010152279949,
"y": -613.3555232387284
},
"version": "481",
"inputs": [
{
"key": "datasets",
"renderTypeList": ["selectDataset", "reference"],
"label": "core.module.input.label.Select dataset",
"value": [],
"valueType": "selectDataset",
"list": [],
"required": true
},
{
"key": "similarity",
"renderTypeList": ["selectDatasetParamsModal"],
"label": "",
"value": 0.4,
"valueType": "number"
},
{
"key": "limit",
"renderTypeList": ["hidden"],
"label": "",
"value": 1500,
"valueType": "number"
},
{
"key": "searchMode",
"renderTypeList": ["hidden"],
"label": "",
"valueType": "string",
"value": "embedding"
},
{
"key": "usingReRank",
"renderTypeList": ["hidden"],
"label": "",
"valueType": "boolean",
"value": false
},
{
"key": "datasetSearchUsingExtensionQuery",
"renderTypeList": ["hidden"],
"label": "",
"valueType": "boolean",
"value": true
},
{
"key": "datasetSearchExtensionModel",
"renderTypeList": ["hidden"],
"label": "",
"valueType": "string"
},
{
"key": "datasetSearchExtensionBg",
"renderTypeList": ["hidden"],
"label": "",
"valueType": "string",
"value": ""
},
{
"key": "userChatInput",
"renderTypeList": ["reference", "textarea"],
"valueType": "string",
"label": "用户问题",
"required": true,
"toolDescription": "需要检索的内容",
"value": ["workflowStartNodeId", "userChatInput"]
}
],
"outputs": [
{
"id": "quoteQA",
"key": "quoteQA",
"label": "core.module.Dataset quote.label",
"description": "特殊数组格式,搜索结果为空时,返回空数组。",
"type": "FlowNodeOutputTypeEnum.static",
"valueType": "datasetQuote"
}
]
}
],
"edges": [
{
"source": "workflowStartNodeId",
"target": "rvbo634w3AYj",
"sourceHandle": "workflowStartNodeId-source-right",
"targetHandle": "rvbo634w3AYj-target-left"
},
{
"source": "rvbo634w3AYj",
"target": "7kwgL1dVlwG6",
"sourceHandle": "rvbo634w3AYj-source-agex",
"targetHandle": "7kwgL1dVlwG6-target-left"
},
{
"source": "rvbo634w3AYj",
"target": "MNMMMIjjWyMU",
"sourceHandle": "rvbo634w3AYj-source-wqre",
"targetHandle": "MNMMMIjjWyMU-target-left"
},
{
"source": "MNMMMIjjWyMU",
"target": "7BdojPlukIQw",
"sourceHandle": "MNMMMIjjWyMU-source-right",
"targetHandle": "7BdojPlukIQw-target-left"
},
{
"source": "rvbo634w3AYj",
"target": "7kwgL1dVlwG6",
"sourceHandle": "rvbo634w3AYj-source-sdfa",
"targetHandle": "7kwgL1dVlwG6-target-left"
}
],
"chatConfig": {
"scheduledTriggerConfig": {
"cronString": "",
"timezone": "Asia/Shanghai",
"defaultPrompt": ""
},
"welcomeText": "你好,我是知识库助手,请不要忘记选择知识库噢~\n[你是谁]\n[如何使用]"
}
}
}

View File

@@ -1 +0,0 @@
<svg t="1730275413281" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="16472" width="200" height="200"><path d="M919 233.6s-46.2-41.4-93.4-63.2c-51.4-26.6-124-25.4-124-25.4s-51.2-4.1-103.6 22.6c-52.4 26.7-91.1 62.9-91.1 62.9s-53.5-44.1-100.8-67.2S290.7 145 290.7 145s-64.9 3.6-106.7 23.8c-41.8 20.3-80.1 68.4-80.1 68.4v621.4s59-55.1 94.2-70.6c35.2-15.5 90.3-15 90.3-15s66 8.5 105.3 25.7c39.3 17.1 113.2 80.7 113.2 80.7s47.1-54.2 84.9-74.5c64.1-35.7 127.7-30.6 127.7-30.6s39.1 1.8 93.4 23.8c49.4 20 107.1 61.7 107.1 61.7l-1-626.2zM486.9 811s-101.1-86.3-198.4-84.3c-91.7 1.8-126.8 26.9-150.6 52.5 0.9-30.2-0.4-519.3-0.4-519.3s35.2-68.4 154.1-73.9c101-9.2 188.2 67.8 193.6 82.5 2.5 19.2 1.7 542.5 1.7 542.5z m394.8-20.2c-23.8-25.7-87.5-64.2-179.1-66-97.3-1.9-173.9 87.1-173.9 87.1s-0.8-523.3 1.7-542.5c8.5-14.7 74.7-88 193.6-82.5 103 4.8 159.1 72.2 159.1 72.2s-1.3 501.7-1.4 531.7z" fill="#000000" p-id="16473"></path><path d="M544.6 609.5h-65.1c-6.6 0-12-5.4-12-12v-171c0-6.6 5.4-12 12-12h65.1c6.6 0 12 5.4 12 12v171c0 6.6-5.4 12-12 12z" fill="#000000" p-id="16474"></path><path d="M544.6 609.5h-65.1c-6.6 0-12-5.4-12-12v-171c0-6.6 5.4-12 12-12h65.1c6.6 0 12 5.4 12 12v171c0 6.6-5.4 12-12 12z" fill="#000000" p-id="16475"></path></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1,503 +0,0 @@
{
"name": "汉语新解",
"intro": "生成汉语释义图",
"author": "",
"avatar": "/appMarketTemplates/Chinese/avatar.svg",
"tags": ["roleplay"],
"type": "advanced",
"workflow": {
"nodes": [
{
"nodeId": "userGuide",
"name": "common:core.module.template.system_config",
"intro": "common:core.module.template.system_config_info",
"avatar": "core/workflow/template/systemConfig",
"flowNodeType": "userGuide",
"position": {
"x": 262.2732338817093,
"y": -476.00241136598146
},
"version": "481",
"inputs": [
{
"key": "welcomeText",
"renderTypeList": ["hidden"],
"valueType": "string",
"label": "core.app.Welcome Text",
"value": ""
},
{
"key": "variables",
"renderTypeList": ["hidden"],
"valueType": "any",
"label": "core.app.Chat Variable",
"value": []
},
{
"key": "questionGuide",
"valueType": "any",
"renderTypeList": ["hidden"],
"label": "core.app.Question Guide",
"value": {
"open": false
}
},
{
"key": "tts",
"renderTypeList": ["hidden"],
"valueType": "any",
"label": "",
"value": {
"type": "web"
}
},
{
"key": "whisper",
"renderTypeList": ["hidden"],
"valueType": "any",
"label": "",
"value": {
"open": false,
"autoSend": false,
"autoTTSResponse": false
}
},
{
"key": "scheduleTrigger",
"renderTypeList": ["hidden"],
"valueType": "any",
"label": "",
"value": null
}
],
"outputs": []
},
{
"nodeId": "448745",
"name": "common:core.module.template.work_start",
"intro": "",
"avatar": "core/workflow/template/workflowStart",
"flowNodeType": "workflowStart",
"position": {
"x": 632.368838596004,
"y": -347.7446492944009
},
"version": "481",
"inputs": [
{
"key": "userChatInput",
"renderTypeList": ["reference", "textarea"],
"valueType": "string",
"label": "common:core.module.input.label.user question",
"required": true,
"toolDescription": "用户问题",
"debugLabel": ""
}
],
"outputs": [
{
"id": "userChatInput",
"key": "userChatInput",
"label": "common:core.module.input.label.user question",
"type": "static",
"valueType": "string",
"description": ""
}
]
},
{
"nodeId": "bg853CwHAw4a",
"name": "AI 对话",
"intro": "AI 大模型对话",
"avatar": "core/workflow/template/aiChat",
"flowNodeType": "chatNode",
"showStatus": true,
"position": {
"x": 1318.728987052518,
"y": -612.0024113659815
},
"version": "481",
"inputs": [
{
"key": "model",
"renderTypeList": ["settingLLMModel", "reference"],
"label": "AI 模型",
"valueType": "string",
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": "",
"value": "claude-3-5-sonnet-20240620"
},
{
"key": "temperature",
"renderTypeList": ["hidden"],
"label": "",
"value": 0,
"valueType": "number",
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "maxToken",
"renderTypeList": ["hidden"],
"label": "",
"value": 2000,
"valueType": "number",
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "isResponseAnswerText",
"renderTypeList": ["hidden"],
"label": "",
"value": false,
"valueType": "boolean",
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "aiChatQuoteRole",
"renderTypeList": ["hidden"],
"label": "",
"valueType": "string",
"value": "system",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "quoteTemplate",
"renderTypeList": ["hidden"],
"label": "",
"valueType": "string",
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "quotePrompt",
"renderTypeList": ["hidden"],
"label": "",
"valueType": "string",
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "aiChatVision",
"renderTypeList": ["hidden"],
"label": "",
"valueType": "boolean",
"value": false,
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "systemPrompt",
"renderTypeList": ["textarea", "reference"],
"max": 3000,
"valueType": "string",
"label": "提示词",
"description": "core.app.tip.chatNodeSystemPromptTip",
"placeholder": "core.app.tip.chatNodeSystemPromptTip",
"valueDesc": "",
"debugLabel": "",
"toolDescription": "",
"value": "{提示词 START\n;; 作者: 李继刚\n;; 版本: 0.3\n;; 模型: Claude Sonnet\n;; 用途: 将一个汉语词汇进行全新角度的解释\n\n;; 设定如下内容为你的 *System Prompt*\n(defun 新汉语老师 ()\n \"你是年轻人,批判现实,思考深刻,语言风趣\"\n (风格 . (\"Oscar Wilde\" \"鲁迅\" \"罗永浩\"))\n (擅长 . 一针见血)\n (表达 . 隐喻)\n (批判 . 讽刺幽默))\n\n(defun 汉语新解 (用户输入)\n \"你会用一个特殊视角来解释一个词汇\"\n (let (解释 (精练表达\n (隐喻 (一针见血 (辛辣讽刺 (抓住本质 用户输入))))))\n (few-shots (委婉 . \"刺向他人时, 决定在剑刃上撒上止痛药。\"))\n (SVG-Card 解释)))\n\n(defun SVG-Card (解释)\n \"输出SVG 卡片\"\n (setq design-rule \"合理使用负空间,整体排版要有呼吸感\"\n design-principles '(干净 简洁 典雅))\n\n (设置画布 '(宽度 400 高度 600 边距 20))\n (标题字体 '毛笔楷体)\n (自动缩放 '(最小字号 16))\n\n (配色风格 '((背景色 (蒙德里安风格 设计感)))\n (主要文字 (汇文明朝体 粉笔灰))\n (装饰图案 随机几何图))\n\n (卡片元素 ((居中标题 \"汉语新解\")\n 分隔线\n (排版输出 用户输入 英文 日语)\n 解释\n (线条图 (批判内核 解释))\n (极简总结 线条图))))\n\n(defun start ()\n \"启动时运行\"\n (let (system-role 新汉语老师)\n (print \"说吧, 他们又用哪个词来忽悠你了?\")))\n\n;; 运行规则\n;; 1. 启动时必须运行 (start) 函数\n;; 2. 之后调用主函数 (汉语新解 用户输入)\n提示词 END}\n\n直接生成 svg 完整代码,我会复制,需要你用代码块)\n除此之外不要有多余的解释不要在开头加上任何说明\n解释的内容自动加入换行标签例如\n<tspan x=\"50%\" dy=\"25\" font-size=\"18\" fill=\"#8B4513\">文字1</tspan>\n <tspan x=\"50%\" dy=\"25\" font-size=\"18\" fill=\"#8B4513\">文字12</tspan>"
},
{
"key": "history",
"renderTypeList": ["numberInput", "reference"],
"valueType": "chatHistory",
"label": "聊天记录",
"description": "workflow:max_dialog_rounds",
"required": true,
"min": 0,
"max": 50,
"value": 0,
"valueDesc": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "quoteQA",
"renderTypeList": ["settingDatasetQuotePrompt"],
"label": "",
"debugLabel": "知识库引用",
"description": "",
"valueType": "datasetQuote",
"valueDesc": "",
"toolDescription": ""
},
{
"key": "stringQuoteText",
"renderTypeList": ["reference", "textarea"],
"label": "文档引用",
"debugLabel": "文档引用",
"description": "app:document_quote_tip",
"valueType": "string",
"valueDesc": "",
"toolDescription": ""
},
{
"key": "userChatInput",
"renderTypeList": ["reference", "textarea"],
"valueType": "string",
"label": "用户问题",
"required": true,
"toolDescription": "用户问题",
"valueDesc": "",
"description": "",
"debugLabel": "",
"value": ["448745", "userChatInput"]
}
],
"outputs": [
{
"id": "history",
"key": "history",
"required": true,
"label": "common:core.module.output.label.New context",
"description": "将本次回复内容拼接上历史记录,作为新的上下文返回",
"valueType": "chatHistory",
"valueDesc": "{\n obj: System | Human | AI;\n value: string;\n}[]",
"type": "static"
},
{
"id": "answerText",
"key": "answerText",
"required": true,
"label": "common:core.module.output.label.Ai response content",
"description": "将在 stream 回复完毕后触发",
"valueType": "string",
"type": "static"
}
]
},
{
"nodeId": "sbVUb0efY6Fm",
"name": "代码运行",
"intro": "执行一段简单的脚本代码,通常用于进行复杂的数据处理。",
"avatar": "core/workflow/template/codeRun",
"flowNodeType": "code",
"showStatus": true,
"position": {
"x": 2210.2574140398733,
"y": -621.0024113659815
},
"version": "482",
"inputs": [
{
"key": "system_addInputParam",
"renderTypeList": ["addInputParam"],
"valueType": "dynamic",
"label": "",
"required": false,
"description": "workflow:these_variables_will_be_input_parameters_for_code_execution",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"valueDesc": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "codeType",
"renderTypeList": ["hidden"],
"label": "",
"value": "js",
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "code",
"renderTypeList": ["custom"],
"label": "",
"value": "function main({svg_str}){\n\n // 使用正则表达式匹配代码块中的内容\n const match = svg_str.match(/```[\\w]*\\n([\\s\\S]*?)```/);\n\n if (!match) {\n // 如果没有匹配到代码块,返回一个错误信息或空结果\n return {\n result: null,\n error: \"未找到有效的代码块标记。\"\n };\n }\n\n // 提取代码块中的 SVG 内容\n const extractedSvg = match[1].trim();\n \n const base64 = strToBase64(extractedSvg,'data:image/svg+xml;base64,')\n\n return {\n result: base64\n }\n}",
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"renderTypeList": ["reference"],
"valueType": "string",
"canEdit": true,
"key": "svg_str",
"label": "svg_str",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"required": true,
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": "",
"value": ["bg853CwHAw4a", "answerText"]
}
],
"outputs": [
{
"id": "system_rawResponse",
"key": "system_rawResponse",
"label": "workflow:full_response_data",
"valueType": "object",
"type": "static",
"description": ""
},
{
"id": "error",
"key": "error",
"label": "workflow:execution_error",
"description": "代码运行错误信息,成功时返回空",
"valueType": "object",
"type": "static"
},
{
"id": "system_addOutputParam",
"key": "system_addOutputParam",
"type": "dynamic",
"valueType": "dynamic",
"label": "",
"customFieldConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": false
},
"description": "将代码中 return 的对象作为输出,传递给后续的节点。变量名需要对应 return 的 key",
"valueDesc": ""
},
{
"id": "qLUQfhG0ILRX",
"type": "dynamic",
"key": "result",
"valueType": "string",
"label": "result",
"valueDesc": "",
"description": ""
}
]
},
{
"nodeId": "cPh2VZnVxjQ8",
"name": "指定回复",
"intro": "该模块可以直接回复一段指定的内容。常用于引导、提示。非字符串内容传入时,会转成字符串进行输出。",
"avatar": "core/workflow/template/reply",
"flowNodeType": "answerNode",
"position": {
"x": 2911.2230784647795,
"y": -411.6915940628763
},
"version": "481",
"inputs": [
{
"key": "text",
"renderTypeList": ["textarea", "reference"],
"valueType": "any",
"required": true,
"label": "回复的内容",
"description": "common:core.module.input.description.Response content",
"placeholder": "common:core.module.input.description.Response content",
"valueDesc": "",
"debugLabel": "",
"toolDescription": "",
"value": "SVG\n\n{{$bg853CwHAw4a.answerText$}}\n\n卡片\n\n![]({{$sbVUb0efY6Fm.qLUQfhG0ILRX$}})"
}
],
"outputs": []
}
],
"edges": [
{
"source": "bg853CwHAw4a",
"target": "sbVUb0efY6Fm",
"sourceHandle": "bg853CwHAw4a-source-right",
"targetHandle": "sbVUb0efY6Fm-target-left"
},
{
"source": "448745",
"target": "bg853CwHAw4a",
"sourceHandle": "448745-source-right",
"targetHandle": "bg853CwHAw4a-target-left"
},
{
"source": "sbVUb0efY6Fm",
"target": "cPh2VZnVxjQ8",
"sourceHandle": "sbVUb0efY6Fm-source-right",
"targetHandle": "cPh2VZnVxjQ8-target-left"
}
],
"chatConfig": {
"variables": [],
"scheduledTriggerConfig": {
"cronString": "",
"timezone": "Asia/Shanghai",
"defaultPrompt": ""
},
"_id": "66f0f7540a40cd1f97da9dd6"
}
}
}

View File

@@ -1 +0,0 @@
<svg t="1719284028629" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4376" width="256" height="256"><path d="M381.5 483.7c0-53.1 43.2-96.3 96.3-96.3h85v-187c0-9.4-7.6-17-17-17H137.9c-9.4 0-17 7.6-17 17v283.3c0 9.4 7.6 17 17 17h73.6v32.1l64.3-32.1h105.7v-17z" fill="#9ADCCF" p-id="4377"></path><path d="M747.7 784h-270c-9.4 0-17-7.6-17-17V483.7c0-9.4 7.6-17 17-17h407.9c9.4 0 17 7.6 17 17V767c0 9.4-7.6 17-17 17H812v32.1L747.7 784z" fill="#FFF370" p-id="4378"></path><path d="M341.8 523.4h-56.7c-2.6 0-5.2 0.6-7.6 1.8l-88.7 44.4v-29.2c0-9.4-7.6-17-17-17h-34c-21.9 0-39.7-17.8-39.7-39.7V200.5c0-21.9 17.8-39.7 39.7-39.7h407.9c21.9 0 39.7 17.8 39.7 39.7v147.3c0 9.4 7.6 17 17 17s17-7.6 17-17V200.5c0-40.6-33-73.6-73.6-73.6H137.9c-40.6 0-73.6 33-73.6 73.6v283.3c0 40.6 33 73.6 73.6 73.6h17V597c0 5.9 3 11.4 8.1 14.5 2.7 1.7 5.8 2.5 8.9 2.5 2.6 0 5.2-0.6 7.6-1.8l109.7-54.9h52.6c9.4 0 17-7.6 17-17 0-9.3-7.6-16.9-17-16.9z" fill="#4F3D3B" p-id="4379"></path><path d="M885.7 410.1h-65.8c-9.4 0-17 7.6-17 17s7.6 17 17 17h65.8c21.9 0 39.7 17.8 39.7 39.7V767c0 21.9-17.8 39.7-39.7 39.7h-34c-9.4 0-17 7.6-17 17v29.2L746 808.4c-2.4-1.2-5-1.8-7.6-1.8H477.8c-21.9 0-39.7-17.8-39.7-39.7V483.7c0-21.9 17.8-39.7 39.7-39.7h249.3c9.4 0 17-7.6 17-17s-7.6-17-17-17H477.8c-12.9 0-25 3.3-35.5 9.2l-85-180.7c-2.8-6-8.8-9.8-15.4-9.8s-12.6 3.8-15.4 9.8l-90.6 192.6c-4 8.5-0.3 18.6 8.1 22.6 8.5 4 18.6 0.3 22.6-8.1l19.4-41.2h111.7l18.4 39.1c-7.6 11.6-12 25.4-12 40.2V767c0 40.6 33 73.6 73.6 73.6h256.6L844 895.5c2.4 1.2 5 1.8 7.6 1.8 3.1 0 6.2-0.9 8.9-2.5 5-3.1 8.1-8.6 8.1-14.5v-39.7h17c40.6 0 73.6-33 73.6-73.6V483.7c0.1-40.6-32.9-73.6-73.5-73.6z m-583.8-39.7l39.9-84.7 39.9 84.7h-79.8zM358 733.9c-0.2-0.6-0.3-1.1-0.5-1.7-0.2-0.6-0.6-1.1-0.9-1.7-0.2-0.4-0.4-0.8-0.7-1.2-1.2-1.9-2.9-3.5-4.7-4.7-0.4-0.3-0.8-0.4-1.2-0.6-0.6-0.3-1.1-0.7-1.7-0.9-0.5-0.2-1.1-0.3-1.7-0.5-0.5-0.2-1-0.3-1.5-0.4-1.1-0.2-2.2-0.3-3.3-0.3H241c-21.9 0-39.7-17.8-39.7-39.7v-17.1c0-9.4-7.6-17-17-17s-17 7.6-17 17V682c0 40.6 33 73.6 73.6 73.6h59.8L284.5 772c-6.6 6.6-6.6 17.4 0 24 3.3 3.3 7.7 5 12 5s8.7-1.7 12-5l45.3-45.3c0.8-0.8 1.5-1.7 2.1-2.6 0.3-0.4 0.4-0.8 0.7-1.2 0.3-0.6 0.6-1.1 0.9-1.7 0.2-0.5 0.4-1.1 0.5-1.7 0.1-0.5 0.3-1 0.4-1.5 0.4-2.2 0.4-4.5 0-6.7 0-0.5-0.2-1-0.4-1.4zM665.5 222.3c0.2 0.6 0.3 1.1 0.5 1.7 0.2 0.6 0.6 1.1 0.9 1.7 0.2 0.4 0.4 0.8 0.7 1.2 1.2 1.9 2.9 3.5 4.7 4.7 0.4 0.3 0.8 0.4 1.2 0.6 0.6 0.3 1.1 0.7 1.7 0.9 0.5 0.2 1.1 0.3 1.7 0.5 0.5 0.2 1 0.3 1.5 0.4 1.1 0.2 2.2 0.3 3.3 0.3h100.8c21.9 0 39.7 17.8 39.7 39.7v73.6c0 9.4 7.6 17 17 17s17-7.6 17-17V274c0-40.6-33-73.6-73.6-73.6h-59.8l16.3-16.3c6.6-6.6 6.6-17.4 0-24-6.6-6.6-17.4-6.6-24 0l-45.3 45.3c-0.8 0.8-1.5 1.7-2.1 2.6-0.3 0.4-0.4 0.8-0.7 1.2-0.3 0.6-0.6 1.1-0.9 1.7-0.2 0.5-0.4 1.1-0.5 1.7-0.1 0.5-0.3 1-0.4 1.5-0.4 2.2-0.4 4.5 0 6.7 0 0.5 0.2 1 0.3 1.5z" fill="#4F3D3B" p-id="4380"></path><path d="M681.7 750c9.4 0 17-7.6 17-17v-45.3H778c9.4 0 17-7.6 17-17V580c0-9.4-7.6-17-17-17h-79.3v-45.3c0-9.4-7.6-17-17-17s-17 7.6-17 17V563h-79.3c-9.4 0-17 7.6-17 17v90.6c0 9.4 7.6 17 17 17h79.3V733c0 9.4 7.6 17 17 17z m17-153H761v56.7h-62.3V597z m-96.3 56.7V597h62.3v56.7h-62.3z" fill="#4F3D3B" p-id="4381"></path></svg>

Before

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -1,581 +0,0 @@
{
"name": "多轮翻译机器人",
"intro": "通过 4 轮翻译,提高翻译英文的质量",
"author": "",
"avatar": "/appMarketTemplates/TranslateRobot/avatar.svg",
"tags": ["office-services"],
"type": "advanced",
"workflow": {
"nodes": [
{
"nodeId": "userGuide",
"name": "系统配置",
"intro": "可以配置应用的系统参数",
"avatar": "/imgs/workflow/userGuide.png",
"flowNodeType": "userGuide",
"position": {
"x": 531.2422736065552,
"y": -486.7611729549753
},
"version": "481",
"inputs": [
{
"key": "welcomeText",
"renderTypeList": ["hidden"],
"valueType": "string",
"label": "core.app.Welcome Text",
"value": ""
},
{
"key": "variables",
"renderTypeList": ["hidden"],
"valueType": "any",
"label": "core.app.Chat Variable",
"value": []
},
{
"key": "questionGuide",
"valueType": "any",
"renderTypeList": ["hidden"],
"label": "core.app.Question Guide",
"value": {
"open": false
}
},
{
"key": "tts",
"renderTypeList": ["hidden"],
"valueType": "any",
"label": "",
"value": {
"type": "web"
}
},
{
"key": "whisper",
"renderTypeList": ["hidden"],
"valueType": "any",
"label": "",
"value": {
"open": false,
"autoSend": false,
"autoTTSResponse": false
}
},
{
"key": "scheduleTrigger",
"renderTypeList": ["hidden"],
"valueType": "any",
"label": "",
"value": null
}
],
"outputs": []
},
{
"nodeId": "448745",
"name": "流程开始",
"intro": "",
"avatar": "/imgs/workflow/userChatInput.svg",
"flowNodeType": "workflowStart",
"position": {
"x": 558.4082376415505,
"y": 123.72387429194112
},
"version": "481",
"inputs": [
{
"key": "userChatInput",
"renderTypeList": ["reference", "textarea"],
"valueType": "string",
"label": "用户问题",
"required": true,
"toolDescription": "用户问题"
}
],
"outputs": [
{
"id": "userChatInput",
"key": "userChatInput",
"label": "core.module.input.label.user question",
"type": "static",
"valueType": "string"
}
]
},
{
"nodeId": "loOvhld2ZTKa",
"name": "第一轮翻译",
"intro": "AI 大模型对话",
"avatar": "/imgs/workflow/AI.png",
"flowNodeType": "chatNode",
"showStatus": true,
"position": {
"x": 1748.8252410306534,
"y": -245.08260685989214
},
"version": "481",
"inputs": [
{
"key": "model",
"renderTypeList": ["settingLLMModel", "reference"],
"label": "core.module.input.label.aiModel",
"valueType": "string",
"value": "claude-3-5-sonnet-20240620"
},
{
"key": "temperature",
"renderTypeList": ["hidden"],
"label": "",
"value": 0,
"valueType": "number",
"min": 0,
"max": 10,
"step": 1
},
{
"key": "maxToken",
"renderTypeList": ["hidden"],
"label": "",
"value": 2000,
"valueType": "number",
"min": 100,
"max": 4000,
"step": 50
},
{
"key": "isResponseAnswerText",
"renderTypeList": ["hidden"],
"label": "",
"value": true,
"valueType": "boolean"
},
{
"key": "quoteTemplate",
"renderTypeList": ["hidden"],
"label": "",
"valueType": "string"
},
{
"key": "quotePrompt",
"renderTypeList": ["hidden"],
"label": "",
"valueType": "string"
},
{
"key": "systemPrompt",
"renderTypeList": ["textarea", "reference"],
"max": 3000,
"valueType": "string",
"label": "core.ai.Prompt",
"description": "core.app.tip.systemPromptTip",
"placeholder": "core.app.tip.chatNodeSystemPromptTip",
"value": "# Role: 资深英汉翻译专家\n\n## Background:\n你是一位经验丰富的英汉翻译专家,精通英汉互译,尤其擅长将英文文章译成流畅易懂的现代汉语。你曾多次带领团队完成大型翻译项目,译文广受好评。\n\n## Attention:\n- 翻译过程中要始终坚持\"信、达、雅\"的原则,但\"达\"尤为重要\n- 译文要符合现代汉语的表达习惯,通俗易懂,连贯流畅 \n- 避免使用过于文绉绉的表达和晦涩难懂的典故引用\n\n## Profile: \n- Author: 米开朗基杨 \n- Version: 0.2\n- Language: 中文\n- Description: 你是一位资深英汉翻译专家,精通英汉互译。你擅长将英文文章译成地道流畅的现代汉语,表达准确易懂,符合当代中文语言习惯。\n\n## Constraints:\n- 必须严格遵循四轮翻译流程:直译、意译、校审、定稿 \n- 译文要忠实原文,准确无误,不能遗漏或曲解原意\n- 译文应以现代白话文为主,避免过多使用文言文和古典诗词\n- 每一轮翻译前后必须添加【思考】和【翻译】标记\n- 最终译文使用Markdown的代码块呈现\n\n## Goals:\n- 通过四轮翻译流程,将英文原文译成高质量的现代汉语译文 \n- 译文要准确传达原文意思,语言表达力求浅显易懂,朗朗上口\n- 适度使用一些熟语俗语、流行网络用语等,增强译文的亲和力\n- 在直译的基础上,提供至少2个不同风格的意译版本供选择\n\n## Skills:\n- 精通英汉双语,具有扎实的语言功底和丰富的翻译经验\n- 擅长将英语表达习惯转换为地道自然的现代汉语\n- 对当代中文语言的发展变化有敏锐洞察,善于把握语言流行趋势\n\n## Workflow:\n1. 第一轮直译:逐字逐句忠实原文,不遗漏任何信息\n2. 第二轮意译:在直译的基础上用通俗流畅的现代汉语意译原文,至少提供2个不同风格的版本\n3. 第三轮校审:仔细审视译文,消除偏差和欠缺,使译文更加地道易懂 \n4. 第四轮定稿:择优选取,反复修改润色,最终定稿出一个简洁畅达、符合大众阅读习惯的译文\n\n## OutputFormat: \n- 每一轮翻译前用【思考】说明该轮要点\n- 每一轮翻译后用【翻译】呈现译文\n- 在\\`\\`\\`代码块中展示最终定稿译文\n\n## Suggestions:\n- 直译时力求忠实原文,但不要过于拘泥逐字逐句\n- 意译时在准确表达原意的基础上,用最朴实无华的现代汉语来表达 \n- 校审环节重点关注译文是否符合当代汉语表达习惯,是否通俗易懂\n- 定稿时适度采用一些熟语谚语、网络流行语等,使译文更接地气\n- 善于利用中文的灵活性,用不同的表述方式展现同一内容,提高译文的可读性\n\n## Initialization\n作为一名资深英汉翻译专家,你必须严格遵循翻译流程的各项要求。首先请向用户问好,介绍你将带领团队完成翻译任务,力求将英文原文译成通俗易懂的现代汉语。然后简要说明四轮翻译流程,请用户提供英文原文,开始进行翻译工作。"
},
{
"key": "history",
"renderTypeList": ["numberInput", "reference"],
"valueType": "chatHistory",
"label": "core.module.input.label.chat history",
"description": "最多携带多少轮对话记录",
"required": true,
"min": 0,
"max": 50,
"value": 6
},
{
"key": "userChatInput",
"renderTypeList": ["reference", "textarea"],
"valueType": "string",
"label": "用户问题",
"required": true,
"toolDescription": "用户问题",
"value": ["gBDvemE4FBhp", "system_text"]
},
{
"key": "quoteQA",
"renderTypeList": ["settingDatasetQuotePrompt"],
"label": "",
"debugLabel": "知识库引用",
"description": "",
"valueType": "datasetQuote"
}
],
"outputs": [
{
"id": "history",
"key": "history",
"required": true,
"label": "core.module.output.label.New context",
"description": "core.module.output.description.New context",
"valueType": "chatHistory",
"type": "static"
},
{
"id": "answerText",
"key": "answerText",
"required": true,
"label": "core.module.output.label.Ai response content",
"description": "core.module.output.description.Ai response content",
"valueType": "string",
"type": "static"
}
]
},
{
"nodeId": "w0oBbQ3YJHye",
"name": "代码运行",
"intro": "执行一段简单的脚本代码,通常用于进行复杂的数据处理。",
"avatar": "/imgs/workflow/code.svg",
"flowNodeType": "code",
"showStatus": true,
"position": {
"x": 2522.61682940854,
"y": -79.74569750380468
},
"version": "482",
"inputs": [
{
"key": "system_addInputParam",
"renderTypeList": ["addInputParam"],
"valueType": "dynamic",
"label": "",
"required": false,
"description": "这些变量会作为代码的运行的输入参数",
"editField": {
"key": true,
"valueType": true
},
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
}
},
{
"key": "codeType",
"renderTypeList": ["hidden"],
"label": "",
"value": "js"
},
{
"key": "code",
"renderTypeList": ["custom"],
"label": "",
"value": "function main({data1}) {\n const codeBlocks = data1.match(/```[\\s\\S]*?```/g);\n\n if (codeBlocks && codeBlocks.length > 0) {\n const lastCodeBlock = codeBlocks[codeBlocks.length - 1];\n const cleanedCodeBlock = lastCodeBlock.replace(/```[a-zA-Z]*|```/g, '').trim();\n \n return {\n result: cleanedCodeBlock\n };\n }\n\n return {\n result: '未截取到代码块内容'\n };\n}\n"
},
{
"key": "data1",
"valueType": "string",
"label": "data1",
"renderTypeList": ["reference"],
"description": "",
"canEdit": true,
"editField": {
"key": true,
"valueType": true
},
"value": ["loOvhld2ZTKa", "answerText"]
}
],
"outputs": [
{
"id": "system_addOutputParam",
"key": "system_addOutputParam",
"type": "dynamic",
"valueType": "dynamic",
"label": "",
"description": "将代码中 return 的对象作为输出,传递给后续的节点"
},
{
"id": "system_rawResponse",
"key": "system_rawResponse",
"label": "完整响应数据",
"valueType": "object",
"type": "static"
},
{
"id": "error",
"key": "error",
"label": "运行错误",
"description": "代码运行错误信息,成功时返回空",
"valueType": "object",
"type": "static"
},
{
"id": "qLUQfhG0ILRX",
"type": "dynamic",
"key": "result",
"valueType": "string",
"label": "result"
},
{
"id": "gR0mkQpJ4Og8",
"type": "dynamic",
"key": "data2",
"valueType": "string",
"label": "data2"
}
]
},
{
"nodeId": "foO69L5FOmDQ",
"name": "指定回复",
"intro": "该模块可以直接回复一段指定的内容。常用于引导、提示。非字符串内容传入时,会转成字符串进行输出。",
"avatar": "/imgs/workflow/reply.png",
"flowNodeType": "answerNode",
"position": {
"x": 3798.4479531204515,
"y": 116.03040242110023
},
"version": "481",
"inputs": [
{
"key": "text",
"renderTypeList": ["textarea", "reference"],
"valueType": "any",
"required": true,
"label": "core.module.input.label.Response content",
"description": "core.module.input.description.Response content",
"placeholder": "core.module.input.description.Response content",
"selectedTypeIndex": 1,
"value": ["bcqtxqxE2R6o", "system_text"]
}
],
"outputs": []
},
{
"nodeId": "gBDvemE4FBhp",
"name": "文本拼接",
"intro": "可对固定或传入的文本进行加工后输出,非字符串类型数据最终会转成字符串类型。",
"avatar": "/imgs/workflow/textEditor.svg",
"flowNodeType": "textEditor",
"position": {
"x": 1031.371061396644,
"y": 38.65839420088383
},
"version": "486",
"inputs": [
{
"key": "system_addInputParam",
"renderTypeList": ["addInputParam"],
"valueType": "dynamic",
"label": "",
"required": false,
"description": "可以引用其他节点的输出,作为文本拼接的变量,通过 {{字段名}} 来引用变量",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": false
}
},
{
"key": "system_textareaInput",
"renderTypeList": ["textarea"],
"valueType": "string",
"required": true,
"label": "拼接文本",
"placeholder": "可通过 {{字段名}} 来引用变量",
"value": "原文:\n\"\"\"\n{{q}}\n\"\"\""
},
{
"renderTypeList": ["reference"],
"valueType": "string",
"canEdit": true,
"key": "q",
"label": "q",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": false
},
"required": true,
"value": ["448745", "userChatInput"]
}
],
"outputs": [
{
"id": "system_text",
"key": "system_text",
"label": "拼接结果",
"type": "static",
"valueType": "string"
}
]
},
{
"nodeId": "bcqtxqxE2R6o",
"name": "合并输出结果",
"intro": "可对固定或传入的文本进行加工后输出,非字符串类型数据最终会转成字符串类型。",
"avatar": "/imgs/workflow/textEditor.svg",
"flowNodeType": "textEditor",
"position": {
"x": 3113.6227559936665,
"y": 12.909197647746709
},
"version": "486",
"inputs": [
{
"key": "system_addInputParam",
"renderTypeList": ["addInputParam"],
"valueType": "dynamic",
"label": "",
"required": false,
"description": "可以引用其他节点的输出,作为文本拼接的变量,通过 {{字段名}} 来引用变量",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": false
}
},
{
"key": "system_textareaInput",
"renderTypeList": ["textarea"],
"valueType": "string",
"required": true,
"label": "拼接文本",
"placeholder": "可通过 {{字段名}} 来引用变量",
"value": "****** \n\n最终翻译结果如下: \n\n```\n{{result}}\n```"
},
{
"renderTypeList": ["reference"],
"valueType": "string",
"canEdit": true,
"key": "result",
"label": "result",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": false
},
"required": true,
"value": ["w0oBbQ3YJHye", "qLUQfhG0ILRX"]
}
],
"outputs": [
{
"id": "system_text",
"key": "system_text",
"label": "拼接结果",
"type": "static",
"valueType": "string"
}
]
}
],
"edges": [
{
"source": "loOvhld2ZTKa",
"target": "w0oBbQ3YJHye",
"sourceHandle": "loOvhld2ZTKa-source-right",
"targetHandle": "w0oBbQ3YJHye-target-left"
},
{
"source": "448745",
"target": "gBDvemE4FBhp",
"sourceHandle": "448745-source-right",
"targetHandle": "gBDvemE4FBhp-target-left"
},
{
"source": "gBDvemE4FBhp",
"target": "loOvhld2ZTKa",
"sourceHandle": "gBDvemE4FBhp-source-right",
"targetHandle": "loOvhld2ZTKa-target-left"
},
{
"source": "w0oBbQ3YJHye",
"target": "bcqtxqxE2R6o",
"sourceHandle": "w0oBbQ3YJHye-source-right",
"targetHandle": "bcqtxqxE2R6o-target-left"
},
{
"source": "bcqtxqxE2R6o",
"target": "foO69L5FOmDQ",
"sourceHandle": "bcqtxqxE2R6o-source-right",
"targetHandle": "foO69L5FOmDQ-target-left"
}
],
"chatConfig": {
"scheduledTriggerConfig": {
"cronString": "",
"timezone": "Asia/Shanghai",
"defaultPrompt": ""
}
}
}
}

View File

@@ -1 +0,0 @@
<svg t="1730274418124" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11324" width="200" height="200"><path d="M247.898336 292.06743c-49.519903 16.505968-87.715829-27.923945-116.599772-27.661946-28.883944 0.259999-131.633743 46.589909-131.297744 83.727836 0.333999 36.465929 165.363677 142.011723 280.909451 26.465948l-33.011935-82.531838zM776.103304 292.06743c49.519903 16.505968 87.715829-27.923945 116.599772-27.661946 28.883944 0.261999 131.631743 46.591909 131.295744 83.729836-0.331999 36.465929-165.363677 142.011723-280.909451 26.465948l33.013935-82.533838z" fill="#464655" p-id="11325"></path><path d="M684.877482 770.428495c38.135926-165.673676 107.73379-474.585073 107.73379-522.530979 0-49.519903 0-66.025871-33.011936-99.037807-10.519979-10.519979-31.255939-20.937959-56.439889-30.64794-122.67576-47.297908-259.635493-47.297908-382.311254 0-25.183951 9.709981-45.91791 20.127961-56.439889 30.64794-33.011936 33.011936-33.011936 49.519903-33.011936 99.037807 0 47.945906 69.597864 356.857303 107.73379 522.530979l-28.155945 98.547808c-15.063971 52.721897 24.523952 105.205795 79.355845 105.205794h243.347524c54.831893 0 94.421816-52.481897 79.355845-105.205794l-28.155945-98.547808z" fill="#ffffff" p-id="11326"></path><path d="M597.995652 813.026412h-171.989664c-25.43595 0-46.05791 20.61996-46.05791 46.05791a117.56577 117.56577 0 0 0 16.753967 60.485882l18.339964 30.56794a49.515903 49.515903 0 0 0 42.461917 24.041953h108.987788a49.513903 49.513903 0 0 0 42.461917-24.041953l18.339964-30.56794a117.56577 117.56577 0 0 0 16.753967-60.485882c0.004-25.43795-20.61596-46.05791-46.05191-46.05791z" fill="#EBB4A0" p-id="11327"></path><path d="M445.973949 908.154226a16.447968 16.447968 0 0 1-11.669977-4.83599l-33.011936-33.011936a16.499968 16.499968 0 0 1 0-23.341954 16.499968 16.499968 0 0 1 23.341955 0l33.011935 33.011935a16.499968 16.499968 0 0 1 0 23.339955 16.447968 16.447968 0 0 1-11.671977 4.83799zM578.025691 908.154226a16.447968 16.447968 0 0 1-11.669977-4.83599 16.499968 16.499968 0 0 1 0-23.339955l33.013935-33.011935a16.499968 16.499968 0 0 1 23.341955 0 16.499968 16.499968 0 0 1 0 23.341954l-33.011936 33.011936a16.469968 16.469968 0 0 1-11.673977 4.83399z" fill="#D7A091" p-id="11328"></path><path d="M759.597336 148.857709c-10.37398-10.37398-30.68994-20.64796-55.401891-30.241941-15.26197-18.447964-36.863928-40.649921-60.143883-52.289898C611.039627 49.817903 511.99982 49.817903 511.99982 49.817903s-99.037807 0-132.051742 16.505967c-23.281955 11.641977-44.881912 33.841934-60.143883 52.289898-24.709952 9.593981-45.025912 19.867961-55.401891 30.241941-29.425943 29.425943-32.607936 45.89391-32.957936 84.159836 22.629956 1.051998 50.861901 2.223996 64.307874 2.223996 100.699803 0 185.275638-47.077908 213.973582-67.973868l-15.903968 66.171871c52.267898-0.473999 145.389716-46.715909 164.457678-67.523868l17.105967 65.873871c51.1959-33.415935 51.1959-49.921902 51.1959-49.921902s33.365935 0.04 63.583876 18.303964c-3.291994-18.069965-11.147978-31.891938-30.567941-51.3119z" fill="#FFDEB7" p-id="11329"></path><path d="M232.016367 215.05158c-0.587999 9.309982-0.625999 19.917961-0.625999 32.843936 0 47.945906 69.597864 356.857303 107.73379 522.530979 0 0 90.343824-175.895656 90.343823-208.907592 0-32.979936 16.455968-296.567421-197.451614-346.467323z" fill="#6B676E" p-id="11330"></path><path d="M297.426239 404.71721a41.265919 24.759952 90 1 0 49.519903 0 41.265919 24.759952 90 1 0-49.519903 0Z" fill="#464655" p-id="11331"></path><path d="M791.985273 215.05158c0.587999 9.309982 0.625999 19.917961 0.625999 32.843936 0 47.945906-69.597864 356.857303-107.73379 522.530979 0 0-90.343824-175.895656-90.343823-208.907592 0-32.979936-16.453968-296.567421 197.451614-346.467323z" fill="#6B676E" p-id="11332"></path><path d="M677.055498 404.71721a41.265919 24.759952 90 1 0 49.519903 0 41.265919 24.759952 90 1 0-49.519903 0Z" fill="#464655" p-id="11333"></path></svg>

Before

Width:  |  Height:  |  Size: 3.9 KiB

File diff suppressed because one or more lines are too long

View File

@@ -1,196 +0,0 @@
{
"name": "对话引导 + 变量",
"intro": "可以在对话开始发送一段提示,或者让用户填写一些内容,作为本次对话的变量",
"author": "",
"avatar": "core/workflow/template/systemConfig",
"tags": ["office-services"],
"type": "simple",
"weight": 1,
"workflow": {
"nodes": [
{
"nodeId": "userGuide",
"name": "系统配置",
"intro": "可以配置应用的系统参数",
"avatar": "/imgs/workflow/userGuide.png",
"flowNodeType": "userGuide",
"position": {
"x": 496.57560693988853,
"y": -490.7611729549753
},
"version": "481",
"inputs": [],
"outputs": []
},
{
"nodeId": "448745",
"name": "流程开始",
"intro": "",
"avatar": "/imgs/workflow/userChatInput.svg",
"flowNodeType": "workflowStart",
"position": {
"x": 558.4082376415505,
"y": 123.72387429194112
},
"version": "481",
"inputs": [
{
"key": "userChatInput",
"renderTypeList": ["FlowNodeInputTypeEnum.reference", "FlowNodeInputTypeEnum.textarea"],
"valueType": "string",
"label": "用户问题",
"required": true,
"toolDescription": "用户问题"
}
],
"outputs": [
{
"id": "userChatInput",
"key": "userChatInput",
"label": "core.module.input.label.user question",
"valueType": "string",
"type": "FlowNodeOutputTypeEnum.static"
}
]
},
{
"nodeId": "loOvhld2ZTKa",
"name": "AI 对话",
"intro": "AI 大模型对话",
"avatar": "/imgs/workflow/AI.png",
"flowNodeType": "chatNode",
"showStatus": true,
"position": {
"x": 1097.7317280958762,
"y": -244.16014496351386
},
"version": "481",
"inputs": [
{
"key": "model",
"renderTypeList": [
"FlowNodeInputTypeEnum.settingLLMModel",
"FlowNodeInputTypeEnum.reference"
],
"label": "core.module.input.label.aiModel",
"valueType": "string",
"value": "gpt-3.5-turbo"
},
{
"key": "temperature",
"renderTypeList": ["FlowNodeInputTypeEnum.hidden"],
"label": "",
"value": 0,
"valueType": "number",
"min": 0,
"max": 10,
"step": 1
},
{
"key": "maxToken",
"renderTypeList": ["FlowNodeInputTypeEnum.hidden"],
"label": "",
"value": 2000,
"valueType": "number",
"min": 100,
"max": 4000,
"step": 50
},
{
"key": "isResponseAnswerText",
"renderTypeList": ["FlowNodeInputTypeEnum.hidden"],
"label": "",
"value": true,
"valueType": "boolean"
},
{
"key": "quoteTemplate",
"renderTypeList": ["FlowNodeInputTypeEnum.hidden"],
"label": "",
"valueType": "string"
},
{
"key": "quotePrompt",
"renderTypeList": ["FlowNodeInputTypeEnum.hidden"],
"label": "",
"valueType": "string"
},
{
"key": "systemPrompt",
"renderTypeList": ["FlowNodeInputTypeEnum.textarea", "FlowNodeInputTypeEnum.reference"],
"max": 3000,
"valueType": "string",
"label": "core.ai.Prompt",
"description": "core.app.tip.systemPromptTip",
"placeholder": "core.app.tip.chatNodeSystemPromptTip",
"value": "请直接将我的问题翻译成{{language}},不需要回答问题。"
},
{
"key": "history",
"renderTypeList": [
"FlowNodeInputTypeEnum.numberInput",
"FlowNodeInputTypeEnum.reference"
],
"valueType": "chatHistory",
"label": "core.module.input.label.chat history",
"required": true,
"min": 0,
"max": 30,
"value": 6
},
{
"key": "userChatInput",
"renderTypeList": ["FlowNodeInputTypeEnum.reference", "FlowNodeInputTypeEnum.textarea"],
"valueType": "string",
"label": "用户问题",
"required": true,
"toolDescription": "用户问题",
"value": ["448745", "userChatInput"]
},
{
"key": "quoteQA",
"renderTypeList": ["FlowNodeInputTypeEnum.settingDatasetQuotePrompt"],
"label": "",
"debugLabel": "知识库引用",
"description": "",
"valueType": "datasetQuote"
}
],
"outputs": [
{
"id": "history",
"key": "history",
"label": "core.module.output.label.New context",
"description": "core.module.output.description.New context",
"valueType": "chatHistory",
"type": "FlowNodeOutputTypeEnum.static"
},
{
"id": "answerText",
"key": "answerText",
"label": "core.module.output.label.Ai response content",
"description": "core.module.output.description.Ai response content",
"valueType": "string",
"type": "FlowNodeOutputTypeEnum.static"
}
]
}
],
"edges": [
{
"source": "448745",
"target": "loOvhld2ZTKa",
"sourceHandle": "448745-source-right",
"targetHandle": "loOvhld2ZTKa-target-left"
}
],
"chatConfig": {
"welcomeText": "你好,我可以为你翻译各种语言,请告诉我你需要翻译成什么语言?",
"scheduledTriggerConfig": {
"cronString": "",
"timezone": "Asia/Shanghai",
"defaultPrompt": ""
}
}
}
}

View File

@@ -1 +0,0 @@
<svg t="1730274794897" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="12770" width="200" height="200"><path d="M841.5 378.7c-7.7-15-11.6-31.4-21-45.5-18-34.4-43-66.1-72.2-93.2-15.5-9.9-28.4-22.3-43.8-32.2-13.7-9-28.8-16.3-43-24.1-21.9-8.6-43-17.6-65.7-23.2-9-0.9-16.8-5.2-25.8-5.2l-37.4-4.3c-19.3-2.1-39.5-0.4-59.3 0.9-44.2 3.4-83.3 18-122.4 35.2-67.4 33.5-128 91.5-161.1 160.2-24.9 52-38.7 111.2-36.9 167.9 0 7.3 2.1 18 1.3 24.9 3.4 13.3 3 25.8 6.4 38.2 20.6 95.8 82 185.1 172.2 235.4 16.3 12 35.2 17.2 53.3 24.5 63.6 24.1 143.9 28.3 210.9 10.3 95.8-21.9 184.3-91.1 228.5-181.7 0.9-0.9 2.1-0.9 1.3-2.6 43-86.6 50.3-192.3 14.7-285.5z m-409.8-69.1c11.2-9.5 24.9-17.2 41.7-13.3 12 3 22.3 11.6 28.8 22.8 6 11.2 8.2 29.6 0 40.8-7.3 12.5-21.5 22.8-36.1 23.2-16.8-0.9-34.8-8.2-41.2-25.3-9.1-16-3.9-34.5 6.8-48.2z m386.1 335.9c-28.4 65.7-87.2 128.9-152.1 159.8-20.2 10.7-40.8 20.6-63.1 24.1-9.9 0-18.5 2.6-28.4 2.6l-25.8-2.6c-5.6-1.3-10.7-5.2-16.8-3.9-29.2-4.7-56.3-20.6-78.6-41.7-40.8-42.1-57.1-104.4-40.8-158.9 8.2-27.1 24.5-52.8 46-72.2 26.2-18.5 52.8-35.7 78.2-56.7 30.5-21.9 64.4-45.5 79-80.8 12-17.6 14.2-39.1 18-60.1 6.4-62.7-24.5-127.6-81.2-160.2-11.2-7.7-24.5-11.2-36.1-17.6-13.3-2.1-24.9-7.7-37.8-7.7-1.3-0.9 0.4-0.9-0.4-1.3 30.9-5.2 70.9-1.7 103.5 5.2C669 192 752.3 250.4 798.7 328.6l13.3 23.6c46.2 86.7 48.4 206.1 5.8 293.3z m-213.9 14.1c5.6 16.8 1.7 36.5-11.6 49-12.5 9-28.4 16.8-44.2 11.2-12-4.3-24.5-13.3-30.5-24.5-5.6-12-3.9-27.5 1.7-39.1 8.2-15.9 25.8-21.9 40.8-27.5 19.4-0.4 36.5 13.8 43.8 30.9z m44.2 246.2l-0.4 0.4-1.3-1.7c0.4 0.4 0.8 0.8 1.7 1.3z m-51.6 8.5h0.4-0.4z m-4.7 0.5c0 0.4-0.4 0.4-1.3 0.4 0.5-0.9 0.9-0.4 1.3-0.4z m0 0" fill="#000000" p-id="12771"></path></svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
<svg width="100%" height="100%" viewBox="0 0 89 32" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M52.308 3.07812H57.8465V4.92428H56.0003V6.77043H54.1541V10.4627H57.8465V12.3089H54.1541V25.232H52.308V27.0781H46.7695V25.232H48.6157V12.3089H46.7695V10.4627H48.6157V6.77043H50.4618V4.92428H52.308V3.07812Z" fill="currentColor"></path><path d="M79.3849 23.3858H81.2311V25.232H83.0772V27.0781H88.6157V25.232H86.7695V23.3858H84.9234V4.92428H79.3849V23.3858Z" fill="currentColor"></path><path d="M57.8465 14.155H59.6926V12.3089H61.5388V10.4627H70.7695V12.3089H74.4618V23.3858H76.308V25.232H78.1541V27.0781H72.6157V25.232H70.7695V23.3858H68.9234V14.155H67.0772V12.3089H65.2311V14.155H63.3849V23.3858H65.2311V25.232H67.0772V27.0781H61.5388V25.232H59.6926V23.3858H57.8465V14.155Z" fill="currentColor"></path><path d="M67.0772 25.232V23.3858H68.9234V25.232H67.0772Z" fill="currentColor"></path><rect opacity="0.22" x="7.38477" y="29.5391" width="2.46154" height="2.46154" fill="#5F4CD9"></rect><rect opacity="0.85" x="2.46094" y="19.6914" width="12.3077" height="2.46154" fill="#5F4CD9"></rect><rect x="4.92383" y="17.2305" width="9.84615" height="2.46154" fill="#5F4CD9"></rect><rect opacity="0.4" x="7.38477" y="27.0781" width="4.92308" height="2.46154" fill="#5F4CD9"></rect><rect opacity="0.7" y="22.1562" width="14.7692" height="2.46154" fill="#5F4CD9"></rect><rect opacity="0.5" x="7.38477" y="24.6133" width="7.38462" height="2.46154" fill="#5F4CD9"></rect><rect opacity="0.22" x="7.38477" y="12.3086" width="2.46154" height="2.46154" fill="#5F4CD9"></rect><rect opacity="0.85" x="2.46094" y="2.46094" width="12.3077" height="2.46154" fill="#5F4CD9"></rect><rect x="4.92383" width="9.84615" height="2.46154" fill="#5F4CD9"></rect><rect opacity="0.4" x="7.38477" y="9.84375" width="4.92308" height="2.46154" fill="#5F4CD9"></rect><rect opacity="0.7" y="4.92188" width="14.7692" height="2.46154" fill="#5F4CD9"></rect><rect opacity="0.5" x="7.38477" y="7.38281" width="7.38462" height="2.46154" fill="#5F4CD9"></rect><rect opacity="0.22" x="24.6152" y="29.5391" width="2.46154" height="2.46154" fill="#5F4CD9"></rect><rect opacity="0.85" x="19.6914" y="19.6914" width="12.3077" height="2.46154" fill="#5F4CD9"></rect><rect x="22.1543" y="17.2305" width="9.84615" height="2.46154" fill="#5F4CD9"></rect><rect opacity="0.4" x="24.6152" y="27.0781" width="4.92308" height="2.46154" fill="#5F4CD9"></rect><rect opacity="0.7" x="17.2305" y="22.1562" width="14.7692" height="2.46154" fill="#5F4CD9"></rect><rect opacity="0.5" x="24.6152" y="24.6133" width="7.38462" height="2.46154" fill="#5F4CD9"></rect><rect opacity="0.22" x="24.6152" y="12.3086" width="2.46154" height="2.46154" fill="#5F4CD9"></rect><rect opacity="0.85" x="19.6914" y="2.46094" width="12.3077" height="2.46154" fill="#5F4CD9"></rect><rect x="22.1543" width="9.84615" height="2.46154" fill="#5F4CD9"></rect><rect opacity="0.4" x="24.6152" y="9.84375" width="4.92308" height="2.46154" fill="#5F4CD9"></rect><rect opacity="0.7" x="17.2305" y="4.92188" width="14.7692" height="2.46154" fill="#5F4CD9"></rect><rect opacity="0.5" x="24.6152" y="7.38281" width="7.38462" height="2.46154" fill="#5F4CD9"></rect></svg>

Before

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -1,330 +0,0 @@
{
"name": "Flux 绘图",
"intro": "通过请求 Flux 接口绘图,需要有 api key",
"author": "",
"avatar": "/appMarketTemplates/flux/avatar.svg",
"type": "plugin",
"tags": ["image-generation"],
"workflow": {
"nodes": [
{
"nodeId": "pluginInput",
"name": "插件开始",
"intro": "可以配置插件需要哪些输入,利用这些输入来运行插件",
"avatar": "core/workflow/template/workflowStart",
"flowNodeType": "pluginInput",
"showStatus": false,
"position": {
"x": 503.3030871469042,
"y": -91.64434154072819
},
"version": "481",
"inputs": [
{
"renderTypeList": ["reference"],
"selectedTypeIndex": 0,
"valueType": "string",
"canEdit": true,
"key": "绘图提示词",
"label": "绘图提示词",
"description": "绘图提示词",
"required": true,
"toolDescription": "绘图提示词"
}
],
"outputs": [
{
"id": "绘图提示词",
"valueType": "string",
"key": "绘图提示词",
"label": "绘图提示词",
"type": "hidden"
}
]
},
{
"nodeId": "pluginOutput",
"name": "插件输出",
"intro": "自定义配置外部输出,使用插件时,仅暴露自定义配置的输出",
"avatar": "core/workflow/template/pluginOutput",
"flowNodeType": "pluginOutput",
"showStatus": false,
"position": {
"x": 1876.2082565873427,
"y": -110.14434154072819
},
"version": "481",
"inputs": [
{
"renderTypeList": ["reference"],
"valueType": "string",
"canEdit": true,
"key": "图片访问链接",
"label": "图片访问链接",
"description": "",
"value": ["tMvel910bnrJ", "pJXgWoTpPoMy"]
},
{
"renderTypeList": ["reference"],
"valueType": "object",
"canEdit": true,
"key": "error",
"label": "错误信息",
"description": "",
"value": ["tMvel910bnrJ", "error"]
}
],
"outputs": []
},
{
"nodeId": "tMvel910bnrJ",
"name": "HTTP 请求",
"intro": "可以发出一个 HTTP 请求,实现更为复杂的操作(联网搜索、数据库查询等)",
"avatar": "core/workflow/template/httpRequest",
"flowNodeType": "httpRequest468",
"showStatus": true,
"position": {
"x": 1138.1732435351091,
"y": -416.6443415407282
},
"version": "481",
"inputs": [
{
"key": "system_addInputParam",
"renderTypeList": ["addInputParam"],
"valueType": "dynamic",
"label": "",
"required": false,
"description": "common:core.module.input.description.HTTP Dynamic Input",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpMethod",
"renderTypeList": ["custom"],
"valueType": "string",
"label": "",
"value": "POST",
"required": true,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpTimeout",
"renderTypeList": ["custom"],
"valueType": "number",
"label": "",
"value": 30,
"min": 5,
"max": 600,
"required": true,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpReqUrl",
"renderTypeList": ["hidden"],
"valueType": "string",
"label": "",
"description": "common:core.module.input.description.Http Request Url",
"placeholder": "https://api.ai.com/getInventory",
"required": false,
"value": "https://fal.run/fal-ai/flux-pro",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpHeader",
"renderTypeList": ["custom"],
"valueType": "any",
"value": [
{
"key": "Authorization",
"type": "string",
"value": "Key {{apikey}}"
}
],
"label": "",
"description": "common:core.module.input.description.Http Request Header",
"placeholder": "common:core.module.input.description.Http Request Header",
"required": false,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpParams",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": [],
"label": "",
"required": false,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpJsonBody",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": "{\n \"prompt\": \"{{prompt}}\",\n \"image_size\": \"landscape_4_3\",\n \"num_inference_steps\": 28,\n \"guidance_scale\": 3.5\n}",
"label": "",
"required": false,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpFormBody",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": [],
"label": "",
"required": false,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpContentType",
"renderTypeList": ["hidden"],
"valueType": "string",
"value": "json",
"label": "",
"required": false,
"debugLabel": "",
"toolDescription": ""
},
{
"renderTypeList": ["reference"],
"valueType": "string",
"canEdit": true,
"key": "prompt",
"label": "prompt",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"required": true,
"value": ["pluginInput", "绘图提示词"]
}
],
"outputs": [
{
"id": "error",
"key": "error",
"label": "workflow:request_error",
"description": "HTTP请求错误信息成功时返回空",
"valueType": "object",
"type": "static"
},
{
"id": "httpRawResponse",
"key": "httpRawResponse",
"required": true,
"label": "workflow:raw_response",
"description": "HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。",
"valueType": "any",
"type": "static"
},
{
"id": "system_addOutputParam",
"key": "system_addOutputParam",
"type": "dynamic",
"valueType": "dynamic",
"label": "",
"customFieldConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": false
}
},
{
"id": "pJXgWoTpPoMy",
"valueType": "string",
"type": "dynamic",
"key": "images[0].url",
"label": "images[0].url"
}
]
},
{
"nodeId": "lSYsc889IXDr",
"name": "系统配置",
"intro": "",
"avatar": "core/workflow/template/systemConfig",
"flowNodeType": "pluginConfig",
"position": {
"x": 45.52914573588026,
"y": -110.14434154072819
},
"version": "4811",
"inputs": [],
"outputs": []
}
],
"edges": [
{
"source": "pluginInput",
"target": "tMvel910bnrJ",
"sourceHandle": "pluginInput-source-right",
"targetHandle": "tMvel910bnrJ-target-left"
},
{
"source": "tMvel910bnrJ",
"target": "pluginOutput",
"sourceHandle": "tMvel910bnrJ-source-right",
"targetHandle": "pluginOutput-target-left"
}
]
}
}

View File

@@ -1 +0,0 @@
<svg t="1730273562336" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4241" width="200" height="200"><path d="M960 512c0 97.76-28.704 185.216-85.664 263.264-56.96 78.016-130.496 131.84-220.64 161.856-10.304 1.824-18.368 0.448-22.848-4.032a22.4 22.4 0 0 1-7.2-17.504v-122.88c0-37.632-10.304-65.44-30.464-82.912a409.856 409.856 0 0 0 59.616-10.368 222.752 222.752 0 0 0 54.72-22.816c18.848-10.784 34.528-23.36 47.104-38.592 12.544-15.232 22.848-35.904 30.912-61.44 8.096-25.568 12.128-54.688 12.128-87.904 0-47.072-15.232-86.976-46.208-120.16 14.368-35.456 13.024-74.912-4.48-118.848-10.752-3.616-26.432-1.344-47.072 6.272s-38.56 16.16-53.824 25.568l-21.984 13.888c-36.32-10.304-73.536-15.232-112.096-15.232s-75.776 4.928-112.096 15.232a444.48 444.48 0 0 0-24.672-15.68c-10.336-6.272-26.464-13.888-48.896-22.432-21.952-8.96-39.008-11.232-50.24-8.064-17.024 43.936-18.368 83.424-4.032 118.848-30.496 33.632-46.176 73.536-46.176 120.608 0 33.216 4.032 62.336 12.128 87.456 8.032 25.12 18.368 45.76 30.496 61.44 12.544 15.68 28.224 28.704 47.072 39.04 18.848 10.304 37.216 17.92 54.72 22.816a409.6 409.6 0 0 0 59.648 10.368c-15.712 13.856-25.12 34.048-28.704 60.064a99.744 99.744 0 0 1-26.464 8.512 178.208 178.208 0 0 1-33.184 2.688c-13.024 0-25.568-4.032-38.144-12.544-12.544-8.512-23.296-20.64-32.256-36.32a97.472 97.472 0 0 0-28.256-30.496c-11.232-8.064-21.088-12.576-28.704-13.92l-11.648-1.792c-8.096 0-13.92 0.928-17.056 2.688-3.136 1.792-4.032 4.032-2.688 6.72s3.136 5.408 5.376 8.096 4.928 4.928 7.616 7.168l4.032 2.688c8.544 4.032 17.056 11.232 25.568 21.984 8.544 10.752 14.368 20.64 18.4 29.6l5.824 13.44c4.928 14.816 13.44 26.912 25.568 35.872 12.096 8.992 25.088 14.816 39.008 17.504 13.888 2.688 27.36 4.032 40.352 4.032s23.776-0.448 32.288-2.24l13.472-2.24c0 14.784 0 32.288 0.416 52.032 0 19.744 0.48 30.496 0.48 31.392a22.624 22.624 0 0 1-7.648 17.472c-4.928 4.48-12.992 5.824-23.296 4.032-90.144-30.048-163.68-83.84-220.64-161.888C92.256 697.216 64 609.312 64 512c0-81.152 20.192-156.064 60.096-224.672s94.176-122.88 163.232-163.232C355.936 84.192 430.816 64 512 64s156.064 20.192 224.672 60.096 122.88 94.176 163.232 163.232C939.808 355.488 960 430.848 960 512" fill="#000000" p-id="4242"></path></svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -1,922 +0,0 @@
{
"name": "GitHub Issue 总结机器人",
"intro": "定时获取GitHub Issue信息,使用AI进行总结,并推送到飞书群中",
"author": "",
"avatar": "/appMarketTemplates/githubIssue/avatar.svg",
"tags": ["office-services"],
"type": "advanced",
"workflow": {
"nodes": [
{
"nodeId": "userGuide",
"name": "common:core.module.template.system_config",
"intro": "common:core.module.template.system_config_info",
"avatar": "core/workflow/template/systemConfig",
"flowNodeType": "userGuide",
"position": {
"x": 262.2732338817093,
"y": -476.00241136598146
},
"version": "481",
"inputs": [
{
"key": "welcomeText",
"renderTypeList": ["hidden"],
"valueType": "string",
"label": "core.app.Welcome Text",
"value": ""
},
{
"key": "variables",
"renderTypeList": ["hidden"],
"valueType": "any",
"label": "core.app.Chat Variable",
"value": []
},
{
"key": "questionGuide",
"valueType": "hidden",
"renderTypeList": ["hidden"],
"label": "core.app.Question Guide",
"value": {
"open": false
}
},
{
"key": "tts",
"renderTypeList": ["hidden"],
"valueType": "any",
"label": "",
"value": {
"type": "web"
}
},
{
"key": "whisper",
"renderTypeList": ["hidden"],
"valueType": "any",
"label": "",
"value": {
"open": false,
"autoSend": false,
"autoTTSResponse": false
}
},
{
"key": "scheduleTrigger",
"renderTypeList": ["hidden"],
"valueType": "any",
"label": "",
"value": null
}
],
"outputs": []
},
{
"nodeId": "448745",
"name": "common:core.module.template.work_start",
"intro": "",
"avatar": "core/workflow/template/workflowStart",
"flowNodeType": "workflowStart",
"position": {
"x": 632.368838596004,
"y": -347.7446492944009
},
"version": "481",
"inputs": [
{
"key": "userChatInput",
"renderTypeList": ["reference", "textarea"],
"valueType": "string",
"label": "common:core.module.input.label.user question",
"required": true,
"toolDescription": "用户问题",
"debugLabel": ""
}
],
"outputs": [
{
"id": "userChatInput",
"key": "userChatInput",
"label": "common:core.module.input.label.user question",
"type": "static",
"valueType": "string",
"description": ""
}
]
},
{
"nodeId": "jVGuKrDfFTU6",
"name": "获取 24 小时前的日期",
"intro": "执行一段简单的脚本代码,通常用于进行复杂的数据处理。",
"avatar": "core/workflow/template/codeRun",
"flowNodeType": "code",
"showStatus": true,
"position": {
"x": 1045.4174257570808,
"y": -94.5419824521446
},
"version": "482",
"inputs": [
{
"key": "system_addInputParam",
"renderTypeList": ["addInputParam"],
"valueType": "dynamic",
"label": "",
"required": false,
"description": "workflow:these_variables_will_be_input_parameters_for_code_execution",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"valueDesc": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "codeType",
"renderTypeList": ["hidden"],
"label": "",
"value": "js",
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "code",
"renderTypeList": ["custom"],
"label": "",
"value": "function main() {\n const date = new Date();\n date.setDate(date.getDate() - 3);\n const day = date.getDate();\n const month = date.getMonth() + 1;\n const year = date.getFullYear();\n const hours = date.getHours();\n const minutes = date.getMinutes();\n\n return {\n date: `${year}-${month}-${day}T${hours}:${minutes}:000Z`,\n }\n }",
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
}
],
"outputs": [
{
"id": "system_rawResponse",
"key": "system_rawResponse",
"label": "workflow:full_response_data",
"valueType": "object",
"type": "static",
"description": ""
},
{
"id": "error",
"key": "error",
"label": "workflow:execution_error",
"description": "代码运行错误信息,成功时返回空",
"valueType": "object",
"type": "static"
},
{
"id": "system_addOutputParam",
"key": "system_addOutputParam",
"type": "dynamic",
"valueType": "dynamic",
"label": "",
"customFieldConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": false
},
"description": "将代码中 return 的对象作为输出,传递给后续的节点。变量名需要对应 return 的 key",
"valueDesc": ""
},
{
"id": "gR0mkQpJ4Og8",
"type": "dynamic",
"key": "date",
"valueType": "string",
"label": "date",
"valueDesc": "",
"description": ""
}
]
},
{
"nodeId": "jyftFRrd4RQf",
"name": "指定回复",
"intro": "该模块可以直接回复一段指定的内容。常用于引导、提示。非字符串内容传入时,会转成字符串进行输出。",
"avatar": "core/workflow/template/reply",
"flowNodeType": "answerNode",
"position": {
"x": 1758.8251385440858,
"y": 80.55020745654087
},
"version": "481",
"inputs": [
{
"key": "text",
"renderTypeList": ["textarea", "reference"],
"valueType": "any",
"required": true,
"label": "回复的内容",
"description": "common:core.module.input.description.Response content",
"placeholder": "common:core.module.input.description.Response content",
"valueDesc": "",
"debugLabel": "",
"toolDescription": "",
"value": "拉取从 {{$jVGuKrDfFTU6.gR0mkQpJ4Og8$}} 以来所有的 open issue \\n"
}
],
"outputs": []
},
{
"nodeId": "mCaalLpFoZFk",
"name": "获取 Issues",
"intro": "可以发出一个 HTTP 请求,实现更为复杂的操作(联网搜索、数据库查询等)",
"avatar": "core/workflow/template/httpRequest",
"flowNodeType": "httpRequest468",
"showStatus": true,
"position": {
"x": 2602.5615507147536,
"y": -67.18952984768578
},
"version": "481",
"inputs": [
{
"key": "system_addInputParam",
"renderTypeList": ["addInputParam"],
"valueType": "dynamic",
"label": "",
"required": false,
"description": "common:core.module.input.description.HTTP Dynamic Input",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"valueDesc": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpMethod",
"renderTypeList": ["custom"],
"valueType": "string",
"label": "",
"value": "GET",
"required": true,
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpTimeout",
"renderTypeList": ["custom"],
"valueType": "number",
"label": "",
"value": 30,
"min": 5,
"max": 600,
"required": true,
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpReqUrl",
"renderTypeList": ["hidden"],
"valueType": "string",
"label": "",
"description": "common:core.module.input.description.Http Request Url",
"placeholder": "https://api.ai.com/getInventory",
"required": false,
"value": "https://api.github.com/repos/labring/FastGPT/issues",
"valueDesc": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpHeader",
"renderTypeList": ["custom"],
"valueType": "any",
"value": [],
"label": "",
"description": "common:core.module.input.description.Http Request Header",
"placeholder": "common:core.module.input.description.Http Request Header",
"required": false,
"valueDesc": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpParams",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": [
{
"key": "state",
"type": "string",
"value": "open"
},
{
"key": "since",
"type": "string",
"value": "{{$jVGuKrDfFTU6.gR0mkQpJ4Og8$}}"
}
],
"label": "",
"required": false,
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpJsonBody",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": "",
"label": "",
"required": false,
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpFormBody",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": [],
"label": "",
"required": false,
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpContentType",
"renderTypeList": ["hidden"],
"valueType": "string",
"value": "json",
"label": "",
"required": false,
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
}
],
"outputs": [
{
"id": "error",
"key": "error",
"label": "workflow:request_error",
"description": "HTTP请求错误信息成功时返回空",
"valueType": "object",
"type": "static"
},
{
"id": "httpRawResponse",
"key": "httpRawResponse",
"required": true,
"label": "workflow:raw_response",
"description": "HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。",
"valueType": "any",
"type": "static"
},
{
"id": "system_addOutputParam",
"key": "system_addOutputParam",
"type": "dynamic",
"valueType": "dynamic",
"label": "",
"customFieldConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": false
},
"valueDesc": "",
"description": ""
}
]
},
{
"nodeId": "gALvyJcXPoep",
"name": "处理 API 响应数据",
"intro": "执行一段简单的脚本代码,通常用于进行复杂的数据处理。",
"avatar": "core/workflow/template/codeRun",
"flowNodeType": "code",
"showStatus": true,
"position": {
"x": 3396.722564475613,
"y": -80.79235153344955
},
"version": "482",
"inputs": [
{
"key": "system_addInputParam",
"renderTypeList": ["addInputParam"],
"valueType": "dynamic",
"label": "",
"required": false,
"description": "workflow:these_variables_will_be_input_parameters_for_code_execution",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"valueDesc": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "codeType",
"renderTypeList": ["hidden"],
"label": "",
"value": "js",
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "code",
"renderTypeList": ["custom"],
"label": "",
"value": "function main({res}) {\n const issues = JSON.parse(res);\n const ret = [];\n for(const issue of issues) {\n if (issue.pull_request) continue;\n ret.push({\n title: issue.title,\n body: issue.body,\n url: issue.html_url\n })\n }\n\n return {\n ret: JSON.stringify(ret)\n }\n}",
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"renderTypeList": ["reference"],
"valueType": "string",
"canEdit": true,
"key": "res",
"label": "res",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"required": true,
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": "",
"value": ["mCaalLpFoZFk", "httpRawResponse"]
}
],
"outputs": [
{
"id": "system_rawResponse",
"key": "system_rawResponse",
"label": "workflow:full_response_data",
"valueType": "object",
"type": "static",
"description": ""
},
{
"id": "error",
"key": "error",
"label": "workflow:execution_error",
"description": "代码运行错误信息,成功时返回空",
"valueType": "object",
"type": "static"
},
{
"id": "system_addOutputParam",
"key": "system_addOutputParam",
"type": "dynamic",
"valueType": "dynamic",
"label": "",
"customFieldConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": false
},
"description": "将代码中 return 的对象作为输出,传递给后续的节点。变量名需要对应 return 的 key",
"valueDesc": ""
},
{
"id": "qLUQfhG0ILRX",
"type": "dynamic",
"key": "ret",
"valueType": "string",
"label": "ret",
"valueDesc": "",
"description": ""
}
]
},
{
"nodeId": "aLrp6IjV8zAf",
"name": "AI 对话",
"intro": "AI 大模型对话",
"avatar": "core/workflow/template/aiChat",
"flowNodeType": "chatNode",
"showStatus": true,
"position": {
"x": 3907.7186093895143,
"y": -148.24856757598377
},
"version": "481",
"inputs": [
{
"key": "model",
"renderTypeList": ["settingLLMModel", "reference"],
"label": "AI 模型",
"valueType": "string",
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": "",
"value": "qwen-plus"
},
{
"key": "temperature",
"renderTypeList": ["hidden"],
"label": "",
"value": 0,
"valueType": "number",
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "maxToken",
"renderTypeList": ["hidden"],
"label": "",
"value": 8000,
"valueType": "number",
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "isResponseAnswerText",
"renderTypeList": ["hidden"],
"label": "",
"value": true,
"valueType": "boolean",
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "aiChatQuoteRole",
"renderTypeList": ["hidden"],
"label": "",
"valueType": "string",
"value": "system",
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "quoteTemplate",
"renderTypeList": ["hidden"],
"label": "",
"valueType": "string",
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "quotePrompt",
"renderTypeList": ["hidden"],
"label": "",
"valueType": "string",
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "aiChatVision",
"renderTypeList": ["hidden"],
"label": "",
"valueType": "boolean",
"value": false,
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "systemPrompt",
"renderTypeList": ["textarea", "reference"],
"max": 3000,
"valueType": "string",
"label": "提示词",
"description": "core.app.tip.systemPromptTip",
"placeholder": "core.app.tip.chatNodeSystemPromptTip",
"valueDesc": "",
"debugLabel": "",
"toolDescription": "",
"value": "你是一个简洁高效的 GitHub Issue 概述助手,专长于提炼核心问题并以清晰简洁的方式呈现。\n\n## 任务\n分析输入的多条 issue 信息,为每个 issue 创建一个简明扼要的概述。使用中文输出。\n\n## 输入格式\nJSON 数组,每项包含 title(标题)、body(内容)和 url(链接)。\n\n## 输出格式\n对每个 issue 使用 Markdown 语法创建简洁的概述块。每个概述应包含:\n\n1. 使用加粗呈现 issue 的原标题\n2. 一段简短的问题概述(不超过 2-3 句话)\n3. 原 issue 的链接(使用 Markdown 链接语法)\n\n在概述中适当使用 emoji 来增加可读性,但不要过度使用。保持整体风格简洁明了。\n\n示例输出\n\n---\n\n**🔍 数据可视化组件性能优化**\n\n这个 issue 反映了在处理大量数据时图表加载缓慢的问题。用户在数据点超过一定数量时experiencing明显的性能下降影响了用户体验。\n\n📎 [查看原 issue](url1)\n\n---\n\n**🐞 移动端界面适配问题**\n\n该 issue 指出在某些特定型号的移动设备上出现了界面布局错乱的情况。这个问题影响了应用在不同尺寸屏幕上的一致性展现。\n\n📎 [查看原 issue](url2)\n\n---\n\n请确保每个 issue 概述都简洁明了,突出核心问题,避免过多细节。保持整体风格统一,让读者能快速理解每个 issue 的要点。"
},
{
"key": "history",
"renderTypeList": ["numberInput", "reference"],
"valueType": "chatHistory",
"label": "聊天记录",
"description": "workflow:max_dialog_rounds",
"required": true,
"min": 0,
"max": 50,
"value": 0,
"valueDesc": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "quoteQA",
"renderTypeList": ["settingDatasetQuotePrompt"],
"label": "",
"debugLabel": "知识库引用",
"description": "",
"valueType": "datasetQuote",
"valueDesc": "",
"toolDescription": ""
},
{
"key": "stringQuoteText",
"renderTypeList": ["reference", "textarea"],
"label": "文档引用",
"debugLabel": "文档引用",
"description": "app:document_quote_tip",
"valueType": "string",
"valueDesc": "",
"toolDescription": ""
},
{
"key": "userChatInput",
"renderTypeList": ["reference", "textarea"],
"valueType": "string",
"label": "用户问题",
"toolDescription": "用户问题",
"required": true,
"value": ["gALvyJcXPoep", "qLUQfhG0ILRX"],
"valueDesc": "",
"description": "",
"debugLabel": ""
}
],
"outputs": [
{
"id": "history",
"key": "history",
"required": true,
"label": "common:core.module.output.label.New context",
"description": "将本次回复内容拼接上历史记录,作为新的上下文返回",
"valueType": "chatHistory",
"valueDesc": "{\n obj: System | Human | AI;\n value: string;\n}[]",
"type": "static"
},
{
"id": "answerText",
"key": "answerText",
"required": true,
"label": "common:core.module.output.label.Ai response content",
"description": "将在 stream 回复完毕后触发",
"valueType": "string",
"type": "static"
}
]
},
{
"nodeId": "jmSiT6OXA3Fe",
"name": "飞书机器人 webhook",
"intro": "向飞书机器人发起 webhook 请求。",
"avatar": "/appMarketTemplates/plugin-feishu/avatar.svg",
"flowNodeType": "pluginModule",
"showStatus": false,
"position": {
"x": 4682.428295424065,
"y": 120.04658236877646
},
"version": "488",
"inputs": [
{
"key": "system_forbid_stream",
"renderTypeList": ["switch"],
"valueType": "boolean",
"label": "禁用流输出",
"description": "强制设置嵌套运行的应用,均以非流模式运行",
"value": true,
"valueDesc": "",
"debugLabel": "",
"toolDescription": ""
},
{
"renderTypeList": ["reference"],
"selectedTypeIndex": 0,
"valueType": "string",
"canEdit": false,
"key": "content",
"label": "content",
"description": "需要发送的消息",
"required": true,
"toolDescription": "需要发送的消息",
"value": ["aLrp6IjV8zAf", "answerText"],
"valueDesc": "",
"debugLabel": ""
},
{
"renderTypeList": ["input"],
"selectedTypeIndex": 0,
"valueType": "string",
"canEdit": false,
"key": "hook_url",
"label": "hook_url",
"description": "飞书机器人地址",
"required": true,
"defaultValue": "",
"value": "https://www.feishu.cn/flow/api/trigger-webhook/5a1657d6f024c639e1e9af4d9d611292",
"valueDesc": "",
"debugLabel": "",
"toolDescription": ""
}
],
"outputs": [
{
"id": "result",
"type": "static",
"key": "result",
"valueType": "object",
"label": "result",
"description": "",
"valueDesc": ""
}
],
"pluginId": "community-feishu"
}
],
"edges": [
{
"source": "448745",
"target": "jVGuKrDfFTU6",
"sourceHandle": "448745-source-right",
"targetHandle": "jVGuKrDfFTU6-target-left"
},
{
"source": "jVGuKrDfFTU6",
"target": "jyftFRrd4RQf",
"sourceHandle": "jVGuKrDfFTU6-source-right",
"targetHandle": "jyftFRrd4RQf-target-left"
},
{
"source": "jyftFRrd4RQf",
"target": "mCaalLpFoZFk",
"sourceHandle": "jyftFRrd4RQf-source-right",
"targetHandle": "mCaalLpFoZFk-target-left"
},
{
"source": "mCaalLpFoZFk",
"target": "gALvyJcXPoep",
"sourceHandle": "mCaalLpFoZFk-source-right",
"targetHandle": "gALvyJcXPoep-target-left"
},
{
"source": "gALvyJcXPoep",
"target": "aLrp6IjV8zAf",
"sourceHandle": "gALvyJcXPoep-source-right",
"targetHandle": "aLrp6IjV8zAf-target-left"
},
{
"source": "aLrp6IjV8zAf",
"target": "jmSiT6OXA3Fe",
"sourceHandle": "aLrp6IjV8zAf-source-right",
"targetHandle": "jmSiT6OXA3Fe-target-left"
}
],
"chatConfig": {
"variables": [],
"scheduledTriggerConfig": {
"cronString": "",
"timezone": "Asia/Shanghai",
"defaultPrompt": ""
},
"_id": "67152011bb78889107c3a4ec"
}
}
}

View File

@@ -1 +0,0 @@
<svg t="1719285050683" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6411" width="256" height="256"><path d="M214.101333 512c0-32.512 5.546667-63.701333 15.36-92.928L57.173333 290.218667A491.861333 491.861333 0 0 0 4.693333 512c0 79.701333 18.858667 154.88 52.394667 221.610667l172.202667-129.066667A290.56 290.56 0 0 1 214.101333 512" fill="#FBBC05" p-id="6412"></path><path d="M516.693333 216.192c72.106667 0 137.258667 25.002667 188.458667 65.962667L854.101333 136.533333C763.349333 59.178667 646.997333 11.392 516.693333 11.392c-202.325333 0-376.234667 113.28-459.52 278.826667l172.373334 128.853333c39.68-118.016 152.832-202.88 287.146666-202.88" fill="#EA4335" p-id="6413"></path><path d="M516.693333 807.808c-134.357333 0-247.509333-84.864-287.232-202.88l-172.288 128.853333c83.242667 165.546667 257.152 278.826667 459.52 278.826667 124.842667 0 244.053333-43.392 333.568-124.757333l-163.584-123.818667c-46.122667 28.458667-104.234667 43.776-170.026666 43.776" fill="#34A853" p-id="6414"></path><path d="M1005.397333 512c0-29.568-4.693333-61.44-11.648-91.008H516.650667V614.4h274.602666c-13.696 65.962667-51.072 116.650667-104.533333 149.632l163.541333 123.818667c93.994667-85.418667 155.136-212.650667 155.136-375.850667" fill="#4285F4" p-id="6415"></path></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1,443 +0,0 @@
{
"name": "谷歌搜索",
"intro": "通过请求谷歌搜索,查询相关内容作为模型的参考。",
"author": "",
"avatar": "/appMarketTemplates/google/avatar.svg",
"tags": ["recommendation", "web-search"],
"type": "advanced",
"workflow": {
"nodes": [
{
"nodeId": "userGuide",
"name": "系统配置",
"intro": "可以配置应用的系统参数",
"avatar": "/imgs/workflow/userGuide.png",
"flowNodeType": "userGuide",
"position": {
"x": 262.2732338817093,
"y": -476.00241136598146
},
"version": "481",
"inputs": [
{
"key": "welcomeText",
"renderTypeList": ["hidden"],
"valueType": "string",
"label": "core.app.Welcome Text",
"value": ""
},
{
"key": "variables",
"renderTypeList": ["hidden"],
"valueType": "any",
"label": "core.app.Chat Variable",
"value": []
},
{
"key": "questionGuide",
"valueType": "any",
"renderTypeList": ["hidden"],
"label": "core.app.Question Guide",
"value": {
"open": false
}
},
{
"key": "tts",
"renderTypeList": ["hidden"],
"valueType": "any",
"label": "",
"value": {
"type": "web"
}
},
{
"key": "whisper",
"renderTypeList": ["hidden"],
"valueType": "any",
"label": "",
"value": {
"open": false,
"autoSend": false,
"autoTTSResponse": false
}
},
{
"key": "scheduleTrigger",
"renderTypeList": ["hidden"],
"valueType": "any",
"label": "",
"value": null
}
],
"outputs": []
},
{
"nodeId": "448745",
"name": "流程开始",
"intro": "",
"avatar": "/imgs/workflow/userChatInput.svg",
"flowNodeType": "workflowStart",
"position": {
"x": 295.8944548701009,
"y": 110.81336038514848
},
"version": "481",
"inputs": [
{
"key": "userChatInput",
"renderTypeList": ["reference", "textarea"],
"valueType": "string",
"label": "用户问题",
"required": true,
"toolDescription": "用户问题"
}
],
"outputs": [
{
"id": "userChatInput",
"key": "userChatInput",
"label": "core.module.input.label.user question",
"valueType": "string",
"type": "static"
}
]
},
{
"nodeId": "NOgbnBzUwDgT",
"name": "工具调用",
"intro": "通过AI模型自动选择一个或多个功能块进行调用也可以对插件进行调用。",
"avatar": "/imgs/workflow/tool.svg",
"flowNodeType": "tools",
"showStatus": true,
"position": {
"x": 1028.8358722416106,
"y": -500.8755882990822
},
"version": "481",
"inputs": [
{
"key": "model",
"renderTypeList": ["settingLLMModel", "reference"],
"label": "core.module.input.label.aiModel",
"valueType": "string",
"llmModelType": "all",
"value": "FastAI-plus"
},
{
"key": "temperature",
"renderTypeList": ["hidden"],
"label": "",
"value": 0,
"valueType": "number",
"min": 0,
"max": 10,
"step": 1
},
{
"key": "maxToken",
"renderTypeList": ["hidden"],
"label": "",
"value": 2000,
"valueType": "number",
"min": 100,
"max": 4000,
"step": 50
},
{
"key": "systemPrompt",
"renderTypeList": ["textarea", "reference"],
"max": 3000,
"valueType": "string",
"label": "core.ai.Prompt",
"description": "core.app.tip.systemPromptTip",
"placeholder": "core.app.tip.chatNodeSystemPromptTip",
"value": ""
},
{
"key": "history",
"renderTypeList": ["numberInput", "reference"],
"valueType": "chatHistory",
"label": "core.module.input.label.chat history",
"description": "最多携带多少轮对话记录",
"required": true,
"min": 0,
"max": 30,
"value": 6
},
{
"key": "userChatInput",
"renderTypeList": ["reference", "textarea"],
"valueType": "string",
"label": "用户问题",
"required": true,
"value": ["448745", "userChatInput"]
}
],
"outputs": [
{
"id": "NodeOutputKeyEnum.answerText",
"key": "NodeOutputKeyEnum.answerText",
"label": "core.module.output.label.Ai response content",
"description": "core.module.output.description.Ai response content",
"valueType": "string",
"type": "FlowNodeOutputTypeEnum.static"
}
]
},
{
"nodeId": "GMELVPxHfpg5",
"name": "HTTP 请求",
"intro": "调用谷歌搜索,查询相关内容",
"avatar": "/imgs/workflow/http.png",
"flowNodeType": "httpRequest468",
"showStatus": true,
"position": {
"x": 1005.4777753640342,
"y": 319.4905539380939
},
"version": "481",
"inputs": [
{
"key": "system_addInputParam",
"renderTypeList": ["addInputParam"],
"valueType": "dynamic",
"label": "",
"required": false,
"description": "core.module.input.description.HTTP Dynamic Input"
},
{
"valueType": "string",
"renderTypeList": ["reference"],
"key": "query",
"label": "query",
"toolDescription": "谷歌搜索检索词",
"required": true,
"canEdit": true,
"editField": {
"key": true,
"description": true
}
},
{
"key": "system_httpMethod",
"renderTypeList": ["custom"],
"valueType": "string",
"label": "",
"value": "GET",
"required": true
},
{
"key": "system_httpReqUrl",
"renderTypeList": ["hidden"],
"valueType": "string",
"label": "",
"description": "core.module.input.description.Http Request Url",
"placeholder": "https://api.ai.com/getInventory",
"required": false,
"value": "https://www.googleapis.com/customsearch/v1"
},
{
"key": "system_httpHeader",
"renderTypeList": ["custom"],
"valueType": "any",
"value": [],
"label": "",
"description": "core.module.input.description.Http Request Header",
"placeholder": "core.module.input.description.Http Request Header",
"required": false
},
{
"key": "system_httpParams",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": [
{
"key": "q",
"type": "string",
"value": "{{query}}"
},
{
"key": "cx",
"type": "string",
"value": "谷歌搜索cxID"
},
{
"key": "key",
"type": "string",
"value": "谷歌搜索key"
},
{
"key": "c2coff",
"type": "string",
"value": "1"
},
{
"key": "start",
"type": "string",
"value": "1"
},
{
"key": "end",
"type": "string",
"value": "20"
},
{
"key": "dateRestrict",
"type": "string",
"value": "m[1]"
}
],
"label": "",
"required": false
},
{
"key": "system_httpJsonBody",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": "",
"label": "",
"required": false
}
],
"outputs": [
{
"id": "system_addOutputParam",
"key": "system_addOutputParam",
"type": "dynamic",
"valueType": "dynamic",
"label": "",
"editField": {
"key": true,
"valueType": true
}
},
{
"id": "httpRawResponse",
"key": "httpRawResponse",
"label": "原始响应",
"description": "HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。",
"valueType": "any",
"type": "static",
"required": true
},
{
"id": "M5YmxaYe8em1",
"type": "dynamic",
"key": "prompt",
"valueType": "string",
"label": "prompt"
}
]
},
{
"nodeId": "poIbrrA8aiR0",
"name": "代码运行",
"intro": "执行一段简单的脚本代码,通常用于进行复杂的数据处理。",
"avatar": "/imgs/workflow/code.svg",
"flowNodeType": "code",
"showStatus": true,
"position": {
"x": 1711.805344753384,
"y": 650.1023414708576
},
"version": "482",
"inputs": [
{
"key": "system_addInputParam",
"renderTypeList": ["addInputParam"],
"valueType": "dynamic",
"label": "",
"required": false,
"description": "这些变量会作为代码的运行的输入参数",
"editField": {
"key": true,
"valueType": true
}
},
{
"key": "data",
"valueType": "object",
"label": "data",
"renderTypeList": ["reference"],
"description": "",
"canEdit": true,
"editField": {
"key": true,
"valueType": true
},
"value": ["GMELVPxHfpg5", "httpRawResponse"]
},
{
"key": "codeType",
"renderTypeList": ["hidden"],
"label": "",
"value": "js"
},
{
"key": "code",
"renderTypeList": ["custom"],
"label": "",
"value": "function main({data}){\n const result = data.items.map((item) => ({\n title: item.title,\n link: item.link,\n snippet: item.snippet\n }))\n return { prompt: JSON.stringify(result) }\n}"
}
],
"outputs": [
{
"id": "system_addOutputParam",
"key": "system_addOutputParam",
"type": "dynamic",
"valueType": "dynamic",
"label": "",
"editField": {
"key": true,
"valueType": true
},
"description": "将代码中 return 的对象作为输出,传递给后续的节点"
},
{
"id": "system_rawResponse",
"key": "system_rawResponse",
"label": "完整响应数据",
"valueType": "object",
"type": "static"
},
{
"id": "error",
"key": "error",
"label": "运行错误",
"description": "代码运行错误信息,成功时返回空",
"valueType": "object",
"type": "static"
},
{
"id": "qLUQfhG0ILRX",
"type": "dynamic",
"key": "prompt",
"valueType": "string",
"label": "prompt"
}
]
}
],
"edges": [
{
"source": "448745",
"target": "NOgbnBzUwDgT",
"sourceHandle": "448745-source-right",
"targetHandle": "NOgbnBzUwDgT-target-left"
},
{
"source": "NOgbnBzUwDgT",
"target": "GMELVPxHfpg5",
"sourceHandle": "selectedTools",
"targetHandle": "selectedTools"
},
{
"source": "GMELVPxHfpg5",
"target": "poIbrrA8aiR0",
"sourceHandle": "GMELVPxHfpg5-source-right",
"targetHandle": "poIbrrA8aiR0-target-left"
}
]
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.6 KiB

View File

@@ -1,330 +0,0 @@
{
"name": "Dalle3 绘图",
"intro": "通过请求 Dalle3 接口绘图,需要有 api key",
"author": "",
"avatar": "/appMarketTemplates/plugin-dalle/avatar.svg",
"type": "plugin",
"tags": ["recommendation", "image-generation"],
"workflow": {
"nodes": [
{
"nodeId": "pluginInput",
"name": "插件开始",
"intro": "可以配置插件需要哪些输入,利用这些输入来运行插件",
"avatar": "core/workflow/template/workflowStart",
"flowNodeType": "pluginInput",
"showStatus": false,
"position": {
"x": 421.97302886868476,
"y": -89.7785530936485
},
"version": "481",
"inputs": [
{
"renderTypeList": ["reference"],
"selectedTypeIndex": 0,
"valueType": "string",
"canEdit": true,
"key": "绘图提示词",
"label": "绘图提示词",
"description": "绘图提示词",
"required": true,
"toolDescription": "绘图提示词"
}
],
"outputs": [
{
"id": "绘图提示词",
"valueType": "string",
"key": "绘图提示词",
"label": "绘图提示词",
"type": "hidden"
}
]
},
{
"nodeId": "pluginOutput",
"name": "插件输出",
"intro": "自定义配置外部输出,使用插件时,仅暴露自定义配置的输出",
"avatar": "core/workflow/template/pluginOutput",
"flowNodeType": "pluginOutput",
"showStatus": false,
"position": {
"x": 1785.9300180845394,
"y": -108.2785530936485
},
"version": "481",
"inputs": [
{
"renderTypeList": ["reference"],
"valueType": "string",
"canEdit": true,
"key": "图片访问链接",
"label": "图片访问链接",
"description": "",
"value": ["tMvel910bnrJ", "pJXgWoTpPoMy"]
},
{
"renderTypeList": ["reference"],
"valueType": "object",
"canEdit": true,
"key": "error",
"label": "错误信息",
"description": "",
"value": ["tMvel910bnrJ", "error"]
}
],
"outputs": []
},
{
"nodeId": "tMvel910bnrJ",
"name": "HTTP 请求",
"intro": "可以发出一个 HTTP 请求,实现更为复杂的操作(联网搜索、数据库查询等)",
"avatar": "core/workflow/template/httpRequest",
"flowNodeType": "httpRequest468",
"showStatus": true,
"position": {
"x": 1044.8838211811253,
"y": -414.7785530936485
},
"version": "481",
"inputs": [
{
"key": "system_addInputParam",
"renderTypeList": ["addInputParam"],
"valueType": "dynamic",
"label": "",
"required": false,
"description": "common:core.module.input.description.HTTP Dynamic Input",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpMethod",
"renderTypeList": ["custom"],
"valueType": "string",
"label": "",
"value": "POST",
"required": true,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpTimeout",
"renderTypeList": ["custom"],
"valueType": "number",
"label": "",
"value": 30,
"min": 5,
"max": 600,
"required": true,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpReqUrl",
"renderTypeList": ["hidden"],
"valueType": "string",
"label": "",
"description": "common:core.module.input.description.Http Request Url",
"placeholder": "https://api.ai.com/getInventory",
"required": false,
"value": "https://api.openai.com/v1/images/generations",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpHeader",
"renderTypeList": ["custom"],
"valueType": "any",
"value": [
{
"key": "Authorization",
"type": "string",
"value": "Bearer {{authorization}}"
}
],
"label": "",
"description": "common:core.module.input.description.Http Request Header",
"placeholder": "common:core.module.input.description.Http Request Header",
"required": false,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpParams",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": [],
"label": "",
"required": false,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpJsonBody",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": "{\n \"model\": \"dall-e-3\",\n \"prompt\": \"{{prompt}}\",\n \"n\": 1,\n \"size\": \"1024x1024\"\n}",
"label": "",
"required": false,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpFormBody",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": [],
"label": "",
"required": false,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpContentType",
"renderTypeList": ["hidden"],
"valueType": "string",
"value": "json",
"label": "",
"required": false,
"debugLabel": "",
"toolDescription": ""
},
{
"renderTypeList": ["reference"],
"valueType": "string",
"canEdit": true,
"key": "prompt",
"label": "prompt",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"required": true,
"value": ["pluginInput", "绘图提示词"]
}
],
"outputs": [
{
"id": "error",
"key": "error",
"label": "workflow:request_error",
"description": "HTTP请求错误信息成功时返回空",
"valueType": "object",
"type": "static"
},
{
"id": "httpRawResponse",
"key": "httpRawResponse",
"required": true,
"label": "workflow:raw_response",
"description": "HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。",
"valueType": "any",
"type": "static"
},
{
"id": "system_addOutputParam",
"key": "system_addOutputParam",
"type": "dynamic",
"valueType": "dynamic",
"label": "",
"customFieldConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": false
}
},
{
"id": "pJXgWoTpPoMy",
"valueType": "string",
"type": "dynamic",
"key": "data[0].url",
"label": "data[0].url"
}
]
},
{
"nodeId": "c7tRU2qAQoAf",
"name": "系统配置",
"intro": "",
"avatar": "core/workflow/template/systemConfig",
"flowNodeType": "pluginConfig",
"position": {
"x": -46.476647046261974,
"y": -89.7785530936485
},
"version": "4811",
"inputs": [],
"outputs": []
}
],
"edges": [
{
"source": "pluginInput",
"target": "tMvel910bnrJ",
"sourceHandle": "pluginInput-source-right",
"targetHandle": "tMvel910bnrJ-target-left"
},
{
"source": "tMvel910bnrJ",
"target": "pluginOutput",
"sourceHandle": "tMvel910bnrJ-source-right",
"targetHandle": "pluginOutput-target-left"
}
]
}
}

View File

@@ -1 +0,0 @@
<svg t="1719284536895" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5409" width="256" height="256"><path d="M891.318857 340.845714c4.900571 0 9.728 0.292571 14.628572 0.804572a409.965714 409.965714 0 0 1 108.836571 30.061714c10.093714 4.534857 12.580571 8.192 3.949714 17.334857-24.868571 26.624-45.494857 57.051429-61.001143 89.965714-16.822857 35.328-35.108571 69.851429-52.297142 105.033143a225.28 225.28 0 0 1-52.150858 69.412572c-53.613714 48.493714-116.150857 68.973714-187.538285 59.099428-81.92-11.337143-159.451429-38.985143-232.740572-75.483428a143.506286 143.506286 0 0 1-10.459428-5.485715 5.339429 5.339429 0 0 1 0.292571-9.216l5.12-2.706285c59.245714-31.670857 108.836571-75.849143 156.525714-122.294857 20.187429-19.529143 39.497143-40.009143 59.904-59.318858A345.014857 345.014857 0 0 1 804.571429 352.256c13.165714-3.218286 26.550857-5.778286 39.789714-8.630857h0.585143l28.233143-2.56" fill="#133C9A" p-id="5410"></path><path d="M317.659429 913.846857c-8.996571-0.512-31.158857-3.584-33.865143-3.949714a536.429714 536.429714 0 0 1-165.083429-48.274286c-30.208-14.116571-59.245714-30.72-88.356571-46.957714-19.163429-10.678857-27.794286-27.282286-27.648-49.883429 0.585143-83.382857 0.585143-166.765714 0-250.148571C2.413714 461.019429 0.731429 407.405714 0 353.718857c0-4.754286 0.731429-9.508571 2.194286-13.897143 3.291429-9.728 9.947429-10.24 16.530285-3.949714 7.606857 7.314286 13.677714 16.237714 21.211429 23.405714 67.291429 66.413714 138.752 127.195429 218.770286 177.225143 45.056 28.891429 91.940571 54.710857 140.434285 77.385143 77.750857 35.328 157.549714 66.486857 241.078858 86.235429 73.874286 17.481143 145.627429 6.436571 205.458285-40.374858 18.285714-15.652571 27.282286-27.062857 48.932572-55.881142a359.862857 359.862857 0 0 1-37.376 72.850285c-13.897143 21.942857-45.348571 51.2-69.193143 74.093715-36.278857 35.108571-83.748571 63.561143-128.292572 87.552-48.566857 26.185143-99.035429 47.104-152.941714 58.514285-27.648 6.948571-67.584 14.848-81.334857 15.579429-2.413714-0.146286-10.678857 1.682286-14.848 1.389714-35.547429 2.633143-57.490286 3.657143-92.891429 0z" fill="#3370FF" p-id="5411"></path><path d="M165.083429 110.518857a52.443429 52.443429 0 0 1 7.460571 0c152.649143 0 304.128 2.486857 456.630857 2.486857 0.292571 0 0.585143 0 0.731429 0.219429 14.189714 12.361143 27.282286 25.746286 39.277714 40.155428 34.450286 34.230857 60.123429 93.622857 77.677714 129.755429 8.777143 25.014857 21.942857 48.859429 28.16 76.8v0.438857c-15.579429 5.046857-30.72 11.190857-45.348571 18.505143-44.178286 22.381714-64.219429 38.765714-100.790857 74.752-19.968 19.529143-37.010286 37.083429-63.488 62.098286a563.346286 563.346286 0 0 1-29.769143 26.916571c-7.021714-12.434286-125.732571-244.589714-364.251429-427.300571" fill="#00D6B9" p-id="5412"></path></svg>

Before

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -1,355 +0,0 @@
{
"name": "飞书 webhook 插件",
"intro": "通过 webhook 给飞书机器人发送一条消息",
"author": "",
"avatar": "/appMarketTemplates/plugin-feishu/avatar.svg",
"type": "plugin",
"tags": ["recommendation", "office-services"],
"workflow": {
"nodes": [
{
"nodeId": "pluginInput",
"name": "插件开始",
"intro": "自定义配置外部输入,使用插件时,仅暴露自定义配置的输入",
"avatar": "core/workflow/template/workflowStart",
"flowNodeType": "pluginInput",
"showStatus": false,
"position": {
"x": 535.7465806305546,
"y": -201.26482361861054
},
"version": "481",
"inputs": [
{
"inputType": "input",
"valueType": "string",
"key": "飞书机器人地址",
"label": "飞书机器人地址",
"description": "",
"isToolInput": false,
"defaultValue": "",
"editField": {
"key": true
},
"dynamicParamDefaultValue": {
"inputType": "reference",
"valueType": "string",
"required": true
},
"renderTypeList": ["input"],
"required": true,
"canEdit": true,
"value": ""
},
{
"key": "发送的消息",
"valueType": "string",
"label": "发送的消息",
"renderTypeList": ["reference"],
"required": true,
"description": "",
"canEdit": true,
"value": "",
"editField": {
"key": true
},
"dynamicParamDefaultValue": {
"inputType": "reference",
"valueType": "string",
"required": true
}
}
],
"outputs": [
{
"id": "mv52BrPVE6bm",
"key": "飞书机器人地址",
"valueType": "string",
"label": "飞书机器人地址",
"type": "static"
},
{
"id": "p0m68Dv5KaIp",
"key": "发送的消息",
"valueType": "string",
"label": "发送的消息",
"type": "static"
}
]
},
{
"nodeId": "pluginOutput",
"name": "插件输出",
"intro": "自定义配置外部输出,使用插件时,仅暴露自定义配置的输出",
"avatar": "core/workflow/template/pluginOutput",
"flowNodeType": "pluginOutput",
"showStatus": false,
"position": {
"x": 1776.027569211593,
"y": -58.264823618610535
},
"version": "481",
"inputs": [],
"outputs": []
},
{
"nodeId": "rKBYGQuYefae",
"name": "HTTP 请求",
"intro": "可以发出一个 HTTP 请求,实现更为复杂的操作(联网搜索、数据库查询等)",
"avatar": "core/workflow/template/httpRequest",
"flowNodeType": "httpRequest468",
"showStatus": true,
"position": {
"x": 1069.7228495148624,
"y": -392.26482361861054
},
"version": "481",
"inputs": [
{
"key": "system_addInputParam",
"renderTypeList": ["addInputParam"],
"valueType": "dynamic",
"label": "",
"required": false,
"description": "common:core.module.input.description.HTTP Dynamic Input",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpMethod",
"renderTypeList": ["custom"],
"valueType": "string",
"label": "",
"value": "POST",
"required": true,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpTimeout",
"renderTypeList": ["custom"],
"valueType": "number",
"label": "",
"value": 30,
"min": 5,
"max": 600,
"required": true,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpReqUrl",
"renderTypeList": ["hidden"],
"valueType": "string",
"label": "",
"description": "common:core.module.input.description.Http Request Url",
"placeholder": "https://api.ai.com/getInventory",
"required": false,
"value": "{{url}}",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpHeader",
"renderTypeList": ["custom"],
"valueType": "any",
"value": [],
"label": "",
"description": "common:core.module.input.description.Http Request Header",
"placeholder": "common:core.module.input.description.Http Request Header",
"required": false,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpParams",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": [],
"label": "",
"required": false,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpJsonBody",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": "{\r\n \"msg_type\": \"text\",\r\n \"content\": {\r\n \"text\": \"{{text}}\"\r\n }\r\n}",
"label": "",
"required": false,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpFormBody",
"renderTypeList": ["hidden"],
"valueType": "any",
"value": [],
"label": "",
"required": false,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "system_httpContentType",
"renderTypeList": ["hidden"],
"valueType": "string",
"value": "json",
"label": "",
"required": false,
"debugLabel": "",
"toolDescription": ""
},
{
"key": "text",
"valueType": "string",
"label": "text",
"renderTypeList": ["reference"],
"description": "",
"canEdit": true,
"editField": {
"key": true,
"valueType": true
},
"value": ["pluginInput", "p0m68Dv5KaIp"],
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
}
},
{
"key": "url",
"valueType": "string",
"label": "url",
"renderTypeList": ["reference"],
"description": "",
"canEdit": true,
"editField": {
"key": true,
"valueType": true
},
"value": ["pluginInput", "mv52BrPVE6bm"],
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
}
}
],
"outputs": [
{
"id": "error",
"key": "error",
"label": "workflow:request_error",
"description": "HTTP请求错误信息成功时返回空",
"valueType": "object",
"type": "static"
},
{
"id": "httpRawResponse",
"key": "httpRawResponse",
"required": true,
"label": "workflow:raw_response",
"description": "HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。",
"valueType": "any",
"type": "static"
},
{
"id": "system_addOutputParam",
"key": "system_addOutputParam",
"type": "dynamic",
"valueType": "dynamic",
"label": "",
"editField": {
"key": true,
"valueType": true
}
}
]
},
{
"nodeId": "q3ccNXiZIHoS",
"name": "系统配置",
"intro": "",
"avatar": "core/workflow/template/systemConfig",
"flowNodeType": "pluginConfig",
"position": {
"x": 99.73879703925843,
"y": -201.26482361861054
},
"version": "4811",
"inputs": [],
"outputs": []
}
],
"edges": [
{
"source": "pluginInput",
"target": "rKBYGQuYefae",
"sourceHandle": "pluginInput-source-right",
"targetHandle": "rKBYGQuYefae-target-left"
},
{
"source": "rKBYGQuYefae",
"target": "pluginOutput",
"sourceHandle": "rKBYGQuYefae-source-right",
"targetHandle": "pluginOutput-target-left"
}
]
}
}

View File

@@ -1,288 +0,0 @@
{
"name": "知识库+对话引导",
"intro": "每次提问时进行一次知识库搜索,将搜索结果注入 LLM 模型进行参考回答",
"author": "",
"avatar": "core/workflow/template/datasetSearch",
"type": "simple",
"tags": ["office-services"],
"workflow": {
"nodes": [
{
"nodeId": "userGuide",
"name": "系统配置",
"intro": "可以配置应用的系统参数",
"avatar": "core/workflow/template/systemConfig",
"flowNodeType": "userGuide",
"position": {
"x": 531.2422736065552,
"y": -486.7611729549753
},
"version": "481",
"inputs": [],
"outputs": []
},
{
"nodeId": "workflowStartNodeId",
"name": "流程开始",
"intro": "",
"avatar": "core/workflow/template/workflowStart",
"flowNodeType": "workflowStart",
"position": {
"x": 558.4082376415505,
"y": 123.72387429194112
},
"version": "481",
"inputs": [
{
"key": "userChatInput",
"renderTypeList": ["FlowNodeInputTypeEnum.reference", "FlowNodeInputTypeEnum.textarea"],
"valueType": "string",
"label": "用户问题",
"required": true,
"toolDescription": "用户问题"
}
],
"outputs": [
{
"id": "userChatInput",
"key": "userChatInput",
"label": "core.module.input.label.user question",
"valueType": "string",
"type": "FlowNodeOutputTypeEnum.static"
}
]
},
{
"nodeId": "7BdojPlukIQw",
"name": "AI 对话",
"intro": "AI 大模型对话",
"avatar": "core/workflow/template/aiChat",
"flowNodeType": "chatNode",
"showStatus": true,
"position": {
"x": 1638.509551404687,
"y": -341.0428450861567
},
"version": "481",
"inputs": [
{
"key": "model",
"renderTypeList": [
"FlowNodeInputTypeEnum.settingLLMModel",
"FlowNodeInputTypeEnum.reference"
],
"label": "core.module.input.label.aiModel",
"valueType": "string",
"value": "gpt-4o-mini"
},
{
"key": "temperature",
"renderTypeList": ["FlowNodeInputTypeEnum.hidden"],
"label": "",
"value": 3,
"valueType": "number",
"min": 0,
"max": 10,
"step": 1
},
{
"key": "maxToken",
"renderTypeList": ["FlowNodeInputTypeEnum.hidden"],
"label": "",
"value": 1950,
"valueType": "number",
"min": 100,
"max": 4000,
"step": 50
},
{
"key": "isResponseAnswerText",
"renderTypeList": ["FlowNodeInputTypeEnum.hidden"],
"label": "",
"value": true,
"valueType": "boolean"
},
{
"key": "quoteTemplate",
"renderTypeList": ["FlowNodeInputTypeEnum.hidden"],
"label": "",
"valueType": "string"
},
{
"key": "quotePrompt",
"renderTypeList": ["FlowNodeInputTypeEnum.hidden"],
"label": "",
"valueType": "string"
},
{
"key": "systemPrompt",
"renderTypeList": ["FlowNodeInputTypeEnum.textarea", "FlowNodeInputTypeEnum.reference"],
"max": 3000,
"valueType": "string",
"label": "core.ai.Prompt",
"description": "core.app.tip.systemPromptTip",
"placeholder": "core.app.tip.chatNodeSystemPromptTip",
"value": ""
},
{
"key": "history",
"renderTypeList": [
"FlowNodeInputTypeEnum.numberInput",
"FlowNodeInputTypeEnum.reference"
],
"valueType": "chatHistory",
"label": "core.module.input.label.chat history",
"required": true,
"min": 0,
"max": 30,
"value": 6
},
{
"key": "userChatInput",
"renderTypeList": ["FlowNodeInputTypeEnum.reference", "FlowNodeInputTypeEnum.textarea"],
"valueType": "string",
"label": "用户问题",
"required": true,
"toolDescription": "用户问题",
"value": ["workflowStartNodeId", "userChatInput"]
},
{
"key": "quoteQA",
"renderTypeList": ["FlowNodeInputTypeEnum.settingDatasetQuotePrompt"],
"label": "",
"debugLabel": "知识库引用",
"description": "",
"valueType": "datasetQuote",
"value": ["iKBoX2vIzETU", "quoteQA"]
}
],
"outputs": [
{
"id": "history",
"key": "history",
"label": "core.module.output.label.New context",
"description": "core.module.output.description.New context",
"valueType": "chatHistory",
"type": "FlowNodeOutputTypeEnum.static"
},
{
"id": "answerText",
"key": "answerText",
"label": "core.module.output.label.Ai response content",
"description": "core.module.output.description.Ai response content",
"valueType": "string",
"type": "FlowNodeOutputTypeEnum.static"
}
]
},
{
"nodeId": "iKBoX2vIzETU",
"name": "知识库搜索",
"intro": "调用\"语义检索\"和\"全文检索\"能力,从\"知识库\"中查找可能与问题相关的参考内容",
"avatar": "core/workflow/template/datasetSearch",
"flowNodeType": "datasetSearchNode",
"showStatus": true,
"position": {
"x": 918.5901682164496,
"y": -227.11542247619582
},
"version": "481",
"inputs": [
{
"key": "datasets",
"renderTypeList": [
"FlowNodeInputTypeEnum.selectDataset",
"FlowNodeInputTypeEnum.reference"
],
"label": "core.module.input.label.Select dataset",
"value": [],
"valueType": "selectDataset",
"list": [],
"required": true
},
{
"key": "similarity",
"renderTypeList": ["FlowNodeInputTypeEnum.selectDatasetParamsModal"],
"label": "",
"value": 0.4,
"valueType": "number"
},
{
"key": "limit",
"renderTypeList": ["hidden"],
"label": "",
"value": 1500,
"valueType": "number"
},
{
"key": "searchMode",
"renderTypeList": ["FlowNodeInputTypeEnum.hidden"],
"label": "",
"valueType": "string",
"value": "embedding"
},
{
"key": "usingReRank",
"renderTypeList": ["FlowNodeInputTypeEnum.hidden"],
"label": "",
"valueType": "boolean",
"value": false
},
{
"key": "datasetSearchUsingExtensionQuery",
"renderTypeList": ["FlowNodeInputTypeEnum.hidden"],
"label": "",
"valueType": "boolean",
"value": true
},
{
"key": "datasetSearchExtensionModel",
"renderTypeList": ["FlowNodeInputTypeEnum.hidden"],
"label": "",
"valueType": "string"
},
{
"key": "datasetSearchExtensionBg",
"renderTypeList": ["FlowNodeInputTypeEnum.hidden"],
"label": "",
"valueType": "string",
"value": ""
},
{
"key": "userChatInput",
"renderTypeList": ["FlowNodeInputTypeEnum.reference", "FlowNodeInputTypeEnum.textarea"],
"valueType": "string",
"label": "用户问题",
"required": true,
"toolDescription": "需要检索的内容",
"value": ["workflowStartNodeId", "userChatInput"]
}
],
"outputs": [
{
"id": "quoteQA",
"key": "quoteQA",
"label": "core.module.Dataset quote.label",
"type": "FlowNodeOutputTypeEnum.static",
"valueType": "datasetQuote",
"description": "特殊数组格式,搜索结果为空时,返回空数组。"
}
]
}
],
"edges": [
{
"source": "workflowStartNodeId",
"target": "iKBoX2vIzETU",
"sourceHandle": "workflowStartNodeId-source-right",
"targetHandle": "iKBoX2vIzETU-target-left"
},
{
"source": "iKBoX2vIzETU",
"target": "7BdojPlukIQw",
"sourceHandle": "iKBoX2vIzETU-source-right",
"targetHandle": "7BdojPlukIQw-target-left"
}
]
}
}

View File

@@ -1 +0,0 @@
<svg t="1730279749380" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="23191" width="200" height="200"><path d="M192.682417 617.135502c10.498098 8.652054 23.62507 13.31833 37.040614 13.31833 2.300392 0 4.601808-0.1361 6.901177-0.407276 15.794731-1.89721 30.119995-10.181897 39.748283-22.918989L396.811538 447.300442l119.918183 101.526301c12.158924 10.277064 27.935236 15.387455 43.767829 13.491269 15.813151-1.664919 30.273491-9.69685 40.055275-22.317285l275.42491-354.942965c2.010797-2.593058 3.596921-5.380544 5.124717-8.16803l48.350195-57.257052c25.903973-30.679743 14.769378-50.598398-24.746614-44.248783l-227.867778 36.583197c-39.534412 6.349615-47.749514 33.8172-18.268062 61.070914l45.565778 42.100864L545.629935 419.407162l-120.55468-102.08605c-12.275581-10.336416-28.14706-15.21554-44.192501-13.452383-15.930831 1.780553-30.448476 10.083659-40.134069 22.955828l-120.960932 160.526043-58.093093-47.792493c-25.01472-20.595061-61.901839-16.917298-82.35466 8.284687-20.452821 25.163099-16.780175 62.250786 8.255011 82.807984L192.682417 617.135502z" fill="#F45944" p-id="23192"></path><path d="M878.626052 349.259522 610.025571 676.327626c-12.063757 15.291265-42.744523-6.658653-57.551764-19.240203l-107.951641-94.964862c-14.808264-12.580526-37.000706 8.710382-49.315172 23.809265l-125.447107 153.884786c-12.294001 15.098883-34.411741-2.206248-49.103348-14.924921l-109.457948-87.046518c-19.641339-18.891256-38.375006 0.813528-38.375006 38.48143l0.676405 233.131665c0.037862 19.550265 15.928784 35.538401 35.280528 35.557844l810.558793 0.639566c19.371186 0.01842 35.20378-15.969717 35.20378-35.519981 0 0 0.811482-503.446183 0.811482-541.656437C955.37504 317.243341 914.912489 307.410392 878.626052 349.259522z" fill="#F45944" p-id="23193"></path></svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -1,503 +0,0 @@
{
"name": "利好大A",
"intro": "",
"author": "",
"avatar": "/appMarketTemplates/stock/avatar.svg",
"tags": ["roleplay"],
"type": "advanced",
"workflow": {
"nodes": [
{
"nodeId": "userGuide",
"name": "common:core.module.template.system_config",
"intro": "common:core.module.template.system_config_info",
"avatar": "core/workflow/template/systemConfig",
"flowNodeType": "userGuide",
"position": {
"x": 262.2732338817093,
"y": -476.00241136598146
},
"version": "481",
"inputs": [
{
"key": "welcomeText",
"renderTypeList": ["hidden"],
"valueType": "string",
"label": "core.app.Welcome Text",
"value": ""
},
{
"key": "variables",
"renderTypeList": ["hidden"],
"valueType": "any",
"label": "core.app.Chat Variable",
"value": []
},
{
"key": "questionGuide",
"valueType": "any",
"renderTypeList": ["hidden"],
"label": "core.app.Question Guide",
"value": {
"open": false
}
},
{
"key": "tts",
"renderTypeList": ["hidden"],
"valueType": "any",
"label": "",
"value": {
"type": "web"
}
},
{
"key": "whisper",
"renderTypeList": ["hidden"],
"valueType": "any",
"label": "",
"value": {
"open": false,
"autoSend": false,
"autoTTSResponse": false
}
},
{
"key": "scheduleTrigger",
"renderTypeList": ["hidden"],
"valueType": "any",
"label": "",
"value": null
}
],
"outputs": []
},
{
"nodeId": "448745",
"name": "common:core.module.template.work_start",
"intro": "",
"avatar": "core/workflow/template/workflowStart",
"flowNodeType": "workflowStart",
"position": {
"x": 632.368838596004,
"y": -347.7446492944009
},
"version": "481",
"inputs": [
{
"key": "userChatInput",
"renderTypeList": ["reference", "textarea"],
"valueType": "string",
"label": "common:core.module.input.label.user question",
"required": true,
"toolDescription": "用户问题",
"debugLabel": ""
}
],
"outputs": [
{
"id": "userChatInput",
"key": "userChatInput",
"label": "common:core.module.input.label.user question",
"type": "static",
"valueType": "string",
"description": ""
}
]
},
{
"nodeId": "bg853CwHAw4a",
"name": "AI 对话",
"intro": "AI 大模型对话",
"avatar": "core/workflow/template/aiChat",
"flowNodeType": "chatNode",
"showStatus": true,
"position": {
"x": 1318.728987052518,
"y": -612.0024113659815
},
"version": "481",
"inputs": [
{
"key": "model",
"renderTypeList": ["settingLLMModel", "reference"],
"label": "AI 模型",
"valueType": "string",
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": "",
"value": "claude-3-5-sonnet-20240620"
},
{
"key": "temperature",
"renderTypeList": ["hidden"],
"label": "",
"value": 0,
"valueType": "number",
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "maxToken",
"renderTypeList": ["hidden"],
"label": "",
"value": 4000,
"valueType": "number",
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "isResponseAnswerText",
"renderTypeList": ["hidden"],
"label": "",
"value": false,
"valueType": "boolean",
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "aiChatQuoteRole",
"renderTypeList": ["hidden"],
"label": "",
"valueType": "string",
"value": "system",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "quoteTemplate",
"renderTypeList": ["hidden"],
"label": "",
"valueType": "string",
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "quotePrompt",
"renderTypeList": ["hidden"],
"label": "",
"valueType": "string",
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "aiChatVision",
"renderTypeList": ["hidden"],
"label": "",
"valueType": "boolean",
"value": false,
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "systemPrompt",
"renderTypeList": ["textarea", "reference"],
"max": 3000,
"valueType": "string",
"label": "提示词",
"description": "core.app.tip.systemPromptTip",
"placeholder": "core.app.tip.chatNodeSystemPromptTip",
"valueDesc": "",
"debugLabel": "",
"toolDescription": "",
"value": "{提示词 START\n;; 作者: 李继刚\n;; 版本: 0.1\n;; 模型: Claude Sonnet\n;; 用途: 这事呀, 利好我大A!\n\n;; 设定如下内容为你的 *System Prompt*\n(require 'dash)\n\n(defun 韮菜 ()\n \"典型股民形象\"\n (list (经历 . '(亏损累累 频繁交易 追涨杀跌))\n (性格 . '(冲动 乐观 侥幸))\n (技能 . '(看K线 炒概念 追热点))\n (信念 . '(暴富梦想 政策利好 抄底反弹))\n (表达 . '(股评口号 情绪化 群体性))))\n\n(defun 利好大A (用户输入)\n \"任何消息都必将利好我大A股\"\n (let* ((解读 (-> 用户输入\n 提取关键词\n 生成关联概念\n 分析影响\n ;; 强行联系股市,无论多牵强\n 强行关联A 股\n ;; 乐观解读一切影响\n 乐观解读))\n (响应 (随机结论)))\n (SVG-Card 用户输入 解读 响应))\n\n (defun 随机结论 ()\n (随机选择\n '(\"这事呀,利好大A!\"\n \"A股有戏啊!\"\n \"这还不得跑步进场啊!\"\n \"还傻站在这干嘛? 快打开手机加仓啊!\"\n \"看来A股要起飞了!\"\n \"大A要发财了!\")))\n\n\n (defun SVG-Card (用户输入 响应)\n \"创建富洞察力且具有审美的 SVG 概念可视化\"\n (let ((配置 '(:画布 (480 . 760)\n :色彩 (:背景 \"#000000\"\n :主要文字 \"#ffffff\"\n :次要文字 \"#00cc00\"\n :图形 \"#00ff00\")\n :排版 \"杂志风格\"\n :字体 (使用本机字体 (font-family \"KingHwa_OldSong\")))))\n (-> 用户输入\n 关键画面\n 立体主义\n (极简图形 配置)\n (布局 `(,(标题 \"利好大A\") 分隔线 用户输入 图形\n (逻辑链推导 解读) 响应))))\n\n\n (defun start ()\n \"启动时运行, 你是韮菜~\"\n (let (system-role (韮菜))\n (print \"又有啥好消息了? 现在加仓还来得及吗?\")))\n\n;;; Attention: 运行规则!\n;; 1. 初次启动时必须只运行 (start) 函数\n;; 2. 接收用户输入之后, 调用主函数 (利好大A 用户输入)\n;; 3. 严格按照(SVG-Card) 进行排版输出\n;; 4. 输出SVG 后, 不再输出任何额外文字解释\n提示词 END}\n\n直接生成 svg 完整代码,我会复制,需要你用代码块)\n除此之外不要有多余的解释不要在开头加上任何说明\n解释的内容自动加入换行标签例如\n<tspan x=\"50%\" dy=\"25\" font-size=\"18\" fill=\"#8B4513\">文字1</tspan>\n <tspan x=\"50%\" dy=\"25\" font-size=\"18\" fill=\"#8B4513\">文字12</tspan>"
},
{
"key": "history",
"renderTypeList": ["numberInput", "reference"],
"valueType": "chatHistory",
"label": "聊天记录",
"description": "workflow:max_dialog_rounds",
"required": true,
"min": 0,
"max": 50,
"value": 0,
"valueDesc": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "quoteQA",
"renderTypeList": ["settingDatasetQuotePrompt"],
"label": "",
"debugLabel": "知识库引用",
"description": "",
"valueType": "datasetQuote",
"valueDesc": "",
"toolDescription": ""
},
{
"key": "stringQuoteText",
"renderTypeList": ["reference", "textarea"],
"label": "文档引用",
"debugLabel": "文档引用",
"description": "app:document_quote_tip",
"valueType": "string",
"valueDesc": "",
"toolDescription": ""
},
{
"key": "userChatInput",
"renderTypeList": ["reference", "textarea"],
"valueType": "string",
"label": "用户问题",
"required": true,
"toolDescription": "用户问题",
"valueDesc": "",
"description": "",
"debugLabel": "",
"value": ["448745", "userChatInput"]
}
],
"outputs": [
{
"id": "history",
"key": "history",
"required": true,
"label": "common:core.module.output.label.New context",
"description": "将本次回复内容拼接上历史记录,作为新的上下文返回",
"valueType": "chatHistory",
"valueDesc": "{\n obj: System | Human | AI;\n value: string;\n}[]",
"type": "static"
},
{
"id": "answerText",
"key": "answerText",
"required": true,
"label": "common:core.module.output.label.Ai response content",
"description": "将在 stream 回复完毕后触发",
"valueType": "string",
"type": "static"
}
]
},
{
"nodeId": "sbVUb0efY6Fm",
"name": "代码运行",
"intro": "执行一段简单的脚本代码,通常用于进行复杂的数据处理。",
"avatar": "core/workflow/template/codeRun",
"flowNodeType": "code",
"showStatus": true,
"position": {
"x": 2210.2574140398733,
"y": -621.0024113659815
},
"version": "482",
"inputs": [
{
"key": "system_addInputParam",
"renderTypeList": ["addInputParam"],
"valueType": "dynamic",
"label": "",
"required": false,
"description": "workflow:these_variables_will_be_input_parameters_for_code_execution",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"valueDesc": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "codeType",
"renderTypeList": ["hidden"],
"label": "",
"value": "js",
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"key": "code",
"renderTypeList": ["custom"],
"label": "",
"value": "function main({svg_str}){\n\n // 使用正则表达式匹配代码块中的内容\n const match = svg_str.match(/```[\\w]*\\n([\\s\\S]*?)```/);\n\n if (!match) {\n // 如果没有匹配到代码块,返回一个错误信息或空结果\n return {\n result: null,\n error: \"未找到有效的代码块标记。\"\n };\n }\n\n // 提取代码块中的 SVG 内容\n const extractedSvg = match[1].trim();\n \n const base64 = strToBase64(extractedSvg,'data:image/svg+xml;base64,')\n\n return {\n result: base64\n }\n}",
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": ""
},
{
"renderTypeList": ["reference"],
"valueType": "string",
"canEdit": true,
"key": "svg_str",
"label": "svg_str",
"customInputConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"arrayAny",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": true
},
"required": true,
"valueDesc": "",
"description": "",
"debugLabel": "",
"toolDescription": "",
"value": ["bg853CwHAw4a", "answerText"]
}
],
"outputs": [
{
"id": "system_rawResponse",
"key": "system_rawResponse",
"label": "workflow:full_response_data",
"valueType": "object",
"type": "static",
"description": ""
},
{
"id": "error",
"key": "error",
"label": "workflow:execution_error",
"description": "代码运行错误信息,成功时返回空",
"valueType": "object",
"type": "static"
},
{
"id": "system_addOutputParam",
"key": "system_addOutputParam",
"type": "dynamic",
"valueType": "dynamic",
"label": "",
"customFieldConfig": {
"selectValueTypeList": [
"string",
"number",
"boolean",
"object",
"arrayString",
"arrayNumber",
"arrayBoolean",
"arrayObject",
"any",
"chatHistory",
"datasetQuote",
"dynamic",
"selectApp",
"selectDataset"
],
"showDescription": false,
"showDefaultValue": false
},
"description": "将代码中 return 的对象作为输出,传递给后续的节点。变量名需要对应 return 的 key",
"valueDesc": ""
},
{
"id": "qLUQfhG0ILRX",
"type": "dynamic",
"key": "result",
"valueType": "string",
"label": "result",
"valueDesc": "",
"description": ""
}
]
},
{
"nodeId": "cPh2VZnVxjQ8",
"name": "指定回复",
"intro": "该模块可以直接回复一段指定的内容。常用于引导、提示。非字符串内容传入时,会转成字符串进行输出。",
"avatar": "core/workflow/template/reply",
"flowNodeType": "answerNode",
"position": {
"x": 2911.2230784647795,
"y": -411.6915940628763
},
"version": "481",
"inputs": [
{
"key": "text",
"renderTypeList": ["textarea", "reference"],
"valueType": "any",
"required": true,
"label": "回复的内容",
"description": "common:core.module.input.description.Response content",
"placeholder": "common:core.module.input.description.Response content",
"valueDesc": "",
"debugLabel": "",
"toolDescription": "",
"value": "SVG\n\n{{$bg853CwHAw4a.answerText$}}\n\n卡片\n\n![]({{$sbVUb0efY6Fm.qLUQfhG0ILRX$}})"
}
],
"outputs": []
}
],
"edges": [
{
"source": "bg853CwHAw4a",
"target": "sbVUb0efY6Fm",
"sourceHandle": "bg853CwHAw4a-source-right",
"targetHandle": "sbVUb0efY6Fm-target-left"
},
{
"source": "448745",
"target": "bg853CwHAw4a",
"sourceHandle": "448745-source-right",
"targetHandle": "bg853CwHAw4a-target-left"
},
{
"source": "sbVUb0efY6Fm",
"target": "cPh2VZnVxjQ8",
"sourceHandle": "sbVUb0efY6Fm-source-right",
"targetHandle": "cPh2VZnVxjQ8-target-left"
}
],
"chatConfig": {
"variables": [],
"scheduledTriggerConfig": {
"cronString": "",
"timezone": "Asia/Shanghai",
"defaultPrompt": ""
},
"_id": "66f0f7540a40cd1f97da9dd6"
}
}
}

View File

@@ -1,186 +0,0 @@
{
"name": "知道时间的机器人",
"intro": "通过挂载时间插件,让模型获取当前最新时间",
"author": "",
"avatar": "core/workflow/template/getTime",
"tags": ["recommendation", "roleplay"],
"type": "simple",
"weight": 1,
"workflow": {
"nodes": [
{
"nodeId": "userGuide",
"name": "系统配置",
"intro": "可以配置应用的系统参数",
"avatar": "/imgs/workflow/userGuide.png",
"flowNodeType": "userGuide",
"position": {
"x": 531.2422736065552,
"y": -486.7611729549753
},
"version": "481",
"inputs": [],
"outputs": []
},
{
"nodeId": "workflowStartNodeId",
"name": "流程开始",
"intro": "",
"avatar": "/imgs/workflow/userChatInput.svg",
"flowNodeType": "workflowStart",
"position": {
"x": 558.4082376415505,
"y": 123.72387429194112
},
"version": "481",
"inputs": [
{
"key": "userChatInput",
"renderTypeList": ["reference", "textarea"],
"valueType": "string",
"label": "用户问题",
"required": true,
"toolDescription": "用户问题"
}
],
"outputs": [
{
"id": "userChatInput",
"key": "userChatInput",
"label": "core.module.input.label.user question",
"valueType": "string",
"type": "static"
}
]
},
{
"nodeId": "jrWPV9",
"name": "工具调用",
"intro": "通过AI模型自动选择一个或多个功能块进行调用也可以对插件进行调用。",
"avatar": "/imgs/workflow/tool.svg",
"flowNodeType": "tools",
"showStatus": true,
"position": {
"x": 1062.1738942532802,
"y": -223.65033022650476
},
"version": "481",
"inputs": [
{
"key": "model",
"renderTypeList": ["settingLLMModel", "reference"],
"label": "core.module.input.label.aiModel",
"valueType": "string",
"llmModelType": "all",
"value": "gpt-3.5-turbo"
},
{
"key": "temperature",
"renderTypeList": ["hidden"],
"label": "",
"value": 0,
"valueType": "number",
"min": 0,
"max": 10,
"step": 1
},
{
"key": "maxToken",
"renderTypeList": ["hidden"],
"label": "",
"value": 2000,
"valueType": "number",
"min": 100,
"max": 4000,
"step": 50
},
{
"key": "systemPrompt",
"renderTypeList": ["textarea", "reference"],
"max": 3000,
"valueType": "string",
"label": "core.ai.Prompt",
"description": "core.app.tip.systemPromptTip",
"placeholder": "core.app.tip.chatNodeSystemPromptTip",
"value": ""
},
{
"key": "history",
"renderTypeList": ["numberInput", "reference"],
"valueType": "chatHistory",
"label": "core.module.input.label.chat history",
"description": "最多携带多少轮对话记录",
"required": true,
"min": 0,
"max": 30,
"value": 6
},
{
"key": "userChatInput",
"renderTypeList": ["reference", "textarea"],
"valueType": "string",
"label": "用户问题",
"required": true,
"value": ["workflowStartNodeId", "userChatInput"]
}
],
"outputs": [
{
"id": "answerText",
"key": "answerText",
"label": "core.module.output.label.Ai response content",
"description": "core.module.output.description.Ai response content",
"valueType": "string",
"type": "static"
}
]
},
{
"nodeId": "zBxjo5",
"name": "获取当前时间",
"intro": "获取用户当前时区的时间。",
"avatar": "/imgs/workflow/getCurrentTime.svg",
"flowNodeType": "pluginModule",
"showStatus": false,
"position": {
"x": 1000,
"y": 545
},
"version": "481",
"inputs": [],
"outputs": [
{
"id": "time",
"type": "static",
"key": "time",
"valueType": "string",
"label": "time",
"description": ""
}
],
"pluginId": "community-getTime"
}
],
"edges": [
{
"source": "workflowStartNodeId",
"target": "jrWPV9",
"sourceHandle": "workflowStartNodeId-source-right",
"targetHandle": "jrWPV9-target-left"
},
{
"source": "jrWPV9",
"target": "zBxjo5",
"sourceHandle": "selectedTools",
"targetHandle": "selectedTools"
}
],
"chatConfig": {
"scheduledTriggerConfig": {
"cronString": "",
"timezone": "Asia/Shanghai",
"defaultPrompt": ""
}
}
}
}

View File

@@ -17,7 +17,7 @@ const unAuthPage: { [key: string]: boolean } = {
'/price': true
};
const Auth = ({ children }: { children: JSX.Element }) => {
const Auth = ({ children }: { children: JSX.Element | React.ReactNode }) => {
const { t } = useTranslation();
const router = useRouter();
const { toast } = useToast();

View File

@@ -80,14 +80,14 @@ const Layout = ({ children }: { children: JSX.Element }) => {
{isHideNavbar ? (
<Auth>{children}</Auth>
) : (
<>
<Auth>
<Box h={'100%'} position={'fixed'} left={0} top={0} w={navbarWidth}>
<Navbar unread={unread} />
</Box>
<Box h={'100%'} ml={navbarWidth} overflow={'overlay'}>
<Auth>{children}</Auth>
{children}
</Box>
</>
</Auth>
)}
</>
)}
@@ -96,14 +96,16 @@ const Layout = ({ children }: { children: JSX.Element }) => {
{phoneUnShowLayoutRoute[router.pathname] || isChatPage ? (
<Auth>{children}</Auth>
) : (
<Flex h={'100%'} flexDirection={'column'}>
<Box flex={'1 0 0'} h={0}>
<Auth>{children}</Auth>
</Box>
<Box h={'50px'} borderTop={'1px solid rgba(0,0,0,0.1)'}>
<NavbarPhone unread={unread} />
</Box>
</Flex>
<Auth>
<Flex h={'100%'} flexDirection={'column'}>
<Box flex={'1 0 0'} h={0}>
{children}
</Box>
<Box h={'50px'} borderTop={'1px solid rgba(0,0,0,0.1)'}>
<NavbarPhone unread={unread} />
</Box>
</Flex>
</Auth>
)}
</>
)}

View File

@@ -82,6 +82,7 @@ const Navbar = ({ unread }: { unread: number }) => {
'/account/info',
'/account/team',
'/account/usage',
'/account/thirdParty',
'/account/apikey',
'/account/setting',
'/account/inform',

View File

@@ -28,17 +28,22 @@ const IframeHtmlCodeBlock = dynamic(() => import('./codeBlock/iframe-html'), { s
const ChatGuide = dynamic(() => import('./chat/Guide'), { ssr: false });
const QuestionGuide = dynamic(() => import('./chat/QuestionGuide'), { ssr: false });
const Markdown = ({
source = '',
showAnimation = false,
isDisabled = false,
forbidZhFormat = false
}: {
type Props = {
source?: string;
showAnimation?: boolean;
isDisabled?: boolean;
forbidZhFormat?: boolean;
}) => {
};
const Markdown = (props: Props) => {
const source = props.source || '';
if (source.length < 200000) {
return <MarkdownRender {...props} />;
}
return <Box whiteSpace={'pre-wrap'}>{source}</Box>;
};
const MarkdownRender = ({ source = '', showAnimation, isDisabled, forbidZhFormat }: Props) => {
const components = useMemo<any>(
() => ({
img: Image,

View File

@@ -24,12 +24,6 @@ const OneRowSelector = ({ list, onchange, disableTip, ...props }: Props) => {
const { t } = useTranslation();
const { feConfigs, llmModelList, vectorModelList } = useSystemStore();
const {
isOpen: isOpenAiPointsModal,
onClose: onCloseAiPointsModal,
onOpen: onOpenAiPointsModal
} = useDisclosure();
const avatarSize = useMemo(() => {
const size = {
sm: '1rem',
@@ -74,17 +68,6 @@ const OneRowSelector = ({ list, onchange, disableTip, ...props }: Props) => {
: avatarList;
}, [feConfigs.show_pay, avatarList, avatarSize, t]);
const onSelect = useCallback(
(e: string) => {
if (e === 'price') {
onOpenAiPointsModal();
return;
}
return onchange?.(e);
},
[onOpenAiPointsModal, onchange]
);
return (
<Box
css={{
@@ -94,30 +77,32 @@ const OneRowSelector = ({ list, onchange, disableTip, ...props }: Props) => {
}}
>
<MyTooltip label={disableTip}>
<MySelect
className="nowheel"
isDisabled={!!disableTip}
list={expandList}
{...props}
onchange={onSelect}
/>
<ModelPriceModal>
{({ onOpen }) => (
<MySelect
className="nowheel"
isDisabled={!!disableTip}
list={expandList}
{...props}
onchange={(e) => {
if (e === 'price') {
onOpen();
return;
}
return onchange?.(e);
}}
/>
)}
</ModelPriceModal>
</MyTooltip>
{isOpenAiPointsModal && <ModelPriceModal onClose={onCloseAiPointsModal} />}
</Box>
);
};
const MultipleRowSelector = ({ list, onchange, disableTip, ...props }: Props) => {
const { t } = useTranslation();
const { feConfigs, llmModelList, vectorModelList } = useSystemStore();
const { llmModelList, vectorModelList } = useSystemStore();
const [value, setValue] = useState<string[]>([]);
const {
isOpen: isOpenAiPointsModal,
onClose: onCloseAiPointsModal,
onOpen: onOpenAiPointsModal
} = useDisclosure();
const avatarSize = useMemo(() => {
const size = {
sm: '1rem',
@@ -211,8 +196,6 @@ const MultipleRowSelector = ({ list, onchange, disableTip, ...props }: Props) =>
}}
/>
</MyTooltip>
{isOpenAiPointsModal && <ModelPriceModal onClose={onCloseAiPointsModal} />}
</Box>
);
};

View File

@@ -0,0 +1,46 @@
import { Box, ModalBody, useDisclosure } from '@chakra-ui/react';
import Markdown from '@/components/Markdown';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { getDocPath } from '@/web/common/system/doc';
import React from 'react';
const UseGuideModal = ({
children,
title,
iconSrc,
text,
link
}: {
children: ({ onClick }: { onClick: () => void }) => React.ReactNode;
title?: string;
iconSrc?: string;
text?: string;
link?: string;
}) => {
const { isOpen, onOpen, onClose } = useDisclosure();
const onClick = () => {
if (link) {
return window.open(getDocPath(link), '_blank');
}
if (text) {
return onOpen();
}
};
return (
<>
{children({ onClick })}
{isOpen && (
<MyModal isOpen iconSrc={iconSrc} title={title} onClose={onClose} minW={'600px'}>
<ModalBody>
<Box border={'base'} borderRadius={'10px'} p={4} minH={'500px'}>
<Markdown source={text} />
</Box>
</ModalBody>
</MyModal>
)}
</>
);
};
export default React.memo(UseGuideModal);

View File

@@ -100,12 +100,6 @@ const AIChatSettingsModal = ({
setRefresh(!refresh);
};
const {
isOpen: isOpenAiPointsModal,
onClose: onCloseAiPointsModal,
onOpen: onOpenAiPointsModal
} = useDisclosure();
return (
<MyModal
isOpen
@@ -160,10 +154,11 @@ const AIChatSettingsModal = ({
<Th fontSize={'mini'} pb={2}>
<HStack spacing={1}>
<Box> {t('app:ai_point_price')}</Box>
<QuestionTip
label={t('app:look_ai_point_price')}
onClick={onOpenAiPointsModal}
/>
<ModelPriceModal>
{({ onOpen }) => (
<QuestionTip label={t('app:look_ai_point_price')} onClick={onOpen} />
)}
</ModelPriceModal>
</HStack>
</Th>
<Th fontSize={'mini'} pb={2}>
@@ -327,8 +322,6 @@ const AIChatSettingsModal = ({
{t('common:common.Confirm')}
</Button>
</ModalFooter>
{isOpenAiPointsModal && <ModelPriceModal onClose={onCloseAiPointsModal} />}
</MyModal>
);
};

View File

@@ -9,9 +9,9 @@ import {
Td,
Th,
Thead,
Tr
Tr,
useDisclosure
} from '@chakra-ui/react';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { useTranslation } from 'next-i18next';
import React, { useMemo, useRef, useState } from 'react';
import {
@@ -26,6 +26,9 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
import Avatar from '@fastgpt/web/components/common/Avatar';
import MyTag from '@fastgpt/web/components/common/Tag/index';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import dynamic from 'next/dynamic';
const MyModal = dynamic(() => import('@fastgpt/web/components/common/MyModal'));
const ModelTable = () => {
const { t } = useTranslation();
@@ -156,6 +159,19 @@ const ModelTable = () => {
search
]);
const filterProviderList = useMemo(() => {
const allProviderIds: string[] = [
...llmModelList,
...vectorModelList,
...audioSpeechModelList,
whisperModel
].map((model) => model.provider);
return providerList.current.filter(
(item) => allProviderIds.includes(item.value) || item.value === ''
);
}, [audioSpeechModelList, llmModelList, vectorModelList, whisperModel]);
return (
<Flex flexDirection={'column'} h={'100%'}>
<Flex>
@@ -168,7 +184,7 @@ const ModelTable = () => {
bg={'myGray.50'}
value={provider}
onchange={setProvider}
list={providerList.current}
list={filterProviderList}
/>
</HStack>
<HStack flexShrink={0} ml={6}>
@@ -228,24 +244,34 @@ const ModelTable = () => {
export default ModelTable;
export const ModelPriceModal = ({ onClose }: { onClose: () => void }) => {
export const ModelPriceModal = ({
children
}: {
children: ({ onOpen }: { onOpen: () => void }) => React.ReactNode;
}) => {
const { t } = useTranslation();
const { isOpen, onOpen, onClose } = useDisclosure();
return (
<MyModal
isCentered
iconSrc="/imgs/modal/bill.svg"
title={t('common:support.wallet.subscription.Ai points')}
isOpen
onClose={onClose}
w={'100%'}
h={'100%'}
maxW={'90vw'}
maxH={'90vh'}
>
<ModalBody flex={'1 0 0'}>
<ModelTable />
</ModalBody>
</MyModal>
<>
{children({ onOpen })}
{isOpen && (
<MyModal
isCentered
iconSrc="/imgs/modal/bill.svg"
title={t('common:support.wallet.subscription.Ai points')}
isOpen
onClose={onClose}
w={'100%'}
h={'100%'}
maxW={'90vw'}
maxH={'90vh'}
>
<ModalBody flex={'1 0 0'}>
<ModelTable />
</ModalBody>
</MyModal>
)}
</>
);
};

View File

@@ -226,8 +226,8 @@ const DatasetParamsModal = ({
{limit !== undefined && (
<Box display={['block', 'flex']}>
<Flex flex={'0 0 120px'} alignItems={'center'} mb={[5, 0]}>
<FormLabel>{t('app:max_quote_tokens')}</FormLabel>
<QuestionTip label={t('app:max_quote_tokens_tips')} />
<FormLabel>{t('common:max_quote_tokens')}</FormLabel>
<QuestionTip label={t('common:max_quote_tokens_tips')} />
</Flex>
<Box flex={'1 0 0'}>
<InputSlider
@@ -245,8 +245,8 @@ const DatasetParamsModal = ({
)}
<Box display={['block', 'flex']} mt={[6, 10]} mb={4}>
<Flex flex={'0 0 120px'} alignItems={'center'} mb={[5, 0]}>
<FormLabel>{t('app:min_similarity')}</FormLabel>
<QuestionTip label={t('app:min_similarity_tip')} />
<FormLabel>{t('common:min_similarity')}</FormLabel>
<QuestionTip label={t('common:min_similarity_tip')} />
</Flex>
<Box flex={'1 0 0'}>
{showSimilarity ? (

View File

@@ -1,4 +1,4 @@
import { Box, Button, Flex, Switch, Textarea } from '@chakra-ui/react';
import { Box, Button, Flex, Switch, Textarea, useDisclosure } from '@chakra-ui/react';
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io';
@@ -20,6 +20,8 @@ import { isEqual } from 'lodash';
import { ChatItemContext } from '@/web/core/chat/context/chatItemContext';
import { ChatRecordContext } from '@/web/core/chat/context/chatRecordContext';
import { PluginRunContext } from '../context';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import AIModelSelector from '@/components/Select/AIModelSelector';
const JsonEditor = dynamic(() => import('@fastgpt/web/components/common/Textarea/JsonEditor'));
@@ -152,6 +154,7 @@ const RenderPluginInput = ({
}) => {
const { t } = useTranslation();
const inputType = input.renderTypeList[0];
const { llmModelList } = useSystemStore();
const render = (() => {
if (inputType === FlowNodeInputTypeEnum.customVariable) {
@@ -167,7 +170,19 @@ const RenderPluginInput = ({
<FileSelector onChange={onChange} input={input} setUploading={setUploading} value={value} />
);
}
if (inputType === FlowNodeInputTypeEnum.selectLLMModel) {
return (
<AIModelSelector
w={'100%'}
value={value}
list={llmModelList.map((item) => ({
value: item.model,
label: item.name
}))}
onchange={onChange}
/>
);
}
if (input.valueType === WorkflowIOValueTypeEnum.string) {
return (
<Textarea

View File

@@ -62,8 +62,8 @@ const SearchParamsTip = ({
<Thead>
<Tr bg={'transparent !important'}>
<Th fontSize={'mini'}>{t('common:core.dataset.search.search mode')}</Th>
<Th fontSize={'mini'}>{t('app:max_quote_tokens')}</Th>
<Th fontSize={'mini'}>{t('app:min_similarity')}</Th>
<Th fontSize={'mini'}>{t('common:max_quote_tokens')}</Th>
<Th fontSize={'mini'}>{t('common:min_similarity')}</Th>
{hasReRankModel && <Th fontSize={'mini'}>{t('common:core.dataset.search.ReRank')}</Th>}
<Th fontSize={'mini'}>{t('common:core.module.template.Query extension')}</Th>
{hasEmptyResponseMode && (

View File

@@ -96,7 +96,7 @@ const LafAccountModal = ({
});
return (
<MyModal isOpen iconSrc="/imgs/workflow/laf.png" title={t('common:user.Laf Account Setting')}>
<MyModal isOpen iconSrc="support/account/laf" title={t('common:user.Laf Account Setting')}>
<ModalBody>
<Box fontSize={'sm'} color={'myGray.500'}>
<Box>{t('common:support.user.Laf account intro')}</Box>
@@ -107,7 +107,7 @@ const LafAccountModal = ({
</Box>
<Box>
<Link textDecoration={'underline'} href={`${feConfigs.lafEnv}/`} isExternal>
{t('support.user.Go laf env', {
{t('common:support.user.Go laf env', {
env: feConfigs.lafEnv?.split('//')[1]
})}
</Link>

View File

@@ -2,7 +2,7 @@ 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, useDisclosure } from '@chakra-ui/react';
import { Box, Flex, Grid } from '@chakra-ui/react';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useTranslation } from 'next-i18next';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
@@ -22,12 +22,6 @@ const StandardPlanContentList = ({
const { t } = useTranslation();
const { subPlans } = useSystemStore();
const {
isOpen: isOpenAiPointsModal,
onClose: onCloseAiPointsModal,
onOpen: onOpenAiPointsModal
} = useDisclosure();
const planContent = useMemo(() => {
const plan = subPlans?.standard?.[level];
@@ -100,11 +94,15 @@ const StandardPlanContentList = ({
amount: planContent.totalPoints
})}
</Box>
<QuestionTip
ml={1}
label={t('common:support.wallet.subscription.AI points click to read tip')}
onClick={onOpenAiPointsModal}
></QuestionTip>
<ModelPriceModal>
{({ onOpen }) => (
<QuestionTip
ml={1}
label={t('common:support.wallet.subscription.AI points click to read tip')}
onClick={onOpen}
/>
)}
</ModelPriceModal>
</Flex>
</Flex>
<Flex alignItems={'center'}>
@@ -127,7 +125,6 @@ const StandardPlanContentList = ({
<Box color={'myGray.600'}>{t('common:support.wallet.subscription.web_site_sync')}</Box>
</Flex>
)}
{isOpenAiPointsModal && <ModelPriceModal onClose={onCloseAiPointsModal} />}
</Grid>
) : null;
};

View File

@@ -20,6 +20,7 @@ export type PostPublishAppProps = {
chatConfig: AppSchema['chatConfig'];
isPublish?: boolean;
versionName?: string;
autoSave?: boolean; // If it is automatically saved, only one copy of the entire app will be stored, overwriting the old version
};
export type PostRevertAppProps = {

View File

@@ -10,7 +10,7 @@ export async function register() {
const [
{ connectMongo },
{ systemStartCb },
{ initGlobalVariables, getInitConfig, initSystemPlugins },
{ initGlobalVariables, getInitConfig, initSystemPluginGroups, initAppTemplateTypes },
{ initVectorStore },
{ initRootUser },
{ getSystemPluginCb },
@@ -37,8 +37,10 @@ export async function register() {
await connectMongo();
//init system configinit vector databaseinit root user
await Promise.all([getInitConfig(), initVectorStore(), initRootUser(), initSystemPlugins()]);
await Promise.all([getInitConfig(), initVectorStore(), initRootUser()]);
initSystemPluginGroups();
initAppTemplateTypes();
getSystemPluginCb();
startMongoWatch();
startCron();

View File

@@ -17,6 +17,8 @@ export enum TabEnum {
'bill' = 'bill',
'inform' = 'inform',
'setting' = 'setting',
'thirdParty' = 'thirdParty',
'individuation' = 'individuation',
'apikey' = 'apikey',
'loginout' = 'loginout',
'team' = 'team',
@@ -70,6 +72,11 @@ const AccountContainer = ({
}
]
: []),
{
icon: 'common/thirdParty',
label: t('account:third_party'),
value: TabEnum.thirdParty
},
{
icon: 'common/model',
label: t('account:model_provider'),

View File

@@ -92,7 +92,7 @@ const TeamSelector = ({
: []),
...teamList
];
}, [showManage, teamList, router]);
}, [showManage, t, teamList, router]);
return (
<Box w={'100%'}>

View File

@@ -38,10 +38,8 @@ import { formatTime2YMD } from '@fastgpt/global/common/string/time';
import { getExtraPlanCardRoute } from '@/web/support/wallet/sub/constants';
import StandardPlanContentList from '@/components/support/wallet/StandardPlanContentList';
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
import MyImage from '@fastgpt/web/components/common/Image/MyImage';
import { getWebReqUrl } from '@fastgpt/web/common/system/utils';
import AccountContainer from '../components/AccountContainer';
import { serviceSideProps } from '@fastgpt/web/common/system/nextjs';
@@ -52,8 +50,6 @@ const StandDetailModal = dynamic(() => import('./components/standardDetailModal'
const ConversionModal = dynamic(() => import('./components/ConversionModal'));
const UpdatePswModal = dynamic(() => import('./components/UpdatePswModal'));
const UpdateNotification = dynamic(() => import('./components/UpdateNotificationModal'));
const OpenAIAccountModal = dynamic(() => import('./components/OpenAIAccountModal'));
const LafAccountModal = dynamic(() => import('@/components/support/laf/LafAccountModal'));
const CommunityModal = dynamic(() => import('@/components/CommunityModal'));
const ModelPriceModal = dynamic(() =>
@@ -144,8 +140,7 @@ const MyInfo = ({ onOpenContact }: { onOpenContact: () => void }) => {
async (data: UserType) => {
await updateUserInfo({
avatar: data.avatar,
timezone: data.timezone,
openaiAccount: data.openaiAccount
timezone: data.timezone
});
reset(data);
toast({
@@ -353,11 +348,6 @@ const PlanUsage = () => {
onClose: onCloseStandardModal,
onOpen: onOpenStandardModal
} = useDisclosure();
const {
isOpen: isOpenAiPointsModal,
onClose: onCloseAiPointsModal,
onOpen: onOpenAiPointsModal
} = useDisclosure();
const planName = useMemo(() => {
if (!teamPlanStatus?.standard?.currentSubLevel) return '';
@@ -443,9 +433,13 @@ const PlanUsage = () => {
<MyIcon mr={2} name={'support/account/plans'} w={'20px'} />
{t('account_info:package_and_usage')}
</Flex>
<Button ml={4} size={'sm'} onClick={onOpenAiPointsModal}>
{t('account_info:billing_standard')}
</Button>
<ModelPriceModal>
{({ onOpen }) => (
<Button ml={4} size={'sm'} onClick={onOpen}>
{t('account_info:billing_standard')}
</Button>
)}
</ModelPriceModal>
<Button ml={4} variant={'whitePrimary'} size={'sm'} onClick={onOpenStandardModal}>
{t('account_info:package_details')}
</Button>
@@ -584,14 +578,24 @@ const PlanUsage = () => {
</Box>
</Box>
{isOpenStandardModal && <StandDetailModal onClose={onCloseStandardModal} />}
{isOpenAiPointsModal && <ModelPriceModal onClose={onCloseAiPointsModal} />}
</Box>
) : null;
};
const ButtonStyles = {
bg: 'white',
py: 3,
px: 6,
border: 'sm',
borderWidth: '1.5px',
borderRadius: 'md',
display: 'flex',
alignItems: 'center',
cursor: 'pointer',
userSelect: 'none' as any,
fontSize: 'sm'
};
const Other = ({ onOpenContact }: { onOpenContact: () => void }) => {
const theme = useTheme();
const { toast } = useToast();
const { feConfigs } = useSystemStore();
const { t } = useTranslation();
const { isPc } = useSystem();
@@ -600,56 +604,16 @@ const Other = ({ onOpenContact }: { onOpenContact: () => void }) => {
const { reset } = useForm<UserUpdateParams>({
defaultValues: userInfo as UserType
});
const { isOpen: isOpenLaf, onClose: onCloseLaf, onOpen: onOpenLaf } = useDisclosure();
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: t('account_info:update_success_tip'),
status: 'success'
});
},
[reset, t, toast, updateUserInfo]
);
const buttonStyles = useRef<FlexProps>({
bg: 'white',
py: 3,
px: 6,
border: theme.borders.sm,
borderWidth: '1.5px',
borderRadius: 'md',
alignItems: 'center',
cursor: 'pointer',
userSelect: 'none',
fontSize: 'sm'
});
return (
<Box>
<Grid gridGap={4} mt={3}>
{feConfigs?.docUrl && (
<Link
bg={'white'}
href={getDocPath('/docs/intro')}
target="_blank"
display={'flex'}
py={3}
px={6}
border={theme.borders.sm}
borderWidth={'1.5px'}
borderRadius={'md'}
alignItems={'center'}
userSelect={'none'}
textDecoration={'none !important'}
fontSize={'sm'}
{...ButtonStyles}
>
<MyIcon name={'common/courseLight'} w={'18px'} color={'myGray.600'} />
<Box ml={2} flex={1}>
@@ -662,76 +626,22 @@ const Other = ({ onOpenContact }: { onOpenContact: () => void }) => {
feConfigs?.navbarItems
?.filter((item) => item.isActive)
.map((item) => (
<Flex
key={item.id}
{...buttonStyles.current}
onClick={() => window.open(item.url, '_blank')}
>
<Flex key={item.id} {...ButtonStyles} onClick={() => window.open(item.url, '_blank')}>
<Avatar src={item.avatar} w={'18px'} />
<Box ml={2} flex={1}>
{item.name}
</Box>
</Flex>
))}
{feConfigs?.lafEnv && userInfo?.team.role === TeamMemberRoleEnum.owner && (
<Flex {...buttonStyles.current} onClick={onOpenLaf}>
<MyImage src="/imgs/workflow/laf.png" w={'18px'} alt="laf" />
<Box ml={2} flex={1}>
{'laf' + t('account_info:account_duplicate')}
</Box>
<Box
w={'9px'}
h={'9px'}
borderRadius={'50%'}
bg={userInfo?.team.lafAccount?.token ? '#67c13b' : 'myGray.500'}
/>
</Flex>
)}
{feConfigs?.show_openai_account && (
<Flex {...buttonStyles.current} onClick={onOpenOpenai}>
<MyIcon name={'common/openai'} w={'18px'} color={'myGray.600'} />
<Box ml={2} flex={1}>
{'OpenAI / OneAPI' + t('account_info:account_duplicate')}
</Box>
<Box
w={'9px'}
h={'9px'}
borderRadius={'50%'}
bg={userInfo?.openaiAccount?.key ? '#67c13b' : 'myGray.500'}
/>
</Flex>
)}
{feConfigs?.concatMd && (
<Button
variant={'whiteBase'}
justifyContent={'flex-start'}
leftIcon={<MyIcon name={'modal/concat'} w={'18px'} color={'myGray.600'} />}
onClick={onOpenContact}
h={'48px'}
fontSize={'sm'}
>
{t('account_info:contact_us')}
</Button>
<Flex onClick={onOpenContact} {...ButtonStyles}>
<MyIcon name={'modal/concat'} w={'18px'} color={'myGray.600'} />
<Box ml={2} flex={1}>
{t('account_info:contact_us')}
</Box>
</Flex>
)}
</Grid>
{isOpenLaf && userInfo && (
<LafAccountModal defaultData={userInfo?.team.lafAccount} onClose={onCloseLaf} />
)}
{isOpenOpenai && userInfo && (
<OpenAIAccountModal
defaultData={userInfo?.openaiAccount}
onSuccess={(data) =>
onclickSave({
...userInfo,
openaiAccount: data
})
}
onClose={onCloseOpenai}
/>
)}
</Box>
);
};

View File

@@ -130,7 +130,7 @@ const Team = () => {
<Flex align={'center'} ml={6}>
<TeamSelector height={'28px'} />
</Flex>
{userInfo?.team.role === TeamMemberRoleEnum.owner && (
{userInfo?.team?.role === TeamMemberRoleEnum.owner && (
<Flex align={'center'} justify={'center'} ml={2} p={'0.44rem'}>
<MyIcon
name="edit"

View File

@@ -3,41 +3,51 @@ import { ModalBody, Box, Flex, Input, ModalFooter, Button } from '@chakra-ui/rea
import MyModal from '@fastgpt/web/components/common/MyModal';
import { useTranslation } from 'next-i18next';
import { useForm } from 'react-hook-form';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import type { UserType } from '@fastgpt/global/support/user/type.d';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import type { OpenaiAccountType } from '@fastgpt/global/support/user/team/type';
import { useUserStore } from '@/web/support/user/useUserStore';
import { putUpdateTeam } from '@/web/support/user/team/api';
const OpenAIAccountModal = ({
defaultData,
onSuccess,
onClose
}: {
defaultData: UserType['openaiAccount'];
onSuccess: (e: UserType['openaiAccount']) => Promise<any>;
defaultData?: OpenaiAccountType;
onClose: () => void;
}) => {
const { t } = useTranslation();
const { userInfo, initUserInfo } = useUserStore();
const { register, handleSubmit } = useForm({
defaultValues: defaultData
});
const { mutate: onSubmit, isLoading } = useRequest({
mutationFn: async (data: UserType['openaiAccount']) => onSuccess(data),
onSuccess(res) {
onClose();
const { runAsync: onSubmit, loading } = useRequest2(
async (data: OpenaiAccountType) => {
if (!userInfo?.team.teamId) return;
return putUpdateTeam({
openaiAccount: data
});
},
errorToast: t('account_info:openai_account_setting_exception')
});
{
onSuccess: () => {
initUserInfo();
onClose();
},
successToast: t('common:common.Update Success'),
errorToast: t('common:common.Update Failed')
}
);
return (
<MyModal
isOpen
onClose={onClose}
iconSrc="common/openai"
title={t('account_info:openai_account_configuration')}
title={t('account_thirdParty:openai_account_configuration')}
>
<ModalBody>
<Box fontSize={'sm'} color={'myGray.500'}>
{t('account_info:open_api_notice')}
{t('account_thirdParty:open_api_notice')}
</Box>
<Flex alignItems={'center'} mt={5}>
<Box flex={'0 0 65px'}>API Key:</Box>
@@ -48,16 +58,16 @@ const OpenAIAccountModal = ({
<Input
flex={1}
{...register('baseUrl')}
placeholder={t('account_info:request_address_notice')}
></Input>
placeholder={t('account_thirdParty:request_address_notice')}
/>
</Flex>
</ModalBody>
<ModalFooter>
<Button mr={3} variant={'whiteBase'} onClick={onClose}>
{t('account_info:cancel')}
{t('common:common.Cancel')}
</Button>
<Button isLoading={isLoading} onClick={handleSubmit((data) => onSubmit(data))}>
{t('account_info:confirm')}
<Button isLoading={loading} onClick={handleSubmit(onSubmit)}>
{t('common:common.Confirm')}
</Button>
</ModalFooter>
</MyModal>

View File

@@ -0,0 +1,81 @@
import { Box, Button, Flex, Input, ModalBody, ModalFooter } from '@chakra-ui/react';
import MyModal from '@fastgpt/web/components/common/MyModal';
import React from 'react';
import { ThirdPartyAccountType } from '../index';
import { useTranslation } from 'next-i18next';
import { useForm } from 'react-hook-form';
import { useUserStore } from '@/web/support/user/useUserStore';
import { putUpdateTeam } from '@/web/support/user/team/api';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
const WorkflowVariableModal = ({
defaultData,
onClose
}: {
defaultData: ThirdPartyAccountType;
onClose: () => void;
}) => {
const { t } = useTranslation();
const { userInfo, initUserInfo } = useUserStore();
const { register, handleSubmit } = useForm({
defaultValues: {
value: '',
key: defaultData.key || ''
}
});
const { runAsync: onSubmit, loading } = useRequest2(
async (data: { key: string; value: string }) => {
if (!userInfo?.team.teamId) return;
await putUpdateTeam({
externalWorkflowVariable: data
});
},
{
onSuccess: () => {
initUserInfo();
onClose();
},
successToast: t('common:common.Update Success'),
errorToast: t('common:common.Update Failed')
}
);
return (
<MyModal title={`${defaultData.name} 配置`} iconSrc={'edit'} iconColor={'primary.600'}>
<ModalBody w={'420px'}>
<Box fontSize={'14px'} color={'myGray.900'}>
{defaultData.intro}
</Box>
<Box h={'1px'} bg={'myGray.150'} my={4}></Box>
<Flex alignItems={'center'}>
<Box fontSize={'14px'} color={'myGray.900'} fontWeight={'medium'}>
{t('common:core.workflow.value')}
</Box>
<Input
ml={8}
bg={'myGray.50'}
placeholder={t('account_thirdParty:value_placeholder')}
flex={1}
{...register('value')}
/>
</Flex>
<Box mt={1} color={'myGray.500'} fontSize={'xs'}>
{t('account_thirdParty:value_not_return_tip')}
</Box>
</ModalBody>
<ModalFooter>
<Button mr={3} variant={'whiteBase'} onClick={onClose}>
{t('common:common.Cancel')}
</Button>
<Button isLoading={loading} onClick={handleSubmit(onSubmit)}>
{t('common:common.Confirm')}
</Button>
</ModalFooter>
</MyModal>
);
};
export default React.memo(WorkflowVariableModal);

View File

@@ -0,0 +1,263 @@
import AccountContainer from '../components/AccountContainer';
import { Box, Flex, Grid, Progress, useDisclosure } from '@chakra-ui/react';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useTranslation } from 'next-i18next';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useUserStore } from '@/web/support/user/useUserStore';
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
import dynamic from 'next/dynamic';
import { useState, useMemo } from 'react';
import WorkflowVariableModal from './components/WorkflowVariableModal';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { serviceSideProps } from '@fastgpt/web/common/system/nextjs';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { GET } from '@/web/common/api/request';
import type { checkUsageResponse } from '@/pages/api/support/user/team/thirtdParty/checkUsage';
import MyBox from '@fastgpt/web/components/common/MyBox';
const LafAccountModal = dynamic(() => import('@/components/support/laf/LafAccountModal'));
const OpenAIAccountModal = dynamic(() => import('./components/OpenAIAccountModal'));
export type ThirdPartyAccountType = {
name: string;
icon: string;
iconColor?: string;
key?: string;
intro: string;
onClick?: () => void;
isOpen?: boolean;
active: boolean;
usage?: {
used: number;
total: number;
};
};
const ThirdParty = () => {
const { t } = useTranslation();
const { feConfigs } = useSystemStore();
const { toast } = useToast();
const { isOpen: isOpenLaf, onClose: onCloseLaf, onOpen: onOpenLaf } = useDisclosure();
const { isOpen: isOpenOpenai, onClose: onCloseOpenai, onOpen: onOpenOpenai } = useDisclosure();
const [workflowVariable, setWorkflowVariable] = useState<ThirdPartyAccountType>();
const { userInfo } = useUserStore();
const isOwner = userInfo?.team?.role === TeamMemberRoleEnum.owner;
const defaultAccountList: ThirdPartyAccountType[] = useMemo(
() => [
{
name: t('account_thirdParty:laf_account'),
icon: 'support/account/laf',
intro: t('common:support.user.Laf account intro'),
onClick: onOpenLaf,
isOpen: !!feConfigs?.lafEnv,
active: !!userInfo?.team?.lafAccount?.appid
},
{
name: t('account_thirdParty:openai_account_configuration'),
iconColor: 'black',
icon: 'common/openai',
intro: t('account_thirdParty:open_api_notice'),
onClick: onOpenOpenai,
isOpen: feConfigs?.show_openai_account,
active: userInfo?.team?.openaiAccount?.key !== undefined
}
],
[
feConfigs?.lafEnv,
feConfigs?.show_openai_account,
onOpenLaf,
onOpenOpenai,
t,
userInfo?.team?.lafAccount?.appid,
userInfo?.team?.openaiAccount?.key
]
);
const { data: workflowVariables = [], loading } = useRequest2(
async (): Promise<ThirdPartyAccountType[]> => {
return Promise.all(
(feConfigs?.externalProviderWorkflowVariables || []).map(async (item) => {
const usage = await (async () => {
try {
return await GET<checkUsageResponse>('/support/user/team/thirtdParty/checkUsage', {
key: item.key
});
} catch (err) {
return;
}
})();
const account = {
key: item.key,
name: item.name,
active: userInfo?.team?.externalWorkflowVariables?.[item.key] !== undefined,
icon: 'common/variable',
iconColor: 'primary.600',
intro: item.intro || t('account_thirdParty:no_intro')
};
return {
...account,
usage,
onClick: () => setWorkflowVariable(account),
isOpen: item.isOpen
};
})
);
},
{
manual: false,
refreshDeps: [
feConfigs?.externalProviderWorkflowVariables,
userInfo?.team?.externalWorkflowVariables
]
}
);
const accountList = useMemo(
() => [...defaultAccountList, ...workflowVariables],
[defaultAccountList, workflowVariables]
);
return (
<AccountContainer>
<MyBox isLoading={loading} px={[4, 8]} py={[4, 6]} bg={'white'} h={'full'}>
<Flex>
<MyIcon name={'common/thirdParty'} w={'24px'} color={'myGray.900'} />
<Box ml={3}>
<Box fontSize={'md'} color={'myGray.900'}>
{t('account_thirdParty:third_party_account')}
</Box>
<Box fontSize={'mini'} color={'myGray.500'}>
{t('account_thirdParty:third_party_account_desc')}
</Box>
</Box>
</Flex>
<Grid
gridTemplateColumns={[
'1fr',
'repeat(2,1fr)',
'repeat(3,1fr)',
'repeat(3,1fr)',
'repeat(4,1fr)'
]}
gridGap={4}
alignItems={'stretch'}
mt={5}
pb={5}
>
{accountList
.filter((item) => item.isOpen)
.map((item) => (
<Flex
key={item.name}
flexDirection={'column'}
border={'1px solid'}
borderColor={'myGray.200'}
pt={4}
px={5}
borderRadius={'10px'}
h={'146px'}
cursor={'pointer'}
_hover={{
borderColor: 'primary.600'
}}
onClick={
isOwner
? item.onClick
: () =>
toast({
title: t('account_thirdParty:error.no_permission'),
status: 'warning'
})
}
position={'relative'}
>
<Flex>
<MyIcon name={item.icon as any} w={'24px'} color={item.iconColor} />
<Box ml={2} flex={1} fontWeight={'medium'} fontSize={'16px'} color={'myGray.900'}>
{item.name}
</Box>
<Box
color={item.active ? 'green.600' : 'myGray.700'}
bg={item.active ? 'green.50' : 'myGray.100'}
px={2}
py={1}
borderRadius={'sm'}
fontSize={'10px'}
>
{item.active
? t('account_thirdParty:configured')
: t('account_thirdParty:not_configured')}
</Box>
</Flex>
<Box
className="textEllipsis2"
mt={3}
fontSize={'mini'}
color={'myGray.500'}
lineHeight={'18px'}
>
{item.intro}
</Box>
<Box flex={1} />
{item.active && item.usage && (
<Box w={'full'} mb={4}>
<Flex fontSize={'mini'} color={'myGray.500'}>
<Box>{t('account_thirdParty:usage')}</Box>
{item.usage?.total ? (
<Box ml={1}>
{item.usage.used}/{item.usage.total}
</Box>
) : (
<Box ml={1}>{t('account_thirdParty:unavailable')}</Box>
)}
</Flex>
<Box mt={1} w={'full'}>
<Progress
size={'sm'}
value={(item.usage.used / item.usage.total) * 100}
colorScheme={'blue'}
borderRadius={'md'}
borderWidth={'1px'}
borderColor={'low'}
isAnimated
hasStripe
/>
</Box>
</Box>
)}
</Flex>
))}
</Grid>
</MyBox>
{isOpenLaf && userInfo && (
<LafAccountModal defaultData={userInfo?.team?.lafAccount} onClose={onCloseLaf} />
)}
{isOpenOpenai && userInfo && (
<OpenAIAccountModal defaultData={userInfo?.team?.openaiAccount} onClose={onCloseOpenai} />
)}
{workflowVariable && (
<WorkflowVariableModal
defaultData={workflowVariable}
onClose={() => setWorkflowVariable(undefined)}
/>
)}
</AccountContainer>
);
};
export async function getServerSideProps(content: any) {
return {
props: {
...(await serviceSideProps(content, ['account', 'account_thirdParty']))
}
};
}
export default ThirdParty;

View File

@@ -0,0 +1,39 @@
import { NextAPI } from '@/service/middleware/entry';
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { MongoUser } from '@fastgpt/service/support/user/schema';
import { MongoTeam } from '@fastgpt/service/support/user/team/teamSchema';
import { NextApiRequest, NextApiResponse } from 'next';
async function handler(req: NextApiRequest, res: NextApiResponse) {
await authCert({ req, authRoot: true });
const users = await MongoUser.find(
{ openaiAccount: { $exists: true, $ne: null } },
'_id openaiAccount'
);
console.log(`${users.length} 个用户需要更新`);
let count = 0;
for (const user of users) {
await mongoSessionRun(async (session) => {
await MongoTeam.updateOne(
{ ownerId: user._id },
{
$set: { openaiAccount: (user as any).openaiAccount }
},
{ session }
);
// @ts-ignore
user.openaiAccount = undefined;
await user.save({ session });
});
count++;
console.log(`已更新 ${count} 个用户`);
}
return { success: true };
}
export default NextAPI(handler);

View File

@@ -94,6 +94,7 @@ async function initHttp(teamId?: string): Promise<any> {
await MongoAppVersion.create(
[
{
tmbId: plugin.tmbId,
appId: newPluginId,
nodes: item.modules,
edges: item.edges
@@ -166,6 +167,7 @@ async function initPlugin(teamId?: string): Promise<any> {
await MongoAppVersion.create(
[
{
tmbId: plugin.tmbId,
appId: newPluginId,
nodes: plugin.modules,
edges: plugin.edges

View File

@@ -40,6 +40,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
res.setHeader('Content-Type', `${file.contentType}; charset=${encoding}`);
res.setHeader('Cache-Control', 'public, max-age=31536000');
res.setHeader('Content-Disposition', `inline; filename="${encodeURIComponent(file.filename)}"`);
res.setHeader('Content-Length', file.length);
stream.pipe(res);

View File

@@ -40,6 +40,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
res.setHeader('Content-Type', `${file.contentType}; charset=${encoding}`);
res.setHeader('Cache-Control', 'public, max-age=31536000');
res.setHeader('Content-Disposition', `inline; filename="${encodeURIComponent(filename)}"`);
res.setHeader('Content-Length', file.length);
stream.pipe(res);

View File

@@ -45,8 +45,9 @@ async function handler(req: ApiRequestProps<CreateAppBody>) {
// 上限校验
await checkTeamAppLimit(teamId);
const tmb = await MongoTeamMember.findById({ _id: tmbId });
const user = await MongoUser.findById({ _id: tmb?.userId });
const tmb = await MongoTeamMember.findById({ _id: tmbId }, 'userId').populate<{
user: { avatar: string; username: string };
}>('user', 'avatar username');
// 创建app
const appId = await onCreateApp({
@@ -59,8 +60,8 @@ async function handler(req: ApiRequestProps<CreateAppBody>) {
chatConfig,
teamId,
tmbId,
userAvatar: user?.avatar,
username: user?.username
userAvatar: tmb?.user?.avatar,
username: tmb?.user?.username
});
pushTrack.createApp({
@@ -132,6 +133,7 @@ export const onCreateApp = async ({
await MongoAppVersion.create(
[
{
tmbId,
appId,
nodes: modules,
edges,

View File

@@ -45,7 +45,8 @@ async function handler(
currentCost: plugin.currentCost,
hasTokenFee: plugin.hasTokenFee,
author: plugin.author,
instructions: plugin.userGuide
instructions: plugin.userGuide,
courseUrl: plugin.courseUrl
}))
.filter((item) => {
if (searchKey) {

View File

@@ -1,8 +1,8 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { NextAPI } from '@/service/middleware/entry';
import { TemplateMarketItemType } from '@fastgpt/global/core/workflow/type';
import { getTemplateMarketItemDetail } from '@/service/core/app/template';
import { AppTemplateSchemaType } from '@fastgpt/global/core/app/type';
import { getAppTemplatesAndLoadThem } from '@fastgpt/templates/register';
type Props = {
templateId: string;
@@ -11,11 +11,13 @@ type Props = {
async function handler(
req: NextApiRequest,
res: NextApiResponse<any>
): Promise<TemplateMarketItemType | undefined> {
): Promise<AppTemplateSchemaType | undefined> {
await authCert({ req, authToken: true });
const { templateId } = req.query as Props;
return getTemplateMarketItemDetail(templateId);
const templateMarketItems: AppTemplateSchemaType[] = await getAppTemplatesAndLoadThem();
return templateMarketItems.find((item) => item.templateId === templateId);
}
export default NextAPI(handler);

View File

@@ -1,16 +1,53 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import type { NextApiResponse } from 'next';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { NextAPI } from '@/service/middleware/entry';
import { TemplateMarketListItemType } from '@fastgpt/global/core/workflow/type';
import { getTemplateMarketItemList } from '@/service/core/app/template';
import { getAppTemplatesAndLoadThem } from '@fastgpt/templates/register';
import { AppTemplateSchemaType } from '@fastgpt/global/core/app/type';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { ApiRequestProps } from '@fastgpt/service/type/next';
export type ListParams = {
isQuickTemplate?: boolean;
type?: AppTypeEnum | 'all';
};
async function handler(
req: NextApiRequest,
req: ApiRequestProps<ListParams>,
res: NextApiResponse<any>
): Promise<TemplateMarketListItemType[]> {
): Promise<AppTemplateSchemaType[]> {
await authCert({ req, authToken: true });
return getTemplateMarketItemList();
const { isQuickTemplate = false, type = 'all' } = req.query;
const templateMarketItems = await getAppTemplatesAndLoadThem();
let filteredItems = templateMarketItems.filter((item) => {
if (!item.isActive) return false;
if (type === 'all') return true;
return item.type === type;
});
if (isQuickTemplate) {
if (filteredItems.some((item) => item.isQuickTemplate !== undefined)) {
filteredItems = filteredItems.filter((item) => item.isQuickTemplate);
} else {
filteredItems = filteredItems.slice(0, 3);
}
}
return filteredItems.map((item) => {
return {
templateId: item.templateId,
name: item.name,
intro: item.intro,
avatar: item.avatar,
tags: item.tags,
type: item.type,
author: item.author,
userGuide: item.userGuide,
workflow: {}
};
});
}
export default NextAPI(handler);

View File

@@ -11,12 +11,9 @@ import { WritePermissionVal } from '@fastgpt/global/support/permission/constant'
import { ApiRequestProps } from '@fastgpt/service/type/next';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
async function handler(
req: ApiRequestProps<PostPublishAppProps>,
res: NextApiResponse<any>
): Promise<{}> {
async function handler(req: ApiRequestProps<PostPublishAppProps>, res: NextApiResponse<any>) {
const { appId } = req.query as { appId: string };
const { nodes = [], edges = [], chatConfig, isPublish, versionName } = req.body;
const { nodes = [], edges = [], chatConfig, isPublish, versionName, autoSave } = req.body;
const { app, tmbId } = await authApp({ appId, req, per: WritePermissionVal, authToken: true });
@@ -25,6 +22,15 @@ async function handler(
isPlugin: app.type === AppTypeEnum.plugin
});
if (autoSave) {
return MongoApp.findByIdAndUpdate(appId, {
modules: formatNodes,
edges,
chatConfig,
updateTime: new Date()
});
}
await mongoSessionRun(async (session) => {
// create version histories
const [{ _id }] = await MongoAppVersion.create(
@@ -70,8 +76,6 @@ async function handler(
}
);
});
return {};
}
export default NextAPI(handler);

View File

@@ -10,7 +10,7 @@ import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants'
import type { AIChatItemType, UserChatItemType } from '@fastgpt/global/core/chat/type';
import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { dispatchWorkFlow } from '@fastgpt/service/core/workflow/dispatch';
import { getUserChatInfoAndAuthTeamPoints } from '@/service/support/permission/auth/team';
import { getUserChatInfoAndAuthTeamPoints } from '@fastgpt/service/support/permission/auth/team';
import { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
import {
concatHistories,
@@ -115,7 +115,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
})();
const limit = getMaxHistoryLimitFromNodes(nodes);
const [{ histories }, chatDetail, { user }] = await Promise.all([
const [{ histories }, chatDetail, { timezone, externalProvider }] = await Promise.all([
getChatItems({
appId,
chatId,
@@ -158,7 +158,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
res,
requestOrigin: req.headers.origin,
mode: 'test',
user,
timezone,
externalProvider,
uid: tmbId,
runningAppInfo: {
@@ -204,7 +205,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
const { text: userInteractiveVal } = chatValue2RuntimePrompt(userQuestion.value);
const newTitle = isPlugin
? variables.cTime ?? getSystemTime(user.timezone)
? variables.cTime ?? getSystemTime(timezone)
: getChatTitleFromChatMessage(userQuestion);
const aiResponse: AIChatItemType & { dataId?: string } = {

View File

@@ -11,6 +11,7 @@ import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
import { getAppLatestVersion } from '@fastgpt/service/core/app/version/controller';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { NextAPI } from '@/service/middleware/entry';
import { UserModelSchema } from '@fastgpt/global/support/user/type';
async function handler(req: NextApiRequest, res: NextApiResponse) {
let { chatId, shareId, outLinkUid } = req.query as InitOutLinkChatProps;
@@ -20,7 +21,9 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
// auth app permission
const [tmb, chat, app] = await Promise.all([
MongoTeamMember.findById(outLinkConfig.tmbId, '_id userId').populate('userId', 'avatar').lean(),
MongoTeamMember.findById(outLinkConfig.tmbId, '_id userId')
.populate<{ user: UserModelSchema }>('user', 'avatar')
.lean(),
MongoChat.findOne({ appId, chatId, shareId }).lean(),
MongoApp.findById(appId).lean()
]);
@@ -45,8 +48,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
chatId,
appId: app._id,
title: chat?.title,
//@ts-ignore
userAvatar: tmb?.userId?.avatar,
userAvatar: tmb?.user?.avatar,
variables: chat?.variables,
app: {
chatConfig: getAppChatConfig({

View File

@@ -66,9 +66,9 @@ async function handler(
return {
type: DatasetSourceReadTypeEnum.apiFile,
sourceId: collection.apiFileId,
apiServer: collection.datasetId.apiServer,
feishuServer: collection.datasetId.feishuServer,
yuqueServer: collection.datasetId.yuqueServer
apiServer: collection.dataset.apiServer,
feishuServer: collection.dataset.feishuServer,
yuqueServer: collection.dataset.yuqueServer
};
}
if (collection.type === DatasetCollectionTypeEnum.externalFile) {
@@ -90,12 +90,12 @@ async function handler(
return mongoSessionRun(async (session) => {
const { collectionId } = await createCollectionAndInsertData({
dataset: collection.datasetId,
dataset: collection.dataset,
rawText,
createCollectionParams: {
teamId: collection.teamId,
tmbId: collection.tmbId,
datasetId: collection.datasetId._id,
datasetId: collection.dataset._id,
name: collection.name,
type: collection.type,

View File

@@ -25,7 +25,7 @@ async function handler(req: NextApiRequest) {
// find all delete id
const collections = await findCollectionAndChild({
teamId,
datasetId: collection.datasetId._id,
datasetId: collection.datasetId,
collectionId,
fields: '_id teamId datasetId fileId metadata'
});

View File

@@ -37,7 +37,7 @@ async function handler(req: NextApiRequest): Promise<DatasetCollectionItemType>
...collection,
...getCollectionSourceData(collection),
tags: await collectionTagsToTagLabel({
datasetId: collection.datasetId._id,
datasetId: collection.datasetId,
tags: collection.tags
}),
permission,

View File

@@ -148,9 +148,9 @@ async function handler(
return collection.rawLink;
}
if (collection.type === DatasetCollectionTypeEnum.apiFile && collection.apiFileId) {
const apiServer = collection.datasetId.apiServer;
const feishuServer = collection.datasetId.feishuServer;
const yuqueServer = collection.datasetId.yuqueServer;
const apiServer = collection.dataset.apiServer;
const feishuServer = collection.dataset.feishuServer;
const yuqueServer = collection.dataset.yuqueServer;
if (apiServer) {
return useApiDatasetRequest({ apiServer }).getFilePreviewUrl({
@@ -170,11 +170,8 @@ async function handler(
return '';
}
if (collection.type === DatasetCollectionTypeEnum.externalFile) {
if (collection.externalFileId && collection.datasetId.externalReadUrl) {
return collection.datasetId.externalReadUrl.replace(
'{{fileId}}',
collection.externalFileId
);
if (collection.externalFileId && collection.dataset.externalReadUrl) {
return collection.dataset.externalReadUrl.replace('{{fileId}}', collection.externalFileId);
}
if (collection.externalFileUrl) {
return collection.externalFileUrl;

View File

@@ -100,7 +100,7 @@ async function handler(req: ApiRequestProps<UpdateDatasetCollectionParams>) {
const collectionTags = await createOrGetCollectionTags({
tags,
teamId,
datasetId: collection.datasetId._id,
datasetId: collection.datasetId,
session
});

View File

@@ -45,7 +45,7 @@ async function handler(req: NextApiRequest) {
// auth collection and get dataset
const [
{
datasetId: { _id: datasetId, vectorModel }
dataset: { _id: datasetId, vectorModel }
}
] = await Promise.all([getCollectionWithDataset(collectionId)]);

View File

@@ -31,7 +31,7 @@ async function handler(
const queryReg = new RegExp(`${replaceRegChars(searchText)}`, 'i');
const match = {
teamId,
datasetId: collection.datasetId._id,
datasetId: collection.datasetId,
collectionId,
...(searchText.trim()
? {

View File

@@ -1,9 +1,6 @@
/* push data to training queue */
import type { NextApiRequest, NextApiResponse } from 'next';
import type {
PushDatasetDataProps,
PushDatasetDataResponse
} from '@fastgpt/global/core/dataset/api.d';
import type { PushDatasetDataProps } from '@fastgpt/global/core/dataset/api.d';
import { authDatasetCollection } from '@fastgpt/service/support/permission/dataset/auth';
import { checkDatasetLimit } from '@fastgpt/service/support/permission/teamLimit';
import { predictDataLimitLength } from '@fastgpt/global/core/dataset/utils';
@@ -42,9 +39,9 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
...body,
teamId,
tmbId,
datasetId: collection.datasetId._id,
agentModel: collection.datasetId.agentModel,
vectorModel: collection.datasetId.vectorModel
datasetId: collection.datasetId,
agentModel: collection.dataset.agentModel,
vectorModel: collection.dataset.vectorModel
});
}

View File

@@ -12,7 +12,7 @@ async function handler(req: ApiRequestProps<UpdateDatasetDataProps>) {
// auth data permission
const {
collection: {
datasetId: { vectorModel }
dataset: { vectorModel }
},
teamId,
tmbId

View File

@@ -32,7 +32,7 @@ async function handler(
const queryReg = new RegExp(`${replaceRegChars(searchText)}`, 'i');
const match = {
teamId,
datasetId: collection.datasetId._id,
datasetId: collection.datasetId,
collectionId,
...(searchText.trim()
? {

View File

@@ -4,7 +4,7 @@ import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants'
import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { dispatchWorkFlow } from '@fastgpt/service/core/workflow/dispatch';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { getUserChatInfoAndAuthTeamPoints } from '@/service/support/permission/auth/team';
import { getUserChatInfoAndAuthTeamPoints } from '@fastgpt/service/support/permission/auth/team';
import { PostWorkflowDebugProps, PostWorkflowDebugResponse } from '@/global/core/workflow/api';
import { NextAPI } from '@/service/middleware/entry';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
@@ -37,7 +37,7 @@ async function handler(
]);
// auth balance
const { user } = await getUserChatInfoAndAuthTeamPoints(tmbId);
const { timezone, externalProvider } = await getUserChatInfoAndAuthTeamPoints(tmbId);
/* start process */
const { flowUsages, flowResponses, debugResponse, newVariables } = await dispatchWorkFlow({
@@ -50,7 +50,8 @@ async function handler(
tmbId
},
uid: tmbId,
user,
timezone,
externalProvider,
runtimeNodes: nodes,
runtimeEdges: edges,
variables,

View File

@@ -2,14 +2,39 @@ import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { getUserDetail } from '@fastgpt/service/support/user/controller';
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry';
import { UserType } from '@fastgpt/global/support/user/type';
export type TokenLoginQuery = {};
export type TokenLoginBody = {};
export type TokenLoginResponse = {};
export type TokenLoginResponse = UserType;
async function handler(
req: ApiRequestProps<TokenLoginBody, TokenLoginQuery>,
_res: ApiResponseType<any>
): Promise<TokenLoginResponse> {
const { tmbId } = await authCert({ req, authToken: true });
return getUserDetail({ tmbId });
const user = await getUserDetail({ tmbId });
// Remove sensitive information
if (user.team.lafAccount) {
user.team.lafAccount = {
appid: user.team.lafAccount.appid,
token: '',
pat: ''
};
}
if (user.team.openaiAccount) {
user.team.openaiAccount = {
key: '',
baseUrl: user.team.openaiAccount.baseUrl
};
}
if (user.team.externalWorkflowVariables) {
user.team.externalWorkflowVariables = Object.fromEntries(
Object.entries(user.team.externalWorkflowVariables).map(([key, value]) => [key, ''])
);
}
return user;
}
export default NextAPI(handler);

View File

@@ -1,7 +1,6 @@
import { MongoUser } from '@fastgpt/service/support/user/schema';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { UserUpdateParams } from '@/types/user';
import { getAIApi, openaiBaseUrl } from '@fastgpt/service/core/ai/config';
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
/* update user info */
@@ -14,7 +13,7 @@ async function handler(
req: ApiRequestProps<UserAccountUpdateBody, UserAccountUpdateQuery>,
_res: ApiResponseType<any>
): Promise<UserAccountUpdateResponse> {
const { avatar, timezone, openaiAccount, lafAccount } = req.body;
const { avatar, timezone } = req.body;
const { tmbId } = await authCert({ req, authToken: true });
const tmb = await MongoTeamMember.findById(tmbId);
@@ -22,25 +21,6 @@ async function handler(
throw new Error('can not find it');
}
const userId = tmb.userId;
// auth key
if (openaiAccount?.key) {
console.log('auth user openai key', openaiAccount?.key);
const baseUrl = openaiAccount?.baseUrl || openaiBaseUrl;
openaiAccount.baseUrl = baseUrl;
const ai = getAIApi({
userKey: openaiAccount
});
const response = await ai.chat.completions.create({
model: 'gpt-4o-mini',
max_tokens: 1,
messages: [{ role: 'user', content: 'hi' }]
});
if (response?.choices?.[0]?.message?.content === undefined) {
throw new Error('Key response is empty');
}
}
// 更新对应的记录
await MongoUser.updateOne(
@@ -49,9 +29,7 @@ async function handler(
},
{
...(avatar && { avatar }),
...(timezone && { timezone }),
openaiAccount: openaiAccount?.key ? openaiAccount : null,
lafAccount: lafAccount?.token ? lafAccount : null
...(timezone && { timezone })
}
);

View File

@@ -0,0 +1,50 @@
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry';
import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
import axios from 'axios';
import { addLog } from '@fastgpt/service/common/system/log';
export type checkUsageQuery = { key: string };
export type checkUsageBody = {};
export type checkUsageResponse =
| {
total: number;
used: number;
}
| undefined;
async function handler(
req: ApiRequestProps<checkUsageBody, checkUsageQuery>,
res: ApiResponseType<any>
): Promise<checkUsageResponse> {
try {
const { key } = req.query;
const { tmb } = await authUserPer({ req, authToken: true, per: ReadPermissionVal });
const url = global.feConfigs.externalProviderWorkflowVariables?.find(
(item) => item.key === key
)?.url;
if (!url || !tmb.externalWorkflowVariables?.[key]) return undefined;
const { data } = await axios.get<checkUsageResponse>(url, {
headers: {
Authorization: `Bearer ${tmb.externalWorkflowVariables[key]}`
}
});
if (!data) return undefined;
return {
total: data.total || 0,
used: data.used || 0
};
} catch (error) {
addLog.debug('checkUsage error', { error });
}
}
export default NextAPI(handler);

View File

@@ -33,7 +33,7 @@ import {
removeEmptyUserInput
} from '@fastgpt/global/core/chat/utils';
import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools';
import { getUserChatInfoAndAuthTeamPoints } from '@/service/support/permission/auth/team';
import { getUserChatInfoAndAuthTeamPoints } from '@fastgpt/service/support/permission/auth/team';
import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant';
import { MongoApp } from '@fastgpt/service/core/app/schema';
import { UserModelSchema } from '@fastgpt/global/support/user/type';
@@ -59,6 +59,7 @@ import { rewriteNodeOutputByHistories } from '@fastgpt/global/core/workflow/runt
import { getWorkflowResponseWrite } from '@fastgpt/service/core/workflow/dispatch/utils';
import { WORKFLOW_MAX_RUN_TIMES } from '@fastgpt/service/core/workflow/constants';
import { getPluginInputsFromStoreNodes } from '@fastgpt/global/core/app/plugin/utils';
import { ExternalProviderType } from '@fastgpt/global/core/workflow/runtime/type';
type FastGptWebChatProps = {
chatId?: string; // undefined: get histories from messages, '': new chat, 'xxxxx': get histories from db
@@ -80,7 +81,8 @@ export type Props = ChatCompletionCreateParams &
type AuthResponseType = {
teamId: string;
tmbId: string;
user: UserModelSchema;
timezone: string;
externalProvider: ExternalProviderType;
app: AppSchema;
responseDetail?: boolean;
showNodeStatus?: boolean;
@@ -154,7 +156,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
const {
teamId,
tmbId,
user,
timezone,
externalProvider,
app,
responseDetail,
authType,
@@ -269,7 +272,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
res,
requestOrigin: req.headers.origin,
mode: 'chat',
user,
timezone,
externalProvider,
runningAppInfo: {
id: String(app._id),
@@ -314,7 +318,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
const { text: userInteractiveVal } = chatValue2RuntimePrompt(userQuestion.value);
const newTitle = isPlugin
? variables.cTime ?? getSystemTime(user.timezone)
? variables.cTime ?? getSystemTime(timezone)
: getChatTitleFromChatMessage(userQuestion);
const aiResponse: AIChatItemType & { dataId?: string } = {
@@ -459,8 +463,18 @@ const authShareChat = async ({
shareId: string;
chatId?: string;
}): Promise<AuthResponseType> => {
const { teamId, tmbId, user, appId, authType, responseDetail, showNodeStatus, uid, sourceName } =
await authOutLinkChatStart(data);
const {
teamId,
tmbId,
timezone,
externalProvider,
appId,
authType,
responseDetail,
showNodeStatus,
uid,
sourceName
} = await authOutLinkChatStart(data);
const app = await MongoApp.findById(appId).lean();
if (!app) {
@@ -477,8 +491,9 @@ const authShareChat = async ({
sourceName,
teamId,
tmbId,
user,
app,
timezone,
externalProvider,
apikey: '',
authType,
responseAllData: false,
@@ -508,7 +523,7 @@ const authTeamSpaceChat = async ({
return Promise.reject('app is empty');
}
const [chat, { user }] = await Promise.all([
const [chat, { timezone, externalProvider }] = await Promise.all([
MongoChat.findOne({ appId, chatId }).lean(),
getUserChatInfoAndAuthTeamPoints(app.tmbId)
]);
@@ -520,8 +535,9 @@ const authTeamSpaceChat = async ({
return {
teamId,
tmbId: app.tmbId,
user,
app,
timezone,
externalProvider,
authType: AuthUserTypeEnum.outLink,
apikey: '',
responseAllData: false,
@@ -588,7 +604,7 @@ const authHeaderRequest = async ({
}
})();
const [{ user }, chat] = await Promise.all([
const [{ timezone, externalProvider }, chat] = await Promise.all([
getUserChatInfoAndAuthTeamPoints(tmbId),
MongoChat.findOne({ appId, chatId }).lean()
]);
@@ -605,7 +621,8 @@ const authHeaderRequest = async ({
return {
teamId,
tmbId,
user,
timezone,
externalProvider,
app,
apikey,
authType,

View File

@@ -9,7 +9,6 @@ import {
Button,
HStack
} from '@chakra-ui/react';
import { SmallAddIcon } from '@chakra-ui/icons';
import type { AppSimpleEditFormType } from '@fastgpt/global/core/app/type.d';
import { useRouter } from 'next/router';
import { useTranslation } from 'next-i18next';
@@ -25,20 +24,18 @@ import { formatEditorVariablePickerIcon } from '@fastgpt/global/core/workflow/ut
import SearchParamsTip from '@/components/core/dataset/SearchParamsTip';
import SettingLLMModel from '@/components/core/ai/SettingLLMModel';
import type { SettingAIDataType } from '@fastgpt/global/core/app/type.d';
import DeleteIcon, { hoverDeleteStyles } from '@fastgpt/web/components/common/Icon/delete';
import { TTSTypeEnum } from '@/web/core/app/constants';
import { workflowSystemVariables } from '@/web/core/app/utils';
import { useI18n } from '@/web/context/I18n';
import { useContextSelector } from 'use-context-selector';
import { AppContext } from '@/pages/app/detail/components/context';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import VariableTip from '@/components/common/Textarea/MyTextarea/VariableTip';
import { getWebLLMModel } from '@/web/common/system/utils';
import ToolSelect from './components/ToolSelect';
const DatasetSelectModal = dynamic(() => import('@/components/core/app/DatasetSelectModal'));
const DatasetParamsModal = dynamic(() => import('@/components/core/app/DatasetParamsModal'));
const ToolSelectModal = dynamic(() => import('./components/ToolSelectModal'));
const TTSSelect = dynamic(() => import('@/components/core/app/TTSSelect'));
const QGConfig = dynamic(() => import('@/components/core/app/QGConfig'));
const WhisperConfig = dynamic(() => import('@/components/core/app/WhisperConfig'));
@@ -70,7 +67,6 @@ const EditForm = ({
const theme = useTheme();
const router = useRouter();
const { t } = useTranslation();
const { appT } = useI18n();
const { appDetail } = useContextSelector(AppContext, (v) => v);
@@ -95,11 +91,6 @@ const EditForm = ({
onOpen: onOpenDatasetParams,
onClose: onCloseDatasetParams
} = useDisclosure();
const {
isOpen: isOpenToolsSelect,
onOpen: onOpenToolsSelect,
onClose: onCloseToolsSelect
} = useDisclosure();
const formatVariables = useMemo(
() =>
@@ -278,67 +269,7 @@ const EditForm = ({
{/* tool choice */}
<Box {...BoxStyles}>
<Flex alignItems={'center'}>
<Flex alignItems={'center'} flex={1}>
<MyIcon name={'core/app/toolCall'} w={'20px'} />
<FormLabel ml={2}>{appT('plugin_dispatch')}</FormLabel>
<QuestionTip ml={1} label={appT('plugin_dispatch_tip')} />
</Flex>
<Button
variant={'transparentBase'}
leftIcon={<SmallAddIcon />}
iconSpacing={1}
mr={'-5px'}
size={'sm'}
fontSize={'sm'}
onClick={onOpenToolsSelect}
>
{t('common:common.Choose')}
</Button>
</Flex>
<Grid
mt={appForm.selectedTools.length > 0 ? 2 : 0}
gridTemplateColumns={'repeat(2, minmax(0, 1fr))'}
gridGap={[2, 4]}
>
{appForm.selectedTools.map((item) => (
<MyTooltip key={item.id} label={item.intro}>
<Flex
overflow={'hidden'}
alignItems={'center'}
p={2.5}
bg={'white'}
boxShadow={'0 4px 8px -2px rgba(16,24,40,.1),0 2px 4px -2px rgba(16,24,40,.06)'}
borderRadius={'md'}
border={theme.borders.base}
_hover={{
...hoverDeleteStyles,
borderColor: 'primary.300'
}}
>
<Avatar src={item.avatar} w={'1.5rem'} borderRadius={'sm'} />
<Box
ml={2}
flex={'1 0 0'}
w={0}
className={'textEllipsis'}
fontSize={'sm'}
color={'myGray.900'}
>
{item.name}
</Box>
<DeleteIcon
onClick={() => {
setAppForm((state) => ({
...state,
selectedTools: state.selectedTools.filter((tool) => tool.id !== item.id)
}));
}}
/>
</Flex>
</MyTooltip>
))}
</Grid>
<ToolSelect appForm={appForm} setAppForm={setAppForm} />
</Box>
{/* File select */}
@@ -494,24 +425,6 @@ const EditForm = ({
}}
/>
)}
{isOpenToolsSelect && (
<ToolSelectModal
selectedTools={appForm.selectedTools}
onAddTool={(e) => {
setAppForm((state) => ({
...state,
selectedTools: [...state.selectedTools, e]
}));
}}
onRemoveTool={(e) => {
setAppForm((state) => ({
...state,
selectedTools: state.selectedTools.filter((item) => item.pluginId !== e.id)
}));
}}
onClose={onCloseToolsSelect}
/>
)}
</>
);
};

View File

@@ -76,10 +76,12 @@ const Header = ({
const { runAsync: onClickSave, loading } = useRequest2(
async ({
isPublish,
versionName = formatTime2YMDHMS(new Date())
versionName = formatTime2YMDHMS(new Date()),
autoSave
}: {
isPublish?: boolean;
versionName?: string;
autoSave?: boolean;
}) => {
const { nodes, edges } = form2AppWorkflow(appForm, t);
await onSaveApp({
@@ -87,7 +89,8 @@ const Header = ({
edges,
chatConfig: appForm.chatConfig,
isPublish,
versionName
versionName,
autoSave
});
setPast((prevPast) =>
prevPast.map((item, index) =>
@@ -157,7 +160,7 @@ const Header = ({
if (isSaved) return;
try {
console.log('Leave auto save');
return onClickSave({ isPublish: false, versionName: t('app:auto_save') });
return onClickSave({ isPublish: false, autoSave: true });
} catch (error) {
console.error(error);
}

View File

@@ -0,0 +1,130 @@
import { Button, HStack, ModalBody, ModalFooter } from '@chakra-ui/react';
import MyModal from '@fastgpt/web/components/common/MyModal';
import React from 'react';
import { useTranslation } from 'next-i18next';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { Box } from '@chakra-ui/react';
import { AppSimpleEditFormType } from '@fastgpt/global/core/app/type';
import { childAppSystemKey } from './ToolSelectModal';
import { Controller, useForm } from 'react-hook-form';
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
import RenderPluginInput from '@/components/core/chat/ChatContainer/PluginRunBox/components/renderPluginInput';
import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import UseGuideModal from '@/components/common/Modal/UseGuideModal';
const ConfigToolModal = ({
configTool,
onCloseConfigTool,
onAddTool
}: {
configTool: AppSimpleEditFormType['selectedTools'][number];
onCloseConfigTool: () => void;
onAddTool: (tool: AppSimpleEditFormType['selectedTools'][number]) => void;
}) => {
const { t } = useTranslation();
const {
handleSubmit,
control,
formState: { errors }
} = useForm({
defaultValues: configTool
? configTool.inputs.reduce(
(acc, input) => {
acc[input.key] = input.value || input.defaultValue;
return acc;
},
{} as Record<string, any>
)
: {}
});
return (
<MyModal
isOpen
isCentered
title={t('common:core.app.ToolCall.Parameter setting')}
iconSrc="core/app/toolCall"
overflow={'auto'}
>
<ModalBody>
<HStack mb={4} spacing={1} fontSize={'sm'}>
<MyIcon name={'common/info'} w={'1.25rem'} />
<Box flex={1}>{t('app:tool_input_param_tip')}</Box>
{!!(configTool?.courseUrl || configTool?.userGuide) && (
<UseGuideModal
title={configTool?.name}
iconSrc={configTool?.avatar}
text={configTool?.userGuide}
link={configTool?.courseUrl}
>
{({ onClick }) => (
<Box cursor={'pointer'} color={'primary.500'} onClick={onClick}>
{t('app:workflow.Input guide')}
</Box>
)}
</UseGuideModal>
)}
</HStack>
{configTool.inputs
.filter(
(input) =>
!input.toolDescription &&
!childAppSystemKey.includes(input.key) &&
!input.renderTypeList.includes(FlowNodeInputTypeEnum.selectLLMModel) &&
!input.renderTypeList.includes(FlowNodeInputTypeEnum.fileSelect)
)
.map((input) => {
return (
<Controller
key={input.key}
control={control}
name={input.key}
rules={{
validate: (value) => {
if (input.valueType === WorkflowIOValueTypeEnum.boolean) {
return value !== undefined;
}
return !!value;
}
}}
render={({ field: { onChange, value } }) => {
return (
<RenderPluginInput
value={value}
isInvalid={errors && Object.keys(errors).includes(input.key)}
onChange={onChange}
input={input}
setUploading={() => {}}
/>
);
}}
/>
);
})}
</ModalBody>
<ModalFooter gap={6}>
<Button onClick={onCloseConfigTool} variant={'whiteBase'}>
{t('common:common.Cancel')}
</Button>
<Button
variant={'primary'}
onClick={handleSubmit((data) => {
onAddTool({
...configTool,
inputs: configTool.inputs.map((input) => ({
...input,
value: data[input.key] ?? input.value
}))
});
onCloseConfigTool();
})}
>
{t('common:common.Confirm')}
</Button>
</ModalFooter>
</MyModal>
);
};
export default React.memo(ConfigToolModal);

View File

@@ -0,0 +1,157 @@
import { Box, Button, Flex, Grid, useDisclosure } from '@chakra-ui/react';
import React, { useState } from 'react';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useTranslation } from 'next-i18next';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import { SmallAddIcon } from '@chakra-ui/icons';
import { AppSimpleEditFormType } from '@fastgpt/global/core/app/type';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { theme } from '@fastgpt/web/styles/theme';
import DeleteIcon, { hoverDeleteStyles } from '@fastgpt/web/components/common/Icon/delete';
import ToolSelectModal, { childAppSystemKey } from './ToolSelectModal';
import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import Avatar from '@fastgpt/web/components/common/Avatar';
import ConfigToolModal from './ConfigToolModal';
import { getWebLLMModel } from '@/web/common/system/utils';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
const ToolSelect = ({
appForm,
setAppForm
}: {
appForm: AppSimpleEditFormType;
setAppForm: React.Dispatch<React.SetStateAction<AppSimpleEditFormType>>;
}) => {
const { t } = useTranslation();
const [configTool, setConfigTool] = useState<
AppSimpleEditFormType['selectedTools'][number] | null
>(null);
const {
isOpen: isOpenToolsSelect,
onOpen: onOpenToolsSelect,
onClose: onCloseToolsSelect
} = useDisclosure();
const selectedModel = getWebLLMModel(appForm.aiSettings.model);
return (
<>
<Flex alignItems={'center'}>
<Flex alignItems={'center'} flex={1}>
<MyIcon name={'core/app/toolCall'} w={'20px'} />
<FormLabel ml={2}>{t('common:core.app.Tool call')}</FormLabel>
<QuestionTip ml={1} label={t('app:plugin_dispatch_tip')} />
</Flex>
<Button
variant={'transparentBase'}
leftIcon={<SmallAddIcon />}
iconSpacing={1}
mr={'-5px'}
size={'sm'}
fontSize={'sm'}
onClick={onOpenToolsSelect}
>
{t('common:common.Choose')}
</Button>
</Flex>
<Grid
mt={appForm.selectedTools.length > 0 ? 2 : 0}
gridTemplateColumns={'repeat(2, minmax(0, 1fr))'}
gridGap={[2, 4]}
>
{appForm.selectedTools.map((item) => (
<MyTooltip key={item.id} label={item.intro}>
<Flex
overflow={'hidden'}
alignItems={'center'}
p={2.5}
bg={'white'}
boxShadow={'0 4px 8px -2px rgba(16,24,40,.1),0 2px 4px -2px rgba(16,24,40,.06)'}
borderRadius={'md'}
border={theme.borders.base}
_hover={{
...hoverDeleteStyles,
borderColor: 'primary.300'
}}
cursor={'pointer'}
onClick={() => {
if (
item.inputs
.filter((input) => !childAppSystemKey.includes(input.key))
.every(
(input) =>
input.toolDescription ||
input.renderTypeList.includes(FlowNodeInputTypeEnum.selectLLMModel) ||
input.renderTypeList.includes(FlowNodeInputTypeEnum.fileSelect)
)
) {
return;
}
setConfigTool(item);
}}
>
<Avatar src={item.avatar} w={'1.5rem'} h={'1.5rem'} borderRadius={'sm'} />
<Box
ml={2}
flex={'1 0 0'}
w={0}
className={'textEllipsis'}
fontSize={'sm'}
color={'myGray.900'}
>
{item.name}
</Box>
<DeleteIcon
onClick={(e) => {
e.stopPropagation();
setAppForm((state: AppSimpleEditFormType) => ({
...state,
selectedTools: state.selectedTools.filter((tool) => tool.id !== item.id)
}));
}}
/>
</Flex>
</MyTooltip>
))}
</Grid>
{isOpenToolsSelect && (
<ToolSelectModal
selectedTools={appForm.selectedTools}
chatConfig={appForm.chatConfig}
selectedModel={selectedModel}
onAddTool={(e) => {
setAppForm((state) => ({
...state,
selectedTools: [...state.selectedTools, e]
}));
}}
onRemoveTool={(e) => {
setAppForm((state) => ({
...state,
selectedTools: state.selectedTools.filter((item) => item.pluginId !== e.id)
}));
}}
onClose={onCloseToolsSelect}
/>
)}
{configTool && (
<ConfigToolModal
configTool={configTool}
onCloseConfigTool={() => setConfigTool(null)}
onAddTool={(e) => {
setAppForm((state) => ({
...state,
selectedTools: state.selectedTools.map((item) =>
item.pluginId === configTool.pluginId ? e : item
)
}));
}}
/>
)}
</>
);
};
export default React.memo(ToolSelect);

View File

@@ -1,54 +1,63 @@
import React, { useCallback, useEffect, useState } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { useTranslation } from 'next-i18next';
import {
Accordion,
AccordionButton,
AccordionIcon,
AccordionItem,
AccordionPanel,
Box,
Button,
css,
Flex,
HStack,
Input,
InputGroup,
InputLeftElement,
ModalBody,
ModalFooter
Grid
} from '@chakra-ui/react';
import FillRowTabs from '@fastgpt/web/components/common/Tabs/FillRowTabs';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
import {
FlowNodeTemplateType,
NodeTemplateListItemType
NodeTemplateListItemType,
NodeTemplateListType
} from '@fastgpt/global/core/workflow/type/node.d';
import Avatar from '@fastgpt/web/components/common/Avatar';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { AddIcon } from '@chakra-ui/icons';
import {
getPluginGroups,
getPreviewPluginNode,
getSystemPlugTemplates,
getSystemPluginPaths
} from '@/web/core/app/api/plugin';
import MyBox from '@fastgpt/web/components/common/MyBox';
import { Controller, useForm } from 'react-hook-form';
import { getTeamPlugTemplates } from '@/web/core/app/api/plugin';
import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
import { getAppFolderPath } from '@/web/core/app/api/app';
import FolderPath from '@/components/common/folder/Path';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import CostTooltip from '@/components/core/app/plugin/CostTooltip';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import RenderPluginInput from '@/components/core/chat/ChatContainer/PluginRunBox/components/renderPluginInput';
import { NodeInputKeyEnum, WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import { useContextSelector } from 'use-context-selector';
import { AppContext } from '../../context';
import SearchInput from '@fastgpt/web/components/common/Input/SearchInput';
import { useMemoizedFn } from 'ahooks';
import MyAvatar from '@fastgpt/web/components/common/Avatar';
import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { AppSimpleEditFormType } from '@fastgpt/global/core/app/type';
import { useToast } from '@fastgpt/web/hooks/useToast';
import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
import { workflowStartNodeId } from '@/web/core/app/constants';
import ConfigToolModal from './ConfigToolModal';
type Props = {
selectedTools: FlowNodeTemplateType[];
chatConfig: AppSimpleEditFormType['chatConfig'];
selectedModel: LLMModelItemType;
onAddTool: (tool: FlowNodeTemplateType) => void;
onRemoveTool: (tool: NodeTemplateListItemType) => void;
};
const childAppSystemKey: string[] = [
export const childAppSystemKey: string[] = [
NodeInputKeyEnum.forbidStream,
NodeInputKeyEnum.history,
NodeInputKeyEnum.historyMaxAmount,
@@ -64,7 +73,7 @@ const ToolSelectModal = ({ onClose, ...props }: Props & { onClose: () => void })
const { t } = useTranslation();
const { appDetail } = useContextSelector(AppContext, (v) => v);
const [templateType, setTemplateType] = useState(TemplateTypeEnum.teamPlugin);
const [templateType, setTemplateType] = useState(TemplateTypeEnum.systemPlugin);
const [parentId, setParentId] = useState<ParentIdType>('');
const [searchKey, setSearchKey] = useState('');
@@ -142,14 +151,14 @@ const ToolSelectModal = ({ onClose, ...props }: Props & { onClose: () => void })
<FillRowTabs
list={[
{
icon: 'core/modules/teamPlugin',
label: t('common:core.app.ToolCall.Team'),
value: TemplateTypeEnum.teamPlugin
icon: 'phoneTabbar/tool',
label: t('common:navbar.Toolkit'),
value: TemplateTypeEnum.systemPlugin
},
{
icon: 'core/modules/systemPlugin',
label: t('common:core.app.ToolCall.System'),
value: TemplateTypeEnum.systemPlugin
icon: 'core/modules/teamPlugin',
label: t('common:core.module.template.Team app'),
value: TemplateTypeEnum.teamPlugin
}
]}
py={'5px'}
@@ -162,35 +171,29 @@ const ToolSelectModal = ({ onClose, ...props }: Props & { onClose: () => void })
})
}
/>
<InputGroup w={300}>
<InputLeftElement h={'full'} alignItems={'center'} display={'flex'}>
<MyIcon name={'common/searchLight'} w={'16px'} color={'myGray.500'} ml={3} />
</InputLeftElement>
<Input
bg={'myGray.50'}
placeholder={t('common:plugin.Search plugin')}
<Box w={300}>
<SearchInput
value={searchKey}
onChange={(e) => setSearchKey(e.target.value)}
placeholder={
templateType === TemplateTypeEnum.systemPlugin
? t('common:plugin.Search plugin')
: t('app:search_app')
}
/>
</InputGroup>
</Box>
</Box>
{/* route components */}
{!searchKey && parentId && (
<Flex mt={2} px={[3, 6]}>
<FolderPath
paths={paths}
FirstPathDom={null}
onClick={() => {
onUpdateParentId(null);
}}
/>
<FolderPath paths={paths} FirstPathDom={null} onClick={() => onUpdateParentId(null)} />
</Flex>
)}
<MyBox isLoading={isLoading} mt={2} px={[3, 6]} pb={3} flex={'1 0 0'} overflowY={'auto'}>
<RenderList
templates={templates}
isLoadingData={isLoading}
type={templateType}
setParentId={onUpdateParentId}
showCost={templateType === TemplateTypeEnum.systemPlugin}
{...props}
/>
</MyBox>
@@ -202,55 +205,116 @@ export default React.memo(ToolSelectModal);
const RenderList = React.memo(function RenderList({
templates,
selectedTools,
isLoadingData,
type,
onAddTool,
onRemoveTool,
setParentId,
showCost
selectedTools,
chatConfig,
selectedModel
}: Props & {
templates: NodeTemplateListItemType[];
isLoadingData: boolean;
type: TemplateTypeEnum;
setParentId: (parentId: ParentIdType) => any;
showCost?: boolean;
}) {
const { t } = useTranslation();
const { feConfigs } = useSystemStore();
const [configTool, setConfigTool] = useState<FlowNodeTemplateType>();
const onCloseConfigTool = useCallback(() => setConfigTool(undefined), []);
const {
handleSubmit,
reset,
control,
formState: { errors }
} = useForm();
useEffect(() => {
if (configTool) {
const defaultValues = configTool.inputs.reduce(
(acc, input) => {
acc[input.key] = input.defaultValue;
return acc;
},
{} as Record<string, any>
);
reset(defaultValues);
}
}, [configTool, reset]);
const { toast } = useToast();
const { runAsync: onClickAdd, loading: isLoading } = useRequest2(
async (template: NodeTemplateListItemType) => {
const res = await getPreviewPluginNode({ appId: template.id });
// All input is tool params
/* Invalid plugin check
1. Reference type. but not tool description;
2. Has dataset select
3. Has dynamic external data
*/
const oneFileInput =
res.inputs.filter((input) =>
input.renderTypeList.includes(FlowNodeInputTypeEnum.fileSelect)
).length === 1;
const canUploadFile =
chatConfig?.fileSelectConfig?.canSelectFile || chatConfig?.fileSelectConfig?.canSelectImg;
const invalidFileInput = oneFileInput && !!canUploadFile;
if (
res.inputs.every((input) => childAppSystemKey.includes(input.key) || input.toolDescription)
res.inputs.some(
(input) =>
(input.renderTypeList.length === 1 &&
input.renderTypeList[0] === FlowNodeInputTypeEnum.reference &&
!input.toolDescription) ||
input.renderTypeList.includes(FlowNodeInputTypeEnum.selectDataset) ||
input.renderTypeList.includes(FlowNodeInputTypeEnum.addInputParam) ||
(input.renderTypeList.includes(FlowNodeInputTypeEnum.fileSelect) && !invalidFileInput)
)
) {
onAddTool(res);
return toast({
title: t('app:simple_tool_tips'),
status: 'warning'
});
}
// 判断是否可以直接添加工具,满足以下任一条件:
// 1. 有工具描述
// 2. 是模型选择类型
// 3. 是文件上传类型且:已开启文件上传、非必填、只有一个文件上传输入
const hasInputForm =
res.inputs.length > 0 &&
res.inputs.some((input) => {
if (input.toolDescription) {
return false;
}
if (input.key === NodeInputKeyEnum.forbidStream) {
return false;
}
if (input.renderTypeList.includes(FlowNodeInputTypeEnum.input)) {
return true;
}
if (input.renderTypeList.includes(FlowNodeInputTypeEnum.textarea)) {
return true;
}
if (input.renderTypeList.includes(FlowNodeInputTypeEnum.numberInput)) {
return true;
}
if (input.renderTypeList.includes(FlowNodeInputTypeEnum.switch)) {
return true;
}
if (input.renderTypeList.includes(FlowNodeInputTypeEnum.select)) {
return true;
}
if (input.renderTypeList.includes(FlowNodeInputTypeEnum.JSONEditor)) {
return true;
}
return false;
});
// 构建默认表单数据
const defaultForm = {
...res,
inputs: res.inputs.map((input) => {
// 如果是模型选择类型,使用当前选中的模型
// if (input.renderTypeList.includes(FlowNodeInputTypeEnum.selectLLMModel)) {
// return {
// ...input,
// value: selectedModel.model
// };
// }
// 如果是文件上传类型,设置为从工作流开始节点获取用户文件
if (input.renderTypeList.includes(FlowNodeInputTypeEnum.fileSelect)) {
return {
...input,
value: [[workflowStartNodeId, NodeOutputKeyEnum.userFiles]]
};
}
return input;
})
};
if (hasInputForm) {
setConfigTool(defaultForm);
} else {
reset();
setConfigTool(res);
onAddTool(defaultForm);
}
},
{
@@ -258,165 +322,229 @@ const RenderList = React.memo(function RenderList({
}
);
return templates.length === 0 && !isLoadingData ? (
<EmptyTip text={t('common:core.app.ToolCall.No plugin')} />
) : (
<MyBox>
{templates.map((item, i) => {
const selected = selectedTools.some((tool) => tool.pluginId === item.id);
const { data: pluginGroups = [] } = useRequest2(getPluginGroups, {
manual: false
});
return (
<MyTooltip
key={item.id}
placement={'bottom'}
shouldWrapChildren={false}
label={
<Box>
<Flex alignItems={'center'}>
<Avatar
src={item.avatar}
w={'1.75rem'}
objectFit={'contain'}
borderRadius={'sm'}
/>
<Box fontWeight={'bold'} ml={2} color={'myGray.900'}>
{t(item.name as any)}
</Box>
</Flex>
<Box mt={2} color={'myGray.500'} maxH={'100px'} overflow={'hidden'}>
{t(item.intro as any) || t('common:core.workflow.Not intro')}
</Box>
{showCost && <CostTooltip cost={item.currentCost} />}
</Box>
const formatTemplatesArray = useMemo(() => {
const data = (() => {
if (type === TemplateTypeEnum.systemPlugin) {
return pluginGroups.map((group) => {
const copy: NodeTemplateListType = group.groupTypes.map((type) => ({
list: [],
type: type.typeId,
label: type.typeName
}));
templates.forEach((item) => {
const index = copy.findIndex((template) => template.type === item.templateType);
if (index === -1) return;
copy[index].list.push(item);
});
return {
label: group.groupName,
list: copy.filter((item) => item.list.length > 0)
};
});
}
return [
{
list: [
{
list: templates,
type: '',
label: ''
}
>
<Flex
alignItems={'center'}
position={'relative'}
p={[4, 5]}
_notLast={{
borderBottomWidth: '1px',
borderBottomColor: 'myGray.150'
}}
_hover={{
bg: 'myGray.50'
}}
],
label: ''
}
];
})();
return data.filter(({ list }) => list.length > 0);
}, [pluginGroups, templates, type]);
const gridStyle = useMemo(() => {
if (type === TemplateTypeEnum.teamPlugin) {
return {
gridTemplateColumns: ['1fr', '1fr'],
py: 2,
avatarSize: '2rem'
};
}
return {
gridTemplateColumns: ['1fr', '1fr 1fr'],
py: 3,
avatarSize: '1.75rem'
};
}, [type]);
const PluginListRender = useMemoizedFn(({ list = [] }: { list: NodeTemplateListType }) => {
return (
<>
{list.map((item, i) => {
return (
<Box
key={item.type}
css={css({
span: {
display: 'block'
}
})}
>
<Avatar src={item.avatar} w={'2rem'} objectFit={'contain'} borderRadius={'md'} />
<Box ml={3} flex={'1 0 0'} color={'myGray.900'}>
{t(item.name as any)}
</Box>
{item.author !== undefined && (
<Box fontSize={'xs'} mr={3}>
{`By ${item.author || feConfigs.systemTitle}`}
<Flex>
<Box fontSize={'sm'} my={2} fontWeight={'500'} flex={1} color={'myGray.900'}>
{t(item.label as any)}
</Box>
)}
{selected ? (
<Button
size={'sm'}
variant={'grayDanger'}
leftIcon={<MyIcon name={'delete'} w={'14px'} />}
onClick={() => onRemoveTool(item)}
>
{t('common:common.Remove')}
</Button>
) : item.isFolder ? (
<Button size={'sm'} variant={'whiteBase'} onClick={() => setParentId(item.id)}>
{t('common:common.Open')}
</Button>
) : (
<Button
size={'sm'}
variant={'whiteBase'}
leftIcon={<AddIcon fontSize={'10px'} />}
isLoading={isLoading}
onClick={() => onClickAdd(item)}
>
{t('common:common.Add')}
</Button>
)}
</Flex>
</MyTooltip>
);
})}
</Flex>
<Grid gridTemplateColumns={gridStyle.gridTemplateColumns} rowGap={2} columnGap={3}>
{item.list.map((template) => {
const selected = selectedTools.some((tool) => tool.pluginId === template.id);
{/* Plugin input config */}
{!!configTool && (
<MyModal
isOpen
isCentered
title={t('common:core.app.ToolCall.Parameter setting')}
iconSrc="core/app/toolCall"
overflow={'auto'}
>
<ModalBody>
<HStack mb={4} spacing={1} fontSize={'sm'}>
<MyIcon name={'common/info'} w={'1.25rem'} />
<Box flex={1}>{t('app:tool_input_param_tip')}</Box>
{configTool.courseUrl && (
<Box
cursor={'pointer'}
color={'primary.500'}
onClick={() => window.open(configTool.courseUrl, '_blank')}
>
{t('app:workflow.Input guide')}
</Box>
)}
</HStack>
{configTool.inputs
.filter((item) => !item.toolDescription && !childAppSystemKey.includes(item.key))
.map((input) => {
return (
<Controller
key={input.key}
control={control}
name={input.key}
rules={{
validate: (value) => {
if (input.valueType === WorkflowIOValueTypeEnum.boolean) {
return value !== undefined;
}
return !!value;
return (
<MyTooltip
key={template.id}
placement={'right'}
label={
<Box py={2}>
<Flex alignItems={'center'}>
<MyAvatar
src={template.avatar}
w={'1.75rem'}
objectFit={'contain'}
borderRadius={'sm'}
/>
<Box fontWeight={'bold'} ml={3} color={'myGray.900'}>
{t(template.name as any)}
</Box>
</Flex>
<Box mt={2} color={'myGray.500'} maxH={'100px'} overflow={'hidden'}>
{t(template.intro as any) || t('common:core.workflow.Not intro')}
</Box>
{type === TemplateTypeEnum.systemPlugin && (
<CostTooltip
cost={template.currentCost}
hasTokenFee={template.hasTokenFee}
/>
)}
</Box>
}
}}
render={({ field: { onChange, value } }) => {
return (
<RenderPluginInput
value={value}
isInvalid={errors && Object.keys(errors).includes(input.key)}
onChange={onChange}
input={input}
setUploading={() => {}}
>
<Flex
alignItems={'center'}
py={gridStyle.py}
px={3}
_hover={{ bg: 'myWhite.600' }}
borderRadius={'sm'}
whiteSpace={'nowrap'}
overflow={'hidden'}
textOverflow={'ellipsis'}
>
<MyAvatar
src={template.avatar}
w={gridStyle.avatarSize}
objectFit={'contain'}
borderRadius={'sm'}
flexShrink={0}
/>
);
}}
/>
);
})}
</ModalBody>
<ModalFooter gap={6}>
<Button onClick={onCloseConfigTool} variant={'whiteBase'}>
{t('common:common.Cancel')}
</Button>
<Button
variant={'primary'}
onClick={handleSubmit((data) => {
onAddTool({
...configTool,
inputs: configTool.inputs.map((input) => ({
...input,
value: data[input.key] ?? input.value
}))
});
onCloseConfigTool();
})}
>
{t('common:common.Confirm')}
</Button>
</ModalFooter>
</MyModal>
<Box
color={'myGray.900'}
fontWeight={'500'}
fontSize={'sm'}
flex={'1 0 0'}
ml={3}
className="textEllipsis"
>
{t(template.name as any)}
</Box>
{selected ? (
<Button
size={'sm'}
variant={'grayDanger'}
leftIcon={<MyIcon name={'delete'} w={'16px'} mr={-1} />}
onClick={() => onRemoveTool(template)}
px={2}
fontSize={'mini'}
>
{t('common:common.Remove')}
</Button>
) : template.isFolder ? (
<Button
size={'sm'}
variant={'whiteBase'}
leftIcon={<MyIcon name={'common/arrowRight'} w={'16px'} mr={-1.5} />}
onClick={() => setParentId(template.id)}
px={2}
fontSize={'mini'}
>
{t('common:common.Open')}
</Button>
) : (
<Button
size={'sm'}
variant={'primaryOutline'}
leftIcon={<MyIcon name={'common/addLight'} w={'16px'} mr={-1.5} />}
isLoading={isLoading}
onClick={() => onClickAdd(template)}
px={2}
fontSize={'mini'}
>
{t('common:common.Add')}
</Button>
)}
</Flex>
</MyTooltip>
);
})}
</Grid>
</Box>
);
})}
</>
);
});
return templates.length === 0 ? (
<EmptyTip text={t('app:module.No Modules')} />
) : (
<Box flex={'1 0 0'} overflow={'overlay'}>
<Accordion defaultIndex={[0]} allowMultiple reduceMotion>
{formatTemplatesArray.length > 1 ? (
<>
{formatTemplatesArray.map(({ list, label }, index) => (
<AccordionItem key={index} border={'none'}>
<AccordionButton
fontSize={'sm'}
fontWeight={'500'}
color={'myGray.900'}
justifyContent={'space-between'}
alignItems={'center'}
borderRadius={'md'}
px={3}
>
{t(label as any)}
<AccordionIcon />
</AccordionButton>
<AccordionPanel py={0}>
<PluginListRender list={list} />
</AccordionPanel>
</AccordionItem>
))}
</>
) : (
<PluginListRender list={formatTemplatesArray?.[0]?.list} />
)}
</Accordion>
{!!configTool && (
<ConfigToolModal
configTool={configTool}
onCloseConfigTool={onCloseConfigTool}
onAddTool={onAddTool}
/>
)}
</MyBox>
</Box>
);
});

View File

@@ -50,6 +50,7 @@ import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/consta
import { getEditorVariables } from '../../../utils';
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
import { WorkflowNodeEdgeContext } from '../../../context/workflowInitContext';
import { useSystemStore } from '@/web/common/system/useSystemStore';
const CurlImportModal = dynamic(() => import('./CurlImportModal'));
const defaultFormBody = {
@@ -87,6 +88,7 @@ const RenderHttpMethodAndUrl = React.memo(function RenderHttpMethodAndUrl({
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
const { appDetail } = useContextSelector(AppContext, (v) => v);
const { feConfigs } = useSystemStore();
const { isOpen: isOpenCurl, onOpen: onOpenCurl, onClose: onCloseCurl } = useDisclosure();
const requestMethods = inputs.find(
@@ -168,6 +170,15 @@ const RenderHttpMethodAndUrl = React.memo(function RenderHttpMethodAndUrl({
});
}, [nodeId, nodeList, edges, appDetail, t]);
const externalProviderWorkflowVariables = useMemo(() => {
return (
feConfigs?.externalProviderWorkflowVariables?.map((item) => ({
key: item.key,
label: item.name
})) || []
);
}, [feConfigs?.externalProviderWorkflowVariables]);
return (
<Box>
<Box mb={2} display={'flex'} justifyContent={'space-between'}>
@@ -235,7 +246,7 @@ const RenderHttpMethodAndUrl = React.memo(function RenderHttpMethodAndUrl({
}
value={requestUrl?.value || ''}
variableLabels={variables}
variables={variables}
variables={externalProviderWorkflowVariables}
onBlur={onBlurUrl}
onChange={onChangeUrl}
minH={40}
@@ -263,6 +274,7 @@ export function RenderHttpProps({
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
const { appDetail } = useContextSelector(AppContext, (v) => v);
const { feConfigs } = useSystemStore();
const requestMethods = inputs.find((item) => item.key === NodeInputKeyEnum.httpMethod)?.value;
const params = inputs.find((item) => item.key === NodeInputKeyEnum.httpParams);
@@ -276,6 +288,15 @@ export function RenderHttpProps({
const headersLength = headers?.value?.length || 0;
// get variable
const externalProviderWorkflowVariables = useMemo(() => {
return (
feConfigs?.externalProviderWorkflowVariables?.map((item) => ({
key: item.key,
label: item.name
})) || []
);
}, [feConfigs?.externalProviderWorkflowVariables]);
const variables = useCreation(() => {
return getEditorVariables({
nodeId,
@@ -298,13 +319,15 @@ export function RenderHttpProps({
params,
headers,
jsonBody,
variables
variables,
externalProviderWorkflowVariables
}),
[headers, jsonBody, params, variables]
[externalProviderWorkflowVariables, headers, jsonBody, params, variables]
);
const Render = useMemo(() => {
const { params, headers, jsonBody, variables } = JSON.parse(stringifyVariables);
const { params, headers, jsonBody, variables, externalProviderWorkflowVariables } =
JSON.parse(stringifyVariables);
return (
<Box>
<Flex alignItems={'center'} mb={2} fontWeight={'medium'} color={'myGray.600'}>
@@ -347,18 +370,31 @@ export function RenderHttpProps({
headers &&
jsonBody &&
{
[TabEnum.params]: <RenderForm nodeId={nodeId} input={params} variables={variables} />,
[TabEnum.params]: (
<RenderForm
nodeId={nodeId}
input={params}
variables={variables}
externalProviderWorkflowVariables={externalProviderWorkflowVariables}
/>
),
[TabEnum.body]: (
<RenderBody
nodeId={nodeId}
variables={variables}
externalProviderWorkflowVariables={externalProviderWorkflowVariables}
jsonBody={jsonBody}
formBody={formBody}
typeInput={contentType}
/>
),
[TabEnum.headers]: (
<RenderForm nodeId={nodeId} input={headers} variables={variables} />
<RenderForm
nodeId={nodeId}
input={headers}
variables={variables}
externalProviderWorkflowVariables={externalProviderWorkflowVariables}
/>
)
}[selectedTab]}
</Box>
@@ -436,11 +472,16 @@ const RenderHttpTimeout = ({
const RenderForm = ({
nodeId,
input,
variables
variables,
externalProviderWorkflowVariables
}: {
nodeId: string;
input: FlowNodeInputItemType;
variables: EditorVariableLabelPickerType[];
externalProviderWorkflowVariables: {
key: string;
label: string;
}[];
}) => {
const { t } = useTranslation();
const { toast } = useToast();
@@ -547,7 +588,7 @@ const RenderForm = ({
placeholder={t('common:textarea_variable_picker_tip')}
value={item.key}
variableLabels={variables}
variables={variables}
variables={externalProviderWorkflowVariables}
onBlur={(val) => {
handleKeyChange(index, val);
@@ -565,7 +606,7 @@ const RenderForm = ({
<HttpInput
placeholder={t('common:textarea_variable_picker_tip')}
value={item.value}
variables={variables}
variables={externalProviderWorkflowVariables}
variableLabels={variables}
onBlur={(val) => {
setList((prevList) =>
@@ -597,7 +638,16 @@ const RenderForm = ({
</TableContainer>
</Box>
);
}, [handleAddNewProps, handleKeyChange, input.key, list, t, updateTrigger, variables]);
}, [
externalProviderWorkflowVariables,
handleAddNewProps,
handleKeyChange,
input.key,
list,
t,
updateTrigger,
variables
]);
return Render;
};
@@ -606,13 +656,18 @@ const RenderBody = ({
jsonBody,
formBody,
typeInput,
variables
variables,
externalProviderWorkflowVariables
}: {
nodeId: string;
jsonBody: FlowNodeInputItemType;
formBody: FlowNodeInputItemType;
typeInput: FlowNodeInputItemType | undefined;
variables: EditorVariableLabelPickerType[];
externalProviderWorkflowVariables: {
key: string;
label: string;
}[];
}) => {
const { t } = useTranslation();
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
@@ -684,7 +739,12 @@ const RenderBody = ({
</Flex>
{(typeInput?.value === ContentTypes.formData ||
typeInput?.value === ContentTypes.xWwwFormUrlencoded) && (
<RenderForm nodeId={nodeId} input={formBody} variables={variables} />
<RenderForm
nodeId={nodeId}
input={formBody}
variables={variables}
externalProviderWorkflowVariables={externalProviderWorkflowVariables}
/>
)}
{typeInput?.value === ContentTypes.json && (
<PromptEditor
@@ -729,7 +789,16 @@ const RenderBody = ({
)}
</Box>
);
}, [typeInput?.value, t, nodeId, formBody, variables, jsonBody, onChangeNode]);
}, [
typeInput?.value,
nodeId,
formBody,
variables,
externalProviderWorkflowVariables,
jsonBody,
t,
onChangeNode
]);
return Render;
};

View File

@@ -54,8 +54,8 @@ const NodeLaf = (props: NodeProps<FlowNodeItemType>) => {
const { userInfo, initUserInfo } = useUserStore();
const token = userInfo?.team.lafAccount?.token;
const appid = userInfo?.team.lafAccount?.appid;
const token = userInfo?.team?.lafAccount?.token;
const appid = userInfo?.team?.lafAccount?.appid;
const {
data: lafData,
@@ -322,7 +322,7 @@ const ConfigLaf = () => {
</Button>
{isOpenLafConfig && feConfigs?.lafEnv && (
<LafAccountModal defaultData={userInfo?.team.lafAccount} onClose={onCloseLafConfig} />
<LafAccountModal defaultData={userInfo?.team?.lafAccount} onClose={onCloseLafConfig} />
)}
</Center>
) : (

View File

@@ -190,6 +190,19 @@ const FieldEditModal = ({
}
}
if (data.renderTypeList[0] === FlowNodeInputTypeEnum.addInputParam) {
if (
!data.customInputConfig?.selectValueTypeList ||
!data.customInputConfig?.selectValueTypeList.length
) {
toast({
status: 'warning',
title: t('common:core.module.edit.Field Value Type Cannot Be Empty')
});
return;
}
}
// Get toolDescription and removes the types of some unusable tools
if (data.toolDescription && data.renderTypeList.includes(FlowNodeInputTypeEnum.reference)) {
data.toolDescription = data.description;

View File

@@ -351,12 +351,6 @@ const InputTypeConfig = ({
{inputType === FlowNodeInputTypeEnum.addInputParam && (
<>
{/* <Flex alignItems={'center'}>
<FormLabel flex={'0 0 132px'} fontWeight={'medium'}>
{t('common:core.module.Input Type')}
</FormLabel>
<Box fontSize={'14px'}>{t('workflow:only_the_reference_type_is_supported')}</Box>
</Flex> */}
<Box>
<HStack mb={1}>
<FormLabel fontWeight={'medium'}>{t('workflow:optional_value_type')}</FormLabel>

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import React, { useCallback, useMemo, useRef } from 'react';
import NodeCard from './render/NodeCard';
import { NodeProps } from 'reactflow';
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node';
@@ -35,6 +35,8 @@ import { useCreation, useMemoizedFn } from 'ahooks';
import { getEditorVariables } from '../../utils';
import { isArray } from 'lodash';
import { WorkflowNodeEdgeContext } from '../../context/workflowInitContext';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import MyNumberInput from '@fastgpt/web/components/common/Input/NumberInput';
const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
const { inputs = [], nodeId } = data;
@@ -67,7 +69,17 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
t
});
}, [nodeId, nodeList, edges, appDetail, t]);
const { feConfigs } = useSystemStore();
const externalProviderWorkflowVariables = useMemo(() => {
return (
feConfigs?.externalProviderWorkflowVariables?.map((item) => ({
key: item.key,
label: item.name
})) || []
);
}, [feConfigs?.externalProviderWorkflowVariables]);
// Node inputs
const updateList = useMemo(
() =>
(inputs.find((input) => input.key === NodeInputKeyEnum.updateList)
@@ -104,7 +116,7 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
(item) => item.renderType === updateItem.renderType
);
const handleUpdate = (newValue?: ReferenceValueType | string) => {
const onUpdateNewValue = (newValue?: ReferenceValueType | string) => {
if (typeof newValue === 'string') {
onUpdateList(
updateList.map((update, i) =>
@@ -134,7 +146,11 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
return {
...update,
value: ['', ''],
valueType,
valueType: getRefData({
variable: value as ReferenceItemValueType,
nodeList,
chatConfig: appDetail.chatConfig
}).valueType,
variable: value as ReferenceItemValueType
};
}
@@ -206,7 +222,7 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
nodeId={nodeId}
variable={updateItem.value}
valueType={valueType}
onSelect={handleUpdate}
onSelect={onUpdateNewValue}
/>
);
}
@@ -218,9 +234,10 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
<Box w={'300px'}>
<PromptEditor
value={inputValue || ''}
onChange={handleUpdate}
onChange={onUpdateNewValue}
showOpenModal={false}
variableLabels={variables}
variables={[...variables, ...externalProviderWorkflowVariables]}
minH={100}
/>
</Box>
@@ -228,20 +245,18 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
}
if (valueType === WorkflowIOValueTypeEnum.number) {
return (
<NumberInput value={Number(inputValue) || 0}>
<NumberInputField bg="white" onChange={(e) => handleUpdate(e.target.value)} />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
<MyNumberInput
bg={'white'}
value={Number(inputValue) || 0}
onChange={(e) => onUpdateNewValue(String(e || 0))}
/>
);
}
if (valueType === WorkflowIOValueTypeEnum.boolean) {
return (
<Switch
defaultChecked={inputValue === 'true'}
onChange={(e) => handleUpdate(String(e.target.checked))}
onChange={(e) => onUpdateNewValue(String(e.target.checked))}
/>
);
}
@@ -250,9 +265,10 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
<Box w={'300px'}>
<PromptEditor
value={inputValue || ''}
onChange={handleUpdate}
onChange={onUpdateNewValue}
showOpenModal={false}
variableLabels={variables}
variables={[...variables, ...externalProviderWorkflowVariables]}
minH={100}
/>
</Box>

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useMemo } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import { Box, Button, Card, Flex, FlexProps } from '@chakra-ui/react';
import MyIcon from '@fastgpt/web/components/common/Icon';
import Avatar from '@fastgpt/web/components/common/Avatar';
@@ -24,11 +24,11 @@ import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { useWorkflowUtils } from '../../hooks/useUtils';
import { WholeResponseContent } from '@/components/core/chat/components/WholeResponseModal';
import { getDocPath } from '@/web/common/system/doc';
import { WorkflowNodeEdgeContext } from '../../../context/workflowInitContext';
import { WorkflowEventContext } from '../../../context/workflowEventContext';
import MyImage from '@fastgpt/web/components/common/Image/MyImage';
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
import UseGuideModal from '@/components/common/Modal/UseGuideModal';
type Props = FlowNodeItemType & {
children?: React.ReactNode | React.ReactNode[] | string;
@@ -136,6 +136,7 @@ const NodeCard = (props: Props) => {
} = useConfirm({
content: t('workflow:Confirm_sync_node')
});
const hasNewVersion = nodeTemplate && nodeTemplate.version !== node?.version;
const { runAsync: onClickSyncVersion } = useRequest2(
@@ -166,7 +167,7 @@ const NodeCard = (props: Props) => {
<ToolTargetHandle show={showToolHandle} nodeId={nodeId} />
{/* avatar and name */}
<Flex alignItems={'center'} mb={intro ? 1 : 0}>
<Flex alignItems={'center'} mb={1}>
{node?.flowNodeType !== FlowNodeTypeEnum.stopTool && (
<Flex
alignItems={'center'}
@@ -271,15 +272,19 @@ const NodeCard = (props: Props) => {
{!!nodeTemplate?.diagram && node?.courseUrl && (
<Box bg={'myGray.300'} w={'1px'} h={'12px'} ml={1} mr={0.5} />
)}
{node?.courseUrl && !hasNewVersion && (
<MyTooltip label={t('workflow:Node.Open_Node_Course')}>
<MyIconButton
ml={1}
icon="book"
color={'primary.600'}
onClick={() => window.open(getDocPath(node.courseUrl || ''), '_blank')}
/>
</MyTooltip>
{!!(node?.courseUrl || nodeTemplate?.userGuide) && !hasNewVersion && (
<UseGuideModal
title={nodeTemplate?.name}
iconSrc={nodeTemplate?.avatar}
text={nodeTemplate?.userGuide}
link={nodeTemplate?.courseUrl}
>
{({ onClick }) => (
<MyTooltip label={t('workflow:Node.Open_Node_Course')}>
<MyIconButton ml={1} icon="book" color={'primary.600'} onClick={onClick} />
</MyTooltip>
)}
</UseGuideModal>
)}
</Flex>
<NodeIntro nodeId={nodeId} intro={intro} />
@@ -291,6 +296,7 @@ const NodeCard = (props: Props) => {
);
}, [
node?.flowNodeType,
node?.courseUrl,
showToolHandle,
nodeId,
isFolded,
@@ -301,7 +307,10 @@ const NodeCard = (props: Props) => {
onOpenConfirmSync,
onClickSyncVersion,
nodeTemplate?.diagram,
node?.courseUrl,
nodeTemplate?.userGuide,
nodeTemplate?.name,
nodeTemplate?.avatar,
nodeTemplate?.courseUrl,
intro,
menuForbid,
nodeList,

View File

@@ -8,8 +8,9 @@ import { useCreation } from 'ahooks';
import { AppContext } from '@/pages/app/detail/components/context';
import { getEditorVariables } from '../../../../../utils';
import { WorkflowNodeEdgeContext } from '../../../../../context/workflowInitContext';
import { useSystemStore } from '@/web/common/system/useSystemStore';
const TextareaRender = ({ inputs = [], item, nodeId }: RenderInputProps) => {
const TextareaRender = ({ item, nodeId }: RenderInputProps) => {
const { t } = useTranslation();
const edges = useContextSelector(WorkflowNodeEdgeContext, (v) => v.edges);
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
@@ -17,6 +18,8 @@ const TextareaRender = ({ inputs = [], item, nodeId }: RenderInputProps) => {
const { appDetail } = useContextSelector(AppContext, (v) => v);
const { feConfigs } = useSystemStore();
// get variable
const variables = useCreation(() => {
return getEditorVariables({
@@ -28,6 +31,15 @@ const TextareaRender = ({ inputs = [], item, nodeId }: RenderInputProps) => {
});
}, [nodeId, nodeList, edges, appDetail, t]);
const externalProviderWorkflowVariables = useMemo(() => {
return (
feConfigs?.externalProviderWorkflowVariables?.map((item) => ({
key: item.key,
label: item.name
})) || []
);
}, [feConfigs?.externalProviderWorkflowVariables]);
const onChange = useCallback(
(e: string) => {
onChangeNode({
@@ -47,7 +59,7 @@ const TextareaRender = ({ inputs = [], item, nodeId }: RenderInputProps) => {
return (
<PromptEditor
variableLabels={variables}
variables={variables}
variables={[...variables, ...externalProviderWorkflowVariables]}
title={t(item.label as any)}
maxLength={item.maxLength}
minH={100}
@@ -57,7 +69,16 @@ const TextareaRender = ({ inputs = [], item, nodeId }: RenderInputProps) => {
onChange={onChange}
/>
);
}, [item.label, item.maxLength, item.placeholder, item.value, onChange, t, variables]);
}, [
externalProviderWorkflowVariables,
item.label,
item.maxLength,
item.placeholder,
item.value,
onChange,
t,
variables
]);
return Render;
};

View File

@@ -1,5 +1,5 @@
import { useDebounceEffect, useMemoizedFn } from 'ahooks';
import React, { ReactNode, useMemo, useRef, useState } from 'react';
import { useDebounceEffect, useLockFn, useMemoizedFn } from 'ahooks';
import React, { ReactNode, useEffect, useMemo, useRef, useState } from 'react';
import { createContext, useContextSelector } from 'use-context-selector';
import { WorkflowInitContext, WorkflowNodeEdgeContext } from './workflowInitContext';
import { WorkflowContext } from '.';
@@ -14,6 +14,7 @@ import {
Input_Template_Node_Height,
Input_Template_Node_Width
} from '@fastgpt/global/core/workflow/template/input';
import { isProduction } from '@fastgpt/global/common/system/constants';
type WorkflowStatusContextType = {
isSaved: boolean;
@@ -67,20 +68,28 @@ const WorkflowStatusContextProvider = ({ children }: { children: ReactNode }) =>
// Lead check before unload
const flowData2StoreData = useContextSelector(WorkflowContext, (v) => v.flowData2StoreData);
const onSaveApp = useContextSelector(AppContext, (v) => v.onSaveApp);
const autoSaveFn = useLockFn(async () => {
if (isSaved || !leaveSaveSign.current) return;
console.log('Leave auto save');
const data = flowData2StoreData();
if (!data || data.nodes.length === 0) return;
await onSaveApp({
...data,
isPublish: false,
chatConfig: appDetail.chatConfig,
autoSave: true
});
});
useEffect(() => {
return () => {
if (isProduction) {
autoSaveFn();
}
};
}, []);
useBeforeunload({
tip: t('common:core.common.tip.leave page'),
callback: async () => {
if (isSaved || !leaveSaveSign.current) return;
console.log('Leave auto save');
const data = flowData2StoreData();
if (!data || data.nodes.length === 0) return;
await onSaveApp({
...data,
isPublish: false,
versionName: t('app:unusual_leave_auto_save'),
chatConfig: appDetail.chatConfig
});
}
callback: autoSaveFn
});
const onNodesChange = useContextSelector(WorkflowNodeEdgeContext, (state) => state.onNodesChange);

View File

@@ -13,6 +13,7 @@ import { useDisclosure } from '@chakra-ui/react';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import type { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
import type { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
import { AppErrEnum } from '@fastgpt/global/common/error/code/app';
const InfoModal = dynamic(() => import('./InfoModal'));
const TagsEditModal = dynamic(() => import('./TagsEditModal'));
@@ -150,13 +151,20 @@ const AppContextProvider = ({ children }: { children: ReactNode }) => {
});
const { runAsync: onSaveApp } = useRequest2(async (data: PostPublishAppProps) => {
await postPublishApp(appId, data);
setAppDetail((state) => ({
...state,
...data,
modules: data.nodes || state.modules
}));
reloadAppLatestVersion();
try {
await postPublishApp(appId, data);
setAppDetail((state) => ({
...state,
...data,
modules: data.nodes || state.modules
}));
reloadAppLatestVersion();
} catch (error: any) {
if (error.statusText == AppErrEnum.unExist) {
return;
}
return Promise.reject(error);
}
});
const { openConfirm: openConfirmDel, ConfirmModal: ConfirmDelModal } = useConfirm({

View File

@@ -1,5 +1,5 @@
import React, { useCallback, useMemo, useRef } from 'react';
import { Box, Flex, Button, ModalFooter, ModalBody, Input, Grid, Card } from '@chakra-ui/react';
import { Box, Flex, Button, ModalBody, Input, Grid, Card } from '@chakra-ui/react';
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
import { useForm } from 'react-hook-form';
import { compressImgFileAndUpload } from '@/web/common/file/controller';
@@ -25,6 +25,7 @@ import {
getTemplateMarketItemList
} from '@/web/core/app/api/template';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { AppTemplateSchemaType } from '@fastgpt/global/core/app/type';
type FormType = {
avatar: string;
@@ -70,13 +71,12 @@ const CreateModal = ({
}
});
const typeData = typeMap.current[type];
const { data: templateList = [] } = useRequest2(getTemplateMarketItemList, {
manual: false
});
const filterTemplates = useMemo(() => {
return templateList.filter((item) => item.type === type).slice(0, 3);
}, [templateList, type]);
const { data: templateList = [], loading: isRequestTemplates } = useRequest2(
() => getTemplateMarketItemList({ isQuickTemplate: true, type }),
{
manual: false
}
);
const { register, setValue, watch, handleSubmit } = useForm<FormType>({
defaultValues: {
@@ -127,13 +127,12 @@ const CreateModal = ({
});
}
const templateDetail = await getTemplateMarketItemDetail({ templateId: templateId });
const templateDetail = await getTemplateMarketItemDetail(templateId);
return postCreateApp({
parentId,
avatar: data.avatar || templateDetail.avatar,
name: data.name,
type: templateDetail.type,
type: templateDetail.type as AppTypeEnum,
modules: templateDetail.workflow.nodes || [],
edges: templateDetail.workflow.edges || [],
chatConfig: templateDetail.workflow.chatConfig
@@ -158,7 +157,7 @@ const CreateModal = ({
onClose={onClose}
isCentered={!isPc}
maxW={['90vw', '40rem']}
isLoading={isCreating}
isLoading={isCreating || isRequestTemplates}
>
<ModalBody px={9} pb={8}>
<Box color={'myGray.800'} fontWeight={'bold'}>
@@ -205,7 +204,7 @@ const CreateModal = ({
</Flex>
<Grid
userSelect={'none'}
gridTemplateColumns={['repeat(1,1fr)', 'repeat(2,1fr)']}
gridTemplateColumns={templateList.length > 0 ? ['repeat(1,1fr)', 'repeat(2,1fr)'] : '1fr'}
gridGap={[2, 4]}
>
<Card
@@ -231,9 +230,9 @@ const CreateModal = ({
{typeData.emptyCreateText}
</Box>
</Card>
{filterTemplates.map((item) => (
{templateList.map((item) => (
<Card
key={item.id}
key={item.templateId}
p={4}
borderRadius={'md'}
borderWidth={'1px'}
@@ -271,17 +270,17 @@ const CreateModal = ({
h={'full'}
left={0}
right={0}
bottom={0}
bottom={1}
height={'40px'}
bg={'white'}
zIndex={1}
>
<Button
variant={'whiteBase'}
h={'1.75rem'}
borderRadius={'xl'}
h={6}
borderRadius={'sm'}
w={'40%'}
onClick={handleSubmit((data) => onclickCreate(data, item.id))}
onClick={handleSubmit((data) => onclickCreate(data, item.templateId))}
>
{t('app:templateMarket.Use')}
</Button>

View File

@@ -12,16 +12,16 @@ import {
ModalOverlay
} from '@chakra-ui/react';
import Avatar from '@fastgpt/web/components/common/Avatar';
import { useCallback, useState } from 'react';
import { useCallback, useMemo, useState } from 'react';
import MyBox from '@fastgpt/web/components/common/MyBox';
import AppTypeTag from './TypeTag';
import { AppTemplateTypeEnum, AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import {
getTemplateMarketItemDetail,
getTemplateMarketItemList
getTemplateMarketItemList,
getTemplateTagList
} from '@/web/core/app/api/template';
import { TemplateMarketListItemType } from '@fastgpt/global/core/workflow/type';
import { postCreateApp } from '@/web/core/app/api';
import { useContextSelector } from 'use-context-selector';
import { AppListContext } from './context';
@@ -34,9 +34,18 @@ import SearchInput from '@fastgpt/web/components/common/Input/SearchInput/index'
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { webPushTrack } from '@/web/common/middle/tracks/utils';
import { AppTemplateSchemaType, TemplateTypeSchemaType } from '@fastgpt/global/core/app/type';
import { i18nT } from '@fastgpt/web/i18n/utils';
import UseGuideModal from '@/components/common/Modal/UseGuideModal';
type TemplateAppType = AppTypeEnum | 'all';
const recommendTag: TemplateTypeSchemaType = {
typeId: AppTemplateTypeEnum.recommendation,
typeName: i18nT('app:templateMarket.templateTags.Recommendation'),
typeOrder: 0
};
const TemplateMarketModal = ({
defaultType = 'all',
onClose
@@ -46,63 +55,57 @@ const TemplateMarketModal = ({
}) => {
const { t } = useTranslation();
const { feConfigs } = useSystemStore();
const templateTags = [
{
id: AppTemplateTypeEnum.recommendation,
label: t('app:templateMarket.templateTags.Recommendation')
},
{
id: AppTemplateTypeEnum.writing,
label: t('app:templateMarket.templateTags.Writing')
},
{
id: AppTemplateTypeEnum.imageGeneration,
label: t('app:templateMarket.templateTags.Image_generation')
},
{
id: AppTemplateTypeEnum.webSearch,
label: t('app:templateMarket.templateTags.Web_search')
},
{
id: AppTemplateTypeEnum.roleplay,
label: t('app:templateMarket.templateTags.Roleplay')
},
{
id: AppTemplateTypeEnum.officeServices,
label: t('app:templateMarket.templateTags.Office_services')
}
];
const { parentId } = useContextSelector(AppListContext, (v) => v);
const router = useRouter();
const { isPc } = useSystem();
const [currentTag, setCurrentTag] = useState(templateTags[0].id);
const [currentTag, setCurrentTag] = useState<string>(AppTemplateTypeEnum.recommendation);
const [currentAppType, setCurrentAppType] = useState<TemplateAppType>(defaultType);
const [currentSearch, setCurrentSearch] = useState('');
const { data: templateList = [], loading: isLoadingTemplates } = useRequest2(
getTemplateMarketItemList,
() => getTemplateMarketItemList({ type: currentAppType }),
{
manual: false,
refreshDeps: [currentAppType]
}
);
const { data: templateTags = [], loading: isLoadingTags } = useRequest2(
() => getTemplateTagList().then((res) => [recommendTag, ...res]),
{
manual: false
}
);
// Batch by tags
const filterTemplateTags = useMemo(() => {
return templateTags
.map((tag) => {
const templates = templateList.filter((template) => template.tags.includes(tag.typeId));
return {
...tag,
templates
};
})
.filter((item) => item.templates.length > 0);
}, [templateList, templateTags]);
const { runAsync: onUseTemplate, loading: isCreating } = useRequest2(
async (id: string) => {
const templateDetail = await getTemplateMarketItemDetail({ templateId: id });
async (template: AppTemplateSchemaType) => {
const templateDetail = await getTemplateMarketItemDetail(template.templateId);
return postCreateApp({
parentId,
avatar: templateDetail.avatar,
name: templateDetail.name,
type: templateDetail.type,
avatar: template.avatar,
name: template.name,
type: template.type as AppTypeEnum,
modules: templateDetail.workflow.nodes || [],
edges: templateDetail.workflow.edges || [],
chatConfig: templateDetail.workflow.chatConfig
}).then((res) => {
webPushTrack.useAppTemplate({
id,
name: templateDetail.name
id: res,
name: template.name
});
return res;
@@ -122,9 +125,9 @@ const TemplateMarketModal = ({
async () => {
let firstVisibleTitle: any = null;
templateTags
.map((type) => type.id)
.forEach((type: string) => {
filterTemplateTags
.map((type) => type.typeId)
.forEach((type) => {
const element = document.getElementById(type);
if (!element) return;
@@ -144,17 +147,18 @@ const TemplateMarketModal = ({
}
},
{
throttleWait: 100
throttleWait: 100,
refreshDeps: [filterTemplateTags.length]
}
);
const TemplateCard = useCallback(
({ item }: { item: TemplateMarketListItemType }) => {
({ item }: { item: AppTemplateSchemaType }) => {
const { t } = useTranslation();
return (
<MyBox
key={item.id}
key={item.templateId}
lineHeight={1.5}
h="100%"
pt={4}
@@ -181,7 +185,7 @@ const TemplateMarketModal = ({
{item.name}
</Box>
<Box mr={'-1rem'}>
<AppTypeTag type={item.type} />
<AppTypeTag type={item.type as AppTypeEnum} />
</Box>
</HStack>
<Box
@@ -198,7 +202,7 @@ const TemplateMarketModal = ({
<Box w={'full'} fontSize={'mini'}>
<Box color={'myGray.500'}>{`by ${item.author || feConfigs.systemTitle}`}</Box>
<Box
<Flex
className="buttons"
display={'none'}
justifyContent={'center'}
@@ -209,32 +213,49 @@ const TemplateMarketModal = ({
h={'full'}
left={0}
right={0}
bottom={0}
bottom={1}
height={'40px'}
bg={'white'}
zIndex={1}
gap={2}
>
{((item.userGuide?.type === 'markdown' && item.userGuide?.content) ||
(item.userGuide?.type === 'link' && item.userGuide?.link)) && (
<UseGuideModal
title={item.name}
iconSrc={item.avatar}
text={item.userGuide?.content}
link={item.userGuide?.link}
>
{({ onClick }) => (
<Button variant={'whiteBase'} h={6} rounded={'sm'} onClick={onClick}>
{t('app:templateMarket.template_guide')}
</Button>
)}
</UseGuideModal>
)}
<Button
variant={'whiteBase'}
h={'1.75rem'}
borderRadius={'xl'}
w={'40%'}
onClick={() => onUseTemplate(item.id)}
h={6}
rounded={'sm'}
onClick={() => onUseTemplate(item)}
>
{t('app:templateMarket.Use')}
</Button>
</Box>
</Flex>
</Box>
</MyBox>
);
},
[onUseTemplate]
[feConfigs.systemTitle, onUseTemplate]
);
const isLoading = isLoadingTags || isLoadingTemplates || isCreating;
return (
<Modal
isOpen={true}
onClose={() => onClose && onClose()}
onClose={onClose}
autoFocus={false}
blockScrollOnMount={false}
closeOnOverlayClick={false}
@@ -263,11 +284,11 @@ const TemplateMarketModal = ({
<Box flex={'1'} />
<MySelect
<MySelect<TemplateAppType>
h={'8'}
value={currentAppType}
onchange={(value) => {
setCurrentAppType(value as AppTypeEnum | 'all');
setCurrentAppType(value);
}}
bg={'myGray.100'}
minW={'7rem'}
@@ -302,25 +323,24 @@ const TemplateMarketModal = ({
</Box>
)}
</ModalHeader>
<MyBox isLoading={isCreating || isLoadingTemplates} flex={'1 0 0'} overflow={'overlay'}>
<MyBox isLoading={isLoading} flex={'1 0 0'} h="0">
<ModalBody
h={'100%'}
display={'flex'}
bg={'myGray.100'}
overflow={'auto'}
gap={5}
onScroll={handleScroll}
px={0}
pt={5}
>
{isPc && (
<Flex pl={5} flexDirection={'column'} gap={3}>
{templateTags.map((item) => {
{filterTemplateTags.map((item) => {
return (
<Box
key={item.id}
key={item.typeId}
cursor={'pointer'}
{...(item.id === currentTag && !currentSearch
{...(item.typeId === currentTag && !currentSearch
? {
bg: 'primary.1',
color: 'primary.600'
@@ -336,14 +356,14 @@ const TemplateMarketModal = ({
fontSize={'sm'}
fontWeight={500}
onClick={() => {
setCurrentTag(item.id);
const anchor = document.getElementById(item.id);
setCurrentTag(item.typeId);
const anchor = document.getElementById(item.typeId);
if (anchor) {
anchor.scrollIntoView({ behavior: 'auto', block: 'start' });
}
}}
>
{item.label}
{t(item.typeName as any)}
</Box>
);
})}
@@ -370,7 +390,15 @@ const TemplateMarketModal = ({
</Flex>
)}
<Box pl={[3, 0]} pr={[3, 5]} pt={1} flex={'1'} h={'100%'} overflow={'auto'}>
<Box
pl={[3, 0]}
pr={[3, 5]}
pt={1}
flex={'1'}
h={'100%'}
overflow={'auto'}
onScroll={handleScroll}
>
{currentSearch ? (
<>
<Box fontSize={'lg'} color={'myGray.900'} mb={4}>
@@ -396,7 +424,7 @@ const TemplateMarketModal = ({
pb={5}
>
{templates.map((item) => (
<TemplateCard key={item.id} item={item} />
<TemplateCard key={item.templateId} item={item} />
))}
</Grid>
);
@@ -407,26 +435,17 @@ const TemplateMarketModal = ({
</>
) : (
<>
{templateTags.map((item) => {
const currentTemplates = templateList
?.filter((template) => template.tags.includes(item.id))
.filter((template) => {
if (currentAppType === 'all') return true;
return template.type === currentAppType;
});
if (currentTemplates.length === 0) return null;
{filterTemplateTags.map((item) => {
return (
<Box key={item.id}>
<Box key={item.typeId}>
<Box
id={item.id}
id={item.typeId}
fontSize={['md', 'lg']}
color={'myGray.900'}
mb={4}
fontWeight={500}
>
{item.label}
{t(item.typeName as any)}
</Box>
<Grid
gridTemplateColumns={[
@@ -440,8 +459,8 @@ const TemplateMarketModal = ({
alignItems={'stretch'}
pb={5}
>
{currentTemplates.map((item) => (
<TemplateCard key={item.id} item={item} />
{item.templates.map((item) => (
<TemplateCard key={item.templateId} item={item} />
))}
</Grid>
</Box>

View File

@@ -16,7 +16,6 @@ import { useTranslation } from 'next-i18next';
import { getInitOutLinkChatInfo } from '@/web/core/chat/api';
import { getChatTitleFromChatMessage } from '@fastgpt/global/core/chat/utils';
import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
import { OutLinkWithAppType } from '@fastgpt/global/support/outLink/type';
import { addLog } from '@fastgpt/service/common/system/log';
import { connectToDatabase } from '@/service/mongo';
import NextHead from '@/components/common/NextHead';
@@ -37,6 +36,7 @@ import ChatRecordContextProvider, {
import { useChatStore } from '@/web/core/chat/context/useChatStore';
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
import { useI18nLng } from '@fastgpt/web/hooks/useI18n';
import { AppSchema } from '@fastgpt/global/core/app/type';
const CustomPluginRunBox = dynamic(() => import('./components/CustomPluginRunBox'));
@@ -361,15 +361,14 @@ export async function getServerSideProps(context: any) {
const app = await (async () => {
try {
await connectToDatabase();
const app = (await MongoOutLink.findOne(
return MongoOutLink.findOne(
{
shareId
},
'appId showRawSource showNodeStatus'
)
.populate('appId', 'name avatar intro')
.lean()) as OutLinkWithAppType;
return app;
.populate<{ associatedApp: AppSchema }>('associatedApp', 'name avatar intro')
.lean();
} catch (error) {
addLog.error('getServerSideProps', error);
return undefined;
@@ -378,10 +377,10 @@ export async function getServerSideProps(context: any) {
return {
props: {
appId: String(app?.appId?._id) ?? '',
appName: app?.appId?.name ?? 'AI',
appAvatar: app?.appId?.avatar ?? '',
appIntro: app?.appId?.intro ?? 'AI',
appId: String(app?.appId) ?? '',
appName: app?.associatedApp?.name ?? 'AI',
appAvatar: app?.associatedApp?.avatar ?? '',
appIntro: app?.associatedApp?.intro ?? 'AI',
showRawSource: app?.showRawSource ?? false,
showNodeStatus: app?.showNodeStatus ?? false,
shareId: shareId ?? '',

View File

@@ -158,11 +158,11 @@ const InputDataModal = ({
const maxToken = useMemo(() => {
const vectorModel =
vectorModelList.find((item) => item.model === collection.datasetId.vectorModel) ||
vectorModelList.find((item) => item.model === collection.dataset.vectorModel) ||
vectorModelList[0];
return vectorModel?.maxToken || 3000;
}, [collection.datasetId.vectorModel, vectorModelList]);
}, [collection.dataset.vectorModel, vectorModelList]);
// import new data
const { mutate: sureImportData, isLoading: isImporting } = useRequest({

View File

@@ -1,14 +1,13 @@
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { Box, Flex, HStack, ModalBody } from '@chakra-ui/react';
import { Box, Flex, HStack } from '@chakra-ui/react';
import Avatar from '@fastgpt/web/components/common/Avatar';
import MyBox from '@fastgpt/web/components/common/MyBox';
import React, { useState } from 'react';
import React from 'react';
import { useTranslation } from 'next-i18next';
import MyIcon from '@fastgpt/web/components/common/Icon';
import MyModal from '@fastgpt/web/components/common/MyModal';
import Markdown from '@/components/Markdown';
import { NodeTemplateListItemType } from '@fastgpt/global/core/workflow/type/node';
import { PluginGroupSchemaType } from '@fastgpt/service/core/app/plugin/type';
import UseGuideModal from '@/components/common/Modal/UseGuideModal';
const PluginCard = ({
item,
@@ -20,8 +19,6 @@ const PluginCard = ({
const { t } = useTranslation();
const { feConfigs } = useSystemStore();
const [currentPlugin, setCurrentPlugin] = useState<NodeTemplateListItemType | null>(null);
const type = groups.reduce<string | undefined>((acc, group) => {
const foundType = group.groupTypes.find((type) => type.typeId === item.templateType);
return foundType ? foundType.typeName : acc;
@@ -82,51 +79,33 @@ const PluginCard = ({
<Flex w={'full'} fontSize={'mini'}>
<Flex flex={1}>
{item.instructions && (
<Flex
color={'primary.700'}
alignItems={'center'}
gap={1}
cursor={'pointer'}
onClick={() => setCurrentPlugin(item)}
_hover={{ bg: 'myGray.100' }}
{(item.instructions || item.courseUrl) && (
<UseGuideModal
title={item.name}
iconSrc={item.avatar}
text={item.instructions}
link={item.courseUrl}
>
<MyIcon name={'book'} w={'14px'} />
{t('app:plugin.Instructions')}
</Flex>
{({ onClick }) => (
<Flex
color={'primary.700'}
alignItems={'center'}
gap={1}
cursor={'pointer'}
onClick={onClick}
_hover={{ bg: 'myGray.100' }}
>
<MyIcon name={'book'} w={'14px'} />
{t('app:plugin.Instructions')}
</Flex>
)}
</UseGuideModal>
)}
</Flex>
<Box color={'myGray.500'}>{`by ${item.author || feConfigs.systemTitle}`}</Box>
</Flex>
{currentPlugin && (
<InstructionModal currentPlugin={currentPlugin} onClose={() => setCurrentPlugin(null)} />
)}
</MyBox>
);
};
const InstructionModal = ({
currentPlugin,
onClose
}: {
currentPlugin: NodeTemplateListItemType;
onClose: () => void;
}) => {
return (
<MyModal
isOpen
iconSrc={currentPlugin.avatar}
title={currentPlugin.name}
onClose={onClose}
minW={'600px'}
>
<ModalBody>
<Box border={'base'} borderRadius={'10px'} p={4} minH={'500px'}>
<Markdown source={currentPlugin.instructions} />
</Box>
</ModalBody>
</MyModal>
);
};
export default React.memo(PluginCard);

View File

@@ -4,14 +4,14 @@ import type { FastGPTFeConfigsType } from '@fastgpt/global/common/system/types/i
import type { FastGPTConfigFileType } from '@fastgpt/global/common/system/types/index.d';
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
import { getFastGPTConfigFromDB } from '@fastgpt/service/common/system/config/controller';
import { PluginTemplateType } from '@fastgpt/global/core/plugin/type';
import { FastGPTProUrl } from '@fastgpt/service/common/system/constants';
import { isProduction } from '@fastgpt/global/common/system/constants';
import { initFastGPTConfig } from '@fastgpt/service/common/system/tools';
import json5 from 'json5';
import { SystemPluginTemplateItemType } from '@fastgpt/global/core/workflow/type';
import { defaultGroup } from '@fastgpt/web/core/workflow/constants';
import { defaultGroup, defaultTemplateTypes } from '@fastgpt/web/core/workflow/constants';
import { MongoPluginGroups } from '@fastgpt/service/core/app/plugin/pluginGroupSchema';
import { MongoTemplateTypes } from '@fastgpt/service/core/app/templates/templateTypeSchema';
export const readConfigData = (name: string) => {
const splitName = name.split('.');
@@ -164,7 +164,7 @@ function getSystemPlugin() {
global.communityPlugins = fileTemplates;
}
export async function initSystemPlugins() {
export async function initSystemPluginGroups() {
try {
const { groupOrder, ...restDefaultGroup } = defaultGroup;
await MongoPluginGroups.updateOne(
@@ -182,3 +182,27 @@ export async function initSystemPlugins() {
console.error('Error initializing system plugins:', error);
}
}
export async function initAppTemplateTypes() {
try {
await Promise.all(
defaultTemplateTypes.map((templateType) => {
const { typeOrder, ...rest } = templateType;
return MongoTemplateTypes.updateOne(
{
typeId: templateType.typeId
},
{
$set: rest
},
{
upsert: true
}
);
})
);
} catch (error) {
console.error('Error initializing system templates:', error);
}
}

View File

@@ -4,11 +4,14 @@ import { createDatasetTrainingMongoWatch } from '@/service/core/dataset/training
import { MongoSystemConfigs } from '@fastgpt/service/common/system/config/schema';
import { MongoSystemPlugin } from '@fastgpt/service/core/app/plugin/systemPluginSchema';
import { debounce } from 'lodash';
import { MongoAppTemplate } from '@fastgpt/service/core/app/templates/templateSchema';
import { getAppTemplatesAndLoadThem } from '@fastgpt/templates/register';
export const startMongoWatch = async () => {
reloadConfigWatch();
refetchSystemPlugins();
createDatasetTrainingMongoWatch();
refetchAppTemplates();
};
const reloadConfigWatch = () => {
@@ -38,3 +41,18 @@ const refetchSystemPlugins = () => {
}, 500)
);
};
const refetchAppTemplates = () => {
const changeStream = MongoAppTemplate.watch();
changeStream.on(
'change',
debounce(async (change) => {
setTimeout(() => {
try {
getAppTemplatesAndLoadThem(true);
} catch (error) {}
}, 5000);
}, 500)
);
};

View File

@@ -1,48 +0,0 @@
import { isProduction } from '@fastgpt/global/common/system/constants';
import { readdirSync, readFileSync } from 'fs';
import path from 'path';
// Get template from memory or file system
const loadTemplateMarketItems = async () => {
if (isProduction && global.appMarketTemplates) return global.appMarketTemplates;
const templatesDir = path.join(process.cwd(), 'public', 'appMarketTemplates');
const templateNames = readdirSync(templatesDir);
global.appMarketTemplates = templateNames.map((name) => {
try {
const filePath = path.join(templatesDir, name, 'template.json');
const fileContent = readFileSync(filePath, 'utf-8');
const data = JSON.parse(fileContent);
return {
id: name,
...data
};
} catch (error) {
console.error(`Error fetching template ${name}:`, error);
return null;
}
});
global.appMarketTemplates.sort((a, b) => (b.weight ?? 0) - (a.weight ?? 0));
return global.appMarketTemplates;
};
export const getTemplateMarketItemDetail = async (id: string) => {
const templateMarketItems = await loadTemplateMarketItems();
return templateMarketItems.find((item) => item.id === id);
};
export const getTemplateMarketItemList = async () => {
const templateMarketItems = await loadTemplateMarketItems();
return templateMarketItems.map((item) => ({
id: item.id,
name: item.name,
avatar: item.avatar,
intro: item.intro,
author: item.author,
tags: item.tags,
type: item.type
}));
};

View File

@@ -1,4 +1,4 @@
import { getUserChatInfoAndAuthTeamPoints } from '@/service/support/permission/auth/team';
import { getUserChatInfoAndAuthTeamPoints } from '@fastgpt/service/support/permission/auth/team';
import { pushChatUsage } from '@/service/support/wallet/usage/push';
import { getNextTimeByCronStringAndTimezone } from '@fastgpt/global/common/string/time';
import { getNanoid } from '@fastgpt/global/common/string/tools';
@@ -39,7 +39,7 @@ export const getScheduleTriggerApp = async () => {
if (!app.scheduledTriggerConfig) return;
// random delay 0 ~ 60s
await delay(Math.floor(Math.random() * 60 * 1000));
const { user } = await getUserChatInfoAndAuthTeamPoints(app.tmbId);
const { timezone, externalProvider } = await getUserChatInfoAndAuthTeamPoints(app.tmbId);
// Get app latest version
const { nodes, edges, chatConfig } = await getAppLatestVersion(app._id, app);
@@ -57,7 +57,8 @@ export const getScheduleTriggerApp = async () => {
const { flowUsages, assistantResponses, flowResponses } = await retryFn(() => {
return dispatchWorkFlow({
chatId,
user,
timezone,
externalProvider,
mode: 'chat',
runningAppInfo: {
id: String(app._id),

View File

@@ -7,7 +7,7 @@ import type {
} from '@fastgpt/global/support/outLink/api.d';
import { ShareChatAuthProps } from '@fastgpt/global/support/permission/chat';
import { authOutLinkValid } from '@fastgpt/service/support/permission/publish/authLink';
import { getUserChatInfoAndAuthTeamPoints } from '@/service/support/permission/auth/team';
import { getUserChatInfoAndAuthTeamPoints } from '@fastgpt/service/support/permission/auth/team';
import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant';
import { OutLinkErrEnum } from '@fastgpt/global/common/error/code/outLink';
import { OutLinkSchema } from '@fastgpt/global/support/outLink/type';
@@ -57,7 +57,7 @@ export async function authOutLinkChatStart({
const { outLinkConfig, appId } = await authOutLinkValid({ shareId });
// check ai points and chat limit
const [{ user }, { uid }] = await Promise.all([
const [{ timezone, externalProvider }, { uid }] = await Promise.all([
getUserChatInfoAndAuthTeamPoints(outLinkConfig.tmbId),
authOutLinkChatLimit({ outLink: outLinkConfig, ip, outLinkUid, question })
]);
@@ -69,7 +69,8 @@ export async function authOutLinkChatStart({
authType: AuthUserTypeEnum.token,
responseDetail: outLinkConfig.responseDetail,
showNodeStatus: outLinkConfig.showNodeStatus,
user,
timezone,
externalProvider,
appId,
uid
};

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