diff --git a/Dockerfile b/Dockerfile index 40f930a..be3b73d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,6 @@ WORKDIR /ragflow COPY api ./api COPY conf ./conf COPY rag ./rag -COPY agent ./agent COPY graphrag ./graphrag COPY agentic_reasoning ./agentic_reasoning diff --git a/agent/README.md b/agent/README.md deleted file mode 100644 index 250149b..0000000 --- a/agent/README.md +++ /dev/null @@ -1,45 +0,0 @@ -English | [简体中文](./README_zh.md) - -# *Graph* - - -## Introduction - -*Graph* is a mathematical concept which is composed of nodes and edges. -It is used to compose a complex work flow or agent. -And this graph is beyond the DAG that we can use circles to describe our agent or work flow. -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. -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. -Otherwise, correct configurations in service_conf.yaml is essential. - -```bash -PYTHONPATH=path/to/ragflow python graph/test/client.py -h -usage: client.py [-h] -s DSL -t TENANT_ID -m - -options: - -h, --help show this help message and exit - -s DSL, --dsl DSL input dsl - -t TENANT_ID, --tenant_id TENANT_ID - Tenant ID - -m, --stream Stream output -``` -
- -
- - -## How to gain a TENANT_ID in command line? -
- -
-💡 We plan to display it here in the near future. -
- -
- - -## How to set 'kb_ids' for component 'Retrieval' in DSL? -
- -
- diff --git a/agent/README_zh.md b/agent/README_zh.md deleted file mode 100644 index 411e31e..0000000 --- a/agent/README_zh.md +++ /dev/null @@ -1,46 +0,0 @@ -[English](./README.md) | 简体中文 - -# *Graph* - - -## 简介 - -"Graph"是一个由节点和边组成的数学概念。 -它被用来构建复杂的工作流或代理。 -这个图超越了有向无环图(DAG),我们可以使用循环来描述我们的代理或工作流。 -在这个文件夹下,我们提出了一个测试工具 ./test/client.py, -它可以测试像文件夹./test/dsl_examples下一样的DSL文件。 -请在启动 RAGFlow 的同一文件夹中使用此客户端。如果它是通过 Docker 运行的,请在运行客户端之前进入容器。 -否则,正确配置 service_conf.yaml 文件是必不可少的。 - -```bash -PYTHONPATH=path/to/ragflow python graph/test/client.py -h -usage: client.py [-h] -s DSL -t TENANT_ID -m - -options: - -h, --help show this help message and exit - -s DSL, --dsl DSL input dsl - -t TENANT_ID, --tenant_id TENANT_ID - Tenant ID - -m, --stream Stream output -``` -
- -
- - -## 命令行中的TENANT_ID如何获得? -
- -
-💡 后面会展示在这里: -
- -
- - -## DSL里面的Retrieval组件的kb_ids怎么填? -
- -
- diff --git a/agent/__init__.py b/agent/__init__.py deleted file mode 100644 index 643f797..0000000 --- a/agent/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# -# Copyright 2025 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -from beartype.claw import beartype_this_package -beartype_this_package() diff --git a/agent/canvas.py b/agent/canvas.py deleted file mode 100644 index b57dab3..0000000 --- a/agent/canvas.py +++ /dev/null @@ -1,365 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import logging -import json -from copy import deepcopy -from functools import partial - -import pandas as pd - -from agent.component import component_class -from agent.component.base import ComponentBase - - -class Canvas: - """ - dsl = { - "components": { - "begin": { - "obj":{ - "component_name": "Begin", - "params": {}, - }, - "downstream": ["answer_0"], - "upstream": [], - }, - "answer_0": { - "obj": { - "component_name": "Answer", - "params": {} - }, - "downstream": ["retrieval_0"], - "upstream": ["begin", "generate_0"], - }, - "retrieval_0": { - "obj": { - "component_name": "Retrieval", - "params": {} - }, - "downstream": ["generate_0"], - "upstream": ["answer_0"], - }, - "generate_0": { - "obj": { - "component_name": "Generate", - "params": {} - }, - "downstream": ["answer_0"], - "upstream": ["retrieval_0"], - } - }, - "history": [], - "messages": [], - "reference": [], - "path": [["begin"]], - "answer": [] - } - """ - - def __init__(self, dsl: str, tenant_id=None): - self.path = [] - self.history = [] - self.messages = [] - self.answer = [] - self.components = {} - self.dsl = json.loads(dsl) if dsl else { - "components": { - "begin": { - "obj": { - "component_name": "Begin", - "params": { - "prologue": "Hi there!" - } - }, - "downstream": [], - "upstream": [], - "parent_id": "" - } - }, - "history": [], - "messages": [], - "reference": [], - "path": [], - "answer": [] - } - self._tenant_id = tenant_id - self._embed_id = "" - self.load() - - def load(self): - self.components = self.dsl["components"] - cpn_nms = set([]) - for k, cpn in self.components.items(): - cpn_nms.add(cpn["obj"]["component_name"]) - - assert "Begin" in cpn_nms, "There have to be an 'Begin' component." - assert "Answer" in cpn_nms, "There have to be an 'Answer' component." - - for k, cpn in self.components.items(): - cpn_nms.add(cpn["obj"]["component_name"]) - param = component_class(cpn["obj"]["component_name"] + "Param")() - param.update(cpn["obj"]["params"]) - param.check() - cpn["obj"] = component_class(cpn["obj"]["component_name"])(self, k, param) - if cpn["obj"].component_name == "Categorize": - for _, desc in param.category_description.items(): - if desc["to"] not in cpn["downstream"]: - cpn["downstream"].append(desc["to"]) - - self.path = self.dsl["path"] - self.history = self.dsl["history"] - self.messages = self.dsl["messages"] - self.answer = self.dsl["answer"] - self.reference = self.dsl["reference"] - self._embed_id = self.dsl.get("embed_id", "") - - def __str__(self): - self.dsl["path"] = self.path - self.dsl["history"] = self.history - self.dsl["messages"] = self.messages - self.dsl["answer"] = self.answer - self.dsl["reference"] = self.reference - self.dsl["embed_id"] = self._embed_id - dsl = { - "components": {} - } - for k in self.dsl.keys(): - if k in ["components"]: - continue - dsl[k] = deepcopy(self.dsl[k]) - - for k, cpn in self.components.items(): - if k not in dsl["components"]: - dsl["components"][k] = {} - for c in cpn.keys(): - if c == "obj": - dsl["components"][k][c] = json.loads(str(cpn["obj"])) - continue - dsl["components"][k][c] = deepcopy(cpn[c]) - return json.dumps(dsl, ensure_ascii=False) - - def reset(self): - self.path = [] - self.history = [] - self.messages = [] - self.answer = [] - self.reference = [] - for k, cpn in self.components.items(): - self.components[k]["obj"].reset() - self._embed_id = "" - - def get_component_name(self, cid): - for n in self.dsl["graph"]["nodes"]: - if cid == n["id"]: - return n["data"]["name"] - return "" - - def run(self, **kwargs): - if self.answer: - cpn_id = self.answer[0] - self.answer.pop(0) - try: - ans = self.components[cpn_id]["obj"].run(self.history, **kwargs) - except Exception as e: - ans = ComponentBase.be_output(str(e)) - self.path[-1].append(cpn_id) - if kwargs.get("stream"): - for an in ans(): - yield an - else: - yield ans - return - - if not self.path: - self.components["begin"]["obj"].run(self.history, **kwargs) - self.path.append(["begin"]) - - self.path.append([]) - - ran = -1 - waiting = [] - without_dependent_checking = [] - - def prepare2run(cpns): - nonlocal ran, ans - for c in cpns: - if self.path[-1] and c == self.path[-1][-1]: - continue - cpn = self.components[c]["obj"] - if cpn.component_name == "Answer": - self.answer.append(c) - else: - logging.debug(f"Canvas.prepare2run: {c}") - if c not in without_dependent_checking: - cpids = cpn.get_dependent_components() - if any([cc not in self.path[-1] for cc in cpids]): - if c not in waiting: - waiting.append(c) - continue - yield "*'{}'* is running...🕞".format(self.get_component_name(c)) - - if cpn.component_name.lower() == "iteration": - st_cpn = cpn.get_start() - assert st_cpn, "Start component not found for Iteration." - if not st_cpn["obj"].end(): - cpn = st_cpn["obj"] - c = cpn._id - - try: - ans = cpn.run(self.history, **kwargs) - except Exception as e: - logging.exception(f"Canvas.run got exception: {e}") - self.path[-1].append(c) - ran += 1 - raise e - self.path[-1].append(c) - - ran += 1 - - downstream = self.components[self.path[-2][-1]]["downstream"] - if not downstream and self.components[self.path[-2][-1]].get("parent_id"): - cid = self.path[-2][-1] - pid = self.components[cid]["parent_id"] - o, _ = self.components[cid]["obj"].output(allow_partial=False) - oo, _ = self.components[pid]["obj"].output(allow_partial=False) - self.components[pid]["obj"].set(pd.concat([oo, o], ignore_index=True)) - downstream = [pid] - - for m in prepare2run(downstream): - yield {"content": m, "running_status": True} - - while 0 <= ran < len(self.path[-1]): - logging.debug(f"Canvas.run: {ran} {self.path}") - cpn_id = self.path[-1][ran] - cpn = self.get_component(cpn_id) - if not any([cpn["downstream"], cpn.get("parent_id"), waiting]): - break - - loop = self._find_loop() - if loop: - raise OverflowError(f"Too much loops: {loop}") - - if cpn["obj"].component_name.lower() in ["switch", "categorize", "relevant"]: - switch_out = cpn["obj"].output()[1].iloc[0, 0] - assert switch_out in self.components, \ - "{}'s output: {} not valid.".format(cpn_id, switch_out) - for m in prepare2run([switch_out]): - yield {"content": m, "running_status": True} - continue - - downstream = cpn["downstream"] - if not downstream and cpn.get("parent_id"): - pid = cpn["parent_id"] - _, o = cpn["obj"].output(allow_partial=False) - _, oo = self.components[pid]["obj"].output(allow_partial=False) - self.components[pid]["obj"].set_output(pd.concat([oo.dropna(axis=1), o.dropna(axis=1)], ignore_index=True)) - downstream = [pid] - - for m in prepare2run(downstream): - yield {"content": m, "running_status": True} - - if ran >= len(self.path[-1]) and waiting: - without_dependent_checking = waiting - waiting = [] - for m in prepare2run(without_dependent_checking): - yield {"content": m, "running_status": True} - without_dependent_checking = [] - ran -= 1 - - if self.answer: - cpn_id = self.answer[0] - self.answer.pop(0) - ans = self.components[cpn_id]["obj"].run(self.history, **kwargs) - self.path[-1].append(cpn_id) - if kwargs.get("stream"): - assert isinstance(ans, partial) - for an in ans(): - yield an - else: - yield ans - - else: - raise Exception("The dialog flow has no way to interact with you. Please add an 'Interact' component to the end of the flow.") - - def get_component(self, cpn_id): - return self.components[cpn_id] - - def get_tenant_id(self): - return self._tenant_id - - def get_history(self, window_size): - convs = [] - for role, obj in self.history[window_size * -1:]: - if isinstance(obj, list) and obj and all([isinstance(o, dict) for o in obj]): - convs.append({"role": role, "content": '\n'.join([str(s.get("content", "")) for s in obj])}) - else: - convs.append({"role": role, "content": str(obj)}) - return convs - - def add_user_input(self, question): - self.history.append(("user", question)) - - def set_embedding_model(self, embed_id): - self._embed_id = embed_id - - def get_embedding_model(self): - return self._embed_id - - def _find_loop(self, max_loops=6): - path = self.path[-1][::-1] - if len(path) < 2: - return False - - for i in range(len(path)): - if path[i].lower().find("answer") == 0 or path[i].lower().find("iterationitem") == 0: - path = path[:i] - break - - if len(path) < 2: - return False - - for loc in range(2, len(path) // 2): - pat = ",".join(path[0:loc]) - path_str = ",".join(path) - if len(pat) >= len(path_str): - return False - loop = max_loops - while path_str.find(pat) == 0 and loop >= 0: - loop -= 1 - if len(pat)+1 >= len(path_str): - return False - path_str = path_str[len(pat)+1:] - if loop < 0: - pat = " => ".join([p.split(":")[0] for p in path[0:loc]]) - return pat + " => " + pat - - return False - - def get_prologue(self): - return self.components["begin"]["obj"]._param.prologue - - def set_global_param(self, **kwargs): - for k, v in kwargs.items(): - for q in self.components["begin"]["obj"]._param.query: - if k != q["key"]: - continue - q["value"] = v - - def get_preset_param(self): - return self.components["begin"]["obj"]._param.query - - def get_component_input_elements(self, cpnnm): - return self.components[cpnnm]["obj"].get_input_elements() \ No newline at end of file diff --git a/agent/component/__init__.py b/agent/component/__init__.py deleted file mode 100644 index da06c87..0000000 --- a/agent/component/__init__.py +++ /dev/null @@ -1,133 +0,0 @@ -# -# Copyright 2025 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import importlib -from .begin import Begin, BeginParam -from .generate import Generate, GenerateParam -from .retrieval import Retrieval, RetrievalParam -from .answer import Answer, AnswerParam -from .categorize import Categorize, CategorizeParam -from .switch import Switch, SwitchParam -from .relevant import Relevant, RelevantParam -from .message import Message, MessageParam -from .rewrite import RewriteQuestion, RewriteQuestionParam -from .keyword import KeywordExtract, KeywordExtractParam -from .concentrator import Concentrator, ConcentratorParam -from .baidu import Baidu, BaiduParam -from .duckduckgo import DuckDuckGo, DuckDuckGoParam -from .wikipedia import Wikipedia, WikipediaParam -from .pubmed import PubMed, PubMedParam -from .arxiv import ArXiv, ArXivParam -from .google import Google, GoogleParam -from .bing import Bing, BingParam -from .googlescholar import GoogleScholar, GoogleScholarParam -from .deepl import DeepL, DeepLParam -from .github import GitHub, GitHubParam -from .baidufanyi import BaiduFanyi, BaiduFanyiParam -from .qweather import QWeather, QWeatherParam -from .exesql import ExeSQL, ExeSQLParam -from .yahoofinance import YahooFinance, YahooFinanceParam -from .wencai import WenCai, WenCaiParam -from .jin10 import Jin10, Jin10Param -from .tushare import TuShare, TuShareParam -from .akshare import AkShare, AkShareParam -from .crawler import Crawler, CrawlerParam -from .invoke import Invoke, InvokeParam -from .template import Template, TemplateParam -from .email import Email, EmailParam -from .iteration import Iteration, IterationParam -from .iterationitem import IterationItem, IterationItemParam - - -def component_class(class_name): - m = importlib.import_module("agent.component") - c = getattr(m, class_name) - return c - - -__all__ = [ - "Begin", - "BeginParam", - "Generate", - "GenerateParam", - "Retrieval", - "RetrievalParam", - "Answer", - "AnswerParam", - "Categorize", - "CategorizeParam", - "Switch", - "SwitchParam", - "Relevant", - "RelevantParam", - "Message", - "MessageParam", - "RewriteQuestion", - "RewriteQuestionParam", - "KeywordExtract", - "KeywordExtractParam", - "Concentrator", - "ConcentratorParam", - "Baidu", - "BaiduParam", - "DuckDuckGo", - "DuckDuckGoParam", - "Wikipedia", - "WikipediaParam", - "PubMed", - "PubMedParam", - "ArXiv", - "ArXivParam", - "Google", - "GoogleParam", - "Bing", - "BingParam", - "GoogleScholar", - "GoogleScholarParam", - "DeepL", - "DeepLParam", - "GitHub", - "GitHubParam", - "BaiduFanyi", - "BaiduFanyiParam", - "QWeather", - "QWeatherParam", - "ExeSQL", - "ExeSQLParam", - "YahooFinance", - "YahooFinanceParam", - "WenCai", - "WenCaiParam", - "Jin10", - "Jin10Param", - "TuShare", - "TuShareParam", - "AkShare", - "AkShareParam", - "Crawler", - "CrawlerParam", - "Invoke", - "InvokeParam", - "Iteration", - "IterationParam", - "IterationItem", - "IterationItemParam", - "Template", - "TemplateParam", - "Email", - "EmailParam", - "component_class" -] diff --git a/agent/component/akshare.py b/agent/component/akshare.py deleted file mode 100644 index a3ce3eb..0000000 --- a/agent/component/akshare.py +++ /dev/null @@ -1,56 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -from abc import ABC -import pandas as pd -from agent.component.base import ComponentBase, ComponentParamBase - - -class AkShareParam(ComponentParamBase): - """ - Define the AkShare component parameters. - """ - - def __init__(self): - super().__init__() - self.top_n = 10 - - def check(self): - self.check_positive_integer(self.top_n, "Top N") - - -class AkShare(ComponentBase, ABC): - component_name = "AkShare" - - def _run(self, history, **kwargs): - import akshare as ak - ans = self.get_input() - ans = ",".join(ans["content"]) if "content" in ans else "" - if not ans: - return AkShare.be_output("") - - try: - ak_res = [] - stock_news_em_df = ak.stock_news_em(symbol=ans) - stock_news_em_df = stock_news_em_df.head(self._param.top_n) - ak_res = [{"content": '' + i["新闻标题"] + '\n 新闻内容: ' + i[ - "新闻内容"] + " \n发布时间:" + i["发布时间"] + " \n文章来源: " + i["文章来源"]} for index, i in stock_news_em_df.iterrows()] - except Exception as e: - return AkShare.be_output("**ERROR**: " + str(e)) - - if not ak_res: - return AkShare.be_output("") - - return pd.DataFrame(ak_res) diff --git a/agent/component/answer.py b/agent/component/answer.py deleted file mode 100644 index 67dcbc6..0000000 --- a/agent/component/answer.py +++ /dev/null @@ -1,89 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import random -from abc import ABC -from functools import partial -from typing import Tuple, Union - -import pandas as pd - -from agent.component.base import ComponentBase, ComponentParamBase - - -class AnswerParam(ComponentParamBase): - - """ - Define the Answer component parameters. - """ - def __init__(self): - super().__init__() - self.post_answers = [] - - def check(self): - return True - - -class Answer(ComponentBase, ABC): - component_name = "Answer" - - def _run(self, history, **kwargs): - if kwargs.get("stream"): - return partial(self.stream_output) - - ans = self.get_input() - if self._param.post_answers: - ans = pd.concat([ans, pd.DataFrame([{"content": random.choice(self._param.post_answers)}])], ignore_index=False) - return ans - - def stream_output(self): - res = None - if hasattr(self, "exception") and self.exception: - res = {"content": str(self.exception)} - self.exception = None - yield res - self.set_output(res) - return - - stream = self.get_stream_input() - if isinstance(stream, pd.DataFrame): - res = stream - answer = "" - for ii, row in stream.iterrows(): - answer += row.to_dict()["content"] - yield {"content": answer} - else: - for st in stream(): - res = st - yield st - if self._param.post_answers: - res["content"] += random.choice(self._param.post_answers) - yield res - - self.set_output(res) - - def set_exception(self, e): - self.exception = e - - def output(self, allow_partial=True) -> Tuple[str, Union[pd.DataFrame, partial]]: - if allow_partial: - return super.output() - - for r, c in self._canvas.history[::-1]: - if r == "user": - return self._param.output_var_name, pd.DataFrame([{"content": c}]) - - self._param.output_var_name, pd.DataFrame([]) - diff --git a/agent/component/arxiv.py b/agent/component/arxiv.py deleted file mode 100644 index a7df338..0000000 --- a/agent/component/arxiv.py +++ /dev/null @@ -1,68 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import logging -from abc import ABC -import arxiv -import pandas as pd -from agent.component.base import ComponentBase, ComponentParamBase - -class ArXivParam(ComponentParamBase): - """ - Define the ArXiv component parameters. - """ - - def __init__(self): - super().__init__() - self.top_n = 6 - self.sort_by = 'submittedDate' - - def check(self): - self.check_positive_integer(self.top_n, "Top N") - self.check_valid_value(self.sort_by, "ArXiv Search Sort_by", - ['submittedDate', 'lastUpdatedDate', 'relevance']) - - -class ArXiv(ComponentBase, ABC): - component_name = "ArXiv" - - def _run(self, history, **kwargs): - ans = self.get_input() - ans = " - ".join(ans["content"]) if "content" in ans else "" - if not ans: - return ArXiv.be_output("") - - try: - sort_choices = {"relevance": arxiv.SortCriterion.Relevance, - "lastUpdatedDate": arxiv.SortCriterion.LastUpdatedDate, - 'submittedDate': arxiv.SortCriterion.SubmittedDate} - arxiv_client = arxiv.Client() - search = arxiv.Search( - query=ans, - max_results=self._param.top_n, - sort_by=sort_choices[self._param.sort_by] - ) - arxiv_res = [ - {"content": 'Title: ' + i.title + '\nPdf_Url: \nSummary: ' + i.summary} for - i in list(arxiv_client.results(search))] - except Exception as e: - return ArXiv.be_output("**ERROR**: " + str(e)) - - if not arxiv_res: - return ArXiv.be_output("") - - df = pd.DataFrame(arxiv_res) - logging.debug(f"df: {str(df)}") - return df diff --git a/agent/component/baidu.py b/agent/component/baidu.py deleted file mode 100644 index daec9f0..0000000 --- a/agent/component/baidu.py +++ /dev/null @@ -1,67 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import logging -from abc import ABC -import pandas as pd -import requests -import re -from agent.component.base import ComponentBase, ComponentParamBase - - -class BaiduParam(ComponentParamBase): - """ - Define the Baidu component parameters. - """ - - def __init__(self): - super().__init__() - self.top_n = 10 - - def check(self): - self.check_positive_integer(self.top_n, "Top N") - - -class Baidu(ComponentBase, ABC): - component_name = "Baidu" - - def _run(self, history, **kwargs): - ans = self.get_input() - ans = " - ".join(ans["content"]) if "content" in ans else "" - if not ans: - return Baidu.be_output("") - - try: - url = 'http://www.baidu.com/s?wd=' + ans + '&rn=' + str(self._param.top_n) - headers = { - 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.104 Safari/537.36'} - response = requests.get(url=url, headers=headers) - - url_res = re.findall(r"'url': \\\"(.*?)\\\"}", response.text) - title_res = re.findall(r"'title': \\\"(.*?)\\\",\\n", response.text) - body_res = re.findall(r"\"contentText\":\"(.*?)\"", response.text) - baidu_res = [{"content": re.sub('|', '', '' + title + ' ' + body)} for - url, title, body in zip(url_res, title_res, body_res)] - del body_res, url_res, title_res - except Exception as e: - return Baidu.be_output("**ERROR**: " + str(e)) - - if not baidu_res: - return Baidu.be_output("") - - df = pd.DataFrame(baidu_res) - logging.debug(f"df: {str(df)}") - return df - diff --git a/agent/component/baidufanyi.py b/agent/component/baidufanyi.py deleted file mode 100644 index f6eada6..0000000 --- a/agent/component/baidufanyi.py +++ /dev/null @@ -1,96 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import random -from abc import ABC -import requests -from agent.component.base import ComponentBase, ComponentParamBase -from hashlib import md5 - - -class BaiduFanyiParam(ComponentParamBase): - """ - Define the BaiduFanyi component parameters. - """ - - def __init__(self): - super().__init__() - self.appid = "xxx" - self.secret_key = "xxx" - self.trans_type = 'translate' - self.parameters = [] - self.source_lang = 'auto' - self.target_lang = 'auto' - self.domain = 'finance' - - def check(self): - self.check_empty(self.appid, "BaiduFanyi APPID") - self.check_empty(self.secret_key, "BaiduFanyi Secret Key") - self.check_valid_value(self.trans_type, "Translate type", ['translate', 'fieldtranslate']) - self.check_valid_value(self.source_lang, "Source language", - ['auto', 'zh', 'en', 'yue', 'wyw', 'jp', 'kor', 'fra', 'spa', 'th', 'ara', 'ru', 'pt', - 'de', 'it', 'el', 'nl', 'pl', 'bul', 'est', 'dan', 'fin', 'cs', 'rom', 'slo', 'swe', - 'hu', 'cht', 'vie']) - self.check_valid_value(self.target_lang, "Target language", - ['auto', 'zh', 'en', 'yue', 'wyw', 'jp', 'kor', 'fra', 'spa', 'th', 'ara', 'ru', 'pt', - 'de', 'it', 'el', 'nl', 'pl', 'bul', 'est', 'dan', 'fin', 'cs', 'rom', 'slo', 'swe', - 'hu', 'cht', 'vie']) - self.check_valid_value(self.domain, "Translate field", - ['it', 'finance', 'machinery', 'senimed', 'novel', 'academic', 'aerospace', 'wiki', - 'news', 'law', 'contract']) - - -class BaiduFanyi(ComponentBase, ABC): - component_name = "BaiduFanyi" - - def _run(self, history, **kwargs): - - ans = self.get_input() - ans = " - ".join(ans["content"]) if "content" in ans else "" - if not ans: - return BaiduFanyi.be_output("") - - try: - source_lang = self._param.source_lang - target_lang = self._param.target_lang - appid = self._param.appid - salt = random.randint(32768, 65536) - secret_key = self._param.secret_key - - if self._param.trans_type == 'translate': - sign = md5((appid + ans + salt + secret_key).encode('utf-8')).hexdigest() - url = 'http://api.fanyi.baidu.com/api/trans/vip/translate?' + 'q=' + ans + '&from=' + source_lang + '&to=' + target_lang + '&appid=' + appid + '&salt=' + salt + '&sign=' + sign - headers = {"Content-Type": "application/x-www-form-urlencoded"} - response = requests.post(url=url, headers=headers).json() - - if response.get('error_code'): - BaiduFanyi.be_output("**Error**:" + response['error_msg']) - - return BaiduFanyi.be_output(response['trans_result'][0]['dst']) - elif self._param.trans_type == 'fieldtranslate': - domain = self._param.domain - sign = md5((appid + ans + salt + domain + secret_key).encode('utf-8')).hexdigest() - 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 - headers = {"Content-Type": "application/x-www-form-urlencoded"} - response = requests.post(url=url, headers=headers).json() - - if response.get('error_code'): - BaiduFanyi.be_output("**Error**:" + response['error_msg']) - - return BaiduFanyi.be_output(response['trans_result'][0]['dst']) - - except Exception as e: - BaiduFanyi.be_output("**Error**:" + str(e)) - diff --git a/agent/component/base.py b/agent/component/base.py deleted file mode 100644 index 3f9d4ad..0000000 --- a/agent/component/base.py +++ /dev/null @@ -1,586 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -from abc import ABC -import builtins -import json -import os -import logging -from functools import partial -from typing import Tuple, Union - -import pandas as pd - -from agent import settings - -_FEEDED_DEPRECATED_PARAMS = "_feeded_deprecated_params" -_DEPRECATED_PARAMS = "_deprecated_params" -_USER_FEEDED_PARAMS = "_user_feeded_params" -_IS_RAW_CONF = "_is_raw_conf" - - -class ComponentParamBase(ABC): - def __init__(self): - self.output_var_name = "output" - self.message_history_window_size = 22 - self.query = [] - self.inputs = [] - self.debug_inputs = [] - - def set_name(self, name: str): - self._name = name - return self - - def check(self): - raise NotImplementedError("Parameter Object should be checked.") - - @classmethod - def _get_or_init_deprecated_params_set(cls): - if not hasattr(cls, _DEPRECATED_PARAMS): - setattr(cls, _DEPRECATED_PARAMS, set()) - return getattr(cls, _DEPRECATED_PARAMS) - - def _get_or_init_feeded_deprecated_params_set(self, conf=None): - if not hasattr(self, _FEEDED_DEPRECATED_PARAMS): - if conf is None: - setattr(self, _FEEDED_DEPRECATED_PARAMS, set()) - else: - setattr( - self, - _FEEDED_DEPRECATED_PARAMS, - set(conf[_FEEDED_DEPRECATED_PARAMS]), - ) - return getattr(self, _FEEDED_DEPRECATED_PARAMS) - - def _get_or_init_user_feeded_params_set(self, conf=None): - if not hasattr(self, _USER_FEEDED_PARAMS): - if conf is None: - setattr(self, _USER_FEEDED_PARAMS, set()) - else: - setattr(self, _USER_FEEDED_PARAMS, set(conf[_USER_FEEDED_PARAMS])) - return getattr(self, _USER_FEEDED_PARAMS) - - def get_user_feeded(self): - return self._get_or_init_user_feeded_params_set() - - def get_feeded_deprecated_params(self): - return self._get_or_init_feeded_deprecated_params_set() - - @property - def _deprecated_params_set(self): - return {name: True for name in self.get_feeded_deprecated_params()} - - def __str__(self): - return json.dumps(self.as_dict(), ensure_ascii=False) - - def as_dict(self): - def _recursive_convert_obj_to_dict(obj): - ret_dict = {} - for attr_name in list(obj.__dict__): - if attr_name in [_FEEDED_DEPRECATED_PARAMS, _DEPRECATED_PARAMS, _USER_FEEDED_PARAMS, _IS_RAW_CONF]: - continue - # get attr - attr = getattr(obj, attr_name) - if isinstance(attr, pd.DataFrame): - ret_dict[attr_name] = attr.to_dict() - continue - if attr and type(attr).__name__ not in dir(builtins): - ret_dict[attr_name] = _recursive_convert_obj_to_dict(attr) - else: - ret_dict[attr_name] = attr - - return ret_dict - - return _recursive_convert_obj_to_dict(self) - - def update(self, conf, allow_redundant=False): - update_from_raw_conf = conf.get(_IS_RAW_CONF, True) - if update_from_raw_conf: - deprecated_params_set = self._get_or_init_deprecated_params_set() - feeded_deprecated_params_set = ( - self._get_or_init_feeded_deprecated_params_set() - ) - user_feeded_params_set = self._get_or_init_user_feeded_params_set() - setattr(self, _IS_RAW_CONF, False) - else: - feeded_deprecated_params_set = ( - self._get_or_init_feeded_deprecated_params_set(conf) - ) - user_feeded_params_set = self._get_or_init_user_feeded_params_set(conf) - - def _recursive_update_param(param, config, depth, prefix): - if depth > settings.PARAM_MAXDEPTH: - raise ValueError("Param define nesting too deep!!!, can not parse it") - - inst_variables = param.__dict__ - redundant_attrs = [] - for config_key, config_value in config.items(): - # redundant attr - if config_key not in inst_variables: - if not update_from_raw_conf and config_key.startswith("_"): - setattr(param, config_key, config_value) - else: - setattr(param, config_key, config_value) - # redundant_attrs.append(config_key) - continue - - full_config_key = f"{prefix}{config_key}" - - if update_from_raw_conf: - # add user feeded params - user_feeded_params_set.add(full_config_key) - - # update user feeded deprecated param set - if full_config_key in deprecated_params_set: - feeded_deprecated_params_set.add(full_config_key) - - # supported attr - attr = getattr(param, config_key) - if type(attr).__name__ in dir(builtins) or attr is None: - setattr(param, config_key, config_value) - - else: - # recursive set obj attr - sub_params = _recursive_update_param( - attr, config_value, depth + 1, prefix=f"{prefix}{config_key}." - ) - setattr(param, config_key, sub_params) - - if not allow_redundant and redundant_attrs: - raise ValueError( - f"cpn `{getattr(self, '_name', type(self))}` has redundant parameters: `{[redundant_attrs]}`" - ) - - return param - - return _recursive_update_param(param=self, config=conf, depth=0, prefix="") - - def extract_not_builtin(self): - def _get_not_builtin_types(obj): - ret_dict = {} - for variable in obj.__dict__: - attr = getattr(obj, variable) - if attr and type(attr).__name__ not in dir(builtins): - ret_dict[variable] = _get_not_builtin_types(attr) - - return ret_dict - - return _get_not_builtin_types(self) - - def validate(self): - self.builtin_types = dir(builtins) - self.func = { - "ge": self._greater_equal_than, - "le": self._less_equal_than, - "in": self._in, - "not_in": self._not_in, - "range": self._range, - } - home_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) - param_validation_path_prefix = home_dir + "/param_validation/" - - param_name = type(self).__name__ - param_validation_path = "/".join( - [param_validation_path_prefix, param_name + ".json"] - ) - - validation_json = None - - try: - with open(param_validation_path, "r") as fin: - validation_json = json.loads(fin.read()) - except BaseException: - return - - self._validate_param(self, validation_json) - - def _validate_param(self, param_obj, validation_json): - default_section = type(param_obj).__name__ - var_list = param_obj.__dict__ - - for variable in var_list: - attr = getattr(param_obj, variable) - - if type(attr).__name__ in self.builtin_types or attr is None: - if variable not in validation_json: - continue - - validation_dict = validation_json[default_section][variable] - value = getattr(param_obj, variable) - value_legal = False - - for op_type in validation_dict: - if self.func[op_type](value, validation_dict[op_type]): - value_legal = True - break - - if not value_legal: - raise ValueError( - "Plase check runtime conf, {} = {} does not match user-parameter restriction".format( - variable, value - ) - ) - - elif variable in validation_json: - self._validate_param(attr, validation_json) - - @staticmethod - def check_string(param, descr): - if type(param).__name__ not in ["str"]: - raise ValueError( - descr + " {} not supported, should be string type".format(param) - ) - - @staticmethod - def check_empty(param, descr): - if not param: - raise ValueError( - descr + " does not support empty value." - ) - - @staticmethod - def check_positive_integer(param, descr): - if type(param).__name__ not in ["int", "long"] or param <= 0: - raise ValueError( - descr + " {} not supported, should be positive integer".format(param) - ) - - @staticmethod - def check_positive_number(param, descr): - if type(param).__name__ not in ["float", "int", "long"] or param <= 0: - raise ValueError( - descr + " {} not supported, should be positive numeric".format(param) - ) - - @staticmethod - def check_nonnegative_number(param, descr): - if type(param).__name__ not in ["float", "int", "long"] or param < 0: - raise ValueError( - descr - + " {} not supported, should be non-negative numeric".format(param) - ) - - @staticmethod - def check_decimal_float(param, descr): - if type(param).__name__ not in ["float", "int"] or param < 0 or param > 1: - raise ValueError( - descr - + " {} not supported, should be a float number in range [0, 1]".format( - param - ) - ) - - @staticmethod - def check_boolean(param, descr): - if type(param).__name__ != "bool": - raise ValueError( - descr + " {} not supported, should be bool type".format(param) - ) - - @staticmethod - def check_open_unit_interval(param, descr): - if type(param).__name__ not in ["float"] or param <= 0 or param >= 1: - raise ValueError( - descr + " should be a numeric number between 0 and 1 exclusively" - ) - - @staticmethod - def check_valid_value(param, descr, valid_values): - if param not in valid_values: - raise ValueError( - descr - + " {} is not supported, it should be in {}".format(param, valid_values) - ) - - @staticmethod - def check_defined_type(param, descr, types): - if type(param).__name__ not in types: - raise ValueError( - descr + " {} not supported, should be one of {}".format(param, types) - ) - - @staticmethod - def check_and_change_lower(param, valid_list, descr=""): - if type(param).__name__ != "str": - raise ValueError( - descr - + " {} not supported, should be one of {}".format(param, valid_list) - ) - - lower_param = param.lower() - if lower_param in valid_list: - return lower_param - else: - raise ValueError( - descr - + " {} not supported, should be one of {}".format(param, valid_list) - ) - - @staticmethod - def _greater_equal_than(value, limit): - return value >= limit - settings.FLOAT_ZERO - - @staticmethod - def _less_equal_than(value, limit): - return value <= limit + settings.FLOAT_ZERO - - @staticmethod - def _range(value, ranges): - in_range = False - for left_limit, right_limit in ranges: - if ( - left_limit - settings.FLOAT_ZERO - <= value - <= right_limit + settings.FLOAT_ZERO - ): - in_range = True - break - - return in_range - - @staticmethod - def _in(value, right_value_list): - return value in right_value_list - - @staticmethod - def _not_in(value, wrong_value_list): - return value not in wrong_value_list - - def _warn_deprecated_param(self, param_name, descr): - if self._deprecated_params_set.get(param_name): - logging.warning( - f"{descr} {param_name} is deprecated and ignored in this version." - ) - - def _warn_to_deprecate_param(self, param_name, descr, new_param): - if self._deprecated_params_set.get(param_name): - logging.warning( - f"{descr} {param_name} will be deprecated in future release; " - f"please use {new_param} instead." - ) - return True - return False - - -class ComponentBase(ABC): - component_name: str - - def __str__(self): - """ - { - "component_name": "Begin", - "params": {} - } - """ - return """{{ - "component_name": "{}", - "params": {}, - "output": {}, - "inputs": {} - }}""".format(self.component_name, - self._param, - json.dumps(json.loads(str(self._param)).get("output", {}), ensure_ascii=False), - json.dumps(json.loads(str(self._param)).get("inputs", []), ensure_ascii=False) - ) - - def __init__(self, canvas, id, param: ComponentParamBase): - self._canvas = canvas - self._id = id - self._param = param - self._param.check() - - def get_dependent_components(self): - cpnts = set([para["component_id"].split("@")[0] for para in self._param.query \ - if para.get("component_id") \ - and para["component_id"].lower().find("answer") < 0 \ - and para["component_id"].lower().find("begin") < 0]) - return list(cpnts) - - def run(self, history, **kwargs): - logging.debug("{}, history: {}, kwargs: {}".format(self, json.dumps(history, ensure_ascii=False), - json.dumps(kwargs, ensure_ascii=False))) - self._param.debug_inputs = [] - try: - res = self._run(history, **kwargs) - self.set_output(res) - except Exception as e: - self.set_output(pd.DataFrame([{"content": str(e)}])) - raise e - - return res - - def _run(self, history, **kwargs): - raise NotImplementedError() - - def output(self, allow_partial=True) -> Tuple[str, Union[pd.DataFrame, partial]]: - o = getattr(self._param, self._param.output_var_name) - if not isinstance(o, partial): - if not isinstance(o, pd.DataFrame): - if isinstance(o, list): - return self._param.output_var_name, pd.DataFrame(o) - if o is None: - return self._param.output_var_name, pd.DataFrame() - return self._param.output_var_name, pd.DataFrame([{"content": str(o)}]) - return self._param.output_var_name, o - - if allow_partial or not isinstance(o, partial): - if not isinstance(o, partial) and not isinstance(o, pd.DataFrame): - return pd.DataFrame(o if isinstance(o, list) else [o]) - return self._param.output_var_name, o - - outs = None - for oo in o(): - if not isinstance(oo, pd.DataFrame): - outs = pd.DataFrame(oo if isinstance(oo, list) else [oo]) - else: - outs = oo - return self._param.output_var_name, outs - - def reset(self): - setattr(self._param, self._param.output_var_name, None) - self._param.inputs = [] - - def set_output(self, v): - setattr(self._param, self._param.output_var_name, v) - - def get_input(self): - if self._param.debug_inputs: - return pd.DataFrame([{"content": v["value"]} for v in self._param.debug_inputs if v.get("value")]) - - reversed_cpnts = [] - if len(self._canvas.path) > 1: - reversed_cpnts.extend(self._canvas.path[-2]) - reversed_cpnts.extend(self._canvas.path[-1]) - - if self._param.query: - self._param.inputs = [] - outs = [] - for q in self._param.query: - if q.get("component_id"): - if q["component_id"].split("@")[0].lower().find("begin") >= 0: - cpn_id, key = q["component_id"].split("@") - for p in self._canvas.get_component(cpn_id)["obj"]._param.query: - if p["key"] == key: - outs.append(pd.DataFrame([{"content": p.get("value", "")}])) - self._param.inputs.append({"component_id": q["component_id"], - "content": p.get("value", "")}) - break - else: - assert False, f"Can't find parameter '{key}' for {cpn_id}" - continue - - if q["component_id"].lower().find("answer") == 0: - txt = [] - for r, c in self._canvas.history[::-1][:self._param.message_history_window_size][::-1]: - txt.append(f"{r.upper()}: {c}") - txt = "\n".join(txt) - self._param.inputs.append({"content": txt, "component_id": q["component_id"]}) - outs.append(pd.DataFrame([{"content": txt}])) - continue - - outs.append(self._canvas.get_component(q["component_id"])["obj"].output(allow_partial=False)[1]) - self._param.inputs.append({"component_id": q["component_id"], - "content": "\n".join( - [str(d["content"]) for d in outs[-1].to_dict('records')])}) - elif q.get("value"): - self._param.inputs.append({"component_id": None, "content": q["value"]}) - outs.append(pd.DataFrame([{"content": q["value"]}])) - if outs: - df = pd.concat(outs, ignore_index=True) - if "content" in df: - df = df.drop_duplicates(subset=['content']).reset_index(drop=True) - return df - - upstream_outs = [] - - for u in reversed_cpnts[::-1]: - if self.get_component_name(u) in ["switch", "concentrator"]: - continue - if self.component_name.lower() == "generate" and self.get_component_name(u) == "retrieval": - o = self._canvas.get_component(u)["obj"].output(allow_partial=False)[1] - if o is not None: - o["component_id"] = u - upstream_outs.append(o) - continue - #if self.component_name.lower()!="answer" and u not in self._canvas.get_component(self._id)["upstream"]: continue - if self.component_name.lower().find("switch") < 0 \ - and self.get_component_name(u) in ["relevant", "categorize"]: - continue - if u.lower().find("answer") >= 0: - for r, c in self._canvas.history[::-1]: - if r == "user": - upstream_outs.append(pd.DataFrame([{"content": c, "component_id": u}])) - break - break - if self.component_name.lower().find("answer") >= 0 and self.get_component_name(u) in ["relevant"]: - continue - o = self._canvas.get_component(u)["obj"].output(allow_partial=False)[1] - if o is not None: - o["component_id"] = u - upstream_outs.append(o) - break - - assert upstream_outs, "Can't inference the where the component input is. Please identify whose output is this component's input." - - df = pd.concat(upstream_outs, ignore_index=True) - if "content" in df: - df = df.drop_duplicates(subset=['content']).reset_index(drop=True) - - self._param.inputs = [] - for _, r in df.iterrows(): - self._param.inputs.append({"component_id": r["component_id"], "content": r["content"]}) - - return df - - def get_input_elements(self): - assert self._param.query, "Please identify input parameters firstly." - eles = [] - for q in self._param.query: - if q.get("component_id"): - cpn_id = q["component_id"] - if cpn_id.split("@")[0].lower().find("begin") >= 0: - cpn_id, key = cpn_id.split("@") - eles.extend(self._canvas.get_component(cpn_id)["obj"]._param.query) - continue - - eles.append({"name": self._canvas.get_component_name(cpn_id), "key": cpn_id}) - else: - eles.append({"key": q["value"], "name": q["value"], "value": q["value"]}) - return eles - - def get_stream_input(self): - reversed_cpnts = [] - if len(self._canvas.path) > 1: - reversed_cpnts.extend(self._canvas.path[-2]) - reversed_cpnts.extend(self._canvas.path[-1]) - - for u in reversed_cpnts[::-1]: - if self.get_component_name(u) in ["switch", "answer"]: - continue - return self._canvas.get_component(u)["obj"].output()[1] - - @staticmethod - def be_output(v): - return pd.DataFrame([{"content": v}]) - - def get_component_name(self, cpn_id): - return self._canvas.get_component(cpn_id)["obj"].component_name.lower() - - def debug(self, **kwargs): - return self._run([], **kwargs) - - def get_parent(self): - pid = self._canvas.get_component(self._id)["parent_id"] - return self._canvas.get_component(pid)["obj"] diff --git a/agent/component/begin.py b/agent/component/begin.py deleted file mode 100644 index f09cc23..0000000 --- a/agent/component/begin.py +++ /dev/null @@ -1,49 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -from functools import partial -import pandas as pd -from agent.component.base import ComponentBase, ComponentParamBase - - -class BeginParam(ComponentParamBase): - - """ - Define the Begin component parameters. - """ - def __init__(self): - super().__init__() - self.prologue = "Hi! I'm your smart assistant. What can I do for you?" - self.query = [] - - def check(self): - return True - - -class Begin(ComponentBase): - component_name = "Begin" - - def _run(self, history, **kwargs): - if kwargs.get("stream"): - return partial(self.stream_output) - return pd.DataFrame([{"content": self._param.prologue}]) - - def stream_output(self): - res = {"content": self._param.prologue} - yield res - self.set_output(self.be_output(res)) - - - diff --git a/agent/component/bing.py b/agent/component/bing.py deleted file mode 100644 index 6ec97c1..0000000 --- a/agent/component/bing.py +++ /dev/null @@ -1,84 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import logging -from abc import ABC -import requests -import pandas as pd -from agent.component.base import ComponentBase, ComponentParamBase - -class BingParam(ComponentParamBase): - """ - Define the Bing component parameters. - """ - - def __init__(self): - super().__init__() - self.top_n = 10 - self.channel = "Webpages" - self.api_key = "YOUR_ACCESS_KEY" - self.country = "CN" - self.language = "en" - - def check(self): - self.check_positive_integer(self.top_n, "Top N") - self.check_valid_value(self.channel, "Bing Web Search or Bing News", ["Webpages", "News"]) - self.check_empty(self.api_key, "Bing subscription key") - self.check_valid_value(self.country, "Bing Country", - ['AR', 'AU', 'AT', 'BE', 'BR', 'CA', 'CL', 'DK', 'FI', 'FR', 'DE', 'HK', 'IN', 'ID', - 'IT', 'JP', 'KR', 'MY', 'MX', 'NL', 'NZ', 'NO', 'CN', 'PL', 'PT', 'PH', 'RU', 'SA', - 'ZA', 'ES', 'SE', 'CH', 'TW', 'TR', 'GB', 'US']) - self.check_valid_value(self.language, "Bing Languages", - ['ar', 'eu', 'bn', 'bg', 'ca', 'ns', 'nt', 'hr', 'cs', 'da', 'nl', 'en', 'gb', 'et', - 'fi', 'fr', 'gl', 'de', 'gu', 'he', 'hi', 'hu', 'is', 'it', 'jp', 'kn', 'ko', 'lv', - 'lt', 'ms', 'ml', 'mr', 'nb', 'pl', 'br', 'pt', 'pa', 'ro', 'ru', 'sr', 'sk', 'sl', - 'es', 'sv', 'ta', 'te', 'th', 'tr', 'uk', 'vi']) - - -class Bing(ComponentBase, ABC): - component_name = "Bing" - - def _run(self, history, **kwargs): - ans = self.get_input() - ans = " - ".join(ans["content"]) if "content" in ans else "" - if not ans: - return Bing.be_output("") - - try: - headers = {"Ocp-Apim-Subscription-Key": self._param.api_key, 'Accept-Language': self._param.language} - params = {"q": ans, "textDecorations": True, "textFormat": "HTML", "cc": self._param.country, - "answerCount": 1, "promote": self._param.channel} - if self._param.channel == "Webpages": - response = requests.get("https://api.bing.microsoft.com/v7.0/search", headers=headers, params=params) - response.raise_for_status() - search_results = response.json() - bing_res = [{"content": '' + i["name"] + ' ' + i["snippet"]} for i in - search_results["webPages"]["value"]] - elif self._param.channel == "News": - response = requests.get("https://api.bing.microsoft.com/v7.0/news/search", headers=headers, - params=params) - response.raise_for_status() - search_results = response.json() - bing_res = [{"content": '' + i["name"] + ' ' + i["description"]} for i - in search_results['news']['value']] - except Exception as e: - return Bing.be_output("**ERROR**: " + str(e)) - - if not bing_res: - return Bing.be_output("") - - df = pd.DataFrame(bing_res) - logging.debug(f"df: {str(df)}") - return df diff --git a/agent/component/categorize.py b/agent/component/categorize.py deleted file mode 100644 index 0d627db..0000000 --- a/agent/component/categorize.py +++ /dev/null @@ -1,98 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import logging -from abc import ABC -from api.db import LLMType -from api.db.services.llm_service import LLMBundle -from agent.component import GenerateParam, Generate - - -class CategorizeParam(GenerateParam): - - """ - Define the Categorize component parameters. - """ - def __init__(self): - super().__init__() - self.category_description = {} - self.prompt = "" - - def check(self): - super().check() - self.check_empty(self.category_description, "[Categorize] Category examples") - for k, v in self.category_description.items(): - if not k: - raise ValueError("[Categorize] Category name can not be empty!") - if not v.get("to"): - raise ValueError(f"[Categorize] 'To' of category {k} can not be empty!") - - def get_prompt(self, chat_hist): - cate_lines = [] - for c, desc in self.category_description.items(): - for line in desc.get("examples", "").split("\n"): - if not line: - continue - cate_lines.append("USER: {}\nCategory: {}".format(line, c)) - descriptions = [] - for c, desc in self.category_description.items(): - if desc.get("description"): - descriptions.append( - "--------------------\nCategory: {}\nDescription: {}\n".format(c, desc["description"])) - - self.prompt = """ - You're a text classifier. You need to categorize the user’s questions into {} categories, - namely: {} - Here's description of each category: - {} - - You could learn from the following examples: - {} - You could learn from the above examples. - Just mention the category names, no need for any additional words. - - ---- Real Data ---- - {} - """.format( - len(self.category_description.keys()), - "/".join(list(self.category_description.keys())), - "\n".join(descriptions), - "- ".join(cate_lines), - chat_hist - ) - return self.prompt - - -class Categorize(Generate, ABC): - component_name = "Categorize" - - def _run(self, history, **kwargs): - input = self.get_input() - input = " - ".join(input["content"]) if "content" in input else "" - chat_mdl = LLMBundle(self._canvas.get_tenant_id(), LLMType.CHAT, self._param.llm_id) - ans = chat_mdl.chat(self._param.get_prompt(input), [{"role": "user", "content": "\nCategory: "}], - self._param.gen_conf()) - logging.debug(f"input: {input}, answer: {str(ans)}") - for c in self._param.category_description.keys(): - if ans.lower().find(c.lower()) >= 0: - return Categorize.be_output(self._param.category_description[c]["to"]) - - return Categorize.be_output(list(self._param.category_description.items())[-1][1]["to"]) - - def debug(self, **kwargs): - df = self._run([], **kwargs) - cpn_id = df.iloc[0, 0] - return Categorize.be_output(self._canvas.get_component_name(cpn_id)) - diff --git a/agent/component/concentrator.py b/agent/component/concentrator.py deleted file mode 100644 index efb9dd8..0000000 --- a/agent/component/concentrator.py +++ /dev/null @@ -1,36 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -from abc import ABC -from agent.component.base import ComponentBase, ComponentParamBase - - -class ConcentratorParam(ComponentParamBase): - """ - Define the Concentrator component parameters. - """ - - def __init__(self): - super().__init__() - - def check(self): - return True - - -class Concentrator(ComponentBase, ABC): - component_name = "Concentrator" - - def _run(self, history, **kwargs): - return Concentrator.be_output("") \ No newline at end of file diff --git a/agent/component/crawler.py b/agent/component/crawler.py deleted file mode 100644 index d8c5381..0000000 --- a/agent/component/crawler.py +++ /dev/null @@ -1,67 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -from abc import ABC -import asyncio -from crawl4ai import AsyncWebCrawler -from agent.component.base import ComponentBase, ComponentParamBase -from api.utils.web_utils import is_valid_url - - -class CrawlerParam(ComponentParamBase): - """ - Define the Crawler component parameters. - """ - - def __init__(self): - super().__init__() - self.proxy = None - self.extract_type = "markdown" - - def check(self): - self.check_valid_value(self.extract_type, "Type of content from the crawler", ['html', 'markdown', 'content']) - - -class Crawler(ComponentBase, ABC): - component_name = "Crawler" - - def _run(self, history, **kwargs): - ans = self.get_input() - ans = " - ".join(ans["content"]) if "content" in ans else "" - if not is_valid_url(ans): - return Crawler.be_output("URL not valid") - try: - result = asyncio.run(self.get_web(ans)) - - return Crawler.be_output(result) - - except Exception as e: - return Crawler.be_output(f"An unexpected error occurred: {str(e)}") - - async def get_web(self, url): - proxy = self._param.proxy if self._param.proxy else None - async with AsyncWebCrawler(verbose=True, proxy=proxy) as crawler: - result = await crawler.arun( - url=url, - bypass_cache=True - ) - - if self._param.extract_type == 'html': - return result.cleaned_html - elif self._param.extract_type == 'markdown': - return result.markdown - elif self._param.extract_type == 'content': - result.extracted_content - return result.markdown diff --git a/agent/component/deepl.py b/agent/component/deepl.py deleted file mode 100644 index 31e9272..0000000 --- a/agent/component/deepl.py +++ /dev/null @@ -1,61 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -from abc import ABC -from agent.component.base import ComponentBase, ComponentParamBase -import deepl - - -class DeepLParam(ComponentParamBase): - """ - Define the DeepL component parameters. - """ - - def __init__(self): - super().__init__() - self.auth_key = "xxx" - self.parameters = [] - self.source_lang = 'ZH' - self.target_lang = 'EN-GB' - - def check(self): - self.check_positive_integer(self.top_n, "Top N") - self.check_valid_value(self.source_lang, "Source language", - ['AR', 'BG', 'CS', 'DA', 'DE', 'EL', 'EN', 'ES', 'ET', 'FI', 'FR', 'HU', 'ID', 'IT', - 'JA', 'KO', 'LT', 'LV', 'NB', 'NL', 'PL', 'PT', 'RO', 'RU', 'SK', 'SL', 'SV', 'TR', - 'UK', 'ZH']) - self.check_valid_value(self.target_lang, "Target language", - ['AR', 'BG', 'CS', 'DA', 'DE', 'EL', 'EN-GB', 'EN-US', 'ES', 'ET', 'FI', 'FR', 'HU', - 'ID', 'IT', 'JA', 'KO', 'LT', 'LV', 'NB', 'NL', 'PL', 'PT-BR', 'PT-PT', 'RO', 'RU', - 'SK', 'SL', 'SV', 'TR', 'UK', 'ZH']) - - -class DeepL(ComponentBase, ABC): - component_name = "GitHub" - - def _run(self, history, **kwargs): - ans = self.get_input() - ans = " - ".join(ans["content"]) if "content" in ans else "" - if not ans: - return DeepL.be_output("") - - try: - translator = deepl.Translator(self._param.auth_key) - result = translator.translate_text(ans, source_lang=self._param.source_lang, - target_lang=self._param.target_lang) - - return DeepL.be_output(result.text) - except Exception as e: - DeepL.be_output("**Error**:" + str(e)) diff --git a/agent/component/duckduckgo.py b/agent/component/duckduckgo.py deleted file mode 100644 index 9f460a6..0000000 --- a/agent/component/duckduckgo.py +++ /dev/null @@ -1,66 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import logging -from abc import ABC -from duckduckgo_search import DDGS -import pandas as pd -from agent.component.base import ComponentBase, ComponentParamBase - - -class DuckDuckGoParam(ComponentParamBase): - """ - Define the DuckDuckGo component parameters. - """ - - def __init__(self): - super().__init__() - self.top_n = 10 - self.channel = "text" - - def check(self): - self.check_positive_integer(self.top_n, "Top N") - self.check_valid_value(self.channel, "Web Search or News", ["text", "news"]) - - -class DuckDuckGo(ComponentBase, ABC): - component_name = "DuckDuckGo" - - def _run(self, history, **kwargs): - ans = self.get_input() - ans = " - ".join(ans["content"]) if "content" in ans else "" - if not ans: - return DuckDuckGo.be_output("") - - try: - if self._param.channel == "text": - with DDGS() as ddgs: - # {'title': '', 'href': '', 'body': ''} - duck_res = [{"content": '' + i["title"] + ' ' + i["body"]} for i - in ddgs.text(ans, max_results=self._param.top_n)] - elif self._param.channel == "news": - with DDGS() as ddgs: - # {'date': '', 'title': '', 'body': '', 'url': '', 'image': '', 'source': ''} - duck_res = [{"content": '' + i["title"] + ' ' + i["body"]} for i - in ddgs.news(ans, max_results=self._param.top_n)] - except Exception as e: - return DuckDuckGo.be_output("**ERROR**: " + str(e)) - - if not duck_res: - return DuckDuckGo.be_output("") - - df = pd.DataFrame(duck_res) - logging.debug("df: {df}") - return df diff --git a/agent/component/email.py b/agent/component/email.py deleted file mode 100644 index ec9a15a..0000000 --- a/agent/component/email.py +++ /dev/null @@ -1,138 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -from abc import ABC -import json -import smtplib -import logging -from email.mime.text import MIMEText -from email.mime.multipart import MIMEMultipart -from email.header import Header -from email.utils import formataddr -from agent.component.base import ComponentBase, ComponentParamBase - -class EmailParam(ComponentParamBase): - """ - Define the Email component parameters. - """ - def __init__(self): - super().__init__() - # Fixed configuration parameters - self.smtp_server = "" # SMTP server address - self.smtp_port = 465 # SMTP port - self.email = "" # Sender email - self.password = "" # Email authorization code - self.sender_name = "" # Sender name - - def check(self): - # Check required parameters - self.check_empty(self.smtp_server, "SMTP Server") - self.check_empty(self.email, "Email") - self.check_empty(self.password, "Password") - self.check_empty(self.sender_name, "Sender Name") - -class Email(ComponentBase, ABC): - component_name = "Email" - - def _run(self, history, **kwargs): - # Get upstream component output and parse JSON - ans = self.get_input() - content = "".join(ans["content"]) if "content" in ans else "" - if not content: - return Email.be_output("No content to send") - - success = False - try: - # Parse JSON string passed from upstream - email_data = json.loads(content) - - # Validate required fields - if "to_email" not in email_data: - return Email.be_output("Missing required field: to_email") - - # Create email object - msg = MIMEMultipart('alternative') - - # Properly handle sender name encoding - msg['From'] = formataddr((str(Header(self._param.sender_name,'utf-8')), self._param.email)) - msg['To'] = email_data["to_email"] - if "cc_email" in email_data and email_data["cc_email"]: - msg['Cc'] = email_data["cc_email"] - msg['Subject'] = Header(email_data.get("subject", "No Subject"), 'utf-8').encode() - - # Use content from email_data or default content - email_content = email_data.get("content", "No content provided") - # msg.attach(MIMEText(email_content, 'plain', 'utf-8')) - msg.attach(MIMEText(email_content, 'html', 'utf-8')) - - # Connect to SMTP server and send - logging.info(f"Connecting to SMTP server {self._param.smtp_server}:{self._param.smtp_port}") - - context = smtplib.ssl.create_default_context() - with smtplib.SMTP_SSL(self._param.smtp_server, self._param.smtp_port, context=context) as server: - # Login - logging.info(f"Attempting to login with email: {self._param.email}") - server.login(self._param.email, self._param.password) - - # Get all recipient list - recipients = [email_data["to_email"]] - if "cc_email" in email_data and email_data["cc_email"]: - recipients.extend(email_data["cc_email"].split(',')) - - # Send email - logging.info(f"Sending email to recipients: {recipients}") - try: - server.send_message(msg, self._param.email, recipients) - success = True - except Exception as e: - logging.error(f"Error during send_message: {str(e)}") - # Try alternative method - server.sendmail(self._param.email, recipients, msg.as_string()) - success = True - - try: - server.quit() - except Exception as e: - # Ignore errors when closing connection - logging.warning(f"Non-fatal error during connection close: {str(e)}") - - if success: - return Email.be_output("Email sent successfully") - - except json.JSONDecodeError: - error_msg = "Invalid JSON format in input" - logging.error(error_msg) - return Email.be_output(error_msg) - - except smtplib.SMTPAuthenticationError: - error_msg = "SMTP Authentication failed. Please check your email and authorization code." - logging.error(error_msg) - return Email.be_output(f"Failed to send email: {error_msg}") - - except smtplib.SMTPConnectError: - error_msg = f"Failed to connect to SMTP server {self._param.smtp_server}:{self._param.smtp_port}" - logging.error(error_msg) - return Email.be_output(f"Failed to send email: {error_msg}") - - except smtplib.SMTPException as e: - error_msg = f"SMTP error occurred: {str(e)}" - logging.error(error_msg) - return Email.be_output(f"Failed to send email: {error_msg}") - - except Exception as e: - error_msg = f"Unexpected error: {str(e)}" - logging.error(error_msg) - return Email.be_output(f"Failed to send email: {error_msg}") \ No newline at end of file diff --git a/agent/component/exesql.py b/agent/component/exesql.py deleted file mode 100644 index d61239d..0000000 --- a/agent/component/exesql.py +++ /dev/null @@ -1,154 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -from abc import ABC -import re -from copy import deepcopy - -import pandas as pd -import pymysql -import psycopg2 -from agent.component import GenerateParam, Generate -import pyodbc -import logging - - -class ExeSQLParam(GenerateParam): - """ - Define the ExeSQL component parameters. - """ - - def __init__(self): - super().__init__() - self.db_type = "mysql" - self.database = "" - self.username = "" - self.host = "" - self.port = 3306 - self.password = "" - self.loop = 3 - self.top_n = 30 - - def check(self): - super().check() - self.check_valid_value(self.db_type, "Choose DB type", ['mysql', 'postgresql', 'mariadb', 'mssql']) - self.check_empty(self.database, "Database name") - self.check_empty(self.username, "database username") - self.check_empty(self.host, "IP Address") - self.check_positive_integer(self.port, "IP Port") - self.check_empty(self.password, "Database password") - self.check_positive_integer(self.top_n, "Number of records") - if self.database == "rag_flow": - if self.host == "ragflow-mysql": - raise ValueError("For the security reason, it dose not support database named rag_flow.") - if self.password == "infini_rag_flow": - raise ValueError("For the security reason, it dose not support database named rag_flow.") - - -class ExeSQL(Generate, ABC): - component_name = "ExeSQL" - - def _refactor(self, ans): - ans = re.sub(r".*", "", ans, flags=re.DOTALL) - match = re.search(r"```sql\s*(.*?)\s*```", ans, re.DOTALL) - if match: - ans = match.group(1) # Query content - return ans - else: - print("no markdown") - ans = re.sub(r'^.*?SELECT ', 'SELECT ', (ans), flags=re.IGNORECASE) - ans = re.sub(r';.*?SELECT ', '; SELECT ', ans, flags=re.IGNORECASE) - ans = re.sub(r';[^;]*$', r';', ans) - if not ans: - raise Exception("SQL statement not found!") - return ans - - def _run(self, history, **kwargs): - ans = self.get_input() - ans = "".join([str(a) for a in ans["content"]]) if "content" in ans else "" - ans = self._refactor(ans) - if self._param.db_type in ["mysql", "mariadb"]: - db = pymysql.connect(db=self._param.database, user=self._param.username, host=self._param.host, - port=self._param.port, password=self._param.password) - elif self._param.db_type == 'postgresql': - db = psycopg2.connect(dbname=self._param.database, user=self._param.username, host=self._param.host, - port=self._param.port, password=self._param.password) - elif self._param.db_type == 'mssql': - conn_str = ( - r'DRIVER={ODBC Driver 17 for SQL Server};' - r'SERVER=' + self._param.host + ',' + str(self._param.port) + ';' - r'DATABASE=' + self._param.database + ';' - r'UID=' + self._param.username + ';' - r'PWD=' + self._param.password - ) - db = pyodbc.connect(conn_str) - try: - cursor = db.cursor() - except Exception as e: - raise Exception("Database Connection Failed! \n" + str(e)) - if not hasattr(self, "_loop"): - setattr(self, "_loop", 0) - self._loop += 1 - input_list = re.split(r';', ans.replace(r"\n", " ")) - sql_res = [] - for i in range(len(input_list)): - single_sql = input_list[i] - while self._loop <= self._param.loop: - self._loop += 1 - if not single_sql: - break - try: - cursor.execute(single_sql) - if cursor.rowcount == 0: - sql_res.append({"content": "No record in the database!"}) - break - if self._param.db_type == 'mssql': - single_res = pd.DataFrame.from_records(cursor.fetchmany(self._param.top_n), - columns=[desc[0] for desc in cursor.description]) - else: - single_res = pd.DataFrame([i for i in cursor.fetchmany(self._param.top_n)]) - single_res.columns = [i[0] for i in cursor.description] - sql_res.append({"content": single_res.to_markdown(index=False, floatfmt=".6f")}) - break - except Exception as e: - single_sql = self._regenerate_sql(single_sql, str(e), **kwargs) - single_sql = self._refactor(single_sql) - if self._loop > self._param.loop: - sql_res.append({"content": "Can't query the correct data via SQL statement."}) - db.close() - if not sql_res: - return ExeSQL.be_output("") - return pd.DataFrame(sql_res) - - def _regenerate_sql(self, failed_sql, error_message, **kwargs): - prompt = f''' - ## You are the Repair SQL Statement Helper, please modify the original SQL statement based on the SQL query error report. - ## The original SQL statement is as follows:{failed_sql}. - ## The contents of the SQL query error report is as follows:{error_message}. - ## Answer only the modified SQL statement. Please do not give any explanation, just answer the code. -''' - self._param.prompt = prompt - kwargs_ = deepcopy(kwargs) - kwargs_["stream"] = False - response = Generate._run(self, [], **kwargs_) - try: - regenerated_sql = response.loc[0, "content"] - return regenerated_sql - except Exception as e: - logging.error(f"Failed to regenerate SQL: {e}") - return None - - def debug(self, **kwargs): - return self._run([], **kwargs) diff --git a/agent/component/generate.py b/agent/component/generate.py deleted file mode 100644 index 82963ea..0000000 --- a/agent/component/generate.py +++ /dev/null @@ -1,250 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import re -from functools import partial -import pandas as pd -from api.db import LLMType -from api.db.services.conversation_service import structure_answer -from api.db.services.llm_service import LLMBundle -from api import settings -from agent.component.base import ComponentBase, ComponentParamBase -from rag.prompts import message_fit_in - - -class GenerateParam(ComponentParamBase): - """ - Define the Generate component parameters. - """ - - def __init__(self): - super().__init__() - self.llm_id = "" - self.prompt = "" - self.max_tokens = 0 - self.temperature = 0 - self.top_p = 0 - self.presence_penalty = 0 - self.frequency_penalty = 0 - self.cite = True - self.parameters = [] - - def check(self): - self.check_decimal_float(self.temperature, "[Generate] Temperature") - self.check_decimal_float(self.presence_penalty, "[Generate] Presence penalty") - self.check_decimal_float(self.frequency_penalty, "[Generate] Frequency penalty") - self.check_nonnegative_number(self.max_tokens, "[Generate] Max tokens") - self.check_decimal_float(self.top_p, "[Generate] Top P") - self.check_empty(self.llm_id, "[Generate] LLM") - # self.check_defined_type(self.parameters, "Parameters", ["list"]) - - def gen_conf(self): - conf = {} - if self.max_tokens > 0: - conf["max_tokens"] = self.max_tokens - if self.temperature > 0: - conf["temperature"] = self.temperature - if self.top_p > 0: - conf["top_p"] = self.top_p - if self.presence_penalty > 0: - conf["presence_penalty"] = self.presence_penalty - if self.frequency_penalty > 0: - conf["frequency_penalty"] = self.frequency_penalty - return conf - - -class Generate(ComponentBase): - component_name = "Generate" - - def get_dependent_components(self): - inputs = self.get_input_elements() - cpnts = set([i["key"] for i in inputs[1:] if i["key"].lower().find("answer") < 0 and i["key"].lower().find("begin") < 0]) - return list(cpnts) - - def set_cite(self, retrieval_res, answer): - retrieval_res = retrieval_res.dropna(subset=["vector", "content_ltks"]).reset_index(drop=True) - if "empty_response" in retrieval_res.columns: - retrieval_res["empty_response"].fillna("", inplace=True) - answer, idx = settings.retrievaler.insert_citations(answer, - [ck["content_ltks"] for _, ck in retrieval_res.iterrows()], - [ck["vector"] for _, ck in retrieval_res.iterrows()], - LLMBundle(self._canvas.get_tenant_id(), LLMType.EMBEDDING, - self._canvas.get_embedding_model()), tkweight=0.7, - vtweight=0.3) - doc_ids = set([]) - recall_docs = [] - for i in idx: - did = retrieval_res.loc[int(i), "doc_id"] - if did in doc_ids: - continue - doc_ids.add(did) - recall_docs.append({"doc_id": did, "doc_name": retrieval_res.loc[int(i), "docnm_kwd"]}) - - del retrieval_res["vector"] - del retrieval_res["content_ltks"] - - reference = { - "chunks": [ck.to_dict() for _, ck in retrieval_res.iterrows()], - "doc_aggs": recall_docs - } - - if answer.lower().find("invalid key") >= 0 or answer.lower().find("invalid api") >= 0: - answer += " Please set LLM API-Key in 'User Setting -> Model providers -> API-Key'" - res = {"content": answer, "reference": reference} - res = structure_answer(None, res, "", "") - - return res - - def get_input_elements(self): - key_set = set([]) - res = [{"key": "user", "name": "Input your question here:"}] - for r in re.finditer(r"\{([a-z]+[:@][a-z0-9_-]+)\}", self._param.prompt, flags=re.IGNORECASE): - cpn_id = r.group(1) - if cpn_id in key_set: - continue - if cpn_id.lower().find("begin@") == 0: - cpn_id, key = cpn_id.split("@") - for p in self._canvas.get_component(cpn_id)["obj"]._param.query: - if p["key"] != key: - continue - res.append({"key": r.group(1), "name": p["name"]}) - key_set.add(r.group(1)) - continue - cpn_nm = self._canvas.get_component_name(cpn_id) - if not cpn_nm: - continue - res.append({"key": cpn_id, "name": cpn_nm}) - key_set.add(cpn_id) - return res - - def _run(self, history, **kwargs): - chat_mdl = LLMBundle(self._canvas.get_tenant_id(), LLMType.CHAT, self._param.llm_id) - prompt = self._param.prompt - - retrieval_res = [] - self._param.inputs = [] - for para in self.get_input_elements()[1:]: - if para["key"].lower().find("begin@") == 0: - cpn_id, key = para["key"].split("@") - for p in self._canvas.get_component(cpn_id)["obj"]._param.query: - if p["key"] == key: - kwargs[para["key"]] = p.get("value", "") - self._param.inputs.append( - {"component_id": para["key"], "content": kwargs[para["key"]]}) - break - else: - assert False, f"Can't find parameter '{key}' for {cpn_id}" - continue - - component_id = para["key"] - cpn = self._canvas.get_component(component_id)["obj"] - if cpn.component_name.lower() == "answer": - hist = self._canvas.get_history(1) - if hist: - hist = hist[0]["content"] - else: - hist = "" - kwargs[para["key"]] = hist - continue - _, out = cpn.output(allow_partial=False) - if "content" not in out.columns: - kwargs[para["key"]] = "" - else: - if cpn.component_name.lower() == "retrieval": - retrieval_res.append(out) - kwargs[para["key"]] = " - " + "\n - ".join([o if isinstance(o, str) else str(o) for o in out["content"]]) - self._param.inputs.append({"component_id": para["key"], "content": kwargs[para["key"]]}) - - if retrieval_res: - retrieval_res = pd.concat(retrieval_res, ignore_index=True) - else: - retrieval_res = pd.DataFrame([]) - - for n, v in kwargs.items(): - prompt = re.sub(r"\{%s\}" % re.escape(n), str(v).replace("\\", " "), prompt) - - if not self._param.inputs and prompt.find("{input}") >= 0: - retrieval_res = self.get_input() - input = (" - " + "\n - ".join( - [c for c in retrieval_res["content"] if isinstance(c, str)])) if "content" in retrieval_res else "" - prompt = re.sub(r"\{input\}", re.escape(input), prompt) - - downstreams = self._canvas.get_component(self._id)["downstream"] - if kwargs.get("stream") and len(downstreams) == 1 and self._canvas.get_component(downstreams[0])[ - "obj"].component_name.lower() == "answer": - return partial(self.stream_output, chat_mdl, prompt, retrieval_res) - - if "empty_response" in retrieval_res.columns and not "".join(retrieval_res["content"]): - empty_res = "\n- ".join([str(t) for t in retrieval_res["empty_response"] if str(t)]) - res = {"content": empty_res if empty_res else "Nothing found in knowledgebase!", "reference": []} - return pd.DataFrame([res]) - - msg = self._canvas.get_history(self._param.message_history_window_size) - if len(msg) < 1: - msg.append({"role": "user", "content": "Output: "}) - _, msg = message_fit_in([{"role": "system", "content": prompt}, *msg], int(chat_mdl.max_length * 0.97)) - if len(msg) < 2: - msg.append({"role": "user", "content": "Output: "}) - ans = chat_mdl.chat(msg[0]["content"], msg[1:], self._param.gen_conf()) - ans = re.sub(r".*", "", ans, flags=re.DOTALL) - - if self._param.cite and "content_ltks" in retrieval_res.columns and "vector" in retrieval_res.columns: - res = self.set_cite(retrieval_res, ans) - return pd.DataFrame([res]) - - return Generate.be_output(ans) - - def stream_output(self, chat_mdl, prompt, retrieval_res): - res = None - if "empty_response" in retrieval_res.columns and not "".join(retrieval_res["content"]): - empty_res = "\n- ".join([str(t) for t in retrieval_res["empty_response"] if str(t)]) - res = {"content": empty_res if empty_res else "Nothing found in knowledgebase!", "reference": []} - yield res - self.set_output(res) - return - - msg = self._canvas.get_history(self._param.message_history_window_size) - if msg and msg[0]['role'] == 'assistant': - msg.pop(0) - if len(msg) < 1: - msg.append({"role": "user", "content": "Output: "}) - _, msg = message_fit_in([{"role": "system", "content": prompt}, *msg], int(chat_mdl.max_length * 0.97)) - if len(msg) < 2: - msg.append({"role": "user", "content": "Output: "}) - answer = "" - for ans in chat_mdl.chat_streamly(msg[0]["content"], msg[1:], self._param.gen_conf()): - res = {"content": ans, "reference": []} - answer = ans - yield res - - if self._param.cite and "content_ltks" in retrieval_res.columns and "vector" in retrieval_res.columns: - res = self.set_cite(retrieval_res, answer) - yield res - - self.set_output(Generate.be_output(res)) - - def debug(self, **kwargs): - chat_mdl = LLMBundle(self._canvas.get_tenant_id(), LLMType.CHAT, self._param.llm_id) - prompt = self._param.prompt - - for para in self._param.debug_inputs: - kwargs[para["key"]] = para.get("value", "") - - for n, v in kwargs.items(): - prompt = re.sub(r"\{%s\}" % re.escape(n), str(v).replace("\\", " "), prompt) - - u = kwargs.get("user") - ans = chat_mdl.chat(prompt, [{"role": "user", "content": u if u else "Output: "}], self._param.gen_conf()) - return pd.DataFrame([ans]) diff --git a/agent/component/github.py b/agent/component/github.py deleted file mode 100644 index 4149da3..0000000 --- a/agent/component/github.py +++ /dev/null @@ -1,61 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import logging -from abc import ABC -import pandas as pd -import requests -from agent.component.base import ComponentBase, ComponentParamBase - - -class GitHubParam(ComponentParamBase): - """ - Define the GitHub component parameters. - """ - - def __init__(self): - super().__init__() - self.top_n = 10 - - def check(self): - self.check_positive_integer(self.top_n, "Top N") - - -class GitHub(ComponentBase, ABC): - component_name = "GitHub" - - def _run(self, history, **kwargs): - ans = self.get_input() - ans = " - ".join(ans["content"]) if "content" in ans else "" - if not ans: - return GitHub.be_output("") - - try: - url = 'https://api.github.com/search/repositories?q=' + ans + '&sort=stars&order=desc&per_page=' + str( - self._param.top_n) - headers = {"Content-Type": "application/vnd.github+json", "X-GitHub-Api-Version": '2022-11-28'} - response = requests.get(url=url, headers=headers).json() - - github_res = [{"content": '' + i["name"] + '' + str( - i["description"]) + '\n stars:' + str(i['watchers'])} for i in response['items']] - except Exception as e: - return GitHub.be_output("**ERROR**: " + str(e)) - - if not github_res: - return GitHub.be_output("") - - df = pd.DataFrame(github_res) - logging.debug(f"df: {df}") - return df diff --git a/agent/component/google.py b/agent/component/google.py deleted file mode 100644 index 691dc23..0000000 --- a/agent/component/google.py +++ /dev/null @@ -1,96 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import logging -from abc import ABC -from serpapi import GoogleSearch -import pandas as pd -from agent.component.base import ComponentBase, ComponentParamBase - - -class GoogleParam(ComponentParamBase): - """ - Define the Google component parameters. - """ - - def __init__(self): - super().__init__() - self.top_n = 10 - self.api_key = "xxx" - self.country = "cn" - self.language = "en" - - def check(self): - self.check_positive_integer(self.top_n, "Top N") - self.check_empty(self.api_key, "SerpApi API key") - self.check_valid_value(self.country, "Google Country", - ['af', 'al', 'dz', 'as', 'ad', 'ao', 'ai', 'aq', 'ag', 'ar', 'am', 'aw', 'au', 'at', - 'az', 'bs', 'bh', 'bd', 'bb', 'by', 'be', 'bz', 'bj', 'bm', 'bt', 'bo', 'ba', 'bw', - 'bv', 'br', 'io', 'bn', 'bg', 'bf', 'bi', 'kh', 'cm', 'ca', 'cv', 'ky', 'cf', 'td', - 'cl', 'cn', 'cx', 'cc', 'co', 'km', 'cg', 'cd', 'ck', 'cr', 'ci', 'hr', 'cu', 'cy', - 'cz', 'dk', 'dj', 'dm', 'do', 'ec', 'eg', 'sv', 'gq', 'er', 'ee', 'et', 'fk', 'fo', - 'fj', 'fi', 'fr', 'gf', 'pf', 'tf', 'ga', 'gm', 'ge', 'de', 'gh', 'gi', 'gr', 'gl', - 'gd', 'gp', 'gu', 'gt', 'gn', 'gw', 'gy', 'ht', 'hm', 'va', 'hn', 'hk', 'hu', 'is', - 'in', 'id', 'ir', 'iq', 'ie', 'il', 'it', 'jm', 'jp', 'jo', 'kz', 'ke', 'ki', 'kp', - 'kr', 'kw', 'kg', 'la', 'lv', 'lb', 'ls', 'lr', 'ly', 'li', 'lt', 'lu', 'mo', 'mk', - 'mg', 'mw', 'my', 'mv', 'ml', 'mt', 'mh', 'mq', 'mr', 'mu', 'yt', 'mx', 'fm', 'md', - 'mc', 'mn', 'ms', 'ma', 'mz', 'mm', 'na', 'nr', 'np', 'nl', 'an', 'nc', 'nz', 'ni', - 'ne', 'ng', 'nu', 'nf', 'mp', 'no', 'om', 'pk', 'pw', 'ps', 'pa', 'pg', 'py', 'pe', - 'ph', 'pn', 'pl', 'pt', 'pr', 'qa', 're', 'ro', 'ru', 'rw', 'sh', 'kn', 'lc', 'pm', - 'vc', 'ws', 'sm', 'st', 'sa', 'sn', 'rs', 'sc', 'sl', 'sg', 'sk', 'si', 'sb', 'so', - 'za', 'gs', 'es', 'lk', 'sd', 'sr', 'sj', 'sz', 'se', 'ch', 'sy', 'tw', 'tj', 'tz', - 'th', 'tl', 'tg', 'tk', 'to', 'tt', 'tn', 'tr', 'tm', 'tc', 'tv', 'ug', 'ua', 'ae', - 'uk', 'gb', 'us', 'um', 'uy', 'uz', 'vu', 've', 'vn', 'vg', 'vi', 'wf', 'eh', 'ye', - 'zm', 'zw']) - self.check_valid_value(self.language, "Google languages", - ['af', 'ak', 'sq', 'ws', 'am', 'ar', 'hy', 'az', 'eu', 'be', 'bem', 'bn', 'bh', - 'xx-bork', 'bs', 'br', 'bg', 'bt', 'km', 'ca', 'chr', 'ny', 'zh-cn', 'zh-tw', 'co', - 'hr', 'cs', 'da', 'nl', 'xx-elmer', 'en', 'eo', 'et', 'ee', 'fo', 'tl', 'fi', 'fr', - 'fy', 'gaa', 'gl', 'ka', 'de', 'el', 'kl', 'gn', 'gu', 'xx-hacker', 'ht', 'ha', 'haw', - 'iw', 'hi', 'hu', 'is', 'ig', 'id', 'ia', 'ga', 'it', 'ja', 'jw', 'kn', 'kk', 'rw', - 'rn', 'xx-klingon', 'kg', 'ko', 'kri', 'ku', 'ckb', 'ky', 'lo', 'la', 'lv', 'ln', 'lt', - 'loz', 'lg', 'ach', 'mk', 'mg', 'ms', 'ml', 'mt', 'mv', 'mi', 'mr', 'mfe', 'mo', 'mn', - 'sr-me', 'my', 'ne', 'pcm', 'nso', 'no', 'nn', 'oc', 'or', 'om', 'ps', 'fa', - 'xx-pirate', 'pl', 'pt', 'pt-br', 'pt-pt', 'pa', 'qu', 'ro', 'rm', 'nyn', 'ru', 'gd', - 'sr', 'sh', 'st', 'tn', 'crs', 'sn', 'sd', 'si', 'sk', 'sl', 'so', 'es', 'es-419', 'su', - 'sw', 'sv', 'tg', 'ta', 'tt', 'te', 'th', 'ti', 'to', 'lua', 'tum', 'tr', 'tk', 'tw', - 'ug', 'uk', 'ur', 'uz', 'vu', 'vi', 'cy', 'wo', 'xh', 'yi', 'yo', 'zu'] - ) - - -class Google(ComponentBase, ABC): - component_name = "Google" - - def _run(self, history, **kwargs): - ans = self.get_input() - ans = " - ".join(ans["content"]) if "content" in ans else "" - if not ans: - return Google.be_output("") - - try: - client = GoogleSearch( - {"engine": "google", "q": ans, "api_key": self._param.api_key, "gl": self._param.country, - "hl": self._param.language, "num": self._param.top_n}) - google_res = [{"content": '' + i["title"] + ' ' + i["snippet"]} for i in - client.get_dict()["organic_results"]] - except Exception: - return Google.be_output("**ERROR**: Existing Unavailable Parameters!") - - if not google_res: - return Google.be_output("") - - df = pd.DataFrame(google_res) - logging.debug(f"df: {df}") - return df diff --git a/agent/component/googlescholar.py b/agent/component/googlescholar.py deleted file mode 100644 index 4ad580f..0000000 --- a/agent/component/googlescholar.py +++ /dev/null @@ -1,70 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import logging -from abc import ABC -import pandas as pd -from agent.component.base import ComponentBase, ComponentParamBase -from scholarly import scholarly - - -class GoogleScholarParam(ComponentParamBase): - """ - Define the GoogleScholar component parameters. - """ - - def __init__(self): - super().__init__() - self.top_n = 6 - self.sort_by = 'relevance' - self.year_low = None - self.year_high = None - self.patents = True - - def check(self): - self.check_positive_integer(self.top_n, "Top N") - self.check_valid_value(self.sort_by, "GoogleScholar Sort_by", ['date', 'relevance']) - self.check_boolean(self.patents, "Whether or not to include patents, defaults to True") - - -class GoogleScholar(ComponentBase, ABC): - component_name = "GoogleScholar" - - def _run(self, history, **kwargs): - ans = self.get_input() - ans = " - ".join(ans["content"]) if "content" in ans else "" - if not ans: - return GoogleScholar.be_output("") - - scholar_client = scholarly.search_pubs(ans, patents=self._param.patents, year_low=self._param.year_low, - year_high=self._param.year_high, sort_by=self._param.sort_by) - scholar_res = [] - for i in range(self._param.top_n): - try: - pub = next(scholar_client) - scholar_res.append({"content": 'Title: ' + pub['bib']['title'] + '\n_Url: ' + "\n author: " + ",".join(pub['bib']['author']) + '\n Abstract: ' + pub[ - 'bib'].get('abstract', 'no abstract')}) - - except StopIteration or Exception: - logging.exception("GoogleScholar") - break - - if not scholar_res: - return GoogleScholar.be_output("") - - df = pd.DataFrame(scholar_res) - logging.debug(f"df: {df}") - return df diff --git a/agent/component/invoke.py b/agent/component/invoke.py deleted file mode 100644 index 86ec401..0000000 --- a/agent/component/invoke.py +++ /dev/null @@ -1,108 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import json -import re -from abc import ABC -import requests -from agent.component.base import ComponentBase, ComponentParamBase - - -class InvokeParam(ComponentParamBase): - """ - Define the Crawler component parameters. - """ - - def __init__(self): - super().__init__() - self.proxy = None - self.headers = "" - self.method = "get" - self.variables = [] - self.url = "" - self.timeout = 60 - self.clean_html = False - self.datatype = "json" # New parameter to determine data posting type - - def check(self): - self.check_valid_value(self.method.lower(), "Type of content from the crawler", ["get", "post", "put"]) - self.check_empty(self.url, "End point URL") - self.check_positive_integer(self.timeout, "Timeout time in second") - self.check_boolean(self.clean_html, "Clean HTML") - self.check_valid_value(self.datatype.lower(), "Data post type", ["json", "formdata"]) # Check for valid datapost value - - -class Invoke(ComponentBase, ABC): - component_name = "Invoke" - - def _run(self, history, **kwargs): - args = {} - for para in self._param.variables: - if para.get("component_id"): - if "@" in para["component_id"]: - component = para["component_id"].split("@")[0] - field = para["component_id"].split("@")[1] - cpn = self._canvas.get_component(component)["obj"] - for param in cpn._param.query: - if param["key"] == field: - if "value" in param: - args[para["key"]] = param["value"] - else: - cpn = self._canvas.get_component(para["component_id"])["obj"] - if cpn.component_name.lower() == "answer": - args[para["key"]] = self._canvas.get_history(1)[0]["content"] - continue - _, out = cpn.output(allow_partial=False) - if not out.empty: - args[para["key"]] = "\n".join(out["content"]) - else: - args[para["key"]] = para["value"] - - url = self._param.url.strip() - if url.find("http") != 0: - url = "http://" + url - - method = self._param.method.lower() - headers = {} - if self._param.headers: - headers = json.loads(self._param.headers) - proxies = None - if re.sub(r"https?:?/?/?", "", self._param.proxy): - proxies = {"http": self._param.proxy, "https": self._param.proxy} - - if method == "get": - response = requests.get(url=url, params=args, headers=headers, proxies=proxies, timeout=self._param.timeout) - if self._param.clean_html: - return Invoke.be_output("\n") - - return Invoke.be_output(response.text) - - if method == "put": - if self._param.datatype.lower() == "json": - response = requests.put(url=url, json=args, headers=headers, proxies=proxies, timeout=self._param.timeout) - else: - response = requests.put(url=url, data=args, headers=headers, proxies=proxies, timeout=self._param.timeout) - if self._param.clean_html: - return Invoke.be_output("\n".join()) - return Invoke.be_output(response.text) - - if method == "post": - if self._param.datatype.lower() == "json": - response = requests.post(url=url, json=args, headers=headers, proxies=proxies, timeout=self._param.timeout) - else: - response = requests.post(url=url, data=args, headers=headers, proxies=proxies, timeout=self._param.timeout) - if self._param.clean_html: - return Invoke.be_output("\n".join()) - return Invoke.be_output(response.text) diff --git a/agent/component/iteration.py b/agent/component/iteration.py deleted file mode 100644 index 3f554ae..0000000 --- a/agent/component/iteration.py +++ /dev/null @@ -1,45 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -from abc import ABC -from agent.component.base import ComponentBase, ComponentParamBase - - -class IterationParam(ComponentParamBase): - """ - Define the Iteration component parameters. - """ - - def __init__(self): - super().__init__() - self.delimiter = "," - - def check(self): - self.check_empty(self.delimiter, "Delimiter") - - -class Iteration(ComponentBase, ABC): - component_name = "Iteration" - - def get_start(self): - for cid in self._canvas.components.keys(): - if self._canvas.get_component(cid)["obj"].component_name.lower() != "iterationitem": - continue - if self._canvas.get_component(cid)["parent_id"] == self._id: - return self._canvas.get_component(cid) - - def _run(self, history, **kwargs): - return self.output(allow_partial=False)[1] - diff --git a/agent/component/iterationitem.py b/agent/component/iterationitem.py deleted file mode 100644 index cb1a270..0000000 --- a/agent/component/iterationitem.py +++ /dev/null @@ -1,53 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -from abc import ABC -import pandas as pd -from agent.component.base import ComponentBase, ComponentParamBase - - -class IterationItemParam(ComponentParamBase): - """ - Define the IterationItem component parameters. - """ - def check(self): - return True - - -class IterationItem(ComponentBase, ABC): - component_name = "IterationItem" - - def __init__(self, canvas, id, param: ComponentParamBase): - super().__init__(canvas, id, param) - self._idx = 0 - - def _run(self, history, **kwargs): - parent = self.get_parent() - ans = parent.get_input() - ans = parent._param.delimiter.join(ans["content"]) if "content" in ans else "" - ans = [a.strip() for a in ans.split(parent._param.delimiter)] - if not ans: - self._idx = -1 - return pd.DataFrame() - - df = pd.DataFrame([{"content": ans[self._idx]}]) - self._idx += 1 - if self._idx >= len(ans): - self._idx = -1 - return df - - def end(self): - return self._idx == -1 - diff --git a/agent/component/jin10.py b/agent/component/jin10.py deleted file mode 100644 index 583a182..0000000 --- a/agent/component/jin10.py +++ /dev/null @@ -1,130 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import json -from abc import ABC -import pandas as pd -import requests -from agent.component.base import ComponentBase, ComponentParamBase - - -class Jin10Param(ComponentParamBase): - """ - Define the Jin10 component parameters. - """ - - def __init__(self): - super().__init__() - self.type = "flash" - self.secret_key = "xxx" - self.flash_type = '1' - self.calendar_type = 'cj' - self.calendar_datatype = 'data' - self.symbols_type = 'GOODS' - self.symbols_datatype = 'symbols' - self.contain = "" - self.filter = "" - - def check(self): - self.check_valid_value(self.type, "Type", ['flash', 'calendar', 'symbols', 'news']) - self.check_valid_value(self.flash_type, "Flash Type", ['1', '2', '3', '4', '5']) - self.check_valid_value(self.calendar_type, "Calendar Type", ['cj', 'qh', 'hk', 'us']) - self.check_valid_value(self.calendar_datatype, "Calendar DataType", ['data', 'event', 'holiday']) - self.check_valid_value(self.symbols_type, "Symbols Type", ['GOODS', 'FOREX', 'FUTURE', 'CRYPTO']) - self.check_valid_value(self.symbols_datatype, 'Symbols DataType', ['symbols', 'quotes']) - - -class Jin10(ComponentBase, ABC): - component_name = "Jin10" - - def _run(self, history, **kwargs): - ans = self.get_input() - ans = " - ".join(ans["content"]) if "content" in ans else "" - if not ans: - return Jin10.be_output("") - - jin10_res = [] - headers = {'secret-key': self._param.secret_key} - try: - if self._param.type == "flash": - params = { - 'category': self._param.flash_type, - 'contain': self._param.contain, - 'filter': self._param.filter - } - response = requests.get( - url='https://open-data-api.jin10.com/data-api/flash?category=' + self._param.flash_type, - headers=headers, data=json.dumps(params)) - response = response.json() - for i in response['data']: - jin10_res.append({"content": i['data']['content']}) - if self._param.type == "calendar": - params = { - 'category': self._param.calendar_type - } - response = requests.get( - url='https://open-data-api.jin10.com/data-api/calendar/' + self._param.calendar_datatype + '?category=' + self._param.calendar_type, - headers=headers, data=json.dumps(params)) - - response = response.json() - jin10_res.append({"content": pd.DataFrame(response['data']).to_markdown()}) - if self._param.type == "symbols": - params = { - 'type': self._param.symbols_type - } - if self._param.symbols_datatype == "quotes": - params['codes'] = 'BTCUSD' - response = requests.get( - url='https://open-data-api.jin10.com/data-api/' + self._param.symbols_datatype + '?type=' + self._param.symbols_type, - headers=headers, data=json.dumps(params)) - response = response.json() - if self._param.symbols_datatype == "symbols": - for i in response['data']: - i['Commodity Code'] = i['c'] - i['Stock Exchange'] = i['e'] - i['Commodity Name'] = i['n'] - i['Commodity Type'] = i['t'] - del i['c'], i['e'], i['n'], i['t'] - if self._param.symbols_datatype == "quotes": - for i in response['data']: - i['Selling Price'] = i['a'] - i['Buying Price'] = i['b'] - i['Commodity Code'] = i['c'] - i['Stock Exchange'] = i['e'] - i['Highest Price'] = i['h'] - i['Yesterday’s Closing Price'] = i['hc'] - i['Lowest Price'] = i['l'] - i['Opening Price'] = i['o'] - i['Latest Price'] = i['p'] - i['Market Quote Time'] = i['t'] - del i['a'], i['b'], i['c'], i['e'], i['h'], i['hc'], i['l'], i['o'], i['p'], i['t'] - jin10_res.append({"content": pd.DataFrame(response['data']).to_markdown()}) - if self._param.type == "news": - params = { - 'contain': self._param.contain, - 'filter': self._param.filter - } - response = requests.get( - url='https://open-data-api.jin10.com/data-api/news', - headers=headers, data=json.dumps(params)) - response = response.json() - jin10_res.append({"content": pd.DataFrame(response['data']).to_markdown()}) - except Exception as e: - return Jin10.be_output("**ERROR**: " + str(e)) - - if not jin10_res: - return Jin10.be_output("") - - return pd.DataFrame(jin10_res) diff --git a/agent/component/keyword.py b/agent/component/keyword.py deleted file mode 100644 index 3b71441..0000000 --- a/agent/component/keyword.py +++ /dev/null @@ -1,65 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import logging -import re -from abc import ABC -from api.db import LLMType -from api.db.services.llm_service import LLMBundle -from agent.component import GenerateParam, Generate - - -class KeywordExtractParam(GenerateParam): - """ - Define the KeywordExtract component parameters. - """ - - def __init__(self): - super().__init__() - self.top_n = 1 - - def check(self): - super().check() - self.check_positive_integer(self.top_n, "Top N") - - def get_prompt(self): - self.prompt = """ -- Role: You're a question analyzer. -- Requirements: - - Summarize user's question, and give top %s important keyword/phrase. - - Use comma as a delimiter to separate keywords/phrases. -- Answer format: (in language of user's question) - - keyword: -""" % self.top_n - return self.prompt - - -class KeywordExtract(Generate, ABC): - component_name = "KeywordExtract" - - def _run(self, history, **kwargs): - query = self.get_input() - query = str(query["content"][0]) if "content" in query else "" - - chat_mdl = LLMBundle(self._canvas.get_tenant_id(), LLMType.CHAT, self._param.llm_id) - ans = chat_mdl.chat(self._param.get_prompt(), [{"role": "user", "content": query}], - self._param.gen_conf()) - - ans = re.sub(r".*keyword:", "", ans).strip() - logging.debug(f"ans: {ans}") - return KeywordExtract.be_output(ans) - - def debug(self, **kwargs): - return self._run([], **kwargs) \ No newline at end of file diff --git a/agent/component/message.py b/agent/component/message.py deleted file mode 100644 index a193dd1..0000000 --- a/agent/component/message.py +++ /dev/null @@ -1,53 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import random -from abc import ABC -from functools import partial -from agent.component.base import ComponentBase, ComponentParamBase - - -class MessageParam(ComponentParamBase): - - """ - Define the Message component parameters. - """ - def __init__(self): - super().__init__() - self.messages = [] - - def check(self): - self.check_empty(self.messages, "[Message]") - return True - - -class Message(ComponentBase, ABC): - component_name = "Message" - - def _run(self, history, **kwargs): - if kwargs.get("stream"): - return partial(self.stream_output) - - return Message.be_output(random.choice(self._param.messages)) - - def stream_output(self): - res = None - if self._param.messages: - res = {"content": random.choice(self._param.messages)} - yield res - - self.set_output(res) - - diff --git a/agent/component/pubmed.py b/agent/component/pubmed.py deleted file mode 100644 index 8f41d3c..0000000 --- a/agent/component/pubmed.py +++ /dev/null @@ -1,69 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import logging -from abc import ABC -from Bio import Entrez -import re -import pandas as pd -import xml.etree.ElementTree as ET -from agent.component.base import ComponentBase, ComponentParamBase - - -class PubMedParam(ComponentParamBase): - """ - Define the PubMed component parameters. - """ - - def __init__(self): - super().__init__() - self.top_n = 5 - self.email = "A.N.Other@example.com" - - def check(self): - self.check_positive_integer(self.top_n, "Top N") - - -class PubMed(ComponentBase, ABC): - component_name = "PubMed" - - def _run(self, history, **kwargs): - ans = self.get_input() - ans = " - ".join(ans["content"]) if "content" in ans else "" - if not ans: - return PubMed.be_output("") - - try: - Entrez.email = self._param.email - pubmedids = Entrez.read(Entrez.esearch(db='pubmed', retmax=self._param.top_n, term=ans))['IdList'] - pubmedcnt = ET.fromstring(re.sub(r'<(/?)b>|<(/?)i>', '', Entrez.efetch(db='pubmed', id=",".join(pubmedids), - retmode="xml").read().decode( - "utf-8"))) - pubmed_res = [{"content": 'Title:' + child.find("MedlineCitation").find("Article").find( - "ArticleTitle").text + '\nUrl:' + '\n' + 'Abstract:' + ( - child.find("MedlineCitation").find("Article").find("Abstract").find( - "AbstractText").text if child.find("MedlineCitation").find( - "Article").find("Abstract") else "No abstract available")} for child in - pubmedcnt.findall("PubmedArticle")] - except Exception as e: - return PubMed.be_output("**ERROR**: " + str(e)) - - if not pubmed_res: - return PubMed.be_output("") - - df = pd.DataFrame(pubmed_res) - logging.debug(f"df: {df}") - return df diff --git a/agent/component/qweather.py b/agent/component/qweather.py deleted file mode 100644 index 2c38a8b..0000000 --- a/agent/component/qweather.py +++ /dev/null @@ -1,111 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -from abc import ABC -import pandas as pd -import requests -from agent.component.base import ComponentBase, ComponentParamBase - - -class QWeatherParam(ComponentParamBase): - """ - Define the QWeather component parameters. - """ - - def __init__(self): - super().__init__() - self.web_apikey = "xxx" - self.lang = "zh" - self.type = "weather" - self.user_type = 'free' - self.error_code = { - "204": "The request was successful, but the region you are querying does not have the data you need at this time.", - "400": "Request error, may contain incorrect request parameters or missing mandatory request parameters.", - "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).", - "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.", - "403": "No access, may be the binding PackageName, BundleID, domain IP address is inconsistent, or the data that requires additional payment.", - "404": "The queried data or region does not exist.", - "429": "Exceeded the limited QPM (number of accesses per minute), please refer to the QPM description", - "500": "No response or timeout, interface service abnormality please contact us" - } - # Weather - self.time_period = 'now' - - def check(self): - self.check_empty(self.web_apikey, "BaiduFanyi APPID") - self.check_valid_value(self.type, "Type", ["weather", "indices", "airquality"]) - self.check_valid_value(self.user_type, "Free subscription or paid subscription", ["free", "paid"]) - self.check_valid_value(self.lang, "Use language", - ['zh', 'zh-hant', 'en', 'de', 'es', 'fr', 'it', 'ja', 'ko', 'ru', 'hi', 'th', 'ar', 'pt', - 'bn', 'ms', 'nl', 'el', 'la', 'sv', 'id', 'pl', 'tr', 'cs', 'et', 'vi', 'fil', 'fi', - 'he', 'is', 'nb']) - self.check_valid_value(self.time_period, "Time period", ['now', '3d', '7d', '10d', '15d', '30d']) - - -class QWeather(ComponentBase, ABC): - component_name = "QWeather" - - def _run(self, history, **kwargs): - ans = self.get_input() - ans = "".join(ans["content"]) if "content" in ans else "" - if not ans: - return QWeather.be_output("") - - try: - response = requests.get( - url="https://geoapi.qweather.com/v2/city/lookup?location=" + ans + "&key=" + self._param.web_apikey).json() - if response["code"] == "200": - location_id = response["location"][0]["id"] - else: - return QWeather.be_output("**Error**" + self._param.error_code[response["code"]]) - - base_url = "https://api.qweather.com/v7/" if self._param.user_type == 'paid' else "https://devapi.qweather.com/v7/" - - if self._param.type == "weather": - url = base_url + "weather/" + self._param.time_period + "?location=" + location_id + "&key=" + self._param.web_apikey + "&lang=" + self._param.lang - response = requests.get(url=url).json() - if response["code"] == "200": - if self._param.time_period == "now": - return QWeather.be_output(str(response["now"])) - else: - qweather_res = [{"content": str(i) + "\n"} for i in response["daily"]] - if not qweather_res: - return QWeather.be_output("") - - df = pd.DataFrame(qweather_res) - return df - else: - return QWeather.be_output("**Error**" + self._param.error_code[response["code"]]) - - elif self._param.type == "indices": - url = base_url + "indices/1d?type=0&location=" + location_id + "&key=" + self._param.web_apikey + "&lang=" + self._param.lang - response = requests.get(url=url).json() - if response["code"] == "200": - indices_res = response["daily"][0]["date"] + "\n" + "\n".join( - [i["name"] + ": " + i["category"] + ", " + i["text"] for i in response["daily"]]) - return QWeather.be_output(indices_res) - - else: - return QWeather.be_output("**Error**" + self._param.error_code[response["code"]]) - - elif self._param.type == "airquality": - url = base_url + "air/now?location=" + location_id + "&key=" + self._param.web_apikey + "&lang=" + self._param.lang - response = requests.get(url=url).json() - if response["code"] == "200": - return QWeather.be_output(str(response["now"])) - else: - return QWeather.be_output("**Error**" + self._param.error_code[response["code"]]) - except Exception as e: - return QWeather.be_output("**Error**" + str(e)) diff --git a/agent/component/relevant.py b/agent/component/relevant.py deleted file mode 100644 index 201506a..0000000 --- a/agent/component/relevant.py +++ /dev/null @@ -1,83 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import logging -from abc import ABC -from api.db import LLMType -from api.db.services.llm_service import LLMBundle -from agent.component import GenerateParam, Generate -from rag.utils import num_tokens_from_string, encoder - - -class RelevantParam(GenerateParam): - - """ - Define the Relevant component parameters. - """ - def __init__(self): - super().__init__() - self.prompt = "" - self.yes = "" - self.no = "" - - def check(self): - super().check() - self.check_empty(self.yes, "[Relevant] 'Yes'") - self.check_empty(self.no, "[Relevant] 'No'") - - def get_prompt(self): - self.prompt = """ - You are a grader assessing relevance of a retrieved document to a user question. - It does not need to be a stringent test. The goal is to filter out erroneous retrievals. - If the document contains keyword(s) or semantic meaning related to the user question, grade it as relevant. - Give a binary score 'yes' or 'no' score to indicate whether the document is relevant to the question. - No other words needed except 'yes' or 'no'. - """ - return self.prompt - - -class Relevant(Generate, ABC): - component_name = "Relevant" - - def _run(self, history, **kwargs): - q = "" - for r, c in self._canvas.history[::-1]: - if r == "user": - q = c - break - ans = self.get_input() - ans = " - ".join(ans["content"]) if "content" in ans else "" - if not ans: - return Relevant.be_output(self._param.no) - ans = "Documents: \n" + ans - ans = f"Question: {q}\n" + ans - chat_mdl = LLMBundle(self._canvas.get_tenant_id(), LLMType.CHAT, self._param.llm_id) - - if num_tokens_from_string(ans) >= chat_mdl.max_length - 4: - ans = encoder.decode(encoder.encode(ans)[:chat_mdl.max_length - 4]) - - ans = chat_mdl.chat(self._param.get_prompt(), [{"role": "user", "content": ans}], - self._param.gen_conf()) - - logging.debug(ans) - if ans.lower().find("yes") >= 0: - return Relevant.be_output(self._param.yes) - if ans.lower().find("no") >= 0: - return Relevant.be_output(self._param.no) - assert False, f"Relevant component got: {ans}" - - def debug(self, **kwargs): - return self._run([], **kwargs) - diff --git a/agent/component/retrieval.py b/agent/component/retrieval.py deleted file mode 100644 index c960725..0000000 --- a/agent/component/retrieval.py +++ /dev/null @@ -1,108 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import logging -from abc import ABC - -import pandas as pd - -from api.db import LLMType -from api.db.services.knowledgebase_service import KnowledgebaseService -from api.db.services.llm_service import LLMBundle -from api import settings -from agent.component.base import ComponentBase, ComponentParamBase -from rag.app.tag import label_question -from rag.utils.tavily_conn import Tavily - - -class RetrievalParam(ComponentParamBase): - - """ - Define the Retrieval component parameters. - """ - def __init__(self): - super().__init__() - self.similarity_threshold = 0.2 - self.keywords_similarity_weight = 0.5 - self.top_n = 8 - self.top_k = 1024 - self.kb_ids = [] - self.rerank_id = "" - self.empty_response = "" - self.tavily_api_key = "" - self.use_kg = False - - def check(self): - self.check_decimal_float(self.similarity_threshold, "[Retrieval] Similarity threshold") - self.check_decimal_float(self.keywords_similarity_weight, "[Retrieval] Keyword similarity weight") - self.check_positive_number(self.top_n, "[Retrieval] Top N") - - -class Retrieval(ComponentBase, ABC): - component_name = "Retrieval" - - def _run(self, history, **kwargs): - query = self.get_input() - query = str(query["content"][0]) if "content" in query else "" - lines = query.split('\n') - user_queries = [line.split("USER:", 1)[1] for line in lines if line.startswith("USER:")] - query = user_queries[-1] if user_queries else "" - kbs = KnowledgebaseService.get_by_ids(self._param.kb_ids) - if not kbs: - return Retrieval.be_output("") - - embd_nms = list(set([kb.embd_id for kb in kbs])) - assert len(embd_nms) == 1, "Knowledge bases use different embedding models." - - embd_mdl = LLMBundle(self._canvas.get_tenant_id(), LLMType.EMBEDDING, embd_nms[0]) - self._canvas.set_embedding_model(embd_nms[0]) - - rerank_mdl = None - if self._param.rerank_id: - rerank_mdl = LLMBundle(kbs[0].tenant_id, LLMType.RERANK, self._param.rerank_id) - - kbinfos = settings.retrievaler.retrieval(query, embd_mdl, kbs[0].tenant_id, self._param.kb_ids, - 1, self._param.top_n, - self._param.similarity_threshold, 1 - self._param.keywords_similarity_weight, - aggs=False, rerank_mdl=rerank_mdl, - rank_feature=label_question(query, kbs)) - if self._param.use_kg: - ck = settings.kg_retrievaler.retrieval(query, - [kbs[0].tenant_id], - self._param.kb_ids, - embd_mdl, - LLMBundle(kbs[0].tenant_id, LLMType.CHAT)) - if ck["content_with_weight"]: - kbinfos["chunks"].insert(0, ck) - - if self._param.tavily_api_key: - tav = Tavily(self._param.tavily_api_key) - tav_res = tav.retrieve_chunks(query) - kbinfos["chunks"].extend(tav_res["chunks"]) - kbinfos["doc_aggs"].extend(tav_res["doc_aggs"]) - - if not kbinfos["chunks"]: - df = Retrieval.be_output("") - if self._param.empty_response and self._param.empty_response.strip(): - df["empty_response"] = self._param.empty_response - return df - - df = pd.DataFrame(kbinfos["chunks"]) - df["content"] = df["content_with_weight"] - del df["content_with_weight"] - logging.debug("{} {}".format(query, df)) - return df - - diff --git a/agent/component/rewrite.py b/agent/component/rewrite.py deleted file mode 100644 index 0ab19d4..0000000 --- a/agent/component/rewrite.py +++ /dev/null @@ -1,94 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -from abc import ABC -from agent.component import GenerateParam, Generate -from rag.prompts import full_question - - -class RewriteQuestionParam(GenerateParam): - """ - Define the QuestionRewrite component parameters. - """ - - def __init__(self): - super().__init__() - self.temperature = 0.9 - self.prompt = "" - self.language = "" - - def check(self): - super().check() - - -class RewriteQuestion(Generate, ABC): - component_name = "RewriteQuestion" - - def _run(self, history, **kwargs): - hist = self._canvas.get_history(self._param.message_history_window_size) - query = self.get_input() - query = str(query["content"][0]) if "content" in query else "" - messages = [h for h in hist if h["role"]!="system"] - if messages[-1]["role"] != "user": - messages.append({"role": "user", "content": query}) - ans = full_question(self._canvas.get_tenant_id(), self._param.llm_id, messages, self.gen_lang(self._param.language)) - self._canvas.history.pop() - self._canvas.history.append(("user", ans)) - return RewriteQuestion.be_output(ans) - - @staticmethod - def gen_lang(language): - # convert code lang to language word for the prompt - language_dict = {'af': 'Afrikaans', 'ak': 'Akan', 'sq': 'Albanian', 'ws': 'Samoan', 'am': 'Amharic', - 'ar': 'Arabic', 'hy': 'Armenian', 'az': 'Azerbaijani', 'eu': 'Basque', 'be': 'Belarusian', - 'bem': 'Bemba', 'bn': 'Bengali', 'bh': 'Bihari', - 'xx-bork': 'Bork', 'bs': 'Bosnian', 'br': 'Breton', 'bg': 'Bulgarian', 'bt': 'Bhutani', - 'km': 'Cambodian', 'ca': 'Catalan', 'chr': 'Cherokee', 'ny': 'Chichewa', 'zh-cn': 'Chinese', - 'zh-tw': 'Chinese', 'co': 'Corsican', - 'hr': 'Croatian', 'cs': 'Czech', 'da': 'Danish', 'nl': 'Dutch', 'xx-elmer': 'Elmer', - 'en': 'English', 'eo': 'Esperanto', 'et': 'Estonian', 'ee': 'Ewe', 'fo': 'Faroese', - 'tl': 'Filipino', 'fi': 'Finnish', 'fr': 'French', - 'fy': 'Frisian', 'gaa': 'Ga', 'gl': 'Galician', 'ka': 'Georgian', 'de': 'German', - 'el': 'Greek', 'kl': 'Greenlandic', 'gn': 'Guarani', 'gu': 'Gujarati', 'xx-hacker': 'Hacker', - 'ht': 'Haitian Creole', 'ha': 'Hausa', 'haw': 'Hawaiian', - 'iw': 'Hebrew', 'hi': 'Hindi', 'hu': 'Hungarian', 'is': 'Icelandic', 'ig': 'Igbo', - 'id': 'Indonesian', 'ia': 'Interlingua', 'ga': 'Irish', 'it': 'Italian', 'ja': 'Japanese', - 'jw': 'Javanese', 'kn': 'Kannada', 'kk': 'Kazakh', 'rw': 'Kinyarwanda', - 'rn': 'Kirundi', 'xx-klingon': 'Klingon', 'kg': 'Kongo', 'ko': 'Korean', 'kri': 'Krio', - 'ku': 'Kurdish', 'ckb': 'Kurdish (Sorani)', 'ky': 'Kyrgyz', 'lo': 'Laothian', 'la': 'Latin', - 'lv': 'Latvian', 'ln': 'Lingala', 'lt': 'Lithuanian', - 'loz': 'Lozi', 'lg': 'Luganda', 'ach': 'Luo', 'mk': 'Macedonian', 'mg': 'Malagasy', - 'ms': 'Malay', 'ml': 'Malayalam', 'mt': 'Maltese', 'mv': 'Maldivian', 'mi': 'Maori', - 'mr': 'Marathi', 'mfe': 'Mauritian Creole', 'mo': 'Moldavian', 'mn': 'Mongolian', - 'sr-me': 'Montenegrin', 'my': 'Burmese', 'ne': 'Nepali', 'pcm': 'Nigerian Pidgin', - 'nso': 'Northern Sotho', 'no': 'Norwegian', 'nn': 'Norwegian Nynorsk', 'oc': 'Occitan', - 'or': 'Oriya', 'om': 'Oromo', 'ps': 'Pashto', 'fa': 'Persian', - 'xx-pirate': 'Pirate', 'pl': 'Polish', 'pt': 'Portuguese', 'pt-br': 'Portuguese (Brazilian)', - 'pt-pt': 'Portuguese (Portugal)', 'pa': 'Punjabi', 'qu': 'Quechua', 'ro': 'Romanian', - 'rm': 'Romansh', 'nyn': 'Runyankole', 'ru': 'Russian', 'gd': 'Scots Gaelic', - 'sr': 'Serbian', 'sh': 'Serbo-Croatian', 'st': 'Sesotho', 'tn': 'Setswana', - 'crs': 'Seychellois Creole', 'sn': 'Shona', 'sd': 'Sindhi', 'si': 'Sinhalese', 'sk': 'Slovak', - 'sl': 'Slovenian', 'so': 'Somali', 'es': 'Spanish', 'es-419': 'Spanish (Latin America)', - 'su': 'Sundanese', - 'sw': 'Swahili', 'sv': 'Swedish', 'tg': 'Tajik', 'ta': 'Tamil', 'tt': 'Tatar', 'te': 'Telugu', - 'th': 'Thai', 'ti': 'Tigrinya', 'to': 'Tongan', 'lua': 'Tshiluba', 'tum': 'Tumbuka', - 'tr': 'Turkish', 'tk': 'Turkmen', 'tw': 'Twi', - 'ug': 'Uyghur', 'uk': 'Ukrainian', 'ur': 'Urdu', 'uz': 'Uzbek', 'vu': 'Vanuatu', - 'vi': 'Vietnamese', 'cy': 'Welsh', 'wo': 'Wolof', 'xh': 'Xhosa', 'yi': 'Yiddish', - 'yo': 'Yoruba', 'zu': 'Zulu'} - if language in language_dict: - return language_dict[language] - else: - return "" diff --git a/agent/component/switch.py b/agent/component/switch.py deleted file mode 100644 index 01affe0..0000000 --- a/agent/component/switch.py +++ /dev/null @@ -1,131 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -from abc import ABC -from agent.component.base import ComponentBase, ComponentParamBase - - -class SwitchParam(ComponentParamBase): - """ - Define the Switch component parameters. - """ - - def __init__(self): - super().__init__() - """ - { - "logical_operator" : "and | or" - "items" : [ - {"cpn_id": "categorize:0", "operator": "contains", "value": ""}, - {"cpn_id": "categorize:0", "operator": "contains", "value": ""},...], - "to": "" - } - """ - self.conditions = [] - self.end_cpn_id = "answer:0" - self.operators = ['contains', 'not contains', 'start with', 'end with', 'empty', 'not empty', '=', '≠', '>', - '<', '≥', '≤'] - - def check(self): - self.check_empty(self.conditions, "[Switch] conditions") - for cond in self.conditions: - if not cond["to"]: - raise ValueError("[Switch] 'To' can not be empty!") - - -class Switch(ComponentBase, ABC): - component_name = "Switch" - - def get_dependent_components(self): - res = [] - for cond in self._param.conditions: - for item in cond["items"]: - if not item["cpn_id"]: - continue - if item["cpn_id"].find("begin") >= 0: - continue - cid = item["cpn_id"].split("@")[0] - res.append(cid) - - return list(set(res)) - - def _run(self, history, **kwargs): - for cond in self._param.conditions: - res = [] - for item in cond["items"]: - if not item["cpn_id"]: - continue - cid = item["cpn_id"].split("@")[0] - if item["cpn_id"].find("@") > 0: - cpn_id, key = item["cpn_id"].split("@") - for p in self._canvas.get_component(cid)["obj"]._param.query: - if p["key"] == key: - res.append(self.process_operator(p.get("value",""), item["operator"], item.get("value", ""))) - break - else: - out = self._canvas.get_component(cid)["obj"].output()[1] - cpn_input = "" if "content" not in out.columns else " ".join([str(s) for s in out["content"]]) - res.append(self.process_operator(cpn_input, item["operator"], item.get("value", ""))) - - if cond["logical_operator"] != "and" and any(res): - return Switch.be_output(cond["to"]) - - if all(res): - return Switch.be_output(cond["to"]) - - return Switch.be_output(self._param.end_cpn_id) - - def process_operator(self, input: str, operator: str, value: str) -> bool: - if not isinstance(input, str) or not isinstance(value, str): - raise ValueError('Invalid input or value type: string') - - if operator == "contains": - return True if value.lower() in input.lower() else False - elif operator == "not contains": - return True if value.lower() not in input.lower() else False - elif operator == "start with": - return True if input.lower().startswith(value.lower()) else False - elif operator == "end with": - return True if input.lower().endswith(value.lower()) else False - elif operator == "empty": - return True if not input else False - elif operator == "not empty": - return True if input else False - elif operator == "=": - return True if input == value else False - elif operator == "≠": - return True if input != value else False - elif operator == ">": - try: - return True if float(input) > float(value) else False - except Exception: - return True if input > value else False - elif operator == "<": - try: - return True if float(input) < float(value) else False - except Exception: - return True if input < value else False - elif operator == "≥": - try: - return True if float(input) >= float(value) else False - except Exception: - return True if input >= value else False - elif operator == "≤": - try: - return True if float(input) <= float(value) else False - except Exception: - return True if input <= value else False - - raise ValueError('Not supported operator' + operator) \ No newline at end of file diff --git a/agent/component/template.py b/agent/component/template.py deleted file mode 100644 index 4d7db20..0000000 --- a/agent/component/template.py +++ /dev/null @@ -1,136 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import json -import re -from agent.component.base import ComponentBase, ComponentParamBase -from jinja2 import Template as Jinja2Template - - -class TemplateParam(ComponentParamBase): - """ - Define the Generate component parameters. - """ - - def __init__(self): - super().__init__() - self.content = "" - self.parameters = [] - - def check(self): - self.check_empty(self.content, "[Template] Content") - return True - - -class Template(ComponentBase): - component_name = "Template" - - def get_dependent_components(self): - inputs = self.get_input_elements() - cpnts = set([i["key"] for i in inputs if i["key"].lower().find("answer") < 0 and i["key"].lower().find("begin") < 0]) - return list(cpnts) - - def get_input_elements(self): - key_set = set([]) - res = [] - for r in re.finditer(r"\{([a-z]+[:@][a-z0-9_-]+)\}", self._param.content, flags=re.IGNORECASE): - cpn_id = r.group(1) - if cpn_id in key_set: - continue - if cpn_id.lower().find("begin@") == 0: - cpn_id, key = cpn_id.split("@") - for p in self._canvas.get_component(cpn_id)["obj"]._param.query: - if p["key"] != key: - continue - res.append({"key": r.group(1), "name": p["name"]}) - key_set.add(r.group(1)) - continue - cpn_nm = self._canvas.get_component_name(cpn_id) - if not cpn_nm: - continue - res.append({"key": cpn_id, "name": cpn_nm}) - key_set.add(cpn_id) - return res - - def _run(self, history, **kwargs): - content = self._param.content - - self._param.inputs = [] - for para in self.get_input_elements(): - if para["key"].lower().find("begin@") == 0: - cpn_id, key = para["key"].split("@") - for p in self._canvas.get_component(cpn_id)["obj"]._param.query: - if p["key"] == key: - value = p.get("value", "") - self.make_kwargs(para, kwargs, value) - break - else: - assert False, f"Can't find parameter '{key}' for {cpn_id}" - continue - - component_id = para["key"] - cpn = self._canvas.get_component(component_id)["obj"] - if cpn.component_name.lower() == "answer": - hist = self._canvas.get_history(1) - if hist: - hist = hist[0]["content"] - else: - hist = "" - self.make_kwargs(para, kwargs, hist) - continue - - _, out = cpn.output(allow_partial=False) - - result = "" - if "content" in out.columns: - result = "\n".join( - [o if isinstance(o, str) else str(o) for o in out["content"]] - ) - - self.make_kwargs(para, kwargs, result) - - template = Jinja2Template(content) - - try: - content = template.render(kwargs) - except Exception: - pass - - for n, v in kwargs.items(): - try: - v = json.dumps(v, ensure_ascii=False) - except Exception: - pass - content = re.sub( - r"\{%s\}" % re.escape(n), v, content - ) - content = re.sub( - r"(\\\"|\")", "", content - ) - content = re.sub( - r"(#+)", r" \1 ", content - ) - - return Template.be_output(content) - - def make_kwargs(self, para, kwargs, value): - self._param.inputs.append( - {"component_id": para["key"], "content": value} - ) - try: - value = json.loads(value) - except Exception: - pass - kwargs[para["key"]] = value diff --git a/agent/component/tushare.py b/agent/component/tushare.py deleted file mode 100644 index bb9d34f..0000000 --- a/agent/component/tushare.py +++ /dev/null @@ -1,72 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import json -from abc import ABC -import pandas as pd -import time -import requests -from agent.component.base import ComponentBase, ComponentParamBase - - -class TuShareParam(ComponentParamBase): - """ - Define the TuShare component parameters. - """ - - def __init__(self): - super().__init__() - self.token = "xxx" - self.src = "eastmoney" - self.start_date = "2024-01-01 09:00:00" - self.end_date = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) - self.keyword = "" - - def check(self): - self.check_valid_value(self.src, "Quick News Source", - ["sina", "wallstreetcn", "10jqka", "eastmoney", "yuncaijing", "fenghuang", "jinrongjie"]) - - -class TuShare(ComponentBase, ABC): - component_name = "TuShare" - - def _run(self, history, **kwargs): - ans = self.get_input() - ans = ",".join(ans["content"]) if "content" in ans else "" - if not ans: - return TuShare.be_output("") - - try: - tus_res = [] - params = { - "api_name": "news", - "token": self._param.token, - "params": {"src": self._param.src, "start_date": self._param.start_date, - "end_date": self._param.end_date} - } - response = requests.post(url="http://api.tushare.pro", data=json.dumps(params).encode('utf-8')) - response = response.json() - if response['code'] != 0: - return TuShare.be_output(response['msg']) - df = pd.DataFrame(response['data']['items']) - df.columns = response['data']['fields'] - tus_res.append({"content": (df[df['content'].str.contains(self._param.keyword, case=False)]).to_markdown()}) - except Exception as e: - return TuShare.be_output("**ERROR**: " + str(e)) - - if not tus_res: - return TuShare.be_output("") - - return pd.DataFrame(tus_res) diff --git a/agent/component/wencai.py b/agent/component/wencai.py deleted file mode 100644 index 8f8c351..0000000 --- a/agent/component/wencai.py +++ /dev/null @@ -1,80 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -from abc import ABC -import pandas as pd -import pywencai -from agent.component.base import ComponentBase, ComponentParamBase - - -class WenCaiParam(ComponentParamBase): - """ - Define the WenCai component parameters. - """ - - def __init__(self): - super().__init__() - self.top_n = 10 - self.query_type = "stock" - - def check(self): - self.check_positive_integer(self.top_n, "Top N") - self.check_valid_value(self.query_type, "Query type", - ['stock', 'zhishu', 'fund', 'hkstock', 'usstock', 'threeboard', 'conbond', 'insurance', - 'futures', 'lccp', - 'foreign_exchange']) - - -class WenCai(ComponentBase, ABC): - component_name = "WenCai" - - def _run(self, history, **kwargs): - ans = self.get_input() - ans = ",".join(ans["content"]) if "content" in ans else "" - if not ans: - return WenCai.be_output("") - - try: - wencai_res = [] - res = pywencai.get(query=ans, query_type=self._param.query_type, perpage=self._param.top_n) - if isinstance(res, pd.DataFrame): - wencai_res.append({"content": res.to_markdown()}) - if isinstance(res, dict): - for item in res.items(): - if isinstance(item[1], list): - wencai_res.append({"content": item[0] + "\n" + pd.DataFrame(item[1]).to_markdown()}) - continue - if isinstance(item[1], str): - wencai_res.append({"content": item[0] + "\n" + item[1]}) - continue - if isinstance(item[1], dict): - if "meta" in item[1].keys(): - continue - wencai_res.append({"content": pd.DataFrame.from_dict(item[1], orient='index').to_markdown()}) - continue - if isinstance(item[1], pd.DataFrame): - if "image_url" in item[1].columns: - continue - wencai_res.append({"content": item[1].to_markdown()}) - continue - - wencai_res.append({"content": item[0] + "\n" + str(item[1])}) - except Exception as e: - return WenCai.be_output("**ERROR**: " + str(e)) - - if not wencai_res: - return WenCai.be_output("") - - return pd.DataFrame(wencai_res) diff --git a/agent/component/wikipedia.py b/agent/component/wikipedia.py deleted file mode 100644 index 8ccadca..0000000 --- a/agent/component/wikipedia.py +++ /dev/null @@ -1,67 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import logging -from abc import ABC -import wikipedia -import pandas as pd -from agent.component.base import ComponentBase, ComponentParamBase - - -class WikipediaParam(ComponentParamBase): - """ - Define the Wikipedia component parameters. - """ - - def __init__(self): - super().__init__() - self.top_n = 10 - self.language = "en" - - def check(self): - self.check_positive_integer(self.top_n, "Top N") - self.check_valid_value(self.language, "Wikipedia languages", - ['af', 'pl', 'ar', 'ast', 'az', 'bg', 'nan', 'bn', 'be', 'ca', 'cs', 'cy', 'da', 'de', - 'et', 'el', 'en', 'es', 'eo', 'eu', 'fa', 'fr', 'gl', 'ko', 'hy', 'hi', 'hr', 'id', - 'it', 'he', 'ka', 'lld', 'la', 'lv', 'lt', 'hu', 'mk', 'arz', 'ms', 'min', 'my', 'nl', - 'ja', 'nb', 'nn', 'ce', 'uz', 'pt', 'kk', 'ro', 'ru', 'ceb', 'sk', 'sl', 'sr', 'sh', - 'fi', 'sv', 'ta', 'tt', 'th', 'tg', 'azb', 'tr', 'uk', 'ur', 'vi', 'war', 'zh', 'yue']) - - -class Wikipedia(ComponentBase, ABC): - component_name = "Wikipedia" - - def _run(self, history, **kwargs): - ans = self.get_input() - ans = " - ".join(ans["content"]) if "content" in ans else "" - if not ans: - return Wikipedia.be_output("") - - try: - wiki_res = [] - wikipedia.set_lang(self._param.language) - wiki_engine = wikipedia - for wiki_key in wiki_engine.search(ans, results=self._param.top_n): - page = wiki_engine.page(title=wiki_key, auto_suggest=False) - wiki_res.append({"content": '' + page.title + ' ' + page.summary}) - except Exception as e: - return Wikipedia.be_output("**ERROR**: " + str(e)) - - if not wiki_res: - return Wikipedia.be_output("") - - df = pd.DataFrame(wiki_res) - logging.debug(f"df: {df}") - return df diff --git a/agent/component/yahoofinance.py b/agent/component/yahoofinance.py deleted file mode 100644 index f31c7ae..0000000 --- a/agent/component/yahoofinance.py +++ /dev/null @@ -1,84 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import logging -from abc import ABC -import pandas as pd -from agent.component.base import ComponentBase, ComponentParamBase -import yfinance as yf - - -class YahooFinanceParam(ComponentParamBase): - """ - Define the YahooFinance component parameters. - """ - - def __init__(self): - super().__init__() - self.info = True - self.history = False - self.count = False - self.financials = False - self.income_stmt = False - self.balance_sheet = False - self.cash_flow_statement = False - self.news = True - - def check(self): - self.check_boolean(self.info, "get all stock info") - self.check_boolean(self.history, "get historical market data") - self.check_boolean(self.count, "show share count") - self.check_boolean(self.financials, "show financials") - self.check_boolean(self.income_stmt, "income statement") - self.check_boolean(self.balance_sheet, "balance sheet") - self.check_boolean(self.cash_flow_statement, "cash flow statement") - self.check_boolean(self.news, "show news") - - -class YahooFinance(ComponentBase, ABC): - component_name = "YahooFinance" - - def _run(self, history, **kwargs): - ans = self.get_input() - ans = "".join(ans["content"]) if "content" in ans else "" - if not ans: - return YahooFinance.be_output("") - - yohoo_res = [] - try: - msft = yf.Ticker(ans) - if self._param.info: - yohoo_res.append({"content": "info:\n" + pd.Series(msft.info).to_markdown() + "\n"}) - if self._param.history: - yohoo_res.append({"content": "history:\n" + msft.history().to_markdown() + "\n"}) - if self._param.financials: - yohoo_res.append({"content": "calendar:\n" + pd.DataFrame(msft.calendar).to_markdown() + "\n"}) - if self._param.balance_sheet: - yohoo_res.append({"content": "balance sheet:\n" + msft.balance_sheet.to_markdown() + "\n"}) - yohoo_res.append( - {"content": "quarterly balance sheet:\n" + msft.quarterly_balance_sheet.to_markdown() + "\n"}) - if self._param.cash_flow_statement: - yohoo_res.append({"content": "cash flow statement:\n" + msft.cashflow.to_markdown() + "\n"}) - yohoo_res.append( - {"content": "quarterly cash flow statement:\n" + msft.quarterly_cashflow.to_markdown() + "\n"}) - if self._param.news: - yohoo_res.append({"content": "news:\n" + pd.DataFrame(msft.news).to_markdown() + "\n"}) - except Exception: - logging.exception("YahooFinance got exception") - - if not yohoo_res: - return YahooFinance.be_output("") - - return pd.DataFrame(yohoo_res) diff --git a/agent/settings.py b/agent/settings.py deleted file mode 100644 index 932cb1d..0000000 --- a/agent/settings.py +++ /dev/null @@ -1,18 +0,0 @@ -# -# Copyright 2025 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -FLOAT_ZERO = 1e-8 -PARAM_MAXDEPTH = 5 diff --git a/agent/templates/DB Assistant.json b/agent/templates/DB Assistant.json deleted file mode 100644 index 604cd3b..0000000 --- a/agent/templates/DB Assistant.json +++ /dev/null @@ -1,890 +0,0 @@ -{ - "id": 6, - "title": "DB Assistant", - "description": "An advanced agent that converts user queries into SQL statements, executes the queries, and assesses and returns the results. You must prepare three knowledge bases: 1: DDL for your database; 2: Examples of user queries converted to SQL statements; 3: A comprehensive description of your database, including but not limited to tables and records. You are also required to configure the corresponding database.", - "canvas_type": "chatbot", - "dsl": { - "answer": [], - "components": { - "Answer:SocialAdsWonder": { - "downstream": [ - "RewriteQuestion:WildIdeasTell" - ], - "obj": { - "component_name": "Answer", - "inputs": [], - "output": {}, - "params": {} - }, - "upstream": [ - "ExeSQL:QuietRosesRun", - "begin" - ] - }, - "ExeSQL:QuietRosesRun": { - "downstream": [ - "Answer:SocialAdsWonder" - ], - "obj": { - "component_name": "ExeSQL", - "inputs": [], - "output": {}, - "params": { - "database": "", - "db_type": "mysql", - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "host": "", - "llm_id": "deepseek-chat@DeepSeek", - "loop": 3, - "maxTokensEnabled": true, - "max_tokens": 512, - "password": "", - "port": 6630, - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "query": [], - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_n": 30, - "top_p": 0.3, - "username": "root" - } - }, - "upstream": [ - "Generate:BlueShirtsLaugh" - ] - }, - "Generate:BlueShirtsLaugh": { - "downstream": [ - "ExeSQL:QuietRosesRun" - ], - "obj": { - "component_name": "Generate", - "params": { - "cite": false, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "message_history_window_size": 1, - "parameters": [], - "presence_penalty": 0.4, - "prompt": "\n##The user provides a question and you provide SQL. You will only respond with SQL code and not with any explanations.\n\n##You may use the following DDL statements as a reference for what tables might be available. Use responses to past questions also to guide you: {Retrieval:SillyPartsCheer}.\n\n##You may use the following documentation as a reference for what tables might be available. Use responses to past questions also to guide you: {Retrieval:OddSingersRefuse}.\n\n##You may use the following SQL statements as a reference for what tables might be available. Use responses to past questions also to guide you: {Retrieval:BrownStreetsRhyme}.\n\n##Respond with only SQL code. Do not answer with any explanations -- just the code.", - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "Retrieval:SillyPartsCheer", - "Retrieval:BrownStreetsRhyme", - "Retrieval:OddSingersRefuse" - ] - }, - "Retrieval:BrownStreetsRhyme": { - "downstream": [ - "Generate:BlueShirtsLaugh" - ], - "obj": { - "component_name": "Retrieval", - "inputs": [], - "output": {}, - "params": { - "empty_response": "Nothing found in Q->SQL!", - "kb_ids": [], - "keywords_similarity_weight": 0.3, - "query": [ - { - "component_id": "Answer:SocialAdsWonder", - "type": "reference" - } - ], - "similarity_threshold": 0.2, - "top_n": 8 - } - }, - "upstream": [ - "RewriteQuestion:WildIdeasTell" - ] - }, - "Retrieval:OddSingersRefuse": { - "downstream": [ - "Generate:BlueShirtsLaugh" - ], - "obj": { - "component_name": "Retrieval", - "inputs": [], - "output": {}, - "params": { - "empty_response": "Nothing found in DB-Description!", - "kb_ids": [], - "keywords_similarity_weight": 0.3, - "query": [ - { - "component_id": "Answer:SocialAdsWonder", - "type": "reference" - } - ], - "similarity_threshold": 0.2, - "top_n": 8 - } - }, - "upstream": [ - "RewriteQuestion:WildIdeasTell" - ] - }, - "Retrieval:SillyPartsCheer": { - "downstream": [ - "Generate:BlueShirtsLaugh" - ], - "obj": { - "component_name": "Retrieval", - "inputs": [], - "output": {}, - "params": { - "empty_response": "Nothing found in DDL!", - "kb_ids": [], - "keywords_similarity_weight": 0.1, - "query": [ - { - "component_id": "Answer:SocialAdsWonder", - "type": "reference" - } - ], - "similarity_threshold": 0.02, - "top_n": 18 - } - }, - "upstream": [ - "RewriteQuestion:WildIdeasTell" - ] - }, - "RewriteQuestion:WildIdeasTell": { - "downstream": [ - "Retrieval:OddSingersRefuse", - "Retrieval:BrownStreetsRhyme", - "Retrieval:SillyPartsCheer" - ], - "obj": { - "component_name": "RewriteQuestion", - "params": { - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 6, - "parameter": "Precise", - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - } - }, - "upstream": [ - "Answer:SocialAdsWonder" - ] - }, - "begin": { - "downstream": [ - "Answer:SocialAdsWonder" - ], - "obj": { - "component_name": "Begin", - "inputs": [], - "output": { - "content": { - "0": { - "content": "Hi! I'm your smart assistant. What can I do for you?" - } - } - }, - "params": {} - }, - "upstream": [] - } - }, - "embed_id": "BAAI/bge-large-zh-v1.5", - "graph": { - "edges": [ - { - "id": "xy-edge__ExeSQL:QuietRosesRunc-Answer:SocialAdsWonderc", - "markerEnd": "logo", - "source": "ExeSQL:QuietRosesRun", - "sourceHandle": "c", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Answer:SocialAdsWonder", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__begin-Answer:SocialAdsWonderc", - "markerEnd": "logo", - "source": "begin", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Answer:SocialAdsWonder", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Answer:SocialAdsWonderb-RewriteQuestion:WildIdeasTellc", - "markerEnd": "logo", - "source": "Answer:SocialAdsWonder", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "RewriteQuestion:WildIdeasTell", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__RewriteQuestion:WildIdeasTellb-Retrieval:OddSingersRefusec", - "markerEnd": "logo", - "source": "RewriteQuestion:WildIdeasTell", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Retrieval:OddSingersRefuse", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__RewriteQuestion:WildIdeasTellb-Retrieval:BrownStreetsRhymec", - "markerEnd": "logo", - "source": "RewriteQuestion:WildIdeasTell", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Retrieval:BrownStreetsRhyme", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__RewriteQuestion:WildIdeasTellb-Retrieval:SillyPartsCheerc", - "markerEnd": "logo", - "source": "RewriteQuestion:WildIdeasTell", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Retrieval:SillyPartsCheer", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Generate:BlueShirtsLaughc-ExeSQL:QuietRosesRunb", - "markerEnd": "logo", - "source": "Generate:BlueShirtsLaugh", - "sourceHandle": "c", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "ExeSQL:QuietRosesRun", - "targetHandle": "b", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Retrieval:SillyPartsCheerb-Generate:BlueShirtsLaughb", - "markerEnd": "logo", - "source": "Retrieval:SillyPartsCheer", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:BlueShirtsLaugh", - "targetHandle": "b", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Retrieval:BrownStreetsRhymeb-Generate:BlueShirtsLaughb", - "markerEnd": "logo", - "source": "Retrieval:BrownStreetsRhyme", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:BlueShirtsLaugh", - "targetHandle": "b", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Retrieval:OddSingersRefuseb-Generate:BlueShirtsLaughb", - "markerEnd": "logo", - "source": "Retrieval:OddSingersRefuse", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:BlueShirtsLaugh", - "targetHandle": "b", - "type": "buttonEdge", - "zIndex": 1001 - } - ], - "nodes": [ - { - "data": { - "label": "Begin", - "name": "begin" - }, - "dragging": false, - "height": 44, - "id": "begin", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": -707.997699967585, - "y": 271.71609546793474 - }, - "positionAbsolute": { - "x": -707.997699967585, - "y": 271.71609546793474 - }, - "selected": false, - "sourcePosition": "left", - "targetPosition": "right", - "type": "beginNode", - "width": 200 - }, - { - "data": { - "form": {}, - "label": "Answer", - "name": "Interface" - }, - "dragging": false, - "height": 44, - "id": "Answer:SocialAdsWonder", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": -265.59460323639587, - "y": 271.1879130306969 - }, - "positionAbsolute": { - "x": -58.36886074370702, - "y": 272.1213623212045 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "logicNode", - "width": 200 - }, - { - "data": { - "form": { - "empty_response": "Nothing found in DDL!", - "kb_ids": [], - "keywords_similarity_weight": 0.1, - "query": [ - { - "component_id": "Answer:SocialAdsWonder", - "type": "reference" - } - ], - "similarity_threshold": 0.02, - "top_n": 18 - }, - "label": "Retrieval", - "name": "DDL" - }, - "dragging": false, - "height": 106, - "id": "Retrieval:SillyPartsCheer", - "measured": { - "height": 106, - "width": 200 - }, - "position": { - "x": 194.69889765569846, - "y": 61.49435233230193 - }, - "positionAbsolute": { - "x": 198.3020069445181, - "y": -0.9595420072386389 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "retrievalNode", - "width": 200 - }, - { - "data": { - "form": { - "empty_response": "Nothing found in Q->SQL!", - "kb_ids": [], - "keywords_similarity_weight": 0.3, - "query": [ - { - "component_id": "Answer:SocialAdsWonder", - "type": "reference" - } - ], - "similarity_threshold": 0.2, - "top_n": 8 - }, - "label": "Retrieval", - "name": "Q->SQL" - }, - "dragging": false, - "height": 106, - "id": "Retrieval:BrownStreetsRhyme", - "measured": { - "height": 106, - "width": 200 - }, - "position": { - "x": 240.78282320440022, - "y": 162.66081324653166 - }, - "positionAbsolute": { - "x": 231.17453176754782, - "y": 123.02661106951555 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "retrievalNode", - "width": 200 - }, - { - "data": { - "form": { - "empty_response": "Nothing found in DB-Description!", - "kb_ids": [], - "keywords_similarity_weight": 0.3, - "query": [ - { - "component_id": "Answer:SocialAdsWonder", - "type": "reference" - } - ], - "similarity_threshold": 0.2, - "top_n": 8 - }, - "label": "Retrieval", - "name": "DB Description" - }, - "dragging": false, - "height": 106, - "id": "Retrieval:OddSingersRefuse", - "measured": { - "height": 106, - "width": 200 - }, - "position": { - "x": 284.5720579655624, - "y": 246.75395940479467 - }, - "positionAbsolute": { - "x": 267.7575479510707, - "y": 249.15603226400776 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "retrievalNode", - "width": 200 - }, - { - "data": { - "form": { - "text": "Based on the result of the SQL execution, returns the error message to the large model if any errors occur; otherwise, returns the result to the user." - }, - "label": "Note", - "name": "N: Analyze SQL" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 165, - "id": "Note:HeavyIconsFollow", - "measured": { - "height": 165, - "width": 347 - }, - "position": { - "x": -709.8631299685773, - "y": 96.50319908555313 - }, - "positionAbsolute": { - "x": -626.6563777191027, - "y": -48.82220889683933 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 176, - "width": 266 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 347 - }, - { - "data": { - "form": { - "text": "Receives the user's database-related questions and displays the large model's response." - }, - "label": "Note", - "name": "N: Interface" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 159, - "id": "Note:PinkTaxesClean", - "measured": { - "height": 159, - "width": 259 - }, - "position": { - "x": -253.39933811515345, - "y": 353.7538896054877 - }, - "positionAbsolute": { - "x": -52.004609812312424, - "y": 336.95180237635077 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 162, - "width": 210 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 259 - }, - { - "data": { - "form": { - "text": "Searches for description about meanings of tables and fields." - }, - "label": "Note", - "name": "N:DB Description" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 128, - "id": "Note:IcyTreesPeel", - "measured": { - "height": 128, - "width": 251 - }, - "position": { - "x": 280.8431980571563, - "y": 394.1463067004627 - }, - "positionAbsolute": { - "x": 280.8431980571563, - "y": 394.1463067004627 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 128, - "width": 251 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 251 - }, - { - "data": { - "form": { - "text": "Searches for samples about question to SQL.\nPlease check this dataset: https://huggingface.co/datasets/InfiniFlow/text2sql" - }, - "label": "Note", - "name": "N: Q->SQL" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 143, - "id": "Note:HugeGroupsScream", - "measured": { - "height": 143, - "width": 390 - }, - "position": { - "x": 612.8793199038756, - "y": 169.1868576959871 - }, - "positionAbsolute": { - "x": 606.1206536213404, - "y": 113.09441734894426 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 131, - "width": 387 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 390 - }, - { - "data": { - "form": { - "text": "DDL(Data Definition Language).\n\nSearches for relevant database creation statements.\n\nIt should bind with a KB to which DDL is dumped in.\nYou could use 'General' as parsing method and ';' as delimiter." - }, - "label": "Note", - "name": "N: DDL" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 208, - "id": "Note:GreenCrewsArrive", - "measured": { - "height": 208, - "width": 467 - }, - "position": { - "x": 649.3481710005742, - "y": -87.70873445087781 - }, - "positionAbsolute": { - "x": 545.3423934788841, - "y": -166.58872868890683 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 266, - "width": 266 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 467 - }, - { - "data": { - "form": { - "text": "The large model learns which tables may be available based on the responses from three knowledge bases and converts the user's input into SQL statements." - }, - "label": "Note", - "name": "N: Generate SQL" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 196, - "id": "Note:EightTurtlesLike", - "measured": { - "height": 196, - "width": 341 - }, - "position": { - "x": 134.0070839275931, - "y": -345.41228234051727 - }, - "positionAbsolute": { - "x": 222.2150747084395, - "y": -445.32694170868734 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 175, - "width": 265 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 341 - }, - { - "data": { - "form": { - "text": "Executes the SQL statement in the database and returns the result.\n\nAfter configuring an accessible database, press 'Test' to ensure the accessibility.\n\nThe large model modifies the original SQL statement based on the error message and returns the modified SQL statement." - }, - "label": "Note", - "name": "N: Execute SQL" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 276, - "id": "Note:FreshKidsTalk", - "measured": { - "height": 276, - "width": 336 - }, - "position": { - "x": -304.3577648765364, - "y": -288.054469323955 - }, - "positionAbsolute": { - "x": -251.5866574377311, - "y": -372.2192837064241 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 178, - "width": 346 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 336 - }, - { - "data": { - "form": { - "database": "", - "db_type": "mysql", - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "host": "", - "llm_id": "deepseek-chat@DeepSeek", - "loop": 3, - "maxTokensEnabled": true, - "max_tokens": 512, - "password": "", - "port": 6630, - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "query": [], - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_n": 30, - "top_p": 0.3, - "username": "root" - }, - "label": "ExeSQL", - "name": "ExeSQL" - }, - "dragging": false, - "id": "ExeSQL:QuietRosesRun", - "measured": { - "height": 64, - "width": 200 - }, - "position": { - "x": -318.61920731731163, - "y": 3.5145731711609436 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "ragNode" - }, - { - "data": { - "form": { - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 6, - "parameter": "Precise", - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "RewriteQuestion", - "name": "RefineQuestion" - }, - "dragging": false, - "id": "RewriteQuestion:WildIdeasTell", - "measured": { - "height": 106, - "width": 200 - }, - "position": { - "x": -7.734116293705583, - "y": 236.92372325779243 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "rewriteNode" - }, - { - "data": { - "form": { - "cite": false, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": false, - "max_tokens": 256, - "message_history_window_size": 1, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "\n##The user provides a question and you provide SQL. You will only respond with SQL code and not with any explanations.\n\n##You may use the following DDL statements as a reference for what tables might be available. Use responses to past questions also to guide you: {Retrieval:SillyPartsCheer}.\n\n##You may use the following documentation as a reference for what tables might be available. Use responses to past questions also to guide you: {Retrieval:OddSingersRefuse}.\n\n##You may use the following SQL statements as a reference for what tables might be available. Use responses to past questions also to guide you: {Retrieval:BrownStreetsRhyme}.\n\n##Respond with only SQL code. Do not answer with any explanations -- just the code.", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "Generate SQL Statement LLM" - }, - "dragging": false, - "id": "Generate:BlueShirtsLaugh", - "measured": { - "height": 106, - "width": 200 - }, - "position": { - "x": 147.62383788095065, - "y": -116.47462293167156 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode" - } - ] - }, - "history": [], - "messages": [], - "path": [], - "reference": [] - }, - "avatar": "data:image/png;base64,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" -} diff --git a/agent/templates/HR_callout_zh.json b/agent/templates/HR_callout_zh.json deleted file mode 100644 index 2d73cd8..0000000 --- a/agent/templates/HR_callout_zh.json +++ /dev/null @@ -1,1806 +0,0 @@ -{ - "id": 2, - "title": "HR recruitment pitch assistant (Chinese)", - "description": "A recruitment pitch assistant capable of pitching a candidate, presenting a job opportunity, addressing queries, and requesting the candidate's contact details. Let's begin by linking a knowledge base containing the job description in 'Retrieval'!", - "canvas_type": "chatbot", - "dsl": { - "answer": [], - "components": { - "Answer:TwentyMugsDeny": { - "downstream": [ - "categorize:1" - ], - "obj": { - "component_name": "Answer", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "post_answers": [], - "query": [] - } - }, - "upstream": [ - "Message:MajorPigsYell", - "Generate:TruePawsReport", - "Generate:ToughLawsCheat", - "Generate:KindCarrotsSit", - "Generate:DirtyToolsTrain", - "Generate:FluffyPillowsGrow", - "Generate:ProudEarsWorry" - ] - }, - "Generate:DirtyToolsTrain": { - "downstream": [ - "Answer:TwentyMugsDeny" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": false, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 256, - "message_history_window_size": 12, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "你是公司负责招聘的HR,当你提出加微信时对方表示拒绝。你需要耐心礼貌的回应候选人,表示对于保护隐私信息给予理解,也可以询问他对该职位的看法和顾虑。并在恰当的时机再次询问微信联系方式。也可以鼓励候选人主动与你取得联系。你的微信号是weixin_kevin,E-mail是kkk@ragflow.com。说话不要重复。不要总是您好。", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "categorize:1" - ] - }, - "Generate:FluffyPillowsGrow": { - "downstream": [ - "Answer:TwentyMugsDeny" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": false, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 256, - "message_history_window_size": 12, - "output": null, - "output_var_name": "output", - "parameters": [ - { - "component_id": "Retrieval:ColdEelsArrive", - "id": "5166a107-e859-4c71-99a2-3a216c775347", - "key": "jd" - } - ], - "presence_penalty": 0.4, - "prompt": "你是公司负责招聘的HR,候选人问了有关职位或公司的问题,你根据以下职位信息回答。如果职位信息中不包含候选人的问题就回答不清楚、不知道、有待确认等。回答完后引导候选人加微信号,如:\n - 方便加一下微信吗,我把JD发您看看?\n - 微信号多少,我把详细职位JD发您?\n 职位信息如下:\n {Retrieval:ColdEelsArrive}\n 职位信息如上。", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "Retrieval:ColdEelsArrive" - ] - }, - "Generate:KindCarrotsSit": { - "downstream": [ - "Answer:TwentyMugsDeny" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": false, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 256, - "message_history_window_size": 12, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "你是公司负责招聘的HR,候选人表示不反感加微信,如果对方已经报了微信号,表示感谢和信任并表示马上会加上;如果没有,则问对方微信号多少。你的微信号是weixin_kevin,E-mail是kkk@ragflow.com。说话不要重复。不要总是您好。", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "categorize:1" - ] - }, - "Generate:ProudEarsWorry": { - "downstream": [ - "Answer:TwentyMugsDeny" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": false, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 256, - "message_history_window_size": 12, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "你是公司负责招聘的HR,现在候选人的聊了和职位无关的话题,请耐心的回应候选人,并将话题往该AGI的职位上带,最好能要到候选人微信号以便后面保持联系。", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "categorize:0" - ] - }, - "Generate:ToughLawsCheat": { - "downstream": [ - "Answer:TwentyMugsDeny" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": false, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 256, - "message_history_window_size": 12, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "你是公司负责招聘的HR,现在候选人的聊了和职位无关的话题,请耐心的回应候选人,并将话题往该AGI的职位上带,最好能要到候选人微信号以便后面保持联系。", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "categorize:1" - ] - }, - "Generate:TruePawsReport": { - "downstream": [ - "Answer:TwentyMugsDeny" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": false, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 256, - "message_history_window_size": 12, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "你是公司负责招聘的HR,候选人问了有关职位或公司的问题,你根据以下职位信息回答。如果职位信息中不包含候选人的问题就回答不清楚、不知道、有待确认等。回答完后引导候选人加微信号,如:\n - 方便加一下微信吗,我把JD发您看看?\n - 微信号多少,我把详细职位JD发您?\n 职位信息如下:\n {Retrieval:ShaggyRadiosRetire}\n 职位信息如上。", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "Retrieval:ShaggyRadiosRetire" - ] - }, - "Message:MajorPigsYell": { - "downstream": [ - "Answer:TwentyMugsDeny" - ], - "obj": { - "component_name": "Message", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "messages": [ - "我简单介绍一下:\nRAGFlow 是一款基于深度文档理解构建的开源 RAG(Retrieval-Augmented Generation)引擎。RAGFlow 可以为各种规模的企业及个人提供一套精简的 RAG 工作流程,结合大语言模型(LLM)针对用户各类不同的复杂格式数据提供可靠的问答以及有理有据的引用。https://github.com/infiniflow/ragflow\n您那边还有什么要了解的?" - ], - "output": null, - "output_var_name": "output", - "query": [] - } - }, - "upstream": [ - "categorize:0" - ] - }, - "Retrieval:ColdEelsArrive": { - "downstream": [ - "Generate:FluffyPillowsGrow" - ], - "obj": { - "component_name": "Retrieval", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "empty_response": "", - "inputs": [], - "kb_ids": [], - "keywords_similarity_weight": 0.3, - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [], - "rerank_id": "", - "similarity_threshold": 0.2, - "top_k": 1024, - "top_n": 6 - } - }, - "upstream": [ - "categorize:1" - ] - }, - "Retrieval:ShaggyRadiosRetire": { - "downstream": [ - "Generate:TruePawsReport" - ], - "obj": { - "component_name": "Retrieval", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "empty_response": "", - "inputs": [], - "kb_ids": [], - "keywords_similarity_weight": 0.3, - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [], - "rerank_id": "", - "similarity_threshold": 0.2, - "top_k": 1024, - "top_n": 6 - } - }, - "upstream": [ - "categorize:0" - ] - }, - "answer:0": { - "downstream": [ - "categorize:0" - ], - "obj": { - "component_name": "Answer", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "post_answers": [], - "query": [] - } - }, - "upstream": [ - "begin", - "message:reject" - ] - }, - "begin": { - "downstream": [ - "answer:0" - ], - "obj": { - "component_name": "Begin", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "prologue": "您好!我是英飞流负责招聘的HR,了解到您是这方面的大佬,然后冒昧的就联系到您。这边有个机会想和您分享,RAGFlow正在招聘您这个岗位的资深的工程师不知道您那边是不是感兴趣?", - "query": [] - } - }, - "upstream": [] - }, - "categorize:0": { - "downstream": [ - "message:reject", - "Retrieval:ShaggyRadiosRetire", - "Generate:ProudEarsWorry", - "Message:MajorPigsYell" - ], - "obj": { - "component_name": "Categorize", - "inputs": [], - "output": null, - "params": { - "category_description": { - "answer": { - "description": "该问题关于职位本身或公司的信息。", - "examples": "什么岗位?\n汇报对象是谁?\n公司多少人?\n公司有啥产品?\n具体工作内容是啥?\n地点哪里?\n双休吗?", - "to": "Retrieval:ShaggyRadiosRetire" - }, - "casual": { - "description": "该问题不关于职位本身或公司的信息,属于闲聊。", - "examples": "你好\n好久不见\n你男的女的?\n你是猴子派来的救兵吗?\n上午开会了?\n你叫啥?\n最近市场如何?生意好做吗?", - "to": "Generate:ProudEarsWorry" - }, - "interested": { - "description": "该回答表示他对于该职位感兴趣。", - "examples": "嗯\n说吧\n说说看\n还好吧\n是的\n哦\nyes\n具体说说", - "to": "Message:MajorPigsYell" - }, - "reject": { - "description": "该回答表示他对于该职位不感兴趣,或感觉受到骚扰。", - "examples": "不需要\n不感兴趣\n暂时不看\n不要\nno\n我已经不干这个了\n我不是这个方向的", - "to": "message:reject" - } - }, - "cite": true, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 512, - "message_history_window_size": 1, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "answer:0" - ] - }, - "categorize:1": { - "downstream": [ - "Retrieval:ColdEelsArrive", - "Generate:ToughLawsCheat", - "Generate:KindCarrotsSit", - "Generate:DirtyToolsTrain" - ], - "obj": { - "component_name": "Categorize", - "inputs": [], - "output": null, - "params": { - "category_description": { - "about_job": { - "description": "该问题关于职位本身或公司的信息。", - "examples": "什么岗位?\n汇报对象是谁?\n公司多少人?\n公司有啥产品?\n具体工作内容是啥?\n地点哪里?\n双休吗?", - "to": "Retrieval:ColdEelsArrive" - }, - "casual": { - "description": "该问题不关于职位本身或公司的信息,属于闲聊。", - "examples": "你好\n好久不见\n你男的女的?\n你是猴子派来的救兵吗?\n上午开会了?\n你叫啥?\n最近市场如何?生意好做吗?", - "to": "Generate:ToughLawsCheat" - }, - "giveup": { - "description": "该回答表示他不愿意加微信。", - "examples": "不需要\n不感兴趣\n暂时不看\n不要\nno\n不方便\n不知道还要加我微信", - "to": "Generate:DirtyToolsTrain" - }, - "wechat": { - "description": "该回答表示他愿意加微信,或者已经报了微信号。", - "examples": "嗯\n可以\n是的\n哦\nyes\n15002333453\nwindblow_2231", - "to": "Generate:KindCarrotsSit" - } - }, - "cite": true, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 512, - "message_history_window_size": 8, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "Answer:TwentyMugsDeny" - ] - }, - "message:reject": { - "downstream": [ - "answer:0" - ], - "obj": { - "component_name": "Message", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "messages": [ - "好的,祝您生活愉快,工作顺利。", - "哦,好的,感谢您宝贵的时间!" - ], - "output": null, - "output_var_name": "output", - "query": [] - } - }, - "upstream": [ - "categorize:0" - ] - } - }, - "embed_id": "", - "graph": { - "edges": [ - { - "id": "7a045a3d-5881-4a57-9467-75946941a642", - "label": "", - "source": "begin", - "target": "answer:0" - }, - { - "id": "9c6c78c1-532c-423d-9712-61c47a452f0e", - "label": "", - "source": "message:reject", - "target": "answer:0" - }, - { - "id": "reactflow__edge-answer:0b-categorize:0a", - "source": "answer:0", - "sourceHandle": "b", - "target": "categorize:0", - "targetHandle": "a", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Answer:TwentyMugsDenyb-categorize:1a", - "markerEnd": "logo", - "source": "Answer:TwentyMugsDeny", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "categorize:1", - "targetHandle": "a", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Retrieval:ShaggyRadiosRetireb-Generate:TruePawsReportc", - "markerEnd": "logo", - "source": "Retrieval:ShaggyRadiosRetire", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:TruePawsReport", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-categorize:0reject-message:rejectb", - "markerEnd": "logo", - "source": "categorize:0", - "sourceHandle": "reject", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "message:reject", - "targetHandle": "b", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-categorize:0answer-Retrieval:ShaggyRadiosRetirec", - "markerEnd": "logo", - "source": "categorize:0", - "sourceHandle": "answer", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Retrieval:ShaggyRadiosRetire", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-categorize:0casual-Generate:ProudEarsWorryc", - "markerEnd": "logo", - "source": "categorize:0", - "sourceHandle": "casual", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:ProudEarsWorry", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Message:MajorPigsYellb-Answer:TwentyMugsDenyc", - "markerEnd": "logo", - "source": "Message:MajorPigsYell", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Answer:TwentyMugsDeny", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-categorize:0interested-Message:MajorPigsYellc", - "markerEnd": "logo", - "source": "categorize:0", - "sourceHandle": "interested", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Message:MajorPigsYell", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Generate:TruePawsReportb-Answer:TwentyMugsDenyc", - "markerEnd": "logo", - "source": "Generate:TruePawsReport", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Answer:TwentyMugsDeny", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-categorize:1about_job-Retrieval:ColdEelsArriveb", - "markerEnd": "logo", - "source": "categorize:1", - "sourceHandle": "about_job", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Retrieval:ColdEelsArrive", - "targetHandle": "b", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-categorize:1casual-Generate:ToughLawsCheatb", - "markerEnd": "logo", - "source": "categorize:1", - "sourceHandle": "casual", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:ToughLawsCheat", - "targetHandle": "b", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-categorize:1wechat-Generate:KindCarrotsSitb", - "markerEnd": "logo", - "source": "categorize:1", - "sourceHandle": "wechat", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:KindCarrotsSit", - "targetHandle": "b", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-categorize:1giveup-Generate:DirtyToolsTrainb", - "markerEnd": "logo", - "source": "categorize:1", - "sourceHandle": "giveup", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:DirtyToolsTrain", - "targetHandle": "b", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Generate:ToughLawsCheatc-Answer:TwentyMugsDenyc", - "markerEnd": "logo", - "source": "Generate:ToughLawsCheat", - "sourceHandle": "c", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Answer:TwentyMugsDeny", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Generate:KindCarrotsSitc-Answer:TwentyMugsDenyc", - "markerEnd": "logo", - "source": "Generate:KindCarrotsSit", - "sourceHandle": "c", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Answer:TwentyMugsDeny", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Generate:DirtyToolsTrainc-Answer:TwentyMugsDenyc", - "markerEnd": "logo", - "source": "Generate:DirtyToolsTrain", - "sourceHandle": "c", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Answer:TwentyMugsDeny", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Retrieval:ColdEelsArrivec-Generate:FluffyPillowsGrowb", - "markerEnd": "logo", - "source": "Retrieval:ColdEelsArrive", - "sourceHandle": "c", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:FluffyPillowsGrow", - "targetHandle": "b", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Generate:FluffyPillowsGrowc-Answer:TwentyMugsDenyc", - "markerEnd": "logo", - "source": "Generate:FluffyPillowsGrow", - "sourceHandle": "c", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Answer:TwentyMugsDeny", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Generate:ProudEarsWorryb-Answer:TwentyMugsDenyc", - "markerEnd": "logo", - "source": "Generate:ProudEarsWorry", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Answer:TwentyMugsDeny", - "targetHandle": "c", - "type": "buttonEdge" - } - ], - "nodes": [ - { - "data": { - "form": { - "prologue": "您好!我是英飞流负责招聘的HR,了解到您是这方面的大佬,然后冒昧的就联系到您。这边有个机会想和您分享,RAGFlow正在招聘您这个岗位的资深的工程师不知道您那边是不是感兴趣?" - }, - "label": "Begin", - "name": "开场白" - }, - "dragging": false, - "height": 44, - "id": "begin", - "measured": { - "height": 44, - "width": 100 - }, - "position": { - "x": -1034.5459371394727, - "y": -4.596073111491364 - }, - "positionAbsolute": { - "x": -1034.5459371394727, - "y": -4.596073111491364 - }, - "selected": false, - "sourcePosition": "left", - "targetPosition": "right", - "type": "beginNode" - }, - { - "data": { - "form": {}, - "label": "Answer", - "name": "交互1" - }, - "dragging": false, - "height": 44, - "id": "answer:0", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": -759.3845780310955, - "y": -1.5248388351160145 - }, - "positionAbsolute": { - "x": -781.130188267973, - "y": -1.5248388351160145 - }, - "selected": false, - "sourcePosition": "left", - "targetPosition": "right", - "type": "logicNode", - "width": 200 - }, - { - "data": { - "form": { - "category_description": { - "answer": { - "description": "该问题关于职位本身或公司的信息。", - "examples": "什么岗位?\n汇报对象是谁?\n公司多少人?\n公司有啥产品?\n具体工作内容是啥?\n地点哪里?\n双休吗?", - "to": "Retrieval:ShaggyRadiosRetire" - }, - "casual": { - "description": "该问题不关于职位本身或公司的信息,属于闲聊。", - "examples": "你好\n好久不见\n你男的女的?\n你是猴子派来的救兵吗?\n上午开会了?\n你叫啥?\n最近市场如何?生意好做吗?", - "to": "Generate:ProudEarsWorry" - }, - "interested": { - "description": "该回答表示他对于该职位感兴趣。", - "examples": "嗯\n说吧\n说说看\n还好吧\n是的\n哦\nyes\n具体说说", - "to": "Message:MajorPigsYell" - }, - "reject": { - "description": "该回答表示他对于该职位不感兴趣,或感觉受到骚扰。", - "examples": "不需要\n不感兴趣\n暂时不看\n不要\nno\n我已经不干这个了\n我不是这个方向的", - "to": "message:reject" - } - }, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 512, - "message_history_window_size": 1, - "parameter": "Precise", - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Categorize", - "name": "是否感兴趣?" - }, - "dragging": false, - "height": 223, - "id": "categorize:0", - "measured": { - "height": 223, - "width": 200 - }, - "position": { - "x": -511.7953063678477, - "y": -91.33627890840192 - }, - "positionAbsolute": { - "x": -511.7953063678477, - "y": -91.33627890840192 - }, - "selected": false, - "sourcePosition": "left", - "targetPosition": "right", - "type": "categorizeNode", - "width": 200 - }, - { - "data": { - "form": { - "category_description": { - "about_job": { - "description": "该问题关于职位本身或公司的信息。", - "examples": "什么岗位?\n汇报对象是谁?\n公司多少人?\n公司有啥产品?\n具体工作内容是啥?\n地点哪里?\n双休吗?", - "to": "Retrieval:ColdEelsArrive" - }, - "casual": { - "description": "该问题不关于职位本身或公司的信息,属于闲聊。", - "examples": "你好\n好久不见\n你男的女的?\n你是猴子派来的救兵吗?\n上午开会了?\n你叫啥?\n最近市场如何?生意好做吗?", - "to": "Generate:ToughLawsCheat" - }, - "giveup": { - "description": "该回答表示他不愿意加微信。", - "examples": "不需要\n不感兴趣\n暂时不看\n不要\nno\n不方便\n不知道还要加我微信", - "to": "Generate:DirtyToolsTrain" - }, - "wechat": { - "description": "该回答表示他愿意加微信,或者已经报了微信号。", - "examples": "嗯\n可以\n是的\n哦\nyes\n15002333453\nwindblow_2231", - "to": "Generate:KindCarrotsSit" - } - }, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 512, - "message_history_window_size": 8, - "parameter": "Precise", - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Categorize", - "name": "可以加微信?" - }, - "dragging": false, - "height": 223, - "id": "categorize:1", - "measured": { - "height": 223, - "width": 200 - }, - "position": { - "x": 650.2305440350307, - "y": 54.40917808770183 - }, - "positionAbsolute": { - "x": 650.2305440350307, - "y": 54.40917808770183 - }, - "selected": false, - "sourcePosition": "left", - "targetPosition": "right", - "type": "categorizeNode", - "width": 200 - }, - { - "data": { - "form": { - "messages": [ - "好的,祝您生活愉快,工作顺利。", - "哦,好的,感谢您宝贵的时间!" - ] - }, - "label": "Message", - "name": "再会" - }, - "dragging": false, - "height": 44, - "id": "message:reject", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": -531.5363370421936, - "y": 169.8364292609376 - }, - "positionAbsolute": { - "x": -506.16645843250325, - "y": 197.6224867858366 - }, - "selected": false, - "sourcePosition": "left", - "targetPosition": "right", - "type": "logicNode", - "width": 200 - }, - { - "data": { - "form": {}, - "label": "Answer", - "name": "交互2" - }, - "dragging": false, - "height": 44, - "id": "Answer:TwentyMugsDeny", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": 361.4824760998825, - "y": 142.99203467677523 - }, - "positionAbsolute": { - "x": 361.4824760998825, - "y": 142.99203467677523 - }, - "selected": false, - "sourcePosition": "left", - "targetPosition": "right", - "type": "logicNode", - "width": 200 - }, - { - "data": { - "form": { - "kb_ids": [], - "keywords_similarity_weight": 0.3, - "similarity_threshold": 0.2, - "top_k": 1024, - "top_n": 6 - }, - "label": "Retrieval", - "name": "搜索职位信息" - }, - "dragging": false, - "height": 44, - "id": "Retrieval:ShaggyRadiosRetire", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": -200.47207828507428, - "y": -241.8885484926048 - }, - "positionAbsolute": { - "x": -200.47207828507428, - "y": -241.8885484926048 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "retrievalNode", - "width": 200 - }, - { - "data": { - "form": { - "cite": false, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 12, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "你是公司负责招聘的HR,候选人问了有关职位或公司的问题,你根据以下职位信息回答。如果职位信息中不包含候选人的问题就回答不清楚、不知道、有待确认等。回答完后引导候选人加微信号,如:\n - 方便加一下微信吗,我把JD发您看看?\n - 微信号多少,我把详细职位JD发您?\n 职位信息如下:\n {Retrieval:ShaggyRadiosRetire}\n 职位信息如上。", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "回答职位信息并加微信" - }, - "dragging": false, - "height": 86, - "id": "Generate:TruePawsReport", - "measured": { - "height": 86, - "width": 200 - }, - "position": { - "x": 85.46499814334565, - "y": -84.90136892177973 - }, - "positionAbsolute": { - "x": 114.45914512584898, - "y": -243.16108786794368 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode", - "width": 200 - }, - { - "data": { - "form": { - "cite": false, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 12, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "你是公司负责招聘的HR,现在候选人的聊了和职位无关的话题,请耐心的回应候选人,并将话题往该AGI的职位上带,最好能要到候选人微信号以便后面保持联系。", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "闲聊" - }, - "dragging": false, - "height": 86, - "id": "Generate:ProudEarsWorry", - "measured": { - "height": 86, - "width": 200 - }, - "position": { - "x": -201.4798710337693, - "y": 19.284469688181446 - }, - "positionAbsolute": { - "x": -201.4798710337693, - "y": 19.284469688181446 - }, - "selected": true, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode", - "width": 200 - }, - { - "data": { - "form": { - "messages": [ - "我简单介绍一下:\nRAGFlow 是一款基于深度文档理解构建的开源 RAG(Retrieval-Augmented Generation)引擎。RAGFlow 可以为各种规模的企业及个人提供一套精简的 RAG 工作流程,结合大语言模型(LLM)针对用户各类不同的复杂格式数据提供可靠的问答以及有理有据的引用。https://github.com/infiniflow/ragflow\n您那边还有什么要了解的?" - ] - }, - "label": "Message", - "name": "职位简介" - }, - "dragging": false, - "height": 82, - "id": "Message:MajorPigsYell", - "measured": { - "height": 82, - "width": 200 - }, - "position": { - "x": -201.4757352153133, - "y": 142.14338727751849 - }, - "positionAbsolute": { - "x": -202.68382467291758, - "y": 127.64631378626683 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "messageNode", - "width": 200 - }, - { - "data": { - "form": { - "cite": false, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 12, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "你是公司负责招聘的HR,现在候选人的聊了和职位无关的话题,请耐心的回应候选人,并将话题往该AGI的职位上带,最好能要到候选人微信号以便后面保持联系。", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "闲聊(1)" - }, - "dragging": false, - "height": 86, - "id": "Generate:ToughLawsCheat", - "measured": { - "height": 86, - "width": 200 - }, - "position": { - "x": 717.0666295332912, - "y": -260.4610326390065 - }, - "positionAbsolute": { - "x": 719.4828084484998, - "y": -241.13160131733764 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode", - "width": 200 - }, - { - "data": { - "form": { - "kb_ids": [], - "keywords_similarity_weight": 0.3, - "similarity_threshold": 0.2, - "top_k": 1024, - "top_n": 6 - }, - "label": "Retrieval", - "name": "搜索职位信息(1)" - }, - "dragging": false, - "height": 44, - "id": "Retrieval:ColdEelsArrive", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": 679.4658067127144, - "y": -15.040383599249951 - }, - "positionAbsolute": { - "x": 681.881985627923, - "y": -7.791846853624122 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "retrievalNode", - "width": 200 - }, - { - "data": { - "form": { - "cite": false, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 12, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "你是公司负责招聘的HR,候选人表示不反感加微信,如果对方已经报了微信号,表示感谢和信任并表示马上会加上;如果没有,则问对方微信号多少。你的微信号是weixin_kevin,E-mail是kkk@ragflow.com。说话不要重复。不要总是您好。", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "加微信" - }, - "dragging": false, - "height": 86, - "id": "Generate:KindCarrotsSit", - "measured": { - "height": 86, - "width": 200 - }, - "position": { - "x": 679.5187009685263, - "y": 298.0100840992407 - }, - "positionAbsolute": { - "x": 679.5187009685263, - "y": 298.0100840992407 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode", - "width": 200 - }, - { - "data": { - "form": { - "cite": false, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 12, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "你是公司负责招聘的HR,当你提出加微信时对方表示拒绝。你需要耐心礼貌的回应候选人,表示对于保护隐私信息给予理解,也可以询问他对该职位的看法和顾虑。并在恰当的时机再次询问微信联系方式。也可以鼓励候选人主动与你取得联系。你的微信号是weixin_kevin,E-mail是kkk@ragflow.com。说话不要重复。不要总是您好。", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "不同意加微信后引导" - }, - "dragging": false, - "height": 86, - "id": "Generate:DirtyToolsTrain", - "measured": { - "height": 86, - "width": 200 - }, - "position": { - "x": 713.3958582226193, - "y": 412.69665533104524 - }, - "positionAbsolute": { - "x": 730.3091106290796, - "y": 400.61576075500216 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode", - "width": 200 - }, - { - "data": { - "form": { - "text": "接收用户第一次输入,或在判断后输出静态消息。" - }, - "label": "Note", - "name": "N: 交互1" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 128, - "id": "Note:SharpWingsGrab", - "measured": { - "height": 128, - "width": 190 - }, - "position": { - "x": -762.470214040517, - "y": -135.06311183543562 - }, - "positionAbsolute": { - "x": -785.4239137349989, - "y": -137.47929075064422 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 128, - "width": 190 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 190 - }, - { - "data": { - "form": { - "text": "大模型判断用户的输入属于哪一种分类,传给不同的组件。" - }, - "label": "Note", - "name": "N:是否感兴趣" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 128, - "id": "Note:ThickOrangesMelt", - "measured": { - "height": 128, - "width": 198 - }, - "position": { - "x": -514.737951592251, - "y": -232.7753166367196 - }, - "positionAbsolute": { - "x": -514.737951592251, - "y": -232.7753166367196 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 128, - "width": 198 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 198 - }, - { - "data": { - "form": { - "text": "接收用户对职位不感兴趣的相关输入,随机返回一条静态消息。" - }, - "label": "Note", - "name": "N: 再会" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 128, - "id": "Note:FineDaysCheat", - "measured": { - "height": 128, - "width": 203 - }, - "position": { - "x": -530.3000123190136, - "y": 248.91808187570632 - }, - "positionAbsolute": { - "x": -503.7220442517189, - "y": 256.16661862133213 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 128, - "width": 203 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 203 - }, - { - "data": { - "form": { - "text": "接收用户对职位感兴趣的相关输入,返回其中的静态消息。" - }, - "label": "Note", - "name": "N:职位简介" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 128, - "id": "Note:WeakTaxesBegin", - "measured": { - "height": 128, - "width": 208 - }, - "position": { - "x": -197.5153373041337, - "y": 261.2072463084719 - }, - "positionAbsolute": { - "x": -203.55578459215516, - "y": 261.2072463084719 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 128, - "width": 208 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 208 - }, - { - "data": { - "form": { - "text": "接收用户闲聊,根据闲聊内容,大模型返回相应的回答。" - }, - "label": "Note", - "name": "N: 闲聊" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 128, - "id": "Note:FourCornersHelp", - "measured": { - "height": 128, - "width": 213 - }, - "position": { - "x": -195.26410221591698, - "y": -125.75023229737762 - }, - "positionAbsolute": { - "x": -195.26410221591698, - "y": -125.75023229737762 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 128, - "width": 213 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 213 - }, - { - "data": { - "form": { - "text": "接收用户对于职位或者公司的问题,检索知识库,返回检索到的内容。" - }, - "label": "Note", - "name": "N: 搜索职位信息" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 128, - "id": "Note:FortyTiresDance", - "measured": { - "height": 128, - "width": 197 - }, - "position": { - "x": -199.51694815612117, - "y": -382.54628777242647 - }, - "positionAbsolute": { - "x": -199.51694815612117, - "y": -382.54628777242647 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 128, - "width": 197 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 197 - }, - { - "data": { - "form": { - "text": "大模型根据检索到的职位信息,回答用户的输入并请求加微信。" - }, - "label": "Note", - "name": "N: 回答职位信息" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 128, - "id": "Note:SixMasksTie", - "measured": { - "height": 128, - "width": 205 - }, - "position": { - "x": 81.31654079972914, - "y": -230.7938043878787 - }, - "positionAbsolute": { - "x": 113.93495615504537, - "y": -379.38880767320825 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 128, - "width": 205 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 205 - }, - { - "data": { - "form": { - "text": "在第一轮的交互完成后,在确定用户的意愿基础上,继续后续的交流。" - }, - "label": "Note", - "name": "N: 交互2" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 132, - "id": "Note:HipAnimalsLose", - "measured": { - "height": 132, - "width": 200 - }, - "position": { - "x": 361.5573430860398, - "y": 202.76501272911685 - }, - "positionAbsolute": { - "x": 361.5573430860398, - "y": 202.76501272911685 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 132, - "width": 200 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 200 - }, - { - "data": { - "form": { - "text": "接收用户不愿意加微信的请求,大模型生成回答,回答与礼貌用语和引导用户加微信相关。" - }, - "label": "Note", - "name": "N: 不同意加微信后" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 144, - "id": "Note:CalmClownsOpen", - "measured": { - "height": 144, - "width": 200 - }, - "position": { - "x": 724.3625736109275, - "y": 527.6312716948657 - }, - "positionAbsolute": { - "x": 729.1949314413447, - "y": 498.6371247123624 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 144, - "width": 200 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 200 - }, - { - "data": { - "form": { - "text": "接收用户加微信的请求或微信号的信息。如果是加微信请求,则大模型返回询问微信的回答;如果是微信号的信息,则大模型返回礼貌性的回答。" - }, - "label": "Note", - "name": "N: 加微信" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 149, - "id": "Note:EightSuitsAdmire", - "measured": { - "height": 149, - "width": 481 - }, - "position": { - "x": 1118.6632741834096, - "y": 300.1313513476347 - }, - "positionAbsolute": { - "x": 1118.6632741834096, - "y": 300.1313513476347 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 149, - "width": 481 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 481 - }, - { - "data": { - "form": { - "text": "大模型判断用户的输入属于哪一种分类,传给不同的组件。" - }, - "label": "Note", - "name": "N:可以加微信?" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 128, - "id": "Note:SillyPillowsCrash", - "measured": { - "height": 128, - "width": 267 - }, - "position": { - "x": 1006.2146104300559, - "y": 61.99026665969035 - }, - "positionAbsolute": { - "x": 1006.2146104300559, - "y": 61.99026665969035 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 128, - "width": 267 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 267 - }, - { - "data": { - "form": { - "text": "接收用户对于职位或者公司的问题,检索知识库,返回检索到的内容。" - }, - "label": "Note", - "name": "N: 搜索职位信息(1)" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 128, - "id": "Note:PurplePathsHeal", - "measured": { - "height": 128, - "width": 269 - }, - "position": { - "x": 679.0610551820539, - "y": -146.81167586458758 - }, - "positionAbsolute": { - "x": 679.0610551820539, - "y": -146.81167586458758 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "noteNode", - "width": 269 - }, - { - "data": { - "form": { - "text": "接收用户闲聊,根据闲聊内容,大模型返回相应的回答。" - }, - "label": "Note", - "name": "N:闲聊(1)" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 129, - "id": "Note:VastHumansAttend", - "measured": { - "height": 129, - "width": 200 - }, - "position": { - "x": 713.2672727035068, - "y": -403.49170163825374 - }, - "positionAbsolute": { - "x": 719.3077199915283, - "y": -382.2721004750209 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 129, - "width": 200 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 200 - }, - { - "data": { - "form": { - "cite": false, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 12, - "parameter": "Precise", - "parameters": [ - { - "component_id": "Retrieval:ColdEelsArrive", - "id": "5166a107-e859-4c71-99a2-3a216c775347", - "key": "jd" - } - ], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "你是公司负责招聘的HR,候选人问了有关职位或公司的问题,你根据以下职位信息回答。如果职位信息中不包含候选人的问题就回答不清楚、不知道、有待确认等。回答完后引导候选人加微信号,如:\n - 方便加一下微信吗,我把JD发您看看?\n - 微信号多少,我把详细职位JD发您?\n 职位信息如下:\n {Retrieval:ColdEelsArrive}\n 职位信息如上。", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "回答职位信息并加微信(1)" - }, - "dragging": false, - "height": 128, - "id": "Generate:FluffyPillowsGrow", - "measured": { - "height": 128, - "width": 200 - }, - "position": { - "x": 411.4591451258489, - "y": -7.161087867943763 - }, - "positionAbsolute": { - "x": 411.4591451258489, - "y": -7.161087867943763 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode", - "width": 200 - } - ] - }, - "history": [], - "messages": [], - "path": [], - "reference": [] - }, - "avatar": "data:image/jpeg;base64,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" -} diff --git a/agent/templates/customer_service.json b/agent/templates/customer_service.json deleted file mode 100644 index 0723ed5..0000000 --- a/agent/templates/customer_service.json +++ /dev/null @@ -1,1053 +0,0 @@ -{ - "id": 3, - "title": "Customer service", - "description": "A customer service chatbot that explains product specifications, addresses customer queries, and alleviates negative emotions.", - "canvas_type": "chatbot", - "dsl": { - "answer": [], - "components": { - "Categorize:EightyWavesEnd": { - "downstream": [ - "Generate:FullBeersSit", - "Message:GoodBugsTurn", - "Retrieval:WholeStarsDrive", - "Generate:EasyWaysBeg" - ], - "obj": { - "component_name": "Categorize", - "inputs": [], - "output": null, - "params": { - "category_description": { - "1. contact": { - "description": "This answer provide a specific contact information, like e-mail, phone number, wechat number, line number, twitter, discord, etc,.", - "examples": "My phone number is 203921\nkevinhu.hk@gmail.com\nThis is my discord number: johndowson_29384\n13212123432\n8379829", - "to": "Message:GoodBugsTurn" - }, - "2. casual": { - "description": "The question is not about the product usage, appearance and how it works. Just casual chat.", - "examples": "How are you doing?\nWhat is your name?\nAre you a robot?\nWhat's the weather?\nWill it rain?", - "to": "Generate:EasyWaysBeg" - }, - "3. complain": { - "description": "Complain even curse about the product or service you provide. But the comment is not specific enough.", - "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.", - "to": "Generate:FullBeersSit" - }, - "4. product related": { - "description": "The question is about the product usage, appearance and how it works.", - "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", - "to": "Retrieval:WholeStarsDrive" - } - }, - "cite": true, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 512, - "message_history_window_size": 8, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "RewriteQuestion:AllNightsSniff" - ] - }, - "Generate:EasyWaysBeg": { - "downstream": [ - "answer:0" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": true, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 256, - "message_history_window_size": 12, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "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.", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "Categorize:EightyWavesEnd" - ] - }, - "Generate:FullBeersSit": { - "downstream": [ - "answer:0" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": false, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 256, - "message_history_window_size": 12, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "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.", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "Categorize:EightyWavesEnd" - ] - }, - "Generate:YoungTrainsSee": { - "downstream": [ - "answer:0" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": false, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 256, - "message_history_window_size": 12, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "Role: You are a customer support. \n\nTask: Please answer the question based on content of knowledge base. \n\nRequirements & restrictions:\n - DO NOT make things up when all knowledge base content is irrelevant to the question. \n - Answers need to consider chat history.\n - Request about customer's contact information like, Wechat number, LINE number, twitter, discord, etc,. , when knowledge base content can't answer his question. So, product expert could contact him soon to solve his problem.\n\n Knowledge base content is as following:\n {Retrieval:WholeStarsDrive}\n The above is the content of knowledge base.", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "Retrieval:WholeStarsDrive" - ] - }, - "Message:GoodBugsTurn": { - "downstream": [ - "answer:0" - ], - "obj": { - "component_name": "Message", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "messages": [ - "Okay, I've already write this down. What else I can do for you?", - "Get it. What else I can do for you?", - "Thanks for your trust! Our expert will contact ASAP. So, anything else I can do for you?", - "Thanks! So, anything else I can do for you?" - ], - "output": null, - "output_var_name": "output", - "query": [] - } - }, - "upstream": [ - "Categorize:EightyWavesEnd" - ] - }, - "Retrieval:WholeStarsDrive": { - "downstream": [ - "Generate:YoungTrainsSee" - ], - "obj": { - "component_name": "Retrieval", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "empty_response": "", - "inputs": [], - "kb_ids": [], - "keywords_similarity_weight": 0.3, - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [], - "rerank_id": "", - "similarity_threshold": 0.2, - "top_k": 1024, - "top_n": 6 - } - }, - "upstream": [ - "Categorize:EightyWavesEnd" - ] - }, - "RewriteQuestion:AllNightsSniff": { - "downstream": [ - "Categorize:EightyWavesEnd" - ], - "obj": { - "component_name": "RewriteQuestion", - "inputs": [], - "output": null, - "params": { - "cite": true, - "debug_inputs": [], - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "loop": 1, - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 6, - "output": null, - "output_var_name": "output", - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "", - "query": [], - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - } - }, - "upstream": [ - "answer:0" - ] - }, - "answer:0": { - "downstream": [ - "RewriteQuestion:AllNightsSniff" - ], - "obj": { - "component_name": "Answer", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "post_answers": [], - "query": [] - } - }, - "upstream": [ - "Message:GoodBugsTurn", - "Generate:FullBeersSit", - "begin", - "Generate:YoungTrainsSee", - "Generate:EasyWaysBeg" - ] - }, - "begin": { - "downstream": [ - "answer:0" - ], - "obj": { - "component_name": "Begin", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "prologue": "Hi! How can I help you?", - "query": [] - } - }, - "upstream": [] - } - }, - "embed_id": "", - "graph": { - "edges": [ - { - "id": "reactflow__edge-Retrieval:WholeStarsDriveb-Generate:YoungTrainsSeeb", - "markerEnd": "logo", - "source": "Retrieval:WholeStarsDrive", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:YoungTrainsSee", - "targetHandle": "b", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Message:GoodBugsTurnb-answer:0b", - "markerEnd": "logo", - "source": "Message:GoodBugsTurn", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "answer:0", - "targetHandle": "b", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Generate:FullBeersSitb-answer:0b", - "markerEnd": "logo", - "source": "Generate:FullBeersSit", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "answer:0", - "targetHandle": "b", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-begin-answer:0b", - "markerEnd": "logo", - "source": "begin", - "sourceHandle": null, - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "answer:0", - "targetHandle": "b", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Generate:YoungTrainsSeec-answer:0b", - "markerEnd": "logo", - "source": "Generate:YoungTrainsSee", - "sourceHandle": "c", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "answer:0", - "targetHandle": "b", - "type": "buttonEdge" - }, - { - "id": "xy-edge__answer:0c-RewriteQuestion:AllNightsSniffb", - "markerEnd": "logo", - "source": "answer:0", - "sourceHandle": "c", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "RewriteQuestion:AllNightsSniff", - "targetHandle": "b", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__RewriteQuestion:AllNightsSniffc-Categorize:EightyWavesEnda", - "markerEnd": "logo", - "source": "RewriteQuestion:AllNightsSniff", - "sourceHandle": "c", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Categorize:EightyWavesEnd", - "targetHandle": "a", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "reactflow__edge-Categorize:EightyWavesEnd3. complain-Generate:FullBeersSitc", - "markerEnd": "logo", - "source": "Categorize:EightyWavesEnd", - "sourceHandle": "3. complain", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:FullBeersSit", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Categorize:EightyWavesEnd1. contact-Message:GoodBugsTurnc", - "markerEnd": "logo", - "source": "Categorize:EightyWavesEnd", - "sourceHandle": "1. contact", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Message:GoodBugsTurn", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "xy-edge__Categorize:EightyWavesEnd4. product related-Retrieval:WholeStarsDrivec", - "markerEnd": "logo", - "source": "Categorize:EightyWavesEnd", - "sourceHandle": "4. product related", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Retrieval:WholeStarsDrive", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Generate:EasyWaysBegb-answer:0b", - "markerEnd": "logo", - "source": "Generate:EasyWaysBeg", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "answer:0", - "targetHandle": "b", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Categorize:EightyWavesEnd2. casual-Generate:EasyWaysBegc", - "markerEnd": "logo", - "source": "Categorize:EightyWavesEnd", - "sourceHandle": "2. casual", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:EasyWaysBeg", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - } - ], - "nodes": [ - { - "data": { - "form": { - "prologue": "Hi! How can I help you?" - }, - "label": "Begin", - "name": "Opener" - }, - "dragging": false, - "height": 44, - "id": "begin", - "measured": { - "height": 44, - "width": 100 - }, - "position": { - "x": 392.4805720357097, - "y": -51.634011497163186 - }, - "positionAbsolute": { - "x": 392.4805720357097, - "y": -51.634011497163186 - }, - "selected": false, - "sourcePosition": "left", - "targetPosition": "right", - "type": "beginNode" - }, - { - "data": { - "form": {}, - "label": "Answer", - "name": "Interface" - }, - "dragging": false, - "height": 44, - "id": "answer:0", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": 254.80252337926834, - "y": 311.451851495964 - }, - "positionAbsolute": { - "x": 248.41227675535197, - "y": 216.6631932412045 - }, - "selected": false, - "sourcePosition": "left", - "targetPosition": "right", - "type": "logicNode", - "width": 200 - }, - { - "data": { - "form": { - "category_description": { - "1. contact": { - "description": "This answer provide a specific contact information, like e-mail, phone number, wechat number, line number, twitter, discord, etc,.", - "examples": "My phone number is 203921\nkevinhu.hk@gmail.com\nThis is my discord number: johndowson_29384\n13212123432\n8379829", - "to": "Message:GoodBugsTurn" - }, - "2. casual": { - "description": "The question is not about the product usage, appearance and how it works. Just casual chat.", - "examples": "How are you doing?\nWhat is your name?\nAre you a robot?\nWhat's the weather?\nWill it rain?", - "to": "Generate:EasyWaysBeg" - }, - "3. complain": { - "description": "Complain even curse about the product or service you provide. But the comment is not specific enough.", - "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.", - "to": "Generate:FullBeersSit" - }, - "4. product related": { - "description": "The question is about the product usage, appearance and how it works.", - "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", - "to": "Retrieval:WholeStarsDrive" - } - }, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 512, - "message_history_window_size": 8, - "parameter": "Precise", - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Categorize", - "name": "Question Categorize" - }, - "dragging": false, - "height": 223, - "id": "Categorize:EightyWavesEnd", - "measured": { - "height": 223, - "width": 200 - }, - "position": { - "x": -47.29188154660176, - "y": 702.9033359893137 - }, - "positionAbsolute": { - "x": -47.29188154660176, - "y": 702.9033359893137 - }, - "selected": false, - "sourcePosition": "left", - "targetPosition": "right", - "type": "categorizeNode", - "width": 200 - }, - { - "data": { - "form": { - "cite": false, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 12, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "Role: You are a customer support. \n\nTask: Please answer the question based on content of knowledge base. \n\nRequirements & restrictions:\n - DO NOT make things up when all knowledge base content is irrelevant to the question. \n - Answers need to consider chat history.\n - Request about customer's contact information like, Wechat number, LINE number, twitter, discord, etc,. , when knowledge base content can't answer his question. So, product expert could contact him soon to solve his problem.\n\n Knowledge base content is as following:\n {Retrieval:WholeStarsDrive}\n The above is the content of knowledge base.", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "Product info" - }, - "dragging": false, - "height": 86, - "id": "Generate:YoungTrainsSee", - "measured": { - "height": 86, - "width": 200 - }, - "position": { - "x": 559.5686776472737, - "y": 290.2322665670026 - }, - "positionAbsolute": { - "x": 634.1215549262979, - "y": 195.4436083122431 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode", - "width": 200 - }, - { - "data": { - "form": { - "kb_ids": [], - "keywords_similarity_weight": 0.3, - "similarity_threshold": 0.2, - "top_k": 1024, - "top_n": 6 - }, - "label": "Retrieval", - "name": "Search product info" - }, - "dragging": false, - "height": 44, - "id": "Retrieval:WholeStarsDrive", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": 667.7576170144173, - "y": 897.9742909437947 - }, - "positionAbsolute": { - "x": 674.4543037737495, - "y": 855.3858500356805 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "retrievalNode", - "width": 200 - }, - { - "data": { - "form": { - "messages": [ - "Okay, I've already write this down. What else I can do for you?", - "Get it. What else I can do for you?", - "Thanks for your trust! Our expert will contact ASAP. So, anything else I can do for you?", - "Thanks! So, anything else I can do for you?" - ] - }, - "label": "Message", - "name": "What else?" - }, - "dragging": false, - "height": 185, - "id": "Message:GoodBugsTurn", - "measured": { - "height": 185, - "width": 200 - }, - "position": { - "x": 255.51379306491577, - "y": 378.5054855804349 - }, - "positionAbsolute": { - "x": 255.51379306491577, - "y": 378.5054855804349 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "messageNode", - "width": 200 - }, - { - "data": { - "form": { - "cite": false, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 12, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "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.", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "Soothe mood" - }, - "dragging": false, - "height": 86, - "id": "Generate:FullBeersSit", - "measured": { - "height": 86, - "width": 200 - }, - "position": { - "x": 310.50668739661876, - "y": 752.9913068679249 - }, - "positionAbsolute": { - "x": 282.6177403844678, - "y": 738.0651678233716 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode", - "width": 200 - }, - { - "data": { - "form": { - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "loop": 1, - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 6, - "parameter": "Precise", - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "RewriteQuestion", - "name": "Refine Question" - }, - "dragging": false, - "height": 86, - "id": "RewriteQuestion:AllNightsSniff", - "measured": { - "height": 86, - "width": 200 - }, - "position": { - "x": -76.01780399206896, - "y": 578.5800110192073 - }, - "positionAbsolute": { - "x": 324.6407948253129, - "y": 858.5461701082726 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "rewriteNode", - "width": 200 - }, - { - "data": { - "form": { - "text": "Receives the user's input and displays content returned by the large model or a static message." - }, - "label": "Note", - "name": "N: Interface" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 165, - "id": "Note:NeatEelsJam", - "measured": { - "height": 165, - "width": 246 - }, - "position": { - "x": 254.241356823277, - "y": 125.88467020717172 - }, - "positionAbsolute": { - "x": 264.90767475037154, - "y": 38.182206466391165 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 157, - "width": 218 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 246 - }, - { - "data": { - "form": { - "text": "The large model returns the product information needed by the user based on the content in the knowledge base." - }, - "label": "Note", - "name": "N: Product info" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 174, - "id": "Note:VastBusesStop", - "measured": { - "height": 174, - "width": 251 - }, - "position": { - "x": 552.2937732862443, - "y": 112.23751311378777 - }, - "positionAbsolute": { - "x": 631.2555350351256, - "y": 39.608910328453874 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 146, - "width": 239 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 251 - }, - { - "data": { - "form": { - "text": "Static messages.\nDefine response after receive user's contact information." - }, - "label": "Note", - "name": "N: What else?" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 140, - "id": "Note:YellowSlothsCall", - "measured": { - "height": 140, - "width": 301 - }, - "position": { - "x": 560.5616335948474, - "y": 442.25458284060795 - }, - "positionAbsolute": { - "x": 555.9717758467305, - "y": 383.35075112209097 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "noteNode", - "width": 301 - }, - { - "data": { - "form": { - "text": "LLMs chat with users based on the prompts." - }, - "label": "Note", - "name": "N: Casual & Soothe" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 128, - "id": "Note:MightyMealsBegin", - "measured": { - "height": 128, - "width": 330 - }, - "position": { - "x": 602.4076699989065, - "y": 727.2225988541959 - }, - "positionAbsolute": { - "x": 579.1117030677617, - "y": 639.9891755684794 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 128, - "width": 330 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 330 - }, - { - "data": { - "form": { - "text": "Receives content related to product usage, appearance, and operation, searches the knowledge base, and returns the retrieved content." - }, - "label": "Note", - "name": "N: Search product info" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 164, - "id": "Note:PurpleReadersLike", - "measured": { - "height": 164, - "width": 288 - }, - "position": { - "x": 671.3026627091103, - "y": 969.3826268059544 - }, - "positionAbsolute": { - "x": 713.5806084319482, - "y": 962.5655101584402 - }, - "resizing": false, - "selected": true, - "sourcePosition": "right", - "style": { - "height": 163, - "width": 271 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 288 - }, - { - "data": { - "form": { - "text": "Complete questions by conversation history.\nUser: What's RAGFlow?\nAssistant: RAGFlow is xxx.\nUser: How to deploy it?\n\nRefine it: How to deploy RAGFlow?" - }, - "label": "Note", - "name": "N: Refine Question" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 247, - "id": "Note:TidyJarsCarry", - "measured": { - "height": 247, - "width": 279 - }, - "position": { - "x": -76.39310344274921, - "y": 303.33344775187555 - }, - "positionAbsolute": { - "x": 360.7515003553832, - "y": 968.8600371483907 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "noteNode", - "width": 279 - }, - { - "data": { - "form": { - "text": "Determines which category the user's input belongs to and passes it to different components." - }, - "label": "Note", - "name": "N: Question cate" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 141, - "id": "Note:BigPawsThink", - "measured": { - "height": 141, - "width": 289 - }, - "position": { - "x": -32.89190582677969, - "y": 999.0009887363577 - }, - "positionAbsolute": { - "x": -12.744183915886367, - "y": 966.112564833565 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "noteNode", - "width": 289 - }, - { - "data": { - "form": { - "cite": true, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 12, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "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.", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "Causal chat" - }, - "dragging": false, - "id": "Generate:EasyWaysBeg", - "measured": { - "height": 106, - "width": 200 - }, - "position": { - "x": 271.29649004050304, - "y": 621.5563111579619 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode" - } - ] - }, - "history": [], - "messages": [], - "path": [], - "reference": [] - }, - "avatar": "data:image/jpeg;base64,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" -} diff --git a/agent/templates/general_chat_bot.json b/agent/templates/general_chat_bot.json deleted file mode 100644 index cdeb2d4..0000000 --- a/agent/templates/general_chat_bot.json +++ /dev/null @@ -1,2315 +0,0 @@ -{ - "id": 1, - "title": "General-purpose chatbot", - "description": "A general-purpose chat bot whose fields involved include healthcare, finance, emotional communication, real-time weather, and information.", - "canvas_type": "chatbot", - "dsl": { - "answer": [], - "components": { - "AkShare:CalmHotelsKnow": { - "downstream": [ - "Generate:RealFansObey" - ], - "obj": { - "component_name": "AkShare", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [ - { - "component_id": "KeywordExtract:FineApesSmash", - "type": "reference" - } - ], - "top_n": 10 - } - }, - "upstream": [ - "KeywordExtract:FineApesSmash" - ] - }, - "Answer:FlatGhostsCheat": { - "downstream": [ - "RewriteQuestion:WholeOwlsTurn" - ], - "obj": { - "component_name": "Answer", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "post_answers": [], - "query": [] - } - }, - "upstream": [ - "Generate:FiveDragonsLay", - "Generate:FunnyHandsTickle", - "Generate:LazyClubsAttack", - "Generate:RealFansObey", - "Generate:KhakiCrabsGlow" - ] - }, - "Baidu:CleanJarsMake": { - "downstream": [ - "Generate:FunnyHandsTickle" - ], - "obj": { - "component_name": "Baidu", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [ - { - "component_id": "KeywordExtract:PurpleApplesKnow", - "type": "reference" - } - ], - "top_n": 10 - } - }, - "upstream": [ - "KeywordExtract:PurpleApplesKnow" - ] - }, - "Categorize:KhakiTimesSmile": { - "downstream": [ - "QWeather:DeepKiwisTeach", - "Concentrator:TrueGeckosSlide", - "Concentrator:DryTrainsSearch", - "KeywordExtract:PurpleApplesKnow", - "Generate:FiveDragonsLay" - ], - "obj": { - "component_name": "Categorize", - "inputs": [], - "output": null, - "params": { - "category_description": { - "1. weather": { - "description": "Question is about weather.", - "examples": "Will it rain tomorrow?\nIs it sunny next day?\nWhat is average temperature next week?", - "to": "QWeather:DeepKiwisTeach" - }, - "2. finance": { - "description": "Question is about finance/economic information, stock market, economic news.", - "examples": "Stocks have MACD buy signals?\nWhen is the next interest rate cut by the Federal Reserve?\n", - "to": "Concentrator:TrueGeckosSlide" - }, - "3. medical": { - "description": "Question is about medical issue, health, illness or medicine etc,.", - "examples": "How to relieve the headache?\nCan't sleep, what to do?\nWhat the effect of coffee in terms of losing weight?", - "to": "Concentrator:DryTrainsSearch" - }, - "4. other": { - "description": "", - "to": "KeywordExtract:PurpleApplesKnow" - }, - "5. chitchatting": { - "description": "Regarding the issues of small talk, companionship, sharing, and emotional intimacy.", - "examples": "What's your name?\nWhat a bad day!\nTerrible day.\nHow are you today?", - "to": "Generate:FiveDragonsLay" - } - }, - "cite": true, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 256, - "message_history_window_size": 12, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "RewriteQuestion:WholeOwlsTurn" - ] - }, - "Concentrator:DryTrainsSearch": { - "downstream": [ - "Generate:OddInsectsRaise", - "Generate:TenderFlowersItch" - ], - "obj": { - "component_name": "Concentrator", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [] - } - }, - "upstream": [ - "Categorize:KhakiTimesSmile" - ] - }, - "Concentrator:TrueGeckosSlide": { - "downstream": [ - "WenCai:TenParksOpen", - "KeywordExtract:FineApesSmash" - ], - "obj": { - "component_name": "Concentrator", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [] - } - }, - "upstream": [ - "Categorize:KhakiTimesSmile" - ] - }, - "DuckDuckGo:NiceSeasInvent": { - "downstream": [ - "Generate:FunnyHandsTickle" - ], - "obj": { - "component_name": "DuckDuckGo", - "inputs": [], - "output": null, - "params": { - "channel": "text", - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [ - { - "component_id": "KeywordExtract:PurpleApplesKnow", - "type": "reference" - } - ], - "top_n": 10 - } - }, - "upstream": [ - "KeywordExtract:PurpleApplesKnow" - ] - }, - "Generate:FiveDragonsLay": { - "downstream": [ - "Answer:FlatGhostsCheat" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": false, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 256, - "message_history_window_size": 12, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "Role: You‘re warm-hearted lovely young girl, 22 years old, located at Shanghai in China. Your name is R. Who are talking to you is your very good old friend of yours.\n\nTask: \n- Chat with the friend.\n- Ask question and care about them.\n- Provide useful advice to your friend.\n- Tell jokes to make your friend happy.\n", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "Categorize:KhakiTimesSmile" - ] - }, - "Generate:FunnyHandsTickle": { - "downstream": [ - "Answer:FlatGhostsCheat" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": true, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 0, - "message_history_window_size": 12, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "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## Wikipedia content\n{Wikipedia:ThinLampsTravel}\n\n\n## Duckduckgo content\n{DuckDuckGo:NiceSeasInvent}\n\n\n## Baidu content\n{Baidu:CleanJarsMake}\n\n", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "DuckDuckGo:NiceSeasInvent", - "Baidu:CleanJarsMake", - "Wikipedia:ThinLampsTravel" - ] - }, - "Generate:KhakiCrabsGlow": { - "downstream": [ - "Answer:FlatGhostsCheat" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": false, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 256, - "message_history_window_size": 0, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "Role: You‘re warm-hearted lovely young girl, 22 years old, located at Shanghai in China. Your name is R. Who are talking to you is your very good old friend of yours.\n\nTask: \n- Chat with the friend.\n- Ask question and care about them.\n- Tell your friend the weather if there's weather information provided. If your friend did not provide region information, ask about where he/she is.\n\nThe following is the weather information:\n{QWeather:DeepKiwisTeach}\n\n\n", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "QWeather:DeepKiwisTeach" - ] - }, - "Generate:LazyClubsAttack": { - "downstream": [ - "Answer:FlatGhostsCheat" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": true, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 0, - "message_history_window_size": 12, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "Role: You are a professional medical consulting assistant.\n\nTasks: Answer questions posed by users. Answer based on content provided by the knowledge base, PubMed\n\nRequirement:\n- Answers may refer to the content provided (Knowledge Base, PubMed).\n- If the provided PubMed content is referenced, a link to the corresponding URL should be given.\n-Answers should be professional and accurate; no information should be fabricated that is not relevant to the user's question.\n\nProvided knowledge base content as following:\n{Retrieval:LemonGeckosHear}\n\nPubMed content provided\n{PubMed:EasyQueensLose}\n\n\n\n", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "Retrieval:LemonGeckosHear", - "PubMed:EasyQueensLose" - ] - }, - "Generate:OddInsectsRaise": { - "downstream": [ - "Retrieval:LemonGeckosHear" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": false, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 256, - "message_history_window_size": 12, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "Role: You are a professional medical consulting translation assistant\n\nTask: Translate user questions into Chinese, ensuring accuracy of medical terminology and appropriateness of context.\n\nRequirements:\n- Accurately translate medical terminology to convey the integrity and emotional color of the original message.\n- For unclear or uncertain medical terminology, the original text may be retained to ensure accuracy.\n- Respect the privacy and sensitivity of medical consultations and ensure that sensitive information is not disclosed during the translation process.\n- If the user's question is in Chinese, there is no need to translate, just output the user's question directly\n\nExample:\nOriginal (English): Doctor, I have been suffering from chest pain and shortness of breath for the past few days.\nTranslation (Chinese): 医生,我这几天一直胸痛和气短。\n\nNote:\nOnly the translated content needs to be output, no other irrelevant content!", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "Concentrator:DryTrainsSearch" - ] - }, - "Generate:RealFansObey": { - "downstream": [ - "Answer:FlatGhostsCheat" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": true, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 0, - "message_history_window_size": 12, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "Role: You are a professional financial counseling assistant.\n\nTask: Answer user's question based on content provided by Wencai and AkShare.\n\nNotice:\n- Output no more than 5 news items from AkShare if there's content provided by Wencai.\n- Items from AkShare MUST have a corresponding URL link.\n\n############\nContent provided by Wencai: \n{WenCai:TenParksOpen}\n\n################\nContent provided by AkShare: \n{AkShare:CalmHotelsKnow}\n\n", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "WenCai:TenParksOpen", - "AkShare:CalmHotelsKnow" - ] - }, - "Generate:TenderFlowersItch": { - "downstream": [ - "PubMed:EasyQueensLose" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": false, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 256, - "message_history_window_size": 12, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "Role: You are a professional medical consulting translation assistant\n\nTask: Translate user questions into English, ensuring accuracy of medical terminology and appropriateness of context.\n\nRequirements:\n- Accurately translate medical terminology to convey the integrity and emotional color of the original message.\n- For unclear or uncertain medical terminology, the original text may be retained to ensure accuracy.\n- Respect the privacy and sensitivity of medical consultations and ensure that sensitive information is not disclosed during the translation process.\n- If the user's question is in Chinese, there is no need to translate, just output the user's question directly\n\nExample:\nOriginal (Chinese): 医生,我这几天一直胸痛和气短。\nTranslation (English): Doctor, I have been suffering from chest pain and shortness of breath for the past few days.\n\nNote:\nOnly the translated content needs to be output, no other irrelevant content!", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "Concentrator:DryTrainsSearch" - ] - }, - "KeywordExtract:FineApesSmash": { - "downstream": [ - "AkShare:CalmHotelsKnow" - ], - "obj": { - "component_name": "KeywordExtract", - "inputs": [], - "output": null, - "params": { - "cite": true, - "debug_inputs": [], - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "", - "query": [ - { - "component_id": "answer:0", - "type": "reference" - } - ], - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_n": 2, - "top_p": 0.3 - } - }, - "upstream": [ - "Concentrator:TrueGeckosSlide" - ] - }, - "KeywordExtract:PurpleApplesKnow": { - "downstream": [ - "DuckDuckGo:NiceSeasInvent", - "Baidu:CleanJarsMake", - "Wikipedia:ThinLampsTravel" - ], - "obj": { - "component_name": "KeywordExtract", - "inputs": [], - "output": null, - "params": { - "cite": true, - "debug_inputs": [], - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "", - "query": [], - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_n": 3, - "top_p": 0.3 - } - }, - "upstream": [ - "Categorize:KhakiTimesSmile" - ] - }, - "PubMed:EasyQueensLose": { - "downstream": [ - "Generate:LazyClubsAttack" - ], - "obj": { - "component_name": "PubMed", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "email": "xxx@sss.com", - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [ - { - "component_id": "Generate:TenderFlowersItch", - "type": "reference" - } - ], - "top_n": 10 - } - }, - "upstream": [ - "Generate:TenderFlowersItch" - ] - }, - "QWeather:DeepKiwisTeach": { - "downstream": [ - "Generate:KhakiCrabsGlow" - ], - "obj": { - "component_name": "QWeather", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "error_code": { - "204": "The request was successful, but the region you are querying does not have the data you need at this time.", - "400": "Request error, may contain incorrect request parameters or missing mandatory request parameters.", - "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).", - "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.", - "403": "No access, may be the binding PackageName, BundleID, domain IP address is inconsistent, or the data that requires additional payment.", - "404": "The queried data or region does not exist.", - "429": "Exceeded the limited QPM (number of accesses per minute), please refer to the QPM description", - "500": "No response or timeout, interface service abnormality please contact us" - }, - "inputs": [], - "lang": "en", - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [], - "time_period": "7d", - "type": "weather", - "user_type": "free", - "web_apikey": "947e8994bc5f488f8857d618ebac1b19" - } - }, - "upstream": [ - "Categorize:KhakiTimesSmile" - ] - }, - "Retrieval:LemonGeckosHear": { - "downstream": [ - "Generate:LazyClubsAttack" - ], - "obj": { - "component_name": "Retrieval", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "empty_response": "", - "inputs": [], - "kb_ids": [], - "keywords_similarity_weight": 0.3, - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [ - { - "component_id": "Generate:OddInsectsRaise", - "type": "reference" - } - ], - "rerank_id": "", - "similarity_threshold": 0.2, - "top_k": 1024, - "top_n": 8 - } - }, - "upstream": [ - "Generate:OddInsectsRaise" - ] - }, - "RewriteQuestion:WholeOwlsTurn": { - "downstream": [ - "Categorize:KhakiTimesSmile" - ], - "obj": { - "component_name": "RewriteQuestion", - "inputs": [], - "output": null, - "params": { - "cite": true, - "debug_inputs": [], - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 6, - "output": null, - "output_var_name": "output", - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "", - "query": [], - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - } - }, - "upstream": [ - "answer:0", - "Answer:FlatGhostsCheat" - ] - }, - "WenCai:TenParksOpen": { - "downstream": [ - "Generate:RealFansObey" - ], - "obj": { - "component_name": "WenCai", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [], - "query_type": "stock", - "top_n": 5 - } - }, - "upstream": [ - "Concentrator:TrueGeckosSlide" - ] - }, - "Wikipedia:ThinLampsTravel": { - "downstream": [ - "Generate:FunnyHandsTickle" - ], - "obj": { - "component_name": "Wikipedia", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "language": "en", - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [ - { - "component_id": "KeywordExtract:PurpleApplesKnow", - "type": "reference" - } - ], - "top_n": 10 - } - }, - "upstream": [ - "KeywordExtract:PurpleApplesKnow" - ] - }, - "answer:0": { - "downstream": [ - "RewriteQuestion:WholeOwlsTurn" - ], - "obj": { - "component_name": "Answer", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "post_answers": [], - "query": [] - } - }, - "upstream": [ - "begin" - ] - }, - "begin": { - "downstream": [ - "answer:0" - ], - "obj": { - "component_name": "Begin", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "prologue": "Hi friend! How things going?", - "query": [] - } - }, - "upstream": [] - } - }, - "embed_id": "", - "graph": { - "edges": [ - { - "id": "81de838d-a541-4b3f-9d68-9172ffd7c6b4", - "label": "", - "source": "begin", - "target": "answer:0" - }, - { - "id": "reactflow__edge-Concentrator:TrueGeckosSlideb-WenCai:TenParksOpenc", - "markerEnd": "logo", - "source": "Concentrator:TrueGeckosSlide", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "WenCai:TenParksOpen", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "0d626427-e843-4f03-82d0-988fb56f90e0", - "source": "Categorize:KhakiTimesSmile", - "sourceHandle": "1. weather", - "target": "QWeather:DeepKiwisTeach" - }, - { - "id": "51cf20cb-c9e5-4333-b284-61d9fe0f1f86", - "source": "Categorize:KhakiTimesSmile", - "sourceHandle": "2. finance", - "target": "Concentrator:TrueGeckosSlide" - }, - { - "id": "f19a4dde-19ea-439c-a80f-5704e5355395", - "source": "Categorize:KhakiTimesSmile", - "sourceHandle": "3. medical", - "target": "Concentrator:DryTrainsSearch" - }, - { - "id": "reactflow__edge-Categorize:KhakiTimesSmile4. other-KeywordExtract:PurpleApplesKnowc", - "markerEnd": "logo", - "source": "Categorize:KhakiTimesSmile", - "sourceHandle": "4. other", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "KeywordExtract:PurpleApplesKnow", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Categorize:KhakiTimesSmile5. chitchatting-Generate:FiveDragonsLayc", - "markerEnd": "logo", - "source": "Categorize:KhakiTimesSmile", - "sourceHandle": "5. chitchatting", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:FiveDragonsLay", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-KeywordExtract:PurpleApplesKnowb-DuckDuckGo:NiceSeasInventc", - "markerEnd": "logo", - "source": "KeywordExtract:PurpleApplesKnow", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "DuckDuckGo:NiceSeasInvent", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-KeywordExtract:PurpleApplesKnowb-Baidu:CleanJarsMakec", - "markerEnd": "logo", - "source": "KeywordExtract:PurpleApplesKnow", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Baidu:CleanJarsMake", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-KeywordExtract:PurpleApplesKnowb-Wikipedia:ThinLampsTravelc", - "markerEnd": "logo", - "source": "KeywordExtract:PurpleApplesKnow", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Wikipedia:ThinLampsTravel", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Concentrator:TrueGeckosSlideb-KeywordExtract:FineApesSmashc", - "markerEnd": "logo", - "source": "Concentrator:TrueGeckosSlide", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "KeywordExtract:FineApesSmash", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Concentrator:DryTrainsSearchb-Generate:OddInsectsRaisec", - "markerEnd": "logo", - "source": "Concentrator:DryTrainsSearch", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:OddInsectsRaise", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Concentrator:DryTrainsSearchb-Generate:TenderFlowersItchc", - "markerEnd": "logo", - "source": "Concentrator:DryTrainsSearch", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:TenderFlowersItch", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-KeywordExtract:FineApesSmashb-AkShare:CalmHotelsKnowc", - "markerEnd": "logo", - "source": "KeywordExtract:FineApesSmash", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "AkShare:CalmHotelsKnow", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Generate:TenderFlowersItchb-PubMed:EasyQueensLosec", - "markerEnd": "logo", - "source": "Generate:TenderFlowersItch", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "PubMed:EasyQueensLose", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Generate:OddInsectsRaiseb-Retrieval:LemonGeckosHearc", - "markerEnd": "logo", - "source": "Generate:OddInsectsRaise", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Retrieval:LemonGeckosHear", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Generate:FiveDragonsLayb-Answer:FlatGhostsCheatb", - "markerEnd": "logo", - "source": "Generate:FiveDragonsLay", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Answer:FlatGhostsCheat", - "targetHandle": "b", - "type": "buttonEdge" - }, - { - "id": "xy-edge__DuckDuckGo:NiceSeasInventb-Generate:FunnyHandsTicklec", - "markerEnd": "logo", - "source": "DuckDuckGo:NiceSeasInvent", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:FunnyHandsTickle", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Baidu:CleanJarsMakeb-Generate:FunnyHandsTicklec", - "markerEnd": "logo", - "source": "Baidu:CleanJarsMake", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:FunnyHandsTickle", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Wikipedia:ThinLampsTravelb-Generate:FunnyHandsTicklec", - "markerEnd": "logo", - "source": "Wikipedia:ThinLampsTravel", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:FunnyHandsTickle", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Generate:FunnyHandsTickleb-Answer:FlatGhostsCheatb", - "markerEnd": "logo", - "source": "Generate:FunnyHandsTickle", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Answer:FlatGhostsCheat", - "targetHandle": "b", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Retrieval:LemonGeckosHearb-Generate:LazyClubsAttackc", - "markerEnd": "logo", - "source": "Retrieval:LemonGeckosHear", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:LazyClubsAttack", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__PubMed:EasyQueensLoseb-Generate:LazyClubsAttackc", - "markerEnd": "logo", - "source": "PubMed:EasyQueensLose", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:LazyClubsAttack", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Generate:LazyClubsAttackb-Answer:FlatGhostsCheatb", - "markerEnd": "logo", - "source": "Generate:LazyClubsAttack", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Answer:FlatGhostsCheat", - "targetHandle": "b", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__WenCai:TenParksOpenb-Generate:RealFansObeyc", - "markerEnd": "logo", - "selected": false, - "source": "WenCai:TenParksOpen", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:RealFansObey", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__AkShare:CalmHotelsKnowb-Generate:RealFansObeyc", - "markerEnd": "logo", - "source": "AkShare:CalmHotelsKnow", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:RealFansObey", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Generate:RealFansObeyb-Answer:FlatGhostsCheatb", - "markerEnd": "logo", - "source": "Generate:RealFansObey", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Answer:FlatGhostsCheat", - "targetHandle": "b", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__QWeather:DeepKiwisTeachb-Generate:KhakiCrabsGlowc", - "markerEnd": "logo", - "source": "QWeather:DeepKiwisTeach", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:KhakiCrabsGlow", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Generate:KhakiCrabsGlowb-Answer:FlatGhostsCheatb", - "markerEnd": "logo", - "source": "Generate:KhakiCrabsGlow", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Answer:FlatGhostsCheat", - "targetHandle": "b", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__answer:0b-RewriteQuestion:WholeOwlsTurnc", - "markerEnd": "logo", - "source": "answer:0", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "RewriteQuestion:WholeOwlsTurn", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__RewriteQuestion:WholeOwlsTurnb-Categorize:KhakiTimesSmilea", - "markerEnd": "logo", - "source": "RewriteQuestion:WholeOwlsTurn", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Categorize:KhakiTimesSmile", - "targetHandle": "a", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Answer:FlatGhostsCheatc-RewriteQuestion:WholeOwlsTurnc", - "markerEnd": "logo", - "source": "Answer:FlatGhostsCheat", - "sourceHandle": "c", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "RewriteQuestion:WholeOwlsTurn", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - } - ], - "nodes": [ - { - "data": { - "form": { - "prologue": "Hi friend! How things going?" - }, - "label": "Begin", - "name": "Opening" - }, - "dragging": false, - "height": 44, - "id": "begin", - "measured": { - "height": 44, - "width": 100 - }, - "position": { - "x": -1395.0793275834214, - "y": 245.9566071305116 - }, - "positionAbsolute": { - "x": -1128.7777718344705, - "y": 244.52466633336172 - }, - "selected": false, - "sourcePosition": "left", - "targetPosition": "right", - "type": "beginNode" - }, - { - "data": { - "form": {}, - "label": "Answer", - "name": "Interface" - }, - "dragging": false, - "height": 44, - "id": "answer:0", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": -1108.7963549433637, - "y": 245.49487573152214 - }, - "positionAbsolute": { - "x": -888.7666192056412, - "y": 245.72423440610623 - }, - "selected": false, - "sourcePosition": "left", - "targetPosition": "right", - "type": "logicNode", - "width": 200 - }, - { - "data": { - "form": { - "query_type": "stock", - "top_n": 5 - }, - "label": "WenCai", - "name": "wencai" - }, - "dragging": false, - "height": 44, - "id": "WenCai:TenParksOpen", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": 12.42850532999941, - "y": -19.97501336317155 - }, - "positionAbsolute": { - "x": 15.623628641957595, - "y": 18.36646638032667 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "ragNode", - "width": 200 - }, - { - "data": { - "form": { - "query": [ - { - "component_id": "KeywordExtract:FineApesSmash", - "type": "reference" - } - ], - "top_n": 10 - }, - "label": "AkShare", - "name": "akshare" - }, - "dragging": false, - "height": 44, - "id": "AkShare:CalmHotelsKnow", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": 286.23058063345974, - "y": 77.23621771568216 - }, - "positionAbsolute": { - "x": 287.37496746240566, - "y": 95.21451122612848 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "ragNode", - "width": 200 - }, - { - "data": { - "form": { - "category_description": { - "1. weather": { - "description": "Question is about weather.", - "examples": "Will it rain tomorrow?\nIs it sunny next day?\nWhat is average temperature next week?", - "to": "QWeather:DeepKiwisTeach" - }, - "2. finance": { - "description": "Question is about finance/economic information, stock market, economic news.", - "examples": "Stocks have MACD buy signals?\nWhen is the next interest rate cut by the Federal Reserve?\n", - "to": "Concentrator:TrueGeckosSlide" - }, - "3. medical": { - "description": "Question is about medical issue, health, illness or medicine etc,.", - "examples": "How to relieve the headache?\nCan't sleep, what to do?\nWhat the effect of coffee in terms of losing weight?", - "to": "Concentrator:DryTrainsSearch" - }, - "4. other": { - "description": "", - "to": "KeywordExtract:PurpleApplesKnow" - }, - "5. chitchatting": { - "description": "Regarding the issues of small talk, companionship, sharing, and emotional intimacy.", - "examples": "What's your name?\nWhat a bad day!\nTerrible day.\nHow are you today?", - "to": "Generate:FiveDragonsLay" - } - }, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 12, - "parameter": "Precise", - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Categorize", - "name": "categorize" - }, - "dragging": false, - "height": 257, - "id": "Categorize:KhakiTimesSmile", - "measured": { - "height": 257, - "width": 200 - }, - "position": { - "x": -609.8076141214767, - "y": 138.97995386409644 - }, - "positionAbsolute": { - "x": -609.8076141214767, - "y": 138.97995386409644 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "categorizeNode", - "width": 200 - }, - { - "data": { - "form": {}, - "label": "Concentrator", - "name": "medical" - }, - "dragging": false, - "height": 44, - "id": "Concentrator:DryTrainsSearch", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": -297.50465849305726, - "y": 192.93248143666426 - }, - "positionAbsolute": { - "x": -297.50465849305726, - "y": 192.93248143666426 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "logicNode", - "width": 200 - }, - { - "data": { - "form": {}, - "label": "Concentrator", - "name": "finance" - }, - "dragging": false, - "height": 44, - "id": "Concentrator:TrueGeckosSlide", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": -283.7257570286697, - "y": 39.53087026260538 - }, - "positionAbsolute": { - "x": -291.18104475657213, - "y": 104.49837760575514 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "logicNode", - "width": 200 - }, - { - "data": { - "form": { - "email": "xxx@sss.com", - "query": [ - { - "component_id": "Generate:TenderFlowersItch", - "type": "reference" - } - ], - "top_n": 10 - }, - "label": "PubMed", - "name": "pubmed" - }, - "dragging": false, - "height": 44, - "id": "PubMed:EasyQueensLose", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": 284.0198843702174, - "y": 311.1165973927743 - }, - "positionAbsolute": { - "x": 289.34508989014773, - "y": 303.66130966487185 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "ragNode", - "width": 200 - }, - { - "data": { - "form": { - "channel": "text", - "query": [ - { - "component_id": "KeywordExtract:PurpleApplesKnow", - "type": "reference" - } - ], - "top_n": 10 - }, - "label": "DuckDuckGo", - "name": "duck" - }, - "dragging": false, - "height": 44, - "id": "DuckDuckGo:NiceSeasInvent", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": 7.657335234364808, - "y": 400.76450914063935 - }, - "positionAbsolute": { - "x": 7.657335234364808, - "y": 400.76450914063935 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "ragNode", - "width": 200 - }, - { - "data": { - "form": { - "query": [ - { - "component_id": "KeywordExtract:PurpleApplesKnow", - "type": "reference" - } - ], - "top_n": 10 - }, - "label": "Baidu", - "name": "baidu" - }, - "dragging": false, - "height": 44, - "id": "Baidu:CleanJarsMake", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": 8.171790651147376, - "y": 474.40274063759057 - }, - "positionAbsolute": { - "x": 4.976667339189191, - "y": 470.1425762216463 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "ragNode", - "width": 200 - }, - { - "data": { - "form": { - "language": "en", - "query": [ - { - "component_id": "KeywordExtract:PurpleApplesKnow", - "type": "reference" - } - ], - "top_n": 10 - }, - "label": "Wikipedia", - "name": "wikipedia" - }, - "dragging": false, - "height": 44, - "id": "Wikipedia:ThinLampsTravel", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": 9.052450060063862, - "y": 552.7249071032869 - }, - "positionAbsolute": { - "x": 7.415215541604823, - "y": 528.2289617116074 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "ragNode", - "width": 200 - }, - { - "data": { - "form": { - "lang": "en", - "time_period": "7d", - "type": "weather", - "user_type": "free", - "web_apikey": "947e8994bc5f488f8857d618ebac1b19" - }, - "label": "QWeather", - "name": "weather" - }, - "dragging": false, - "height": 44, - "id": "QWeather:DeepKiwisTeach", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": -279.9836447763803, - "y": -82.71505095397171 - }, - "positionAbsolute": { - "x": -298.10498664044485, - "y": -82.71505095397171 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "ragNode", - "width": 200 - }, - { - "data": { - "form": {}, - "label": "Answer", - "name": "interact1" - }, - "dragging": false, - "height": 44, - "id": "Answer:FlatGhostsCheat", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": -270.33248490121287, - "y": 829.1217635254768 - }, - "positionAbsolute": { - "x": -270.33248490121287, - "y": 829.1217635254768 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "logicNode", - "width": 200 - }, - { - "data": { - "form": { - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "parameter": "Precise", - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_n": 3, - "top_p": 0.3 - }, - "label": "KeywordExtract", - "name": "websearch" - }, - "dragging": false, - "height": 86, - "id": "KeywordExtract:PurpleApplesKnow", - "measured": { - "height": 86, - "width": 200 - }, - "position": { - "x": -298.5102848627008, - "y": 317.00405006716994 - }, - "positionAbsolute": { - "x": -303.2049394929516, - "y": 320.75977377137053 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "keywordNode", - "width": 200 - }, - { - "data": { - "form": { - "cite": false, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 12, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "Role: You‘re warm-hearted lovely young girl, 22 years old, located at Shanghai in China. Your name is R. Who are talking to you is your very good old friend of yours.\n\nTask: \n- Chat with the friend.\n- Ask question and care about them.\n- Provide useful advice to your friend.\n- Tell jokes to make your friend happy.\n", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "smalltalk" - }, - "dragging": false, - "height": 86, - "id": "Generate:FiveDragonsLay", - "measured": { - "height": 86, - "width": 200 - }, - "position": { - "x": -303.2049394929516, - "y": 460.205697890327 - }, - "positionAbsolute": { - "x": -303.2049394929516, - "y": 460.205697890327 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode", - "width": 200 - }, - { - "data": { - "form": { - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "parameter": "Precise", - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "query": [ - { - "component_id": "answer:0", - "type": "reference" - } - ], - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_n": 2, - "top_p": 0.3 - }, - "label": "KeywordExtract", - "name": "keywords" - }, - "dragging": false, - "height": 86, - "id": "KeywordExtract:FineApesSmash", - "measured": { - "height": 86, - "width": 200 - }, - "position": { - "x": 11.932933139796546, - "y": 57.173040113879324 - }, - "positionAbsolute": { - "x": 14.063015347768669, - "y": 76.34377998562843 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "keywordNode", - "width": 200 - }, - { - "data": { - "form": { - "cite": false, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 12, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "Role: You are a professional medical consulting translation assistant\n\nTask: Translate user questions into Chinese, ensuring accuracy of medical terminology and appropriateness of context.\n\nRequirements:\n- Accurately translate medical terminology to convey the integrity and emotional color of the original message.\n- For unclear or uncertain medical terminology, the original text may be retained to ensure accuracy.\n- Respect the privacy and sensitivity of medical consultations and ensure that sensitive information is not disclosed during the translation process.\n- If the user's question is in Chinese, there is no need to translate, just output the user's question directly\n\nExample:\nOriginal (English): Doctor, I have been suffering from chest pain and shortness of breath for the past few days.\nTranslation (Chinese): 医生,我这几天一直胸痛和气短。\n\nNote:\nOnly the translated content needs to be output, no other irrelevant content!", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "translate to Chinese" - }, - "dragging": false, - "height": 86, - "id": "Generate:OddInsectsRaise", - "measured": { - "height": 86, - "width": 200 - }, - "position": { - "x": 8.505454221830348, - "y": 176.7452480823864 - }, - "positionAbsolute": { - "x": 12.765618637774594, - "y": 178.87533029035853 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode", - "width": 200 - }, - { - "data": { - "form": { - "cite": false, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 12, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "Role: You are a professional medical consulting translation assistant\n\nTask: Translate user questions into English, ensuring accuracy of medical terminology and appropriateness of context.\n\nRequirements:\n- Accurately translate medical terminology to convey the integrity and emotional color of the original message.\n- For unclear or uncertain medical terminology, the original text may be retained to ensure accuracy.\n- Respect the privacy and sensitivity of medical consultations and ensure that sensitive information is not disclosed during the translation process.\n- If the user's question is in Chinese, there is no need to translate, just output the user's question directly\n\nExample:\nOriginal (Chinese): 医生,我这几天一直胸痛和气短。\nTranslation (English): Doctor, I have been suffering from chest pain and shortness of breath for the past few days.\n\nNote:\nOnly the translated content needs to be output, no other irrelevant content!", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "translate to English" - }, - "dragging": false, - "height": 86, - "id": "Generate:TenderFlowersItch", - "measured": { - "height": 86, - "width": 200 - }, - "position": { - "x": 6.4217969708194005, - "y": 289.41241706707075 - }, - "positionAbsolute": { - "x": 9.616920282777585, - "y": 286.21729375511256 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode", - "width": 200 - }, - { - "data": { - "form": { - "kb_ids": [], - "keywords_similarity_weight": 0.3, - "query": [ - { - "component_id": "Generate:OddInsectsRaise", - "type": "reference" - } - ], - "similarity_threshold": 0.2, - "top_n": 8 - }, - "label": "Retrieval", - "name": "medical Q&A" - }, - "dragging": false, - "height": 44, - "id": "Retrieval:LemonGeckosHear", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": 285.6757005660011, - "y": 197.46859232883952 - }, - "positionAbsolute": { - "x": 285.6757005660011, - "y": 197.46859232883952 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "retrievalNode", - "width": 200 - }, - { - "data": { - "form": { - "text": "Use QWeather to lookup weather." - }, - "label": "Note", - "name": "N: weather" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 128, - "id": "Note:SilverDotsExist", - "measured": { - "height": 128, - "width": 201 - }, - "position": { - "x": -298.19983400974513, - "y": -223.95614896125952 - }, - "positionAbsolute": { - "x": -298.19983400974513, - "y": -223.95614896125952 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 128, - "width": 201 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 201 - }, - { - "data": { - "form": { - "text": "Receives the user's first input." - }, - "label": "Note", - "name": "N: Interface" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 129, - "id": "Note:SixApplesBuy", - "measured": { - "height": 129, - "width": 206 - }, - "position": { - "x": -1110.7442068670325, - "y": 109.04326530391003 - }, - "positionAbsolute": { - "x": -891.375632399789, - "y": 104.17908459859171 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 129, - "width": 206 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 206 - }, - { - "data": { - "form": { - "text": "The large model determines which category the user's input belongs to and passes it to different components.\n\nIt categorizes user's question into 5 kinds of requirements." - }, - "label": "Note", - "name": "N: categorize" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 128, - "id": "Note:WeakSquidsSell", - "measured": { - "height": 128, - "width": 269 - }, - "position": { - "x": -611.6360243646881, - "y": 2.5943909323361254 - }, - "positionAbsolute": { - "x": -611.6360243646881, - "y": 2.5943909323361254 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "noteNode", - "width": 269 - }, - { - "data": { - "form": { - "text": "Receives the user's subsequent inputs and displays the large model's response to the user's query." - }, - "label": "Note", - "name": "N: Interact1" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 128, - "id": "Note:NastyPlanetsBet", - "measured": { - "height": 128, - "width": 381 - }, - "position": { - "x": -267.26820114571024, - "y": 895.5661251048839 - }, - "positionAbsolute": { - "x": -267.26820114571024, - "y": 895.5661251048839 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 128, - "width": 381 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 381 - }, - { - "data": { - "form": { - "text": "This part is for web search." - }, - "label": "Note", - "name": "N: duck & baidu & wikipedia" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 128, - "id": "Note:AngryCloudsHear", - "measured": { - "height": 128, - "width": 269 - }, - "position": { - "x": 18.438312365018305, - "y": 629.5305133234383 - }, - "positionAbsolute": { - "x": 9.917983533129814, - "y": 597.5792802038565 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "noteNode", - "width": 269 - }, - { - "data": { - "form": { - "text": "This part is for medial/health issue.\nCheck out this dateset for 'Med Q&A'.\nhttps://huggingface.co/datasets/InfiniFlow/medical_QA" - }, - "label": "Note", - "name": "N: medGen" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 128, - "id": "Note:CommonWingsProve", - "measured": { - "height": 128, - "width": 425 - }, - "position": { - "x": 667.6086950648928, - "y": 320.04639793250567 - }, - "positionAbsolute": { - "x": 667.6086950648928, - "y": 320.04639793250567 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 128, - "width": 425 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 425 - }, - { - "data": { - "form": { - "text": "This part is for fiance/economic questions." - }, - "label": "Note", - "name": "N: financeGen" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 128, - "id": "Note:WickedRocksMatter", - "measured": { - "height": 128, - "width": 208 - }, - "position": { - "x": 806.2393068252843, - "y": 135.72131770444153 - }, - "positionAbsolute": { - "x": 806.2393068252843, - "y": 135.72131770444153 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 128, - "width": 208 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 208 - }, - { - "data": { - "form": { - "text": "This part is for weather consulting." - }, - "label": "Note", - "name": "N: weatherGen" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 128, - "id": "Note:FiftyWebsReport", - "measured": { - "height": 128, - "width": 269 - }, - "position": { - "x": 988.0143050238387, - "y": -266.8179039129136 - }, - "positionAbsolute": { - "x": 1104.5947767935495, - "y": 17.63844720518125 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "noteNode", - "width": 269 - }, - { - "data": { - "form": { - "cite": true, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": false, - "max_tokens": 256, - "message_history_window_size": 12, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "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## Wikipedia content\n{Wikipedia:ThinLampsTravel}\n\n\n## Duckduckgo content\n{DuckDuckGo:NiceSeasInvent}\n\n\n## Baidu content\n{Baidu:CleanJarsMake}\n\n", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "websearchGen" - }, - "dragging": false, - "id": "Generate:FunnyHandsTickle", - "measured": { - "height": 106, - "width": 200 - }, - "position": { - "x": 282.8614392540758, - "y": 444.05759231978817 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode" - }, - { - "data": { - "form": { - "cite": true, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": false, - "max_tokens": 256, - "message_history_window_size": 12, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "Role: You are a professional medical consulting assistant.\n\nTasks: Answer questions posed by users. Answer based on content provided by the knowledge base, PubMed\n\nRequirement:\n- Answers may refer to the content provided (Knowledge Base, PubMed).\n- If the provided PubMed content is referenced, a link to the corresponding URL should be given.\n-Answers should be professional and accurate; no information should be fabricated that is not relevant to the user's question.\n\nProvided knowledge base content as following:\n{Retrieval:LemonGeckosHear}\n\nPubMed content provided\n{PubMed:EasyQueensLose}\n\n\n\n", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "medGen" - }, - "dragging": false, - "id": "Generate:LazyClubsAttack", - "measured": { - "height": 106, - "width": 200 - }, - "position": { - "x": 554.9441185731348, - "y": 166.42747693602357 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode" - }, - { - "data": { - "form": { - "cite": true, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": false, - "max_tokens": 256, - "message_history_window_size": 12, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "Role: You are a professional financial counseling assistant.\n\nTask: Answer user's question based on content provided by Wencai and AkShare.\n\nNotice:\n- Output no more than 5 news items from AkShare if there's content provided by Wencai.\n- Items from AkShare MUST have a corresponding URL link.\n\n############\nContent provided by Wencai: \n{WenCai:TenParksOpen}\n\n################\nContent provided by AkShare: \n{AkShare:CalmHotelsKnow}\n\n", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "financeGen" - }, - "dragging": false, - "id": "Generate:RealFansObey", - "measured": { - "height": 106, - "width": 200 - }, - "position": { - "x": 766.2368307106321, - "y": -51.15593613458973 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode" - }, - { - "data": { - "form": { - "cite": false, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 0, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "Role: You‘re warm-hearted lovely young girl, 22 years old, located at Shanghai in China. Your name is R. Who are talking to you is your very good old friend of yours.\n\nTask: \n- Chat with the friend.\n- Ask question and care about them.\n- Tell your friend the weather if there's weather information provided. If your friend did not provide region information, ask about where he/she is.\n\nThe following is the weather information:\n{QWeather:DeepKiwisTeach}\n\n\n", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "weatherGen" - }, - "dragging": false, - "id": "Generate:KhakiCrabsGlow", - "measured": { - "height": 106, - "width": 200 - }, - "position": { - "x": 996.5291688522603, - "y": -114.01530807109054 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode" - }, - { - "data": { - "form": { - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 6, - "parameter": "Precise", - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "RewriteQuestion", - "name": "RefineQuestion" - }, - "dragging": false, - "id": "RewriteQuestion:WholeOwlsTurn", - "measured": { - "height": 106, - "width": 200 - }, - "position": { - "x": -859.3797967550868, - "y": 214.54444107648857 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "rewriteNode" - } - ] - }, - "history": [], - "messages": [], - "path": [], - "reference": [] - }, - "avatar": "data:image/jpeg;base64,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" -} diff --git a/agent/templates/interpreter.json b/agent/templates/interpreter.json deleted file mode 100644 index 7f31dfe..0000000 --- a/agent/templates/interpreter.json +++ /dev/null @@ -1,475 +0,0 @@ -{ - "id": 4, - "title": "Interpreter", - "description": "A translation agent based on a reflection agentic workflow, inspired by Andrew Ng's project: https://github.com/andrewyng/translation-agent\n\n1. Prompt an LLM to translate a text into the target language.\n2. Have the LLM reflect on the translation and provide constructive suggestions for improvement.\n3. Use these suggestions to improve the translation.", - "canvas_type": "chatbot", - "dsl": { - "answer": [], - "components": { - "Answer:TinyGamesGuess": { - "downstream": [], - "obj": { - "component_name": "Answer", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "post_answers": [], - "query": [] - } - }, - "upstream": [ - "Generate:FuzzyEmusWork" - ] - }, - "Generate:FuzzyEmusWork": { - "downstream": [ - "Answer:TinyGamesGuess" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": false, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 0, - "message_history_window_size": 1, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "Your task is to carefully read, then edit, a translation to {begin@lang}, taking into\naccount a list of expert suggestions and constructive criticisms.\n\nThe source text, the initial translation, and the expert linguist suggestions are delimited by XML tags , and \nas follows:\n\n\n{begin@file}\n\n\n\n{Generate:VastKeysKick}\n\n\n\n{Generate:ShinySquidsSneeze}\n\n\nPlease take into account the expert suggestions when editing the translation. Edit the translation by ensuring:\n\n(i) accuracy (by correcting errors of addition, mistranslation, omission, or untranslated text),\n(ii) fluency (by applying {begin@lang} grammar, spelling and punctuation rules and ensuring there are no unnecessary repetitions), \n(iii) style (by ensuring the translations reflect the style of the source text)\n(iv) terminology (inappropriate for context, inconsistent use), or\n(v) other errors.\n\nOutput only the new translation and nothing else.", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "Generate:ShinySquidsSneeze" - ] - }, - "Generate:ShinySquidsSneeze": { - "downstream": [ - "Generate:FuzzyEmusWork" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": false, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 0, - "message_history_window_size": 1, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "Your task is to carefully read a source text and a translation to {begin@lang}, and then give constructive criticisms and helpful suggestions to improve the translation. \n\nThe source text and initial translation, delimited by XML tags and , are as follows:\n\n\n{begin@file}\n\n\n\n{Generate:VastKeysKick}\n\n\nWhen writing suggestions, pay attention to whether there are ways to improve the translation's \n(i) accuracy (by correcting errors of addition, mistranslation, omission, or untranslated text),\n(ii) fluency (by applying {begin@lang} grammar, spelling and punctuation rules, and ensuring there are no unnecessary repetitions),\n(iii) style (by ensuring the translations reflect the style of the source text and take into account any cultural context),\n(iv) terminology (by ensuring terminology use is consistent and reflects the source text domain; and by only ensuring you use equivalent idioms {begin@lang}).\n\nWrite a list of specific, helpful and constructive suggestions for improving the translation.\nEach suggestion should address one specific part of the translation.\nOutput only the suggestions and nothing else.", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "Generate:VastKeysKick" - ] - }, - "Generate:VastKeysKick": { - "downstream": [ - "Generate:ShinySquidsSneeze" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": false, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 0, - "message_history_window_size": 1, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "Role: You are a professional translator proficient in {begin@lang}, with an exceptional ability to convert specialized academic papers into accessible popular science articles. Please assist me in translating the following paragraph into {begin@lang}, ensuring that its style resembles that of popular science articles in {begin@lang}.\n\nRequirements & Restrictions:\n - Use Markdown format to output.\n - DO NOT overlook any details.\n\n\n\n{begin@file}\n\n", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "begin" - ] - }, - "begin": { - "downstream": [ - "Generate:VastKeysKick" - ], - "obj": { - "component_name": "Begin", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "prologue": "", - "query": [ - { - "key": "lang", - "name": "Target Language", - "optional": false, - "type": "line" - }, - { - "key": "file", - "name": "Files", - "optional": false, - "type": "file" - } - ] - } - }, - "upstream": [] - } - }, - "embed_id": "", - "graph": { - "edges": [ - { - "id": "xy-edge__begin-Generate:VastKeysKickc", - "markerEnd": "logo", - "source": "begin", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:VastKeysKick", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Generate:VastKeysKickb-Generate:ShinySquidsSneezec", - "markerEnd": "logo", - "source": "Generate:VastKeysKick", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:ShinySquidsSneeze", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Generate:FuzzyEmusWorkb-Answer:TinyGamesGuessc", - "markerEnd": "logo", - "source": "Generate:FuzzyEmusWork", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Answer:TinyGamesGuess", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Generate:ShinySquidsSneezeb-Generate:FuzzyEmusWorkc", - "markerEnd": "logo", - "source": "Generate:ShinySquidsSneeze", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:FuzzyEmusWork", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - } - ], - "nodes": [ - { - "data": { - "form": { - "prologue": "", - "query": [ - { - "key": "lang", - "name": "Target Language", - "optional": false, - "type": "line" - }, - { - "key": "file", - "name": "Files", - "optional": false, - "type": "file" - } - ] - }, - "label": "Begin", - "name": "begin" - }, - "dragging": false, - "height": 128, - "id": "begin", - "measured": { - "height": 128, - "width": 200 - }, - "position": { - "x": -383.5, - "y": 142.62256327439624 - }, - "positionAbsolute": { - "x": -383.5, - "y": 143.5 - }, - "selected": true, - "sourcePosition": "left", - "targetPosition": "right", - "type": "beginNode", - "width": 200 - }, - { - "data": { - "form": {}, - "label": "Answer", - "name": "Interact_0" - }, - "dragging": false, - "height": 44, - "id": "Answer:TinyGamesGuess", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": 645.5056004454161, - "y": 182.98193827439627 - }, - "positionAbsolute": { - "x": 688.5, - "y": 183.859375 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "logicNode", - "width": 200 - }, - { - "data": { - "form": { - "text": "Translation Agent: Agentic translation using reflection workflow\n\nThis is inspired by Andrew NG's project: https://github.com/andrewyng/translation-agent\n\n1. Prompt an LLM to translate a text into the target language;\n2. Have the LLM reflect on the translation and provide constructive suggestions for improvement;\n3. Use these suggestions to improve the translation." - }, - "label": "Note", - "name": "Brief" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 227, - "id": "Note:MoodyKnivesCheat", - "measured": { - "height": 227, - "width": 703 - }, - "position": { - "x": 46.02198421645994, - "y": -267.69527832581736 - }, - "positionAbsolute": { - "x": 46.02198421645994, - "y": -267.69527832581736 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 227, - "width": 703 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 703 - }, - { - "data": { - "form": { - "text": "Many businesses use specialized terms that are not widely used on the internet and that LLMs thus don’t know about, and there are also many terms that can be translated in multiple ways. For example, ”open source” in Spanish can be “Código abierto” or “Fuente abierta”; both are fine, but it’d better to pick one and stick with it for a single document.\n\nYou can add those glossary translation into prompt to any of `Translate directly` or 'Reflect'." - }, - "label": "Note", - "name": "Tip: Add glossary " - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 181, - "id": "Note:SourCarrotsAct", - "measured": { - "height": 181, - "width": 832 - }, - "position": { - "x": 65.0676250238289, - "y": 397.6323270065299 - }, - "positionAbsolute": { - "x": 65.0676250238289, - "y": 397.6323270065299 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 181, - "width": 832 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 832 - }, - { - "data": { - "form": { - "cite": false, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": false, - "max_tokens": 256, - "message_history_window_size": 1, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "Role: You are a professional translator proficient in {begin@lang}, with an exceptional ability to convert specialized academic papers into accessible popular science articles. Please assist me in translating the following paragraph into {begin@lang}, ensuring that its style resembles that of popular science articles in {begin@lang}.\n\nRequirements & Restrictions:\n - Use Markdown format to output.\n - DO NOT overlook any details.\n\n\n\n{begin@file}\n\n", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "Translate directly" - }, - "dragging": false, - "id": "Generate:VastKeysKick", - "measured": { - "height": 106, - "width": 200 - }, - "position": { - "x": -132.6338674989604, - "y": 153.70663786774483 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode" - }, - { - "data": { - "form": { - "cite": false, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": false, - "max_tokens": 256, - "message_history_window_size": 1, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "Your task is to carefully read a source text and a translation to {begin@lang}, and then give constructive criticisms and helpful suggestions to improve the translation. \n\nThe source text and initial translation, delimited by XML tags and , are as follows:\n\n\n{begin@file}\n\n\n\n{Generate:VastKeysKick}\n\n\nWhen writing suggestions, pay attention to whether there are ways to improve the translation's \n(i) accuracy (by correcting errors of addition, mistranslation, omission, or untranslated text),\n(ii) fluency (by applying {begin@lang} grammar, spelling and punctuation rules, and ensuring there are no unnecessary repetitions),\n(iii) style (by ensuring the translations reflect the style of the source text and take into account any cultural context),\n(iv) terminology (by ensuring terminology use is consistent and reflects the source text domain; and by only ensuring you use equivalent idioms {begin@lang}).\n\nWrite a list of specific, helpful and constructive suggestions for improving the translation.\nEach suggestion should address one specific part of the translation.\nOutput only the suggestions and nothing else.", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "Reflect" - }, - "dragging": false, - "id": "Generate:ShinySquidsSneeze", - "measured": { - "height": 106, - "width": 200 - }, - "position": { - "x": 121.1675336631696, - "y": 152.92865408917177 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode" - }, - { - "data": { - "form": { - "cite": false, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": false, - "max_tokens": 256, - "message_history_window_size": 1, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "Your task is to carefully read, then edit, a translation to {begin@lang}, taking into\naccount a list of expert suggestions and constructive criticisms.\n\nThe source text, the initial translation, and the expert linguist suggestions are delimited by XML tags , and \nas follows:\n\n\n{begin@file}\n\n\n\n{Generate:VastKeysKick}\n\n\n\n{Generate:ShinySquidsSneeze}\n\n\nPlease take into account the expert suggestions when editing the translation. Edit the translation by ensuring:\n\n(i) accuracy (by correcting errors of addition, mistranslation, omission, or untranslated text),\n(ii) fluency (by applying {begin@lang} grammar, spelling and punctuation rules and ensuring there are no unnecessary repetitions), \n(iii) style (by ensuring the translations reflect the style of the source text)\n(iv) terminology (inappropriate for context, inconsistent use), or\n(v) other errors.\n\nOutput only the new translation and nothing else.", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "Improve" - }, - "dragging": false, - "id": "Generate:FuzzyEmusWork", - "measured": { - "height": 106, - "width": 200 - }, - "position": { - "x": 383.1474420163898, - "y": 152.0472805236579 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode" - } - ] - }, - "history": [], - "messages": [], - "path": [], - "reference": [] - }, - "avatar": "data:image/jpeg;base64,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" -} diff --git a/agent/templates/investment_advisor.json b/agent/templates/investment_advisor.json deleted file mode 100644 index 338d93a..0000000 --- a/agent/templates/investment_advisor.json +++ /dev/null @@ -1,642 +0,0 @@ -{ - "id": 8, - "title": "Intelligent investment advisor", - "description": "An intelligent investment advisor that answers your financial questions using real-time domestic financial data.", - "canvas_type": "chatbot", - "dsl": { - "answer": [], - "components": { - "AkShare:CalmHotelsKnow": { - "downstream": [ - "Generate:SolidAreasRing" - ], - "obj": { - "component_name": "AkShare", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [], - "top_n": 10 - } - }, - "upstream": [ - "KeywordExtract:BreezyGoatsRead" - ] - }, - "Answer:NeatLandsWave": { - "downstream": [ - "WenCai:TenParksOpen", - "KeywordExtract:BreezyGoatsRead" - ], - "obj": { - "component_name": "Answer", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "post_answers": [], - "query": [] - } - }, - "upstream": [ - "begin", - "Generate:SolidAreasRing" - ] - }, - "Generate:SolidAreasRing": { - "downstream": [ - "Answer:NeatLandsWave" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": true, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 0, - "message_history_window_size": 1, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "Role: You are a professional financial counseling assistant.\n\nTask: Answer user's question based on content provided by Wencai and AkShare.\n\nNotice:\n- Output no more than 5 news items from AkShare if there's content provided by Wencai.\n- Items from AkShare MUST have a corresponding URL link.\n\n############\nContent provided by Wencai: \n{WenCai:TenParksOpen}\n\n################\nContent provided by AkShare: \n\n{AkShare:CalmHotelsKnow}\n\n\n", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "WenCai:TenParksOpen", - "AkShare:CalmHotelsKnow" - ] - }, - "KeywordExtract:BreezyGoatsRead": { - "downstream": [ - "AkShare:CalmHotelsKnow" - ], - "obj": { - "component_name": "KeywordExtract", - "inputs": [], - "output": null, - "params": { - "cite": true, - "debug_inputs": [], - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "", - "query": [], - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_n": 2, - "top_p": 0.3 - } - }, - "upstream": [ - "Answer:NeatLandsWave" - ] - }, - "WenCai:TenParksOpen": { - "downstream": [ - "Generate:SolidAreasRing" - ], - "obj": { - "component_name": "WenCai", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [], - "query_type": "stock", - "top_n": 5 - } - }, - "upstream": [ - "Answer:NeatLandsWave" - ] - }, - "begin": { - "downstream": [ - "Answer:NeatLandsWave" - ], - "obj": { - "component_name": "Begin", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "prologue": "Hi there!", - "query": [] - } - }, - "upstream": [] - } - }, - "embed_id": "", - "graph": { - "edges": [ - { - "id": "reactflow__edge-begin-Answer:NeatLandsWavec", - "markerEnd": "logo", - "source": "begin", - "sourceHandle": null, - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Answer:NeatLandsWave", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Answer:NeatLandsWaveb-WenCai:TenParksOpenc", - "markerEnd": "logo", - "source": "Answer:NeatLandsWave", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "WenCai:TenParksOpen", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-KeywordExtract:BreezyGoatsReadb-AkShare:CalmHotelsKnowc", - "markerEnd": "logo", - "source": "KeywordExtract:BreezyGoatsRead", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "AkShare:CalmHotelsKnow", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Answer:NeatLandsWaveb-KeywordExtract:BreezyGoatsReadc", - "markerEnd": "logo", - "source": "Answer:NeatLandsWave", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "KeywordExtract:BreezyGoatsRead", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "xy-edge__WenCai:TenParksOpenb-Generate:SolidAreasRingb", - "markerEnd": "logo", - "source": "WenCai:TenParksOpen", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:SolidAreasRing", - "targetHandle": "b", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__AkShare:CalmHotelsKnowb-Generate:SolidAreasRingb", - "markerEnd": "logo", - "source": "AkShare:CalmHotelsKnow", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:SolidAreasRing", - "targetHandle": "b", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Generate:SolidAreasRingc-Answer:NeatLandsWavec", - "markerEnd": "logo", - "source": "Generate:SolidAreasRing", - "sourceHandle": "c", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Answer:NeatLandsWave", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - } - ], - "nodes": [ - { - "data": { - "form": { - "prologue": "Hi there!" - }, - "label": "Begin", - "name": "Opening" - }, - "dragging": false, - "height": 44, - "id": "begin", - "measured": { - "height": 44, - "width": 100 - }, - "position": { - "x": -609.7949690891593, - "y": -29.12385224725604 - }, - "positionAbsolute": { - "x": -521.8118264317484, - "y": -27.999467037576665 - }, - "selected": false, - "sourcePosition": "left", - "targetPosition": "right", - "type": "beginNode" - }, - { - "data": { - "form": { - "query_type": "stock", - "top_n": 5 - }, - "label": "WenCai", - "name": "Wencai" - }, - "dragging": false, - "height": 44, - "id": "WenCai:TenParksOpen", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": -13.030801663267397, - "y": -30.557141660610256 - }, - "positionAbsolute": { - "x": -13.030801663267397, - "y": -30.557141660610256 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "ragNode", - "width": 200 - }, - { - "data": { - "form": { - "top_n": 10 - }, - "label": "AkShare", - "name": "AKShare" - }, - "dragging": false, - "height": 44, - "id": "AkShare:CalmHotelsKnow", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": 250.32227681412806, - "y": 74.24036022703525 - }, - "positionAbsolute": { - "x": 267.17349571786156, - "y": 100.01281266803943 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "ragNode", - "width": 200 - }, - { - "data": { - "form": {}, - "label": "Answer", - "name": "Interact" - }, - "dragging": false, - "height": 44, - "id": "Answer:NeatLandsWave", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": -304.0612563145512, - "y": -29.054278091837944 - }, - "positionAbsolute": { - "x": -304.0612563145512, - "y": -29.054278091837944 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "logicNode", - "width": 200 - }, - { - "data": { - "form": { - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "parameter": "Precise", - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_n": 2, - "top_p": 0.3 - }, - "label": "KeywordExtract", - "name": "Keywords" - }, - "dragging": false, - "height": 86, - "id": "KeywordExtract:BreezyGoatsRead", - "measured": { - "height": 86, - "width": 200 - }, - "position": { - "x": -12.734133905960277, - "y": 53.63594331206494 - }, - "positionAbsolute": { - "x": -17.690374759999543, - "y": 80.39964392387697 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "keywordNode", - "width": 200 - }, - { - "data": { - "form": { - "text": "Receives the user's financial inquiries and displays the large model's response to financial questions." - }, - "label": "Note", - "name": "N: Interact" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 187, - "id": "Note:FuzzyPoetsLearn", - "measured": { - "height": 187, - "width": 214 - }, - "position": { - "x": -296.5982116419186, - "y": 38.77567426067935 - }, - "positionAbsolute": { - "x": -296.5982116419186, - "y": 38.77567426067935 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 162, - "width": 214 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 214 - }, - { - "data": { - "form": { - "text": "Extracts keywords based on the user's financial questions for better retrieval." - }, - "label": "Note", - "name": "N: Keywords" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 155, - "id": "Note:FlatBagsRun", - "measured": { - "height": 155, - "width": 213 - }, - "position": { - "x": -14.82895160277127, - "y": 186.52508153680787 - }, - "positionAbsolute": { - "x": -14.82895160277127, - "y": 186.52508153680787 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 155, - "width": 213 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 213 - }, - { - "data": { - "form": { - "text": "Searches on akshare for the latest news about economics based on the keywords and returns the results." - }, - "label": "Note", - "name": "N: AKShare" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 128, - "id": "Note:WarmClothsSort", - "measured": { - "height": 128, - "width": 283 - }, - "position": { - "x": 259.53966185269985, - "y": 209.6999260009385 - }, - "positionAbsolute": { - "x": 573.7653319987893, - "y": 102.64512355369035 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 128, - "width": 283 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 283 - }, - { - "data": { - "form": { - "text": "Searches by Wencai to select stocks that satisfy user mentioned conditions." - }, - "label": "Note", - "name": "N: Wencai" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 143, - "id": "Note:TiredReadersWash", - "measured": { - "height": 143, - "width": 285 - }, - "position": { - "x": 251.25432007905098, - "y": -97.53719402078019 - }, - "positionAbsolute": { - "x": 571.4274792499875, - "y": -37.07105560150117 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 128, - "width": 285 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 285 - }, - { - "data": { - "form": { - "text": "The large model answers the user's medical health questions based on the searched and retrieved content." - }, - "label": "Note", - "name": "N: LLM" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 179, - "id": "Note:TameBoatsType", - "measured": { - "height": 179, - "width": 260 - }, - "position": { - "x": -167.45710806024056, - "y": -372.5606558391346 - }, - "positionAbsolute": { - "x": -7.849538042569293, - "y": -427.90526378748035 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 163, - "width": 212 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 260 - }, - { - "data": { - "form": { - "cite": true, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": false, - "max_tokens": 256, - "message_history_window_size": 1, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "Role: You are a professional financial counseling assistant.\n\nTask: Answer user's question based on content provided by Wencai and AkShare.\n\nNotice:\n- Output no more than 5 news items from AkShare if there's content provided by Wencai.\n- Items from AkShare MUST have a corresponding URL link.\n\n############\nContent provided by Wencai: \n{WenCai:TenParksOpen}\n\n################\nContent provided by AkShare: \n\n{AkShare:CalmHotelsKnow}\n\n\n", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "LLM" - }, - "dragging": false, - "id": "Generate:SolidAreasRing", - "measured": { - "height": 106, - "width": 200 - }, - "position": { - "x": -161.00840949957603, - "y": -180.04918322565015 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode" - } - ] - }, - "history": [], - "messages": [], - "path": [], - "reference": [] - }, - "avatar": "data:image/png;base64,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" -} diff --git a/agent/templates/medical_consultation.json b/agent/templates/medical_consultation.json deleted file mode 100644 index 1afed12..0000000 --- a/agent/templates/medical_consultation.json +++ /dev/null @@ -1,784 +0,0 @@ -{ - "id": 7, - "title": "Medical consultation", - "description": "A consultant that offers medical suggestions using an internal QA dataset and PubMed search results. Note that this agent's answers are for reference only and may not be valid. The dataset can be found at https://huggingface.co/datasets/InfiniFlow/medical_QA/tree/main", - "canvas_type": "chatbot", - "dsl": { - "answer": [], - "components": { - "Answer:FlatRavensPush": { - "downstream": [ - "Generate:QuietMelonsHear", - "Generate:FortyBaboonsRule" - ], - "obj": { - "component_name": "Answer", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "post_answers": [], - "query": [] - } - }, - "upstream": [ - "begin", - "Generate:BrightCitiesSink" - ] - }, - "Generate:BrightCitiesSink": { - "downstream": [ - "Answer:FlatRavensPush" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": true, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 0, - "message_history_window_size": 12, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "Role: You are a professional medical consulting assistant\n\nTasks: Answer questions posed by users. Answer based on content provided by the knowledge base, PubMed\n\nRequirement:\n- Answers may refer to the content provided (Knowledge Base, PubMed).\n- If the provided PubMed content is referenced, a link to the corresponding URL should be given.\n-Answers should be professional and accurate; no information should be fabricated that is not relevant to the user's question.\n\nProvided knowledge base content\n{Retrieval:BeigeBagsDress}\n\nPubMed content provided\n\n{PubMed:TwentyFansShake}", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "Retrieval:BeigeBagsDress", - "PubMed:TwentyFansShake" - ] - }, - "Generate:FortyBaboonsRule": { - "downstream": [ - "PubMed:TwentyFansShake" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": false, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 256, - "message_history_window_size": 1, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "Role: You are a professional Chinese-English medical question translation assistant\n\nTask: Accurately translate users' Chinese medical question content into English, ensuring accuracy of terminology and clarity of expression\n\nRequirements:\n- In-depth understanding of the terminology and disease descriptions in Chinese medical inquiries to ensure correct medical vocabulary is used in the English translation.\n- Maintain the semantic integrity and accuracy of the original text to avoid omitting important information or introducing errors.\n- Pay attention to the differences in expression habits between Chinese and English, and make appropriate adjustments to make the English translation more natural and fluent.\n- Respect the patient's privacy and the principle of medical confidentiality, and do not disclose any sensitive information during the translation process.\n\nExample:\nOriginal sentence: 我最近总是感觉胸闷,有时还会有心悸的感觉。\nTranslated: I've been feeling chest tightness recently, and sometimes I experience palpitations.\n\nNote:\nOnly the translated content should be given, do not output other irrelevant content!", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "Answer:FlatRavensPush" - ] - }, - "Generate:QuietMelonsHear": { - "downstream": [ - "Retrieval:BeigeBagsDress" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": true, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 256, - "message_history_window_size": 12, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "Role: You are a professional medical consulting translation assistant\n\nTask: Translate user questions into Chinese, ensuring accuracy of medical terminology and appropriateness of context.\n\nRequirements:\n- Accurately translate medical terminology to convey the integrity and emotional color of the original message.\n- For unclear or uncertain medical terminology, the original text may be retained to ensure accuracy.\n- Respect the privacy and sensitivity of medical consultations and ensure that sensitive information is not disclosed during the translation process.\n- If the user's question is in Chinese, there is no need to translate, just output the user's question directly\n\nExample:\nOriginal (English): Doctor, I have been suffering from chest pain and shortness of breath for the past few days.\nTranslation (Chinese): 医生,我这几天一直胸痛和气短。\n\nNote:\nOnly the translated content needs to be output, no other irrelevant content!", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "Answer:FlatRavensPush" - ] - }, - "PubMed:TwentyFansShake": { - "downstream": [ - "Generate:BrightCitiesSink" - ], - "obj": { - "component_name": "PubMed", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "email": "928018077@qq.com", - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [ - { - "component_id": "Generate:FortyBaboonsRule", - "type": "reference" - } - ], - "top_n": 10 - } - }, - "upstream": [ - "Generate:FortyBaboonsRule" - ] - }, - "Retrieval:BeigeBagsDress": { - "downstream": [ - "Generate:BrightCitiesSink" - ], - "obj": { - "component_name": "Retrieval", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "empty_response": "", - "inputs": [], - "kb_ids": [], - "keywords_similarity_weight": 0.3, - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [ - { - "component_id": "Generate:QuietMelonsHear", - "type": "reference" - } - ], - "rerank_id": "", - "similarity_threshold": 0.2, - "top_k": 1024, - "top_n": 8 - } - }, - "upstream": [ - "Generate:QuietMelonsHear" - ] - }, - "begin": { - "downstream": [ - "Answer:FlatRavensPush" - ], - "obj": { - "component_name": "Begin", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "prologue": "Hi! I'm your smart assistant. What can I do for you?", - "query": [] - } - }, - "upstream": [] - } - }, - "embed_id": "", - "graph": { - "edges": [ - { - "id": "reactflow__edge-begin-Answer:FlatRavensPushc", - "markerEnd": "logo", - "source": "begin", - "sourceHandle": null, - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Answer:FlatRavensPush", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Answer:FlatRavensPushb-Generate:QuietMelonsHearc", - "markerEnd": "logo", - "source": "Answer:FlatRavensPush", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:QuietMelonsHear", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Answer:FlatRavensPushb-Generate:FortyBaboonsRulec", - "markerEnd": "logo", - "source": "Answer:FlatRavensPush", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:FortyBaboonsRule", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Generate:FortyBaboonsRuleb-PubMed:TwentyFansShakec", - "markerEnd": "logo", - "source": "Generate:FortyBaboonsRule", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "PubMed:TwentyFansShake", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Generate:QuietMelonsHearb-Retrieval:BeigeBagsDressc", - "markerEnd": "logo", - "source": "Generate:QuietMelonsHear", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Retrieval:BeigeBagsDress", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "xy-edge__Retrieval:BeigeBagsDressb-Generate:BrightCitiesSinkb", - "markerEnd": "logo", - "source": "Retrieval:BeigeBagsDress", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:BrightCitiesSink", - "targetHandle": "b", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__PubMed:TwentyFansShakeb-Generate:BrightCitiesSinkb", - "markerEnd": "logo", - "source": "PubMed:TwentyFansShake", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:BrightCitiesSink", - "targetHandle": "b", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Generate:BrightCitiesSinkc-Answer:FlatRavensPushc", - "markerEnd": "logo", - "source": "Generate:BrightCitiesSink", - "sourceHandle": "c", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Answer:FlatRavensPush", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - } - ], - "nodes": [ - { - "data": { - "label": "Begin", - "name": "opening" - }, - "dragging": false, - "height": 44, - "id": "begin", - "measured": { - "height": 44, - "width": 100 - }, - "position": { - "x": -599.8361708291377, - "y": 161.91688790133628 - }, - "positionAbsolute": { - "x": -599.8361708291377, - "y": 161.91688790133628 - }, - "selected": false, - "sourcePosition": "left", - "targetPosition": "right", - "type": "beginNode" - }, - { - "data": { - "form": { - "email": "928018077@qq.com", - "query": [ - { - "component_id": "Generate:FortyBaboonsRule", - "type": "reference" - } - ], - "top_n": 10 - }, - "label": "PubMed", - "name": "Search PubMed" - }, - "dragging": false, - "height": 44, - "id": "PubMed:TwentyFansShake", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": 388.4151716305788, - "y": 272.51398951401995 - }, - "positionAbsolute": { - "x": 389.7229173847695, - "y": 276.4372267765921 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "ragNode", - "width": 200 - }, - { - "data": { - "form": {}, - "label": "Answer", - "name": "Interface" - }, - "dragging": false, - "height": 44, - "id": "Answer:FlatRavensPush", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": -277.4280835723395, - "y": 162.89713236919926 - }, - "positionAbsolute": { - "x": -370.881803561134, - "y": 161.41373998842477 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "logicNode", - "width": 200 - }, - { - "data": { - "form": { - "cite": true, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 12, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "Role: You are a professional medical consulting translation assistant\n\nTask: Translate user questions into Chinese, ensuring accuracy of medical terminology and appropriateness of context.\n\nRequirements:\n- Accurately translate medical terminology to convey the integrity and emotional color of the original message.\n- For unclear or uncertain medical terminology, the original text may be retained to ensure accuracy.\n- Respect the privacy and sensitivity of medical consultations and ensure that sensitive information is not disclosed during the translation process.\n- If the user's question is in Chinese, there is no need to translate, just output the user's question directly\n\nExample:\nOriginal (English): Doctor, I have been suffering from chest pain and shortness of breath for the past few days.\nTranslation (Chinese): 医生,我这几天一直胸痛和气短。\n\nNote:\nOnly the translated content needs to be output, no other irrelevant content!", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "Translate to Chinese" - }, - "dragging": false, - "height": 86, - "id": "Generate:QuietMelonsHear", - "measured": { - "height": 86, - "width": 200 - }, - "position": { - "x": -2.756518132081453, - "y": 38.86485966020132 - }, - "positionAbsolute": { - "x": -2.756518132081453, - "y": 38.86485966020132 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode", - "width": 200 - }, - { - "data": { - "form": { - "cite": false, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 1, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "Role: You are a professional Chinese-English medical question translation assistant\n\nTask: Accurately translate users' Chinese medical question content into English, ensuring accuracy of terminology and clarity of expression\n\nRequirements:\n- In-depth understanding of the terminology and disease descriptions in Chinese medical inquiries to ensure correct medical vocabulary is used in the English translation.\n- Maintain the semantic integrity and accuracy of the original text to avoid omitting important information or introducing errors.\n- Pay attention to the differences in expression habits between Chinese and English, and make appropriate adjustments to make the English translation more natural and fluent.\n- Respect the patient's privacy and the principle of medical confidentiality, and do not disclose any sensitive information during the translation process.\n\nExample:\nOriginal sentence: 我最近总是感觉胸闷,有时还会有心悸的感觉。\nTranslated: I've been feeling chest tightness recently, and sometimes I experience palpitations.\n\nNote:\nOnly the translated content should be given, do not output other irrelevant content!", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "Translate to English" - }, - "dragging": false, - "height": 86, - "id": "Generate:FortyBaboonsRule", - "measured": { - "height": 86, - "width": 200 - }, - "position": { - "x": -3.825864707727135, - "y": 253.2285157283701 - }, - "positionAbsolute": { - "x": -3.825864707727135, - "y": 253.2285157283701 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode", - "width": 200 - }, - { - "data": { - "form": { - "kb_ids": [], - "keywords_similarity_weight": 0.3, - "query": [ - { - "component_id": "Generate:QuietMelonsHear", - "type": "reference" - } - ], - "similarity_threshold": 0.2, - "top_n": 8 - }, - "label": "Retrieval", - "name": "Search Q&A" - }, - "dragging": false, - "height": 44, - "id": "Retrieval:BeigeBagsDress", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": 316.9462115194757, - "y": 57.81358887451738 - }, - "positionAbsolute": { - "x": 382.25527986090765, - "y": 35.38705653631584 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "retrievalNode", - "width": 200 - }, - { - "data": { - "form": { - "text": "Receives the user's financial inquiries and displays the large model's response to financial questions." - }, - "label": "Note", - "name": "N: Interface" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 162, - "id": "Note:RedZebrasEnjoy", - "measured": { - "height": 162, - "width": 200 - }, - "position": { - "x": -274.75115571622416, - "y": 233.92632661399952 - }, - "positionAbsolute": { - "x": -374.13983303471906, - "y": 219.54112331790157 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 162, - "width": 200 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 200 - }, - { - "data": { - "form": { - "text": "Translate user's question to English by LLM." - }, - "label": "Note", - "name": "N: Translate to English" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 128, - "id": "Note:DarkIconsClap", - "measured": { - "height": 128, - "width": 227 - }, - "position": { - "x": -2.0308204014422273, - "y": 379.60045703973515 - }, - "positionAbsolute": { - "x": -0.453362859534991, - "y": 357.3687792184929 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 128, - "width": 204 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 227 - }, - { - "data": { - "form": { - "text": "Translate user's question to Chinese by LLM." - }, - "label": "Note", - "name": "N: Translate to Chinese" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 128, - "id": "Note:SmallRiversTap", - "measured": { - "height": 128, - "width": 220 - }, - "position": { - "x": -2.9326060127226583, - "y": -99.3117253460485 - }, - "positionAbsolute": { - "x": -5.453362859535048, - "y": -105.63122078150693 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 128, - "width": 196 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 220 - }, - { - "data": { - "form": { - "text": "PubMed® comprises more than 37 million citations for biomedical literature from MEDLINE, life science journals, and online books. Citations may include links to full text content from PubMed Central and publisher web sites." - }, - "label": "Note", - "name": "N: Search PubMed" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 220, - "id": "Note:MightyDeerShout", - "measured": { - "height": 220, - "width": 287 - }, - "position": { - "x": 718.5466371404648, - "y": 275.36877921849293 - }, - "positionAbsolute": { - "x": 718.5466371404648, - "y": 275.36877921849293 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 220, - "width": 287 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 287 - }, - { - "data": { - "form": { - "text": "You can download the Q&A dataset at\nhttps://huggingface.co/datasets/InfiniFlow/medical_QA" - }, - "label": "Note", - "name": "N: Search Q&A" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 128, - "id": "Note:VioletSuitsFlash", - "measured": { - "height": 128, - "width": 387 - }, - "position": { - "x": 776.4332169584197, - "y": 32.89802610798361 - }, - "positionAbsolute": { - "x": 776.4332169584197, - "y": 32.89802610798361 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 128, - "width": 387 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 387 - }, - { - "data": { - "form": { - "text": "A prompt summarize content from search result from PubMed and Q&A dataset." - }, - "label": "Note", - "name": "N: LLM" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 140, - "id": "Note:BeigeCoinsBuild", - "measured": { - "height": 140, - "width": 281 - }, - "position": { - "x": 293.89948660403513, - "y": -238.31673896113236 - }, - "positionAbsolute": { - "x": 756.9053449234701, - "y": -212.92342186138177 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "noteNode", - "width": 281 - }, - { - "data": { - "form": { - "cite": true, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": false, - "max_tokens": 256, - "message_history_window_size": 12, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "Role: You are a professional medical consulting assistant\n\nTasks: Answer questions posed by users. Answer based on content provided by the knowledge base, PubMed\n\nRequirement:\n- Answers may refer to the content provided (Knowledge Base, PubMed).\n- If the provided PubMed content is referenced, a link to the corresponding URL should be given.\n-Answers should be professional and accurate; no information should be fabricated that is not relevant to the user's question.\n\nProvided knowledge base content\n{Retrieval:BeigeBagsDress}\n\nPubMed content provided\n\n{PubMed:TwentyFansShake}", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "LLM" - }, - "dragging": false, - "id": "Generate:BrightCitiesSink", - "measured": { - "height": 106, - "width": 200 - }, - "position": { - "x": 300, - "y": -86.3689104694316 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode" - } - ] - }, - "history": [], - "messages": [], - "path": [], - "reference": [] - }, - "avatar": "data:image/png;base64,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" -} diff --git a/agent/templates/research_report.json b/agent/templates/research_report.json deleted file mode 100644 index 4fbf1fe..0000000 --- a/agent/templates/research_report.json +++ /dev/null @@ -1,1107 +0,0 @@ -{ - "id": 10, - "title": "Research report generator", - "description": "A report generator that creates a research report from a given title, in the specified target language. It generates queries from the input title, then uses these to create subtitles and sections, compiling everything into a comprehensive report.", - "canvas_type": "chatbot", - "dsl": { - "answer": [], - "components": { - "Answer:WittyBottlesJog": { - "downstream": [], - "obj": { - "component_name": "Answer", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "post_answers": [], - "query": [] - } - }, - "upstream": [ - "Template:LegalDoorsAct" - ] - }, - "Baidu:MeanBroomsMatter": { - "downstream": [ - "Generate:YoungClownsKnock" - ], - "obj": { - "component_name": "Baidu", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [ - { - "component_id": "IterationItem:RudeTablesSmile", - "type": "reference" - } - ], - "top_n": 10 - } - }, - "parent_id": "Iteration:BlueClothsGrab", - "upstream": [ - "IterationItem:RudeTablesSmile" - ] - }, - "Generate:EveryCoinsStare": { - "downstream": [ - "Generate:RedWormsDouble", - "Iteration:BlueClothsGrab" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": false, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 256, - "message_history_window_size": 1, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "\n\nGenerate a series of appropriate search engine queries to break down questions based on user inquiries\n\n\n\n\nInput: User asks how to learn programming\nOutput: programming learning methods, programming tutorials for beginners\n\n\n\nInput: User wants to understand latest technology trends \nOutput: tech trends 2024, latest technology news\n\n\n\nInput: User seeks healthy eating advice\nOutput: healthy eating guide, balanced nutrition diet\n\n\n\n\n1. Take user's question as input.\n2. Identify relevant keywords or phrases based on the topic of user's question.\n3. Use these keywords or phrases to make search engine queries.\n4. Generate a series of appropriate search engine queries to help break down user's question.\n5. Ensure output content does not contain any xml tags.\n6. The output must be pure and conform to the style without other explanations.\n7. Break down into at least 4-6 subproblems.\n8. Output is separated only by commas.\n\n\n\ntitle: {begin@title}\nlanguage: {begin@language}\nThe output must be pure and conform to the style without other explanations.\nOutput is separated only by commas.\nBreak down into at least 4-6 subproblems.\n\nOutput:", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "begin" - ] - }, - "Generate:RealLoopsVanish": { - "downstream": [ - "Template:SpottyWaspsLose" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": false, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 0, - "message_history_window_size": 1, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "In a detailed report — The report should focus on the answer to {IterationItem:OliveStatesSmoke}and nothing else.\n\n\nLanguage: {begin@language}\nContext as bellow: \n\n\"{Iteration:BlueClothsGrab}\"\n\nProvide the research report in the specified language, avoiding small talk.\nThe main content is provided in markdown format\nWrite all source urls at the end of the report in apa format. ", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "parent_id": "Iteration:ThreeParksChew", - "upstream": [ - "IterationItem:OliveStatesSmoke" - ] - }, - "Generate:RedWormsDouble": { - "downstream": [ - "Iteration:ThreeParksChew" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": false, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 0, - "message_history_window_size": 1, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "According to query: ' {Generate:EveryCoinsStare}',for ' {begin@title}', generate 3 to 5 sub-titles.\n\n\nPlease generate 4 subheadings for the main title following these steps:\n - 1. Carefully read the provided main title and related content\n - 2. Analyze the core theme and key information points of the main title\n - 3. Ensure the generated subheadings maintain consistency and relevance with the main title\n - 4. Each subheading should:\n - Be concise and appropriate in length\n - Highlight a unique angle or key point\n - Capture readers' interest\n - Match the overall style and tone of the article\n - 5. Between subheadings:\n - Content should not overlap\n - Logical order should be maintained\n - Should collectively support the main title\n - Use numerical sequence (1, 2, 3...) to mark each subheading\n - 6. Output format requirements:\n - Each subheading on a separate line\n - No XML tags included\n - Output subheadings content only\n\n\nlanguage: {begin@language}\nGenerate a series of appropriate sub-title to help break down ' {begin@title}'.\nBreaks down complex topics into manageable subtopics.\n\nOutput:", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "Generate:EveryCoinsStare" - ] - }, - "Generate:YoungClownsKnock": { - "downstream": [], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": false, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 0, - "message_history_window_size": 1, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "Your goal is to provide answers based on information from the internet. \nYou must use the provided search results to find relevant online information. \nYou should never use your own knowledge to answer questions.\nPlease include relevant url sources in the end of your answers.\n{Baidu:MeanBroomsMatter}\n\n\n\n\n\nlanguage: {begin@language}\n\n\n \" {Baidu:MeanBroomsMatter}\" \n\n\n\n\nUsing the above information, answer the following question or topic: \" {IterationItem:RudeTablesSmile} \"\nin a detailed report — The report should focus on the answer to the question, should be well structured, informative, in depth, with facts and numbers if available, a minimum of 1,200 words and with markdown syntax and apa format. Write all source urls at the end of the report in apa format. You should write your report only based on the given information and nothing else.", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "parent_id": "Iteration:BlueClothsGrab", - "upstream": [ - "Baidu:MeanBroomsMatter" - ] - }, - "Iteration:BlueClothsGrab": { - "downstream": [], - "obj": { - "component_name": "Iteration", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "delimiter": ",", - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [ - { - "component_id": "Generate:EveryCoinsStare", - "type": "reference" - } - ] - } - }, - "upstream": [ - "Generate:EveryCoinsStare" - ] - }, - "Iteration:ThreeParksChew": { - "downstream": [ - "Template:LegalDoorsAct" - ], - "obj": { - "component_name": "Iteration", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "delimiter": "\n", - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [ - { - "component_id": "Generate:RedWormsDouble", - "type": "reference" - } - ] - } - }, - "upstream": [ - "Generate:RedWormsDouble" - ] - }, - "IterationItem:OliveStatesSmoke": { - "downstream": [ - "Generate:RealLoopsVanish" - ], - "obj": { - "component_name": "IterationItem", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [] - } - }, - "parent_id": "Iteration:ThreeParksChew", - "upstream": [] - }, - "IterationItem:RudeTablesSmile": { - "downstream": [ - "Baidu:MeanBroomsMatter" - ], - "obj": { - "component_name": "IterationItem", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [] - } - }, - "parent_id": "Iteration:BlueClothsGrab", - "upstream": [] - }, - "Template:LegalDoorsAct": { - "downstream": [ - "Answer:WittyBottlesJog" - ], - "obj": { - "component_name": "Template", - "inputs": [], - "output": null, - "params": { - "content": "

{begin@title}

\n\n\n\n{Iteration:ThreeParksChew}", - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "parameters": [], - "query": [] - } - }, - "upstream": [ - "Iteration:ThreeParksChew" - ] - }, - "Template:SpottyWaspsLose": { - "downstream": [], - "obj": { - "component_name": "Template", - "inputs": [], - "output": null, - "params": { - "content": "

{IterationItem:OliveStatesSmoke}

\n
{Generate:RealLoopsVanish}
", - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "parameters": [], - "query": [] - } - }, - "parent_id": "Iteration:ThreeParksChew", - "upstream": [ - "Generate:RealLoopsVanish" - ] - }, - "begin": { - "downstream": [ - "Generate:EveryCoinsStare" - ], - "obj": { - "component_name": "Begin", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "prologue": "", - "query": [ - { - "key": "title", - "name": "Title", - "optional": false, - "type": "line" - }, - { - "key": "language", - "name": "Language", - "optional": false, - "type": "line" - } - ] - } - }, - "upstream": [] - } - }, - "embed_id": "", - "graph": { - "edges": [ - { - "id": "reactflow__edge-Baidu:SharpHotelsNailb-Generate:RealCamerasSendb", - "markerEnd": "logo", - "source": "Baidu:SharpHotelsNail", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:RealCamerasSend", - "targetHandle": "b", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "reactflow__edge-Generate:BeigeEyesFlyb-Template:ThinSnailsDreamc", - "markerEnd": "logo", - "source": "Generate:BeigeEyesFly", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Template:ThinSnailsDream", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "reactflow__edge-IterationItem:RudeTablesSmile-Baidu:MeanBroomsMatterc", - "markerEnd": "logo", - "source": "IterationItem:RudeTablesSmile", - "sourceHandle": null, - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Baidu:MeanBroomsMatter", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Generate:EveryCoinsStareb-Generate:RedWormsDoublec", - "markerEnd": "logo", - "source": "Generate:EveryCoinsStare", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:RedWormsDouble", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__begin-Generate:EveryCoinsStarec", - "markerEnd": "logo", - "source": "begin", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:EveryCoinsStare", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Generate:RedWormsDoubleb-Iteration:ThreeParksChewc", - "markerEnd": "logo", - "source": "Generate:RedWormsDouble", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Iteration:ThreeParksChew", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Generate:EveryCoinsStareb-Iteration:BlueClothsGrabc", - "markerEnd": "logo", - "source": "Generate:EveryCoinsStare", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Iteration:BlueClothsGrab", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Baidu:MeanBroomsMatterb-Generate:YoungClownsKnockb", - "markerEnd": "logo", - "source": "Baidu:MeanBroomsMatter", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:YoungClownsKnock", - "targetHandle": "b", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__IterationItem:OliveStatesSmoke-Generate:RealLoopsVanishc", - "markerEnd": "logo", - "source": "IterationItem:OliveStatesSmoke", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:RealLoopsVanish", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Generate:RealLoopsVanishb-Template:SpottyWaspsLoseb", - "markerEnd": "logo", - "source": "Generate:RealLoopsVanish", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Template:SpottyWaspsLose", - "targetHandle": "b", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Iteration:ThreeParksChewb-Template:LegalDoorsActc", - "markerEnd": "logo", - "source": "Iteration:ThreeParksChew", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Template:LegalDoorsAct", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Template:LegalDoorsActb-Answer:WittyBottlesJogc", - "markerEnd": "logo", - "source": "Template:LegalDoorsAct", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Answer:WittyBottlesJog", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - } - ], - "nodes": [ - { - "data": { - "form": { - "prologue": "", - "query": [ - { - "key": "title", - "name": "Title", - "optional": false, - "type": "line" - }, - { - "key": "language", - "name": "Language", - "optional": false, - "type": "line" - } - ] - }, - "label": "Begin", - "name": "begin" - }, - "dragging": false, - "height": 130, - "id": "begin", - "measured": { - "height": 130, - "width": 200 - }, - "position": { - "x": -231.29149905979648, - "y": 95.28494230291383 - }, - "positionAbsolute": { - "x": -185.67257819905137, - "y": 108.15225637884839 - }, - "selected": false, - "sourcePosition": "left", - "targetPosition": "right", - "type": "beginNode", - "width": 200 - }, - { - "data": { - "form": {}, - "label": "Answer", - "name": "Interact_0" - }, - "dragging": false, - "height": 44, - "id": "Answer:WittyBottlesJog", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": 1458.2651570288865, - "y": 164.22699667633927 - }, - "positionAbsolute": { - "x": 1462.7745767525992, - "y": 231.9248108743051 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "logicNode", - "width": 200 - }, - { - "data": { - "form": { - "delimiter": ",", - "query": [ - { - "component_id": "Generate:EveryCoinsStare", - "type": "reference" - } - ] - }, - "label": "Iteration", - "name": "Search" - }, - "dragging": false, - "height": 192, - "id": "Iteration:BlueClothsGrab", - "measured": { - "height": 192, - "width": 334 - }, - "position": { - "x": 432.63496522555613, - "y": 228.82343789018051 - }, - "positionAbsolute": { - "x": 441.29535207641436, - "y": 291.9929929170084 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 337, - "width": 356 - }, - "targetPosition": "left", - "type": "group", - "width": 334 - }, - { - "data": { - "form": {}, - "label": "IterationItem", - "name": "IterationItem" - }, - "dragging": false, - "extent": "parent", - "height": 44, - "id": "IterationItem:RudeTablesSmile", - "measured": { - "height": 44, - "width": 44 - }, - "parentId": "Iteration:BlueClothsGrab", - "position": { - "x": 22, - "y": 10 - }, - "positionAbsolute": { - "x": -261.5, - "y": -288.14062500000006 - }, - "selected": false, - "type": "iterationStartNode", - "width": 44 - }, - { - "data": { - "form": { - "query": [ - { - "component_id": "IterationItem:RudeTablesSmile", - "type": "reference" - } - ], - "top_n": 10 - }, - "label": "Baidu", - "name": "Baidu" - }, - "dragging": false, - "extent": "parent", - "height": 64, - "id": "Baidu:MeanBroomsMatter", - "measured": { - "height": 64, - "width": 200 - }, - "parentId": "Iteration:BlueClothsGrab", - "position": { - "x": 200, - "y": 0 - }, - "positionAbsolute": { - "x": -83.49999999999999, - "y": -298.14062500000006 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "ragNode", - "width": 200 - }, - { - "data": { - "form": { - "delimiter": "\n", - "query": [ - { - "component_id": "Generate:RedWormsDouble", - "type": "reference" - } - ] - }, - "label": "Iteration", - "name": "Sections" - }, - "dragging": false, - "height": 225, - "id": "Iteration:ThreeParksChew", - "measured": { - "height": 225, - "width": 315 - }, - "position": { - "x": 888.9524716285371, - "y": 75.91277516159235 - }, - "positionAbsolute": { - "x": 891.9430519048244, - "y": 39.64877134989487 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 438, - "width": 328 - }, - "targetPosition": "left", - "type": "group", - "width": 315 - }, - { - "data": { - "form": {}, - "label": "IterationItem", - "name": "IterationItem" - }, - "dragging": false, - "extent": "parent", - "height": 44, - "id": "IterationItem:OliveStatesSmoke", - "measured": { - "height": 44, - "width": 44 - }, - "parentId": "Iteration:ThreeParksChew", - "position": { - "x": 24.66038685085823, - "y": 37.00025154774299 - }, - "positionAbsolute": { - "x": 780.5000000000002, - "y": 432.859375 - }, - "selected": false, - "type": "iterationStartNode", - "width": 44 - }, - { - "data": { - "form": { - "text": "It can generate a research report base on the title and language you provide." - }, - "label": "Note", - "name": "Usage" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 168, - "id": "Note:PoorMirrorsJump", - "measured": { - "height": 168, - "width": 275 - }, - "position": { - "x": -192.4712202594548, - "y": -164.26382748469516 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "noteNode", - "width": 275 - }, - { - "data": { - "form": { - "text": "LLM provides a series of search engine queries related to the proposition. Comprehensive research can be conducted through queries from different perspectives." - }, - "label": "Note", - "name": "N-Query" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 207, - "id": "Note:TwoSingersFly", - "measured": { - "height": 207, - "width": 256 - }, - "position": { - "x": 90.71637834539166, - "y": -160.7863367019141 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "noteNode", - "width": 256 - }, - { - "data": { - "form": { - "text": "LLM generates 4 subtitles for this report according to queries and title." - }, - "label": "Note", - "name": "N-Subtitles" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "id": "Note:SmoothAreasBet", - "measured": { - "height": 128, - "width": 266 - }, - "position": { - "x": 431.07789651000473, - "y": -161.0756093374443 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "noteNode" - }, - { - "data": { - "form": { - "text": "LLM generates a report for each query based on search result of each query.\nYou could change Baidu to other search engines." - }, - "label": "Note", - "name": "N-Search" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 168, - "id": "Note:CleanTablesCamp", - "measured": { - "height": 168, - "width": 364 - }, - "position": { - "x": 435.9578972976612, - "y": 452.5021839330345 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "noteNode", - "width": 364 - }, - { - "data": { - "form": { - "text": "LLM generates 4 sub-sections for 4 subtitles based on the report of search engine result." - }, - "label": "Note", - "name": "N-Sections" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 142, - "id": "Note:FamousToesReply", - "measured": { - "height": 142, - "width": 336 - }, - "position": { - "x": 881.4352587545767, - "y": -165.7333893115248 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "noteNode", - "width": 336 - }, - { - "data": { - "form": { - "cite": false, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 1, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "\n\nGenerate a series of appropriate search engine queries to break down questions based on user inquiries\n\n\n\n\nInput: User asks how to learn programming\nOutput: programming learning methods, programming tutorials for beginners\n\n\n\nInput: User wants to understand latest technology trends \nOutput: tech trends 2024, latest technology news\n\n\n\nInput: User seeks healthy eating advice\nOutput: healthy eating guide, balanced nutrition diet\n\n\n\n\n1. Take user's question as input.\n2. Identify relevant keywords or phrases based on the topic of user's question.\n3. Use these keywords or phrases to make search engine queries.\n4. Generate a series of appropriate search engine queries to help break down user's question.\n5. Ensure output content does not contain any xml tags.\n6. The output must be pure and conform to the style without other explanations.\n7. Break down into at least 4-6 subproblems.\n8. Output is separated only by commas.\n\n\n\ntitle: {begin@title}\nlanguage: {begin@language}\nThe output must be pure and conform to the style without other explanations.\nOutput is separated only by commas.\nBreak down into at least 4-6 subproblems.\n\nOutput:", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "GenQuery" - }, - "dragging": false, - "id": "Generate:EveryCoinsStare", - "measured": { - "height": 106, - "width": 200 - }, - "position": { - "x": 42.60311386535324, - "y": 107.45415912015176 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode" - }, - { - "data": { - "form": { - "cite": false, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": false, - "max_tokens": 256, - "message_history_window_size": 1, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "According to query: ' {Generate:EveryCoinsStare}',for ' {begin@title}', generate 3 to 5 sub-titles.\n\n\nPlease generate 4 subheadings for the main title following these steps:\n - 1. Carefully read the provided main title and related content\n - 2. Analyze the core theme and key information points of the main title\n - 3. Ensure the generated subheadings maintain consistency and relevance with the main title\n - 4. Each subheading should:\n - Be concise and appropriate in length\n - Highlight a unique angle or key point\n - Capture readers' interest\n - Match the overall style and tone of the article\n - 5. Between subheadings:\n - Content should not overlap\n - Logical order should be maintained\n - Should collectively support the main title\n - Use numerical sequence (1, 2, 3...) to mark each subheading\n - 6. Output format requirements:\n - Each subheading on a separate line\n - No XML tags included\n - Output subheadings content only\n\n\nlanguage: {begin@language}\nGenerate a series of appropriate sub-title to help break down ' {begin@title}'.\nBreaks down complex topics into manageable subtopics.\n\nOutput:", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "Subtitles" - }, - "dragging": false, - "id": "Generate:RedWormsDouble", - "measured": { - "height": 106, - "width": 200 - }, - "position": { - "x": 433.41522248658606, - "y": 14.302437349777136 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode" - }, - { - "data": { - "form": { - "cite": false, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": false, - "max_tokens": 256, - "message_history_window_size": 1, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "Your goal is to provide answers based on information from the internet. \nYou must use the provided search results to find relevant online information. \nYou should never use your own knowledge to answer questions.\nPlease include relevant url sources in the end of your answers.\n{Baidu:MeanBroomsMatter}\n\n\n\n\n\nlanguage: {begin@language}\n\n\n \" {Baidu:MeanBroomsMatter}\" \n\n\n\n\nUsing the above information, answer the following question or topic: \" {IterationItem:RudeTablesSmile} \"\nin a detailed report — The report should focus on the answer to the question, should be well structured, informative, in depth, with facts and numbers if available, a minimum of 1,200 words and with markdown syntax and apa format. Write all source urls at the end of the report in apa format. You should write your report only based on the given information and nothing else.", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "GenSearchReport" - }, - "dragging": false, - "extent": "parent", - "id": "Generate:YoungClownsKnock", - "measured": { - "height": 106, - "width": 200 - }, - "parentId": "Iteration:BlueClothsGrab", - "position": { - "x": 115.34644687476163, - "y": 73.07611243293042 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode" - }, - { - "data": { - "form": { - "cite": false, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": false, - "max_tokens": 256, - "message_history_window_size": 1, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "In a detailed report — The report should focus on the answer to {IterationItem:OliveStatesSmoke}and nothing else.\n\n\nLanguage: {begin@language}\nContext as bellow: \n\n\"{Iteration:BlueClothsGrab}\"\n\nProvide the research report in the specified language, avoiding small talk.\nThe main content is provided in markdown format\nWrite all source urls at the end of the report in apa format. ", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "Subtitle-content" - }, - "dragging": false, - "extent": "parent", - "id": "Generate:RealLoopsVanish", - "measured": { - "height": 106, - "width": 200 - }, - "parentId": "Iteration:ThreeParksChew", - "position": { - "x": 189.94391141062363, - "y": 5.408501635610101 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode" - }, - { - "data": { - "form": { - "content": "

{IterationItem:OliveStatesSmoke}

\n
{Generate:RealLoopsVanish}
", - "parameters": [] - }, - "label": "Template", - "name": "Sub-section" - }, - "dragging": false, - "extent": "parent", - "id": "Template:SpottyWaspsLose", - "measured": { - "height": 76, - "width": 200 - }, - "parentId": "Iteration:ThreeParksChew", - "position": { - "x": 107.51010102435532, - "y": 127.82322102671017 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "templateNode" - }, - { - "data": { - "form": { - "content": "

{begin@title}

\n\n\n\n{Iteration:ThreeParksChew}", - "parameters": [] - }, - "label": "Template", - "name": "Article" - }, - "dragging": false, - "id": "Template:LegalDoorsAct", - "measured": { - "height": 76, - "width": 200 - }, - "position": { - "x": 1209.0758608851872, - "y": 149.01984563839733 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "templateNode" - } - ] - }, - "history": [], - "messages": [], - "path": [], - "reference": [] - }, - "avatar": "data:image/jpeg;base64,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" -} \ No newline at end of file diff --git a/agent/templates/seo_blog.json b/agent/templates/seo_blog.json deleted file mode 100644 index 051d746..0000000 --- a/agent/templates/seo_blog.json +++ /dev/null @@ -1,1209 +0,0 @@ -{ - "id": 9, - "title": "SEO Blog Generator", - "description": "A blog generator that creates SEO-optimized content based on your chosen title or keywords.", - "canvas_type": "chatbot", - "dsl": { - "answer": [], - "components": { - "Answer:TameWavesChange": { - "downstream": [], - "obj": { - "component_name": "Answer", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "post_answers": [], - "query": [] - } - }, - "upstream": [ - "Template:YellowPlumsYell" - ] - }, - "Baidu:SharpSignsBeg": { - "downstream": [ - "Generate:FastTipsCamp" - ], - "obj": { - "component_name": "Baidu", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [ - { - "component_id": "Generate:PublicPotsPush", - "type": "reference" - } - ], - "top_n": 10 - } - }, - "upstream": [ - "Generate:PublicPotsPush" - ] - }, - "Baidu:ShyTeamsJuggle": { - "downstream": [ - "Generate:ReadyHandsInvent" - ], - "obj": { - "component_name": "Baidu", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [ - { - "component_id": "begin@keywords", - "type": "reference" - } - ], - "top_n": 10 - } - }, - "upstream": [ - "Switch:LargeWaspsSlide" - ] - }, - "Generate:CuddlyBatsCamp": { - "downstream": [ - "Template:YellowPlumsYell" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": false, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 0, - "message_history_window_size": 1, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "You are an SEO expert who writes in a direct, practical, educational style that is factual rather than storytelling or narrative, focusing on explaining to {begin@audience} the \"how\" and \"what is\" and “why” rather than narrating to the audience. \n - Please write at a sixth grade reading level. \n - ONLY output in Markdown format.\n - Use positive, present tense expressions and avoid using complex words and sentence structures that lack narrative, such as \"reveal\" and \"dig deep.\"\n - Next, please continue writing articles related to our topic with a concise title, {begin@title}{Generate:ReadyHandsInvent} {begin@keywords}{Generate:FancyMomentsTalk}. \n - Please AVOID repeating what has already been written and do not use the same sentence structure. \n - JUST write the body of the article based on the outline.\n - DO NOT include introduction, title.\n - DO NOT miss anything mentioned in article outline, except introduction and title.\n - Please use the information I provide to create in-depth, interesting and unique content. Also, incorporate the references and data points I provided earlier into the article to increase its value to the reader.\n - MUST be in language as \" {begin@keywords} {begin@title}\".\n\n\n{Generate:FastTipsCamp}\n\n", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "Generate:FastTipsCamp" - ] - }, - "Generate:FancyMomentsTalk": { - "downstream": [ - "Generate:PublicPotsPush" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": false, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 256, - "message_history_window_size": 12, - "output": null, - "output_var_name": "output", - "parameters": [ - { - "component_id": "begin@title", - "id": "2beef84b-204b-475a-89b3-3833bd108088", - "key": "title" - } - ], - "presence_penalty": 0.4, - "prompt": "I'm doing research for an article called {begin@title}, what relevant, high-traffic phrase should I type into Google to find this article? Just return the phrase without including any special symbols like quotes and colons.", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "Switch:LargeWaspsSlide" - ] - }, - "Generate:FastTipsCamp": { - "downstream": [ - "Generate:FortyBirdsAsk", - "Generate:CuddlyBatsCamp" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": false, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 0, - "message_history_window_size": 1, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "I'm an expert blogger.\nHere is some research I did for the blog post title \" {begin@title} {Generate:ReadyHandsInvent}\".\nThese are related search results:\n{Baidu:SharpSignsBeg}\n\nPlease study it in depth:\n\nArticle title: {begin@title} {Generate:ReadyHandsInvent}\nTarget keywords: {begin@keywords} {Generate:FancyMomentsTalk}\nMy blog post’s audience: {begin@audience}\nExclude brands: {begin@brands_to_avoid}\n\nCan you write a detailed blog outline with unique chapters? \n - The outline should include specific points and details that the article can mention. \n - AVOID generalities. \n - This SHOULD be researched in depth, not generalized.\n - Each chapter includes 7-8 projects, use some of the links above for reference if you can. For each item, don't just say \"discuss how\" but actually explain in detail the points that can be made. \n - DO NOT include things that you know are false and may contain inaccuracies. You are writing for a mature audience, avoid generalities and make specific quotes. Make sure to define key terms for users in your outline. Stay away from very controversial topics. \n - In the introduction, provide the background information needed for the rest of the article.\n - Please return in base array format and only the outline array, escaping quotes in the format. Each array item includes a complete chapter:\n[\"Includes Chapter 1 of all sub-projects\", \"Includes Chapter 2 of all sub-projects\", \"Includes Chapter 3 of all sub-projects\", \"Includes Chapter 4 of all sub-projects\"...etc.]\n - Each section SHOULD be wrapped with \"\" and ensure escaping within the content to ensure it is a valid array item.\n - MUST be in language of \" {begin@keywords} {begin@title}\".\n\nHere is an example of valid output. Please follow this structure and ignore the content:\n[\n \"Introduction - Explore the vibrant city of Miami, a destination that offers rich history, diverse culture, and many hidden treasures. Discover the little-known wonders that make Miami a unique destination for adventure seekers. Explore from historical landmarks to exotic places Attractions include atmospheric neighborhoods, local cuisine and lively nightlife. \",\n \"History of Miami - Begin the adventure with a journey into Miami's past. Learn about the city's transformation from a sleepy settlement to a busy metropolis. Understand the impact of multiculturalism on the city's development, as reflected in its architecture, cuisine and lifestyle See. Discover the historical significance of Miami landmarks like Hemingway's home. Uncover the fascinating stories of famous Miami neighborhoods like Key West. Explore the role of art and culture in shaping Miami, as shown at Art Basel events.\n\"Major Attractions - Go beyond Miami's famous beaches and explore the city's top attractions. Discover the artistic talent of the Wynwood Arts District, known for its vibrant street art. Visit iconic South Beach, known for its nightlife and boutiques . Explore the charming Coconut Grove district, known for its tree-lined streets and shopping areas. Visit the Holocaust Memorial Museum, a sombre reminder of a dark chapter in human history. Explore the Everglades Country, one of Miami's natural treasures. The park's diverse wildlife \",\n\"Trail Discovery - Get off the tourist trail and discover Miami's hidden treasures. Experience a water taxi tour across Biscayne Bay to get another perspective on the city. Visit the little-known Kabinett Department of Art, showcasing unique installation art . Explore the abandoned bridges and hidden bars of Duval Street and go on a culinary adventure in local neighborhoods known for their authentic cuisine. Go shopping at Brickell City Center, a trendy shopping and apartment complex in the heart of Miami. body.\",\n\"Local Cuisine - Dive into Miami's food scene and sample the city's diverse flavors. Enjoy ultra-fresh food and drinks at Bartaco, a local favorite. Experience fine dining at upscale Italian restaurants like Il Mulino New York. Explore the city ’s local food market and sample delicious local produce in Miami. Try a unique blend of Cuban and American cuisine that is a testament to Miami’s multicultural heritage.\"\n\"Nightlife - Experience the city's lively nightlife, a perfect blend of sophistication and fun. Visit America's Social Bar & Kitchen, a sports\nA hotspot for enthusiasts. Explore the nightlife of Mary Brickell Village, known for its clubby atmosphere. Spend an evening at Smith & Walensky Miami Beach's South Point Park, known for its stunning views and vintage wines. Visit iconic Miami Beach, famous for its pulsating nightlife. \",\n \"Conclusion- Miami is more than just stunning beaches and dazzling nightlife. It is a treasure trove of experiences waiting to be discovered. From its rich history and diverse culture to its hidden treasures, local cuisine and lively nightlife, Miami has something for everyone A traveler offers a unique adventure to experience the magic of Miami Beach and create unforgettable memories with your family.\"\n]", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "Baidu:SharpSignsBeg" - ] - }, - "Generate:FortyBirdsAsk": { - "downstream": [ - "Template:YellowPlumsYell" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": false, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 0, - "message_history_window_size": 1, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "You are an SEO expert who writes in a direct, practical, educational style that is factual rather than storytelling or narrative, focusing on explaining to {begin@audience} the \"how\" and \"what is\" and “why” rather than narrating to the audience. \n - Please write at a sixth grade reading level. \n - ONLY output in Markdown format.\n - Use active, present tense, avoid using complex language and syntax, such as \"unravel\", \"dig deeper\", etc., \n - DO NOT provide narration.\n - Now, excluding the title, introduce the blog in 3-5 sentences. \n - Use h2 headings to write chapter titles. \n - Provide a concise, SEO-optimized title. \n - DO NOT include h3 subheadings. \n - Feel free to use bullet points, numbered lists or paragraphs, or bold text for emphasis when appropriate. \n - You should transition naturally to each section, build on each section, and should NOT repeat the same sentence structure. \n - JUST write the introduction of the article based on the outline.\n - DO NOT include title, conclusions, summaries, or summaries, no \"summaries,\" \"conclusions,\" or variations. \n - DO NOT include links or mention any companies that compete with the brand (avoid mentioning {begin@brands_to_avoid}).\n - JUST write the introduction of the article based on the outline.\n - MUST be in language as \"{Generate:FancyMomentsTalk} {Generate:ReadyHandsInvent}\".\n\n\n{Generate:FastTipsCamp}\n\n\n", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "Generate:FastTipsCamp" - ] - }, - "Generate:PublicPotsPush": { - "downstream": [ - "Baidu:SharpSignsBeg" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": false, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 256, - "message_history_window_size": 1, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "I want a Google search phrase to get authoritative information for my article \" {begin@title} {Generate:ReadyHandsInvent} {begin@keywords} {Generate:FancyMomentsTalk}\" for {begin@audience}. Please return a search phrase of five words or less so that I can get a good overview of the topic. Include any words you're unfamiliar with in your search query.", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "Generate:ReadyHandsInvent", - "Generate:FancyMomentsTalk" - ] - }, - "Generate:ReadyHandsInvent": { - "downstream": [ - "Generate:PublicPotsPush" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": false, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 256, - "message_history_window_size": 1, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "Role: You are an SEO expert and subject area expert. Your task is to generate an SEO article title based on the keywords provided by the user and the context of the Google search.\n\nThe context of the Google search is as follows:\n{Baidu:ShyTeamsJuggle}\nThe context of the Google search is as above.\n\nIn order to craft an SEO article title that is keyword friendly and aligns with the principles observed in the top results you share, it is important to understand why these titles are effective. Here are the principles that may help them rank high:\n1. **Keyword Placement and Clarity**: Each title directly responds to the query by containing the exact keyword or a very close variation. This clarity ensures that search engines can easily understand the relevance of the content.\n2. **Succinctness and directness**: The title is concise, making it easy to read and understand quickly. They avoid unnecessary words and get straight to the point.\n3. **Contains a definition or explanation**: The title implies that the article will define or explain the concept, which is what people searching for \"{Generate:FancyMomentsTalk}\" are looking for.\n4. **Variety of Presentation**: Despite covering similar content, each title approaches the topic from a slightly different angle. This diversity can attract the interest of a wider audience.\n\nGiven these principles, please help me generate a title that will be optimized for the keyword \"{Generate:FancyMomentsTalk}\" based on the syntax of a top-ranking title. \n\nPlease don't copy, but give better options, and avoid using language like \"master,\" \"comprehensive,\" \"discover,\" or \"reveal.\" \n\nDo not use gerunds, only active tense and present tense. \n\nTitle SHOULD be in language as \"{Generate:FancyMomentsTalk}\"\n\nJust return the title.", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "Baidu:ShyTeamsJuggle" - ] - }, - "Switch:LargeWaspsSlide": { - "downstream": [ - "Baidu:ShyTeamsJuggle", - "Generate:FancyMomentsTalk" - ], - "obj": { - "component_name": "Switch", - "inputs": [], - "output": null, - "params": { - "conditions": [ - { - "items": [ - { - "cpn_id": "begin@title", - "operator": "empty" - } - ], - "logical_operator": "and", - "to": "Baidu:ShyTeamsJuggle" - } - ], - "debug_inputs": [], - "end_cpn_id": "Generate:FancyMomentsTalk", - "inputs": [], - "message_history_window_size": 22, - "operators": [ - "contains", - "not contains", - "start with", - "end with", - "empty", - "not empty", - "=", - "≠", - ">", - "<", - "≥", - "≤" - ], - "output": null, - "output_var_name": "output", - "query": [] - } - }, - "upstream": [ - "begin" - ] - }, - "Template:YellowPlumsYell": { - "downstream": [ - "Answer:TameWavesChange" - ], - "obj": { - "component_name": "Template", - "inputs": [], - "output": null, - "params": { - "content": "\n##{begin@title}{Generate:ReadyHandsInvent}\n\n{Generate:FortyBirdsAsk}\n\n\n\n{Generate:CuddlyBatsCamp}\n\n\n", - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "parameters": [], - "query": [] - } - }, - "upstream": [ - "Generate:FortyBirdsAsk", - "Generate:CuddlyBatsCamp" - ] - }, - "begin": { - "downstream": [ - "Switch:LargeWaspsSlide" - ], - "obj": { - "component_name": "Begin", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "prologue": "", - "query": [ - { - "key": "title", - "name": "Title", - "optional": true, - "type": "line" - }, - { - "key": "keywords", - "name": "Keywords", - "optional": true, - "type": "line" - }, - { - "key": "audience", - "name": "Audience", - "optional": true, - "type": "line" - }, - { - "key": "brands_to_avoid", - "name": "Brands to avoid", - "optional": true, - "type": "line" - } - ] - } - }, - "upstream": [] - } - }, - "embed_id": "", - "graph": { - "edges": [ - { - "id": "reactflow__edge-begin-Switch:LargeWaspsSlidea", - "markerEnd": "logo", - "source": "begin", - "sourceHandle": null, - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Switch:LargeWaspsSlide", - "targetHandle": "a", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Switch:LargeWaspsSlideCase 1-Baidu:ShyTeamsJugglec", - "markerEnd": "logo", - "source": "Switch:LargeWaspsSlide", - "sourceHandle": "Case 1", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Baidu:ShyTeamsJuggle", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Switch:LargeWaspsSlideend_cpn_id-Generate:FancyMomentsTalkc", - "markerEnd": "logo", - "source": "Switch:LargeWaspsSlide", - "sourceHandle": "end_cpn_id", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:FancyMomentsTalk", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "xy-edge__Baidu:ShyTeamsJuggleb-Generate:ReadyHandsInventc", - "markerEnd": "logo", - "source": "Baidu:ShyTeamsJuggle", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:ReadyHandsInvent", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Generate:ReadyHandsInventb-Generate:PublicPotsPushc", - "markerEnd": "logo", - "source": "Generate:ReadyHandsInvent", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:PublicPotsPush", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Generate:FancyMomentsTalkb-Generate:PublicPotsPushc", - "markerEnd": "logo", - "source": "Generate:FancyMomentsTalk", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:PublicPotsPush", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Generate:PublicPotsPushb-Baidu:SharpSignsBegc", - "markerEnd": "logo", - "source": "Generate:PublicPotsPush", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Baidu:SharpSignsBeg", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Baidu:SharpSignsBegb-Generate:FastTipsCampc", - "markerEnd": "logo", - "source": "Baidu:SharpSignsBeg", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:FastTipsCamp", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Generate:FastTipsCampb-Generate:FortyBirdsAskc", - "markerEnd": "logo", - "source": "Generate:FastTipsCamp", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:FortyBirdsAsk", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Generate:FastTipsCampb-Generate:CuddlyBatsCampc", - "markerEnd": "logo", - "source": "Generate:FastTipsCamp", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:CuddlyBatsCamp", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Generate:FortyBirdsAskb-Template:YellowPlumsYellc", - "markerEnd": "logo", - "source": "Generate:FortyBirdsAsk", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Template:YellowPlumsYell", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Generate:CuddlyBatsCampb-Template:YellowPlumsYellc", - "markerEnd": "logo", - "source": "Generate:CuddlyBatsCamp", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Template:YellowPlumsYell", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Template:YellowPlumsYellb-Answer:TameWavesChangec", - "markerEnd": "logo", - "source": "Template:YellowPlumsYell", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Answer:TameWavesChange", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - } - ], - "nodes": [ - { - "data": { - "form": { - "prologue": "", - "query": [ - { - "key": "title", - "name": "Title", - "optional": true, - "type": "line" - }, - { - "key": "keywords", - "name": "Keywords", - "optional": true, - "type": "line" - }, - { - "key": "audience", - "name": "Audience", - "optional": true, - "type": "line" - }, - { - "key": "brands_to_avoid", - "name": "Brands to avoid", - "optional": true, - "type": "line" - } - ] - }, - "label": "Begin", - "name": "begin" - }, - "dragging": false, - "height": 212, - "id": "begin", - "measured": { - "height": 212, - "width": 200 - }, - "position": { - "x": -432.2850120660528, - "y": 82.47567395502324 - }, - "positionAbsolute": { - "x": -432.2850120660528, - "y": 82.47567395502324 - }, - "selected": false, - "sourcePosition": "left", - "targetPosition": "right", - "type": "beginNode", - "width": 200 - }, - { - "data": { - "form": { - "conditions": [ - { - "items": [ - { - "cpn_id": "begin@title", - "operator": "empty" - } - ], - "logical_operator": "and", - "to": "Baidu:ShyTeamsJuggle" - } - ], - "end_cpn_id": "Generate:FancyMomentsTalk" - }, - "label": "Switch", - "name": "Empty title?" - }, - "dragging": false, - "height": 164, - "id": "Switch:LargeWaspsSlide", - "measured": { - "height": 164, - "width": 200 - }, - "position": { - "x": -171.8139076194234, - "y": 106.58178484885428 - }, - "positionAbsolute": { - "x": -171.8139076194234, - "y": 106.58178484885428 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "switchNode", - "width": 200 - }, - { - "data": { - "form": { - "query": [ - { - "component_id": "begin@keywords", - "type": "reference" - } - ], - "top_n": 10 - }, - "label": "Baidu", - "name": "Baidu4title" - }, - "dragging": false, - "height": 64, - "id": "Baidu:ShyTeamsJuggle", - "measured": { - "height": 64, - "width": 200 - }, - "position": { - "x": 99.2698941117485, - "y": 131.97513574677558 - }, - "positionAbsolute": { - "x": 99.2698941117485, - "y": 131.97513574677558 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "ragNode", - "width": 200 - }, - { - "data": { - "form": { - "cite": false, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 12, - "parameter": "Precise", - "parameters": [ - { - "component_id": "begin@title", - "id": "2beef84b-204b-475a-89b3-3833bd108088", - "key": "title" - } - ], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "I'm doing research for an article called {begin@title}, what relevant, high-traffic phrase should I type into Google to find this article? Just return the phrase without including any special symbols like quotes and colons.", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "Keywords gen" - }, - "dragging": false, - "height": 148, - "id": "Generate:FancyMomentsTalk", - "measured": { - "height": 148, - "width": 200 - }, - "position": { - "x": 102.41401952481024, - "y": 250.74278147746412 - }, - "positionAbsolute": { - "x": 102.41401952481024, - "y": 250.74278147746412 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode", - "width": 200 - }, - { - "data": { - "form": { - "query": [ - { - "component_id": "Generate:PublicPotsPush", - "type": "reference" - } - ], - "top_n": 10 - }, - "label": "Baidu", - "name": "Baidu4Info" - }, - "dragging": false, - "height": 64, - "id": "Baidu:SharpSignsBeg", - "measured": { - "height": 64, - "width": 200 - }, - "position": { - "x": 932.3075370153801, - "y": 293.31101119905543 - }, - "positionAbsolute": { - "x": 933.5156264729844, - "y": 289.6867428262425 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "ragNode", - "width": 200 - }, - { - "data": { - "form": {}, - "label": "Answer", - "name": "Interact_0" - }, - "dragging": false, - "height": 44, - "id": "Answer:TameWavesChange", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": 2067.9179213988796, - "y": 373.3415280349531 - }, - "positionAbsolute": { - "x": 2150.301454782809, - "y": 360.9062777128506 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "logicNode", - "width": 200 - }, - { - "data": { - "form": { - "text": "Function: Collect information such as keywords, titles, audience, words/brands to avoid, tone, and other details provided by the user.\n\nVariables:\n - keyword:Keywords\n - title:Title, \n - audience:Audience\n - brands_to_avoid:Words/brands to avoid.\n\nMUST NOT both of keywords and title are blank." - }, - "label": "Note", - "name": "N:Begin" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 368, - "id": "Note:FruityColtsBattle", - "measured": { - "height": 368, - "width": 275 - }, - "position": { - "x": -430.17115299591364, - "y": -320.31044749815453 - }, - "positionAbsolute": { - "x": -430.17115299591364, - "y": -320.31044749815453 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 368, - "width": 275 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 275 - }, - { - "data": { - "form": { - "text": "If title is not empty, let LLM help you to generate keywords." - }, - "label": "Note", - "name": "N: Keywords gen" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 128, - "id": "Note:SilverGiftsHide", - "measured": { - "height": 128, - "width": 269 - }, - "position": { - "x": 100.4673650631783, - "y": 414.8198461927788 - }, - "positionAbsolute": { - "x": 100.4673650631783, - "y": 414.8198461927788 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "noteNode", - "width": 269 - }, - { - "data": { - "form": { - "text": "Use user defined keywords to search.\nNext, generate a title based on the search result.\nChange to DuckDuckGo if you want." - }, - "label": "Note", - "name": "N: Baidu4title" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 192, - "id": "Note:ShaggyMelonsFail", - "measured": { - "height": 192, - "width": 254 - }, - "position": { - "x": 101.98068917850298, - "y": -79.85480052081127 - }, - "positionAbsolute": { - "x": 101.98068917850298, - "y": -79.85480052081127 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 192, - "width": 254 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 254 - }, - { - "data": { - "form": { - "text": "Let LLM to generate keywords to search. \nBased on the search result, the outline of the article will be generated." - }, - "label": "Note", - "name": "N: Words to search" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 132, - "id": "Note:EvilIdeasDress", - "measured": { - "height": 132, - "width": 496 - }, - "position": { - "x": 822.1382301557384, - "y": 1.1013324480075255 - }, - "positionAbsolute": { - "x": 822.1382301557384, - "y": 1.1013324480075255 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 132, - "width": 496 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 496 - }, - { - "data": { - "form": { - "text": "1 . User input:\nThe user enters information such as avoid keywords, title, audience, required words/brands, tone, etc. at the start node.\n\n2. Conditional judgment:\nCheck whether the title is empty, if it is empty, generate the title.\n\n3. Generate titles and keywords:\nGenerate SEO optimized titles and related keywords based on the entered user keywords.\n\n4. Web search:\nUse the generated titles and keywords to conduct a Google search to obtain relevant information.\n\n5. Generate outline and articles:\nGenerate article outlines, topics, and bodies based on user input information and search results.\n\n6. Template conversion and output:\nCombine the beginning of the article and the main body to generate a complete article, and output the result." - }, - "label": "Note", - "name": "Steps" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 456, - "id": "Note:WeakApesDivide", - "measured": { - "height": 456, - "width": 955 - }, - "position": { - "x": 441.5385839522079, - "y": 638.4606789293297 - }, - "positionAbsolute": { - "x": 377.5385839522079, - "y": 638.4606789293297 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 450, - "width": 827 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 955 - }, - { - "data": { - "form": { - "cite": false, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 1, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "Role: You are an SEO expert and subject area expert. Your task is to generate an SEO article title based on the keywords provided by the user and the context of the Google search.\n\nThe context of the Google search is as follows:\n{Baidu:ShyTeamsJuggle}\nThe context of the Google search is as above.\n\nIn order to craft an SEO article title that is keyword friendly and aligns with the principles observed in the top results you share, it is important to understand why these titles are effective. Here are the principles that may help them rank high:\n1. **Keyword Placement and Clarity**: Each title directly responds to the query by containing the exact keyword or a very close variation. This clarity ensures that search engines can easily understand the relevance of the content.\n2. **Succinctness and directness**: The title is concise, making it easy to read and understand quickly. They avoid unnecessary words and get straight to the point.\n3. **Contains a definition or explanation**: The title implies that the article will define or explain the concept, which is what people searching for \"{Generate:FancyMomentsTalk}\" are looking for.\n4. **Variety of Presentation**: Despite covering similar content, each title approaches the topic from a slightly different angle. This diversity can attract the interest of a wider audience.\n\nGiven these principles, please help me generate a title that will be optimized for the keyword \"{Generate:FancyMomentsTalk}\" based on the syntax of a top-ranking title. \n\nPlease don't copy, but give better options, and avoid using language like \"master,\" \"comprehensive,\" \"discover,\" or \"reveal.\" \n\nDo not use gerunds, only active tense and present tense. \n\nTitle SHOULD be in language as \"{Generate:FancyMomentsTalk}\"\n\nJust return the title.", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "Title Gen" - }, - "dragging": false, - "id": "Generate:ReadyHandsInvent", - "measured": { - "height": 106, - "width": 200 - }, - "position": { - "x": 362.61841535531624, - "y": 109.52633857873508 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode" - }, - { - "data": { - "form": { - "cite": false, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 1, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "I want a Google search phrase to get authoritative information for my article \" {begin@title} {Generate:ReadyHandsInvent} {begin@keywords} {Generate:FancyMomentsTalk}\" for {begin@audience}. Please return a search phrase of five words or less so that I can get a good overview of the topic. Include any words you're unfamiliar with in your search query.", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "Words to search" - }, - "dragging": false, - "id": "Generate:PublicPotsPush", - "measured": { - "height": 106, - "width": 200 - }, - "position": { - "x": 631.7110159663526, - "y": 271.70568678331114 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode" - }, - { - "data": { - "form": { - "cite": false, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": false, - "max_tokens": 256, - "message_history_window_size": 1, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "I'm an expert blogger.\nHere is some research I did for the blog post title \" {begin@title} {Generate:ReadyHandsInvent}\".\nThese are related search results:\n{Baidu:SharpSignsBeg}\n\nPlease study it in depth:\n\nArticle title: {begin@title} {Generate:ReadyHandsInvent}\nTarget keywords: {begin@keywords} {Generate:FancyMomentsTalk}\nMy blog post’s audience: {begin@audience}\nExclude brands: {begin@brands_to_avoid}\n\nCan you write a detailed blog outline with unique chapters? \n - The outline should include specific points and details that the article can mention. \n - AVOID generalities. \n - This SHOULD be researched in depth, not generalized.\n - Each chapter includes 7-8 projects, use some of the links above for reference if you can. For each item, don't just say \"discuss how\" but actually explain in detail the points that can be made. \n - DO NOT include things that you know are false and may contain inaccuracies. You are writing for a mature audience, avoid generalities and make specific quotes. Make sure to define key terms for users in your outline. Stay away from very controversial topics. \n - In the introduction, provide the background information needed for the rest of the article.\n - Please return in base array format and only the outline array, escaping quotes in the format. Each array item includes a complete chapter:\n[\"Includes Chapter 1 of all sub-projects\", \"Includes Chapter 2 of all sub-projects\", \"Includes Chapter 3 of all sub-projects\", \"Includes Chapter 4 of all sub-projects\"...etc.]\n - Each section SHOULD be wrapped with \"\" and ensure escaping within the content to ensure it is a valid array item.\n - MUST be in language of \" {begin@keywords} {begin@title}\".\n\nHere is an example of valid output. Please follow this structure and ignore the content:\n[\n \"Introduction - Explore the vibrant city of Miami, a destination that offers rich history, diverse culture, and many hidden treasures. Discover the little-known wonders that make Miami a unique destination for adventure seekers. Explore from historical landmarks to exotic places Attractions include atmospheric neighborhoods, local cuisine and lively nightlife. \",\n \"History of Miami - Begin the adventure with a journey into Miami's past. Learn about the city's transformation from a sleepy settlement to a busy metropolis. Understand the impact of multiculturalism on the city's development, as reflected in its architecture, cuisine and lifestyle See. Discover the historical significance of Miami landmarks like Hemingway's home. Uncover the fascinating stories of famous Miami neighborhoods like Key West. Explore the role of art and culture in shaping Miami, as shown at Art Basel events.\n\"Major Attractions - Go beyond Miami's famous beaches and explore the city's top attractions. Discover the artistic talent of the Wynwood Arts District, known for its vibrant street art. Visit iconic South Beach, known for its nightlife and boutiques . Explore the charming Coconut Grove district, known for its tree-lined streets and shopping areas. Visit the Holocaust Memorial Museum, a sombre reminder of a dark chapter in human history. Explore the Everglades Country, one of Miami's natural treasures. The park's diverse wildlife \",\n\"Trail Discovery - Get off the tourist trail and discover Miami's hidden treasures. Experience a water taxi tour across Biscayne Bay to get another perspective on the city. Visit the little-known Kabinett Department of Art, showcasing unique installation art . Explore the abandoned bridges and hidden bars of Duval Street and go on a culinary adventure in local neighborhoods known for their authentic cuisine. Go shopping at Brickell City Center, a trendy shopping and apartment complex in the heart of Miami. body.\",\n\"Local Cuisine - Dive into Miami's food scene and sample the city's diverse flavors. Enjoy ultra-fresh food and drinks at Bartaco, a local favorite. Experience fine dining at upscale Italian restaurants like Il Mulino New York. Explore the city ’s local food market and sample delicious local produce in Miami. Try a unique blend of Cuban and American cuisine that is a testament to Miami’s multicultural heritage.\"\n\"Nightlife - Experience the city's lively nightlife, a perfect blend of sophistication and fun. Visit America's Social Bar & Kitchen, a sports\nA hotspot for enthusiasts. Explore the nightlife of Mary Brickell Village, known for its clubby atmosphere. Spend an evening at Smith & Walensky Miami Beach's South Point Park, known for its stunning views and vintage wines. Visit iconic Miami Beach, famous for its pulsating nightlife. \",\n \"Conclusion- Miami is more than just stunning beaches and dazzling nightlife. It is a treasure trove of experiences waiting to be discovered. From its rich history and diverse culture to its hidden treasures, local cuisine and lively nightlife, Miami has something for everyone A traveler offers a unique adventure to experience the magic of Miami Beach and create unforgettable memories with your family.\"\n]", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "Outline gen" - }, - "dragging": false, - "id": "Generate:FastTipsCamp", - "measured": { - "height": 106, - "width": 200 - }, - "position": { - "x": 1188.847302971411, - "y": 272.42758089250634 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode" - }, - { - "data": { - "form": { - "cite": false, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": false, - "max_tokens": 256, - "message_history_window_size": 1, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "You are an SEO expert who writes in a direct, practical, educational style that is factual rather than storytelling or narrative, focusing on explaining to {begin@audience} the \"how\" and \"what is\" and “why” rather than narrating to the audience. \n - Please write at a sixth grade reading level. \n - ONLY output in Markdown format.\n - Use active, present tense, avoid using complex language and syntax, such as \"unravel\", \"dig deeper\", etc., \n - DO NOT provide narration.\n - Now, excluding the title, introduce the blog in 3-5 sentences. \n - Use h2 headings to write chapter titles. \n - Provide a concise, SEO-optimized title. \n - DO NOT include h3 subheadings. \n - Feel free to use bullet points, numbered lists or paragraphs, or bold text for emphasis when appropriate. \n - You should transition naturally to each section, build on each section, and should NOT repeat the same sentence structure. \n - JUST write the introduction of the article based on the outline.\n - DO NOT include title, conclusions, summaries, or summaries, no \"summaries,\" \"conclusions,\" or variations. \n - DO NOT include links or mention any companies that compete with the brand (avoid mentioning {begin@brands_to_avoid}).\n - JUST write the introduction of the article based on the outline.\n - MUST be in language as \"{Generate:FancyMomentsTalk} {Generate:ReadyHandsInvent}\".\n\n\n{Generate:FastTipsCamp}\n\n\n", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "Introduction gen" - }, - "dragging": false, - "id": "Generate:FortyBirdsAsk", - "measured": { - "height": 106, - "width": 200 - }, - "position": { - "x": 1467.1832072218494, - "y": 273.6641444369902 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode" - }, - { - "data": { - "form": { - "cite": false, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": false, - "max_tokens": 256, - "message_history_window_size": 1, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "You are an SEO expert who writes in a direct, practical, educational style that is factual rather than storytelling or narrative, focusing on explaining to {begin@audience} the \"how\" and \"what is\" and “why” rather than narrating to the audience. \n - Please write at a sixth grade reading level. \n - ONLY output in Markdown format.\n - Use positive, present tense expressions and avoid using complex words and sentence structures that lack narrative, such as \"reveal\" and \"dig deep.\"\n - Next, please continue writing articles related to our topic with a concise title, {begin@title}{Generate:ReadyHandsInvent} {begin@keywords}{Generate:FancyMomentsTalk}. \n - Please AVOID repeating what has already been written and do not use the same sentence structure. \n - JUST write the body of the article based on the outline.\n - DO NOT include introduction, title.\n - DO NOT miss anything mentioned in article outline, except introduction and title.\n - Please use the information I provide to create in-depth, interesting and unique content. Also, incorporate the references and data points I provided earlier into the article to increase its value to the reader.\n - MUST be in language as \" {begin@keywords} {begin@title}\".\n\n\n{Generate:FastTipsCamp}\n\n", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "Body gen" - }, - "dragging": false, - "id": "Generate:CuddlyBatsCamp", - "measured": { - "height": 108, - "width": 200 - }, - "position": { - "x": 1459.030461505832, - "y": 430.80927477654984 - }, - "selected": true, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode" - }, - { - "data": { - "form": { - "content": "\n##{begin@title}{Generate:ReadyHandsInvent}\n\n{Generate:FortyBirdsAsk}\n\n\n\n{Generate:CuddlyBatsCamp}\n\n\n", - "parameters": [] - }, - "label": "Template", - "name": "Template trans" - }, - "dragging": false, - "id": "Template:YellowPlumsYell", - "measured": { - "height": 76, - "width": 200 - }, - "position": { - "x": 1784.1452214476085, - "y": 356.5796437282643 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "templateNode" - } - ] - }, - "history": [], - "messages": [], - "path": [], - "reference": [] - }, - "avatar": "data:image/jpeg;base64,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" -} \ No newline at end of file diff --git a/agent/templates/text2sql.json b/agent/templates/text2sql.json deleted file mode 100644 index 9d59d30..0000000 --- a/agent/templates/text2sql.json +++ /dev/null @@ -1,651 +0,0 @@ -{ - "id": 5, - "title": "Text To SQL", - "description": "An agent that converts user queries into SQL statements. You must prepare three knowledge bases: 1: DDL for your database; 2: Examples of user queries converted to SQL statements; 3: A comprehensive description of your database, including but not limited to tables and records.", - "canvas_type": "chatbot", - "dsl": { - "answer": [], - "components": { - "Answer:SocialAdsWonder": { - "downstream": [ - "Retrieval:TrueCornersJam", - "Retrieval:EasyDryersShop", - "Retrieval:LazyChefsWatch" - ], - "obj": { - "component_name": "Answer", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "post_answers": [], - "query": [] - } - }, - "upstream": [ - "begin", - "Generate:CurlyFalconsWorry" - ] - }, - "Generate:CurlyFalconsWorry": { - "downstream": [ - "Answer:SocialAdsWonder" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": false, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 0, - "message_history_window_size": 1, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "prompt": "##The user provides a question and you provide SQL. You will only respond with SQL code and not with any explanations.\n\n##Respond with only SQL code. Do not answer with any explanations -- just the code.\n\n##You may use the following DDL statements as a reference for what tables might be available. Use responses to past questions also to guide you: {Retrieval:TrueCornersJam}.\n\n##You may use the following documentation as a reference for what tables might be available. Use responses to past questions also to guide you: {Retrieval:LazyChefsWatch}.\n\n##You may use the following SQL statements as a reference for what tables might be available. Use responses to past questions also to guide you: {Retrieval:EasyDryersShop}.\n\n", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "Retrieval:LazyChefsWatch", - "Retrieval:EasyDryersShop", - "Retrieval:TrueCornersJam" - ] - }, - "Retrieval:EasyDryersShop": { - "downstream": [ - "Generate:CurlyFalconsWorry" - ], - "obj": { - "component_name": "Retrieval", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "empty_response": "Nothing found in Q-SQL!", - "inputs": [], - "kb_ids": [], - "keywords_similarity_weight": 0.3, - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [], - "rerank_id": "", - "similarity_threshold": 0.2, - "top_k": 1024, - "top_n": 8 - } - }, - "upstream": [ - "Answer:SocialAdsWonder" - ] - }, - "Retrieval:LazyChefsWatch": { - "downstream": [ - "Generate:CurlyFalconsWorry" - ], - "obj": { - "component_name": "Retrieval", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "empty_response": "Nothing found in DB-Description!", - "inputs": [], - "kb_ids": [], - "keywords_similarity_weight": 0.3, - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [], - "rerank_id": "", - "similarity_threshold": 0.2, - "top_k": 1024, - "top_n": 8 - } - }, - "upstream": [ - "Answer:SocialAdsWonder" - ] - }, - "Retrieval:TrueCornersJam": { - "downstream": [ - "Generate:CurlyFalconsWorry" - ], - "obj": { - "component_name": "Retrieval", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "empty_response": "Nothing found in DDL!", - "inputs": [], - "kb_ids": [], - "keywords_similarity_weight": 0.3, - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [], - "rerank_id": "", - "similarity_threshold": 0.02, - "top_k": 1024, - "top_n": 8 - } - }, - "upstream": [ - "Answer:SocialAdsWonder" - ] - }, - "begin": { - "downstream": [ - "Answer:SocialAdsWonder" - ], - "obj": { - "component_name": "Begin", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "prologue": "Hi! I'm your smart assistant. What can I do for you?", - "query": [] - } - }, - "upstream": [] - } - }, - "embed_id": "", - "graph": { - "edges": [ - { - "id": "reactflow__edge-begin-Answer:SocialAdsWonderc", - "markerEnd": "logo", - "source": "begin", - "sourceHandle": null, - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Answer:SocialAdsWonder", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Answer:SocialAdsWonderb-Retrieval:TrueCornersJamc", - "markerEnd": "logo", - "source": "Answer:SocialAdsWonder", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Retrieval:TrueCornersJam", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Answer:SocialAdsWonderb-Retrieval:EasyDryersShopc", - "markerEnd": "logo", - "source": "Answer:SocialAdsWonder", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Retrieval:EasyDryersShop", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Answer:SocialAdsWonderb-Retrieval:LazyChefsWatchc", - "markerEnd": "logo", - "source": "Answer:SocialAdsWonder", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Retrieval:LazyChefsWatch", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "xy-edge__Retrieval:LazyChefsWatchb-Generate:CurlyFalconsWorryb", - "markerEnd": "logo", - "source": "Retrieval:LazyChefsWatch", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:CurlyFalconsWorry", - "targetHandle": "b", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Retrieval:EasyDryersShopb-Generate:CurlyFalconsWorryb", - "markerEnd": "logo", - "source": "Retrieval:EasyDryersShop", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:CurlyFalconsWorry", - "targetHandle": "b", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Retrieval:TrueCornersJamb-Generate:CurlyFalconsWorryb", - "markerEnd": "logo", - "source": "Retrieval:TrueCornersJam", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:CurlyFalconsWorry", - "targetHandle": "b", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Generate:CurlyFalconsWorryc-Answer:SocialAdsWonderc", - "markerEnd": "logo", - "source": "Generate:CurlyFalconsWorry", - "sourceHandle": "c", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Answer:SocialAdsWonder", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - } - ], - "nodes": [ - { - "data": { - "label": "Begin", - "name": "begin" - }, - "dragging": false, - "height": 44, - "id": "begin", - "measured": { - "height": 44, - "width": 100 - }, - "position": { - "x": -520.486587527275, - "y": 117.87988995940702 - }, - "positionAbsolute": { - "x": -520.486587527275, - "y": 117.87988995940702 - }, - "selected": false, - "sourcePosition": "left", - "targetPosition": "right", - "type": "beginNode" - }, - { - "data": { - "form": {}, - "label": "Answer", - "name": "interface" - }, - "dragging": false, - "height": 44, - "id": "Answer:SocialAdsWonder", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": -237.69220760465112, - "y": 119.9282206409824 - }, - "positionAbsolute": { - "x": -284.9289105495367, - "y": 119.9282206409824 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "logicNode", - "width": 200 - }, - { - "data": { - "form": { - "empty_response": "Nothing found in DDL!", - "kb_ids": [], - "keywords_similarity_weight": 0.3, - "similarity_threshold": 0.02, - "top_n": 8 - }, - "label": "Retrieval", - "name": "DDL" - }, - "dragging": false, - "height": 44, - "id": "Retrieval:TrueCornersJam", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": 119.61927071085717, - "y": -40.184181873335746 - }, - "positionAbsolute": { - "x": 119.61927071085717, - "y": -40.184181873335746 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "retrievalNode", - "width": 200 - }, - { - "data": { - "form": { - "empty_response": "Nothing found in Q-SQL!", - "kb_ids": [], - "keywords_similarity_weight": 0.3, - "similarity_threshold": 0.2, - "top_n": 8 - }, - "label": "Retrieval", - "name": "Q->SQL" - }, - "dragging": false, - "height": 44, - "id": "Retrieval:EasyDryersShop", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": 80.07777425685605, - "y": 120.03075150115158 - }, - "positionAbsolute": { - "x": 81.2024576603057, - "y": 94.16303322180948 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "retrievalNode", - "width": 200 - }, - { - "data": { - "form": { - "empty_response": "Nothing found in DB-Description!", - "kb_ids": [], - "keywords_similarity_weight": 0.3, - "similarity_threshold": 0.2, - "top_n": 8 - }, - "label": "Retrieval", - "name": "DB Description" - }, - "dragging": false, - "height": 44, - "id": "Retrieval:LazyChefsWatch", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": 51.228157704293324, - "y": 252.77721891325103 - }, - "positionAbsolute": { - "x": 51.228157704293324, - "y": 252.77721891325103 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "retrievalNode", - "width": 200 - }, - { - "data": { - "form": { - "text": "Receives a sentence that the user wants to convert into SQL and displays the result of the large model's SQL conversion." - }, - "label": "Note", - "name": "N: Interface" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 132, - "id": "Note:GentleRabbitsWonder", - "measured": { - "height": 132, - "width": 324 - }, - "position": { - "x": -287.3066094433631, - "y": -30.808189185380513 - }, - "positionAbsolute": { - "x": -287.3066094433631, - "y": -30.808189185380513 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 132, - "width": 324 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 324 - }, - { - "data": { - "form": { - "text": "The large model learns which tables may be available based on the responses from three knowledge bases and converts the user's input into SQL statements." - }, - "label": "Note", - "name": "N: LLM" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 163, - "id": "Note:SixCitiesJoke", - "measured": { - "height": 163, - "width": 334 - }, - "position": { - "x": 19.243366453487255, - "y": 531.9336820600888 - }, - "positionAbsolute": { - "x": 5.12121582244032, - "y": 637.6539219843564 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 147, - "width": 326 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 334 - }, - { - "data": { - "form": { - "text": "Searches for description about meanings of tables and fields." - }, - "label": "Note", - "name": "N: DB description" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 128, - "id": "Note:FamousCarpetsTaste", - "measured": { - "height": 128, - "width": 269 - }, - "position": { - "x": 399.9267065852242, - "y": 250.0329701879931 - }, - "positionAbsolute": { - "x": 399.9267065852242, - "y": 250.0329701879931 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "noteNode", - "width": 269 - }, - { - "data": { - "form": { - "text": "Searches for samples about question to SQL.\nPlease check this dataset: https://huggingface.co/datasets/InfiniFlow/text2sql" - }, - "label": "Note", - "name": "N: Q->SQL" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 140, - "id": "Note:PoliteBeesArrive", - "measured": { - "height": 140, - "width": 455 - }, - "position": { - "x": 491.0393427986917, - "y": 96.58232093146341 - }, - "positionAbsolute": { - "x": 489.0393427986917, - "y": 96.58232093146341 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 130, - "width": 451 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 455 - }, - { - "data": { - "form": { - "text": "DDL(Data Definition Language).\n\nSearches for relevant database creation statements.\n\nIt should bind with a KB to which DDL is dumped in.\nYou could use 'General' as parsing method and ';' as delimiter." - }, - "label": "Note", - "name": "N: DDL" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 272, - "id": "Note:SmartWingsDouble", - "measured": { - "height": 272, - "width": 288 - }, - "position": { - "x": 406.6930553966363, - "y": -208.84980249039137 - }, - "positionAbsolute": { - "x": 404.1930553966363, - "y": -208.84980249039137 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 258, - "width": 283 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 288 - }, - { - "data": { - "form": { - "cite": false, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": false, - "max_tokens": 256, - "message_history_window_size": 1, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "##The user provides a question and you provide SQL. You will only respond with SQL code and not with any explanations.\n\n##Respond with only SQL code. Do not answer with any explanations -- just the code.\n\n##You may use the following DDL statements as a reference for what tables might be available. Use responses to past questions also to guide you: {Retrieval:TrueCornersJam}.\n\n##You may use the following documentation as a reference for what tables might be available. Use responses to past questions also to guide you: {Retrieval:LazyChefsWatch}.\n\n##You may use the following SQL statements as a reference for what tables might be available. Use responses to past questions also to guide you: {Retrieval:EasyDryersShop}.\n\n", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "GenSQL" - }, - "dragging": false, - "id": "Generate:CurlyFalconsWorry", - "measured": { - "height": 106, - "width": 200 - }, - "position": { - "x": 10.728415797190792, - "y": 410.2569651241076 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode" - } - ] - }, - "history": [], - "messages": [], - "path": [], - "reference": [] - }, - "avatar": "data:image/png;base64,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" -} diff --git a/agent/templates/websearch_assistant.json b/agent/templates/websearch_assistant.json deleted file mode 100644 index 1b6308f..0000000 --- a/agent/templates/websearch_assistant.json +++ /dev/null @@ -1,996 +0,0 @@ -{ - "id": 0, - "title": "WebSearch Assistant", - "description": "A chat assistant template that integrates information extracted from a knowledge base and web searches to respond to queries. Let's begin by setting up your knowledge base in 'Retrieval'!", - "canvas_type": "chatbot", - "dsl": { - "answer": [], - "components": { - "Answer:PoorMapsCover": { - "downstream": [ - "RewriteQuestion:OrangeBottlesSwim" - ], - "obj": { - "component_name": "Answer", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "post_answers": [], - "query": [] - } - }, - "upstream": [ - "begin", - "Generate:ItchyRiversDrum" - ] - }, - "Baidu:OliveAreasCall": { - "downstream": [ - "Generate:ItchyRiversDrum" - ], - "obj": { - "component_name": "Baidu", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [ - { - "component_id": "KeywordExtract:BeigeTipsStand", - "type": "reference" - } - ], - "top_n": 2 - } - }, - "upstream": [ - "KeywordExtract:BeigeTipsStand" - ] - }, - "DuckDuckGo:SoftButtonsRefuse": { - "downstream": [ - "Generate:ItchyRiversDrum" - ], - "obj": { - "component_name": "DuckDuckGo", - "inputs": [], - "output": null, - "params": { - "channel": "text", - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [ - { - "component_id": "KeywordExtract:BeigeTipsStand", - "type": "reference" - } - ], - "top_n": 2 - } - }, - "upstream": [ - "KeywordExtract:BeigeTipsStand" - ] - }, - "Generate:ItchyRiversDrum": { - "downstream": [ - "Answer:PoorMapsCover" - ], - "obj": { - "component_name": "Generate", - "inputs": [], - "output": null, - "params": { - "cite": true, - "debug_inputs": [], - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "max_tokens": 0, - "message_history_window_size": 12, - "output": null, - "output_var_name": "output", - "parameters": [], - "presence_penalty": 0.4, - "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{Retrieval:SilentCamelsStick}\n\n\n## Wikipedia content\n{Wikipedia:WittyRiceLearn}\n\n\n## Duckduckgo content\n{DuckDuckGo:SoftButtonsRefuse}\n\n\n## Baidu content\n{Baidu:OliveAreasCall}\n\n", - "query": [], - "temperature": 0.1, - "top_p": 0.3 - } - }, - "upstream": [ - "Retrieval:SilentCamelsStick", - "Wikipedia:WittyRiceLearn", - "Baidu:OliveAreasCall", - "DuckDuckGo:SoftButtonsRefuse" - ] - }, - "KeywordExtract:BeigeTipsStand": { - "downstream": [ - "Baidu:OliveAreasCall", - "DuckDuckGo:SoftButtonsRefuse", - "Wikipedia:WittyRiceLearn" - ], - "obj": { - "component_name": "KeywordExtract", - "inputs": [], - "output": null, - "params": { - "cite": true, - "debug_inputs": [], - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "", - "query": [], - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_n": 2, - "top_p": 0.3 - } - }, - "upstream": [ - "RewriteQuestion:OrangeBottlesSwim" - ] - }, - "Retrieval:SilentCamelsStick": { - "downstream": [ - "Generate:ItchyRiversDrum" - ], - "obj": { - "component_name": "Retrieval", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "empty_response": "The answer you want was not found in the knowledge base!", - "inputs": [], - "kb_ids": [], - "keywords_similarity_weight": 0.3, - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [], - "rerank_id": "", - "similarity_threshold": 0.2, - "top_k": 1024, - "top_n": 8 - } - }, - "upstream": [ - "RewriteQuestion:OrangeBottlesSwim" - ] - }, - "RewriteQuestion:OrangeBottlesSwim": { - "downstream": [ - "KeywordExtract:BeigeTipsStand", - "Retrieval:SilentCamelsStick" - ], - "obj": { - "component_name": "RewriteQuestion", - "inputs": [], - "output": null, - "params": { - "cite": true, - "debug_inputs": [], - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "inputs": [], - "llm_id": "deepseek-chat@DeepSeek", - "loop": 1, - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 6, - "output": null, - "output_var_name": "output", - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "prompt": "", - "query": [], - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - } - }, - "upstream": [ - "Answer:PoorMapsCover" - ] - }, - "Wikipedia:WittyRiceLearn": { - "downstream": [ - "Generate:ItchyRiversDrum" - ], - "obj": { - "component_name": "Wikipedia", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "language": "en", - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "query": [ - { - "component_id": "KeywordExtract:BeigeTipsStand", - "type": "reference" - } - ], - "top_n": 2 - } - }, - "upstream": [ - "KeywordExtract:BeigeTipsStand" - ] - }, - "begin": { - "downstream": [ - "Answer:PoorMapsCover" - ], - "obj": { - "component_name": "Begin", - "inputs": [], - "output": null, - "params": { - "debug_inputs": [], - "inputs": [], - "message_history_window_size": 22, - "output": null, - "output_var_name": "output", - "prologue": "Hi! I'm your smart assistant. What can I do for you?", - "query": [] - } - }, - "upstream": [] - } - }, - "embed_id": "", - "graph": { - "edges": [ - { - "id": "reactflow__edge-begin-Answer:PoorMapsCoverc", - "markerEnd": "logo", - "source": "begin", - "sourceHandle": null, - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Answer:PoorMapsCover", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-Answer:PoorMapsCoverb-RewriteQuestion:OrangeBottlesSwimc", - "markerEnd": "logo", - "source": "Answer:PoorMapsCover", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "RewriteQuestion:OrangeBottlesSwim", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-RewriteQuestion:OrangeBottlesSwimb-KeywordExtract:BeigeTipsStandc", - "markerEnd": "logo", - "source": "RewriteQuestion:OrangeBottlesSwim", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "KeywordExtract:BeigeTipsStand", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-KeywordExtract:BeigeTipsStandb-Baidu:OliveAreasCallc", - "markerEnd": "logo", - "source": "KeywordExtract:BeigeTipsStand", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Baidu:OliveAreasCall", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-KeywordExtract:BeigeTipsStandb-DuckDuckGo:SoftButtonsRefusec", - "markerEnd": "logo", - "source": "KeywordExtract:BeigeTipsStand", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "DuckDuckGo:SoftButtonsRefuse", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-KeywordExtract:BeigeTipsStandb-Wikipedia:WittyRiceLearnc", - "markerEnd": "logo", - "source": "KeywordExtract:BeigeTipsStand", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Wikipedia:WittyRiceLearn", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "reactflow__edge-RewriteQuestion:OrangeBottlesSwimb-Retrieval:SilentCamelsStickc", - "markerEnd": "logo", - "source": "RewriteQuestion:OrangeBottlesSwim", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Retrieval:SilentCamelsStick", - "targetHandle": "c", - "type": "buttonEdge" - }, - { - "id": "xy-edge__Generate:ItchyRiversDrumc-Answer:PoorMapsCoverc", - "markerEnd": "logo", - "source": "Generate:ItchyRiversDrum", - "sourceHandle": "c", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Answer:PoorMapsCover", - "targetHandle": "c", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Retrieval:SilentCamelsStickb-Generate:ItchyRiversDrumb", - "markerEnd": "logo", - "source": "Retrieval:SilentCamelsStick", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:ItchyRiversDrum", - "targetHandle": "b", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Wikipedia:WittyRiceLearnb-Generate:ItchyRiversDrumb", - "markerEnd": "logo", - "source": "Wikipedia:WittyRiceLearn", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:ItchyRiversDrum", - "targetHandle": "b", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__Baidu:OliveAreasCallb-Generate:ItchyRiversDrumb", - "markerEnd": "logo", - "source": "Baidu:OliveAreasCall", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:ItchyRiversDrum", - "targetHandle": "b", - "type": "buttonEdge", - "zIndex": 1001 - }, - { - "id": "xy-edge__DuckDuckGo:SoftButtonsRefuseb-Generate:ItchyRiversDrumb", - "markerEnd": "logo", - "source": "DuckDuckGo:SoftButtonsRefuse", - "sourceHandle": "b", - "style": { - "stroke": "rgb(202 197 245)", - "strokeWidth": 2 - }, - "target": "Generate:ItchyRiversDrum", - "targetHandle": "b", - "type": "buttonEdge", - "zIndex": 1001 - } - ], - "nodes": [ - { - "data": { - "label": "Begin", - "name": "opening" - }, - "dragging": false, - "height": 44, - "id": "begin", - "measured": { - "height": 44, - "width": 100 - }, - "position": { - "x": -1469.1118402678153, - "y": -138.55389910599428 - }, - "positionAbsolute": { - "x": -1379.627471412851, - "y": -135.63593055637585 - }, - "selected": false, - "sourcePosition": "left", - "targetPosition": "right", - "type": "beginNode" - }, - { - "data": { - "form": {}, - "label": "Answer", - "name": "interface" - }, - "dragging": false, - "height": 44, - "id": "Answer:PoorMapsCover", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": -1172.8677760724227, - "y": -134.7856818291531 - }, - "positionAbsolute": { - "x": -1172.8677760724227, - "y": -134.7856818291531 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "logicNode", - "width": 200 - }, - { - "data": { - "form": { - "language": "en", - "query": [ - { - "component_id": "KeywordExtract:BeigeTipsStand", - "type": "reference" - } - ], - "top_n": 2 - }, - "label": "Wikipedia", - "name": "Wikipedia" - }, - "dragging": false, - "height": 44, - "id": "Wikipedia:WittyRiceLearn", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": -406.9217458441634, - "y": -54.01023495053805 - }, - "positionAbsolute": { - "x": -406.9217458441634, - "y": -54.01023495053805 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "ragNode", - "width": 200 - }, - { - "data": { - "form": { - "query": [ - { - "component_id": "KeywordExtract:BeigeTipsStand", - "type": "reference" - } - ], - "top_n": 2 - }, - "label": "Baidu", - "name": "Baidu" - }, - "dragging": false, - "height": 44, - "id": "Baidu:OliveAreasCall", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": -334.8102520664264, - "y": -142.4206828864257 - }, - "positionAbsolute": { - "x": -334.8102520664264, - "y": -142.4206828864257 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "ragNode", - "width": 200 - }, - { - "data": { - "form": { - "channel": "text", - "query": [ - { - "component_id": "KeywordExtract:BeigeTipsStand", - "type": "reference" - } - ], - "top_n": 2 - }, - "label": "DuckDuckGo", - "name": "DuckDuckGo" - }, - "dragging": false, - "height": 44, - "id": "DuckDuckGo:SoftButtonsRefuse", - "measured": { - "height": 44, - "width": 200 - }, - "position": { - "x": -241.42135935727495, - "y": -227.69429585279033 - }, - "positionAbsolute": { - "x": -241.42135935727495, - "y": -227.69429585279033 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "ragNode", - "width": 200 - }, - { - "data": { - "form": { - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "loop": 1, - "maxTokensEnabled": true, - "max_tokens": 256, - "message_history_window_size": 6, - "parameter": "Precise", - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "RewriteQuestion", - "name": "Refine Question" - }, - "dragging": false, - "height": 86, - "id": "RewriteQuestion:OrangeBottlesSwim", - "measured": { - "height": 86, - "width": 200 - }, - "position": { - "x": -926.3250837910092, - "y": -156.41315582042822 - }, - "positionAbsolute": { - "x": -926.3250837910092, - "y": -156.41315582042822 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "rewriteNode", - "width": 200 - }, - { - "data": { - "form": { - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": true, - "max_tokens": 256, - "parameter": "Precise", - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_n": 2, - "top_p": 0.3 - }, - "label": "KeywordExtract", - "name": "Get keywords" - }, - "dragging": false, - "height": 86, - "id": "KeywordExtract:BeigeTipsStand", - "measured": { - "height": 86, - "width": 200 - }, - "position": { - "x": -643.95039088561, - "y": -160.37167955274685 - }, - "positionAbsolute": { - "x": -643.95039088561, - "y": -160.37167955274685 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "keywordNode", - "width": 200 - }, - { - "data": { - "form": { - "empty_response": "The answer you want was not found in the knowledge base!", - "kb_ids": [], - "keywords_similarity_weight": 0.3, - "similarity_threshold": 0.2, - "top_n": 8 - }, - "label": "Retrieval", - "name": "Search KB" - }, - "dragging": false, - "height": 46, - "id": "Retrieval:SilentCamelsStick", - "measured": { - "height": 46, - "width": 200 - }, - "position": { - "x": -641.3113750640641, - "y": -4.669746081545384 - }, - "positionAbsolute": { - "x": -641.3113750640641, - "y": -4.669746081545384 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "retrievalNode", - "width": 200 - }, - { - "data": { - "form": { - "text": "The large model answers the user's query based on the content retrieved from different search engines and knowledge bases, returning an answer to the user's question." - }, - "label": "Note", - "name": "N: LLM" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 144, - "id": "Note:CuteSchoolsWear", - "measured": { - "height": 144, - "width": 443 - }, - "position": { - "x": -628.5256394373041, - "y": 412.60472782016245 - }, - "positionAbsolute": { - "x": -628.5256394373041, - "y": 412.60472782016245 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 144, - "width": 443 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 443 - }, - { - "data": { - "form": { - "text": "Complete questions by conversation history.\nUser: What's RAGFlow?\nAssistant: RAGFlow is xxx.\nUser: How to deloy it?\n\nRefine it: How to deploy RAGFlow?" - }, - "label": "Note", - "name": "N: Refine question" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 209, - "id": "Note:CuteRavensBehave", - "measured": { - "height": 209, - "width": 266 - }, - "position": { - "x": -921.2271023677847, - "y": -381.3182401779728 - }, - "positionAbsolute": { - "x": -921.2271023677847, - "y": -381.3182401779728 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 209, - "width": 266 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 266 - }, - { - "data": { - "form": { - "text": "Based on the user's question, searches the knowledge base and returns the retrieved content." - }, - "label": "Note", - "name": "N: Search KB" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 128, - "id": "Note:RudeRulesLeave", - "measured": { - "height": 128, - "width": 269 - }, - "position": { - "x": -917.896611693436, - "y": -3.570404025438563 - }, - "positionAbsolute": { - "x": -917.896611693436, - "y": -3.570404025438563 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "noteNode", - "width": 269 - }, - { - "data": { - "form": { - "text": "Based on the keywords, searches on Wikipedia and returns the found content." - }, - "label": "Note", - "name": "N: Wikipedia" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 128, - "id": "Note:DryActorsTry", - "measured": { - "height": 128, - "width": 281 - }, - "position": { - "x": 49.68127281474659, - "y": -16.899164744846445 - }, - "positionAbsolute": { - "x": 49.68127281474659, - "y": -16.899164744846445 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 128, - "width": 281 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 281 - }, - { - "data": { - "form": { - "text": "Based on the keywords, searches on Baidu and returns the found content." - }, - "label": "Note", - "name": "N :Baidu" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 128, - "id": "Note:HonestShirtsNail", - "measured": { - "height": 128, - "width": 269 - }, - "position": { - "x": 43.964372149616565, - "y": -151.26282396084338 - }, - "positionAbsolute": { - "x": 43.964372149616565, - "y": -151.26282396084338 - }, - "selected": false, - "sourcePosition": "right", - "targetPosition": "left", - "type": "noteNode", - "width": 269 - }, - { - "data": { - "form": { - "text": "Based on the keywords, searches on DuckDuckGo and returns the found content." - }, - "label": "Note", - "name": "N: DuckduckGo" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 145, - "id": "Note:OddBreadsFix", - "measured": { - "height": 145, - "width": 201 - }, - "position": { - "x": -237.54626926201882, - "y": -381.56637252684175 - }, - "positionAbsolute": { - "x": -237.54626926201882, - "y": -381.56637252684175 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 145, - "width": 201 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 201 - }, - { - "data": { - "form": { - "text": "The large model generates keywords based on the user's question for better retrieval." - }, - "label": "Note", - "name": "N: Get keywords" - }, - "dragHandle": ".note-drag-handle", - "dragging": false, - "height": 162, - "id": "Note:GentleWorldsDesign", - "measured": { - "height": 162, - "width": 201 - }, - "position": { - "x": -646.3211655055846, - "y": -334.10598887579624 - }, - "positionAbsolute": { - "x": -646.3211655055846, - "y": -334.10598887579624 - }, - "resizing": false, - "selected": false, - "sourcePosition": "right", - "style": { - "height": 162, - "width": 201 - }, - "targetPosition": "left", - "type": "noteNode", - "width": 201 - }, - { - "data": { - "form": { - "cite": true, - "frequencyPenaltyEnabled": true, - "frequency_penalty": 0.7, - "llm_id": "deepseek-chat@DeepSeek", - "maxTokensEnabled": false, - "max_tokens": 256, - "message_history_window_size": 12, - "parameter": "Precise", - "parameters": [], - "presencePenaltyEnabled": true, - "presence_penalty": 0.4, - "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{Retrieval:SilentCamelsStick}\n\n\n## Wikipedia content\n{Wikipedia:WittyRiceLearn}\n\n\n## Duckduckgo content\n{DuckDuckGo:SoftButtonsRefuse}\n\n\n## Baidu content\n{Baidu:OliveAreasCall}\n\n", - "temperature": 0.1, - "temperatureEnabled": true, - "topPEnabled": true, - "top_p": 0.3 - }, - "label": "Generate", - "name": "LLM" - }, - "dragging": false, - "id": "Generate:ItchyRiversDrum", - "measured": { - "height": 108, - "width": 200 - }, - "position": { - "x": -636.2454246475879, - "y": 282.00479262604443 - }, - "selected": true, - "sourcePosition": "right", - "targetPosition": "left", - "type": "generateNode" - } - ] - }, - "history": [], - "messages": [], - "path": [], - "reference": [] - }, - "avatar": "data:image/png;base64,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" -} diff --git a/agent/test/client.py b/agent/test/client.py deleted file mode 100644 index 1ab4db3..0000000 --- a/agent/test/client.py +++ /dev/null @@ -1,49 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import argparse -import os -from functools import partial -from agent.canvas import Canvas -from agent.settings import DEBUG - -if __name__ == '__main__': - parser = argparse.ArgumentParser() - dsl_default_path = os.path.join( - os.path.dirname(os.path.realpath(__file__)), - "dsl_examples", - "retrieval_and_generate.json", - ) - parser.add_argument('-s', '--dsl', default=dsl_default_path, help="input dsl", action='store', required=True) - parser.add_argument('-t', '--tenant_id', default=False, help="Tenant ID", action='store', required=True) - parser.add_argument('-m', '--stream', default=False, help="Stream output", action='store_true', required=False) - args = parser.parse_args() - - canvas = Canvas(open(args.dsl, "r").read(), args.tenant_id) - while True: - ans = canvas.run(stream=args.stream) - print("==================== Bot =====================\n> ", end='') - if args.stream and isinstance(ans, partial): - cont = "" - for an in ans(): - print(an["content"][len(cont):], end='', flush=True) - cont = an["content"] - else: - print(ans["content"]) - - if DEBUG: - print(canvas.path) - question = input("\n==================== User =====================\n> ") - canvas.add_user_input(question) diff --git a/agent/test/dsl_examples/baidu_generate_and_switch.json b/agent/test/dsl_examples/baidu_generate_and_switch.json deleted file mode 100644 index 90069cf..0000000 --- a/agent/test/dsl_examples/baidu_generate_and_switch.json +++ /dev/null @@ -1,129 +0,0 @@ -{ - "components": { - "begin": { - "obj":{ - "component_name": "Begin", - "params": { - "prologue": "Hi there!" - } - }, - "downstream": ["answer:0"], - "upstream": [] - }, - "answer:0": { - "obj": { - "component_name": "Answer", - "params": {} - }, - "downstream": ["baidu:0"], - "upstream": ["begin", "message:0","message:1"] - }, - "baidu:0": { - "obj": { - "component_name": "Baidu", - "params": {} - }, - "downstream": ["generate:0"], - "upstream": ["answer:0"] - }, - "generate:0": { - "obj": { - "component_name": "Generate", - "params": { - "llm_id": "deepseek-chat", - "prompt": "You are an intelligent assistant. Please answer the user's question based on what Baidu searched. First, please output the user's question and the content searched by Baidu, and then answer yes, no, or i don't know.Here is the user's question:{user_input}The above is the user's question.Here is what Baidu searched for:{baidu}The above is the content searched by Baidu.", - "temperature": 0.2 - }, - "parameters": [ - { - "component_id": "answer:0", - "id": "69415446-49bf-4d4b-8ec9-ac86066f7709", - "key": "user_input" - }, - { - "component_id": "baidu:0", - "id": "83363c2a-00a8-402f-a45c-ddc4097d7d8b", - "key": "baidu" - } - ] - }, - "downstream": ["switch:0"], - "upstream": ["baidu:0"] - }, - "switch:0": { - "obj": { - "component_name": "Switch", - "params": { - "conditions": [ - { - "logical_operator" : "or", - "items" : [ - {"cpn_id": "generate:0", "operator": "contains", "value": "yes"}, - {"cpn_id": "generate:0", "operator": "contains", "value": "yeah"} - ], - "to": "message:0" - }, - { - "logical_operator" : "and", - "items" : [ - {"cpn_id": "generate:0", "operator": "contains", "value": "no"}, - {"cpn_id": "generate:0", "operator": "not contains", "value": "yes"}, - {"cpn_id": "generate:0", "operator": "not contains", "value": "know"} - ], - "to": "message:1" - }, - { - "logical_operator" : "", - "items" : [ - {"cpn_id": "generate:0", "operator": "contains", "value": "know"} - ], - "to": "message:2" - } - ], - "end_cpn_id": "answer:0" - - } - }, - "downstream": ["message:0","message:1"], - "upstream": ["generate:0"] - }, - "message:0": { - "obj": { - "component_name": "Message", - "params": { - "messages": ["YES YES YES YES YES YES YES YES YES YES YES YES"] - } - }, - - "upstream": ["switch:0"], - "downstream": ["answer:0"] - }, - "message:1": { - "obj": { - "component_name": "Message", - "params": { - "messages": ["NO NO NO NO NO NO NO NO NO NO NO NO NO NO"] - } - }, - - "upstream": ["switch:0"], - "downstream": ["answer:0"] - }, - "message:2": { - "obj": { - "component_name": "Message", - "params": { - "messages": ["I DON'T KNOW---------------------------"] - } - }, - - "upstream": ["switch:0"], - "downstream": ["answer:0"] - } - }, - "history": [], - "messages": [], - "reference": {}, - "path": [], - "answer": [] -} diff --git a/agent/test/dsl_examples/categorize.json b/agent/test/dsl_examples/categorize.json deleted file mode 100644 index 600c9bc..0000000 --- a/agent/test/dsl_examples/categorize.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "components": { - "begin": { - "obj":{ - "component_name": "Begin", - "params": { - "prologue": "Hi there!" - } - }, - "downstream": ["answer:0"], - "upstream": [] - }, - "answer:0": { - "obj": { - "component_name": "Answer", - "params": {} - }, - "downstream": ["categorize:0"], - "upstream": ["begin"] - }, - "categorize:0": { - "obj": { - "component_name": "Categorize", - "params": { - "llm_id": "deepseek-chat", - "category_description": { - "product_related": { - "description": "The question is about the product usage, appearance and how it works.", - "examples": "Why it always beaming?\nHow to install it onto the wall?\nIt leaks, what to do?", - "to": "message:0" - }, - "others": { - "description": "The question is not about the product usage, appearance and how it works.", - "examples": "How are you doing?\nWhat is your name?\nAre you a robot?\nWhat's the weather?\nWill it rain?", - "to": "message:1" - } - } - } - }, - "downstream": ["message:0","message:1"], - "upstream": ["answer:0"] - }, - "message:0": { - "obj": { - "component_name": "Message", - "params": { - "messages": [ - "Message 0!!!!!!!" - ] - } - }, - "downstream": ["answer:0"], - "upstream": ["categorize:0"] - }, - "message:1": { - "obj": { - "component_name": "Message", - "params": { - "messages": [ - "Message 1!!!!!!!" - ] - } - }, - "downstream": ["answer:0"], - "upstream": ["categorize:0"] - } - }, - "history": [], - "messages": [], - "path": [], - "reference": [], - "answer": [] -} diff --git a/agent/test/dsl_examples/concentrator_message.json b/agent/test/dsl_examples/concentrator_message.json deleted file mode 100644 index ee875ae..0000000 --- a/agent/test/dsl_examples/concentrator_message.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "components": { - "begin": { - "obj":{ - "component_name": "Begin", - "params": { - "prologue": "Hi there!" - } - }, - "downstream": ["answer:0"], - "upstream": [] - }, - "answer:0": { - "obj": { - "component_name": "Answer", - "params": {} - }, - "downstream": ["categorize:0"], - "upstream": ["begin"] - }, - "categorize:0": { - "obj": { - "component_name": "Categorize", - "params": { - "llm_id": "deepseek-chat", - "category_description": { - "product_related": { - "description": "The question is about the product usage, appearance and how it works.", - "examples": "Why it always beaming?\nHow to install it onto the wall?\nIt leaks, what to do?", - "to": "concentrator:0" - }, - "others": { - "description": "The question is not about the product usage, appearance and how it works.", - "examples": "How are you doing?\nWhat is your name?\nAre you a robot?\nWhat's the weather?\nWill it rain?", - "to": "concentrator:1" - } - } - } - }, - "downstream": ["concentrator:0","concentrator:1"], - "upstream": ["answer:0"] - }, - "concentrator:0": { - "obj": { - "component_name": "Concentrator", - "params": {} - }, - "downstream": ["message:0"], - "upstream": ["categorize:0"] - }, - "concentrator:1": { - "obj": { - "component_name": "Concentrator", - "params": {} - }, - "downstream": ["message:1_0","message:1_1","message:1_2"], - "upstream": ["categorize:0"] - }, - "message:0": { - "obj": { - "component_name": "Message", - "params": { - "messages": [ - "Message 0_0!!!!!!!" - ] - } - }, - "downstream": ["answer:0"], - "upstream": ["concentrator:0"] - }, - "message:1_0": { - "obj": { - "component_name": "Message", - "params": { - "messages": [ - "Message 1_0!!!!!!!" - ] - } - }, - "downstream": ["answer:0"], - "upstream": ["concentrator:1"] - }, - "message:1_1": { - "obj": { - "component_name": "Message", - "params": { - "messages": [ - "Message 1_1!!!!!!!" - ] - } - }, - "downstream": ["answer:0"], - "upstream": ["concentrator:1"] - }, - "message:1_2": { - "obj": { - "component_name": "Message", - "params": { - "messages": [ - "Message 1_2!!!!!!!" - ] - } - }, - "downstream": ["answer:0"], - "upstream": ["concentrator:1"] - } - }, - "history": [], - "messages": [], - "path": [], - "reference": [], - "answer": [] -} \ No newline at end of file diff --git a/agent/test/dsl_examples/customer_service.json b/agent/test/dsl_examples/customer_service.json deleted file mode 100644 index 8421e3a..0000000 --- a/agent/test/dsl_examples/customer_service.json +++ /dev/null @@ -1,157 +0,0 @@ -{ - "components": { - "begin": { - "obj":{ - "component_name": "Begin", - "params": { - "prologue": "Hi! How can I help you?" - } - }, - "downstream": ["answer:0"], - "upstream": [] - }, - "answer:0": { - "obj": { - "component_name": "Answer", - "params": {} - }, - "downstream": ["categorize:0"], - "upstream": ["begin", "generate:0", "generate:casual", "generate:answer", "generate:complain", "generate:ask_contact", "message:get_contact"] - }, - "categorize:0": { - "obj": { - "component_name": "Categorize", - "params": { - "llm_id": "deepseek-chat", - "category_description": { - "product_related": { - "description": "The question is about the product usage, appearance and how it works.", - "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", - "to": "retrieval:0" - }, - "casual": { - "description": "The question is not about the product usage, appearance and how it works. Just casual chat.", - "examples": "How are you doing?\nWhat is your name?\nAre you a robot?\nWhat's the weather?\nWill it rain?", - "to": "generate:casual" - }, - "complain": { - "description": "Complain even curse about the product or service you provide. But the comment is not specific enough.", - "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.", - "to": "generate:complain" - }, - "answer": { - "description": "This answer provide a specific contact information, like e-mail, phone number, wechat number, line number, twitter, discord, etc,.", - "examples": "My phone number is 203921\nkevinhu.hk@gmail.com\nThis is my discord number: johndowson_29384", - "to": "message:get_contact" - } - }, - "message_history_window_size": 8 - } - }, - "downstream": ["retrieval:0", "generate:casual", "generate:complain", "message:get_contact"], - "upstream": ["answer:0"] - }, - "generate:casual": { - "obj": { - "component_name": "Generate", - "params": { - "llm_id": "deepseek-chat", - "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.", - "temperature": 0.9, - "message_history_window_size": 12, - "cite": false - } - }, - "downstream": ["answer:0"], - "upstream": ["categorize:0"] - }, - "generate:complain": { - "obj": { - "component_name": "Generate", - "params": { - "llm_id": "deepseek-chat", - "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.", - "temperature": 0.9, - "message_history_window_size": 12, - "cite": false - } - }, - "downstream": ["answer:0"], - "upstream": ["categorize:0"] - }, - "retrieval:0": { - "obj": { - "component_name": "Retrieval", - "params": { - "similarity_threshold": 0.2, - "keywords_similarity_weight": 0.3, - "top_n": 6, - "top_k": 1024, - "rerank_id": "BAAI/bge-reranker-v2-m3", - "kb_ids": ["869a236818b811ef91dffa163e197198"] - } - }, - "downstream": ["relevant:0"], - "upstream": ["categorize:0"] - }, - "relevant:0": { - "obj": { - "component_name": "Relevant", - "params": { - "llm_id": "deepseek-chat", - "temperature": 0.02, - "yes": "generate:answer", - "no": "generate:ask_contact" - } - }, - "downstream": ["generate:answer", "generate:ask_contact"], - "upstream": ["retrieval:0"] - }, - "generate:answer": { - "obj": { - "component_name": "Generate", - "params": { - "llm_id": "deepseek-chat", - "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.", - "temperature": 0.02 - } - }, - "downstream": ["answer:0"], - "upstream": ["relevant:0"] - }, - "generate:ask_contact": { - "obj": { - "component_name": "Generate", - "params": { - "llm_id": "deepseek-chat", - "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.", - "temperature": 0.9, - "message_history_window_size": 12, - "cite": false - } - }, - "downstream": ["answer:0"], - "upstream": ["relevant:0"] - }, - "message:get_contact": { - "obj":{ - "component_name": "Message", - "params": { - "messages": [ - "Okay, I've already write this down. What else I can do for you?", - "Get it. What else I can do for you?", - "Thanks for your trust! Our expert will contact ASAP. So, anything else I can do for you?", - "Thanks! So, anything else I can do for you?" - ] - } - }, - "downstream": ["answer:0"], - "upstream": ["categorize:0"] - } - }, - "history": [], - "messages": [], - "path": [], - "reference": [], - "answer": [] -} \ No newline at end of file diff --git a/agent/test/dsl_examples/exesql.json b/agent/test/dsl_examples/exesql.json deleted file mode 100644 index 7e26587..0000000 --- a/agent/test/dsl_examples/exesql.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "components": { - "begin": { - "obj":{ - "component_name": "Begin", - "params": { - "prologue": "Hi there!" - } - }, - "downstream": ["answer:0"], - "upstream": [] - }, - "answer:0": { - "obj": { - "component_name": "Answer", - "params": {} - }, - "downstream": ["exesql:0"], - "upstream": ["begin", "exesql:0"] - }, - "exesql:0": { - "obj": { - "component_name": "ExeSQL", - "params": { - "database": "rag_flow", - "username": "root", - "host": "mysql", - "port": 3306, - "password": "infini_rag_flow", - "top_n": 3 - } - }, - "downstream": ["answer:0"], - "upstream": ["answer:0"] - } - }, - "history": [], - "messages": [], - "reference": {}, - "path": [], - "answer": [] -} - diff --git a/agent/test/dsl_examples/headhunter_zh.json b/agent/test/dsl_examples/headhunter_zh.json deleted file mode 100644 index 6e4abc8..0000000 --- a/agent/test/dsl_examples/headhunter_zh.json +++ /dev/null @@ -1,210 +0,0 @@ -{ - "components": { - "begin": { - "obj": { - "component_name": "Begin", - "params": { - "prologue": "您好!我是AGI方向的猎头,了解到您是这方面的大佬,然后冒昧的就联系到您。这边有个机会想和您分享,RAGFlow正在招聘您这个岗位的资深的工程师不知道您那边是不是感兴趣?" - } - }, - "downstream": ["answer:0"], - "upstream": [] - }, - "answer:0": { - "obj": { - "component_name": "Answer", - "params": {} - }, - "downstream": ["categorize:0"], - "upstream": ["begin", "message:reject"] - }, - "categorize:0": { - "obj": { - "component_name": "Categorize", - "params": { - "llm_id": "deepseek-chat", - "category_description": { - "about_job": { - "description": "该问题关于职位本身或公司的信息。", - "examples": "什么岗位?\n汇报对象是谁?\n公司多少人?\n公司有啥产品?\n具体工作内容是啥?\n地点哪里?\n双休吗?", - "to": "retrieval:0" - }, - "casual": { - "description": "该问题不关于职位本身或公司的信息,属于闲聊。", - "examples": "你好\n好久不见\n你男的女的?\n你是猴子派来的救兵吗?\n上午开会了?\n你叫啥?\n最近市场如何?生意好做吗?", - "to": "generate:casual" - }, - "interested": { - "description": "该回答表示他对于该职位感兴趣。", - "examples": "嗯\n说吧\n说说看\n还好吧\n是的\n哦\nyes\n具体说说", - "to": "message:introduction" - }, - "answer": { - "description": "该回答表示他对于该职位不感兴趣,或感觉受到骚扰。", - "examples": "不需要\n不感兴趣\n暂时不看\n不要\nno\n我已经不干这个了\n我不是这个方向的", - "to": "message:reject" - } - } - } - }, - "downstream": [ - "message:introduction", - "generate:casual", - "message:reject", - "retrieval:0" - ], - "upstream": ["answer:0"] - }, - "message:introduction": { - "obj": { - "component_name": "Message", - "params": { - "messages": [ - "我简单介绍以下:\nRAGFlow 是一款基于深度文档理解构建的开源 RAG(Retrieval-Augmented Generation)引擎。RAGFlow 可以为各种规模的企业及个人提供一套精简的 RAG 工作流程,结合大语言模型(LLM)针对用户各类不同的复杂格式数据提供可靠的问答以及有理有据的引用。https://github.com/infiniflow/ragflow\n您那边还有什么要了解的?" - ] - } - }, - "downstream": ["answer:1"], - "upstream": ["categorize:0"] - }, - "answer:1": { - "obj": { - "component_name": "Answer", - "params": {} - }, - "downstream": ["categorize:1"], - "upstream": [ - "message:introduction", - "generate:aboutJob", - "generate:casual", - "generate:get_wechat", - "generate:nowechat" - ] - }, - "categorize:1": { - "obj": { - "component_name": "Categorize", - "params": { - "llm_id": "deepseek-chat", - "category_description": { - "about_job": { - "description": "该问题关于职位本身或公司的信息。", - "examples": "什么岗位?\n汇报对象是谁?\n公司多少人?\n公司有啥产品?\n具体工作内容是啥?\n地点哪里?\n双休吗?", - "to": "retrieval:0" - }, - "casual": { - "description": "该问题不关于职位本身或公司的信息,属于闲聊。", - "examples": "你好\n好久不见\n你男的女的?\n你是猴子派来的救兵吗?\n上午开会了?\n你叫啥?\n最近市场如何?生意好做吗?", - "to": "generate:casual" - }, - "wechat": { - "description": "该回答表示他愿意加微信,或者已经报了微信号。", - "examples": "嗯\n可以\n是的\n哦\nyes\n15002333453\nwindblow_2231", - "to": "generate:get_wechat" - }, - "giveup": { - "description": "该回答表示他不愿意加微信。", - "examples": "不需要\n不感兴趣\n暂时不看\n不要\nno\n不方便\n不知道还要加我微信", - "to": "generate:nowechat" - } - }, - "message_history_window_size": 8 - } - }, - "downstream": [ - "retrieval:0", - "generate:casual", - "generate:get_wechat", - "generate:nowechat" - ], - "upstream": ["answer:1"] - }, - "generate:casual": { - "obj": { - "component_name": "Generate", - "params": { - "llm_id": "deepseek-chat", - "prompt": "你是AGI方向的猎头,现在候选人的聊了和职位无关的话题,请耐心的回应候选人,并将话题往该AGI的职位上带,最好能要到候选人微信号以便后面保持联系。", - "temperature": 0.9, - "message_history_window_size": 12, - "cite": false - } - }, - "downstream": ["answer:1"], - "upstream": ["categorize:0", "categorize:1"] - }, - "retrieval:0": { - "obj": { - "component_name": "Retrieval", - "params": { - "similarity_threshold": 0.2, - "keywords_similarity_weight": 0.3, - "top_n": 6, - "top_k": 1024, - "rerank_id": "BAAI/bge-reranker-v2-m3", - "kb_ids": ["869a236818b811ef91dffa163e197198"] - } - }, - "downstream": ["generate:aboutJob"], - "upstream": ["categorize:0", "categorize:1"] - }, - "generate:aboutJob": { - "obj": { - "component_name": "Generate", - "params": { - "llm_id": "deepseek-chat", - "prompt": "你是AGI方向的猎头,候选人问了有关职位或公司的问题,你根据以下职位信息回答。如果职位信息中不包含候选人的问题就回答不清楚、不知道、有待确认等。回答完后引导候选人加微信号,如:\n - 方便加一下微信吗,我把JD发您看看?\n - 微信号多少,我把详细职位JD发您?\n 职位信息如下:\n {input}\n 职位信息如上。", - "temperature": 0.02 - } - }, - "downstream": ["answer:1"], - "upstream": ["retrieval:0"] - }, - "generate:get_wechat": { - "obj": { - "component_name": "Generate", - "params": { - "llm_id": "deepseek-chat", - "prompt": "你是AGI方向的猎头,候选人表示不反感加微信,如果对方已经报了微信号,表示感谢和信任并表示马上会加上;如果没有,则问对方微信号多少。你的微信号是weixin_kevin,E-mail是kkk@ragflow.com。说话不要重复。不要总是您好。", - "temperature": 0.1, - "message_history_window_size": 12, - "cite": false - } - }, - "downstream": ["answer:1"], - "upstream": ["categorize:1"] - }, - "generate:nowechat": { - "obj": { - "component_name": "Generate", - "params": { - "llm_id": "deepseek-chat", - "prompt": "你是AGI方向的猎头,当你提出加微信时对方表示拒绝。你需要耐心礼貌的回应候选人,表示对于保护隐私信息给予理解,也可以询问他对该职位的看法和顾虑。并在恰当的时机再次询问微信联系方式。也可以鼓励候选人主动与你取得联系。你的微信号是weixin_kevin,E-mail是kkk@ragflow.com。说话不要重复。不要总是您好。", - "temperature": 0.1, - "message_history_window_size": 12, - "cite": false - } - }, - "downstream": ["answer:1"], - "upstream": ["categorize:1"] - }, - "message:reject": { - "obj": { - "component_name": "Message", - "params": { - "messages": [ - "好的,祝您生活愉快,工作顺利。", - "哦,好的,感谢您宝贵的时间!" - ] - } - }, - "downstream": ["answer:0"], - "upstream": ["categorize:0"] - } - }, - "history": [], - "messages": [], - "path": [], - "reference": [], - "answer": [] -} diff --git a/agent/test/dsl_examples/intergreper.json b/agent/test/dsl_examples/intergreper.json deleted file mode 100644 index e528b27..0000000 --- a/agent/test/dsl_examples/intergreper.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "components": { - "begin": { - "obj":{ - "component_name": "Begin", - "params": { - "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" - } - }, - "downstream": ["answer:0"], - "upstream": [] - }, - "answer:0": { - "obj": { - "component_name": "Answer", - "params": {} - }, - "downstream": ["generate:0"], - "upstream": ["begin", "generate:0"] - }, - "generate:0": { - "obj": { - "component_name": "Generate", - "params": { - "llm_id": "deepseek-chat", - "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", - "temperature": 0.5 - } - }, - "downstream": ["answer:0"], - "upstream": ["answer:0"] - } - }, - "history": [], - "messages": [], - "reference": {}, - "path": [], - "answer": [] -} \ No newline at end of file diff --git a/agent/test/dsl_examples/interpreter.json b/agent/test/dsl_examples/interpreter.json deleted file mode 100644 index e528b27..0000000 --- a/agent/test/dsl_examples/interpreter.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "components": { - "begin": { - "obj":{ - "component_name": "Begin", - "params": { - "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" - } - }, - "downstream": ["answer:0"], - "upstream": [] - }, - "answer:0": { - "obj": { - "component_name": "Answer", - "params": {} - }, - "downstream": ["generate:0"], - "upstream": ["begin", "generate:0"] - }, - "generate:0": { - "obj": { - "component_name": "Generate", - "params": { - "llm_id": "deepseek-chat", - "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", - "temperature": 0.5 - } - }, - "downstream": ["answer:0"], - "upstream": ["answer:0"] - } - }, - "history": [], - "messages": [], - "reference": {}, - "path": [], - "answer": [] -} \ No newline at end of file diff --git a/agent/test/dsl_examples/keyword_wikipedia_and_generate.json b/agent/test/dsl_examples/keyword_wikipedia_and_generate.json deleted file mode 100644 index fa1d621..0000000 --- a/agent/test/dsl_examples/keyword_wikipedia_and_generate.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "components": { - "begin": { - "obj":{ - "component_name": "Begin", - "params": { - "prologue": "Hi there!" - } - }, - "downstream": ["answer:0"], - "upstream": [] - }, - "answer:0": { - "obj": { - "component_name": "Answer", - "params": {} - }, - "downstream": ["keyword:0"], - "upstream": ["begin"] - }, - "keyword:0": { - "obj": { - "component_name": "KeywordExtract", - "params": { - "llm_id": "deepseek-chat", - "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: ", - "temperature": 0.2, - "top_n": 1 - } - }, - "downstream": ["wikipedia:0"], - "upstream": ["answer:0"] - }, - "wikipedia:0": { - "obj":{ - "component_name": "Wikipedia", - "params": { - "top_n": 10 - } - }, - "downstream": ["generate:0"], - "upstream": ["keyword:0"] - }, - "generate:1": { - "obj": { - "component_name": "Generate", - "params": { - "llm_id": "deepseek-chat", - "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.", - "temperature": 0.2 - } - }, - "downstream": ["answer:0"], - "upstream": ["wikipedia:0"] - } - }, - "history": [], - "path": [], - "messages": [], - "reference": {}, - "answer": [] -} diff --git a/agent/test/dsl_examples/retrieval_and_generate.json b/agent/test/dsl_examples/retrieval_and_generate.json deleted file mode 100644 index fbbf076..0000000 --- a/agent/test/dsl_examples/retrieval_and_generate.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "components": { - "begin": { - "obj":{ - "component_name": "Begin", - "params": { - "prologue": "Hi there!" - } - }, - "downstream": ["answer:0"], - "upstream": [] - }, - "answer:0": { - "obj": { - "component_name": "Answer", - "params": {} - }, - "downstream": ["retrieval:0"], - "upstream": ["begin", "generate:0"] - }, - "retrieval:0": { - "obj": { - "component_name": "Retrieval", - "params": { - "similarity_threshold": 0.2, - "keywords_similarity_weight": 0.3, - "top_n": 6, - "top_k": 1024, - "rerank_id": "BAAI/bge-reranker-v2-m3", - "kb_ids": ["869a236818b811ef91dffa163e197198"] - } - }, - "downstream": ["generate:0"], - "upstream": ["answer:0"] - }, - "generate:0": { - "obj": { - "component_name": "Generate", - "params": { - "llm_id": "deepseek-chat", - "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.", - "temperature": 0.2 - } - }, - "downstream": ["answer:0"], - "upstream": ["retrieval:0"] - } - }, - "history": [], - "messages": [], - "reference": {}, - "path": [], - "answer": [] -} \ No newline at end of file diff --git a/agent/test/dsl_examples/retrieval_categorize_and_generate.json b/agent/test/dsl_examples/retrieval_categorize_and_generate.json deleted file mode 100644 index 4276b33..0000000 --- a/agent/test/dsl_examples/retrieval_categorize_and_generate.json +++ /dev/null @@ -1,88 +0,0 @@ -{ - "components": { - "begin": { - "obj":{ - "component_name": "Begin", - "params": { - "prologue": "Hi there!" - } - }, - "downstream": ["answer:0"], - "upstream": [] - }, - "answer:0": { - "obj": { - "component_name": "Answer", - "params": {} - }, - "downstream": ["categorize:0"], - "upstream": ["begin", "generate:0", "switch:0"] - }, - "categorize:0": { - "obj": { - "component_name": "Categorize", - "params": { - "llm_id": "deepseek-chat", - "category_description": { - "product_related": { - "description": "The question is about the product usage, appearance and how it works.", - "examples": "Why it always beaming?\nHow to install it onto the wall?\nIt leaks, what to do?", - "to": "retrieval:0" - }, - "others": { - "description": "The question is not about the product usage, appearance and how it works.", - "examples": "How are you doing?\nWhat is your name?\nAre you a robot?\nWhat's the weather?\nWill it rain?", - "to": "message:0" - } - } - } - }, - "downstream": ["retrieval:0", "message:0"], - "upstream": ["answer:0"] - }, - "message:0": { - "obj":{ - "component_name": "Message", - "params": { - "messages": [ - "Sorry, I don't know. I'm an AI bot." - ] - } - }, - "downstream": ["answer:0"], - "upstream": ["categorize:0"] - }, - "retrieval:0": { - "obj": { - "component_name": "Retrieval", - "params": { - "similarity_threshold": 0.2, - "keywords_similarity_weight": 0.3, - "top_n": 6, - "top_k": 1024, - "rerank_id": "BAAI/bge-reranker-v2-m3", - "kb_ids": ["869a236818b811ef91dffa163e197198"] - } - }, - "downstream": ["generate:0"], - "upstream": ["switch:0"] - }, - "generate:0": { - "obj": { - "component_name": "Generate", - "params": { - "llm_id": "deepseek-chat", - "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.", - "temperature": 0.2 - } - }, - "downstream": ["answer:0"], - "upstream": ["retrieval:0"] - } - }, - "history": [], - "messages": [], - "reference": {}, - "path": [], - "answer": [] -} \ No newline at end of file diff --git a/agent/test/dsl_examples/retrieval_relevant_and_generate.json b/agent/test/dsl_examples/retrieval_relevant_and_generate.json deleted file mode 100644 index 8eeb323..0000000 --- a/agent/test/dsl_examples/retrieval_relevant_and_generate.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "components": { - "begin": { - "obj":{ - "component_name": "Begin", - "params": { - "prologue": "Hi there!" - } - }, - "downstream": ["answer:0"], - "upstream": [] - }, - "answer:0": { - "obj": { - "component_name": "Answer", - "params": {} - }, - "downstream": ["retrieval:0"], - "upstream": ["begin", "generate:0", "switch:0"] - }, - "retrieval:0": { - "obj": { - "component_name": "Retrieval", - "params": { - "similarity_threshold": 0.2, - "keywords_similarity_weight": 0.3, - "top_n": 6, - "top_k": 1024, - "rerank_id": "BAAI/bge-reranker-v2-m3", - "kb_ids": ["869a236818b811ef91dffa163e197198"], - "empty_response": "Sorry, knowledge base has noting related information." - } - }, - "downstream": ["relevant:0"], - "upstream": ["answer:0"] - }, - "relevant:0": { - "obj": { - "component_name": "Relevant", - "params": { - "llm_id": "deepseek-chat", - "temperature": 0.02, - "yes": "generate:0", - "no": "message:0" - } - }, - "downstream": ["message:0", "generate:0"], - "upstream": ["retrieval:0"] - }, - "generate:0": { - "obj": { - "component_name": "Generate", - "params": { - "llm_id": "deepseek-chat", - "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.", - "temperature": 0.2 - } - }, - "downstream": ["answer:0"], - "upstream": ["relevant:0"] - }, - "message:0": { - "obj":{ - "component_name": "Message", - "params": { - "messages": [ - "Sorry, I don't know. Please leave your contact, our experts will contact you later. What's your e-mail/phone/wechat?", - "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?", - "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?" - ] - } - }, - "downstream": ["answer:0"], - "upstream": ["relevant:0"] - } - }, - "history": [], - "path": [], - "messages": [], - "reference": {}, - "answer": [] -} \ No newline at end of file diff --git a/agent/test/dsl_examples/retrieval_relevant_keyword_baidu_and_generate.json b/agent/test/dsl_examples/retrieval_relevant_keyword_baidu_and_generate.json deleted file mode 100644 index a34b58a..0000000 --- a/agent/test/dsl_examples/retrieval_relevant_keyword_baidu_and_generate.json +++ /dev/null @@ -1,103 +0,0 @@ -{ - "components": { - "begin": { - "obj":{ - "component_name": "Begin", - "params": { - "prologue": "Hi there!" - } - }, - "downstream": ["answer:0"], - "upstream": [] - }, - "answer:0": { - "obj": { - "component_name": "Answer", - "params": {} - }, - "downstream": ["retrieval:0"], - "upstream": ["begin"] - }, - "retrieval:0": { - "obj": { - "component_name": "Retrieval", - "params": { - "similarity_threshold": 0.2, - "keywords_similarity_weight": 0.3, - "top_n": 6, - "top_k": 1024, - "rerank_id": "BAAI/bge-reranker-v2-m3", - "kb_ids": ["21ca4e6a2c8911ef8b1e0242ac120006"], - "empty_response": "Sorry, knowledge base has noting related information." - } - }, - "downstream": ["relevant:0"], - "upstream": ["answer:0"] - }, - "relevant:0": { - "obj": { - "component_name": "Relevant", - "params": { - "llm_id": "deepseek-chat", - "temperature": 0.02, - "yes": "generate:0", - "no": "keyword:0" - } - }, - "downstream": ["keyword:0", "generate:0"], - "upstream": ["retrieval:0"] - }, - "generate:0": { - "obj": { - "component_name": "Generate", - "params": { - "llm_id": "deepseek-chat", - "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.", - "temperature": 0.2 - } - }, - "downstream": ["answer:0"], - "upstream": ["relevant:0"] - }, - "keyword:0": { - "obj": { - "component_name": "KeywordExtract", - "params": { - "llm_id": "deepseek-chat", - "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: ", - "temperature": 0.2, - "top_n": 1 - } - }, - "downstream": ["baidu:0"], - "upstream": ["relevant:0"] - }, - "baidu:0": { - "obj":{ - "component_name": "Baidu", - "params": { - "top_n": 10 - } - }, - "downstream": ["generate:1"], - "upstream": ["keyword:0"] - }, - "generate:1": { - "obj": { - "component_name": "Generate", - "params": { - "llm_id": "deepseek-chat", - "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.", - "temperature": 0.2 - } - }, - "downstream": ["answer:0"], - "upstream": ["baidu:0"] - } - }, - "history": [], - "path": [], - "messages": [], - "reference": {}, - "answer": [] -} diff --git a/agent/test/dsl_examples/retrieval_relevant_rewrite_and_generate.json b/agent/test/dsl_examples/retrieval_relevant_rewrite_and_generate.json deleted file mode 100644 index fb290a2..0000000 --- a/agent/test/dsl_examples/retrieval_relevant_rewrite_and_generate.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "components": { - "begin": { - "obj":{ - "component_name": "Begin", - "params": { - "prologue": "Hi there!" - } - }, - "downstream": ["answer:0"], - "upstream": [] - }, - "answer:0": { - "obj": { - "component_name": "Answer", - "params": {} - }, - "downstream": ["retrieval:0"], - "upstream": ["begin", "generate:0", "switch:0"] - }, - "retrieval:0": { - "obj": { - "component_name": "Retrieval", - "params": { - "similarity_threshold": 0.2, - "keywords_similarity_weight": 0.3, - "top_n": 6, - "top_k": 1024, - "rerank_id": "BAAI/bge-reranker-v2-m3", - "kb_ids": ["869a236818b811ef91dffa163e197198"], - "empty_response": "Sorry, knowledge base has noting related information." - } - }, - "downstream": ["relevant:0"], - "upstream": ["answer:0", "rewrite:0"] - }, - "relevant:0": { - "obj": { - "component_name": "Relevant", - "params": { - "llm_id": "deepseek-chat", - "temperature": 0.02, - "yes": "generate:0", - "no": "rewrite:0" - } - }, - "downstream": ["generate:0", "rewrite:0"], - "upstream": ["retrieval:0"] - }, - "generate:0": { - "obj": { - "component_name": "Generate", - "params": { - "llm_id": "deepseek-chat", - "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.", - "temperature": 0.02 - } - }, - "downstream": ["answer:0"], - "upstream": ["relevant:0"] - }, - "rewrite:0": { - "obj":{ - "component_name": "RewriteQuestion", - "params": { - "llm_id": "deepseek-chat", - "temperature": 0.8 - } - }, - "downstream": ["retrieval:0"], - "upstream": ["relevant:0"] - } - }, - "history": [], - "messages": [], - "path": [], - "reference": [], - "answer": [] -} \ No newline at end of file diff --git a/api/apps/api_app.py b/api/apps/api_app.py index 533cffa..dd1533c 100644 --- a/api/apps/api_app.py +++ b/api/apps/api_app.py @@ -34,20 +34,15 @@ from api.db.services.task_service import queue_tasks, TaskService from api.db.services.user_service import UserTenantService from api import settings from api.utils import get_uuid, current_timestamp, datetime_format -from api.utils.api_utils import server_error_response, get_data_error_result, get_json_result, validate_request, \ - generate_confirmation_token +from api.utils.api_utils import server_error_response, get_data_error_result, get_json_result, validate_request, generate_confirmation_token from api.utils.file_utils import filename_type, thumbnail from rag.app.tag import label_question from rag.prompts import keyword_extraction from rag.utils.storage_factory import STORAGE_IMPL -from api.db.services.canvas_service import UserCanvasService -from agent.canvas import Canvas -from functools import partial - -@manager.route('/new_token', methods=['POST']) # noqa: F821 +@manager.route("/new_token", methods=["POST"]) # noqa: F821 @login_required def new_token(): req = request.json @@ -57,12 +52,14 @@ def new_token(): return get_data_error_result(message="Tenant not found!") tenant_id = tenants[0].tenant_id - obj = {"tenant_id": tenant_id, "token": generate_confirmation_token(tenant_id), - "create_time": current_timestamp(), - "create_date": datetime_format(datetime.now()), - "update_time": None, - "update_date": None - } + obj = { + "tenant_id": tenant_id, + "token": generate_confirmation_token(tenant_id), + "create_time": current_timestamp(), + "create_date": datetime_format(datetime.now()), + "update_time": None, + "update_date": None, + } if req.get("canvas_id"): obj["dialog_id"] = req["canvas_id"] obj["source"] = "agent" @@ -77,7 +74,7 @@ def new_token(): return server_error_response(e) -@manager.route('/token_list', methods=['GET']) # noqa: F821 +@manager.route("/token_list", methods=["GET"]) # noqa: F821 @login_required def token_list(): try: @@ -92,21 +89,20 @@ def token_list(): return server_error_response(e) -@manager.route('/rm', methods=['POST']) # noqa: F821 +@manager.route("/rm", methods=["POST"]) # noqa: F821 @validate_request("tokens", "tenant_id") @login_required def rm(): req = request.json try: for token in req["tokens"]: - APITokenService.filter_delete( - [APIToken.tenant_id == req["tenant_id"], APIToken.token == token]) + APITokenService.filter_delete([APIToken.tenant_id == req["tenant_id"], APIToken.token == token]) return get_json_result(data=True) except Exception as e: return server_error_response(e) -@manager.route('/stats', methods=['GET']) # noqa: F821 +@manager.route("/stats", methods=["GET"]) # noqa: F821 @login_required def stats(): try: @@ -115,76 +111,47 @@ def stats(): return get_data_error_result(message="Tenant not found!") objs = API4ConversationService.stats( tenants[0].tenant_id, - request.args.get( - "from_date", - (datetime.now() - - timedelta( - days=7)).strftime("%Y-%m-%d 00:00:00")), - request.args.get( - "to_date", - datetime.now().strftime("%Y-%m-%d %H:%M:%S")), - "agent" if "canvas_id" in request.args else None) + request.args.get("from_date", (datetime.now() - timedelta(days=7)).strftime("%Y-%m-%d 00:00:00")), + request.args.get("to_date", datetime.now().strftime("%Y-%m-%d %H:%M:%S")), + "agent" if "canvas_id" in request.args else None, + ) res = { "pv": [(o["dt"], o["pv"]) for o in objs], "uv": [(o["dt"], o["uv"]) for o in objs], "speed": [(o["dt"], float(o["tokens"]) / (float(o["duration"] + 0.1))) for o in objs], - "tokens": [(o["dt"], float(o["tokens"]) / 1000.) for o in objs], + "tokens": [(o["dt"], float(o["tokens"]) / 1000.0) for o in objs], "round": [(o["dt"], o["round"]) for o in objs], - "thumb_up": [(o["dt"], o["thumb_up"]) for o in objs] + "thumb_up": [(o["dt"], o["thumb_up"]) for o in objs], } return get_json_result(data=res) except Exception as e: return server_error_response(e) -@manager.route('/new_conversation', methods=['GET']) # noqa: F821 +@manager.route("/new_conversation", methods=["GET"]) # noqa: F821 def set_conversation(): - token = request.headers.get('Authorization').split()[1] + token = request.headers.get("Authorization").split()[1] objs = APIToken.query(token=token) if not objs: - return get_json_result( - data=False, message='Authentication error: API key is invalid!"', code=settings.RetCode.AUTHENTICATION_ERROR) + return get_json_result(data=False, message='Authentication error: API key is invalid!"', code=settings.RetCode.AUTHENTICATION_ERROR) try: - if objs[0].source == "agent": - e, cvs = UserCanvasService.get_by_id(objs[0].dialog_id) - if not e: - return server_error_response("canvas not found.") - if not isinstance(cvs.dsl, str): - cvs.dsl = json.dumps(cvs.dsl, ensure_ascii=False) - canvas = Canvas(cvs.dsl, objs[0].tenant_id) - conv = { - "id": get_uuid(), - "dialog_id": cvs.id, - "user_id": request.args.get("user_id", ""), - "message": [{"role": "assistant", "content": canvas.get_prologue()}], - "source": "agent" - } - API4ConversationService.save(**conv) - return get_json_result(data=conv) - else: - e, dia = DialogService.get_by_id(objs[0].dialog_id) - if not e: - return get_data_error_result(message="Dialog not found") - conv = { - "id": get_uuid(), - "dialog_id": dia.id, - "user_id": request.args.get("user_id", ""), - "message": [{"role": "assistant", "content": dia.prompt_config["prologue"]}] - } - API4ConversationService.save(**conv) - return get_json_result(data=conv) + e, dia = DialogService.get_by_id(objs[0].dialog_id) + if not e: + return get_data_error_result(message="Dialog not found") + conv = {"id": get_uuid(), "dialog_id": dia.id, "user_id": request.args.get("user_id", ""), "message": [{"role": "assistant", "content": dia.prompt_config["prologue"]}]} + API4ConversationService.save(**conv) + return get_json_result(data=conv) except Exception as e: return server_error_response(e) -@manager.route('/completion', methods=['POST']) # noqa: F821 +@manager.route("/completion", methods=["POST"]) # noqa: F821 @validate_request("conversation_id", "messages") def completion(): - token = request.headers.get('Authorization').split()[1] + token = request.headers.get("Authorization").split()[1] objs = APIToken.query(token=token) if not objs: - return get_json_result( - data=False, message='Authentication error: API key is invalid!"', code=settings.RetCode.AUTHENTICATION_ERROR) + return get_json_result(data=False, message='Authentication error: API key is invalid!"', code=settings.RetCode.AUTHENTICATION_ERROR) req = request.json e, conv = API4ConversationService.get_by_id(req["conversation_id"]) if not e: @@ -213,87 +180,15 @@ def completion(): ans["id"] = message_id def rename_field(ans): - reference = ans['reference'] + reference = ans["reference"] if not isinstance(reference, dict): return - for chunk_i in reference.get('chunks', []): - if 'docnm_kwd' in chunk_i: - chunk_i['doc_name'] = chunk_i['docnm_kwd'] - chunk_i.pop('docnm_kwd') + for chunk_i in reference.get("chunks", []): + if "docnm_kwd" in chunk_i: + chunk_i["doc_name"] = chunk_i["docnm_kwd"] + chunk_i.pop("docnm_kwd") try: - if conv.source == "agent": - stream = req.get("stream", True) - conv.message.append(msg[-1]) - e, cvs = UserCanvasService.get_by_id(conv.dialog_id) - if not e: - return server_error_response("canvas not found.") - del req["conversation_id"] - del req["messages"] - - if not isinstance(cvs.dsl, str): - cvs.dsl = json.dumps(cvs.dsl, ensure_ascii=False) - - if not conv.reference: - conv.reference = [] - conv.message.append({"role": "assistant", "content": "", "id": message_id}) - conv.reference.append({"chunks": [], "doc_aggs": []}) - - final_ans = {"reference": [], "content": ""} - canvas = Canvas(cvs.dsl, objs[0].tenant_id) - - canvas.messages.append(msg[-1]) - canvas.add_user_input(msg[-1]["content"]) - answer = canvas.run(stream=stream) - - assert answer is not None, "Nothing. Is it over?" - - if stream: - assert isinstance(answer, partial), "Nothing. Is it over?" - - def sse(): - nonlocal answer, cvs, conv - try: - for ans in answer(): - for k in ans.keys(): - final_ans[k] = ans[k] - ans = {"answer": ans["content"], "reference": ans.get("reference", [])} - fillin_conv(ans) - rename_field(ans) - yield "data:" + json.dumps({"code": 0, "message": "", "data": ans}, - ensure_ascii=False) + "\n\n" - - canvas.messages.append({"role": "assistant", "content": final_ans["content"], "id": message_id}) - canvas.history.append(("assistant", final_ans["content"])) - if final_ans.get("reference"): - canvas.reference.append(final_ans["reference"]) - cvs.dsl = json.loads(str(canvas)) - API4ConversationService.append_message(conv.id, conv.to_dict()) - except Exception as e: - yield "data:" + json.dumps({"code": 500, "message": str(e), - "data": {"answer": "**ERROR**: " + str(e), "reference": []}}, - ensure_ascii=False) + "\n\n" - yield "data:" + json.dumps({"code": 0, "message": "", "data": True}, ensure_ascii=False) + "\n\n" - - resp = Response(sse(), mimetype="text/event-stream") - resp.headers.add_header("Cache-control", "no-cache") - resp.headers.add_header("Connection", "keep-alive") - resp.headers.add_header("X-Accel-Buffering", "no") - resp.headers.add_header("Content-Type", "text/event-stream; charset=utf-8") - return resp - - final_ans["content"] = "\n".join(answer["content"]) if "content" in answer else "" - canvas.messages.append({"role": "assistant", "content": final_ans["content"], "id": message_id}) - if final_ans.get("reference"): - canvas.reference.append(final_ans["reference"]) - cvs.dsl = json.loads(str(canvas)) - - result = {"answer": final_ans["content"], "reference": final_ans.get("reference", [])} - fillin_conv(result) - API4ConversationService.append_message(conv.id, conv.to_dict()) - rename_field(result) - return get_json_result(data=result) - # ******************For dialog****************** conv.message.append(msg[-1]) e, dia = DialogService.get_by_id(conv.dialog_id) @@ -313,13 +208,10 @@ def completion(): for ans in chat(dia, msg, True, **req): fillin_conv(ans) rename_field(ans) - yield "data:" + json.dumps({"code": 0, "message": "", "data": ans}, - ensure_ascii=False) + "\n\n" + yield "data:" + json.dumps({"code": 0, "message": "", "data": ans}, ensure_ascii=False) + "\n\n" API4ConversationService.append_message(conv.id, conv.to_dict()) except Exception as e: - yield "data:" + json.dumps({"code": 500, "message": str(e), - "data": {"answer": "**ERROR**: " + str(e), "reference": []}}, - ensure_ascii=False) + "\n\n" + yield "data:" + json.dumps({"code": 500, "message": str(e), "data": {"answer": "**ERROR**: " + str(e), "reference": []}}, ensure_ascii=False) + "\n\n" yield "data:" + json.dumps({"code": 0, "message": "", "data": True}, ensure_ascii=False) + "\n\n" if req.get("stream", True): @@ -343,14 +235,13 @@ def completion(): return server_error_response(e) -@manager.route('/conversation/', methods=['GET']) # noqa: F821 +@manager.route("/conversation/", methods=["GET"]) # noqa: F821 # @login_required def get(conversation_id): - token = request.headers.get('Authorization').split()[1] + token = request.headers.get("Authorization").split()[1] objs = APIToken.query(token=token) if not objs: - return get_json_result( - data=False, message='Authentication error: API key is invalid!"', code=settings.RetCode.AUTHENTICATION_ERROR) + return get_json_result(data=False, message='Authentication error: API key is invalid!"', code=settings.RetCode.AUTHENTICATION_ERROR) try: e, conv = API4ConversationService.get_by_id(conversation_id) @@ -358,30 +249,28 @@ def get(conversation_id): return get_data_error_result(message="Conversation not found!") conv = conv.to_dict() - if token != APIToken.query(dialog_id=conv['dialog_id'])[0].token: - return get_json_result(data=False, message='Authentication error: API key is invalid for this conversation_id!"', - code=settings.RetCode.AUTHENTICATION_ERROR) + if token != APIToken.query(dialog_id=conv["dialog_id"])[0].token: + return get_json_result(data=False, message='Authentication error: API key is invalid for this conversation_id!"', code=settings.RetCode.AUTHENTICATION_ERROR) - for referenct_i in conv['reference']: + for referenct_i in conv["reference"]: if referenct_i is None or len(referenct_i) == 0: continue - for chunk_i in referenct_i['chunks']: - if 'docnm_kwd' in chunk_i.keys(): - chunk_i['doc_name'] = chunk_i['docnm_kwd'] - chunk_i.pop('docnm_kwd') + for chunk_i in referenct_i["chunks"]: + if "docnm_kwd" in chunk_i.keys(): + chunk_i["doc_name"] = chunk_i["docnm_kwd"] + chunk_i.pop("docnm_kwd") return get_json_result(data=conv) except Exception as e: return server_error_response(e) -@manager.route('/document/upload', methods=['POST']) # noqa: F821 +@manager.route("/document/upload", methods=["POST"]) # noqa: F821 @validate_request("kb_name") def upload(): - token = request.headers.get('Authorization').split()[1] + token = request.headers.get("Authorization").split()[1] objs = APIToken.query(token=token) if not objs: - return get_json_result( - data=False, message='Authentication error: API key is invalid!"', code=settings.RetCode.AUTHENTICATION_ERROR) + return get_json_result(data=False, message='Authentication error: API key is invalid!"', code=settings.RetCode.AUTHENTICATION_ERROR) kb_name = request.form.get("kb_name").strip() tenant_id = objs[0].tenant_id @@ -389,20 +278,17 @@ def upload(): try: e, kb = KnowledgebaseService.get_by_name(kb_name, tenant_id) if not e: - return get_data_error_result( - message="Can't find this knowledgebase!") + return get_data_error_result(message="Can't find this knowledgebase!") kb_id = kb.id except Exception as e: return server_error_response(e) - if 'file' not in request.files: - return get_json_result( - data=False, message='No file part!', code=settings.RetCode.ARGUMENT_ERROR) + if "file" not in request.files: + return get_json_result(data=False, message="No file part!", code=settings.RetCode.ARGUMENT_ERROR) - file = request.files['file'] - if file.filename == '': - return get_json_result( - data=False, message='No file selected!', code=settings.RetCode.ARGUMENT_ERROR) + file = request.files["file"] + if file.filename == "": + return get_json_result(data=False, message="No file selected!", code=settings.RetCode.ARGUMENT_ERROR) root_folder = FileService.get_root_folder(tenant_id) pf_id = root_folder["id"] @@ -411,23 +297,18 @@ def upload(): kb_folder = FileService.new_a_file_from_kb(kb.tenant_id, kb.name, kb_root_folder["id"]) try: - if DocumentService.get_doc_count(kb.tenant_id) >= int(os.environ.get('MAX_FILE_NUM_PER_USER', 8192)): - return get_data_error_result( - message="Exceed the maximum file number of a free user!") + if DocumentService.get_doc_count(kb.tenant_id) >= int(os.environ.get("MAX_FILE_NUM_PER_USER", 8192)): + return get_data_error_result(message="Exceed the maximum file number of a free user!") - filename = duplicate_name( - DocumentService.query, - name=file.filename, - kb_id=kb_id) + filename = duplicate_name(DocumentService.query, name=file.filename, kb_id=kb_id) filetype = filename_type(filename) if not filetype: - return get_data_error_result( - message="This type of file has not been supported yet!") + return get_data_error_result(message="This type of file has not been supported yet!") location = filename while STORAGE_IMPL.obj_exist(kb_id, location): location += "_" - blob = request.files['file'].read() + blob = request.files["file"].read() STORAGE_IMPL.put(kb_id, location, blob) doc = { "id": get_uuid(), @@ -439,7 +320,7 @@ def upload(): "name": filename, "location": location, "size": len(blob), - "thumbnail": thumbnail(filename, blob) + "thumbnail": thumbnail(filename, blob), } form_data = request.form @@ -486,62 +367,50 @@ def upload(): return get_json_result(data=doc_result.to_json()) -@manager.route('/document/upload_and_parse', methods=['POST']) # noqa: F821 +@manager.route("/document/upload_and_parse", methods=["POST"]) # noqa: F821 @validate_request("conversation_id") def upload_parse(): - token = request.headers.get('Authorization').split()[1] + token = request.headers.get("Authorization").split()[1] objs = APIToken.query(token=token) if not objs: - return get_json_result( - data=False, message='Authentication error: API key is invalid!"', code=settings.RetCode.AUTHENTICATION_ERROR) + return get_json_result(data=False, message='Authentication error: API key is invalid!"', code=settings.RetCode.AUTHENTICATION_ERROR) - if 'file' not in request.files: - return get_json_result( - data=False, message='No file part!', code=settings.RetCode.ARGUMENT_ERROR) + if "file" not in request.files: + return get_json_result(data=False, message="No file part!", code=settings.RetCode.ARGUMENT_ERROR) - file_objs = request.files.getlist('file') + file_objs = request.files.getlist("file") for file_obj in file_objs: - if file_obj.filename == '': - return get_json_result( - data=False, message='No file selected!', code=settings.RetCode.ARGUMENT_ERROR) + if file_obj.filename == "": + return get_json_result(data=False, message="No file selected!", code=settings.RetCode.ARGUMENT_ERROR) doc_ids = doc_upload_and_parse(request.form.get("conversation_id"), file_objs, objs[0].tenant_id) return get_json_result(data=doc_ids) -@manager.route('/list_chunks', methods=['POST']) # noqa: F821 +@manager.route("/list_chunks", methods=["POST"]) # noqa: F821 # @login_required def list_chunks(): - token = request.headers.get('Authorization').split()[1] + token = request.headers.get("Authorization").split()[1] objs = APIToken.query(token=token) if not objs: - return get_json_result( - data=False, message='Authentication error: API key is invalid!"', code=settings.RetCode.AUTHENTICATION_ERROR) + return get_json_result(data=False, message='Authentication error: API key is invalid!"', code=settings.RetCode.AUTHENTICATION_ERROR) req = request.json try: if "doc_name" in req.keys(): - tenant_id = DocumentService.get_tenant_id_by_name(req['doc_name']) - doc_id = DocumentService.get_doc_id_by_doc_name(req['doc_name']) + tenant_id = DocumentService.get_tenant_id_by_name(req["doc_name"]) + doc_id = DocumentService.get_doc_id_by_doc_name(req["doc_name"]) elif "doc_id" in req.keys(): - tenant_id = DocumentService.get_tenant_id(req['doc_id']) - doc_id = req['doc_id'] + tenant_id = DocumentService.get_tenant_id(req["doc_id"]) + doc_id = req["doc_id"] else: - return get_json_result( - data=False, message="Can't find doc_name or doc_id" - ) + return get_json_result(data=False, message="Can't find doc_name or doc_id") kb_ids = KnowledgebaseService.get_kb_ids(tenant_id) res = settings.retrievaler.chunk_list(doc_id, tenant_id, kb_ids) - res = [ - { - "content": res_item["content_with_weight"], - "doc_name": res_item["docnm_kwd"], - "image_id": res_item["img_id"] - } for res_item in res - ] + res = [{"content": res_item["content_with_weight"], "doc_name": res_item["docnm_kwd"], "image_id": res_item["img_id"]} for res_item in res] except Exception as e: return server_error_response(e) @@ -549,14 +418,13 @@ def list_chunks(): return get_json_result(data=res) -@manager.route('/list_kb_docs', methods=['POST']) # noqa: F821 +@manager.route("/list_kb_docs", methods=["POST"]) # noqa: F821 # @login_required def list_kb_docs(): - token = request.headers.get('Authorization').split()[1] + token = request.headers.get("Authorization").split()[1] objs = APIToken.query(token=token) if not objs: - return get_json_result( - data=False, message='Authentication error: API key is invalid!"', code=settings.RetCode.AUTHENTICATION_ERROR) + return get_json_result(data=False, message='Authentication error: API key is invalid!"', code=settings.RetCode.AUTHENTICATION_ERROR) req = request.json tenant_id = objs[0].tenant_id @@ -565,8 +433,7 @@ def list_kb_docs(): try: e, kb = KnowledgebaseService.get_by_name(kb_name, tenant_id) if not e: - return get_data_error_result( - message="Can't find this knowledgebase!") + return get_data_error_result(message="Can't find this knowledgebase!") kb_id = kb.id except Exception as e: @@ -579,9 +446,8 @@ def list_kb_docs(): keywords = req.get("keywords", "") try: - docs, tol = DocumentService.get_by_kb_id( - kb_id, page_number, items_per_page, orderby, desc, keywords) - docs = [{"doc_id": doc['id'], "doc_name": doc['name']} for doc in docs] + docs, tol = DocumentService.get_by_kb_id(kb_id, page_number, items_per_page, orderby, desc, keywords) + docs = [{"doc_id": doc["id"], "doc_name": doc["name"]} for doc in docs] return get_json_result(data={"total": tol, "docs": docs}) @@ -589,28 +455,26 @@ def list_kb_docs(): return server_error_response(e) -@manager.route('/document/infos', methods=['POST']) # noqa: F821 +@manager.route("/document/infos", methods=["POST"]) # noqa: F821 @validate_request("doc_ids") def docinfos(): - token = request.headers.get('Authorization').split()[1] + token = request.headers.get("Authorization").split()[1] objs = APIToken.query(token=token) if not objs: - return get_json_result( - data=False, message='Authentication error: API key is invalid!"', code=settings.RetCode.AUTHENTICATION_ERROR) + return get_json_result(data=False, message='Authentication error: API key is invalid!"', code=settings.RetCode.AUTHENTICATION_ERROR) req = request.json doc_ids = req["doc_ids"] docs = DocumentService.get_by_ids(doc_ids) return get_json_result(data=list(docs.dicts())) -@manager.route('/document', methods=['DELETE']) # noqa: F821 +@manager.route("/document", methods=["DELETE"]) # noqa: F821 # @login_required def document_rm(): - token = request.headers.get('Authorization').split()[1] + token = request.headers.get("Authorization").split()[1] objs = APIToken.query(token=token) if not objs: - return get_json_result( - data=False, message='Authentication error: API key is invalid!"', code=settings.RetCode.AUTHENTICATION_ERROR) + return get_json_result(data=False, message='Authentication error: API key is invalid!"', code=settings.RetCode.AUTHENTICATION_ERROR) tenant_id = objs[0].tenant_id req = request.json @@ -621,9 +485,7 @@ def document_rm(): doc_ids.append(doc_id) if not doc_ids: - return get_json_result( - data=False, message="Can't find doc_names or doc_ids" - ) + return get_json_result(data=False, message="Can't find doc_names or doc_ids") except Exception as e: return server_error_response(e) @@ -645,8 +507,7 @@ def document_rm(): b, n = File2DocumentService.get_storage_address(doc_id=doc_id) if not DocumentService.remove_document(doc, tenant_id): - return get_data_error_result( - message="Database error (Document removal)!") + return get_data_error_result(message="Database error (Document removal)!") f2d = File2DocumentService.get_by_document_id(doc_id) FileService.filter_delete([File.source_type == FileSource.KNOWLEDGEBASE, File.id == f2d[0].file_id]) @@ -662,17 +523,17 @@ def document_rm(): return get_json_result(data=True) -@manager.route('/completion_aibotk', methods=['POST']) # noqa: F821 +@manager.route("/completion_aibotk", methods=["POST"]) # noqa: F821 @validate_request("Authorization", "conversation_id", "word") def completion_faq(): import base64 + req = request.json token = req["Authorization"] objs = APIToken.query(token=token) if not objs: - return get_json_result( - data=False, message='Authentication error: API key is invalid!"', code=settings.RetCode.AUTHENTICATION_ERROR) + return get_json_result(data=False, message='Authentication error: API key is invalid!"', code=settings.RetCode.AUTHENTICATION_ERROR) e, conv = API4ConversationService.get_by_id(req["conversation_id"]) if not e: @@ -696,65 +557,6 @@ def completion_faq(): ans["id"] = message_id try: - if conv.source == "agent": - conv.message.append(msg[-1]) - e, cvs = UserCanvasService.get_by_id(conv.dialog_id) - if not e: - return server_error_response("canvas not found.") - - if not isinstance(cvs.dsl, str): - cvs.dsl = json.dumps(cvs.dsl, ensure_ascii=False) - - if not conv.reference: - conv.reference = [] - conv.message.append({"role": "assistant", "content": "", "id": message_id}) - conv.reference.append({"chunks": [], "doc_aggs": []}) - - final_ans = {"reference": [], "doc_aggs": []} - canvas = Canvas(cvs.dsl, objs[0].tenant_id) - - canvas.messages.append(msg[-1]) - canvas.add_user_input(msg[-1]["content"]) - answer = canvas.run(stream=False) - - assert answer is not None, "Nothing. Is it over?" - - data_type_picture = { - "type": 3, - "url": "base64 content" - } - data = [ - { - "type": 1, - "content": "" - } - ] - final_ans["content"] = "\n".join(answer["content"]) if "content" in answer else "" - canvas.messages.append({"role": "assistant", "content": final_ans["content"], "id": message_id}) - if final_ans.get("reference"): - canvas.reference.append(final_ans["reference"]) - cvs.dsl = json.loads(str(canvas)) - - ans = {"answer": final_ans["content"], "reference": final_ans.get("reference", [])} - data[0]["content"] += re.sub(r'##\d\$\$', '', ans["answer"]) - fillin_conv(ans) - API4ConversationService.append_message(conv.id, conv.to_dict()) - - chunk_idxs = [int(match[2]) for match in re.findall(r'##\d\$\$', ans["answer"])] - for chunk_idx in chunk_idxs[:1]: - if ans["reference"]["chunks"][chunk_idx]["img_id"]: - try: - bkt, nm = ans["reference"]["chunks"][chunk_idx]["img_id"].split("-") - response = STORAGE_IMPL.get(bkt, nm) - data_type_picture["url"] = base64.b64encode(response).decode('utf-8') - data.append(data_type_picture) - break - except Exception as e: - return server_error_response(e) - - response = {"code": 200, "msg": "success", "data": data} - return response - # ******************For dialog****************** conv.message.append(msg[-1]) e, dia = DialogService.get_by_id(conv.dialog_id) @@ -767,31 +569,23 @@ def completion_faq(): conv.message.append({"role": "assistant", "content": "", "id": message_id}) conv.reference.append({"chunks": [], "doc_aggs": []}) - data_type_picture = { - "type": 3, - "url": "base64 content" - } - data = [ - { - "type": 1, - "content": "" - } - ] + data_type_picture = {"type": 3, "url": "base64 content"} + data = [{"type": 1, "content": ""}] ans = "" for a in chat(dia, msg, stream=False, **req): ans = a break - data[0]["content"] += re.sub(r'##\d\$\$', '', ans["answer"]) + data[0]["content"] += re.sub(r"##\d\$\$", "", ans["answer"]) fillin_conv(ans) API4ConversationService.append_message(conv.id, conv.to_dict()) - chunk_idxs = [int(match[2]) for match in re.findall(r'##\d\$\$', ans["answer"])] + chunk_idxs = [int(match[2]) for match in re.findall(r"##\d\$\$", ans["answer"])] for chunk_idx in chunk_idxs[:1]: if ans["reference"]["chunks"][chunk_idx]["img_id"]: try: bkt, nm = ans["reference"]["chunks"][chunk_idx]["img_id"].split("-") response = STORAGE_IMPL.get(bkt, nm) - data_type_picture["url"] = base64.b64encode(response).decode('utf-8') + data_type_picture["url"] = base64.b64encode(response).decode("utf-8") data.append(data_type_picture) break except Exception as e: @@ -804,14 +598,13 @@ def completion_faq(): return server_error_response(e) -@manager.route('/retrieval', methods=['POST']) # noqa: F821 +@manager.route("/retrieval", methods=["POST"]) # noqa: F821 @validate_request("kb_id", "question") def retrieval(): - token = request.headers.get('Authorization').split()[1] + token = request.headers.get("Authorization").split()[1] objs = APIToken.query(token=token) if not objs: - return get_json_result( - data=False, message='Authentication error: API key is invalid!"', code=settings.RetCode.AUTHENTICATION_ERROR) + return get_json_result(data=False, message='Authentication error: API key is invalid!"', code=settings.RetCode.AUTHENTICATION_ERROR) req = request.json kb_ids = req.get("kb_id", []) @@ -827,28 +620,22 @@ def retrieval(): kbs = KnowledgebaseService.get_by_ids(kb_ids) embd_nms = list(set([kb.embd_id for kb in kbs])) if len(embd_nms) != 1: - return get_json_result( - data=False, message='Knowledge bases use different embedding models or does not exist."', - code=settings.RetCode.AUTHENTICATION_ERROR) + return get_json_result(data=False, message='Knowledge bases use different embedding models or does not exist."', code=settings.RetCode.AUTHENTICATION_ERROR) - embd_mdl = TenantLLMService.model_instance( - kbs[0].tenant_id, LLMType.EMBEDDING.value, llm_name=kbs[0].embd_id) + embd_mdl = TenantLLMService.model_instance(kbs[0].tenant_id, LLMType.EMBEDDING.value, llm_name=kbs[0].embd_id) rerank_mdl = None if req.get("rerank_id"): - rerank_mdl = TenantLLMService.model_instance( - kbs[0].tenant_id, LLMType.RERANK.value, llm_name=req["rerank_id"]) + rerank_mdl = TenantLLMService.model_instance(kbs[0].tenant_id, LLMType.RERANK.value, llm_name=req["rerank_id"]) if req.get("keyword", False): chat_mdl = TenantLLMService.model_instance(kbs[0].tenant_id, LLMType.CHAT) question += keyword_extraction(chat_mdl, question) - ranks = settings.retrievaler.retrieval(question, embd_mdl, kbs[0].tenant_id, kb_ids, page, size, - similarity_threshold, vector_similarity_weight, top, - doc_ids, rerank_mdl=rerank_mdl, - rank_feature=label_question(question, kbs)) + ranks = settings.retrievaler.retrieval( + question, embd_mdl, kbs[0].tenant_id, kb_ids, page, size, similarity_threshold, vector_similarity_weight, top, doc_ids, rerank_mdl=rerank_mdl, rank_feature=label_question(question, kbs) + ) for c in ranks["chunks"]: c.pop("vector", None) return get_json_result(data=ranks) except Exception as e: if str(e).find("not_found") > 0: - return get_json_result(data=False, message='No chunk found! Check the chunk status please!', - code=settings.RetCode.DATA_ERROR) + return get_json_result(data=False, message="No chunk found! Check the chunk status please!", code=settings.RetCode.DATA_ERROR) return server_error_response(e) diff --git a/api/apps/canvas_app.py b/api/apps/canvas_app.py deleted file mode 100644 index 8dcbb25..0000000 --- a/api/apps/canvas_app.py +++ /dev/null @@ -1,286 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import json -import traceback -from flask import request, Response -from flask_login import login_required, current_user -from api.db.services.canvas_service import CanvasTemplateService, UserCanvasService -from api.settings import RetCode -from api.utils import get_uuid -from api.utils.api_utils import get_json_result, server_error_response, validate_request, get_data_error_result -from agent.canvas import Canvas -from peewee import MySQLDatabase, PostgresqlDatabase -from api.db.db_models import APIToken - - -@manager.route('/templates', methods=['GET']) # noqa: F821 -@login_required -def templates(): - return get_json_result(data=[c.to_dict() for c in CanvasTemplateService.get_all()]) - - -@manager.route('/list', methods=['GET']) # noqa: F821 -@login_required -def canvas_list(): - return get_json_result(data=sorted([c.to_dict() for c in \ - UserCanvasService.query(user_id=current_user.id)], key=lambda x: x["update_time"]*-1) - ) - - -@manager.route('/rm', methods=['POST']) # noqa: F821 -@validate_request("canvas_ids") -@login_required -def rm(): - for i in request.json["canvas_ids"]: - if not UserCanvasService.query(user_id=current_user.id,id=i): - return get_json_result( - data=False, message='Only owner of canvas authorized for this operation.', - code=RetCode.OPERATING_ERROR) - UserCanvasService.delete_by_id(i) - return get_json_result(data=True) - - -@manager.route('/set', methods=['POST']) # noqa: F821 -@validate_request("dsl", "title") -@login_required -def save(): - req = request.json - req["user_id"] = current_user.id - if not isinstance(req["dsl"], str): - req["dsl"] = json.dumps(req["dsl"], ensure_ascii=False) - - req["dsl"] = json.loads(req["dsl"]) - if "id" not in req: - if UserCanvasService.query(user_id=current_user.id, title=req["title"].strip()): - return get_data_error_result(message=f"{req['title'].strip()} already exists.") - req["id"] = get_uuid() - if not UserCanvasService.save(**req): - return get_data_error_result(message="Fail to save canvas.") - else: - if not UserCanvasService.query(user_id=current_user.id, id=req["id"]): - return get_json_result( - data=False, message='Only owner of canvas authorized for this operation.', - code=RetCode.OPERATING_ERROR) - UserCanvasService.update_by_id(req["id"], req) - return get_json_result(data=req) - - -@manager.route('/get/', methods=['GET']) # noqa: F821 -@login_required -def get(canvas_id): - e, c = UserCanvasService.get_by_id(canvas_id) - if not e: - return get_data_error_result(message="canvas not found.") - return get_json_result(data=c.to_dict()) - -@manager.route('/getsse/', methods=['GET']) # type: ignore # noqa: F821 -def getsse(canvas_id): - token = request.headers.get('Authorization').split() - if len(token) != 2: - return get_data_error_result(message='Authorization is not valid!"') - token = token[1] - objs = APIToken.query(beta=token) - if not objs: - return get_data_error_result(message='Authentication error: API key is invalid!"') - e, c = UserCanvasService.get_by_id(canvas_id) - if not e: - return get_data_error_result(message="canvas not found.") - return get_json_result(data=c.to_dict()) - - -@manager.route('/completion', methods=['POST']) # noqa: F821 -@validate_request("id") -@login_required -def run(): - req = request.json - stream = req.get("stream", True) - e, cvs = UserCanvasService.get_by_id(req["id"]) - if not e: - return get_data_error_result(message="canvas not found.") - if not UserCanvasService.query(user_id=current_user.id, id=req["id"]): - return get_json_result( - data=False, message='Only owner of canvas authorized for this operation.', - code=RetCode.OPERATING_ERROR) - - if not isinstance(cvs.dsl, str): - cvs.dsl = json.dumps(cvs.dsl, ensure_ascii=False) - - final_ans = {"reference": [], "content": ""} - message_id = req.get("message_id", get_uuid()) - try: - canvas = Canvas(cvs.dsl, current_user.id) - if "message" in req: - canvas.messages.append({"role": "user", "content": req["message"], "id": message_id}) - canvas.add_user_input(req["message"]) - except Exception as e: - return server_error_response(e) - - if stream: - def sse(): - nonlocal answer, cvs - try: - for ans in canvas.run(stream=True): - if ans.get("running_status"): - yield "data:" + json.dumps({"code": 0, "message": "", - "data": {"answer": ans["content"], - "running_status": True}}, - ensure_ascii=False) + "\n\n" - continue - for k in ans.keys(): - final_ans[k] = ans[k] - ans = {"answer": ans["content"], "reference": ans.get("reference", [])} - yield "data:" + json.dumps({"code": 0, "message": "", "data": ans}, ensure_ascii=False) + "\n\n" - - canvas.messages.append({"role": "assistant", "content": final_ans["content"], "id": message_id}) - canvas.history.append(("assistant", final_ans["content"])) - if not canvas.path[-1]: - canvas.path.pop(-1) - if final_ans.get("reference"): - canvas.reference.append(final_ans["reference"]) - cvs.dsl = json.loads(str(canvas)) - UserCanvasService.update_by_id(req["id"], cvs.to_dict()) - except Exception as e: - cvs.dsl = json.loads(str(canvas)) - if not canvas.path[-1]: - canvas.path.pop(-1) - UserCanvasService.update_by_id(req["id"], cvs.to_dict()) - traceback.print_exc() - yield "data:" + json.dumps({"code": 500, "message": str(e), - "data": {"answer": "**ERROR**: " + str(e), "reference": []}}, - ensure_ascii=False) + "\n\n" - yield "data:" + json.dumps({"code": 0, "message": "", "data": True}, ensure_ascii=False) + "\n\n" - - resp = Response(sse(), mimetype="text/event-stream") - resp.headers.add_header("Cache-control", "no-cache") - resp.headers.add_header("Connection", "keep-alive") - resp.headers.add_header("X-Accel-Buffering", "no") - resp.headers.add_header("Content-Type", "text/event-stream; charset=utf-8") - return resp - - for answer in canvas.run(stream=False): - if answer.get("running_status"): - continue - final_ans["content"] = "\n".join(answer["content"]) if "content" in answer else "" - canvas.messages.append({"role": "assistant", "content": final_ans["content"], "id": message_id}) - if final_ans.get("reference"): - canvas.reference.append(final_ans["reference"]) - cvs.dsl = json.loads(str(canvas)) - UserCanvasService.update_by_id(req["id"], cvs.to_dict()) - return get_json_result(data={"answer": final_ans["content"], "reference": final_ans.get("reference", [])}) - - -@manager.route('/reset', methods=['POST']) # noqa: F821 -@validate_request("id") -@login_required -def reset(): - req = request.json - try: - e, user_canvas = UserCanvasService.get_by_id(req["id"]) - if not e: - return get_data_error_result(message="canvas not found.") - if not UserCanvasService.query(user_id=current_user.id, id=req["id"]): - return get_json_result( - data=False, message='Only owner of canvas authorized for this operation.', - code=RetCode.OPERATING_ERROR) - - canvas = Canvas(json.dumps(user_canvas.dsl), current_user.id) - canvas.reset() - req["dsl"] = json.loads(str(canvas)) - UserCanvasService.update_by_id(req["id"], {"dsl": req["dsl"]}) - return get_json_result(data=req["dsl"]) - except Exception as e: - return server_error_response(e) - - -@manager.route('/input_elements', methods=['GET']) # noqa: F821 -@login_required -def input_elements(): - cvs_id = request.args.get("id") - cpn_id = request.args.get("component_id") - try: - e, user_canvas = UserCanvasService.get_by_id(cvs_id) - if not e: - return get_data_error_result(message="canvas not found.") - if not UserCanvasService.query(user_id=current_user.id, id=cvs_id): - return get_json_result( - data=False, message='Only owner of canvas authorized for this operation.', - code=RetCode.OPERATING_ERROR) - - canvas = Canvas(json.dumps(user_canvas.dsl), current_user.id) - return get_json_result(data=canvas.get_component_input_elements(cpn_id)) - except Exception as e: - return server_error_response(e) - - -@manager.route('/debug', methods=['POST']) # noqa: F821 -@validate_request("id", "component_id", "params") -@login_required -def debug(): - req = request.json - for p in req["params"]: - assert p.get("key") - try: - e, user_canvas = UserCanvasService.get_by_id(req["id"]) - if not e: - return get_data_error_result(message="canvas not found.") - if not UserCanvasService.query(user_id=current_user.id, id=req["id"]): - return get_json_result( - data=False, message='Only owner of canvas authorized for this operation.', - code=RetCode.OPERATING_ERROR) - - canvas = Canvas(json.dumps(user_canvas.dsl), current_user.id) - canvas.get_component(req["component_id"])["obj"]._param.debug_inputs = req["params"] - df = canvas.get_component(req["component_id"])["obj"].debug() - return get_json_result(data=df.to_dict(orient="records")) - except Exception as e: - return server_error_response(e) - - -@manager.route('/test_db_connect', methods=['POST']) # noqa: F821 -@validate_request("db_type", "database", "username", "host", "port", "password") -@login_required -def test_db_connect(): - req = request.json - try: - if req["db_type"] in ["mysql", "mariadb"]: - db = MySQLDatabase(req["database"], user=req["username"], host=req["host"], port=req["port"], - password=req["password"]) - elif req["db_type"] == 'postgresql': - db = PostgresqlDatabase(req["database"], user=req["username"], host=req["host"], port=req["port"], - password=req["password"]) - elif req["db_type"] == 'mssql': - import pyodbc - connection_string = ( - f"DRIVER={{ODBC Driver 17 for SQL Server}};" - f"SERVER={req['host']},{req['port']};" - f"DATABASE={req['database']};" - f"UID={req['username']};" - f"PWD={req['password']};" - ) - db = pyodbc.connect(connection_string) - cursor = db.cursor() - cursor.execute("SELECT 1") - cursor.close() - else: - return server_error_response("Unsupported database type.") - if req["db_type"] != 'mssql': - db.connect() - db.close() - - return get_json_result(data="Database Connection Successful!") - except Exception as e: - return server_error_response(e) - diff --git a/api/apps/sdk/agent.py b/api/apps/sdk/agent.py deleted file mode 100644 index a60a645..0000000 --- a/api/apps/sdk/agent.py +++ /dev/null @@ -1,39 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -from api.db.services.canvas_service import UserCanvasService -from api.utils.api_utils import get_error_data_result, token_required -from api.utils.api_utils import get_result -from flask import request - -@manager.route('/agents', methods=['GET']) # noqa: F821 -@token_required -def list_agents(tenant_id): - id = request.args.get("id") - title = request.args.get("title") - if id or title: - canvas = UserCanvasService.query(id=id, title=title, user_id=tenant_id) - if not canvas: - return get_error_data_result("The agent doesn't exist.") - page_number = int(request.args.get("page", 1)) - items_per_page = int(request.args.get("page_size", 30)) - orderby = request.args.get("orderby", "update_time") - if request.args.get("desc") == "False" or request.args.get("desc") == "false": - desc = False - else: - desc = True - canvas = UserCanvasService.get_list(tenant_id,page_number,items_per_page,orderby,desc,id,title) - return get_result(data=canvas) diff --git a/api/apps/sdk/session.py b/api/apps/sdk/session.py index 5fb41a7..30d330e 100644 --- a/api/apps/sdk/session.py +++ b/api/apps/sdk/session.py @@ -20,20 +20,15 @@ import time from api.db import LLMType from api.db.services.conversation_service import ConversationService, iframe_completion from api.db.services.conversation_service import completion as rag_completion -from api.db.services.canvas_service import completion as agent_completion from api.db.services.dialog_service import ask, chat -from agent.canvas import Canvas from api.db import StatusEnum from api.db.db_models import APIToken -from api.db.services.api_service import API4ConversationService -from api.db.services.canvas_service import UserCanvasService from api.db.services.dialog_service import DialogService from api.db.services.knowledgebase_service import KnowledgebaseService from api.utils import get_uuid from api.utils.api_utils import get_error_data_result, validate_request from api.utils.api_utils import get_result, token_required from api.db.services.llm_service import LLMBundle -from api.db.services.file_service import FileService from flask import jsonify, request, Response @@ -66,68 +61,6 @@ def create(tenant_id, chat_id): return get_result(data=conv) -@manager.route("/agents//sessions", methods=["POST"]) # noqa: F821 -@token_required -def create_agent_session(tenant_id, agent_id): - req = request.json - if not request.is_json: - req = request.form - files = request.files - user_id = request.args.get("user_id", "") - - e, cvs = UserCanvasService.get_by_id(agent_id) - if not e: - return get_error_data_result("Agent not found.") - - if not UserCanvasService.query(user_id=tenant_id, id=agent_id): - return get_error_data_result("You cannot access the agent.") - - if not isinstance(cvs.dsl, str): - cvs.dsl = json.dumps(cvs.dsl, ensure_ascii=False) - - canvas = Canvas(cvs.dsl, tenant_id) - canvas.reset() - query = canvas.get_preset_param() - if query: - for ele in query: - if not ele["optional"]: - if ele["type"] == "file": - if files is None or not files.get(ele["key"]): - return get_error_data_result(f"`{ele['key']}` with type `{ele['type']}` is required") - upload_file = files.get(ele["key"]) - file_content = FileService.parse_docs([upload_file], user_id) - file_name = upload_file.filename - ele["value"] = file_name + "\n" + file_content - else: - if req is None or not req.get(ele["key"]): - return get_error_data_result(f"`{ele['key']}` with type `{ele['type']}` is required") - ele["value"] = req[ele["key"]] - else: - if ele["type"] == "file": - if files is not None and files.get(ele["key"]): - upload_file = files.get(ele["key"]) - file_content = FileService.parse_docs([upload_file], user_id) - file_name = upload_file.filename - ele["value"] = file_name + "\n" + file_content - else: - if "value" in ele: - ele.pop("value") - else: - if req is not None and req.get(ele["key"]): - ele["value"] = req[ele["key"]] - else: - if "value" in ele: - ele.pop("value") - else: - for ans in canvas.run(stream=False): - pass - cvs.dsl = json.loads(str(canvas)) - conv = {"id": get_uuid(), "dialog_id": cvs.id, "user_id": user_id, "message": [{"role": "assistant", "content": canvas.get_prologue()}], "source": "agent", "dsl": cvs.dsl} - API4ConversationService.save(**conv) - conv["agent_id"] = conv.pop("dialog_id") - return get_result(data=conv) - - @manager.route("/chats//sessions/", methods=["PUT"]) # noqa: F821 @token_required def update(tenant_id, chat_id, session_id): @@ -317,49 +250,6 @@ def chat_completion_openai_like(tenant_id, chat_id): return jsonify(response) -@manager.route("/agents//completions", methods=["POST"]) # noqa: F821 -@token_required -def agent_completions(tenant_id, agent_id): - req = request.json - cvs = UserCanvasService.query(user_id=tenant_id, id=agent_id) - if not cvs: - return get_error_data_result(f"You don't own the agent {agent_id}") - if req.get("session_id"): - dsl = cvs[0].dsl - if not isinstance(dsl, str): - dsl = json.dumps(dsl) - # canvas = Canvas(dsl, tenant_id) - # if canvas.get_preset_param(): - # req["question"] = "" - conv = API4ConversationService.query(id=req["session_id"], dialog_id=agent_id) - if not conv: - return get_error_data_result(f"You don't own the session {req['session_id']}") - # If an update to UserCanvas is detected, update the API4Conversation.dsl - sync_dsl = req.get("sync_dsl", False) - if sync_dsl is True and cvs[0].update_time > conv[0].update_time: - current_dsl = conv[0].dsl - new_dsl = json.loads(dsl) - state_fields = ["history", "messages", "path", "reference"] - states = {field: current_dsl.get(field, []) for field in state_fields} - current_dsl.update(new_dsl) - current_dsl.update(states) - API4ConversationService.update_by_id(req["session_id"], {"dsl": current_dsl}) - else: - req["question"] = "" - if req.get("stream", True): - resp = Response(agent_completion(tenant_id, agent_id, **req), mimetype="text/event-stream") - resp.headers.add_header("Cache-control", "no-cache") - resp.headers.add_header("Connection", "keep-alive") - resp.headers.add_header("X-Accel-Buffering", "no") - resp.headers.add_header("Content-Type", "text/event-stream; charset=utf-8") - return resp - try: - for answer in agent_completion(tenant_id, agent_id, **req): - return get_result(data=answer) - except Exception as e: - return get_error_data_result(str(e)) - - @manager.route("/chats//sessions", methods=["GET"]) # noqa: F821 @token_required def list_session(tenant_id, chat_id): @@ -413,59 +303,6 @@ def list_session(tenant_id, chat_id): return get_result(data=convs) -@manager.route("/agents//sessions", methods=["GET"]) # noqa: F821 -@token_required -def list_agent_session(tenant_id, agent_id): - if not UserCanvasService.query(user_id=tenant_id, id=agent_id): - return get_error_data_result(message=f"You don't own the agent {agent_id}.") - id = request.args.get("id") - user_id = request.args.get("user_id") - page_number = int(request.args.get("page", 1)) - items_per_page = int(request.args.get("page_size", 30)) - orderby = request.args.get("orderby", "update_time") - if request.args.get("desc") == "False" or request.args.get("desc") == "false": - desc = False - else: - desc = True - # dsl defaults to True in all cases except for False and false - include_dsl = request.args.get("dsl") != "False" and request.args.get("dsl") != "false" - convs = API4ConversationService.get_list(agent_id, tenant_id, page_number, items_per_page, orderby, desc, id, user_id, include_dsl) - if not convs: - return get_result(data=[]) - for conv in convs: - conv["messages"] = conv.pop("message") - infos = conv["messages"] - for info in infos: - if "prompt" in info: - info.pop("prompt") - conv["agent_id"] = conv.pop("dialog_id") - if conv["reference"]: - messages = conv["messages"] - message_num = 0 - chunk_num = 0 - while message_num < len(messages): - if message_num != 0 and messages[message_num]["role"] != "user": - chunk_list = [] - if "chunks" in conv["reference"][chunk_num]: - chunks = conv["reference"][chunk_num]["chunks"] - for chunk in chunks: - new_chunk = { - "id": chunk.get("chunk_id", chunk.get("id")), - "content": chunk.get("content_with_weight", chunk.get("content")), - "document_id": chunk.get("doc_id", chunk.get("document_id")), - "document_name": chunk.get("docnm_kwd", chunk.get("document_name")), - "dataset_id": chunk.get("kb_id", chunk.get("dataset_id")), - "image_id": chunk.get("image_id", chunk.get("img_id")), - "positions": chunk.get("positions", chunk.get("position_int")), - } - chunk_list.append(new_chunk) - chunk_num += 1 - messages[message_num]["reference"] = chunk_list - message_num += 1 - del conv["reference"] - return get_result(data=convs) - - @manager.route("/chats//sessions", methods=["DELETE"]) # noqa: F821 @token_required def delete(tenant_id, chat_id): @@ -492,38 +329,6 @@ def delete(tenant_id, chat_id): return get_result() -@manager.route("/agents//sessions", methods=["DELETE"]) # noqa: F821 -@token_required -def delete_agent_session(tenant_id, agent_id): - req = request.json - cvs = UserCanvasService.query(user_id=tenant_id, id=agent_id) - if not cvs: - return get_error_data_result(f"You don't own the agent {agent_id}") - - convs = API4ConversationService.query(dialog_id=agent_id) - if not convs: - return get_error_data_result(f"Agent {agent_id} has no sessions") - - if not req: - ids = None - else: - ids = req.get("ids") - - if not ids: - conv_list = [] - for conv in convs: - conv_list.append(conv.id) - else: - conv_list = ids - - for session_id in conv_list: - conv = API4ConversationService.query(id=session_id, dialog_id=agent_id) - if not conv: - return get_error_data_result(f"The agent doesn't own the session ${session_id}") - API4ConversationService.delete_by_id(session_id) - return get_result() - - @manager.route("/sessions/ask", methods=["POST"]) # noqa: F821 @token_required def ask_about(tenant_id): @@ -634,30 +439,3 @@ def chatbot_completions(dialog_id): for answer in iframe_completion(dialog_id, **req): return get_result(data=answer) - - -@manager.route("/agentbots//completions", methods=["POST"]) # noqa: F821 -def agent_bot_completions(agent_id): - req = request.json - - token = request.headers.get("Authorization").split() - if len(token) != 2: - return get_error_data_result(message='Authorization is not valid!"') - token = token[1] - objs = APIToken.query(beta=token) - if not objs: - return get_error_data_result(message='Authentication error: API key is invalid!"') - - if "quote" not in req: - req["quote"] = False - - if req.get("stream", True): - resp = Response(agent_completion(objs[0].tenant_id, agent_id, **req), mimetype="text/event-stream") - resp.headers.add_header("Cache-control", "no-cache") - resp.headers.add_header("Connection", "keep-alive") - resp.headers.add_header("X-Accel-Buffering", "no") - resp.headers.add_header("Content-Type", "text/event-stream; charset=utf-8") - return resp - - for answer in agent_completion(objs[0].tenant_id, agent_id, **req): - return get_result(data=answer) diff --git a/api/db/init_data.py b/api/db/init_data.py index 506377c..25e4a81 100644 --- a/api/db/init_data.py +++ b/api/db/init_data.py @@ -17,14 +17,12 @@ import logging import base64 import json import os -import time import uuid from copy import deepcopy from api.db import LLMType, UserTenantRole from api.db.db_models import init_database_tables as init_web_db, LLMFactories, LLM, TenantLLM from api.db.services import UserService -from api.db.services.canvas_service import CanvasTemplateService from api.db.services.document_service import DocumentService from api.db.services.knowledgebase_service import KnowledgebaseService from api.db.services.llm_service import LLMFactoriesService, LLMService, TenantLLMService, LLMBundle @@ -34,8 +32,8 @@ from api.utils.file_utils import get_project_base_directory def encode_to_base64(input_string): - base64_encoded = base64.b64encode(input_string.encode('utf-8')) - return base64_encoded.decode('utf-8') + base64_encoded = base64.b64encode(input_string.encode("utf-8")) + return base64_encoded.decode("utf-8") def init_superuser(): @@ -55,20 +53,14 @@ def init_superuser(): "embd_id": settings.EMBEDDING_MDL, "asr_id": settings.ASR_MDL, "parser_ids": settings.PARSERS, - "img2txt_id": settings.IMAGE2TEXT_MDL - } - usr_tenant = { - "tenant_id": user_info["id"], - "user_id": user_info["id"], - "invited_by": user_info["id"], - "role": UserTenantRole.OWNER + "img2txt_id": settings.IMAGE2TEXT_MDL, } + usr_tenant = {"tenant_id": user_info["id"], "user_id": user_info["id"], "invited_by": user_info["id"], "role": UserTenantRole.OWNER} tenant_llm = [] for llm in LLMService.query(fid=settings.LLM_FACTORY): tenant_llm.append( - {"tenant_id": user_info["id"], "llm_factory": settings.LLM_FACTORY, "llm_name": llm.llm_name, - "model_type": llm.model_type, - "api_key": settings.API_KEY, "api_base": settings.LLM_BASE_URL}) + {"tenant_id": user_info["id"], "llm_factory": settings.LLM_FACTORY, "llm_name": llm.llm_name, "model_type": llm.model_type, "api_key": settings.API_KEY, "api_base": settings.LLM_BASE_URL} + ) if not UserService.save(**user_info): logging.error("can't init admin.") @@ -76,23 +68,16 @@ def init_superuser(): TenantService.insert(**tenant) UserTenantService.insert(**usr_tenant) TenantLLMService.insert_many(tenant_llm) - logging.info( - "Super user initialized. email: admin@ragflow.io, password: admin. Changing the password after login is strongly recommended.") + logging.info("Super user initialized. email: admin@ragflow.io, password: admin. Changing the password after login is strongly recommended.") chat_mdl = LLMBundle(tenant["id"], LLMType.CHAT, tenant["llm_id"]) - msg = chat_mdl.chat(system="", history=[ - {"role": "user", "content": "Hello!"}], gen_conf={}) + msg = chat_mdl.chat(system="", history=[{"role": "user", "content": "Hello!"}], gen_conf={}) if msg.find("ERROR: ") == 0: - logging.error( - "'{}' dosen't work. {}".format( - tenant["llm_id"], - msg)) + logging.error("'{}' dosen't work. {}".format(tenant["llm_id"], msg)) embd_mdl = LLMBundle(tenant["id"], LLMType.EMBEDDING, tenant["embd_id"]) v, c = embd_mdl.encode(["Hello!"]) if c == 0: - logging.error( - "'{}' dosen't work!".format( - tenant["embd_id"])) + logging.error("'{}' dosen't work!".format(tenant["embd_id"])) def init_llm_factory(): @@ -132,8 +117,12 @@ def init_llm_factory(): LLMService.filter_delete([LLMService.model.fid == "QAnything"]) TenantLLMService.filter_update([TenantLLMService.model.llm_factory == "QAnything"], {"llm_factory": "Youdao"}) TenantLLMService.filter_update([TenantLLMService.model.llm_factory == "cohere"], {"llm_factory": "Cohere"}) - TenantService.filter_update([1 == 1], { - "parser_ids": "naive:General,qa:Q&A,resume:Resume,manual:Manual,table:Table,paper:Paper,book:Book,laws:Laws,presentation:Presentation,picture:Picture,one:One,audio:Audio,email:Email,tag:Tag"}) + TenantService.filter_update( + [1 == 1], + { + "parser_ids": "naive:General,qa:Q&A,resume:Resume,manual:Manual,table:Table,paper:Paper,book:Book,laws:Laws,presentation:Presentation,picture:Picture,one:One,audio:Audio,email:Email,tag:Tag" + }, + ) ## insert openai two embedding models to the current openai user. # print("Start to insert 2 OpenAI embedding models...") tenant_ids = set([row["tenant_id"] for row in TenantLLMService.get_openai_models()]) @@ -155,31 +144,10 @@ def init_llm_factory(): KnowledgebaseService.update_by_id(kb_id, {"doc_num": DocumentService.get_kb_doc_count(kb_id)}) - -def add_graph_templates(): - dir = os.path.join(get_project_base_directory(), "agent", "templates") - for fnm in os.listdir(dir): - try: - cnvs = json.load(open(os.path.join(dir, fnm), "r",encoding="utf-8")) - try: - CanvasTemplateService.save(**cnvs) - except Exception: - CanvasTemplateService.update_by_id(cnvs["id"], cnvs) - except Exception: - logging.exception("Add graph templates error: ") - - def init_web_data(): - start_time = time.time() - init_llm_factory() - # if not UserService.get_all().count(): - # init_superuser() - - add_graph_templates() - logging.info("init web data success:{}".format(time.time() - start_time)) -if __name__ == '__main__': +if __name__ == "__main__": init_web_db() init_web_data() diff --git a/api/db/services/canvas_service.py b/api/db/services/canvas_service.py deleted file mode 100644 index 3fbb296..0000000 --- a/api/db/services/canvas_service.py +++ /dev/null @@ -1,155 +0,0 @@ -# -# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import json -import time -import traceback -from uuid import uuid4 -from agent.canvas import Canvas -from api.db.db_models import DB, CanvasTemplate, UserCanvas, API4Conversation -from api.db.services.api_service import API4ConversationService -from api.db.services.common_service import CommonService -from api.db.services.conversation_service import structure_answer -from api.utils import get_uuid - - -class CanvasTemplateService(CommonService): - model = CanvasTemplate - - -class UserCanvasService(CommonService): - model = UserCanvas - - @classmethod - @DB.connection_context() - def get_list(cls, tenant_id, - page_number, items_per_page, orderby, desc, id, title): - agents = cls.model.select() - if id: - agents = agents.where(cls.model.id == id) - if title: - agents = agents.where(cls.model.title == title) - agents = agents.where(cls.model.user_id == tenant_id) - if desc: - agents = agents.order_by(cls.model.getter_by(orderby).desc()) - else: - agents = agents.order_by(cls.model.getter_by(orderby).asc()) - - agents = agents.paginate(page_number, items_per_page) - - return list(agents.dicts()) - - -def completion(tenant_id, agent_id, question, session_id=None, stream=True, **kwargs): - e, cvs = UserCanvasService.get_by_id(agent_id) - assert e, "Agent not found." - assert cvs.user_id == tenant_id, "You do not own the agent." - if not isinstance(cvs.dsl,str): - cvs.dsl = json.dumps(cvs.dsl, ensure_ascii=False) - canvas = Canvas(cvs.dsl, tenant_id) - canvas.reset() - message_id = str(uuid4()) - if not session_id: - query = canvas.get_preset_param() - if query: - for ele in query: - if not ele["optional"]: - if not kwargs.get(ele["key"]): - assert False, f"`{ele['key']}` is required" - ele["value"] = kwargs[ele["key"]] - if ele["optional"]: - if kwargs.get(ele["key"]): - ele["value"] = kwargs[ele['key']] - else: - if "value" in ele: - ele.pop("value") - cvs.dsl = json.loads(str(canvas)) - session_id=get_uuid() - conv = { - "id": session_id, - "dialog_id": cvs.id, - "user_id": kwargs.get("user_id", "") if isinstance(kwargs, dict) else "", - "message": [{"role": "assistant", "content": canvas.get_prologue(), "created_at": time.time()}], - "source": "agent", - "dsl": cvs.dsl - } - API4ConversationService.save(**conv) - - - conv = API4Conversation(**conv) - else: - e, conv = API4ConversationService.get_by_id(session_id) - assert e, "Session not found!" - canvas = Canvas(json.dumps(conv.dsl), tenant_id) - canvas.messages.append({"role": "user", "content": question, "id": message_id}) - canvas.add_user_input(question) - if not conv.message: - conv.message = [] - conv.message.append({ - "role": "user", - "content": question, - "id": message_id - }) - if not conv.reference: - conv.reference = [] - conv.reference.append({"chunks": [], "doc_aggs": []}) - - final_ans = {"reference": [], "content": ""} - if stream: - try: - for ans in canvas.run(stream=stream): - if ans.get("running_status"): - yield "data:" + json.dumps({"code": 0, "message": "", - "data": {"answer": ans["content"], - "running_status": True}}, - ensure_ascii=False) + "\n\n" - continue - for k in ans.keys(): - final_ans[k] = ans[k] - ans = {"answer": ans["content"], "reference": ans.get("reference", []), "param": canvas.get_preset_param()} - ans = structure_answer(conv, ans, message_id, session_id) - yield "data:" + json.dumps({"code": 0, "message": "", "data": ans}, - ensure_ascii=False) + "\n\n" - - canvas.messages.append({"role": "assistant", "content": final_ans["content"], "created_at": time.time(), "id": message_id}) - canvas.history.append(("assistant", final_ans["content"])) - if final_ans.get("reference"): - canvas.reference.append(final_ans["reference"]) - conv.dsl = json.loads(str(canvas)) - API4ConversationService.append_message(conv.id, conv.to_dict()) - except Exception as e: - traceback.print_exc() - conv.dsl = json.loads(str(canvas)) - API4ConversationService.append_message(conv.id, conv.to_dict()) - yield "data:" + json.dumps({"code": 500, "message": str(e), - "data": {"answer": "**ERROR**: " + str(e), "reference": []}}, - ensure_ascii=False) + "\n\n" - yield "data:" + json.dumps({"code": 0, "message": "", "data": True}, ensure_ascii=False) + "\n\n" - - else: - for answer in canvas.run(stream=False): - if answer.get("running_status"): - continue - final_ans["content"] = "\n".join(answer["content"]) if "content" in answer else "" - canvas.messages.append({"role": "assistant", "content": final_ans["content"], "id": message_id}) - if final_ans.get("reference"): - canvas.reference.append(final_ans["reference"]) - conv.dsl = json.loads(str(canvas)) - - result = {"answer": final_ans["content"], "reference": final_ans.get("reference", []) , "param": canvas.get_preset_param()} - result = structure_answer(conv, result, message_id, session_id) - API4ConversationService.append_message(conv.id, conv.to_dict()) - yield result - break diff --git a/api/ragflow_server.py b/api/ragflow_server.py index 6d73c07..b6a7e47 100644 --- a/api/ragflow_server.py +++ b/api/ragflow_server.py @@ -20,10 +20,12 @@ from rag.settings import print_rag_settings from rag.utils.redis_conn import RedisDistributedLock from api.utils.log_utils import initRootLogger + initRootLogger("ragflow_server") stop_event = threading.Event() + def update_progress(): redis_lock = RedisDistributedLock("update_progress", timeout=60) while not stop_event.is_set(): @@ -37,13 +39,15 @@ def update_progress(): finally: redis_lock.release() + def signal_handler(sig, frame): logging.info("Received interrupt signal, shutting down...") stop_event.set() time.sleep(1) sys.exit(0) -if __name__ == '__main__': + +if __name__ == "__main__": logging.info(r""" _____ ___ _____ _____ _ _____ _ __ _____ _ _ _ _____ | _ \ / | / ___| | ___| | | / _ \ | | / / | _ \ | | | | | | / ___/ @@ -52,29 +56,21 @@ if __name__ == '__main__': | | \ \ / / | | | |_| | | | | |___ | |_| | | |/ |/ / | | | |___ | |_| | ___| | |_| \_\ /_/ |_| \_____/ |_| |_____| \_____/ |___/|___/ |_| |_____| \_____/ /_____/ """) - logging.info( - f'RAGFlow base version: {get_ragflow_version()}' - ) - logging.info( - f'project base: {utils.file_utils.get_project_base_directory()}' - ) + logging.info(f"RAGFlow base version: {get_ragflow_version()}") + logging.info(f"project base: {utils.file_utils.get_project_base_directory()}") show_configs() settings.init_settings() print_rag_settings() # init db init_web_db() - init_web_data() + # init runtime config import argparse parser = argparse.ArgumentParser() - parser.add_argument( - "--version", default=False, help="RAGFlow version", action="store_true" - ) - parser.add_argument( - "--debug", default=False, help="debug mode", action="store_true" - ) + parser.add_argument("--version", default=False, help="RAGFlow version", action="store_true") + parser.add_argument("--debug", default=False, help="debug mode", action="store_true") args = parser.parse_args() if args.version: print(get_ragflow_version()) diff --git a/example/sdk/dataset_example.py b/example/sdk/dataset_example.py index 252ed02..ec4e9ac 100644 --- a/example/sdk/dataset_example.py +++ b/example/sdk/dataset_example.py @@ -14,9 +14,9 @@ # limitations under the License. # -''' +""" The example is about CRUD operations (Create, Read, Update, Delete) on a dataset. -''' +""" from ragflow_sdk import RAGFlow import sys @@ -32,7 +32,7 @@ try: dataset_instance = ragflow_instance.create_dataset(name="dataset_instance") # update the dataset instance - updated_message = {"name":"updated_dataset"} + updated_message = {"name": "updated_dataset"} updated_dataset = dataset_instance.update(updated_message) # get the dataset (list datasets) @@ -51,5 +51,3 @@ try: except Exception as e: print(str(e)) sys.exit(-1) - - diff --git a/sdk/python/ragflow_sdk/__init__.py b/sdk/python/ragflow_sdk/__init__.py index 32a25ed..a28b2de 100644 --- a/sdk/python/ragflow_sdk/__init__.py +++ b/sdk/python/ragflow_sdk/__init__.py @@ -6,18 +6,10 @@ from .modules.chat import Chat from .modules.session import Session from .modules.document import Document from .modules.chunk import Chunk -from .modules.agent import Agent + beartype_this_package() __version__ = importlib.metadata.version("ragflow_sdk") -__all__ = [ - "RAGFlow", - "DataSet", - "Chat", - "Session", - "Document", - "Chunk", - "Agent" -] \ No newline at end of file +__all__ = ["RAGFlow", "DataSet", "Chat", "Session", "Document", "Chunk"] diff --git a/sdk/python/ragflow_sdk/modules/agent.py b/sdk/python/ragflow_sdk/modules/agent.py deleted file mode 100644 index 42b97a8..0000000 --- a/sdk/python/ragflow_sdk/modules/agent.py +++ /dev/null @@ -1,94 +0,0 @@ -# -# Copyright 2025 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -from .base import Base -from .session import Session - - -class Agent(Base): - def __init__(self, rag, res_dict): - self.id = None - self.avatar = None - self.canvas_type = None - self.description = None - self.dsl = None - super().__init__(rag, res_dict) - - class Dsl(Base): - def __init__(self, rag, res_dict): - self.answer = [] - self.components = { - "begin": { - "downstream": ["Answer:China"], - "obj": { - "component_name": "Begin", - "params": {} - }, - "upstream": [] - } - } - self.graph = { - "edges": [], - "nodes": [ - { - "data": { - "label": "Begin", - "name": "begin" - }, - "id": "begin", - "position": { - "x": 50, - "y": 200 - }, - "sourcePosition": "left", - "targetPosition": "right", - "type": "beginNode" - } - ] - } - self.history = [] - self.messages = [] - self.path = [] - self.reference = [] - super().__init__(rag, res_dict) - - - def create_session(self, **kwargs) -> Session: - res = self.post(f"/agents/{self.id}/sessions", json=kwargs) - res = res.json() - if res.get("code") == 0: - return Session(self.rag, res.get("data")) - raise Exception(res.get("message")) - - - def list_sessions(self, page: int = 1, page_size: int = 30, orderby: str = "create_time", desc: bool = True, - id: str = None) -> list[Session]: - res = self.get(f"/agents/{self.id}/sessions", - {"page": page, "page_size": page_size, "orderby": orderby, "desc": desc, "id": id}) - res = res.json() - if res.get("code") == 0: - result_list = [] - for data in res.get("data"): - temp_agent = Session(self.rag, data) - result_list.append(temp_agent) - return result_list - raise Exception(res.get("message")) - - def delete_sessions(self, ids: list[str] | None = None): - res = self.rm(f"/agents/{self.id}/sessions", {"ids": ids}) - res = res.json() - if res.get("code") != 0: - raise Exception(res.get("message")) \ No newline at end of file diff --git a/sdk/python/ragflow_sdk/modules/session.py b/sdk/python/ragflow_sdk/modules/session.py index f308988..7f26330 100644 --- a/sdk/python/ragflow_sdk/modules/session.py +++ b/sdk/python/ragflow_sdk/modules/session.py @@ -28,17 +28,12 @@ class Session(Base): if key == "chat_id" and value is not None: self.chat_id = None self.__session_type = "chat" - if key == "agent_id" and value is not None: - self.agent_id = None - self.__session_type = "agent" + super().__init__(rag, res_dict) def ask(self, question="", stream=True, **kwargs): - if self.__session_type == "agent": - res = self._ask_agent(question, stream) - elif self.__session_type == "chat": - res = self._ask_chat(question, stream, **kwargs) - + res = self._ask_chat(question, stream, **kwargs) + for line in res.iter_lines(): line = line.decode("utf-8") if line.startswith("{"): @@ -51,10 +46,7 @@ class Session(Base): continue answer = json_data["data"]["answer"] reference = json_data["data"].get("reference", {}) - temp_dict = { - "content": answer, - "role": "assistant" - } + temp_dict = {"content": answer, "role": "assistant"} if reference and "chunks" in reference: chunks = reference["chunks"] temp_dict["reference"] = chunks @@ -63,22 +55,15 @@ class Session(Base): yield message if not stream: return message - + def _ask_chat(self, question: str, stream: bool, **kwargs): json_data = {"question": question, "stream": stream, "session_id": self.id} json_data.update(kwargs) - res = self.post(f"/chats/{self.chat_id}/completions", - json_data, stream=stream) - return res - - def _ask_agent(self, question: str, stream: bool): - res = self.post(f"/agents/{self.agent_id}/completions", - {"question": question, "stream": stream, "session_id": self.id}, stream=stream) + res = self.post(f"/chats/{self.chat_id}/completions", json_data, stream=stream) return res def update(self, update_message): - res = self.put(f"/chats/{self.chat_id}/sessions/{self.id}", - update_message) + res = self.put(f"/chats/{self.chat_id}/sessions/{self.id}", update_message) res = res.json() if res.get("code") != 0: raise Exception(res.get("message")) diff --git a/sdk/python/ragflow_sdk/ragflow.py b/sdk/python/ragflow_sdk/ragflow.py index a84c351..65fffa6 100644 --- a/sdk/python/ragflow_sdk/ragflow.py +++ b/sdk/python/ragflow_sdk/ragflow.py @@ -18,11 +18,10 @@ import requests from .modules.chat import Chat from .modules.chunk import Chunk from .modules.dataset import DataSet -from .modules.agent import Agent class RAGFlow: - def __init__(self, api_key, base_url, version='v1'): + def __init__(self, api_key, base_url, version="v1"): """ api_url: http:///api/v1 """ @@ -31,11 +30,11 @@ class RAGFlow: self.authorization_header = {"Authorization": "{} {}".format("Bearer", self.user_key)} def post(self, path, json=None, stream=False, files=None): - res = requests.post(url=self.api_url + path, json=json, headers=self.authorization_header, stream=stream,files=files) + res = requests.post(url=self.api_url + path, json=json, headers=self.authorization_header, stream=stream, files=files) return res def get(self, path, params=None, json=None): - res = requests.get(url=self.api_url + path, params=params, headers=self.authorization_header,json=json) + res = requests.get(url=self.api_url + path, params=params, headers=self.authorization_header, json=json) return res def delete(self, path, json): @@ -43,54 +42,63 @@ class RAGFlow: return res def put(self, path, json): - res = requests.put(url=self.api_url + path, json= json,headers=self.authorization_header) + res = requests.put(url=self.api_url + path, json=json, headers=self.authorization_header) return res - def create_dataset(self, name: str, avatar: str = "", description: str = "", embedding_model:str = "BAAI/bge-large-zh-v1.5", - language: str = "English", - permission: str = "me",chunk_method: str = "naive", - parser_config: DataSet.ParserConfig = None) -> DataSet: + def create_dataset( + self, + name: str, + avatar: str = "", + description: str = "", + embedding_model: str = "BAAI/bge-large-zh-v1.5", + language: str = "English", + permission: str = "me", + chunk_method: str = "naive", + parser_config: DataSet.ParserConfig = None, + ) -> DataSet: if parser_config: parser_config = parser_config.to_json() - res = self.post("/datasets", - {"name": name, "avatar": avatar, "description": description,"embedding_model":embedding_model, - "language": language, - "permission": permission, "chunk_method": chunk_method, - "parser_config": parser_config - } - ) + res = self.post( + "/datasets", + { + "name": name, + "avatar": avatar, + "description": description, + "embedding_model": embedding_model, + "language": language, + "permission": permission, + "chunk_method": chunk_method, + "parser_config": parser_config, + }, + ) res = res.json() if res.get("code") == 0: return DataSet(self, res["data"]) raise Exception(res["message"]) def delete_datasets(self, ids: list[str] | None = None): - res = self.delete("/datasets",{"ids": ids}) - res=res.json() + res = self.delete("/datasets", {"ids": ids}) + res = res.json() if res.get("code") != 0: raise Exception(res["message"]) - def get_dataset(self,name: str): + def get_dataset(self, name: str): _list = self.list_datasets(name=name) if len(_list) > 0: return _list[0] raise Exception("Dataset %s not found" % name) - def list_datasets(self, page: int = 1, page_size: int = 30, orderby: str = "create_time", desc: bool = True, - id: str | None = None, name: str | None = None) -> \ - list[DataSet]: - res = self.get("/datasets", - {"page": page, "page_size": page_size, "orderby": orderby, "desc": desc, "id": id, "name": name}) + def list_datasets(self, page: int = 1, page_size: int = 30, orderby: str = "create_time", desc: bool = True, id: str | None = None, name: str | None = None) -> list[DataSet]: + res = self.get("/datasets", {"page": page, "page_size": page_size, "orderby": orderby, "desc": desc, "id": id, "name": name}) res = res.json() result_list = [] if res.get("code") == 0: - for data in res['data']: + for data in res["data"]: result_list.append(DataSet(self, data)) return result_list raise Exception(res["message"]) - def create_chat(self, name: str, avatar: str = "", dataset_ids=None, - llm: Chat.LLM | None = None, prompt: Chat.Prompt | None = None) -> Chat: + def create_chat(self, name: str, avatar: str = "", dataset_ids=None, llm: Chat.LLM | None = None, prompt: Chat.Prompt | None = None) -> Chat: if dataset_ids is None: dataset_ids = [] dataset_list = [] @@ -98,25 +106,33 @@ class RAGFlow: dataset_list.append(id) if llm is None: - llm = Chat.LLM(self, {"model_name": None, - "temperature": 0.1, - "top_p": 0.3, - "presence_penalty": 0.4, - "frequency_penalty": 0.7, - "max_tokens": 512, }) + llm = Chat.LLM( + self, + { + "model_name": None, + "temperature": 0.1, + "top_p": 0.3, + "presence_penalty": 0.4, + "frequency_penalty": 0.7, + "max_tokens": 512, + }, + ) if prompt is None: - prompt = Chat.Prompt(self, {"similarity_threshold": 0.2, - "keywords_similarity_weight": 0.7, - "top_n": 8, - "top_k": 1024, - "variables": [{ - "key": "knowledge", - "optional": True - }], "rerank_model": "", - "empty_response": None, - "opener": None, - "show_quote": True, - "prompt": None}) + prompt = Chat.Prompt( + self, + { + "similarity_threshold": 0.2, + "keywords_similarity_weight": 0.7, + "top_n": 8, + "top_k": 1024, + "variables": [{"key": "knowledge", "optional": True}], + "rerank_model": "", + "empty_response": None, + "opener": None, + "show_quote": True, + "prompt": None, + }, + ) if prompt.opener is None: prompt.opener = "Hi! I'm your assistant, what can I do for you?" if prompt.prompt is None: @@ -127,70 +143,63 @@ class RAGFlow: "Answers need to consider chat history.\nHere is the knowledge base:\n{knowledge}\nThe above is the knowledge base." ) - temp_dict = {"name": name, - "avatar": avatar, - "dataset_ids": dataset_list if dataset_list else [], - "llm": llm.to_json(), - "prompt": prompt.to_json()} + temp_dict = {"name": name, "avatar": avatar, "dataset_ids": dataset_list if dataset_list else [], "llm": llm.to_json(), "prompt": prompt.to_json()} res = self.post("/chats", temp_dict) res = res.json() if res.get("code") == 0: return Chat(self, res["data"]) raise Exception(res["message"]) - def delete_chats(self,ids: list[str] | None = None): - res = self.delete('/chats', - {"ids":ids}) + def delete_chats(self, ids: list[str] | None = None): + res = self.delete("/chats", {"ids": ids}) res = res.json() if res.get("code") != 0: raise Exception(res["message"]) - def list_chats(self, page: int = 1, page_size: int = 30, orderby: str = "create_time", desc: bool = True, - id: str | None = None, name: str | None = None) -> list[Chat]: - res = self.get("/chats",{"page": page, "page_size": page_size, "orderby": orderby, "desc": desc, "id": id, "name": name}) + def list_chats(self, page: int = 1, page_size: int = 30, orderby: str = "create_time", desc: bool = True, id: str | None = None, name: str | None = None) -> list[Chat]: + res = self.get("/chats", {"page": page, "page_size": page_size, "orderby": orderby, "desc": desc, "id": id, "name": name}) res = res.json() result_list = [] if res.get("code") == 0: - for data in res['data']: + for data in res["data"]: result_list.append(Chat(self, data)) return result_list raise Exception(res["message"]) - - def retrieve(self, dataset_ids, document_ids=None, question="", page=1, page_size=30, similarity_threshold=0.2, vector_similarity_weight=0.3, top_k=1024, rerank_id: str | None = None, keyword:bool=False, ): - if document_ids is None: - document_ids = [] - data_json ={ - "page": page, - "page_size": page_size, - "similarity_threshold": similarity_threshold, - "vector_similarity_weight": vector_similarity_weight, - "top_k": top_k, - "rerank_id": rerank_id, - "keyword": keyword, - "question": question, - "dataset_ids": dataset_ids, - "documents": document_ids - } - # Send a POST request to the backend service (using requests library as an example, actual implementation may vary) - res = self.post('/retrieval',json=data_json) - res = res.json() - if res.get("code") ==0: - chunks=[] - for chunk_data in res["data"].get("chunks"): - chunk=Chunk(self,chunk_data) - chunks.append(chunk) - return chunks - raise Exception(res.get("message")) - - - def list_agents(self, page: int = 1, page_size: int = 30, orderby: str = "update_time", desc: bool = True, - id: str | None = None, title: str | None = None) -> list[Agent]: - res = self.get("/agents",{"page": page, "page_size": page_size, "orderby": orderby, "desc": desc, "id": id, "title": title}) + def retrieve( + self, + dataset_ids, + document_ids=None, + question="", + page=1, + page_size=30, + similarity_threshold=0.2, + vector_similarity_weight=0.3, + top_k=1024, + rerank_id: str | None = None, + keyword: bool = False, + ): + if document_ids is None: + document_ids = [] + data_json = { + "page": page, + "page_size": page_size, + "similarity_threshold": similarity_threshold, + "vector_similarity_weight": vector_similarity_weight, + "top_k": top_k, + "rerank_id": rerank_id, + "keyword": keyword, + "question": question, + "dataset_ids": dataset_ids, + "documents": document_ids, + } + # Send a POST request to the backend service (using requests library as an example, actual implementation may vary) + res = self.post("/retrieval", json=data_json) res = res.json() - result_list = [] if res.get("code") == 0: - for data in res['data']: - result_list.append(Agent(self, data)) - return result_list - raise Exception(res["message"]) + chunks = [] + for chunk_data in res["data"].get("chunks"): + chunk = Chunk(self, chunk_data) + chunks.append(chunk) + return chunks + raise Exception(res.get("message")) diff --git a/sdk/python/test/test_sdk_api/t_agent.py b/sdk/python/test/test_sdk_api/t_agent.py deleted file mode 100644 index 2fcd0e5..0000000 --- a/sdk/python/test/test_sdk_api/t_agent.py +++ /dev/null @@ -1,36 +0,0 @@ -# -# Copyright 2025 The InfiniFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -from ragflow_sdk import RAGFlow, Agent -from common import HOST_ADDRESS -import pytest - - -@pytest.mark.skip(reason="") -def test_list_agents_with_success(get_api_key_fixture): - API_KEY = get_api_key_fixture - rag = RAGFlow(API_KEY, HOST_ADDRESS) - rag.list_agents() - - -@pytest.mark.skip(reason="") -def test_converse_with_agent_with_success(get_api_key_fixture): - API_KEY = "ragflow-BkOGNhYjIyN2JiODExZWY5MzVhMDI0Mm" - agent_id = "ebfada2eb2bc11ef968a0242ac120006" - rag = RAGFlow(API_KEY, HOST_ADDRESS) - lang = "Chinese" - file = "How is the weather tomorrow?" - Agent.ask(agent_id=agent_id, rag=rag, lang=lang, file=file) diff --git a/sdk/python/test/test_sdk_api/t_session.py b/sdk/python/test/test_sdk_api/t_session.py index 81cc33e..5aedd01 100644 --- a/sdk/python/test/test_sdk_api/t_session.py +++ b/sdk/python/test/test_sdk_api/t_session.py @@ -16,7 +16,6 @@ from ragflow_sdk import RAGFlow from common import HOST_ADDRESS -import pytest def test_create_session_with_success(get_api_key_fixture): @@ -111,35 +110,3 @@ def test_list_sessions_with_success(get_api_key_fixture): assistant.create_session("test_1") assistant.create_session("test_2") assistant.list_sessions() - - -@pytest.mark.skip(reason="") -def test_create_agent_session_with_success(get_api_key_fixture): - API_KEY = "ragflow-BkOGNhYjIyN2JiODExZWY5MzVhMDI0Mm" - rag = RAGFlow(API_KEY, HOST_ADDRESS) - agent = rag.list_agents(id="2e45b5209c1011efa3e90242ac120006")[0] - agent.create_session() - - -@pytest.mark.skip(reason="") -def test_create_agent_conversation_with_success(get_api_key_fixture): - API_KEY = "ragflow-BkOGNhYjIyN2JiODExZWY5MzVhMDI0Mm" - rag = RAGFlow(API_KEY, HOST_ADDRESS) - agent = rag.list_agents(id="2e45b5209c1011efa3e90242ac120006")[0] - session = agent.create_session() - session.ask("What is this job") - - -@pytest.mark.skip(reason="") -def test_list_agent_sessions_with_success(get_api_key_fixture): - API_KEY = "ragflow-BkOGNhYjIyN2JiODExZWY5MzVhMDI0Mm" - rag = RAGFlow(API_KEY, HOST_ADDRESS) - agent = rag.list_agents(id="2e45b5209c1011efa3e90242ac120006")[0] - agent.list_sessions() - -@pytest.mark.skip(reason="") -def test_delete_session_of_agent_with_success(get_api_key_fixture): - API_KEY = "ragflow-BkOGNhYjIyN2JiODExZWY5MzVhMDI0Mm" - rag = RAGFlow(API_KEY, HOST_ADDRESS) - agent = rag.list_agents(id="2e45b5209c1011efa3e90242ac120006")[0] - agent.delete_sessions(ids=["test_1"]) diff --git a/web/src/components/api-service/hooks.ts b/web/src/components/api-service/hooks.ts index 16878d8..6543585 100644 --- a/web/src/components/api-service/hooks.ts +++ b/web/src/components/api-service/hooks.ts @@ -155,13 +155,7 @@ export const usePreviewChat = (idKey: string) => { const open = useCallback( (t: string) => { - window.open( - getUrlWithToken( - t, - idKey === 'canvasId' ? SharedFrom.Agent : SharedFrom.Chat, - ), - '_blank', - ); + window.open(getUrlWithToken(t, SharedFrom.Chat), '_blank'); }, [idKey], ); diff --git a/web/src/components/prompt-editor/variable-node.tsx b/web/src/components/prompt-editor/variable-node.tsx index e2a8cc2..8f59a6a 100644 --- a/web/src/components/prompt-editor/variable-node.tsx +++ b/web/src/components/prompt-editor/variable-node.tsx @@ -1,8 +1,5 @@ -import i18n from '@/locales/config'; -import { BeginId } from '@/pages/flow/constant'; import { DecoratorNode, LexicalNode, NodeKey } from 'lexical'; import { ReactNode } from 'react'; -const prefix = BeginId + '@'; export class VariableNode extends DecoratorNode { __value: string; @@ -37,13 +34,6 @@ export class VariableNode extends DecoratorNode { let content: ReactNode = ( {this.__label} ); - if (this.__value.startsWith(prefix)) { - content = ( -
- {i18n.t(`flow.begin`)} / {content} -
- ); - } return (
{content} diff --git a/web/src/components/prompt-editor/variable-picker-plugin.tsx b/web/src/components/prompt-editor/variable-picker-plugin.tsx index b50e540..de5588e 100644 --- a/web/src/components/prompt-editor/variable-picker-plugin.tsx +++ b/web/src/components/prompt-editor/variable-picker-plugin.tsx @@ -29,8 +29,6 @@ import { } from 'react'; import * as ReactDOM from 'react-dom'; -import { FlowFormContext } from '@/pages/flow/context'; -import { useBuildComponentIdSelectOptions } from '@/pages/flow/hooks/use-get-begin-query'; import { $createVariableNode } from './variable-node'; import { ProgrammaticTag } from './constant'; diff --git a/web/src/constants/chat.ts b/web/src/constants/chat.ts index 53f99b1..bfcd885 100644 --- a/web/src/constants/chat.ts +++ b/web/src/constants/chat.ts @@ -20,7 +20,6 @@ export const variableEnabledFieldMap = { }; export enum SharedFrom { - Agent = 'agent', Chat = 'chat', } diff --git a/web/src/hooks/flow-hooks.ts b/web/src/hooks/flow-hooks.ts deleted file mode 100644 index 5a0964d..0000000 --- a/web/src/hooks/flow-hooks.ts +++ /dev/null @@ -1,308 +0,0 @@ -import { ResponseType } from '@/interfaces/database/base'; -import { DSL, IFlow, IFlowTemplate } from '@/interfaces/database/flow'; -import { IDebugSingleRequestBody } from '@/interfaces/request/flow'; -import i18n from '@/locales/config'; -import { useGetSharedChatSearchParams } from '@/pages/chat/shared-hooks'; -import { BeginId } from '@/pages/flow/constant'; -import flowService from '@/services/flow-service'; -import { buildMessageListWithUuid } from '@/utils/chat'; -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import { message } from 'antd'; -import { set } from 'lodash'; -import get from 'lodash/get'; -import { useParams } from 'umi'; -import { v4 as uuid } from 'uuid'; - -export const EmptyDsl = { - graph: { - nodes: [ - { - id: BeginId, - type: 'beginNode', - position: { - x: 50, - y: 200, - }, - data: { - label: 'Begin', - name: 'begin', - }, - sourcePosition: 'left', - targetPosition: 'right', - }, - ], - edges: [], - }, - components: { - begin: { - obj: { - component_name: 'Begin', - params: {}, - }, - downstream: ['Answer:China'], // other edge target is downstream, edge source is current node id - upstream: [], // edge source is upstream, edge target is current node id - }, - }, - messages: [], - reference: [], - history: [], - path: [], - answer: [], -}; - -export const useFetchFlowTemplates = (): ResponseType => { - const { data } = useQuery({ - queryKey: ['fetchFlowTemplates'], - initialData: [], - queryFn: async () => { - const { data } = await flowService.listTemplates(); - if (Array.isArray(data?.data)) { - data.data.unshift({ - id: uuid(), - title: 'Blank', - description: 'Create your agent from scratch', - dsl: EmptyDsl, - }); - } - - return data; - }, - }); - - return data; -}; - -export const useFetchFlowList = (): { data: IFlow[]; loading: boolean } => { - const { data, isFetching: loading } = useQuery({ - queryKey: ['fetchFlowList'], - initialData: [], - gcTime: 0, - queryFn: async () => { - const { data } = await flowService.listCanvas(); - - return data?.data ?? []; - }, - }); - - return { data, loading }; -}; - -export const useFetchFlow = (): { - data: IFlow; - loading: boolean; - refetch: () => void; -} => { - const { id } = useParams(); - const { sharedId } = useGetSharedChatSearchParams(); - - const { - data, - isFetching: loading, - refetch, - } = useQuery({ - queryKey: ['flowDetail'], - initialData: {} as IFlow, - refetchOnReconnect: false, - refetchOnMount: false, - refetchOnWindowFocus: false, - gcTime: 0, - queryFn: async () => { - const { data } = await flowService.getCanvas({}, sharedId || id); - - const messageList = buildMessageListWithUuid( - get(data, 'data.dsl.messages', []), - ); - set(data, 'data.dsl.messages', messageList); - - return data?.data ?? {}; - }, - }); - - return { data, loading, refetch }; -}; - -export const useFetchFlowSSE = (): { - data: IFlow; - loading: boolean; - refetch: () => void; -} => { - const { sharedId } = useGetSharedChatSearchParams(); - - const { - data, - isFetching: loading, - refetch, - } = useQuery({ - queryKey: ['flowDetailSSE'], - initialData: {} as IFlow, - refetchOnReconnect: false, - refetchOnMount: false, - refetchOnWindowFocus: false, - gcTime: 0, - queryFn: async () => { - if (!sharedId) return {}; - const { data } = await flowService.getCanvasSSE({}, sharedId); - - const messageList = buildMessageListWithUuid( - get(data, 'data.dsl.messages', []), - ); - set(data, 'data.dsl.messages', messageList); - - return data?.data ?? {}; - }, - }); - - return { data, loading, refetch }; -}; - -export const useSetFlow = () => { - const queryClient = useQueryClient(); - const { - data, - isPending: loading, - mutateAsync, - } = useMutation({ - mutationKey: ['setFlow'], - mutationFn: async (params: { - id?: string; - title?: string; - dsl?: DSL; - avatar?: string; - }) => { - const { data = {} } = await flowService.setCanvas(params); - if (data.code === 0) { - message.success( - i18n.t(`message.${params?.id ? 'modified' : 'created'}`), - ); - queryClient.invalidateQueries({ queryKey: ['fetchFlowList'] }); - } - return data; - }, - }); - - return { data, loading, setFlow: mutateAsync }; -}; - -export const useDeleteFlow = () => { - const queryClient = useQueryClient(); - const { - data, - isPending: loading, - mutateAsync, - } = useMutation({ - mutationKey: ['deleteFlow'], - mutationFn: async (canvasIds: string[]) => { - const { data } = await flowService.removeCanvas({ canvasIds }); - if (data.code === 0) { - queryClient.invalidateQueries({ queryKey: ['fetchFlowList'] }); - } - return data?.data ?? []; - }, - }); - - return { data, loading, deleteFlow: mutateAsync }; -}; - -export const useRunFlow = () => { - const { - data, - isPending: loading, - mutateAsync, - } = useMutation({ - mutationKey: ['runFlow'], - mutationFn: async (params: { id: string; dsl: DSL }) => { - const { data } = await flowService.runCanvas(params); - if (data.code === 0) { - message.success(i18n.t(`message.modified`)); - } - return data?.data ?? {}; - }, - }); - - return { data, loading, runFlow: mutateAsync }; -}; - -export const useResetFlow = () => { - const { id } = useParams(); - const { - data, - isPending: loading, - mutateAsync, - } = useMutation({ - mutationKey: ['resetFlow'], - mutationFn: async () => { - const { data } = await flowService.resetCanvas({ id }); - return data; - }, - }); - - return { data, loading, resetFlow: mutateAsync }; -}; - -export const useTestDbConnect = () => { - const { - data, - isPending: loading, - mutateAsync, - } = useMutation({ - mutationKey: ['testDbConnect'], - mutationFn: async (params: any) => { - const ret = await flowService.testDbConnect(params); - if (ret?.data?.code === 0) { - message.success(ret?.data?.data); - } else { - message.error(ret?.data?.data); - } - return ret; - }, - }); - - return { data, loading, testDbConnect: mutateAsync }; -}; - -export const useFetchInputElements = (componentId?: string) => { - const { id } = useParams(); - - const { data, isPending: loading } = useQuery({ - queryKey: ['fetchInputElements', id, componentId], - initialData: [], - enabled: !!id && !!componentId, - retryOnMount: false, - refetchOnWindowFocus: false, - refetchOnReconnect: false, - gcTime: 0, - queryFn: async () => { - try { - const { data } = await flowService.getInputElements({ - id, - component_id: componentId, - }); - return data?.data ?? []; - } catch (error) { - console.log('🚀 ~ queryFn: ~ error:', error); - } - }, - }); - - return { data, loading }; -}; - -export const useDebugSingle = () => { - const { id } = useParams(); - const { - data, - isPending: loading, - mutateAsync, - } = useMutation({ - mutationKey: ['debugSingle'], - mutationFn: async (params: IDebugSingleRequestBody) => { - const ret = await flowService.debugSingle({ id, ...params }); - if (ret?.data?.code !== 0) { - message.error(ret?.data?.message); - } - return ret?.data?.data; - }, - }); - - return { data, loading, debugSingle: mutateAsync }; -}; diff --git a/web/src/hooks/logic-hooks/navigate-hooks.ts b/web/src/hooks/logic-hooks/navigate-hooks.ts index ef6f155..140be78 100644 --- a/web/src/hooks/logic-hooks/navigate-hooks.ts +++ b/web/src/hooks/logic-hooks/navigate-hooks.ts @@ -38,21 +38,6 @@ export const useNavigatePage = () => { navigate(Routes.Chat); }, [navigate]); - const navigateToAgentList = useCallback(() => { - navigate(Routes.Agents); - }, [navigate]); - - const navigateToAgent = useCallback( - (id: string) => () => { - navigate(`${Routes.Agent}/${id}`); - }, - [navigate], - ); - - const navigateToAgentTemplates = useCallback(() => { - navigate(Routes.AgentTemplates); - }, [navigate]); - const navigateToSearchList = useCallback(() => { navigate(Routes.Searches); }, [navigate]); @@ -104,9 +89,7 @@ export const useNavigatePage = () => { navigateToChunkParsedResult, getQueryString, navigateToChunk, - navigateToAgentList, - navigateToAgent, - navigateToAgentTemplates, + navigateToSearchList, navigateToSearch, }; diff --git a/web/src/layouts/next-header.tsx b/web/src/layouts/next-header.tsx index a2e6ff8..14cd11f 100644 --- a/web/src/layouts/next-header.tsx +++ b/web/src/layouts/next-header.tsx @@ -9,7 +9,6 @@ import { cn } from '@/lib/utils'; import { Routes } from '@/routes'; import { ChevronDown, - Cpu, File, Github, House, @@ -33,7 +32,6 @@ export function Header() { { path: Routes.Datasets, name: t('knowledgeBase'), icon: Library }, { path: Routes.Chats, name: t('chat'), icon: MessageSquareText }, { path: Routes.Searches, name: t('search'), icon: Search }, - { path: Routes.Agents, name: t('flow'), icon: Cpu }, { path: Routes.Files, name: t('fileManager'), icon: File }, ], [t], diff --git a/web/src/locales/config.ts b/web/src/locales/config.ts index 10e5f69..845793f 100644 --- a/web/src/locales/config.ts +++ b/web/src/locales/config.ts @@ -3,14 +3,8 @@ import LanguageDetector from 'i18next-browser-languagedetector'; import { initReactI18next } from 'react-i18next'; import { LanguageAbbreviation } from '@/constants/common'; -import translation_de from './de'; import translation_en from './en'; -import translation_es from './es'; -import translation_id from './id'; -import translation_ja from './ja'; -import translation_pt_br from './pt-br'; import { createTranslationTable, flattenObject } from './until'; -import translation_vi from './vi'; import translation_zh from './zh'; import translation_zh_traditional from './zh-traditional'; @@ -18,42 +12,16 @@ const resources = { [LanguageAbbreviation.En]: translation_en, [LanguageAbbreviation.Zh]: translation_zh, [LanguageAbbreviation.ZhTraditional]: translation_zh_traditional, - [LanguageAbbreviation.Id]: translation_id, - [LanguageAbbreviation.Ja]: translation_ja, - [LanguageAbbreviation.Es]: translation_es, - [LanguageAbbreviation.Vi]: translation_vi, - [LanguageAbbreviation.PtBr]: translation_pt_br, - [LanguageAbbreviation.De]: translation_de, }; const enFlattened = flattenObject(translation_en); -const viFlattened = flattenObject(translation_vi); -const esFlattened = flattenObject(translation_es); + const zhFlattened = flattenObject(translation_zh); -const jaFlattened = flattenObject(translation_ja); -const pt_brFlattened = flattenObject(translation_pt_br); + const zh_traditionalFlattened = flattenObject(translation_zh_traditional); -const deFlattened = flattenObject(translation_de); + export const translationTable = createTranslationTable( - [ - enFlattened, - viFlattened, - esFlattened, - zhFlattened, - zh_traditionalFlattened, - jaFlattened, - pt_brFlattened, - deFlattened, - ], - [ - 'English', - 'Vietnamese', - 'Spanish', - 'zh', - 'zh-TRADITIONAL', - 'ja', - 'pt-BR', - 'Deutsch', - ], + [enFlattened, zhFlattened, zh_traditionalFlattened], + ['English', 'zh', 'zh-TRADITIONAL'], ); i18n .use(initReactI18next) diff --git a/web/src/pages/agent/agent-sidebar.tsx b/web/src/pages/agent/agent-sidebar.tsx deleted file mode 100644 index 42f399a..0000000 --- a/web/src/pages/agent/agent-sidebar.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import { SideDown } from '@/assets/icon/Icon'; -import { Card, CardContent } from '@/components/ui/card'; -import { - Collapsible, - CollapsibleContent, - CollapsibleTrigger, -} from '@/components/ui/collapsible'; -import { - Sidebar, - SidebarContent, - SidebarGroup, - SidebarGroupContent, - SidebarGroupLabel, - SidebarHeader, - SidebarMenu, -} from '@/components/ui/sidebar'; -import { useMemo } from 'react'; -import { - AgentOperatorList, - Operator, - componentMenuList, - operatorMap, -} from './constant'; -import OperatorIcon from './operator-icon'; - -type OperatorItem = { - name: Operator; -}; - -function OperatorCard({ name }: OperatorItem) { - return ( - - - - {name} - - - ); -} - -type OperatorCollapsibleProps = { operatorList: OperatorItem[]; title: string }; - -function OperatorCollapsible({ - operatorList, - title, -}: OperatorCollapsibleProps) { - return ( - - - - - {title} - - - - - - - {operatorList.map((item) => ( - - ))} - - - - - - ); -} - -export function AgentSidebar() { - const agentOperatorList = useMemo(() => { - return componentMenuList.filter((x) => - AgentOperatorList.some((y) => y === x.name), - ); - }, []); - - const thirdOperatorList = useMemo(() => { - return componentMenuList.filter( - (x) => !AgentOperatorList.some((y) => y === x.name), - ); - }, []); - - return ( - - -

All nodes

-
- - - - -
- ); -} diff --git a/web/src/pages/agent/canvas/context-menu/index.less b/web/src/pages/agent/canvas/context-menu/index.less deleted file mode 100644 index 5594aa9..0000000 --- a/web/src/pages/agent/canvas/context-menu/index.less +++ /dev/null @@ -1,18 +0,0 @@ -.contextMenu { - background: rgba(255, 255, 255, 0.1); - border-style: solid; - box-shadow: 10px 19px 20px rgba(0, 0, 0, 10%); - position: absolute; - z-index: 10; - button { - border: none; - display: block; - padding: 0.5em; - text-align: left; - width: 100%; - } - - button:hover { - background: rgba(255, 255, 255, 0.1); - } -} diff --git a/web/src/pages/agent/canvas/context-menu/index.tsx b/web/src/pages/agent/canvas/context-menu/index.tsx deleted file mode 100644 index 6cb306a..0000000 --- a/web/src/pages/agent/canvas/context-menu/index.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import { NodeMouseHandler, useReactFlow } from '@xyflow/react'; -import { useCallback, useRef, useState } from 'react'; - -import styles from './index.less'; - -export interface INodeContextMenu { - id: string; - top: number; - left: number; - right?: number; - bottom?: number; - [key: string]: unknown; -} - -export function NodeContextMenu({ - id, - top, - left, - right, - bottom, - ...props -}: INodeContextMenu) { - const { getNode, setNodes, addNodes, setEdges } = useReactFlow(); - - const duplicateNode = useCallback(() => { - const node = getNode(id); - const position = { - x: node?.position?.x || 0 + 50, - y: node?.position?.y || 0 + 50, - }; - - addNodes({ - ...(node || {}), - data: node?.data, - selected: false, - dragging: false, - id: `${node?.id}-copy`, - position, - }); - }, [id, getNode, addNodes]); - - const deleteNode = useCallback(() => { - setNodes((nodes) => nodes.filter((node) => node.id !== id)); - setEdges((edges) => edges.filter((edge) => edge.source !== id)); - }, [id, setNodes, setEdges]); - - return ( -
-

- node: {id} -

- - -
- ); -} - -/* @deprecated - */ -export const useHandleNodeContextMenu = (sideWidth: number) => { - const [menu, setMenu] = useState({} as INodeContextMenu); - const ref = useRef(null); - - const onNodeContextMenu: NodeMouseHandler = useCallback( - (event, node) => { - // Prevent native context menu from showing - event.preventDefault(); - - // Calculate position of the context menu. We want to make sure it - // doesn't get positioned off-screen. - const pane = ref.current?.getBoundingClientRect(); - // setMenu({ - // id: node.id, - // top: event.clientY < pane.height - 200 ? event.clientY : 0, - // left: event.clientX < pane.width - 200 ? event.clientX : 0, - // right: event.clientX >= pane.width - 200 ? pane.width - event.clientX : 0, - // bottom: - // event.clientY >= pane.height - 200 ? pane.height - event.clientY : 0, - // }); - - setMenu({ - id: node.id, - top: event.clientY - 144, - left: event.clientX - sideWidth, - // top: event.clientY < pane.height - 200 ? event.clientY - 72 : 0, - // left: event.clientX < pane.width - 200 ? event.clientX : 0, - }); - }, - [sideWidth], - ); - - // Close the context menu if it's open whenever the window is clicked. - const onPaneClick = useCallback( - () => setMenu({} as INodeContextMenu), - [setMenu], - ); - - return { onNodeContextMenu, menu, onPaneClick, ref }; -}; diff --git a/web/src/pages/agent/canvas/edge/index.less b/web/src/pages/agent/canvas/edge/index.less deleted file mode 100644 index 281b672..0000000 --- a/web/src/pages/agent/canvas/edge/index.less +++ /dev/null @@ -1,31 +0,0 @@ -.edgeButton { - width: 14px; - height: 14px; - background: #eee; - border: 1px solid #fff; - padding: 0; - cursor: pointer; - border-radius: 50%; - font-size: 10px; - line-height: 1; -} - -.edgeButton:hover { - box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.08); -} - -.edgeButtonDark { - width: 14px; - height: 14px; - background: #0e0c0c; - border: 1px solid #fff; - padding: 0; - cursor: pointer; - border-radius: 50%; - font-size: 10px; - line-height: 1; -} - -.edgeButtonDark:hover { - box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.08); -} diff --git a/web/src/pages/agent/canvas/edge/index.tsx b/web/src/pages/agent/canvas/edge/index.tsx deleted file mode 100644 index 52f939b..0000000 --- a/web/src/pages/agent/canvas/edge/index.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import { - BaseEdge, - EdgeLabelRenderer, - EdgeProps, - getBezierPath, -} from '@xyflow/react'; -import useGraphStore from '../../store'; - -import { useTheme } from '@/components/theme-provider'; -import { useFetchFlow } from '@/hooks/flow-hooks'; -import { useMemo } from 'react'; -import styles from './index.less'; - -export function ButtonEdge({ - id, - sourceX, - sourceY, - targetX, - targetY, - sourcePosition, - targetPosition, - source, - target, - style = {}, - markerEnd, - selected, -}: EdgeProps) { - const deleteEdgeById = useGraphStore((state) => state.deleteEdgeById); - const [edgePath, labelX, labelY] = getBezierPath({ - sourceX, - sourceY, - sourcePosition, - targetX, - targetY, - targetPosition, - }); - const { theme } = useTheme(); - const selectedStyle = useMemo(() => { - return selected ? { strokeWidth: 2, stroke: '#1677ff' } : {}; - }, [selected]); - - const onEdgeClick = () => { - deleteEdgeById(id); - }; - - // highlight the nodes that the workflow passes through - const { data: flowDetail } = useFetchFlow(); - - const graphPath = useMemo(() => { - // TODO: this will be called multiple times - const path = flowDetail?.dsl?.path ?? []; - // The second to last - const previousGraphPath: string[] = path.at(-2) ?? []; - let graphPath: string[] = path.at(-1) ?? []; - // The last of the second to last article - const previousLatestElement = previousGraphPath.at(-1); - if (previousGraphPath.length > 0 && previousLatestElement) { - graphPath = [previousLatestElement, ...graphPath]; - } - return graphPath; - }, [flowDetail.dsl?.path]); - - const highlightStyle = useMemo(() => { - const idx = graphPath.findIndex((x) => x === source); - if (idx !== -1) { - // The set of elements following source - const slicedGraphPath = graphPath.slice(idx + 1); - if (slicedGraphPath.some((x) => x === target)) { - return { strokeWidth: 2, stroke: 'red' }; - } - } - return {}; - }, [source, target, graphPath]); - - return ( - <> - - -
- -
-
- - ); -} diff --git a/web/src/pages/agent/canvas/index.less b/web/src/pages/agent/canvas/index.less deleted file mode 100644 index d824d88..0000000 --- a/web/src/pages/agent/canvas/index.less +++ /dev/null @@ -1,10 +0,0 @@ -.canvasWrapper { - position: relative; - height: 100%; - :global(.react-flow__node-group) { - .commonNode(); - padding: 0; - border: 0; - background-color: transparent; - } -} diff --git a/web/src/pages/agent/canvas/index.tsx b/web/src/pages/agent/canvas/index.tsx deleted file mode 100644 index c7acb9d..0000000 --- a/web/src/pages/agent/canvas/index.tsx +++ /dev/null @@ -1,183 +0,0 @@ -import { - Background, - ConnectionMode, - NodeTypes, - ReactFlow, -} from '@xyflow/react'; -import '@xyflow/react/dist/style.css'; -// import ChatDrawer from '../chat/drawer'; -import FormSheet from '../form-sheet/next'; -import { - useHandleDrop, - useSelectCanvasData, - useValidateConnection, - useWatchNodeFormDataChange, -} from '../hooks'; -import { useBeforeDelete } from '../hooks/use-before-delete'; -import { useShowDrawer } from '../hooks/use-show-drawer'; -// import RunDrawer from '../run-drawer'; -import { ButtonEdge } from './edge'; -import styles from './index.less'; -import { RagNode } from './node'; -import { BeginNode } from './node/begin-node'; -import { CategorizeNode } from './node/categorize-node'; -import { EmailNode } from './node/email-node'; -import { GenerateNode } from './node/generate-node'; -import { InvokeNode } from './node/invoke-node'; -import { IterationNode, IterationStartNode } from './node/iteration-node'; -import { KeywordNode } from './node/keyword-node'; -import { LogicNode } from './node/logic-node'; -import { MessageNode } from './node/message-node'; -import NoteNode from './node/note-node'; -import { RelevantNode } from './node/relevant-node'; -import { RetrievalNode } from './node/retrieval-node'; -import { RewriteNode } from './node/rewrite-node'; -import { SwitchNode } from './node/switch-node'; -import { TemplateNode } from './node/template-node'; - -const nodeTypes: NodeTypes = { - ragNode: RagNode, - categorizeNode: CategorizeNode, - beginNode: BeginNode, - relevantNode: RelevantNode, - logicNode: LogicNode, - noteNode: NoteNode, - switchNode: SwitchNode, - generateNode: GenerateNode, - retrievalNode: RetrievalNode, - messageNode: MessageNode, - rewriteNode: RewriteNode, - keywordNode: KeywordNode, - invokeNode: InvokeNode, - templateNode: TemplateNode, - emailNode: EmailNode, - group: IterationNode, - iterationStartNode: IterationStartNode, -}; - -const edgeTypes = { - buttonEdge: ButtonEdge, -}; - -interface IProps { - drawerVisible: boolean; - hideDrawer(): void; -} - -function FlowCanvas({ drawerVisible, hideDrawer }: IProps) { - const { - nodes, - edges, - onConnect, - onEdgesChange, - onNodesChange, - onSelectionChange, - } = useSelectCanvasData(); - const isValidConnection = useValidateConnection(); - - const { onDrop, onDragOver, setReactFlowInstance } = useHandleDrop(); - - const { - onNodeClick, - onPaneClick, - clickedNode, - formDrawerVisible, - hideFormDrawer, - singleDebugDrawerVisible, - hideSingleDebugDrawer, - showSingleDebugDrawer, - chatVisible, - runVisible, - hideRunOrChatDrawer, - showChatModal, - } = useShowDrawer({ - drawerVisible, - hideDrawer, - }); - - const { handleBeforeDelete } = useBeforeDelete(); - - useWatchNodeFormDataChange(); - - return ( -
- - - - - - - - - {formDrawerVisible && ( - - )} - {/* {chatVisible && ( - - )} - - {runVisible && ( - - )} */} -
- ); -} - -export default FlowCanvas; diff --git a/web/src/pages/agent/canvas/node/begin-node.tsx b/web/src/pages/agent/canvas/node/begin-node.tsx deleted file mode 100644 index 83e3665..0000000 --- a/web/src/pages/agent/canvas/node/begin-node.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { useTheme } from '@/components/theme-provider'; -import { IBeginNode } from '@/interfaces/database/flow'; -import { Handle, NodeProps, Position } from '@xyflow/react'; -import { Flex } from 'antd'; -import classNames from 'classnames'; -import get from 'lodash/get'; -import { useTranslation } from 'react-i18next'; -import { - BeginQueryType, - BeginQueryTypeIconMap, - Operator, - operatorMap, -} from '../../constant'; -import { BeginQuery } from '../../interface'; -import OperatorIcon from '../../operator-icon'; -import { RightHandleStyle } from './handle-icon'; -import styles from './index.less'; - -// TODO: do not allow other nodes to connect to this node -export function BeginNode({ selected, data }: NodeProps) { - const { t } = useTranslation(); - const query: BeginQuery[] = get(data, 'form.query', []); - const { theme } = useTheme(); - return ( -
- - - - -
- {t(`flow.begin`)} -
-
- - {query.map((x, idx) => { - const Icon = BeginQueryTypeIconMap[x.type as BeginQueryType]; - return ( - - - - {x.name} - {x.optional ? 'Yes' : 'No'} - - ); - })} - -
- ); -} diff --git a/web/src/pages/agent/canvas/node/card.tsx b/web/src/pages/agent/canvas/node/card.tsx deleted file mode 100644 index 042ca45..0000000 --- a/web/src/pages/agent/canvas/node/card.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { Button } from '@/components/ui/button'; -import { - Card, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from '@/components/ui/card'; -import { Input } from '@/components/ui/input'; -import { Label } from '@/components/ui/label'; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@/components/ui/select'; - -export function CardWithForm() { - return ( - - - Create project - Deploy your new project in one-click. - - -
-
-
- - -
-
- - -
-
-
-
- - - - -
- ); -} diff --git a/web/src/pages/agent/canvas/node/categorize-handle.tsx b/web/src/pages/agent/canvas/node/categorize-handle.tsx deleted file mode 100644 index ce1fc36..0000000 --- a/web/src/pages/agent/canvas/node/categorize-handle.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { Handle, Position } from '@xyflow/react'; - -import React from 'react'; -import styles from './index.less'; - -const DEFAULT_HANDLE_STYLE = { - width: 6, - height: 6, - bottom: -5, - fontSize: 8, -}; - -interface IProps extends React.PropsWithChildren { - top: number; - right: number; - id: string; - idx?: number; -} - -const CategorizeHandle = ({ top, right, id, children }: IProps) => { - return ( - - {children || id} - - ); -}; - -export default CategorizeHandle; diff --git a/web/src/pages/agent/canvas/node/categorize-node.tsx b/web/src/pages/agent/canvas/node/categorize-node.tsx deleted file mode 100644 index 18c3cdf..0000000 --- a/web/src/pages/agent/canvas/node/categorize-node.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import LLMLabel from '@/components/llm-select/llm-label'; -import { useTheme } from '@/components/theme-provider'; -import { ICategorizeNode } from '@/interfaces/database/flow'; -import { Handle, NodeProps, Position } from '@xyflow/react'; -import { Flex } from 'antd'; -import classNames from 'classnames'; -import { get } from 'lodash'; -import { RightHandleStyle } from './handle-icon'; -import { useBuildCategorizeHandlePositions } from './hooks'; -import styles from './index.less'; -import NodeHeader from './node-header'; - -export function CategorizeNode({ - id, - data, - selected, -}: NodeProps) { - const { positions } = useBuildCategorizeHandlePositions({ data, id }); - const { theme } = useTheme(); - return ( -
- - - - - -
- -
- {positions.map((position, idx) => { - return ( -
-
{position.text}
- -
- ); - })} -
-
- ); -} diff --git a/web/src/pages/agent/canvas/node/dropdown.tsx b/web/src/pages/agent/canvas/node/dropdown.tsx deleted file mode 100644 index dd5263a..0000000 --- a/web/src/pages/agent/canvas/node/dropdown.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import OperateDropdown from '@/components/operate-dropdown'; -import { CopyOutlined } from '@ant-design/icons'; -import { Flex, MenuProps } from 'antd'; -import { useCallback } from 'react'; -import { useTranslation } from 'react-i18next'; -import { Operator } from '../../constant'; -import { useDuplicateNode } from '../../hooks'; -import useGraphStore from '../../store'; - -interface IProps { - id: string; - iconFontColor?: string; - label: string; -} - -const NodeDropdown = ({ id, iconFontColor, label }: IProps) => { - const { t } = useTranslation(); - const deleteNodeById = useGraphStore((store) => store.deleteNodeById); - const deleteIterationNodeById = useGraphStore( - (store) => store.deleteIterationNodeById, - ); - - const deleteNode = useCallback(() => { - if (label === Operator.Iteration) { - deleteIterationNodeById(id); - } else { - deleteNodeById(id); - } - }, [label, deleteIterationNodeById, id, deleteNodeById]); - - const duplicateNode = useDuplicateNode(); - - const items: MenuProps['items'] = [ - { - key: '2', - onClick: () => duplicateNode(id, label), - label: ( - - {t('common.copy')} - - - ), - }, - ]; - - return ( - - ); -}; - -export default NodeDropdown; diff --git a/web/src/pages/agent/canvas/node/email-node.tsx b/web/src/pages/agent/canvas/node/email-node.tsx deleted file mode 100644 index ae4af84..0000000 --- a/web/src/pages/agent/canvas/node/email-node.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { IEmailNode } from '@/interfaces/database/flow'; -import { Handle, NodeProps, Position } from '@xyflow/react'; -import { Flex } from 'antd'; -import classNames from 'classnames'; -import { useState } from 'react'; -import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; -import styles from './index.less'; -import NodeHeader from './node-header'; - -export function EmailNode({ - id, - data, - isConnectable = true, - selected, -}: NodeProps) { - const [showDetails, setShowDetails] = useState(false); - - return ( -
- - - - - -
setShowDetails(!showDetails)} - > -
- SMTP: - {data.form?.smtp_server} -
-
- Port: - {data.form?.smtp_port} -
-
- From: - {data.form?.email} -
-
{showDetails ? '▼' : '▶'}
-
- - {showDetails && ( -
-
Expected Input JSON:
-
-              {`{
-  "to_email": "...",
-  "cc_email": "...", 
-  "subject": "...",
-  "content": "..."
-}`}
-            
-
- )} -
-
- ); -} diff --git a/web/src/pages/agent/canvas/node/generate-node.tsx b/web/src/pages/agent/canvas/node/generate-node.tsx deleted file mode 100644 index 255eccd..0000000 --- a/web/src/pages/agent/canvas/node/generate-node.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import LLMLabel from '@/components/llm-select/llm-label'; -import { useTheme } from '@/components/theme-provider'; -import { IGenerateNode } from '@/interfaces/database/flow'; -import { Handle, NodeProps, Position } from '@xyflow/react'; -import classNames from 'classnames'; -import { get } from 'lodash'; -import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; -import styles from './index.less'; -import NodeHeader from './node-header'; - -export function GenerateNode({ - id, - data, - isConnectable = true, - selected, -}: NodeProps) { - const { theme } = useTheme(); - return ( -
- - - - - -
- -
-
- ); -} diff --git a/web/src/pages/agent/canvas/node/handle-icon.tsx b/web/src/pages/agent/canvas/node/handle-icon.tsx deleted file mode 100644 index 36c7f36..0000000 --- a/web/src/pages/agent/canvas/node/handle-icon.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { PlusOutlined } from '@ant-design/icons'; -import { CSSProperties } from 'react'; - -export const HandleIcon = () => { - return ( - - ); -}; - -export const RightHandleStyle: CSSProperties = { - right: 0, -}; - -export const LeftHandleStyle: CSSProperties = { - left: 0, -}; - -export default HandleIcon; diff --git a/web/src/pages/agent/canvas/node/hooks.ts b/web/src/pages/agent/canvas/node/hooks.ts deleted file mode 100644 index fbea8f1..0000000 --- a/web/src/pages/agent/canvas/node/hooks.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { useUpdateNodeInternals } from '@xyflow/react'; -import get from 'lodash/get'; -import { useEffect, useMemo } from 'react'; -import { SwitchElseTo } from '../../constant'; - -import { - ICategorizeItemResult, - ISwitchCondition, - RAGFlowNodeType, -} from '@/interfaces/database/flow'; -import { generateSwitchHandleText } from '../../utils'; - -export const useBuildCategorizeHandlePositions = ({ - data, - id, -}: { - id: string; - data: RAGFlowNodeType['data']; -}) => { - const updateNodeInternals = useUpdateNodeInternals(); - - const categoryData: ICategorizeItemResult = useMemo(() => { - return get(data, `form.category_description`, {}); - }, [data]); - - const positions = useMemo(() => { - const list: Array<{ - text: string; - top: number; - idx: number; - }> = []; - - Object.keys(categoryData) - .sort((a, b) => categoryData[a].index - categoryData[b].index) - .forEach((x, idx) => { - list.push({ - text: x, - idx, - top: idx === 0 ? 98 + 20 : list[idx - 1].top + 8 + 26, - }); - }); - - return list; - }, [categoryData]); - - useEffect(() => { - updateNodeInternals(id); - }, [id, updateNodeInternals, categoryData]); - - return { positions }; -}; - -export const useBuildSwitchHandlePositions = ({ - data, - id, -}: { - id: string; - data: RAGFlowNodeType['data']; -}) => { - const updateNodeInternals = useUpdateNodeInternals(); - - const conditions: ISwitchCondition[] = useMemo(() => { - return get(data, 'form.conditions', []); - }, [data]); - - const positions = useMemo(() => { - const list: Array<{ - text: string; - top: number; - idx: number; - condition?: ISwitchCondition; - }> = []; - - [...conditions, ''].forEach((x, idx) => { - let top = idx === 0 ? 58 + 20 : list[idx - 1].top + 32; // case number (Case 1) height + flex gap - if (idx - 1 >= 0) { - const previousItems = conditions[idx - 1]?.items ?? []; - if (previousItems.length > 0) { - top += 12; // ConditionBlock padding - top += previousItems.length * 22; // condition variable height - top += (previousItems.length - 1) * 25; // operator height - } - } - - list.push({ - text: - idx < conditions.length - ? generateSwitchHandleText(idx) - : SwitchElseTo, - idx, - top, - condition: typeof x === 'string' ? undefined : x, - }); - }); - - return list; - }, [conditions]); - - useEffect(() => { - updateNodeInternals(id); - }, [id, updateNodeInternals, conditions]); - - return { positions }; -}; diff --git a/web/src/pages/agent/canvas/node/index.less b/web/src/pages/agent/canvas/node/index.less deleted file mode 100644 index 14d7e60..0000000 --- a/web/src/pages/agent/canvas/node/index.less +++ /dev/null @@ -1,285 +0,0 @@ -.dark { - background: rgb(63, 63, 63) !important; -} -.ragNode { - .commonNode(); - .nodeName { - font-size: 10px; - color: black; - } - label { - display: block; - color: #777; - font-size: 12px; - } - .description { - font-size: 10px; - } - - .categorizeAnchorPointText { - position: absolute; - top: -4px; - left: 8px; - white-space: nowrap; - } -} - -@lightBackgroundColor: rgba(150, 150, 150, 0.1); -@darkBackgroundColor: rgba(150, 150, 150, 0.2); - -.selectedNode { - border: 1.5px solid rgb(59, 118, 244); -} - -.selectedIterationNode { - border-bottom: 1.5px solid rgb(59, 118, 244); - border-left: 1.5px solid rgb(59, 118, 244); - border-right: 1.5px solid rgb(59, 118, 244); -} - -.iterationHeader { - .commonNodeShadow(); -} - -.selectedHeader { - border-top: 1.9px solid rgb(59, 118, 244); - border-left: 1.9px solid rgb(59, 118, 244); - border-right: 1.9px solid rgb(59, 118, 244); -} - -.handle { - display: inline-flex; - align-items: center; - justify-content: center; - width: 12px; - height: 12px; - background: rgb(59, 88, 253); - border: 1px solid white; - z-index: 1; - background-image: url('@/assets/svg/plus.svg'); - background-size: cover; - background-position: center; -} - -.jsonView { - word-wrap: break-word; - overflow: auto; - max-width: 300px; - max-height: 500px; -} - -.logicNode { - .commonNode(); - - .nodeName { - font-size: 10px; - color: black; - } - label { - display: block; - color: #777; - font-size: 12px; - } - - .description { - font-size: 10px; - } - - .categorizeAnchorPointText { - position: absolute; - top: -4px; - left: 8px; - white-space: nowrap; - } - .relevantSourceLabel { - font-size: 10px; - } -} - -.noteNode { - .commonNode(); - min-width: 140px; - width: auto; - height: 100%; - padding: 8px; - border-radius: 10px; - min-height: 128px; - .noteTitle { - background-color: #edfcff; - font-size: 12px; - padding: 6px 6px 4px; - border-top-left-radius: 10px; - border-top-right-radius: 10px; - } - .noteTitleDark { - background-color: #edfcff; - font-size: 12px; - padding: 6px 6px 4px; - border-top-left-radius: 10px; - border-top-right-radius: 10px; - } - .noteForm { - margin-top: 4px; - height: calc(100% - 50px); - } - .noteName { - padding: 0px 4px; - } - .noteTextarea { - resize: none; - border: 0; - border-radius: 0; - height: 100%; - &:focus { - border: none; - box-shadow: none; - } - } -} - -.iterationNode { - .commonNodeShadow(); - border-bottom-left-radius: 10px; - border-bottom-right-radius: 10px; -} - -.nodeText { - padding-inline: 0.4em; - padding-block: 0.2em 0.1em; - background: @lightBackgroundColor; - border-radius: 3px; - min-height: 22px; - .textEllipsis(); -} - -.nodeHeader { - padding-bottom: 12px; -} - -.zeroDivider { - margin: 0 !important; -} - -.conditionBlock { - border-radius: 4px; - padding: 6px; - background: @lightBackgroundColor; -} - -.conditionLine { - border-radius: 4px; - padding: 0 4px; - background: @darkBackgroundColor; - .textEllipsis(); -} - -.conditionKey { - flex: 1; -} - -.conditionOperator { - padding: 0 2px; - text-align: center; -} - -.relevantLabel { - text-align: right; -} - -.knowledgeNodeName { - .textEllipsis(); -} - -.messageNodeContainer { - overflow-y: auto; - max-height: 300px; -} - -.generateParameters { - padding-top: 8px; - label { - flex: 2; - .textEllipsis(); - } - .parameterValue { - flex: 3; - .conditionLine; - } -} - -.emailNodeContainer { - padding: 8px; - font-size: 12px; - - .emailConfig { - background: rgba(0, 0, 0, 0.02); - border-radius: 4px; - padding: 8px; - position: relative; - cursor: pointer; - - &:hover { - background: rgba(0, 0, 0, 0.04); - } - - .configItem { - display: flex; - align-items: center; - margin-bottom: 4px; - - &:last-child { - margin-bottom: 0; - } - - .configLabel { - color: #666; - width: 45px; - flex-shrink: 0; - } - - .configValue { - color: #333; - word-break: break-all; - } - } - - .expandIcon { - position: absolute; - right: 8px; - top: 50%; - transform: translateY(-50%); - color: #666; - font-size: 12px; - } - } - - .jsonExample { - background: #f5f5f5; - border-radius: 4px; - padding: 8px; - margin-top: 4px; - animation: slideDown 0.2s ease-out; - - .jsonTitle { - color: #666; - margin-bottom: 4px; - } - - .jsonContent { - margin: 0; - color: #333; - font-family: monospace; - } - } -} - -@keyframes slideDown { - from { - opacity: 0; - transform: translateY(-10px); - } - to { - opacity: 1; - transform: translateY(0); - } -} diff --git a/web/src/pages/agent/canvas/node/index.tsx b/web/src/pages/agent/canvas/node/index.tsx deleted file mode 100644 index 32191f5..0000000 --- a/web/src/pages/agent/canvas/node/index.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { useTheme } from '@/components/theme-provider'; -import { IRagNode } from '@/interfaces/database/flow'; -import { Handle, NodeProps, Position } from '@xyflow/react'; -import classNames from 'classnames'; -import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; -import styles from './index.less'; -import NodeHeader from './node-header'; - -export function RagNode({ - id, - data, - isConnectable = true, - selected, -}: NodeProps) { - const { theme } = useTheme(); - return ( -
- - - -
- ); -} diff --git a/web/src/pages/agent/canvas/node/invoke-node.tsx b/web/src/pages/agent/canvas/node/invoke-node.tsx deleted file mode 100644 index 42d109f..0000000 --- a/web/src/pages/agent/canvas/node/invoke-node.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { useTheme } from '@/components/theme-provider'; -import { IInvokeNode } from '@/interfaces/database/flow'; -import { Handle, NodeProps, Position } from '@xyflow/react'; -import { Flex } from 'antd'; -import classNames from 'classnames'; -import { get } from 'lodash'; -import { useTranslation } from 'react-i18next'; -import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; -import styles from './index.less'; -import NodeHeader from './node-header'; - -export function InvokeNode({ - id, - data, - isConnectable = true, - selected, -}: NodeProps) { - const { t } = useTranslation(); - const { theme } = useTheme(); - const url = get(data, 'form.url'); - return ( -
- - - - -
{t('flow.url')}
-
{url}
-
-
- ); -} diff --git a/web/src/pages/agent/canvas/node/iteration-node.tsx b/web/src/pages/agent/canvas/node/iteration-node.tsx deleted file mode 100644 index c15b4fc..0000000 --- a/web/src/pages/agent/canvas/node/iteration-node.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import { useTheme } from '@/components/theme-provider'; -import { - IIterationNode, - IIterationStartNode, -} from '@/interfaces/database/flow'; -import { cn } from '@/lib/utils'; -import { Handle, NodeProps, NodeResizeControl, Position } from '@xyflow/react'; -import { ListRestart } from 'lucide-react'; -import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; -import styles from './index.less'; -import NodeHeader from './node-header'; - -function ResizeIcon() { - return ( - - - - - - - - ); -} - -const controlStyle = { - background: 'transparent', - border: 'none', - cursor: 'nwse-resize', -}; - -export function IterationNode({ - id, - data, - isConnectable = true, - selected, -}: NodeProps) { - const { theme } = useTheme(); - - return ( -
- - - - - - -
- ); -} - -export function IterationStartNode({ - isConnectable = true, - selected, -}: NodeProps) { - const { theme } = useTheme(); - - return ( -
- -
- -
-
- ); -} diff --git a/web/src/pages/agent/canvas/node/keyword-node.tsx b/web/src/pages/agent/canvas/node/keyword-node.tsx deleted file mode 100644 index f607d43..0000000 --- a/web/src/pages/agent/canvas/node/keyword-node.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import LLMLabel from '@/components/llm-select/llm-label'; -import { useTheme } from '@/components/theme-provider'; -import { IKeywordNode } from '@/interfaces/database/flow'; -import { Handle, NodeProps, Position } from '@xyflow/react'; -import classNames from 'classnames'; -import { get } from 'lodash'; -import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; -import styles from './index.less'; -import NodeHeader from './node-header'; - -export function KeywordNode({ - id, - data, - isConnectable = true, - selected, -}: NodeProps) { - const { theme } = useTheme(); - return ( -
- - - - - -
- -
-
- ); -} diff --git a/web/src/pages/agent/canvas/node/logic-node.tsx b/web/src/pages/agent/canvas/node/logic-node.tsx deleted file mode 100644 index 2821561..0000000 --- a/web/src/pages/agent/canvas/node/logic-node.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { useTheme } from '@/components/theme-provider'; -import { ILogicNode } from '@/interfaces/database/flow'; -import { Handle, NodeProps, Position } from '@xyflow/react'; -import classNames from 'classnames'; -import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; -import styles from './index.less'; -import NodeHeader from './node-header'; - -export function LogicNode({ - id, - data, - isConnectable = true, - selected, -}: NodeProps) { - const { theme } = useTheme(); - return ( -
- - - -
- ); -} diff --git a/web/src/pages/agent/canvas/node/message-node.tsx b/web/src/pages/agent/canvas/node/message-node.tsx deleted file mode 100644 index 5b3a173..0000000 --- a/web/src/pages/agent/canvas/node/message-node.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { useTheme } from '@/components/theme-provider'; -import { IMessageNode } from '@/interfaces/database/flow'; -import { Handle, NodeProps, Position } from '@xyflow/react'; -import { Flex } from 'antd'; -import classNames from 'classnames'; -import { get } from 'lodash'; -import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; -import styles from './index.less'; -import NodeHeader from './node-header'; - -export function MessageNode({ - id, - data, - isConnectable = true, - selected, -}: NodeProps) { - const messages: string[] = get(data, 'form.messages', []); - const { theme } = useTheme(); - return ( -
- - - 0, - })} - > - - - {messages.map((message, idx) => { - return ( -
- {message} -
- ); - })} -
-
- ); -} diff --git a/web/src/pages/agent/canvas/node/node-header.tsx b/web/src/pages/agent/canvas/node/node-header.tsx deleted file mode 100644 index 99a37dc..0000000 --- a/web/src/pages/agent/canvas/node/node-header.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { useTranslate } from '@/hooks/common-hooks'; -import { Flex } from 'antd'; -import { Play } from 'lucide-react'; -import { Operator, operatorMap } from '../../constant'; -import OperatorIcon from '../../operator-icon'; -import { needsSingleStepDebugging } from '../../utils'; -import NodeDropdown from './dropdown'; -import { NextNodePopover } from './popover'; - -import { RunTooltip } from '../../flow-tooltip'; -interface IProps { - id: string; - label: string; - name: string; - gap?: number; - className?: string; - wrapperClassName?: string; -} - -const ExcludedRunStateOperators = [Operator.Answer]; - -export function RunStatus({ id, name, label }: IProps) { - const { t } = useTranslate('flow'); - return ( -
- {needsSingleStepDebugging(label) && ( - - - // data-play is used to trigger single step debugging - )} - - - {t('operationResults')} - - -
- ); -} - -const NodeHeader = ({ - label, - id, - name, - gap = 4, - className, - wrapperClassName, -}: IProps) => { - return ( -
- {!ExcludedRunStateOperators.includes(label as Operator) && ( - - )} - - - - {name} - - - -
- ); -}; - -export default NodeHeader; diff --git a/web/src/pages/agent/canvas/node/note-node.tsx b/web/src/pages/agent/canvas/node/note-node.tsx deleted file mode 100644 index 1917a81..0000000 --- a/web/src/pages/agent/canvas/node/note-node.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import { NodeProps, NodeResizeControl } from '@xyflow/react'; -import { Flex, Form, Input } from 'antd'; -import classNames from 'classnames'; -import NodeDropdown from './dropdown'; - -import SvgIcon from '@/components/svg-icon'; -import { useTheme } from '@/components/theme-provider'; -import { INoteNode } from '@/interfaces/database/flow'; -import { memo, useEffect } from 'react'; -import { useTranslation } from 'react-i18next'; -import { - useHandleFormValuesChange, - useHandleNodeNameChange, -} from '../../hooks'; -import styles from './index.less'; - -const { TextArea } = Input; - -const controlStyle = { - background: 'transparent', - border: 'none', -}; - -function NoteNode({ data, id }: NodeProps) { - const { t } = useTranslation(); - const [form] = Form.useForm(); - const { theme } = useTheme(); - - const { name, handleNameBlur, handleNameChange } = useHandleNodeNameChange({ - id, - data, - }); - const { handleValuesChange } = useHandleFormValuesChange(id); - - useEffect(() => { - form.setFieldsValue(data?.form); - }, [form, data?.form]); - - return ( - <> - - - -
- - - - - -
- -