Compare commits
31 Commits
v4.8.11-fi
...
v4.8.12-al
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b0706ed92 | ||
|
|
718108a552 | ||
|
|
025facbc2d | ||
|
|
87b4061302 | ||
|
|
618729a254 | ||
|
|
3f34c33d4c | ||
|
|
779ff29ed5 | ||
|
|
445a38924f | ||
|
|
7ec2253f0a | ||
|
|
6522cc7dfa | ||
|
|
40f527a021 | ||
|
|
4aaf9bfeb0 | ||
|
|
5efa70f3a1 | ||
|
|
d45d18cc3b | ||
|
|
8bdb35ff51 | ||
|
|
e56965a8ed | ||
|
|
d4e0a43771 | ||
|
|
00638d6ee7 | ||
|
|
da281ea9ec | ||
|
|
4f1ce640a7 | ||
|
|
2a2b919daf | ||
|
|
3f01cc9c63 | ||
|
|
bc70428d89 | ||
|
|
d55ccc9f64 | ||
|
|
27ef4dc8ea | ||
|
|
daa5b552b1 | ||
|
|
fe6c8897ce | ||
|
|
0dcda9822e | ||
|
|
06e1c5d68d | ||
|
|
d52da7d4e0 | ||
|
|
a42707064a |
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 飞书话题群
|
||||
url: https://oss.laf.run/otnvvf-imgs/feishu3.png
|
||||
url: https://oss.laf.run/otnvvf-imgs/fastgpt-feishu1.png
|
||||
about: FastGPT 全是问题群
|
||||
|
||||
BIN
.github/imgs/image.png
vendored
Normal file
BIN
.github/imgs/image.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
@@ -77,7 +77,7 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
|
||||
`4` OpenAPI 接口
|
||||
- [x] completions 接口 (chat 模式对齐 GPT 接口)
|
||||
- [x] 知识库 CRUD
|
||||
- [ ] 对话 CRUD
|
||||
- [x] 对话 CRUD
|
||||
|
||||
`5` 运营能力
|
||||
- [x] 免登录分享窗口
|
||||
@@ -120,7 +120,7 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
|
||||
|
||||
扫码加入飞书话题群 (新开,逐渐弃用微信群):
|
||||
|
||||

|
||||

|
||||
|
||||
<a href="#readme">
|
||||
<img src="https://img.shields.io/badge/-返回顶部-7d09f1.svg" alt="#" align="right">
|
||||
|
||||
207
README_en.md
207
README_en.md
@@ -4,6 +4,8 @@
|
||||
|
||||
# FastGPT
|
||||
|
||||

|
||||
|
||||
<p align="center">
|
||||
<a href="./README_en.md">English</a> |
|
||||
<a href="./README.md">简体中文</a> |
|
||||
@@ -12,106 +14,67 @@
|
||||
|
||||
FastGPT is a knowledge-based platform built on the LLMs, offers a comprehensive suite of out-of-the-box capabilities such as data processing, RAG retrieval, and visual AI workflow orchestration, letting you easily develop and deploy complex question-answering systems without the need for extensive setup or configuration.
|
||||
|
||||
[](https://github.com/labring/FastGPT/stargazers)
|
||||
[](https://github.com/labring/FastGPT/pulls)
|
||||
[](https://github.com/labring/FastGPT/pulls)
|
||||
[](https://github.com/labring/FastGPT/blob/main/LICENSE)
|
||||
[](https://doc.tryfastgpt.ai/docs/intro)
|
||||
[](https://doc.tryfastgpt.ai/docs/intro)
|
||||
[](https://tryfastgpt.ai/)
|
||||
|
||||
[](https://discord.gg/mp68xkZn2Q)
|
||||
[](https://oss.laf.run/otnvvf-imgs/feishu3.png)
|
||||
|
||||
</div>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://tryfastgpt.ai/">
|
||||
<img height="21" src="https://img.shields.io/badge/Try it Online-d4eaf7?style=flat-square&logo=spoj&logoColor=7d09f1" alt="cloud">
|
||||
</a>
|
||||
<a href="https://doc.tryfastgpt.ai/docs/intro">
|
||||
<img height="21" src="https://img.shields.io/badge/Documents-7d09f1?style=flat-square" alt="document">
|
||||
</a>
|
||||
<a href="https://doc.tryfastgpt.ai/docs/development">
|
||||
<img height="21" src="https://img.shields.io/badge/Local Development-%23d4eaf7?style=flat-square&logo=xcode&logoColor=7d09f1" alt="development">
|
||||
</a>
|
||||
<a href="https://github.com/labring/FastGPT/blob/main/LICENSE">
|
||||
<img height="21" src="https://img.shields.io/badge/License-Apache--2.0-ffffff?style=flat-square&labelColor=d4eaf7&color=7d09f1" alt="license">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](https://discord.gg/mp68xkZn2Q)
|
||||
|
||||
</div>
|
||||
## 🎥 Comprehensive Feature Demonstration
|
||||
|
||||
https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409bd33f6d4
|
||||
|
||||
## 🛸 Use Cloud Services
|
||||
## 🛸 Online Use
|
||||
|
||||
Cloud: [tryfastgpt.ai](https://tryfastgpt.ai/)
|
||||
Website: [tryfastgpt.ai](https://tryfastgpt.ai/)
|
||||
|
||||
| | |
|
||||
| ---------------------------------- | ---------------------------------- |
|
||||
| Conversational AI Setup | Workflow Automation |
|
||||
|  |  |
|
||||
| Knowledge Base Setup | Integration Process |
|
||||
|  |  |
|
||||
|
||||
<a href="#readme">
|
||||
<a href="#FastGPT">
|
||||
<img src="https://img.shields.io/badge/-Back_to_Top-7d09f1.svg" alt="#" align="right">
|
||||
</a>
|
||||
|
||||
## 💡 Features
|
||||
|
||||
`1` Application Orchestration Features
|
||||
|
||||
- [x] Offers a straightforward mode, eliminating the need for complex orchestration
|
||||
- [x] Provides clear next-step instructions in dialogues
|
||||
- [x] Facilitates workflow orchestration
|
||||
- [x] Tracks references in source files
|
||||
- [x] Encapsulates modules for enhanced reuse at multiple levels
|
||||
- [x] Combines search and reordering functions
|
||||
- [ ] Includes a tool module
|
||||
- [ ] Integrates [Laf](https://github.com/labring/laf) for online HTTP module creation
|
||||
- [ ] Plugin encapsulation capabilities
|
||||
|
||||
`2` Knowledge Base Features
|
||||
|
||||
- [x] Allows for the mixed use of multiple databases
|
||||
- [x] Keeps track of modifications and deletions in data chunks
|
||||
- [x] Enables specific vector models for each knowledge base
|
||||
- [x] Stores original source files
|
||||
- [x] Supports direct input and segment-based QA import
|
||||
- [x] Compatible with a variety of file formats: pdf, docx, txt, html, md, csv
|
||||
- [x] Facilitates URL reading and bulk CSV importing
|
||||
- [ ] Supports PPT and Excel file import
|
||||
- [ ] Features a file reader
|
||||
- [ ] Offers diverse data preprocessing options
|
||||
|
||||
`3` Application Debugging Features
|
||||
|
||||
- [x] Enables targeted search testing within the knowledge base
|
||||
- [x] Allows feedback, editing, and deletion during conversations
|
||||
- [x] Presents the full context of interactions
|
||||
- [x] Displays all intermediate values within modules
|
||||
- [ ] Advanced DeBug mode for orchestration
|
||||
|
||||
`4` OpenAPI Interface
|
||||
|
||||
- [x] The completions interface (aligned with GPT's chat mode interface)
|
||||
- [x] CRUD operations for the knowledge base
|
||||
- [ ] CRUD operations for conversations
|
||||
|
||||
`5` Operational Features
|
||||
|
||||
- [x] Share without requiring login
|
||||
- [x] Easy embedding with Iframe
|
||||
- [x] Customizable chat window embedding with features like default open, drag-and-drop
|
||||
- [x] Centralizes conversation records for review and annotation
|
||||
| **Features** | **Details** |
|
||||
|--------------------------------------------|---------------------------------------------------|
|
||||
| **Application Orchestration Features** | ✅ Offers a straightforward mode, eliminating the need for complex orchestration <br> ✅ Provides clear next-step instructions in dialogues <br> ✅ Facilitates workflow orchestration <br> ✅ Tracks references in source files <br> ✅ Encapsulates modules for enhanced reuse at multiple levels <br> ✅ Combines search and reordering functions <br> 🔜 Includes a tool module <br> 🔜 Integrates [Laf](https://github.com/labring/laf) for online HTTP module creation <br> 🔜 Plugin encapsulation capabilities |
|
||||
| **Knowledge Base Features** | ✅ Allows for the mixed use of multiple databases <br> ✅ Keeps track of modifications and deletions in data chunks <br> ✅ Enables specific vector models for each knowledge base <br> ✅ Stores original source files <br> ✅ Supports direct input and segment-based QA import <br> ✅ Compatible with a variety of file formats: pdf, docx, txt, html, md, csv <br> ✅ Facilitates URL reading and bulk CSV importing <br> 🔜 Supports PPT and Excel file import <br> 🔜 Features a file reader <br> 🔜 Offers diverse data preprocessing options |
|
||||
| **Application Debugging Features** | ✅ Enables targeted search testing within the knowledge base <br> ✅ Allows feedback, editing, and deletion during conversations <br> ✅ Presents the full context of interactions <br> ✅ Displays all intermediate values within modules <br> 🔜 Advanced Debug mode for orchestration |
|
||||
| **OpenAPI Interface** | ✅ The completions interface (aligned with GPT's chat mode interface) <br> ✅ CRUD operations for the knowledge base <br> 🔜 CRUD operations for conversation |
|
||||
| **Operational Features** | ✅ Share without requiring login <br> ✅ Easy embedding with Iframe <br> ✅ Customizable chat window embedding with features like default open, drag-and-drop <br> ✅ Centralizes conversation records for review and annotation |
|
||||
|
||||
|
||||
<a href="#readme">
|
||||
<a href="#FastGPT">
|
||||
<img src="https://img.shields.io/badge/-Back_to_Top-7d09f1.svg" alt="#" align="right">
|
||||
</a>
|
||||
|
||||
## 👨💻 Development
|
||||
|
||||
Project tech stack: NextJs + TS + ChakraUI + Mongo + Postgres (Vector plugin)
|
||||
Project tech stack: NextJs + TS + ChakraUI + MongoDB + PostgreSQL (PG Vector plug-in)/Milvus
|
||||
|
||||
- **⚡ Deployment**
|
||||
- **⚡ Fast Deployment**
|
||||
|
||||
> When using [Sealos](https://sealos.io) services, there is no need to purchase servers or domain names. It supports high concurrency and dynamic scaling, and the database application uses the kubeblocks database, which far exceeds the simple Docker container deployment in terms of IO performance.
|
||||
<div align="center">
|
||||
[](https://cloud.sealos.io/?openapp=system-fastdeploy%3FtemplateName%3Dfastgpt)
|
||||
</div>
|
||||
|
||||
Give it a 2-4 minute wait after deployment as it sets up the database. Initially, it might be a tad slow since we're using the basic settings.
|
||||
Give it a 2-4 minute wait after deployment as it sets up the database. Initially, it might be a too slow since we're using the basic settings.
|
||||
|
||||
[sealos one click deployment tutorial](https://doc.tryfastgpt.ai/docs/development/sealos/)
|
||||
|
||||
- [Getting Started with Local Development](https://doc.tryfastgpt.ai/docs/development)
|
||||
- [Deploying FastGPT](https://doc.tryfastgpt.ai/docs/installation)
|
||||
@@ -119,25 +82,7 @@ Project tech stack: NextJs + TS + ChakraUI + Mongo + Postgres (Vector plugin)
|
||||
- [Configuring Multiple Models](https://doc.tryfastgpt.ai/docs/installation/reference/models)
|
||||
- [Version Updates & Upgrades](https://doc.tryfastgpt.ai/docs/installation/upgrading)
|
||||
|
||||
|
||||
## 🏘️ Community & support
|
||||
|
||||
+ 🌐 Visit the [FastGPT website](https://tryfastgpt.ai/) for full documentation and useful links.
|
||||
+ 💬 Join our [Discord server](https://discord.gg/mp68xkZn2Q) is to chat with FastGPT developers and other FastGPT users. This is a good place to learn about FastGPT, ask questions, and share your experiences.
|
||||
+ 🐞 Create [GitHub Issues](https://github.com/labring/FastGPT/issues/new/choose) for bug reports and feature requests.
|
||||
|
||||
<a href="#readme">
|
||||
<img src="https://img.shields.io/badge/-Back_to_Top-7d09f1.svg" alt="#" align="right">
|
||||
</a>
|
||||
|
||||
## 👀 Others
|
||||
|
||||
- [FastGPT FAQ](https://kjqvjse66l.feishu.cn/docx/HtrgdT0pkonP4kxGx8qcu6XDnGh)
|
||||
- [Docker Deployment Tutorial Video](https://www.bilibili.com/video/BV1jo4y147fT/)
|
||||
- [Official Account Integration Video Tutorial](https://www.bilibili.com/video/BV1xh4y1t7fy/)
|
||||
- [FastGPT Knowledge Base Demo](https://www.bilibili.com/video/BV1Wo4y1p7i1/)
|
||||
|
||||
<a href="#readme">
|
||||
<a href="#FastGPT">
|
||||
<img src="https://img.shields.io/badge/-Back_to_Top-7d09f1.svg" alt="#" align="right">
|
||||
</a>
|
||||
|
||||
@@ -148,7 +93,7 @@ Project tech stack: NextJs + TS + ChakraUI + Mongo + Postgres (Vector plugin)
|
||||
- [One API: Multi-model management, supports Azure, Wenxin Yiyuan, etc.](https://github.com/songquanpeng/one-api)
|
||||
- [TuShan: Build a backend management system in 5 minutes](https://github.com/msgbyte/tushan)
|
||||
|
||||
<a href="#readme">
|
||||
<a href="#FastGPT">
|
||||
<img src="https://img.shields.io/badge/-Back_to_Top-7d09f1.svg" alt="#" align="right">
|
||||
</a>
|
||||
|
||||
@@ -156,10 +101,69 @@ Project tech stack: NextJs + TS + ChakraUI + Mongo + Postgres (Vector plugin)
|
||||
|
||||
- [luolinAI: Enterprise WeChat bot, ready to use](https://github.com/luolin-ai/FastGPT-Enterprise-WeChatbot)
|
||||
|
||||
<a href="#readme">
|
||||
<a href="#FastGPT">
|
||||
<img src="https://img.shields.io/badge/-Back_to_Top-7d09f1.svg" alt="#" align="right">
|
||||
</a>
|
||||
|
||||
|
||||
## 🏘️ Community & Support
|
||||
|
||||
+ 🌐 Visit the [FastGPT website](https://tryfastgpt.ai/) for full documentation and useful links.
|
||||
+ 💬 Join our [Discord server](https://discord.gg/mp68xkZn2Q) is to chat with FastGPT developers and other FastGPT users. This is a good place to learn about FastGPT, ask questions, and share your experiences.
|
||||
+ 🐞 Create [GitHub Issues](https://github.com/labring/FastGPT/issues/new/choose) for bug reports and feature requests.
|
||||
|
||||
<a href="#FastGPT">
|
||||
<img src="https://img.shields.io/badge/-Back_to_Top-7d09f1.svg" alt="#" align="right">
|
||||
</a>
|
||||
|
||||
## 👀 Others
|
||||
|
||||
- [FastGPT FAQ](https://kjqvjse66l.feishu.cn/docx/HtrgdT0pkonP4kxGx8qcu6XDnGh)
|
||||
- [Docker Deployment Tutorial Video](https://www.bilibili.com/video/BV1jo4y147fT/)
|
||||
- [Official Account Integration Video Tutorial](https://www.bilibili.com/video/BV1xh4y1t7fy/)
|
||||
- [FastGPT Knowledge Base Demo](https://www.bilibili.com/video/BV1Wo4y1p7i1/)
|
||||
|
||||
<a href="#FastGPT">
|
||||
<img src="https://img.shields.io/badge/-Back_to_Top-7d09f1.svg" alt="#" align="right">
|
||||
</a>
|
||||
|
||||
## 🌱 Contributors
|
||||
|
||||
We welcome all forms of contributions. If you are interested in contributing code, you can check out our GitHub [Issues](https://github.com/labring/FastGPT/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc) to show us your ideas.
|
||||
|
||||
<a href="https://github.com/labring/FastGPT/graphs/contributors" target="_blank">
|
||||
<table>
|
||||
<tr>
|
||||
<th colspan="2">
|
||||
<br><img src="https://contrib.rocks/image?repo=labring/FastGPT"><br><br>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://next.ossinsight.io/widgets/official/compose-org-active-contributors/thumbnail.png?activity=active&period=past_28_days&owner_id=102226726&repo_ids=605673387&image_size=2x3&color_scheme=dark">
|
||||
<img alt="Active participants of labring - past 28 days" src="https://next.ossinsight.io/widgets/official/compose-org-active-contributors/thumbnail.png?activity=active&period=past_28_days&owner_id=102226726&repo_ids=605673387&image_size=2x3&color_scheme=light">
|
||||
</picture>
|
||||
</td>
|
||||
<td rowspan="2">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://next.ossinsight.io/widgets/official/compose-org-participants-growth/thumbnail.png?activity=new&period=past_28_days&owner_id=102226726&repo_ids=605673387&image_size=4x7&color_scheme=dark">
|
||||
<img alt="New trends of labring" src="https://next.ossinsight.io/widgets/official/compose-org-participants-growth/thumbnail.png?activity=new&period=past_28_days&owner_id=102226726&repo_ids=605673387&image_size=4x7&color_scheme=light">
|
||||
</picture>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://next.ossinsight.io/widgets/official/compose-org-active-contributors/thumbnail.png?activity=new&period=past_28_days&owner_id=102226726&repo_ids=605673387&image_size=2x3&color_scheme=dark">
|
||||
<img alt="New participants of labring - past 28 days" src="https://next.ossinsight.io/widgets/official/compose-org-active-contributors/thumbnail.png?activity=new&period=past_28_days&owner_id=102226726&repo_ids=605673387&image_size=2x3&color_scheme=light">
|
||||
</picture>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</a>
|
||||
|
||||
|
||||
## 🌟 Star History
|
||||
|
||||
<a href="https://github.com/labring/FastGPT/stargazers" target="_blank" style="display: block" align="center">
|
||||
@@ -169,3 +173,20 @@ Project tech stack: NextJs + TS + ChakraUI + Mongo + Postgres (Vector plugin)
|
||||
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=labring/FastGPT&type=Date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
<a href="#FastGPT">
|
||||
<img src="https://img.shields.io/badge/-Back_to_Top-7d09f1.svg" alt="#" align="right">
|
||||
</a>
|
||||
|
||||
## 📄 Usage Agreement
|
||||
|
||||
This repository complies with the [FastGPT Open Source License](./LICENSE) open source agreement.
|
||||
|
||||
1. Direct commercial use as a backend service is allowed, but provision of SaaS services is not allowed.
|
||||
2. Without commercial authorization, any form of commercial service must retain relevant copyright information.
|
||||
3. For full details, please see [FastGPT Open Source License](./LICENSE)
|
||||
4. Contact: Dennis@sealos.io , [click to view commercial version pricing strategy](https://doc.tryfastgpt.ai/docs/commercial)
|
||||
|
||||
<a href="#FastGPT">
|
||||
<img src="https://img.shields.io/badge/-Back_to_Top-7d09f1.svg" alt="#" align="right">
|
||||
</a>
|
||||
5
docSite/assets/images/social/lark.svg
Normal file
5
docSite/assets/images/social/lark.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg t="1728884315234" class="icon" viewBox="0 0 1224 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4847" width="28" height="28" fill="currentColor">
|
||||
|
||||
<path d="M1224.146926 401.768509a50.444385 50.444385 0 0 0-23.813954-38.631991c-6.095363-3.741292-61.752335-36.782364-141.475481-43.949671a317.253146 317.253146 0 0 0-135.884563 16.982943L746.964061 25.579507A50.444385 50.444385 0 0 0 703.077446 0h-418.268027A50.444385 50.444385 0 0 0 248.027055 84.97777c3.236848 3.447033 296.360763 315.739814 426.969683 459.653442-59.734559 55.762064-103.558119 83.800735-127.666331 96.832201l-200.894764-140.823909a50.045034 50.045034 0 0 0-6.97814-4.098606L79.416697 314.205464A50.444385 50.444385 0 0 0 0.744475 364.124387c0.210185 1.177036 20.619142 118.607361 42.036988 237.635091C86.815207 847.297523 91.775572 859.656397 95.054457 867.874628c5.065457 12.611096 14.334613 24.549601 44.895503 44.538188a595.916337 595.916337 0 0 0 69.361029 38.337733c49.519571 23.603769 128.212812 54.437899 221.59798 67.25918a623.009175 623.009175 0 0 0 85.061845 5.948234c131.491697 0 290.055215-44.138837 418.373119-211.404011 73.564728-96.054517 118.250046-163.944252 154.086578-218.592335 44.033745-67.070014 70.622139-107.551633 118.838564-150.177139a50.444385 50.444385 0 0 0 16.877851-42.015969zM673.693591 100.88877L834.443032 384.638437a413.097477 413.097477 0 0 0-63.055481 59.356226c-8.743693 10.04684-17.256183 19.568218-25.579507 28.711263C656.248242 373.961042 497.033151 203.332909 401.188819 100.88877zM305.491617 882.125167c-59.86067-22.594881-102.065806-47.85911-118.523287-59.692523-10.299062-45.610132-39.935138-209.638457-65.829922-355.780044l391.238243 274.270325a48.132351 48.132351 0 0 0 6.725918 3.951477l189.166445 132.689752a398.300458 398.300458 0 0 1-155.410744 44.138837c-97.336645 7.713787-188.262649-17.277202-247.366653-39.577824z m698.654734-343.442189c-34.932737 53.197808-78.398982 119.385045-149.819824 212.496972a503.371908 503.371908 0 0 1-58.641598 64.33761l-158.185184-110.830518c35.31107-23.813953 81.152405-60.070855 135.905581-114.803013a48.342536 48.342536 0 0 0 14.944149-15.154334c18.790533-19.379051 38.568936-40.859952 59.272153-64.694924 57.086229-65.745849 124.009113-96.243683 198.540692-90.673782a247.639894 247.639894 0 0 1 38.589955 6.011289c-28.290893 33.62959-51.936698 69.63427-80.605924 113.3107z" p-id="4848"></path>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -11,6 +11,6 @@ FastGPT 是一个由用户和贡献者参与推动的开源项目,如果您对
|
||||
|
||||
+ 📱 扫码加入社区微信交流群👇
|
||||
|
||||
<img width="400px" src="https://oss.laf.run/htr4n1-images/fastgpt-qr-code.jpg" class="medium-zoom-image" />
|
||||
<img width="400px" src="https://oss.laf.run/otnvvf-imgs/fastgpt-feishu1.png" class="medium-zoom-image" />
|
||||
|
||||
+ 🐞 请将任何 FastGPT 的 Bug、问题和需求提交到 [GitHub Issue](https://github.com/labring/fastgpt/issues/new/choose)。
|
||||
@@ -188,19 +188,22 @@ weight: 708
|
||||
|
||||
## 关于模型 logo
|
||||
|
||||
统一放置在项目的`public/imgs/model/xxx`目录中,目前内置了以下几种,如果有需要,可以PR增加。默认头像为 Hugging face 的 logo~
|
||||
统一放置在项目的`public/imgs/model/xxx`目录中,目前内置了以下几种,如果有需要,可以PR增加。默认logo为 Hugging face 的 logo~
|
||||
|
||||
- /imgs/model/baichuan.svg - 百川
|
||||
- /imgs/model/chatglm.svg - 智谱
|
||||
- /imgs/model/baichuan.svg - 百川智能
|
||||
- /imgs/model/chatglm.svg - 智谱清言
|
||||
- /imgs/model/claude.svg - claude
|
||||
- /imgs/model/deepseek.svg - deepseek
|
||||
- /imgs/model/doubao.svg - 火山豆包
|
||||
- /imgs/model/ernie.svg - 文心一言
|
||||
- /imgs/model/gemini.svg - gemini
|
||||
- /imgs/model/huggingface.svg - Hugging face【默认logo】
|
||||
- /imgs/model/minimax.svg - minimax
|
||||
- /imgs/model/moonshot.svg - 月之暗面
|
||||
- /imgs/model/openai.svg - OpenAI GPT
|
||||
- /imgs/model/qwen.svg - 通义千问
|
||||
- /imgs/model/sparkDesk.svg - 讯飞星火
|
||||
- /imgs/model/yi.svg - 零一万物
|
||||
- /imgs/model/gemini.svg - gemini
|
||||
- /imgs/model/deepseek.svg - deepseek
|
||||
- /imgs/model/minimax.svg - minimax
|
||||
-
|
||||
|
||||
## 特殊模型
|
||||
@@ -227,6 +230,27 @@ weight: 708
|
||||
}
|
||||
```
|
||||
|
||||
### ReRank 接入(硅基流动)
|
||||
|
||||
有免费的 `bge-reranker-v2-m3` 模型可以使用。
|
||||
|
||||
1. 注册硅基流动账号: https://siliconflow.cn/
|
||||
2. 进入控制台,获取 API key: https://cloud.siliconflow.cn/account/ak
|
||||
3. 修改 FastGPT 配置文件
|
||||
|
||||
```json
|
||||
{
|
||||
"reRankModels": [
|
||||
{
|
||||
"model": "BAAI/bge-reranker-v2-m3", // 这里的model需要对应 siliconflow 的模型名
|
||||
"name": "BAAI/bge-reranker-v2-m3",
|
||||
"requestUrl": "https://api.siliconflow.cn/v1/rerank",
|
||||
"requestAuth": "siliconflow 上申请的 key"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### ReRank 接入(Cohere)
|
||||
|
||||
这个重排模型对中文不是很好,不如 bge 的好用。
|
||||
@@ -239,7 +263,7 @@ weight: 708
|
||||
"reRankModels": [
|
||||
{
|
||||
"model": "rerank-multilingual-v2.0", // 这里的model需要对应 cohere 的模型名
|
||||
"name": "检索重排", // 随意
|
||||
"name": "rerank-multilingual-v2.0",
|
||||
"requestUrl": "https://api.cohere.ai/v1/rerank",
|
||||
"requestAuth": "Coherer上申请的key"
|
||||
}
|
||||
|
||||
@@ -35,6 +35,8 @@ weight: 707
|
||||
|
||||
### Milvus版本
|
||||
|
||||
暂不推荐,部分系统存在精度丢失,等待修复。
|
||||
|
||||
对于千万级以上向量性能更优秀。
|
||||
|
||||
[点击查看 Milvus 官方推荐配置](https://milvus.io/docs/prerequisite-docker.md)
|
||||
@@ -49,6 +51,8 @@ weight: 707
|
||||
|
||||
### zilliz cloud版本
|
||||
|
||||
暂不推荐,部分系统存在精度丢失,等待修复。
|
||||
|
||||
亿级以上向量首选。
|
||||
|
||||
由于向量库使用了 Cloud,无需占用本地资源,无需太关注。
|
||||
|
||||
@@ -150,7 +150,7 @@ FastGPT 在`pnpm i`后会执行`postinstall`脚本,用于自动生成`ChakraUI
|
||||
|
||||
遇到困难了吗?有任何问题吗? 加入微信群与开发者和用户保持沟通。
|
||||
|
||||
<img width="400px" src="https://oss.laf.run/htr4n1-images/fastgpt-qr-code.jpg" class="medium-zoom-image" />
|
||||
<img width="400px" src="https://oss.laf.run/otnvvf-imgs/fastgpt-feishu1.png" class="medium-zoom-image" />
|
||||
|
||||
## 代码结构说明
|
||||
|
||||
|
||||
@@ -7,13 +7,15 @@ toc: true
|
||||
weight: 852
|
||||
---
|
||||
|
||||
# 发起对话
|
||||
|
||||
{{% alert icon="🤖 " context="success" %}}
|
||||
* 该接口的 API Key 需使用`应用特定的 key`,否则会报错。
|
||||
|
||||
* 有些包调用时,`BaseUrl`需要添加`v1`路径,有些不需要,如果出现404情况,可补充`v1`重试。
|
||||
{{% /alert %}}
|
||||
|
||||
## 发起对话(简易应用和工作流)
|
||||
## 请求简易应用和工作流
|
||||
|
||||
对话接口兼容`GPT`的接口!如果你的项目使用的是标准的`GPT`官方接口,可以直接通过修改`BaseUrl`和 `Authorization`来访问 FastGpt 应用,不过需要注意下面几个规则:
|
||||
|
||||
@@ -24,12 +26,12 @@ weight: 852
|
||||
|
||||
### 请求
|
||||
|
||||
{{< tabs tabTotal="2" >}}
|
||||
{{< tab tabName="请求示例" >}}
|
||||
{{< tabs tabTotal="3" >}}
|
||||
{{< tab tabName="基础请求示例" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
```bash
|
||||
curl --location --request POST 'https://api.fastgpt.in/api/v1/chat/completions' \
|
||||
curl --location --request POST 'http://localhost:3000/api/v1/chat/completions' \
|
||||
--header 'Authorization: Bearer fastgpt-xxxxxx' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
@@ -42,8 +44,49 @@ curl --location --request POST 'https://api.fastgpt.in/api/v1/chat/completions'
|
||||
},
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "导演是谁",
|
||||
"role": "user"
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab tabName="图片/文件请求示例" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
* 仅`messages`有部分区别,其他参数一致。
|
||||
* 目前不支持上次文件,需上传到自己的对象存储中,获取对应的文件链接。
|
||||
|
||||
```bash
|
||||
curl --location --request POST 'http://localhost:3000/api/v1/chat/completions' \
|
||||
--header 'Authorization: Bearer fastgpt-xxxxxx' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"chatId": "abcd",
|
||||
"stream": false,
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "导演是谁"
|
||||
},
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": "图片链接"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "file_url",
|
||||
"name": "文件名",
|
||||
"url": "文档链接,支持 txt md html word pdf ppt csv excel"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}'
|
||||
@@ -269,7 +312,6 @@ event取值:
|
||||
{{< /tabs >}}
|
||||
|
||||
|
||||
|
||||
## 请求插件
|
||||
|
||||
插件的接口与对话接口一致,仅请求参数略有区别,有以下规定:
|
||||
@@ -455,8 +497,728 @@ event取值:
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
## 使用案例
|
||||
|
||||
- [接入 NextWeb/ChatGPT web 等应用](/docs/course/openapi)
|
||||
- [接入 onwechat](/docs/use-cases/onwechat)
|
||||
- [接入 飞书](/docs/course/feishu)
|
||||
|
||||
# 对话 CRUD
|
||||
|
||||
{{% alert icon="🤖 " context="success" %}}
|
||||
* 以下接口可使用任意`API Key`调用。
|
||||
* 4.8.12 以上版本才能使用
|
||||
{{% /alert %}}
|
||||
|
||||
**重要字段**
|
||||
|
||||
* chatId - 指一个应用下,某一个对话窗口的 ID
|
||||
* dataId - 指一个对话窗口下,某一个对话记录的 ID
|
||||
|
||||
## 历史记录
|
||||
|
||||
### 获取某个应用历史记录
|
||||
|
||||
{{< tabs tabTotal="3" >}}
|
||||
{{< tab tabName="请求示例" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
```bash
|
||||
curl --location --request POST 'http://localhost:3000/api/core/chat/getHistories' \
|
||||
--header 'Authorization: Bearer {{apikey}}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"appId": "appId",
|
||||
"offset": 0,
|
||||
"pageSize": 20
|
||||
}'
|
||||
```
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab tabName="参数说明" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
{{% alert icon=" " context="success" %}}
|
||||
- appId - 应用 Id
|
||||
- offset - 偏移量,即从第几条数据开始取
|
||||
- pageSize - 记录数量
|
||||
{{% /alert %}}
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab tabName="响应示例" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"statusText": "",
|
||||
"message": "",
|
||||
"data": {
|
||||
"list": [
|
||||
{
|
||||
"chatId": "usdAP1GbzSGu",
|
||||
"updateTime": "2024-10-13T03:29:05.779Z",
|
||||
"appId": "66e29b870b24ce35330c0f08",
|
||||
"customTitle": "",
|
||||
"title": "你好",
|
||||
"top": false
|
||||
},
|
||||
{
|
||||
"chatId": "lC0uTAsyNBlZ",
|
||||
"updateTime": "2024-10-13T03:22:19.950Z",
|
||||
"appId": "66e29b870b24ce35330c0f08",
|
||||
"customTitle": "",
|
||||
"title": "测试",
|
||||
"top": false
|
||||
}
|
||||
],
|
||||
"total": 2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
### 修改某个对话的标题
|
||||
|
||||
{{< tabs tabTotal="3" >}}
|
||||
{{< tab tabName="请求示例" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
```bash
|
||||
curl --location --request POST 'http://localhost:3000/api/core/chat/updateHistory' \
|
||||
--header 'Authorization: Bearer {{apikey}}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"appId": "appId",
|
||||
"chatId": "chatId",
|
||||
"customTitle": "自定义标题"
|
||||
}'
|
||||
```
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab tabName="参数说明" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
{{% alert icon=" " context="success" %}}
|
||||
- appId - 应用 Id
|
||||
- chatId - 历史记录 Id
|
||||
- customTitle - 自定义对话名
|
||||
{{% /alert %}}
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab tabName="响应示例" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"statusText": "",
|
||||
"message": "",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
### 置顶 / 取消置顶
|
||||
{{< tabs tabTotal="3" >}}
|
||||
{{< tab tabName="请求示例" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
```bash
|
||||
curl --location --request POST 'http://localhost:3000/api/core/chat/updateHistory' \
|
||||
--header 'Authorization: Bearer {{apikey}}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"appId": "appId",
|
||||
"chatId": "chatId",
|
||||
"top": true
|
||||
}'
|
||||
```
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab tabName="参数说明" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
{{% alert icon=" " context="success" %}}
|
||||
- appId - 应用Id
|
||||
- chatId - 历史记录 Id
|
||||
- top - 是否置顶,ture 置顶,false 取消置顶
|
||||
{{% /alert %}}
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab tabName="响应示例" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"statusText": "",
|
||||
"message": "",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
### 删除某个历史记录
|
||||
|
||||
{{< tabs tabTotal="3" >}}
|
||||
{{< tab tabName="请求示例" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
```bash
|
||||
curl --location --request DELETE 'http://localhost:3000/api/core/chat/delHistory?chatId={{chatId}}&appId={{appId}}' \
|
||||
--header 'Authorization: Bearer {{apikey}}'
|
||||
```
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab tabName="参数说明" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
{{% alert icon=" " context="success" %}}
|
||||
- appId - 应用 Id
|
||||
- chatId - 历史记录 Id
|
||||
{{% /alert %}}
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab tabName="响应示例" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"statusText": "",
|
||||
"message": "",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
### 清空所有历史记录
|
||||
|
||||
{{< tabs tabTotal="3" >}}
|
||||
{{< tab tabName="请求示例" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
```bash
|
||||
curl --location --request DELETE 'http://localhost:3000/api/core/chat/clearHistories?appId={{appId}}' \
|
||||
--header 'Authorization: Bearer {{apikey}}'
|
||||
```
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab tabName="参数说明" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
{{% alert icon=" " context="success" %}}
|
||||
- appId - 应用 Id
|
||||
{{% /alert %}}
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab tabName="响应示例" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"statusText": "",
|
||||
"message": "",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
## 对话记录
|
||||
|
||||
指的是某个 chatId 下的对话记录操作。
|
||||
|
||||
### 获取单个对话初始化信息
|
||||
|
||||
{{< tabs tabTotal="3" >}}
|
||||
{{< tab tabName="请求示例" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
```bash
|
||||
curl --location --request GET 'http://localhost:3000/api/core/chat/init?appId={{appId}}&chatId={{chatId}}' \
|
||||
--header 'Authorization: Bearer {{apikey}}'
|
||||
```
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab tabName="参数说明" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
{{% alert icon=" " context="success" %}}
|
||||
- appId - 应用 Id
|
||||
- chatId - 历史记录 Id
|
||||
{{% /alert %}}
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab tabName="响应示例" >}}
|
||||
{{< markdownify >}}
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"statusText": "",
|
||||
"message": "",
|
||||
"data": {
|
||||
"chatId": "sPVOuEohjo3w",
|
||||
"appId": "66e29b870b24ce35330c0f08",
|
||||
"variables": {
|
||||
|
||||
},
|
||||
"app": {
|
||||
"chatConfig": {
|
||||
"questionGuide": true,
|
||||
"ttsConfig": {
|
||||
"type": "web"
|
||||
},
|
||||
"whisperConfig": {
|
||||
"open": false,
|
||||
"autoSend": false,
|
||||
"autoTTSResponse": false
|
||||
},
|
||||
"chatInputGuide": {
|
||||
"open": false,
|
||||
"textList": [
|
||||
|
||||
],
|
||||
"customUrl": ""
|
||||
},
|
||||
"instruction": "",
|
||||
"variables": [
|
||||
|
||||
],
|
||||
"fileSelectConfig": {
|
||||
"canSelectFile": true,
|
||||
"canSelectImg": true,
|
||||
"maxFiles": 10
|
||||
},
|
||||
"_id": "66f1139aaab9ddaf1b5c596d",
|
||||
"welcomeText": ""
|
||||
},
|
||||
"chatModels": [
|
||||
"GPT-4o-mini"
|
||||
],
|
||||
"name": "测试",
|
||||
"avatar": "/imgs/app/avatar/workflow.svg",
|
||||
"intro": "",
|
||||
"type": "advanced",
|
||||
"pluginInputs": [
|
||||
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
### 获取对话记录列表
|
||||
|
||||
{{< tabs tabTotal="3" >}}
|
||||
{{< tab tabName="请求示例" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
```bash
|
||||
curl --location --request POST 'http://localhost:3000/api/core/chat/getPaginationRecords' \
|
||||
--header 'Authorization: Bearer {{apikey}}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"appId": "appId",
|
||||
"chatId": "chatId",
|
||||
"offset": 0,
|
||||
"pageSize": 10,
|
||||
"loadCustomFeedbacks": true
|
||||
}'
|
||||
```
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab tabName="参数说明" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
{{% alert icon=" " context="success" %}}
|
||||
- appId - 应用 Id
|
||||
- chatId - 历史记录 Id
|
||||
- offset - 偏移量
|
||||
- pageSize - 记录数量
|
||||
- loadCustomFeedbacks - 是否读取自定义反馈(可选)
|
||||
{{% /alert %}}
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab tabName="响应示例" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"statusText": "",
|
||||
"message": "",
|
||||
"data": {
|
||||
"list": [
|
||||
{
|
||||
"_id": "670b84e6796057dda04b0fd2",
|
||||
"dataId": "jzqdV4Ap1u004rhd2WW8yGLn",
|
||||
"obj": "Human",
|
||||
"value": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": {
|
||||
"content": "你好"
|
||||
}
|
||||
}
|
||||
],
|
||||
"customFeedbacks": [
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"_id": "670b84e6796057dda04b0fd3",
|
||||
"dataId": "x9KQWcK9MApGdDQH7z7bocw1",
|
||||
"obj": "AI",
|
||||
"value": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": {
|
||||
"content": "你好!有什么我可以帮助你的吗?"
|
||||
}
|
||||
}
|
||||
],
|
||||
"customFeedbacks": [
|
||||
|
||||
],
|
||||
"llmModuleAccount": 1,
|
||||
"totalQuoteList": [
|
||||
|
||||
],
|
||||
"totalRunningTime": 2.42,
|
||||
"historyPreviewLength": 2
|
||||
}
|
||||
],
|
||||
"total": 2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
### 获取单个对话记录运行详情
|
||||
|
||||
{{< tabs tabTotal="3" >}}
|
||||
{{< tab tabName="请求示例" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
```bash
|
||||
curl --location --request GET 'http://localhost:3000/api/core/chat/getResData?appId={{appId}}&chatId={{chatId}}&dataId={{dataId}}' \
|
||||
--header 'Authorization: Bearer {{apikey}}'
|
||||
```
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab tabName="参数说明" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
{{% alert icon=" " context="success" %}}
|
||||
- appId - 应用 Id
|
||||
- chatId - 对话 Id
|
||||
- dataId - 对话记录 Id
|
||||
{{% /alert %}}
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab tabName="响应示例" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"statusText": "",
|
||||
"message": "",
|
||||
"data": [
|
||||
{
|
||||
"id": "mVlxkz8NfyfU",
|
||||
"nodeId": "448745",
|
||||
"moduleName": "common:core.module.template.work_start",
|
||||
"moduleType": "workflowStart",
|
||||
"runningTime": 0
|
||||
},
|
||||
{
|
||||
"id": "b3FndAdHSobY",
|
||||
"nodeId": "z04w8JXSYjl3",
|
||||
"moduleName": "AI 对话",
|
||||
"moduleType": "chatNode",
|
||||
"runningTime": 1.22,
|
||||
"totalPoints": 0.02475,
|
||||
"model": "GPT-4o-mini",
|
||||
"tokens": 75,
|
||||
"query": "测试",
|
||||
"maxToken": 2000,
|
||||
"historyPreview": [
|
||||
{
|
||||
"obj": "Human",
|
||||
"value": "你好"
|
||||
},
|
||||
{
|
||||
"obj": "AI",
|
||||
"value": "你好!有什么我可以帮助你的吗?"
|
||||
},
|
||||
{
|
||||
"obj": "Human",
|
||||
"value": "测试"
|
||||
},
|
||||
{
|
||||
"obj": "AI",
|
||||
"value": "测试成功!请问你有什么具体的问题或者需要讨论的话题吗?"
|
||||
}
|
||||
],
|
||||
"contextTotalLen": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
|
||||
### 删除对话记录
|
||||
|
||||
{{< tabs tabTotal="3" >}}
|
||||
{{< tab tabName="请求示例" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
```bash
|
||||
curl --location --request DELETE 'http://localhost:3000/api/core/chat/item/delete?contentId={{contentId}}&chatId={{chatId}}&appId={{appId}}' \
|
||||
--header 'Authorization: Bearer {{apikey}}'
|
||||
```
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab tabName="参数说明" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
{{% alert icon=" " context="success" %}}
|
||||
- appId - 应用 Id
|
||||
- chatId - 历史记录 Id
|
||||
- contentId - 对话记录 Id
|
||||
{{% /alert %}}
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab tabName="响应示例" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"statusText": "",
|
||||
"message": "",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
### 点赞 / 取消点赞
|
||||
|
||||
{{< tabs tabTotal="3" >}}
|
||||
{{< tab tabName="请求示例" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
```bash
|
||||
curl --location --request POST 'http://localhost:3000/api/core/chat/feedback/updateUserFeedback' \
|
||||
--header 'Authorization: Bearer {{apikey}}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"appId": "appId",
|
||||
"chatId": "chatId",
|
||||
"dataId": "dataId",
|
||||
"userGoodFeedback": "yes"
|
||||
}'
|
||||
```
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab tabName="参数说明" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
{{% alert icon=" " context="success" %}}
|
||||
- appId - 应用 Id
|
||||
- chatId - 历史记录 Id
|
||||
- dataId - 对话记录 Id
|
||||
- userGoodFeedback - 用户点赞时的信息(可选),取消点赞时不填此参数即可
|
||||
{{% /alert %}}
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab tabName="响应示例" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"statusText": "",
|
||||
"message": "",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
### 点踩 / 取消点踩
|
||||
|
||||
{{< tabs tabTotal="3" >}}
|
||||
{{< tab tabName="请求示例" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
```bash
|
||||
curl --location --request POST 'http://localhost:3000/api/core/chat/feedback/updateUserFeedback' \
|
||||
--header 'Authorization: Bearer {{apikey}}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"appId": "appId",
|
||||
"chatId": "chatId",
|
||||
"dataId": "dataId",
|
||||
"userBadFeedback": "yes"
|
||||
}'
|
||||
```
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab tabName="参数说明" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
{{% alert icon=" " context="success" %}}
|
||||
- appId - 应用 Id
|
||||
- chatId - 历史记录 Id
|
||||
- dataId - 对话记录 Id
|
||||
- userBadFeedback - 用户点踩时的信息(可选),取消点踩时不填此参数即可
|
||||
{{% /alert %}}
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab tabName="响应示例" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"statusText": "",
|
||||
"message": "",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
## 猜你想问
|
||||
|
||||
{{< tabs tabTotal="3" >}}
|
||||
{{< tab tabName="请求示例" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
```bash
|
||||
curl --location --request POST 'http://localhost:3000/api/core/ai/agent/createQuestionGuide' \
|
||||
--header 'Authorization: Bearer {{apikey}}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"messages":[
|
||||
{
|
||||
"role": "user",
|
||||
"content": "你好"
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "你好!有什么我可以帮助你的吗?"
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab tabName="参数说明" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
{{% alert icon=" " context="success" %}}
|
||||
- messages - 对话消息,提供给 AI 的消息记录
|
||||
{{% /alert %}}
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab tabName="响应示例" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"statusText": "",
|
||||
"message": "",
|
||||
"data": [
|
||||
"你对AI有什么看法?",
|
||||
"想了解AI的应用吗?",
|
||||
"你希望AI能做什么?"
|
||||
]
|
||||
}
|
||||
```
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ weight: 853
|
||||
**新例子**
|
||||
|
||||
```bash
|
||||
curl --location --request POST 'https://api.fastgpt.in/api/support/wallet/usage/createTrainingUsage' \
|
||||
curl --location --request POST 'http://localhost:3000/api/support/wallet/usage/createTrainingUsage' \
|
||||
--header 'Authorization: Bearer {{apikey}}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
@@ -34,7 +34,7 @@ curl --location --request POST 'https://api.fastgpt.in/api/support/wallet/usage/
|
||||
**x例子**
|
||||
|
||||
```bash
|
||||
curl --location --request POST 'https://api.fastgpt.in/api/support/wallet/bill/createTrainingBill' \
|
||||
curl --location --request POST 'http://localhost:3000/api/support/wallet/bill/createTrainingBill' \
|
||||
--header 'Authorization: Bearer {{apikey}}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
|
||||
@@ -7,6 +7,15 @@ toc: true
|
||||
weight: 827
|
||||
---
|
||||
|
||||
## 修改商业版环境变量
|
||||
|
||||
增加 oneapi 地址和令牌。
|
||||
|
||||
```
|
||||
OPENAI_BASE_URL=http://oneapi:3000/v1
|
||||
CHAT_API_KEY=sk-fastgpt
|
||||
```
|
||||
|
||||
## 初始化脚本
|
||||
|
||||
从任意终端,发起 1 个 HTTP 请求。其中 {{rootkey}} 替换成环境变量里的 `rootkey`;{{host}} 替换成自己域名
|
||||
|
||||
@@ -7,8 +7,23 @@ toc: true
|
||||
weight: 812
|
||||
---
|
||||
|
||||
## 更新指南
|
||||
|
||||
|
||||
## 更新说明
|
||||
|
||||
1. 新增 - 全局变量支持更多数据类型
|
||||
2. 新增 - FE_DOMAIN 环境变量,配置该环境变量后,上传文件/图片会补全后缀后得到完整地址。(可解决 docx 文件图片链接,有时会无法被模型识别问题)
|
||||
3. 修复 - 文件后缀判断,去除 query 影响。
|
||||
1. 新增 - 全局变量支持数字类型,支持配置默认值和部分输入框参数。
|
||||
2. 新增 - 插件自定义输入,文本输入框、数字输入框、选择框、开关,默认都支持作为变量引用。
|
||||
3. 新增 - FE_DOMAIN 环境变量,配置该环境变量后,上传文件/图片会补全后缀后得到完整地址。(可解决 docx 文件图片链接,有时模型会伪造图片域名)
|
||||
4. 新增 - 工具调用支持使用交互节点
|
||||
5. 新增 - Debug 模式支持输入全局变量
|
||||
6. 新增 - chat OpenAPI 文档
|
||||
7. 新增 - wiki 搜索插件
|
||||
8. 新增 - Google 搜索插件
|
||||
9. 新增 - 数据库连接和操作插件
|
||||
10. 新增 - Cookie 隐私协议提示
|
||||
11. 新增 - HTTP 节点支持 JSONPath 表达式
|
||||
12. 修复 - 文件后缀判断,去除 query 影响。
|
||||
13. 修复 - AI 响应为空时,会造成 LLM 历史记录合并。
|
||||
14. 修复 - 用户交互节点未阻塞流程。
|
||||
15. 修复 - 新建 APP,有时候会导致空指针报错。
|
||||
|
||||
@@ -120,10 +120,12 @@ HTTP 模块会向对应的地址发送一个 `HTTP` 请求,实际操作与 Pos
|
||||
|
||||
### 如何获取返回值
|
||||
|
||||
从图中可以看出,FastGPT可以添加多个返回值,这个返回值并不代表接口的返回值,而是代表`如何解析接口返回值`,可以通过 key 来`提取`接口响应的值。例如:
|
||||
从图中可以看出,FastGPT可以添加多个返回值,这个返回值并不代表接口的返回值,而是代表`如何解析接口返回值`,可以通过 `JSON path` 的语法,来`提取`接口响应的值。
|
||||
|
||||
语法可以参考: https://github.com/JSONPath-Plus/JSONPath?tab=readme-ov-file
|
||||
|
||||
{{< tabs tabTotal="2" >}}
|
||||
{{< tab tabName="接口响应格式" >}}
|
||||
{{< tab tabName="接口响应示例" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
```json
|
||||
@@ -148,23 +150,23 @@ HTTP 模块会向对应的地址发送一个 `HTTP` 请求,实际操作与 Pos
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
{{< tab tabName="FastGPT 转化后的格式" >}}
|
||||
{{< tab tabName="提取示例" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "测试",
|
||||
"data.user": { "name": "xxx", "age": 12 },
|
||||
"data.user.name": "xxx",
|
||||
"data.user.age": 12,
|
||||
"data.list": [ { "name": "xxx", "age": 50 }, [{ "test": 22 }] ],
|
||||
"data.list[0]": { "name": "xxx", "age": 50 },
|
||||
"data.list[0].name": "xxx",
|
||||
"data.list[0].age": 50,
|
||||
"data.list[1]": [ { "test": 22 } ],
|
||||
"data.list[1][0]": { "test": 22 },
|
||||
"data.list[1][0].test": 22,
|
||||
"data.psw": "xxx"
|
||||
"$.message": "测试",
|
||||
"$.data.user": { "name": "xxx", "age": 12 },
|
||||
"$.data.user.name": "xxx",
|
||||
"$.data.user.age": 12,
|
||||
"$.data.list": [ { "name": "xxx", "age": 50 }, [{ "test": 22 }] ],
|
||||
"$.data.list[0]": { "name": "xxx", "age": 50 },
|
||||
"$.data.list[0].name": "xxx",
|
||||
"$.data.list[0].age": 50,
|
||||
"$.data.list[1]": [ { "test": 22 } ],
|
||||
"$.data.list[1][0]": { "test": 22 },
|
||||
"$.data.list[1][0].test": 22,
|
||||
"$.data.psw": "xxx"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -250,4 +252,4 @@ export default async function (ctx: FunctionContext) {
|
||||
|
||||
- [谷歌搜索](/docs/workflow/examples/google_search/)
|
||||
- [发送飞书webhook](/docs/workflow/examples/feishu_webhook/)
|
||||
- [实验室预约(操作数据库)](/docs/workflow/examples/lab_appointment/)
|
||||
- [实验室预约(操作数据库)](/docs/workflow/examples/lab_appointment/)
|
||||
|
||||
@@ -69,6 +69,7 @@ defaultContentLanguageInSubdir = false
|
||||
# twitter = "" # YOUR_TWITTER_ID
|
||||
# instagram = "colinwilson" # YOUR_INSTAGRAM_ID
|
||||
# rss = true # show rss icon with link
|
||||
lark = "https://oss.laf.run/otnvvf-imgs/fastgpt-feishu1.png"
|
||||
wechat = "https://oss.laf.run/htr4n1-images/fastgpt-qr-code.jpg"
|
||||
|
||||
[params.docs] # Parameters for the /docs 'template'
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
{{ $.Scratch.Set "pathName" (printf "%s" (.Site.Params.docs.pathName | default "docs")) }}
|
||||
<!-- social_list -->
|
||||
<!-- change -->
|
||||
{{ $social_params := slice "github" "twitter" "instagram" "rss" "wechat" }}
|
||||
{{ $social_params := slice "github" "twitter" "instagram" "rss" "wechat" "lark" }}
|
||||
{{ range $social_params }}
|
||||
{{ if isset site.Params.social . }}
|
||||
{{ $.Scratch.Add "social_list" (slice .) }}
|
||||
|
||||
@@ -52,7 +52,12 @@
|
||||
{{ $path := printf "images/social/%s.%s" . "svg" }}
|
||||
<li class="list-inline-item mb-0">
|
||||
<!-- change -->
|
||||
<a href="{{ if eq . `rss` }} {{ `index.xml` | absURL }} {{ else if eq . `wechat` }} {{ index site.Params.social . | absURL }} {{ else }} https://{{ . }}.com/{{ index site.Params.social . }} {{ end }}" alt="{{ . }}" rel="noopener noreferrer" target="_blank">
|
||||
<a href="{{ if eq . `rss` }} {{ `index.xml` | absURL }}
|
||||
{{ else if eq . `wechat` }} {{ index site.Params.social . | absURL }}
|
||||
{{ else if eq . `lark` }} {{ index site.Params.social . | absURL }}
|
||||
{{ else }} https://{{ . }}.com/{{ index site.Params.social . }}
|
||||
{{ end }}"
|
||||
alt="{{ . }}" rel="noopener noreferrer" target="_blank">
|
||||
<div class="btn btn-icon btn-default border-0">
|
||||
{{ with resources.Get $path }}
|
||||
{{ .Content | safeHTML }}
|
||||
|
||||
@@ -154,7 +154,7 @@ services:
|
||||
- MILVUS_TOKEN=none
|
||||
# sandbox 地址
|
||||
- SANDBOX_URL=http://sandbox:3000
|
||||
# 前端地址
|
||||
# 前端地址: http://localhost:3000
|
||||
- FE_DOMAIN=
|
||||
# 日志等级: debug, info, warn, error
|
||||
- LOG_LEVEL=info
|
||||
|
||||
@@ -72,8 +72,8 @@ services:
|
||||
# fastgpt
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: ghcr.io/labring/fastgpt-sandbox:4.8.11 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:4.8.11 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.8.11 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.11 # 阿里云
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
@@ -111,7 +111,7 @@ services:
|
||||
- PG_URL=postgresql://username:password@pg:5432/postgres
|
||||
# sandbox 地址
|
||||
- SANDBOX_URL=http://sandbox:3000
|
||||
# 前端地址
|
||||
# 前端地址: http://localhost:3000
|
||||
- FE_DOMAIN=
|
||||
# 日志等级: debug, info, warn, error
|
||||
- LOG_LEVEL=info
|
||||
|
||||
@@ -92,7 +92,7 @@ services:
|
||||
- MILVUS_TOKEN=zilliz_cloud_token
|
||||
# sandbox 地址
|
||||
- SANDBOX_URL=http://sandbox:3000
|
||||
# 前端地址
|
||||
# 前端地址: http://localhost:3000
|
||||
- FE_DOMAIN=
|
||||
# 日志等级: debug, info, warn, error
|
||||
- LOG_LEVEL=info
|
||||
|
||||
@@ -70,10 +70,10 @@ export const uploadMarkdownBase64 = async ({
|
||||
}
|
||||
|
||||
// Remove white space on both sides of the picture
|
||||
const trimReg = /(!\[.*\]\(.*\))\s*/g;
|
||||
if (trimReg.test(rawText)) {
|
||||
rawText = rawText.replace(trimReg, '$1');
|
||||
}
|
||||
// const trimReg = /(!\[.*\]\(.*\))\s*/g;
|
||||
// if (trimReg.test(rawText)) {
|
||||
// rawText = rawText.replace(trimReg, '$1');
|
||||
// }
|
||||
|
||||
return rawText;
|
||||
};
|
||||
|
||||
@@ -33,9 +33,9 @@ export function replaceVariable(text: any, obj: Record<string, string | number>)
|
||||
|
||||
for (const key in obj) {
|
||||
const val = obj[key];
|
||||
if (!['string', 'number'].includes(typeof val)) continue;
|
||||
const formatVal = typeof val === 'object' ? JSON.stringify(val) : String(val);
|
||||
|
||||
text = text.replace(new RegExp(`{{(${key})}}`, 'g'), String(val));
|
||||
text = text.replace(new RegExp(`{{(${key})}}`, 'g'), formatVal);
|
||||
}
|
||||
return text || '';
|
||||
}
|
||||
@@ -63,6 +63,7 @@ export const getNanoid = (size = 12) => {
|
||||
|
||||
return `${firstChar}${randomsStr}`;
|
||||
};
|
||||
export const customNanoid = (str: string, size: number) => customAlphabet(str, size)();
|
||||
|
||||
/* Custom text to reg, need to replace special chats */
|
||||
export const replaceRegChars = (text: string) => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
|
||||
40
packages/global/core/ai/type.d.ts
vendored
40
packages/global/core/ai/type.d.ts
vendored
@@ -4,12 +4,14 @@ import type {
|
||||
ChatCompletionChunk,
|
||||
ChatCompletionMessageParam as SdkChatCompletionMessageParam,
|
||||
ChatCompletionToolMessageParam,
|
||||
ChatCompletionAssistantMessageParam,
|
||||
ChatCompletionContentPart as SdkChatCompletionContentPart,
|
||||
ChatCompletionUserMessageParam as SdkChatCompletionUserMessageParam
|
||||
ChatCompletionUserMessageParam as SdkChatCompletionUserMessageParam,
|
||||
ChatCompletionToolMessageParam as SdkChatCompletionToolMessageParam,
|
||||
ChatCompletionAssistantMessageParam as SdkChatCompletionAssistantMessageParam,
|
||||
ChatCompletionContentPartText
|
||||
} from 'openai/resources';
|
||||
import { ChatMessageTypeEnum } from './constants';
|
||||
import { InteractiveNodeResponseItemType } from '../workflow/template/system/interactive/type';
|
||||
import { WorkflowInteractiveResponseType } from '../workflow/template/system/interactive/type';
|
||||
export * from 'openai/resources';
|
||||
|
||||
// Extension of ChatCompletionMessageParam, Add file url type
|
||||
@@ -22,18 +24,31 @@ export type ChatCompletionContentPartFile = {
|
||||
export type ChatCompletionContentPart =
|
||||
| SdkChatCompletionContentPart
|
||||
| ChatCompletionContentPartFile;
|
||||
type CustomChatCompletionUserMessageParam = {
|
||||
content: string | Array<ChatCompletionContentPart>;
|
||||
type CustomChatCompletionUserMessageParam = Omit<ChatCompletionUserMessageParam, 'content'> & {
|
||||
role: 'user';
|
||||
content: string | Array<ChatCompletionContentPart>;
|
||||
};
|
||||
type CustomChatCompletionToolMessageParam = SdkChatCompletionToolMessageParam & {
|
||||
role: 'tool';
|
||||
name?: string;
|
||||
};
|
||||
type CustomChatCompletionAssistantMessageParam = SdkChatCompletionAssistantMessageParam & {
|
||||
role: 'assistant';
|
||||
interactive?: WorkflowInteractiveResponseType;
|
||||
};
|
||||
|
||||
export type ChatCompletionMessageParam = (
|
||||
| Exclude<SdkChatCompletionMessageParam, SdkChatCompletionUserMessageParam>
|
||||
| Exclude<
|
||||
SdkChatCompletionMessageParam,
|
||||
| SdkChatCompletionUserMessageParam
|
||||
| SdkChatCompletionToolMessageParam
|
||||
| SdkChatCompletionAssistantMessageParam
|
||||
>
|
||||
| CustomChatCompletionUserMessageParam
|
||||
| CustomChatCompletionToolMessageParam
|
||||
| CustomChatCompletionAssistantMessageParam
|
||||
) & {
|
||||
dataId?: string;
|
||||
interactive?: InteractiveNodeResponseItemType;
|
||||
};
|
||||
export type SdkChatCompletionMessageParam = SdkChatCompletionMessageParam;
|
||||
|
||||
@@ -47,11 +62,12 @@ export type ChatCompletionMessageToolCall = ChatCompletionMessageToolCall & {
|
||||
toolName?: string;
|
||||
toolAvatar?: string;
|
||||
};
|
||||
export type ChatCompletionMessageFunctionCall = ChatCompletionAssistantMessageParam.FunctionCall & {
|
||||
id?: string;
|
||||
toolName?: string;
|
||||
toolAvatar?: string;
|
||||
};
|
||||
export type ChatCompletionMessageFunctionCall =
|
||||
SdkChatCompletionAssistantMessageParam.FunctionCall & {
|
||||
id?: string;
|
||||
toolName?: string;
|
||||
toolAvatar?: string;
|
||||
};
|
||||
|
||||
// Stream response
|
||||
export type StreamChatType = Stream<ChatCompletionChunk>;
|
||||
|
||||
17
packages/global/core/app/type.d.ts
vendored
17
packages/global/core/app/type.d.ts
vendored
@@ -13,6 +13,7 @@ import { StoreEdgeItemType } from '../workflow/type/edge';
|
||||
import { PermissionSchemaType, PermissionValueType } from '../../support/permission/type';
|
||||
import { AppPermission } from '../../support/permission/app/controller';
|
||||
import { ParentIdType } from '../../common/parentFolder/type';
|
||||
import { FlowNodeInputTypeEnum } from 'core/workflow/node/constant';
|
||||
|
||||
export type AppSchema = {
|
||||
_id: string;
|
||||
@@ -114,11 +115,19 @@ export type VariableItemType = {
|
||||
id: string;
|
||||
key: string;
|
||||
label: string;
|
||||
type: `${VariableInputEnum}`;
|
||||
type: VariableInputEnum;
|
||||
required: boolean;
|
||||
maxLen: number;
|
||||
enums: { value: string }[];
|
||||
valueType: WorkflowIOValueTypeEnum;
|
||||
description: string;
|
||||
valueType?: WorkflowIOValueTypeEnum;
|
||||
defaultValue?: any;
|
||||
|
||||
// input
|
||||
maxLength?: number;
|
||||
// numberInput
|
||||
max?: number;
|
||||
min?: number;
|
||||
// select
|
||||
enums?: { value: string; label: string }[];
|
||||
};
|
||||
// tts
|
||||
export type AppTTSConfigType = {
|
||||
|
||||
@@ -90,8 +90,9 @@ export const chats2GPTMessages = ({
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const aiResults: ChatCompletionMessageParam[] = [];
|
||||
//AI
|
||||
item.value.forEach((value) => {
|
||||
item.value.forEach((value, i) => {
|
||||
if (value.type === ChatItemValueTypeEnum.tool && value.tools && reserveTool) {
|
||||
const tool_calls: ChatCompletionMessageToolCall[] = [];
|
||||
const toolResponse: ChatCompletionToolMessageParam[] = [];
|
||||
@@ -111,28 +112,56 @@ export const chats2GPTMessages = ({
|
||||
content: tool.response
|
||||
});
|
||||
});
|
||||
results = results
|
||||
.concat({
|
||||
aiResults.push({
|
||||
dataId,
|
||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
||||
tool_calls
|
||||
});
|
||||
aiResults.push(...toolResponse);
|
||||
} else if (
|
||||
value.type === ChatItemValueTypeEnum.text &&
|
||||
typeof value.text?.content === 'string'
|
||||
) {
|
||||
if (!value.text.content && item.value.length > 1) {
|
||||
return;
|
||||
}
|
||||
// Concat text
|
||||
const lastValue = item.value[i - 1];
|
||||
const lastResult = aiResults[aiResults.length - 1];
|
||||
if (
|
||||
lastValue &&
|
||||
lastValue.type === ChatItemValueTypeEnum.text &&
|
||||
typeof lastResult.content === 'string'
|
||||
) {
|
||||
lastResult.content += value.text.content;
|
||||
} else {
|
||||
aiResults.push({
|
||||
dataId,
|
||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
||||
tool_calls
|
||||
})
|
||||
.concat(toolResponse);
|
||||
} else if (value.text?.content) {
|
||||
results.push({
|
||||
dataId,
|
||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
||||
content: value.text.content
|
||||
});
|
||||
content: value.text.content
|
||||
});
|
||||
}
|
||||
} else if (value.type === ChatItemValueTypeEnum.interactive) {
|
||||
results = results.concat({
|
||||
aiResults.push({
|
||||
dataId,
|
||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
||||
interactive: value.interactive,
|
||||
content: ''
|
||||
interactive: value.interactive
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Auto add empty assistant message
|
||||
results = results.concat(
|
||||
aiResults.length > 0
|
||||
? aiResults
|
||||
: [
|
||||
{
|
||||
dataId,
|
||||
role: ChatCompletionRequestMessageRoleEnum.Assistant,
|
||||
content: ''
|
||||
}
|
||||
]
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -215,14 +244,7 @@ export const GPTMessages2Chats = (
|
||||
obj === ChatRoleEnum.AI &&
|
||||
item.role === ChatCompletionRequestMessageRoleEnum.Assistant
|
||||
) {
|
||||
if (item.content && typeof item.content === 'string') {
|
||||
value.push({
|
||||
type: ChatItemValueTypeEnum.text,
|
||||
text: {
|
||||
content: item.content
|
||||
}
|
||||
});
|
||||
} else if (item.tool_calls && reserveTool) {
|
||||
if (item.tool_calls && reserveTool) {
|
||||
// save tool calls
|
||||
const toolCalls = item.tool_calls as ChatCompletionMessageToolCall[];
|
||||
value.push({
|
||||
@@ -278,6 +300,18 @@ export const GPTMessages2Chats = (
|
||||
type: ChatItemValueTypeEnum.interactive,
|
||||
interactive: item.interactive
|
||||
});
|
||||
} else if (typeof item.content === 'string') {
|
||||
const lastValue = value[value.length - 1];
|
||||
if (lastValue && lastValue.type === ChatItemValueTypeEnum.text && lastValue.text) {
|
||||
lastValue.text.content += item.content;
|
||||
} else {
|
||||
value.push({
|
||||
type: ChatItemValueTypeEnum.text,
|
||||
text: {
|
||||
content: item.content
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
2
packages/global/core/chat/api.d.ts
vendored
2
packages/global/core/chat/api.d.ts
vendored
@@ -1,7 +1,7 @@
|
||||
export type UpdateChatFeedbackProps = {
|
||||
appId: string;
|
||||
chatId: string;
|
||||
chatItemId: string;
|
||||
dataId: string;
|
||||
shareId?: string;
|
||||
teamId?: string;
|
||||
teamToken?: string;
|
||||
|
||||
6
packages/global/core/chat/type.d.ts
vendored
6
packages/global/core/chat/type.d.ts
vendored
@@ -15,7 +15,7 @@ import type { AppSchema as AppType } from '@fastgpt/global/core/app/type.d';
|
||||
import { DatasetSearchModeEnum } from '../dataset/constants';
|
||||
import { DispatchNodeResponseType } from '../workflow/runtime/type.d';
|
||||
import { ChatBoxInputType } from '../../../../projects/app/src/components/core/chat/ChatContainer/ChatBox/type';
|
||||
import { InteractiveNodeResponseItemType } from '../workflow/template/system/interactive/type';
|
||||
import { WorkflowInteractiveResponseType } from '../workflow/template/system/interactive/type';
|
||||
|
||||
export type ChatSchema = {
|
||||
_id: string;
|
||||
@@ -73,7 +73,7 @@ export type AIChatItemValueItemType = {
|
||||
content: string;
|
||||
};
|
||||
tools?: ToolModuleResponseItemType[];
|
||||
interactive?: InteractiveNodeResponseItemType;
|
||||
interactive?: WorkflowInteractiveResponseType;
|
||||
};
|
||||
export type AIChatItemType = {
|
||||
obj: ChatRoleEnum.AI;
|
||||
@@ -100,7 +100,7 @@ export type ChatItemSchema = (UserChatItemType | SystemChatItemType | AIChatItem
|
||||
};
|
||||
|
||||
export type AdminFbkType = {
|
||||
dataId: string;
|
||||
feedbackDataId: string;
|
||||
datasetId: string;
|
||||
collectionId: string;
|
||||
q: string;
|
||||
|
||||
@@ -143,3 +143,29 @@ export const getChatSourceByPublishChannel = (publishChannel: PublishChannelEnum
|
||||
return ChatSourceEnum.online;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Merge chat responseData
|
||||
1. Same tool mergeSignId (Interactive tool node)
|
||||
*/
|
||||
export const mergeChatResponseData = (responseDataList: ChatHistoryItemResType[]) => {
|
||||
let lastResponse: ChatHistoryItemResType | undefined = undefined;
|
||||
|
||||
return responseDataList.reduce<ChatHistoryItemResType[]>((acc, curr) => {
|
||||
if (lastResponse && lastResponse.mergeSignId && curr.mergeSignId === lastResponse.mergeSignId) {
|
||||
// 替换 lastResponse
|
||||
const concatResponse: ChatHistoryItemResType = {
|
||||
...curr,
|
||||
runningTime: +((lastResponse.runningTime || 0) + (curr.runningTime || 0)).toFixed(2),
|
||||
totalPoints: (lastResponse.totalPoints || 0) + (curr.totalPoints || 0),
|
||||
childTotalPoints: (lastResponse.childTotalPoints || 0) + (curr.childTotalPoints || 0),
|
||||
toolCallTokens: (lastResponse.toolCallTokens || 0) + (curr.toolCallTokens || 0),
|
||||
toolDetail: [...(lastResponse.toolDetail || []), ...(curr.toolDetail || [])]
|
||||
};
|
||||
return [...acc.slice(0, -1), concatResponse];
|
||||
} else {
|
||||
lastResponse = curr;
|
||||
return [...acc, curr];
|
||||
}
|
||||
}, []);
|
||||
};
|
||||
|
||||
@@ -267,29 +267,51 @@ export enum NodeOutputKeyEnum {
|
||||
export enum VariableInputEnum {
|
||||
input = 'input',
|
||||
textarea = 'textarea',
|
||||
numberInput = 'numberInput',
|
||||
select = 'select',
|
||||
custom = 'custom'
|
||||
}
|
||||
export const variableMap = {
|
||||
export const variableMap: Record<
|
||||
VariableInputEnum,
|
||||
{
|
||||
icon: string;
|
||||
label: string;
|
||||
value: VariableInputEnum;
|
||||
defaultValueType: WorkflowIOValueTypeEnum;
|
||||
description?: string;
|
||||
}
|
||||
> = {
|
||||
[VariableInputEnum.input]: {
|
||||
icon: 'core/app/variable/input',
|
||||
title: i18nT('common:core.module.variable.input type'),
|
||||
desc: ''
|
||||
icon: 'core/workflow/inputType/input',
|
||||
label: i18nT('common:core.workflow.inputType.textInput'),
|
||||
value: VariableInputEnum.input,
|
||||
defaultValueType: WorkflowIOValueTypeEnum.string
|
||||
},
|
||||
[VariableInputEnum.textarea]: {
|
||||
icon: 'core/app/variable/textarea',
|
||||
title: i18nT('common:core.module.variable.textarea type'),
|
||||
desc: i18nT('app:variable.textarea_type_desc')
|
||||
icon: 'core/workflow/inputType/textarea',
|
||||
label: i18nT('common:core.workflow.inputType.textarea'),
|
||||
value: VariableInputEnum.textarea,
|
||||
defaultValueType: WorkflowIOValueTypeEnum.string,
|
||||
description: i18nT('app:variable.textarea_type_desc')
|
||||
},
|
||||
[VariableInputEnum.numberInput]: {
|
||||
icon: 'core/workflow/inputType/numberInput',
|
||||
label: i18nT('common:core.workflow.inputType.number input'),
|
||||
value: VariableInputEnum.numberInput,
|
||||
defaultValueType: WorkflowIOValueTypeEnum.number
|
||||
},
|
||||
[VariableInputEnum.select]: {
|
||||
icon: 'core/app/variable/select',
|
||||
title: i18nT('common:core.module.variable.select type'),
|
||||
desc: ''
|
||||
icon: 'core/workflow/inputType/option',
|
||||
label: i18nT('common:core.workflow.inputType.select'),
|
||||
value: VariableInputEnum.select,
|
||||
defaultValueType: WorkflowIOValueTypeEnum.string
|
||||
},
|
||||
[VariableInputEnum.custom]: {
|
||||
icon: 'core/app/variable/external',
|
||||
title: i18nT('common:core.module.variable.Custom type'),
|
||||
desc: i18nT('app:variable.select type_desc')
|
||||
icon: 'core/workflow/inputType/customVariable',
|
||||
label: i18nT('common:core.workflow.inputType.custom'),
|
||||
value: VariableInputEnum.custom,
|
||||
defaultValueType: WorkflowIOValueTypeEnum.string,
|
||||
description: i18nT('app:variable.select type_desc')
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -3,12 +3,12 @@ import { i18nT } from '../../../../web/i18n/utils';
|
||||
export enum FlowNodeInputTypeEnum { // render ui
|
||||
reference = 'reference', // reference to other node output
|
||||
input = 'input', // one line input
|
||||
textarea = 'textarea',
|
||||
numberInput = 'numberInput',
|
||||
switch = 'switch', // true/false
|
||||
select = 'select',
|
||||
|
||||
// editor
|
||||
textarea = 'textarea',
|
||||
JSONEditor = 'JSONEditor',
|
||||
|
||||
addInputParam = 'addInputParam', // params input
|
||||
@@ -38,9 +38,6 @@ export const FlowNodeInputMap: Record<
|
||||
[FlowNodeInputTypeEnum.reference]: {
|
||||
icon: 'core/workflow/inputType/reference'
|
||||
},
|
||||
[FlowNodeInputTypeEnum.input]: {
|
||||
icon: 'core/workflow/inputType/input'
|
||||
},
|
||||
[FlowNodeInputTypeEnum.numberInput]: {
|
||||
icon: 'core/workflow/inputType/numberInput'
|
||||
},
|
||||
@@ -50,9 +47,6 @@ export const FlowNodeInputMap: Record<
|
||||
[FlowNodeInputTypeEnum.switch]: {
|
||||
icon: 'core/workflow/inputType/switch'
|
||||
},
|
||||
[FlowNodeInputTypeEnum.textarea]: {
|
||||
icon: 'core/workflow/inputType/textarea'
|
||||
},
|
||||
[FlowNodeInputTypeEnum.JSONEditor]: {
|
||||
icon: 'core/workflow/inputType/jsonEditor'
|
||||
},
|
||||
@@ -85,6 +79,12 @@ export const FlowNodeInputMap: Record<
|
||||
},
|
||||
[FlowNodeInputTypeEnum.custom]: {
|
||||
icon: 'core/workflow/inputType/custom'
|
||||
},
|
||||
[FlowNodeInputTypeEnum.input]: {
|
||||
icon: 'core/workflow/inputType/input'
|
||||
},
|
||||
[FlowNodeInputTypeEnum.textarea]: {
|
||||
icon: 'core/workflow/inputType/textarea'
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ export type RuntimeNodeItemType = {
|
||||
intro?: StoreNodeItemType['intro'];
|
||||
flowNodeType: StoreNodeItemType['flowNodeType'];
|
||||
showStatus?: StoreNodeItemType['showStatus'];
|
||||
isEntry?: StoreNodeItemType['isEntry'];
|
||||
isEntry?: boolean;
|
||||
|
||||
inputs: FlowNodeInputItemType[];
|
||||
outputs: FlowNodeOutputItemType[];
|
||||
@@ -108,12 +108,14 @@ export type DispatchNodeResponseType = {
|
||||
customOutputs?: Record<string, any>;
|
||||
nodeInputs?: Record<string, any>;
|
||||
nodeOutputs?: Record<string, any>;
|
||||
mergeSignId?: string;
|
||||
|
||||
// bill
|
||||
tokens?: number;
|
||||
model?: string;
|
||||
contextTotalLen?: number;
|
||||
totalPoints?: number;
|
||||
childTotalPoints?: number;
|
||||
|
||||
// chat
|
||||
temperature?: number;
|
||||
|
||||
@@ -69,7 +69,7 @@ export const initWorkflowEdgeStatus = (
|
||||
histories?: ChatItemType[]
|
||||
): RuntimeEdgeItemType[] => {
|
||||
// If there is a history, use the last interactive value
|
||||
if (!!histories) {
|
||||
if (histories && histories.length > 0) {
|
||||
const memoryEdges = getLastInteractiveValue(histories)?.memoryEdges;
|
||||
|
||||
if (memoryEdges && memoryEdges.length > 0) {
|
||||
@@ -90,7 +90,7 @@ export const getWorkflowEntryNodeIds = (
|
||||
histories?: ChatItemType[]
|
||||
) => {
|
||||
// If there is a history, use the last interactive entry node
|
||||
if (!!histories) {
|
||||
if (histories && histories.length > 0) {
|
||||
const entryNodeIds = getLastInteractiveValue(histories)?.entryNodeIds;
|
||||
|
||||
if (Array.isArray(entryNodeIds) && entryNodeIds.length > 0) {
|
||||
|
||||
@@ -54,6 +54,7 @@ export const AiChatModule: FlowNodeTemplateType = {
|
||||
intro: i18nT('workflow:template.ai_chat_intro'),
|
||||
showStatus: true,
|
||||
isTool: true,
|
||||
courseUrl: '/docs/workflow/modules/ai_chat/',
|
||||
version: '481',
|
||||
inputs: [
|
||||
Input_Template_SettingAiModel,
|
||||
|
||||
@@ -17,7 +17,7 @@ export const AssignedAnswerModule: FlowNodeTemplateType = {
|
||||
avatar: 'core/workflow/template/reply',
|
||||
name: i18nT('workflow:assigned_reply'),
|
||||
intro: i18nT('workflow:intro_assigned_reply'),
|
||||
|
||||
courseUrl: '/docs/workflow/modules/reply/',
|
||||
version: '481',
|
||||
isTool: true,
|
||||
inputs: [
|
||||
|
||||
@@ -31,6 +31,7 @@ export const ClassifyQuestionModule: FlowNodeTemplateType = {
|
||||
intro: i18nT('workflow:intro_question_classification'),
|
||||
showStatus: true,
|
||||
version: '481',
|
||||
courseUrl: '/docs/workflow/modules/question_classify/',
|
||||
inputs: [
|
||||
{
|
||||
...Input_Template_SelectAIModel,
|
||||
|
||||
@@ -26,6 +26,7 @@ export const ContextExtractModule: FlowNodeTemplateType = {
|
||||
intro: i18nT('workflow:intro_text_content_extraction'),
|
||||
showStatus: true,
|
||||
isTool: true,
|
||||
courseUrl: '/docs/workflow/modules/content_extract/',
|
||||
version: '481',
|
||||
inputs: [
|
||||
{
|
||||
|
||||
@@ -17,6 +17,7 @@ export const CustomFeedbackNode: FlowNodeTemplateType = {
|
||||
avatar: 'core/workflow/template/customFeedback',
|
||||
name: i18nT('workflow:custom_feedback'),
|
||||
intro: i18nT('workflow:intro_custom_feedback'),
|
||||
courseUrl: '/docs/workflow/modules/custom_feedback/',
|
||||
version: '486',
|
||||
inputs: [
|
||||
{
|
||||
|
||||
@@ -29,6 +29,7 @@ export const DatasetSearchModule: FlowNodeTemplateType = {
|
||||
intro: Dataset_SEARCH_DESC,
|
||||
showStatus: true,
|
||||
isTool: true,
|
||||
courseUrl: '/docs/workflow/modules/dataset_search/',
|
||||
version: '481',
|
||||
inputs: [
|
||||
{
|
||||
|
||||
@@ -27,6 +27,7 @@ export const HttpNode468: FlowNodeTemplateType = {
|
||||
intro: i18nT('workflow:intro_http_request'),
|
||||
showStatus: true,
|
||||
isTool: true,
|
||||
courseUrl: '/docs/workflow/modules/http/',
|
||||
version: '481',
|
||||
inputs: [
|
||||
{
|
||||
@@ -113,7 +114,9 @@ export const HttpNode468: FlowNodeTemplateType = {
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
...Output_Template_AddOutput
|
||||
...Output_Template_AddOutput,
|
||||
label: i18nT('workflow:http_extract_output'),
|
||||
description: i18nT('workflow:http_extract_output_description')
|
||||
},
|
||||
{
|
||||
id: NodeOutputKeyEnum.error,
|
||||
|
||||
@@ -23,6 +23,7 @@ export const IfElseNode: FlowNodeTemplateType = {
|
||||
name: i18nT('workflow:condition_checker'),
|
||||
intro: i18nT('workflow:execute_different_branches_based_on_conditions'),
|
||||
showStatus: true,
|
||||
courseUrl: '/docs/workflow/modules/tfswitch/',
|
||||
version: '481',
|
||||
inputs: [
|
||||
{
|
||||
|
||||
@@ -22,7 +22,7 @@ export const FormInputNode: FlowNodeTemplateType = {
|
||||
avatar: 'core/workflow/template/formInput',
|
||||
name: i18nT('app:workflow.form_input'),
|
||||
intro: i18nT(`app:workflow.form_input_tip`),
|
||||
showStatus: true,
|
||||
isTool: true,
|
||||
version: '4811',
|
||||
inputs: [
|
||||
{
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { NodeOutputItemType } from '../../../../chat/type';
|
||||
import { FlowNodeOutputItemType } from '../../../type/io';
|
||||
import { RuntimeEdgeItemType } from '../../../runtime/type';
|
||||
import type { NodeOutputItemType } from '../../../../chat/type';
|
||||
import type { FlowNodeOutputItemType } from '../../../type/io';
|
||||
import type { RuntimeEdgeItemType } from '../../../runtime/type';
|
||||
import { FlowNodeInputTypeEnum } from 'core/workflow/node/constant';
|
||||
import { WorkflowIOValueTypeEnum } from 'core/workflow/constants';
|
||||
import type { ChatCompletionMessageParam } from '../../../../ai/type';
|
||||
|
||||
export type UserSelectOptionItemType = {
|
||||
key: string;
|
||||
@@ -32,6 +33,12 @@ type InteractiveBasicType = {
|
||||
entryNodeIds: string[];
|
||||
memoryEdges: RuntimeEdgeItemType[];
|
||||
nodeOutputs: NodeOutputItemType[];
|
||||
|
||||
toolParams?: {
|
||||
entryNodeIds: string[]; // 记录工具中,交互节点的 Id,而不是起始工作流的入口
|
||||
memoryMessages: ChatCompletionMessageParam[]; // 这轮工具中,产生的新的 messages
|
||||
toolCallId: string; // 记录对应 tool 的id,用于后续交互节点可以替换掉 tool 的 response
|
||||
};
|
||||
};
|
||||
|
||||
type UserSelectInteractive = {
|
||||
@@ -52,5 +59,5 @@ type UserInputInteractive = {
|
||||
};
|
||||
};
|
||||
|
||||
export type InteractiveNodeResponseItemType = InteractiveBasicType &
|
||||
(UserSelectInteractive | UserInputInteractive);
|
||||
export type InteractiveNodeResponseType = UserSelectInteractive | UserInputInteractive;
|
||||
export type WorkflowInteractiveResponseType = InteractiveBasicType & InteractiveNodeResponseType;
|
||||
|
||||
@@ -23,7 +23,7 @@ export const UserSelectNode: FlowNodeTemplateType = {
|
||||
diagram: '/imgs/app/userSelect.svg',
|
||||
name: i18nT('app:workflow.user_select'),
|
||||
intro: i18nT(`app:workflow.user_select_tip`),
|
||||
showStatus: true,
|
||||
isTool: true,
|
||||
version: '489',
|
||||
inputs: [
|
||||
{
|
||||
|
||||
@@ -32,6 +32,7 @@ export const LafModule: FlowNodeTemplateType = {
|
||||
intro: i18nT('workflow:intro_laf_function_call'),
|
||||
showStatus: true,
|
||||
isTool: true,
|
||||
courseUrl: '/docs/workflow/modules/laf/',
|
||||
version: '481',
|
||||
inputs: [
|
||||
{
|
||||
|
||||
@@ -26,6 +26,7 @@ export const CodeNode: FlowNodeTemplateType = {
|
||||
name: i18nT('workflow:code_execution'),
|
||||
intro: i18nT('workflow:execute_a_simple_script_code_usually_for_complex_data_processing'),
|
||||
showStatus: true,
|
||||
courseUrl: '/docs/workflow/modules/sandbox/',
|
||||
version: '482',
|
||||
inputs: [
|
||||
{
|
||||
|
||||
@@ -23,6 +23,7 @@ export const TextEditorNode: FlowNodeTemplateType = {
|
||||
avatar: 'core/workflow/template/textConcat',
|
||||
name: i18nT('workflow:text_concatenation'),
|
||||
intro: i18nT('workflow:intro_text_concatenation'),
|
||||
courseUrl: '/docs/workflow/modules/text_editor/',
|
||||
version: '486',
|
||||
inputs: [
|
||||
{
|
||||
|
||||
@@ -31,6 +31,7 @@ export const ToolModule: FlowNodeTemplateType = {
|
||||
name: i18nT('workflow:template.tool_call'),
|
||||
intro: i18nT('workflow:template.tool_call_intro'),
|
||||
showStatus: true,
|
||||
courseUrl: '/docs/workflow/modules/tool/',
|
||||
version: '481',
|
||||
inputs: [
|
||||
{
|
||||
|
||||
@@ -30,6 +30,7 @@ export const WorkflowStart: FlowNodeTemplateType = {
|
||||
intro: '',
|
||||
forbidDelete: true,
|
||||
unique: true,
|
||||
courseUrl: '/docs/workflow/modules/input/',
|
||||
version: '481',
|
||||
inputs: [{ ...Input_Template_UserChatInput, toolDescription: i18nT('workflow:user_question') }],
|
||||
outputs: [
|
||||
|
||||
@@ -35,7 +35,7 @@ export type WorkflowTemplateType = {
|
||||
avatar: string;
|
||||
intro?: string;
|
||||
author?: string;
|
||||
inputExplanationUrl?: string;
|
||||
courseUrl?: string;
|
||||
version: string;
|
||||
|
||||
showStatus?: boolean;
|
||||
|
||||
2
packages/global/core/workflow/type/node.d.ts
vendored
2
packages/global/core/workflow/type/node.d.ts
vendored
@@ -32,7 +32,6 @@ export type FlowNodeCommonType = {
|
||||
avatar?: string;
|
||||
name: string;
|
||||
intro?: string; // template list intro
|
||||
inputExplanationUrl?: string;
|
||||
showStatus?: boolean; // chatting response step status
|
||||
version: string;
|
||||
|
||||
@@ -69,6 +68,7 @@ export type FlowNodeTemplateType = FlowNodeCommonType & {
|
||||
unique?: boolean;
|
||||
|
||||
diagram?: string; // diagram url
|
||||
courseUrl?: string; // course url
|
||||
};
|
||||
|
||||
export type NodeTemplateListItemType = {
|
||||
|
||||
@@ -230,6 +230,7 @@ export const appData2FlowNodeIO = ({
|
||||
FlowNodeInputTypeEnum.textarea,
|
||||
FlowNodeInputTypeEnum.reference
|
||||
],
|
||||
[VariableInputEnum.numberInput]: [FlowNodeInputTypeEnum.numberInput],
|
||||
[VariableInputEnum.select]: [FlowNodeInputTypeEnum.select],
|
||||
[VariableInputEnum.custom]: [
|
||||
FlowNodeInputTypeEnum.input,
|
||||
@@ -246,7 +247,7 @@ export const appData2FlowNodeIO = ({
|
||||
description: '',
|
||||
valueType: WorkflowIOValueTypeEnum.any,
|
||||
required: item.required,
|
||||
list: item.enums.map((enumItem) => ({
|
||||
list: item.enums?.map((enumItem) => ({
|
||||
label: enumItem.value,
|
||||
value: enumItem.value
|
||||
}))
|
||||
@@ -391,22 +392,25 @@ export function replaceEditorVariable({
|
||||
}
|
||||
];
|
||||
}
|
||||
return [];
|
||||
return [
|
||||
{
|
||||
id: item.key,
|
||||
value: item.value,
|
||||
nodeId: runningNode.nodeId
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
const allVariables = [...globalVariables, ...nodeVariables, ...customInputs];
|
||||
|
||||
// Replace {{$xxx.xxx$}} to value
|
||||
for (const key in allVariables) {
|
||||
const val = allVariables[key];
|
||||
const regex = new RegExp(`\\{\\{\\$(${val.nodeId}\\.${val.id})\\$\\}\\}`, 'g');
|
||||
if (['string', 'number'].includes(typeof val.value)) {
|
||||
text = text.replace(regex, String(val.value));
|
||||
} else if (['object'].includes(typeof val.value)) {
|
||||
text = text.replace(regex, JSON.stringify(val.value));
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
const variable = allVariables[key];
|
||||
const val = variable.value;
|
||||
const formatVal = typeof val === 'object' ? JSON.stringify(val) : String(val);
|
||||
|
||||
const regex = new RegExp(`\\{\\{\\$(${variable.nodeId}\\.${variable.id})\\$\\}\\}`, 'g');
|
||||
text = text.replace(regex, formatVal);
|
||||
}
|
||||
return text || '';
|
||||
}
|
||||
|
||||
@@ -3,11 +3,16 @@
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"duck-duck-scrape": "^2.2.5",
|
||||
"lodash": "^4.17.21",
|
||||
"@types/pg": "^8.6.6",
|
||||
"axios": "^1.5.1",
|
||||
"duck-duck-scrape": "^2.2.5",
|
||||
"echarts": "5.4.1",
|
||||
"expr-eval": "^2.0.2",
|
||||
"echarts": "5.4.1"
|
||||
"lodash": "^4.17.21",
|
||||
"mysql2": "^3.11.3",
|
||||
"json5": "^2.2.3",
|
||||
"pg": "^8.10.0",
|
||||
"wikijs": "^6.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fastgpt/global": "workspace:*",
|
||||
|
||||
@@ -13,7 +13,8 @@ const staticPluginList = [
|
||||
'Doc2X/URLImg2text',
|
||||
`Doc2X/FilePDF2text`,
|
||||
`Doc2X/FileImg2text`,
|
||||
'feishu'
|
||||
'feishu',
|
||||
'google'
|
||||
];
|
||||
// Run in worker thread (Have npm packages)
|
||||
const packagePluginList = [
|
||||
@@ -24,7 +25,9 @@ const packagePluginList = [
|
||||
'duckduckgo/searchNews',
|
||||
'duckduckgo/searchVideo',
|
||||
'drawing',
|
||||
'drawing/baseChart'
|
||||
'drawing/baseChart',
|
||||
'wiki',
|
||||
'databaseConnection'
|
||||
];
|
||||
|
||||
export const list = [...staticPluginList, ...packagePluginList];
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"name": "Doc2X 图像(文件)识别",
|
||||
"avatar": "plugins/doc2x",
|
||||
"intro": "将上传的图片文件发送至Doc2X进行解析,返回带LaTeX公式的markdown格式的文本",
|
||||
"inputExplanationUrl": "https://fael3z0zfze.feishu.cn/wiki/Rkc5witXWiJoi5kORd2cofh6nDg?fromScene=spaceOverview",
|
||||
"courseUrl": "https://fael3z0zfze.feishu.cn/wiki/Rkc5witXWiJoi5kORd2cofh6nDg?fromScene=spaceOverview",
|
||||
"showStatus": true,
|
||||
"weight": 10,
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"name": "Doc2X PDF文件(文件)识别",
|
||||
"avatar": "plugins/doc2x",
|
||||
"intro": "将上传的PDF文件发送至Doc2X进行解析,返回带LaTeX公式的markdown格式的文本",
|
||||
"inputExplanationUrl": "https://fael3z0zfze.feishu.cn/wiki/Rkc5witXWiJoi5kORd2cofh6nDg?fromScene=spaceOverview",
|
||||
"courseUrl": "https://fael3z0zfze.feishu.cn/wiki/Rkc5witXWiJoi5kORd2cofh6nDg?fromScene=spaceOverview",
|
||||
"showStatus": true,
|
||||
"weight": 10,
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"name": "Doc2X 图像(URL)识别",
|
||||
"avatar": "plugins/doc2x",
|
||||
"intro": "从URL下载图片并发送至Doc2X进行解析,返回带LaTeX公式的markdown格式的文本",
|
||||
"inputExplanationUrl": "https://fael3z0zfze.feishu.cn/wiki/Rkc5witXWiJoi5kORd2cofh6nDg?fromScene=spaceOverview",
|
||||
"courseUrl": "https://fael3z0zfze.feishu.cn/wiki/Rkc5witXWiJoi5kORd2cofh6nDg?fromScene=spaceOverview",
|
||||
"showStatus": true,
|
||||
"weight": 10,
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"name": "Doc2X PDF文件(URL)识别",
|
||||
"avatar": "plugins/doc2x",
|
||||
"intro": "从URL下载PDF文件,并发送至Doc2X进行解析,返回带LaTeX公式的markdown格式的文本",
|
||||
"inputExplanationUrl": "https://fael3z0zfze.feishu.cn/wiki/Rkc5witXWiJoi5kORd2cofh6nDg?fromScene=spaceOverview",
|
||||
"courseUrl": "https://fael3z0zfze.feishu.cn/wiki/Rkc5witXWiJoi5kORd2cofh6nDg?fromScene=spaceOverview",
|
||||
"showStatus": true,
|
||||
"weight": 10,
|
||||
|
||||
|
||||
70
packages/plugins/src/databaseConnection/index.ts
Normal file
70
packages/plugins/src/databaseConnection/index.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { Client as PgClient } from 'pg'; // PostgreSQL 客户端
|
||||
import mysql from 'mysql2/promise'; // MySQL 客户端
|
||||
|
||||
type Props = {
|
||||
databaseType: string;
|
||||
host: string;
|
||||
port: string;
|
||||
databaseName: string;
|
||||
user: string;
|
||||
password: string;
|
||||
sql: string;
|
||||
};
|
||||
|
||||
type Response = Promise<{
|
||||
result: any; // 根据你的 SQL 查询结果类型调整
|
||||
}>;
|
||||
|
||||
const main = async ({
|
||||
databaseType,
|
||||
host,
|
||||
port,
|
||||
databaseName,
|
||||
user,
|
||||
password,
|
||||
sql
|
||||
}: Props): Response => {
|
||||
let result;
|
||||
|
||||
try {
|
||||
if (databaseType === 'PostgreSQL') {
|
||||
const client = new PgClient({
|
||||
host,
|
||||
port: parseInt(port, 10),
|
||||
database: databaseName,
|
||||
user,
|
||||
password
|
||||
});
|
||||
|
||||
await client.connect();
|
||||
const res = await client.query(sql);
|
||||
result = res.rows;
|
||||
await client.end();
|
||||
} else if (databaseType === 'MySQL') {
|
||||
const connection = await mysql.createConnection({
|
||||
host,
|
||||
port: parseInt(port, 10),
|
||||
database: databaseName,
|
||||
user,
|
||||
password
|
||||
});
|
||||
|
||||
const [rows] = await connection.execute(sql);
|
||||
result = rows;
|
||||
await connection.end();
|
||||
}
|
||||
return {
|
||||
result
|
||||
};
|
||||
} catch (error: unknown) {
|
||||
// 使用类型断言来处理错误
|
||||
if (error instanceof Error) {
|
||||
console.error('Database query error:', error.message);
|
||||
return Promise.reject(error.message);
|
||||
}
|
||||
console.error('Database query error:', error);
|
||||
return Promise.reject('An unknown error occurred');
|
||||
}
|
||||
};
|
||||
|
||||
export default main;
|
||||
687
packages/plugins/src/databaseConnection/template.json
Normal file
687
packages/plugins/src/databaseConnection/template.json
Normal file
@@ -0,0 +1,687 @@
|
||||
{
|
||||
"author": "",
|
||||
"version": "4811",
|
||||
"name": "数据源配置",
|
||||
"avatar": "core/workflow/template/datasource",
|
||||
"intro": "可连接常用数据库,并执行sql",
|
||||
"showStatus": true,
|
||||
"weight": 10,
|
||||
|
||||
"isTool": true,
|
||||
"templateType": "tools",
|
||||
|
||||
"workflow": {
|
||||
"nodes": [
|
||||
{
|
||||
"nodeId": "pluginInput",
|
||||
"name": "workflow:template.plugin_start",
|
||||
"intro": "workflow:intro_plugin_input",
|
||||
"avatar": "core/workflow/template/workflowStart",
|
||||
"flowNodeType": "pluginInput",
|
||||
"showStatus": false,
|
||||
"position": {
|
||||
"x": 334.24111198705634,
|
||||
"y": -260.8285440670886
|
||||
},
|
||||
"version": "481",
|
||||
"inputs": [
|
||||
{
|
||||
"renderTypeList": ["select", "reference"],
|
||||
"selectedTypeIndex": 0,
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
"key": "databaseType",
|
||||
"label": "databaseType",
|
||||
"description": "数据库的类型",
|
||||
"defaultValue": "",
|
||||
"list": [
|
||||
{
|
||||
"label": "MySQL",
|
||||
"value": "MySQL"
|
||||
},
|
||||
{
|
||||
"label": "PostgreSQL",
|
||||
"value": "PostgreSQL"
|
||||
}
|
||||
],
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"renderTypeList": ["input", "reference"],
|
||||
"selectedTypeIndex": 0,
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
"key": "host",
|
||||
"label": "host",
|
||||
"description": "数据库连接host",
|
||||
"defaultValue": "",
|
||||
"required": true,
|
||||
"list": [
|
||||
{
|
||||
"label": "",
|
||||
"value": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"renderTypeList": ["numberInput", "reference"],
|
||||
"selectedTypeIndex": 0,
|
||||
"valueType": "number",
|
||||
"canEdit": true,
|
||||
"key": "port",
|
||||
"label": "port",
|
||||
"description": "数据库连接端口号",
|
||||
"defaultValue": "",
|
||||
"required": true,
|
||||
"list": [
|
||||
{
|
||||
"label": "",
|
||||
"value": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"renderTypeList": ["input", "reference"],
|
||||
"selectedTypeIndex": 0,
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
"key": "databaseName",
|
||||
"label": "databaseName",
|
||||
"description": "数据库名称",
|
||||
"defaultValue": "",
|
||||
"required": true,
|
||||
"list": [
|
||||
{
|
||||
"label": "",
|
||||
"value": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"renderTypeList": ["input", "reference"],
|
||||
"selectedTypeIndex": 0,
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
"key": "password",
|
||||
"label": "password",
|
||||
"description": "数据库密码",
|
||||
"defaultValue": "",
|
||||
"list": [
|
||||
{
|
||||
"label": "",
|
||||
"value": ""
|
||||
}
|
||||
],
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"renderTypeList": ["input", "reference"],
|
||||
"selectedTypeIndex": 0,
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
"key": "user",
|
||||
"label": "user",
|
||||
"description": "数据库账号",
|
||||
"defaultValue": "",
|
||||
"list": [
|
||||
{
|
||||
"label": "",
|
||||
"value": ""
|
||||
}
|
||||
],
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"renderTypeList": ["input", "reference"],
|
||||
"selectedTypeIndex": 0,
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
"key": "sql",
|
||||
"label": "sql",
|
||||
"description": "sql语句,可以传入sql语句直接执行",
|
||||
"defaultValue": "",
|
||||
"list": [
|
||||
{
|
||||
"label": "",
|
||||
"value": ""
|
||||
}
|
||||
],
|
||||
"required": true,
|
||||
"toolDescription": "sql语句,可以传入sql语句直接执行"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"id": "databaseType",
|
||||
"valueType": "string",
|
||||
"key": "databaseType",
|
||||
"label": "databaseType",
|
||||
"type": "hidden"
|
||||
},
|
||||
{
|
||||
"id": "host",
|
||||
"valueType": "string",
|
||||
"key": "host",
|
||||
"label": "host",
|
||||
"type": "hidden"
|
||||
},
|
||||
{
|
||||
"id": "port",
|
||||
"valueType": "number",
|
||||
"key": "port",
|
||||
"label": "port",
|
||||
"type": "hidden"
|
||||
},
|
||||
{
|
||||
"id": "dataBaseName",
|
||||
"valueType": "string",
|
||||
"key": "databaseName",
|
||||
"label": "databaseName",
|
||||
"type": "hidden"
|
||||
},
|
||||
{
|
||||
"id": "dataBasePwd",
|
||||
"valueType": "string",
|
||||
"key": "password",
|
||||
"label": "password",
|
||||
"type": "hidden"
|
||||
},
|
||||
{
|
||||
"id": "user",
|
||||
"valueType": "string",
|
||||
"key": "user",
|
||||
"label": "user",
|
||||
"type": "hidden"
|
||||
},
|
||||
{
|
||||
"id": "sql",
|
||||
"valueType": "string",
|
||||
"key": "sql",
|
||||
"label": "sql",
|
||||
"type": "hidden"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"nodeId": "pluginOutput",
|
||||
"name": "common:core.module.template.self_output",
|
||||
"intro": "workflow:intro_custom_plugin_output",
|
||||
"avatar": "core/workflow/template/pluginOutput",
|
||||
"flowNodeType": "pluginOutput",
|
||||
"showStatus": false,
|
||||
"position": {
|
||||
"x": 1788.4723692358186,
|
||||
"y": -153.2313912808486
|
||||
},
|
||||
"version": "481",
|
||||
"inputs": [
|
||||
{
|
||||
"renderTypeList": ["reference"],
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
"key": "result",
|
||||
"label": "result",
|
||||
"isToolOutput": true,
|
||||
"description": "数据库连接结果",
|
||||
"value": ["zBeXy7YZEiXe", "httpRawResponse"]
|
||||
}
|
||||
],
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"nodeId": "pluginConfig",
|
||||
"name": "common:core.module.template.system_config",
|
||||
"intro": "",
|
||||
"avatar": "core/workflow/template/systemConfig",
|
||||
"flowNodeType": "pluginConfig",
|
||||
"position": {
|
||||
"x": -133.25818142678844,
|
||||
"y": -200.98784849888733
|
||||
},
|
||||
"version": "4811",
|
||||
"inputs": [],
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"nodeId": "zBeXy7YZEiXe",
|
||||
"name": "数据库连接",
|
||||
"intro": "可以发出一个 HTTP 请求,实现更为复杂的操作(联网搜索、数据库查询等)",
|
||||
"avatar": "core/workflow/template/httpRequest",
|
||||
"flowNodeType": "httpRequest468",
|
||||
"showStatus": true,
|
||||
"position": {
|
||||
"x": 1035.92763304296,
|
||||
"y": -498.57137296107504
|
||||
},
|
||||
"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": "POST",
|
||||
"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": "databaseConnection",
|
||||
"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": [],
|
||||
"label": "",
|
||||
"required": false,
|
||||
"valueDesc": "",
|
||||
"description": "",
|
||||
"debugLabel": "",
|
||||
"toolDescription": ""
|
||||
},
|
||||
{
|
||||
"key": "system_httpJsonBody",
|
||||
"renderTypeList": ["hidden"],
|
||||
"valueType": "any",
|
||||
"value": "{\r\n \"databaseType\":\"{{databaseType-H}}\",\r\n \"host\":\"{{host-H}}\",\r\n \"port\":\"{{port-H}}\",\r\n \"databaseName\":\"{{databaseName-H}}\",\r\n \"user\":\"{{databaseUser-H}}\",\r\n \"password\":\"{{databasePwd-H}}\",\r\n \"sql\":\"{{sql-H}}\"\r\n}",
|
||||
"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": ""
|
||||
},
|
||||
{
|
||||
"renderTypeList": ["reference"],
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
"key": "databaseType-H",
|
||||
"label": "databaseType-H",
|
||||
"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", "databaseType"]
|
||||
},
|
||||
{
|
||||
"renderTypeList": ["reference"],
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
"key": "host-H",
|
||||
"label": "host-H",
|
||||
"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", "host"]
|
||||
},
|
||||
{
|
||||
"renderTypeList": ["reference"],
|
||||
"valueType": "number",
|
||||
"canEdit": true,
|
||||
"key": "port-H",
|
||||
"label": "port-H",
|
||||
"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", "port"]
|
||||
},
|
||||
{
|
||||
"renderTypeList": ["reference"],
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
"key": "databaseName-H",
|
||||
"label": "databaseName-H",
|
||||
"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", "dataBaseName"]
|
||||
},
|
||||
{
|
||||
"renderTypeList": ["reference"],
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
"key": "databasePwd-H",
|
||||
"label": "databasePwd-H",
|
||||
"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", "dataBasePwd"]
|
||||
},
|
||||
{
|
||||
"renderTypeList": ["reference"],
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
"key": "databaseUser-H",
|
||||
"label": "databaseUser-H",
|
||||
"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", "user"]
|
||||
},
|
||||
{
|
||||
"renderTypeList": ["reference"],
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
"key": "sql-H",
|
||||
"label": "sql-H",
|
||||
"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", "sql"]
|
||||
}
|
||||
],
|
||||
"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": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"source": "pluginInput",
|
||||
"target": "zBeXy7YZEiXe",
|
||||
"sourceHandle": "pluginInput-source-right",
|
||||
"targetHandle": "zBeXy7YZEiXe-target-left"
|
||||
},
|
||||
{
|
||||
"source": "zBeXy7YZEiXe",
|
||||
"target": "pluginOutput",
|
||||
"sourceHandle": "zBeXy7YZEiXe-source-right",
|
||||
"targetHandle": "pluginOutput-target-left"
|
||||
}
|
||||
],
|
||||
"chatConfig": {
|
||||
"welcomeText": "",
|
||||
"variables": [],
|
||||
"questionGuide": false,
|
||||
"ttsConfig": {
|
||||
"type": "web"
|
||||
},
|
||||
"whisperConfig": {
|
||||
"open": false,
|
||||
"autoSend": false,
|
||||
"autoTTSResponse": false
|
||||
},
|
||||
"chatInputGuide": {
|
||||
"open": false,
|
||||
"textList": [],
|
||||
"customUrl": ""
|
||||
},
|
||||
"instruction": "数据源配置,支持主流数据库配置",
|
||||
"_id": "670a23b31957c5b9899b4a4d"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
import * as echarts from 'echarts';
|
||||
import json5 from 'json5';
|
||||
import { getFileSavePath } from '../../../utils';
|
||||
import * as fs from 'fs';
|
||||
import { SystemPluginSpecialResponse } from '../../../type.d';
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
@@ -8,7 +12,7 @@ type Props = {
|
||||
};
|
||||
|
||||
type Response = Promise<{
|
||||
result: string;
|
||||
result: SystemPluginSpecialResponse;
|
||||
}>;
|
||||
|
||||
type SeriesData = {
|
||||
@@ -37,8 +41,8 @@ const generateChart = async (title: string, xAxis: string, yAxis: string, chartT
|
||||
let parsedXAxis: string[] = [];
|
||||
let parsedYAxis: number[] = [];
|
||||
try {
|
||||
parsedXAxis = JSON.parse(xAxis);
|
||||
parsedYAxis = JSON.parse(yAxis);
|
||||
parsedXAxis = json5.parse(xAxis);
|
||||
parsedYAxis = json5.parse(yAxis);
|
||||
} catch (error: any) {
|
||||
console.error('解析数据时出错:', error);
|
||||
return Promise.reject('Data error');
|
||||
@@ -78,16 +82,26 @@ const generateChart = async (title: string, xAxis: string, yAxis: string, chartT
|
||||
|
||||
chart.setOption(option);
|
||||
// 生成 Base64 图像
|
||||
const base64Image = chart.getDataURL({ type: 'png' });
|
||||
const base64Image = chart.getDataURL();
|
||||
const svgData = decodeURIComponent(base64Image.split(',')[1]);
|
||||
|
||||
const fileName = `chart_${Date.now()}.svg`;
|
||||
const filePath = getFileSavePath(fileName);
|
||||
fs.writeFileSync(filePath, svgData);
|
||||
// 释放图表实例
|
||||
chart.dispose();
|
||||
|
||||
return base64Image;
|
||||
return filePath;
|
||||
};
|
||||
|
||||
const main = async ({ title, xAxis, yAxis, chartType }: Props): Response => {
|
||||
const filePath = await generateChart(title, xAxis, yAxis, chartType);
|
||||
return {
|
||||
result: await generateChart(title, xAxis, yAxis, chartType)
|
||||
result: {
|
||||
type: 'SYSTEM_PLUGIN_FILE',
|
||||
path: filePath,
|
||||
contentType: 'image/svg+xml'
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"author": "",
|
||||
"version": "486",
|
||||
"version": "4812",
|
||||
"name": "基础图表",
|
||||
"avatar": "core/workflow/template/baseChart",
|
||||
"intro": "根据数据生成图表,可根据chartType生成柱状图,折线图,饼图",
|
||||
@@ -8,7 +8,7 @@
|
||||
"weight": 10,
|
||||
|
||||
"isTool": true,
|
||||
"templateType": "search",
|
||||
"templateType": "tools",
|
||||
|
||||
"workflow": {
|
||||
"nodes": [
|
||||
@@ -21,12 +21,12 @@
|
||||
"showStatus": false,
|
||||
"position": {
|
||||
"x": 613.7921798611637,
|
||||
"y": -124.66724109717275
|
||||
"y": -123.07734867626235
|
||||
},
|
||||
"version": "481",
|
||||
"inputs": [
|
||||
{
|
||||
"renderTypeList": ["reference"],
|
||||
"renderTypeList": ["input", "reference"],
|
||||
"selectedTypeIndex": 0,
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
@@ -44,16 +44,16 @@
|
||||
"toolDescription": "BI图表的标题"
|
||||
},
|
||||
{
|
||||
"renderTypeList": ["reference"],
|
||||
"renderTypeList": ["input", "reference"],
|
||||
"selectedTypeIndex": 0,
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
"key": "xAxis",
|
||||
"label": "xAxis",
|
||||
"description": "x轴数据",
|
||||
"description": "x轴数据,例如:[\"A\", \"B\", \"C\"]",
|
||||
"defaultValue": "",
|
||||
"required": true,
|
||||
"toolDescription": "x轴数据,例如:['A', 'B', 'C']",
|
||||
"toolDescription": "x轴数据,例如:[\"A\", \"B\", \"C\"]",
|
||||
"list": [
|
||||
{
|
||||
"label": "",
|
||||
@@ -62,13 +62,13 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"renderTypeList": ["reference"],
|
||||
"renderTypeList": ["input", "reference"],
|
||||
"selectedTypeIndex": 0,
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
"key": "yAxis",
|
||||
"label": "yAxis",
|
||||
"description": "y轴数据",
|
||||
"description": "y轴数据,例如:['1', '2', '3']",
|
||||
"defaultValue": "",
|
||||
"list": [
|
||||
{
|
||||
@@ -77,10 +77,10 @@
|
||||
}
|
||||
],
|
||||
"required": true,
|
||||
"toolDescription": "y轴数据,例如:['A', 'B', 'C']"
|
||||
"toolDescription": "y轴数据,例如:['1', '2', '3']"
|
||||
},
|
||||
{
|
||||
"renderTypeList": ["select"],
|
||||
"renderTypeList": ["select", "reference"],
|
||||
"selectedTypeIndex": 0,
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
@@ -103,7 +103,7 @@
|
||||
"value": "饼图"
|
||||
}
|
||||
],
|
||||
"toolDescription": "图表类型,目前支持三种: 柱状图,折线图,饼图"
|
||||
"toolDescription": "图表类型:柱状图,折线图,饼图"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
@@ -181,7 +181,7 @@
|
||||
"valueType": "dynamic",
|
||||
"label": "",
|
||||
"required": false,
|
||||
"description": "接收前方节点的输出值作为变量,这些变量可以被 HTTP 请求参数使用。",
|
||||
"description": "common:core.module.input.description.HTTP Dynamic Input",
|
||||
"customInputConfig": {
|
||||
"selectValueTypeList": [
|
||||
"string",
|
||||
@@ -192,6 +192,7 @@
|
||||
"arrayNumber",
|
||||
"arrayBoolean",
|
||||
"arrayObject",
|
||||
"arrayAny",
|
||||
"any",
|
||||
"chatHistory",
|
||||
"datasetQuote",
|
||||
@@ -237,7 +238,7 @@
|
||||
"renderTypeList": ["hidden"],
|
||||
"valueType": "string",
|
||||
"label": "",
|
||||
"description": "新的 HTTP 请求地址。如果出现两个“请求地址”,可以删除该模块重新加入,会拉取最新的模块配置。",
|
||||
"description": "common:core.module.input.description.Http Request Url",
|
||||
"placeholder": "https://api.ai.com/getInventory",
|
||||
"required": false,
|
||||
"valueDesc": "",
|
||||
@@ -251,7 +252,7 @@
|
||||
"valueType": "any",
|
||||
"value": [],
|
||||
"label": "",
|
||||
"description": "自定义请求头,请严格填入 JSON 字符串。\n1. 确保最后一个属性没有逗号\n2. 确保 key 包含双引号\n例如:{\"Authorization\":\"Bearer xxx\"}",
|
||||
"description": "common:core.module.input.description.Http Request Header",
|
||||
"placeholder": "common:core.module.input.description.Http Request Header",
|
||||
"required": false,
|
||||
"valueDesc": "",
|
||||
@@ -322,6 +323,7 @@
|
||||
"arrayNumber",
|
||||
"arrayBoolean",
|
||||
"arrayObject",
|
||||
"arrayAny",
|
||||
"any",
|
||||
"chatHistory",
|
||||
"datasetQuote",
|
||||
@@ -351,6 +353,7 @@
|
||||
"arrayNumber",
|
||||
"arrayBoolean",
|
||||
"arrayObject",
|
||||
"arrayAny",
|
||||
"any",
|
||||
"chatHistory",
|
||||
"datasetQuote",
|
||||
@@ -380,6 +383,7 @@
|
||||
"arrayNumber",
|
||||
"arrayBoolean",
|
||||
"arrayObject",
|
||||
"arrayAny",
|
||||
"any",
|
||||
"chatHistory",
|
||||
"datasetQuote",
|
||||
@@ -409,6 +413,7 @@
|
||||
"arrayNumber",
|
||||
"arrayBoolean",
|
||||
"arrayObject",
|
||||
"arrayAny",
|
||||
"any",
|
||||
"chatHistory",
|
||||
"datasetQuote",
|
||||
@@ -424,6 +429,23 @@
|
||||
}
|
||||
],
|
||||
"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",
|
||||
@@ -453,25 +475,6 @@
|
||||
"valueDesc": "",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"id": "error",
|
||||
"key": "error",
|
||||
"label": "请求错误",
|
||||
"description": "HTTP请求错误信息,成功时返回空",
|
||||
"valueType": "object",
|
||||
"type": "static",
|
||||
"valueDesc": ""
|
||||
},
|
||||
{
|
||||
"id": "httpRawResponse",
|
||||
"key": "httpRawResponse",
|
||||
"required": true,
|
||||
"label": "原始响应",
|
||||
"description": "HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。",
|
||||
"valueType": "any",
|
||||
"type": "static",
|
||||
"valueDesc": ""
|
||||
},
|
||||
{
|
||||
"id": "bzaYjKyQFOw2",
|
||||
"valueType": "string",
|
||||
@@ -495,6 +498,26 @@
|
||||
"sourceHandle": "ws0DFKJnCPhk-source-right",
|
||||
"targetHandle": "pluginOutput-target-left"
|
||||
}
|
||||
]
|
||||
],
|
||||
"chatConfig": {
|
||||
"welcomeText": "",
|
||||
"variables": [],
|
||||
"questionGuide": false,
|
||||
"ttsConfig": {
|
||||
"type": "web"
|
||||
},
|
||||
"whisperConfig": {
|
||||
"open": false,
|
||||
"autoSend": false,
|
||||
"autoTTSResponse": false
|
||||
},
|
||||
"chatInputGuide": {
|
||||
"open": false,
|
||||
"textList": [],
|
||||
"customUrl": ""
|
||||
},
|
||||
"instruction": "数据源配置,支持主流数据库配置",
|
||||
"_id": "670a23b31957c5b9899b4a4d"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"author": "",
|
||||
"author": "silencezhang7",
|
||||
"version": "486",
|
||||
"name": "BI图表功能",
|
||||
"avatar": "core/workflow/template/BI",
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"version": "481",
|
||||
"inputs": [
|
||||
{
|
||||
"renderTypeList": ["reference"],
|
||||
"renderTypeList": ["input", "reference"],
|
||||
"selectedTypeIndex": 0,
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"version": "481",
|
||||
"inputs": [
|
||||
{
|
||||
"renderTypeList": ["reference"],
|
||||
"renderTypeList": ["input", "reference"],
|
||||
"selectedTypeIndex": 0,
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"version": "481",
|
||||
"inputs": [
|
||||
{
|
||||
"renderTypeList": ["reference"],
|
||||
"renderTypeList": ["input", "reference"],
|
||||
"selectedTypeIndex": 0,
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"version": "481",
|
||||
"inputs": [
|
||||
{
|
||||
"renderTypeList": ["reference"],
|
||||
"renderTypeList": ["input", "reference"],
|
||||
"selectedTypeIndex": 0,
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
"avatar": "core/workflow/template/duckduckgo",
|
||||
"intro": "DuckDuckGo 服务,包含网络搜索、图片搜索、新闻搜索等。",
|
||||
"showStatus": false,
|
||||
"weight": 100,
|
||||
"weight": 11,
|
||||
|
||||
"isTool": true,
|
||||
"templateType": "tools",
|
||||
"templateType": "search",
|
||||
|
||||
"workflow": {
|
||||
"nodes": [],
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"name": "飞书机器人 webhook",
|
||||
"avatar": "/appMarketTemplates/plugin-feishu/avatar.svg",
|
||||
"intro": "向飞书机器人发起 webhook 请求。",
|
||||
"inputExplanationUrl": "https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot#f62e72d5",
|
||||
"courseUrl": "https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot#f62e72d5",
|
||||
"showStatus": false,
|
||||
"weight": 10,
|
||||
|
||||
|
||||
559
packages/plugins/src/google/template.json
Normal file
559
packages/plugins/src/google/template.json
Normal file
@@ -0,0 +1,559 @@
|
||||
{
|
||||
"author": "",
|
||||
"version": "4811",
|
||||
"name": "Google搜索",
|
||||
"avatar": "core/workflow/template/google",
|
||||
"intro": "在google中搜索。",
|
||||
"showStatus": true,
|
||||
"weight": 10,
|
||||
|
||||
"isTool": true,
|
||||
"templateType": "search",
|
||||
|
||||
"workflow": {
|
||||
"nodes": [
|
||||
{
|
||||
"nodeId": "pluginInput",
|
||||
"name": "workflow:template.plugin_start",
|
||||
"intro": "workflow:intro_plugin_input",
|
||||
"avatar": "core/workflow/template/workflowStart",
|
||||
"flowNodeType": "pluginInput",
|
||||
"showStatus": false,
|
||||
"position": {
|
||||
"x": 636.3048409085379,
|
||||
"y": -238.61714728578016
|
||||
},
|
||||
"version": "481",
|
||||
"inputs": [
|
||||
{
|
||||
"renderTypeList": ["input", "reference"],
|
||||
"selectedTypeIndex": 0,
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
"key": "cx",
|
||||
"label": "cx",
|
||||
"description": "Google搜索cxID",
|
||||
"defaultValue": "",
|
||||
"list": [
|
||||
{
|
||||
"label": "",
|
||||
"value": ""
|
||||
}
|
||||
],
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"renderTypeList": ["input", "reference"],
|
||||
"selectedTypeIndex": 0,
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
"key": "key",
|
||||
"label": "key",
|
||||
"description": "Google搜索key",
|
||||
"defaultValue": "",
|
||||
"required": true,
|
||||
"list": []
|
||||
},
|
||||
{
|
||||
"renderTypeList": ["input", "reference"],
|
||||
"selectedTypeIndex": 0,
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
"key": "query",
|
||||
"label": "query",
|
||||
"description": "查询字段值",
|
||||
"defaultValue": "",
|
||||
"list": [
|
||||
{
|
||||
"label": "",
|
||||
"value": ""
|
||||
}
|
||||
],
|
||||
"required": true,
|
||||
"toolDescription": "查询字段值"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"id": "cx",
|
||||
"valueType": "string",
|
||||
"key": "cx",
|
||||
"label": "cx",
|
||||
"type": "hidden"
|
||||
},
|
||||
{
|
||||
"id": "key",
|
||||
"valueType": "string",
|
||||
"key": "key",
|
||||
"label": "key",
|
||||
"type": "hidden"
|
||||
},
|
||||
{
|
||||
"id": "query",
|
||||
"valueType": "string",
|
||||
"key": "query",
|
||||
"label": "query",
|
||||
"type": "hidden"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"nodeId": "pluginOutput",
|
||||
"name": "common:core.module.template.self_output",
|
||||
"intro": "workflow:intro_custom_plugin_output",
|
||||
"avatar": "core/workflow/template/pluginOutput",
|
||||
"flowNodeType": "pluginOutput",
|
||||
"showStatus": false,
|
||||
"position": {
|
||||
"x": 2764.1105686698083,
|
||||
"y": -30.617147285780163
|
||||
},
|
||||
"version": "481",
|
||||
"inputs": [
|
||||
{
|
||||
"renderTypeList": ["reference"],
|
||||
"valueType": "object",
|
||||
"canEdit": true,
|
||||
"key": "result",
|
||||
"label": "result",
|
||||
"isToolOutput": true,
|
||||
"description": "",
|
||||
"value": ["pZTkvleFSZXo", "system_rawResponse"]
|
||||
}
|
||||
],
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"nodeId": "pluginConfig",
|
||||
"name": "common:core.module.template.system_config",
|
||||
"intro": "",
|
||||
"avatar": "core/workflow/template/systemConfig",
|
||||
"flowNodeType": "pluginConfig",
|
||||
"position": {
|
||||
"x": 184.66337662472682,
|
||||
"y": -216.05298493910115
|
||||
},
|
||||
"version": "4811",
|
||||
"inputs": [],
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"nodeId": "nyA6oA8mF1iW",
|
||||
"name": "HTTP 请求",
|
||||
"intro": "调用谷歌搜索,查询相关内容",
|
||||
"avatar": "core/workflow/template/httpRequest",
|
||||
"flowNodeType": "httpRequest468",
|
||||
"showStatus": true,
|
||||
"position": {
|
||||
"x": 1335.0647252518884,
|
||||
"y": -455.9043948565971
|
||||
},
|
||||
"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": "GET",
|
||||
"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://www.googleapis.com/customsearch/v1",
|
||||
"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": [
|
||||
{
|
||||
"key": "q",
|
||||
"type": "string",
|
||||
"value": "{{query}}"
|
||||
},
|
||||
{
|
||||
"key": "cx",
|
||||
"type": "string",
|
||||
"value": "{{$pluginInput.cx$}}"
|
||||
},
|
||||
{
|
||||
"key": "key",
|
||||
"type": "string",
|
||||
"value": "{{$pluginInput.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,
|
||||
"debugLabel": "",
|
||||
"toolDescription": ""
|
||||
},
|
||||
{
|
||||
"key": "system_httpJsonBody",
|
||||
"renderTypeList": ["hidden"],
|
||||
"valueType": "any",
|
||||
"value": "",
|
||||
"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": ""
|
||||
},
|
||||
{
|
||||
"valueType": "string",
|
||||
"renderTypeList": ["reference"],
|
||||
"key": "query",
|
||||
"label": "query",
|
||||
"toolDescription": "谷歌搜索检索词",
|
||||
"required": true,
|
||||
"canEdit": true,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"description": true
|
||||
},
|
||||
"customInputConfig": {
|
||||
"selectValueTypeList": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean",
|
||||
"object",
|
||||
"arrayString",
|
||||
"arrayNumber",
|
||||
"arrayBoolean",
|
||||
"arrayObject",
|
||||
"arrayAny",
|
||||
"any",
|
||||
"chatHistory",
|
||||
"datasetQuote",
|
||||
"dynamic",
|
||||
"selectApp",
|
||||
"selectDataset"
|
||||
],
|
||||
"showDescription": false,
|
||||
"showDefaultValue": true
|
||||
},
|
||||
"value": ["pluginInput", "query"]
|
||||
}
|
||||
],
|
||||
"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
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "M5YmxaYe8em1",
|
||||
"type": "dynamic",
|
||||
"key": "prompt",
|
||||
"valueType": "string",
|
||||
"label": "prompt"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"nodeId": "pZTkvleFSZXo",
|
||||
"name": "代码运行",
|
||||
"intro": "执行一段简单的脚本代码,通常用于进行复杂的数据处理。",
|
||||
"avatar": "core/workflow/template/codeRun",
|
||||
"flowNodeType": "code",
|
||||
"showStatus": true,
|
||||
"position": {
|
||||
"x": 2153.5325687235554,
|
||||
"y": -188.04429852303304
|
||||
},
|
||||
"version": "482",
|
||||
"inputs": [
|
||||
{
|
||||
"key": "system_addInputParam",
|
||||
"renderTypeList": ["addInputParam"],
|
||||
"valueType": "dynamic",
|
||||
"label": "",
|
||||
"required": false,
|
||||
"description": "workflow:these_variables_will_be_input_parameters_for_code_execution",
|
||||
"editField": {
|
||||
"key": true,
|
||||
"valueType": true
|
||||
},
|
||||
"customInputConfig": {
|
||||
"selectValueTypeList": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean",
|
||||
"object",
|
||||
"arrayString",
|
||||
"arrayNumber",
|
||||
"arrayBoolean",
|
||||
"arrayObject",
|
||||
"arrayAny",
|
||||
"any",
|
||||
"chatHistory",
|
||||
"datasetQuote",
|
||||
"dynamic",
|
||||
"selectApp",
|
||||
"selectDataset"
|
||||
],
|
||||
"showDescription": false,
|
||||
"showDefaultValue": true
|
||||
},
|
||||
"debugLabel": "",
|
||||
"toolDescription": ""
|
||||
},
|
||||
{
|
||||
"key": "codeType",
|
||||
"renderTypeList": ["hidden"],
|
||||
"label": "",
|
||||
"value": "js",
|
||||
"debugLabel": "",
|
||||
"toolDescription": ""
|
||||
},
|
||||
{
|
||||
"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 JSON.stringify(result) \n}",
|
||||
"debugLabel": "",
|
||||
"toolDescription": ""
|
||||
},
|
||||
{
|
||||
"key": "data",
|
||||
"valueType": "object",
|
||||
"label": "data",
|
||||
"renderTypeList": ["reference"],
|
||||
"description": "",
|
||||
"canEdit": true,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"valueType": true
|
||||
},
|
||||
"value": ["nyA6oA8mF1iW", "httpRawResponse"],
|
||||
"customInputConfig": {
|
||||
"selectValueTypeList": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean",
|
||||
"object",
|
||||
"arrayString",
|
||||
"arrayNumber",
|
||||
"arrayBoolean",
|
||||
"arrayObject",
|
||||
"arrayAny",
|
||||
"any",
|
||||
"chatHistory",
|
||||
"datasetQuote",
|
||||
"dynamic",
|
||||
"selectApp",
|
||||
"selectDataset"
|
||||
],
|
||||
"showDescription": false,
|
||||
"showDefaultValue": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"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": "",
|
||||
"editField": {
|
||||
"key": true,
|
||||
"valueType": true
|
||||
},
|
||||
"description": "将代码中 return 的对象作为输出,传递给后续的节点"
|
||||
},
|
||||
{
|
||||
"id": "qLUQfhG0ILRX",
|
||||
"type": "dynamic",
|
||||
"key": "prompt",
|
||||
"valueType": "string",
|
||||
"label": "prompt"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"source": "pluginInput",
|
||||
"target": "nyA6oA8mF1iW",
|
||||
"sourceHandle": "pluginInput-source-right",
|
||||
"targetHandle": "nyA6oA8mF1iW-target-left"
|
||||
},
|
||||
{
|
||||
"source": "nyA6oA8mF1iW",
|
||||
"target": "pZTkvleFSZXo",
|
||||
"sourceHandle": "nyA6oA8mF1iW-source-right",
|
||||
"targetHandle": "pZTkvleFSZXo-target-left"
|
||||
},
|
||||
{
|
||||
"source": "pZTkvleFSZXo",
|
||||
"target": "pluginOutput",
|
||||
"sourceHandle": "pZTkvleFSZXo-source-right",
|
||||
"targetHandle": "pluginOutput-target-left"
|
||||
}
|
||||
],
|
||||
"chatConfig": {
|
||||
"welcomeText": "",
|
||||
"variables": [],
|
||||
"questionGuide": false,
|
||||
"ttsConfig": {
|
||||
"type": "web"
|
||||
},
|
||||
"whisperConfig": {
|
||||
"open": false,
|
||||
"autoSend": false,
|
||||
"autoTTSResponse": false
|
||||
},
|
||||
"chatInputGuide": {
|
||||
"open": false,
|
||||
"textList": [],
|
||||
"customUrl": ""
|
||||
},
|
||||
"instruction": "",
|
||||
"_id": "6709e90cd9873479ee78fe71"
|
||||
}
|
||||
}
|
||||
}
|
||||
43
packages/plugins/src/wiki/index.ts
Normal file
43
packages/plugins/src/wiki/index.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { addLog } from '@fastgpt/service/common/system/log';
|
||||
import { delay } from '@fastgpt/global/common/system/utils';
|
||||
import wiki from 'wikijs';
|
||||
|
||||
type Props = {
|
||||
query: string;
|
||||
};
|
||||
|
||||
// Response type same as HTTP outputs
|
||||
type Response = Promise<{
|
||||
result: string;
|
||||
}>;
|
||||
|
||||
const main = async (props: Props, retry = 3): Response => {
|
||||
const { query } = props;
|
||||
|
||||
try {
|
||||
const searchResults = await wiki({ apiUrl: 'https://zh.wikipedia.org/w/api.php' })
|
||||
.page(query)
|
||||
.then((page) => {
|
||||
return page.summary();
|
||||
});
|
||||
|
||||
return {
|
||||
result: searchResults
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
if (retry <= 0) {
|
||||
addLog.warn('search wiki error', { error });
|
||||
return {
|
||||
result: getErrText(error, 'Failed to fetch data from wiki')
|
||||
};
|
||||
}
|
||||
|
||||
await delay(Math.random() * 5000);
|
||||
return main(props, retry - 1);
|
||||
}
|
||||
};
|
||||
|
||||
export default main;
|
||||
341
packages/plugins/src/wiki/template.json
Normal file
341
packages/plugins/src/wiki/template.json
Normal file
@@ -0,0 +1,341 @@
|
||||
{
|
||||
"author": "",
|
||||
"version": "4811",
|
||||
"name": "Wiki搜索",
|
||||
"avatar": "core/workflow/template/wiki",
|
||||
"intro": "在Wiki中查询释义。",
|
||||
"showStatus": true,
|
||||
"weight": 10,
|
||||
|
||||
"isTool": true,
|
||||
"templateType": "search",
|
||||
|
||||
"workflow": {
|
||||
"nodes": [
|
||||
{
|
||||
"nodeId": "pluginInput",
|
||||
"name": "插件开始",
|
||||
"intro": "可以配置插件需要哪些输入,利用这些输入来运行插件",
|
||||
"avatar": "core/workflow/template/workflowStart",
|
||||
"flowNodeType": "pluginInput",
|
||||
"showStatus": false,
|
||||
"position": {
|
||||
"x": 484.02074451450517,
|
||||
"y": -79.06127656499825
|
||||
},
|
||||
"version": "481",
|
||||
"inputs": [
|
||||
{
|
||||
"renderTypeList": ["input", "reference"],
|
||||
"selectedTypeIndex": 0,
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
"key": "query",
|
||||
"label": "query",
|
||||
"description": "检索词",
|
||||
"required": true,
|
||||
"toolDescription": "检索词",
|
||||
"list": []
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"id": "query",
|
||||
"valueType": "string",
|
||||
"key": "query",
|
||||
"label": "query",
|
||||
"type": "hidden"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"nodeId": "pluginOutput",
|
||||
"name": "插件输出",
|
||||
"intro": "自定义配置外部输出,使用插件时,仅暴露自定义配置的输出",
|
||||
"avatar": "core/workflow/template/pluginOutput",
|
||||
"flowNodeType": "pluginOutput",
|
||||
"showStatus": false,
|
||||
"position": {
|
||||
"x": 1759.5180706702588,
|
||||
"y": -60.56127656499825
|
||||
},
|
||||
"version": "481",
|
||||
"inputs": [
|
||||
{
|
||||
"renderTypeList": ["reference"],
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
"key": "result",
|
||||
"label": "result",
|
||||
"description": " 检索结果",
|
||||
"value": ["hjnVuJAOwyXV", "lEyy5QqyIBrK"]
|
||||
}
|
||||
],
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"nodeId": "hjnVuJAOwyXV",
|
||||
"name": "HTTP 请求",
|
||||
"intro": "可以发出一个 HTTP 请求,实现更为复杂的操作(联网搜索、数据库查询等)",
|
||||
"avatar": "core/workflow/template/httpRequest",
|
||||
"flowNodeType": "httpRequest468",
|
||||
"showStatus": true,
|
||||
"position": {
|
||||
"x": 1054.6774638324207,
|
||||
"y": -403.06127656499825
|
||||
},
|
||||
"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": "wiki",
|
||||
"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": "{\n \"query\": \"{{query}}\"\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": "query",
|
||||
"label": "query",
|
||||
"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", "query"]
|
||||
}
|
||||
],
|
||||
"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": "lEyy5QqyIBrK",
|
||||
"valueType": "string",
|
||||
"type": "dynamic",
|
||||
"key": "result",
|
||||
"label": "result"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"nodeId": "f1mRh1D85H2D",
|
||||
"name": "系统配置",
|
||||
"intro": "",
|
||||
"avatar": "core/workflow/template/systemConfig",
|
||||
"flowNodeType": "pluginConfig",
|
||||
"position": {
|
||||
"x": -28.511358745511643,
|
||||
"y": -103.56127656499825
|
||||
},
|
||||
"version": "4811",
|
||||
"inputs": [],
|
||||
"outputs": []
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"source": "pluginInput",
|
||||
"target": "hjnVuJAOwyXV",
|
||||
"sourceHandle": "pluginInput-source-right",
|
||||
"targetHandle": "hjnVuJAOwyXV-target-left"
|
||||
},
|
||||
{
|
||||
"source": "hjnVuJAOwyXV",
|
||||
"target": "pluginOutput",
|
||||
"sourceHandle": "hjnVuJAOwyXV-source-right",
|
||||
"targetHandle": "pluginOutput-target-left"
|
||||
}
|
||||
],
|
||||
"chatConfig": {
|
||||
"welcomeText": "",
|
||||
"variables": [],
|
||||
"questionGuide": false,
|
||||
"ttsConfig": {
|
||||
"type": "web"
|
||||
},
|
||||
"whisperConfig": {
|
||||
"open": false,
|
||||
"autoSend": false,
|
||||
"autoTTSResponse": false
|
||||
},
|
||||
"chatInputGuide": {
|
||||
"open": false,
|
||||
"textList": [],
|
||||
"customUrl": ""
|
||||
},
|
||||
"instruction": "",
|
||||
"_id": "67075cd2702bd7168ef8cb2d"
|
||||
}
|
||||
}
|
||||
}
|
||||
6
packages/plugins/type.d.ts
vendored
6
packages/plugins/type.d.ts
vendored
@@ -1,7 +1,13 @@
|
||||
import { PluginTemplateType } from '@fastgpt/global/core/plugin/type.d';
|
||||
import { systemPluginResponseEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import { SystemPluginTemplateItemType } from '@fastgpt/global/core/workflow/type';
|
||||
|
||||
export type SystemPluginResponseType = Promise<Record<string, any>>;
|
||||
export type SystemPluginSpecialResponse = {
|
||||
type: 'SYSTEM_PLUGIN_FILE';
|
||||
path: string;
|
||||
contentType: string;
|
||||
};
|
||||
|
||||
declare global {
|
||||
var systemPlugins: SystemPluginTemplateItemType[];
|
||||
|
||||
15
packages/plugins/utils.ts
Normal file
15
packages/plugins/utils.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import path from 'path';
|
||||
import * as fs from 'fs';
|
||||
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
|
||||
export const getFileSavePath = (name: string) => {
|
||||
if (isProduction) {
|
||||
return `/app/plugin_file/${name}`;
|
||||
}
|
||||
const filePath = path.join(process.cwd(), 'local', 'plugin_file', name);
|
||||
|
||||
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
||||
|
||||
return filePath;
|
||||
};
|
||||
@@ -20,7 +20,7 @@ export const cheerioToHtml = ({
|
||||
const selectDom = $(usedSelector);
|
||||
|
||||
// remove i element
|
||||
selectDom.find('i,script').remove();
|
||||
selectDom.find('i,script,style').remove();
|
||||
|
||||
// remove empty a element
|
||||
selectDom
|
||||
|
||||
@@ -25,7 +25,7 @@ export const countGptMessagesTokens = async (
|
||||
number
|
||||
>({
|
||||
name: WorkerNameEnum.countGptMessagesTokens,
|
||||
maxReservedThreads: global.systemEnv?.tokenWorkers || 20
|
||||
maxReservedThreads: global.systemEnv?.tokenWorkers || 50
|
||||
});
|
||||
|
||||
const total = await workerController.run({ messages, tools, functionCall });
|
||||
|
||||
@@ -4,11 +4,15 @@ import { countGptMessagesTokens } from '../../../common/string/tiktoken/index';
|
||||
import { loadRequestMessages } from '../../chat/utils';
|
||||
import { llmCompletionsBodyFormat } from '../utils';
|
||||
|
||||
export const Prompt_QuestionGuide = `你是一个AI智能助手,你的任务是结合对话记录,推测我下一步的问题。
|
||||
你需要生成 3 个可能的问题,引导我继续提问,生成的问题要求:
|
||||
1. 生成问题的语言,与最后一个用户提问语言一致。
|
||||
2. 问题的长度应小于20个字符。
|
||||
3. 按 JSON 格式返回: ["question1", "question2", "question3"]。`;
|
||||
export const Prompt_QuestionGuide = `You are an AI assistant tasked with predicting the user's next question based on the conversation history. Your goal is to generate 3 potential questions that will guide the user to continue the conversation. When generating these questions, adhere to the following rules:
|
||||
|
||||
1. Use the same language as the user's last question in the conversation history.
|
||||
2. Keep each question under 20 characters in length.
|
||||
3. Return the questions in JSON format: ["question1", "question2", "question3"].
|
||||
|
||||
Analyze the conversation history provided to you and use it as context to generate relevant and engaging follow-up questions. Your predictions should be logical extensions of the current topic or related areas that the user might be interested in exploring further.
|
||||
|
||||
Remember to maintain consistency in tone and style with the existing conversation while providing diverse options for the user to choose from. Your goal is to keep the conversation flowing naturally and help the user delve deeper into the subject matter or explore related topics.`;
|
||||
|
||||
export async function createQuestionGuide({
|
||||
messages,
|
||||
|
||||
@@ -96,7 +96,7 @@ export async function getChildAppPreviewNode({
|
||||
avatar: app.avatar,
|
||||
name: app.name,
|
||||
intro: app.intro,
|
||||
inputExplanationUrl: app.inputExplanationUrl,
|
||||
courseUrl: app.courseUrl,
|
||||
showStatus: app.showStatus,
|
||||
isTool: true,
|
||||
version: app.version,
|
||||
|
||||
@@ -55,22 +55,22 @@ export const adaptStringValue = (value: any): ChatItemValueItemType[] => {
|
||||
export const addCustomFeedbacks = async ({
|
||||
appId,
|
||||
chatId,
|
||||
chatItemId,
|
||||
dataId,
|
||||
feedbacks
|
||||
}: {
|
||||
appId: string;
|
||||
chatId?: string;
|
||||
chatItemId?: string;
|
||||
dataId?: string;
|
||||
feedbacks: string[];
|
||||
}) => {
|
||||
if (!chatId || !chatItemId) return;
|
||||
if (!chatId || !dataId) return;
|
||||
|
||||
try {
|
||||
await MongoChatItem.findOneAndUpdate(
|
||||
{
|
||||
appId,
|
||||
chatId,
|
||||
dataId: chatItemId
|
||||
dataId
|
||||
},
|
||||
{
|
||||
$push: { customFeedbacks: { $each: feedbacks } }
|
||||
|
||||
@@ -12,6 +12,7 @@ import { mongoSessionRun } from '../../common/mongo/sessionRun';
|
||||
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
||||
import { getAppChatConfig, getGuideModule } from '@fastgpt/global/core/workflow/utils';
|
||||
import { AppChatConfigType } from '@fastgpt/global/core/app/type';
|
||||
import { mergeChatResponseData } from '@fastgpt/global/core/chat/utils';
|
||||
|
||||
type Props = {
|
||||
chatId: string;
|
||||
@@ -143,6 +144,7 @@ export const updateInteractiveChat = async ({
|
||||
|
||||
if (!chatItem || chatItem.obj !== ChatRoleEnum.AI) return;
|
||||
|
||||
// Update interactive value
|
||||
const interactiveValue = chatItem.value[chatItem.value.length - 1];
|
||||
|
||||
if (
|
||||
@@ -160,31 +162,36 @@ export const updateInteractiveChat = async ({
|
||||
return userInteractiveVal;
|
||||
}
|
||||
})();
|
||||
interactiveValue.interactive =
|
||||
interactiveValue.interactive.type === 'userSelect'
|
||||
? {
|
||||
...interactiveValue.interactive,
|
||||
params: {
|
||||
...interactiveValue.interactive.params,
|
||||
userSelectedVal: userInteractiveVal
|
||||
}
|
||||
}
|
||||
: {
|
||||
...interactiveValue.interactive,
|
||||
params: {
|
||||
...interactiveValue.interactive.params,
|
||||
inputForm: interactiveValue.interactive.params.inputForm.map((item) => {
|
||||
const itemValue = parsedUserInteractiveVal[item.label];
|
||||
return itemValue !== undefined
|
||||
? {
|
||||
...item,
|
||||
value: itemValue
|
||||
}
|
||||
: item;
|
||||
}),
|
||||
submitted: true
|
||||
}
|
||||
};
|
||||
|
||||
if (interactiveValue.interactive.type === 'userSelect') {
|
||||
interactiveValue.interactive = {
|
||||
...interactiveValue.interactive,
|
||||
params: {
|
||||
...interactiveValue.interactive.params,
|
||||
userSelectedVal: userInteractiveVal
|
||||
}
|
||||
};
|
||||
} else if (
|
||||
interactiveValue.interactive.type === 'userInput' &&
|
||||
typeof parsedUserInteractiveVal === 'object'
|
||||
) {
|
||||
interactiveValue.interactive = {
|
||||
...interactiveValue.interactive,
|
||||
params: {
|
||||
...interactiveValue.interactive.params,
|
||||
inputForm: interactiveValue.interactive.params.inputForm.map((item) => {
|
||||
const itemValue = parsedUserInteractiveVal[item.label];
|
||||
return itemValue !== undefined
|
||||
? {
|
||||
...item,
|
||||
value: itemValue
|
||||
}
|
||||
: item;
|
||||
}),
|
||||
submitted: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (aiResponse.customFeedbacks) {
|
||||
chatItem.customFeedbacks = chatItem.customFeedbacks
|
||||
@@ -194,7 +201,7 @@ export const updateInteractiveChat = async ({
|
||||
|
||||
if (aiResponse.responseData) {
|
||||
chatItem.responseData = chatItem.responseData
|
||||
? [...chatItem.responseData, ...aiResponse.responseData]
|
||||
? mergeChatResponseData([...chatItem.responseData, ...aiResponse.responseData])
|
||||
: aiResponse.responseData;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,17 +11,6 @@ import { serverRequestBaseUrl } from '../../common/api/serverRequest';
|
||||
import { i18nT } from '../../../web/i18n/utils';
|
||||
import { addLog } from '../../common/system/log';
|
||||
|
||||
/* slice chat context by tokens */
|
||||
const filterEmptyMessages = (messages: ChatCompletionMessageParam[]) => {
|
||||
return messages.filter((item) => {
|
||||
if (item.role === ChatCompletionRequestMessageRoleEnum.System) return !!item.content;
|
||||
if (item.role === ChatCompletionRequestMessageRoleEnum.User) return !!item.content;
|
||||
if (item.role === ChatCompletionRequestMessageRoleEnum.Assistant)
|
||||
return !!item.content || !!item.function_call || !!item.tool_calls;
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
export const filterGPTMessageByMaxTokens = async ({
|
||||
messages = [],
|
||||
maxTokens
|
||||
@@ -52,7 +41,7 @@ export const filterGPTMessageByMaxTokens = async ({
|
||||
|
||||
// If the text length is less than half of the maximum token, no calculation is required
|
||||
if (rawTextLen < maxTokens * 0.5) {
|
||||
return filterEmptyMessages(messages);
|
||||
return messages;
|
||||
}
|
||||
|
||||
// filter startWith system prompt
|
||||
@@ -95,7 +84,7 @@ export const filterGPTMessageByMaxTokens = async ({
|
||||
}
|
||||
}
|
||||
|
||||
return filterEmptyMessages([...systemPrompts, ...chats]);
|
||||
return [...systemPrompts, ...chats];
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -215,7 +204,7 @@ export const loadRequestMessages = async ({
|
||||
return;
|
||||
}
|
||||
if (item.role === ChatCompletionRequestMessageRoleEnum.User) {
|
||||
if (!item.content) return;
|
||||
if (item.content === undefined) return;
|
||||
|
||||
if (typeof item.content === 'string') {
|
||||
return {
|
||||
@@ -234,14 +223,7 @@ export const loadRequestMessages = async ({
|
||||
}
|
||||
}
|
||||
if (item.role === ChatCompletionRequestMessageRoleEnum.Assistant) {
|
||||
if (
|
||||
item.content !== undefined &&
|
||||
!item.content &&
|
||||
!item.tool_calls &&
|
||||
!item.function_call
|
||||
)
|
||||
return;
|
||||
if (Array.isArray(item.content) && item.content.length === 0) return;
|
||||
if (item.content === undefined && !item.tool_calls && !item.function_call) return;
|
||||
}
|
||||
|
||||
return item;
|
||||
|
||||
@@ -243,7 +243,7 @@ const toolChoice = async (props: ActionProps) => {
|
||||
const arg: Record<string, any> = (() => {
|
||||
try {
|
||||
return json5.parse(
|
||||
response?.choices?.[0]?.message?.tool_calls?.[0]?.function?.arguments || '{}'
|
||||
response?.choices?.[0]?.message?.tool_calls?.[0]?.function?.arguments || ''
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(agentFunction.parameters);
|
||||
|
||||
@@ -22,10 +22,12 @@ import { DispatchFlowResponse, WorkflowResponseType } from '../../type';
|
||||
import { countGptMessagesTokens } from '../../../../../common/string/tiktoken/index';
|
||||
import { getNanoid, sliceStrStartEnd } from '@fastgpt/global/common/string/tools';
|
||||
import { AIChatItemType } from '@fastgpt/global/core/chat/type';
|
||||
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
||||
import { updateToolInputValue } from './utils';
|
||||
import { chats2GPTMessages, GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
||||
import { formatToolResponse, initToolCallEdges, initToolNodes } from './utils';
|
||||
import { computedMaxToken, llmCompletionsBodyFormat } from '../../../../ai/utils';
|
||||
import { toolValueTypeList } from '@fastgpt/global/core/workflow/constants';
|
||||
import { WorkflowInteractiveResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
||||
|
||||
type FunctionRunResponseType = {
|
||||
toolRunResponse: DispatchFlowResponse;
|
||||
@@ -33,25 +35,107 @@ type FunctionRunResponseType = {
|
||||
}[];
|
||||
|
||||
export const runToolWithFunctionCall = async (
|
||||
props: DispatchToolModuleProps & {
|
||||
messages: ChatCompletionMessageParam[];
|
||||
toolNodes: ToolNodeItemType[];
|
||||
toolModel: LLMModelItemType;
|
||||
},
|
||||
props: DispatchToolModuleProps,
|
||||
response?: RunToolResponse
|
||||
): Promise<RunToolResponse> => {
|
||||
const { messages, toolNodes, toolModel, interactiveEntryToolParams, ...workflowProps } = props;
|
||||
const {
|
||||
toolModel,
|
||||
toolNodes,
|
||||
messages,
|
||||
res,
|
||||
requestOrigin,
|
||||
runtimeNodes,
|
||||
runtimeEdges,
|
||||
node,
|
||||
stream,
|
||||
workflowStreamResponse,
|
||||
params: { temperature = 0, maxToken = 4000, aiChatVision }
|
||||
} = props;
|
||||
} = workflowProps;
|
||||
|
||||
// Interactive
|
||||
if (interactiveEntryToolParams) {
|
||||
initToolNodes(runtimeNodes, interactiveEntryToolParams.entryNodeIds);
|
||||
initToolCallEdges(runtimeEdges, interactiveEntryToolParams.entryNodeIds);
|
||||
|
||||
// Run entry tool
|
||||
const toolRunResponse = await dispatchWorkFlow({
|
||||
...workflowProps,
|
||||
isToolCall: true
|
||||
});
|
||||
const stringToolResponse = formatToolResponse(toolRunResponse.toolResponses);
|
||||
|
||||
workflowStreamResponse?.({
|
||||
event: SseResponseEventEnum.toolResponse,
|
||||
data: {
|
||||
tool: {
|
||||
id: interactiveEntryToolParams.toolCallId,
|
||||
toolName: '',
|
||||
toolAvatar: '',
|
||||
params: '',
|
||||
response: sliceStrStartEnd(stringToolResponse, 5000, 5000)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Check stop signal
|
||||
const hasStopSignal = toolRunResponse.flowResponses?.some((item) => item.toolStop);
|
||||
// Check interactive response(Only 1 interaction is reserved)
|
||||
const workflowInteractiveResponse = toolRunResponse.workflowInteractiveResponse;
|
||||
|
||||
const requestMessages = [
|
||||
...messages,
|
||||
...interactiveEntryToolParams.memoryMessages.map((item) =>
|
||||
!workflowInteractiveResponse &&
|
||||
item.role === 'function' &&
|
||||
item.name === interactiveEntryToolParams.toolCallId
|
||||
? {
|
||||
...item,
|
||||
content: stringToolResponse
|
||||
}
|
||||
: item
|
||||
)
|
||||
];
|
||||
|
||||
if (hasStopSignal || workflowInteractiveResponse) {
|
||||
// Get interactive tool data
|
||||
const toolWorkflowInteractiveResponse: WorkflowInteractiveResponseType | undefined =
|
||||
workflowInteractiveResponse
|
||||
? {
|
||||
...workflowInteractiveResponse,
|
||||
toolParams: {
|
||||
entryNodeIds: workflowInteractiveResponse.entryNodeIds,
|
||||
toolCallId: interactiveEntryToolParams.toolCallId,
|
||||
memoryMessages: [...interactiveEntryToolParams.memoryMessages]
|
||||
}
|
||||
}
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
dispatchFlowResponse: [toolRunResponse],
|
||||
toolNodeTokens: 0,
|
||||
completeMessages: requestMessages,
|
||||
assistantResponses: toolRunResponse.assistantResponses,
|
||||
runTimes: toolRunResponse.runTimes,
|
||||
toolWorkflowInteractiveResponse
|
||||
};
|
||||
}
|
||||
|
||||
return runToolWithFunctionCall(
|
||||
{
|
||||
...props,
|
||||
interactiveEntryToolParams: undefined,
|
||||
// Rewrite toolCall messages
|
||||
messages: requestMessages
|
||||
},
|
||||
{
|
||||
dispatchFlowResponse: [toolRunResponse],
|
||||
toolNodeTokens: 0,
|
||||
assistantResponses: toolRunResponse.assistantResponses,
|
||||
runTimes: toolRunResponse.runTimes
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
|
||||
const assistantResponses = response?.assistantResponses || [];
|
||||
|
||||
const functions: ChatCompletionCreateParams.Function[] = toolNodes.map((item) => {
|
||||
@@ -130,7 +214,7 @@ export const runToolWithFunctionCall = async (
|
||||
toolModel
|
||||
);
|
||||
|
||||
// console.log(JSON.stringify(requestBody, null, 2));
|
||||
// console.log(JSON.stringify(requestMessages, null, 2));
|
||||
/* Run llm */
|
||||
const ai = getAIApi({
|
||||
timeout: 480000
|
||||
@@ -190,30 +274,13 @@ export const runToolWithFunctionCall = async (
|
||||
}
|
||||
})();
|
||||
|
||||
initToolNodes(runtimeNodes, [toolNode.nodeId], startParams);
|
||||
const toolRunResponse = await dispatchWorkFlow({
|
||||
...props,
|
||||
isToolCall: true,
|
||||
runtimeNodes: runtimeNodes.map((item) =>
|
||||
item.nodeId === toolNode.nodeId
|
||||
? {
|
||||
...item,
|
||||
isEntry: true,
|
||||
inputs: updateToolInputValue({ params: startParams, inputs: item.inputs })
|
||||
}
|
||||
: {
|
||||
...item,
|
||||
isEntry: false
|
||||
}
|
||||
)
|
||||
...workflowProps,
|
||||
isToolCall: true
|
||||
});
|
||||
|
||||
const stringToolResponse = (() => {
|
||||
if (typeof toolRunResponse.toolResponses === 'object') {
|
||||
return JSON.stringify(toolRunResponse.toolResponses, null, 2);
|
||||
}
|
||||
|
||||
return toolRunResponse.toolResponses ? String(toolRunResponse.toolResponses) : 'none';
|
||||
})();
|
||||
const stringToolResponse = formatToolResponse(toolRunResponse.toolResponses);
|
||||
|
||||
const functionCallMsg: ChatCompletionFunctionMessageParam = {
|
||||
role: ChatCompletionRequestMessageRoleEnum.Function,
|
||||
@@ -243,6 +310,10 @@ export const runToolWithFunctionCall = async (
|
||||
).filter(Boolean) as FunctionRunResponseType;
|
||||
|
||||
const flatToolsResponseData = toolsRunResponse.map((item) => item.toolRunResponse).flat();
|
||||
// concat tool responses
|
||||
const dispatchFlowResponse = response
|
||||
? response.dispatchFlowResponse.concat(flatToolsResponseData)
|
||||
: flatToolsResponseData;
|
||||
|
||||
const functionCall = functionCalls[0];
|
||||
if (functionCall && !res?.closed) {
|
||||
@@ -274,32 +345,67 @@ export const runToolWithFunctionCall = async (
|
||||
...toolsRunResponse.map((item) => item?.functionCallMsg)
|
||||
];
|
||||
|
||||
// tool node assistant
|
||||
/*
|
||||
Get tool node assistant response
|
||||
history assistant
|
||||
current tool assistant
|
||||
tool child assistant
|
||||
*/
|
||||
const toolNodeAssistant = GPTMessages2Chats([
|
||||
assistantToolMsgParams,
|
||||
...toolsRunResponse.map((item) => item?.functionCallMsg)
|
||||
])[0] as AIChatItemType;
|
||||
const toolChildAssistants = flatToolsResponseData
|
||||
.map((item) => item.assistantResponses)
|
||||
.flat()
|
||||
.filter((item) => item.type !== ChatItemValueTypeEnum.interactive);
|
||||
const toolNodeAssistants = [
|
||||
...assistantResponses,
|
||||
...toolNodeAssistant.value,
|
||||
...toolChildAssistants
|
||||
];
|
||||
|
||||
const toolNodeAssistants = [...assistantResponses, ...toolNodeAssistant.value];
|
||||
const runTimes =
|
||||
(response?.runTimes || 0) +
|
||||
flatToolsResponseData.reduce((sum, item) => sum + item.runTimes, 0);
|
||||
const toolNodeTokens = response?.toolNodeTokens ? response.toolNodeTokens + tokens : tokens;
|
||||
|
||||
// concat tool responses
|
||||
const dispatchFlowResponse = response
|
||||
? response.dispatchFlowResponse.concat(flatToolsResponseData)
|
||||
: flatToolsResponseData;
|
||||
|
||||
/* check stop signal */
|
||||
// Check stop signal
|
||||
const hasStopSignal = flatToolsResponseData.some(
|
||||
(item) => !!item.flowResponses?.find((item) => item.toolStop)
|
||||
);
|
||||
if (hasStopSignal) {
|
||||
// Check interactive response(Only 1 interaction is reserved)
|
||||
const workflowInteractiveResponseItem = toolsRunResponse.find(
|
||||
(item) => item.toolRunResponse.workflowInteractiveResponse
|
||||
);
|
||||
if (hasStopSignal || workflowInteractiveResponseItem) {
|
||||
// Get interactive tool data
|
||||
const workflowInteractiveResponse =
|
||||
workflowInteractiveResponseItem?.toolRunResponse.workflowInteractiveResponse;
|
||||
|
||||
// Flashback traverses completeMessages, intercepting messages that know the first user
|
||||
const firstUserIndex = completeMessages.findLastIndex((item) => item.role === 'user');
|
||||
const newMessages = completeMessages.slice(firstUserIndex + 1);
|
||||
|
||||
const toolWorkflowInteractiveResponse: WorkflowInteractiveResponseType | undefined =
|
||||
workflowInteractiveResponse
|
||||
? {
|
||||
...workflowInteractiveResponse,
|
||||
toolParams: {
|
||||
entryNodeIds: workflowInteractiveResponse.entryNodeIds,
|
||||
toolCallId: workflowInteractiveResponseItem?.functionCallMsg.name,
|
||||
memoryMessages: newMessages
|
||||
}
|
||||
}
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
dispatchFlowResponse,
|
||||
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
||||
toolNodeTokens,
|
||||
completeMessages,
|
||||
assistantResponses: toolNodeAssistants,
|
||||
runTimes:
|
||||
(response?.runTimes || 0) +
|
||||
flatToolsResponseData.reduce((sum, item) => sum + item.runTimes, 0)
|
||||
runTimes,
|
||||
toolWorkflowInteractiveResponse
|
||||
};
|
||||
}
|
||||
|
||||
@@ -310,11 +416,9 @@ export const runToolWithFunctionCall = async (
|
||||
},
|
||||
{
|
||||
dispatchFlowResponse,
|
||||
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
||||
toolNodeTokens,
|
||||
assistantResponses: toolNodeAssistants,
|
||||
runTimes:
|
||||
(response?.runTimes || 0) +
|
||||
flatToolsResponseData.reduce((sum, item) => sum + item.runTimes, 0)
|
||||
runTimes
|
||||
}
|
||||
);
|
||||
} else {
|
||||
@@ -332,7 +436,7 @@ export const runToolWithFunctionCall = async (
|
||||
|
||||
return {
|
||||
dispatchFlowResponse: response?.dispatchFlowResponse || [],
|
||||
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
||||
toolNodeTokens: response?.toolNodeTokens ? response.toolNodeTokens + tokens : tokens,
|
||||
completeMessages,
|
||||
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value],
|
||||
runTimes: (response?.runTimes || 0) + 1
|
||||
|
||||
@@ -9,7 +9,7 @@ import { filterToolNodeIdByEdges, getHistories } from '../../utils';
|
||||
import { runToolWithToolChoice } from './toolChoice';
|
||||
import { DispatchToolModuleProps, ToolNodeItemType } from './type.d';
|
||||
import { ChatItemType, UserChatItemValueItemType } from '@fastgpt/global/core/chat/type';
|
||||
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import {
|
||||
GPTMessages2Chats,
|
||||
chatValue2RuntimePrompt,
|
||||
@@ -24,9 +24,11 @@ import { runToolWithPromptCall } from './promptCall';
|
||||
import { replaceVariable } from '@fastgpt/global/common/string/tools';
|
||||
import { getMultiplePrompt, Prompt_Tool_Call } from './constants';
|
||||
import { filterToolResponseToPreview } from './utils';
|
||||
import { InteractiveNodeResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||
|
||||
type Response = DispatchNodeResultType<{
|
||||
[NodeOutputKeyEnum.answerText]: string;
|
||||
[DispatchNodeResponseKeyEnum.interactive]?: InteractiveNodeResponseType;
|
||||
}>;
|
||||
|
||||
/*
|
||||
@@ -64,19 +66,18 @@ export const toolCallMessagesAdapt = ({
|
||||
|
||||
export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<Response> => {
|
||||
const {
|
||||
node: { nodeId, name },
|
||||
node: { nodeId, name, isEntry },
|
||||
runtimeNodes,
|
||||
runtimeEdges,
|
||||
histories,
|
||||
query,
|
||||
|
||||
params: { model, systemPrompt, userChatInput, history = 6 }
|
||||
} = props;
|
||||
|
||||
const toolModel = getLLMModel(model);
|
||||
const chatHistories = getHistories(history, histories);
|
||||
|
||||
/* get tool params */
|
||||
|
||||
const toolNodeIds = filterToolNodeIdByEdges({ nodeId, edges: runtimeEdges });
|
||||
|
||||
// Gets the module to which the tool is connected
|
||||
@@ -94,42 +95,66 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
|
||||
};
|
||||
});
|
||||
|
||||
const messages: ChatItemType[] = [
|
||||
...getSystemPrompt_ChatItemType(toolModel.defaultSystemChatPrompt),
|
||||
...getSystemPrompt_ChatItemType(systemPrompt),
|
||||
// Add file input prompt to histories
|
||||
...chatHistories.map((item) => {
|
||||
if (item.obj === ChatRoleEnum.Human) {
|
||||
return {
|
||||
...item,
|
||||
value: toolCallMessagesAdapt({
|
||||
userInput: item.value
|
||||
})
|
||||
};
|
||||
// Check interactive entry
|
||||
const interactiveResponse = (() => {
|
||||
const lastHistory = chatHistories[chatHistories.length - 1];
|
||||
if (isEntry && lastHistory?.obj === ChatRoleEnum.AI) {
|
||||
const lastValue = lastHistory.value[lastHistory.value.length - 1];
|
||||
if (
|
||||
lastValue?.type === ChatItemValueTypeEnum.interactive &&
|
||||
lastValue.interactive?.toolParams
|
||||
) {
|
||||
return lastValue.interactive;
|
||||
}
|
||||
return item;
|
||||
}),
|
||||
{
|
||||
obj: ChatRoleEnum.Human,
|
||||
value: toolCallMessagesAdapt({
|
||||
userInput: runtimePrompt2ChatsValue({
|
||||
text: userChatInput,
|
||||
files: chatValue2RuntimePrompt(query).files
|
||||
})
|
||||
})
|
||||
}
|
||||
];
|
||||
})();
|
||||
props.node.isEntry = false;
|
||||
|
||||
// console.log(JSON.stringify(messages, null, 2));
|
||||
const messages: ChatItemType[] = (() => {
|
||||
const value: ChatItemType[] = [
|
||||
...getSystemPrompt_ChatItemType(toolModel.defaultSystemChatPrompt),
|
||||
...getSystemPrompt_ChatItemType(systemPrompt),
|
||||
// Add file input prompt to histories
|
||||
...chatHistories.map((item) => {
|
||||
if (item.obj === ChatRoleEnum.Human) {
|
||||
return {
|
||||
...item,
|
||||
value: toolCallMessagesAdapt({
|
||||
userInput: item.value
|
||||
})
|
||||
};
|
||||
}
|
||||
return item;
|
||||
}),
|
||||
{
|
||||
obj: ChatRoleEnum.Human,
|
||||
value: toolCallMessagesAdapt({
|
||||
userInput: runtimePrompt2ChatsValue({
|
||||
text: userChatInput,
|
||||
files: chatValue2RuntimePrompt(query).files
|
||||
})
|
||||
})
|
||||
}
|
||||
];
|
||||
if (interactiveResponse) {
|
||||
return value.slice(0, -2);
|
||||
}
|
||||
return value;
|
||||
})();
|
||||
|
||||
const {
|
||||
toolWorkflowInteractiveResponse,
|
||||
dispatchFlowResponse, // tool flow response
|
||||
totalTokens,
|
||||
toolNodeTokens,
|
||||
completeMessages = [], // The actual message sent to AI(just save text)
|
||||
assistantResponses = [], // FastGPT system store assistant.value response
|
||||
runTimes
|
||||
} = await (async () => {
|
||||
const adaptMessages = chats2GPTMessages({ messages, reserveId: false });
|
||||
const adaptMessages = chats2GPTMessages({
|
||||
messages,
|
||||
reserveId: false
|
||||
// reserveTool: !!toolModel.toolChoice
|
||||
});
|
||||
|
||||
if (toolModel.toolChoice) {
|
||||
return runToolWithToolChoice({
|
||||
@@ -137,7 +162,8 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
|
||||
toolNodes,
|
||||
toolModel,
|
||||
maxRunToolTimes: 30,
|
||||
messages: adaptMessages
|
||||
messages: adaptMessages,
|
||||
interactiveEntryToolParams: interactiveResponse?.toolParams
|
||||
});
|
||||
}
|
||||
if (toolModel.functionCall) {
|
||||
@@ -145,7 +171,8 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
|
||||
...props,
|
||||
toolNodes,
|
||||
toolModel,
|
||||
messages: adaptMessages
|
||||
messages: adaptMessages,
|
||||
interactiveEntryToolParams: interactiveResponse?.toolParams
|
||||
});
|
||||
}
|
||||
|
||||
@@ -172,13 +199,14 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
|
||||
...props,
|
||||
toolNodes,
|
||||
toolModel,
|
||||
messages: adaptMessages
|
||||
messages: adaptMessages,
|
||||
interactiveEntryToolParams: interactiveResponse?.toolParams
|
||||
});
|
||||
})();
|
||||
|
||||
const { totalPoints, modelName } = formatModelChars2Points({
|
||||
model,
|
||||
tokens: totalTokens,
|
||||
tokens: toolNodeTokens,
|
||||
modelType: ModelTypeEnum.llm
|
||||
});
|
||||
|
||||
@@ -216,21 +244,24 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
|
||||
[DispatchNodeResponseKeyEnum.assistantResponses]: previewAssistantResponses,
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||
totalPoints: totalPointsUsage,
|
||||
toolCallTokens: totalTokens,
|
||||
toolCallTokens: toolNodeTokens,
|
||||
childTotalPoints: flatUsages.reduce((sum, item) => sum + item.totalPoints, 0),
|
||||
model: modelName,
|
||||
query: userChatInput,
|
||||
historyPreview: getHistoryPreview(GPTMessages2Chats(completeMessages, false), 10000),
|
||||
toolDetail: childToolResponse
|
||||
toolDetail: childToolResponse,
|
||||
mergeSignId: nodeId
|
||||
},
|
||||
[DispatchNodeResponseKeyEnum.nodeDispatchUsages]: [
|
||||
{
|
||||
moduleName: name,
|
||||
totalPoints,
|
||||
model: modelName,
|
||||
tokens: totalTokens
|
||||
tokens: toolNodeTokens
|
||||
},
|
||||
...flatUsages
|
||||
],
|
||||
[DispatchNodeResponseKeyEnum.newVariables]: newVariables
|
||||
[DispatchNodeResponseKeyEnum.newVariables]: newVariables,
|
||||
[DispatchNodeResponseKeyEnum.interactive]: toolWorkflowInteractiveResponse
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
|
||||
import { getAIApi } from '../../../../ai/config';
|
||||
import { filterGPTMessageByMaxTokens, loadRequestMessages } from '../../../../chat/utils';
|
||||
import {
|
||||
@@ -24,10 +23,12 @@ import {
|
||||
} from '@fastgpt/global/common/string/tools';
|
||||
import { AIChatItemType } from '@fastgpt/global/core/chat/type';
|
||||
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
||||
import { updateToolInputValue } from './utils';
|
||||
import { formatToolResponse, initToolCallEdges, initToolNodes } from './utils';
|
||||
import { computedMaxToken, llmCompletionsBodyFormat } from '../../../../ai/utils';
|
||||
import { WorkflowResponseType } from '../../type';
|
||||
import { toolValueTypeList } from '@fastgpt/global/core/workflow/constants';
|
||||
import { WorkflowInteractiveResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
|
||||
|
||||
type FunctionCallCompletion = {
|
||||
id: string;
|
||||
@@ -38,27 +39,105 @@ type FunctionCallCompletion = {
|
||||
};
|
||||
|
||||
const ERROR_TEXT = 'Tool run error';
|
||||
const INTERACTIVE_STOP_SIGNAL = 'INTERACTIVE_STOP_SIGNAL';
|
||||
|
||||
export const runToolWithPromptCall = async (
|
||||
props: DispatchToolModuleProps & {
|
||||
messages: ChatCompletionMessageParam[];
|
||||
toolNodes: ToolNodeItemType[];
|
||||
toolModel: LLMModelItemType;
|
||||
},
|
||||
props: DispatchToolModuleProps,
|
||||
response?: RunToolResponse
|
||||
): Promise<RunToolResponse> => {
|
||||
const { messages, toolNodes, toolModel, interactiveEntryToolParams, ...workflowProps } = props;
|
||||
const {
|
||||
toolModel,
|
||||
toolNodes,
|
||||
messages,
|
||||
res,
|
||||
requestOrigin,
|
||||
runtimeNodes,
|
||||
runtimeEdges,
|
||||
node,
|
||||
stream,
|
||||
workflowStreamResponse,
|
||||
params: { temperature = 0, maxToken = 4000, aiChatVision }
|
||||
} = props;
|
||||
} = workflowProps;
|
||||
|
||||
if (interactiveEntryToolParams) {
|
||||
initToolNodes(runtimeNodes, interactiveEntryToolParams.entryNodeIds);
|
||||
initToolCallEdges(runtimeEdges, interactiveEntryToolParams.entryNodeIds);
|
||||
|
||||
// Run entry tool
|
||||
const toolRunResponse = await dispatchWorkFlow({
|
||||
...workflowProps,
|
||||
isToolCall: true
|
||||
});
|
||||
const stringToolResponse = formatToolResponse(toolRunResponse.toolResponses);
|
||||
|
||||
workflowStreamResponse?.({
|
||||
event: SseResponseEventEnum.toolResponse,
|
||||
data: {
|
||||
tool: {
|
||||
id: interactiveEntryToolParams.toolCallId,
|
||||
toolName: '',
|
||||
toolAvatar: '',
|
||||
params: '',
|
||||
response: sliceStrStartEnd(stringToolResponse, 5000, 5000)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Check interactive response(Only 1 interaction is reserved)
|
||||
const workflowInteractiveResponseItem = toolRunResponse?.workflowInteractiveResponse
|
||||
? toolRunResponse
|
||||
: undefined;
|
||||
|
||||
// Rewrite toolCall messages
|
||||
const concatMessages = [...messages.slice(0, -1), ...interactiveEntryToolParams.memoryMessages];
|
||||
const lastMessage = concatMessages[concatMessages.length - 1];
|
||||
lastMessage.content = workflowInteractiveResponseItem
|
||||
? lastMessage.content
|
||||
: replaceVariable(lastMessage.content, {
|
||||
[INTERACTIVE_STOP_SIGNAL]: stringToolResponse
|
||||
});
|
||||
|
||||
// Check stop signal
|
||||
const hasStopSignal = toolRunResponse.flowResponses.some((item) => !!item.toolStop);
|
||||
if (hasStopSignal || workflowInteractiveResponseItem) {
|
||||
// Get interactive tool data
|
||||
const workflowInteractiveResponse =
|
||||
workflowInteractiveResponseItem?.workflowInteractiveResponse;
|
||||
const toolWorkflowInteractiveResponse: WorkflowInteractiveResponseType | undefined =
|
||||
workflowInteractiveResponse
|
||||
? {
|
||||
...workflowInteractiveResponse,
|
||||
toolParams: {
|
||||
entryNodeIds: workflowInteractiveResponse.entryNodeIds,
|
||||
toolCallId: '',
|
||||
memoryMessages: [lastMessage]
|
||||
}
|
||||
}
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
dispatchFlowResponse: [toolRunResponse],
|
||||
toolNodeTokens: 0,
|
||||
completeMessages: concatMessages,
|
||||
assistantResponses: toolRunResponse.assistantResponses,
|
||||
runTimes: toolRunResponse.runTimes,
|
||||
toolWorkflowInteractiveResponse
|
||||
};
|
||||
}
|
||||
|
||||
return runToolWithPromptCall(
|
||||
{
|
||||
...props,
|
||||
interactiveEntryToolParams: undefined,
|
||||
messages: concatMessages
|
||||
},
|
||||
{
|
||||
dispatchFlowResponse: [toolRunResponse],
|
||||
toolNodeTokens: 0,
|
||||
assistantResponses: toolRunResponse.assistantResponses,
|
||||
runTimes: toolRunResponse.runTimes
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const assistantResponses = response?.assistantResponses || [];
|
||||
|
||||
const toolsPrompt = JSON.stringify(
|
||||
@@ -131,7 +210,7 @@ export const runToolWithPromptCall = async (
|
||||
toolModel
|
||||
);
|
||||
|
||||
// console.log(JSON.stringify(requestBody, null, 2));
|
||||
// console.log(JSON.stringify(requestMessages, null, 2));
|
||||
/* Run llm */
|
||||
const ai = getAIApi({
|
||||
timeout: 480000
|
||||
@@ -199,7 +278,7 @@ export const runToolWithPromptCall = async (
|
||||
|
||||
return {
|
||||
dispatchFlowResponse: response?.dispatchFlowResponse || [],
|
||||
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
||||
toolNodeTokens: response?.toolNodeTokens ? response.toolNodeTokens + tokens : tokens,
|
||||
completeMessages,
|
||||
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value],
|
||||
runTimes: (response?.runTimes || 0) + 1
|
||||
@@ -238,30 +317,13 @@ export const runToolWithPromptCall = async (
|
||||
}
|
||||
});
|
||||
|
||||
const moduleRunResponse = await dispatchWorkFlow({
|
||||
...props,
|
||||
isToolCall: true,
|
||||
runtimeNodes: runtimeNodes.map((item) =>
|
||||
item.nodeId === toolNode.nodeId
|
||||
? {
|
||||
...item,
|
||||
isEntry: true,
|
||||
inputs: updateToolInputValue({ params: startParams, inputs: item.inputs })
|
||||
}
|
||||
: {
|
||||
...item,
|
||||
isEntry: false
|
||||
}
|
||||
)
|
||||
initToolNodes(runtimeNodes, [toolNode.nodeId], startParams);
|
||||
const toolResponse = await dispatchWorkFlow({
|
||||
...workflowProps,
|
||||
isToolCall: true
|
||||
});
|
||||
|
||||
const stringToolResponse = (() => {
|
||||
if (typeof moduleRunResponse.toolResponses === 'object') {
|
||||
return JSON.stringify(moduleRunResponse.toolResponses, null, 2);
|
||||
}
|
||||
|
||||
return moduleRunResponse.toolResponses ? String(moduleRunResponse.toolResponses) : 'none';
|
||||
})();
|
||||
const stringToolResponse = formatToolResponse(toolResponse.toolResponses);
|
||||
|
||||
workflowStreamResponse?.({
|
||||
event: SseResponseEventEnum.toolResponse,
|
||||
@@ -277,7 +339,7 @@ export const runToolWithPromptCall = async (
|
||||
});
|
||||
|
||||
return {
|
||||
moduleRunResponse,
|
||||
toolResponse,
|
||||
toolResponsePrompt: stringToolResponse
|
||||
};
|
||||
})();
|
||||
@@ -317,30 +379,60 @@ export const runToolWithPromptCall = async (
|
||||
assistantToolMsgParams,
|
||||
functionResponseMessage
|
||||
])[0] as AIChatItemType;
|
||||
const toolNodeAssistants = [...assistantResponses, ...toolNodeAssistant.value];
|
||||
const toolChildAssistants = toolsRunResponse.toolResponse.assistantResponses.filter(
|
||||
(item) => item.type !== ChatItemValueTypeEnum.interactive
|
||||
);
|
||||
const toolNodeAssistants = [
|
||||
...assistantResponses,
|
||||
...toolNodeAssistant.value,
|
||||
...toolChildAssistants
|
||||
];
|
||||
|
||||
const dispatchFlowResponse = response
|
||||
? response.dispatchFlowResponse.concat(toolsRunResponse.moduleRunResponse)
|
||||
: [toolsRunResponse.moduleRunResponse];
|
||||
? [...response.dispatchFlowResponse, toolsRunResponse.toolResponse]
|
||||
: [toolsRunResponse.toolResponse];
|
||||
|
||||
// Check interactive response(Only 1 interaction is reserved)
|
||||
const workflowInteractiveResponseItem = toolsRunResponse.toolResponse?.workflowInteractiveResponse
|
||||
? toolsRunResponse.toolResponse
|
||||
: undefined;
|
||||
|
||||
// get the next user prompt
|
||||
lastMessage.content += `${replaceAnswer}
|
||||
TOOL_RESPONSE: """
|
||||
${toolsRunResponse.toolResponsePrompt}
|
||||
${workflowInteractiveResponseItem ? `{{${INTERACTIVE_STOP_SIGNAL}}}` : toolsRunResponse.toolResponsePrompt}
|
||||
"""
|
||||
ANSWER: `;
|
||||
|
||||
/* check stop signal */
|
||||
const hasStopSignal = toolsRunResponse.moduleRunResponse.flowResponses.some(
|
||||
(item) => !!item.toolStop
|
||||
);
|
||||
if (hasStopSignal) {
|
||||
const runTimes = (response?.runTimes || 0) + toolsRunResponse.toolResponse.runTimes;
|
||||
const toolNodeTokens = response?.toolNodeTokens ? response.toolNodeTokens + tokens : tokens;
|
||||
|
||||
// Check stop signal
|
||||
const hasStopSignal = toolsRunResponse.toolResponse.flowResponses.some((item) => !!item.toolStop);
|
||||
|
||||
if (hasStopSignal || workflowInteractiveResponseItem) {
|
||||
// Get interactive tool data
|
||||
const workflowInteractiveResponse =
|
||||
workflowInteractiveResponseItem?.workflowInteractiveResponse;
|
||||
const toolWorkflowInteractiveResponse: WorkflowInteractiveResponseType | undefined =
|
||||
workflowInteractiveResponse
|
||||
? {
|
||||
...workflowInteractiveResponse,
|
||||
toolParams: {
|
||||
entryNodeIds: workflowInteractiveResponse.entryNodeIds,
|
||||
toolCallId: '',
|
||||
memoryMessages: [lastMessage]
|
||||
}
|
||||
}
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
dispatchFlowResponse,
|
||||
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
||||
toolNodeTokens,
|
||||
completeMessages: filterMessages,
|
||||
assistantResponses: toolNodeAssistants,
|
||||
runTimes: (response?.runTimes || 0) + toolsRunResponse.moduleRunResponse.runTimes
|
||||
runTimes,
|
||||
toolWorkflowInteractiveResponse
|
||||
};
|
||||
}
|
||||
|
||||
@@ -351,9 +443,9 @@ ANSWER: `;
|
||||
},
|
||||
{
|
||||
dispatchFlowResponse,
|
||||
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
||||
toolNodeTokens,
|
||||
assistantResponses: toolNodeAssistants,
|
||||
runTimes: (response?.runTimes || 0) + toolsRunResponse.moduleRunResponse.runTimes
|
||||
runTimes
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
|
||||
import { getAIApi } from '../../../../ai/config';
|
||||
import { filterGPTMessageByMaxTokens, loadRequestMessages } from '../../../../chat/utils';
|
||||
import {
|
||||
@@ -22,11 +21,13 @@ import { DispatchFlowResponse, WorkflowResponseType } from '../../type';
|
||||
import { countGptMessagesTokens } from '../../../../../common/string/tiktoken/index';
|
||||
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
||||
import { AIChatItemType } from '@fastgpt/global/core/chat/type';
|
||||
import { updateToolInputValue } from './utils';
|
||||
import { formatToolResponse, initToolCallEdges, initToolNodes } from './utils';
|
||||
import { computedMaxToken, llmCompletionsBodyFormat } from '../../../../ai/utils';
|
||||
import { getNanoid, sliceStrStartEnd } from '@fastgpt/global/common/string/tools';
|
||||
import { addLog } from '../../../../../common/system/log';
|
||||
import { toolValueTypeList } from '@fastgpt/global/core/workflow/constants';
|
||||
import { WorkflowInteractiveResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
|
||||
|
||||
type ToolRunResponseType = {
|
||||
toolRunResponse: DispatchFlowResponse;
|
||||
@@ -34,26 +35,61 @@ type ToolRunResponseType = {
|
||||
}[];
|
||||
|
||||
/*
|
||||
调用思路
|
||||
1. messages 接收发送给AI的消息
|
||||
2. response 记录递归运行结果(累计计算 dispatchFlowResponse, totalTokens和assistantResponses)
|
||||
3. 如果运行工具的话,则需要把工具中的结果累计加到dispatchFlowResponse中。 本次消耗的 token 加到 totalTokens, assistantResponses 记录当前工具运行的内容。
|
||||
调用思路:
|
||||
先Check 是否是交互节点触发
|
||||
|
||||
交互模式:
|
||||
1. 从缓存中获取工作流运行数据
|
||||
2. 运行工作流
|
||||
3. 检测是否有停止信号或交互响应
|
||||
- 无:汇总结果,递归运行工具
|
||||
- 有:缓存结果,结束调用
|
||||
|
||||
非交互模式:
|
||||
1. 组合 tools
|
||||
2. 过滤 messages
|
||||
3. Load request llm messages: system prompt, histories, human question, (assistant responses, tool responses, assistant responses....)
|
||||
4. 请求 LLM 获取结果
|
||||
|
||||
- 有工具调用
|
||||
1. 批量运行工具的工作流,获取结果(工作流原生结果,工具执行结果)
|
||||
2. 合并递归中,所有工具的原生运行结果
|
||||
3. 组合 assistants tool 响应
|
||||
4. 组合本次 request 和 llm response 的 messages,并计算出消耗的 tokens
|
||||
5. 组合本次 request、llm response 和 tool response 结果
|
||||
6. 组合本次的 assistant responses: history assistant + tool assistant + tool child assistant
|
||||
7. 判断是否还有停止信号或交互响应
|
||||
- 无:递归运行工具
|
||||
- 有:缓存结果,结束调用
|
||||
- 无工具调用
|
||||
1. 汇总结果,递归运行工具
|
||||
2. 计算 completeMessages 和 tokens 后返回。
|
||||
|
||||
交互节点额外缓存结果包括:
|
||||
1. 入口的节点 id
|
||||
2. toolCallId: 本次工具调用的 ID,可以找到是调用了哪个工具,入口并不会记录工具的 id
|
||||
3. messages:本次递归中,assistants responses 和 tool responses
|
||||
*/
|
||||
|
||||
export const runToolWithToolChoice = async (
|
||||
props: DispatchToolModuleProps & {
|
||||
messages: ChatCompletionMessageParam[];
|
||||
toolNodes: ToolNodeItemType[];
|
||||
toolModel: LLMModelItemType;
|
||||
maxRunToolTimes: number;
|
||||
},
|
||||
response?: RunToolResponse
|
||||
): Promise<RunToolResponse> => {
|
||||
const { messages, toolNodes, toolModel, maxRunToolTimes, ...workflowProps } = props;
|
||||
const {
|
||||
messages,
|
||||
toolNodes,
|
||||
toolModel,
|
||||
maxRunToolTimes,
|
||||
interactiveEntryToolParams,
|
||||
...workflowProps
|
||||
} = props;
|
||||
const {
|
||||
res,
|
||||
requestOrigin,
|
||||
runtimeNodes,
|
||||
runtimeEdges,
|
||||
stream,
|
||||
workflowStreamResponse,
|
||||
params: { temperature = 0, maxToken = 4000, aiChatVision }
|
||||
@@ -63,6 +99,92 @@ export const runToolWithToolChoice = async (
|
||||
return response;
|
||||
}
|
||||
|
||||
// Interactive
|
||||
if (interactiveEntryToolParams) {
|
||||
initToolNodes(runtimeNodes, interactiveEntryToolParams.entryNodeIds);
|
||||
initToolCallEdges(runtimeEdges, interactiveEntryToolParams.entryNodeIds);
|
||||
|
||||
// Run entry tool
|
||||
const toolRunResponse = await dispatchWorkFlow({
|
||||
...workflowProps,
|
||||
isToolCall: true
|
||||
});
|
||||
const stringToolResponse = formatToolResponse(toolRunResponse.toolResponses);
|
||||
|
||||
// Response to frontend
|
||||
workflowStreamResponse?.({
|
||||
event: SseResponseEventEnum.toolResponse,
|
||||
data: {
|
||||
tool: {
|
||||
id: interactiveEntryToolParams.toolCallId,
|
||||
toolName: '',
|
||||
toolAvatar: '',
|
||||
params: '',
|
||||
response: sliceStrStartEnd(stringToolResponse, 5000, 5000)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Check stop signal
|
||||
const hasStopSignal = toolRunResponse.flowResponses?.some((item) => item.toolStop);
|
||||
// Check interactive response(Only 1 interaction is reserved)
|
||||
const workflowInteractiveResponse = toolRunResponse.workflowInteractiveResponse;
|
||||
|
||||
const requestMessages = [
|
||||
...messages,
|
||||
...interactiveEntryToolParams.memoryMessages.map((item) =>
|
||||
item.role === 'tool' && item.tool_call_id === interactiveEntryToolParams.toolCallId
|
||||
? {
|
||||
...item,
|
||||
content: stringToolResponse
|
||||
}
|
||||
: item
|
||||
)
|
||||
];
|
||||
|
||||
if (hasStopSignal || workflowInteractiveResponse) {
|
||||
// Get interactive tool data
|
||||
const toolWorkflowInteractiveResponse: WorkflowInteractiveResponseType | undefined =
|
||||
workflowInteractiveResponse
|
||||
? {
|
||||
...workflowInteractiveResponse,
|
||||
toolParams: {
|
||||
entryNodeIds: workflowInteractiveResponse.entryNodeIds,
|
||||
toolCallId: interactiveEntryToolParams.toolCallId,
|
||||
memoryMessages: interactiveEntryToolParams.memoryMessages
|
||||
}
|
||||
}
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
dispatchFlowResponse: [toolRunResponse],
|
||||
toolNodeTokens: 0,
|
||||
completeMessages: requestMessages,
|
||||
assistantResponses: toolRunResponse.assistantResponses,
|
||||
runTimes: toolRunResponse.runTimes,
|
||||
toolWorkflowInteractiveResponse
|
||||
};
|
||||
}
|
||||
|
||||
return runToolWithToolChoice(
|
||||
{
|
||||
...props,
|
||||
interactiveEntryToolParams: undefined,
|
||||
maxRunToolTimes: maxRunToolTimes - 1,
|
||||
// Rewrite toolCall messages
|
||||
messages: requestMessages
|
||||
},
|
||||
{
|
||||
dispatchFlowResponse: [toolRunResponse],
|
||||
toolNodeTokens: 0,
|
||||
assistantResponses: toolRunResponse.assistantResponses,
|
||||
runTimes: toolRunResponse.runTimes
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
|
||||
const assistantResponses = response?.assistantResponses || [];
|
||||
|
||||
const tools: ChatCompletionTool[] = toolNodes.map((item) => {
|
||||
@@ -146,7 +268,7 @@ export const runToolWithToolChoice = async (
|
||||
},
|
||||
toolModel
|
||||
);
|
||||
|
||||
// console.log(JSON.stringify(requestMessages, null, 2), '==requestBody');
|
||||
/* Run llm */
|
||||
const ai = getAIApi({
|
||||
timeout: 480000
|
||||
@@ -234,30 +356,13 @@ export const runToolWithToolChoice = async (
|
||||
}
|
||||
})();
|
||||
|
||||
initToolNodes(runtimeNodes, [toolNode.nodeId], startParams);
|
||||
const toolRunResponse = await dispatchWorkFlow({
|
||||
...workflowProps,
|
||||
isToolCall: true,
|
||||
runtimeNodes: runtimeNodes.map((item) =>
|
||||
item.nodeId === toolNode.nodeId
|
||||
? {
|
||||
...item,
|
||||
isEntry: true,
|
||||
inputs: updateToolInputValue({ params: startParams, inputs: item.inputs })
|
||||
}
|
||||
: {
|
||||
...item,
|
||||
isEntry: false
|
||||
}
|
||||
)
|
||||
isToolCall: true
|
||||
});
|
||||
|
||||
const stringToolResponse = (() => {
|
||||
if (typeof toolRunResponse.toolResponses === 'object') {
|
||||
return JSON.stringify(toolRunResponse.toolResponses, null, 2);
|
||||
}
|
||||
|
||||
return toolRunResponse.toolResponses ? String(toolRunResponse.toolResponses) : 'none';
|
||||
})();
|
||||
const stringToolResponse = formatToolResponse(toolRunResponse.toolResponses);
|
||||
|
||||
const toolMsgParams: ChatCompletionToolMessageParam = {
|
||||
tool_call_id: tool.id,
|
||||
@@ -274,7 +379,7 @@ export const runToolWithToolChoice = async (
|
||||
toolName: '',
|
||||
toolAvatar: '',
|
||||
params: '',
|
||||
response: sliceStrStartEnd(stringToolResponse, 2000, 2000)
|
||||
response: sliceStrStartEnd(stringToolResponse, 5000, 5000)
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -288,6 +393,10 @@ export const runToolWithToolChoice = async (
|
||||
).filter(Boolean) as ToolRunResponseType;
|
||||
|
||||
const flatToolsResponseData = toolsRunResponse.map((item) => item.toolRunResponse).flat();
|
||||
// concat tool responses
|
||||
const dispatchFlowResponse = response
|
||||
? response.dispatchFlowResponse.concat(flatToolsResponseData)
|
||||
: flatToolsResponseData;
|
||||
|
||||
if (toolCalls.length > 0 && !res?.closed) {
|
||||
// Run the tool, combine its results, and perform another round of AI calls
|
||||
@@ -329,31 +438,67 @@ export const runToolWithToolChoice = async (
|
||||
...toolsRunResponse.map((item) => item?.toolMsgParams)
|
||||
];
|
||||
|
||||
// Assistant tool response adapt to chatStore
|
||||
/*
|
||||
Get tool node assistant response
|
||||
history assistant
|
||||
current tool assistant
|
||||
tool child assistant
|
||||
*/
|
||||
const toolNodeAssistant = GPTMessages2Chats([
|
||||
...assistantToolMsgParams,
|
||||
...toolsRunResponse.map((item) => item?.toolMsgParams)
|
||||
])[0] as AIChatItemType;
|
||||
const toolNodeAssistants = [...assistantResponses, ...toolNodeAssistant.value];
|
||||
const toolChildAssistants = flatToolsResponseData
|
||||
.map((item) => item.assistantResponses)
|
||||
.flat()
|
||||
.filter((item) => item.type !== ChatItemValueTypeEnum.interactive); // 交互节点留着下次记录
|
||||
const toolNodeAssistants = [
|
||||
...assistantResponses,
|
||||
...toolNodeAssistant.value,
|
||||
...toolChildAssistants
|
||||
];
|
||||
|
||||
// concat tool responses
|
||||
const dispatchFlowResponse = response
|
||||
? response.dispatchFlowResponse.concat(flatToolsResponseData)
|
||||
: flatToolsResponseData;
|
||||
const runTimes =
|
||||
(response?.runTimes || 0) +
|
||||
flatToolsResponseData.reduce((sum, item) => sum + item.runTimes, 0);
|
||||
const toolNodeTokens = response ? response.toolNodeTokens + tokens : tokens;
|
||||
|
||||
/* check stop signal */
|
||||
// Check stop signal
|
||||
const hasStopSignal = flatToolsResponseData.some(
|
||||
(item) => !!item.flowResponses?.find((item) => item.toolStop)
|
||||
);
|
||||
if (hasStopSignal) {
|
||||
// Check interactive response(Only 1 interaction is reserved)
|
||||
const workflowInteractiveResponseItem = toolsRunResponse.find(
|
||||
(item) => item.toolRunResponse.workflowInteractiveResponse
|
||||
);
|
||||
if (hasStopSignal || workflowInteractiveResponseItem) {
|
||||
// Get interactive tool data
|
||||
const workflowInteractiveResponse =
|
||||
workflowInteractiveResponseItem?.toolRunResponse.workflowInteractiveResponse;
|
||||
|
||||
// Flashback traverses completeMessages, intercepting messages that know the first user
|
||||
const firstUserIndex = completeMessages.findLastIndex((item) => item.role === 'user');
|
||||
const newMessages = completeMessages.slice(firstUserIndex + 1);
|
||||
|
||||
const toolWorkflowInteractiveResponse: WorkflowInteractiveResponseType | undefined =
|
||||
workflowInteractiveResponse
|
||||
? {
|
||||
...workflowInteractiveResponse,
|
||||
toolParams: {
|
||||
entryNodeIds: workflowInteractiveResponse.entryNodeIds,
|
||||
toolCallId: workflowInteractiveResponseItem?.toolMsgParams.tool_call_id,
|
||||
memoryMessages: newMessages
|
||||
}
|
||||
}
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
dispatchFlowResponse,
|
||||
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
||||
toolNodeTokens,
|
||||
completeMessages,
|
||||
assistantResponses: toolNodeAssistants,
|
||||
runTimes:
|
||||
(response?.runTimes || 0) +
|
||||
flatToolsResponseData.reduce((sum, item) => sum + item.runTimes, 0)
|
||||
runTimes,
|
||||
toolWorkflowInteractiveResponse
|
||||
};
|
||||
}
|
||||
|
||||
@@ -365,11 +510,9 @@ export const runToolWithToolChoice = async (
|
||||
},
|
||||
{
|
||||
dispatchFlowResponse,
|
||||
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
||||
toolNodeTokens,
|
||||
assistantResponses: toolNodeAssistants,
|
||||
runTimes:
|
||||
(response?.runTimes || 0) +
|
||||
flatToolsResponseData.reduce((sum, item) => sum + item.runTimes, 0)
|
||||
runTimes
|
||||
}
|
||||
);
|
||||
} else {
|
||||
@@ -386,7 +529,7 @@ export const runToolWithToolChoice = async (
|
||||
|
||||
return {
|
||||
dispatchFlowResponse: response?.dispatchFlowResponse || [],
|
||||
totalTokens: response?.totalTokens ? response.totalTokens + tokens : tokens,
|
||||
toolNodeTokens: response ? response.toolNodeTokens + tokens : tokens,
|
||||
completeMessages,
|
||||
assistantResponses: [...assistantResponses, ...toolNodeAssistant.value],
|
||||
runTimes: (response?.runTimes || 0) + 1
|
||||
|
||||
@@ -9,6 +9,8 @@ import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type';
|
||||
import type { DispatchFlowResponse } from '../../type.d';
|
||||
import { AIChatItemValueItemType, ChatItemValueItemType } from '@fastgpt/global/core/chat/type';
|
||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import { WorkflowInteractiveResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||
import { LLMModelItemType } from '@fastgpt/global/core/ai/model';
|
||||
|
||||
export type DispatchToolModuleProps = ModuleDispatchProps<{
|
||||
[NodeInputKeyEnum.history]?: ChatItemType[];
|
||||
@@ -19,13 +21,19 @@ export type DispatchToolModuleProps = ModuleDispatchProps<{
|
||||
[NodeInputKeyEnum.aiChatTemperature]: number;
|
||||
[NodeInputKeyEnum.aiChatMaxToken]: number;
|
||||
[NodeInputKeyEnum.aiChatVision]?: boolean;
|
||||
}>;
|
||||
}> & {
|
||||
messages: ChatCompletionMessageParam[];
|
||||
toolNodes: ToolNodeItemType[];
|
||||
toolModel: LLMModelItemType;
|
||||
interactiveEntryToolParams?: WorkflowInteractiveResponseType['toolParams'];
|
||||
};
|
||||
|
||||
export type RunToolResponse = {
|
||||
dispatchFlowResponse: DispatchFlowResponse[];
|
||||
totalTokens: number;
|
||||
toolNodeTokens: number;
|
||||
completeMessages?: ChatCompletionMessageParam[];
|
||||
assistantResponses?: AIChatItemValueItemType[];
|
||||
toolWorkflowInteractiveResponse?: WorkflowInteractiveResponseType;
|
||||
[DispatchNodeResponseKeyEnum.runTimes]: number;
|
||||
};
|
||||
export type ToolNodeItemType = RuntimeNodeItemType & {
|
||||
|
||||
@@ -2,6 +2,8 @@ import { sliceStrStartEnd } from '@fastgpt/global/common/string/tools';
|
||||
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { AIChatItemValueItemType } from '@fastgpt/global/core/chat/type';
|
||||
import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io';
|
||||
import { RuntimeEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
||||
import { RuntimeNodeItemType } from '@fastgpt/global/core/workflow/runtime/type';
|
||||
|
||||
export const updateToolInputValue = ({
|
||||
params,
|
||||
@@ -34,3 +36,35 @@ export const filterToolResponseToPreview = (response: AIChatItemValueItemType[])
|
||||
return item;
|
||||
});
|
||||
};
|
||||
|
||||
export const formatToolResponse = (toolResponses: any) => {
|
||||
if (typeof toolResponses === 'object') {
|
||||
return JSON.stringify(toolResponses, null, 2);
|
||||
}
|
||||
|
||||
return toolResponses ? String(toolResponses) : 'none';
|
||||
};
|
||||
|
||||
// 在原参上改变值,不修改原对象,tool workflow 中,使用的还是原对象
|
||||
export const initToolCallEdges = (edges: RuntimeEdgeItemType[], entryNodeIds: string[]) => {
|
||||
edges.forEach((edge) => {
|
||||
if (entryNodeIds.includes(edge.target)) {
|
||||
edge.status = 'active';
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const initToolNodes = (
|
||||
nodes: RuntimeNodeItemType[],
|
||||
entryNodeIds: string[],
|
||||
startParams?: Record<string, any>
|
||||
) => {
|
||||
nodes.forEach((node) => {
|
||||
if (entryNodeIds.includes(node.nodeId)) {
|
||||
node.isEntry = true;
|
||||
if (startParams) {
|
||||
node.inputs = updateToolInputValue({ params: startParams, inputs: node.inputs });
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -62,8 +62,8 @@ import { dispatchCustomFeedback } from './tools/customFeedback';
|
||||
import { dispatchReadFiles } from './tools/readFiles';
|
||||
import { dispatchUserSelect } from './interactive/userSelect';
|
||||
import {
|
||||
InteractiveNodeResponseItemType,
|
||||
UserSelectInteractive
|
||||
WorkflowInteractiveResponseType,
|
||||
InteractiveNodeResponseType
|
||||
} from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||
import { dispatchRunAppNode } from './plugin/runApp';
|
||||
import { dispatchLoop } from './loop/runLoop';
|
||||
@@ -174,10 +174,10 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
let toolRunResponse: ToolRunResponseItemType; // Run with tool mode. Result will response to tool node.
|
||||
let debugNextStepRunNodes: RuntimeNodeItemType[] = [];
|
||||
// 记录交互节点,交互节点需要在工作流完全结束后再进行计算
|
||||
let workflowInteractiveResponse:
|
||||
let nodeInteractiveResponse:
|
||||
| {
|
||||
entryNodeIds: string[];
|
||||
interactiveResponse: UserSelectInteractive;
|
||||
interactiveResponse: InteractiveNodeResponseType;
|
||||
}
|
||||
| undefined;
|
||||
|
||||
@@ -307,7 +307,7 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
interactiveResponse
|
||||
}: {
|
||||
entryNodeIds: string[];
|
||||
interactiveResponse: UserSelectInteractive;
|
||||
interactiveResponse: InteractiveNodeResponseType;
|
||||
}): AIChatItemValueItemType {
|
||||
// Get node outputs
|
||||
const nodeOutputs: NodeOutputItemType[] = [];
|
||||
@@ -323,24 +323,23 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
});
|
||||
});
|
||||
|
||||
const interactiveResult: InteractiveNodeResponseItemType = {
|
||||
const interactiveResult: WorkflowInteractiveResponseType = {
|
||||
...interactiveResponse,
|
||||
entryNodeIds,
|
||||
memoryEdges: runtimeEdges.map((edge) => ({
|
||||
...edge,
|
||||
status: entryNodeIds.includes(edge.target)
|
||||
? 'active'
|
||||
: entryNodeIds.includes(edge.source)
|
||||
? 'waiting'
|
||||
: edge.status
|
||||
status: entryNodeIds.includes(edge.target) ? 'active' : edge.status
|
||||
})),
|
||||
nodeOutputs
|
||||
};
|
||||
|
||||
props.workflowStreamResponse?.({
|
||||
event: SseResponseEventEnum.interactive,
|
||||
data: { interactive: interactiveResult }
|
||||
});
|
||||
// Tool call, not need interactive response
|
||||
if (!props.isToolCall) {
|
||||
props.workflowStreamResponse?.({
|
||||
event: SseResponseEventEnum.interactive,
|
||||
data: { interactive: interactiveResult }
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
type: ChatItemValueTypeEnum.interactive,
|
||||
@@ -404,7 +403,8 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
// In the current version, only one interactive node is allowed at the same time
|
||||
const interactiveResponse = nodeRunResult.result?.[DispatchNodeResponseKeyEnum.interactive];
|
||||
if (interactiveResponse) {
|
||||
workflowInteractiveResponse = {
|
||||
pushStore(nodeRunResult.node, nodeRunResult.result);
|
||||
nodeInteractiveResponse = {
|
||||
entryNodeIds: [nodeRunResult.node.nodeId],
|
||||
interactiveResponse
|
||||
};
|
||||
@@ -599,7 +599,8 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
// Interactive node is not the entry node, return interactive result
|
||||
if (
|
||||
item.flowNodeType !== FlowNodeTypeEnum.userSelect &&
|
||||
item.flowNodeType !== FlowNodeTypeEnum.formInput
|
||||
item.flowNodeType !== FlowNodeTypeEnum.formInput &&
|
||||
item.flowNodeType !== FlowNodeTypeEnum.tools
|
||||
) {
|
||||
item.isEntry = false;
|
||||
}
|
||||
@@ -615,13 +616,16 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
}
|
||||
|
||||
// Interactive node
|
||||
if (workflowInteractiveResponse) {
|
||||
const interactiveResult = handleInteractiveResult({
|
||||
entryNodeIds: workflowInteractiveResponse.entryNodeIds,
|
||||
interactiveResponse: workflowInteractiveResponse.interactiveResponse
|
||||
});
|
||||
chatAssistantResponse.push(interactiveResult);
|
||||
}
|
||||
const interactiveResult = (() => {
|
||||
if (nodeInteractiveResponse) {
|
||||
const interactiveAssistant = handleInteractiveResult({
|
||||
entryNodeIds: nodeInteractiveResponse.entryNodeIds,
|
||||
interactiveResponse: nodeInteractiveResponse.interactiveResponse
|
||||
});
|
||||
chatAssistantResponse.push(interactiveAssistant);
|
||||
return interactiveAssistant.interactive;
|
||||
}
|
||||
})();
|
||||
|
||||
return {
|
||||
flowResponses: chatResponses,
|
||||
@@ -631,6 +635,7 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
||||
finishedEdges: runtimeEdges,
|
||||
nextStepRunNodes: debugNextStepRunNodes
|
||||
},
|
||||
workflowInteractiveResponse: interactiveResult,
|
||||
[DispatchNodeResponseKeyEnum.runTimes]: workflowRunTimes,
|
||||
[DispatchNodeResponseKeyEnum.assistantResponses]:
|
||||
mergeAssistantResponseAnswerText(chatAssistantResponse),
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
UserInputInteractive
|
||||
} from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||
import { addLog } from '../../../../common/system/log';
|
||||
import { getLastInteractiveValue } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
|
||||
type Props = ModuleDispatchProps<{
|
||||
[NodeInputKeyEnum.description]: string;
|
||||
@@ -32,8 +33,10 @@ export const dispatchFormInput = async (props: Props): Promise<FormInputResponse
|
||||
} = props;
|
||||
const { isEntry } = node;
|
||||
|
||||
const interactive = getLastInteractiveValue(histories);
|
||||
|
||||
// Interactive node is not the entry node, return interactive result
|
||||
if (!isEntry) {
|
||||
if (!isEntry || interactive?.type !== 'userInput') {
|
||||
return {
|
||||
[DispatchNodeResponseKeyEnum.interactive]: {
|
||||
type: 'userInput',
|
||||
@@ -61,6 +64,7 @@ export const dispatchFormInput = async (props: Props): Promise<FormInputResponse
|
||||
[DispatchNodeResponseKeyEnum.rewriteHistories]: histories.slice(0, -2), // Removes the current session record as the history of subsequent nodes
|
||||
...userInputVal,
|
||||
[NodeOutputKeyEnum.formInputResult]: userInputVal,
|
||||
[DispatchNodeResponseKeyEnum.toolResponses]: userInputVal,
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||
formInputResult: userInputVal
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import type {
|
||||
UserSelectOptionItemType
|
||||
} from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||
import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
|
||||
import { getLastInteractiveValue } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
|
||||
type Props = ModuleDispatchProps<{
|
||||
[NodeInputKeyEnum.description]: string;
|
||||
@@ -30,8 +31,10 @@ export const dispatchUserSelect = async (props: Props): Promise<UserSelectRespon
|
||||
} = props;
|
||||
const { nodeId, isEntry } = node;
|
||||
|
||||
const interactive = getLastInteractiveValue(histories);
|
||||
|
||||
// Interactive node is not the entry node, return interactive result
|
||||
if (!isEntry) {
|
||||
if (!isEntry || interactive?.type !== 'userSelect') {
|
||||
return {
|
||||
[DispatchNodeResponseKeyEnum.interactive]: {
|
||||
type: 'userSelect',
|
||||
@@ -64,6 +67,7 @@ export const dispatchUserSelect = async (props: Props): Promise<UserSelectRespon
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||
userSelectResult: userSelectedVal
|
||||
},
|
||||
[DispatchNodeResponseKeyEnum.toolResponses]: userSelectedVal,
|
||||
[NodeOutputKeyEnum.selectResult]: userSelectedVal
|
||||
};
|
||||
};
|
||||
|
||||
@@ -17,7 +17,7 @@ export const dispatchCustomFeedback = (props: Record<string, any>): Response =>
|
||||
const {
|
||||
runningAppInfo: { id: appId },
|
||||
chatId,
|
||||
responseChatItemId: chatItemId,
|
||||
responseChatItemId: dataId,
|
||||
stream,
|
||||
workflowStreamResponse,
|
||||
params: { system_textareaInput: feedbackText = '' }
|
||||
@@ -27,13 +27,13 @@ export const dispatchCustomFeedback = (props: Record<string, any>): Response =>
|
||||
addCustomFeedbacks({
|
||||
appId,
|
||||
chatId,
|
||||
chatItemId,
|
||||
dataId,
|
||||
feedbacks: [feedbackText]
|
||||
});
|
||||
}, 60000);
|
||||
|
||||
if (stream) {
|
||||
if (!chatId || !chatItemId) {
|
||||
if (!chatId || !dataId) {
|
||||
workflowStreamResponse?.({
|
||||
event: SseResponseEventEnum.fastAnswer,
|
||||
data: textAdaptGptResponse({
|
||||
|
||||
@@ -18,6 +18,11 @@ import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/util
|
||||
import { getSystemPluginCb } from '../../../../../plugins/register';
|
||||
import { ContentTypes } from '@fastgpt/global/core/workflow/constants';
|
||||
import { replaceEditorVariable } from '@fastgpt/global/core/workflow/utils';
|
||||
import { uploadFile } from '../../../../common/file/gridfs/controller';
|
||||
import { ReadFileBaseUrl } from '@fastgpt/global/common/file/constants';
|
||||
import { createFileToken } from '../../../../support/permission/controller';
|
||||
import { removeFilesByPaths } from '../../../../common/file/utils';
|
||||
import { JSONPath } from 'jsonpath-plus';
|
||||
|
||||
type PropsArrType = {
|
||||
key: string;
|
||||
@@ -55,7 +60,7 @@ const contentTypeMap = {
|
||||
|
||||
export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<HttpResponse> => {
|
||||
let {
|
||||
runningAppInfo: { id: appId },
|
||||
runningAppInfo: { id: appId, teamId, tmbId },
|
||||
chatId,
|
||||
responseChatItemId,
|
||||
variables,
|
||||
@@ -204,7 +209,12 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
||||
const { formatResponse, rawResponse } = await (async () => {
|
||||
const systemPluginCb = await getSystemPluginCb();
|
||||
if (systemPluginCb[httpReqUrl]) {
|
||||
const pluginResult = await systemPluginCb[httpReqUrl](requestBody);
|
||||
const pluginResult = await replaceSystemPluginResponse({
|
||||
response: await systemPluginCb[httpReqUrl](requestBody),
|
||||
teamId,
|
||||
tmbId
|
||||
});
|
||||
|
||||
return {
|
||||
formatResponse: pluginResult,
|
||||
rawResponse: pluginResult
|
||||
@@ -222,11 +232,10 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
||||
|
||||
// format output value type
|
||||
const results: Record<string, any> = {};
|
||||
for (const key in formatResponse) {
|
||||
const output = node.outputs.find((item) => item.key === key);
|
||||
if (!output) continue;
|
||||
results[key] = valueTypeFormat(formatResponse[key], output.valueType);
|
||||
}
|
||||
node.outputs.forEach((item) => {
|
||||
const key = item.key.startsWith('$') ? item.key : `$.${item.key}`;
|
||||
results[item.key] = JSONPath({ path: key, json: formatResponse })[0];
|
||||
});
|
||||
|
||||
if (typeof formatResponse[NodeOutputKeyEnum.answerText] === 'string') {
|
||||
workflowStreamResponse?.({
|
||||
@@ -293,80 +302,8 @@ async function fetchData({
|
||||
data: ['POST', 'PUT', 'PATCH'].includes(method) ? body : undefined
|
||||
});
|
||||
|
||||
/*
|
||||
parse the json:
|
||||
{
|
||||
user: {
|
||||
name: 'xxx',
|
||||
age: 12
|
||||
},
|
||||
list: [
|
||||
{
|
||||
name: 'xxx',
|
||||
age: 50
|
||||
},
|
||||
[{ test: 22 }]
|
||||
],
|
||||
psw: 'xxx'
|
||||
}
|
||||
|
||||
result: {
|
||||
'user': { name: 'xxx', age: 12 },
|
||||
'user.name': 'xxx',
|
||||
'user.age': 12,
|
||||
'list': [ { name: 'xxx', age: 50 }, [ [Object] ] ],
|
||||
'list[0]': { name: 'xxx', age: 50 },
|
||||
'list[0].name': 'xxx',
|
||||
'list[0].age': 50,
|
||||
'list[1]': [ { test: 22 } ],
|
||||
'list[1][0]': { test: 22 },
|
||||
'list[1][0].test': 22,
|
||||
'psw': 'xxx'
|
||||
}
|
||||
*/
|
||||
const parseJson = (obj: Record<string, any>, prefix = '') => {
|
||||
let result: Record<string, any> = {};
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
for (let i = 0; i < obj.length; i++) {
|
||||
result[`${prefix}[${i}]`] = obj[i];
|
||||
|
||||
if (Array.isArray(obj[i])) {
|
||||
result = {
|
||||
...result,
|
||||
...parseJson(obj[i], `${prefix}[${i}]`)
|
||||
};
|
||||
} else if (typeof obj[i] === 'object') {
|
||||
result = {
|
||||
...result,
|
||||
...parseJson(obj[i], `${prefix}[${i}].`)
|
||||
};
|
||||
}
|
||||
}
|
||||
} else if (typeof obj == 'object') {
|
||||
for (const key in obj) {
|
||||
result[`${prefix}${key}`] = obj[key];
|
||||
|
||||
if (Array.isArray(obj[key])) {
|
||||
result = {
|
||||
...result,
|
||||
...parseJson(obj[key], `${prefix}${key}`)
|
||||
};
|
||||
} else if (typeof obj[key] === 'object') {
|
||||
result = {
|
||||
...result,
|
||||
...parseJson(obj[key], `${prefix}${key}.`)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
return {
|
||||
formatResponse:
|
||||
typeof response === 'object' && !Array.isArray(response) ? parseJson(response) : {},
|
||||
formatResponse: typeof response === 'object' ? response : {},
|
||||
rawResponse: response
|
||||
};
|
||||
}
|
||||
@@ -405,3 +342,40 @@ function removeUndefinedSign(obj: Record<string, any>) {
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Replace some special response from system plugin
|
||||
async function replaceSystemPluginResponse({
|
||||
response,
|
||||
teamId,
|
||||
tmbId
|
||||
}: {
|
||||
response: Record<string, any>;
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
}) {
|
||||
for await (const key of Object.keys(response)) {
|
||||
if (typeof response[key] === 'object' && response[key].type === 'SYSTEM_PLUGIN_FILE') {
|
||||
const fileObj = response[key];
|
||||
const filename = fileObj.path.split('/').pop() || `${tmbId}-${Date.now()}`;
|
||||
try {
|
||||
const fileId = await uploadFile({
|
||||
teamId,
|
||||
tmbId,
|
||||
bucketName: 'chat',
|
||||
path: fileObj.path,
|
||||
filename,
|
||||
contentType: fileObj.contentType,
|
||||
metadata: {}
|
||||
});
|
||||
response[key] = `${ReadFileBaseUrl}?filename=${filename}&token=${await createFileToken({
|
||||
bucketName: 'chat',
|
||||
teamId,
|
||||
tmbId,
|
||||
fileId
|
||||
})}`;
|
||||
} catch (error) {}
|
||||
removeFilesByPaths([fileObj.path]);
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
SseResponseEventEnum
|
||||
} from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import { RuntimeNodeItemType } from '@fastgpt/global/core/workflow/runtime/type';
|
||||
import { WorkflowInteractiveResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||
import { RuntimeEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
|
||||
import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type';
|
||||
|
||||
@@ -20,6 +21,7 @@ export type DispatchFlowResponse = {
|
||||
finishedEdges: RuntimeEdgeItemType[];
|
||||
nextStepRunNodes: RuntimeNodeItemType[];
|
||||
};
|
||||
workflowInteractiveResponse?: WorkflowInteractiveResponseType;
|
||||
[DispatchNodeResponseKeyEnum.toolResponses]: ToolRunResponseItemType;
|
||||
[DispatchNodeResponseKeyEnum.assistantResponses]: AIChatItemValueItemType[];
|
||||
[DispatchNodeResponseKeyEnum.runTimes]: number;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"iconv-lite": "^0.6.3",
|
||||
"joplin-turndown-plugin-gfm": "^1.0.12",
|
||||
"json5": "^2.2.3",
|
||||
"jsonpath-plus": "^10.1.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"lodash": "^4.17.21",
|
||||
"mammoth": "^1.6.0",
|
||||
|
||||
@@ -47,10 +47,10 @@ export const getResourcePermission = async ({
|
||||
const tmbPer = (
|
||||
await MongoResourcePermission.findOne(
|
||||
{
|
||||
tmbId,
|
||||
teamId,
|
||||
resourceType,
|
||||
resourceId
|
||||
teamId,
|
||||
resourceId,
|
||||
tmbId
|
||||
},
|
||||
'permission'
|
||||
).lean()
|
||||
@@ -109,9 +109,9 @@ export async function getResourceAllClbs({
|
||||
)): Promise<ResourcePermissionType[]> {
|
||||
return MongoResourcePermission.find(
|
||||
{
|
||||
resourceId,
|
||||
resourceType: resourceType,
|
||||
teamId: teamId,
|
||||
resourceId,
|
||||
groupId: {
|
||||
$exists: false
|
||||
}
|
||||
@@ -131,8 +131,8 @@ export const delResourcePermission = ({
|
||||
...props
|
||||
}: {
|
||||
resourceType: PerResourceTypeEnum;
|
||||
resourceId: string;
|
||||
teamId: string;
|
||||
resourceId: string;
|
||||
tmbId: string;
|
||||
session?: ClientSession;
|
||||
}) => {
|
||||
|
||||
@@ -44,12 +44,33 @@ try {
|
||||
{
|
||||
resourceType: 1,
|
||||
teamId: 1,
|
||||
tmbId: 1,
|
||||
resourceId: 1,
|
||||
groupId: 1
|
||||
},
|
||||
{
|
||||
unique: true
|
||||
unique: true,
|
||||
partialFilterExpression: {
|
||||
groupId: {
|
||||
$exists: true
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
ResourcePermissionSchema.index(
|
||||
{
|
||||
resourceType: 1,
|
||||
teamId: 1,
|
||||
resourceId: 1,
|
||||
tmbId: 1
|
||||
},
|
||||
{
|
||||
unique: true,
|
||||
partialFilterExpression: {
|
||||
tmbId: {
|
||||
$exists: true
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -228,6 +228,10 @@ export const iconPaths = {
|
||||
import('./icons/core/workflow/template/duckduckgo.svg'),
|
||||
'core/workflow/template/extractJson': () =>
|
||||
import('./icons/core/workflow/template/extractJson.svg'),
|
||||
'core/workflow/template/wiki': () => import('./icons/core/workflow/template/wiki.svg'),
|
||||
'core/workflow/template/datasource': () =>
|
||||
import('./icons/core/workflow/template/datasource.svg'),
|
||||
'core/workflow/template/google': () => import('./icons/core/workflow/template/google.svg'),
|
||||
'core/workflow/template/fetchUrl': () => import('./icons/core/workflow/template/fetchUrl.svg'),
|
||||
'core/workflow/template/formInput': () => import('./icons/core/workflow/template/formInput.svg'),
|
||||
'core/workflow/template/getTime': () => import('./icons/core/workflow/template/getTime.svg'),
|
||||
@@ -274,6 +278,7 @@ export const iconPaths = {
|
||||
'core/workflow/versionHistories': () => import('./icons/core/workflow/versionHistories.svg'),
|
||||
date: () => import('./icons/date.svg'),
|
||||
delete: () => import('./icons/delete.svg'),
|
||||
drag: () => import('./icons/drag.svg'),
|
||||
edit: () => import('./icons/edit.svg'),
|
||||
empty: () => import('./icons/empty.svg'),
|
||||
export: () => import('./icons/export.svg'),
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1729242018825" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4362" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M514.174587 1024a457.891171 192.489282 0 1 0 0-384.978564 457.891171 192.489282 0 1 0 0 384.978564Z" fill="#2F82F5" p-id="4363"></path><path d="M56.385749 623.773747H126.381852v212.086144H56.385749zM902.171988 624.234248H972.168091v212.341977h-69.996103z" fill="#2F82F5" p-id="4364"></path><path d="M514.225753 860.419927a457.891171 192.489282 0 1 0 0-384.978564 457.891171 192.489282 0 1 0 0 384.978564Z" fill="#92BDFA" p-id="4365"></path><path d="M377.150052 920.387348s63.395593 11.563684 142.90871 10.642682a862.875931 862.875931 0 0 0 172.073752-18.420027c61.24659-12.894019 116.660171-36.891221 116.660171-36.89122v23.997202s-43.747564 24.764703-116.660171 37.454054c-72.912607 12.689352-122.493179 19.443362-172.073752 18.420027-62.474592-1.279169-142.908709-9.772848-142.90871-9.772847v-25.429871z" fill="#FFFFFF" p-id="4366"></path><path d="M161.328736 891.222306a30.751212 29.165043 90 1 0 58.330086 0 30.751212 29.165043 90 1 0-58.330086 0Z" fill="#FFFFFF" p-id="4367"></path><path d="M514.225753 817.542198a457.891171 193.666117 0 1 0 0-387.332234 457.891171 193.666117 0 1 0 0 387.332234Z" fill="#0166F3" p-id="4368"></path><path d="M56.385749 406.468595H126.381852v214.337481H56.385749zM902.171988 402.375256H972.168091v225.952331h-69.996103z" fill="#0166F3" p-id="4369"></path><path d="M514.225753 646.696447a457.891171 193.666117 0 1 0 0-387.332234 457.891171 193.666117 0 1 0 0 387.332234Z" fill="#92BDFA" p-id="4370"></path><path d="M377.150052 711.473542s63.395593 11.61485 142.90871 10.642682a862.927097 862.927097 0 0 0 172.073752-18.420027c61.24659-12.894019 116.660171-36.840054 116.660171-36.840053v23.946035s-43.747564 24.764703-116.660171 37.505221c-72.912607 12.689352-122.493179 19.443362-172.073752 18.420027-62.474592-1.330335-142.908709-9.824014-142.90871-9.824014v-25.429871z" fill="#FFFFFF" p-id="4371"></path><path d="M161.328736 676.066157a30.751212 29.165043 90 1 0 58.330086 0 30.751212 29.165043 90 1 0-58.330086 0Z" fill="#FFFFFF" p-id="4372"></path><path d="M514.225753 603.460551a457.891171 201.136461 0 1 0 0-402.272923 457.891171 201.136461 0 1 0 0 402.272923Z" fill="#939FAE" p-id="4373"></path><path d="M56.385749 209.067306H126.381852v193.256783H56.385749zM902.223155 215.207315h69.996102v172.073752h-69.996102z" fill="#939FAE" p-id="4374"></path><path d="M514.27692 402.272923a457.891171 201.136461 0 1 0 0-402.272923 457.891171 201.136461 0 1 0 0 402.272923Z" fill="#D5DBE4" p-id="4375"></path><path d="M377.252386 486.135212s63.395593 11.563684 142.908709 10.642682a862.927097 862.927097 0 0 0 172.073752-18.420027c61.24659-12.945186 116.660171-36.891221 116.660171-36.89122v23.946035s-43.747564 24.81587-116.660171 37.505221c-72.912607 12.689352-122.493179 19.443362-172.073752 18.420027-62.474592-1.330335-142.908709-9.824014-142.908709-9.824014V486.186379z" fill="#FFFFFF" p-id="4376"></path><path d="M161.328736 461.012342a30.751212 29.165043 90 1 0 58.330086 0 30.751212 29.165043 90 1 0-58.330086 0Z" fill="#FFFFFF" p-id="4377"></path><path d="M514.225753 603.460551a457.891171 201.136461 0 1 0 0-402.272923 457.891171 201.136461 0 1 0 0 402.272923Z" fill="#2F82F5" p-id="4378"></path><path d="M56.385749 209.067306H126.381852v193.256783H56.385749zM902.223155 215.207315h69.996102v172.073752h-69.996102z" fill="#2F82F5" p-id="4379"></path><path d="M514.27692 402.272923a457.891171 201.136461 0 1 0 0-402.272923 457.891171 201.136461 0 1 0 0 402.272923Z" fill="#92BDFA" p-id="4380"></path><path d="M514.328087 299.632439a224.161495 98.444811 0 1 0 0-196.889622 224.161495 98.444811 0 1 0 0 196.889622Z" fill="#FFFFFF" p-id="4381"></path><path d="M377.252386 486.135212s63.395593 11.563684 142.908709 10.642682a862.927097 862.927097 0 0 0 172.073752-18.420027c61.24659-12.945186 116.660171-36.891221 116.660171-36.89122v23.946035s-43.747564 24.81587-116.660171 37.505221c-72.912607 12.689352-122.493179 19.443362-172.073752 18.420027-62.474592-1.330335-142.908709-9.824014-142.908709-9.824014V486.186379z" fill="#FFFFFF" p-id="4382"></path><path d="M161.328736 461.012342a30.751212 29.165043 90 1 0 58.330086 0 30.751212 29.165043 90 1 0-58.330086 0Z" fill="#FFFFFF" p-id="4383"></path></svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
@@ -0,0 +1 @@
|
||||
<svg t="1729136070356" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4261" width="200" height="200"><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="4262"></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="4263"></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="4264"></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="4265"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1 @@
|
||||
<svg t="1728613783770" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4250" width="200" height="200"><path d="M530.077727 610.234325c-49.762357 97.384981-104.639199 199.047382-151.85864 295.595298-0.433882 0.433882-22.974247 0-22.974247-0.433882-71.867818-168.012551-147.146315-334.722434-219.044832-503.168868C118.774127 361.393887 60.486606 295.04476 20.521383 295.478641c0-4.681626-0.433882-15.315824-0.433882-21.703301l247.972675 0 0 21.269419c-29.360701 1.704828-80.827887 20.400632-66.349127 52.30425 33.608445 76.146261 158.6493 367.927697 192.257745 441.935248 22.974247-45.514613 88.455611-167.143765 115.27442-218.61095-21.269419-43.405579-89.757257-205.031677-111.460558-244.995877-14.881942-27.221991-55.281047-29.795607-85.478813-30.197766 0-6.821359 0-12.340049 0-21.269419l218.177068 0.403183 0 19.99745c-29.763884 0.867764-58.256822 11.905144-45.079708 39.995923 28.92682 61.23362 46.785559 104.205317 73.604368 160.354128 8.495488-16.587793 53.141314-106.34505 73.573669-154.401556 13.610996-31.469736-5.952572-43.808762-59.125609-45.080731 0.433882-5.54939 0-15.749706 0.433882-20.866237 68.053955-0.403183 170.120562-0.403183 188.413184-0.837064l0 20.834514c-34.44551 1.270946-70.162989 19.563568-88.889493 48.490388l-90.594321 188.010001c9.766434 24.679076 96.981798 218.61095 105.910145 239.880369l187.576119-432.571996c-13.177114-35.282574-55.714929-42.940998-72.301699-43.37488 0-5.51869 0-14.044878 0-21.269419l195.636702 1.704828 0.433882 0.837064-0.433882 18.726503c-42.940998 1.270946-68.892043 24.245194-85.478813 61.667501-38.290071 88.48631-158.246117 367.493815-237.772358 549.117362l-20.834514 0L530.077727 610.234325z" fill="#3E3A39" p-id="4251"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user