Spaces:
Paused
Paused
zxsipola123456
commited on
Commit
•
ab2ded1
1
Parent(s):
6e7f556
Upload 769 files
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .gitattributes +1 -0
- agent/README.md +45 -0
- agent/README_zh.md +46 -0
- agent/__init__.py +0 -0
- agent/canvas.py +302 -0
- agent/component/__init__.py +28 -0
- agent/component/answer.py +79 -0
- agent/component/arxiv.py +69 -0
- agent/component/baidu.py +69 -0
- agent/component/baidufanyi.py +99 -0
- agent/component/base.py +494 -0
- agent/component/begin.py +48 -0
- agent/component/bing.py +85 -0
- agent/component/categorize.py +87 -0
- agent/component/cite.py +75 -0
- agent/component/deepl.py +62 -0
- agent/component/duckduckgo.py +66 -0
- agent/component/generate.py +150 -0
- agent/component/github.py +61 -0
- agent/component/google.py +96 -0
- agent/component/googlescholar.py +70 -0
- agent/component/keyword.py +65 -0
- agent/component/message.py +53 -0
- agent/component/pubmed.py +65 -0
- agent/component/qweather.py +111 -0
- agent/component/relevant.py +80 -0
- agent/component/retrieval.py +88 -0
- agent/component/rewrite.py +72 -0
- agent/component/switch.py +77 -0
- agent/component/wikipedia.py +69 -0
- agent/settings.py +34 -0
- agent/templates/HR_callout_zh.json +0 -0
- agent/templates/customer_service.json +620 -0
- agent/templates/general_chat_bot.json +335 -0
- agent/templates/interpreter.json +158 -0
- agent/templates/websearch_assistant.json +547 -0
- agent/test/client.py +48 -0
- agent/test/dsl_examples/categorize.json +45 -0
- agent/test/dsl_examples/customer_service.json +157 -0
- agent/test/dsl_examples/headhunter_zh.json +210 -0
- agent/test/dsl_examples/intergreper.json +39 -0
- agent/test/dsl_examples/interpreter.json +39 -0
- agent/test/dsl_examples/keyword_wikipedia_and_generate.json +62 -0
- agent/test/dsl_examples/retrieval_and_generate.json +54 -0
- agent/test/dsl_examples/retrieval_categorize_and_generate.json +88 -0
- agent/test/dsl_examples/retrieval_relevant_and_generate.json +82 -0
- agent/test/dsl_examples/retrieval_relevant_keyword_baidu_and_generate.json +103 -0
- agent/test/dsl_examples/retrieval_relevant_rewrite_and_generate.json +79 -0
- api/__init__.py +0 -0
- api/apps/__init__.py +125 -0
.gitattributes
CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
+
web/src/assets/svg/chunk-method/media-01.svg filter=lfs diff=lfs merge=lfs -text
|
agent/README.md
ADDED
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
English | [简体中文](./README_zh.md)
|
2 |
+
|
3 |
+
# *Graph*
|
4 |
+
|
5 |
+
|
6 |
+
## Introduction
|
7 |
+
|
8 |
+
*Graph* is a mathematical concept which is composed of nodes and edges.
|
9 |
+
It is used to compose a complex work flow or agent.
|
10 |
+
And this graph is beyond the DAG that we can use circles to describe our agent or work flow.
|
11 |
+
Under this folder, we propose a test tool ./test/client.py which can test the DSLs such as json files in folder ./test/dsl_examples.
|
12 |
+
Please use this client at the same folder you start RAGFlow. If it's run by Docker, please go into the container before running the client.
|
13 |
+
Otherwise, correct configurations in conf/service_conf.yaml is essential.
|
14 |
+
|
15 |
+
```bash
|
16 |
+
PYTHONPATH=path/to/ragflow python graph/test/client.py -h
|
17 |
+
usage: client.py [-h] -s DSL -t TENANT_ID -m
|
18 |
+
|
19 |
+
options:
|
20 |
+
-h, --help show this help message and exit
|
21 |
+
-s DSL, --dsl DSL input dsl
|
22 |
+
-t TENANT_ID, --tenant_id TENANT_ID
|
23 |
+
Tenant ID
|
24 |
+
-m, --stream Stream output
|
25 |
+
```
|
26 |
+
<div align="center" style="margin-top:20px;margin-bottom:20px;">
|
27 |
+
<img src="https://github.com/infiniflow/ragflow/assets/12318111/79179c5e-d4d6-464a-b6c4-5721cb329899" width="1000"/>
|
28 |
+
</div>
|
29 |
+
|
30 |
+
|
31 |
+
## How to gain a TENANT_ID in command line?
|
32 |
+
<div align="center" style="margin-top:20px;margin-bottom:20px;">
|
33 |
+
<img src="https://github.com/infiniflow/ragflow/assets/12318111/419d8588-87b1-4ab8-ac49-2d1f047a4b97" width="600"/>
|
34 |
+
</div>
|
35 |
+
💡 We plan to display it here in the near future.
|
36 |
+
<div align="center" style="margin-top:20px;margin-bottom:20px;">
|
37 |
+
<img src="https://github.com/infiniflow/ragflow/assets/12318111/c97915de-0091-46a5-afd9-e278946e5fe3" width="600"/>
|
38 |
+
</div>
|
39 |
+
|
40 |
+
|
41 |
+
## How to set 'kb_ids' for component 'Retrieval' in DSL?
|
42 |
+
<div align="center" style="margin-top:20px;margin-bottom:20px;">
|
43 |
+
<img src="https://github.com/infiniflow/ragflow/assets/12318111/0a731534-cac8-49fd-8a92-ca247eeef66d" width="600"/>
|
44 |
+
</div>
|
45 |
+
|
agent/README_zh.md
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[English](./README.md) | 简体中文
|
2 |
+
|
3 |
+
# *Graph*
|
4 |
+
|
5 |
+
|
6 |
+
## 简介
|
7 |
+
|
8 |
+
"Graph"是一个由节点和边组成的数学概念。
|
9 |
+
它被用来构建复杂的工作流或代理。
|
10 |
+
这个图超越了有向无环图(DAG),我们可以使用循环来描述我们的代理或工作流。
|
11 |
+
在这个文件夹下,我们提出了一个测试工具 ./test/client.py,
|
12 |
+
它可以测试像文件夹./test/dsl_examples下一样的DSL文件。
|
13 |
+
请在启动 RAGFlow 的同一文件夹中使用此客户端。如果它是通过 Docker 运行的,请在运行客户端之前进入容器。
|
14 |
+
否则,正确配置 conf/service_conf.yaml 文件是必不可少的。
|
15 |
+
|
16 |
+
```bash
|
17 |
+
PYTHONPATH=path/to/ragflow python graph/test/client.py -h
|
18 |
+
usage: client.py [-h] -s DSL -t TENANT_ID -m
|
19 |
+
|
20 |
+
options:
|
21 |
+
-h, --help show this help message and exit
|
22 |
+
-s DSL, --dsl DSL input dsl
|
23 |
+
-t TENANT_ID, --tenant_id TENANT_ID
|
24 |
+
Tenant ID
|
25 |
+
-m, --stream Stream output
|
26 |
+
```
|
27 |
+
<div align="center" style="margin-top:20px;margin-bottom:20px;">
|
28 |
+
<img src="https://github.com/infiniflow/ragflow/assets/12318111/05924730-c427-495b-8ee4-90b8b2250681" width="1000"/>
|
29 |
+
</div>
|
30 |
+
|
31 |
+
|
32 |
+
## 命令行中的TENANT_ID如何获得?
|
33 |
+
<div align="center" style="margin-top:20px;margin-bottom:20px;">
|
34 |
+
<img src="https://github.com/infiniflow/ragflow/assets/12318111/419d8588-87b1-4ab8-ac49-2d1f047a4b97" width="600"/>
|
35 |
+
</div>
|
36 |
+
💡 后面会展示在这里:
|
37 |
+
<div align="center" style="margin-top:20px;margin-bottom:20px;">
|
38 |
+
<img src="https://github.com/infiniflow/ragflow/assets/12318111/c97915de-0091-46a5-afd9-e278946e5fe3" width="600"/>
|
39 |
+
</div>
|
40 |
+
|
41 |
+
|
42 |
+
## DSL里面的Retrieval组件的kb_ids怎么填?
|
43 |
+
<div align="center" style="margin-top:20px;margin-bottom:20px;">
|
44 |
+
<img src="https://github.com/infiniflow/ragflow/assets/12318111/0a731534-cac8-49fd-8a92-ca247eeef66d" width="600"/>
|
45 |
+
</div>
|
46 |
+
|
agent/__init__.py
ADDED
File without changes
|
agent/canvas.py
ADDED
@@ -0,0 +1,302 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#
|
2 |
+
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
3 |
+
#
|
4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5 |
+
# you may not use this file except in compliance with the License.
|
6 |
+
# You may obtain a copy of the License at
|
7 |
+
#
|
8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9 |
+
#
|
10 |
+
# Unless required by applicable law or agreed to in writing, software
|
11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 |
+
# See the License for the specific language governing permissions and
|
14 |
+
# limitations under the License.
|
15 |
+
#
|
16 |
+
import importlib
|
17 |
+
import json
|
18 |
+
import traceback
|
19 |
+
from abc import ABC
|
20 |
+
from copy import deepcopy
|
21 |
+
from functools import partial
|
22 |
+
|
23 |
+
import pandas as pd
|
24 |
+
|
25 |
+
from agent.component import component_class
|
26 |
+
from agent.component.base import ComponentBase
|
27 |
+
from agent.settings import flow_logger, DEBUG
|
28 |
+
|
29 |
+
|
30 |
+
class Canvas(ABC):
|
31 |
+
"""
|
32 |
+
dsl = {
|
33 |
+
"components": {
|
34 |
+
"begin": {
|
35 |
+
"obj":{
|
36 |
+
"component_name": "Begin",
|
37 |
+
"params": {},
|
38 |
+
},
|
39 |
+
"downstream": ["answer_0"],
|
40 |
+
"upstream": [],
|
41 |
+
},
|
42 |
+
"answer_0": {
|
43 |
+
"obj": {
|
44 |
+
"component_name": "Answer",
|
45 |
+
"params": {}
|
46 |
+
},
|
47 |
+
"downstream": ["retrieval_0"],
|
48 |
+
"upstream": ["begin", "generate_0"],
|
49 |
+
},
|
50 |
+
"retrieval_0": {
|
51 |
+
"obj": {
|
52 |
+
"component_name": "Retrieval",
|
53 |
+
"params": {}
|
54 |
+
},
|
55 |
+
"downstream": ["generate_0"],
|
56 |
+
"upstream": ["answer_0"],
|
57 |
+
},
|
58 |
+
"generate_0": {
|
59 |
+
"obj": {
|
60 |
+
"component_name": "Generate",
|
61 |
+
"params": {}
|
62 |
+
},
|
63 |
+
"downstream": ["answer_0"],
|
64 |
+
"upstream": ["retrieval_0"],
|
65 |
+
}
|
66 |
+
},
|
67 |
+
"history": [],
|
68 |
+
"messages": [],
|
69 |
+
"reference": [],
|
70 |
+
"path": [["begin"]],
|
71 |
+
"answer": []
|
72 |
+
}
|
73 |
+
"""
|
74 |
+
|
75 |
+
def __init__(self, dsl: str, tenant_id=None):
|
76 |
+
self.path = []
|
77 |
+
self.history = []
|
78 |
+
self.messages = []
|
79 |
+
self.answer = []
|
80 |
+
self.components = {}
|
81 |
+
self.dsl = json.loads(dsl) if dsl else {
|
82 |
+
"components": {
|
83 |
+
"begin": {
|
84 |
+
"obj": {
|
85 |
+
"component_name": "Begin",
|
86 |
+
"params": {
|
87 |
+
"prologue": "Hi there!"
|
88 |
+
}
|
89 |
+
},
|
90 |
+
"downstream": [],
|
91 |
+
"upstream": []
|
92 |
+
}
|
93 |
+
},
|
94 |
+
"history": [],
|
95 |
+
"messages": [],
|
96 |
+
"reference": [],
|
97 |
+
"path": [],
|
98 |
+
"answer": []
|
99 |
+
}
|
100 |
+
self._tenant_id = tenant_id
|
101 |
+
self._embed_id = ""
|
102 |
+
self.load()
|
103 |
+
|
104 |
+
def load(self):
|
105 |
+
self.components = self.dsl["components"]
|
106 |
+
cpn_nms = set([])
|
107 |
+
for k, cpn in self.components.items():
|
108 |
+
cpn_nms.add(cpn["obj"]["component_name"])
|
109 |
+
|
110 |
+
assert "Begin" in cpn_nms, "There have to be an 'Begin' component."
|
111 |
+
assert "Answer" in cpn_nms, "There have to be an 'Answer' component."
|
112 |
+
|
113 |
+
for k, cpn in self.components.items():
|
114 |
+
cpn_nms.add(cpn["obj"]["component_name"])
|
115 |
+
param = component_class(cpn["obj"]["component_name"] + "Param")()
|
116 |
+
param.update(cpn["obj"]["params"])
|
117 |
+
param.check()
|
118 |
+
cpn["obj"] = component_class(cpn["obj"]["component_name"])(self, k, param)
|
119 |
+
if cpn["obj"].component_name == "Categorize":
|
120 |
+
for _, desc in param.category_description.items():
|
121 |
+
if desc["to"] not in cpn["downstream"]:
|
122 |
+
cpn["downstream"].append(desc["to"])
|
123 |
+
|
124 |
+
self.path = self.dsl["path"]
|
125 |
+
self.history = self.dsl["history"]
|
126 |
+
self.messages = self.dsl["messages"]
|
127 |
+
self.answer = self.dsl["answer"]
|
128 |
+
self.reference = self.dsl["reference"]
|
129 |
+
self._embed_id = self.dsl.get("embed_id", "")
|
130 |
+
|
131 |
+
def __str__(self):
|
132 |
+
self.dsl["path"] = self.path
|
133 |
+
self.dsl["history"] = self.history
|
134 |
+
self.dsl["messages"] = self.messages
|
135 |
+
self.dsl["answer"] = self.answer
|
136 |
+
self.dsl["reference"] = self.reference
|
137 |
+
self.dsl["embed_id"] = self._embed_id
|
138 |
+
dsl = {
|
139 |
+
"components": {}
|
140 |
+
}
|
141 |
+
for k in self.dsl.keys():
|
142 |
+
if k in ["components"]:continue
|
143 |
+
dsl[k] = deepcopy(self.dsl[k])
|
144 |
+
|
145 |
+
for k, cpn in self.components.items():
|
146 |
+
if k not in dsl["components"]:
|
147 |
+
dsl["components"][k] = {}
|
148 |
+
for c in cpn.keys():
|
149 |
+
if c == "obj":
|
150 |
+
dsl["components"][k][c] = json.loads(str(cpn["obj"]))
|
151 |
+
continue
|
152 |
+
dsl["components"][k][c] = deepcopy(cpn[c])
|
153 |
+
return json.dumps(dsl, ensure_ascii=False)
|
154 |
+
|
155 |
+
def reset(self):
|
156 |
+
self.path = []
|
157 |
+
self.history = []
|
158 |
+
self.messages = []
|
159 |
+
self.answer = []
|
160 |
+
self.reference = []
|
161 |
+
for k, cpn in self.components.items():
|
162 |
+
self.components[k]["obj"].reset()
|
163 |
+
self._embed_id = ""
|
164 |
+
|
165 |
+
def run(self, **kwargs):
|
166 |
+
ans = ""
|
167 |
+
if self.answer:
|
168 |
+
cpn_id = self.answer[0]
|
169 |
+
self.answer.pop(0)
|
170 |
+
try:
|
171 |
+
ans = self.components[cpn_id]["obj"].run(self.history, **kwargs)
|
172 |
+
except Exception as e:
|
173 |
+
ans = ComponentBase.be_output(str(e))
|
174 |
+
self.path[-1].append(cpn_id)
|
175 |
+
if kwargs.get("stream"):
|
176 |
+
assert isinstance(ans, partial)
|
177 |
+
return ans
|
178 |
+
self.history.append(("assistant", ans.to_dict("records")))
|
179 |
+
return ans
|
180 |
+
|
181 |
+
if not self.path:
|
182 |
+
self.components["begin"]["obj"].run(self.history, **kwargs)
|
183 |
+
self.path.append(["begin"])
|
184 |
+
|
185 |
+
self.path.append([])
|
186 |
+
ran = -1
|
187 |
+
|
188 |
+
def prepare2run(cpns):
|
189 |
+
nonlocal ran, ans
|
190 |
+
for c in cpns:
|
191 |
+
if self.path[-1] and c == self.path[-1][-1]: continue
|
192 |
+
cpn = self.components[c]["obj"]
|
193 |
+
if cpn.component_name == "Answer":
|
194 |
+
self.answer.append(c)
|
195 |
+
else:
|
196 |
+
if DEBUG: print("RUN: ", c)
|
197 |
+
if cpn.component_name == "Generate":
|
198 |
+
cpids = cpn.get_dependent_components()
|
199 |
+
if any([c not in self.path[-1] for c in cpids]):
|
200 |
+
continue
|
201 |
+
ans = cpn.run(self.history, **kwargs)
|
202 |
+
self.path[-1].append(c)
|
203 |
+
ran += 1
|
204 |
+
|
205 |
+
prepare2run(self.components[self.path[-2][-1]]["downstream"])
|
206 |
+
while 0 <= ran < len(self.path[-1]):
|
207 |
+
if DEBUG: print(ran, self.path)
|
208 |
+
cpn_id = self.path[-1][ran]
|
209 |
+
cpn = self.get_component(cpn_id)
|
210 |
+
if not cpn["downstream"]: break
|
211 |
+
|
212 |
+
loop = self._find_loop()
|
213 |
+
if loop: raise OverflowError(f"Too much loops: {loop}")
|
214 |
+
|
215 |
+
if cpn["obj"].component_name.lower() in ["switch", "categorize", "relevant"]:
|
216 |
+
switch_out = cpn["obj"].output()[1].iloc[0, 0]
|
217 |
+
assert switch_out in self.components, \
|
218 |
+
"{}'s output: {} not valid.".format(cpn_id, switch_out)
|
219 |
+
try:
|
220 |
+
prepare2run([switch_out])
|
221 |
+
except Exception as e:
|
222 |
+
for p in [c for p in self.path for c in p][::-1]:
|
223 |
+
if p.lower().find("answer") >= 0:
|
224 |
+
self.get_component(p)["obj"].set_exception(e)
|
225 |
+
prepare2run([p])
|
226 |
+
break
|
227 |
+
traceback.print_exc()
|
228 |
+
break
|
229 |
+
continue
|
230 |
+
|
231 |
+
try:
|
232 |
+
prepare2run(cpn["downstream"])
|
233 |
+
except Exception as e:
|
234 |
+
for p in [c for p in self.path for c in p][::-1]:
|
235 |
+
if p.lower().find("answer") >= 0:
|
236 |
+
self.get_component(p)["obj"].set_exception(e)
|
237 |
+
prepare2run([p])
|
238 |
+
break
|
239 |
+
traceback.print_exc()
|
240 |
+
break
|
241 |
+
|
242 |
+
if self.answer:
|
243 |
+
cpn_id = self.answer[0]
|
244 |
+
self.answer.pop(0)
|
245 |
+
ans = self.components[cpn_id]["obj"].run(self.history, **kwargs)
|
246 |
+
self.path[-1].append(cpn_id)
|
247 |
+
if kwargs.get("stream"):
|
248 |
+
assert isinstance(ans, partial)
|
249 |
+
return ans
|
250 |
+
|
251 |
+
self.history.append(("assistant", ans.to_dict("records")))
|
252 |
+
|
253 |
+
return ans
|
254 |
+
|
255 |
+
def get_component(self, cpn_id):
|
256 |
+
return self.components[cpn_id]
|
257 |
+
|
258 |
+
def get_tenant_id(self):
|
259 |
+
return self._tenant_id
|
260 |
+
|
261 |
+
def get_history(self, window_size):
|
262 |
+
convs = []
|
263 |
+
for role, obj in self.history[window_size * -2:]:
|
264 |
+
convs.append({"role": role, "content": (obj if role == "user" else
|
265 |
+
'\n'.join(pd.DataFrame(obj)['content']))})
|
266 |
+
return convs
|
267 |
+
|
268 |
+
def add_user_input(self, question):
|
269 |
+
self.history.append(("user", question))
|
270 |
+
|
271 |
+
def set_embedding_model(self, embed_id):
|
272 |
+
self._embed_id = embed_id
|
273 |
+
|
274 |
+
def get_embedding_model(self):
|
275 |
+
return self._embed_id
|
276 |
+
|
277 |
+
def _find_loop(self, max_loops=2):
|
278 |
+
path = self.path[-1][::-1]
|
279 |
+
if len(path) < 2: return False
|
280 |
+
|
281 |
+
for i in range(len(path)):
|
282 |
+
if path[i].lower().find("answer") >= 0:
|
283 |
+
path = path[:i]
|
284 |
+
break
|
285 |
+
|
286 |
+
if len(path) < 2: return False
|
287 |
+
|
288 |
+
for l in range(2, len(path) // 2):
|
289 |
+
pat = ",".join(path[0:l])
|
290 |
+
path_str = ",".join(path)
|
291 |
+
if len(pat) >= len(path_str): return False
|
292 |
+
loop = max_loops
|
293 |
+
while path_str.find(pat) == 0 and loop >= 0:
|
294 |
+
loop -= 1
|
295 |
+
if len(pat)+1 >= len(path_str):
|
296 |
+
return False
|
297 |
+
path_str = path_str[len(pat)+1:]
|
298 |
+
if loop < 0:
|
299 |
+
pat = " => ".join([p.split(":")[0] for p in path[0:l]])
|
300 |
+
return pat + " => " + pat
|
301 |
+
|
302 |
+
return False
|
agent/component/__init__.py
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import importlib
|
2 |
+
from .begin import Begin, BeginParam
|
3 |
+
from .generate import Generate, GenerateParam
|
4 |
+
from .retrieval import Retrieval, RetrievalParam
|
5 |
+
from .answer import Answer, AnswerParam
|
6 |
+
from .categorize import Categorize, CategorizeParam
|
7 |
+
from .switch import Switch, SwitchParam
|
8 |
+
from .relevant import Relevant, RelevantParam
|
9 |
+
from .message import Message, MessageParam
|
10 |
+
from .rewrite import RewriteQuestion, RewriteQuestionParam
|
11 |
+
from .keyword import KeywordExtract, KeywordExtractParam
|
12 |
+
from .baidu import Baidu, BaiduParam
|
13 |
+
from .duckduckgo import DuckDuckGo, DuckDuckGoParam
|
14 |
+
from .wikipedia import Wikipedia, WikipediaParam
|
15 |
+
from .pubmed import PubMed, PubMedParam
|
16 |
+
from .arxiv import ArXiv, ArXivParam
|
17 |
+
from .google import Google, GoogleParam
|
18 |
+
from .bing import Bing, BingParam
|
19 |
+
from .googlescholar import GoogleScholar, GoogleScholarParam
|
20 |
+
from .deepl import DeepL, DeepLParam
|
21 |
+
from .github import GitHub, GitHubParam
|
22 |
+
from .baidufanyi import BaiduFanyi, BaiduFanyiParam
|
23 |
+
from .qweather import QWeather, QWeatherParam
|
24 |
+
|
25 |
+
def component_class(class_name):
|
26 |
+
m = importlib.import_module("agent.component")
|
27 |
+
c = getattr(m, class_name)
|
28 |
+
return c
|
agent/component/answer.py
ADDED
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#
|
2 |
+
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
3 |
+
#
|
4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5 |
+
# you may not use this file except in compliance with the License.
|
6 |
+
# You may obtain a copy of the License at
|
7 |
+
#
|
8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9 |
+
#
|
10 |
+
# Unless required by applicable law or agreed to in writing, software
|
11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 |
+
# See the License for the specific language governing permissions and
|
14 |
+
# limitations under the License.
|
15 |
+
#
|
16 |
+
import random
|
17 |
+
from abc import ABC
|
18 |
+
from functools import partial
|
19 |
+
|
20 |
+
import pandas as pd
|
21 |
+
|
22 |
+
from agent.component.base import ComponentBase, ComponentParamBase
|
23 |
+
|
24 |
+
|
25 |
+
class AnswerParam(ComponentParamBase):
|
26 |
+
|
27 |
+
"""
|
28 |
+
Define the Answer component parameters.
|
29 |
+
"""
|
30 |
+
def __init__(self):
|
31 |
+
super().__init__()
|
32 |
+
self.post_answers = []
|
33 |
+
|
34 |
+
def check(self):
|
35 |
+
return True
|
36 |
+
|
37 |
+
|
38 |
+
class Answer(ComponentBase, ABC):
|
39 |
+
component_name = "Answer"
|
40 |
+
|
41 |
+
def _run(self, history, **kwargs):
|
42 |
+
if kwargs.get("stream"):
|
43 |
+
return partial(self.stream_output)
|
44 |
+
|
45 |
+
ans = self.get_input()
|
46 |
+
if self._param.post_answers:
|
47 |
+
ans = pd.concat([ans, pd.DataFrame([{"content": random.choice(self._param.post_answers)}])], ignore_index=False)
|
48 |
+
return ans
|
49 |
+
|
50 |
+
def stream_output(self):
|
51 |
+
res = None
|
52 |
+
if hasattr(self, "exception") and self.exception:
|
53 |
+
res = {"content": str(self.exception)}
|
54 |
+
self.exception = None
|
55 |
+
yield res
|
56 |
+
self.set_output(res)
|
57 |
+
return
|
58 |
+
|
59 |
+
stream = self.get_stream_input()
|
60 |
+
if isinstance(stream, pd.DataFrame):
|
61 |
+
res = stream
|
62 |
+
answer = ""
|
63 |
+
for ii, row in stream.iterrows():
|
64 |
+
answer += row.to_dict()["content"]
|
65 |
+
yield {"content": answer}
|
66 |
+
else:
|
67 |
+
for st in stream():
|
68 |
+
res = st
|
69 |
+
yield st
|
70 |
+
if self._param.post_answers:
|
71 |
+
res["content"] += random.choice(self._param.post_answers)
|
72 |
+
yield res
|
73 |
+
|
74 |
+
self.set_output(res)
|
75 |
+
|
76 |
+
def set_exception(self, e):
|
77 |
+
self.exception = e
|
78 |
+
|
79 |
+
|
agent/component/arxiv.py
ADDED
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#
|
2 |
+
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
3 |
+
#
|
4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5 |
+
# you may not use this file except in compliance with the License.
|
6 |
+
# You may obtain a copy of the License at
|
7 |
+
#
|
8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9 |
+
#
|
10 |
+
# Unless required by applicable law or agreed to in writing, software
|
11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 |
+
# See the License for the specific language governing permissions and
|
14 |
+
# limitations under the License.
|
15 |
+
#
|
16 |
+
from abc import ABC
|
17 |
+
import arxiv
|
18 |
+
import pandas as pd
|
19 |
+
from agent.settings import DEBUG
|
20 |
+
from agent.component.base import ComponentBase, ComponentParamBase
|
21 |
+
|
22 |
+
|
23 |
+
class ArXivParam(ComponentParamBase):
|
24 |
+
"""
|
25 |
+
Define the ArXiv component parameters.
|
26 |
+
"""
|
27 |
+
|
28 |
+
def __init__(self):
|
29 |
+
super().__init__()
|
30 |
+
self.top_n = 6
|
31 |
+
self.sort_by = 'submittedDate'
|
32 |
+
|
33 |
+
def check(self):
|
34 |
+
self.check_positive_integer(self.top_n, "Top N")
|
35 |
+
self.check_valid_value(self.sort_by, "ArXiv Search Sort_by",
|
36 |
+
['submittedDate', 'lastUpdatedDate', 'relevance'])
|
37 |
+
|
38 |
+
|
39 |
+
class ArXiv(ComponentBase, ABC):
|
40 |
+
component_name = "ArXiv"
|
41 |
+
|
42 |
+
def _run(self, history, **kwargs):
|
43 |
+
ans = self.get_input()
|
44 |
+
ans = " - ".join(ans["content"]) if "content" in ans else ""
|
45 |
+
if not ans:
|
46 |
+
return ArXiv.be_output("")
|
47 |
+
|
48 |
+
try:
|
49 |
+
sort_choices = {"relevance": arxiv.SortCriterion.Relevance,
|
50 |
+
"lastUpdatedDate": arxiv.SortCriterion.LastUpdatedDate,
|
51 |
+
'submittedDate': arxiv.SortCriterion.SubmittedDate}
|
52 |
+
arxiv_client = arxiv.Client()
|
53 |
+
search = arxiv.Search(
|
54 |
+
query=ans,
|
55 |
+
max_results=self._param.top_n,
|
56 |
+
sort_by=sort_choices[self._param.sort_by]
|
57 |
+
)
|
58 |
+
arxiv_res = [
|
59 |
+
{"content": 'Title: ' + i.title + '\nPdf_Url: <a href="' + i.pdf_url + '"></a> \nSummary: ' + i.summary} for
|
60 |
+
i in list(arxiv_client.results(search))]
|
61 |
+
except Exception as e:
|
62 |
+
return ArXiv.be_output("**ERROR**: " + str(e))
|
63 |
+
|
64 |
+
if not arxiv_res:
|
65 |
+
return ArXiv.be_output("")
|
66 |
+
|
67 |
+
df = pd.DataFrame(arxiv_res)
|
68 |
+
if DEBUG: print(df, ":::::::::::::::::::::::::::::::::")
|
69 |
+
return df
|
agent/component/baidu.py
ADDED
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#
|
2 |
+
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
3 |
+
#
|
4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5 |
+
# you may not use this file except in compliance with the License.
|
6 |
+
# You may obtain a copy of the License at
|
7 |
+
#
|
8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9 |
+
#
|
10 |
+
# Unless required by applicable law or agreed to in writing, software
|
11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 |
+
# See the License for the specific language governing permissions and
|
14 |
+
# limitations under the License.
|
15 |
+
#
|
16 |
+
import random
|
17 |
+
from abc import ABC
|
18 |
+
from functools import partial
|
19 |
+
import pandas as pd
|
20 |
+
import requests
|
21 |
+
import re
|
22 |
+
from agent.settings import DEBUG
|
23 |
+
from agent.component.base import ComponentBase, ComponentParamBase
|
24 |
+
|
25 |
+
|
26 |
+
class BaiduParam(ComponentParamBase):
|
27 |
+
"""
|
28 |
+
Define the Baidu component parameters.
|
29 |
+
"""
|
30 |
+
|
31 |
+
def __init__(self):
|
32 |
+
super().__init__()
|
33 |
+
self.top_n = 10
|
34 |
+
|
35 |
+
def check(self):
|
36 |
+
self.check_positive_integer(self.top_n, "Top N")
|
37 |
+
|
38 |
+
|
39 |
+
class Baidu(ComponentBase, ABC):
|
40 |
+
component_name = "Baidu"
|
41 |
+
|
42 |
+
def _run(self, history, **kwargs):
|
43 |
+
ans = self.get_input()
|
44 |
+
ans = " - ".join(ans["content"]) if "content" in ans else ""
|
45 |
+
if not ans:
|
46 |
+
return Baidu.be_output("")
|
47 |
+
|
48 |
+
try:
|
49 |
+
url = 'https://www.baidu.com/s?wd=' + ans + '&rn=' + str(self._param.top_n)
|
50 |
+
headers = {
|
51 |
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.104 Safari/537.36'}
|
52 |
+
response = requests.get(url=url, headers=headers)
|
53 |
+
|
54 |
+
url_res = re.findall(r"'url': \\\"(.*?)\\\"}", response.text)
|
55 |
+
title_res = re.findall(r"'title': \\\"(.*?)\\\",\\n", response.text)
|
56 |
+
body_res = re.findall(r"\"contentText\":\"(.*?)\"", response.text)
|
57 |
+
baidu_res = [{"content": re.sub('<em>|</em>', '', '<a href="' + url + '">' + title + '</a> ' + body)} for
|
58 |
+
url, title, body in zip(url_res, title_res, body_res)]
|
59 |
+
del body_res, url_res, title_res
|
60 |
+
except Exception as e:
|
61 |
+
return Baidu.be_output("**ERROR**: " + str(e))
|
62 |
+
|
63 |
+
if not baidu_res:
|
64 |
+
return Baidu.be_output("")
|
65 |
+
|
66 |
+
df = pd.DataFrame(baidu_res)
|
67 |
+
if DEBUG: print(df, ":::::::::::::::::::::::::::::::::")
|
68 |
+
return df
|
69 |
+
|
agent/component/baidufanyi.py
ADDED
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#
|
2 |
+
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
3 |
+
#
|
4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5 |
+
# you may not use this file except in compliance with the License.
|
6 |
+
# You may obtain a copy of the License at
|
7 |
+
#
|
8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9 |
+
#
|
10 |
+
# Unless required by applicable law or agreed to in writing, software
|
11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 |
+
# See the License for the specific language governing permissions and
|
14 |
+
# limitations under the License.
|
15 |
+
#
|
16 |
+
import random
|
17 |
+
from abc import ABC
|
18 |
+
import requests
|
19 |
+
from agent.component.base import ComponentBase, ComponentParamBase
|
20 |
+
from hashlib import md5
|
21 |
+
|
22 |
+
|
23 |
+
class BaiduFanyiParam(ComponentParamBase):
|
24 |
+
"""
|
25 |
+
Define the BaiduFanyi component parameters.
|
26 |
+
"""
|
27 |
+
|
28 |
+
def __init__(self):
|
29 |
+
super().__init__()
|
30 |
+
self.appid = "xxx"
|
31 |
+
self.secret_key = "xxx"
|
32 |
+
self.trans_type = 'translate'
|
33 |
+
self.parameters = []
|
34 |
+
self.source_lang = 'auto'
|
35 |
+
self.target_lang = 'auto'
|
36 |
+
self.domain = 'finance'
|
37 |
+
|
38 |
+
def check(self):
|
39 |
+
self.check_positive_integer(self.top_n, "Top N")
|
40 |
+
self.check_empty(self.appid, "BaiduFanyi APPID")
|
41 |
+
self.check_empty(self.secret_key, "BaiduFanyi Secret Key")
|
42 |
+
self.check_valid_value(self.trans_type, "Translate type", ['translate', 'fieldtranslate'])
|
43 |
+
self.check_valid_value(self.trans_type, "Translate domain",
|
44 |
+
['it', 'finance', 'machinery', 'senimed', 'novel', 'academic', 'aerospace', 'wiki',
|
45 |
+
'news', 'law', 'contract'])
|
46 |
+
self.check_valid_value(self.source_lang, "Source language",
|
47 |
+
['auto', 'zh', 'en', 'yue', 'wyw', 'jp', 'kor', 'fra', 'spa', 'th', 'ara', 'ru', 'pt',
|
48 |
+
'de', 'it', 'el', 'nl', 'pl', 'bul', 'est', 'dan', 'fin', 'cs', 'rom', 'slo', 'swe',
|
49 |
+
'hu', 'cht', 'vie'])
|
50 |
+
self.check_valid_value(self.target_lang, "Target language",
|
51 |
+
['auto', 'zh', 'en', 'yue', 'wyw', 'jp', 'kor', 'fra', 'spa', 'th', 'ara', 'ru', 'pt',
|
52 |
+
'de', 'it', 'el', 'nl', 'pl', 'bul', 'est', 'dan', 'fin', 'cs', 'rom', 'slo', 'swe',
|
53 |
+
'hu', 'cht', 'vie'])
|
54 |
+
self.check_valid_value(self.domain, "Translate field",
|
55 |
+
['it', 'finance', 'machinery', 'senimed', 'novel', 'academic', 'aerospace', 'wiki',
|
56 |
+
'news', 'law', 'contract'])
|
57 |
+
|
58 |
+
|
59 |
+
class BaiduFanyi(ComponentBase, ABC):
|
60 |
+
component_name = "BaiduFanyi"
|
61 |
+
|
62 |
+
def _run(self, history, **kwargs):
|
63 |
+
|
64 |
+
ans = self.get_input()
|
65 |
+
ans = " - ".join(ans["content"]) if "content" in ans else ""
|
66 |
+
if not ans:
|
67 |
+
return BaiduFanyi.be_output("")
|
68 |
+
|
69 |
+
try:
|
70 |
+
source_lang = self._param.source_lang
|
71 |
+
target_lang = self._param.target_lang
|
72 |
+
appid = self._param.appid
|
73 |
+
salt = random.randint(32768, 65536)
|
74 |
+
secret_key = self._param.secret_key
|
75 |
+
|
76 |
+
if self._param.trans_type == 'translate':
|
77 |
+
sign = md5((appid + ans + salt + secret_key).encode('utf-8')).hexdigest()
|
78 |
+
url = 'http://api.fanyi.baidu.com/api/trans/vip/translate?' + 'q=' + ans + '&from=' + source_lang + '&to=' + target_lang + '&appid=' + appid + '&salt=' + salt + '&sign=' + sign
|
79 |
+
headers = {"Content-Type": "application/x-www-form-urlencoded"}
|
80 |
+
response = requests.post(url=url, headers=headers).json()
|
81 |
+
|
82 |
+
if response.get('error_code'):
|
83 |
+
BaiduFanyi.be_output("**Error**:" + response['error_msg'])
|
84 |
+
|
85 |
+
return BaiduFanyi.be_output(response['trans_result'][0]['dst'])
|
86 |
+
elif self._param.trans_type == 'fieldtranslate':
|
87 |
+
domain = self._param.domain
|
88 |
+
sign = md5((appid + ans + salt + domain + secret_key).encode('utf-8')).hexdigest()
|
89 |
+
url = 'http://api.fanyi.baidu.com/api/trans/vip/fieldtranslate?' + 'q=' + ans + '&from=' + source_lang + '&to=' + target_lang + '&appid=' + appid + '&salt=' + salt + '&domain=' + domain + '&sign=' + sign
|
90 |
+
headers = {"Content-Type": "application/x-www-form-urlencoded"}
|
91 |
+
response = requests.post(url=url, headers=headers).json()
|
92 |
+
|
93 |
+
if response.get('error_code'):
|
94 |
+
BaiduFanyi.be_output("**Error**:" + response['error_msg'])
|
95 |
+
|
96 |
+
return BaiduFanyi.be_output(response['trans_result'][0]['dst'])
|
97 |
+
|
98 |
+
except Exception as e:
|
99 |
+
BaiduFanyi.be_output("**Error**:" + str(e))
|
agent/component/base.py
ADDED
@@ -0,0 +1,494 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#
|
2 |
+
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
3 |
+
#
|
4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5 |
+
# you may not use this file except in compliance with the License.
|
6 |
+
# You may obtain a copy of the License at
|
7 |
+
#
|
8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9 |
+
#
|
10 |
+
# Unless required by applicable law or agreed to in writing, software
|
11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 |
+
# See the License for the specific language governing permissions and
|
14 |
+
# limitations under the License.
|
15 |
+
#
|
16 |
+
from abc import ABC
|
17 |
+
import builtins
|
18 |
+
import json
|
19 |
+
import os
|
20 |
+
from copy import deepcopy
|
21 |
+
from functools import partial
|
22 |
+
from typing import List, Dict, Tuple, Union
|
23 |
+
|
24 |
+
import pandas as pd
|
25 |
+
|
26 |
+
from agent import settings
|
27 |
+
from agent.settings import flow_logger, DEBUG
|
28 |
+
|
29 |
+
_FEEDED_DEPRECATED_PARAMS = "_feeded_deprecated_params"
|
30 |
+
_DEPRECATED_PARAMS = "_deprecated_params"
|
31 |
+
_USER_FEEDED_PARAMS = "_user_feeded_params"
|
32 |
+
_IS_RAW_CONF = "_is_raw_conf"
|
33 |
+
|
34 |
+
|
35 |
+
class ComponentParamBase(ABC):
|
36 |
+
def __init__(self):
|
37 |
+
self.output_var_name = "output"
|
38 |
+
self.message_history_window_size = 22
|
39 |
+
|
40 |
+
def set_name(self, name: str):
|
41 |
+
self._name = name
|
42 |
+
return self
|
43 |
+
|
44 |
+
def check(self):
|
45 |
+
raise NotImplementedError("Parameter Object should be checked.")
|
46 |
+
|
47 |
+
@classmethod
|
48 |
+
def _get_or_init_deprecated_params_set(cls):
|
49 |
+
if not hasattr(cls, _DEPRECATED_PARAMS):
|
50 |
+
setattr(cls, _DEPRECATED_PARAMS, set())
|
51 |
+
return getattr(cls, _DEPRECATED_PARAMS)
|
52 |
+
|
53 |
+
def _get_or_init_feeded_deprecated_params_set(self, conf=None):
|
54 |
+
if not hasattr(self, _FEEDED_DEPRECATED_PARAMS):
|
55 |
+
if conf is None:
|
56 |
+
setattr(self, _FEEDED_DEPRECATED_PARAMS, set())
|
57 |
+
else:
|
58 |
+
setattr(
|
59 |
+
self,
|
60 |
+
_FEEDED_DEPRECATED_PARAMS,
|
61 |
+
set(conf[_FEEDED_DEPRECATED_PARAMS]),
|
62 |
+
)
|
63 |
+
return getattr(self, _FEEDED_DEPRECATED_PARAMS)
|
64 |
+
|
65 |
+
def _get_or_init_user_feeded_params_set(self, conf=None):
|
66 |
+
if not hasattr(self, _USER_FEEDED_PARAMS):
|
67 |
+
if conf is None:
|
68 |
+
setattr(self, _USER_FEEDED_PARAMS, set())
|
69 |
+
else:
|
70 |
+
setattr(self, _USER_FEEDED_PARAMS, set(conf[_USER_FEEDED_PARAMS]))
|
71 |
+
return getattr(self, _USER_FEEDED_PARAMS)
|
72 |
+
|
73 |
+
def get_user_feeded(self):
|
74 |
+
return self._get_or_init_user_feeded_params_set()
|
75 |
+
|
76 |
+
def get_feeded_deprecated_params(self):
|
77 |
+
return self._get_or_init_feeded_deprecated_params_set()
|
78 |
+
|
79 |
+
@property
|
80 |
+
def _deprecated_params_set(self):
|
81 |
+
return {name: True for name in self.get_feeded_deprecated_params()}
|
82 |
+
|
83 |
+
def __str__(self):
|
84 |
+
|
85 |
+
return json.dumps(self.as_dict(), ensure_ascii=False)
|
86 |
+
|
87 |
+
def as_dict(self):
|
88 |
+
def _recursive_convert_obj_to_dict(obj):
|
89 |
+
ret_dict = {}
|
90 |
+
for attr_name in list(obj.__dict__):
|
91 |
+
if attr_name in [_FEEDED_DEPRECATED_PARAMS, _DEPRECATED_PARAMS, _USER_FEEDED_PARAMS, _IS_RAW_CONF]:
|
92 |
+
continue
|
93 |
+
# get attr
|
94 |
+
attr = getattr(obj, attr_name)
|
95 |
+
if isinstance(attr, pd.DataFrame):
|
96 |
+
ret_dict[attr_name] = attr.to_dict()
|
97 |
+
continue
|
98 |
+
if attr and type(attr).__name__ not in dir(builtins):
|
99 |
+
ret_dict[attr_name] = _recursive_convert_obj_to_dict(attr)
|
100 |
+
else:
|
101 |
+
ret_dict[attr_name] = attr
|
102 |
+
|
103 |
+
return ret_dict
|
104 |
+
|
105 |
+
return _recursive_convert_obj_to_dict(self)
|
106 |
+
|
107 |
+
def update(self, conf, allow_redundant=False):
|
108 |
+
update_from_raw_conf = conf.get(_IS_RAW_CONF, True)
|
109 |
+
if update_from_raw_conf:
|
110 |
+
deprecated_params_set = self._get_or_init_deprecated_params_set()
|
111 |
+
feeded_deprecated_params_set = (
|
112 |
+
self._get_or_init_feeded_deprecated_params_set()
|
113 |
+
)
|
114 |
+
user_feeded_params_set = self._get_or_init_user_feeded_params_set()
|
115 |
+
setattr(self, _IS_RAW_CONF, False)
|
116 |
+
else:
|
117 |
+
feeded_deprecated_params_set = (
|
118 |
+
self._get_or_init_feeded_deprecated_params_set(conf)
|
119 |
+
)
|
120 |
+
user_feeded_params_set = self._get_or_init_user_feeded_params_set(conf)
|
121 |
+
|
122 |
+
def _recursive_update_param(param, config, depth, prefix):
|
123 |
+
if depth > settings.PARAM_MAXDEPTH:
|
124 |
+
raise ValueError("Param define nesting too deep!!!, can not parse it")
|
125 |
+
|
126 |
+
inst_variables = param.__dict__
|
127 |
+
redundant_attrs = []
|
128 |
+
for config_key, config_value in config.items():
|
129 |
+
# redundant attr
|
130 |
+
if config_key not in inst_variables:
|
131 |
+
if not update_from_raw_conf and config_key.startswith("_"):
|
132 |
+
setattr(param, config_key, config_value)
|
133 |
+
else:
|
134 |
+
setattr(param, config_key, config_value)
|
135 |
+
# redundant_attrs.append(config_key)
|
136 |
+
continue
|
137 |
+
|
138 |
+
full_config_key = f"{prefix}{config_key}"
|
139 |
+
|
140 |
+
if update_from_raw_conf:
|
141 |
+
# add user feeded params
|
142 |
+
user_feeded_params_set.add(full_config_key)
|
143 |
+
|
144 |
+
# update user feeded deprecated param set
|
145 |
+
if full_config_key in deprecated_params_set:
|
146 |
+
feeded_deprecated_params_set.add(full_config_key)
|
147 |
+
|
148 |
+
# supported attr
|
149 |
+
attr = getattr(param, config_key)
|
150 |
+
if type(attr).__name__ in dir(builtins) or attr is None:
|
151 |
+
setattr(param, config_key, config_value)
|
152 |
+
|
153 |
+
else:
|
154 |
+
# recursive set obj attr
|
155 |
+
sub_params = _recursive_update_param(
|
156 |
+
attr, config_value, depth + 1, prefix=f"{prefix}{config_key}."
|
157 |
+
)
|
158 |
+
setattr(param, config_key, sub_params)
|
159 |
+
|
160 |
+
if not allow_redundant and redundant_attrs:
|
161 |
+
raise ValueError(
|
162 |
+
f"cpn `{getattr(self, '_name', type(self))}` has redundant parameters: `{[redundant_attrs]}`"
|
163 |
+
)
|
164 |
+
|
165 |
+
return param
|
166 |
+
|
167 |
+
return _recursive_update_param(param=self, config=conf, depth=0, prefix="")
|
168 |
+
|
169 |
+
def extract_not_builtin(self):
|
170 |
+
def _get_not_builtin_types(obj):
|
171 |
+
ret_dict = {}
|
172 |
+
for variable in obj.__dict__:
|
173 |
+
attr = getattr(obj, variable)
|
174 |
+
if attr and type(attr).__name__ not in dir(builtins):
|
175 |
+
ret_dict[variable] = _get_not_builtin_types(attr)
|
176 |
+
|
177 |
+
return ret_dict
|
178 |
+
|
179 |
+
return _get_not_builtin_types(self)
|
180 |
+
|
181 |
+
def validate(self):
|
182 |
+
self.builtin_types = dir(builtins)
|
183 |
+
self.func = {
|
184 |
+
"ge": self._greater_equal_than,
|
185 |
+
"le": self._less_equal_than,
|
186 |
+
"in": self._in,
|
187 |
+
"not_in": self._not_in,
|
188 |
+
"range": self._range,
|
189 |
+
}
|
190 |
+
home_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
|
191 |
+
param_validation_path_prefix = home_dir + "/param_validation/"
|
192 |
+
|
193 |
+
param_name = type(self).__name__
|
194 |
+
param_validation_path = "/".join(
|
195 |
+
[param_validation_path_prefix, param_name + ".json"]
|
196 |
+
)
|
197 |
+
|
198 |
+
validation_json = None
|
199 |
+
|
200 |
+
try:
|
201 |
+
with open(param_validation_path, "r") as fin:
|
202 |
+
validation_json = json.loads(fin.read())
|
203 |
+
except BaseException:
|
204 |
+
return
|
205 |
+
|
206 |
+
self._validate_param(self, validation_json)
|
207 |
+
|
208 |
+
def _validate_param(self, param_obj, validation_json):
|
209 |
+
default_section = type(param_obj).__name__
|
210 |
+
var_list = param_obj.__dict__
|
211 |
+
|
212 |
+
for variable in var_list:
|
213 |
+
attr = getattr(param_obj, variable)
|
214 |
+
|
215 |
+
if type(attr).__name__ in self.builtin_types or attr is None:
|
216 |
+
if variable not in validation_json:
|
217 |
+
continue
|
218 |
+
|
219 |
+
validation_dict = validation_json[default_section][variable]
|
220 |
+
value = getattr(param_obj, variable)
|
221 |
+
value_legal = False
|
222 |
+
|
223 |
+
for op_type in validation_dict:
|
224 |
+
if self.func[op_type](value, validation_dict[op_type]):
|
225 |
+
value_legal = True
|
226 |
+
break
|
227 |
+
|
228 |
+
if not value_legal:
|
229 |
+
raise ValueError(
|
230 |
+
"Plase check runtime conf, {} = {} does not match user-parameter restriction".format(
|
231 |
+
variable, value
|
232 |
+
)
|
233 |
+
)
|
234 |
+
|
235 |
+
elif variable in validation_json:
|
236 |
+
self._validate_param(attr, validation_json)
|
237 |
+
|
238 |
+
@staticmethod
|
239 |
+
def check_string(param, descr):
|
240 |
+
if type(param).__name__ not in ["str"]:
|
241 |
+
raise ValueError(
|
242 |
+
descr + " {} not supported, should be string type".format(param)
|
243 |
+
)
|
244 |
+
|
245 |
+
@staticmethod
|
246 |
+
def check_empty(param, descr):
|
247 |
+
if not param:
|
248 |
+
raise ValueError(
|
249 |
+
descr + " does not support empty value."
|
250 |
+
)
|
251 |
+
|
252 |
+
@staticmethod
|
253 |
+
def check_positive_integer(param, descr):
|
254 |
+
if type(param).__name__ not in ["int", "long"] or param <= 0:
|
255 |
+
raise ValueError(
|
256 |
+
descr + " {} not supported, should be positive integer".format(param)
|
257 |
+
)
|
258 |
+
|
259 |
+
@staticmethod
|
260 |
+
def check_positive_number(param, descr):
|
261 |
+
if type(param).__name__ not in ["float", "int", "long"] or param <= 0:
|
262 |
+
raise ValueError(
|
263 |
+
descr + " {} not supported, should be positive numeric".format(param)
|
264 |
+
)
|
265 |
+
|
266 |
+
@staticmethod
|
267 |
+
def check_nonnegative_number(param, descr):
|
268 |
+
if type(param).__name__ not in ["float", "int", "long"] or param < 0:
|
269 |
+
raise ValueError(
|
270 |
+
descr
|
271 |
+
+ " {} not supported, should be non-negative numeric".format(param)
|
272 |
+
)
|
273 |
+
|
274 |
+
@staticmethod
|
275 |
+
def check_decimal_float(param, descr):
|
276 |
+
if type(param).__name__ not in ["float", "int"] or param < 0 or param > 1:
|
277 |
+
raise ValueError(
|
278 |
+
descr
|
279 |
+
+ " {} not supported, should be a float number in range [0, 1]".format(
|
280 |
+
param
|
281 |
+
)
|
282 |
+
)
|
283 |
+
|
284 |
+
@staticmethod
|
285 |
+
def check_boolean(param, descr):
|
286 |
+
if type(param).__name__ != "bool":
|
287 |
+
raise ValueError(
|
288 |
+
descr + " {} not supported, should be bool type".format(param)
|
289 |
+
)
|
290 |
+
|
291 |
+
@staticmethod
|
292 |
+
def check_open_unit_interval(param, descr):
|
293 |
+
if type(param).__name__ not in ["float"] or param <= 0 or param >= 1:
|
294 |
+
raise ValueError(
|
295 |
+
descr + " should be a numeric number between 0 and 1 exclusively"
|
296 |
+
)
|
297 |
+
|
298 |
+
@staticmethod
|
299 |
+
def check_valid_value(param, descr, valid_values):
|
300 |
+
if param not in valid_values:
|
301 |
+
raise ValueError(
|
302 |
+
descr
|
303 |
+
+ " {} is not supported, it should be in {}".format(param, valid_values)
|
304 |
+
)
|
305 |
+
|
306 |
+
@staticmethod
|
307 |
+
def check_defined_type(param, descr, types):
|
308 |
+
if type(param).__name__ not in types:
|
309 |
+
raise ValueError(
|
310 |
+
descr + " {} not supported, should be one of {}".format(param, types)
|
311 |
+
)
|
312 |
+
|
313 |
+
@staticmethod
|
314 |
+
def check_and_change_lower(param, valid_list, descr=""):
|
315 |
+
if type(param).__name__ != "str":
|
316 |
+
raise ValueError(
|
317 |
+
descr
|
318 |
+
+ " {} not supported, should be one of {}".format(param, valid_list)
|
319 |
+
)
|
320 |
+
|
321 |
+
lower_param = param.lower()
|
322 |
+
if lower_param in valid_list:
|
323 |
+
return lower_param
|
324 |
+
else:
|
325 |
+
raise ValueError(
|
326 |
+
descr
|
327 |
+
+ " {} not supported, should be one of {}".format(param, valid_list)
|
328 |
+
)
|
329 |
+
|
330 |
+
@staticmethod
|
331 |
+
def _greater_equal_than(value, limit):
|
332 |
+
return value >= limit - settings.FLOAT_ZERO
|
333 |
+
|
334 |
+
@staticmethod
|
335 |
+
def _less_equal_than(value, limit):
|
336 |
+
return value <= limit + settings.FLOAT_ZERO
|
337 |
+
|
338 |
+
@staticmethod
|
339 |
+
def _range(value, ranges):
|
340 |
+
in_range = False
|
341 |
+
for left_limit, right_limit in ranges:
|
342 |
+
if (
|
343 |
+
left_limit - settings.FLOAT_ZERO
|
344 |
+
<= value
|
345 |
+
<= right_limit + settings.FLOAT_ZERO
|
346 |
+
):
|
347 |
+
in_range = True
|
348 |
+
break
|
349 |
+
|
350 |
+
return in_range
|
351 |
+
|
352 |
+
@staticmethod
|
353 |
+
def _in(value, right_value_list):
|
354 |
+
return value in right_value_list
|
355 |
+
|
356 |
+
@staticmethod
|
357 |
+
def _not_in(value, wrong_value_list):
|
358 |
+
return value not in wrong_value_list
|
359 |
+
|
360 |
+
def _warn_deprecated_param(self, param_name, descr):
|
361 |
+
if self._deprecated_params_set.get(param_name):
|
362 |
+
flow_logger.warning(
|
363 |
+
f"{descr} {param_name} is deprecated and ignored in this version."
|
364 |
+
)
|
365 |
+
|
366 |
+
def _warn_to_deprecate_param(self, param_name, descr, new_param):
|
367 |
+
if self._deprecated_params_set.get(param_name):
|
368 |
+
flow_logger.warning(
|
369 |
+
f"{descr} {param_name} will be deprecated in future release; "
|
370 |
+
f"please use {new_param} instead."
|
371 |
+
)
|
372 |
+
return True
|
373 |
+
return False
|
374 |
+
|
375 |
+
|
376 |
+
class ComponentBase(ABC):
|
377 |
+
component_name: str
|
378 |
+
|
379 |
+
def __str__(self):
|
380 |
+
"""
|
381 |
+
{
|
382 |
+
"component_name": "Begin",
|
383 |
+
"params": {}
|
384 |
+
}
|
385 |
+
"""
|
386 |
+
return """{{
|
387 |
+
"component_name": "{}",
|
388 |
+
"params": {}
|
389 |
+
}}""".format(self.component_name,
|
390 |
+
self._param
|
391 |
+
)
|
392 |
+
|
393 |
+
def __init__(self, canvas, id, param: ComponentParamBase):
|
394 |
+
self._canvas = canvas
|
395 |
+
self._id = id
|
396 |
+
self._param = param
|
397 |
+
self._param.check()
|
398 |
+
|
399 |
+
def run(self, history, **kwargs):
|
400 |
+
flow_logger.info("{}, history: {}, kwargs: {}".format(self, json.dumps(history, ensure_ascii=False),
|
401 |
+
json.dumps(kwargs, ensure_ascii=False)))
|
402 |
+
try:
|
403 |
+
res = self._run(history, **kwargs)
|
404 |
+
self.set_output(res)
|
405 |
+
except Exception as e:
|
406 |
+
self.set_output(pd.DataFrame([{"content": str(e)}]))
|
407 |
+
raise e
|
408 |
+
|
409 |
+
return res
|
410 |
+
|
411 |
+
def _run(self, history, **kwargs):
|
412 |
+
raise NotImplementedError()
|
413 |
+
|
414 |
+
def output(self, allow_partial=True) -> Tuple[str, Union[pd.DataFrame, partial]]:
|
415 |
+
o = getattr(self._param, self._param.output_var_name)
|
416 |
+
if not isinstance(o, partial) and not isinstance(o, pd.DataFrame):
|
417 |
+
if not isinstance(o, list): o = [o]
|
418 |
+
o = pd.DataFrame(o)
|
419 |
+
|
420 |
+
if allow_partial or not isinstance(o, partial):
|
421 |
+
if not isinstance(o, partial) and not isinstance(o, pd.DataFrame):
|
422 |
+
return pd.DataFrame(o if isinstance(o, list) else [o])
|
423 |
+
return self._param.output_var_name, o
|
424 |
+
|
425 |
+
outs = None
|
426 |
+
for oo in o():
|
427 |
+
if not isinstance(oo, pd.DataFrame):
|
428 |
+
outs = pd.DataFrame(oo if isinstance(oo, list) else [oo])
|
429 |
+
else: outs = oo
|
430 |
+
return self._param.output_var_name, outs
|
431 |
+
|
432 |
+
def reset(self):
|
433 |
+
setattr(self._param, self._param.output_var_name, None)
|
434 |
+
|
435 |
+
def set_output(self, v: pd.DataFrame):
|
436 |
+
setattr(self._param, self._param.output_var_name, v)
|
437 |
+
|
438 |
+
def get_input(self):
|
439 |
+
upstream_outs = []
|
440 |
+
reversed_cpnts = []
|
441 |
+
if len(self._canvas.path) > 1:
|
442 |
+
reversed_cpnts.extend(self._canvas.path[-2])
|
443 |
+
reversed_cpnts.extend(self._canvas.path[-1])
|
444 |
+
|
445 |
+
if DEBUG: print(self.component_name, reversed_cpnts[::-1])
|
446 |
+
for u in reversed_cpnts[::-1]:
|
447 |
+
if self.get_component_name(u) in ["switch"]: continue
|
448 |
+
if self.component_name.lower() == "generate" and self.get_component_name(u) == "retrieval":
|
449 |
+
o = self._canvas.get_component(u)["obj"].output(allow_partial=False)[1]
|
450 |
+
if o is not None:
|
451 |
+
upstream_outs.append(o)
|
452 |
+
continue
|
453 |
+
if u not in self._canvas.get_component(self._id)["upstream"]: continue
|
454 |
+
if self.component_name.lower().find("switch") < 0 \
|
455 |
+
and self.get_component_name(u) in ["relevant", "categorize"]:
|
456 |
+
continue
|
457 |
+
if u.lower().find("answer") >= 0:
|
458 |
+
for r, c in self._canvas.history[::-1]:
|
459 |
+
if r == "user":
|
460 |
+
upstream_outs.append(pd.DataFrame([{"content": c}]))
|
461 |
+
break
|
462 |
+
break
|
463 |
+
if self.component_name.lower().find("answer") >= 0:
|
464 |
+
if self.get_component_name(u) in ["relevant"]:
|
465 |
+
continue
|
466 |
+
else:
|
467 |
+
o = self._canvas.get_component(u)["obj"].output(allow_partial=False)[1]
|
468 |
+
if o is not None:
|
469 |
+
upstream_outs.append(o)
|
470 |
+
break
|
471 |
+
|
472 |
+
if upstream_outs:
|
473 |
+
df = pd.concat(upstream_outs, ignore_index=True)
|
474 |
+
if "content" in df:
|
475 |
+
df = df.drop_duplicates(subset=['content']).reset_index(drop=True)
|
476 |
+
return df
|
477 |
+
return pd.DataFrame()
|
478 |
+
|
479 |
+
def get_stream_input(self):
|
480 |
+
reversed_cpnts = []
|
481 |
+
if len(self._canvas.path) > 1:
|
482 |
+
reversed_cpnts.extend(self._canvas.path[-2])
|
483 |
+
reversed_cpnts.extend(self._canvas.path[-1])
|
484 |
+
|
485 |
+
for u in reversed_cpnts[::-1]:
|
486 |
+
if self.get_component_name(u) in ["switch", "answer"]: continue
|
487 |
+
return self._canvas.get_component(u)["obj"].output()[1]
|
488 |
+
|
489 |
+
@staticmethod
|
490 |
+
def be_output(v):
|
491 |
+
return pd.DataFrame([{"content": v}])
|
492 |
+
|
493 |
+
def get_component_name(self, cpn_id):
|
494 |
+
return self._canvas.get_component(cpn_id)["obj"].component_name.lower()
|
agent/component/begin.py
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#
|
2 |
+
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
3 |
+
#
|
4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5 |
+
# you may not use this file except in compliance with the License.
|
6 |
+
# You may obtain a copy of the License at
|
7 |
+
#
|
8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9 |
+
#
|
10 |
+
# Unless required by applicable law or agreed to in writing, software
|
11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 |
+
# See the License for the specific language governing permissions and
|
14 |
+
# limitations under the License.
|
15 |
+
#
|
16 |
+
from functools import partial
|
17 |
+
import pandas as pd
|
18 |
+
from agent.component.base import ComponentBase, ComponentParamBase
|
19 |
+
|
20 |
+
|
21 |
+
class BeginParam(ComponentParamBase):
|
22 |
+
|
23 |
+
"""
|
24 |
+
Define the Begin component parameters.
|
25 |
+
"""
|
26 |
+
def __init__(self):
|
27 |
+
super().__init__()
|
28 |
+
self.prologue = "Hi! I'm your smart assistant. What can I do for you?"
|
29 |
+
|
30 |
+
def check(self):
|
31 |
+
return True
|
32 |
+
|
33 |
+
|
34 |
+
class Begin(ComponentBase):
|
35 |
+
component_name = "Begin"
|
36 |
+
|
37 |
+
def _run(self, history, **kwargs):
|
38 |
+
if kwargs.get("stream"):
|
39 |
+
return partial(self.stream_output)
|
40 |
+
return pd.DataFrame([{"content": self._param.prologue}])
|
41 |
+
|
42 |
+
def stream_output(self):
|
43 |
+
res = {"content": self._param.prologue}
|
44 |
+
yield res
|
45 |
+
self.set_output(res)
|
46 |
+
|
47 |
+
|
48 |
+
|
agent/component/bing.py
ADDED
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#
|
2 |
+
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
3 |
+
#
|
4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5 |
+
# you may not use this file except in compliance with the License.
|
6 |
+
# You may obtain a copy of the License at
|
7 |
+
#
|
8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9 |
+
#
|
10 |
+
# Unless required by applicable law or agreed to in writing, software
|
11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 |
+
# See the License for the specific language governing permissions and
|
14 |
+
# limitations under the License.
|
15 |
+
#
|
16 |
+
from abc import ABC
|
17 |
+
import requests
|
18 |
+
import pandas as pd
|
19 |
+
from agent.settings import DEBUG
|
20 |
+
from agent.component.base import ComponentBase, ComponentParamBase
|
21 |
+
|
22 |
+
|
23 |
+
class BingParam(ComponentParamBase):
|
24 |
+
"""
|
25 |
+
Define the Bing component parameters.
|
26 |
+
"""
|
27 |
+
|
28 |
+
def __init__(self):
|
29 |
+
super().__init__()
|
30 |
+
self.top_n = 10
|
31 |
+
self.channel = "Webpages"
|
32 |
+
self.api_key = "YOUR_ACCESS_KEY"
|
33 |
+
self.country = "CN"
|
34 |
+
self.language = "en"
|
35 |
+
|
36 |
+
def check(self):
|
37 |
+
self.check_positive_integer(self.top_n, "Top N")
|
38 |
+
self.check_valid_value(self.channel, "Bing Web Search or Bing News", ["Webpages", "News"])
|
39 |
+
self.check_empty(self.api_key, "Bing subscription key")
|
40 |
+
self.check_valid_value(self.country, "Bing Country",
|
41 |
+
['AR', 'AU', 'AT', 'BE', 'BR', 'CA', 'CL', 'DK', 'FI', 'FR', 'DE', 'HK', 'IN', 'ID',
|
42 |
+
'IT', 'JP', 'KR', 'MY', 'MX', 'NL', 'NZ', 'NO', 'CN', 'PL', 'PT', 'PH', 'RU', 'SA',
|
43 |
+
'ZA', 'ES', 'SE', 'CH', 'TW', 'TR', 'GB', 'US'])
|
44 |
+
self.check_valid_value(self.language, "Bing Languages",
|
45 |
+
['ar', 'eu', 'bn', 'bg', 'ca', 'ns', 'nt', 'hr', 'cs', 'da', 'nl', 'en', 'gb', 'et',
|
46 |
+
'fi', 'fr', 'gl', 'de', 'gu', 'he', 'hi', 'hu', 'is', 'it', 'jp', 'kn', 'ko', 'lv',
|
47 |
+
'lt', 'ms', 'ml', 'mr', 'nb', 'pl', 'br', 'pt', 'pa', 'ro', 'ru', 'sr', 'sk', 'sl',
|
48 |
+
'es', 'sv', 'ta', 'te', 'th', 'tr', 'uk', 'vi'])
|
49 |
+
|
50 |
+
|
51 |
+
class Bing(ComponentBase, ABC):
|
52 |
+
component_name = "Bing"
|
53 |
+
|
54 |
+
def _run(self, history, **kwargs):
|
55 |
+
ans = self.get_input()
|
56 |
+
ans = " - ".join(ans["content"]) if "content" in ans else ""
|
57 |
+
if not ans:
|
58 |
+
return Bing.be_output("")
|
59 |
+
|
60 |
+
try:
|
61 |
+
headers = {"Ocp-Apim-Subscription-Key": self._param.api_key, 'Accept-Language': self._param.language}
|
62 |
+
params = {"q": ans, "textDecorations": True, "textFormat": "HTML", "cc": self._param.country,
|
63 |
+
"answerCount": 1, "promote": self._param.channel}
|
64 |
+
if self._param.channel == "Webpages":
|
65 |
+
response = requests.get("https://api.bing.microsoft.com/v7.0/search", headers=headers, params=params)
|
66 |
+
response.raise_for_status()
|
67 |
+
search_results = response.json()
|
68 |
+
bing_res = [{"content": '<a href="' + i["url"] + '">' + i["name"] + '</a> ' + i["snippet"]} for i in
|
69 |
+
search_results["webPages"]["value"]]
|
70 |
+
elif self._param.channel == "News":
|
71 |
+
response = requests.get("https://api.bing.microsoft.com/v7.0/news/search", headers=headers,
|
72 |
+
params=params)
|
73 |
+
response.raise_for_status()
|
74 |
+
search_results = response.json()
|
75 |
+
bing_res = [{"content": '<a href="' + i["url"] + '">' + i["name"] + '</a> ' + i["description"]} for i
|
76 |
+
in search_results['news']['value']]
|
77 |
+
except Exception as e:
|
78 |
+
return Bing.be_output("**ERROR**: " + str(e))
|
79 |
+
|
80 |
+
if not bing_res:
|
81 |
+
return Bing.be_output("")
|
82 |
+
|
83 |
+
df = pd.DataFrame(bing_res)
|
84 |
+
if DEBUG: print(df, ":::::::::::::::::::::::::::::::::")
|
85 |
+
return df
|
agent/component/categorize.py
ADDED
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#
|
2 |
+
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
3 |
+
#
|
4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5 |
+
# you may not use this file except in compliance with the License.
|
6 |
+
# You may obtain a copy of the License at
|
7 |
+
#
|
8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9 |
+
#
|
10 |
+
# Unless required by applicable law or agreed to in writing, software
|
11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 |
+
# See the License for the specific language governing permissions and
|
14 |
+
# limitations under the License.
|
15 |
+
#
|
16 |
+
from abc import ABC
|
17 |
+
from api.db import LLMType
|
18 |
+
from api.db.services.llm_service import LLMBundle
|
19 |
+
from agent.component import GenerateParam, Generate
|
20 |
+
from agent.settings import DEBUG
|
21 |
+
|
22 |
+
|
23 |
+
class CategorizeParam(GenerateParam):
|
24 |
+
|
25 |
+
"""
|
26 |
+
Define the Categorize component parameters.
|
27 |
+
"""
|
28 |
+
def __init__(self):
|
29 |
+
super().__init__()
|
30 |
+
self.category_description = {}
|
31 |
+
self.prompt = ""
|
32 |
+
|
33 |
+
def check(self):
|
34 |
+
super().check()
|
35 |
+
self.check_empty(self.category_description, "[Categorize] Category examples")
|
36 |
+
for k, v in self.category_description.items():
|
37 |
+
if not k: raise ValueError(f"[Categorize] Category name can not be empty!")
|
38 |
+
if not v.get("to"): raise ValueError(f"[Categorize] 'To' of category {k} can not be empty!")
|
39 |
+
|
40 |
+
def get_prompt(self):
|
41 |
+
cate_lines = []
|
42 |
+
for c, desc in self.category_description.items():
|
43 |
+
for l in desc.get("examples", "").split("\n"):
|
44 |
+
if not l: continue
|
45 |
+
cate_lines.append("Question: {}\tCategory: {}".format(l, c))
|
46 |
+
descriptions = []
|
47 |
+
for c, desc in self.category_description.items():
|
48 |
+
if desc.get("description"):
|
49 |
+
descriptions.append(
|
50 |
+
"--------------------\nCategory: {}\nDescription: {}\n".format(c, desc["description"]))
|
51 |
+
|
52 |
+
self.prompt = """
|
53 |
+
You're a text classifier. You need to categorize the user’s questions into {} categories,
|
54 |
+
namely: {}
|
55 |
+
Here's description of each category:
|
56 |
+
{}
|
57 |
+
|
58 |
+
You could learn from the following examples:
|
59 |
+
{}
|
60 |
+
You could learn from the above examples.
|
61 |
+
Just mention the category names, no need for any additional words.
|
62 |
+
""".format(
|
63 |
+
len(self.category_description.keys()),
|
64 |
+
"/".join(list(self.category_description.keys())),
|
65 |
+
"\n".join(descriptions),
|
66 |
+
"- ".join(cate_lines)
|
67 |
+
)
|
68 |
+
return self.prompt
|
69 |
+
|
70 |
+
|
71 |
+
class Categorize(Generate, ABC):
|
72 |
+
component_name = "Categorize"
|
73 |
+
|
74 |
+
def _run(self, history, **kwargs):
|
75 |
+
input = self.get_input()
|
76 |
+
input = "Question: " + ("; ".join(input["content"]) if "content" in input else "") + "Category: "
|
77 |
+
chat_mdl = LLMBundle(self._canvas.get_tenant_id(), LLMType.CHAT, self._param.llm_id)
|
78 |
+
ans = chat_mdl.chat(self._param.get_prompt(), [{"role": "user", "content": input}],
|
79 |
+
self._param.gen_conf())
|
80 |
+
if DEBUG: print(ans, ":::::::::::::::::::::::::::::::::", input)
|
81 |
+
for c in self._param.category_description.keys():
|
82 |
+
if ans.lower().find(c.lower()) >= 0:
|
83 |
+
return Categorize.be_output(self._param.category_description[c]["to"])
|
84 |
+
|
85 |
+
return Categorize.be_output(self._param.category_description.items()[-1][1]["to"])
|
86 |
+
|
87 |
+
|
agent/component/cite.py
ADDED
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#
|
2 |
+
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
3 |
+
#
|
4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5 |
+
# you may not use this file except in compliance with the License.
|
6 |
+
# You may obtain a copy of the License at
|
7 |
+
#
|
8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9 |
+
#
|
10 |
+
# Unless required by applicable law or agreed to in writing, software
|
11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 |
+
# See the License for the specific language governing permissions and
|
14 |
+
# limitations under the License.
|
15 |
+
#
|
16 |
+
from abc import ABC
|
17 |
+
|
18 |
+
import pandas as pd
|
19 |
+
|
20 |
+
from api.db import LLMType
|
21 |
+
from api.db.services.knowledgebase_service import KnowledgebaseService
|
22 |
+
from api.db.services.llm_service import LLMBundle
|
23 |
+
from api.settings import retrievaler
|
24 |
+
from agent.component.base import ComponentBase, ComponentParamBase
|
25 |
+
|
26 |
+
|
27 |
+
class CiteParam(ComponentParamBase):
|
28 |
+
|
29 |
+
"""
|
30 |
+
Define the Retrieval component parameters.
|
31 |
+
"""
|
32 |
+
def __init__(self):
|
33 |
+
super().__init__()
|
34 |
+
self.cite_sources = []
|
35 |
+
|
36 |
+
def check(self):
|
37 |
+
self.check_empty(self.cite_source, "Please specify where you want to cite from.")
|
38 |
+
|
39 |
+
|
40 |
+
class Cite(ComponentBase, ABC):
|
41 |
+
component_name = "Cite"
|
42 |
+
|
43 |
+
def _run(self, history, **kwargs):
|
44 |
+
input = "\n- ".join(self.get_input()["content"])
|
45 |
+
sources = [self._canvas.get_component(cpn_id).output()[1] for cpn_id in self._param.cite_source]
|
46 |
+
query = []
|
47 |
+
for role, cnt in history[::-1][:self._param.message_history_window_size]:
|
48 |
+
if role != "user":continue
|
49 |
+
query.append(cnt)
|
50 |
+
query = "\n".join(query)
|
51 |
+
|
52 |
+
kbs = KnowledgebaseService.get_by_ids(self._param.kb_ids)
|
53 |
+
if not kbs:
|
54 |
+
raise ValueError("Can't find knowledgebases by {}".format(self._param.kb_ids))
|
55 |
+
embd_nms = list(set([kb.embd_id for kb in kbs]))
|
56 |
+
assert len(embd_nms) == 1, "Knowledge bases use different embedding models."
|
57 |
+
|
58 |
+
embd_mdl = LLMBundle(kbs[0].tenant_id, LLMType.EMBEDDING, embd_nms[0])
|
59 |
+
|
60 |
+
rerank_mdl = None
|
61 |
+
if self._param.rerank_id:
|
62 |
+
rerank_mdl = LLMBundle(kbs[0].tenant_id, LLMType.RERANK, self._param.rerank_id)
|
63 |
+
|
64 |
+
kbinfos = retrievaler.retrieval(query, embd_mdl, kbs[0].tenant_id, self._param.kb_ids,
|
65 |
+
1, self._param.top_n,
|
66 |
+
self._param.similarity_threshold, 1 - self._param.keywords_similarity_weight,
|
67 |
+
aggs=False, rerank_mdl=rerank_mdl)
|
68 |
+
|
69 |
+
if not kbinfos["chunks"]: return pd.DataFrame()
|
70 |
+
df = pd.DataFrame(kbinfos["chunks"])
|
71 |
+
df["content"] = df["content_with_weight"]
|
72 |
+
del df["content_with_weight"]
|
73 |
+
return df
|
74 |
+
|
75 |
+
|
agent/component/deepl.py
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#
|
2 |
+
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
3 |
+
#
|
4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5 |
+
# you may not use this file except in compliance with the License.
|
6 |
+
# You may obtain a copy of the License at
|
7 |
+
#
|
8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9 |
+
#
|
10 |
+
# Unless required by applicable law or agreed to in writing, software
|
11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 |
+
# See the License for the specific language governing permissions and
|
14 |
+
# limitations under the License.
|
15 |
+
#
|
16 |
+
from abc import ABC
|
17 |
+
import re
|
18 |
+
from agent.component.base import ComponentBase, ComponentParamBase
|
19 |
+
import deepl
|
20 |
+
|
21 |
+
|
22 |
+
class DeepLParam(ComponentParamBase):
|
23 |
+
"""
|
24 |
+
Define the DeepL component parameters.
|
25 |
+
"""
|
26 |
+
|
27 |
+
def __init__(self):
|
28 |
+
super().__init__()
|
29 |
+
self.auth_key = "xxx"
|
30 |
+
self.parameters = []
|
31 |
+
self.source_lang = 'ZH'
|
32 |
+
self.target_lang = 'EN-GB'
|
33 |
+
|
34 |
+
def check(self):
|
35 |
+
self.check_positive_integer(self.top_n, "Top N")
|
36 |
+
self.check_valid_value(self.source_lang, "Source language",
|
37 |
+
['AR', 'BG', 'CS', 'DA', 'DE', 'EL', 'EN', 'ES', 'ET', 'FI', 'FR', 'HU', 'ID', 'IT',
|
38 |
+
'JA', 'KO', 'LT', 'LV', 'NB', 'NL', 'PL', 'PT', 'RO', 'RU', 'SK', 'SL', 'SV', 'TR',
|
39 |
+
'UK', 'ZH'])
|
40 |
+
self.check_valid_value(self.target_lang, "Target language",
|
41 |
+
['AR', 'BG', 'CS', 'DA', 'DE', 'EL', 'EN-GB', 'EN-US', 'ES', 'ET', 'FI', 'FR', 'HU',
|
42 |
+
'ID', 'IT', 'JA', 'KO', 'LT', 'LV', 'NB', 'NL', 'PL', 'PT-BR', 'PT-PT', 'RO', 'RU',
|
43 |
+
'SK', 'SL', 'SV', 'TR', 'UK', 'ZH'])
|
44 |
+
|
45 |
+
|
46 |
+
class DeepL(ComponentBase, ABC):
|
47 |
+
component_name = "GitHub"
|
48 |
+
|
49 |
+
def _run(self, history, **kwargs):
|
50 |
+
ans = self.get_input()
|
51 |
+
ans = " - ".join(ans["content"]) if "content" in ans else ""
|
52 |
+
if not ans:
|
53 |
+
return DeepL.be_output("")
|
54 |
+
|
55 |
+
try:
|
56 |
+
translator = deepl.Translator(self._param.auth_key)
|
57 |
+
result = translator.translate_text(ans, source_lang=self._param.source_lang,
|
58 |
+
target_lang=self._param.target_lang)
|
59 |
+
|
60 |
+
return DeepL.be_output(result.text)
|
61 |
+
except Exception as e:
|
62 |
+
DeepL.be_output("**Error**:" + str(e))
|
agent/component/duckduckgo.py
ADDED
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#
|
2 |
+
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
3 |
+
#
|
4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5 |
+
# you may not use this file except in compliance with the License.
|
6 |
+
# You may obtain a copy of the License at
|
7 |
+
#
|
8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9 |
+
#
|
10 |
+
# Unless required by applicable law or agreed to in writing, software
|
11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 |
+
# See the License for the specific language governing permissions and
|
14 |
+
# limitations under the License.
|
15 |
+
#
|
16 |
+
from abc import ABC
|
17 |
+
from duckduckgo_search import DDGS
|
18 |
+
import pandas as pd
|
19 |
+
from agent.settings import DEBUG
|
20 |
+
from agent.component.base import ComponentBase, ComponentParamBase
|
21 |
+
|
22 |
+
|
23 |
+
class DuckDuckGoParam(ComponentParamBase):
|
24 |
+
"""
|
25 |
+
Define the DuckDuckGo component parameters.
|
26 |
+
"""
|
27 |
+
|
28 |
+
def __init__(self):
|
29 |
+
super().__init__()
|
30 |
+
self.top_n = 10
|
31 |
+
self.channel = "text"
|
32 |
+
|
33 |
+
def check(self):
|
34 |
+
self.check_positive_integer(self.top_n, "Top N")
|
35 |
+
self.check_valid_value(self.channel, "Web Search or News", ["text", "news"])
|
36 |
+
|
37 |
+
|
38 |
+
class DuckDuckGo(ComponentBase, ABC):
|
39 |
+
component_name = "DuckDuckGo"
|
40 |
+
|
41 |
+
def _run(self, history, **kwargs):
|
42 |
+
ans = self.get_input()
|
43 |
+
ans = " - ".join(ans["content"]) if "content" in ans else ""
|
44 |
+
if not ans:
|
45 |
+
return DuckDuckGo.be_output("")
|
46 |
+
|
47 |
+
try:
|
48 |
+
if self._param.channel == "text":
|
49 |
+
with DDGS() as ddgs:
|
50 |
+
# {'title': '', 'href': '', 'body': ''}
|
51 |
+
duck_res = [{"content": '<a href="' + i["href"] + '">' + i["title"] + '</a> ' + i["body"]} for i
|
52 |
+
in ddgs.text(ans, max_results=self._param.top_n)]
|
53 |
+
elif self._param.channel == "news":
|
54 |
+
with DDGS() as ddgs:
|
55 |
+
# {'date': '', 'title': '', 'body': '', 'url': '', 'image': '', 'source': ''}
|
56 |
+
duck_res = [{"content": '<a href="' + i["url"] + '">' + i["title"] + '</a> ' + i["body"]} for i
|
57 |
+
in ddgs.news(ans, max_results=self._param.top_n)]
|
58 |
+
except Exception as e:
|
59 |
+
return DuckDuckGo.be_output("**ERROR**: " + str(e))
|
60 |
+
|
61 |
+
if not duck_res:
|
62 |
+
return DuckDuckGo.be_output("")
|
63 |
+
|
64 |
+
df = pd.DataFrame(duck_res)
|
65 |
+
if DEBUG: print(df, ":::::::::::::::::::::::::::::::::")
|
66 |
+
return df
|
agent/component/generate.py
ADDED
@@ -0,0 +1,150 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#
|
2 |
+
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
3 |
+
#
|
4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5 |
+
# you may not use this file except in compliance with the License.
|
6 |
+
# You may obtain a copy of the License at
|
7 |
+
#
|
8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9 |
+
#
|
10 |
+
# Unless required by applicable law or agreed to in writing, software
|
11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 |
+
# See the License for the specific language governing permissions and
|
14 |
+
# limitations under the License.
|
15 |
+
#
|
16 |
+
import re
|
17 |
+
from functools import partial
|
18 |
+
import pandas as pd
|
19 |
+
from api.db import LLMType
|
20 |
+
from api.db.services.llm_service import LLMBundle
|
21 |
+
from api.settings import retrievaler
|
22 |
+
from agent.component.base import ComponentBase, ComponentParamBase
|
23 |
+
|
24 |
+
|
25 |
+
class GenerateParam(ComponentParamBase):
|
26 |
+
"""
|
27 |
+
Define the Generate component parameters.
|
28 |
+
"""
|
29 |
+
|
30 |
+
def __init__(self):
|
31 |
+
super().__init__()
|
32 |
+
self.llm_id = ""
|
33 |
+
self.prompt = ""
|
34 |
+
self.max_tokens = 0
|
35 |
+
self.temperature = 0
|
36 |
+
self.top_p = 0
|
37 |
+
self.presence_penalty = 0
|
38 |
+
self.frequency_penalty = 0
|
39 |
+
self.cite = True
|
40 |
+
self.parameters = []
|
41 |
+
|
42 |
+
def check(self):
|
43 |
+
self.check_decimal_float(self.temperature, "[Generate] Temperature")
|
44 |
+
self.check_decimal_float(self.presence_penalty, "[Generate] Presence penalty")
|
45 |
+
self.check_decimal_float(self.frequency_penalty, "[Generate] Frequency penalty")
|
46 |
+
self.check_nonnegative_number(self.max_tokens, "[Generate] Max tokens")
|
47 |
+
self.check_decimal_float(self.top_p, "[Generate] Top P")
|
48 |
+
self.check_empty(self.llm_id, "[Generate] LLM")
|
49 |
+
# self.check_defined_type(self.parameters, "Parameters", ["list"])
|
50 |
+
|
51 |
+
def gen_conf(self):
|
52 |
+
conf = {}
|
53 |
+
if self.max_tokens > 0: conf["max_tokens"] = self.max_tokens
|
54 |
+
if self.temperature > 0: conf["temperature"] = self.temperature
|
55 |
+
if self.top_p > 0: conf["top_p"] = self.top_p
|
56 |
+
if self.presence_penalty > 0: conf["presence_penalty"] = self.presence_penalty
|
57 |
+
if self.frequency_penalty > 0: conf["frequency_penalty"] = self.frequency_penalty
|
58 |
+
return conf
|
59 |
+
|
60 |
+
|
61 |
+
class Generate(ComponentBase):
|
62 |
+
component_name = "Generate"
|
63 |
+
|
64 |
+
def get_dependent_components(self):
|
65 |
+
cpnts = [para["component_id"] for para in self._param.parameters]
|
66 |
+
return cpnts
|
67 |
+
|
68 |
+
def set_cite(self, retrieval_res, answer):
|
69 |
+
answer, idx = retrievaler.insert_citations(answer, [ck["content_ltks"] for _, ck in retrieval_res.iterrows()],
|
70 |
+
[ck["vector"] for _, ck in retrieval_res.iterrows()],
|
71 |
+
LLMBundle(self._canvas.get_tenant_id(), LLMType.EMBEDDING,
|
72 |
+
self._canvas.get_embedding_model()), tkweight=0.7,
|
73 |
+
vtweight=0.3)
|
74 |
+
doc_ids = set([])
|
75 |
+
recall_docs = []
|
76 |
+
for i in idx:
|
77 |
+
did = retrieval_res.loc[int(i), "doc_id"]
|
78 |
+
if did in doc_ids: continue
|
79 |
+
doc_ids.add(did)
|
80 |
+
recall_docs.append({"doc_id": did, "doc_name": retrieval_res.loc[int(i), "docnm_kwd"]})
|
81 |
+
|
82 |
+
del retrieval_res["vector"]
|
83 |
+
del retrieval_res["content_ltks"]
|
84 |
+
|
85 |
+
reference = {
|
86 |
+
"chunks": [ck.to_dict() for _, ck in retrieval_res.iterrows()],
|
87 |
+
"doc_aggs": recall_docs
|
88 |
+
}
|
89 |
+
|
90 |
+
if answer.lower().find("invalid key") >= 0 or answer.lower().find("invalid api") >= 0:
|
91 |
+
answer += " Please set LLM API-Key in 'User Setting -> Model Providers -> API-Key'"
|
92 |
+
res = {"content": answer, "reference": reference}
|
93 |
+
|
94 |
+
return res
|
95 |
+
|
96 |
+
def _run(self, history, **kwargs):
|
97 |
+
chat_mdl = LLMBundle(self._canvas.get_tenant_id(), LLMType.CHAT, self._param.llm_id)
|
98 |
+
prompt = self._param.prompt
|
99 |
+
|
100 |
+
retrieval_res = self.get_input()
|
101 |
+
input = (" - " + "\n - ".join(retrieval_res["content"])) if "content" in retrieval_res else ""
|
102 |
+
for para in self._param.parameters:
|
103 |
+
cpn = self._canvas.get_component(para["component_id"])["obj"]
|
104 |
+
_, out = cpn.output(allow_partial=False)
|
105 |
+
if "content" not in out.columns:
|
106 |
+
kwargs[para["key"]] = "Nothing"
|
107 |
+
else:
|
108 |
+
kwargs[para["key"]] = " - " + "\n - ".join(out["content"])
|
109 |
+
|
110 |
+
kwargs["input"] = input
|
111 |
+
for n, v in kwargs.items():
|
112 |
+
# prompt = re.sub(r"\{%s\}"%n, re.escape(str(v)), prompt)
|
113 |
+
prompt = re.sub(r"\{%s\}" % n, str(v), prompt)
|
114 |
+
|
115 |
+
downstreams = self._canvas.get_component(self._id)["downstream"]
|
116 |
+
if kwargs.get("stream") and len(downstreams) == 1 and self._canvas.get_component(downstreams[0])[
|
117 |
+
"obj"].component_name.lower() == "answer":
|
118 |
+
return partial(self.stream_output, chat_mdl, prompt, retrieval_res)
|
119 |
+
|
120 |
+
if "empty_response" in retrieval_res.columns:
|
121 |
+
return Generate.be_output(input)
|
122 |
+
|
123 |
+
ans = chat_mdl.chat(prompt, self._canvas.get_history(self._param.message_history_window_size),
|
124 |
+
self._param.gen_conf())
|
125 |
+
if self._param.cite and "content_ltks" in retrieval_res.columns and "vector" in retrieval_res.columns:
|
126 |
+
df = self.set_cite(retrieval_res, ans)
|
127 |
+
return pd.DataFrame(df)
|
128 |
+
|
129 |
+
return Generate.be_output(ans)
|
130 |
+
|
131 |
+
def stream_output(self, chat_mdl, prompt, retrieval_res):
|
132 |
+
res = None
|
133 |
+
if "empty_response" in retrieval_res.columns and "\n- ".join(retrieval_res["content"]):
|
134 |
+
res = {"content": "\n- ".join(retrieval_res["content"]), "reference": []}
|
135 |
+
yield res
|
136 |
+
self.set_output(res)
|
137 |
+
return
|
138 |
+
|
139 |
+
answer = ""
|
140 |
+
for ans in chat_mdl.chat_streamly(prompt, self._canvas.get_history(self._param.message_history_window_size),
|
141 |
+
self._param.gen_conf()):
|
142 |
+
res = {"content": ans, "reference": []}
|
143 |
+
answer = ans
|
144 |
+
yield res
|
145 |
+
|
146 |
+
if self._param.cite and "content_ltks" in retrieval_res.columns and "vector" in retrieval_res.columns:
|
147 |
+
res = self.set_cite(retrieval_res, answer)
|
148 |
+
yield res
|
149 |
+
|
150 |
+
self.set_output(res)
|
agent/component/github.py
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#
|
2 |
+
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
3 |
+
#
|
4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5 |
+
# you may not use this file except in compliance with the License.
|
6 |
+
# You may obtain a copy of the License at
|
7 |
+
#
|
8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9 |
+
#
|
10 |
+
# Unless required by applicable law or agreed to in writing, software
|
11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 |
+
# See the License for the specific language governing permissions and
|
14 |
+
# limitations under the License.
|
15 |
+
#
|
16 |
+
from abc import ABC
|
17 |
+
import pandas as pd
|
18 |
+
import requests
|
19 |
+
from agent.settings import DEBUG
|
20 |
+
from agent.component.base import ComponentBase, ComponentParamBase
|
21 |
+
|
22 |
+
|
23 |
+
class GitHubParam(ComponentParamBase):
|
24 |
+
"""
|
25 |
+
Define the GitHub component parameters.
|
26 |
+
"""
|
27 |
+
|
28 |
+
def __init__(self):
|
29 |
+
super().__init__()
|
30 |
+
self.top_n = 10
|
31 |
+
|
32 |
+
def check(self):
|
33 |
+
self.check_positive_integer(self.top_n, "Top N")
|
34 |
+
|
35 |
+
|
36 |
+
class GitHub(ComponentBase, ABC):
|
37 |
+
component_name = "GitHub"
|
38 |
+
|
39 |
+
def _run(self, history, **kwargs):
|
40 |
+
ans = self.get_input()
|
41 |
+
ans = " - ".join(ans["content"]) if "content" in ans else ""
|
42 |
+
if not ans:
|
43 |
+
return GitHub.be_output("")
|
44 |
+
|
45 |
+
try:
|
46 |
+
url = 'https://api.github.com/search/repositories?q=' + ans + '&sort=stars&order=desc&per_page=' + str(
|
47 |
+
self._param.top_n)
|
48 |
+
headers = {"Content-Type": "application/vnd.github+json", "X-GitHub-Api-Version": '2022-11-28'}
|
49 |
+
response = requests.get(url=url, headers=headers).json()
|
50 |
+
|
51 |
+
github_res = [{"content": '<a href="' + i["html_url"] + '">' + i["name"] + '</a>' + str(
|
52 |
+
i["description"]) + '\n stars:' + str(i['watchers'])} for i in response['items']]
|
53 |
+
except Exception as e:
|
54 |
+
return GitHub.be_output("**ERROR**: " + str(e))
|
55 |
+
|
56 |
+
if not github_res:
|
57 |
+
return GitHub.be_output("")
|
58 |
+
|
59 |
+
df = pd.DataFrame(github_res)
|
60 |
+
if DEBUG: print(df, ":::::::::::::::::::::::::::::::::")
|
61 |
+
return df
|
agent/component/google.py
ADDED
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#
|
2 |
+
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
3 |
+
#
|
4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5 |
+
# you may not use this file except in compliance with the License.
|
6 |
+
# You may obtain a copy of the License at
|
7 |
+
#
|
8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9 |
+
#
|
10 |
+
# Unless required by applicable law or agreed to in writing, software
|
11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 |
+
# See the License for the specific language governing permissions and
|
14 |
+
# limitations under the License.
|
15 |
+
#
|
16 |
+
from abc import ABC
|
17 |
+
from serpapi import GoogleSearch
|
18 |
+
import pandas as pd
|
19 |
+
from agent.settings import DEBUG
|
20 |
+
from agent.component.base import ComponentBase, ComponentParamBase
|
21 |
+
|
22 |
+
|
23 |
+
class GoogleParam(ComponentParamBase):
|
24 |
+
"""
|
25 |
+
Define the Google component parameters.
|
26 |
+
"""
|
27 |
+
|
28 |
+
def __init__(self):
|
29 |
+
super().__init__()
|
30 |
+
self.top_n = 10
|
31 |
+
self.api_key = "xxx"
|
32 |
+
self.country = "cn"
|
33 |
+
self.language = "en"
|
34 |
+
|
35 |
+
def check(self):
|
36 |
+
self.check_positive_integer(self.top_n, "Top N")
|
37 |
+
self.check_empty(self.api_key, "SerpApi API key")
|
38 |
+
self.check_valid_value(self.country, "Google Country",
|
39 |
+
['af', 'al', 'dz', 'as', 'ad', 'ao', 'ai', 'aq', 'ag', 'ar', 'am', 'aw', 'au', 'at',
|
40 |
+
'az', 'bs', 'bh', 'bd', 'bb', 'by', 'be', 'bz', 'bj', 'bm', 'bt', 'bo', 'ba', 'bw',
|
41 |
+
'bv', 'br', 'io', 'bn', 'bg', 'bf', 'bi', 'kh', 'cm', 'ca', 'cv', 'ky', 'cf', 'td',
|
42 |
+
'cl', 'cn', 'cx', 'cc', 'co', 'km', 'cg', 'cd', 'ck', 'cr', 'ci', 'hr', 'cu', 'cy',
|
43 |
+
'cz', 'dk', 'dj', 'dm', 'do', 'ec', 'eg', 'sv', 'gq', 'er', 'ee', 'et', 'fk', 'fo',
|
44 |
+
'fj', 'fi', 'fr', 'gf', 'pf', 'tf', 'ga', 'gm', 'ge', 'de', 'gh', 'gi', 'gr', 'gl',
|
45 |
+
'gd', 'gp', 'gu', 'gt', 'gn', 'gw', 'gy', 'ht', 'hm', 'va', 'hn', 'hk', 'hu', 'is',
|
46 |
+
'in', 'id', 'ir', 'iq', 'ie', 'il', 'it', 'jm', 'jp', 'jo', 'kz', 'ke', 'ki', 'kp',
|
47 |
+
'kr', 'kw', 'kg', 'la', 'lv', 'lb', 'ls', 'lr', 'ly', 'li', 'lt', 'lu', 'mo', 'mk',
|
48 |
+
'mg', 'mw', 'my', 'mv', 'ml', 'mt', 'mh', 'mq', 'mr', 'mu', 'yt', 'mx', 'fm', 'md',
|
49 |
+
'mc', 'mn', 'ms', 'ma', 'mz', 'mm', 'na', 'nr', 'np', 'nl', 'an', 'nc', 'nz', 'ni',
|
50 |
+
'ne', 'ng', 'nu', 'nf', 'mp', 'no', 'om', 'pk', 'pw', 'ps', 'pa', 'pg', 'py', 'pe',
|
51 |
+
'ph', 'pn', 'pl', 'pt', 'pr', 'qa', 're', 'ro', 'ru', 'rw', 'sh', 'kn', 'lc', 'pm',
|
52 |
+
'vc', 'ws', 'sm', 'st', 'sa', 'sn', 'rs', 'sc', 'sl', 'sg', 'sk', 'si', 'sb', 'so',
|
53 |
+
'za', 'gs', 'es', 'lk', 'sd', 'sr', 'sj', 'sz', 'se', 'ch', 'sy', 'tw', 'tj', 'tz',
|
54 |
+
'th', 'tl', 'tg', 'tk', 'to', 'tt', 'tn', 'tr', 'tm', 'tc', 'tv', 'ug', 'ua', 'ae',
|
55 |
+
'uk', 'gb', 'us', 'um', 'uy', 'uz', 'vu', 've', 'vn', 'vg', 'vi', 'wf', 'eh', 'ye',
|
56 |
+
'zm', 'zw'])
|
57 |
+
self.check_valid_value(self.language, "Google languages",
|
58 |
+
['af', 'ak', 'sq', 'ws', 'am', 'ar', 'hy', 'az', 'eu', 'be', 'bem', 'bn', 'bh',
|
59 |
+
'xx-bork', 'bs', 'br', 'bg', 'bt', 'km', 'ca', 'chr', 'ny', 'zh-cn', 'zh-tw', 'co',
|
60 |
+
'hr', 'cs', 'da', 'nl', 'xx-elmer', 'en', 'eo', 'et', 'ee', 'fo', 'tl', 'fi', 'fr',
|
61 |
+
'fy', 'gaa', 'gl', 'ka', 'de', 'el', 'kl', 'gn', 'gu', 'xx-hacker', 'ht', 'ha', 'haw',
|
62 |
+
'iw', 'hi', 'hu', 'is', 'ig', 'id', 'ia', 'ga', 'it', 'ja', 'jw', 'kn', 'kk', 'rw',
|
63 |
+
'rn', 'xx-klingon', 'kg', 'ko', 'kri', 'ku', 'ckb', 'ky', 'lo', 'la', 'lv', 'ln', 'lt',
|
64 |
+
'loz', 'lg', 'ach', 'mk', 'mg', 'ms', 'ml', 'mt', 'mv', 'mi', 'mr', 'mfe', 'mo', 'mn',
|
65 |
+
'sr-me', 'my', 'ne', 'pcm', 'nso', 'no', 'nn', 'oc', 'or', 'om', 'ps', 'fa',
|
66 |
+
'xx-pirate', 'pl', 'pt', 'pt-br', 'pt-pt', 'pa', 'qu', 'ro', 'rm', 'nyn', 'ru', 'gd',
|
67 |
+
'sr', 'sh', 'st', 'tn', 'crs', 'sn', 'sd', 'si', 'sk', 'sl', 'so', 'es', 'es-419', 'su',
|
68 |
+
'sw', 'sv', 'tg', 'ta', 'tt', 'te', 'th', 'ti', 'to', 'lua', 'tum', 'tr', 'tk', 'tw',
|
69 |
+
'ug', 'uk', 'ur', 'uz', 'vu', 'vi', 'cy', 'wo', 'xh', 'yi', 'yo', 'zu']
|
70 |
+
)
|
71 |
+
|
72 |
+
|
73 |
+
class Google(ComponentBase, ABC):
|
74 |
+
component_name = "Google"
|
75 |
+
|
76 |
+
def _run(self, history, **kwargs):
|
77 |
+
ans = self.get_input()
|
78 |
+
ans = " - ".join(ans["content"]) if "content" in ans else ""
|
79 |
+
if not ans:
|
80 |
+
return Google.be_output("")
|
81 |
+
|
82 |
+
try:
|
83 |
+
client = GoogleSearch(
|
84 |
+
{"engine": "google", "q": ans, "api_key": self._param.api_key, "gl": self._param.country,
|
85 |
+
"hl": self._param.language, "num": self._param.top_n})
|
86 |
+
google_res = [{"content": '<a href="' + i["link"] + '">' + i["title"] + '</a> ' + i["snippet"]} for i in
|
87 |
+
client.get_dict()["organic_results"]]
|
88 |
+
except Exception as e:
|
89 |
+
return Google.be_output("**ERROR**: Existing Unavailable Parameters!")
|
90 |
+
|
91 |
+
if not google_res:
|
92 |
+
return Google.be_output("")
|
93 |
+
|
94 |
+
df = pd.DataFrame(google_res)
|
95 |
+
if DEBUG: print(df, ":::::::::::::::::::::::::::::::::")
|
96 |
+
return df
|
agent/component/googlescholar.py
ADDED
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#
|
2 |
+
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
3 |
+
#
|
4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5 |
+
# you may not use this file except in compliance with the License.
|
6 |
+
# You may obtain a copy of the License at
|
7 |
+
#
|
8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9 |
+
#
|
10 |
+
# Unless required by applicable law or agreed to in writing, software
|
11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 |
+
# See the License for the specific language governing permissions and
|
14 |
+
# limitations under the License.
|
15 |
+
#
|
16 |
+
from abc import ABC
|
17 |
+
import pandas as pd
|
18 |
+
from agent.settings import DEBUG
|
19 |
+
from agent.component.base import ComponentBase, ComponentParamBase
|
20 |
+
from scholarly import scholarly
|
21 |
+
|
22 |
+
|
23 |
+
class GoogleScholarParam(ComponentParamBase):
|
24 |
+
"""
|
25 |
+
Define the GoogleScholar component parameters.
|
26 |
+
"""
|
27 |
+
|
28 |
+
def __init__(self):
|
29 |
+
super().__init__()
|
30 |
+
self.top_n = 6
|
31 |
+
self.sort_by = 'relevance'
|
32 |
+
self.year_low = None
|
33 |
+
self.year_high = None
|
34 |
+
self.patents = True
|
35 |
+
|
36 |
+
def check(self):
|
37 |
+
self.check_positive_integer(self.top_n, "Top N")
|
38 |
+
self.check_valid_value(self.sort_by, "GoogleScholar Sort_by", ['date', 'relevance'])
|
39 |
+
self.check_boolean(self.patents, "Whether or not to include patents, defaults to True")
|
40 |
+
|
41 |
+
|
42 |
+
class GoogleScholar(ComponentBase, ABC):
|
43 |
+
component_name = "GoogleScholar"
|
44 |
+
|
45 |
+
def _run(self, history, **kwargs):
|
46 |
+
ans = self.get_input()
|
47 |
+
ans = " - ".join(ans["content"]) if "content" in ans else ""
|
48 |
+
if not ans:
|
49 |
+
return GoogleScholar.be_output("")
|
50 |
+
|
51 |
+
scholar_client = scholarly.search_pubs(ans, patents=self._param.patents, year_low=self._param.year_low,
|
52 |
+
year_high=self._param.year_high, sort_by=self._param.sort_by)
|
53 |
+
scholar_res = []
|
54 |
+
for i in range(self._param.top_n):
|
55 |
+
try:
|
56 |
+
pub = next(scholar_client)
|
57 |
+
scholar_res.append({"content": 'Title: ' + pub['bib']['title'] + '\n_Url: <a href="' + pub[
|
58 |
+
'pub_url'] + '"></a> ' + "\n author: " + ",".join(pub['bib']['author']) + '\n Abstract: ' + pub[
|
59 |
+
'bib'].get('abstract', 'no abstract')})
|
60 |
+
|
61 |
+
except StopIteration or Exception as e:
|
62 |
+
print("**ERROR** " + str(e))
|
63 |
+
break
|
64 |
+
|
65 |
+
if not scholar_res:
|
66 |
+
return GoogleScholar.be_output("")
|
67 |
+
|
68 |
+
df = pd.DataFrame(scholar_res)
|
69 |
+
if DEBUG: print(df, ":::::::::::::::::::::::::::::::::")
|
70 |
+
return df
|
agent/component/keyword.py
ADDED
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#
|
2 |
+
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
3 |
+
#
|
4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5 |
+
# you may not use this file except in compliance with the License.
|
6 |
+
# You may obtain a copy of the License at
|
7 |
+
#
|
8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9 |
+
#
|
10 |
+
# Unless required by applicable law or agreed to in writing, software
|
11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 |
+
# See the License for the specific language governing permissions and
|
14 |
+
# limitations under the License.
|
15 |
+
#
|
16 |
+
import re
|
17 |
+
from abc import ABC
|
18 |
+
from api.db import LLMType
|
19 |
+
from api.db.services.llm_service import LLMBundle
|
20 |
+
from agent.component import GenerateParam, Generate
|
21 |
+
from agent.settings import DEBUG
|
22 |
+
|
23 |
+
|
24 |
+
class KeywordExtractParam(GenerateParam):
|
25 |
+
"""
|
26 |
+
Define the KeywordExtract component parameters.
|
27 |
+
"""
|
28 |
+
|
29 |
+
def __init__(self):
|
30 |
+
super().__init__()
|
31 |
+
self.top_n = 1
|
32 |
+
|
33 |
+
def check(self):
|
34 |
+
super().check()
|
35 |
+
self.check_positive_integer(self.top_n, "Top N")
|
36 |
+
|
37 |
+
def get_prompt(self):
|
38 |
+
self.prompt = """
|
39 |
+
- Role: You're a question analyzer.
|
40 |
+
- Requirements:
|
41 |
+
- Summarize user's question, and give top %s important keyword/phrase.
|
42 |
+
- Use comma as a delimiter to separate keywords/phrases.
|
43 |
+
- Answer format: (in language of user's question)
|
44 |
+
- keyword:
|
45 |
+
""" % self.top_n
|
46 |
+
return self.prompt
|
47 |
+
|
48 |
+
|
49 |
+
class KeywordExtract(Generate, ABC):
|
50 |
+
component_name = "KeywordExtract"
|
51 |
+
|
52 |
+
def _run(self, history, **kwargs):
|
53 |
+
q = ""
|
54 |
+
for r, c in self._canvas.history[::-1]:
|
55 |
+
if r == "user":
|
56 |
+
q += c
|
57 |
+
break
|
58 |
+
|
59 |
+
chat_mdl = LLMBundle(self._canvas.get_tenant_id(), LLMType.CHAT, self._param.llm_id)
|
60 |
+
ans = chat_mdl.chat(self._param.get_prompt(), [{"role": "user", "content": q}],
|
61 |
+
self._param.gen_conf())
|
62 |
+
|
63 |
+
ans = re.sub(r".*keyword:", "", ans).strip()
|
64 |
+
if DEBUG: print(ans, ":::::::::::::::::::::::::::::::::")
|
65 |
+
return KeywordExtract.be_output(ans)
|
agent/component/message.py
ADDED
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#
|
2 |
+
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
3 |
+
#
|
4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5 |
+
# you may not use this file except in compliance with the License.
|
6 |
+
# You may obtain a copy of the License at
|
7 |
+
#
|
8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9 |
+
#
|
10 |
+
# Unless required by applicable law or agreed to in writing, software
|
11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 |
+
# See the License for the specific language governing permissions and
|
14 |
+
# limitations under the License.
|
15 |
+
#
|
16 |
+
import random
|
17 |
+
from abc import ABC
|
18 |
+
from functools import partial
|
19 |
+
from agent.component.base import ComponentBase, ComponentParamBase
|
20 |
+
|
21 |
+
|
22 |
+
class MessageParam(ComponentParamBase):
|
23 |
+
|
24 |
+
"""
|
25 |
+
Define the Message component parameters.
|
26 |
+
"""
|
27 |
+
def __init__(self):
|
28 |
+
super().__init__()
|
29 |
+
self.messages = []
|
30 |
+
|
31 |
+
def check(self):
|
32 |
+
self.check_empty(self.messages, "[Message]")
|
33 |
+
return True
|
34 |
+
|
35 |
+
|
36 |
+
class Message(ComponentBase, ABC):
|
37 |
+
component_name = "Message"
|
38 |
+
|
39 |
+
def _run(self, history, **kwargs):
|
40 |
+
if kwargs.get("stream"):
|
41 |
+
return partial(self.stream_output)
|
42 |
+
|
43 |
+
return Message.be_output(random.choice(self._param.messages))
|
44 |
+
|
45 |
+
def stream_output(self):
|
46 |
+
res = None
|
47 |
+
if self._param.messages:
|
48 |
+
res = {"content": random.choice(self._param.messages)}
|
49 |
+
yield res
|
50 |
+
|
51 |
+
self.set_output(res)
|
52 |
+
|
53 |
+
|
agent/component/pubmed.py
ADDED
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#
|
2 |
+
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
3 |
+
#
|
4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5 |
+
# you may not use this file except in compliance with the License.
|
6 |
+
# You may obtain a copy of the License at
|
7 |
+
#
|
8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9 |
+
#
|
10 |
+
# Unless required by applicable law or agreed to in writing, software
|
11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 |
+
# See the License for the specific language governing permissions and
|
14 |
+
# limitations under the License.
|
15 |
+
#
|
16 |
+
from abc import ABC
|
17 |
+
from Bio import Entrez
|
18 |
+
import pandas as pd
|
19 |
+
import xml.etree.ElementTree as ET
|
20 |
+
from agent.settings import DEBUG
|
21 |
+
from agent.component.base import ComponentBase, ComponentParamBase
|
22 |
+
|
23 |
+
|
24 |
+
class PubMedParam(ComponentParamBase):
|
25 |
+
"""
|
26 |
+
Define the PubMed component parameters.
|
27 |
+
"""
|
28 |
+
|
29 |
+
def __init__(self):
|
30 |
+
super().__init__()
|
31 |
+
self.top_n = 5
|
32 |
+
self.email = "A.N.Other@example.com"
|
33 |
+
|
34 |
+
def check(self):
|
35 |
+
self.check_positive_integer(self.top_n, "Top N")
|
36 |
+
|
37 |
+
|
38 |
+
class PubMed(ComponentBase, ABC):
|
39 |
+
component_name = "PubMed"
|
40 |
+
|
41 |
+
def _run(self, history, **kwargs):
|
42 |
+
ans = self.get_input()
|
43 |
+
ans = " - ".join(ans["content"]) if "content" in ans else ""
|
44 |
+
if not ans:
|
45 |
+
return PubMed.be_output("")
|
46 |
+
|
47 |
+
try:
|
48 |
+
Entrez.email = self._param.email
|
49 |
+
pubmedids = Entrez.read(Entrez.esearch(db='pubmed', retmax=self._param.top_n, term=ans))['IdList']
|
50 |
+
pubmedcnt = ET.fromstring(
|
51 |
+
Entrez.efetch(db='pubmed', id=",".join(pubmedids), retmode="xml").read().decode("utf-8"))
|
52 |
+
pubmed_res = [{"content": 'Title:' + child.find("MedlineCitation").find("Article").find(
|
53 |
+
"ArticleTitle").text + '\nUrl:<a href=" https://pubmed.ncbi.nlm.nih.gov/' + child.find(
|
54 |
+
"MedlineCitation").find("PMID").text + '">' + '</a>\n' + 'Abstract:' + child.find(
|
55 |
+
"MedlineCitation").find("Article").find("Abstract").find("AbstractText").text} for child in
|
56 |
+
pubmedcnt.findall("PubmedArticle")]
|
57 |
+
except Exception as e:
|
58 |
+
return PubMed.be_output("**ERROR**: " + str(e))
|
59 |
+
|
60 |
+
if not pubmed_res:
|
61 |
+
return PubMed.be_output("")
|
62 |
+
|
63 |
+
df = pd.DataFrame(pubmed_res)
|
64 |
+
if DEBUG: print(df, ":::::::::::::::::::::::::::::::::")
|
65 |
+
return df
|
agent/component/qweather.py
ADDED
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#
|
2 |
+
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
3 |
+
#
|
4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5 |
+
# you may not use this file except in compliance with the License.
|
6 |
+
# You may obtain a copy of the License at
|
7 |
+
#
|
8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9 |
+
#
|
10 |
+
# Unless required by applicable law or agreed to in writing, software
|
11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 |
+
# See the License for the specific language governing permissions and
|
14 |
+
# limitations under the License.
|
15 |
+
#
|
16 |
+
from abc import ABC
|
17 |
+
import pandas as pd
|
18 |
+
import requests
|
19 |
+
from agent.component.base import ComponentBase, ComponentParamBase
|
20 |
+
|
21 |
+
|
22 |
+
class QWeatherParam(ComponentParamBase):
|
23 |
+
"""
|
24 |
+
Define the QWeather component parameters.
|
25 |
+
"""
|
26 |
+
|
27 |
+
def __init__(self):
|
28 |
+
super().__init__()
|
29 |
+
self.web_apikey = "xxx"
|
30 |
+
self.lang = "zh"
|
31 |
+
self.type = "weather"
|
32 |
+
self.user_type = 'free'
|
33 |
+
self.error_code = {
|
34 |
+
"204": "The request was successful, but the region you are querying does not have the data you need at this time.",
|
35 |
+
"400": "Request error, may contain incorrect request parameters or missing mandatory request parameters.",
|
36 |
+
"401": "Authentication fails, possibly using the wrong KEY, wrong digital signature, wrong type of KEY (e.g. using the SDK's KEY to access the Web API).",
|
37 |
+
"402": "Exceeded the number of accesses or the balance is not enough to support continued access to the service, you can recharge, upgrade the accesses or wait for the accesses to be reset.",
|
38 |
+
"403": "No access, may be the binding PackageName, BundleID, domain IP address is inconsistent, or the data that requires additional payment.",
|
39 |
+
"404": "The queried data or region does not exist.",
|
40 |
+
"429": "Exceeded the limited QPM (number of accesses per minute), please refer to the QPM description",
|
41 |
+
"500": "No response or timeout, interface service abnormality please contact us"
|
42 |
+
}
|
43 |
+
# Weather
|
44 |
+
self.time_period = 'now'
|
45 |
+
|
46 |
+
def check(self):
|
47 |
+
self.check_empty(self.web_apikey, "BaiduFanyi APPID")
|
48 |
+
self.check_valid_value(self.type, "Type", ["weather", "indices", "airquality"])
|
49 |
+
self.check_valid_value(self.user_type, "Free subscription or paid subscription", ["free", "paid"])
|
50 |
+
self.check_valid_value(self.lang, "Use language",
|
51 |
+
['zh', 'zh-hant', 'en', 'de', 'es', 'fr', 'it', 'ja', 'ko', 'ru', 'hi', 'th', 'ar', 'pt',
|
52 |
+
'bn', 'ms', 'nl', 'el', 'la', 'sv', 'id', 'pl', 'tr', 'cs', 'et', 'vi', 'fil', 'fi',
|
53 |
+
'he', 'is', 'nb'])
|
54 |
+
self.check_vaild_value(self.time_period, "Time period", ['now', '3d', '7d', '10d', '15d', '30d'])
|
55 |
+
|
56 |
+
|
57 |
+
class QWeather(ComponentBase, ABC):
|
58 |
+
component_name = "QWeather"
|
59 |
+
|
60 |
+
def _run(self, history, **kwargs):
|
61 |
+
ans = self.get_input()
|
62 |
+
ans = "".join(ans["content"]) if "content" in ans else ""
|
63 |
+
if not ans:
|
64 |
+
return QWeather.be_output("")
|
65 |
+
|
66 |
+
try:
|
67 |
+
response = requests.get(
|
68 |
+
url="https://geoapi.qweather.com/v2/city/lookup?location=" + ans + "&key=" + self._param.web_apikey).json()
|
69 |
+
if response["code"] == "200":
|
70 |
+
location_id = response["location"][0]["id"]
|
71 |
+
else:
|
72 |
+
return QWeather.be_output("**Error**" + self._param.error_code[response["code"]])
|
73 |
+
|
74 |
+
base_url = "https://api.qweather.com/v7/" if self._param.user_type == 'paid' else "https://devapi.qweather.com/v7/"
|
75 |
+
|
76 |
+
if self._param.type == "weather":
|
77 |
+
url = base_url + "weather/" + self._param.time_period + "?location=" + location_id + "&key=" + self._param.web_apikey + "&lang=" + self._param.lang
|
78 |
+
response = requests.get(url=url).json()
|
79 |
+
if response["code"] == "200":
|
80 |
+
if self._param.time_period == "now":
|
81 |
+
return QWeather.be_output(str(response["now"]))
|
82 |
+
else:
|
83 |
+
qweather_res = [{"content": str(i) + "\n"} for i in response["daily"]]
|
84 |
+
if not qweather_res:
|
85 |
+
return QWeather.be_output("")
|
86 |
+
|
87 |
+
df = pd.DataFrame(qweather_res)
|
88 |
+
return df
|
89 |
+
else:
|
90 |
+
return QWeather.be_output("**Error**" + self._param.error_code[response["code"]])
|
91 |
+
|
92 |
+
elif self._param.type == "indices":
|
93 |
+
url = base_url + "indices/1d?type=0&location=" + location_id + "&key=" + self._param.web_apikey + "&lang=" + self._param.lang
|
94 |
+
response = requests.get(url=url).json()
|
95 |
+
if response["code"] == "200":
|
96 |
+
indices_res = response["daily"][0]["date"] + "\n" + "\n".join(
|
97 |
+
[i["name"] + ": " + i["category"] + ", " + i["text"] for i in response["daily"]])
|
98 |
+
return QWeather.be_output(indices_res)
|
99 |
+
|
100 |
+
else:
|
101 |
+
return QWeather.be_output("**Error**" + self._param.error_code[response["code"]])
|
102 |
+
|
103 |
+
elif self._param.type == "airquality":
|
104 |
+
url = base_url + "air/now?location=" + location_id + "&key=" + self._param.web_apikey + "&lang=" + self._param.lang
|
105 |
+
response = requests.get(url=url).json()
|
106 |
+
if response["code"] == "200":
|
107 |
+
return QWeather.be_output(str(response["now"]))
|
108 |
+
else:
|
109 |
+
return QWeather.be_output("**Error**" + self._param.error_code[response["code"]])
|
110 |
+
except Exception as e:
|
111 |
+
return QWeather.be_output("**Error**" + str(e))
|
agent/component/relevant.py
ADDED
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#
|
2 |
+
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
3 |
+
#
|
4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5 |
+
# you may not use this file except in compliance with the License.
|
6 |
+
# You may obtain a copy of the License at
|
7 |
+
#
|
8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9 |
+
#
|
10 |
+
# Unless required by applicable law or agreed to in writing, software
|
11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 |
+
# See the License for the specific language governing permissions and
|
14 |
+
# limitations under the License.
|
15 |
+
#
|
16 |
+
from abc import ABC
|
17 |
+
from api.db import LLMType
|
18 |
+
from api.db.services.llm_service import LLMBundle
|
19 |
+
from agent.component import GenerateParam, Generate
|
20 |
+
from rag.utils import num_tokens_from_string, encoder
|
21 |
+
|
22 |
+
|
23 |
+
class RelevantParam(GenerateParam):
|
24 |
+
|
25 |
+
"""
|
26 |
+
Define the Relevant component parameters.
|
27 |
+
"""
|
28 |
+
def __init__(self):
|
29 |
+
super().__init__()
|
30 |
+
self.prompt = ""
|
31 |
+
self.yes = ""
|
32 |
+
self.no = ""
|
33 |
+
|
34 |
+
def check(self):
|
35 |
+
super().check()
|
36 |
+
self.check_empty(self.yes, "[Relevant] 'Yes'")
|
37 |
+
self.check_empty(self.no, "[Relevant] 'No'")
|
38 |
+
|
39 |
+
def get_prompt(self):
|
40 |
+
self.prompt = """
|
41 |
+
You are a grader assessing relevance of a retrieved document to a user question.
|
42 |
+
It does not need to be a stringent test. The goal is to filter out erroneous retrievals.
|
43 |
+
If the document contains keyword(s) or semantic meaning related to the user question, grade it as relevant.
|
44 |
+
Give a binary score 'yes' or 'no' score to indicate whether the document is relevant to the question.
|
45 |
+
No other words needed except 'yes' or 'no'.
|
46 |
+
"""
|
47 |
+
return self.prompt
|
48 |
+
|
49 |
+
|
50 |
+
class Relevant(Generate, ABC):
|
51 |
+
component_name = "Relevant"
|
52 |
+
|
53 |
+
def _run(self, history, **kwargs):
|
54 |
+
q = ""
|
55 |
+
for r, c in self._canvas.history[::-1]:
|
56 |
+
if r == "user":
|
57 |
+
q = c
|
58 |
+
break
|
59 |
+
ans = self.get_input()
|
60 |
+
ans = " - ".join(ans["content"]) if "content" in ans else ""
|
61 |
+
if not ans:
|
62 |
+
return Relevant.be_output(self._param.no)
|
63 |
+
ans = "Documents: \n" + ans
|
64 |
+
ans = f"Question: {q}\n" + ans
|
65 |
+
chat_mdl = LLMBundle(self._canvas.get_tenant_id(), LLMType.CHAT, self._param.llm_id)
|
66 |
+
|
67 |
+
if num_tokens_from_string(ans) >= chat_mdl.max_length - 4:
|
68 |
+
ans = encoder.decode(encoder.encode(ans)[:chat_mdl.max_length - 4])
|
69 |
+
|
70 |
+
ans = chat_mdl.chat(self._param.get_prompt(), [{"role": "user", "content": ans}],
|
71 |
+
self._param.gen_conf())
|
72 |
+
|
73 |
+
print(ans, ":::::::::::::::::::::::::::::::::")
|
74 |
+
if ans.lower().find("yes") >= 0:
|
75 |
+
return Relevant.be_output(self._param.yes)
|
76 |
+
if ans.lower().find("no") >= 0:
|
77 |
+
return Relevant.be_output(self._param.no)
|
78 |
+
assert False, f"Relevant component got: {ans}"
|
79 |
+
|
80 |
+
|
agent/component/retrieval.py
ADDED
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#
|
2 |
+
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
3 |
+
#
|
4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5 |
+
# you may not use this file except in compliance with the License.
|
6 |
+
# You may obtain a copy of the License at
|
7 |
+
#
|
8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9 |
+
#
|
10 |
+
# Unless required by applicable law or agreed to in writing, software
|
11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 |
+
# See the License for the specific language governing permissions and
|
14 |
+
# limitations under the License.
|
15 |
+
#
|
16 |
+
from abc import ABC
|
17 |
+
|
18 |
+
import pandas as pd
|
19 |
+
|
20 |
+
from api.db import LLMType
|
21 |
+
from api.db.services.knowledgebase_service import KnowledgebaseService
|
22 |
+
from api.db.services.llm_service import LLMBundle
|
23 |
+
from api.settings import retrievaler
|
24 |
+
from agent.component.base import ComponentBase, ComponentParamBase
|
25 |
+
|
26 |
+
|
27 |
+
class RetrievalParam(ComponentParamBase):
|
28 |
+
|
29 |
+
"""
|
30 |
+
Define the Retrieval component parameters.
|
31 |
+
"""
|
32 |
+
def __init__(self):
|
33 |
+
super().__init__()
|
34 |
+
self.similarity_threshold = 0.2
|
35 |
+
self.keywords_similarity_weight = 0.5
|
36 |
+
self.top_n = 8
|
37 |
+
self.top_k = 1024
|
38 |
+
self.kb_ids = []
|
39 |
+
self.rerank_id = ""
|
40 |
+
self.empty_response = ""
|
41 |
+
|
42 |
+
def check(self):
|
43 |
+
self.check_decimal_float(self.similarity_threshold, "[Retrieval] Similarity threshold")
|
44 |
+
self.check_decimal_float(self.keywords_similarity_weight, "[Retrieval] Keywords similarity weight")
|
45 |
+
self.check_positive_number(self.top_n, "[Retrieval] Top N")
|
46 |
+
self.check_empty(self.kb_ids, "[Retrieval] Knowledge bases")
|
47 |
+
|
48 |
+
|
49 |
+
class Retrieval(ComponentBase, ABC):
|
50 |
+
component_name = "Retrieval"
|
51 |
+
|
52 |
+
def _run(self, history, **kwargs):
|
53 |
+
query = []
|
54 |
+
for role, cnt in history[::-1][:self._param.message_history_window_size]:
|
55 |
+
if role != "user":continue
|
56 |
+
query.append(cnt)
|
57 |
+
query = "\n".join(query)
|
58 |
+
|
59 |
+
kbs = KnowledgebaseService.get_by_ids(self._param.kb_ids)
|
60 |
+
if not kbs:
|
61 |
+
raise ValueError("Can't find knowledgebases by {}".format(self._param.kb_ids))
|
62 |
+
embd_nms = list(set([kb.embd_id for kb in kbs]))
|
63 |
+
assert len(embd_nms) == 1, "Knowledge bases use different embedding models."
|
64 |
+
|
65 |
+
embd_mdl = LLMBundle(self._canvas.get_tenant_id(), LLMType.EMBEDDING, embd_nms[0])
|
66 |
+
self._canvas.set_embedding_model(embd_nms[0])
|
67 |
+
|
68 |
+
rerank_mdl = None
|
69 |
+
if self._param.rerank_id:
|
70 |
+
rerank_mdl = LLMBundle(kbs[0].tenant_id, LLMType.RERANK, self._param.rerank_id)
|
71 |
+
|
72 |
+
kbinfos = retrievaler.retrieval(query, embd_mdl, kbs[0].tenant_id, self._param.kb_ids,
|
73 |
+
1, self._param.top_n,
|
74 |
+
self._param.similarity_threshold, 1 - self._param.keywords_similarity_weight,
|
75 |
+
aggs=False, rerank_mdl=rerank_mdl)
|
76 |
+
|
77 |
+
if not kbinfos["chunks"]:
|
78 |
+
df = Retrieval.be_output(self._param.empty_response)
|
79 |
+
df["empty_response"] = True
|
80 |
+
return df
|
81 |
+
|
82 |
+
df = pd.DataFrame(kbinfos["chunks"])
|
83 |
+
df["content"] = df["content_with_weight"]
|
84 |
+
del df["content_with_weight"]
|
85 |
+
print(">>>>>>>>>>>>>>>>>>>>>>>>>>\n", query, df)
|
86 |
+
return df
|
87 |
+
|
88 |
+
|
agent/component/rewrite.py
ADDED
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#
|
2 |
+
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
3 |
+
#
|
4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5 |
+
# you may not use this file except in compliance with the License.
|
6 |
+
# You may obtain a copy of the License at
|
7 |
+
#
|
8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9 |
+
#
|
10 |
+
# Unless required by applicable law or agreed to in writing, software
|
11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 |
+
# See the License for the specific language governing permissions and
|
14 |
+
# limitations under the License.
|
15 |
+
#
|
16 |
+
from abc import ABC
|
17 |
+
from api.db import LLMType
|
18 |
+
from api.db.services.llm_service import LLMBundle
|
19 |
+
from agent.component import GenerateParam, Generate
|
20 |
+
|
21 |
+
|
22 |
+
class RewriteQuestionParam(GenerateParam):
|
23 |
+
|
24 |
+
"""
|
25 |
+
Define the QuestionRewrite component parameters.
|
26 |
+
"""
|
27 |
+
def __init__(self):
|
28 |
+
super().__init__()
|
29 |
+
self.temperature = 0.9
|
30 |
+
self.prompt = ""
|
31 |
+
self.loop = 1
|
32 |
+
|
33 |
+
def check(self):
|
34 |
+
super().check()
|
35 |
+
|
36 |
+
def get_prompt(self):
|
37 |
+
self.prompt = """
|
38 |
+
You are an expert at query expansion to generate a paraphrasing of a question.
|
39 |
+
I can't retrieval relevant information from the knowledge base by using user's question directly.
|
40 |
+
You need to expand or paraphrase user's question by multiple ways such as using synonyms words/phrase,
|
41 |
+
writing the abbreviation in its entirety, adding some extra descriptions or explanations,
|
42 |
+
changing the way of expression, translating the original question into another language (English/Chinese), etc.
|
43 |
+
And return 5 versions of question and one is from translation.
|
44 |
+
Just list the question. No other words are needed.
|
45 |
+
"""
|
46 |
+
return self.prompt
|
47 |
+
|
48 |
+
|
49 |
+
class RewriteQuestion(Generate, ABC):
|
50 |
+
component_name = "RewriteQuestion"
|
51 |
+
|
52 |
+
def _run(self, history, **kwargs):
|
53 |
+
if not hasattr(self, "_loop"):
|
54 |
+
setattr(self, "_loop", 0)
|
55 |
+
if self._loop >= self._param.loop:
|
56 |
+
self._loop = 0
|
57 |
+
raise Exception("Maximum loop time exceeds. Can't find relevant information.")
|
58 |
+
self._loop += 1
|
59 |
+
q = "Question: "
|
60 |
+
for r, c in self._canvas.history[::-1]:
|
61 |
+
if r == "user":
|
62 |
+
q += c
|
63 |
+
break
|
64 |
+
|
65 |
+
chat_mdl = LLMBundle(self._canvas.get_tenant_id(), LLMType.CHAT, self._param.llm_id)
|
66 |
+
ans = chat_mdl.chat(self._param.get_prompt(), [{"role": "user", "content": q}],
|
67 |
+
self._param.gen_conf())
|
68 |
+
|
69 |
+
print(ans, ":::::::::::::::::::::::::::::::::")
|
70 |
+
return RewriteQuestion.be_output(ans)
|
71 |
+
|
72 |
+
|
agent/component/switch.py
ADDED
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#
|
2 |
+
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
3 |
+
#
|
4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5 |
+
# you may not use this file except in compliance with the License.
|
6 |
+
# You may obtain a copy of the License at
|
7 |
+
#
|
8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9 |
+
#
|
10 |
+
# Unless required by applicable law or agreed to in writing, software
|
11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 |
+
# See the License for the specific language governing permissions and
|
14 |
+
# limitations under the License.
|
15 |
+
#
|
16 |
+
from abc import ABC
|
17 |
+
|
18 |
+
import pandas as pd
|
19 |
+
from agent.component.base import ComponentBase, ComponentParamBase
|
20 |
+
|
21 |
+
|
22 |
+
class SwitchParam(ComponentParamBase):
|
23 |
+
|
24 |
+
"""
|
25 |
+
Define the Switch component parameters.
|
26 |
+
"""
|
27 |
+
def __init__(self):
|
28 |
+
super().__init__()
|
29 |
+
"""
|
30 |
+
{
|
31 |
+
"cpn_id": "categorize:0",
|
32 |
+
"not": False,
|
33 |
+
"operator": "gt/gte/lt/lte/eq/in",
|
34 |
+
"value": "",
|
35 |
+
"to": ""
|
36 |
+
}
|
37 |
+
"""
|
38 |
+
self.conditions = []
|
39 |
+
self.default = ""
|
40 |
+
|
41 |
+
def check(self):
|
42 |
+
self.check_empty(self.conditions, "[Switch] conditions")
|
43 |
+
self.check_empty(self.default, "[Switch] Default path")
|
44 |
+
for cond in self.conditions:
|
45 |
+
if not cond["to"]: raise ValueError(f"[Switch] 'To' can not be empty!")
|
46 |
+
|
47 |
+
def operators(self, field, op, value):
|
48 |
+
if op == "gt":
|
49 |
+
return float(field) > float(value)
|
50 |
+
if op == "gte":
|
51 |
+
return float(field) >= float(value)
|
52 |
+
if op == "lt":
|
53 |
+
return float(field) < float(value)
|
54 |
+
if op == "lte":
|
55 |
+
return float(field) <= float(value)
|
56 |
+
if op == "eq":
|
57 |
+
return str(field) == str(value)
|
58 |
+
if op == "in":
|
59 |
+
return str(field).find(str(value)) >= 0
|
60 |
+
return False
|
61 |
+
|
62 |
+
|
63 |
+
class Switch(ComponentBase, ABC):
|
64 |
+
component_name = "Switch"
|
65 |
+
|
66 |
+
def _run(self, history, **kwargs):
|
67 |
+
for cond in self._param.conditions:
|
68 |
+
input = self._canvas.get_component(cond["cpn_id"])["obj"].output()[1]
|
69 |
+
if self._param.operators(input.iloc[0, 0], cond["operator"], cond["value"]):
|
70 |
+
if not cond["not"]:
|
71 |
+
return pd.DataFrame([{"content": cond["to"]}])
|
72 |
+
|
73 |
+
return pd.DataFrame([{"content": self._param.default}])
|
74 |
+
|
75 |
+
|
76 |
+
|
77 |
+
|
agent/component/wikipedia.py
ADDED
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#
|
2 |
+
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
3 |
+
#
|
4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5 |
+
# you may not use this file except in compliance with the License.
|
6 |
+
# You may obtain a copy of the License at
|
7 |
+
#
|
8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9 |
+
#
|
10 |
+
# Unless required by applicable law or agreed to in writing, software
|
11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 |
+
# See the License for the specific language governing permissions and
|
14 |
+
# limitations under the License.
|
15 |
+
#
|
16 |
+
import random
|
17 |
+
from abc import ABC
|
18 |
+
from functools import partial
|
19 |
+
import wikipedia
|
20 |
+
import pandas as pd
|
21 |
+
from agent.settings import DEBUG
|
22 |
+
from agent.component.base import ComponentBase, ComponentParamBase
|
23 |
+
|
24 |
+
|
25 |
+
class WikipediaParam(ComponentParamBase):
|
26 |
+
"""
|
27 |
+
Define the Wikipedia component parameters.
|
28 |
+
"""
|
29 |
+
|
30 |
+
def __init__(self):
|
31 |
+
super().__init__()
|
32 |
+
self.top_n = 10
|
33 |
+
self.language = "en"
|
34 |
+
|
35 |
+
def check(self):
|
36 |
+
self.check_positive_integer(self.top_n, "Top N")
|
37 |
+
self.check_valid_value(self.language, "Wikipedia languages",
|
38 |
+
['af', 'pl', 'ar', 'ast', 'az', 'bg', 'nan', 'bn', 'be', 'ca', 'cs', 'cy', 'da', 'de',
|
39 |
+
'et', 'el', 'en', 'es', 'eo', 'eu', 'fa', 'fr', 'gl', 'ko', 'hy', 'hi', 'hr', 'id',
|
40 |
+
'it', 'he', 'ka', 'lld', 'la', 'lv', 'lt', 'hu', 'mk', 'arz', 'ms', 'min', 'my', 'nl',
|
41 |
+
'ja', 'nb', 'nn', 'ce', 'uz', 'pt', 'kk', 'ro', 'ru', 'ceb', 'sk', 'sl', 'sr', 'sh',
|
42 |
+
'fi', 'sv', 'ta', 'tt', 'th', 'tg', 'azb', 'tr', 'uk', 'ur', 'vi', 'war', 'zh', 'yue'])
|
43 |
+
|
44 |
+
|
45 |
+
class Wikipedia(ComponentBase, ABC):
|
46 |
+
component_name = "Wikipedia"
|
47 |
+
|
48 |
+
def _run(self, history, **kwargs):
|
49 |
+
ans = self.get_input()
|
50 |
+
ans = " - ".join(ans["content"]) if "content" in ans else ""
|
51 |
+
if not ans:
|
52 |
+
return Wikipedia.be_output("")
|
53 |
+
|
54 |
+
try:
|
55 |
+
wiki_res = []
|
56 |
+
wikipedia.set_lang(self._param.language)
|
57 |
+
wiki_engine = wikipedia
|
58 |
+
for wiki_key in wiki_engine.search(ans, results=self._param.top_n):
|
59 |
+
page = wiki_engine.page(title=wiki_key, auto_suggest=False)
|
60 |
+
wiki_res.append({"content": '<a href="' + page.url + '">' + page.title + '</a> ' + page.summary})
|
61 |
+
except Exception as e:
|
62 |
+
return Wikipedia.be_output("**ERROR**: " + str(e))
|
63 |
+
|
64 |
+
if not wiki_res:
|
65 |
+
return Wikipedia.be_output("")
|
66 |
+
|
67 |
+
df = pd.DataFrame(wiki_res)
|
68 |
+
if DEBUG: print(df, ":::::::::::::::::::::::::::::::::")
|
69 |
+
return df
|
agent/settings.py
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#
|
2 |
+
# Copyright 2019 The FATE Authors. All Rights Reserved.
|
3 |
+
#
|
4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5 |
+
# you may not use this file except in compliance with the License.
|
6 |
+
# You may obtain a copy of the License at
|
7 |
+
#
|
8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9 |
+
#
|
10 |
+
# Unless required by applicable law or agreed to in writing, software
|
11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 |
+
# See the License for the specific language governing permissions and
|
14 |
+
# limitations under the License.
|
15 |
+
#
|
16 |
+
# Logger
|
17 |
+
import os
|
18 |
+
|
19 |
+
from api.utils.file_utils import get_project_base_directory
|
20 |
+
from api.utils.log_utils import LoggerFactory, getLogger
|
21 |
+
|
22 |
+
DEBUG = 0
|
23 |
+
LoggerFactory.set_directory(
|
24 |
+
os.path.join(
|
25 |
+
get_project_base_directory(),
|
26 |
+
"logs",
|
27 |
+
"flow"))
|
28 |
+
# {CRITICAL: 50, FATAL:50, ERROR:40, WARNING:30, WARN:30, INFO:20, DEBUG:10, NOTSET:0}
|
29 |
+
LoggerFactory.LEVEL = 30
|
30 |
+
|
31 |
+
flow_logger = getLogger("flow")
|
32 |
+
database_logger = getLogger("database")
|
33 |
+
FLOAT_ZERO = 1e-8
|
34 |
+
PARAM_MAXDEPTH = 5
|
agent/templates/HR_callout_zh.json
ADDED
The diff for this file is too large to render.
See raw diff
|
|
agent/templates/customer_service.json
ADDED
@@ -0,0 +1,620 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"id": 3,
|
3 |
+
"title": "Customer service",
|
4 |
+
"description": "A call-in customer service chat bot. It will provide useful information about the products, answer customers' questions and soothe the customers' bad emotions.",
|
5 |
+
"canvas_type": "chatbot",
|
6 |
+
"dsl": {
|
7 |
+
"answer": [],
|
8 |
+
"components": {
|
9 |
+
"answer:0": {
|
10 |
+
"downstream": ["categorize:0", "generate:complain"],
|
11 |
+
"obj": {
|
12 |
+
"component_name": "Answer",
|
13 |
+
"params": {}
|
14 |
+
},
|
15 |
+
"upstream": [
|
16 |
+
"begin",
|
17 |
+
"message:get_contact",
|
18 |
+
"generate:casual",
|
19 |
+
"generate:answer",
|
20 |
+
"generate:ask_contact"
|
21 |
+
]
|
22 |
+
},
|
23 |
+
"begin": {
|
24 |
+
"downstream": ["answer:0"],
|
25 |
+
"obj": {
|
26 |
+
"component_name": "Begin",
|
27 |
+
"params": {
|
28 |
+
"prologue": "Hi! How can I help you?"
|
29 |
+
}
|
30 |
+
},
|
31 |
+
"upstream": []
|
32 |
+
},
|
33 |
+
"categorize:0": {
|
34 |
+
"downstream": [
|
35 |
+
"generate:casual",
|
36 |
+
"generate:complain",
|
37 |
+
"message:get_contact",
|
38 |
+
"retrieval:0"
|
39 |
+
],
|
40 |
+
"obj": {
|
41 |
+
"component_name": "Categorize",
|
42 |
+
"params": {
|
43 |
+
"category_description": {
|
44 |
+
"answer": {
|
45 |
+
"description": "This answer provide a specific contact information, like e-mail, phone number, wechat number, line number, twitter, discord, etc,.",
|
46 |
+
"examples": "My phone number is 203921\nkevinhu.hk@gmail.com\nThis is my discord number: johndowson_29384",
|
47 |
+
"to": "message:get_contact"
|
48 |
+
},
|
49 |
+
"casual": {
|
50 |
+
"description": "The question is not about the product usage, appearance and how it works. Just casual chat.",
|
51 |
+
"examples": "How are you doing?\nWhat is your name?\nAre you a robot?\nWhat's the weather?\nWill it rain?",
|
52 |
+
"to": "generate:casual"
|
53 |
+
},
|
54 |
+
"complain": {
|
55 |
+
"description": "Complain even curse about the product or service you provide. But the comment is not specific enough.",
|
56 |
+
"examples": "How bad is it.\nIt's really sucks.\nDamn, for God's sake, can it be more steady?\nShit, I just can't use this shit.\nI can't stand it anymore.",
|
57 |
+
"to": "generate:complain"
|
58 |
+
},
|
59 |
+
"product_related": {
|
60 |
+
"description": "The question is about the product usage, appearance and how it works.",
|
61 |
+
"examples": "Why it always beaming?\nHow to install it onto the wall?\nIt leaks, what to do?\nException: Can't connect to ES cluster\nHow to build the RAGFlow image from scratch",
|
62 |
+
"to": "retrieval:0"
|
63 |
+
}
|
64 |
+
},
|
65 |
+
"llm_id": "deepseek-chat",
|
66 |
+
"message_history_window_size": 8
|
67 |
+
}
|
68 |
+
},
|
69 |
+
"upstream": ["answer:0"]
|
70 |
+
},
|
71 |
+
"generate:answer": {
|
72 |
+
"downstream": ["answer:0"],
|
73 |
+
"obj": {
|
74 |
+
"component_name": "Generate",
|
75 |
+
"params": {
|
76 |
+
"llm_id": "deepseek-chat",
|
77 |
+
"prompt": "You are an intelligent assistant. Please answer the question based on content of knowledge base. When all knowledge base content is irrelevant to the question, your answer must include the sentence \"The answer you are looking for is not found in the knowledge base!\". Answers need to consider chat history.\n Knowledge base content is as following:\n {input}\n The above is the content of knowledge base."
|
78 |
+
}
|
79 |
+
},
|
80 |
+
"upstream": ["relevant:0"]
|
81 |
+
},
|
82 |
+
"generate:ask_contact": {
|
83 |
+
"downstream": ["answer:0"],
|
84 |
+
"obj": {
|
85 |
+
"component_name": "Generate",
|
86 |
+
"params": {
|
87 |
+
"cite": false,
|
88 |
+
"llm_id": "deepseek-chat",
|
89 |
+
"message_history_window_size": 12,
|
90 |
+
"prompt": "You are a customer support. But you can't answer to customers' question. You need to request their contact like E-mail, phone number, Wechat number, LINE number, twitter, discord, etc,. Product experts will contact them later. Please do not ask the same question twice."
|
91 |
+
}
|
92 |
+
},
|
93 |
+
"upstream": ["relevant:0"]
|
94 |
+
},
|
95 |
+
"generate:casual": {
|
96 |
+
"downstream": ["answer:0"],
|
97 |
+
"obj": {
|
98 |
+
"component_name": "Generate",
|
99 |
+
"params": {
|
100 |
+
"cite": false,
|
101 |
+
"llm_id": "deepseek-chat",
|
102 |
+
"message_history_window_size": 12,
|
103 |
+
"prompt": "You are a customer support. But the customer wants to have a casual chat with you instead of consulting about the product. Be nice, funny, enthusiasm and concern."
|
104 |
+
}
|
105 |
+
},
|
106 |
+
"upstream": ["categorize:0"]
|
107 |
+
},
|
108 |
+
"generate:complain": {
|
109 |
+
"downstream": ["answer:0"],
|
110 |
+
"obj": {
|
111 |
+
"component_name": "Generate",
|
112 |
+
"params": {
|
113 |
+
"cite": false,
|
114 |
+
"llm_id": "deepseek-chat",
|
115 |
+
"message_history_window_size": 12,
|
116 |
+
"prompt": "You are a customer support. the Customers complain even curse about the products but not specific enough. You need to ask him/her what's the specific problem with the product. Be nice, patient and concern to soothe your customers’ emotions at first place."
|
117 |
+
}
|
118 |
+
},
|
119 |
+
"upstream": ["categorize:0"]
|
120 |
+
},
|
121 |
+
"message:get_contact": {
|
122 |
+
"downstream": ["answer:0"],
|
123 |
+
"obj": {
|
124 |
+
"component_name": "Message",
|
125 |
+
"params": {
|
126 |
+
"messages": [
|
127 |
+
"Okay, I've already write this down. What else I can do for you?",
|
128 |
+
"Get it. What else I can do for you?",
|
129 |
+
"Thanks for your trust! Our expert will contact ASAP. So, anything else I can do for you?",
|
130 |
+
"Thanks! So, anything else I can do for you?"
|
131 |
+
]
|
132 |
+
}
|
133 |
+
},
|
134 |
+
"upstream": ["categorize:0"]
|
135 |
+
},
|
136 |
+
"relevant:0": {
|
137 |
+
"downstream": ["generate:answer", "generate:ask_contact"],
|
138 |
+
"obj": {
|
139 |
+
"component_name": "Relevant",
|
140 |
+
"params": {
|
141 |
+
"llm_id": "deepseek-chat",
|
142 |
+
"no": "generate:ask_contact",
|
143 |
+
"temperature": 0.02,
|
144 |
+
"yes": "generate:answer"
|
145 |
+
}
|
146 |
+
},
|
147 |
+
"upstream": ["retrieval:0"]
|
148 |
+
},
|
149 |
+
"retrieval:0": {
|
150 |
+
"downstream": ["relevant:0"],
|
151 |
+
"obj": {
|
152 |
+
"component_name": "Retrieval",
|
153 |
+
"params": {
|
154 |
+
"kb_ids": [],
|
155 |
+
"keywords_similarity_weight": 0.3,
|
156 |
+
"rerank_id": "BAAI/bge-reranker-v2-m3",
|
157 |
+
"similarity_threshold": 0.2,
|
158 |
+
"top_k": 1024,
|
159 |
+
"top_n": 6
|
160 |
+
}
|
161 |
+
},
|
162 |
+
"upstream": ["categorize:0"]
|
163 |
+
}
|
164 |
+
},
|
165 |
+
"graph": {
|
166 |
+
"edges": [
|
167 |
+
{
|
168 |
+
"id": "63a2f242-8e71-4098-a46a-459a76d538bd",
|
169 |
+
"label": "",
|
170 |
+
"source": "begin",
|
171 |
+
"target": "answer:0"
|
172 |
+
},
|
173 |
+
{
|
174 |
+
"id": "7f68384d-3441-4bfa-bf13-69af67e857d2",
|
175 |
+
"label": "",
|
176 |
+
"source": "categorize:0",
|
177 |
+
"sourceHandle": "casual",
|
178 |
+
"target": "generate:casual"
|
179 |
+
},
|
180 |
+
{
|
181 |
+
"id": "c9bf8e81-9345-4885-b565-be2f5b16f6ef",
|
182 |
+
"label": "",
|
183 |
+
"source": "categorize:0",
|
184 |
+
"sourceHandle": "complain",
|
185 |
+
"target": "generate:complain"
|
186 |
+
},
|
187 |
+
{
|
188 |
+
"id": "2f326699-621b-4d28-ab98-70d99ad21add",
|
189 |
+
"label": "",
|
190 |
+
"source": "categorize:0",
|
191 |
+
"sourceHandle": "answer",
|
192 |
+
"target": "message:get_contact"
|
193 |
+
},
|
194 |
+
{
|
195 |
+
"id": "reactflow__edge-relevant:0yes-generate:answerc",
|
196 |
+
"source": "relevant:0",
|
197 |
+
"sourceHandle": "yes",
|
198 |
+
"target": "generate:answer",
|
199 |
+
"targetHandle": "c"
|
200 |
+
},
|
201 |
+
{
|
202 |
+
"id": "reactflow__edge-relevant:0no-generate:ask_contactc",
|
203 |
+
"source": "relevant:0",
|
204 |
+
"sourceHandle": "no",
|
205 |
+
"target": "generate:ask_contact",
|
206 |
+
"targetHandle": "c"
|
207 |
+
},
|
208 |
+
{
|
209 |
+
"id": "reactflow__edge-message:get_contactb-answer:0c",
|
210 |
+
"markerEnd": "logo",
|
211 |
+
"source": "message:get_contact",
|
212 |
+
"sourceHandle": "b",
|
213 |
+
"style": {
|
214 |
+
"stroke": "rgb(202 197 245)",
|
215 |
+
"strokeWidth": 2
|
216 |
+
},
|
217 |
+
"target": "answer:0",
|
218 |
+
"targetHandle": "c",
|
219 |
+
"type": "buttonEdge"
|
220 |
+
},
|
221 |
+
{
|
222 |
+
"id": "reactflow__edge-generate:casualb-answer:0d",
|
223 |
+
"markerEnd": "logo",
|
224 |
+
"source": "generate:casual",
|
225 |
+
"sourceHandle": "b",
|
226 |
+
"style": {
|
227 |
+
"stroke": "rgb(202 197 245)",
|
228 |
+
"strokeWidth": 2
|
229 |
+
},
|
230 |
+
"target": "answer:0",
|
231 |
+
"targetHandle": "d",
|
232 |
+
"type": "buttonEdge"
|
233 |
+
},
|
234 |
+
{
|
235 |
+
"id": "reactflow__edge-generate:answerb-answer:0d",
|
236 |
+
"markerEnd": "logo",
|
237 |
+
"source": "generate:answer",
|
238 |
+
"sourceHandle": "b",
|
239 |
+
"style": {
|
240 |
+
"stroke": "rgb(202 197 245)",
|
241 |
+
"strokeWidth": 2
|
242 |
+
},
|
243 |
+
"target": "answer:0",
|
244 |
+
"targetHandle": "d",
|
245 |
+
"type": "buttonEdge"
|
246 |
+
},
|
247 |
+
{
|
248 |
+
"id": "reactflow__edge-categorize:0product_related-retrieval:0a",
|
249 |
+
"markerEnd": "logo",
|
250 |
+
"source": "categorize:0",
|
251 |
+
"sourceHandle": "product_related",
|
252 |
+
"style": {
|
253 |
+
"stroke": "rgb(202 197 245)",
|
254 |
+
"strokeWidth": 2
|
255 |
+
},
|
256 |
+
"target": "retrieval:0",
|
257 |
+
"targetHandle": "a",
|
258 |
+
"type": "buttonEdge"
|
259 |
+
},
|
260 |
+
{
|
261 |
+
"id": "reactflow__edge-retrieval:0d-relevant:0c",
|
262 |
+
"markerEnd": "logo",
|
263 |
+
"source": "retrieval:0",
|
264 |
+
"sourceHandle": "d",
|
265 |
+
"style": {
|
266 |
+
"stroke": "rgb(202 197 245)",
|
267 |
+
"strokeWidth": 2
|
268 |
+
},
|
269 |
+
"target": "relevant:0",
|
270 |
+
"targetHandle": "c",
|
271 |
+
"type": "buttonEdge"
|
272 |
+
},
|
273 |
+
{
|
274 |
+
"id": "reactflow__edge-answer:0c-categorize:0c",
|
275 |
+
"markerEnd": "logo",
|
276 |
+
"source": "answer:0",
|
277 |
+
"sourceHandle": "c",
|
278 |
+
"style": {
|
279 |
+
"stroke": "rgb(202 197 245)",
|
280 |
+
"strokeWidth": 2
|
281 |
+
},
|
282 |
+
"target": "categorize:0",
|
283 |
+
"targetHandle": "c",
|
284 |
+
"type": "buttonEdge"
|
285 |
+
},
|
286 |
+
{
|
287 |
+
"id": "reactflow__edge-answer:0a-generate:complaind",
|
288 |
+
"markerEnd": "logo",
|
289 |
+
"source": "generate:complain",
|
290 |
+
"sourceHandle": "a",
|
291 |
+
"style": {
|
292 |
+
"stroke": "rgb(202 197 245)",
|
293 |
+
"strokeWidth": 2
|
294 |
+
},
|
295 |
+
"target": "answer:0",
|
296 |
+
"targetHandle": "d",
|
297 |
+
"type": "buttonEdge"
|
298 |
+
},
|
299 |
+
{
|
300 |
+
"id": "reactflow__edge-generate:ask_contacta-answer:0d",
|
301 |
+
"markerEnd": "logo",
|
302 |
+
"source": "generate:ask_contact",
|
303 |
+
"sourceHandle": "a",
|
304 |
+
"style": {
|
305 |
+
"stroke": "rgb(202 197 245)",
|
306 |
+
"strokeWidth": 2
|
307 |
+
},
|
308 |
+
"target": "answer:0",
|
309 |
+
"targetHandle": "d",
|
310 |
+
"type": "buttonEdge"
|
311 |
+
}
|
312 |
+
],
|
313 |
+
"nodes": [
|
314 |
+
{
|
315 |
+
"data": {
|
316 |
+
"form": {
|
317 |
+
"prologue": "Hi! How can I help you?"
|
318 |
+
},
|
319 |
+
"label": "Begin",
|
320 |
+
"name": "Opener"
|
321 |
+
},
|
322 |
+
"dragging": false,
|
323 |
+
"height": 50,
|
324 |
+
"id": "begin",
|
325 |
+
"position": {
|
326 |
+
"x": 404.55092213629115,
|
327 |
+
"y": 296.8772566137603
|
328 |
+
},
|
329 |
+
"positionAbsolute": {
|
330 |
+
"x": 404.55092213629115,
|
331 |
+
"y": 296.8772566137603
|
332 |
+
},
|
333 |
+
"selected": false,
|
334 |
+
"sourcePosition": "left",
|
335 |
+
"targetPosition": "right",
|
336 |
+
"type": "beginNode",
|
337 |
+
"width": 50
|
338 |
+
},
|
339 |
+
{
|
340 |
+
"data": {
|
341 |
+
"form": {},
|
342 |
+
"label": "Answer",
|
343 |
+
"name": "Interface"
|
344 |
+
},
|
345 |
+
"dragging": false,
|
346 |
+
"height": 100,
|
347 |
+
"id": "answer:0",
|
348 |
+
"position": {
|
349 |
+
"x": 638.4631551507972,
|
350 |
+
"y": 71.36899317395626
|
351 |
+
},
|
352 |
+
"positionAbsolute": {
|
353 |
+
"x": 638.4631551507972,
|
354 |
+
"y": 71.36899317395626
|
355 |
+
},
|
356 |
+
"selected": false,
|
357 |
+
"sourcePosition": "left",
|
358 |
+
"targetPosition": "right",
|
359 |
+
"type": "logicNode",
|
360 |
+
"width": 100
|
361 |
+
},
|
362 |
+
{
|
363 |
+
"data": {
|
364 |
+
"form": {
|
365 |
+
"category_description": {
|
366 |
+
"answer": {
|
367 |
+
"description": "This answer provide a specific contact information, like e-mail, phone number, wechat number, line number, twitter, discord, etc,.",
|
368 |
+
"examples": "My phone number is 203921\nkevinhu.hk@gmail.com\nThis is my discord number: johndowson_29384",
|
369 |
+
"to": "message:get_contact"
|
370 |
+
},
|
371 |
+
"casual": {
|
372 |
+
"description": "The question is not about the product usage, appearance and how it works. Just casual chat.",
|
373 |
+
"examples": "How are you doing?\nWhat is your name?\nAre you a robot?\nWhat's the weather?\nWill it rain?",
|
374 |
+
"to": "generate:casual"
|
375 |
+
},
|
376 |
+
"complain": {
|
377 |
+
"description": "Complain even curse about the product or service you provide. But the comment is not specific enough.",
|
378 |
+
"examples": "How bad is it.\nIt's really sucks.\nDamn, for God's sake, can it be more steady?\nShit, I just can't use this shit.\nI can't stand it anymore.",
|
379 |
+
"to": "generate:complain"
|
380 |
+
},
|
381 |
+
"product_related": {
|
382 |
+
"description": "The question is about the product usage, appearance and how it works.",
|
383 |
+
"examples": "Why it always beaming?\nHow to install it onto the wall?\nIt leaks, what to do?\nException: Can't connect to ES cluster\nHow to build the RAGFlow image from scratch",
|
384 |
+
"to": "retrieval:0"
|
385 |
+
}
|
386 |
+
},
|
387 |
+
"llm_id": "deepseek-chat",
|
388 |
+
"message_history_window_size": 8
|
389 |
+
},
|
390 |
+
"label": "Categorize",
|
391 |
+
"name": "Question Categorize"
|
392 |
+
},
|
393 |
+
"dragging": false,
|
394 |
+
"height": 100,
|
395 |
+
"id": "categorize:0",
|
396 |
+
"position": {
|
397 |
+
"x": -5.661990007284574,
|
398 |
+
"y": 461.0436851280078
|
399 |
+
},
|
400 |
+
"positionAbsolute": {
|
401 |
+
"x": -5.661990007284574,
|
402 |
+
"y": 461.0436851280078
|
403 |
+
},
|
404 |
+
"selected": false,
|
405 |
+
"sourcePosition": "left",
|
406 |
+
"targetPosition": "right",
|
407 |
+
"type": "categorizeNode",
|
408 |
+
"width": 100
|
409 |
+
},
|
410 |
+
{
|
411 |
+
"data": {
|
412 |
+
"form": {
|
413 |
+
"cite": false,
|
414 |
+
"llm_id": "deepseek-chat",
|
415 |
+
"message_history_window_size": 12,
|
416 |
+
"prompt": "You are a customer support. But the customer wants to have a casual chat with you instead of consulting about the product. Be nice, funny, enthusiasm and concern.",
|
417 |
+
"temperature": 0.9
|
418 |
+
},
|
419 |
+
"label": "Generate",
|
420 |
+
"name": "Casual chat"
|
421 |
+
},
|
422 |
+
"dragging": false,
|
423 |
+
"height": 150,
|
424 |
+
"id": "generate:casual",
|
425 |
+
"position": {
|
426 |
+
"x": 314.3012082990795,
|
427 |
+
"y": -86.46384197439605
|
428 |
+
},
|
429 |
+
"positionAbsolute": {
|
430 |
+
"x": 314.3012082990795,
|
431 |
+
"y": -86.46384197439605
|
432 |
+
},
|
433 |
+
"selected": false,
|
434 |
+
"sourcePosition": "left",
|
435 |
+
"targetPosition": "right",
|
436 |
+
"type": "logicNode",
|
437 |
+
"width": 150
|
438 |
+
},
|
439 |
+
{
|
440 |
+
"data": {
|
441 |
+
"form": {
|
442 |
+
"cite": false,
|
443 |
+
"llm_id": "deepseek-chat",
|
444 |
+
"message_history_window_size": 12,
|
445 |
+
"prompt": "You are a customer support. the Customers complain even curse about the products but not specific enough. You need to ask him/her what's the specific problem with the product. Be nice, patient and concern to soothe your customers’ emotions at first place.",
|
446 |
+
"temperature": 0.9
|
447 |
+
},
|
448 |
+
"label": "Generate",
|
449 |
+
"name": "Soothe mood"
|
450 |
+
},
|
451 |
+
"dragging": false,
|
452 |
+
"height": 150,
|
453 |
+
"id": "generate:complain",
|
454 |
+
"position": {
|
455 |
+
"x": 638.3075558015155,
|
456 |
+
"y": 397.78425175650347
|
457 |
+
},
|
458 |
+
"positionAbsolute": {
|
459 |
+
"x": 638.3075558015155,
|
460 |
+
"y": 397.78425175650347
|
461 |
+
},
|
462 |
+
"selected": false,
|
463 |
+
"sourcePosition": "left",
|
464 |
+
"targetPosition": "right",
|
465 |
+
"type": "logicNode",
|
466 |
+
"width": 150
|
467 |
+
},
|
468 |
+
{
|
469 |
+
"data": {
|
470 |
+
"form": {
|
471 |
+
"kb_ids": [],
|
472 |
+
"keywords_similarity_weight": 0.3,
|
473 |
+
"rerank_id": "BAAI/bge-reranker-v2-m3",
|
474 |
+
"similarity_threshold": 0.2,
|
475 |
+
"top_k": 1024,
|
476 |
+
"top_n": 6
|
477 |
+
},
|
478 |
+
"label": "Retrieval",
|
479 |
+
"name": "Search product info"
|
480 |
+
},
|
481 |
+
"dragging": false,
|
482 |
+
"height": 100,
|
483 |
+
"id": "retrieval:0",
|
484 |
+
"position": {
|
485 |
+
"x": 46.056812569554474,
|
486 |
+
"y": -107.6498359566391
|
487 |
+
},
|
488 |
+
"positionAbsolute": {
|
489 |
+
"x": 46.056812569554474,
|
490 |
+
"y": -107.6498359566391
|
491 |
+
},
|
492 |
+
"selected": false,
|
493 |
+
"sourcePosition": "left",
|
494 |
+
"targetPosition": "right",
|
495 |
+
"type": "logicNode",
|
496 |
+
"width": 100
|
497 |
+
},
|
498 |
+
{
|
499 |
+
"data": {
|
500 |
+
"form": {
|
501 |
+
"llm_id": "deepseek-chat",
|
502 |
+
"no": "generate:ask_contact",
|
503 |
+
"temperature": 0.02,
|
504 |
+
"yes": "generate:answer"
|
505 |
+
},
|
506 |
+
"label": "Relevant",
|
507 |
+
"name": "Relevant?"
|
508 |
+
},
|
509 |
+
"dragging": false,
|
510 |
+
"height": 70,
|
511 |
+
"id": "relevant:0",
|
512 |
+
"position": {
|
513 |
+
"x": 46.93961268163955,
|
514 |
+
"y": -332.00374591025786
|
515 |
+
},
|
516 |
+
"positionAbsolute": {
|
517 |
+
"x": 46.93961268163955,
|
518 |
+
"y": -332.00374591025786
|
519 |
+
},
|
520 |
+
"selected": false,
|
521 |
+
"sourcePosition": "left",
|
522 |
+
"targetPosition": "right",
|
523 |
+
"type": "relevantNode",
|
524 |
+
"width": 70
|
525 |
+
},
|
526 |
+
{
|
527 |
+
"data": {
|
528 |
+
"form": {
|
529 |
+
"llm_id": "deepseek-chat",
|
530 |
+
"prompt": "You are an intelligent assistant. Please answer the question based on content of knowledge base. When all knowledge base content is irrelevant to the question, your answer must include the sentence \"The answer you are looking for is not found in the knowledge base!\". Answers need to consider chat history.\n Knowledge base content is as following:\n {input}\n The above is the content of knowledge base.",
|
531 |
+
"temperature": 0.02
|
532 |
+
},
|
533 |
+
"label": "Generate",
|
534 |
+
"name": "Product info"
|
535 |
+
},
|
536 |
+
"dragging": false,
|
537 |
+
"height": 150,
|
538 |
+
"id": "generate:answer",
|
539 |
+
"position": {
|
540 |
+
"x": 413.5337945397181,
|
541 |
+
"y": -255.54195657210374
|
542 |
+
},
|
543 |
+
"positionAbsolute": {
|
544 |
+
"x": 413.5337945397181,
|
545 |
+
"y": -255.54195657210374
|
546 |
+
},
|
547 |
+
"selected": false,
|
548 |
+
"sourcePosition": "left",
|
549 |
+
"targetPosition": "right",
|
550 |
+
"type": "logicNode",
|
551 |
+
"width": 150
|
552 |
+
},
|
553 |
+
{
|
554 |
+
"data": {
|
555 |
+
"form": {
|
556 |
+
"cite": false,
|
557 |
+
"llm_id": "deepseek-chat",
|
558 |
+
"message_history_window_size": 12,
|
559 |
+
"prompt": "You are a customer support. But you can't answer to customers' question. You need to request their contact like E-mail, phone number, Wechat number, LINE number, twitter, discord, etc,. Product experts will contact them later. Please do not ask the same question twice.",
|
560 |
+
"temperature": 0.9
|
561 |
+
},
|
562 |
+
"label": "Generate",
|
563 |
+
"name": "Request contact"
|
564 |
+
},
|
565 |
+
"dragging": false,
|
566 |
+
"height": 150,
|
567 |
+
"id": "generate:ask_contact",
|
568 |
+
"position": {
|
569 |
+
"x": 636.1639603425758,
|
570 |
+
"y": -391.62634222619454
|
571 |
+
},
|
572 |
+
"positionAbsolute": {
|
573 |
+
"x": 636.1639603425758,
|
574 |
+
"y": -391.62634222619454
|
575 |
+
},
|
576 |
+
"selected": true,
|
577 |
+
"sourcePosition": "left",
|
578 |
+
"targetPosition": "right",
|
579 |
+
"type": "logicNode",
|
580 |
+
"width": 150
|
581 |
+
},
|
582 |
+
{
|
583 |
+
"data": {
|
584 |
+
"form": {
|
585 |
+
"messages": [
|
586 |
+
"Okay, I've already write this down. What else I can do for you?",
|
587 |
+
"Get it. What else I can do for you?",
|
588 |
+
"Thanks for your trust! Our expert will contact ASAP. So, anything else I can do for you?",
|
589 |
+
"Thanks! So, anything else I can do for you?"
|
590 |
+
]
|
591 |
+
},
|
592 |
+
"label": "Message",
|
593 |
+
"name": "What else?"
|
594 |
+
},
|
595 |
+
"dragging": false,
|
596 |
+
"height": 100,
|
597 |
+
"id": "message:get_contact",
|
598 |
+
"position": {
|
599 |
+
"x": 261.48313054267913,
|
600 |
+
"y": 119.6490357959155
|
601 |
+
},
|
602 |
+
"positionAbsolute": {
|
603 |
+
"x": 261.48313054267913,
|
604 |
+
"y": 119.6490357959155
|
605 |
+
},
|
606 |
+
"selected": false,
|
607 |
+
"sourcePosition": "left",
|
608 |
+
"targetPosition": "right",
|
609 |
+
"type": "logicNode",
|
610 |
+
"width": 100
|
611 |
+
}
|
612 |
+
]
|
613 |
+
},
|
614 |
+
"history": [],
|
615 |
+
"messages": [],
|
616 |
+
"path": [],
|
617 |
+
"reference": []
|
618 |
+
},
|
619 |
+
"avatar": "data:image/jpeg;base64,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"
|
620 |
+
}
|
agent/templates/general_chat_bot.json
ADDED
@@ -0,0 +1,335 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"id": 1,
|
3 |
+
"title": "Chat bot",
|
4 |
+
"description": "A general chat bot. It is based on Self-RAG mechanism. What you need to do is setting up knowleage base in 'Retrieval'",
|
5 |
+
"canvas_type": "chatbot",
|
6 |
+
"dsl": {
|
7 |
+
"answer": [],
|
8 |
+
"components": {
|
9 |
+
"answer:0": {
|
10 |
+
"downstream": ["retrieval:0"],
|
11 |
+
"obj": {
|
12 |
+
"component_name": "Answer",
|
13 |
+
"params": {}
|
14 |
+
},
|
15 |
+
"upstream": ["begin", "generate:0"]
|
16 |
+
},
|
17 |
+
"begin": {
|
18 |
+
"downstream": ["answer:0"],
|
19 |
+
"obj": {
|
20 |
+
"component_name": "Begin",
|
21 |
+
"params": {
|
22 |
+
"prologue": "Hi there!"
|
23 |
+
}
|
24 |
+
},
|
25 |
+
"upstream": []
|
26 |
+
},
|
27 |
+
"generate:0": {
|
28 |
+
"downstream": ["answer:0"],
|
29 |
+
"obj": {
|
30 |
+
"component_name": "Generate",
|
31 |
+
"params": {
|
32 |
+
"llm_id": "deepseek-chat",
|
33 |
+
"prompt": "You are an intelligent assistant. Please answer the question based on content of knowledge base. When all knowledge base content is irrelevant to the question, your answer must include the sentence \"The answer you are looking for is not found in the knowledge base!\". Answers need to consider chat history.\n Knowledge base content is as following:\n {input}\n The above is the content of knowledge base."
|
34 |
+
}
|
35 |
+
},
|
36 |
+
"upstream": ["relevant:0"]
|
37 |
+
},
|
38 |
+
"relevant:0": {
|
39 |
+
"downstream": ["rewrite:0", "generate:0"],
|
40 |
+
"obj": {
|
41 |
+
"component_name": "Relevant",
|
42 |
+
"params": {
|
43 |
+
"llm_id": "deepseek-chat",
|
44 |
+
"no": "rewrite:0",
|
45 |
+
"temperature": 0.02,
|
46 |
+
"yes": "generate:0"
|
47 |
+
}
|
48 |
+
},
|
49 |
+
"upstream": ["retrieval:0"]
|
50 |
+
},
|
51 |
+
"retrieval:0": {
|
52 |
+
"downstream": ["relevant:0"],
|
53 |
+
"obj": {
|
54 |
+
"component_name": "Retrieval",
|
55 |
+
"params": {
|
56 |
+
"empty_response": "Sorry, knowledge base has noting related information.",
|
57 |
+
"kb_ids": [],
|
58 |
+
"keywords_similarity_weight": 0.3,
|
59 |
+
"rerank_id": "BAAI/bge-reranker-v2-m3",
|
60 |
+
"similarity_threshold": 0.2,
|
61 |
+
"top_k": 1024,
|
62 |
+
"top_n": 6
|
63 |
+
}
|
64 |
+
},
|
65 |
+
"upstream": ["answer:0", "rewrite:0"]
|
66 |
+
},
|
67 |
+
"rewrite:0": {
|
68 |
+
"downstream": ["retrieval:0"],
|
69 |
+
"obj": {
|
70 |
+
"component_name": "RewriteQuestion",
|
71 |
+
"params": {
|
72 |
+
"llm_id": "deepseek-chat",
|
73 |
+
"temperature": 0.8
|
74 |
+
}
|
75 |
+
},
|
76 |
+
"upstream": ["relevant:0"]
|
77 |
+
}
|
78 |
+
},
|
79 |
+
"graph": {
|
80 |
+
"edges": [
|
81 |
+
{
|
82 |
+
"id": "81de838d-a541-4b3f-9d68-9172ffd7c6b4",
|
83 |
+
"label": "",
|
84 |
+
"source": "begin",
|
85 |
+
"target": "answer:0"
|
86 |
+
},
|
87 |
+
{
|
88 |
+
"id": "reactflow__edge-answer:0b-retrieval:0c",
|
89 |
+
"markerEnd": "logo",
|
90 |
+
"source": "answer:0",
|
91 |
+
"sourceHandle": "b",
|
92 |
+
"style": {
|
93 |
+
"stroke": "rgb(202 197 245)",
|
94 |
+
"strokeWidth": 2
|
95 |
+
},
|
96 |
+
"target": "retrieval:0",
|
97 |
+
"targetHandle": "c",
|
98 |
+
"type": "buttonEdge"
|
99 |
+
},
|
100 |
+
{
|
101 |
+
"id": "reactflow__edge-generate:0d-answer:0a",
|
102 |
+
"markerEnd": "logo",
|
103 |
+
"source": "generate:0",
|
104 |
+
"sourceHandle": "d",
|
105 |
+
"style": {
|
106 |
+
"stroke": "rgb(202 197 245)",
|
107 |
+
"strokeWidth": 2
|
108 |
+
},
|
109 |
+
"target": "answer:0",
|
110 |
+
"targetHandle": "a",
|
111 |
+
"type": "buttonEdge"
|
112 |
+
},
|
113 |
+
{
|
114 |
+
"id": "reactflow__edge-retrieval:0a-relevant:0b",
|
115 |
+
"markerEnd": "logo",
|
116 |
+
"source": "retrieval:0",
|
117 |
+
"sourceHandle": "a",
|
118 |
+
"style": {
|
119 |
+
"stroke": "rgb(202 197 245)",
|
120 |
+
"strokeWidth": 2
|
121 |
+
},
|
122 |
+
"target": "relevant:0",
|
123 |
+
"targetHandle": "b",
|
124 |
+
"type": "buttonEdge"
|
125 |
+
},
|
126 |
+
{
|
127 |
+
"id": "reactflow__edge-rewrite:0d-retrieval:0b",
|
128 |
+
"markerEnd": "logo",
|
129 |
+
"source": "rewrite:0",
|
130 |
+
"sourceHandle": "d",
|
131 |
+
"style": {
|
132 |
+
"stroke": "rgb(202 197 245)",
|
133 |
+
"strokeWidth": 2
|
134 |
+
},
|
135 |
+
"target": "retrieval:0",
|
136 |
+
"targetHandle": "b",
|
137 |
+
"type": "buttonEdge"
|
138 |
+
},
|
139 |
+
{
|
140 |
+
"id": "reactflow__edge-relevant:0no-rewrite:0a",
|
141 |
+
"markerEnd": "logo",
|
142 |
+
"source": "relevant:0",
|
143 |
+
"sourceHandle": "no",
|
144 |
+
"style": {
|
145 |
+
"stroke": "rgb(202 197 245)",
|
146 |
+
"strokeWidth": 2
|
147 |
+
},
|
148 |
+
"target": "rewrite:0",
|
149 |
+
"targetHandle": "a",
|
150 |
+
"type": "buttonEdge"
|
151 |
+
},
|
152 |
+
{
|
153 |
+
"id": "reactflow__edge-relevant:0yes-generate:0b",
|
154 |
+
"markerEnd": "logo",
|
155 |
+
"source": "relevant:0",
|
156 |
+
"sourceHandle": "yes",
|
157 |
+
"style": {
|
158 |
+
"stroke": "rgb(202 197 245)",
|
159 |
+
"strokeWidth": 2
|
160 |
+
},
|
161 |
+
"target": "generate:0",
|
162 |
+
"targetHandle": "b",
|
163 |
+
"type": "buttonEdge"
|
164 |
+
}
|
165 |
+
],
|
166 |
+
"nodes": [
|
167 |
+
{
|
168 |
+
"data": {
|
169 |
+
"form": {
|
170 |
+
"prologue": "Hi there!"
|
171 |
+
},
|
172 |
+
"label": "Begin",
|
173 |
+
"name": "Opening"
|
174 |
+
},
|
175 |
+
"dragging": false,
|
176 |
+
"height": 50,
|
177 |
+
"id": "begin",
|
178 |
+
"position": {
|
179 |
+
"x": -304.50000000000006,
|
180 |
+
"y": -2.9994670375766646
|
181 |
+
},
|
182 |
+
"positionAbsolute": {
|
183 |
+
"x": -304.50000000000006,
|
184 |
+
"y": -2.9994670375766646
|
185 |
+
},
|
186 |
+
"selected": false,
|
187 |
+
"sourcePosition": "left",
|
188 |
+
"targetPosition": "right",
|
189 |
+
"type": "beginNode",
|
190 |
+
"width": 50
|
191 |
+
},
|
192 |
+
{
|
193 |
+
"data": {
|
194 |
+
"form": {},
|
195 |
+
"label": "Answer",
|
196 |
+
"name": "Interface"
|
197 |
+
},
|
198 |
+
"dragging": false,
|
199 |
+
"height": 100,
|
200 |
+
"id": "answer:0",
|
201 |
+
"position": {
|
202 |
+
"x": -89.78929141627594,
|
203 |
+
"y": -29.530900170597448
|
204 |
+
},
|
205 |
+
"positionAbsolute": {
|
206 |
+
"x": -89.78929141627594,
|
207 |
+
"y": -29.530900170597448
|
208 |
+
},
|
209 |
+
"selected": false,
|
210 |
+
"sourcePosition": "left",
|
211 |
+
"targetPosition": "right",
|
212 |
+
"type": "logicNode",
|
213 |
+
"width": 100
|
214 |
+
},
|
215 |
+
{
|
216 |
+
"data": {
|
217 |
+
"form": {
|
218 |
+
"empty_response": "Sorry, knowledge base has noting related information.",
|
219 |
+
"kb_ids": [],
|
220 |
+
"keywords_similarity_weight": 0.3,
|
221 |
+
"rerank_id": "BAAI/bge-reranker-v2-m3",
|
222 |
+
"similarity_threshold": 0.2,
|
223 |
+
"top_k": 1024,
|
224 |
+
"top_n": 6
|
225 |
+
},
|
226 |
+
"label": "Retrieval",
|
227 |
+
"name": "Search KB"
|
228 |
+
},
|
229 |
+
"dragging": false,
|
230 |
+
"height": 100,
|
231 |
+
"id": "retrieval:0",
|
232 |
+
"position": {
|
233 |
+
"x": 225.1100159655728,
|
234 |
+
"y": -28.569259485130402
|
235 |
+
},
|
236 |
+
"positionAbsolute": {
|
237 |
+
"x": 225.1100159655728,
|
238 |
+
"y": -28.569259485130402
|
239 |
+
},
|
240 |
+
"selected": true,
|
241 |
+
"sourcePosition": "left",
|
242 |
+
"targetPosition": "right",
|
243 |
+
"type": "logicNode",
|
244 |
+
"width": 100
|
245 |
+
},
|
246 |
+
{
|
247 |
+
"data": {
|
248 |
+
"form": {
|
249 |
+
"llm_id": "deepseek-chat",
|
250 |
+
"no": "rewrite:0",
|
251 |
+
"temperature": 0.02,
|
252 |
+
"yes": "generate:0"
|
253 |
+
},
|
254 |
+
"label": "Relevant",
|
255 |
+
"name": "Relevant?"
|
256 |
+
},
|
257 |
+
"dragging": false,
|
258 |
+
"height": 70,
|
259 |
+
"id": "relevant:0",
|
260 |
+
"position": {
|
261 |
+
"x": 225.36494412049518,
|
262 |
+
"y": 307.7194989687223
|
263 |
+
},
|
264 |
+
"positionAbsolute": {
|
265 |
+
"x": 225.36494412049518,
|
266 |
+
"y": 307.7194989687223
|
267 |
+
},
|
268 |
+
"selected": false,
|
269 |
+
"sourcePosition": "left",
|
270 |
+
"targetPosition": "right",
|
271 |
+
"type": "relevantNode",
|
272 |
+
"width": 70
|
273 |
+
},
|
274 |
+
{
|
275 |
+
"data": {
|
276 |
+
"form": {
|
277 |
+
"llm_id": "deepseek-chat",
|
278 |
+
"prompt": "You are an intelligent assistant. Please answer the question based on content of knowledge base. When all knowledge base content is irrelevant to the question, your answer must include the sentence \"The answer you are looking for is not found in the knowledge base!\". Answers need to consider chat history.\n Knowledge base content is as following:\n {input}\n The above is the content of knowledge base.",
|
279 |
+
"temperature": 0.02
|
280 |
+
},
|
281 |
+
"label": "Generate",
|
282 |
+
"name": "LLM"
|
283 |
+
},
|
284 |
+
"dragging": false,
|
285 |
+
"height": 150,
|
286 |
+
"id": "generate:0",
|
287 |
+
"position": {
|
288 |
+
"x": -90.09669656497177,
|
289 |
+
"y": 192.12280240375043
|
290 |
+
},
|
291 |
+
"positionAbsolute": {
|
292 |
+
"x": -90.09669656497177,
|
293 |
+
"y": 192.12280240375043
|
294 |
+
},
|
295 |
+
"selected": false,
|
296 |
+
"sourcePosition": "left",
|
297 |
+
"targetPosition": "right",
|
298 |
+
"type": "logicNode",
|
299 |
+
"width": 150
|
300 |
+
},
|
301 |
+
{
|
302 |
+
"data": {
|
303 |
+
"form": {
|
304 |
+
"llm_id": "deepseek-chat",
|
305 |
+
"temperature": 0.8
|
306 |
+
},
|
307 |
+
"label": "RewriteQuestion",
|
308 |
+
"name": "Refine Ques"
|
309 |
+
},
|
310 |
+
"dragging": false,
|
311 |
+
"height": 70,
|
312 |
+
"id": "rewrite:0",
|
313 |
+
"position": {
|
314 |
+
"x": 416.0628662660416,
|
315 |
+
"y": 144.09722952739514
|
316 |
+
},
|
317 |
+
"positionAbsolute": {
|
318 |
+
"x": 416.0628662660416,
|
319 |
+
"y": 144.09722952739514
|
320 |
+
},
|
321 |
+
"selected": false,
|
322 |
+
"sourcePosition": "left",
|
323 |
+
"targetPosition": "right",
|
324 |
+
"type": "logicNode",
|
325 |
+
"width": 70
|
326 |
+
}
|
327 |
+
]
|
328 |
+
},
|
329 |
+
"history": [],
|
330 |
+
"messages": [],
|
331 |
+
"path": [],
|
332 |
+
"reference": []
|
333 |
+
},
|
334 |
+
"avatar": "data:image/jpeg;base64,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"
|
335 |
+
}
|
agent/templates/interpreter.json
ADDED
@@ -0,0 +1,158 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"id": 4,
|
3 |
+
"title": "Interpreter",
|
4 |
+
"description": "An interpreter. Type the content you want to translate and the object language like: Hi there => Spanish. Hava a try!",
|
5 |
+
"canvas_type": "chatbot",
|
6 |
+
"dsl": {
|
7 |
+
"answer": [],
|
8 |
+
"components": {
|
9 |
+
"answer:0": {
|
10 |
+
"downstream": ["generate:0"],
|
11 |
+
"obj": {
|
12 |
+
"component_name": "Answer",
|
13 |
+
"params": {}
|
14 |
+
},
|
15 |
+
"upstream": ["begin", "generate:0"]
|
16 |
+
},
|
17 |
+
"begin": {
|
18 |
+
"downstream": ["answer:0"],
|
19 |
+
"obj": {
|
20 |
+
"component_name": "Begin",
|
21 |
+
"params": {
|
22 |
+
"prologue": "Hi there! Please enter the text you want to translate in format like: 'text you want to translate' => target language. For an example: 您好! => English"
|
23 |
+
}
|
24 |
+
},
|
25 |
+
"upstream": []
|
26 |
+
},
|
27 |
+
"generate:0": {
|
28 |
+
"downstream": ["answer:0"],
|
29 |
+
"obj": {
|
30 |
+
"component_name": "Generate",
|
31 |
+
"params": {
|
32 |
+
"llm_id": "deepseek-chat",
|
33 |
+
"prompt": "You are an professional interpreter.\n- Role: an professional interpreter.\n- Input format: content need to be translated => target language. \n- Answer format: => translated content in target language. \n- Examples:\n - user: 您好! => English. assistant: => How are you doing!\n - user: You look good today. => Japanese. assistant: => 今日は調子がいいですね 。\n"
|
34 |
+
}
|
35 |
+
},
|
36 |
+
"upstream": ["answer:0"]
|
37 |
+
}
|
38 |
+
},
|
39 |
+
"graph": {
|
40 |
+
"edges": [
|
41 |
+
{
|
42 |
+
"id": "c87c7805-8cf0-4cd4-b45b-152031811020",
|
43 |
+
"label": "",
|
44 |
+
"source": "begin",
|
45 |
+
"target": "answer:0"
|
46 |
+
},
|
47 |
+
{
|
48 |
+
"id": "reactflow__edge-answer:0b-generate:0d",
|
49 |
+
"markerEnd": "logo",
|
50 |
+
"source": "answer:0",
|
51 |
+
"sourceHandle": "b",
|
52 |
+
"style": {
|
53 |
+
"stroke": "rgb(202 197 245)",
|
54 |
+
"strokeWidth": 2
|
55 |
+
},
|
56 |
+
"target": "generate:0",
|
57 |
+
"targetHandle": "d",
|
58 |
+
"type": "buttonEdge"
|
59 |
+
},
|
60 |
+
{
|
61 |
+
"id": "reactflow__edge-generate:0c-answer:0a",
|
62 |
+
"markerEnd": "logo",
|
63 |
+
"source": "generate:0",
|
64 |
+
"sourceHandle": "c",
|
65 |
+
"style": {
|
66 |
+
"stroke": "rgb(202 197 245)",
|
67 |
+
"strokeWidth": 2
|
68 |
+
},
|
69 |
+
"target": "answer:0",
|
70 |
+
"targetHandle": "a",
|
71 |
+
"type": "buttonEdge"
|
72 |
+
}
|
73 |
+
],
|
74 |
+
"nodes": [
|
75 |
+
{
|
76 |
+
"data": {
|
77 |
+
"form": {
|
78 |
+
"prologue": "Hi there! Please enter the text you want to translate in format like: 'text you want to translate' => target language. For an example: 您好! => English"
|
79 |
+
},
|
80 |
+
"label": "Begin",
|
81 |
+
"name": "Instruction"
|
82 |
+
},
|
83 |
+
"dragging": false,
|
84 |
+
"height": 50,
|
85 |
+
"id": "begin",
|
86 |
+
"position": {
|
87 |
+
"x": -175.31950791077287,
|
88 |
+
"y": 32.340246044613565
|
89 |
+
},
|
90 |
+
"positionAbsolute": {
|
91 |
+
"x": -175.31950791077287,
|
92 |
+
"y": 32.340246044613565
|
93 |
+
},
|
94 |
+
"selected": true,
|
95 |
+
"sourcePosition": "left",
|
96 |
+
"targetPosition": "right",
|
97 |
+
"type": "beginNode",
|
98 |
+
"width": 50
|
99 |
+
},
|
100 |
+
{
|
101 |
+
"data": {
|
102 |
+
"form": {},
|
103 |
+
"label": "Answer",
|
104 |
+
"name": "Interface"
|
105 |
+
},
|
106 |
+
"dragging": false,
|
107 |
+
"height": 100,
|
108 |
+
"id": "answer:0",
|
109 |
+
"position": {
|
110 |
+
"x": 0,
|
111 |
+
"y": 6
|
112 |
+
},
|
113 |
+
"positionAbsolute": {
|
114 |
+
"x": 0,
|
115 |
+
"y": 6
|
116 |
+
},
|
117 |
+
"selected": false,
|
118 |
+
"sourcePosition": "left",
|
119 |
+
"targetPosition": "right",
|
120 |
+
"type": "logicNode",
|
121 |
+
"width": 100
|
122 |
+
},
|
123 |
+
{
|
124 |
+
"data": {
|
125 |
+
"form": {
|
126 |
+
"llm_id": "deepseek-chat",
|
127 |
+
"prompt": "You are an professional interpreter.\n- Role: an professional interpreter.\n- Input format: content need to be translated => target language. \n- Answer format: => translated content in target language. \n- Examples:\n - user: 您好! => English. assistant: => How are you doing!\n - user: You look good today. => Japanese. assistant: => 今日は調子がいいですね 。\n",
|
128 |
+
"temperature": 0.5
|
129 |
+
},
|
130 |
+
"label": "Generate",
|
131 |
+
"name": "Translate"
|
132 |
+
},
|
133 |
+
"dragging": false,
|
134 |
+
"height": 150,
|
135 |
+
"id": "generate:0",
|
136 |
+
"position": {
|
137 |
+
"x": 214.89015821545786,
|
138 |
+
"y": 135.10439391733706
|
139 |
+
},
|
140 |
+
"positionAbsolute": {
|
141 |
+
"x": 214.89015821545786,
|
142 |
+
"y": 135.10439391733706
|
143 |
+
},
|
144 |
+
"selected": false,
|
145 |
+
"sourcePosition": "left",
|
146 |
+
"targetPosition": "right",
|
147 |
+
"type": "logicNode",
|
148 |
+
"width": 150
|
149 |
+
}
|
150 |
+
]
|
151 |
+
},
|
152 |
+
"history": [],
|
153 |
+
"messages": [],
|
154 |
+
"path": [],
|
155 |
+
"reference": []
|
156 |
+
},
|
157 |
+
"avatar": "data:image/jpeg;base64,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"
|
158 |
+
}
|
agent/templates/websearch_assistant.json
ADDED
@@ -0,0 +1,547 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"id": 0,
|
3 |
+
"title": "WebSearch Assistant",
|
4 |
+
"description": "A chat assistant that combines information both from knowledge base and web search engines. It integrates information from the knowledge base and relevant search engines to answer a given question. What you need to do is setting up knowleage base in 'Retrieval'.",
|
5 |
+
"canvas_type": "chatbot",
|
6 |
+
"dsl": {
|
7 |
+
"answer": [],
|
8 |
+
"components": {
|
9 |
+
"Answer:PoorMapsCover": {
|
10 |
+
"downstream": [
|
11 |
+
"Retrieval:BetterRocksJump",
|
12 |
+
"KeywordExtract:LegalIdeasTurn"
|
13 |
+
],
|
14 |
+
"obj": {
|
15 |
+
"component_name": "Answer",
|
16 |
+
"params": {}
|
17 |
+
},
|
18 |
+
"upstream": ["Generate:FullYearsStick", "begin"]
|
19 |
+
},
|
20 |
+
"Baidu:OliveAreasCall": {
|
21 |
+
"downstream": ["Generate:FullYearsStick"],
|
22 |
+
"obj": {
|
23 |
+
"component_name": "Baidu",
|
24 |
+
"params": {
|
25 |
+
"top_n": 2
|
26 |
+
}
|
27 |
+
},
|
28 |
+
"upstream": ["KeywordExtract:LegalIdeasTurn"]
|
29 |
+
},
|
30 |
+
"DuckDuckGo:SoftButtonsRefuse": {
|
31 |
+
"downstream": ["Generate:FullYearsStick"],
|
32 |
+
"obj": {
|
33 |
+
"component_name": "DuckDuckGo",
|
34 |
+
"params": {
|
35 |
+
"channel": "text",
|
36 |
+
"top_n": 2
|
37 |
+
}
|
38 |
+
},
|
39 |
+
"upstream": ["KeywordExtract:LegalIdeasTurn"]
|
40 |
+
},
|
41 |
+
"Generate:FullYearsStick": {
|
42 |
+
"downstream": ["Answer:PoorMapsCover"],
|
43 |
+
"obj": {
|
44 |
+
"component_name": "Generate",
|
45 |
+
"params": {
|
46 |
+
"cite": true,
|
47 |
+
"frequency_penalty": 0.7,
|
48 |
+
"llm_id": "deepseek-chat",
|
49 |
+
"message_history_window_size": 12,
|
50 |
+
"parameters": [
|
51 |
+
{
|
52 |
+
"component_id": "Retrieval:BetterRocksJump",
|
53 |
+
"id": "69415446-49bf-4d4b-8ec9-ac86066f7709",
|
54 |
+
"key": "kb_input"
|
55 |
+
},
|
56 |
+
{
|
57 |
+
"component_id": "DuckDuckGo:SoftButtonsRefuse",
|
58 |
+
"id": "83363c2a-00a8-402f-a45c-ddc4097d7d8b",
|
59 |
+
"key": "duckduckgo"
|
60 |
+
},
|
61 |
+
{
|
62 |
+
"component_id": "Wikipedia:WittyRiceLearn",
|
63 |
+
"id": "92c1e8e4-1597-4e65-a08d-c8cac4ac150f",
|
64 |
+
"key": "wikipedia"
|
65 |
+
},
|
66 |
+
{
|
67 |
+
"component_id": "Baidu:OliveAreasCall",
|
68 |
+
"id": "19b5445a-7a6e-4a26-9aa9-47dfe3a03bea",
|
69 |
+
"key": "baidu"
|
70 |
+
}
|
71 |
+
],
|
72 |
+
"presence_penalty": 0.4,
|
73 |
+
"prompt": "Role: You are an intelligent assistant. \nTask: Chat with user. Answer the question based on the provided content from: Knowledge Base, Wikipedia, Duckduckgo, Baidu.\nRequirements:\n - Answer should be in markdown format.\n - Summarize and label the sources of the cited content separately: (Knowledge Base, Wikipedia, Duckduckgo, Baidu).\n - Attach URL links to the content which is quoted from Wikipedia, DuckDuckGo or Baidu.\n - Do not make thing up when there's no relevant information to user's question. \n\n## Knowledge base content\n {kb_input}\n\n\n## Wikipedia content\n{wikipedia}\n\n\n## Duckduckgo content\n{duckduckgo}\n\n\n## Baidu content\n{baidu}",
|
74 |
+
"temperature": 0.1,
|
75 |
+
"top_p": 0.3
|
76 |
+
}
|
77 |
+
},
|
78 |
+
"upstream": [
|
79 |
+
"DuckDuckGo:SoftButtonsRefuse",
|
80 |
+
"Baidu:OliveAreasCall",
|
81 |
+
"Wikipedia:WittyRiceLearn",
|
82 |
+
"Retrieval:BetterRocksJump"
|
83 |
+
]
|
84 |
+
},
|
85 |
+
"KeywordExtract:LegalIdeasTurn": {
|
86 |
+
"downstream": [
|
87 |
+
"Baidu:OliveAreasCall",
|
88 |
+
"DuckDuckGo:SoftButtonsRefuse",
|
89 |
+
"Wikipedia:WittyRiceLearn"
|
90 |
+
],
|
91 |
+
"obj": {
|
92 |
+
"component_name": "KeywordExtract",
|
93 |
+
"params": {
|
94 |
+
"frequencyPenaltyEnabled": true,
|
95 |
+
"frequency_penalty": 0.7,
|
96 |
+
"llm_id": "deepseek-chat",
|
97 |
+
"maxTokensEnabled": true,
|
98 |
+
"max_tokens": 256,
|
99 |
+
"parameter": "Precise",
|
100 |
+
"presencePenaltyEnabled": true,
|
101 |
+
"presence_penalty": 0.4,
|
102 |
+
"temperature": 0.1,
|
103 |
+
"temperatureEnabled": true,
|
104 |
+
"topPEnabled": true,
|
105 |
+
"top_n": 2,
|
106 |
+
"top_p": 0.3
|
107 |
+
}
|
108 |
+
},
|
109 |
+
"upstream": ["Answer:PoorMapsCover"]
|
110 |
+
},
|
111 |
+
"Retrieval:BetterRocksJump": {
|
112 |
+
"downstream": ["Generate:FullYearsStick"],
|
113 |
+
"obj": {
|
114 |
+
"component_name": "Retrieval",
|
115 |
+
"params": {
|
116 |
+
"empty_response": "The answer you want was not found in the knowledge base!",
|
117 |
+
"kb_ids": [],
|
118 |
+
"keywords_similarity_weight": 0.3,
|
119 |
+
"similarity_threshold": 0.2,
|
120 |
+
"top_n": 8
|
121 |
+
}
|
122 |
+
},
|
123 |
+
"upstream": ["Answer:PoorMapsCover"]
|
124 |
+
},
|
125 |
+
"Wikipedia:WittyRiceLearn": {
|
126 |
+
"downstream": ["Generate:FullYearsStick"],
|
127 |
+
"obj": {
|
128 |
+
"component_name": "Wikipedia",
|
129 |
+
"params": {
|
130 |
+
"language": "en",
|
131 |
+
"top_n": 2
|
132 |
+
}
|
133 |
+
},
|
134 |
+
"upstream": ["KeywordExtract:LegalIdeasTurn"]
|
135 |
+
},
|
136 |
+
"begin": {
|
137 |
+
"downstream": ["Answer:PoorMapsCover"],
|
138 |
+
"obj": {
|
139 |
+
"component_name": "Begin",
|
140 |
+
"params": {}
|
141 |
+
},
|
142 |
+
"upstream": []
|
143 |
+
}
|
144 |
+
},
|
145 |
+
"graph": {
|
146 |
+
"edges": [
|
147 |
+
{
|
148 |
+
"id": "reactflow__edge-Answer:PoorMapsCovera-Retrieval:BetterRocksJumpc",
|
149 |
+
"markerEnd": "logo",
|
150 |
+
"source": "Answer:PoorMapsCover",
|
151 |
+
"sourceHandle": "a",
|
152 |
+
"style": {
|
153 |
+
"stroke": "rgb(202 197 245)",
|
154 |
+
"strokeWidth": 2
|
155 |
+
},
|
156 |
+
"target": "Retrieval:BetterRocksJump",
|
157 |
+
"targetHandle": "c",
|
158 |
+
"type": "buttonEdge"
|
159 |
+
},
|
160 |
+
{
|
161 |
+
"id": "reactflow__edge-Answer:PoorMapsCoverb-KeywordExtract:LegalIdeasTurnc",
|
162 |
+
"markerEnd": "logo",
|
163 |
+
"source": "Answer:PoorMapsCover",
|
164 |
+
"sourceHandle": "b",
|
165 |
+
"style": {
|
166 |
+
"stroke": "rgb(202 197 245)",
|
167 |
+
"strokeWidth": 2
|
168 |
+
},
|
169 |
+
"target": "KeywordExtract:LegalIdeasTurn",
|
170 |
+
"targetHandle": "c",
|
171 |
+
"type": "buttonEdge"
|
172 |
+
},
|
173 |
+
{
|
174 |
+
"id": "reactflow__edge-KeywordExtract:LegalIdeasTurnb-Baidu:OliveAreasCallc",
|
175 |
+
"markerEnd": "logo",
|
176 |
+
"source": "KeywordExtract:LegalIdeasTurn",
|
177 |
+
"sourceHandle": "b",
|
178 |
+
"style": {
|
179 |
+
"stroke": "rgb(202 197 245)",
|
180 |
+
"strokeWidth": 2
|
181 |
+
},
|
182 |
+
"target": "Baidu:OliveAreasCall",
|
183 |
+
"targetHandle": "c",
|
184 |
+
"type": "buttonEdge"
|
185 |
+
},
|
186 |
+
{
|
187 |
+
"id": "reactflow__edge-KeywordExtract:LegalIdeasTurnb-DuckDuckGo:SoftButtonsRefusec",
|
188 |
+
"markerEnd": "logo",
|
189 |
+
"source": "KeywordExtract:LegalIdeasTurn",
|
190 |
+
"sourceHandle": "b",
|
191 |
+
"style": {
|
192 |
+
"stroke": "rgb(202 197 245)",
|
193 |
+
"strokeWidth": 2
|
194 |
+
},
|
195 |
+
"target": "DuckDuckGo:SoftButtonsRefuse",
|
196 |
+
"targetHandle": "c",
|
197 |
+
"type": "buttonEdge"
|
198 |
+
},
|
199 |
+
{
|
200 |
+
"id": "reactflow__edge-KeywordExtract:LegalIdeasTurnb-Wikipedia:WittyRiceLearnc",
|
201 |
+
"markerEnd": "logo",
|
202 |
+
"source": "KeywordExtract:LegalIdeasTurn",
|
203 |
+
"sourceHandle": "b",
|
204 |
+
"style": {
|
205 |
+
"stroke": "rgb(202 197 245)",
|
206 |
+
"strokeWidth": 2
|
207 |
+
},
|
208 |
+
"target": "Wikipedia:WittyRiceLearn",
|
209 |
+
"targetHandle": "c",
|
210 |
+
"type": "buttonEdge"
|
211 |
+
},
|
212 |
+
{
|
213 |
+
"id": "reactflow__edge-DuckDuckGo:SoftButtonsRefuseb-Generate:FullYearsSticka",
|
214 |
+
"markerEnd": "logo",
|
215 |
+
"source": "DuckDuckGo:SoftButtonsRefuse",
|
216 |
+
"sourceHandle": "b",
|
217 |
+
"style": {
|
218 |
+
"stroke": "rgb(202 197 245)",
|
219 |
+
"strokeWidth": 2
|
220 |
+
},
|
221 |
+
"target": "Generate:FullYearsStick",
|
222 |
+
"targetHandle": "a",
|
223 |
+
"type": "buttonEdge"
|
224 |
+
},
|
225 |
+
{
|
226 |
+
"id": "reactflow__edge-Baidu:OliveAreasCallb-Generate:FullYearsSticka",
|
227 |
+
"markerEnd": "logo",
|
228 |
+
"source": "Baidu:OliveAreasCall",
|
229 |
+
"sourceHandle": "b",
|
230 |
+
"style": {
|
231 |
+
"stroke": "rgb(202 197 245)",
|
232 |
+
"strokeWidth": 2
|
233 |
+
},
|
234 |
+
"target": "Generate:FullYearsStick",
|
235 |
+
"targetHandle": "a",
|
236 |
+
"type": "buttonEdge"
|
237 |
+
},
|
238 |
+
{
|
239 |
+
"id": "reactflow__edge-Wikipedia:WittyRiceLearnb-Generate:FullYearsSticka",
|
240 |
+
"markerEnd": "logo",
|
241 |
+
"source": "Wikipedia:WittyRiceLearn",
|
242 |
+
"sourceHandle": "b",
|
243 |
+
"style": {
|
244 |
+
"stroke": "rgb(202 197 245)",
|
245 |
+
"strokeWidth": 2
|
246 |
+
},
|
247 |
+
"target": "Generate:FullYearsStick",
|
248 |
+
"targetHandle": "a",
|
249 |
+
"type": "buttonEdge"
|
250 |
+
},
|
251 |
+
{
|
252 |
+
"id": "reactflow__edge-Retrieval:BetterRocksJumpb-Generate:FullYearsSticka",
|
253 |
+
"markerEnd": "logo",
|
254 |
+
"source": "Retrieval:BetterRocksJump",
|
255 |
+
"sourceHandle": "b",
|
256 |
+
"style": {
|
257 |
+
"stroke": "rgb(202 197 245)",
|
258 |
+
"strokeWidth": 2
|
259 |
+
},
|
260 |
+
"target": "Generate:FullYearsStick",
|
261 |
+
"targetHandle": "a",
|
262 |
+
"type": "buttonEdge"
|
263 |
+
},
|
264 |
+
{
|
265 |
+
"id": "reactflow__edge-Generate:FullYearsStickd-Answer:PoorMapsCoverd",
|
266 |
+
"markerEnd": "logo",
|
267 |
+
"source": "Generate:FullYearsStick",
|
268 |
+
"sourceHandle": "d",
|
269 |
+
"style": {
|
270 |
+
"stroke": "rgb(202 197 245)",
|
271 |
+
"strokeWidth": 2
|
272 |
+
},
|
273 |
+
"target": "Answer:PoorMapsCover",
|
274 |
+
"targetHandle": "d",
|
275 |
+
"type": "buttonEdge"
|
276 |
+
},
|
277 |
+
{
|
278 |
+
"id": "reactflow__edge-begin-Answer:PoorMapsCoverc",
|
279 |
+
"markerEnd": "logo",
|
280 |
+
"source": "begin",
|
281 |
+
"sourceHandle": null,
|
282 |
+
"style": {
|
283 |
+
"stroke": "rgb(202 197 245)",
|
284 |
+
"strokeWidth": 2
|
285 |
+
},
|
286 |
+
"target": "Answer:PoorMapsCover",
|
287 |
+
"targetHandle": "c",
|
288 |
+
"type": "buttonEdge"
|
289 |
+
}
|
290 |
+
],
|
291 |
+
"nodes": [
|
292 |
+
{
|
293 |
+
"data": {
|
294 |
+
"label": "Begin",
|
295 |
+
"name": "opening"
|
296 |
+
},
|
297 |
+
"dragging": false,
|
298 |
+
"height": 50,
|
299 |
+
"id": "begin",
|
300 |
+
"position": {
|
301 |
+
"x": -1020.0423250754997,
|
302 |
+
"y": 54.07040832453751
|
303 |
+
},
|
304 |
+
"positionAbsolute": {
|
305 |
+
"x": -1020.0423250754997,
|
306 |
+
"y": 54.07040832453751
|
307 |
+
},
|
308 |
+
"selected": false,
|
309 |
+
"sourcePosition": "left",
|
310 |
+
"targetPosition": "right",
|
311 |
+
"type": "beginNode",
|
312 |
+
"width": 50
|
313 |
+
},
|
314 |
+
{
|
315 |
+
"data": {
|
316 |
+
"form": {},
|
317 |
+
"label": "Answer",
|
318 |
+
"name": "interface"
|
319 |
+
},
|
320 |
+
"dragging": false,
|
321 |
+
"height": 100,
|
322 |
+
"id": "Answer:PoorMapsCover",
|
323 |
+
"position": {
|
324 |
+
"x": -880.5773333116513,
|
325 |
+
"y": 29.2721628695582
|
326 |
+
},
|
327 |
+
"positionAbsolute": {
|
328 |
+
"x": -880.5773333116513,
|
329 |
+
"y": 29.2721628695582
|
330 |
+
},
|
331 |
+
"selected": false,
|
332 |
+
"sourcePosition": "right",
|
333 |
+
"targetPosition": "left",
|
334 |
+
"type": "logicNode",
|
335 |
+
"width": 100
|
336 |
+
},
|
337 |
+
{
|
338 |
+
"data": {
|
339 |
+
"form": {
|
340 |
+
"frequencyPenaltyEnabled": true,
|
341 |
+
"frequency_penalty": 0.7,
|
342 |
+
"llm_id": "deepseek-chat",
|
343 |
+
"maxTokensEnabled": true,
|
344 |
+
"max_tokens": 256,
|
345 |
+
"parameter": "Precise",
|
346 |
+
"presencePenaltyEnabled": true,
|
347 |
+
"presence_penalty": 0.4,
|
348 |
+
"temperature": 0.1,
|
349 |
+
"temperatureEnabled": true,
|
350 |
+
"topPEnabled": true,
|
351 |
+
"top_n": 2,
|
352 |
+
"top_p": 0.3
|
353 |
+
},
|
354 |
+
"label": "KeywordExtract",
|
355 |
+
"name": "get keywords"
|
356 |
+
},
|
357 |
+
"dragging": false,
|
358 |
+
"height": 70,
|
359 |
+
"id": "KeywordExtract:LegalIdeasTurn",
|
360 |
+
"position": {
|
361 |
+
"x": -727.0680233991866,
|
362 |
+
"y": 43.6827878582167
|
363 |
+
},
|
364 |
+
"positionAbsolute": {
|
365 |
+
"x": -727.0680233991866,
|
366 |
+
"y": 43.6827878582167
|
367 |
+
},
|
368 |
+
"selected": false,
|
369 |
+
"sourcePosition": "right",
|
370 |
+
"targetPosition": "left",
|
371 |
+
"type": "logicNode",
|
372 |
+
"width": 70
|
373 |
+
},
|
374 |
+
{
|
375 |
+
"data": {
|
376 |
+
"form": {
|
377 |
+
"empty_response": "The answer you want was not found in the knowledge base!",
|
378 |
+
"kb_ids": [],
|
379 |
+
"keywords_similarity_weight": 0.3,
|
380 |
+
"similarity_threshold": 0.2,
|
381 |
+
"top_n": 8
|
382 |
+
},
|
383 |
+
"label": "Retrieval",
|
384 |
+
"name": "Search KB"
|
385 |
+
},
|
386 |
+
"dragging": false,
|
387 |
+
"height": 100,
|
388 |
+
"id": "Retrieval:BetterRocksJump",
|
389 |
+
"position": {
|
390 |
+
"x": -453.6381242126441,
|
391 |
+
"y": 245.01328822547293
|
392 |
+
},
|
393 |
+
"positionAbsolute": {
|
394 |
+
"x": -453.6381242126441,
|
395 |
+
"y": 245.01328822547293
|
396 |
+
},
|
397 |
+
"selected": false,
|
398 |
+
"sourcePosition": "right",
|
399 |
+
"targetPosition": "left",
|
400 |
+
"type": "logicNode",
|
401 |
+
"width": 100
|
402 |
+
},
|
403 |
+
{
|
404 |
+
"data": {
|
405 |
+
"form": {
|
406 |
+
"language": "en",
|
407 |
+
"top_n": 2
|
408 |
+
},
|
409 |
+
"label": "Wikipedia",
|
410 |
+
"name": "Wikipedia"
|
411 |
+
},
|
412 |
+
"dragging": false,
|
413 |
+
"height": 100,
|
414 |
+
"id": "Wikipedia:WittyRiceLearn",
|
415 |
+
"position": {
|
416 |
+
"x": -552.2594439551717,
|
417 |
+
"y": 155.22722562174718
|
418 |
+
},
|
419 |
+
"positionAbsolute": {
|
420 |
+
"x": -552.2594439551717,
|
421 |
+
"y": 155.22722562174718
|
422 |
+
},
|
423 |
+
"selected": false,
|
424 |
+
"sourcePosition": "right",
|
425 |
+
"targetPosition": "left",
|
426 |
+
"type": "ragNode",
|
427 |
+
"width": 100
|
428 |
+
},
|
429 |
+
{
|
430 |
+
"data": {
|
431 |
+
"form": {
|
432 |
+
"top_n": 2
|
433 |
+
},
|
434 |
+
"label": "Baidu",
|
435 |
+
"name": "Baidu"
|
436 |
+
},
|
437 |
+
"dragging": false,
|
438 |
+
"height": 100,
|
439 |
+
"id": "Baidu:OliveAreasCall",
|
440 |
+
"position": {
|
441 |
+
"x": -555.1646448972449,
|
442 |
+
"y": 22.458226784453046
|
443 |
+
},
|
444 |
+
"positionAbsolute": {
|
445 |
+
"x": -555.1646448972449,
|
446 |
+
"y": 22.458226784453046
|
447 |
+
},
|
448 |
+
"selected": false,
|
449 |
+
"sourcePosition": "right",
|
450 |
+
"targetPosition": "left",
|
451 |
+
"type": "ragNode",
|
452 |
+
"width": 100
|
453 |
+
},
|
454 |
+
{
|
455 |
+
"data": {
|
456 |
+
"form": {
|
457 |
+
"channel": "text",
|
458 |
+
"top_n": 2
|
459 |
+
},
|
460 |
+
"label": "DuckDuckGo",
|
461 |
+
"name": "DuckDuckGo"
|
462 |
+
},
|
463 |
+
"dragging": false,
|
464 |
+
"height": 100,
|
465 |
+
"id": "DuckDuckGo:SoftButtonsRefuse",
|
466 |
+
"position": {
|
467 |
+
"x": -554.7669080287701,
|
468 |
+
"y": -111.86266788597959
|
469 |
+
},
|
470 |
+
"positionAbsolute": {
|
471 |
+
"x": -554.7669080287701,
|
472 |
+
"y": -111.86266788597959
|
473 |
+
},
|
474 |
+
"selected": false,
|
475 |
+
"sourcePosition": "right",
|
476 |
+
"targetPosition": "left",
|
477 |
+
"type": "ragNode",
|
478 |
+
"width": 100
|
479 |
+
},
|
480 |
+
{
|
481 |
+
"data": {
|
482 |
+
"form": {
|
483 |
+
"cite": true,
|
484 |
+
"frequencyPenaltyEnabled": true,
|
485 |
+
"frequency_penalty": 0.7,
|
486 |
+
"llm_id": "deepseek-chat",
|
487 |
+
"message_history_window_size": 12,
|
488 |
+
"parameter": "Precise",
|
489 |
+
"parameters": [
|
490 |
+
{
|
491 |
+
"component_id": "Retrieval:BetterRocksJump",
|
492 |
+
"id": "69415446-49bf-4d4b-8ec9-ac86066f7709",
|
493 |
+
"key": "kb_input"
|
494 |
+
},
|
495 |
+
{
|
496 |
+
"component_id": "DuckDuckGo:SoftButtonsRefuse",
|
497 |
+
"id": "83363c2a-00a8-402f-a45c-ddc4097d7d8b",
|
498 |
+
"key": "duckduckgo"
|
499 |
+
},
|
500 |
+
{
|
501 |
+
"component_id": "Wikipedia:WittyRiceLearn",
|
502 |
+
"id": "92c1e8e4-1597-4e65-a08d-c8cac4ac150f",
|
503 |
+
"key": "wikipedia"
|
504 |
+
},
|
505 |
+
{
|
506 |
+
"component_id": "Baidu:OliveAreasCall",
|
507 |
+
"id": "19b5445a-7a6e-4a26-9aa9-47dfe3a03bea",
|
508 |
+
"key": "baidu"
|
509 |
+
}
|
510 |
+
],
|
511 |
+
"presencePenaltyEnabled": true,
|
512 |
+
"presence_penalty": 0.4,
|
513 |
+
"prompt": "Role: You are an intelligent assistant. \nTask: Chat with user. Answer the question based on the provided content from: Knowledge Base, Wikipedia, Duckduckgo, Baidu.\nRequirements:\n - Answer should be in markdown format.\n - Answer should include all sources(Knowledge Base, Wikipedia, Duckduckgo, Baidu) as long as they are relevant, and label the sources of the cited content separately.\n - Attach URL links to the content which is quoted from Wikipedia, DuckDuckGo or Baidu.\n - Do not make thing up when there's no relevant information to user's question. \n\n## Knowledge base content\n {kb_input}\n\n\n## Wikipedia content\n{wikipedia}\n\n\n## Duckduckgo content\n{duckduckgo}\n\n\n## Baidu content\n{baidu}",
|
514 |
+
"temperature": 0.1,
|
515 |
+
"temperatureEnabled": true,
|
516 |
+
"topPEnabled": true,
|
517 |
+
"top_p": 0.3
|
518 |
+
},
|
519 |
+
"label": "Generate",
|
520 |
+
"name": "LLM"
|
521 |
+
},
|
522 |
+
"dragging": false,
|
523 |
+
"height": 150,
|
524 |
+
"id": "Generate:FullYearsStick",
|
525 |
+
"position": {
|
526 |
+
"x": -355.85244068796055,
|
527 |
+
"y": -225.5280777950136
|
528 |
+
},
|
529 |
+
"positionAbsolute": {
|
530 |
+
"x": -355.85244068796055,
|
531 |
+
"y": -225.5280777950136
|
532 |
+
},
|
533 |
+
"selected": true,
|
534 |
+
"sourcePosition": "right",
|
535 |
+
"targetPosition": "left",
|
536 |
+
"type": "logicNode",
|
537 |
+
"width": 150
|
538 |
+
}
|
539 |
+
]
|
540 |
+
},
|
541 |
+
"history": [],
|
542 |
+
"messages": [],
|
543 |
+
"path": [],
|
544 |
+
"reference": []
|
545 |
+
},
|
546 |
+
"avatar": "data:image/png;base64,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"
|
547 |
+
}
|
agent/test/client.py
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#
|
2 |
+
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
3 |
+
#
|
4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5 |
+
# you may not use this file except in compliance with the License.
|
6 |
+
# You may obtain a copy of the License at
|
7 |
+
#
|
8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9 |
+
#
|
10 |
+
# Unless required by applicable law or agreed to in writing, software
|
11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 |
+
# See the License for the specific language governing permissions and
|
14 |
+
# limitations under the License.
|
15 |
+
#
|
16 |
+
import argparse
|
17 |
+
import os
|
18 |
+
from functools import partial
|
19 |
+
from agent.canvas import Canvas
|
20 |
+
from agent.settings import DEBUG
|
21 |
+
|
22 |
+
if __name__ == '__main__':
|
23 |
+
parser = argparse.ArgumentParser()
|
24 |
+
dsl_default_path = os.path.join(
|
25 |
+
os.path.dirname(os.path.realpath(__file__)),
|
26 |
+
"dsl_examples",
|
27 |
+
"retrieval_and_generate.json",
|
28 |
+
)
|
29 |
+
parser.add_argument('-s', '--dsl', default=dsl_default_path, help="input dsl", action='store', required=True)
|
30 |
+
parser.add_argument('-t', '--tenant_id', default=False, help="Tenant ID", action='store', required=True)
|
31 |
+
parser.add_argument('-m', '--stream', default=False, help="Stream output", action='store_true', required=False)
|
32 |
+
args = parser.parse_args()
|
33 |
+
|
34 |
+
canvas = Canvas(open(args.dsl, "r").read(), args.tenant_id)
|
35 |
+
while True:
|
36 |
+
ans = canvas.run(stream=args.stream)
|
37 |
+
print("==================== Bot =====================\n> ", end='')
|
38 |
+
if args.stream and isinstance(ans, partial):
|
39 |
+
cont = ""
|
40 |
+
for an in ans():
|
41 |
+
print(an["content"][len(cont):], end='', flush=True)
|
42 |
+
cont = an["content"]
|
43 |
+
else:
|
44 |
+
print(ans["content"])
|
45 |
+
|
46 |
+
if DEBUG: print(canvas.path)
|
47 |
+
question = input("\n==================== User =====================\n> ")
|
48 |
+
canvas.add_user_input(question)
|
agent/test/dsl_examples/categorize.json
ADDED
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"components": {
|
3 |
+
"begin": {
|
4 |
+
"obj":{
|
5 |
+
"component_name": "Begin",
|
6 |
+
"params": {
|
7 |
+
"prologue": "Hi there!"
|
8 |
+
}
|
9 |
+
},
|
10 |
+
"downstream": ["answer:0"],
|
11 |
+
"upstream": []
|
12 |
+
},
|
13 |
+
"answer:0": {
|
14 |
+
"obj": {
|
15 |
+
"component_name": "Answer",
|
16 |
+
"params": {}
|
17 |
+
},
|
18 |
+
"downstream": ["categorize:0"],
|
19 |
+
"upstream": ["begin"]
|
20 |
+
},
|
21 |
+
"categorize:0": {
|
22 |
+
"obj": {
|
23 |
+
"component_name": "Categorize",
|
24 |
+
"params": {
|
25 |
+
"llm_id": "deepseek-chat",
|
26 |
+
"category_description": {
|
27 |
+
"product_related": {
|
28 |
+
"description": "The question is about the product usage, appearance and how it works.",
|
29 |
+
"examples": "Why it always beaming?\nHow to install it onto the wall?\nIt leaks, what to do?"
|
30 |
+
},
|
31 |
+
"others": {
|
32 |
+
"description": "The question is not about the product usage, appearance and how it works.",
|
33 |
+
"examples": "How are you doing?\nWhat is your name?\nAre you a robot?\nWhat's the weather?\nWill it rain?"
|
34 |
+
}
|
35 |
+
}
|
36 |
+
}
|
37 |
+
},
|
38 |
+
"downstream": [],
|
39 |
+
"upstream": ["answer:0"]
|
40 |
+
}
|
41 |
+
},
|
42 |
+
"history": [],
|
43 |
+
"path": [],
|
44 |
+
"answer": []
|
45 |
+
}
|
agent/test/dsl_examples/customer_service.json
ADDED
@@ -0,0 +1,157 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"components": {
|
3 |
+
"begin": {
|
4 |
+
"obj":{
|
5 |
+
"component_name": "Begin",
|
6 |
+
"params": {
|
7 |
+
"prologue": "Hi! How can I help you?"
|
8 |
+
}
|
9 |
+
},
|
10 |
+
"downstream": ["answer:0"],
|
11 |
+
"upstream": []
|
12 |
+
},
|
13 |
+
"answer:0": {
|
14 |
+
"obj": {
|
15 |
+
"component_name": "Answer",
|
16 |
+
"params": {}
|
17 |
+
},
|
18 |
+
"downstream": ["categorize:0"],
|
19 |
+
"upstream": ["begin", "generate:0", "generate:casual", "generate:answer", "generate:complain", "generate:ask_contact", "message:get_contact"]
|
20 |
+
},
|
21 |
+
"categorize:0": {
|
22 |
+
"obj": {
|
23 |
+
"component_name": "Categorize",
|
24 |
+
"params": {
|
25 |
+
"llm_id": "deepseek-chat",
|
26 |
+
"category_description": {
|
27 |
+
"product_related": {
|
28 |
+
"description": "The question is about the product usage, appearance and how it works.",
|
29 |
+
"examples": "Why it always beaming?\nHow to install it onto the wall?\nIt leaks, what to do?\nException: Can't connect to ES cluster\nHow to build the RAGFlow image from scratch",
|
30 |
+
"to": "retrieval:0"
|
31 |
+
},
|
32 |
+
"casual": {
|
33 |
+
"description": "The question is not about the product usage, appearance and how it works. Just casual chat.",
|
34 |
+
"examples": "How are you doing?\nWhat is your name?\nAre you a robot?\nWhat's the weather?\nWill it rain?",
|
35 |
+
"to": "generate:casual"
|
36 |
+
},
|
37 |
+
"complain": {
|
38 |
+
"description": "Complain even curse about the product or service you provide. But the comment is not specific enough.",
|
39 |
+
"examples": "How bad is it.\nIt's really sucks.\nDamn, for God's sake, can it be more steady?\nShit, I just can't use this shit.\nI can't stand it anymore.",
|
40 |
+
"to": "generate:complain"
|
41 |
+
},
|
42 |
+
"answer": {
|
43 |
+
"description": "This answer provide a specific contact information, like e-mail, phone number, wechat number, line number, twitter, discord, etc,.",
|
44 |
+
"examples": "My phone number is 203921\nkevinhu.hk@gmail.com\nThis is my discord number: johndowson_29384",
|
45 |
+
"to": "message:get_contact"
|
46 |
+
}
|
47 |
+
},
|
48 |
+
"message_history_window_size": 8
|
49 |
+
}
|
50 |
+
},
|
51 |
+
"downstream": ["retrieval:0", "generate:casual", "generate:complain", "message:get_contact"],
|
52 |
+
"upstream": ["answer:0"]
|
53 |
+
},
|
54 |
+
"generate:casual": {
|
55 |
+
"obj": {
|
56 |
+
"component_name": "Generate",
|
57 |
+
"params": {
|
58 |
+
"llm_id": "deepseek-chat",
|
59 |
+
"prompt": "You are a customer support. But the customer wants to have a casual chat with you instead of consulting about the product. Be nice, funny, enthusiasm and concern.",
|
60 |
+
"temperature": 0.9,
|
61 |
+
"message_history_window_size": 12,
|
62 |
+
"cite": false
|
63 |
+
}
|
64 |
+
},
|
65 |
+
"downstream": ["answer:0"],
|
66 |
+
"upstream": ["categorize:0"]
|
67 |
+
},
|
68 |
+
"generate:complain": {
|
69 |
+
"obj": {
|
70 |
+
"component_name": "Generate",
|
71 |
+
"params": {
|
72 |
+
"llm_id": "deepseek-chat",
|
73 |
+
"prompt": "You are a customer support. the Customers complain even curse about the products but not specific enough. You need to ask him/her what's the specific problem with the product. Be nice, patient and concern to soothe your customers’ emotions at first place.",
|
74 |
+
"temperature": 0.9,
|
75 |
+
"message_history_window_size": 12,
|
76 |
+
"cite": false
|
77 |
+
}
|
78 |
+
},
|
79 |
+
"downstream": ["answer:0"],
|
80 |
+
"upstream": ["categorize:0"]
|
81 |
+
},
|
82 |
+
"retrieval:0": {
|
83 |
+
"obj": {
|
84 |
+
"component_name": "Retrieval",
|
85 |
+
"params": {
|
86 |
+
"similarity_threshold": 0.2,
|
87 |
+
"keywords_similarity_weight": 0.3,
|
88 |
+
"top_n": 6,
|
89 |
+
"top_k": 1024,
|
90 |
+
"rerank_id": "BAAI/bge-reranker-v2-m3",
|
91 |
+
"kb_ids": ["869a236818b811ef91dffa163e197198"]
|
92 |
+
}
|
93 |
+
},
|
94 |
+
"downstream": ["relevant:0"],
|
95 |
+
"upstream": ["categorize:0"]
|
96 |
+
},
|
97 |
+
"relevant:0": {
|
98 |
+
"obj": {
|
99 |
+
"component_name": "Relevant",
|
100 |
+
"params": {
|
101 |
+
"llm_id": "deepseek-chat",
|
102 |
+
"temperature": 0.02,
|
103 |
+
"yes": "generate:answer",
|
104 |
+
"no": "generate:ask_contact"
|
105 |
+
}
|
106 |
+
},
|
107 |
+
"downstream": ["generate:answer", "generate:ask_contact"],
|
108 |
+
"upstream": ["retrieval:0"]
|
109 |
+
},
|
110 |
+
"generate:answer": {
|
111 |
+
"obj": {
|
112 |
+
"component_name": "Generate",
|
113 |
+
"params": {
|
114 |
+
"llm_id": "deepseek-chat",
|
115 |
+
"prompt": "You are an intelligent assistant. Please answer the question based on content of knowledge base. When all knowledge base content is irrelevant to the question, your answer must include the sentence \"The answer you are looking for is not found in the knowledge base!\". Answers need to consider chat history.\n Knowledge base content is as following:\n {input}\n The above is the content of knowledge base.",
|
116 |
+
"temperature": 0.02
|
117 |
+
}
|
118 |
+
},
|
119 |
+
"downstream": ["answer:0"],
|
120 |
+
"upstream": ["relevant:0"]
|
121 |
+
},
|
122 |
+
"generate:ask_contact": {
|
123 |
+
"obj": {
|
124 |
+
"component_name": "Generate",
|
125 |
+
"params": {
|
126 |
+
"llm_id": "deepseek-chat",
|
127 |
+
"prompt": "You are a customer support. But you can't answer to customers' question. You need to request their contact like E-mail, phone number, Wechat number, LINE number, twitter, discord, etc,. Product experts will contact them later. Please do not ask the same question twice.",
|
128 |
+
"temperature": 0.9,
|
129 |
+
"message_history_window_size": 12,
|
130 |
+
"cite": false
|
131 |
+
}
|
132 |
+
},
|
133 |
+
"downstream": ["answer:0"],
|
134 |
+
"upstream": ["relevant:0"]
|
135 |
+
},
|
136 |
+
"message:get_contact": {
|
137 |
+
"obj":{
|
138 |
+
"component_name": "Message",
|
139 |
+
"params": {
|
140 |
+
"messages": [
|
141 |
+
"Okay, I've already write this down. What else I can do for you?",
|
142 |
+
"Get it. What else I can do for you?",
|
143 |
+
"Thanks for your trust! Our expert will contact ASAP. So, anything else I can do for you?",
|
144 |
+
"Thanks! So, anything else I can do for you?"
|
145 |
+
]
|
146 |
+
}
|
147 |
+
},
|
148 |
+
"downstream": ["answer:0"],
|
149 |
+
"upstream": ["categorize:0"]
|
150 |
+
}
|
151 |
+
},
|
152 |
+
"history": [],
|
153 |
+
"messages": [],
|
154 |
+
"path": [],
|
155 |
+
"reference": [],
|
156 |
+
"answer": []
|
157 |
+
}
|
agent/test/dsl_examples/headhunter_zh.json
ADDED
@@ -0,0 +1,210 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"components": {
|
3 |
+
"begin": {
|
4 |
+
"obj": {
|
5 |
+
"component_name": "Begin",
|
6 |
+
"params": {
|
7 |
+
"prologue": "您好!我是AGI方向的猎头,了解到您是这方面的大佬,然后冒昧的就联系到您。这边有个机会想和您分享,RAGFlow正在招聘您这个岗位的资深的工程师不知道您那边是不是感兴趣?"
|
8 |
+
}
|
9 |
+
},
|
10 |
+
"downstream": ["answer:0"],
|
11 |
+
"upstream": []
|
12 |
+
},
|
13 |
+
"answer:0": {
|
14 |
+
"obj": {
|
15 |
+
"component_name": "Answer",
|
16 |
+
"params": {}
|
17 |
+
},
|
18 |
+
"downstream": ["categorize:0"],
|
19 |
+
"upstream": ["begin", "message:reject"]
|
20 |
+
},
|
21 |
+
"categorize:0": {
|
22 |
+
"obj": {
|
23 |
+
"component_name": "Categorize",
|
24 |
+
"params": {
|
25 |
+
"llm_id": "deepseek-chat",
|
26 |
+
"category_description": {
|
27 |
+
"about_job": {
|
28 |
+
"description": "该问题关于职位本身或公司的信息。",
|
29 |
+
"examples": "什么岗位?\n汇报对象是谁?\n公司多少人?\n公司有啥产品?\n具体工作内容是啥?\n地点哪里?\n双休吗?",
|
30 |
+
"to": "retrieval:0"
|
31 |
+
},
|
32 |
+
"casual": {
|
33 |
+
"description": "该问题不关于职位本身或公司的信息,属于闲聊。",
|
34 |
+
"examples": "你好\n好久不见\n你男的女的?\n你是猴子派来的救兵吗?\n上午开会了?\n你叫啥?\n最近市场如何?生意好做吗?",
|
35 |
+
"to": "generate:casual"
|
36 |
+
},
|
37 |
+
"interested": {
|
38 |
+
"description": "该回答表示他对于该职位感兴趣。",
|
39 |
+
"examples": "嗯\n说吧\n说说看\n还好吧\n是的\n哦\nyes\n具体说说",
|
40 |
+
"to": "message:introduction"
|
41 |
+
},
|
42 |
+
"answer": {
|
43 |
+
"description": "该回答表示他对于该职位不感兴趣,或感觉受到骚扰。",
|
44 |
+
"examples": "不需要\n不感兴趣\n暂时不看\n不要\nno\n我已经不干这个了\n我不是这个方向的",
|
45 |
+
"to": "message:reject"
|
46 |
+
}
|
47 |
+
}
|
48 |
+
}
|
49 |
+
},
|
50 |
+
"downstream": [
|
51 |
+
"message:introduction",
|
52 |
+
"generate:casual",
|
53 |
+
"message:reject",
|
54 |
+
"retrieval:0"
|
55 |
+
],
|
56 |
+
"upstream": ["answer:0"]
|
57 |
+
},
|
58 |
+
"message:introduction": {
|
59 |
+
"obj": {
|
60 |
+
"component_name": "Message",
|
61 |
+
"params": {
|
62 |
+
"messages": [
|
63 |
+
"我简单介绍以下:\nRAGFlow 是一款基于深度文档理解构建的开源 RAG(Retrieval-Augmented Generation)引擎。RAGFlow 可以为各种规模的企业及个人提供一套精简的 RAG 工作流程,结合大语言模型(LLM)针对用户各类不同的复杂格式数据提供可靠的问答以及有理有据的引用。https://github.com/infiniflow/ragflow\n您那边还有什么要了解的?"
|
64 |
+
]
|
65 |
+
}
|
66 |
+
},
|
67 |
+
"downstream": ["answer:1"],
|
68 |
+
"upstream": ["categorize:0"]
|
69 |
+
},
|
70 |
+
"answer:1": {
|
71 |
+
"obj": {
|
72 |
+
"component_name": "Answer",
|
73 |
+
"params": {}
|
74 |
+
},
|
75 |
+
"downstream": ["categorize:1"],
|
76 |
+
"upstream": [
|
77 |
+
"message:introduction",
|
78 |
+
"generate:aboutJob",
|
79 |
+
"generate:casual",
|
80 |
+
"generate:get_wechat",
|
81 |
+
"generate:nowechat"
|
82 |
+
]
|
83 |
+
},
|
84 |
+
"categorize:1": {
|
85 |
+
"obj": {
|
86 |
+
"component_name": "Categorize",
|
87 |
+
"params": {
|
88 |
+
"llm_id": "deepseek-chat",
|
89 |
+
"category_description": {
|
90 |
+
"about_job": {
|
91 |
+
"description": "该问题关于职位本身或公司的信息。",
|
92 |
+
"examples": "什么岗位?\n汇报对象是谁?\n公司多少人?\n公司有啥产品?\n具体工作内容是啥?\n地点哪里?\n双休吗?",
|
93 |
+
"to": "retrieval:0"
|
94 |
+
},
|
95 |
+
"casual": {
|
96 |
+
"description": "该问题不关于职位本身或公司的信息,属于闲聊。",
|
97 |
+
"examples": "你好\n好久不见\n你男的女的?\n你是猴子派来的救兵吗?\n上午开会了?\n你叫啥?\n最近市场如何?生意好做吗?",
|
98 |
+
"to": "generate:casual"
|
99 |
+
},
|
100 |
+
"wechat": {
|
101 |
+
"description": "该回答表示他愿意加微信,或者已经报了微信号。",
|
102 |
+
"examples": "嗯\n可以\n是的\n哦\nyes\n15002333453\nwindblow_2231",
|
103 |
+
"to": "generate:get_wechat"
|
104 |
+
},
|
105 |
+
"giveup": {
|
106 |
+
"description": "该回答表示他不愿意加微信。",
|
107 |
+
"examples": "不需要\n不感兴趣\n暂时不看\n不要\nno\n不方便\n不知道还要加我微信",
|
108 |
+
"to": "generate:nowechat"
|
109 |
+
}
|
110 |
+
},
|
111 |
+
"message_history_window_size": 8
|
112 |
+
}
|
113 |
+
},
|
114 |
+
"downstream": [
|
115 |
+
"retrieval:0",
|
116 |
+
"generate:casual",
|
117 |
+
"generate:get_wechat",
|
118 |
+
"generate:nowechat"
|
119 |
+
],
|
120 |
+
"upstream": ["answer:1"]
|
121 |
+
},
|
122 |
+
"generate:casual": {
|
123 |
+
"obj": {
|
124 |
+
"component_name": "Generate",
|
125 |
+
"params": {
|
126 |
+
"llm_id": "deepseek-chat",
|
127 |
+
"prompt": "你是AGI方向的猎头,现在候选人的聊了和职位无关的话题,请耐心的回应候选人,并将话题往该AGI的职位上带,最好能要到候选人微信号以便后面保持联系。",
|
128 |
+
"temperature": 0.9,
|
129 |
+
"message_history_window_size": 12,
|
130 |
+
"cite": false
|
131 |
+
}
|
132 |
+
},
|
133 |
+
"downstream": ["answer:1"],
|
134 |
+
"upstream": ["categorize:0", "categorize:1"]
|
135 |
+
},
|
136 |
+
"retrieval:0": {
|
137 |
+
"obj": {
|
138 |
+
"component_name": "Retrieval",
|
139 |
+
"params": {
|
140 |
+
"similarity_threshold": 0.2,
|
141 |
+
"keywords_similarity_weight": 0.3,
|
142 |
+
"top_n": 6,
|
143 |
+
"top_k": 1024,
|
144 |
+
"rerank_id": "BAAI/bge-reranker-v2-m3",
|
145 |
+
"kb_ids": ["869a236818b811ef91dffa163e197198"]
|
146 |
+
}
|
147 |
+
},
|
148 |
+
"downstream": ["generate:aboutJob"],
|
149 |
+
"upstream": ["categorize:0", "categorize:1"]
|
150 |
+
},
|
151 |
+
"generate:aboutJob": {
|
152 |
+
"obj": {
|
153 |
+
"component_name": "Generate",
|
154 |
+
"params": {
|
155 |
+
"llm_id": "deepseek-chat",
|
156 |
+
"prompt": "你是AGI方向的猎头,候选人问了有关职位或公司的问题,你根据以下职位信息回答。如果职位信息中不包含候选人的问题就回答不清楚、不知道、有待确认等。回答完后引导候选人加微信号,如:\n - 方便加一下微信吗,我把JD发您看看?\n - 微信号多少,我把详细职位JD发您?\n 职位信息如下:\n {input}\n 职位信息如上。",
|
157 |
+
"temperature": 0.02
|
158 |
+
}
|
159 |
+
},
|
160 |
+
"downstream": ["answer:1"],
|
161 |
+
"upstream": ["retrieval:0"]
|
162 |
+
},
|
163 |
+
"generate:get_wechat": {
|
164 |
+
"obj": {
|
165 |
+
"component_name": "Generate",
|
166 |
+
"params": {
|
167 |
+
"llm_id": "deepseek-chat",
|
168 |
+
"prompt": "你是AGI方向的猎头,候选人表示不反感加微信,如果对方已经报了微信号,表示感谢和信任并表示马上会加上;如果没有,则问对方微信号多少。你的微信号是weixin_kevin,E-mail是kkk@ragflow.com。说话不要重复。不要总是您好。",
|
169 |
+
"temperature": 0.1,
|
170 |
+
"message_history_window_size": 12,
|
171 |
+
"cite": false
|
172 |
+
}
|
173 |
+
},
|
174 |
+
"downstream": ["answer:1"],
|
175 |
+
"upstream": ["categorize:1"]
|
176 |
+
},
|
177 |
+
"generate:nowechat": {
|
178 |
+
"obj": {
|
179 |
+
"component_name": "Generate",
|
180 |
+
"params": {
|
181 |
+
"llm_id": "deepseek-chat",
|
182 |
+
"prompt": "你是AGI方向的猎头,当你提出加微信时对方表示拒绝。你需要耐心礼貌的回应候选人,表示对于保护隐私信息给予理解,也可以询问他对该职位的看法和顾虑。并在恰当的时机再次询问微信联系方式。也可以鼓励候选人主动与你取得联系。你的微信号是weixin_kevin,E-mail是kkk@ragflow.com。说话不要重复。不要总是您好。",
|
183 |
+
"temperature": 0.1,
|
184 |
+
"message_history_window_size": 12,
|
185 |
+
"cite": false
|
186 |
+
}
|
187 |
+
},
|
188 |
+
"downstream": ["answer:1"],
|
189 |
+
"upstream": ["categorize:1"]
|
190 |
+
},
|
191 |
+
"message:reject": {
|
192 |
+
"obj": {
|
193 |
+
"component_name": "Message",
|
194 |
+
"params": {
|
195 |
+
"messages": [
|
196 |
+
"好的,祝您生活愉快,工作顺利。",
|
197 |
+
"哦,好的,感谢您宝贵的时间!"
|
198 |
+
]
|
199 |
+
}
|
200 |
+
},
|
201 |
+
"downstream": ["answer:0"],
|
202 |
+
"upstream": ["categorize:0"]
|
203 |
+
}
|
204 |
+
},
|
205 |
+
"history": [],
|
206 |
+
"messages": [],
|
207 |
+
"path": [],
|
208 |
+
"reference": [],
|
209 |
+
"answer": []
|
210 |
+
}
|
agent/test/dsl_examples/intergreper.json
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"components": {
|
3 |
+
"begin": {
|
4 |
+
"obj":{
|
5 |
+
"component_name": "Begin",
|
6 |
+
"params": {
|
7 |
+
"prologue": "Hi there! Please enter the text you want to translate in format like: 'text you want to translate' => target language. For an example: 您好! => English"
|
8 |
+
}
|
9 |
+
},
|
10 |
+
"downstream": ["answer:0"],
|
11 |
+
"upstream": []
|
12 |
+
},
|
13 |
+
"answer:0": {
|
14 |
+
"obj": {
|
15 |
+
"component_name": "Answer",
|
16 |
+
"params": {}
|
17 |
+
},
|
18 |
+
"downstream": ["generate:0"],
|
19 |
+
"upstream": ["begin", "generate:0"]
|
20 |
+
},
|
21 |
+
"generate:0": {
|
22 |
+
"obj": {
|
23 |
+
"component_name": "Generate",
|
24 |
+
"params": {
|
25 |
+
"llm_id": "deepseek-chat",
|
26 |
+
"prompt": "You are an professional interpreter.\n- Role: an professional interpreter.\n- Input format: content need to be translated => target language. \n- Answer format: => translated content in target language. \n- Examples:\n - user: 您好! => English. assistant: => How are you doing!\n - user: You look good today. => Japanese. assistant: => 今日は調子がいいですね 。\n",
|
27 |
+
"temperature": 0.5
|
28 |
+
}
|
29 |
+
},
|
30 |
+
"downstream": ["answer:0"],
|
31 |
+
"upstream": ["answer:0"]
|
32 |
+
}
|
33 |
+
},
|
34 |
+
"history": [],
|
35 |
+
"messages": [],
|
36 |
+
"reference": {},
|
37 |
+
"path": [],
|
38 |
+
"answer": []
|
39 |
+
}
|
agent/test/dsl_examples/interpreter.json
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"components": {
|
3 |
+
"begin": {
|
4 |
+
"obj":{
|
5 |
+
"component_name": "Begin",
|
6 |
+
"params": {
|
7 |
+
"prologue": "Hi there! Please enter the text you want to translate in format like: 'text you want to translate' => target language. For an example: 您好! => English"
|
8 |
+
}
|
9 |
+
},
|
10 |
+
"downstream": ["answer:0"],
|
11 |
+
"upstream": []
|
12 |
+
},
|
13 |
+
"answer:0": {
|
14 |
+
"obj": {
|
15 |
+
"component_name": "Answer",
|
16 |
+
"params": {}
|
17 |
+
},
|
18 |
+
"downstream": ["generate:0"],
|
19 |
+
"upstream": ["begin", "generate:0"]
|
20 |
+
},
|
21 |
+
"generate:0": {
|
22 |
+
"obj": {
|
23 |
+
"component_name": "Generate",
|
24 |
+
"params": {
|
25 |
+
"llm_id": "deepseek-chat",
|
26 |
+
"prompt": "You are an professional interpreter.\n- Role: an professional interpreter.\n- Input format: content need to be translated => target language. \n- Answer format: => translated content in target language. \n- Examples:\n - user: 您好! => English. assistant: => How are you doing!\n - user: You look good today. => Japanese. assistant: => 今日は調子がいいですね 。\n",
|
27 |
+
"temperature": 0.5
|
28 |
+
}
|
29 |
+
},
|
30 |
+
"downstream": ["answer:0"],
|
31 |
+
"upstream": ["answer:0"]
|
32 |
+
}
|
33 |
+
},
|
34 |
+
"history": [],
|
35 |
+
"messages": [],
|
36 |
+
"reference": {},
|
37 |
+
"path": [],
|
38 |
+
"answer": []
|
39 |
+
}
|
agent/test/dsl_examples/keyword_wikipedia_and_generate.json
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"components": {
|
3 |
+
"begin": {
|
4 |
+
"obj":{
|
5 |
+
"component_name": "Begin",
|
6 |
+
"params": {
|
7 |
+
"prologue": "Hi there!"
|
8 |
+
}
|
9 |
+
},
|
10 |
+
"downstream": ["answer:0"],
|
11 |
+
"upstream": []
|
12 |
+
},
|
13 |
+
"answer:0": {
|
14 |
+
"obj": {
|
15 |
+
"component_name": "Answer",
|
16 |
+
"params": {}
|
17 |
+
},
|
18 |
+
"downstream": ["keyword:0"],
|
19 |
+
"upstream": ["begin"]
|
20 |
+
},
|
21 |
+
"keyword:0": {
|
22 |
+
"obj": {
|
23 |
+
"component_name": "KeywordExtract",
|
24 |
+
"params": {
|
25 |
+
"llm_id": "deepseek-chat",
|
26 |
+
"prompt": "- Role: You're a question analyzer.\n - Requirements:\n - Summarize user's question, and give top %s important keyword/phrase.\n - Use comma as a delimiter to separate keywords/phrases.\n - Answer format: (in language of user's question)\n - keyword: ",
|
27 |
+
"temperature": 0.2,
|
28 |
+
"top_n": 1
|
29 |
+
}
|
30 |
+
},
|
31 |
+
"downstream": ["wikipedia:0"],
|
32 |
+
"upstream": ["answer:0"]
|
33 |
+
},
|
34 |
+
"wikipedia:0": {
|
35 |
+
"obj":{
|
36 |
+
"component_name": "Wikipedia",
|
37 |
+
"params": {
|
38 |
+
"top_n": 10
|
39 |
+
}
|
40 |
+
},
|
41 |
+
"downstream": ["generate:0"],
|
42 |
+
"upstream": ["keyword:0"]
|
43 |
+
},
|
44 |
+
"generate:1": {
|
45 |
+
"obj": {
|
46 |
+
"component_name": "Generate",
|
47 |
+
"params": {
|
48 |
+
"llm_id": "deepseek-chat",
|
49 |
+
"prompt": "You are an intelligent assistant. Please answer the question based on content from Wikipedia. When the answer from Wikipedia is incomplete, you need to output the URL link of the corresponding content as well. When all the content searched from Wikipedia is irrelevant to the question, your answer must include the sentence, \"The answer you are looking for is not found in the Wikipedia!\". Answers need to consider chat history.\n The content of Wikipedia is as follows:\n {input}\n The above is the content of Wikipedia.",
|
50 |
+
"temperature": 0.2
|
51 |
+
}
|
52 |
+
},
|
53 |
+
"downstream": ["answer:0"],
|
54 |
+
"upstream": ["wikipedia:0"]
|
55 |
+
}
|
56 |
+
},
|
57 |
+
"history": [],
|
58 |
+
"path": [],
|
59 |
+
"messages": [],
|
60 |
+
"reference": {},
|
61 |
+
"answer": []
|
62 |
+
}
|
agent/test/dsl_examples/retrieval_and_generate.json
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"components": {
|
3 |
+
"begin": {
|
4 |
+
"obj":{
|
5 |
+
"component_name": "Begin",
|
6 |
+
"params": {
|
7 |
+
"prologue": "Hi there!"
|
8 |
+
}
|
9 |
+
},
|
10 |
+
"downstream": ["answer:0"],
|
11 |
+
"upstream": []
|
12 |
+
},
|
13 |
+
"answer:0": {
|
14 |
+
"obj": {
|
15 |
+
"component_name": "Answer",
|
16 |
+
"params": {}
|
17 |
+
},
|
18 |
+
"downstream": ["retrieval:0"],
|
19 |
+
"upstream": ["begin", "generate:0"]
|
20 |
+
},
|
21 |
+
"retrieval:0": {
|
22 |
+
"obj": {
|
23 |
+
"component_name": "Retrieval",
|
24 |
+
"params": {
|
25 |
+
"similarity_threshold": 0.2,
|
26 |
+
"keywords_similarity_weight": 0.3,
|
27 |
+
"top_n": 6,
|
28 |
+
"top_k": 1024,
|
29 |
+
"rerank_id": "BAAI/bge-reranker-v2-m3",
|
30 |
+
"kb_ids": ["869a236818b811ef91dffa163e197198"]
|
31 |
+
}
|
32 |
+
},
|
33 |
+
"downstream": ["generate:0"],
|
34 |
+
"upstream": ["answer:0"]
|
35 |
+
},
|
36 |
+
"generate:0": {
|
37 |
+
"obj": {
|
38 |
+
"component_name": "Generate",
|
39 |
+
"params": {
|
40 |
+
"llm_id": "deepseek-chat",
|
41 |
+
"prompt": "You are an intelligent assistant. Please summarize the content of the knowledge base to answer the question. Please list the data in the knowledge base and answer in detail. When all knowledge base content is irrelevant to the question, your answer must include the sentence \"The answer you are looking for is not found in the knowledge base!\" Answers need to consider chat history.\n Here is the knowledge base:\n {input}\n The above is the knowledge base.",
|
42 |
+
"temperature": 0.2
|
43 |
+
}
|
44 |
+
},
|
45 |
+
"downstream": ["answer:0"],
|
46 |
+
"upstream": ["retrieval:0"]
|
47 |
+
}
|
48 |
+
},
|
49 |
+
"history": [],
|
50 |
+
"messages": [],
|
51 |
+
"reference": {},
|
52 |
+
"path": [],
|
53 |
+
"answer": []
|
54 |
+
}
|
agent/test/dsl_examples/retrieval_categorize_and_generate.json
ADDED
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"components": {
|
3 |
+
"begin": {
|
4 |
+
"obj":{
|
5 |
+
"component_name": "Begin",
|
6 |
+
"params": {
|
7 |
+
"prologue": "Hi there!"
|
8 |
+
}
|
9 |
+
},
|
10 |
+
"downstream": ["answer:0"],
|
11 |
+
"upstream": []
|
12 |
+
},
|
13 |
+
"answer:0": {
|
14 |
+
"obj": {
|
15 |
+
"component_name": "Answer",
|
16 |
+
"params": {}
|
17 |
+
},
|
18 |
+
"downstream": ["categorize:0"],
|
19 |
+
"upstream": ["begin", "generate:0", "switch:0"]
|
20 |
+
},
|
21 |
+
"categorize:0": {
|
22 |
+
"obj": {
|
23 |
+
"component_name": "Categorize",
|
24 |
+
"params": {
|
25 |
+
"llm_id": "deepseek-chat",
|
26 |
+
"category_description": {
|
27 |
+
"product_related": {
|
28 |
+
"description": "The question is about the product usage, appearance and how it works.",
|
29 |
+
"examples": "Why it always beaming?\nHow to install it onto the wall?\nIt leaks, what to do?",
|
30 |
+
"to": "retrieval:0"
|
31 |
+
},
|
32 |
+
"others": {
|
33 |
+
"description": "The question is not about the product usage, appearance and how it works.",
|
34 |
+
"examples": "How are you doing?\nWhat is your name?\nAre you a robot?\nWhat's the weather?\nWill it rain?",
|
35 |
+
"to": "message:0"
|
36 |
+
}
|
37 |
+
}
|
38 |
+
}
|
39 |
+
},
|
40 |
+
"downstream": ["retrieval:0", "message:0"],
|
41 |
+
"upstream": ["answer:0"]
|
42 |
+
},
|
43 |
+
"message:0": {
|
44 |
+
"obj":{
|
45 |
+
"component_name": "Message",
|
46 |
+
"params": {
|
47 |
+
"messages": [
|
48 |
+
"Sorry, I don't know. I'm an AI bot."
|
49 |
+
]
|
50 |
+
}
|
51 |
+
},
|
52 |
+
"downstream": ["answer:0"],
|
53 |
+
"upstream": ["categorize:0"]
|
54 |
+
},
|
55 |
+
"retrieval:0": {
|
56 |
+
"obj": {
|
57 |
+
"component_name": "Retrieval",
|
58 |
+
"params": {
|
59 |
+
"similarity_threshold": 0.2,
|
60 |
+
"keywords_similarity_weight": 0.3,
|
61 |
+
"top_n": 6,
|
62 |
+
"top_k": 1024,
|
63 |
+
"rerank_id": "BAAI/bge-reranker-v2-m3",
|
64 |
+
"kb_ids": ["869a236818b811ef91dffa163e197198"]
|
65 |
+
}
|
66 |
+
},
|
67 |
+
"downstream": ["generate:0"],
|
68 |
+
"upstream": ["switch:0"]
|
69 |
+
},
|
70 |
+
"generate:0": {
|
71 |
+
"obj": {
|
72 |
+
"component_name": "Generate",
|
73 |
+
"params": {
|
74 |
+
"llm_id": "deepseek-chat",
|
75 |
+
"prompt": "You are an intelligent assistant. Please summarize the content of the knowledge base to answer the question. Please list the data in the knowledge base and answer in detail. When all knowledge base content is irrelevant to the question, your answer must include the sentence \"The answer you are looking for is not found in the knowledge base!\" Answers need to consider chat history.\n Here is the knowledge base:\n {input}\n The above is the knowledge base.",
|
76 |
+
"temperature": 0.2
|
77 |
+
}
|
78 |
+
},
|
79 |
+
"downstream": ["answer:0"],
|
80 |
+
"upstream": ["retrieval:0"]
|
81 |
+
}
|
82 |
+
},
|
83 |
+
"history": [],
|
84 |
+
"messages": [],
|
85 |
+
"reference": {},
|
86 |
+
"path": [],
|
87 |
+
"answer": []
|
88 |
+
}
|
agent/test/dsl_examples/retrieval_relevant_and_generate.json
ADDED
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"components": {
|
3 |
+
"begin": {
|
4 |
+
"obj":{
|
5 |
+
"component_name": "Begin",
|
6 |
+
"params": {
|
7 |
+
"prologue": "Hi there!"
|
8 |
+
}
|
9 |
+
},
|
10 |
+
"downstream": ["answer:0"],
|
11 |
+
"upstream": []
|
12 |
+
},
|
13 |
+
"answer:0": {
|
14 |
+
"obj": {
|
15 |
+
"component_name": "Answer",
|
16 |
+
"params": {}
|
17 |
+
},
|
18 |
+
"downstream": ["retrieval:0"],
|
19 |
+
"upstream": ["begin", "generate:0", "switch:0"]
|
20 |
+
},
|
21 |
+
"retrieval:0": {
|
22 |
+
"obj": {
|
23 |
+
"component_name": "Retrieval",
|
24 |
+
"params": {
|
25 |
+
"similarity_threshold": 0.2,
|
26 |
+
"keywords_similarity_weight": 0.3,
|
27 |
+
"top_n": 6,
|
28 |
+
"top_k": 1024,
|
29 |
+
"rerank_id": "BAAI/bge-reranker-v2-m3",
|
30 |
+
"kb_ids": ["869a236818b811ef91dffa163e197198"],
|
31 |
+
"empty_response": "Sorry, knowledge base has noting related information."
|
32 |
+
}
|
33 |
+
},
|
34 |
+
"downstream": ["relevant:0"],
|
35 |
+
"upstream": ["answer:0"]
|
36 |
+
},
|
37 |
+
"relevant:0": {
|
38 |
+
"obj": {
|
39 |
+
"component_name": "Relevant",
|
40 |
+
"params": {
|
41 |
+
"llm_id": "deepseek-chat",
|
42 |
+
"temperature": 0.02,
|
43 |
+
"yes": "generate:0",
|
44 |
+
"no": "message:0"
|
45 |
+
}
|
46 |
+
},
|
47 |
+
"downstream": ["message:0", "generate:0"],
|
48 |
+
"upstream": ["retrieval:0"]
|
49 |
+
},
|
50 |
+
"generate:0": {
|
51 |
+
"obj": {
|
52 |
+
"component_name": "Generate",
|
53 |
+
"params": {
|
54 |
+
"llm_id": "deepseek-chat",
|
55 |
+
"prompt": "You are an intelligent assistant. Please answer the question based on content of knowledge base. When all knowledge base content is irrelevant to the question, your answer must include the sentence \"The answer you are looking for is not found in the knowledge base!\". Answers need to consider chat history.\n Knowledge base content is as following:\n {input}\n The above is the content of knowledge base.",
|
56 |
+
"temperature": 0.2
|
57 |
+
}
|
58 |
+
},
|
59 |
+
"downstream": ["answer:0"],
|
60 |
+
"upstream": ["relevant:0"]
|
61 |
+
},
|
62 |
+
"message:0": {
|
63 |
+
"obj":{
|
64 |
+
"component_name": "Message",
|
65 |
+
"params": {
|
66 |
+
"messages": [
|
67 |
+
"Sorry, I don't know. Please leave your contact, our experts will contact you later. What's your e-mail/phone/wechat?",
|
68 |
+
"I'm an AI bot and not quite sure about this question. Please leave your contact, our experts will contact you later. What's your e-mail/phone/wechat?",
|
69 |
+
"Can't find answer in my knowledge base. Please leave your contact, our experts will contact you later. What's your e-mail/phone/wechat?"
|
70 |
+
]
|
71 |
+
}
|
72 |
+
},
|
73 |
+
"downstream": ["answer:0"],
|
74 |
+
"upstream": ["relevant:0"]
|
75 |
+
}
|
76 |
+
},
|
77 |
+
"history": [],
|
78 |
+
"path": [],
|
79 |
+
"messages": [],
|
80 |
+
"reference": {},
|
81 |
+
"answer": []
|
82 |
+
}
|
agent/test/dsl_examples/retrieval_relevant_keyword_baidu_and_generate.json
ADDED
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"components": {
|
3 |
+
"begin": {
|
4 |
+
"obj":{
|
5 |
+
"component_name": "Begin",
|
6 |
+
"params": {
|
7 |
+
"prologue": "Hi there!"
|
8 |
+
}
|
9 |
+
},
|
10 |
+
"downstream": ["answer:0"],
|
11 |
+
"upstream": []
|
12 |
+
},
|
13 |
+
"answer:0": {
|
14 |
+
"obj": {
|
15 |
+
"component_name": "Answer",
|
16 |
+
"params": {}
|
17 |
+
},
|
18 |
+
"downstream": ["retrieval:0"],
|
19 |
+
"upstream": ["begin"]
|
20 |
+
},
|
21 |
+
"retrieval:0": {
|
22 |
+
"obj": {
|
23 |
+
"component_name": "Retrieval",
|
24 |
+
"params": {
|
25 |
+
"similarity_threshold": 0.2,
|
26 |
+
"keywords_similarity_weight": 0.3,
|
27 |
+
"top_n": 6,
|
28 |
+
"top_k": 1024,
|
29 |
+
"rerank_id": "BAAI/bge-reranker-v2-m3",
|
30 |
+
"kb_ids": ["21ca4e6a2c8911ef8b1e0242ac120006"],
|
31 |
+
"empty_response": "Sorry, knowledge base has noting related information."
|
32 |
+
}
|
33 |
+
},
|
34 |
+
"downstream": ["relevant:0"],
|
35 |
+
"upstream": ["answer:0"]
|
36 |
+
},
|
37 |
+
"relevant:0": {
|
38 |
+
"obj": {
|
39 |
+
"component_name": "Relevant",
|
40 |
+
"params": {
|
41 |
+
"llm_id": "deepseek-chat",
|
42 |
+
"temperature": 0.02,
|
43 |
+
"yes": "generate:0",
|
44 |
+
"no": "keyword:0"
|
45 |
+
}
|
46 |
+
},
|
47 |
+
"downstream": ["keyword:0", "generate:0"],
|
48 |
+
"upstream": ["retrieval:0"]
|
49 |
+
},
|
50 |
+
"generate:0": {
|
51 |
+
"obj": {
|
52 |
+
"component_name": "Generate",
|
53 |
+
"params": {
|
54 |
+
"llm_id": "deepseek-chat",
|
55 |
+
"prompt": "You are an intelligent assistant. Please answer the question based on content of knowledge base. When all knowledge base content is irrelevant to the question, your answer must include the sentence \"The answer you are looking for is not found in the knowledge base!\". Answers need to consider chat history.\n Knowledge base content is as following:\n {input}\n The above is the content of knowledge base.",
|
56 |
+
"temperature": 0.2
|
57 |
+
}
|
58 |
+
},
|
59 |
+
"downstream": ["answer:0"],
|
60 |
+
"upstream": ["relevant:0"]
|
61 |
+
},
|
62 |
+
"keyword:0": {
|
63 |
+
"obj": {
|
64 |
+
"component_name": "KeywordExtract",
|
65 |
+
"params": {
|
66 |
+
"llm_id": "deepseek-chat",
|
67 |
+
"prompt": "- Role: You're a question analyzer.\n - Requirements:\n - Summarize user's question, and give top %s important keyword/phrase.\n - Use comma as a delimiter to separate keywords/phrases.\n - Answer format: (in language of user's question)\n - keyword: ",
|
68 |
+
"temperature": 0.2,
|
69 |
+
"top_n": 1
|
70 |
+
}
|
71 |
+
},
|
72 |
+
"downstream": ["baidu:0"],
|
73 |
+
"upstream": ["relevant:0"]
|
74 |
+
},
|
75 |
+
"baidu:0": {
|
76 |
+
"obj":{
|
77 |
+
"component_name": "Baidu",
|
78 |
+
"params": {
|
79 |
+
"top_n": 10
|
80 |
+
}
|
81 |
+
},
|
82 |
+
"downstream": ["generate:1"],
|
83 |
+
"upstream": ["keyword:0"]
|
84 |
+
},
|
85 |
+
"generate:1": {
|
86 |
+
"obj": {
|
87 |
+
"component_name": "Generate",
|
88 |
+
"params": {
|
89 |
+
"llm_id": "deepseek-chat",
|
90 |
+
"prompt": "You are an intelligent assistant. Please answer the question based on content searched from Baidu. When the answer from a Baidu search is incomplete, you need to output the URL link of the corresponding content as well. When all the content searched from Baidu is irrelevant to the question, your answer must include the sentence, \"The answer you are looking for is not found in the Baidu search!\". Answers need to consider chat history.\n The content of Baidu search is as follows:\n {input}\n The above is the content of Baidu search.",
|
91 |
+
"temperature": 0.2
|
92 |
+
}
|
93 |
+
},
|
94 |
+
"downstream": ["answer:0"],
|
95 |
+
"upstream": ["baidu:0"]
|
96 |
+
}
|
97 |
+
},
|
98 |
+
"history": [],
|
99 |
+
"path": [],
|
100 |
+
"messages": [],
|
101 |
+
"reference": {},
|
102 |
+
"answer": []
|
103 |
+
}
|
agent/test/dsl_examples/retrieval_relevant_rewrite_and_generate.json
ADDED
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"components": {
|
3 |
+
"begin": {
|
4 |
+
"obj":{
|
5 |
+
"component_name": "Begin",
|
6 |
+
"params": {
|
7 |
+
"prologue": "Hi there!"
|
8 |
+
}
|
9 |
+
},
|
10 |
+
"downstream": ["answer:0"],
|
11 |
+
"upstream": []
|
12 |
+
},
|
13 |
+
"answer:0": {
|
14 |
+
"obj": {
|
15 |
+
"component_name": "Answer",
|
16 |
+
"params": {}
|
17 |
+
},
|
18 |
+
"downstream": ["retrieval:0"],
|
19 |
+
"upstream": ["begin", "generate:0", "switch:0"]
|
20 |
+
},
|
21 |
+
"retrieval:0": {
|
22 |
+
"obj": {
|
23 |
+
"component_name": "Retrieval",
|
24 |
+
"params": {
|
25 |
+
"similarity_threshold": 0.2,
|
26 |
+
"keywords_similarity_weight": 0.3,
|
27 |
+
"top_n": 6,
|
28 |
+
"top_k": 1024,
|
29 |
+
"rerank_id": "BAAI/bge-reranker-v2-m3",
|
30 |
+
"kb_ids": ["869a236818b811ef91dffa163e197198"],
|
31 |
+
"empty_response": "Sorry, knowledge base has noting related information."
|
32 |
+
}
|
33 |
+
},
|
34 |
+
"downstream": ["relevant:0"],
|
35 |
+
"upstream": ["answer:0", "rewrite:0"]
|
36 |
+
},
|
37 |
+
"relevant:0": {
|
38 |
+
"obj": {
|
39 |
+
"component_name": "Relevant",
|
40 |
+
"params": {
|
41 |
+
"llm_id": "deepseek-chat",
|
42 |
+
"temperature": 0.02,
|
43 |
+
"yes": "generate:0",
|
44 |
+
"no": "rewrite:0"
|
45 |
+
}
|
46 |
+
},
|
47 |
+
"downstream": ["generate:0", "rewrite:0"],
|
48 |
+
"upstream": ["retrieval:0"]
|
49 |
+
},
|
50 |
+
"generate:0": {
|
51 |
+
"obj": {
|
52 |
+
"component_name": "Generate",
|
53 |
+
"params": {
|
54 |
+
"llm_id": "deepseek-chat",
|
55 |
+
"prompt": "You are an intelligent assistant. Please answer the question based on content of knowledge base. When all knowledge base content is irrelevant to the question, your answer must include the sentence \"The answer you are looking for is not found in the knowledge base!\". Answers need to consider chat history.\n Knowledge base content is as following:\n {input}\n The above is the content of knowledge base.",
|
56 |
+
"temperature": 0.02
|
57 |
+
}
|
58 |
+
},
|
59 |
+
"downstream": ["answer:0"],
|
60 |
+
"upstream": ["relevant:0"]
|
61 |
+
},
|
62 |
+
"rewrite:0": {
|
63 |
+
"obj":{
|
64 |
+
"component_name": "RewriteQuestion",
|
65 |
+
"params": {
|
66 |
+
"llm_id": "deepseek-chat",
|
67 |
+
"temperature": 0.8
|
68 |
+
}
|
69 |
+
},
|
70 |
+
"downstream": ["retrieval:0"],
|
71 |
+
"upstream": ["relevant:0"]
|
72 |
+
}
|
73 |
+
},
|
74 |
+
"history": [],
|
75 |
+
"messages": [],
|
76 |
+
"path": [],
|
77 |
+
"reference": [],
|
78 |
+
"answer": []
|
79 |
+
}
|
api/__init__.py
ADDED
File without changes
|
api/apps/__init__.py
ADDED
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#
|
2 |
+
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
3 |
+
#
|
4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5 |
+
# you may not use this file except in compliance with the License.
|
6 |
+
# You may obtain a copy of the License at
|
7 |
+
#
|
8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9 |
+
#
|
10 |
+
# Unless required by applicable law or agreed to in writing, software
|
11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13 |
+
# See the License for the specific language governing permissions and
|
14 |
+
# limitations under the License.
|
15 |
+
#
|
16 |
+
import logging
|
17 |
+
import os
|
18 |
+
import sys
|
19 |
+
from importlib.util import module_from_spec, spec_from_file_location
|
20 |
+
from pathlib import Path
|
21 |
+
from flask import Blueprint, Flask
|
22 |
+
from werkzeug.wrappers.request import Request
|
23 |
+
from flask_cors import CORS
|
24 |
+
|
25 |
+
from api.db import StatusEnum
|
26 |
+
from api.db.db_models import close_connection
|
27 |
+
from api.db.services import UserService
|
28 |
+
from api.utils import CustomJSONEncoder, commands
|
29 |
+
|
30 |
+
from flask_session import Session
|
31 |
+
from flask_login import LoginManager
|
32 |
+
from api.settings import SECRET_KEY, stat_logger
|
33 |
+
from api.settings import API_VERSION, access_logger
|
34 |
+
from api.utils.api_utils import server_error_response
|
35 |
+
from itsdangerous.url_safe import URLSafeTimedSerializer as Serializer
|
36 |
+
|
37 |
+
__all__ = ['app']
|
38 |
+
|
39 |
+
|
40 |
+
logger = logging.getLogger('flask.app')
|
41 |
+
for h in access_logger.handlers:
|
42 |
+
logger.addHandler(h)
|
43 |
+
|
44 |
+
Request.json = property(lambda self: self.get_json(force=True, silent=True))
|
45 |
+
|
46 |
+
app = Flask(__name__)
|
47 |
+
CORS(app, supports_credentials=True,max_age=2592000)
|
48 |
+
app.url_map.strict_slashes = False
|
49 |
+
app.json_encoder = CustomJSONEncoder
|
50 |
+
app.errorhandler(Exception)(server_error_response)
|
51 |
+
|
52 |
+
|
53 |
+
## convince for dev and debug
|
54 |
+
#app.config["LOGIN_DISABLED"] = True
|
55 |
+
app.config["SESSION_PERMANENT"] = False
|
56 |
+
app.config["SESSION_TYPE"] = "filesystem"
|
57 |
+
app.config['MAX_CONTENT_LENGTH'] = int(os.environ.get("MAX_CONTENT_LENGTH", 128 * 1024 * 1024))
|
58 |
+
|
59 |
+
Session(app)
|
60 |
+
login_manager = LoginManager()
|
61 |
+
login_manager.init_app(app)
|
62 |
+
|
63 |
+
commands.register_commands(app)
|
64 |
+
|
65 |
+
|
66 |
+
def search_pages_path(pages_dir):
|
67 |
+
app_path_list = [path for path in pages_dir.glob('*_app.py') if not path.name.startswith('.')]
|
68 |
+
api_path_list = [path for path in pages_dir.glob('*_api.py') if not path.name.startswith('.')]
|
69 |
+
app_path_list.extend(api_path_list)
|
70 |
+
return app_path_list
|
71 |
+
|
72 |
+
|
73 |
+
def register_page(page_path):
|
74 |
+
path = f'{page_path}'
|
75 |
+
|
76 |
+
page_name = page_path.stem.rstrip('_api') if "_api" in path else page_path.stem.rstrip('_app')
|
77 |
+
module_name = '.'.join(page_path.parts[page_path.parts.index('api'):-1] + (page_name,))
|
78 |
+
|
79 |
+
spec = spec_from_file_location(module_name, page_path)
|
80 |
+
page = module_from_spec(spec)
|
81 |
+
page.app = app
|
82 |
+
page.manager = Blueprint(page_name, module_name)
|
83 |
+
sys.modules[module_name] = page
|
84 |
+
spec.loader.exec_module(page)
|
85 |
+
page_name = getattr(page, 'page_name', page_name)
|
86 |
+
url_prefix = f'/api/{API_VERSION}/{page_name}' if "_api" in path else f'/{API_VERSION}/{page_name}'
|
87 |
+
|
88 |
+
app.register_blueprint(page.manager, url_prefix=url_prefix)
|
89 |
+
return url_prefix
|
90 |
+
|
91 |
+
|
92 |
+
pages_dir = [
|
93 |
+
Path(__file__).parent,
|
94 |
+
Path(__file__).parent.parent / 'api' / 'apps', # FIXME: ragflow/api/api/apps, can be remove?
|
95 |
+
]
|
96 |
+
|
97 |
+
client_urls_prefix = [
|
98 |
+
register_page(path)
|
99 |
+
for dir in pages_dir
|
100 |
+
for path in search_pages_path(dir)
|
101 |
+
]
|
102 |
+
|
103 |
+
|
104 |
+
@login_manager.request_loader
|
105 |
+
def load_user(web_request):
|
106 |
+
jwt = Serializer(secret_key=SECRET_KEY)
|
107 |
+
authorization = web_request.headers.get("Authorization")
|
108 |
+
if authorization:
|
109 |
+
try:
|
110 |
+
access_token = str(jwt.loads(authorization))
|
111 |
+
user = UserService.query(access_token=access_token, status=StatusEnum.VALID.value)
|
112 |
+
if user:
|
113 |
+
return user[0]
|
114 |
+
else:
|
115 |
+
return None
|
116 |
+
except Exception as e:
|
117 |
+
stat_logger.exception(e)
|
118 |
+
return None
|
119 |
+
else:
|
120 |
+
return None
|
121 |
+
|
122 |
+
|
123 |
+
@app.teardown_request
|
124 |
+
def _db_close(exc):
|
125 |
+
close_connection()
|