From f69841de86415d10de822c26cca0d0e1c8a42493 Mon Sep 17 00:00:00 2001 From: userpj Date: Thu, 7 Nov 2024 18:58:15 +0800 Subject: [PATCH 01/85] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=B5=81=E6=B0=B4?= =?UTF-8?q?=E7=BA=BF=E5=8D=95=E6=B5=8B=E7=9B=AE=E5=BD=95=20(#581)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/python-package.yml | 2 +- CONTRIBUTING.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 5147e50e4..dc4a33ab5 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -144,7 +144,7 @@ jobs: echo "检测到Python或Shell文件被更改(根据环境变量APPBUILDER_PYTHON_TESTS),准备启动单元测试Test with unittest部分..." cd cicd/app-builder pwd - sh appbuilder/tests/run_python_test.sh + sh python/tests/run_python_test.sh fi Go-Test-CI: runs-on: ubuntu-latest diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9f34fe673..f1e7a9ecc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -64,7 +64,7 @@ and add/update `.vscode/settings.json` file with the following content: "python.testing.unittestArgs": [ "-v", "-s", - "./appbuilder", + "./python", "-p", "test*.py" ], From 7f68cc2edab546974e7855142dc5d8a43c557d7d Mon Sep 17 00:00:00 2001 From: C9luster <138663536+C9luster@users.noreply.github.com> Date: Thu, 7 Nov 2024 19:05:42 +0800 Subject: [PATCH 02/85] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E5=90=8D=E7=A7=B0=20(#579)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 更新模型名称 * 更新百度Search单元测试 --------- Co-authored-by: yinjiaqi --- README.md | 4 +- appbuilder/core/agent.py | 2 +- .../core/components/gbi/nl2sql/README.md | 2 +- .../core/components/gbi/nl2sql/component.py | 2 +- .../components/gbi/select_table/README.md | 2 +- .../components/gbi/select_table/component.py | 2 +- .../components/llms/dialog_summary/README.md | 2 +- .../llms/dialog_summary/component.py | 2 +- .../llms/hallucination_detection/README.md | 6 +-- .../llms/hallucination_detection/component.py | 4 +- .../llms/is_complex_query/README.md | 4 +- .../llms/is_complex_query/component.py | 2 +- appbuilder/core/components/llms/mrc/README.md | 6 +-- .../core/components/llms/nl2pandas/README.md | 2 +- .../components/llms/nl2pandas/component.py | 2 +- .../llms/oral_query_generation/README.md | 6 +-- .../llms/oral_query_generation/component.py | 6 +-- .../core/components/llms/playground/README.md | 2 +- .../components/llms/playground/component.py | 2 +- .../components/llms/qa_pair_mining/README.md | 4 +- .../llms/qa_pair_mining/component.py | 2 +- .../llms/query_decomposition/README.md | 4 +- .../llms/query_decomposition/component.py | 2 +- .../components/llms/query_rewrite/README.md | 2 +- .../llms/query_rewrite/component.py | 2 +- .../llms/similar_question/README.md | 2 +- .../llms/similar_question/component.py | 2 +- .../components/llms/style_rewrite/README.md | 2 +- .../llms/style_rewrite/component.py | 2 +- .../components/llms/style_writing/README.md | 2 +- .../llms/style_writing/component.py | 2 +- .../components/llms/tag_extraction/README.md | 2 +- .../llms/tag_extraction/component.py | 2 +- .../rag_with_baidu_search/README.md | 4 +- .../rag_with_baidu_search_pro/README.md | 4 +- .../tests/test_appbuilder_components_trace.py | 2 +- appbuilder/tests/test_dialog_summary.py | 2 +- .../tests/test_hallucination_detection.py | 2 +- appbuilder/tests/test_is_complex_query.py | 2 +- appbuilder/tests/test_mrc.py | 2 +- appbuilder/tests/test_nl2pandas.py | 2 +- .../tests/test_oral_query_generation.py | 2 +- appbuilder/tests/test_qa_pair_mining.py | 2 +- appbuilder/tests/test_query_decomposition.py | 2 +- appbuilder/tests/test_query_rewrite.py | 4 +- appbuilder/tests/test_rag_baidu_search.py | 2 +- appbuilder/tests/test_rag_baidu_search_pro.py | 22 +++++++++ .../test_rag_with_baidu_search_component.py | 48 ------------------- appbuilder/tests/test_similar_question.py | 2 +- appbuilder/tests/test_style_rewrite.py | 2 +- appbuilder/tests/test_style_writing.py | 2 +- appbuilder/tests/test_tag_extraction.py | 2 +- appbuilder/utils/model_util.py | 4 +- cookbooks/components/agent_runtime.ipynb | 2 +- .../components/rag_with_baidusearch.ipynb | 4 +- cookbooks/components/text_generation.ipynb | 8 ++-- cookbooks/end2end_application/rag/rag.ipynb | 4 +- docs/README_en.md | 4 +- docs/README_ja.md | 4 +- docs/basic_module/README.md | 8 ++-- docs/basic_module/agentruntime.md | 2 +- docs/develop_guide/README.md | 2 +- docs/quick_start/README.md | 8 ++-- ...der.core.components.llms.dialog_summary.md | 2 +- ...components.llms.hallucination_detection.md | 2 +- ...r.core.components.llms.is_complex_query.md | 2 +- ...pbuilder.core.components.llms.nl2pandas.md | 2 +- ...e.components.llms.oral_query_generation.md | 4 +- ...builder.core.components.llms.playground.md | 2 +- ...ore.components.llms.query_decomposition.md | 2 +- ...lder.core.components.llms.query_rewrite.md | 2 +- ...r.core.components.llms.similar_question.md | 2 +- ...lder.core.components.llms.style_rewrite.md | 2 +- ...lder.core.components.llms.style_writing.md | 2 +- ...der.core.components.llms.tag_extraction.md | 2 +- docs/sphinx_md/appbuilder.core.md | 2 +- 76 files changed, 127 insertions(+), 153 deletions(-) delete mode 100644 appbuilder/tests/test_rag_with_baidu_search_component.py diff --git a/README.md b/README.md index 2c51dcf69..c32e48c08 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,7 @@ template_str = "你扮演{role}, 请回答我的问题。\n\n问题:{question} # 定义输入,调用playground组件 input = appbuilder.Message({"role": "java工程师", "question": "请简要回答java语言的内存回收机制是什么,要求100字以内"}) -playground = appbuilder.Playground(prompt_template=template_str, model="Qianfan-Appbuilder-Speed-8k") +playground = appbuilder.Playground(prompt_template=template_str, model="Qianfan-Agent-Speed-8k") # 以打字机的方式,流式展示大模型回答内容 output = playground(input, stream=True, temperature=1e-10) @@ -157,7 +157,7 @@ import os # 设置环境中的TOKEN,以下TOKEN为访问和QPS受限的试用TOKEN,正式使用请替换为您的个人TOKEN os.environ["APPBUILDER_TOKEN"] = "bce-v3/ALTAK-n5AYUIUJMarF7F7iFXVeK/1bf65eed7c8c7efef9b11388524fa1087f90ea58" -rag_with_baidu_search_pro = appbuilder.RagWithBaiduSearchPro(model="Qianfan-Appbuilder-Speed-8k") +rag_with_baidu_search_pro = appbuilder.RagWithBaiduSearchPro(model="Qianfan-Agent-Speed-8k") input = appbuilder.Message("9.11和9.8哪个大") result = rag_with_baidu_search_pro.run( diff --git a/appbuilder/core/agent.py b/appbuilder/core/agent.py index dd4a2d1d2..4c248d485 100644 --- a/appbuilder/core/agent.py +++ b/appbuilder/core/agent.py @@ -108,7 +108,7 @@ class AgentRuntime(BaseModel): class PlaygroundWithHistory(Component): def __init__(self): super().__init__() - self.query_rewrite = QueryRewrite(model="Qianfan-Appbuilder-Speed-8k") + self.query_rewrite = QueryRewrite(model="Qianfan-Agent-Speed-8k") self.play = Playground( prompt_template="{query}", model="eb-4" diff --git a/appbuilder/core/components/gbi/nl2sql/README.md b/appbuilder/core/components/gbi/nl2sql/README.md index 1ae778a87..050a9fca5 100644 --- a/appbuilder/core/components/gbi/nl2sql/README.md +++ b/appbuilder/core/components/gbi/nl2sql/README.md @@ -59,7 +59,7 @@ print(f"llm result: {nl2sql_result_message.content.llm_result}") ## 参数说明 ### 初始化参数 -- model_name: 支持的模型名字 ERNIE-Bot 4.0, ERNIE-Bot, ERNIE-Bot-turbo, Qianfan-Appbuilder-Speed-8k +- model_name: 支持的模型名字 ERNIE-Bot 4.0, ERNIE-Bot, ERNIE-Bot-turbo, Qianfan-Agent-Speed-8k - table_schemas: 表的 schema,例如: ``` diff --git a/appbuilder/core/components/gbi/nl2sql/component.py b/appbuilder/core/components/gbi/nl2sql/component.py index 2b6f9b7fa..9d3d6c709 100644 --- a/appbuilder/core/components/gbi/nl2sql/component.py +++ b/appbuilder/core/components/gbi/nl2sql/component.py @@ -38,7 +38,7 @@ def __init__(self, model_name: str, table_schemas: List[str], knowledge: Dict = 创建 gbi nl2sql 对象 Args: - model_name: 支持的模型名字 ERNIE-Bot 4.0, ERNIE-Bot, ERNIE-Bot-turbo, Qianfan-Appbuilder-Speed-8k + model_name: 支持的模型名字 ERNIE-Bot 4.0, ERNIE-Bot, ERNIE-Bot-turbo, Qianfan-Agent-Speed-8k table_schemas: 表的 schema 列表,例如: ``` CREATE TABLE `mytable` ( `d_year` COMMENT '年度,2019,2020..2022..', diff --git a/appbuilder/core/components/gbi/select_table/README.md b/appbuilder/core/components/gbi/select_table/README.md index bc7ce9176..4d173b792 100644 --- a/appbuilder/core/components/gbi/select_table/README.md +++ b/appbuilder/core/components/gbi/select_table/README.md @@ -51,7 +51,7 @@ print(f"选的表是: {select_table_result_message.content}") ## 参数说明 ### 初始化参数 -- model_name: 支持的模型名字 ERNIE-Bot 4.0, ERNIE-Bot, ERNIE-Bot-turbo, Qianfan-Appbuilder-Speed-8k +- model_name: 支持的模型名字 ERNIE-Bot 4.0, ERNIE-Bot, ERNIE-Bot-turbo, Qianfan-Agent-Speed-8k - table_descriptions: 表的描述是个字典,key: 是表的名字, value: 是表的描述,例如: ``` diff --git a/appbuilder/core/components/gbi/select_table/component.py b/appbuilder/core/components/gbi/select_table/component.py index f0d7511d8..ceb4cdca5 100644 --- a/appbuilder/core/components/gbi/select_table/component.py +++ b/appbuilder/core/components/gbi/select_table/component.py @@ -36,7 +36,7 @@ def __init__(self, model_name: str, table_descriptions: Dict[str, str], 创建 GBI 选表对象 Args: - model_name: 支持的模型名字 ERNIE-Bot 4.0, ERNIE-Bot, ERNIE-Bot-turbo, Qianfan-Appbuilder-Speed-8k + model_name: 支持的模型名字 ERNIE-Bot 4.0, ERNIE-Bot, ERNIE-Bot-turbo, Qianfan-Agent-Speed-8k table_descriptions: 表的描述是个字典,key: 是表的名字, value: 是表的描述,例如: { "超市营收明细表": "超市营收明细表,包含超市各种信息等", diff --git a/appbuilder/core/components/llms/dialog_summary/README.md b/appbuilder/core/components/llms/dialog_summary/README.md index 05a9eb786..2ef9b0665 100644 --- a/appbuilder/core/components/llms/dialog_summary/README.md +++ b/appbuilder/core/components/llms/dialog_summary/README.md @@ -23,7 +23,7 @@ import os # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" -dialog_summary = appbuilder.DialogSummary("Qianfan-Appbuilder-Speed-8k") +dialog_summary = appbuilder.DialogSummary("Qianfan-Agent-Speed-8k") text = "用户:喂我想查一下我的话费\n坐席:好的女士您话费余的话还有87.49元钱\n用户:好的知道了谢谢\n坐席:嗯不客气祝您生活愉快再见" answer = dialog_summary(appbuilder.Message(text)) print(answer) diff --git a/appbuilder/core/components/llms/dialog_summary/component.py b/appbuilder/core/components/llms/dialog_summary/component.py index 9da11d2d9..6b5b62d8c 100644 --- a/appbuilder/core/components/llms/dialog_summary/component.py +++ b/appbuilder/core/components/llms/dialog_summary/component.py @@ -32,7 +32,7 @@ class DialogSummary(CompletionBaseComponent): # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 os.environ["APPBUILDER_TOKEN"] = '...' - dialog_summary = appbuilder.DialogSummary("Qianfan-Appbuilder-Speed-8k") + dialog_summary = appbuilder.DialogSummary("Qianfan-Agent-Speed-8k") text = "用户:喂我想查一下我的话费\n坐席:好的女士您话费余的话还有87.49元钱\n用户:好的知道了谢谢\n坐席:嗯不客气祝您生活愉快再见" answer = dialog_summary(appbuilder.Message(text)) print(answer) diff --git a/appbuilder/core/components/llms/hallucination_detection/README.md b/appbuilder/core/components/llms/hallucination_detection/README.md index 66bf8b206..528cb6812 100644 --- a/appbuilder/core/components/llms/hallucination_detection/README.md +++ b/appbuilder/core/components/llms/hallucination_detection/README.md @@ -37,8 +37,8 @@ context = \ 很多怕胖的女生看到猪皮就怕怕,但其实猪皮含有大量胶原蛋白,营养价值很高呢!这里红通通的猪皮还经过韩国秘制酱汁处理过,会有一点点辣味。烤猪皮的时候也需特别注意火侯,这样吃起来才会有外脆内Q的口感!''' answer = '澳门新麻蒲烤肉店并不是每天开门。' -#! 该组件推荐使用Qianfan-Appbuilder-Speed-8k模型。 -hallucination_detection = appbuilder.HallucinationDetection('Qianfan-Appbuilder-Speed-8k') +#! 该组件推荐使用Qianfan-Agent-Speed-8k模型。 +hallucination_detection = appbuilder.HallucinationDetection('Qianfan-Agent-Speed-8k') inputs = {'query': query, 'context': context, 'answer': answer} msg = appbuilder.Message(inputs) result = hallucination_detection.run(msg) @@ -58,7 +58,7 @@ os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" ### 初始化参数 | 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | | ------- | ------- | -------- | -------- | -------- | -| `model` | str | 是 | 模型名称,用于指定要使用的千帆模型。推荐使用Qianfan-Appbuilder-Speed-8k模型。 | Qianfan-Appbuilder-Speed-8k | +| `model` | str | 是 | 模型名称,用于指定要使用的千帆模型。推荐使用Qianfan-Agent-Speed-8k模型。 | Qianfan-Agent-Speed-8k | | `secret_key` | str | 否 | 用户鉴权token,默认从环境变量中获取: `os.getenv("APPBUILDER_TOKEN", "")` | bce-v3/XXX | | `gateway` | str | 否 | 后端网关服务地址,默认从环境变量中获取: `os.getenv("GATEWAY_URL", "")` | https://appbuilder.baidu.com | | `lazy_certification` | bool | 否 | 延迟认证,为True时在第一次运行时认证。默认为False。 | False | diff --git a/appbuilder/core/components/llms/hallucination_detection/component.py b/appbuilder/core/components/llms/hallucination_detection/component.py index 48fc83dfd..2757a61f6 100644 --- a/appbuilder/core/components/llms/hallucination_detection/component.py +++ b/appbuilder/core/components/llms/hallucination_detection/component.py @@ -26,7 +26,7 @@ class HallucinationDetection(CompletionBaseComponent): """ 幻觉检测。输入,判断answer中是否存在幻觉。 - *注:该组件推荐使用Qianfan-Appbuilder-Speed-8k模型。* + *注:该组件推荐使用Qianfan-Agent-Speed-8k模型。* Examples: @@ -103,7 +103,7 @@ def __init__( """初始化幻觉检测组件。 Args: - model (str|None): 模型名称,用于指定要使用的千帆模型。推荐使用Qianfan-Appbuilder-Speed-8k模型。 + model (str|None): 模型名称,用于指定要使用的千帆模型。推荐使用Qianfan-Agent-Speed-8k模型。 secret_key (str, 可选): 用户鉴权token, 默认从环境变量中获取: os.getenv("APPBUILDER_TOKEN", ""). gateway (str, 可选): 后端网关服务地址,默认从环境变量中获取: os.getenv("GATEWAY_URL", "") lazy_certification (bool, 可选): 延迟认证,为True时在第一次运行时认证. Defaults to False. diff --git a/appbuilder/core/components/llms/is_complex_query/README.md b/appbuilder/core/components/llms/is_complex_query/README.md index b5f70f0f7..a3bab9473 100644 --- a/appbuilder/core/components/llms/is_complex_query/README.md +++ b/appbuilder/core/components/llms/is_complex_query/README.md @@ -21,7 +21,7 @@ import appbuilder # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 os.environ["APPBUILDER_TOKEN"] = "..." -is_complex_query = appbuilder.IsComplexQuery(model="Qianfan-Appbuilder-Speed-8k") +is_complex_query = appbuilder.IsComplexQuery(model="Qianfan-Agent-Speed-8k") msg = "吸塑包装盒在工业化生产和物流运输中分别有什么重要性?" msg = appbuilder.Message(msg) @@ -42,7 +42,7 @@ os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" ### 初始化参数 |参数名称 |参数类型 |是否必须 |描述 |示例值| |--------|--------|--------|----|------| -|model |str |是 |模型名称,用于指定要使用的千帆模型|Qianfan-Appbuilder-Speed-8k| +|model |str |是 |模型名称,用于指定要使用的千帆模型|Qianfan-Agent-Speed-8k| ### 调用参数 |参数名称 |参数类型 |是否必须 |描述 |示例值| diff --git a/appbuilder/core/components/llms/is_complex_query/component.py b/appbuilder/core/components/llms/is_complex_query/component.py index 662d4f01a..f0191b7d8 100644 --- a/appbuilder/core/components/llms/is_complex_query/component.py +++ b/appbuilder/core/components/llms/is_complex_query/component.py @@ -38,7 +38,7 @@ class IsComplexQuery(CompletionBaseComponent): # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 os.environ["APPBUILDER_TOKEN"] = "..." - is_complex_query = appbuilder.IsComplexQuery(model="Qianfan-Appbuilder-Speed-8k") + is_complex_query = appbuilder.IsComplexQuery(model="Qianfan-Agent-Speed-8k") msg = "吸塑包装盒在工业化生产和物流运输中分别有什么重要性?" msg = appbuilder.Message(msg) diff --git a/appbuilder/core/components/llms/mrc/README.md b/appbuilder/core/components/llms/mrc/README.md index ff1ad9fd0..eaf152beb 100644 --- a/appbuilder/core/components/llms/mrc/README.md +++ b/appbuilder/core/components/llms/mrc/README.md @@ -9,7 +9,7 @@ MRC(阅读理解问答模块)是一项先进的自然语言处理功能, ### 特色优势 我们的MRC模块,基于百度自研的先进语言模型文新一言,提供了一系列强大的阅读理解问答功能。在保持文本理解和问题回答的高精度的同时, 我们特别强调了答案的质量和交互体验。以下是我们MRC模块的几个主要功能特色: - - 1.多版本模型支持:我们的MRC模块包括不同版本的文新一言大模型,Erniebot 4.0、Qianfan-Appbuilder-Speed-8k等,每个版本都针对特定的应用场景进行了优化。 用户可以根据自己的需求选择最适合的模型版本,以获得最佳的性能。 + - 1.多版本模型支持:我们的MRC模块包括不同版本的文新一言大模型,Erniebot 4.0、Qianfan-Agent-Speed-8k等,每个版本都针对特定的应用场景进行了优化。 用户可以根据自己的需求选择最适合的模型版本,以获得最佳的性能。 - 2.答案格式的多样性: - 拒答功能:当问题超出模型知识范围或不具体时,模型可以选择不回答,避免提供误导性信息。 - 澄清功能:对于模棱两可或含糊的问题,模型可以请求更多信息或对问题进行澄清,以确保答案的准确性。 @@ -41,7 +41,7 @@ import os os.environ["APPBUILDER_TOKEN"] = "..." # 创建MRC对象 -mrc_component = appbuilder.MRC(model="Qianfan-Appbuilder-Speed-8k") +mrc_component = appbuilder.MRC(model="Qianfan-Agent-Speed-8k") # 初始化参数 msg = "残疾人怎么办相关证件" @@ -119,7 +119,7 @@ import os os.environ["APPBUILDER_TOKEN"] = '...' # 创建MRC对象 -mrc_component = appbuilder.MRC(model="Qianfan-Appbuilder-Speed-8k") +mrc_component = appbuilder.MRC(model="Qianfan-Agent-Speed-8k") # 初始化参数 msg = "残疾人怎么办相关证件" diff --git a/appbuilder/core/components/llms/nl2pandas/README.md b/appbuilder/core/components/llms/nl2pandas/README.md index 81f1210c8..a6828200f 100644 --- a/appbuilder/core/components/llms/nl2pandas/README.md +++ b/appbuilder/core/components/llms/nl2pandas/README.md @@ -30,7 +30,7 @@ query = "海淀区有哪些学校" query = appbuilder.Message(query) #定义并运行Nl2pandas实例,得到结果 -nl2pandas = appbuilder.Nl2pandasComponent(model="Qianfan-Appbuilder-Speed-8k") +nl2pandas = appbuilder.Nl2pandasComponent(model="Qianfan-Agent-Speed-8k") answer = nl2pandas(query, table_info = table_info) ``` diff --git a/appbuilder/core/components/llms/nl2pandas/component.py b/appbuilder/core/components/llms/nl2pandas/component.py index eb13e5bcf..95fae2134 100644 --- a/appbuilder/core/components/llms/nl2pandas/component.py +++ b/appbuilder/core/components/llms/nl2pandas/component.py @@ -38,7 +38,7 @@ class Nl2pandasComponent(CompletionBaseComponent): query = "海淀区有哪些学校" query = appbuilder.Message(query) - nl2pandas = appbuilder.Nl2pandasComponent(model="Qianfan-Appbuilder-Speed-8k") + nl2pandas = appbuilder.Nl2pandasComponent(model="Qianfan-Agent-Speed-8k") answer = nl2pandas(query, table_info = table_info) """ name = "nl2pandas" diff --git a/appbuilder/core/components/llms/oral_query_generation/README.md b/appbuilder/core/components/llms/oral_query_generation/README.md index 4e700d3e8..4a796b11b 100644 --- a/appbuilder/core/components/llms/oral_query_generation/README.md +++ b/appbuilder/core/components/llms/oral_query_generation/README.md @@ -28,8 +28,8 @@ text = ('文档标题:在OPPO Reno5上使用视频超级防抖\n' '防抖后手机屏幕将出现超级防抖Pro开关,点击即可开启或关闭。 除此之外,前置视频同样加持防抖算法,边走边拍也能稳定聚焦脸部' ',实时视频分享您的生活。') -#! 该组件推荐使用Qianfan-Appbuilder-Speed-8k模型。 -oral_query_generation = appbuilder.OralQueryGeneration(model='Qianfan-Appbuilder-Speed-8k') +#! 该组件推荐使用Qianfan-Agent-Speed-8k模型。 +oral_query_generation = appbuilder.OralQueryGeneration(model='Qianfan-Agent-Speed-8k') result = oral_query_generation(appbuilder.Message(text), query_type='全部', output_format='str') print(result) @@ -47,7 +47,7 @@ os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" | 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | | ------- | ------- | -------- | -------- | -------- | -| `model` | str | 是 | 模型名称,用于指定要使用的千帆模型。推荐使用Qianfan-Appbuilder-Speed-8k模型。 | Qianfan-Appbuilder-Speed-8k | +| `model` | str | 是 | 模型名称,用于指定要使用的千帆模型。推荐使用Qianfan-Agent-Speed-8k模型。 | Qianfan-Agent-Speed-8k | | `secret_key` | str | 否 | 用户鉴权token,默认从环境变量中获取: `os.getenv("APPBUILDER_TOKEN", "")` | bce-v3/XXX | | `gateway` | str | 否 | 后端网关服务地址,默认从环境变量中获取: `os.getenv("GATEWAY_URL", "")` | https://appbuilder.baidu.com | | `lazy_certification` | bool | 否 | 延迟认证,为True时在第一次运行时认证。默认为False。 | False | diff --git a/appbuilder/core/components/llms/oral_query_generation/component.py b/appbuilder/core/components/llms/oral_query_generation/component.py index 5f2a28aa2..bce15e8e5 100644 --- a/appbuilder/core/components/llms/oral_query_generation/component.py +++ b/appbuilder/core/components/llms/oral_query_generation/component.py @@ -27,7 +27,7 @@ class OralQueryGeneration(CompletionBaseComponent): r""" 口语化Query生成,可用于问答场景下对文档增强索引。 - *注:该组件推荐使用Qianfan-Appbuilder-Speed-8k模型。* + *注:该组件推荐使用Qianfan-Agent-Speed-8k模型。* Examples: @@ -43,7 +43,7 @@ class OralQueryGeneration(CompletionBaseComponent): '防抖 开启路径:打开「相机 > 视频 > 点击屏幕上方的“超级防抖”标识」 后置视频同时支持超级防抖和超级防抖Pro功能,开启超级' '防抖后手机屏幕将出现超级防抖Pro开关,点击即可开启或关闭。 除此之外,前置视频同样加持防抖算法,边走边拍也能稳定聚焦脸部' ',实时视频分享您的生活。') - oral_query_generation = appbuilder.OralQueryGeneration(model='Qianfan-Appbuilder-Speed-8k') + oral_query_generation = appbuilder.OralQueryGeneration(model='Qianfan-Agent-Speed-8k') answer = oral_query_generation(appbuilder.Message(text), query_type='全部', output_format='str') print(answer.content) """ @@ -88,7 +88,7 @@ def __init__( """初始化口语化Query生成模型。 Args: - model (str|None): 模型名称,用于指定要使用的千帆模型。推荐使用Qianfan-Appbuilder-Speed-8k模型。 + model (str|None): 模型名称,用于指定要使用的千帆模型。推荐使用Qianfan-Agent-Speed-8k模型。 secret_key (str, 可选): 用户鉴权token, 默认从环境变量中获取: os.getenv("APPBUILDER_TOKEN", ""). gateway (str, 可选): 后端网关服务地址,默认从环境变量中获取: os.getenv("GATEWAY_URL", "") lazy_certification (bool, 可选): 延迟认证,为True时在第一次运行时认证. Defaults to False. diff --git a/appbuilder/core/components/llms/playground/README.md b/appbuilder/core/components/llms/playground/README.md index 38ac161c8..73c598591 100644 --- a/appbuilder/core/components/llms/playground/README.md +++ b/appbuilder/core/components/llms/playground/README.md @@ -26,7 +26,7 @@ os.environ["APPBUILDER_TOKEN"] = "..." play = appbuilder.Playground( prompt_template="你好,{name},我是{bot_name},{bot_name}是一个{bot_type},我可以{bot_function},你可以问我{bot_question}。", - model="Qianfan-Appbuilder-Speed-8k" + model="Qianfan-Agent-Speed-8k" ) play(appbuilder.Message({"name": "小明", "bot_name": "小红", "bot_type": "聊天机器人", "bot_function": "聊天", "bot_question": "你好吗?"}), stream=False) ``` diff --git a/appbuilder/core/components/llms/playground/component.py b/appbuilder/core/components/llms/playground/component.py index 28020d2cf..be0344d72 100644 --- a/appbuilder/core/components/llms/playground/component.py +++ b/appbuilder/core/components/llms/playground/component.py @@ -31,7 +31,7 @@ class Playground(CompletionBaseComponent): import appbuilder os.environ["APPBUILDER_TOKEN"] = "..." - play = appbuilder.Playground(prompt_template="你好,{name},我是{bot_name},{bot_name}是一个{bot_type},我可以{bot_function},你可以问我{bot_question}。", model="Qianfan-Appbuilder-Speed-8k") + play = appbuilder.Playground(prompt_template="你好,{name},我是{bot_name},{bot_name}是一个{bot_type},我可以{bot_function},你可以问我{bot_question}。", model="Qianfan-Agent-Speed-8k") play(appbuilder.Message({"name": "小明", "bot_name": "小红", "bot_type": "聊天机器人", "bot_function": "聊天", "bot_question": "你好吗?"}), stream=False) """ diff --git a/appbuilder/core/components/llms/qa_pair_mining/README.md b/appbuilder/core/components/llms/qa_pair_mining/README.md index 5dc650605..8fcfa6e5e 100644 --- a/appbuilder/core/components/llms/qa_pair_mining/README.md +++ b/appbuilder/core/components/llms/qa_pair_mining/README.md @@ -23,7 +23,7 @@ import appbuilder # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 os.environ["APPBUILDER_TOKEN"] = "..." -qa_mining = appbuilder.QAPairMining(model="Qianfan-Appbuilder-Speed-8k") +qa_mining = appbuilder.QAPairMining(model="Qianfan-Agent-Speed-8k") # 输入文本(对此文本挖掘问答对) msg = '2017年,工商银行根据外部宏观环境变化,及时调整业务策略,优化资产负债结构,' + \ '保持存贷款业务协调发展,提升资产负债配置效率。' + \ @@ -110,7 +110,7 @@ split_result = splitter(parse_result) # 每个段落抽取问答对,并返回结果 for doc_segment in split_result.content["paragraphs"]: - qa_mining = QAPairMining(model="Qianfan-Appbuilder-Speed-8k") + qa_mining = QAPairMining(model="Qianfan-Agent-Speed-8k") text = doc_segment.get("text", "") if text == "": logger.error("Text is null. break") diff --git a/appbuilder/core/components/llms/qa_pair_mining/component.py b/appbuilder/core/components/llms/qa_pair_mining/component.py index 495cae874..d8acb07c5 100644 --- a/appbuilder/core/components/llms/qa_pair_mining/component.py +++ b/appbuilder/core/components/llms/qa_pair_mining/component.py @@ -33,7 +33,7 @@ class QAPairMining(CompletionBaseComponent): os.environ["APPBUILDER_TOKEN"] = "..." - qa_mining = appbuilder.QAPairMining(model="Qianfan-Appbuilder-Speed-8k") + qa_mining = appbuilder.QAPairMining(model="Qianfan-Agent-Speed-8k") # 输入文本(对此文本挖掘问答对) msg = '2017年,工商银行根据外部宏观环境变化,及时调整业务策略,优化资产负债结构,' + \ '保持存贷款业务协调发展,提升资产负债配置效率。' + \ diff --git a/appbuilder/core/components/llms/query_decomposition/README.md b/appbuilder/core/components/llms/query_decomposition/README.md index bfd249c49..d1b7f06bc 100644 --- a/appbuilder/core/components/llms/query_decomposition/README.md +++ b/appbuilder/core/components/llms/query_decomposition/README.md @@ -21,7 +21,7 @@ import appbuilder # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 os.environ["APPBUILDER_TOKEN"] = "..." -query_decomposition = appbuilder.QueryDecomposition(model="Qianfan-Appbuilder-Speed-8k") +query_decomposition = appbuilder.QueryDecomposition(model="Qianfan-Agent-Speed-8k") msg = "吸塑包装盒在工业化生产和物流运输中分别有什么重要性?" msg = appbuilder.Message(msg) @@ -42,7 +42,7 @@ os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" ### 初始化参数 |参数名称 |参数类型 |是否必须 |描述 |示例值| |--------|--------|--------|----|------| -|model |str |是 |模型名称,用于指定要使用的千帆模型|Qianfan-Appbuilder-Speed-8k| +|model |str |是 |模型名称,用于指定要使用的千帆模型|Qianfan-Agent-Speed-8k| ### 调用参数 |参数名称 |参数类型 |是否必须 |描述 |示例值| diff --git a/appbuilder/core/components/llms/query_decomposition/component.py b/appbuilder/core/components/llms/query_decomposition/component.py index 46f15196e..067215b2b 100644 --- a/appbuilder/core/components/llms/query_decomposition/component.py +++ b/appbuilder/core/components/llms/query_decomposition/component.py @@ -33,7 +33,7 @@ class QueryDecomposition(CompletionBaseComponent): # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 os.environ["APPBUILDER_TOKEN"] = "..." - query_decomposition = appbuilder.QueryDecomposition(model="Qianfan-Appbuilder-Speed-8k") + query_decomposition = appbuilder.QueryDecomposition(model="Qianfan-Agent-Speed-8k") msg = "吸塑包装盒在工业化生产和物流运输中分别有什么重要性?" msg = appbuilder.Message(msg) diff --git a/appbuilder/core/components/llms/query_rewrite/README.md b/appbuilder/core/components/llms/query_rewrite/README.md index 608e44514..ca568cf03 100644 --- a/appbuilder/core/components/llms/query_rewrite/README.md +++ b/appbuilder/core/components/llms/query_rewrite/README.md @@ -24,7 +24,7 @@ import appbuilder os.environ["APPBUILDER_TOKEN"] = '...' # 初始化并使用 QueryRewrite 组件 -query_rewrite = appbuilder.QueryRewrite(model="Qianfan-Appbuilder-Speed-8k") +query_rewrite = appbuilder.QueryRewrite(model="Qianfan-Agent-Speed-8k") answer = query_rewrite(appbuilder.Message(['我应该怎么办理护照?', '您可以查询官网或人工咨询', '我需要准备哪些材料?', '身份证、免冠照片一张以及填写完整的《中国公民因私出国(境)申请表》', '在哪里办']), rewrite_type="带机器人回复") print(answer) ``` diff --git a/appbuilder/core/components/llms/query_rewrite/component.py b/appbuilder/core/components/llms/query_rewrite/component.py index 48e727936..964a269bb 100644 --- a/appbuilder/core/components/llms/query_rewrite/component.py +++ b/appbuilder/core/components/llms/query_rewrite/component.py @@ -31,7 +31,7 @@ class QueryRewrite(CompletionBaseComponent): import appbuilder os.environ["APPBUILDER_TOKEN"] = '...' - query_rewrite = appbuilder.QueryRewrite(model="Qianfan-Appbuilder-Speed-8k") + query_rewrite = appbuilder.QueryRewrite(model="Qianfan-Agent-Speed-8k") answer = query_rewrite(appbuilder.Message(['我应该怎么办理护照?', '您可以查询官网或人工咨询', '我需要准备哪些材料?', diff --git a/appbuilder/core/components/llms/similar_question/README.md b/appbuilder/core/components/llms/similar_question/README.md index 4cd75df4d..ca5b11297 100644 --- a/appbuilder/core/components/llms/similar_question/README.md +++ b/appbuilder/core/components/llms/similar_question/README.md @@ -23,7 +23,7 @@ import appbuilder # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 os.environ["APPBUILDER_TOKEN"] = "..." -similar_question = appbuilder.SimilarQuestion(model="Qianfan-Appbuilder-Speed-8k") +similar_question = appbuilder.SimilarQuestion(model="Qianfan-Agent-Speed-8k") msg = "我想吃冰淇淋,哪里的冰淇淋比较好吃?" msg = appbuilder.Message(msg) diff --git a/appbuilder/core/components/llms/similar_question/component.py b/appbuilder/core/components/llms/similar_question/component.py index e95dbe3f0..39f79262f 100644 --- a/appbuilder/core/components/llms/similar_question/component.py +++ b/appbuilder/core/components/llms/similar_question/component.py @@ -35,7 +35,7 @@ class SimilarQuestion(CompletionBaseComponent): os.environ["APPBUILDER_TOKEN"] = "..." - qa_mining = appbuilder.SimilarQuestion(model="Qianfan-Appbuilder-Speed-8k") + qa_mining = appbuilder.SimilarQuestion(model="Qianfan-Agent-Speed-8k") msg = "我想吃冰淇淋,哪里的冰淇淋比较好吃?" msg = appbuilder.Message(msg) diff --git a/appbuilder/core/components/llms/style_rewrite/README.md b/appbuilder/core/components/llms/style_rewrite/README.md index f79650110..f694e3762 100644 --- a/appbuilder/core/components/llms/style_rewrite/README.md +++ b/appbuilder/core/components/llms/style_rewrite/README.md @@ -33,7 +33,7 @@ import appbuilder os.environ["APPBUILDER_TOKEN"] = "..." # 初始化并使用 StyleRewrite 组件 -style_rewrite = appbuilder.StyleRewrite(model="Qianfan-Appbuilder-Speed-8k") +style_rewrite = appbuilder.StyleRewrite(model="Qianfan-Agent-Speed-8k") answer = style_rewrite(appbuilder.Message("文心大模型发布新版"), style="激励话术") ``` diff --git a/appbuilder/core/components/llms/style_rewrite/component.py b/appbuilder/core/components/llms/style_rewrite/component.py index c37dd6557..dd7bc60da 100644 --- a/appbuilder/core/components/llms/style_rewrite/component.py +++ b/appbuilder/core/components/llms/style_rewrite/component.py @@ -31,7 +31,7 @@ class StyleRewrite(CompletionBaseComponent): import appbuilder os.environ["APPBUILDER_TOKEN"] = '...' - style_rewrite = appbuilder.StyleRewrite(model="Qianfan-Appbuilder-Speed-8k") + style_rewrite = appbuilder.StyleRewrite(model="Qianfan-Agent-Speed-8k") answer = style_rewrite(appbuilder.Message("文心大模型发布新版本"), style="激励话术") """ diff --git a/appbuilder/core/components/llms/style_writing/README.md b/appbuilder/core/components/llms/style_writing/README.md index 40d8f140f..9ae429339 100644 --- a/appbuilder/core/components/llms/style_writing/README.md +++ b/appbuilder/core/components/llms/style_writing/README.md @@ -26,7 +26,7 @@ import os # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 os.environ["APPBUILDER_TOKEN"] = "..." -model = "Qianfan-Appbuilder-Speed-8k" +model = "Qianfan-Agent-Speed-8k" style_writing = appbuilder.StyleWriting(model) query = "帮我写一篇关于人体工学椅的文案" diff --git a/appbuilder/core/components/llms/style_writing/component.py b/appbuilder/core/components/llms/style_writing/component.py index 7379899dd..499946172 100644 --- a/appbuilder/core/components/llms/style_writing/component.py +++ b/appbuilder/core/components/llms/style_writing/component.py @@ -55,7 +55,7 @@ class StyleWriting(CompletionBaseComponent): # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 os.environ["APPBUILDER_TOKEN"] = '...' - style_writing = appbuilder.StyleWriting(model="Qianfan-Appbuilder-Speed-8k") + style_writing = appbuilder.StyleWriting(model="Qianfan-Agent-Speed-8k") answer = style_writing(appbuilder.Message("帮我写一篇关于人体工学椅的文案"), style_query="小红书", length=100) """ diff --git a/appbuilder/core/components/llms/tag_extraction/README.md b/appbuilder/core/components/llms/tag_extraction/README.md index e5ea6410a..313541a7f 100644 --- a/appbuilder/core/components/llms/tag_extraction/README.md +++ b/appbuilder/core/components/llms/tag_extraction/README.md @@ -31,7 +31,7 @@ import appbuilder # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 os.environ["APPBUILDER_TOKEN"] = '...' -tag_extraction = appbuilder.TagExtraction(model="Qianfan-Appbuilder-Speed-8k") +tag_extraction = appbuilder.TagExtraction(model="Qianfan-Agent-Speed-8k") result = tag_extraction(appbuilder.Message("从这段文本中抽取关键标签")) ``` diff --git a/appbuilder/core/components/llms/tag_extraction/component.py b/appbuilder/core/components/llms/tag_extraction/component.py index fd6d5c56f..69b7eca57 100644 --- a/appbuilder/core/components/llms/tag_extraction/component.py +++ b/appbuilder/core/components/llms/tag_extraction/component.py @@ -29,7 +29,7 @@ class TagExtraction(CompletionBaseComponent): # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 os.environ["APPBUILDER_TOKEN"] = '...' - tag_extraction = appbuilder.TagExtraction(model="Qianfan-Appbuilder-Speed-8k") + tag_extraction = appbuilder.TagExtraction(model="Qianfan-Agent-Speed-8k") answer = tag_extraction(appbuilder.Message("从这段文本中抽取关键标签")) """ diff --git a/appbuilder/core/components/rag_with_baidu_search/README.md b/appbuilder/core/components/rag_with_baidu_search/README.md index fc44a554b..8196d8886 100644 --- a/appbuilder/core/components/rag_with_baidu_search/README.md +++ b/appbuilder/core/components/rag_with_baidu_search/README.md @@ -38,7 +38,7 @@ import os os.environ["APPBUILDER_TOKEN"] = '...' # 创建rag_with_baidusearch对象 -rag_with_baidusearch_component = appbuilder.RAGWithBaiduSearch(model="Qianfan-Appbuilder-Speed-8k") +rag_with_baidusearch_component = appbuilder.RAGWithBaiduSearch(model="Qianfan-Agent-Speed-8k") # 运行rag_with_baidusearch基本组件 msg = appbuilder.Message("残疾人怎么办相关证件") @@ -111,7 +111,7 @@ os.environ["APPBUILDER_TOKEN"] = '...' # 创建rag_with_baidusearch对象, 并初始化人设指令 rag_with_baidusearch_component = appbuilder.RAGWithBaiduSearch( - model="Qianfan-Appbuilder-Speed-8k", + model="Qianfan-Agent-Speed-8k", instruction=appbuilder.Message("你是问答助手,在回答问题前需要加上: 很高兴为您解答")) diff --git a/appbuilder/core/components/rag_with_baidu_search_pro/README.md b/appbuilder/core/components/rag_with_baidu_search_pro/README.md index 58d74dd42..816a26c8b 100644 --- a/appbuilder/core/components/rag_with_baidu_search_pro/README.md +++ b/appbuilder/core/components/rag_with_baidu_search_pro/README.md @@ -26,7 +26,7 @@ import os os.environ["APPBUILDER_TOKEN"] = '...' # 创建rag_with_baidusearch_pro对象 -rag_with_baidu_search_pro = appbuilder.RagWithBaiduSearchPro(model="Qianfan-Appbuilder-Speed-8k") +rag_with_baidu_search_pro = appbuilder.RagWithBaiduSearchPro(model="Qianfan-Agent-Speed-8k") # 运行rag_with_baidusearch基本组件 msg = appbuilder.Message("残疾人怎么办相关证件") @@ -88,7 +88,7 @@ os.environ["APPBUILDER_TOKEN"] = '...' # 创建rag_with_baidusearch对象, 并初始化人设指令 rag_with_baidusearch_pro = appbuilder.RagWithBaiduSearchPro( - model="Qianfan-Appbuilder-Speed-8k", + model="Qianfan-Agent-Speed-8k", instruction=appbuilder.Message("你是问答助手,在回答问题前需要加上: 很高兴为您解答")) diff --git a/appbuilder/tests/test_appbuilder_components_trace.py b/appbuilder/tests/test_appbuilder_components_trace.py index 009a1eb8c..a33b4ef7a 100644 --- a/appbuilder/tests/test_appbuilder_components_trace.py +++ b/appbuilder/tests/test_appbuilder_components_trace.py @@ -55,7 +55,7 @@ def setUp(self): "%2Fa6c4d2ca8a3f0259f4cae8ae3fa98a9f75afde1a063eaec04847c99ab7d1e411" self.asr = appbuilder.ASR() self.play = appbuilder.Playground(prompt_template="你好,{name},我是{bot_name},{bot_name}是一个{bot_type},我可以{bot_function},你可以问我{bot_question}。", model='eb-4') - model_name = 'Qianfan-Appbuilder-Speed-8k' + model_name = 'Qianfan-Agent-Speed-8k' secret_key = os.getenv('SECRET_KEY', None) self.hallucination_detection = appbuilder.HallucinationDetection(model=model_name, secret_key=secret_key) diff --git a/appbuilder/tests/test_dialog_summary.py b/appbuilder/tests/test_dialog_summary.py index 40aa2cc98..2c5edfbf6 100644 --- a/appbuilder/tests/test_dialog_summary.py +++ b/appbuilder/tests/test_dialog_summary.py @@ -28,7 +28,7 @@ def setUp(self): Returns: 无返回值,方法中执行了环境变量的赋值操作。 """ - self.model_name = "Qianfan-Appbuilder-Speed-8k" + self.model_name = "Qianfan-Agent-Speed-8k" self.node = appbuilder.DialogSummary(model=self.model_name) def test_run_with_default_params(self): diff --git a/appbuilder/tests/test_hallucination_detection.py b/appbuilder/tests/test_hallucination_detection.py index 6a4e29565..a0a429f7e 100644 --- a/appbuilder/tests/test_hallucination_detection.py +++ b/appbuilder/tests/test_hallucination_detection.py @@ -51,7 +51,7 @@ def setUp(self): 无返回值,方法中执行了环境变量的赋值操作。 """ - self.model_name = 'Qianfan-Appbuilder-Speed-8k' + self.model_name = 'Qianfan-Agent-Speed-8k' secret_key = os.getenv('SECRET_KEY', None) self.hallucination_detection = appbuilder.HallucinationDetection(model=self.model_name, secret_key=secret_key) diff --git a/appbuilder/tests/test_is_complex_query.py b/appbuilder/tests/test_is_complex_query.py index 54c28f1ab..7ae9ee73f 100644 --- a/appbuilder/tests/test_is_complex_query.py +++ b/appbuilder/tests/test_is_complex_query.py @@ -29,7 +29,7 @@ def setUp(self): Returns: 无返回值,方法中执行了环境变量的赋值操作。 """ - self.model_name = "Qianfan-Appbuilder-Speed-8k" + self.model_name = "Qianfan-Agent-Speed-8k" self.node = appbuilder.IsComplexQuery(model=self.model_name) def test_run_with_default_params(self): diff --git a/appbuilder/tests/test_mrc.py b/appbuilder/tests/test_mrc.py index 2794d95f1..b156b642f 100644 --- a/appbuilder/tests/test_mrc.py +++ b/appbuilder/tests/test_mrc.py @@ -23,7 +23,7 @@ def setUp(self): return mrc class ''' # 设置环境变量和初始化TestMRCComponent实例 - self.model_name = "Qianfan-Appbuilder-Speed-8k" + self.model_name = "Qianfan-Agent-Speed-8k" self.mrc = appbuilder.MRC(model=self.model_name) def test_mrc_with_custom_context_list(self): diff --git a/appbuilder/tests/test_nl2pandas.py b/appbuilder/tests/test_nl2pandas.py index 2e4c293e2..814755c4a 100644 --- a/appbuilder/tests/test_nl2pandas.py +++ b/appbuilder/tests/test_nl2pandas.py @@ -25,7 +25,7 @@ def setUp(self): """ 设置环境变量及必要数据。 """ - self.model_name = "Qianfan-Appbuilder-Speed-8k" + self.model_name = "Qianfan-Agent-Speed-8k" self.node = appbuilder.Nl2pandasComponent(model=self.model_name) self.table_info = '''表格列信息如下: 学校名 : 清华附小 , 字符串类型,代表小学学校的名称 diff --git a/appbuilder/tests/test_oral_query_generation.py b/appbuilder/tests/test_oral_query_generation.py index 82deafe14..8d1ffefea 100644 --- a/appbuilder/tests/test_oral_query_generation.py +++ b/appbuilder/tests/test_oral_query_generation.py @@ -36,7 +36,7 @@ def setUp(self): 无返回值,方法中执行了环境变量的赋值操作。 """ - self.model_name = 'Qianfan-Appbuilder-Speed-8k' + self.model_name = 'Qianfan-Agent-Speed-8k' secret_key = os.getenv('SECRET_KEY', None) self.query_generation = appbuilder.OralQueryGeneration(model=self.model_name, secret_key=secret_key) diff --git a/appbuilder/tests/test_qa_pair_mining.py b/appbuilder/tests/test_qa_pair_mining.py index e3c54e926..1fb098cb8 100644 --- a/appbuilder/tests/test_qa_pair_mining.py +++ b/appbuilder/tests/test_qa_pair_mining.py @@ -31,7 +31,7 @@ def setUp(self): Returns: 无返回值,方法中执行了环境变量的赋值操作。 """ - self.model_name = "Qianfan-Appbuilder-Speed-8k" + self.model_name = "Qianfan-Agent-Speed-8k" self.node = appbuilder.QAPairMining(model=self.model_name) def test_run_with_default_params(self): diff --git a/appbuilder/tests/test_query_decomposition.py b/appbuilder/tests/test_query_decomposition.py index b83b41996..64c16f013 100644 --- a/appbuilder/tests/test_query_decomposition.py +++ b/appbuilder/tests/test_query_decomposition.py @@ -29,7 +29,7 @@ def setUp(self): Returns: 无返回值,方法中执行了环境变量的赋值操作。 """ - self.model_name = "Qianfan-Appbuilder-Speed-8k" + self.model_name = "Qianfan-Agent-Speed-8k" self.node = appbuilder.QueryDecomposition(model=self.model_name) def test_run_with_default_params(self): diff --git a/appbuilder/tests/test_query_rewrite.py b/appbuilder/tests/test_query_rewrite.py index c6e9165c9..a2fe94b32 100644 --- a/appbuilder/tests/test_query_rewrite.py +++ b/appbuilder/tests/test_query_rewrite.py @@ -32,7 +32,7 @@ def setUp(self): 无返回值。 """ # 设置环境变量和初始化TranslateComponent实例 - self.model_name = "Qianfan-Appbuilder-Speed-8k" + self.model_name = "Qianfan-Agent-Speed-8k" self.node = appbuilder.QueryRewrite(model=self.model_name) def test_run_with_default_params(self): @@ -54,7 +54,7 @@ def test_run_with_custom_params(self): def test_run_with_stream_and_temperature(self): """测试不同的 stream 和 temperature 参数值""" - node = appbuilder.QueryRewrite("Qianfan-Appbuilder-Speed-8k") + node = appbuilder.QueryRewrite("Qianfan-Agent-Speed-8k") query = ['我应该怎么办理护照?', '您可以查询官网或人工咨询', '我需要准备哪些材料?', '身份证、免冠照片一张以及填写完整的《中国公民因私出国(境)申请表》', '在哪里办'] msg = appbuilder.Message(query) answer = node(msg, rewrite_type="带机器人回复", stream=False, temperature=0.5) diff --git a/appbuilder/tests/test_rag_baidu_search.py b/appbuilder/tests/test_rag_baidu_search.py index a8ffe00eb..144cacb0e 100644 --- a/appbuilder/tests/test_rag_baidu_search.py +++ b/appbuilder/tests/test_rag_baidu_search.py @@ -11,7 +11,7 @@ def setUp(self): return rag_with_baidu_search class """ # 设置环境变量和初始化TestMRCComponent实例 - self.model_name = "Qianfan-Appbuilder-Speed-8k" + self.model_name = "Qianfan-Agent-Speed-8k" self.rag_with_baidu_search = appbuilder.RAGWithBaiduSearch(model=self.model_name) def test_rag_with_baidu_search(self): diff --git a/appbuilder/tests/test_rag_baidu_search_pro.py b/appbuilder/tests/test_rag_baidu_search_pro.py index b5cea10c8..804736743 100644 --- a/appbuilder/tests/test_rag_baidu_search_pro.py +++ b/appbuilder/tests/test_rag_baidu_search_pro.py @@ -9,6 +9,8 @@ import unittest import appbuilder +from appbuilder.core._exception import AppBuilderServerException + @unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") class TestRagBaiduSearch(unittest.TestCase): @@ -71,5 +73,25 @@ def test_rag_with_baidu_search_pro_stream(self): self.assertTrue(flag_content and flag_ref_content) + def test_rag_with_baidu_search_component_RAGWithBaiduSearch(self): + rwbs=appbuilder.RAGWithBaiduSearch(model='ERNIE-Bot 4.0') + + # test_get_search_input + text='text' + res_text=rwbs._get_search_input(text) + self.assertEqual(res_text, 'text') + text='UTF-8是一种变长字节表示的Unicode字符集编码方式,它可以使用1到4个字节来表示一个字符。' + res_text=rwbs._get_search_input(text) + self.assertEqual(res_text, 'UTF-8是一种变长字节表示的Unicode字符集编码方式,它可') + + # test run + message=appbuilder.Message() + message.content=""" + appbuilderappbuilderappbuilderappbuilderappbuilderappbuilderappbuilderappbuilder + appbuilderappbuilderappbuilderappbuilderappbuilderappbuilderappbuilderappbuilder + """ + with self.assertRaises(AppBuilderServerException): + rwbs.run(message=message) + if __name__ == '__main__': unittest.main() diff --git a/appbuilder/tests/test_rag_with_baidu_search_component.py b/appbuilder/tests/test_rag_with_baidu_search_component.py deleted file mode 100644 index d9bc7c17e..000000000 --- a/appbuilder/tests/test_rag_with_baidu_search_component.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) 2023 Baidu, Inc. 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 unittest -import os - -from appbuilder.core.components.rag_with_baidu_search.component import RAGWithBaiduSearch -from appbuilder.core.message import Message -from appbuilder.core._exception import * - -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") -class TestRagWithBaiduSearchComponent(unittest.TestCase): - def test_rag_with_baidu_search_component_RAGWithBaiduSearch(self): - rwbs=RAGWithBaiduSearch(model='ERNIE-Bot 4.0') - - # test_get_search_input - text='text' - res_text=rwbs._get_search_input(text) - self.assertEqual(res_text, 'text') - text='UTF-8是一种变长字节表示的Unicode字符集编码方式,它可以使用1到4个字节来表示一个字符。' - res_text=rwbs._get_search_input(text) - self.assertEqual(res_text, 'UTF-8是一种变长字节表示的Unicode字符集编码方式,它可') - - # test run - message=Message() - message.content='message' - res_response=rwbs.run(message=message) - assert message.content.startswith('message') - - message.content=""" - appbuilderappbuilderappbuilderappbuilderappbuilderappbuilderappbuilderappbuilder - appbuilderappbuilderappbuilderappbuilderappbuilderappbuilderappbuilderappbuilder - """ - with self.assertRaises(AppBuilderServerException): - rwbs.run(message=message) - -if __name__ == '__main__': - unittest.main() \ No newline at end of file diff --git a/appbuilder/tests/test_similar_question.py b/appbuilder/tests/test_similar_question.py index b47f877ec..c021f00ca 100644 --- a/appbuilder/tests/test_similar_question.py +++ b/appbuilder/tests/test_similar_question.py @@ -28,7 +28,7 @@ def setUp(self): Returns: 无返回值,方法中执行了环境变量的赋值操作。 """ - self.model_name = "Qianfan-Appbuilder-Speed-8k" + self.model_name = "Qianfan-Agent-Speed-8k" self.node = appbuilder.SimilarQuestion(model=self.model_name) def test_run_with_default_params(self): diff --git a/appbuilder/tests/test_style_rewrite.py b/appbuilder/tests/test_style_rewrite.py index 3087fe43c..33d1ad8a5 100644 --- a/appbuilder/tests/test_style_rewrite.py +++ b/appbuilder/tests/test_style_rewrite.py @@ -31,7 +31,7 @@ def setUp(self): Returns: 无返回值,方法中执行了环境变量的赋值操作。 """ - self.model_name = "Qianfan-Appbuilder-Speed-8k" + self.model_name = "Qianfan-Agent-Speed-8k" self.node = appbuilder.StyleRewrite(model=self.model_name) self.sc=StyleChoices.YINGXIAO diff --git a/appbuilder/tests/test_style_writing.py b/appbuilder/tests/test_style_writing.py index 836f15104..98e5b8417 100644 --- a/appbuilder/tests/test_style_writing.py +++ b/appbuilder/tests/test_style_writing.py @@ -32,7 +32,7 @@ def setUp(self): Returns: 无返回值,方法中执行了环境变量的赋值操作。 """ - self.model_name = "Qianfan-Appbuilder-Speed-8k" + self.model_name = "Qianfan-Agent-Speed-8k" self.node = appbuilder.StyleWriting(model=self.model_name) self.sqc=StyleQueryChoices.BILIBILI self.lc=LengthChoices.SHORT diff --git a/appbuilder/tests/test_tag_extraction.py b/appbuilder/tests/test_tag_extraction.py index 85fb24d41..0c6146d87 100644 --- a/appbuilder/tests/test_tag_extraction.py +++ b/appbuilder/tests/test_tag_extraction.py @@ -25,7 +25,7 @@ def setUp(self): Returns: 无返回值,方法中执行了环境变量的赋值操作。 """ - self.model_name = "Qianfan-Appbuilder-Speed-8k" + self.model_name = "Qianfan-Agent-Speed-8k" self.tag_extraction = appbuilder.TagExtraction(model=self.model_name) def test_run_with_default_params(self): diff --git a/appbuilder/utils/model_util.py b/appbuilder/utils/model_util.py index c77c27221..c2116e963 100644 --- a/appbuilder/utils/model_util.py +++ b/appbuilder/utils/model_util.py @@ -30,7 +30,7 @@ ("ERNIE-Bot-turbo", "eb-turbo"), ("EB-turbo-AppBuilder专用版", "eb-turbo-appbuilder"), ("EB-turbo-AppBuilder专用版", "ernie_speed_appbuilder"), - ("EB-turbo-AppBuilder专用版", "Qianfan-Appbuilder-Speed-8k"), + ("EB-turbo-AppBuilder专用版", "Qianfan-Agent-Speed-8k"), ] class RemoteModel(object): @@ -62,7 +62,7 @@ def get_remote_name_by_short_name(self, short_name: str) -> Optional[str]: """ # TODO(chengmo): 使用logging 替换 print,解决print多次的问题 if short_name == "eb-turbo-appbuilder": - print("Deprecate warning: model [eb-turbo-appbuilder] is deprecated, please use [Qianfan-Appbuilder-Speed-8k]") + print("Deprecate warning: model [eb-turbo-appbuilder] is deprecated, please use [Qianfan-Agent-Speed-8k]") if short_name in self.short_names: return self.remote_name diff --git a/cookbooks/components/agent_runtime.ipynb b/cookbooks/components/agent_runtime.ipynb index 6fb095f17..63ee56d78 100644 --- a/cookbooks/components/agent_runtime.ipynb +++ b/cookbooks/components/agent_runtime.ipynb @@ -426,7 +426,7 @@ "class PlaygroundWithHistory(Component):\n", " def __init__(self):\n", " super().__init__()\n", - " self.query_rewrite = QueryRewrite(model=\"Qianfan-Appbuilder-Speed-8k\")\n", + " self.query_rewrite = QueryRewrite(model=\"Qianfan-Agent-Speed-8k\")\n", " self.playground = Playground(\n", " prompt_template=\"{query}\",\n", " model=\"ERNIE-Bot\"\n", diff --git a/cookbooks/components/rag_with_baidusearch.ipynb b/cookbooks/components/rag_with_baidusearch.ipynb index 677ff07bf..477729bc1 100644 --- a/cookbooks/components/rag_with_baidusearch.ipynb +++ b/cookbooks/components/rag_with_baidusearch.ipynb @@ -82,8 +82,8 @@ "friendly = False # 友好度提升\n", "cite = True # 溯源\n", "\n", - "# 使用 Qianfan-Appbuilder-Speed-8kder 模型\n", - "component = appbuilder.RAGWithBaiduSearch(model=\"Qianfan-Appbuilder-Speed-8k\")\n", + "# 使用 Qianfan-Agent-Speed-8k 模型\n", + "component = appbuilder.RAGWithBaiduSearch(model=\"Qianfan-Agent-Speed-8k\")\n", "query = appbuilder.Message(\"海淀区的面积是多少\")\n", "\n", "answer = component.run(\n", diff --git a/cookbooks/components/text_generation.ipynb b/cookbooks/components/text_generation.ipynb index 65683fbc9..17b2319c5 100644 --- a/cookbooks/components/text_generation.ipynb +++ b/cookbooks/components/text_generation.ipynb @@ -101,7 +101,7 @@ "商品信息:\n", "'''\n", "# 创建商品信息生成组件\n", - "product_information_generation = Playground(prompt_template=prompt_template, model='Qianfan-Appbuilder-Speed-8k')\n", + "product_information_generation = Playground(prompt_template=prompt_template, model='Qianfan-Agent-Speed-8k')\n", "\n", "# 获取商品信息\n", "# 填充prompt_template参数的参数映射表,需要与prompt_template对应\n", @@ -152,7 +152,7 @@ "from appbuilder import QAPairMining\n", "\n", "# 初始化问答对生成组件\n", - "qa_pair_mining = QAPairMining(model='Qianfan-Appbuilder-Speed-8k')\n", + "qa_pair_mining = QAPairMining(model='Qianfan-Agent-Speed-8k')\n", "\n", "# 获取问答对\n", "response = qa_pair_mining(Message(product_information), stream=False, temperature=1e-10)\n", @@ -210,7 +210,7 @@ "'''\n", "\n", "# 初始化风格写作组件\n", - "style_writing = StyleWriting(model='Qianfan-Appbuilder-Speed-8k')\n", + "style_writing = StyleWriting(model='Qianfan-Agent-Speed-8k')\n", "\n", "# 获取小红书文案\n", "response = style_writing(Message(query), style_query='小红书', length=300)\n", @@ -260,7 +260,7 @@ "from appbuilder import TagExtraction\n", "\n", "# 初始化标签抽取组件\n", - "tag_extraction = TagExtraction(model='Qianfan-Appbuilder-Speed-8k')\n", + "tag_extraction = TagExtraction(model='Qianfan-Agent-Speed-8k')\n", "\n", "# 获取标签\n", "response = tag_extraction(Message(copywriting), stream=False, temperature=1e-10)\n", diff --git a/cookbooks/end2end_application/rag/rag.ipynb b/cookbooks/end2end_application/rag/rag.ipynb index 5a1d39c27..3eef0c479 100644 --- a/cookbooks/end2end_application/rag/rag.ipynb +++ b/cookbooks/end2end_application/rag/rag.ipynb @@ -171,7 +171,7 @@ "具备团队协作精神,能够与团队成员有效沟通,共同推进项目进展。'''\n", "\n", "# 标签抽取的组件\n", - "tagger = appbuilder.TagExtraction(model=\"Qianfan-Appbuilder-Speed-8k\")\n", + "tagger = appbuilder.TagExtraction(model=\"Qianfan-Agent-Speed-8k\")\n", "\n", "# 从JD抽取标签并打印\n", "tags = tagger(appbuilder.Message(job_desc))\n", @@ -234,7 +234,7 @@ "\n", "play = appbuilder.Playground(\n", " prompt_template=\"基于候选人姓名、职责描述和简历内容,概括一下{name}的推荐理由。\\n候选人姓名: {name}\\n职责描述: {JD}\\n简历内容: {resume}\\n推荐理由: \",\n", - " model=\"Qianfan-Appbuilder-Speed-8k\"\n", + " model=\"Qianfan-Agent-Speed-8k\"\n", ")\n", "\n", "resume_summary = play(appbuilder.Message({\"JD\": job_desc, \"name\": sorted_resumes[0][0], \"resume\": \"\\n\".join(list(resume_content[sorted_resumes[0][0]]))}))\n", diff --git a/docs/README_en.md b/docs/README_en.md index fc3b076e4..f423f800c 100644 --- a/docs/README_en.md +++ b/docs/README_en.md @@ -82,7 +82,7 @@ template_str = "你扮演{role}, 请回答我的问题。\n\n问题:{question} # 定义输入,调用playground组件 input = appbuilder.Message({"role": "java工程师", "question": "请简要回答java语言的内存回收机制是什么,要求100字以内"}) -playground = appbuilder.Playground(prompt_template=template_str, model="Qianfan-Appbuilder-Speed-8k") +playground = appbuilder.Playground(prompt_template=template_str, model="Qianfan-Agent-Speed-8k") # 以打字机的方式,流式展示大模型回答内容 output = playground(input, stream=True, temperature=1e-10) @@ -128,7 +128,7 @@ import os # 设置环境中的TOKEN,以下TOKEN为访问和QPS受限的试用TOKEN,正式使用请替换为您的个人TOKEN os.environ["APPBUILDER_TOKEN"] = "bce-v3/ALTAK-n5AYUIUJMarF7F7iFXVeK/1bf65eed7c8c7efef9b11388524fa1087f90ea58" -rag_with_baidu_search_pro = appbuilder.RagWithBaiduSearchPro(model="Qianfan-Appbuilder-Speed-8k") +rag_with_baidu_search_pro = appbuilder.RagWithBaiduSearchPro(model="Qianfan-Agent-Speed-8k") input = appbuilder.Message("9.11和9.8哪个大") result = rag_with_baidu_search_pro.run( diff --git a/docs/README_ja.md b/docs/README_ja.md index e8aa39aa4..23bc623a2 100644 --- a/docs/README_ja.md +++ b/docs/README_ja.md @@ -79,7 +79,7 @@ template_str = "あなたは{role}の役割を果たします。私の質問に # 入力を定義し、playgroundコンポーネントを呼び出します。 input = appbuilder.Message({"role": "Javaエンジニア", "question": "Java言語のメモリ回収メカニズムについて簡単に説明してください。100文字以内でお願いします。"}) -playground = appbuilder.Playground(prompt_template=template_str, model="Qianfan-Appbuilder-Speed-8k") +playground = appbuilder.Playground(prompt_template=template_str, model="Qianfan-Agent-Speed-8k") # タイプライターのように、ストリーム形式で大規模モデルの回答内容を表示します。 output = playground(input, stream=True, temperature=1e-10) @@ -124,7 +124,7 @@ import os # 環境変数にTOKENを設定します。以下のTOKENはアクセスとQPSが制限された試用TOKENです。正式な使用には個人のTOKENに置き換えてください。 os.environ["APPBUILDER_TOKEN"] = "bce-v3/ALTAK-n5AYUIUJMarF7F7iFXVeK/1bf65eed7c8c7efef9b11388524fa1087f90ea58" -rag_with_baidu_search_pro = appbuilder.RagWithBaiduSearchPro(model="Qianfan-Appbuilder-Speed-8k") +rag_with_baidu_search_pro = appbuilder.RagWithBaiduSearchPro(model="Qianfan-Agent-Speed-8k") input = appbuilder.Message("9.11と9.8のどちらが大きいですか?") result = rag_with_baidu_search_pro.run( diff --git a/docs/basic_module/README.md b/docs/basic_module/README.md index 0724d56f3..0c0cd811e 100644 --- a/docs/basic_module/README.md +++ b/docs/basic_module/README.md @@ -52,7 +52,7 @@ ERNIE-Bot 4.0, ERNIE-Bot, ERNIE-3.5-4K-0205, ERNIE-3.5-8K-0205, ERNIE-3.5-8K-122 | ERNIE-Bot | eb | | ERNIE-Bot-turbo | eb-turbo | | EB-turbo-AppBuilder专用版 | ernie_speed_appbuilder | -| Qianfan-Appbuilder-Speed-8k | ernie_speed_appbuilder | +| Qianfan-Agent-Speed-8k | ernie_speed_appbuilder | @@ -66,7 +66,7 @@ os.environ["APPBUILDER_TOKEN"] = "bce-v3/ALTAK-n5AYUIUJMarF7F7iFXVeK/1bf65eed7c8 # 空模版组件 template_str = "你扮演{role}, 请回答我的问题。\n\n问题:{question}。\n\n回答:" -playground = appbuilder.Playground(prompt_template=template_str, model="Qianfan-Appbuilder-Speed-8k") +playground = appbuilder.Playground(prompt_template=template_str, model="Qianfan-Agent-Speed-8k") # 定义输入,调用空模版组件 input = appbuilder.Message({"role": "java工程师", "question": "java语言的内存回收机制是什么"}) @@ -83,7 +83,7 @@ import os os.environ["APPBUILDER_TOKEN"] = "bce-v3/ALTAK-n5AYUIUJMarF7F7iFXVeK/1bf65eed7c8c7efef9b11388524fa1087f90ea58" # 相似问生成组件 -similar_q = appbuilder.SimilarQuestion(model="Qianfan-Appbuilder-Speed-8k") +similar_q = appbuilder.SimilarQuestion(model="Qianfan-Agent-Speed-8k") # 定义输入,调用相似问生成 input = appbuilder.Message("我想吃冰淇淋,哪里的冰淇淋比较好吃?") @@ -128,7 +128,7 @@ import appbuilder # 空模版组件 playground = appbuilder.Playground( prompt_template="{query}", - model="Qianfan-Appbuilder-Speed-8k" + model="Qianfan-Agent-Speed-8k" ) # 使用 AgentRuntime 来服务化playground组件 diff --git a/docs/basic_module/agentruntime.md b/docs/basic_module/agentruntime.md index d4a4e080c..fc18c4de8 100644 --- a/docs/basic_module/agentruntime.md +++ b/docs/basic_module/agentruntime.md @@ -120,7 +120,7 @@ os.environ["APPBUILDER_TOKEN"] = 'YOUR_APPBUILDER_TOKEN' class PlaygroundWithHistory(Component): def __init__(self): super().__init__() - self.query_rewrite = QueryRewrite(model="Qianfan-Appbuilder-Speed-8k") + self.query_rewrite = QueryRewrite(model="Qianfan-Agent-Speed-8k") self.play = Playground( prompt_template="{query}", model="eb-4" diff --git a/docs/develop_guide/README.md b/docs/develop_guide/README.md index 560f5596d..921d17c59 100644 --- a/docs/develop_guide/README.md +++ b/docs/develop_guide/README.md @@ -43,7 +43,7 @@ class SimilarQuestion(CompletionBaseComponent): os.environ["APPBUILDER_TOKEN"] = "..." - qa_mining = appbuilder.SimilarQuestion(model="Qianfan-Appbuilder-Speed-8k") + qa_mining = appbuilder.SimilarQuestion(model="Qianfan-Agent-Speed-8k") msg = "我想吃冰淇淋,哪里的冰淇淋比较好吃?" msg = appbuilder.Message(msg) diff --git a/docs/quick_start/README.md b/docs/quick_start/README.md index 8f56fde9b..f1a5f2ff5 100644 --- a/docs/quick_start/README.md +++ b/docs/quick_start/README.md @@ -42,7 +42,7 @@ ERNIE-Bot 4.0, ERNIE-Bot, ERNIE-3.5-4K-0205, ERNIE-3.5-8K-0205, ERNIE-3.5-8K-122 | ERNIE-Bot | eb | | ERNIE-Bot-turbo | eb-turbo | | EB-turbo-AppBuilder专用版 | ernie_speed_appbuilder | -| Qianfan-Appbuilder-Speed-8k | ernie_speed_appbuilder | +| Qianfan-Agent-Speed-8k | ernie_speed_appbuilder | @@ -56,7 +56,7 @@ os.environ["APPBUILDER_TOKEN"] = "bce-v3/ALTAK-n5AYUIUJMarF7F7iFXVeK/1bf65eed7c8 # 空模版组件 template_str = "你扮演{role}, 请回答我的问题。\n\n问题:{question}。\n\n回答:" -playground = appbuilder.Playground(prompt_template=template_str, model="Qianfan-Appbuilder-Speed-8k") +playground = appbuilder.Playground(prompt_template=template_str, model="Qianfan-Agent-Speed-8k") # 定义输入,调用空模版组件 input = appbuilder.Message({"role": "java工程师", "question": "java语言的内存回收机制是什么"}) @@ -73,7 +73,7 @@ import os os.environ["APPBUILDER_TOKEN"] = "bce-v3/ALTAK-n5AYUIUJMarF7F7iFXVeK/1bf65eed7c8c7efef9b11388524fa1087f90ea58" # 相似问生成组件 -similar_q = appbuilder.SimilarQuestion(model="Qianfan-Appbuilder-Speed-8k") +similar_q = appbuilder.SimilarQuestion(model="Qianfan-Agent-Speed-8k") # 定义输入,调用相似问生成 input = appbuilder.Message("我想吃冰淇淋,哪里的冰淇淋比较好吃?") @@ -118,7 +118,7 @@ import appbuilder # 空模版组件 playground = appbuilder.Playground( prompt_template="{query}", - model="Qianfan-Appbuilder-Speed-8k" + model="Qianfan-Agent-Speed-8k" ) # 使用 AgentRuntime 来服务化playground组件 diff --git a/docs/sphinx_md/appbuilder.core.components.llms.dialog_summary.md b/docs/sphinx_md/appbuilder.core.components.llms.dialog_summary.md index 79fc5f997..88c6b8db3 100644 --- a/docs/sphinx_md/appbuilder.core.components.llms.dialog_summary.md +++ b/docs/sphinx_md/appbuilder.core.components.llms.dialog_summary.md @@ -19,7 +19,7 @@ import os # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 os.environ["APPBUILDER_TOKEN"] = '...' -dialog_summary = appbuilder.DialogSummary("Qianfan-Appbuilder-Speed-8k") +dialog_summary = appbuilder.DialogSummary("Qianfan-Agent-Speed-8k") text = "用户:喂我想查一下我的话费\n坐席:好的女士您话费余的话还有87.49元钱\n用户:好的知道了谢谢\n坐席:嗯不客气祝您生活愉快再见" answer = dialog_summary(appbuilder.Message(text)) print(answer) diff --git a/docs/sphinx_md/appbuilder.core.components.llms.hallucination_detection.md b/docs/sphinx_md/appbuilder.core.components.llms.hallucination_detection.md index 712456eb7..d5400d62d 100644 --- a/docs/sphinx_md/appbuilder.core.components.llms.hallucination_detection.md +++ b/docs/sphinx_md/appbuilder.core.components.llms.hallucination_detection.md @@ -9,7 +9,7 @@ 基类:`CompletionBaseComponent` 幻觉检测。输入,判断answer中是否存在幻觉。 - *注:该组件推荐使用Qianfan-Appbuilder-Speed-8k模型。* + *注:该组件推荐使用Qianfan-Agent-Speed-8k模型。* Examples: diff --git a/docs/sphinx_md/appbuilder.core.components.llms.is_complex_query.md b/docs/sphinx_md/appbuilder.core.components.llms.is_complex_query.md index ed83b24b5..ede4feb93 100644 --- a/docs/sphinx_md/appbuilder.core.components.llms.is_complex_query.md +++ b/docs/sphinx_md/appbuilder.core.components.llms.is_complex_query.md @@ -21,7 +21,7 @@ import appbuilder # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 os.environ["APPBUILDER_TOKEN"] = "..." -is_complex_query = appbuilder.IsComplexQuery(model="Qianfan-Appbuilder-Speed-8k") +is_complex_query = appbuilder.IsComplexQuery(model="Qianfan-Agent-Speed-8k") msg = "吸塑包装盒在工业化生产和物流运输中分别有什么重要性?" msg = appbuilder.Message(msg) diff --git a/docs/sphinx_md/appbuilder.core.components.llms.nl2pandas.md b/docs/sphinx_md/appbuilder.core.components.llms.nl2pandas.md index 10972ad85..d9bf1d518 100644 --- a/docs/sphinx_md/appbuilder.core.components.llms.nl2pandas.md +++ b/docs/sphinx_md/appbuilder.core.components.llms.nl2pandas.md @@ -27,7 +27,7 @@ table_info = '''表格列信息如下: query = "海淀区有哪些学校" query = appbuilder.Message(query) -nl2pandas = appbuilder.Nl2pandasComponent(model="Qianfan-Appbuilder-Speed-8k") +nl2pandas = appbuilder.Nl2pandasComponent(model="Qianfan-Agent-Speed-8k") answer = nl2pandas(query, table_info = table_info) ``` diff --git a/docs/sphinx_md/appbuilder.core.components.llms.oral_query_generation.md b/docs/sphinx_md/appbuilder.core.components.llms.oral_query_generation.md index b9a13d412..02a51f76b 100644 --- a/docs/sphinx_md/appbuilder.core.components.llms.oral_query_generation.md +++ b/docs/sphinx_md/appbuilder.core.components.llms.oral_query_generation.md @@ -9,7 +9,7 @@ 基类:`CompletionBaseComponent` 口语化Query生成,可用于问答场景下对文档增强索引。 - *注:该组件推荐使用Qianfan-Appbuilder-Speed-8k模型。* + *注:该组件推荐使用Qianfan-Agent-Speed-8k模型。* Examples: @@ -24,7 +24,7 @@ text = ('文档标题:在OPPO Reno5上使用视频超级防抖\n' '防抖 开启路径:打开「相机 > 视频 > 点击屏幕上方的“超级防抖”标识」 后置视频同时支持超级防抖和超级防抖Pro功能,开启超级' '防抖后手机屏幕将出现超级防抖Pro开关,点击即可开启或关闭。 除此之外,前置视频同样加持防抖算法,边走边拍也能稳定聚焦脸部' ',实时视频分享您的生活。') -oral_query_generation = appbuilder.OralQueryGeneration(model='Qianfan-Appbuilder-Speed-8k') +oral_query_generation = appbuilder.OralQueryGeneration(model='Qianfan-Agent-Speed-8k') answer = oral_query_generation(appbuilder.Message(text), query_type='全部', output_format='str') print(answer.content) ``` diff --git a/docs/sphinx_md/appbuilder.core.components.llms.playground.md b/docs/sphinx_md/appbuilder.core.components.llms.playground.md index 233718b3a..11f92c75b 100644 --- a/docs/sphinx_md/appbuilder.core.components.llms.playground.md +++ b/docs/sphinx_md/appbuilder.core.components.llms.playground.md @@ -17,7 +17,7 @@ import os import appbuilder os.environ["APPBUILDER_TOKEN"] = "..." -play = appbuilder.Playground(prompt_template="你好,{name},我是{bot_name},{bot_name}是一个{bot_type},我可以{bot_function},你可以问我{bot_question}。", model="Qianfan-Appbuilder-Speed-8k") +play = appbuilder.Playground(prompt_template="你好,{name},我是{bot_name},{bot_name}是一个{bot_type},我可以{bot_function},你可以问我{bot_question}。", model="Qianfan-Agent-Speed-8k") play(appbuilder.Message({"name": "小明", "bot_name": "小红", "bot_type": "聊天机器人", "bot_function": "聊天", "bot_question": "你好吗?"}), stream=False) ``` diff --git a/docs/sphinx_md/appbuilder.core.components.llms.query_decomposition.md b/docs/sphinx_md/appbuilder.core.components.llms.query_decomposition.md index 089db8d46..fe98c5896 100644 --- a/docs/sphinx_md/appbuilder.core.components.llms.query_decomposition.md +++ b/docs/sphinx_md/appbuilder.core.components.llms.query_decomposition.md @@ -21,7 +21,7 @@ import appbuilder # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 os.environ["APPBUILDER_TOKEN"] = "..." -query_decomposition = appbuilder.QueryDecomposition(model="Qianfan-Appbuilder-Speed-8k") +query_decomposition = appbuilder.QueryDecomposition(model="Qianfan-Agent-Speed-8k") msg = "吸塑包装盒在工业化生产和物流运输中分别有什么重要性?" msg = appbuilder.Message(msg) diff --git a/docs/sphinx_md/appbuilder.core.components.llms.query_rewrite.md b/docs/sphinx_md/appbuilder.core.components.llms.query_rewrite.md index b706e12f5..43fd6aa1b 100644 --- a/docs/sphinx_md/appbuilder.core.components.llms.query_rewrite.md +++ b/docs/sphinx_md/appbuilder.core.components.llms.query_rewrite.md @@ -18,7 +18,7 @@ Examples: import appbuilder os.environ["APPBUILDER_TOKEN"] = '...' -query_rewrite = appbuilder.QueryRewrite(model="Qianfan-Appbuilder-Speed-8k") +query_rewrite = appbuilder.QueryRewrite(model="Qianfan-Agent-Speed-8k") answer = query_rewrite(appbuilder.Message(['我应该怎么办理护照?', '您可以查询官网或人工咨询', '我需要准备哪些材料?', diff --git a/docs/sphinx_md/appbuilder.core.components.llms.similar_question.md b/docs/sphinx_md/appbuilder.core.components.llms.similar_question.md index 393311eeb..06fa9b57a 100644 --- a/docs/sphinx_md/appbuilder.core.components.llms.similar_question.md +++ b/docs/sphinx_md/appbuilder.core.components.llms.similar_question.md @@ -20,7 +20,7 @@ import appbuilder os.environ["APPBUILDER_TOKEN"] = "..." -qa_mining = appbuilder.SimilarQuestion(model="Qianfan-Appbuilder-Speed-8k") +qa_mining = appbuilder.SimilarQuestion(model="Qianfan-Agent-Speed-8k") msg = "我想吃冰淇淋,哪里的冰淇淋比较好吃?" msg = appbuilder.Message(msg) diff --git a/docs/sphinx_md/appbuilder.core.components.llms.style_rewrite.md b/docs/sphinx_md/appbuilder.core.components.llms.style_rewrite.md index 6f8239ba6..10567fb42 100644 --- a/docs/sphinx_md/appbuilder.core.components.llms.style_rewrite.md +++ b/docs/sphinx_md/appbuilder.core.components.llms.style_rewrite.md @@ -17,7 +17,7 @@ import os import appbuilder os.environ["APPBUILDER_TOKEN"] = '...' -style_rewrite = appbuilder.StyleRewrite(model="Qianfan-Appbuilder-Speed-8k") +style_rewrite = appbuilder.StyleRewrite(model="Qianfan-Agent-Speed-8k") answer = style_rewrite(appbuilder.Message("文心大模型发布新版本"), style="激励话术") ``` diff --git a/docs/sphinx_md/appbuilder.core.components.llms.style_writing.md b/docs/sphinx_md/appbuilder.core.components.llms.style_writing.md index e525936a3..d33f64173 100644 --- a/docs/sphinx_md/appbuilder.core.components.llms.style_writing.md +++ b/docs/sphinx_md/appbuilder.core.components.llms.style_writing.md @@ -18,7 +18,7 @@ import appbuilder # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 os.environ["APPBUILDER_TOKEN"] = '...' -style_writing = appbuilder.StyleWriting(model="Qianfan-Appbuilder-Speed-8k") +style_writing = appbuilder.StyleWriting(model="Qianfan-Agent-Speed-8k") answer = style_writing(appbuilder.Message("帮我写一篇关于人体工学椅的文案"), style_query="小红书", length=100) ``` diff --git a/docs/sphinx_md/appbuilder.core.components.llms.tag_extraction.md b/docs/sphinx_md/appbuilder.core.components.llms.tag_extraction.md index 79fa7c357..1fbb62f50 100644 --- a/docs/sphinx_md/appbuilder.core.components.llms.tag_extraction.md +++ b/docs/sphinx_md/appbuilder.core.components.llms.tag_extraction.md @@ -17,7 +17,7 @@ import appbuilder # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 os.environ["APPBUILDER_TOKEN"] = '...' -tag_extraction = appbuilder.TagExtraction(model="Qianfan-Appbuilder-Speed-8k") +tag_extraction = appbuilder.TagExtraction(model="Qianfan-Agent-Speed-8k") answer = tag_extraction(appbuilder.Message("从这段文本中抽取关键标签")) ``` diff --git a/docs/sphinx_md/appbuilder.core.md b/docs/sphinx_md/appbuilder.core.md index c53ac75cd..acd1d9004 100644 --- a/docs/sphinx_md/appbuilder.core.md +++ b/docs/sphinx_md/appbuilder.core.md @@ -91,7 +91,7 @@ os.environ["APPBUILDER_TOKEN"] = '...' class PlaygroundWithHistory(Component): def __init__(self): super().__init__() - self.query_rewrite = QueryRewrite(model="Qianfan-Appbuilder-Speed-8k") + self.query_rewrite = QueryRewrite(model="Qianfan-Agent-Speed-8k") self.play = Playground( prompt_template="{query}", model="eb-4" From 96d2781aa2b7a2c3f093458ee121f99ab8045264 Mon Sep 17 00:00:00 2001 From: userpj Date: Thu, 7 Nov 2024 19:15:19 +0800 Subject: [PATCH 03/85] =?UTF-8?q?appbuilder=E6=96=87=E4=BB=B6=E5=A4=B9?= =?UTF-8?q?=E9=87=8D=E5=91=BD=E5=90=8D=E4=B8=BApython?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 34 +++++++++--------- appbuilder/utils/chainlit.md | 24 ------------- {appbuilder => python}/__init__.py | 0 {appbuilder => python}/core/__init__.py | 0 {appbuilder => python}/core/_client.py | 0 {appbuilder => python}/core/_exception.py | 0 {appbuilder => python}/core/_session.py | 0 {appbuilder => python}/core/agent.py | 0 .../core/assistant/__init__.py | 0 .../core/assistant/assistants/__init__.py | 0 .../core/assistant/assistants/assistants.py | 0 .../core/assistant/assistants/files.py | 0 {appbuilder => python}/core/assistant/base.py | 0 .../core/assistant/threads/__init__.py | 0 .../assistant/threads/messages/__init__.py | 0 .../assistant/threads/messages/messages.py | 0 .../core/assistant/threads/runs/__init__.py | 0 .../core/assistant/threads/runs/runs.py | 0 .../core/assistant/threads/runs/steps.py | 0 .../assistant/threads/runs/stream_helper.py | 0 .../core/assistant/threads/threads.py | 0 .../core/assistant/type/__init__.py | 0 .../core/assistant/type/assistant_type.py | 0 .../core/assistant/type/public_type.py | 0 .../core/assistant/type/thread_type.py | 0 {appbuilder => python}/core/component.py | 0 .../core/components/__init__.py | 0 .../components/animal_recognize/README.md | 0 .../components/animal_recognize/__init__.py | 0 .../components/animal_recognize/component.py | 0 .../core/components/animal_recognize/model.py | 0 .../core/components/asr/README.md | 0 .../core/components/asr/__init__.py | 0 .../core/components/asr/component.py | 0 .../core/components/asr/model.py | 0 .../core/components/dish_recognize/README.md | 0 .../components/dish_recognize/__init__.py | 0 .../components/dish_recognize/component.py | 0 .../core/components/dish_recognize/model.py | 0 .../components/doc_crop_enhance/README.md | 0 .../components/doc_crop_enhance/__init__.py | 0 .../components/doc_crop_enhance/component.py | 0 .../core/components/doc_crop_enhance/model.py | 0 .../components/doc_format_converter/README.md | 0 .../doc_format_converter/__init__.py | 0 .../doc_format_converter/component.py | 0 .../components/doc_format_converter/model.py | 0 .../core/components/doc_parser/README.md | 0 .../core/components/doc_parser/__init__.py | 0 .../core/components/doc_parser/base.py | 0 .../core/components/doc_parser/doc_parser.py | 0 .../core/components/doc_splitter/README.md | 0 .../core/components/doc_splitter/__init__.py | 0 .../components/doc_splitter/doc_splitter.py | 0 .../document_understanding/README.md | 0 .../document_understanding/__init__.py | 0 .../components/document_understanding/base.py | 0 .../document_understanding/component.py | 0 .../core/components/embeddings/README.md | 0 .../core/components/embeddings/__init__.py | 0 .../core/components/embeddings/base.py | 0 .../core/components/embeddings/component.py | 0 .../core/components/excel2figure/README.md | 0 .../core/components/excel2figure/__init__.py | 0 .../core/components/excel2figure/base.py | 0 .../core/components/excel2figure/component.py | 0 .../core/components/extract_table/README.md | 0 .../core/components/extract_table/__init__.py | 0 .../components/extract_table/component.py | 0 .../core/components/gbi/__init__.py | 0 .../core/components/gbi/basic.py | 0 .../core/components/gbi/nl2sql/README.md | 0 .../core/components/gbi/nl2sql/__init__.py | 0 .../core/components/gbi/nl2sql/base.py | 0 .../core/components/gbi/nl2sql/component.py | 0 .../components/gbi/select_table/README.md | 0 .../components/gbi/select_table/__init__.py | 0 .../core/components/gbi/select_table/base.py | 0 .../components/gbi/select_table/component.py | 0 .../core/components/general_ocr/README.md | 0 .../core/components/general_ocr/__init__.py | 0 .../core/components/general_ocr/component.py | 0 .../core/components/general_ocr/model.py | 0 .../core/components/handwrite_ocr/README.md | 0 .../core/components/handwrite_ocr/__init__.py | 0 .../components/handwrite_ocr/component.py | 0 .../core/components/handwrite_ocr/model.py | 0 .../components/image_understand/README.md | 0 .../components/image_understand/__init__.py | 0 .../components/image_understand/component.py | 0 .../core/components/image_understand/model.py | 0 .../components/landmark_recognize/README.md | 0 .../components/landmark_recognize/__init__.py | 0 .../landmark_recognize/component.py | 0 .../components/landmark_recognize/model.py | 0 .../core/components/llms/__init__.py | 0 .../core/components/llms/base.py | 0 .../components/llms/dialog_summary/README.md | 0 .../llms/dialog_summary/__init__.py | 0 .../components/llms/dialog_summary/base.py | 0 .../llms/dialog_summary/component.py | 0 .../llms/hallucination_detection/README.md | 0 .../llms/hallucination_detection/__init__.py | 0 .../llms/hallucination_detection/base.py | 0 .../llms/hallucination_detection/component.py | 0 .../llms/is_complex_query/README.md | 0 .../llms/is_complex_query/__init__.py | 0 .../components/llms/is_complex_query/base.py | 0 .../llms/is_complex_query/component.py | 0 .../core/components/llms/mrc/README.md | 0 .../core/components/llms/mrc/__init__.py | 0 .../core/components/llms/mrc/base.py | 0 .../core/components/llms/mrc/component.py | 0 .../core/components/llms/nl2pandas/README.md | 0 .../components/llms/nl2pandas/__init__.py | 0 .../core/components/llms/nl2pandas/base.py | 0 .../components/llms/nl2pandas/component.py | 0 .../llms/oral_query_generation/README.md | 0 .../llms/oral_query_generation/__init__.py | 0 .../llms/oral_query_generation/base.py | 0 .../llms/oral_query_generation/component.py | 0 .../core/components/llms/playground/README.md | 0 .../components/llms/playground/__init__.py | 0 .../core/components/llms/playground/base.py | 0 .../components/llms/playground/component.py | 0 .../components/llms/qa_pair_mining/README.md | 0 .../llms/qa_pair_mining/__init__.py | 0 .../components/llms/qa_pair_mining/base.py | 0 .../llms/qa_pair_mining/component.py | 0 .../llms/query_decomposition/README.md | 0 .../llms/query_decomposition/__init__.py | 0 .../llms/query_decomposition/base.py | 0 .../llms/query_decomposition/component.py | 0 .../components/llms/query_rewrite/README.md | 0 .../components/llms/query_rewrite/__init__.py | 0 .../components/llms/query_rewrite/base.py | 0 .../llms/query_rewrite/component.py | 0 .../llms/similar_question/README.md | 0 .../llms/similar_question/__init__.py | 0 .../components/llms/similar_question/base.py | 0 .../llms/similar_question/component.py | 0 .../components/llms/style_rewrite/README.md | 0 .../components/llms/style_rewrite/__init__.py | 0 .../components/llms/style_rewrite/base.py | 0 .../llms/style_rewrite/component.py | 0 .../components/llms/style_writing/README.md | 0 .../components/llms/style_writing/__init__.py | 0 .../components/llms/style_writing/base.py | 0 .../llms/style_writing/component.py | 0 .../components/llms/tag_extraction/README.md | 0 .../llms/tag_extraction/__init__.py | 0 .../components/llms/tag_extraction/base.py | 0 .../llms/tag_extraction/component.py | 0 .../core/components/matching/README.md | 0 .../core/components/matching/__init__.py | 0 .../core/components/matching/base.py | 0 .../core/components/matching/component.py | 0 .../core/components/mix_card_ocr/README.md | 0 .../core/components/mix_card_ocr/__init__.py | 0 .../core/components/mix_card_ocr/component.py | 0 .../core/components/mix_card_ocr/model.py | 0 .../components/object_recognize/README.md | 0 .../components/object_recognize/__init__.py | 0 .../components/object_recognize/component.py | 0 .../core/components/object_recognize/model.py | 0 .../core/components/plant_recognize/README.md | 0 .../components/plant_recognize/__init__.py | 0 .../components/plant_recognize/component.py | 0 .../core/components/plant_recognize/model.py | 0 .../ppt_generation_from_file/README.md | 0 .../ppt_generation_from_file/__init__.py | 0 .../ppt_generation_from_file/base.py | 0 .../ppt_generation_from_file/component.py | 0 .../ppt_generation_from_instruction/README.md | 0 .../__init__.py | 0 .../ppt_generation_from_instruction/base.py | 0 .../component.py | 0 .../ppt_generation_from_paper/README.md | 0 .../ppt_generation_from_paper/__init__.py | 0 .../ppt_generation_from_paper/base.py | 0 .../ppt_generation_from_paper/component.py | 0 .../core/components/qrcode_ocr/README.md | 0 .../core/components/qrcode_ocr/__init__.py | 0 .../core/components/qrcode_ocr/component.py | 0 .../core/components/qrcode_ocr/model.py | 0 .../rag_with_baidu_search/README.md | 0 .../rag_with_baidu_search/__init__.py | 0 .../rag_with_baidu_search/component.py | 0 .../components/rag_with_baidu_search/model.py | 0 .../rag_with_baidu_search_pro/README.md | 0 .../rag_with_baidu_search_pro/__init__.py | 0 .../rag_with_baidu_search_pro/component.py | 0 .../parse_rag_pro_response.py | 0 .../core/components/retriever/README.md | 0 .../core/components/retriever/__init__.py | 0 .../components/retriever/baidu_vdb/README.md | 0 .../retriever/baidu_vdb/__init__.py | 0 .../retriever/baidu_vdb/baiduvdb_retriever.py | 0 .../components/retriever/baidu_vdb/model.py | 0 .../core/components/retriever/bes/README.md | 0 .../core/components/retriever/bes/__init__.py | 0 .../components/retriever/bes/bes_retriever.py | 0 .../components/retriever/reranker/README.md | 0 .../components/retriever/reranker/__init__.py | 0 .../components/retriever/reranker/model.py | 0 .../components/retriever/reranker/rerank.py | 0 .../core/components/table_ocr/README.md | 0 .../core/components/table_ocr/__init__.py | 0 .../core/components/table_ocr/component.py | 0 .../core/components/table_ocr/model.py | 0 .../core/components/text_to_image/README.md | 0 .../core/components/text_to_image/__init__.py | 0 .../components/text_to_image/component.py | 0 .../core/components/text_to_image/model.py | 0 .../core/components/translate/README.md | 0 .../core/components/translate/__init__.py | 0 .../core/components/translate/component.py | 0 .../core/components/translate/model.py | 0 .../core/components/tts/README.md | 0 .../core/components/tts/__init__.py | 0 .../core/components/tts/component.py | 0 .../core/components/tts/model.py | 0 .../core/console/__init__.py | 0 .../console/appbuilder_client/__init__.py | 0 .../appbuilder_client/appbuilder_client.py | 0 .../console/appbuilder_client/data_class.py | 0 .../appbuilder_client/event_handler.py | 0 {appbuilder => python}/core/console/base.py | 0 .../core/console/dataset/__init__.py | 0 .../core/console/dataset/dataset.py | 0 .../core/console/dataset/model.py | 0 .../core/console/knowledge_base/__init__.py | 0 .../core/console/knowledge_base/data_class.py | 0 .../console/knowledge_base/knowledge_base.py | 0 .../core/console/rag/__init__.py | 0 .../core/console/rag/rag.py | 0 {appbuilder => python}/core/constants.py | 0 {appbuilder => python}/core/context.py | 0 {appbuilder => python}/core/functional.py | 0 {appbuilder => python}/core/message.py | 0 .../core/session_message.py | 0 {appbuilder => python}/core/user_session.py | 0 {appbuilder => python}/core/utils.py | 0 {appbuilder => python}/tests/__init__.py | 0 .../tests/component_check.py | 0 .../tests/component_collector.py | 0 .../tests/data/qa_appbuilder_client_demo.pdf | Bin .../tests/data/qa_demo.xlsx | Bin .../qa_doc_parser_extract_table_from_doc.png | Bin .../tests/parallel_ut_run.py | 0 .../tests/print_components_error_info.py | 0 {appbuilder => python}/tests/pytest_config.py | 0 {appbuilder => python}/tests/pytest_utils.py | 0 .../tests/run_python_test.sh | 0 {appbuilder => python}/tests/sed_str.py | 0 {appbuilder => python}/tests/test.pdf | Bin {appbuilder => python}/tests/test_agent.py | 0 .../tests/test_all_components.py | 0 .../tests/test_animal_recognize.py | 0 .../tests/test_appbuilder_assistant_trace.py | 0 .../tests/test_appbuilder_client.py | 0 .../tests/test_appbuilder_client_app_list.py | 0 ...test_appbuilder_client_run_with_handler.py | 0 .../tests/test_appbuilder_client_toolcall.py | 0 ...ppbuilder_client_toolcall_event_handler.py | 0 ...der_client_toolcall_event_handler_error.py | 0 ...er_client_toolcall_event_handler_stream.py | 0 ...uilder_client_toolcall_event_handler_v2.py | 0 ...uilder_client_toolcall_event_handler_v3.py | 0 .../test_appbuilder_client_toolcall_stream.py | 0 .../test_appbuilder_client_toolcall_v2.py | 0 .../test_appbuilder_client_toolcall_v3.py | 0 .../tests/test_appbuilder_client_trace.py | 0 .../tests/test_appbuilder_components_trace.py | 0 ...st_appbuilder_core_components_retriever.py | 0 .../tests/test_appbuilder_sentry_trace_off.py | 0 .../tests/test_appbuilder_sentry_trace_on.py | 0 .../test_appbuilder_trace_raise_error.py | 0 {appbuilder => python}/tests/test_asr.py | 0 .../tests/test_assistant_basic_import.py | 0 .../tests/test_assistant_class_assistans.py | 0 .../tests/test_assistant_class_files.py | 0 .../tests/test_assistant_class_messages.py | 0 .../tests/test_assistant_class_runs.py | 0 .../tests/test_assistant_class_runs_v2.py | 0 .../tests/test_assistant_class_threads.py | 0 .../tests/test_assistant_e2e_funccall.py | 0 .../test_assistant_e2e_funccall_component.py | 0 .../tests/test_assistant_e2e_run.py | 0 .../tests/test_assistant_e2e_stream_cancel.py | 0 ...test_assistant_e2e_stream_event_handler.py | 0 ...t_assistant_e2e_stream_event_handler_v2.py | 0 .../test_assistant_e2e_stream_funccall.py | 0 .../tests/test_assistant_e2e_stream_run.py | 0 .../tests/test_bes_retriever.py | 0 .../tests/test_console_dataset.py | 0 .../tests/test_console_rag.py | 0 .../tests/test_core_agent.py | 0 .../tests/test_core_client.py | 0 ...est_core_components_baidu_vdb_retriever.py | 0 .../tests/test_core_components_doc.py | 0 .../tests/test_core_components_embedding.py | 0 .../tests/test_core_components_table.py | 0 .../tests/test_core_components_tts.py | 0 .../tests/test_core_console_base.py | 0 .../tests/test_core_user_session.py | 0 .../tests/test_core_utils.py | 0 .../tests/test_dialog_summary.py | 0 .../tests/test_dish_recognize.py | 0 .../tests/test_doc_crop_enhance.py | 0 .../tests/test_doc_format_converter.py | 0 .../tests/test_doc_parser.py | 0 .../tests/test_doc_splitter.py | 0 .../tests/test_document_understanding.py | 0 .../tests/test_embedding.py | 0 .../tests/test_extract_table.py | 0 .../tests/test_gbi_nl2sql.py | 0 .../tests/test_gbi_select_table.py | 0 .../tests/test_general_ocr.py | 0 .../tests/test_get_app_list.py | 0 .../tests/test_get_model_list.py | 0 .../tests/test_hallucination_detection.py | 0 .../tests/test_handwrite_ocr.py | 0 .../tests/test_image_understand.py | 0 .../tests/test_is_complex_query.py | 0 .../tests/test_knowledge_base.py | 0 .../tests/test_landmark_recognize.py | 0 .../tests/test_langchain_adapter_run.py | 0 .../tests/test_langchain_adapter_tool_eval.py | 0 .../tests/test_langchain_error.py | 0 {appbuilder => python}/tests/test_llm_base.py | 0 {appbuilder => python}/tests/test_matching.py | 0 {appbuilder => python}/tests/test_message.py | 0 .../tests/test_mix_card_ocr.py | 0 {appbuilder => python}/tests/test_mrc.py | 0 .../tests/test_nl2pandas.py | 0 .../tests/test_object_recognize.py | 0 .../tests/test_oral_query_generation.py | 0 .../tests/test_plant_recognize.py | 0 .../tests/test_playground.py | 0 .../tests/test_ppt_generation_from_file.py | 0 .../test_ppt_generation_from_instruction.py | 0 .../tests/test_ppt_generation_from_paper.py | 0 .../tests/test_qa_aicape_animal_rec.py | 0 .../tests/test_qa_aicape_doc_crop_enhance.py | 0 .../tests/test_qa_aicape_handwriting_ocr.py | 0 .../tests/test_qa_aicape_image_understand.py | 0 .../tests/test_qa_aicape_mixcard_ocr.py | 0 .../tests/test_qa_aicape_plant_rec.py | 0 .../tests/test_qa_aicape_qrcode_orc.py | 0 .../tests/test_qa_aicape_table_ocr.py | 0 ...st_qa_doc_parser_extract_table_from_doc.py | 0 .../tests/test_qa_llm_dialog_summary.py | 0 .../tests/test_qa_llm_excel2figure.py | 0 .../test_qa_llm_get_qianfan_model_list.py | 0 .../tests/test_qa_llm_is_complex_query.py | 0 .../tests/test_qa_llm_matching.py | 0 .../test_qa_llm_oral_query_generation.py | 0 .../tests/test_qa_llm_paddle_speech_tts.py | 0 .../tests/test_qa_llm_pandas.py | 0 .../tests/test_qa_llm_query_decomposition.py | 0 .../tests/test_qa_llm_style_rewrite.py | 0 .../tests/test_qa_pair_mining.py | 0 .../tests/test_qrcode_ocr.py | 0 .../tests/test_query_decomposition.py | 0 .../tests/test_query_rewrite.py | 0 .../tests/test_rag_baidu_search.py | 0 .../tests/test_rag_baidu_search_pro.py | 0 {appbuilder => python}/tests/test_rerank.py | 0 .../tests/test_similar_question.py | 0 .../tests/test_style_rewrite.py | 0 .../tests/test_style_writing.py | 0 .../tests/test_table_ocr.py | 0 .../tests/test_tag_extraction.py | 0 .../tests/test_text_to_image.py | 0 {appbuilder => python}/tests/test_trace.py | 0 .../tests/test_trace_skip_raise_error.py | 0 .../tests/test_translate.py | 0 {appbuilder => python}/tests/test_tts.py | 0 {appbuilder => python}/tests/test_utils.py | 0 .../tests/test_utils_collector.py | 0 .../tests/test_utils_logger.py | 0 .../tests/test_utils_logging_util.py | 0 .../tests/test_vdb_retriever.py | 0 .../tests/title_splitter.docx | Bin .../tests/whitelist_components.txt | 0 {appbuilder => python}/utils/__init__.py | 0 {appbuilder => python}/utils/_bcc.py | 0 {appbuilder => python}/utils/bce_deploy.py | 0 {appbuilder => python}/utils/collector.py | 0 {appbuilder => python}/utils/func_utils.py | 0 .../utils/json_schema_to_model.py | 0 {appbuilder => python}/utils/logger_util.py | 0 {appbuilder => python}/utils/model_util.py | 0 {appbuilder => python}/utils/sse_util.py | 0 .../utils/trace/__init__.py | 0 .../utils/trace/_function.py | 0 .../utils/trace/phoenix_wrapper.py | 0 {appbuilder => python}/utils/trace/tracer.py | 0 .../utils/trace/tracer_wrapper.py | 0 400 files changed, 17 insertions(+), 41 deletions(-) delete mode 100644 appbuilder/utils/chainlit.md rename {appbuilder => python}/__init__.py (100%) rename {appbuilder => python}/core/__init__.py (100%) rename {appbuilder => python}/core/_client.py (100%) rename {appbuilder => python}/core/_exception.py (100%) rename {appbuilder => python}/core/_session.py (100%) rename {appbuilder => python}/core/agent.py (100%) rename {appbuilder => python}/core/assistant/__init__.py (100%) rename {appbuilder => python}/core/assistant/assistants/__init__.py (100%) rename {appbuilder => python}/core/assistant/assistants/assistants.py (100%) rename {appbuilder => python}/core/assistant/assistants/files.py (100%) rename {appbuilder => python}/core/assistant/base.py (100%) rename {appbuilder => python}/core/assistant/threads/__init__.py (100%) rename {appbuilder => python}/core/assistant/threads/messages/__init__.py (100%) rename {appbuilder => python}/core/assistant/threads/messages/messages.py (100%) rename {appbuilder => python}/core/assistant/threads/runs/__init__.py (100%) rename {appbuilder => python}/core/assistant/threads/runs/runs.py (100%) rename {appbuilder => python}/core/assistant/threads/runs/steps.py (100%) rename {appbuilder => python}/core/assistant/threads/runs/stream_helper.py (100%) rename {appbuilder => python}/core/assistant/threads/threads.py (100%) rename {appbuilder => python}/core/assistant/type/__init__.py (100%) rename {appbuilder => python}/core/assistant/type/assistant_type.py (100%) rename {appbuilder => python}/core/assistant/type/public_type.py (100%) rename {appbuilder => python}/core/assistant/type/thread_type.py (100%) rename {appbuilder => python}/core/component.py (100%) rename {appbuilder => python}/core/components/__init__.py (100%) rename {appbuilder => python}/core/components/animal_recognize/README.md (100%) rename {appbuilder => python}/core/components/animal_recognize/__init__.py (100%) rename {appbuilder => python}/core/components/animal_recognize/component.py (100%) rename {appbuilder => python}/core/components/animal_recognize/model.py (100%) rename {appbuilder => python}/core/components/asr/README.md (100%) rename {appbuilder => python}/core/components/asr/__init__.py (100%) rename {appbuilder => python}/core/components/asr/component.py (100%) rename {appbuilder => python}/core/components/asr/model.py (100%) rename {appbuilder => python}/core/components/dish_recognize/README.md (100%) rename {appbuilder => python}/core/components/dish_recognize/__init__.py (100%) rename {appbuilder => python}/core/components/dish_recognize/component.py (100%) rename {appbuilder => python}/core/components/dish_recognize/model.py (100%) rename {appbuilder => python}/core/components/doc_crop_enhance/README.md (100%) rename {appbuilder => python}/core/components/doc_crop_enhance/__init__.py (100%) rename {appbuilder => python}/core/components/doc_crop_enhance/component.py (100%) rename {appbuilder => python}/core/components/doc_crop_enhance/model.py (100%) rename {appbuilder => python}/core/components/doc_format_converter/README.md (100%) rename {appbuilder => python}/core/components/doc_format_converter/__init__.py (100%) rename {appbuilder => python}/core/components/doc_format_converter/component.py (100%) rename {appbuilder => python}/core/components/doc_format_converter/model.py (100%) rename {appbuilder => python}/core/components/doc_parser/README.md (100%) rename {appbuilder => python}/core/components/doc_parser/__init__.py (100%) rename {appbuilder => python}/core/components/doc_parser/base.py (100%) rename {appbuilder => python}/core/components/doc_parser/doc_parser.py (100%) rename {appbuilder => python}/core/components/doc_splitter/README.md (100%) rename {appbuilder => python}/core/components/doc_splitter/__init__.py (100%) rename {appbuilder => python}/core/components/doc_splitter/doc_splitter.py (100%) rename {appbuilder => python}/core/components/document_understanding/README.md (100%) rename {appbuilder => python}/core/components/document_understanding/__init__.py (100%) rename {appbuilder => python}/core/components/document_understanding/base.py (100%) rename {appbuilder => python}/core/components/document_understanding/component.py (100%) rename {appbuilder => python}/core/components/embeddings/README.md (100%) rename {appbuilder => python}/core/components/embeddings/__init__.py (100%) rename {appbuilder => python}/core/components/embeddings/base.py (100%) rename {appbuilder => python}/core/components/embeddings/component.py (100%) rename {appbuilder => python}/core/components/excel2figure/README.md (100%) rename {appbuilder => python}/core/components/excel2figure/__init__.py (100%) rename {appbuilder => python}/core/components/excel2figure/base.py (100%) rename {appbuilder => python}/core/components/excel2figure/component.py (100%) rename {appbuilder => python}/core/components/extract_table/README.md (100%) rename {appbuilder => python}/core/components/extract_table/__init__.py (100%) rename {appbuilder => python}/core/components/extract_table/component.py (100%) rename {appbuilder => python}/core/components/gbi/__init__.py (100%) rename {appbuilder => python}/core/components/gbi/basic.py (100%) rename {appbuilder => python}/core/components/gbi/nl2sql/README.md (100%) rename {appbuilder => python}/core/components/gbi/nl2sql/__init__.py (100%) rename {appbuilder => python}/core/components/gbi/nl2sql/base.py (100%) rename {appbuilder => python}/core/components/gbi/nl2sql/component.py (100%) rename {appbuilder => python}/core/components/gbi/select_table/README.md (100%) rename {appbuilder => python}/core/components/gbi/select_table/__init__.py (100%) rename {appbuilder => python}/core/components/gbi/select_table/base.py (100%) rename {appbuilder => python}/core/components/gbi/select_table/component.py (100%) rename {appbuilder => python}/core/components/general_ocr/README.md (100%) rename {appbuilder => python}/core/components/general_ocr/__init__.py (100%) rename {appbuilder => python}/core/components/general_ocr/component.py (100%) rename {appbuilder => python}/core/components/general_ocr/model.py (100%) rename {appbuilder => python}/core/components/handwrite_ocr/README.md (100%) rename {appbuilder => python}/core/components/handwrite_ocr/__init__.py (100%) rename {appbuilder => python}/core/components/handwrite_ocr/component.py (100%) rename {appbuilder => python}/core/components/handwrite_ocr/model.py (100%) rename {appbuilder => python}/core/components/image_understand/README.md (100%) rename {appbuilder => python}/core/components/image_understand/__init__.py (100%) rename {appbuilder => python}/core/components/image_understand/component.py (100%) rename {appbuilder => python}/core/components/image_understand/model.py (100%) rename {appbuilder => python}/core/components/landmark_recognize/README.md (100%) rename {appbuilder => python}/core/components/landmark_recognize/__init__.py (100%) rename {appbuilder => python}/core/components/landmark_recognize/component.py (100%) rename {appbuilder => python}/core/components/landmark_recognize/model.py (100%) rename {appbuilder => python}/core/components/llms/__init__.py (100%) rename {appbuilder => python}/core/components/llms/base.py (100%) rename {appbuilder => python}/core/components/llms/dialog_summary/README.md (100%) rename {appbuilder => python}/core/components/llms/dialog_summary/__init__.py (100%) rename {appbuilder => python}/core/components/llms/dialog_summary/base.py (100%) rename {appbuilder => python}/core/components/llms/dialog_summary/component.py (100%) rename {appbuilder => python}/core/components/llms/hallucination_detection/README.md (100%) rename {appbuilder => python}/core/components/llms/hallucination_detection/__init__.py (100%) rename {appbuilder => python}/core/components/llms/hallucination_detection/base.py (100%) rename {appbuilder => python}/core/components/llms/hallucination_detection/component.py (100%) rename {appbuilder => python}/core/components/llms/is_complex_query/README.md (100%) rename {appbuilder => python}/core/components/llms/is_complex_query/__init__.py (100%) rename {appbuilder => python}/core/components/llms/is_complex_query/base.py (100%) rename {appbuilder => python}/core/components/llms/is_complex_query/component.py (100%) rename {appbuilder => python}/core/components/llms/mrc/README.md (100%) rename {appbuilder => python}/core/components/llms/mrc/__init__.py (100%) rename {appbuilder => python}/core/components/llms/mrc/base.py (100%) rename {appbuilder => python}/core/components/llms/mrc/component.py (100%) rename {appbuilder => python}/core/components/llms/nl2pandas/README.md (100%) rename {appbuilder => python}/core/components/llms/nl2pandas/__init__.py (100%) rename {appbuilder => python}/core/components/llms/nl2pandas/base.py (100%) rename {appbuilder => python}/core/components/llms/nl2pandas/component.py (100%) rename {appbuilder => python}/core/components/llms/oral_query_generation/README.md (100%) rename {appbuilder => python}/core/components/llms/oral_query_generation/__init__.py (100%) rename {appbuilder => python}/core/components/llms/oral_query_generation/base.py (100%) rename {appbuilder => python}/core/components/llms/oral_query_generation/component.py (100%) rename {appbuilder => python}/core/components/llms/playground/README.md (100%) rename {appbuilder => python}/core/components/llms/playground/__init__.py (100%) rename {appbuilder => python}/core/components/llms/playground/base.py (100%) rename {appbuilder => python}/core/components/llms/playground/component.py (100%) rename {appbuilder => python}/core/components/llms/qa_pair_mining/README.md (100%) rename {appbuilder => python}/core/components/llms/qa_pair_mining/__init__.py (100%) rename {appbuilder => python}/core/components/llms/qa_pair_mining/base.py (100%) rename {appbuilder => python}/core/components/llms/qa_pair_mining/component.py (100%) rename {appbuilder => python}/core/components/llms/query_decomposition/README.md (100%) rename {appbuilder => python}/core/components/llms/query_decomposition/__init__.py (100%) rename {appbuilder => python}/core/components/llms/query_decomposition/base.py (100%) rename {appbuilder => python}/core/components/llms/query_decomposition/component.py (100%) rename {appbuilder => python}/core/components/llms/query_rewrite/README.md (100%) rename {appbuilder => python}/core/components/llms/query_rewrite/__init__.py (100%) rename {appbuilder => python}/core/components/llms/query_rewrite/base.py (100%) rename {appbuilder => python}/core/components/llms/query_rewrite/component.py (100%) rename {appbuilder => python}/core/components/llms/similar_question/README.md (100%) rename {appbuilder => python}/core/components/llms/similar_question/__init__.py (100%) rename {appbuilder => python}/core/components/llms/similar_question/base.py (100%) rename {appbuilder => python}/core/components/llms/similar_question/component.py (100%) rename {appbuilder => python}/core/components/llms/style_rewrite/README.md (100%) rename {appbuilder => python}/core/components/llms/style_rewrite/__init__.py (100%) rename {appbuilder => python}/core/components/llms/style_rewrite/base.py (100%) rename {appbuilder => python}/core/components/llms/style_rewrite/component.py (100%) rename {appbuilder => python}/core/components/llms/style_writing/README.md (100%) rename {appbuilder => python}/core/components/llms/style_writing/__init__.py (100%) rename {appbuilder => python}/core/components/llms/style_writing/base.py (100%) rename {appbuilder => python}/core/components/llms/style_writing/component.py (100%) rename {appbuilder => python}/core/components/llms/tag_extraction/README.md (100%) rename {appbuilder => python}/core/components/llms/tag_extraction/__init__.py (100%) rename {appbuilder => python}/core/components/llms/tag_extraction/base.py (100%) rename {appbuilder => python}/core/components/llms/tag_extraction/component.py (100%) rename {appbuilder => python}/core/components/matching/README.md (100%) rename {appbuilder => python}/core/components/matching/__init__.py (100%) rename {appbuilder => python}/core/components/matching/base.py (100%) rename {appbuilder => python}/core/components/matching/component.py (100%) rename {appbuilder => python}/core/components/mix_card_ocr/README.md (100%) rename {appbuilder => python}/core/components/mix_card_ocr/__init__.py (100%) rename {appbuilder => python}/core/components/mix_card_ocr/component.py (100%) rename {appbuilder => python}/core/components/mix_card_ocr/model.py (100%) rename {appbuilder => python}/core/components/object_recognize/README.md (100%) rename {appbuilder => python}/core/components/object_recognize/__init__.py (100%) rename {appbuilder => python}/core/components/object_recognize/component.py (100%) rename {appbuilder => python}/core/components/object_recognize/model.py (100%) rename {appbuilder => python}/core/components/plant_recognize/README.md (100%) rename {appbuilder => python}/core/components/plant_recognize/__init__.py (100%) rename {appbuilder => python}/core/components/plant_recognize/component.py (100%) rename {appbuilder => python}/core/components/plant_recognize/model.py (100%) rename {appbuilder => python}/core/components/ppt_generation_from_file/README.md (100%) rename {appbuilder => python}/core/components/ppt_generation_from_file/__init__.py (100%) rename {appbuilder => python}/core/components/ppt_generation_from_file/base.py (100%) rename {appbuilder => python}/core/components/ppt_generation_from_file/component.py (100%) rename {appbuilder => python}/core/components/ppt_generation_from_instruction/README.md (100%) rename {appbuilder => python}/core/components/ppt_generation_from_instruction/__init__.py (100%) rename {appbuilder => python}/core/components/ppt_generation_from_instruction/base.py (100%) rename {appbuilder => python}/core/components/ppt_generation_from_instruction/component.py (100%) rename {appbuilder => python}/core/components/ppt_generation_from_paper/README.md (100%) rename {appbuilder => python}/core/components/ppt_generation_from_paper/__init__.py (100%) rename {appbuilder => python}/core/components/ppt_generation_from_paper/base.py (100%) rename {appbuilder => python}/core/components/ppt_generation_from_paper/component.py (100%) rename {appbuilder => python}/core/components/qrcode_ocr/README.md (100%) rename {appbuilder => python}/core/components/qrcode_ocr/__init__.py (100%) rename {appbuilder => python}/core/components/qrcode_ocr/component.py (100%) rename {appbuilder => python}/core/components/qrcode_ocr/model.py (100%) rename {appbuilder => python}/core/components/rag_with_baidu_search/README.md (100%) rename {appbuilder => python}/core/components/rag_with_baidu_search/__init__.py (100%) rename {appbuilder => python}/core/components/rag_with_baidu_search/component.py (100%) rename {appbuilder => python}/core/components/rag_with_baidu_search/model.py (100%) rename {appbuilder => python}/core/components/rag_with_baidu_search_pro/README.md (100%) rename {appbuilder => python}/core/components/rag_with_baidu_search_pro/__init__.py (100%) rename {appbuilder => python}/core/components/rag_with_baidu_search_pro/component.py (100%) rename {appbuilder => python}/core/components/rag_with_baidu_search_pro/parse_rag_pro_response.py (100%) rename {appbuilder => python}/core/components/retriever/README.md (100%) rename {appbuilder => python}/core/components/retriever/__init__.py (100%) rename {appbuilder => python}/core/components/retriever/baidu_vdb/README.md (100%) rename {appbuilder => python}/core/components/retriever/baidu_vdb/__init__.py (100%) rename {appbuilder => python}/core/components/retriever/baidu_vdb/baiduvdb_retriever.py (100%) rename {appbuilder => python}/core/components/retriever/baidu_vdb/model.py (100%) rename {appbuilder => python}/core/components/retriever/bes/README.md (100%) rename {appbuilder => python}/core/components/retriever/bes/__init__.py (100%) rename {appbuilder => python}/core/components/retriever/bes/bes_retriever.py (100%) rename {appbuilder => python}/core/components/retriever/reranker/README.md (100%) rename {appbuilder => python}/core/components/retriever/reranker/__init__.py (100%) rename {appbuilder => python}/core/components/retriever/reranker/model.py (100%) rename {appbuilder => python}/core/components/retriever/reranker/rerank.py (100%) rename {appbuilder => python}/core/components/table_ocr/README.md (100%) rename {appbuilder => python}/core/components/table_ocr/__init__.py (100%) rename {appbuilder => python}/core/components/table_ocr/component.py (100%) rename {appbuilder => python}/core/components/table_ocr/model.py (100%) rename {appbuilder => python}/core/components/text_to_image/README.md (100%) rename {appbuilder => python}/core/components/text_to_image/__init__.py (100%) rename {appbuilder => python}/core/components/text_to_image/component.py (100%) rename {appbuilder => python}/core/components/text_to_image/model.py (100%) rename {appbuilder => python}/core/components/translate/README.md (100%) rename {appbuilder => python}/core/components/translate/__init__.py (100%) rename {appbuilder => python}/core/components/translate/component.py (100%) rename {appbuilder => python}/core/components/translate/model.py (100%) rename {appbuilder => python}/core/components/tts/README.md (100%) rename {appbuilder => python}/core/components/tts/__init__.py (100%) rename {appbuilder => python}/core/components/tts/component.py (100%) rename {appbuilder => python}/core/components/tts/model.py (100%) rename {appbuilder => python}/core/console/__init__.py (100%) rename {appbuilder => python}/core/console/appbuilder_client/__init__.py (100%) rename {appbuilder => python}/core/console/appbuilder_client/appbuilder_client.py (100%) rename {appbuilder => python}/core/console/appbuilder_client/data_class.py (100%) rename {appbuilder => python}/core/console/appbuilder_client/event_handler.py (100%) rename {appbuilder => python}/core/console/base.py (100%) rename {appbuilder => python}/core/console/dataset/__init__.py (100%) rename {appbuilder => python}/core/console/dataset/dataset.py (100%) rename {appbuilder => python}/core/console/dataset/model.py (100%) rename {appbuilder => python}/core/console/knowledge_base/__init__.py (100%) rename {appbuilder => python}/core/console/knowledge_base/data_class.py (100%) rename {appbuilder => python}/core/console/knowledge_base/knowledge_base.py (100%) rename {appbuilder => python}/core/console/rag/__init__.py (100%) rename {appbuilder => python}/core/console/rag/rag.py (100%) rename {appbuilder => python}/core/constants.py (100%) rename {appbuilder => python}/core/context.py (100%) rename {appbuilder => python}/core/functional.py (100%) rename {appbuilder => python}/core/message.py (100%) rename {appbuilder => python}/core/session_message.py (100%) rename {appbuilder => python}/core/user_session.py (100%) rename {appbuilder => python}/core/utils.py (100%) rename {appbuilder => python}/tests/__init__.py (100%) rename {appbuilder => python}/tests/component_check.py (100%) rename {appbuilder => python}/tests/component_collector.py (100%) rename {appbuilder => python}/tests/data/qa_appbuilder_client_demo.pdf (100%) rename {appbuilder => python}/tests/data/qa_demo.xlsx (100%) rename {appbuilder => python}/tests/data/qa_doc_parser_extract_table_from_doc.png (100%) rename {appbuilder => python}/tests/parallel_ut_run.py (100%) rename {appbuilder => python}/tests/print_components_error_info.py (100%) rename {appbuilder => python}/tests/pytest_config.py (100%) rename {appbuilder => python}/tests/pytest_utils.py (100%) rename {appbuilder => python}/tests/run_python_test.sh (100%) rename {appbuilder => python}/tests/sed_str.py (100%) rename {appbuilder => python}/tests/test.pdf (100%) rename {appbuilder => python}/tests/test_agent.py (100%) rename {appbuilder => python}/tests/test_all_components.py (100%) rename {appbuilder => python}/tests/test_animal_recognize.py (100%) rename {appbuilder => python}/tests/test_appbuilder_assistant_trace.py (100%) rename {appbuilder => python}/tests/test_appbuilder_client.py (100%) rename {appbuilder => python}/tests/test_appbuilder_client_app_list.py (100%) rename {appbuilder => python}/tests/test_appbuilder_client_run_with_handler.py (100%) rename {appbuilder => python}/tests/test_appbuilder_client_toolcall.py (100%) rename {appbuilder => python}/tests/test_appbuilder_client_toolcall_event_handler.py (100%) rename {appbuilder => python}/tests/test_appbuilder_client_toolcall_event_handler_error.py (100%) rename {appbuilder => python}/tests/test_appbuilder_client_toolcall_event_handler_stream.py (100%) rename {appbuilder => python}/tests/test_appbuilder_client_toolcall_event_handler_v2.py (100%) rename {appbuilder => python}/tests/test_appbuilder_client_toolcall_event_handler_v3.py (100%) rename {appbuilder => python}/tests/test_appbuilder_client_toolcall_stream.py (100%) rename {appbuilder => python}/tests/test_appbuilder_client_toolcall_v2.py (100%) rename {appbuilder => python}/tests/test_appbuilder_client_toolcall_v3.py (100%) rename {appbuilder => python}/tests/test_appbuilder_client_trace.py (100%) rename {appbuilder => python}/tests/test_appbuilder_components_trace.py (100%) rename {appbuilder => python}/tests/test_appbuilder_core_components_retriever.py (100%) rename {appbuilder => python}/tests/test_appbuilder_sentry_trace_off.py (100%) rename {appbuilder => python}/tests/test_appbuilder_sentry_trace_on.py (100%) rename {appbuilder => python}/tests/test_appbuilder_trace_raise_error.py (100%) rename {appbuilder => python}/tests/test_asr.py (100%) rename {appbuilder => python}/tests/test_assistant_basic_import.py (100%) rename {appbuilder => python}/tests/test_assistant_class_assistans.py (100%) rename {appbuilder => python}/tests/test_assistant_class_files.py (100%) rename {appbuilder => python}/tests/test_assistant_class_messages.py (100%) rename {appbuilder => python}/tests/test_assistant_class_runs.py (100%) rename {appbuilder => python}/tests/test_assistant_class_runs_v2.py (100%) rename {appbuilder => python}/tests/test_assistant_class_threads.py (100%) rename {appbuilder => python}/tests/test_assistant_e2e_funccall.py (100%) rename {appbuilder => python}/tests/test_assistant_e2e_funccall_component.py (100%) rename {appbuilder => python}/tests/test_assistant_e2e_run.py (100%) rename {appbuilder => python}/tests/test_assistant_e2e_stream_cancel.py (100%) rename {appbuilder => python}/tests/test_assistant_e2e_stream_event_handler.py (100%) rename {appbuilder => python}/tests/test_assistant_e2e_stream_event_handler_v2.py (100%) rename {appbuilder => python}/tests/test_assistant_e2e_stream_funccall.py (100%) rename {appbuilder => python}/tests/test_assistant_e2e_stream_run.py (100%) rename {appbuilder => python}/tests/test_bes_retriever.py (100%) rename {appbuilder => python}/tests/test_console_dataset.py (100%) rename {appbuilder => python}/tests/test_console_rag.py (100%) rename {appbuilder => python}/tests/test_core_agent.py (100%) rename {appbuilder => python}/tests/test_core_client.py (100%) rename {appbuilder => python}/tests/test_core_components_baidu_vdb_retriever.py (100%) rename {appbuilder => python}/tests/test_core_components_doc.py (100%) rename {appbuilder => python}/tests/test_core_components_embedding.py (100%) rename {appbuilder => python}/tests/test_core_components_table.py (100%) rename {appbuilder => python}/tests/test_core_components_tts.py (100%) rename {appbuilder => python}/tests/test_core_console_base.py (100%) rename {appbuilder => python}/tests/test_core_user_session.py (100%) rename {appbuilder => python}/tests/test_core_utils.py (100%) rename {appbuilder => python}/tests/test_dialog_summary.py (100%) rename {appbuilder => python}/tests/test_dish_recognize.py (100%) rename {appbuilder => python}/tests/test_doc_crop_enhance.py (100%) rename {appbuilder => python}/tests/test_doc_format_converter.py (100%) rename {appbuilder => python}/tests/test_doc_parser.py (100%) rename {appbuilder => python}/tests/test_doc_splitter.py (100%) rename {appbuilder => python}/tests/test_document_understanding.py (100%) rename {appbuilder => python}/tests/test_embedding.py (100%) rename {appbuilder => python}/tests/test_extract_table.py (100%) rename {appbuilder => python}/tests/test_gbi_nl2sql.py (100%) rename {appbuilder => python}/tests/test_gbi_select_table.py (100%) rename {appbuilder => python}/tests/test_general_ocr.py (100%) rename {appbuilder => python}/tests/test_get_app_list.py (100%) rename {appbuilder => python}/tests/test_get_model_list.py (100%) rename {appbuilder => python}/tests/test_hallucination_detection.py (100%) rename {appbuilder => python}/tests/test_handwrite_ocr.py (100%) rename {appbuilder => python}/tests/test_image_understand.py (100%) rename {appbuilder => python}/tests/test_is_complex_query.py (100%) rename {appbuilder => python}/tests/test_knowledge_base.py (100%) rename {appbuilder => python}/tests/test_landmark_recognize.py (100%) rename {appbuilder => python}/tests/test_langchain_adapter_run.py (100%) rename {appbuilder => python}/tests/test_langchain_adapter_tool_eval.py (100%) rename {appbuilder => python}/tests/test_langchain_error.py (100%) rename {appbuilder => python}/tests/test_llm_base.py (100%) rename {appbuilder => python}/tests/test_matching.py (100%) rename {appbuilder => python}/tests/test_message.py (100%) rename {appbuilder => python}/tests/test_mix_card_ocr.py (100%) rename {appbuilder => python}/tests/test_mrc.py (100%) rename {appbuilder => python}/tests/test_nl2pandas.py (100%) rename {appbuilder => python}/tests/test_object_recognize.py (100%) rename {appbuilder => python}/tests/test_oral_query_generation.py (100%) rename {appbuilder => python}/tests/test_plant_recognize.py (100%) rename {appbuilder => python}/tests/test_playground.py (100%) rename {appbuilder => python}/tests/test_ppt_generation_from_file.py (100%) rename {appbuilder => python}/tests/test_ppt_generation_from_instruction.py (100%) rename {appbuilder => python}/tests/test_ppt_generation_from_paper.py (100%) rename {appbuilder => python}/tests/test_qa_aicape_animal_rec.py (100%) rename {appbuilder => python}/tests/test_qa_aicape_doc_crop_enhance.py (100%) rename {appbuilder => python}/tests/test_qa_aicape_handwriting_ocr.py (100%) rename {appbuilder => python}/tests/test_qa_aicape_image_understand.py (100%) rename {appbuilder => python}/tests/test_qa_aicape_mixcard_ocr.py (100%) rename {appbuilder => python}/tests/test_qa_aicape_plant_rec.py (100%) rename {appbuilder => python}/tests/test_qa_aicape_qrcode_orc.py (100%) rename {appbuilder => python}/tests/test_qa_aicape_table_ocr.py (100%) rename {appbuilder => python}/tests/test_qa_doc_parser_extract_table_from_doc.py (100%) rename {appbuilder => python}/tests/test_qa_llm_dialog_summary.py (100%) rename {appbuilder => python}/tests/test_qa_llm_excel2figure.py (100%) rename {appbuilder => python}/tests/test_qa_llm_get_qianfan_model_list.py (100%) rename {appbuilder => python}/tests/test_qa_llm_is_complex_query.py (100%) rename {appbuilder => python}/tests/test_qa_llm_matching.py (100%) rename {appbuilder => python}/tests/test_qa_llm_oral_query_generation.py (100%) rename {appbuilder => python}/tests/test_qa_llm_paddle_speech_tts.py (100%) rename {appbuilder => python}/tests/test_qa_llm_pandas.py (100%) rename {appbuilder => python}/tests/test_qa_llm_query_decomposition.py (100%) rename {appbuilder => python}/tests/test_qa_llm_style_rewrite.py (100%) rename {appbuilder => python}/tests/test_qa_pair_mining.py (100%) rename {appbuilder => python}/tests/test_qrcode_ocr.py (100%) rename {appbuilder => python}/tests/test_query_decomposition.py (100%) rename {appbuilder => python}/tests/test_query_rewrite.py (100%) rename {appbuilder => python}/tests/test_rag_baidu_search.py (100%) rename {appbuilder => python}/tests/test_rag_baidu_search_pro.py (100%) rename {appbuilder => python}/tests/test_rerank.py (100%) rename {appbuilder => python}/tests/test_similar_question.py (100%) rename {appbuilder => python}/tests/test_style_rewrite.py (100%) rename {appbuilder => python}/tests/test_style_writing.py (100%) rename {appbuilder => python}/tests/test_table_ocr.py (100%) rename {appbuilder => python}/tests/test_tag_extraction.py (100%) rename {appbuilder => python}/tests/test_text_to_image.py (100%) rename {appbuilder => python}/tests/test_trace.py (100%) rename {appbuilder => python}/tests/test_trace_skip_raise_error.py (100%) rename {appbuilder => python}/tests/test_translate.py (100%) rename {appbuilder => python}/tests/test_tts.py (100%) rename {appbuilder => python}/tests/test_utils.py (100%) rename {appbuilder => python}/tests/test_utils_collector.py (100%) rename {appbuilder => python}/tests/test_utils_logger.py (100%) rename {appbuilder => python}/tests/test_utils_logging_util.py (100%) rename {appbuilder => python}/tests/test_vdb_retriever.py (100%) rename {appbuilder => python}/tests/title_splitter.docx (100%) rename {appbuilder => python}/tests/whitelist_components.txt (100%) rename {appbuilder => python}/utils/__init__.py (100%) rename {appbuilder => python}/utils/_bcc.py (100%) rename {appbuilder => python}/utils/bce_deploy.py (100%) rename {appbuilder => python}/utils/collector.py (100%) rename {appbuilder => python}/utils/func_utils.py (100%) rename {appbuilder => python}/utils/json_schema_to_model.py (100%) rename {appbuilder => python}/utils/logger_util.py (100%) rename {appbuilder => python}/utils/model_util.py (100%) rename {appbuilder => python}/utils/sse_util.py (100%) rename {appbuilder => python}/utils/trace/__init__.py (100%) rename {appbuilder => python}/utils/trace/_function.py (100%) rename {appbuilder => python}/utils/trace/phoenix_wrapper.py (100%) rename {appbuilder => python}/utils/trace/tracer.py (100%) rename {appbuilder => python}/utils/trace/tracer_wrapper.py (100%) diff --git a/README.md b/README.md index c32e48c08..154f32cf4 100644 --- a/README.md +++ b/README.md @@ -51,23 +51,23 @@ AppBuilder-SDK不仅提供了百度智能云提供的基础能力组件,同时 | 阶段 |组件名称 | 组件类型 |组件链接 | |--------|--------|--------|---| -| 文档解析 | 文档矫正增强 (DocCropEnhance) | 基础能力组件 | [链接](./appbuilder/core/components/doc_crop_enhance/README.md) | -| 文档解析 | 文档格式转换 (DocFormatConverter) | 基础能力组件 | [链接](./appbuilder/core/components/doc_format_converter/README.md)| -| 文档解析 | 文档解析(DocParser)| 基础能力组件 | [链接](./appbuilder/core/components/doc_parser/README.md) | -| 文档解析 | 表格抽取组件(ExtractTableFromDoc)| 基础能力组件 | [链接](./appbuilder/core/components/extract_table/README.md) | -| 文档解析 | 通用文字识别-高精度版(GeneralOCR)| 基础能力组件 | [链接](./appbuilder/core/components/general_ocr/README.md) | -| 文档切片 | 文档切分(DocSplitter)| 基础能力组件 | [链接](./appbuilder/core/components/doc_splitter/README.md) | -| 切片向量化 | 向量计算(Embedding) | 基础能力组件 | [链接](./appbuilder/core/components/embeddings/README.md) | -| 索引构建及切片召回 | 向量检索-VectorDB(BaiduVectorDBRetriever) | 基础能力组件 | [链接](./appbuilder/core/components/retriever/baidu_vdb/README.md) | -| 索引构建及切片召回 | 向量检索-BES(BaiduElasticSearchRetriever) | 基础能力组件 | [链接](./appbuilder/core/components/retriever/bes/README.md) | -| 文档切片及答案生成 | 问答对挖掘(QAPairMining)| 高级能力组件 | [链接](./appbuilder/core/components/llms/qa_pair_mining/README.md) | -| 文档切片及答案生成 | 相似问生成(SimilarQuestion)| 高级能力组件 | [链接](./appbuilder/core/components/llms/similar_question/README.md) | -| 答案生成| 标签抽取(TagExtraction)| 高级能力组件 | [链接](./appbuilder/core/components/llms/tag_extraction/README.md) | -| 答案生成 | 复杂Query判定(IsComplexQuery)| 高级能力组件 | [链接](./appbuilder/core/components/llms/is_complex_query/README.md) | -| 答案生成 | 复杂Query分解(QueryDecomposition)| 高级能力组件 | [链接](./appbuilder/core/components/llms/query_decomposition/README.md) | -| 答案生成 | 多轮改写 (QueryRewrite)| 高级能力组件 | [链接](./appbuilder/core/components/llms/query_rewrite/README.md) | -| 答案生成 | 阅读理解问答(MRC)| 高级能力组件 | [链接](./appbuilder/core/components/llms/mrc/README.md) | -| 答案生成 | 幻觉检测(Hallucination Detection)| 高级能力组件 | [链接](./appbuilder/core/components/llms/hallucination_detection/README.md) | +| 文档解析 | 文档矫正增强 (DocCropEnhance) | 基础能力组件 | [链接](./python/core/components/doc_crop_enhance/README.md) | +| 文档解析 | 文档格式转换 (DocFormatConverter) | 基础能力组件 | [链接](./python/core/components/doc_format_converter/README.md)| +| 文档解析 | 文档解析(DocParser)| 基础能力组件 | [链接](./python/core/components/doc_parser/README.md) | +| 文档解析 | 表格抽取组件(ExtractTableFromDoc)| 基础能力组件 | [链接](./python/core/components/extract_table/README.md) | +| 文档解析 | 通用文字识别-高精度版(GeneralOCR)| 基础能力组件 | [链接](./python/core/components/general_ocr/README.md) | +| 文档切片 | 文档切分(DocSplitter)| 基础能力组件 | [链接](./python/core/components/doc_splitter/README.md) | +| 切片向量化 | 向量计算(Embedding) | 基础能力组件 | [链接](./python/core/components/embeddings/README.md) | +| 索引构建及切片召回 | 向量检索-VectorDB(BaiduVectorDBRetriever) | 基础能力组件 | [链接](./python/core/components/retriever/baidu_vdb/README.md) | +| 索引构建及切片召回 | 向量检索-BES(BaiduElasticSearchRetriever) | 基础能力组件 | [链接](./python/core/components/retriever/bes/README.md) | +| 文档切片及答案生成 | 问答对挖掘(QAPairMining)| 高级能力组件 | [链接](./python/core/components/llms/qa_pair_mining/README.md) | +| 文档切片及答案生成 | 相似问生成(SimilarQuestion)| 高级能力组件 | [链接](./python/core/components/llms/similar_question/README.md) | +| 答案生成| 标签抽取(TagExtraction)| 高级能力组件 | [链接](./python/core/components/llms/tag_extraction/README.md) | +| 答案生成 | 复杂Query判定(IsComplexQuery)| 高级能力组件 | [链接](./python/core/components/llms/is_complex_query/README.md) | +| 答案生成 | 复杂Query分解(QueryDecomposition)| 高级能力组件 | [链接](./python/core/components/llms/query_decomposition/README.md) | +| 答案生成 | 多轮改写 (QueryRewrite)| 高级能力组件 | [链接](./python/core/components/llms/query_rewrite/README.md) | +| 答案生成 | 阅读理解问答(MRC)| 高级能力组件 | [链接](./python/core/components/llms/mrc/README.md) | +| 答案生成 | 幻觉检测(Hallucination Detection)| 高级能力组件 | [链接](./python/core/components/llms/hallucination_detection/README.md) | > 功能预告:在AppBuiler-SDK 1.0.0版本中,AppBuilder-SDK可联动AppBuilder平台,自定义离线与在线处理的能力及Pipeline,构建更加灵活、可沉淀、可复用的产业级`RAG`应用,敬请期待 diff --git a/appbuilder/utils/chainlit.md b/appbuilder/utils/chainlit.md deleted file mode 100644 index d9ebfc4d8..000000000 --- a/appbuilder/utils/chainlit.md +++ /dev/null @@ -1,24 +0,0 @@ -# 欢迎使用AppBuilder-SDK🚀 -您好,欢迎您体验AppBuilder-SDK的Chainlit可视化服务化功能。 - -## 什么是AppBuilder-SDK👋 -百度智能云千帆AppBuilder-SDK是[百度智能云千帆AppBuilder](https://appbuilder.cloud.baidu.com/)面向AI原生应用开发者提供的一站式开发平台的客户端SDK。 - -我们提供自底向上的:基础组件、流程编排、端到端应用 三类功能。使用百度智能云千帆AppBuilder-SDK,你可以: - -- 配合百度智能云千帆AppBuilder平台[网页端](https://console.bce.baidu.com/ai_apaas/app),分钟级在本地搭建包含百度工业实践的`端到端的AI原生应用` -- 配合 `基础组件` & `流程编排`,积木式搭建个性化的Assistant + FunctionCall应用 -- 提供 `API调用` & `交互式窗口` 两种服务化部署方式,支持快速上云,平滑嵌入到你的产品中 - -## 如何使用AppBuilder-SDK & Chainlit可视化功能💻 -当前SDK中的`AgentRuntime`模块基于Chainlit实现了基础的可视化功能,支持AppBuilderClient + 能力组件实现可视化交互。 -- `chainlit_demo`接口支持基础组件的简单交互 -- `chainlit_agent`接口支持AppBuilderClient的进阶交互,提供新建会话和上传文件的功能 - -如果对于可视化有更多需求,可以参考AppBuilder SDK中的代码,基于Chainlit进行二次开发。 - -## 链接 🔗 -- [AppBuilder官网](https://appbuilder.cloud.baidu.com/) -- [AppBuilder-SDK开源代码仓库](https://github.com/baidubce/app-builder) -- [AppBuilder文档中心](https://cloud.baidu.com/doc/AppBuilder/index.html) -- [Chainlit文档中心](https://docs.chainlit.io/get-started/overview) \ No newline at end of file diff --git a/appbuilder/__init__.py b/python/__init__.py similarity index 100% rename from appbuilder/__init__.py rename to python/__init__.py diff --git a/appbuilder/core/__init__.py b/python/core/__init__.py similarity index 100% rename from appbuilder/core/__init__.py rename to python/core/__init__.py diff --git a/appbuilder/core/_client.py b/python/core/_client.py similarity index 100% rename from appbuilder/core/_client.py rename to python/core/_client.py diff --git a/appbuilder/core/_exception.py b/python/core/_exception.py similarity index 100% rename from appbuilder/core/_exception.py rename to python/core/_exception.py diff --git a/appbuilder/core/_session.py b/python/core/_session.py similarity index 100% rename from appbuilder/core/_session.py rename to python/core/_session.py diff --git a/appbuilder/core/agent.py b/python/core/agent.py similarity index 100% rename from appbuilder/core/agent.py rename to python/core/agent.py diff --git a/appbuilder/core/assistant/__init__.py b/python/core/assistant/__init__.py similarity index 100% rename from appbuilder/core/assistant/__init__.py rename to python/core/assistant/__init__.py diff --git a/appbuilder/core/assistant/assistants/__init__.py b/python/core/assistant/assistants/__init__.py similarity index 100% rename from appbuilder/core/assistant/assistants/__init__.py rename to python/core/assistant/assistants/__init__.py diff --git a/appbuilder/core/assistant/assistants/assistants.py b/python/core/assistant/assistants/assistants.py similarity index 100% rename from appbuilder/core/assistant/assistants/assistants.py rename to python/core/assistant/assistants/assistants.py diff --git a/appbuilder/core/assistant/assistants/files.py b/python/core/assistant/assistants/files.py similarity index 100% rename from appbuilder/core/assistant/assistants/files.py rename to python/core/assistant/assistants/files.py diff --git a/appbuilder/core/assistant/base.py b/python/core/assistant/base.py similarity index 100% rename from appbuilder/core/assistant/base.py rename to python/core/assistant/base.py diff --git a/appbuilder/core/assistant/threads/__init__.py b/python/core/assistant/threads/__init__.py similarity index 100% rename from appbuilder/core/assistant/threads/__init__.py rename to python/core/assistant/threads/__init__.py diff --git a/appbuilder/core/assistant/threads/messages/__init__.py b/python/core/assistant/threads/messages/__init__.py similarity index 100% rename from appbuilder/core/assistant/threads/messages/__init__.py rename to python/core/assistant/threads/messages/__init__.py diff --git a/appbuilder/core/assistant/threads/messages/messages.py b/python/core/assistant/threads/messages/messages.py similarity index 100% rename from appbuilder/core/assistant/threads/messages/messages.py rename to python/core/assistant/threads/messages/messages.py diff --git a/appbuilder/core/assistant/threads/runs/__init__.py b/python/core/assistant/threads/runs/__init__.py similarity index 100% rename from appbuilder/core/assistant/threads/runs/__init__.py rename to python/core/assistant/threads/runs/__init__.py diff --git a/appbuilder/core/assistant/threads/runs/runs.py b/python/core/assistant/threads/runs/runs.py similarity index 100% rename from appbuilder/core/assistant/threads/runs/runs.py rename to python/core/assistant/threads/runs/runs.py diff --git a/appbuilder/core/assistant/threads/runs/steps.py b/python/core/assistant/threads/runs/steps.py similarity index 100% rename from appbuilder/core/assistant/threads/runs/steps.py rename to python/core/assistant/threads/runs/steps.py diff --git a/appbuilder/core/assistant/threads/runs/stream_helper.py b/python/core/assistant/threads/runs/stream_helper.py similarity index 100% rename from appbuilder/core/assistant/threads/runs/stream_helper.py rename to python/core/assistant/threads/runs/stream_helper.py diff --git a/appbuilder/core/assistant/threads/threads.py b/python/core/assistant/threads/threads.py similarity index 100% rename from appbuilder/core/assistant/threads/threads.py rename to python/core/assistant/threads/threads.py diff --git a/appbuilder/core/assistant/type/__init__.py b/python/core/assistant/type/__init__.py similarity index 100% rename from appbuilder/core/assistant/type/__init__.py rename to python/core/assistant/type/__init__.py diff --git a/appbuilder/core/assistant/type/assistant_type.py b/python/core/assistant/type/assistant_type.py similarity index 100% rename from appbuilder/core/assistant/type/assistant_type.py rename to python/core/assistant/type/assistant_type.py diff --git a/appbuilder/core/assistant/type/public_type.py b/python/core/assistant/type/public_type.py similarity index 100% rename from appbuilder/core/assistant/type/public_type.py rename to python/core/assistant/type/public_type.py diff --git a/appbuilder/core/assistant/type/thread_type.py b/python/core/assistant/type/thread_type.py similarity index 100% rename from appbuilder/core/assistant/type/thread_type.py rename to python/core/assistant/type/thread_type.py diff --git a/appbuilder/core/component.py b/python/core/component.py similarity index 100% rename from appbuilder/core/component.py rename to python/core/component.py diff --git a/appbuilder/core/components/__init__.py b/python/core/components/__init__.py similarity index 100% rename from appbuilder/core/components/__init__.py rename to python/core/components/__init__.py diff --git a/appbuilder/core/components/animal_recognize/README.md b/python/core/components/animal_recognize/README.md similarity index 100% rename from appbuilder/core/components/animal_recognize/README.md rename to python/core/components/animal_recognize/README.md diff --git a/appbuilder/core/components/animal_recognize/__init__.py b/python/core/components/animal_recognize/__init__.py similarity index 100% rename from appbuilder/core/components/animal_recognize/__init__.py rename to python/core/components/animal_recognize/__init__.py diff --git a/appbuilder/core/components/animal_recognize/component.py b/python/core/components/animal_recognize/component.py similarity index 100% rename from appbuilder/core/components/animal_recognize/component.py rename to python/core/components/animal_recognize/component.py diff --git a/appbuilder/core/components/animal_recognize/model.py b/python/core/components/animal_recognize/model.py similarity index 100% rename from appbuilder/core/components/animal_recognize/model.py rename to python/core/components/animal_recognize/model.py diff --git a/appbuilder/core/components/asr/README.md b/python/core/components/asr/README.md similarity index 100% rename from appbuilder/core/components/asr/README.md rename to python/core/components/asr/README.md diff --git a/appbuilder/core/components/asr/__init__.py b/python/core/components/asr/__init__.py similarity index 100% rename from appbuilder/core/components/asr/__init__.py rename to python/core/components/asr/__init__.py diff --git a/appbuilder/core/components/asr/component.py b/python/core/components/asr/component.py similarity index 100% rename from appbuilder/core/components/asr/component.py rename to python/core/components/asr/component.py diff --git a/appbuilder/core/components/asr/model.py b/python/core/components/asr/model.py similarity index 100% rename from appbuilder/core/components/asr/model.py rename to python/core/components/asr/model.py diff --git a/appbuilder/core/components/dish_recognize/README.md b/python/core/components/dish_recognize/README.md similarity index 100% rename from appbuilder/core/components/dish_recognize/README.md rename to python/core/components/dish_recognize/README.md diff --git a/appbuilder/core/components/dish_recognize/__init__.py b/python/core/components/dish_recognize/__init__.py similarity index 100% rename from appbuilder/core/components/dish_recognize/__init__.py rename to python/core/components/dish_recognize/__init__.py diff --git a/appbuilder/core/components/dish_recognize/component.py b/python/core/components/dish_recognize/component.py similarity index 100% rename from appbuilder/core/components/dish_recognize/component.py rename to python/core/components/dish_recognize/component.py diff --git a/appbuilder/core/components/dish_recognize/model.py b/python/core/components/dish_recognize/model.py similarity index 100% rename from appbuilder/core/components/dish_recognize/model.py rename to python/core/components/dish_recognize/model.py diff --git a/appbuilder/core/components/doc_crop_enhance/README.md b/python/core/components/doc_crop_enhance/README.md similarity index 100% rename from appbuilder/core/components/doc_crop_enhance/README.md rename to python/core/components/doc_crop_enhance/README.md diff --git a/appbuilder/core/components/doc_crop_enhance/__init__.py b/python/core/components/doc_crop_enhance/__init__.py similarity index 100% rename from appbuilder/core/components/doc_crop_enhance/__init__.py rename to python/core/components/doc_crop_enhance/__init__.py diff --git a/appbuilder/core/components/doc_crop_enhance/component.py b/python/core/components/doc_crop_enhance/component.py similarity index 100% rename from appbuilder/core/components/doc_crop_enhance/component.py rename to python/core/components/doc_crop_enhance/component.py diff --git a/appbuilder/core/components/doc_crop_enhance/model.py b/python/core/components/doc_crop_enhance/model.py similarity index 100% rename from appbuilder/core/components/doc_crop_enhance/model.py rename to python/core/components/doc_crop_enhance/model.py diff --git a/appbuilder/core/components/doc_format_converter/README.md b/python/core/components/doc_format_converter/README.md similarity index 100% rename from appbuilder/core/components/doc_format_converter/README.md rename to python/core/components/doc_format_converter/README.md diff --git a/appbuilder/core/components/doc_format_converter/__init__.py b/python/core/components/doc_format_converter/__init__.py similarity index 100% rename from appbuilder/core/components/doc_format_converter/__init__.py rename to python/core/components/doc_format_converter/__init__.py diff --git a/appbuilder/core/components/doc_format_converter/component.py b/python/core/components/doc_format_converter/component.py similarity index 100% rename from appbuilder/core/components/doc_format_converter/component.py rename to python/core/components/doc_format_converter/component.py diff --git a/appbuilder/core/components/doc_format_converter/model.py b/python/core/components/doc_format_converter/model.py similarity index 100% rename from appbuilder/core/components/doc_format_converter/model.py rename to python/core/components/doc_format_converter/model.py diff --git a/appbuilder/core/components/doc_parser/README.md b/python/core/components/doc_parser/README.md similarity index 100% rename from appbuilder/core/components/doc_parser/README.md rename to python/core/components/doc_parser/README.md diff --git a/appbuilder/core/components/doc_parser/__init__.py b/python/core/components/doc_parser/__init__.py similarity index 100% rename from appbuilder/core/components/doc_parser/__init__.py rename to python/core/components/doc_parser/__init__.py diff --git a/appbuilder/core/components/doc_parser/base.py b/python/core/components/doc_parser/base.py similarity index 100% rename from appbuilder/core/components/doc_parser/base.py rename to python/core/components/doc_parser/base.py diff --git a/appbuilder/core/components/doc_parser/doc_parser.py b/python/core/components/doc_parser/doc_parser.py similarity index 100% rename from appbuilder/core/components/doc_parser/doc_parser.py rename to python/core/components/doc_parser/doc_parser.py diff --git a/appbuilder/core/components/doc_splitter/README.md b/python/core/components/doc_splitter/README.md similarity index 100% rename from appbuilder/core/components/doc_splitter/README.md rename to python/core/components/doc_splitter/README.md diff --git a/appbuilder/core/components/doc_splitter/__init__.py b/python/core/components/doc_splitter/__init__.py similarity index 100% rename from appbuilder/core/components/doc_splitter/__init__.py rename to python/core/components/doc_splitter/__init__.py diff --git a/appbuilder/core/components/doc_splitter/doc_splitter.py b/python/core/components/doc_splitter/doc_splitter.py similarity index 100% rename from appbuilder/core/components/doc_splitter/doc_splitter.py rename to python/core/components/doc_splitter/doc_splitter.py diff --git a/appbuilder/core/components/document_understanding/README.md b/python/core/components/document_understanding/README.md similarity index 100% rename from appbuilder/core/components/document_understanding/README.md rename to python/core/components/document_understanding/README.md diff --git a/appbuilder/core/components/document_understanding/__init__.py b/python/core/components/document_understanding/__init__.py similarity index 100% rename from appbuilder/core/components/document_understanding/__init__.py rename to python/core/components/document_understanding/__init__.py diff --git a/appbuilder/core/components/document_understanding/base.py b/python/core/components/document_understanding/base.py similarity index 100% rename from appbuilder/core/components/document_understanding/base.py rename to python/core/components/document_understanding/base.py diff --git a/appbuilder/core/components/document_understanding/component.py b/python/core/components/document_understanding/component.py similarity index 100% rename from appbuilder/core/components/document_understanding/component.py rename to python/core/components/document_understanding/component.py diff --git a/appbuilder/core/components/embeddings/README.md b/python/core/components/embeddings/README.md similarity index 100% rename from appbuilder/core/components/embeddings/README.md rename to python/core/components/embeddings/README.md diff --git a/appbuilder/core/components/embeddings/__init__.py b/python/core/components/embeddings/__init__.py similarity index 100% rename from appbuilder/core/components/embeddings/__init__.py rename to python/core/components/embeddings/__init__.py diff --git a/appbuilder/core/components/embeddings/base.py b/python/core/components/embeddings/base.py similarity index 100% rename from appbuilder/core/components/embeddings/base.py rename to python/core/components/embeddings/base.py diff --git a/appbuilder/core/components/embeddings/component.py b/python/core/components/embeddings/component.py similarity index 100% rename from appbuilder/core/components/embeddings/component.py rename to python/core/components/embeddings/component.py diff --git a/appbuilder/core/components/excel2figure/README.md b/python/core/components/excel2figure/README.md similarity index 100% rename from appbuilder/core/components/excel2figure/README.md rename to python/core/components/excel2figure/README.md diff --git a/appbuilder/core/components/excel2figure/__init__.py b/python/core/components/excel2figure/__init__.py similarity index 100% rename from appbuilder/core/components/excel2figure/__init__.py rename to python/core/components/excel2figure/__init__.py diff --git a/appbuilder/core/components/excel2figure/base.py b/python/core/components/excel2figure/base.py similarity index 100% rename from appbuilder/core/components/excel2figure/base.py rename to python/core/components/excel2figure/base.py diff --git a/appbuilder/core/components/excel2figure/component.py b/python/core/components/excel2figure/component.py similarity index 100% rename from appbuilder/core/components/excel2figure/component.py rename to python/core/components/excel2figure/component.py diff --git a/appbuilder/core/components/extract_table/README.md b/python/core/components/extract_table/README.md similarity index 100% rename from appbuilder/core/components/extract_table/README.md rename to python/core/components/extract_table/README.md diff --git a/appbuilder/core/components/extract_table/__init__.py b/python/core/components/extract_table/__init__.py similarity index 100% rename from appbuilder/core/components/extract_table/__init__.py rename to python/core/components/extract_table/__init__.py diff --git a/appbuilder/core/components/extract_table/component.py b/python/core/components/extract_table/component.py similarity index 100% rename from appbuilder/core/components/extract_table/component.py rename to python/core/components/extract_table/component.py diff --git a/appbuilder/core/components/gbi/__init__.py b/python/core/components/gbi/__init__.py similarity index 100% rename from appbuilder/core/components/gbi/__init__.py rename to python/core/components/gbi/__init__.py diff --git a/appbuilder/core/components/gbi/basic.py b/python/core/components/gbi/basic.py similarity index 100% rename from appbuilder/core/components/gbi/basic.py rename to python/core/components/gbi/basic.py diff --git a/appbuilder/core/components/gbi/nl2sql/README.md b/python/core/components/gbi/nl2sql/README.md similarity index 100% rename from appbuilder/core/components/gbi/nl2sql/README.md rename to python/core/components/gbi/nl2sql/README.md diff --git a/appbuilder/core/components/gbi/nl2sql/__init__.py b/python/core/components/gbi/nl2sql/__init__.py similarity index 100% rename from appbuilder/core/components/gbi/nl2sql/__init__.py rename to python/core/components/gbi/nl2sql/__init__.py diff --git a/appbuilder/core/components/gbi/nl2sql/base.py b/python/core/components/gbi/nl2sql/base.py similarity index 100% rename from appbuilder/core/components/gbi/nl2sql/base.py rename to python/core/components/gbi/nl2sql/base.py diff --git a/appbuilder/core/components/gbi/nl2sql/component.py b/python/core/components/gbi/nl2sql/component.py similarity index 100% rename from appbuilder/core/components/gbi/nl2sql/component.py rename to python/core/components/gbi/nl2sql/component.py diff --git a/appbuilder/core/components/gbi/select_table/README.md b/python/core/components/gbi/select_table/README.md similarity index 100% rename from appbuilder/core/components/gbi/select_table/README.md rename to python/core/components/gbi/select_table/README.md diff --git a/appbuilder/core/components/gbi/select_table/__init__.py b/python/core/components/gbi/select_table/__init__.py similarity index 100% rename from appbuilder/core/components/gbi/select_table/__init__.py rename to python/core/components/gbi/select_table/__init__.py diff --git a/appbuilder/core/components/gbi/select_table/base.py b/python/core/components/gbi/select_table/base.py similarity index 100% rename from appbuilder/core/components/gbi/select_table/base.py rename to python/core/components/gbi/select_table/base.py diff --git a/appbuilder/core/components/gbi/select_table/component.py b/python/core/components/gbi/select_table/component.py similarity index 100% rename from appbuilder/core/components/gbi/select_table/component.py rename to python/core/components/gbi/select_table/component.py diff --git a/appbuilder/core/components/general_ocr/README.md b/python/core/components/general_ocr/README.md similarity index 100% rename from appbuilder/core/components/general_ocr/README.md rename to python/core/components/general_ocr/README.md diff --git a/appbuilder/core/components/general_ocr/__init__.py b/python/core/components/general_ocr/__init__.py similarity index 100% rename from appbuilder/core/components/general_ocr/__init__.py rename to python/core/components/general_ocr/__init__.py diff --git a/appbuilder/core/components/general_ocr/component.py b/python/core/components/general_ocr/component.py similarity index 100% rename from appbuilder/core/components/general_ocr/component.py rename to python/core/components/general_ocr/component.py diff --git a/appbuilder/core/components/general_ocr/model.py b/python/core/components/general_ocr/model.py similarity index 100% rename from appbuilder/core/components/general_ocr/model.py rename to python/core/components/general_ocr/model.py diff --git a/appbuilder/core/components/handwrite_ocr/README.md b/python/core/components/handwrite_ocr/README.md similarity index 100% rename from appbuilder/core/components/handwrite_ocr/README.md rename to python/core/components/handwrite_ocr/README.md diff --git a/appbuilder/core/components/handwrite_ocr/__init__.py b/python/core/components/handwrite_ocr/__init__.py similarity index 100% rename from appbuilder/core/components/handwrite_ocr/__init__.py rename to python/core/components/handwrite_ocr/__init__.py diff --git a/appbuilder/core/components/handwrite_ocr/component.py b/python/core/components/handwrite_ocr/component.py similarity index 100% rename from appbuilder/core/components/handwrite_ocr/component.py rename to python/core/components/handwrite_ocr/component.py diff --git a/appbuilder/core/components/handwrite_ocr/model.py b/python/core/components/handwrite_ocr/model.py similarity index 100% rename from appbuilder/core/components/handwrite_ocr/model.py rename to python/core/components/handwrite_ocr/model.py diff --git a/appbuilder/core/components/image_understand/README.md b/python/core/components/image_understand/README.md similarity index 100% rename from appbuilder/core/components/image_understand/README.md rename to python/core/components/image_understand/README.md diff --git a/appbuilder/core/components/image_understand/__init__.py b/python/core/components/image_understand/__init__.py similarity index 100% rename from appbuilder/core/components/image_understand/__init__.py rename to python/core/components/image_understand/__init__.py diff --git a/appbuilder/core/components/image_understand/component.py b/python/core/components/image_understand/component.py similarity index 100% rename from appbuilder/core/components/image_understand/component.py rename to python/core/components/image_understand/component.py diff --git a/appbuilder/core/components/image_understand/model.py b/python/core/components/image_understand/model.py similarity index 100% rename from appbuilder/core/components/image_understand/model.py rename to python/core/components/image_understand/model.py diff --git a/appbuilder/core/components/landmark_recognize/README.md b/python/core/components/landmark_recognize/README.md similarity index 100% rename from appbuilder/core/components/landmark_recognize/README.md rename to python/core/components/landmark_recognize/README.md diff --git a/appbuilder/core/components/landmark_recognize/__init__.py b/python/core/components/landmark_recognize/__init__.py similarity index 100% rename from appbuilder/core/components/landmark_recognize/__init__.py rename to python/core/components/landmark_recognize/__init__.py diff --git a/appbuilder/core/components/landmark_recognize/component.py b/python/core/components/landmark_recognize/component.py similarity index 100% rename from appbuilder/core/components/landmark_recognize/component.py rename to python/core/components/landmark_recognize/component.py diff --git a/appbuilder/core/components/landmark_recognize/model.py b/python/core/components/landmark_recognize/model.py similarity index 100% rename from appbuilder/core/components/landmark_recognize/model.py rename to python/core/components/landmark_recognize/model.py diff --git a/appbuilder/core/components/llms/__init__.py b/python/core/components/llms/__init__.py similarity index 100% rename from appbuilder/core/components/llms/__init__.py rename to python/core/components/llms/__init__.py diff --git a/appbuilder/core/components/llms/base.py b/python/core/components/llms/base.py similarity index 100% rename from appbuilder/core/components/llms/base.py rename to python/core/components/llms/base.py diff --git a/appbuilder/core/components/llms/dialog_summary/README.md b/python/core/components/llms/dialog_summary/README.md similarity index 100% rename from appbuilder/core/components/llms/dialog_summary/README.md rename to python/core/components/llms/dialog_summary/README.md diff --git a/appbuilder/core/components/llms/dialog_summary/__init__.py b/python/core/components/llms/dialog_summary/__init__.py similarity index 100% rename from appbuilder/core/components/llms/dialog_summary/__init__.py rename to python/core/components/llms/dialog_summary/__init__.py diff --git a/appbuilder/core/components/llms/dialog_summary/base.py b/python/core/components/llms/dialog_summary/base.py similarity index 100% rename from appbuilder/core/components/llms/dialog_summary/base.py rename to python/core/components/llms/dialog_summary/base.py diff --git a/appbuilder/core/components/llms/dialog_summary/component.py b/python/core/components/llms/dialog_summary/component.py similarity index 100% rename from appbuilder/core/components/llms/dialog_summary/component.py rename to python/core/components/llms/dialog_summary/component.py diff --git a/appbuilder/core/components/llms/hallucination_detection/README.md b/python/core/components/llms/hallucination_detection/README.md similarity index 100% rename from appbuilder/core/components/llms/hallucination_detection/README.md rename to python/core/components/llms/hallucination_detection/README.md diff --git a/appbuilder/core/components/llms/hallucination_detection/__init__.py b/python/core/components/llms/hallucination_detection/__init__.py similarity index 100% rename from appbuilder/core/components/llms/hallucination_detection/__init__.py rename to python/core/components/llms/hallucination_detection/__init__.py diff --git a/appbuilder/core/components/llms/hallucination_detection/base.py b/python/core/components/llms/hallucination_detection/base.py similarity index 100% rename from appbuilder/core/components/llms/hallucination_detection/base.py rename to python/core/components/llms/hallucination_detection/base.py diff --git a/appbuilder/core/components/llms/hallucination_detection/component.py b/python/core/components/llms/hallucination_detection/component.py similarity index 100% rename from appbuilder/core/components/llms/hallucination_detection/component.py rename to python/core/components/llms/hallucination_detection/component.py diff --git a/appbuilder/core/components/llms/is_complex_query/README.md b/python/core/components/llms/is_complex_query/README.md similarity index 100% rename from appbuilder/core/components/llms/is_complex_query/README.md rename to python/core/components/llms/is_complex_query/README.md diff --git a/appbuilder/core/components/llms/is_complex_query/__init__.py b/python/core/components/llms/is_complex_query/__init__.py similarity index 100% rename from appbuilder/core/components/llms/is_complex_query/__init__.py rename to python/core/components/llms/is_complex_query/__init__.py diff --git a/appbuilder/core/components/llms/is_complex_query/base.py b/python/core/components/llms/is_complex_query/base.py similarity index 100% rename from appbuilder/core/components/llms/is_complex_query/base.py rename to python/core/components/llms/is_complex_query/base.py diff --git a/appbuilder/core/components/llms/is_complex_query/component.py b/python/core/components/llms/is_complex_query/component.py similarity index 100% rename from appbuilder/core/components/llms/is_complex_query/component.py rename to python/core/components/llms/is_complex_query/component.py diff --git a/appbuilder/core/components/llms/mrc/README.md b/python/core/components/llms/mrc/README.md similarity index 100% rename from appbuilder/core/components/llms/mrc/README.md rename to python/core/components/llms/mrc/README.md diff --git a/appbuilder/core/components/llms/mrc/__init__.py b/python/core/components/llms/mrc/__init__.py similarity index 100% rename from appbuilder/core/components/llms/mrc/__init__.py rename to python/core/components/llms/mrc/__init__.py diff --git a/appbuilder/core/components/llms/mrc/base.py b/python/core/components/llms/mrc/base.py similarity index 100% rename from appbuilder/core/components/llms/mrc/base.py rename to python/core/components/llms/mrc/base.py diff --git a/appbuilder/core/components/llms/mrc/component.py b/python/core/components/llms/mrc/component.py similarity index 100% rename from appbuilder/core/components/llms/mrc/component.py rename to python/core/components/llms/mrc/component.py diff --git a/appbuilder/core/components/llms/nl2pandas/README.md b/python/core/components/llms/nl2pandas/README.md similarity index 100% rename from appbuilder/core/components/llms/nl2pandas/README.md rename to python/core/components/llms/nl2pandas/README.md diff --git a/appbuilder/core/components/llms/nl2pandas/__init__.py b/python/core/components/llms/nl2pandas/__init__.py similarity index 100% rename from appbuilder/core/components/llms/nl2pandas/__init__.py rename to python/core/components/llms/nl2pandas/__init__.py diff --git a/appbuilder/core/components/llms/nl2pandas/base.py b/python/core/components/llms/nl2pandas/base.py similarity index 100% rename from appbuilder/core/components/llms/nl2pandas/base.py rename to python/core/components/llms/nl2pandas/base.py diff --git a/appbuilder/core/components/llms/nl2pandas/component.py b/python/core/components/llms/nl2pandas/component.py similarity index 100% rename from appbuilder/core/components/llms/nl2pandas/component.py rename to python/core/components/llms/nl2pandas/component.py diff --git a/appbuilder/core/components/llms/oral_query_generation/README.md b/python/core/components/llms/oral_query_generation/README.md similarity index 100% rename from appbuilder/core/components/llms/oral_query_generation/README.md rename to python/core/components/llms/oral_query_generation/README.md diff --git a/appbuilder/core/components/llms/oral_query_generation/__init__.py b/python/core/components/llms/oral_query_generation/__init__.py similarity index 100% rename from appbuilder/core/components/llms/oral_query_generation/__init__.py rename to python/core/components/llms/oral_query_generation/__init__.py diff --git a/appbuilder/core/components/llms/oral_query_generation/base.py b/python/core/components/llms/oral_query_generation/base.py similarity index 100% rename from appbuilder/core/components/llms/oral_query_generation/base.py rename to python/core/components/llms/oral_query_generation/base.py diff --git a/appbuilder/core/components/llms/oral_query_generation/component.py b/python/core/components/llms/oral_query_generation/component.py similarity index 100% rename from appbuilder/core/components/llms/oral_query_generation/component.py rename to python/core/components/llms/oral_query_generation/component.py diff --git a/appbuilder/core/components/llms/playground/README.md b/python/core/components/llms/playground/README.md similarity index 100% rename from appbuilder/core/components/llms/playground/README.md rename to python/core/components/llms/playground/README.md diff --git a/appbuilder/core/components/llms/playground/__init__.py b/python/core/components/llms/playground/__init__.py similarity index 100% rename from appbuilder/core/components/llms/playground/__init__.py rename to python/core/components/llms/playground/__init__.py diff --git a/appbuilder/core/components/llms/playground/base.py b/python/core/components/llms/playground/base.py similarity index 100% rename from appbuilder/core/components/llms/playground/base.py rename to python/core/components/llms/playground/base.py diff --git a/appbuilder/core/components/llms/playground/component.py b/python/core/components/llms/playground/component.py similarity index 100% rename from appbuilder/core/components/llms/playground/component.py rename to python/core/components/llms/playground/component.py diff --git a/appbuilder/core/components/llms/qa_pair_mining/README.md b/python/core/components/llms/qa_pair_mining/README.md similarity index 100% rename from appbuilder/core/components/llms/qa_pair_mining/README.md rename to python/core/components/llms/qa_pair_mining/README.md diff --git a/appbuilder/core/components/llms/qa_pair_mining/__init__.py b/python/core/components/llms/qa_pair_mining/__init__.py similarity index 100% rename from appbuilder/core/components/llms/qa_pair_mining/__init__.py rename to python/core/components/llms/qa_pair_mining/__init__.py diff --git a/appbuilder/core/components/llms/qa_pair_mining/base.py b/python/core/components/llms/qa_pair_mining/base.py similarity index 100% rename from appbuilder/core/components/llms/qa_pair_mining/base.py rename to python/core/components/llms/qa_pair_mining/base.py diff --git a/appbuilder/core/components/llms/qa_pair_mining/component.py b/python/core/components/llms/qa_pair_mining/component.py similarity index 100% rename from appbuilder/core/components/llms/qa_pair_mining/component.py rename to python/core/components/llms/qa_pair_mining/component.py diff --git a/appbuilder/core/components/llms/query_decomposition/README.md b/python/core/components/llms/query_decomposition/README.md similarity index 100% rename from appbuilder/core/components/llms/query_decomposition/README.md rename to python/core/components/llms/query_decomposition/README.md diff --git a/appbuilder/core/components/llms/query_decomposition/__init__.py b/python/core/components/llms/query_decomposition/__init__.py similarity index 100% rename from appbuilder/core/components/llms/query_decomposition/__init__.py rename to python/core/components/llms/query_decomposition/__init__.py diff --git a/appbuilder/core/components/llms/query_decomposition/base.py b/python/core/components/llms/query_decomposition/base.py similarity index 100% rename from appbuilder/core/components/llms/query_decomposition/base.py rename to python/core/components/llms/query_decomposition/base.py diff --git a/appbuilder/core/components/llms/query_decomposition/component.py b/python/core/components/llms/query_decomposition/component.py similarity index 100% rename from appbuilder/core/components/llms/query_decomposition/component.py rename to python/core/components/llms/query_decomposition/component.py diff --git a/appbuilder/core/components/llms/query_rewrite/README.md b/python/core/components/llms/query_rewrite/README.md similarity index 100% rename from appbuilder/core/components/llms/query_rewrite/README.md rename to python/core/components/llms/query_rewrite/README.md diff --git a/appbuilder/core/components/llms/query_rewrite/__init__.py b/python/core/components/llms/query_rewrite/__init__.py similarity index 100% rename from appbuilder/core/components/llms/query_rewrite/__init__.py rename to python/core/components/llms/query_rewrite/__init__.py diff --git a/appbuilder/core/components/llms/query_rewrite/base.py b/python/core/components/llms/query_rewrite/base.py similarity index 100% rename from appbuilder/core/components/llms/query_rewrite/base.py rename to python/core/components/llms/query_rewrite/base.py diff --git a/appbuilder/core/components/llms/query_rewrite/component.py b/python/core/components/llms/query_rewrite/component.py similarity index 100% rename from appbuilder/core/components/llms/query_rewrite/component.py rename to python/core/components/llms/query_rewrite/component.py diff --git a/appbuilder/core/components/llms/similar_question/README.md b/python/core/components/llms/similar_question/README.md similarity index 100% rename from appbuilder/core/components/llms/similar_question/README.md rename to python/core/components/llms/similar_question/README.md diff --git a/appbuilder/core/components/llms/similar_question/__init__.py b/python/core/components/llms/similar_question/__init__.py similarity index 100% rename from appbuilder/core/components/llms/similar_question/__init__.py rename to python/core/components/llms/similar_question/__init__.py diff --git a/appbuilder/core/components/llms/similar_question/base.py b/python/core/components/llms/similar_question/base.py similarity index 100% rename from appbuilder/core/components/llms/similar_question/base.py rename to python/core/components/llms/similar_question/base.py diff --git a/appbuilder/core/components/llms/similar_question/component.py b/python/core/components/llms/similar_question/component.py similarity index 100% rename from appbuilder/core/components/llms/similar_question/component.py rename to python/core/components/llms/similar_question/component.py diff --git a/appbuilder/core/components/llms/style_rewrite/README.md b/python/core/components/llms/style_rewrite/README.md similarity index 100% rename from appbuilder/core/components/llms/style_rewrite/README.md rename to python/core/components/llms/style_rewrite/README.md diff --git a/appbuilder/core/components/llms/style_rewrite/__init__.py b/python/core/components/llms/style_rewrite/__init__.py similarity index 100% rename from appbuilder/core/components/llms/style_rewrite/__init__.py rename to python/core/components/llms/style_rewrite/__init__.py diff --git a/appbuilder/core/components/llms/style_rewrite/base.py b/python/core/components/llms/style_rewrite/base.py similarity index 100% rename from appbuilder/core/components/llms/style_rewrite/base.py rename to python/core/components/llms/style_rewrite/base.py diff --git a/appbuilder/core/components/llms/style_rewrite/component.py b/python/core/components/llms/style_rewrite/component.py similarity index 100% rename from appbuilder/core/components/llms/style_rewrite/component.py rename to python/core/components/llms/style_rewrite/component.py diff --git a/appbuilder/core/components/llms/style_writing/README.md b/python/core/components/llms/style_writing/README.md similarity index 100% rename from appbuilder/core/components/llms/style_writing/README.md rename to python/core/components/llms/style_writing/README.md diff --git a/appbuilder/core/components/llms/style_writing/__init__.py b/python/core/components/llms/style_writing/__init__.py similarity index 100% rename from appbuilder/core/components/llms/style_writing/__init__.py rename to python/core/components/llms/style_writing/__init__.py diff --git a/appbuilder/core/components/llms/style_writing/base.py b/python/core/components/llms/style_writing/base.py similarity index 100% rename from appbuilder/core/components/llms/style_writing/base.py rename to python/core/components/llms/style_writing/base.py diff --git a/appbuilder/core/components/llms/style_writing/component.py b/python/core/components/llms/style_writing/component.py similarity index 100% rename from appbuilder/core/components/llms/style_writing/component.py rename to python/core/components/llms/style_writing/component.py diff --git a/appbuilder/core/components/llms/tag_extraction/README.md b/python/core/components/llms/tag_extraction/README.md similarity index 100% rename from appbuilder/core/components/llms/tag_extraction/README.md rename to python/core/components/llms/tag_extraction/README.md diff --git a/appbuilder/core/components/llms/tag_extraction/__init__.py b/python/core/components/llms/tag_extraction/__init__.py similarity index 100% rename from appbuilder/core/components/llms/tag_extraction/__init__.py rename to python/core/components/llms/tag_extraction/__init__.py diff --git a/appbuilder/core/components/llms/tag_extraction/base.py b/python/core/components/llms/tag_extraction/base.py similarity index 100% rename from appbuilder/core/components/llms/tag_extraction/base.py rename to python/core/components/llms/tag_extraction/base.py diff --git a/appbuilder/core/components/llms/tag_extraction/component.py b/python/core/components/llms/tag_extraction/component.py similarity index 100% rename from appbuilder/core/components/llms/tag_extraction/component.py rename to python/core/components/llms/tag_extraction/component.py diff --git a/appbuilder/core/components/matching/README.md b/python/core/components/matching/README.md similarity index 100% rename from appbuilder/core/components/matching/README.md rename to python/core/components/matching/README.md diff --git a/appbuilder/core/components/matching/__init__.py b/python/core/components/matching/__init__.py similarity index 100% rename from appbuilder/core/components/matching/__init__.py rename to python/core/components/matching/__init__.py diff --git a/appbuilder/core/components/matching/base.py b/python/core/components/matching/base.py similarity index 100% rename from appbuilder/core/components/matching/base.py rename to python/core/components/matching/base.py diff --git a/appbuilder/core/components/matching/component.py b/python/core/components/matching/component.py similarity index 100% rename from appbuilder/core/components/matching/component.py rename to python/core/components/matching/component.py diff --git a/appbuilder/core/components/mix_card_ocr/README.md b/python/core/components/mix_card_ocr/README.md similarity index 100% rename from appbuilder/core/components/mix_card_ocr/README.md rename to python/core/components/mix_card_ocr/README.md diff --git a/appbuilder/core/components/mix_card_ocr/__init__.py b/python/core/components/mix_card_ocr/__init__.py similarity index 100% rename from appbuilder/core/components/mix_card_ocr/__init__.py rename to python/core/components/mix_card_ocr/__init__.py diff --git a/appbuilder/core/components/mix_card_ocr/component.py b/python/core/components/mix_card_ocr/component.py similarity index 100% rename from appbuilder/core/components/mix_card_ocr/component.py rename to python/core/components/mix_card_ocr/component.py diff --git a/appbuilder/core/components/mix_card_ocr/model.py b/python/core/components/mix_card_ocr/model.py similarity index 100% rename from appbuilder/core/components/mix_card_ocr/model.py rename to python/core/components/mix_card_ocr/model.py diff --git a/appbuilder/core/components/object_recognize/README.md b/python/core/components/object_recognize/README.md similarity index 100% rename from appbuilder/core/components/object_recognize/README.md rename to python/core/components/object_recognize/README.md diff --git a/appbuilder/core/components/object_recognize/__init__.py b/python/core/components/object_recognize/__init__.py similarity index 100% rename from appbuilder/core/components/object_recognize/__init__.py rename to python/core/components/object_recognize/__init__.py diff --git a/appbuilder/core/components/object_recognize/component.py b/python/core/components/object_recognize/component.py similarity index 100% rename from appbuilder/core/components/object_recognize/component.py rename to python/core/components/object_recognize/component.py diff --git a/appbuilder/core/components/object_recognize/model.py b/python/core/components/object_recognize/model.py similarity index 100% rename from appbuilder/core/components/object_recognize/model.py rename to python/core/components/object_recognize/model.py diff --git a/appbuilder/core/components/plant_recognize/README.md b/python/core/components/plant_recognize/README.md similarity index 100% rename from appbuilder/core/components/plant_recognize/README.md rename to python/core/components/plant_recognize/README.md diff --git a/appbuilder/core/components/plant_recognize/__init__.py b/python/core/components/plant_recognize/__init__.py similarity index 100% rename from appbuilder/core/components/plant_recognize/__init__.py rename to python/core/components/plant_recognize/__init__.py diff --git a/appbuilder/core/components/plant_recognize/component.py b/python/core/components/plant_recognize/component.py similarity index 100% rename from appbuilder/core/components/plant_recognize/component.py rename to python/core/components/plant_recognize/component.py diff --git a/appbuilder/core/components/plant_recognize/model.py b/python/core/components/plant_recognize/model.py similarity index 100% rename from appbuilder/core/components/plant_recognize/model.py rename to python/core/components/plant_recognize/model.py diff --git a/appbuilder/core/components/ppt_generation_from_file/README.md b/python/core/components/ppt_generation_from_file/README.md similarity index 100% rename from appbuilder/core/components/ppt_generation_from_file/README.md rename to python/core/components/ppt_generation_from_file/README.md diff --git a/appbuilder/core/components/ppt_generation_from_file/__init__.py b/python/core/components/ppt_generation_from_file/__init__.py similarity index 100% rename from appbuilder/core/components/ppt_generation_from_file/__init__.py rename to python/core/components/ppt_generation_from_file/__init__.py diff --git a/appbuilder/core/components/ppt_generation_from_file/base.py b/python/core/components/ppt_generation_from_file/base.py similarity index 100% rename from appbuilder/core/components/ppt_generation_from_file/base.py rename to python/core/components/ppt_generation_from_file/base.py diff --git a/appbuilder/core/components/ppt_generation_from_file/component.py b/python/core/components/ppt_generation_from_file/component.py similarity index 100% rename from appbuilder/core/components/ppt_generation_from_file/component.py rename to python/core/components/ppt_generation_from_file/component.py diff --git a/appbuilder/core/components/ppt_generation_from_instruction/README.md b/python/core/components/ppt_generation_from_instruction/README.md similarity index 100% rename from appbuilder/core/components/ppt_generation_from_instruction/README.md rename to python/core/components/ppt_generation_from_instruction/README.md diff --git a/appbuilder/core/components/ppt_generation_from_instruction/__init__.py b/python/core/components/ppt_generation_from_instruction/__init__.py similarity index 100% rename from appbuilder/core/components/ppt_generation_from_instruction/__init__.py rename to python/core/components/ppt_generation_from_instruction/__init__.py diff --git a/appbuilder/core/components/ppt_generation_from_instruction/base.py b/python/core/components/ppt_generation_from_instruction/base.py similarity index 100% rename from appbuilder/core/components/ppt_generation_from_instruction/base.py rename to python/core/components/ppt_generation_from_instruction/base.py diff --git a/appbuilder/core/components/ppt_generation_from_instruction/component.py b/python/core/components/ppt_generation_from_instruction/component.py similarity index 100% rename from appbuilder/core/components/ppt_generation_from_instruction/component.py rename to python/core/components/ppt_generation_from_instruction/component.py diff --git a/appbuilder/core/components/ppt_generation_from_paper/README.md b/python/core/components/ppt_generation_from_paper/README.md similarity index 100% rename from appbuilder/core/components/ppt_generation_from_paper/README.md rename to python/core/components/ppt_generation_from_paper/README.md diff --git a/appbuilder/core/components/ppt_generation_from_paper/__init__.py b/python/core/components/ppt_generation_from_paper/__init__.py similarity index 100% rename from appbuilder/core/components/ppt_generation_from_paper/__init__.py rename to python/core/components/ppt_generation_from_paper/__init__.py diff --git a/appbuilder/core/components/ppt_generation_from_paper/base.py b/python/core/components/ppt_generation_from_paper/base.py similarity index 100% rename from appbuilder/core/components/ppt_generation_from_paper/base.py rename to python/core/components/ppt_generation_from_paper/base.py diff --git a/appbuilder/core/components/ppt_generation_from_paper/component.py b/python/core/components/ppt_generation_from_paper/component.py similarity index 100% rename from appbuilder/core/components/ppt_generation_from_paper/component.py rename to python/core/components/ppt_generation_from_paper/component.py diff --git a/appbuilder/core/components/qrcode_ocr/README.md b/python/core/components/qrcode_ocr/README.md similarity index 100% rename from appbuilder/core/components/qrcode_ocr/README.md rename to python/core/components/qrcode_ocr/README.md diff --git a/appbuilder/core/components/qrcode_ocr/__init__.py b/python/core/components/qrcode_ocr/__init__.py similarity index 100% rename from appbuilder/core/components/qrcode_ocr/__init__.py rename to python/core/components/qrcode_ocr/__init__.py diff --git a/appbuilder/core/components/qrcode_ocr/component.py b/python/core/components/qrcode_ocr/component.py similarity index 100% rename from appbuilder/core/components/qrcode_ocr/component.py rename to python/core/components/qrcode_ocr/component.py diff --git a/appbuilder/core/components/qrcode_ocr/model.py b/python/core/components/qrcode_ocr/model.py similarity index 100% rename from appbuilder/core/components/qrcode_ocr/model.py rename to python/core/components/qrcode_ocr/model.py diff --git a/appbuilder/core/components/rag_with_baidu_search/README.md b/python/core/components/rag_with_baidu_search/README.md similarity index 100% rename from appbuilder/core/components/rag_with_baidu_search/README.md rename to python/core/components/rag_with_baidu_search/README.md diff --git a/appbuilder/core/components/rag_with_baidu_search/__init__.py b/python/core/components/rag_with_baidu_search/__init__.py similarity index 100% rename from appbuilder/core/components/rag_with_baidu_search/__init__.py rename to python/core/components/rag_with_baidu_search/__init__.py diff --git a/appbuilder/core/components/rag_with_baidu_search/component.py b/python/core/components/rag_with_baidu_search/component.py similarity index 100% rename from appbuilder/core/components/rag_with_baidu_search/component.py rename to python/core/components/rag_with_baidu_search/component.py diff --git a/appbuilder/core/components/rag_with_baidu_search/model.py b/python/core/components/rag_with_baidu_search/model.py similarity index 100% rename from appbuilder/core/components/rag_with_baidu_search/model.py rename to python/core/components/rag_with_baidu_search/model.py diff --git a/appbuilder/core/components/rag_with_baidu_search_pro/README.md b/python/core/components/rag_with_baidu_search_pro/README.md similarity index 100% rename from appbuilder/core/components/rag_with_baidu_search_pro/README.md rename to python/core/components/rag_with_baidu_search_pro/README.md diff --git a/appbuilder/core/components/rag_with_baidu_search_pro/__init__.py b/python/core/components/rag_with_baidu_search_pro/__init__.py similarity index 100% rename from appbuilder/core/components/rag_with_baidu_search_pro/__init__.py rename to python/core/components/rag_with_baidu_search_pro/__init__.py diff --git a/appbuilder/core/components/rag_with_baidu_search_pro/component.py b/python/core/components/rag_with_baidu_search_pro/component.py similarity index 100% rename from appbuilder/core/components/rag_with_baidu_search_pro/component.py rename to python/core/components/rag_with_baidu_search_pro/component.py diff --git a/appbuilder/core/components/rag_with_baidu_search_pro/parse_rag_pro_response.py b/python/core/components/rag_with_baidu_search_pro/parse_rag_pro_response.py similarity index 100% rename from appbuilder/core/components/rag_with_baidu_search_pro/parse_rag_pro_response.py rename to python/core/components/rag_with_baidu_search_pro/parse_rag_pro_response.py diff --git a/appbuilder/core/components/retriever/README.md b/python/core/components/retriever/README.md similarity index 100% rename from appbuilder/core/components/retriever/README.md rename to python/core/components/retriever/README.md diff --git a/appbuilder/core/components/retriever/__init__.py b/python/core/components/retriever/__init__.py similarity index 100% rename from appbuilder/core/components/retriever/__init__.py rename to python/core/components/retriever/__init__.py diff --git a/appbuilder/core/components/retriever/baidu_vdb/README.md b/python/core/components/retriever/baidu_vdb/README.md similarity index 100% rename from appbuilder/core/components/retriever/baidu_vdb/README.md rename to python/core/components/retriever/baidu_vdb/README.md diff --git a/appbuilder/core/components/retriever/baidu_vdb/__init__.py b/python/core/components/retriever/baidu_vdb/__init__.py similarity index 100% rename from appbuilder/core/components/retriever/baidu_vdb/__init__.py rename to python/core/components/retriever/baidu_vdb/__init__.py diff --git a/appbuilder/core/components/retriever/baidu_vdb/baiduvdb_retriever.py b/python/core/components/retriever/baidu_vdb/baiduvdb_retriever.py similarity index 100% rename from appbuilder/core/components/retriever/baidu_vdb/baiduvdb_retriever.py rename to python/core/components/retriever/baidu_vdb/baiduvdb_retriever.py diff --git a/appbuilder/core/components/retriever/baidu_vdb/model.py b/python/core/components/retriever/baidu_vdb/model.py similarity index 100% rename from appbuilder/core/components/retriever/baidu_vdb/model.py rename to python/core/components/retriever/baidu_vdb/model.py diff --git a/appbuilder/core/components/retriever/bes/README.md b/python/core/components/retriever/bes/README.md similarity index 100% rename from appbuilder/core/components/retriever/bes/README.md rename to python/core/components/retriever/bes/README.md diff --git a/appbuilder/core/components/retriever/bes/__init__.py b/python/core/components/retriever/bes/__init__.py similarity index 100% rename from appbuilder/core/components/retriever/bes/__init__.py rename to python/core/components/retriever/bes/__init__.py diff --git a/appbuilder/core/components/retriever/bes/bes_retriever.py b/python/core/components/retriever/bes/bes_retriever.py similarity index 100% rename from appbuilder/core/components/retriever/bes/bes_retriever.py rename to python/core/components/retriever/bes/bes_retriever.py diff --git a/appbuilder/core/components/retriever/reranker/README.md b/python/core/components/retriever/reranker/README.md similarity index 100% rename from appbuilder/core/components/retriever/reranker/README.md rename to python/core/components/retriever/reranker/README.md diff --git a/appbuilder/core/components/retriever/reranker/__init__.py b/python/core/components/retriever/reranker/__init__.py similarity index 100% rename from appbuilder/core/components/retriever/reranker/__init__.py rename to python/core/components/retriever/reranker/__init__.py diff --git a/appbuilder/core/components/retriever/reranker/model.py b/python/core/components/retriever/reranker/model.py similarity index 100% rename from appbuilder/core/components/retriever/reranker/model.py rename to python/core/components/retriever/reranker/model.py diff --git a/appbuilder/core/components/retriever/reranker/rerank.py b/python/core/components/retriever/reranker/rerank.py similarity index 100% rename from appbuilder/core/components/retriever/reranker/rerank.py rename to python/core/components/retriever/reranker/rerank.py diff --git a/appbuilder/core/components/table_ocr/README.md b/python/core/components/table_ocr/README.md similarity index 100% rename from appbuilder/core/components/table_ocr/README.md rename to python/core/components/table_ocr/README.md diff --git a/appbuilder/core/components/table_ocr/__init__.py b/python/core/components/table_ocr/__init__.py similarity index 100% rename from appbuilder/core/components/table_ocr/__init__.py rename to python/core/components/table_ocr/__init__.py diff --git a/appbuilder/core/components/table_ocr/component.py b/python/core/components/table_ocr/component.py similarity index 100% rename from appbuilder/core/components/table_ocr/component.py rename to python/core/components/table_ocr/component.py diff --git a/appbuilder/core/components/table_ocr/model.py b/python/core/components/table_ocr/model.py similarity index 100% rename from appbuilder/core/components/table_ocr/model.py rename to python/core/components/table_ocr/model.py diff --git a/appbuilder/core/components/text_to_image/README.md b/python/core/components/text_to_image/README.md similarity index 100% rename from appbuilder/core/components/text_to_image/README.md rename to python/core/components/text_to_image/README.md diff --git a/appbuilder/core/components/text_to_image/__init__.py b/python/core/components/text_to_image/__init__.py similarity index 100% rename from appbuilder/core/components/text_to_image/__init__.py rename to python/core/components/text_to_image/__init__.py diff --git a/appbuilder/core/components/text_to_image/component.py b/python/core/components/text_to_image/component.py similarity index 100% rename from appbuilder/core/components/text_to_image/component.py rename to python/core/components/text_to_image/component.py diff --git a/appbuilder/core/components/text_to_image/model.py b/python/core/components/text_to_image/model.py similarity index 100% rename from appbuilder/core/components/text_to_image/model.py rename to python/core/components/text_to_image/model.py diff --git a/appbuilder/core/components/translate/README.md b/python/core/components/translate/README.md similarity index 100% rename from appbuilder/core/components/translate/README.md rename to python/core/components/translate/README.md diff --git a/appbuilder/core/components/translate/__init__.py b/python/core/components/translate/__init__.py similarity index 100% rename from appbuilder/core/components/translate/__init__.py rename to python/core/components/translate/__init__.py diff --git a/appbuilder/core/components/translate/component.py b/python/core/components/translate/component.py similarity index 100% rename from appbuilder/core/components/translate/component.py rename to python/core/components/translate/component.py diff --git a/appbuilder/core/components/translate/model.py b/python/core/components/translate/model.py similarity index 100% rename from appbuilder/core/components/translate/model.py rename to python/core/components/translate/model.py diff --git a/appbuilder/core/components/tts/README.md b/python/core/components/tts/README.md similarity index 100% rename from appbuilder/core/components/tts/README.md rename to python/core/components/tts/README.md diff --git a/appbuilder/core/components/tts/__init__.py b/python/core/components/tts/__init__.py similarity index 100% rename from appbuilder/core/components/tts/__init__.py rename to python/core/components/tts/__init__.py diff --git a/appbuilder/core/components/tts/component.py b/python/core/components/tts/component.py similarity index 100% rename from appbuilder/core/components/tts/component.py rename to python/core/components/tts/component.py diff --git a/appbuilder/core/components/tts/model.py b/python/core/components/tts/model.py similarity index 100% rename from appbuilder/core/components/tts/model.py rename to python/core/components/tts/model.py diff --git a/appbuilder/core/console/__init__.py b/python/core/console/__init__.py similarity index 100% rename from appbuilder/core/console/__init__.py rename to python/core/console/__init__.py diff --git a/appbuilder/core/console/appbuilder_client/__init__.py b/python/core/console/appbuilder_client/__init__.py similarity index 100% rename from appbuilder/core/console/appbuilder_client/__init__.py rename to python/core/console/appbuilder_client/__init__.py diff --git a/appbuilder/core/console/appbuilder_client/appbuilder_client.py b/python/core/console/appbuilder_client/appbuilder_client.py similarity index 100% rename from appbuilder/core/console/appbuilder_client/appbuilder_client.py rename to python/core/console/appbuilder_client/appbuilder_client.py diff --git a/appbuilder/core/console/appbuilder_client/data_class.py b/python/core/console/appbuilder_client/data_class.py similarity index 100% rename from appbuilder/core/console/appbuilder_client/data_class.py rename to python/core/console/appbuilder_client/data_class.py diff --git a/appbuilder/core/console/appbuilder_client/event_handler.py b/python/core/console/appbuilder_client/event_handler.py similarity index 100% rename from appbuilder/core/console/appbuilder_client/event_handler.py rename to python/core/console/appbuilder_client/event_handler.py diff --git a/appbuilder/core/console/base.py b/python/core/console/base.py similarity index 100% rename from appbuilder/core/console/base.py rename to python/core/console/base.py diff --git a/appbuilder/core/console/dataset/__init__.py b/python/core/console/dataset/__init__.py similarity index 100% rename from appbuilder/core/console/dataset/__init__.py rename to python/core/console/dataset/__init__.py diff --git a/appbuilder/core/console/dataset/dataset.py b/python/core/console/dataset/dataset.py similarity index 100% rename from appbuilder/core/console/dataset/dataset.py rename to python/core/console/dataset/dataset.py diff --git a/appbuilder/core/console/dataset/model.py b/python/core/console/dataset/model.py similarity index 100% rename from appbuilder/core/console/dataset/model.py rename to python/core/console/dataset/model.py diff --git a/appbuilder/core/console/knowledge_base/__init__.py b/python/core/console/knowledge_base/__init__.py similarity index 100% rename from appbuilder/core/console/knowledge_base/__init__.py rename to python/core/console/knowledge_base/__init__.py diff --git a/appbuilder/core/console/knowledge_base/data_class.py b/python/core/console/knowledge_base/data_class.py similarity index 100% rename from appbuilder/core/console/knowledge_base/data_class.py rename to python/core/console/knowledge_base/data_class.py diff --git a/appbuilder/core/console/knowledge_base/knowledge_base.py b/python/core/console/knowledge_base/knowledge_base.py similarity index 100% rename from appbuilder/core/console/knowledge_base/knowledge_base.py rename to python/core/console/knowledge_base/knowledge_base.py diff --git a/appbuilder/core/console/rag/__init__.py b/python/core/console/rag/__init__.py similarity index 100% rename from appbuilder/core/console/rag/__init__.py rename to python/core/console/rag/__init__.py diff --git a/appbuilder/core/console/rag/rag.py b/python/core/console/rag/rag.py similarity index 100% rename from appbuilder/core/console/rag/rag.py rename to python/core/console/rag/rag.py diff --git a/appbuilder/core/constants.py b/python/core/constants.py similarity index 100% rename from appbuilder/core/constants.py rename to python/core/constants.py diff --git a/appbuilder/core/context.py b/python/core/context.py similarity index 100% rename from appbuilder/core/context.py rename to python/core/context.py diff --git a/appbuilder/core/functional.py b/python/core/functional.py similarity index 100% rename from appbuilder/core/functional.py rename to python/core/functional.py diff --git a/appbuilder/core/message.py b/python/core/message.py similarity index 100% rename from appbuilder/core/message.py rename to python/core/message.py diff --git a/appbuilder/core/session_message.py b/python/core/session_message.py similarity index 100% rename from appbuilder/core/session_message.py rename to python/core/session_message.py diff --git a/appbuilder/core/user_session.py b/python/core/user_session.py similarity index 100% rename from appbuilder/core/user_session.py rename to python/core/user_session.py diff --git a/appbuilder/core/utils.py b/python/core/utils.py similarity index 100% rename from appbuilder/core/utils.py rename to python/core/utils.py diff --git a/appbuilder/tests/__init__.py b/python/tests/__init__.py similarity index 100% rename from appbuilder/tests/__init__.py rename to python/tests/__init__.py diff --git a/appbuilder/tests/component_check.py b/python/tests/component_check.py similarity index 100% rename from appbuilder/tests/component_check.py rename to python/tests/component_check.py diff --git a/appbuilder/tests/component_collector.py b/python/tests/component_collector.py similarity index 100% rename from appbuilder/tests/component_collector.py rename to python/tests/component_collector.py diff --git a/appbuilder/tests/data/qa_appbuilder_client_demo.pdf b/python/tests/data/qa_appbuilder_client_demo.pdf similarity index 100% rename from appbuilder/tests/data/qa_appbuilder_client_demo.pdf rename to python/tests/data/qa_appbuilder_client_demo.pdf diff --git a/appbuilder/tests/data/qa_demo.xlsx b/python/tests/data/qa_demo.xlsx similarity index 100% rename from appbuilder/tests/data/qa_demo.xlsx rename to python/tests/data/qa_demo.xlsx diff --git a/appbuilder/tests/data/qa_doc_parser_extract_table_from_doc.png b/python/tests/data/qa_doc_parser_extract_table_from_doc.png similarity index 100% rename from appbuilder/tests/data/qa_doc_parser_extract_table_from_doc.png rename to python/tests/data/qa_doc_parser_extract_table_from_doc.png diff --git a/appbuilder/tests/parallel_ut_run.py b/python/tests/parallel_ut_run.py similarity index 100% rename from appbuilder/tests/parallel_ut_run.py rename to python/tests/parallel_ut_run.py diff --git a/appbuilder/tests/print_components_error_info.py b/python/tests/print_components_error_info.py similarity index 100% rename from appbuilder/tests/print_components_error_info.py rename to python/tests/print_components_error_info.py diff --git a/appbuilder/tests/pytest_config.py b/python/tests/pytest_config.py similarity index 100% rename from appbuilder/tests/pytest_config.py rename to python/tests/pytest_config.py diff --git a/appbuilder/tests/pytest_utils.py b/python/tests/pytest_utils.py similarity index 100% rename from appbuilder/tests/pytest_utils.py rename to python/tests/pytest_utils.py diff --git a/appbuilder/tests/run_python_test.sh b/python/tests/run_python_test.sh similarity index 100% rename from appbuilder/tests/run_python_test.sh rename to python/tests/run_python_test.sh diff --git a/appbuilder/tests/sed_str.py b/python/tests/sed_str.py similarity index 100% rename from appbuilder/tests/sed_str.py rename to python/tests/sed_str.py diff --git a/appbuilder/tests/test.pdf b/python/tests/test.pdf similarity index 100% rename from appbuilder/tests/test.pdf rename to python/tests/test.pdf diff --git a/appbuilder/tests/test_agent.py b/python/tests/test_agent.py similarity index 100% rename from appbuilder/tests/test_agent.py rename to python/tests/test_agent.py diff --git a/appbuilder/tests/test_all_components.py b/python/tests/test_all_components.py similarity index 100% rename from appbuilder/tests/test_all_components.py rename to python/tests/test_all_components.py diff --git a/appbuilder/tests/test_animal_recognize.py b/python/tests/test_animal_recognize.py similarity index 100% rename from appbuilder/tests/test_animal_recognize.py rename to python/tests/test_animal_recognize.py diff --git a/appbuilder/tests/test_appbuilder_assistant_trace.py b/python/tests/test_appbuilder_assistant_trace.py similarity index 100% rename from appbuilder/tests/test_appbuilder_assistant_trace.py rename to python/tests/test_appbuilder_assistant_trace.py diff --git a/appbuilder/tests/test_appbuilder_client.py b/python/tests/test_appbuilder_client.py similarity index 100% rename from appbuilder/tests/test_appbuilder_client.py rename to python/tests/test_appbuilder_client.py diff --git a/appbuilder/tests/test_appbuilder_client_app_list.py b/python/tests/test_appbuilder_client_app_list.py similarity index 100% rename from appbuilder/tests/test_appbuilder_client_app_list.py rename to python/tests/test_appbuilder_client_app_list.py diff --git a/appbuilder/tests/test_appbuilder_client_run_with_handler.py b/python/tests/test_appbuilder_client_run_with_handler.py similarity index 100% rename from appbuilder/tests/test_appbuilder_client_run_with_handler.py rename to python/tests/test_appbuilder_client_run_with_handler.py diff --git a/appbuilder/tests/test_appbuilder_client_toolcall.py b/python/tests/test_appbuilder_client_toolcall.py similarity index 100% rename from appbuilder/tests/test_appbuilder_client_toolcall.py rename to python/tests/test_appbuilder_client_toolcall.py diff --git a/appbuilder/tests/test_appbuilder_client_toolcall_event_handler.py b/python/tests/test_appbuilder_client_toolcall_event_handler.py similarity index 100% rename from appbuilder/tests/test_appbuilder_client_toolcall_event_handler.py rename to python/tests/test_appbuilder_client_toolcall_event_handler.py diff --git a/appbuilder/tests/test_appbuilder_client_toolcall_event_handler_error.py b/python/tests/test_appbuilder_client_toolcall_event_handler_error.py similarity index 100% rename from appbuilder/tests/test_appbuilder_client_toolcall_event_handler_error.py rename to python/tests/test_appbuilder_client_toolcall_event_handler_error.py diff --git a/appbuilder/tests/test_appbuilder_client_toolcall_event_handler_stream.py b/python/tests/test_appbuilder_client_toolcall_event_handler_stream.py similarity index 100% rename from appbuilder/tests/test_appbuilder_client_toolcall_event_handler_stream.py rename to python/tests/test_appbuilder_client_toolcall_event_handler_stream.py diff --git a/appbuilder/tests/test_appbuilder_client_toolcall_event_handler_v2.py b/python/tests/test_appbuilder_client_toolcall_event_handler_v2.py similarity index 100% rename from appbuilder/tests/test_appbuilder_client_toolcall_event_handler_v2.py rename to python/tests/test_appbuilder_client_toolcall_event_handler_v2.py diff --git a/appbuilder/tests/test_appbuilder_client_toolcall_event_handler_v3.py b/python/tests/test_appbuilder_client_toolcall_event_handler_v3.py similarity index 100% rename from appbuilder/tests/test_appbuilder_client_toolcall_event_handler_v3.py rename to python/tests/test_appbuilder_client_toolcall_event_handler_v3.py diff --git a/appbuilder/tests/test_appbuilder_client_toolcall_stream.py b/python/tests/test_appbuilder_client_toolcall_stream.py similarity index 100% rename from appbuilder/tests/test_appbuilder_client_toolcall_stream.py rename to python/tests/test_appbuilder_client_toolcall_stream.py diff --git a/appbuilder/tests/test_appbuilder_client_toolcall_v2.py b/python/tests/test_appbuilder_client_toolcall_v2.py similarity index 100% rename from appbuilder/tests/test_appbuilder_client_toolcall_v2.py rename to python/tests/test_appbuilder_client_toolcall_v2.py diff --git a/appbuilder/tests/test_appbuilder_client_toolcall_v3.py b/python/tests/test_appbuilder_client_toolcall_v3.py similarity index 100% rename from appbuilder/tests/test_appbuilder_client_toolcall_v3.py rename to python/tests/test_appbuilder_client_toolcall_v3.py diff --git a/appbuilder/tests/test_appbuilder_client_trace.py b/python/tests/test_appbuilder_client_trace.py similarity index 100% rename from appbuilder/tests/test_appbuilder_client_trace.py rename to python/tests/test_appbuilder_client_trace.py diff --git a/appbuilder/tests/test_appbuilder_components_trace.py b/python/tests/test_appbuilder_components_trace.py similarity index 100% rename from appbuilder/tests/test_appbuilder_components_trace.py rename to python/tests/test_appbuilder_components_trace.py diff --git a/appbuilder/tests/test_appbuilder_core_components_retriever.py b/python/tests/test_appbuilder_core_components_retriever.py similarity index 100% rename from appbuilder/tests/test_appbuilder_core_components_retriever.py rename to python/tests/test_appbuilder_core_components_retriever.py diff --git a/appbuilder/tests/test_appbuilder_sentry_trace_off.py b/python/tests/test_appbuilder_sentry_trace_off.py similarity index 100% rename from appbuilder/tests/test_appbuilder_sentry_trace_off.py rename to python/tests/test_appbuilder_sentry_trace_off.py diff --git a/appbuilder/tests/test_appbuilder_sentry_trace_on.py b/python/tests/test_appbuilder_sentry_trace_on.py similarity index 100% rename from appbuilder/tests/test_appbuilder_sentry_trace_on.py rename to python/tests/test_appbuilder_sentry_trace_on.py diff --git a/appbuilder/tests/test_appbuilder_trace_raise_error.py b/python/tests/test_appbuilder_trace_raise_error.py similarity index 100% rename from appbuilder/tests/test_appbuilder_trace_raise_error.py rename to python/tests/test_appbuilder_trace_raise_error.py diff --git a/appbuilder/tests/test_asr.py b/python/tests/test_asr.py similarity index 100% rename from appbuilder/tests/test_asr.py rename to python/tests/test_asr.py diff --git a/appbuilder/tests/test_assistant_basic_import.py b/python/tests/test_assistant_basic_import.py similarity index 100% rename from appbuilder/tests/test_assistant_basic_import.py rename to python/tests/test_assistant_basic_import.py diff --git a/appbuilder/tests/test_assistant_class_assistans.py b/python/tests/test_assistant_class_assistans.py similarity index 100% rename from appbuilder/tests/test_assistant_class_assistans.py rename to python/tests/test_assistant_class_assistans.py diff --git a/appbuilder/tests/test_assistant_class_files.py b/python/tests/test_assistant_class_files.py similarity index 100% rename from appbuilder/tests/test_assistant_class_files.py rename to python/tests/test_assistant_class_files.py diff --git a/appbuilder/tests/test_assistant_class_messages.py b/python/tests/test_assistant_class_messages.py similarity index 100% rename from appbuilder/tests/test_assistant_class_messages.py rename to python/tests/test_assistant_class_messages.py diff --git a/appbuilder/tests/test_assistant_class_runs.py b/python/tests/test_assistant_class_runs.py similarity index 100% rename from appbuilder/tests/test_assistant_class_runs.py rename to python/tests/test_assistant_class_runs.py diff --git a/appbuilder/tests/test_assistant_class_runs_v2.py b/python/tests/test_assistant_class_runs_v2.py similarity index 100% rename from appbuilder/tests/test_assistant_class_runs_v2.py rename to python/tests/test_assistant_class_runs_v2.py diff --git a/appbuilder/tests/test_assistant_class_threads.py b/python/tests/test_assistant_class_threads.py similarity index 100% rename from appbuilder/tests/test_assistant_class_threads.py rename to python/tests/test_assistant_class_threads.py diff --git a/appbuilder/tests/test_assistant_e2e_funccall.py b/python/tests/test_assistant_e2e_funccall.py similarity index 100% rename from appbuilder/tests/test_assistant_e2e_funccall.py rename to python/tests/test_assistant_e2e_funccall.py diff --git a/appbuilder/tests/test_assistant_e2e_funccall_component.py b/python/tests/test_assistant_e2e_funccall_component.py similarity index 100% rename from appbuilder/tests/test_assistant_e2e_funccall_component.py rename to python/tests/test_assistant_e2e_funccall_component.py diff --git a/appbuilder/tests/test_assistant_e2e_run.py b/python/tests/test_assistant_e2e_run.py similarity index 100% rename from appbuilder/tests/test_assistant_e2e_run.py rename to python/tests/test_assistant_e2e_run.py diff --git a/appbuilder/tests/test_assistant_e2e_stream_cancel.py b/python/tests/test_assistant_e2e_stream_cancel.py similarity index 100% rename from appbuilder/tests/test_assistant_e2e_stream_cancel.py rename to python/tests/test_assistant_e2e_stream_cancel.py diff --git a/appbuilder/tests/test_assistant_e2e_stream_event_handler.py b/python/tests/test_assistant_e2e_stream_event_handler.py similarity index 100% rename from appbuilder/tests/test_assistant_e2e_stream_event_handler.py rename to python/tests/test_assistant_e2e_stream_event_handler.py diff --git a/appbuilder/tests/test_assistant_e2e_stream_event_handler_v2.py b/python/tests/test_assistant_e2e_stream_event_handler_v2.py similarity index 100% rename from appbuilder/tests/test_assistant_e2e_stream_event_handler_v2.py rename to python/tests/test_assistant_e2e_stream_event_handler_v2.py diff --git a/appbuilder/tests/test_assistant_e2e_stream_funccall.py b/python/tests/test_assistant_e2e_stream_funccall.py similarity index 100% rename from appbuilder/tests/test_assistant_e2e_stream_funccall.py rename to python/tests/test_assistant_e2e_stream_funccall.py diff --git a/appbuilder/tests/test_assistant_e2e_stream_run.py b/python/tests/test_assistant_e2e_stream_run.py similarity index 100% rename from appbuilder/tests/test_assistant_e2e_stream_run.py rename to python/tests/test_assistant_e2e_stream_run.py diff --git a/appbuilder/tests/test_bes_retriever.py b/python/tests/test_bes_retriever.py similarity index 100% rename from appbuilder/tests/test_bes_retriever.py rename to python/tests/test_bes_retriever.py diff --git a/appbuilder/tests/test_console_dataset.py b/python/tests/test_console_dataset.py similarity index 100% rename from appbuilder/tests/test_console_dataset.py rename to python/tests/test_console_dataset.py diff --git a/appbuilder/tests/test_console_rag.py b/python/tests/test_console_rag.py similarity index 100% rename from appbuilder/tests/test_console_rag.py rename to python/tests/test_console_rag.py diff --git a/appbuilder/tests/test_core_agent.py b/python/tests/test_core_agent.py similarity index 100% rename from appbuilder/tests/test_core_agent.py rename to python/tests/test_core_agent.py diff --git a/appbuilder/tests/test_core_client.py b/python/tests/test_core_client.py similarity index 100% rename from appbuilder/tests/test_core_client.py rename to python/tests/test_core_client.py diff --git a/appbuilder/tests/test_core_components_baidu_vdb_retriever.py b/python/tests/test_core_components_baidu_vdb_retriever.py similarity index 100% rename from appbuilder/tests/test_core_components_baidu_vdb_retriever.py rename to python/tests/test_core_components_baidu_vdb_retriever.py diff --git a/appbuilder/tests/test_core_components_doc.py b/python/tests/test_core_components_doc.py similarity index 100% rename from appbuilder/tests/test_core_components_doc.py rename to python/tests/test_core_components_doc.py diff --git a/appbuilder/tests/test_core_components_embedding.py b/python/tests/test_core_components_embedding.py similarity index 100% rename from appbuilder/tests/test_core_components_embedding.py rename to python/tests/test_core_components_embedding.py diff --git a/appbuilder/tests/test_core_components_table.py b/python/tests/test_core_components_table.py similarity index 100% rename from appbuilder/tests/test_core_components_table.py rename to python/tests/test_core_components_table.py diff --git a/appbuilder/tests/test_core_components_tts.py b/python/tests/test_core_components_tts.py similarity index 100% rename from appbuilder/tests/test_core_components_tts.py rename to python/tests/test_core_components_tts.py diff --git a/appbuilder/tests/test_core_console_base.py b/python/tests/test_core_console_base.py similarity index 100% rename from appbuilder/tests/test_core_console_base.py rename to python/tests/test_core_console_base.py diff --git a/appbuilder/tests/test_core_user_session.py b/python/tests/test_core_user_session.py similarity index 100% rename from appbuilder/tests/test_core_user_session.py rename to python/tests/test_core_user_session.py diff --git a/appbuilder/tests/test_core_utils.py b/python/tests/test_core_utils.py similarity index 100% rename from appbuilder/tests/test_core_utils.py rename to python/tests/test_core_utils.py diff --git a/appbuilder/tests/test_dialog_summary.py b/python/tests/test_dialog_summary.py similarity index 100% rename from appbuilder/tests/test_dialog_summary.py rename to python/tests/test_dialog_summary.py diff --git a/appbuilder/tests/test_dish_recognize.py b/python/tests/test_dish_recognize.py similarity index 100% rename from appbuilder/tests/test_dish_recognize.py rename to python/tests/test_dish_recognize.py diff --git a/appbuilder/tests/test_doc_crop_enhance.py b/python/tests/test_doc_crop_enhance.py similarity index 100% rename from appbuilder/tests/test_doc_crop_enhance.py rename to python/tests/test_doc_crop_enhance.py diff --git a/appbuilder/tests/test_doc_format_converter.py b/python/tests/test_doc_format_converter.py similarity index 100% rename from appbuilder/tests/test_doc_format_converter.py rename to python/tests/test_doc_format_converter.py diff --git a/appbuilder/tests/test_doc_parser.py b/python/tests/test_doc_parser.py similarity index 100% rename from appbuilder/tests/test_doc_parser.py rename to python/tests/test_doc_parser.py diff --git a/appbuilder/tests/test_doc_splitter.py b/python/tests/test_doc_splitter.py similarity index 100% rename from appbuilder/tests/test_doc_splitter.py rename to python/tests/test_doc_splitter.py diff --git a/appbuilder/tests/test_document_understanding.py b/python/tests/test_document_understanding.py similarity index 100% rename from appbuilder/tests/test_document_understanding.py rename to python/tests/test_document_understanding.py diff --git a/appbuilder/tests/test_embedding.py b/python/tests/test_embedding.py similarity index 100% rename from appbuilder/tests/test_embedding.py rename to python/tests/test_embedding.py diff --git a/appbuilder/tests/test_extract_table.py b/python/tests/test_extract_table.py similarity index 100% rename from appbuilder/tests/test_extract_table.py rename to python/tests/test_extract_table.py diff --git a/appbuilder/tests/test_gbi_nl2sql.py b/python/tests/test_gbi_nl2sql.py similarity index 100% rename from appbuilder/tests/test_gbi_nl2sql.py rename to python/tests/test_gbi_nl2sql.py diff --git a/appbuilder/tests/test_gbi_select_table.py b/python/tests/test_gbi_select_table.py similarity index 100% rename from appbuilder/tests/test_gbi_select_table.py rename to python/tests/test_gbi_select_table.py diff --git a/appbuilder/tests/test_general_ocr.py b/python/tests/test_general_ocr.py similarity index 100% rename from appbuilder/tests/test_general_ocr.py rename to python/tests/test_general_ocr.py diff --git a/appbuilder/tests/test_get_app_list.py b/python/tests/test_get_app_list.py similarity index 100% rename from appbuilder/tests/test_get_app_list.py rename to python/tests/test_get_app_list.py diff --git a/appbuilder/tests/test_get_model_list.py b/python/tests/test_get_model_list.py similarity index 100% rename from appbuilder/tests/test_get_model_list.py rename to python/tests/test_get_model_list.py diff --git a/appbuilder/tests/test_hallucination_detection.py b/python/tests/test_hallucination_detection.py similarity index 100% rename from appbuilder/tests/test_hallucination_detection.py rename to python/tests/test_hallucination_detection.py diff --git a/appbuilder/tests/test_handwrite_ocr.py b/python/tests/test_handwrite_ocr.py similarity index 100% rename from appbuilder/tests/test_handwrite_ocr.py rename to python/tests/test_handwrite_ocr.py diff --git a/appbuilder/tests/test_image_understand.py b/python/tests/test_image_understand.py similarity index 100% rename from appbuilder/tests/test_image_understand.py rename to python/tests/test_image_understand.py diff --git a/appbuilder/tests/test_is_complex_query.py b/python/tests/test_is_complex_query.py similarity index 100% rename from appbuilder/tests/test_is_complex_query.py rename to python/tests/test_is_complex_query.py diff --git a/appbuilder/tests/test_knowledge_base.py b/python/tests/test_knowledge_base.py similarity index 100% rename from appbuilder/tests/test_knowledge_base.py rename to python/tests/test_knowledge_base.py diff --git a/appbuilder/tests/test_landmark_recognize.py b/python/tests/test_landmark_recognize.py similarity index 100% rename from appbuilder/tests/test_landmark_recognize.py rename to python/tests/test_landmark_recognize.py diff --git a/appbuilder/tests/test_langchain_adapter_run.py b/python/tests/test_langchain_adapter_run.py similarity index 100% rename from appbuilder/tests/test_langchain_adapter_run.py rename to python/tests/test_langchain_adapter_run.py diff --git a/appbuilder/tests/test_langchain_adapter_tool_eval.py b/python/tests/test_langchain_adapter_tool_eval.py similarity index 100% rename from appbuilder/tests/test_langchain_adapter_tool_eval.py rename to python/tests/test_langchain_adapter_tool_eval.py diff --git a/appbuilder/tests/test_langchain_error.py b/python/tests/test_langchain_error.py similarity index 100% rename from appbuilder/tests/test_langchain_error.py rename to python/tests/test_langchain_error.py diff --git a/appbuilder/tests/test_llm_base.py b/python/tests/test_llm_base.py similarity index 100% rename from appbuilder/tests/test_llm_base.py rename to python/tests/test_llm_base.py diff --git a/appbuilder/tests/test_matching.py b/python/tests/test_matching.py similarity index 100% rename from appbuilder/tests/test_matching.py rename to python/tests/test_matching.py diff --git a/appbuilder/tests/test_message.py b/python/tests/test_message.py similarity index 100% rename from appbuilder/tests/test_message.py rename to python/tests/test_message.py diff --git a/appbuilder/tests/test_mix_card_ocr.py b/python/tests/test_mix_card_ocr.py similarity index 100% rename from appbuilder/tests/test_mix_card_ocr.py rename to python/tests/test_mix_card_ocr.py diff --git a/appbuilder/tests/test_mrc.py b/python/tests/test_mrc.py similarity index 100% rename from appbuilder/tests/test_mrc.py rename to python/tests/test_mrc.py diff --git a/appbuilder/tests/test_nl2pandas.py b/python/tests/test_nl2pandas.py similarity index 100% rename from appbuilder/tests/test_nl2pandas.py rename to python/tests/test_nl2pandas.py diff --git a/appbuilder/tests/test_object_recognize.py b/python/tests/test_object_recognize.py similarity index 100% rename from appbuilder/tests/test_object_recognize.py rename to python/tests/test_object_recognize.py diff --git a/appbuilder/tests/test_oral_query_generation.py b/python/tests/test_oral_query_generation.py similarity index 100% rename from appbuilder/tests/test_oral_query_generation.py rename to python/tests/test_oral_query_generation.py diff --git a/appbuilder/tests/test_plant_recognize.py b/python/tests/test_plant_recognize.py similarity index 100% rename from appbuilder/tests/test_plant_recognize.py rename to python/tests/test_plant_recognize.py diff --git a/appbuilder/tests/test_playground.py b/python/tests/test_playground.py similarity index 100% rename from appbuilder/tests/test_playground.py rename to python/tests/test_playground.py diff --git a/appbuilder/tests/test_ppt_generation_from_file.py b/python/tests/test_ppt_generation_from_file.py similarity index 100% rename from appbuilder/tests/test_ppt_generation_from_file.py rename to python/tests/test_ppt_generation_from_file.py diff --git a/appbuilder/tests/test_ppt_generation_from_instruction.py b/python/tests/test_ppt_generation_from_instruction.py similarity index 100% rename from appbuilder/tests/test_ppt_generation_from_instruction.py rename to python/tests/test_ppt_generation_from_instruction.py diff --git a/appbuilder/tests/test_ppt_generation_from_paper.py b/python/tests/test_ppt_generation_from_paper.py similarity index 100% rename from appbuilder/tests/test_ppt_generation_from_paper.py rename to python/tests/test_ppt_generation_from_paper.py diff --git a/appbuilder/tests/test_qa_aicape_animal_rec.py b/python/tests/test_qa_aicape_animal_rec.py similarity index 100% rename from appbuilder/tests/test_qa_aicape_animal_rec.py rename to python/tests/test_qa_aicape_animal_rec.py diff --git a/appbuilder/tests/test_qa_aicape_doc_crop_enhance.py b/python/tests/test_qa_aicape_doc_crop_enhance.py similarity index 100% rename from appbuilder/tests/test_qa_aicape_doc_crop_enhance.py rename to python/tests/test_qa_aicape_doc_crop_enhance.py diff --git a/appbuilder/tests/test_qa_aicape_handwriting_ocr.py b/python/tests/test_qa_aicape_handwriting_ocr.py similarity index 100% rename from appbuilder/tests/test_qa_aicape_handwriting_ocr.py rename to python/tests/test_qa_aicape_handwriting_ocr.py diff --git a/appbuilder/tests/test_qa_aicape_image_understand.py b/python/tests/test_qa_aicape_image_understand.py similarity index 100% rename from appbuilder/tests/test_qa_aicape_image_understand.py rename to python/tests/test_qa_aicape_image_understand.py diff --git a/appbuilder/tests/test_qa_aicape_mixcard_ocr.py b/python/tests/test_qa_aicape_mixcard_ocr.py similarity index 100% rename from appbuilder/tests/test_qa_aicape_mixcard_ocr.py rename to python/tests/test_qa_aicape_mixcard_ocr.py diff --git a/appbuilder/tests/test_qa_aicape_plant_rec.py b/python/tests/test_qa_aicape_plant_rec.py similarity index 100% rename from appbuilder/tests/test_qa_aicape_plant_rec.py rename to python/tests/test_qa_aicape_plant_rec.py diff --git a/appbuilder/tests/test_qa_aicape_qrcode_orc.py b/python/tests/test_qa_aicape_qrcode_orc.py similarity index 100% rename from appbuilder/tests/test_qa_aicape_qrcode_orc.py rename to python/tests/test_qa_aicape_qrcode_orc.py diff --git a/appbuilder/tests/test_qa_aicape_table_ocr.py b/python/tests/test_qa_aicape_table_ocr.py similarity index 100% rename from appbuilder/tests/test_qa_aicape_table_ocr.py rename to python/tests/test_qa_aicape_table_ocr.py diff --git a/appbuilder/tests/test_qa_doc_parser_extract_table_from_doc.py b/python/tests/test_qa_doc_parser_extract_table_from_doc.py similarity index 100% rename from appbuilder/tests/test_qa_doc_parser_extract_table_from_doc.py rename to python/tests/test_qa_doc_parser_extract_table_from_doc.py diff --git a/appbuilder/tests/test_qa_llm_dialog_summary.py b/python/tests/test_qa_llm_dialog_summary.py similarity index 100% rename from appbuilder/tests/test_qa_llm_dialog_summary.py rename to python/tests/test_qa_llm_dialog_summary.py diff --git a/appbuilder/tests/test_qa_llm_excel2figure.py b/python/tests/test_qa_llm_excel2figure.py similarity index 100% rename from appbuilder/tests/test_qa_llm_excel2figure.py rename to python/tests/test_qa_llm_excel2figure.py diff --git a/appbuilder/tests/test_qa_llm_get_qianfan_model_list.py b/python/tests/test_qa_llm_get_qianfan_model_list.py similarity index 100% rename from appbuilder/tests/test_qa_llm_get_qianfan_model_list.py rename to python/tests/test_qa_llm_get_qianfan_model_list.py diff --git a/appbuilder/tests/test_qa_llm_is_complex_query.py b/python/tests/test_qa_llm_is_complex_query.py similarity index 100% rename from appbuilder/tests/test_qa_llm_is_complex_query.py rename to python/tests/test_qa_llm_is_complex_query.py diff --git a/appbuilder/tests/test_qa_llm_matching.py b/python/tests/test_qa_llm_matching.py similarity index 100% rename from appbuilder/tests/test_qa_llm_matching.py rename to python/tests/test_qa_llm_matching.py diff --git a/appbuilder/tests/test_qa_llm_oral_query_generation.py b/python/tests/test_qa_llm_oral_query_generation.py similarity index 100% rename from appbuilder/tests/test_qa_llm_oral_query_generation.py rename to python/tests/test_qa_llm_oral_query_generation.py diff --git a/appbuilder/tests/test_qa_llm_paddle_speech_tts.py b/python/tests/test_qa_llm_paddle_speech_tts.py similarity index 100% rename from appbuilder/tests/test_qa_llm_paddle_speech_tts.py rename to python/tests/test_qa_llm_paddle_speech_tts.py diff --git a/appbuilder/tests/test_qa_llm_pandas.py b/python/tests/test_qa_llm_pandas.py similarity index 100% rename from appbuilder/tests/test_qa_llm_pandas.py rename to python/tests/test_qa_llm_pandas.py diff --git a/appbuilder/tests/test_qa_llm_query_decomposition.py b/python/tests/test_qa_llm_query_decomposition.py similarity index 100% rename from appbuilder/tests/test_qa_llm_query_decomposition.py rename to python/tests/test_qa_llm_query_decomposition.py diff --git a/appbuilder/tests/test_qa_llm_style_rewrite.py b/python/tests/test_qa_llm_style_rewrite.py similarity index 100% rename from appbuilder/tests/test_qa_llm_style_rewrite.py rename to python/tests/test_qa_llm_style_rewrite.py diff --git a/appbuilder/tests/test_qa_pair_mining.py b/python/tests/test_qa_pair_mining.py similarity index 100% rename from appbuilder/tests/test_qa_pair_mining.py rename to python/tests/test_qa_pair_mining.py diff --git a/appbuilder/tests/test_qrcode_ocr.py b/python/tests/test_qrcode_ocr.py similarity index 100% rename from appbuilder/tests/test_qrcode_ocr.py rename to python/tests/test_qrcode_ocr.py diff --git a/appbuilder/tests/test_query_decomposition.py b/python/tests/test_query_decomposition.py similarity index 100% rename from appbuilder/tests/test_query_decomposition.py rename to python/tests/test_query_decomposition.py diff --git a/appbuilder/tests/test_query_rewrite.py b/python/tests/test_query_rewrite.py similarity index 100% rename from appbuilder/tests/test_query_rewrite.py rename to python/tests/test_query_rewrite.py diff --git a/appbuilder/tests/test_rag_baidu_search.py b/python/tests/test_rag_baidu_search.py similarity index 100% rename from appbuilder/tests/test_rag_baidu_search.py rename to python/tests/test_rag_baidu_search.py diff --git a/appbuilder/tests/test_rag_baidu_search_pro.py b/python/tests/test_rag_baidu_search_pro.py similarity index 100% rename from appbuilder/tests/test_rag_baidu_search_pro.py rename to python/tests/test_rag_baidu_search_pro.py diff --git a/appbuilder/tests/test_rerank.py b/python/tests/test_rerank.py similarity index 100% rename from appbuilder/tests/test_rerank.py rename to python/tests/test_rerank.py diff --git a/appbuilder/tests/test_similar_question.py b/python/tests/test_similar_question.py similarity index 100% rename from appbuilder/tests/test_similar_question.py rename to python/tests/test_similar_question.py diff --git a/appbuilder/tests/test_style_rewrite.py b/python/tests/test_style_rewrite.py similarity index 100% rename from appbuilder/tests/test_style_rewrite.py rename to python/tests/test_style_rewrite.py diff --git a/appbuilder/tests/test_style_writing.py b/python/tests/test_style_writing.py similarity index 100% rename from appbuilder/tests/test_style_writing.py rename to python/tests/test_style_writing.py diff --git a/appbuilder/tests/test_table_ocr.py b/python/tests/test_table_ocr.py similarity index 100% rename from appbuilder/tests/test_table_ocr.py rename to python/tests/test_table_ocr.py diff --git a/appbuilder/tests/test_tag_extraction.py b/python/tests/test_tag_extraction.py similarity index 100% rename from appbuilder/tests/test_tag_extraction.py rename to python/tests/test_tag_extraction.py diff --git a/appbuilder/tests/test_text_to_image.py b/python/tests/test_text_to_image.py similarity index 100% rename from appbuilder/tests/test_text_to_image.py rename to python/tests/test_text_to_image.py diff --git a/appbuilder/tests/test_trace.py b/python/tests/test_trace.py similarity index 100% rename from appbuilder/tests/test_trace.py rename to python/tests/test_trace.py diff --git a/appbuilder/tests/test_trace_skip_raise_error.py b/python/tests/test_trace_skip_raise_error.py similarity index 100% rename from appbuilder/tests/test_trace_skip_raise_error.py rename to python/tests/test_trace_skip_raise_error.py diff --git a/appbuilder/tests/test_translate.py b/python/tests/test_translate.py similarity index 100% rename from appbuilder/tests/test_translate.py rename to python/tests/test_translate.py diff --git a/appbuilder/tests/test_tts.py b/python/tests/test_tts.py similarity index 100% rename from appbuilder/tests/test_tts.py rename to python/tests/test_tts.py diff --git a/appbuilder/tests/test_utils.py b/python/tests/test_utils.py similarity index 100% rename from appbuilder/tests/test_utils.py rename to python/tests/test_utils.py diff --git a/appbuilder/tests/test_utils_collector.py b/python/tests/test_utils_collector.py similarity index 100% rename from appbuilder/tests/test_utils_collector.py rename to python/tests/test_utils_collector.py diff --git a/appbuilder/tests/test_utils_logger.py b/python/tests/test_utils_logger.py similarity index 100% rename from appbuilder/tests/test_utils_logger.py rename to python/tests/test_utils_logger.py diff --git a/appbuilder/tests/test_utils_logging_util.py b/python/tests/test_utils_logging_util.py similarity index 100% rename from appbuilder/tests/test_utils_logging_util.py rename to python/tests/test_utils_logging_util.py diff --git a/appbuilder/tests/test_vdb_retriever.py b/python/tests/test_vdb_retriever.py similarity index 100% rename from appbuilder/tests/test_vdb_retriever.py rename to python/tests/test_vdb_retriever.py diff --git a/appbuilder/tests/title_splitter.docx b/python/tests/title_splitter.docx similarity index 100% rename from appbuilder/tests/title_splitter.docx rename to python/tests/title_splitter.docx diff --git a/appbuilder/tests/whitelist_components.txt b/python/tests/whitelist_components.txt similarity index 100% rename from appbuilder/tests/whitelist_components.txt rename to python/tests/whitelist_components.txt diff --git a/appbuilder/utils/__init__.py b/python/utils/__init__.py similarity index 100% rename from appbuilder/utils/__init__.py rename to python/utils/__init__.py diff --git a/appbuilder/utils/_bcc.py b/python/utils/_bcc.py similarity index 100% rename from appbuilder/utils/_bcc.py rename to python/utils/_bcc.py diff --git a/appbuilder/utils/bce_deploy.py b/python/utils/bce_deploy.py similarity index 100% rename from appbuilder/utils/bce_deploy.py rename to python/utils/bce_deploy.py diff --git a/appbuilder/utils/collector.py b/python/utils/collector.py similarity index 100% rename from appbuilder/utils/collector.py rename to python/utils/collector.py diff --git a/appbuilder/utils/func_utils.py b/python/utils/func_utils.py similarity index 100% rename from appbuilder/utils/func_utils.py rename to python/utils/func_utils.py diff --git a/appbuilder/utils/json_schema_to_model.py b/python/utils/json_schema_to_model.py similarity index 100% rename from appbuilder/utils/json_schema_to_model.py rename to python/utils/json_schema_to_model.py diff --git a/appbuilder/utils/logger_util.py b/python/utils/logger_util.py similarity index 100% rename from appbuilder/utils/logger_util.py rename to python/utils/logger_util.py diff --git a/appbuilder/utils/model_util.py b/python/utils/model_util.py similarity index 100% rename from appbuilder/utils/model_util.py rename to python/utils/model_util.py diff --git a/appbuilder/utils/sse_util.py b/python/utils/sse_util.py similarity index 100% rename from appbuilder/utils/sse_util.py rename to python/utils/sse_util.py diff --git a/appbuilder/utils/trace/__init__.py b/python/utils/trace/__init__.py similarity index 100% rename from appbuilder/utils/trace/__init__.py rename to python/utils/trace/__init__.py diff --git a/appbuilder/utils/trace/_function.py b/python/utils/trace/_function.py similarity index 100% rename from appbuilder/utils/trace/_function.py rename to python/utils/trace/_function.py diff --git a/appbuilder/utils/trace/phoenix_wrapper.py b/python/utils/trace/phoenix_wrapper.py similarity index 100% rename from appbuilder/utils/trace/phoenix_wrapper.py rename to python/utils/trace/phoenix_wrapper.py diff --git a/appbuilder/utils/trace/tracer.py b/python/utils/trace/tracer.py similarity index 100% rename from appbuilder/utils/trace/tracer.py rename to python/utils/trace/tracer.py diff --git a/appbuilder/utils/trace/tracer_wrapper.py b/python/utils/trace/tracer_wrapper.py similarity index 100% rename from appbuilder/utils/trace/tracer_wrapper.py rename to python/utils/trace/tracer_wrapper.py From 72fd74d1db0fb5f9821c04af851db27a921ae5b2 Mon Sep 17 00:00:00 2001 From: userpj Date: Thu, 7 Nov 2024 19:19:41 +0800 Subject: [PATCH 04/85] update unittest --- python/tests/run_python_test.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python/tests/run_python_test.sh b/python/tests/run_python_test.sh index 00f082fc7..cce13512b 100644 --- a/python/tests/run_python_test.sh +++ b/python/tests/run_python_test.sh @@ -57,7 +57,7 @@ python3 -m pip uninstall -y pydantic python3 -m pip install pydantic==2.7.4 python3 -m pip install langchain==0.3.0 python3 -m pip install datamodel-code-generator==0.25.8 -cd appbuilder/tests/ +cd python/tests/ # 5、执行parallel_ut_run.py,运行python单元测试 @@ -66,7 +66,7 @@ python3 parallel_ut_run.py run_result=$? # 6、基于coverage 测试结果计算全量单测覆盖率 -# coverage combine ./appbuilder/tests/ +# coverage combine ./python/tests/ coverage combine coverage xml -o coverage.xml coverage html -d coverage_html @@ -82,8 +82,8 @@ echo "--------------------------" # 替换后 /Users/chengmo/workspace/baidu_code/app-builder/ # 首先获取appbuilder-sdk的python lib的安装目录 python_lib=$(python3 -m pip show appbuilder-sdk | grep Location | awk '{print $2}') -# 再获取git clone的目录, 当前目录为 app-builder/appbuilder/tests, 取 app-builder/目录 -git_dir=$(pwd | sed 's/appbuilder\/tests//') +# 再获取git clone的目录, 当前目录为 app-builder/python/tests, 取 app-builder/目录 +git_dir=$(pwd | sed 's/python\/tests//') # 批量替换coverage.xml文件中的python_lib为git_dir,并将源文件备份一个orignal.xml后缀 python3 -u sed_str.py coverage.xml $python_lib $git_dir # 最后进行增量代码覆盖率测试 From 06bb12407f9fab756932c17197a566af7355497d Mon Sep 17 00:00:00 2001 From: userpj Date: Thu, 7 Nov 2024 20:01:36 +0800 Subject: [PATCH 05/85] update --- .github/workflows/python-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index dc4a33ab5..2beeff461 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -83,7 +83,7 @@ jobs: # 比较当前分支与 merge_base 之间的差异 changed_files=$(git diff --name-only --diff-filter=ACMRT $merge_base) - changed_files_py_sh=$(git diff --name-only --diff-filter=ACMRT $merge_base -- $(find appbuilder -type f \( -name '*.py' -o -name '*.sh' \))) + changed_files_py_sh=$(git diff --name-only --diff-filter=ACMRT $merge_base -- $(find python -type f \( -name '*.py' -o -name '*.sh' \))) echo "发生更改的文件为:" echo "$changed_files" From 036c1f75c2f271af8695a17593f5a492271a2ea4 Mon Sep 17 00:00:00 2001 From: userpj Date: Thu, 7 Nov 2024 20:07:30 +0800 Subject: [PATCH 06/85] update --- setup.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 3e9327ff4..b2090b3ed 100755 --- a/setup.py +++ b/setup.py @@ -25,8 +25,9 @@ with open('requirements.txt') as f: requirements = f.read().splitlines() -packages = find_packages() +packages = [p.replace("python", "appbuilder") for p in find_packages()] package_data = {} + for package in packages: if package.startswith('appbuilder.utils'): package_data[package] = ["*.md"] @@ -45,6 +46,7 @@ author_email="dongdaxiang@baidu.com", packages=packages, package_data=package_data, + package_dir={"appbuilder": "python"}, install_requires=requirements, python_requires=">=3.9", extras_require={ From a4b07219e30d8b71128c747acac9bdb9e46cf368 Mon Sep 17 00:00:00 2001 From: userpj Date: Thu, 7 Nov 2024 20:30:04 +0800 Subject: [PATCH 07/85] update --- setup.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index b2090b3ed..71be5f788 100755 --- a/setup.py +++ b/setup.py @@ -20,14 +20,27 @@ Date: 2023/12/01 11:19:24 """ +import os +import shutil +import datetime from setuptools import setup, find_packages with open('requirements.txt') as f: requirements = f.read().splitlines() -packages = [p.replace("python", "appbuilder") for p in find_packages()] -package_data = {} +now = datetime.datetime.now() +timestamp_str = now.strftime("%Y%m%d_%H%M%S") +package_dir = f"appbuilder_sdk_{timestamp_str}" +if not os.path.exists(package_dir): + os.makedirs(package_dir) + +shutil.copytree("python", os.path.join(package_dir, "appbuilder")) +shutil.copy("requirements.txt", package_dir) + +packages = find_packages(where=package_dir) +package_data = {} +print(packages) for package in packages: if package.startswith('appbuilder.utils'): package_data[package] = ["*.md"] @@ -46,7 +59,7 @@ author_email="dongdaxiang@baidu.com", packages=packages, package_data=package_data, - package_dir={"appbuilder": "python"}, + package_dir={'': package_dir}, install_requires=requirements, python_requires=">=3.9", extras_require={ @@ -66,3 +79,5 @@ long_description="百度智能云千帆AppBuilder, 开箱即用的组件与框架, 高效开发你的AI原生应用, 更多信息请登录: https://appbuilder.cloud.baidu.com/", url="https://github.com/baidubce/app-builder", ) + +shutil.rmtree(package_dir) From bc1b69d97ffbd8ca37b959e9a93ca39bb9f6ea19 Mon Sep 17 00:00:00 2001 From: userpj Date: Thu, 7 Nov 2024 20:35:34 +0800 Subject: [PATCH 08/85] update docs --- docs/basic_module/knowledgebase.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/basic_module/knowledgebase.md b/docs/basic_module/knowledgebase.md index 0867b337e..9c67158ac 100644 --- a/docs/basic_module/knowledgebase.md +++ b/docs/basic_module/knowledgebase.md @@ -421,7 +421,7 @@ knowledge = appbuilder.KnowledgeBase() knowledge.upload_documents( id=knowledge_base_id, content_format="rawText", - file_path="./appbuilder/tests/data/qa_appbuilder_client_demo.pdf", + file_path="./python/tests/data/qa_appbuilder_client_demo.pdf", processOption=appbuilder.DocumentProcessOption( template="custom", parser=appbuilder.DocumentChoices( From 739eda0b8f2f14583b02fd6078a79749478870c2 Mon Sep 17 00:00:00 2001 From: peiwenYe <963623403@qq.com> Date: Fri, 8 Nov 2024 11:42:35 +0800 Subject: [PATCH 09/85] =?UTF-8?q?=E5=A2=9E=E5=8A=A0TreeMind=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=20(#584)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: yepeiwen01 --- python/__init__.py | 2 + python/core/components/tree_mind/README.md | 60 ++++++++ python/core/components/tree_mind/__init__.py | 14 ++ python/core/components/tree_mind/component.py | 136 ++++++++++++++++++ python/core/components/tree_mind/model.py | 45 ++++++ python/tests/component_check.py | 1 + python/tests/test_treemind.py | 66 +++++++++ 7 files changed, 324 insertions(+) create mode 100644 python/core/components/tree_mind/README.md create mode 100644 python/core/components/tree_mind/__init__.py create mode 100644 python/core/components/tree_mind/component.py create mode 100644 python/core/components/tree_mind/model.py create mode 100644 python/tests/test_treemind.py diff --git a/python/__init__.py b/python/__init__.py index efe4a79ce..a0bdcf71e 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -114,6 +114,7 @@ def get_default_header(): from .core.components.image_understand.component import ImageUnderstand from .core.components.mix_card_ocr.component import MixCardOCR from .core.components.document_understanding.component import DocumentUnderstanding +from .core.components.tree_mind.component import TreeMind __COMPONENTS__ = [ "RagWithBaiduSearchPro", "RAGWithBaiduSearch", @@ -164,6 +165,7 @@ def get_default_header(): "ImageUnderstand", "MixCardOCR", "DocumentUnderstanding", + "TreeMind" ] # NOQA from appbuilder.core.message import Message diff --git a/python/core/components/tree_mind/README.md b/python/core/components/tree_mind/README.md new file mode 100644 index 000000000..0dd22fb3c --- /dev/null +++ b/python/core/components/tree_mind/README.md @@ -0,0 +1,60 @@ +# 树图 (TreeMind) + +## 简介 +树图(TreeMind)提供智能思维导图制作工具和丰富的模板,支持脑图、逻辑图、树形图、鱼骨图、组织架构图、时间轴、时间线等多种专业格式。 + +### 功能介绍 +树图(TreeMind)是一款智能思维导图制作工具,它提供了一个用户友好的平台来创建和编辑各种类型的图表。该工具支持多种专业格式,包括脑图、逻辑图、树形图、鱼骨图、组织架构图、时间轴和时间线等,满足不同用户在不同场景下的需求。 + +### 特色优势 +TreeMind提供丰富的模板,支持多种图表格式,用户可以根据个人需求自由编辑和调整图表。 + +### 应用场景 +年度总结:用户可以利用TreeMind生成年度总结的思维导图,整理和回顾一年的工作成果和经验教训。 +项目管理:在项目管理中,TreeMind可以用来规划项目流程、组织架构和时间线,确保项目按计划进行。 +教育和学习:教师和学生可以使用TreeMind来创建课程大纲、学习笔记和复习资料,提高学习效率。 +商业策划:商业人士可以利用TreeMind来制定商业策略、市场分析和竞争对手分析等。 +会议记录:在会议中,TreeMind可以作为记录工具,帮助整理会议要点和行动计划。 + +## 基本用法 + +下面是文生图的代码示例: + +```python +import os +import appbuilder +# 设置环境变量和初始化 +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = "..." + +treemind = appbuilder.TreeMind() +query = "生成一份年度总结的思维导图" +msg = appbuilder.Message(query) +out = treemind.run(msg) +print(out.content) +``` +{'result': '生成的思维导图:![图片url](https://static.shutu.cn/shutu/static/open6e/2024/05/24/dbd67eddec13f3a6a75857b9c6e06d85.jpeg)思维导图已经为您生成好了,如果您觉得这个思维导图还不够完美,或者您的想法需要更自由地表达,点击编辑按钮,对思维导图变形、变色、变内容、甚至可以添加新的元素,您可以通过这个链接编辑:https://gapi.shutu.cn/ai/edit-mind-url?works_guid=open5ab4af46187ff7c138fcd95de09efe92_bdappbuilder。', 'img_link': 'https://static.shutu.cn/shutu/static/open6e/2024/05/24/dbd67eddec13f3a6a75857b9c6e06d85.jpeg', 'edit_link': 'https://gapi.shutu.cn/ai/edit-mind-url?works_guid=open5ab4af46187ff7c138fcd95de09efe92_bdappbuilder'} + + +## 参数说明 + +### 鉴权配置 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 初始化参数 +无 + +### 调用参数 +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +|---------|---------|------|-----------------------------|------------------------------------------------| +| message | obj:`Message` | 是 | 输入的消息,用于生成思维导图,这是一个必需的参数 | Message(content={"query": "生成一张年终总结的思维导图"}) | + +### 响应参数 + +| 参数名称 |参数类型 | 描述 | 示例 | +|-------------|--------|------|------| +| resp | obj:`Message` | 组件返回结果 | Message(name=msg, content={'result': '生成的思维导图:xxx。思维导图已经为您生成好了,如果您觉得这个思维导图还不够完美,或者您的想法需要更自由地表达,点击编辑按钮,对思维导图变形、变色、变内容、甚至可以添加新的元素,您可以通过这个链接编辑:xxx。', 'img_link': 'xxx', 'edit_link': 'xxx'}, mtype=dict) | \ No newline at end of file diff --git a/python/core/components/tree_mind/__init__.py b/python/core/components/tree_mind/__init__.py new file mode 100644 index 000000000..3e12d0913 --- /dev/null +++ b/python/core/components/tree_mind/__init__.py @@ -0,0 +1,14 @@ +# Copyright (c) 2023 Baidu, Inc. All Rights Reserved. +# 版权所有 (c) 2023 百度公司。保留所有权利。 +# +# 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. \ No newline at end of file diff --git a/python/core/components/tree_mind/component.py b/python/core/components/tree_mind/component.py new file mode 100644 index 000000000..a6d317b35 --- /dev/null +++ b/python/core/components/tree_mind/component.py @@ -0,0 +1,136 @@ +# Copyright (c) 2023 Baidu, Inc. 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. + +r"""树图工具""" + +import json +from typing import Dict, List, Optional, Any +from appbuilder.core.message import Message +from appbuilder.core._client import HTTPClient +from appbuilder.core._exception import * +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace +from appbuilder.core.components.tree_mind.model import TreeMindRequest, TreeMindResponse + + +from appbuilder.core.component import Component + +class TreeMind(Component): + r""" + 树图工具,提供智能思维导图制作工具和丰富的模板,支持脑图、逻辑图、树形图、鱼骨图、组织架构图、时间轴、时间线等多种专业格式。 + .. code-block:: python + + import os + import requests + import appbuilder + # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 + os.environ["GATEWAY_URL"] = "..." + os.environ["APPBUILDER_TOKEN"] = "..." + treemind = appbuilder.TreeMind() + resp = treemind.run(appbuilder.Message("生成一份年度总结的思维导图"), to_lang="en") + print(resp.content) + # 输出 {'from_lang':'zh', 'to_lang':'en', 'trans_result':[{'src':'你好','dst':'hello'},{'src':'中国','dst':'China'}]} + """ + + name = "tree_mind" + version = "v1" + manifests = [ + { + "name": "tree_mind", + "description": "根据用户输入的信息,生成详细智能思维导图、脑图、逻辑图、树形图、鱼骨图、组织架构图、时间轴、时间线等多种专业格式思维导图", + "parameters": { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "用户想要生成思维导图的内容" + }, + }, + "required": [ + "query" + ] + } + } + ] + + def _post(self, query, **kwargs): + if query is None or query == "": + raise InvalidRequestArgumentError("query is empty!" ) + request = TreeMindRequest(query_text=query) + headers = self.http_client.auth_header(kwargs.get("traceid")) + + headers['Content-Type'] = 'application/json' + tree_mind_url = self.http_client.service_url("/v1/component/component/query_mind_open") + + payload = TreeMindRequest.model_dump(request) + + response = self.http_client.session.post(tree_mind_url, headers=headers, json=payload) + self.http_client.check_response_header(response) + data = response.text + treemind_dict = json.loads(data.split("data:")[-1]) + treemind_response = TreeMindResponse(**treemind_dict) + jump_link = treemind_response.info.downloadInfo.fileInfo.jumpLink + img_link = treemind_response.info.downloadInfo.fileInfo.pic + return img_link, jump_link + + @components_run_stream_trace + def tool_eval( + self, + query, + **kwargs, + ): + r"""调用树图查询接口 + Args: + query (string): 用户想要生成思维导图的内容 + Returns: + dict: 返回生成的思维导图的图片链接和跳转链接 + """ + + img_link, jump_link = self._post(query, **kwargs) + + inst = "你必须遵循指令,输出无需总结,只需要将,“原样输出内容”对应的内容原样输出即可:\n" + img_res = f"原样输出内容:![图片url]({img_link})\n" + jump_res = f"{query}的思维导图已经为您生成好了,您可以通过这个链接编辑:编辑链接:{jump_link}。" + end_talk = "如果您觉得这个思维导图还不够完美,或者您的想法需要更自由地表达,点击编辑按钮,对思维导图变形、变色、变内容、甚至可以添加新的元素,快来试试吧!" + result = inst + img_res + jump_res + end_talk + urls = [img_link, jump_link] + yield { + "type": "text", + "text": result, + "visible_scope": 'llm', + } + yield { + "type": "urls", + "text": urls, + "visible_scope": 'all', + } + + + @HTTPClient.check_param + @components_run_trace + def run(self, message: Message, **kwargs) -> Message: + """运行组件 + Args: + message (Message): 消息对象 + Returns: + Message: 返回消息对象 + """ + query = message.content + img_link, jump_link = self._post(query, **kwargs) + + result = { + "result": "生成的思维导图:![图片url]({img_link})思维导图已经为您生成好了,如果您觉得这个思维导图还不够完美,或者您的想法需要更自由地表达,点击编辑按钮,对思维导图变形、变色、变内容、甚至可以添加新的元素,您可以通过这个链接编辑:{jump_link}。".format(img_link=img_link, jump_link=jump_link), + "img_link": img_link, + "edit_link": jump_link + } + return Message(content=result) \ No newline at end of file diff --git a/python/core/components/tree_mind/model.py b/python/core/components/tree_mind/model.py new file mode 100644 index 000000000..d0fa54986 --- /dev/null +++ b/python/core/components/tree_mind/model.py @@ -0,0 +1,45 @@ +# Copyright (c) 2023 Baidu, Inc. 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. +""" +树图model +""" + +import proto +from typing import List +from pydantic import BaseModel + + +class TreeMindRequest(BaseModel): + query_text:str + +class FileInfo(BaseModel): + jumpLink: str + jumpText: str + pic: str + +class DownloadInfo(BaseModel): + fileInfo: FileInfo + endDesc: str + +class Info(BaseModel): + downloadInfo: DownloadInfo + +class TreeMindResponse(BaseModel): + errCode: str + errMsg: str + time_diff: int + info: Info + + + diff --git a/python/tests/component_check.py b/python/tests/component_check.py index 64ff4b060..98ac5444e 100644 --- a/python/tests/component_check.py +++ b/python/tests/component_check.py @@ -160,6 +160,7 @@ def check(self, component_cls) -> CheckInfo: for param_name, param in signature.parameters.items(): if param_name == 'kwargs' or param_name == 'args' or param_name == 'self': continue + tool_eval_input_params.append(param_name) if param_name not in required_params: check_pass_flag = False ileagal_params.append(param_name) diff --git a/python/tests/test_treemind.py b/python/tests/test_treemind.py new file mode 100644 index 000000000..ebb7be071 --- /dev/null +++ b/python/tests/test_treemind.py @@ -0,0 +1,66 @@ +# Copyright (c) 2024 Baidu, Inc. 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 os +import unittest +from appbuilder.core.message import Message +from appbuilder import TreeMind + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") +class TestTreeMindComponent(unittest.TestCase): + def setUp(self): + """ + 初始化测试用例,设置环境变量和网关URL。 + 如果没有设置CAR_EXPERT_TOKEN环境变量,则使用空字符串。 + Args: + None. + Returns: + None. + """ + self.tm = TreeMind() + self.query = "生成一份年度总结的思维导图" + + def test_treemind_component_tool_eval(self): + """测试tool_eval方法的返回值是否正确 + """ + result = self.tm.tool_eval(query=self.query) + self.assertIsNotNone(result) + for r in result: + print(r) + self.assertIsNotNone(r) + self.assertIn(r["type"],["text", "urls"]) + + def test_treemind_component_run(self): + """测试run函数的返回值是否正确 + """ + msg = Message(content=self.query) + result = self.tm.run(msg) + print(result) + self.assertIsNotNone(result) + self.assertIsInstance(result, Message) + + def test_run_with_invalid_input(self): + """测试run函数在传入无效输入的情况下的行为。 + """ + message = Message(content={}) + with self.assertRaises(ValueError): + self.tm.run(message) + + def test_tool_eval_invalid(self): + """测试 tool 方法传入无效输入的情况下的行为""" + with self.assertRaises(TypeError): + result = self.tm.tool_eval(name="treemind", streaming=True, origin_query="") + next(result) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file From 29655a3d0b2b4f1678e06b57fb9942982df8dc6d Mon Sep 17 00:00:00 2001 From: peiwenYe <963623403@qq.com> Date: Mon, 11 Nov 2024 10:44:32 +0800 Subject: [PATCH 10/85] =?UTF-8?q?=E4=BC=98=E5=8C=96TreeMind=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E8=BE=93=E5=87=BA=E8=A7=86=E8=A7=89=E6=95=88=E6=9E=9C?= =?UTF-8?q?=20(#586)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: yepeiwen01 --- python/core/components/tree_mind/README.md | 6 +++++- python/core/components/tree_mind/component.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/python/core/components/tree_mind/README.md b/python/core/components/tree_mind/README.md index 0dd22fb3c..3c2b546ff 100644 --- a/python/core/components/tree_mind/README.md +++ b/python/core/components/tree_mind/README.md @@ -32,8 +32,12 @@ query = "生成一份年度总结的思维导图" msg = appbuilder.Message(query) out = treemind.run(msg) print(out.content) + +>>> +{'result': '思维导图已经为您生成好了,您可以点击'img_link'对应的链接查看,如果您觉得这个思维导图还不够完美,或者您的想法需要更自由地表达,点击'edit_link'对应的链接,对思维导图变形、变色、变内容、甚至可以添加新的元素, 'img_link': 'https://static.shutu.cn/shutu/static/open6e/2024/05/24/dbd67eddec13f3a6a75857b9c6e06d85.jpeg', 'edit_link': 'https://gapi.shutu.cn/ai/edit-mind-url?works_guid=open5ab4af46187ff7c138fcd95de09efe92_bdappbuilder'} ``` -{'result': '生成的思维导图:![图片url](https://static.shutu.cn/shutu/static/open6e/2024/05/24/dbd67eddec13f3a6a75857b9c6e06d85.jpeg)思维导图已经为您生成好了,如果您觉得这个思维导图还不够完美,或者您的想法需要更自由地表达,点击编辑按钮,对思维导图变形、变色、变内容、甚至可以添加新的元素,您可以通过这个链接编辑:https://gapi.shutu.cn/ai/edit-mind-url?works_guid=open5ab4af46187ff7c138fcd95de09efe92_bdappbuilder。', 'img_link': 'https://static.shutu.cn/shutu/static/open6e/2024/05/24/dbd67eddec13f3a6a75857b9c6e06d85.jpeg', 'edit_link': 'https://gapi.shutu.cn/ai/edit-mind-url?works_guid=open5ab4af46187ff7c138fcd95de09efe92_bdappbuilder'} + +生成的思维导图:
![图片url](https://bj.bcebos.com/appbuilder-sdk-components/TreeMind-年终总结思维导图.jpeg) ## 参数说明 diff --git a/python/core/components/tree_mind/component.py b/python/core/components/tree_mind/component.py index a6d317b35..9a5cc776a 100644 --- a/python/core/components/tree_mind/component.py +++ b/python/core/components/tree_mind/component.py @@ -129,7 +129,7 @@ def run(self, message: Message, **kwargs) -> Message: img_link, jump_link = self._post(query, **kwargs) result = { - "result": "生成的思维导图:![图片url]({img_link})思维导图已经为您生成好了,如果您觉得这个思维导图还不够完美,或者您的想法需要更自由地表达,点击编辑按钮,对思维导图变形、变色、变内容、甚至可以添加新的元素,您可以通过这个链接编辑:{jump_link}。".format(img_link=img_link, jump_link=jump_link), + "result": "思维导图已经为您生成好了,您可以点击'img_link'对应的链接查看,如果您觉得这个思维导图还不够完美,或者您的想法需要更自由地表达,点击'edit_link'对应的链接,对思维导图变形、变色、变内容、甚至可以添加新的元素", "img_link": img_link, "edit_link": jump_link } From 4d1ea986892181925302b9ab6f88ef11bd6e6295 Mon Sep 17 00:00:00 2001 From: peiwenYe <963623403@qq.com> Date: Mon, 11 Nov 2024 18:59:07 +0800 Subject: [PATCH 11/85] =?UTF-8?q?TreeMind=20test=20case=E4=B8=AD=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0sleep=E6=93=8D=E4=BD=9C=EF=BC=8C=E8=A7=84=E9=81=BFQPS?= =?UTF-8?q?=E8=B6=85=E9=99=90=E6=8A=A5=E9=94=99=20(#588)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 优化TreeMind组件输出视觉效果 * 优化TreeMind组件视觉输出效果 * test case中增加sleep操作,规避QPS超限报错 --------- Co-authored-by: yepeiwen01 --- python/core/components/tree_mind/README.md | 6 ++++-- python/tests/test_treemind.py | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/python/core/components/tree_mind/README.md b/python/core/components/tree_mind/README.md index 3c2b546ff..3d8ebaaaa 100644 --- a/python/core/components/tree_mind/README.md +++ b/python/core/components/tree_mind/README.md @@ -34,7 +34,9 @@ out = treemind.run(msg) print(out.content) >>> -{'result': '思维导图已经为您生成好了,您可以点击'img_link'对应的链接查看,如果您觉得这个思维导图还不够完美,或者您的想法需要更自由地表达,点击'edit_link'对应的链接,对思维导图变形、变色、变内容、甚至可以添加新的元素, 'img_link': 'https://static.shutu.cn/shutu/static/open6e/2024/05/24/dbd67eddec13f3a6a75857b9c6e06d85.jpeg', 'edit_link': 'https://gapi.shutu.cn/ai/edit-mind-url?works_guid=open5ab4af46187ff7c138fcd95de09efe92_bdappbuilder'} +{'result': '思维导图已经为您生成好了,您可以点击'img_link'对应的链接查看,如果您觉得这个思维导图还不够完美,或者您的想法需要更自由地表达,点击'edit_link'对应的链接,对思维导图变形、变色、变内容、甚至可以添加新的元素, +'img_link': 'https://static.shutu.cn/shutu/static/open6e/2024/05/24/dbd67eddec13f3a6a75857b9c6e06d85.jpeg', +'edit_link': 'https://gapi.shutu.cn/ai/edit-mind-url?works_guid=open5ab4af46187ff7c138fcd95de09efe92_bdappbuilder'} ``` 生成的思维导图:
![图片url](https://bj.bcebos.com/appbuilder-sdk-components/TreeMind-年终总结思维导图.jpeg) @@ -59,6 +61,6 @@ os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" ### 响应参数 -| 参数名称 |参数类型 | 描述 | 示例 | +| 参数名称 |参数类型 | 描述 | 示例值 | |-------------|--------|------|------| | resp | obj:`Message` | 组件返回结果 | Message(name=msg, content={'result': '生成的思维导图:xxx。思维导图已经为您生成好了,如果您觉得这个思维导图还不够完美,或者您的想法需要更自由地表达,点击编辑按钮,对思维导图变形、变色、变内容、甚至可以添加新的元素,您可以通过这个链接编辑:xxx。', 'img_link': 'xxx', 'edit_link': 'xxx'}, mtype=dict) | \ No newline at end of file diff --git a/python/tests/test_treemind.py b/python/tests/test_treemind.py index ebb7be071..823c9e13f 100644 --- a/python/tests/test_treemind.py +++ b/python/tests/test_treemind.py @@ -33,6 +33,8 @@ def setUp(self): def test_treemind_component_tool_eval(self): """测试tool_eval方法的返回值是否正确 """ + import time + time.sleep(1) result = self.tm.tool_eval(query=self.query) self.assertIsNotNone(result) for r in result: From 1e09e101aaf14ef81083c039b400af37004385b8 Mon Sep 17 00:00:00 2001 From: C9luster <138663536+C9luster@users.noreply.github.com> Date: Tue, 12 Nov 2024 09:34:34 +0800 Subject: [PATCH 12/85] =?UTF-8?q?=E6=9B=B4=E6=96=B0python=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95=E6=B5=81=E6=B0=B4=E7=BA=BF=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=E9=80=BB=E8=BE=91,=E8=A7=A3=E5=86=B3=E5=A2=9E?= =?UTF-8?q?=E9=87=8F=E8=A6=86=E7=9B=96=E7=8E=87=E6=A3=80=E6=B5=8B=E9=97=AE?= =?UTF-8?q?=E9=A2=98=20(#590)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 更新python单元测试流水线脚本逻辑,解决增量覆盖率检测问题 Co-authored-by: yinjiaqi --- python/tests/run_python_test.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python/tests/run_python_test.sh b/python/tests/run_python_test.sh index cce13512b..00f1de742 100644 --- a/python/tests/run_python_test.sh +++ b/python/tests/run_python_test.sh @@ -86,11 +86,13 @@ python_lib=$(python3 -m pip show appbuilder-sdk | grep Location | awk '{print $2 git_dir=$(pwd | sed 's/python\/tests//') # 批量替换coverage.xml文件中的python_lib为git_dir,并将源文件备份一个orignal.xml后缀 python3 -u sed_str.py coverage.xml $python_lib $git_dir +sed -i 's|/appbuilder|python|g' coverage.xml # 最后进行增量代码覆盖率测试 echo "增量代码覆盖率为:" diff-cover coverage.xml --compare-branch=upstream/master --html-report coverage_diff.html --fail-under=90 cover_result=$? + echo "--------------------------" echo "CI 流水线运行结果如下: " echo "单测运行结果: $run_result" From e26db447d952f3c73dcc8b5247503fbcab5b77c3 Mon Sep 17 00:00:00 2001 From: C9luster <138663536+C9luster@users.noreply.github.com> Date: Wed, 13 Nov 2024 14:35:24 +0800 Subject: [PATCH 13/85] =?UTF-8?q?SDK=20=E6=96=87=E6=A1=A3=E7=BB=93?= =?UTF-8?q?=E6=9E=84=E5=8D=87=E7=BA=A7=EF=BC=8C=E4=BC=98=E5=8C=96=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E7=94=9F=E6=88=90=E6=96=87=E6=A1=A3=E8=84=9A=E6=9C=AC?= =?UTF-8?q?=20(#593)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * SDK文档结构调整以及链接地址更换 * 测试mkdocs文档是否运行正常 * 更改.gitignore文件 * 回滚更改.gitignore文件 * 更改mkdocs.yml文件配置 * 更改mkdocs文件配置 * 完成SDK代码库脚本及文档更新 * 更行Python-API文档相对路径 * 添加API文档目录 * 更改API目录文档 * 完成组件文档迁移功能,并完善整个文档 * 更新脚本注释 --------- Co-authored-by: yinjiaqi --- .gitignore | 1 + docs/API-Reference/Python/PythonAPI.md | 7 + .../appbuilder.core.assistant.assistants.md | 126 +- .../Python}/appbuilder.core.assistant.md | 0 .../appbuilder.core.assistant.threads.md | 18 +- ...builder.core.assistant.threads.messages.md | 88 + .../appbuilder.core.assistant.threads.runs.md | 176 +- ...uilder.core.components.animal_recognize.md | 14 +- .../Python}/appbuilder.core.components.asr.md | 20 +- ...pbuilder.core.components.dish_recognize.md | 8 +- ...uilder.core.components.doc_crop_enhance.md | 14 +- ...er.core.components.doc_format_converter.md | 18 +- .../appbuilder.core.components.doc_parser.md | 12 +- ...appbuilder.core.components.doc_splitter.md | 36 +- ....core.components.document_understanding.md | 59 + .../appbuilder.core.components.embeddings.md | 6 +- ...appbuilder.core.components.excel2figure.md | 32 +- ...ppbuilder.core.components.extract_table.md | 8 +- .../Python}/appbuilder.core.components.gbi.md | 0 .../appbuilder.core.components.gbi.nl2sql.md | 6 +- ...uilder.core.components.gbi.select_table.md | 8 +- .../appbuilder.core.components.general_ocr.md | 16 +- ...ppbuilder.core.components.handwrite_ocr.md | 16 +- ...uilder.core.components.image_understand.md | 16 +- ...lder.core.components.landmark_recognize.md | 10 +- ...der.core.components.llms.dialog_summary.md | 10 +- ...components.llms.hallucination_detection.md | 30 +- ...r.core.components.llms.is_complex_query.md | 8 +- .../appbuilder.core.components.llms.md | 0 .../appbuilder.core.components.llms.mrc.md | 20 +- ...pbuilder.core.components.llms.nl2pandas.md | 10 +- ...e.components.llms.oral_query_generation.md | 24 +- ...builder.core.components.llms.playground.md | 18 +- ...der.core.components.llms.qa_pair_mining.md | 8 +- ...ore.components.llms.query_decomposition.md | 8 +- ...lder.core.components.llms.query_rewrite.md | 14 +- ...r.core.components.llms.similar_question.md | 16 +- ...lder.core.components.llms.style_rewrite.md | 20 +- ...lder.core.components.llms.style_writing.md | 28 +- ...der.core.components.llms.tag_extraction.md | 8 +- .../appbuilder.core.components.matching.md | 10 +- .../Python}/appbuilder.core.components.md | 22 + ...appbuilder.core.components.mix_card_ocr.md | 20 +- ...uilder.core.components.object_recognize.md | 26 +- ...builder.core.components.plant_recognize.md | 16 +- ...ore.components.ppt_generation_from_file.md | 34 +- ...ponents.ppt_generation_from_instruction.md | 34 +- ...re.components.ppt_generation_from_paper.md | 36 +- .../appbuilder.core.components.qrcode_ocr.md | 34 +- ...r.core.components.rag_with_baidu_search.md | 22 +- ...re.components.rag_with_baidu_search_pro.md | 26 +- ...der.core.components.retriever.baidu_vdb.md | 44 +- ...ppbuilder.core.components.retriever.bes.md | 26 +- .../appbuilder.core.components.retriever.md | 0 ...lder.core.components.retriever.reranker.md | 4 +- .../appbuilder.core.components.table_ocr.md | 44 +- ...ppbuilder.core.components.text_to_image.md | 52 +- .../appbuilder.core.components.translate.md | 22 +- .../appbuilder.core.components.tree_mind.md | 53 + .../Python}/appbuilder.core.components.tts.md | 24 +- ...pbuilder.core.console.appbuilder_client.md | 76 +- .../appbuilder.core.console.dataset.md | 58 +- .../appbuilder.core.console.knowledge_base.md | 132 +- .../Python}/appbuilder.core.console.md | 1 + .../Python}/appbuilder.core.console.rag.md | 6 +- .../Python}/appbuilder.core.md | 72 +- .../Python}/appbuilder.md | 0 .../Python}/index.md | 0 .../Python}/modules.md | 0 .../RAG/BasicKnowledge}/rag.md | 0 .../Components/animal_recognize/README.md | 112 ++ docs/BasisModule/Components/asr/README.md | 82 + .../Components/dish_recognize/README.md | 77 + .../Components/doc_crop_enhance/README.md | 95 ++ .../Components/doc_format_converter/README.md | 71 + .../Components/doc_parser/README.md | 210 +++ .../Components/doc_splitter/README.md | 163 ++ .../document_understanding/README.md | 96 ++ .../Components/embeddings/README.md | 156 ++ .../Components/excel2figure/README.md | 86 + .../Components/extract_table/README.md | 88 + .../Components/gbi/nl2sql/README.md | 261 +++ .../Components/gbi/select_table/README.md | 132 ++ .../Components/general_ocr/README.md | 113 ++ .../Components/handwrite_ocr/README.md | 153 ++ .../Components/image_understand/README.md | 87 + .../Components/landmark_recognize/README.md | 88 + .../Components/llms/dialog_summary/README.md | 76 + .../llms/hallucination_detection/README.md | 95 ++ .../llms/is_complex_query/README.md | 76 + .../BasisModule/Components/llms/mrc/README.md | 145 ++ .../Components/llms/nl2pandas/README.md | 68 + .../llms/oral_query_generation/README.md | 107 ++ .../Components/llms/playground/README.md | 82 + .../Components/llms/qa_pair_mining/README.md | 125 ++ .../llms/query_decomposition/README.md | 75 + .../Components/llms/query_rewrite/README.md | 66 + .../llms/similar_question/README.md | 90 + .../Components/llms/style_rewrite/README.md | 82 + .../Components/llms/style_writing/README.md | 78 + .../Components/llms/tag_extraction/README.md | 83 + .../BasisModule/Components/matching/README.md | 89 + .../Components/mix_card_ocr/README.md | 215 +++ .../Components/object_recognize/README.md | 115 ++ .../Components/plant_recognize/README.md | 116 ++ .../ppt_generation_from_file/README.md | 83 + .../ppt_generation_from_instruction/README.md | 90 + .../ppt_generation_from_paper/README.md | 97 ++ .../Components/qrcode_ocr/README.md | 111 ++ .../rag_with_baidu_search/README.md | 158 ++ .../rag_with_baidu_search_pro/README.md | 134 ++ .../Components/retriever/README.md | 10 + .../Components/retriever/baidu_vdb/README.md | 108 ++ .../Components/retriever/bes/README.md | 88 + .../Components/retriever/reranker/README.md | 91 + .../Components/table_ocr/README.md | 161 ++ .../Components/text_to_image/README.md | 93 + .../Components/translate/README.md | 316 ++++ .../Components/tree_mind/README.md | 66 + docs/BasisModule/Components/tts/README.md | 165 ++ .../Deployment/AgentChainlit.md} | 0 .../Deployment}/agentruntime.md | 0 .../Deployment}/cloud.md | 9 +- .../Deployment}/flask.md | 0 .../Deployment}/usersession.md | 0 .../Model}/get_model_list.md | 0 .../Application}/appbuilder_client.md | 0 .../Platform/Application}/get_app_list.md | 0 .../Platform/CustomComponents}/components.md | 0 .../Platform/KnowledgeBase}/knowledgebase.md | 0 docs/BasisModule/Trace/README.md | 7 + docs/{trace => BasisModule/Trace}/basic.md | 0 .../Trace}/phoenix_method.md | 0 .../ChangeLog}/changelog.md | 2 +- .../EnvironmentalParameters}/env.md | 0 .../HowToContributeCode}/README.md | 4 +- .../README.md | 8 + .../README.md | 16 +- .../StartFirstAINativeApplication}/README.md | 4 +- .../StartFirstAINativeApplication}/install.md | 0 docs/README.md | 82 +- docs/README_en.md | 6 +- docs/README_ja.md | 6 +- docs/Tools/DocPass/DocPass.md | 1 + .../MarkdownSh}/markdown2rst.py | 0 .../MarkdownSh}/markdown_parse.py | 0 docs/{sphinx_doc => Tools/SphinxSh}/Makefile | 0 docs/Tools/SphinxSh/PythonAPI.md | 7 + .../{sphinx_doc => Tools/SphinxSh}/READEME.md | 11 +- .../SphinxSh}/appbuilder.core.rst | 0 docs/Tools/SphinxSh/get_components_md.py | 71 + docs/{sphinx_doc => Tools/SphinxSh}/make.bat | 0 .../SphinxSh}/requirements.txt | 0 .../SphinxSh}/source/conf.py | 0 .../SphinxSh}/source/index.rst | 0 .../SphinxSh}/update_doc.sh | 65 +- .../SphinxSh}/update_rst.py | 0 docs/advanced_application/README.md | 8 - docs/advanced_application/real_practice.md | 3 - docs/basic_module/assistant_sdk.md | 296 ---- docs/basic_module/assistant_type.md | 1506 ----------------- docs/basic_module/dataset.md | 231 --- docs/service/README.md | 7 - ...builder.core.assistant.threads.messages.md | 88 - docs/trace/README.md | 7 - mkdocs.yml | 123 ++ 166 files changed, 7015 insertions(+), 3008 deletions(-) create mode 100644 docs/API-Reference/Python/PythonAPI.md rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.assistant.assistants.md (53%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.assistant.md (100%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.assistant.threads.md (93%) create mode 100644 docs/API-Reference/Python/appbuilder.core.assistant.threads.messages.md rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.assistant.threads.runs.md (70%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.animal_recognize.md (81%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.asr.md (70%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.dish_recognize.md (83%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.doc_crop_enhance.md (70%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.doc_format_converter.md (80%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.doc_parser.md (79%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.doc_splitter.md (76%) create mode 100644 docs/API-Reference/Python/appbuilder.core.components.document_understanding.md rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.embeddings.md (88%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.excel2figure.md (61%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.extract_table.md (80%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.gbi.md (100%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.gbi.nl2sql.md (89%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.gbi.select_table.md (79%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.general_ocr.md (76%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.handwrite_ocr.md (78%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.image_understand.md (78%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.landmark_recognize.md (75%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.llms.dialog_summary.md (72%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.llms.hallucination_detection.md (77%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.llms.is_complex_query.md (66%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.llms.md (100%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.llms.mrc.md (58%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.llms.nl2pandas.md (65%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.llms.oral_query_generation.md (74%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.llms.playground.md (61%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.llms.qa_pair_mining.md (55%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.llms.query_decomposition.md (66%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.llms.query_rewrite.md (70%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.llms.similar_question.md (70%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.llms.style_rewrite.md (62%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.llms.style_writing.md (76%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.llms.tag_extraction.md (76%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.matching.md (82%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.md (94%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.mix_card_ocr.md (73%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.object_recognize.md (70%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.plant_recognize.md (81%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.ppt_generation_from_file.md (68%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.ppt_generation_from_instruction.md (67%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.ppt_generation_from_paper.md (67%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.qrcode_ocr.md (62%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.rag_with_baidu_search.md (50%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.rag_with_baidu_search_pro.md (70%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.retriever.baidu_vdb.md (64%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.retriever.bes.md (79%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.retriever.md (100%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.retriever.reranker.md (87%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.table_ocr.md (54%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.text_to_image.md (58%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.translate.md (81%) create mode 100644 docs/API-Reference/Python/appbuilder.core.components.tree_mind.md rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.components.tts.md (63%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.console.appbuilder_client.md (61%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.console.dataset.md (51%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.console.knowledge_base.md (58%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.console.md (98%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.console.rag.md (91%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.core.md (85%) rename docs/{sphinx_md => API-Reference/Python}/appbuilder.md (100%) rename docs/{sphinx_md => API-Reference/Python}/index.md (100%) rename docs/{sphinx_md => API-Reference/Python}/modules.md (100%) rename docs/{basic_module => Application/RAG/BasicKnowledge}/rag.md (100%) create mode 100644 docs/BasisModule/Components/animal_recognize/README.md create mode 100644 docs/BasisModule/Components/asr/README.md create mode 100644 docs/BasisModule/Components/dish_recognize/README.md create mode 100644 docs/BasisModule/Components/doc_crop_enhance/README.md create mode 100644 docs/BasisModule/Components/doc_format_converter/README.md create mode 100644 docs/BasisModule/Components/doc_parser/README.md create mode 100644 docs/BasisModule/Components/doc_splitter/README.md create mode 100644 docs/BasisModule/Components/document_understanding/README.md create mode 100644 docs/BasisModule/Components/embeddings/README.md create mode 100644 docs/BasisModule/Components/excel2figure/README.md create mode 100644 docs/BasisModule/Components/extract_table/README.md create mode 100644 docs/BasisModule/Components/gbi/nl2sql/README.md create mode 100644 docs/BasisModule/Components/gbi/select_table/README.md create mode 100644 docs/BasisModule/Components/general_ocr/README.md create mode 100644 docs/BasisModule/Components/handwrite_ocr/README.md create mode 100644 docs/BasisModule/Components/image_understand/README.md create mode 100644 docs/BasisModule/Components/landmark_recognize/README.md create mode 100644 docs/BasisModule/Components/llms/dialog_summary/README.md create mode 100644 docs/BasisModule/Components/llms/hallucination_detection/README.md create mode 100644 docs/BasisModule/Components/llms/is_complex_query/README.md create mode 100644 docs/BasisModule/Components/llms/mrc/README.md create mode 100644 docs/BasisModule/Components/llms/nl2pandas/README.md create mode 100644 docs/BasisModule/Components/llms/oral_query_generation/README.md create mode 100644 docs/BasisModule/Components/llms/playground/README.md create mode 100644 docs/BasisModule/Components/llms/qa_pair_mining/README.md create mode 100644 docs/BasisModule/Components/llms/query_decomposition/README.md create mode 100644 docs/BasisModule/Components/llms/query_rewrite/README.md create mode 100644 docs/BasisModule/Components/llms/similar_question/README.md create mode 100644 docs/BasisModule/Components/llms/style_rewrite/README.md create mode 100644 docs/BasisModule/Components/llms/style_writing/README.md create mode 100644 docs/BasisModule/Components/llms/tag_extraction/README.md create mode 100644 docs/BasisModule/Components/matching/README.md create mode 100644 docs/BasisModule/Components/mix_card_ocr/README.md create mode 100644 docs/BasisModule/Components/object_recognize/README.md create mode 100644 docs/BasisModule/Components/plant_recognize/README.md create mode 100644 docs/BasisModule/Components/ppt_generation_from_file/README.md create mode 100644 docs/BasisModule/Components/ppt_generation_from_instruction/README.md create mode 100644 docs/BasisModule/Components/ppt_generation_from_paper/README.md create mode 100644 docs/BasisModule/Components/qrcode_ocr/README.md create mode 100644 docs/BasisModule/Components/rag_with_baidu_search/README.md create mode 100644 docs/BasisModule/Components/rag_with_baidu_search_pro/README.md create mode 100644 docs/BasisModule/Components/retriever/README.md create mode 100644 docs/BasisModule/Components/retriever/baidu_vdb/README.md create mode 100644 docs/BasisModule/Components/retriever/bes/README.md create mode 100644 docs/BasisModule/Components/retriever/reranker/README.md create mode 100644 docs/BasisModule/Components/table_ocr/README.md create mode 100644 docs/BasisModule/Components/text_to_image/README.md create mode 100644 docs/BasisModule/Components/translate/README.md create mode 100644 docs/BasisModule/Components/tree_mind/README.md create mode 100644 docs/BasisModule/Components/tts/README.md rename docs/{service/chainlit.md => BasisModule/Deployment/AgentChainlit.md} (100%) rename docs/{basic_module => BasisModule/Deployment}/agentruntime.md (100%) rename docs/{service => BasisModule/Deployment}/cloud.md (83%) rename docs/{service => BasisModule/Deployment}/flask.md (100%) rename docs/{basic_module => BasisModule/Deployment}/usersession.md (100%) rename docs/{basic_module => BasisModule/Model}/get_model_list.md (100%) rename docs/{basic_module => BasisModule/Platform/Application}/appbuilder_client.md (100%) rename docs/{basic_module => BasisModule/Platform/Application}/get_app_list.md (100%) rename docs/{basic_module => BasisModule/Platform/CustomComponents}/components.md (100%) rename docs/{basic_module => BasisModule/Platform/KnowledgeBase}/knowledgebase.md (100%) create mode 100644 docs/BasisModule/Trace/README.md rename docs/{trace => BasisModule/Trace}/basic.md (100%) rename docs/{trace => BasisModule/Trace}/phoenix_method.md (100%) rename docs/{quick_start => DevelopGuide/ChangeLog}/changelog.md (98%) rename docs/{develop_guide => DevelopGuide/EnvironmentalParameters}/env.md (100%) rename docs/{develop_guide => DevelopGuide/HowToContributeCode}/README.md (95%) create mode 100644 docs/QuickStart/CurrentlySupportedProgrammingLanguages/README.md rename docs/{basic_module => QuickStart/ExamplesOfIndustrialPracticeApplications}/README.md (85%) rename docs/{quick_start => QuickStart/StartFirstAINativeApplication}/README.md (98%) rename docs/{quick_start => QuickStart/StartFirstAINativeApplication}/install.md (100%) create mode 100644 docs/Tools/DocPass/DocPass.md rename docs/{tools => Tools/MarkdownSh}/markdown2rst.py (100%) rename docs/{tools => Tools/MarkdownSh}/markdown_parse.py (100%) rename docs/{sphinx_doc => Tools/SphinxSh}/Makefile (100%) create mode 100644 docs/Tools/SphinxSh/PythonAPI.md rename docs/{sphinx_doc => Tools/SphinxSh}/READEME.md (88%) rename docs/{sphinx_doc => Tools/SphinxSh}/appbuilder.core.rst (100%) create mode 100644 docs/Tools/SphinxSh/get_components_md.py rename docs/{sphinx_doc => Tools/SphinxSh}/make.bat (100%) rename docs/{sphinx_doc => Tools/SphinxSh}/requirements.txt (100%) rename docs/{sphinx_doc => Tools/SphinxSh}/source/conf.py (100%) rename docs/{sphinx_doc => Tools/SphinxSh}/source/index.rst (100%) rename docs/{sphinx_doc => Tools/SphinxSh}/update_doc.sh (69%) rename docs/{sphinx_doc => Tools/SphinxSh}/update_rst.py (100%) delete mode 100644 docs/advanced_application/README.md delete mode 100644 docs/advanced_application/real_practice.md delete mode 100644 docs/basic_module/assistant_sdk.md delete mode 100644 docs/basic_module/assistant_type.md delete mode 100644 docs/basic_module/dataset.md delete mode 100644 docs/service/README.md delete mode 100644 docs/sphinx_md/appbuilder.core.assistant.threads.messages.md delete mode 100644 docs/trace/README.md create mode 100644 mkdocs.yml diff --git a/.gitignore b/.gitignore index af8593a72..f8d30f31b 100644 --- a/.gitignore +++ b/.gitignore @@ -154,3 +154,4 @@ go/coverage_diff.html .gitignore go/appbuilder/coverage_full.html go/appbuilder/coverage_diff.html + diff --git a/docs/API-Reference/Python/PythonAPI.md b/docs/API-Reference/Python/PythonAPI.md new file mode 100644 index 000000000..4801ebacd --- /dev/null +++ b/docs/API-Reference/Python/PythonAPI.md @@ -0,0 +1,7 @@ +# Python API Reference + + +- [基础 API](appbuilder.md) + - [Assistant API](appbuilder.core.assistant.md) + - [Components API](appbuilder.core.components.md) + - [Console API](appbuilder.core.console.md) \ No newline at end of file diff --git a/docs/sphinx_md/appbuilder.core.assistant.assistants.md b/docs/API-Reference/Python/appbuilder.core.assistant.assistants.md similarity index 53% rename from docs/sphinx_md/appbuilder.core.assistant.assistants.md rename to docs/API-Reference/Python/appbuilder.core.assistant.assistants.md index 5ce5c50fe..35606e21b 100644 --- a/docs/sphinx_md/appbuilder.core.assistant.assistants.md +++ b/docs/API-Reference/Python/appbuilder.core.assistant.assistants.md @@ -8,21 +8,21 @@ 基类:`object` -#### create(name: str, description: str, model: str | None = 'ERNIE-4.0-8K', response_format: str | None = 'text', instructions: str | None = '你是百度制作的AI助手', thought_instructions: str | None = '', chat_instructions: str | None = '', tools: list[AssistantTool] | None = [], file_ids: list[str] | None = [], metadata: dict | None = {}) → AssistantCreateResponse +#### create(name: str, description: str, model: str | None = 'ERNIE-4.0T-8K', response_format: str | None = 'text', instructions: str | None = '你是百度制作的AI助手', thought_instructions: str | None = '', chat_instructions: str | None = '', tools: list[AssistantTool] | None = [], file_ids: list[str] | None = [], metadata: dict | None = {}) → AssistantCreateResponse 创建助手实例 * **参数:** - * **name** (*str*) – 助手名称 - * **description** (*str*) – 助手描述 - * **model** (*Optional* *[**str* *]* *,* *optional*) – 模型名称. Defaults to “ERNIE-4.0-8K”. - * **response_format** (*Optional* *[**str* *]* *,* *optional*) – 响应格式. Defaults to ‘text’. - * **instructions** (*Optional* *[**str* *]* *,* *optional*) – 指令. Defaults to “”. - * **thought_instructions** (*Optional* *[**str* *]* *,* *optional*) – 思考指令. Defaults to “”. - * **chat_instructions** (*Optional* *[**str* *]* *,* *optional*) – 聊天指令. Defaults to “”. - * **tools** (*Optional* *[**list* *[**assistant_type.AssistantTool* *]* *]* *,* *optional*) – 工具列表. Defaults to []. - * **file_ids** (*Optional* *[**list* *[**str* *]* *]* *,* *optional*) – 文件ID列表. Defaults to []. - * **metadata** (*Optional* *[**dict* *]* *,* *optional*) – 元数据. Defaults to {}. + * **name** (*str*) -- 助手名称 + * **description** (*str*) -- 助手描述 + * **model** (*Optional* *[**str* *]* *,* *optional*) -- 模型名称. Defaults to "ERNIE-4.0T-8K". + * **response_format** (*Optional* *[**str* *]* *,* *optional*) -- 响应格式. Defaults to 'text'. + * **instructions** (*Optional* *[**str* *]* *,* *optional*) -- 指令. Defaults to "". + * **thought_instructions** (*Optional* *[**str* *]* *,* *optional*) -- 思考指令. Defaults to "". + * **chat_instructions** (*Optional* *[**str* *]* *,* *optional*) -- 聊天指令. Defaults to "". + * **tools** (*Optional* *[**list* *[**assistant_type.AssistantTool* *]* *]* *,* *optional*) -- 工具列表. Defaults to []. + * **file_ids** (*Optional* *[**list* *[**str* *]* *]* *,* *optional*) -- 文件ID列表. Defaults to []. + * **metadata** (*Optional* *[**dict* *]* *,* *optional*) -- 元数据. Defaults to {}. * **返回:** 助手创建响应 * **返回类型:** @@ -33,13 +33,13 @@ 根据assistant_id删除指定Assitant * **参数:** - **assistant_id** (*Optional* *[**str* *]*) – 待删除的助手实例ID。 + **assistant_id** (*Optional* *[**str* *]*) -- 待删除的助手实例ID。 * **返回:** 删除助手实例后的响应结果。 * **返回类型:** assistant_type.AssistantDeleteResponse * **抛出:** - **HttpRequestError** – 发送HTTP请求时发生错误。 + **HttpRequestError** -- 发送HTTP请求时发生错误。 #### *property* files @@ -57,10 +57,10 @@ 查询当前用户已创建的assistant列表 * **参数:** - * **limit** (*Optional* *[**int* *]* *,* *optional*) – 返回助手列表的最大数量,默认为20。 - * **order** (*Optional* *[**str* *]* *,* *optional*) – 返回助手列表的排序方式,可选值为”asc”或”desc”,默认为”desc”。 - * **after** (*Optional* *[**str* *]* *,* *optional*) – 返回助手列表中id在指定id之后的助手,默认为空字符串。 - * **before** (*Optional* *[**str* *]* *,* *optional*) – 返回助手列表中id在指定id之前的助手,默认为空字符串。 + * **limit** (*Optional* *[**int* *]* *,* *optional*) -- 返回助手列表的最大数量,默认为20。 + * **order** (*Optional* *[**str* *]* *,* *optional*) -- 返回助手列表的排序方式,可选值为"asc"或"desc",默认为"desc"。 + * **after** (*Optional* *[**str* *]* *,* *optional*) -- 返回助手列表中id在指定id之后的助手,默认为空字符串。 + * **before** (*Optional* *[**str* *]* *,* *optional*) -- 返回助手列表中id在指定id之前的助手,默认为空字符串。 * **返回:** 助手列表响应体。 * **返回类型:** @@ -71,8 +71,8 @@ 指定file_id和assistant_id,挂载File到对应的Assistant * **参数:** - * **assistant_id** (*Optional* *[**str* *]*) – 助理ID。 - * **file_id** (*Optional* *[**str* *]*) – 文件ID。 + * **assistant_id** (*Optional* *[**str* *]*) -- 助理ID。 + * **file_id** (*Optional* *[**str* *]*) -- 文件ID。 * **返回:** 助理文件列表响应对象。 * **返回类型:** @@ -83,11 +83,11 @@ 查询Assistant挂载的File列表 * **参数:** - * **assistant_id** (*Optional* *[**str* *]*) – 助手ID,为空时获取当前登录用户的助手文件列表。 - * **limit** (*Optional* *[**int* *]* *,* *optional*) – 每页最多显示多少个文件。默认为20。 - * **order** (*Optional* *[**AssistantListRole* *]* *,* *optional*) – 文件列表排序方式。可选值为 ‘asc’ 或 ‘desc’。默认为 ‘desc’。 - * **after** (*Optional* *[**str* *]* *,* *optional*) – 返回文件ID大于该值的文件列表。默认为空字符串。 - * **before** (*Optional* *[**str* *]* *,* *optional*) – 返回文件ID小于该值的文件列表。默认为空字符串。 + * **assistant_id** (*Optional* *[**str* *]*) -- 助手ID,为空时获取当前登录用户的助手文件列表。 + * **limit** (*Optional* *[**int* *]* *,* *optional*) -- 每页最多显示多少个文件。默认为20。 + * **order** (*Optional* *[**AssistantListRole* *]* *,* *optional*) -- 文件列表排序方式。可选值为 'asc' 或 'desc'。默认为 'desc'。 + * **after** (*Optional* *[**str* *]* *,* *optional*) -- 返回文件ID大于该值的文件列表。默认为空字符串。 + * **before** (*Optional* *[**str* *]* *,* *optional*) -- 返回文件ID小于该值的文件列表。默认为空字符串。 * **返回:** 包含文件列表信息的响应对象。 * **返回类型:** @@ -98,21 +98,21 @@ 根据assistant_id查询Assistant信息 * **参数:** - **assistant_id** (*Optional* *[**str* *]*) – 助手ID + **assistant_id** (*Optional* *[**str* *]*) -- 助手ID * **返回:** 助手查询响应结果 * **返回类型:** assistant_type.AssistantQueryResponse * **抛出:** - **HTTPError** – 请求失败,抛出HTTPError异常 + **HTTPError** -- 请求失败,抛出HTTPError异常 #### unmount_files(assistant_id: str | None, file_id: str | None) → AssistantFilesDeleteResponse 指定assistant_id和file_id,解绑Assistant中对应File的关联 * **参数:** - * **assistant_id** (*Optional* *[**str* *]*) – 助理ID。 - * **file_id** (*Optional* *[**str* *]*) – 文件ID。 + * **assistant_id** (*Optional* *[**str* *]*) -- 助理ID。 + * **file_id** (*Optional* *[**str* *]*) -- 文件ID。 * **返回:** 响应对象。 * **返回类型:** @@ -123,17 +123,17 @@ 根据assistant_id修改一个已创建的Assistant * **参数:** - * **assistant_id** (*str*) – 助手ID。 - * **model** (*Optional* *[**str* *]*) – 助手模型。 - * **name** (*Optional* *[**str* *]*) – 助手名称。 - * **description** (*Optional* *[**str* *]*) – 助手描述。 - * **response_format** (*Optional* *[**str* *]* *,* *optional*) – 响应格式。默认为None。 - * **instructions** (*Optional* *[**str* *]* *,* *optional*) – 助手指令。默认为None。 - * **thought_instructions** (*Optional* *[**str* *]* *,* *optional*) – 思考指令。默认为None。 - * **chat_instructions** (*Optional* *[**str* *]* *,* *optional*) – 聊天指令。默认为None。 - * **tools** (*Optional* *[**list* *[**assistant_type.AssistantTool* *]* *]* *,* *optional*) – 助手工具列表。默认为空列表。 - * **file_ids** (*Optional* *[**list* *[**str* *]* *]* *,* *optional*) – 文件ID列表。默认为空列表。 - * **metadata** (*Optional* *[**dict* *]* *,* *optional*) – 助手元数据。默认为空字典。 + * **assistant_id** (*str*) -- 助手ID。 + * **model** (*Optional* *[**str* *]*) -- 助手模型。 + * **name** (*Optional* *[**str* *]*) -- 助手名称。 + * **description** (*Optional* *[**str* *]*) -- 助手描述。 + * **response_format** (*Optional* *[**str* *]* *,* *optional*) -- 响应格式。默认为None。 + * **instructions** (*Optional* *[**str* *]* *,* *optional*) -- 助手指令。默认为None。 + * **thought_instructions** (*Optional* *[**str* *]* *,* *optional*) -- 思考指令。默认为None。 + * **chat_instructions** (*Optional* *[**str* *]* *,* *optional*) -- 聊天指令。默认为None。 + * **tools** (*Optional* *[**list* *[**assistant_type.AssistantTool* *]* *]* *,* *optional*) -- 助手工具列表。默认为空列表。 + * **file_ids** (*Optional* *[**list* *[**str* *]* *]* *,* *optional*) -- 文件ID列表。默认为空列表。 + * **metadata** (*Optional* *[**dict* *]* *,* *optional*) -- 助手元数据。默认为空字典。 * **返回:** 助手更新响应。 * **返回类型:** @@ -150,61 +150,61 @@ 获取指定文件的内容 * **参数:** - * **file_id** (*str*) – 文件ID - * **timeout** (*Optional* *[**int* *]* *,* *optional*) – 请求超时时间,单位秒. Defaults to None. + * **file_id** (*str*) -- 文件ID + * **timeout** (*Optional* *[**int* *]* *,* *optional*) -- 请求超时时间,单位秒. Defaults to None. * **返回:** 包含文件内容的响应对象 * **返回类型:** assistant_type.AssistantFilesContentResponse * **抛出:** - * **TypeError** – 当file_id不是字符串类型时引发此异常 - * **FileNotFoundError** – 当指定的文件路径不存在时引发此异常 - * **HTTPConnectionException** – 当请求失败时引发此异常 + * **TypeError** -- 当file_id不是字符串类型时引发此异常 + * **FileNotFoundError** -- 当指定的文件路径不存在时引发此异常 + * **HTTPConnectionException** -- 当请求失败时引发此异常 #### create(file_path: str, purpose: str = 'assistant') → AssistantFilesCreateResponse 上传文件到助理存储中。 * **参数:** - * **file_path** (*str*) – 要上传的文件路径。 - * **purpose** (*str* *,* *optional*) – 上传文件的用途。默认为 “assistant”。 + * **file_path** (*str*) -- 要上传的文件路径。 + * **purpose** (*str* *,* *optional*) -- 上传文件的用途。默认为 "assistant"。 * **返回:** 上传文件后返回的响应对象。 * **返回类型:** assistant_type.AssistantFilesCreateResponse * **抛出:** - **ValueError** – 如果指定的文件路径不存在,则会引发此异常。 + **ValueError** -- 如果指定的文件路径不存在,则会引发此异常。 #### delete(file_id: str) → AssistantFilesDeleteResponse 删除文件 * **参数:** - **file_id** (*str*) – 文件ID + **file_id** (*str*) -- 文件ID * **返回:** 删除文件后的响应对象。 * **返回类型:** assistant_type.AssistantFilesDeleteResponse * **抛出:** - **无** – + **无** -- #### download(file_id: str, file_path: str = '', timeout: int | None = None) 下载文件 * **参数:** - * **file_id** (*str*) – 文件ID - * **file_path** (*str* *,* *optional*) – 文件保存路径,默认为空字符串。如果未指定,则使用文件名的默认值。要求若文件路径不为空,需要以/结尾。 - * **timeout** (*Optional* *[**int* *]* *,* *optional*) – 请求超时时间,单位秒。如果未指定,则使用默认超时时间。 + * **file_id** (*str*) -- 文件ID + * **file_path** (*str* *,* *optional*) -- 文件保存路径,默认为空字符串。如果未指定,则使用文件名的默认值。要求若文件路径不为空,需要以/结尾。 + * **timeout** (*Optional* *[**int* *]* *,* *optional*) -- 请求超时时间,单位秒。如果未指定,则使用默认超时时间。 * **返回:** None * **抛出:** - * **TypeError** – 当file_path或file_id类型不为str时引发此异常。 - * **ValueError** – 当file_id为空或None时,或file_path不是文件目录时引发此异常。 - * **FileNotFoundError** – 当指定的文件路径或文件不存在时引发此异常。 - * **OSError** – 当磁盘空间不足时引发此异常。 - * **HTTPConnectionException** – 当请求失败时引发此异常。 - * **Exception** – 当发生其他异常时引发此异常。 + * **TypeError** -- 当file_path或file_id类型不为str时引发此异常。 + * **ValueError** -- 当file_id为空或None时,或file_path不是文件目录时引发此异常。 + * **FileNotFoundError** -- 当指定的文件路径或文件不存在时引发此异常。 + * **OSError** -- 当磁盘空间不足时引发此异常。 + * **HTTPConnectionException** -- 当请求失败时引发此异常。 + * **Exception** -- 当发生其他异常时引发此异常。 #### list() → AssistantFilesListResponse @@ -217,7 +217,7 @@ **无** * **返回:** 文件列表的响应对象,包含以下属性: - - object (str): 表示对象类型,默认值为 “list” + - object (str): 表示对象类型,默认值为 "list" - data (list[AssistantFilesListData]): 包含文件信息的列表,列表中的每个元素为 AssistantFilesListData 对象。该对象包含以下属性: > - id (str): 文件ID > - bytes (int): 文件大小(字节) @@ -231,18 +231,18 @@ * **返回类型:** assistant_type.AssistantFilesListResponse * **抛出:** - **assistant_type.AssistantError** – 请求发生错误时抛出,具体错误信息可通过 error_msg 属性获取。 + **assistant_type.AssistantError** -- 请求发生错误时抛出,具体错误信息可通过 error_msg 属性获取。 #### query(file_id: str) → AssistantFilesQueryResponse 根据文件ID查询文件信息 * **参数:** - **file_id** (*str*) – 文件ID + **file_id** (*str*) -- 文件ID * **返回:** 文件查询响应对象 * **返回类型:** assistant_type.AssistantFilesQueryResponse * **抛出:** - * **TypeError** – 如果file_id不是str类型 - * **ValueError** – 如果file_id不存在 + * **TypeError** -- 如果file_id不是str类型 + * **ValueError** -- 如果file_id不存在 diff --git a/docs/sphinx_md/appbuilder.core.assistant.md b/docs/API-Reference/Python/appbuilder.core.assistant.md similarity index 100% rename from docs/sphinx_md/appbuilder.core.assistant.md rename to docs/API-Reference/Python/appbuilder.core.assistant.md diff --git a/docs/sphinx_md/appbuilder.core.assistant.threads.md b/docs/API-Reference/Python/appbuilder.core.assistant.threads.md similarity index 93% rename from docs/sphinx_md/appbuilder.core.assistant.threads.md rename to docs/API-Reference/Python/appbuilder.core.assistant.threads.md index beecf5fa4..7b626032e 100644 --- a/docs/sphinx_md/appbuilder.core.assistant.threads.md +++ b/docs/API-Reference/Python/appbuilder.core.assistant.threads.md @@ -74,11 +74,11 @@ 创建一个新的对话线程。 * **参数:** - **messages** – 要发送给助手的消息列表。如果不传入此参数,则会创建一个空对话线程。 + **messages** -- 要发送给助手的消息列表。如果不传入此参数,则会创建一个空对话线程。 * **返回:** 一个ThreadCreateResponse对象,包含新创建的线程的相关信息。 * **抛出:** - **ValueError** – 如果传入的messages参数不是列表类型。 + **ValueError** -- 如果传入的messages参数不是列表类型。 #### delete(thread_id: str) → ThreadDeleteResponse @@ -88,7 +88,7 @@ * **返回:** 一个ThreadDeleteResponse对象,包含对话线程的相关信息。 * **抛出:** - **ValueError** – 如果传入的thread_id参数不是字符串类型。 + **ValueError** -- 如果传入的thread_id参数不是字符串类型。 #### *property* messages *: [Messages](appbuilder.core.assistant.threads.messages.md#appbuilder.core.assistant.threads.messages.messages.Messages)* @@ -106,11 +106,11 @@ 查询对话线程信息。 * **参数:** - **thread_id** – 要查询的对话线程ID。 + **thread_id** -- 要查询的对话线程ID。 * **返回:** 一个ThreadQueryResponse对象,包含对话线程的相关信息。 * **抛出:** - **ValueError** – 如果传入的thread_id参数不是字符串类型。 + **ValueError** -- 如果传入的thread_id参数不是字符串类型。 #### *property* runs *: [Runs](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.runs.Runs)* @@ -128,12 +128,12 @@ 更新线程信息 * **参数:** - * **thread_id** (*str*) – 线程ID - * **metadata** (*Optional* *[**dict* *]* *,* *optional*) – 线程元数据. 默认为空字典. + * **thread_id** (*str*) -- 线程ID + * **metadata** (*Optional* *[**dict* *]* *,* *optional*) -- 线程元数据. 默认为空字典. * **返回:** 线程更新响应 * **返回类型:** thread_type.ThreadUpdateResponse * **抛出:** - * **TypeError** – 如果metadata不是字典类型 - * **ValueError** – 如果metadata的键超过64个字符或值超过512个字符 + * **TypeError** -- 如果metadata不是字典类型 + * **ValueError** -- 如果metadata的键超过64个字符或值超过512个字符 diff --git a/docs/API-Reference/Python/appbuilder.core.assistant.threads.messages.md b/docs/API-Reference/Python/appbuilder.core.assistant.threads.messages.md new file mode 100644 index 000000000..267910805 --- /dev/null +++ b/docs/API-Reference/Python/appbuilder.core.assistant.threads.messages.md @@ -0,0 +1,88 @@ +# appbuilder.core.assistant.threads.messages package + +## Submodules + +## appbuilder.core.assistant.threads.messages.messages module + +### *class* appbuilder.core.assistant.threads.messages.messages.Messages + +基类:`object` + +#### create(thread_id: str, content: str, role: str | None = 'user', file_ids: list[str] | None = []) → AssistantMessageCreateResponse + +创建一条消息。 + +* **参数:** + * **thread_id** (*str*) -- 线程ID。 + * **content** (*str*) -- 消息内容。 + * **role** (*Optional* *[**str* *]* *,* *optional*) -- 角色,可选值为"user"或"assistant"。默认为"user"。 + * **file_ids** (*Optional* *[**list* *[**str* *]* *]* *,* *optional*) -- 消息中包含的文件ID列表。默认为空列表。 +* **返回:** + 消息创建响应对象。 +* **返回类型:** + thread_type.AssistantMessageCreateResponse +* **抛出:** + **HttpError** -- 如果请求失败,则抛出HttpError异常。 + +#### files(thread_id: str, message_id: str, limit: int | None = 20, order: str | None = 'desc', after: str | None = '', before: str | None = '') → AssistantMessageFilesResponse + +获取指定消息 ID 的附件信息。 + +* **参数:** + * **thread_id** (*str*) -- 线程 ID。 + * **messsages_id** (*str*) -- 消息 ID。 + * **limit** (*Optional* *[**int* *]* *,* *optional*) -- 返回结果的最大数量,默认为 20。 + * **order** (*Optional* *[**str* *]* *,* *optional*) -- 排序方式,可选值为 "asc" 或 "desc",默认为 "desc"。 + * **after** (*Optional* *[**str* *]* *,* *optional*) -- 返回结果的时间范围,只返回时间晚于该时间戳的消息附件,默认为空。 + * **before** (*Optional* *[**str* *]* *,* *optional*) -- 返回结果的时间范围,只返回时间早于该时间戳的消息附件,默认为空。 +* **返回:** + 附件信息响应对象。 +* **返回类型:** + thread_type.AssistantMessageFilesResponse + +#### list(thread_id: str, limit: int = 20, order: str = 'desc', after: str = '', before: str = '') → AssistantMessageListResponse + +查询指定Thread下的Message列表 + +* **参数:** + * **thread_id** (*str*) -- 线程ID。 + * **limit** (*int* *,* *optional*) -- 返回消息的最大数量,取值范围为[1,20]。默认为-20。 + * **order** (*Optional* *[**str* *]* *,* *optional*) -- 排序方式,可选值为"asc"或"desc"。默认为"desc"。 + * **after** (*Optional* *[**str* *]* *,* *optional*) -- 查询指定message_id之后创建的Message。 + * **before** (*Optional* *[**str* *]* *,* *optional*) -- 查询指定message_id之前创建的Message +* **返回:** + 查询thread下的message列表响应对象。 +* **返回类型:** + thread_type.AssistantMessageListResponse +* **抛出:** + **HttpError** -- 如果请求失败,则抛出HttpError异常。 + +#### query(thread_id: str, message_id: str) → AssistantMessageQueryResponse + +根据message_id查询指定Message的信息 + +* **参数:** + * **thread_id** (*str*) -- 线程ID + * **message_id** (*str*) -- 消息ID +* **返回:** + 消息信息响应 +* **返回类型:** + thread_type.AssistantMessageQueryResponse +* **抛出:** + **HttpError** -- 如果请求失败,则抛出HttpError异常。 + +#### update(thread_id: str, message_id: str, content: str | None, file_ids: list[str] | None = []) → AssistantMessageUpdateResponse + +修改Message对象,允许content和file_ids字段 + +* **参数:** + * **thread_id** (*str*) -- 线程ID。 + * **message_id** (*str*) -- 消息ID。 + * **content** (*Optional* *[**str* *]* *,* *optional*) -- 消息内容。默认为空字符串。 + * **file_ids** (*Optional* *[**list* *[**str* *]* *]* *,* *optional*) -- 消息中包含的文件ID列表。默认为空列表。 +* **返回:** + 消息更新响应对象。 +* **返回类型:** + thread_type.AssistantMessageUpdateResponse +* **抛出:** + **HttpError** -- 如果请求失败,则抛出HttpError异常。 diff --git a/docs/sphinx_md/appbuilder.core.assistant.threads.runs.md b/docs/API-Reference/Python/appbuilder.core.assistant.threads.runs.md similarity index 70% rename from docs/sphinx_md/appbuilder.core.assistant.threads.runs.md rename to docs/API-Reference/Python/appbuilder.core.assistant.threads.runs.md index 1bb5428bd..8133643b5 100644 --- a/docs/sphinx_md/appbuilder.core.assistant.threads.runs.md +++ b/docs/API-Reference/Python/appbuilder.core.assistant.threads.runs.md @@ -13,8 +13,8 @@ 取消指定线程的运行 * **参数:** - * **run_id** (*str*) – 运行的ID - * **thread_id** (*str*) – 线程的ID + * **run_id** (*str*) -- 运行的ID + * **thread_id** (*str*) -- 线程的ID * **返回:** 取消运行的结果 * **返回类型:** @@ -25,25 +25,25 @@ 列出对应thread的历史run记录 * **参数:** - * **thread_id** (*str*) – 线程ID - * **limit** (*int* *,* *optional*) – 列表数量限制,默认为20 - * **order** (*str* *,* *optional*) – 排序方式,’asc’为升序,’desc’为降序,默认为’desc’ - * **after** (*str* *,* *optional*) – 返回在指定时间之后的运行列表,默认为空字符串 - * **before** (*str* *,* *optional*) – 返回在指定时间之前的运行列表,默认为空字符串 + * **thread_id** (*str*) -- 线程ID + * **limit** (*int* *,* *optional*) -- 列表数量限制,默认为20 + * **order** (*str* *,* *optional*) -- 排序方式,'asc'为升序,'desc'为降序,默认为'desc' + * **after** (*str* *,* *optional*) -- 返回在指定时间之后的运行列表,默认为空字符串 + * **before** (*str* *,* *optional*) -- 返回在指定时间之前的运行列表,默认为空字符串 * **返回:** 列出对应thread的历史run记录 * **返回类型:** thread_type.RunListResponse * **抛出:** - **无** – + **无** -- #### query(thread_id: str, run_id: str) → RunResult 根据thread_id和run_id,查询run的详情 * **参数:** - * **thread_id** (*str*) – 线程ID。 - * **run_id** (*str*) – 运行ID。 + * **thread_id** (*str*) -- 线程ID。 + * **run_id** (*str*) -- 运行ID。 * **返回:** 查询到的运行结果。 * **返回类型:** @@ -52,26 +52,26 @@ #### run(assistant_id: str, thread_id: str | None = '', thread: AssistantThread | None = None, model: str | None = None, response_format: str | None = 'text', instructions: str | None = '', thought_instructions: str | None = '', chat_instructions: str | None = '', tools: list[AssistantTool] | None = [], metadata: dict | None = {}, tool_output: ToolOutput | None = None, model_parameters: AssistantModelParameters | None = None, user_info: AssistantUserInfo | None = None, user_loc: AssistantUserLoc | None = None) → RunResult * **参数:** - * **assistant_id** (*str*) – 助手id - * **thread_id** (*Optional* *[**str* *]* *,* *optional*) – 对话id. Defaults to “”. - * **thread** (*Optional* *[**thread_type.AssistantThread* *]* *,* *optional*) – 对话信息. Defaults to None. - * **model** (*Optional* *[**str* *]* *,* *optional*) – 模型名称. Defaults to None. - * **response_format** (*Optional* *[**str* *]* *,* *optional*) – 返回格式. Defaults to “text”. - * **instructions** (*Optional* *[**str* *]* *,* *optional*) – 指令信息. Defaults to “”. - * **thought_instructions** (*Optional* *[**str* *]* *,* *optional*) – 思考指令信息. Defaults to “”. - * **chat_instructions** (*Optional* *[**str* *]* *,* *optional*) – 闲聊指令信息. Defaults to “”. - * **tools** (*Optional* *[**list* *[**assistant_type.AssistantTool* *]* *]* *,* *optional*) – 工具列表. Defaults to []. - * **metadata** (*Optional* *[**dict* *]* *,* *optional*) – 元数据. Defaults to {}. - * **tool_output** (*Optional* *[**thread_type.ToolOutput* *]* *,* *optional*) – 工具输出. Defaults to None. - * **model_parameters** (*Optional* *[**public_type.AssistantModelParameters* *]* *,* *optional*) – 模型运行参数. Defaults to None. - * **user_info** (*Optional* *[**public_type.AssistantUserInfo* *]* *,* *optional*) – 用户身份信息. Defaults to None. - * **user_loc** (*Optional* *[**public_type.AssistantUserLoc* *]* *,* *optional*) – 用户定位信息. Defaults to None. + * **assistant_id** (*str*) -- 助手id + * **thread_id** (*Optional* *[**str* *]* *,* *optional*) -- 对话id. Defaults to "". + * **thread** (*Optional* *[**thread_type.AssistantThread* *]* *,* *optional*) -- 对话信息. Defaults to None. + * **model** (*Optional* *[**str* *]* *,* *optional*) -- 模型名称. Defaults to None. + * **response_format** (*Optional* *[**str* *]* *,* *optional*) -- 返回格式. Defaults to "text". + * **instructions** (*Optional* *[**str* *]* *,* *optional*) -- 指令信息. Defaults to "". + * **thought_instructions** (*Optional* *[**str* *]* *,* *optional*) -- 思考指令信息. Defaults to "". + * **chat_instructions** (*Optional* *[**str* *]* *,* *optional*) -- 闲聊指令信息. Defaults to "". + * **tools** (*Optional* *[**list* *[**assistant_type.AssistantTool* *]* *]* *,* *optional*) -- 工具列表. Defaults to []. + * **metadata** (*Optional* *[**dict* *]* *,* *optional*) -- 元数据. Defaults to {}. + * **tool_output** (*Optional* *[**thread_type.ToolOutput* *]* *,* *optional*) -- 工具输出. Defaults to None. + * **model_parameters** (*Optional* *[**public_type.AssistantModelParameters* *]* *,* *optional*) -- 模型运行参数. Defaults to None. + * **user_info** (*Optional* *[**public_type.AssistantUserInfo* *]* *,* *optional*) -- 用户身份信息. Defaults to None. + * **user_loc** (*Optional* *[**public_type.AssistantUserLoc* *]* *,* *optional*) -- 用户定位信息. Defaults to None. * **返回:** 运行结果 * **返回类型:** thread_type.RunResult * **抛出:** - **ValueError** – thread_id和thread不能同时为空,model_parameters的各个参数不在规定范围内 + **ValueError** -- thread_id和thread不能同时为空,model_parameters的各个参数不在规定范围内 #### NOTE 1. 如果thread_id没有传,则thread必须要传值 @@ -94,24 +94,24 @@ 启动一个流式运行的对话,用于处理对话流中的消息。 * **参数:** - * **assistant_id** (*str*) – 助理ID。 - * **thread_id** (*Optional* *[**str* *]* *,* *optional*) – 线程ID,用于恢复历史对话。默认为空字符串。 - * **thread** (*Optional* *[**thread_type.AssistantThread* *]* *,* *optional*) – 线程对象,用于恢复历史对话。默认为None。 - * **model** (*Optional* *[**str* *]* *,* *optional*) – 使用的模型名称。默认为None。 - * **response_format** (*Optional* *[**str* *]* *,* *optional*) – 响应格式,支持”text”和”json”两种格式。默认为”text”。 - * **instructions** (*Optional* *[**str* *]* *,* *optional*) – 指令文本。默认为空字符串。 - * **thought_instructions** (*Optional* *[**str* *]* *,* *optional*) – 思考指令文本。默认为空字符串。 - * **chat_instructions** (*Optional* *[**str* *]* *,* *optional*) – 聊天指令文本。默认为空字符串。 - * **tools** (*Optional* *[**list* *[**assistant_type.AssistantTool* *]* *]* *,* *optional*) – 使用的工具列表。默认为空列表。 - * **metadata** (*Optional* *[**dict* *]* *,* *optional*) – 元数据字典。默认为空字典。 - * **tool_output** (*Optional* *[**thread_type.ToolOutput* *]* *,* *optional*) – 工具输出对象。默认为None。 - * **model_parameters** (*Optional* *[**public_type.AssistantModelParameters* *]* *,* *optional*) – 模型参数对象。默认为None。 + * **assistant_id** (*str*) -- 助理ID。 + * **thread_id** (*Optional* *[**str* *]* *,* *optional*) -- 线程ID,用于恢复历史对话。默认为空字符串。 + * **thread** (*Optional* *[**thread_type.AssistantThread* *]* *,* *optional*) -- 线程对象,用于恢复历史对话。默认为None。 + * **model** (*Optional* *[**str* *]* *,* *optional*) -- 使用的模型名称。默认为None。 + * **response_format** (*Optional* *[**str* *]* *,* *optional*) -- 响应格式,支持"text"和"json"两种格式。默认为"text"。 + * **instructions** (*Optional* *[**str* *]* *,* *optional*) -- 指令文本。默认为空字符串。 + * **thought_instructions** (*Optional* *[**str* *]* *,* *optional*) -- 思考指令文本。默认为空字符串。 + * **chat_instructions** (*Optional* *[**str* *]* *,* *optional*) -- 聊天指令文本。默认为空字符串。 + * **tools** (*Optional* *[**list* *[**assistant_type.AssistantTool* *]* *]* *,* *optional*) -- 使用的工具列表。默认为空列表。 + * **metadata** (*Optional* *[**dict* *]* *,* *optional*) -- 元数据字典。默认为空字典。 + * **tool_output** (*Optional* *[**thread_type.ToolOutput* *]* *,* *optional*) -- 工具输出对象。默认为None。 + * **model_parameters** (*Optional* *[**public_type.AssistantModelParameters* *]* *,* *optional*) -- 模型参数对象。默认为None。 * **返回:** 返回一个迭代器,每次迭代返回一个处理结果对象,可能是 StreamRunStatus 或 StreamRunMessage。 * **返回类型:** Union[thread_type.StreamRunStatus, thread_type.StreamRunMessage, None] * **抛出:** - **ValueError** – 如果thread_id和thread参数同时为空,则会引发ValueError异常。 + **ValueError** -- 如果thread_id和thread参数同时为空,则会引发ValueError异常。 #### NOTE 1. 如果thread_id没有传,则thread必须要传值。 @@ -123,36 +123,36 @@ 使用带有事件处理器的流运行助手 * **参数:** - * **assistant_id** (*str*) – 助手的唯一标识符 - * **thread_id** (*Optional* *[**str* *]* *,* *optional*) – 会话线程的标识符,默认为空字符串. 默认为 “”. - * **thread** (*Optional* *[**thread_type.AssistantThread* *]* *,* *optional*) – 会话线程对象,默认为None. 默认为 None. - * **model** (*Optional* *[**str* *]* *,* *optional*) – 模型标识符,默认为None. 默认为 None. - * **response_format** (*Optional* *[**str* *]* *,* *optional*) – 响应格式,可选值为”text”或”json”,默认为”text”. 默认为 “text”. - * **instructions** (*Optional* *[**str* *]* *,* *optional*) – 主要指令,默认为空字符串. 默认为 “”. - * **thought_instructions** (*Optional* *[**str* *]* *,* *optional*) – 思维指令,默认为空字符串. 默认为 “”. - * **chat_instructions** (*Optional* *[**str* *]* *,* *optional*) – 聊天指令,默认为空字符串. 默认为 “”. - * **tools** (*Optional* *[**list* *[**assistant_type.AssistantTool* *]* *]* *,* *optional*) – 助手工具列表,默认为空列表. 默认为 []. - * **metadata** (*Optional* *[**dict* *]* *,* *optional*) – 元数据字典,默认为空字典. 默认为 {}. - * **tool_output** (*Optional* *[**thread_type.ToolOutput* *]* *,* *optional*) – 工具输出对象,默认为None. 默认为 None. - * **event_handler** (*Optional* *[*[*AssistantEventHandler*](#appbuilder.core.assistant.threads.runs.stream_helper.AssistantEventHandler) *]* *,* *optional*) – 事件处理器对象,默认为None. 默认为 None. - * **model_parameters** (*Optional* *[**public_type.AssistantModelParameters* *]* *,* *optional*) – 模型参数对象,默认为None. 默认为 None. - * **user_info** (*Optional* *[**public_type.AssistantUserInfo* *]* *,* *optional*) – 用户信息对象,默认为None. 默认为 None. - * **user_loc** (*Optional* *[**public_type.AssistantUserLoc* *]* *,* *optional*) – 用户位置信息对象,默认为None. 默认为 None. + * **assistant_id** (*str*) -- 助手的唯一标识符 + * **thread_id** (*Optional* *[**str* *]* *,* *optional*) -- 会话线程的标识符,默认为空字符串. 默认为 "". + * **thread** (*Optional* *[**thread_type.AssistantThread* *]* *,* *optional*) -- 会话线程对象,默认为None. 默认为 None. + * **model** (*Optional* *[**str* *]* *,* *optional*) -- 模型标识符,默认为None. 默认为 None. + * **response_format** (*Optional* *[**str* *]* *,* *optional*) -- 响应格式,可选值为"text"或"json",默认为"text". 默认为 "text". + * **instructions** (*Optional* *[**str* *]* *,* *optional*) -- 主要指令,默认为空字符串. 默认为 "". + * **thought_instructions** (*Optional* *[**str* *]* *,* *optional*) -- 思维指令,默认为空字符串. 默认为 "". + * **chat_instructions** (*Optional* *[**str* *]* *,* *optional*) -- 聊天指令,默认为空字符串. 默认为 "". + * **tools** (*Optional* *[**list* *[**assistant_type.AssistantTool* *]* *]* *,* *optional*) -- 助手工具列表,默认为空列表. 默认为 []. + * **metadata** (*Optional* *[**dict* *]* *,* *optional*) -- 元数据字典,默认为空字典. 默认为 {}. + * **tool_output** (*Optional* *[**thread_type.ToolOutput* *]* *,* *optional*) -- 工具输出对象,默认为None. 默认为 None. + * **event_handler** (*Optional* *[*[*AssistantEventHandler*](#appbuilder.core.assistant.threads.runs.stream_helper.AssistantEventHandler) *]* *,* *optional*) -- 事件处理器对象,默认为None. 默认为 None. + * **model_parameters** (*Optional* *[**public_type.AssistantModelParameters* *]* *,* *optional*) -- 模型参数对象,默认为None. 默认为 None. + * **user_info** (*Optional* *[**public_type.AssistantUserInfo* *]* *,* *optional*) -- 用户信息对象,默认为None. 默认为 None. + * **user_loc** (*Optional* *[**public_type.AssistantUserLoc* *]* *,* *optional*) -- 用户位置信息对象,默认为None. 默认为 None. * **返回:** 返回的流管理器对象 * **返回类型:** [AssistantStreamManager](#appbuilder.core.assistant.threads.runs.stream_helper.AssistantStreamManager) * **抛出:** - **HTTPError** – 如果HTTP响应状态码不为200,则抛出HTTPError异常 + **HTTPError** -- 如果HTTP响应状态码不为200,则抛出HTTPError异常 #### submit_tool_outputs(run_id: str, thread_id: str, tool_outputs: list[ToolOutput] | None) → RunResult 向服务端提交工具输出 * **参数:** - * **run_id** (*str*) – 运行ID - * **thread_id** (*str*) – 线程ID - * **tool_outputs** (*Optional* *[**list* *[**thread_type.ToolOutput* *]* *]*) – 工具输出列表,可选 + * **run_id** (*str*) -- 运行ID + * **thread_id** (*str*) -- 线程ID + * **tool_outputs** (*Optional* *[**list* *[**thread_type.ToolOutput* *]* *]*) -- 工具输出列表,可选 * **返回:** 运行结果 * **返回类型:** @@ -169,12 +169,12 @@ 根据thread_id和run_id,列出对应run的历史step记录 * **参数:** - * **thread_id** (*str*) – 线程ID - * **run_id** (*str*) – 运行ID - * **limit** (*int* *,* *optional*) – 步骤数量限制,默认为20 - * **order** (*str* *,* *optional*) – 排序方式,’asc’表示升序,’desc’表示降序,默认为’desc’ - * **after** (*str* *,* *optional*) – 过滤出时间戳晚于此值的步骤,默认为空 - * **before** (*str* *,* *optional*) – 过滤出时间戳早于此值的步骤,默认为空 + * **thread_id** (*str*) -- 线程ID + * **run_id** (*str*) -- 运行ID + * **limit** (*int* *,* *optional*) -- 步骤数量限制,默认为20 + * **order** (*str* *,* *optional*) -- 排序方式,'asc'表示升序,'desc'表示降序,默认为'desc' + * **after** (*str* *,* *optional*) -- 过滤出时间戳晚于此值的步骤,默认为空 + * **before** (*str* *,* *optional*) -- 过滤出时间戳早于此值的步骤,默认为空 * **返回:** 线程运行步骤列表的响应对象 * **返回类型:** @@ -185,9 +185,9 @@ 根据thread_id,run_id和step_id,查询对应step的信息 * **参数:** - * **thread_id** (*str*) – 线程ID - * **run_id** (*str*) – 运行ID - * **step_id** (*str*) – 步骤ID + * **thread_id** (*str*) -- 线程ID + * **run_id** (*str*) -- 运行ID + * **step_id** (*str*) -- 步骤ID * **返回:** 步骤运行结果 * **返回类型:** @@ -219,7 +219,7 @@ Assistant事件处理程序通常与具体的Assistant实例相关联,用于 当触发 message_creation 事件时回调此函数。 * **参数:** - **status_event** (*thread_type.StreamRunStatus*) – 消息创建状态事件对象 + **status_event** (*thread_type.StreamRunStatus*) -- 消息创建状态事件对象 * **返回:** None @@ -231,7 +231,7 @@ Assistant事件处理程序通常与具体的Assistant实例相关联,用于 当触发 messages 打印事件时回调此函数。 * **参数:** - **messages_event** (*thread_type.StreamRunMessage*) – 包含消息内容的事件对象 + **messages_event** (*thread_type.StreamRunMessage*) -- 包含消息内容的事件对象 * **返回:** None @@ -243,7 +243,7 @@ Assistant事件处理程序通常与具体的Assistant实例相关联,用于 当触发 run_begin 事件时回调此函数。 * **参数:** - **status_event** (*thread_type.StreamRunStatus*) – 运行开始状态事件对象 + **status_event** (*thread_type.StreamRunStatus*) -- 运行开始状态事件对象 * **返回:** None @@ -255,7 +255,7 @@ Assistant事件处理程序通常与具体的Assistant实例相关联,用于 当触发 run_cancelling 事件时回调此函数。 * **参数:** - **status_event** (*thread_type.StreamRunStatus*) – 运行取消状态事件对象 + **status_event** (*thread_type.StreamRunStatus*) -- 运行取消状态事件对象 * **返回:** None @@ -267,7 +267,7 @@ Assistant事件处理程序通常与具体的Assistant实例相关联,用于 当触发 run_end 事件时回调此函数。 * **参数:** - **status_event** (*thread_type.StreamRunStatus*) – 运行结束状态事件对象 + **status_event** (*thread_type.StreamRunStatus*) -- 运行结束状态事件对象 * **返回:** None @@ -279,7 +279,7 @@ Assistant事件处理程序通常与具体的Assistant实例相关联,用于 当触发 tool_calls 事件时回调此函数。 * **参数:** - **status_event** (*thread_type.StreamRunStatus*) – 工具调用状态事件对象 + **status_event** (*thread_type.StreamRunStatus*) -- 工具调用状态事件对象 * **返回:** None @@ -291,7 +291,7 @@ Assistant事件处理程序通常与具体的Assistant实例相关联,用于 当触发 tool_step_begin 事件时回调此函数。 * **参数:** - **status_event** (*thread_type.StreamRunStatus*) – 工具步骤开始状态事件对象 + **status_event** (*thread_type.StreamRunStatus*) -- 工具步骤开始状态事件对象 * **返回:** None @@ -303,7 +303,7 @@ Assistant事件处理程序通常与具体的Assistant实例相关联,用于 当触发 tool_step_end 事件时回调此函数。 * **参数:** - **status_event** (*thread_type.StreamRunStatus*) – 工具步骤结束状态事件对象 + **status_event** (*thread_type.StreamRunStatus*) -- 工具步骤结束状态事件对象 * **返回:** None @@ -315,7 +315,7 @@ Assistant事件处理程序通常与具体的Assistant实例相关联,用于 当触发 tool_submitted_output 事件时回调此函数。 * **参数:** - **status_event** (*thread_type.StreamRunStatus*) – 工具提交输出状态事件对象 + **status_event** (*thread_type.StreamRunStatus*) -- 工具提交输出状态事件对象 * **返回:** None @@ -331,7 +331,7 @@ Assistant事件处理程序通常与具体的Assistant实例相关联,用于 * **返回:** 无 * **抛出:** - **无** – + **无** -- ### *class* appbuilder.core.assistant.threads.runs.stream_helper.AssistantStreamManager(response, event_handler: [AssistantEventHandler](#appbuilder.core.assistant.threads.runs.stream_helper.AssistantEventHandler)) @@ -454,11 +454,11 @@ StreamRunContext类用于管理和维护流式运行时的上下文信息。 设置当前助手ID。 * **参数:** - **assistant_id** (*str*) – 需要设置的助手ID。 + **assistant_id** (*str*) -- 需要设置的助手ID。 * **返回:** None * **抛出:** - **无** – + **无** -- #### NOTE 如果输入的assistant_id是有效的字符串且长度大于0,则将其设置为当前助手ID;否则,将当前助手ID设置为None。 @@ -468,22 +468,22 @@ StreamRunContext类用于管理和维护流式运行时的上下文信息。 设置当前事件 * **参数:** - **event** (*thread_type.StreamRunStatus* *or* *thread_type.StreamRunMessage*) – 需要设置的事件对象 + **event** (*thread_type.StreamRunStatus* *or* *thread_type.StreamRunMessage*) -- 需要设置的事件对象 * **返回:** None * **抛出:** - **无** – + **无** -- #### set_current_run_id(run_id) 设置当前运行ID。 * **参数:** - **run_id** (*str*) – 运行ID字符串。 + **run_id** (*str*) -- 运行ID字符串。 * **返回:** None * **抛出:** - **无** – + **无** -- 注意事项: : 如果传入的run_id不是字符串类型或长度为0,则不设置当前运行ID,将其设置为None。 @@ -493,11 +493,11 @@ StreamRunContext类用于管理和维护流式运行时的上下文信息。 设置当前运行步骤的ID。 * **参数:** - **run_step_id** (*str*) – 需要设置的运行步骤ID。 + **run_step_id** (*str*) -- 需要设置的运行步骤ID。 * **返回:** None * **抛出:** - **无** – + **无** -- #### NOTE 如果传入的run_step_id是一个非空字符串,则将其设置为当前运行步骤的ID; @@ -508,11 +508,11 @@ StreamRunContext类用于管理和维护流式运行时的上下文信息。 设置当前线程的ID。 * **参数:** - **thread_id** (*str*) – 要设置的线程ID。 + **thread_id** (*str*) -- 要设置的线程ID。 * **返回:** None * **抛出:** - **无** – + **无** -- #### NOTE 如果thread_id不是字符串类型或者长度为0,则不会设置当前线程的ID,并将其设置为None。 @@ -522,8 +522,8 @@ StreamRunContext类用于管理和维护流式运行时的上下文信息。 设置当前工具调用列表。 * **参数:** - **tool_calls** (*list* *of* *thread_type.ToolCall*) – 工具调用列表。 + **tool_calls** (*list* *of* *thread_type.ToolCall*) -- 工具调用列表。 * **返回:** None * **抛出:** - **AssertionError** – 如果 tool_calls 不是 ToolCall 对象的列表。 + **AssertionError** -- 如果 tool_calls 不是 ToolCall 对象的列表。 diff --git a/docs/sphinx_md/appbuilder.core.components.animal_recognize.md b/docs/API-Reference/Python/appbuilder.core.components.animal_recognize.md similarity index 81% rename from docs/sphinx_md/appbuilder.core.components.animal_recognize.md rename to docs/API-Reference/Python/appbuilder.core.components.animal_recognize.md index 1d4ec718d..12cd23ef1 100644 --- a/docs/sphinx_md/appbuilder.core.components.animal_recognize.md +++ b/docs/API-Reference/Python/appbuilder.core.components.animal_recognize.md @@ -34,9 +34,9 @@ print(out.content) 根据输入消息运行动物识别功能 * **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) – 输入的消息对象,其中应包含需要识别的图像数据或URL - * **timeout** (*float* *,* *optional*) – 超时时间,单位为秒。默认为None,表示无超时限制。Defaults to None. - * **retry** (*int* *,* *optional*) – 重试次数。默认为0,表示不重试。Defaults to 0. + * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入的消息对象,其中应包含需要识别的图像数据或URL + * **timeout** (*float* *,* *optional*) -- 超时时间,单位为秒。默认为None,表示无超时限制。Defaults to None. + * **retry** (*int* *,* *optional*) -- 重试次数。默认为0,表示不重试。Defaults to 0. * **返回:** 识别结果的消息对象 * **返回类型:** @@ -47,10 +47,10 @@ print(out.content) 用于工具的执行,通过调用底层接口进行动物识别。 * **参数:** - * **name** (*str*) – 工具名。 - * **streaming** (*bool*) – 是否流式返回。 - * **origin_query** (*str*) – 用户原始query。 - * **\*\*kwargs** – 工具调用的额外关键字参数。 + * **name** (*str*) -- 工具名。 + * **streaming** (*bool*) -- 是否流式返回。 + * **origin_query** (*str*) -- 用户原始query。 + * **\*\*kwargs** -- 工具调用的额外关键字参数。 * **返回:** 动物识别结果,包括识别出的动物类别和相应的置信度信息。 * **返回类型:** diff --git a/docs/sphinx_md/appbuilder.core.components.asr.md b/docs/API-Reference/Python/appbuilder.core.components.asr.md similarity index 70% rename from docs/sphinx_md/appbuilder.core.components.asr.md rename to docs/API-Reference/Python/appbuilder.core.components.asr.md index 50af740f8..29afeb9ec 100644 --- a/docs/sphinx_md/appbuilder.core.components.asr.md +++ b/docs/API-Reference/Python/appbuilder.core.components.asr.md @@ -36,13 +36,13 @@ print(out.content) # eg: {"result": ["北京科技馆。"]} 执行语音识别操作,并返回识别结果。 * **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) – 输入消息对象,包含待识别的音频数据。该参数为必需项,格式如:Message(content={“raw_audio”: b”…”})。 - * **audio_format** (*str* *,* *optional*) – 音频文件格式,支持pcm/wav/amr/m4a,不区分大小写,推荐使用pcm格式。默认为”pcm”。 - * **rate** (*int* *,* *optional*) – 音频采样率,固定为16000。默认为16000。 - * **timeout** (*float* *,* *optional*) – HTTP请求超时时间。默认为None。 - * **retry** (*int* *,* *optional*) – HTTP请求重试次数。默认为0。 + * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入消息对象,包含待识别的音频数据。该参数为必需项,格式如:Message(content={"raw_audio": b"..."})。 + * **audio_format** (*str* *,* *optional*) -- 音频文件格式,支持pcm/wav/amr/m4a,不区分大小写,推荐使用pcm格式。默认为"pcm"。 + * **rate** (*int* *,* *optional*) -- 音频采样率,固定为16000。默认为16000。 + * **timeout** (*float* *,* *optional*) -- HTTP请求超时时间。默认为None。 + * **retry** (*int* *,* *optional*) -- HTTP请求重试次数。默认为0。 * **返回:** - 语音识别结果,格式如:Message(content={“result”: [“识别结果”]})。 + 语音识别结果,格式如:Message(content={"result": ["识别结果"]})。 * **返回类型:** [Message](appbuilder.core.md#appbuilder.core.message.Message) @@ -51,13 +51,13 @@ print(out.content) # eg: {"result": ["北京科技馆。"]} 评估给定文件名或文件URL的语音识别结果。 * **参数:** - * **name** (*str*) – 函数调用名称。 - * **streaming** (*bool*) – 是否以流的方式返回结果。 - * **\*\*kwargs** – 关键字参数,用于指定文件名、文件URL等参数。 + * **name** (*str*) -- 函数调用名称。 + * **streaming** (*bool*) -- 是否以流的方式返回结果。 + * **\*\*kwargs** -- 关键字参数,用于指定文件名、文件URL等参数。 * **返回:** 如果streaming为True,则通过生成器逐个返回包含识别结果的消息对象; 如果streaming为False,则返回包含识别结果的JSON字符串。 * **抛出:** - **InvalidRequestArgumentError** – 如果未设置文件名或文件URL不存在,则抛出此异常。 + **InvalidRequestArgumentError** -- 如果未设置文件名或文件URL不存在,则抛出此异常。 #### version *= 'v1'* diff --git a/docs/sphinx_md/appbuilder.core.components.dish_recognize.md b/docs/API-Reference/Python/appbuilder.core.components.dish_recognize.md similarity index 83% rename from docs/sphinx_md/appbuilder.core.components.dish_recognize.md rename to docs/API-Reference/Python/appbuilder.core.components.dish_recognize.md index 92bc986db..cddc7d7a5 100644 --- a/docs/sphinx_md/appbuilder.core.components.dish_recognize.md +++ b/docs/API-Reference/Python/appbuilder.core.components.dish_recognize.md @@ -33,10 +33,10 @@ with open("xxxx.jpg", "rb") as f: 根据输入图片进行菜品识别。 * **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) – 输入待识别图片,支持传图片二进制流和图片URL。 - * **timeout** (*float* *,* *optional*) – 请求超时时间,默认为 None。 - * **retry** (*int* *,* *optional*) – 重试次数,默认为 0。 + * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入待识别图片,支持传图片二进制流和图片URL。 + * **timeout** (*float* *,* *optional*) -- 请求超时时间,默认为 None。 + * **retry** (*int* *,* *optional*) -- 重试次数,默认为 0。 * **返回:** - 包含菜品识别结果的输出消息。例如,Message(content={‘result’: [{‘name’: ‘剁椒鱼头’, ‘calorie’: ‘127’}]}) + 包含菜品识别结果的输出消息。例如,Message(content={'result': [{'name': '剁椒鱼头', 'calorie': '127'}]}) * **返回类型:** [Message](appbuilder.core.md#appbuilder.core.message.Message) diff --git a/docs/sphinx_md/appbuilder.core.components.doc_crop_enhance.md b/docs/API-Reference/Python/appbuilder.core.components.doc_crop_enhance.md similarity index 70% rename from docs/sphinx_md/appbuilder.core.components.doc_crop_enhance.md rename to docs/API-Reference/Python/appbuilder.core.components.doc_crop_enhance.md index 9852d77bd..2d4b7d126 100644 --- a/docs/sphinx_md/appbuilder.core.components.doc_crop_enhance.md +++ b/docs/API-Reference/Python/appbuilder.core.components.doc_crop_enhance.md @@ -32,18 +32,18 @@ print(out.content) 文档矫正增强 * **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) – 输入图片或图片url下载地址用于执行操作。举例: Message(content={“raw_image”: b”…”, - * **"enhance_type"** – 3})或 Message(content={“url”: “[https://image/download/url](https://image/download/url)”})。 - * **enhance_type** (*int* *,* *可选*) – 选择是否开启图像增强功能,如开启可选择增强效果,可选值如下: + * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入图片或图片url下载地址用于执行操作。举例: Message(content={"raw_image": b"...", + * **"enhance_type"** -- 3})或 Message(content={"url": "[https://image/download/url](https://image/download/url)"})。 + * **enhance_type** (*int* *,* *可选*) -- 选择是否开启图像增强功能,如开启可选择增强效果,可选值如下: - 0:默认值,不开启增强功能 - 1:去阴影 - 2:增强并锐化 - 3:黑白滤镜。 - * **timeout** (*float* *,* *可选*) – HTTP超时时间 - * **retry** (*int* *,* *可选*) – HTTP重试次数 + * **timeout** (*float* *,* *可选*) -- HTTP超时时间 + * **retry** (*int* *,* *可选*) -- HTTP重试次数 * **返回:** - 识别结果。举例: Message(name=msg, content={‘image_processed’: ‘…’, - ‘points’: [{‘x’: 220, ‘y’: 705}, {‘x’: 240, ‘y’: 0}, {‘x’: 885, ‘y’: 2}, {‘x’: 980, ‘y’: 759}]}, + 识别结果。举例: Message(name=msg, content={'image_processed': '...', + 'points': [{'x': 220, 'y': 705}, {'x': 240, 'y': 0}, {'x': 885, 'y': 2}, {'x': 980, 'y': 759}]}, mtype=dict) * **返回类型:** [Message](appbuilder.core.md#appbuilder.core.message.Message) diff --git a/docs/sphinx_md/appbuilder.core.components.doc_format_converter.md b/docs/API-Reference/Python/appbuilder.core.components.doc_format_converter.md similarity index 80% rename from docs/sphinx_md/appbuilder.core.components.doc_format_converter.md rename to docs/API-Reference/Python/appbuilder.core.components.doc_format_converter.md index 820d87730..564426f80 100644 --- a/docs/sphinx_md/appbuilder.core.components.doc_format_converter.md +++ b/docs/API-Reference/Python/appbuilder.core.components.doc_format_converter.md @@ -42,15 +42,15 @@ print(out.content) 将PDF、JPG、PNG、BMP等格式文件转换为Word、Excel格式,并返回转换后的文件URL。 * **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) – 包含待转换文件路径和页码信息的消息对象。 - * **timeout** (*float* *,* *optional*) – 请求超时时间,单位为秒。默认为None,表示不设置超时时间。 - * **retry** (*int* *,* *optional*) – 请求重试次数。默认为0,表示不重试。 + * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 包含待转换文件路径和页码信息的消息对象。 + * **timeout** (*float* *,* *optional*) -- 请求超时时间,单位为秒。默认为None,表示不设置超时时间。 + * **retry** (*int* *,* *optional*) -- 请求重试次数。默认为0,表示不重试。 * **返回:** 包含转换后文件URL的消息对象。 * **返回类型:** [Message](appbuilder.core.md#appbuilder.core.message.Message) * **抛出:** - **AppBuilderServerException** – 文档格式转换服务发生错误时抛出。 + **AppBuilderServerException** -- 文档格式转换服务发生错误时抛出。 #### submitDocFormatConverterTask(request: DocFormatConverterSubmitRequest, timeout: float | None = None, retry: int = 0, request_id: str | None = None) → DocFormatConverterSubmitResponse @@ -65,9 +65,9 @@ print(out.content) 评估工具函数。 * **参数:** - * **streaming** (*bool*) – 是否流式输出。如果为True,则逐个生成文件URL;如果为False,则直接返回结果内容。 - * **origin_query** (*str*) – 原始查询字符串。 - * **\*\*kwargs** – 其他关键字参数,包括但不限于: + * **streaming** (*bool*) -- 是否流式输出。如果为True,则逐个生成文件URL;如果为False,则直接返回结果内容。 + * **origin_query** (*str*) -- 原始查询字符串。 + * **\*\*kwargs** -- 其他关键字参数,包括但不限于: traceid (str): 请求的跟踪ID,用于日志追踪。 file_url (str): 文件的URL地址。如果为空,则从file_urls和file_name中获取。 file_urls (dict): 包含多个文件路径与URL的映射关系的字典。 @@ -76,7 +76,7 @@ print(out.content) * **返回:** 如果streaming为True,则逐个生成包含文件URL的字典;如果streaming为False,则直接返回结果内容。 * **抛出:** - * **InvalidRequestArgumentError** – 如果请求格式错误,如page_num不是整数、file_url为空且无法从file_urls和file_name中获取file_url等。 - * **AppBuilderServerException** – 如果服务执行过程中出现异常。 + * **InvalidRequestArgumentError** -- 如果请求格式错误,如page_num不是整数、file_url为空且无法从file_urls和file_name中获取file_url等。 + * **AppBuilderServerException** -- 如果服务执行过程中出现异常。 #### version *= 'v1'* diff --git a/docs/sphinx_md/appbuilder.core.components.doc_parser.md b/docs/API-Reference/Python/appbuilder.core.components.doc_parser.md similarity index 79% rename from docs/sphinx_md/appbuilder.core.components.doc_parser.md rename to docs/API-Reference/Python/appbuilder.core.components.doc_parser.md index dc0e43787..5e87eadfa 100644 --- a/docs/sphinx_md/appbuilder.core.components.doc_parser.md +++ b/docs/API-Reference/Python/appbuilder.core.components.doc_parser.md @@ -33,7 +33,7 @@ parse_result = parser(msg) 将解析结果的内容转化成ParseResult的结构 * **参数:** - **response** (*Dict*) – 解析后的响应字典,包含文件内容、目录等信息 + **response** (*Dict*) -- 解析后的响应字典,包含文件内容、目录等信息 * **返回:** 转换后的ParseResult结构,包含段落节点树、页面内容和PDF数据 * **返回类型:** @@ -46,22 +46,22 @@ parse_result = parser(msg) 对传入的文件进行解析 * **参数:** - * **input_message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**str* *]*) – 输入为文件的路径 - * **return_raw** (*bool* *,* *optional*) – 是否返回云端服务的原始结果。默认为False。 + * **input_message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**str* *]*) -- 输入为文件的路径 + * **return_raw** (*bool* *,* *optional*) -- 是否返回云端服务的原始结果。默认为False。 * **返回:** 文件的解析结果。 * **返回类型:** [Message](appbuilder.core.md#appbuilder.core.message.Message)[ParseResult] * **抛出:** - * **ValueError** – 如果传入的文件路径不是字符串类型。 - * **AppBuilderServerException** – 如果文件解析过程中出现异常,将抛出该异常。 + * **ValueError** -- 如果传入的文件路径不是字符串类型。 + * **AppBuilderServerException** -- 如果文件解析过程中出现异常,将抛出该异常。 #### set_config(config: ParserConfig) 设置解析配置 * **参数:** - **config** (*ParserConfig*) – 解析配置对象 + **config** (*ParserConfig*) -- 解析配置对象 * **返回:** None diff --git a/docs/sphinx_md/appbuilder.core.components.doc_splitter.md b/docs/API-Reference/Python/appbuilder.core.components.doc_splitter.md similarity index 76% rename from docs/sphinx_md/appbuilder.core.components.doc_splitter.md rename to docs/API-Reference/Python/appbuilder.core.components.doc_splitter.md index 8513b0b79..90037c55b 100644 --- a/docs/sphinx_md/appbuilder.core.components.doc_splitter.md +++ b/docs/API-Reference/Python/appbuilder.core.components.doc_splitter.md @@ -20,10 +20,10 @@ Examples: 不得套取现金。不得用于其他违反国家法律、政策规定的领域,不得用于监管机构禁止银行贷款进入的领域。 切分结果: -: [“贷款资金不得用于从事股本权益性投资,不得用于购买股票、有价证券、期货、理财产品等金融产品。不得用于从事房地产经营, - 不得用于借贷牟取非法收入。不得用于个”, - “不得用于个人或其控制的企业生产经营。不得套取现金。不得用于其他违反国家法律、政策规定的领域, - 不得用于监管机构禁止银行贷款进入的领域。”] +: ["贷款资金不得用于从事股本权益性投资,不得用于购买股票、有价证券、期货、理财产品等金融产品。不得用于从事房地产经营, + 不得用于借贷牟取非法收入。不得用于个", + "不得用于个人或其控制的企业生产经营。不得套取现金。不得用于其他违反国家法律、政策规定的领域, + 不得用于监管机构禁止银行贷款进入的领域。"] #### meta *: [ComponentArguments](appbuilder.core.md#appbuilder.core.component.ComponentArguments)* *= ComponentArguments(name='', tool_desc={'description': 'split data to chunks with max size in doc'})* @@ -34,13 +34,13 @@ Examples: 对输入的解析文档结果,按照最大段落块大小、结尾分隔符等,处理为多个段落结果 * **参数:** - **(****obj** (*message*) – Message): 上游docparser的文档解析结果 + **(****obj** (*message*) -- Message): 上游docparser的文档解析结果 * **返回:** Message: 文档分隔后的段落结果 * **返回类型:** obj * **抛出:** - **ValueError** – 如果 message.content 的类型不是 ParseResult,则抛出 ValueError 异常 + **ValueError** -- 如果 message.content 的类型不是 ParseResult,则抛出 ValueError 异常 Examples: @@ -95,16 +95,16 @@ print(res_paras.content) 运行函数,根据splitter_type将文档分割成多个部分 * **参数:** - **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) – 包含文档内容的消息对象 + **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 包含文档内容的消息对象 * **返回:** 分割后的文档列表 * **返回类型:** list * **抛出:** - * **ValueError** – 如果message.content不是ParseResult类型,抛出异常 - * **ValueError** – 如果splitter_type为空,抛出异常 - * **ValueError** – 如果ParseResult不包含原始值,抛出异常 - * **ValueError** – 如果splitter_type不是split_by_chunk或split_by_title,抛出异常 + * **ValueError** -- 如果message.content不是ParseResult类型,抛出异常 + * **ValueError** -- 如果splitter_type为空,抛出异常 + * **ValueError** -- 如果ParseResult不包含原始值,抛出异常 + * **ValueError** -- 如果splitter_type不是split_by_chunk或split_by_title,抛出异常 ### *class* appbuilder.core.components.doc_splitter.doc_splitter.TitleSplitter(meta: [ComponentArguments](appbuilder.core.md#appbuilder.core.component.ComponentArguments) | None = ComponentArguments(name='', tool_desc={}), secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) @@ -126,11 +126,11 @@ print(res_paras.content) 2、抵押房产如有共同所有人,借款人必须为之一,且其他共同所有人必须同意以该房产办理最高额抵押登记,并提供同意抵押的合法有效的书面文件。 切分结果: -: [“一、简介 叠贷业务是指借款人家庭为满足购房、购车、装修、教育、医疗、旅游、日常消费等符合国家法律法规规定的消费用途。”, - “二、申请条件 (一)基本条件 1、年满18周岁的自然人,具有完全民事行为能力,能提供有效身份证明或居留证明; 2、有稳定职业和收入, - 有偿还贷款本息的能力;”, - “二、申请条件 (二)抵押房产所有人的要求 1、抵押房产的所有人应为借款人本人。 2、抵押房产如有共同所有人,借款人必须为之一, - 且其他共同所有人必须同意以该房产办理最高额抵押登记,并提供同意抵押的合法有效的书面文件。”】 +: ["一、简介 叠贷业务是指借款人家庭为满足购房、购车、装修、教育、医疗、旅游、日常消费等符合国家法律法规规定的消费用途。", + "二、申请条件 (一)基本条件 1、年满18周岁的自然人,具有完全民事行为能力,能提供有效身份证明或居留证明; 2、有稳定职业和收入, + 有偿还贷款本息的能力;", + "二、申请条件 (二)抵押房产所有人的要求 1、抵押房产的所有人应为借款人本人。 2、抵押房产如有共同所有人,借款人必须为之一, + 且其他共同所有人必须同意以该房产办理最高额抵押登记,并提供同意抵押的合法有效的书面文件。"】 #### name *: str* *= 'doc_to_title_level'* @@ -139,13 +139,13 @@ print(res_paras.content) 对输入的解析文档结果,按照各标题层级,处理为多个段落结果 * **参数:** - **(****obj** (*input_message*) – Message): 上游docparser的文档解析结果 + **(****obj** (*input_message*) -- Message): 上游docparser的文档解析结果 * **返回:** Message: 文档分隔后的段落结果 * **返回类型:** obj * **抛出:** - **ValueError** – 如果message.content的类型不是ParseResult,则抛出异常 + **ValueError** -- 如果message.content的类型不是ParseResult,则抛出异常 Examples: diff --git a/docs/API-Reference/Python/appbuilder.core.components.document_understanding.md b/docs/API-Reference/Python/appbuilder.core.components.document_understanding.md new file mode 100644 index 000000000..b8a8c9f8f --- /dev/null +++ b/docs/API-Reference/Python/appbuilder.core.components.document_understanding.md @@ -0,0 +1,59 @@ +# appbuilder.core.components.document_understanding package + +## Submodules + +## appbuilder.core.components.document_understanding.component module + +Copyright (c) 2023 Baidu, Inc. 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](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. + +### *class* appbuilder.core.components.document_understanding.component.DocumentUnderstanding(secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False, instruction: [Message](appbuilder.core.md#appbuilder.core.message.Message) | None = None, addition_instruction: [Message](appbuilder.core.md#appbuilder.core.message.Message) | None = None, file_path: str | None = None, app_id: str | None = None) + +基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) + +#### get_addition_instruction(addition_instruction: str) + +拼接addition_instruction + +#### get_conversation_id(app_id: str) + +#### get_file_id(conversation_id: str, app_id: str, file_path: str) + +#### manifests *= [{'description': '该工具支持对图片以及文档内容进行理解,并基于图片以及文档内容对用户的提问进行回答,包括但不限于文档内容问答、总结摘要、内容分析。', 'name': 'document_understanding', 'parameters': {'properties': {'addition_instruction': {'description': '用户增强指令', 'type': 'string'}, 'app_id': {'description': '系统应用ID', 'type': 'string'}, 'file_path': {'description': '用户上传的文档的文件路径', 'type': 'string'}, 'instruction': {'description': '用户指令', 'type': 'string'}, 'query': {'description': '用户输入的query', 'type': 'string'}}, 'required': ['query', 'file_path', 'instruction', 'addition_instruction', 'app_id'], 'type': 'object'}}]* + +#### meta + +`DocumentUnderstandingArgs` 的别名 + +#### name *= 'document_understanding'* + +#### run(message: [Message](appbuilder.core.md#appbuilder.core.message.Message), file_path, instruction='', addition_instruction='', app_id='', stream=False, timeout=None) + +run方法,用于执行长文档理解任务 +:param message: 用户输入query +:param file_path: 用户输入的文件路径 +:param instruction: 用户输入的人设指令 +:param addition_instruction: 用户输入的增强版指令(如有) +:param app_id: 用户输入的app_id + +* **返回:** + 模型运行后的输出消息。 +* **返回类型:** + result ([Message](appbuilder.core.md#appbuilder.core.message.Message)) + +#### tool_eval(message: [Message](appbuilder.core.md#appbuilder.core.message.Message), file_path: str, stream: bool = False, \*\*kwargs) + +用于function call + +#### version *= 'v1'* diff --git a/docs/sphinx_md/appbuilder.core.components.embeddings.md b/docs/API-Reference/Python/appbuilder.core.components.embeddings.md similarity index 88% rename from docs/sphinx_md/appbuilder.core.components.embeddings.md rename to docs/API-Reference/Python/appbuilder.core.components.embeddings.md index 45a1c0c83..cd5cb2113 100644 --- a/docs/sphinx_md/appbuilder.core.components.embeddings.md +++ b/docs/API-Reference/Python/appbuilder.core.components.embeddings.md @@ -14,7 +14,7 @@ Embedding-V1是基于百度文心大模型技术的文本表示模型,将文 #### model -str = “Embedding-V1” +str = "Embedding-V1" ### 示例 @@ -40,7 +40,7 @@ embedding_batch = embedding.batch(Message(["hello", "world"])) 批量处理文本数据。 * **参数:** - **texts** (*Union* *[*[*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**List* *[**str* *]* *]* *,* *List* *[**str* *]* *]*) – 待处理的文本数据,可以是 Message 类型,包含多个文本列表,也可以是普通列表类型,包含多个文本。 + **texts** (*Union* *[*[*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**List* *[**str* *]* *]* *,* *List* *[**str* *]* *]*) -- 待处理的文本数据,可以是 Message 类型,包含多个文本列表,也可以是普通列表类型,包含多个文本。 * **返回:** 处理后的结果,为 Message 类型,包含一个二维浮点数列表,每个子列表对应输入文本列表中一个文本的处理结果。 * **返回类型:** @@ -57,7 +57,7 @@ embedding_batch = embedding.batch(Message(["hello", "world"])) 处理给定的文本或消息对象,并返回包含处理结果的消息对象。 * **参数:** - **text** (*Union* *[*[*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**str* *]* *,* *str* *]*) – 待处理的文本或消息对象。 + **text** (*Union* *[*[*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**str* *]* *,* *str* *]*) -- 待处理的文本或消息对象。 * **返回:** 处理后的结果,封装在消息对象中。结果是一个浮点数列表。 * **返回类型:** diff --git a/docs/sphinx_md/appbuilder.core.components.excel2figure.md b/docs/API-Reference/Python/appbuilder.core.components.excel2figure.md similarity index 61% rename from docs/sphinx_md/appbuilder.core.components.excel2figure.md rename to docs/API-Reference/Python/appbuilder.core.components.excel2figure.md index 17630c492..a369ba294 100644 --- a/docs/sphinx_md/appbuilder.core.components.excel2figure.md +++ b/docs/API-Reference/Python/appbuilder.core.components.excel2figure.md @@ -13,10 +13,10 @@ excel2figure component excel2figure 组件类 * **参数:** - * **model** – str - * **secret_key** – Optional[str] - * **gateway** – str - * **lazy_certification** – bool + * **model** -- str + * **secret_key** -- Optional[str] + * **gateway** -- str + * **lazy_certification** -- bool #### excluded_models *: List[str]* *= ['Yi-34B-Chat', 'ChatLaw']* @@ -35,7 +35,7 @@ excel2figure 组件类 执行 excel2figure。 * **参数:** - **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) – 消息对象,其 content 属性是一个字典,包含以下键值对: + **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 消息对象,其 content 属性是一个字典,包含以下键值对: - query (str): 用户的问题。 - excel_file_url (str): 用户的 Excel 文件地址。 * **返回:** @@ -43,15 +43,15 @@ excel2figure 组件类 * **返回类型:** [Message](appbuilder.core.md#appbuilder.core.message.Message) * **抛出:** - **ValueError** – 当 message.content 解析失败时抛出此异常。 + **ValueError** -- 当 message.content 解析失败时抛出此异常。 #### set_secret_key_and_gateway(\*\*kwargs) 设置密钥和网关地址。 * **参数:** - * **secret_key** (*Optional* *[**str* *]* *,* *optional*) – 密钥,默认为None。如果未指定,则使用实例当前的密钥。 - * **gateway** (*str* *,* *optional*) – 网关地址,默认为空字符串。如果未指定,则使用实例当前的网关地址。 + * **secret_key** (*Optional* *[**str* *]* *,* *optional*) -- 密钥,默认为None。如果未指定,则使用实例当前的密钥。 + * **gateway** (*str* *,* *optional*) -- 网关地址,默认为空字符串。如果未指定,则使用实例当前的网关地址。 * **返回:** None @@ -60,17 +60,17 @@ excel2figure 组件类 对指定的Excel文件进行图表生成和评估。 * **参数:** - * **streaming** (*bool*) – 是否以流式传输方式返回结果。如果为True,则通过生成器返回结果;如果为False,则直接返回结果。 - * **origin_query** (*str*) – 原始查询字符串,用于在缺少其他查询参数时使用。 - * **file_urls** (*dict*) – 包含Excel文件信息的字典,其中键为文件名,值为文件URL。 - * **\*\*kwargs** – 其他关键字参数,可以包括查询字符串等。 + * **streaming** (*bool*) -- 是否以流式传输方式返回结果。如果为True,则通过生成器返回结果;如果为False,则直接返回结果。 + * **origin_query** (*str*) -- 原始查询字符串,用于在缺少其他查询参数时使用。 + * **file_urls** (*dict*) -- 包含Excel文件信息的字典,其中键为文件名,值为文件URL。 + * **\*\*kwargs** -- 其他关键字参数,可以包括查询字符串等。 * **返回:** 如果streaming为True,则通过生成器返回结果。每个结果是一个字典,包含以下键: - - event (str): 事件类型,始终为’excel_to_figure’。 - - type (str): 数据类型,始终为’files’。 + - event (str): 事件类型,始终为'excel_to_figure'。 + - type (str): 数据类型,始终为'files'。 - text (list of str): 包含生成的图表信息的列表。 如果streaming为False,则直接返回一个包含上述信息的字典。 * **抛出:** - * **ValueError** – 如果file_urls的长度不等于1,则抛出异常。 - * **RuntimeError** – 如果Excel文件到图表的转换失败或出现异常,则抛出异常。 + * **ValueError** -- 如果file_urls的长度不等于1,则抛出异常。 + * **RuntimeError** -- 如果Excel文件到图表的转换失败或出现异常,则抛出异常。 diff --git a/docs/sphinx_md/appbuilder.core.components.extract_table.md b/docs/API-Reference/Python/appbuilder.core.components.extract_table.md similarity index 80% rename from docs/sphinx_md/appbuilder.core.components.extract_table.md rename to docs/API-Reference/Python/appbuilder.core.components.extract_table.md index 0dd74ab8c..965b19487 100644 --- a/docs/sphinx_md/appbuilder.core.components.extract_table.md +++ b/docs/API-Reference/Python/appbuilder.core.components.extract_table.md @@ -49,13 +49,13 @@ logger.info("Tables: {}".format( 将文档原始解析结果,请求云端进行表格抽取,返回表格列表。 * **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) – 文档原始解析结果。 - * **table_max_size** (*int*) – 单个表格的长度的最大值(包含上文),按字符数即len(table_str)统计,默认为800。如果表格超长,则会被拆 分成多个子表格,拆分的最小粒度为表格的行。若单行就超长,则会强制按table_max_size截断。截断时会优先截断上文,尽量保留表格内容。 - * **doc_node_num_before_table** (*int*) – 表格前附加的上文DocParser Node的数量,默认为1。范围:1~10。 + * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 文档原始解析结果。 + * **table_max_size** (*int*) -- 单个表格的长度的最大值(包含上文),按字符数即len(table_str)统计,默认为800。如果表格超长,则会被拆 分成多个子表格,拆分的最小粒度为表格的行。若单行就超长,则会强制按table_max_size截断。截断时会优先截断上文,尽量保留表格内容。 + * **doc_node_num_before_table** (*int*) -- 表格前附加的上文DocParser Node的数量,默认为1。范围:1~10。 * **返回:** 返回解析后的消息实体对象 : Message.content (list): 解析出来的文档表格,list(二维)。解析出来的文档表格,如果元素长度为1,则对应原文档中格式化后的 长度不超过\`table_max_size\`的表格;如果元素长度>1,则是对应原文档中一个大表格,该表格被拆分成的多个子表格,以满足设置 大小。输出结果数据结构样例:[[{table1}], [{table2-part1}, {table2-part2}]] * **返回类型:** [Message](appbuilder.core.md#appbuilder.core.message.Message) * **抛出:** - **ValueError** – 当输入参数不为文档原始解析结果时,或值不合法时,抛出异常。 + **ValueError** -- 当输入参数不为文档原始解析结果时,或值不合法时,抛出异常。 diff --git a/docs/sphinx_md/appbuilder.core.components.gbi.md b/docs/API-Reference/Python/appbuilder.core.components.gbi.md similarity index 100% rename from docs/sphinx_md/appbuilder.core.components.gbi.md rename to docs/API-Reference/Python/appbuilder.core.components.gbi.md diff --git a/docs/sphinx_md/appbuilder.core.components.gbi.nl2sql.md b/docs/API-Reference/Python/appbuilder.core.components.gbi.nl2sql.md similarity index 89% rename from docs/sphinx_md/appbuilder.core.components.gbi.nl2sql.md rename to docs/API-Reference/Python/appbuilder.core.components.gbi.nl2sql.md index 314004e68..a2121380e 100644 --- a/docs/sphinx_md/appbuilder.core.components.gbi.nl2sql.md +++ b/docs/API-Reference/Python/appbuilder.core.components.gbi.nl2sql.md @@ -21,15 +21,15 @@ gib nl2sql 执行自然语言转SQL操作。 * **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) – + * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 包含用户问题和会话历史的消息对象。 - message.content 是一个字典,包含以下关键字: > 1. query: 用户问题 > 2. session: 会话历史列表,参考 SessionRecord > 3. column_constraint: 列选约束,参考 ColumnItem 具体定义 - * **timeout** (*float*) – 超时时间,默认为60秒。 - * **retry** (*int*) – 重试次数,默认为0次。 + * **timeout** (*float*) -- 超时时间,默认为60秒。 + * **retry** (*int*) -- 重试次数,默认为0次。 * **返回:** 转换结果以Message对象形式返回,其中content属性包含NL2SqlResult对象。 * **返回类型:** diff --git a/docs/sphinx_md/appbuilder.core.components.gbi.select_table.md b/docs/API-Reference/Python/appbuilder.core.components.gbi.select_table.md similarity index 79% rename from docs/sphinx_md/appbuilder.core.components.gbi.select_table.md rename to docs/API-Reference/Python/appbuilder.core.components.gbi.select_table.md index 438a0eb5d..cdd0ec0c8 100644 --- a/docs/sphinx_md/appbuilder.core.components.gbi.select_table.md +++ b/docs/API-Reference/Python/appbuilder.core.components.gbi.select_table.md @@ -17,17 +17,17 @@ gbi 选表 执行查询操作,返回识别的表名列表。 * **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) – + * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 包含查询信息的消息对象。 - message.content 字典包含以下 key: > 1. query (str): 用户的问题输入。 > 2. session (list, optional): 对话历史,默认为空列表。 - * **timeout** (*int* *,* *optional*) – 超时时间,默认为 60 秒。 - * **retry** (*int* *,* *optional*) – 重试次数,默认为 0。 + * **timeout** (*int* *,* *optional*) -- 超时时间,默认为 60 秒。 + * **retry** (*int* *,* *optional*) -- 重试次数,默认为 0。 * **返回:** 包含识别出的表名列表的 Message 对象。 * **返回类型:** [Message](appbuilder.core.md#appbuilder.core.message.Message)[List[str]] * **抛出:** - **ValueError** – 如果输入的 message.content 不符合期望的格式,将抛出 ValueError 异常。 + **ValueError** -- 如果输入的 message.content 不符合期望的格式,将抛出 ValueError 异常。 diff --git a/docs/sphinx_md/appbuilder.core.components.general_ocr.md b/docs/API-Reference/Python/appbuilder.core.components.general_ocr.md similarity index 76% rename from docs/sphinx_md/appbuilder.core.components.general_ocr.md rename to docs/API-Reference/Python/appbuilder.core.components.general_ocr.md index f25a153a3..5be1c578b 100644 --- a/docs/sphinx_md/appbuilder.core.components.general_ocr.md +++ b/docs/API-Reference/Python/appbuilder.core.components.general_ocr.md @@ -37,11 +37,11 @@ print(out.content) 执行图片中的文字识别。 * **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) – 输入图片或图片url下载地址用于执行识别操作。举例: Message(content={“raw_image”: b”…”}) 或 Message(content={“url”: “[https://image/download/url](https://image/download/url)”})。 - * **timeout** (*float* *,* *可选*) – HTTP超时时间。 - * **retry** (*int* *,* *可选*) – HTTP重试次数。 + * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入图片或图片url下载地址用于执行识别操作。举例: Message(content={"raw_image": b"..."}) 或 Message(content={"url": "[https://image/download/url](https://image/download/url)"})。 + * **timeout** (*float* *,* *可选*) -- HTTP超时时间。 + * **retry** (*int* *,* *可选*) -- HTTP重试次数。 * **返回:** - 模型识别结果。举例: Message(content={“words_result”:[{“words”:”100”}, {“words”:”G8”}]})。 + 模型识别结果。举例: Message(content={"words_result":[{"words":"100"}, {"words":"G8"}]})。 * **返回类型:** [Message](appbuilder.core.md#appbuilder.core.message.Message) @@ -50,9 +50,9 @@ print(out.content) 根据给定的参数执行OCR识别功能。 * **参数:** - * **name** (*str*) – 函数名称,此处未使用,但为保持一致性保留。 - * **streaming** (*bool*) – 是否以流式方式返回结果。如果为True,则逐个返回结果,否则返回全部结果。 - * **kwargs** – 关键字参数,支持以下参数: + * **name** (*str*) -- 函数名称,此处未使用,但为保持一致性保留。 + * **streaming** (*bool*) -- 是否以流式方式返回结果。如果为True,则逐个返回结果,否则返回全部结果。 + * **kwargs** -- 关键字参数,支持以下参数: traceid (str): 请求的唯一标识符,用于追踪请求和响应。 img_url (str): 待识别图片的URL。 file_urls (dict): 包含文件名和对应URL的字典。如果提供了img_url,则忽略此参数。 @@ -61,6 +61,6 @@ print(out.content) 如果streaming为False,则返回包含识别结果的JSON字符串。 如果streaming为True,则逐个返回包含识别结果的字典。 * **抛出:** - **InvalidRequestArgumentError** – 如果请求格式错误(例如未设置文件名或指定文件名对应的URL不存在),则抛出此异常。 + **InvalidRequestArgumentError** -- 如果请求格式错误(例如未设置文件名或指定文件名对应的URL不存在),则抛出此异常。 #### version *= 'v1'* diff --git a/docs/sphinx_md/appbuilder.core.components.handwrite_ocr.md b/docs/API-Reference/Python/appbuilder.core.components.handwrite_ocr.md similarity index 78% rename from docs/sphinx_md/appbuilder.core.components.handwrite_ocr.md rename to docs/API-Reference/Python/appbuilder.core.components.handwrite_ocr.md index e424d5911..535f0f1db 100644 --- a/docs/sphinx_md/appbuilder.core.components.handwrite_ocr.md +++ b/docs/API-Reference/Python/appbuilder.core.components.handwrite_ocr.md @@ -39,9 +39,9 @@ print(out.content) 输入图片并识别其中的文字 * **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) – 输入图片或图片url下载地址用于执行识别操作.例如: Message(content={“raw_image”: b”…”}) 或 Message(content={“url”: “[https://image/download/url](https://image/download/url)”}). - * **timeout** (*float* *,* *optional*) – HTTP超时时间. 默认为None. - * **retry** (*int* *,* *optional*) – HTTP重试次数. 默认为0. + * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入图片或图片url下载地址用于执行识别操作.例如: Message(content={"raw_image": b"..."}) 或 Message(content={"url": "[https://image/download/url](https://image/download/url)"}). + * **timeout** (*float* *,* *optional*) -- HTTP超时时间. 默认为None. + * **retry** (*int* *,* *optional*) -- HTTP重试次数. 默认为0. * **返回:** 手写体模型识别结果. * **返回类型:** @@ -52,15 +52,15 @@ print(out.content) 对指定文件或URL进行手写识别。 * **参数:** - * **name** (*str*) – 任务名称。 - * **streaming** (*bool*) – 是否以流式形式返回结果。 - * **kwargs** – 其他参数,包括: + * **name** (*str*) -- 任务名称。 + * **streaming** (*bool*) -- 是否以流式形式返回结果。 + * **kwargs** -- 其他参数,包括: traceid (str, optional): 请求的traceid,用于标识请求的唯一性。默认为None。 - file_names (List[str], optional): 待识别的文件名列表。默认为None,此时会从kwargs中获取’files’参数。 + file_names (List[str], optional): 待识别的文件名列表。默认为None,此时会从kwargs中获取'files'参数。 file_urls (Dict[str, str], optional): 文件名与URL的映射字典。默认为空字典。 * **返回:** 如果streaming为True,则以生成器形式返回识别结果,否则直接返回结果字符串。 * **抛出:** - **InvalidRequestArgumentError** – 如果请求格式错误,例如指定的文件名对应的URL不存在。 + **InvalidRequestArgumentError** -- 如果请求格式错误,例如指定的文件名对应的URL不存在。 #### version *= 'v1'* diff --git a/docs/sphinx_md/appbuilder.core.components.image_understand.md b/docs/API-Reference/Python/appbuilder.core.components.image_understand.md similarity index 78% rename from docs/sphinx_md/appbuilder.core.components.image_understand.md rename to docs/API-Reference/Python/appbuilder.core.components.image_understand.md index 67b47e5dd..e5559114b 100644 --- a/docs/sphinx_md/appbuilder.core.components.image_understand.md +++ b/docs/API-Reference/Python/appbuilder.core.components.image_understand.md @@ -39,10 +39,10 @@ print(out.content) 执行图像内容理解 * **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) – 输入图片或图片url下载地址用于执行识别操作. 举例: Message(content={“raw_image”: b”…”, “question”: “图片主要内容是什么?”}) - 或 Message(content={“url”: “[https://image/download/url](https://image/download/url)”, “question”: “图片主要内容是什么?”}). - * **timeout** (*float* *,* *optional*) – HTTP超时时间. 默认为 None. - * **retry** (*int* *,* *optional*) – HTTP重试次数. 默认为 0. + * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入图片或图片url下载地址用于执行识别操作. 举例: Message(content={"raw_image": b"...", "question": "图片主要内容是什么?"}) + 或 Message(content={"url": "[https://image/download/url](https://image/download/url)", "question": "图片主要内容是什么?"}). + * **timeout** (*float* *,* *optional*) -- HTTP超时时间. 默认为 None. + * **retry** (*int* *,* *optional*) -- HTTP重试次数. 默认为 0. * **返回:** 模型识别结果. * **返回类型:** @@ -53,10 +53,10 @@ print(out.content) 用于工具的执行,调用底层接口进行图像内容理解 * **参数:** - * **name** (*str*) – 工具名 - * **streaming** (*bool*) – 是否流式返回 - * **origin_query** (*str*) – 用户原始query - * **\*\*kwargs** – 工具调用的额外关键字参数 + * **name** (*str*) -- 工具名 + * **streaming** (*bool*) -- 是否流式返回 + * **origin_query** (*str*) -- 用户原始query + * **\*\*kwargs** -- 工具调用的额外关键字参数 * **返回:** 图片内容理解结果 * **返回类型:** diff --git a/docs/sphinx_md/appbuilder.core.components.landmark_recognize.md b/docs/API-Reference/Python/appbuilder.core.components.landmark_recognize.md similarity index 75% rename from docs/sphinx_md/appbuilder.core.components.landmark_recognize.md rename to docs/API-Reference/Python/appbuilder.core.components.landmark_recognize.md index 0e94a9140..12395481d 100644 --- a/docs/sphinx_md/appbuilder.core.components.landmark_recognize.md +++ b/docs/API-Reference/Python/appbuilder.core.components.landmark_recognize.md @@ -30,12 +30,12 @@ with open("xxxx.jpg", "rb") as f: 执行地标识别任务 * **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) – 输入消息对象,包含待识别的图片或图片URL。 - 例如:Message(content={“raw_image”: b”…”}) 或 Message(content={“url”: “[https://image/download/url](https://image/download/url)”})。 - * **timeout** (*float* *,* *optional*) – HTTP请求的超时时间。默认为None。 - * **retry** (*int* *,* *optional*) – HTTP请求的重试次数。默认为0。 + * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入消息对象,包含待识别的图片或图片URL。 + 例如:Message(content={"raw_image": b"..."}) 或 Message(content={"url": "[https://image/download/url](https://image/download/url)"})。 + * **timeout** (*float* *,* *optional*) -- HTTP请求的超时时间。默认为None。 + * **retry** (*int* *,* *optional*) -- HTTP请求的重试次数。默认为0。 * **返回:** 地标识别结果的消息对象。 - : 例如:Message(content={“landmark”: b”狮身人面像”}) + : 例如:Message(content={"landmark": b"狮身人面像"}) * **返回类型:** [Message](appbuilder.core.md#appbuilder.core.message.Message) diff --git a/docs/sphinx_md/appbuilder.core.components.llms.dialog_summary.md b/docs/API-Reference/Python/appbuilder.core.components.llms.dialog_summary.md similarity index 72% rename from docs/sphinx_md/appbuilder.core.components.llms.dialog_summary.md rename to docs/API-Reference/Python/appbuilder.core.components.llms.dialog_summary.md index 88c6b8db3..3743ecc9f 100644 --- a/docs/sphinx_md/appbuilder.core.components.llms.dialog_summary.md +++ b/docs/API-Reference/Python/appbuilder.core.components.llms.dialog_summary.md @@ -8,7 +8,7 @@ 基类:`CompletionBaseComponent` -会话小结大模型组件, 基于生成式大模型对一段用户与坐席的对话生成总结,结果按{“诉求”: “”, “回应”: “”, “解决情况”: “”}格式输出。 +会话小结大模型组件, 基于生成式大模型对一段用户与坐席的对话生成总结,结果按{"诉求": "", "回应": "", "解决情况": ""}格式输出。 Examples: @@ -36,12 +36,12 @@ print(answer) 使用给定的输入运行模型并返回结果。 * **参数:** - * **(****obj** (*message*) – Message): 输入消息,用于模型的主要输入内容。这是一个必需的参数。 - * **stream** (*bool* *,* *optional*) – 指定是否以流式形式返回响应。默认为 False。 - * **temperature** (*float* *,* *optional*) – 模型配置的温度参数,用于调整模型的生成概率。 + * **(****obj** (*message*) -- Message): 输入消息,用于模型的主要输入内容。这是一个必需的参数。 + * **stream** (*bool* *,* *optional*) -- 指定是否以流式形式返回响应。默认为 False。 + * **temperature** (*float* *,* *optional*) -- 模型配置的温度参数,用于调整模型的生成概率。 取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。 默认值为 1e-10。 - * **top_p** (*float* *,* *optional*) – 影响输出文本的多样性,取值越大,生成文本的多样性越强。 + * **top_p** (*float* *,* *optional*) -- 影响输出文本的多样性,取值越大,生成文本的多样性越强。 取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。 默认值为 0。 * **返回:** diff --git a/docs/sphinx_md/appbuilder.core.components.llms.hallucination_detection.md b/docs/API-Reference/Python/appbuilder.core.components.llms.hallucination_detection.md similarity index 77% rename from docs/sphinx_md/appbuilder.core.components.llms.hallucination_detection.md rename to docs/API-Reference/Python/appbuilder.core.components.llms.hallucination_detection.md index d5400d62d..8a63b5a5d 100644 --- a/docs/sphinx_md/appbuilder.core.components.llms.hallucination_detection.md +++ b/docs/API-Reference/Python/appbuilder.core.components.llms.hallucination_detection.md @@ -46,11 +46,11 @@ print(result) Send a byte array of an audio file to obtain the result of speech recognition. * **参数:** - * **version** (*str*) – API version. - * **base_url** (*str*) – Base URL of the API. - * **request** (*Request*) – Request object containing audio file and other parameters. - * **timeout** (*float* *,* *optional*) – Timeout for the request. Defaults to None. - * **retry** (*int* *,* *optional*) – Number of retries for the request. Defaults to 0. + * **version** (*str*) -- API version. + * **base_url** (*str*) -- Base URL of the API. + * **request** (*Request*) -- Request object containing audio file and other parameters. + * **timeout** (*float* *,* *optional*) -- Timeout for the request. Defaults to None. + * **retry** (*int* *,* *optional*) -- Number of retries for the request. Defaults to 0. * **返回:** Processed response object. * **返回类型:** @@ -69,28 +69,28 @@ Send a byte array of an audio file to obtain the result of speech recognition. 使用给定的输入运行模型并返回结果。 * **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) – 输入消息,包含 query、context 和 answer。是必需的参数。 - * **stream** (*bool* *,* *可选*) – 是否以流式形式返回响应。默认为 False。 - * **temperature** (*float* *,* *可选*) – 模型配置的温度参数,用于调整模型的生成概率。 + * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入消息,包含 query、context 和 answer。是必需的参数。 + * **stream** (*bool* *,* *可选*) -- 是否以流式形式返回响应。默认为 False。 + * **temperature** (*float* *,* *可选*) -- 模型配置的温度参数,用于调整模型的生成概率。 取值范围为 0.0 到 1.0,较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 - * **top_p** (*float* *,* *可选*) – 影响输出文本的多样性,取值越大,生成文本的多样性越强。 + * **top_p** (*float* *,* *可选*) -- 影响输出文本的多样性,取值越大,生成文本的多样性越强。 取值范围为 0.0 到 1.0,较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 * **返回:** 模型运行后的输出消息。 * **返回类型:** result ([Message](appbuilder.core.md#appbuilder.core.message.Message)) * **抛出:** - * **AssertionError** – 如果输入的 message 中缺少 query、context 或 answer。 - * **AppBuilderServerException** – 如果请求执行失败,将抛出异常,包含服务错误码和错误信息。 + * **AssertionError** -- 如果输入的 message 中缺少 query、context 或 answer。 + * **AppBuilderServerException** -- 如果请求执行失败,将抛出异常,包含服务错误码和错误信息。 #### tool_eval(name: str, stream: bool = False, \*\*kwargs) 调用函数进行工具评估。 * **参数:** - * **name** (*str*) – 函数名,当前方法未使用此参数,预留接口。 - * **stream** (*bool* *,* *optional*) – 是否以流的方式返回结果,默认为False。如果为True,则逐个返回结果;如果为False,则一次性返回所有结果。 - * **\*\*kwargs** – + * **name** (*str*) -- 函数名,当前方法未使用此参数,预留接口。 + * **stream** (*bool* *,* *optional*) -- 是否以流的方式返回结果,默认为False。如果为True,则逐个返回结果;如果为False,则一次性返回所有结果。 + * **\*\*kwargs** -- 关键字参数,包含评估所需的输入参数。 - query (str): 查询语句。 @@ -102,6 +102,6 @@ Send a byte array of an audio file to obtain the result of speech recognition. * **返回:** 如果stream为False,返回包含所有评估结果的列表;如果stream为True,逐个返回评估结果。 * **抛出:** - **ValueError** – 如果缺少query、context或answer参数,将引发此异常。 + **ValueError** -- 如果缺少query、context或answer参数,将引发此异常。 #### version *: str* *= 'v1'* diff --git a/docs/sphinx_md/appbuilder.core.components.llms.is_complex_query.md b/docs/API-Reference/Python/appbuilder.core.components.llms.is_complex_query.md similarity index 66% rename from docs/sphinx_md/appbuilder.core.components.llms.is_complex_query.md rename to docs/API-Reference/Python/appbuilder.core.components.llms.is_complex_query.md index ede4feb93..a9c32c1a4 100644 --- a/docs/sphinx_md/appbuilder.core.components.llms.is_complex_query.md +++ b/docs/API-Reference/Python/appbuilder.core.components.llms.is_complex_query.md @@ -41,10 +41,10 @@ print("Answer: \n{}".format(answer.content)) 给定输入(message)到模型运行,同时指定运行参数,并返回结果。 * **参数:** - * **(****obj** (*message*) – Message): 输入消息,用于模型的主要输入内容。这是一个必需的参数。 - * **stream** (*bool* *,* *optional*) – 指定是否以流式形式返回响应。默认为 False。 - * **temperature** (*float* *,* *optional*) – 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 - * **top_p** (*float* *,* *optional*) – 影响输出文本的多样性,取值越大,生成文本的多样性越强。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 + * **(****obj** (*message*) -- Message): 输入消息,用于模型的主要输入内容。这是一个必需的参数。 + * **stream** (*bool* *,* *optional*) -- 指定是否以流式形式返回响应。默认为 False。 + * **temperature** (*float* *,* *optional*) -- 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 + * **top_p** (*float* *,* *optional*) -- 影响输出文本的多样性,取值越大,生成文本的多样性越强。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 * **返回:** Message: 模型运行后的输出消息。 * **返回类型:** diff --git a/docs/sphinx_md/appbuilder.core.components.llms.md b/docs/API-Reference/Python/appbuilder.core.components.llms.md similarity index 100% rename from docs/sphinx_md/appbuilder.core.components.llms.md rename to docs/API-Reference/Python/appbuilder.core.components.llms.md diff --git a/docs/sphinx_md/appbuilder.core.components.llms.mrc.md b/docs/API-Reference/Python/appbuilder.core.components.llms.mrc.md similarity index 58% rename from docs/sphinx_md/appbuilder.core.components.llms.mrc.md rename to docs/API-Reference/Python/appbuilder.core.components.llms.mrc.md index 685a4b635..08ef170d8 100644 --- a/docs/sphinx_md/appbuilder.core.components.llms.mrc.md +++ b/docs/API-Reference/Python/appbuilder.core.components.llms.mrc.md @@ -48,16 +48,16 @@ print(result) 运行阅读理解问答模型并返回结果。 * **参数:** - * **(****obj** (*context_list*) – Message): 输入消息,包含用户提出的问题。这是一个必需的参数。 - * **(****obj** – Message): 用户输入的问题对应的段落文本列表。这是一个必需的参数。 - * **reject** (*bool* *,* *可选*) – 拒答开关,如果为 True,则启用拒答能力。默认为 False。 - * **clarify** (*bool* *,* *可选*) – 澄清开关,如果为 True,则启用澄清能力。默认为 False。 - * **highlight** (*bool* *,* *可选*) – 重点强调开关,如果为 True,则启用重点强调能力。默认为 False。 - * **friendly** (*bool* *,* *可选*) – 友好性提升开关,如果为 True,则启用友好性提升能力。默认为 False。 - * **cite** (*bool* *,* *可选*) – 溯源开关,如果为 True,则启用溯源能力。默认为 False。 - * **stream** (*bool* *,* *可选*) – 指定是否以流式形式返回响应。默认为 False。 - * **temperature** (*float* *,* *可选*) – 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 - * **top_p** (*float* *,* *可选*) – 影响输出文本的多样性,取值越大,生成文本的多样性越强。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 + * **(****obj** (*context_list*) -- Message): 输入消息,包含用户提出的问题。这是一个必需的参数。 + * **(****obj** -- Message): 用户输入的问题对应的段落文本列表。这是一个必需的参数。 + * **reject** (*bool* *,* *可选*) -- 拒答开关,如果为 True,则启用拒答能力。默认为 False。 + * **clarify** (*bool* *,* *可选*) -- 澄清开关,如果为 True,则启用澄清能力。默认为 False。 + * **highlight** (*bool* *,* *可选*) -- 重点强调开关,如果为 True,则启用重点强调能力。默认为 False。 + * **friendly** (*bool* *,* *可选*) -- 友好性提升开关,如果为 True,则启用友好性提升能力。默认为 False。 + * **cite** (*bool* *,* *可选*) -- 溯源开关,如果为 True,则启用溯源能力。默认为 False。 + * **stream** (*bool* *,* *可选*) -- 指定是否以流式形式返回响应。默认为 False。 + * **temperature** (*float* *,* *可选*) -- 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 + * **top_p** (*float* *,* *可选*) -- 影响输出文本的多样性,取值越大,生成文本的多样性越强。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 * **返回:** Message: 模型运行后的输出消息。 * **返回类型:** diff --git a/docs/sphinx_md/appbuilder.core.components.llms.nl2pandas.md b/docs/API-Reference/Python/appbuilder.core.components.llms.nl2pandas.md similarity index 65% rename from docs/sphinx_md/appbuilder.core.components.llms.nl2pandas.md rename to docs/API-Reference/Python/appbuilder.core.components.llms.nl2pandas.md index d9bf1d518..3913aa222 100644 --- a/docs/sphinx_md/appbuilder.core.components.llms.nl2pandas.md +++ b/docs/API-Reference/Python/appbuilder.core.components.llms.nl2pandas.md @@ -42,11 +42,11 @@ answer = nl2pandas(query, table_info = table_info) 使用给定的输入运行模型并返回结果。 * **参数:** - * **(****obj** (*table_info*) – Message): 输入问题,通常是针对表格信息的提问,如’海淀区的小学有哪些’。这是一个必需的参数。 - * **(****obj** – Message, optional): 表格信息,包括表格列名、对应列名的示例和释义。默认值为 None,但这是一个必需的参数。 - * **stream** (*bool* *,* *optional*) – 指定是否以流式形式返回响应。默认为 False。 - * **temperature** (*float* *,* *optional*) – 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 - * **top_p** (*float* *,* *optional*) – 影响输出文本的多样性,取值越大,生成文本的多样性越强。取值范围为 0.0 到 1.0,较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 + * **(****obj** (*table_info*) -- Message): 输入问题,通常是针对表格信息的提问,如'海淀区的小学有哪些'。这是一个必需的参数。 + * **(****obj** -- Message, optional): 表格信息,包括表格列名、对应列名的示例和释义。默认值为 None,但这是一个必需的参数。 + * **stream** (*bool* *,* *optional*) -- 指定是否以流式形式返回响应。默认为 False。 + * **temperature** (*float* *,* *optional*) -- 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 + * **top_p** (*float* *,* *optional*) -- 影响输出文本的多样性,取值越大,生成文本的多样性越强。取值范围为 0.0 到 1.0,较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 * **返回:** Message: 模型运行后的输出消息。 * **返回类型:** diff --git a/docs/sphinx_md/appbuilder.core.components.llms.oral_query_generation.md b/docs/API-Reference/Python/appbuilder.core.components.llms.oral_query_generation.md similarity index 74% rename from docs/sphinx_md/appbuilder.core.components.llms.oral_query_generation.md rename to docs/API-Reference/Python/appbuilder.core.components.llms.oral_query_generation.md index 02a51f76b..cfe950bb8 100644 --- a/docs/sphinx_md/appbuilder.core.components.llms.oral_query_generation.md +++ b/docs/API-Reference/Python/appbuilder.core.components.llms.oral_query_generation.md @@ -50,13 +50,13 @@ Send a byte array of an audio file to obtain the result of speech recognition. 使用给定的输入运行模型并返回结果。 * **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) – 输入消息,包含query、context和answer等信息。这是一个必需的参数。 - * **query_type** (*str* *,* *可选*) – 待生成的query类型,包括问题、短语和全部(问题+短语)。默认为全部。 - * **output_format** (*str* *,* *可选*) – 输出格式,包括json和str,当stream为True时,只能以json形式输出。默认为str。 - * **stream** (*bool* *,* *可选*) – 指定是否以流式形式返回响应。默认为 False。 - * **temperature** (*float* *,* *可选*) – 模型配置的温度参数,用于调整模型的生成概率。 + * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入消息,包含query、context和answer等信息。这是一个必需的参数。 + * **query_type** (*str* *,* *可选*) -- 待生成的query类型,包括问题、短语和全部(问题+短语)。默认为全部。 + * **output_format** (*str* *,* *可选*) -- 输出格式,包括json和str,当stream为True时,只能以json形式输出。默认为str。 + * **stream** (*bool* *,* *可选*) -- 指定是否以流式形式返回响应。默认为 False。 + * **temperature** (*float* *,* *可选*) -- 模型配置的温度参数,用于调整模型的生成概率。 取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 - * **top_p** (*float* *,* *可选*) – 影响输出文本的多样性,取值越大,生成文本的多样性越强。 + * **top_p** (*float* *,* *可选*) -- 影响输出文本的多样性,取值越大,生成文本的多样性越强。 取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 * **返回:** 模型运行后的输出消息。 @@ -68,17 +68,17 @@ Send a byte array of an audio file to obtain the result of speech recognition. 调用函数进行工具评估。 * **参数:** - * **name** (*str*) – 评估工具的名称。 - * **stream** (*bool* *,* *optional*) – 是否以流的形式返回结果。默认为False。 - * **\*\*kwargs** – 关键字参数,可以包含以下参数: + * **name** (*str*) -- 评估工具的名称。 + * **stream** (*bool* *,* *optional*) -- 是否以流的形式返回结果。默认为False。 + * **\*\*kwargs** -- 关键字参数,可以包含以下参数: text (str): 需要评估的文本。 - query_type (str, optional): 查询类型,默认为’全部’。 - output_format (str, optional): 输出格式,默认为’str’。 + query_type (str, optional): 查询类型,默认为'全部'。 + output_format (str, optional): 输出格式,默认为'str'。 model_configs (dict, optional): 模型配置,默认为空字典。 * **返回:** 如果stream为False,则返回评估结果列表; 如果stream为True,则逐个返回评估结果。 * **抛出:** - **ValueError** – 如果未提供text参数,则抛出ValueError异常。 + **ValueError** -- 如果未提供text参数,则抛出ValueError异常。 #### version *: str* *= 'v1'* diff --git a/docs/sphinx_md/appbuilder.core.components.llms.playground.md b/docs/API-Reference/Python/appbuilder.core.components.llms.playground.md similarity index 61% rename from docs/sphinx_md/appbuilder.core.components.llms.playground.md rename to docs/API-Reference/Python/appbuilder.core.components.llms.playground.md index 11f92c75b..2c4fe647a 100644 --- a/docs/sphinx_md/appbuilder.core.components.llms.playground.md +++ b/docs/API-Reference/Python/appbuilder.core.components.llms.playground.md @@ -34,18 +34,18 @@ play(appbuilder.Message({"name": "小明", "bot_name": "小红", "bot_type": " 使用给定的输入运行模型并返回结果。 * **参数:** - * **(****obj** (*message*) – Message): 输入消息,用于模型的主要输入内容。这是一个必需的参数。 - * **stream** (*bool* *,* *可选*) – 指定是否以流式形式返回响应。默认为 False。 - * **temperature** (*float* *,* *可选*) – 模型配置的温度参数,用于调整模型的生成概率。 + * **(****obj** (*message*) -- Message): 输入消息,用于模型的主要输入内容。这是一个必需的参数。 + * **stream** (*bool* *,* *可选*) -- 指定是否以流式形式返回响应。默认为 False。 + * **temperature** (*float* *,* *可选*) -- 模型配置的温度参数,用于调整模型的生成概率。 取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 - * **top_p** (*float* *,* *可选*) – 影响输出文本的多样性,取值越大,生成文本的多样性越强。 + * **top_p** (*float* *,* *可选*) -- 影响输出文本的多样性,取值越大,生成文本的多样性越强。 取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 - * **max_output_tokens** (*int* *,* *可选*) – 指定生成的文本的最大长度,默认最大输出token数为1024, 最小为2, + * **max_output_tokens** (*int* *,* *可选*) -- 指定生成的文本的最大长度,默认最大输出token数为1024, 最小为2, 最大输出token与选择的模型有关。 - * **disable_search** (*bool* *,* *可选*) – 是否强制关闭实时搜索功能,默认为 True,表示关闭。 - * **response_format** (*str* *,* *可选*) – 指定返回的消息格式,默认为 ‘text’,以文本模式返回。 - 可选 ‘json_object’,以 json 格式返回,但可能存在不满足效果的情况。 - * **stop** (*list* *[**str* *]* *,* *可选*) – 生成停止标识,当模型生成结果以 stop 中某个元素结尾时,停止文本生成。 + * **disable_search** (*bool* *,* *可选*) -- 是否强制关闭实时搜索功能,默认为 True,表示关闭。 + * **response_format** (*str* *,* *可选*) -- 指定返回的消息格式,默认为 'text',以文本模式返回。 + 可选 'json_object',以 json 格式返回,但可能存在不满足效果的情况。 + * **stop** (*list* *[**str* *]* *,* *可选*) -- 生成停止标识,当模型生成结果以 stop 中某个元素结尾时,停止文本生成。 每个元素长度不超过 20 字符,最多 4 个元素。 * **返回:** Message: 模型运行后的输出消息。 diff --git a/docs/sphinx_md/appbuilder.core.components.llms.qa_pair_mining.md b/docs/API-Reference/Python/appbuilder.core.components.llms.qa_pair_mining.md similarity index 55% rename from docs/sphinx_md/appbuilder.core.components.llms.qa_pair_mining.md rename to docs/API-Reference/Python/appbuilder.core.components.llms.qa_pair_mining.md index 72dbbc898..b24b8d971 100644 --- a/docs/sphinx_md/appbuilder.core.components.llms.qa_pair_mining.md +++ b/docs/API-Reference/Python/appbuilder.core.components.llms.qa_pair_mining.md @@ -23,10 +23,10 @@ Examples: 给定输入(message)到模型运行,同时指定运行参数,并返回结果。 * **参数:** - * **(****obj** (*message*) – Message): 输入消息,用于模型的主要输入内容。这是一个必需的参数。 - * **stream** (*bool* *,* *optional*) – 指定是否以流式形式返回响应。默认为 False。 - * **temperature** (*float* *,* *optional*) – 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 - * **top_p** (*float* *,* *optional*) – 影响输出文本的多样性,取值越大,生成文本的多样性越强。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 + * **(****obj** (*message*) -- Message): 输入消息,用于模型的主要输入内容。这是一个必需的参数。 + * **stream** (*bool* *,* *optional*) -- 指定是否以流式形式返回响应。默认为 False。 + * **temperature** (*float* *,* *optional*) -- 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 + * **top_p** (*float* *,* *optional*) -- 影响输出文本的多样性,取值越大,生成文本的多样性越强。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 * **返回:** Message: 模型运行后的输出消息。 * **返回类型:** diff --git a/docs/sphinx_md/appbuilder.core.components.llms.query_decomposition.md b/docs/API-Reference/Python/appbuilder.core.components.llms.query_decomposition.md similarity index 66% rename from docs/sphinx_md/appbuilder.core.components.llms.query_decomposition.md rename to docs/API-Reference/Python/appbuilder.core.components.llms.query_decomposition.md index fe98c5896..e809a4290 100644 --- a/docs/sphinx_md/appbuilder.core.components.llms.query_decomposition.md +++ b/docs/API-Reference/Python/appbuilder.core.components.llms.query_decomposition.md @@ -41,10 +41,10 @@ print("Answer: \n{}".format(answer.content)) 给定输入(message)到模型运行,同时指定运行参数,并返回结果。 * **参数:** - * **(****obj** (*message*) – Message): 输入消息,用于模型的主要输入内容。这是一个必需的参数。 - * **stream** (*bool* *,* *optional*) – 指定是否以流式形式返回响应。默认为 False。 - * **temperature** (*float* *,* *optional*) – 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 - * **top_p** (*float* *,* *optional*) – 影响输出文本的多样性,取值越大,生成文本的多样性越强。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 + * **(****obj** (*message*) -- Message): 输入消息,用于模型的主要输入内容。这是一个必需的参数。 + * **stream** (*bool* *,* *optional*) -- 指定是否以流式形式返回响应。默认为 False。 + * **temperature** (*float* *,* *optional*) -- 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 + * **top_p** (*float* *,* *optional*) -- 影响输出文本的多样性,取值越大,生成文本的多样性越强。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 * **返回:** Message: 模型运行后的输出消息。 * **返回类型:** diff --git a/docs/sphinx_md/appbuilder.core.components.llms.query_rewrite.md b/docs/API-Reference/Python/appbuilder.core.components.llms.query_rewrite.md similarity index 70% rename from docs/sphinx_md/appbuilder.core.components.llms.query_rewrite.md rename to docs/API-Reference/Python/appbuilder.core.components.llms.query_rewrite.md index 43fd6aa1b..5afe91de3 100644 --- a/docs/sphinx_md/appbuilder.core.components.llms.query_rewrite.md +++ b/docs/API-Reference/Python/appbuilder.core.components.llms.query_rewrite.md @@ -38,14 +38,14 @@ answer = query_rewrite(appbuilder.Message(['我应该怎么办理护照?', 使用给定的输入运行模型并返回结果。 * **参数:** - * **(****obj** (*message*) – Message): 输入消息,用于模型的主要输入内容。这是一个必需的参数。 - * **rewrite_type** (*str* *,* *可选*) – 改写类型选项,可选值为 ‘带机器人回复’(改写时参考user查询历史和assistant回复历史), - ‘仅用户查询’(改写时参考user查询历史)。默认为”带机器人回复”。 - * **stream** (*bool* *,* *可选*) – 指定是否以流式形式返回响应。默认为 False。 - * **temperature** (*float* *,* *可选*) – 模型配置的温度参数,用于调整模型的生成概率。 + * **(****obj** (*message*) -- Message): 输入消息,用于模型的主要输入内容。这是一个必需的参数。 + * **rewrite_type** (*str* *,* *可选*) -- 改写类型选项,可选值为 '带机器人回复'(改写时参考user查询历史和assistant回复历史), + '仅用户查询'(改写时参考user查询历史)。默认为"带机器人回复"。 + * **stream** (*bool* *,* *可选*) -- 指定是否以流式形式返回响应。默认为 False。 + * **temperature** (*float* *,* *可选*) -- 模型配置的温度参数,用于调整模型的生成概率。 取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。 默认值为 1e-10。 - * **top_p** (*float* *,* *可选*) – 影响输出文本的多样性,取值越大,生成文本的多样性越强。 + * **top_p** (*float* *,* *可选*) -- 影响输出文本的多样性,取值越大,生成文本的多样性越强。 取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。 默认值为 0。 * **返回:** @@ -53,6 +53,6 @@ answer = query_rewrite(appbuilder.Message(['我应该怎么办理护照?', * **返回类型:** obj * **抛出:** - **ValueError** – 如果输入消息为空或不符合要求,将抛出 ValueError 异常。 + **ValueError** -- 如果输入消息为空或不符合要求,将抛出 ValueError 异常。 #### version *: str* *= 'v1'* diff --git a/docs/sphinx_md/appbuilder.core.components.llms.similar_question.md b/docs/API-Reference/Python/appbuilder.core.components.llms.similar_question.md similarity index 70% rename from docs/sphinx_md/appbuilder.core.components.llms.similar_question.md rename to docs/API-Reference/Python/appbuilder.core.components.llms.similar_question.md index 06fa9b57a..1dead46a1 100644 --- a/docs/sphinx_md/appbuilder.core.components.llms.similar_question.md +++ b/docs/API-Reference/Python/appbuilder.core.components.llms.similar_question.md @@ -42,10 +42,10 @@ print("Answer: \n{}".format(answer.content)) 给定输入(message)到模型运行,同时指定运行参数,并返回结果。 * **参数:** - * **(****obj** (*message*) – Message): 输入消息,用于模型的主要输入内容。这是一个必需的参数。 - * **stream** (*bool* *,* *可选*) – 指定是否以流式形式返回响应。默认为 False。 - * **temperature** (*float* *,* *可选*) – 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 - * **top_p** (*float* *,* *可选*) – 影响输出文本的多样性,取值越大,生成文本的多样性越强。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 + * **(****obj** (*message*) -- Message): 输入消息,用于模型的主要输入内容。这是一个必需的参数。 + * **stream** (*bool* *,* *可选*) -- 指定是否以流式形式返回响应。默认为 False。 + * **temperature** (*float* *,* *可选*) -- 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 + * **top_p** (*float* *,* *可选*) -- 影响输出文本的多样性,取值越大,生成文本的多样性越强。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 * **返回:** Message: 模型运行后的输出消息。 * **返回类型:** @@ -56,9 +56,9 @@ print("Answer: \n{}".format(answer.content)) 执行函数调用的评估工具。 * **参数:** - * **name** (*str*) – 函数名。 - * **streaming** (*bool* *,* *optional*) – 是否以流式方式输出结果。默认为False。 - * **\*\*kwargs** – + * **name** (*str*) -- 函数名。 + * **streaming** (*bool* *,* *optional*) -- 是否以流式方式输出结果。默认为False。 + * **\*\*kwargs** -- 其他关键字参数,包括: traceid (str, optional): 请求的追踪ID。 @@ -70,6 +70,6 @@ print("Answer: \n{}".format(answer.content)) 如果streaming为False,则返回评估结果的字符串表示。 如果streaming为True,则生成评估结果的字符串表示的迭代器。 * **抛出:** - **ValueError** – 如果未提供query参数,则抛出此异常。 + **ValueError** -- 如果未提供query参数,则抛出此异常。 #### version *: str* *= 'v1'* diff --git a/docs/sphinx_md/appbuilder.core.components.llms.style_rewrite.md b/docs/API-Reference/Python/appbuilder.core.components.llms.style_rewrite.md similarity index 62% rename from docs/sphinx_md/appbuilder.core.components.llms.style_rewrite.md rename to docs/API-Reference/Python/appbuilder.core.components.llms.style_rewrite.md index 10567fb42..a77e7dea5 100644 --- a/docs/sphinx_md/appbuilder.core.components.llms.style_rewrite.md +++ b/docs/API-Reference/Python/appbuilder.core.components.llms.style_rewrite.md @@ -34,11 +34,11 @@ answer = style_rewrite(appbuilder.Message("文心大模型发布新版本"), sty 使用给定的输入运行模型并返回结果。 * **参数:** - * **(****obj** (*message*) – Message): 输入消息,用于模型的主要输入内容。这是一个必需的参数。 - * **style** (*str* *,* *optional*) – 想要转换的文本风格,目前有营销、客服、直播、激励及教学五种话术可选。默认为”营销话术”。 - * **stream** (*bool* *,* *optional*) – 指定是否以流式形式返回响应。默认为 False。 - * **temperature** (*float* *,* *optional*) – 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 - * **top_p** (*float* *,* *optional*) – 影响输出文本的多样性,取值越大,生成文本的多样性越强。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 + * **(****obj** (*message*) -- Message): 输入消息,用于模型的主要输入内容。这是一个必需的参数。 + * **style** (*str* *,* *optional*) -- 想要转换的文本风格,目前有营销、客服、直播、激励及教学五种话术可选。默认为"营销话术"。 + * **stream** (*bool* *,* *optional*) -- 指定是否以流式形式返回响应。默认为 False。 + * **temperature** (*float* *,* *optional*) -- 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 + * **top_p** (*float* *,* *optional*) -- 影响输出文本的多样性,取值越大,生成文本的多样性越强。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 * **返回:** Message: 模型运行后的输出消息。 * **返回类型:** @@ -49,14 +49,14 @@ answer = style_rewrite(appbuilder.Message("文心大模型发布新版本"), sty 执行工具评估函数 * **参数:** - * **name** (*str*) – 函数名称,本函数不使用该参数,但保留以符合某些框架的要求。 - * **streaming** (*bool* *,* *optional*) – 是否以流的形式返回结果。默认为 False,即一次性返回结果。如果设置为 True,则以生成器形式逐个返回结果。 - * **\*\*kwargs** – + * **name** (*str*) -- 函数名称,本函数不使用该参数,但保留以符合某些框架的要求。 + * **streaming** (*bool* *,* *optional*) -- 是否以流的形式返回结果。默认为 False,即一次性返回结果。如果设置为 True,则以生成器形式逐个返回结果。 + * **\*\*kwargs** -- 其他参数,包含但不限于: traceid (str): 请求的跟踪ID,用于日志记录和跟踪。 query (str): 待评估的文本。 - style (str, optional): 评估风格,可选值为 [‘营销话术’, ‘客服话术’, ‘直播话术’, ‘激励话术’, ‘教学话术’]。默认为 ‘营销话术’。 + style (str, optional): 评估风格,可选值为 ['营销话术', '客服话术', '直播话术', '激励话术', '教学话术']。默认为 '营销话术'。 model_configs (dict, optional): 模型配置参数,可选的键包括: > temperature (float, optional): 温度参数,用于控制生成文本的随机性。默认为 1e-10。 > top_p (float, optional): top_p 采样参数,用于控制生成文本的多样性。默认为 0.0。 @@ -64,6 +64,6 @@ answer = style_rewrite(appbuilder.Message("文心大模型发布新版本"), sty 如果 streaming 为 False,则直接返回评估结果字符串。 如果 streaming 为 True,则以生成器形式逐个返回评估结果字符串。 * **抛出:** - **ValueError** – 如果缺少参数 ‘query’。 + **ValueError** -- 如果缺少参数 'query'。 #### version *: str* *= 'v1'* diff --git a/docs/sphinx_md/appbuilder.core.components.llms.style_writing.md b/docs/API-Reference/Python/appbuilder.core.components.llms.style_writing.md similarity index 76% rename from docs/sphinx_md/appbuilder.core.components.llms.style_writing.md rename to docs/API-Reference/Python/appbuilder.core.components.llms.style_writing.md index d33f64173..f33e8621c 100644 --- a/docs/sphinx_md/appbuilder.core.components.llms.style_writing.md +++ b/docs/API-Reference/Python/appbuilder.core.components.llms.style_writing.md @@ -35,13 +35,13 @@ answer = style_writing(appbuilder.Message("帮我写一篇关于人体工学椅 使用给定的输入运行模型并返回结果。 * **参数:** - * **(****obj** (*message*) – Message): 输入消息,用于模型的主要输入内容。这是一个必需的参数。 - * **style_query** (*str*) – 风格查询选项,用于指定写作风格。有效的选项包括 ‘B站’, ‘小红书’, ‘通用’。默认值为 ‘通用’。 - * **length** (*int*) – 输出内容的长度。有效的选项包括 100(短),300(中),600(长)。默认值为 100。 - * **stream** (*bool* *,* *optional*) – 指定是否以流式形式返回响应。默认为 False。 - * **temperature** (*float* *,* *optional*) – 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 - * **top_p** (*float* *,* *optional*) – 影响输出文本的多样性,取值越大,生成文本的多样性越强。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 - * **request_id** (*str* *,* *optional*) – 请求ID,用于跟踪和识别请求。 + * **(****obj** (*message*) -- Message): 输入消息,用于模型的主要输入内容。这是一个必需的参数。 + * **style_query** (*str*) -- 风格查询选项,用于指定写作风格。有效的选项包括 'B站', '小红书', '通用'。默认值为 '通用'。 + * **length** (*int*) -- 输出内容的长度。有效的选项包括 100(短),300(中),600(长)。默认值为 100。 + * **stream** (*bool* *,* *optional*) -- 指定是否以流式形式返回响应。默认为 False。 + * **temperature** (*float* *,* *optional*) -- 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 + * **top_p** (*float* *,* *optional*) -- 影响输出文本的多样性,取值越大,生成文本的多样性越强。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 + * **request_id** (*str* *,* *optional*) -- 请求ID,用于跟踪和识别请求。 * **返回:** Message: 模型运行后的输出消息。 * **返回类型:** @@ -52,15 +52,15 @@ answer = style_writing(appbuilder.Message("帮我写一篇关于人体工学椅 对指定的工具进行函数调用评估。 * **参数:** - * **name** (*str*) – 工具名称。 - * **streaming** (*bool* *,* *optional*) – 是否以流的方式返回结果。默认为False。 - * **\*\*kwargs** – 其他参数。 + * **name** (*str*) -- 工具名称。 + * **streaming** (*bool* *,* *optional*) -- 是否以流的方式返回结果。默认为False。 + * **\*\*kwargs** -- 其他参数。 * **返回:** 如果 streaming 为 False,则返回评估结果字符串;如果 streaming 为 True,则返回一个生成器,每次迭代返回评估结果字符串的一部分。 * **返回类型:** str 或 generator * **抛出:** - **ValueError** – 如果未提供必要的参数 ‘query’。 + **ValueError** -- 如果未提供必要的参数 'query'。 #### version *: str* *= 'v1'* @@ -72,21 +72,21 @@ answer = style_writing(appbuilder.Message("帮我写一篇关于人体工学椅 #### message -Message = Field(…) +Message = Field(...) * **Type:** [appbuilder.core.message.Message](appbuilder.core.md#appbuilder.core.message.Message) #### style_query -StyleQueryChoices = Field(…) +StyleQueryChoices = Field(...) * **Type:** appbuilder.core.components.llms.style_writing.base.StyleQueryChoices #### length -LengthChoices = Field(…) +LengthChoices = Field(...) * **Type:** appbuilder.core.components.llms.style_writing.base.LengthChoices diff --git a/docs/sphinx_md/appbuilder.core.components.llms.tag_extraction.md b/docs/API-Reference/Python/appbuilder.core.components.llms.tag_extraction.md similarity index 76% rename from docs/sphinx_md/appbuilder.core.components.llms.tag_extraction.md rename to docs/API-Reference/Python/appbuilder.core.components.llms.tag_extraction.md index 1fbb62f50..c9a4ffc68 100644 --- a/docs/sphinx_md/appbuilder.core.components.llms.tag_extraction.md +++ b/docs/API-Reference/Python/appbuilder.core.components.llms.tag_extraction.md @@ -32,12 +32,12 @@ answer = tag_extraction(appbuilder.Message("从这段文本中抽取关键标签 使用给定的输入运行模型并返回结果。 * **参数:** - * **(****obj** (*message*) – Message, 必选): 输入消息,用于模型的主要输入内容。 - * **stream** (*bool* *,* *可选*) – 指定是否以流式形式返回响应。默认为 False。 - * **temperature** (*float* *,* *可选*) – 模型配置的温度参数,用于调整模型的生成概率。 + * **(****obj** (*message*) -- Message, 必选): 输入消息,用于模型的主要输入内容。 + * **stream** (*bool* *,* *可选*) -- 指定是否以流式形式返回响应。默认为 False。 + * **temperature** (*float* *,* *可选*) -- 模型配置的温度参数,用于调整模型的生成概率。 取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。 默认值为 1e-10。 - * **top_p** (*float* *,* *可选*) – 影响输出文本的多样性,取值越大,生成文本的多样性越强。 + * **top_p** (*float* *,* *可选*) -- 影响输出文本的多样性,取值越大,生成文本的多样性越强。 取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。 默认值为 0。 * **返回:** diff --git a/docs/sphinx_md/appbuilder.core.components.matching.md b/docs/API-Reference/Python/appbuilder.core.components.matching.md similarity index 82% rename from docs/sphinx_md/appbuilder.core.components.matching.md rename to docs/API-Reference/Python/appbuilder.core.components.matching.md index 6ed34d843..ff369bd44 100644 --- a/docs/sphinx_md/appbuilder.core.components.matching.md +++ b/docs/API-Reference/Python/appbuilder.core.components.matching.md @@ -41,9 +41,9 @@ print(contexts_matched.content) 根据给定的查询和上下文,返回匹配的上下文列表。 * **参数:** - * **query** (*Union* *[*[*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**str* *]* *,* *str* *]*) – 查询字符串或Message对象,包含查询字符串。 - * **contexts** (*Union* *[*[*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**List* *[**str* *]* *]* *,* *List* *[**str* *]* *]*) – 上下文字符串列表或Message对象,包含上下文字符串列表。 - * **return_score** (*bool* *,* *optional*) – 是否返回匹配得分。默认为False。 + * **query** (*Union* *[*[*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**str* *]* *,* *str* *]*) -- 查询字符串或Message对象,包含查询字符串。 + * **contexts** (*Union* *[*[*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**List* *[**str* *]* *]* *,* *List* *[**str* *]* *]*) -- 上下文字符串列表或Message对象,包含上下文字符串列表。 + * **return_score** (*bool* *,* *optional*) -- 是否返回匹配得分。默认为False。 * **返回:** 匹配的上下文列表。如果return_score为True,则返回包含得分和上下文的元组列表;否则仅返回上下文列表。 * **返回类型:** @@ -54,8 +54,8 @@ print(contexts_matched.content) 计算query和context的相似度 * **参数:** - * **query_embedding** (*Union* *[*[*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**List* *[**float* *]* *]* *,* *List* *[**float* *]* *]*) – query的embedding,长度为n的数组 - * **context_embeddings** (*Union* *[*[*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**List* *[**List* *[**float* *]* *]* *]* *,* *List* *[**List* *[**float* *]* *]* *]*) – context的embedding,长度为m x n的矩阵,其中m表示候选context的数量 + * **query_embedding** (*Union* *[*[*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**List* *[**float* *]* *]* *,* *List* *[**float* *]* *]*) -- query的embedding,长度为n的数组 + * **context_embeddings** (*Union* *[*[*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**List* *[**List* *[**float* *]* *]* *]* *,* *List* *[**List* *[**float* *]* *]* *]*) -- context的embedding,长度为m x n的矩阵,其中m表示候选context的数量 * **返回:** query和所有候选context的相似度列表 * **返回类型:** diff --git a/docs/sphinx_md/appbuilder.core.components.md b/docs/API-Reference/Python/appbuilder.core.components.md similarity index 94% rename from docs/sphinx_md/appbuilder.core.components.md rename to docs/API-Reference/Python/appbuilder.core.components.md index 55b6048b6..04801185b 100644 --- a/docs/sphinx_md/appbuilder.core.components.md +++ b/docs/API-Reference/Python/appbuilder.core.components.md @@ -69,6 +69,19 @@ * [`TitleSplitter.name`](appbuilder.core.components.doc_splitter.md#appbuilder.core.components.doc_splitter.doc_splitter.TitleSplitter.name) * [`TitleSplitter.run()`](appbuilder.core.components.doc_splitter.md#appbuilder.core.components.doc_splitter.doc_splitter.TitleSplitter.run) * [`TitleSplitter.tool_desc`](appbuilder.core.components.doc_splitter.md#appbuilder.core.components.doc_splitter.doc_splitter.TitleSplitter.tool_desc) +* [appbuilder.core.components.document_understanding package](appbuilder.core.components.document_understanding.md) + * [Submodules](appbuilder.core.components.document_understanding.md#submodules) + * [appbuilder.core.components.document_understanding.component module](appbuilder.core.components.document_understanding.md#module-appbuilder.core.components.document_understanding.component) + * [`DocumentUnderstanding`](appbuilder.core.components.document_understanding.md#appbuilder.core.components.document_understanding.component.DocumentUnderstanding) + * [`DocumentUnderstanding.get_addition_instruction()`](appbuilder.core.components.document_understanding.md#appbuilder.core.components.document_understanding.component.DocumentUnderstanding.get_addition_instruction) + * [`DocumentUnderstanding.get_conversation_id()`](appbuilder.core.components.document_understanding.md#appbuilder.core.components.document_understanding.component.DocumentUnderstanding.get_conversation_id) + * [`DocumentUnderstanding.get_file_id()`](appbuilder.core.components.document_understanding.md#appbuilder.core.components.document_understanding.component.DocumentUnderstanding.get_file_id) + * [`DocumentUnderstanding.manifests`](appbuilder.core.components.document_understanding.md#appbuilder.core.components.document_understanding.component.DocumentUnderstanding.manifests) + * [`DocumentUnderstanding.meta`](appbuilder.core.components.document_understanding.md#appbuilder.core.components.document_understanding.component.DocumentUnderstanding.meta) + * [`DocumentUnderstanding.name`](appbuilder.core.components.document_understanding.md#appbuilder.core.components.document_understanding.component.DocumentUnderstanding.name) + * [`DocumentUnderstanding.run()`](appbuilder.core.components.document_understanding.md#appbuilder.core.components.document_understanding.component.DocumentUnderstanding.run) + * [`DocumentUnderstanding.tool_eval()`](appbuilder.core.components.document_understanding.md#appbuilder.core.components.document_understanding.component.DocumentUnderstanding.tool_eval) + * [`DocumentUnderstanding.version`](appbuilder.core.components.document_understanding.md#appbuilder.core.components.document_understanding.component.DocumentUnderstanding.version) * [appbuilder.core.components.embeddings package](appbuilder.core.components.embeddings.md) * [Submodules](appbuilder.core.components.embeddings.md#submodules) * [appbuilder.core.components.embeddings.component module](appbuilder.core.components.embeddings.md#module-appbuilder.core.components.embeddings.component) @@ -365,6 +378,15 @@ * [`Translation.run()`](appbuilder.core.components.translate.md#appbuilder.core.components.translate.component.Translation.run) * [`Translation.tool_eval()`](appbuilder.core.components.translate.md#appbuilder.core.components.translate.component.Translation.tool_eval) * [`Translation.version`](appbuilder.core.components.translate.md#appbuilder.core.components.translate.component.Translation.version) +* [appbuilder.core.components.tree_mind package](appbuilder.core.components.tree_mind.md) + * [Submodules](appbuilder.core.components.tree_mind.md#submodules) + * [appbuilder.core.components.tree_mind.component module](appbuilder.core.components.tree_mind.md#module-appbuilder.core.components.tree_mind.component) + * [`TreeMind`](appbuilder.core.components.tree_mind.md#appbuilder.core.components.tree_mind.component.TreeMind) + * [`TreeMind.manifests`](appbuilder.core.components.tree_mind.md#appbuilder.core.components.tree_mind.component.TreeMind.manifests) + * [`TreeMind.name`](appbuilder.core.components.tree_mind.md#appbuilder.core.components.tree_mind.component.TreeMind.name) + * [`TreeMind.run()`](appbuilder.core.components.tree_mind.md#appbuilder.core.components.tree_mind.component.TreeMind.run) + * [`TreeMind.tool_eval()`](appbuilder.core.components.tree_mind.md#appbuilder.core.components.tree_mind.component.TreeMind.tool_eval) + * [`TreeMind.version`](appbuilder.core.components.tree_mind.md#appbuilder.core.components.tree_mind.component.TreeMind.version) * [appbuilder.core.components.tts package](appbuilder.core.components.tts.md) * [Submodules](appbuilder.core.components.tts.md#submodules) * [appbuilder.core.components.tts.component module](appbuilder.core.components.tts.md#module-appbuilder.core.components.tts.component) diff --git a/docs/sphinx_md/appbuilder.core.components.mix_card_ocr.md b/docs/API-Reference/Python/appbuilder.core.components.mix_card_ocr.md similarity index 73% rename from docs/sphinx_md/appbuilder.core.components.mix_card_ocr.md rename to docs/API-Reference/Python/appbuilder.core.components.mix_card_ocr.md index f5397f15a..df7942037 100644 --- a/docs/sphinx_md/appbuilder.core.components.mix_card_ocr.md +++ b/docs/API-Reference/Python/appbuilder.core.components.mix_card_ocr.md @@ -42,10 +42,10 @@ print(out.content) 执行身份证识别操作 * **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) – 包含待识别图片或图片下载URL的Message对象. - 示例: Message(content={“raw_image”: b”…”}) 或 Message(content={“url”: “[https://image/download/url](https://image/download/url)”}). - * **timeout** (*float* *,* *可选*) – HTTP请求的超时时间,默认为None. - * **retry** (*int* *,* *可选*) – HTTP请求的重试次数,默认为0. + * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 包含待识别图片或图片下载URL的Message对象. + 示例: Message(content={"raw_image": b"..."}) 或 Message(content={"url": "[https://image/download/url](https://image/download/url)"}). + * **timeout** (*float* *,* *可选*) -- HTTP请求的超时时间,默认为None. + * **retry** (*int* *,* *可选*) -- HTTP请求的重试次数,默认为0. * **返回:** 包含身份证识别结果的Message对象. * **返回类型:** @@ -56,16 +56,16 @@ print(out.content) 对指定文件进行OCR识别。 * **参数:** - * **name** (*str*) – API名称。 - * **streaming** (*bool*) – 是否流式输出。如果为True,则逐个返回识别结果;如果为False,则一次性返回所有识别结果。 - * **\*\*kwargs** – 其他参数。 + * **name** (*str*) -- API名称。 + * **streaming** (*bool*) -- 是否流式输出。如果为True,则逐个返回识别结果;如果为False,则一次性返回所有识别结果。 + * **\*\*kwargs** -- 其他参数。 * **返回:** 如果streaming为False,则返回包含所有识别结果的JSON字符串。 如果streaming为True,则逐个返回包含识别结果的字典,每个字典包含以下字段: - > type (str): 消息类型,固定为”text”。 + > type (str): 消息类型,固定为"text"。 > text (str): 识别结果的JSON字符串。 - > visible_scope (str): 消息可见范围,可以是”llm”或”user”。 + > visible_scope (str): 消息可见范围,可以是"llm"或"user"。 * **抛出:** - **InvalidRequestArgumentError** – 如果请求格式错误,即文件URL不存在时抛出。 + **InvalidRequestArgumentError** -- 如果请求格式错误,即文件URL不存在时抛出。 #### version *= 'v1'* diff --git a/docs/sphinx_md/appbuilder.core.components.object_recognize.md b/docs/API-Reference/Python/appbuilder.core.components.object_recognize.md similarity index 70% rename from docs/sphinx_md/appbuilder.core.components.object_recognize.md rename to docs/API-Reference/Python/appbuilder.core.components.object_recognize.md index 8cae40ecb..ed5f25984 100644 --- a/docs/sphinx_md/appbuilder.core.components.object_recognize.md +++ b/docs/API-Reference/Python/appbuilder.core.components.object_recognize.md @@ -35,15 +35,15 @@ print(out.content) 通用物体识别 * **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) – 输入图片或图片url下载地址用于执行识别操作。 - 例如: Message(content={“raw_image”: b”…”}) 或 Message(content={“url”: “[https://image/download/url](https://image/download/url)”})。 - * **timeout** (*float* *,* *optional*) – HTTP超时时间,默认为None。 - * **retry** (*int* *,* *optional*) – HTTP重试次数,默认为0。 + * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入图片或图片url下载地址用于执行识别操作。 + 例如: Message(content={"raw_image": b"..."}) 或 Message(content={"url": "[https://image/download/url](https://image/download/url)"})。 + * **timeout** (*float* *,* *optional*) -- HTTP超时时间,默认为None。 + * **retry** (*int* *,* *optional*) -- HTTP重试次数,默认为0。 * **返回:** 模型识别结果。 - : 例如: Message(content={“result”:[{“keyword”:”苹果”, - : ”score”:0.94553,”root”:”植物-蔷薇科”},{“keyword”:”姬娜果”,”score”:0.730442,”root”:”植物-其它”}, - {“keyword”:”红富士”,”score”:0.505194,”root”:”植物-其它”}]}) + : 例如: Message(content={"result":[{"keyword":"苹果", + : "score":0.94553,"root":"植物-蔷薇科"},{"keyword":"姬娜果","score":0.730442,"root":"植物-其它"}, + {"keyword":"红富士","score":0.505194,"root":"植物-其它"}]}) * **返回类型:** [Message](appbuilder.core.md#appbuilder.core.message.Message) @@ -52,9 +52,9 @@ print(out.content) 评估并识别传入图像中的物体或场景。 * **参数:** - * **name** (*str*) – 调用此方法的对象名称。 - * **streaming** (*bool*) – 是否以流式方式返回结果。如果是True,则以生成器形式返回结果;如果是False,则直接返回字符串形式的识别结果。 - * **\*\*kwargs** – 任意关键字参数,支持以下参数: + * **name** (*str*) -- 调用此方法的对象名称。 + * **streaming** (*bool*) -- 是否以流式方式返回结果。如果是True,则以生成器形式返回结果;如果是False,则直接返回字符串形式的识别结果。 + * **\*\*kwargs** -- 任意关键字参数,支持以下参数: traceid (str, optional): 请求的追踪ID,用于追踪请求处理流程。默认为None。 img_url (str, optional): 待识别图像的URL地址。默认为None,如果未指定,则尝试从file_urls和img_name参数中获取图像路径。 file_urls (dict, optional): 包含文件名和对应URL的字典。默认为空字典。 @@ -62,12 +62,12 @@ print(out.content) score_threshold (float, optional): 置信度阈值,低于此阈值的识别结果将被忽略。默认为0.5。 * **返回:** 如果streaming为True,则返回一个生成器,生成器中的元素为包含识别结果的字典,字典包含以下键: - : type (str): 结果类型,固定为”text”。 + : type (str): 结果类型,固定为"text"。 text (str): 识别结果的JSON字符串表示。 - visible_scope (str): 结果的可见范围,’llm’表示仅对LLM可见,’user’表示对用户可见。 + visible_scope (str): 结果的可见范围,'llm'表示仅对LLM可见,'user'表示对用户可见。 如果streaming为False,则直接返回识别结果的JSON字符串表示。 * **抛出:** - **InvalidRequestArgumentError** – 如果请求格式错误(如未设置文件名或文件URL不存在),则抛出此异常。 + **InvalidRequestArgumentError** -- 如果请求格式错误(如未设置文件名或文件URL不存在),则抛出此异常。 #### version *= 'v1'* diff --git a/docs/sphinx_md/appbuilder.core.components.plant_recognize.md b/docs/API-Reference/Python/appbuilder.core.components.plant_recognize.md similarity index 81% rename from docs/sphinx_md/appbuilder.core.components.plant_recognize.md rename to docs/API-Reference/Python/appbuilder.core.components.plant_recognize.md index 93b8f33b1..77333ed07 100644 --- a/docs/sphinx_md/appbuilder.core.components.plant_recognize.md +++ b/docs/API-Reference/Python/appbuilder.core.components.plant_recognize.md @@ -45,10 +45,10 @@ print(out.content) 输入图片并识别其中的植物 * **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) – 输入图片或图片url下载地址用于执行识别操作. 举例: Message(content={“raw_image”: b”…”}) - * **Message****(****content={"url"** ( *或*) – “[https://image/download/uel](https://image/download/uel)”}). - * **timeout** (*float* *,* *optional*) – HTTP超时时间,默认为None - * **retry** (*int* *,* *optional*) – HTTP重试次数,默认为0 + * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入图片或图片url下载地址用于执行识别操作. 举例: Message(content={"raw_image": b"..."}) + * **Message****(****content={"url"** ( *或*) -- "[https://image/download/uel](https://image/download/uel)"}). + * **timeout** (*float* *,* *optional*) -- HTTP超时时间,默认为None + * **retry** (*int* *,* *optional*) -- HTTP重试次数,默认为0 * **返回:** 模型识别结果 * **返回类型:** @@ -59,10 +59,10 @@ print(out.content) 用于工具的执行,通过调用底层接口进行植物识别 * **参数:** - * **name** (*str*) – 工具名 - * **streaming** (*bool*) – 是否流式返回 - * **origin_query** (*str*) – 用户原始query - * **\*\*kwargs** – 工具调用的额外关键字参数 + * **name** (*str*) -- 工具名 + * **streaming** (*bool*) -- 是否流式返回 + * **origin_query** (*str*) -- 用户原始query + * **\*\*kwargs** -- 工具调用的额外关键字参数 * **返回:** 植物识别结果,包括识别出的植物类别和相应的置信度信息 * **返回类型:** diff --git a/docs/sphinx_md/appbuilder.core.components.ppt_generation_from_file.md b/docs/API-Reference/Python/appbuilder.core.components.ppt_generation_from_file.md similarity index 68% rename from docs/sphinx_md/appbuilder.core.components.ppt_generation_from_file.md rename to docs/API-Reference/Python/appbuilder.core.components.ppt_generation_from_file.md index 736863a2c..5b0b3cec3 100644 --- a/docs/sphinx_md/appbuilder.core.components.ppt_generation_from_file.md +++ b/docs/API-Reference/Python/appbuilder.core.components.ppt_generation_from_file.md @@ -31,14 +31,14 @@ print(answer.content) 获取PPT下载链接 * **参数:** - * **job_id** (*str*) – 任务ID - * **timeout** (*float* *,* *optional*) – 请求超时时间,默认为None。 + * **job_id** (*str*) -- 任务ID + * **timeout** (*float* *,* *optional*) -- 请求超时时间,默认为None。 * **返回:** PPT下载链接 * **返回类型:** str * **抛出:** - **Exception** – PPT生成请求错误 + **Exception** -- PPT生成请求错误 #### get_ppt_download_link_url *= '/ppt/text2ppt/apps/ppt-download'* @@ -47,16 +47,16 @@ print(answer.content) 轮询查看PPT生成状态 * **参数:** - * **job_id** (*str*) – PPT生成任务的唯一标识符 - * **request_times** (*int* *,* *optional*) – 轮询请求次数,默认为60次。 - * **request_interval** (*int* *,* *optional*) – 每次轮询请求的间隔时间(秒),默认为5秒。 - * **timeout** (*float* *,* *optional*) – 请求的超时时间(秒),默认为None,即无超时限制。 + * **job_id** (*str*) -- PPT生成任务的唯一标识符 + * **request_times** (*int* *,* *optional*) -- 轮询请求次数,默认为60次。 + * **request_interval** (*int* *,* *optional*) -- 每次轮询请求的间隔时间(秒),默认为5秒。 + * **timeout** (*float* *,* *optional*) -- 请求的超时时间(秒),默认为None,即无超时限制。 * **返回:** PPT生成状态码,1表示正在生成,2表示生成完成,3表示生成失败。 * **返回类型:** int * **抛出:** - **Exception** – 如果PPT生成状态码不为2(生成完成),则抛出异常。 + **Exception** -- 如果PPT生成状态码不为2(生成完成),则抛出异常。 #### get_ppt_generation_status_url *= '/ppt/text2ppt/apps/ppt-result'* @@ -73,14 +73,14 @@ print(answer.content) 创建PPT生成任务 * **参数:** - * **post_data** (*dict*) – 包含PPT生成任务所需数据的字典 - * **timeout** (*float* *,* *optional*) – 请求超时时间,默认为None,表示不设置超时时间。 + * **post_data** (*dict*) -- 包含PPT生成任务所需数据的字典 + * **timeout** (*float* *,* *optional*) -- 请求超时时间,默认为None,表示不设置超时时间。 * **返回:** PPT生成任务的Job ID * **返回类型:** str * **抛出:** - **Exception** – 如果PPT生成任务请求失败,抛出异常 + **Exception** -- 如果PPT生成任务请求失败,抛出异常 #### ppt_generation_url *= '/ppt/text2ppt/apps/ppt-create-file'* @@ -89,9 +89,9 @@ print(answer.content) 使用给定的输入运行模型并返回结果。 * **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) – 输入消息,用于传入请求参数。 - * **poll_request_times** (*int*) – 轮询请求结果次数。 - * **poll_request_interval** (*int*) – 轮询请求的间隔时间(秒)。 + * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入消息,用于传入请求参数。 + * **poll_request_times** (*int*) -- 轮询请求结果次数。 + * **poll_request_interval** (*int*) -- 轮询请求的间隔时间(秒)。 * **返回:** 模型运行后的输出消息。 * **返回类型:** @@ -102,13 +102,13 @@ print(answer.content) 用于执行function call的功能。 * **参数:** - * **stream** (*bool* *,* *optional*) – 是否以生成器的方式返回结果,默认为False。 - * **\*\*kwargs** – 任意关键字参数,目前只支持’file_url’。 + * **stream** (*bool* *,* *optional*) -- 是否以生成器的方式返回结果,默认为False。 + * **\*\*kwargs** -- 任意关键字参数,目前只支持'file_url'。 * **返回:** 如果stream为False,则返回一个字符串,表示ppt下载链接。 如果stream为True,则返回一个生成器,生成器产生一个字符串,表示ppt下载链接。 * **抛出:** - **ValueError** – 如果’file_url’为空,则抛出异常。 + **ValueError** -- 如果'file_url'为空,则抛出异常。 #### uniform_prefix *= '/api/v1/component/component'* diff --git a/docs/sphinx_md/appbuilder.core.components.ppt_generation_from_instruction.md b/docs/API-Reference/Python/appbuilder.core.components.ppt_generation_from_instruction.md similarity index 67% rename from docs/sphinx_md/appbuilder.core.components.ppt_generation_from_instruction.md rename to docs/API-Reference/Python/appbuilder.core.components.ppt_generation_from_instruction.md index 5bfcdea93..b2d888aa6 100644 --- a/docs/sphinx_md/appbuilder.core.components.ppt_generation_from_instruction.md +++ b/docs/API-Reference/Python/appbuilder.core.components.ppt_generation_from_instruction.md @@ -34,14 +34,14 @@ print(answer.content) 获取PPT下载链接 * **参数:** - * **job_id** (*str*) – 作业ID - * **timeout** (*float* *,* *optional*) – 请求超时时间,默认为None。 + * **job_id** (*str*) -- 作业ID + * **timeout** (*float* *,* *optional*) -- 请求超时时间,默认为None。 * **返回:** PPT下载链接 * **返回类型:** str * **抛出:** - **Exception** – 当PPT生成请求失败时抛出异常 + **Exception** -- 当PPT生成请求失败时抛出异常 #### get_ppt_download_link_url *= '/ppt/text2ppt/apps/ppt-download'* @@ -50,10 +50,10 @@ print(answer.content) 轮询查看PPT生成状态 * **参数:** - * **job_id** (*str*) – PPT生成任务的唯一标识符 - * **request_times** (*int* *,* *optional*) – 轮询请求的次数,默认为60次。 - * **request_interval** (*int* *,* *optional*) – 每次轮询请求之间的间隔时间(秒),默认为5秒。 - * **timeout** (*float* *,* *optional*) – 请求的超时时间(秒)。如果未设置,则使用http_client的默认超时时间。 + * **job_id** (*str*) -- PPT生成任务的唯一标识符 + * **request_times** (*int* *,* *optional*) -- 轮询请求的次数,默认为60次。 + * **request_interval** (*int* *,* *optional*) -- 每次轮询请求之间的间隔时间(秒),默认为5秒。 + * **timeout** (*float* *,* *optional*) -- 请求的超时时间(秒)。如果未设置,则使用http_client的默认超时时间。 * **返回:** PPT生成状态码。 : - 1:正在生成 @@ -62,7 +62,7 @@ print(answer.content) * **返回类型:** int * **抛出:** - **Exception** – PPT生成过程中出现异常时抛出。 + **Exception** -- PPT生成过程中出现异常时抛出。 #### get_ppt_generation_status_url *= '/ppt/text2ppt/apps/ppt-result'* @@ -79,14 +79,14 @@ print(answer.content) 创建PPT生成任务 * **参数:** - * **post_data** (*dict*) – 请求数据 - * **timeout** (*float* *,* *optional*) – 请求超时时间,默认为None. + * **post_data** (*dict*) -- 请求数据 + * **timeout** (*float* *,* *optional*) -- 请求超时时间,默认为None. * **返回:** 任务ID * **返回类型:** str * **抛出:** - **Exception** – PPT生成请求失败 + **Exception** -- PPT生成请求失败 #### ppt_generation_url *= '/ppt/text2ppt/apps/ppt-create'* @@ -95,9 +95,9 @@ print(answer.content) 使用给定的输入运行模型并返回结果。 * **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) – 输入消息,用于传入请求参数。 - * **poll_request_times** (*int* *,* *optional*) – 轮询请求结果次数,默认为60。 - * **poll_request_interval** (*int* *,* *optional*) – 轮询请求的间隔时间(秒),默认为5。 + * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入消息,用于传入请求参数。 + * **poll_request_times** (*int* *,* *optional*) -- 轮询请求结果次数,默认为60。 + * **poll_request_interval** (*int* *,* *optional*) -- 轮询请求的间隔时间(秒),默认为5。 * **返回:** 模型运行后的输出消息,包含PPT下载链接。 * **返回类型:** @@ -108,12 +108,12 @@ print(answer.content) 评估给定的文本内容。 * **参数:** - * **stream** (*bool* *,* *optional*) – 是否以生成器形式返回结果,默认为False。如果为True,则逐个生成下载链接;如果为False,则直接返回下载链接。 - * **\*\*kwargs** – 关键字参数,可以传递其他参数,但当前只使用 ‘text’ 参数。 + * **stream** (*bool* *,* *optional*) -- 是否以生成器形式返回结果,默认为False。如果为True,则逐个生成下载链接;如果为False,则直接返回下载链接。 + * **\*\*kwargs** -- 关键字参数,可以传递其他参数,但当前只使用 'text' 参数。 * **返回:** 如果 stream 为 False,则返回一个包含下载链接的字符串;如果 stream 为 True,则逐个生成下载链接。 * **抛出:** - **ValueError** – 如果 ‘text’ 参数为空,则抛出此异常。 + **ValueError** -- 如果 'text' 参数为空,则抛出此异常。 #### uniform_prefix *= '/api/v1/component/component'* diff --git a/docs/sphinx_md/appbuilder.core.components.ppt_generation_from_paper.md b/docs/API-Reference/Python/appbuilder.core.components.ppt_generation_from_paper.md similarity index 67% rename from docs/sphinx_md/appbuilder.core.components.ppt_generation_from_paper.md rename to docs/API-Reference/Python/appbuilder.core.components.ppt_generation_from_paper.md index 4ef1c6a6b..cd355a311 100644 --- a/docs/sphinx_md/appbuilder.core.components.ppt_generation_from_paper.md +++ b/docs/API-Reference/Python/appbuilder.core.components.ppt_generation_from_paper.md @@ -31,14 +31,14 @@ print(answer.content) 获取PPT下载链接 * **参数:** - * **job_id** (*str*) – 任务ID - * **timeout** (*float* *,* *optional*) – 请求超时时间,默认为None. + * **job_id** (*str*) -- 任务ID + * **timeout** (*float* *,* *optional*) -- 请求超时时间,默认为None. * **返回:** PPT下载链接 * **返回类型:** str * **抛出:** - **Exception** – PPT生成请求失败 + **Exception** -- PPT生成请求失败 #### get_ppt_download_link_url *= '/ppt/text2ppt/apps/ppt-download'* @@ -47,10 +47,10 @@ print(answer.content) 轮询查看PPT生成状态 * **参数:** - * **job_id** (*str*) – 任务ID - * **request_times** (*int* *,* *optional*) – 请求次数,默认为60次。 - * **request_interval** (*int* *,* *optional*) – 请求间隔时间,默认为5秒。 - * **timeout** (*float* *,* *optional*) – 请求超时时间,默认为None,即不设置超时时间。 + * **job_id** (*str*) -- 任务ID + * **request_times** (*int* *,* *optional*) -- 请求次数,默认为60次。 + * **request_interval** (*int* *,* *optional*) -- 请求间隔时间,默认为5秒。 + * **timeout** (*float* *,* *optional*) -- 请求超时时间,默认为None,即不设置超时时间。 * **返回:** PPT生成状态码。 : - 1: PPT正在生成中 @@ -59,7 +59,7 @@ print(answer.content) * **返回类型:** int * **抛出:** - **Exception** – PPT生成失败或请求失败时抛出异常。 + **Exception** -- PPT生成失败或请求失败时抛出异常。 #### get_ppt_generation_status_url *= '/ppt/text2ppt/apps/ppt-result'* @@ -76,14 +76,14 @@ print(answer.content) 创建PPT生成任务 * **参数:** - * **post_data** (*dict*) – 发送的POST请求体数据 - * **timeout** (*float* *,* *optional*) – 请求超时时间,默认为None。 + * **post_data** (*dict*) -- 发送的POST请求体数据 + * **timeout** (*float* *,* *optional*) -- 请求超时时间,默认为None。 * **返回:** 返回的任务ID * **返回类型:** str * **抛出:** - **Exception** – 如果PPT生成请求失败,抛出异常 + **Exception** -- 如果PPT生成请求失败,抛出异常 #### ppt_generation_url *= '/ppt/text2ppt/apps/ppt-create-thesis'* @@ -92,28 +92,28 @@ print(answer.content) 使用给定的输入运行模型并返回结果。 * **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) – 输入消息,用于传入请求参数。 - * **poll_request_times** (*int*) – 轮询请求结果次数,默认为60次。 - * **poll_request_interval** (*int*) – 轮询请求的间隔时间(秒),默认为5秒。 + * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入消息,用于传入请求参数。 + * **poll_request_times** (*int*) -- 轮询请求结果次数,默认为60次。 + * **poll_request_interval** (*int*) -- 轮询请求的间隔时间(秒),默认为5秒。 * **返回:** 模型运行后的输出消息,包含PPT下载链接。 * **返回类型:** [Message](appbuilder.core.md#appbuilder.core.message.Message) * **抛出:** - **Exception** – 当输入参数中缺少必要的键时,抛出异常。 + **Exception** -- 当输入参数中缺少必要的键时,抛出异常。 #### tool_eval(stream: bool = False, \*\*kwargs) 使用指定的file_key来评估并获取相应的结果。 * **参数:** - * **stream** (*bool* *,* *optional*) – 是否以生成器的方式逐项返回结果,默认为False。 - * **\*\*kwargs** – 关键字参数,用于传递其他参数,目前仅支持file_key。 + * **stream** (*bool* *,* *optional*) -- 是否以生成器的方式逐项返回结果,默认为False。 + * **\*\*kwargs** -- 关键字参数,用于传递其他参数,目前仅支持file_key。 * **返回:** 如果stream为False,则直接返回结果。 如果stream为True,则逐个返回结果。 * **抛出:** - **ValueError** – 如果参数file_key为空,则抛出异常。 + **ValueError** -- 如果参数file_key为空,则抛出异常。 #### uniform_prefix *= '/api/v1/component/component'* diff --git a/docs/sphinx_md/appbuilder.core.components.qrcode_ocr.md b/docs/API-Reference/Python/appbuilder.core.components.qrcode_ocr.md similarity index 62% rename from docs/sphinx_md/appbuilder.core.components.qrcode_ocr.md rename to docs/API-Reference/Python/appbuilder.core.components.qrcode_ocr.md index 723fd30fd..78c18175f 100644 --- a/docs/sphinx_md/appbuilder.core.components.qrcode_ocr.md +++ b/docs/API-Reference/Python/appbuilder.core.components.qrcode_ocr.md @@ -34,40 +34,40 @@ print(out.content) 执行二维码识别操作。 * **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) – 输入的图片或图片URL下载地址,用于执行识别操作。例如: - Message(content={“raw_image”: b”…”, “location”: “”}) 或 - Message(content={“url”: “[https://image/download/url](https://image/download/url)”})。 - * **location** (*str* *,* *可选*) – 是否需要返回二维码位置信息,默认为 “true”。 - * **timeout** (*float* *,* *可选*) – HTTP请求的超时时间。 - * **retry** (*int* *,* *可选*) – HTTP请求的重试次数。 + * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入的图片或图片URL下载地址,用于执行识别操作。例如: + Message(content={"raw_image": b"...", "location": ""}) 或 + Message(content={"url": "[https://image/download/url](https://image/download/url)"})。 + * **location** (*str* *,* *可选*) -- 是否需要返回二维码位置信息,默认为 "true"。 + * **timeout** (*float* *,* *可选*) -- HTTP请求的超时时间。 + * **retry** (*int* *,* *可选*) -- HTTP请求的重试次数。 * **返回:** 识别结果,包含识别到的二维码信息。例如: - : Message(name=msg, content={‘codes_result’: [{‘type’: ‘QR_CODE’, ‘text’: [’[http://weixin.qq.com/r/cS7M1PHE5qyZrbW393tj](http://weixin.qq.com/r/cS7M1PHE5qyZrbW393tj)’], - : ’location’: {‘top’: 63, ‘left’: 950, ‘width’: 220, ‘height’: 211}}, …]}, mtype=dict) + : Message(name=msg, content={'codes_result': [{'type': 'QR_CODE', 'text': ['[http://weixin.qq.com/r/cS7M1PHE5qyZrbW393tj](http://weixin.qq.com/r/cS7M1PHE5qyZrbW393tj)'], + : 'location': {'top': 63, 'left': 950, 'width': 220, 'height': 211}}, ...]}, mtype=dict) * **返回类型:** [Message](appbuilder.core.md#appbuilder.core.message.Message) * **抛出:** - **InvalidRequestArgumentError** – 如果 location 参数非法,将抛出该异常。 + **InvalidRequestArgumentError** -- 如果 location 参数非法,将抛出该异常。 #### tool_eval(name: str, streaming: bool, \*\*kwargs) 评估工具函数 * **参数:** - * **name** (*str*) – 工具名称 - * **streaming** (*bool*) – 是否流式输出 - * **\*\*kwargs** – 其他关键字参数 + * **name** (*str*) -- 工具名称 + * **streaming** (*bool*) -- 是否流式输出 + * **\*\*kwargs** -- 其他关键字参数 * **关键字参数:** - * **traceid** (*str*) – 请求的traceid - * **file_names** (*List* *[**str* *]*) – 文件名列表 - * **locations** (*str*) – 是否需要获取位置信息,可选值为’true’或’false’,默认为’false’ - * **file_urls** (*Dict* *[**str* *,* *str* *]*) – 文件名到文件URL的映射 + * **traceid** (*str*) -- 请求的traceid + * **file_names** (*List* *[**str* *]*) -- 文件名列表 + * **locations** (*str*) -- 是否需要获取位置信息,可选值为'true'或'false',默认为'false' + * **file_urls** (*Dict* *[**str* *,* *str* *]*) -- 文件名到文件URL的映射 * **返回:** 如果streaming为True,则返回一个生成器,生成两个字典,分别代表LLM和用户可见的内容; : 如果streaming为False,则返回一个JSON字符串,包含评估结果 * **返回类型:** Union[str, Generator[Dict[str, Any], None, None]] * **抛出:** - **InvalidRequestArgumentError** – 如果请求格式错误,或者位置信息不合法,则抛出该异常 + **InvalidRequestArgumentError** -- 如果请求格式错误,或者位置信息不合法,则抛出该异常 #### version *= 'v1'* diff --git a/docs/sphinx_md/appbuilder.core.components.rag_with_baidu_search.md b/docs/API-Reference/Python/appbuilder.core.components.rag_with_baidu_search.md similarity index 50% rename from docs/sphinx_md/appbuilder.core.components.rag_with_baidu_search.md rename to docs/API-Reference/Python/appbuilder.core.components.rag_with_baidu_search.md index 790c6bbe2..52c3092f3 100644 --- a/docs/sphinx_md/appbuilder.core.components.rag_with_baidu_search.md +++ b/docs/API-Reference/Python/appbuilder.core.components.rag_with_baidu_search.md @@ -17,21 +17,21 @@ 执行模型推理 * **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) – 用户输入的消息对象 - * **instruction** (*Instruction* *,* *optional*) – 用户提供的指令信息,默认为None。如果未提供,则使用默认的指令信息。 - * **reject** (*bool* *,* *optional*) – 是否拒绝执行,默认为None。如果未提供,则使用默认设置。 - * **clarify** (*bool* *,* *optional*) – 是否需要澄清,默认为None。如果未提供,则使用默认设置。 - * **highlight** (*bool* *,* *optional*) – 是否高亮显示,默认为None。如果未提供,则使用默认设置。 - * **friendly** (*bool* *,* *optional*) – 是否以友好的方式回答,默认为None。如果未提供,则使用默认设置。 - * **cite** (*bool* *,* *optional*) – 是否引用原始信息,默认为None。如果未提供,则使用默认设置。 - * **stream** (*bool* *,* *optional*) – 是否以流式方式返回结果,默认为False。 - * **temperature** (*float* *,* *optional*) – 温度参数,用于控制生成文本的多样性,默认为1e-10。 - * **top_p** (*float* *,* *optional*) – 截断概率阈值,用于控制生成文本的多样性,默认为1e-10。 + * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 用户输入的消息对象 + * **instruction** (*Instruction* *,* *optional*) -- 用户提供的指令信息,默认为None。如果未提供,则使用默认的指令信息。 + * **reject** (*bool* *,* *optional*) -- 是否拒绝执行,默认为None。如果未提供,则使用默认设置。 + * **clarify** (*bool* *,* *optional*) -- 是否需要澄清,默认为None。如果未提供,则使用默认设置。 + * **highlight** (*bool* *,* *optional*) -- 是否高亮显示,默认为None。如果未提供,则使用默认设置。 + * **friendly** (*bool* *,* *optional*) -- 是否以友好的方式回答,默认为None。如果未提供,则使用默认设置。 + * **cite** (*bool* *,* *optional*) -- 是否引用原始信息,默认为None。如果未提供,则使用默认设置。 + * **stream** (*bool* *,* *optional*) -- 是否以流式方式返回结果,默认为False。 + * **temperature** (*float* *,* *optional*) -- 温度参数,用于控制生成文本的多样性,默认为1e-10。 + * **top_p** (*float* *,* *optional*) -- 截断概率阈值,用于控制生成文本的多样性,默认为1e-10。 * **返回:** 推理结果消息对象 * **返回类型:** [Message](appbuilder.core.md#appbuilder.core.message.Message) * **抛出:** - **AppBuilderServerException** – 如果输入消息内容过长(超过72个字符)或推理结果中存在错误,则抛出异常。 + **AppBuilderServerException** -- 如果输入消息内容过长(超过72个字符)或推理结果中存在错误,则抛出异常。 #### version *: str* *= 'v1'* diff --git a/docs/sphinx_md/appbuilder.core.components.rag_with_baidu_search_pro.md b/docs/API-Reference/Python/appbuilder.core.components.rag_with_baidu_search_pro.md similarity index 70% rename from docs/sphinx_md/appbuilder.core.components.rag_with_baidu_search_pro.md rename to docs/API-Reference/Python/appbuilder.core.components.rag_with_baidu_search_pro.md index 5d4e818d3..437bc3f90 100644 --- a/docs/sphinx_md/appbuilder.core.components.rag_with_baidu_search_pro.md +++ b/docs/API-Reference/Python/appbuilder.core.components.rag_with_baidu_search_pro.md @@ -19,28 +19,28 @@ RagWithBaiduSearchPro 组件 执行模型推理。 * **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) – 待处理的信息对象。 - * **stream** (*bool* *,* *optional*) – 是否以流的形式接收响应数据。默认为False。 - * **instruction** (*Instruction* *,* *optional*) – 指令信息对象。默认为None。 - * **model** (*str* *,* *optional*) – 模型名称。默认为None,表示使用当前实例的模型。 - * **temperature** (*float* *,* *optional*) – 温度参数,控制生成文本的随机性。默认为1e-10。 - * **top_p** (*float* *,* *optional*) – 累积概率阈值,用于控制生成文本的多样性。默认为1e-10。 - * **search_top_k** (*int* *,* *optional*) – 搜索候选结果的数量。默认为4。 - * **hide_corner_markers** (*bool* *,* *optional*) – 是否隐藏响应中的边界标记。默认为True。 + * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 待处理的信息对象。 + * **stream** (*bool* *,* *optional*) -- 是否以流的形式接收响应数据。默认为False。 + * **instruction** (*Instruction* *,* *optional*) -- 指令信息对象。默认为None。 + * **model** (*str* *,* *optional*) -- 模型名称。默认为None,表示使用当前实例的模型。 + * **temperature** (*float* *,* *optional*) -- 温度参数,控制生成文本的随机性。默认为1e-10。 + * **top_p** (*float* *,* *optional*) -- 累积概率阈值,用于控制生成文本的多样性。默认为1e-10。 + * **search_top_k** (*int* *,* *optional*) -- 搜索候选结果的数量。默认为4。 + * **hide_corner_markers** (*bool* *,* *optional*) -- 是否隐藏响应中的边界标记。默认为True。 * **返回:** 处理后的信息对象。 * **返回类型:** [Message](appbuilder.core.md#appbuilder.core.message.Message) * **抛出:** - **AppBuilderServerException** – 如果输入信息或指令过长,将抛出此异常。 + **AppBuilderServerException** -- 如果输入信息或指令过长,将抛出此异常。 #### set_secret_key_and_gateway(\*\*kwargs) 设置密钥和网关地址。 * **参数:** - * **secret_key** (*Optional* *[**str* *]* *,* *optional*) – 密钥,默认为None。如果未指定,则使用实例当前的密钥。 - * **gateway** (*str* *,* *optional*) – 网关地址,默认为空字符串。如果未指定,则使用实例当前的网关地址。 + * **secret_key** (*Optional* *[**str* *]* *,* *optional*) -- 密钥,默认为None。如果未指定,则使用实例当前的密钥。 + * **gateway** (*str* *,* *optional*) -- 网关地址,默认为空字符串。如果未指定,则使用实例当前的网关地址。 * **返回:** None @@ -53,7 +53,7 @@ RagWithBaiduSearchPro 组件 RagWithBaiduSearchPro 的参数 * **参数:** - **query** (*str*) – 用户的 query 输入 + **query** (*str*) -- 用户的 query 输入 #### model_computed_fields *: ClassVar[dict[str, ComputedFieldInfo]]* *= {}* @@ -143,7 +143,7 @@ A dictionary of computed field names and their corresponding ComputedFieldInfo o Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict]. -#### model_fields *: ClassVar[dict[str, FieldInfo]]* *= {'hide_corner_markers': FieldInfo(annotation=bool, required=False, default=True), 'instruction': FieldInfo(annotation=str, required=True), 'message': FieldInfo(annotation=object, required=True), 'model': FieldInfo(annotation=Union[str, NoneType], required=False), 'search_top_k': FieldInfo(annotation=int, required=False, default=4, description='search_top_k必须是大于等于1的整数', metadata=[None, Interval(gt=None, ge=1, lt=None, le=None), None]), 'stream': FieldInfo(annotation=bool, required=False, default=False), 'temperature': FieldInfo(annotation=float, required=False, default=1e-10, description='temperature范围在0到1之间', metadata=[None, Interval(gt=None, ge=0, lt=None, le=1), None, None]), 'top_p': FieldInfo(annotation=float, required=False, default=1e-10, description='top_p范围在0到1之间', metadata=[None, Interval(gt=None, ge=0, lt=None, le=1), None, None])}* +#### model_fields *: ClassVar[dict[str, FieldInfo]]* *= {'hide_corner_markers': FieldInfo(annotation=bool, required=False, default=True), 'instruction': FieldInfo(annotation=str, required=True), 'message': FieldInfo(annotation=object, required=True), 'model': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'search_top_k': FieldInfo(annotation=int, required=False, default=4, description='search_top_k必须是大于等于1的整数', metadata=[None, Interval(gt=None, ge=1, lt=None, le=None), None]), 'stream': FieldInfo(annotation=bool, required=False, default=False), 'temperature': FieldInfo(annotation=float, required=False, default=1e-10, description='temperature范围在0到1之间', metadata=[None, Interval(gt=None, ge=0, lt=None, le=1), None, None]), 'top_p': FieldInfo(annotation=float, required=False, default=1e-10, description='top_p范围在0到1之间', metadata=[None, Interval(gt=None, ge=0, lt=None, le=1), None, None])}* Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo]. diff --git a/docs/sphinx_md/appbuilder.core.components.retriever.baidu_vdb.md b/docs/API-Reference/Python/appbuilder.core.components.retriever.baidu_vdb.md similarity index 64% rename from docs/sphinx_md/appbuilder.core.components.retriever.baidu_vdb.md rename to docs/API-Reference/Python/appbuilder.core.components.retriever.baidu_vdb.md index e789cdf18..d1d4c6b0c 100644 --- a/docs/sphinx_md/appbuilder.core.components.retriever.baidu_vdb.md +++ b/docs/API-Reference/Python/appbuilder.core.components.retriever.baidu_vdb.md @@ -38,15 +38,15 @@ res = retriever(query) 根据query进行查询 * **参数:** - * **query** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**str* *]*) – 需要查询的内容,类型为Message,包含要查询的文本。 - * **top_k** (*int* *,* *optional*) – 查询结果中匹配度最高的top_k个结果,默认为1。 + * **query** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**str* *]*) -- 需要查询的内容,类型为Message,包含要查询的文本。 + * **top_k** (*int* *,* *optional*) -- 查询结果中匹配度最高的top_k个结果,默认为1。 * **返回:** 查询到的结果,包含文本和匹配得分。 * **返回类型:** [Message](appbuilder.core.md#appbuilder.core.message.Message)[Dict] * **抛出:** - * **TypeError** – 如果query不是Message类型,或者top_k不是整数类型。 - * **ValueError** – 如果top_k不是正整数,或者query的内容为空字符串,或者长度超过512个字符。 + * **TypeError** -- 如果query不是Message类型,或者top_k不是整数类型。 + * **ValueError** -- 如果top_k不是正整数,或者query的内容为空字符串,或者长度超过512个字符。 #### tool_desc *: Dict[str, Any]* *= {'description': 'a retriever based on Baidu VectorDB'}* @@ -61,12 +61,12 @@ Baidu VDB向量存储检索工具 向bes中插入数据段 * **参数:** - * **segments** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) – 需要插入的数据段。 - * **metadata** (*str* *,* *optional*) – 元数据,默认为空字符串。 + * **segments** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 需要插入的数据段。 + * **metadata** (*str* *,* *optional*) -- 元数据,默认为空字符串。 * **返回:** 无返回值 * **抛出:** - **ValueError** – 如果segments为空,则抛出此异常。 + **ValueError** -- 如果segments为空,则抛出此异常。 #### as_retriever() @@ -95,14 +95,14 @@ Baidu VDB向量存储检索工具 从参数中实例化类。 * **参数:** - * **cls** (*type*) – 类对象,即当前函数所属的类。 - * **instance_id** (*str*) – 实例ID。 - * **api_key** (*str*) – API密钥。 - * **account** (*str* *,* *optional*) – 账户名,默认为’root’。 Defaults to DEFAULT_ACCOUNT. - * **database_name** (*str* *,* *optional*) – 数据库名,默认为’AppBuilderDatabase’。 Defaults to DEFAULT_DATABASE_NAME. - * **table_name** (*str* *,* *optional*) – 表名,默认为’AppBuilderTable’。 Defaults to DEFAULT_TABLE_NAME. - * **drop_exists** (*bool* *,* *optional*) – 是否删除已存在的表,默认为False。 Defaults to False. - * **\*\*kwargs** – 其他参数,可选的维度参数dimension默认为384。 + * **cls** (*type*) -- 类对象,即当前函数所属的类。 + * **instance_id** (*str*) -- 实例ID。 + * **api_key** (*str*) -- API密钥。 + * **account** (*str* *,* *optional*) -- 账户名,默认为'root'。 Defaults to DEFAULT_ACCOUNT. + * **database_name** (*str* *,* *optional*) -- 数据库名,默认为'AppBuilderDatabase'。 Defaults to DEFAULT_DATABASE_NAME. + * **table_name** (*str* *,* *optional*) -- 表名,默认为'AppBuilderTable'。 Defaults to DEFAULT_TABLE_NAME. + * **drop_exists** (*bool* *,* *optional*) -- 是否删除已存在的表,默认为False。 Defaults to False. + * **\*\*kwargs** -- 其他参数,可选的维度参数dimension默认为384。 * **返回:** 类实例,包含实例ID、账户名、API密钥、数据库名、表参数等属性。 * **返回类型:** @@ -119,11 +119,11 @@ See the following documentation for details: [https://cloud.baidu.com/doc/VDB/s/mlrsob0p6](https://cloud.baidu.com/doc/VDB/s/mlrsob0p6) * **参数:** - * **int** (*partition*) – The dimension of vector. - * **int** – The number of replicas in the table. - * **int** – The number of partitions in the table. - * **index_type** (*Optional* *[**str* *]*) – HNSW, FLAT… Default value is “HNSW” - * **metric_type** (*Optional* *[**str* *]*) – L2, COSINE, IP. Default value is “L2” - * **drop_exists** (*Optional* *[**bool* *]*) – Delete the existing Table. Default value is False. - * **vector_params** (*Optional* *[**Dict* *]*) – if HNSW set parameters: M and efConstruction, for example {‘M’: 16, efConstruction: 200} + * **int** (*partition*) -- The dimension of vector. + * **int** -- The number of replicas in the table. + * **int** -- The number of partitions in the table. + * **index_type** (*Optional* *[**str* *]*) -- HNSW, FLAT... Default value is "HNSW" + * **metric_type** (*Optional* *[**str* *]*) -- L2, COSINE, IP. Default value is "L2" + * **drop_exists** (*Optional* *[**bool* *]*) -- Delete the existing Table. Default value is False. + * **vector_params** (*Optional* *[**Dict* *]*) -- if HNSW set parameters: M and efConstruction, for example {'M': 16, efConstruction: 200} default is HNSW diff --git a/docs/sphinx_md/appbuilder.core.components.retriever.bes.md b/docs/API-Reference/Python/appbuilder.core.components.retriever.bes.md similarity index 79% rename from docs/sphinx_md/appbuilder.core.components.retriever.bes.md rename to docs/API-Reference/Python/appbuilder.core.components.retriever.bes.md index f2e35797c..51c03a9b6 100644 --- a/docs/sphinx_md/appbuilder.core.components.retriever.bes.md +++ b/docs/API-Reference/Python/appbuilder.core.components.retriever.bes.md @@ -36,8 +36,8 @@ res = retriever(query) 根据query进行查询 * **参数:** - * **query** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**str* *]*) – 需要查询的内容,以Message对象的形式传递。 - * **top_k** (*int* *,* *optional*) – 查询结果中匹配度最高的top_k个结果。默认为1。 + * **query** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**str* *]*) -- 需要查询的内容,以Message对象的形式传递。 + * **top_k** (*int* *,* *optional*) -- 查询结果中匹配度最高的top_k个结果。默认为1。 * **返回:** 查询到的结果,包含文本、元数据以及匹配得分,以Message对象的形式返回。 * **返回类型:** @@ -56,8 +56,8 @@ BES向量存储检索工具 向BES中插入数据 * **参数:** - * **segments** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**str* *]*) – 需要插入的内容,包含多个文本段 - * **metadata** (*str* *,* *optional*) – 元数据,默认为空字符串。 + * **segments** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**str* *]*) -- 需要插入的内容,包含多个文本段 + * **metadata** (*str* *,* *optional*) -- 元数据,默认为空字符串。 * **返回:** 无返回值 @@ -79,8 +79,8 @@ BES向量存储检索工具 创建索引的mapping * **参数:** - * **index_type** (*str*) – 索引类型,如”hnsw” - * **vector_dims** (*int*) – 向量的维度 + * **index_type** (*str*) -- 索引类型,如"hnsw" + * **vector_dims** (*int*) -- 向量的维度 * **返回:** 索引的mapping配置 * **返回类型:** @@ -109,12 +109,12 @@ BES向量存储检索工具 根据段落创建一个bes向量索引。 * **参数:** - * **segments** (*list*) – 切分的文本段落列表。 - * **cluster_id** (*str*) – bes集群ID。 - * **user_name** (*str*) – bes用户名。 - * **password** (*str*) – bes用户密码。 - * **embedding** ([*Embedding*](appbuilder.core.components.embeddings.md#appbuilder.core.components.embeddings.component.Embedding) *,* *optional*) – 文本段落embedding工具,默认为None,使用默认的Embedding类。 - * **\*\*kwargs** – 其他初始化参数。 + * **segments** (*list*) -- 切分的文本段落列表。 + * **cluster_id** (*str*) -- bes集群ID。 + * **user_name** (*str*) -- bes用户名。 + * **password** (*str*) -- bes用户密码。 + * **embedding** ([*Embedding*](appbuilder.core.components.embeddings.md#appbuilder.core.components.embeddings.component.Embedding) *,* *optional*) -- 文本段落embedding工具,默认为None,使用默认的Embedding类。 + * **\*\*kwargs** -- 其他初始化参数。 * **返回:** bes索引实例。 * **返回类型:** @@ -125,7 +125,7 @@ BES向量存储检索工具 生成随机的ID。 * **参数:** - **length** (*int* *,* *optional*) – 生成ID的长度,默认为16。 + **length** (*int* *,* *optional*) -- 生成ID的长度,默认为16。 * **返回:** 生成的随机ID。 * **返回类型:** diff --git a/docs/sphinx_md/appbuilder.core.components.retriever.md b/docs/API-Reference/Python/appbuilder.core.components.retriever.md similarity index 100% rename from docs/sphinx_md/appbuilder.core.components.retriever.md rename to docs/API-Reference/Python/appbuilder.core.components.retriever.md diff --git a/docs/sphinx_md/appbuilder.core.components.retriever.reranker.md b/docs/API-Reference/Python/appbuilder.core.components.retriever.reranker.md similarity index 87% rename from docs/sphinx_md/appbuilder.core.components.retriever.reranker.md rename to docs/API-Reference/Python/appbuilder.core.components.retriever.reranker.md index dc239e53a..fb14caab8 100644 --- a/docs/sphinx_md/appbuilder.core.components.retriever.reranker.md +++ b/docs/API-Reference/Python/appbuilder.core.components.retriever.reranker.md @@ -39,8 +39,8 @@ print(ranked_1) 运行查询,对给定的文本集合进行批量处理,并返回处理后的结果列表。 * **参数:** - * **query** (*Union* *[*[*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**str* *]* *,* *str* *]*) – 查询条件,可以是字符串或Message对象。 - * **texts** (*Union* *[*[*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**List* *[**str* *]* *]* *,* *List* *[**str* *]* *]*) – 待处理的文本集合,可以是字符串列表或包含字符串列表的Message对象。 + * **query** (*Union* *[*[*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**str* *]* *,* *str* *]*) -- 查询条件,可以是字符串或Message对象。 + * **texts** (*Union* *[*[*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**List* *[**str* *]* *]* *,* *List* *[**str* *]* *]*) -- 待处理的文本集合,可以是字符串列表或包含字符串列表的Message对象。 * **返回:** 处理后的结果列表,每个元素是一个字典,包含处理后的文本信息。 * **返回类型:** diff --git a/docs/sphinx_md/appbuilder.core.components.table_ocr.md b/docs/API-Reference/Python/appbuilder.core.components.table_ocr.md similarity index 54% rename from docs/sphinx_md/appbuilder.core.components.table_ocr.md rename to docs/API-Reference/Python/appbuilder.core.components.table_ocr.md index a25af836d..ec67cc225 100644 --- a/docs/sphinx_md/appbuilder.core.components.table_ocr.md +++ b/docs/API-Reference/Python/appbuilder.core.components.table_ocr.md @@ -31,7 +31,7 @@ print(out.content) 将表格识别结果转换为Markdown格式。 * **参数:** - **tables_result** (*list*) – 表格识别结果列表,每个元素是一个包含表格数据的字典,其中包含表格体(body)等字段。 + **tables_result** (*list*) -- 表格识别结果列表,每个元素是一个包含表格数据的字典,其中包含表格体(body)等字段。 * **返回:** 包含Markdown格式表格的字符串列表。 * **返回类型:** @@ -46,24 +46,24 @@ print(out.content) 表格文字识别 * **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) – 输入图片或图片url下载地址用于执行识别操作。 - 举例: Message(content={“raw_image”: b”…”}) - 或 Message(content={“url”: “[https://image/download/url](https://image/download/url)”})。 - * **timeout** (*float* *,* *可选*) – HTTP超时时间。 - * **retry** (*int* *,* *可选*) – HTTP重试次数。 + * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入图片或图片url下载地址用于执行识别操作。 + 举例: Message(content={"raw_image": b"..."}) + 或 Message(content={"url": "[https://image/download/url](https://image/download/url)"})。 + * **timeout** (*float* *,* *可选*) -- HTTP超时时间。 + * **retry** (*int* *,* *可选*) -- HTTP重试次数。 * **返回:** 识别结果。 - : 举例: Message(name=msg, content={‘tables_result’: [{ - ‘table_location’: [{‘x’: 15, ‘y’: 15}, {‘x’: 371, ‘y’: 15}, {‘x’: 371, ‘y’: 98}, {‘x’: 15, - ‘y’: 98}], ‘header’: [], ‘body’: [{‘cell_location’: [{‘x’: 15, ‘y’: 15}, {‘x’: 120, ‘y’: 15}, - {‘x’: 120, ‘y’: 58}, {‘x’: 15, ‘y’: 58}], ‘row_start’: 0, ‘row_end’: 1, ‘col_start’: 0, - ‘col_end’: 1, ‘words’: ‘参数’}, {‘cell_location’: [{‘x’: 120, ‘y’: 15}, {‘x’: 371, ‘y’: 15}, - {‘x’: 371, ‘y’: 58}, {‘x’: 120, ‘y’: 58}], ‘row_start’: 0, ‘row_end’: 1, ‘col_start’: 1, - ‘col_end’: 2, ‘words’: ‘值’}, {‘cell_location’: [{‘x’: 15, ‘y’: 58}, {‘x’: 120, ‘y’: 58}, - {‘x’: 120, ‘y’: 98}, {‘x’: 15, ‘y’: 98}], ‘row_start’: 1, ‘row_end’: 2, ‘col_start’: 0, - ‘col_end’: 1, ‘words’: ‘Content-Type’}, {‘cell_location’: [{‘x’: 120, ‘y’: 58}, {‘x’: 371, - ‘y’: 58}, {‘x’: 371, ‘y’: 98}, {‘x’: 120, ‘y’: 98}], ‘row_start’: 1, ‘row_end’: 2, ‘col_start’: - 1, ‘col_end’: 2, ‘words’: ‘application/x-www-form-urlencoded’}], ‘footer’: []}]}, mtype=dict) + : 举例: Message(name=msg, content={'tables_result': [{ + 'table_location': [{'x': 15, 'y': 15}, {'x': 371, 'y': 15}, {'x': 371, 'y': 98}, {'x': 15, + 'y': 98}], 'header': [], 'body': [{'cell_location': [{'x': 15, 'y': 15}, {'x': 120, 'y': 15}, + {'x': 120, 'y': 58}, {'x': 15, 'y': 58}], 'row_start': 0, 'row_end': 1, 'col_start': 0, + 'col_end': 1, 'words': '参数'}, {'cell_location': [{'x': 120, 'y': 15}, {'x': 371, 'y': 15}, + {'x': 371, 'y': 58}, {'x': 120, 'y': 58}], 'row_start': 0, 'row_end': 1, 'col_start': 1, + 'col_end': 2, 'words': '值'}, {'cell_location': [{'x': 15, 'y': 58}, {'x': 120, 'y': 58}, + {'x': 120, 'y': 98}, {'x': 15, 'y': 98}], 'row_start': 1, 'row_end': 2, 'col_start': 0, + 'col_end': 1, 'words': 'Content-Type'}, {'cell_location': [{'x': 120, 'y': 58}, {'x': 371, + 'y': 58}, {'x': 371, 'y': 98}, {'x': 120, 'y': 98}], 'row_start': 1, 'row_end': 2, 'col_start': + 1, 'col_end': 2, 'words': 'application/x-www-form-urlencoded'}], 'footer': []}]}, mtype=dict) * **返回类型:** message ([Message](appbuilder.core.md#appbuilder.core.message.Message)) @@ -72,17 +72,17 @@ print(out.content) 对传入文件进行处理,并返回处理结果。 * **参数:** - * **name** (*str*) – 工具的名称。 - * **streaming** (*bool*) – 是否为流式处理。若为True,则以生成器形式返回结果;若为False,则直接返回结果。 - * **\*\*kwargs** – 关键字参数,包含以下参数: + * **name** (*str*) -- 工具的名称。 + * **streaming** (*bool*) -- 是否为流式处理。若为True,则以生成器形式返回结果;若为False,则直接返回结果。 + * **\*\*kwargs** -- 关键字参数,包含以下参数: traceid (str): 请求的唯一标识符。 file_names (List[str]): 文件名列表,表示需要处理的文件名。 files (List[str]): 同file_names,用于兼容老版本接口。 file_urls (Dict[str, str]): 文件名和对应URL的映射字典。 * **返回:** - 若streaming为True,则以生成器形式返回处理结果,每个元素为包含type和text的字典,type固定为”text”,text为处理结果的JSON字符串。 + 若streaming为True,则以生成器形式返回处理结果,每个元素为包含type和text的字典,type固定为"text",text为处理结果的JSON字符串。 若streaming为False,则直接返回处理结果的JSON字符串。 * **抛出:** - **InvalidRequestArgumentError** – 若传入文件名在file_urls中未找到对应的URL,则抛出此异常。 + **InvalidRequestArgumentError** -- 若传入文件名在file_urls中未找到对应的URL,则抛出此异常。 #### version *= 'v1'* diff --git a/docs/sphinx_md/appbuilder.core.components.text_to_image.md b/docs/API-Reference/Python/appbuilder.core.components.text_to_image.md similarity index 58% rename from docs/sphinx_md/appbuilder.core.components.text_to_image.md rename to docs/API-Reference/Python/appbuilder.core.components.text_to_image.md index ea5773e2d..413d6a87d 100644 --- a/docs/sphinx_md/appbuilder.core.components.text_to_image.md +++ b/docs/API-Reference/Python/appbuilder.core.components.text_to_image.md @@ -30,10 +30,10 @@ print(out.content) # eg: {"img_urls": ["xxx"]} 检查服务错误信息 * **参数:** - * **request_id** (*str*) – 请求ID - * **data** (*dict*) – 响应数据 + * **request_id** (*str*) -- 请求ID + * **data** (*dict*) -- 响应数据 * **抛出:** - **AppBuilderServerException** – 如果响应数据中包含错误信息,则抛出异常 + **AppBuilderServerException** -- 如果响应数据中包含错误信息,则抛出异常 * **返回:** None @@ -42,7 +42,7 @@ print(out.content) # eg: {"img_urls": ["xxx"]} 从作画生成的返回结果中提取图片url。 * **参数:** - **(****obj** (*response*) – Text2ImageQueryResponse): 作画生成的返回结果。 + **(****obj** (*response*) -- Text2ImageQueryResponse): 作画生成的返回结果。 * **返回:** 从返回体中提取的图片url列表。 * **返回类型:** @@ -55,10 +55,10 @@ print(out.content) # eg: {"img_urls": ["xxx"]} 将文本查询请求转换为图像数据。 * **参数:** - * **request** (*Text2ImageQueryRequest*) – 输入请求,必填参数。 - * **timeout** (*float* *,* *optional*) – 请求的超时时间,默认为None。 - * **retry** (*int* *,* *optional*) – 请求的重试次数,默认为0。 - * **request_id** (*str* *,* *optional*) – 请求的唯一标识符,默认为None。 + * **request** (*Text2ImageQueryRequest*) -- 输入请求,必填参数。 + * **timeout** (*float* *,* *optional*) -- 请求的超时时间,默认为None。 + * **retry** (*int* *,* *optional*) -- 请求的重试次数,默认为0。 + * **request_id** (*str* *,* *optional*) -- 请求的唯一标识符,默认为None。 * **返回:** 接口返回的输出消息。 * **返回类型:** @@ -69,35 +69,35 @@ print(out.content) # eg: {"img_urls": ["xxx"]} 执行文本到图像的生成任务。 * **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) – 包含任务相关信息的消息对象。 - * **width** (*int* *,* *optional*) – 生成的图像的宽度,默认为1024。 - * **height** (*int* *,* *optional*) – 生成的图像的高度,默认为1024。 - * **image_num** (*int* *,* *optional*) – 生成图像的数量,默认为1。 - * **image** (*Optional* *[**str* *]* *,* *optional*) – 参考图像的路径或URL,默认为None。 - * **url** (*Optional* *[**str* *]* *,* *optional*) – 参考图像的URL,默认为None。 - * **pdf_file** (*Optional* *[**str* *]* *,* *optional*) – 参考PDF文件的路径,默认为None。 - * **pdf_file_num** (*Optional* *[**str* *]* *,* *optional*) – 参考PDF文件中的页码范围,默认为None。 - * **change_degree** (*Optional* *[**int* *]* *,* *optional*) – 图像变换的程度,默认为None。 - * **text_content** (*Optional* *[**str* *]* *,* *optional*) – 需要转换的文本内容,默认为None。 - * **task_time_out** (*Optional* *[**int* *]* *,* *optional*) – 任务超时时间,默认为None。 - * **text_check** (*Optional* *[**int* *]* *,* *optional*) – 是否进行文本内容检查,默认为1。 - * **request_id** (*Optional* *[**str* *]* *,* *optional*) – 请求的唯一标识,默认为None。 + * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 包含任务相关信息的消息对象。 + * **width** (*int* *,* *optional*) -- 生成的图像的宽度,默认为1024。 + * **height** (*int* *,* *optional*) -- 生成的图像的高度,默认为1024。 + * **image_num** (*int* *,* *optional*) -- 生成图像的数量,默认为1。 + * **image** (*Optional* *[**str* *]* *,* *optional*) -- 参考图像的路径或URL,默认为None。 + * **url** (*Optional* *[**str* *]* *,* *optional*) -- 参考图像的URL,默认为None。 + * **pdf_file** (*Optional* *[**str* *]* *,* *optional*) -- 参考PDF文件的路径,默认为None。 + * **pdf_file_num** (*Optional* *[**str* *]* *,* *optional*) -- 参考PDF文件中的页码范围,默认为None。 + * **change_degree** (*Optional* *[**int* *]* *,* *optional*) -- 图像变换的程度,默认为None。 + * **text_content** (*Optional* *[**str* *]* *,* *optional*) -- 需要转换的文本内容,默认为None。 + * **task_time_out** (*Optional* *[**int* *]* *,* *optional*) -- 任务超时时间,默认为None。 + * **text_check** (*Optional* *[**int* *]* *,* *optional*) -- 是否进行文本内容检查,默认为1。 + * **request_id** (*Optional* *[**str* *]* *,* *optional*) -- 请求的唯一标识,默认为None。 * **返回:** 包含生成图像URL的消息对象。 * **返回类型:** [Message](appbuilder.core.md#appbuilder.core.message.Message) * **抛出:** - **HTTPError** – 请求失败时抛出异常。 + **HTTPError** -- 请求失败时抛出异常。 #### submitText2ImageTask(request: Text2ImageSubmitRequest, timeout: float | None = None, retry: int = 0, request_id: str | None = None) → Text2ImageSubmitResponse 使用给定的输入并返回文生图的任务信息。 * **参数:** - * **(****obj** (*request*) – Text2ImageSubmitRequest): 输入请求,这是一个必需的参数。 - * **timeout** (*float* *,* *optional*) – 请求的超时时间。默认为None。 - * **retry** (*int* *,* *optional*) – 请求的重试次数。默认为0。 - * **request_id** (*str* *,* *optional*) – 请求的唯一标识符。默认为None。 + * **(****obj** (*request*) -- Text2ImageSubmitRequest): 输入请求,这是一个必需的参数。 + * **timeout** (*float* *,* *optional*) -- 请求的超时时间。默认为None。 + * **retry** (*int* *,* *optional*) -- 请求的重试次数。默认为0。 + * **request_id** (*str* *,* *optional*) -- 请求的唯一标识符。默认为None。 * **返回:** Text2ImageSubmitResponse: 接口返回的输出消息。 * **返回类型:** diff --git a/docs/sphinx_md/appbuilder.core.components.translate.md b/docs/API-Reference/Python/appbuilder.core.components.translate.md similarity index 81% rename from docs/sphinx_md/appbuilder.core.components.translate.md rename to docs/API-Reference/Python/appbuilder.core.components.translate.md index c5b4659d0..6d1ab6c65 100644 --- a/docs/sphinx_md/appbuilder.core.components.translate.md +++ b/docs/API-Reference/Python/appbuilder.core.components.translate.md @@ -36,14 +36,14 @@ print(resp.content) 根据提供的文本以及语种参数执行文本翻译 * **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) – 翻译文本。 - * **from_lang** (*str*) – 翻译的源语言。默认为 “auto”。 - * **to_lang** (*str*) – 翻译的目标语言。默认为 “en”。 - * **timeout** (*float* *,* *optional*) – 翻译请求的超时时间。 - * **retry** (*int* *,* *optional*) – 重试次数。 + * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 翻译文本。 + * **from_lang** (*str*) -- 翻译的源语言。默认为 "auto"。 + * **to_lang** (*str*) -- 翻译的目标语言。默认为 "en"。 + * **timeout** (*float* *,* *optional*) -- 翻译请求的超时时间。 + * **retry** (*int* *,* *optional*) -- 重试次数。 * **返回:** 返回的文本翻译结果。 - 例如,Message(content={‘from_lang’: ‘zh’, ‘to_lang’: ‘en’, ‘trans_result’: [{‘src’: ‘你好’, ‘dst’: ‘hello’}]}) + 例如,Message(content={'from_lang': 'zh', 'to_lang': 'en', 'trans_result': [{'src': '你好', 'dst': 'hello'}]}) * **返回类型:** [Message](appbuilder.core.md#appbuilder.core.message.Message) @@ -52,16 +52,16 @@ print(resp.content) 工具函数,用于翻译指定的文本。 * **参数:** - * **name** (*str*) – 函数名称,此参数在本函数中未使用。 - * **streaming** (*bool*) – 是否流式输出翻译结果。 - * **\*\*kwargs** – 关键字参数,可以包含以下参数: + * **name** (*str*) -- 函数名称,此参数在本函数中未使用。 + * **streaming** (*bool*) -- 是否流式输出翻译结果。 + * **\*\*kwargs** -- 关键字参数,可以包含以下参数: - traceid (str, optional): 请求的唯一标识符,默认为None。 - q (str): 待翻译的文本。 - - to_lang (str, optional): 目标语言代码,默认为”en”。 + - to_lang (str, optional): 目标语言代码,默认为"en"。 * **返回:** 如果streaming为True,则返回生成器,生成包含翻译结果的字典; 如果streaming为False,则返回包含翻译结果的JSON字符串。 * **抛出:** - **InvalidRequestArgumentError** – 如果未设置参数”q”,则抛出此异常。 + **InvalidRequestArgumentError** -- 如果未设置参数"q",则抛出此异常。 #### version *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.tree_mind.md b/docs/API-Reference/Python/appbuilder.core.components.tree_mind.md new file mode 100644 index 000000000..a068a7b00 --- /dev/null +++ b/docs/API-Reference/Python/appbuilder.core.components.tree_mind.md @@ -0,0 +1,53 @@ +# appbuilder.core.components.tree_mind package + +## Submodules + +## appbuilder.core.components.tree_mind.component module + +树图工具 + +### *class* appbuilder.core.components.tree_mind.component.TreeMind(meta: [ComponentArguments](appbuilder.core.md#appbuilder.core.component.ComponentArguments) | None = ComponentArguments(name='', tool_desc={}), secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) + +基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) + +树图工具,提供智能思维导图制作工具和丰富的模板,支持脑图、逻辑图、树形图、鱼骨图、组织架构图、时间轴、时间线等多种专业格式。 +.. code-block:: python + +> import os +> import requests +> import appbuilder +> # 请前往千帆AppBuilder官网创建密钥,流程详见:[https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5) +> os.environ["GATEWAY_URL"] = "..." +> os.environ["APPBUILDER_TOKEN"] = "..." +> treemind = appbuilder.TreeMind() +> resp = treemind.run(appbuilder.Message("生成一份年度总结的思维导图"), to_lang="en") +> print(resp.content) +> # 输出 {'from_lang':'zh', 'to_lang':'en', 'trans_result':[{'src':'你好','dst':'hello'},{'src':'中国','dst':'China'}]} + +#### manifests *= [{'description': '根据用户输入的信息,生成详细智能思维导图、脑图、逻辑图、树形图、鱼骨图、组织架构图、时间轴、时间线等多种专业格式思维导图', 'name': 'tree_mind', 'parameters': {'properties': {'query': {'description': '用户想要生成思维导图的内容', 'type': 'string'}}, 'required': ['query'], 'type': 'object'}}]* + +#### name *= 'tree_mind'* + +#### run(message: [Message](appbuilder.core.md#appbuilder.core.message.Message), \*\*kwargs) → [Message](appbuilder.core.md#appbuilder.core.message.Message) + +运行组件 +:param message: 消息对象 +:type message: Message + +* **返回:** + 返回消息对象 +* **返回类型:** + [Message](appbuilder.core.md#appbuilder.core.message.Message) + +#### tool_eval(query, \*\*kwargs) + +调用树图查询接口 +:param query: 用户想要生成思维导图的内容 +:type query: string + +* **返回:** + 返回生成的思维导图的图片链接和跳转链接 +* **返回类型:** + dict + +#### version *= 'v1'* diff --git a/docs/sphinx_md/appbuilder.core.components.tts.md b/docs/API-Reference/Python/appbuilder.core.components.tts.md similarity index 63% rename from docs/sphinx_md/appbuilder.core.components.tts.md rename to docs/API-Reference/Python/appbuilder.core.components.tts.md index 3a50839f7..b82357296 100644 --- a/docs/sphinx_md/appbuilder.core.components.tts.md +++ b/docs/API-Reference/Python/appbuilder.core.components.tts.md @@ -41,25 +41,25 @@ with open("sample.wav", "wb") as f: 执行文本转语音。 * **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) – 待转为语音的文本。 - 举例: Message(content={“text”: “欢迎使用百度语音”})如果选择baidu-tts模型,text最大文本长度为1024 GBK编码长度,大约为512个中英文字符;如果选择paddlespeech-tts模型, text最大文本长度是510个字符。 - * **model** (*str* *,* *可选*) – 默认是\`baidu-tts\`模型,可设置为\`paddlespeech-tts\`。 - * **speed** (*int* *,* *可选*) – 语音语速,默认是5中等语速,取值范围在0~15之间, + * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 待转为语音的文本。 + 举例: Message(content={"text": "欢迎使用百度语音"})如果选择baidu-tts模型,text最大文本长度为1024 GBK编码长度,大约为512个中英文字符;如果选择paddlespeech-tts模型, text最大文本长度是510个字符。 + * **model** (*str* *,* *可选*) -- 默认是\`baidu-tts\`模型,可设置为\`paddlespeech-tts\`。 + * **speed** (*int* *,* *可选*) -- 语音语速,默认是5中等语速,取值范围在0~15之间, 如果选择模型为paddlespeech-tts,参数自动失效。 - * **pitch** (*int* *,* *可选*) – 语音音调,默认是5中等音调,取值范围在0~15之间, + * **pitch** (*int* *,* *可选*) -- 语音音调,默认是5中等音调,取值范围在0~15之间, 如果选择模型为paddlespeech-tts,参数自动失效。 - * **volume** (*int* *,* *音量*) – 语音音量,默认是5中等音量,取值范围在0~15之间, + * **volume** (*int* *,* *音量*) -- 语音音量,默认是5中等音量,取值范围在0~15之间, 如果选择模型为paddlespeech-tts,参数自动失效。 - * **person** (*int* *,* *可选*) – 语音人物特征,默认是0, + * **person** (*int* *,* *可选*) -- 语音人物特征,默认是0, 可选值包括度小宇=1 度小美=0 度逍遥(基础)=3 度丫丫=4 度逍遥(精品)=5003 度小鹿=5118 度博文=106 度小童=110 度小萌=111 度米朵=103 度小娇=5, 如果选择模型为paddlespeech-tts,参数自动失效。 - * **audio_type** (*str* *,* *可选*) – 音频文件格式,默认是\`mp3\`, + * **audio_type** (*str* *,* *可选*) -- 音频文件格式,默认是\`mp3\`, 如果选择\`paddlespeech-tts\`模型,参数只能设为\`wav\`。 - * **timeout** (*float* *,* *可选*) – HTTP超时时间。 - * **retry** (*int* *,* *可选*) – HTTP重试次数。 - * **stream** (*bool* *,* *可选*) – 是否以流的形式返回音频数据,默认为False。 + * **timeout** (*float* *,* *可选*) -- HTTP超时时间。 + * **retry** (*int* *,* *可选*) -- HTTP重试次数。 + * **stream** (*bool* *,* *可选*) -- 是否以流的形式返回音频数据,默认为False。 * **返回:** - 文本转语音结果。举例: Message(content={“audio_binary”: b”xxx”, “audio_type”: “mp3”}) + 文本转语音结果。举例: Message(content={"audio_binary": b"xxx", "audio_type": "mp3"}) * **返回类型:** message ([Message](appbuilder.core.md#appbuilder.core.message.Message)) diff --git a/docs/sphinx_md/appbuilder.core.console.appbuilder_client.md b/docs/API-Reference/Python/appbuilder.core.console.appbuilder_client.md similarity index 61% rename from docs/sphinx_md/appbuilder.core.console.appbuilder_client.md rename to docs/API-Reference/Python/appbuilder.core.console.appbuilder_client.md index ff843e12f..6569c7aa4 100644 --- a/docs/sphinx_md/appbuilder.core.console.appbuilder_client.md +++ b/docs/API-Reference/Python/appbuilder.core.console.appbuilder_client.md @@ -71,15 +71,15 @@ print(message.content) 运行智能体应用 * **参数:** - * **query** (*str*) – query内容 - * **conversation_id** (*str*) – 唯一会话ID,如需开始新的会话,请使用self.create_conversation创建新的会话 - * **file_ids** (*list* *[**str* *]*) – 文件ID列表 - * **stream** (*bool*) – 为True时,流式返回,需要将message.content.answer拼接起来才是完整的回答;为False时,对应非流式返回 - * **tools** (*list* *[**data_class.Tools* *]*) – 一个Tools组成的列表,其中每个Tools对应一个工具的配置, 默认为None - * **tool_outputs** (*list* *[**data_class.ToolOutput* *]*) – 工具输出列表,格式为list[ToolOutput], ToolOutputd内容为本地的工具执行结果,以自然语言/json dump str描述,默认为None - * **tool_choice** (*data_class.ToolChoice*) – 控制大模型使用组件的方式,默认为None - * **end_user_id** (*str*) – 用户ID,用于区分不同用户 - * **kwargs** – 其他参数 + * **query** (*str*) -- query内容 + * **conversation_id** (*str*) -- 唯一会话ID,如需开始新的会话,请使用self.create_conversation创建新的会话 + * **file_ids** (*list* *[**str* *]*) -- 文件ID列表 + * **stream** (*bool*) -- 为True时,流式返回,需要将message.content.answer拼接起来才是完整的回答;为False时,对应非流式返回 + * **tools** (*list* *[**data_class.Tools* *]*) -- 一个Tools组成的列表,其中每个Tools对应一个工具的配置, 默认为None + * **tool_outputs** (*list* *[**data_class.ToolOutput* *]*) -- 工具输出列表,格式为list[ToolOutput], ToolOutputd内容为本地的工具执行结果,以自然语言/json dump str描述,默认为None + * **tool_choice** (*data_class.ToolChoice*) -- 控制大模型使用组件的方式,默认为None + * **end_user_id** (*str*) -- 用户ID,用于区分不同用户 + * **kwargs** -- 其他参数 * **返回:** 对话结果,一个Message对象,使用message.content获取内容。 * **返回类型:** @@ -90,13 +90,13 @@ print(message.content) 运行智能体应用,并通过事件处理器处理事件 * **参数:** - * **conversation_id** (*str*) – 唯一会话ID,如需开始新的会话,请使用self.create_conversation创建新的会话 - * **query** (*str*) – 查询字符串 - * **file_ids** (*list*) – 文件ID列表 - * **tools** (*list* *[**data_class.Tools* *]* *,* *可选*) – 一个Tools组成的列表,其中每个Tools对应一个工具的配置, 默认为None - * **stream** (*bool*) – 是否流式响应 - * **event_handler** (*EventHandler*) – 事件处理器 - * **kwargs** – 其他参数 + * **conversation_id** (*str*) -- 唯一会话ID,如需开始新的会话,请使用self.create_conversation创建新的会话 + * **query** (*str*) -- 查询字符串 + * **file_ids** (*list*) -- 文件ID列表 + * **tools** (*list* *[**data_class.Tools* *]* *,* *可选*) -- 一个Tools组成的列表,其中每个Tools对应一个工具的配置, 默认为None + * **stream** (*bool*) -- 是否流式响应 + * **event_handler** (*EventHandler*) -- 事件处理器 + * **kwargs** -- 其他参数 * **返回:** 事件处理器 * **返回类型:** @@ -109,13 +109,27 @@ print(message.content) 该接口用于在对话中上传文件供大模型处理,文件的有效期为7天并且不超过对话的有效期。一次只能上传一个文件。 * **参数:** - * **conversation_id** (*str*) – 会话ID - * **local_file_path** (*str*) – 本地文件路径 + * **conversation_id** (*str*) -- 会话ID + * **local_file_path** (*str*) -- 本地文件路径 * **返回:** 唯一文件ID * **返回类型:** response (str) +### appbuilder.core.console.appbuilder_client.appbuilder_client.describe_apps(marker: str | None = None, maxKeys: int = 10, secret_key: str | None = None, gateway: str | None = None) → list[AppOverview] + +该接口查询用户下状态为已发布的应用列表 + +* **参数:** + * **maxKeys** (*int* *,* *optional*) -- 返回结果的最大数量,默认值为10,最大为100。 + * **marker** (*str* *,* *optional*) -- 起始位置,即从哪个游标开始查询,默认值为空字符串。 + * **secret_key** (*Optional* *[**str* *]* *,* *optional*) -- 认证密钥。如果未指定,则使用默认的密钥。默认值为None。 + * **gateway** (*Optional* *[**str* *]* *,* *optional*) -- 网关地址。如果未指定,则使用默认的地址。默认值为None。 +* **返回:** + 应用列表。 +* **返回类型:** + DescribeAppsResponse + ### appbuilder.core.console.appbuilder_client.appbuilder_client.get_all_apps() 获取所有应用列表。 @@ -133,11 +147,11 @@ print(message.content) 该接口查询用户下状态为已发布的应用列表 * **参数:** - * **limit** (*int* *,* *optional*) – 返回结果的最大数量,默认值为10。 - * **after** (*str* *,* *optional*) – 返回结果中第一个应用的游标值,用于分页查询。默认值为空字符串。 - * **before** (*str* *,* *optional*) – 返回结果中最后一个应用的游标值,用于分页查询。默认值为空字符串。 - * **secret_key** (*Optional* *[**str* *]* *,* *optional*) – 认证密钥。如果未指定,则使用默认的密钥。默认值为None。 - * **gateway_v2** (*Optional* *[**str* *]* *,* *optional*) – 网关地址。如果未指定,则使用默认的地址。默认值为None。 + * **limit** (*int* *,* *optional*) -- 返回结果的最大数量,默认值为10。 + * **after** (*str* *,* *optional*) -- 返回结果中第一个应用的游标值,用于分页查询。默认值为空字符串。 + * **before** (*str* *,* *optional*) -- 返回结果中最后一个应用的游标值,用于分页查询。默认值为空字符串。 + * **secret_key** (*Optional* *[**str* *]* *,* *optional*) -- 认证密钥。如果未指定,则使用默认的密钥。默认值为None。 + * **gateway_v2** (*Optional* *[**str* *]* *,* *optional*) -- 网关地址。如果未指定,则使用默认的地址。默认值为None。 * **返回:** 应用列表。 * **返回类型:** @@ -162,14 +176,14 @@ print(message.content) 初始化类实例并设置相关参数。 * **参数:** - * **appbuilder_client** (*object*) – AppBuilder客户端实例对象。 - * **conversation_id** (*str*) – 对话ID。 - * **query** (*str*) – 用户输入的查询语句。 - * **file_ids** (*list* *,* *optional*) – 文件ID列表,默认为None。 - * **tools** (*list* *,* *optional*) – 工具列表,默认为None。 - * **stream** (*bool* *,* *optional*) – 是否使用流式处理,默认为False。 - * **event_handler** (*callable* *,* *optional*) – 事件处理函数,默认为None。 - * **\*\*kwargs** – 其他可选参数。 + * **appbuilder_client** (*object*) -- AppBuilder客户端实例对象。 + * **conversation_id** (*str*) -- 对话ID。 + * **query** (*str*) -- 用户输入的查询语句。 + * **file_ids** (*list* *,* *optional*) -- 文件ID列表,默认为None。 + * **tools** (*list* *,* *optional*) -- 工具列表,默认为None。 + * **stream** (*bool* *,* *optional*) -- 是否使用流式处理,默认为False。 + * **event_handler** (*callable* *,* *optional*) -- 事件处理函数,默认为None。 + * **\*\*kwargs** -- 其他可选参数。 * **返回:** None diff --git a/docs/sphinx_md/appbuilder.core.console.dataset.md b/docs/API-Reference/Python/appbuilder.core.console.dataset.md similarity index 51% rename from docs/sphinx_md/appbuilder.core.console.dataset.md rename to docs/API-Reference/Python/appbuilder.core.console.dataset.md index 2e6d7f29d..82b41e3a0 100644 --- a/docs/sphinx_md/appbuilder.core.console.dataset.md +++ b/docs/API-Reference/Python/appbuilder.core.console.dataset.md @@ -37,14 +37,14 @@ dataset.delete_documents(document_ids) 向知识库中添加文档 * **参数:** - * **file_path_list** – 文档路径列表 - * **is_custom_process_rule** – 是否使用自定义文档处理规则, 默认为False, 使用平台的默认规则,为True时使用自定义规则 - * **custom_process_rule** – 自定义文档规则,当is_custom_process_rule为True时生效,格式示例如下: - * **{** – “separators”: [”。”, “,”], # 文本切分符,支持这几种[ , , “?”, , “!”, “?”, “……”] - “target_length”: 300, # 文本切片片段长度,取值范围[300, 800] - “overlap_rate”: 0.3 # 文本片段重叠率,取值范围[0, 0.3] + * **file_path_list** -- 文档路径列表 + * **is_custom_process_rule** -- 是否使用自定义文档处理规则, 默认为False, 使用平台的默认规则,为True时使用自定义规则 + * **custom_process_rule** -- 自定义文档规则,当is_custom_process_rule为True时生效,格式示例如下: + * **{** -- "separators": ["。", ","], # 文本切分符,支持这几种[ , , "?", , "!", "?", "……"] + "target_length": 300, # 文本切片片段长度,取值范围[300, 800] + "overlap_rate": 0.3 # 文本片段重叠率,取值范围[0, 0.3] * **}** - * **is_enhanced** – 是否开启知识增强, 默认为False,在检索问答时通过知识点来索引到对应的切片,大模型根据切片内容生成答案,开启知识增强会调用大模型抽取更加丰富的知识点,增加切片的召回率 + * **is_enhanced** -- 是否开启知识增强, 默认为False,在检索问答时通过知识点来索引到对应的切片,大模型根据切片内容生成答案,开启知识增强会调用大模型抽取更加丰富的知识点,增加切片的召回率 * **返回:** 添加文档的响应结果,包含以下属性: - dataset_id (str): 知识库id @@ -59,7 +59,7 @@ dataset.delete_documents(document_ids) 创建知识库 * **参数:** - **dataset_name** – 知识库名称 + **dataset_name** -- 知识库名称 * **返回:** 创建成功的知识库实例 * **返回类型:** @@ -72,7 +72,7 @@ dataset.delete_documents(document_ids) 删除知识库中的文档 * **参数:** - **document_ids** – 文档id列表 + **document_ids** -- 文档id列表 * **返回:** None @@ -83,34 +83,34 @@ dataset.delete_documents(document_ids) 获取知识库中的文档列表 * **参数:** - * **page** – 第几页 - * **limit** – 每页文档数 - * **keyword** – 文件名关键字,支持模糊查询 + * **page** -- 第几页 + * **limit** -- 每页文档数 + * **keyword** -- 文件名关键字,支持模糊查询 * **返回:** DocumentListResponses实例,返回示例: { - “data”: [ + "data": [ > { - > : “id”:”d2d1bc1a-1763-4162-88b2-0dad225da16f”, # 文档id - > “name”: “唐诗三百首(全集)全新编辑版.pdf”, # 文档名称 - > “created_from”: “web”, # 创建来源 - > “created_by”: “76efed91-cf19-435d-993c-cdd901d6d13c”, # 创建人 - > “created_at”: 1705958975, # 创建时间 - > “indexing_status”: “indexing”, # 文档处理状态 - > “error”: null, # 文档处理错误信息 - > “enabled”: true, # 文档是否启用 - > “disabled_at”: null, # 文档禁用时间 - > “disabled_by”: null, # 文档禁用人 - > “display_status”: “indexing”, # 文档显示状态,和前端展示状态一致 - > “word_count”: 5024 # 文档字数 + > : "id":"d2d1bc1a-1763-4162-88b2-0dad225da16f", # 文档id + > "name": "唐诗三百首(全集)全新编辑版.pdf", # 文档名称 + > "created_from": "web", # 创建来源 + > "created_by": "76efed91-cf19-435d-993c-cdd901d6d13c", # 创建人 + > "created_at": 1705958975, # 创建时间 + > "indexing_status": "indexing", # 文档处理状态 + > "error": null, # 文档处理错误信息 + > "enabled": true, # 文档是否启用 + > "disabled_at": null, # 文档禁用时间 + > "disabled_by": null, # 文档禁用人 + > "display_status": "indexing", # 文档显示状态,和前端展示状态一致 + > "word_count": 5024 # 文档字数 > } ], - “has_more”: false, # 是否还有下一页 - “limit”: 10, # 每页文档数 - “total”: 1, # 总页数 - “page”: 1 # 当前页 + "has_more": false, # 是否还有下一页 + "limit": 10, # 每页文档数 + "total": 1, # 总页数 + "page": 1 # 当前页 } #### get_file_list_url *: str* *= '/v1/ai_engine/agi_platform/v1/datasets/documents/list_page'* diff --git a/docs/sphinx_md/appbuilder.core.console.knowledge_base.md b/docs/API-Reference/Python/appbuilder.core.console.knowledge_base.md similarity index 58% rename from docs/sphinx_md/appbuilder.core.console.knowledge_base.md rename to docs/API-Reference/Python/appbuilder.core.console.knowledge_base.md index 0f0e79ac0..02ac2d384 100644 --- a/docs/sphinx_md/appbuilder.core.console.knowledge_base.md +++ b/docs/API-Reference/Python/appbuilder.core.console.knowledge_base.md @@ -26,12 +26,12 @@ print("文档列表: ", list_res) 添加文档到知识库 * **参数:** - * **content_type** (*str*) – 内容类型,可选值有”raw_text”, “qa”。 - * **file_ids** (*List* *[**str* *]* *,* *optional*) – 文件ID列表。默认为空列表。 - * **is_enhanced** (*bool* *,* *optional*) – 是否增强。默认为False。 - * **custom_process_rule** (*Optional* *[**data_class.CustomProcessRule* *]* *,* *optional*) – 自定义处理规则。默认为None。 - * **knowledge_base_id** (*Optional* *[**str* *]* *,* *optional*) – 知识库ID。默认为None,此时使用当前类的knowledge_id属性。 - * **client_token** (*str*) – 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 + * **content_type** (*str*) -- 内容类型,可选值有"raw_text", "qa"。 + * **file_ids** (*List* *[**str* *]* *,* *optional*) -- 文件ID列表。默认为空列表。 + * **is_enhanced** (*bool* *,* *optional*) -- 是否增强。默认为False。 + * **custom_process_rule** (*Optional* *[**data_class.CustomProcessRule* *]* *,* *optional*) -- 自定义处理规则。默认为None。 + * **knowledge_base_id** (*Optional* *[**str* *]* *,* *optional*) -- 知识库ID。默认为None,此时使用当前类的knowledge_id属性。 + * **client_token** (*str*) -- 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 * **返回:** 添加文档的相应结果。包含以下属性: - request_id (str): 请求ID @@ -45,29 +45,31 @@ print("文档列表: ", list_res) 创建文档块 * **参数:** - * **documentId** (*str*) – 文档ID - * **content** (*str*) – 内容 - * **client_token** (*str* *,* *optional*) – 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 + * **documentId** (*str*) -- 文档ID + * **content** (*str*) -- 内容 + * **client_token** (*str* *,* *optional*) -- 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 * **返回:** 创建文档块的相应消息, 包含以下属性: - id (str): 切片ID * **返回类型:** CreateChunkResponse -#### create_documents(id: str | None = None, contentFormat: str = '', source: DocumentSource | None = None, processOption: DocumentProcessOption | None = None, client_token: str | None = None) +#### create_documents(id: str | None = None, contentFormat: str = '', source: DocumentSource | None = None, processOption: DocumentProcessOption | None = None, client_token: str | None = None) → KnowledgeBaseCreateDocumentsResponse 创建文档 * **参数:** - * **id** (*Optional* *[**str* *]* *,* *optional*) – 知识库ID,如果不指定则使用当前实例的knowledge_id属性。默认值为None。 - * **contentFormat** (*str* *,* *optional*) – 文档内容格式,可以是”rawText”, “qa”之一。默认值为””。 - * **source** (*data_class.DocumentSource* *,* *optional*) – 文档源数据。默认值为None。 - * **processOption** (*data_class.DocumentProcessOption* *,* *optional*) – 文档处理选项。默认值为None。 - * **client_token** (*str* *,* *optional*) – 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 + * **id** (*Optional* *[**str* *]* *,* *optional*) -- 知识库ID,如果不指定则使用当前实例的knowledge_id属性。默认值为None。 + * **contentFormat** (*str* *,* *optional*) -- 文档内容格式,可以是"rawText", "qa"之一。默认值为""。 + * **source** (*data_class.DocumentSource* *,* *optional*) -- 文档源数据。默认值为None。 + * **processOption** (*data_class.DocumentProcessOption* *,* *optional*) -- 文档处理选项。默认值为None。 + * **client_token** (*str* *,* *optional*) -- 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 * **返回:** - 响应数据,包含请求ID: requestId + 创建知识库文档的响应消息,返回一个KnowledgeBaseCreateDocumentsResponse对象,包含以下属性: + - requestId (str): 请求ID + - documentIds (list[str]): 文档ID列表 * **返回类型:** - dict + KnowledgeBaseCreateDocumentsResponse #### *classmethod* create_knowledge(knowledge_name: str) → [KnowledgeBase](#appbuilder.core.console.knowledge_base.knowledge_base.KnowledgeBase) @@ -76,7 +78,7 @@ print("文档列表: ", list_res) Deprecated: use create_knowledge_base instead * **参数:** - **knowledge_name** (*str*) – 知识库名称 + **knowledge_name** (*str*) -- 知识库名称 * **返回:** 返回一个KnowledgeBase对象 * **返回类型:** @@ -87,13 +89,13 @@ Deprecated: use create_knowledge_base instead 创建知识库 * **参数:** - * **name** (*str*) – 知识库名称。 - * **description** (*str*) – 知识库描述。 - * **type** (*str* *,* *optional*) – 知识库类型。默认为”public”。 - * **esUrl** (*str* *,* *optional*) – Elasticsearch服务器地址。默认为None。 - * **esUserName** (*str* *,* *optional*) – Elasticsearch用户名。默认为None。 - * **esPassword** (*str* *,* *optional*) – Elasticsearch密码。默认为None。 - * **client_token** (*str* *,* *optional*) – 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 + * **name** (*str*) -- 知识库名称。 + * **description** (*str*) -- 知识库描述。 + * **type** (*str* *,* *optional*) -- 知识库类型。默认为"public"。 + * **esUrl** (*str* *,* *optional*) -- Elasticsearch服务器地址。默认为None。 + * **esUserName** (*str* *,* *optional*) -- Elasticsearch用户名。默认为None。 + * **esPassword** (*str* *,* *optional*) -- Elasticsearch密码。默认为None。 + * **client_token** (*str* *,* *optional*) -- 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 * **返回:** 创建知识库的响应消息,包含以下属性: - id (str): 知识库ID @@ -108,8 +110,8 @@ Deprecated: use create_knowledge_base instead 删除文档块 * **参数:** - * **chunkId** (*str*) – 文档块ID - * **client_token** (*str* *,* *optional*) – 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 + * **chunkId** (*str*) -- 文档块ID + * **client_token** (*str* *,* *optional*) -- 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 * **返回:** 响应数据,包含请求ID: requestId * **返回类型:** @@ -120,9 +122,9 @@ Deprecated: use create_knowledge_base instead 删除知识库中的文档 * **参数:** - * **document_id** (*str*) – 文档ID - * **knowledge_base_id** (*Optional* *[**str* *]* *,* *optional*) – 知识库ID。默认为None,此时使用当前类的knowledge_id属性。 - * **client_token** (*str*) – 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 + * **document_id** (*str*) -- 文档ID + * **knowledge_base_id** (*Optional* *[**str* *]* *,* *optional*) -- 知识库ID。默认为None,此时使用当前类的knowledge_id属性。 + * **client_token** (*str*) -- 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 * **返回:** 删除文档的响应消息,包含以下属性: - request_id (str): 请求ID @@ -134,8 +136,8 @@ Deprecated: use create_knowledge_base instead 删除知识库 * **参数:** - * **knowledge_base_id** (*Optional* *[**str* *]* *,* *optional*) – 知识库ID,如果不指定则使用当前实例的knowledge_id属性。默认值为None。 - * **client_token** (*str* *,* *optional*) – 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 + * **knowledge_base_id** (*Optional* *[**str* *]* *,* *optional*) -- 知识库ID,如果不指定则使用当前实例的knowledge_id属性。默认值为None。 + * **client_token** (*str* *,* *optional*) -- 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 * **返回:** requestId * **返回类型:** @@ -146,7 +148,7 @@ Deprecated: use create_knowledge_base instead 获取文档块详情 * **参数:** - **chunkId** (*str*) – 文档块ID + **chunkId** (*str*) -- 文档块ID * **返回:** 文档块详情,一个DescribeChunkResponse对象,包含以下属性: - id (str): 切片ID @@ -169,10 +171,10 @@ Deprecated: use create_knowledge_base instead 获取文档块列表 * **参数:** - * **documentId** (*str*) – 文档ID - * **marker** (*str* *,* *optional*) – 分页标记,用于指定从哪个位置开始返回结果。默认为None,表示从头开始返回结果。 - * **maxKeys** (*int* *,* *optional*) – 最大返回数量,用于限制每次请求返回的最大文档块数目。默认为None,表示不限制返回数量。 - * **type** (*str* *,* *optional*) – 文档块类型。默认为None,表示不限定类型。 + * **documentId** (*str*) -- 文档ID + * **marker** (*str* *,* *optional*) -- 分页标记,用于指定从哪个位置开始返回结果。默认为None,表示从头开始返回结果。 + * **maxKeys** (*int* *,* *optional*) -- 最大返回数量,用于限制每次请求返回的最大文档块数目。默认为None,表示不限制返回数量。 + * **type** (*str* *,* *optional*) -- 文档块类型。默认为None,表示不限定类型。 * **返回:** 文档块列表,一个DescribeChunksResponse对象,包含以下属性: - data (list[DescribeChunkResponse]): 切片列表 @@ -188,23 +190,23 @@ Deprecated: use create_knowledge_base instead 获取知识库中所有文档。 * **参数:** - **knowledge_base_id** (*Optional* *[**str* *]* *,* *optional*) – 知识库的ID。如果为None,则使用当前实例的knowledge_id。默认为None。 + **knowledge_base_id** (*Optional* *[**str* *]* *,* *optional*) -- 知识库的ID。如果为None,则使用当前实例的knowledge_id。默认为None。 * **返回:** 包含所有文档的列表。 * **返回类型:** list * **抛出:** - **ValueError** – 如果knowledge_base_id为空,且当前实例没有已创建的knowledge_id时抛出。 + **ValueError** -- 如果knowledge_base_id为空,且当前实例没有已创建的knowledge_id时抛出。 #### get_documents_list(limit: int = 10, after: str | None = '', before: str | None = '', knowledge_base_id: str | None = None) → KnowledgeBaseGetDocumentsListResponse 获取知识库中的文档列表 * **参数:** - * **limit** (*int* *,* *optional*) – 限制数量。默认为10。 - * **after** (*Optional* *[**str* *]* *,* *optional*) – 起始位置。默认为空字符串””。 - * **before** (*Optional* *[**str* *]* *,* *optional*) – 结束位置。默认为空字符串””。 - * **knowledge_base_id** (*Optional* *[**str* *]* *,* *optional*) – 知识库ID。默认为None,此时使用当前类的knowledge_id属性。 + * **limit** (*int* *,* *optional*) -- 限制数量。默认为10。 + * **after** (*Optional* *[**str* *]* *,* *optional*) -- 起始位置。默认为空字符串""。 + * **before** (*Optional* *[**str* *]* *,* *optional*) -- 结束位置。默认为空字符串""。 + * **knowledge_base_id** (*Optional* *[**str* *]* *,* *optional*) -- 知识库ID。默认为None,此时使用当前类的knowledge_id属性。 * **返回:** 知识库文档列表服务的响应消息,包含以下属性: - request_id (str): 请求ID @@ -217,7 +219,7 @@ Deprecated: use create_knowledge_base instead 获取知识库详情 * **参数:** - **knowledge_base_id** (*Optional* *[**str* *]* *,* *optional*) – 知识库ID,如果不指定则使用当前实例的knowledge_id属性。默认值为None。 + **knowledge_base_id** (*Optional* *[**str* *]* *,* *optional*) -- 知识库ID,如果不指定则使用当前实例的knowledge_id属性。默认值为None。 * **返回:** 知识库详情,返回一个KnowledgeBaseDetailResponse对象,包含以下属性: - id (str): 知识库ID @@ -232,9 +234,9 @@ Deprecated: use create_knowledge_base instead 获取知识库列表 * **参数:** - * **knowledge_base_id** (*Optional* *[**str* *]* *,* *optional*) – 知识库ID,如果不指定则使用当前实例的knowledge_id属性。默认值为None。 - * **maxKeys** (*int* *,* *optional*) – 最大键数。默认值为10。 - * **keyword** (*Optional* *[**str* *]* *,* *optional*) – 关键字。默认值为None。 + * **knowledge_base_id** (*Optional* *[**str* *]* *,* *optional*) -- 知识库ID,如果不指定则使用当前实例的knowledge_id属性。默认值为None。 + * **maxKeys** (*int* *,* *optional*) -- 最大键数。默认值为10。 + * **keyword** (*Optional* *[**str* *]* *,* *optional*) -- 关键字。默认值为None。 * **返回:** 获取知识库列表的响应消息,返回一个KnowledgeBaseGetListResponse对象,包含以下属性: - requestId (str): 请求ID @@ -251,9 +253,9 @@ Deprecated: use create_knowledge_base instead 修改文档块 * **参数:** - * **chunkId** (*str*) – 文档块ID - * **content** (*str*) – 内容 - * **enable** (*bool*) – 是否启用 + * **chunkId** (*str*) -- 文档块ID + * **content** (*str*) -- 内容 + * **enable** (*bool*) -- 是否启用 * **返回:** 响应数据,包含请求ID: requestId * **返回类型:** @@ -264,37 +266,39 @@ Deprecated: use create_knowledge_base instead 修改知识库信息 * **参数:** - * **knowledge_base_id** (*Optional* *[**str* *]* *,* *optional*) – 知识库ID,如果不指定则使用当前实例的knowledge_id属性。默认值为None。 - * **name** (*Optional* *[**str* *]* *,* *optional*) – 新的知识库名称。默认值为None。 - * **description** (*Optional* *[**str* *]* *,* *optional*) – 新的知识库描述。默认值为None。 - * **client_token** (*str* *,* *optional*) – 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 + * **knowledge_base_id** (*Optional* *[**str* *]* *,* *optional*) -- 知识库ID,如果不指定则使用当前实例的knowledge_id属性。默认值为None。 + * **name** (*Optional* *[**str* *]* *,* *optional*) -- 新的知识库名称。默认值为None。 + * **description** (*Optional* *[**str* *]* *,* *optional*) -- 新的知识库描述。默认值为None。 + * **client_token** (*str* *,* *optional*) -- 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 * **返回:** 响应数据,包含请求ID: requestId。 * **返回类型:** dict -#### upload_documents(file_path: str, content_format: str = 'rawText', id: str | None = None, processOption: DocumentProcessOption | None = None, client_token: str | None = None) +#### upload_documents(file_path: str, content_format: str = 'rawText', id: str | None = None, processOption: DocumentProcessOption | None = None, client_token: str | None = None) → KnowledgeBaseUploadDocumentsResponse 上传文档 * **参数:** - * **file_path** (*str*) – 文件路径 - * **content_format** (*str* *,* *optional*) – 内容格式。默认值为”rawText”。 - * **id** (*Optional* *[**str* *]* *,* *optional*) – 知识库ID,如果不指定则使用当前实例的knowledge_id属性。默认值为None。 - * **processOption** (*data_class.DocumentProcessOption* *,* *optional*) – 文档处理选项。默认值为None。 - * **client_token** (*str* *,* *optional*) – 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 + * **file_path** (*str*) -- 文件路径 + * **content_format** (*str* *,* *optional*) -- 内容格式。默认值为"rawText"。 + * **id** (*Optional* *[**str* *]* *,* *optional*) -- 知识库ID,如果不指定则使用当前实例的knowledge_id属性。默认值为None。 + * **processOption** (*data_class.DocumentProcessOption* *,* *optional*) -- 文档处理选项。默认值为None。 + * **client_token** (*str* *,* *optional*) -- 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 * **返回:** - 响应数据,包含请求ID: requestId + 创建知识库文档的响应消息,返回一个KnowledgeBaseUploadDocumentsResponse对象,包含以下属性: + - requestId (str): 请求ID + - documentId (str): 文档ID * **返回类型:** - dict + KnowledgeBaseUploadDocumentsResponse #### upload_file(file_path: str, client_token: str | None = None) → KnowledgeBaseUploadFileResponse 上传文件到知识库 * **参数:** - * **file_path** (*str*) – 文件路径 - * **client_token** (*str* *,* *optional*) – 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 + * **file_path** (*str*) -- 文件路径 + * **client_token** (*str* *,* *optional*) -- 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 * **返回:** 返回一个KnowledgeBaseUploadFileResponse对象,包含以下属性: - request_id (int): 请求id diff --git a/docs/sphinx_md/appbuilder.core.console.md b/docs/API-Reference/Python/appbuilder.core.console.md similarity index 98% rename from docs/sphinx_md/appbuilder.core.console.md rename to docs/API-Reference/Python/appbuilder.core.console.md index edee8f3d3..c81532990 100644 --- a/docs/sphinx_md/appbuilder.core.console.md +++ b/docs/API-Reference/Python/appbuilder.core.console.md @@ -11,6 +11,7 @@ * [`AppBuilderClient.run()`](appbuilder.core.console.appbuilder_client.md#appbuilder.core.console.appbuilder_client.appbuilder_client.AppBuilderClient.run) * [`AppBuilderClient.run_with_handler()`](appbuilder.core.console.appbuilder_client.md#appbuilder.core.console.appbuilder_client.appbuilder_client.AppBuilderClient.run_with_handler) * [`AppBuilderClient.upload_local_file()`](appbuilder.core.console.appbuilder_client.md#appbuilder.core.console.appbuilder_client.appbuilder_client.AppBuilderClient.upload_local_file) + * [`describe_apps()`](appbuilder.core.console.appbuilder_client.md#appbuilder.core.console.appbuilder_client.appbuilder_client.describe_apps) * [`get_all_apps()`](appbuilder.core.console.appbuilder_client.md#appbuilder.core.console.appbuilder_client.appbuilder_client.get_all_apps) * [`get_app_list()`](appbuilder.core.console.appbuilder_client.md#appbuilder.core.console.appbuilder_client.appbuilder_client.get_app_list) * [appbuilder.core.console.appbuilder_client.data_class module](appbuilder.core.console.appbuilder_client.md#module-appbuilder.core.console.appbuilder_client.event_handler) diff --git a/docs/sphinx_md/appbuilder.core.console.rag.md b/docs/API-Reference/Python/appbuilder.core.console.rag.md similarity index 91% rename from docs/sphinx_md/appbuilder.core.console.rag.md rename to docs/API-Reference/Python/appbuilder.core.console.rag.md index 8587a8a37..7ee93457a 100644 --- a/docs/sphinx_md/appbuilder.core.console.rag.md +++ b/docs/API-Reference/Python/appbuilder.core.console.rag.md @@ -54,9 +54,9 @@ print(answer.extra) # 获取结果来源 RAG问答 * **参数:** - * **query** – 用户输入的文本 - * **stream** – 是否开启流式模式 - * **conversation_id** – 会话ID,不传表示新建对话 + * **query** -- 用户输入的文本 + * **stream** -- 是否开启流式模式 + * **conversation_id** -- 会话ID,不传表示新建对话 * **返回:** rag答案 * **返回类型:** diff --git a/docs/sphinx_md/appbuilder.core.md b/docs/API-Reference/Python/appbuilder.core.md similarity index 85% rename from docs/sphinx_md/appbuilder.core.md rename to docs/API-Reference/Python/appbuilder.core.md index acd1d9004..6b00f0fdc 100644 --- a/docs/sphinx_md/appbuilder.core.md +++ b/docs/API-Reference/Python/appbuilder.core.md @@ -24,10 +24,10 @@ AgentRuntime 是对组件调用的服务化封装,开发者不是必须要用 此外,结合 Component 和 Message 自带的运行和调试接口,可以方便开发者快速获得一个调试 Agent 的服务。 * **参数:** - * **component** ([*Component*](#appbuilder.core.component.Component)) – 可运行的 Component, 需要实现 run(message, stream, args) 方法 - * **user_session_config** (*sqlalchemy.engine.URL* *|**str* *|**None*) – Session 输出存储配置字符串。默认使用 sqlite:///user_session.db + * **component** ([*Component*](#appbuilder.core.component.Component)) -- 可运行的 Component, 需要实现 run(message, stream, args) 方法 + * **user_session_config** (*sqlalchemy.engine.URL* *|**str* *|**None*) -- Session 输出存储配置字符串。默认使用 sqlite:///user_session.db 遵循 sqlalchemy 后端定义,参考文档:https://docs.sqlalchemy.org/en/20/core/engines.html#backend-specific-urls - * **tool_choice** (*ToolChoice*) – 可用于Agent强制执行的组件工具 + * **tool_choice** (*ToolChoice*) -- 可用于Agent强制执行的组件工具 ### 示例 @@ -209,8 +209,8 @@ conn.close() 将 appbuilder client 服务化,提供 chainlit demo 页面 * **参数:** - * **host** (*str*) – 服务 host - * **port** (*int*) – 服务 port + * **host** (*str*) -- 服务 host + * **port** (*int*) -- 服务 port * **返回:** None @@ -219,8 +219,8 @@ conn.close() 将 component 服务化,提供 chainlit demo 页面 * **参数:** - * **host** (*str*) – 服务 host - * **port** (*int*) – 服务 port + * **host** (*str*) -- 服务 host + * **port** (*int*) -- 服务 port * **返回:** None @@ -229,9 +229,9 @@ conn.close() 执行一次对话 * **参数:** - * **message** ([*Message*](#appbuilder.core.message.Message)) – 该次对话用户输入的 Message - * **stream** (*bool*) – 是否流式请求 - * **\*\*args** – 其他参数,会被透传到 component + * **message** ([*Message*](#appbuilder.core.message.Message)) -- 该次对话用户输入的 Message + * **stream** (*bool*) -- 是否流式请求 + * **\*\*args** -- 其他参数,会被透传到 component * **返回:** 返回的 Message * **返回类型:** @@ -253,7 +253,7 @@ conn.close() 初始化 AgentRuntime,UserSession 会在这里被初始化 * **参数:** - **values** (*Dict*) – 初始化参数 + **values** (*Dict*) -- 初始化参数 * **返回:** None @@ -265,7 +265,7 @@ A dictionary of computed field names and their corresponding ComputedFieldInfo o Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict]. -#### model_fields *: ClassVar[dict[str, FieldInfo]]* *= {'component': FieldInfo(annotation=Component, required=True), 'tool_choice': FieldInfo(annotation=ToolChoice, required=False), 'user_session': FieldInfo(annotation=Union[Any, NoneType], required=False), 'user_session_config': FieldInfo(annotation=Union[Any, str, NoneType], required=False)}* +#### model_fields *: ClassVar[dict[str, FieldInfo]]* *= {'component': FieldInfo(annotation=Component, required=True), 'tool_choice': FieldInfo(annotation=ToolChoice, required=False, default=None), 'user_session': FieldInfo(annotation=Union[Any, NoneType], required=False, default=None), 'user_session_config': FieldInfo(annotation=Union[Any, str, NoneType], required=False, default=None)}* Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo]. @@ -281,7 +281,7 @@ This replaces Model._\_fields_\_ from Pydantic V1. * **返回:** 无 * **抛出:** - **无** – + **无** -- 说明: : 从 utils 文件夹中拷贝 chainlit.md 文件到当前工作目录下,如果当前工作目录下已存在 chainlit.md 文件,则不拷贝。 @@ -291,10 +291,10 @@ This replaces Model._\_fields_\_ from Pydantic V1. 将 component 服务化,提供 Flask http API 接口 * **参数:** - * **host** (*str*) – 服务运行的host地址,默认为’0.0.0.0’ - * **debug** (*bool*) – 是否开启debug模式,默认为True - * **port** (*int*) – 服务运行的端口号,默认为8092 - * **url_rule** (*str*) – 服务的URL规则,默认为”/chat” + * **host** (*str*) -- 服务运行的host地址,默认为'0.0.0.0' + * **debug** (*bool*) -- 是否开启debug模式,默认为True + * **port** (*int*) -- 服务运行的端口号,默认为8092 + * **url_rule** (*str*) -- 服务的URL规则,默认为"/chat" * **返回:** None @@ -306,7 +306,7 @@ This replaces Model._\_fields_\_ from Pydantic V1. ## appbuilder.core.message module -### *class* appbuilder.core.message.Message(content: \_T | None = None, \*, name: str | None = 'msg', mtype: str | None = 'dict', id: str | None = '88caf1fe-da8b-4dfb-a87c-32e88df1d5cb', \*\*data) +### *class* appbuilder.core.message.Message(content: \_T | None = None, \*, name: str | None = 'msg', mtype: str | None = 'dict', id: str | None = 'b197cfc9-234e-4811-8304-fb8783b77a92', \*\*data) 基类:`BaseModel`, `Generic`[`_T`] @@ -352,7 +352,7 @@ A dictionary of computed field names and their corresponding ComputedFieldInfo o Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict]. -#### model_fields *: ClassVar[dict[str, FieldInfo]]* *= {'content': FieldInfo(annotation=Union[~_T, NoneType], required=False, default={}), 'id': FieldInfo(annotation=Union[str, NoneType], required=False, default='88caf1fe-da8b-4dfb-a87c-32e88df1d5cb'), 'mtype': FieldInfo(annotation=Union[str, NoneType], required=False, default='dict'), 'name': FieldInfo(annotation=Union[str, NoneType], required=False, default='msg')}* +#### model_fields *: ClassVar[dict[str, FieldInfo]]* *= {'content': FieldInfo(annotation=Union[~_T, NoneType], required=False, default={}), 'id': FieldInfo(annotation=Union[str, NoneType], required=False, default='b197cfc9-234e-4811-8304-fb8783b77a92'), 'mtype': FieldInfo(annotation=Union[str, NoneType], required=False, default='dict'), 'name': FieldInfo(annotation=Union[str, NoneType], required=False, default='msg')}* Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo]. @@ -376,42 +376,42 @@ Component模块包括组件基类,用户自定义组件需要继承Component Component基类, 其它实现的Component子类需要继承该基类,并至少实现run方法. * **参数:** - * **meta** ([*ComponentArguments*](#appbuilder.core.component.ComponentArguments)) – component meta information. - * **secret_key** (*str*) – user authentication token. - * **gateway** (*str*) – backend gateway server address. - * **lazy_certification** (*bool*) – lazy certification flag. + * **meta** ([*ComponentArguments*](#appbuilder.core.component.ComponentArguments)) -- component meta information. + * **secret_key** (*str*) -- user authentication token. + * **gateway** (*str*) -- backend gateway server address. + * **lazy_certification** (*bool*) -- lazy certification flag. #### *async* abatch(\*args, \*\*kwargs) → List[[Message](#appbuilder.core.message.Message)] abatch method,待子类重写实现 * **参数:** - * **args** – list of arguments - * **kwargs** – keyword arguments + * **args** -- list of arguments + * **kwargs** -- keyword arguments #### *async* arun(\*args, \*\*kwargs) → [Message](#appbuilder.core.message.Message) | None arun method,待子类重写实现 * **参数:** - * **args** – list of arguments - * **kwargs** – keyword arguments + * **args** -- list of arguments + * **kwargs** -- keyword arguments #### batch(\*args, \*\*kwargs) → List[[Message](#appbuilder.core.message.Message)] batch method,待子类重写实现 * **参数:** - * **args** – list of arguments - * **kwargs** – keyword arguments + * **args** -- list of arguments + * **kwargs** -- keyword arguments #### create_langchain_tool(tool_name='', \*\*kwargs) create_langchain_tool method,将AB-SDK的Tool转换为LangChain的StructuredTool * **参数:** - * **tool_name** – string, optional, default is empty string - * **kwargs** – keyword arguments + * **tool_name** -- string, optional, default is empty string + * **kwargs** -- keyword arguments * **返回:** StructuredTool @@ -433,16 +433,16 @@ create_langchain_tool method,将AB-SDK的Tool转换为LangChain的StructuredTool run method,待子类重写实现 * **参数:** - * **inputs** – list of arguments - * **kwargs** – keyword arguments + * **inputs** -- list of arguments + * **kwargs** -- keyword arguments #### set_secret_key_and_gateway(secret_key: str | None = None, gateway: str = '') 设置密钥和网关地址。 * **参数:** - * **secret_key** (*Optional* *[**str* *]* *,* *optional*) – 密钥,默认为None。如果未指定,则使用实例当前的密钥。 - * **gateway** (*str* *,* *optional*) – 网关地址,默认为空字符串。如果未指定,则使用实例当前的网关地址。 + * **secret_key** (*Optional* *[**str* *]* *,* *optional*) -- 密钥,默认为None。如果未指定,则使用实例当前的密钥。 + * **gateway** (*str* *,* *optional*) -- 网关地址,默认为空字符串。如果未指定,则使用实例当前的网关地址。 * **返回:** None @@ -460,7 +460,7 @@ tool_desc method,待子类重写实现 tool_eval method,待子类重写实现 * **参数:** - **kwargs** – keyword arguments + **kwargs** -- keyword arguments #### tool_name() → List[str] diff --git a/docs/sphinx_md/appbuilder.md b/docs/API-Reference/Python/appbuilder.md similarity index 100% rename from docs/sphinx_md/appbuilder.md rename to docs/API-Reference/Python/appbuilder.md diff --git a/docs/sphinx_md/index.md b/docs/API-Reference/Python/index.md similarity index 100% rename from docs/sphinx_md/index.md rename to docs/API-Reference/Python/index.md diff --git a/docs/sphinx_md/modules.md b/docs/API-Reference/Python/modules.md similarity index 100% rename from docs/sphinx_md/modules.md rename to docs/API-Reference/Python/modules.md diff --git a/docs/basic_module/rag.md b/docs/Application/RAG/BasicKnowledge/rag.md similarity index 100% rename from docs/basic_module/rag.md rename to docs/Application/RAG/BasicKnowledge/rag.md diff --git a/docs/BasisModule/Components/animal_recognize/README.md b/docs/BasisModule/Components/animal_recognize/README.md new file mode 100644 index 000000000..536d4ca61 --- /dev/null +++ b/docs/BasisModule/Components/animal_recognize/README.md @@ -0,0 +1,112 @@ +# 动物识别 (AnimalRecognition) + +## 简介 +动物识别 (AnimalRecognition) 支持对于输入的一张图片(可正常解码),输出动物识别结果。 + +### 功能介绍 +* 识别动物名称 + + 识别近八千种动物,接口返回动物名称、置信度信息,支持自定义返回结果数, 并可获取识别结果对应的百科信息; + +### 应用场景 +* 拍照识图 + + 根据拍摄照片,识别图片中动物的名称,可配合其它识图能力对识别的结果进一步细化,提升用户体验,广泛应用于拍照识图类APP中。 + + +## 基本用法 + +我们选取一张可爱的大熊猫照片,使用动物识别组件进行识别。 + + +![大熊猫](https://bj.bcebos.com/v1/appbuilder/animal_recognize_test.png?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-24T12%3A19%3A16Z%2F-1%2Fhost%2F411bad53034fa8f9c6edbe5c4909d76ecf6fad6862cf937c03f8c5260d51c6ae +) + + +下面是动物识别的代码示例: +```python +import os +import appbuilder +import requests + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = '...' + +# 从BOS读取样例图片 +image_url = "https://bj.bcebos.com/v1/appbuilder/animal_recognize_test.png?" \ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-24T" \ + "12%3A19%3A16Z%2F-1%2Fhost%2F411bad53034fa8f9c6edbe5c4909d76ecf6fad68" \ + "62cf937c03f8c5260d51c6ae" +raw_image = requests.get(image_url).content +# 创建动物识别组件实例 +animal_recognition = appbuilder.AnimalRecognition() +# 执行识别操作并获取结果 +out = animal_recognition.run(appbuilder.Message(content={"raw_image": raw_image})) +print(out.content) +``` + + +## 参数说明 + +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 初始化参数 +无 + +### 调用参数 +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +|---------|---------|------|-----------------------------|------------------------------------------------| +| message | String | 是 | 输入的消息,用于模型的主要输入内容。这是一个必需的参数 | Message(content={"raw_image": b"待识别的图片字节流数据"}) | +|timeout| Float | 否 | HTTP超时时间,单位:秒 |1|| +| retry | Integer | 否 | HTTP重试次数 | 3 | + +### 响应参数 +| 参数名称 | 参数类型 | 描述 | 示例值 | +|--------|---------|------|---------------------------------------| +| result | Array[] | 返回结果 | [{"name":"国宝大熊猫","score":"0.975161"}] | +| +name | String | 动物名称 | "国宝大熊猫" | +| +score | String | 置信度 | "0.975161" | +### 响应示例 +```json +{ + "result":[ + { + "name":"国宝大熊猫", + "score":"0.975161" + }, + { + "name":"秦岭四宝", + "score":"0.0161979" + }, + { + "name":"团团圆圆", + "score":"0.00239265" + }, + { + "name":"圆仔", + "score":"0.00192277" + }, + { + "name":"棕色大熊猫", + "score":"0.00130296" + }, + { + "name":"小熊猫", + "score":"0.000275865" + } + ] +} +``` + +## 高级用法 + +目前该模块仅提供基础的动物识别功能。 + + +## 更新记录和贡献 +* 动物识别能力 (2024-01) \ No newline at end of file diff --git a/docs/BasisModule/Components/asr/README.md b/docs/BasisModule/Components/asr/README.md new file mode 100644 index 000000000..c9d9e9be4 --- /dev/null +++ b/docs/BasisModule/Components/asr/README.md @@ -0,0 +1,82 @@ +# 短语音识别-极速版 (Automatic Speech Recognition) + +## 简介 +短语音识别 (Automatic Speech Recognition) 可以将音频流实时识别为文字,并返回每句话的开始和结束时间,适用于手机语音输入、语音搜索、人机对话等语音交互场景。 + +### 功能介绍 +通过极速API接口,将语音识别为文字,毫秒级响应,快速返回识别结果。 + +### 特色优势 +采用领先国际的流式端到端建模方法SMLTA,近场普通话语音识别准确率可达98%;采用最新识别解码技术,识别速度提升5倍以上,极速返回识别结果;专有GPU服务集群、提供企业级的稳定服务,弹性灵活的高并发承载及高可靠性保障。 + +### 应用场景 +语音输入、语音搜索、人机对话等。 + +## 基本用法 + +下面是短语音识别的代码示例: + +```python +import os +import requests +import appbuilder +# 设置环境变量和初始化 +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = "..." + +asr = appbuilder.ASR() + +audio_file_url = "https://bj.bcebos.com/v1/appbuilder/asr_test.pcm?authorization=bce-auth-v1" \ + "%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-11T10%3A56%3A41Z%2F-1%2Fhost" \ + "%2Fa6c4d2ca8a3f0259f4cae8ae3fa98a9f75afde1a063eaec04847c99ab7d1e411" +audio_data = requests.get(audio_file_url).content +content_data = {"audio_format": "pcm", "raw_audio": audio_data, "rate": 16000} +msg = appbuilder.Message(content_data) +out = asr.run(msg) +print(out.content) + +# {'result': ['北京科技馆。']} +``` +## 参数说明 + +### 鉴权配置 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 初始化参数 + +无 + +### 调用参数 +|参数名称 |参数类型 |是否必须 |描述 | 示例值 | +|--------|--------|--------|----|--------| +|message |String |是 |输入的消息,用于模型的主要输入内容。这是一个必需的参数,语音时长最长为60S| Message(content={"raw_audio": b"..."}) | +|audio_format|String|是 |定义语言文件的格式,包括"pcm"、"wav"、"amr",默认值为"pcm"| pcm | +|rate|Integer|是 |定义录音采样率,固定值16000| 16000 | +|timeout| Float | 否 | HTTP超时时间,单位:秒 |1|| +|retry|Integer|是 |HTTP重试次数| 3 | + +### 响应参数 +|参数名称 | 参数类型 |描述 |示例值| +|--------|--------------|----|------| +|result | List[String] |返回结果|["北京科技馆。"]| +### 响应示例 +```json +{"result": ["北京科技馆。"]} +``` +### 错误码 +| 错误码 |描述| +|---|---| +| 0 |success| +| 2000 |data empty| + +## 高级用法 + +目前该模块仅提供基础的语音识别功能。 + + +## 更新记录和贡献 +* 短语音识别能力 (2023-12) diff --git a/docs/BasisModule/Components/dish_recognize/README.md b/docs/BasisModule/Components/dish_recognize/README.md new file mode 100644 index 000000000..543c95d27 --- /dev/null +++ b/docs/BasisModule/Components/dish_recognize/README.md @@ -0,0 +1,77 @@ +# 菜品识别(DishRecognition) + +## 简介 +菜品识别组件(DishRecognition)可以识别超过9千种菜品,可准确识别图片中的菜品名称、卡路里,适用于多种客户识别菜品的业务场景中。 + +### 功能介绍 +识别超过9千种菜品,适用于识别只含有单个菜品的图片,接口返回菜品的名称、卡路里等综合信息 + +### 特色优势 +识别精度高,响应速度快 + +### 应用场景 +1. 餐饮健康:根据拍摄照片,识别图片中菜品名称,获取菜品参考卡路里含量和百科信息,可结合识别结果进一步提供饮食推荐、健康管理方案等相关功能,增强用户体验,广泛应用于餐饮娱乐类和健康管理类APP中 +2. 智能结算:根据拍摄照片,识别图片中菜品名称和位置,提高结算效率,减少人工录入成本,广泛应用于餐饮行业中 + +## 基本用法 +通过如下示例代码可以快速开始使用菜品识别组件: + +示例图片为: + +![菜品识别示例图片](https://bj.bcebos.com/v1/appbuilder/dish_recognize_test.jpg?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-11T10%3A58%3A25Z%2F-1%2Fhost%2F7b8fc08b2be5adfaeaa4e3a0bb0d1a1281b10da3d6b798e116cce3e37feb3438) + +```python +import os +import requests +import appbuilder + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = '...' + +dish_recognition = appbuilder.DishRecognition() + +image_url = "https://bj.bcebos.com/v1/appbuilder/dish_recognize_test.jpg?" \ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-11T" \ + "10%3A58%3A25Z%2F-1%2Fhost%2F7b8fc08b2be5adfaeaa4e3a0bb0d1a1281b10da" \ + "3d6b798e116cce3e37feb3438" +raw_image = requests.get(image_url).content + +resp = dish_recognition(appbuilder.Message({"raw_image": raw_image})) +# 输出{'result': [{'name': '剁椒鱼头', 'calorie': '127'}]} +print(resp.content) +``` + +## 参数说明 +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 初始化参数 +无 + +### 调用参数 +|参数名称 |参数类型 |是否必须 |描述 | 示例值 | +|--------|--------|--------|----|--------| +|message |obj:`Message` |是 |待识别的图片字节流数据或url| Message(content={"raw_image": b"..."}) 或Message(content={"url": "..."}) | +|timeout| Float | 否 | HTTP超时时间,单位:秒 |1|| +|retry|Integer|否 |HTTP重试次数| 3 | + +### 响应参数 +|参数名称 |参数类型 |描述 |示例值| +|--------|--------|----|------| +|result |List[Object] |返回结果|[{"name": "剁椒鱼头", "calorie": "127"}]| +|result[0].name |String |菜品名称|剁椒鱼头| +|result[0].calorie |String |菜品卡路里含量|127| + + +### 响应示例 +```json +{"result": [{"name": "剁椒鱼头", "calorie": "127"}]} +``` + + +## 更新记录和贡献 +* 菜品识别 (2024-01) \ No newline at end of file diff --git a/docs/BasisModule/Components/doc_crop_enhance/README.md b/docs/BasisModule/Components/doc_crop_enhance/README.md new file mode 100644 index 000000000..892c424bb --- /dev/null +++ b/docs/BasisModule/Components/doc_crop_enhance/README.md @@ -0,0 +1,95 @@ +# 文档矫正增强 (DocCropEnhance) + +## 简介 +文档矫正增强 (DocCropEnhance) 可对图片中的文件、卡证、票据等内容进行四角点检测定位,提取主体内容并对其进行矫正,同时可选图片增强效果进一步提升图片清晰度,达到主体检测矫正并增强的目的,提升图片整体质量 +### 功能介绍 +* 文档矫正增强 + + 支持对文档中的文件、卡证等内容进行主体检测与矫正,同时可开启增强功能 +### 特色优势 +* 技术领先 + + 模型针对图片倾斜、弯曲等情况进行专项优化,鲁棒性强,在处理图像的同时,可完整保留原有文档内容 +* 能力丰富 + + 提供文档图片矫正、增强、去手写等多项能力,全方位提升图像质量,适用于采集质量把控、文字识别效果提升等多应用场景 +### 应用场景 + 旨在改善文档图像质量,提升可读性和可处理性,广泛应用于图像处理和分析、归档和数字化等领域 +## 基本用法 + +下面是文档矫正增强代码示例: + +示例图片为 + +![示例图片](https://bj.bcebos.com/v1/appbuilder/doc_enhance_test.png?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-24T12%3A51%3A09Z%2F-1%2Fhost%2F2020d2433da471b40dafa933d557a1ebe8abf28df78010f865e45dfcd6dc3951) + + +```python +import os +import appbuilder +import requests + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = '...' + +# 从BOS读取样例图片 +image_url = "https://bj.bcebos.com/v1/appbuilder/doc_enhance_test.png?" \ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01" \ + "-24T12%3A51%3A09Z%2F-1%2Fhost%2F2020d2433da471b40dafa933d557a1e" \ + "be8abf28df78010f865e45dfcd6dc3951" +raw_image = requests.get(image_url).content +# 创建文档矫正增强组件实例 +doc_crop_enhance = appbuilder.DocCropEnhance() +# 执行操作并获取结果 +out = doc_crop_enhance.run(appbuilder.Message(content={"raw_image": raw_image}),enhance_type=3) +print(out.content) +# {"image_processed": "...", 'points': [{'y': 1371, 'x': 0}, {'x': 0, 'y': 0}, {'x': 997, 'y': 0}, {'x': 994, 'y': 1371}]} +``` + + +## 参数说明 + +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 初始化参数 +无 + +### 调用参数 +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +|--------------|---------|------|-----------------------------------------------------------------------------------------------------------------------------|------------------------------------------------| +| message | String | 是 | 输入的消息,用于模型的主要输入内容。这是一个必需的参数 | Message(content={"raw_image": b"待识别的图片字节流数据"}) | +| enhance_type | Integer | 否 | 选择是否开启图像增强功能,如开启可选择增强效果,可选值如下:enhance_type =0:默认值,不开启增强功能,enhance_type = 1:去阴影,enhance_type = 2:增强并锐化,enhance_type = 3:黑白滤镜 | 0 | +|timeout| Float | 否 | HTTP超时时间,单位:秒 |1|| +| retry | Integer | 否 | HTTP重试次数 | 3 | + +### 响应参数 +| 参数名称 | 参数类型 | 描述 | 示例值 | +|-----------------|---------|-------------------------------------------|-----------------------------------------------------------------------------------------| +| image_processed | String | 返回处理后的图片,base64编码 | "..." | +| points | Array[] | 检测到的图片内主体在原图中的四角点坐标 | [{ "x": 0, "y": 1371 },{ "x": 0, "y": 0 },{ "x": 997, "y": 0 },{ "x": 994, "y": 1371 }] | + +### 响应示例 +```json +{ + "image_processed": "...", + "points": [ + { "x": 0, "y": 1371 }, + { "x": 0, "y": 0 }, + { "x": 997, "y": 0 }, + { "x": 994, "y": 1371 } + ] +} +``` + +## 高级用法 + +目前该模块仅提供基础的文档矫正增强功能。 + + +## 更新记录和贡献 +* 文档矫正增强能力 (2024-01) \ No newline at end of file diff --git a/docs/BasisModule/Components/doc_format_converter/README.md b/docs/BasisModule/Components/doc_format_converter/README.md new file mode 100644 index 000000000..97623e0fb --- /dev/null +++ b/docs/BasisModule/Components/doc_format_converter/README.md @@ -0,0 +1,71 @@ +# 文档格式转换 (DocFormatConverter) + +## 简介 +文档格式转换:识别文档内文字及版面布局,可将多种类型的版式文档转换为流式文档。 + +### 功能介绍 +支持识别图片中文档版面布局,提取文字内容,并转换为保留原文档版式的Word/Excel,方便二次编辑和复制。 + +### 特色优势 +1、多种格式互转:支持多种格式相互转换,覆盖全面; + +2、图像预处理:支持对文件朝向检测、印章/水印去除后等预处理,提升格式转换效果。 + +### 应用场景 +文档电子化:标题/正文/表格/配图等版式信息精准识别与还原,快速录入文档内容,实现纸质档案电子化。 + +## 基本用法 + + +```python +import os +import requests +import appbuilder + + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = '...' + +doc_format_converter = appbuilder.DocFormatConverter() + +image_url = "https://ai-cape-strategy-data.bj.bcebos.com/document-restructure/1EF33F9307451C9413D5D1160.jpg" + +resp = doc_format_converter(appbuilder.Message({"file_path": image_url})) +# 输出{"word_url":"", "excel_url":""} +print(resp.content) +``` + +## 参数说明 + +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 + +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + + +### 初始化参数 +无 + +### 调用参数 +| 参数名称 |参数类型 |是否必须 | 描述 | 示例值 | +|------------|--------|--------|-------------|------| +| file_path |String |是 | 需要转换的文件的本地存储路径或远程URL,支持图片, URL长度不超过1024字节,图片base64编码后大小不超过4M,最短边至少15px,最长边最大4096px,支持jpg/jpeg/png/bmp格式 | "./test.png" | + + +### 响应参数 +| 参数名称 |参数类型 | 描述 | 示例值 | +|-------------|--------|------|-------------------------| +| word_url |Message | 还原后的word文件的下载地址,文件识别失败时返回空 | "http://bos.bce.cn/dsfkjc.docx"| +| excel_url |Message | 还原后的Excel文件的下载地址(源文件中含表格时才会输出),若文档中没有表格则返回"" | "http://bos.bce.cn/dsfkjc.xlsx"| + +### 常见错误信息 +| 错误信息 | 描述 | +|-------------------------|-------------| +|IAM Certification failed |IAM鉴权失败| +|Check file failed!|文件检查错误,请检查文件大小以及URL是否符合要求 | + +## 更新记录和贡献 +* 文档格式转换 (2024-04) \ No newline at end of file diff --git a/docs/BasisModule/Components/doc_parser/README.md b/docs/BasisModule/Components/doc_parser/README.md new file mode 100644 index 000000000..2fb605139 --- /dev/null +++ b/docs/BasisModule/Components/doc_parser/README.md @@ -0,0 +1,210 @@ +# 文档解析(DocParser) + +## 简介 +文档解析组件(DocParser)可以用于文档内容解析,支持PDF、JPG、DOC、TXT、XLS、PPT等16种文档格式的内容解析。 + +### 功能介绍 +文档解析组件(DocParser)支持从文档中解析出文档字符内容、版式信息、位置坐标、表格结构、阅读顺序、标题段落层级树等内容 + +### 特色优势 +DocParser支持解析以下几种类型的文档: +* 版式文档:「pdf」、「jpg」、「jpeg」、「png」、「bmp」、「tif」、「tiff」、「ofd」 +* 流式文档:「doc」、「docx」、「txt」、「xls」、「xlsx」、「wps」、「ppt」、「pptx」 + +支持解析的文档内容包括: +* 文档的版式分析,识别文档中的标题、正文、页眉页脚、表格等 +* 文档内的文字内容、位置坐标 +* 表格结构和内容 +* 构建文档标题段落层级树 +* 构建文档阅读顺序 +* 支持以上类型文档转成pdf格式 + +### 应用场景 +* 文档内容解析 +* 文档版式解析 +* 文档格式转化 + +## 基本用法 + +以下是使用DocParser快速开始的一个示例: + +```python +from appbuilder.core.components.doc_parser.doc_parser import DocParser +from appbuilder.core.message import Message +import os +import requests + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = "..." + +# 进行文档内容解析 +file_url = "https://agi-dev-platform-bos.bj.bcebos.com/ut_appbuilder/test.pdf?authorization=bce-auth-v1/e464e6f951124fdbb2410c590ef9ed2f/2024-01-25T12%3A56%3A15Z/-1/host/b54178fea9be115eafa2a8589aeadfcfaeba20d726f434f871741d4a6cb0c70d" +file_data = requests.get(file_url).content +file_path = "./test.pdf" # 待解析的文件路径 +with open(file_path, "wb") as f: + f.write(file_data) +msg = Message(file_path) +parser = DocParser() +parse_result = parser(msg) +print(parse_result.content) +``` + +## 参数说明 + +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` +### 初始化参数 +无 + +### 调用参数 +| 参数名称 |参数类型 |是否必须 | 描述 | 示例值 | +|------------|--------|--------|-------------|------| +| message |String |是 | 需要解析的文档的存储路径 | "./test.pdf" | +| return_raw |bool|否 | 指定是否返回原始的解析结果结构,默认为 False。 | True | + +### 响应参数 +| 参数名称 |参数类型 | 描述 | 示例值 | +|-------------|--------|------|-------------------------| +| parseResult |Message | 解析结果 | ParseResult对象,包含文档解析的内容 | +### 响应示例 +```python +class ParseResult(BaseModel): + """ + 解析结果整体结构 + """ + para_node_tree: Optional[List[ParaNode]] = [] # 标题段落层级树,当ParserConfig.return_para_node_tree为True时有内容 + page_contents: Optional[List[PageContent]] = [] # 页面的解析内容,详细内容参考base.py中的PageContent类 + pdf_data: Optional[str] = "" # pdf格式数据, 当ParserConfig.convert_file_to_pdf为True时有内容 + raw: Optional[Dict] = {} # 云端服务的原始解析结果 +``` + +## 高级用法 +DocParser支持自定义文档解析的配置和对解析结果进行二次处理,以下是一个示例: + +```python +from appbuilder.core.components.doc_parser.doc_parser import DocParser +from appbuilder.core.message import Message +import os +import requests + +# 设置环境变量 +os.environ["APPBUILDER_TOKEN"] = "..." + +# 先进行文档内容解析 +file_url = "https://agi-dev-platform-bos.bj.bcebos.com/ut_appbuilder/test.pdf?authorization=bce-auth-v1/e464e6f951124fdbb2410c590ef9ed2f/2024-01-25T12%3A56%3A15Z/-1/host/b54178fea9be115eafa2a8589aeadfcfaeba20d726f434f871741d4a6cb0c70d" +file_data = requests.get(file_url).content +file_path = "./test.pdf" # 待解析的文件路径 +with open(file_path, "wb") as f: + f.write(file_data) +msg = Message(file_path) + +parser = DocParser() +config = parser.config +config.convert_file_to_pdf = True # 指定将当前文件转换成pdf格式 +config.page_filter = [0, 2] # 只解析第1页和第3页,注意:页码从0开始 + +parse_result = parser(msg) +file_content = parse_result.content +pdf_data = file_content.pdf_data # 获取原始文件转化成pdf之后的数据 +page_content = file_content.page_content[1] # 获取第3页的解析结果 +page_table = page_content.tables[0] # 第3页中第一个表格的解析结果(如有),表格的解析内容的结构详见上一章详细说明部分关于表格结果的说明 +cells = page_table.cells # 表格的单元格信息 +cell_text = cells[0] # 表格第一个单元格的文本内容 +matrix = page_table.cell_matrix # 表格的单元格矩阵,用来描述单元格的空间位置信息 +... +自定义处理表格内容 +... +``` +### 高级用法参数详细说明 + +在base.py中定义了DocParser配置和结果结构,下面做一些详细的说明和解释: +### DocParser配置 +```python +class ParserConfig(BaseModel): + """ + DocParser解析配置 + """ + convert_file_to_pdf: bool = Field(alias="need_pdffile_data", default=False) # 是否需要将当前文件转换成pdf格式 + page_filter: List[int] = Field(alias="page_filter", default=None) # 指定解析的页码,默认传None,代表全部解析 + return_para_node_tree: bool = Field(alias="return_para_nodes", default=True) # 是否需要返回标题段落层级树 + erase_watermark: bool = Field(alias="erase_watermark", default=False) # 解析的过程中是否需要去除水印的干扰 +``` +### DocParser解析结果 +```python +class ParseResult(BaseModel): + """ + 解析结果整体结构 + """ + para_node_tree: Optional[List[ParaNode]] = [] # 标题段落层级树,当ParserConfig.return_para_node_tree为True时有内容 + page_contents: Optional[List[PageContent]] = [] # 页面的解析内容,详细内容参考base.py中的PageContent类 + pdf_data: Optional[str] = "" # pdf格式数据, 当ParserConfig.convert_file_to_pdf为True时有内容 + raw: Optional[Dict] = {} # 云端服务的原始解析结果 + +class ParaNode(BaseModel): + """ + 文档内容层级树结构 + """ + node_id: int # 标题段落层级树的节点id + text: str # 节点文本 + para_type: str # 节点类型,包括:title、text、table + parent: Optional[int] # 父节点id,文本的父节点是标题,标题的父节点是更高一级的标题 + children: List[int] # 子节点id列表,标题节点才会有子节点 + position: List[Position] # 节点位置信息,包括:页码和在对应页面的位置坐标 + table: Optional[Table] = None # 节点类型为table时,包含表格解析结果 + +class PageContent(BaseModel): + """ + 单页文档内容结构 + """ + page_num: int # 页码 + page_width: int # 页面宽度 + page_height: int # 页面高度 + page_angle: int # 页面旋转角度 + page_type: str # 页面类型 + page_layouts: List[Layout] # 页面版式信息 + titles: Optional[List[Layout]] = [] # 页面标题信息 + tables: Optional[List[Table]] = [] # 页面表格信息 + +class Layout(BaseModel): + """ + layout结构 + """ + type: str # 布局类型 + text: str # 布局文本 + box: List[int] # 布局位置信息,包括:左上角x、y坐标和宽高 + node_id: int # 布局在标题层级树中的节点id + +class Table(BaseModel): + """ + 表格结构 + """ + box: List[int] # 表格位置信息,包括:左上角x、y坐标和宽高 + cells: List[Layout] = Field(alias="children") # 表格单元格信息,列表形式 + matrix: List[List[int]] # 表格单元格矩阵,用来描述单元格的空间位置信息 + node_id: int # 表格在标题层级树中的节点id +``` +表格解析结构说明, 以下图为例: + +![表格](https://bj.bcebos.com/v1/appbuilder-sdk-components/table.png?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-05-30T03%3A09%3A32Z%2F-1%2Fhost%2Fb36695708e047713b5fe17b49733228aecdaf46322a5cec8d4e7bd0989032197) + +```python +# cells中一共有26个元素,matrix中的每一个元素代表单元格在cells中的索引 +cells = [{"box": [90, 376, 21, 10], "type": "cell", "text": "序号", "node_id": 1}, ...] +matrix = [ + [0, 1, 2, 3], + [4, 5, 6, 7], + [8, 9, 10, 11], + [12, 13, 14, 15], + [16, 17, 18, 19], + [20, 21, 22, 23], + [24, 24, 25, 26] + ] +``` + + +## 更新记录和贡献 +* 文档解析能力 (2023-12) \ No newline at end of file diff --git a/docs/BasisModule/Components/doc_splitter/README.md b/docs/BasisModule/Components/doc_splitter/README.md new file mode 100644 index 000000000..01185f1a6 --- /dev/null +++ b/docs/BasisModule/Components/doc_splitter/README.md @@ -0,0 +1,163 @@ +# 文档切分(DocSplitter) + +## 简介 +文档切分组件(DocSplitter)可以用于对文档进行段落切分。 + +### 功能介绍 +对解析后的文档,支持将文档划分为多个段落,便于后续处理和分析。 +目前支持的文档切分类型splitter_type如下: +* split_by_chunk:按照最大段落大小,对文档进行切分 +* split_by_title:按照文档的title标识层级进行段落切分 + +### 特色优势 +组件对文档分隔段落,准确高效,且有多种可选策略,代码简单可快速上手,是后续大模型使用文档信息的基础。 + + +### 应用场景 +对解析后的各类型文档进行分段,用于后续任务的输入。 + + +## 基本用法 +--- +参考tests目录下的[test_doc_splitter.py](https://github.com/baidubce/app-builder/blob/master/appbuilder/tests/test_doc_splitter.py),可快速搭建自己的文档切分用例。 + +以下是DocSplitter快速开始的一个示例。 + +#### DocSplitter示例: + +```python +import os +import requests +from appbuilder.core.components.doc_parser.doc_parser import DocParser +from appbuilder.core.components.doc_splitter.doc_splitter import DocSplitter +from appbuilder.core.message import Message + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = "..." + +# 先解析 +file_url = "https://agi-dev-platform-bos.bj.bcebos.com/ut_appbuilder/test.pdf?authorization=bce-auth-v1/e464e6f951124fdbb2410c590ef9ed2f/2024-01-25T12%3A56%3A15Z/-1/host/b54178fea9be115eafa2a8589aeadfcfaeba20d726f434f871741d4a6cb0c70d" +file_data = requests.get(file_url).content +file_path = "./test.pdf" +with open(file_path, "wb") as f: + f.write(file_data) + +msg = Message(file_path) + +parser = DocParser() +parse_result = parser(msg, return_raw=True) + +# 基于parser的结果切分段落 +splitter = DocSplitter(splitter_type="split_by_chunk") +res_paras = splitter(parse_result) + +# 打印结果 +print(res_paras.content) +``` +## 参数说明 + +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 初始化参数 +`splitter_type`(str): 切分器的类型,支持`split_by_chunk`和`split_by_title`两种方式,必选参数 + +### 调用参数 +* `message`(Message): 上游`docparser`的文档解析结果 +* 备注: 文档解析时,`parser(msg, return_raw=True)`函数的参数`return_raw`必须为`True` + +|参数名称 |参数类型 |是否必须 |描述 | 示例值 | +|--------|--------|--------|----|--------| +|splitter_type |String |是 |文本提取器类型, 目前支持`split_by_chunk`, `split_by_title`| DocSplitter(splitter_type="split_by_chunk") | + + +### 响应参数 +|参数名称 | 参数类型 |描述 | 示例值 | +|--------|------|----|----------------| +|res_paras |Message |返回结果| [{段落1}, {段落2}] | + +### 响应示例 +``` +Message(name=msg, content={'paragraphs': [{'text': '第十节其他重要事项'}]}) +``` + +### 错误码 +无 + + +## DocSplitter高级用法 + +#### 示例: + +```python +import os +import requests +from appbuilder.core.components.doc_parser.doc_parser import DocParser +from appbuilder.core.components.doc_splitter.doc_splitter import DocSplitter +from appbuilder.core.message import Message + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = "..." + +# 先解析 +file_url = "https://agi-dev-platform-bos.bj.bcebos.com/ut_appbuilder/test.pdf?authorization=bce-auth-v1/e464e6f951124fdbb2410c590ef9ed2f/2024-01-25T12%3A56%3A15Z/-1/host/b54178fea9be115eafa2a8589aeadfcfaeba20d726f434f871741d4a6cb0c70d" +file_data = requests.get(file_url).content +file_path = "./test.pdf" +with open(file_path, "wb") as f: + f.write(file_data) + +msg = Message(file_path) + +parser = DocParser() +parse_result = parser(msg, return_raw=True) + +# 基于parser的结果切分段落 +doc_splitter = DocSplitter(splitter_type="split_by_chunk", + separators=["。", "!", "?", ".", "!", "?", "……", "|\n"], + max_segment_length=800, + overlap=0) +res_paras = doc_splitter(parse_result) + +# 打印结果 +print(res_paras.content) +``` +## 参数说明: + +### 鉴权配置 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 调用参数 +* `message`(Message): 上游`docparser`的文档解析结果 +* 备注: 文档解析时,`parser(msg, return_raw=True)`函数的参数`return_raw`必须为`True` +* 备注: `splitter_type`为`split_by_title`时,`max_segment_length`, `separators`, `overlap`, `join_symbol`参数不起作用 + +|参数名称 | 参数类型 | 是否必须 |描述 | 示例值 | +|--------|---------|------|----|-------| +|splitter_type | String | 是 |文本提取器类型, 目前支持`split_by_chunk`, `split_by_title`| DocSplitter(splitter_type="split_by_chunk") | +|max_segment_length| Integer | 否 |切分时段落的最大长度| 800 | +|separators| List | 否 |固定字数时,段落最后截断的分隔符| ["。", "!", "?", ".", "!", "?", "……", "|\n"] | +|overlap| Integer | 否 |分隔的段落间重叠的内容字数| 200 | +|join_symbol| String | 否 |组成固定字数段落时,文本块段落间的链接符| 空字符 | + +### 响应参数 +|参数名称 | 参数类型 |描述 | 示例值 | +|--------|------|----|----------------| +|res_paras |Message |返回结果| [{段落1}, {段落2}] | + +### 响应示例 +``` +Message(name=msg, content={'paragraphs': [{'text': '第十节其他重要事项'}]}) +``` + +## 更新记录和贡献 +* 文档分隔 (2023-12) + + diff --git a/docs/BasisModule/Components/document_understanding/README.md b/docs/BasisModule/Components/document_understanding/README.md new file mode 100644 index 000000000..1bd9333ff --- /dev/null +++ b/docs/BasisModule/Components/document_understanding/README.md @@ -0,0 +1,96 @@ +# 长文档内容理解(DocumentUnderstanding) + +## 简介 +长文档内容理解组件(DocumentUnderstanding)支持对图片以及文档内容进行理解,并基于图片以及文档内容对用户的提问进行回答, +包括但不限于文档内容问答、总结摘要、内容分析。 +### 功能介绍 +根据用户上传的文档(支持txt、docx、pdf、xlsx、png、jpg、jpeg等多种格式)、query、指令生成大模型答案 +### 特色优势 +处理长上下文的大模型内容理解任务 +### 应用场景 +长上下文的文档问答 + +## 基本用法 +### 快速开始 + +```python + +import os +import appbuilder + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +APPBUILDER_TOKEN = "YOUR-TOKEN" +os.environ["APPBUILDER_TOKEN"] = APPBUILDER_TOKEN +du = appbuilder.DocumentUnderstanding() +query = appbuilder.Message("这篇文档讲了什么") +instruction = "请根据文档内容回答问题,用一句话简短概括" +addition_instruction = "用一句话简短概括" ##用户增强指令,可选填,该内容会进一步增强大模型的指令跟随能力,将你最需要增强效果的指令填于此,内容可以与上述的"instruction"基础指令有重复,注意:该字段内容过多会一定程度影响大模型内容严谨度,请注意控制该字段的指令字数 +app_id = "YOUR-APP-ID" ##你需要在系统上自己的账号下(https://qianfan.cloud.baidu.com/appbuilder)创建任意空Agent,并获取该Agent的app_id(即界面上的应用ID,在首页->个人空间->应用 里面即会显示应用ID),这里任意空Agent就可以,无需任何配置信息,这个agent的作用只是为了获取app_id信息 +file_path = "YOUR-FILE-PATH" ##填写你的本地待分析文件路径 +stream = False ##是否开启流式输出功能 +response_ = du.run(query, + file_path, + instruction=instruction, + addition_instruction=addition_instruction, + app_id=app_id, + stream=stream) + +for result in response_: + print(result) ##打印输出的大模型答案 +``` + + +## 参数说明 +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +import os +os.environ['APPBUILDER_TOKEN'] = 'bce-YOURTOKEN' +``` + + +### 初始化参数 + +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +| ------- | ------- | -------- | -------- | -------- | +| `secret_key` | str | 否 | 用户鉴权token,默认从环境变量中获取: `os.getenv("APPBUILDER_TOKEN", "")` | bce-v3/XXX | +| `gateway` | str | 否 | 后端网关服务地址,默认从环境变量中获取: `os.getenv("GATEWAY_URL", "")` | https://appbuilder.baidu.com | +| `lazy_certification` | bool | 否 | 延迟认证,为True时在第一次运行时认证。默认为False。 | False | + + +### 调用参数 + +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +|------------------------|------|------|--------------------------------------------------------------------------|-----------------------------| +| `message` | obj | 是 | 输入消息,用户输入query。 | Message(content=input_data) | +| `file_path` | str | 是 | 用户需要分析的文档 | "test.pdf" | +| `app_id` | str | 是 | 你需要在系统上自己的账号下(https://qianfan.cloud.baidu.com/appbuilder)创建任意空Agent,并获取该Agent的app_id(即界面上的应用ID,在首页->个人空间->应用 里面即会显示应用ID),这里任意空Agent就可以,无需任何配置信息,这个agent的作用只是为了获取app_id信息 | "YOUR-APP-ID" | +| `instruction` | str | 否 | 用户指令 | "你的回答要严谨客观,且答案一定要分点阐述" | +| `addition_instruction` | str | 否 | 用户增强指令,可选填,该内容会进一步增强大模型的指令跟随能力,将你最需要增强效果的指令填于此,注意:该字段内容过多会一定程度影响大模型内容严谨度 | "你的答案需要分点阐述" | + +### 响应参数 +| 参数名称 | 参数类型 | 描述 | 示例值 | +| ------- |------| -------- | -------- | +| `result` | str | 模型运行后的输出结果 | "" | + +### 响应示例-流式输出 +``` +data: {"type": "text", "text": "文件解析完成, 耗时13485.63ms\n\n"} request_id: f99a7230-649f-4170-ade7-62d8368a18e6 +data: {"type": "text", "text": "**Human", "event_status": "running"} request_id: f99a7230-649f-4170-ade7-62d8368a18e6 +data: {"type": "text", "text": "-Timescale Adaptation in an Open-Ended Task Space** 文档详细介绍了DeepMind团队开发的自适应代理(Adaptive Agent,简称", "event_status": "running"} request_id: f99a7230-649f-4170-ade7-62d8368a18e6 +data: {"type": "text", "text": "AdA)在开放任务空间中的快速适应能力。", "event_status": "running"} request_id: f99a7230-649f-4170-ade7-62d8368a18e6 +data: {"type": "text", "text": "", "event_status": "done"} request_id: f99a7230-649f-4170-ade7-62d8368a18e6 +``` + +### 响应示例-非流式输出 +``` +{'code': 0, 'message': '', 'result': {'text': '文件解析完成, 耗时14572.57ms\n\n**Human-Timescale Adaptation in an Open-Ended Task Space** 文档详细介绍了DeepMind团队开发的自适应代理(Adaptive Agent,简称AdA)在开放任务空间中的快速适应能力。以下是文档的主要内容和贡献点:\n\n1. **引言**:\n - 强调了快速适应能力对于人工智能的重要性,特别是在现实世界中的应用和与人类互动的场景中。\n - 提出了通过元强化学习(meta-RL)和自动课程学习(auto-curriculum learning)等方法,训练能够在未见过的环境中快速适应的代理。\n\n2. **自适应代理(AdA)**:\n - 介绍了AdA的设计和训练方法,包括其在开放任务空间中的适应行为、记忆架构、以及如何通过自动课程学习来优化训练过程。\n - 展示了AdA能够在几分钟内解决复杂的3D任务,且不需要进一步的代理训练,显示了其快速适应的能力。\n\n3. **实验与结果**:\n - 在多个方面评估了AdA的性能,包括其在单代理和多代理设置下的适应能力、不同架构和课程学习方法的影响、以及模型大小和记忆长度对性能的影响。\n - 通过与人类玩家的比较,证明了AdA在适应速度上与人类相当。\n\n4. **相关工作**:\n - 回顾了与本工作相关的领域,包括程序化环境生成、开放任务学习、适应性和强化学习中的Transformer应用等。\n\n5. **结论**:\n - 总结了AdA的贡献,强调了其在开放任务空间中快速适应的能力,以及通过元强化学习和自动课程学习等方法训练大型模型的可能性。\n\n6. **作者和贡献**:\n - 列出了主要贡献者和部分贡献者,以及项目的赞助商和认可。\n\n**主要贡献点**:\n- 提出了AdA,一个能够在开放任务空间中快速适应的代理,其适应速度与人类相当。\n- 通过元强化学习和自动课程学习等方法,训练了大型Transformer模型,展示了其在开放任务空间中的快速适应能力。\n- 分析了不同架构、课程学习方法、模型大小和记忆长度对AdA性能的影响,提供了详细的实验结果和比较。\n- 通过与人类玩家的比较,证明了AdA在适应速度上的优势。'}, 'request_id': '687642b0-b877-49ed-9ad9-65d76de0ea58'} +``` + +## 高级用法 + +## 更新记录和贡献 +### 2024.10. 15 +#### [Added] +- 第一版 \ No newline at end of file diff --git a/docs/BasisModule/Components/embeddings/README.md b/docs/BasisModule/Components/embeddings/README.md new file mode 100644 index 000000000..f3c54390d --- /dev/null +++ b/docs/BasisModule/Components/embeddings/README.md @@ -0,0 +1,156 @@ +# 向量计算(Embedding) + +## 简介 + +向量计算组件(Embedding)支持将文本转化为用数值表示的向量形式,用于文本检索、信息推荐、知识挖掘等场景。嵌入(Embedding)是一种在机器学习和自然语言处理中常用的技术,主要用于将大量高维数据(如单词、图像等)转换为更低维的向量表示。这些向量表示捕获了原始数据的关键特征和关系。 + +### 功能介绍 + +1. 维度降低:将高维数据(如词汇表中的单词)映射到低维空间,使得数据处理更高效。 +2. 特征学习:学习数据的内在特征,使得具有相似含义的元素在嵌入空间中彼此接近。 +3. 关系映射:在嵌入空间中,数据点的距离和方向可以表示元素之间的关系。 + +### 特色优势 + +Embedding-V1,是基于百度文心大模型技术的文本表示模型,在Embedding模块中,我们使用Embedding-V1作为默认模型。 + +### 应用场景 + +1. 文本检索 +2. 信息推荐 +3. 知识挖掘 + +## 基本用法 + +当前支持的embedding底座模型暂时只包括: +- embedding-v1 + +### 下面是使用单条字符串测试的代码示例 + +请注意,您必须确保字符串的token长度小于384 + +```python +import os +import appbuilder + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = '...' + +embedding = appbuilder.Embedding() + +out = embedding("hello world!") +# 得到一个长度为384的float数组 +print(out.content) +``` + +### 下面是使用多条字符串测试的代码示例 + +```python +import os +import appbuilder + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = '...' + +embedding = appbuilder.Embedding() + +outs = embedding.batch(["hello", "world"]) +# 得到一个长度为 2 x 384的float 二维数组 +print(out.content) +``` + +### 下面是使用上游的Message作为输入的代码示例 + +```python +import os +import appbuilder + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = '...' + +from appbuilder import Message + +embedding = appbuilder.Embedding() + +query = Message("你好,世界!") +out = embedding(query) +# 得到一个长度为384的float数组 +print(out.content) +``` + +### 下面是批量运行的代码示例 + +```python +import os +import appbuilder + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = '...' + +from appbuilder import Message + +embedding = appbuilder.Embedding() + +query = Message([ + "你好", + "世界" +]) +outs = embedding.batch(query) +# 得到一个长度为 2 x 384的float 二维数组 +print(outs.content) +``` + +## 参数说明 + +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 初始化参数 + +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +| -------- | -------- | -------- | ------------------------------------------------------------ | ---------------- | +| model | 字符串 | 可选 | 指定底座模型的类型。当前仅支持 embedding-v1 作为可选值。若不指定,默认值为 embedding-v1。 | embedding-v1 | + +### 调用参数 + +#### 单条 + +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +| -------- | -------- | -------- | ------------------------------------------------------------ | ---------------- | +| text | 字符串 | 必须 | 一个类型为 string 的句子,用于输入。该句子的长度不能超过384个字符,通常为用户的输入。 | "您好,我需要帮助。" | + +#### 批量 + +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +| -------- | --------------- | -------- | ---------------------------------------------------------------- | ------------------------------------ | +| texts | 字符串列表 | 必须 | 一个类型为 List[string] 的句子数组。数组中的每个元素都是一个句子,且每个句子的长度不能超过384个字符。通常这些句子为和用户输入相关的文本候选集。 | ["您好,我需要帮助。", "请问有什么可以帮您?"] | + +### 响应示例 + +#### 单条 + +``` +[0.1, 0.2, 0.3, ....] +``` + +#### 批量 + +``` +[ + [0.1, 0.2, ...], + ..., + [0.1, 0.2, ...], +] +``` + +### 错误码 + +无 + +## 更新记录和贡献 + +* embedding-v1 (2023-12) diff --git a/docs/BasisModule/Components/excel2figure/README.md b/docs/BasisModule/Components/excel2figure/README.md new file mode 100644 index 000000000..0e1d5565a --- /dev/null +++ b/docs/BasisModule/Components/excel2figure/README.md @@ -0,0 +1,86 @@ +# Excel转图表(Excel2Figure) + +## 简介 +Excel转图表(Excel2Figure)组件通过理解对表格信息的提问,生成对应语义的图表。 + +### 功能介绍 +Excel2Figure 是一个高级的数据可视化工具,它结合了大语言模型的强大语义理解能力,以帮助用户将 Excel 表格数据转换成直观、易理解的图表。用户只需通过自然语言描述他们想要呈现的数据和图表类型,Excel2Figure 会解析这些指令,自动从Excel数据中提取相关信息,生成符合用户需求的图表。 + +### 特色优势 +- 强大的语义理解:利用文心一言大语言模型的先进技术,Excel2Figure能够理解复杂的自然语言指令,包括数据筛选、分析需求和图表类型等。 +- 用户友好的交互:用户可以用自己熟悉的语言描述数据可视化需求,无需学习复杂的软件操作或编程语言。 +- 支持多样化的图表类型:根据用户的自然语言描述,Excel2Figure能够生成多种类型的图表,包括但不限于柱状图、线形图、饼图、散点图等。 +- 快速准确的数据处理:通过语义理解快速定位和处理Excel中的数据,大大减少了数据准备的时间和出错的可能性。 + +### 应用场景 +- 学术研究:研究人员可以简单描述他们需要的图表类型和数据集,Excel2Figure将帮助他们将研究数据可视化,以便在学术论文或演讲中展示。 +- 市场趋势分析:市场分析师利用Excel2Figure快速生成展示市场调查结果和消费者行为分析的图表,帮助团队理解市场动态。 +- 教育用途:教师可以利用这个工具将复杂的数据集转换为学生更容易理解的图表,提高教学效果和学生的学习兴趣。 +- 个人数据管理和展示:个人用户可以使用Excel2Figure来跟踪和展示自己的财务状况、健康数据或任何其他类型的个人记录。 + + +## 基本用法 + +### 快速开启 + +```python +import appbuilder +import os + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +# 设置环境变量 +os.environ["APPBUILDER_TOKEN"] = '...' + +# 创建 component 对象,推荐使用 ERNIE-Bot 4.0 获取更稳定的画图效果 +component = appbuilder.Excel2Figure(model="ERNIE-Bot 4.0") + +# 准备 excel 文件链接,该链接需要是公网可访问的地址 +excel_file_url = "https://agi-dev-platform-bos.bj.bcebos.com/ut_appbuilder/[测试]超市收入明细表格.xlsx?authorization=bce-auth-v1/e464e6f951124fdbb2410c590ef9ed2f/2024-02-21T09%3A51%3A14Z/-1/host/1802a9c9142ef328d61e7673db7c1f05842b2af93d18a02ac7ef7aa6f64db54e" + +# 针对 excel 文件内容绘制图表 +result = component.run(appbuilder.Message({ + "query": "2020年各个月份的利润分别是多少?使用条形图绘制出来", + "excel_file_url": excel_file_url, +})) + +# 输出运行结果 +print(result) +# Message(name=msg, content="http://可访问的文件地址...", mtype=str) +``` + +如果绘图成功,预期结果为一个可访问的文件地址,文件链接**过期时间限制为24小时**;如果绘图失败,预期结果为空字符串,需要调整query。 + +这里给出上述代码运行得到的图表(每次运行结果可能会发生变化,仅供参考): +![2020年各个月份利润条形图.png](https://agi-dev-platform-bos.bj.bcebos.com/ut_appbuilder/%5B%E6%B5%8B%E8%AF%95%5D2020%E5%B9%B4%E5%90%84%E4%B8%AA%E6%9C%88%E4%BB%BD%E5%88%A9%E6%B6%A6%E6%9D%A1%E5%BD%A2%E5%9B%BE.png?authorization=bce-auth-v1/e464e6f951124fdbb2410c590ef9ed2f/2024-02-21T10%3A00%3A16Z/-1/host/b68f35825ad99075caf8bd009e4871ee9eb7b718e550968fdf12695b1502bc78) + + +## 参数说明 + +### 初始化参数 +- `model`: 模型名称,用于指定要使用的千帆模型。 + +### 调用参数 +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +|--------|--------|---|----|------------------------------------------| +| msg | Message | 是 | 输入消息,包含用户提出的问题 query 和一个公网可访问的 excel 文件链接 excel_file_url。| - | +| +query | String | 是 | 用户提出的问题,长度小于 400 | "2020年各个月份的利润分别是多少?使用条形图绘制出来" | +| +excel_file_url | String | 是 | 公网可访问的 excel 文件链接 | "https://agi-dev-platform-bos.bj.bcebos.com/ut_appbuilder/%5B%E6%B5%8B%E8%AF%95%5D%E8%B6%85%E5%B8%82%E6%94%B6%E5%85%A5%E6%98%8E%E7%BB%86%E8%A1%A8%E6%A0%BC.xlsx?authorization=bce-auth-v1/e464e6f951124fdbb2410c590ef9ed2f/2024-02-21T09%3A51%3A14Z/-1/host/1802a9c9142ef328d61e7673db7c1f05842b2af93d18a02ac7ef7aa6f64db54e" | + +### 响应参数 +| 参数名称 | 参数类型 | 描述 | 示例值 | +|--------|--------|----|------| +| result | Message | 返回结果。| - | +| +content | String | 如果图表绘制成功,则会返回一个可下载的图片链接,有效期为24小时;如果绘制失败,则会返回空字符串。 | "https://agi-dev-platform-bos.bj.bcebos.com/ut_appbuilder/%5B%E6%B5%8B%E8%AF%95%5D2020%E5%B9%B4%E5%90%84%E4%B8%AA%E6%9C%88%E4%BB%BD%E5%88%A9%E6%B6%A6%E6%9D%A1%E5%BD%A2%E5%9B%BE.png?authorization=bce-auth-v1/e464e6f951124fdbb2410c590ef9ed2f/2024-02-21T10%3A00%3A16Z/-1/host/b68f35825ad99075caf8bd009e4871ee9eb7b718e550968fdf12695b1502bc78" | + +### 响应示例 +```shell +Message(name=msg, content="https://agi-dev-platform-bos.bj.bcebos.com/ut_appbuilder/%5B%E6%B5%8B%E8%AF%95%5D2020%E5%B9%B4%E5%90%84%E4%B8%AA%E6%9C%88%E4%BB%BD%E5%88%A9%E6%B6%A6%E6%9D%A1%E5%BD%A2%E5%9B%BE.png?authorization=bce-auth-v1/e464e6f951124fdbb2410c590ef9ed2f/2024-02-21T10%3A00%3A16Z/-1/host/b68f35825ad99075caf8bd009e4871ee9eb7b718e550968fdf12695b1502bc78", mtype=str) +``` + +### 错误码 +| 错误码 | 描述 | +|--------|--------| +| pydantic.error_wrappers.ValidationError | 输入参数校验错误 | + +## 更新记录和贡献 +* Excel转图表 (2024-02) diff --git a/docs/BasisModule/Components/extract_table/README.md b/docs/BasisModule/Components/extract_table/README.md new file mode 100644 index 000000000..2a0b9c1cd --- /dev/null +++ b/docs/BasisModule/Components/extract_table/README.md @@ -0,0 +1,88 @@ +# 表格抽取组件(ExtractTableFromDoc) + +## 简介 +表格抽取组件(ExtractTableFromDoc)是用于文档表格处理的组件,从文档中抽取表格。支持对文档表格大小进行限制,限制后自动进行拆分、跨页合并等处理;支持合并表格上文,提取的表格为Markdown格式。 + +### 功能介绍 +从文档中抽取表格。支持对文档表格大小进行限制,限制后自动进行拆分、跨页合并等处理;支持合并表格上文,设置表格上文数量,提取的表格为Markdown格式。 + +### 特色优势 +组件抽取表格,准确高效,代码简单可快速上手;且不依赖本地计算资源。 + +### 适用场景 +文档表格解析与处理,用于后续任务的输入。 + +## 基本用法 +下面是一个基本用法的样例 + +```python +import os +import json +import requests + +from appbuilder.utils.logger_util import logger +from appbuilder import Message, ExtractTableFromDoc, DocParser + + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = "..." + + +# 进行文档内容解析 +file_url = "https://agi-dev-platform-bos.bj.bcebos.com/ut_appbuilder/test.pdf?authorization=bce-auth-v1/e464e6f951124fdbb2410c590ef9ed2f/2024-01-25T12%3A56%3A15Z/-1/host/b54178fea9be115eafa2a8589aeadfcfaeba20d726f434f871741d4a6cb0c70d" +file_data = requests.get(file_url).content +file_path = "./test.pdf" # 待解析的文件路径 +with open(file_path, "wb") as f: + f.write(file_data) + +msg = Message(file_path) + +parser = DocParser() +# ExtractTableFromDoc输入为文档原始解析结果,此处需要带上原始结果,return_raw=True. +doc = parser(msg, return_raw=True).content.raw + +# 抽取文档中的表格 +parser = ExtractTableFromDoc() +result = parser.run(Message(doc)) + +logger.info("Tables: {}".format( + json.dumps(result.content, ensure_ascii=False)) +) +``` + +## 参数说明 +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 初始化参数 +无 + +### 调用参数 + +|参数名称 |参数类型 |是否必须 |描述 |示例值| +|--------|--------|--------|----|------| +|message | Dict |是 | 输入的消息,用于模型的主要输入内容,必须为Docparser解析后的结果raw,需要设置return_raw=True。这是一个必需的参数。| `Message(parser(msg, return_raw=True).content.raw)` | +|table_max_size |int |否 |单个表格的长度的最大值(包含上文),按字符数即len(table_str)统计,默认为800。如果表格超长,则会被拆分成多个子表格,拆分的最小粒度为表格的行。若单行就超长,则会强制按table_max_size截断。截断时会优先截断上文,尽量保留表格内容。 | 800 | +|doc_node_num_before_table |int |否 |表格前附加的上文DocParser Node的数量,默认为1。范围:1~10。 | 1 | + +### 响应参数 +|参数名称 |参数类型 |描述 |示例值| +|--------|--------|----|------| +| - | List | 解析出来的文档表格,如果元素长度为1,则对应原文档中格式化后的长度不超过`table_max_size`的表格;如果元素长度>1,则是对应原文档中一个大表格,该表格被拆分成的多个子表格,以满足设置大小。 | 见响应示例 | + +### 错误码 +|错误码|描述| +|------|---| + + +### 响应示例 +```json +[[{"para": "table1"}], [{"para": "table2-part1"}, {"para": "table2-part2"}]] +``` + +## 更新记录和贡献 +* 表格抽取能力 (2023-12) \ No newline at end of file diff --git a/docs/BasisModule/Components/gbi/nl2sql/README.md b/docs/BasisModule/Components/gbi/nl2sql/README.md new file mode 100644 index 000000000..050a9fca5 --- /dev/null +++ b/docs/BasisModule/Components/gbi/nl2sql/README.md @@ -0,0 +1,261 @@ +# GBI 问表 + +## 简介 +GBI 问表,根据提供的 mysql 表的 schema 信息,生成对应问题的 sql 语句。 + +### 功能介绍 +GBI 问表,根据提供的 mysql 表的 schema 信息,生成对应问题的 sql 语句。 + +### 特色优势 +直接生成 sql 语句,无需人工编写。 + +### 应用场景 +1. 业务人员需要根据问题生成 sql 语句,但是不熟悉 sql 语法。 +2. 业务人员需要根据问题生成 sql 语句,但是不熟悉表的名称。 + + +## 基本用法 +这里是一个示例,展示如何基于 mysql 表的 schema, 根据问题生成 sql 语句。 + + +```python +import logging +import os +import appbuilder +from appbuilder.core.message import Message +from appbuilder.core.components.gbi.basic import SessionRecord + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = "..." + +SUPER_MARKET_SCHEMA = """ +CREATE TABLE `supper_market_info` ( + `订单编号` varchar(32) DEFAULT NULL, + `订单日期` date DEFAULT NULL, + `邮寄方式` varchar(32) DEFAULT NULL, + `地区` varchar(32) DEFAULT NULL, + `省份` varchar(32) DEFAULT NULL, + `客户类型` varchar(32) DEFAULT NULL, + `客户名称` varchar(32) DEFAULT NULL, + `商品类别` varchar(32) DEFAULT NULL, + `制造商` varchar(32) DEFAULT NULL, + `商品名称` varchar(32) DEFAULT NULL, + `数量` int(11) DEFAULT NULL, + `销售额` int(11) DEFAULT NULL, + `利润` int(11) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 +""" + +table_schemas = [SUPER_MARKET_SCHEMA] +gbi_nl2sql = appbuilder.NL2Sql(model_name="ERNIE-Bot 4.0", table_schemas=table_schemas) +query = "列出超市中的所有数据" +nl2sql_result_message = gbi_nl2sql(Message({"query": query})) + +print(f"sql: {nl2sql_result_message.content.sql}") +print(f"llm result: {nl2sql_result_message.content.llm_result}") +``` + + +## 参数说明 + +### 初始化参数 +- model_name: 支持的模型名字 ERNIE-Bot 4.0, ERNIE-Bot, ERNIE-Bot-turbo, Qianfan-Agent-Speed-8k +- table_schemas: 表的 schema,例如: + +``` +CREATE TABLE `supper_market_info` ( + `订单编号` varchar(32) DEFAULT NULL, + `订单日期` date DEFAULT NULL, + `邮寄方式` varchar(32) DEFAULT NULL, + `地区` varchar(32) DEFAULT NULL, + `省份` varchar(32) DEFAULT NULL, + `客户类型` varchar(32) DEFAULT NULL, + `客户名称` varchar(32) DEFAULT NULL, + `商品类别` varchar(32) DEFAULT NULL, + `制造商` varchar(32) DEFAULT NULL, + `商品名称` varchar(32) DEFAULT NULL, + `数量` int(11) DEFAULT NULL, + `销售额` int(11) DEFAULT NULL, + `利润` int(11) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 +``` + +- knowledge: 用于提供一些知识, 比如 {"毛利率": "(销售收入 - 销售成本) / 销售收入"} +- prompt_template: prompt 模版, 必须包含的格式如下: + ***你的描述 + {schema} + ***你的描述 + {column_instrument} + ***你的描述 + {kg} + ***你的描述 + 当前时间:{date} + ***你的描述 + {history_instrument} + ***你的描述 + 当前问题:{query} + 回答: + +### 调用参数 +- message: message.content 是 字典,包含: query, session, column_constraint 三个key + * query: 用户的问题 + * session: gbi session 的历史 列表, 参考 SessionRecord + * column_constraint: 列选约束 参考 ColumnItem 具体定义 + +#### SessionRecord 初始化参数 +- query: 用户的问题 +- answer: gbi_nl2sql 返回的结果 NL2SqlResult + +#### ColumnItem 初始化参数如下 +- ori_value: query 中的 词语, 比如: "北京去年收入", 分词后: "北京, 去年, 收入", ori_value 是分词中某一个,比如: ori_value = "北京" +- column_name: 对应数据库中的列名称, city +- column_value: 对应数据库中的列值, 北京市 +- table_name: 该列所属的表名称 +- is_like: 与 ori_value 的匹配是包含 还是 等于,包含: True; 等于: False + +### 返回值 +- NL2SqlResult 的 message + +#### NL2SqlResult 初始化参数如下 +- llm_result: 大模型返回的结果 +- sql: 从 llm_result 中抽取的 sql 语句 + +## 高级用法 +### 设置 session + + +```python +session = list() +session.append(SessionRecord(query=query, answer=nl2sql_result_message.content)) +``` + +再次问表 + + +```python +nl2sql_result_message2 = gbi_nl2sql(Message({"query": "查看商品类别是水果的所有数据", + "session": session})) +print(f"sql: {nl2sql_result_message2.content.sql}") +print(f"llm result: {nl2sql_result_message2.content.llm_result}") +``` + + sql: + SELECT * FROM supper_market_info WHERE 商品类别 = '水果'; + ----------------- + llm result: ```sql + SELECT * FROM supper_market_info WHERE 商品类别 = '水果'; + ``` + + +### 增加列选优化 +实际上数据中 "商品类别" 存储的是 "新鲜水果", 那么就可以通过列选的限制来优化 sql. + + +```python +from appbuilder.core.components.gbi.basic import ColumnItem + +column_constraint = [ColumnItem(ori_value="水果", + column_name="商品类别", + column_value="新鲜水果", + table_name="超市营收明细表", + is_like=False)] +nl2sql_result_message2 = gbi_nl2sql(Message({"query": "查看商品类别是水果的所有数据", + "column_constraint": column_constraint})) + +print(f"sql: {nl2sql_result_message2.content.sql}") +print(f"llm result: {nl2sql_result_message2.content.llm_result}") +``` + + sql: + SELECT * FROM supper_market_info WHERE 商品类别='新鲜水果' + ----------------- + llm result: ```sql + SELECT * FROM supper_market_info WHERE 商品类别='新鲜水果' + ``` + + +从上面我们看到,商品类别不在是 "水果" 而是 修订为 "新鲜水果" + +### 增加知识优化 +当计算某些特殊知识的时候,大模型是不知道的,所以需要告诉大模型具体的知识,比如: +利润率的计算方式: 利润/销售额 +可以将该知识注入。具体示例如下: + + +```python +# 注入知识 +gbi_nl2sql.knowledge["利润率"] = "计算方式: 利润/销售额" +``` + + +```python +query3 = "列出商品类别是日用品的利润率" +msg3 = Message(query3) + +nl2sql_result_message3 = gbi_nl2sql(Message({"query": "列出商品类别是日用品的利润率"})) +print(f"sql: {nl2sql_result_message3.content.sql}") +print(f"llm result: {nl2sql_result_message3.content.llm_result}") +``` + + sql: + SELECT 商品类别, SUM(利润)/SUM(销售额) AS 利润率 + FROM supper_market_info + WHERE 商品类别 = '日用品' + GROUP BY 商品类别 + ----------------- + llm result: ```sql + SELECT 商品类别, SUM(利润)/SUM(销售额) AS 利润率 + FROM supper_market_info + WHERE 商品类别 = '日用品' + GROUP BY 商品类别 + ``` + + +## 调整 prompt 模版 +有时候,我们希望定义自己的prompt, 但是必须遵循对应的 prompt 模版的格式。 + + +问表的 prompt template 必须包含: +1. {schema} - 表的 schema 信息 +2. {instrument} - 列选限制的信息 +3. {kg} - 知识 +4. {date} - 时间 +5. {history_prompt} - 历史 +6. {query} - 当前问题 + +参考下面的示例 + + +```python +NL2SQL_PROMPT_TEMPLATE = """ + MySql 表 Schema 如下: + {schema} + 请根据用户当前问题,联系历史信息,仅编写1个sql,其中 sql 语句需要使用```sql ```这种 markdown 形式给出。 + 请参考列选信息: + {instrument} + 请参考知识: + {kg} + 当前时间:{date} + 历史信息如下: + {history_prompt} + 当前问题:"{query}" + 回答: +""" +``` + + +```python +gbi_nl2sql5 = appbuilder.NL2Sql(model_name="ERNIE-Bot 4.0", table_schemas=table_schemas, prompt_template=NL2SQL_PROMPT_TEMPLATE) +nl2sql_result_message5 = gbi_nl2sql5(Message({"query": "查看商品类别是水果的所有数据"})) + +print(f"sql: {nl2sql_result_message5.content.sql}") +print(f"llm result: {nl2sql_result_message5.content.llm_result}") +``` + + sql: + SELECT * FROM supper_market_info WHERE 商品类别 = '水果' + ----------------- + llm result: ```sql + SELECT * FROM supper_market_info WHERE 商品类别 = '水果' + ``` + diff --git a/docs/BasisModule/Components/gbi/select_table/README.md b/docs/BasisModule/Components/gbi/select_table/README.md new file mode 100644 index 000000000..4d173b792 --- /dev/null +++ b/docs/BasisModule/Components/gbi/select_table/README.md @@ -0,0 +1,132 @@ +# GBI 选表 + +## 简介 +GBI 选表:根据提供的多个 MySql 表名 以及 表名对应的描述信息,通过 query 选择一个或多个最合适的表来回答该 query。 +一般的适用场景是,当有数据库有多个表的时候,但是实际只有1个表能回答该 query,那么,通过该能力将该表选择出来,用于后面的 问表 环节。 + + +### 功能介绍 +GBI 选表,根据提供的多个 MySql 表名 以及 表名对应的描述信息,通过 query 选择一个或多个最合适的表来回答该 query。 +一般的适用场景是,当有数据库有多个表的时候,但是实际只有1个表能回答该 query,那么,通过该能力将该表选择出来,用于后面的 问表 环节。 + +### 特色优势 +可直接通过上传Excel进行数据问答 + +### 应用场景 +1. 营销数据分析 +2. 表格问答 + + +## 基本用法 +下面是根据提供的表的描述信息以及 query 选择对应的表的示例。 + + +```python +import logging +import os +import appbuilder +from appbuilder.core.message import Message +from appbuilder.core.components.gbi.basic import SessionRecord + + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = "..." + +# 表的描述信息, key: 表名; value: 是表的描述 +table_descriptions = { + "supper_market_info": "超市营收明细表,包含超市各种信息等", + "product_sales_info": "产品销售表" +} + + +# 生成问表对象 +select_table = appbuilder.SelectTable(model_name="ERNIE-Bot 4.0", table_descriptions=table_descriptions) +select_table_result_message = select_table(Message({"query": "列出超市中的所有数据"})) + +print(f"选的表是: {select_table_result_message.content}") +``` + + 选的表是: ['supper_market_info'] + + +## 参数说明 +### 初始化参数 +- model_name: 支持的模型名字 ERNIE-Bot 4.0, ERNIE-Bot, ERNIE-Bot-turbo, Qianfan-Agent-Speed-8k +- table_descriptions: 表的描述是个字典,key: 是表的名字, value: 是表的描述,例如: + +``` +{ + "supper_market_info": "超市营收明细表,包含超市各种信息等", + "product_sales_info": "产品销售表" +} +``` +- prompt_template: prompt 模版, 必须包含如下: + 1. {num} - 表的数量, 注意 {num} 有两个地方出现 + 2. {table_desc} - 表的描述 + 3. {query} - query + 参考下面的示例: + +``` +你是一个专业的业务人员,下面有{num}张表,具体表名如下: +{table_desc} +请根据问题帮我选择上述1-{num}种的其中相关表并返回,可以为多表,也可以为单表, +返回多张表请用“,”隔开 +返回格式请参考如下示例: +问题:有多少个审核通过的投运单? +回答: ```DWD_MAT_OPERATION``` +请严格参考示例只不要返回无关内容,直接给出最终答案后面的内容,分析步骤不要输出 +问题:{query} +回答: +``` + +### 调用参数 +- message: message.content 是用户的问题,包含的key: query, session + * query: 用户提出的问题 + * session: SessionRecord 列表 + +#### SessionRecord 初始化参数 +- query: 用户的问题 +- answer: gbi_nl2sql 返回的结果 NL2SqlResult + +### 返回值 +识别的表名的列表例如: +`["table_name"]` + +## 调整 prompt 模版 +有时候,我们希望定义自己的prompt, 选表支持 prompt 模版的定制化,但是必须遵循对应的 prompt 模版的格式。 + +### 选表 prompt 调整 +选表的 prompt template, 必须包含 +1. {num} - 表的数量, 注意 {num} 有两个地方出现 +2. {table_desc} - 表的描述 +3. {query} - query, 参考下面的示例: + + +```python +SELECT_TABLE_PROMPT_TEMPLATE = """ +你是一个专业的业务人员,下面有{num}张表,具体表名如下: +{table_desc} +请根据问题帮我选择上述1-{num}种的其中相关表并返回,可以为多表,也可以为单表, +返回多张表请用“,”隔开 +返回格式请参考如下示例: +问题:有多少个审核通过的投运单? +回答: ```DWD_MAT_OPERATION``` +请严格参考示例只不要返回无关内容,直接给出最终答案后面的内容,分析步骤不要输出 +问题:{query} +回答: +""" +``` + + +```python +select_table4 = appbuilder.SelectTable(model_name="ERNIE-Bot 4.0", + table_descriptions=table_descriptions, + prompt_template=SELECT_TABLE_PROMPT_TEMPLATE) + +select_table_result_message4 = select_table4(Message({"query": "列出超市中的所有数据"})) + +print(f"选的表是: {select_table_result_message4.content}") +``` + + 选的表是: ['supper_market_info'] + diff --git a/docs/BasisModule/Components/general_ocr/README.md b/docs/BasisModule/Components/general_ocr/README.md new file mode 100644 index 000000000..2ffc394b3 --- /dev/null +++ b/docs/BasisModule/Components/general_ocr/README.md @@ -0,0 +1,113 @@ +# 通用文字识别-高精度版(GeneralOCR) + +## 简介 + +通用文字识别组件(GeneralOCR)支持多场景、多语种、高精度的文字识别服务,对图片全部文字内容进行检测识别。 + +### 功能介绍 +覆盖多种通用场景、20+种语言的高精度整图文字检测和识别服务,包括各类印刷和手写文档、网络图片、表格、印章、数字、二维码等; + +### 特色优势 +* 准确率高 + + 多项ICDAR指标居世界第一,识别准确率高 +### 应用场景 +支持多场景、多语种、高精度的文字识别服务,可用于纸质文档电子化、办公文档/报表识别、图像内容审核等场景 +## 基本用法 + +以下是一个简单的例子来演示如何开始使用GeneralOCR组件: + +示例图片为![示例图片](https://bj.bcebos.com/v1/appbuilder/general_ocr_test.png?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-11T10%3A59%3A17Z%2F-1%2Fhost%2F081bf7bcccbda5207c82a4de074628b04ae857a27513734d765495f89ffa5f73) + +```python +import os +import appbuilder +import requests + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = '...' +# 从BOS读取样例图片 +image_url = "https://bj.bcebos.com/v1/appbuilder/general_ocr_test.png?"\ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-"\ + "11T10%3A59%3A17Z%2F-1%2Fhost%2F081bf7bcccbda5207c82a4de074628b04ae"\ + "857a27513734d765495f89ffa5f73" +raw_image = requests.get(image_url).content +general_ocr = appbuilder.GeneralOCR() +out = general_ocr.run(appbuilder.Message(content={"raw_image": raw_image})) +print(out.content) +``` + +## 参数说明 + +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 初始化参数 + +无 + +### 调用参数 +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +|---------|---------|------|-----------------------------|------------------------------------------------| +| message | String | 是 | 输入的消息,用于模型的主要输入内容。这是一个必需的参数 | Message(content={"raw_image": b"待识别的图片字节流数据"}),图像数据,base64编码后进行urlencode,要求base64编码和urlencode后大小不超过10M,最短边至少15px,最长边最大8192px,支持jpg/jpeg/png/bmp格式 | +|timeout| Float | 否 | HTTP超时时间,单位:秒 |1|| +| retry | Integer | 否 | HTTP重试次数 | 3 | + +### 响应参数 +| 参数名称 | 参数类型 | 描述 | 示例值 | +|--------------|---------|---------|---------------------------------------------------| +| words_result | Array[] | 返回结果 | [{"words":"一站式企业级大模型平台,提供先进的生成式AI生产及应用全流程开发工具链"}] | +| + words | String | 识别结果字符串 | "百度智能云千帆大模型平台" | + +### 响应示例 +```json +{ + "words_result":[ + { + "words":"一站式企业级大模型平台,提供先进的生成式AI生产及应用全流程开发工具链" + }, + { + "words":"百度智能云千帆大模型平台" + }, + { + "words":"文心大模型4.0已正式发布,个人和企业客户可通过百度智能云千帆大模型平台接入使用" + }, + { + "words":"立即使用" + }, + { + "words":"在线体验" + }, + { + "words":"使用文档" + }, + { + "words":"定价说明" + }, + { + "words":"千帆社区" + }, + { + "words":"常见概念、使用指导" + }, + { + "words":"定价、计费方式、计量说明" + }, + { + "words":"大模型开发学习、交流社区" + } + ] +} +``` + +## 高级用法 + +目前该模块仅提供基础通用文字识别功能。 + + +## 更新记录和贡献 +* 通用文字识别能力 (2023-12) diff --git a/docs/BasisModule/Components/handwrite_ocr/README.md b/docs/BasisModule/Components/handwrite_ocr/README.md new file mode 100644 index 000000000..a783c421c --- /dev/null +++ b/docs/BasisModule/Components/handwrite_ocr/README.md @@ -0,0 +1,153 @@ +# 手写文字识别 (HandwriteOCR) + +## 简介 +手写文字识别 (HandwriteOCR),图片中的手写中文、手写数字进行检测和识别,针对不规则的手写字体进行专项优化,识别准确率可达90%以上。 + + +### 功能介绍 +检测识别图片中的手写中文、手写数字,针对不规则的手写字体进行专项优化 + + +### 特色优势 +识别准确率可达90%以上 + +### 应用场景 +手写文字识别 + + +## 基本用法 + +下面是手写文字识别的代码示例: + +示例图片为 ![示例图片](https://bj.bcebos.com/v1/appbuilder/test_handwrite_ocr.jpg?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-23T11%3A58%3A09Z%2F-1%2Fhost%2F677f93445fb65157bee11cd492ce213d5c56e7a41827e45ce7e32b083d195c8b) + +```python +import os +import requests +import appbuilder + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = "..." +image_url="https://bj.bcebos.com/v1/appbuilder/test_handwrite_ocr.jpg?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-23T11%3A58%3A09Z%2F-1%2Fhost%2F677f93445fb65157bee11cd492ce213d5c56e7a41827e45ce7e32b083d195c8b" + +# 从BOS存储读取样例文件 +raw_image = requests.get(image_url).content +inp = appbuilder.Message(content={"raw_image": raw_image}) +# inp = Message(content={"url": image_url}) + +# 运行手写文字识别 +handwrite_ocr = appbuilder.HandwriteOCR() +out = handwrite_ocr.run(inp) +# 打印识别结果 +print(out.content) + +``` + + +## 参数说明 + +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +import os + +os.environ["APPBUILDER_TOKEN"] = "..." +``` + +### 初始化参数 + +无 + +### 调用参数 +| 参数名称 | 参数类型 | 是否必须 | 描述 |示例值| +|------------|--------|------|-----------------------------|---| +| message | String | 是 | 输入的消息,用于模型的主要输入内容。这是一个必需的参数 || +| +content | Dict | 是 | 消息内容 || +| +raw_image | String | 否 | 原始图片字节流 || +| +url | String | 否 | 图片下载链接地址 || +|timeout| Float | 否 | HTTP超时时间,单位:秒 |1|| +|retry|Integer| 否 | HTTP重试次数 |3|| + + +### 响应参数 +| 参数名称 | 参数类型 | 描述 | 示例值 | +|-----------|----------|--|-----------------------------------------------------------------| +| contents | List | 文本内容块 | | +| +text | String | 文本字符串 | | +| +Position | Dict | 文本位置信息 | | +| ++left | Interger | 表示定位位置的长方形左上顶点的水平坐标 | | +| ++top | Interger | 表示定位位置的长方形左上顶点的垂直坐标| | +| ++width | Interger | 表示定位位置的长方形的宽度 | | +| ++height | Interger | 表示定位位置的长方形的高度 | | +| direction | Interger | 图像旋转角度 | 图像旋转角度,0(正向),- 1(逆时针90度),- 2(逆时针180度),- 3(逆时针270度)| + +### 响应示例 +```json +{ + "contents": [{ + "text": "我们家住的小区里有很多银杏树。", + "position": { + "left": 390, + "top": 46, + "width": 1801, + "height": 161 + } + }, + { + "text": "它们笔直笔直的,就像一位正在站岗的", + "position": { + "left": 131, + "top": 263, + "width": 2083, + "height": 170 + } + }, + { + "text": "卫兵。它枝繁叶茂,长的非常好,它的叶子", + "position": { + "left": 154, + "top": 483, + "width": 2023, + "height": 161 + } + }, + { + "text": "有些小的像一把把小扇子,大的也像扇子。", + "position": { + "left": 151, + "top": 699, + "width": 2167, + "height": 168 + } + }, + { + "text": "但是中间有一个缺口,就像被淘汽的小", + "position": { + "left": 148, + "top": 929, + "width": 2123, + "height": 177 + } + }, + { + "text": "朋友用剪刀剪掉了一样。", + "position": { + "left": 161, + "top": 1165, + "width": 1340, + "height": 217 + } + } + ], + "direction": 0 +} +``` + + +## 高级用法 +目前该模块仅提供基础的手写体识别。 + + +## 更新记录和贡献 +* 手写文字识别 (2024-01) +* 手写文字识别 (2024-02) diff --git a/docs/BasisModule/Components/image_understand/README.md b/docs/BasisModule/Components/image_understand/README.md new file mode 100644 index 000000000..4f6960f00 --- /dev/null +++ b/docs/BasisModule/Components/image_understand/README.md @@ -0,0 +1,87 @@ +# 图像内容理解 (ImageUnderstand) + +## 简介 +图像内容理解 (ImageUnderstand),输出理解图片后的文本信息 +### 功能介绍 +支持输入图片和提问信息,可对输入图片进行理解,输出对图片的一句话描述 +### 特色优势 +同时,支持客户自主选择,是否调用大模型对输出文本进行润色,目前支持百度文心大模型调用。 + +### 应用场景 +图像内容理解 + +## 基本用法 + +下面是图像内容理解的代码示例: + +示例图片为:![示例图片](https://bj.bcebos.com/v1/appbuilder/test_image_understand.jpeg?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-24T09%3A41%3A01Z%2F-1%2Fhost%2Fe8665506e30e0edaec4f1cc84a2507c4cb3fdb9b769de3a5bfe25c372b7e56e6) + +```python +import os +import appbuilder +import requests + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = "..." + +# 从BOS存储读取样例文件 +image_url = "https://bj.bcebos.com/v1/appbuilder/test_image_understand.jpeg?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-24T09%3A41%3A01Z%2F-1%2Fhost%2Fe8665506e30e0edaec4f1cc84a2507c4cb3fdb9b769de3a5bfe25c372b7e56e6" +raw_image = requests.get(image_url).content +# 输入参数为一张图片 +inp = appbuilder.Message(content={"raw_image": raw_image, "question": "图片里内容是什么?"}) +# 进行图像内容理解 +image_understand = appbuilder.ImageUnderstand() +out = image_understand.run(inp) +# 打印识别结果 +print(out.content) +``` + + +## 参数说明 + +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +import os +os.environ["APPBUILDER_TOKEN"] = "..." +``` + +### 初始化参数 + +无 + +### 调用参数 + +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +|------------|--------|------|-----------------------------------|-----| +| message | String | 是 | 输入的消息,用于模型的主要输入内容。这是一个必需的参数 | | +| +content | Dict | 是 | 消息内容 | | +| +raw_image | String | 否 | 原始图片字节流 | | +| +url | String | 否 | 图片下载链接地址 | | +| +question | String | 是 | 问题字符串,长度小于100 | | +| +language | String | 否 | 描述内容的所使用的语言,默认是zh-CN(中文),可选en(英文) | | + +|timeout| Float | 否 | HTTP超时时间,单位:秒 |1|| +|retry| Integer | 否 | HTTP重试次数 |3|| + +### 响应参数 +| 参数名称 | 参数类型 | 描述 | 示例值 | +|-----------|------|--------|-------------------------------------------------| +| description | String | 图像理解内容 | "用户上传的图像,经过前期模型分析存在以下信息:;;整个图像内容" | + +### 响应示例 +```json +{ + "description": "用户上传的图像,经过前期模型分析存在以下信息:;;整个图像内容可以表述为:...,回答如下问题:图片里内容是什么?, 注意不要复述提供的资料内容" +} +``` + +### 错误码 +|错误码|描述| +|------|---| + +## 高级用法 +目前该模块仅提供基础的图像内容理解。 + +## 更新记录和贡献 +* 图像内容理解 (2024-01) \ No newline at end of file diff --git a/docs/BasisModule/Components/landmark_recognize/README.md b/docs/BasisModule/Components/landmark_recognize/README.md new file mode 100644 index 000000000..8c66ec756 --- /dev/null +++ b/docs/BasisModule/Components/landmark_recognize/README.md @@ -0,0 +1,88 @@ +# 地标识别(LandmarkRecognition) + +## 简介 +地标识别组件(LandmarkRecognition)可以识别12万中外著名地标、热门景点,可返回地标名称。 + +### 功能介绍 +识别中外著名地标、热门景点,可返回地标名称 + +### 特色优势 +可以识别12万中外著名地标、热门景点 + +### 应用场景 +地点识别 + + +## 基本用法 + +下面是地标识别的代码示例: + +示例图片为:![示例图片](https://bj.bcebos.com/v1/appbuilder/landmark_test.jpeg?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-11T10%3A59%3A56Z%2F-1%2Fhost%2Fc249a068c6f321b91da0d0fd629b26ded58dcac2b6a3674f32378f5eb8df1ed0) + +```python +import os + +import requests + +import appbuilder + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = '...' +# 从BOS存储读取样例文件 +image_url = "https://bj.bcebos.com/v1/appbuilder/landmark_test.jpeg?" \ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-" \ + "11T10%3A59%3A56Z%2F-1%2Fhost%2Fc249a068c6f321b91" \ + "da0d0fd629b26ded58dcac2b6a3674f32378f5eb8df1ed0" +raw_image = requests.get(image_url).content +# 输入参数为一张图片 +inp = appbuilder.Message(content={"raw_image": raw_image}) +# 进行地标识别 +landmark_recognize = appbuilder.LandmarkRecognition() +out = landmark_recognize.run(inp) +# 打印识别结果 +print(out.content) # eg: {"landmark": "尼罗河"} +``` + + +## 参数说明 + +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +import os + +os.environ["APPBUILDER_TOKEN"] = "..." +``` + +### 初始化参数 + +无 + +### 调用参数 +| 参数名称 | 参数类型 | 是否必须 | 描述 |示例值| +|------------|--------|------|-----------------------------|---| +| message | String | 是 | 输入的消息,用于模型的主要输入内容。这是一个必需的参数 || +| +content | Dict | 是 | 消息内容 || +| +raw_image | String | 否 | 原始图片字节流 || +| +url | String | 否 | 图片下载链接地址 || +|timeout| Float | 否 | HTTP超时时间,单位:秒 |1|| +|retry|Integer| 否 | HTTP重试次数 |3|| + +### 响应参数 +| 参数名称 | 参数类型 | 描述 | 示例值 | +|----------|------|------|--------| +| landmark | str | 地标名字 | 比如:尼罗河 | +### 响应示例 +```json +{"landmark": "尼罗河"} +``` + +### 错误码 +|错误码|描述| +|------|---| + +## 高级用法 +目前该模块仅提供基础的地标识别。 + +## 更新记录和贡献 +* 地标识别 (2024-01) diff --git a/docs/BasisModule/Components/llms/dialog_summary/README.md b/docs/BasisModule/Components/llms/dialog_summary/README.md new file mode 100644 index 000000000..2ef9b0665 --- /dev/null +++ b/docs/BasisModule/Components/llms/dialog_summary/README.md @@ -0,0 +1,76 @@ +# 会话小结(DialogSummary) + +## 简介 +会话小结(DialogSummary)基于生成式大模型对一段用户与坐席的对话生成总结,结果按{"诉求": "", "回应": "", "解决情况": ""}格式输出。适用于运营商、金融、汽车等多种场景的对话总结。 + +### 功能介绍 +基于生成式大模型对一段用户与坐席的对话生成总结。 + +### 特色优势 +基于生成式大模型对一段用户与坐席的对话生成总结,结果按{"诉求": "", "回应": "", "解决情况": ""}格式输出。 + +### 应用场景 +适用于运营商、金融、汽车等多种场景的对话总结。 + +## 基本用法 + +为了快速开始使用会话小结组件,您可以参考以下步骤: + +```python +import appbuilder +import os + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" + +dialog_summary = appbuilder.DialogSummary("Qianfan-Agent-Speed-8k") +text = "用户:喂我想查一下我的话费\n坐席:好的女士您话费余的话还有87.49元钱\n用户:好的知道了谢谢\n坐席:嗯不客气祝您生活愉快再见" +answer = dialog_summary(appbuilder.Message(text)) +print(answer) +``` + +## 参数说明 + +### 鉴权配置 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 初始化参数 +- `model` (str|None): 模型名称,用于指定要使用的千帆模型。 + +### 调用参数 +### 调用参数 +|参数名称 |参数类型 | 是否必须 | 描述 | 示例值 | +|--------|--------|---|-----------------------------------------------------------------------------|---------------| +|message |Message | 是 | 输入消息,包含用户提出的问题。 | Message("你好") | +|stream|bool| 否 | 是否以流式形式返回响应 | False | +|temperature|float| 否 | 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 | 0.0001 | + +### 响应参数 +|参数名称 |参数类型 |描述 |示例值| +|--------|--------|----|------| +|result |Message |返回结果|对象,包含模型运行后的输出消息。| +### 响应示例 +```json +{"result": ["您话费余的话还有87.49元钱"]} +``` +### 错误码 +无 + +## 高级用法 + +暂无 + +## 示例和案例研究 + +目前暂无具体的实际应用案例。 + +## API文档 + +暂无 + +## 更新记录和贡献 +* 会话小结更新Readme (2023-12) diff --git a/docs/BasisModule/Components/llms/hallucination_detection/README.md b/docs/BasisModule/Components/llms/hallucination_detection/README.md new file mode 100644 index 000000000..528cb6812 --- /dev/null +++ b/docs/BasisModule/Components/llms/hallucination_detection/README.md @@ -0,0 +1,95 @@ +# 幻觉检测(Hallucination Detection) + +## 简介 +幻觉检测(Hallucination Detection)针对问答场景,检测答案中是否存在幻觉。 + +### 功能介绍 +幻觉检测(Hallucination Detection)针对问答场景,检测答案中是否存在幻觉。 + +### 特色优势 +无。 + +### 应用场景 +问答场景,比如RAG问答。 + +## 基本用法 + +下面是幻觉检测的代码示例: + +```python +import os +import appbuilder +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ['APPBUILDER_TOKEN'] = '...' + + +query = '澳门新麻蒲烤肉店每天开门吗?' +context = \ +'''澳门美食: 澳门新麻蒲韩国烤肉店 +在澳门一年四季之中除了火锅,烤肉也相当受欢迎。提到韩烧,有一间令我印象最深刻,就是号称韩国第一的烤肉店-新麻蒲韩国烤肉店,光是韩国的分店便多达四百多间,海外分店更是遍布世界各地,2016年便落户澳门筷子基区,在原本已经食肆林立的地方一起百花齐放!店内的装修跟韩国分店还完度几乎没差,让食客彷如置身于韩国的感觉,还要大赞其抽风系统不俗,离开时身上都不会沾上烤肉味耶! +时间:周一至周日 下午5:00 - 上午3:00 +电话:+853 2823 4012 +地址:澳门筷子基船澳街海擎天第三座地下O号铺96号 +必食推介: +护心肉二人套餐 +来新麻蒲必试的有两样东西,现在差不多每间烤肉店都有炉边烤蛋,但大家知道吗?原来新麻蒲就是炉边烤蛋的开创者,既然是始祖,这已经是个非吃不可的理由!还有一款必试的就是护心肉,即是猪的横隔膜与肝中间的部分,每头猪也只有200克这种肉,非常珍贵,其味道吃起来有种独特的肉香味,跟牛护心肉一样精彩! +秘制猪皮 +很多怕胖的女生看到猪皮就怕怕,但其实猪皮含有大量胶原蛋白,营养价值很高呢!这里红通通的猪皮还经过韩国秘制酱汁处理过,会有一点点辣味。烤猪皮的时候也需特别注意火侯,这样吃起来才会有外脆内Q的口感!''' +answer = '澳门新麻蒲烤肉店并不是每天开门。' + +#! 该组件推荐使用Qianfan-Agent-Speed-8k模型。 +hallucination_detection = appbuilder.HallucinationDetection('Qianfan-Agent-Speed-8k') +inputs = {'query': query, 'context': context, 'answer': answer} +msg = appbuilder.Message(inputs) +result = hallucination_detection.run(msg) + +print(result) +``` + +## 参数说明 + +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 初始化参数 +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +| ------- | ------- | -------- | -------- | -------- | +| `model` | str | 是 | 模型名称,用于指定要使用的千帆模型。推荐使用Qianfan-Agent-Speed-8k模型。 | Qianfan-Agent-Speed-8k | +| `secret_key` | str | 否 | 用户鉴权token,默认从环境变量中获取: `os.getenv("APPBUILDER_TOKEN", "")` | bce-v3/XXX | +| `gateway` | str | 否 | 后端网关服务地址,默认从环境变量中获取: `os.getenv("GATEWAY_URL", "")` | https://appbuilder.baidu.com | +| `lazy_certification` | bool | 否 | 延迟认证,为True时在第一次运行时认证。默认为False。 | False | + +### 调用参数 +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +| ------- | ------- | -------- | -------- | -------- | +| `message` | obj | 是 | 输入信息,用于传入用户查询query、根据query得到的检索结果context和基于context生成的query的答案answer。 | Message(content={'query': '...', 'context': '...', 'answer': '...'}) | +| `stream` | bool | 否 | 指定是否以流式形式返回响应。默认为 False。 | False | +| `temperature` | float | 否 | 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 | 0.1 | +| `top_p` | float | 否 | 影响输出文本的多样性,取值越大,生成文本的多样性越强。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0.0。 | 0.0 | + +### 响应参数 +| 参数名称 | 参数类型 | 描述 | 示例值 | +| ------- | ------- | -------- | -------- | +| `result` | obj | 模型运行后的输出结果(可通过result.content获取结果核心内容) | Message(content='...') | + +### 响应示例 +``` +Message(name=msg, content=存在幻觉, mtype=dict, extra={}, token_usage={'prompt_tokens': 748, 'completion_tokens': 2, 'total_tokens': 750}) +``` + +### 错误码 +|错误码|描述| +| ------ | ------ | + +## 高级用法 +可用于RAG问答的答案检测。 + +## 更新记录和贡献 +### 2024.5.22 +#### [Added] +- 增加幻觉检测组件。 +- 增加幻觉检测组件单元测试。 \ No newline at end of file diff --git a/docs/BasisModule/Components/llms/is_complex_query/README.md b/docs/BasisModule/Components/llms/is_complex_query/README.md new file mode 100644 index 000000000..a3bab9473 --- /dev/null +++ b/docs/BasisModule/Components/llms/is_complex_query/README.md @@ -0,0 +1,76 @@ +# 复杂Query判定(IsComplexQuery) + +## 简介 +复杂Query判定组件(IsComplexQuery)可以根据输入的提问进行初步的分类,区分简单问题和复杂问题,以便后续运用不同的处理流程处理。可应用于知识问答场景。 + +### 功能介绍 +在知识问答领域中存在很多复杂问题需要处理,这些复杂问题通常需要进行问题分解并采用分治的方法处理。复杂Query判定组件尝试定义复杂问题和简单问题的概念,对用户的问题进行初步的分类,方便下游使用不同类型的流程来处理当前的简单问题/复杂问题。 + +### 特色优势 +复杂Query判定组件通过对问题进行有效分类,系统可以更快速地将简单问题导向快速回答流程,而将复杂问题导向更深入的分析流程。这种判定能力可以提高整个问答系统的效率和准确性。 + +### 应用场景 +广泛应用于知识问答场景 + +## 基本用法 +下面是复杂Query判定的代码示例: +```python +import os +import appbuilder + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = "..." + +is_complex_query = appbuilder.IsComplexQuery(model="Qianfan-Agent-Speed-8k") + +msg = "吸塑包装盒在工业化生产和物流运输中分别有什么重要性?" +msg = appbuilder.Message(msg) +answer = is_complex_query(msg) + +print("Answer: \n{}".format(answer.content)) +``` + +## 参数说明 + +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 初始化参数 +|参数名称 |参数类型 |是否必须 |描述 |示例值| +|--------|--------|--------|----|------| +|model |str |是 |模型名称,用于指定要使用的千帆模型|Qianfan-Agent-Speed-8k| + +### 调用参数 +|参数名称 |参数类型 |是否必须 |描述 |示例值| +|--------|--------|--------|----|------| +|message |obj:`Message`|是 |输入消息,用于模型的主要输入内容。这是一个必需的参数| | +|stream|bool|否 |指定是否以流式形式返回响应。默认为 False|False| +|temperature|float|否 |模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10|1e-10| + +### 响应参数 +|参数名称 |参数类型 |描述 |示例值| +|--------|--------|----|------| +|Message |obj:`Message` |输出消息,包含模型运行后的输出| | + +### 响应示例 +```text +分析:这个问题涉及到吸塑包装盒在工业化生产和物流运输中的重要性。回答这个问题需要从多个角度来考虑,比如生产方面、运输方面、环保方面等。这需要对吸塑包装盒有深入的了解,并且需要考虑到生产、运输等各个环节。因此,这是一个复杂问题。 +类型:复杂问题 +``` + +### 错误码 +无 + +## 高级用法 +你可以通过自定义调整参数来获得想要的结果,例如: +```python +# 流式返回, 调整模型temperature参数 +answer = is_complex_query(msg, stream=True, temperature=0.5) +``` + +## 更新记录和贡献 +* 复杂Query判定 (2024-01) diff --git a/docs/BasisModule/Components/llms/mrc/README.md b/docs/BasisModule/Components/llms/mrc/README.md new file mode 100644 index 000000000..eaf152beb --- /dev/null +++ b/docs/BasisModule/Components/llms/mrc/README.md @@ -0,0 +1,145 @@ +# 阅读理解问答(MRC) + +## 简介 +阅读理解问答(MRC)组件是基于生成式大模型的阅读理解问答系统。该组件支持拒答、澄清、重点强调、友好性提升、溯源等多种功能,可用于回答用户提出的问题。 + +### 功能介绍 +MRC(阅读理解问答模块)是一项先进的自然语言处理功能,旨在使机器能够理解、分析文本内容,并基于这些内容回答相关问题。 本模块基于大语言模型,提供对文本内容的深入理解和精确回答能力。 + +### 特色优势 +我们的MRC模块,基于百度自研的先进语言模型文新一言,提供了一系列强大的阅读理解问答功能。在保持文本理解和问题回答的高精度的同时, +我们特别强调了答案的质量和交互体验。以下是我们MRC模块的几个主要功能特色: + - 1.多版本模型支持:我们的MRC模块包括不同版本的文新一言大模型,Erniebot 4.0、Qianfan-Agent-Speed-8k等,每个版本都针对特定的应用场景进行了优化。 用户可以根据自己的需求选择最适合的模型版本,以获得最佳的性能。 + - 2.答案格式的多样性: + - 拒答功能:当问题超出模型知识范围或不具体时,模型可以选择不回答,避免提供误导性信息。 + - 澄清功能:对于模棱两可或含糊的问题,模型可以请求更多信息或对问题进行澄清,以确保答案的准确性。 + - 重点内容强调:模型可以识别并强调答案中的关键信息,使答案更清晰、更易于理解。 + - 友好度提升:模型可以以更自然、更亲切礼貌的方式呈现答案,且必要时对答案进行分点论述,改善用户体验。 + - 答案溯源:模型能提供答案的来源信息,增强答案的可信度和透明度。 + + - 3.灵活的功能开关:每项功能都配备了开关,用户可以根据不同的应用场景和需求,灵活地启用或关闭某些功能。这种可定制性确保了MRC模块能够在各种环境下提供最优化的表现。 + +### 应用场景 +我们的MRC模块,凭借文心大模型强大的文本理解能力,以及多功能性,已在多个领域展现出显著的价值。 + - 政务服务:在政务领域,MRC模块可以帮助构建智能问答系统,为公民提供关于政策、法规和服务程序的即时信息。它可以通过理解复杂的政府文件和公文,准确回答与政策相关的查询,极大地提高政府服务的效率和透明度。 + - 法律咨询:法律领域充满了专业术语和复杂的概念。我们的MRC模块能够深入理解法律文献和案例,为法律专业人士和普通民众提供准确的法律咨询。无论是寻找相关法条还是理解特定法律案例,MRC模块都能提供快速、可靠的帮助。 + - 医疗健康:在医疗健康领域,准确的信息至关重要。MRC模块能够解析医学文献、病例报告和临床试验数据,为医生和研究人员提供支持,帮助他们在诊断、治疗和研究中做出更明智的决策。此外,它也能在患者咨询中发挥作用,为患者提供关于疾病、治疗方案和药物的详细信息。 + - 教育和研究:MRC模块可以作为学习和研究的强大工具,帮助学生和研究人员快速找到他们需要的信息。无论是解答学术问题,还是提供详细的背景资料,MRC模块都能提供卓越的支持。 + - 企业客户服务:企业可以利用MRC模块构建高效的客服系统,提供24/7的咨询服务。从产品详情到服务流程的解释,MRC模块都能提供准确、及时的答案,提升客户满意度并减轻人工客服的压力。 + - 金融咨询:在金融领域,MRC模块可以帮助用户理解复杂的金融产品、市场趋势和投资策略。通过提供准确的数据解读和市场分析,MRC模块能够辅助投资者和金融专业人士做出更明智的决策。 +除上述场景以外,还可应用于其他更多生产生活的场景中。 + +## 基本用法 + +### 快速开启 + +```python +import appbuilder +import os + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = "..." + +# 创建MRC对象 +mrc_component = appbuilder.MRC(model="Qianfan-Agent-Speed-8k") + +# 初始化参数 +msg = "残疾人怎么办相关证件" +msg = appbuilder.Message(msg) +context_list = appbuilder.Message(["""如何办理残疾人通行证一、残疾人通行证办理条件: +1、持有中华人民共和国残疾人证,下肢残疾或者听力残疾; +2、持有准驾车型为C1(听力残疾)、C2(左下肢残疾、听力残疾)""", + """3、本人拥有本市登记核发的非营运小型载客汽车,车辆须在检验有效期内,并有有效交强险凭证, +C5车辆加装操纵辅助装置后已办理变更手续。二、办理地点:北京市朝阳区左家庄北里35号: +北京市无障碍环境建设促进中心"""]) + +# 模拟运行MRC基本组件 +result = mrc_component.run(msg, context_list) + +# 输出运行结果 +print(result) +``` + +## 参数说明 + +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +import os +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 初始化参数 +- `model`: 模型名称,用于指定要使用的千帆模型。 + +### 调用参数 +|参数名称 |参数类型 | 是否必须 |描述 | 示例值 | +|--------|--------|---|----|------------------------------------------| +|msg |Message | 是 |输入消息,包含用户提出的问题。| Message("你好") | +|context_list|Message| 是 |用户输入的问题对应的段落文本列表。| Message(["""context1""","""context2"""]) | +|reject|bool| 否 |拒绝开关,如果为 True,则启用该能力。默认为 False。当输入的问题在context_list中没有找到答案时,开关开启时,模型会用特定话术("当前文档库找不到对应的答案,我可以尝试用我的常识来回答你。")做回复的开头,并后接自有知识做回复内容。| eg.示例值 | +|clarify|bool| 否 |澄清开关,如果为 True,则启用该能力。默认为 False。 当输入的问题比较模糊、或者主体指代不清晰,且context_list中包含有可以回答该模糊问题的多种潜在备选答案时,开启该开关,大模型会以特定的话术做澄清反问,引导用户继续补充问题发问。举例子,query:发电机的续航时间? Answer: 根据搜索结果得到了xx和xx两种型号的发电机,您的问题具体涉及到哪一个?请补充关键信息,作为完整的问题重新发问。| eg.示例值 | +|highlight|bool| 否 |重点强调开关,如果为 True,则启用该能力。默认为 False。开启该功能时,回复结果中会高亮显示关键部分的内容。| 加粗的部分是**重点内容** | +|friendly|bool| 否 |友好性提升开关,如果为 True,则启用该能力。默认为 False。开关开启时,部分回复的开头会加礼貌用语。且如果回答涉及到大段的信息,会倾向于以<总-分>或者<总-分-总>的形式做分点论述,使得答案的格式更规整,可读性更强。| eg.示例值 | +|cite|bool| 否 |溯源开关,如果为 True,则启用该能力。默认为 False。开关开启时,回复内容后会接形如(^[1]^)的标记来表示回答内容在原文(context_list)中的来源索引。例如:按照当地公安机关出入境管理部门规定的其他材料办理^[2]^。| eg.示例值 | +|temperature|float| 否 |模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。| 0.0001 | + + +### 响应参数 +|参数名称 |参数类型 |描述 |示例值| +|--------|--------|----|------| +|result |Message |返回结果|对象,包含模型运行后的输出消息。| +### 响应示例 +```json +{"result": "极氪007的售价区间为20.99~29.99万元。"} +``` + +### 错误码 +无 + + + +## 高级用法 +该组件的高级用法包括定制化的输入处理、输出处理,以及更复杂的调用场景。用户可以根据具体需求扩展组件功能,实现个性化的问答系统。 +包括如下功能: +1、拒答 +2、澄清反问 +3、重点强调 +4、友好度提升 +5、溯源 + + +### 代码样例 +```python +import appbuilder +import os + +# 设置环境变量 +os.environ["APPBUILDER_TOKEN"] = '...' + +# 创建MRC对象 +mrc_component = appbuilder.MRC(model="Qianfan-Agent-Speed-8k") + +# 初始化参数 +msg = "残疾人怎么办相关证件" +msg = appbuilder.Message(msg) +context_list = appbuilder.Message(["""如何办理残疾人通行证一、残疾人通行证办理条件: +1、持有中华人民共和国残疾人证,下肢残疾或者听力残疾; +2、持有准驾车型为C1(听力残疾)、C2(左下肢残疾、听力残疾)""", + """3、本人拥有本市登记核发的非营运小型载客汽车,车辆须在检验有效期内,并有有效交强险凭证, +C5车辆加装操纵辅助装置后已办理变更手续。二、办理地点:北京市朝阳区左家庄北里35号: +北京市无障碍环境建设促进中心"""]) + +# 模拟运行MRC组件,开启拒答、澄清追问、重点强调、友好性提升和溯源能力五个功能 +result = mrc_component.run(msg, context_list, reject=True, + clarify=True, highlight=True, friendly=True, cite=True) + +# 输出运行结果 +print(result) +``` + +## 更新记录和贡献 +* 阅读理解问答 (2023-12) + + diff --git a/docs/BasisModule/Components/llms/nl2pandas/README.md b/docs/BasisModule/Components/llms/nl2pandas/README.md new file mode 100644 index 000000000..a6828200f --- /dev/null +++ b/docs/BasisModule/Components/llms/nl2pandas/README.md @@ -0,0 +1,68 @@ +# 自然语言转pandas (nl2pandas) + +## 简介 +自然语言转pandas (nl2pandas)这个组件通过理解对表格信息的提问,生成对应语义的可执行Python代码,主要使用Pandas库。它可以用于基于表格的查询,问答等多种场景。 + +### 功能介绍 +自然语言转pandas (nl2pandas)根据用户的输入的表格信息和查询query,利用大语言模型的理解和生成能力,自动生成符合查询语义的一行或多行pandas代码。 + +### 特色优势 +自然语言转pandas (nl2pandas),基于百度自研的大语言模型文心一言,提供内置的自然语言转pandas代码的能力,无需更多的prompt描述,即可生成对应的查询代码。 + +### 应用场景 +自然语言转pandas (nl2pandas)可用对结构化表格数据的自然语言查询场景,问答场景等。 + +## 基本用法 +这里是一个简单示例,展示如何使用自然语言转Pandas组件: + +```python +import os +import appbuilder + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = "..." + +#定义表格信息 假设有一个小学学校表格,包含学校名、所属地区、创办时间、类别、学生人数、教职工人数、教学班数量等列。列名后给出示例(例如清华附小是学校名的示例),以及列值类型(字符串类型、数字值类型),最后给出列名的解释。列之间使用换行符分隔。 +table_info = '''表格列信息如下:\n学校名 : 清华附小 , 字符串类型,代表小学学校的名称\n所属地区 : 西城区 , 字符串类型,表示该小学学校所在的位置\n创办时间 : 1998 , 数字值类型,表示该小学学校的创办时间\n类别 : 公立小学 , 字符串类型,表示该小学学校所在的类别\n学生人数 : 2000 , 数字值类型,表示该小学学校的学生数量\n教职工人数 : 140 , 数字值类型,表示该小学学校的教职工数量\n教学班数量 : 122 , 数字值类型,表示该小学学校的教学班数量''' + +#定义查询问题 +query = "海淀区有哪些学校" +query = appbuilder.Message(query) + +#定义并运行Nl2pandas实例,得到结果 +nl2pandas = appbuilder.Nl2pandasComponent(model="Qianfan-Agent-Speed-8k") +answer = nl2pandas(query, table_info = table_info) +``` + +## 参数说明 + +### 鉴权配置 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 初始化参数 +- `model`: 模型名称,用于指定要使用的千帆模型。 + +### 调用参数 +|参数名称 |参数类型 |是否必须 |描述 |示例值| +|--------|--------|--------|----|------| +|query |Message | 是 | 查询语句,一般是针对表格信息的提问 |例如'海淀区的小学有哪些'。建议长度是50字以内。 | +|table_info |str | 是 | 表格信息,是表格列名以及对应列名的举例和释义 |例如:’表格列信息如下:\n学校名 : 清华附小 , 字符串类型,代表小学学校的名称\n所属地区 : 西城区 , 字符串类型,表示该小学学校所在的位置\n创办时间 : 1998 , 数字值类型,表示该小学学校的创办时间\n类别 : 公立小学 , 字符串类型,表示该小学学校所在的类别\n学生人数 : 2000 , 数字值类型,表示该小学学校的学生数量\n教职工人数 : 140 , 数字值类型,表示该小学学校的教职工数量\n教学班数量 : 122 , 数字值类型,表示该小学学校的教学班数量‘ | +|stream |bool | 否 |指定是否以流式形式返回响应。默认为 False。 |False | +|temperature |float | 否 |模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 |0.7 | + +### 响应参数 +|参数名称 |参数类型 |描述 |示例值| +|--------|--------|----|------| +|result |Message |返回结果|Message(content="df.loc[df['所属地区'] == "海淀区","学校名"]")| + +## 高级用法 + +多个表格查询或跨表查询场景。 + +## 更新记录和贡献 + +- 初始版本发布(2023-10) \ No newline at end of file diff --git a/docs/BasisModule/Components/llms/oral_query_generation/README.md b/docs/BasisModule/Components/llms/oral_query_generation/README.md new file mode 100644 index 000000000..4a796b11b --- /dev/null +++ b/docs/BasisModule/Components/llms/oral_query_generation/README.md @@ -0,0 +1,107 @@ +# 口语化Query生成(Oral Query Generation) + +## 简介 +口语化Query生成组件(Oral Query Generation)可以基于输入文本生成与文档内容相关的Query。 + +### 功能介绍 +基于输入文本生成与文档内容相关的Query。 + +### 特色优势 +生成的query划分为问题和短语两种类型,可分别用于不同场景。 + +### 应用场景 +可用于推荐问题生成、标签生成、文档索引增强等。 + +## 基本用法 +### 快速开始 +```python +import os +import appbuilder + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = "..." + + +text = ('文档标题:在OPPO Reno5上使用视频超级防抖\n' + '文档摘要:OPPO Reno5上的视频超级防抖,视频超级防抖3.0,多代视频防抖算法积累,这一代依旧超级防抖超级稳。 开启视频超级' + '防抖 开启路径:打开「相机 > 视频 > 点击屏幕上方的“超级防抖”标识」 后置视频同时支持超级防抖和超级防抖Pro功能,开启超级' + '防抖后手机屏幕将出现超级防抖Pro开关,点击即可开启或关闭。 除此之外,前置视频同样加持防抖算法,边走边拍也能稳定聚焦脸部' + ',实时视频分享您的生活。') + +#! 该组件推荐使用Qianfan-Agent-Speed-8k模型。 +oral_query_generation = appbuilder.OralQueryGeneration(model='Qianfan-Agent-Speed-8k') +result = oral_query_generation(appbuilder.Message(text), query_type='全部', output_format='str') + +print(result) +``` + +## 参数说明 +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 初始化参数 + +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +| ------- | ------- | -------- | -------- | -------- | +| `model` | str | 是 | 模型名称,用于指定要使用的千帆模型。推荐使用Qianfan-Agent-Speed-8k模型。 | Qianfan-Agent-Speed-8k | +| `secret_key` | str | 否 | 用户鉴权token,默认从环境变量中获取: `os.getenv("APPBUILDER_TOKEN", "")` | bce-v3/XXX | +| `gateway` | str | 否 | 后端网关服务地址,默认从环境变量中获取: `os.getenv("GATEWAY_URL", "")` | https://appbuilder.baidu.com | +| `lazy_certification` | bool | 否 | 延迟认证,为True时在第一次运行时认证。默认为False。 | False | + +### 调用参数 + +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +| ------- | ------- | -------- | -------- | -------- | +| `message` | obj | 是 | 输入消息,用于模型的主要输入内容。 | Message(content='...') | +| `query_type` | str | 否 | 待生成的query类型,包括问题、短语和全部(问题+短语)。默认为全部。 | 全部 | +| `output_format` | str | 否 | 输出格式,包括json和str。默认为str。 | str | +| `stream` | bool | 否 | 指定是否以流式形式返回响应。默认为 False。 | False | +| `temperature` | float | 否 | 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 | 0.1 | +| `top_p` | float | 否 | 影响输出文本的多样性,取值越大,生成文本的多样性越强。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0.0。 | 0.0 | + +### 响应参数 +| 参数名称 | 参数类型 | 描述 | 示例值 | +| ------- | ------- | -------- | -------- | +| `result` | obj | 模型运行后的输出结果 | Message(content='...') | + +### 响应示例 +``` +Message(name=msg, content=1. OPPO Reno5上有什么特殊的功能? +2. 视频超级防抖是什么? +3. 视频超级防抖有什么作用? +4. 如何在OPPO Reno5上开启视频超级防抖? +5. 视频超级防抖Pro是什么? +6. 开启视频超级防抖后,屏幕上会出现什么? +7. 前置视频有防抖算法吗? +8. OPPO Reno5上的视频超级防抖 +9. 视频超级防抖3.0 +10. 多代视频防抖算法积累的作用 +11. 开启视频超级防抖的方法 +12. 视频超级防抖Pro的功能 +13. 开启视频超级防抖后,屏幕上会出现的东西 +14. 前置视频防抖算法的作用, mtype=dict, extra={}) +``` + +## 高级用法 + +## 更新记录和贡献 +### 2024.5.22 +#### [Updated] +- 升级能力,主要升级内容如下: + - 生成的query要求能够使用输入文本进行回答。 + - 生成的query划分为问题和短语类型。 + - 生成的query数量不再限制为10个。 +- 在调用组件时,支持输出问题、短语或全部(问题 + 短语);支持输出格式为json或者str(兼容之前版本的输出格式)。 + +### 2024.1.24 +#### [Updated] +- 更新README。 + +### 2023.12.07 +#### [Added] +- 增加口语化Query生成组件。 +- 增加口语化Query生成组件单元测试。 \ No newline at end of file diff --git a/docs/BasisModule/Components/llms/playground/README.md b/docs/BasisModule/Components/llms/playground/README.md new file mode 100644 index 000000000..73c598591 --- /dev/null +++ b/docs/BasisModule/Components/llms/playground/README.md @@ -0,0 +1,82 @@ +# 空应用(Playground) + +## 简介 +Playground空应用(Playground)是一款灵活的组件,允许用户自定义prompt模板并执行。 + + +### 功能介绍 +Playground空应用(Playground)是一款灵活的组件,允许用户自定义prompt模板并执行。它适用于各种场景,特别是在需要自定义输入模板和使用预训练模型进行交互的情况下。 + +### 特色优势 +灵活可自定义,用户可自由定义提示词,来跟大模型进行交互。 + +### 应用场景 +在需要灵活定义提示词的场景,并且其他提供的开箱即用的组件无法满足的情况下使用。 + +## 基本用法 + +要开始使用 Playground,你需要设置prompt模板和模型名称。以下是一个基本示例: + +```python +import os +import appbuilder + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = "..." + +play = appbuilder.Playground( + prompt_template="你好,{name},我是{bot_name},{bot_name}是一个{bot_type},我可以{bot_function},你可以问我{bot_question}。", + model="Qianfan-Agent-Speed-8k" +) +play(appbuilder.Message({"name": "小明", "bot_name": "小红", "bot_type": "聊天机器人", "bot_function": "聊天", "bot_question": "你好吗?"}), stream=False) +``` + +## 参数说明 +### 初始化参数 + +| 参数名称 | 类型 | 说明 | +|----------------|------------|----------------------------------| +| prompt_template | str | 输入模板,用于指定prompt格式。 | +| model | str \| None | 模型名称,用于指定要使用的千帆模型。 | + +### 调用参数 + +| 参数名称 | 类型 | 说明 | 默认值 | +|-----------|---------------|----------------------------|------| +| message | obj:`Message` | 输入消息,必需参数。 | 无 | +| stream | bool | 是否以流式形式返回响应。 | False | +| temperature | float | 模型配置的温度参数。 | 1e-10 | +| max_output_tokens | int | 指定生成的文本的最大长度,默认最大输出token数为1024, 最小为2, 最大输出token与选择的模型有关 | 1024 | +|disable_search| bool | 是否关闭搜索功能,默认关闭 | True | +|response_format| str | 指定返回的响应格式,可选值有:`text`, `json_object`| text | +|stop| str | 生成停止标识,当模型生成结果以stop中某个元素结尾时,停止文本生成。每个元素长度不超过20字符,最多4个元素. | [] | + + +### 响应参数 + +| 类型 | 说明 | +|----------------|--------------------| +| obj:`Message` | 模型运行后的输出消息。 | + +### 响应示例 +```json +{"result": "北京科技馆。"} +``` + +### 错误码 +|错误码|描述| +|------|---| + + +## 高级用法 +此部分可根据实际应用场景提供更复杂的示例和用法说明。 + +## 示例和案例研究 +目前暂无具体案例,将在未来更新。 + +## API文档 +无 + +## 更新记录和贡献 +- 2024年01月24日 更新Readme格式,调整请求参数样式,新增特色优势 +- 2024年08月01日 更新playground组件的入参,并且前向兼容,支持未来的大模型对话参数 diff --git a/docs/BasisModule/Components/llms/qa_pair_mining/README.md b/docs/BasisModule/Components/llms/qa_pair_mining/README.md new file mode 100644 index 000000000..8fcfa6e5e --- /dev/null +++ b/docs/BasisModule/Components/llms/qa_pair_mining/README.md @@ -0,0 +1,125 @@ +# 问答对挖掘(QAPairMining) + +## 简介 +问答对挖掘(QAPairMining)可以基于输入文本内容,快速生成多个问题及对应答案,极大提高信息提炼的效率和准确性。广泛用于在线客服、智能问答等领域。 + +### 功能介绍 +基于输入文本内容,快速生成多个问题及对应答案;可与文档解析、分段联用,快速生成文档的问答对。 + +### 特色优势 +问答对挖掘组件快速基于输入文本生成的问题和答案,准确率高;可快速依据文档生成FAQ,减少人工成本。 + +### 适用场景 +适用于在线客服、智能问答等场景 + +## 基本用法 + +### 快速开始 + +```python +import os +import appbuilder + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = "..." + +qa_mining = appbuilder.QAPairMining(model="Qianfan-Agent-Speed-8k") +# 输入文本(对此文本挖掘问答对) +msg = '2017年,工商银行根据外部宏观环境变化,及时调整业务策略,优化资产负债结构,' + \ + '保持存贷款业务协调发展,提升资产负债配置效率。' + \ + '2018年3月末,工商银行总资产264,937.81亿元,比上年末增加4,067.38亿元.' +msg = appbuilder.Message(msg) +answer = qa_mining(msg) + +print(">>> Output: {}".format(answer.content)) +``` + +## 参数说明 +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 初始化参数 +无 + +### 调用参数 + +|参数名称 |参数类型 |是否必须 |描述 |示例值| +|--------|--------|--------|----|------| +| message | String |是 |输入消息,用于模型的主要输入内容。这是一个必需的参数。| `Message("2017年,工商银行根据...")` | +| stream |bool|否 |指定是否以流式形式返回响应。默认为 False。| False | +| temperature |float|否 |模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 | 1e-10 | + +### 响应参数 +|参数名称 |参数类型 |描述 |示例值| +|--------|--------|----|------| +| result | String | 输出消息,包含模型运行后的输出内容。| 见响应示例 | + +### 响应示例 +```text +问题:2017年,工商银行是如何应对外部宏观环境变化的? +答案:工商银行根据外部宏观环境变化,及时调整业务策略,优化资产负债结构,保持存贷款业务协调发展,提升资产负债配置效率。 + +问题:2018年3月末,工商银行总资产是多少? +答案:264,937.81亿元。 +``` + +### 错误码 +|错误码|描述| +|------|---| + +## 高级用法 + +基于一篇文档,快速生成多个问题及对应答案,极大提高信息提炼的效率和准确性。 +主要流程如下: + +1. 读取本地文档,文档解析分段,获取段落; +2. 段落作为问答对挖掘的输入,挖掘问答对。 + +### 代码样例 +```python +import os +import requests + +from appbuilder.utils.logger_util import logger +from appbuilder import Message, DocParser, DocSplitter, QAPairMining + + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +# os.environ["APPBUILDER_TOKEN"] = "..." + +# 进行文档内容解析 +file_url = "https://agi-dev-platform-bos.bj.bcebos.com/ut_appbuilder/test.pdf?authorization=bce-auth-v1/e464e6f951124fdbb2410c590ef9ed2f/2024-01-25T12%3A56%3A15Z/-1/host/b54178fea9be115eafa2a8589aeadfcfaeba20d726f434f871741d4a6cb0c70d" +file_data = requests.get(file_url).content +file_path = "./test.pdf" # 待解析的文件路径 +with open(file_path, "wb") as f: + f.write(file_data) + +# 解析文档 +msg = Message(file_path) +parser = DocParser() +parse_result = parser.run(msg, return_raw=True) + +# 对文档进行分段落,split_by_chunk需要return_raw=True +splitter = DocSplitter( + splitter_type="split_by_chunk", overlap=0) +split_result = splitter(parse_result) + +# 每个段落抽取问答对,并返回结果 +for doc_segment in split_result.content["paragraphs"]: + qa_mining = QAPairMining(model="Qianfan-Agent-Speed-8k") + text = doc_segment.get("text", "") + if text == "": + logger.error("Text is null. break") + break + logger.info("Input: \n{}".format(text)) + answer = qa_mining(Message(text)) + logger.info("Output: \n{}".format(answer.content)) + break # 样例代码只跑1个段落 +``` + +## 更新记录和贡献 +* 问答对挖掘能力 (2023-12) diff --git a/docs/BasisModule/Components/llms/query_decomposition/README.md b/docs/BasisModule/Components/llms/query_decomposition/README.md new file mode 100644 index 000000000..d1b7f06bc --- /dev/null +++ b/docs/BasisModule/Components/llms/query_decomposition/README.md @@ -0,0 +1,75 @@ +# 复杂Query分解(QueryDecomposition) + +## 简介 +复杂Query分解(QueryDecomposition)可以将已经确定为复杂问题的原始问题拆解为一个个简单问题。广泛应用在知识问答场景。 + +### 功能介绍 +在知识问答领域中存在很多复杂问题需要处理,这些复杂问题通常需要进行问题分解并采用分治的方法处理。复杂Query分解组件尝试对已经判定为复杂问题的原始问题进行拆解,把复杂问题拆解为一个个简单问题。 + +### 特色优势 +复杂Query分解组件可以将复杂问题分解为不同层级的简单问题。这有助于更系统地分析和解决问题,提高答案的全面性。 + +### 应用场景 +广泛应用于知识问答场景 + +## 基本用法 +下面是复杂Query分解的代码示例: +```python +import os +import appbuilder + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = "..." + +query_decomposition = appbuilder.QueryDecomposition(model="Qianfan-Agent-Speed-8k") + +msg = "吸塑包装盒在工业化生产和物流运输中分别有什么重要性?" +msg = appbuilder.Message(msg) +answer = query_decomposition(msg) + +print("Answer: \n{}".format(answer.content)) +``` + +## 参数说明 + +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 初始化参数 +|参数名称 |参数类型 |是否必须 |描述 |示例值| +|--------|--------|--------|----|------| +|model |str |是 |模型名称,用于指定要使用的千帆模型|Qianfan-Agent-Speed-8k| + +### 调用参数 +|参数名称 |参数类型 |是否必须 |描述 |示例值| +|--------|--------|--------|----|------| +|message |obj:`Message`|是 |输入消息,用于模型的主要输入内容。这是一个必需的参数| | +|stream|bool|否 |指定是否以流式形式返回响应。默认为 False|False| +|temperature|float|否 |模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10|1e-10| + +### 响应参数 +|参数名称 |参数类型 |描述 |示例值| +|--------|--------|----|------| +|Message |obj:`Message` |输出消息,包含模型运行后的输出| | + +### 响应示例 +```text +1. 吸塑包装盒在工业化生产中有什么重要性? +2. 吸塑包装盒在物流运输中有什么重要性? +``` +### 错误码 +无 + +## 高级用法 +你可以通过自定义调整参数来获得想要的结果,例如: +```python +# 流式返回, 调整模型temperature参数 +answer = query_decomposition(msg, stream=True, temperature=0.5) +``` + +## 更新记录和贡献 +* 复杂Query分解 (2024-01) diff --git a/docs/BasisModule/Components/llms/query_rewrite/README.md b/docs/BasisModule/Components/llms/query_rewrite/README.md new file mode 100644 index 000000000..ca568cf03 --- /dev/null +++ b/docs/BasisModule/Components/llms/query_rewrite/README.md @@ -0,0 +1,66 @@ +# 多轮改写 (QueryRewrite) + +## 简介 +多轮改写组件 (QueryRewrite) 是一个用于处理多轮对话和查询改写的组件。它主要用于理解和优化用户与机器人的交互过程,进行指代消解及省略补全。该组件支持不同的改写类型,可根据对话历史生成更准确的用户查询。 + +### 功能介绍 +多轮改写组件 (QueryRewrite) 据用户和机器人的聊天记录,改写用户当前query,利用大语言模型的理解及生成能力,进行指代消解及省略补全。 + +### 特色优势 +多轮改写组件 (QueryRewrite) ,基于百度自研的大语言模型文心一言,无需更多的prompt描述,即可根据对话历史生成更准确的用户查询。 + +### 应用场景 +多轮改写组件 (QueryRewrite) 可用于智能问答、对话式搜索等场景。 + +## 基本用法 + +以下是一个简单的例子,展示如何快速开始使用 QueryRewrite 组件: + +```python +import os +import appbuilder + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = '...' + +# 初始化并使用 QueryRewrite 组件 +query_rewrite = appbuilder.QueryRewrite(model="Qianfan-Agent-Speed-8k") +answer = query_rewrite(appbuilder.Message(['我应该怎么办理护照?', '您可以查询官网或人工咨询', '我需要准备哪些材料?', '身份证、免冠照片一张以及填写完整的《中国公民因私出国(境)申请表》', '在哪里办']), rewrite_type="带机器人回复") +print(answer) +``` + +## 参数说明 + +### 鉴权配置 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 初始化参数 +- `model`: 模型名称,用于指定要使用的千帆模型。 + +### 调用参数 +|参数名称 |参数类型 |是否必须 |描述 |示例值| +|--------|--------|--------|----|------| +|message |Message |是 |需要改写的文本,用于模型的主要输入内容,这是一个必需的参数。 |Message(content=['我应该怎么办理护照?', '您可以查询官网或人工咨询','我需要准备哪些材料?', '身份证、免冠照片一张以及填写完整的《中国公民因私出国(境)申请表》', '在哪里办']) | +|rewrite_type |str |否 |改写类型选项,可选值为 '带机器人回复'(改写时参考user查询历史和assistant回复历史),'仅用户查询'(改写时参考user查询历史)。 默认是"带机器人回复" |"带机器人回复" | +|stream |bool | 否 |指定是否以流式形式返回响应。默认为 False。 |False | +|temperature |float | 否 |模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。对于此组件勿动! |1e-10 | + +### 响应参数 +|参数名称 |参数类型 |描述 |示例值| +|--------|--------|----|------| +|result |Message |返回结果|Message(content="身份证在哪办")| + +### 响应示例 +``` +Message(name=msg, content="身份证在哪办", mtype=dict, extra={}) +``` + +## 示例和案例研究 +实际应用中,QueryRewrite 可用于多种场景,如信息检索、智能对话等。 + +## 更新记录和贡献 +当前版本:v2 (2023-12) diff --git a/docs/BasisModule/Components/llms/similar_question/README.md b/docs/BasisModule/Components/llms/similar_question/README.md new file mode 100644 index 000000000..ca5b11297 --- /dev/null +++ b/docs/BasisModule/Components/llms/similar_question/README.md @@ -0,0 +1,90 @@ +# 相似问生成(SimilarQuestion) + +## 简介 +相似问生成组件(SimilarQuestion)可以用于基于输入的问题,挖掘出与该问题相关的类似问题。广泛用于客服、问答等场景。 + +### 功能介绍 +基于输入的问题,挖掘出与该问题相关的类似问题。 + +### 特色优势 +相似问生成组件,可一次生成多个相似问题,准确率可达90%以上。 + +### 应用场景 +应用于客服、问答等场景 + +## 基本用法 + +### 快速开始 + +```python +import os +import appbuilder + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = "..." + +similar_question = appbuilder.SimilarQuestion(model="Qianfan-Agent-Speed-8k") + +msg = "我想吃冰淇淋,哪里的冰淇淋比较好吃?" +msg = appbuilder.Message(msg) +answer = similar_question(msg) + +print("Answer: \n{}".format(answer.content)) +``` + +## 参数说明 + +### 初始化参数 +无 + + +### 调用参数 +|参数名称 |参数类型 |是否必须 |描述 |示例值| +|--------|--------|--------|----|------| +| message | String |是 |输入消息,用于模型的主要输入内容。这是一个必需的参数。| `Message("我想吃冰淇淋,哪里的冰淇淋比较好吃?")` | +| stream |bool|否 |指定是否以流式形式返回响应。默认为 False。| False | +| temperature |float|否 |模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 | 1e-10 | + +### 响应参数 +|参数名称 |参数类型 |描述 |示例值| +|--------|--------|----|------| +| result | String | 返回结果 | "1. 哪里能品尝到美味的冰淇淋?\n2. 哪..." | + +### 响应示例 + +```text +1. 哪里能品尝到美味的冰淇淋? +2. 哪里能买到好吃的冰淇淋? +3. 哪里能品尝到正宗的冰淇淋? +4. 哪里能品尝到最新鲜的冰淇淋? +5. 哪里能买到口感最好的冰淇淋? +6. 哪里能品尝到最经典的冰淇淋? +``` + +### 错误码 +|错误码|描述| +|------|---| + + +## 高级用法 + +### 特殊场景示例 + +你可以根据特定的场景调整参数来获得更精确的结果,例如: + +```python +# 流式返回, 调整模型temperature参数 +answer = similar_question(msg, stream=True, temperature=0.5) +``` + +## 示例和案例研究 + +### 示例 + +- **场景:** 用户提出问题 +- **输入:** "我想吃冰淇淋,哪里的冰淇淋比较好吃?" +- **输出:** 1. 请问哪里的冰淇淋最美味? 2. 在哪些地方可以品尝到最好的冰淇淋? ..... + + +## 更新记录和贡献 +* 相似问生成能力 (2023-12) \ No newline at end of file diff --git a/docs/BasisModule/Components/llms/style_rewrite/README.md b/docs/BasisModule/Components/llms/style_rewrite/README.md new file mode 100644 index 000000000..f694e3762 --- /dev/null +++ b/docs/BasisModule/Components/llms/style_rewrite/README.md @@ -0,0 +1,82 @@ +# 风格转写 (StyleRewrite) + +## 简介 +风格转写组件(StyleRewrite) 可以基于生成式大模型对文本的风格进行改写。支持多种文本风格,包括营销、客服、直播、激励及教学话术。 + + +### 功能介绍 +文本风格转写能够将一段文本转换成不同的风格(营销、客服、直播、激励及教学话术),同时保持原文的基本意义不变。基于大模型的文本理解与生成能力,根据用户指定的风格重新组织语言和表达方式。它不仅能识别和模仿不同的写作风格,还能在转换过程中保持文本的连贯性和逻辑性。 + +### 特色优势 +- 准确性与流畅性: 在转写时保持原文意义的准确传达,同时确保文本流畅自然。 +- 风格多样性: 能够覆盖广泛的文本风格,满足不同场合的需求。 +- 提高效率: 节省人工重新编写文本的时间。 + +### 应用场景 +不同的文本风格可以应用到以下不同的场景中: +- 营销风格:该风格的转写能力可以根据商品的描述生成一段营销文案,生动地为用户介绍商品特点。 +- 客服风格:针对客户关心的产品问题,用礼貌易懂的口语化文本进行回复,提升客户服务体验。 +- 直播风格:在线直播平台的主播或内容创作者可以使用文本风格转写技术来优化其直播脚本,使其更吸引特定的观众群体。 +- 激励风格:在激励演讲、团队管理或个人发展领域,文本风格转写可以帮助创建更有动力和启发性的内容。起到鼓舞人心的作用。 +- 教学风格:可以帮助教师或教材作者生成互动感强、并且引人入胜的教学材料。 + + +## 基本用法 + +以下是一个简单的例子,展示如何快速开始使用 StyleRewrite 组件: + +```python +import os +import appbuilder + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = "..." + +# 初始化并使用 StyleRewrite 组件 +style_rewrite = appbuilder.StyleRewrite(model="Qianfan-Agent-Speed-8k") +answer = style_rewrite(appbuilder.Message("文心大模型发布新版"), style="激励话术") +``` + + + +## 参数说明 + +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 初始化参数 +无 + +### 调用参数 +|参数名称 |参数类型 |是否必须 |描述 |示例值| +|--------|--------|--------|----|------| +|message |String |是 |需要改写的文本|文心大模型发布新版| +|style|String|否 |想要转换的文本风格,默认为"营销话术",目前支持营销、客服、直播、激励及教学五种话术。|激励话术| +|stream|bool|否 |指定是否以流式形式返回响应,默认为 False。|True| +|temperature|float|否 |模型配置的温度参数,默认为 1e-10|1e-10| + +### 响应参数 +|参数名称 |参数类型 |描述 |示例值| +|--------|--------|----|------| +|result |String |返回结果|"文心NLP大模型平台再添新功绩!近日,文心NLP大模型平台成功发布新版,这标志着我们在这个领域又取得了新的里程碑。新版的文心NLP大模型平台在技术、功能和易用性等方面都有了显著的提升,为用户提供了更好的使用体验。"| + +### 响应示例 +```json +{"result": "文心NLP大模型平台再添新功绩!近日,文心NLP大模型平台成功发布新版,这标志着我们在这个领域又取得了新的里程碑。新版的文心NLP大模型平台在技术、功能和易用性等方面都有了显著的提升,为用户提供了更好的使用体验。"} +``` + +### 错误码 +|错误码|描述| +|------|---| + +## 高级用法 + +目前该模块仅提供基础的文本风格转写功能。 + + +## 更新记录和贡献 +* 当前版本:v1 diff --git a/docs/BasisModule/Components/llms/style_writing/README.md b/docs/BasisModule/Components/llms/style_writing/README.md new file mode 100644 index 000000000..9ae429339 --- /dev/null +++ b/docs/BasisModule/Components/llms/style_writing/README.md @@ -0,0 +1,78 @@ +# 风格写作(StyleWriting) + +## 简介 +风格写作组件(StyleWriting)是一款基于生成式大模型进行文本创作的工具,支持多种风格,包括B站、小红书等,适用于编写文案、广告等多种场景。 + +### 功能介绍 +风格写作组件(StyleWriting)根据用户的输入内容和风格要求,利用大语言模型的生成能力,自动生成符合特定风格的文案。 + + +### 特色优势 +风格写作组件(StyleWriting),基于百度自研的大语言模型文新一言,提供内置的风格生成能力,无需更多的prompt描述,即可生成对应风格的文案。 + + +### 应用场景 +风格写作组件(StyleWriting)可用于特定平台的文案生成分发营销场景。 + + +## 基本用法 + +为了快速开始使用风格写作组件,您可以参考以下步骤: + +```python +import appbuilder +import os + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = "..." + +model = "Qianfan-Agent-Speed-8k" +style_writing = appbuilder.StyleWriting(model) + +query = "帮我写一篇关于人体工学椅的文案" +style = "小红书" +length = 100 + +msg = appbuilder.Message(query) +answer = style_writing(message=msg, style_query=style, length=length) +print(answer) +``` + +## 参数说明 + +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 初始化参数 +- `model`: 模型名称,用于指定要使用的千帆模型。 + +### 调用参数 +|参数名称 |参数类型 |是否必须 |描述 |示例值| +|--------|--------|--------|----|------| +|message |Message |是 |输入的消息,用于模型的主要输入内容,这是一个必需的参数。 |Message(content="帮我生成一个介绍保温杯的话术") | +|style_query |str |否 |定义生成文案的格式,包括"通用"、"B站"、"小红书",默认为"通用" |"通用" | +|length |int |否 |定义生成文案的长度,可选 '短' (100), '中' (300), '长' (600), 默认100 |100 | +|stream |bool | 否 |指定是否以流式形式返回响应。默认为 False。 |False | +|temperature |float | 否 |模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 |0.7 | + +### 响应参数 +|参数名称 |参数类型 |描述 |示例值| +|--------|--------|----|------| +|result |Message |返回结果|Message(content="大家好,我给你们介绍一款家里和办公室都要备上的保温杯。...")| + +### 响应示例 +``` +Message(name=msg, content=大家好,我给你们介绍一款家里和办公室都要备上的保温杯。我平常上班的时候都会装上一杯热开水,但用普通保温杯装上一会就凉了,所以我赶紧在网上淘了一个好货。它是双层设计,内层是不锈钢材质,外层是玻璃材质,非常贴心,冷热都能装。装上热水,保温效果非常出色,到晚上还是热的。这个保温杯的外观也非常漂亮,采用优质不锈钢材质,耐磨、易清洗。同时,它还非常轻便,可以轻松放入口袋、背包中。有了这个保温杯后,我再也不用担心喝水问题了。无论是在家里、办公室还是户外活动,它都能随时随地为你提供热水。而且,它还非常安全、健康,采用了优质的保温材料和先进的生产工艺。无论男女老少都可以使用这款保温杯哦。快来购买吧。", mtype=dict, extra={}) +``` + +## 高级用法 + +使用风格写作组件进行更复杂的文本创作,例如调整不同的风格和长度参数来适应特定的写作场景。 + +## 更新记录和贡献 + +- 初始版本发布(2023-10) diff --git a/docs/BasisModule/Components/llms/tag_extraction/README.md b/docs/BasisModule/Components/llms/tag_extraction/README.md new file mode 100644 index 000000000..313541a7f --- /dev/null +++ b/docs/BasisModule/Components/llms/tag_extraction/README.md @@ -0,0 +1,83 @@ +# 标签抽取(TagExtraction) + +## 简介 +标签抽取组件(TagExtraction)是一款高效的标签抽取组件,基于生成式大模型,专门用于从文本中提取关键标签。它适用于各种文本分析场景,如内容分类、关键词提取等。 + +### 功能介绍 +标签抽取组件(TagExtraction)专门设计用于从各类文本中高效地提取关键标签。此组件利用先进的生成式大模型,可以准确识别和提取文本中的重要信息,如关键词、短语或主题。它不仅能快速分析大量文本数据,还能精准识别文本的核心内容,支持用户快速了解文本的主要信息和结构 + +### 特色优势 +- 高效准确:基于先进的生成式大模型,提供高效且准确的标签提取功能。 +- 适用广泛:能够处理不同类型和格式的文本数据,适用于多种文本分析场景。 +- 格式友好:输出格式采用规范化编号输出,后处理时方便快捷。 + +### 应用场景 +标签抽取组件可以广泛应用于多种场景: + +- 内容分类:快速为文章或文档分类,提高信息管理和检索效率。 +- 关键词提取:从文本中提取关键词,帮助用户快速了解文本主题和内容。 +- 数据分析:在大数据分析中,可以用于预处理,提取有价值的信息。 +- 搜索引擎优化:帮助网站或博客提取有效标签,改善其在搜索引擎中的可见度。 + + +## 基本用法 + +要开始使用 `TagExtraction`,首先需要设置环境变量 `APPBUILDER_TOKEN`,然后创建 `TagExtraction` 实例并传递文本消息。 + +```python +import os +import appbuilder + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = '...' + +tag_extraction = appbuilder.TagExtraction(model="Qianfan-Agent-Speed-8k") +result = tag_extraction(appbuilder.Message("从这段文本中抽取关键标签")) +``` + +这个例子展示了如何实例化 `TagExtraction` 组件并使用一个文本消息进行标签抽取。 + +## 参数说明 + +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 初始化参数 + +无 + +### 调用参数 + +|参数名称 |参数类型 |是否必须 |描述 |示例值| +|--------|--------|--------|----|------| +|message |String |是 |需要抽取标签的文本|从这段文本中抽取关键标签| +|stream|bool|否 |指定是否以流式形式返回响应,默认为 False。|True| +|temperature|float|否 |模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。|1e-10| + +### 响应参数 +|参数名称 |参数类型 |描述 |示例值| +|--------|--------|----|------| +|result |Message |返回结果|对象,包含模型运行后的输出消息。| +### 响应示例 +```json +{"result": "1.5G 2.云计算 3.人工智能 4.数字经济 5.数据中心 6.新型基础设施 7.政策优化 8.产业发展 9.国家重视 10.快速增长"} +``` + +## 高级用法 + +高级用法可以包括自定义模型参数或使用不同的模型源。例如,可以通过指定不同的 `model` 来使用特定于域的模型进行标签抽取。 + +```python +tag_extraction = appbuilder.TagExtraction(model="custom-model") +result = tag_extraction(appbuilder.Message("自定义模型抽取的标签")) +``` + +## 示例和案例研究 + +在实际应用中,`TagExtraction` 可以用于新闻文章、社交媒体帖子或其他任何文本内容的关键标签提取,帮助内容创建者或营销分析师快速了解主要主题和趋势。 + + diff --git a/docs/BasisModule/Components/matching/README.md b/docs/BasisModule/Components/matching/README.md new file mode 100644 index 000000000..3134870eb --- /dev/null +++ b/docs/BasisModule/Components/matching/README.md @@ -0,0 +1,89 @@ +# 语义匹配(Matching) + +## 简介 + +语义匹配组件(Matching)可以计算query与文本列表之间的相似度关系,并根据其进行排序。 + +### 功能介绍 + +根据query与文本列表之间的相似度关系,并根据其进行排序。 + +### 特色优势 + +基于百度文心大模型技术的文本表示模型,学习数据的内在特征,使得排序效果相较于bm25等排序算法,可以更好地处理相似问和同义、近义句子之间的偏序关系。 + +### 应用场景 + +1. 语义排序 + +## 基本用法 + +### 下面是基于query和文本之间的相似度进行匹配排序的代码示例 + +```python +import os +import appbuilder + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = '...' + +# 初始化所需要的组件 +embedding = appbuilder.Embedding() +matching = appbuilder.Matching(embedding) + +# 定义query和文本列表 +query = appbuilder.Message("你好") +contexts = appbuilder.Message(["世界", "你好"]) + +contexts_matched = matching(query, contexts) +print(contexts_matched.content) +``` + +``` +['你好', '世界'] +``` + +## 参数说明 + +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 初始化参数 + +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +| ---------- | ----------- | -------- | ------------------------------------------------------------- | --------------- | +| embedding | Embedding | 可选 | 一个类型为Embedding的Component,用于初始化 Matching 的向量计算功能。底座模型当前仅支持 embedding-v1 作为可选值。若不指定,默认值为 embedding-v1 。 | appbuilder.Embedding() | + +### 调用参数 + +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +| --------- | ----------- | -------- | ------------------------------------------------------------ | ---------------------------------- | +| query | 字符串 | 必须 | 一个类型为 string 的句子,用于输入。该句子的长度不能超过384个字符,通常为用户输入的问题。 | "如何提高工作效率?" | +| contexts | 字符串列表 | 必须 | 一个类型为 List[string] 的句子数组。数组中的每个元素都是一个句子,且每个句子的长度不能超过384个字符。这些句子通常为与问题相关的文本候选集。 | ["时间管理技巧", "提高专注力的方法"] | +| return_score | 布尔 | 可选 | 默认为False, 仅返回排序后的字符串列表;当设置为True时,返回匹配分数和字符串的二元组列表 | + +### 响应示例 + +默认为排完序后的字符串列表 + +``` +["时间管理技巧", "提高专注力的方法"] +``` + +当设置`return_score = True`时,二元组的第一个值为相似度分数,第二个值为字符串 + +``` +[(0.9999999852985002, '你好'), (0.18920520439845268, '世界')] +``` + +### 错误码 + +无 + +## 更新记录和贡献 + +* 语义匹配 (2023-12) diff --git a/docs/BasisModule/Components/mix_card_ocr/README.md b/docs/BasisModule/Components/mix_card_ocr/README.md new file mode 100644 index 000000000..60bd2a925 --- /dev/null +++ b/docs/BasisModule/Components/mix_card_ocr/README.md @@ -0,0 +1,215 @@ +# 身份证混贴识别(MixCardOCR) + +## 简介 +身份证混贴识别(MixCardOCR),身份证混贴识别支持自动检测与识别身份证正反面在同一张图片上的场景,一次识别图片中身份证正反面所有字段。 + + +### 功能介绍 +支持对二代居民身份证正反面所有8个字段进行结构化识别,包括姓名、性别、民族、出生日期、住址、身份证号、签发机关、有效期限,识别准确率超过99%;同时支持身份证正面头像检测,并返回头像切片的base64编码及位置信息。 + +### 特色优势 +同时,支持对用户上传的身份证图片进行图像风险和质量检测,可识别图片是否为复印件或临时身份证,是否被翻拍或编辑,是否存在正反颠倒、模糊、欠曝、过曝等质量问题。 + + + +### 应用场景 +身份证混贴识别支持自动检测与识别身份证正反面在同一张图片上的场景,一次识别图片中身份证正反面所有字段。 + + +## 基本用法 + +下面是身份证混贴识别的代码示例: + +示例图片为:![示例图片](https://bj.bcebos.com/v1/appbuilder/test_mix_card_ocr.jpeg?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-24T06%3A18%3A11Z%2F-1%2Fhost%2F695b8041c1ded194b9e80dbe1865e4393da5a3515e90d72d81ef18296bd29598) + +```python +import os +import requests +import appbuilder + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = "..." + +image_url= "https://bj.bcebos.com/v1/appbuilder/test_mix_card_ocr.jpeg?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-24T06%3A18%3A11Z%2F-1%2Fhost%2F695b8041c1ded194b9e80dbe1865e4393da5a3515e90d72d81ef18296bd29598" + + +# 从BOS存储读取样例文件 +raw_image = requests.get(image_url).content +inp = appbuilder.Message(content={"raw_image": raw_image}) +# inp = Message(content={"url": image_url}) + +# 运行身份证混贴识别OCR +mix_card_ocr = appbuilder.MixCardOCR() +out = mix_card_ocr.run(inp) +# 打印识别结果 +print(out.content) + +# {'front': {'fields': [{'key': '出生', 'value': '19920225', 'position': {'left': 620, 'top': 218, 'width': 239, 'height': 30}}, {'key': '性别', 'value': '女', 'position': {'left': 616, 'top': 164, 'width': 25, 'height': 30}}, {'key': '民族', 'value': '汉', 'position': {'left': 766, 'top': 164, 'width': 29, 'height': 30}}, {'key': '姓名', 'value': '姚佳', 'position': {'left': 621, 'top': 102, 'width': 84, 'height': 35}}, {'key': '公民身份号码', 'value': '110103199202250229', 'position': {'left': 733, 'top': 417, 'width': 399, 'height': 36}}, {'key': '住址', 'value': '北京市海淀区仙秀园555号', 'position': {'left': 618, 'top': 277, 'width': 253, 'height': 67}}], 'position': {'left': 483, 'top': 42, 'width': 737, 'height': 464}}, 'back': {'fields': [{'key': '签发日期', 'value': '20150413', 'position': {'left': 789, 'top': 946, 'width': 139, 'height': 34}}, {'key': '签发机关', 'value': '北京市公安局海淀分局', 'position': {'left': 787, 'top': 883, 'width': 275, 'height': 35}}, {'key': '失效日期', 'value': '20350413', 'position': {'left': 946, 'top': 945, 'width': 144, 'height': 34}}], 'position': {'left': 473, 'top': 537, 'width': 749, 'height': 480}}, 'direction': 0} +``` + + +## 参数说明 + +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +import os + +os.environ["APPBUILDER_TOKEN"] = "..." +``` + +### 初始化参数 + +无 + +### 调用参数 +| 参数名称 | 参数类型 | 是否必须 | 描述 |示例值| +|------------|--------|------|-----------------------------|---| +| message | String | 是 | 输入的消息,用于模型的主要输入内容。这是一个必需的参数 || +| +content | Dict | 是 | 消息内容 || +| +raw_image | String | 否 | 原始图片字节流 || +| +url | String | 否 | 图片下载链接地址 || +|timeout| Float | 否 | HTTP超时时间,单位:秒 |1|| +|retry|Integer| 否 | HTTP重试次数 |3|| + +### 响应参数 + +| 参数名称 | 参数类型 | 描述 | 示例值 | +|------------|--------|---------------------|----------------------------------------------------| +| front | object | 身份证正面信息 | | +| +fields | list | 字段信息 | | +| ++key | str | 字段名 | | +| ++value | str | 字段值 | | +| ++position | object | 字段坐标,结构同下position | | +| +position | object | 身份证正面坐标 | | +| ++left | int | 表示定位位置的长方形左上顶点的水平坐标 | | +| ++top | int | 表示定位位置的长方形左上顶点的垂直坐标 | | +| ++width | int | 表示定位位置的长方形的宽度 | | +| ++height | int | 表示定位位置的长方形的高度 | | +| back | object | 身份证反面信息,结构同front | | +| direction | int | 图像旋转角度 | 图像旋转角度,0(正向),- 1(逆时针90度),- 2(逆时针180度),- 3(逆时针270度) | + +### 响应示例 +```json +{ + "front": { + "fields": [{ + "key": "出生", + "value": "19920225", + "position": { + "left": 620, + "top": 218, + "width": 239, + "height": 30 + } + }, + { + "key": "性别", + "value": "女", + "position": { + "left": 616, + "top": 164, + "width": 25, + "height": 30 + } + }, + { + "key": "民族", + "value": "汉", + "position": { + "left": 766, + "top": 164, + "width": 29, + "height": 30 + } + }, + { + "key": "姓名", + "value": "姚佳", + "position": { + "left": 621, + "top": 102, + "width": 84, + "height": 35 + } + }, + { + "key": "公民身份号码", + "value": "110103199202250229", + "position": { + "left": 733, + "top": 417, + "width": 399, + "height": 36 + } + }, + { + "key": "住址", + "value": "北京市海淀区仙秀园555号", + "position": { + "left": 618, + "top": 277, + "width": 253, + "height": 67 + } + } + ], + "position": { + "left": 483, + "top": 42, + "width": 737, + "height": 464 + } + }, + "back": { + "fields": [{ + "key": "签发日期", + "value": "20150413", + "position": { + "left": 789, + "top": 946, + "width": 139, + "height": 34 + } + }, + { + "key": "签发机关", + "value": "北京市公安局海淀分局", + "position": { + "left": 787, + "top": 883, + "width": 275, + "height": 35 + } + }, + { + "key": "失效日期", + "value": "20350413", + "position": { + "left": 946, + "top": 945, + "width": 144, + "height": 34 + } + } + ], + "position": { + "left": 473, + "top": 537, + "width": 749, + "height": 480 + } + }, + "direction": 0 +} +``` + +### 错误码 +|错误码|描述| +|------|---| + +## 高级用法 +目前该模块仅提供基础的身份证混贴识别。 + +## 更新记录和贡献 +* 身份证混贴识别 (2024-01) \ No newline at end of file diff --git a/docs/BasisModule/Components/object_recognize/README.md b/docs/BasisModule/Components/object_recognize/README.md new file mode 100644 index 000000000..2ac5d94f2 --- /dev/null +++ b/docs/BasisModule/Components/object_recognize/README.md @@ -0,0 +1,115 @@ +# 通用物体和场景识别-高级版(ObjectRecognition) + +## 简介 +通用物体和场景识别组件(ObjectRecognition)可以识别超过10万类常见物体和场景,接口返回大类及细分类的名称。广泛适用于图像或视频内容分析、拍照识图等业务场景。 +### 功能介绍 +* 识别物体或场景名称 + + 识别动物、植物、商品、建筑、风景、动漫、食材、公众人物等10万个常见物体及场景,接口返回大类及细分类的名称结果; + +### 特色优势 +* 可识别超过10万类常见物体和场景,接口返回大类及细分类的名称,并支持获取识别结果对应的百科信息; + +### 应用场景 +可以识别超过10万类常见物体和场景,广泛适用于图像或视频内容分析、拍照识图等业务场景 + + +## 基本用法 + +示例图片为: + +![示例图片](https://bj.bcebos.com/v1/appbuilder/object_recognize_test.png?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-11T11%3A00%3A19Z%2F-1%2Fhost%2F2c31bf29205f61e58df661dc80af31a1dc1ba1de0a8f072bc5a87102bd32f9e3) + + + +```python +import os +import appbuilder +import requests + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = '...' + +# 从BOS读取样例图片 +image_url = "https://bj.bcebos.com/v1/appbuilder/object_recognize_test.png?"\ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-"\ + "11T11%3A00%3A19Z%2F-1%2Fhost%2F2c31bf29205f61e58df661dc80af31a1dc"\ + "1ba1de0a8f072bc5a87102bd32f9e3" +raw_image = requests.get(image_url).content +# 创建物体识别组件实例 +object_recognition = appbuilder.ObjectRecognition() +# 执行识别操作并获取结果 +out = object_recognition.run(appbuilder.Message(content={"raw_image": raw_image})) +print(out.content) +# {'result': [{'keyword': '苹果', 'score': 0.961247, 'root': '植物-蔷薇科'}, {'keyword': '姬娜果', 'score': 0.740838, 'root': '植物-其它'}, {'keyword': '梨子', 'score': 0.392218, 'root': '商品-水果'}, {'keyword': '车厘子', 'score': 0.193986, 'root': '植物-其它'}, {'keyword': '石榴', 'score': 0.000239, 'root': '植物-千屈菜科'}]} +``` + +## 参数说明 + +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 初始化参数 +无 + +### 调用参数 (以表格形式展示) +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +|---------|---------|------|-----------------------------|------------------------------------------------| +| message | String | 是 | 输入的消息,用于模型的主要输入内容。这是一个必需的参数 | Message(content={"raw_image": b"待识别的图片字节流数据"}) | +|timeout| Float | 否 | HTTP超时时间,单位:秒 |1|| +| retry | Integer | 否 | HTTP重试次数 | 3 | + +### 响应参数 +| 参数名称 | 参数类型 | 描述 | 示例值 | +|----------|---------|-------------|-----------------------------------------------------| +| result | Array[] | 返回结果 | [{"keyword":"苹果","score":0.961247,"root":"植物-蔷薇科"}] | +| +keyword | String | 图片中的物体或场景名称 | "苹果" | +| +score | Float | 置信度 | 0.961247 | +| +root | String | 识别结果的上层标签 | "植物-蔷薇科" | + + +### 响应示例 +```json +{ + "result":[ + { + "keyword":"苹果", + "score":0.961247, + "root":"植物-蔷薇科" + }, + { + "keyword":"姬娜果", + "score":0.740838, + "root":"植物-其它" + }, + { + "keyword":"梨子", + "score":0.392218, + "root":"商品-水果" + }, + { + "keyword":"车厘子", + "score":0.193986, + "root":"植物-其它" + }, + { + "keyword":"石榴", + "score":0.000239, + "root":"植物-千屈菜科" + } + ] +} +``` +### 错误码 +| 错误码 | 描述 | +|-----|----| + +## 高级用法 +目前该模块仅提供基础通用物体与场景识别功能。 + +## 更新记录和贡献 +* 通用物体及场景识别 (2023-12-08) diff --git a/docs/BasisModule/Components/plant_recognize/README.md b/docs/BasisModule/Components/plant_recognize/README.md new file mode 100644 index 000000000..e8b04d83c --- /dev/null +++ b/docs/BasisModule/Components/plant_recognize/README.md @@ -0,0 +1,116 @@ +# 植物识别(PlantRecognition) + +## 简介 +植物识别(PlantRecognition),即对于输入的一张图片(可正常解码,且长宽比较合适),输出植物识别结果。 + +### 功能介绍 +可识别超过2万种常见植物和近8千种花卉,接口返回植物的名称,并支持获取识别结果对应的百科信息 + +### 特色优势 +还可使用EasyDL定制训练平台,定制识别植物种类 + +### 应用场景 +适用于拍照识图、幼教科普、图像内容分析等场景 + +## 基本用法 + +下面是植物识别的代码示例: + +示例图片为 + +![示例图片](https://bj.bcebos.com/v1/appbuilder/palnt_recognize_test.jpg?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-23T09%3A51%3A03Z%2F-1%2Fhost%2Faa2217067f78f0236c8262cdd89a4b4f4b2188d971ca547c53d01742af4a2cbe) + +```python +import os +import requests +import appbuilder + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = "..." +image_url = "https://bj.bcebos.com/v1/appbuilder/palnt_recognize_test.jpg?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-23T09%3A51%3A03Z%2F-1%2Fhost%2Faa2217067f78f0236c8262cdd89a4b4f4b2188d971ca547c53d01742af4a2cbe" + +# 从BOS存储读取样例文件 +raw_image = requests.get(image_url).content +inp = appbuilder.Message(content={"raw_image": raw_image}) +# inp = Message(content={"url": image_url}) + +# 运行植物识别 +plant_recognize = appbuilder.PlantRecognition() +out = plant_recognize.run(inp) +# 打印识别结果 +print(out.content) + +# {'plant_score_list': [{'name': '榕树', 'score': 0.4230029582977295}, {'name': '榆树', 'score': 0.1273619383573532}, {'name': '美国榆', 'score': 0.12065108865499496}, {'name': '白蜡树', 'score': 0.11650644987821579}, {'name': '雨树', 'score': 0.045340824872255325}]} +``` + + +## 参数说明 + +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +import os + +os.environ["APPBUILDER_TOKEN"] = "..." +``` + +### 初始化参数 + +无 + +### 调用参数 +| 参数名称 | 参数类型 | 是否必须 | 描述 |示例值| +|------------|--------|------|-----------------------------|---| +| message | String | 是 | 输入的消息,用于模型的主要输入内容。这是一个必需的参数 || +| +content | Dict | 是 | 消息内容 || +| +raw_image | String | 否 | 原始图片字节流 || +| +url | String | 否 | 图片下载链接地址 || +|timeout| Float | 否 | HTTP超时时间,单位:秒 |1|| +|retry|Integer| 否 | HTTP重试次数 |3|| + +### 响应参数 +| 参数名称 | 参数类型 | 描述 | 示例值 | +|------------------|--------|--------|--------------------------------------------------| +| plant_score_list | List | 植物识别列表 | | +| name | String | 植物名 | | +| score | Float | 植物识别打分 | | + + +### 响应示例 +```json +{ + "plant_score_list": [ + { + "name": "榕树", + "score": 0.4230029582977295 + }, + { + "name": "榆树", + "score": 0.1273619383573532 + }, + { + "name": "美国榆", + "score": 0.1206519496 + }, + { + "name": "白蜡树", + "score": 0.11650644987821579 + }, + { + "name": "雨树", + "score": 0.045340824872255325 + } + ] +} +``` + +### 错误码 +|错误码|描述| +|------|---| + +## 高级用法 +目前该模块仅提供基础的植物识别。 + + +## 更新记录和贡献 +* 植物识别 (2024-01) \ No newline at end of file diff --git a/docs/BasisModule/Components/ppt_generation_from_file/README.md b/docs/BasisModule/Components/ppt_generation_from_file/README.md new file mode 100644 index 000000000..0267c84f9 --- /dev/null +++ b/docs/BasisModule/Components/ppt_generation_from_file/README.md @@ -0,0 +1,83 @@ +# 文件生成PPT(PPTGenerationFromFile) + +## 简介 +文件生成PPT组件(PPTGenerationFromFile)可以根据上传的文件(支持**中英文**)生成PPT。 + +### 功能介绍 +根据上传的文件(支持**中英文**)生成PPT。 + +### 特色优势 +可根据文件(支持**中英文**)生成高质量PPT。 + +### 应用场景 +PPT生成。 + +## 基本用法 +### 快速开始 +```python +import os +import appbuilder + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ['APPBUILDER_TOKEN'] = '...' + + +ppt_generator = appbuilder.PPTGenerationFromFile() + +user_input = { + 'file_url':'http://image.yoojober.com/users/chatppt/temp/2024-06/6672a92c87e6f.doc', + 'user_name':'百度千帆AppBuilder' +} +result = ppt_generator(appbuilder.Message(user_input)) +print(result.content) +``` + +## 参数说明 +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ['APPBUILDER_TOKEN'] = 'bce-YOURTOKEN' +``` + +### 初始化参数 + +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +| ------- | ------- | -------- | -------- | -------- | +| `secret_key` | str | 否 | 用户鉴权token,默认从环境变量中获取: `os.getenv("APPBUILDER_TOKEN", "")` | bce-v3/XXX | +| `gateway` | str | 否 | 后端网关服务地址,默认从环境变量中获取: `os.getenv("GATEWAY_URL", "")` | https://appbuilder.baidu.com | +| `lazy_certification` | bool | 否 | 延迟认证,为True时在第一次运行时认证。默认为False。 | False | + +### 调用参数 + +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +| ------- | ------- | -------- | -------- | -------- | +| `message` | obj | 是 | 输入消息,用于模型的主要输入内容。 | Message(content=input_data) | +| `poll_request_times` | int | 否 | 轮询请求结果次数。默认为60。 | 60 | +| `poll_request_interval` | int | 否 | 轮询请求的间隔时间(秒)。默认为5。 | 5 | + +其中message包含的input_data包括以下参数: + +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +| ------- | ------- | -------- | -------- | -------- | +| `file_url` | str | 是 | 文件链接。 | http://image.yoojober.com/users/chatppt/temp/2024-06/6672a92c87e6f.doc | +| `user_name` | str | 否 | 作者名。 | 百度千帆AppBuilder | + + +### 响应参数 +| 参数名称 | 参数类型 | 描述 | 示例值 | +| ------- | ------- | -------- | -------- | +| `result` | obj | 模型运行后的输出结果,包含PPT下载链接。 | Message(content='...') | + +### 响应示例 +``` +https://download.yoojober.com/chatppt_business/2024-07/d1a1ab518ebcfbba7908a6734fa11d13.pptx?e=1721964933&token=8_2qFlGEVQZPpFvHdGR6gg2t9A9QZfWT9wwTl92s:9bs8LWV1SQLJNJoJgtd-sLF-CTw= +``` + +## 高级用法 + +## 更新记录和贡献 +### 2024.8.1 +#### [Added] +- 增加文件生成PPT组件。 +- 增加文件生成PPT组件单元测试。 \ No newline at end of file diff --git a/docs/BasisModule/Components/ppt_generation_from_instruction/README.md b/docs/BasisModule/Components/ppt_generation_from_instruction/README.md new file mode 100644 index 000000000..34c8f0109 --- /dev/null +++ b/docs/BasisModule/Components/ppt_generation_from_instruction/README.md @@ -0,0 +1,90 @@ +# 指令生成PPT(PPTGenerationFromInstruction) + +## 简介 +指令生成PPT组件(PPTGenerationFromInstruction)可以基于指令或者自定义信息生成PPT。 + +### 功能介绍 +基于指令或者自定义信息生成PPT。 + +### 特色优势 +- 可生成高质量PPT。 +- 支持传入自定义信息生成PPT。 +- 生成PPT复杂度可控。 + +### 应用场景 +PPT生成。 + +## 基本用法 +### 快速开始 +```python +import os +import appbuilder + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ['APPBUILDER_TOKEN'] = '...' + + +ppt_generator = appbuilder.PPTGenerationFromInstruction() + +user_input = { + 'text': '生成一个介绍北京的PPT。', + 'custom_data': {}, + 'complex': 1, + 'user_name': '百度千帆AppBuilder' +} +result = ppt_generator(appbuilder.Message(user_input)) +print(result.content) +``` + +## 参数说明 +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ['APPBUILDER_TOKEN'] = 'bce-YOURTOKEN' +``` + +### 初始化参数 + +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +| ------- | ------- | -------- | -------- | -------- | +| `secret_key` | str | 否 | 用户鉴权token,默认从环境变量中获取: `os.getenv("APPBUILDER_TOKEN", "")` | bce-v3/XXX | +| `gateway` | str | 否 | 后端网关服务地址,默认从环境变量中获取: `os.getenv("GATEWAY_URL", "")` | https://appbuilder.baidu.com | +| `lazy_certification` | bool | 否 | 延迟认证,为True时在第一次运行时认证。默认为False。 | False | + +### 调用参数 + +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +| ------- | ------- | -------- | -------- | -------- | +| `message` | obj | 是 | 输入消息,用于模型的主要输入内容。 | Message(content=input_data) | +| `poll_request_times` | int | 否 | 轮询请求结果次数。默认为60。 | 60 | +| `poll_request_interval` | int | 否 | 轮询请求的间隔时间(秒)。默认为5。 | 5 | + +其中message包含的input_data包括以下参数: + +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +| ------- | ------- | -------- | -------- | -------- | +| `text` | str | 是 | 请求生成PPT的query。可为空字符串"",与custom_data有一个参数不为空即可。 | 请你生成一个介绍北京的PPT。 | +| `custom_data` | obj | 是 | 自定义参数,可指定标题、副标题、作者、结构等信息。可为空字典{},与text有一个参数不为空即可。 | `{"title": "标题", "sub_title": "副标题", "author": "作者", "catalogs": [{"catalog": "一级大纲1", "sub_catalog": ["一级大纲1-二级大纲1"]}, {"catalog": "一级大纲2", "sub_catalog": ["一级大纲2-二级大纲1"]}], "contents": [{"catalog_index": 0, "sub_catalog_index": 0, "content": ["一级大纲1-二级大纲1-内容1", "一级大纲1-二级大纲1-内容2"]}, {"catalog_index": 1, "sub_catalog_index": 0, "content": [{"key": "一级大纲2-二级大纲1-小标题1", "value": "一级大纲2-二级大纲1-子内容1"}, {"key": "一级大纲2-二级大纲1-小标题2", "value": "一级大纲2-二级大纲1-子内容2"}, {"key": "一级大纲2-二级大纲1-小标题3", "value": "一级大纲2-二级大纲1-子内容3"}], "picture": ["https://image.yoojober.com/chatppt_business/2024-02/000114cd07b809cb8c6bb22674e814da.png"]}]}` | +| `complex` | integer | 否 | PPT复杂度,可选:1、2、3,其中1最简单、3最复杂。 | 1 | +| `font_name` | str | 否 | PPT字体,可选:黑体、宋体、仿宋、幼圆、楷体、隶书。 | 黑体 | +| `user_name` | str | 否 | 作者名。 | 百度千帆AppBuilder | + + +### 响应参数 +| 参数名称 | 参数类型 | 描述 | 示例值 | +| ------- | ------- | -------- | -------- | +| `result` | obj | 模型运行后的输出结果,包含PPT下载链接。 | Message(content='...') | + +### 响应示例 +``` +https://download.yoojober.com/chatppt_business/2024-07/bf2af50285e52261507abdd7385e02c4.pptx?e=1721964536&token=8_2qFlGEVQZPpFvHdGR6gg2t9A9QZfWT9wwTl92s:nikgRM9RbPxzClBvmKrweeKd9Ck= +``` + +## 高级用法 + +## 更新记录和贡献 +### 2024.8.1 +#### [Added] +- 增加指令生成PPT组件。 +- 增加指令生成PPT组件单元测试。 \ No newline at end of file diff --git a/docs/BasisModule/Components/ppt_generation_from_paper/README.md b/docs/BasisModule/Components/ppt_generation_from_paper/README.md new file mode 100644 index 000000000..022f25d01 --- /dev/null +++ b/docs/BasisModule/Components/ppt_generation_from_paper/README.md @@ -0,0 +1,97 @@ +# 论文生成PPT(PPTGenerationFromPaper) + +## 简介 +论文生成PPT组件(PPTGenerationFromPaper)可以根据上传的论文(支持**中英文**)生成PPT。 + +### 功能介绍 +根据上传的论文(支持**中英文**)生成PPT。 + +### 特色优势 +可根据论文(支持**中英文**)生成高质量PPT。 + +### 应用场景 +PPT生成。 + +## 基本用法 +### 快速开始 +```python +import os +import appbuilder + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ['APPBUILDER_TOKEN'] = '...' + + +ppt_generator = appbuilder.PPTGenerationFromPaper() + +user_input = { + 'file_key': 'http://image.yoojober.com/users/chatppt/temp/2024-06/6672aa839a9da.docx', + 'style': '科技', + 'color': '蓝色', + 'title': '', + 'pleader': '百度千帆AppBuilder', + 'advisor': '百度千帆AppBuilder', + 'school': '北京大学', + 'school_logo': '', + 'school_picture': '' +} +result = ppt_generator(appbuilder.Message(user_input)) +print(result.content) +``` + +## 参数说明 +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ['APPBUILDER_TOKEN'] = 'bce-YOURTOKEN' +``` + +### 初始化参数 + +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +| ------- | ------- | -------- | -------- | -------- | +| `secret_key` | str | 否 | 用户鉴权token,默认从环境变量中获取: `os.getenv("APPBUILDER_TOKEN", "")` | bce-v3/XXX | +| `gateway` | str | 否 | 后端网关服务地址,默认从环境变量中获取: `os.getenv("GATEWAY_URL", "")` | https://appbuilder.baidu.com | +| `lazy_certification` | bool | 否 | 延迟认证,为True时在第一次运行时认证。默认为False。 | False | + +### 调用参数 + +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +| ------- | ------- | -------- | -------- | -------- | +| `message` | obj | 是 | 输入消息,用于模型的主要输入内容。 | Message(content=input_data) | +| `poll_request_times` | int | 否 | 轮询请求结果次数。默认为60。 | 60 | +| `poll_request_interval` | int | 否 | 轮询请求的间隔时间(秒)。默认为5。 | 5 | + +其中message包含的input_data包括以下参数: + +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +| ------- | ------- | -------- | -------- | -------- | +| `file_key` | str | 是 | 论文链接。 | http://image.yoojober.com/users/chatppt/temp/2024-06/6672aa839a9da.docx | +| `style` | str | 否 | PPT风格,可选:科技、商务、小清新、可爱卡通、中国风、极简、党政。 | 科技 | +| `color` | str | 否 | PPT主色调。可选:紫色、红色、橙色、黄色、绿色、青色、蓝色、粉色。 | 紫色 | +| `title` | str | 否 | 自定义标题。优先使用自定义标题,如果为空则使用解析结果中的标题。 | 论文分享 | +| `pleader` | str | 否 | 汇报人。 | 百度千帆AppBuilder | +| `advisor` | str | 否 | 指导教师。 | 百度千帆AppBuilder | +| `school` | str | 否 | 学校名称。 | 北京大学 | +| `school_logo` | str | 否 | 学校logo链接。 | | +| `school_picture` | str | 否 | 学校图片链接。 | | + + +### 响应参数 +| 参数名称 | 参数类型 | 描述 | 示例值 | +| ------- | ------- | -------- | -------- | +| `result` | obj | 模型运行后的输出结果,包含PPT下载链接。 | Message(content='...') | + +### 响应示例 +``` +https://download.yoojober.com/chatppt_business/2024-07/6f472b65ee324d2da7849b6003a896e3.pptx?e=1721964794&token=8_2qFlGEVQZPpFvHdGR6gg2t9A9QZfWT9wwTl92s:nG-hbPN51uPP8FOeTY2jdQcT51w= +``` + +## 高级用法 + +## 更新记录和贡献 +### 2024.8.1 +#### [Added] +- 增加论文生成PPT组件。 +- 增加论文生成PPT组件单元测试。 \ No newline at end of file diff --git a/docs/BasisModule/Components/qrcode_ocr/README.md b/docs/BasisModule/Components/qrcode_ocr/README.md new file mode 100644 index 000000000..217917bdf --- /dev/null +++ b/docs/BasisModule/Components/qrcode_ocr/README.md @@ -0,0 +1,111 @@ +# 二维码识别 (QRcodeOCR) + +## 简介 +二维码识别 (QRcodeOCR) 可对图片中的二维码、条形码进行检测和识别,返回存储的文字信息及其位置信息。 + + +### 功能介绍 +* 二维码识别 + + 检测识别图片中的二维码(包括QR_CODE、DATA_MATRIX、AZTEC、PDF_417 4类),自动返回存储的内容。 +* 条形码识别 + + 检测识别图片中的条形码(包括CODE_128、UPC_A、EAN_13、ITF、CODABAR 等9类),自动返回存储的内容。 +### 特色优势 +* 支持对图片中的二维码、条形码进行检测和识别,自动返回存储的内容。 + +### 应用场景 +* 物品信息管理 + + 解析识别各类物品的二维码或条形码信息,应用于商品、药品出入库管理及货物运输管理等场景,轻松一扫即可快速完成对物品信息的读取、登记和存储,简化物品管理流程 +## 基本用法 + +下面是二维码识别的代码示例: + +示例图片为: +![示例图片](https://bj.bcebos.com/v1/appbuilder/qrcode_ocr_test.png?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-24T12%3A45%3A13Z%2F-1%2Fhost%2Ffc43d07b41903aeeb5a023131ba6e74ab057ce26d50e966dc31ff083e6a9c41b) + +```python +import os +import appbuilder +import requests + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = '...' + +# 从BOS读取样例图片 +image_url = "https://bj.bcebos.com/v1/appbuilder/qrcode_ocr_test.png?" \ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-" \ + "01-24T12%3A45%3A13Z%2F-1%2Fhost%2Ffc43d07b41903aeeb5a023131ba6" \ + "e74ab057ce26d50e966dc31ff083e6a9c41b" +raw_image = requests.get(image_url).content +# 创建二维码识别组件实例 +qrcode_ocr = appbuilder.QRcodeOCR() +# 执行识别操作并获取结果 +out = qrcode_ocr.run(appbuilder.Message(content={"raw_image": raw_image}), location="true") +print(out.content) +# {'codes_result': [{'type': 'QR_CODE', 'text': ['ocr文字识别'], 'location': {'top': 506, 'left': 1302, 'width': 1972, 'height': 1961}}]} +``` + + +## 参数说明 + +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 初始化参数 +无 + +### 调用参数 (以表格形式展示) +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +|----------|---------|------|-------------------------------------------------------------------------|------------------------------------------------| +| message | String | 是 | 输入的消息,用于模型的主要输入内容。这是一个必需的参数 | Message(content={"raw_image": b"待识别的图片字节流数据"}) | +| location | String | 否 | 是否输出二维码/条形码位置信息,false:不返回位置信息,true:默认值,返回图中二维码/条形码的位置信息,包括上边距、左边距、宽度、高度 | "false" | +|timeout| Float | 否 | HTTP超时时间,单位:秒 |1|| +| retry | Integer | 否 | HTTP重试次数 | 3 | + +### 响应参数 +| 参数名称 | 参数类型 | 描述 | 示例值 | +|--------------|----------|-------------|-------------------------------------------------------------------------------------------------------------------| +| codes_result | Array[] | 返回结果 | [{'type': 'QR_CODE', 'text': ['ocr文字识别'], 'location': {'top': 506, 'left': 1302, 'width': 1972, 'height': 1961}}] | +| +type | String | 识别码类型条码类型 | 'QR_CODE' | +| +text | Array[] | 条形码/二维码识别内容 | ['ocr文字识别'] | +| +location | Object{} | 条形码/二维码位置信息 | {'top': 506, 'left': 1302, 'width': 1972, 'height': 1961} | +| ++top | Integer | 条形码/二维码的上边距 | 506 | +| ++left | Integer | 条形码/二维码的左边距 | 1302 | +| ++width | Integer | 条形码/二维码的宽度 | 1972 | +| ++height | Integer | 条形码/二维码的高度 | 1961 | + + +### 响应示例 +```json +{ + "codes_result": [ + { + "type": "QR_CODE", + "text": ["ocr文字识别"], + "location": { + "top": 506, + "left": 1302, + "width": 1972, + "height": 1961 + } + } + ] +} +``` +### 错误码 +| 错误码 | 描述 | +|-----|----| + +## 高级用法 + +目前该模块仅提供基础的二维码识别功能。 + + +## 更新记录和贡献 +* 二维码识别能力 (2024-01) \ No newline at end of file diff --git a/docs/BasisModule/Components/rag_with_baidu_search/README.md b/docs/BasisModule/Components/rag_with_baidu_search/README.md new file mode 100644 index 000000000..8196d8886 --- /dev/null +++ b/docs/BasisModule/Components/rag_with_baidu_search/README.md @@ -0,0 +1,158 @@ +# 百度搜索RAG(deprecate) + +## 简介 +百度搜索(BaiduSearch),通过百度搜索引擎搜索相关内容。 +现推荐使用RagWithBaiduSearchPro +| | 旧组件(百度搜索RAG)| 新组件(百度搜索RAG_PRO) +|--------------|------------------ |------------------ +| 指令 | ☑️ | ☑️ +| 拒绝回答开关 | ☑️ | ❌ +| 高亮开关 | ☑️ | ❌ +| 友好度开关 | ☑️ | ❌ +| 澄清开关 | ☑️ | ❌ +| 溯源开关 | ☑️ | ☑️ +| 流式请求 | ☑️ | ☑️ +| temperature | ☑️ | ☑️ +| top_p | ☑️ | ☑️ +| 检索个数 | ❌ | ☑️ +| 检索类型(网页、视频等) | ❌ | ☑️ + +### 功能介绍 +百度搜索是最大的中文搜索引擎,帮助用户在海量信息中找到最需要的内容。 + +### 特色优势 +百度搜索凭借先进的中文搜索技术、个性化推荐、全面的信息覆盖、即时的搜索结果和强大的安全防护,为用户提供快速、准确、安全的搜索体验,满足多样化的信息需求。 + +### 应用场景 +通用搜索领域 + +## 基本用法 +以下是一个简单的例子来演示如何开始使用百度搜索组件: + +```python +import appbuilder +import os + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +# 设置环境变量 +os.environ["APPBUILDER_TOKEN"] = '...' + +# 创建rag_with_baidusearch对象 +rag_with_baidusearch_component = appbuilder.RAGWithBaiduSearch(model="Qianfan-Agent-Speed-8k") + +# 运行rag_with_baidusearch基本组件 +msg = appbuilder.Message("残疾人怎么办相关证件") +result = rag_with_baidusearch_component.run(msg) + +# 获取reference +references = result.extra + +# 输出运行结果 +print(result) +``` + +## 参数说明 +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +import os + +os.environ["APPBUILDER_TOKEN"] = "..." +``` + +### 初始化参数 +- `model`: 模型名称,用于指定要使用的千帆模型。 +- `instruction (obj:Message, 可选)`: 可设定人设,如:你是问答助手,在回答问题前需要加上“很高兴为您解答:” +- `reject (bool, 可选)`: 拒绝开关,如果为 True,则启用该能力。默认为 False。当输入的问题在搜索结果中没有找到答案时,开关开启时,模型会用特定话术("当前文档库找不到对应的答案,我可以尝试用我的常识来回答你。")做回复的开头,并后接自有知识做回复内容。 +- `clarify (bool, 可选)`: 澄清开关,如果为 True,则启用该能力。默认为 False。 当输入的问题比较模糊、或者主体指代不清晰,且context_list中包含有可以回答该模糊问题的多种潜在备选答案时,开启该开关,大模型会以特定的话术做澄清反问,引导用户继续补充问题发问。举例子,query:发电机的续航时间? Answer: 根据搜索结果得到了xx和xx两种型号的发电机,您的问题具体涉及到哪一个?请补充关键信息,作为完整的问题重新发问。 +- `highlight (bool, 可选)`: 重点强调开关,如果为 True,则启用该能力。默认为 False。开启该功能时,回复结果中会高亮显示关键部分的内容。 +- `friendly (bool, 可选)`: 友好性提升开关,如果为 True,则启用该能力。默认为 False。开关开启时,部分回复的开头会加礼貌用语。且如果回答涉及到大段的信息,会倾向于以<总-分>或者<总-分-总>的形式做分点论述,使得答案的格式更规整,可读性更强。 +- `cite (bool, 可选)`: 溯源开关,如果为 True,则启用该能力。默认为 False。开关开启时,回复内容后会使用引用标记来标注回答内容参考的搜索结果序号,如^[1]^ (引用单个搜索结果),^[1][2]^(引用多个搜索结果)。例如:按照当地公安机关出入境管理部门规定的其他材料办理^[2]^。 + + +### 调用参数 +调用参数中的 instruction, reject, clarify, highlight, friendly 和 cite 会覆盖初始化时的参数。 + +- `msg (obj:Message)`: 输入消息,包含用户提出的问题。这是一个必需的参数。 +- `instruction (obj:Message, 可选)`: 可设定人设,如:你是问答助手,在回答问题前需要加上“很高兴为您解答:” +- `reject (bool, 可选)`: 拒绝开关,如果为 True,则启用该能力。默认为 False。当输入的问题在搜索结果中没有找到答案时,开关开启时,模型会用特定话术("当前文档库找不到对应的答案,我可以尝试用我的常识来回答你。")做回复的开头,并后接自有知识做回复内容。 +- `clarify (bool, 可选)`: 澄清开关,如果为 True,则启用该能力。默认为 False。 当输入的问题比较模糊、或者主体指代不清晰,且context_list中包含有可以回答该模糊问题的多种潜在备选答案时,开启该开关,大模型会以特定的话术做澄清反问,引导用户继续补充问题发问。举例子,query:发电机的续航时间? Answer: 根据搜索结果得到了xx和xx两种型号的发电机,您的问题具体涉及到哪一个?请补充关键信息,作为完整的问题重新发问。 +- `highlight (bool, 可选)`: 重点强调开关,如果为 True,则启用该能力。默认为 False。开启该功能时,回复结果中会高亮显示关键部分的内容。 +- `friendly (bool, 可选)`: 友好性提升开关,如果为 True,则启用该能力。默认为 False。开关开启时,部分回复的开头会加礼貌用语。且如果回答涉及到大段的信息,会倾向于以<总-分>或者<总-分-总>的形式做分点论述,使得答案的格式更规整,可读性更强。 +- `cite (bool, 可选)`: 溯源开关,如果为 True,则启用该能力。默认为 False。开关开启时,回复内容后会使用引用标记来标注回答内容参考的搜索结果序号,如^[1]^ (引用单个搜索结果),^[1][2]^(引用多个搜索结果)。例如:按照当地公安机关出入境管理部门规定的其他材料办理^[2]^。 +- `stream (bool, 可选)`: 指定是否以流式形式返回响应。默认为 False。 +- `temperature (float, 可选)`: 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 +- `top_p (float, 可选)`: 模型配置的top_p参数,top_p值越高输出文本越多样,top_p值越低输出文本越稳定。取值范围为 0.0 到 1.0,默认值为 1e-10。 + + +### 返回值 +- 返回一个 `Message` 对象,包含模型运行后的输出消息。 + + +## 高级用法 +该组件的高级用法包括定制化的输入处理、输出处理,以及更复杂的调用场景。用户可以根据具体需求扩展组件功能,实现个性化的问答系统。 +包括如下功能: +1、人设 +2、拒答 +3、澄清反问 +4、重点强调 +5、友好度提升 +6、溯源 + + +### 代码样例 + +```python +import appbuilder +import os + +# 设置环境变量 +os.environ["APPBUILDER_TOKEN"] = '...' + +# 创建rag_with_baidusearch对象, 并初始化人设指令 +rag_with_baidusearch_component = appbuilder.RAGWithBaiduSearch( + model="Qianfan-Agent-Speed-8k", + instruction=appbuilder.Message("你是问答助手,在回答问题前需要加上: 很高兴为您解答")) + + +# 运行rag_with_baidusearch组件,开启拒答、澄清追问、重点强调、友好性提升、溯源能力功能 +msg = appbuilder.Message("残疾人怎么办相关证件") +result = rag_with_baidusearch_component.run( + msg, reject=True, clarify=True, highlight=True, + friendly=True, cite=True, temperature=0.5, stream=False) + +# 输出运行结果 +print(result) +``` + +### 返回参数说明 + +返回的message中具体字段说明如下: + +| 字段 | 字段说明 | +|---------------|--------| +| name | 名称 | +| mtype | 类型 | +| content | 内容 | +| extra | 引用 | +| +search_baidu | 百度搜索结果 | +| ++content | 网页内容摘要 | +| ++url | 网页链接 | +| ++ref_id | 序号 | +| ++title | 标题 | +| ++icon | 网站图标 | +| ++site_name | 网站名 | + + +### 典型返回样例 +``` +Message(name=msg, content=您好,请问您是想询问关于残疾人办理什么证件的问题吗?如果是,我可以为您提供一些信息。 + +首先,如果您是首次申请办理残疾人证,需要携带身份证、户口簿和三张两寸近期免冠白底彩色照片到县残联办证窗口提出申请。如果您因身体原因无法亲自前往,可以联系村(社区)工作人员代办申请。 + +其次,如果您是指残疾类型等级证明,您需要携带相关材料到指定医院或医生进行评级,并由医生签名盖章。 + +最后,如果您是指残疾人享受低保或残疾人贫困证的一级肢体、视力、智力、精神、多重及60周岁以上的一级听力、语言的重度残疾人可以享受重度残疾人生活补助,那么您需要携带身份证、户口本和残疾证申请表到县、市、区级残联进行办理。 + +希望这些信息对您有所帮助。如果您还有其他问题,欢迎随时提问。^[2]^, mtype=dict, extra={'search_baidu': [{'content': '(一)3张两寸近期免冠白底彩色照片。 (二)身份证、户口簿原件及复印件。 (三)申请智力、精神类残疾人证和未成年人申请残疾人证需同时提供法定监护人的身份证、户口本原件及复印件和监护人的证明材料。监护人证明材料为以下三项中任意一项: (1)能体现双方直系血缘亲属关系的户口簿。 (2)申请人所在村(社区)出具的说明双方关系的证明材料。 (3)其他能够证明其双方关系的合法证件。(法院判决书、结婚证、出生证明等) (四)经常居住地的有效居住证(户籍地不在本市申请人需提供此证件,本市户籍申请人无需提供此证件)。 (五)经常居住地残联要求的其他材料。 残疾证办理事项及流程', 'icon': 'https://ss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=1505232404,3530227258&fm=195&app=88&f=JPEG?w=200&h=200', 'url': 'https://www.jingzhou.gov.cn/ztfwnew/shjz/cjrbl/index.shtml', 'ref_id': '1', 'site_name': '荆州市人民政府', 'title': '残疾人证办理服务'}, {'content': '{#}申请{@}. 首次申请办理残疾人证人员,需持申请人居民身份证,户口簿和3张两寸近期免冠白底彩照,到县残联办证窗口(县政务服务中心一楼1号窗口)提出办证申请,填写《中华人民共和国残疾人证申请表》.如因身体原因个人无法出行办证,可联系村(社区)工作人员代办申请.', 'icon': None, 'url': 'https://mp.weixin.qq.com/s?__biz=MzIxMzM5ODY5OQ==&mid=2247485042&idx=1&sn=26a4cad0122d24971d3f5ce598af3564&chksm=97b623b6a0c1aaa02f776c19f567e0b3fabdef3d9f5c957e1f260f286fe5356101fd1ac4e675&scene=27', 'ref_id': '2', 'site_name': '微信公众平台', 'title': '残疾人证如何办理?到哪里评定?你想知道的都在这里'}, {'content': '一、残疾人如何办残疾证 1、户口所在地的县、市、区级残联领取《残疾人证申请表》和《残疾评定表》; 2、身份证或户口本复印件一张; 3、两寸彩色相片2-6张(多带不碍事,各地标准不一); 4、残疾类型等级证明。残疾很明显的可以直接到残联进行评级(像肢体类)审核办理,不明显的必须到指定医院、指定医生进行评级签名并盖章。 一切手续完备,就到县、市、区级残联进行办理,快的话立等可取,慢的话7-15天也差不多了。 二、残疾证有什么用? 1、持有残疾证的残疾人可享受低保或持残疾人贫困证的一级肢体、视力、智力、精神、多重及60周岁以上的一级听力、语言的重度残疾人,可享受重度残疾人生活补助。 2、残疾人托(安)养方面,一级重度残疾人(不含听力、语言、视力残疾)或18至60周岁二级重度残疾人(不含听力、语言、视力残疾),集中托养:低保户、贫困户的对象补助每年补助现金按各地政策规定金额发放。', 'icon': 'https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=215799447,688541359&fm=195&app=88&f=JPEG?w=200&h=200', 'url': 'https://mip.66law.cn/laws/1060751.aspx', 'ref_id': '3', 'site_name': '华律网', 'title': '残疾人如何办残疾证-证件办理|华律办事直通车'}]}) +``` diff --git a/docs/BasisModule/Components/rag_with_baidu_search_pro/README.md b/docs/BasisModule/Components/rag_with_baidu_search_pro/README.md new file mode 100644 index 000000000..816a26c8b --- /dev/null +++ b/docs/BasisModule/Components/rag_with_baidu_search_pro/README.md @@ -0,0 +1,134 @@ +# 百度搜索RAG_PRO + +## 简介 +百度搜索RAG_PRO组件旨在解决传统生成模型在生成长文本时可能会受到信息获取不足的问题,核心思想是将百度搜索与LLM相结合,使得生成的文本可以借助检索到的信息进行增强,从而提高生成文本的质量和相关性。 + +### 功能介绍 +基于百度搜索结果进行RAG检索增强问答。 +百度搜索RAG_PRO组件支持配置用户指令,temperature,top_p,以及溯源开关等,为用户提供了更灵活的控制选项。 +对比旧版百度搜索RAG,新版百度搜索RAG_PRO在支持配置检索个数和检索类型(网页、视频等)方面进行了升级。 + +### 特色优势 +百度搜索RAG_PRO组件的综合优势在于通过结合百度搜索的搜索引擎技术和ERNIE模型的语义理解能力,可以更准确地理解用户的搜索意图,并提供与搜索查询相关性更高的搜索结果。 + +### 应用场景 +通用搜索领域 + +## 基本用法 +以下是一个简单的例子来演示如何开始使用百度搜索RAG_PRO组件: + +```python +import appbuilder +import os + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +# 设置环境变量 +os.environ["APPBUILDER_TOKEN"] = '...' + +# 创建rag_with_baidusearch_pro对象 +rag_with_baidu_search_pro = appbuilder.RagWithBaiduSearchPro(model="Qianfan-Agent-Speed-8k") + +# 运行rag_with_baidusearch基本组件 +msg = appbuilder.Message("残疾人怎么办相关证件") +result = rag_with_baidu_search_pro.run(msg) + +# 获取reference +references = result.extra + +# 输出运行结果 +print(result) +``` + +## 参数说明 +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +import os + +os.environ["APPBUILDER_TOKEN"] = "..." +``` + +### 初始化参数 +- `model`: 模型名称,用于指定要使用的千帆模型。 +- `instruction (obj:Message, 可选)`: 可设定人设,如:你是问答助手,在回答问题前需要加上“很高兴为您解答:” + + +### 调用参数 +调用参数中的 instruction 会覆盖初始化时的参数。 + +- `msg (obj:Message)`: 输入消息,包含用户提出的问题。这是一个必需的参数。 +- `instruction (obj:Message, 可选)`: 可设定人设,如:你是问答助手,在回答问题前需要加上“很高兴为您解答:” +- `stream (bool, 可选)`: 指定是否以流式形式返回响应。默认为 False。 +- `temperature (float, 可选)`: 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 +- `top_p (float, 可选)`: 模型配置的top_p参数,top_p值越高输出文本越多样,top_p值越低输出文本越稳定。取值范围为 0.0 到 1.0,默认值为 1e-10。 +- `search_top_k (int, 可选)`: 指定百度搜索返回的检索个数,最多10,默认为4。 +- `hide_corner_markers (bool, 可选)`: 溯源开关,True隐藏来源,False显示来源,默认为True,不显示结果来源。 + + +### 返回值 +- 返回一个 `Message` 对象,包含模型运行后的输出消息。 + + +## 高级用法 +该组件的高级用法包括定制化的输入处理、输出处理,以及更复杂的调用场景。用户可以根据具体需求扩展组件功能,实现个性化的问答系统。 +包括如下功能: +1、人设 +2、溯源 +3、百度搜索检索个数 + + +### 代码样例 + +```python +import appbuilder +import os + +# 设置环境变量 +os.environ["APPBUILDER_TOKEN"] = '...' + +# 创建rag_with_baidusearch对象, 并初始化人设指令 +rag_with_baidusearch_pro = appbuilder.RagWithBaiduSearchPro( + model="Qianfan-Agent-Speed-8k", + instruction=appbuilder.Message("你是问答助手,在回答问题前需要加上: 很高兴为您解答")) + + +# 运行rag_with_baidusearch组件,开启拒答、澄清追问、重点强调、友好性提升、溯源能力功能 +msg = appbuilder.Message("残疾人怎么办相关证件") +result = rag_with_baidusearch_pro.run( + msg, temperature=0.5, stream=False) + +# 输出运行结果 +print(result) +``` + +### 返回参数说明 + +返回的message中具体字段说明如下: + +| 字段 | 字段说明 | +|---------------|--------| +| name | 名称 | +| mtype | 类型 | +| content | 内容 | +| extra | 引用 | +| +search_baidu | 百度搜索结果 | +| ++content | 网页内容摘要 | +| ++url | 网页链接 | +| ++ref_id | 序号 | +| ++title | 标题 | +| ++icon | 网站图标 | +| ++site_name | 网站名 | + + +### 典型返回样例 +``` +Message(name=msg, content=您好,请问您是想询问关于残疾人办理什么证件的问题吗?如果是,我可以为您提供一些信息。 + +首先,如果您是首次申请办理残疾人证,需要携带身份证、户口簿和三张两寸近期免冠白底彩色照片到县残联办证窗口提出申请。如果您因身体原因无法亲自前往,可以联系村(社区)工作人员代办申请。 + +其次,如果您是指残疾类型等级证明,您需要携带相关材料到指定医院或医生进行评级,并由医生签名盖章。 + +最后,如果您是指残疾人享受低保或残疾人贫困证的一级肢体、视力、智力、精神、多重及60周岁以上的一级听力、语言的重度残疾人可以享受重度残疾人生活补助,那么您需要携带身份证、户口本和残疾证申请表到县、市、区级残联进行办理。 + +希望这些信息对您有所帮助。如果您还有其他问题,欢迎随时提问。^[2]^, mtype=dict, extra={'search_baidu': [{'content': '(一)3张两寸近期免冠白底彩色照片。 (二)身份证、户口簿原件及复印件。 (三)申请智力、精神类残疾人证和未成年人申请残疾人证需同时提供法定监护人的身份证、户口本原件及复印件和监护人的证明材料。监护人证明材料为以下三项中任意一项: (1)能体现双方直系血缘亲属关系的户口簿。 (2)申请人所在村(社区)出具的说明双方关系的证明材料。 (3)其他能够证明其双方关系的合法证件。(法院判决书、结婚证、出生证明等) (四)经常居住地的有效居住证(户籍地不在本市申请人需提供此证件,本市户籍申请人无需提供此证件)。 (五)经常居住地残联要求的其他材料。 残疾证办理事项及流程', 'icon': 'https://ss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=1505232404,3530227258&fm=195&app=88&f=JPEG?w=200&h=200', 'url': 'https://www.jingzhou.gov.cn/ztfwnew/shjz/cjrbl/index.shtml', 'ref_id': '1', 'site_name': '荆州市人民政府', 'title': '残疾人证办理服务'}, {'content': '{#}申请{@}. 首次申请办理残疾人证人员,需持申请人居民身份证,户口簿和3张两寸近期免冠白底彩照,到县残联办证窗口(县政务服务中心一楼1号窗口)提出办证申请,填写《中华人民共和国残疾人证申请表》.如因身体原因个人无法出行办证,可联系村(社区)工作人员代办申请.', 'icon': None, 'url': 'https://mp.weixin.qq.com/s?__biz=MzIxMzM5ODY5OQ==&mid=2247485042&idx=1&sn=26a4cad0122d24971d3f5ce598af3564&chksm=97b623b6a0c1aaa02f776c19f567e0b3fabdef3d9f5c957e1f260f286fe5356101fd1ac4e675&scene=27', 'ref_id': '2', 'site_name': '微信公众平台', 'title': '残疾人证如何办理?到哪里评定?你想知道的都在这里'}, {'content': '一、残疾人如何办残疾证 1、户口所在地的县、市、区级残联领取《残疾人证申请表》和《残疾评定表》; 2、身份证或户口本复印件一张; 3、两寸彩色相片2-6张(多带不碍事,各地标准不一); 4、残疾类型等级证明。残疾很明显的可以直接到残联进行评级(像肢体类)审核办理,不明显的必须到指定医院、指定医生进行评级签名并盖章。 一切手续完备,就到县、市、区级残联进行办理,快的话立等可取,慢的话7-15天也差不多了。 二、残疾证有什么用? 1、持有残疾证的残疾人可享受低保或持残疾人贫困证的一级肢体、视力、智力、精神、多重及60周岁以上的一级听力、语言的重度残疾人,可享受重度残疾人生活补助。 2、残疾人托(安)养方面,一级重度残疾人(不含听力、语言、视力残疾)或18至60周岁二级重度残疾人(不含听力、语言、视力残疾),集中托养:低保户、贫困户的对象补助每年补助现金按各地政策规定金额发放。', 'icon': 'https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=215799447,688541359&fm=195&app=88&f=JPEG?w=200&h=200', 'url': 'https://mip.66law.cn/laws/1060751.aspx', 'ref_id': '3', 'site_name': '华律网', 'title': '残疾人如何办残疾证-证件办理|华律办事直通车'}]}) +``` diff --git a/docs/BasisModule/Components/retriever/README.md b/docs/BasisModule/Components/retriever/README.md new file mode 100644 index 000000000..dddf9d4ef --- /dev/null +++ b/docs/BasisModule/Components/retriever/README.md @@ -0,0 +1,10 @@ +# 向量检索 + +## 简介 +Appbuilder提供多种向量数据库作为向量检索的底座,当前主要支持百度向量数据库、百度 ElasticSearch。 + +### 功能介绍 +`向量检索-VDB`组件(Baidu VDB Retriever)以百度向量数据库作为向量存储和检索的底座。百度向量数据库是一个专注于多维向量数据的存储、检索和分析的企业级分布式数据库服务。基于百度自主研发的向量数据库内核,VectorDB在保证高性能和高可用性的同时,也特别注重易用性和可扩展性。它支持多种索引类型和相似度计算方法,能够满足各类复杂和多样化的数据应用需求。特别值得一提的是,VectorDB能够管理高达数十亿的向量规模,同时保持毫秒级的查询响应时间,非常适合进行大规模的向量检索和分析任务。 + +`向量检索-BES`组件(Baidu ElasticSearch Retriever)以百度 ElasticSearch作为向量存储和检索的底座。百度 ElasticSearch是一款专为企业级需求设计的分布式搜索和分析服务,它在全面兼容开源ElasticSearch的基础上,提供了更多增强功能。这款服务的核心优势在于其高性能和高可靠性,它为处理结构化和非结构化数据提供了一个低成本且高效的平台。对于关注数据安全的客户来说,百度ElasticSearch提供了先进的权限管理机制,使得您可以根据业务需求自由地配置集群权限。 + diff --git a/docs/BasisModule/Components/retriever/baidu_vdb/README.md b/docs/BasisModule/Components/retriever/baidu_vdb/README.md new file mode 100644 index 000000000..b97d49086 --- /dev/null +++ b/docs/BasisModule/Components/retriever/baidu_vdb/README.md @@ -0,0 +1,108 @@ +# 向量检索-VectorDB(BaiduVectorDBRetriever) + +## 简介 +向量检索-VectorDB(BaiduVectorDBRetriever)基于一款百度向量数据库的内容检索组件,支持根据文本的向量的相似度进行内容检索。 + +### 功能介绍 +向量检索-VectorDB(BaiduVectorDBRetriever)用于在将文本内容输入到百度向量数据库,根据文本的向量相似度进行高效的内容检索。 + +### 特色优势 +高效准确:基于百度向量数据库的强大能力,提供高效且准确的内容检索功能。 + +### 应用场景 +各种内容检索场景 + +## 准备工作 +在使用向量检索-VectorDB(BaiduVectorDBRetriever)进行内容检索之前,需要到百度向量数据库官网创建相应的实例,[教程](https://cloud.baidu.com/doc/VDB/s/hlrsoazuf)。 + +## 基本用法 + +以下是有关如何开始使用向量检索-VectorDB(BaiduVectorDBRetriever)的代码示例: + +补充说明: +- `you_vdb_instance_id` 为VectorDB 实例ID,请替换为您的实例ID,在VectorDB控制台界面上可以查看 +- `your_api_key` 为您在VectorDB上申请的账户密钥,请替换为您自己的root账户密钥,在VectorDB控制台界面上可以查看 + +```python +import os +import appbuilder + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = '...' + +segments = appbuilder.Message(["文心一言大模型", "百度在线科技有限公司"]) +# 初始化构建索引 +vector_index = appbuilder.BaiduVDBVectorStoreIndex.from_params( + instance_id=your_instance_id, + api_key=your_api_key, + drop_exists=True, +) +vector_index.add_segments(segments) + +query = appbuilder.Message("文心一言") +retriever = vector_index.as_retriever() +res = retriever(query) +print(res) +``` + +## 参数说明 + +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 初始化参数说明: +`BaiduVDBVectorStoreIndex()` 实例化参数说明: +- instance_id(str,必填):百度向量数据库的实例id,创建实例时获取 +- api_key (str,必填):连接向量数据库所需的密码,创建实例时获取 +- account (str,非必填):连接向量数据库所需的用户名,默认root +- database_name (str,非必填) :向量数据库的名称,默认为AppBuilderDatabase +- table_params (TableParams,非必填) :VectorDB table参数,参考链接[VectorDB table params](https://cloud.baidu.com/doc/VDB/s/mlrsob0p6) +- embedding (Embedding,非必填) :appbuilder.Embedding类型,若有构造好的Embedding,可以增量插入,否则默认新建embedding + +------- + +`BaiduVDBVectorStoreIndex().from_params()` 构造函数参数说明: +- instance_id(str,必填):百度向量数据库的实例id,创建实例时获取 +- api_key (str,必填):连接向量数据库所需的密码,创建实例时获取 +- account (str,非必填):连接向量数据库所需的用户名,默认root +- database_name (str,非必填) :向量数据库的名称,默认为AppBuilderDatabase +- table_name (str,非必填) :向量数据库的表名,默认为AppBuilderTable +- drop_exists (bool, 非必填) :是否清空数据库历史记录,默认为False + +------- + + +### 调用参数: + +`BaiduVDBRetriever().run()` 函数参数说明: + +| 参数名称 | 参数类型 |是否必须 | 描述 | 示例值 | +|---------|--------|--------|------------------|---------------| +| message | String |是 | 需要检索的内容, 类型为Message,content类型为str, 长度要求(0,512) | "中国2023人均GDP" | +| top_k | int |否 | 返回相似度最高的top_k个内容,top_k的数值范围(1,embedding索引数量] | 1 | + + +### 响应参数 + +`BaiduVDBRetriever().run()` 函数返回值说明: + +| 参数名称 | 参数类型 | 描述 | 示例值 | +|------|--------|-----|--------------------| +| text | string | 检索结果 | "中国2023年人均GDP8.94万元" | +| score | float | 相似度 | 0.95 | +| meta | dict | 元信息 | "" | +### 响应示例 +```json +{"text": "中国2023年人均GDP8.94万元", "score": 0.95, "meta": ""} +``` + +## 高级用法: + +本组件根据向量的相似度进行检索,支持使用不同的embedding方法和索引方式来优化检索的效果。 + +## 更新记录和贡献 +* 向量检索能力 (2024-03) diff --git a/docs/BasisModule/Components/retriever/bes/README.md b/docs/BasisModule/Components/retriever/bes/README.md new file mode 100644 index 000000000..df7911d7c --- /dev/null +++ b/docs/BasisModule/Components/retriever/bes/README.md @@ -0,0 +1,88 @@ +# 向量检索-BES(BaiduElasticSearchRetriever) + +## 简介 +向量检索-BES组件(BaiduElasticSearchRetriever)基于一款Baidu ElasticSearch的内容检索组件,支持根据文本的向量的相似度进行内容检索。 + +### 功能介绍 +向量检索-BES组件(BaiduElasticSearchRetriever)用于在将文本内容输入到Baidu ElasticSearch,根据文本的向量相似度进行高效的内容检索。 + +### 特色优势 +- 高效准确:基于Baidu ElasticSearch的强大能力,提供高效且准确的内容检索功能。 + +### 应用场景 +各种内容检索场景 + +## 准备工作 +在使用BaiduElasticSearchRetriever进行内容检索之前,需要到Baidu ElasticSearch官网创建相应的集群,详情见[教程](https://cloud.baidu.com/doc/BES/s/gke3ocf89)。 + +注:创建集群时请选择7.10.2版本的ES,否则可能无法使用本组件。 + +## 基本用法 + +以下是有关如何开始使用BESRetriever的代码示例: + +```python +import os +import appbuilder + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = '...' + +embedding = appbuilder.Embedding() +segments = appbuilder.Message(["文心一言大模型", "百度在线科技有限公司"]) +# 初始化构建索引 +vector_index = appbuilder.BESVectorStoreIndex.from_segments(segments=segments, cluster_id=es_cluster_id, user_name=es_username, + password=es_password, embedding=embedding) +# 获取当前索引中的全部内容 +all_content = vector_index.get_all_segments() +print(all_content) +# 转化为retriever +retriever = vector_index.as_retriever() +# 按照query进行检索 +query = appbuilder.Message("文心一言") +res = retriever(query=query, top_k=1) +print(res) +# 删除当前索引中的全部内容 +vector_index.delete_all_segments() +``` + +## 参数说明 + +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 初始化参数说明: + +- segments (Message[List[str]],必填):需要入库的文本段落 +- cluster_id (str,必填):ElacticSearch集群的id,创建集群时获取 +- user_name (str,必填):连接ES集群所需的用户名,创建集群时获取 +- password (str,必填):连接ES集群所需的密码,创建集群时获取 +- embedding (obj,非必填):用于将文本转为向量的模型,默认为Embedding + +### 调用参数: +| 参数名称 | 参数类型 |是否必须 | 描述 | 示例值 | +|---------|--------|--------|------------------|---------------| +| message | String |是 | 需要检索的内容 | "中国2023人均GDP" | +| top_k | int |否 | 返回相似度最高的top_k个内容 | 1 | + +### 响应参数 +| 参数名称 | 参数类型 | 描述 | 示例值 | +|------|--------|-----|--------------------| +| text | string | 检索结果 | "中国2023年人均GDP8.94万元" | +| score | float | 相似度 | 0.95 | +| meta | dict | 元信息 | "" | +### 响应示例 +```json +{"text": "中国2023年人均GDP8.94万元", "score": 0.95, "meta": ""} +``` + +## 高级用法: + +本组件根据向量的相似度进行检索,支持使用不同的embedding方法和索引方式来优化检索的效果。 + +## 更新记录和贡献 +* 向量检索能力 (2023-12) \ No newline at end of file diff --git a/docs/BasisModule/Components/retriever/reranker/README.md b/docs/BasisModule/Components/retriever/reranker/README.md new file mode 100644 index 000000000..fbe1d35da --- /dev/null +++ b/docs/BasisModule/Components/retriever/reranker/README.md @@ -0,0 +1,91 @@ +# 文本精排(Reranker) + +## 简介 +文本精排能力,将Query召回到的N个候选文本段落进行精排;保证与Query相关程度越高的文本段落排序越靠前,提升检索效果。 + +### 功能介绍 +文本精排(Reranker)用于检索排序,输入为Query和Top K个段落,输出为每个段落的排序得分;Query相关程度越高的文本段落排序越靠前,用于提升检索效果。 + +### 特色优势 +- 高效准确:基于开源模型[ +bce-reranker](https://huggingface.co/maidalun1020/bce-reranker-base_v1)的能力,提供高效且准确的内容检索功能。[百度云推理服务Api](https://cloud.baidu.com/doc/WENXINWORKSHOP/s/xlu216rqn) + +### 应用场景 +检索排序场景 + + +## 基本用法 + +以下是有关如何开始使用BESRetriever的代码示例: + +```python +import os +import appbuilder +from appbuilder import Message + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = '...' + +reranker = appbuilder.Reranker() +ranked_1 = reranker("你好", ["他也好", "hello?"]) +print(ranked_1) + +# 使用上游的Message作为输入的代码示例 +ranked_2 = reranker(appbuilder.Message("你好"), appbuilder.Message(["他也好", "hello?"])) +print(ranked_2) +``` + +## 参数说明 +### 初始化参数说明: + +| 参数名称 | 参数类型 |是否必须 | 描述 | 示例值 | +|---------|--------|--------|------------------|---------------| +| model | str |是 | 指定底座模型的类型。当前仅支持 bce-reranker-base 作为可选值。若不指定,默认值为 bce-reranker-base。 | bce-reranker-base | + + +### 调用参数: + +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +|---------|--------|--------|------------------|---------------| +| query | str |是 | 精排Query,长度小于1600。 | "你好" | +| texts | List[str] | 是 | 精排输入段落,会对列表里的所有内容排序,最大长度为50. | ["你好", "世界"] | + +### 响应参数: + +| 参数名称 | 参数类型 | 描述 | 示例值 | +|---------|--------|------------------|---------------| +| +document | str | 输入的段落 | "hello?" | +| +relevance_score | float | 精排的相关性分数 | 0.565118 | +| +index | int | 输入段落的原始index | -- | + + +### 响应示例 +#### 输入 +```python +query="你好", text=["他也好", "hello?"] +``` + +#### 响应 +```json +[ + { + "document": "hello?", + "relevance_score": 0.5651187300682068, + "index": 1 + }, + { + "document": "他也好", + "relevance_score": 0.47729530930519104, + "index": 0 + } +] +``` + + +### 错误码 + +无 + +## 更新记录和贡献 + +* reranker-base (2024-08) diff --git a/docs/BasisModule/Components/table_ocr/README.md b/docs/BasisModule/Components/table_ocr/README.md new file mode 100644 index 000000000..8134f60e1 --- /dev/null +++ b/docs/BasisModule/Components/table_ocr/README.md @@ -0,0 +1,161 @@ +# 表格文字识别 (TableOCR) + +## 简介 +表格文字识别 (TableOCR) 可支持识别图片中的表格内容,返回各表格的表头表尾内容、单元格文字内容及其行列位置信息,全面覆盖各类表格样式,包括常规有线表格、无线表格、含合并单元格表格。同时,支持多表格内容识别。 +### 功能介绍 +* 简单表格文字识别 + +支持识别具备完整框线的常规简单表格,结构化输出表头、表尾及每个单元格的文字内容。 +* 复杂表格文字识别 + +可识别无表格框线,但行、列位置明确的表格,支持含合并单元格的复杂表格文字识别。 +### 特色优势 +* 支持识别图片中的表格内容,返回各表格的表头表尾内容、单元格文字内容及其行列位置信息,全面覆盖各类表格样式,包括常规有线表格、无线表格、含合并单元格表格。同时,支持单图中多表格内容的识别。 + +### 应用场景 +* 信息登记表识别 + + 对个人、商品、公示内容等纸质信息登记表进行识别,用于登记信息的结构化整理和统计,大幅度降低人力录入成本,提升信息管理的便捷性 +* 财税报表识别 + + 提取识别银行对账单、资产负债表、损益表等财税场景常用表格内容,快速实现表格内容的电子化,用于财税信息统计、存档及核算,大幅度提升信息录入效率 +## 基本用法 + +下面是表格文字识别的代码示例: + +示例图片为 + +![示例图片](https://bj.bcebos.com/v1/appbuilder/table_ocr_test.png?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-24T12%3A37%3A09Z%2F-1%2Fhost%2Fab528a5a9120d328dc6d18c6064079145ff4698856f477b820147768fc2187d3) + +```python +import os +import appbuilder +import requests + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = '...' + +# 从BOS读取样例图片 +image_url = "https://bj.bcebos.com/v1/appbuilder/table_ocr_test.png?" \ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024" \ + "-01-24T12%3A37%3A09Z%2F-1%2Fhost%2Fab528a5a9120d328dc6d18c6" \ + "064079145ff4698856f477b820147768fc2187d3" +raw_image = requests.get(image_url).content +# 创建表格文字识别组件实例 +table_ocr = appbuilder.TableOCR() +# 执行识别操作并获取结果 +out = table_ocr.run(appbuilder.Message(content={"raw_image": raw_image})) +print(out.content) +``` + + +## 参数说明 + +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 初始化参数 +无 + +### 调用参数 (以表格形式展示) +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +|---------|---------|------|-----------------------------|------------------------------------------------| +| message | String | 是 | 输入的消息,用于模型的主要输入内容。这是一个必需的参数 | Message(content={"raw_image": b"待识别的图片字节流数据"}) | +|timeout| Float | 否 | HTTP超时时间,单位:秒 |1|| +| retry | Integer | 否 | HTTP重试次数 | 3 | + +### 响应参数 +| 参数名称 | 参数类型 | 描述 | 示例值 | +|------------------|---------|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| tables_result | array[] | 返回结果 | [{"table_location": [],"header": [],"body": [],"footer": []}] | +| +table_location | array[] | 单个表格的四角点x,y坐标 | [{ "x": 15, "y": 15 },{ "x": 371, "y": 15 },{ "x": 371, "y": 98 },{ "x": 15, "y": 98 }], | +| +header | array[] | 表头信息 | [{'location': [{'x': 325, 'y': 40}, {'x': 528, 'y': 40}, {'x': 528, 'y': 71}, {'x': 325, 'y': 71}], 'words': '财务状况变动表'}, {...}] | +| +body | array[] | 单元格信息 | [{"cell_location": [{ "x": 15, "y": 15 },{ "x": 120, "y": 15 },{ "x": 120, "y": 58 },{ "x": 15, "y": 58 }],"row_start": 0,"row_end": 1,"col_start": 0,"col_end": 1,"words": "参数"},{...}] | +| +footer | array[] | 表尾信息 | [{'location': [...], 'words': '...'}, {...}] | + +### 响应示例 +```json +{ + "tables_result": [ + { + "table_location": [ + { "x": 15, "y": 15 }, + { "x": 371, "y": 15 }, + { "x": 371, "y": 98 }, + { "x": 15, "y": 98 } + ], + "header": [], + "body": [ + { + "cell_location": [ + { "x": 15, "y": 15 }, + { "x": 120, "y": 15 }, + { "x": 120, "y": 58 }, + { "x": 15, "y": 58 } + ], + "row_start": 0, + "row_end": 1, + "col_start": 0, + "col_end": 1, + "words": "参数" + }, + { + "cell_location": [ + { "x": 120, "y": 15 }, + { "x": 371, "y": 15 }, + { "x": 371, "y": 58 }, + { "x": 120, "y": 58 } + ], + "row_start": 0, + "row_end": 1, + "col_start": 1, + "col_end": 2, + "words": "值" + }, + { + "cell_location": [ + { "x": 15, "y": 58 }, + { "x": 120, "y": 58 }, + { "x": 120, "y": 98 }, + { "x": 15, "y": 98 } + ], + "row_start": 1, + "row_end": 2, + "col_start": 0, + "col_end": 1, + "words": "Content-Type" + }, + { + "cell_location": [ + { "x": 120, "y": 58 }, + { "x": 371, "y": 58 }, + { "x": 371, "y": 98 }, + { "x": 120, "y": 98 } + ], + "row_start": 1, + "row_end": 2, + "col_start": 1, + "col_end": 2, + "words": "application/x-www-form-urlencoded" + } + ], + "footer": [] + } + ] +} +``` +### 错误码 +| 错误码 | 描述 | +|-----|----| + +## 高级用法 + +目前该模块仅提供基础的表格文字识别功能。 + + +## 更新记录和贡献 +* 表格文字识别能力 (2024-01) \ No newline at end of file diff --git a/docs/BasisModule/Components/text_to_image/README.md b/docs/BasisModule/Components/text_to_image/README.md new file mode 100644 index 000000000..09c1671da --- /dev/null +++ b/docs/BasisModule/Components/text_to_image/README.md @@ -0,0 +1,93 @@ +# 文生图 (Text2Image) + +## 简介 +文生图(Text2Image)基于文心大模型,可以根据用户输入的文本,自动创作不限定风格的图,为内容创作者提供灵感和高质量配图。 + +### 功能介绍 +AI一下,文字成画,AI 精准理解中文文本,支持用户自由输入,只需一句话,让文字秒变精美画作,支持自定义丰富的修饰词,可生成不同风格、不同构图、不同流派的图片,满足个性化的图片生成需求。 +### 特色优势 +利用知识增强扩散模型,学习过程融入语言、视觉、跨模态等多源知识,生成图像语义一致性更高,基于混合降噪专家网络,全球最大跨模态生成模型,参数规模达到240亿,根据生成阶段选择最优生成“专家”,从图像轮廓渐进优化细节,全面提升生成质量。 +### 应用场景 +图片素材、艺术插图、海报制作、故事插图、壁纸制作、电商应用、室内设计、影视制作、游戏原画设计、服务创意启发平台等。 + +## 基本用法 + +下面是文生图的代码示例: + +```python +import os +import appbuilder +# 设置环境变量和初始化 +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = "..." + +text2Image = appbuilder.Text2Image() +content_data = {"prompt": "上海的经典风景", "width": 1024, "height": 1024, "image_num": 1} +msg = appbuilder.Message(content_data) +out = text2Image.run(msg) +print(out.content) +#{'img_urls': ['...']} +``` + +生成的"上海的经典风景"图片如下 + +![示例图片](https://bj.bcebos.com/v1/appbuilder-sdk-components/shanghai.png?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-05-30T03%3A08%3A30Z%2F-1%2Fhost%2F64296a40b3f01d39776129e0b4ce732b1784f2f91e3afcf9dd7c1de8c3df6a0a) + + +## 参数说明 + +### 鉴权配置 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 初始化参数 + +无 + +### 调用参数 +|参数名称 |参数类型 |是否必须 |描述 | 示例值 | +|--------|--------|--------|----|--------| +|message |String |是 |输入的消息,输入的消息,用于模型的主要输入内容。这是一个必需的参数| Message(content={"prompt": "上海的经典风景"}) | +|width|Integer|是 |图片宽度,支持:512x512、640x360、360x640、1024x1024、1280x720、720x1280、2048x2048、2560x1440、1440x2560。| 1024 | +|height|Integer|是 |图片高度,支持:512x512、640x360、360x640、1024x1024、1280x720、720x1280、2048x2048、2560x1440、1440x2560。| 1024 | +|image_num|Integer|是 |生成图片数量,默认一张,支持生成 1-8 张。| 1 | +|timeout| Float | 否 | HTTP超时时间,单位:秒 |1|| +|retry|Integer|是 |HTTP重试次数| 3 | + +### 响应参数 +|参数名称 |参数类型 |描述 |示例值| +|--------|--------|----|------| +|result |String |返回结果|["xxx"]| + +### 响应示例 +```json +{"img_urls": ["xxx"]} +``` +### 错误码 +| 错误码 |描述| +|---|---| +| 282000 |服务器内部错误,请再次请求, 如果持续出现此类错误,请在控制台提交工单联系技术支持团队| +| 282004 |请求中包含敏感词、非法参数、字数超限,或上传违规参考图,请检查后重新尝试| +| 282003 |缺少必要参数| +| 17 |日配额流量超限| +| 18 |QPS 超限额| +| 216630 |服务器内部错误,请再次请求,如果持续出现此类错误,请通过工单联系技术支持| +| 501 |文本黄反拦截| +| 201 |模型生图失败| +| 216100 |参数不满足格式要求| +| 216201 |参考图不满足格式要求| +| 4 |错误信息为中文的“请求超限”指所有用户提交的 AI 作画总数超限制| +| 13 |错误信息为中文的“QPS 超限”指单个用户使用提交请求接口的 QPS 超限| +| 15 |错误信息为中文的“并发超限”指单个用户使用 AI 作画的并发超限| +| 17 |错误信息为中文的“用量超限”指单个用户使用 AI 作画的用量超限| + + + +## 高级用法 + +目前该模块仅提供基础的文生图功能。 +## 更新记录和贡献 +* 文生图能力 (2023-12) diff --git a/docs/BasisModule/Components/translate/README.md b/docs/BasisModule/Components/translate/README.md new file mode 100644 index 000000000..23f1ba468 --- /dev/null +++ b/docs/BasisModule/Components/translate/README.md @@ -0,0 +1,316 @@ +# 文本翻译-通用版(Translation) + +## 简介 + +文本翻译组件(Translation)提供多种语言互译的在线文本翻译服务。支持术语定制功能,用户可对翻译结果进行干预,快速提高翻译质量。可广泛应用于移动端、PC网站、智能硬件等不同产品形态中,满足多领域、多场景的翻译需求。 + +### 功能介绍 + +支持200+语种互译,传入待翻译内容,并指定源语言(支持语种自动检测)和目标语言,即可获得翻译结果,并支持术语干预。 +支持在【创建应用】-【添加工具组件】时,对以下语言的试用: + +|名称 | 代码 | 名称 | 代码 | 名称 | 代码 | +|------------|--------|------------|--------|------------|--------| +| 自动检测 | 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 | | | + +### 特色优势 + +1. 依托互联网数据资源和自然语言处理技术优势,上线全球首个互联网神经网络翻译系统,日均响应千亿字符请求 +2. 2019国际机器翻译评测(WMT19)中,获得中英翻译第一,提供业界领先的机器翻译服务 +3. 支持用户上传术语对翻译结果进行干预,优化翻译质量,用户可根据不同产品、不同领域创建多个术语库 +4. 翻译请求可实现实时响应,服务稳定性高,在海外也可及时获取翻译结果,保障用户稳定的服务体验 + +### 应用场景 + +1. 教育学习:在外语教学及学习场景中,通过实时句子翻译、单词释义、语音合成等功能,帮助师生沟通、外教课后点评,辅助阅读和写作,全面提升学习效率与质量 +2. 手机厂商:应用于手机系统中,实现手机系统取词翻译、对话文本翻译等服务。为手机应用开发者提供便捷的翻译功能 +3. 跨境电商:在商业全球化背景下,针对跨国商贸服务中产品名称、详情页等网站基本信息进行翻译,助力企业开拓国际市场 +4. 智能硬件:应用于翻译机、学习机、智能手表等硬件系统中,为用户提供文本翻译、词典及语音合成等能力,实现便捷准确的多语种互译功能 + +## 基本用法 + +通过如下示例代码可以快速开始使用文本翻译组件: + +```python +import os +import appbuilder + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = '...' + +translate = appbuilder.Translation() +resp = translate(appbuilder.Message("你好\n中国"), from_lang="zh", to_lang="en") +# 输出{'from_lang': 'zh', 'to_lang': 'en', 'trans_result': [{'src': '你好', 'dst': 'hello'}, {'src': '中国', 'dst': 'China'}]} +print(resp.content) +``` + +## 参数说明 + +### 鉴权配置 + +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 + +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 初始化参数说明 + +无 + +### 调用参数说明 + +|参数名称 |参数类型 |是否必须 |描述 | 示例值 | +|--------|--------|--------|----|--------| +|message |obj:`Message` |是 |输入的请求翻译文本| Message("你好") | +|from_lang|String|否 |翻译的源语言,默认为`auto`,表示自动检测语言。| zh | +|to_lang|Integer|否 |需要翻译的目标语言,默认为`en`,表示英语。| en | +|timeout| Float | 否 | HTTP超时时间,单位:秒 |1|| +|retry|Integer|否 |HTTP重试次数| 3 | + +### 响应参数 + +|参数名称 |参数类型 |描述 |示例值| +|--------|--------|----|------| +|from_lang |String |翻译源语言| zh| +|to_lang |String |翻译目标语言|en| +|trans_result |List[Object] |返回结果|[{'src': '你好', 'dst': 'hello'}]| +|trans_result[0].src |String |源文本|你好| +|trans_result[0].dst |String |目标文本|hello| + +### 响应示例 + +```json +{ + "from_lang": "zh", + "to_lang": "en", + "trans_result": [ + { + "src": "你好", + "dst": "hello" + } + ] +} +``` + +### 错误码 +|错误码|描述| +|------|---| + +## 更新记录和贡献 +* 文本翻译-通用版 (2024-01) + +## 语种列表 + +|名称 |代码 |语种检测| +|----|-----|------| +| 阿拉伯语 | ara | 是 | +| 爱尔兰语 | gle | 是 | +| 奥克语 | oci | 是 | +| 阿尔巴尼亚语 | alb | 是 | +| 阿尔及利亚阿拉伯语 | arq | 否 | +| 阿肯语 | aka | 否 | +| 阿拉贡语 | arg | 否 | +| 阿姆哈拉语 | amh | 是 | +| 阿萨姆语 | asm | 是 | +| 艾马拉语 | aym | 否 | +| 阿塞拜疆语 | aze | 是 | +| 阿斯图里亚斯语 | ast | 是 | +| 奥塞梯语 | oss | 否 | +| 爱沙尼亚语 | est | 是 | +| 奥杰布瓦语 | oji | 否 | +| 奥里亚语 | ori | 是 | +| 奥罗莫语 | orm | 否 | +| 波兰语 | pl | 是 | +| 波斯语 | per | 是 | +| 布列塔尼语 | bre | 是 | +| 巴什基尔语 | bak | 否 | +| 巴斯克语 | baq | 是 | +| 巴西葡萄牙语 | pot | 否 | +| 白俄罗斯语 | bel | 是 | +| 柏柏尔语 | ber | 是 | +| 邦板牙语 | pam | 否 | +| 保加利亚语 | bul | 是 | +| 北方萨米语 | sme | 否 | +| 北索托语 | ped | 否 | +| 本巴语 | bem | 否 | +| 比林语 | bli | 否 | +| 比斯拉马语 | bis | 否 | +| 俾路支语 | bal | 否 | +| 冰岛语 | ice | 是 | +| 波斯尼亚语 | bos | 是 | +| 博杰普尔语 | bho | 否 | +| 楚瓦什语 | chv | 否 | +| 聪加语 | tso | 否 | +| 丹麦语 | dan | 是 | +| 德语 | de | 是 | +| 鞑靼语 | tat | 是 | +| 掸语 | sha | 否 | +| 德顿语 | tet | 否 | +| 迪维希语 | div | 否 | +| 低地德语 | log | 是 | +| 俄语 | ru | 是 | +| 法语 | fra | 是 | +| 菲律宾语 | fil | 是 | +| 芬兰语 | fin | 是 | +| 梵语 | san | 否 | +| 弗留利语 | fri | 否 | +| 富拉尼语 | ful | 否 | +| 法罗语 | fao | 否 | +| 盖尔语 | gla | 否 | +| 刚果语 | kon | 否 | +| 高地索布语 | ups | 否 | +| 高棉语 | hkm | 是 | +| 格陵兰语 | kal | 否 | +| 格鲁吉亚语 | geo | 是 | +| 古吉拉特语 | guj | 是 | +| 古希腊语 | gra | 否 | +| 古英语 | eno | 否 | +| 瓜拉尼语 | grn | 否 | +| 韩语 | kor | 是 | +| 荷兰语 | nl | 是 | +| 胡帕语 | hup | 否 | +| 哈卡钦语 | hak | 否 | +| 海地语 | ht | 否 | +| 豪萨语 | hau | 否 | +| 黑山语 | mot | 否 | +| 吉尔吉斯语 | kir | 否 | +| 加利西亚语 | glg | 是 | +| 加拿大法语 | frn | 否 | +| 加泰罗尼亚语 | cat | 是 | +| 捷克语 | cs | 是 | +| 卡拜尔语 | kab | 是 | +| 卡纳达语 | kan | 是 | +| 卡努里语 | kau | 否 | +| 卡舒比语 | kah | 否 | +| 康瓦尔语 | cor | 否 | +| 科萨语 | xho | 是 | +| 科西嘉语 | cos | 否 | +| 克里克语 | cre | 否 | +| 克里米亚鞑靼语 | cri | 否 | +| 克林贡语 | kli | 否 | +| 克罗地亚语 | hrv | 是 | +| 克丘亚语 | que | 否 | +| 克什米尔语 | kas | 否 | +| 孔卡尼语 | kok | 否 | +| 库尔德语 | kur | 是 | +| 拉丁语 | lat | 是 | +| 老挝语 | lao | 否 | +| 罗马尼亚语 | rom | 是 | +| 拉特加莱语 | lag | 否 | +| 拉脱维亚语 | lav | 是 | +| 林堡语 | lim | 否 | +| 林加拉语 | lin | 否 | +| 卢干达语 | lug | 否 | +| 卢森堡语 | ltz | 否 | +| 卢森尼亚语 | ruy | 否 | +| 卢旺达语 | kin | 是 | +| 立陶宛语 | lit | 是 | +| 罗曼什语 | roh | 否 | +| 罗姆语 | ro | 否 | +| 逻辑语 | loj | 否 | +| 马来语 | may | 是 | +| 缅甸语 | bur | 是 | +| 马拉地语 | mar | 否 | +| 马拉加斯语 | mg | 是 | +| 马拉雅拉姆语 | mal | 是 | +| 马其顿语 | mac | 是 | +| 马绍尔语 | mah | 否 | +| 迈蒂利语 | mai | 是 | +| 曼克斯语 | glv | 否 | +| 毛里求斯克里奥尔语 | mau | 否 | +| 毛利语 | mao | 否 | +| 孟加拉语 | ben | 是 | +| 马耳他语 | mlt | 是 | +| 苗语 | hmn | 否 | +| 挪威语 | nor | 是 | +| 那不勒斯语 | nea | 否 | +| 南恩德贝莱语 | nbl | 否 | +| 南非荷兰语 | afr | 是 | +| 南索托语 | sot | 否 | +| 尼泊尔语 | nep | 是 | +| 葡萄牙语 | pt | 是 | +| 旁遮普语 | pan | 是 | +| 帕皮阿门托语 | pap | 否 | +| 普什图语 | pus | 否 | +| 齐切瓦语 | nya | 否 | +| 契维语 | twi | 否 | +| 切罗基语 | chr | 否 | +| 日语 | jp | 是 | +| 瑞典语 | swe | 是 | +| 萨丁尼亚语 | srd | 否 | +| 萨摩亚语 | sm | 否 | +| 塞尔维亚-克罗地亚语 | sec | 否 | +| 塞尔维亚语 | srp | 是 | +| 桑海语 | sol | 否 | +| 僧伽罗语 | sin | 是 | +| 世界语 | epo | 是 | +| 书面挪威语 | nob | 是 | +| 斯洛伐克语 | sk | 是 | +| 斯洛文尼亚语 | slo | 是 | +| 斯瓦希里语 | swa | 是 | +| 索马里语 | som | 是 | +| 塞尔维亚语(西里尔) | src | 否 | +| 泰语 | th | 是 | +| 土耳其语 | tr | 是 | +| 塔吉克语 | tgk | 是 | +| 泰米尔语 | tam | 是 | +| 他加禄语 | tgl | 是 | +| 提格利尼亚语 | tir | 否 | +| 泰卢固语 | tel | 是 | +| 突尼斯阿拉伯语 | tua | 否 | +| 土库曼语 | tuk | 否 | +| 乌克兰语 | ukr | 是 | +| 瓦隆语 | wln | 是 | +| 威尔士语 | wel | 是 | +| 文达语 | ven | 否 | +| 沃洛夫语 | wol | 否 | +| 乌尔都语 | urd | 是 | +| 西班牙语 | spa | 是 | +| 希伯来语 | heb | 是 | +| 希腊语 | el | 是 | +| 匈牙利语 | hu | 是 | +| 西弗里斯语 | fry | 是 | +| 西里西亚语 | sil | 否 | +| 希利盖农语 | hil | 否 | +| 下索布语 | los | 否 | +| 夏威夷语 | haw | 否 | +| 新挪威语 | nno | 是 | +| 西非书面语 | nqo | 否 | +| 信德语 | snd | 否 | +| 修纳语 | sna | 否 | +| 宿务语 | ceb | 否 | +| 叙利亚语 | syr | 否 | +| 巽他语 | sun | 否 | +| 英语 | en | 是 | +| 印地语 | hi | 是 | +| 印尼语 | id | 是 | +| 意大利语 | it | 是 | +| 越南语 | vie | 是 | +| 意第绪语 | yid | 否 | +| 因特语 | ina | 否 | +| 亚齐语 | ach | 否 | +| 印古什语 | ing | 否 | +| 伊博语 | ibo | 否 | +| 伊多语 | ido | 否 | +| 约鲁巴语 | yor | 否 | +| 亚美尼亚语 | arm | 是 | +| 伊努克提图特语 | iku | 否 | +| 中文(简体) | zh | 是 | +| 中文(繁体) | cht | 是 | +| 中文(文言文) | wyw | 是 | +| 中文(粤语) | yue | 是 | +| 扎扎其语 | zaz | 否 | +| 中古法语 | frm | 否 | +| 祖鲁语 | zul | 否 | +| 爪哇语 | jav | 否 | diff --git a/docs/BasisModule/Components/tree_mind/README.md b/docs/BasisModule/Components/tree_mind/README.md new file mode 100644 index 000000000..3d8ebaaaa --- /dev/null +++ b/docs/BasisModule/Components/tree_mind/README.md @@ -0,0 +1,66 @@ +# 树图 (TreeMind) + +## 简介 +树图(TreeMind)提供智能思维导图制作工具和丰富的模板,支持脑图、逻辑图、树形图、鱼骨图、组织架构图、时间轴、时间线等多种专业格式。 + +### 功能介绍 +树图(TreeMind)是一款智能思维导图制作工具,它提供了一个用户友好的平台来创建和编辑各种类型的图表。该工具支持多种专业格式,包括脑图、逻辑图、树形图、鱼骨图、组织架构图、时间轴和时间线等,满足不同用户在不同场景下的需求。 + +### 特色优势 +TreeMind提供丰富的模板,支持多种图表格式,用户可以根据个人需求自由编辑和调整图表。 + +### 应用场景 +年度总结:用户可以利用TreeMind生成年度总结的思维导图,整理和回顾一年的工作成果和经验教训。 +项目管理:在项目管理中,TreeMind可以用来规划项目流程、组织架构和时间线,确保项目按计划进行。 +教育和学习:教师和学生可以使用TreeMind来创建课程大纲、学习笔记和复习资料,提高学习效率。 +商业策划:商业人士可以利用TreeMind来制定商业策略、市场分析和竞争对手分析等。 +会议记录:在会议中,TreeMind可以作为记录工具,帮助整理会议要点和行动计划。 + +## 基本用法 + +下面是文生图的代码示例: + +```python +import os +import appbuilder +# 设置环境变量和初始化 +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = "..." + +treemind = appbuilder.TreeMind() +query = "生成一份年度总结的思维导图" +msg = appbuilder.Message(query) +out = treemind.run(msg) +print(out.content) + +>>> +{'result': '思维导图已经为您生成好了,您可以点击'img_link'对应的链接查看,如果您觉得这个思维导图还不够完美,或者您的想法需要更自由地表达,点击'edit_link'对应的链接,对思维导图变形、变色、变内容、甚至可以添加新的元素, +'img_link': 'https://static.shutu.cn/shutu/static/open6e/2024/05/24/dbd67eddec13f3a6a75857b9c6e06d85.jpeg', +'edit_link': 'https://gapi.shutu.cn/ai/edit-mind-url?works_guid=open5ab4af46187ff7c138fcd95de09efe92_bdappbuilder'} +``` + +生成的思维导图:
![图片url](https://bj.bcebos.com/appbuilder-sdk-components/TreeMind-年终总结思维导图.jpeg) + + +## 参数说明 + +### 鉴权配置 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +# 设置环境中的TOKEN,以下示例略 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +``` + +### 初始化参数 +无 + +### 调用参数 +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +|---------|---------|------|-----------------------------|------------------------------------------------| +| message | obj:`Message` | 是 | 输入的消息,用于生成思维导图,这是一个必需的参数 | Message(content={"query": "生成一张年终总结的思维导图"}) | + +### 响应参数 + +| 参数名称 |参数类型 | 描述 | 示例值 | +|-------------|--------|------|------| +| resp | obj:`Message` | 组件返回结果 | Message(name=msg, content={'result': '生成的思维导图:xxx。思维导图已经为您生成好了,如果您觉得这个思维导图还不够完美,或者您的想法需要更自由地表达,点击编辑按钮,对思维导图变形、变色、变内容、甚至可以添加新的元素,您可以通过这个链接编辑:xxx。', 'img_link': 'xxx', 'edit_link': 'xxx'}, mtype=dict) | \ No newline at end of file diff --git a/docs/BasisModule/Components/tts/README.md b/docs/BasisModule/Components/tts/README.md new file mode 100644 index 000000000..a21dff6c2 --- /dev/null +++ b/docs/BasisModule/Components/tts/README.md @@ -0,0 +1,165 @@ +# 短文本在线合成(TTS) + +## 简介 +短文本在线合成组件(TTS)提供高度拟人、流畅自然的语音合成服务,将文本朗读出来,基础音库性价比更高,精品音库听感更逼真。 + +### 功能介绍 +提供高度拟人、流畅自然的语音合成服务。 + +### 特色优势 +将文本朗读出来,基础音库性价比更高,精品音库听感更逼真。可实时生成语音输出,几乎没有延迟,更加自然流畅。 + +### 应用场景 +文本朗读 + + +## 基本用法 + +下面是语音合成的代码示例: +```python +import os +import appbuilder + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +tts = appbuilder.TTS() +cwd = os.getcwd() + +# 使用baidu-tts模型, 默认返回MP3格式 +inp = appbuilder.Message(content={"text": "欢迎使用语音合成"}) +out = tts.run(inp) +mp3_sample_path = os.path.join(cwd,"sample.mp3") +with open(mp3_sample_path, "wb") as f: + f.write(out.content["audio_binary"]) +print("成功将文本转语音,mp3格式文件已写入:{}".format(mp3_sample_path)) + +# 使用paddlespeech-tts模型,目前只支持返回WAV格式 +wav_sample_path = os.path.join(cwd,"sample.wav") +inp = appbuilder.Message(content={"text": "欢迎使用语音合成"}) +out = tts.run(inp, model="paddlespeech-tts", audio_type="wav") +with open(wav_sample_path, "wb") as f: + f.write(out.content["audio_binary"]) +print("成功将文本转语音,wav格式文件已写入:{}".format(wav_sample_path)) +``` + + +## 参数说明 + +### 鉴权说明 +使用组件之前,请首先申请并设置鉴权参数,可参考[组件使用流程](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5)。 +```python +import os + +os.environ["APPBUILDER_TOKEN"] = "..." +``` + +### 初始化参数 + +无 + +### 调用参数 +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +|------------|---------|------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------| +| message | String | 是 | 待转成语音的文本 | Message(content={"text": "需合成的文本"}) | +| model | String | 否 | 默认是`baidu-tts`模型,可选值:`paddlespeech-tts`、`baidu-tts` | paddlespeech-tts | +| speed | Integer | 否 | 语音语速,默认是5中等语速,取值范围在0~15之间,仅当模型为`baidu-tts`参数有效,如果模型为`paddlespeech-tts`,参数自动失效 | 5 | +| pitch | Integer | 否 | 语音音调,默认是5中等音调,取值范围在0~15之间,仅当模型为`baidu-tts`参数有效,如果模型为`paddlespeech-tts`,参数自动失效 | 5 | +| volume | Integer | 否 | 语音音量,默认是5中等音量,取值范围在0~15之间,,仅当模型为`baidu-tts`参数有效,如果模型为`paddlespeech-tts`,参数自动失效 | 5 | +| person | Integer | 否 | 语音人物特征,默认是0(度小美),普通音库可选值包括: 0(度小美)、1(度小宇)、3(度逍遥-基础)、4(度丫丫);精品音库包括:5003(度逍遥-精品)、 5118(度小鹿) 、106(度博文)、 110(度小童)、 111(度小萌)、 103(度米朵)、 5(度小娇),仅当模型为`baidu-tts`参数有效,如果模型为`paddlespeech-tts`,参数自动失效 | 0 | +| audio_type | String | 否 | 音频文件格式,如果使用`baidu-tts`模型可选`mp3`, `wav`; 如果使用`paddlespeech-tts`模型非流式返回,参数只能设为`wav`;如果使用`paddlespeech-tts`模型流式返回,参数只能设为`pcm` | wav | +| stream | Bool | 否 | 默认是False, 目前`paddlespeech-tts`模型支持流式返回,`baidu-tts`模型不支持流式返回 | False | +| retry | Integer | 否 | HTTP重试次数 | 3 | +| timeout | Integer | 否 | HTTP超时时间 | 5 | + +### 非流式语音响应参数 +| 参数名称 | 参数类型 | 描述 | 示例值 | +|---------------|--------|--------|---------| +| content | Dict | 消息内容 | 无 | +| +audio_binary | Bytes | 音频二进制流 | b'语音流' | +| +audio_type | String | 音频类型 | wav/mp3 | + + +### 流式语音响应参数 +| 参数名称 | 参数类型 | 描述 | 示例值 | +|---------|------------------|----------|-----| +| content | Python Generator | 可迭代的二进制流 | 无 | + + +### 响应示例 +```json +{ +"content": { + "audio_binary": "", + "audio_type": "mp3" + } +} +``` + + +### 错误码 +| 错误码 | 描述 | +|-----|----| + +## 高级用法 + +### TTS实时播放语音流 +```python +import os +import appbuilder +import pyaudio + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +tts = appbuilder.TTS() +# 使用paddlespeech-tts模型,目前只支持返回WAV格式 +inp = appbuilder.Message(content={"text": """随着科技的迅速发展,教育领域也经历了巨大的变革。科技不仅改变了教学和学习的方式,还扩展了教育的可能性和边界。 + 从在线课程到交互式学习工具,科技为学生和教师提供了前所未有的资源和机遇。科技使得个性化学习成为可能。通过智能学习系统和适应性学习技术, + 教育内容可以根据学生的学习速度和能力进行定制。"""}) +# 仅支持model为paddlespeech-tts,audio_type为pcm, stream为True +out = tts.run(inp, model="paddlespeech-tts", audio_type="pcm", stream=True) +play = pyaudio.PyAudio() +stream = play.open(format=play.get_format_from_width(2), + channels=1, + rate=24000, + output=True, + frames_per_buffer=2048) +# 实时播放语音流 +for pcm in out.content: + stream.write(pcm) +stream.stop_stream() +stream.close() +``` +### pcm文件转wav + +```python +import os +import appbuilder +import wave + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" +tts = appbuilder.TTS() +inp = appbuilder.Message(content={"text": """随着科技的迅速发展,教育领域也经历了巨大的变革。科技不仅改变了教学和学习的方式,还扩展了教育的可能性和边界。 + 从在线课程到交互式学习工具,科技为学生和教师提供了前所未有的资源和机遇。科技使得个性化学习成为可能。通过智能学习系统和适应性学习技术, + 教育内容可以根据学生的学习速度和能力进行定制。"""}) +# 仅支持model为paddlespeech-tts,audio_type为pcm, stream为True +out = tts.run(inp, model="paddlespeech-tts", audio_type="pcm", stream=True) +count = 1 +cwd = os.getcwd() +for pcm in out.content: + wave_sample_path = os.path.join(cwd, "{}.wav".format(count)) + wavfile = wave.open(wave_sample_path, 'wb') + wavfile.setnchannels(1) + wavfile.setsampwidth(2) + wavfile.setframerate(24000) + wavfile.writeframes(pcm) + wavfile.close() + print("成功将第{}个pcm语音块转换成wav格式,并将对应文件写入:{}".format(count, wave_sample_path)) + count += 1 +``` + + +## 更新记录和贡献 +* 短文本在线合成 (2024-01) +* 增加流式能力 (2024-02) + diff --git a/docs/service/chainlit.md b/docs/BasisModule/Deployment/AgentChainlit.md similarity index 100% rename from docs/service/chainlit.md rename to docs/BasisModule/Deployment/AgentChainlit.md diff --git a/docs/basic_module/agentruntime.md b/docs/BasisModule/Deployment/agentruntime.md similarity index 100% rename from docs/basic_module/agentruntime.md rename to docs/BasisModule/Deployment/agentruntime.md diff --git a/docs/service/cloud.md b/docs/BasisModule/Deployment/cloud.md similarity index 83% rename from docs/service/cloud.md rename to docs/BasisModule/Deployment/cloud.md index af8092af7..40980f3ce 100644 --- a/docs/service/cloud.md +++ b/docs/BasisModule/Deployment/cloud.md @@ -98,10 +98,13 @@ curl --location 'http://{public_ip}:8091/chat' \ ### 接入AppBuilder工作流示例: * 进入AppBuilder官网,先后点击“个人空间”、“组件”、“创建组件”、“API接入” -wechat + +wechat * 点击“编辑API”、“导入cURL”,粘贴上面的curl命令,解析并导入 -wechat + +wechat * 调试通过 -wechat \ No newline at end of file + +wechat \ No newline at end of file diff --git a/docs/service/flask.md b/docs/BasisModule/Deployment/flask.md similarity index 100% rename from docs/service/flask.md rename to docs/BasisModule/Deployment/flask.md diff --git a/docs/basic_module/usersession.md b/docs/BasisModule/Deployment/usersession.md similarity index 100% rename from docs/basic_module/usersession.md rename to docs/BasisModule/Deployment/usersession.md diff --git a/docs/basic_module/get_model_list.md b/docs/BasisModule/Model/get_model_list.md similarity index 100% rename from docs/basic_module/get_model_list.md rename to docs/BasisModule/Model/get_model_list.md diff --git a/docs/basic_module/appbuilder_client.md b/docs/BasisModule/Platform/Application/appbuilder_client.md similarity index 100% rename from docs/basic_module/appbuilder_client.md rename to docs/BasisModule/Platform/Application/appbuilder_client.md diff --git a/docs/basic_module/get_app_list.md b/docs/BasisModule/Platform/Application/get_app_list.md similarity index 100% rename from docs/basic_module/get_app_list.md rename to docs/BasisModule/Platform/Application/get_app_list.md diff --git a/docs/basic_module/components.md b/docs/BasisModule/Platform/CustomComponents/components.md similarity index 100% rename from docs/basic_module/components.md rename to docs/BasisModule/Platform/CustomComponents/components.md diff --git a/docs/basic_module/knowledgebase.md b/docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md similarity index 100% rename from docs/basic_module/knowledgebase.md rename to docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md diff --git a/docs/BasisModule/Trace/README.md b/docs/BasisModule/Trace/README.md new file mode 100644 index 000000000..2a8bd2ff3 --- /dev/null +++ b/docs/BasisModule/Trace/README.md @@ -0,0 +1,7 @@ +# Appbuilder Trace 文档 + +本文档目录包含以下内容 + +- [Appbuilder Trace跟踪功能基本用法](basic.md) +- [Phoneix可视化软件的进阶用法](phoenix_method.md) +- [Appbuilder Trace跟踪功能示例](https://github.com/baidubce/app-builder/blob/master/cookbooks/appbuilder_trace/trace.ipynb) \ No newline at end of file diff --git a/docs/trace/basic.md b/docs/BasisModule/Trace/basic.md similarity index 100% rename from docs/trace/basic.md rename to docs/BasisModule/Trace/basic.md diff --git a/docs/trace/phoenix_method.md b/docs/BasisModule/Trace/phoenix_method.md similarity index 100% rename from docs/trace/phoenix_method.md rename to docs/BasisModule/Trace/phoenix_method.md diff --git a/docs/quick_start/changelog.md b/docs/DevelopGuide/ChangeLog/changelog.md similarity index 98% rename from docs/quick_start/changelog.md rename to docs/DevelopGuide/ChangeLog/changelog.md index e1f0dd2f5..71dbee66b 100644 --- a/docs/quick_start/changelog.md +++ b/docs/DevelopGuide/ChangeLog/changelog.md @@ -58,7 +58,7 @@ * **2024.10.18 v0.9.5版本发布** [ReleaseNote](https://github.com/baidubce/app-builder/releases/tag/0.9.5) * 更新KnowledgeBase组件,新增`切片详情`获取接口,新增切片关联的图片ID字段 * AppBuilderTrace 支持SentrySDK - * AppBuilder新增[Sphinx API文档](/docs/sphinx_md/appbuilder.md) + * AppBuilder新增[Sphinx API文档](../../API-Reference/Python/modules.md) * **2024.10.26 v0.9.6版本发布** [ReleaseNote](https://github.com/baidubce/app-builder/releases/tag/0.9.6) * 更新AppBuilderClient,简化Java & Go语言使用ToolCall的方式 * 新增长文档内容理解组件 diff --git a/docs/develop_guide/env.md b/docs/DevelopGuide/EnvironmentalParameters/env.md similarity index 100% rename from docs/develop_guide/env.md rename to docs/DevelopGuide/EnvironmentalParameters/env.md diff --git a/docs/develop_guide/README.md b/docs/DevelopGuide/HowToContributeCode/README.md similarity index 95% rename from docs/develop_guide/README.md rename to docs/DevelopGuide/HowToContributeCode/README.md index 921d17c59..6c96406e8 100644 --- a/docs/develop_guide/README.md +++ b/docs/DevelopGuide/HowToContributeCode/README.md @@ -2,8 +2,8 @@ 该文档目录包含以下内容: -- [二次开发基本介绍](https://github.com/baidubce/app-builder/blob/master/docs/develop_guide/README.md) -- [AppBuilder SDK 运行环境超参配置说明](https://github.com/baidubce/app-builder/blob/master/docs/develop_guide/env.md) +- [二次开发基本介绍](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/HowToContributeCode/README.md) +- [AppBuilder SDK 运行环境超参配置说明](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/EnvironmentalParameters/env.md) ## 二次开发 diff --git a/docs/QuickStart/CurrentlySupportedProgrammingLanguages/README.md b/docs/QuickStart/CurrentlySupportedProgrammingLanguages/README.md new file mode 100644 index 000000000..99aaf4c8f --- /dev/null +++ b/docs/QuickStart/CurrentlySupportedProgrammingLanguages/README.md @@ -0,0 +1,8 @@ +## SDK当前支持的编程语言 +- 平台功能 SDK: 支持Pyhon/Java/Go + - [应用管理](../../BasisModule/Platform/Application/get_app_list.md) + - [应用调用 AppBuilderClient SDK](../../BasisModule/Platform/Application/appbuilder_client.md) + - [知识库管理 KnowledgeBase SDK](../../BasisModule/Platform/KnowledgeBase/knowledgebase.md) +- AI基础能力组件 SDK:支持Python + - [获取模型列表](../../BasisModule/Model/get_model_list.md) + - [基础能力组件](../../docs/BasisModule/Platform/CustomComponents/components.md) \ No newline at end of file diff --git a/docs/basic_module/README.md b/docs/QuickStart/ExamplesOfIndustrialPracticeApplications/README.md similarity index 85% rename from docs/basic_module/README.md rename to docs/QuickStart/ExamplesOfIndustrialPracticeApplications/README.md index 0c0cd811e..431f2446e 100644 --- a/docs/basic_module/README.md +++ b/docs/QuickStart/ExamplesOfIndustrialPracticeApplications/README.md @@ -6,24 +6,10 @@ AppBuilder面向开发者提供AI原生应用一站式开发工具,包括基 封装程度由高至低,AppBuilder 提供了三种类型的SDK | 分类 | 场景及使用方式 | 百度云文档链接 | SDK 文档链接| |--------|--------|------------|------------| -| 端到端应用 | 在 AppBuilder 产品界面上通过零代码、低代码方式创建的 AI 原生应用,支持通过应用 API/SDK 进行调用 | [应用API及SDK](https://cloud.baidu.com/doc/AppBuilder/s/Plvggbuzc) | [AppBuilder Client SDK](https://github.com/baidubce/app-builder/blob/master/docs/basic_module/appbuilder_client.md) | -| 流程编排 | 基于 Assistants API,可通过全代码形式创建和调试专属智能体(Agent) | [AssistantAPI](https://cloud.baidu.com/doc/AppBuilder/s/nluzkdben) | [AssistantSDK](https://github.com/baidubce/app-builder/blob/master/docs/basic_module/assistant_sdk.md) | +| 端到端应用 | 在 AppBuilder 产品界面上通过零代码、低代码方式创建的 AI 原生应用,支持通过应用 API/SDK 进行调用 | [应用API及SDK](https://cloud.baidu.com/doc/AppBuilder/s/Plvggbuzc) | [AppBuilder Client SDK](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/Application/appbuilder_client.md) | | 工具组件 | 基于组件 SDK,可调用包括大模型组件、AI能力组件等在内的多种组件 | [组件SDK](https://cloud.baidu.com/doc/AppBuilder/s/Glqb6dfiz) | [组件列表](https://cloud.baidu.com/doc/AppBuilder/s/Glqb6dfiz#3%E3%80%81%E5%BC%80%E9%80%9A%E7%BB%84%E4%BB%B6%E6%9C%8D%E5%8A%A1) | -## 本文档目录的内容 -- 平台功能 SDK: 支持Pyhon/Java/Go - - [应用管理](/docs/basic_module/get_app_list.md) - - [应用调用 AppBuilderClient SDK](/docs/basic_module/appbuilder_client.md) - - [知识库管理 KnowledgeBase SDK](/docs/basic_module/knowledgebase.md) -- Assistant SDK:支持Python - - [Assistant SDK 快速开始](/docs/basic_module/assistant_sdk.md) - - [Assistant SDK API说明](/docs/basic_module/assistant_type.md) -- AI基础能力组件 SDK:支持Python - - [获取模型列表](/docs/basic_module/get_model_list.md) - - [基础能力组件](/docs/basic_module/components.md) - - ## 功能示例 diff --git a/docs/quick_start/README.md b/docs/QuickStart/StartFirstAINativeApplication/README.md similarity index 98% rename from docs/quick_start/README.md rename to docs/QuickStart/StartFirstAINativeApplication/README.md index f1a5f2ff5..5b579369d 100644 --- a/docs/quick_start/README.md +++ b/docs/QuickStart/StartFirstAINativeApplication/README.md @@ -2,8 +2,8 @@ 该文档目录包含以下内容: -- [SDK安装](https://github.com/baidubce/app-builder/blob/master/docs/quick_start/install.md) -- [版本更新历史](https://github.com/baidubce/app-builder/blob/master/docs/quick_start/changelog.md) +- [SDK安装](https://github.com/baidubce/app-builder/blob/master/docs/QuickStart/StartFirstAINativeApplication/install.md) +- [版本更新历史](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/ChangeLog/changelog.md) ## 预备步骤 在正式开始使用AppBuilder-SDK之前,可以阅读以下内容: diff --git a/docs/quick_start/install.md b/docs/QuickStart/StartFirstAINativeApplication/install.md similarity index 100% rename from docs/quick_start/install.md rename to docs/QuickStart/StartFirstAINativeApplication/install.md diff --git a/docs/README.md b/docs/README.md index b45df8be4..211ba4ae1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,33 +1,61 @@ # AppBuilder-SDK Documentataion ## Github 文档 -- [快速开始](https://github.com/baidubce/app-builder/blob/master/docs/quick_start/README.md) - - [安装说明](https://github.com/baidubce/app-builder/blob/master/docs/quick_start/install.md) - - [版本说明](https://github.com/baidubce/app-builder/blob/master/docs/quick_start/changelog.md) -- [基础功能](https://github.com/baidubce/app-builder/blob/master/docs/basic_module/README.md) - - 平台功能 SDK: 支持Pyhon/Java/Go - - [应用管理](https://github.com/baidubce/app-builder/blob/master/docs/basic_module/get_app_list.md) - - [应用调用 AppBuilderClient SDK](https://github.com/baidubce/app-builder/blob/master/docs/basic_module/appbuilder_client.md) - - [知识库管理 KnowledgeBase SDK](https://github.com/baidubce/app-builder/blob/master/docs/basic_module/knowledgebase.md) - - Assistant SDK:支持Python - - [Assistant SDK 快速开始](https://github.com/baidubce/app-builder/blob/master/docs/basic_module/assistant_sdk.md) - - [Assistant SDK API说明](https://github.com/baidubce/app-builder/blob/master/docs/basic_module/assistant_type.md) - - AI基础能力组件 SDK:支持Python - - [获取模型列表](https://github.com/baidubce/app-builder/blob/master/docs/basic_module/get_model_list.md) - - [基础能力组件](https://github.com/baidubce/app-builder/blob/master/docs/basic_module/components.md) -- [进阶实践](https://github.com/baidubce/app-builder/blob/master/docs/advanced_application/README.md) - - [Cookbooks](https://github.com/baidubce/app-builder/blob/master/cookbooks/README.md) - - [Appbuilder-SDK Trace跟踪功能](https://github.com/baidubce/app-builder/blob/master/docs/trace/README.md) - - [Appbuilder-SDK Trace基础用法](https://github.com/baidubce/app-builder/blob/master/docs/trace/basic.md) - - [Appbuilder-SDK Trace进阶用法](https://github.com/baidubce/app-builder/blob/master/docs/trace/phoenix_method.md) - - [组件调用的服务化封装](https://github.com/baidubce/app-builder/blob/master/docs/basic_module/agentruntime.md) - - [会话数据管理工具](https://github.com/baidubce/app-builder/blob/master/docs/basic_module/usersession.md) -- [SDK服务化部署](https://github.com/baidubce/app-builder/blob/master/docs/service/README.md) - - [API调用](https://github.com/baidubce/app-builder/blob/master/docs/service/flask.md) - - [交互式服务](https://github.com/baidubce/app-builder/blob/master/docs/service/chainlit.md) - - [公有云部署](https://github.com/baidubce/app-builder/blob/master/docs/service/cloud.md) -- [二次开发](https://github.com/baidubce/app-builder/blob/master/docs/develop_guide/README.md) - - [AppBuilder SDK 运行环境超参配置说明](https://github.com/baidubce/app-builder/blob/master/docs/develop_guide/env.md) +- [首页](https://github.com/baidubce/app-builder/blob/master/docs/README.md) + - 快速上手: + - 开始你的第一个AI原生应用: + - [安装](https://github.com/baidubce/app-builder/blob/master/docs/QuickStart/StartFirstAINativeApplication/install.md) + - [快速开始](https://github.com/baidubce/app-builder/blob/master/docs/QuickStart/StartFirstAINativeApplication/README.md) + - 产业实践应用范例: + - [SDK使用示例](https://github.com/baidubce/app-builder/blob/master/docs/QuickStart/ExamplesOfIndustrialPracticeApplications/README.md) + - [SDK当前支持的编程语言](https://github.com/baidubce/app-builder/blob/master/docs/QuickStart/CurrentlySupportedProgrammingLanguages/README.md) + - 基础: + - 模型: + - [获取模型列表](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Model/get_model_list.md) + - [组件](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - 监控: + - [TRACE基础功能](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/basic.md) + - [TRACE拓展功能](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/phoenix_method.md) + - 部署: + - [交互式前端部署](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/AgentChainlit.md) + - [公有云部署](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/cloud.md) + - [API 访问](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/flask.md) + - [AgentRuntime](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/agentruntime.md) + - [UserSession](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/usersession.md) + - 平台: + - 应用: + - [AppBuilderClient组件](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/Application/appbuilder_client.md) + - [获取AppBuilder已发布的应用列表](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/Application/get_app_list.md) + - 知识库: + - [知识库组件](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md) + - 自定义组件: + - [基础能力组件](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/CustomComponents/components.md) + - 应用: + - Agent: + - [基础知识](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [使用官方组件](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [ToolCall](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [ToolChoice](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [使用异步和流式加速客户端调用](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - RAG: + - [基础知识](https://github.com/baidubce/app-builder/blob/master/docs/Application/RAG/BasicKnowledge/rag.md) + - [知识库管理](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [Reference信息处理](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - Workflow: + - [基础知识](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [从零使用Workflow组装一个RAG应用](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [从零使用Workflow组装一个Agent应用](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - 开发者指南: + - [如何贡献代码](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/HowToContributeCode/README.md) + - [版本升级日志](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/ChangeLog/changelog.md) + - [常见问题FAQ](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [日志管理](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [错误信息](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [环境参数](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/EnvironmentalParameters/env.md) + - API Reference: + - [Python API Reference](https://github.com/baidubce/app-builder/blob/master/docs/API-Reference/Python/PythonAPI.md) + - [Java API Reference](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [Go API Reference](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) ## 平台文档 diff --git a/docs/README_en.md b/docs/README_en.md index f423f800c..f2c79ddb6 100644 --- a/docs/README_en.md +++ b/docs/README_en.md @@ -1,5 +1,5 @@
-logo +logo
[![License](https://img.shields.io/badge/license-Apache%202-blue.svg)](LICENSE) @@ -240,7 +240,7 @@ Hook: ## Panorama of Baidu AI Cloud Qianfan AppBuilder SDK capability
-wechat +wechat
@@ -265,7 +265,7 @@ Hook: ## Open source community and activities

AppBuilder-SDK WeChat Group QR Code

-wechat +wechat
- [Github Issue](https://github.com/baidubce/app-builder/issues): Submit installation/usage issues, report bugs, suggest new features, communicate development plans, etc diff --git a/docs/README_ja.md b/docs/README_ja.md index 23bc623a2..79aa542d3 100644 --- a/docs/README_ja.md +++ b/docs/README_ja.md @@ -1,5 +1,5 @@
-logo +logo
[![License](https://img.shields.io/badge/license-Apache%202-blue.svg)](LICENSE) @@ -236,7 +236,7 @@ print(answer.content.answer) ## Baidu AI Cloud Qianfan AppBuilder SDKの機能全景
-wechat +wechat
@@ -261,7 +261,7 @@ print(answer.content.answer) ## オープンソースコミュニティと活動

AppBuilder-SDK WeChatグループQRコード

-wechat +wechat
- [Github Issue](https://github.com/baidubce/app-builder/issues): インストール/使用の問題を提出し、バグを報告し、新機能を提案し、開発計画をコミュニケーションします。 diff --git a/docs/Tools/DocPass/DocPass.md b/docs/Tools/DocPass/DocPass.md new file mode 100644 index 000000000..72099740c --- /dev/null +++ b/docs/Tools/DocPass/DocPass.md @@ -0,0 +1 @@ +# 文档更新中,敬请期待 \ No newline at end of file diff --git a/docs/tools/markdown2rst.py b/docs/Tools/MarkdownSh/markdown2rst.py similarity index 100% rename from docs/tools/markdown2rst.py rename to docs/Tools/MarkdownSh/markdown2rst.py diff --git a/docs/tools/markdown_parse.py b/docs/Tools/MarkdownSh/markdown_parse.py similarity index 100% rename from docs/tools/markdown_parse.py rename to docs/Tools/MarkdownSh/markdown_parse.py diff --git a/docs/sphinx_doc/Makefile b/docs/Tools/SphinxSh/Makefile similarity index 100% rename from docs/sphinx_doc/Makefile rename to docs/Tools/SphinxSh/Makefile diff --git a/docs/Tools/SphinxSh/PythonAPI.md b/docs/Tools/SphinxSh/PythonAPI.md new file mode 100644 index 000000000..4801ebacd --- /dev/null +++ b/docs/Tools/SphinxSh/PythonAPI.md @@ -0,0 +1,7 @@ +# Python API Reference + + +- [基础 API](appbuilder.md) + - [Assistant API](appbuilder.core.assistant.md) + - [Components API](appbuilder.core.components.md) + - [Console API](appbuilder.core.console.md) \ No newline at end of file diff --git a/docs/sphinx_doc/READEME.md b/docs/Tools/SphinxSh/READEME.md similarity index 88% rename from docs/sphinx_doc/READEME.md rename to docs/Tools/SphinxSh/READEME.md index 80c7b3762..d2a7c6af4 100644 --- a/docs/sphinx_doc/READEME.md +++ b/docs/Tools/SphinxSh/READEME.md @@ -4,9 +4,14 @@ - 完成SDK代码开发 - 依照google规范编写注释--仅需要对类和非私有方法进行注释 -- 进入根目录的docs/sphinx_doc目录下执行update_doc.sh脚本 -- 执行成功后,在docs/sphinx_doc/build/markdown目录下查看生成的文档是否无误 -- 迁移所有生成的文档到docs/sphinx_md目录下 +- 进入根目录的docs/Tools/SphinxSh目录下执行update_doc.sh脚本 +- 执行成功后,在docs/Tools/SphinxSh/build/markdown目录下查看生成的文档是否无误 +- 迁移所有生成的文档到docs/API-Reference/Python目录下 + +## 脚本功能 + +- 依据注释自动生成文档,并将文档迁移到docs/API-Reference/Python目录下 +- 将组件README.md文件按照目录格式迁移到BasisModule/Components目录下,为mkdocs生成文档提供基础链接 ## 代码注释规范 diff --git a/docs/sphinx_doc/appbuilder.core.rst b/docs/Tools/SphinxSh/appbuilder.core.rst similarity index 100% rename from docs/sphinx_doc/appbuilder.core.rst rename to docs/Tools/SphinxSh/appbuilder.core.rst diff --git a/docs/Tools/SphinxSh/get_components_md.py b/docs/Tools/SphinxSh/get_components_md.py new file mode 100644 index 000000000..2f6e56569 --- /dev/null +++ b/docs/Tools/SphinxSh/get_components_md.py @@ -0,0 +1,71 @@ +import os + +def find_readme_files(base_path): + readme_files = [] + for root, dirs, files in os.walk(base_path): + for file in files: + if file.lower() == "readme.md": + readme_files.append(os.path.join(root, file)) + return readme_files + +def extract_first_line(readme_path): + try: + with open(readme_path, 'r', encoding='utf-8') as file: + first_line = file.readline().strip() + if first_line.startswith('#'): + first_line = first_line[1:].strip() + return first_line + except Exception as e: + print(f"Error reading {readme_path}: {str(e)}") + return None + +def update_mkdocs_yml(results, mkdocs_path='../../../mkdocs.yml'): + try: + with open(mkdocs_path, 'r', encoding='utf-8') as file: + lines = file.readlines() + + # 查找 " - 组件:" 行的索引 + start_index = -1 + end_index = -1 + for i, line in enumerate(lines): + if line.strip() == '- 组件:': + start_index = i + elif line.strip() == '- 监控:': + end_index = i + break + + if start_index == -1 or end_index == -1: + print("未找到 '- 组件:' 或 '- 监控:' 行") + return + + # 删除两标记之间的内容 + del lines[start_index + 1:end_index] + + # 在找到的行后面插入新的结果 + for result in reversed(results): + lines.insert(start_index + 1, f" - {result}\n") + + # 写回文件 + with open(mkdocs_path, 'w', encoding='utf-8') as file: + file.writelines(lines) + print("mkdocs.yml 更新成功") + except Exception as e: + print(f"Error updating {mkdocs_path}: {str(e)}") + +def main(): + base_path = '../../BasisModule/Components' # 当前目录 + readme_files = find_readme_files(base_path) + results = [] + + for readme_path in readme_files: + first_line = extract_first_line(readme_path) + if first_line: + relative_path = os.path.relpath(readme_path, start=base_path) + result = f"{first_line}: BasisModule/Components/{relative_path.replace(os.sep, '/')}" + results.append(result) + + # 更新 mkdocs.yml 文件 + update_mkdocs_yml(results) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/docs/sphinx_doc/make.bat b/docs/Tools/SphinxSh/make.bat similarity index 100% rename from docs/sphinx_doc/make.bat rename to docs/Tools/SphinxSh/make.bat diff --git a/docs/sphinx_doc/requirements.txt b/docs/Tools/SphinxSh/requirements.txt similarity index 100% rename from docs/sphinx_doc/requirements.txt rename to docs/Tools/SphinxSh/requirements.txt diff --git a/docs/sphinx_doc/source/conf.py b/docs/Tools/SphinxSh/source/conf.py similarity index 100% rename from docs/sphinx_doc/source/conf.py rename to docs/Tools/SphinxSh/source/conf.py diff --git a/docs/sphinx_doc/source/index.rst b/docs/Tools/SphinxSh/source/index.rst similarity index 100% rename from docs/sphinx_doc/source/index.rst rename to docs/Tools/SphinxSh/source/index.rst diff --git a/docs/sphinx_doc/update_doc.sh b/docs/Tools/SphinxSh/update_doc.sh similarity index 69% rename from docs/sphinx_doc/update_doc.sh rename to docs/Tools/SphinxSh/update_doc.sh index bf2167238..94eaf9462 100644 --- a/docs/sphinx_doc/update_doc.sh +++ b/docs/Tools/SphinxSh/update_doc.sh @@ -4,9 +4,9 @@ echo "========================开始更新文档========================" echo "================注释@HTTPClient.check_param装饰器===============" echo "当前路径:" pwd -cd ../.. +cd ../../.. find . -name "*.py" -exec sed -i '' 's/@HTTPClient\.check_param/# @HTTPClient.check_param/g' {} \; -cd docs/sphinx_doc +cd docs/Tools/SphinxSh echo "===============注释@HTTPClient.check_param装饰器完成==============" # 2、安装依赖 @@ -16,18 +16,34 @@ pwd python3 -m pip install -r requirements.txt echo "安装当前目录的Appbuilder-SDK:" -cd ../.. +cd ../../.. python3 -m pip uninstall appbuilder-sdk -y rm -rf dist python3 -u setup.py bdist_wheel python3 -m pip install dist/*.whl # 更新builde目录 rm -rf build -cd docs/sphinx_doc + +# 检查appbuilder目录是否已存在 +if [ -d "appbuilder" ]; then + echo "Error: Directory 'appbuilder' already exists." + exit 1 +fi + +# 检查python目录是否存在,如果存在则重命名为appbuilder +if [ -d "python" ]; then + mv python appbuilder + echo "Directory 'python' has been renamed to 'appbuilder'." +else + echo "Directory 'python' does not exist." + find . -name "*.py" -exec sed -i '' 's/# @HTTPClient\.check_param/@HTTPClient.check_param/g' {} \; || { echo "恢复装饰器失败"; exit 1; } + exit 1 +fi +cd docs/Tools/SphinxSh echo "=========================安装依赖=========================" -# 3、删除 doc/build 下的所有文件夹 +# 3、删除 build 下的所有文件夹 echo "================删除 doc/build 下的所有文件夹================" echo "当前路径:" pwd @@ -53,7 +69,7 @@ echo "=========删除doc/source下除index.rst的所有.rst文件完成========= echo "============删除原有的 docs/sphinx_md 文件夹及其文件============" echo "当前路径:" pwd -rm -rf ../docs/sphinx_md/* +rm -rf ../../API-Reference/Python/* echo "===========删除原有的 docs/sphinx_md 文件夹及其文件完成===========" @@ -61,8 +77,8 @@ echo "===========删除原有的 docs/sphinx_md 文件夹及其文件完成===== echo "=======执行命令 sphinx-apidoc -o source ../appbuilder/=======" echo "当前路径:" pwd -sphinx-apidoc -o source ../../appbuilder/ -# 删除test目录内容 +sphinx-apidoc -o source ../../../appbuilder/ +# 删除多余文档目录 rm ./source/appbuilder.tests.* rm ./source/appbuilder.utils.* rm ./source/appbuilder.core.assistant.type.rst @@ -76,7 +92,7 @@ cd .. echo "======执行命令 sphinx-apidoc -o source ../appbuilder/完成======" -# 7、在doc目录下执行命令 make markdown +# 7、在doc目录下执行命令 make markdown && make html echo "==============在doc目录下执行命令 make markdown================" echo "当前路径:" pwd @@ -91,17 +107,19 @@ echo "当前路径: $(pwd)" export PATH=/path/to/your/python:$PATH # 执行 make markdown make markdown || { echo "make markdown 命令失败"; exit 1; } -make html || { echo "make html 命令失败"; exit 1; } +# 迁移目录文档 +cp PythonAPI.md build/markdown/ +cp -r build/markdown/ ../../API-Reference/Python echo "=============在doc目录下执行命令 make markdown 完成==============" # 8、恢复装饰器 echo "========================恢复装饰器========================" -cd ../.. +cd ../../.. echo "当前路径:" pwd find . -name "*.py" -exec sed -i '' 's/# @HTTPClient\.check_param/@HTTPClient.check_param/g' {} \; || { echo "恢复装饰器失败"; exit 1; } -cd docs/sphinx_doc +cd docs/Tools/SphinxSh echo "========================恢复装饰器完成========================" @@ -114,7 +132,30 @@ find . -maxdepth 1 -type f -name '*.rst' ! -name 'index.rst' -exec rm {} \;|| { cd .. echo "删除 doc/source 下除index.rst的所有.rst文件完成" rm -rf /build/doctrees/* +cd ../../.. +if [ -d "appbuilder" ]; then + mv appbuilder python + echo "Directory 'appbuilder' has been renamed to 'python'." +else + echo "Directory 'appbuilder' does not exist." + exit 1 +fi +cd docs/Tools/SphinxSh echo "======================清理多余文件完成======================" +# 10、拷贝组件README.md文件到docs/BasisModule/Components目录 +echo "====拷贝组件README.md文件到docs/BasisModule/Components目录====" +echo "当前路径:" +pwd +cd ../../.. +cp -r python/core/components/* docs/BasisModule/Components +cd docs/BasisModule/Components +find . -type f -name "*.py" -exec rm {} + +cd ../../Tools/SphinxSh +# 运行mkdocs更改文件 +python3 get_components_md.py + +echo "====拷贝组件README.md文件到docs/BasisModule/Components目录完成====" + echo "========================更新文档完成========================" diff --git a/docs/sphinx_doc/update_rst.py b/docs/Tools/SphinxSh/update_rst.py similarity index 100% rename from docs/sphinx_doc/update_rst.py rename to docs/Tools/SphinxSh/update_rst.py diff --git a/docs/advanced_application/README.md b/docs/advanced_application/README.md deleted file mode 100644 index 98d7dc9d8..000000000 --- a/docs/advanced_application/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# 进阶应用 - -本文档目录包含以下内容 - -- [入门Cookbook](https://github.com/baidubce/app-builder/blob/master/cookbooks/README.md) -- [进阶业务实践](https://github.com/baidubce/app-builder/blob/master/docs/advanced_application/real_practice.md) -- [组件调用的服务化封装](https://github.com/baidubce/app-builder/blob/master/docs/basic_module/agentruntime.md) -- [会话数据管理工具](https://github.com/baidubce/app-builder/blob/master/docs/basic_module/userseesion.md) \ No newline at end of file diff --git a/docs/advanced_application/real_practice.md b/docs/advanced_application/real_practice.md deleted file mode 100644 index 37be06309..000000000 --- a/docs/advanced_application/real_practice.md +++ /dev/null @@ -1,3 +0,0 @@ -# 业务实践 - -> 内容审核中,敬请期待 \ No newline at end of file diff --git a/docs/basic_module/assistant_sdk.md b/docs/basic_module/assistant_sdk.md deleted file mode 100644 index c0042dd9c..000000000 --- a/docs/basic_module/assistant_sdk.md +++ /dev/null @@ -1,296 +0,0 @@ -# AppBuilder Assistant SDK - -## 简介 - -百度智能云千帆 AppBuilder 在提供零代码、低代码的AI原生应用搭建功能之外,也提供全代码灵活开发与集成能力。基于官方 API/SDK,开放丰富的组件服务,提供具备强大对话、思考及工具调用能力的 Agent 应用框架。 - -封装程度由高至低,提供了三种类型的SDK -| 分类 | 场景及使用方式 | 百度云文档链接 | SDK 文档链接| -|--------|--------|------------|------------| -| 端到端应用 | 在 AppBuilder 产品界面上通过零代码、低代码方式创建的 AI 原生应用,支持通过应用 API/SDK 进行调用 | [应用API及SDK](https://cloud.baidu.com/doc/AppBuilder/s/Plvggbuzc) | [Agent SDK](https://github.com/baidubce/app-builder/blob/master/docs/basic_module/appbuilder_client.md) | -| 代码态智能体 | 基于 Assistants API,可通过全代码形式创建和调试专属智能体(Agent) | [AssistantAPI](https://cloud.baidu.com/doc/AppBuilder/s/nluzkdben) | *当前文档* | -| 工具组件 | 基于组件 SDK,可调用包括大模型组件、AI能力组件等在内的多种组件 | [组件SDK](https://cloud.baidu.com/doc/AppBuilder/s/Glqb6dfiz) | [组件列表](https://cloud.baidu.com/doc/AppBuilder/s/Glqb6dfiz#3%E3%80%81%E5%BC%80%E9%80%9A%E7%BB%84%E4%BB%B6%E6%9C%8D%E5%8A%A1) | - -Assistants API/SDK 正在内测中,敬请期待公测版本。 - - - -### 功能介绍 - -Assistant SDK允许您在自己的应用程序中,使用纯代码构建人工智能助手。该助手可以利用模型、工具和文件来响应用户的诉求,提供关键的FunctionCall能力。 - -### 特色优势 - -与端到端应用相比,Assistants API/SDK 提供了更灵活、更强大的FunctionCall能力,可以满足更复杂的业务场景。基础功能对标 [OpenAI Assistant SDK](https://platform.openai.com/docs/assistants/overview?context=with-streaming),且会结合国内开发者习惯 与 最广泛的ToB场景,提供更多的易用功能与端到端示例。 - -### 应用场景 - -使用SDK纯代码构建Assistant助手,适合有进阶开发能力的开发者。 - -## 基本用法 - -以下是使用SDK进行构建的代码示例,更多详细信息请参考[Assistant API文档](https://cloud.baidu.com/doc/AppBuilder/s/nluzkdben) 与 [Assistant SDK 数据类型文档](https://cloud.baidu.com/doc/AppBuilder/s/nluzkdben) - -### 总览 -一个标准的Assistant构建及使用过程如下: -1. 创建一个Assistant -2. 创建一个Thread -3. 为Thread添加一个Message -4. 创建并运行一个Thread.Run - -下面的QuickStart会分别介绍每一个步骤的最简上手步骤 - -### QuickStart - - -#### Step1:创建一个Assistant - -Assistant 是一个助手的实例,助手可以添加多种参数,包括但不限于 -- 名称 `name` -- 模型 `model` -- 人设指令 `instructions`: 概述`Assistant`的整体功能和定位,需要它扮演一个什么样的『角色』 -- 思维指令 `thought_instructions`: 与业务逻辑和规则相关的指令。希望模型遵守的行为规范、准则及要求,尽可能清晰、详尽的在这里给出描述 -- 对话指令 `chat_instructions`: 与模型最终给出的回复内容相关的指令 - - -以下是一个创建Assistant的示例,更多支持的创建参数参考[Assistant API文档](https://cloud.baidu.com/doc/AppBuilder/s/nluzkdben) -```python -import appbuilder -os.environ["APPBUILDER_TOKEN"] = "your_appbuilder_token" - -assistant = appbuilder.assistant.assistants.create( - name="my_first_assistant", - description="你是一个热心肠的朋友,可以回答一些问题", - instructions="请用亲切的语气回答用户的每一个问题", - ) - -``` - - -#### Step2:创建一个Thread - -`thread` 代表一组对话,等价为`agent sdk`中的`conversation`,该`thread`对话中存储多组 `user <-> assistant`的对话 - -```python -import appbuilder -os.environ["APPBUILDER_TOKEN"] = "your_appbuilder_token" - -thread = appbuilder.assistant.threads.create() - -``` - -#### Step3:为Thread添加一个Message - -用户或应用程序的消息内容,可以作为消息对象添加到`thread`中。消息可以包含文本`content`及`file_id`。添加到`thread`中的消息,需要遵循一唱一和的 `user问-assistant答` 顺序,并保证最后`thread`的最后一个`message`的`role`为`user`。 - -```python -import appbuilder -os.environ["APPBUILDER_TOKEN"] = "your_appbuilder_token" - -thread = appbuilder.assistant.threads.create() -message = appbuilder.assistant.threads.messages.create( - thread_id=thread.id, content="hello world") - -``` - -#### Step4:创建并运行一个Thread.Run -将所有消息都添加到`thread`后,您可以使用`runs`下的方法,创建`run`,并使用相应的`assistant`来生成回复。assistant的回复将被自动添加到`thread`中。 - -```python -import appbuilder -os.environ["APPBUILDER_TOKEN"] = "your_appbuilder_token" - -assistant = appbuilder.assistant.assistants.create( - name="my_first_assistant", - description="你是一个热心肠的朋友,可以回答一些问题", - instructions="请用亲切的语气回答用户的每一个问题") - -thread = appbuilder.assistant.threads.create() - -message = appbuilder.assistant.threads.messages.create( - thread_id=thread.id, - content="你好", -) - -run_result = appbuilder.assistant.threads.runs.run( - thread_id=thread.id, - assistant_id=assistant.id, -) -``` - -### assistant其余功能展示 - - -#### Step1:创建一个Assistant - -```python -import appbuilder -os.environ["APPBUILDER_TOKEN"] = "your_appbuilder_token" - -assistant = appbuilder.assistant.assistants.create( - model = "ERNIE-4.0-8K", - name="test-assistant", - description="test", -) -``` - -#### Step2:查询当前用户创建的Assistant列表 - -```python -assistants_list = appbuilder.assistant.assistants.list() -``` - -#### Step3:查询Assistant详情 - -```python -# 这里的assistant_id为创建的Assistant的id -assistant_query = appbuilder.assistant.assistants.query(assistant_id=assistant.id) -``` - -#### Step4:更新Assistant - -```python -# 更新Assistant的name和description -assistant_update = appbuilder.assistant.assistants.update( - assistant_id = assistant.id, - model="ERNIE-4.0-8K", - name="Test_Name", - description = "test_description" -) -``` - -#### Step5:Assistant关于Files的操作 - -```python -# 上传一个File -file_path = "Your File address" -file = appbuilder.assistant.assistants.files.create(file_path=file_path) - -# 挂载File到Assistant -assistant_mount = appbuilder.assistant.assistants.mount_files( - assistant_id = assistant.id, - file_id = file.id, -) - -# 查询Assistant挂载的File列表 -assistant_files_list = appbuilder.assistant.assistants.mounted_files_list( - assistant_id = assistant.id, -) - -# 取消Assistant挂载的File -assistant_files_delete = appbuilder.assistant.assistants.unmount_files( - assistant_id = assistant.id, - file_id = file.id, -) -``` - -#### Step6:删除Assistant - -```python -assistant_delete = appbuilder.assistant.assistants.delete(assistant_id=assistant.id) -``` - -### files其余功能展示 - - -#### Step1:上传一个File - -```python -import appbuilder -os.environ["APPBUILDER_TOKEN"] = "your_appbuilder_token" -file_path = "Your File address" -file = appbuilder.assistant.assistants.files.create(file_path=file_path) -``` - -#### Step2:file的相关操作 - -```python -# 查询已上传的文件列表 -files_list = appbuilder.assistant.assistants.files.list() - -# 查询已上传的文件信息 -files_query = appbuilder.assistant.assistants.files.query(file_id=file.id) - -# 下载已上传的文件 -# file_path:下载文件保存的地址 -file_download = appbuilder.assistant.assistants.files.download(file_id=file.id,file_path="Your File address") - -# 查看已上传文件的内容 -files_content=appbuilder.assistant.assistants.files.content(file_id=file.id) - -# 删除已上传的文件 -files_delete = appbuilder.assistant.assistants.files.delete(file_id=file.id) -``` - -### thread其余功能展示 - - -#### Step1:创建一个Thread - -```python -import appbuilder -os.environ["APPBUILDER_TOKEN"] = "your_appbuilder_token" -thread = appbuilder.assistant.threads.create() -``` - -#### Step2:Tread的相关操作 - -```python -# 根据thread_id查询Thread对象的信息 -thr_query = appbuilder.assistant.threads.query(thread_id=thread.id) - -# 根据thread_id,对thread进行修改。当前Thread 仅可以修改metadata字段 -thr_update = appbuilder.assistant.threads.update(thread_id=thread.id,metadata={"test":"123"}) - -# 根据thread_id,删除Thread对象 -thr_delete = appbuilder.assistant.threads.delete(thread_id=thread.id) -``` - -### Message其余功能展示 - -#### Step1:创建一个Message - -```python -import appbuilder -os.environ["APPBUILDER_TOKEN"] = "your_appbuilder_token" -msg = appbuilder.assistant.threads.messages.create(thread_id=thread.id,content="hello world") -``` - -#### Step2:Message的相关操作 - -```python -# 查询指定Thread下的Message列表 -# 默认返回20条,limit可以指定返回的条数 -msg_list = appbuilder.assistant.threads.messages.list( - thread_id=msg.thread_id, - limit=1 -) - -# 根据message_id查询Message对象的信息 -msg_query = appbuilder.assistant.threads.messages.query( - thread_id=msg.thread_id, - message_id=msg.id -) - -# 根据message_id,对Message进行修改。当前Message 允许content和file_ids字段 -msg_update= appbuilder.assistant.threads.messages.update( - thread_id=msg.thread_id, - message_id=msg.id, - content='你好' -) - -# 查询一个Message对象下的文件列表 -# 默认返回20条,limit可以指定返回的条数 -msg_files = appbuilder.assistant.threads.messages.files( - thread_id=msg_update.thread_id, - message_id=msg_update.id, - limit=1 -) -``` - - -## 进阶用法 - -- [Assistant SDK 数据类型文档](https://cloud.baidu.com/doc/AppBuilder/s/nluzkdben) -- [Assistant SDK 基础能力Cookbook](https://github.com/baidubce/app-builder/blob/master/cookbooks/README.md) - diff --git a/docs/basic_module/assistant_type.md b/docs/basic_module/assistant_type.md deleted file mode 100644 index 7ec55894c..000000000 --- a/docs/basic_module/assistant_type.md +++ /dev/null @@ -1,1506 +0,0 @@ -# Assistant API说明 - -`Assistant SDK` 的基础接口 与 `Assistant API` 的接口与组织方式保持一致,且一一对应,更详细的接口说明请参考 [Assistant API](https://ai.baidu.com/ai-doc/ASSISTANT/Ck3d7) - -## appbuilder.assistant.assistants - -#### appbuilder.assistant.assistants.create - -功能:创建assistant实例 - -```python -def create(self, - name: str, - description: str, - model: Optional[str] = "ERNIE-4.0-8K", - response_format: Optional[str] = 'text', - instructions: Optional[str] = "", - thought_instructions: Optional[str] = "", - chat_instructions: Optional[str] = "", - tools: Optional[list[assistant_type.AssistantTool]] = [], - file_ids: Optional[list[str]] = [], - metadata: Optional[dict] = {}, - ) -> assistant_type.AssistantCreateResponse: - """ - 创建助手实例 - - Args: - name (str): 助手名称 - description (str): 助手描述 - model (Optional[str], optional): 模型名称. Defaults to "ERNIE-4.0-8K". - response_format (Optional[str], optional): 响应格式. Defaults to 'text'. - instructions (Optional[str], optional): 指令. Defaults to "". - thought_instructions (Optional[str], optional): 思考指令. Defaults to "". - chat_instructions (Optional[str], optional): 聊天指令. Defaults to "". - tools (Optional[list[assistant_type.AssistantTool]], optional): 工具列表. Defaults to []. - file_ids (Optional[list[str]], optional): 文件ID列表. Defaults to []. - metadata (Optional[dict], optional): 元数据. Defaults to {}. - - Returns: - assistant_type.AssistantCreateResponse: 助手创建响应 - - """ -``` - -- appbuilder.assistant.assistants.create 对应的 [Assistant API 链接](https://cloud.baidu.com/doc/AppBuilder/s/qluzl0y5e#1%E5%88%9B%E5%BB%BAassistant) - -- appbuilder.assistant.assistants.create 的[请求参数](https://cloud.baidu.com/doc/AppBuilder/s/qluzl0y5e#%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0) - -```python -class AssistantCreateRequest(BaseModel): - model: str = Field(default="ERNIE-4.0-8K") # 使用的模型 - name: str = Field(default="", min_length=1, max_length=128, pattern="^[\u4e00-\u9fa50-9a-zA-Z_-]+$") # 助理名称 - description: str = Field(default="", max_length=512) # 助理描述 - response_format: ResponseFormat = Field(default=ResponseFormat.TEXT) # 响应格式 - instructions: str = Field(default="你是百度制作的AI助手", max_length=4096) # 助理的通用指令 - thought_instructions: str = Field(default="", max_length=4096) # 助理的思维指令 - chat_instructions: str = Field(default="", max_length=4096) # 助理的聊天指令 - tools: list[AssistantTool] = Field(default=[], max_length=10) # 助理使用的工具列表 - file_ids: list[str] = Field(default=[], max_length=10) # 关联文件的ID列表 - metadata: dict = Field(default={}, max_length=16) # 元数据 -``` - -- appbuilder.assistant.assistants.create 的[响应参数](https://cloud.baidu.com/doc/AppBuilder/s/qluzl0y5e#%E5%93%8D%E5%BA%94%E5%8F%82%E6%95%B0) - -```python -class AssistantCreateResponse(BaseModel): - id: Optional[str] = "" # 助理ID - object: Optional[str] = "" # 助理对象标识 - name: Optional[str] = "" # 助理名称 - description: Optional[str] = "" # 助理描述 - instructions: Optional[str] # 助理的通用指令 - tools: Optional[list[AssistantTool]] = Field(default=[]) # 助理使用的工具列表 - created_at: Optional[int] = 0 # 助理创建时间戳 - thought_instructions: Optional[str] = "" # 助理的思维指令 - chat_instructions: Optional[str] = "" # 助理的聊天指令 - response_format: Optional[ResponseFormat] = Field(default=ResponseFormat.TEXT) # 响应格式 - file_ids: Optional[list[str]] = Field(default=[]) # 关联文件的ID列表 - metadata: Optional[dict] = Field(default={}, max_length=16) # 元数据 -``` - -#### appbuilder.assistant.assistants.update - -功能:根据assistant_id修改一个已创建的Assistant - -```python -def update(self, - assistant_id: str, - model: Optional[str], - name: Optional[str], - description: Optional[str], - instructions: Optional[str] = "", - tools: Optional[list[assistant_type.AssistantTool]] = [], - thought_instructions: Optional[str] = "", - chat_instructions: Optional[str] = "", - response_format: Optional[str] = "text", - file_ids: Optional[list[str]] = [], - metadata: Optional[dict] = {} - ) -> assistant_type.AssistantUpdateResponse: - """ - 根据assistant_id修改一个已创建的Assistant - - Args: - assistant_id (str): 助手ID。 - model (Optional[str]): 助手模型。 - name (Optional[str]): 助手名称。 - description (Optional[str]): 助手描述。 - response_format (Optional[str], optional): 响应格式。默认为None。 - instructions (Optional[str], optional): 助手指令。默认为None。 - thought_instructions (Optional[str], optional): 思考指令。默认为None。 - chat_instructions (Optional[str], optional): 聊天指令。默认为None。 - tools (Optional[list[assistant_type.AssistantTool]], optional): 助手工具列表。默认为空列表。 - file_ids (Optional[list[str]], optional): 文件ID列表。默认为空列表。 - metadata (Optional[dict], optional): 助手元数据。默认为空字典。 - - Returns: - assistant_type.AssistantUpdateResponse: 助手更新响应。 - - """ -``` - -- appbuilder.assistant.assistants.update 对应的 [Assistant API 链接](https://cloud.baidu.com/doc/AppBuilder/s/qluzl0y5e#2%E4%BF%AE%E6%94%B9assistant) - -- appbuilder.assistant.assistants.update 的[请求参数](https://cloud.baidu.com/doc/AppBuilder/s/qluzl0y5e#%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0-1) - -```python -class AssistantUpdateRequest(BaseModel): - assistant_id: Optional[str] = "" # 助理ID - model: str = Field(default="ERNIE-4.0-8K") # 使用的模型 - name: str = Field(default="", min_length=1, max_length=128, pattern="^[\u4e00-\u9fa50-9a-zA-Z_-]+$") # 助理名称 - description: str = Field(default="", max_length=512) # 助理描述 - response_format: ResponseFormat = Field(default=ResponseFormat.TEXT) # 响应格式 - instructions: str = Field(default="你是百度制作的AI助手", max_length=4096) # 助理的通用指令 - thought_instructions: str = Field(default="", max_length=4096) # 助理的思维指令 - chat_instructions: str = Field(default="", max_length=4096) # 助理的聊天指令 - tools: list[AssistantTool] = Field(default=[], max_length=10) # 助理使用的工具列表 - file_ids: list[str] = Field(default=[], max_length=10) # 关联文件的ID列表 - metadata: dict = Field(default={}, max_length=16) # 元数据 -``` - -- appbuilder.assistant.assistants.update 的[响应参数](https://cloud.baidu.com/doc/AppBuilder/s/qluzl0y5e#%E5%93%8D%E5%BA%94%E5%8F%82%E6%95%B0-1) - -```python -class AssistantUpdateResponse(BaseModel): - id: Optional[str] = "" # 助理ID - model: Optional[str] = "" # 助理对象标识 - name: Optional[str] = "" # 助理名称 - description: Optional[str] = "" # 助理描述 - response_format: Optional[ResponseFormat] = Field(default=ResponseFormat.TEXT) # 响应格式 - instructions: Optional[str] # 助理的通用指令 - created_at: Optional[int] = 0 # 助理创建时间戳 - thought_instructions: Optional[str] = "" # 助理的思维指令 - chat_instructions: Optional[str] = "" # 助理的聊天指令 - tools: Optional[list[AssistantTool]] = Field(default=[]) # 助理使用的工具列表 - file_ids: Optional[list[str]] = Field(default=[]) # 关联文件的ID列表 - metadata: Optional[dict] = Field(default={}, max_length=16) # 元数据 -``` - -#### appbuilder.assistant.assistants.list - -功能:查询当前用户已创建的assistant列表 - -```python -def list(self, - limit: Optional[int] = 20, - order: Optional[str] = "desc", - after: Optional[str] = "", - before: Optional[str] = "", - ) -> assistant_type.AssistantListResponse: - """ - 查询当前用户已创建的assistant列表 - - Args: - limit (Optional[int], optional): 返回助手列表的最大数量,默认为20。 - order (Optional[str], optional): 返回助手列表的排序方式,可选值为"asc"或"desc",默认为"desc"。 - after (Optional[str], optional): 返回助手列表中id在指定id之后的助手,默认为空字符串。 - before (Optional[str], optional): 返回助手列表中id在指定id之前的助手,默认为空字符串。 - - Returns: - assistant_type.AssistantListResponse: 助手列表响应体。 - - """ -``` - -- appbuilder.assistant.assistants.list 对应的 [Assistant API 链接](https://cloud.baidu.com/doc/AppBuilder/s/qluzl0y5e#3-%E6%9F%A5%E8%AF%A2assistant%E5%88%97%E8%A1%A8) - -- appbuilder.assistant.assistants.list 的[请求参数](https://cloud.baidu.com/doc/AppBuilder/s/qluzl0y5e#%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0-2) - -```python -class AssistantListRequest(BaseModel): - limit: Optional[int] = Field(default=20) # 列举结果数量上限 - order: Optional[AssistantListRole] = Field(default= AssistantListRole.DESC) # 排序字段 - after: Optional[str] = Field(default="") # 查询指定assistant_id之后创建的Assistant - before: Optional[str] = Field(default="") # 查询指定assistant_id之前创建的Assistant -``` - -- appbuilder.assistant.assistants.list 的[响应参数](https://cloud.baidu.com/doc/AppBuilder/s/qluzl0y5e#%E5%93%8D%E5%BA%94%E5%8F%82%E6%95%B0-2) - -```python -class AssistantListResponse(BaseModel): - object: str = "list" # 结构类型,返回值固定为 list - data: Optional[list[AssistantCreateResponse]] = Field(default=[]) # Assistant对象列表 - first_id: Optional[str] = "" # 返回的列表中第一条assistant的id - last_id: Optional[str] = "" # 返回的列表中最后一条assistant的id - has_more: bool = False # 是否还有更多的数据 -``` - -#### appbuilder.assistant.assistants.query - -功能:根据assistant_id查询Assistant信息 - -```python -def query(self, - assistant_id: Optional[str]) -> assistant_type.AssistantQueryResponse: - """ - 根据assistant_id查询Assistant信息 - - Args: - assistant_id (Optional[str]): 助手ID - - Returns: - assistant_type.AssistantQueryResponse: 助手查询响应结果 - - Raises: - HTTPError: 请求失败,抛出HTTPError异常 - """ -``` - -- appbuilder.assistant.assistants.query 对应的 [Assistant API 链接](https://cloud.baidu.com/doc/AppBuilder/s/qluzl0y5e#4-%E6%9F%A5%E8%AF%A2assistant) - -- appbuilder.assistant.assistants.query 的[请求参数](https://cloud.baidu.com/doc/AppBuilder/s/qluzl0y5e#%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0-3) - -```python -class AssistantQueryRequest(BaseModel): - assistant_id: Optional[str] = "" # 助理ID -``` - -- appbuilder.assistant.assistants.query 的[响应参数](https://cloud.baidu.com/doc/AppBuilder/s/qluzl0y5e#%E5%93%8D%E5%BA%94%E5%8F%82%E6%95%B0-3) - -```python -class AssistantQueryResponse(BaseModel): - id: Optional[str] = "" # 助理ID - object: Optional[str] = "" # 助理对象标识 - name: Optional[str] = "" # 助理名称 - description: Optional[str] = "" # 助理描述 - instructions: Optional[str] # 助理的通用指令 - tools: Optional[list[AssistantTool]] = Field(default=[]) # 助理使用的工具列表 - created_at: Optional[int] = 0 # 助理创建时间戳 - thought_instructions: Optional[str] = "" # 助理的思维指令 - chat_instructions: Optional[str] = "" # 助理的聊天指令 - response_format: Optional[ResponseFormat] = Field(default=ResponseFormat.TEXT) # 响应格式 - file_ids: Optional[list[str]] = Field(default=[]) # 关联文件的ID列表 - metadata: Optional[dict] = Field(default={}, max_length=16) # 元数据 -``` - -#### appbuilder.assistant.assistants.delete - -功能:根据assistant_id删除指定Assitant - -```python -def delete(self, - assistant_id: Optional[str]) -> assistant_type.AssistantDeleteResponse: - """ - 根据assistant_id删除指定Assitant - - Args: - assistant_id (Optional[str]): 待删除的助手实例ID。 - - Returns: - assistant_type.AssistantDeleteResponse: 删除助手实例后的响应结果。 - - Raises: - HttpRequestError: 发送HTTP请求时发生错误。 - - """ -``` - -- appbuilder.assistant.assistants.delete 对应的 [Assistant API 链接](https://cloud.baidu.com/doc/AppBuilder/s/qluzl0y5e#5-%E5%88%A0%E9%99%A4assistant) - -- appbuilder.assistant.assistants.delete 的[请求参数](https://cloud.baidu.com/doc/AppBuilder/s/qluzl0y5e#%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0-4) - -```python -class AssistantDeleteRequest(BaseModel): - assistant_id: Optional[str] = "" # 助理ID -``` - -- appbuilder.assistant.assistants.delete 的[响应参数](https://cloud.baidu.com/doc/AppBuilder/s/qluzl0y5e#%E5%93%8D%E5%BA%94%E5%8F%82%E6%95%B0-4) - -```python -class AssistantDeleteResponse(BaseModel): - id: Optional[str] = "" # 助理ID - object: Optional[str] = "" # 助理对象标识 - deleted: bool = False # 删除状态 -``` - -#### appbuilder.assistant.assistants.mount_files - -功能:指定file_id和assistant_id,挂载File到对应的Assistant - -```python -def mount_files(self, - assistant_id: Optional[str], - file_id: Optional[str] - ) -> assistant_type.AssistantFilesResponse: - """ - 指定file_id和assistant_id,挂载File到对应的Assistant - - Args: - assistant_id (Optional[str]): 助理ID。 - file_id (Optional[str]): 文件ID。 - - Returns: - assistant_type.AssistantFilesResponse: 助理文件列表响应对象。 - - """ -``` - -- appbuilder.assistant.assistants.mount_files 对应的 [Assistant API 链接](https://cloud.baidu.com/doc/AppBuilder/s/qluzl0y5e#6-%E6%8C%82%E8%BD%BDfile%E5%88%B0assistant) - -- appbuilder.assistant.assistants.mount_files 的[请求参数](https://cloud.baidu.com/doc/AppBuilder/s/qluzl0y5e#%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0-5) - -```python -class AssistantFilesRequest(BaseModel): - assistant_id: Optional[str] = "" # 助理ID - file_id: Optional[str] = "" # File对象的id -``` - -- appbuilder.assistant.assistants.mount_files 的[响应参数](https://cloud.baidu.com/doc/AppBuilder/s/qluzl0y5e#%E5%93%8D%E5%BA%94%E5%8F%82%E6%95%B0-5) - -```python -class AssistantFilesResponse(BaseModel): - id: Optional[str] = "" # File对象的id,值等于入参 - object: Optional[str] = "" # 助理对象标识 - created_at: Optional[int] = 0 # 助理创建时间戳 - assistant_id: Optional[str] = "" # Assistant对象的id,值等于入参 -``` - -#### appbuilder.assistant.assistants.mounted_files_list - -功能:查询Assistant挂载的File列表 - -```python -def mounted_files_list(self, - assistant_id: Optional[str], - limit: Optional[int] = 20, - order: Optional[str] = 'desc' , - after: Optional[str] = "", - before: Optional[str] = "") -> assistant_type.AssistantMountedFilesListResponse: - """ - 查询Assistant挂载的File列表 - - Args: - assistant_id (Optional[str]): 助手ID,为空时获取当前登录用户的助手文件列表。 - limit (Optional[int], optional): 每页最多显示多少个文件。默认为20。 - order (Optional[AssistantListRole], optional): 文件列表排序方式。可选值为 'asc' 或 'desc'。默认为 'desc'。 - after (Optional[str], optional): 返回文件ID大于该值的文件列表。默认为空字符串。 - before (Optional[str], optional): 返回文件ID小于该值的文件列表。默认为空字符串。 - - Returns: - assistant_type.AssistantFilesListResponse: 包含文件列表信息的响应对象。 - - """ -``` - -- appbuilder.assistant.assistants.mounted_files_list 对应的 [Assistant API 链接](https://cloud.baidu.com/doc/AppBuilder/s/qluzl0y5e#7-%E6%9F%A5%E8%AF%A2assistant%E6%8C%82%E8%BD%BD%E7%9A%84file%E5%88%97%E8%A1%A8) - -- appbuilder.assistant.assistants.mounted_files_list 的[请求参数](https://cloud.baidu.com/doc/AppBuilder/s/qluzl0y5e#%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0-6) - -```python -class AssistantFilesDeleteRequest(BaseModel): - assistant_id: Optional[str] = "" # 助理ID - file_id: Optional[str] = "" # File对象的id -``` - -- appbuilder.assistant.assistants.mounted_files_list 的[响应参数](https://cloud.baidu.com/doc/AppBuilder/s/qluzl0y5e#%E5%93%8D%E5%BA%94%E5%8F%82%E6%95%B0-6) - -```python -class AssistantFilesContentResponse(BaseModel): - content_type:Optional[str] = "" # 文件类型 - content :Optional[bytes] =b"" # 二进制流数据 -``` - -#### appbuilder.assistant.assistants.unmount_files - -功能:指定assistant_id和file_id,解绑Assistant中对应File的关联 - -```python -def unmount_files(self, - assistant_id: Optional[str], - file_id: Optional[str] - ) -> assistant_type.AssistantFilesDeleteResponse: - """ - 指定assistant_id和file_id,解绑Assistant中对应File的关联 - - Args: - assistant_id (Optional[str]): 助理ID。 - file_id (Optional[str]): 文件ID。 - Returns: - assistant_type.AssistantFilesDeleteResponse: 响应对象。 - """ -``` - -- appbuilder.assistant.assistants.unmount_files 对应的 [Assistant API 链接](https://cloud.baidu.com/doc/AppBuilder/s/qluzl0y5e#8-%E8%A7%A3%E7%BB%91assistant%E6%8C%82%E8%BD%BD%E7%9A%84file) - -- appbuilder.assistant.assistants.unmount_files 的[请求参数](https://cloud.baidu.com/doc/AppBuilder/s/qluzl0y5e#%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0-7) - -```python -class AssistantFilesDeleteRequest(BaseModel): - assistant_id: Optional[str] = "" # 助理ID - file_id: Optional[str] = "" # File对象的id -``` - -- appbuilder.assistant.assistants.unmount_files 的[响应参数](https://cloud.baidu.com/doc/AppBuilder/s/qluzl0y5e#%E5%93%8D%E5%BA%94%E5%8F%82%E6%95%B0-7) - -```python -class AssistantFilesDeleteResponse(BaseModel): - id: str = "" # 文件ID - object: str = "" # 文件对象标识 - deleted: bool = False # 是否删除成功 -``` - - -## appbuilder.assistant.assistants.files - - -#### appbuilder.assistant.assistants.files.create - -功能:上传并创建一个文件实例,该文件与assistant和thread解耦,可以单独使用。 - -```python -def create(self, file_path: str, purpose: str = "assistant") -> assistant_type.AssistantFilesCreateResponse: - """ - 上传文件到助理存储中。 - - Args: - file_path (str): 要上传的文件路径。 - purpose (str, optional): 上传文件的用途。默认为 "assistant"。 - - Returns: - assistant_type.AssistantFilesCreateResponse: 上传文件后返回的响应对象。 - - Raises: - ValueError: 如果指定的文件路径不存在,则会引发此异常。 - """ -``` - -- appbuilder.assistant.assistants.files.create 对应的 [Assistant API 链接](https://cloud.baidu.com/doc/AppBuilder/s/ulv0g1t3x#1%E4%B8%8A%E4%BC%A0%E6%96%87%E4%BB%B6) - -- appbuilder.assistant.assistants.files.create 的[请求参数](https://cloud.baidu.com/doc/AppBuilder/s/ulv0g1t3x#%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0) - -```python -file_path (str): 要上传的文件路径。 -purpose (str, optional): 上传文件的用途。默认为 "assistant"。 -``` - -- appbuilder.assistant.assistants.files.create 的[响应参数](https://cloud.baidu.com/doc/AppBuilder/s/ulv0g1t3x#%E5%93%8D%E5%BA%94%E5%8F%82%E6%95%B0) - -```python -class AssistantFilesCreateResponse(BaseModel): - id: str = "" # 文件ID - bytes: int = 0 # 文件大小(字节) - object: str = "" # 文件对象标识 - purpose: str = "" # 文件用途 - create_at: int = 0 # 文件创建时间戳 - filename: str = "" # 文件名 - classification_id: str = "" # 文件分类ID -``` - -#### appbuilder.assistant.assistants.files.list - -功能:列出存储中的文件列表 - -```python -def list(self) -> assistant_type.AssistantFilesListResponse: - """ - 列出存储中的文件列表 - - Args: - 无 - - Returns: - assistant_type.AssistantFilesListResponse: 文件列表的响应对象,包含以下属性: - - Raises: - assistant_type.AssistantError: 请求发生错误时抛出,具体错误信息可通过 `error_msg` 属性获取 - """ -``` - -- appbuilder.assistant.assistants.files.list 对应的 [Assistant API 链接](https://cloud.baidu.com/doc/AppBuilder/s/ulv0g1t3x#2%E6%9F%A5%E8%AF%A2%E5%B7%B2%E4%B8%8A%E4%BC%A0%E7%9A%84%E6%96%87%E4%BB%B6%E5%88%97%E8%A1%A8) - -- appbuilder.assistant.assistants.files.list 的[响应参数](https://cloud.baidu.com/doc/AppBuilder/s/ulv0g1t3x#%E5%93%8D%E5%BA%94%E5%8F%82%E6%95%B0-1) - -```python -class AssistantFilesListResponse(BaseModel): - object :str = "list" - data: list[AssistantFilesListData] = [] -``` -**注:**AssistantFilesListData类即为AssistantFilesQueryResponse类 - -#### appbuilder.assistant.assistants.files.query - -功能:根据文件ID查询文件信息 - -```python -def query(self, - file_id: str, - ) -> assistant_type.AssistantFilesQueryResponse: - """ - 根据文件ID查询文件信息 - - Args: - file_id (str): 文件ID - - Returns: - assistant_type.AssistantFilesQueryResponse: 文件查询响应对象 - - Raises: - TypeError: 如果file_id不是str类型 - ValueError: 如果file_id不存在 - """ -``` - -- appbuilder.assistant.assistants.files.query 对应的 [Assistant API 链接](https://cloud.baidu.com/doc/AppBuilder/s/ulv0g1t3x#3%E6%9F%A5%E8%AF%A2%E5%B7%B2%E4%B8%8A%E4%BC%A0%E7%9A%84%E6%96%87%E4%BB%B6%E4%BF%A1%E6%81%AF) - -- appbuilder.assistant.assistants.files.query 的[请求参数](https://cloud.baidu.com/doc/AppBuilder/s/ulv0g1t3x#%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0-2) - -```python -file_id (str): 文件ID -``` - -- appbuilder.assistant.assistants.files.query 的[响应参数](https://cloud.baidu.com/doc/AppBuilder/s/ulv0g1t3x#%E5%93%8D%E5%BA%94%E5%8F%82%E6%95%B0-2) - -```python -class AssistantFilesQueryResponse(BaseModel): - id: str = "" # 文件ID - bytes: int = 0 # 文件大小(字节) - object: str = "" # 文件对象标识 - purpose: str = "" # 文件用途 - censored :AuditStatus = Field() # 审核状态 - create_at: int = 0 # 文件创建时间戳 - filename: str = "" # 文件名 - classification_id: str = "" # 文件分类ID - file_type: str = "" # 文件类型 -``` - -#### appbuilder.assistant.assistants.files.delete - -功能:根据file_id删除一个已上传的文件 - -```python -def delete(self, - file_id: str, - ) -> assistant_type.AssistantFilesDeleteResponse: -""" -删除文件 -Args: - file_id (str): 文件ID -Returns: - assistant_type.AssistantFilesDeleteResponse: 删除文件后的响应对象。 -Raises: - 无 -""" -``` - -- appbuilder.assistant.assistants.files.delete 对应的 [Assistant API 链接](https://cloud.baidu.com/doc/AppBuilder/s/ulv0g1t3x#4%E5%88%A0%E9%99%A4%E6%96%87%E4%BB%B6) - -- appbuilder.assistant.assistants.files.delete 的[请求参数](https://cloud.baidu.com/doc/AppBuilder/s/ulv0g1t3x#%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0-3) - -```python -file_id (str): 文件ID -``` - -- appbuilder.assistant.assistants.files.delete 的[响应参数](https://cloud.baidu.com/doc/AppBuilder/s/ulv0g1t3x#%E5%93%8D%E5%BA%94%E5%8F%82%E6%95%B0-3) - -```python -class AssistantFilesDeleteResponse(BaseModel): - id: str = "" # 文件ID - object: str = "" # 文件对象标识 - deleted: bool = False # 是否删除成功 -``` - -#### appbuilder.assistant.assistants.files.download - -功能:下载文件 - -```python -def download(self, - file_id:str, - file_path:str="", # 要求若文件路径不为空,需要以/结尾,默认下载到当前文件夹 - timeout:Optional[int]=None, - ): - """ - 下载文件 - - Args: - file_id (str): 文件ID - file_path (str, optional): 文件保存路径,默认为空字符串。如果未指定,则使用文件名的默认值。要求若文件路径不为空,需要以/结尾。 - timeout (Optional[int], optional): 请求超时时间,单位秒。如果未指定,则使用默认超时时间。 - - Returns: - None - - Raises: - TypeError: 当file_path或file_id类型不为str时引发此异常。 - ValueError: 当file_id为空或None时,或file_path不是文件目录时引发此异常。 - FileNotFoundError: 当指定的文件路径或文件不存在时引发此异常。 - OSError: 当磁盘空间不足时引发此异常。 - HTTPConnectionException: 当请求失败时引发此异常。 - Exception: 当发生其他异常时引发此异常。 - """ -``` - -- appbuilder.assistant.assistants.files.download 对应的 [Assistant API 链接](https://cloud.baidu.com/doc/AppBuilder/s/ulv0g1t3x#5%E4%B8%8B%E8%BD%BD%E6%96%87%E4%BB%B6) - -- appbuilder.assistant.assistants.files.download 的[请求参数](https://cloud.baidu.com/doc/AppBuilder/s/ulv0g1t3x#%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0-4) - -```python -file_id (str): 文件ID -``` - -#### appbuilder.assistant.assistants.files.content - -功能:获取指定文件的内容 - -```python -def content(self, - file_id:str, - timeout:Optional[int]=None): - """ - 获取指定文件的内容 - - Args: - file_id (str): 文件ID - timeout (Optional[int], optional): 请求超时时间,单位秒. Defaults to None. - - Returns: - assistant_type.AssistantFilesContentResponse: 包含文件内容的响应对象 - - Raises: - TypeError: 当file_id不是字符串类型时引发此异常 - FileNotFoundError: 当指定的文件路径不存在时引发此异常 - HTTPConnectionException: 当请求失败时引发此异常 - - """ -``` - -- appbuilder.assistant.assistants.files.content 对应的 [Assistant API 链接](https://cloud.baidu.com/doc/AppBuilder/s/ulv0g1t3x#6%E6%9F%A5%E7%9C%8B%E6%96%87%E4%BB%B6%E5%86%85%E5%AE%B9) - -- appbuilder.assistant.assistants.files.content 的[请求参数](https://cloud.baidu.com/doc/AppBuilder/s/ulv0g1t3x#%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0-5) - -```python -file_id (str): 文件ID -``` - -## appbuilder.assistant.threads - -#### appbuilder.assistant.threads.create - -功能:创建一个对话线程(等价于converstaion) - -```python -def create(self, messages: Optional[list[thread_type.AssistantMessage]] = []) -> thread_type.ThreadCreateResponse: - """ - 创建一个新的对话线程。 - - Args: - messages: 要发送给助手的消息列表。如果不传入此参数,则会创建一个空对话线程。 - - Returns: - 一个ThreadCreateResponse对象,包含新创建的线程的相关信息。 - - Raises: - ValueError: 如果传入的messages参数不是列表类型。 - - """ -``` - -- appbuilder.assistant.threads.create 对应的 [Assistant API 链接](https://cloud.baidu.com/doc/AppBuilder/s/Nlv0g3e50#1-%E5%88%9B%E5%BB%BAthread) - -- appbuilder.assistant.threads.create 的[请求参数](https://cloud.baidu.com/doc/AppBuilder/s/Nlv0g3e50#%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0) - -```python -class ThreadCreateRequest(BaseModel): - messages: list[AssistantMessage] -``` - -- appbuilder.assistant.threads.create 的[响应参数](https://cloud.baidu.com/doc/AppBuilder/s/Nlv0g3e50#%E5%93%8D%E5%BA%94%E5%8F%82%E6%95%B0) - -```python -class ThreadCreateResponse(BaseModel): - id: str = "" - object: str = "" - created_at: int = 0 - metadata: dict = {} -``` - -#### appbuilder.assistant.threads.query - -功能:查询对话线程信息。 - -```python -def query(self, - thread_id:str)->thread_type.ThreadQueryResponse: - """ - 查询对话线程信息。 - Args: - thread_id: 要查询的对话线程ID。 - Returns: - 一个ThreadQueryResponse对象,包含对话线程的相关信息。 - Raises: - ValueError: 如果传入的thread_id参数不是字符串类型。 - """ -``` - -- appbuilder.assistant.threads.query 对应的 [Assistant API 链接](https://cloud.baidu.com/doc/AppBuilder/s/Nlv0g3e50#2-%E6%9F%A5%E8%AF%A2thread) - -- appbuilder.assistant.threads.query 的[请求参数](https://cloud.baidu.com/doc/AppBuilder/s/Nlv0g3e50#%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0-1) - -```python -class ThreadQueryRequest(BaseModel): - thread_id: str -``` - -- appbuilder.assistant.threads.query 的[响应参数](https://cloud.baidu.com/doc/AppBuilder/s/Nlv0g3e50#%E5%93%8D%E5%BA%94%E5%8F%82%E6%95%B0-1) - -```python -class ThreadQueryResponse(BaseModel): - id: str = "" - object: str = "" - created_at: int = 0 - metadata: dict = {} -``` - -#### appbuilder.assistant.threads.delete - -功能:删除对话线程。 - -```python -def delete(self, - thread_id:str)->thread_type.ThreadDeleteResponse: - """ - 删除对话线程。 - Args: - thread_id: 要删除的对话线程ID。 - Returns: - 一个ThreadDeleteResponse对象,包含对话线程的相关信息。 - Raises: - ValueError: 如果传入的thread_id参数不是字符串类型。 - """ -``` - -- appbuilder.assistant.threads.delete 对应的 [Assistant API 链接](https://cloud.baidu.com/doc/AppBuilder/s/Nlv0g3e50#3-%E5%88%A0%E9%99%A4thread) - -- appbuilder.assistant.threads.delete 的[请求参数](https://cloud.baidu.com/doc/AppBuilder/s/Nlv0g3e50#%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0-2) - -```python -class ThreadDeleteRequest(BaseModel): - thread_id: str -``` - -- appbuilder.assistant.threads.delete 的[响应参数](https://cloud.baidu.com/doc/AppBuilder/s/Nlv0g3e50#%E5%93%8D%E5%BA%94%E5%8F%82%E6%95%B0-2) - -```python -class ThreadDeleteResponse(BaseModel): - id: str = "" - object: str = "" - deleted: bool = False -``` - -#### appbuilder.assistant.threads.update - -功能: - -```python -def update(self, - thread_id:str , - metadata:Optional[dict] ={} )->thread_type.ThreadUpdateResponse: - """ - 更新线程信息 - - Args: - thread_id (str): 线程ID - metadata (Optional[dict], optional): 线程元数据. 默认为空字典. - - Returns: - thread_type.ThreadUpdateResponse: 线程更新响应 - - Raises: - TypeError: 如果metadata不是字典类型 - ValueError: 如果metadata的键超过64个字符或值超过512个字符 - """ -``` - -- appbuilder.assistant.threads.update 对应的 [Assistant API 链接](https://cloud.baidu.com/doc/AppBuilder/s/Nlv0g3e50#4-%E4%BF%AE%E6%94%B9thread) - -- appbuilder.assistant.threads.update 的[请求参数](https://cloud.baidu.com/doc/AppBuilder/s/Nlv0g3e50#%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0-3) - -```python -class ThreadUpdateRequest(BaseModel): - thread_id: str - metadata: Optional[dict] = Field(default={}, max_length=16) -``` - -- appbuilder.assistant.threads.update 的[响应参数](https://cloud.baidu.com/doc/AppBuilder/s/Nlv0g3e50#%E5%93%8D%E5%BA%94%E5%8F%82%E6%95%B0-3) - -```python -class ThreadUpdateResponse(BaseModel): - id: str = "" - object: str = "" - created_at: Optional[int] = 0 - metadata: Optional[dict] = {} -``` - -## appbuilder.assistant.threads.messages - -#### appbuilder.assistant.threads.messages.create - -功能:在指定的thread中,最后位置附加一条消息 - -```python -def create(self, - thread_id: str, - content: str, - role: Optional[str] = "user", - file_ids: Optional[list[str]] = []) -> thread_type.AssistantMessageCreateResponse: - """ - 创建一条消息。 - - Args: - thread_id (str): 线程ID。 - content (str): 消息内容。 - role (Optional[str], optional): 角色,可选值为"user"或"assistant"。默认为"user"。 - file_ids (Optional[list[str]], optional): 消息中包含的文件ID列表。默认为空列表。 - - Returns: - thread_type.AssistantMessageCreateResponse: 消息创建响应对象。 - - Raises: - HttpError: 如果请求失败,则抛出HttpError异常。 - """ -``` - -- appbuilder.assistant.threads.messages.create 对应的 [Assistant API 链接](https://cloud.baidu.com/doc/AppBuilder/s/qlv0g47sk#1-%E5%88%9B%E5%BB%BAmessage) - -- appbuilder.assistant.threads.messages.create 的[请求参数](https://cloud.baidu.com/doc/AppBuilder/s/qlv0g47sk#%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0) - -```python -class AssistantMessageCreateRequest(BaseModel): - thread_id: str - role: AssistantMessageRole = Field( - default=AssistantMessageRole.USER) - content: str - file_ids: Optional[list[str]] = Field(default=[], max_length=10) -``` - -- appbuilder.assistant.threads.messages.create 的[响应参数](https://cloud.baidu.com/doc/AppBuilder/s/qlv0g47sk#%E5%93%8D%E5%BA%94%E5%8F%82%E6%95%B0) - -```python -class AssistantMessageCreateResponse(BaseModel): - id: str = "" - object: str = "" - role: AssistantMessageRole = Field( - default=AssistantMessageRole.USER) - content: Optional[list[AssistantContent]] = [] - created_at: int = 0 - thread_id: str = "" - assistant_id: Optional[str] = "" - run_id: Optional[str] = "" - file_ids: Optional[list[str]] = [] -``` - -#### appbuilder.assistant.threads.messages.list - -功能:查询指定Thread下的Message列表 - -```python -def list(self, - thread_id: str, - limit: int = 20, - order: str = "desc", - after: str = "", - before: str = "") -> thread_type.AssistantMessageListResponse: - """ - 查询指定Thread下的Message列表 - Args: - thread_id (str): 线程ID。 - limit (int, optional): 返回消息的最大数量,取值范围为[1,20]。默认为-20。 - order (Optional[str], optional): 排序方式,可选值为"asc"或"desc"。默认为"desc"。 - after (Optional[str], optional): 查询指定message_id之后创建的Message。 - before (Optional[str], optional): 查询指定message_id之前创建的Message - - Returns: - thread_type.AssistantMessageListResponse: 查询thread下的message列表响应对象。 - - Raises: - HttpError: 如果请求失败,则抛出HttpError异常。 - """ -``` - -- appbuilder.assistant.threads.messages.list 对应的 [Assistant API 链接](https://cloud.baidu.com/doc/AppBuilder/s/qlv0g47sk#2-%E6%9F%A5%E8%AF%A2thread%E4%B8%8B%E7%9A%84message%E5%88%97%E8%A1%A8) - -- appbuilder.assistant.threads.messages.list 的[请求参数](https://cloud.baidu.com/doc/AppBuilder/s/qlv0g47sk#%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0-1) - -```python -class AssistantMessageListRequest(BaseModel): - thread_id: str - limit: int = -20 - order: AssistantMessageListRole = Field( - default=AssistantMessageListRole.DESC) - after: str = "" - before: str = "" -``` - -- appbuilder.assistant.threads.messages.list 的[响应参数](https://cloud.baidu.com/doc/AppBuilder/s/qlv0g47sk#%E5%93%8D%E5%BA%94%E5%8F%82%E6%95%B0-1) - -```python -class AssistantMessageListResponse(BaseModel): - object: str = "" - data: list[AssistantMessageListResponseData] = [] - first_id: Optional[str] = "" - last_id: Optional[str] = "" - has_more: bool = False -``` -**注:**AssistantMessageListResponseData类即为AssistantMessageQueryResponse类 - -#### appbuilder.assistant.threads.messages.query - -功能:根据message_id查询指定Message的信息 - -```python -def query(self, - thread_id:str, - message_id:str) -> thread_type.AssistantMessageQueryResponse: - """ - 根据message_id查询指定Message的信息 - - Args: - thread_id (str): 线程ID - message_id (str): 消息ID - - Returns: - thread_type.AssistantMessageQueryResponse: 消息信息响应 - - Raises: - HttpError: 如果请求失败,则抛出HttpError异常。 - """ -``` - -- appbuilder.assistant.threads.messages.query 对应的 [Assistant API 链接](https://cloud.baidu.com/doc/AppBuilder/s/qlv0g47sk#3-%E6%9F%A5%E8%AF%A2%E6%8C%87%E5%AE%9Amessage) - -- appbuilder.assistant.threads.messages.query 的[请求参数](https://cloud.baidu.com/doc/AppBuilder/s/qlv0g47sk#%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0-2) - -```python -class AssistantMessageQueryRequest(BaseModel): - thread_id: str - message_id: str -``` - -- appbuilder.assistant.threads.messages.query 的[响应参数](https://cloud.baidu.com/doc/AppBuilder/s/qlv0g47sk#%E5%93%8D%E5%BA%94%E5%8F%82%E6%95%B0-2) - -```python -class AssistantMessageQueryResponse(BaseModel): - id: str = "" - object: str = "" - role: AssistantMessageRole = Field() - content: Optional[list[AssistantContent]] = [] - created_at: int = 0 - thread_id: str = "" - assistant_id: Optional[str] = "" - run_id: Optional[str] = "" - file_ids: Optional[list[str]] = [] -``` - -#### appbuilder.assistant.threads.messages.update - -功能:修改Message对象,允许content和file_ids字段 - -```python -def update(self, - thread_id: str, - message_id: str, - content: Optional[str], - file_ids: Optional[list[str]] = []) -> thread_type.AssistantMessageUpdateResponse: - """ - 修改Message对象,允许content和file_ids字段 - Args: - thread_id (str): 线程ID。 - message_id (str): 消息ID。 - content (Optional[str], optional): 消息内容。默认为空字符串。 - file_ids (Optional[list[str]], optional): 消息中包含的文件ID列表。默认为空列表。 - Returns: - thread_type.AssistantMessageUpdateResponse: 消息更新响应对象。 - Raises: - HttpError: 如果请求失败,则抛出HttpError异常。 - """ -``` - -- appbuilder.assistant.threads.messages.update 对应的 [Assistant API 链接](https://cloud.baidu.com/doc/AppBuilder/s/qlv0g47sk#4-%E4%BF%AE%E6%94%B9message%E5%AF%B9%E8%B1%A1) - -- appbuilder.assistant.threads.messages.update 的[请求参数](https://cloud.baidu.com/doc/AppBuilder/s/qlv0g47sk#%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0-3) - -```python -class AssistantMessageUpdateRequest(BaseModel): - thread_id: str - message_id: str - content: Optional[str] - file_ids: Optional[list[str]] = [] -``` - -- appbuilder.assistant.threads.messages.update 的[响应参数](https://cloud.baidu.com/doc/AppBuilder/s/qlv0g47sk#%E5%93%8D%E5%BA%94%E5%8F%82%E6%95%B0-3) - -```python -class AssistantMessageUpdateResponse(BaseModel): - id: str = "" - object: str = "" - role: AssistantMessageRole = Field(default=AssistantMessageRole.USER) - content: Optional[list[AssistantContent]] = [] - created_at: int = 0 - thread_id: str = "" - assistant_id: Optional[str] = "" - run_id: Optional[str] = "" - file_ids: Optional[list[str]] = [] -``` - -#### appbuilder.assistant.threads.messages.files - -功能:查询一个Message对象下的文件列表 - -```python -def files(self, - thread_id:str, - message_id:str, - limit:Optional[int] = 20, - order:Optional[str] = "desc", - after:Optional[str] = "", - before:Optional[str] = "") -> thread_type.AssistantMessageFilesResponse: - """ - 获取指定消息 ID 的附件信息。 - - Args: - thread_id (str): 线程 ID。 - messsages_id (str): 消息 ID。 - limit (Optional[int], optional): 返回结果的最大数量,默认为 20。 - order (Optional[str], optional): 排序方式,可选值为 "asc" 或 "desc",默认为 "desc"。 - after (Optional[str], optional): 返回结果的时间范围,只返回时间晚于该时间戳的消息附件,默认为空。 - before (Optional[str], optional): 返回结果的时间范围,只返回时间早于该时间戳的消息附件,默认为空。 - - Returns: - thread_type.AssistantMessageFilesResponse: 附件信息响应对象。 - """ -``` - -- appbuilder.assistant.threads.messages.files 对应的 [Assistant API 链接](https://cloud.baidu.com/doc/AppBuilder/s/qlv0g47sk#5-%E6%9F%A5%E8%AF%A2message%E4%B8%8B%E7%9A%84%E6%96%87%E4%BB%B6%E5%88%97%E8%A1%A8) - -- appbuilder.assistant.threads.messages.files 的[请求参数](https://cloud.baidu.com/doc/AppBuilder/s/qlv0g47sk#%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0-4) - -```python -class AssistantMessageFilesRequest(BaseModel): - thread_id: str - message_id: str - limit: int = -20 - order : AssistantMessageListRole = Field( - default=AssistantMessageListRole.DESC) - after: str = "" - before: str = "" -``` - -- appbuilder.assistant.threads.messages.files 的[响应参数](https://cloud.baidu.com/doc/AppBuilder/s/qlv0g47sk#%E5%93%8D%E5%BA%94%E5%8F%82%E6%95%B0-4) - -```python -class AssistantMessageFilesResponse(BaseModel): - object: str = "" - data: list[AssistantContentFilesData] = [] - first_id: Optional[str] = "" - last_id: Optional[str] = "" - has_more: bool = False -``` -**注:**AssistantContentFilesData类即为AssistantFilesResponse类 - - -## appbuilder.assistant.threads.runs - -#### appbuilder.assistant.threads.runs.run - -功能:同步非流式运行方法,使用指定的assistant与thread - - -```python -def run(self, - assistant_id: str, - thread_id: Optional[str] = "", - thread: Optional[thread_type.AssistantThread] = None, - model: Optional[str] = "ERNIE-4.0-8K", - response_format: Optional[str] = "text", - instructions: Optional[str] = "", - thought_instructions: Optional[str] = "", - chat_instructions: Optional[str] = "", - tools: Optional[list[assistant_type.AssistantTool]] = [], - metadata: Optional[dict] = {}, - tool_output: Optional[thread_type.ToolOutput] = None, - model_parameters: Optional[public_type.AssistantModelParameters] = None, - user_info: Optional[public_type.AssistantUserInfo] = None, - user_loc: Optional[public_type.AssistantUserLoc] = None, - ) -> thread_type.RunResult: - """ - Args: - assistant_id (str): 助手id - thread_id (Optional[str], optional): 对话id. Defaults to "". - thread (Optional[thread_type.AssistantThread], optional): 对话信息. Defaults to None. - model (Optional[str], optional): 模型名称. Defaults to "ERNIE-4.0-8K". - response_format (Optional[str], optional): 返回格式. Defaults to "text". - instructions (Optional[str], optional): 指令信息. Defaults to "". - thought_instructions (Optional[str], optional): 思考指令信息. Defaults to "". - chat_instructions (Optional[str], optional): 闲聊指令信息. Defaults to "". - tools (Optional[list[assistant_type.AssistantTool]], optional): 工具列表. Defaults to []. - metadata (Optional[dict], optional): 元数据. Defaults to {}. - tool_output (Optional[thread_type.ToolOutput], optional): 工具输出. Defaults to None. - model_parameters (Optional[public_type.AssistantModelParameters], optional): 模型运行参数. Defaults to None. - user_info (Optional[public_type.AssistantUserInfo], optional): 用户身份信息. Defaults to None. - user_loc (Optional[public_type.AssistantUserLoc], optional): 用户定位信息. Defaults to None. - Returns: - thread_type.RunResult: 运行结果 - - Raises: - ValueError: thread_id和thread不能同时为空,model_parameters的各个参数不在规定范围内 - - Note: - 1. 如果thread_id没有传,则thread必须要传值 - 2. 如果这里不传值,thread_id查出来的历史对话,最后一条消息的role必须为user - 3. 如果这里传值,则需要保证thread_id查出来的历史对话 + 本轮追加的thread对话,最后一条消息的role必须为user - """ -``` - -- appbuilder.assistant.threads.runs.run 对应的 [Assistant API 链接](https://cloud.baidu.com/doc/AppBuilder/s/dlv0g4x9m#1-%E8%BF%90%E8%A1%8C) - -- appbuilder.assistant.threads..runs.run 的[请求参数](https://cloud.baidu.com/doc/AppBuilder/s/dlv0g4x9m#%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0) - -```python -class AssistantRunRequest(BaseModel): - thread_id: Optional[str] = Field(default="") - model: str = Field(default="ERNIE-4.0-8K") - assistant_id: Optional[str] = Field(default="") - metadata: Optional[dict] = Field(default={}, max_length=16) - response_format: ResponseFormat = Field(default=ResponseFormat.TEXT) - instructions: Optional[str] = Field(default="", max_length=4096) - thought_instructions: Optional[str] = Field(default="", max_length=4096) - chat_instructions: Optional[str] = Field(default="", max_length=4096) - stream: Optional[bool] = False - model_parameters: Optional[AssistantModelParameters] = AssistantModelParameters() - class Config: - extra = "forbid" - protected_namespaces = () - thread: Optional[AssistantThread] = None - tools: Optional[list[AssistantTool]] = [] - tool_output: Optional[ToolOutput] = None - user_info: Optional[AssistantUserInfo] = None - user_loc: Optional[AssistantUserLoc] = None -``` - -- appbuilder.assistant.threads..runs.run 的[流式响应参数](https://cloud.baidu.com/doc/AppBuilder/s/dlv0g4x9m#%E6%B5%81%E5%BC%8F%E5%93%8D%E5%BA%94%E5%8F%82%E6%95%B0) - -- appbuilder.assistant.threads..runs.run 的[非流式响应参数](https://cloud.baidu.com/doc/AppBuilder/s/dlv0g4x9m#%E9%9D%9E%E6%B5%81%E5%BC%8F%E5%93%8D%E5%BA%94%E5%8F%82%E6%95%B0) - -```python -class RunResult(BaseModel): - id: str = "" - object: str = "" - assistant_id: str = "" - thread_id: str = "" - - model: str = "" - instructions: str = "" - thought_instructions: str = "" - chat_instructions: str = "" - tools: Optional[list[AssistantTool]] = None - file_ids: Optional[list[str]] = None - - status: str = "" - required_action: Optional[RequiredAction] = None - last_error: Optional[LastError] = None - final_answer: Optional[FinalAnswer] = None - created_at: int = 0 - started_at: int = 0 - expired_at: int = 0 - cancelled_at: int = 0 - failed_at: int = 0 - completed_at: int = 0 -``` - -#### appbuilder.assistant.threads.runs.stream_run - -功能:流式运行方法,使用指定的assistant与thread - -```python -def stream_run(self, - assistant_id: str, - thread_id: Optional[str] = "", - thread: Optional[thread_type.AssistantThread] = None, - model: Optional[str] = "ERNIE-4.0-8K", - response_format: Optional[str] = "text", - instructions: Optional[str] = "", - thought_instructions: Optional[str] = "", - chat_instructions: Optional[str] = "", - tools: Optional[list[assistant_type.AssistantTool]] = [], - metadata: Optional[dict] = {}, - tool_output: Optional[thread_type.ToolOutput] = None, - model_parameters: Optional[public_type.AssistantModelParameters] = None, - user_info: Optional[public_type.AssistantUserInfo] = None, - user_loc: Optional[public_type.AssistantUserLoc] = None, - ) -> Union[thread_type.StreamRunStatus, thread_type.StreamRunMessage, None]: - """ - 启动一个流式运行的对话,用于处理对话流中的消息。 - - Args: - assistant_id (str): 助理ID。 - thread_id (Optional[str], optional): 线程ID,用于恢复历史对话。默认为空字符串。 - thread (Optional[thread_type.AssistantThread], optional): 线程对象,用于恢复历史对话。默认为None。 - model (Optional[str], optional): 使用的模型名称。默认为"ERNIE-4.0-8K"。 - response_format (Optional[str], optional): 响应格式,支持"text"和"json"两种格式。默认为"text"。 - instructions (Optional[str], optional): 指令文本。默认为空字符串。 - thought_instructions (Optional[str], optional): 思考指令文本。默认为空字符串。 - chat_instructions (Optional[str], optional): 聊天指令文本。默认为空字符串。 - tools (Optional[list[assistant_type.AssistantTool]], optional): 使用的工具列表。默认为空列表。 - metadata (Optional[dict], optional): 元数据字典。默认为空字典。 - tool_output (Optional[thread_type.ToolOutput], optional): 工具输出对象。默认为None。 - model_parameters (Optional[public_type.AssistantModelParameters], optional): 模型参数对象。默认为None。 - - Returns: - Union[thread_type.StreamRunStatus, thread_type.StreamRunMessage, None]: 返回一个迭代器,每次迭代返回一个处理结果对象,可能是 StreamRunStatus 或 StreamRunMessage。 - - Raises: - ValueError: 如果thread_id和thread参数同时为空,则会引发ValueError异常。 - - Note: - 1. 如果thread_id没有传,则thread必须要传值。 - 2. 如果这里不传值,thread_id查出来的历史对话,最后一条消息的role必须为user。 - 3. 如果这里传值,则需要保证thread_id查出来的历史对话 + 本轮追加的thread对话,最后一条消息的role必须为user。 - """ -``` - -#### appbuilder.assistant.threads.runs.submit_tool_outputs - -功能:在流式运行中,提交本地FunctionCall的运行结果 - -```python -def submit_tool_outputs(self, - run_id: str, - thread_id: str, - tool_outputs: Optional[list[thread_type.ToolOutput]]) -> thread_type.RunResult: - """ - 向服务端提交工具输出 - - Args: - run_id (str): 运行ID - thread_id (str): 线程ID - tool_outputs (Optional[list[thread_type.ToolOutput]]): 工具输出列表,可选 - - Returns: - thread_type.RunResult: 运行结果 - - """ -``` - -- appbuilder.assistant.threads.runs.submit_tool_outputs 对应的 [Assistant API 链接](https://cloud.baidu.com/doc/AppBuilder/s/dlv0g4x9m#2-%E6%B5%81%E5%BC%8F%E8%B0%83%E7%94%A8%E6%97%B6%E6%8F%90%E4%BA%A4%E5%B7%A5%E5%85%B7%E8%BE%93%E5%87%BA) - -- appbuilder.assistant.threads..runs.submit_tool_outputs 的[请求参数](https://cloud.baidu.com/doc/AppBuilder/s/dlv0g4x9m#%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0-1) - -```python -class AssistantSubmitToolOutputsRequest(BaseModel): - thread_id: str = Field(default="", min_length=1) - run_id: str = Field(default="", min_length=1) - tool_outputs: Optional[list[ToolOutput]] = Field(default=[], min_length=1) -``` - -- appbuilder.assistant.threads..runs.submit_tool_outputs 的[响应参数](https://cloud.baidu.com/doc/AppBuilder/s/dlv0g4x9m#%E5%93%8D%E5%BA%94%E8%AF%B4%E6%98%8E-1) - - -```python -class RunResult(BaseModel): - id: str = "" - object: str = "" - assistant_id: str = "" - thread_id: str = "" - - model: str = "" - instructions: str = "" - thought_instructions: str = "" - chat_instructions: str = "" - tools: Optional[list[AssistantTool]] = None - file_ids: Optional[list[str]] = None - - status: str = "" - required_action: Optional[RequiredAction] = None - last_error: Optional[LastError] = None - final_answer: Optional[FinalAnswer] = None - created_at: int = 0 - started_at: int = 0 - expired_at: int = 0 - cancelled_at: int = 0 - failed_at: int = 0 - completed_at: int = 0 -``` - -#### appbuilder.assistant.threads.runs.cancel - -功能:在流式运行过程,中断该thread.stream_run的运行 - -```python -def cancel(self, run_id: str, thread_id: str) -> thread_type.RunResult: - """ - 取消指定线程的运行 - - Args: - run_id (str): 运行的ID - thread_id (str): 线程的ID - - Returns: - thread_type.RunResult: 取消运行的结果 - - """ -``` - -- appbuilder.assistant.threads.runs.cancel 对应的 [Assistant API 链接](https://cloud.baidu.com/doc/AppBuilder/s/dlv0g4x9m#3-%E6%B5%81%E5%BC%8F%E8%B0%83%E7%94%A8%E6%97%B6%E5%8F%96%E6%B6%88%E8%BF%90%E8%A1%8C) - -- appbuilder.assistant.threads..runs.cancel 的[请求参数](https://cloud.baidu.com/doc/AppBuilder/s/dlv0g4x9m#%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0-2) - -```python -class AssistantRunCancelRequest(BaseModel): - thread_id: str = Field(default="", min_length=1) - run_id: str = Field(default="", min_length=1) - -``` - -- appbuilder.assistant.threads..runs.cancel 的[响应参数](https://cloud.baidu.com/doc/AppBuilder/s/dlv0g4x9m#%E5%93%8D%E5%BA%94%E5%8F%82%E6%95%B0-1) - - -```python -class RunResult(BaseModel): - id: str = "" - object: str = "" - assistant_id: str = "" - thread_id: str = "" - - model: str = "" - instructions: str = "" - thought_instructions: str = "" - chat_instructions: str = "" - tools: Optional[list[AssistantTool]] = None - file_ids: Optional[list[str]] = None - - status: str = "" - required_action: Optional[RequiredAction] = None - last_error: Optional[LastError] = None - final_answer: Optional[FinalAnswer] = None - created_at: int = 0 - started_at: int = 0 - expired_at: int = 0 - cancelled_at: int = 0 - failed_at: int = 0 - completed_at: int = 0 - -``` - - -## appbuilder.assistant公共类 - -- AssistantTool - -```python -class AssistantTool(BaseModel): - """ - 表示助理工具的模型。 - - Attributes: - type (str): 工具的类型,默认为 'function'。 - function (AssistantFunction): 功能的实例。 - """ - type: str = 'function' - function: AssistantFunction = None -``` - -- AssistantContent - -```python -class AssistantContent(BaseModel): - """ - 表示助理内容的模型。 - - Attributes: - type (str): 内容类型,默认为 "text"。 - text (Optional[AssistantText]): 文本内容的实例,默认为None。 - """ - type: str = "text" - text: Optional[AssistantText] = None -``` - -- AssistantText - -```python -class AssistantText(BaseModel): - """ - 表示助理文本内容的模型。 - - Attributes: - value (str): 文本的值。 - annotations (Optional[list[str]]): 文本的注解列表,默认为None。 - """ - value: str = "" - annotations: Optional[list[str]] = None -``` - -- ResponseFormat - -```python -class ResponseFormat(str, Enum): - """ - 表示响应格式的枚举类型。 - - Values: - TEXT: 文本格式。 - JSON_OBJECT: JSON对象格式。 - """ - TEXT = 'text' - JSON_OBJECT = 'json_object' -``` - -- AssistantModelParameters - - AssistantThoughtParameters - - AssistantChatParameters - -```python -class AssistantModelParameters(BaseModel): - """ - 表示助理模型的参数的模型。 - Attributes: - chat_parameters (Optional[AssistantChatParameters]): 聊天参数的实例,默认为None。 - thought_parameters (Optional[AssistantThoughtParameters]): 思考参数的实例,默认为None。 - """ - chat_parameters: Optional[AssistantChatParameters] = AssistantChatParameters() - thought_parameters: Optional[AssistantThoughtParameters] = AssistantThoughtParameters() - - -class AssistantThoughtParameters(BaseModel): - """ - 表示助理思考参数的模型。 - Attributes: - temperature (Optional[float]): 采样温度,较高的数值会使输出更随机。取值范围严格大于0,小于等于1,默认为0.01。 - top_p (Optional[float]): top_p,核采样方法的概率阈值,影响输出文本的多样性,较高的数值会使输出的文本更加多样性。取值范围大于等于0,小于等于1,默认为0。 - penalty_score (Optional[float]): 惩罚分数,减少重复生成的现象,值越大表示惩罚越大。取值范围大于等于1,小于等于2,默认为1.0。 - """ - temperature: Optional[float] = 0.01 - top_p: Optional[float] = 0 - penalty_score: Optional[float] = 1.0 - - -class AssistantChatParameters(BaseModel): - """ - 表示助理聊天参数的模型。 - Attributes: - temperature (Optional[float]): 采样温度,较高的数值会使输出更随机。取值范围严格大于0,小于等于1,默认为0.8。 - top_p (Optional[float]): top_p,核采样方法的概率阈值,影响输出文本的多样性,较高的数值会使输出的文本更加多样性。取值范围大于等于0,小于等于1,默认为0.8。 - penalty_score (Optional[float]): 惩罚分数,减少重复生成的现象,值越大表示惩罚越大。取值范围大于等于1,小于等于2,默认为1.0。 - """ - temperature: Optional[float] = 0.8 - top_p: Optional[float] = 0.8 - penalty_score: Optional[float] = 1.0 -``` - - --AssistantUserInfo - -```python -class AssistantUserInfo(BaseModel): - """ - 表示用户信息。 - Attributes: - id (Optional[str]): 用户ID,默认为None。 - name (Optional[str]): 用户名称,默认为None。 - nickname (Optional[str]): 用户昵称,默认为None。 - watermark (Optional[str]): 用户水印,默认为None。 - intro (Optional[str]): 用户简介,默认为None。 - baidu_id (Optional[str]): 用户百度ID,默认为None。 - """ - id:Optional[str] = None - name:Optional[str] = None - nickname:Optional[str] = None - watermark:Optional[str] = None - intro:Optional[str] = None - baidu_id: Optional[str] = None -``` - -- AssistantUserLoc - -```python -class AssistantUserLoc(BaseModel): - """ - 表示用户位置信息。 - Attributes: - loc (Optional[str]): 用户当前的地理位置信息,使用json格式描述 - uip (Optional[str]): 用户的ipv4地址 - uipv6 (Optional[str]): 用户的ipv6地址 - """ - loc:Optional[str] = None - uip:Optional[str] = None - uipv6:Optional[str] = None - -``` \ No newline at end of file diff --git a/docs/basic_module/dataset.md b/docs/basic_module/dataset.md deleted file mode 100644 index 9bf7304dc..000000000 --- a/docs/basic_module/dataset.md +++ /dev/null @@ -1,231 +0,0 @@ -# 知识库组件(Dataset)(即将下线) - -## 简介 - -知识库组件(Dataset)是对线上知识库操作的组件,可以通过SDK实现创建知识库、添加知识文档、查询知识库文档、删除知识文档等操作,可在平台console中查看结果。 - -### 功能介绍 - -对console端知识库进行操作,可以通过SDK实现创建知识库、添加知识文档、查询知识库文档、删除知识文档等操作,可在平台console中查看结果。 - -### 特色优势 - -和console端知识库操作一致,可实现快速创建、查询、删除等操作。 - -### 应用场景 - -通过SDK代码实现console端知识库操作。 - -## 基本用法 - -### Python - -#### 组件初始化参数 - -| 参数名称 | 参数类型 | 描述 | 示例值 | -|------------|--------|---------|------------| -| dataset_id | string | 线上数据集ID | "正确的数据集ID" | - -#### 创建知识库调用参数 - -| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | -|--------------|--------|------|--------|--------------| -| dataset_name | String | 是 | 知识库的名字 | "my_dataset" | - -#### 创建知识库响应参数 - -| 参数名称 | 参数类型 | 描述 | 示例值 | -|--------|---------|-----------|-----------------------------------------------------------------------------------| -| result | Dataset | dataset实例 | Dataset(dataset_id=2dae2091-99dc-47dd-8600-ff7c4b3ed93d, dataset_name=my_dataset) | - -响应示例: - -``` -Dataset(dataset_id=2dae2091-99dc-47dd-8600-ff7c4b3ed93d, dataset_name=my_dataset) -``` - -#### 添加文档调用参数 - -| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | -|------------------------|--------------|------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------| -| file_paths | List[String] | 是 | 文档路径列表 | ["./path1", "./path2"] | -| is_custom_process_rule | bool | 否 | 是否使用自定义文档处理策略 | True | -| custom_process_rule | Dict | 否 | 自定义文档处理策略,在is_custom_process_rule为True时需要设置,separators文本切分符,支持这几种[ , , "?", , "!", "?", "……"],target_length是文本切片片段长度,取值范围[300, 800],overlap_rate是文本片段重叠率,取值范围[0, 0.3] | {"separators":["。", ","], "target_length": 300, "overlap_rate": 0.3} | -| is_enhanced | bool | 否 | 是否开启知识增强,在检索问答时通过知识点来索引到对应的切片,大模型根据切片内容生成答案,开启知识增强会调用大模型抽取更加丰富的知识点,增加切片的召回率 | False | - -#### 添加文档响应参数 - -| 参数名称 | 参数类型 | 描述 | 示例值 | -|--------|----------------------|----------|-------------------------------------------------------------------------------------------------------------------------------| -| result | AddDocumentsResponse | 添加文档响应信息 | AddDocumentsResponse(dataset_id='4437e170-876a-4634-9469-2ff6b76584e7' document_ids=['a279f3f2-e779-45c8-85ba-19f63c1c1316']) | - -响应示例: - -``` -AddDocumentsResponse(dataset_id='4437e170-876a-4634-9469-2ff6b76584e7' document_ids=['a279f3f2-e779-45c8-85ba-19f63c1c1316']) -``` - -#### 获取文档列表调用参数 - -| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | -|---------|--------|------|----------|------| -| page | int | 是 | 页码,从1开始 | 1 | -| limit | int | 是 | 每页包含文档数量 | 10 | -| keyword | string | 否 | 关键词匹配 | "ai" | - -#### 获取文档列表响应参数 - -| 参数名称 | 参数类型 | 描述 | 示例值 | -|--------|----------------------|--------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| result | DocumentListResponse | 文档列表对象 | DocumentListResponse(data=[FileInfo(id='a279f3f2-e779-45c8-85ba-19f63c1c1316', name='test.pdf', created_at=1706515108, indexing_status='parsing', error=None, enabled=True, disabled_at=None, disabled_by=None, display_status='处理中', word_count=0)], has_more=False, limit=10, total=1, page=1) | - -响应示例: - -``` -DocumentListResponse(data=[FileInfo(id='a279f3f2-e779-45c8-85ba-19f63c1c1316', name='test.pdf', created_at=1706515108, indexing_status='parsing', error=None, enabled=True, disabled_at=None, disabled_by=None, display_status='处理中', word_count=0)], has_more=False, limit=10, total=1, page=1) -``` - -#### 删除文档调用参数 - -| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | -|--------------|--------------|------|--------|----------------| -| document_ids | List[String] | 是 | 文档id列表 | ["1234567890"] | - -#### 删除文档响应参数 - -无 - -#### 示例代码 - -下面是示例代码,相应的操作结果可在平台console中查看,与console端同步 - -```python -import appbuilder -import os -import requests - -# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 -# 设置环境变量 -os.environ["APPBUILDER_TOKEN"] = "..." - -# 初始化已有线上知识库,dataset_id 可在平台console中查看获取,详情可在下方初始化参数部分查看 -dataset_id = "..." -dataset = appbuilder.console.Dataset(dataset_id) -# 或创建全新知识库 -dataset = appbuilder.console.Dataset.create_dataset("my_dataset") - -file_url = "https://agi-dev-platform-bos.bj.bcebos.com/ut_appbuilder/test.pdf?authorization=bce-auth-v1/e464e6f951124fdbb2410c590ef9ed2f/2024-01-25T12%3A56%3A15Z/-1/host/b54178fea9be115eafa2a8589aeadfcfaeba20d726f434f871741d4a6cb0c70d" -file_data = requests.get(file_url).content -file_path = "./test.pdf" # 待解析的文件路径 -with open(file_path, "wb") as f: - f.write(file_data) - -# 上传文档到知识库 -file_paths = ["./test.pdf"] -document_infos = dataset.add_documents(file_paths) -print(document_infos) - -# 获取第一页的文档列表, 每页10条 -document_list = dataset.get_documents(1, 10) -print(document_list) - -# 删除一个文档 -document_ids = [document_infos.document_ids[0]] -dataset.delete_documents(document_ids) -``` - -### Java - -#### 组件初始化参数 - -| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | -|--------|-----------|------|---------|-----| -| datasetId | String | 否|线上数据集ID,可通过createDataset()创建数据集 | "正确的数据集ID" | - -#### 示例代码 -```java -class DatasetDemo { - public static void main(String[] args) throws IOException, AppBuilderServerException { - // 填写自己的APPBUILDER_TOKEN - System.setProperty("APPBUILDER_TOKEN", "填写秘钥"); - - Dataset ds = new Dataset(); - // 创建数据集 - String datasetId = ds.createDataset("java创建数据集"); - System.out.println("datasetId: " + datasetId); - - // 上传文档,填写正确的文件路径 - String filePath = "src/test/java/中秋节.docx"; - String[] documentIds = ds.addDocuments(new ArrayList<>(Collections.singletonList(filePath)), false, null, false); - System.out.println("documentIds: " + Arrays.toString(documentIds)); - - // 获取文档列表 - DocumentListResponse resp = ds.getDocumentList(1, 20, ""); - System.out.println("document total num: " + resp.getResult().getTotal()); - for (DocumentListData data : resp.getResult().getData()) { - System.out.println("documentList: " + data.getName()); - } - // 删除文档 - ds.deleteDocuments(documentIds); - } -} -``` - -### Go - -#### 组件初始化参数 - -| 参数名称 | 参数类型 | 描述 | 示例值 | -|--------|-----------|---------|-----| -| config | SDKConfig | SDK配置信息 | | - -#### 示例代码 - -```Go -package main - -import ( - "fmt" - "os" - - "github.com/baidubce/app-builder/go/appbuilder" - -) - -func main() { - // 设置APPBUILDER_TOKEN、GATEWAY_URL环境变量 - os.Setenv("APPBUILDER_TOKEN", "请设置正确的应用密钥") - // 默认可不填,默认值是 https://appbuilder.baidu.com - os.Setenv("GATEWAY_URL", "") - config, err := appbuilder.NewSDKConfig("", "") - if err != nil { - fmt.Println("new config failed: ", err) - return - } - // 初始化dataset实例 - dataset, _ := appbuilder.NewDataset(config) - // 创建dataset - datasetID, err := dataset.Create("name") - if err != nil { - fmt.Println("create dataset failed: ", err) - return - } - // 上传文档 - documentID, err := dataset.UploadLocalFile(datasetID, "/Users/zhangxiaoyu15/Desktop/cv.pdf") - if err != nil { - fmt.Println("upload file failed: ", err) - return - } - // 获取数据集下第一页不超过10篇文档 - _, err = dataset.ListDocument(datasetID, 1, 10, "") - if err != nil { - fmt.Println("list document failed: ", err) - return - } - // 删除文档 - if err := dataset.DeleteDocument(datasetID, documentID); err != nil { - fmt.Println("delete document failed: ", err) - return - } -} -``` \ No newline at end of file diff --git a/docs/service/README.md b/docs/service/README.md deleted file mode 100644 index bd70db831..000000000 --- a/docs/service/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# 服务化部署 - -本文档目录包含以下内容 - -- [基于Flask的API访问](https://github.com/baidubce/app-builder/blob/master/docs/service/flask.md) -- [基于Chainlit的交互式前端页面](https://github.com/baidubce/app-builder/blob/master/docs/service/chainlit.md) -- [公有云部署](https://github.com/baidubce/app-builder/blob/master/docs/service/cloud.md) \ No newline at end of file diff --git a/docs/sphinx_md/appbuilder.core.assistant.threads.messages.md b/docs/sphinx_md/appbuilder.core.assistant.threads.messages.md deleted file mode 100644 index c9108fe23..000000000 --- a/docs/sphinx_md/appbuilder.core.assistant.threads.messages.md +++ /dev/null @@ -1,88 +0,0 @@ -# appbuilder.core.assistant.threads.messages package - -## Submodules - -## appbuilder.core.assistant.threads.messages.messages module - -### *class* appbuilder.core.assistant.threads.messages.messages.Messages - -基类:`object` - -#### create(thread_id: str, content: str, role: str | None = 'user', file_ids: list[str] | None = []) → AssistantMessageCreateResponse - -创建一条消息。 - -* **参数:** - * **thread_id** (*str*) – 线程ID。 - * **content** (*str*) – 消息内容。 - * **role** (*Optional* *[**str* *]* *,* *optional*) – 角色,可选值为”user”或”assistant”。默认为”user”。 - * **file_ids** (*Optional* *[**list* *[**str* *]* *]* *,* *optional*) – 消息中包含的文件ID列表。默认为空列表。 -* **返回:** - 消息创建响应对象。 -* **返回类型:** - thread_type.AssistantMessageCreateResponse -* **抛出:** - **HttpError** – 如果请求失败,则抛出HttpError异常。 - -#### files(thread_id: str, message_id: str, limit: int | None = 20, order: str | None = 'desc', after: str | None = '', before: str | None = '') → AssistantMessageFilesResponse - -获取指定消息 ID 的附件信息。 - -* **参数:** - * **thread_id** (*str*) – 线程 ID。 - * **messsages_id** (*str*) – 消息 ID。 - * **limit** (*Optional* *[**int* *]* *,* *optional*) – 返回结果的最大数量,默认为 20。 - * **order** (*Optional* *[**str* *]* *,* *optional*) – 排序方式,可选值为 “asc” 或 “desc”,默认为 “desc”。 - * **after** (*Optional* *[**str* *]* *,* *optional*) – 返回结果的时间范围,只返回时间晚于该时间戳的消息附件,默认为空。 - * **before** (*Optional* *[**str* *]* *,* *optional*) – 返回结果的时间范围,只返回时间早于该时间戳的消息附件,默认为空。 -* **返回:** - 附件信息响应对象。 -* **返回类型:** - thread_type.AssistantMessageFilesResponse - -#### list(thread_id: str, limit: int = 20, order: str = 'desc', after: str = '', before: str = '') → AssistantMessageListResponse - -查询指定Thread下的Message列表 - -* **参数:** - * **thread_id** (*str*) – 线程ID。 - * **limit** (*int* *,* *optional*) – 返回消息的最大数量,取值范围为[1,20]。默认为-20。 - * **order** (*Optional* *[**str* *]* *,* *optional*) – 排序方式,可选值为”asc”或”desc”。默认为”desc”。 - * **after** (*Optional* *[**str* *]* *,* *optional*) – 查询指定message_id之后创建的Message。 - * **before** (*Optional* *[**str* *]* *,* *optional*) – 查询指定message_id之前创建的Message -* **返回:** - 查询thread下的message列表响应对象。 -* **返回类型:** - thread_type.AssistantMessageListResponse -* **抛出:** - **HttpError** – 如果请求失败,则抛出HttpError异常。 - -#### query(thread_id: str, message_id: str) → AssistantMessageQueryResponse - -根据message_id查询指定Message的信息 - -* **参数:** - * **thread_id** (*str*) – 线程ID - * **message_id** (*str*) – 消息ID -* **返回:** - 消息信息响应 -* **返回类型:** - thread_type.AssistantMessageQueryResponse -* **抛出:** - **HttpError** – 如果请求失败,则抛出HttpError异常。 - -#### update(thread_id: str, message_id: str, content: str | None, file_ids: list[str] | None = []) → AssistantMessageUpdateResponse - -修改Message对象,允许content和file_ids字段 - -* **参数:** - * **thread_id** (*str*) – 线程ID。 - * **message_id** (*str*) – 消息ID。 - * **content** (*Optional* *[**str* *]* *,* *optional*) – 消息内容。默认为空字符串。 - * **file_ids** (*Optional* *[**list* *[**str* *]* *]* *,* *optional*) – 消息中包含的文件ID列表。默认为空列表。 -* **返回:** - 消息更新响应对象。 -* **返回类型:** - thread_type.AssistantMessageUpdateResponse -* **抛出:** - **HttpError** – 如果请求失败,则抛出HttpError异常。 diff --git a/docs/trace/README.md b/docs/trace/README.md deleted file mode 100644 index 9d08343a1..000000000 --- a/docs/trace/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Appbuilder Trace 文档 - -本文档目录包含以下内容 - -- [Appbuilder Trace跟踪功能基本用法](https://github.com/baidubce/app-builder/blob/master/docs/trace/basic.md) -- [Phoneix可视化软件的进阶用法](https://github.com/baidubce/app-builder/blob/master/docs/trace/phoenix_method.md) -- [Appbuilder Trace跟踪功能示例](https://github.com/baidubce/app-builder/blob/master/cookbooks/appbuilder_trace/trace.ipynb) \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 000000000..50be6ebf5 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,123 @@ +site_name: Appbuilder-SDK + +# 网站页面结构和导航配置 +nav: + - 首页: README.md + - 快速上手: + - 开始你的第一个AI原生应用: + - 安装: QuickStart/StartFirstAINativeApplication/install.md + - 快速开始: QuickStart/StartFirstAINativeApplication/README.md + - 产业实践应用范例: + - SDK使用示例: QuickStart/ExamplesOfIndustrialPracticeApplications/README.md + - SDK当前支持的编程语言: QuickStart/CurrentlySupportedProgrammingLanguages/README.md + - 基础: + - 模型: + - 获取模型列表: BasisModule/Model/get_model_list.md + - 组件: + - 语义匹配(Matching): BasisModule/Components/matching/README.md + - 表格文字识别 (TableOCR): BasisModule/Components/table_ocr/README.md + - 文件生成PPT(PPTGenerationFromFile): BasisModule/Components/ppt_generation_from_file/README.md + - GBI 选表: BasisModule/Components/gbi/select_table/README.md + - GBI 问表: BasisModule/Components/gbi/nl2sql/README.md + - Excel转图表(Excel2Figure): BasisModule/Components/excel2figure/README.md + - 向量计算(Embedding): BasisModule/Components/embeddings/README.md + - 论文生成PPT(PPTGenerationFromPaper): BasisModule/Components/ppt_generation_from_paper/README.md + - 地标识别(LandmarkRecognition): BasisModule/Components/landmark_recognize/README.md + - 手写文字识别 (HandwriteOCR): BasisModule/Components/handwrite_ocr/README.md + - 通用文字识别-高精度版(GeneralOCR): BasisModule/Components/general_ocr/README.md + - 文本翻译-通用版(Translation): BasisModule/Components/translate/README.md + - 百度搜索RAG(deprecate): BasisModule/Components/rag_with_baidu_search/README.md + - 文档切分(DocSplitter): BasisModule/Components/doc_splitter/README.md + - 表格抽取组件(ExtractTableFromDoc): BasisModule/Components/extract_table/README.md + - 向量检索: BasisModule/Components/retriever/README.md + - 文本精排(Reranker): BasisModule/Components/retriever/reranker/README.md + - 向量检索-VectorDB(BaiduVectorDBRetriever): BasisModule/Components/retriever/baidu_vdb/README.md + - 向量检索-BES(BaiduElasticSearchRetriever): BasisModule/Components/retriever/bes/README.md + - 通用物体和场景识别-高级版(ObjectRecognition): BasisModule/Components/object_recognize/README.md + - 文档解析(DocParser): BasisModule/Components/doc_parser/README.md + - 文档格式转换 (DocFormatConverter): BasisModule/Components/doc_format_converter/README.md + - 身份证混贴识别(MixCardOCR): BasisModule/Components/mix_card_ocr/README.md + - 短语音识别-极速版 (Automatic Speech Recognition): BasisModule/Components/asr/README.md + - 文生图 (Text2Image): BasisModule/Components/text_to_image/README.md + - 长文档内容理解(DocumentUnderstanding): BasisModule/Components/document_understanding/README.md + - 短文本在线合成(TTS): BasisModule/Components/tts/README.md + - 植物识别(PlantRecognition): BasisModule/Components/plant_recognize/README.md + - 树图 (TreeMind): BasisModule/Components/tree_mind/README.md + - 菜品识别(DishRecognition): BasisModule/Components/dish_recognize/README.md + - 指令生成PPT(PPTGenerationFromInstruction): BasisModule/Components/ppt_generation_from_instruction/README.md + - 动物识别 (AnimalRecognition): BasisModule/Components/animal_recognize/README.md + - 百度搜索RAG_PRO: BasisModule/Components/rag_with_baidu_search_pro/README.md + - 二维码识别 (QRcodeOCR): BasisModule/Components/qrcode_ocr/README.md + - 复杂Query判定(IsComplexQuery): BasisModule/Components/llms/is_complex_query/README.md + - 空应用(Playground): BasisModule/Components/llms/playground/README.md + - 口语化Query生成(Oral Query Generation): BasisModule/Components/llms/oral_query_generation/README.md + - 阅读理解问答(MRC): BasisModule/Components/llms/mrc/README.md + - 多轮改写 (QueryRewrite): BasisModule/Components/llms/query_rewrite/README.md + - 风格写作(StyleWriting): BasisModule/Components/llms/style_writing/README.md + - 复杂Query分解(QueryDecomposition): BasisModule/Components/llms/query_decomposition/README.md + - 会话小结(DialogSummary): BasisModule/Components/llms/dialog_summary/README.md + - 幻觉检测(Hallucination Detection): BasisModule/Components/llms/hallucination_detection/README.md + - 相似问生成(SimilarQuestion): BasisModule/Components/llms/similar_question/README.md + - 标签抽取(TagExtraction): BasisModule/Components/llms/tag_extraction/README.md + - 问答对挖掘(QAPairMining): BasisModule/Components/llms/qa_pair_mining/README.md + - 风格转写 (StyleRewrite): BasisModule/Components/llms/style_rewrite/README.md + - 自然语言转pandas (nl2pandas): BasisModule/Components/llms/nl2pandas/README.md + - 文档矫正增强 (DocCropEnhance): BasisModule/Components/doc_crop_enhance/README.md + - 图像内容理解 (ImageUnderstand): BasisModule/Components/image_understand/README.md + - 监控: + - TRACE基础功能: BasisModule/Trace/basic.md + - TRACE拓展功能: BasisModule/Trace/phoenix_method.md + - 部署: + - 交互式前端部署: BasisModule/Deployment/AgentChainlit.md + - 公有云部署: BasisModule/Deployment/cloud.md + - API 访问: BasisModule/Deployment/flask.md + - AgentRuntime: BasisModule/Deployment/agentruntime.md + - UserSession: BasisModule/Deployment/usersession.md + - 平台: + - 应用: + - AppBuilderClient组件: BasisModule/Platform/Application/appbuilder_client.md + - 获取AppBuilder已发布的应用列表: BasisModule/Platform/Application/get_app_list.md + - 知识库: + - 知识库组件: BasisModule/Platform/KnowledgeBase/knowledgebase.md + - 自定义组件: + - 基础能力组件: BasisModule/Platform/CustomComponents/components.md + - 应用: + # - Agent: + # - 基础知识: + # - 使用官方组件: + # - ToolCall: + # - ToolChoice: + # - 使用异步和流式加速客户端调用: + - RAG: + - 基础知识: Application/RAG/BasicKnowledge/rag.md + # - 知识库管理: + # - Reference信息处理: + # - Workflow: + # - 基础知识: + # - 从零使用Workflow组装一个RAG应用: + # - 从零使用Workflow组装一个Agent应用: + - 开发者指南: + - 如何贡献代码: DevelopGuide/HowToContributeCode/README.md + - 版本升级日志: DevelopGuide/ChangeLog/changelog.md + # - 常见问题FAQ: + # - 日志管理: + # - 错误信息: + - 环境参数: DevelopGuide/EnvironmentalParameters/env.md + - API Reference: + - Python API Reference: API-Reference/Python/PythonAPI.md + # - Java API Reference: + # - Go API Reference: + +# 主题设置 +theme: + name: material + +# 插件列表 +plugins: + - search + +# 其他配置 +markdown_extensions: + - codehilite + - toc: + permalink: true \ No newline at end of file From e269434d39ceb72fe27792d15fa619d85ef45063 Mon Sep 17 00:00:00 2001 From: userpj Date: Wed, 13 Nov 2024 17:03:32 +0800 Subject: [PATCH 14/85] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=9F=A5=E8=AF=86?= =?UTF-8?q?=E5=BA=93=E6=96=87=E6=A1=A3=E4=B8=8A=E4=BC=A0=E8=8C=83=E5=9B=B4?= =?UTF-8?q?=20(#595)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../live_broadcast_material/2024_08_22/knowledgebase.ipynb | 2 +- .../Python/appbuilder.core.console.knowledge_base.md | 2 +- docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md | 4 ++-- python/core/console/knowledge_base/data_class.py | 3 +-- python/core/console/knowledge_base/knowledge_base.py | 2 +- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/cookbooks/live_broadcast_material/2024_08_22/knowledgebase.ipynb b/cookbooks/live_broadcast_material/2024_08_22/knowledgebase.ipynb index c86928ead..1bfae0c9d 100644 --- a/cookbooks/live_broadcast_material/2024_08_22/knowledgebase.ipynb +++ b/cookbooks/live_broadcast_material/2024_08_22/knowledgebase.ipynb @@ -202,7 +202,7 @@ "source": [ "###### 上传文档到知识库\n", "- 主要提供自定义文档处理策略,向知识库添加文档\n", - " - 文档格式:rawText (允许配置后续分割策略), qa(不支持配置后续分割策略)\n", + " - 文档格式:rawText (允许配置后续分割策略)\n", " - 文档处理策略" ] }, diff --git a/docs/API-Reference/Python/appbuilder.core.console.knowledge_base.md b/docs/API-Reference/Python/appbuilder.core.console.knowledge_base.md index 02ac2d384..c9c5e75d1 100644 --- a/docs/API-Reference/Python/appbuilder.core.console.knowledge_base.md +++ b/docs/API-Reference/Python/appbuilder.core.console.knowledge_base.md @@ -60,7 +60,7 @@ print("文档列表: ", list_res) * **参数:** * **id** (*Optional* *[**str* *]* *,* *optional*) -- 知识库ID,如果不指定则使用当前实例的knowledge_id属性。默认值为None。 - * **contentFormat** (*str* *,* *optional*) -- 文档内容格式,可以是"rawText", "qa"之一。默认值为""。 + * **contentFormat** (*str* *,* *optional*) -- 文档内容格式,可以是"rawText"。默认值为""。 * **source** (*data_class.DocumentSource* *,* *optional*) -- 文档源数据。默认值为None。 * **processOption** (*data_class.DocumentProcessOption* *,* *optional*) -- 文档处理选项。默认值为None。 * **client_token** (*str* *,* *optional*) -- 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 diff --git a/docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md b/docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md index 9c67158ac..fa32f386e 100644 --- a/docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md +++ b/docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md @@ -266,7 +266,7 @@ knowledge.delete_knowledge_base("da51a988-cbe7-4b24-aa5b-768985e8xxxx") | 参数名称 | 参数类型 | 是否必传 | 描述 | 示例值 | | ------------- | --------------------- | -------------------------------------- | ------------------------------------------------------------ | ---------------- | | id | string | 是 | 知识库id | "正确的知识库ID" | -| contentFormat | string | 是 | 文档格式:rawText (允许配置后续分割策略), qa(不支持配置后续分割策略) | "rawText" | +| contentFormat | string | 是 | 文档格式:rawText (允许配置后续分割策略) | "rawText" | | source | DocumentSource | 是 | 数据来源 | | | processOption | DocumentProcessOption | 当contentFormat参数配置为rawText时必传 | 文档处理策略 | | @@ -363,7 +363,7 @@ knowledge.create_documents( | 参数名称 | 参数类型 | 是否必传 | 描述 | 示例值 | | -------------- | --------------------- | -------- | ------------------------------------------------------------ | ---------------- | | file_path | string | 是 | 文件路径 | "正确的文件路径" | -| content_format | string | 否 | 文档格式:rawText (允许配置后续分割策略), qa(不支持配置后续分割策略) | "rawText" | +| content_format | string | 否 | 文档格式:rawText (允许配置后续分割策略) | "rawText" | | id | string | 是 | 知识库ID | "正确的知识库ID" | | processOption | DocumentProcessOption | 是 | 文档处理策略 | | diff --git a/python/core/console/knowledge_base/data_class.py b/python/core/console/knowledge_base/data_class.py index 909d4cdca..ef2e9d718 100644 --- a/python/core/console/knowledge_base/data_class.py +++ b/python/core/console/knowledge_base/data_class.py @@ -213,8 +213,7 @@ class KnowledgeBaseCreateDocumentsRequest(BaseModel): source: DocumentSource = Field(..., description="文档来源") contentFormat: str = Field( ..., - description="文档内容格式, (rawText 普通文件上传 | qa 问答对)", - enum=["rawText", "qa"], + description="文档内容格式, (rawText 普通文件上传)" ) processOption: Optional[DocumentProcessOption] = Field( None, description="文档处理选项" diff --git a/python/core/console/knowledge_base/knowledge_base.py b/python/core/console/knowledge_base/knowledge_base.py index 7aa75bb28..87e6aeb53 100644 --- a/python/core/console/knowledge_base/knowledge_base.py +++ b/python/core/console/knowledge_base/knowledge_base.py @@ -491,7 +491,7 @@ def create_documents( Args: id (Optional[str], optional): 知识库ID,如果不指定则使用当前实例的knowledge_id属性。默认值为None。 - contentFormat (str, optional): 文档内容格式,可以是"rawText", "qa"之一。默认值为""。 + contentFormat (str, optional): 文档内容格式,可以是"rawText"。默认值为""。 source (data_class.DocumentSource, optional): 文档源数据。默认值为None。 processOption (data_class.DocumentProcessOption, optional): 文档处理选项。默认值为None。 client_token (str, optional): 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 From 83d4828b803840cb914dbac95a14dd3e75b10c90 Mon Sep 17 00:00:00 2001 From: C9luster <138663536+C9luster@users.noreply.github.com> Date: Wed, 13 Nov 2024 17:13:17 +0800 Subject: [PATCH 15/85] =?UTF-8?q?=E6=9B=B4=E6=96=B0SDK=20=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E9=93=BE=E6=8E=A5=EF=BC=8C=E4=BB=A5=E5=8F=8A=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E9=83=A8=E5=88=86=E6=96=87=E6=A1=A3=20(#596)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 更新docs文档链接 * 更新docs文档链接Image为image * 更新docs文档图片链接 * 更新组件文档链接 --------- Co-authored-by: yinjiaqi --- README.md | 79 +++++++++++++----- docs/Application/RAG/BasicKnowledge/rag.md | 2 +- docs/BasisModule/Components/Components.md | 3 + .../README.md | 2 +- docs/README.md | 2 +- docs/README_en.md | 80 ++++++++++++++----- docs/README_ja.md | 79 +++++++++++++----- 7 files changed, 186 insertions(+), 61 deletions(-) create mode 100644 docs/BasisModule/Components/Components.md diff --git a/README.md b/README.md index 154f32cf4..0707d179c 100644 --- a/README.md +++ b/README.md @@ -23,9 +23,9 @@ - **调用** - 调用大模型,可自由调用您在[百度智能云千帆大模型平台](https://qianfan.cloud.baidu.com/)的模型,开发并调优prompt - 调用能力组件,提供40+个源于百度生态的[优质组件](https://cloud.baidu.com/doc/AppBuilder/s/Glqb6dfiz#3%E3%80%81%E5%BC%80%E9%80%9A%E7%BB%84%E4%BB%B6%E6%9C%8D%E5%8A%A1),赋能Agent应用 - - 调用AI原生应用,通过[AppBuilderClient](/docs/basic_module/appbuilder_client.md)可访问并管理在百度智能云千帆AppBuilder[网页端](https://console.bce.baidu.com/ai_apaas/app)发布的AI原生应用,并可注册本地函数联动端云组件 + - 调用AI原生应用,通过[AppBuilderClient](/docs/BasisModule/Platform/Application/appbuilder_client.md)可访问并管理在百度智能云千帆AppBuilder[网页端](https://console.bce.baidu.com/ai_apaas/app)发布的AI原生应用,并可注册本地函数联动端云组件 - **编排** - - 配置知识库,通过[KnowledgeBase](/docs/basic_module/knowledgebase.md)管理知识库,进行文档及知识切片的增删改查,配合[网页端](https://console.bce.baidu.com/ai_apaas/app)开发产业级的`RAG`应用 + - 配置知识库,通过[KnowledgeBase](/docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md)管理知识库,进行文档及知识切片的增删改查,配合[网页端](https://console.bce.baidu.com/ai_apaas/app)开发产业级的`RAG`应用 - 编排工作流,提供了`Message`、`Component`、`AgentRuntime`多级工作流抽象,实现工作流编排,并可与LangChain、OpenAI等业界生态能力打通 - **监控** - 提供了可视化Tracing、详细DebugLog等监控工具,助力开发者在生产环境应用 @@ -78,14 +78,14 @@ AppBuilder-SDK不仅提供了百度智能云提供的基础能力组件,同时 #### 百度智能云千帆AppBuilder-SDK 最新版本 0.9.6 (2024-10-26) -百度智能云千帆AppBuilder-SDK 更新记录&最新特性请查阅我们的[版本说明](/docs/quick_start/changelog.md) +百度智能云千帆AppBuilder-SDK 更新记录&最新特性请查阅我们的[版本说明](/docs/DevelopGuide/ChangeLog/changelog.md) - `Python`版本安装,要求Python版本 >= `3.9` ```bash python3 -m pip install --upgrade appbuilder-sdk ``` -- `Java` 及 `Go` 版本安装,以及通过`Docker`镜像使用,请查阅[安装说明](/docs/quick_start/install.md) +- `Java` 及 `Go` 版本安装,以及通过`Docker`镜像使用,请查阅[安装说明](/docs/QuickStart/StartFirstAINativeApplication/install.md) ## 快速开始你的AI原生应用开发之旅 @@ -278,21 +278,62 @@ Hook: ## 用户文档 -- [快速开始](/docs/quick_start/README.md) - - [安装说明](/docs/quick_start/install.md) - - [版本说明](/docs/quick_start/changelog.md) -- [基础功能](/docs/basic_module/README.md) - - [基础能力组件](/docs/basic_module/components.md) - - [流程编排](/docs/basic_module/assistant_sdk.md) - - [端到端应用](/docs/basic_module/appbuilder_client.md) -- [进阶实践](/docs/advanced_application/README.md) - - [Cookbooks](/cookbooks/README.md) - - [AppBuilder Trace](https://github.com/baidubce/app-builder/blob/master/docs/trace/README.md) -- [服务化部署](/docs/service/README.md) - - [API调用](/docs/service/flask.md) - - [交互式前端](/docs/service/chainlit.md) - - [公有云部署](/docs/service/cloud.md) -- [二次开发](/docs/develop_guide/README.md) +## Github 文档 +- [首页](https://github.com/baidubce/app-builder/blob/master/docs/README.md) + - 快速上手: + - 开始你的第一个AI原生应用: + - [安装](https://github.com/baidubce/app-builder/blob/master/docs/QuickStart/StartFirstAINativeApplication/install.md) + - [快速开始](https://github.com/baidubce/app-builder/blob/master/docs/QuickStart/StartFirstAINativeApplication/README.md) + - 产业实践应用范例: + - [SDK使用示例](https://github.com/baidubce/app-builder/blob/master/docs/QuickStart/ExamplesOfIndustrialPracticeApplications/README.md) + - [SDK当前支持的编程语言](https://github.com/baidubce/app-builder/blob/master/docs/QuickStart/CurrentlySupportedProgrammingLanguages/README.md) + - 基础: + - 模型: + - [获取模型列表](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Model/get_model_list.md) + - [组件](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Components/Components.md) + - 监控: + - [TRACE基础功能](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/basic.md) + - [TRACE拓展功能](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/phoenix_method.md) + - 部署: + - [交互式前端部署](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/AgentChainlit.md) + - [公有云部署](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/cloud.md) + - [API 访问](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/flask.md) + - [AgentRuntime](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/agentruntime.md) + - [UserSession](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/usersession.md) + - 平台: + - 应用: + - [AppBuilderClient组件](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/Application/appbuilder_client.md) + - [获取AppBuilder已发布的应用列表](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/Application/get_app_list.md) + - 知识库: + - [知识库组件](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md) + - 自定义组件: + - [基础能力组件](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/CustomComponents/components.md) + - 应用: + - Agent: + - [基础知识](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [使用官方组件](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [ToolCall](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [ToolChoice](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [使用异步和流式加速客户端调用](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - RAG: + - [基础知识](https://github.com/baidubce/app-builder/blob/master/docs/Application/RAG/BasicKnowledge/rag.md) + - [知识库管理](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [Reference信息处理](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - Workflow: + - [基础知识](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [从零使用Workflow组装一个RAG应用](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [从零使用Workflow组装一个Agent应用](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - 开发者指南: + - [如何贡献代码](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/HowToContributeCode/README.md) + - [版本升级日志](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/ChangeLog/changelog.md) + - [常见问题FAQ](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [日志管理](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [错误信息](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [环境参数](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/EnvironmentalParameters/env.md) + - API Reference: + - [Python API Reference](https://github.com/baidubce/app-builder/blob/master/docs/API-Reference/Python/PythonAPI.md) + - [Java API Reference](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [Go API Reference](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) ## 开源社区与活动 diff --git a/docs/Application/RAG/BasicKnowledge/rag.md b/docs/Application/RAG/BasicKnowledge/rag.md index cda74541d..c017e98c2 100644 --- a/docs/Application/RAG/BasicKnowledge/rag.md +++ b/docs/Application/RAG/BasicKnowledge/rag.md @@ -4,7 +4,7 @@ RAG是基于线上RAG应用的问答组件,可以使用该组件利用线上RAG应用进行问答。
-⚠️本组件仅适用于2024-04-02之前创建的历史RAG应用,最新创建的AgentBuilder应用,请参考[AgentBuilder应用](agent_builder.md) +⚠️本组件仅适用于2024-04-02之前创建的历史RAG应用,最新创建的AgentBuilder应用,请参考[AppBuilder应用](../../../BasisModule/Platform/Application/appbuilder_client.md) 进行调用。 diff --git a/docs/BasisModule/Components/Components.md b/docs/BasisModule/Components/Components.md new file mode 100644 index 000000000..85027a3e7 --- /dev/null +++ b/docs/BasisModule/Components/Components.md @@ -0,0 +1,3 @@ +# 组件文档 + +- 当前目录为Appbuilder-SDK的组件文档目录 \ No newline at end of file diff --git a/docs/QuickStart/CurrentlySupportedProgrammingLanguages/README.md b/docs/QuickStart/CurrentlySupportedProgrammingLanguages/README.md index 99aaf4c8f..2efdbe91a 100644 --- a/docs/QuickStart/CurrentlySupportedProgrammingLanguages/README.md +++ b/docs/QuickStart/CurrentlySupportedProgrammingLanguages/README.md @@ -5,4 +5,4 @@ - [知识库管理 KnowledgeBase SDK](../../BasisModule/Platform/KnowledgeBase/knowledgebase.md) - AI基础能力组件 SDK:支持Python - [获取模型列表](../../BasisModule/Model/get_model_list.md) - - [基础能力组件](../../docs/BasisModule/Platform/CustomComponents/components.md) \ No newline at end of file + - [基础能力组件](../../BasisModule/Platform/CustomComponents/components.md) \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index 211ba4ae1..8bb36d53f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -12,7 +12,7 @@ - 基础: - 模型: - [获取模型列表](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Model/get_model_list.md) - - [组件](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [组件](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Components/Components.md) - 监控: - [TRACE基础功能](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/basic.md) - [TRACE拓展功能](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/phoenix_method.md) diff --git a/docs/README_en.md b/docs/README_en.md index f2c79ddb6..ed4d56c2f 100644 --- a/docs/README_en.md +++ b/docs/README_en.md @@ -1,5 +1,5 @@
-logo +logo
[![License](https://img.shields.io/badge/license-Apache%202-blue.svg)](LICENSE) @@ -49,14 +49,14 @@ Baidu AI Cloud Qianfan AppBuilder-SDK offers the following essential features fo #### The latest version of Baidu AI Cloud Qianfan AppBuilder SDK is 0.9.6 (2024-10-26) -Baidu AI Cloud Qianfan AppBuilder SDK ReleaseNote please refer to our [version description](/docs/quick_start/changelog.md) +Baidu AI Cloud Qianfan AppBuilder SDK ReleaseNote please refer to our [version description](/docs/DevelopGuide/ChangeLog/changelog.md) - We recommend installing the latest stable version of `Python` ```bash python3 -m pip install --upgrade appbuilder-sdk ``` -- For installation of `Java` and `Go` versions, as well as using `Docker` images, please refer to the [Installation Instructions](/docs/quick_start/install.md) +- For installation of `Java` and `Go` versions, as well as using `Docker` images, please refer to the [Installation Instructions](/docs/QuickStart/StartFirstAINativeApplication/install.md) ## Quickly start your first AI native application! @@ -240,32 +240,72 @@ Hook: ## Panorama of Baidu AI Cloud Qianfan AppBuilder SDK capability
-wechat +wechat
## User Documentation -- [Quick start](/docs/quick_start/README.md) - - [Installation instructions](/docs/quick_start/install.md) - - [Release note](/docs/quick_start/changelog.md) -- [Basic components](/docs/basic_module/README.md) - - [Basic Ability Components](/docs/basic_module/components.md) - - [Process orchestration](/docs/basic_module/assistant_sdk.md) - - [End-to-end applications](/docs/basic_module/appbuilder_client.md) -- [Advanced Practice](/docs/advanced_application/README.md) - - [Cookbooks](/cookbooks/README.md) -- [Service deployment](/docs/service/README.md) - - [API calls](/docs/service/flask.md) - - [Interactive front-end](/docs/service/chainlit.md) - - [Cloud deployment](/docs/service/cloud.md) -- [Secondary development](/docs/develop_guide/README.md) - +- [Home](https://github.com/baidubce/app-builder/blob/master/docs/README.md) + - Quick Start: + - Start your first AI-native application: + - [Installation](https://github.com/baidubce/app-builder/blob/master/docs/QuickStart/StartFirstAINativeApplication/install.md) + - [Quick Start Guide](https://github.com/baidubce/app-builder/blob/master/docs/QuickStart/StartFirstAINativeApplication/README.md) + - Industrial practice application examples: + - [SDK usage examples](https://github.com/baidubce/app-builder/blob/master/docs/QuickStart/ExamplesOfIndustrialPracticeApplications/README.md) + - [Currently supported programming languages by SDK](https://github.com/baidubce/app-builder/blob/master/docs/QuickStart/CurrentlySupportedProgrammingLanguages/README.md) + - Basics: + - Models: + - [Get model list](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Model/get_model_list.md) + - [Components](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Components/Components.md) + - Monitoring: + - [TRACE basic functions](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/basic.md) + - [TRACE extended functions](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/phoenix_method.md) + - Deployment: + - [Interactive front-end deployment](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/AgentChainlit.md) + - [Public cloud deployment](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/cloud.md) + - [API access](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/flask.md) + - [AgentRuntime](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/agentruntime.md) + - [UserSession](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/usersession.md) + - Platform: + - Applications: + - [AppBuilderClient component](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/Application/appbuilder_client.md) + - [Get the list of applications published by AppBuilder](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/Application/get_app_list.md) + - Knowledge Base: + - [Knowledge Base component](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md) + - Custom Components: + - [Basic capabilities component](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/CustomComponents/components.md) + - Applications: + - Agent: + - [Basic knowledge](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [Using official components](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [ToolCall](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [ToolChoice](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [Using asynchronous and streaming accelerated client calls](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - RAG: + - [Basic knowledge](https://github.com/baidubce/app-builder/blob/master/docs/Application/RAG/BasicKnowledge/rag.md) + - [Knowledge Base Management](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [Reference Information Processing](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - Workflow: + - [Basic knowledge](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [Assembling a RAG application from scratch using Workflow](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [Assembling an Agent application from scratch using Workflow](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - Developer Guide: + - [How to contribute code](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/HowToContributeCode/README.md) + - [Version upgrade log](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/ChangeLog/changelog.md) + - [FAQ](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [Log Management](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [Error messages](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [Environmental parameters](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/EnvironmentalParameters/env.md) + - API Reference: + - [Python API Reference](https://github.com/baidubce/app-builder/blob/master/docs/API-Reference/Python/PythonAPI.md) + - [Java API Reference](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [Go API Reference](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) ## Open source community and activities

AppBuilder-SDK WeChat Group QR Code

-wechat +wechat
- [Github Issue](https://github.com/baidubce/app-builder/issues): Submit installation/usage issues, report bugs, suggest new features, communicate development plans, etc diff --git a/docs/README_ja.md b/docs/README_ja.md index 79aa542d3..3e0027c93 100644 --- a/docs/README_ja.md +++ b/docs/README_ja.md @@ -1,5 +1,5 @@
-logo +logo
[![License](https://img.shields.io/badge/license-Apache%202-blue.svg)](LICENSE) @@ -46,14 +46,14 @@ Baidu AI Cloud Qianfan AppBuilder-SDKは、AIアプリケーション開発者 #### Baidu AI Cloud Qianfan AppBuilder SDKの最新バージョンは0.9.6(2024-10-26)です -Baidu AI Cloud Qianfan AppBuilder SDKのリリースノートについては、[バージョン説明](/docs/quick_start/changelog.md)をご覧ください。 +Baidu AI Cloud Qianfan AppBuilder SDKのリリースノートについては、[バージョン説明](DevelopGuide/ChangeLog/changelog.md)をご覧ください。 - 最新の安定バージョンの`Python`をインストールすることをお勧めします。 ```bash python3 -m pip install --upgrade appbuilder-sdk ``` -- `Java`および`Go`バージョンのインストールや`Docker`イメージの使用については、[インストール手順](/docs/quick_start/install.md)をご覧ください。 +- `Java`および`Go`バージョンのインストールや`Docker`イメージの使用については、[インストール手順](QuickStart/StartFirstAINativeApplication/install.md)をご覧ください。 ## 最初のAIネイティブアプリケーションをすぐに開始しましょう! @@ -236,32 +236,73 @@ print(answer.content.answer) ## Baidu AI Cloud Qianfan AppBuilder SDKの機能全景
-wechat +wechat
## ユーザードキュメント -- [クイックスタート](/docs/quick_start/README.md) - - [インストール手順](/docs/quick_start/install.md) - - [リリースノート](/docs/quick_start/changelog.md) -- [基本コンポーネント](/docs/basic_module/README.md) - - [基本機能コンポーネント](/docs/basic_module/components.md) - - [プロセスオーケストレーション](/docs/basic_module/assistant_sdk.md) - - [エンドツーエンドアプリケーション](/docs/basic_module/appbuilder_client.md) -- [高度な実践](/docs/advanced_application/README.md) - - [Cookbooks](/cookbooks/README.md) -- [サービスデプロイメント](/docs/service/README.md) - - [API呼び出し](/docs/service/flask.md) - - [インタラクティブフロントエンド](/docs/service/chainlit.md) - - [クラウドデプロイメント](/docs/service/cloud.md) -- [二次開発](/docs/develop_guide/README.md) +- [ホームページ](https://github.com/baidubce/app-builder/blob/master/docs/README.md) + - クイックスタート: + - 最初のAIネイティブアプリケーションを開始する: + - [インストール](https://github.com/baidubce/app-builder/blob/master/docs/QuickStart/StartFirstAINativeApplication/install.md) + - [クイックスタートガイド](https://github.com/baidubce/app-builder/blob/master/docs/QuickStart/StartFirstAINativeApplication/README.md) + - 産業実践アプリケーション例: + - [SDK使用例](https://github.com/baidubce/app-builder/blob/master/docs/QuickStart/ExamplesOfIndustrialPracticeApplications/README.md) + - [現在SDKがサポートしているプログラミング言語](https://github.com/baidubce/app-builder/blob/master/docs/QuickStart/CurrentlySupportedProgrammingLanguages/README.md) + - 基本: + - モデル: + - [モデルリストの取得](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Model/get_model_list.md) + - [コンポーネント](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Components/Components.md) + - 監視: + - [TRACE基本機能](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/basic.md) + - [TRACE拡張機能](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/phoenix_method.md) + - デプロイ: + - [インタラクティブなフロントエンドデプロイ](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/AgentChainlit.md) + - [パブリッククラウドデプロイ](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/cloud.md) + - [API アクセス](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/flask.md) + - [AgentRuntime](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/agentruntime.md) + - [UserSession](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/usersession.md) + - プラットフォーム: + - アプリケーション: + - [AppBuilderClientコンポーネント](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/Application/appbuilder_client.md) + - [AppBuilderで公開されたアプリケーションリストの取得](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/Application/get_app_list.md) + - ナレッジベース: + - [ナレッジベースコンポーネント](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md) + - カスタムコンポーネント: + - [基本機能コンポーネント](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/CustomComponents/components.md) + - アプリケーション: + - エージェント: + - [基本知識](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [公式コンポーネントの使用](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [ToolCall](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [ToolChoice](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [非同期およびストリーミングを使用してクライアント呼び出しを加速する](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - RAG: + - [基本知識](https://github.com/baidubce/app-builder/blob/master/docs/Application/RAG/BasicKnowledge/rag.md) + - [ナレッジベース管理](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [Reference情報処理](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - ワークフロー: + - [基本知識](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [Workflowを使ってRAGアプリケーションをゼロから組み立てる](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [Workflowを使ってAgentアプリケーションをゼロから組み立てる](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - 開発者ガイド: + - [コードの貢献方法](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/HowToContributeCode/README.md) + - [バージョンアップログ](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/ChangeLog/changelog.md) + - [よくある質問FAQ](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [ログ管理](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [エラーメッセージ](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [環境パラメータ](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/EnvironmentalParameters/env.md) + - APIリファレンス: + - [Python APIリファレンス](https://github.com/baidubce/app-builder/blob/master/docs/API-Reference/Python/PythonAPI.md) + - [Java APIリファレンス](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [Go APIリファレンス](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) ## オープンソースコミュニティと活動

AppBuilder-SDK WeChatグループQRコード

-wechat +wechat
- [Github Issue](https://github.com/baidubce/app-builder/issues): インストール/使用の問題を提出し、バグを報告し、新機能を提案し、開発計画をコミュニケーションします。 From cedef1ed0a6758d35a800b3193b99eb85dd83477 Mon Sep 17 00:00:00 2001 From: userpj Date: Thu, 14 Nov 2024 10:37:49 +0800 Subject: [PATCH 16/85] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E7=BA=A7=E5=88=AB=E9=85=8D=E7=BD=AE=E5=AF=B9sse=20client?= =?UTF-8?q?=E4=B8=8D=E7=94=9F=E6=95=88=E7=9A=84=E9=97=AE=E9=A2=98=20(#597)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 修复sse client日志级别不被配置的问题 * update unittest * update --- python/tests/test_utils.py | 48 +++++++++++++++++++++++++++++++------- python/utils/sse_util.py | 13 ++++++----- 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/python/tests/test_utils.py b/python/tests/test_utils.py index 0ded0046d..8bce87418 100644 --- a/python/tests/test_utils.py +++ b/python/tests/test_utils.py @@ -17,7 +17,7 @@ from unittest.mock import MagicMock from appbuilder.utils.sse_util import SSEClient,Event from appbuilder.utils.model_util import RemoteModel,Models -from appbuilder.utils.logger_util import LoggerWithLoggerId,_setup_logging +from appbuilder.utils.logger_util import LoggerWithLoggerId,_setup_logging,logger from threading import current_thread # 创建一个logger类 @@ -40,7 +40,7 @@ def test_sse_util_SSEClient(self): # 测试是否抛出 StopIteration 异常,表示没有更多事件 with self.assertRaises(StopIteration): next(event_generator) - + # test_events mock_event_source.__iter__.return_value = iter([ b': Test event 1\n\n', @@ -52,26 +52,56 @@ def test_sse_util_SSEClient(self): sse_client = SSEClient(event_source=mock_event_source) for event in sse_client.events(): pass + + # test_close + sse_client.close() + + def test_sse_util_SSEClient_DEBUG(self): + logger.setLoglevel("DEBUG") + mock_event_source = MagicMock() + mock_event_source.__iter__.return_value = iter( + [b"data: Test event 1\n\n", b"data: Last incomplete event"] + ) + sse_client = SSEClient(event_source=mock_event_source) + event_generator = sse_client._read() + self.assertEqual(next(event_generator), b"data: Test event 1\n\n") + self.assertEqual(next(event_generator), b"data: Last incomplete event") + # 测试是否抛出 StopIteration 异常,表示没有更多事件 + with self.assertRaises(StopIteration): + next(event_generator) + + # test_events + mock_event_source.__iter__.return_value = iter( + [ + b": Test event 1\n\n", + b"test: Test event 2\n\n", + b"data:Testevent3\n\n", + b"data\n\n", + b"event:Testevent5\n\n", + ] + ) + sse_client = SSEClient(event_source=mock_event_source) + for event in sse_client.events(): + pass + # test_close sse_client.close() - + def test_sse_util_Event(self): # test_str_ event_str=str(Event(id='id',retry=10)) assert event_str.startswith('message') - + def test_model_util_RemoteModel(self): # test_get_remote_name_by_short_name rm=RemoteModel(remote_name='test_remote') rm.get_remote_name_by_short_name(short_name="eb-turbo-appbuilder") - + def test_model_util_Models(self): # test_list models=Models() models.list(retry=-1) - - + + if __name__ == '__main__': unittest.main() - - \ No newline at end of file diff --git a/python/utils/sse_util.py b/python/utils/sse_util.py index 54fcfdd6e..027923c8e 100644 --- a/python/utils/sse_util.py +++ b/python/utils/sse_util.py @@ -14,6 +14,7 @@ """ SSE Client util """ +from appbuilder.utils.logger_util import logger import logging class SSEClient: @@ -27,7 +28,7 @@ def __init__(self, event_source, char_enc='utf-8'): 事件源应为二进制流,并具有 close() 方法。 这通常是实现 io.BinaryIOBase 的东西,比如 httplib 或 urllib3HTTPResponse 对象。 """ - logging.info(f'Initialized SSE client from event source {event_source}') + logger.info(f'Initialized SSE client from event source {event_source}') self._event_source = event_source self._char_enc = char_enc @@ -67,13 +68,13 @@ def events(self): # ignored. if not line.strip() or line.startswith(':'): continue - logging.debug(f"raw line: {line}") + logger.debug(f"raw line: {line}") data = line.split(':', 1) field = data[0] # Ignore unknown fields. if field not in event.__dict__: event.raw += line - logging.info(f'Saw invalid field {field} while parsing Server Side Event') + logger.info(f'Saw invalid field {field} while parsing Server Side Event') continue if len(data) > 1: @@ -111,10 +112,10 @@ def events(self): # Empty event names default to 'message' event.event = event.event or 'message' # Dispatch the event - if logging.getLogger().getEffectiveLevel() == logging.DEBUG: - logging.debug(f'Dispatching {event.debug_str}...') + if logger.getEffectiveLevel() == logging.DEBUG: + logger.debug(f'Dispatching {event.debug_str}...') else: - logging.info(f'Dispatching {event}...') + logger.info(f'Dispatching {event}...') yield event def close(self): From 1be8f2aa49ce4ea0608bf2abb4d68b9b02614538 Mon Sep 17 00:00:00 2001 From: peiwenYe <963623403@qq.com> Date: Thu, 14 Nov 2024 20:24:14 +0800 Subject: [PATCH 17/85] =?UTF-8?q?=20=E5=A2=9E=E5=8A=A0=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E8=BE=93=E5=87=BA=E6=A0=A1=E9=AA=8C=20(#600)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 组件输出规范检查 Co-authored-by: yepeiwen01 --- python/tests/component_check.py | 185 ++++++++++++++- python/tests/component_collector.py | 1 + python/tests/component_output_schemas.py | 268 ++++++++++++++++++++++ python/tests/component_tool_eval_cases.py | 47 ++++ python/tests/run_python_test.sh | 1 + python/tests/test_all_components.py | 2 +- 6 files changed, 499 insertions(+), 5 deletions(-) create mode 100644 python/tests/component_output_schemas.py create mode 100644 python/tests/component_tool_eval_cases.py diff --git a/python/tests/component_check.py b/python/tests/component_check.py index 98ac5444e..a342ee75c 100644 --- a/python/tests/component_check.py +++ b/python/tests/component_check.py @@ -14,9 +14,14 @@ import os import json import inspect +import jsonschema +from jsonschema import validate, ValidationError, SchemaError from pydantic import BaseModel +from typing import Generator from appbuilder.utils.func_utils import Singleton from appbuilder.utils.json_schema_to_model import json_schema_to_pydantic_model +from component_tool_eval_cases import component_tool_eval_cases +from component_output_schemas import type_to_json_schemas, components_tool_eval_output_json_maps class CheckInfo(BaseModel): @@ -145,13 +150,26 @@ def check(self, component_cls) -> CheckInfo: properties = manifest['parameters']['properties'] required_params = [] anyOf = manifest['parameters'].get('anyOf', None) + required_exists = False if anyOf: for anyOf_dict in anyOf: - required_params += anyOf_dict['required'] + if 'required' in anyOf_dict: + required_exists = True + required_params += anyOf_dict['required'] + if not anyOf: - required_params += manifest['parameters']['required'] - + if 'required' in manifest['parameters']: + required_exists = True + required_params += manifest['parameters']['required'] + if not required_exists: + check_pass_flag = False + invalid_details.append("mainfest 未定义required参数") + return CheckInfo( + check_rule_name=self.rule_name, + check_result=check_pass_flag, + check_detail=",".join(invalid_details)) + # 交互检查 tool_eval_input_params = [] print("required_params: {}".format(required_params)) @@ -240,7 +258,166 @@ def check(self, component_cls) -> CheckInfo: check_detail="以下ToolEval方法参数名称是系统保留字段,请更换:{}".format(",".join(invalid_details)) if len(invalid_details) > 0 else "") +class ToolEvalOutputJsonRule(RuleBase): + """ + 检查tool_eval的输出结果是否符合对应的json schema + """ + def __init__(self): + super().__init__() + self.rule_name = 'ToolEvalOutputJsonRule' + self.output_types = list(type_to_json_schemas.keys()) + + def _check_pre_format(self, outputs): + invalid_details = [] + if "content" not in outputs: + invalid_details.append("ToolEval返回值不符合规范:返回内容缺少content") + return invalid_details + + for content in outputs["content"]: + if "type" not in content: + invalid_details.append("ToolEval返回值不符合规范:返回content缺少type") + break + + out_type = content["type"] + if out_type not in self.output_types: + invalid_details.append("ToolEval返回值不符合JSON Schema:返回content.type={} 不是合法的输出类型".format(out_type)) + break + return invalid_details + + def _check_jsonschema(self, outputs, output_schemas): + """检查输出格式是否符合对应的json schema + """ + invalid_details = [] + if len(self._check_pre_format(outputs)) > 0 : + return invalid_details + + for content in outputs["content"]: + out_type = content["type"] + out_schema = type_to_json_schemas[out_type] + if out_schema not in output_schemas: + invalid_details.append("ToolEval返回值不符合JSON Schema:{} 不是该组件期望的Json Schema输出类型".format(out_schema['$schema'])) + continue + try: + validate(instance=content, schema=out_schema) + except Exception as e: + invalid_details.append("ToolEval返回值不符合JSON Schema: {}\n".format(e.message)) + return invalid_details + + def _gather_iter_outputs(self, outputs): + text_output = "" + oral_text_output = "" + code_output = "" + for content in outputs["content"]: + out_type = content["type"] + if out_type == "text": + text_output += content["text"]["info"] + elif out_type == "oral_text": + oral_text_output += content["oral_text"]["info"] + elif out_type == "code": + code_output += content["code"]["code"] + return { + "text": text_output, + "oral_text": oral_text_output, + "code": code_output, + } + + def _check_text_and_code(self, component_case, output_dict): + """检查输出的内容是否符合预期,只检查text(包含oral_text)和code + """ + if not hasattr(component_case,"outputs"): + return [] + + expected_output = component_case.outputs() + expected_output_texts = [] + expected_output_oral_texts = [] + expected_output_codes = [] + if "text" in expected_output: + expected_output_texts = expected_output["text"] + if "oral_text" in expected_output: + expected_output_oral_texts = expected_output["oral_text"] + if "code" in expected_output: + expected_output_codes = expected_output["code"] + + lost_texts = [] + lost_oral_texts = [] + lost_code = [] + for expected_output_text in expected_output_texts: + if expected_output_text not in output_dict["text"]: + lost_texts.append(expected_output_text) + + for expected_output_oral_text in expected_output_oral_texts: + if expected_output_oral_text not in output_dict["oral_text"]: + lost_oral_texts.append(expected_output_oral_text) + + for expected_output_code in expected_output_codes: + if expected_output_code not in output_dict["code"]: + lost_code.append(expected_output_code) + + error_message = "" + if len(lost_texts) > 0: + error_message += "应包含text:{}".format(", ".join(lost_texts)) + if len(lost_oral_texts) > 0: + error_message += "应包含oral_text:{}".format(", ".join(lost_oral_texts)) + if len(lost_code) > 0: + error_message += "应包含code:{}".format(", ".join(lost_code)) + + if error_message != "": + return ["ToolEval返回内容与预期不符: " + error_message] + else: + return [] + + def check(self, component_cls) -> CheckInfo: + invalid_details = [] + component_cls_name = component_cls.__name__ + if component_cls_name not in components_tool_eval_output_json_maps: + invalid_details.append("{} 没有注册到 components_tool_eval_output_json_maps 中".format(component_cls_name)) + else: + output_json_schemas = components_tool_eval_output_json_maps[component_cls_name] + if component_cls_name not in component_tool_eval_cases: + invalid_details.append("{} 没有添加测试case到 component_tool_eval_cases 中".format(component_cls_name)) + else: + component_case = component_tool_eval_cases[component_cls_name]() + input_dict = component_case.inputs() + component_obj = component_cls() + + try: + stream_output_dict = {"text": "", "oral_text":"", "code": ""} + stream_outputs = component_obj.tool_eval(**input_dict) + for stream_output in stream_outputs: #校验流式输出 + iter_invalid_detail = self._check_jsonschema(stream_output, output_json_schemas) + invalid_details.extend(["流式" + error_message for error_message in iter_invalid_detail]) + iter_output_dict = self._gather_iter_outputs(stream_output) + stream_output_dict["text"] += iter_output_dict["text"] + stream_output_dict["oral_text"] += iter_output_dict["oral_text"] + stream_output_dict["code"] += iter_output_dict["code"] + if len(invalid_details) == 0: + invalid_details.extend(self._check_text_and_code(component_case, stream_output_dict)) + except Exception as e: + invalid_details.append("ToolEval执行失败: {}".format(e)) + + try: + non_stream_outputs = component_obj.non_stream_tool_eval(**input_dict) + non_stream_invalid_details = self._check_jsonschema(non_stream_outputs, output_json_schemas) #校验非流式输出 + invalid_details.extend(["非流式" + error_message for error_message in non_stream_invalid_details]) + if len(invalid_details) == 0: + non_stream_output_dict = self._gather_iter_outputs(non_stream_outputs) + invalid_details.extend(self._check_text_and_code(component_case, non_stream_output_dict)) + except Exception as e: + invalid_details.append(" NonStreamToolEval执行失败: {}".format(e)) + + if len(invalid_details) > 0: + return CheckInfo( + check_rule_name=self.rule_name, + check_result=False, + check_detail=",".join(invalid_details)) + else: + return CheckInfo( + check_rule_name=self.rule_name, + check_result=True, + check_detail="") + register_component_check_rule("ManifestValidRule", ManifestValidRule) register_component_check_rule("MainfestMatchToolEvalRule", MainfestMatchToolEvalRule) -register_component_check_rule("ToolEvalInputNameRule", ToolEvalInputNameRule) \ No newline at end of file +register_component_check_rule("ToolEvalInputNameRule", ToolEvalInputNameRule) +register_component_check_rule("ToolEvalOutputJsonRule", ToolEvalOutputJsonRule) \ No newline at end of file diff --git a/python/tests/component_collector.py b/python/tests/component_collector.py index 0d7381b7e..36272a110 100644 --- a/python/tests/component_collector.py +++ b/python/tests/component_collector.py @@ -69,6 +69,7 @@ "ImageUnderstand", "MixCardOCR", "DocumentUnderstanding", + "TreeMind" ] diff --git a/python/tests/component_output_schemas.py b/python/tests/component_output_schemas.py new file mode 100644 index 000000000..3869020fa --- /dev/null +++ b/python/tests/component_output_schemas.py @@ -0,0 +1,268 @@ +# Copyright (c) 2024 Baidu, Inc. 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 copy +base_item_schema = { + "$schema": "base iter template", + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "name": { + "type": "string" + }, + "text": { + "type": "object" + }, + "visible_scope": { + "enum": ["llm", "user", "all"] + }, + "raw_data": { + "type": "object" + }, + "usage": { + "type": "object", + "properties": { + "prompt_tokens": { + "type": "integer" + }, + "completion_tokens": { + "type": "integer" + }, + "total_tokens": { + "type": "integer" + }, + "name": { + "type": "string" + } + }, + }, + "metrics": { + "type": "object", + "properties": { + "first_token_time(s)": { + "type": "number" + }, + "total_time(s)": { + "type": "number" + }, + "memory_used(MB)": { + "type": "number" + } + } + } + }, + "required": ["type", "text"] +} + +text_schema = copy.deepcopy(base_item_schema) +text_schema["$schema"] = "text_schema" +text_schema["properties"]["type"] = { + "type": "string", + "enum": ["text"] +} +text_schema["properties"]["text"] = { + "type": "object", + "properties": { + "info": { + "type": "string" + } + }, + "required": ["info"] +} + +code_schema = copy.deepcopy(base_item_schema) +code_schema["$schema"] = "code_schema" +code_schema["properties"]["type"] = { + "type": "string", + "enum": ["code"] +} +code_schema["properties"]["text"] = { + "type": "object", + "properties": { + "code": { + "type": "string" + } + }, + "required": ["code"] +} + +file_schema = copy.deepcopy(base_item_schema) +file_schema["$schema"] = "file_schema" +file_schema["properties"]["type"] = { + "type": "string", + "enum": ["file"] +} +file_schema["properties"]["text"] = { + "type": "object", + "properties": { + "filename": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": ["filename", "url"] +} + +url_schema = copy.deepcopy(base_item_schema) +url_schema["$schema"] = "url_schema" +url_schema["properties"]["type"] = { + "type": "string", + "enum": ["url"] +} +url_schema["properties"]["text"] = { + "type": "object", + "properties": { + "url": { + "type": "string" + } + }, + "required": ["url"] +} + +oral_text_schema = copy.deepcopy(base_item_schema) +oral_text_schema["$schema"] = "oral_text_schema" +oral_text_schema["properties"]["type"] = { + "type": "string", + "enum": ["oral_text"] +} +oral_text_schema["properties"]["text"] = { + "type": "object", + "properties": { + "info": { + "type": "string" + } + }, + "required": ["info"] +} + +references_schema = copy.deepcopy(base_item_schema) +references_schema["$schema"] = "references_schema" +references_schema["properties"]["type"] = "references" +references_schema["properties"]["text"] = { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "resource_type": { + "type": "string" + }, + "doc_id": { + "type": "string" + }, + "icon": { + "type": "string", + }, + "site_name": { + "type": "string" + }, + "content": { + "type": "string" + }, + "title": { + "type": "string" + }, + "mock_id": { + "type": "string" + }, + "from": { + "type": "string" + }, + "image_url": { + "type": "string" + }, + "video_url": { + "type": "string" + } + }, + "required": ["type", "resource_type", "doc_id", "icon", "site_name", "content", "title", "mock_id", "from", "image_url", "video_url"] +} + +image_schema = copy.deepcopy(base_item_schema) +image_schema["$schema"] = "image_schema" +image_schema["properties"]["type"] = { + "type": "string", + "enum": ["image"] +} +image_schema["properties"]["text"] = { + "type": "object", + "properties": { + "filename": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": ["filename", "url"] +} + +chart_schema = copy.deepcopy(base_item_schema) +chart_schema["$schema"] = "chart_schema" +chart_schema["properties"]["type"] = { + "type": "string", + "enum": ["chart"] +} +chart_schema["properties"]["text"] = { + "type": "object", + "properties": { + "filename": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": ["filename", "url"] +} + +audio_schema = copy.deepcopy(base_item_schema) +audio_schema["$schema"] = "audio_schema" +audio_schema["properties"]["type"] = { + "type": "string", + "enum": ["audio"] +} +audio_schema["properties"]["text"] = { + "type": "object", + "properties": { + "filename": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": ["filename", "url"] +} + +type_to_json_schemas = { + "text": text_schema, + 'code': code_schema, + "files": file_schema, + "urls": url_schema, + "oral_text": oral_text_schema, + "references": references_schema, + "image": image_schema, + "chart": chart_schema, + "audio": audio_schema +} + +components_tool_eval_output_json_maps = { + "AnimalRecognition": [text_schema], + "ASR": [text_schema], + "TreeMind": [text_schema, url_schema] +} \ No newline at end of file diff --git a/python/tests/component_tool_eval_cases.py b/python/tests/component_tool_eval_cases.py new file mode 100644 index 000000000..38e6519ee --- /dev/null +++ b/python/tests/component_tool_eval_cases.py @@ -0,0 +1,47 @@ +# Copyright (c) 2023 Baidu, Inc. 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. + +class AnimalRecognitionCase: + def inputs(self): + return { + "img_name": "", + "img_url": "https://bj.bcebos.com/v1/appbuilder/animal_recognize_test.png?" \ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-24T" \ + "12%3A19%3A16Z%2F-1%2Fhost%2F411bad53034fa8f9c6edbe5c4909d76ecf6fad68" \ + "62cf937c03f8c5260d51c6ae" + } + def outputs(self): + return {"text": ["熊猫"]} + +class ASRCase: + def inputs(self): + return { + "file_url": "https://bj.bcebos.com/v1/appbuilder/asr_test.pcm?authorization=bce-auth-v1" \ + "%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-11T10%3A56%3A41Z%2F-1%2Fhost" \ + "%2Fa6c4d2ca8a3f0259f4cae8ae3fa98a9f75afde1a063eaec04847c99ab7d1e411" + } + def outputs(self): + return {"text": ["北京科技馆"]} + +class TreeMindCase: + def inputs(self): + return {"query": "生成一份年度总结的思维导图"} + + + +component_tool_eval_cases = { + "AnimalRecognition": AnimalRecognitionCase, + "ASR": ASRCase, + "TreeMind": TreeMindCase +} \ No newline at end of file diff --git a/python/tests/run_python_test.sh b/python/tests/run_python_test.sh index 00f1de742..1375643e6 100644 --- a/python/tests/run_python_test.sh +++ b/python/tests/run_python_test.sh @@ -57,6 +57,7 @@ python3 -m pip uninstall -y pydantic python3 -m pip install pydantic==2.7.4 python3 -m pip install langchain==0.3.0 python3 -m pip install datamodel-code-generator==0.25.8 +python3 -m pip install jsonschema cd python/tests/ diff --git a/python/tests/test_all_components.py b/python/tests/test_all_components.py index b553c4408..1f3920d23 100644 --- a/python/tests/test_all_components.py +++ b/python/tests/test_all_components.py @@ -81,7 +81,7 @@ def test_component(self): component_names = error_df["Component Name"].tolist() for component_name in component_names: if component_name in self.whitelist_components: - print("{}zu白名单中,暂时忽略报错。".format(component_name)) + print("{}在白名单中,暂时忽略报错。".format(component_name), flush=True) else: raise AppbuilderBuildexException(f"组件 {component_name} 未在白名单中,请检查是否需要添加到白名单。") From b5cc146658e91569922a3206d840d0f676956a2b Mon Sep 17 00:00:00 2001 From: Dong Daxiang <35550832+guru4elephant@users.noreply.github.com> Date: Mon, 18 Nov 2024 19:24:32 +0800 Subject: [PATCH 18/85] Update knowledgebase.md --- docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md b/docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md index fa32f386e..2cccf7ecb 100644 --- a/docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md +++ b/docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md @@ -653,7 +653,7 @@ my_knowledge_base_id = "your_knowledge_base_id" my_knowledge = appbuilder.KnowledgeBase(my_knowledge_base_id) print("知识库ID: ", my_knowledge.knowledge_id) -doc_list = knowledge.get_all_documents(my_knowledge_base_id) +doc_list = my_knowledge.get_all_documents(my_knowledge_base_id) for message in doc_list: print(message) ``` @@ -1243,4 +1243,4 @@ func TestChunk(t *testing.T) { t.Fatalf("delete chunk failed: %v", err) } } -``` \ No newline at end of file +``` From 4a5ac28afd3f2f3f3ee91a8d83161301b7d569aa Mon Sep 17 00:00:00 2001 From: userpj Date: Tue, 19 Nov 2024 10:43:27 +0800 Subject: [PATCH 19/85] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=9F=A5=E8=AF=86?= =?UTF-8?q?=E5=BA=93=E6=96=87=E6=A1=A3=E5=88=97=E8=A1=A8=E7=A4=BA=E4=BE=8B?= =?UTF-8?q?=E4=BB=A3=E7=A0=81bug=20(#604)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Python/appbuilder.core.console.knowledge_base.md | 2 +- docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md | 2 +- python/core/console/knowledge_base/knowledge_base.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/API-Reference/Python/appbuilder.core.console.knowledge_base.md b/docs/API-Reference/Python/appbuilder.core.console.knowledge_base.md index c9c5e75d1..78f3deacf 100644 --- a/docs/API-Reference/Python/appbuilder.core.console.knowledge_base.md +++ b/docs/API-Reference/Python/appbuilder.core.console.knowledge_base.md @@ -17,7 +17,7 @@ my_knowledge_base_id = "your_knowledge_base_id" my_knowledge = appbuilder.KnowledgeBase(my_knowledge_base_id) print("知识库ID: ", my_knowledge.knowledge_id) -list_res = my_knowledge.get_documents_list(my_knowledge_base_id) +list_res = my_knowledge.get_documents_list() print("文档列表: ", list_res) ``` diff --git a/docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md b/docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md index 2cccf7ecb..f5503dbc9 100644 --- a/docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md +++ b/docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md @@ -610,7 +610,7 @@ my_knowledge_base_id = "your_knowledge_base_id" my_knowledge = appbuilder.KnowledgeBase(my_knowledge_base_id) print("知识库ID: ", my_knowledge.knowledge_id) -list_res = my_knowledge.get_documents_list(my_knowledge_base_id) +list_res = my_knowledge.get_documents_list() print("文档列表: ", list_res) # 知识库ID: da51a988-cbe7-4b24-aa5b-768985e8xxxx diff --git a/python/core/console/knowledge_base/knowledge_base.py b/python/core/console/knowledge_base/knowledge_base.py index 87e6aeb53..b2f6b1775 100644 --- a/python/core/console/knowledge_base/knowledge_base.py +++ b/python/core/console/knowledge_base/knowledge_base.py @@ -42,7 +42,7 @@ class KnowledgeBase(Component): my_knowledge = appbuilder.KnowledgeBase(my_knowledge_base_id) print("知识库ID: ", my_knowledge.knowledge_id) - list_res = my_knowledge.get_documents_list(my_knowledge_base_id) + list_res = my_knowledge.get_documents_list() print("文档列表: ", list_res) """ From 5e9d83f2d44a9ab27ab0007216847c0ea520e7c9 Mon Sep 17 00:00:00 2001 From: C9luster <138663536+C9luster@users.noreply.github.com> Date: Wed, 20 Nov 2024 15:48:45 +0800 Subject: [PATCH 20/85] =?UTF-8?q?=E6=9B=B4=E6=94=B9Assistant-run=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E4=BC=A0=E5=8F=82=20(#605)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 更改Assistant-run函数传参 * 更新type文件以及cancel新增参数 * 更新模型名称 * 更新模型名称 * 更新模型名称 --------- Co-authored-by: yinjiaqi --- python/core/assistant/threads/runs/runs.py | 60 ++++++++++--------- python/core/assistant/type/thread_type.py | 7 ++- python/tests/test_agent.py | 6 +- .../tests/test_appbuilder_components_trace.py | 4 +- .../tests/test_appbuilder_sentry_trace_on.py | 2 +- python/tests/test_dialog_summary.py | 2 +- python/tests/test_hallucination_detection.py | 2 +- python/tests/test_is_complex_query.py | 2 +- python/tests/test_mrc.py | 2 +- python/tests/test_nl2pandas.py | 2 +- python/tests/test_oral_query_generation.py | 2 +- python/tests/test_playground.py | 2 +- python/tests/test_qa_llm_dialog_summary.py | 2 +- .../test_qa_llm_get_qianfan_model_list.py | 1 - python/tests/test_qa_pair_mining.py | 2 +- python/tests/test_query_decomposition.py | 2 +- python/tests/test_query_rewrite.py | 4 +- python/tests/test_similar_question.py | 2 +- python/tests/test_style_rewrite.py | 4 +- python/tests/test_style_writing.py | 2 +- python/tests/test_tag_extraction.py | 2 +- 21 files changed, 58 insertions(+), 56 deletions(-) diff --git a/python/core/assistant/threads/runs/runs.py b/python/core/assistant/threads/runs/runs.py index 11562934b..efcd4ae42 100644 --- a/python/core/assistant/threads/runs/runs.py +++ b/python/core/assistant/threads/runs/runs.py @@ -51,9 +51,9 @@ def run(self, thread: Optional[thread_type.AssistantThread] = None, model: Optional[str] = None, response_format: Optional[str] = "text", - instructions: Optional[str] = "", - thought_instructions: Optional[str] = "", - chat_instructions: Optional[str] = "", + instructions: Optional[str] = None, + thought_instructions: Optional[str] = None, + chat_instructions: Optional[str] = None, tools: Optional[list[assistant_type.AssistantTool]] = [], metadata: Optional[dict] = {}, tool_output: Optional[thread_type.ToolOutput] = None, @@ -68,9 +68,9 @@ def run(self, thread (Optional[thread_type.AssistantThread], optional): 对话信息. Defaults to None. model (Optional[str], optional): 模型名称. Defaults to None. response_format (Optional[str], optional): 返回格式. Defaults to "text". - instructions (Optional[str], optional): 指令信息. Defaults to "". - thought_instructions (Optional[str], optional): 思考指令信息. Defaults to "". - chat_instructions (Optional[str], optional): 闲聊指令信息. Defaults to "". + instructions (Optional[str], optional): 指令信息. Defaults to None. + thought_instructions (Optional[str], optional): 思考指令信息. Defaults to None. + chat_instructions (Optional[str], optional): 闲聊指令信息. Defaults to None. tools (Optional[list[assistant_type.AssistantTool]], optional): 工具列表. Defaults to []. metadata (Optional[dict], optional): 元数据. Defaults to {}. tool_output (Optional[thread_type.ToolOutput], optional): 工具输出. Defaults to None. @@ -149,9 +149,9 @@ def _stream(self, thread: Optional[thread_type.AssistantThread] = None, model: Optional[str] = None, response_format: Optional[str] = "text", - instructions: Optional[str] = "", - thought_instructions: Optional[str] = "", - chat_instructions: Optional[str] = "", + instructions: Optional[str] = None, + thought_instructions: Optional[str] = None, + chat_instructions: Optional[str] = None, tools: Optional[list[assistant_type.AssistantTool]] = [], metadata: Optional[dict] = {}, tool_output: Optional[thread_type.ToolOutput] = None, @@ -168,9 +168,9 @@ def _stream(self, thread (Optional[thread_type.AssistantThread], optional): 线程对象,用于恢复历史对话。默认为None。 model (Optional[str], optional): 使用的模型名称。默认为None。 response_format (Optional[str], optional): 响应格式,支持"text"和"json"两种格式。默认为"text"。 - instructions (Optional[str], optional): 指令文本。默认为空字符串。 - thought_instructions (Optional[str], optional): 思考指令文本。默认为空字符串。 - chat_instructions (Optional[str], optional): 聊天指令文本。默认为空字符串。 + instructions (Optional[str], optional): 指令文本。默认为 None。 + thought_instructions (Optional[str], optional): 思考指令文本。默认为 None。 + chat_instructions (Optional[str], optional): 聊天指令文本。默认为 None。 tools (Optional[list[assistant_type.AssistantTool]], optional): 使用的工具列表。默认为空列表。 metadata (Optional[dict], optional): 元数据字典。默认为空字典。 tool_output (Optional[thread_type.ToolOutput], optional): 工具输出对象。默认为None。 @@ -249,9 +249,9 @@ def stream_run(self, thread: Optional[thread_type.AssistantThread] = None, model: Optional[str] = None, response_format: Optional[str] = "text", - instructions: Optional[str] = "", - thought_instructions: Optional[str] = "", - chat_instructions: Optional[str] = "", + instructions: Optional[str] = None, + thought_instructions: Optional[str] = None, + chat_instructions: Optional[str] = None, tools: Optional[list[assistant_type.AssistantTool]] = [], metadata: Optional[dict] = {}, tool_output: Optional[thread_type.ToolOutput] = None, @@ -268,9 +268,9 @@ def stream_run(self, thread (Optional[thread_type.AssistantThread], optional): 线程对象,用于恢复历史对话。默认为None。 model (Optional[str], optional): 使用的模型名称。默认为None。 response_format (Optional[str], optional): 响应格式,支持"text"和"json"两种格式。默认为"text"。 - instructions (Optional[str], optional): 指令文本。默认为空字符串。 - thought_instructions (Optional[str], optional): 思考指令文本。默认为空字符串。 - chat_instructions (Optional[str], optional): 聊天指令文本。默认为空字符串。 + instructions (Optional[str], optional): 指令文本。默认为 None。 + thought_instructions (Optional[str], optional): 思考指令文本。默认为 None。 + chat_instructions (Optional[str], optional): 聊天指令文本。默认为 None。 tools (Optional[list[assistant_type.AssistantTool]], optional): 使用的工具列表。默认为空列表。 metadata (Optional[dict], optional): 元数据字典。默认为空字典。 tool_output (Optional[thread_type.ToolOutput], optional): 工具输出对象。默认为None。 @@ -315,9 +315,9 @@ def stream_run_with_handler(self, thread: Optional[thread_type.AssistantThread] = None, model: Optional[str] = None, response_format: Optional[str] = "text", - instructions: Optional[str] = "", - thought_instructions: Optional[str] = "", - chat_instructions: Optional[str] = "", + instructions: Optional[str] = None, + thought_instructions: Optional[str] = None, + chat_instructions: Optional[str] = None, tools: Optional[list[assistant_type.AssistantTool]] = [], metadata: Optional[dict] = {}, tool_output: Optional[thread_type.ToolOutput] = None, @@ -335,9 +335,9 @@ def stream_run_with_handler(self, thread (Optional[thread_type.AssistantThread], optional): 会话线程对象,默认为None. 默认为 None. model (Optional[str], optional): 模型标识符,默认为None. 默认为 None. response_format (Optional[str], optional): 响应格式,可选值为"text"或"json",默认为"text". 默认为 "text". - instructions (Optional[str], optional): 主要指令,默认为空字符串. 默认为 "". - thought_instructions (Optional[str], optional): 思维指令,默认为空字符串. 默认为 "". - chat_instructions (Optional[str], optional): 聊天指令,默认为空字符串. 默认为 "". + instructions (Optional[str], optional): 主要指令,默认为空字符串. 默认为 None. + thought_instructions (Optional[str], optional): 思维指令,默认为空字符串. 默认为 None. + chat_instructions (Optional[str], optional): 聊天指令,默认为空字符串. 默认为 None. tools (Optional[list[assistant_type.AssistantTool]], optional): 助手工具列表,默认为空列表. 默认为 []. metadata (Optional[dict], optional): 元数据字典,默认为空字典. 默认为 {}. tool_output (Optional[thread_type.ToolOutput], optional): 工具输出对象,默认为None. 默认为 None. @@ -453,24 +453,26 @@ def submit_tool_outputs(self, return resp @assistent_tool_trace - def cancel(self, run_id: str, thread_id: str) -> thread_type.RunResult: + def cancel(self, run_id: str, thread_id: str, sync: bool = False) -> thread_type.RunResult: """ 取消指定线程的运行 - + Args: run_id (str): 运行的ID thread_id (str): 线程的ID - + sync (bool, optional): 是否同步执行. 默认为False. + Returns: thread_type.RunResult: 取消运行的结果 - + """ headers = self._http_client.auth_header() url = self._http_client.service_url("/v2/threads/runs/cancel") req = thread_type.AssistantRunCancelRequest( run_id=run_id, - thread_id=thread_id + thread_id=thread_id, + sync = sync ) response = self._http_client.session.post( diff --git a/python/core/assistant/type/thread_type.py b/python/core/assistant/type/thread_type.py index 74251ff65..ec467cfde 100644 --- a/python/core/assistant/type/thread_type.py +++ b/python/core/assistant/type/thread_type.py @@ -338,9 +338,9 @@ class AssistantRunRequest(BaseModel): assistant_id: Optional[str] = Field(default="") metadata: Optional[dict] = Field(default={}, max_length=16) response_format: ResponseFormat = Field(default=ResponseFormat.TEXT) - instructions: Optional[str] = Field(default="", max_length=4096) - thought_instructions: Optional[str] = Field(default="", max_length=4096) - chat_instructions: Optional[str] = Field(default="", max_length=4096) + instructions: Optional[str] = Field(default= None, max_length=4096) + thought_instructions: Optional[str] = Field(default=None, max_length=4096) + chat_instructions: Optional[str] = Field(default=None, max_length=4096) stream: Optional[bool] = False model_parameters: Optional[AssistantModelParameters] = AssistantModelParameters() class Config: @@ -362,6 +362,7 @@ class AssistantSubmitToolOutputsRequest(BaseModel): class AssistantRunCancelRequest(BaseModel): thread_id: str = Field(default="", min_length=1) run_id: str = Field(default="", min_length=1) + sync:bool = False class RunListOrderEnum(str, Enum): diff --git a/python/tests/test_agent.py b/python/tests/test_agent.py index c03f20ddc..0cde85ad8 100644 --- a/python/tests/test_agent.py +++ b/python/tests/test_agent.py @@ -128,7 +128,7 @@ def test_init_with_valid_component(self): """ 测试在component有效时运行 """ component = Playground( prompt_template="{query}", - model="eb-4" + model="ERNIE-3.5-8K" ) agent = AgentRuntime(component=component) @@ -142,7 +142,7 @@ def test_chat_with_valid_message_and_blocking(self): """ 测试在消息有效时处理 """ component = Playground( prompt_template="{query}", - model="eb-4" + model="ERNIE-3.5-8K" ) agent = appbuilder.AgentRuntime(component=component) message = appbuilder.Message({"query": "你好"}) @@ -153,7 +153,7 @@ def test_chat_with_valid_message_and_streaming(self): """ 测试在消息有效时处理 """ component = Playground( prompt_template="{query}", - model="eb-4" + model="ERNIE-3.5-8K" ) agent = AgentRuntime(component=component) message = Message({"query": "你好"}) diff --git a/python/tests/test_appbuilder_components_trace.py b/python/tests/test_appbuilder_components_trace.py index a33b4ef7a..494651923 100644 --- a/python/tests/test_appbuilder_components_trace.py +++ b/python/tests/test_appbuilder_components_trace.py @@ -54,8 +54,8 @@ def setUp(self): "%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-11T10%3A56%3A41Z%2F-1%2Fhost" \ "%2Fa6c4d2ca8a3f0259f4cae8ae3fa98a9f75afde1a063eaec04847c99ab7d1e411" self.asr = appbuilder.ASR() - self.play = appbuilder.Playground(prompt_template="你好,{name},我是{bot_name},{bot_name}是一个{bot_type},我可以{bot_function},你可以问我{bot_question}。", model='eb-4') - model_name = 'Qianfan-Agent-Speed-8k' + self.play = appbuilder.Playground(prompt_template="你好,{name},我是{bot_name},{bot_name}是一个{bot_type},我可以{bot_function},你可以问我{bot_question}。", model="ERNIE-3.5-8K") + model_name = "ERNIE-3.5-8K" secret_key = os.getenv('SECRET_KEY', None) self.hallucination_detection = appbuilder.HallucinationDetection(model=model_name, secret_key=secret_key) diff --git a/python/tests/test_appbuilder_sentry_trace_on.py b/python/tests/test_appbuilder_sentry_trace_on.py index fa37eeb96..48b66ce12 100644 --- a/python/tests/test_appbuilder_sentry_trace_on.py +++ b/python/tests/test_appbuilder_sentry_trace_on.py @@ -72,7 +72,7 @@ def test_sentry_normal(self): # 启动事务 with sentry_sdk.start_transaction(op="task", name="UT-Components-trace-test"): # test Components run - play = appbuilder.Playground(prompt_template="你好,{name},我是{bot_name},{bot_name}是一个{bot_type},我可以{bot_function},你可以问我{bot_question}。", model='eb-4') + play = appbuilder.Playground(prompt_template="你好,{name},我是{bot_name},{bot_name}是一个{bot_type},我可以{bot_function},你可以问我{bot_question}。", model="ERNIE-3.5-8K") msg = appbuilder.Message({ "name": "小明", "bot_name": "机器人", diff --git a/python/tests/test_dialog_summary.py b/python/tests/test_dialog_summary.py index 2c5edfbf6..9362fa2ee 100644 --- a/python/tests/test_dialog_summary.py +++ b/python/tests/test_dialog_summary.py @@ -28,7 +28,7 @@ def setUp(self): Returns: 无返回值,方法中执行了环境变量的赋值操作。 """ - self.model_name = "Qianfan-Agent-Speed-8k" + self.model_name = "ERNIE-3.5-8K" self.node = appbuilder.DialogSummary(model=self.model_name) def test_run_with_default_params(self): diff --git a/python/tests/test_hallucination_detection.py b/python/tests/test_hallucination_detection.py index a0a429f7e..dec2f82cd 100644 --- a/python/tests/test_hallucination_detection.py +++ b/python/tests/test_hallucination_detection.py @@ -51,7 +51,7 @@ def setUp(self): 无返回值,方法中执行了环境变量的赋值操作。 """ - self.model_name = 'Qianfan-Agent-Speed-8k' + self.model_name = 'ERNIE-3.5-8K' secret_key = os.getenv('SECRET_KEY', None) self.hallucination_detection = appbuilder.HallucinationDetection(model=self.model_name, secret_key=secret_key) diff --git a/python/tests/test_is_complex_query.py b/python/tests/test_is_complex_query.py index 7ae9ee73f..5c2fb6031 100644 --- a/python/tests/test_is_complex_query.py +++ b/python/tests/test_is_complex_query.py @@ -29,7 +29,7 @@ def setUp(self): Returns: 无返回值,方法中执行了环境变量的赋值操作。 """ - self.model_name = "Qianfan-Agent-Speed-8k" + self.model_name = "ERNIE-3.5-8K" self.node = appbuilder.IsComplexQuery(model=self.model_name) def test_run_with_default_params(self): diff --git a/python/tests/test_mrc.py b/python/tests/test_mrc.py index b156b642f..bcf36f89c 100644 --- a/python/tests/test_mrc.py +++ b/python/tests/test_mrc.py @@ -23,7 +23,7 @@ def setUp(self): return mrc class ''' # 设置环境变量和初始化TestMRCComponent实例 - self.model_name = "Qianfan-Agent-Speed-8k" + self.model_name = "ERNIE-3.5-8K" self.mrc = appbuilder.MRC(model=self.model_name) def test_mrc_with_custom_context_list(self): diff --git a/python/tests/test_nl2pandas.py b/python/tests/test_nl2pandas.py index 814755c4a..adde549a8 100644 --- a/python/tests/test_nl2pandas.py +++ b/python/tests/test_nl2pandas.py @@ -25,7 +25,7 @@ def setUp(self): """ 设置环境变量及必要数据。 """ - self.model_name = "Qianfan-Agent-Speed-8k" + self.model_name = "ERNIE-3.5-8K" self.node = appbuilder.Nl2pandasComponent(model=self.model_name) self.table_info = '''表格列信息如下: 学校名 : 清华附小 , 字符串类型,代表小学学校的名称 diff --git a/python/tests/test_oral_query_generation.py b/python/tests/test_oral_query_generation.py index 8d1ffefea..c9b497f17 100644 --- a/python/tests/test_oral_query_generation.py +++ b/python/tests/test_oral_query_generation.py @@ -36,7 +36,7 @@ def setUp(self): 无返回值,方法中执行了环境变量的赋值操作。 """ - self.model_name = 'Qianfan-Agent-Speed-8k' + self.model_name = "ERNIE-3.5-8K" secret_key = os.getenv('SECRET_KEY', None) self.query_generation = appbuilder.OralQueryGeneration(model=self.model_name, secret_key=secret_key) diff --git a/python/tests/test_playground.py b/python/tests/test_playground.py index e45d5c4f7..c0f15e0c2 100644 --- a/python/tests/test_playground.py +++ b/python/tests/test_playground.py @@ -15,7 +15,7 @@ def setUp(self): 无返回值,方法中执行了环境变量的赋值操作。 """ - self.model_name = "eb-4" + self.model_name = "ERNIE-3.5-8K" self.play = appbuilder.Playground(prompt_template="你好,{name},我是{bot_name},{bot_name}是一个{bot_type},我可以{bot_function},你可以问我{bot_question}。", model=self.model_name) def test_run_with_valid_message(self): diff --git a/python/tests/test_qa_llm_dialog_summary.py b/python/tests/test_qa_llm_dialog_summary.py index c22a52625..ff75695d8 100644 --- a/python/tests/test_qa_llm_dialog_summary.py +++ b/python/tests/test_qa_llm_dialog_summary.py @@ -20,7 +20,7 @@ class TestDialogSummaryComponent(unittest.TestCase): def test_run(self): - model_name = "ernie_speed_appbuilder" + model_name = "ERNIE-3.5-8K" text = ("客户:你好,我购买的新笔记本电脑出现了无法连接无线网络的问题。客服:你好,很抱歉听到你遇到了" "网络问题。请问你尝试过重新启动路由器和笔记本电脑吗?客户:是的,我已经尝试过,但问题仍然存在。客服:" "明白了。请问其他设备是否能够成功连接到同一网络?客户:是的,我的手机和平板电脑都可以正常连接。客服:" diff --git a/python/tests/test_qa_llm_get_qianfan_model_list.py b/python/tests/test_qa_llm_get_qianfan_model_list.py index 09b697121..3061466c9 100644 --- a/python/tests/test_qa_llm_get_qianfan_model_list.py +++ b/python/tests/test_qa_llm_get_qianfan_model_list.py @@ -34,7 +34,6 @@ def test_run(self): ) self.assertIn("ERNIE", str(models)) - self.assertIn("AppBuilder", str(models)) self.assertGreater(len(models), 0, "Model list should not be empty") if __name__ == '__main__': diff --git a/python/tests/test_qa_pair_mining.py b/python/tests/test_qa_pair_mining.py index 1fb098cb8..111d0dc06 100644 --- a/python/tests/test_qa_pair_mining.py +++ b/python/tests/test_qa_pair_mining.py @@ -31,7 +31,7 @@ def setUp(self): Returns: 无返回值,方法中执行了环境变量的赋值操作。 """ - self.model_name = "Qianfan-Agent-Speed-8k" + self.model_name = "ERNIE-3.5-8K" self.node = appbuilder.QAPairMining(model=self.model_name) def test_run_with_default_params(self): diff --git a/python/tests/test_query_decomposition.py b/python/tests/test_query_decomposition.py index 64c16f013..41fcee1be 100644 --- a/python/tests/test_query_decomposition.py +++ b/python/tests/test_query_decomposition.py @@ -29,7 +29,7 @@ def setUp(self): Returns: 无返回值,方法中执行了环境变量的赋值操作。 """ - self.model_name = "Qianfan-Agent-Speed-8k" + self.model_name = "ERNIE-3.5-8K" self.node = appbuilder.QueryDecomposition(model=self.model_name) def test_run_with_default_params(self): diff --git a/python/tests/test_query_rewrite.py b/python/tests/test_query_rewrite.py index a2fe94b32..5a136fb87 100644 --- a/python/tests/test_query_rewrite.py +++ b/python/tests/test_query_rewrite.py @@ -32,7 +32,7 @@ def setUp(self): 无返回值。 """ # 设置环境变量和初始化TranslateComponent实例 - self.model_name = "Qianfan-Agent-Speed-8k" + self.model_name = "ERNIE-3.5-8K" self.node = appbuilder.QueryRewrite(model=self.model_name) def test_run_with_default_params(self): @@ -54,7 +54,7 @@ def test_run_with_custom_params(self): def test_run_with_stream_and_temperature(self): """测试不同的 stream 和 temperature 参数值""" - node = appbuilder.QueryRewrite("Qianfan-Agent-Speed-8k") + node = appbuilder.QueryRewrite("ERNIE-3.5-8K") query = ['我应该怎么办理护照?', '您可以查询官网或人工咨询', '我需要准备哪些材料?', '身份证、免冠照片一张以及填写完整的《中国公民因私出国(境)申请表》', '在哪里办'] msg = appbuilder.Message(query) answer = node(msg, rewrite_type="带机器人回复", stream=False, temperature=0.5) diff --git a/python/tests/test_similar_question.py b/python/tests/test_similar_question.py index c021f00ca..97d8b8d46 100644 --- a/python/tests/test_similar_question.py +++ b/python/tests/test_similar_question.py @@ -28,7 +28,7 @@ def setUp(self): Returns: 无返回值,方法中执行了环境变量的赋值操作。 """ - self.model_name = "Qianfan-Agent-Speed-8k" + self.model_name = "ERNIE-3.5-8K" self.node = appbuilder.SimilarQuestion(model=self.model_name) def test_run_with_default_params(self): diff --git a/python/tests/test_style_rewrite.py b/python/tests/test_style_rewrite.py index 33d1ad8a5..4e99b6279 100644 --- a/python/tests/test_style_rewrite.py +++ b/python/tests/test_style_rewrite.py @@ -19,7 +19,7 @@ from appbuilder.core.components.llms.style_rewrite.base import StyleChoices import appbuilder -# @unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestStyleRewriteComponent(unittest.TestCase): def setUp(self): """ @@ -31,7 +31,7 @@ def setUp(self): Returns: 无返回值,方法中执行了环境变量的赋值操作。 """ - self.model_name = "Qianfan-Agent-Speed-8k" + self.model_name = "ERNIE-3.5-8K" self.node = appbuilder.StyleRewrite(model=self.model_name) self.sc=StyleChoices.YINGXIAO diff --git a/python/tests/test_style_writing.py b/python/tests/test_style_writing.py index 98e5b8417..7f8b5c27a 100644 --- a/python/tests/test_style_writing.py +++ b/python/tests/test_style_writing.py @@ -32,7 +32,7 @@ def setUp(self): Returns: 无返回值,方法中执行了环境变量的赋值操作。 """ - self.model_name = "Qianfan-Agent-Speed-8k" + self.model_name = "ERNIE-3.5-8K" self.node = appbuilder.StyleWriting(model=self.model_name) self.sqc=StyleQueryChoices.BILIBILI self.lc=LengthChoices.SHORT diff --git a/python/tests/test_tag_extraction.py b/python/tests/test_tag_extraction.py index 0c6146d87..10ef2ad6a 100644 --- a/python/tests/test_tag_extraction.py +++ b/python/tests/test_tag_extraction.py @@ -25,7 +25,7 @@ def setUp(self): Returns: 无返回值,方法中执行了环境变量的赋值操作。 """ - self.model_name = "Qianfan-Agent-Speed-8k" + self.model_name = "ERNIE-3.5-8K" self.tag_extraction = appbuilder.TagExtraction(model=self.model_name) def test_run_with_default_params(self): From 5d33bdf067eef27d107dac9c41406566ede005d1 Mon Sep 17 00:00:00 2001 From: userpj Date: Wed, 20 Nov 2024 19:57:42 +0800 Subject: [PATCH 21/85] =?UTF-8?q?chatflow=E5=B7=A5=E4=BD=9C=E6=B5=81?= =?UTF-8?q?=E5=BA=94=E7=94=A8=E5=AF=B9=E8=AF=9D=E9=AA=8C=E8=AF=81=EF=BC=8C?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0'=E4=BF=A1=E6=81=AF=E6=94=B6=E9=9B=86?= =?UTF-8?q?=E8=8A=82=E7=82=B9'=E5=9B=9E=E5=A4=8D=E5=8A=9F=E8=83=BD=20(#601?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chatflow工作流应用对话验证,增加'信息收集节点'回复功能 --- go/appbuilder/app_builder_client.go | 70 +++++-- go/appbuilder/app_builder_client_data.go | 84 ++++++-- go/appbuilder/app_builder_client_test.go | 102 +++++++++- .../AppBuilderClientRunRequest.java | 76 +++++++- .../model/appbuilderclient/Event.java | 2 + .../model/appbuilderclient/EventContent.java | 11 ++ .../appbuilder/AppBuilderClientTest.java | 42 ++++ .../assistant/threads/messages/messages.py | 1 - .../appbuilder_client/appbuilder_client.py | 131 ++++++++++--- .../console/appbuilder_client/data_class.py | 52 ++++- .../appbuilder_client/event_handler.py | 182 ++++++++++++------ .../tests/test_appbuilder_client_chatflow.py | 108 +++++++++++ ...ppbuilder_client_chatflow_event_handler.py | 128 ++++++++++++ ...uilder_client_chatflow_event_handler_v2.py | 86 +++++++++ 14 files changed, 947 insertions(+), 128 deletions(-) create mode 100644 python/tests/test_appbuilder_client_chatflow.py create mode 100644 python/tests/test_appbuilder_client_chatflow_event_handler.py create mode 100644 python/tests/test_appbuilder_client_chatflow_event_handler_v2.py diff --git a/go/appbuilder/app_builder_client.go b/go/appbuilder/app_builder_client.go index 1040c4227..c2c6921c8 100644 --- a/go/appbuilder/app_builder_client.go +++ b/go/appbuilder/app_builder_client.go @@ -26,6 +26,7 @@ import ( "net/url" "os" "path/filepath" + "reflect" "strconv" "time" ) @@ -250,17 +251,26 @@ func (t *AppBuilderClient) UploadLocalFile(conversationID string, filePath strin return val.(string), nil } -func (t *AppBuilderClient) Run(conversationID string, query string, fileIDS []string, stream bool) (AppBuilderClientIterator, error) { - if len(conversationID) == 0 { - return nil, errors.New("conversationID mustn't be empty") +func (t *AppBuilderClient) Run(param ...interface{}) (AppBuilderClientIterator, error) { + if len(param) == 0 { + return nil, errors.New("no arguments provided") + } + var err error + var req AppBuilderClientRunRequest + + if reflect.TypeOf(param[0]) == reflect.TypeOf(AppBuilderClientRunRequest{}) { + req = param[0].(AppBuilderClientRunRequest) + } else { + req, err = t.buildAppBuilderClientRunRequest(param...) + if err != nil { + return nil, err + } } - m := map[string]any{ - "app_id": t.appID, - "conversation_id": conversationID, - "query": query, - "file_ids": fileIDS, - "stream": stream, + + if len(req.ConversationID) == 0 { + return nil, errors.New("conversationID mustn't be empty") } + request := http.Request{} serviceURL, err := t.sdkConfig.ServiceURLV2("/app/conversation/runs") @@ -268,33 +278,63 @@ func (t *AppBuilderClient) Run(conversationID string, query string, fileIDS []st return nil, err } + header := t.sdkConfig.AuthHeaderV2() request.URL = serviceURL request.Method = "POST" - header := t.sdkConfig.AuthHeaderV2() header.Set("Content-Type", "application/json") request.Header = header - data, _ := json.Marshal(m) + data, _ := json.Marshal(req) request.Body = NopCloser(bytes.NewReader(data)) - request.ContentLength = int64(len(data)) // 手动设置长度 + request.ContentLength = int64(len(data)) t.sdkConfig.BuildCurlCommand(&request) - resp, err := t.client.Do(&request) if err != nil { return nil, err } - requestID, err := checkHTTPResponse(resp) if err != nil { return nil, fmt.Errorf("requestID=%s, err=%v", requestID, err) } r := NewSSEReader(1024*1024, bufio.NewReader(resp.Body)) - if stream { + if req.Stream { return &AppBuilderClientStreamIterator{requestID: requestID, r: r, body: resp.Body}, nil } return &AppBuilderClientOnceIterator{body: resp.Body}, nil } +func (t *AppBuilderClient) buildAppBuilderClientRunRequest(param ...interface{}) (AppBuilderClientRunRequest, error) { + conversationID, ok := param[0].(string) + if !ok { + return AppBuilderClientRunRequest{}, errors.New("conversationID must be string type") + } + query, ok := param[1].(string) + if !ok { + return AppBuilderClientRunRequest{}, errors.New("query must be string type") + } + + var fileIDS []string + if param[2] != nil { + fileIDS, ok = param[2].([]string) + if !ok { + fileIDS = nil + } + } + + stream, ok := param[3].(bool) + if !ok { + stream = false + } + + return AppBuilderClientRunRequest{ + AppID: t.appID, + ConversationID: conversationID, + Query: query, + Stream: stream, + FileIDs: fileIDS, + }, nil +} + func (t *AppBuilderClient) RunWithToolCall(req AppBuilderClientRunRequest) (AppBuilderClientIterator, error) { if len(req.ConversationID) == 0 { return nil, errors.New("conversationID mustn't be empty") diff --git a/go/appbuilder/app_builder_client_data.go b/go/appbuilder/app_builder_client_data.go index 288c24805..7ca8c5ac0 100644 --- a/go/appbuilder/app_builder_client_data.go +++ b/go/appbuilder/app_builder_client_data.go @@ -23,25 +23,33 @@ import ( ) const ( - CodeContentType = "code" - TextContentType = "text" - ImageContentType = "image" - RAGContentType = "rag" - FunctionCallContentType = "function_call" - AudioContentType = "audio" - VideoContentType = "video" - StatusContentType = "status" + CodeContentType = "code" + TextContentType = "text" + ImageContentType = "image" + RAGContentType = "rag" + FunctionCallContentType = "function_call" + AudioContentType = "audio" + VideoContentType = "video" + StatusContentType = "status" + ChatflowInterruptContentType = "chatflow_interrupt" + PublishMessageContentType = "publish_message" +) + +const ( + ChatflowEventType = "chatflow" ) var TypeToStruct = map[string]reflect.Type{ - CodeContentType: reflect.TypeOf(CodeDetail{}), - TextContentType: reflect.TypeOf(TextDetail{}), - ImageContentType: reflect.TypeOf(ImageDetail{}), - RAGContentType: reflect.TypeOf(RAGDetail{}), - FunctionCallContentType: reflect.TypeOf(FunctionCallDetail{}), - AudioContentType: reflect.TypeOf(AudioDetail{}), - VideoContentType: reflect.TypeOf(VideoDetail{}), - StatusContentType: reflect.TypeOf(StatusDetail{}), + CodeContentType: reflect.TypeOf(CodeDetail{}), + TextContentType: reflect.TypeOf(TextDetail{}), + ImageContentType: reflect.TypeOf(ImageDetail{}), + RAGContentType: reflect.TypeOf(RAGDetail{}), + FunctionCallContentType: reflect.TypeOf(FunctionCallDetail{}), + AudioContentType: reflect.TypeOf(AudioDetail{}), + VideoContentType: reflect.TypeOf(VideoDetail{}), + StatusContentType: reflect.TypeOf(StatusDetail{}), + ChatflowInterruptContentType: reflect.TypeOf(ChatflowInterruptDetail{}), + PublishMessageContentType: reflect.TypeOf(PublishMessageDetail{}), } type AppBuilderClientRunRequest struct { @@ -50,9 +58,11 @@ type AppBuilderClientRunRequest struct { Stream bool `json:"stream"` EndUserID *string `json:"end_user_id"` ConversationID string `json:"conversation_id"` + FileIDs []string `json:"file_ids"` Tools []Tool `json:"tools"` ToolOutputs []ToolOutput `json:"tool_outputs"` ToolChoice *ToolChoice `json:"tool_choice"` + Action *Action `json:"action"` } type Tool struct { @@ -81,6 +91,36 @@ type ToolChoiceFunction struct { Input map[string]interface{} `json:"input"` } +type Action struct { + ActionType string `json:"action_type"` + Paramters *ActionParamters `json:"parameters"` +} + +type ActionParamters struct { + InterruptEvent *ActionInterruptEvent `json:"interrupt_event"` +} + +type ActionInterruptEvent struct { + ID string `json:"id"` + Type string `json:"type"` +} + +func NewResumeAction(eventId string) *Action { + return NewAction("resume", eventId, "chat") +} + +func NewAction(actionType string, eventId string, eventType string) *Action { + return &Action{ + ActionType: actionType, + Paramters: &ActionParamters{ + InterruptEvent: &ActionInterruptEvent{ + ID: eventId, + Type: eventType, + }, + }, + } +} + type AgentBuilderRawResponse struct { RequestID string `json:"request_id"` Date string `json:"date"` @@ -122,7 +162,7 @@ type Event struct { EventType string ContentType string Usage Usage - Detail any // 将any替换为interface{} + Detail any ToolCalls []ToolCall } @@ -185,6 +225,16 @@ type VideoDetail struct { type StatusDetail struct{} +type ChatflowInterruptDetail struct { + InterruptEventID string `json:"interrupt_event_id"` + InterruptEventType string `json:"interrupt_event_type"` +} + +type PublishMessageDetail struct { + Message string `json:"message"` + MessageID string `json:"message_id"` +} + type DefaultDetail struct { URLS []string `json:"urls"` Files []string `json:"files"` diff --git a/go/appbuilder/app_builder_client_test.go b/go/appbuilder/app_builder_client_test.go index 000ba85aa..e6b817566 100644 --- a/go/appbuilder/app_builder_client_test.go +++ b/go/appbuilder/app_builder_client_test.go @@ -574,7 +574,7 @@ func TestAppBuilderClientRunToolChoice(t *testing.T) { input := make(map[string]any) input["city"] = "北京" end_user_id := "go_user_id_0" - i, err := client.RunWithToolCall(AppBuilderClientRunRequest{ + i, err := client.Run(AppBuilderClientRunRequest{ ConversationID: conversationID, AppID: appID, Query: "你能干什么", @@ -610,3 +610,103 @@ func TestAppBuilderClientRunToolChoice(t *testing.T) { t.Logf("%s========== OK: %s ==========%s", "\033[32m", t.Name(), "\033[0m") } } + +func TestAppBuilderClientRunChatflow(t *testing.T) { + var logBuffer bytes.Buffer + + os.Setenv("APPBUILDER_LOGLEVEL", "DEBUG") + + config, err := NewSDKConfig("", "") + if err != nil { + t.Logf("%s========== FAIL: %s ==========%s", "\033[31m", t.Name(), "\033[0m") + t.Fatalf("new http client config failed: %v", err) + } + + appID := "4403205e-fb83-4fac-96d8-943bdb63796f" + client, err := NewAppBuilderClient(appID, config) + if err != nil { + t.Logf("%s========== FAIL: %s ==========%s", "\033[31m", t.Name(), "\033[0m") + t.Fatalf("new AgentBuidler instance failed") + } + + conversationID, err := client.CreateConversation() + if err != nil { + t.Logf("%s========== FAIL: %s ==========%s", "\033[31m", t.Name(), "\033[0m") + t.Fatalf("create conversation failed: %v", err) + } + + i, err := client.Run(AppBuilderClientRunRequest{ + ConversationID: conversationID, + AppID: appID, + Query: "查天气", + Stream: true, + }) + + if err != nil { + t.Logf("%s========== FAIL: %s ==========%s", "\033[31m", t.Name(), "\033[0m") + t.Fatalf("run failed:%v", err) + } + + var interruptId string + for answer, err := i.Next(); err == nil; answer, err = i.Next() { + for _, ev := range answer.Events { + if ev.ContentType == ChatflowInterruptContentType { + if ev.EventType != ChatflowEventType { + t.Logf("%s========== FAIL: %s ==========%s", "\033[31m", t.Name(), "\033[0m") + t.Fatalf("event type error:%v", err) + } + + deatil := ev.Detail.(ChatflowInterruptDetail) + interruptId = deatil.InterruptEventID + break + } + } + } + + if len(interruptId) == 0 { + t.Logf("%s========== FAIL: %s ==========%s", "\033[31m", t.Name(), "\033[0m") + t.Fatalf("interrupt id is empty") + } + + i2, err := client.Run(AppBuilderClientRunRequest{ + ConversationID: conversationID, + AppID: appID, + Query: "我先查个航班动态", + Stream: true, + Action: NewResumeAction(interruptId), + }) + if err != nil { + t.Logf("%s========== FAIL: %s ==========%s", "\033[31m", t.Name(), "\033[0m") + t.Fatalf("run failed:%v", err) + } + + var message string + for answer, err := i2.Next(); err == nil; answer, err = i2.Next() { + for _, ev := range answer.Events { + if ev.ContentType == PublishMessageContentType { + if ev.EventType != ChatflowEventType { + t.Logf("%s========== FAIL: %s ==========%s", "\033[31m", t.Name(), "\033[0m") + t.Fatalf("event type error:%v", err) + } + + detail := ev.Detail.(PublishMessageDetail) + message = detail.Message + break + } + } + } + if len(message) == 0 { + t.Logf("%s========== FAIL: %s ==========%s", "\033[31m", t.Name(), "\033[0m") + t.Fatalf("message is empty") + } + fmt.Println(message) + + // 如果测试失败,则输出缓冲区中的日志 + if t.Failed() { + t.Logf("%s========== FAIL: %s ==========%s", "\033[31m", t.Name(), "\033[0m") + fmt.Println(logBuffer.String()) + } else { // else 紧跟在右大括号后面 + // 测试通过,打印文件名和测试函数名 + t.Logf("%s========== OK: %s ==========%s", "\033[32m", t.Name(), "\033[0m") + } +} diff --git a/java/src/main/java/com/baidubce/appbuilder/model/appbuilderclient/AppBuilderClientRunRequest.java b/java/src/main/java/com/baidubce/appbuilder/model/appbuilderclient/AppBuilderClientRunRequest.java index a1148a0ee..b836f987d 100644 --- a/java/src/main/java/com/baidubce/appbuilder/model/appbuilderclient/AppBuilderClientRunRequest.java +++ b/java/src/main/java/com/baidubce/appbuilder/model/appbuilderclient/AppBuilderClientRunRequest.java @@ -19,6 +19,7 @@ public class AppBuilderClientRunRequest { private ToolOutput[] ToolOutputs; @SerializedName("tool_choice") private ToolChoice ToolChoice; + private Action action; public AppBuilderClientRunRequest() { } @@ -123,6 +124,14 @@ public void setToolChoice(ToolChoice toolChoice) { this.ToolChoice = toolChoice; } + public Action getAction() { + return action; + } + + public void setAction(Action action) { + this.action = action; + } + public static class Tool { private String type; private Function function; @@ -189,9 +198,9 @@ public static class ToolChoice { private String type; private Function function; - public ToolChoice(String type, Function function) { - this.type=type; - this.function=function; + public ToolChoice(String type, Function function) { + this.type = type; + this.function = function; } public String getType() { @@ -220,4 +229,65 @@ public Map getInput() { } } } + + public static class Action { + @SerializedName("action_type") + private String actionType; + private Parameters parameters; + + // 回复消息节点构造方法 + public static Action createAction(String interruptId) { + return createAction("resume", interruptId, "chat"); + } + + public static Action createAction(String actionType, String id, String type) { + Parameters.InterruptEvent interruptEvent = new Parameters.InterruptEvent(id, type); + Parameters parameters = new Parameters(interruptEvent); + return new Action(actionType, parameters); + } + + public Action(String actionType, Parameters parameters) { + this.actionType = actionType; + this.parameters = parameters; + } + + public String getActionType() { + return actionType; + } + + public Parameters getParameters() { + return parameters; + } + + public static class Parameters { + @SerializedName("interrupt_event") + private InterruptEvent interruptEvent; + + public Parameters(InterruptEvent interruptEvent) { + this.interruptEvent = interruptEvent; + } + + public InterruptEvent getInterruptEvent() { + return interruptEvent; + } + + public static class InterruptEvent { + private String id; + private String type; + + public InterruptEvent(String id, String type) { + this.id = id; + this.type = type; + } + + public String getId() { + return id; + } + + public String getType() { + return type; + } + } + } + } } diff --git a/java/src/main/java/com/baidubce/appbuilder/model/appbuilderclient/Event.java b/java/src/main/java/com/baidubce/appbuilder/model/appbuilderclient/Event.java index e2da38e61..fe7248974 100644 --- a/java/src/main/java/com/baidubce/appbuilder/model/appbuilderclient/Event.java +++ b/java/src/main/java/com/baidubce/appbuilder/model/appbuilderclient/Event.java @@ -4,6 +4,8 @@ import com.google.gson.annotations.SerializedName; public class Event { + public static final String ChatflowEventType = "chatflow"; + private String code; private String message; private String eventType; diff --git a/java/src/main/java/com/baidubce/appbuilder/model/appbuilderclient/EventContent.java b/java/src/main/java/com/baidubce/appbuilder/model/appbuilderclient/EventContent.java index 2ade51b70..4c6a672af 100644 --- a/java/src/main/java/com/baidubce/appbuilder/model/appbuilderclient/EventContent.java +++ b/java/src/main/java/com/baidubce/appbuilder/model/appbuilderclient/EventContent.java @@ -5,6 +5,17 @@ import java.util.Map; public class EventContent { + public static final String CodeContentType = "code"; + public static final String TextContentType = "text"; + public static final String ImageContentType = "image"; + public static final String RAGContentType = "rag"; + public static final String FunctionCallContentType = "function_call"; + public static final String AudioContentType = "audio"; + public static final String VideoContentType = "video"; + public static final String StatusContentType = "status"; + public static final String ChatflowInterruptContentType = "chatflow_interrupt"; + public static final String PublishMessageContentType = "publish_message"; + @SerializedName("event_code") private String eventCode; @SerializedName("event_message") diff --git a/java/src/test/java/com/baidubce/appbuilder/AppBuilderClientTest.java b/java/src/test/java/com/baidubce/appbuilder/AppBuilderClientTest.java index 44b1764f4..cb33dc655 100644 --- a/java/src/test/java/com/baidubce/appbuilder/AppBuilderClientTest.java +++ b/java/src/test/java/com/baidubce/appbuilder/AppBuilderClientTest.java @@ -13,6 +13,8 @@ import com.baidubce.appbuilder.model.appbuilderclient.AppBuilderClientResult; import com.baidubce.appbuilder.model.appbuilderclient.AppListRequest; import com.baidubce.appbuilder.model.appbuilderclient.AppsDescribeRequest; +import com.baidubce.appbuilder.model.appbuilderclient.Event; +import com.baidubce.appbuilder.model.appbuilderclient.EventContent; import com.baidubce.appbuilder.model.appbuilderclient.AppBuilderClientRunRequest; import org.junit.Before; import org.junit.Test; @@ -21,12 +23,14 @@ public class AppBuilderClientTest { String appId; + String chatflowAppId; @Before public void setUp() { System.setProperty("APPBUILDER_TOKEN", System.getenv("APPBUILDER_TOKEN")); System.setProperty("APPBUILDER_LOGLEVEL", "DEBUG"); appId = "aa8af334-df27-4855-b3d1-0d249c61fc08"; + chatflowAppId = "4403205e-fb83-4fac-96d8-943bdb63796f"; } @Test @@ -120,4 +124,42 @@ public void AppBuilderClientRunToolChoiceTest() throws IOException, AppBuilderSe System.out.println(result); } } + + @Test + public void AppBuilderClientRunChatflowTest() throws IOException, AppBuilderServerException { + AppBuilderClient builder = new AppBuilderClient(chatflowAppId); + String conversationId = builder.createConversation(); + assertNotNull(conversationId); + AppBuilderClientRunRequest request = new AppBuilderClientRunRequest(chatflowAppId, conversationId, "查天气", true); + AppBuilderClientIterator itor = builder.run(request); + assertTrue(itor.hasNext()); + String interruptEventId = ""; + while (itor.hasNext()) { + AppBuilderClientResult result = itor.next(); + for (Event event : result.getEvents()) { + System.out.println(event.getContentType()); + if (event.getContentType().equals(EventContent.ChatflowInterruptContentType)) { + assertEquals(event.getEventType(), Event.ChatflowEventType); + interruptEventId = event.getDetail().get("interrupt_event_id").toString(); + } + } + } + + assert interruptEventId != null && !interruptEventId.isEmpty(); + AppBuilderClientRunRequest request2 = new AppBuilderClientRunRequest(chatflowAppId, conversationId, "我先查个航班动态", + true); + request2.setAction(AppBuilderClientRunRequest.Action.createAction(interruptEventId)); + AppBuilderClientIterator itor2 = builder.run(request2); + assertTrue(itor2.hasNext()); + String message = ""; + while (itor2.hasNext()) { + AppBuilderClientResult result2 = itor2.next(); + for (Event event : result2.getEvents()) { + if (event.getContentType().equals(EventContent.PublishMessageContentType)) { + message = event.getDetail().get("message").toString(); + } + } + } + assert message != null && !message.isEmpty(); + } } diff --git a/python/core/assistant/threads/messages/messages.py b/python/core/assistant/threads/messages/messages.py index 8038ef788..c06eaf5e6 100644 --- a/python/core/assistant/threads/messages/messages.py +++ b/python/core/assistant/threads/messages/messages.py @@ -243,4 +243,3 @@ def files(self, self._http_client.check_assistant_response(request_id, data) response = thread_type.AssistantMessageFilesResponse(**data) return response - \ No newline at end of file diff --git a/python/core/console/appbuilder_client/appbuilder_client.py b/python/core/console/appbuilder_client/appbuilder_client.py index 3442318b9..357580bef 100644 --- a/python/core/console/appbuilder_client/appbuilder_client.py +++ b/python/core/console/appbuilder_client/appbuilder_client.py @@ -16,6 +16,7 @@ import os import json import uuid +import queue from typing import Optional from appbuilder.core.component import Message, Component from appbuilder.core.console.appbuilder_client import data_class @@ -26,6 +27,7 @@ from appbuilder.utils.logger_util import logger from appbuilder.utils.trace.tracer_wrapper import client_run_trace, client_tool_trace + @deprecated(reason="use describe_apps instead") @client_tool_trace def get_app_list( @@ -72,13 +74,14 @@ def get_app_list( out = resp.data return out + @client_tool_trace def describe_apps( - marker: Optional[str]=None, - maxKeys: int=10, + marker: Optional[str] = None, + maxKeys: int = 10, secret_key: Optional[str] = None, gateway: Optional[str] = None -)-> list[data_class.AppOverview]: +) -> list[data_class.AppOverview]: """ 该接口查询用户下状态为已发布的应用列表 @@ -144,11 +147,11 @@ class AppBuilderClient(Component): r""" AppBuilderClient 组件支持调用在[百度智能云千帆AppBuilder](https://cloud.baidu.com/product/AppBuilder)平台上 构建并发布的智能体应用,具体包括创建会话、上传文档、运行对话等。 - + Examples: - + .. code-block:: python - + import appbuilder # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 os.environ["APPBUILDER_TOKEN"] = '...' @@ -160,15 +163,15 @@ class AppBuilderClient(Component): message = client.run(conversation_id, "今天你好吗?") # 打印对话结果 print(message.content) - + """ def __init__(self, app_id: str, **kwargs): r"""初始化智能体应用 - + Args: app_id (str: 必须) : 应用唯一ID - + Returns: response (obj: `AppBuilderClient`): 智能体实例 """ @@ -186,13 +189,13 @@ def create_conversation(self) -> str: r"""创建会话并返回会话ID 会话ID在服务端用于上下文管理、绑定会话文档等,如需开始新的会话,请创建并使用新的会话ID - + Args: 无 - + Returns: response (str): 唯一会话ID - + """ headers = self.http_client.auth_header_v2() headers["Content-Type"] = "application/json" @@ -208,19 +211,20 @@ def create_conversation(self) -> str: @client_tool_trace def upload_local_file(self, conversation_id, local_file_path: str) -> str: r"""上传文件并将文件与会话ID进行绑定,后续可使用该文件ID进行对话,目前仅支持上传xlsx、jsonl、pdf、png等文件格式 - + 该接口用于在对话中上传文件供大模型处理,文件的有效期为7天并且不超过对话的有效期。一次只能上传一个文件。 Args: conversation_id (str) : 会话ID local_file_path (str) : 本地文件路径 - + Returns: response (str): 唯一文件ID - + """ if len(conversation_id) == 0: - raise ValueError("conversation_id is empty, you can run self.create_conversation to get a conversation_id") + raise ValueError( + "conversation_id is empty, you can run self.create_conversation to get a conversation_id") filepath = os.path.abspath(local_file_path) if not os.path.exists(filepath): @@ -250,10 +254,11 @@ def run(self, conversation_id: str, tool_outputs: list[data_class.ToolOutput] = None, tool_choice: data_class.ToolChoice = None, end_user_id: str = None, + action: data_class.Action = None, **kwargs ) -> Message: r"""运行智能体应用 - + Args: query (str): query内容 conversation_id (str): 唯一会话ID,如需开始新的会话,请使用self.create_conversation创建新的会话 @@ -263,9 +268,10 @@ def run(self, conversation_id: str, tool_outputs(list[data_class.ToolOutput]): 工具输出列表,格式为list[ToolOutput], ToolOutputd内容为本地的工具执行结果,以自然语言/json dump str描述,默认为None tool_choice(data_class.ToolChoice): 控制大模型使用组件的方式,默认为None end_user_id (str): 用户ID,用于区分不同用户 + action(data_class.Action): 对话时要进行的特殊操作。如回复工作流agent中“信息收集节点“的消息。 kwargs: 其他参数 - - Returns: + + Returns: message (Message): 对话结果,一个Message对象,使用message.content获取内容。 """ @@ -275,7 +281,8 @@ def run(self, conversation_id: str, ) if query == "" and (tool_outputs is None or len(tool_outputs) == 0): - raise ValueError("AppBuilderClient Run API: query and tool_outputs cannot both be empty") + raise ValueError( + "AppBuilderClient Run API: query and tool_outputs cannot both be empty") req = data_class.AppBuilderClientRequest( app_id=self.app_id, @@ -287,6 +294,7 @@ def run(self, conversation_id: str, tool_outputs=tool_outputs, tool_choice=tool_choice, end_user_id=end_user_id, + action=action, ) headers = self.http_client.auth_header_v2() @@ -308,13 +316,14 @@ def run(self, conversation_id: str, return Message(content=out) def run_with_handler(self, - conversation_id: str, - query: str = "", - file_ids: list = [], - tools: list[data_class.Tool] = None, - stream: bool = False, - event_handler = None, - **kwargs): + conversation_id: str, + query: str = "", + file_ids: list = [], + tools: list[data_class.Tool] = None, + stream: bool = False, + event_handler=None, + action=None, + **kwargs): r"""运行智能体应用,并通过事件处理器处理事件 Args: @@ -324,6 +333,8 @@ def run_with_handler(self, tools(list[data_class.Tools], 可选): 一个Tools组成的列表,其中每个Tools对应一个工具的配置, 默认为None stream (bool): 是否流式响应 event_handler (EventHandler): 事件处理器 + action(dataclass.Action) 对话时要进行的特殊操作。如回复工作流agent中“信息收集节点“的消息。 + kwargs: 其他参数 Returns: @@ -337,11 +348,71 @@ def run_with_handler(self, file_ids=file_ids, tools=tools, stream=stream, + action=action, **kwargs ) return event_handler + def run_multiple_dialog_with_handler(self, + conversation_id: str, + queries: iter = None, + file_ids: iter = None, + tools: iter = None, + stream: bool = False, + event_handler=None, + actions: iter = None, + **kwargs): + r"""运行智能体应用,并通过事件处理器处理事件 + + Args: + conversation_id (str): 唯一会话ID,如需开始新的会话,请使用self.create_conversation创建新的会话 + queries (iter): 查询字符串可迭代对象 + file_ids (iter): 文件ID列表 + tools(iter, 可选): 一个Tools组成的列表,其中每个Tools对应一个工具的配置, 默认为None + stream (bool): 是否流式响应 + event_handler (EventHandler): 事件处理器 + actions(iter) 对话时要进行的特殊操作。如回复工作流agent中“信息收集节点“的消息。 + + kwargs: 其他参数 + Returns: + EventHandler: 事件处理器 + """ + assert event_handler is not None, "event_handler is None" + assert queries is not None, "queries is None" + + iter_queries = iter(queries) + iter_file_ids = iter(file_ids) if file_ids else iter([]) + iter_tools = iter(tools) if tools else iter([]) + iter_actions = iter(actions) if actions else iter([]) + + for index, query in enumerate(iter_queries): + file_id = next(iter_file_ids, None) + tool = next(iter_tools, None) + action = next(iter_actions, None) + + if index == 0: + yield from self.run_with_handler( + conversation_id=conversation_id, + query=query, + file_ids=file_id, + tools=tool, + stream=stream, + event_handler=event_handler, + action=action, + **kwargs, + ) + else: + event_handler.new_dialog( + query=query, + file_ids=file_id, + tools=tool, + stream=stream, + action=action, + ) + yield event_handler + event_handler.reset_state() + @staticmethod def _iterate_events(request_id, events) -> data_class.AppBuilderClientAnswer: for event in events: @@ -374,11 +445,11 @@ class AgentBuilder(AppBuilderClient): r"""AgentBuilder是继承自AppBuilderClient的一个子类,用于构建和管理智能体应用。 支持调用在[百度智能云千帆AppBuilder](https://cloud.baidu.com/product/AppBuilder)平台上 构建并发布的智能体应用,具体包括创建会话、上传文档、运行对话等。 - + Examples: - + .. code-block:: python - + import appbuilder # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 os.environ["APPBUILDER_TOKEN"] = '...' diff --git a/python/core/console/appbuilder_client/data_class.py b/python/core/console/appbuilder_client/data_class.py index b19174f25..cbc06ff31 100644 --- a/python/core/console/appbuilder_client/data_class.py +++ b/python/core/console/appbuilder_client/data_class.py @@ -23,21 +23,26 @@ class Function(BaseModel): description: str = Field(..., description="工具描述") parameters: dict = Field(..., description="工具参数, json_schema格式") + class Tool(BaseModel): type: str = "function" function: Function = Field(..., description="工具信息") + class ToolOutput(BaseModel): tool_call_id: str = Field(..., description="工具调用ID") output: str = Field(..., description="工具输出") + class FunctionCallDetail(BaseModel): name: str = Field(..., description="函数的名称") arguments: dict = Field(..., description="模型希望您传递给函数的参数") + class ToolCall(BaseModel): id: str = Field(..., description="工具调用ID") - type: str = Field("function", description="需要输出的工具调用的类型。就目前而言,这始终是function") + type: str = Field( + "function", description="需要输出的工具调用的类型。就目前而言,这始终是function") function: FunctionCallDetail = Field(..., description="函数定义") @@ -62,6 +67,37 @@ class ToolChoice(BaseModel): ) +class ActionInterruptEvent(BaseModel): + id: str = Field(..., description="要回复的'信息收集节点'中断事件ID") + type: str = Field(..., description="要回复的'信息收集节点'中断事件类型,当前仅chat") + + +class ActionParameters(BaseModel): + interrupt_event: ActionInterruptEvent = Field( + ..., description="要回复的'信息收集节点'中断事件") + + +class Action(BaseModel): + action_type: str = Field(..., + description="action类型,目前可用值'resume', 用于回复信息收集节点的消息") + parameters: ActionParameters = Field( + ..., + description="对话时要进行的特殊操作。如回复工作流agent中'信息收集节点'的消息。", + ) + + @classmethod + def create_resume_action(cls, event_id): + return { + "action_type": "resume", + "parameters": { + "interrupt_event": { + "id": event_id, + "type": "chat" + } + } + } + + class AppBuilderClientRequest(BaseModel): """会话请求参数 属性: @@ -80,6 +116,7 @@ class AppBuilderClientRequest(BaseModel): tool_outputs: Optional[list[ToolOutput]] = None tool_choice: Optional[ToolChoice] = None end_user_id: Optional[str] = None + action: Optional[Action] = None class Usage(BaseModel): @@ -296,11 +333,13 @@ class CreateConversationResponse(BaseModel): class AppBuilderClientAppListRequest(BaseModel): - limit: int = Field(default=10, description="当次查询的数据大小,默认10,最大值100", le=100, ge=1) + limit: int = Field( + default=10, description="当次查询的数据大小,默认10,最大值100", le=100, ge=1) after: str = Field( default="", description="用于分页的游标。after 是一个应用的id,它定义了在列表中的位置。例如,如果你发出一个列表请求并收到 10个对象,以 app_id_123 结束,那么你后续的调用可以包含 after=app_id_123 以获取列表的下一页数据。") before: str = Field(default="", description="用于分页的游标。与after相反,填写它将获取前一页数据") + class AppOverview(BaseModel): id: str = Field("", description="应用ID") name: str = Field("", description="应用名称") @@ -312,14 +351,19 @@ class AppOverview(BaseModel): isPublished: Optional[bool] = Field(None, description="是否已发布") updateTime: Optional[int] = Field(None, description="更新时间。时间戳,单位秒") + class AppBuilderClientAppListResponse(BaseModel): request_id: str = Field("", description="请求ID") data: Optional[list[AppOverview]] = Field( [], description="应用概览列表") + class DescribeAppsRequest(BaseModel): - maxKeys: int = Field(default=10, description="当次查询的数据大小,默认10,最大值100", le=100, ge=1) - marker: str = Field(default=None, description="用于分页的游标。marker 是应用的id,它定义了在列表中的位置。例如,如果你发出一个列表请求并收到 10个对象,以 app_id_123 开始,那么可以使用 marker=app_id_123 来获取列表的下一页数据") + maxKeys: int = Field( + default=10, description="当次查询的数据大小,默认10,最大值100", le=100, ge=1) + marker: str = Field( + default=None, description="用于分页的游标。marker 是应用的id,它定义了在列表中的位置。例如,如果你发出一个列表请求并收到 10个对象,以 app_id_123 开始,那么可以使用 marker=app_id_123 来获取列表的下一页数据") + class DescribeAppsResponse(BaseModel): requestId: str = Field("", description="请求ID") diff --git a/python/core/console/appbuilder_client/event_handler.py b/python/core/console/appbuilder_client/event_handler.py index e2bf40a39..843914043 100644 --- a/python/core/console/appbuilder_client/event_handler.py +++ b/python/core/console/appbuilder_client/event_handler.py @@ -21,13 +21,13 @@ class AppBuilderClientRunContext(object): def __init__(self) -> None: """ 初始化方法。 - + Args: 无参数。 - + Returns: None - + """ self.current_event = None self.current_tool_calls = None @@ -41,18 +41,21 @@ class AppBuilderEventHandler(object): def __init__(self): pass - def init(self, - appbuilder_client, - conversation_id, - query, - file_ids=None, - tools=None, - stream: bool = False, - event_handler=None, - **kwargs): + def init( + self, + appbuilder_client, + conversation_id, + query, + file_ids=None, + tools=None, + stream: bool = False, + event_handler=None, + action=None, + **kwargs + ): """ 初始化类实例并设置相关参数。 - + Args: appbuilder_client (object): AppBuilder客户端实例对象。 conversation_id (str): 对话ID。 @@ -61,11 +64,12 @@ def init(self, tools (list, optional): 工具列表,默认为None。 stream (bool, optional): 是否使用流式处理,默认为False。 event_handler (callable, optional): 事件处理函数,默认为None。 + action (object, optional): 对话时要进行的特殊操作。如回复工作流agent中“信息收集节点“的消息。 **kwargs: 其他可选参数。 - + Returns: None - + """ self._appbuilder_client = appbuilder_client self._conversation_id = conversation_id @@ -78,19 +82,21 @@ def init(self, self._is_complete = False self._need_tool_call = False self._last_tool_output = None + self._action = action - self._iterator = self.__run_process__() if not self._stream else self.__stream_run_process__() + self._iterator = self.__run_process__( + ) if not self._stream else self.__stream_run_process__() def __run_process__(self): """ 运行进程,并在每次执行后生成结果。 - + Args: 无参数。 - + Returns: Generator: 生成器,每次执行后返回结果。 - + """ while not self._is_complete: if not self._need_tool_call: @@ -100,19 +106,19 @@ def __run_process__(self): res = self._submit_tool_output() self.__event_process__(res) yield res - - self.reset_state() + if self._need_tool_call and self._is_complete: + self.reset_state() def __event_process__(self, run_response): """ 处理事件响应。 - + Args: run_response (RunResponse): 运行时响应对象。 - + Returns: None - + Raises: ValueError: 当解析事件时发生异常或工具输出为空时。 """ @@ -120,9 +126,9 @@ def __event_process__(self, run_response): event = run_response.content.events[-1] except Exception as e: raise ValueError(e) - + event_status = event.status - + if event.status == 'success': self._is_complete = True elif event.status == 'interrupt': @@ -136,9 +142,11 @@ def __event_process__(self, run_response): "interrupt": self.interrupt, "success": self.success, } - + run_context = AppBuilderClientRunContext() self._update_run_context(run_context, run_response.content) + self.handle_event_type(run_context, run_response.content) + self.handle_content_type(run_context, run_response.content) if event_status in context_func_map: func = context_func_map[event_status] func_res = func(run_context, run_response.content) @@ -150,11 +158,13 @@ def __event_process__(self, run_response): else: if not isinstance(func_res[0], data_class.ToolOutput): try: - check_tool_output = data_class.ToolOutput(**func_res[0]) + check_tool_output = data_class.ToolOutput( + **func_res[0]) except Exception as e: - logger.error("func interrupt's output should be list[ToolOutput] or list[dict(can be trans to ToolOutput)]") + logger.error( + "func interrupt's output should be list[ToolOutput] or list[dict(can be trans to ToolOutput)]") raise ValueError(e) - self._last_tool_output =func_res + self._last_tool_output = func_res else: logger.warning( "Unknown status: {}, response data: {}".format(event_status, run_response)) @@ -162,13 +172,13 @@ def __event_process__(self, run_response): def __stream_run_process__(self): """ 流式运行处理函数 - + Args: 无参数。 - + Returns: Generator[Any, None, None]: 返回处理结果的生成器。 - + """ while not self._is_complete: if not self._need_tool_call: @@ -176,18 +186,18 @@ def __stream_run_process__(self): else: res = self._submit_tool_output() for msg in self.__stream_event_process__(res): - yield msg + yield msg def __stream_event_process__(self, run_response): """ 处理流事件,并调用对应的方法 - + Args: run_response: 包含流事件信息的响应对象 - + Returns: None - + Raises: ValueError: 当处理事件时发生异常或中断时工具输出为空时 """ @@ -198,9 +208,9 @@ def __stream_event_process__(self, run_response): event = msg.events[-1] except Exception as e: raise ValueError(e) - + event_status = event.status - + if event.status == 'success': self._is_complete = True elif event.status == 'interrupt': @@ -214,9 +224,11 @@ def __stream_event_process__(self, run_response): "interrupt": self.interrupt, "success": self.success, } - + run_context = AppBuilderClientRunContext() self._update_run_context(run_context, msg) + self.handle_event_type(run_context, msg) + self.handle_content_type(run_context, msg) if event_status in context_func_map: func = context_func_map[event_status] func_res = func(run_context, msg) @@ -228,32 +240,34 @@ def __stream_event_process__(self, run_response): else: if not isinstance(func_res[0], data_class.ToolOutput): try: - check_tool_output = data_class.ToolOutput(**func_res[0]) + check_tool_output = data_class.ToolOutput( + **func_res[0]) except Exception as e: - logger.info("func interrupt's output should be list[ToolOutput] or list[dict(can be trans to ToolOutput)]") + logger.info( + "func interrupt's output should be list[ToolOutput] or list[dict(can be trans to ToolOutput)]") raise ValueError(e) - self._last_tool_output =func_res + self._last_tool_output = func_res else: logger.warning( "Unknown status: {}, response data: {}".format(event_status, run_response)) - + yield msg def _update_run_context(self, run_context, run_response): """ 更新运行上下文。 - + Args: run_context (dict): 运行上下文字典。 run_response (object): 运行响应对象。 - + Returns: None - + """ run_context.current_event = run_response.events[-1] run_context.current_tool_calls = run_context.current_event.tool_calls - run_context.current_status = run_context.current_event.status + run_context.current_status = run_context.current_event.status run_context.need_tool_submit = run_context.current_status == 'interrupt' run_context.is_complete = run_context.current_status == 'success' try: @@ -270,7 +284,8 @@ def _run(self): query=self._query, file_ids=self._file_ids, stream=self._stream, - tools=self._tools + tools=self._tools, + action=self._action, ) return res @@ -297,19 +312,19 @@ def __enter__(self): def __exit__(self, exc_type, exc_val, exc_tb) -> None: if exc_type is not None: raise exc_val - + return def reset_state(self): """ 重置该对象的状态,将所有实例变量设置为默认值。 - + Args: 无 - + Returns: 无 - + """ self._appbuilder_client = None self._conversation_id = None @@ -324,20 +339,73 @@ def reset_state(self): self._need_tool_call = False self._iterator = None + def new_dialog( + self, + query=None, + file_ids=None, + tools=None, + action=None, + stream: bool = None, + event_handler=None, + **kwargs + ): + """ + 重置handler部分参数,用于复用该handler进行多轮对话。 + + Args: + query (str): 用户输入的查询语句。 + file_ids (list, optional): 文件ID列表,默认为None。 + tools (list, optional): 工具列表,默认为None。 + stream (bool, optional): 是否使用流式处理,默认为False。 + action (object, optional): 对话时要进行的特殊操作。如回复工作流agent中“信息收集节点“的消息。 + event_handler (callable, optional): 事件处理函数,默认为None。 + **kwargs: 其他可选参数。 + + Returns: + None + + """ + self._query = query or self._query + self._stream = stream or self._stream + + self._file_ids = file_ids + self._tools = tools + self._event_handler = event_handler + self._kwargs = kwargs + self._action = action + + # 重置部分状态 + self._is_complete = False + self._need_tool_call = False + self._last_tool_output = None + self._iterator = ( + self.__run_process__() + if not self._stream + else self.__stream_run_process__() + ) + def until_done(self): """ 迭代并遍历内部迭代器中的所有元素,直到迭代器耗尽。 - + Args: 无参数。 - + Returns: 无返回值。 - + """ for _ in self._iterator: pass + def handle_content_type(self, run_context, run_response): + # 用户可重载该方法,用于处理不同类型的content_type + pass + + def handle_event_type(self, run_context, run_response): + # 用户可重载该方法,用于处理不同类型的event_type + pass + def interrupt(self, run_context, run_response): # 用户可重载该方法,当event_status为interrupt时,会调用该方法 pass @@ -349,7 +417,7 @@ def preparing(self, run_context, run_response): def running(self, run_context, run_response): # 用户可重载该方法,当event_status为running时,会调用该方法 pass - + def error(self, run_context, run_response): # 用户可重载该方法,当event_status为error时,会调用该方法 pass diff --git a/python/tests/test_appbuilder_client_chatflow.py b/python/tests/test_appbuilder_client_chatflow.py new file mode 100644 index 000000000..c62a4d60f --- /dev/null +++ b/python/tests/test_appbuilder_client_chatflow.py @@ -0,0 +1,108 @@ +# Copyright (c) 2024 Baidu, Inc. 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 unittest +import os +import appbuilder +from appbuilder.core.console.appbuilder_client import data_class + + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") +class TestAppBuilderClientChatflow(unittest.TestCase): + def setUp(self): + """ + 设置环境变量。 + + Args: + 无参数,默认值为空。 + + Returns: + 无返回值,方法中执行了环境变量的赋值操作。 + """ + self.app_id = "4403205e-fb83-4fac-96d8-943bdb63796f" + + def test_appbuilder_run_chatflow(self): + # 如果app_id为空,则跳过单测执行, 避免单测因配置无效而失败 + """ + 如果app_id为空,则跳过单测执行, 避免单测因配置无效而失败 + + Args: + self (unittest.TestCase): unittest的TestCase对象 + + Raises: + None: 如果app_id不为空,则不会引发任何异常 + unittest.SkipTest (optional): 如果app_id为空,则跳过单测执行 + """ + if len(self.app_id) == 0: + self.skipTest("self.app_id is empty") + appbuilder.logger.setLevel("ERROR") + interrupt_ids = [] + builder = appbuilder.AppBuilderClient(self.app_id) + conversation_id = builder.create_conversation() + msg = builder.run(conversation_id, "查天气", stream=True) + + interrupt_event_id = None + for ans in msg.content: + for event in ans.events: + if event.content_type == "chatflow_interrupt": + assert event.event_type == "chatflow" + interrupt_event_id = event.detail.get("interrupt_event_id") + break + self.assertIsNotNone(interrupt_event_id) + interrupt_ids.append(interrupt_event_id) + + msg = builder.run( + conversation_id, + "查航班", + stream=True, + action=data_class.Action.create_resume_action(interrupt_event_id), + ) + interrupt_event_id = None + for ans in msg.content: + for event in ans.events: + if event.content_type == "chatflow_interrupt": + assert event.event_type == "chatflow" + interrupt_event_id = event.detail.get("interrupt_event_id") + break + self.assertIsNotNone(interrupt_event_id) + interrupt_ids.append(interrupt_event_id) + + msg2 = builder.run(conversation_id=conversation_id, + query="CA1234", stream=True, + action=data_class.Action.create_resume_action(interrupt_ids.pop())) + interrupt_event_id = None + for ans in msg2.content: + for event in ans.events: + if event.content_type == "chatflow_interrupt": + assert event.event_type == "chatflow" + interrupt_event_id = event.detail.get("interrupt_event_id") + break + self.assertIsNotNone(interrupt_event_id) + interrupt_ids.append(interrupt_event_id) + + msg2 = builder.run(conversation_id=conversation_id, + query="北京的", stream=True, + action=data_class.Action.create_resume_action(interrupt_ids.pop())) + has_multiple_dialog_event = False + for ans in msg2.content: + for event in ans.events: + if event.content_type == "multiple_dialog_event": + assert event.event_type == "chatflow" + has_multiple_dialog_event = True + break + self.assertTrue(has_multiple_dialog_event) + + +if __name__ == "__main__": + unittest.main() diff --git a/python/tests/test_appbuilder_client_chatflow_event_handler.py b/python/tests/test_appbuilder_client_chatflow_event_handler.py new file mode 100644 index 000000000..db82da1ee --- /dev/null +++ b/python/tests/test_appbuilder_client_chatflow_event_handler.py @@ -0,0 +1,128 @@ +# Copyright (c) 2024 Baidu, Inc. 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 os +import unittest +import appbuilder +from appbuilder.core.console.appbuilder_client.event_handler import ( + AppBuilderEventHandler, +) + + +class MyEventHandler(AppBuilderEventHandler): + def __init__(self): + super().__init__() + self.interrupt_ids = [] + + def handle_content_type(self, run_context, run_response): + interrupt_event_id = None + event = run_response.events[-1] + if event.content_type == "chatflow_interrupt": + interrupt_event_id = event.detail.get("interrupt_event_id") + if interrupt_event_id is not None: + self.interrupt_ids.append(interrupt_event_id) + + def _create_action(self): + if len(self.interrupt_ids) == 0: + return None + event_id = self.interrupt_ids.pop() + return { + "action_type": "resume", + "parameters": {"interrupt_event": {"id": event_id, "type": "chat"}}, + } + + def run(self, query=None): + super().new_dialog( + query=query, + action=self._create_action(), + ) + + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") +class TestAppBuilderClientChatflow(unittest.TestCase): + def setUp(self): + """ + 设置环境变量。 + + Args: + 无参数,默认值为空。 + + Returns: + 无返回值,方法中执行了环境变量的赋值操作。 + """ + self.app_id = "4403205e-fb83-4fac-96d8-943bdb63796f" + + def test_appbuilder_client_run_with_handler_stream(self): + if len(self.app_id) == 0: + self.skipTest("self.app_id is empty") + appbuilder.logger.setLevel("ERROR") + builder = appbuilder.AppBuilderClient(self.app_id) + conversation_id = builder.create_conversation() + + event_handler = MyEventHandler() + event_handler.init(appbuilder_client=builder, + conversation_id=conversation_id, stream=True, query="查天气") + for data in event_handler: + pass + event_handler.run( + query="查航班", + ) + for data in event_handler: + pass + event_handler.run( + query="CA1234", + ) + for data in event_handler: + pass + event_handler.run( + query="北京的", + ) + for data in event_handler: + pass + + def test_appbuilder_client_run_with_handler(self): + if len(self.app_id) == 0: + self.skipTest("self.app_id is empty") + appbuilder.logger.setLevel("ERROR") + builder = appbuilder.AppBuilderClient(self.app_id) + conversation_id = builder.create_conversation() + + event_handler = MyEventHandler() + event_handler.init( + appbuilder_client=builder, + conversation_id=conversation_id, + stream=False, + query="查天气", + ) + for data in event_handler: + pass + event_handler.run( + query="查航班", + ) + for data in event_handler: + pass + event_handler.run( + query="CA1234", + ) + for data in event_handler: + pass + event_handler.run( + query="北京的", + ) + for data in event_handler: + pass + + +if __name__ == "__main__": + unittest.main() diff --git a/python/tests/test_appbuilder_client_chatflow_event_handler_v2.py b/python/tests/test_appbuilder_client_chatflow_event_handler_v2.py new file mode 100644 index 000000000..3ca9cb52b --- /dev/null +++ b/python/tests/test_appbuilder_client_chatflow_event_handler_v2.py @@ -0,0 +1,86 @@ +# Copyright (c) 2024 Baidu, Inc. 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 os +import unittest +import appbuilder +from appbuilder.core.console.appbuilder_client.event_handler import ( + AppBuilderEventHandler, +) + + +class MyEventHandler(AppBuilderEventHandler): + def __init__(self): + super().__init__() + self.interrupt_ids = [] + + def handle_content_type(self, run_context, run_response): + interrupt_event_id = None + event = run_response.events[-1] + if event.content_type == "chatflow_interrupt": + interrupt_event_id = event.detail.get("interrupt_event_id") + if interrupt_event_id is not None: + self.interrupt_ids.append(interrupt_event_id) + + def _create_action(self): + if len(self.interrupt_ids) == 0: + return None + event_id = self.interrupt_ids.pop() + return { + "action_type": "resume", + "parameters": {"interrupt_event": {"id": event_id, "type": "chat"}}, + } + + def gen_action(self): + while True: + yield self._create_action() + + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") +class TestAppBuilderClientChatflow(unittest.TestCase): + def setUp(self): + """ + 设置环境变量。 + + Args: + 无参数,默认值为空。 + + Returns: + 无返回值,方法中执行了环境变量的赋值操作。 + """ + self.app_id = "4403205e-fb83-4fac-96d8-943bdb63796f" + + def test_appbuilder_client_run_with_handler_multiple_dialog(self): + if len(self.app_id) == 0: + self.skipTest("self.app_id is empty") + appbuilder.logger.setLevel("DEBUG") + builder = appbuilder.AppBuilderClient(self.app_id) + conversation_id = builder.create_conversation() + + queries = ["查天气", "查航班", "CA1234", "北京的"] + event_handler = MyEventHandler() + event_handler = builder.run_multiple_dialog_with_handler( + conversation_id=conversation_id, + queries=queries, + event_handler=event_handler, + stream=True, + actions=event_handler.gen_action(), + ) + for data in event_handler: + for ans in data: + pass + + +if __name__ == "__main__": + unittest.main() From c512b865dfafcbc5ea02bc9ae99db37bc461c89b Mon Sep 17 00:00:00 2001 From: C9luster <138663536+C9luster@users.noreply.github.com> Date: Wed, 20 Nov 2024 20:33:41 +0800 Subject: [PATCH 22/85] =?UTF-8?q?=E8=A7=84=E8=8C=83components=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E5=90=8D=E7=A7=B0=20(#608)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 规范components组件名称 * 更新文件目录 * 更新单元测试import error * 删除install.sh文件 --------- Co-authored-by: yinjiaqi --- python/__init__.py | 16 ++++++++-------- .../doc_parser/{doc_parser.py => component.py} | 0 .../{doc_splitter.py => component.py} | 0 .../rag_with_baidu_search_pro/component.py | 2 +- .../{parse_rag_pro_response.py => model.py} | 0 .../components/retriever/baidu_vdb/__init__.py | 6 +++--- .../{baiduvdb_retriever.py => component.py} | 0 python/core/components/retriever/bes/__init__.py | 4 ++-- .../bes/{bes_retriever.py => component.py} | 0 .../reranker/{rerank.py => component.py} | 0 .../test_appbuilder_core_components_retriever.py | 2 +- .../test_core_components_baidu_vdb_retriever.py | 4 ++-- python/tests/test_core_components_doc.py | 2 +- 13 files changed, 18 insertions(+), 18 deletions(-) rename python/core/components/doc_parser/{doc_parser.py => component.py} (100%) rename python/core/components/doc_splitter/{doc_splitter.py => component.py} (100%) rename python/core/components/rag_with_baidu_search_pro/{parse_rag_pro_response.py => model.py} (100%) rename python/core/components/retriever/baidu_vdb/{baiduvdb_retriever.py => component.py} (100%) rename python/core/components/retriever/bes/{bes_retriever.py => component.py} (100%) rename python/core/components/retriever/reranker/{rerank.py => component.py} (100%) diff --git a/python/__init__.py b/python/__init__.py index a0bdcf71e..f1678fac9 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -83,14 +83,14 @@ def get_default_header(): from .core.components.landmark_recognize.component import LandmarkRecognition from .core.components.tts.component import TTS from .core.components.extract_table.component import ExtractTableFromDoc -from .core.components.doc_parser.doc_parser import DocParser, ParserConfig -from .core.components.doc_splitter.doc_splitter import DocSplitter -from .core.components.retriever.bes.bes_retriever import BESRetriever -from .core.components.retriever.bes.bes_retriever import BESVectorStoreIndex -from .core.components.retriever.baidu_vdb.baiduvdb_retriever import BaiduVDBVectorStoreIndex -from .core.components.retriever.baidu_vdb.baiduvdb_retriever import BaiduVDBRetriever -from .core.components.retriever.baidu_vdb.baiduvdb_retriever import TableParams -from .core.components.retriever.reranker.rerank import Reranker +from .core.components.doc_parser.component import DocParser, ParserConfig +from .core.components.doc_splitter.component import DocSplitter +from .core.components.retriever.bes.component import BESRetriever +from .core.components.retriever.bes.component import BESVectorStoreIndex +from .core.components.retriever.baidu_vdb.component import BaiduVDBVectorStoreIndex +from .core.components.retriever.baidu_vdb.component import BaiduVDBRetriever +from .core.components.retriever.baidu_vdb.component import TableParams +from .core.components.retriever.reranker.component import Reranker from .core.components.ppt_generation_from_instruction.component import PPTGenerationFromInstruction from .core.components.ppt_generation_from_paper.component import PPTGenerationFromPaper from .core.components.ppt_generation_from_file.component import PPTGenerationFromFile diff --git a/python/core/components/doc_parser/doc_parser.py b/python/core/components/doc_parser/component.py similarity index 100% rename from python/core/components/doc_parser/doc_parser.py rename to python/core/components/doc_parser/component.py diff --git a/python/core/components/doc_splitter/doc_splitter.py b/python/core/components/doc_splitter/component.py similarity index 100% rename from python/core/components/doc_splitter/doc_splitter.py rename to python/core/components/doc_splitter/component.py diff --git a/python/core/components/rag_with_baidu_search_pro/component.py b/python/core/components/rag_with_baidu_search_pro/component.py index e82d1b844..1da458880 100644 --- a/python/core/components/rag_with_baidu_search_pro/component.py +++ b/python/core/components/rag_with_baidu_search_pro/component.py @@ -18,7 +18,7 @@ from appbuilder.core.message import Message from appbuilder.core.utils import ModelInfo, ttl_lru_cache from appbuilder.core._exception import AppBuilderServerException -from appbuilder.core.components.rag_with_baidu_search_pro.parse_rag_pro_response import ParseRagProResponse +from appbuilder.core.components.rag_with_baidu_search_pro.model import ParseRagProResponse from appbuilder.utils.trace.tracer_wrapper import components_run_trace from pydantic import BaseModel, Field, conint, confloat from typing import Optional diff --git a/python/core/components/rag_with_baidu_search_pro/parse_rag_pro_response.py b/python/core/components/rag_with_baidu_search_pro/model.py similarity index 100% rename from python/core/components/rag_with_baidu_search_pro/parse_rag_pro_response.py rename to python/core/components/rag_with_baidu_search_pro/model.py diff --git a/python/core/components/retriever/baidu_vdb/__init__.py b/python/core/components/retriever/baidu_vdb/__init__.py index f62cda550..8554a947d 100644 --- a/python/core/components/retriever/baidu_vdb/__init__.py +++ b/python/core/components/retriever/baidu_vdb/__init__.py @@ -12,6 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from .baiduvdb_retriever import BaiduVDBVectorStoreIndex -from .baiduvdb_retriever import BaiduVDBRetriever -from .baiduvdb_retriever import TableParams \ No newline at end of file +from .component import BaiduVDBVectorStoreIndex +from .component import BaiduVDBRetriever +from .component import TableParams \ No newline at end of file diff --git a/python/core/components/retriever/baidu_vdb/baiduvdb_retriever.py b/python/core/components/retriever/baidu_vdb/component.py similarity index 100% rename from python/core/components/retriever/baidu_vdb/baiduvdb_retriever.py rename to python/core/components/retriever/baidu_vdb/component.py diff --git a/python/core/components/retriever/bes/__init__.py b/python/core/components/retriever/bes/__init__.py index 9c060610c..9ffa29216 100644 --- a/python/core/components/retriever/bes/__init__.py +++ b/python/core/components/retriever/bes/__init__.py @@ -12,5 +12,5 @@ # See the License for the specific language governing permissions and # limitations under the License. -from .bes_retriever import BESVectorStoreIndex -from .bes_retriever import BESRetriever +from .component import BESVectorStoreIndex +from .component import BESRetriever diff --git a/python/core/components/retriever/bes/bes_retriever.py b/python/core/components/retriever/bes/component.py similarity index 100% rename from python/core/components/retriever/bes/bes_retriever.py rename to python/core/components/retriever/bes/component.py diff --git a/python/core/components/retriever/reranker/rerank.py b/python/core/components/retriever/reranker/component.py similarity index 100% rename from python/core/components/retriever/reranker/rerank.py rename to python/core/components/retriever/reranker/component.py diff --git a/python/tests/test_appbuilder_core_components_retriever.py b/python/tests/test_appbuilder_core_components_retriever.py index 81316a3b6..84b330ff4 100644 --- a/python/tests/test_appbuilder_core_components_retriever.py +++ b/python/tests/test_appbuilder_core_components_retriever.py @@ -17,7 +17,7 @@ import sys import time -from appbuilder.core.components.retriever.baidu_vdb.baiduvdb_retriever import _try_import,BaiduVDBVectorStoreIndex +from appbuilder.core.components.retriever.baidu_vdb.component import _try_import,BaiduVDBVectorStoreIndex @unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") diff --git a/python/tests/test_core_components_baidu_vdb_retriever.py b/python/tests/test_core_components_baidu_vdb_retriever.py index 0bca9c283..530adc6e7 100644 --- a/python/tests/test_core_components_baidu_vdb_retriever.py +++ b/python/tests/test_core_components_baidu_vdb_retriever.py @@ -17,8 +17,8 @@ import appbuilder -from appbuilder.core.components.retriever.bes.bes_retriever import BESVectorStoreIndex -from appbuilder.core.components.retriever.baidu_vdb.baiduvdb_retriever import BaiduVDBVectorStoreIndex,TableParams +from appbuilder import BESVectorStoreIndex +from appbuilder import BaiduVDBVectorStoreIndex,TableParams from appbuilder.core.component import Message @unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") diff --git a/python/tests/test_core_components_doc.py b/python/tests/test_core_components_doc.py index d0a7387bc..e43d96dfc 100644 --- a/python/tests/test_core_components_doc.py +++ b/python/tests/test_core_components_doc.py @@ -16,7 +16,7 @@ import copy from appbuilder.core.components.doc_crop_enhance.component import DocCropEnhance -from appbuilder.core.components.doc_splitter.doc_splitter import DocSplitter, ChunkSplitter, TitleSplitter +from appbuilder.core.components.doc_splitter.component import DocSplitter, ChunkSplitter, TitleSplitter from appbuilder.core.message import Message from appbuilder.core.components.doc_parser.base import ParseResult, ParaNode,Position from appbuilder.core.components.doc_format_converter.component import DocFormatConverter From 9ccf7d0ef35dad3e4b09a22321942f31305516f5 Mon Sep 17 00:00:00 2001 From: peiwenYe <963623403@qq.com> Date: Thu, 21 Nov 2024 15:24:38 +0800 Subject: [PATCH 23/85] =?UTF-8?q?=E5=A2=9E=E5=8A=A0v2=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=20(#607)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 组件输出规范检查 --- python/core/component.py | 255 ++++++++++++++--- .../components/animal_recognize/README.md | 12 +- .../components/image_understand/README.md | 20 +- python/core/components/v2/__init__.py | 21 ++ .../v2/animal_recognize/__init__.py | 13 + .../v2/animal_recognize/component.py | 208 ++++++++++++++ .../v2/image_understand/__init__.py | 15 + .../v2/image_understand/component.py | 270 ++++++++++++++++++ python/tests/component_check.py | 17 +- python/tests/component_collector.py | 22 +- python/tests/component_output_schemas.py | 3 +- python/tests/component_tool_eval_cases.py | 13 +- python/tests/parallel_ut_run.py | 2 +- python/tests/print_components_error_info.py | 15 +- python/tests/test_all_components.py | 21 +- python/tests/test_base_component.py | 55 ++++ python/tests/test_v2_animal_recognize.py | 156 ++++++++++ python/tests/test_v2_image_understand.py | 123 ++++++++ 18 files changed, 1168 insertions(+), 73 deletions(-) create mode 100644 python/core/components/v2/__init__.py create mode 100644 python/core/components/v2/animal_recognize/__init__.py create mode 100644 python/core/components/v2/animal_recognize/component.py create mode 100644 python/core/components/v2/image_understand/__init__.py create mode 100644 python/core/components/v2/image_understand/component.py create mode 100644 python/tests/test_base_component.py create mode 100644 python/tests/test_v2_animal_recognize.py create mode 100644 python/tests/test_v2_image_understand.py diff --git a/python/core/component.py b/python/core/component.py index 5b52063ca..c95435b82 100644 --- a/python/core/component.py +++ b/python/core/component.py @@ -18,7 +18,9 @@ from enum import Enum from pydantic import BaseModel -from typing import Dict, List, Optional, Any +from pydantic import Field +from typing import ( + Dict, List, Optional, Any, Generator, Union, AsyncGenerator) from appbuilder.core.utils import ttl_lru_cache from appbuilder.core._client import HTTPClient from appbuilder.core.message import Message @@ -27,7 +29,7 @@ class ComponentArguments(BaseModel): """ ComponentArguments define Component meta fields - + Attributes: name (str): component name. tool_desc (dict): component description. @@ -58,6 +60,83 @@ def extract_values_to_dict(self): return inputs +class Text(BaseModel): + info: str = Field(default="", description="具体文本内容") + + +class Code(BaseModel): + code: str = Field(default="", description="代码片段") + + +class Files(BaseModel): + filename: str = Field(default="", description="文件名") + url: str = Field(default="", description="文件url") + + +class Urls(BaseModel): + url: str = Field(default="", description="链接地址") + + +class OralText(BaseModel): + info: str = Field(default="", description="口语化文本内容") + + +class References(BaseModel): + type: str = Field(default="", description="类型") + resource_type: str = Field(default="", description="资源类型") + icon: str = Field(default="", description="站点图标") + site_name: str = Field(default="", description="站点名") + source: str = Field(default="", description="来源") + doc_id: str = Field(default="", description="文档id") + title: str = Field(default="", description="标题") + content: str = Field(default="", description="内容") + image_content: str = Field(default="", description="图片内容") + mock_id: Optional[str] = Field(default="", description="模拟数据id") + image_url: str = Field(default="", description="图片url") + video_url: str = Field(default="", description="视频url") + + +class Image(BaseModel): + filename: str = Field(default="", description="图片名称") + url: str = Field(default="", description="图片url") + byte: Optional[bytes] = Field(default=b'', description="图片二进制数据") + + +class Chart(BaseModel): + filename: str = Field(default="", description="图表名称") + url: str = Field(default="", description="图表url") + + +class Audio(BaseModel): + filename: str = Field(default="", description="音频名称") + url: str = Field(default="", description="音频url") + byte: Optional[bytes] = Field(default=b'', description="音频二进制数据") + + +class Content(BaseModel): + name: str = Field(default="", + description="介绍当前yield内容的阶段名, 使用name的必要条件,是同一组件会输出不同type的content,并且需要加以区分,方便前端渲染与用户展示") + visible_scope: str = Field(default="all", + description="为了界面展示明确的说明字段,三种取值:llm、user、all。llm为思考模型可见,类似function calling结果中submit的执行结果,user为终端用户可见,all包含上述两者") + raw_data: dict = Field(default={}, + description="raw_data是原始数据,可以是任何格式,比如json、html等,具体由开发者决定,用户上游请求结果透传,内部系统返回的信息,例如API节点收到的resp,大模型节点的MB resp") + usage: dict = Field(default={}, + description="大模型的token用量, ") + metrics: dict = Field(default={}, + description="耗时、性能、内存等trace及debug所需信息") + type: str = Field(default="text", + description="代表event 类型,包括 text、code、files、urls、oral_text、references、image、chart、audio该字段的取值决定了下面text字段的内容结构") + text: Union[Text, Code, Files, Urls, OralText, References, Image, Chart, Audio] = Field(default=Text, + description="代表当前 event 元素的内容,每一种 event 对应的 text 结构固定") + + +class ComponentOutput(BaseModel): + role: str = Field(default="tool", + description="role是区分当前消息来源的重要字段,对于绝大多数组件而言,都是填写tool,标明role所在的消息来源为组件。部分思考及问答组件,role需要填写为assistant") + content: list[Content] = Field(default=[], + description="content是当前组件返回内容的主要payload,List[Content],每个Content Dict 包括了当前输出的一个元素") + + class Component: """ Component基类, 其它实现的Component子类需要继承该基类,并至少实现run方法. @@ -95,18 +174,18 @@ def __init__( self.lazy_certification = lazy_certification if not self.lazy_certification: self.set_secret_key_and_gateway(self.secret_key, self.gateway) - + def set_secret_key_and_gateway(self, secret_key: Optional[str] = None, gateway: str = ""): """ 设置密钥和网关地址。 - + Args: secret_key (Optional[str], optional): 密钥,默认为None。如果未指定,则使用实例当前的密钥。 gateway (str, optional): 网关地址,默认为空字符串。如果未指定,则使用实例当前的网关地址。 - + Returns: None - + """ self.secret_key = secret_key self.gateway = gateway @@ -116,13 +195,13 @@ def set_secret_key_and_gateway(self, secret_key: Optional[str] = None, gateway: def http_client(self): """ 获取 HTTP 客户端实例。 - + Args: 无 - + Returns: HTTPClient: HTTP 客户端实例。 - + """ if self._http_client is None: self._http_client = HTTPClient(self.secret_key, self.gateway) @@ -132,7 +211,24 @@ def __call__(self, *inputs, **kwargs): r"""implement __call__ method""" return self.run(*inputs, **kwargs) - def run(self, *inputs, **kwargs): + def tool_eval(self, *input, **kwargs) -> Generator: + """ + 对给定的输入执行工具的FunctionCall。 + + Args: + *input: 一个可变数量的参数,代表输入数据。 + **kwargs: 关键字参数,可以包含任意数量的键值对,用于传递额外的参数。 + + Returns: + Generator[dict, ComponentOutput]: 生成器,生成字典和ComponentOutput类型的对象。 + + Raises: + NotImplementedError: 如果子类没有实现此方法,则抛出此异常。 + + """ + raise NotImplementedError + + def run(self, *inputs, **kwargs) -> Message: """ run method,待子类重写实现 @@ -143,8 +239,45 @@ def run(self, *inputs, **kwargs): raise NotImplementedError def batch(self, *args, **kwargs) -> List[Message]: + """ + 批量处理输入并返回结果列表。 + + Args: + *args: 可变数量的输入参数,每个参数将依次被处理。 + **kwargs: 关键字参数,这些参数将被传递给每个输入的处理函数。 + + Returns: + List[Message]: 包含处理结果的列表,每个元素对应一个输入参数的处理结果。 + + """ + + results = [self.run(inp, **kwargs) for inp in args] + return results + + def non_stream_tool_eval(self, *args, **kwargs) -> Union[ComponentOutput, dict]: + """ + 对工具评估结果进行非流式处理。 + + Args: + *args: 可变参数,具体参数依赖于调用的工具评估函数。 + **kwargs: 关键字参数,具体参数依赖于调用的工具评估函数。 + + Returns: + Union[ComponentOutput, dict]: 返回包含评估结果的 ComponentOutput 对象或字典。 + 如果评估结果为空,则返回包含评估结果的字典。 + + """ + result = ComponentOutput() + result_content = [] + for iter_result in self.tool_eval(*args, **kwargs): + result.role = iter_result.role + result_content += iter_result.content + result.content = result_content + return result + + async def atool_eval(self, *args, **kwargs) -> AsyncGenerator: r""" - batch method,待子类重写实现 + atool_eval method,待子类重写实现 Args: args: list of arguments @@ -204,16 +337,6 @@ def tool_name(self) -> List[str]: """ return [manifest["name"] for manifest in self.manifests] - def tool_eval(self, **kwargs): - r""" - tool_eval method,待子类重写实现 - - Args: - kwargs: keyword arguments - """ - if len(self.manifests) > 0: - raise NotImplementedError - def create_langchain_tool(self, tool_name="", **kwargs): r""" create_langchain_tool method,将AB-SDK的Tool转换为LangChain的StructuredTool @@ -230,24 +353,27 @@ def create_langchain_tool(self, tool_name="", **kwargs): except ImportError: raise ImportError( "Please install langchain to use create_langchain_tool.") - + # NOTE(chengmo): 可以支持LangChain的组件,必须要求具备mainfest if self.manifests == []: - raise ValueError("Compnent {} No manifests found. Cannot convert it into LangChain Tool".format(type(self))) + raise ValueError( + "Compnent {} No manifests found. Cannot convert it into LangChain Tool".format(type(self))) langchain_tool_json_schema = {} # NOTE(chengmo): 虽然现阶段,组件的mainfest列表中最多只有一个元素,但是需要兼容后期可能的多Tool的情况 if len(self.manifests) > 1: if tool_name == "": - raise ValueError("Multiple tools found, please use 'tool_name' specify which one to use.") + raise ValueError( + "Multiple tools found, please use 'tool_name' specify which one to use.") for manifest in self.manifests: if manifest["name"] == tool_name: langchain_tool_json_schema = manifest break - + if langchain_tool_json_schema == {}: - raise ValueError("Tool {} not found in mainfest.".format(tool_name)) + raise ValueError( + "Tool {} not found in mainfest.".format(tool_name)) else: langchain_tool_json_schema = self.manifests[0] @@ -275,7 +401,8 @@ def create_langchain_tool(self, tool_name="", **kwargs): schema, name_override=langchain_tool_name) except Exception as e: - raise RuntimeError("Failed to generate Pydantic model for tool schema: {}".format(e)) + raise RuntimeError( + "Failed to generate Pydantic model for tool schema: {}".format(e)) return StructuredTool.from_function( func=langchain_tool_func, @@ -284,17 +411,17 @@ def create_langchain_tool(self, tool_name="", **kwargs): args_schema=langchain_tool_model, return_direct=False ) - + def _has_implemented_tool_eval(self): has_tool_eval = False try: # 调用self.tool_eval方法,如果抛出NotImplementedError异常,则说明没有实现tool_eval方法 - self.tool_eval(**{'name':"", "streaming":False}) + self.tool_eval(**{'name': "", "streaming": False}) except NotImplementedError: has_tool_eval = False else: has_tool_eval = True - + return has_tool_eval def _langchain_run_implement(self, **kwargs): @@ -309,7 +436,7 @@ def _langchain_tool_eval_implement(self, **kwargs): res = self.tool_eval(**kwargs) final_result = "" - + # TODO(chengmo): 在组件标准化管理前,复用DTE对流式组件的处理逻辑 for step in res: if isinstance(step, str): @@ -318,5 +445,67 @@ def _langchain_tool_eval_implement(self, **kwargs): final_result += step.get("text", "") return final_result - - + def create_output(self, type, text, role="tool", name="", visible_scope="all", raw_data={}, usage={}, metrics={}): + """create_text_output + + Args: + type (str): 类型,包括"text", "code", "files", "urls", "oral_text", "references", "image", "chart", "audio" + text (str|dict): text字段,可输入str或dict + role (str, optional): 当前消息来源. Defaults to "tool". + name (str, optional): 当前yield内容的step name. Defaults to "". + visible_scope (str, optional): 界面展示明确的说明字段. Defaults to "all". + raw_data (dict, optional): 内部信息,由开发者请求透传. Defaults to {}. + usage (dict, optional): 大模型的token用量. Defaults to {}. + metrics (dict, optional): 耗时、性能、内存等trace及debug所需信息. Defaults to {}. + + Returns: + ComponentOutput: 组件输出 + """ + if isinstance(text, str): + if type == "text": + text = {"info": text} + elif type == "code": + text = {"code": text} + elif type == "urls": + text = {"url": text} + elif type == "oral_text": + text = {"info": text} + else: + raise ValueError("Only when type=text/code/urls/oral_text, string text is allowed! Please give dict text") + else: + if type == "text": + key_list = ["info"] + elif type == "code": + key_list = ["code"] + elif type == "oral_text": + key_list = ["info"] + elif type == "urls": + key_list = ["url"] + elif type == "files": + key_list = ["filename", "url"] + elif type == "references": + key_list = ["type", "resource_type", "icon", "site_name", "source", "doc_id", "title", "content", "image_content", "image_url", "video_url"] + elif type == "image": + key_list = ["filename", "url"] + elif type == "chart": + key_list = ["filename", "url"] + elif type == "audio": + key_list = ["filename", "url"] + else: + raise ValueError("Unknown type: {}".format(type)) + assert all(key in text for key in key_list), "all keys:{} must be included in the text field".format(key_list) + + assert role in ["tool", "assistant"], "role must be 'tool' or 'assistant'" + result = { + "role": role, + "content": [{ + "type": type, + "name": name, + "text": text, + "visible_scope": visible_scope, + "raw_data": raw_data, + "usage": usage, + "metrics": metrics + }] + } + return ComponentOutput(**result) \ No newline at end of file diff --git a/python/core/components/animal_recognize/README.md b/python/core/components/animal_recognize/README.md index 536d4ca61..e18fd62d2 100644 --- a/python/core/components/animal_recognize/README.md +++ b/python/core/components/animal_recognize/README.md @@ -61,16 +61,20 @@ os.environ["APPBUILDER_TOKEN"] = "bce-YOURTOKEN" ### 调用参数 | 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | |---------|---------|------|-----------------------------|------------------------------------------------| -| message | String | 是 | 输入的消息,用于模型的主要输入内容。这是一个必需的参数 | Message(content={"raw_image": b"待识别的图片字节流数据"}) | +| message | obj:`Message` | 是 | 输入的消息,用于模型的主要输入内容。这是一个必需的参数 | | +| +content | Dict | 是 | 消息内容 | {"raw_image": ...., "url": "http://xxx.png"} | +| ++raw_image | Bytes | 是 | 输入的图片数据,图片数据需要base64编码 | - | +| ++url | String | 否 | 图像下载链接 | - | |timeout| Float | 否 | HTTP超时时间,单位:秒 |1|| | retry | Integer | 否 | HTTP重试次数 | 3 | ### 响应参数 | 参数名称 | 参数类型 | 描述 | 示例值 | |--------|---------|------|---------------------------------------| -| result | Array[] | 返回结果 | [{"name":"国宝大熊猫","score":"0.975161"}] | -| +name | String | 动物名称 | "国宝大熊猫" | -| +score | String | 置信度 | "0.975161" | +| message | obj:`Message` | 动物识别输出内容 | - | +| +content | Dict | 返回结果 | "result": [{"name":"国宝大熊猫","score":"0.975161"}] | +| ++name | String | 动物名称 | "国宝大熊猫" | +| ++score | String | 置信度 | "0.975161" | ### 响应示例 ```json { diff --git a/python/core/components/image_understand/README.md b/python/core/components/image_understand/README.md index 4f6960f00..f77c8806c 100644 --- a/python/core/components/image_understand/README.md +++ b/python/core/components/image_understand/README.md @@ -54,23 +54,25 @@ os.environ["APPBUILDER_TOKEN"] = "..." | 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | |------------|--------|------|-----------------------------------|-----| -| message | String | 是 | 输入的消息,用于模型的主要输入内容。这是一个必需的参数 | | +| message | obj:`Message` | 是 | 输入的消息,用于模型的主要输入内容。这是一个必需的参数 | | | +content | Dict | 是 | 消息内容 | | -| +raw_image | String | 否 | 原始图片字节流 | | -| +url | String | 否 | 图片下载链接地址 | | -| +question | String | 是 | 问题字符串,长度小于100 | | -| +language | String | 否 | 描述内容的所使用的语言,默认是zh-CN(中文),可选en(英文) | | - -|timeout| Float | 否 | HTTP超时时间,单位:秒 |1|| -|retry| Integer | 否 | HTTP重试次数 |3|| +| ++raw_image | String | 否 | 原始图片字节流 | | +| ++url | String | 否 | 图片下载链接地址 | | +| ++question | String | 是 | 问题字符串,长度小于100 | | +| ++language | String | 否 | 描述内容的所使用的语言,默认是zh-CN(中文),可选en(英文) | | +|timeout| Float | 否 | HTTP超时时间,单位:秒 |1|| +|retry| Integer | 否 | HTTP重试次数 |3|| ### 响应参数 | 参数名称 | 参数类型 | 描述 | 示例值 | |-----------|------|--------|-------------------------------------------------| -| description | String | 图像理解内容 | "用户上传的图像,经过前期模型分析存在以下信息:;;整个图像内容" | +| message | obj:`Message` | 图像理解输出内容 | - | +| +content | Dict | 输出内容 | "description": "用户上传的图像,经过前期模型分析存在以下信息..., 整个图像内容可以表述为...,回答如下..." | +| ++description | String | 图像内容理解结果 | "用户上传的图像,经过前期模型分析存在以下信息...,回答如下问题:图片里内容是什么?" | ### 响应示例 ```json +output.content = { "description": "用户上传的图像,经过前期模型分析存在以下信息:;;整个图像内容可以表述为:...,回答如下问题:图片里内容是什么?, 注意不要复述提供的资料内容" } diff --git a/python/core/components/v2/__init__.py b/python/core/components/v2/__init__.py new file mode 100644 index 000000000..d1a3b8811 --- /dev/null +++ b/python/core/components/v2/__init__.py @@ -0,0 +1,21 @@ +# Copyright (c) 2023 Baidu, Inc. 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 .animal_recognize.component import AnimalRecognition +from .image_understand.component import ImageUnderstand + +__V2_COMPONENTS__ = [ + "AnimalRecognition", + "ImageUnderstand", +] # NOQA \ No newline at end of file diff --git a/python/core/components/v2/animal_recognize/__init__.py b/python/core/components/v2/animal_recognize/__init__.py new file mode 100644 index 000000000..85017b89f --- /dev/null +++ b/python/core/components/v2/animal_recognize/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2023 Baidu, Inc. 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. \ No newline at end of file diff --git a/python/core/components/v2/animal_recognize/component.py b/python/core/components/v2/animal_recognize/component.py new file mode 100644 index 000000000..9c8b5ed4a --- /dev/null +++ b/python/core/components/v2/animal_recognize/component.py @@ -0,0 +1,208 @@ +# Copyright (c) 2023 Baidu, Inc. 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. + +"""animal recognize component.""" + +import base64 +import json + +from appbuilder.core.component import Component, ComponentOutput +from appbuilder.core.components.animal_recognize.model import * +from appbuilder.core.message import Message +from appbuilder.core._client import HTTPClient +from appbuilder.core._exception import AppBuilderServerException +from typing import Generator, Union +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace + +TOP_NUM = 1 +BAIKE_NUM = 0 + + +class AnimalRecognition(Component): + r""" + 用于识别一张图片,即对于输入的一张图片(可正常解码,且长宽比较合适),输出动物识别结果。 + + Examples: + + .. code-block:: python + + import appbuilder + # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 + os.environ["APPBUILDER_TOKEN"] = '...' + + animal_recognition = appbuilder.AnimalRecognition() + with open("./animal_recognition_test.png", "rb") as f: + out = self.component.run(appbuilder.Message(content={"raw_image": f.read()})) + print(out.content) + + """ + name = "animal_rec" + version = "v1" + manifests = [ + { + "name": "animal_rec", + "description": "用于识别图片中动物类别,可识别近八千种动物", + "parameters": { + "type": "object", + "properties": { + "img_name": { + "type": "string", + "description": "待识别图片的文件名" + }, + "img_url": { + "type": "string", + "description": "待识别图片的url" + } + }, + "anyOf": [ + { + "required": [ + "img_name" + ] + }, + { + "required": [ + "img_url" + ] + } + ] + } + } + ] + + @HTTPClient.check_param + @components_run_trace + def run(self, message: Message, timeout: float = None, retry: int = 0) -> Message: + """ + 根据输入消息运行动物识别功能 + + Args: + message (Message): 输入的消息对象,其中应包含需要识别的图像数据或URL + timeout (float, optional): 超时时间,单位为秒。默认为None,表示无超时限制。Defaults to None. + retry (int, optional): 重试次数。默认为0,表示不重试。Defaults to 0. + + Returns: + Message: 识别结果的消息对象 + + """ + inp = AnimalRecognitionInMsg(**message.content) + req = AnimalRecognitionRequest() + if inp.raw_image: + req.image = base64.b64encode(inp.raw_image) + if inp.url: + req.url = inp.url + req.top_num = 6 + req.baike_num = 0 + result, _ = self._recognize(req, timeout, retry) + result_dict = proto.Message.to_dict(result) + out = AnimalRecognitionOutMsg(**result_dict) + return Message(content=out.model_dump()) + + def _recognize( + self, + request: AnimalRecognitionRequest, + timeout: float = None, + retry: int = 0, + request_id: str = None, + ) -> AnimalRecognitionResponse: + r"""调用底层接口进行动物识别 + + 参数: + request (obj: `AnimalRecognitionRequest`) : 动物识别输入参数 + 返回: + response (obj: `AnimalRecognitionResponse`): 动物识别返回结果 + """ + if not request.image and not request.url: + raise ValueError("request format error, one of image or url must be set") + + data = AnimalRecognitionRequest.to_dict(request) + if self.http_client.retry.total != retry: + self.http_client.retry.total = retry + headers = self.http_client.auth_header(request_id) + headers['content-type'] = 'application/x-www-form-urlencoded' + url = self.http_client.service_url("/v1/bce/aip/image-classify/v1/animal") + response = self.http_client.session.post(url, headers=headers, data=data, timeout=timeout) + self.http_client.check_response_header(response) + data = response.json() + self.http_client.check_response_json(data) + request_id = self.http_client.response_request_id(response) + self.__class__._check_service_error(request_id, data) + animalRes = AnimalRecognitionResponse.from_json(json.dumps(data)) + animalRes.request_id = request_id + return animalRes, data + + @components_run_stream_trace + def tool_eval( + self, + img_name: str, + img_url: str, + **kwargs, + ) -> Union[Generator[str, None, None], str]: + """ + 用于工具的执行,通过调用底层接口进行动物识别。 + + Args: + name (str): 工具名。 + streaming (bool): 是否流式返回。 + origin_query (str): 用户原始query。 + **kwargs: 工具调用的额外关键字参数。 + + Returns: + Union[Generator[str, None, None], str]: 动物识别结果,包括识别出的动物类别和相应的置信度信息。 + """ + traceid = kwargs.get("traceid") + file_urls = kwargs.get("file_urls", {}) + yield from self._recognize_w_post_process(img_name, img_url, file_urls, request_id=traceid) + + def _recognize_w_post_process(self, img_name, img_url, file_urls, request_id=None) -> str: + r"""调底层接口对图片或图片url进行动物识别,并返回类别及其置信度 + Args: + img_name (str): 图片文件名 + img_url (str): 图片url + file_urls (dict): 文件名与对应文件url的映射 + Returns: + str: 动物识别结果,包括识别出的动物类别和相应的置信度信息 + """ + req = AnimalRecognitionRequest() + if img_name in file_urls: + req.url = file_urls[img_name] + if img_url: + if img_url in file_urls: + img_url = file_urls[img_url] + req.url = img_url + req.top_num = TOP_NUM + req.baike_num = BAIKE_NUM + result, raw_data = self._recognize(req, request_id=request_id) + result_dict = proto.Message.to_dict(result) + rec_res = "模型识别结果为:\n" + for rec_info in result_dict['result']: + rec_res += "类别: {} 置信度: {}\n".format(rec_info['name'], rec_info['score']) + output = self.create_output(type="text", text=rec_res, raw_data=raw_data) + yield output + + @staticmethod + def _check_service_error(request_id: str, data: dict): + r"""个性化服务response参数检查 + 参数: + request_id (str) : 请求ID + data (dict) : 动物识别body返回 + 返回: + 无 + """ + if "error_code" in data or "error_msg" in data: + raise AppBuilderServerException( + request_id=request_id, + service_err_code=data.get("error_code"), + service_err_message=data.get("error_msg") + ) diff --git a/python/core/components/v2/image_understand/__init__.py b/python/core/components/v2/image_understand/__init__.py new file mode 100644 index 000000000..fc3a1a2ea --- /dev/null +++ b/python/core/components/v2/image_understand/__init__.py @@ -0,0 +1,15 @@ +# Copyright (c) 2023 Baidu, Inc. 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. + + diff --git a/python/core/components/v2/image_understand/component.py b/python/core/components/v2/image_understand/component.py new file mode 100644 index 000000000..b10874645 --- /dev/null +++ b/python/core/components/v2/image_understand/component.py @@ -0,0 +1,270 @@ +# Copyright (c) 2023 Baidu, Inc. 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. + +r"""图像内容理解""" +import base64 +import time + +from appbuilder.core.component import Component, ComponentOutput +from appbuilder.core.message import Message +from appbuilder.core._client import HTTPClient +from appbuilder.core._exception import AppBuilderServerException +from appbuilder.core.components.image_understand.model import * +from typing import Generator, Union +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace + + +class ImageUnderstand(Component): + r""" + 图像内容理解组件,即对于输入的一张图片(可正常解码,且长宽比适宜)与问题,输出对图片的描述 + + Examples: + + .. code-block:: python + + import os + import appbuilder + os.environ["GATEWAY_URL"] = "..." + os.environ["APPBUILDER_TOKEN"] = "..." + # 从BOS存储读取样例文件 + image_url = "https://bj.bcebos.com/v1/appbuilder/test_image_understand.jpeg?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-24T09%3A41%3A01Z%2F-1%2Fhost%2Fe8665506e30e0edaec4f1cc84a2507c4cb3fdb9b769de3a5bfe25c372b7e56e6" + # 输入参数为一张图片 + inp = Message(content={"url": image_url, "question": "图片里内容是什么?"}) + # 进行图像内容理解 + image_understand = ImageUnderstand() + out = image_understand.run(inp) + # 打印识别结果 + print(out.content) + """ + name = "image_understanding" + version = "v1" + manifests = [ + { + "name": "image_understanding", + "description": "可对输入图片进行理解,可输出图片描述、OCR 及图像识别结果", + "parameters": { + "type": "object", + "properties": { + "img_name": { + "type": "string", + "description": "待识别图片的文件名" + }, + "img_url": { + "type": "string", + "description": "待识别图片的url" + } + }, + "anyOf": [ + { + "required": [ + "img_name" + ] + }, + { + "required": [ + "img_url" + ] + } + ] + } + } + ] + + @HTTPClient.check_param + @components_run_trace + def run(self, message: Message, timeout: float = None, retry: int = 0) -> Message: + """ + 执行图像内容理解 + + Args: + message (Message): 输入图片或图片url下载地址用于执行识别操作. 举例: Message(content={"raw_image": b"...", "question": "图片主要内容是什么?"}) + 或 Message(content={"url": "https://image/download/url", "question": "图片主要内容是什么?"}). + timeout (float, optional): HTTP超时时间. 默认为 None. + retry (int, optional): HTTP重试次数. 默认为 0. + + Returns: + Message: 模型识别结果. + + """ + inp = ImageUnderstandInMsg(**message.content) + request = ImageUnderstandRequest() + # 兼容新参数,确保输出结果一致 + request.subject_detect = False + request.llm_switch = False + if inp.raw_image: + request.image = base64.b64encode(inp.raw_image) + if inp.url: + request.url = inp.url + if inp.question == "": + raise ValueError("request format error, question is empty") + if len(inp.question) > 100: + raise ValueError(f"request format error, expected len(question)>100, got {len(inp.question)}") + if inp.language != "zh-CN" and inp.language != "en": + raise ValueError(f"request format error, expected language in ['zh-CN', 'en'], got {inp.language}") + request.question = inp.question + request.output_CHN = True + if inp.language == "en": + request.output_CHN = False + response, _ = self.__recognize(request, timeout, retry) + out = ImageUnderstandOutMsg(description=response.result.description_to_llm) + return Message(content=out.model_dump()) + + def __recognize( + self, + request: ImageUnderstandRequest, + timeout: float = None, + retry: int = 0, + request_id: str = None, + ) -> ImageUnderstandResponse: + r"""调用底层接口进行图像内容理解 + + 参数: + request (obj: `ImageUnderstandRequest`) : 图像内容理解输入 + + 返回: + response (obj: `ImageUnderstandResponse`): 图像内容理解输出 + """ + if not request.image and not request.url: + raise ValueError("request format error, one of image or url must be set") + if retry != self.http_client.retry.total: + self.http_client.retry.total = retry + data = ImageUnderstandRequest.to_dict(request) + headers = self.http_client.auth_header(request_id) + headers['Content-Type'] = 'application/json' + url = self.http_client.service_url("/v1/bce/aip/image-classify/v1/image-understanding/request") + response = self.http_client.session.post(url, json=data, timeout=timeout, headers=headers) + self.http_client.check_response_header(response) + data = response.json() + self.http_client.check_response_json(data) + request_id = self.http_client.response_request_id(response) + self.__class__.__check_create_task_service_error(request_id, data) + task = ImageUnderstandTask(data, request_id=request_id) + task_id = task.result.get("task_id", "") + if task_id == "": + raise AppBuilderServerException(request_id=request_id, service_err_message="empty task_id") + url = self.http_client.service_url("/v1/bce/aip/image-classify/v1/image-understanding/get-result") + while True: + response = self.http_client.session.post(url, json={"task_id": task_id}, timeout=timeout, headers=headers) + self.http_client.check_response_header(response) + data = response.json() + self.http_client.check_response_json(data) + request_id = self.http_client.response_request_id(response) + self.__class__.__check_service_error(request_id, data.get("result", {})) + # 处理成功 + response = ImageUnderstandResponse(data) + if response.result.ret_code == 0: + return ImageUnderstandResponse(data), data + # 还在处理中 + if response.result.ret_code == 1: + # 避免触发限流(>1QPS),等待1.1秒 + time.sleep(1.1) + + @components_run_stream_trace + def tool_eval( + self, + img_name: str, + img_url: str, + **kwargs, + ) -> Union[Generator[str, None, None], str]: + """ + 用于工具的执行,调用底层接口进行图像内容理解 + + Args: + img_name (str): 图片文件名 + img_url (str): 图片url + **kwargs: 工具调用的额外关键字参数 + + Returns: + Union[Generator[str, None, None], str]: 图片内容理解结果 + """ + traceid = kwargs.get("traceid") + file_urls = kwargs.get("file_urls", {}) + rec_res, raw_data = self._recognize_w_post_process(img_name, img_url, file_urls, request_id=traceid) + llm_result = self.create_output(type="text", text=rec_res, name="text_1", raw_data=raw_data, visible_scope='llm') + yield llm_result + user_result = self.create_output(type="text", text="", name="text_2", raw_data=raw_data, visible_scope='user') + yield user_result + + def _recognize_w_post_process( + self, + img_name, + img_url, + file_urls, + question="图片内容有哪些", + request_id=None, + ) -> str: + r""" + 参数: + img_name (str): 图片文件名 + img_url (bool): 图片url + question (str): 询问有关图片内容的问题 + file_urls (dict): 文件名与对应文件url的映射 + + 返回: + str: 图片内容理解结果 + """ + req = ImageUnderstandRequest() + # 兼容新参数,确保输出结果一致 + req.subject_detect = False + req.llm_switch = False + req.question = question + if img_name in file_urls: + req.url = file_urls[img_name] + if img_url: + if img_url in file_urls: + img_url = file_urls[img_url] + req.url = img_url + response, raw_data = self.__recognize(req, request_id=request_id) + description_to_llm = response.result.description_to_llm + description_processed = description_to_llm.rsplit("。", 2)[0] + return description_processed, raw_data + + @staticmethod + def __check_service_error(request_id: str, data: dict): + r"""个性化服务response参数检查 + + 参数: + request (dict) : 图像内容理解body返回 + 返回: + 无 + """ + ret_code = data.get("ret_code", 0) + if ret_code != 0 and ret_code != 1: + raise AppBuilderServerException( + request_id=request_id, + service_err_code=data.get("ret_code", ""), + service_err_message=data.get("ret_msg", "") + ) + + @staticmethod + def __check_create_task_service_error(request_id: str, data: dict): + r"""个性化服务response参数检查 + 参数: + request_id (str) : 任务请求ID + data (dict): 响应数据 + 返回: + 无 + """ + + if "error_code" in data and "error_msg" in data: + raise AppBuilderServerException( + request_id=request_id, + service_err_code=data.get("error_code", ""), + service_err_message=data.get("error_msg", "") + ) + + + + + diff --git a/python/tests/component_check.py b/python/tests/component_check.py index a342ee75c..b72f3171c 100644 --- a/python/tests/component_check.py +++ b/python/tests/component_check.py @@ -14,7 +14,7 @@ import os import json import inspect -import jsonschema +import time from jsonschema import validate, ValidationError, SchemaError from pydantic import BaseModel from typing import Generator @@ -307,14 +307,14 @@ def _gather_iter_outputs(self, outputs): text_output = "" oral_text_output = "" code_output = "" - for content in outputs["content"]: - out_type = content["type"] + for content in outputs.content: + out_type = content.type if out_type == "text": - text_output += content["text"]["info"] + text_output += content.text.info elif out_type == "oral_text": - oral_text_output += content["oral_text"]["info"] + oral_text_output += content.oral_text.info elif out_type == "code": - code_output += content["code"]["code"] + code_output += content.code.code return { "text": text_output, "oral_text": oral_text_output, @@ -384,7 +384,7 @@ def check(self, component_cls) -> CheckInfo: stream_output_dict = {"text": "", "oral_text":"", "code": ""} stream_outputs = component_obj.tool_eval(**input_dict) for stream_output in stream_outputs: #校验流式输出 - iter_invalid_detail = self._check_jsonschema(stream_output, output_json_schemas) + iter_invalid_detail = self._check_jsonschema(stream_output.model_dump(), output_json_schemas) invalid_details.extend(["流式" + error_message for error_message in iter_invalid_detail]) iter_output_dict = self._gather_iter_outputs(stream_output) stream_output_dict["text"] += iter_output_dict["text"] @@ -395,9 +395,10 @@ def check(self, component_cls) -> CheckInfo: except Exception as e: invalid_details.append("ToolEval执行失败: {}".format(e)) + time.sleep(2) try: non_stream_outputs = component_obj.non_stream_tool_eval(**input_dict) - non_stream_invalid_details = self._check_jsonschema(non_stream_outputs, output_json_schemas) #校验非流式输出 + non_stream_invalid_details = self._check_jsonschema(non_stream_outputs.model_dump(), output_json_schemas) #校验非流式输出 invalid_details.extend(["非流式" + error_message for error_message in non_stream_invalid_details]) if len(invalid_details) == 0: non_stream_output_dict = self._gather_iter_outputs(non_stream_outputs) diff --git a/python/tests/component_collector.py b/python/tests/component_collector.py index 36272a110..904c22e01 100644 --- a/python/tests/component_collector.py +++ b/python/tests/component_collector.py @@ -76,16 +76,14 @@ def get_component_white_list(): return COMPONENT_WHITE_LIST -def get_all_components(): - from appbuilder import __COMPONENTS__ - +def get_components(components_list, import_prefix): components = {} - for component in __COMPONENTS__: + for component in components_list: if component in SKIP_COMPONENTS: continue try: - component_obj = eval("appbuilder."+component) + component_obj = eval(import_prefix+component) components[component]= { "obj": component_obj, "import_error": "" @@ -99,6 +97,18 @@ def get_all_components(): return components +def get_all_components(): + from appbuilder import __COMPONENTS__ + all_components = get_components(__COMPONENTS__, "appbuilder.") + return all_components + +def get_v2_components(): + from appbuilder.core.components.v2 import __V2_COMPONENTS__ + v2_components = get_components(__V2_COMPONENTS__, "appbuilder.core.components.v2.") + return v2_components + if __name__ == '__main__': all_components = get_all_components() - print(all_components) \ No newline at end of file + v2_components = get_v2_components() + print("all_components: ", all_components) + print("v2_components: ", v2_components) \ No newline at end of file diff --git a/python/tests/component_output_schemas.py b/python/tests/component_output_schemas.py index 3869020fa..c515c7b17 100644 --- a/python/tests/component_output_schemas.py +++ b/python/tests/component_output_schemas.py @@ -264,5 +264,6 @@ components_tool_eval_output_json_maps = { "AnimalRecognition": [text_schema], "ASR": [text_schema], - "TreeMind": [text_schema, url_schema] + "TreeMind": [text_schema, url_schema], + "ImageUnderstand": [text_schema] } \ No newline at end of file diff --git a/python/tests/component_tool_eval_cases.py b/python/tests/component_tool_eval_cases.py index 38e6519ee..0675eca61 100644 --- a/python/tests/component_tool_eval_cases.py +++ b/python/tests/component_tool_eval_cases.py @@ -38,10 +38,21 @@ class TreeMindCase: def inputs(self): return {"query": "生成一份年度总结的思维导图"} - +class ImageUnderstandCase: + def inputs(self): + return { + "img_url": "https://bj.bcebos.com/v1/appbuilder/animal_recognize_test.png?" \ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-24T" \ + "12%3A19%3A16Z%2F-1%2Fhost%2F411bad53034fa8f9c6edbe5c4909d76ecf6fad68" \ + "62cf937c03f8c5260d51c6ae", + "img_name": "test_img.jpg" + } + def outputs(self): + return {"text": ["熊猫"]} component_tool_eval_cases = { "AnimalRecognition": AnimalRecognitionCase, + "ImageUnderstand": ImageUnderstandCase, "ASR": ASRCase, "TreeMind": TreeMindCase } \ No newline at end of file diff --git a/python/tests/parallel_ut_run.py b/python/tests/parallel_ut_run.py index b64757829..dec626016 100644 --- a/python/tests/parallel_ut_run.py +++ b/python/tests/parallel_ut_run.py @@ -107,7 +107,7 @@ def get_all_unittest_file(): if file == "py" and prefix == "test": fullname = os.path.join(root, f) choose_test_case(fullname) - + logger.info("\n需要跳过的单测用例:{}个".format(len(SKIP_UNITTEST))) for idx, case in enumerate(SKIP_UNITTEST): logger.info("--> {}. {}".format(idx+1, case)) diff --git a/python/tests/print_components_error_info.py b/python/tests/print_components_error_info.py index 2135ca23a..4cef36fb4 100644 --- a/python/tests/print_components_error_info.py +++ b/python/tests/print_components_error_info.py @@ -1,3 +1,5 @@ +import os + def pretty_print_dict(kv_dict, header=["Key", "Value"]): spacing = 2 max_k = 25 @@ -48,6 +50,13 @@ def read_error_file(filename): if __name__ == "__main__": - filename = 'components_error_info.txt' - kv_dict, header = read_error_file(filename) - print(pretty_print_dict(kv_dict, header=header)) \ No newline at end of file + if os.path.exists('components_error_info.txt'): + print("旧组件:") + filename = 'components_error_info.txt' + kv_dict, header = read_error_file(filename) + print(pretty_print_dict(kv_dict, header=header)) + if os.path.exists('v2_components_error_info.txt'): + print("v2组件:") + filename = 'v2_components_error_info.txt' + kv_dict, header = read_error_file(filename) + print(pretty_print_dict(kv_dict, header=header)) \ No newline at end of file diff --git a/python/tests/test_all_components.py b/python/tests/test_all_components.py index 1f3920d23..404c400c1 100644 --- a/python/tests/test_all_components.py +++ b/python/tests/test_all_components.py @@ -18,12 +18,11 @@ from appbuilder.core.component import Component from appbuilder.core.components.llms.base import CompletionBaseComponent from appbuilder.core._exception import AppbuilderBuildexException -from component_collector import get_all_components, get_component_white_list +from component_collector import get_all_components, get_v2_components, get_component_white_list from appbuilder.tests.component_check import ComponentCheckBase -def write_error_data(error_df,error_stats): - txt_file_path = 'components_error_info.txt' +def write_error_data(txt_file_path, error_df,error_stats): with open(txt_file_path, 'w') as file: file.write("Component Name\tError Message\n") for _, row in error_df.iterrows(): @@ -37,14 +36,15 @@ def write_error_data(error_df,error_stats): class TestComponentManifestsAndToolEval(unittest.TestCase): def setUp(self) -> None: self.all_components = get_all_components() + self.v2_components = get_v2_components() self.whitelist_components = get_component_white_list() self.component_check_base = ComponentCheckBase() - def test_component(self): + def _test_component(self, components, whitelist_components, txt_file_path): error_data = [] error_stats ={} - for name, import_res in self.all_components.items(): + for name, import_res in components.items(): if import_res["import_error"] != "": error_data.append({"Component Name": name, "Error Message": import_res["import_error"]}) @@ -75,12 +75,12 @@ def test_component(self): for error, count in error_stats.items(): print(f"错误信息: {error}, 出现次数: {count}") # 将报错信息写入文件 - write_error_data(error_df, error_stats) + write_error_data(txt_file_path, error_df, error_stats) # 判断报错组件是否位于白名单中 component_names = error_df["Component Name"].tolist() for component_name in component_names: - if component_name in self.whitelist_components: + if component_name in whitelist_components: print("{}在白名单中,暂时忽略报错。".format(component_name), flush=True) else: raise AppbuilderBuildexException(f"组件 {component_name} 未在白名单中,请检查是否需要添加到白名单。") @@ -89,5 +89,12 @@ def test_component(self): print("\n所有组件测试通过,无错误信息。") + def test_all_components(self): + self._test_component(self.all_components, self.whitelist_components, 'components_error_info.txt') + + def test_v2_components(self): + self._test_component(self.v2_components, [], 'v2_components_error_info.txt') + + if __name__ == '__main__': unittest.main() \ No newline at end of file diff --git a/python/tests/test_base_component.py b/python/tests/test_base_component.py new file mode 100644 index 000000000..3d82aea25 --- /dev/null +++ b/python/tests/test_base_component.py @@ -0,0 +1,55 @@ +import os +import unittest +from appbuilder.core.component import Component +from appbuilder.core.component import ComponentOutput + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") +class TestBaseComponent(unittest.TestCase): + def setUp(self) -> None: + self.component = Component() + + + def test_valid_output_with_str(self): + out1 = self.component.create_output(type="text", text="test") + out2 = self.component.create_output(type="code", text="import appbuilder") + out3 = self.component.create_output(type="urls", text="http://www.baidu.com") + out4 = self.component.create_output(type="oral_text", text="你是哪个") + self.assertIsInstance(out1, ComponentOutput) + self.assertIsInstance(out2, ComponentOutput) + self.assertIsInstance(out3, ComponentOutput) + self.assertIsInstance(out4, ComponentOutput) + + def test_valid_output_with_dict(self): + output1 = self.component.create_output(type="text", text={"info": "1"}) + output2 = self.component.create_output(type="code", text={"code": "1"}) + output3 = self.component.create_output(type="urls", text={"url": "http://www.baidu.com"}) + output4 = self.component.create_output(type="oral_text", text={"info": "你好"}) + output5 = self.component.create_output(type="files", text={"filename": "file.txt", "url": "http://www.baidu.com"}) + output6 = self.component.create_output(type="image", text={"filename": "file.png", "url": "http://www.baidu.com"}) + output7 = self.component.create_output(type="chart", text={"filename": "file.jpg", "url": "http://www.baidu.com"}) + output8 = self.component.create_output(type="audio", text={"filename": "file.mp3", "url": "http://www.baidu.com"}) + self.assertIsInstance(output1, ComponentOutput) + self.assertIsInstance(output2, ComponentOutput) + self.assertIsInstance(output3, ComponentOutput) + self.assertIsInstance(output4, ComponentOutput) + self.assertIsInstance(output5, ComponentOutput) + self.assertIsInstance(output6, ComponentOutput) + self.assertIsInstance(output7, ComponentOutput) + self.assertIsInstance(output8, ComponentOutput) + + + def test_invalid_output_type_json(self): + with self.assertRaises(ValueError): + output = self.component.create_output(type="json", text="") + with self.assertRaises(AssertionError): + output = self.component.create_output(type="files", text={}) + with self.assertRaises(AssertionError): + output = self.component.create_output(type="references", text={"info": "text"}) + with self.assertRaises(AssertionError): + output = self.component.create_output(type="image", text={"url": "https://example.com/img"}) + with self.assertRaises(AssertionError): + output = self.component.create_output(type="chart", text={"url": "https://example.com/chart"}) + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/python/tests/test_v2_animal_recognize.py b/python/tests/test_v2_animal_recognize.py new file mode 100644 index 000000000..b9b7c5e32 --- /dev/null +++ b/python/tests/test_v2_animal_recognize.py @@ -0,0 +1,156 @@ +# Copyright (c) 2023 Baidu, Inc. 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 unittest +import requests +import appbuilder +from appbuilder.core.components.v2 import AnimalRecognition +import os + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") +class TestAnimalRecognition(unittest.TestCase): + def setUp(self): + """ + 设置环境变量。 + """ + self.animal_recognition = AnimalRecognition() + + def test_run_with_raw_image(self): + """ + 使用原始图片进行单测 + + Args: + None + + Returns: + None + + """ + image_url = "https://bj.bcebos.com/v1/appbuilder/animal_recognize_test.png?" \ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-24T" \ + "12%3A19%3A16Z%2F-1%2Fhost%2F411bad53034fa8f9c6edbe5c4909d76ecf6fad68" \ + "62cf937c03f8c5260d51c6ae" + raw_image = requests.get(image_url).content + # Create message with raw_image + message = appbuilder.Message(content={"raw_image": raw_image}) + # Recognize animal + output = self.animal_recognition.run(message) + # Assert output is not None + self.assertIsNotNone(output) + self.assertIsInstance(output, appbuilder.Message) + self.assertIsInstance(output.content["result"], list) + self.assertIsInstance(output.content["result"][0]["name"], str) + + + def test_run_with_url(self): + """ + 使用图片 URL 进行单测 + + Args: + None + + Returns: + None + + """ + image_url = "https://bj.bcebos.com/v1/appbuilder/animal_recognize_test.png?" \ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-24T" \ + "12%3A19%3A16Z%2F-1%2Fhost%2F411bad53034fa8f9c6edbe5c4909d76ecf6fad68" \ + "62cf937c03f8c5260d51c6ae" + # Create message with image URL + message = appbuilder.Message(content={"url": image_url}) + # Recognize animal + output = self.animal_recognition.run(message) + # Assert output is not None + self.assertIsNotNone(output) + self.assertIsInstance(output, appbuilder.Message) + + def test_run_with_timeout_and_retry(self): + """ + 测试run方法,timeout、retry参数 + + Args: + None + + Returns: + None + + """ + image_url = "https://bj.bcebos.com/v1/appbuilder/animal_recognize_test.png?" \ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-24T" \ + "12%3A19%3A16Z%2F-1%2Fhost%2F411bad53034fa8f9c6edbe5c4909d76ecf6fad68" \ + "62cf937c03f8c5260d51c6ae" + raw_image = requests.get(image_url).content + # Create message with raw_image + message = appbuilder.Message(content={"url": image_url, "raw_image": raw_image}) + # Recognize animal with timeout and retry parameters + output = self.animal_recognition.run(message, timeout=5.0, retry=3) + # Assert output is not None + self.assertIsNotNone(output) + + def test_run_with_invalid_input(self): + """ + 测试run函数在传入无效输入的情况下的行为。 + + Args: + None + + Returns: + None + + """ + # create empty message + message = appbuilder.Message(content={}) + # Assert ValueError is raised + with self.assertRaises(ValueError): + self.animal_recognition.run(message) + + def test_run_with_invalid_url(self): + """ + 测试run函数在传入无效URL的情况下的行为。 + + Args: + None + + Returns: + None + + """ + url = "http://example.com/invalid_url.jpg" + message = appbuilder.Message(content={"url": url}) + with self.assertRaises(appbuilder.AppBuilderServerException): + self.animal_recognition.run(message) + + def test_tool_eval_valid(self): + """测试 tool 方法对有效请求的处理。""" + img_url = "https://bj.bcebos.com/v1/appbuilder/animal_recognize_test.png?" \ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-24T" \ + "12%3A19%3A16Z%2F-1%2Fhost%2F411bad53034fa8f9c6edbe5c4909d76ecf6fad68" \ + "62cf937c03f8c5260d51c6ae" + img_name = "test_img.jpg" + file_urls = {img_name: img_url} + result = self.animal_recognition.tool_eval(img_name=img_name, img_url=img_url, file_urls=file_urls) + res = [item for item in result] + self.assertNotEqual(len(res), 0) + + def test_tool_eval_invalid(self): + """测试 tool 方法对无效请求的处理。""" + with self.assertRaises(TypeError): + result = self.animal_recognition.tool_eval(name="animal_recognition", streaming=True, + origin_query="") + next(result) + + +if __name__ == '__main__': + unittest.main() diff --git a/python/tests/test_v2_image_understand.py b/python/tests/test_v2_image_understand.py new file mode 100644 index 000000000..a9f639519 --- /dev/null +++ b/python/tests/test_v2_image_understand.py @@ -0,0 +1,123 @@ +# Copyright (c) 2023 Baidu, Inc. 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 os +import unittest +import requests +import appbuilder +import time + +from appbuilder.core.message import Message +from appbuilder.core._exception import AppBuilderServerException +from appbuilder.core.components.v2 import ImageUnderstand + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") +class TestImageUnderstand(unittest.TestCase): + def setUp(self): + """ + 设置环境变量 + Args: + None. + Returns: + None. + """ + # 从BOS存储读取样例文件 + self.image_url = "https://bj.bcebos.com/v1/appbuilder/test_image_understand.jpeg?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-24T09%3A41%3A01Z%2F-1%2Fhost%2Fe8665506e30e0edaec4f1cc84a2507c4cb3fdb9b769de3a5bfe25c372b7e56e6" + self.raw_image = requests.get(self.image_url).content + self.image_understand = ImageUnderstand() + + # 输入参数为一张图片 + + def test_run_with_image_url(self): + """ + 使用图片url进行单测 + + Args: + None + + Returns: + None + + """ + # Create message with raw_image + inp = Message(content={"url": self.image_url, "question": "图像内容是什么?"}) + msg = self.image_understand.run(inp) + self.assertIsNotNone(msg.content) + self.assertIsInstance(msg, Message) + self.assertIsInstance(msg.content["description"], str) + time.sleep(1) + + def test_run_with_raw_image(self): + """ + 使用原始图片进行单测 + + Args: + None + + Returns: + None + + """ + # Create message with raw_image + inp = Message(content={"raw_image": self.raw_image, "question": "图像内容是什么?"}) + msg = self.image_understand.run(inp) + self.assertIsNotNone(msg.content) + self.assertIsInstance(msg, Message) + self.assertIsInstance(msg.content["description"], str) + time.sleep(1) + + def test_tool_eval_valid(self): + """测试 tool 方法对有效请求的处理。""" + img_url = "https://bj.bcebos.com/v1/appbuilder/animal_recognize_test.png?" \ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-24T" \ + "12%3A19%3A16Z%2F-1%2Fhost%2F411bad53034fa8f9c6edbe5c4909d76ecf6fad68" \ + "62cf937c03f8c5260d51c6ae" + img_name = "test_img.jpg" + + file_urls = {img_name: img_url} + result = self.image_understand.tool_eval(img_name=img_name, img_url=img_url) + res = [item for item in result] + self.assertNotEqual(len(res), 0) + time.sleep(1) + + def test_tool_eval_invalid(self): + """测试 tool 方法对无效请求的处理。""" + with self.assertRaises(TypeError): + result = self.image_understand.tool_eval(name="image_understand", streaming=True, + origin_query="") + next(result) + time.sleep(1) + + def test_run_language_en(self): + """测试 tool 方法对无效请求的处理。""" + inp = Message(content={"raw_image": self.raw_image, "question": "图像内容是什么?", "language": "en"}) + self.image_understand.run(inp) + time.sleep(1) + + def test_run_raise(self): + # question is empty + with self.assertRaises(ValueError): + inp = Message(content={"raw_image": self.raw_image, "question": ""}) + self.image_understand.run(inp) + + # question length bigger than 100 + with self.assertRaises(ValueError): + question="test"*26 + inp = Message(content={"raw_image": self.raw_image, "question": question, "language": ""}) + self.image_understand.run(inp) + + +if __name__ == "__main__": + unittest.main() + From 64a87ca6cd3997385e10044752c07838dabdc822 Mon Sep 17 00:00:00 2001 From: C9luster <138663536+C9luster@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:41:26 +0800 Subject: [PATCH 24/85] =?UTF-8?q?=E6=9B=B4=E6=96=B0SDK=E6=96=87=E6=A1=A3?= =?UTF-8?q?=20(#610)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 更新SDK文档 * 更行java-API文档,只展示public函数 * 更新pythonAPI目录 * 更新Components组件文档视图 * 删除API文档采用挂载机器自动生成逻辑 --------- Co-authored-by: yinjiaqi --- README.md | 3 +- docs/API-Reference/Python/PythonAPI.md | 7 - .../appbuilder.core.assistant.assistants.md | 248 -------- .../Python/appbuilder.core.assistant.md | 46 -- .../appbuilder.core.assistant.threads.md | 139 ----- ...builder.core.assistant.threads.messages.md | 88 --- .../appbuilder.core.assistant.threads.runs.md | 529 ------------------ ...uilder.core.components.animal_recognize.md | 59 -- .../Python/appbuilder.core.components.asr.md | 63 --- ...pbuilder.core.components.dish_recognize.md | 42 -- ...uilder.core.components.doc_crop_enhance.md | 49 -- ...er.core.components.doc_format_converter.md | 82 --- .../appbuilder.core.components.doc_parser.md | 68 --- ...appbuilder.core.components.doc_splitter.md | 174 ------ ....core.components.document_understanding.md | 59 -- .../appbuilder.core.components.embeddings.md | 66 --- ...appbuilder.core.components.excel2figure.md | 76 --- ...ppbuilder.core.components.extract_table.md | 61 -- .../Python/appbuilder.core.components.gbi.md | 17 - .../appbuilder.core.components.gbi.nl2sql.md | 36 -- ...uilder.core.components.gbi.select_table.md | 33 -- .../appbuilder.core.components.general_ocr.md | 66 --- ...ppbuilder.core.components.handwrite_ocr.md | 66 --- ...uilder.core.components.image_understand.md | 65 --- ...lder.core.components.landmark_recognize.md | 41 -- ...der.core.components.llms.dialog_summary.md | 52 -- ...components.llms.hallucination_detection.md | 107 ---- ...r.core.components.llms.is_complex_query.md | 53 -- .../Python/appbuilder.core.components.llms.md | 143 ----- .../appbuilder.core.components.llms.mrc.md | 66 --- ...pbuilder.core.components.llms.nl2pandas.md | 55 -- ...e.components.llms.oral_query_generation.md | 84 --- ...builder.core.components.llms.playground.md | 57 -- ...der.core.components.llms.qa_pair_mining.md | 35 -- ...ore.components.llms.query_decomposition.md | 53 -- ...lder.core.components.llms.query_rewrite.md | 58 -- ...r.core.components.llms.similar_question.md | 75 --- ...lder.core.components.llms.style_rewrite.md | 69 --- ...lder.core.components.llms.style_writing.md | 113 ---- ...der.core.components.llms.tag_extraction.md | 48 -- .../appbuilder.core.components.matching.md | 64 --- .../Python/appbuilder.core.components.md | 396 ------------- ...appbuilder.core.components.mix_card_ocr.md | 71 --- ...uilder.core.components.object_recognize.md | 73 --- ...builder.core.components.plant_recognize.md | 71 --- ...ore.components.ppt_generation_from_file.md | 115 ---- ...ponents.ppt_generation_from_instruction.md | 120 ---- ...re.components.ppt_generation_from_paper.md | 120 ---- .../appbuilder.core.components.qrcode_ocr.md | 73 --- ...r.core.components.rag_with_baidu_search.md | 37 -- ...re.components.rag_with_baidu_search_pro.md | 163 ------ ...der.core.components.retriever.baidu_vdb.md | 129 ----- ...ppbuilder.core.components.retriever.bes.md | 147 ----- .../appbuilder.core.components.retriever.md | 47 -- ...lder.core.components.retriever.reranker.md | 49 -- .../appbuilder.core.components.table_ocr.md | 88 --- ...ppbuilder.core.components.text_to_image.md | 104 ---- .../appbuilder.core.components.translate.md | 67 --- .../appbuilder.core.components.tree_mind.md | 53 -- .../Python/appbuilder.core.components.tts.md | 65 --- ...pbuilder.core.console.appbuilder_client.md | 214 ------- .../Python/appbuilder.core.console.dataset.md | 120 ---- .../appbuilder.core.console.knowledge_base.md | 308 ---------- .../Python/appbuilder.core.console.md | 72 --- .../Python/appbuilder.core.console.rag.md | 63 --- docs/API-Reference/Python/appbuilder.core.md | 515 ----------------- docs/API-Reference/Python/appbuilder.md | 67 --- docs/API-Reference/Python/index.md | 10 - docs/API-Reference/Python/modules.md | 10 - docs/BasisModule/Trace/Debug.md | 54 ++ .../StartFirstAINativeApplication/README.md | 50 -- docs/README_en.md | 3 +- docs/README_ja.md | 3 +- docs/Tools/JavaAPI/JavaAPI.md | 5 + docs/Tools/JavaAPI/READEME.md | 90 +++ docs/Tools/JavaAPI/java_api_update.sh | 16 + docs/Tools/SphinxSh/PythonAPI.md | 2 +- docs/Tools/SphinxSh/READEME.md | 2 +- docs/Tools/SphinxSh/update_doc.sh | 3 + docs/Tools/SphinxSh/update_lib.py | 201 +++++++ .../appbuilderclient/AppBuilderClient.java | 8 + .../console/appbuilderclient/AppList.java | 18 + .../console/knowledgebase/Knowledgebase.java | 310 ++++++++++ mkdocs.yml | 3 +- 84 files changed, 715 insertions(+), 6735 deletions(-) delete mode 100644 docs/API-Reference/Python/PythonAPI.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.assistant.assistants.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.assistant.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.assistant.threads.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.assistant.threads.messages.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.assistant.threads.runs.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.animal_recognize.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.asr.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.dish_recognize.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.doc_crop_enhance.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.doc_format_converter.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.doc_parser.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.doc_splitter.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.document_understanding.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.embeddings.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.excel2figure.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.extract_table.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.gbi.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.gbi.nl2sql.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.gbi.select_table.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.general_ocr.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.handwrite_ocr.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.image_understand.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.landmark_recognize.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.llms.dialog_summary.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.llms.hallucination_detection.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.llms.is_complex_query.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.llms.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.llms.mrc.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.llms.nl2pandas.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.llms.oral_query_generation.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.llms.playground.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.llms.qa_pair_mining.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.llms.query_decomposition.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.llms.query_rewrite.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.llms.similar_question.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.llms.style_rewrite.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.llms.style_writing.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.llms.tag_extraction.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.matching.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.mix_card_ocr.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.object_recognize.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.plant_recognize.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.ppt_generation_from_file.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.ppt_generation_from_instruction.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.ppt_generation_from_paper.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.qrcode_ocr.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.rag_with_baidu_search.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.rag_with_baidu_search_pro.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.retriever.baidu_vdb.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.retriever.bes.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.retriever.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.retriever.reranker.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.table_ocr.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.text_to_image.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.translate.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.tree_mind.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.components.tts.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.console.appbuilder_client.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.console.dataset.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.console.knowledge_base.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.console.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.console.rag.md delete mode 100644 docs/API-Reference/Python/appbuilder.core.md delete mode 100644 docs/API-Reference/Python/appbuilder.md delete mode 100644 docs/API-Reference/Python/index.md delete mode 100644 docs/API-Reference/Python/modules.md create mode 100644 docs/BasisModule/Trace/Debug.md create mode 100644 docs/Tools/JavaAPI/JavaAPI.md create mode 100644 docs/Tools/JavaAPI/READEME.md create mode 100644 docs/Tools/JavaAPI/java_api_update.sh create mode 100644 docs/Tools/SphinxSh/update_lib.py diff --git a/README.md b/README.md index 0707d179c..2342f814a 100644 --- a/README.md +++ b/README.md @@ -294,6 +294,7 @@ Hook: - 监控: - [TRACE基础功能](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/basic.md) - [TRACE拓展功能](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/phoenix_method.md) + - [Debug功能](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/Debug.md) - 部署: - [交互式前端部署](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/AgentChainlit.md) - [公有云部署](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/cloud.md) @@ -332,7 +333,7 @@ Hook: - [环境参数](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/EnvironmentalParameters/env.md) - API Reference: - [Python API Reference](https://github.com/baidubce/app-builder/blob/master/docs/API-Reference/Python/PythonAPI.md) - - [Java API Reference](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [Java API Reference](https://github.com/baidubce/app-builder/blob/master/docs/API-Reference/Java/JavaAPI.md) - [Go API Reference](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) diff --git a/docs/API-Reference/Python/PythonAPI.md b/docs/API-Reference/Python/PythonAPI.md deleted file mode 100644 index 4801ebacd..000000000 --- a/docs/API-Reference/Python/PythonAPI.md +++ /dev/null @@ -1,7 +0,0 @@ -# Python API Reference - - -- [基础 API](appbuilder.md) - - [Assistant API](appbuilder.core.assistant.md) - - [Components API](appbuilder.core.components.md) - - [Console API](appbuilder.core.console.md) \ No newline at end of file diff --git a/docs/API-Reference/Python/appbuilder.core.assistant.assistants.md b/docs/API-Reference/Python/appbuilder.core.assistant.assistants.md deleted file mode 100644 index 35606e21b..000000000 --- a/docs/API-Reference/Python/appbuilder.core.assistant.assistants.md +++ /dev/null @@ -1,248 +0,0 @@ -# appbuilder.core.assistant.assistants package - -## Submodules - -## appbuilder.core.assistant.assistants.assistants module - -### *class* appbuilder.core.assistant.assistants.assistants.Assistants - -基类:`object` - -#### create(name: str, description: str, model: str | None = 'ERNIE-4.0T-8K', response_format: str | None = 'text', instructions: str | None = '你是百度制作的AI助手', thought_instructions: str | None = '', chat_instructions: str | None = '', tools: list[AssistantTool] | None = [], file_ids: list[str] | None = [], metadata: dict | None = {}) → AssistantCreateResponse - -创建助手实例 - -* **参数:** - * **name** (*str*) -- 助手名称 - * **description** (*str*) -- 助手描述 - * **model** (*Optional* *[**str* *]* *,* *optional*) -- 模型名称. Defaults to "ERNIE-4.0T-8K". - * **response_format** (*Optional* *[**str* *]* *,* *optional*) -- 响应格式. Defaults to 'text'. - * **instructions** (*Optional* *[**str* *]* *,* *optional*) -- 指令. Defaults to "". - * **thought_instructions** (*Optional* *[**str* *]* *,* *optional*) -- 思考指令. Defaults to "". - * **chat_instructions** (*Optional* *[**str* *]* *,* *optional*) -- 聊天指令. Defaults to "". - * **tools** (*Optional* *[**list* *[**assistant_type.AssistantTool* *]* *]* *,* *optional*) -- 工具列表. Defaults to []. - * **file_ids** (*Optional* *[**list* *[**str* *]* *]* *,* *optional*) -- 文件ID列表. Defaults to []. - * **metadata** (*Optional* *[**dict* *]* *,* *optional*) -- 元数据. Defaults to {}. -* **返回:** - 助手创建响应 -* **返回类型:** - assistant_type.AssistantCreateResponse - -#### delete(assistant_id: str | None) → AssistantDeleteResponse - -根据assistant_id删除指定Assitant - -* **参数:** - **assistant_id** (*Optional* *[**str* *]*) -- 待删除的助手实例ID。 -* **返回:** - 删除助手实例后的响应结果。 -* **返回类型:** - assistant_type.AssistantDeleteResponse -* **抛出:** - **HttpRequestError** -- 发送HTTP请求时发生错误。 - -#### *property* files - -获取当前工作目录下的文件对象。 - -* **参数:** - **无** -* **返回:** - 返回当前工作目录下的文件对象。 -* **返回类型:** - [Files](#appbuilder.core.assistant.assistants.files.Files) - -#### list(limit: int | None = 20, order: str | None = 'desc', after: str | None = '', before: str | None = '') → AssistantListResponse - -查询当前用户已创建的assistant列表 - -* **参数:** - * **limit** (*Optional* *[**int* *]* *,* *optional*) -- 返回助手列表的最大数量,默认为20。 - * **order** (*Optional* *[**str* *]* *,* *optional*) -- 返回助手列表的排序方式,可选值为"asc"或"desc",默认为"desc"。 - * **after** (*Optional* *[**str* *]* *,* *optional*) -- 返回助手列表中id在指定id之后的助手,默认为空字符串。 - * **before** (*Optional* *[**str* *]* *,* *optional*) -- 返回助手列表中id在指定id之前的助手,默认为空字符串。 -* **返回:** - 助手列表响应体。 -* **返回类型:** - assistant_type.AssistantListResponse - -#### mount_files(assistant_id: str | None, file_id: str | None) → AssistantFilesResponse - -指定file_id和assistant_id,挂载File到对应的Assistant - -* **参数:** - * **assistant_id** (*Optional* *[**str* *]*) -- 助理ID。 - * **file_id** (*Optional* *[**str* *]*) -- 文件ID。 -* **返回:** - 助理文件列表响应对象。 -* **返回类型:** - assistant_type.AssistantFilesResponse - -#### mounted_files_list(assistant_id: str | None, limit: int | None = 20, order: str | None = 'desc', after: str | None = '', before: str | None = '') → AssistantMountedFilesListResponse - -查询Assistant挂载的File列表 - -* **参数:** - * **assistant_id** (*Optional* *[**str* *]*) -- 助手ID,为空时获取当前登录用户的助手文件列表。 - * **limit** (*Optional* *[**int* *]* *,* *optional*) -- 每页最多显示多少个文件。默认为20。 - * **order** (*Optional* *[**AssistantListRole* *]* *,* *optional*) -- 文件列表排序方式。可选值为 'asc' 或 'desc'。默认为 'desc'。 - * **after** (*Optional* *[**str* *]* *,* *optional*) -- 返回文件ID大于该值的文件列表。默认为空字符串。 - * **before** (*Optional* *[**str* *]* *,* *optional*) -- 返回文件ID小于该值的文件列表。默认为空字符串。 -* **返回:** - 包含文件列表信息的响应对象。 -* **返回类型:** - assistant_type.AssistantFilesListResponse - -#### query(assistant_id: str | None) → AssistantQueryResponse - -根据assistant_id查询Assistant信息 - -* **参数:** - **assistant_id** (*Optional* *[**str* *]*) -- 助手ID -* **返回:** - 助手查询响应结果 -* **返回类型:** - assistant_type.AssistantQueryResponse -* **抛出:** - **HTTPError** -- 请求失败,抛出HTTPError异常 - -#### unmount_files(assistant_id: str | None, file_id: str | None) → AssistantFilesDeleteResponse - -指定assistant_id和file_id,解绑Assistant中对应File的关联 - -* **参数:** - * **assistant_id** (*Optional* *[**str* *]*) -- 助理ID。 - * **file_id** (*Optional* *[**str* *]*) -- 文件ID。 -* **返回:** - 响应对象。 -* **返回类型:** - assistant_type.AssistantFilesDeleteResponse - -#### update(assistant_id: str, model: str | None, name: str | None, description: str | None, instructions: str | None = '', tools: list[AssistantTool] | None = [], thought_instructions: str | None = '', chat_instructions: str | None = '', response_format: str | None = 'text', file_ids: list[str] | None = [], metadata: dict | None = {}) → AssistantUpdateResponse - -根据assistant_id修改一个已创建的Assistant - -* **参数:** - * **assistant_id** (*str*) -- 助手ID。 - * **model** (*Optional* *[**str* *]*) -- 助手模型。 - * **name** (*Optional* *[**str* *]*) -- 助手名称。 - * **description** (*Optional* *[**str* *]*) -- 助手描述。 - * **response_format** (*Optional* *[**str* *]* *,* *optional*) -- 响应格式。默认为None。 - * **instructions** (*Optional* *[**str* *]* *,* *optional*) -- 助手指令。默认为None。 - * **thought_instructions** (*Optional* *[**str* *]* *,* *optional*) -- 思考指令。默认为None。 - * **chat_instructions** (*Optional* *[**str* *]* *,* *optional*) -- 聊天指令。默认为None。 - * **tools** (*Optional* *[**list* *[**assistant_type.AssistantTool* *]* *]* *,* *optional*) -- 助手工具列表。默认为空列表。 - * **file_ids** (*Optional* *[**list* *[**str* *]* *]* *,* *optional*) -- 文件ID列表。默认为空列表。 - * **metadata** (*Optional* *[**dict* *]* *,* *optional*) -- 助手元数据。默认为空字典。 -* **返回:** - 助手更新响应。 -* **返回类型:** - assistant_type.AssistantUpdateResponse - -## appbuilder.core.assistant.assistants.files module - -### *class* appbuilder.core.assistant.assistants.files.Files - -基类:`object` - -#### content(file_id: str, timeout: int | None = None) - -获取指定文件的内容 - -* **参数:** - * **file_id** (*str*) -- 文件ID - * **timeout** (*Optional* *[**int* *]* *,* *optional*) -- 请求超时时间,单位秒. Defaults to None. -* **返回:** - 包含文件内容的响应对象 -* **返回类型:** - assistant_type.AssistantFilesContentResponse -* **抛出:** - * **TypeError** -- 当file_id不是字符串类型时引发此异常 - * **FileNotFoundError** -- 当指定的文件路径不存在时引发此异常 - * **HTTPConnectionException** -- 当请求失败时引发此异常 - -#### create(file_path: str, purpose: str = 'assistant') → AssistantFilesCreateResponse - -上传文件到助理存储中。 - -* **参数:** - * **file_path** (*str*) -- 要上传的文件路径。 - * **purpose** (*str* *,* *optional*) -- 上传文件的用途。默认为 "assistant"。 -* **返回:** - 上传文件后返回的响应对象。 -* **返回类型:** - assistant_type.AssistantFilesCreateResponse -* **抛出:** - **ValueError** -- 如果指定的文件路径不存在,则会引发此异常。 - -#### delete(file_id: str) → AssistantFilesDeleteResponse - -删除文件 - -* **参数:** - **file_id** (*str*) -- 文件ID -* **返回:** - 删除文件后的响应对象。 -* **返回类型:** - assistant_type.AssistantFilesDeleteResponse -* **抛出:** - **无** -- - -#### download(file_id: str, file_path: str = '', timeout: int | None = None) - -下载文件 - -* **参数:** - * **file_id** (*str*) -- 文件ID - * **file_path** (*str* *,* *optional*) -- 文件保存路径,默认为空字符串。如果未指定,则使用文件名的默认值。要求若文件路径不为空,需要以/结尾。 - * **timeout** (*Optional* *[**int* *]* *,* *optional*) -- 请求超时时间,单位秒。如果未指定,则使用默认超时时间。 -* **返回:** - None -* **抛出:** - * **TypeError** -- 当file_path或file_id类型不为str时引发此异常。 - * **ValueError** -- 当file_id为空或None时,或file_path不是文件目录时引发此异常。 - * **FileNotFoundError** -- 当指定的文件路径或文件不存在时引发此异常。 - * **OSError** -- 当磁盘空间不足时引发此异常。 - * **HTTPConnectionException** -- 当请求失败时引发此异常。 - * **Exception** -- 当发生其他异常时引发此异常。 - -#### list() → AssistantFilesListResponse - -列出存储中的文件列表。 - -此方法向存储服务发送请求,获取已上传的文件列表。返回的文件列表包含每个文件的详细信息, -包括文件ID、大小、用途、审核状态、创建时间、文件名、文件分类ID等。 - -* **参数:** - **无** -* **返回:** - 文件列表的响应对象,包含以下属性: - - object (str): 表示对象类型,默认值为 "list" - - data (list[AssistantFilesListData]): 包含文件信息的列表,列表中的每个元素为 AssistantFilesListData 对象。该对象包含以下属性: - > - id (str): 文件ID - > - bytes (int): 文件大小(字节) - > - object (str): 文件对象标识 - > - purpose (str): 文件用途 - > - censored (AuditStatus): 文件的审核状态 - > - create_at (int): 文件创建时间戳 - > - filename (str): 文件名 - > - classification_id (str): 文件分类ID - > - file_type (str): 文件类型 -* **返回类型:** - assistant_type.AssistantFilesListResponse -* **抛出:** - **assistant_type.AssistantError** -- 请求发生错误时抛出,具体错误信息可通过 error_msg 属性获取。 - -#### query(file_id: str) → AssistantFilesQueryResponse - -根据文件ID查询文件信息 - -* **参数:** - **file_id** (*str*) -- 文件ID -* **返回:** - 文件查询响应对象 -* **返回类型:** - assistant_type.AssistantFilesQueryResponse -* **抛出:** - * **TypeError** -- 如果file_id不是str类型 - * **ValueError** -- 如果file_id不存在 diff --git a/docs/API-Reference/Python/appbuilder.core.assistant.md b/docs/API-Reference/Python/appbuilder.core.assistant.md deleted file mode 100644 index 6cb34887a..000000000 --- a/docs/API-Reference/Python/appbuilder.core.assistant.md +++ /dev/null @@ -1,46 +0,0 @@ -# appbuilder.core.assistant package - -## Subpackages - -* [appbuilder.core.assistant.assistants package](appbuilder.core.assistant.assistants.md) - * [Submodules](appbuilder.core.assistant.assistants.md#submodules) - * [appbuilder.core.assistant.assistants.assistants module](appbuilder.core.assistant.assistants.md#module-appbuilder.core.assistant.assistants.assistants) - * [`Assistants`](appbuilder.core.assistant.assistants.md#appbuilder.core.assistant.assistants.assistants.Assistants) - * [`Assistants.create()`](appbuilder.core.assistant.assistants.md#appbuilder.core.assistant.assistants.assistants.Assistants.create) - * [`Assistants.delete()`](appbuilder.core.assistant.assistants.md#appbuilder.core.assistant.assistants.assistants.Assistants.delete) - * [`Assistants.files`](appbuilder.core.assistant.assistants.md#appbuilder.core.assistant.assistants.assistants.Assistants.files) - * [`Assistants.list()`](appbuilder.core.assistant.assistants.md#appbuilder.core.assistant.assistants.assistants.Assistants.list) - * [`Assistants.mount_files()`](appbuilder.core.assistant.assistants.md#appbuilder.core.assistant.assistants.assistants.Assistants.mount_files) - * [`Assistants.mounted_files_list()`](appbuilder.core.assistant.assistants.md#appbuilder.core.assistant.assistants.assistants.Assistants.mounted_files_list) - * [`Assistants.query()`](appbuilder.core.assistant.assistants.md#appbuilder.core.assistant.assistants.assistants.Assistants.query) - * [`Assistants.unmount_files()`](appbuilder.core.assistant.assistants.md#appbuilder.core.assistant.assistants.assistants.Assistants.unmount_files) - * [`Assistants.update()`](appbuilder.core.assistant.assistants.md#appbuilder.core.assistant.assistants.assistants.Assistants.update) - * [appbuilder.core.assistant.assistants.files module](appbuilder.core.assistant.assistants.md#module-appbuilder.core.assistant.assistants.files) - * [`Files`](appbuilder.core.assistant.assistants.md#appbuilder.core.assistant.assistants.files.Files) - * [`Files.content()`](appbuilder.core.assistant.assistants.md#appbuilder.core.assistant.assistants.files.Files.content) - * [`Files.create()`](appbuilder.core.assistant.assistants.md#appbuilder.core.assistant.assistants.files.Files.create) - * [`Files.delete()`](appbuilder.core.assistant.assistants.md#appbuilder.core.assistant.assistants.files.Files.delete) - * [`Files.download()`](appbuilder.core.assistant.assistants.md#appbuilder.core.assistant.assistants.files.Files.download) - * [`Files.list()`](appbuilder.core.assistant.assistants.md#appbuilder.core.assistant.assistants.files.Files.list) - * [`Files.query()`](appbuilder.core.assistant.assistants.md#appbuilder.core.assistant.assistants.files.Files.query) -* [appbuilder.core.assistant.threads package](appbuilder.core.assistant.threads.md) - * [Subpackages](appbuilder.core.assistant.threads.md#subpackages) - * [appbuilder.core.assistant.threads.messages package](appbuilder.core.assistant.threads.messages.md) - * [Submodules](appbuilder.core.assistant.threads.messages.md#submodules) - * [appbuilder.core.assistant.threads.messages.messages module](appbuilder.core.assistant.threads.messages.md#module-appbuilder.core.assistant.threads.messages.messages) - * [appbuilder.core.assistant.threads.runs package](appbuilder.core.assistant.threads.runs.md) - * [Submodules](appbuilder.core.assistant.threads.runs.md#submodules) - * [appbuilder.core.assistant.threads.runs.runs module](appbuilder.core.assistant.threads.runs.md#module-appbuilder.core.assistant.threads.runs.runs) - * [appbuilder.core.assistant.threads.runs.steps module](appbuilder.core.assistant.threads.runs.md#module-appbuilder.core.assistant.threads.runs.steps) - * [appbuilder.core.assistant.threads.runs.stream_helper module](appbuilder.core.assistant.threads.runs.md#module-appbuilder.core.assistant.threads.runs.stream_helper) - * [Submodules](appbuilder.core.assistant.threads.md#submodules) - * [appbuilder.core.assistant.threads.threads module](appbuilder.core.assistant.threads.md#module-appbuilder.core.assistant.threads.threads) - * [`Threads`](appbuilder.core.assistant.threads.md#appbuilder.core.assistant.threads.threads.Threads) - * [`Threads.create()`](appbuilder.core.assistant.threads.md#appbuilder.core.assistant.threads.threads.Threads.create) - * [`Threads.delete()`](appbuilder.core.assistant.threads.md#appbuilder.core.assistant.threads.threads.Threads.delete) - * [`Threads.messages`](appbuilder.core.assistant.threads.md#appbuilder.core.assistant.threads.threads.Threads.messages) - * [`Threads.query()`](appbuilder.core.assistant.threads.md#appbuilder.core.assistant.threads.threads.Threads.query) - * [`Threads.runs`](appbuilder.core.assistant.threads.md#appbuilder.core.assistant.threads.threads.Threads.runs) - * [`Threads.update()`](appbuilder.core.assistant.threads.md#appbuilder.core.assistant.threads.threads.Threads.update) - -## Submodules diff --git a/docs/API-Reference/Python/appbuilder.core.assistant.threads.md b/docs/API-Reference/Python/appbuilder.core.assistant.threads.md deleted file mode 100644 index 7b626032e..000000000 --- a/docs/API-Reference/Python/appbuilder.core.assistant.threads.md +++ /dev/null @@ -1,139 +0,0 @@ -# appbuilder.core.assistant.threads package - -## Subpackages - -* [appbuilder.core.assistant.threads.messages package](appbuilder.core.assistant.threads.messages.md) - * [Submodules](appbuilder.core.assistant.threads.messages.md#submodules) - * [appbuilder.core.assistant.threads.messages.messages module](appbuilder.core.assistant.threads.messages.md#module-appbuilder.core.assistant.threads.messages.messages) - * [`Messages`](appbuilder.core.assistant.threads.messages.md#appbuilder.core.assistant.threads.messages.messages.Messages) - * [`Messages.create()`](appbuilder.core.assistant.threads.messages.md#appbuilder.core.assistant.threads.messages.messages.Messages.create) - * [`Messages.files()`](appbuilder.core.assistant.threads.messages.md#appbuilder.core.assistant.threads.messages.messages.Messages.files) - * [`Messages.list()`](appbuilder.core.assistant.threads.messages.md#appbuilder.core.assistant.threads.messages.messages.Messages.list) - * [`Messages.query()`](appbuilder.core.assistant.threads.messages.md#appbuilder.core.assistant.threads.messages.messages.Messages.query) - * [`Messages.update()`](appbuilder.core.assistant.threads.messages.md#appbuilder.core.assistant.threads.messages.messages.Messages.update) -* [appbuilder.core.assistant.threads.runs package](appbuilder.core.assistant.threads.runs.md) - * [Submodules](appbuilder.core.assistant.threads.runs.md#submodules) - * [appbuilder.core.assistant.threads.runs.runs module](appbuilder.core.assistant.threads.runs.md#module-appbuilder.core.assistant.threads.runs.runs) - * [`Runs`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.runs.Runs) - * [`Runs.cancel()`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.runs.Runs.cancel) - * [`Runs.list()`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.runs.Runs.list) - * [`Runs.query()`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.runs.Runs.query) - * [`Runs.run()`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.runs.Runs.run) - * [`Runs.steps`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.runs.Runs.steps) - * [`Runs.stream_run()`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.runs.Runs.stream_run) - * [`Runs.stream_run_with_handler()`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.runs.Runs.stream_run_with_handler) - * [`Runs.submit_tool_outputs()`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.runs.Runs.submit_tool_outputs) - * [appbuilder.core.assistant.threads.runs.steps module](appbuilder.core.assistant.threads.runs.md#module-appbuilder.core.assistant.threads.runs.steps) - * [`Steps`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.steps.Steps) - * [`Steps.list()`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.steps.Steps.list) - * [`Steps.query()`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.steps.Steps.query) - * [appbuilder.core.assistant.threads.runs.stream_helper module](appbuilder.core.assistant.threads.runs.md#module-appbuilder.core.assistant.threads.runs.stream_helper) - * [`AssistantEventHandler`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.stream_helper.AssistantEventHandler) - * [`AssistantEventHandler.message_creation()`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.stream_helper.AssistantEventHandler.message_creation) - * [`AssistantEventHandler.messages()`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.stream_helper.AssistantEventHandler.messages) - * [`AssistantEventHandler.run_begin()`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.stream_helper.AssistantEventHandler.run_begin) - * [`AssistantEventHandler.run_cancelling()`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.stream_helper.AssistantEventHandler.run_cancelling) - * [`AssistantEventHandler.run_end()`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.stream_helper.AssistantEventHandler.run_end) - * [`AssistantEventHandler.tool_calls()`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.stream_helper.AssistantEventHandler.tool_calls) - * [`AssistantEventHandler.tool_step_begin()`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.stream_helper.AssistantEventHandler.tool_step_begin) - * [`AssistantEventHandler.tool_step_end()`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.stream_helper.AssistantEventHandler.tool_step_end) - * [`AssistantEventHandler.tool_submitted_output()`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.stream_helper.AssistantEventHandler.tool_submitted_output) - * [`AssistantEventHandler.until_done()`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.stream_helper.AssistantEventHandler.until_done) - * [`AssistantStreamManager`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.stream_helper.AssistantStreamManager) - * [`StreamRunContext`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.stream_helper.StreamRunContext) - * [`StreamRunContext.current_event`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.stream_helper.StreamRunContext.current_event) - * [`StreamRunContext.current_tool_calls`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.stream_helper.StreamRunContext.current_tool_calls) - * [`StreamRunContext.current_run_id`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.stream_helper.StreamRunContext.current_run_id) - * [`StreamRunContext.current_run_step_id`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.stream_helper.StreamRunContext.current_run_step_id) - * [`StreamRunContext.current_thread_id`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.stream_helper.StreamRunContext.current_thread_id) - * [`StreamRunContext.current_assistant_id`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.stream_helper.StreamRunContext.current_assistant_id) - * [`StreamRunContext.current_assistant_id`](appbuilder.core.assistant.threads.runs.md#id0) - * [`StreamRunContext.current_event`](appbuilder.core.assistant.threads.runs.md#id1) - * [`StreamRunContext.current_run_id`](appbuilder.core.assistant.threads.runs.md#id2) - * [`StreamRunContext.current_run_step_id`](appbuilder.core.assistant.threads.runs.md#id3) - * [`StreamRunContext.current_thread_id`](appbuilder.core.assistant.threads.runs.md#id4) - * [`StreamRunContext.current_tool_calls`](appbuilder.core.assistant.threads.runs.md#id5) - * [`StreamRunContext.reset_step_context()`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.stream_helper.StreamRunContext.reset_step_context) - * [`StreamRunContext.set_current_assistant_id()`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.stream_helper.StreamRunContext.set_current_assistant_id) - * [`StreamRunContext.set_current_event()`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.stream_helper.StreamRunContext.set_current_event) - * [`StreamRunContext.set_current_run_id()`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.stream_helper.StreamRunContext.set_current_run_id) - * [`StreamRunContext.set_current_run_step_id()`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.stream_helper.StreamRunContext.set_current_run_step_id) - * [`StreamRunContext.set_current_thread_id()`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.stream_helper.StreamRunContext.set_current_thread_id) - * [`StreamRunContext.set_current_tool_calls()`](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.stream_helper.StreamRunContext.set_current_tool_calls) - -## Submodules - -## appbuilder.core.assistant.threads.threads module - -### *class* appbuilder.core.assistant.threads.threads.Threads - -基类:`object` - -#### create(messages: list[AssistantMessage] | None = []) → ThreadCreateResponse - -创建一个新的对话线程。 - -* **参数:** - **messages** -- 要发送给助手的消息列表。如果不传入此参数,则会创建一个空对话线程。 -* **返回:** - 一个ThreadCreateResponse对象,包含新创建的线程的相关信息。 -* **抛出:** - **ValueError** -- 如果传入的messages参数不是列表类型。 - -#### delete(thread_id: str) → ThreadDeleteResponse - -删除对话线程。 -:param thread_id: 要删除的对话线程ID。 - -* **返回:** - 一个ThreadDeleteResponse对象,包含对话线程的相关信息。 -* **抛出:** - **ValueError** -- 如果传入的thread_id参数不是字符串类型。 - -#### *property* messages *: [Messages](appbuilder.core.assistant.threads.messages.md#appbuilder.core.assistant.threads.messages.messages.Messages)* - -获取消息实例 - -* **参数:** - **无** -* **返回:** - 返回Messages实例 -* **返回类型:** - [Messages](appbuilder.core.assistant.threads.messages.md#appbuilder.core.assistant.threads.messages.messages.Messages) - -#### query(thread_id: str) → ThreadQueryResponse - -查询对话线程信息。 - -* **参数:** - **thread_id** -- 要查询的对话线程ID。 -* **返回:** - 一个ThreadQueryResponse对象,包含对话线程的相关信息。 -* **抛出:** - **ValueError** -- 如果传入的thread_id参数不是字符串类型。 - -#### *property* runs *: [Runs](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.runs.Runs)* - -返回Runs对象。 - -* **参数:** - **无** -* **返回:** - 一个Runs对象实例。 -* **返回类型:** - [Runs](appbuilder.core.assistant.threads.runs.md#appbuilder.core.assistant.threads.runs.runs.Runs) - -#### update(thread_id: str, metadata: dict | None = {}) → ThreadUpdateResponse - -更新线程信息 - -* **参数:** - * **thread_id** (*str*) -- 线程ID - * **metadata** (*Optional* *[**dict* *]* *,* *optional*) -- 线程元数据. 默认为空字典. -* **返回:** - 线程更新响应 -* **返回类型:** - thread_type.ThreadUpdateResponse -* **抛出:** - * **TypeError** -- 如果metadata不是字典类型 - * **ValueError** -- 如果metadata的键超过64个字符或值超过512个字符 diff --git a/docs/API-Reference/Python/appbuilder.core.assistant.threads.messages.md b/docs/API-Reference/Python/appbuilder.core.assistant.threads.messages.md deleted file mode 100644 index 267910805..000000000 --- a/docs/API-Reference/Python/appbuilder.core.assistant.threads.messages.md +++ /dev/null @@ -1,88 +0,0 @@ -# appbuilder.core.assistant.threads.messages package - -## Submodules - -## appbuilder.core.assistant.threads.messages.messages module - -### *class* appbuilder.core.assistant.threads.messages.messages.Messages - -基类:`object` - -#### create(thread_id: str, content: str, role: str | None = 'user', file_ids: list[str] | None = []) → AssistantMessageCreateResponse - -创建一条消息。 - -* **参数:** - * **thread_id** (*str*) -- 线程ID。 - * **content** (*str*) -- 消息内容。 - * **role** (*Optional* *[**str* *]* *,* *optional*) -- 角色,可选值为"user"或"assistant"。默认为"user"。 - * **file_ids** (*Optional* *[**list* *[**str* *]* *]* *,* *optional*) -- 消息中包含的文件ID列表。默认为空列表。 -* **返回:** - 消息创建响应对象。 -* **返回类型:** - thread_type.AssistantMessageCreateResponse -* **抛出:** - **HttpError** -- 如果请求失败,则抛出HttpError异常。 - -#### files(thread_id: str, message_id: str, limit: int | None = 20, order: str | None = 'desc', after: str | None = '', before: str | None = '') → AssistantMessageFilesResponse - -获取指定消息 ID 的附件信息。 - -* **参数:** - * **thread_id** (*str*) -- 线程 ID。 - * **messsages_id** (*str*) -- 消息 ID。 - * **limit** (*Optional* *[**int* *]* *,* *optional*) -- 返回结果的最大数量,默认为 20。 - * **order** (*Optional* *[**str* *]* *,* *optional*) -- 排序方式,可选值为 "asc" 或 "desc",默认为 "desc"。 - * **after** (*Optional* *[**str* *]* *,* *optional*) -- 返回结果的时间范围,只返回时间晚于该时间戳的消息附件,默认为空。 - * **before** (*Optional* *[**str* *]* *,* *optional*) -- 返回结果的时间范围,只返回时间早于该时间戳的消息附件,默认为空。 -* **返回:** - 附件信息响应对象。 -* **返回类型:** - thread_type.AssistantMessageFilesResponse - -#### list(thread_id: str, limit: int = 20, order: str = 'desc', after: str = '', before: str = '') → AssistantMessageListResponse - -查询指定Thread下的Message列表 - -* **参数:** - * **thread_id** (*str*) -- 线程ID。 - * **limit** (*int* *,* *optional*) -- 返回消息的最大数量,取值范围为[1,20]。默认为-20。 - * **order** (*Optional* *[**str* *]* *,* *optional*) -- 排序方式,可选值为"asc"或"desc"。默认为"desc"。 - * **after** (*Optional* *[**str* *]* *,* *optional*) -- 查询指定message_id之后创建的Message。 - * **before** (*Optional* *[**str* *]* *,* *optional*) -- 查询指定message_id之前创建的Message -* **返回:** - 查询thread下的message列表响应对象。 -* **返回类型:** - thread_type.AssistantMessageListResponse -* **抛出:** - **HttpError** -- 如果请求失败,则抛出HttpError异常。 - -#### query(thread_id: str, message_id: str) → AssistantMessageQueryResponse - -根据message_id查询指定Message的信息 - -* **参数:** - * **thread_id** (*str*) -- 线程ID - * **message_id** (*str*) -- 消息ID -* **返回:** - 消息信息响应 -* **返回类型:** - thread_type.AssistantMessageQueryResponse -* **抛出:** - **HttpError** -- 如果请求失败,则抛出HttpError异常。 - -#### update(thread_id: str, message_id: str, content: str | None, file_ids: list[str] | None = []) → AssistantMessageUpdateResponse - -修改Message对象,允许content和file_ids字段 - -* **参数:** - * **thread_id** (*str*) -- 线程ID。 - * **message_id** (*str*) -- 消息ID。 - * **content** (*Optional* *[**str* *]* *,* *optional*) -- 消息内容。默认为空字符串。 - * **file_ids** (*Optional* *[**list* *[**str* *]* *]* *,* *optional*) -- 消息中包含的文件ID列表。默认为空列表。 -* **返回:** - 消息更新响应对象。 -* **返回类型:** - thread_type.AssistantMessageUpdateResponse -* **抛出:** - **HttpError** -- 如果请求失败,则抛出HttpError异常。 diff --git a/docs/API-Reference/Python/appbuilder.core.assistant.threads.runs.md b/docs/API-Reference/Python/appbuilder.core.assistant.threads.runs.md deleted file mode 100644 index 8133643b5..000000000 --- a/docs/API-Reference/Python/appbuilder.core.assistant.threads.runs.md +++ /dev/null @@ -1,529 +0,0 @@ -# appbuilder.core.assistant.threads.runs package - -## Submodules - -## appbuilder.core.assistant.threads.runs.runs module - -### *class* appbuilder.core.assistant.threads.runs.runs.Runs - -基类:`object` - -#### cancel(run_id: str, thread_id: str) → RunResult - -取消指定线程的运行 - -* **参数:** - * **run_id** (*str*) -- 运行的ID - * **thread_id** (*str*) -- 线程的ID -* **返回:** - 取消运行的结果 -* **返回类型:** - thread_type.RunResult - -#### list(thread_id: str, limit: int = 20, order: str = 'desc', after: str = '', before: str = '') → RunListResponse - -列出对应thread的历史run记录 - -* **参数:** - * **thread_id** (*str*) -- 线程ID - * **limit** (*int* *,* *optional*) -- 列表数量限制,默认为20 - * **order** (*str* *,* *optional*) -- 排序方式,'asc'为升序,'desc'为降序,默认为'desc' - * **after** (*str* *,* *optional*) -- 返回在指定时间之后的运行列表,默认为空字符串 - * **before** (*str* *,* *optional*) -- 返回在指定时间之前的运行列表,默认为空字符串 -* **返回:** - 列出对应thread的历史run记录 -* **返回类型:** - thread_type.RunListResponse -* **抛出:** - **无** -- - -#### query(thread_id: str, run_id: str) → RunResult - -根据thread_id和run_id,查询run的详情 - -* **参数:** - * **thread_id** (*str*) -- 线程ID。 - * **run_id** (*str*) -- 运行ID。 -* **返回:** - 查询到的运行结果。 -* **返回类型:** - thread_type.RunResult - -#### run(assistant_id: str, thread_id: str | None = '', thread: AssistantThread | None = None, model: str | None = None, response_format: str | None = 'text', instructions: str | None = '', thought_instructions: str | None = '', chat_instructions: str | None = '', tools: list[AssistantTool] | None = [], metadata: dict | None = {}, tool_output: ToolOutput | None = None, model_parameters: AssistantModelParameters | None = None, user_info: AssistantUserInfo | None = None, user_loc: AssistantUserLoc | None = None) → RunResult - -* **参数:** - * **assistant_id** (*str*) -- 助手id - * **thread_id** (*Optional* *[**str* *]* *,* *optional*) -- 对话id. Defaults to "". - * **thread** (*Optional* *[**thread_type.AssistantThread* *]* *,* *optional*) -- 对话信息. Defaults to None. - * **model** (*Optional* *[**str* *]* *,* *optional*) -- 模型名称. Defaults to None. - * **response_format** (*Optional* *[**str* *]* *,* *optional*) -- 返回格式. Defaults to "text". - * **instructions** (*Optional* *[**str* *]* *,* *optional*) -- 指令信息. Defaults to "". - * **thought_instructions** (*Optional* *[**str* *]* *,* *optional*) -- 思考指令信息. Defaults to "". - * **chat_instructions** (*Optional* *[**str* *]* *,* *optional*) -- 闲聊指令信息. Defaults to "". - * **tools** (*Optional* *[**list* *[**assistant_type.AssistantTool* *]* *]* *,* *optional*) -- 工具列表. Defaults to []. - * **metadata** (*Optional* *[**dict* *]* *,* *optional*) -- 元数据. Defaults to {}. - * **tool_output** (*Optional* *[**thread_type.ToolOutput* *]* *,* *optional*) -- 工具输出. Defaults to None. - * **model_parameters** (*Optional* *[**public_type.AssistantModelParameters* *]* *,* *optional*) -- 模型运行参数. Defaults to None. - * **user_info** (*Optional* *[**public_type.AssistantUserInfo* *]* *,* *optional*) -- 用户身份信息. Defaults to None. - * **user_loc** (*Optional* *[**public_type.AssistantUserLoc* *]* *,* *optional*) -- 用户定位信息. Defaults to None. -* **返回:** - 运行结果 -* **返回类型:** - thread_type.RunResult -* **抛出:** - **ValueError** -- thread_id和thread不能同时为空,model_parameters的各个参数不在规定范围内 - -#### NOTE -1. 如果thread_id没有传,则thread必须要传值 -2. 如果这里不传值,thread_id查出来的历史对话,最后一条消息的role必须为user -3. 如果这里传值,则需要保证thread_id查出来的历史对话 + 本轮追加的thread对话,最后一条消息的role必须为user - -#### *property* steps *: [Steps](#appbuilder.core.assistant.threads.runs.steps.Steps)* - -返回步骤对象 - -* **参数:** - **无** -* **返回:** - 返回一个新的Steps对象 -* **返回类型:** - [Steps](#appbuilder.core.assistant.threads.runs.steps.Steps) - -#### stream_run(assistant_id: str, thread_id: str | None = '', thread: AssistantThread | None = None, model: str | None = None, response_format: str | None = 'text', instructions: str | None = '', thought_instructions: str | None = '', chat_instructions: str | None = '', tools: list[AssistantTool] | None = [], metadata: dict | None = {}, tool_output: ToolOutput | None = None, model_parameters: AssistantModelParameters | None = None, user_info: AssistantUserInfo | None = None, user_loc: AssistantUserLoc | None = None) → StreamRunStatus | StreamRunMessage | None - -启动一个流式运行的对话,用于处理对话流中的消息。 - -* **参数:** - * **assistant_id** (*str*) -- 助理ID。 - * **thread_id** (*Optional* *[**str* *]* *,* *optional*) -- 线程ID,用于恢复历史对话。默认为空字符串。 - * **thread** (*Optional* *[**thread_type.AssistantThread* *]* *,* *optional*) -- 线程对象,用于恢复历史对话。默认为None。 - * **model** (*Optional* *[**str* *]* *,* *optional*) -- 使用的模型名称。默认为None。 - * **response_format** (*Optional* *[**str* *]* *,* *optional*) -- 响应格式,支持"text"和"json"两种格式。默认为"text"。 - * **instructions** (*Optional* *[**str* *]* *,* *optional*) -- 指令文本。默认为空字符串。 - * **thought_instructions** (*Optional* *[**str* *]* *,* *optional*) -- 思考指令文本。默认为空字符串。 - * **chat_instructions** (*Optional* *[**str* *]* *,* *optional*) -- 聊天指令文本。默认为空字符串。 - * **tools** (*Optional* *[**list* *[**assistant_type.AssistantTool* *]* *]* *,* *optional*) -- 使用的工具列表。默认为空列表。 - * **metadata** (*Optional* *[**dict* *]* *,* *optional*) -- 元数据字典。默认为空字典。 - * **tool_output** (*Optional* *[**thread_type.ToolOutput* *]* *,* *optional*) -- 工具输出对象。默认为None。 - * **model_parameters** (*Optional* *[**public_type.AssistantModelParameters* *]* *,* *optional*) -- 模型参数对象。默认为None。 -* **返回:** - 返回一个迭代器,每次迭代返回一个处理结果对象,可能是 StreamRunStatus 或 StreamRunMessage。 -* **返回类型:** - Union[thread_type.StreamRunStatus, thread_type.StreamRunMessage, None] -* **抛出:** - **ValueError** -- 如果thread_id和thread参数同时为空,则会引发ValueError异常。 - -#### NOTE -1. 如果thread_id没有传,则thread必须要传值。 -2. 如果这里不传值,thread_id查出来的历史对话,最后一条消息的role必须为user。 -3. 如果这里传值,则需要保证thread_id查出来的历史对话 + 本轮追加的thread对话,最后一条消息的role必须为user。 - -#### stream_run_with_handler(assistant_id: str, thread_id: str | None = '', thread: AssistantThread | None = None, model: str | None = None, response_format: str | None = 'text', instructions: str | None = '', thought_instructions: str | None = '', chat_instructions: str | None = '', tools: list[AssistantTool] | None = [], metadata: dict | None = {}, tool_output: ToolOutput | None = None, event_handler: [AssistantEventHandler](#appbuilder.core.assistant.threads.runs.stream_helper.AssistantEventHandler) | None = None, model_parameters: AssistantModelParameters | None = None, user_info: AssistantUserInfo | None = None, user_loc: AssistantUserLoc | None = None) → [AssistantStreamManager](#appbuilder.core.assistant.threads.runs.stream_helper.AssistantStreamManager) - -使用带有事件处理器的流运行助手 - -* **参数:** - * **assistant_id** (*str*) -- 助手的唯一标识符 - * **thread_id** (*Optional* *[**str* *]* *,* *optional*) -- 会话线程的标识符,默认为空字符串. 默认为 "". - * **thread** (*Optional* *[**thread_type.AssistantThread* *]* *,* *optional*) -- 会话线程对象,默认为None. 默认为 None. - * **model** (*Optional* *[**str* *]* *,* *optional*) -- 模型标识符,默认为None. 默认为 None. - * **response_format** (*Optional* *[**str* *]* *,* *optional*) -- 响应格式,可选值为"text"或"json",默认为"text". 默认为 "text". - * **instructions** (*Optional* *[**str* *]* *,* *optional*) -- 主要指令,默认为空字符串. 默认为 "". - * **thought_instructions** (*Optional* *[**str* *]* *,* *optional*) -- 思维指令,默认为空字符串. 默认为 "". - * **chat_instructions** (*Optional* *[**str* *]* *,* *optional*) -- 聊天指令,默认为空字符串. 默认为 "". - * **tools** (*Optional* *[**list* *[**assistant_type.AssistantTool* *]* *]* *,* *optional*) -- 助手工具列表,默认为空列表. 默认为 []. - * **metadata** (*Optional* *[**dict* *]* *,* *optional*) -- 元数据字典,默认为空字典. 默认为 {}. - * **tool_output** (*Optional* *[**thread_type.ToolOutput* *]* *,* *optional*) -- 工具输出对象,默认为None. 默认为 None. - * **event_handler** (*Optional* *[*[*AssistantEventHandler*](#appbuilder.core.assistant.threads.runs.stream_helper.AssistantEventHandler) *]* *,* *optional*) -- 事件处理器对象,默认为None. 默认为 None. - * **model_parameters** (*Optional* *[**public_type.AssistantModelParameters* *]* *,* *optional*) -- 模型参数对象,默认为None. 默认为 None. - * **user_info** (*Optional* *[**public_type.AssistantUserInfo* *]* *,* *optional*) -- 用户信息对象,默认为None. 默认为 None. - * **user_loc** (*Optional* *[**public_type.AssistantUserLoc* *]* *,* *optional*) -- 用户位置信息对象,默认为None. 默认为 None. -* **返回:** - 返回的流管理器对象 -* **返回类型:** - [AssistantStreamManager](#appbuilder.core.assistant.threads.runs.stream_helper.AssistantStreamManager) -* **抛出:** - **HTTPError** -- 如果HTTP响应状态码不为200,则抛出HTTPError异常 - -#### submit_tool_outputs(run_id: str, thread_id: str, tool_outputs: list[ToolOutput] | None) → RunResult - -向服务端提交工具输出 - -* **参数:** - * **run_id** (*str*) -- 运行ID - * **thread_id** (*str*) -- 线程ID - * **tool_outputs** (*Optional* *[**list* *[**thread_type.ToolOutput* *]* *]*) -- 工具输出列表,可选 -* **返回:** - 运行结果 -* **返回类型:** - thread_type.RunResult - -## appbuilder.core.assistant.threads.runs.steps module - -### *class* appbuilder.core.assistant.threads.runs.steps.Steps - -基类:`object` - -#### list(thread_id: str, run_id: str, limit: int = 20, order: str = 'desc', after: str = '', before: str = '') → RunStepListResponse - -根据thread_id和run_id,列出对应run的历史step记录 - -* **参数:** - * **thread_id** (*str*) -- 线程ID - * **run_id** (*str*) -- 运行ID - * **limit** (*int* *,* *optional*) -- 步骤数量限制,默认为20 - * **order** (*str* *,* *optional*) -- 排序方式,'asc'表示升序,'desc'表示降序,默认为'desc' - * **after** (*str* *,* *optional*) -- 过滤出时间戳晚于此值的步骤,默认为空 - * **before** (*str* *,* *optional*) -- 过滤出时间戳早于此值的步骤,默认为空 -* **返回:** - 线程运行步骤列表的响应对象 -* **返回类型:** - thread_type.RunStepListResponse - -#### query(thread_id: str, run_id: str, step_id: str) → RunStepResult - -根据thread_id,run_id和step_id,查询对应step的信息 - -* **参数:** - * **thread_id** (*str*) -- 线程ID - * **run_id** (*str*) -- 运行ID - * **step_id** (*str*) -- 步骤ID -* **返回:** - 步骤运行结果 -* **返回类型:** - thread_type.RunStepResult - -## appbuilder.core.assistant.threads.runs.stream_helper module - -### *class* appbuilder.core.assistant.threads.runs.stream_helper.AssistantEventHandler - -基类:`object` - -AssistantEventHandler类用于处理Assistant流式返回的相关事件。 - -这个类作为Assistant流式事件的处理中心,负责接收和处理来自Assistant的各种事件, -如用户交互、数据更新、状态变化等。通过实现不同的事件处理方法, -可以定义Assistant在不同事件下的行为逻辑。 - -Assistant事件处理程序通常与具体的Assistant实例相关联,用于管理和控制Assistant的运行流程, -以及与其他系统组件的交互。 - -该类包含多个方法,每个方法对应一种特定事件的处理逻辑。 -当相应的事件发生时,Assistant或相关系统会调用这些方法,以执行预定义的操作。 - -通过继承AssistantEventHandler类并重写其方法,可以实现自定义的Assistant流式事件处理逻辑, -从而满足特定的业务需求。 - -#### message_creation(status_event: StreamRunStatus) - -当触发 message_creation 事件时回调此函数。 - -* **参数:** - **status_event** (*thread_type.StreamRunStatus*) -- 消息创建状态事件对象 -* **返回:** - None - -#### NOTE -用户可以重载此函数,实现自定义的消息创建处理逻辑。 - -#### messages(messages_event: StreamRunMessage) - -当触发 messages 打印事件时回调此函数。 - -* **参数:** - **messages_event** (*thread_type.StreamRunMessage*) -- 包含消息内容的事件对象 -* **返回:** - None - -#### NOTE -用户可以重载此函数,实现自定义的消息处理逻辑。 - -#### run_begin(status_event: StreamRunStatus) - -当触发 run_begin 事件时回调此函数。 - -* **参数:** - **status_event** (*thread_type.StreamRunStatus*) -- 运行开始状态事件对象 -* **返回:** - None - -#### NOTE -用户可以重载此函数,实现自定义的运行开始处理逻辑。 - -#### run_cancelling(status_event: StreamRunStatus) - -当触发 run_cancelling 事件时回调此函数。 - -* **参数:** - **status_event** (*thread_type.StreamRunStatus*) -- 运行取消状态事件对象 -* **返回:** - None - -#### NOTE -用户可以重载此函数,实现自定义的运行取消处理逻辑。 - -#### run_end(status_event: StreamRunStatus) - -当触发 run_end 事件时回调此函数。 - -* **参数:** - **status_event** (*thread_type.StreamRunStatus*) -- 运行结束状态事件对象 -* **返回:** - None - -#### NOTE -用户可以重载此函数,实现自定义的运行结束处理逻辑。 - -#### tool_calls(status_event: StreamRunStatus) - -当触发 tool_calls 事件时回调此函数。 - -* **参数:** - **status_event** (*thread_type.StreamRunStatus*) -- 工具调用状态事件对象 -* **返回:** - None - -#### NOTE -用户可以重载此函数,实现自定义的工具调用处理逻辑。 - -#### tool_step_begin(status_event: StreamRunStatus) - -当触发 tool_step_begin 事件时回调此函数。 - -* **参数:** - **status_event** (*thread_type.StreamRunStatus*) -- 工具步骤开始状态事件对象 -* **返回:** - None - -#### NOTE -用户可以重载此函数,实现自定义的工具步骤开始处理逻辑。 - -#### tool_step_end(status_event: StreamRunStatus) - -当触发 tool_step_end 事件时回调此函数。 - -* **参数:** - **status_event** (*thread_type.StreamRunStatus*) -- 工具步骤结束状态事件对象 -* **返回:** - None - -#### NOTE -用户可以重载此函数,实现自定义的工具步骤结束处理逻辑。 - -#### tool_submitted_output(status_event: StreamRunStatus) - -当触发 tool_submitted_output 事件时回调此函数。 - -* **参数:** - **status_event** (*thread_type.StreamRunStatus*) -- 工具提交输出状态事件对象 -* **返回:** - None - -#### NOTE -用户可以重载此函数,实现自定义的工具输出处理逻辑。 - -#### until_done() - -直到迭代器结束为止,持续迭代。 - -* **参数:** - **无** -* **返回:** - 无 -* **抛出:** - **无** -- - -### *class* appbuilder.core.assistant.threads.runs.stream_helper.AssistantStreamManager(response, event_handler: [AssistantEventHandler](#appbuilder.core.assistant.threads.runs.stream_helper.AssistantEventHandler)) - -基类:[`AssistantEventHandler`](#appbuilder.core.assistant.threads.runs.stream_helper.AssistantEventHandler) - -### *class* appbuilder.core.assistant.threads.runs.stream_helper.StreamRunContext - -基类:`object` - -StreamRunContext类用于管理和维护流式运行时的上下文信息。 - -这个类提供了存储和获取当前流事件、工具调用、运行ID、运行步骤ID、线程ID和助手ID等属性的功能。 -通过创建StreamRunContext的实例,可以方便地跟踪和处理流式运行时的各种状态和数据。 - -#### current_event - -当前流事件的对象。 - -#### current_tool_calls - -当前工具调用的相关信息。 - -#### current_run_id - -当前运行的唯一标识符。 - -#### current_run_step_id - -当前运行步骤的唯一标识符。 - -#### current_thread_id - -当前线程的唯一标识符。 - -#### current_assistant_id - -当前助手的唯一标识符。 - -#### NOTE -这个类通常作为其他流式处理类(如StreamProcessor、StreamHandler等)的组成部分, -用于在流式处理过程中传递和共享上下文信息。 - -#### *property* current_assistant_id *: str | None* - -获取当前助手ID。 - -* **参数:** - **无** -* **返回:** - 返回当前助手ID的字符串,如果未设置则返回None。 -* **返回类型:** - Union[str, None] - -#### *property* current_event *: StreamRunStatus | StreamRunMessage | None* - -获取当前事件。 - -* **参数:** - **无** -* **返回:** - 当前事件,可能为StreamRunStatus类型(表示流运行状态)、StreamRunMessage类型(表示流运行消息)或None。 -* **返回类型:** - Union[thread_type.StreamRunStatus, thread_type.StreamRunMessage, None] - -#### *property* current_run_id *: str | None* - -获取当前运行的ID。 - -* **参数:** - **无参数。** -* **返回:** - 返回当前运行的ID,如果没有当前运行的ID,则返回None。 -* **返回类型:** - str 或 None - -#### *property* current_run_step_id *: str | None* - -获取当前运行的步骤ID。 - -* **参数:** - **无参数。** -* **返回:** - 当前运行的步骤ID,如果没有运行任何步骤则返回None。 -* **返回类型:** - Union[str, None] - -#### *property* current_thread_id *: str | None* - -获取当前线程的ID。 - -* **参数:** - **无参数。** -* **返回:** - 当前线程的ID,如果当前没有线程ID则返回None。 -* **返回类型:** - Union[str, None] - -#### *property* current_tool_calls *: list[ToolCall] | None* - -获取当前工具调用列表。 - -* **参数:** - **无** -* **返回:** - 如果存在当前工具调用列表,则返回该列表;否则返回None。 -* **返回类型:** - Union[list[thread_type.ToolCall], None] - -#### reset_step_context() - -重置步骤上下文。 - -* **参数:** - **无** -* **返回:** - 无 - -#### set_current_assistant_id(assistant_id) - -设置当前助手ID。 - -* **参数:** - **assistant_id** (*str*) -- 需要设置的助手ID。 -* **返回:** - None -* **抛出:** - **无** -- - -#### NOTE -如果输入的assistant_id是有效的字符串且长度大于0,则将其设置为当前助手ID;否则,将当前助手ID设置为None。 - -#### set_current_event(event) - -设置当前事件 - -* **参数:** - **event** (*thread_type.StreamRunStatus* *or* *thread_type.StreamRunMessage*) -- 需要设置的事件对象 -* **返回:** - None -* **抛出:** - **无** -- - -#### set_current_run_id(run_id) - -设置当前运行ID。 - -* **参数:** - **run_id** (*str*) -- 运行ID字符串。 -* **返回:** - None -* **抛出:** - **无** -- - -注意事项: -: 如果传入的run_id不是字符串类型或长度为0,则不设置当前运行ID,将其设置为None。 - -#### set_current_run_step_id(run_step_id) - -设置当前运行步骤的ID。 - -* **参数:** - **run_step_id** (*str*) -- 需要设置的运行步骤ID。 -* **返回:** - None -* **抛出:** - **无** -- - -#### NOTE -如果传入的run_step_id是一个非空字符串,则将其设置为当前运行步骤的ID; -否则,将当前运行步骤的ID设置为None。 - -#### set_current_thread_id(thread_id) - -设置当前线程的ID。 - -* **参数:** - **thread_id** (*str*) -- 要设置的线程ID。 -* **返回:** - None -* **抛出:** - **无** -- - -#### NOTE -如果thread_id不是字符串类型或者长度为0,则不会设置当前线程的ID,并将其设置为None。 - -#### set_current_tool_calls(tool_calls) - -设置当前工具调用列表。 - -* **参数:** - **tool_calls** (*list* *of* *thread_type.ToolCall*) -- 工具调用列表。 -* **返回:** - None -* **抛出:** - **AssertionError** -- 如果 tool_calls 不是 ToolCall 对象的列表。 diff --git a/docs/API-Reference/Python/appbuilder.core.components.animal_recognize.md b/docs/API-Reference/Python/appbuilder.core.components.animal_recognize.md deleted file mode 100644 index 12cd23ef1..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.animal_recognize.md +++ /dev/null @@ -1,59 +0,0 @@ -# appbuilder.core.components.animal_recognize package - -## Submodules - -## appbuilder.core.components.animal_recognize.component module - -animal recognize component. - -### *class* appbuilder.core.components.animal_recognize.component.AnimalRecognition(meta: [ComponentArguments](appbuilder.core.md#appbuilder.core.component.ComponentArguments) | None = ComponentArguments(name='', tool_desc={}), secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -用于识别一张图片,即对于输入的一张图片(可正常解码,且长宽比较合适),输出动物识别结果。 - -Examples: - -```python -import appbuilder -# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 -os.environ["APPBUILDER_TOKEN"] = '...' - -animal_recognition = appbuilder.AnimalRecognition() -with open("./animal_recognition_test.png", "rb") as f: - out = self.component.run(appbuilder.Message(content={"raw_image": f.read()})) -print(out.content) -``` - -#### manifests *= [{'description': '用于识别图片中动物类别,可识别近八千种动物', 'name': 'animal_rec', 'parameters': {'anyOf': [{'required': ['img_name']}, {'required': ['img_url']}], 'properties': {'img_name': {'description': '待识别图片的文件名', 'type': 'string'}, 'img_url': {'description': '待识别图片的url', 'type': 'string'}}, 'type': 'object'}}]* - -#### name *= 'animal_rec'* - -#### run(message: [Message](appbuilder.core.md#appbuilder.core.message.Message), timeout: float = None, retry: int = 0) → [Message](appbuilder.core.md#appbuilder.core.message.Message) - -根据输入消息运行动物识别功能 - -* **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入的消息对象,其中应包含需要识别的图像数据或URL - * **timeout** (*float* *,* *optional*) -- 超时时间,单位为秒。默认为None,表示无超时限制。Defaults to None. - * **retry** (*int* *,* *optional*) -- 重试次数。默认为0,表示不重试。Defaults to 0. -* **返回:** - 识别结果的消息对象 -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message) - -#### tool_eval(name: str, streaming: bool, origin_query: str, \*\*kwargs) → Generator[str, None, None] | str - -用于工具的执行,通过调用底层接口进行动物识别。 - -* **参数:** - * **name** (*str*) -- 工具名。 - * **streaming** (*bool*) -- 是否流式返回。 - * **origin_query** (*str*) -- 用户原始query。 - * **\*\*kwargs** -- 工具调用的额外关键字参数。 -* **返回:** - 动物识别结果,包括识别出的动物类别和相应的置信度信息。 -* **返回类型:** - Union[Generator[str, None, None], str] - -#### version *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.asr.md b/docs/API-Reference/Python/appbuilder.core.components.asr.md deleted file mode 100644 index 29afeb9ec..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.asr.md +++ /dev/null @@ -1,63 +0,0 @@ -# appbuilder.core.components.asr package - -## Submodules - -## appbuilder.core.components.asr.component module - -ASR component. - -### *class* appbuilder.core.components.asr.component.ASR(meta: [ComponentArguments](appbuilder.core.md#appbuilder.core.component.ComponentArguments) | None = ComponentArguments(name='', tool_desc={}), secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -ASR组件,即对于输入的语音文件,输出语音识别结果 - -Examples: - -```python -import appbuilder -asr = appbuilder.ASR() -os.environ["APPBUILDER_TOKEN"] = '...' - -with open("xxxx.pcm", "rb") as f: - audio_data = f.read() -content_data = {"audio_format": "pcm", "raw_audio": audio_data, "rate": 16000} -msg = appbuilder.Message(content_data) -out = asr.run(msg) -print(out.content) # eg: {"result": ["北京科技馆。"]} -``` - -#### manifests *= [{'description': '对于输入的语音文件进行识别,输出语音识别结果。', 'name': 'asr', 'parameters': {'anyOf': [{'required': ['file_url']}, {'required': ['file_name']}], 'properties': {'file_name': {'description': '待识别语音文件名,用于生成获取语音的url', 'type': 'string'}, 'file_type': {'description': '语音文件类型,支持pcm/wav/amr/m4a', 'enum': ['pcm', 'wav', 'amr', 'm4a'], 'type': 'string'}, 'file_url': {'description': '输入语音文件的url,根据url获取到语音文件', 'type': 'string'}}, 'type': 'object'}}]* - -#### name *= 'asr'* - -#### run(message: [Message](appbuilder.core.md#appbuilder.core.message.Message), audio_format: str = 'pcm', rate: int = 16000, timeout: float = None, retry: int = 0, \*\*kwargs) → [Message](appbuilder.core.md#appbuilder.core.message.Message) - -执行语音识别操作,并返回识别结果。 - -* **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入消息对象,包含待识别的音频数据。该参数为必需项,格式如:Message(content={"raw_audio": b"..."})。 - * **audio_format** (*str* *,* *optional*) -- 音频文件格式,支持pcm/wav/amr/m4a,不区分大小写,推荐使用pcm格式。默认为"pcm"。 - * **rate** (*int* *,* *optional*) -- 音频采样率,固定为16000。默认为16000。 - * **timeout** (*float* *,* *optional*) -- HTTP请求超时时间。默认为None。 - * **retry** (*int* *,* *optional*) -- HTTP请求重试次数。默认为0。 -* **返回:** - 语音识别结果,格式如:Message(content={"result": ["识别结果"]})。 -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message) - -#### tool_eval(name: str, streaming: bool, \*\*kwargs) - -评估给定文件名或文件URL的语音识别结果。 - -* **参数:** - * **name** (*str*) -- 函数调用名称。 - * **streaming** (*bool*) -- 是否以流的方式返回结果。 - * **\*\*kwargs** -- 关键字参数,用于指定文件名、文件URL等参数。 -* **返回:** - 如果streaming为True,则通过生成器逐个返回包含识别结果的消息对象; - 如果streaming为False,则返回包含识别结果的JSON字符串。 -* **抛出:** - **InvalidRequestArgumentError** -- 如果未设置文件名或文件URL不存在,则抛出此异常。 - -#### version *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.dish_recognize.md b/docs/API-Reference/Python/appbuilder.core.components.dish_recognize.md deleted file mode 100644 index cddc7d7a5..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.dish_recognize.md +++ /dev/null @@ -1,42 +0,0 @@ -# appbuilder.core.components.dish_recognize package - -## Submodules - -## appbuilder.core.components.dish_recognize.component module - -菜品识别组件. - -### *class* appbuilder.core.components.dish_recognize.component.DishRecognition(meta: [ComponentArguments](appbuilder.core.md#appbuilder.core.component.ComponentArguments) | None = ComponentArguments(name='', tool_desc={}), secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -菜品识别组件,适用于识别只含有单个菜品的图片,返回识别的菜品名称和卡路里信息 - -Examples: - -```python -import appbuilder - -# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 -os.environ["APPBUILDER_TOKEN"] = '...' - -dish_recognition = appbuilder.DishRecognition() - -with open("xxxx.jpg", "rb") as f: - resp = dish_recognition(appbuilder.Message({"raw_image": f.read()})) - # 输出示例 {'result': [{'name': '剁椒鱼头', 'calorie': '127'}]} - print(resp.content) -``` - -#### run(message: [Message](appbuilder.core.md#appbuilder.core.message.Message), timeout: float = None, retry: int = 0) → [Message](appbuilder.core.md#appbuilder.core.message.Message) - -根据输入图片进行菜品识别。 - -* **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入待识别图片,支持传图片二进制流和图片URL。 - * **timeout** (*float* *,* *optional*) -- 请求超时时间,默认为 None。 - * **retry** (*int* *,* *optional*) -- 重试次数,默认为 0。 -* **返回:** - 包含菜品识别结果的输出消息。例如,Message(content={'result': [{'name': '剁椒鱼头', 'calorie': '127'}]}) -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message) diff --git a/docs/API-Reference/Python/appbuilder.core.components.doc_crop_enhance.md b/docs/API-Reference/Python/appbuilder.core.components.doc_crop_enhance.md deleted file mode 100644 index 2d4b7d126..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.doc_crop_enhance.md +++ /dev/null @@ -1,49 +0,0 @@ -# appbuilder.core.components.doc_crop_enhance package - -## Submodules - -## appbuilder.core.components.doc_crop_enhance.component module - -doc_crop_enhance component. - -### *class* appbuilder.core.components.doc_crop_enhance.component.DocCropEnhance(meta: [ComponentArguments](appbuilder.core.md#appbuilder.core.component.ComponentArguments) | None = ComponentArguments(name='', tool_desc={}), secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -对图片中的文件、卡证、票据等内容进行四角点检测定位,提取主体内容并对其进行矫正,同时可选图片增强效果进一步提升图片清晰度, -达到主体检测矫正并增强的目的,提升图片整体质量 - -Examples: - -```python -import appbuilder - -# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 -os.environ["APPBUILDER_TOKEN"] = '...' - -doc_crop_enhance = appbuilder.DocCropEnhance() -with open("./doc_enhance_test.png", "rb") as f: - out = self.component.run(appbuilder.Message(content={"raw_image": f.read()})) -print(out.content) -``` - -#### run(message: [Message](appbuilder.core.md#appbuilder.core.message.Message), enhance_type: int = 0, timeout: float = None, retry: int = 0) → [Message](appbuilder.core.md#appbuilder.core.message.Message) - -文档矫正增强 - -* **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入图片或图片url下载地址用于执行操作。举例: Message(content={"raw_image": b"...", - * **"enhance_type"** -- 3})或 Message(content={"url": "[https://image/download/url](https://image/download/url)"})。 - * **enhance_type** (*int* *,* *可选*) -- 选择是否开启图像增强功能,如开启可选择增强效果,可选值如下: - - 0:默认值,不开启增强功能 - - 1:去阴影 - - 2:增强并锐化 - - 3:黑白滤镜。 - * **timeout** (*float* *,* *可选*) -- HTTP超时时间 - * **retry** (*int* *,* *可选*) -- HTTP重试次数 -* **返回:** - 识别结果。举例: Message(name=msg, content={'image_processed': '...', - 'points': [{'x': 220, 'y': 705}, {'x': 240, 'y': 0}, {'x': 885, 'y': 2}, {'x': 980, 'y': 759}]}, - mtype=dict) -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message) diff --git a/docs/API-Reference/Python/appbuilder.core.components.doc_format_converter.md b/docs/API-Reference/Python/appbuilder.core.components.doc_format_converter.md deleted file mode 100644 index 564426f80..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.doc_format_converter.md +++ /dev/null @@ -1,82 +0,0 @@ -# appbuilder.core.components.doc_format_converter package - -## Submodules - -## appbuilder.core.components.doc_format_converter.component module - -文档格式转换 - -### *class* appbuilder.core.components.doc_format_converter.component.DocFormatConverter(meta: [ComponentArguments](appbuilder.core.md#appbuilder.core.component.ComponentArguments) | None = ComponentArguments(name='', tool_desc={}), secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -可识别图片/PDF文档版面布局,提取文字内容,并转换为保留原文档版式的Word、Excel文档,方便二次编辑和复制, -可支持含表格、印章、水印、手写等内容的文档。满足文档格式转换、企业档案电子化等信息管理需求。 - -### 示例 - -```python -import appbuilder -# 请前往千帆AppBuilder官网创建密钥 -os.environ["APPBUILDER_TOKEN"] = '...' - -table_ocr = appbuilder.DocFormatConverter() -out = self.component.run(appbuilder.Message(content={"file_path": ""})) -print(out.content) -``` - -#### manifests *= [{'description': '提供文档格式转换功能,包含图片转word、图片转excel、PDF转word、PDF转excel', 'name': 'doc_format_converter', 'parameters': {'anyOf': [{'required': ['file_name']}, {'required': ['file_url']}], 'properties': {'file_name': {'description': '待转换文件的文件名称', 'type': 'string'}, 'file_url': {'description': '待转换文件的URL地址', 'type': 'string'}, 'page_num': {'anyOf': [{'type': 'string'}, {'type': 'integer'}], 'description': '待转换PDF文档的页码, 从1开始, 如果不传则默认转换全部页码'}}, 'type': 'object'}}]* - -#### name *= 'doc_converter'* - -#### queryDocFormatConverterTask(request: DocFormatConverterQueryRequest, timeout: float | None = None, retry: int = 0, request_id: str | None = None) → DocFormatConverterQueryResponse - -查询任务 -:param request: 请求参数 -:type request: DoFormatcConverterQueryRequest -:return: 返回结果 -:rtype: DocFormatConverterSubmitResponse - -#### run(message: [Message](appbuilder.core.md#appbuilder.core.message.Message), timeout: float = None, retry: int = 0, request_id: str = None) → [Message](appbuilder.core.md#appbuilder.core.message.Message) - -将PDF、JPG、PNG、BMP等格式文件转换为Word、Excel格式,并返回转换后的文件URL。 - -* **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 包含待转换文件路径和页码信息的消息对象。 - * **timeout** (*float* *,* *optional*) -- 请求超时时间,单位为秒。默认为None,表示不设置超时时间。 - * **retry** (*int* *,* *optional*) -- 请求重试次数。默认为0,表示不重试。 -* **返回:** - 包含转换后文件URL的消息对象。 -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message) -* **抛出:** - **AppBuilderServerException** -- 文档格式转换服务发生错误时抛出。 - -#### submitDocFormatConverterTask(request: DocFormatConverterSubmitRequest, timeout: float | None = None, retry: int = 0, request_id: str | None = None) → DocFormatConverterSubmitResponse - -提交任务 -:param request: 请求参数 -:type request: DocFormatConverterSubmitRequest -:return: 返回结果 -:rtype: DocFormatConverterSubmitResponse - -#### tool_eval(streaming: bool, origin_query: str, \*\*kwargs) - -评估工具函数。 - -* **参数:** - * **streaming** (*bool*) -- 是否流式输出。如果为True,则逐个生成文件URL;如果为False,则直接返回结果内容。 - * **origin_query** (*str*) -- 原始查询字符串。 - * **\*\*kwargs** -- 其他关键字参数,包括但不限于: - traceid (str): 请求的跟踪ID,用于日志追踪。 - file_url (str): 文件的URL地址。如果为空,则从file_urls和file_name中获取。 - file_urls (dict): 包含多个文件路径与URL的映射关系的字典。 - file_name (str): 文件名。如果file_url为空,则从file_urls和file_name中获取file_url。 - page_num (Union[int, str]): 需要处理的页面编号,如果为字符串,必须为纯数字。 -* **返回:** - 如果streaming为True,则逐个生成包含文件URL的字典;如果streaming为False,则直接返回结果内容。 -* **抛出:** - * **InvalidRequestArgumentError** -- 如果请求格式错误,如page_num不是整数、file_url为空且无法从file_urls和file_name中获取file_url等。 - * **AppBuilderServerException** -- 如果服务执行过程中出现异常。 - -#### version *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.doc_parser.md b/docs/API-Reference/Python/appbuilder.core.components.doc_parser.md deleted file mode 100644 index 5e87eadfa..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.doc_parser.md +++ /dev/null @@ -1,68 +0,0 @@ -# appbuilder.core.components.doc_parser package - -## Submodules - -## appbuilder.core.components.doc_parser.doc_parser module - -文档解析 - -### *class* appbuilder.core.components.doc_parser.doc_parser.DocParser(meta: [ComponentArguments](appbuilder.core.md#appbuilder.core.component.ComponentArguments) | None = ComponentArguments(name='', tool_desc={}), secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -文档解析组件,用于对文档的内容进行解析。 - -Examples: - -```python -import appbuilder -os.environ["APPBUILDER_TOKEN"] = '...' - -file_path = "./test.pdf" # 待解析的文件路径 -msg = Message(file_path) -parser = appbuilder.DocParser() -parse_result = parser(msg) -``` - -#### base_url *: str* *= '/v1/bce/xmind/parser'* - -#### config *: ParserConfig* *= ParserConfig(convert_file_to_pdf=False, page_filter=None, return_para_node_tree=True, erase_watermark=False)* - -#### make_parse_result(response: Dict) - -将解析结果的内容转化成ParseResult的结构 - -* **参数:** - **response** (*Dict*) -- 解析后的响应字典,包含文件内容、目录等信息 -* **返回:** - 转换后的ParseResult结构,包含段落节点树、页面内容和PDF数据 -* **返回类型:** - Dict - -#### name *: str* *= 'doc_parser'* - -#### run(input_message: [Message](appbuilder.core.md#appbuilder.core.message.Message), return_raw=False) → [Message](appbuilder.core.md#appbuilder.core.message.Message) - -对传入的文件进行解析 - -* **参数:** - * **input_message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**str* *]*) -- 输入为文件的路径 - * **return_raw** (*bool* *,* *optional*) -- 是否返回云端服务的原始结果。默认为False。 -* **返回:** - 文件的解析结果。 -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message)[ParseResult] -* **抛出:** - * **ValueError** -- 如果传入的文件路径不是字符串类型。 - * **AppBuilderServerException** -- 如果文件解析过程中出现异常,将抛出该异常。 - -#### set_config(config: ParserConfig) - -设置解析配置 - -* **参数:** - **config** (*ParserConfig*) -- 解析配置对象 -* **返回:** - None - -#### tool_desc *: Dict[str, Any]* *= {'description': 'parse document content'}* diff --git a/docs/API-Reference/Python/appbuilder.core.components.doc_splitter.md b/docs/API-Reference/Python/appbuilder.core.components.doc_splitter.md deleted file mode 100644 index 90037c55b..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.doc_splitter.md +++ /dev/null @@ -1,174 +0,0 @@ -# appbuilder.core.components.doc_splitter package - -## Submodules - -## appbuilder.core.components.doc_splitter.doc_splitter module - -对文档进行段落切分 - -### *class* appbuilder.core.components.doc_splitter.doc_splitter.ChunkSplitter(max_segment_length=800, overlap=200, separators=['。', '!', '?', '.', '!', '?', '……', '|\\n'], join_symbol='', \*\*kwargs) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -文档按照块大小切分段落 - -Examples: - -原始文档: -: 贷款资金不得用于从事股本权益性投资,不得用于购买股票、有价证券、期货、理财产品等金融产品。 - 不得用于从事房地产经营,不得用于借贷牟取非法收入。不得用于个人或其控制的企业生产经营。 - 不得套取现金。不得用于其他违反国家法律、政策规定的领域,不得用于监管机构禁止银行贷款进入的领域。 - -切分结果: -: ["贷款资金不得用于从事股本权益性投资,不得用于购买股票、有价证券、期货、理财产品等金融产品。不得用于从事房地产经营, - 不得用于借贷牟取非法收入。不得用于个", - "不得用于个人或其控制的企业生产经营。不得套取现金。不得用于其他违反国家法律、政策规定的领域, - 不得用于监管机构禁止银行贷款进入的领域。"] - -#### meta *: [ComponentArguments](appbuilder.core.md#appbuilder.core.component.ComponentArguments)* *= ComponentArguments(name='', tool_desc={'description': 'split data to chunks with max size in doc'})* - -#### name *: str* *= 'doc_to_chunk'* - -#### run(message: [Message](appbuilder.core.md#appbuilder.core.message.Message)) - -对输入的解析文档结果,按照最大段落块大小、结尾分隔符等,处理为多个段落结果 - -* **参数:** - **(****obj** (*message*) -- Message): 上游docparser的文档解析结果 -* **返回:** - Message: 文档分隔后的段落结果 -* **返回类型:** - obj -* **抛出:** - **ValueError** -- 如果 message.content 的类型不是 ParseResult,则抛出 ValueError 异常 - -Examples: - -```python -import os -from appbuilder.core.components.doc_parser.doc_parser import DocParser -from appbuilder.core.components.doc_splitter.doc_splitter import DocSplitter, ChunkSplitter -from appbuilder.core.message import Message - -# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 -os.environ["APPBUILDER_TOKEN"] = "..." - -# 先解析 -msg = Message("./test.pdf") -parser = DocParser() -parse_result = parser(msg, return_raw=True) - -# 基于parser的结果切分段落 -splitter = ChunkSplitter() -res_paras = splitter(parse_result) - -# 打印结果 -print(res_paras.content) -``` - -### *class* appbuilder.core.components.doc_splitter.doc_splitter.DocSplitter(splitter_type, max_segment_length=800, overlap=200, separators=['。', '!', '?', '.', '!', '?', '……', '|\\n'], join_symbol='', \*\*kwargs) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -文档段落切分组件 - -#### name - -组件名称。 - -* **Type:** - str - -#### meta - -组件元数据。 - -* **Type:** - [ComponentArguments](appbuilder.core.md#appbuilder.core.component.ComponentArguments) - -#### meta *: [ComponentArguments](appbuilder.core.md#appbuilder.core.component.ComponentArguments)* *= ComponentArguments(name='', tool_desc={'description': 'split data to segments in doc'})* - -#### name *: str* *= 'doc_to_parapraphs'* - -#### run(message: [Message](appbuilder.core.md#appbuilder.core.message.Message)) - -运行函数,根据splitter_type将文档分割成多个部分 - -* **参数:** - **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 包含文档内容的消息对象 -* **返回:** - 分割后的文档列表 -* **返回类型:** - list -* **抛出:** - * **ValueError** -- 如果message.content不是ParseResult类型,抛出异常 - * **ValueError** -- 如果splitter_type为空,抛出异常 - * **ValueError** -- 如果ParseResult不包含原始值,抛出异常 - * **ValueError** -- 如果splitter_type不是split_by_chunk或split_by_title,抛出异常 - -### *class* appbuilder.core.components.doc_splitter.doc_splitter.TitleSplitter(meta: [ComponentArguments](appbuilder.core.md#appbuilder.core.component.ComponentArguments) | None = ComponentArguments(name='', tool_desc={}), secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -文档按照标题层级切分段落 - -### 示例 - -原始文档: -: 一、简介 - 叠贷业务是指借款人家庭为满足购房、购车、装修、教育、医疗、旅游、日常消费等符合国家法律法规规定的消费用途。 - 二、申请条件 - (一)基本条件 - 1、年满18周岁的自然人,具有完全民事行为能力,能提供有效身份证明或居留证明; - 2、有稳定职业和收入,有偿还贷款本息的能力; - (二)抵押房产所有人的要求 - 1、抵押房产的所有人应为借款人本人 - 2、抵押房产如有共同所有人,借款人必须为之一,且其他共同所有人必须同意以该房产办理最高额抵押登记,并提供同意抵押的合法有效的书面文件。 - -切分结果: -: ["一、简介 叠贷业务是指借款人家庭为满足购房、购车、装修、教育、医疗、旅游、日常消费等符合国家法律法规规定的消费用途。", - "二、申请条件 (一)基本条件 1、年满18周岁的自然人,具有完全民事行为能力,能提供有效身份证明或居留证明; 2、有稳定职业和收入, - 有偿还贷款本息的能力;", - "二、申请条件 (二)抵押房产所有人的要求 1、抵押房产的所有人应为借款人本人。 2、抵押房产如有共同所有人,借款人必须为之一, - 且其他共同所有人必须同意以该房产办理最高额抵押登记,并提供同意抵押的合法有效的书面文件。"】 - -#### name *: str* *= 'doc_to_title_level'* - -#### run(input_message: [Message](appbuilder.core.md#appbuilder.core.message.Message)) → [Message](appbuilder.core.md#appbuilder.core.message.Message) - -对输入的解析文档结果,按照各标题层级,处理为多个段落结果 - -* **参数:** - **(****obj** (*input_message*) -- Message): 上游docparser的文档解析结果 -* **返回:** - Message: 文档分隔后的段落结果 -* **返回类型:** - obj -* **抛出:** - **ValueError** -- 如果message.content的类型不是ParseResult,则抛出异常 - -Examples: - -```python -import os -from appbuilder.core.components.doc_parser.doc_parser import DocParser -from appbuilder.core.components.doc_splitter.doc_splitter import DocSplitter, TitleSplitter -from appbuilder.core.message import Message - -# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 -os.environ["APPBUILDER_TOKEN"] = "..." - -# 先解析 -msg = Message("./title_splitter.docx") -parser = DocParser() -parse_result = parser(msg, return_raw=True) - -# 基于parser的结果切分段落 -splitter = TitleSplitter() -res_paras = splitter(parse_result) - -# 打印结果 -print(res_paras.content) -``` - -#### tool_desc *: Dict[str, Any]* *= {'description': 'split document content by titles'}* diff --git a/docs/API-Reference/Python/appbuilder.core.components.document_understanding.md b/docs/API-Reference/Python/appbuilder.core.components.document_understanding.md deleted file mode 100644 index b8a8c9f8f..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.document_understanding.md +++ /dev/null @@ -1,59 +0,0 @@ -# appbuilder.core.components.document_understanding package - -## Submodules - -## appbuilder.core.components.document_understanding.component module - -Copyright (c) 2023 Baidu, Inc. 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](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. - -### *class* appbuilder.core.components.document_understanding.component.DocumentUnderstanding(secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False, instruction: [Message](appbuilder.core.md#appbuilder.core.message.Message) | None = None, addition_instruction: [Message](appbuilder.core.md#appbuilder.core.message.Message) | None = None, file_path: str | None = None, app_id: str | None = None) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -#### get_addition_instruction(addition_instruction: str) - -拼接addition_instruction - -#### get_conversation_id(app_id: str) - -#### get_file_id(conversation_id: str, app_id: str, file_path: str) - -#### manifests *= [{'description': '该工具支持对图片以及文档内容进行理解,并基于图片以及文档内容对用户的提问进行回答,包括但不限于文档内容问答、总结摘要、内容分析。', 'name': 'document_understanding', 'parameters': {'properties': {'addition_instruction': {'description': '用户增强指令', 'type': 'string'}, 'app_id': {'description': '系统应用ID', 'type': 'string'}, 'file_path': {'description': '用户上传的文档的文件路径', 'type': 'string'}, 'instruction': {'description': '用户指令', 'type': 'string'}, 'query': {'description': '用户输入的query', 'type': 'string'}}, 'required': ['query', 'file_path', 'instruction', 'addition_instruction', 'app_id'], 'type': 'object'}}]* - -#### meta - -`DocumentUnderstandingArgs` 的别名 - -#### name *= 'document_understanding'* - -#### run(message: [Message](appbuilder.core.md#appbuilder.core.message.Message), file_path, instruction='', addition_instruction='', app_id='', stream=False, timeout=None) - -run方法,用于执行长文档理解任务 -:param message: 用户输入query -:param file_path: 用户输入的文件路径 -:param instruction: 用户输入的人设指令 -:param addition_instruction: 用户输入的增强版指令(如有) -:param app_id: 用户输入的app_id - -* **返回:** - 模型运行后的输出消息。 -* **返回类型:** - result ([Message](appbuilder.core.md#appbuilder.core.message.Message)) - -#### tool_eval(message: [Message](appbuilder.core.md#appbuilder.core.message.Message), file_path: str, stream: bool = False, \*\*kwargs) - -用于function call - -#### version *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.embeddings.md b/docs/API-Reference/Python/appbuilder.core.components.embeddings.md deleted file mode 100644 index cd5cb2113..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.embeddings.md +++ /dev/null @@ -1,66 +0,0 @@ -# appbuilder.core.components.embeddings package - -## Submodules - -## appbuilder.core.components.embeddings.component module - -ernie bot embedding - -### *class* appbuilder.core.components.embeddings.component.Embedding(model='Embedding-V1') - -基类:`EmbeddingBaseComponent` - -Embedding-V1是基于百度文心大模型技术的文本表示模型,将文本转化为用数值表示的向量形式,用于文本检索、信息推荐、知识挖掘等场景。 - -#### model - -str = "Embedding-V1" - -### 示例 - -```python -import appbuilder -from appbuilder import Message - -os.environ["APPBUILDER_TOKEN"] = '...' - -embedding = appbuilder.Embedding() - -embedding_single = embedding(Message("hello world!")) - -embedding_batch = embedding.batch(Message(["hello", "world"])) -``` - -#### accepted_models *= ['Embedding-V1']* - -#### base_urls *= {'Embedding-V1': '/v1/bce/wenxinworkshop/ai_custom/v1/embeddings/embedding-v1'}* - -#### batch(texts: [Message](appbuilder.core.md#appbuilder.core.message.Message)[List[str]] | List[str]) → [Message](appbuilder.core.md#appbuilder.core.message.Message)[List[List[float]]] - -批量处理文本数据。 - -* **参数:** - **texts** (*Union* *[*[*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**List* *[**str* *]* *]* *,* *List* *[**str* *]* *]*) -- 待处理的文本数据,可以是 Message 类型,包含多个文本列表,也可以是普通列表类型,包含多个文本。 -* **返回:** - 处理后的结果,为 Message 类型,包含一个二维浮点数列表,每个子列表对应输入文本列表中一个文本的处理结果。 -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message)[List[List[float]]] - -#### meta - -`EmbeddingArgs` 的别名 - -#### name *: str* *= 'embedding'* - -#### run(text: [Message](appbuilder.core.md#appbuilder.core.message.Message)[str] | str) → [Message](appbuilder.core.md#appbuilder.core.message.Message)[List[float]] - -处理给定的文本或消息对象,并返回包含处理结果的消息对象。 - -* **参数:** - **text** (*Union* *[*[*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**str* *]* *,* *str* *]*) -- 待处理的文本或消息对象。 -* **返回:** - 处理后的结果,封装在消息对象中。结果是一个浮点数列表。 -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message)[List[float]] - -#### version *: str* *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.excel2figure.md b/docs/API-Reference/Python/appbuilder.core.components.excel2figure.md deleted file mode 100644 index a369ba294..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.excel2figure.md +++ /dev/null @@ -1,76 +0,0 @@ -# appbuilder.core.components.excel2figure package - -## Submodules - -## appbuilder.core.components.excel2figure.component module - -excel2figure component - -### *class* appbuilder.core.components.excel2figure.component.Excel2Figure(model: str, secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -excel2figure 组件类 - -* **参数:** - * **model** -- str - * **secret_key** -- Optional[str] - * **gateway** -- str - * **lazy_certification** -- bool - -#### excluded_models *: List[str]* *= ['Yi-34B-Chat', 'ChatLaw']* - -#### manifests *= [{'description': 'Excel转图表工具,当用户需要根据Excel图表的数据进行数据分析并绘制图表(柱状图、折线图、雷达图等),使用该工具。', 'name': 'excel_to_figure', 'parameters': {'properties': {'query': {'description': '需要根据Excel图表的数据进行数据分析并绘制图表的请求描述。', 'type': 'string'}}, 'required': ['query'], 'type': 'object'}}]* - -#### meta - -`Excel2FigureArgs` 的别名 - -#### model_info *: ModelInfo* *= None* - -#### model_type *: str* *= 'chat'* - -#### run(message: [Message](appbuilder.core.md#appbuilder.core.message.Message)) → [Message](appbuilder.core.md#appbuilder.core.message.Message) - -执行 excel2figure。 - -* **参数:** - **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 消息对象,其 content 属性是一个字典,包含以下键值对: - - query (str): 用户的问题。 - - excel_file_url (str): 用户的 Excel 文件地址。 -* **返回:** - 处理后的消息对象。 -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message) -* **抛出:** - **ValueError** -- 当 message.content 解析失败时抛出此异常。 - -#### set_secret_key_and_gateway(\*\*kwargs) - -设置密钥和网关地址。 - -* **参数:** - * **secret_key** (*Optional* *[**str* *]* *,* *optional*) -- 密钥,默认为None。如果未指定,则使用实例当前的密钥。 - * **gateway** (*str* *,* *optional*) -- 网关地址,默认为空字符串。如果未指定,则使用实例当前的网关地址。 -* **返回:** - None - -#### tool_eval(streaming: bool, origin_query: str, file_urls: dict, \*\*kwargs) - -对指定的Excel文件进行图表生成和评估。 - -* **参数:** - * **streaming** (*bool*) -- 是否以流式传输方式返回结果。如果为True,则通过生成器返回结果;如果为False,则直接返回结果。 - * **origin_query** (*str*) -- 原始查询字符串,用于在缺少其他查询参数时使用。 - * **file_urls** (*dict*) -- 包含Excel文件信息的字典,其中键为文件名,值为文件URL。 - * **\*\*kwargs** -- 其他关键字参数,可以包括查询字符串等。 -* **返回:** - 如果streaming为True,则通过生成器返回结果。每个结果是一个字典,包含以下键: - - event (str): 事件类型,始终为'excel_to_figure'。 - - type (str): 数据类型,始终为'files'。 - - text (list of str): 包含生成的图表信息的列表。 - - 如果streaming为False,则直接返回一个包含上述信息的字典。 -* **抛出:** - * **ValueError** -- 如果file_urls的长度不等于1,则抛出异常。 - * **RuntimeError** -- 如果Excel文件到图表的转换失败或出现异常,则抛出异常。 diff --git a/docs/API-Reference/Python/appbuilder.core.components.extract_table.md b/docs/API-Reference/Python/appbuilder.core.components.extract_table.md deleted file mode 100644 index 965b19487..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.extract_table.md +++ /dev/null @@ -1,61 +0,0 @@ -# appbuilder.core.components.extract_table package - -## Submodules - -## appbuilder.core.components.extract_table.component module - -文档表格转换 - -### *class* appbuilder.core.components.extract_table.component.ExtractTableFromDoc(meta: [ComponentArguments](appbuilder.core.md#appbuilder.core.component.ComponentArguments) | None = ComponentArguments(name='', tool_desc={}), secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -文档表格抽取 - -### 示例 - -```python -import os -import json - -from appbuilder.utils.logger_util import logger -from appbuilder import Message, ExtractTableFromDoc, DocParser - -# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 -os.environ["APPBUILDER_TOKEN"] = "..." - -# 测试文档解析器使用默认配置,xxx为待解析的文档路径。 -msg = Message("xxx") -parser = DocParser() -# ExtractTableFromDoc输入为文档原始解析结果,此处需要带上原始结果,return_raw=True. -doc = parser(msg, return_raw=True).content.raw - -# 抽取文档中的表格 -parser = ExtractTableFromDoc() -result = parser.run(Message(doc)) - -logger.info("Tables: {}".format( - json.dumps(result.content, ensure_ascii=False))) -``` - -#### base_url *= '/rpc/2.0/cloud_hub/v1/ai_engine/copilot_engine/v1/api/doc_search_tools/doc_table_to_markdown_parser'* - -#### meta *: [ComponentArguments](appbuilder.core.md#appbuilder.core.component.ComponentArguments)* *= ComponentArguments(name='', tool_desc={'description': 'Extract table from doc, table format is markdown'})* - -#### name *: str* *= 'extract_table_from_doc'* - -#### run(message: [Message](appbuilder.core.md#appbuilder.core.message.Message), table_max_size: int = 800, doc_node_num_before_table: int = 1) - -将文档原始解析结果,请求云端进行表格抽取,返回表格列表。 - -* **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 文档原始解析结果。 - * **table_max_size** (*int*) -- 单个表格的长度的最大值(包含上文),按字符数即len(table_str)统计,默认为800。如果表格超长,则会被拆 分成多个子表格,拆分的最小粒度为表格的行。若单行就超长,则会强制按table_max_size截断。截断时会优先截断上文,尽量保留表格内容。 - * **doc_node_num_before_table** (*int*) -- 表格前附加的上文DocParser Node的数量,默认为1。范围:1~10。 -* **返回:** - 返回解析后的消息实体对象 - : Message.content (list): 解析出来的文档表格,list(二维)。解析出来的文档表格,如果元素长度为1,则对应原文档中格式化后的 长度不超过\`table_max_size\`的表格;如果元素长度>1,则是对应原文档中一个大表格,该表格被拆分成的多个子表格,以满足设置 大小。输出结果数据结构样例:[[{table1}], [{table2-part1}, {table2-part2}]] -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message) -* **抛出:** - **ValueError** -- 当输入参数不为文档原始解析结果时,或值不合法时,抛出异常。 diff --git a/docs/API-Reference/Python/appbuilder.core.components.gbi.md b/docs/API-Reference/Python/appbuilder.core.components.gbi.md deleted file mode 100644 index 756f3ab34..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.gbi.md +++ /dev/null @@ -1,17 +0,0 @@ -# appbuilder.core.components.gbi package - -## Subpackages - -* [appbuilder.core.components.gbi.nl2sql package](appbuilder.core.components.gbi.nl2sql.md) - * [Submodules](appbuilder.core.components.gbi.nl2sql.md#submodules) - * [appbuilder.core.components.gbi.nl2sql.component module](appbuilder.core.components.gbi.nl2sql.md#module-appbuilder.core.components.gbi.nl2sql.component) - * [`NL2Sql`](appbuilder.core.components.gbi.nl2sql.md#appbuilder.core.components.gbi.nl2sql.component.NL2Sql) - * [`NL2Sql.meta`](appbuilder.core.components.gbi.nl2sql.md#appbuilder.core.components.gbi.nl2sql.component.NL2Sql.meta) - * [`NL2Sql.run()`](appbuilder.core.components.gbi.nl2sql.md#appbuilder.core.components.gbi.nl2sql.component.NL2Sql.run) -* [appbuilder.core.components.gbi.select_table package](appbuilder.core.components.gbi.select_table.md) - * [Submodules](appbuilder.core.components.gbi.select_table.md#submodules) - * [appbuilder.core.components.gbi.select_table.component module](appbuilder.core.components.gbi.select_table.md#module-appbuilder.core.components.gbi.select_table.component) - * [`SelectTable`](appbuilder.core.components.gbi.select_table.md#appbuilder.core.components.gbi.select_table.component.SelectTable) - * [`SelectTable.run()`](appbuilder.core.components.gbi.select_table.md#appbuilder.core.components.gbi.select_table.component.SelectTable.run) - -## Submodules diff --git a/docs/API-Reference/Python/appbuilder.core.components.gbi.nl2sql.md b/docs/API-Reference/Python/appbuilder.core.components.gbi.nl2sql.md deleted file mode 100644 index a2121380e..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.gbi.nl2sql.md +++ /dev/null @@ -1,36 +0,0 @@ -# appbuilder.core.components.gbi.nl2sql package - -## Submodules - -## appbuilder.core.components.gbi.nl2sql.component module - -GBI nl2sql component. - -### *class* appbuilder.core.components.gbi.nl2sql.component.NL2Sql(model_name: str, table_schemas: List[str], knowledge: Dict | None = None, prompt_template: str = '') - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -gib nl2sql - -#### meta - -`NL2SqlArgs` 的别名 - -#### run(message: [Message](appbuilder.core.md#appbuilder.core.message.Message), timeout: float = 60, retry: int = 0) → [Message](appbuilder.core.md#appbuilder.core.message.Message)[NL2SqlResult] - -执行自然语言转SQL操作。 - -* **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- - - 包含用户问题和会话历史的消息对象。 - - message.content 是一个字典,包含以下关键字: - > 1. query: 用户问题 - > 2. session: 会话历史列表,参考 SessionRecord - > 3. column_constraint: 列选约束,参考 ColumnItem 具体定义 - * **timeout** (*float*) -- 超时时间,默认为60秒。 - * **retry** (*int*) -- 重试次数,默认为0次。 -* **返回:** - 转换结果以Message对象形式返回,其中content属性包含NL2SqlResult对象。 -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message)[NL2SqlResult] diff --git a/docs/API-Reference/Python/appbuilder.core.components.gbi.select_table.md b/docs/API-Reference/Python/appbuilder.core.components.gbi.select_table.md deleted file mode 100644 index cdd0ec0c8..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.gbi.select_table.md +++ /dev/null @@ -1,33 +0,0 @@ -# appbuilder.core.components.gbi.select_table package - -## Submodules - -## appbuilder.core.components.gbi.select_table.component module - -GBI nl2sql component. - -### *class* appbuilder.core.components.gbi.select_table.component.SelectTable(model_name: str, table_descriptions: Dict[str, str], prompt_template: str = '') - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -gbi 选表 - -#### run(message: [Message](appbuilder.core.md#appbuilder.core.message.Message), timeout: int = 60, retry: int = 0) → [Message](appbuilder.core.md#appbuilder.core.message.Message)[List[str]] - -执行查询操作,返回识别的表名列表。 - -* **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- - - 包含查询信息的消息对象。 - - message.content 字典包含以下 key: - > 1. query (str): 用户的问题输入。 - > 2. session (list, optional): 对话历史,默认为空列表。 - * **timeout** (*int* *,* *optional*) -- 超时时间,默认为 60 秒。 - * **retry** (*int* *,* *optional*) -- 重试次数,默认为 0。 -* **返回:** - 包含识别出的表名列表的 Message 对象。 -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message)[List[str]] -* **抛出:** - **ValueError** -- 如果输入的 message.content 不符合期望的格式,将抛出 ValueError 异常。 diff --git a/docs/API-Reference/Python/appbuilder.core.components.general_ocr.md b/docs/API-Reference/Python/appbuilder.core.components.general_ocr.md deleted file mode 100644 index 5be1c578b..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.general_ocr.md +++ /dev/null @@ -1,66 +0,0 @@ -# appbuilder.core.components.general_ocr package - -## Submodules - -## appbuilder.core.components.general_ocr.component module - -general ocr component. - -### *class* appbuilder.core.components.general_ocr.component.GeneralOCR(meta: [ComponentArguments](appbuilder.core.md#appbuilder.core.component.ComponentArguments) | None = ComponentArguments(name='', tool_desc={}), secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -提供通用文字识别能力,在通用文字识别的基础上,提供更高精度的识别服务,支持更多语种识别(丹麦语、荷兰语、马来语、 -瑞典语、印尼语、波兰语、罗马尼亚语、土耳其语、希腊语、匈牙利语、泰语、越语、阿拉伯语、印地语及部分中国少数民族语言), -并将字库从1w+扩展到2w+,能识别所有常用字和大部分生僻字。 - -Examples: - -```python -import appbuilder - -# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 -os.environ["APPBUILDER_TOKEN"] = '...' - -general_ocr = appbuilder.GeneralOCR() -with open("./general_ocr_test.png", "rb") as f: - out = general_ocr.run(appbuilder.Message(content={"raw_image": f.read()})) -print(out.content) -``` - -#### manifests *= [{'description': '提供更高精度的通用文字识别能力,能够识别图片中的文字,不支持html后缀文件的输入', 'name': 'general_ocr', 'parameters': {'anyOf': [{'required': ['img_url']}, {'required': ['img_name']}], 'properties': {'img_name': {'description': '待识别图片的文件名,用于生成图片url', 'type': 'string'}, 'img_url': {'description': '待识别图片的url,根据该url能够获取图片', 'type': 'string'}}, 'type': 'object'}}]* - -#### name *= 'general_ocr'* - -#### run(message: [Message](appbuilder.core.md#appbuilder.core.message.Message), timeout: float = None, retry: int = 0) → [Message](appbuilder.core.md#appbuilder.core.message.Message) - -执行图片中的文字识别。 - -* **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入图片或图片url下载地址用于执行识别操作。举例: Message(content={"raw_image": b"..."}) 或 Message(content={"url": "[https://image/download/url](https://image/download/url)"})。 - * **timeout** (*float* *,* *可选*) -- HTTP超时时间。 - * **retry** (*int* *,* *可选*) -- HTTP重试次数。 -* **返回:** - 模型识别结果。举例: Message(content={"words_result":[{"words":"100"}, {"words":"G8"}]})。 -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message) - -#### tool_eval(name: str, streaming: bool, \*\*kwargs) - -根据给定的参数执行OCR识别功能。 - -* **参数:** - * **name** (*str*) -- 函数名称,此处未使用,但为保持一致性保留。 - * **streaming** (*bool*) -- 是否以流式方式返回结果。如果为True,则逐个返回结果,否则返回全部结果。 - * **kwargs** -- 关键字参数,支持以下参数: - traceid (str): 请求的唯一标识符,用于追踪请求和响应。 - img_url (str): 待识别图片的URL。 - file_urls (dict): 包含文件名和对应URL的字典。如果提供了img_url,则忽略此参数。 - img_name (str): 待识别图片的文件名,与file_urls配合使用。 -* **返回:** - 如果streaming为False,则返回包含识别结果的JSON字符串。 - 如果streaming为True,则逐个返回包含识别结果的字典。 -* **抛出:** - **InvalidRequestArgumentError** -- 如果请求格式错误(例如未设置文件名或指定文件名对应的URL不存在),则抛出此异常。 - -#### version *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.handwrite_ocr.md b/docs/API-Reference/Python/appbuilder.core.components.handwrite_ocr.md deleted file mode 100644 index 535f0f1db..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.handwrite_ocr.md +++ /dev/null @@ -1,66 +0,0 @@ -# appbuilder.core.components.handwrite_ocr package - -## Submodules - -## appbuilder.core.components.handwrite_ocr.component module - -手写文字识别组件 - -### *class* appbuilder.core.components.handwrite_ocr.component.HandwriteOCR(meta: [ComponentArguments](appbuilder.core.md#appbuilder.core.component.ComponentArguments) | None = ComponentArguments(name='', tool_desc={}), secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -手写文字识别组件 - -Examples: - -```python -import os -import appbuilder -os.environ["GATEWAY_URL"] = "..." -os.environ["APPBUILDER_TOKEN"] = "..." -# 从BOS存储读取样例文件 -image_url="https://bj.bcebos.com/v1/appbuilder/test_handwrite_ocr.jpg?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-23T11%3A58%3A09Z%2F-1%2Fhost%2F677f93445fb65157bee11cd492ce213d5c56e7a41827e45ce7e32b083d195c8b" -# 输入参数为一张图片 -inp = appbuilder.Message(content={"url": image_url}) -# 进行植物识别 -handwrite_ocr = HandwriteOCR() -out = handwrite_ocr.run(inp) -# 打印识别结果 -print(out.content) -``` - -#### manifests *= [{'description': '需要对图片中手写体文字进行识别时,使用该工具,不支持PDF文件,如果用户没有提供图片文件,应引导用户提供图片,而不是尝试使用该工具', 'name': 'handwriting_ocr', 'parameters': {'properties': {'file_names': {'description': '待识别文件的文件名', 'items': {'type': 'string'}, 'type': 'array'}}, 'required': ['file_names'], 'type': 'object'}}]* - -#### name *= 'handwriting_ocr'* - -#### run(message: [Message](appbuilder.core.md#appbuilder.core.message.Message), timeout: float = None, retry: int = 0) → [Message](appbuilder.core.md#appbuilder.core.message.Message) - -输入图片并识别其中的文字 - -* **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入图片或图片url下载地址用于执行识别操作.例如: Message(content={"raw_image": b"..."}) 或 Message(content={"url": "[https://image/download/url](https://image/download/url)"}). - * **timeout** (*float* *,* *optional*) -- HTTP超时时间. 默认为None. - * **retry** (*int* *,* *optional*) -- HTTP重试次数. 默认为0. -* **返回:** - 手写体模型识别结果. -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message) - -#### tool_eval(name: str, streaming: bool, \*\*kwargs) - -对指定文件或URL进行手写识别。 - -* **参数:** - * **name** (*str*) -- 任务名称。 - * **streaming** (*bool*) -- 是否以流式形式返回结果。 - * **kwargs** -- 其他参数,包括: - traceid (str, optional): 请求的traceid,用于标识请求的唯一性。默认为None。 - file_names (List[str], optional): 待识别的文件名列表。默认为None,此时会从kwargs中获取'files'参数。 - file_urls (Dict[str, str], optional): 文件名与URL的映射字典。默认为空字典。 -* **返回:** - 如果streaming为True,则以生成器形式返回识别结果,否则直接返回结果字符串。 -* **抛出:** - **InvalidRequestArgumentError** -- 如果请求格式错误,例如指定的文件名对应的URL不存在。 - -#### version *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.image_understand.md b/docs/API-Reference/Python/appbuilder.core.components.image_understand.md deleted file mode 100644 index e5559114b..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.image_understand.md +++ /dev/null @@ -1,65 +0,0 @@ -# appbuilder.core.components.image_understand package - -## Submodules - -## appbuilder.core.components.image_understand.component module - -图像内容理解 - -### *class* appbuilder.core.components.image_understand.component.ImageUnderstand(meta: [ComponentArguments](appbuilder.core.md#appbuilder.core.component.ComponentArguments) | None = ComponentArguments(name='', tool_desc={}), secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -图像内容理解组件,即对于输入的一张图片(可正常解码,且长宽比适宜)与问题,输出对图片的描述 - -Examples: - -```python -import os -import appbuilder -os.environ["GATEWAY_URL"] = "..." -os.environ["APPBUILDER_TOKEN"] = "..." -# 从BOS存储读取样例文件 -image_url = "https://bj.bcebos.com/v1/appbuilder/test_image_understand.jpeg?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-24T09%3A41%3A01Z%2F-1%2Fhost%2Fe8665506e30e0edaec4f1cc84a2507c4cb3fdb9b769de3a5bfe25c372b7e56e6" -# 输入参数为一张图片 -inp = Message(content={"url": image_url, "question": "图片里内容是什么?"}) -# 进行图像内容理解 -image_understand = ImageUnderstand() -out = image_understand.run(inp) -# 打印识别结果 -print(out.content) -``` - -#### manifests *= [{'description': '可对输入图片进行理解,可输出图片描述、OCR 及图像识别结果', 'name': 'image_understanding', 'parameters': {'anyOf': [{'required': ['img_name']}, {'required': ['img_url']}], 'properties': {'img_name': {'description': '待识别图片的文件名', 'type': 'string'}, 'img_url': {'description': '待识别图片的url', 'type': 'string'}}, 'type': 'object'}}]* - -#### name *= 'image_understanding'* - -#### run(message: [Message](appbuilder.core.md#appbuilder.core.message.Message), timeout: float = None, retry: int = 0) → [Message](appbuilder.core.md#appbuilder.core.message.Message) - -执行图像内容理解 - -* **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入图片或图片url下载地址用于执行识别操作. 举例: Message(content={"raw_image": b"...", "question": "图片主要内容是什么?"}) - 或 Message(content={"url": "[https://image/download/url](https://image/download/url)", "question": "图片主要内容是什么?"}). - * **timeout** (*float* *,* *optional*) -- HTTP超时时间. 默认为 None. - * **retry** (*int* *,* *optional*) -- HTTP重试次数. 默认为 0. -* **返回:** - 模型识别结果. -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message) - -#### tool_eval(name: str, streaming: bool, origin_query: str, \*\*kwargs) → Generator[str, None, None] | str - -用于工具的执行,调用底层接口进行图像内容理解 - -* **参数:** - * **name** (*str*) -- 工具名 - * **streaming** (*bool*) -- 是否流式返回 - * **origin_query** (*str*) -- 用户原始query - * **\*\*kwargs** -- 工具调用的额外关键字参数 -* **返回:** - 图片内容理解结果 -* **返回类型:** - Union[Generator[str, None, None], str] - -#### version *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.landmark_recognize.md b/docs/API-Reference/Python/appbuilder.core.components.landmark_recognize.md deleted file mode 100644 index 12395481d..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.landmark_recognize.md +++ /dev/null @@ -1,41 +0,0 @@ -# appbuilder.core.components.landmark_recognize package - -## Submodules - -## appbuilder.core.components.landmark_recognize.component module - -landmark recognize component. - -### *class* appbuilder.core.components.landmark_recognize.component.LandmarkRecognition(meta: [ComponentArguments](appbuilder.core.md#appbuilder.core.component.ComponentArguments) | None = ComponentArguments(name='', tool_desc={}), secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -识别地标组件,即对于输入的一张图片(可正常解码,且长宽比适宜),输出图片中的地标识别结果 - -Examples: - -```python -import appbuilder -os.environ["APPBUILDER_TOKEN"] = '...' -landmark_recognize = appbuilder.LandmarkRecognition() -with open("xxxx.jpg", "rb") as f: - inp = appbuilder.Message(content={"raw_image": f.read()}) - out = landmark_recognize.run(inp) - # 打印识别结果 - print(out.content) # eg: {"landmark": "狮身人面相"} -``` - -#### run(message: [Message](appbuilder.core.md#appbuilder.core.message.Message), timeout: float = None, retry: int = 0) → [Message](appbuilder.core.md#appbuilder.core.message.Message) - -执行地标识别任务 - -* **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入消息对象,包含待识别的图片或图片URL。 - 例如:Message(content={"raw_image": b"..."}) 或 Message(content={"url": "[https://image/download/url](https://image/download/url)"})。 - * **timeout** (*float* *,* *optional*) -- HTTP请求的超时时间。默认为None。 - * **retry** (*int* *,* *optional*) -- HTTP请求的重试次数。默认为0。 -* **返回:** - 地标识别结果的消息对象。 - : 例如:Message(content={"landmark": b"狮身人面像"}) -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message) diff --git a/docs/API-Reference/Python/appbuilder.core.components.llms.dialog_summary.md b/docs/API-Reference/Python/appbuilder.core.components.llms.dialog_summary.md deleted file mode 100644 index 3743ecc9f..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.llms.dialog_summary.md +++ /dev/null @@ -1,52 +0,0 @@ -# appbuilder.core.components.llms.dialog_summary package - -## Submodules - -## appbuilder.core.components.llms.dialog_summary.component module - -### *class* appbuilder.core.components.llms.dialog_summary.component.DialogSummary(model=None, secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:`CompletionBaseComponent` - -会话小结大模型组件, 基于生成式大模型对一段用户与坐席的对话生成总结,结果按{"诉求": "", "回应": "", "解决情况": ""}格式输出。 - -Examples: - -```python -import app -import os - -# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 -os.environ["APPBUILDER_TOKEN"] = '...' - -dialog_summary = appbuilder.DialogSummary("Qianfan-Agent-Speed-8k") -text = "用户:喂我想查一下我的话费\n坐席:好的女士您话费余的话还有87.49元钱\n用户:好的知道了谢谢\n坐席:嗯不客气祝您生活愉快再见" -answer = dialog_summary(appbuilder.Message(text)) -print(answer) -``` - -#### meta - -`DialogSummaryArgs` 的别名 - -#### name *: str* *= 'dialog_summary'* - -#### run(message, stream=False, temperature=1e-10, top_p=0) - -使用给定的输入运行模型并返回结果。 - -* **参数:** - * **(****obj** (*message*) -- Message): 输入消息,用于模型的主要输入内容。这是一个必需的参数。 - * **stream** (*bool* *,* *optional*) -- 指定是否以流式形式返回响应。默认为 False。 - * **temperature** (*float* *,* *optional*) -- 模型配置的温度参数,用于调整模型的生成概率。 - 取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。 - 默认值为 1e-10。 - * **top_p** (*float* *,* *optional*) -- 影响输出文本的多样性,取值越大,生成文本的多样性越强。 - 取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。 - 默认值为 0。 -* **返回:** - Message: 模型运行后的输出消息。 -* **返回类型:** - obj - -#### version *: str* *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.llms.hallucination_detection.md b/docs/API-Reference/Python/appbuilder.core.components.llms.hallucination_detection.md deleted file mode 100644 index 8a63b5a5d..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.llms.hallucination_detection.md +++ /dev/null @@ -1,107 +0,0 @@ -# appbuilder.core.components.llms.hallucination_detection package - -## Submodules - -## appbuilder.core.components.llms.hallucination_detection.component module - -### *class* appbuilder.core.components.llms.hallucination_detection.component.HallucinationDetection(model=None, secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:`CompletionBaseComponent` - -幻觉检测。输入,判断answer中是否存在幻觉。 - *注:该组件推荐使用Qianfan-Agent-Speed-8k模型。* - -Examples: - -```python -import os -import appbuilder -# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 -os.environ['APPBUILDER_TOKEN'] = '...' - -hallucination_detection = appbuilder.HallucinationDetection() - -query = '' -context = '''澳门美食: 澳门新麻蒲韩国烤肉店 -在澳门一年四季之中除了火锅,烤肉也相当受欢迎。提到韩烧,有一间令我印象最深刻,就是号称韩国第一的烤肉店-新麻蒲韩国烤肉店,光是韩国的分店便多达四百多间,海外分店更是遍布世界各地,2016年便落户澳门筷子基区,在原本已经食肆林立的地方一起百花齐放!店内的装修跟韩国分店还完度几乎没差,让食客彷如置身于韩国的感觉,还要大赞其抽风系统不俗,离开时身上都不会沾上烤肉味耶! -时间:周一至周日 下午5:00 - 上午3:00 -电话:+853 2823 4012 -地址:澳门筷子基船澳街海擎天第三座地下O号铺96号 -必食推介: -护心肉二人套餐 -来新麻蒲必试的有两样东西,现在差不多每间烤肉店都有炉边烤蛋,但大家知道吗?原来新麻蒲就是炉边烤蛋的开创者,既然是始祖,这已经是个非吃不可的理由!还有一款必试的就是护心肉,即是猪的横隔膜与肝中间的部分,每头猪也只有200克这种肉,非常珍贵,其味道吃起来有种独特的肉香味,跟牛护心肉一样精彩! -秘制猪皮 -很多怕胖的女生看到猪皮就怕怕,但其实猪皮含有大量胶原蛋白,营养价值很高呢!这里红通通的猪皮还经过韩国秘制酱汁处理过,会有一点点辣味。烤猪皮的时候也需特别注意火侯,这样吃起来才会有外脆内Q的口感!''' -answer = '澳门新麻蒲烤肉店并不是每天开门。' - -inputs = {'query': query, 'context': context, 'answer': answer} -msg = appbuilder.Message(inputs) -result = hallucination_detection.run(msg) - -print(result) -``` - -#### completion(version, base_url, request, timeout: float | None = None, retry: int = 0) - -Send a byte array of an audio file to obtain the result of speech recognition. - -* **参数:** - * **version** (*str*) -- API version. - * **base_url** (*str*) -- Base URL of the API. - * **request** (*Request*) -- Request object containing audio file and other parameters. - * **timeout** (*float* *,* *optional*) -- Timeout for the request. Defaults to None. - * **retry** (*int* *,* *optional*) -- Number of retries for the request. Defaults to 0. -* **返回:** - Processed response object. -* **返回类型:** - Response - -#### manifests *= [{'description': '输入用户查询query、检索结果context以及根据检索结果context生成的用户查询query的回答answer,判断answer中是否存在幻觉。', 'name': 'hallucination_detection', 'parameters': {'properties': {'answer': {'description': '根据检索结果context生成的用户查询query的回答answer。', 'text': 'string'}, 'context': {'description': '检索结果。', 'text': 'string'}, 'query': {'description': '用户查询。', 'text': 'string'}}, 'required': ['query', 'context', 'answer'], 'type': 'object'}}]* - -#### meta - -`HallucinationDetectionArgs` 的别名 - -#### name *: str* *= 'hallucination_detection'* - -#### run(message, stream=False, temperature=1e-10, top_p=0.0) - -使用给定的输入运行模型并返回结果。 - -* **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入消息,包含 query、context 和 answer。是必需的参数。 - * **stream** (*bool* *,* *可选*) -- 是否以流式形式返回响应。默认为 False。 - * **temperature** (*float* *,* *可选*) -- 模型配置的温度参数,用于调整模型的生成概率。 - 取值范围为 0.0 到 1.0,较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 - * **top_p** (*float* *,* *可选*) -- 影响输出文本的多样性,取值越大,生成文本的多样性越强。 - 取值范围为 0.0 到 1.0,较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 -* **返回:** - 模型运行后的输出消息。 -* **返回类型:** - result ([Message](appbuilder.core.md#appbuilder.core.message.Message)) -* **抛出:** - * **AssertionError** -- 如果输入的 message 中缺少 query、context 或 answer。 - * **AppBuilderServerException** -- 如果请求执行失败,将抛出异常,包含服务错误码和错误信息。 - -#### tool_eval(name: str, stream: bool = False, \*\*kwargs) - -调用函数进行工具评估。 - -* **参数:** - * **name** (*str*) -- 函数名,当前方法未使用此参数,预留接口。 - * **stream** (*bool* *,* *optional*) -- 是否以流的方式返回结果,默认为False。如果为True,则逐个返回结果;如果为False,则一次性返回所有结果。 - * **\*\*kwargs** -- - - 关键字参数,包含评估所需的输入参数。 - - query (str): 查询语句。 - - context (str): 上下文信息。 - - answer (str): 参考答案。 - - model_configs (dict, optional): 模型配置信息,默认为空字典。包含以下字段: - : - temperature (float, optional): 温度参数,用于控制生成文本的随机性,默认为1e-10。 - - top_p (float, optional): 截断概率,用于控制生成文本的质量,默认为0.0。 -* **返回:** - 如果stream为False,返回包含所有评估结果的列表;如果stream为True,逐个返回评估结果。 -* **抛出:** - **ValueError** -- 如果缺少query、context或answer参数,将引发此异常。 - -#### version *: str* *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.llms.is_complex_query.md b/docs/API-Reference/Python/appbuilder.core.components.llms.is_complex_query.md deleted file mode 100644 index a9c32c1a4..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.llms.is_complex_query.md +++ /dev/null @@ -1,53 +0,0 @@ -# appbuilder.core.components.llms.is_complex_query package - -## Submodules - -## appbuilder.core.components.llms.is_complex_query.component module - -is complex query - -### *class* appbuilder.core.components.llms.is_complex_query.component.IsComplexQuery(model=None, secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:`CompletionBaseComponent` - -基于输入的问题, 对问题进行初步的分类,方便下游使用不同类型的流程来处理当前的简单问题/复杂问题。广泛用于知识问答场景。 - -Examples: - -```python -import os -import appbuilder - -# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 -os.environ["APPBUILDER_TOKEN"] = "..." - -is_complex_query = appbuilder.IsComplexQuery(model="Qianfan-Agent-Speed-8k") - -msg = "吸塑包装盒在工业化生产和物流运输中分别有什么重要性?" -msg = appbuilder.Message(msg) -answer = is_complex_query(msg) - -print("Answer: \n{}".format(answer.content)) -``` - -#### meta - -`IsComplexQueryMeta` 的别名 - -#### name *: str* *= 'is_complex_query'* - -#### run(message, stream=False, temperature=1e-10, top_p=0) - -给定输入(message)到模型运行,同时指定运行参数,并返回结果。 - -* **参数:** - * **(****obj** (*message*) -- Message): 输入消息,用于模型的主要输入内容。这是一个必需的参数。 - * **stream** (*bool* *,* *optional*) -- 指定是否以流式形式返回响应。默认为 False。 - * **temperature** (*float* *,* *optional*) -- 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 - * **top_p** (*float* *,* *optional*) -- 影响输出文本的多样性,取值越大,生成文本的多样性越强。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 -* **返回:** - Message: 模型运行后的输出消息。 -* **返回类型:** - obj - -#### version *: str* *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.llms.md b/docs/API-Reference/Python/appbuilder.core.components.llms.md deleted file mode 100644 index 2ce8ca3e5..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.llms.md +++ /dev/null @@ -1,143 +0,0 @@ -# appbuilder.core.components.llms package - -## Subpackages - -* [appbuilder.core.components.llms.dialog_summary package](appbuilder.core.components.llms.dialog_summary.md) - * [Submodules](appbuilder.core.components.llms.dialog_summary.md#submodules) - * [appbuilder.core.components.llms.dialog_summary.component module](appbuilder.core.components.llms.dialog_summary.md#module-appbuilder.core.components.llms.dialog_summary.component) - * [`DialogSummary`](appbuilder.core.components.llms.dialog_summary.md#appbuilder.core.components.llms.dialog_summary.component.DialogSummary) - * [`DialogSummary.meta`](appbuilder.core.components.llms.dialog_summary.md#appbuilder.core.components.llms.dialog_summary.component.DialogSummary.meta) - * [`DialogSummary.name`](appbuilder.core.components.llms.dialog_summary.md#appbuilder.core.components.llms.dialog_summary.component.DialogSummary.name) - * [`DialogSummary.run()`](appbuilder.core.components.llms.dialog_summary.md#appbuilder.core.components.llms.dialog_summary.component.DialogSummary.run) - * [`DialogSummary.version`](appbuilder.core.components.llms.dialog_summary.md#appbuilder.core.components.llms.dialog_summary.component.DialogSummary.version) -* [appbuilder.core.components.llms.hallucination_detection package](appbuilder.core.components.llms.hallucination_detection.md) - * [Submodules](appbuilder.core.components.llms.hallucination_detection.md#submodules) - * [appbuilder.core.components.llms.hallucination_detection.component module](appbuilder.core.components.llms.hallucination_detection.md#module-appbuilder.core.components.llms.hallucination_detection.component) - * [`HallucinationDetection`](appbuilder.core.components.llms.hallucination_detection.md#appbuilder.core.components.llms.hallucination_detection.component.HallucinationDetection) - * [`HallucinationDetection.completion()`](appbuilder.core.components.llms.hallucination_detection.md#appbuilder.core.components.llms.hallucination_detection.component.HallucinationDetection.completion) - * [`HallucinationDetection.manifests`](appbuilder.core.components.llms.hallucination_detection.md#appbuilder.core.components.llms.hallucination_detection.component.HallucinationDetection.manifests) - * [`HallucinationDetection.meta`](appbuilder.core.components.llms.hallucination_detection.md#appbuilder.core.components.llms.hallucination_detection.component.HallucinationDetection.meta) - * [`HallucinationDetection.name`](appbuilder.core.components.llms.hallucination_detection.md#appbuilder.core.components.llms.hallucination_detection.component.HallucinationDetection.name) - * [`HallucinationDetection.run()`](appbuilder.core.components.llms.hallucination_detection.md#appbuilder.core.components.llms.hallucination_detection.component.HallucinationDetection.run) - * [`HallucinationDetection.tool_eval()`](appbuilder.core.components.llms.hallucination_detection.md#appbuilder.core.components.llms.hallucination_detection.component.HallucinationDetection.tool_eval) - * [`HallucinationDetection.version`](appbuilder.core.components.llms.hallucination_detection.md#appbuilder.core.components.llms.hallucination_detection.component.HallucinationDetection.version) -* [appbuilder.core.components.llms.is_complex_query package](appbuilder.core.components.llms.is_complex_query.md) - * [Submodules](appbuilder.core.components.llms.is_complex_query.md#submodules) - * [appbuilder.core.components.llms.is_complex_query.component module](appbuilder.core.components.llms.is_complex_query.md#module-appbuilder.core.components.llms.is_complex_query.component) - * [`IsComplexQuery`](appbuilder.core.components.llms.is_complex_query.md#appbuilder.core.components.llms.is_complex_query.component.IsComplexQuery) - * [`IsComplexQuery.meta`](appbuilder.core.components.llms.is_complex_query.md#appbuilder.core.components.llms.is_complex_query.component.IsComplexQuery.meta) - * [`IsComplexQuery.name`](appbuilder.core.components.llms.is_complex_query.md#appbuilder.core.components.llms.is_complex_query.component.IsComplexQuery.name) - * [`IsComplexQuery.run()`](appbuilder.core.components.llms.is_complex_query.md#appbuilder.core.components.llms.is_complex_query.component.IsComplexQuery.run) - * [`IsComplexQuery.version`](appbuilder.core.components.llms.is_complex_query.md#appbuilder.core.components.llms.is_complex_query.component.IsComplexQuery.version) -* [appbuilder.core.components.llms.mrc package](appbuilder.core.components.llms.mrc.md) - * [Submodules](appbuilder.core.components.llms.mrc.md#submodules) - * [appbuilder.core.components.llms.mrc.component module](appbuilder.core.components.llms.mrc.md#module-appbuilder.core.components.llms.mrc.component) - * [`MRC`](appbuilder.core.components.llms.mrc.md#appbuilder.core.components.llms.mrc.component.MRC) - * [`MRC.meta`](appbuilder.core.components.llms.mrc.md#appbuilder.core.components.llms.mrc.component.MRC.meta) - * [`MRC.name`](appbuilder.core.components.llms.mrc.md#appbuilder.core.components.llms.mrc.component.MRC.name) - * [`MRC.run()`](appbuilder.core.components.llms.mrc.md#appbuilder.core.components.llms.mrc.component.MRC.run) - * [`MRC.version`](appbuilder.core.components.llms.mrc.md#appbuilder.core.components.llms.mrc.component.MRC.version) -* [appbuilder.core.components.llms.nl2pandas package](appbuilder.core.components.llms.nl2pandas.md) - * [Submodules](appbuilder.core.components.llms.nl2pandas.md#submodules) - * [appbuilder.core.components.llms.nl2pandas.component module](appbuilder.core.components.llms.nl2pandas.md#module-appbuilder.core.components.llms.nl2pandas.component) - * [`Nl2pandasComponent`](appbuilder.core.components.llms.nl2pandas.md#appbuilder.core.components.llms.nl2pandas.component.Nl2pandasComponent) - * [`Nl2pandasComponent.meta`](appbuilder.core.components.llms.nl2pandas.md#appbuilder.core.components.llms.nl2pandas.component.Nl2pandasComponent.meta) - * [`Nl2pandasComponent.name`](appbuilder.core.components.llms.nl2pandas.md#appbuilder.core.components.llms.nl2pandas.component.Nl2pandasComponent.name) - * [`Nl2pandasComponent.run()`](appbuilder.core.components.llms.nl2pandas.md#appbuilder.core.components.llms.nl2pandas.component.Nl2pandasComponent.run) - * [`Nl2pandasComponent.version`](appbuilder.core.components.llms.nl2pandas.md#appbuilder.core.components.llms.nl2pandas.component.Nl2pandasComponent.version) -* [appbuilder.core.components.llms.oral_query_generation package](appbuilder.core.components.llms.oral_query_generation.md) - * [Submodules](appbuilder.core.components.llms.oral_query_generation.md#submodules) - * [appbuilder.core.components.llms.oral_query_generation.component module](appbuilder.core.components.llms.oral_query_generation.md#module-appbuilder.core.components.llms.oral_query_generation.component) - * [`OralQueryGeneration`](appbuilder.core.components.llms.oral_query_generation.md#appbuilder.core.components.llms.oral_query_generation.component.OralQueryGeneration) - * [`OralQueryGeneration.completion()`](appbuilder.core.components.llms.oral_query_generation.md#appbuilder.core.components.llms.oral_query_generation.component.OralQueryGeneration.completion) - * [`OralQueryGeneration.manifests`](appbuilder.core.components.llms.oral_query_generation.md#appbuilder.core.components.llms.oral_query_generation.component.OralQueryGeneration.manifests) - * [`OralQueryGeneration.meta`](appbuilder.core.components.llms.oral_query_generation.md#appbuilder.core.components.llms.oral_query_generation.component.OralQueryGeneration.meta) - * [`OralQueryGeneration.name`](appbuilder.core.components.llms.oral_query_generation.md#appbuilder.core.components.llms.oral_query_generation.component.OralQueryGeneration.name) - * [`OralQueryGeneration.regenerate_output()`](appbuilder.core.components.llms.oral_query_generation.md#appbuilder.core.components.llms.oral_query_generation.component.OralQueryGeneration.regenerate_output) - * [`OralQueryGeneration.run()`](appbuilder.core.components.llms.oral_query_generation.md#appbuilder.core.components.llms.oral_query_generation.component.OralQueryGeneration.run) - * [`OralQueryGeneration.tool_eval()`](appbuilder.core.components.llms.oral_query_generation.md#appbuilder.core.components.llms.oral_query_generation.component.OralQueryGeneration.tool_eval) - * [`OralQueryGeneration.version`](appbuilder.core.components.llms.oral_query_generation.md#appbuilder.core.components.llms.oral_query_generation.component.OralQueryGeneration.version) -* [appbuilder.core.components.llms.playground package](appbuilder.core.components.llms.playground.md) - * [Submodules](appbuilder.core.components.llms.playground.md#submodules) - * [appbuilder.core.components.llms.playground.component module](appbuilder.core.components.llms.playground.md#module-appbuilder.core.components.llms.playground.component) - * [`Playground`](appbuilder.core.components.llms.playground.md#appbuilder.core.components.llms.playground.component.Playground) - * [`Playground.meta`](appbuilder.core.components.llms.playground.md#appbuilder.core.components.llms.playground.component.Playground.meta) - * [`Playground.name`](appbuilder.core.components.llms.playground.md#appbuilder.core.components.llms.playground.component.Playground.name) - * [`Playground.prompt_template`](appbuilder.core.components.llms.playground.md#appbuilder.core.components.llms.playground.component.Playground.prompt_template) - * [`Playground.run()`](appbuilder.core.components.llms.playground.md#appbuilder.core.components.llms.playground.component.Playground.run) - * [`Playground.variable_names`](appbuilder.core.components.llms.playground.md#appbuilder.core.components.llms.playground.component.Playground.variable_names) - * [`Playground.version`](appbuilder.core.components.llms.playground.md#appbuilder.core.components.llms.playground.component.Playground.version) -* [appbuilder.core.components.llms.qa_pair_mining package](appbuilder.core.components.llms.qa_pair_mining.md) - * [Submodules](appbuilder.core.components.llms.qa_pair_mining.md#submodules) - * [appbuilder.core.components.llms.qa_pair_mining.component module](appbuilder.core.components.llms.qa_pair_mining.md#module-appbuilder.core.components.llms.qa_pair_mining.component) - * [`QAPairMining`](appbuilder.core.components.llms.qa_pair_mining.md#appbuilder.core.components.llms.qa_pair_mining.component.QAPairMining) - * [`QAPairMining.meta`](appbuilder.core.components.llms.qa_pair_mining.md#appbuilder.core.components.llms.qa_pair_mining.component.QAPairMining.meta) - * [`QAPairMining.name`](appbuilder.core.components.llms.qa_pair_mining.md#appbuilder.core.components.llms.qa_pair_mining.component.QAPairMining.name) - * [`QAPairMining.run()`](appbuilder.core.components.llms.qa_pair_mining.md#appbuilder.core.components.llms.qa_pair_mining.component.QAPairMining.run) - * [`QAPairMining.version`](appbuilder.core.components.llms.qa_pair_mining.md#appbuilder.core.components.llms.qa_pair_mining.component.QAPairMining.version) -* [appbuilder.core.components.llms.query_decomposition package](appbuilder.core.components.llms.query_decomposition.md) - * [Submodules](appbuilder.core.components.llms.query_decomposition.md#submodules) - * [appbuilder.core.components.llms.query_decomposition.component module](appbuilder.core.components.llms.query_decomposition.md#module-appbuilder.core.components.llms.query_decomposition.component) - * [`QueryDecomposition`](appbuilder.core.components.llms.query_decomposition.md#appbuilder.core.components.llms.query_decomposition.component.QueryDecomposition) - * [`QueryDecomposition.meta`](appbuilder.core.components.llms.query_decomposition.md#appbuilder.core.components.llms.query_decomposition.component.QueryDecomposition.meta) - * [`QueryDecomposition.name`](appbuilder.core.components.llms.query_decomposition.md#appbuilder.core.components.llms.query_decomposition.component.QueryDecomposition.name) - * [`QueryDecomposition.run()`](appbuilder.core.components.llms.query_decomposition.md#appbuilder.core.components.llms.query_decomposition.component.QueryDecomposition.run) - * [`QueryDecomposition.version`](appbuilder.core.components.llms.query_decomposition.md#appbuilder.core.components.llms.query_decomposition.component.QueryDecomposition.version) -* [appbuilder.core.components.llms.query_rewrite package](appbuilder.core.components.llms.query_rewrite.md) - * [Submodules](appbuilder.core.components.llms.query_rewrite.md#submodules) - * [appbuilder.core.components.llms.query_rewrite.component module](appbuilder.core.components.llms.query_rewrite.md#module-appbuilder.core.components.llms.query_rewrite.component) - * [`QueryRewrite`](appbuilder.core.components.llms.query_rewrite.md#appbuilder.core.components.llms.query_rewrite.component.QueryRewrite) - * [`QueryRewrite.meta`](appbuilder.core.components.llms.query_rewrite.md#appbuilder.core.components.llms.query_rewrite.component.QueryRewrite.meta) - * [`QueryRewrite.name`](appbuilder.core.components.llms.query_rewrite.md#appbuilder.core.components.llms.query_rewrite.component.QueryRewrite.name) - * [`QueryRewrite.run()`](appbuilder.core.components.llms.query_rewrite.md#appbuilder.core.components.llms.query_rewrite.component.QueryRewrite.run) - * [`QueryRewrite.version`](appbuilder.core.components.llms.query_rewrite.md#appbuilder.core.components.llms.query_rewrite.component.QueryRewrite.version) -* [appbuilder.core.components.llms.similar_question package](appbuilder.core.components.llms.similar_question.md) - * [Submodules](appbuilder.core.components.llms.similar_question.md#submodules) - * [appbuilder.core.components.llms.similar_question.component module](appbuilder.core.components.llms.similar_question.md#module-appbuilder.core.components.llms.similar_question.component) - * [`SimilarQuestion`](appbuilder.core.components.llms.similar_question.md#appbuilder.core.components.llms.similar_question.component.SimilarQuestion) - * [`SimilarQuestion.manifests`](appbuilder.core.components.llms.similar_question.md#appbuilder.core.components.llms.similar_question.component.SimilarQuestion.manifests) - * [`SimilarQuestion.meta`](appbuilder.core.components.llms.similar_question.md#appbuilder.core.components.llms.similar_question.component.SimilarQuestion.meta) - * [`SimilarQuestion.name`](appbuilder.core.components.llms.similar_question.md#appbuilder.core.components.llms.similar_question.component.SimilarQuestion.name) - * [`SimilarQuestion.run()`](appbuilder.core.components.llms.similar_question.md#appbuilder.core.components.llms.similar_question.component.SimilarQuestion.run) - * [`SimilarQuestion.tool_eval()`](appbuilder.core.components.llms.similar_question.md#appbuilder.core.components.llms.similar_question.component.SimilarQuestion.tool_eval) - * [`SimilarQuestion.version`](appbuilder.core.components.llms.similar_question.md#appbuilder.core.components.llms.similar_question.component.SimilarQuestion.version) -* [appbuilder.core.components.llms.style_rewrite package](appbuilder.core.components.llms.style_rewrite.md) - * [Submodules](appbuilder.core.components.llms.style_rewrite.md#submodules) - * [appbuilder.core.components.llms.style_rewrite.component module](appbuilder.core.components.llms.style_rewrite.md#module-appbuilder.core.components.llms.style_rewrite.component) - * [`StyleRewrite`](appbuilder.core.components.llms.style_rewrite.md#appbuilder.core.components.llms.style_rewrite.component.StyleRewrite) - * [`StyleRewrite.manifests`](appbuilder.core.components.llms.style_rewrite.md#appbuilder.core.components.llms.style_rewrite.component.StyleRewrite.manifests) - * [`StyleRewrite.meta`](appbuilder.core.components.llms.style_rewrite.md#appbuilder.core.components.llms.style_rewrite.component.StyleRewrite.meta) - * [`StyleRewrite.name`](appbuilder.core.components.llms.style_rewrite.md#appbuilder.core.components.llms.style_rewrite.component.StyleRewrite.name) - * [`StyleRewrite.run()`](appbuilder.core.components.llms.style_rewrite.md#appbuilder.core.components.llms.style_rewrite.component.StyleRewrite.run) - * [`StyleRewrite.tool_eval()`](appbuilder.core.components.llms.style_rewrite.md#appbuilder.core.components.llms.style_rewrite.component.StyleRewrite.tool_eval) - * [`StyleRewrite.version`](appbuilder.core.components.llms.style_rewrite.md#appbuilder.core.components.llms.style_rewrite.component.StyleRewrite.version) -* [appbuilder.core.components.llms.style_writing package](appbuilder.core.components.llms.style_writing.md) - * [Submodules](appbuilder.core.components.llms.style_writing.md#submodules) - * [appbuilder.core.components.llms.style_writing.component module](appbuilder.core.components.llms.style_writing.md#module-appbuilder.core.components.llms.style_writing.component) - * [`StyleWriting`](appbuilder.core.components.llms.style_writing.md#appbuilder.core.components.llms.style_writing.component.StyleWriting) - * [`StyleWriting.manifests`](appbuilder.core.components.llms.style_writing.md#appbuilder.core.components.llms.style_writing.component.StyleWriting.manifests) - * [`StyleWriting.meta`](appbuilder.core.components.llms.style_writing.md#appbuilder.core.components.llms.style_writing.component.StyleWriting.meta) - * [`StyleWriting.name`](appbuilder.core.components.llms.style_writing.md#appbuilder.core.components.llms.style_writing.component.StyleWriting.name) - * [`StyleWriting.run()`](appbuilder.core.components.llms.style_writing.md#appbuilder.core.components.llms.style_writing.component.StyleWriting.run) - * [`StyleWriting.tool_eval()`](appbuilder.core.components.llms.style_writing.md#appbuilder.core.components.llms.style_writing.component.StyleWriting.tool_eval) - * [`StyleWriting.version`](appbuilder.core.components.llms.style_writing.md#appbuilder.core.components.llms.style_writing.component.StyleWriting.version) - * [`StyleWritingArgs`](appbuilder.core.components.llms.style_writing.md#appbuilder.core.components.llms.style_writing.component.StyleWritingArgs) - * [`StyleWritingArgs.message`](appbuilder.core.components.llms.style_writing.md#appbuilder.core.components.llms.style_writing.component.StyleWritingArgs.message) - * [`StyleWritingArgs.style_query`](appbuilder.core.components.llms.style_writing.md#appbuilder.core.components.llms.style_writing.component.StyleWritingArgs.style_query) - * [`StyleWritingArgs.length`](appbuilder.core.components.llms.style_writing.md#appbuilder.core.components.llms.style_writing.component.StyleWritingArgs.length) - * [`StyleWritingArgs.length`](appbuilder.core.components.llms.style_writing.md#id0) - * [`StyleWritingArgs.message`](appbuilder.core.components.llms.style_writing.md#id1) - * [`StyleWritingArgs.model_computed_fields`](appbuilder.core.components.llms.style_writing.md#appbuilder.core.components.llms.style_writing.component.StyleWritingArgs.model_computed_fields) - * [`StyleWritingArgs.model_config`](appbuilder.core.components.llms.style_writing.md#appbuilder.core.components.llms.style_writing.component.StyleWritingArgs.model_config) - * [`StyleWritingArgs.model_fields`](appbuilder.core.components.llms.style_writing.md#appbuilder.core.components.llms.style_writing.component.StyleWritingArgs.model_fields) - * [`StyleWritingArgs.style_query`](appbuilder.core.components.llms.style_writing.md#id2) -* [appbuilder.core.components.llms.tag_extraction package](appbuilder.core.components.llms.tag_extraction.md) - * [Submodules](appbuilder.core.components.llms.tag_extraction.md#submodules) - * [appbuilder.core.components.llms.tag_extraction.component module](appbuilder.core.components.llms.tag_extraction.md#module-appbuilder.core.components.llms.tag_extraction.component) - * [`TagExtraction`](appbuilder.core.components.llms.tag_extraction.md#appbuilder.core.components.llms.tag_extraction.component.TagExtraction) - * [`TagExtraction.meta`](appbuilder.core.components.llms.tag_extraction.md#appbuilder.core.components.llms.tag_extraction.component.TagExtraction.meta) - * [`TagExtraction.name`](appbuilder.core.components.llms.tag_extraction.md#appbuilder.core.components.llms.tag_extraction.component.TagExtraction.name) - * [`TagExtraction.run()`](appbuilder.core.components.llms.tag_extraction.md#appbuilder.core.components.llms.tag_extraction.component.TagExtraction.run) - * [`TagExtraction.version`](appbuilder.core.components.llms.tag_extraction.md#appbuilder.core.components.llms.tag_extraction.component.TagExtraction.version) - -## Submodules diff --git a/docs/API-Reference/Python/appbuilder.core.components.llms.mrc.md b/docs/API-Reference/Python/appbuilder.core.components.llms.mrc.md deleted file mode 100644 index 08ef170d8..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.llms.mrc.md +++ /dev/null @@ -1,66 +0,0 @@ -# appbuilder.core.components.llms.mrc package - -## Submodules - -## appbuilder.core.components.llms.mrc.component module - -### *class* appbuilder.core.components.llms.mrc.component.MRC(model=None, secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:`CompletionBaseComponent` - -阅读理解问答组件,基于大模型进行阅读理解问答,支持拒答、澄清、重点强调、友好性提升、溯源等多种功能,可用于回答用户提出的问题。 - -Examples: - -```python -import appbuilder -import os - -# 设置环境变量 -os.environ["APPBUILDER_TOKEN"] = '...' - -# 创建MRC对象 -mrc_component = appbuilder.MRC() - -#初始化参数 -msg = "残疾人怎么办相关证件" -msg = appbuilder.Message(msg) -context_list = appbuilder.Message(["如何办理残疾人通行证一、残疾人通行证办理条件: -1、持有中华人民共和国残疾人证,下肢残疾或者听力残疾;2、持有准驾车型为C1(听力残疾)、 -C2(左下肢残疾、听力残疾", "3、本人拥有本市登记核发的非营运小型载客汽车,车辆须在检验有效期内, -并有有效交强险凭证,C5车辆加装操纵辅助装置后已办理变更手续。二、办理地点:北京市朝阳区左家庄北里35号: -北京市无障碍环境建设促进中心"]) - -# 模拟运行MRC组件,开启拒答、澄清追问、重点强调、友好性提升和溯源能力五个功能 -result = mrc_component.run(msg, context_list, reject=True, - clarify=True, highlight=True, friendly=True, cite=True) - -# 输出运行结果 -print(result) -``` - -#### meta *: MrcArgs* - -#### name *: str* *= 'mrc'* - -#### run(message, context_list, reject=False, clarify=False, highlight=False, friendly=False, cite=False, stream=False, temperature=1e-10, top_p=0) - -运行阅读理解问答模型并返回结果。 - -* **参数:** - * **(****obj** (*context_list*) -- Message): 输入消息,包含用户提出的问题。这是一个必需的参数。 - * **(****obj** -- Message): 用户输入的问题对应的段落文本列表。这是一个必需的参数。 - * **reject** (*bool* *,* *可选*) -- 拒答开关,如果为 True,则启用拒答能力。默认为 False。 - * **clarify** (*bool* *,* *可选*) -- 澄清开关,如果为 True,则启用澄清能力。默认为 False。 - * **highlight** (*bool* *,* *可选*) -- 重点强调开关,如果为 True,则启用重点强调能力。默认为 False。 - * **friendly** (*bool* *,* *可选*) -- 友好性提升开关,如果为 True,则启用友好性提升能力。默认为 False。 - * **cite** (*bool* *,* *可选*) -- 溯源开关,如果为 True,则启用溯源能力。默认为 False。 - * **stream** (*bool* *,* *可选*) -- 指定是否以流式形式返回响应。默认为 False。 - * **temperature** (*float* *,* *可选*) -- 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 - * **top_p** (*float* *,* *可选*) -- 影响输出文本的多样性,取值越大,生成文本的多样性越强。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 -* **返回:** - Message: 模型运行后的输出消息。 -* **返回类型:** - obj - -#### version *: str* *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.llms.nl2pandas.md b/docs/API-Reference/Python/appbuilder.core.components.llms.nl2pandas.md deleted file mode 100644 index 3913aa222..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.llms.nl2pandas.md +++ /dev/null @@ -1,55 +0,0 @@ -# appbuilder.core.components.llms.nl2pandas package - -## Submodules - -## appbuilder.core.components.llms.nl2pandas.component module - -### *class* appbuilder.core.components.llms.nl2pandas.component.Nl2pandasComponent(model=None, secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:`CompletionBaseComponent` - -自然语言转pandas大模型组件, 基于生成式大模型对query进行理解并生成对应语义的可执行python代码(主要使用pandas),可用于基于表格的查询、问答等多种场景。 - -Examples: - -```python -import appbuilder -os.environ["APPBUILDER_TOKEN"] = '...' -table_info = '''表格列信息如下: -学校名 : 清华附小 , 字符串类型,代表小学学校的名称 -所属地区 : 西城区 , 字符串类型,表示该小学学校所在的位置 -创办时间 : 1998 , 数字值类型,表示该小学学校的创办时间 -类别 : 公立小学 , 字符串类型,表示该小学学校所在的类别 -学生人数 : 2000 , 数字值类型,表示该小学学校的学生数量 -教职工人数 : 140 , 数字值类型,表示该小学学校的教职工数量 -教学班数量 : 122 , 数字值类型,表示该小学学校的教学班数量 -''' -query = "海淀区有哪些学校" -query = appbuilder.Message(query) - -nl2pandas = appbuilder.Nl2pandasComponent(model="Qianfan-Agent-Speed-8k") -answer = nl2pandas(query, table_info = table_info) -``` - -#### meta - -`Nl2pandasArgs` 的别名 - -#### name *: str* *= 'nl2pandas'* - -#### run(message, table_info=None, stream=False, temperature=1e-10, top_p=0) - -使用给定的输入运行模型并返回结果。 - -* **参数:** - * **(****obj** (*table_info*) -- Message): 输入问题,通常是针对表格信息的提问,如'海淀区的小学有哪些'。这是一个必需的参数。 - * **(****obj** -- Message, optional): 表格信息,包括表格列名、对应列名的示例和释义。默认值为 None,但这是一个必需的参数。 - * **stream** (*bool* *,* *optional*) -- 指定是否以流式形式返回响应。默认为 False。 - * **temperature** (*float* *,* *optional*) -- 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 - * **top_p** (*float* *,* *optional*) -- 影响输出文本的多样性,取值越大,生成文本的多样性越强。取值范围为 0.0 到 1.0,较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 -* **返回:** - Message: 模型运行后的输出消息。 -* **返回类型:** - obj - -#### version *: str* *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.llms.oral_query_generation.md b/docs/API-Reference/Python/appbuilder.core.components.llms.oral_query_generation.md deleted file mode 100644 index cfe950bb8..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.llms.oral_query_generation.md +++ /dev/null @@ -1,84 +0,0 @@ -# appbuilder.core.components.llms.oral_query_generation package - -## Submodules - -## appbuilder.core.components.llms.oral_query_generation.component module - -### *class* appbuilder.core.components.llms.oral_query_generation.component.OralQueryGeneration(model=None, secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:`CompletionBaseComponent` - -口语化Query生成,可用于问答场景下对文档增强索引。 - *注:该组件推荐使用Qianfan-Agent-Speed-8k模型。* - -Examples: - -```python -import os -import appbuilder - -os.environ["APPBUILDER_TOKEN"] = '...' - -text = ('文档标题:在OPPO Reno5上使用视频超级防抖\n' - '文档摘要:OPPO Reno5上的视频超级防抖,视频超级防抖3.0,多代视频防抖算法积累,这一代依旧超级防抖超级稳。 开启视频超级' - '防抖 开启路径:打开「相机 > 视频 > 点击屏幕上方的“超级防抖”标识」 后置视频同时支持超级防抖和超级防抖Pro功能,开启超级' - '防抖后手机屏幕将出现超级防抖Pro开关,点击即可开启或关闭。 除此之外,前置视频同样加持防抖算法,边走边拍也能稳定聚焦脸部' - ',实时视频分享您的生活。') -oral_query_generation = appbuilder.OralQueryGeneration(model='Qianfan-Agent-Speed-8k') -answer = oral_query_generation(appbuilder.Message(text), query_type='全部', output_format='str') -print(answer.content) -``` - -#### completion(version, base_url, request, timeout: float | None = None, retry: int = 0) - -Send a byte array of an audio file to obtain the result of speech recognition. - -#### manifests *= [{'description': '输入文本、待生成的query类型和输出格式,生成query,并按照要求的格式进行输出。', 'name': 'query_generation', 'parameters': {'properties': {'output_format': {'description': '输出格式,可选json或str,str格式与老版本输出格式相同。', 'text': 'string'}, 'query_type': {'description': '待生成的query类型,可选问题、短语以及全部(问题 + 短语)。', 'text': 'string'}, 'text': {'description': '输入文本,组件会根据该输入文本生成query。', 'text': 'string'}}, 'required': ['text'], 'type': 'object'}}]* - -#### meta - -`OralQueryGenerationArgs` 的别名 - -#### name *: str* *= 'query_generation'* - -#### regenerate_output(model_output, output_format) - -兼容老版本的输出格式 - -#### run(message, query_type='全部', output_format='str', stream=False, temperature=1e-10, top_p=0.0) - -使用给定的输入运行模型并返回结果。 - -* **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入消息,包含query、context和answer等信息。这是一个必需的参数。 - * **query_type** (*str* *,* *可选*) -- 待生成的query类型,包括问题、短语和全部(问题+短语)。默认为全部。 - * **output_format** (*str* *,* *可选*) -- 输出格式,包括json和str,当stream为True时,只能以json形式输出。默认为str。 - * **stream** (*bool* *,* *可选*) -- 指定是否以流式形式返回响应。默认为 False。 - * **temperature** (*float* *,* *可选*) -- 模型配置的温度参数,用于调整模型的生成概率。 - 取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 - * **top_p** (*float* *,* *可选*) -- 影响输出文本的多样性,取值越大,生成文本的多样性越强。 - 取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 -* **返回:** - 模型运行后的输出消息。 -* **返回类型:** - result ([Message](appbuilder.core.md#appbuilder.core.message.Message)) - -#### tool_eval(name: str, stream: bool = False, \*\*kwargs) - -调用函数进行工具评估。 - -* **参数:** - * **name** (*str*) -- 评估工具的名称。 - * **stream** (*bool* *,* *optional*) -- 是否以流的形式返回结果。默认为False。 - * **\*\*kwargs** -- 关键字参数,可以包含以下参数: - text (str): 需要评估的文本。 - query_type (str, optional): 查询类型,默认为'全部'。 - output_format (str, optional): 输出格式,默认为'str'。 - model_configs (dict, optional): 模型配置,默认为空字典。 -* **返回:** - 如果stream为False,则返回评估结果列表; - 如果stream为True,则逐个返回评估结果。 -* **抛出:** - **ValueError** -- 如果未提供text参数,则抛出ValueError异常。 - -#### version *: str* *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.llms.playground.md b/docs/API-Reference/Python/appbuilder.core.components.llms.playground.md deleted file mode 100644 index 2c4fe647a..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.llms.playground.md +++ /dev/null @@ -1,57 +0,0 @@ -# appbuilder.core.components.llms.playground package - -## Submodules - -## appbuilder.core.components.llms.playground.component module - -### *class* appbuilder.core.components.llms.playground.component.Playground(prompt_template=None, model=None, secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:`CompletionBaseComponent` - -空模板, 支持用户自定义prompt模板,并进行执行 - -Examples: - -```python -import os -import appbuilder -os.environ["APPBUILDER_TOKEN"] = "..." - -play = appbuilder.Playground(prompt_template="你好,{name},我是{bot_name},{bot_name}是一个{bot_type},我可以{bot_function},你可以问我{bot_question}。", model="Qianfan-Agent-Speed-8k") -play(appbuilder.Message({"name": "小明", "bot_name": "小红", "bot_type": "聊天机器人", "bot_function": "聊天", "bot_question": "你好吗?"}), stream=False) -``` - -#### meta - -`PlaygroundArgs` 的别名 - -#### name *: str* *= 'playground'* - -#### prompt_template *= ''* - -#### run(message, stream=False, temperature=1e-10, top_p=0.0, max_output_tokens=1024, disable_search=True, response_format='text', stop=[], \*\*kwargs) - -使用给定的输入运行模型并返回结果。 - -* **参数:** - * **(****obj** (*message*) -- Message): 输入消息,用于模型的主要输入内容。这是一个必需的参数。 - * **stream** (*bool* *,* *可选*) -- 指定是否以流式形式返回响应。默认为 False。 - * **temperature** (*float* *,* *可选*) -- 模型配置的温度参数,用于调整模型的生成概率。 - 取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 - * **top_p** (*float* *,* *可选*) -- 影响输出文本的多样性,取值越大,生成文本的多样性越强。 - 取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 - * **max_output_tokens** (*int* *,* *可选*) -- 指定生成的文本的最大长度,默认最大输出token数为1024, 最小为2, - 最大输出token与选择的模型有关。 - * **disable_search** (*bool* *,* *可选*) -- 是否强制关闭实时搜索功能,默认为 True,表示关闭。 - * **response_format** (*str* *,* *可选*) -- 指定返回的消息格式,默认为 'text',以文本模式返回。 - 可选 'json_object',以 json 格式返回,但可能存在不满足效果的情况。 - * **stop** (*list* *[**str* *]* *,* *可选*) -- 生成停止标识,当模型生成结果以 stop 中某个元素结尾时,停止文本生成。 - 每个元素长度不超过 20 字符,最多 4 个元素。 -* **返回:** - Message: 模型运行后的输出消息。 -* **返回类型:** - obj - -#### variable_names *= {}* - -#### version *: str* *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.llms.qa_pair_mining.md b/docs/API-Reference/Python/appbuilder.core.components.llms.qa_pair_mining.md deleted file mode 100644 index b24b8d971..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.llms.qa_pair_mining.md +++ /dev/null @@ -1,35 +0,0 @@ -# appbuilder.core.components.llms.qa_pair_mining package - -## Submodules - -## appbuilder.core.components.llms.qa_pair_mining.component module - -### *class* appbuilder.core.components.llms.qa_pair_mining.component.QAPairMining(model=None, secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:`CompletionBaseComponent` - -基于输入文本内容,快速生成多个问题及对应答案,极大提高信息提炼的效率和准确性.广泛用于在线客服、智能问答等领域。 - -Examples: - -#### meta - -`QAPairMiningMeta` 的别名 - -#### name *: str* *= 'qa_pair_mining'* - -#### run(message, stream=False, temperature=1e-10, top_p=0.0) - -给定输入(message)到模型运行,同时指定运行参数,并返回结果。 - -* **参数:** - * **(****obj** (*message*) -- Message): 输入消息,用于模型的主要输入内容。这是一个必需的参数。 - * **stream** (*bool* *,* *optional*) -- 指定是否以流式形式返回响应。默认为 False。 - * **temperature** (*float* *,* *optional*) -- 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 - * **top_p** (*float* *,* *optional*) -- 影响输出文本的多样性,取值越大,生成文本的多样性越强。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 -* **返回:** - Message: 模型运行后的输出消息。 -* **返回类型:** - obj - -#### version *: str* *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.llms.query_decomposition.md b/docs/API-Reference/Python/appbuilder.core.components.llms.query_decomposition.md deleted file mode 100644 index e809a4290..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.llms.query_decomposition.md +++ /dev/null @@ -1,53 +0,0 @@ -# appbuilder.core.components.llms.query_decomposition package - -## Submodules - -## appbuilder.core.components.llms.query_decomposition.component module - -query decomposition - -### *class* appbuilder.core.components.llms.query_decomposition.component.QueryDecomposition(model=None, secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:`CompletionBaseComponent` - -尝试对已经判定为复杂问题的原始问题进行拆解,把复杂问题拆解为一个个简单问题。广泛用于知识问答场景。 - -Examples: - -```python -import os -import appbuilder - -# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 -os.environ["APPBUILDER_TOKEN"] = "..." - -query_decomposition = appbuilder.QueryDecomposition(model="Qianfan-Agent-Speed-8k") - -msg = "吸塑包装盒在工业化生产和物流运输中分别有什么重要性?" -msg = appbuilder.Message(msg) -answer = query_decomposition(msg) - -print("Answer: \n{}".format(answer.content)) -``` - -#### meta - -`QueryDecompositionMeta` 的别名 - -#### name *: str* *= 'query_decomposition'* - -#### run(message, stream=False, temperature=1e-10, top_p=0.0) - -给定输入(message)到模型运行,同时指定运行参数,并返回结果。 - -* **参数:** - * **(****obj** (*message*) -- Message): 输入消息,用于模型的主要输入内容。这是一个必需的参数。 - * **stream** (*bool* *,* *optional*) -- 指定是否以流式形式返回响应。默认为 False。 - * **temperature** (*float* *,* *optional*) -- 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 - * **top_p** (*float* *,* *optional*) -- 影响输出文本的多样性,取值越大,生成文本的多样性越强。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 -* **返回:** - Message: 模型运行后的输出消息。 -* **返回类型:** - obj - -#### version *: str* *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.llms.query_rewrite.md b/docs/API-Reference/Python/appbuilder.core.components.llms.query_rewrite.md deleted file mode 100644 index 5afe91de3..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.llms.query_rewrite.md +++ /dev/null @@ -1,58 +0,0 @@ -# appbuilder.core.components.llms.query_rewrite package - -## Submodules - -## appbuilder.core.components.llms.query_rewrite.component module - -多轮改写 - -### *class* appbuilder.core.components.llms.query_rewrite.component.QueryRewrite(model=None, secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:`CompletionBaseComponent` - -多轮改写大模型组件, 基于生成式大模型进行多轮对话query改写的组件。它主要用于理解和优化用户与机器人的交互过程,进行指代消解及省略补全。该组件支持不同的改写类型,可根据对话历史生成更准确的用户查询。 - -Examples: - -```python -import appbuilder -os.environ["APPBUILDER_TOKEN"] = '...' - -query_rewrite = appbuilder.QueryRewrite(model="Qianfan-Agent-Speed-8k") -answer = query_rewrite(appbuilder.Message(['我应该怎么办理护照?', - '您可以查询官网或人工咨询', - '我需要准备哪些材料?', - '身份证、免冠照片一张以及填写完整的《中国公民因私出国(境)申请表》', - '在哪里办']), - rewrite_type="带机器人回复") -``` - -#### meta - -`QueryRewriteArgs` 的别名 - -#### name *: str* *= 'query_rewrite'* - -#### run(message, rewrite_type='带机器人回复', stream=False, temperature=1e-10, top_p=0) - -使用给定的输入运行模型并返回结果。 - -* **参数:** - * **(****obj** (*message*) -- Message): 输入消息,用于模型的主要输入内容。这是一个必需的参数。 - * **rewrite_type** (*str* *,* *可选*) -- 改写类型选项,可选值为 '带机器人回复'(改写时参考user查询历史和assistant回复历史), - '仅用户查询'(改写时参考user查询历史)。默认为"带机器人回复"。 - * **stream** (*bool* *,* *可选*) -- 指定是否以流式形式返回响应。默认为 False。 - * **temperature** (*float* *,* *可选*) -- 模型配置的温度参数,用于调整模型的生成概率。 - 取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。 - 默认值为 1e-10。 - * **top_p** (*float* *,* *可选*) -- 影响输出文本的多样性,取值越大,生成文本的多样性越强。 - 取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。 - 默认值为 0。 -* **返回:** - Message: 模型运行后的输出消息。 -* **返回类型:** - obj -* **抛出:** - **ValueError** -- 如果输入消息为空或不符合要求,将抛出 ValueError 异常。 - -#### version *: str* *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.llms.similar_question.md b/docs/API-Reference/Python/appbuilder.core.components.llms.similar_question.md deleted file mode 100644 index 1dead46a1..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.llms.similar_question.md +++ /dev/null @@ -1,75 +0,0 @@ -# appbuilder.core.components.llms.similar_question package - -## Submodules - -## appbuilder.core.components.llms.similar_question.component module - -similar question - -### *class* appbuilder.core.components.llms.similar_question.component.SimilarQuestion(model=None, secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:`CompletionBaseComponent` - -基于输入的问题, 挖掘出与该问题相关的类似问题。广泛用于客服、问答等场景。 - -Examples: - -```python -import os -import appbuilder - -os.environ["APPBUILDER_TOKEN"] = "..." - -qa_mining = appbuilder.SimilarQuestion(model="Qianfan-Agent-Speed-8k") - -msg = "我想吃冰淇淋,哪里的冰淇淋比较好吃?" -msg = appbuilder.Message(msg) -answer = qa_mining(msg) - -print("Answer: \n{}".format(answer.content)) -``` - -#### manifests *= [{'description': '基于输入的问题,挖掘出与该问题相关的类似问题。', 'name': 'similar_question', 'parameters': {'properties': {'query': {'description': '输入的问题,用于大模型根据该问题输出相关的类似问题。', 'type': 'string'}}, 'required': ['query'], 'type': 'object'}}]* - -#### meta - -`SimilarQuestionMeta` 的别名 - -#### name *: str* *= 'similar_question'* - -#### run(message, stream=False, temperature=1e-10, top_p=0.0, request_id=None) - -给定输入(message)到模型运行,同时指定运行参数,并返回结果。 - -* **参数:** - * **(****obj** (*message*) -- Message): 输入消息,用于模型的主要输入内容。这是一个必需的参数。 - * **stream** (*bool* *,* *可选*) -- 指定是否以流式形式返回响应。默认为 False。 - * **temperature** (*float* *,* *可选*) -- 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 - * **top_p** (*float* *,* *可选*) -- 影响输出文本的多样性,取值越大,生成文本的多样性越强。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 -* **返回:** - Message: 模型运行后的输出消息。 -* **返回类型:** - obj - -#### tool_eval(name: str, streaming: bool = False, \*\*kwargs) - -执行函数调用的评估工具。 - -* **参数:** - * **name** (*str*) -- 函数名。 - * **streaming** (*bool* *,* *optional*) -- 是否以流式方式输出结果。默认为False。 - * **\*\*kwargs** -- - - 其他关键字参数,包括: - traceid (str, optional): 请求的追踪ID。 - query (str): 输入的查询字符串。 - model_configs (dict, optional): 模型配置字典,包括: - > temperature (float, optional): 温度参数,用于控制输出结果的多样性。默认为1e-10。 - > top_p (float, optional): 截断概率,用于控制生成文本的质量。默认为0.0。 -* **返回:** - 如果streaming为False,则返回评估结果的字符串表示。 - 如果streaming为True,则生成评估结果的字符串表示的迭代器。 -* **抛出:** - **ValueError** -- 如果未提供query参数,则抛出此异常。 - -#### version *: str* *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.llms.style_rewrite.md b/docs/API-Reference/Python/appbuilder.core.components.llms.style_rewrite.md deleted file mode 100644 index a77e7dea5..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.llms.style_rewrite.md +++ /dev/null @@ -1,69 +0,0 @@ -# appbuilder.core.components.llms.style_rewrite package - -## Submodules - -## appbuilder.core.components.llms.style_rewrite.component module - -### *class* appbuilder.core.components.llms.style_rewrite.component.StyleRewrite(model=None, secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:`CompletionBaseComponent` - -文本风格转写大模型组件, 基于生成式大模型对文本的风格进行改写,支持有营销、客服、直播、激励及教学五种话术。 - -Examples: - -```python -import os -import appbuilder -os.environ["APPBUILDER_TOKEN"] = '...' - -style_rewrite = appbuilder.StyleRewrite(model="Qianfan-Agent-Speed-8k") -answer = style_rewrite(appbuilder.Message("文心大模型发布新版本"), style="激励话术") -``` - -#### manifests *= [{'description': '能够将一段文本转换成不同的风格(营销、客服、直播、激励及教学话术),同时保持原文的基本意义不变。', 'name': 'style_rewrite', 'parameters': {'properties': {'query': {'description': '需要改写的文本。', 'type': 'string'}, 'style': {'description': '想要转换的文本风格,目前有营销、客服、直播、激励及教学五种话术可选. 默认是营销话术。', 'enum': ['营销话术', '客服话术', '直播话术', '激励话术', '教学话术'], 'type': 'string'}}, 'required': ['query'], 'type': 'object'}}]* - -#### meta - -`StyleRewriteArgs` 的别名 - -#### name *: str* *= 'style_rewrite'* - -#### run(message, style='营销话术', stream=False, temperature=1e-10, top_p=0.0, request_id=None) - -使用给定的输入运行模型并返回结果。 - -* **参数:** - * **(****obj** (*message*) -- Message): 输入消息,用于模型的主要输入内容。这是一个必需的参数。 - * **style** (*str* *,* *optional*) -- 想要转换的文本风格,目前有营销、客服、直播、激励及教学五种话术可选。默认为"营销话术"。 - * **stream** (*bool* *,* *optional*) -- 指定是否以流式形式返回响应。默认为 False。 - * **temperature** (*float* *,* *optional*) -- 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 - * **top_p** (*float* *,* *optional*) -- 影响输出文本的多样性,取值越大,生成文本的多样性越强。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 -* **返回:** - Message: 模型运行后的输出消息。 -* **返回类型:** - obj - -#### tool_eval(name: str, streaming: bool = False, \*\*kwargs) - -执行工具评估函数 - -* **参数:** - * **name** (*str*) -- 函数名称,本函数不使用该参数,但保留以符合某些框架的要求。 - * **streaming** (*bool* *,* *optional*) -- 是否以流的形式返回结果。默认为 False,即一次性返回结果。如果设置为 True,则以生成器形式逐个返回结果。 - * **\*\*kwargs** -- - - 其他参数,包含但不限于: - traceid (str): 请求的跟踪ID,用于日志记录和跟踪。 - query (str): 待评估的文本。 - style (str, optional): 评估风格,可选值为 ['营销话术', '客服话术', '直播话术', '激励话术', '教学话术']。默认为 '营销话术'。 - model_configs (dict, optional): 模型配置参数,可选的键包括: - > temperature (float, optional): 温度参数,用于控制生成文本的随机性。默认为 1e-10。 - > top_p (float, optional): top_p 采样参数,用于控制生成文本的多样性。默认为 0.0。 -* **返回:** - 如果 streaming 为 False,则直接返回评估结果字符串。 - 如果 streaming 为 True,则以生成器形式逐个返回评估结果字符串。 -* **抛出:** - **ValueError** -- 如果缺少参数 'query'。 - -#### version *: str* *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.llms.style_writing.md b/docs/API-Reference/Python/appbuilder.core.components.llms.style_writing.md deleted file mode 100644 index f33e8621c..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.llms.style_writing.md +++ /dev/null @@ -1,113 +0,0 @@ -# appbuilder.core.components.llms.style_writing package - -## Submodules - -## appbuilder.core.components.llms.style_writing.component module - -### *class* appbuilder.core.components.llms.style_writing.component.StyleWriting(model=None, secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:`CompletionBaseComponent` - -风格写作大模型组件, 基于生成式大模型进行风格写作,支持B站、小红书等多种风格,可用于文案、广告等多种场景。 - -Examples: - -```python -import os -import appbuilder -# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 -os.environ["APPBUILDER_TOKEN"] = '...' - -style_writing = appbuilder.StyleWriting(model="Qianfan-Agent-Speed-8k") -answer = style_writing(appbuilder.Message("帮我写一篇关于人体工学椅的文案"), style_query="小红书", length=100) -``` - -#### manifests *= [{'description': '根据用户输入的文案要求和文案风格,生成符合特定风格的产品介绍或宣传文案。目前支持生成小红书风格、B站风格或通用风格的文案。', 'name': 'style_writing', 'parameters': {'properties': {'length': {'description': '用于定义输出内容的长度。有效的选项包括 100(短)、300(中)、600(长),默认值为 100。', 'enum': [100, 300, 600], 'type': 'integer'}, 'query': {'description': '用于描述生成文案的主题和要求。', 'type': 'string'}, 'style': {'description': '用于定义文案生成的风格,包括通用、B站、小红书,默认为通用。', 'enum': ['通用', 'B站', '小红书'], 'type': 'string'}}, 'required': ['query'], 'type': 'object'}}]* - -#### meta - -[`StyleWritingArgs`](#appbuilder.core.components.llms.style_writing.component.StyleWritingArgs) 的别名 - -#### name *: str* *= 'style_writing'* - -#### run(message, style_query='通用', length=100, stream=False, temperature=1e-10, top_p=0, request_id=None) - -使用给定的输入运行模型并返回结果。 - -* **参数:** - * **(****obj** (*message*) -- Message): 输入消息,用于模型的主要输入内容。这是一个必需的参数。 - * **style_query** (*str*) -- 风格查询选项,用于指定写作风格。有效的选项包括 'B站', '小红书', '通用'。默认值为 '通用'。 - * **length** (*int*) -- 输出内容的长度。有效的选项包括 100(短),300(中),600(长)。默认值为 100。 - * **stream** (*bool* *,* *optional*) -- 指定是否以流式形式返回响应。默认为 False。 - * **temperature** (*float* *,* *optional*) -- 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 - * **top_p** (*float* *,* *optional*) -- 影响输出文本的多样性,取值越大,生成文本的多样性越强。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 - * **request_id** (*str* *,* *optional*) -- 请求ID,用于跟踪和识别请求。 -* **返回:** - Message: 模型运行后的输出消息。 -* **返回类型:** - obj - -#### tool_eval(name: str, streaming: bool = False, \*\*kwargs) - -对指定的工具进行函数调用评估。 - -* **参数:** - * **name** (*str*) -- 工具名称。 - * **streaming** (*bool* *,* *optional*) -- 是否以流的方式返回结果。默认为False。 - * **\*\*kwargs** -- 其他参数。 -* **返回:** - 如果 streaming 为 False,则返回评估结果字符串;如果 streaming 为 True,则返回一个生成器,每次迭代返回评估结果字符串的一部分。 -* **返回类型:** - str 或 generator -* **抛出:** - **ValueError** -- 如果未提供必要的参数 'query'。 - -#### version *: str* *= 'v1'* - -### *class* appbuilder.core.components.llms.style_writing.component.StyleWritingArgs(\*, name: str = '', tool_desc: Dict[str, Any] = {}, message: [Message](appbuilder.core.md#appbuilder.core.message.Message), style_query: StyleQueryChoices, length: LengthChoices) - -基类:[`ComponentArguments`](appbuilder.core.md#appbuilder.core.component.ComponentArguments) - -风格写作配置 - -#### message - -Message = Field(...) - -* **Type:** - [appbuilder.core.message.Message](appbuilder.core.md#appbuilder.core.message.Message) - -#### style_query - -StyleQueryChoices = Field(...) - -* **Type:** - appbuilder.core.components.llms.style_writing.base.StyleQueryChoices - -#### length - -LengthChoices = Field(...) - -* **Type:** - appbuilder.core.components.llms.style_writing.base.LengthChoices - -#### length *: LengthChoices* - -#### message *: [Message](appbuilder.core.md#appbuilder.core.message.Message)* - -#### model_computed_fields *: ClassVar[dict[str, ComputedFieldInfo]]* *= {}* - -A dictionary of computed field names and their corresponding ComputedFieldInfo objects. - -#### model_config *: ClassVar[ConfigDict]* *= {}* - -Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict]. - -#### model_fields *: ClassVar[dict[str, FieldInfo]]* *= {'length': FieldInfo(annotation=LengthChoices, required=True, description="输出长度,可选值为 '短' (100), '中' (300), '长' (600)。", json_schema_extra={'variable_name': 'length'}), 'message': FieldInfo(annotation=Message, required=True, description="输入消息,用于模型的主要输入内容,例如'帮我生成一个介绍保温杯的话术'", json_schema_extra={'variable_name': 'query'}), 'name': FieldInfo(annotation=str, required=False, default=''), 'style_query': FieldInfo(annotation=StyleQueryChoices, required=True, description="风格查询选项,可选值为 'B站', '小红书', '通用'。", json_schema_extra={'variable_name': 'style_query'}), 'tool_desc': FieldInfo(annotation=Dict[str, Any], required=False, default={})}* - -Metadata about the fields defined on the model, -mapping of field names to [FieldInfo][pydantic.fields.FieldInfo]. - -This replaces Model._\_fields_\_ from Pydantic V1. - -#### style_query *: StyleQueryChoices* diff --git a/docs/API-Reference/Python/appbuilder.core.components.llms.tag_extraction.md b/docs/API-Reference/Python/appbuilder.core.components.llms.tag_extraction.md deleted file mode 100644 index c9a4ffc68..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.llms.tag_extraction.md +++ /dev/null @@ -1,48 +0,0 @@ -# appbuilder.core.components.llms.tag_extraction package - -## Submodules - -## appbuilder.core.components.llms.tag_extraction.component module - -### *class* appbuilder.core.components.llms.tag_extraction.component.TagExtraction(model=None, secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:`CompletionBaseComponent` - -标签抽取组件,基于生成式大模型进行关键标签的抽取。 - -Examples: - -```python -import appbuilder -# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 -os.environ["APPBUILDER_TOKEN"] = '...' - -tag_extraction = appbuilder.TagExtraction(model="Qianfan-Agent-Speed-8k") -answer = tag_extraction(appbuilder.Message("从这段文本中抽取关键标签")) -``` - -#### meta - -`TagExtractionArgs` 的别名 - -#### name *: str* *= 'tag_extraction'* - -#### run(message, stream=False, temperature=1e-10, top_p=0.0) - -使用给定的输入运行模型并返回结果。 - -* **参数:** - * **(****obj** (*message*) -- Message, 必选): 输入消息,用于模型的主要输入内容。 - * **stream** (*bool* *,* *可选*) -- 指定是否以流式形式返回响应。默认为 False。 - * **temperature** (*float* *,* *可选*) -- 模型配置的温度参数,用于调整模型的生成概率。 - 取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。 - 默认值为 1e-10。 - * **top_p** (*float* *,* *可选*) -- 影响输出文本的多样性,取值越大,生成文本的多样性越强。 - 取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。 - 默认值为 0。 -* **返回:** - Message: 模型运行后的输出消息。 -* **返回类型:** - obj - -#### version *: str* *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.matching.md b/docs/API-Reference/Python/appbuilder.core.components.matching.md deleted file mode 100644 index ff369bd44..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.matching.md +++ /dev/null @@ -1,64 +0,0 @@ -# appbuilder.core.components.matching package - -## Submodules - -## appbuilder.core.components.matching.component module - -### *class* appbuilder.core.components.matching.component.Matching(embedding_component: EmbeddingBaseComponent) - -基类:`MatchingBaseComponent` - -基于Embedding类型的文本表示模型,输入query和文本列表,对其进行排序或者相似度计算 - -### 示例 - -```python -import appbuilder -os.environ["APPBUILDER_TOKEN"] = '...' - -# 初始化所需要的组件 -embedding = appbuilder.Embedding() -matching = appbuilder.Matching(embedding) - -# 定义输入query和文本列表 -query = appbuilder.Message("你好") -contexts = appbuilder.Message(["世界", "你好"]) - -# 根据query,对文本列表做相似度排序 -contexts_matched = matching(query, contexts) -print(contexts_matched.content) -# ['你好', '世界'] -``` - -#### meta - -`MatchingArgs` 的别名 - -#### name *: str* *= 'Matching'* - -#### run(query: [Message](appbuilder.core.md#appbuilder.core.message.Message)[str] | str, contexts: [Message](appbuilder.core.md#appbuilder.core.message.Message)[List[str]] | List[str], return_score: bool = False) → [Message](appbuilder.core.md#appbuilder.core.message.Message)[List[str]] - -根据给定的查询和上下文,返回匹配的上下文列表。 - -* **参数:** - * **query** (*Union* *[*[*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**str* *]* *,* *str* *]*) -- 查询字符串或Message对象,包含查询字符串。 - * **contexts** (*Union* *[*[*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**List* *[**str* *]* *]* *,* *List* *[**str* *]* *]*) -- 上下文字符串列表或Message对象,包含上下文字符串列表。 - * **return_score** (*bool* *,* *optional*) -- 是否返回匹配得分。默认为False。 -* **返回:** - 匹配的上下文列表。如果return_score为True,则返回包含得分和上下文的元组列表;否则仅返回上下文列表。 -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message)[List[str]] - -#### semantics(query_embedding: [Message](appbuilder.core.md#appbuilder.core.message.Message)[List[float]] | List[float], context_embeddings: [Message](appbuilder.core.md#appbuilder.core.message.Message)[List[List[float]]] | List[List[float]]) → [Message](appbuilder.core.md#appbuilder.core.message.Message)[List[float]] - -计算query和context的相似度 - -* **参数:** - * **query_embedding** (*Union* *[*[*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**List* *[**float* *]* *]* *,* *List* *[**float* *]* *]*) -- query的embedding,长度为n的数组 - * **context_embeddings** (*Union* *[*[*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**List* *[**List* *[**float* *]* *]* *]* *,* *List* *[**List* *[**float* *]* *]* *]*) -- context的embedding,长度为m x n的矩阵,其中m表示候选context的数量 -* **返回:** - query和所有候选context的相似度列表 -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message)[List[float]] - -#### version *: str* *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.md b/docs/API-Reference/Python/appbuilder.core.components.md deleted file mode 100644 index 04801185b..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.md +++ /dev/null @@ -1,396 +0,0 @@ -# appbuilder.core.components package - -## Subpackages - -* [appbuilder.core.components.animal_recognize package](appbuilder.core.components.animal_recognize.md) - * [Submodules](appbuilder.core.components.animal_recognize.md#submodules) - * [appbuilder.core.components.animal_recognize.component module](appbuilder.core.components.animal_recognize.md#module-appbuilder.core.components.animal_recognize.component) - * [`AnimalRecognition`](appbuilder.core.components.animal_recognize.md#appbuilder.core.components.animal_recognize.component.AnimalRecognition) - * [`AnimalRecognition.manifests`](appbuilder.core.components.animal_recognize.md#appbuilder.core.components.animal_recognize.component.AnimalRecognition.manifests) - * [`AnimalRecognition.name`](appbuilder.core.components.animal_recognize.md#appbuilder.core.components.animal_recognize.component.AnimalRecognition.name) - * [`AnimalRecognition.run()`](appbuilder.core.components.animal_recognize.md#appbuilder.core.components.animal_recognize.component.AnimalRecognition.run) - * [`AnimalRecognition.tool_eval()`](appbuilder.core.components.animal_recognize.md#appbuilder.core.components.animal_recognize.component.AnimalRecognition.tool_eval) - * [`AnimalRecognition.version`](appbuilder.core.components.animal_recognize.md#appbuilder.core.components.animal_recognize.component.AnimalRecognition.version) -* [appbuilder.core.components.asr package](appbuilder.core.components.asr.md) - * [Submodules](appbuilder.core.components.asr.md#submodules) - * [appbuilder.core.components.asr.component module](appbuilder.core.components.asr.md#module-appbuilder.core.components.asr.component) - * [`ASR`](appbuilder.core.components.asr.md#appbuilder.core.components.asr.component.ASR) - * [`ASR.manifests`](appbuilder.core.components.asr.md#appbuilder.core.components.asr.component.ASR.manifests) - * [`ASR.name`](appbuilder.core.components.asr.md#appbuilder.core.components.asr.component.ASR.name) - * [`ASR.run()`](appbuilder.core.components.asr.md#appbuilder.core.components.asr.component.ASR.run) - * [`ASR.tool_eval()`](appbuilder.core.components.asr.md#appbuilder.core.components.asr.component.ASR.tool_eval) - * [`ASR.version`](appbuilder.core.components.asr.md#appbuilder.core.components.asr.component.ASR.version) -* [appbuilder.core.components.dish_recognize package](appbuilder.core.components.dish_recognize.md) - * [Submodules](appbuilder.core.components.dish_recognize.md#submodules) - * [appbuilder.core.components.dish_recognize.component module](appbuilder.core.components.dish_recognize.md#module-appbuilder.core.components.dish_recognize.component) - * [`DishRecognition`](appbuilder.core.components.dish_recognize.md#appbuilder.core.components.dish_recognize.component.DishRecognition) - * [`DishRecognition.run()`](appbuilder.core.components.dish_recognize.md#appbuilder.core.components.dish_recognize.component.DishRecognition.run) -* [appbuilder.core.components.doc_crop_enhance package](appbuilder.core.components.doc_crop_enhance.md) - * [Submodules](appbuilder.core.components.doc_crop_enhance.md#submodules) - * [appbuilder.core.components.doc_crop_enhance.component module](appbuilder.core.components.doc_crop_enhance.md#module-appbuilder.core.components.doc_crop_enhance.component) - * [`DocCropEnhance`](appbuilder.core.components.doc_crop_enhance.md#appbuilder.core.components.doc_crop_enhance.component.DocCropEnhance) - * [`DocCropEnhance.run()`](appbuilder.core.components.doc_crop_enhance.md#appbuilder.core.components.doc_crop_enhance.component.DocCropEnhance.run) -* [appbuilder.core.components.doc_format_converter package](appbuilder.core.components.doc_format_converter.md) - * [Submodules](appbuilder.core.components.doc_format_converter.md#submodules) - * [appbuilder.core.components.doc_format_converter.component module](appbuilder.core.components.doc_format_converter.md#module-appbuilder.core.components.doc_format_converter.component) - * [`DocFormatConverter`](appbuilder.core.components.doc_format_converter.md#appbuilder.core.components.doc_format_converter.component.DocFormatConverter) - * [`DocFormatConverter.manifests`](appbuilder.core.components.doc_format_converter.md#appbuilder.core.components.doc_format_converter.component.DocFormatConverter.manifests) - * [`DocFormatConverter.name`](appbuilder.core.components.doc_format_converter.md#appbuilder.core.components.doc_format_converter.component.DocFormatConverter.name) - * [`DocFormatConverter.queryDocFormatConverterTask()`](appbuilder.core.components.doc_format_converter.md#appbuilder.core.components.doc_format_converter.component.DocFormatConverter.queryDocFormatConverterTask) - * [`DocFormatConverter.run()`](appbuilder.core.components.doc_format_converter.md#appbuilder.core.components.doc_format_converter.component.DocFormatConverter.run) - * [`DocFormatConverter.submitDocFormatConverterTask()`](appbuilder.core.components.doc_format_converter.md#appbuilder.core.components.doc_format_converter.component.DocFormatConverter.submitDocFormatConverterTask) - * [`DocFormatConverter.tool_eval()`](appbuilder.core.components.doc_format_converter.md#appbuilder.core.components.doc_format_converter.component.DocFormatConverter.tool_eval) - * [`DocFormatConverter.version`](appbuilder.core.components.doc_format_converter.md#appbuilder.core.components.doc_format_converter.component.DocFormatConverter.version) -* [appbuilder.core.components.doc_parser package](appbuilder.core.components.doc_parser.md) - * [Submodules](appbuilder.core.components.doc_parser.md#submodules) - * [appbuilder.core.components.doc_parser.doc_parser module](appbuilder.core.components.doc_parser.md#module-appbuilder.core.components.doc_parser.doc_parser) - * [`DocParser`](appbuilder.core.components.doc_parser.md#appbuilder.core.components.doc_parser.doc_parser.DocParser) - * [`DocParser.base_url`](appbuilder.core.components.doc_parser.md#appbuilder.core.components.doc_parser.doc_parser.DocParser.base_url) - * [`DocParser.config`](appbuilder.core.components.doc_parser.md#appbuilder.core.components.doc_parser.doc_parser.DocParser.config) - * [`DocParser.make_parse_result()`](appbuilder.core.components.doc_parser.md#appbuilder.core.components.doc_parser.doc_parser.DocParser.make_parse_result) - * [`DocParser.name`](appbuilder.core.components.doc_parser.md#appbuilder.core.components.doc_parser.doc_parser.DocParser.name) - * [`DocParser.run()`](appbuilder.core.components.doc_parser.md#appbuilder.core.components.doc_parser.doc_parser.DocParser.run) - * [`DocParser.set_config()`](appbuilder.core.components.doc_parser.md#appbuilder.core.components.doc_parser.doc_parser.DocParser.set_config) - * [`DocParser.tool_desc`](appbuilder.core.components.doc_parser.md#appbuilder.core.components.doc_parser.doc_parser.DocParser.tool_desc) -* [appbuilder.core.components.doc_splitter package](appbuilder.core.components.doc_splitter.md) - * [Submodules](appbuilder.core.components.doc_splitter.md#submodules) - * [appbuilder.core.components.doc_splitter.doc_splitter module](appbuilder.core.components.doc_splitter.md#module-appbuilder.core.components.doc_splitter.doc_splitter) - * [`ChunkSplitter`](appbuilder.core.components.doc_splitter.md#appbuilder.core.components.doc_splitter.doc_splitter.ChunkSplitter) - * [`ChunkSplitter.meta`](appbuilder.core.components.doc_splitter.md#appbuilder.core.components.doc_splitter.doc_splitter.ChunkSplitter.meta) - * [`ChunkSplitter.name`](appbuilder.core.components.doc_splitter.md#appbuilder.core.components.doc_splitter.doc_splitter.ChunkSplitter.name) - * [`ChunkSplitter.run()`](appbuilder.core.components.doc_splitter.md#appbuilder.core.components.doc_splitter.doc_splitter.ChunkSplitter.run) - * [`DocSplitter`](appbuilder.core.components.doc_splitter.md#appbuilder.core.components.doc_splitter.doc_splitter.DocSplitter) - * [`DocSplitter.name`](appbuilder.core.components.doc_splitter.md#appbuilder.core.components.doc_splitter.doc_splitter.DocSplitter.name) - * [`DocSplitter.meta`](appbuilder.core.components.doc_splitter.md#appbuilder.core.components.doc_splitter.doc_splitter.DocSplitter.meta) - * [`DocSplitter.meta`](appbuilder.core.components.doc_splitter.md#id0) - * [`DocSplitter.name`](appbuilder.core.components.doc_splitter.md#id1) - * [`DocSplitter.run()`](appbuilder.core.components.doc_splitter.md#appbuilder.core.components.doc_splitter.doc_splitter.DocSplitter.run) - * [`TitleSplitter`](appbuilder.core.components.doc_splitter.md#appbuilder.core.components.doc_splitter.doc_splitter.TitleSplitter) - * [`TitleSplitter.name`](appbuilder.core.components.doc_splitter.md#appbuilder.core.components.doc_splitter.doc_splitter.TitleSplitter.name) - * [`TitleSplitter.run()`](appbuilder.core.components.doc_splitter.md#appbuilder.core.components.doc_splitter.doc_splitter.TitleSplitter.run) - * [`TitleSplitter.tool_desc`](appbuilder.core.components.doc_splitter.md#appbuilder.core.components.doc_splitter.doc_splitter.TitleSplitter.tool_desc) -* [appbuilder.core.components.document_understanding package](appbuilder.core.components.document_understanding.md) - * [Submodules](appbuilder.core.components.document_understanding.md#submodules) - * [appbuilder.core.components.document_understanding.component module](appbuilder.core.components.document_understanding.md#module-appbuilder.core.components.document_understanding.component) - * [`DocumentUnderstanding`](appbuilder.core.components.document_understanding.md#appbuilder.core.components.document_understanding.component.DocumentUnderstanding) - * [`DocumentUnderstanding.get_addition_instruction()`](appbuilder.core.components.document_understanding.md#appbuilder.core.components.document_understanding.component.DocumentUnderstanding.get_addition_instruction) - * [`DocumentUnderstanding.get_conversation_id()`](appbuilder.core.components.document_understanding.md#appbuilder.core.components.document_understanding.component.DocumentUnderstanding.get_conversation_id) - * [`DocumentUnderstanding.get_file_id()`](appbuilder.core.components.document_understanding.md#appbuilder.core.components.document_understanding.component.DocumentUnderstanding.get_file_id) - * [`DocumentUnderstanding.manifests`](appbuilder.core.components.document_understanding.md#appbuilder.core.components.document_understanding.component.DocumentUnderstanding.manifests) - * [`DocumentUnderstanding.meta`](appbuilder.core.components.document_understanding.md#appbuilder.core.components.document_understanding.component.DocumentUnderstanding.meta) - * [`DocumentUnderstanding.name`](appbuilder.core.components.document_understanding.md#appbuilder.core.components.document_understanding.component.DocumentUnderstanding.name) - * [`DocumentUnderstanding.run()`](appbuilder.core.components.document_understanding.md#appbuilder.core.components.document_understanding.component.DocumentUnderstanding.run) - * [`DocumentUnderstanding.tool_eval()`](appbuilder.core.components.document_understanding.md#appbuilder.core.components.document_understanding.component.DocumentUnderstanding.tool_eval) - * [`DocumentUnderstanding.version`](appbuilder.core.components.document_understanding.md#appbuilder.core.components.document_understanding.component.DocumentUnderstanding.version) -* [appbuilder.core.components.embeddings package](appbuilder.core.components.embeddings.md) - * [Submodules](appbuilder.core.components.embeddings.md#submodules) - * [appbuilder.core.components.embeddings.component module](appbuilder.core.components.embeddings.md#module-appbuilder.core.components.embeddings.component) - * [`Embedding`](appbuilder.core.components.embeddings.md#appbuilder.core.components.embeddings.component.Embedding) - * [`Embedding.model`](appbuilder.core.components.embeddings.md#appbuilder.core.components.embeddings.component.Embedding.model) - * [`Embedding.accepted_models`](appbuilder.core.components.embeddings.md#appbuilder.core.components.embeddings.component.Embedding.accepted_models) - * [`Embedding.base_urls`](appbuilder.core.components.embeddings.md#appbuilder.core.components.embeddings.component.Embedding.base_urls) - * [`Embedding.batch()`](appbuilder.core.components.embeddings.md#appbuilder.core.components.embeddings.component.Embedding.batch) - * [`Embedding.meta`](appbuilder.core.components.embeddings.md#appbuilder.core.components.embeddings.component.Embedding.meta) - * [`Embedding.name`](appbuilder.core.components.embeddings.md#appbuilder.core.components.embeddings.component.Embedding.name) - * [`Embedding.run()`](appbuilder.core.components.embeddings.md#appbuilder.core.components.embeddings.component.Embedding.run) - * [`Embedding.version`](appbuilder.core.components.embeddings.md#appbuilder.core.components.embeddings.component.Embedding.version) -* [appbuilder.core.components.excel2figure package](appbuilder.core.components.excel2figure.md) - * [Submodules](appbuilder.core.components.excel2figure.md#submodules) - * [appbuilder.core.components.excel2figure.component module](appbuilder.core.components.excel2figure.md#module-appbuilder.core.components.excel2figure.component) - * [`Excel2Figure`](appbuilder.core.components.excel2figure.md#appbuilder.core.components.excel2figure.component.Excel2Figure) - * [`Excel2Figure.excluded_models`](appbuilder.core.components.excel2figure.md#appbuilder.core.components.excel2figure.component.Excel2Figure.excluded_models) - * [`Excel2Figure.manifests`](appbuilder.core.components.excel2figure.md#appbuilder.core.components.excel2figure.component.Excel2Figure.manifests) - * [`Excel2Figure.meta`](appbuilder.core.components.excel2figure.md#appbuilder.core.components.excel2figure.component.Excel2Figure.meta) - * [`Excel2Figure.model_info`](appbuilder.core.components.excel2figure.md#appbuilder.core.components.excel2figure.component.Excel2Figure.model_info) - * [`Excel2Figure.model_type`](appbuilder.core.components.excel2figure.md#appbuilder.core.components.excel2figure.component.Excel2Figure.model_type) - * [`Excel2Figure.run()`](appbuilder.core.components.excel2figure.md#appbuilder.core.components.excel2figure.component.Excel2Figure.run) - * [`Excel2Figure.set_secret_key_and_gateway()`](appbuilder.core.components.excel2figure.md#appbuilder.core.components.excel2figure.component.Excel2Figure.set_secret_key_and_gateway) - * [`Excel2Figure.tool_eval()`](appbuilder.core.components.excel2figure.md#appbuilder.core.components.excel2figure.component.Excel2Figure.tool_eval) -* [appbuilder.core.components.extract_table package](appbuilder.core.components.extract_table.md) - * [Submodules](appbuilder.core.components.extract_table.md#submodules) - * [appbuilder.core.components.extract_table.component module](appbuilder.core.components.extract_table.md#module-appbuilder.core.components.extract_table.component) - * [`ExtractTableFromDoc`](appbuilder.core.components.extract_table.md#appbuilder.core.components.extract_table.component.ExtractTableFromDoc) - * [`ExtractTableFromDoc.base_url`](appbuilder.core.components.extract_table.md#appbuilder.core.components.extract_table.component.ExtractTableFromDoc.base_url) - * [`ExtractTableFromDoc.meta`](appbuilder.core.components.extract_table.md#appbuilder.core.components.extract_table.component.ExtractTableFromDoc.meta) - * [`ExtractTableFromDoc.name`](appbuilder.core.components.extract_table.md#appbuilder.core.components.extract_table.component.ExtractTableFromDoc.name) - * [`ExtractTableFromDoc.run()`](appbuilder.core.components.extract_table.md#appbuilder.core.components.extract_table.component.ExtractTableFromDoc.run) -* [appbuilder.core.components.gbi package](appbuilder.core.components.gbi.md) - * [Subpackages](appbuilder.core.components.gbi.md#subpackages) - * [appbuilder.core.components.gbi.nl2sql package](appbuilder.core.components.gbi.nl2sql.md) - * [Submodules](appbuilder.core.components.gbi.nl2sql.md#submodules) - * [appbuilder.core.components.gbi.nl2sql.component module](appbuilder.core.components.gbi.nl2sql.md#module-appbuilder.core.components.gbi.nl2sql.component) - * [appbuilder.core.components.gbi.select_table package](appbuilder.core.components.gbi.select_table.md) - * [Submodules](appbuilder.core.components.gbi.select_table.md#submodules) - * [appbuilder.core.components.gbi.select_table.component module](appbuilder.core.components.gbi.select_table.md#module-appbuilder.core.components.gbi.select_table.component) - * [Submodules](appbuilder.core.components.gbi.md#submodules) -* [appbuilder.core.components.general_ocr package](appbuilder.core.components.general_ocr.md) - * [Submodules](appbuilder.core.components.general_ocr.md#submodules) - * [appbuilder.core.components.general_ocr.component module](appbuilder.core.components.general_ocr.md#module-appbuilder.core.components.general_ocr.component) - * [`GeneralOCR`](appbuilder.core.components.general_ocr.md#appbuilder.core.components.general_ocr.component.GeneralOCR) - * [`GeneralOCR.manifests`](appbuilder.core.components.general_ocr.md#appbuilder.core.components.general_ocr.component.GeneralOCR.manifests) - * [`GeneralOCR.name`](appbuilder.core.components.general_ocr.md#appbuilder.core.components.general_ocr.component.GeneralOCR.name) - * [`GeneralOCR.run()`](appbuilder.core.components.general_ocr.md#appbuilder.core.components.general_ocr.component.GeneralOCR.run) - * [`GeneralOCR.tool_eval()`](appbuilder.core.components.general_ocr.md#appbuilder.core.components.general_ocr.component.GeneralOCR.tool_eval) - * [`GeneralOCR.version`](appbuilder.core.components.general_ocr.md#appbuilder.core.components.general_ocr.component.GeneralOCR.version) -* [appbuilder.core.components.handwrite_ocr package](appbuilder.core.components.handwrite_ocr.md) - * [Submodules](appbuilder.core.components.handwrite_ocr.md#submodules) - * [appbuilder.core.components.handwrite_ocr.component module](appbuilder.core.components.handwrite_ocr.md#module-appbuilder.core.components.handwrite_ocr.component) - * [`HandwriteOCR`](appbuilder.core.components.handwrite_ocr.md#appbuilder.core.components.handwrite_ocr.component.HandwriteOCR) - * [`HandwriteOCR.manifests`](appbuilder.core.components.handwrite_ocr.md#appbuilder.core.components.handwrite_ocr.component.HandwriteOCR.manifests) - * [`HandwriteOCR.name`](appbuilder.core.components.handwrite_ocr.md#appbuilder.core.components.handwrite_ocr.component.HandwriteOCR.name) - * [`HandwriteOCR.run()`](appbuilder.core.components.handwrite_ocr.md#appbuilder.core.components.handwrite_ocr.component.HandwriteOCR.run) - * [`HandwriteOCR.tool_eval()`](appbuilder.core.components.handwrite_ocr.md#appbuilder.core.components.handwrite_ocr.component.HandwriteOCR.tool_eval) - * [`HandwriteOCR.version`](appbuilder.core.components.handwrite_ocr.md#appbuilder.core.components.handwrite_ocr.component.HandwriteOCR.version) -* [appbuilder.core.components.image_understand package](appbuilder.core.components.image_understand.md) - * [Submodules](appbuilder.core.components.image_understand.md#submodules) - * [appbuilder.core.components.image_understand.component module](appbuilder.core.components.image_understand.md#module-appbuilder.core.components.image_understand.component) - * [`ImageUnderstand`](appbuilder.core.components.image_understand.md#appbuilder.core.components.image_understand.component.ImageUnderstand) - * [`ImageUnderstand.manifests`](appbuilder.core.components.image_understand.md#appbuilder.core.components.image_understand.component.ImageUnderstand.manifests) - * [`ImageUnderstand.name`](appbuilder.core.components.image_understand.md#appbuilder.core.components.image_understand.component.ImageUnderstand.name) - * [`ImageUnderstand.run()`](appbuilder.core.components.image_understand.md#appbuilder.core.components.image_understand.component.ImageUnderstand.run) - * [`ImageUnderstand.tool_eval()`](appbuilder.core.components.image_understand.md#appbuilder.core.components.image_understand.component.ImageUnderstand.tool_eval) - * [`ImageUnderstand.version`](appbuilder.core.components.image_understand.md#appbuilder.core.components.image_understand.component.ImageUnderstand.version) -* [appbuilder.core.components.landmark_recognize package](appbuilder.core.components.landmark_recognize.md) - * [Submodules](appbuilder.core.components.landmark_recognize.md#submodules) - * [appbuilder.core.components.landmark_recognize.component module](appbuilder.core.components.landmark_recognize.md#module-appbuilder.core.components.landmark_recognize.component) - * [`LandmarkRecognition`](appbuilder.core.components.landmark_recognize.md#appbuilder.core.components.landmark_recognize.component.LandmarkRecognition) - * [`LandmarkRecognition.run()`](appbuilder.core.components.landmark_recognize.md#appbuilder.core.components.landmark_recognize.component.LandmarkRecognition.run) -* [appbuilder.core.components.llms package](appbuilder.core.components.llms.md) - * [Subpackages](appbuilder.core.components.llms.md#subpackages) - * [appbuilder.core.components.llms.dialog_summary package](appbuilder.core.components.llms.dialog_summary.md) - * [Submodules](appbuilder.core.components.llms.dialog_summary.md#submodules) - * [appbuilder.core.components.llms.dialog_summary.component module](appbuilder.core.components.llms.dialog_summary.md#module-appbuilder.core.components.llms.dialog_summary.component) - * [appbuilder.core.components.llms.hallucination_detection package](appbuilder.core.components.llms.hallucination_detection.md) - * [Submodules](appbuilder.core.components.llms.hallucination_detection.md#submodules) - * [appbuilder.core.components.llms.hallucination_detection.component module](appbuilder.core.components.llms.hallucination_detection.md#module-appbuilder.core.components.llms.hallucination_detection.component) - * [appbuilder.core.components.llms.is_complex_query package](appbuilder.core.components.llms.is_complex_query.md) - * [Submodules](appbuilder.core.components.llms.is_complex_query.md#submodules) - * [appbuilder.core.components.llms.is_complex_query.component module](appbuilder.core.components.llms.is_complex_query.md#module-appbuilder.core.components.llms.is_complex_query.component) - * [appbuilder.core.components.llms.mrc package](appbuilder.core.components.llms.mrc.md) - * [Submodules](appbuilder.core.components.llms.mrc.md#submodules) - * [appbuilder.core.components.llms.mrc.component module](appbuilder.core.components.llms.mrc.md#module-appbuilder.core.components.llms.mrc.component) - * [appbuilder.core.components.llms.nl2pandas package](appbuilder.core.components.llms.nl2pandas.md) - * [Submodules](appbuilder.core.components.llms.nl2pandas.md#submodules) - * [appbuilder.core.components.llms.nl2pandas.component module](appbuilder.core.components.llms.nl2pandas.md#module-appbuilder.core.components.llms.nl2pandas.component) - * [appbuilder.core.components.llms.oral_query_generation package](appbuilder.core.components.llms.oral_query_generation.md) - * [Submodules](appbuilder.core.components.llms.oral_query_generation.md#submodules) - * [appbuilder.core.components.llms.oral_query_generation.component module](appbuilder.core.components.llms.oral_query_generation.md#module-appbuilder.core.components.llms.oral_query_generation.component) - * [appbuilder.core.components.llms.playground package](appbuilder.core.components.llms.playground.md) - * [Submodules](appbuilder.core.components.llms.playground.md#submodules) - * [appbuilder.core.components.llms.playground.component module](appbuilder.core.components.llms.playground.md#module-appbuilder.core.components.llms.playground.component) - * [appbuilder.core.components.llms.qa_pair_mining package](appbuilder.core.components.llms.qa_pair_mining.md) - * [Submodules](appbuilder.core.components.llms.qa_pair_mining.md#submodules) - * [appbuilder.core.components.llms.qa_pair_mining.component module](appbuilder.core.components.llms.qa_pair_mining.md#module-appbuilder.core.components.llms.qa_pair_mining.component) - * [appbuilder.core.components.llms.query_decomposition package](appbuilder.core.components.llms.query_decomposition.md) - * [Submodules](appbuilder.core.components.llms.query_decomposition.md#submodules) - * [appbuilder.core.components.llms.query_decomposition.component module](appbuilder.core.components.llms.query_decomposition.md#module-appbuilder.core.components.llms.query_decomposition.component) - * [appbuilder.core.components.llms.query_rewrite package](appbuilder.core.components.llms.query_rewrite.md) - * [Submodules](appbuilder.core.components.llms.query_rewrite.md#submodules) - * [appbuilder.core.components.llms.query_rewrite.component module](appbuilder.core.components.llms.query_rewrite.md#module-appbuilder.core.components.llms.query_rewrite.component) - * [appbuilder.core.components.llms.similar_question package](appbuilder.core.components.llms.similar_question.md) - * [Submodules](appbuilder.core.components.llms.similar_question.md#submodules) - * [appbuilder.core.components.llms.similar_question.component module](appbuilder.core.components.llms.similar_question.md#module-appbuilder.core.components.llms.similar_question.component) - * [appbuilder.core.components.llms.style_rewrite package](appbuilder.core.components.llms.style_rewrite.md) - * [Submodules](appbuilder.core.components.llms.style_rewrite.md#submodules) - * [appbuilder.core.components.llms.style_rewrite.component module](appbuilder.core.components.llms.style_rewrite.md#module-appbuilder.core.components.llms.style_rewrite.component) - * [appbuilder.core.components.llms.style_writing package](appbuilder.core.components.llms.style_writing.md) - * [Submodules](appbuilder.core.components.llms.style_writing.md#submodules) - * [appbuilder.core.components.llms.style_writing.component module](appbuilder.core.components.llms.style_writing.md#module-appbuilder.core.components.llms.style_writing.component) - * [appbuilder.core.components.llms.tag_extraction package](appbuilder.core.components.llms.tag_extraction.md) - * [Submodules](appbuilder.core.components.llms.tag_extraction.md#submodules) - * [appbuilder.core.components.llms.tag_extraction.component module](appbuilder.core.components.llms.tag_extraction.md#module-appbuilder.core.components.llms.tag_extraction.component) - * [Submodules](appbuilder.core.components.llms.md#submodules) -* [appbuilder.core.components.matching package](appbuilder.core.components.matching.md) - * [Submodules](appbuilder.core.components.matching.md#submodules) - * [appbuilder.core.components.matching.component module](appbuilder.core.components.matching.md#module-appbuilder.core.components.matching.component) - * [`Matching`](appbuilder.core.components.matching.md#appbuilder.core.components.matching.component.Matching) - * [`Matching.meta`](appbuilder.core.components.matching.md#appbuilder.core.components.matching.component.Matching.meta) - * [`Matching.name`](appbuilder.core.components.matching.md#appbuilder.core.components.matching.component.Matching.name) - * [`Matching.run()`](appbuilder.core.components.matching.md#appbuilder.core.components.matching.component.Matching.run) - * [`Matching.semantics()`](appbuilder.core.components.matching.md#appbuilder.core.components.matching.component.Matching.semantics) - * [`Matching.version`](appbuilder.core.components.matching.md#appbuilder.core.components.matching.component.Matching.version) -* [appbuilder.core.components.mix_card_ocr package](appbuilder.core.components.mix_card_ocr.md) - * [Submodules](appbuilder.core.components.mix_card_ocr.md#submodules) - * [appbuilder.core.components.mix_card_ocr.component module](appbuilder.core.components.mix_card_ocr.md#module-appbuilder.core.components.mix_card_ocr.component) - * [`MixCardOCR`](appbuilder.core.components.mix_card_ocr.md#appbuilder.core.components.mix_card_ocr.component.MixCardOCR) - * [`MixCardOCR.manifests`](appbuilder.core.components.mix_card_ocr.md#appbuilder.core.components.mix_card_ocr.component.MixCardOCR.manifests) - * [`MixCardOCR.name`](appbuilder.core.components.mix_card_ocr.md#appbuilder.core.components.mix_card_ocr.component.MixCardOCR.name) - * [`MixCardOCR.run()`](appbuilder.core.components.mix_card_ocr.md#appbuilder.core.components.mix_card_ocr.component.MixCardOCR.run) - * [`MixCardOCR.tool_eval()`](appbuilder.core.components.mix_card_ocr.md#appbuilder.core.components.mix_card_ocr.component.MixCardOCR.tool_eval) - * [`MixCardOCR.version`](appbuilder.core.components.mix_card_ocr.md#appbuilder.core.components.mix_card_ocr.component.MixCardOCR.version) -* [appbuilder.core.components.object_recognize package](appbuilder.core.components.object_recognize.md) - * [Submodules](appbuilder.core.components.object_recognize.md#submodules) - * [appbuilder.core.components.object_recognize.component module](appbuilder.core.components.object_recognize.md#module-appbuilder.core.components.object_recognize.component) - * [`ObjectRecognition`](appbuilder.core.components.object_recognize.md#appbuilder.core.components.object_recognize.component.ObjectRecognition) - * [`ObjectRecognition.manifests`](appbuilder.core.components.object_recognize.md#appbuilder.core.components.object_recognize.component.ObjectRecognition.manifests) - * [`ObjectRecognition.name`](appbuilder.core.components.object_recognize.md#appbuilder.core.components.object_recognize.component.ObjectRecognition.name) - * [`ObjectRecognition.run()`](appbuilder.core.components.object_recognize.md#appbuilder.core.components.object_recognize.component.ObjectRecognition.run) - * [`ObjectRecognition.tool_eval()`](appbuilder.core.components.object_recognize.md#appbuilder.core.components.object_recognize.component.ObjectRecognition.tool_eval) - * [`ObjectRecognition.version`](appbuilder.core.components.object_recognize.md#appbuilder.core.components.object_recognize.component.ObjectRecognition.version) -* [appbuilder.core.components.plant_recognize package](appbuilder.core.components.plant_recognize.md) - * [Submodules](appbuilder.core.components.plant_recognize.md#submodules) - * [appbuilder.core.components.plant_recognize.component module](appbuilder.core.components.plant_recognize.md#module-appbuilder.core.components.plant_recognize.component) - * [`PlantRecognition`](appbuilder.core.components.plant_recognize.md#appbuilder.core.components.plant_recognize.component.PlantRecognition) - * [`PlantRecognition.manifests`](appbuilder.core.components.plant_recognize.md#appbuilder.core.components.plant_recognize.component.PlantRecognition.manifests) - * [`PlantRecognition.name`](appbuilder.core.components.plant_recognize.md#appbuilder.core.components.plant_recognize.component.PlantRecognition.name) - * [`PlantRecognition.run()`](appbuilder.core.components.plant_recognize.md#appbuilder.core.components.plant_recognize.component.PlantRecognition.run) - * [`PlantRecognition.tool_eval()`](appbuilder.core.components.plant_recognize.md#appbuilder.core.components.plant_recognize.component.PlantRecognition.tool_eval) - * [`PlantRecognition.version`](appbuilder.core.components.plant_recognize.md#appbuilder.core.components.plant_recognize.component.PlantRecognition.version) -* [appbuilder.core.components.ppt_generation_from_file package](appbuilder.core.components.ppt_generation_from_file.md) - * [Submodules](appbuilder.core.components.ppt_generation_from_file.md#submodules) - * [appbuilder.core.components.ppt_generation_from_file.component module](appbuilder.core.components.ppt_generation_from_file.md#module-appbuilder.core.components.ppt_generation_from_file.component) - * [`PPTGenerationFromFile`](appbuilder.core.components.ppt_generation_from_file.md#appbuilder.core.components.ppt_generation_from_file.component.PPTGenerationFromFile) - * [`PPTGenerationFromFile.get_ppt_download_link()`](appbuilder.core.components.ppt_generation_from_file.md#appbuilder.core.components.ppt_generation_from_file.component.PPTGenerationFromFile.get_ppt_download_link) - * [`PPTGenerationFromFile.get_ppt_download_link_url`](appbuilder.core.components.ppt_generation_from_file.md#appbuilder.core.components.ppt_generation_from_file.component.PPTGenerationFromFile.get_ppt_download_link_url) - * [`PPTGenerationFromFile.get_ppt_generation_status()`](appbuilder.core.components.ppt_generation_from_file.md#appbuilder.core.components.ppt_generation_from_file.component.PPTGenerationFromFile.get_ppt_generation_status) - * [`PPTGenerationFromFile.get_ppt_generation_status_url`](appbuilder.core.components.ppt_generation_from_file.md#appbuilder.core.components.ppt_generation_from_file.component.PPTGenerationFromFile.get_ppt_generation_status_url) - * [`PPTGenerationFromFile.manifests`](appbuilder.core.components.ppt_generation_from_file.md#appbuilder.core.components.ppt_generation_from_file.component.PPTGenerationFromFile.manifests) - * [`PPTGenerationFromFile.meta`](appbuilder.core.components.ppt_generation_from_file.md#appbuilder.core.components.ppt_generation_from_file.component.PPTGenerationFromFile.meta) - * [`PPTGenerationFromFile.name`](appbuilder.core.components.ppt_generation_from_file.md#appbuilder.core.components.ppt_generation_from_file.component.PPTGenerationFromFile.name) - * [`PPTGenerationFromFile.ppt_generation()`](appbuilder.core.components.ppt_generation_from_file.md#appbuilder.core.components.ppt_generation_from_file.component.PPTGenerationFromFile.ppt_generation) - * [`PPTGenerationFromFile.ppt_generation_url`](appbuilder.core.components.ppt_generation_from_file.md#appbuilder.core.components.ppt_generation_from_file.component.PPTGenerationFromFile.ppt_generation_url) - * [`PPTGenerationFromFile.run()`](appbuilder.core.components.ppt_generation_from_file.md#appbuilder.core.components.ppt_generation_from_file.component.PPTGenerationFromFile.run) - * [`PPTGenerationFromFile.tool_eval()`](appbuilder.core.components.ppt_generation_from_file.md#appbuilder.core.components.ppt_generation_from_file.component.PPTGenerationFromFile.tool_eval) - * [`PPTGenerationFromFile.uniform_prefix`](appbuilder.core.components.ppt_generation_from_file.md#appbuilder.core.components.ppt_generation_from_file.component.PPTGenerationFromFile.uniform_prefix) - * [`PPTGenerationFromFile.version`](appbuilder.core.components.ppt_generation_from_file.md#appbuilder.core.components.ppt_generation_from_file.component.PPTGenerationFromFile.version) -* [appbuilder.core.components.ppt_generation_from_instruction package](appbuilder.core.components.ppt_generation_from_instruction.md) - * [Submodules](appbuilder.core.components.ppt_generation_from_instruction.md#submodules) - * [appbuilder.core.components.ppt_generation_from_instruction.component module](appbuilder.core.components.ppt_generation_from_instruction.md#module-appbuilder.core.components.ppt_generation_from_instruction.component) - * [`PPTGenerationFromInstruction`](appbuilder.core.components.ppt_generation_from_instruction.md#appbuilder.core.components.ppt_generation_from_instruction.component.PPTGenerationFromInstruction) - * [`PPTGenerationFromInstruction.get_ppt_download_link()`](appbuilder.core.components.ppt_generation_from_instruction.md#appbuilder.core.components.ppt_generation_from_instruction.component.PPTGenerationFromInstruction.get_ppt_download_link) - * [`PPTGenerationFromInstruction.get_ppt_download_link_url`](appbuilder.core.components.ppt_generation_from_instruction.md#appbuilder.core.components.ppt_generation_from_instruction.component.PPTGenerationFromInstruction.get_ppt_download_link_url) - * [`PPTGenerationFromInstruction.get_ppt_generation_status()`](appbuilder.core.components.ppt_generation_from_instruction.md#appbuilder.core.components.ppt_generation_from_instruction.component.PPTGenerationFromInstruction.get_ppt_generation_status) - * [`PPTGenerationFromInstruction.get_ppt_generation_status_url`](appbuilder.core.components.ppt_generation_from_instruction.md#appbuilder.core.components.ppt_generation_from_instruction.component.PPTGenerationFromInstruction.get_ppt_generation_status_url) - * [`PPTGenerationFromInstruction.manifests`](appbuilder.core.components.ppt_generation_from_instruction.md#appbuilder.core.components.ppt_generation_from_instruction.component.PPTGenerationFromInstruction.manifests) - * [`PPTGenerationFromInstruction.meta`](appbuilder.core.components.ppt_generation_from_instruction.md#appbuilder.core.components.ppt_generation_from_instruction.component.PPTGenerationFromInstruction.meta) - * [`PPTGenerationFromInstruction.name`](appbuilder.core.components.ppt_generation_from_instruction.md#appbuilder.core.components.ppt_generation_from_instruction.component.PPTGenerationFromInstruction.name) - * [`PPTGenerationFromInstruction.ppt_generation()`](appbuilder.core.components.ppt_generation_from_instruction.md#appbuilder.core.components.ppt_generation_from_instruction.component.PPTGenerationFromInstruction.ppt_generation) - * [`PPTGenerationFromInstruction.ppt_generation_url`](appbuilder.core.components.ppt_generation_from_instruction.md#appbuilder.core.components.ppt_generation_from_instruction.component.PPTGenerationFromInstruction.ppt_generation_url) - * [`PPTGenerationFromInstruction.run()`](appbuilder.core.components.ppt_generation_from_instruction.md#appbuilder.core.components.ppt_generation_from_instruction.component.PPTGenerationFromInstruction.run) - * [`PPTGenerationFromInstruction.tool_eval()`](appbuilder.core.components.ppt_generation_from_instruction.md#appbuilder.core.components.ppt_generation_from_instruction.component.PPTGenerationFromInstruction.tool_eval) - * [`PPTGenerationFromInstruction.uniform_prefix`](appbuilder.core.components.ppt_generation_from_instruction.md#appbuilder.core.components.ppt_generation_from_instruction.component.PPTGenerationFromInstruction.uniform_prefix) - * [`PPTGenerationFromInstruction.version`](appbuilder.core.components.ppt_generation_from_instruction.md#appbuilder.core.components.ppt_generation_from_instruction.component.PPTGenerationFromInstruction.version) -* [appbuilder.core.components.ppt_generation_from_paper package](appbuilder.core.components.ppt_generation_from_paper.md) - * [Submodules](appbuilder.core.components.ppt_generation_from_paper.md#submodules) - * [appbuilder.core.components.ppt_generation_from_paper.component module](appbuilder.core.components.ppt_generation_from_paper.md#module-appbuilder.core.components.ppt_generation_from_paper.component) - * [`PPTGenerationFromPaper`](appbuilder.core.components.ppt_generation_from_paper.md#appbuilder.core.components.ppt_generation_from_paper.component.PPTGenerationFromPaper) - * [`PPTGenerationFromPaper.get_ppt_download_link()`](appbuilder.core.components.ppt_generation_from_paper.md#appbuilder.core.components.ppt_generation_from_paper.component.PPTGenerationFromPaper.get_ppt_download_link) - * [`PPTGenerationFromPaper.get_ppt_download_link_url`](appbuilder.core.components.ppt_generation_from_paper.md#appbuilder.core.components.ppt_generation_from_paper.component.PPTGenerationFromPaper.get_ppt_download_link_url) - * [`PPTGenerationFromPaper.get_ppt_generation_status()`](appbuilder.core.components.ppt_generation_from_paper.md#appbuilder.core.components.ppt_generation_from_paper.component.PPTGenerationFromPaper.get_ppt_generation_status) - * [`PPTGenerationFromPaper.get_ppt_generation_status_url`](appbuilder.core.components.ppt_generation_from_paper.md#appbuilder.core.components.ppt_generation_from_paper.component.PPTGenerationFromPaper.get_ppt_generation_status_url) - * [`PPTGenerationFromPaper.manifests`](appbuilder.core.components.ppt_generation_from_paper.md#appbuilder.core.components.ppt_generation_from_paper.component.PPTGenerationFromPaper.manifests) - * [`PPTGenerationFromPaper.meta`](appbuilder.core.components.ppt_generation_from_paper.md#appbuilder.core.components.ppt_generation_from_paper.component.PPTGenerationFromPaper.meta) - * [`PPTGenerationFromPaper.name`](appbuilder.core.components.ppt_generation_from_paper.md#appbuilder.core.components.ppt_generation_from_paper.component.PPTGenerationFromPaper.name) - * [`PPTGenerationFromPaper.ppt_generation()`](appbuilder.core.components.ppt_generation_from_paper.md#appbuilder.core.components.ppt_generation_from_paper.component.PPTGenerationFromPaper.ppt_generation) - * [`PPTGenerationFromPaper.ppt_generation_url`](appbuilder.core.components.ppt_generation_from_paper.md#appbuilder.core.components.ppt_generation_from_paper.component.PPTGenerationFromPaper.ppt_generation_url) - * [`PPTGenerationFromPaper.run()`](appbuilder.core.components.ppt_generation_from_paper.md#appbuilder.core.components.ppt_generation_from_paper.component.PPTGenerationFromPaper.run) - * [`PPTGenerationFromPaper.tool_eval()`](appbuilder.core.components.ppt_generation_from_paper.md#appbuilder.core.components.ppt_generation_from_paper.component.PPTGenerationFromPaper.tool_eval) - * [`PPTGenerationFromPaper.uniform_prefix`](appbuilder.core.components.ppt_generation_from_paper.md#appbuilder.core.components.ppt_generation_from_paper.component.PPTGenerationFromPaper.uniform_prefix) - * [`PPTGenerationFromPaper.version`](appbuilder.core.components.ppt_generation_from_paper.md#appbuilder.core.components.ppt_generation_from_paper.component.PPTGenerationFromPaper.version) -* [appbuilder.core.components.qrcode_ocr package](appbuilder.core.components.qrcode_ocr.md) - * [Submodules](appbuilder.core.components.qrcode_ocr.md#submodules) - * [appbuilder.core.components.qrcode_ocr.component module](appbuilder.core.components.qrcode_ocr.md#module-appbuilder.core.components.qrcode_ocr.component) - * [`QRcodeOCR`](appbuilder.core.components.qrcode_ocr.md#appbuilder.core.components.qrcode_ocr.component.QRcodeOCR) - * [`QRcodeOCR.manifests`](appbuilder.core.components.qrcode_ocr.md#appbuilder.core.components.qrcode_ocr.component.QRcodeOCR.manifests) - * [`QRcodeOCR.name`](appbuilder.core.components.qrcode_ocr.md#appbuilder.core.components.qrcode_ocr.component.QRcodeOCR.name) - * [`QRcodeOCR.run()`](appbuilder.core.components.qrcode_ocr.md#appbuilder.core.components.qrcode_ocr.component.QRcodeOCR.run) - * [`QRcodeOCR.tool_eval()`](appbuilder.core.components.qrcode_ocr.md#appbuilder.core.components.qrcode_ocr.component.QRcodeOCR.tool_eval) - * [`QRcodeOCR.version`](appbuilder.core.components.qrcode_ocr.md#appbuilder.core.components.qrcode_ocr.component.QRcodeOCR.version) -* [appbuilder.core.components.rag_with_baidu_search package](appbuilder.core.components.rag_with_baidu_search.md) - * [Submodules](appbuilder.core.components.rag_with_baidu_search.md#submodules) - * [appbuilder.core.components.rag_with_baidu_search.component module](appbuilder.core.components.rag_with_baidu_search.md#module-appbuilder.core.components.rag_with_baidu_search.component) - * [`RAGWithBaiduSearch`](appbuilder.core.components.rag_with_baidu_search.md#appbuilder.core.components.rag_with_baidu_search.component.RAGWithBaiduSearch) - * [`RAGWithBaiduSearch.meta`](appbuilder.core.components.rag_with_baidu_search.md#appbuilder.core.components.rag_with_baidu_search.component.RAGWithBaiduSearch.meta) - * [`RAGWithBaiduSearch.name`](appbuilder.core.components.rag_with_baidu_search.md#appbuilder.core.components.rag_with_baidu_search.component.RAGWithBaiduSearch.name) - * [`RAGWithBaiduSearch.run()`](appbuilder.core.components.rag_with_baidu_search.md#appbuilder.core.components.rag_with_baidu_search.component.RAGWithBaiduSearch.run) - * [`RAGWithBaiduSearch.version`](appbuilder.core.components.rag_with_baidu_search.md#appbuilder.core.components.rag_with_baidu_search.component.RAGWithBaiduSearch.version) -* [appbuilder.core.components.rag_with_baidu_search_pro package](appbuilder.core.components.rag_with_baidu_search_pro.md) - * [Submodules](appbuilder.core.components.rag_with_baidu_search_pro.md#submodules) - * [appbuilder.core.components.rag_with_baidu_search_pro.component module](appbuilder.core.components.rag_with_baidu_search_pro.md#module-appbuilder.core.components.rag_with_baidu_search_pro.component) - * [`RagWithBaiduSearchPro`](appbuilder.core.components.rag_with_baidu_search_pro.md#appbuilder.core.components.rag_with_baidu_search_pro.component.RagWithBaiduSearchPro) - * [`RagWithBaiduSearchPro.meta`](appbuilder.core.components.rag_with_baidu_search_pro.md#appbuilder.core.components.rag_with_baidu_search_pro.component.RagWithBaiduSearchPro.meta) - * [`RagWithBaiduSearchPro.name`](appbuilder.core.components.rag_with_baidu_search_pro.md#appbuilder.core.components.rag_with_baidu_search_pro.component.RagWithBaiduSearchPro.name) - * [`RagWithBaiduSearchPro.run()`](appbuilder.core.components.rag_with_baidu_search_pro.md#appbuilder.core.components.rag_with_baidu_search_pro.component.RagWithBaiduSearchPro.run) - * [`RagWithBaiduSearchPro.set_secret_key_and_gateway()`](appbuilder.core.components.rag_with_baidu_search_pro.md#appbuilder.core.components.rag_with_baidu_search_pro.component.RagWithBaiduSearchPro.set_secret_key_and_gateway) - * [`RagWithBaiduSearchPro.version`](appbuilder.core.components.rag_with_baidu_search_pro.md#appbuilder.core.components.rag_with_baidu_search_pro.component.RagWithBaiduSearchPro.version) - * [`RagWithBaiduSearchProArgs`](appbuilder.core.components.rag_with_baidu_search_pro.md#appbuilder.core.components.rag_with_baidu_search_pro.component.RagWithBaiduSearchProArgs) - * [`RagWithBaiduSearchProArgs.model_computed_fields`](appbuilder.core.components.rag_with_baidu_search_pro.md#appbuilder.core.components.rag_with_baidu_search_pro.component.RagWithBaiduSearchProArgs.model_computed_fields) - * [`RagWithBaiduSearchProArgs.model_config`](appbuilder.core.components.rag_with_baidu_search_pro.md#appbuilder.core.components.rag_with_baidu_search_pro.component.RagWithBaiduSearchProArgs.model_config) - * [`RagWithBaiduSearchProArgs.model_fields`](appbuilder.core.components.rag_with_baidu_search_pro.md#appbuilder.core.components.rag_with_baidu_search_pro.component.RagWithBaiduSearchProArgs.model_fields) - * [`RagWithBaiduSearchProArgs.query`](appbuilder.core.components.rag_with_baidu_search_pro.md#appbuilder.core.components.rag_with_baidu_search_pro.component.RagWithBaiduSearchProArgs.query) - * [`RagWithBaiduSearchProRequest`](appbuilder.core.components.rag_with_baidu_search_pro.md#appbuilder.core.components.rag_with_baidu_search_pro.component.RagWithBaiduSearchProRequest) - * [`RagWithBaiduSearchProRequest.message`](appbuilder.core.components.rag_with_baidu_search_pro.md#appbuilder.core.components.rag_with_baidu_search_pro.component.RagWithBaiduSearchProRequest.message) - * [`RagWithBaiduSearchProRequest.stream`](appbuilder.core.components.rag_with_baidu_search_pro.md#appbuilder.core.components.rag_with_baidu_search_pro.component.RagWithBaiduSearchProRequest.stream) - * [`RagWithBaiduSearchProRequest.instruction`](appbuilder.core.components.rag_with_baidu_search_pro.md#appbuilder.core.components.rag_with_baidu_search_pro.component.RagWithBaiduSearchProRequest.instruction) - * [`RagWithBaiduSearchProRequest.model`](appbuilder.core.components.rag_with_baidu_search_pro.md#appbuilder.core.components.rag_with_baidu_search_pro.component.RagWithBaiduSearchProRequest.model) - * [`RagWithBaiduSearchProRequest.temperature`](appbuilder.core.components.rag_with_baidu_search_pro.md#appbuilder.core.components.rag_with_baidu_search_pro.component.RagWithBaiduSearchProRequest.temperature) - * [`RagWithBaiduSearchProRequest.top_p`](appbuilder.core.components.rag_with_baidu_search_pro.md#appbuilder.core.components.rag_with_baidu_search_pro.component.RagWithBaiduSearchProRequest.top_p) - * [`RagWithBaiduSearchProRequest.search_top_k`](appbuilder.core.components.rag_with_baidu_search_pro.md#appbuilder.core.components.rag_with_baidu_search_pro.component.RagWithBaiduSearchProRequest.search_top_k) - * [`RagWithBaiduSearchProRequest.hide_corner_markers`](appbuilder.core.components.rag_with_baidu_search_pro.md#appbuilder.core.components.rag_with_baidu_search_pro.component.RagWithBaiduSearchProRequest.hide_corner_markers) - * [`RagWithBaiduSearchProRequest.instruction`](appbuilder.core.components.rag_with_baidu_search_pro.md#id0) - * [`RagWithBaiduSearchProRequest.message`](appbuilder.core.components.rag_with_baidu_search_pro.md#id1) - * [`RagWithBaiduSearchProRequest.model`](appbuilder.core.components.rag_with_baidu_search_pro.md#id2) - * [`RagWithBaiduSearchProRequest.model_computed_fields`](appbuilder.core.components.rag_with_baidu_search_pro.md#appbuilder.core.components.rag_with_baidu_search_pro.component.RagWithBaiduSearchProRequest.model_computed_fields) - * [`RagWithBaiduSearchProRequest.model_config`](appbuilder.core.components.rag_with_baidu_search_pro.md#appbuilder.core.components.rag_with_baidu_search_pro.component.RagWithBaiduSearchProRequest.model_config) - * [`RagWithBaiduSearchProRequest.model_fields`](appbuilder.core.components.rag_with_baidu_search_pro.md#appbuilder.core.components.rag_with_baidu_search_pro.component.RagWithBaiduSearchProRequest.model_fields) - * [`RagWithBaiduSearchProRequest.search_top_k`](appbuilder.core.components.rag_with_baidu_search_pro.md#id3) - * [`RagWithBaiduSearchProRequest.stream`](appbuilder.core.components.rag_with_baidu_search_pro.md#id4) - * [`RagWithBaiduSearchProRequest.temperature`](appbuilder.core.components.rag_with_baidu_search_pro.md#id5) - * [`RagWithBaiduSearchProRequest.top_p`](appbuilder.core.components.rag_with_baidu_search_pro.md#id6) - * [appbuilder.core.components.rag_with_baidu_search_pro.parse_rag_pro_response module](appbuilder.core.components.rag_with_baidu_search_pro.md#module-appbuilder.core.components.rag_with_baidu_search_pro) -* [appbuilder.core.components.retriever package](appbuilder.core.components.retriever.md) - * [Subpackages](appbuilder.core.components.retriever.md#subpackages) - * [appbuilder.core.components.retriever.baidu_vdb package](appbuilder.core.components.retriever.baidu_vdb.md) - * [Submodules](appbuilder.core.components.retriever.baidu_vdb.md#submodules) - * [appbuilder.core.components.retriever.baidu_vdb.baiduvdb_retriever module](appbuilder.core.components.retriever.baidu_vdb.md#module-appbuilder.core.components.retriever.baidu_vdb.baiduvdb_retriever) - * [appbuilder.core.components.retriever.bes package](appbuilder.core.components.retriever.bes.md) - * [Submodules](appbuilder.core.components.retriever.bes.md#submodules) - * [appbuilder.core.components.retriever.bes.bes_retriever module](appbuilder.core.components.retriever.bes.md#module-appbuilder.core.components.retriever.bes.bes_retriever) - * [appbuilder.core.components.retriever.reranker package](appbuilder.core.components.retriever.reranker.md) - * [Submodules](appbuilder.core.components.retriever.reranker.md#submodules) - * [appbuilder.core.components.retriever.reranker.rerank module](appbuilder.core.components.retriever.reranker.md#module-appbuilder.core.components.retriever.reranker.rerank) -* [appbuilder.core.components.table_ocr package](appbuilder.core.components.table_ocr.md) - * [Submodules](appbuilder.core.components.table_ocr.md#submodules) - * [appbuilder.core.components.table_ocr.component module](appbuilder.core.components.table_ocr.md#module-appbuilder.core.components.table_ocr.component) - * [`TableOCR`](appbuilder.core.components.table_ocr.md#appbuilder.core.components.table_ocr.component.TableOCR) - * [`TableOCR.get_table_markdown()`](appbuilder.core.components.table_ocr.md#appbuilder.core.components.table_ocr.component.TableOCR.get_table_markdown) - * [`TableOCR.manifests`](appbuilder.core.components.table_ocr.md#appbuilder.core.components.table_ocr.component.TableOCR.manifests) - * [`TableOCR.name`](appbuilder.core.components.table_ocr.md#appbuilder.core.components.table_ocr.component.TableOCR.name) - * [`TableOCR.run()`](appbuilder.core.components.table_ocr.md#appbuilder.core.components.table_ocr.component.TableOCR.run) - * [`TableOCR.tool_eval()`](appbuilder.core.components.table_ocr.md#appbuilder.core.components.table_ocr.component.TableOCR.tool_eval) - * [`TableOCR.version`](appbuilder.core.components.table_ocr.md#appbuilder.core.components.table_ocr.component.TableOCR.version) -* [appbuilder.core.components.text_to_image package](appbuilder.core.components.text_to_image.md) - * [Submodules](appbuilder.core.components.text_to_image.md#submodules) - * [appbuilder.core.components.text_to_image.component module](appbuilder.core.components.text_to_image.md#module-appbuilder.core.components.text_to_image.component) - * [`Text2Image`](appbuilder.core.components.text_to_image.md#appbuilder.core.components.text_to_image.component.Text2Image) - * [`Text2Image.check_service_error()`](appbuilder.core.components.text_to_image.md#appbuilder.core.components.text_to_image.component.Text2Image.check_service_error) - * [`Text2Image.extract_img_urls()`](appbuilder.core.components.text_to_image.md#appbuilder.core.components.text_to_image.component.Text2Image.extract_img_urls) - * [`Text2Image.manifests`](appbuilder.core.components.text_to_image.md#appbuilder.core.components.text_to_image.component.Text2Image.manifests) - * [`Text2Image.queryText2ImageData()`](appbuilder.core.components.text_to_image.md#appbuilder.core.components.text_to_image.component.Text2Image.queryText2ImageData) - * [`Text2Image.run()`](appbuilder.core.components.text_to_image.md#appbuilder.core.components.text_to_image.component.Text2Image.run) - * [`Text2Image.submitText2ImageTask()`](appbuilder.core.components.text_to_image.md#appbuilder.core.components.text_to_image.component.Text2Image.submitText2ImageTask) -* [appbuilder.core.components.translate package](appbuilder.core.components.translate.md) - * [Submodules](appbuilder.core.components.translate.md#submodules) - * [appbuilder.core.components.translate.component module](appbuilder.core.components.translate.md#module-appbuilder.core.components.translate.component) - * [`Translation`](appbuilder.core.components.translate.md#appbuilder.core.components.translate.component.Translation) - * [`Translation.manifests`](appbuilder.core.components.translate.md#appbuilder.core.components.translate.component.Translation.manifests) - * [`Translation.name`](appbuilder.core.components.translate.md#appbuilder.core.components.translate.component.Translation.name) - * [`Translation.run()`](appbuilder.core.components.translate.md#appbuilder.core.components.translate.component.Translation.run) - * [`Translation.tool_eval()`](appbuilder.core.components.translate.md#appbuilder.core.components.translate.component.Translation.tool_eval) - * [`Translation.version`](appbuilder.core.components.translate.md#appbuilder.core.components.translate.component.Translation.version) -* [appbuilder.core.components.tree_mind package](appbuilder.core.components.tree_mind.md) - * [Submodules](appbuilder.core.components.tree_mind.md#submodules) - * [appbuilder.core.components.tree_mind.component module](appbuilder.core.components.tree_mind.md#module-appbuilder.core.components.tree_mind.component) - * [`TreeMind`](appbuilder.core.components.tree_mind.md#appbuilder.core.components.tree_mind.component.TreeMind) - * [`TreeMind.manifests`](appbuilder.core.components.tree_mind.md#appbuilder.core.components.tree_mind.component.TreeMind.manifests) - * [`TreeMind.name`](appbuilder.core.components.tree_mind.md#appbuilder.core.components.tree_mind.component.TreeMind.name) - * [`TreeMind.run()`](appbuilder.core.components.tree_mind.md#appbuilder.core.components.tree_mind.component.TreeMind.run) - * [`TreeMind.tool_eval()`](appbuilder.core.components.tree_mind.md#appbuilder.core.components.tree_mind.component.TreeMind.tool_eval) - * [`TreeMind.version`](appbuilder.core.components.tree_mind.md#appbuilder.core.components.tree_mind.component.TreeMind.version) -* [appbuilder.core.components.tts package](appbuilder.core.components.tts.md) - * [Submodules](appbuilder.core.components.tts.md#submodules) - * [appbuilder.core.components.tts.component module](appbuilder.core.components.tts.md#module-appbuilder.core.components.tts.component) - * [`TTS`](appbuilder.core.components.tts.md#appbuilder.core.components.tts.component.TTS) - * [`TTS.Baidu_TTS`](appbuilder.core.components.tts.md#appbuilder.core.components.tts.component.TTS.Baidu_TTS) - * [`TTS.PaddleSpeech_TTS`](appbuilder.core.components.tts.md#appbuilder.core.components.tts.component.TTS.PaddleSpeech_TTS) - * [`TTS.run()`](appbuilder.core.components.tts.md#appbuilder.core.components.tts.component.TTS.run) diff --git a/docs/API-Reference/Python/appbuilder.core.components.mix_card_ocr.md b/docs/API-Reference/Python/appbuilder.core.components.mix_card_ocr.md deleted file mode 100644 index df7942037..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.mix_card_ocr.md +++ /dev/null @@ -1,71 +0,0 @@ -# appbuilder.core.components.mix_card_ocr package - -## Submodules - -## appbuilder.core.components.mix_card_ocr.component module - -身份证混贴识别组件 - -### *class* appbuilder.core.components.mix_card_ocr.component.MixCardOCR(meta: [ComponentArguments](appbuilder.core.md#appbuilder.core.component.ComponentArguments) | None = ComponentArguments(name='', tool_desc={}), secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -身份证混贴识别组件 - -Examples: - -```python -import os -import requests -import appbuilder - -os.environ["GATEWAY_URL"] = "..." -os.environ["APPBUILDER_TOKEN"] = "..." -# 从BOS存储读取样例文件 -image_url="https://bj.bcebos.com/v1/appbuilder/test_mix_card_ocr.jpeg?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-24T06%3A18%3A11Z%2F-1%2Fhost%2F695b8041c1ded194b9e80dbe1865e4393da5a3515e90d72d81ef18296bd29598" -raw_image = requests.get(image_url).content -# 输入参数为一张图片 -inp = appbuilder.Message(content={"raw_image": raw_image}) -# 进行识别 -mix_card_ocr = MixCardOCR() -out = mix_card_ocr.run(inp) -# 打印识别结果 -print(out.content) -``` - -#### manifests *= [{'description': '当身份证正反面在同一张图片上,需要识别图片中身份证正反面所有字段时,使用该工具', 'name': 'mixcard_ocr', 'parameters': {'properties': {'file_names': {'description': '待识别文件的文件名', 'items': {'type': 'string'}, 'type': 'array'}}, 'required': ['file_names'], 'type': 'object'}}]* - -#### name *= 'mixcard_ocr'* - -#### run(message: [Message](appbuilder.core.md#appbuilder.core.message.Message), timeout: float = None, retry: int = 0) → [Message](appbuilder.core.md#appbuilder.core.message.Message) - -执行身份证识别操作 - -* **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 包含待识别图片或图片下载URL的Message对象. - 示例: Message(content={"raw_image": b"..."}) 或 Message(content={"url": "[https://image/download/url](https://image/download/url)"}). - * **timeout** (*float* *,* *可选*) -- HTTP请求的超时时间,默认为None. - * **retry** (*int* *,* *可选*) -- HTTP请求的重试次数,默认为0. -* **返回:** - 包含身份证识别结果的Message对象. -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message) - -#### tool_eval(name: str, streaming: bool, \*\*kwargs) - -对指定文件进行OCR识别。 - -* **参数:** - * **name** (*str*) -- API名称。 - * **streaming** (*bool*) -- 是否流式输出。如果为True,则逐个返回识别结果;如果为False,则一次性返回所有识别结果。 - * **\*\*kwargs** -- 其他参数。 -* **返回:** - 如果streaming为False,则返回包含所有识别结果的JSON字符串。 - 如果streaming为True,则逐个返回包含识别结果的字典,每个字典包含以下字段: - > type (str): 消息类型,固定为"text"。 - > text (str): 识别结果的JSON字符串。 - > visible_scope (str): 消息可见范围,可以是"llm"或"user"。 -* **抛出:** - **InvalidRequestArgumentError** -- 如果请求格式错误,即文件URL不存在时抛出。 - -#### version *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.object_recognize.md b/docs/API-Reference/Python/appbuilder.core.components.object_recognize.md deleted file mode 100644 index ed5f25984..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.object_recognize.md +++ /dev/null @@ -1,73 +0,0 @@ -# appbuilder.core.components.object_recognize package - -## Submodules - -## appbuilder.core.components.object_recognize.component module - -object recognize component. - -### *class* appbuilder.core.components.object_recognize.component.ObjectRecognition(meta: [ComponentArguments](appbuilder.core.md#appbuilder.core.component.ComponentArguments) | None = ComponentArguments(name='', tool_desc={}), secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -提供通用物体及场景识别能力,即对于输入的一张图片(可正常解码,且长宽比适宜),输出图片中的多 -个物体及场景标签。 - -Examples: - -```python -import appbuilder -# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 -os.environ["APPBUILDER_TOKEN"] = '...' - -object_recognition = appbuilder.ObjectRecognition() -with open("./object_recognition_test.jepg", "rb") as f: - out = self.component.run(appbuilder.Message(content={"raw_image": f.read()})) -print(out.content) -``` - -#### manifests *= [{'description': '提供通用物体及场景识别能力,即对于输入的一张图片,输出图片中的多个物体及场景标签。', 'name': 'object_recognition', 'parameters': {'anyOf': [{'required': ['img_url']}, {'required': ['img_name']}], 'properties': {'img_name': {'description': '待识别图片的文件名,用于生成图片url', 'type': 'string'}, 'img_url': {'description': '待识别图片的url,根据该url能够获取图片', 'type': 'string'}}, 'type': 'object'}}]* - -#### name *= 'object_recognition'* - -#### run(message: [Message](appbuilder.core.md#appbuilder.core.message.Message), timeout: float = None, retry: int = 0) → [Message](appbuilder.core.md#appbuilder.core.message.Message) - -通用物体识别 - -* **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入图片或图片url下载地址用于执行识别操作。 - 例如: Message(content={"raw_image": b"..."}) 或 Message(content={"url": "[https://image/download/url](https://image/download/url)"})。 - * **timeout** (*float* *,* *optional*) -- HTTP超时时间,默认为None。 - * **retry** (*int* *,* *optional*) -- HTTP重试次数,默认为0。 -* **返回:** - 模型识别结果。 - : 例如: Message(content={"result":[{"keyword":"苹果", - : "score":0.94553,"root":"植物-蔷薇科"},{"keyword":"姬娜果","score":0.730442,"root":"植物-其它"}, - {"keyword":"红富士","score":0.505194,"root":"植物-其它"}]}) -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message) - -#### tool_eval(name: str, streaming: bool, \*\*kwargs) - -评估并识别传入图像中的物体或场景。 - -* **参数:** - * **name** (*str*) -- 调用此方法的对象名称。 - * **streaming** (*bool*) -- 是否以流式方式返回结果。如果是True,则以生成器形式返回结果;如果是False,则直接返回字符串形式的识别结果。 - * **\*\*kwargs** -- 任意关键字参数,支持以下参数: - traceid (str, optional): 请求的追踪ID,用于追踪请求处理流程。默认为None。 - img_url (str, optional): 待识别图像的URL地址。默认为None,如果未指定,则尝试从file_urls和img_name参数中获取图像路径。 - file_urls (dict, optional): 包含文件名和对应URL的字典。默认为空字典。 - img_name (str, optional): 待识别图像的文件名。如果img_url未指定,则根据img_name从file_urls中获取图像的URL。默认为None。 - score_threshold (float, optional): 置信度阈值,低于此阈值的识别结果将被忽略。默认为0.5。 -* **返回:** - 如果streaming为True,则返回一个生成器,生成器中的元素为包含识别结果的字典,字典包含以下键: - : type (str): 结果类型,固定为"text"。 - text (str): 识别结果的JSON字符串表示。 - visible_scope (str): 结果的可见范围,'llm'表示仅对LLM可见,'user'表示对用户可见。 - - 如果streaming为False,则直接返回识别结果的JSON字符串表示。 -* **抛出:** - **InvalidRequestArgumentError** -- 如果请求格式错误(如未设置文件名或文件URL不存在),则抛出此异常。 - -#### version *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.plant_recognize.md b/docs/API-Reference/Python/appbuilder.core.components.plant_recognize.md deleted file mode 100644 index 77333ed07..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.plant_recognize.md +++ /dev/null @@ -1,71 +0,0 @@ -# appbuilder.core.components.plant_recognize package - -## Submodules - -## appbuilder.core.components.plant_recognize.component module - -植物识别组件 - -### *class* appbuilder.core.components.plant_recognize.component.PlantRecognition(meta: [ComponentArguments](appbuilder.core.md#appbuilder.core.component.ComponentArguments) | None = ComponentArguments(name='', tool_desc={}), secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -植物识别组件,即对于输入的一张图片(可正常解码,且长宽比适宜),输出图片中的植物识别结果 - -Examples: - -```python -import os -import requests -import appbuilder - -# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 -os.environ["GATEWAY_URL"] = "..." -os.environ["APPBUILDER_TOKEN"] = "..." -image_url = "https://bj.bcebos.com/v1/appbuilder/palnt_recognize_test.jpg?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-23T09%3A51%3A03Z%2F-1%2Fhost%2Faa2217067f78f0236c8262cdd89a4b4f4b2188d971ca547c53d01742af4a2cbe" - -# 从BOS存储读取样例文件 -raw_image = requests.get(image_url).content -inp = appbuilder.Message(content={"raw_image": raw_image}) -# inp = Message(content={"url": image_url}) - -# 运行植物识别 -plant_recognize = appbuilder.PlantRecognition() -out = plant_recognize.run(inp) -# 打印识别结果 -print(out.content) -``` - -#### manifests *= [{'description': '用于识别图片中植物类别', 'name': 'plant_rec', 'parameters': {'anyOf': [{'required': ['img_name']}, {'required': ['img_url']}], 'properties': {'img_name': {'description': '待识别图片的文件名', 'type': 'string'}, 'img_url': {'description': '待识别图片的url', 'type': 'string'}}, 'type': 'object'}}]* - -#### name *= 'plant_rec'* - -#### run(message: [Message](appbuilder.core.md#appbuilder.core.message.Message), timeout: float = None, retry: int = 0) → [Message](appbuilder.core.md#appbuilder.core.message.Message) - -输入图片并识别其中的植物 - -* **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入图片或图片url下载地址用于执行识别操作. 举例: Message(content={"raw_image": b"..."}) - * **Message****(****content={"url"** ( *或*) -- "[https://image/download/uel](https://image/download/uel)"}). - * **timeout** (*float* *,* *optional*) -- HTTP超时时间,默认为None - * **retry** (*int* *,* *optional*) -- HTTP重试次数,默认为0 -* **返回:** - 模型识别结果 -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message) - -#### tool_eval(name: str, streaming: bool, origin_query: str, \*\*kwargs) → Generator[str, None, None] | str - -用于工具的执行,通过调用底层接口进行植物识别 - -* **参数:** - * **name** (*str*) -- 工具名 - * **streaming** (*bool*) -- 是否流式返回 - * **origin_query** (*str*) -- 用户原始query - * **\*\*kwargs** -- 工具调用的额外关键字参数 -* **返回:** - 植物识别结果,包括识别出的植物类别和相应的置信度信息 -* **返回类型:** - Union[Generator[str, None, None], str] - -#### version *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.ppt_generation_from_file.md b/docs/API-Reference/Python/appbuilder.core.components.ppt_generation_from_file.md deleted file mode 100644 index 5b0b3cec3..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.ppt_generation_from_file.md +++ /dev/null @@ -1,115 +0,0 @@ -# appbuilder.core.components.ppt_generation_from_file package - -## Submodules - -## appbuilder.core.components.ppt_generation_from_file.component module - -### *class* appbuilder.core.components.ppt_generation_from_file.component.PPTGenerationFromFile(secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -文件生成PPT。 - -Examples: - -```python -import os -import appbuilder - -os.environ["APPBUILDER_TOKEN"] = '...' - -ppt_generator = appbuilder.PPTGenerationFromFile() -user_input = { - 'file_url': 'http://image.yoojober.com/users/chatppt/temp/2024-06/6672aa839a9da.docx' -} -answer = ppt_generator(appbuilder.Message(user_input)) -print(answer.content) -``` - -#### get_ppt_download_link(job_id: str, timeout: float | None = None) - -获取PPT下载链接 - -* **参数:** - * **job_id** (*str*) -- 任务ID - * **timeout** (*float* *,* *optional*) -- 请求超时时间,默认为None。 -* **返回:** - PPT下载链接 -* **返回类型:** - str -* **抛出:** - **Exception** -- PPT生成请求错误 - -#### get_ppt_download_link_url *= '/ppt/text2ppt/apps/ppt-download'* - -#### get_ppt_generation_status(job_id: str, request_times: int = 60, request_interval: int = 5, timeout: float | None = None) - -轮询查看PPT生成状态 - -* **参数:** - * **job_id** (*str*) -- PPT生成任务的唯一标识符 - * **request_times** (*int* *,* *optional*) -- 轮询请求次数,默认为60次。 - * **request_interval** (*int* *,* *optional*) -- 每次轮询请求的间隔时间(秒),默认为5秒。 - * **timeout** (*float* *,* *optional*) -- 请求的超时时间(秒),默认为None,即无超时限制。 -* **返回:** - PPT生成状态码,1表示正在生成,2表示生成完成,3表示生成失败。 -* **返回类型:** - int -* **抛出:** - **Exception** -- 如果PPT生成状态码不为2(生成完成),则抛出异常。 - -#### get_ppt_generation_status_url *= '/ppt/text2ppt/apps/ppt-result'* - -#### manifests *= [{'description': '根据上传的文件(非论文)生成PPT。', 'name': 'ppt_generation_from_file', 'parameters': {'properties': {'file_url': {'description': '用户上传的文件的链接。', 'text': 'string'}}, 'required': ['file_url'], 'type': 'object'}}]* - -#### meta - -`PPTGenerationFromFileArgs` 的别名 - -#### name *= 'ppt_generation_from_file'* - -#### ppt_generation(post_data: dict, timeout: float | None = None) - -创建PPT生成任务 - -* **参数:** - * **post_data** (*dict*) -- 包含PPT生成任务所需数据的字典 - * **timeout** (*float* *,* *optional*) -- 请求超时时间,默认为None,表示不设置超时时间。 -* **返回:** - PPT生成任务的Job ID -* **返回类型:** - str -* **抛出:** - **Exception** -- 如果PPT生成任务请求失败,抛出异常 - -#### ppt_generation_url *= '/ppt/text2ppt/apps/ppt-create-file'* - -#### run(message: [Message](appbuilder.core.md#appbuilder.core.message.Message), poll_request_times=60, poll_request_interval=5) → [Message](appbuilder.core.md#appbuilder.core.message.Message) - -使用给定的输入运行模型并返回结果。 - -* **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入消息,用于传入请求参数。 - * **poll_request_times** (*int*) -- 轮询请求结果次数。 - * **poll_request_interval** (*int*) -- 轮询请求的间隔时间(秒)。 -* **返回:** - 模型运行后的输出消息。 -* **返回类型:** - result ([Message](appbuilder.core.md#appbuilder.core.message.Message)) - -#### tool_eval(stream: bool = False, \*\*kwargs) - -用于执行function call的功能。 - -* **参数:** - * **stream** (*bool* *,* *optional*) -- 是否以生成器的方式返回结果,默认为False。 - * **\*\*kwargs** -- 任意关键字参数,目前只支持'file_url'。 -* **返回:** - 如果stream为False,则返回一个字符串,表示ppt下载链接。 - 如果stream为True,则返回一个生成器,生成器产生一个字符串,表示ppt下载链接。 -* **抛出:** - **ValueError** -- 如果'file_url'为空,则抛出异常。 - -#### uniform_prefix *= '/api/v1/component/component'* - -#### version *: str* diff --git a/docs/API-Reference/Python/appbuilder.core.components.ppt_generation_from_instruction.md b/docs/API-Reference/Python/appbuilder.core.components.ppt_generation_from_instruction.md deleted file mode 100644 index b2d888aa6..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.ppt_generation_from_instruction.md +++ /dev/null @@ -1,120 +0,0 @@ -# appbuilder.core.components.ppt_generation_from_instruction package - -## Submodules - -## appbuilder.core.components.ppt_generation_from_instruction.component module - -### *class* appbuilder.core.components.ppt_generation_from_instruction.component.PPTGenerationFromInstruction(secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -指令生成PPT,可通过传入对PPT的描述或者自定义参数进行生成。 - -### 示例 - -```python -import os -import appbuilder - -os.environ["APPBUILDER_TOKEN"] = '...' - -ppt_generator = appbuilder.PPTGenerationFromInstruction() -input_data = { - 'text': '生成一个介绍北京的PPT。', - 'custom_data': {}, - 'complex': 3, - 'user_name': '百度千帆AppBuilder' -} -answer = ppt_generator(appbuilder.Message(input_data)) -print(answer.content) -``` - -#### get_ppt_download_link(job_id: str, timeout: float | None = None) - -获取PPT下载链接 - -* **参数:** - * **job_id** (*str*) -- 作业ID - * **timeout** (*float* *,* *optional*) -- 请求超时时间,默认为None。 -* **返回:** - PPT下载链接 -* **返回类型:** - str -* **抛出:** - **Exception** -- 当PPT生成请求失败时抛出异常 - -#### get_ppt_download_link_url *= '/ppt/text2ppt/apps/ppt-download'* - -#### get_ppt_generation_status(job_id: str, request_times: int = 60, request_interval: int = 5, timeout: float | None = None) - -轮询查看PPT生成状态 - -* **参数:** - * **job_id** (*str*) -- PPT生成任务的唯一标识符 - * **request_times** (*int* *,* *optional*) -- 轮询请求的次数,默认为60次。 - * **request_interval** (*int* *,* *optional*) -- 每次轮询请求之间的间隔时间(秒),默认为5秒。 - * **timeout** (*float* *,* *optional*) -- 请求的超时时间(秒)。如果未设置,则使用http_client的默认超时时间。 -* **返回:** - PPT生成状态码。 - : - 1:正在生成 - - 2:生成完成 - - 3:生成失败 -* **返回类型:** - int -* **抛出:** - **Exception** -- PPT生成过程中出现异常时抛出。 - -#### get_ppt_generation_status_url *= '/ppt/text2ppt/apps/ppt-result'* - -#### manifests *= [{'description': '根据输入指令生成PPT。', 'name': 'ppt_generation_from_instruction', 'parameters': {'properties': {'text': {'description': '用户请求生成PPT的指令。', 'example': '生成一个介绍北京的PPT。', 'text': 'string'}}, 'required': ['text'], 'type': 'object'}}]* - -#### meta - -`PPTGenerationFromInstructionArgs` 的别名 - -#### name *= 'ppt_generation_from_instruction'* - -#### ppt_generation(post_data: dict, timeout: float | None = None) - -创建PPT生成任务 - -* **参数:** - * **post_data** (*dict*) -- 请求数据 - * **timeout** (*float* *,* *optional*) -- 请求超时时间,默认为None. -* **返回:** - 任务ID -* **返回类型:** - str -* **抛出:** - **Exception** -- PPT生成请求失败 - -#### ppt_generation_url *= '/ppt/text2ppt/apps/ppt-create'* - -#### run(message: [Message](appbuilder.core.md#appbuilder.core.message.Message), poll_request_times=60, poll_request_interval=5) → [Message](appbuilder.core.md#appbuilder.core.message.Message) - -使用给定的输入运行模型并返回结果。 - -* **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入消息,用于传入请求参数。 - * **poll_request_times** (*int* *,* *optional*) -- 轮询请求结果次数,默认为60。 - * **poll_request_interval** (*int* *,* *optional*) -- 轮询请求的间隔时间(秒),默认为5。 -* **返回:** - 模型运行后的输出消息,包含PPT下载链接。 -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message) - -#### tool_eval(stream: bool = False, \*\*kwargs) - -评估给定的文本内容。 - -* **参数:** - * **stream** (*bool* *,* *optional*) -- 是否以生成器形式返回结果,默认为False。如果为True,则逐个生成下载链接;如果为False,则直接返回下载链接。 - * **\*\*kwargs** -- 关键字参数,可以传递其他参数,但当前只使用 'text' 参数。 -* **返回:** - 如果 stream 为 False,则返回一个包含下载链接的字符串;如果 stream 为 True,则逐个生成下载链接。 -* **抛出:** - **ValueError** -- 如果 'text' 参数为空,则抛出此异常。 - -#### uniform_prefix *= '/api/v1/component/component'* - -#### version *: str* diff --git a/docs/API-Reference/Python/appbuilder.core.components.ppt_generation_from_paper.md b/docs/API-Reference/Python/appbuilder.core.components.ppt_generation_from_paper.md deleted file mode 100644 index cd355a311..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.ppt_generation_from_paper.md +++ /dev/null @@ -1,120 +0,0 @@ -# appbuilder.core.components.ppt_generation_from_paper package - -## Submodules - -## appbuilder.core.components.ppt_generation_from_paper.component module - -### *class* appbuilder.core.components.ppt_generation_from_paper.component.PPTGenerationFromPaper(secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -论文生成PPT。 - -Examples: - -```python -import os -import appbuilder - -os.environ["APPBUILDER_TOKEN"] = '...' - -ppt_generator = appbuilder.PPTGenerationFromPaper() -user_input = { - 'file_key': 'http://image.yoojober.com/users/chatppt/temp/2024-06/6672aa839a9da.docx' -} -answer = ppt_generator(appbuilder.Message(user_input)) -print(answer.content) -``` - -#### get_ppt_download_link(job_id: str, timeout: float | None = None) - -获取PPT下载链接 - -* **参数:** - * **job_id** (*str*) -- 任务ID - * **timeout** (*float* *,* *optional*) -- 请求超时时间,默认为None. -* **返回:** - PPT下载链接 -* **返回类型:** - str -* **抛出:** - **Exception** -- PPT生成请求失败 - -#### get_ppt_download_link_url *= '/ppt/text2ppt/apps/ppt-download'* - -#### get_ppt_generation_status(job_id: str, request_times: int = 60, request_interval: int = 5, timeout: float | None = None) - -轮询查看PPT生成状态 - -* **参数:** - * **job_id** (*str*) -- 任务ID - * **request_times** (*int* *,* *optional*) -- 请求次数,默认为60次。 - * **request_interval** (*int* *,* *optional*) -- 请求间隔时间,默认为5秒。 - * **timeout** (*float* *,* *optional*) -- 请求超时时间,默认为None,即不设置超时时间。 -* **返回:** - PPT生成状态码。 - : - 1: PPT正在生成中 - - 2: PPT生成完成 - - 3: PPT生成失败 -* **返回类型:** - int -* **抛出:** - **Exception** -- PPT生成失败或请求失败时抛出异常。 - -#### get_ppt_generation_status_url *= '/ppt/text2ppt/apps/ppt-result'* - -#### manifests *= [{'description': '根据上传的论文生成PPT。', 'name': 'ppt_generation_from_paper', 'parameters': {'properties': {'file_key': {'description': '用户上传的论文的链接。', 'text': 'string'}}, 'required': ['file_key'], 'type': 'object'}}]* - -#### meta - -`PPTGenerationFromPaperArgs` 的别名 - -#### name *= 'ppt_generation_from_paper'* - -#### ppt_generation(post_data: dict, timeout: float | None = None) - -创建PPT生成任务 - -* **参数:** - * **post_data** (*dict*) -- 发送的POST请求体数据 - * **timeout** (*float* *,* *optional*) -- 请求超时时间,默认为None。 -* **返回:** - 返回的任务ID -* **返回类型:** - str -* **抛出:** - **Exception** -- 如果PPT生成请求失败,抛出异常 - -#### ppt_generation_url *= '/ppt/text2ppt/apps/ppt-create-thesis'* - -#### run(message: [Message](appbuilder.core.md#appbuilder.core.message.Message), poll_request_times=60, poll_request_interval=5) → [Message](appbuilder.core.md#appbuilder.core.message.Message) - -使用给定的输入运行模型并返回结果。 - -* **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入消息,用于传入请求参数。 - * **poll_request_times** (*int*) -- 轮询请求结果次数,默认为60次。 - * **poll_request_interval** (*int*) -- 轮询请求的间隔时间(秒),默认为5秒。 -* **返回:** - 模型运行后的输出消息,包含PPT下载链接。 -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message) -* **抛出:** - **Exception** -- 当输入参数中缺少必要的键时,抛出异常。 - -#### tool_eval(stream: bool = False, \*\*kwargs) - -使用指定的file_key来评估并获取相应的结果。 - -* **参数:** - * **stream** (*bool* *,* *optional*) -- 是否以生成器的方式逐项返回结果,默认为False。 - * **\*\*kwargs** -- 关键字参数,用于传递其他参数,目前仅支持file_key。 -* **返回:** - 如果stream为False,则直接返回结果。 - 如果stream为True,则逐个返回结果。 -* **抛出:** - **ValueError** -- 如果参数file_key为空,则抛出异常。 - -#### uniform_prefix *= '/api/v1/component/component'* - -#### version *: str* diff --git a/docs/API-Reference/Python/appbuilder.core.components.qrcode_ocr.md b/docs/API-Reference/Python/appbuilder.core.components.qrcode_ocr.md deleted file mode 100644 index 78c18175f..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.qrcode_ocr.md +++ /dev/null @@ -1,73 +0,0 @@ -# appbuilder.core.components.qrcode_ocr package - -## Submodules - -## appbuilder.core.components.qrcode_ocr.component module - -qrcode ocr component. - -### *class* appbuilder.core.components.qrcode_ocr.component.QRcodeOCR(meta: [ComponentArguments](appbuilder.core.md#appbuilder.core.component.ComponentArguments) | None = ComponentArguments(name='', tool_desc={}), secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -对图片中的二维码、条形码进行检测和识别,返回存储的文字信息及其位置信息。 - -Examples: - -```python -import appbuilder -# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 -os.environ["APPBUILDER_TOKEN"] = '...' - -qrcode_ocr = appbuilder.QRcodeOCR() -with open("./qrcode_ocr_test.png", "rb") as f: - out = self.component.run(appbuilder.Message(content={"raw_image": f.read(),"location": "true"})) -print(out.content) -``` - -#### manifests *= [{'description': '需要对图片中的二维码、条形码进行检测和识别,返回存储的文字信息及其位置信息,使用该工具', 'name': 'qrcode_ocr', 'parameters': {'properties': {'file_names': {'description': '待识别文件的文件名', 'items': {'type': 'string'}, 'type': 'array'}, 'location': {'description': '是否输出二维码/条形码位置信息', 'type': 'string'}}, 'required': ['file_names'], 'type': 'object'}}]* - -#### name *= 'qrcode_ocr'* - -#### run(message: [Message](appbuilder.core.md#appbuilder.core.message.Message), location: str = 'true', timeout: float = None, retry: int = 0) → [Message](appbuilder.core.md#appbuilder.core.message.Message) - -执行二维码识别操作。 - -* **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入的图片或图片URL下载地址,用于执行识别操作。例如: - Message(content={"raw_image": b"...", "location": ""}) 或 - Message(content={"url": "[https://image/download/url](https://image/download/url)"})。 - * **location** (*str* *,* *可选*) -- 是否需要返回二维码位置信息,默认为 "true"。 - * **timeout** (*float* *,* *可选*) -- HTTP请求的超时时间。 - * **retry** (*int* *,* *可选*) -- HTTP请求的重试次数。 -* **返回:** - 识别结果,包含识别到的二维码信息。例如: - : Message(name=msg, content={'codes_result': [{'type': 'QR_CODE', 'text': ['[http://weixin.qq.com/r/cS7M1PHE5qyZrbW393tj](http://weixin.qq.com/r/cS7M1PHE5qyZrbW393tj)'], - : 'location': {'top': 63, 'left': 950, 'width': 220, 'height': 211}}, ...]}, mtype=dict) -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message) -* **抛出:** - **InvalidRequestArgumentError** -- 如果 location 参数非法,将抛出该异常。 - -#### tool_eval(name: str, streaming: bool, \*\*kwargs) - -评估工具函数 - -* **参数:** - * **name** (*str*) -- 工具名称 - * **streaming** (*bool*) -- 是否流式输出 - * **\*\*kwargs** -- 其他关键字参数 -* **关键字参数:** - * **traceid** (*str*) -- 请求的traceid - * **file_names** (*List* *[**str* *]*) -- 文件名列表 - * **locations** (*str*) -- 是否需要获取位置信息,可选值为'true'或'false',默认为'false' - * **file_urls** (*Dict* *[**str* *,* *str* *]*) -- 文件名到文件URL的映射 -* **返回:** - 如果streaming为True,则返回一个生成器,生成两个字典,分别代表LLM和用户可见的内容; - : 如果streaming为False,则返回一个JSON字符串,包含评估结果 -* **返回类型:** - Union[str, Generator[Dict[str, Any], None, None]] -* **抛出:** - **InvalidRequestArgumentError** -- 如果请求格式错误,或者位置信息不合法,则抛出该异常 - -#### version *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.rag_with_baidu_search.md b/docs/API-Reference/Python/appbuilder.core.components.rag_with_baidu_search.md deleted file mode 100644 index 52c3092f3..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.rag_with_baidu_search.md +++ /dev/null @@ -1,37 +0,0 @@ -# appbuilder.core.components.rag_with_baidu_search package - -## Submodules - -## appbuilder.core.components.rag_with_baidu_search.component module - -### *class* appbuilder.core.components.rag_with_baidu_search.component.RAGWithBaiduSearch(model, secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False, instruction: [Message](appbuilder.core.md#appbuilder.core.message.Message) | None = None, reject: bool | None = False, clarify: bool | None = False, highlight: bool | None = False, friendly: bool | None = False, cite: bool | None = False) - -基类:`CompletionBaseComponent` - -#### meta *: RAGWithBaiduSearchArgs* - -#### name *: str* *= 'rag_with_baidu_search'* - -#### run(message, instruction=None, reject=None, clarify=None, highlight=None, friendly=None, cite=None, stream=False, temperature=1e-10, top_p=1e-10) - -执行模型推理 - -* **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 用户输入的消息对象 - * **instruction** (*Instruction* *,* *optional*) -- 用户提供的指令信息,默认为None。如果未提供,则使用默认的指令信息。 - * **reject** (*bool* *,* *optional*) -- 是否拒绝执行,默认为None。如果未提供,则使用默认设置。 - * **clarify** (*bool* *,* *optional*) -- 是否需要澄清,默认为None。如果未提供,则使用默认设置。 - * **highlight** (*bool* *,* *optional*) -- 是否高亮显示,默认为None。如果未提供,则使用默认设置。 - * **friendly** (*bool* *,* *optional*) -- 是否以友好的方式回答,默认为None。如果未提供,则使用默认设置。 - * **cite** (*bool* *,* *optional*) -- 是否引用原始信息,默认为None。如果未提供,则使用默认设置。 - * **stream** (*bool* *,* *optional*) -- 是否以流式方式返回结果,默认为False。 - * **temperature** (*float* *,* *optional*) -- 温度参数,用于控制生成文本的多样性,默认为1e-10。 - * **top_p** (*float* *,* *optional*) -- 截断概率阈值,用于控制生成文本的多样性,默认为1e-10。 -* **返回:** - 推理结果消息对象 -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message) -* **抛出:** - **AppBuilderServerException** -- 如果输入消息内容过长(超过72个字符)或推理结果中存在错误,则抛出异常。 - -#### version *: str* *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.rag_with_baidu_search_pro.md b/docs/API-Reference/Python/appbuilder.core.components.rag_with_baidu_search_pro.md deleted file mode 100644 index 437bc3f90..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.rag_with_baidu_search_pro.md +++ /dev/null @@ -1,163 +0,0 @@ -# appbuilder.core.components.rag_with_baidu_search_pro package - -## Submodules - -## appbuilder.core.components.rag_with_baidu_search_pro.component module - -### *class* appbuilder.core.components.rag_with_baidu_search_pro.component.RagWithBaiduSearchPro(model: str, secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False, instruction: [Message](appbuilder.core.md#appbuilder.core.message.Message) | None = None) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -RagWithBaiduSearchPro 组件 - -#### meta *: [RagWithBaiduSearchProArgs](#appbuilder.core.components.rag_with_baidu_search_pro.component.RagWithBaiduSearchProArgs)* - -#### name *= 'rag_with_baidu_search_pro'* - -#### run(message, stream=False, instruction=None, model=None, temperature=1e-10, top_p=1e-10, search_top_k=4, hide_corner_markers=True) - -执行模型推理。 - -* **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 待处理的信息对象。 - * **stream** (*bool* *,* *optional*) -- 是否以流的形式接收响应数据。默认为False。 - * **instruction** (*Instruction* *,* *optional*) -- 指令信息对象。默认为None。 - * **model** (*str* *,* *optional*) -- 模型名称。默认为None,表示使用当前实例的模型。 - * **temperature** (*float* *,* *optional*) -- 温度参数,控制生成文本的随机性。默认为1e-10。 - * **top_p** (*float* *,* *optional*) -- 累积概率阈值,用于控制生成文本的多样性。默认为1e-10。 - * **search_top_k** (*int* *,* *optional*) -- 搜索候选结果的数量。默认为4。 - * **hide_corner_markers** (*bool* *,* *optional*) -- 是否隐藏响应中的边界标记。默认为True。 -* **返回:** - 处理后的信息对象。 -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message) -* **抛出:** - **AppBuilderServerException** -- 如果输入信息或指令过长,将抛出此异常。 - -#### set_secret_key_and_gateway(\*\*kwargs) - -设置密钥和网关地址。 - -* **参数:** - * **secret_key** (*Optional* *[**str* *]* *,* *optional*) -- 密钥,默认为None。如果未指定,则使用实例当前的密钥。 - * **gateway** (*str* *,* *optional*) -- 网关地址,默认为空字符串。如果未指定,则使用实例当前的网关地址。 -* **返回:** - None - -#### version *= 'v1'* - -### *class* appbuilder.core.components.rag_with_baidu_search_pro.component.RagWithBaiduSearchProArgs(\*, name: str = '', tool_desc: Dict[str, Any] = {}, query: Annotated[str, MaxLen(max_length=300)]) - -基类:[`ComponentArguments`](appbuilder.core.md#appbuilder.core.component.ComponentArguments) - -RagWithBaiduSearchPro 的参数 - -* **参数:** - **query** (*str*) -- 用户的 query 输入 - -#### model_computed_fields *: ClassVar[dict[str, ComputedFieldInfo]]* *= {}* - -A dictionary of computed field names and their corresponding ComputedFieldInfo objects. - -#### model_config *: ClassVar[ConfigDict]* *= {}* - -Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict]. - -#### model_fields *: ClassVar[dict[str, FieldInfo]]* *= {'name': FieldInfo(annotation=str, required=False, default=''), 'query': FieldInfo(annotation=str, required=True, description='用户的 query 输入', metadata=[MaxLen(max_length=300)]), 'tool_desc': FieldInfo(annotation=Dict[str, Any], required=False, default={})}* - -Metadata about the fields defined on the model, -mapping of field names to [FieldInfo][pydantic.fields.FieldInfo]. - -This replaces Model._\_fields_\_ from Pydantic V1. - -#### query *: str* - -### *class* appbuilder.core.components.rag_with_baidu_search_pro.component.RagWithBaiduSearchProRequest(\*, message: object, stream: bool = False, instruction: str, model: str | None = None, temperature: Annotated[float, None, Interval(gt=None, ge=0, lt=None, le=1), None, None] = 1e-10, top_p: Annotated[float, None, Interval(gt=None, ge=0, lt=None, le=1), None, None] = 1e-10, search_top_k: Annotated[int, None, Interval(gt=None, ge=1, lt=None, le=None), None] = 4, hide_corner_markers: bool = True) - -基类:`BaseModel` - -RagWithBaiduSearchPro 的请求 - -#### message - -用户的消息 - -* **Type:** - object - -#### stream - -是否流式处理 - -* **Type:** - bool - -#### instruction - -指令 - -* **Type:** - str - -#### model - -模型名称 - -* **Type:** - Optional[str] - -#### temperature - -温度,范围在0到1之间 - -* **Type:** - confloat(ge=0, le=1) - -#### top_p - -top_p,范围在0到1之间 - -* **Type:** - confloat(ge=0, le=1) - -#### search_top_k - -search_top_k, - -* **Type:** - conint(ge=1) - -#### hide_corner_markers *: bool* - -#### instruction *: str* - -#### message *: object* - -#### model *: str | None* - -#### model_computed_fields *: ClassVar[dict[str, ComputedFieldInfo]]* *= {}* - -A dictionary of computed field names and their corresponding ComputedFieldInfo objects. - -#### model_config *: ClassVar[ConfigDict]* *= {}* - -Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict]. - -#### model_fields *: ClassVar[dict[str, FieldInfo]]* *= {'hide_corner_markers': FieldInfo(annotation=bool, required=False, default=True), 'instruction': FieldInfo(annotation=str, required=True), 'message': FieldInfo(annotation=object, required=True), 'model': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'search_top_k': FieldInfo(annotation=int, required=False, default=4, description='search_top_k必须是大于等于1的整数', metadata=[None, Interval(gt=None, ge=1, lt=None, le=None), None]), 'stream': FieldInfo(annotation=bool, required=False, default=False), 'temperature': FieldInfo(annotation=float, required=False, default=1e-10, description='temperature范围在0到1之间', metadata=[None, Interval(gt=None, ge=0, lt=None, le=1), None, None]), 'top_p': FieldInfo(annotation=float, required=False, default=1e-10, description='top_p范围在0到1之间', metadata=[None, Interval(gt=None, ge=0, lt=None, le=1), None, None])}* - -Metadata about the fields defined on the model, -mapping of field names to [FieldInfo][pydantic.fields.FieldInfo]. - -This replaces Model._\_fields_\_ from Pydantic V1. - -#### search_top_k *: Annotated[int, None, Interval(gt=None, ge=1, lt=None, le=None), None]* - -#### stream *: bool* - -#### temperature *: Annotated[float, None, Interval(gt=None, ge=0, lt=None, le=1), None, None]* - -#### top_p *: Annotated[float, None, Interval(gt=None, ge=0, lt=None, le=1), None, None]* - -## appbuilder.core.components.rag_with_baidu_search_pro.parse_rag_pro_response module - -init diff --git a/docs/API-Reference/Python/appbuilder.core.components.retriever.baidu_vdb.md b/docs/API-Reference/Python/appbuilder.core.components.retriever.baidu_vdb.md deleted file mode 100644 index d1d4c6b0c..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.retriever.baidu_vdb.md +++ /dev/null @@ -1,129 +0,0 @@ -# appbuilder.core.components.retriever.baidu_vdb package - -## Submodules - -## appbuilder.core.components.retriever.baidu_vdb.baiduvdb_retriever module - -基于Baidu VDB的retriever - -### *class* appbuilder.core.components.retriever.baidu_vdb.baiduvdb_retriever.BaiduVDBRetriever(embedding, table) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -向量检索组件,用于检索和query相匹配的内容 - -Examples: - -```python -import appbuilder -os.environ["APPBUILDER_TOKEN"] = '...' - -segments = appbuilder.Message(["文心一言大模型", "百度在线科技有限公司"]) -vector_index = appbuilder.BaiduVDBVectorStoreIndex.from_params( - self.instance_id, - self.api_key, -) -vector_index.add_segments(segments) - -query = appbuilder.Message("文心一言") -time.sleep(5) -retriever = vector_index.as_retriever() -res = retriever(query) -``` - -#### name *: str* *= 'BaiduVectorDBRetriever'* - -#### run(query: [Message](appbuilder.core.md#appbuilder.core.message.Message), top_k: int = 1) - -根据query进行查询 - -* **参数:** - * **query** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**str* *]*) -- 需要查询的内容,类型为Message,包含要查询的文本。 - * **top_k** (*int* *,* *optional*) -- 查询结果中匹配度最高的top_k个结果,默认为1。 -* **返回:** - 查询到的结果,包含文本和匹配得分。 -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message)[Dict] -* **抛出:** - * **TypeError** -- 如果query不是Message类型,或者top_k不是整数类型。 - * **ValueError** -- 如果top_k不是正整数,或者query的内容为空字符串,或者长度超过512个字符。 - -#### tool_desc *: Dict[str, Any]* *= {'description': 'a retriever based on Baidu VectorDB'}* - -### *class* appbuilder.core.components.retriever.baidu_vdb.baiduvdb_retriever.BaiduVDBVectorStoreIndex(instance_id: str, api_key: str, account: str = 'root', database_name: str = 'AppBuilderDatabase', table_params: ~appbuilder.core.components.retriever.baidu_vdb.baiduvdb_retriever.TableParams = , embedding=None) - -基类:`object` - -Baidu VDB向量存储检索工具 - -#### add_segments(segments: [Message](appbuilder.core.md#appbuilder.core.message.Message), metadata='') - -向bes中插入数据段 - -* **参数:** - * **segments** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 需要插入的数据段。 - * **metadata** (*str* *,* *optional*) -- 元数据,默认为空字符串。 -* **返回:** - 无返回值 -* **抛出:** - **ValueError** -- 如果segments为空,则抛出此异常。 - -#### as_retriever() - -将对象转化为retriever - -* **参数:** - **无** -* **返回:** - 转化后的retriever对象 -* **返回类型:** - [BaiduVDBRetriever](#appbuilder.core.components.retriever.baidu_vdb.baiduvdb_retriever.BaiduVDBRetriever) - -#### *property* client *: Any* - -获取客户端对象。 - -* **参数:** - **无参数** -* **返回:** - 返回客户端对象,具体类型依赖于vdb_client属性的值。 -* **返回类型:** - Any - -#### *classmethod* from_params(instance_id: str, api_key: str, account: str = 'root', database_name: str = 'AppBuilderDatabase', table_name: str = 'AppBuilderTable', drop_exists: bool = False, \*\*kwargs) - -从参数中实例化类。 - -* **参数:** - * **cls** (*type*) -- 类对象,即当前函数所属的类。 - * **instance_id** (*str*) -- 实例ID。 - * **api_key** (*str*) -- API密钥。 - * **account** (*str* *,* *optional*) -- 账户名,默认为'root'。 Defaults to DEFAULT_ACCOUNT. - * **database_name** (*str* *,* *optional*) -- 数据库名,默认为'AppBuilderDatabase'。 Defaults to DEFAULT_DATABASE_NAME. - * **table_name** (*str* *,* *optional*) -- 表名,默认为'AppBuilderTable'。 Defaults to DEFAULT_TABLE_NAME. - * **drop_exists** (*bool* *,* *optional*) -- 是否删除已存在的表,默认为False。 Defaults to False. - * **\*\*kwargs** -- 其他参数,可选的维度参数dimension默认为384。 -* **返回:** - 类实例,包含实例ID、账户名、API密钥、数据库名、表参数等属性。 -* **返回类型:** - cls - -#### vdb_uri_prefix *= b'/api/v1/bce/vdb/instance/'* - -### *class* appbuilder.core.components.retriever.baidu_vdb.baiduvdb_retriever.TableParams(dimension: int, table_name: str = 'AppBuilderTable', replication: int = 3, partition: int = 1, index_type: str = 'HNSW', metric_type: str = 'L2', drop_exists: bool = False, vector_params: Dict | None = None) - -基类:`object` - -Baidu VectorDB table params. -See the following documentation for details: -[https://cloud.baidu.com/doc/VDB/s/mlrsob0p6](https://cloud.baidu.com/doc/VDB/s/mlrsob0p6) - -* **参数:** - * **int** (*partition*) -- The dimension of vector. - * **int** -- The number of replicas in the table. - * **int** -- The number of partitions in the table. - * **index_type** (*Optional* *[**str* *]*) -- HNSW, FLAT... Default value is "HNSW" - * **metric_type** (*Optional* *[**str* *]*) -- L2, COSINE, IP. Default value is "L2" - * **drop_exists** (*Optional* *[**bool* *]*) -- Delete the existing Table. Default value is False. - * **vector_params** (*Optional* *[**Dict* *]*) -- if HNSW set parameters: M and efConstruction, for example {'M': 16, efConstruction: 200} - default is HNSW diff --git a/docs/API-Reference/Python/appbuilder.core.components.retriever.bes.md b/docs/API-Reference/Python/appbuilder.core.components.retriever.bes.md deleted file mode 100644 index 51c03a9b6..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.retriever.bes.md +++ /dev/null @@ -1,147 +0,0 @@ -# appbuilder.core.components.retriever.bes package - -## Submodules - -## appbuilder.core.components.retriever.bes.bes_retriever module - -基于baidu ES的retriever - -### *class* appbuilder.core.components.retriever.bes.bes_retriever.BESRetriever(embedding, index_name, bes_client, index_type='hnsw') - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -向量检索组件,用于检索和query相匹配的内容 - -Examples: - -```python -import appbuilder -os.environ["APPBUILDER_TOKEN"] = '...' - -segments = appbuilder.Message(["文心一言大模型", "百度在线科技有限公司"]) -vector_index = appbuilder.BESVectorStoreIndex.from_segments(segments, self.cluster_id, self.username, - self.password) -query = appbuilder.Message("文心一言") -time.sleep(5) -retriever = vector_index.as_retriever() -res = retriever(query) -``` - -#### base_es_url *: str* *= '/v1/bce/bes/cluster/'* - -#### name *: str* *= 'BaiduElasticSearchRetriever'* - -#### run(query: [Message](appbuilder.core.md#appbuilder.core.message.Message), top_k: int = 1) - -根据query进行查询 - -* **参数:** - * **query** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**str* *]*) -- 需要查询的内容,以Message对象的形式传递。 - * **top_k** (*int* *,* *optional*) -- 查询结果中匹配度最高的top_k个结果。默认为1。 -* **返回:** - 查询到的结果,包含文本、元数据以及匹配得分,以Message对象的形式返回。 -* **返回类型:** - obj ([Message](appbuilder.core.md#appbuilder.core.message.Message)[Dict]) - -#### tool_desc *: Dict[str, Any]* *= {'description': 'a retriever based on Baidu ElasticSearch'}* - -### *class* appbuilder.core.components.retriever.bes.bes_retriever.BESVectorStoreIndex(cluster_id, user_name, password, embedding=None, index_name=None, index_type='hnsw', prefix='/rpc/2.0/cloud_hub') - -基类:`object` - -BES向量存储检索工具 - -#### add_segments(segments: [Message](appbuilder.core.md#appbuilder.core.message.Message), metadata='') - -向BES中插入数据 - -* **参数:** - * **segments** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**str* *]*) -- 需要插入的内容,包含多个文本段 - * **metadata** (*str* *,* *optional*) -- 元数据,默认为空字符串。 -* **返回:** - 无返回值 - -#### as_retriever() - -将当前对象转化为retriever。 - -* **参数:** - **无** -* **返回:** - 转化后的retriever对象 -* **返回类型:** - [BESRetriever](#appbuilder.core.components.retriever.bes.bes_retriever.BESRetriever) - -#### base_es_url *: str* *= '/v1/bce/bes/cluster/'* - -#### *static* create_index_mappings(index_type, vector_dims) - -创建索引的mapping - -* **参数:** - * **index_type** (*str*) -- 索引类型,如"hnsw" - * **vector_dims** (*int*) -- 向量的维度 -* **返回:** - 索引的mapping配置 -* **返回类型:** - dict - -#### delete_all_segments() - -删除索引中的全部内容。 - -* **参数:** - **无** -* **返回:** - 无 - -#### *property* es - -获取Elasticsearch客户端实例。 - -* **参数:** - **无** -* **返回:** - Elasticsearch客户端实例。 - -#### *classmethod* from_segments(segments, cluster_id, user_name, password, embedding=None, \*\*kwargs) - -根据段落创建一个bes向量索引。 - -* **参数:** - * **segments** (*list*) -- 切分的文本段落列表。 - * **cluster_id** (*str*) -- bes集群ID。 - * **user_name** (*str*) -- bes用户名。 - * **password** (*str*) -- bes用户密码。 - * **embedding** ([*Embedding*](appbuilder.core.components.embeddings.md#appbuilder.core.components.embeddings.component.Embedding) *,* *optional*) -- 文本段落embedding工具,默认为None,使用默认的Embedding类。 - * **\*\*kwargs** -- 其他初始化参数。 -* **返回:** - bes索引实例。 -* **返回类型:** - BesVectorIndex - -#### *static* generate_id(length=16) - -生成随机的ID。 - -* **参数:** - **length** (*int* *,* *optional*) -- 生成ID的长度,默认为16。 -* **返回:** - 生成的随机ID。 -* **返回类型:** - str - -#### get_all_segments() - -获取索引中的全部内容 - -#### *property* helpers - -获取帮助器实例。 - -* **参数:** - **无** -* **返回:** - 帮助器实例。 -* **返回类型:** - \_helpers (对象) diff --git a/docs/API-Reference/Python/appbuilder.core.components.retriever.md b/docs/API-Reference/Python/appbuilder.core.components.retriever.md deleted file mode 100644 index 4c80323e6..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.retriever.md +++ /dev/null @@ -1,47 +0,0 @@ -# appbuilder.core.components.retriever package - -## Subpackages - -* [appbuilder.core.components.retriever.baidu_vdb package](appbuilder.core.components.retriever.baidu_vdb.md) - * [Submodules](appbuilder.core.components.retriever.baidu_vdb.md#submodules) - * [appbuilder.core.components.retriever.baidu_vdb.baiduvdb_retriever module](appbuilder.core.components.retriever.baidu_vdb.md#module-appbuilder.core.components.retriever.baidu_vdb.baiduvdb_retriever) - * [`BaiduVDBRetriever`](appbuilder.core.components.retriever.baidu_vdb.md#appbuilder.core.components.retriever.baidu_vdb.baiduvdb_retriever.BaiduVDBRetriever) - * [`BaiduVDBRetriever.name`](appbuilder.core.components.retriever.baidu_vdb.md#appbuilder.core.components.retriever.baidu_vdb.baiduvdb_retriever.BaiduVDBRetriever.name) - * [`BaiduVDBRetriever.run()`](appbuilder.core.components.retriever.baidu_vdb.md#appbuilder.core.components.retriever.baidu_vdb.baiduvdb_retriever.BaiduVDBRetriever.run) - * [`BaiduVDBRetriever.tool_desc`](appbuilder.core.components.retriever.baidu_vdb.md#appbuilder.core.components.retriever.baidu_vdb.baiduvdb_retriever.BaiduVDBRetriever.tool_desc) - * [`BaiduVDBVectorStoreIndex`](appbuilder.core.components.retriever.baidu_vdb.md#appbuilder.core.components.retriever.baidu_vdb.baiduvdb_retriever.BaiduVDBVectorStoreIndex) - * [`BaiduVDBVectorStoreIndex.add_segments()`](appbuilder.core.components.retriever.baidu_vdb.md#appbuilder.core.components.retriever.baidu_vdb.baiduvdb_retriever.BaiduVDBVectorStoreIndex.add_segments) - * [`BaiduVDBVectorStoreIndex.as_retriever()`](appbuilder.core.components.retriever.baidu_vdb.md#appbuilder.core.components.retriever.baidu_vdb.baiduvdb_retriever.BaiduVDBVectorStoreIndex.as_retriever) - * [`BaiduVDBVectorStoreIndex.client`](appbuilder.core.components.retriever.baidu_vdb.md#appbuilder.core.components.retriever.baidu_vdb.baiduvdb_retriever.BaiduVDBVectorStoreIndex.client) - * [`BaiduVDBVectorStoreIndex.from_params()`](appbuilder.core.components.retriever.baidu_vdb.md#appbuilder.core.components.retriever.baidu_vdb.baiduvdb_retriever.BaiduVDBVectorStoreIndex.from_params) - * [`BaiduVDBVectorStoreIndex.vdb_uri_prefix`](appbuilder.core.components.retriever.baidu_vdb.md#appbuilder.core.components.retriever.baidu_vdb.baiduvdb_retriever.BaiduVDBVectorStoreIndex.vdb_uri_prefix) - * [`TableParams`](appbuilder.core.components.retriever.baidu_vdb.md#appbuilder.core.components.retriever.baidu_vdb.baiduvdb_retriever.TableParams) -* [appbuilder.core.components.retriever.bes package](appbuilder.core.components.retriever.bes.md) - * [Submodules](appbuilder.core.components.retriever.bes.md#submodules) - * [appbuilder.core.components.retriever.bes.bes_retriever module](appbuilder.core.components.retriever.bes.md#module-appbuilder.core.components.retriever.bes.bes_retriever) - * [`BESRetriever`](appbuilder.core.components.retriever.bes.md#appbuilder.core.components.retriever.bes.bes_retriever.BESRetriever) - * [`BESRetriever.base_es_url`](appbuilder.core.components.retriever.bes.md#appbuilder.core.components.retriever.bes.bes_retriever.BESRetriever.base_es_url) - * [`BESRetriever.name`](appbuilder.core.components.retriever.bes.md#appbuilder.core.components.retriever.bes.bes_retriever.BESRetriever.name) - * [`BESRetriever.run()`](appbuilder.core.components.retriever.bes.md#appbuilder.core.components.retriever.bes.bes_retriever.BESRetriever.run) - * [`BESRetriever.tool_desc`](appbuilder.core.components.retriever.bes.md#appbuilder.core.components.retriever.bes.bes_retriever.BESRetriever.tool_desc) - * [`BESVectorStoreIndex`](appbuilder.core.components.retriever.bes.md#appbuilder.core.components.retriever.bes.bes_retriever.BESVectorStoreIndex) - * [`BESVectorStoreIndex.add_segments()`](appbuilder.core.components.retriever.bes.md#appbuilder.core.components.retriever.bes.bes_retriever.BESVectorStoreIndex.add_segments) - * [`BESVectorStoreIndex.as_retriever()`](appbuilder.core.components.retriever.bes.md#appbuilder.core.components.retriever.bes.bes_retriever.BESVectorStoreIndex.as_retriever) - * [`BESVectorStoreIndex.base_es_url`](appbuilder.core.components.retriever.bes.md#appbuilder.core.components.retriever.bes.bes_retriever.BESVectorStoreIndex.base_es_url) - * [`BESVectorStoreIndex.create_index_mappings()`](appbuilder.core.components.retriever.bes.md#appbuilder.core.components.retriever.bes.bes_retriever.BESVectorStoreIndex.create_index_mappings) - * [`BESVectorStoreIndex.delete_all_segments()`](appbuilder.core.components.retriever.bes.md#appbuilder.core.components.retriever.bes.bes_retriever.BESVectorStoreIndex.delete_all_segments) - * [`BESVectorStoreIndex.es`](appbuilder.core.components.retriever.bes.md#appbuilder.core.components.retriever.bes.bes_retriever.BESVectorStoreIndex.es) - * [`BESVectorStoreIndex.from_segments()`](appbuilder.core.components.retriever.bes.md#appbuilder.core.components.retriever.bes.bes_retriever.BESVectorStoreIndex.from_segments) - * [`BESVectorStoreIndex.generate_id()`](appbuilder.core.components.retriever.bes.md#appbuilder.core.components.retriever.bes.bes_retriever.BESVectorStoreIndex.generate_id) - * [`BESVectorStoreIndex.get_all_segments()`](appbuilder.core.components.retriever.bes.md#appbuilder.core.components.retriever.bes.bes_retriever.BESVectorStoreIndex.get_all_segments) - * [`BESVectorStoreIndex.helpers`](appbuilder.core.components.retriever.bes.md#appbuilder.core.components.retriever.bes.bes_retriever.BESVectorStoreIndex.helpers) -* [appbuilder.core.components.retriever.reranker package](appbuilder.core.components.retriever.reranker.md) - * [Submodules](appbuilder.core.components.retriever.reranker.md#submodules) - * [appbuilder.core.components.retriever.reranker.rerank module](appbuilder.core.components.retriever.reranker.md#module-appbuilder.core.components.retriever.reranker.rerank) - * [`Reranker`](appbuilder.core.components.retriever.reranker.md#appbuilder.core.components.retriever.reranker.rerank.Reranker) - * [`Reranker.accepted_models`](appbuilder.core.components.retriever.reranker.md#appbuilder.core.components.retriever.reranker.rerank.Reranker.accepted_models) - * [`Reranker.base_urls`](appbuilder.core.components.retriever.reranker.md#appbuilder.core.components.retriever.reranker.rerank.Reranker.base_urls) - * [`Reranker.meta`](appbuilder.core.components.retriever.reranker.md#appbuilder.core.components.retriever.reranker.rerank.Reranker.meta) - * [`Reranker.name`](appbuilder.core.components.retriever.reranker.md#appbuilder.core.components.retriever.reranker.rerank.Reranker.name) - * [`Reranker.run()`](appbuilder.core.components.retriever.reranker.md#appbuilder.core.components.retriever.reranker.rerank.Reranker.run) - * [`Reranker.version`](appbuilder.core.components.retriever.reranker.md#appbuilder.core.components.retriever.reranker.rerank.Reranker.version) diff --git a/docs/API-Reference/Python/appbuilder.core.components.retriever.reranker.md b/docs/API-Reference/Python/appbuilder.core.components.retriever.reranker.md deleted file mode 100644 index fb14caab8..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.retriever.reranker.md +++ /dev/null @@ -1,49 +0,0 @@ -# appbuilder.core.components.retriever.reranker package - -## Submodules - -## appbuilder.core.components.retriever.reranker.rerank module - -Reranker 文本精排 - -### *class* appbuilder.core.components.retriever.reranker.rerank.Reranker(model='bce-reranker-base') - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -Examples: - -```python -import os -import appbuilder -from appbuilder import Message - -os.environ["APPBUILDER_TOKEN"] = '...' - -reranker = appbuilder.Reranker() -ranked_1 = reranker("你好", ["他也好", "hello?"]) -print(ranked_1) -``` - -#### accepted_models *= ['bce-reranker-base']* - -#### base_urls *= {'bce-reranker-base': '/api/v1/component/component/bce_reranker_base'}* - -#### meta - -`RerankerArgs` 的别名 - -#### name *: str* *= 'reranker'* - -#### run(query: [Message](appbuilder.core.md#appbuilder.core.message.Message)[str] | str, texts: [Message](appbuilder.core.md#appbuilder.core.message.Message)[List[str]] | List[str]) → [Message](appbuilder.core.md#appbuilder.core.message.Message)[List[dict]] - -运行查询,对给定的文本集合进行批量处理,并返回处理后的结果列表。 - -* **参数:** - * **query** (*Union* *[*[*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**str* *]* *,* *str* *]*) -- 查询条件,可以是字符串或Message对象。 - * **texts** (*Union* *[*[*Message*](appbuilder.core.md#appbuilder.core.message.Message) *[**List* *[**str* *]* *]* *,* *List* *[**str* *]* *]*) -- 待处理的文本集合,可以是字符串列表或包含字符串列表的Message对象。 -* **返回:** - 处理后的结果列表,每个元素是一个字典,包含处理后的文本信息。 -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message)[List[dict]] - -#### version *: str* *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.table_ocr.md b/docs/API-Reference/Python/appbuilder.core.components.table_ocr.md deleted file mode 100644 index ec67cc225..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.table_ocr.md +++ /dev/null @@ -1,88 +0,0 @@ -# appbuilder.core.components.table_ocr package - -## Submodules - -## appbuilder.core.components.table_ocr.component module - -table ocr component. - -### *class* appbuilder.core.components.table_ocr.component.TableOCR(meta: [ComponentArguments](appbuilder.core.md#appbuilder.core.component.ComponentArguments) | None = ComponentArguments(name='', tool_desc={}), secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -支持识别图片中的表格内容,返回各表格的表头表尾内容、单元格文字内容及其行列位置信息,全面覆盖各类表格样式,包括常规有线表格、 -无线表格、含合并单元格表格。同时,支持多表格内容识别。 - -Examples: - -```python -import appbuilder -# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 -os.environ["APPBUILDER_TOKEN"] = '...' - -table_ocr = appbuilder.TableOCR() -with open("./table_ocr_test.png", "rb") as f: - out = self.component.run(appbuilder.Message(content={"raw_image": f.read()})) -print(out.content) -``` - -#### get_table_markdown(tables_result) - -将表格识别结果转换为Markdown格式。 - -* **参数:** - **tables_result** (*list*) -- 表格识别结果列表,每个元素是一个包含表格数据的字典,其中包含表格体(body)等字段。 -* **返回:** - 包含Markdown格式表格的字符串列表。 -* **返回类型:** - list - -#### manifests *= [{'description': '需要识别图片中的表格内容,使用该工具, 但不支持html后缀文件的识别', 'name': 'table_ocr', 'parameters': {'properties': {'file_names': {'description': '待识别图片的文件名', 'items': {'type': 'string'}, 'type': 'array'}}, 'required': ['file_names'], 'type': 'object'}}]* - -#### name *= 'table_ocr'* - -#### run(message: [Message](appbuilder.core.md#appbuilder.core.message.Message), timeout: float = None, retry: int = 0) → [Message](appbuilder.core.md#appbuilder.core.message.Message) - -表格文字识别 - -* **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 输入图片或图片url下载地址用于执行识别操作。 - 举例: Message(content={"raw_image": b"..."}) - 或 Message(content={"url": "[https://image/download/url](https://image/download/url)"})。 - * **timeout** (*float* *,* *可选*) -- HTTP超时时间。 - * **retry** (*int* *,* *可选*) -- HTTP重试次数。 -* **返回:** - 识别结果。 - : 举例: Message(name=msg, content={'tables_result': [{ - 'table_location': [{'x': 15, 'y': 15}, {'x': 371, 'y': 15}, {'x': 371, 'y': 98}, {'x': 15, - 'y': 98}], 'header': [], 'body': [{'cell_location': [{'x': 15, 'y': 15}, {'x': 120, 'y': 15}, - {'x': 120, 'y': 58}, {'x': 15, 'y': 58}], 'row_start': 0, 'row_end': 1, 'col_start': 0, - 'col_end': 1, 'words': '参数'}, {'cell_location': [{'x': 120, 'y': 15}, {'x': 371, 'y': 15}, - {'x': 371, 'y': 58}, {'x': 120, 'y': 58}], 'row_start': 0, 'row_end': 1, 'col_start': 1, - 'col_end': 2, 'words': '值'}, {'cell_location': [{'x': 15, 'y': 58}, {'x': 120, 'y': 58}, - {'x': 120, 'y': 98}, {'x': 15, 'y': 98}], 'row_start': 1, 'row_end': 2, 'col_start': 0, - 'col_end': 1, 'words': 'Content-Type'}, {'cell_location': [{'x': 120, 'y': 58}, {'x': 371, - 'y': 58}, {'x': 371, 'y': 98}, {'x': 120, 'y': 98}], 'row_start': 1, 'row_end': 2, 'col_start': - 1, 'col_end': 2, 'words': 'application/x-www-form-urlencoded'}], 'footer': []}]}, mtype=dict) -* **返回类型:** - message ([Message](appbuilder.core.md#appbuilder.core.message.Message)) - -#### tool_eval(name: str, streaming: bool, \*\*kwargs) - -对传入文件进行处理,并返回处理结果。 - -* **参数:** - * **name** (*str*) -- 工具的名称。 - * **streaming** (*bool*) -- 是否为流式处理。若为True,则以生成器形式返回结果;若为False,则直接返回结果。 - * **\*\*kwargs** -- 关键字参数,包含以下参数: - traceid (str): 请求的唯一标识符。 - file_names (List[str]): 文件名列表,表示需要处理的文件名。 - files (List[str]): 同file_names,用于兼容老版本接口。 - file_urls (Dict[str, str]): 文件名和对应URL的映射字典。 -* **返回:** - 若streaming为True,则以生成器形式返回处理结果,每个元素为包含type和text的字典,type固定为"text",text为处理结果的JSON字符串。 - 若streaming为False,则直接返回处理结果的JSON字符串。 -* **抛出:** - **InvalidRequestArgumentError** -- 若传入文件名在file_urls中未找到对应的URL,则抛出此异常。 - -#### version *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.text_to_image.md b/docs/API-Reference/Python/appbuilder.core.components.text_to_image.md deleted file mode 100644 index 413d6a87d..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.text_to_image.md +++ /dev/null @@ -1,104 +0,0 @@ -# appbuilder.core.components.text_to_image package - -## Submodules - -## appbuilder.core.components.text_to_image.component module - -Text2Image component. - -### *class* appbuilder.core.components.text_to_image.component.Text2Image(meta: [ComponentArguments](appbuilder.core.md#appbuilder.core.component.ComponentArguments) | None = ComponentArguments(name='', tool_desc={}), secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -文生图组件,即对于输入的文本,输出生成的图片url。 - -Examples: - -```python -import appbuilder -text_to_image = appbuilder.Text2Image() -os.environ["APPBUILDER_TOKEN"] = '...' -content_data = {"prompt": "上海的经典风景", "width": 1024, "height": 1024, "image_num": 1} -msg = appbuilder.Message(content_data) -out = text_to_image.run(inp) -# 打印生成结果 -print(out.content) # eg: {"img_urls": ["xxx"]} -``` - -#### *static* check_service_error(request_id: str, data: dict) - -检查服务错误信息 - -* **参数:** - * **request_id** (*str*) -- 请求ID - * **data** (*dict*) -- 响应数据 -* **抛出:** - **AppBuilderServerException** -- 如果响应数据中包含错误信息,则抛出异常 -* **返回:** - None - -#### extract_img_urls(response: Text2ImageQueryResponse) - -从作画生成的返回结果中提取图片url。 - -* **参数:** - **(****obj** (*response*) -- Text2ImageQueryResponse): 作画生成的返回结果。 -* **返回:** - 从返回体中提取的图片url列表。 -* **返回类型:** - List[str] - -#### manifests *= [{'description': '文生图,该组件只用于图片创作。当用户需要进行场景、人物、海报等内容的绘制时,使用该画图组件。如果用户需要生成图表(柱状图、折线图、雷达图等),则必须使用代码解释器。', 'name': 'text_to_image', 'parameters': {'properties': {'query': {'description': '文生图用的query。特别注意,这个字段只能由中文字符组成,不能含有任何英语描述。', 'type': 'string'}}, 'required': ['query'], 'type': 'object'}}]* - -#### queryText2ImageData(request: Text2ImageQueryRequest, timeout: float | None = None, retry: int = 0, request_id: str | None = None) → Text2ImageQueryResponse - -将文本查询请求转换为图像数据。 - -* **参数:** - * **request** (*Text2ImageQueryRequest*) -- 输入请求,必填参数。 - * **timeout** (*float* *,* *optional*) -- 请求的超时时间,默认为None。 - * **retry** (*int* *,* *optional*) -- 请求的重试次数,默认为0。 - * **request_id** (*str* *,* *optional*) -- 请求的唯一标识符,默认为None。 -* **返回:** - 接口返回的输出消息。 -* **返回类型:** - Text2ImageQueryResponse - -#### run(message: [Message](appbuilder.core.md#appbuilder.core.message.Message), width: int = 1024, height: int = 1024, image_num: int = 1, image: str | None = None, url: str | None = None, pdf_file: str | None = None, pdf_file_num: str | None = None, change_degree: int | None = None, text_content: str | None = None, task_time_out: int | None = None, text_check: int | None = 1, request_id: str | None = None) - -执行文本到图像的生成任务。 - -* **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 包含任务相关信息的消息对象。 - * **width** (*int* *,* *optional*) -- 生成的图像的宽度,默认为1024。 - * **height** (*int* *,* *optional*) -- 生成的图像的高度,默认为1024。 - * **image_num** (*int* *,* *optional*) -- 生成图像的数量,默认为1。 - * **image** (*Optional* *[**str* *]* *,* *optional*) -- 参考图像的路径或URL,默认为None。 - * **url** (*Optional* *[**str* *]* *,* *optional*) -- 参考图像的URL,默认为None。 - * **pdf_file** (*Optional* *[**str* *]* *,* *optional*) -- 参考PDF文件的路径,默认为None。 - * **pdf_file_num** (*Optional* *[**str* *]* *,* *optional*) -- 参考PDF文件中的页码范围,默认为None。 - * **change_degree** (*Optional* *[**int* *]* *,* *optional*) -- 图像变换的程度,默认为None。 - * **text_content** (*Optional* *[**str* *]* *,* *optional*) -- 需要转换的文本内容,默认为None。 - * **task_time_out** (*Optional* *[**int* *]* *,* *optional*) -- 任务超时时间,默认为None。 - * **text_check** (*Optional* *[**int* *]* *,* *optional*) -- 是否进行文本内容检查,默认为1。 - * **request_id** (*Optional* *[**str* *]* *,* *optional*) -- 请求的唯一标识,默认为None。 -* **返回:** - 包含生成图像URL的消息对象。 -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message) -* **抛出:** - **HTTPError** -- 请求失败时抛出异常。 - -#### submitText2ImageTask(request: Text2ImageSubmitRequest, timeout: float | None = None, retry: int = 0, request_id: str | None = None) → Text2ImageSubmitResponse - -使用给定的输入并返回文生图的任务信息。 - -* **参数:** - * **(****obj** (*request*) -- Text2ImageSubmitRequest): 输入请求,这是一个必需的参数。 - * **timeout** (*float* *,* *optional*) -- 请求的超时时间。默认为None。 - * **retry** (*int* *,* *optional*) -- 请求的重试次数。默认为0。 - * **request_id** (*str* *,* *optional*) -- 请求的唯一标识符。默认为None。 -* **返回:** - Text2ImageSubmitResponse: 接口返回的输出消息。 -* **返回类型:** - obj diff --git a/docs/API-Reference/Python/appbuilder.core.components.translate.md b/docs/API-Reference/Python/appbuilder.core.components.translate.md deleted file mode 100644 index 6d1ab6c65..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.translate.md +++ /dev/null @@ -1,67 +0,0 @@ -# appbuilder.core.components.translate package - -## Submodules - -## appbuilder.core.components.translate.component module - -文本翻译-通用版组件 - -### *class* appbuilder.core.components.translate.component.Translation(meta: [ComponentArguments](appbuilder.core.md#appbuilder.core.component.ComponentArguments) | None = ComponentArguments(name='', tool_desc={}), secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -文本翻译组件,可支持中、英、日、韩等200+语言互译,100+语种自动检测。 -支持语种列表可参照 [https://ai.baidu.com/ai-doc/MT/4kqryjku9#%E8%AF%AD%E7%A7%8D%E5%88%97%E8%A1%A8](https://ai.baidu.com/ai-doc/MT/4kqryjku9#%E8%AF%AD%E7%A7%8D%E5%88%97%E8%A1%A8) - -Examples: - -```python -import appbuilder - -# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 -os.environ["APPBUILDER_TOKEN"] = '...' - -translate = appbuilder.Translation() -resp = translate(appbuilder.Message("你好\n中国"), to_lang="en") -# 输出 {'from_lang':'zh', 'to_lang':'en', 'trans_result':[{'src':'你好','dst':'hello'},{'src':'中国','dst':'China'}]} -print(resp.content) -``` - -#### manifests *= [{'description': '文本翻译通用版工具,会根据指定的目标语言对文本进行翻译,并返回翻译后的文本。', 'name': 'translation', 'parameters': {'properties': {'q': {'description': '需要翻译的源文本,文本翻译工具会将该文本翻译成对应的目标语言', 'type': 'string'}, 'to_lang': {'description': "翻译的目标语言类型,'en'表示翻译成英语, 'zh'表示翻译成中文,'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'表示翻译成越南语,默认为'en'", 'enum': ['en', 'zh', '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'], 'type': 'string'}}, 'required': ['q'], 'type': 'object'}}]* - -#### name *= 'translate'* - -#### run(message: [Message](appbuilder.core.md#appbuilder.core.message.Message), from_lang: str = 'auto', to_lang: str = 'en', timeout: float = None, retry: int = 0) → [Message](appbuilder.core.md#appbuilder.core.message.Message) - -根据提供的文本以及语种参数执行文本翻译 - -* **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 翻译文本。 - * **from_lang** (*str*) -- 翻译的源语言。默认为 "auto"。 - * **to_lang** (*str*) -- 翻译的目标语言。默认为 "en"。 - * **timeout** (*float* *,* *optional*) -- 翻译请求的超时时间。 - * **retry** (*int* *,* *optional*) -- 重试次数。 -* **返回:** - 返回的文本翻译结果。 - 例如,Message(content={'from_lang': 'zh', 'to_lang': 'en', 'trans_result': [{'src': '你好', 'dst': 'hello'}]}) -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message) - -#### tool_eval(name: str, streaming: bool, \*\*kwargs) - -工具函数,用于翻译指定的文本。 - -* **参数:** - * **name** (*str*) -- 函数名称,此参数在本函数中未使用。 - * **streaming** (*bool*) -- 是否流式输出翻译结果。 - * **\*\*kwargs** -- 关键字参数,可以包含以下参数: - - traceid (str, optional): 请求的唯一标识符,默认为None。 - - q (str): 待翻译的文本。 - - to_lang (str, optional): 目标语言代码,默认为"en"。 -* **返回:** - 如果streaming为True,则返回生成器,生成包含翻译结果的字典; - 如果streaming为False,则返回包含翻译结果的JSON字符串。 -* **抛出:** - **InvalidRequestArgumentError** -- 如果未设置参数"q",则抛出此异常。 - -#### version *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.tree_mind.md b/docs/API-Reference/Python/appbuilder.core.components.tree_mind.md deleted file mode 100644 index a068a7b00..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.tree_mind.md +++ /dev/null @@ -1,53 +0,0 @@ -# appbuilder.core.components.tree_mind package - -## Submodules - -## appbuilder.core.components.tree_mind.component module - -树图工具 - -### *class* appbuilder.core.components.tree_mind.component.TreeMind(meta: [ComponentArguments](appbuilder.core.md#appbuilder.core.component.ComponentArguments) | None = ComponentArguments(name='', tool_desc={}), secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -树图工具,提供智能思维导图制作工具和丰富的模板,支持脑图、逻辑图、树形图、鱼骨图、组织架构图、时间轴、时间线等多种专业格式。 -.. code-block:: python - -> import os -> import requests -> import appbuilder -> # 请前往千帆AppBuilder官网创建密钥,流程详见:[https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5](https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5) -> os.environ["GATEWAY_URL"] = "..." -> os.environ["APPBUILDER_TOKEN"] = "..." -> treemind = appbuilder.TreeMind() -> resp = treemind.run(appbuilder.Message("生成一份年度总结的思维导图"), to_lang="en") -> print(resp.content) -> # 输出 {'from_lang':'zh', 'to_lang':'en', 'trans_result':[{'src':'你好','dst':'hello'},{'src':'中国','dst':'China'}]} - -#### manifests *= [{'description': '根据用户输入的信息,生成详细智能思维导图、脑图、逻辑图、树形图、鱼骨图、组织架构图、时间轴、时间线等多种专业格式思维导图', 'name': 'tree_mind', 'parameters': {'properties': {'query': {'description': '用户想要生成思维导图的内容', 'type': 'string'}}, 'required': ['query'], 'type': 'object'}}]* - -#### name *= 'tree_mind'* - -#### run(message: [Message](appbuilder.core.md#appbuilder.core.message.Message), \*\*kwargs) → [Message](appbuilder.core.md#appbuilder.core.message.Message) - -运行组件 -:param message: 消息对象 -:type message: Message - -* **返回:** - 返回消息对象 -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message) - -#### tool_eval(query, \*\*kwargs) - -调用树图查询接口 -:param query: 用户想要生成思维导图的内容 -:type query: string - -* **返回:** - 返回生成的思维导图的图片链接和跳转链接 -* **返回类型:** - dict - -#### version *= 'v1'* diff --git a/docs/API-Reference/Python/appbuilder.core.components.tts.md b/docs/API-Reference/Python/appbuilder.core.components.tts.md deleted file mode 100644 index b82357296..000000000 --- a/docs/API-Reference/Python/appbuilder.core.components.tts.md +++ /dev/null @@ -1,65 +0,0 @@ -# appbuilder.core.components.tts package - -## Submodules - -## appbuilder.core.components.tts.component module - -text to speech component. - -### *class* appbuilder.core.components.tts.component.TTS(\*args, \*\*kwargs) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -文本转语音组件,即输入一段文本将其转为一段语音 - -Examples: - -```python -import appbuilder -os.environ["APPBUILDER_TOKEN"] = '...' -tts = appbuilder.TTS() - -# 默认使用baidu-tts模型, 默认返回MP3格式 -inp = appbuilder.Message(content={"text": "欢迎使用语音合成"}) -out = tts.run(inp) -with open("sample.mp3", "wb") as f: - f.write(out.content["audio_binary"]) - -# 使用paddlespeech-tts模型,目前只支持返回WAV格式 -inp = appbuilder.Message(content={"text": "欢迎使用语音合成"}) -out = tts.run(inp, model="paddlespeech-tts", audio_type="wav") -with open("sample.wav", "wb") as f: - f.write(out.content["audio_binary"]) -``` - -#### Baidu_TTS *= 'baidu-tts'* - -#### PaddleSpeech_TTS *= 'paddlespeech-tts'* - -#### run(message: [Message](appbuilder.core.md#appbuilder.core.message.Message), model: Literal['baidu-tts', 'paddlespeech-tts'] = 'baidu-tts', speed: int = 5, pitch: int = 5, volume: int = 5, person: int = 0, audio_type: Literal['mp3', 'wav', 'pcm'] = 'mp3', timeout: float = None, retry: int = 0, stream: bool = False) → [Message](appbuilder.core.md#appbuilder.core.message.Message) - -执行文本转语音。 - -* **参数:** - * **message** ([*Message*](appbuilder.core.md#appbuilder.core.message.Message)) -- 待转为语音的文本。 - 举例: Message(content={"text": "欢迎使用百度语音"})如果选择baidu-tts模型,text最大文本长度为1024 GBK编码长度,大约为512个中英文字符;如果选择paddlespeech-tts模型, text最大文本长度是510个字符。 - * **model** (*str* *,* *可选*) -- 默认是\`baidu-tts\`模型,可设置为\`paddlespeech-tts\`。 - * **speed** (*int* *,* *可选*) -- 语音语速,默认是5中等语速,取值范围在0~15之间, - 如果选择模型为paddlespeech-tts,参数自动失效。 - * **pitch** (*int* *,* *可选*) -- 语音音调,默认是5中等音调,取值范围在0~15之间, - 如果选择模型为paddlespeech-tts,参数自动失效。 - * **volume** (*int* *,* *音量*) -- 语音音量,默认是5中等音量,取值范围在0~15之间, - 如果选择模型为paddlespeech-tts,参数自动失效。 - * **person** (*int* *,* *可选*) -- 语音人物特征,默认是0, - 可选值包括度小宇=1 度小美=0 度逍遥(基础)=3 度丫丫=4 度逍遥(精品)=5003 - 度小鹿=5118 度博文=106 度小童=110 度小萌=111 度米朵=103 度小娇=5, - 如果选择模型为paddlespeech-tts,参数自动失效。 - * **audio_type** (*str* *,* *可选*) -- 音频文件格式,默认是\`mp3\`, - 如果选择\`paddlespeech-tts\`模型,参数只能设为\`wav\`。 - * **timeout** (*float* *,* *可选*) -- HTTP超时时间。 - * **retry** (*int* *,* *可选*) -- HTTP重试次数。 - * **stream** (*bool* *,* *可选*) -- 是否以流的形式返回音频数据,默认为False。 -* **返回:** - 文本转语音结果。举例: Message(content={"audio_binary": b"xxx", "audio_type": "mp3"}) -* **返回类型:** - message ([Message](appbuilder.core.md#appbuilder.core.message.Message)) diff --git a/docs/API-Reference/Python/appbuilder.core.console.appbuilder_client.md b/docs/API-Reference/Python/appbuilder.core.console.appbuilder_client.md deleted file mode 100644 index 6569c7aa4..000000000 --- a/docs/API-Reference/Python/appbuilder.core.console.appbuilder_client.md +++ /dev/null @@ -1,214 +0,0 @@ -# appbuilder.core.console.appbuilder_client package - -## Submodules - -## appbuilder.core.console.appbuilder_client.appbuilder_client module - -AppBuilderClient组件 - -### *class* appbuilder.core.console.appbuilder_client.appbuilder_client.AgentBuilder(app_id: str) - -基类:[`AppBuilderClient`](#appbuilder.core.console.appbuilder_client.appbuilder_client.AppBuilderClient) - -AgentBuilder是继承自AppBuilderClient的一个子类,用于构建和管理智能体应用。 -支持调用在[百度智能云千帆AppBuilder]([https://cloud.baidu.com/product/AppBuilder](https://cloud.baidu.com/product/AppBuilder))平台上 -构建并发布的智能体应用,具体包括创建会话、上传文档、运行对话等。 - -Examples: - -```python -import appbuilder -# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 -os.environ["APPBUILDER_TOKEN"] = '...' -# 可在Console 应用页面获取 -app_id = "app_id" -client = appbuilder.AppBuilderClient("app_id") -conversation_id = client.create_conversation() -file_id = client.upload_local_file(conversation_id, "/path/to/file") -message = client.run(conversation_id, "今天你好吗?") -# 打印对话结果 -print(message.content) -``` - -### *class* appbuilder.core.console.appbuilder_client.appbuilder_client.AppBuilderClient(app_id: str, \*\*kwargs) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -AppBuilderClient 组件支持调用在[百度智能云千帆AppBuilder]([https://cloud.baidu.com/product/AppBuilder](https://cloud.baidu.com/product/AppBuilder))平台上 -构建并发布的智能体应用,具体包括创建会话、上传文档、运行对话等。 - -Examples: - -```python -import appbuilder -# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 -os.environ["APPBUILDER_TOKEN"] = '...' -# 可在Console 应用页面获取 -app_id = "app_id" -client = appbuilder.AppBuilderClient("app_id") -conversation_id = client.create_conversation() -file_id = client.upload_local_file(conversation_id, "/path/to/file") -message = client.run(conversation_id, "今天你好吗?") -# 打印对话结果 -print(message.content) -``` - -#### create_conversation() → str - -创建会话并返回会话ID - -会话ID在服务端用于上下文管理、绑定会话文档等,如需开始新的会话,请创建并使用新的会话ID - -* **参数:** - **无** -* **返回:** - 唯一会话ID -* **返回类型:** - response (str) - -#### run(conversation_id: str, query: str = '', file_ids: list = [], stream: bool = False, tools: list[Tool] = None, tool_outputs: list[ToolOutput] = None, tool_choice: ToolChoice = None, end_user_id: str = None, \*\*kwargs) → [Message](appbuilder.core.md#appbuilder.core.message.Message) - -运行智能体应用 - -* **参数:** - * **query** (*str*) -- query内容 - * **conversation_id** (*str*) -- 唯一会话ID,如需开始新的会话,请使用self.create_conversation创建新的会话 - * **file_ids** (*list* *[**str* *]*) -- 文件ID列表 - * **stream** (*bool*) -- 为True时,流式返回,需要将message.content.answer拼接起来才是完整的回答;为False时,对应非流式返回 - * **tools** (*list* *[**data_class.Tools* *]*) -- 一个Tools组成的列表,其中每个Tools对应一个工具的配置, 默认为None - * **tool_outputs** (*list* *[**data_class.ToolOutput* *]*) -- 工具输出列表,格式为list[ToolOutput], ToolOutputd内容为本地的工具执行结果,以自然语言/json dump str描述,默认为None - * **tool_choice** (*data_class.ToolChoice*) -- 控制大模型使用组件的方式,默认为None - * **end_user_id** (*str*) -- 用户ID,用于区分不同用户 - * **kwargs** -- 其他参数 -* **返回:** - 对话结果,一个Message对象,使用message.content获取内容。 -* **返回类型:** - message ([Message](appbuilder.core.md#appbuilder.core.message.Message)) - -#### run_with_handler(conversation_id: str, query: str = '', file_ids: list = [], tools: list[Tool] | None = None, stream: bool = False, event_handler=None, \*\*kwargs) - -运行智能体应用,并通过事件处理器处理事件 - -* **参数:** - * **conversation_id** (*str*) -- 唯一会话ID,如需开始新的会话,请使用self.create_conversation创建新的会话 - * **query** (*str*) -- 查询字符串 - * **file_ids** (*list*) -- 文件ID列表 - * **tools** (*list* *[**data_class.Tools* *]* *,* *可选*) -- 一个Tools组成的列表,其中每个Tools对应一个工具的配置, 默认为None - * **stream** (*bool*) -- 是否流式响应 - * **event_handler** (*EventHandler*) -- 事件处理器 - * **kwargs** -- 其他参数 -* **返回:** - 事件处理器 -* **返回类型:** - EventHandler - -#### upload_local_file(conversation_id, local_file_path: str) → str - -上传文件并将文件与会话ID进行绑定,后续可使用该文件ID进行对话,目前仅支持上传xlsx、jsonl、pdf、png等文件格式 - -该接口用于在对话中上传文件供大模型处理,文件的有效期为7天并且不超过对话的有效期。一次只能上传一个文件。 - -* **参数:** - * **conversation_id** (*str*) -- 会话ID - * **local_file_path** (*str*) -- 本地文件路径 -* **返回:** - 唯一文件ID -* **返回类型:** - response (str) - -### appbuilder.core.console.appbuilder_client.appbuilder_client.describe_apps(marker: str | None = None, maxKeys: int = 10, secret_key: str | None = None, gateway: str | None = None) → list[AppOverview] - -该接口查询用户下状态为已发布的应用列表 - -* **参数:** - * **maxKeys** (*int* *,* *optional*) -- 返回结果的最大数量,默认值为10,最大为100。 - * **marker** (*str* *,* *optional*) -- 起始位置,即从哪个游标开始查询,默认值为空字符串。 - * **secret_key** (*Optional* *[**str* *]* *,* *optional*) -- 认证密钥。如果未指定,则使用默认的密钥。默认值为None。 - * **gateway** (*Optional* *[**str* *]* *,* *optional*) -- 网关地址。如果未指定,则使用默认的地址。默认值为None。 -* **返回:** - 应用列表。 -* **返回类型:** - DescribeAppsResponse - -### appbuilder.core.console.appbuilder_client.appbuilder_client.get_all_apps() - -获取所有应用列表。 - -* **参数:** - **无参数。** -* **返回:** - 包含所有应用信息的列表,每个元素为一个App对象, - 其中App对象的结构取决于get_app_list函数的返回结果。 -* **返回类型:** - List[App] - -### appbuilder.core.console.appbuilder_client.appbuilder_client.get_app_list(limit: int = 10, after: str = '', before: str = '', secret_key: str | None = None, gateway_v2: str | None = None) → list[AppOverview] - -该接口查询用户下状态为已发布的应用列表 - -* **参数:** - * **limit** (*int* *,* *optional*) -- 返回结果的最大数量,默认值为10。 - * **after** (*str* *,* *optional*) -- 返回结果中第一个应用的游标值,用于分页查询。默认值为空字符串。 - * **before** (*str* *,* *optional*) -- 返回结果中最后一个应用的游标值,用于分页查询。默认值为空字符串。 - * **secret_key** (*Optional* *[**str* *]* *,* *optional*) -- 认证密钥。如果未指定,则使用默认的密钥。默认值为None。 - * **gateway_v2** (*Optional* *[**str* *]* *,* *optional*) -- 网关地址。如果未指定,则使用默认的地址。默认值为None。 -* **返回:** - 应用列表。 -* **返回类型:** - list[data_class.AppOverview] - -## appbuilder.core.console.appbuilder_client.data_class module - -### *class* appbuilder.core.console.appbuilder_client.event_handler.AppBuilderClientRunContext - -基类:`object` - -### *class* appbuilder.core.console.appbuilder_client.event_handler.AppBuilderEventHandler - -基类:`object` - -#### done(run_context, run_response) - -#### error(run_context, run_response) - -#### init(appbuilder_client, conversation_id, query, file_ids=None, tools=None, stream: bool = False, event_handler=None, \*\*kwargs) - -初始化类实例并设置相关参数。 - -* **参数:** - * **appbuilder_client** (*object*) -- AppBuilder客户端实例对象。 - * **conversation_id** (*str*) -- 对话ID。 - * **query** (*str*) -- 用户输入的查询语句。 - * **file_ids** (*list* *,* *optional*) -- 文件ID列表,默认为None。 - * **tools** (*list* *,* *optional*) -- 工具列表,默认为None。 - * **stream** (*bool* *,* *optional*) -- 是否使用流式处理,默认为False。 - * **event_handler** (*callable* *,* *optional*) -- 事件处理函数,默认为None。 - * **\*\*kwargs** -- 其他可选参数。 -* **返回:** - None - -#### interrupt(run_context, run_response) - -#### preparing(run_context, run_response) - -#### reset_state() - -重置该对象的状态,将所有实例变量设置为默认值。 - -* **参数:** - **无** -* **返回:** - 无 - -#### running(run_context, run_response) - -#### success(run_context, run_response) - -#### until_done() - -迭代并遍历内部迭代器中的所有元素,直到迭代器耗尽。 - -* **参数:** - **无参数。** -* **返回:** - 无返回值。 diff --git a/docs/API-Reference/Python/appbuilder.core.console.dataset.md b/docs/API-Reference/Python/appbuilder.core.console.dataset.md deleted file mode 100644 index 82b41e3a0..000000000 --- a/docs/API-Reference/Python/appbuilder.core.console.dataset.md +++ /dev/null @@ -1,120 +0,0 @@ -# appbuilder.core.console.dataset package - -## Submodules - -## appbuilder.core.console.dataset.dataset module - -### *class* appbuilder.core.console.dataset.dataset.Dataset(dataset_id: str = '', dataset_name: str = '') - -基类:`object` - -console知识库操作工具 - -Examples: - -```python -import appbuilder -import os - -os.environ["APPBUILDER_TOKEN"] = '...' - -dataset = appbuilder.Dataset.create_dataset("baidu-test") - -# 上传文档 -file_paths = ["./test.pdf"] -document_infos = dataset.add_documents(file_paths) - -# 获取第一页的文档列表, 每页10条 -document_list = dataset.get_documents(1, 10) - -# 删除文档 -document_ids = [document_infos.document_ids[0]] -dataset.delete_documents(document_ids) -``` - -#### add_documents(file_path_list: List[str], is_custom_process_rule: bool = False, custom_process_rule: Dict = None, is_enhanced: bool = False) → AddDocumentsResponse - -向知识库中添加文档 - -* **参数:** - * **file_path_list** -- 文档路径列表 - * **is_custom_process_rule** -- 是否使用自定义文档处理规则, 默认为False, 使用平台的默认规则,为True时使用自定义规则 - * **custom_process_rule** -- 自定义文档规则,当is_custom_process_rule为True时生效,格式示例如下: - * **{** -- "separators": ["。", ","], # 文本切分符,支持这几种[ , , "?", , "!", "?", "……"] - "target_length": 300, # 文本切片片段长度,取值范围[300, 800] - "overlap_rate": 0.3 # 文本片段重叠率,取值范围[0, 0.3] - * **}** - * **is_enhanced** -- 是否开启知识增强, 默认为False,在检索问答时通过知识点来索引到对应的切片,大模型根据切片内容生成答案,开启知识增强会调用大模型抽取更加丰富的知识点,增加切片的召回率 -* **返回:** - 添加文档的响应结果,包含以下属性: - - dataset_id (str): 知识库id - - document_ids (List[str]): 文档id列表 -* **返回类型:** - AddDocumentsResponse - -#### add_file_url *: str* *= '/v1/ai_engine/agi_platform/v1/datasets/documents'* - -#### *classmethod* create_dataset(dataset_name: str) - -创建知识库 - -* **参数:** - **dataset_name** -- 知识库名称 -* **返回:** - 创建成功的知识库实例 -* **返回类型:** - [Dataset](#appbuilder.core.console.dataset.dataset.Dataset) - -#### create_url *: str* *= '/v1/ai_engine/agi_platform/v1/datasets/create'* - -#### delete_documents(document_ids: List[str]) - -删除知识库中的文档 - -* **参数:** - **document_ids** -- 文档id列表 -* **返回:** - None - -#### delete_file_url *: str* *= '/v1/ai_engine/agi_platform/v1/datasets/document/delete'* - -#### get_documents(page: int, limit: int, keyword: str = '') → DocumentListResponse - -获取知识库中的文档列表 - -* **参数:** - * **page** -- 第几页 - * **limit** -- 每页文档数 - * **keyword** -- 文件名关键字,支持模糊查询 -* **返回:** - DocumentListResponses实例,返回示例: - { - "data": [ - > { - > : "id":"d2d1bc1a-1763-4162-88b2-0dad225da16f", # 文档id - > "name": "唐诗三百首(全集)全新编辑版.pdf", # 文档名称 - > "created_from": "web", # 创建来源 - > "created_by": "76efed91-cf19-435d-993c-cdd901d6d13c", # 创建人 - > "created_at": 1705958975, # 创建时间 - > "indexing_status": "indexing", # 文档处理状态 - > "error": null, # 文档处理错误信息 - > "enabled": true, # 文档是否启用 - > "disabled_at": null, # 文档禁用时间 - > "disabled_by": null, # 文档禁用人 - > "display_status": "indexing", # 文档显示状态,和前端展示状态一致 - > "word_count": 5024 # 文档字数 - - > } - - ], - "has_more": false, # 是否还有下一页 - "limit": 10, # 每页文档数 - "total": 1, # 总页数 - "page": 1 # 当前页 - } - -#### get_file_list_url *: str* *= '/v1/ai_engine/agi_platform/v1/datasets/documents/list_page'* - -#### *property* http_client - -#### upload_file_url *: str* *= '/v1/ai_engine/agi_platform/v1/datasets/files/upload'* diff --git a/docs/API-Reference/Python/appbuilder.core.console.knowledge_base.md b/docs/API-Reference/Python/appbuilder.core.console.knowledge_base.md deleted file mode 100644 index 78f3deacf..000000000 --- a/docs/API-Reference/Python/appbuilder.core.console.knowledge_base.md +++ /dev/null @@ -1,308 +0,0 @@ - - -### *class* appbuilder.core.console.knowledge_base.knowledge_base.KnowledgeBase(knowledge_id: str | None = None, knowledge_name: str | None = None, \*\*kwargs) - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -console知识库操作工具,用于创建、删除、查询、更新知识库等操作 - -Examples: - -```python -import os -import appbuilder -os.environ["APPBUILDER_TOKEN"] = "your_appbuilder_token" - -my_knowledge_base_id = "your_knowledge_base_id" -my_knowledge = appbuilder.KnowledgeBase(my_knowledge_base_id) -print("知识库ID: ", my_knowledge.knowledge_id) - -list_res = my_knowledge.get_documents_list() -print("文档列表: ", list_res) -``` - -#### add_document(content_type: str, file_ids: list[str] = [], is_enhanced: bool = False, custom_process_rule: CustomProcessRule | None = None, knowledge_base_id: str | None = None, client_token: str | None = None) → KnowledgeBaseAddDocumentResponse - -添加文档到知识库 - -* **参数:** - * **content_type** (*str*) -- 内容类型,可选值有"raw_text", "qa"。 - * **file_ids** (*List* *[**str* *]* *,* *optional*) -- 文件ID列表。默认为空列表。 - * **is_enhanced** (*bool* *,* *optional*) -- 是否增强。默认为False。 - * **custom_process_rule** (*Optional* *[**data_class.CustomProcessRule* *]* *,* *optional*) -- 自定义处理规则。默认为None。 - * **knowledge_base_id** (*Optional* *[**str* *]* *,* *optional*) -- 知识库ID。默认为None,此时使用当前类的knowledge_id属性。 - * **client_token** (*str*) -- 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 -* **返回:** - 添加文档的相应结果。包含以下属性: - - request_id (str): 请求ID - - knowledge_base_id (str): 知识库ID - - document_ids (list[str]): 成功新建的文档id集合 -* **返回类型:** - KnowledgeBaseAddDocumentResponse - -#### create_chunk(documentId: str, content: str, client_token: str | None = None) → CreateChunkResponse - -创建文档块 - -* **参数:** - * **documentId** (*str*) -- 文档ID - * **content** (*str*) -- 内容 - * **client_token** (*str* *,* *optional*) -- 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 -* **返回:** - 创建文档块的相应消息, 包含以下属性: - - id (str): 切片ID -* **返回类型:** - CreateChunkResponse - -#### create_documents(id: str | None = None, contentFormat: str = '', source: DocumentSource | None = None, processOption: DocumentProcessOption | None = None, client_token: str | None = None) → KnowledgeBaseCreateDocumentsResponse - -创建文档 - -* **参数:** - * **id** (*Optional* *[**str* *]* *,* *optional*) -- 知识库ID,如果不指定则使用当前实例的knowledge_id属性。默认值为None。 - * **contentFormat** (*str* *,* *optional*) -- 文档内容格式,可以是"rawText"。默认值为""。 - * **source** (*data_class.DocumentSource* *,* *optional*) -- 文档源数据。默认值为None。 - * **processOption** (*data_class.DocumentProcessOption* *,* *optional*) -- 文档处理选项。默认值为None。 - * **client_token** (*str* *,* *optional*) -- 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 -* **返回:** - 创建知识库文档的响应消息,返回一个KnowledgeBaseCreateDocumentsResponse对象,包含以下属性: - - requestId (str): 请求ID - - documentIds (list[str]): 文档ID列表 -* **返回类型:** - KnowledgeBaseCreateDocumentsResponse - -#### *classmethod* create_knowledge(knowledge_name: str) → [KnowledgeBase](#appbuilder.core.console.knowledge_base.knowledge_base.KnowledgeBase) - -创建知识库 - -Deprecated: use create_knowledge_base instead - -* **参数:** - **knowledge_name** (*str*) -- 知识库名称 -* **返回:** - 返回一个KnowledgeBase对象 -* **返回类型:** - [KnowledgeBase](#appbuilder.core.console.knowledge_base.knowledge_base.KnowledgeBase) - -#### create_knowledge_base(name: str, description: str, type: str = 'public', esUrl: str | None = None, esUserName: str | None = None, esPassword: str | None = None, client_token: str | None = None) → KnowledgeBaseDetailResponse - -创建知识库 - -* **参数:** - * **name** (*str*) -- 知识库名称。 - * **description** (*str*) -- 知识库描述。 - * **type** (*str* *,* *optional*) -- 知识库类型。默认为"public"。 - * **esUrl** (*str* *,* *optional*) -- Elasticsearch服务器地址。默认为None。 - * **esUserName** (*str* *,* *optional*) -- Elasticsearch用户名。默认为None。 - * **esPassword** (*str* *,* *optional*) -- Elasticsearch密码。默认为None。 - * **client_token** (*str* *,* *optional*) -- 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 -* **返回:** - 创建知识库的响应消息,包含以下属性: - - id (str): 知识库ID - - name (str): 知识库名称 - - description (Optional[str], optional): 知识库描述 - - config (Optional[KnowledgeBaseConfig], optional): 知识库配置 -* **返回类型:** - KnowledgeBaseDetailResponse - -#### delete_chunk(chunkId: str, client_token: str | None = None) - -删除文档块 - -* **参数:** - * **chunkId** (*str*) -- 文档块ID - * **client_token** (*str* *,* *optional*) -- 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 -* **返回:** - 响应数据,包含请求ID: requestId -* **返回类型:** - dict - -#### delete_document(document_id: str, knowledge_base_id: str | None = None, client_token: str | None = None) → KnowledgeBaseDeleteDocumentResponse - -删除知识库中的文档 - -* **参数:** - * **document_id** (*str*) -- 文档ID - * **knowledge_base_id** (*Optional* *[**str* *]* *,* *optional*) -- 知识库ID。默认为None,此时使用当前类的knowledge_id属性。 - * **client_token** (*str*) -- 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 -* **返回:** - 删除文档的响应消息,包含以下属性: - - request_id (str): 请求ID -* **返回类型:** - KnowledgeBaseDeleteDocumentResponse - -#### delete_knowledge_base(knowledge_base_id: str | None = None, client_token: str | None = None) - -删除知识库 - -* **参数:** - * **knowledge_base_id** (*Optional* *[**str* *]* *,* *optional*) -- 知识库ID,如果不指定则使用当前实例的knowledge_id属性。默认值为None。 - * **client_token** (*str* *,* *optional*) -- 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 -* **返回:** - requestId -* **返回类型:** - 响应数据,包含请求ID - -#### describe_chunk(chunkId: str) → DescribeChunkResponse - -获取文档块详情 - -* **参数:** - **chunkId** (*str*) -- 文档块ID -* **返回:** - 文档块详情,一个DescribeChunkResponse对象,包含以下属性: - - id (str): 切片ID - - type (str): 切片类型 - - knowledgeBaseId (str): 知识库ID - - documentId (str): 文档ID - - content (str): 文档内容 - - enabled (bool): 是否启用 - - wordCount (int): 切片内字符数量 - - tokenCount (int): 切片内token数量 - - status (str): 切片状态 - - statusMessage (str): 切片状态信息 - - createTime (int): 创建时间 - - updateTime (int): 更新时间 -* **返回类型:** - DescribeChunkResponse - -#### describe_chunks(documentId: str, marker: str | None = None, maxKeys: int | None = None, type: str | None = None) → DescribeChunksResponse - -获取文档块列表 - -* **参数:** - * **documentId** (*str*) -- 文档ID - * **marker** (*str* *,* *optional*) -- 分页标记,用于指定从哪个位置开始返回结果。默认为None,表示从头开始返回结果。 - * **maxKeys** (*int* *,* *optional*) -- 最大返回数量,用于限制每次请求返回的最大文档块数目。默认为None,表示不限制返回数量。 - * **type** (*str* *,* *optional*) -- 文档块类型。默认为None,表示不限定类型。 -* **返回:** - 文档块列表,一个DescribeChunksResponse对象,包含以下属性: - - data (list[DescribeChunkResponse]): 切片列表 - - marker (str): 起始位置 - - isTruncated (bool): true表示后面还有数据,false表示已经是最后一页 - - nextMarker (str): 下一页起始位置 - - maxKeys (int): 本次查询包含的最大结果集数量 -* **返回类型:** - DescribeChunksResponse - -#### get_all_documents(knowledge_base_id: str | None = None) → list - -获取知识库中所有文档。 - -* **参数:** - **knowledge_base_id** (*Optional* *[**str* *]* *,* *optional*) -- 知识库的ID。如果为None,则使用当前实例的knowledge_id。默认为None。 -* **返回:** - 包含所有文档的列表。 -* **返回类型:** - list -* **抛出:** - **ValueError** -- 如果knowledge_base_id为空,且当前实例没有已创建的knowledge_id时抛出。 - -#### get_documents_list(limit: int = 10, after: str | None = '', before: str | None = '', knowledge_base_id: str | None = None) → KnowledgeBaseGetDocumentsListResponse - -获取知识库中的文档列表 - -* **参数:** - * **limit** (*int* *,* *optional*) -- 限制数量。默认为10。 - * **after** (*Optional* *[**str* *]* *,* *optional*) -- 起始位置。默认为空字符串""。 - * **before** (*Optional* *[**str* *]* *,* *optional*) -- 结束位置。默认为空字符串""。 - * **knowledge_base_id** (*Optional* *[**str* *]* *,* *optional*) -- 知识库ID。默认为None,此时使用当前类的knowledge_id属性。 -* **返回:** - 知识库文档列表服务的响应消息,包含以下属性: - - request_id (str): 请求ID - - data (list[Document]): 文档信息列表 -* **返回类型:** - KnowledgeBaseGetDocumentsListResponse - -#### get_knowledge_base_detail(knowledge_base_id: str | None = None) → KnowledgeBaseDetailResponse - -获取知识库详情 - -* **参数:** - **knowledge_base_id** (*Optional* *[**str* *]* *,* *optional*) -- 知识库ID,如果不指定则使用当前实例的knowledge_id属性。默认值为None。 -* **返回:** - 知识库详情,返回一个KnowledgeBaseDetailResponse对象,包含以下属性: - - id (str): 知识库ID - - name(str): 知识库名称 - - description(Optional[str], optional): 知识库描述 - - config(Optional[KnowledgeBaseConfig], optional): 知识库配置 -* **返回类型:** - KnowledgeBaseDetailResponse - -#### get_knowledge_base_list(knowledge_base_id: str | None = None, maxKeys: int = 10, keyword: str | None = None) → KnowledgeBaseGetListResponse - -获取知识库列表 - -* **参数:** - * **knowledge_base_id** (*Optional* *[**str* *]* *,* *optional*) -- 知识库ID,如果不指定则使用当前实例的knowledge_id属性。默认值为None。 - * **maxKeys** (*int* *,* *optional*) -- 最大键数。默认值为10。 - * **keyword** (*Optional* *[**str* *]* *,* *optional*) -- 关键字。默认值为None。 -* **返回:** - 获取知识库列表的响应消息,返回一个KnowledgeBaseGetListResponse对象,包含以下属性: - - requestId (str): 请求ID - - data (list[KnowledgeBaseDetailResponse]): 知识库详情列表 - - marker (str): 起始位置 - - nextMarker (str): 下一页起始位置 - - maxKeys (int): 返回文档数量大小,默认10,最大值100 - - isTruncated (bool): 是否有更多结果 -* **返回类型:** - KnowledgeBaseGetListResponse - -#### modify_chunk(chunkId: str, content: str, enable: bool, client_token: str | None = None) - -修改文档块 - -* **参数:** - * **chunkId** (*str*) -- 文档块ID - * **content** (*str*) -- 内容 - * **enable** (*bool*) -- 是否启用 -* **返回:** - 响应数据,包含请求ID: requestId -* **返回类型:** - dict - -#### modify_knowledge_base(knowledge_base_id: str | None = None, name: str | None = None, description: str | None = None, client_token: str | None = None) - -修改知识库信息 - -* **参数:** - * **knowledge_base_id** (*Optional* *[**str* *]* *,* *optional*) -- 知识库ID,如果不指定则使用当前实例的knowledge_id属性。默认值为None。 - * **name** (*Optional* *[**str* *]* *,* *optional*) -- 新的知识库名称。默认值为None。 - * **description** (*Optional* *[**str* *]* *,* *optional*) -- 新的知识库描述。默认值为None。 - * **client_token** (*str* *,* *optional*) -- 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 -* **返回:** - 响应数据,包含请求ID: requestId。 -* **返回类型:** - dict - -#### upload_documents(file_path: str, content_format: str = 'rawText', id: str | None = None, processOption: DocumentProcessOption | None = None, client_token: str | None = None) → KnowledgeBaseUploadDocumentsResponse - -上传文档 - -* **参数:** - * **file_path** (*str*) -- 文件路径 - * **content_format** (*str* *,* *optional*) -- 内容格式。默认值为"rawText"。 - * **id** (*Optional* *[**str* *]* *,* *optional*) -- 知识库ID,如果不指定则使用当前实例的knowledge_id属性。默认值为None。 - * **processOption** (*data_class.DocumentProcessOption* *,* *optional*) -- 文档处理选项。默认值为None。 - * **client_token** (*str* *,* *optional*) -- 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 -* **返回:** - 创建知识库文档的响应消息,返回一个KnowledgeBaseUploadDocumentsResponse对象,包含以下属性: - - requestId (str): 请求ID - - documentId (str): 文档ID -* **返回类型:** - KnowledgeBaseUploadDocumentsResponse - -#### upload_file(file_path: str, client_token: str | None = None) → KnowledgeBaseUploadFileResponse - -上传文件到知识库 - -* **参数:** - * **file_path** (*str*) -- 文件路径 - * **client_token** (*str* *,* *optional*) -- 客户端令牌。默认为None,此时会自动生成一个随机UUID作为客户端令牌。 -* **返回:** - 返回一个KnowledgeBaseUploadFileResponse对象,包含以下属性: - - request_id (int): 请求id - - id (str): 文件id - - name (dict): 文件名称 -* **返回类型:** - KnowledgeBaseUploadFileResponse diff --git a/docs/API-Reference/Python/appbuilder.core.console.md b/docs/API-Reference/Python/appbuilder.core.console.md deleted file mode 100644 index c81532990..000000000 --- a/docs/API-Reference/Python/appbuilder.core.console.md +++ /dev/null @@ -1,72 +0,0 @@ -# appbuilder.core.console package - -## Subpackages - -* [appbuilder.core.console.appbuilder_client package](appbuilder.core.console.appbuilder_client.md) - * [Submodules](appbuilder.core.console.appbuilder_client.md#submodules) - * [appbuilder.core.console.appbuilder_client.appbuilder_client module](appbuilder.core.console.appbuilder_client.md#module-appbuilder.core.console.appbuilder_client.appbuilder_client) - * [`AgentBuilder`](appbuilder.core.console.appbuilder_client.md#appbuilder.core.console.appbuilder_client.appbuilder_client.AgentBuilder) - * [`AppBuilderClient`](appbuilder.core.console.appbuilder_client.md#appbuilder.core.console.appbuilder_client.appbuilder_client.AppBuilderClient) - * [`AppBuilderClient.create_conversation()`](appbuilder.core.console.appbuilder_client.md#appbuilder.core.console.appbuilder_client.appbuilder_client.AppBuilderClient.create_conversation) - * [`AppBuilderClient.run()`](appbuilder.core.console.appbuilder_client.md#appbuilder.core.console.appbuilder_client.appbuilder_client.AppBuilderClient.run) - * [`AppBuilderClient.run_with_handler()`](appbuilder.core.console.appbuilder_client.md#appbuilder.core.console.appbuilder_client.appbuilder_client.AppBuilderClient.run_with_handler) - * [`AppBuilderClient.upload_local_file()`](appbuilder.core.console.appbuilder_client.md#appbuilder.core.console.appbuilder_client.appbuilder_client.AppBuilderClient.upload_local_file) - * [`describe_apps()`](appbuilder.core.console.appbuilder_client.md#appbuilder.core.console.appbuilder_client.appbuilder_client.describe_apps) - * [`get_all_apps()`](appbuilder.core.console.appbuilder_client.md#appbuilder.core.console.appbuilder_client.appbuilder_client.get_all_apps) - * [`get_app_list()`](appbuilder.core.console.appbuilder_client.md#appbuilder.core.console.appbuilder_client.appbuilder_client.get_app_list) - * [appbuilder.core.console.appbuilder_client.data_class module](appbuilder.core.console.appbuilder_client.md#module-appbuilder.core.console.appbuilder_client.event_handler) - * [`AppBuilderClientRunContext`](appbuilder.core.console.appbuilder_client.md#appbuilder.core.console.appbuilder_client.event_handler.AppBuilderClientRunContext) - * [`AppBuilderEventHandler`](appbuilder.core.console.appbuilder_client.md#appbuilder.core.console.appbuilder_client.event_handler.AppBuilderEventHandler) - * [`AppBuilderEventHandler.done()`](appbuilder.core.console.appbuilder_client.md#appbuilder.core.console.appbuilder_client.event_handler.AppBuilderEventHandler.done) - * [`AppBuilderEventHandler.error()`](appbuilder.core.console.appbuilder_client.md#appbuilder.core.console.appbuilder_client.event_handler.AppBuilderEventHandler.error) - * [`AppBuilderEventHandler.init()`](appbuilder.core.console.appbuilder_client.md#appbuilder.core.console.appbuilder_client.event_handler.AppBuilderEventHandler.init) - * [`AppBuilderEventHandler.interrupt()`](appbuilder.core.console.appbuilder_client.md#appbuilder.core.console.appbuilder_client.event_handler.AppBuilderEventHandler.interrupt) - * [`AppBuilderEventHandler.preparing()`](appbuilder.core.console.appbuilder_client.md#appbuilder.core.console.appbuilder_client.event_handler.AppBuilderEventHandler.preparing) - * [`AppBuilderEventHandler.reset_state()`](appbuilder.core.console.appbuilder_client.md#appbuilder.core.console.appbuilder_client.event_handler.AppBuilderEventHandler.reset_state) - * [`AppBuilderEventHandler.running()`](appbuilder.core.console.appbuilder_client.md#appbuilder.core.console.appbuilder_client.event_handler.AppBuilderEventHandler.running) - * [`AppBuilderEventHandler.success()`](appbuilder.core.console.appbuilder_client.md#appbuilder.core.console.appbuilder_client.event_handler.AppBuilderEventHandler.success) - * [`AppBuilderEventHandler.until_done()`](appbuilder.core.console.appbuilder_client.md#appbuilder.core.console.appbuilder_client.event_handler.AppBuilderEventHandler.until_done) -* [appbuilder.core.console.dataset package](appbuilder.core.console.dataset.md) - * [Submodules](appbuilder.core.console.dataset.md#submodules) - * [appbuilder.core.console.dataset.dataset module](appbuilder.core.console.dataset.md#module-appbuilder.core.console.dataset.dataset) - * [`Dataset`](appbuilder.core.console.dataset.md#appbuilder.core.console.dataset.dataset.Dataset) - * [`Dataset.add_documents()`](appbuilder.core.console.dataset.md#appbuilder.core.console.dataset.dataset.Dataset.add_documents) - * [`Dataset.add_file_url`](appbuilder.core.console.dataset.md#appbuilder.core.console.dataset.dataset.Dataset.add_file_url) - * [`Dataset.create_dataset()`](appbuilder.core.console.dataset.md#appbuilder.core.console.dataset.dataset.Dataset.create_dataset) - * [`Dataset.create_url`](appbuilder.core.console.dataset.md#appbuilder.core.console.dataset.dataset.Dataset.create_url) - * [`Dataset.delete_documents()`](appbuilder.core.console.dataset.md#appbuilder.core.console.dataset.dataset.Dataset.delete_documents) - * [`Dataset.delete_file_url`](appbuilder.core.console.dataset.md#appbuilder.core.console.dataset.dataset.Dataset.delete_file_url) - * [`Dataset.get_documents()`](appbuilder.core.console.dataset.md#appbuilder.core.console.dataset.dataset.Dataset.get_documents) - * [`Dataset.get_file_list_url`](appbuilder.core.console.dataset.md#appbuilder.core.console.dataset.dataset.Dataset.get_file_list_url) - * [`Dataset.http_client`](appbuilder.core.console.dataset.md#appbuilder.core.console.dataset.dataset.Dataset.http_client) - * [`Dataset.upload_file_url`](appbuilder.core.console.dataset.md#appbuilder.core.console.dataset.dataset.Dataset.upload_file_url) -* [`KnowledgeBase`](appbuilder.core.console.knowledge_base.md) - * [`KnowledgeBase.add_document()`](appbuilder.core.console.knowledge_base.md#appbuilder.core.console.knowledge_base.knowledge_base.KnowledgeBase.add_document) - * [`KnowledgeBase.create_chunk()`](appbuilder.core.console.knowledge_base.md#appbuilder.core.console.knowledge_base.knowledge_base.KnowledgeBase.create_chunk) - * [`KnowledgeBase.create_documents()`](appbuilder.core.console.knowledge_base.md#appbuilder.core.console.knowledge_base.knowledge_base.KnowledgeBase.create_documents) - * [`KnowledgeBase.create_knowledge()`](appbuilder.core.console.knowledge_base.md#appbuilder.core.console.knowledge_base.knowledge_base.KnowledgeBase.create_knowledge) - * [`KnowledgeBase.create_knowledge_base()`](appbuilder.core.console.knowledge_base.md#appbuilder.core.console.knowledge_base.knowledge_base.KnowledgeBase.create_knowledge_base) - * [`KnowledgeBase.delete_chunk()`](appbuilder.core.console.knowledge_base.md#appbuilder.core.console.knowledge_base.knowledge_base.KnowledgeBase.delete_chunk) - * [`KnowledgeBase.delete_document()`](appbuilder.core.console.knowledge_base.md#appbuilder.core.console.knowledge_base.knowledge_base.KnowledgeBase.delete_document) - * [`KnowledgeBase.delete_knowledge_base()`](appbuilder.core.console.knowledge_base.md#appbuilder.core.console.knowledge_base.knowledge_base.KnowledgeBase.delete_knowledge_base) - * [`KnowledgeBase.describe_chunk()`](appbuilder.core.console.knowledge_base.md#appbuilder.core.console.knowledge_base.knowledge_base.KnowledgeBase.describe_chunk) - * [`KnowledgeBase.describe_chunks()`](appbuilder.core.console.knowledge_base.md#appbuilder.core.console.knowledge_base.knowledge_base.KnowledgeBase.describe_chunks) - * [`KnowledgeBase.get_all_documents()`](appbuilder.core.console.knowledge_base.md#appbuilder.core.console.knowledge_base.knowledge_base.KnowledgeBase.get_all_documents) - * [`KnowledgeBase.get_documents_list()`](appbuilder.core.console.knowledge_base.md#appbuilder.core.console.knowledge_base.knowledge_base.KnowledgeBase.get_documents_list) - * [`KnowledgeBase.get_knowledge_base_detail()`](appbuilder.core.console.knowledge_base.md#appbuilder.core.console.knowledge_base.knowledge_base.KnowledgeBase.get_knowledge_base_detail) - * [`KnowledgeBase.get_knowledge_base_list()`](appbuilder.core.console.knowledge_base.md#appbuilder.core.console.knowledge_base.knowledge_base.KnowledgeBase.get_knowledge_base_list) - * [`KnowledgeBase.modify_chunk()`](appbuilder.core.console.knowledge_base.md#appbuilder.core.console.knowledge_base.knowledge_base.KnowledgeBase.modify_chunk) - * [`KnowledgeBase.modify_knowledge_base()`](appbuilder.core.console.knowledge_base.md#appbuilder.core.console.knowledge_base.knowledge_base.KnowledgeBase.modify_knowledge_base) - * [`KnowledgeBase.upload_documents()`](appbuilder.core.console.knowledge_base.md#appbuilder.core.console.knowledge_base.knowledge_base.KnowledgeBase.upload_documents) - * [`KnowledgeBase.upload_file()`](appbuilder.core.console.knowledge_base.md#appbuilder.core.console.knowledge_base.knowledge_base.KnowledgeBase.upload_file) -* [appbuilder.core.console.rag package](appbuilder.core.console.rag.md) - * [Submodules](appbuilder.core.console.rag.md#submodules) - * [appbuilder.core.console.rag.rag module](appbuilder.core.console.rag.md#module-appbuilder.core.console.rag.rag) - * [`RAG`](appbuilder.core.console.rag.md#appbuilder.core.console.rag.rag.RAG) - * [`RAG.debug()`](appbuilder.core.console.rag.md#appbuilder.core.console.rag.rag.RAG.debug) - * [`RAG.http_client`](appbuilder.core.console.rag.md#appbuilder.core.console.rag.rag.RAG.http_client) - * [`RAG.integrated_url`](appbuilder.core.console.rag.md#appbuilder.core.console.rag.rag.RAG.integrated_url) - * [`RAG.name`](appbuilder.core.console.rag.md#appbuilder.core.console.rag.rag.RAG.name) - * [`RAG.run()`](appbuilder.core.console.rag.md#appbuilder.core.console.rag.rag.RAG.run) - -## Submodules diff --git a/docs/API-Reference/Python/appbuilder.core.console.rag.md b/docs/API-Reference/Python/appbuilder.core.console.rag.md deleted file mode 100644 index 7ee93457a..000000000 --- a/docs/API-Reference/Python/appbuilder.core.console.rag.md +++ /dev/null @@ -1,63 +0,0 @@ -# appbuilder.core.console.rag package - -## Submodules - -## appbuilder.core.console.rag.rag module - -### *class* appbuilder.core.console.rag.rag.RAG(app_id: str = '') - -基类:[`Component`](appbuilder.core.md#appbuilder.core.component.Component) - -console RAG组件,利用console端RAG应用进行问答 - -Examples: - -```python -import appbuilder -import os - -os.environ["APPBUILDER_TOKEN"] = '...' -conversation_id = '...' -app_id = '...' # 线上知识库ID -conversation_id = '...' # 会话ID,可选参数,不传默认新建会话 -rag_app = appbuilder.console.RAG(app_id) -query = "中国的首都在哪里" -answer = rag_app.run(appbuilder.Message(query)) # 新建会话 -print(answer) -conversation_id = answer.conversation_id # 获取会话ID,可用于下次会话 -print(conversation_id) -query = "它有哪些旅游景点" -answer = rag_app.run(appbuilder.Message(query), conversation_id) # 接上次会话 -print(answer.content) -print(answer.extra) # 获取结果来源 -``` - -#### debug(query: [Message](appbuilder.core.md#appbuilder.core.message.Message)) - -#### *property* http_client - -获取 HTTP 客户端实例。 - -* **参数:** - **无** -* **返回:** - HTTP 客户端实例。 -* **返回类型:** - HTTPClient - -#### integrated_url *: str* *= '/v1/ai_engine/agi_platform/v1/instance/integrated'* - -#### name *= 'rag'* - -#### run(query: [Message](appbuilder.core.md#appbuilder.core.message.Message), conversation_id: str = '', stream: bool = False) → [Message](appbuilder.core.md#appbuilder.core.message.Message) - -RAG问答 - -* **参数:** - * **query** -- 用户输入的文本 - * **stream** -- 是否开启流式模式 - * **conversation_id** -- 会话ID,不传表示新建对话 -* **返回:** - rag答案 -* **返回类型:** - [Message](appbuilder.core.md#appbuilder.core.message.Message) diff --git a/docs/API-Reference/Python/appbuilder.core.md b/docs/API-Reference/Python/appbuilder.core.md deleted file mode 100644 index 6b00f0fdc..000000000 --- a/docs/API-Reference/Python/appbuilder.core.md +++ /dev/null @@ -1,515 +0,0 @@ -# appbuilder.core package - -## Subpackages - -* [appbuilder.core.assistant package](appbuilder.core.assistant.md) - * [Subpackages](appbuilder.core.assistant.md#subpackages) - * [Submodules](appbuilder.core.assistant.md#submodules) -* [appbuilder.core.components package](appbuilder.core.components.md) - * [Subpackages](appbuilder.core.components.md#subpackages) -* [appbuilder.core.console package](appbuilder.core.console.md) - * [Subpackages](appbuilder.core.console.md#subpackages) - * [Submodules](appbuilder.core.console.md#submodules) - -## Submodules - -## appbuilder.core.agent module - -### *class* appbuilder.core.agent.AgentRuntime(\*, component: [Component](#appbuilder.core.component.Component), user_session_config: Any | str | None = None, user_session: Any | None = None, tool_choice: ToolChoice = None) - -基类:`BaseModel` - -AgentRuntime 是对组件调用的服务化封装,开发者不是必须要用 AgentRuntime 才能运行自己的组件服务。 -但 AgentRuntime 可以快速帮助开发者服务化组件服务,并且提供API、对话框等部署方式。 -此外,结合 Component 和 Message 自带的运行和调试接口,可以方便开发者快速获得一个调试 Agent 的服务。 - -* **参数:** - * **component** ([*Component*](#appbuilder.core.component.Component)) -- 可运行的 Component, 需要实现 run(message, stream, args) 方法 - * **user_session_config** (*sqlalchemy.engine.URL* *|**str* *|**None*) -- Session 输出存储配置字符串。默认使用 sqlite:///user_session.db - 遵循 sqlalchemy 后端定义,参考文档:https://docs.sqlalchemy.org/en/20/core/engines.html#backend-specific-urls - * **tool_choice** (*ToolChoice*) -- 可用于Agent强制执行的组件工具 - -### 示例 - -```python -import os -import sys -import appbuilder -os.environ["APPBUILDER_TOKEN"] = '...' - -component = appbuilder.Playground( - prompt_template="{query}", - model="eb-4" -) -agent = appbuilder.AgentRuntime(component=component) -message = appbuilder.Message({"query": "你好"}) -print(agent.chat(message, stream=False)) -``` - -```python -import os -import sys -import appbuilder -os.environ["APPBUILDER_TOKEN"] = '...' - -component = appbuilder.Playground( - prompt_template="{query}", - model="eb-4" -) -user_session_config = "sqlite:///foo.db" -agent = appbuilder.AgentRuntime( - component=component, user_session_config=user_session_config) -agent.serve(debug=False, port=8091) -``` - -```python -import os -import sys -import appbuilder -os.environ["APPBUILDER_TOKEN"] = '...' - -component = appbuilder.Playground( - prompt_template="{query}", - model="eb-4" -) -agent = appbuilder.AgentRuntime(component=component) -agent.chainlit_demo(port=8091) -``` - -Session 数据管理 : 除去上述简单应用外,还支持 Session 数据管理,下面是一个例子 - -```python -import os -import sys -from appbuilder.core.component import Component -from appbuilder import ( - AgentRuntime, UserSession, Message, QueryRewrite, Playground, -) - -os.environ["APPBUILDER_TOKEN"] = '...' - -class PlaygroundWithHistory(Component): - def __init__(self): - super().__init__() - self.query_rewrite = QueryRewrite(model="Qianfan-Agent-Speed-8k") - self.play = Playground( - prompt_template="{query}", - model="eb-4" - ) - - def run(self, message: Message, stream: bool=False): - user_session = UserSession() - # 获取 Session 历史数据 - history_queries = user_session.get_history("query", limit=1) - history_answers = user_session.get_history("answer", limit=1) - - if history_queries and history_answers: - history = [] - for query, answer in zip(history_queries, history_answers): - history.extend([query.content, answer.content]) - logging.info(f"history: {history}") - message = self.query_rewrite( - Message(history + [message.content]), rewrite_type="带机器人回复") - logging.info(f"message: {message}") - answer = self.play.run(message, stream) - # 保存本轮数据 - user_session.append({ - "query": message, - "answer": answer, - }) - return answer - -agent = AgentRuntime(component=PlaygroundWithHistory()) -agent.chainlit_demo(port=8091) -``` - -请求时认证 : component在创建时可以不进行认证,由AgentRuntime服务化后带入AppbuilderToken - -```python -import appbuilder - -component = appbuilder.Playground( - prompt_template="{query}", - model="eb-4", - lazy_certification=True, # 在创建时不进行认证 -) -agent = appbuilder.AgentRuntime(component=component) -agent.serve(debug=False, port=8091) -``` - -```shell -curl --location 'http://0.0.0.0:8091/chat' \ - --header 'Content-Type: application/json' \ - --header 'X-Appbuilder-Token: ...' \ - --data '{ - "message": "你是谁", - "stream": false - }' -``` - -Session 信息查看 : 查看本地user_session.db数据库内部信息,下面是一个例子 - -```python -import sqlite3 -import json - -# 连接到 SQLite 数据库 -# 如果文件不存在,会自动在当前目录创建: -user_session_path = 'your_user_session.db地址' -conn = sqlite3.connect(user_session_path) -cursor = conn.cursor() - -# 执行一条 SQL 语句,列出所有表 -cursor.execute("SELECT name FROM sqlite_master WHERE type='table';") -print(cursor.fetchall()) - -# 查询appbuilder_session_messages表的列信息 -cursor.execute("PRAGMA table_info(appbuilder_session_messages);") -columns_info = cursor.fetchall() - -column_names = [info[1] for info in columns_info] # info[1]是列名的位置 -for column_name in column_names: - print(column_name) - -# 查询特定表中的数据 -cursor.execute("SELECT message_value FROM appbuilder_session_messages;") -for row in cursor.fetchall(): - print(json.loads(row[0])) - -# 关闭 Connection: -conn.close() -``` - -#### *class* Config - -基类:`object` - -检查配置 - -#### extra - -额外属性,默认为 Extra.forbid,即禁止添加任何额外的属性 - -* **Type:** - Extra - -#### arbitrary_types_allowed - -任意类型是否允许,默认为 True - -* **Type:** - bool - -#### arbitrary_types_allowed *= True* - -#### extra *= 'forbid'* - -#### chainlit_agent(host='0.0.0.0', port=8091) - -将 appbuilder client 服务化,提供 chainlit demo 页面 - -* **参数:** - * **host** (*str*) -- 服务 host - * **port** (*int*) -- 服务 port -* **返回:** - None - -#### chainlit_demo(host='0.0.0.0', port=8091) - -将 component 服务化,提供 chainlit demo 页面 - -* **参数:** - * **host** (*str*) -- 服务 host - * **port** (*int*) -- 服务 port -* **返回:** - None - -#### chat(message: [Message](#appbuilder.core.message.Message), stream: bool = False, \*\*args) → [Message](#appbuilder.core.message.Message) - -执行一次对话 - -* **参数:** - * **message** ([*Message*](#appbuilder.core.message.Message)) -- 该次对话用户输入的 Message - * **stream** (*bool*) -- 是否流式请求 - * **\*\*args** -- 其他参数,会被透传到 component -* **返回:** - 返回的 Message -* **返回类型:** - [Message](#appbuilder.core.message.Message)([Message](#appbuilder.core.message.Message)) - -#### component *: [Component](#appbuilder.core.component.Component)* - -#### create_flask_app(url_rule='/chat') - -创建 Flask 应用,主要用于 Gunicorn 这样的 WSGI 服务器来运行服务。 - -* **参数:** - **None** -* **返回:** - Flask - -#### *classmethod* init(values: Dict) → Dict - -初始化 AgentRuntime,UserSession 会在这里被初始化 - -* **参数:** - **values** (*Dict*) -- 初始化参数 -* **返回:** - None - -#### model_computed_fields *: ClassVar[dict[str, ComputedFieldInfo]]* *= {}* - -A dictionary of computed field names and their corresponding ComputedFieldInfo objects. - -#### model_config *: ClassVar[ConfigDict]* *= {'arbitrary_types_allowed': True, 'extra': 'forbid'}* - -Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict]. - -#### model_fields *: ClassVar[dict[str, FieldInfo]]* *= {'component': FieldInfo(annotation=Component, required=True), 'tool_choice': FieldInfo(annotation=ToolChoice, required=False, default=None), 'user_session': FieldInfo(annotation=Union[Any, NoneType], required=False, default=None), 'user_session_config': FieldInfo(annotation=Union[Any, str, NoneType], required=False, default=None)}* - -Metadata about the fields defined on the model, -mapping of field names to [FieldInfo][pydantic.fields.FieldInfo]. - -This replaces Model._\_fields_\_ from Pydantic V1. - -#### prepare_chainlit_readme() - -准备 Chainlit 的 README 文件 - -* **参数:** - **无** -* **返回:** - 无 -* **抛出:** - **无** -- - -说明: -: 从 utils 文件夹中拷贝 chainlit.md 文件到当前工作目录下,如果当前工作目录下已存在 chainlit.md 文件,则不拷贝。 - -#### serve(host='0.0.0.0', debug=True, port=8092, url_rule='/chat') - -将 component 服务化,提供 Flask http API 接口 - -* **参数:** - * **host** (*str*) -- 服务运行的host地址,默认为'0.0.0.0' - * **debug** (*bool*) -- 是否开启debug模式,默认为True - * **port** (*int*) -- 服务运行的端口号,默认为8092 - * **url_rule** (*str*) -- 服务的URL规则,默认为"/chat" -* **返回:** - None - -#### tool_choice *: ToolChoice* - -#### user_session *: Any | None* - -#### user_session_config *: Any | str | None* - -## appbuilder.core.message module - -### *class* appbuilder.core.message.Message(content: \_T | None = None, \*, name: str | None = 'msg', mtype: str | None = 'dict', id: str | None = 'b197cfc9-234e-4811-8304-fb8783b77a92', \*\*data) - -基类:`BaseModel`, `Generic`[`_T`] - -Message class - -#### content - -The message content - -* **Type:** - appbuilder.core.message._T | None - -#### name - -The message name - -* **Type:** - str | None - -#### mtype - -The message type - -* **Type:** - str | None - -#### id - -The message id - -* **Type:** - str | None - -#### content *: \_T | None* - -#### id *: str | None* - -#### model_computed_fields *: ClassVar[dict[str, ComputedFieldInfo]]* *= {}* - -A dictionary of computed field names and their corresponding ComputedFieldInfo objects. - -#### model_config *: ClassVar[ConfigDict]* *= {'extra': 'allow'}* - -Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict]. - -#### model_fields *: ClassVar[dict[str, FieldInfo]]* *= {'content': FieldInfo(annotation=Union[~_T, NoneType], required=False, default={}), 'id': FieldInfo(annotation=Union[str, NoneType], required=False, default='b197cfc9-234e-4811-8304-fb8783b77a92'), 'mtype': FieldInfo(annotation=Union[str, NoneType], required=False, default='dict'), 'name': FieldInfo(annotation=Union[str, NoneType], required=False, default='msg')}* - -Metadata about the fields defined on the model, -mapping of field names to [FieldInfo][pydantic.fields.FieldInfo]. - -This replaces Model._\_fields_\_ from Pydantic V1. - -#### mtype *: str | None* - -#### name *: str | None* - -## appbuilder.core.component module - - - -Component模块包括组件基类,用户自定义组件需要继承Component类,并至少实现run方法 - -### *class* appbuilder.core.component.Component(meta: [ComponentArguments](#appbuilder.core.component.ComponentArguments) | None = ComponentArguments(name='', tool_desc={}), secret_key: str | None = None, gateway: str = '', lazy_certification: bool = False) - -基类:`object` - -Component基类, 其它实现的Component子类需要继承该基类,并至少实现run方法. - -* **参数:** - * **meta** ([*ComponentArguments*](#appbuilder.core.component.ComponentArguments)) -- component meta information. - * **secret_key** (*str*) -- user authentication token. - * **gateway** (*str*) -- backend gateway server address. - * **lazy_certification** (*bool*) -- lazy certification flag. - -#### *async* abatch(\*args, \*\*kwargs) → List[[Message](#appbuilder.core.message.Message)] - -abatch method,待子类重写实现 - -* **参数:** - * **args** -- list of arguments - * **kwargs** -- keyword arguments - -#### *async* arun(\*args, \*\*kwargs) → [Message](#appbuilder.core.message.Message) | None - -arun method,待子类重写实现 - -* **参数:** - * **args** -- list of arguments - * **kwargs** -- keyword arguments - -#### batch(\*args, \*\*kwargs) → List[[Message](#appbuilder.core.message.Message)] - -batch method,待子类重写实现 - -* **参数:** - * **args** -- list of arguments - * **kwargs** -- keyword arguments - -#### create_langchain_tool(tool_name='', \*\*kwargs) - -create_langchain_tool method,将AB-SDK的Tool转换为LangChain的StructuredTool - -* **参数:** - * **tool_name** -- string, optional, default is empty string - * **kwargs** -- keyword arguments -* **返回:** - StructuredTool - -#### *property* http_client - -获取 HTTP 客户端实例。 - -* **参数:** - **无** -* **返回:** - HTTP 客户端实例。 -* **返回类型:** - HTTPClient - -#### manifests *= []* - -#### run(\*inputs, \*\*kwargs) - -run method,待子类重写实现 - -* **参数:** - * **inputs** -- list of arguments - * **kwargs** -- keyword arguments - -#### set_secret_key_and_gateway(secret_key: str | None = None, gateway: str = '') - -设置密钥和网关地址。 - -* **参数:** - * **secret_key** (*Optional* *[**str* *]* *,* *optional*) -- 密钥,默认为None。如果未指定,则使用实例当前的密钥。 - * **gateway** (*str* *,* *optional*) -- 网关地址,默认为空字符串。如果未指定,则使用实例当前的网关地址。 -* **返回:** - None - -#### tool_desc() → List[str] - -tool_desc method,待子类重写实现 - -* **参数:** - **None** -* **返回:** - list of strings - -#### tool_eval(\*\*kwargs) - -tool_eval method,待子类重写实现 - -* **参数:** - **kwargs** -- keyword arguments - -#### tool_name() → List[str] - -tool_name method,待子类重写实现 - -* **参数:** - **None** -* **返回:** - list of strings - -### *class* appbuilder.core.component.ComponentArguments(\*, name: str = '', tool_desc: Dict[str, Any] = {}) - -基类:`BaseModel` - -ComponentArguments define Component meta fields - -#### name - -component name. - -* **Type:** - str - -#### tool_desc - -component description. - -* **Type:** - dict - -#### extract_values_to_dict() - -extract ComponentArguments fields to dict - -#### model_computed_fields *: ClassVar[dict[str, ComputedFieldInfo]]* *= {}* - -A dictionary of computed field names and their corresponding ComputedFieldInfo objects. - -#### model_config *: ClassVar[ConfigDict]* *= {}* - -Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict]. - -#### model_fields *: ClassVar[dict[str, FieldInfo]]* *= {'name': FieldInfo(annotation=str, required=False, default=''), 'tool_desc': FieldInfo(annotation=Dict[str, Any], required=False, default={})}* - -Metadata about the fields defined on the model, -mapping of field names to [FieldInfo][pydantic.fields.FieldInfo]. - -This replaces Model._\_fields_\_ from Pydantic V1. - -#### name *: str* - -#### tool_desc *: Dict[str, Any]* diff --git a/docs/API-Reference/Python/appbuilder.md b/docs/API-Reference/Python/appbuilder.md deleted file mode 100644 index cbd84e5b8..000000000 --- a/docs/API-Reference/Python/appbuilder.md +++ /dev/null @@ -1,67 +0,0 @@ -# appbuilder package - -## Subpackages - -* [appbuilder.core package](appbuilder.core.md) - * [Subpackages](appbuilder.core.md#subpackages) - * [appbuilder.core.assistant package](appbuilder.core.assistant.md) - * [Subpackages](appbuilder.core.assistant.md#subpackages) - * [Submodules](appbuilder.core.assistant.md#submodules) - * [appbuilder.core.components package](appbuilder.core.components.md) - * [Subpackages](appbuilder.core.components.md#subpackages) - * [appbuilder.core.console package](appbuilder.core.console.md) - * [Subpackages](appbuilder.core.console.md#subpackages) - * [Submodules](appbuilder.core.console.md#submodules) - * [Submodules](appbuilder.core.md#submodules) - * [appbuilder.core.agent module](appbuilder.core.md#module-appbuilder.core.agent) - * [`AgentRuntime`](appbuilder.core.md#appbuilder.core.agent.AgentRuntime) - * [`AgentRuntime.Config`](appbuilder.core.md#appbuilder.core.agent.AgentRuntime.Config) - * [`AgentRuntime.chainlit_agent()`](appbuilder.core.md#appbuilder.core.agent.AgentRuntime.chainlit_agent) - * [`AgentRuntime.chainlit_demo()`](appbuilder.core.md#appbuilder.core.agent.AgentRuntime.chainlit_demo) - * [`AgentRuntime.chat()`](appbuilder.core.md#appbuilder.core.agent.AgentRuntime.chat) - * [`AgentRuntime.component`](appbuilder.core.md#appbuilder.core.agent.AgentRuntime.component) - * [`AgentRuntime.create_flask_app()`](appbuilder.core.md#appbuilder.core.agent.AgentRuntime.create_flask_app) - * [`AgentRuntime.init()`](appbuilder.core.md#appbuilder.core.agent.AgentRuntime.init) - * [`AgentRuntime.model_computed_fields`](appbuilder.core.md#appbuilder.core.agent.AgentRuntime.model_computed_fields) - * [`AgentRuntime.model_config`](appbuilder.core.md#appbuilder.core.agent.AgentRuntime.model_config) - * [`AgentRuntime.model_fields`](appbuilder.core.md#appbuilder.core.agent.AgentRuntime.model_fields) - * [`AgentRuntime.prepare_chainlit_readme()`](appbuilder.core.md#appbuilder.core.agent.AgentRuntime.prepare_chainlit_readme) - * [`AgentRuntime.serve()`](appbuilder.core.md#appbuilder.core.agent.AgentRuntime.serve) - * [`AgentRuntime.tool_choice`](appbuilder.core.md#appbuilder.core.agent.AgentRuntime.tool_choice) - * [`AgentRuntime.user_session`](appbuilder.core.md#appbuilder.core.agent.AgentRuntime.user_session) - * [`AgentRuntime.user_session_config`](appbuilder.core.md#appbuilder.core.agent.AgentRuntime.user_session_config) - * [appbuilder.core.message module](appbuilder.core.md#module-appbuilder.core.message) - * [`Message`](appbuilder.core.md#appbuilder.core.message.Message) - * [`Message.content`](appbuilder.core.md#appbuilder.core.message.Message.content) - * [`Message.name`](appbuilder.core.md#appbuilder.core.message.Message.name) - * [`Message.mtype`](appbuilder.core.md#appbuilder.core.message.Message.mtype) - * [`Message.id`](appbuilder.core.md#appbuilder.core.message.Message.id) - * [`Message.content`](appbuilder.core.md#id2) - * [`Message.id`](appbuilder.core.md#id3) - * [`Message.model_computed_fields`](appbuilder.core.md#appbuilder.core.message.Message.model_computed_fields) - * [`Message.model_config`](appbuilder.core.md#appbuilder.core.message.Message.model_config) - * [`Message.model_fields`](appbuilder.core.md#appbuilder.core.message.Message.model_fields) - * [`Message.mtype`](appbuilder.core.md#id4) - * [`Message.name`](appbuilder.core.md#id5) - * [appbuilder.core.component module](appbuilder.core.md#appbuilder-core-component-module) - * [`Component`](appbuilder.core.md#appbuilder.core.component.Component) - * [`Component.abatch()`](appbuilder.core.md#appbuilder.core.component.Component.abatch) - * [`Component.arun()`](appbuilder.core.md#appbuilder.core.component.Component.arun) - * [`Component.batch()`](appbuilder.core.md#appbuilder.core.component.Component.batch) - * [`Component.create_langchain_tool()`](appbuilder.core.md#appbuilder.core.component.Component.create_langchain_tool) - * [`Component.http_client`](appbuilder.core.md#appbuilder.core.component.Component.http_client) - * [`Component.manifests`](appbuilder.core.md#appbuilder.core.component.Component.manifests) - * [`Component.run()`](appbuilder.core.md#appbuilder.core.component.Component.run) - * [`Component.set_secret_key_and_gateway()`](appbuilder.core.md#appbuilder.core.component.Component.set_secret_key_and_gateway) - * [`Component.tool_desc()`](appbuilder.core.md#appbuilder.core.component.Component.tool_desc) - * [`Component.tool_eval()`](appbuilder.core.md#appbuilder.core.component.Component.tool_eval) - * [`Component.tool_name()`](appbuilder.core.md#appbuilder.core.component.Component.tool_name) - * [`ComponentArguments`](appbuilder.core.md#appbuilder.core.component.ComponentArguments) - * [`ComponentArguments.name`](appbuilder.core.md#appbuilder.core.component.ComponentArguments.name) - * [`ComponentArguments.tool_desc`](appbuilder.core.md#appbuilder.core.component.ComponentArguments.tool_desc) - * [`ComponentArguments.extract_values_to_dict()`](appbuilder.core.md#appbuilder.core.component.ComponentArguments.extract_values_to_dict) - * [`ComponentArguments.model_computed_fields`](appbuilder.core.md#appbuilder.core.component.ComponentArguments.model_computed_fields) - * [`ComponentArguments.model_config`](appbuilder.core.md#appbuilder.core.component.ComponentArguments.model_config) - * [`ComponentArguments.model_fields`](appbuilder.core.md#appbuilder.core.component.ComponentArguments.model_fields) - * [`ComponentArguments.name`](appbuilder.core.md#id6) - * [`ComponentArguments.tool_desc`](appbuilder.core.md#id7) diff --git a/docs/API-Reference/Python/index.md b/docs/API-Reference/Python/index.md deleted file mode 100644 index 089673eeb..000000000 --- a/docs/API-Reference/Python/index.md +++ /dev/null @@ -1,10 +0,0 @@ - - -# Appbuilder-SDK documentation - -Add your content using `reStructuredText` syntax. See the -[reStructuredText](https://www.sphinx-doc.org/en/master/usage/restructuredtext/index.html) -documentation for details. diff --git a/docs/API-Reference/Python/modules.md b/docs/API-Reference/Python/modules.md deleted file mode 100644 index 7f992a63a..000000000 --- a/docs/API-Reference/Python/modules.md +++ /dev/null @@ -1,10 +0,0 @@ -# appbuilder - -* [appbuilder package](appbuilder.md) - * [Subpackages](appbuilder.md#subpackages) - * [appbuilder.core package](appbuilder.core.md) - * [Subpackages](appbuilder.core.md#subpackages) - * [Submodules](appbuilder.core.md#submodules) - * [appbuilder.core.agent module](appbuilder.core.md#module-appbuilder.core.agent) - * [appbuilder.core.message module](appbuilder.core.md#module-appbuilder.core.message) - * [appbuilder.core.component module](appbuilder.core.md#appbuilder-core-component-module) diff --git a/docs/BasisModule/Trace/Debug.md b/docs/BasisModule/Trace/Debug.md new file mode 100644 index 000000000..11d4239e3 --- /dev/null +++ b/docs/BasisModule/Trace/Debug.md @@ -0,0 +1,54 @@ +# Appbuilder-SDK Debug + +## 打印DEBUG日志 + +开启DEBUG日志,可以打印出更多的日志信息,方便调试,包括且不限于:请求URL、请求头、请求参数等。 + +```bash +# 可以设置环境变量开启 +# 开启DEBUG +export APPBUILDER_LOGLEVEL=DEBUG +# 关闭DEBUG +export APPBUILDER_LOGLEVEL=INFO +``` + +也可以在代码中设置,优先级高于环境变量。 +```python +# python +appbuilder.logger.setLoglevel("DEBUG") +``` +```java +// java +System.setProperty("APPBUILDER_LOGLEVEL", "DEBUG"); +``` +```golang +// golang +os.Setenv("APPBUILDER_LOGLEVEL", "DEBUG") +``` + +## 指定日志文件 + +如果需要将日志输出到指定文件,方便落盘。默认输出为标准输出流。 +可以设置环境变量`APPBUILDER_LOGFILE`。 + +```bash +# 文件名及路径可以根据实际使用修改 +export APPBUILDER_LOGFILE=/tmp/appbuilder.log +``` + +也可以在代码中设置,优先级高于环境变量。 + +```python +# python +appbuilder.logger.setFilename("/tmp/appbuilder.log") +``` + +```java +// java +System.setProperty("APPBUILDER_LOGLFILE", "/tmp/appbuilder.log"); +``` + +```golang +// golang +os.Setenv("APPBUILDER_LOGLEVEL", "/tmp/appbuilder.log") +``` \ No newline at end of file diff --git a/docs/QuickStart/StartFirstAINativeApplication/README.md b/docs/QuickStart/StartFirstAINativeApplication/README.md index 5b579369d..9da4edee0 100644 --- a/docs/QuickStart/StartFirstAINativeApplication/README.md +++ b/docs/QuickStart/StartFirstAINativeApplication/README.md @@ -150,54 +150,4 @@ if __name__ == "__main__": agent_builder = AppBuilderClient(app_id) agent = AgentRuntime(component=agent_builder) agent.chainlit_agent(port=8091) -``` - -### 打印DEBUG日志 - -开启DEBUG日志,可以打印出更多的日志信息,方便调试,包括且不限于:请求URL、请求头、请求参数等。 - -```bash -# 可以设置环境变量开启 -# 开启DEBUG -export APPBUILDER_LOGLEVEL=DEBUG -# 关闭DEBUG -export APPBUILDER_LOGLEVEL=INFO -``` - -也可以在代码中设置,优先级高于环境变量。 -```python -# python -appbuilder.logger.setLoglevel("DEBUG") -``` -```java -// java -System.setProperty("APPBUILDER_LOGLEVEL", "DEBUG"); -``` -```golang -// golang -os.Setenv("APPBUILDER_LOGLEVEL", "DEBUG") -``` - -### 指定日志文件 - -如果需要将日志输出到指定文件,方便落盘。默认输出为标准输出流。 -可以设置环境变量`APPBUILDER_LOGFILE`。 - -```bash -# 文件名及路径可以根据实际使用修改 -export APPBUILDER_LOGFILE=/tmp/appbuilder.log -``` - -也可以在代码中设置,优先级高于环境变量。 -```python -# python -appbuilder.logger.setFilename("/tmp/appbuilder.log") -``` -```java -// java -System.setProperty("APPBUILDER_LOGLFILE", "/tmp/appbuilder.log"); -``` -```golang -// golang -os.Setenv("APPBUILDER_LOGLEVEL", "/tmp/appbuilder.log") ``` \ No newline at end of file diff --git a/docs/README_en.md b/docs/README_en.md index ed4d56c2f..bd3eaeb3f 100644 --- a/docs/README_en.md +++ b/docs/README_en.md @@ -261,6 +261,7 @@ Hook: - Monitoring: - [TRACE basic functions](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/basic.md) - [TRACE extended functions](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/phoenix_method.md) + - [Debug functionality](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/Debug.md) - Deployment: - [Interactive front-end deployment](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/AgentChainlit.md) - [Public cloud deployment](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/cloud.md) @@ -299,7 +300,7 @@ Hook: - [Environmental parameters](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/EnvironmentalParameters/env.md) - API Reference: - [Python API Reference](https://github.com/baidubce/app-builder/blob/master/docs/API-Reference/Python/PythonAPI.md) - - [Java API Reference](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [Java API Reference](https://github.com/baidubce/app-builder/blob/master/docs/API-Reference/Java/JavaAPI.md) - [Go API Reference](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) ## Open source community and activities diff --git a/docs/README_ja.md b/docs/README_ja.md index 3e0027c93..7f2bb10a9 100644 --- a/docs/README_ja.md +++ b/docs/README_ja.md @@ -257,6 +257,7 @@ print(answer.content.answer) - 監視: - [TRACE基本機能](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/basic.md) - [TRACE拡張機能](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/phoenix_method.md) + - [Debug機能](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/Debug.md) - デプロイ: - [インタラクティブなフロントエンドデプロイ](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/AgentChainlit.md) - [パブリッククラウドデプロイ](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/cloud.md) @@ -295,7 +296,7 @@ print(answer.content.answer) - [環境パラメータ](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/EnvironmentalParameters/env.md) - APIリファレンス: - [Python APIリファレンス](https://github.com/baidubce/app-builder/blob/master/docs/API-Reference/Python/PythonAPI.md) - - [Java APIリファレンス](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [Java APIリファレンス](https://github.com/baidubce/app-builder/blob/master/docs/API-Reference/Java/JavaAPI.md) - [Go APIリファレンス](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) diff --git a/docs/Tools/JavaAPI/JavaAPI.md b/docs/Tools/JavaAPI/JavaAPI.md new file mode 100644 index 000000000..4a1a3d440 --- /dev/null +++ b/docs/Tools/JavaAPI/JavaAPI.md @@ -0,0 +1,5 @@ +# Java API Reference + +- [Java API](index.html) + - [All API List](index-all.html) + - [帮助文档](help-doc.html) \ No newline at end of file diff --git a/docs/Tools/JavaAPI/READEME.md b/docs/Tools/JavaAPI/READEME.md new file mode 100644 index 000000000..985951a95 --- /dev/null +++ b/docs/Tools/JavaAPI/READEME.md @@ -0,0 +1,90 @@ +# Appbuilder-SDK Java 自动文档生成 + +## 操作流程 + +- 完成SDK代码开发 +- 依照google规范编写注释--仅需要对类和非私有方法进行注释 +- 进入根目录的docs/Tools/JavaAPI目录下执行java_api_update.sh脚本 + +## 脚本功能 + +- 依据注释自动生成文档,并将文档迁移到docs/API-Reference/Java目录下 + +## 代码注释规范 + +### 基本格式 + +Javadoc注释是用/**开始,用\*/结束的,位于类、方法或字段之前。例如 + +```java +/** + * 这是一个用于演示的类。 + */ +public class Demo { +} +``` + +### 类和接口注释 + +对于类和接口,Javadoc注释应该解释其整体功能和用途,以及如何与其他类或接口交互 + +```java +/** + * 这是一个计算工具类,提供静态方法来进行数学计算。 + */ +public class MathUtils { + // ... +} +``` + +### 方法注释 + +每个公共和受保护的方法应该有Javadoc注释,说明方法的作用、参数、返回值以及可能抛出的异常。 + +- `@param` 用来描述参数 +- `@return` 描述返回值(如果方法不返回任何内容,则不需要此标签) +- `@throws` 或 `@exception` 描述可能抛出的异常 + +```java +/** + * 计算两个整数的和。 + * + * @param a 第一个整数 + * @param b 第二个整数 + * @return 两个整数的和 + */ +public static int add(int a, int b) { + return a + b; +} +``` + +### 字段注释 + +公共字段应有简短的注释说明其作用。如果字段的用途不是显而易见的,应该提供详细的描述。 + +```java +/** + * 默认的错误消息。 + */ +public static final String DEFAULT_ERROR_MESSAGE = "An error occurred."; +``` + +### 通用标签 + +除了上述特定标签外,Javadoc还支持以下一些通用标签: + +- `@see` 参考其他相关类、方法或文档 +- `@since` 指明从哪个版本开始添加的 +- `@version` 标明当前代码的版本 +- `@deprecated` 指明方法或类不再推荐使用 + +```java +/** + * @deprecated 由于安全问题,此方法从版本1.5开始不推荐使用。 + */ +@Deprecated +public void oldMethod() { + // ... +} +``` + diff --git a/docs/Tools/JavaAPI/java_api_update.sh b/docs/Tools/JavaAPI/java_api_update.sh new file mode 100644 index 000000000..dc7b3dd16 --- /dev/null +++ b/docs/Tools/JavaAPI/java_api_update.sh @@ -0,0 +1,16 @@ +#1、清除生成文档环境 +cd ../../.. +rm -rf java/doc +rm -rf docs/API-Reference/Java/* + +# 2、生成Java API文档 +cd java +javadoc -d doc -sourcepath src/main/java -subpackages com.baidubce.appbuilder -exclude com.baidubce.appbuilder.base -encoding UTF-8 -charset UTF-8 -public + +# 3、迁移文档到docs目录,并删除java/doc目录 +cd .. +cp -r java/doc/* docs/API-Reference/Java +rm -rf java/doc + +# 4、辅助Java API文档目录文档到docs目录 +cp docs/Tools/JavaAPI/JavaAPI.md docs/API-Reference/Java \ No newline at end of file diff --git a/docs/Tools/SphinxSh/PythonAPI.md b/docs/Tools/SphinxSh/PythonAPI.md index 4801ebacd..41246452f 100644 --- a/docs/Tools/SphinxSh/PythonAPI.md +++ b/docs/Tools/SphinxSh/PythonAPI.md @@ -1,7 +1,7 @@ # Python API Reference -- [基础 API](appbuilder.md) +- [Python API](appbuilder.md) - [Assistant API](appbuilder.core.assistant.md) - [Components API](appbuilder.core.components.md) - [Console API](appbuilder.core.console.md) \ No newline at end of file diff --git a/docs/Tools/SphinxSh/READEME.md b/docs/Tools/SphinxSh/READEME.md index d2a7c6af4..e5056df63 100644 --- a/docs/Tools/SphinxSh/READEME.md +++ b/docs/Tools/SphinxSh/READEME.md @@ -1,4 +1,4 @@ -# Appbuilder-SDK 自动文档生成 +# Appbuilder-SDK Python 自动文档生成 ## 操作流程 diff --git a/docs/Tools/SphinxSh/update_doc.sh b/docs/Tools/SphinxSh/update_doc.sh index 94eaf9462..4b4aedc35 100644 --- a/docs/Tools/SphinxSh/update_doc.sh +++ b/docs/Tools/SphinxSh/update_doc.sh @@ -155,6 +155,9 @@ cd ../../Tools/SphinxSh # 运行mkdocs更改文件 python3 get_components_md.py +# 更改API目录文件结构 +python3 update_lib.py + echo "====拷贝组件README.md文件到docs/BasisModule/Components目录完成====" diff --git a/docs/Tools/SphinxSh/update_lib.py b/docs/Tools/SphinxSh/update_lib.py new file mode 100644 index 000000000..93f8d0ab1 --- /dev/null +++ b/docs/Tools/SphinxSh/update_lib.py @@ -0,0 +1,201 @@ +import subprocess + +def mv_new_md(input_filename, output_filename): + """ + 将新生成的Markdown文件移动到目标位置并删除原文件。 + + Args: + input_filename (str): 目标文件名。 + output_filename (str): 新生成的Markdown文件名。 + + Returns: + None + + """ + command = ['mv', output_filename, input_filename] + subprocess.run(command, check=True) + command = ['rm', '-rf', output_filename] + subprocess.run(command, check=True) + +def process_line_for_assistant(line): + """ + 处理一行文本以供助手使用。 + + Args: + line (str): 要处理的文本行。 + + Returns: + str or None: 如果行中包含'module'字段且不包含"subpackages"或"submodules",则返回处理后的行。 + 否则返回None表示应该删除该行。 + + 说明: + 1. 仅提取存在'module'字段且不包含"subpackages"或"submodules"的行,并返回格式处理之后的行。 + 2. 处理后的行格式为"- [模块名](链接)",其中模块名为文本行中最后一个点后的内容, + 链接为原文本行中的链接部分。 + 3. 如果模块名在指定的模块列表中('assistants', 'files', 'messages', 'runs', 'threads'), + 则返回处理后的行,否则返回None。 + """ + # 仅提取存在module字段且不包含"subpackages"或"submodules"的行,并返回格式处理之后的行 + if 'module' in line.lower() and 'subpackages' not in line.lower() and 'submodules' not in line.lower(): + open_module = ('assistants', 'files', 'messages', 'runs', 'threads') + + parts = line.split('](') + + new_name = parts[0].split(' module')[0].split('.')[-1] + if new_name in open_module: + return f"- [{new_name}]({parts[-1]}" + else: + return None + else: + # 如果行中没有"module",返回None表示应该删除该行 + return None + +def process_line_for_components(line): + """ + 对给定的行进行组件处理。 + + Args: + line (str): 需要处理的行。 + + Returns: + str or None: 处理后的行。如果行中没有包含"module"字段,或者包含"subpackages"或"submodules",则返回None,表示应该删除该行。 + + """ + # 仅提取存在module字段且不包含"subpackages"或"submodules"的行,并返回格式处理之后的行 + if 'module' in line.lower() and 'subpackages' not in line.lower() and 'submodules' not in line.lower(): + parts = line.split('](') + new_names = parts[0].split(' module')[0].split('.') + if len(new_names) == 5: + return f"- [{new_names[-2]}]({parts[-1]}" + else: + if new_names[-3] == 'llms': + return f"- [{new_names[-2]}--(LLM)]({parts[-1]}" + elif new_names[-3] == 'retriever': + return f"- [{new_names[-2]}--(retriever)]({parts[-1]}" + elif new_names[-3] == 'gbi': + return f"- [{new_names[-2]}--(GBI)]({parts[-1]}" + else: + # 如果行中没有"module",返回None表示应该删除该行 + return None + +def process_line_for_console(line): + """ + 对给定行进行格式处理,并返回处理后的行。 + + Args: + line (str): 需要处理的行文本。 + + Returns: + str: 处理后的行文本,如果行应被删除则返回None。 + + 说明: + 该函数仅处理包含"module"字段且不包含"subpackages"或"submodules"的行。 + 对于包含"module"的行,会提取模块名并返回格式化后的Markdown链接。 + 如果模块名是"data_class",则直接返回None。 + 如果行中包含"(appbuilder.core.console.knowledge_base.md)",则返回固定的Markdown链接。 + 如果行中不包含"module",则返回None表示该行应被删除。 + """ + # 仅提取存在module字段且不包含"subpackages"或"submodules"的行,并返回格式处理之后的行 + if 'module' in line.lower() and 'subpackages' not in line.lower() and 'submodules' not in line.lower(): + parts = line.split('](') + new_names = parts[0].split(' module')[0].split('.') + if new_names[-1] == 'data_class': + return None + else: + return f"- [{new_names[-1]}]({parts[-1]}" + elif '(appbuilder.core.console.knowledge_base.md)' in line: + return "- [knowledge_base](appbuilder.core.console.knowledge_base.md)" + else: + # 如果行中没有"module",返回None表示应该删除该行 + return None + +def process_file_for_assistant(input_filename, output_filename): + """ + 为助手处理文件。 + + Args: + input_filename (str): 输入文件名。 + output_filename (str): 输出文件名。 + + Returns: + None + + """ + with open(input_filename, 'r') as file: + lines = file.readlines() + + new_lines = [] + for line in lines: + new_line = process_line_for_assistant(line.strip()) + if new_line: + new_lines.append(new_line) + + with open(output_filename, 'w') as file: + for line in new_lines: + file.write(line + '\n') + + mv_new_md(input_filename, output_filename) + +def process_file_for_components(input_filename, output_filename): + """ + 从文件中读取文本行,处理每行文本以提取组件信息,并将结果写入到新的文件中。 + + Args: + input_filename (str): 输入文件的路径。 + output_filename (str): 输出文件的路径。 + + Returns: + None + + """ + with open(input_filename, 'r') as file: + lines = file.readlines() + + new_lines = [] + for line in lines: + new_line = process_line_for_components(line.strip()) + if new_line: + new_lines.append(new_line) + + with open(output_filename, 'w') as file: + for line in new_lines: + file.write(line + '\n') + + mv_new_md(input_filename, output_filename) + + +def process_file_for_console(input_filename, output_filename): + """ + 将文件内容处理后输出到另一个文件,并移动原文件。 + + Args: + input_filename (str): 输入文件名。 + output_filename (str): 输出文件名。 + + Returns: + None + + """ + with open(input_filename, 'r') as file: + lines = file.readlines() + + new_lines = [] + for line in lines: + new_line = process_line_for_console(line.strip()) + if new_line: + new_lines.append(new_line) + + with open(output_filename, 'w') as file: + for line in new_lines: + file.write(line + '\n') + + mv_new_md(input_filename, output_filename) + + +if __name__ == "__main__": + # 处理Assistant.md文件 + process_file_for_assistant("../../API-Reference/Python/appbuilder.core.assistant.md", "../../API-Reference/Python/new_appbuilder.core.assistant.md") + # 处理Components.md文件 + process_file_for_components("../../API-Reference/Python/appbuilder.core.components.md", "../../API-Reference/Python/new_appbuilder.core.components.md") + # 处理console.md文件 + process_file_for_console("../../API-Reference/Python/appbuilder.core.console.md", "../../API-Reference/Python/new_appbuilder.core.console.md") \ No newline at end of file diff --git a/java/src/main/java/com/baidubce/appbuilder/console/appbuilderclient/AppBuilderClient.java b/java/src/main/java/com/baidubce/appbuilder/console/appbuilderclient/AppBuilderClient.java index 284c02930..8219424ab 100644 --- a/java/src/main/java/com/baidubce/appbuilder/console/appbuilderclient/AppBuilderClient.java +++ b/java/src/main/java/com/baidubce/appbuilder/console/appbuilderclient/AppBuilderClient.java @@ -131,6 +131,14 @@ public AppBuilderClientIterator run(String query, String conversationId, String[ return new AppBuilderClientIterator(response.getBody()); } + /** + * 执行应用构建客户端运行请求 + * + * @param requestBody 请求体,包含运行所需的所有信息 + * @return 返回包含构建客户端响应的迭代器 + * @throws IOException 如果在执行请求时发生I/O错误 + * @throws AppBuilderServerException 如果应用构建服务器返回错误响应 + */ public AppBuilderClientIterator run(AppBuilderClientRunRequest requestBody) throws IOException, AppBuilderServerException { String url = AppBuilderConfig.AGENTBUILDER_RUN_URL; diff --git a/java/src/main/java/com/baidubce/appbuilder/console/appbuilderclient/AppList.java b/java/src/main/java/com/baidubce/appbuilder/console/appbuilderclient/AppList.java index a212fb058..d9888f1a0 100644 --- a/java/src/main/java/com/baidubce/appbuilder/console/appbuilderclient/AppList.java +++ b/java/src/main/java/com/baidubce/appbuilder/console/appbuilderclient/AppList.java @@ -23,6 +23,16 @@ public AppList() { super(); } + /** + * @deprecated 该方法已被弃用,请改用新方法 + * + * 根据请求获取应用列表 + * + * @param request 请求参数 + * @return 应用列表数组 + * @throws IOException 如果发生输入输出异常 + * @throws AppBuilderServerException 如果发生应用构建服务器异常 + */ @Deprecated public App[] getAppList(AppListRequest request) throws IOException, AppBuilderServerException { String url = AppBuilderConfig.APP_LIST_URL; @@ -34,6 +44,14 @@ public App[] getAppList(AppListRequest request) throws IOException, AppBuilderSe return respBody.getData(); } + /** + * 描述应用程序信息 + * + * @param request 包含应用程序描述信息的请求对象 + * @return 包含应用程序描述信息的响应对象 + * @throws IOException 如果发生I/O异常 + * @throws AppBuilderServerException 如果发生AppBuilder服务器异常 + */ public AppsDescribeResponse describeApps(AppsDescribeRequest request) throws IOException, AppBuilderServerException { String url = AppBuilderConfig.APPS_DESCRIBE_URL; diff --git a/java/src/main/java/com/baidubce/appbuilder/console/knowledgebase/Knowledgebase.java b/java/src/main/java/com/baidubce/appbuilder/console/knowledgebase/Knowledgebase.java index 1f48b905e..aed73056b 100644 --- a/java/src/main/java/com/baidubce/appbuilder/console/knowledgebase/Knowledgebase.java +++ b/java/src/main/java/com/baidubce/appbuilder/console/knowledgebase/Knowledgebase.java @@ -37,10 +37,28 @@ public String uploadFile(String filePath) throws IOException, AppBuilderServerEx return innerUploadFile(filePath, java.util.UUID.randomUUID().toString()); } + /** + * 上传文件 + * + * @param filePath 文件路径 + * @param clientToken 客户端令牌 + * @return 上传文件的结果 + * @throws IOException 如果发生I/O错误 + * @throws AppBuilderServerException 如果应用构建服务器发生错误 + */ public String uploadFile(String filePath, String clientToken) throws IOException, AppBuilderServerException { return innerUploadFile(filePath, clientToken); } + /** + * 上传文件到指定服务器。 + * + * @param filePath 文件路径 + * @param clientToken 客户端令牌 + * @return 文件ID + * @throws IOException 当发生输入输出异常时抛出 + * @throws AppBuilderServerException 当发生应用构建服务器异常时抛出 + */ private String innerUploadFile(String filePath, String clientToken) throws IOException, AppBuilderServerException { String url = AppBuilderConfig.KNOWLEDGEBASE_UPLOAD_FILE_URL; @@ -73,11 +91,29 @@ public String[] addDocument(DocumentAddRequest req) return innerAddDocument(req, java.util.UUID.randomUUID().toString()); } + /** + * 向应用程序中添加文档 + * + * @param req 文档添加请求对象,包含需要添加的文档信息 + * @param clientToken 客户端令牌,用于验证请求来源 + * @return 返回一个字符串数组,包含操作结果 + * @throws IOException 如果发生输入/输出异常,抛出此异常 + * @throws AppBuilderServerException 如果应用程序构建服务器发生异常,抛出此异常 + */ public String[] addDocument(DocumentAddRequest req, String clientToken) throws IOException, AppBuilderServerException { return innerAddDocument(req, clientToken); } + /** + * 向知识库中添加文档 + * + * @param req 文档添加请求对象 + * @param clientToken 客户端令牌 + * @return 文档ID数组 + * @throws IOException 如果发生I/O异常 + * @throws AppBuilderServerException 如果应用构建服务器异常 + */ private String[] innerAddDocument(DocumentAddRequest req, String clientToken) throws IOException, AppBuilderServerException { String url = AppBuilderConfig.KNOWLEDGEBASE_ADD_DOCUMENT_URL; @@ -96,6 +132,14 @@ private String[] innerAddDocument(DocumentAddRequest req, String clientToken) return respBody.getDocumentIds(); } + /** + * 获取文档列表 + * + * @param request 文档列表请求对象,包含请求参数 + * @return 文档数组 + * @throws IOException 当发生输入输出异常时抛出 + * @throws AppBuilderServerException 当应用构建服务器发生异常时抛出 + */ public Document[] getDocumentList(DocumentListRequest request) throws IOException, AppBuilderServerException { String url = AppBuilderConfig.KNOWLEDGEBASE_DOCUMENT_LIST_URL; @@ -107,16 +151,39 @@ public Document[] getDocumentList(DocumentListRequest request) return respBody.getData(); } + /** + * 删除文档 + * + * @param request 文档删除请求对象 + * @throws IOException 如果在删除文档过程中发生I/O异常 + * @throws AppBuilderServerException 如果在删除文档过程中发生AppBuilder服务器异常 + */ public void deleteDocument(DocumentDeleteRequest request) throws IOException, AppBuilderServerException { innerDeleteDocument(request, java.util.UUID.randomUUID().toString()); } + /** + * 删除文档 + * + * @param request 删除文档的请求对象,包含要删除的文档ID等信息 + * @param clientToken 客户端令牌,用于验证请求合法性 + * @throws IOException 如果发生输入输出异常 + * @throws AppBuilderServerException 如果发生应用构建服务器异常 + */ public void deleteDocument(DocumentDeleteRequest request, String clientToken) throws IOException, AppBuilderServerException { innerDeleteDocument(request, clientToken); } + /** + * 删除文档 + * + * @param request 删除文档请求 + * @param clientToken 客户端令牌 + * @throws IOException IO异常 + * @throws AppBuilderServerException 应用构建服务器异常 + */ private void innerDeleteDocument(DocumentDeleteRequest request, String clientToken) throws IOException, AppBuilderServerException { String url = AppBuilderConfig.KNOWLEDGEBASE_DELETE_DOCUMENT_URL; @@ -131,16 +198,42 @@ private void innerDeleteDocument(DocumentDeleteRequest request, String clientTok } } + /** + * 创建知识库详情 + * + * @param request 请求体,包含创建知识库所需的信息 + * @return 创建成功后的知识库详情 + * @throws IOException 当输入输出操作发生错误时抛出 + * @throws AppBuilderServerException 当应用构建服务器发生错误时抛出 + */ public KnowledgeBaseDetail createKnowledgeBase(KnowledgeBaseDetail request) throws IOException, AppBuilderServerException { return innerCreateKnowledgeBase(request, java.util.UUID.randomUUID().toString()); } + /** + * 创建知识库详情 + * + * @param request 包含创建知识库详情所需的参数 + * @param clientToken 客户端令牌,用于身份验证 + * @return 创建成功后的知识库详情对象 + * @throws IOException 如果发生I/O错误 + * @throws AppBuilderServerException 如果发生应用程序构建服务器异常 + */ public KnowledgeBaseDetail createKnowledgeBase(KnowledgeBaseDetail request, String clientToken) throws IOException, AppBuilderServerException { return innerCreateKnowledgeBase(request, clientToken); } + /** + * 创建一个知识库详情 + * + * @param request 知识库详情请求对象 + * @param clientToken 客户端令牌 + * @return 创建成功后的知识库详情对象 + * @throws IOException IO异常 + * @throws AppBuilderServerException AppBuilder服务器异常 + */ private KnowledgeBaseDetail innerCreateKnowledgeBase(KnowledgeBaseDetail request, String clientToken) throws IOException, AppBuilderServerException { String url = AppBuilderConfig.KNOWLEDGEBASE_CREATE_URL; @@ -154,6 +247,14 @@ private KnowledgeBaseDetail innerCreateKnowledgeBase(KnowledgeBaseDetail request return respBody; } + /** + * 根据知识库ID获取知识库详情 + * + * @param knowledgeBaseId 知识库ID + * @return KnowledgeBaseDetail 知识库详情对象 + * @throws IOException 如果在发送HTTP请求或解析响应时发生IO异常 + * @throws AppBuilderServerException 如果发生AppBuilder服务器异常 + */ public KnowledgeBaseDetail getKnowledgeBaseDetail(String knowledgeBaseId) throws IOException, AppBuilderServerException { String url = AppBuilderConfig.KNOWLEDGEBASE_DETAIL_URL; @@ -169,16 +270,39 @@ public KnowledgeBaseDetail getKnowledgeBaseDetail(String knowledgeBaseId) return respBody; } + /** + * 删除知识库 + * + * @param knowledgeBaseId 知识库ID + * @throws IOException 当发生输入输出异常时抛出 + * @throws AppBuilderServerException 当应用构建服务器异常时抛出 + */ public void deleteKnowledgeBase(String knowledgeBaseId) throws IOException, AppBuilderServerException { innerDeleteKnowledgeBase(knowledgeBaseId, java.util.UUID.randomUUID().toString()); } + /** + * 删除知识库 + * + * @param knowledgeBaseId 知识库ID + * @param clientToken 客户端令牌 + * @throws IOException 如果发生输入输出异常 + * @throws AppBuilderServerException 如果发生应用构建服务器异常 + */ public void deleteKnowledgeBase(String knowledgeBaseId, String clientToken) throws IOException, AppBuilderServerException { innerDeleteKnowledgeBase(knowledgeBaseId, clientToken); } + /** + * 删除知识库 + * + * @param knowledgeBaseId 知识库ID + * @param clientToken 客户端令牌 + * @throws IOException IO异常 + * @throws AppBuilderServerException AppBuilder服务器异常 + */ private void innerDeleteKnowledgeBase(String knowledgeBaseId, String clientToken) throws IOException, AppBuilderServerException { String url = AppBuilderConfig.KNOWLEDGEBASE_DELETE_URL; @@ -193,16 +317,39 @@ private void innerDeleteKnowledgeBase(String knowledgeBaseId, String clientToken httpClient.execute(postRequest, null); } + /** + * 修改知识库。 + * + * @param request 知识库修改请求对象 + * @throws IOException 如果在I/O操作过程中发生错误,则抛出此异常 + * @throws AppBuilderServerException 如果应用构建服务器发生错误,则抛出此异常 + */ public void modifyKnowledgeBase(KnowledgeBaseModifyRequest request) throws IOException, AppBuilderServerException { modifyKnowledgeBase(request, java.util.UUID.randomUUID().toString()); } + /** + * 修改知识库 + * + * @param request 修改请求对象,包含需要修改的内容 + * @param clientToken 客户端令牌,用于验证请求来源的合法性 + * @throws IOException 如果发生输入输出异常 + * @throws AppBuilderServerException 如果应用构建服务器发生异常 + */ public void modifyKnowledgeBase(KnowledgeBaseModifyRequest request, String clientToken) throws IOException, AppBuilderServerException { innerModifyKnowledgeBase(request, clientToken); } + /** + * 修改知识库 + * + * @param request 修改知识库的请求对象 + * @param clientToken 客户端令牌 + * @throws IOException 当请求过程中发生I/O异常时抛出 + * @throws AppBuilderServerException 当请求过程中发生服务器异常时抛出 + */ private void innerModifyKnowledgeBase(KnowledgeBaseModifyRequest request, String clientToken) throws IOException, AppBuilderServerException { String url = AppBuilderConfig.KNOWLEDGEBASE_MODIFY_URL; @@ -215,6 +362,14 @@ private void innerModifyKnowledgeBase(KnowledgeBaseModifyRequest request, String httpClient.execute(postRequest, null); } + /** + * 获取知识库列表 + * + * @param request 包含请求参数的知识库列表请求对象 + * @return 知识库列表响应对象 + * @throws IOException 如果发生I/O异常,抛出此异常 + * @throws AppBuilderServerException 如果应用构建服务器发生异常,抛出此异常 + */ public KnowledgeBaseListResponse getKnowledgeBaseList(KnowledgeBaseListRequest request) throws IOException, AppBuilderServerException { String url = AppBuilderConfig.KNOWLEDGEBASE_LIST_URL; @@ -229,16 +384,42 @@ public KnowledgeBaseListResponse getKnowledgeBaseList(KnowledgeBaseListRequest r return respBody; } + /** + * 创建文档 + * + * @param request 文档创建请求 + * @return 创建文档的响应结果 + * @throws IOException 当发生输入输出异常时抛出 + * @throws AppBuilderServerException 当应用构建服务器发生异常时抛出 + */ public DocumentsCreateResponse createDocuments(DocumentsCreateRequest request) throws IOException, AppBuilderServerException { return innerCreateDocuments(request, java.util.UUID.randomUUID().toString()); } + /** + * 创建文档 + * + * @param request 包含创建文档所需信息的请求对象 + * @param clientToken 客户端令牌,用于身份验证 + * @return 包含创建文档结果的响应对象 + * @throws IOException 如果发生输入/输出错误 + * @throws AppBuilderServerException 如果应用程序构建服务器发生错误 + */ public DocumentsCreateResponse createDocuments(DocumentsCreateRequest request, String clientToken) throws IOException, AppBuilderServerException { return innerCreateDocuments(request, clientToken); } + /** + * 创建文档的内部方法 + * + * @param request 创建文档的请求参数 + * @param clientToken 客户端令牌 + * @return 创建文档的响应结果 + * @throws IOException 如果发生输入输出异常 + * @throws AppBuilderServerException 如果应用构建服务器发生异常 + */ private DocumentsCreateResponse innerCreateDocuments(DocumentsCreateRequest request, String clientToken) throws IOException, AppBuilderServerException { String url = AppBuilderConfig.KNOWLEDGEBASE_CREATE_DOCUMENTS_URL; @@ -256,16 +437,45 @@ private DocumentsCreateResponse innerCreateDocuments(DocumentsCreateRequest requ return respBody; } + /** + * 上传文档 + * + * @param filePath 文件路径 + * @param request 文档创建请求 + * @return 文档上传响应 + * @throws IOException 如果发生I/O错误,则抛出此异常 + * @throws AppBuilderServerException 如果发生应用程序构建服务器错误,则抛出此异常 + */ public DocumentsUploadResponse uploadDocuments(String filePath, DocumentsCreateRequest request) throws IOException, AppBuilderServerException { return innerUploadDocuments(filePath, request, java.util.UUID.randomUUID().toString()); } + /** + * 上传文档 + * + * @param filePath 文件路径 + * @param request 文档上传请求对象 + * @param clientToken 客户端Token + * @return 文档上传响应对象 + * @throws IOException 抛出IO异常 + * @throws AppBuilderServerException 抛出应用构建服务器异常 + */ public DocumentsUploadResponse uploadDocuments(String filePath, DocumentsCreateRequest request, String clientToken) throws IOException, AppBuilderServerException { return innerUploadDocuments(filePath, request, clientToken); } + /** + * 上传文档到知识库 + * + * @param filePath 要上传的文件的路径 + * @param request 文档上传请求对象 + * @param clientToken 客户端令牌 + * @return 上传文档响应对象 + * @throws IOException 如果在IO操作过程中发生错误 + * @throws AppBuilderServerException 如果在应用构建服务器操作过程中发生错误 + */ private DocumentsUploadResponse innerUploadDocuments(String filePath, DocumentsCreateRequest request, String clientToken) throws IOException, AppBuilderServerException { String url = AppBuilderConfig.KNOWLEDGEBASE_UPLOAD_DOCUMENTS_URL; @@ -285,16 +495,45 @@ private DocumentsUploadResponse innerUploadDocuments(String filePath, DocumentsC return respBody; } + /** + * 根据给定的文档ID和内容创建一个块 + * + * @param documentId 文档ID + * @param content 块的内容 + * @return 创建的块的字符串表示 + * @throws IOException 如果在文件操作中发生错误 + * @throws AppBuilderServerException 如果在应用程序构建服务器操作中发生错误 + */ public String createChunk(String documentId, String content) throws IOException, AppBuilderServerException { return innerCreateChunk(documentId, content, java.util.UUID.randomUUID().toString()); } + /** + * 创建文档块 + * + * @param documentId 文档ID + * @param content 文档内容 + * @param clientToken 客户端令牌 + * @return 创建的文档块字符串 + * @throws IOException 如果发生I/O错误 + * @throws AppBuilderServerException 如果应用程序构建服务器异常 + */ public String createChunk(String documentId, String content, String clientToken) throws IOException, AppBuilderServerException { return innerCreateChunk(documentId, content, clientToken); } + /** + * 内部创建文档片段的方法 + * + * @param documentId 文档ID + * @param content 文档内容 + * @param clientToken 客户端令牌 + * @return 创建的文档片段ID + * @throws IOException IO异常 + * @throws AppBuilderServerException 应用构建服务器异常 + */ private String innerCreateChunk(String documentId, String content, String clientToken) throws IOException, AppBuilderServerException { String url = AppBuilderConfig.CHUNK_CREATE_URL; @@ -310,16 +549,45 @@ private String innerCreateChunk(String documentId, String content, String client return respBody.getChunkId(); } + /** + * 修改指定块的内容 + * + * @param chunkId 块标识符 + * @param content 要设置的内容 + * @param enable 是否启用块 + * @throws IOException 当发生输入输出异常时抛出 + * @throws AppBuilderServerException 当发生应用构建服务器异常时抛出 + */ public void modifyChunk(String chunkId, String content, boolean enable) throws IOException, AppBuilderServerException { innerModifyChunk(chunkId, content, enable, java.util.UUID.randomUUID().toString()); } + /** + * 修改指定区块的内容及其启用状态 + * + * @param chunkId 区块的ID + * @param content 需要设置的新内容 + * @param enable 是否启用该区块,true表示启用,false表示禁用 + * @param clientToken 客户端令牌,用于验证客户端身份 + * @throws IOException 如果发生I/O错误 + * @throws AppBuilderServerException 如果发生应用构建服务器错误 + */ public void modifyChunk(String chunkId, String content, boolean enable, String clientToken) throws IOException, AppBuilderServerException { innerModifyChunk(chunkId, content, enable, clientToken); } + /** + * 修改指定的块内容 + * + * @param chunkId 块的ID + * @param content 块的新内容 + * @param enable 是否启用块,true 表示启用,false 表示禁用 + * @param clientToken 客户端令牌 + * @throws IOException 如果发生I/O错误 + * @throws AppBuilderServerException 如果应用构建服务器发生错误 + */ private void innerModifyChunk(String chunkId, String content, boolean enable, String clientToken) throws IOException, AppBuilderServerException { String url = AppBuilderConfig.CHUNK_MODIFY_URL; @@ -333,14 +601,37 @@ private void innerModifyChunk(String chunkId, String content, boolean enable, St httpClient.execute(postRequest, ChunkCreateResponse.class); } + /** + * 删除指定的文件块。 + * + * @param chunkId 要删除的文件块的ID + * @throws IOException 如果发生I/O错误 + * @throws AppBuilderServerException 如果发生应用构建服务器异常 + */ public void deleteChunk(String chunkId) throws IOException, AppBuilderServerException { innderDeleteChunk(chunkId, java.util.UUID.randomUUID().toString()); } + /** + * 删除指定的块。 + * + * @param chunkId 块标识 + * @param clientToken 客户端令牌 + * @throws IOException 如果发生输入输出异常 + * @throws AppBuilderServerException 如果发生应用程序构建服务器异常 + */ public void deleteChunk(String chunkId, String clientToken) throws IOException, AppBuilderServerException { innderDeleteChunk(chunkId, clientToken); } + /** + * 删除指定块 + * + * @param chunkId 块ID + * @param clientToken 客户端令牌 + * @throws IOException 当I/O操作失败时抛出 + * @throws AppBuilderServerException 当应用构建服务器异常时抛出 + */ private void innderDeleteChunk(String chunkId, String clientToken) throws IOException, AppBuilderServerException { String url = AppBuilderConfig.CHUNK_DELETE_URL; ChunkDeleteRequest request = new ChunkDeleteRequest(); @@ -353,6 +644,14 @@ private void innderDeleteChunk(String chunkId, String clientToken) throws IOExce httpClient.execute(postRequest, ChunkCreateResponse.class); } + /** + * 根据块ID描述块 + * + * @param chunkId 块ID + * @return 描述块的响应对象 + * @throws IOException 如果发生输入输出异常 + * @throws AppBuilderServerException 如果发生应用构建服务器异常 + */ public ChunkDescribeResponse describeChunk(String chunkId) throws IOException, AppBuilderServerException { String url = AppBuilderConfig.CHUNK_DESCRIBE_URL; @@ -368,6 +667,17 @@ public ChunkDescribeResponse describeChunk(String chunkId) return respBody; } + /** + * 描述文档的分块信息 + * + * @param documentId 文档ID + * @param marker 分页标记 + * @param maxKeys 返回的最大分块数量 + * @param type 分块类型 + * @return 返回包含文档分块信息的ChunksDescribeResponse对象 + * @throws IOException 如果发生I/O异常 + * @throws AppBuilderServerException 如果发生AppBuilder服务器异常 + */ public ChunksDescribeResponse describeChunks(String documentId, String marker, Integer maxKeys, String type) throws IOException, AppBuilderServerException { String url = AppBuilderConfig.CHUNKS_DESCRIBE_URL; diff --git a/mkdocs.yml b/mkdocs.yml index 50be6ebf5..7ec97eff6 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -67,6 +67,7 @@ nav: - 监控: - TRACE基础功能: BasisModule/Trace/basic.md - TRACE拓展功能: BasisModule/Trace/phoenix_method.md + - Debug功能: BasisModule/Trace/Debug.md - 部署: - 交互式前端部署: BasisModule/Deployment/AgentChainlit.md - 公有云部署: BasisModule/Deployment/cloud.md @@ -105,7 +106,7 @@ nav: - 环境参数: DevelopGuide/EnvironmentalParameters/env.md - API Reference: - Python API Reference: API-Reference/Python/PythonAPI.md - # - Java API Reference: + - Java API Reference: API-Reference/Java/JavaAPI.md # - Go API Reference: # 主题设置 From f96bb484d6c4a399fea19af5db30c0d1e41e878b Mon Sep 17 00:00:00 2001 From: userpj Date: Mon, 25 Nov 2024 10:28:29 +0800 Subject: [PATCH 25/85] =?UTF-8?q?Python=20ToolCall=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=87=BD=E6=95=B0=E3=80=81=E6=B3=A8=E8=A7=A3=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../end2end_application/agent/tool_call.ipynb | 505 ++++++++++++-- .../Platform/Application/appbuilder_client.md | 410 ++++++++---- python/__init__.py | 3 + python/core/manifest/__init__.py | 13 + python/core/manifest/manifest_decorator.py | 289 ++++++++ python/core/manifest/manifest_signature.py | 162 +++++ python/core/manifest/models.py | 138 ++++ .../tests/test_appbuilder_client_toolcall.py | 171 ++++- python/tests/test_manifest.py | 386 +++++++++++ python/tests/test_manifest_decorator.py | 197 ++++++ python/tests/test_manifest_signature.py | 629 ++++++++++++++++++ 11 files changed, 2692 insertions(+), 211 deletions(-) create mode 100644 python/core/manifest/__init__.py create mode 100644 python/core/manifest/manifest_decorator.py create mode 100644 python/core/manifest/manifest_signature.py create mode 100644 python/core/manifest/models.py create mode 100644 python/tests/test_manifest.py create mode 100644 python/tests/test_manifest_decorator.py create mode 100644 python/tests/test_manifest_signature.py diff --git a/cookbooks/end2end_application/agent/tool_call.ipynb b/cookbooks/end2end_application/agent/tool_call.ipynb index 0946ed830..782fb9503 100644 --- a/cookbooks/end2end_application/agent/tool_call.ipynb +++ b/cookbooks/end2end_application/agent/tool_call.ipynb @@ -269,11 +269,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "vscode": { - "languageId": "plaintext" - } - }, + "metadata": {}, "outputs": [], "source": [ "import os\n", @@ -299,13 +295,17 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "plaintext" + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Agent第一次回答: 很抱歉,由于个人隐私保护的原则,我无法直接查询并告知您本公司张三同学的生日。如果您需要了解这个信息,建议您通过合法且正当的途径,比如直接询问张三同学本人,或者查阅公司内部的员工档案,但前提是您需要确保有合适的权限和授权。尊重和保护个人隐私是我们每个人的责任。\n" + ] } - }, - "outputs": [], + ], "source": [ "message_1 = app_client.run(\n", " conversation_id=conversation_id,\n", @@ -348,17 +348,17 @@ "\n", "##### 赋予应用一个本地查询组件能力\n", "\n", + "以下示例展示了三种方式来使用 ToolCall 进行调用,并演示了如何在 AppBuilder 环境中配置和执行会话调用。\n", + "\n", + "**方式1:使用 JSONSchema 格式直接描述 tools 调用**\n", + "\n", "- 这里我们使用info_dict模拟一个数据库查询的返回结果" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "plaintext" - } - }, + "execution_count": 25, + "metadata": {}, "outputs": [], "source": [ "def get_person_infomation(name: str):\n", @@ -377,20 +377,20 @@ "tools = [\n", " {\n", " \"type\": \"function\",\n", - "\"function\": {\n", - " \"name\": \"get_person_infomation\",\n", - " \"description\": \"查找公司内指定人员的信息\",\n", - " \"parameters\": {\n", - " \"type\": \"object\",\n", - " \"properties\": {\n", - " \"name\": {\n", - " \"type\": \"string\",\n", - " \"description\": \"人员名称,例如:张三、李四\",\n", + " \"function\": {\n", + " \"name\": \"get_person_infomation\",\n", + " \"description\": \"查找公司内指定人员的信息\",\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"name\": {\n", + " \"type\": \"string\",\n", + " \"description\": \"人员名称,例如:张三、李四\",\n", + " },\n", + " },\n", + " \"required\": [\"name\"],\n", " },\n", " },\n", - " \"required\": [\"name\"],\n", - " },\n", - "},\n", " }\n", "]" ] @@ -404,13 +404,59 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "plaintext" + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Agent的中间思考过程:\n", + "{\n", + " \"code\": 0,\n", + " \"message\": \"\",\n", + " \"status\": \"interrupt\",\n", + " \"event_type\": \"Interrupt\",\n", + " \"content_type\": \"contexts\",\n", + " \"detail\": {\n", + " \"text\": {\n", + " \"function_call\": {\n", + " \"thought\": \"用户想要查询公司内指定人员张三的生日信息,这是一个具有明确目的和关键信息的需求。根据我们可用的工具,get_person_infomation 工具能够查找公司内指定人员的信息,包括生日等。因此,通过调用这个工具并传入张三作为参数,我们可以获取到张三的生日信息,从而满足用户的需求。\",\n", + " \"name\": \"get_person_infomation\",\n", + " \"arguments\": {\n", + " \"name\": \"张三\"\n", + " },\n", + " \"usage\": {\n", + " \"prompt_tokens\": 564,\n", + " \"completion_tokens\": 115,\n", + " \"total_tokens\": 679,\n", + " \"name\": \"ERNIE-4.0-8K\",\n", + " \"type\": \"plan\"\n", + " },\n", + " \"tool_call_id\": \"baf86c61-6627-4229-bc81-a17eda1bce36\"\n", + " },\n", + " \"used_tool\": []\n", + " }\n", + " },\n", + " \"usage\": null,\n", + " \"tool_calls\": [\n", + " {\n", + " \"id\": \"baf86c61-6627-4229-bc81-a17eda1bce36\",\n", + " \"type\": \"function\",\n", + " \"function\": {\n", + " \"name\": \"get_person_infomation\",\n", + " \"arguments\": {\n", + " \"name\": \"张三\"\n", + " }\n", + " }\n", + " }\n", + " ]\n", + "}\n", + "Agent思考结束,等待我们上传本地结果\n", + "\n" + ] } - }, - "outputs": [], + ], "source": [ "message_2 = app_client.run(\n", " conversation_id=conversation_id,\n", @@ -465,13 +511,18 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "plaintext" + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "local_func_result: 您要查找的张三的生日是:1980年1月1日\n", + "\n" + ] } - }, - "outputs": [], + ], "source": [ "tool_call = message_2.content.events[-1].tool_calls[-1]\n", "tool_call_id = tool_call.id\n", @@ -494,13 +545,17 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "plaintext" + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Agent 拥有了本地函数调用能力后,回答是: 您要找的张三的生日是1980年1月1日。\n" + ] } - }, - "outputs": [], + ], "source": [ "message_3 = app_client.run(\n", " conversation_id=conversation_id,\n", @@ -582,6 +637,344 @@ "```" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**方式2:使用 function_to_model 将函数对象传递为 ToolCall 的调用**\n", + "\n", + "- 前置步骤:设置环境变量和初始化操作" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import appbuilder\n", + "import os\n", + "import json\n", + "\n", + "# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5\n", + "# 设置环境变量\n", + "# AppBuilder Token,此处为试用Token,速度Quota有限制,正式使用替换为您个人的Token\n", + "os.environ[\"APPBUILDER_TOKEN\"] = \"bce-v3/ALTAK-n5AYUIUJMarF7F7iFXVeK/1bf65eed7c8c7efef9b11388524fa1087f90ea58\"\n", + "\n", + "# 应用为:智能问题解决者\n", + "app_id = \"b9473e78-754b-463a-916b-f0a9097a8e5f\"\n", + "# 初始化智能体\n", + "client = appbuilder.AppBuilderClient(app_id)\n", + "# 创建会话\n", + "conversation_id = client.create_conversation()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- 定义函数和函数列表,按照谷歌规范写好注释" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "#定义示例函数\n", + "def get_current_weather(location: str, unit: str) -> str:\n", + " \"\"\"获取指定中国城市的当前天气信息。\n", + "\n", + " 仅支持中国城市的天气查询。参数 `location` 为中国城市名称,其他国家城市不支持天气查询。\n", + "\n", + " Args:\n", + " location (str): 城市名,例如:\"北京\"。\n", + " unit (int): 温度单位,支持 \"celsius\" 或 \"fahrenheit\"。\n", + "\n", + " Returns:\n", + " str: 天气情况描述\n", + " \"\"\"\n", + " return \"北京今天25度\"\n", + " \n", + "#定义函数列表\n", + "functions = [get_current_weather]\n", + "function_map = {f.__name__: f for f in functions}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- 查看一下function_to_model函数转化的结果" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{\n", + " \"type\": \"function\",\n", + " \"function\": {\n", + " \"name\": \"get_current_weather\",\n", + " \"description\": \"获取指定中国城市的当前天气信息。\\n\\n 仅支持中国城市的天气查询。参数 `location` 为中国城市名称,其他国家城市不支持天气查询。\\n\\n Args:\\n location (str): 城市名,例如:\\\"北京\\\"。\\n unit (int): 温度单位,支持 \\\"celsius\\\" 或 \\\"fahrenheit\\\"。\\n\\n Returns:\\n str: 天气情况描述\",\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"location\": {\n", + " \"name\": \"location\",\n", + " \"type\": \"str\",\n", + " \"description\": null,\n", + " \"required\": true\n", + " },\n", + " \"unit\": {\n", + " \"name\": \"unit\",\n", + " \"type\": \"str\",\n", + " \"description\": null,\n", + " \"required\": true\n", + " }\n", + " },\n", + " \"required\": [\n", + " \"location\",\n", + " \"unit\"\n", + " ]\n", + " },\n", + " \"returns\": {\n", + " \"type\": \"str\",\n", + " \"description\": null\n", + " }\n", + " }\n", + "}\n" + ] + } + ], + "source": [ + "print(json.dumps(appbuilder.Manifest.from_function(get_current_weather).model_dump(), indent=4, ensure_ascii=False))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- 调用大模型进行函数调用" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "ename": "BadRequestException", + "evalue": "request_id=928f30c6-d712-448b-a831-dc4cd15307b0 , http status code is 400, body is {\"code\": \"QuotaLimitExceeded\", \"message\": \"quota\\u8d44\\u6e90\\u5df2\\u8fbe\\u4e0a\\u9650\", \"request_id\": \"928f30c6-d712-448b-a831-dc4cd15307b0\"}", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mBadRequestException\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[5], line 2\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m#调用大模型\u001b[39;00m\n\u001b[0;32m----> 2\u001b[0m msg \u001b[38;5;241m=\u001b[39m \u001b[43mclient\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 3\u001b[0m \u001b[43m \u001b[49m\u001b[43mconversation_id\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconversation_id\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 4\u001b[0m \u001b[43m \u001b[49m\u001b[43mquery\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m今天北京的天气怎么样?\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 5\u001b[0m \u001b[43m \u001b[49m\u001b[43mtools\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m \u001b[49m\u001b[43m[\u001b[49m\u001b[43mappbuilder\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mManifest\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfrom_function\u001b[49m\u001b[43m(\u001b[49m\u001b[43mf\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmodel_dump\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mf\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mfunctions\u001b[49m\u001b[43m]\u001b[49m\n\u001b[1;32m 6\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 7\u001b[0m \u001b[38;5;28mprint\u001b[39m(msg\u001b[38;5;241m.\u001b[39mmodel_dump_json(indent\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m4\u001b[39m))\n\u001b[1;32m 8\u001b[0m \u001b[38;5;66;03m# 获取最后的事件和工具调用信息\u001b[39;00m\n", + "File \u001b[0;32m/opt/anaconda3/envs/testenv/lib/python3.9/site-packages/appbuilder/core/console/appbuilder_client/appbuilder_client.py:306\u001b[0m, in \u001b[0;36mAppBuilderClient.run\u001b[0;34m(self, conversation_id, query, file_ids, stream, tools, tool_outputs, tool_choice, end_user_id, action, **kwargs)\u001b[0m\n\u001b[1;32m 302\u001b[0m url \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mhttp_client\u001b[38;5;241m.\u001b[39mservice_url_v2(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m/app/conversation/runs\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 303\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mhttp_client\u001b[38;5;241m.\u001b[39msession\u001b[38;5;241m.\u001b[39mpost(\n\u001b[1;32m 304\u001b[0m url, headers\u001b[38;5;241m=\u001b[39mheaders, json\u001b[38;5;241m=\u001b[39mreq\u001b[38;5;241m.\u001b[39mmodel_dump(), timeout\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, stream\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m\n\u001b[1;32m 305\u001b[0m )\n\u001b[0;32m--> 306\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mhttp_client\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcheck_response_header\u001b[49m\u001b[43m(\u001b[49m\u001b[43mresponse\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 307\u001b[0m request_id \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mhttp_client\u001b[38;5;241m.\u001b[39mresponse_request_id(response)\n\u001b[1;32m 308\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m stream:\n", + "File \u001b[0;32m/opt/anaconda3/envs/testenv/lib/python3.9/site-packages/appbuilder/core/_client.py:120\u001b[0m, in \u001b[0;36mHTTPClient.check_response_header\u001b[0;34m(response)\u001b[0m\n\u001b[1;32m 116\u001b[0m message \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrequest_id=\u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;124m , http status code is \u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;124m, body is \u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;241m.\u001b[39mformat(\n\u001b[1;32m 117\u001b[0m \u001b[38;5;18m__class__\u001b[39m\u001b[38;5;241m.\u001b[39mresponse_request_id(response), status_code, response\u001b[38;5;241m.\u001b[39mtext\n\u001b[1;32m 118\u001b[0m )\n\u001b[1;32m 119\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m status_code \u001b[38;5;241m==\u001b[39m requests\u001b[38;5;241m.\u001b[39mcodes\u001b[38;5;241m.\u001b[39mbad_request:\n\u001b[0;32m--> 120\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m BadRequestException(message)\n\u001b[1;32m 121\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m status_code \u001b[38;5;241m==\u001b[39m requests\u001b[38;5;241m.\u001b[39mcodes\u001b[38;5;241m.\u001b[39mforbidden:\n\u001b[1;32m 122\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m ForbiddenException(message)\n", + "\u001b[0;31mBadRequestException\u001b[0m: request_id=928f30c6-d712-448b-a831-dc4cd15307b0 , http status code is 400, body is {\"code\": \"QuotaLimitExceeded\", \"message\": \"quota\\u8d44\\u6e90\\u5df2\\u8fbe\\u4e0a\\u9650\", \"request_id\": \"928f30c6-d712-448b-a831-dc4cd15307b0\"}" + ] + } + ], + "source": [ + "#调用大模型\n", + "msg = client.run(\n", + " conversation_id=conversation_id,\n", + " query=\"今天北京的天气怎么样?\",\n", + " tools = [appbuilder.Manifest.from_function(f).model_dump() for f in functions]\n", + " )\n", + "print(msg.model_dump_json(indent=4))\n", + "# 获取最后的事件和工具调用信息\n", + "event = msg.content.events[-1]\n", + "tool_call = event.tool_calls[-1]\n", + "\n", + "# 获取函数名称和参数\n", + "name = tool_call.function.name\n", + "args = tool_call.function.arguments\n", + "\n", + "# 将函数名称映射到具体的函数并执行\n", + "raw_result = function_map[name](**args)\n", + "\n", + "# 传递工具的输出\n", + "msg_2 = client.run(\n", + " conversation_id=conversation_id,\n", + " tool_outputs=[{\n", + " \"tool_call_id\": tool_call.id,\n", + " \"output\": str(raw_result)\n", + " }],\n", + ")\n", + "print(msg_2.model_dump_json(indent=4))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**方式3: 使用装饰器进行描述**\n", + "\n", + "- 前置步骤:设置环境变量和初始化操作" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import json\n", + "import appbuilder\n", + "from appbuilder import manifest, manifest_parameter\n", + "\n", + "# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5\n", + "# 设置环境变量\n", + "# AppBuilder Token,此处为试用Token,速度Quota有限制,正式使用替换为您个人的Token\n", + "#os.environ[\"APPBUILDER_TOKEN\"] = \"bce-v3/ALTAK-n5AYUIUJMarF7F7iFXVeK/1bf65eed7c8c7efef9b11388524fa1087f90ea58\"\n", + "os.environ[\"APPBUILDER_TOKEN\"] = \"bce-v3/ALTAK-DKaql4wY9ojwp2uMe8IEj/7ae1190aff0684153de365381d9b06beab3064c5\"\n", + "\n", + "# 应用为:智能问题解决者\n", + "#app_id = \"b9473e78-754b-463a-916b-f0a9097a8e5f\"\n", + "app_id = \"7cc4c21f-0e25-4a76-baf7-01a2b923a1a7\"\n", + "# 初始化智能体\n", + "client = appbuilder.AppBuilderClient(app_id)\n", + "# 创建会话\n", + "conversation_id = client.create_conversation()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- 定义函数和函数列表,并用装饰器对函数进行进行描述." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "#使用function装饰描述函数,function_parameter装饰器描述参数,function_return装饰器描述函数返回值。\n", + "@manifest(description=\"获取指定中国城市的当前天气信息。仅支持中国城市的天气查询。参数 `location` 为中国城市名称,其他国家城市不支持天气查询。\")\n", + "@manifest_parameter(name=\"location\", description=\"城市名,例如:北京。\")\n", + "@manifest_parameter(name=\"unit\", description=\"温度单位,支持 'celsius' 或 'fahrenheit'\")\n", + "#定义示例函数\n", + "def get_current_weather(location: str, unit: str) -> str:\n", + " return \"北京今天25度\"\n", + "\n", + "#定义函数列表\n", + "functions = [get_current_weather]\n", + "function_map = {f.__name__: f for f in functions}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- 查看一下装饰器的转化内容" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{\n", + " \"type\": \"function\",\n", + " \"function\": {\n", + " \"name\": \"get_current_weather\",\n", + " \"description\": \"获取指定中国城市的当前天气信息。仅支持中国城市的天气查询。参数 `location` 为中国城市名称,其他国家城市不支持天气查询。\",\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"location\": {\n", + " \"name\": \"location\",\n", + " \"type\": \"str\",\n", + " \"description\": \"城市名,例如:北京。\",\n", + " \"required\": true\n", + " },\n", + " \"unit\": {\n", + " \"name\": \"unit\",\n", + " \"type\": \"str\",\n", + " \"description\": \"温度单位,支持 'celsius' 或 'fahrenheit'\",\n", + " \"required\": true\n", + " }\n", + " },\n", + " \"required\": [\n", + " \"location\",\n", + " \"unit\"\n", + " ]\n", + " }\n", + " }\n", + "}\n" + ] + } + ], + "source": [ + "# 将 model_dump() 的输出进行格式化打印\n", + "print(json.dumps(get_current_weather.__ab_manifest__.model_dump(), indent=4, ensure_ascii=False))" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "ename": "BadRequestException", + "evalue": "request_id=76224253-163f-46d3-a5eb-b22c6fcec2be , http status code is 400, body is {\"code\": \"QuotaLimitExceeded\", \"message\": \"quota\\u8d44\\u6e90\\u5df2\\u8fbe\\u4e0a\\u9650\", \"request_id\": \"76224253-163f-46d3-a5eb-b22c6fcec2be\"}", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mBadRequestException\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[10], line 2\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m#调用大模型\u001b[39;00m\n\u001b[0;32m----> 2\u001b[0m msg \u001b[38;5;241m=\u001b[39m \u001b[43mclient\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 3\u001b[0m \u001b[43m \u001b[49m\u001b[43mconversation_id\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mconversation_id\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 4\u001b[0m \u001b[43m \u001b[49m\u001b[43mquery\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m今天北京的天气怎么样?\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 5\u001b[0m \u001b[43m \u001b[49m\u001b[43mtools\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m \u001b[49m\u001b[43m[\u001b[49m\u001b[43mget_current_weather\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m__ab_manifest__\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmodel_dump\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m]\u001b[49m\n\u001b[1;32m 6\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 7\u001b[0m \u001b[38;5;28mprint\u001b[39m(msg\u001b[38;5;241m.\u001b[39mmodel_dump_json(indent\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m4\u001b[39m))\n\u001b[1;32m 8\u001b[0m \u001b[38;5;66;03m# 获取最后的事件和工具调用信息\u001b[39;00m\n", + "File \u001b[0;32m/opt/anaconda3/envs/testenv/lib/python3.9/site-packages/appbuilder/core/console/appbuilder_client/appbuilder_client.py:306\u001b[0m, in \u001b[0;36mAppBuilderClient.run\u001b[0;34m(self, conversation_id, query, file_ids, stream, tools, tool_outputs, tool_choice, end_user_id, action, **kwargs)\u001b[0m\n\u001b[1;32m 302\u001b[0m url \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mhttp_client\u001b[38;5;241m.\u001b[39mservice_url_v2(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m/app/conversation/runs\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 303\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mhttp_client\u001b[38;5;241m.\u001b[39msession\u001b[38;5;241m.\u001b[39mpost(\n\u001b[1;32m 304\u001b[0m url, headers\u001b[38;5;241m=\u001b[39mheaders, json\u001b[38;5;241m=\u001b[39mreq\u001b[38;5;241m.\u001b[39mmodel_dump(), timeout\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, stream\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m\n\u001b[1;32m 305\u001b[0m )\n\u001b[0;32m--> 306\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mhttp_client\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcheck_response_header\u001b[49m\u001b[43m(\u001b[49m\u001b[43mresponse\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 307\u001b[0m request_id \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mhttp_client\u001b[38;5;241m.\u001b[39mresponse_request_id(response)\n\u001b[1;32m 308\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m stream:\n", + "File \u001b[0;32m/opt/anaconda3/envs/testenv/lib/python3.9/site-packages/appbuilder/core/_client.py:120\u001b[0m, in \u001b[0;36mHTTPClient.check_response_header\u001b[0;34m(response)\u001b[0m\n\u001b[1;32m 116\u001b[0m message \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrequest_id=\u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;124m , http status code is \u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;124m, body is \u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;241m.\u001b[39mformat(\n\u001b[1;32m 117\u001b[0m \u001b[38;5;18m__class__\u001b[39m\u001b[38;5;241m.\u001b[39mresponse_request_id(response), status_code, response\u001b[38;5;241m.\u001b[39mtext\n\u001b[1;32m 118\u001b[0m )\n\u001b[1;32m 119\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m status_code \u001b[38;5;241m==\u001b[39m requests\u001b[38;5;241m.\u001b[39mcodes\u001b[38;5;241m.\u001b[39mbad_request:\n\u001b[0;32m--> 120\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m BadRequestException(message)\n\u001b[1;32m 121\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m status_code \u001b[38;5;241m==\u001b[39m requests\u001b[38;5;241m.\u001b[39mcodes\u001b[38;5;241m.\u001b[39mforbidden:\n\u001b[1;32m 122\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m ForbiddenException(message)\n", + "\u001b[0;31mBadRequestException\u001b[0m: request_id=76224253-163f-46d3-a5eb-b22c6fcec2be , http status code is 400, body is {\"code\": \"QuotaLimitExceeded\", \"message\": \"quota\\u8d44\\u6e90\\u5df2\\u8fbe\\u4e0a\\u9650\", \"request_id\": \"76224253-163f-46d3-a5eb-b22c6fcec2be\"}" + ] + } + ], + "source": [ + "#调用大模型\n", + "msg = client.run(\n", + " conversation_id=conversation_id,\n", + " query=\"今天北京的天气怎么样?\",\n", + " tools = [get_current_weather.__ab_manifest__.model_dump()]\n", + " )\n", + "print(msg.model_dump_json(indent=4))\n", + "# 获取最后的事件和工具调用信息\n", + "event = msg.content.events[-1]\n", + "tool_call = event.tool_calls[-1]\n", + "\n", + "# 获取函数名称和参数\n", + "name = tool_call.function.name\n", + "args = tool_call.function.arguments\n", + "\n", + "# 将函数名称映射到具体的函数并执行\n", + "raw_result = function_map[name](**args)\n", + "\n", + "# 传递工具的输出\n", + "msg_2 = client.run(\n", + " conversation_id=conversation_id,\n", + " tool_outputs=[{\n", + " \"tool_call_id\": tool_call.id,\n", + " \"output\": str(raw_result)\n", + " }],\n", + ")\n", + "print(msg_2.model_dump_json(indent=4))" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -773,10 +1166,24 @@ } ], "metadata": { + "kernelspec": { + "display_name": "testenv", + "language": "python", + "name": "python3" + }, "language_info": { - "name": "python" + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.20" } }, "nbformat": 4, "nbformat_minor": 2 -} +} \ No newline at end of file diff --git a/docs/BasisModule/Platform/Application/appbuilder_client.md b/docs/BasisModule/Platform/Application/appbuilder_client.md index 672baf1c9..6f3d320fd 100644 --- a/docs/BasisModule/Platform/Application/appbuilder_client.md +++ b/docs/BasisModule/Platform/Application/appbuilder_client.md @@ -25,9 +25,9 @@ AppBuilderClient组件支持调用在[百度智能云千帆AppBuilder](https://c #### 方法参数 -| 参数名称 | 参数类型 | 描述 | 示例值 | -|--------|--------|------------|-----------| -| app_id | string | 线上Agent应用的ID | "正确的应用ID" | +| 参数名称 | 参数类型 | 描述 | 示例值 | +| -------- | -------- | ----------------- | -------------- | +| app_id | string | 线上Agent应用的ID | "正确的应用ID" | #### 方法返回值 @@ -40,67 +40,67 @@ AppBuilderClient组件支持调用在[百度智能云千帆AppBuilder](https://c #### 方法返回值 - 参数名称 | 参数类型 | 描述 | 示例值 | -|--------|--------|------------|-----------| -| conversation_id | string | 会话的ID | "80c5bbee-931d-4ed9-a4ff-63e1971bd071" | + | 参数名称 | 参数类型 | 描述 | 示例值 | + | --------------- | -------- | -------- | -------------------------------------- | + | conversation_id | string | 会话的ID | "80c5bbee-931d-4ed9-a4ff-63e1971bd071" | ### `AppBuilderClient().upload_local_file(file_path: str)-> str` #### 方法参数 -| 参数名称 | 参数类型 | 描述 | 示例值 | -|--------|--------|------------|-----------| -| file_path | string | 文件路径 | "正确的文件路径" | +| 参数名称 | 参数类型 | 描述 | 示例值 | +| --------- | -------- | -------- | ---------------- | +| file_path | string | 文件路径 | "正确的文件路径" | #### 方法返回值 -| 参数名称 | 参数类型 | 描述 | 示例值 | -|--------|--------|------------|-----------| -| file_id | string | 文件ID | "80c5bbee-931d-4ed9-a4ff-63e1971bd | +| 参数名称 | 参数类型 | 描述 | 示例值 | +| -------- | -------- | ------ | ---------------------------------- | +| file_id | string | 文件ID | "80c5bbee-931d-4ed9-a4ff-63e1971bd | ### `AppBuilderClient().run() -> Message` #### 方法参数 -| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | -| --------------- | ------------------ | -------- | ------------------------------------------------------------ | ----------------- | -| conversation_id | String | 是 | 会话ID | | -| query | String | 否 | query问题内容 | "今天天气怎么样?" | -| file_ids | list[String] | 否 | 对话可引用的文档ID | | -| stream | Bool | 否 | 为true时则流式返回,为false时则一次性返回所有内容, 推荐设为true,降低首token时延 | False | -| end_user_id | String | 否 | 终端用户ID,限制6 - 64字符 | | -| tools | List[Tool] | 否 | 一个列表,其中每个字典对应一个工具的配置 | | -| tools[0] | Tool | 否 | 工具配置 | | +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +| --------------- | ------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- | +| conversation_id | String | 是 | 会话ID | | +| query | String | 否 | query问题内容 | "今天天气怎么样?" | +| file_ids | list[String] | 否 | 对话可引用的文档ID | | +| stream | Bool | 否 | 为true时则流式返回,为false时则一次性返回所有内容, 推荐设为true,降低首token时延 | False | +| end_user_id | String | 否 | 终端用户ID,限制6 - 64字符 | | +| tools | List[Tool] | 否 | 一个列表,其中每个字典对应一个工具的配置 | | +| tools[0] | Tool | 否 | 工具配置 | | | +type | String | 否 | 枚举:
**file_retrieval**: 知识库检索工具能够理解文档内容,支持用户针对文档内容的问答。
**code_interpreter**: 代码解释器, 代码解释器能够生成并执行代码,从而协助用户解决复杂问题,涵盖科学计算(包括普通数学计算题)、数据可视化、文件编辑处理(图片、PDF文档、视频、音频等)、文件格式转换(如WAV、MP3、text、SRT、PNG、jpg、MP4、GIF、MP3等)、数据分析&清洗&处理(文件以excel、csv格式为主)、机器学习&深度学习建模&自然语言处理等多个领域。
**function**: 支持fucntion call模式调用工具 | | -| +function | Function | 否 | Function工具描述
仅当**type为**`**function**` 时需要且必须填写 | | -| ++name | String | 否 | 函数名
只允许数字、大小写字母和中划线和下划线,最大长度为64个字符。一次运行中唯一。 | | -| ++description | String | 否 | 工具描述 | | -| ++parameters | Dict | 否 | 工具参数, json_schema格式 | | -| tool_outputs | List[ToolOutput] | 否 | 内容为本地的工具执行结果,以自然语言/json dump str描述 | | -| tool_outputs[0] | ToolOutput | 否 | 工具执行结果 | | -| +tool_call_id | String | 否 | 工具调用ID | | -| +output | String | 否 | 工具输出 | | -| tool_choice | ToolChoice | 否 | 控制大模型使用组件的方式,仅对自主规划Agent生效。 | | -| +type | String | 否 | auto/function,auto表示由LLM自动判断调什么组件;function表示由用户指定调用哪个组件。 | | -| +function | ToolChoiceFunction | 否 | 组件对象,包括组件的英文名称和入参 | | -| ++name | String | 否 | 组件的英文名称(唯一标识) | | -| ++input | String | 否 | 组件入参,当组件没有入参时填入空对象{} | | +| +function | Function | 否 | Function工具描述
仅当**type为**`**function**` 时需要且必须填写 | | +| ++name | String | 否 | 函数名
只允许数字、大小写字母和中划线和下划线,最大长度为64个字符。一次运行中唯一。 | | +| ++description | String | 否 | 工具描述 | | +| ++parameters | Dict | 否 | 工具参数, json_schema格式 | | +| tool_outputs | List[ToolOutput] | 否 | 内容为本地的工具执行结果,以自然语言/json dump str描述 | | +| tool_outputs[0] | ToolOutput | 否 | 工具执行结果 | | +| +tool_call_id | String | 否 | 工具调用ID | | +| +output | String | 否 | 工具输出 | | +| tool_choice | ToolChoice | 否 | 控制大模型使用组件的方式,仅对自主规划Agent生效。 | | +| +type | String | 否 | auto/function,auto表示由LLM自动判断调什么组件;function表示由用户指定调用哪个组件。 | | +| +function | ToolChoiceFunction | 否 | 组件对象,包括组件的英文名称和入参 | | +| ++name | String | 否 | 组件的英文名称(唯一标识) | | +| ++input | String | 否 | 组件入参,当组件没有入参时填入空对象{} | | #### Run方法非流式返回值 Run非流式方法返回一个`Message`对象,该对象包含以下属性: -| 参数名称 | 参数类型 | 描述 | 示例值 | -|----------------|------------------------|------------|------------------------------------------------------------------------| -| content | AppBuilderClientAnswer | 对话返回结果 | | -| +answer | String | 智能体应用返回的回答 | | -| +events | List[Event] | 事件列表 | | -| +events[0] | Event | 具体事件内容 | | -| ++code | String | 错误码 | | -| ++message | String | 错误具体消息 | | -| ++status | String | 事件状态 | 状态描述,preparing(准备运行)running(运行中)error(执行错误) done(执行完成) | -| ++event_type | String | 事件类型 | | -| ++content_type | String | 内容类型 | 可选值包括:code text, image, status,image, function_call, rag, audio、video等 | -| ++detail | Dict | 事件输出详情 | 代码解释器、文生图、工具组件、RAG等的详细输出内容 | -| ++usage | Usage | 模型调用的token用量 | Usage(prompt_tokens=1322, completion_tokens=80, total_tokens=1402, name='ERNIE-4.0-8K') | +| 参数名称 | 参数类型 | 描述 | 示例值 | +| -------------- | ---------------------- | -------------------- | --------------------------------------------------------------------------------------- | +| content | AppBuilderClientAnswer | 对话返回结果 | | +| +answer | String | 智能体应用返回的回答 | | +| +events | List[Event] | 事件列表 | | +| +events[0] | Event | 具体事件内容 | | +| ++code | String | 错误码 | | +| ++message | String | 错误具体消息 | | +| ++status | String | 事件状态 | 状态描述,preparing(准备运行)running(运行中)error(执行错误) done(执行完成) | +| ++event_type | String | 事件类型 | | +| ++content_type | String | 内容类型 | 可选值包括:code text, image, status,image, function_call, rag, audio、video等 | +| ++detail | Dict | 事件输出详情 | 代码解释器、文生图、工具组件、RAG等的详细输出内容 | +| ++usage | Usage | 模型调用的token用量 | Usage(prompt_tokens=1322, completion_tokens=80, total_tokens=1402, name='ERNIE-4.0-8K') | `AppBuilderClientAnswer`类型定义如下: ```python @@ -139,9 +139,9 @@ class Event(BaseModel): #### Run方法流式返回值 -| 参数名称 | 参数类型 | 描述 | 示例值 | -|---------|------------------|--------------|-----| -| content | Python Generator | 可迭代,每次迭代返回AppBuilderClientAnswer类型 | 无 | +| 参数名称 | 参数类型 | 描述 | 示例值 | +| -------- | ---------------- | ---------------------------------------------- | ------ | +| content | Python Generator | 可迭代,每次迭代返回AppBuilderClientAnswer类型 | 无 | #### 非流式调用示例 @@ -229,8 +229,14 @@ for content in message.content: print(answer) ``` + + #### Run方法带ToolCall调用示例 +以下示例展示了三种方式来使用 ToolCall 进行调用,并演示了如何在 AppBuilder 环境中配置和执行会话调用。 + +**方式1:使用 JSONSchema 格式直接描述 tools 调用** + ```python import appbuilder from appbuilder.core.console.appbuilder_client import data_class @@ -279,6 +285,129 @@ msg_2 = client.run( print(msg_2.model_dump_json(indent=4)) ``` +**方式2: 使用 function_to_model 将函数对象传递为 ToolCall 的调用** + +```python +import appbuilder +import os + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +# 设置环境变量 +os.environ["APPBUILDER_TOKEN"] = "..." +app_id = "..." # 已发布AppBuilder应用的ID +# 初始化智能体 +client = appbuilder.AppBuilderClient(app_id) +# 创建会话 +conversation_id = client.create_conversation() +#注意:要使用此方法要为函数写好注释。最好按照谷歌规范来写 + +#定义示例函数 +def get_current_weather(location: str, unit: str) -> str: + """获取指定中国城市的当前天气信息。 + + 仅支持中国城市的天气查询。参数 `location` 为中国城市名称,其他国家城市不支持天气查询。 + + Args: + location (str): 城市名,例如:"北京"。 + unit (int): 温度单位,支持 "celsius" 或 "fahrenheit"。 + + Returns: + str: 天气情况描述 + """ + return "北京今天25度" + +#定义函数列表 +functions = [get_current_weather] +function_map = {f.__name__: f for f in functions} +#调用大模型 +msg = client.run( + conversation_id=conversation_id, + query="今天北京的天气怎么样?", + tools = [appbuilder.Manifest.from_function(f).model_dump() for f in functions] + ) +print(msg.model_dump_json(indent=4)) +# 获取最后的事件和工具调用信息 +event = msg.content.events[-1] +tool_call = event.tool_calls[-1] + +# 获取函数名称和参数 +name = tool_call.function.name +args = tool_call.function.arguments + +# 将函数名称映射到具体的函数并执行 +raw_result = function_map[name](**args) + +# 传递工具的输出 +msg_2 = client.run( + conversation_id=conversation_id, + tool_outputs=[{ + "tool_call_id": tool_call.id, + "output": str(raw_result) + }], +) +print(msg_2.model_dump_json(indent=4)) +``` + +**方式3: 使用装饰器进行描述** + +```python +import os +import json +import appbuilder +from appbuilder import manifest, manifest_parameter + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +# 设置环境变量 +os.environ["APPBUILDER_TOKEN"] = "" +app_id = "" # 已发布AppBuilder应用的ID +# 初始化智能体 +client = appbuilder.AppBuilderClient(app_id) +# 创建会话 +conversation_id = client.create_conversation() + +#使用manifest装饰描述函数,manifest_parameter装饰器描述参数,manifest_return装饰器描述函数返回值。 +@manifest(description="获取指定中国城市的当前天气信息。仅支持中国城市的天气查询。参数 `location` 为中国城市名称,其他国家城市不支持天气查询。") +@manifest_parameter(name="location", description="城市名,例如:北京。") +@manifest_parameter(name="unit", description="温度单位,支持 'celsius' 或 'fahrenheit'") +#定义示例函数 +def get_current_weather(location: str, unit: str) -> str: + return "北京今天25度" + +print(json.dumps((get_current_weather.__ab_manifest__).model_dump(), indent=4, ensure_ascii=False)) +#定义函数列表 +functions = [get_current_weather] +function_map = {f.__name__: f for f in functions} +#调用大模型 +msg = client.run( + conversation_id=conversation_id, + query="今天北京的天气怎么样?", + tools = [(get_current_weather.__ab_manifest__).model_dump()] + ) +print(msg.model_dump_json(indent=4)) +# 获取最后的事件和工具调用信息 +event = msg.content.events[-1] +tool_call = event.tool_calls[-1] + +# 获取函数名称和参数 +name = tool_call.function.name +args = tool_call.function.arguments + +# 将函数名称映射到具体的函数并执行 +raw_result = function_map[name](**args) + +# 传递工具的输出 +msg_2 = client.run( + conversation_id=conversation_id, + tool_outputs=[{ + "tool_call_id": tool_call.id, + "output": str(raw_result) + }], +) +print(msg_2.model_dump_json(indent=4)) +``` + + + #### Run方法带ToolChoice使用示例: * 注意:当前功能为试运行阶段,可能存在如下问题,如使用过程遇到其他问题,欢迎提issue或微信群讨论。 @@ -320,9 +449,9 @@ answer = app_builder_client.run( #### 方法参数 -| 参数名称 | 参数类型 | 描述 | 示例值 | -|--------|-----------|------------|-----------| -| appID | String | 线上Agent应用的ID | "正确的应用ID" | +| 参数名称 | 参数类型 | 描述 | 示例值 | +| -------- | -------- | ----------------- | -------------- | +| appID | String | 线上Agent应用的ID | "正确的应用ID" | #### 方法返回值 @@ -336,54 +465,54 @@ answer = app_builder_client.run( #### 方法返回值 -| 参数名称 | 参数类型 | 描述 | 示例值 | -|--------|-----------|------------|-----------| -| conversationId | String | 创建的会话ID | "正确的会话ID" | +| 参数名称 | 参数类型 | 描述 | 示例值 | +| -------------- | -------- | ------------ | -------------- | +| conversationId | String | 创建的会话ID | "正确的会话ID" | ### ```AppBuilderClient().run()``` #### Run方法入参`AppBuilderCientRunRequest` -| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | -|--------|-------|------|-------|---------| -| query | String | 是 | query内容 | "汽车性能参数怎么样" | -| conversationId | String | 是 | 对话id,可以通过createConversation()获取 | | -| stream | boolean | 是 | 为true时则流式返回,为false时则一次性返回所有内容, 推荐设为true,降低首token时延 | | -| tools | List[Tool] | 否 | 一个列表,其中每个字典对应一个工具的配置 | | -| tools[0] | Tool | 否 | 工具配置 | | -| +type | String | 否 | 枚举:
**file_retrieval**: 知识库检索工具能够理解文档内容,支持用户针对文档内容的问答。
**code_interpreter**: 代码解释器, 代码解释器能够生成并执行代码,从而协助用户解决复杂问题,涵盖科学计算(包括普通数学计算题)、数据可视化、文件编辑处理(图片、PDF文档、视频、音频等)、文件格式转换(如WAV、MP3、text、SRT、PNG、jpg、MP4、GIF、MP3等)、数据分析&清洗&处理(文件以excel、csv格式为主)、机器学习&深度学习建模&自然语言处理等多个领域。
**function**: 支持fucntion call模式调用工具 | | -| +function | Function | 否 | Function工具描述
仅当**type为**`**function**` 时需要且必须填写 | | -| ++name | String | 否 | 函数名
只允许数字、大小写字母和中划线和下划线,最大长度为64个字符。一次运行中唯一。 | | -| ++description | String | 否 | 工具描述 | | -| ++parameters | Dict | 否 | 工具参数, json_schema格式 | | -| tool_outputs | List[ToolOutput] | 否 | 内容为本地的工具执行结果,以自然语言/json dump str描述 | | -| tool_outputs[0] | ToolOutput | 否 | 工具执行结果 | | -| +tool_call_id | String | 否 | 工具调用ID | | -| +output | String | 否 | 工具输出 | | -| tool_choice | ToolChoice | 否 | 控制大模型使用组件的方式,仅对自主规划Agent生效。 | | -| +type | String | 否 | auto/function,auto表示由LLM自动判断调什么组件;function表示由用户指定调用哪个组件。 | | -| +function | ToolChoiceFunction | 否 | 组件对象,包括组件的英文名称和入参 | | -| ++name | String | 否 | 组件的英文名称(唯一标识) | | -| ++input | String | 否 | 组件入参,当组件没有入参时填入空对象{} | | +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +| --------------- | ------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | +| query | String | 是 | query内容 | "汽车性能参数怎么样" | +| conversationId | String | 是 | 对话id,可以通过createConversation()获取 | | +| stream | boolean | 是 | 为true时则流式返回,为false时则一次性返回所有内容, 推荐设为true,降低首token时延 | | +| tools | List[Tool] | 否 | 一个列表,其中每个字典对应一个工具的配置 | | +| tools[0] | Tool | 否 | 工具配置 | | +| +type | String | 否 | 枚举:
**file_retrieval**: 知识库检索工具能够理解文档内容,支持用户针对文档内容的问答。
**code_interpreter**: 代码解释器, 代码解释器能够生成并执行代码,从而协助用户解决复杂问题,涵盖科学计算(包括普通数学计算题)、数据可视化、文件编辑处理(图片、PDF文档、视频、音频等)、文件格式转换(如WAV、MP3、text、SRT、PNG、jpg、MP4、GIF、MP3等)、数据分析&清洗&处理(文件以excel、csv格式为主)、机器学习&深度学习建模&自然语言处理等多个领域。
**function**: 支持fucntion call模式调用工具 | | +| +function | Function | 否 | Function工具描述
仅当**type为**`**function**` 时需要且必须填写 | | +| ++name | String | 否 | 函数名
只允许数字、大小写字母和中划线和下划线,最大长度为64个字符。一次运行中唯一。 | | +| ++description | String | 否 | 工具描述 | | +| ++parameters | Dict | 否 | 工具参数, json_schema格式 | | +| tool_outputs | List[ToolOutput] | 否 | 内容为本地的工具执行结果,以自然语言/json dump str描述 | | +| tool_outputs[0] | ToolOutput | 否 | 工具执行结果 | | +| +tool_call_id | String | 否 | 工具调用ID | | +| +output | String | 否 | 工具输出 | | +| tool_choice | ToolChoice | 否 | 控制大模型使用组件的方式,仅对自主规划Agent生效。 | | +| +type | String | 否 | auto/function,auto表示由LLM自动判断调什么组件;function表示由用户指定调用哪个组件。 | | +| +function | ToolChoiceFunction | 否 | 组件对象,包括组件的英文名称和入参 | | +| ++name | String | 否 | 组件的英文名称(唯一标识) | | +| ++input | String | 否 | 组件入参,当组件没有入参时填入空对象{} | | #### Run方法出参 -| 参数名称 | 参数类型 | 描述 | 示例值 | -|----------------------|--------------|--------------------|-----| -| AppBuilderClientIterator | AppBuilderClientIterator | 回答迭代器,流式/非流式均统一返回该类型,每次迭代返回AppBuilderClientIterator类型 | | +| 参数名称 | 参数类型 | 描述 | 示例值 | +| ------------------------ | ------------------------ | -------------------------------------------------------------------------------- | ------ | +| AppBuilderClientIterator | AppBuilderClientIterator | 回答迭代器,流式/非流式均统一返回该类型,每次迭代返回AppBuilderClientIterator类型 | | #### 迭代AppBuilderClientIterator -| 参数名称 | 参数类型 | 描述 | 示例值 | -|---------------|-------------|------------|------------------------------------------------------------------------| -| +answer | String | 智能体应用返回的回答 | | -| +events | Event[] | 事件列表 | | -| +events[0] | Event | 具体事件内容 | | -| ++code | string | 错误码 | | -| ++message | string | 错误具体消息 | | -| ++status | string | 事件状态 | 状态描述,preparing(准备运行)running(运行中)error(执行错误) done(执行完成)| -| ++eventType | string | 事件类型 | | -| ++contentType | string | 内容类型 | 可选值包括:code text, image, status,image, function_call, rag, audio、video等 | -| ++detail | Map | 事件输出详情 | 代码解释器、文生图、工具组件、RAG等的详细输出内容 | -| ++usage | Usage | 模型调用的token用量 | Usage(prompt_tokens=1322, completion_tokens=80, total_tokens=1402, name='ERNIE-4.0-8K') | +| 参数名称 | 参数类型 | 描述 | 示例值 | +| ------------- | ------------------- | -------------------- | --------------------------------------------------------------------------------------- | +| +answer | String | 智能体应用返回的回答 | | +| +events | Event[] | 事件列表 | | +| +events[0] | Event | 具体事件内容 | | +| ++code | string | 错误码 | | +| ++message | string | 错误具体消息 | | +| ++status | string | 事件状态 | 状态描述,preparing(准备运行)running(运行中)error(执行错误) done(执行完成) | +| ++eventType | string | 事件类型 | | +| ++contentType | string | 内容类型 | 可选值包括:code text, image, status,image, function_call, rag, audio、video等 | +| ++detail | Map | 事件输出详情 | 代码解释器、文生图、工具组件、RAG等的详细输出内容 | +| ++usage | Usage | 模型调用的token用量 | Usage(prompt_tokens=1322, completion_tokens=80, total_tokens=1402, name='ERNIE-4.0-8K') | #### 示例代码 @@ -709,51 +838,51 @@ class AppBuilderClientDemo { #### 方法参数 -| 参数名称 | 参数类型 | 描述 | 示例值 | -|--------|-----------|------------|-----------| -| app_id | string | 线上Agent应用的ID | "正确的应用ID" | -| config | SDKConfig | SDK配置信息 | | +| 参数名称 | 参数类型 | 描述 | 示例值 | +| -------- | --------- | ----------------- | -------------- | +| app_id | string | 线上Agent应用的ID | "正确的应用ID" | +| config | SDKConfig | SDK配置信息 | | ### ```CreateConversation()``` #### 方法入参 无 #### 方法出参 -| 参数名称 | 参数类型 | 描述 | 示例值 | -|--------------|----------|----------------------|---------------| -| conversation | str | 创建成功的对话对象,后续操作都基于该对象进行 | | +| 参数名称 | 参数类型 | 描述 | 示例值 | +| ------------ | -------- | -------------------------------------------- | ------ | +| conversation | str | 创建成功的对话对象,后续操作都基于该对象进行 | | ### ```Run()``` #### Run方法入参 -| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | -|----------------|--------------|------|----------------------------------------------------|-------------| -| conversationID | string | 是 | 对话ID,可以通过CreateConversation()获取 | | -| query | string | 是 | query内容 | "汽车性能参数怎么样" | -| stream | bool | 是 | 为true时则流式返回,为false时则一次性返回所有内容, 推荐设为true,降低首token时延 | | -| file_ids | list[String] | 否 | 对话可引用的文档ID | | +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +| -------------- | ------------ | -------- | -------------------------------------------------------------------------------- | -------------------- | +| conversationID | string | 是 | 对话ID,可以通过CreateConversation()获取 | | +| query | string | 是 | query内容 | "汽车性能参数怎么样" | +| stream | bool | 是 | 为true时则流式返回,为false时则一次性返回所有内容, 推荐设为true,降低首token时延 | | +| file_ids | list[String] | 否 | 对话可引用的文档ID | | #### Run方法出参 -| 参数名称 | 参数类型 | 描述 | 示例值 | -|----------------------|----------------------|----------------------|-----| -| AppBuilderClientIterator | AppBuilderClientIterator | 回答迭代器,流式/非流式均统一返回该类型 | | -| error | error | 存在错误时error不为nil,反之 | | +| 参数名称 | 参数类型 | 描述 | 示例值 | +| ------------------------ | ------------------------ | --------------------------------------- | ------ | +| AppBuilderClientIterator | AppBuilderClientIterator | 回答迭代器,流式/非流式均统一返回该类型 | | +| error | error | 存在错误时error不为nil,反之 | | #### 迭代AgentBuilderIterator -| 参数名称 | 参数类型 | 描述 | 示例值 | -|---------------|-------------|------------|------------------------------------------------------------------------| -| +Answer | string | 智能体应用返回的回答 | | -| +Events | []Event | 事件列表 | | -| +Events[0] | Event | 具体事件内容 | | -| ++Code | string | 错误码 | | -| ++Message | string | 错误具体消息 | | -| ++Status | string | 事件状态 | 状态描述,preparing(准备运行)running(运行中)error(执行错误) done(执行完成) | -| ++EventType | string | 事件类型 | | -| ++ContentType | string | 内容类型 | 可选值包括:code text, image, status,image, function_call, rag, audio、video等 | -| ++Detail | interface{} | 事件输出详情 | 代码解释器、文生图、工具组件、RAG等的详细输出内容 | -| ++Usage | Usage | 模型调用的token用量 | Usage(prompt_tokens=1322, completion_tokens=80, total_tokens=1402, name='ERNIE-4.0-8K') | +| 参数名称 | 参数类型 | 描述 | 示例值 | +| ------------- | ----------- | -------------------- | --------------------------------------------------------------------------------------- | +| +Answer | string | 智能体应用返回的回答 | | +| +Events | []Event | 事件列表 | | +| +Events[0] | Event | 具体事件内容 | | +| ++Code | string | 错误码 | | +| ++Message | string | 错误具体消息 | | +| ++Status | string | 事件状态 | 状态描述,preparing(准备运行)running(运行中)error(执行错误) done(执行完成) | +| ++EventType | string | 事件类型 | | +| ++ContentType | string | 内容类型 | 可选值包括:code text, image, status,image, function_call, rag, audio、video等 | +| ++Detail | interface{} | 事件输出详情 | 代码解释器、文生图、工具组件、RAG等的详细输出内容 | +| ++Usage | Usage | 模型调用的token用量 | Usage(prompt_tokens=1322, completion_tokens=80, total_tokens=1402, name='ERNIE-4.0-8K') | #### 示例代码 @@ -835,15 +964,15 @@ func main() { #### Run方法入参`AppBuilderClientRunRequest` -| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | -| -------------- | ---------- | -------- | ------------------------------------------------------------ | -------------------- | -| ConversationID | string | 是 | 对话ID,可以通过CreateConversation()获取 | | -| Query | string | 是 | query内容 | "汽车性能参数怎么样" | +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +| -------------- | ---------- | -------- | -------------------------------------------------------------------------------- | -------------------- | +| ConversationID | string | 是 | 对话ID,可以通过CreateConversation()获取 | | +| Query | string | 是 | query内容 | "汽车性能参数怎么样" | | Stream | bool | 是 | 为true时则流式返回,为false时则一次性返回所有内容, 推荐设为true,降低首token时延 | | -| AppID | string | 是 | 应用ID,线上Agent应用的ID | | -| Tools | []Tool | 否 | 一个列表,其中每个字典对应一个工具的配置 | | -| ToolOuptus | []ToolOupt | 否 | 内容为本地的工具执行结果,以自然语言/json dump str描述 | | -| ToolChoice | ToolChoice | 否 | 控制大模型使用组件的方式,仅对自主规划Agent生效。 | | +| AppID | string | 是 | 应用ID,线上Agent应用的ID | | +| Tools | []Tool | 否 | 一个列表,其中每个字典对应一个工具的配置 | | +| ToolOuptus | []ToolOupt | 否 | 内容为本地的工具执行结果,以自然语言/json dump str描述 | | +| ToolChoice | ToolChoice | 否 | 控制大模型使用组件的方式,仅对自主规划Agent生效。 | | `Tool`、`ToolOutput`、`ToolChoice`定义如下: @@ -884,17 +1013,17 @@ type ToolChoiceFunction struct { #### 迭代AgentBuilderIterator -| 参数名称 | 参数类型 | 描述 | 示例值 | -| ------------- | ----------- | -------------------- | ------------------------------------------------------------ | -| +Answer | string | 智能体应用返回的回答 | | -| +Events | []Event | 事件列表 | | -| +Events[0] | Event | 具体事件内容 | | -| ++Code | string | 错误码 | | -| ++Message | string | 错误具体消息 | | -| ++Status | string | 事件状态 | 状态描述,preparing(准备运行)running(运行中)error(执行错误) done(执行完成) | -| ++EventType | string | 事件类型 | | -| ++ContentType | string | 内容类型 | 可选值包括:code text, image, status,image, function_call, rag, audio、video等 | -| ++Detail | interface{} | 事件输出详情 | 代码解释器、文生图、工具组件、RAG等的详细输出内容 | +| 参数名称 | 参数类型 | 描述 | 示例值 | +| ------------- | ----------- | -------------------- | --------------------------------------------------------------------------------------- | +| +Answer | string | 智能体应用返回的回答 | | +| +Events | []Event | 事件列表 | | +| +Events[0] | Event | 具体事件内容 | | +| ++Code | string | 错误码 | | +| ++Message | string | 错误具体消息 | | +| ++Status | string | 事件状态 | 状态描述,preparing(准备运行)running(运行中)error(执行错误) done(执行完成) | +| ++EventType | string | 事件类型 | | +| ++ContentType | string | 内容类型 | 可选值包括:code text, image, status,image, function_call, rag, audio、video等 | +| ++Detail | interface{} | 事件输出详情 | 代码解释器、文生图、工具组件、RAG等的详细输出内容 | | ++Usage | Usage | 模型调用的token用量 | Usage(prompt_tokens=1322, completion_tokens=80, total_tokens=1402, name='ERNIE-4.0-8K') | #### ToolCall示例代码 @@ -1096,4 +1225,3 @@ func main() { } } ``` - diff --git a/python/__init__.py b/python/__init__.py index f1678fac9..c9796bb84 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -174,6 +174,9 @@ def get_default_header(): from appbuilder.utils.logger_util import logger +from appbuilder.core.manifest.manifest_decorator import manifest, manifest_parameter +from appbuilder.core.manifest.models import Manifest + from appbuilder.core.utils import get_model_list from appbuilder.core.console.appbuilder_client.appbuilder_client import AppBuilderClient diff --git a/python/core/manifest/__init__.py b/python/core/manifest/__init__.py new file mode 100644 index 000000000..ba760dad3 --- /dev/null +++ b/python/core/manifest/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2024 Baidu, Inc. 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. diff --git a/python/core/manifest/manifest_decorator.py b/python/core/manifest/manifest_decorator.py new file mode 100644 index 000000000..a1c9e4051 --- /dev/null +++ b/python/core/manifest/manifest_decorator.py @@ -0,0 +1,289 @@ +# Copyright (c) 2024 Baidu, Inc. 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 inspect +from typing import Any, Dict, Optional + +from pydantic.v1 import BaseModel as PydanticBaseModel # noqa: F403 # type: ignore +from pydantic.v1 import create_model + +from appbuilder.core.manifest.manifest_signature import get_signature +from appbuilder.core.manifest.models import Manifest, PropertyModel, ParametersModel + + +# The following two functions are here to allow dynamically updating function description. +# Check tool/builtin/openapi_plugin.py for example. +def function_description(cls, func): + """Get the description of a function.""" + + if hasattr(func, "__ab_manifest__"): + view = func.__ab_manifest__ + return view.description if view else "" + + +def update_function_description(func, description): + """Update function description.""" + func.__dict__["__kernel_function_description__"] = description or "" + func.__dict__["__ab_manifest_description__"] = description or "" + + +def get_function_schema_with_inspect(method): + ( + args, + _, + varkw, + defaults, + kwonlyargs, + kwonlydefaults, + annotations, + ) = inspect.getfullargspec(method) + if len(args) > 0 and (args[0] == "self" or args[0] == "cls"): + args = args[1:] # remove self or cls + + if args or varkw: + if defaults is None: + defaults = () + non_default_args_count = len(args) - len(defaults) + defaults = (...,) * non_default_args_count + defaults + + keyword_only_params = { + param: kwonlydefaults.get(param, Any) for param in kwonlyargs + } + params = { + param: (annotations.get(param, Any), default) + for param, default in zip(args, defaults) + } + return create_model( + "func", + **params, + **keyword_only_params, + __base__=PydanticBaseModel, + __config__=None, + __doc__="", + ).schema()["properties"] + else: # method has no arguments + return None + + +def manifest( + *, + description: Optional[str] = None, + name: Optional[str] = None, +): + """ + Decorator for functions. Use some information to extract meta about function. + Priority as follows: + 1. User provided value via arguments + 2. Function signature + annotations + + Args: + description (str, optional): The functionality of the function, general user guideline. + name (str, optional): The name of the function. + """ + + def decorator(func): + """Decorator for function.""" + + # Get meta from signature + sig_params, sig_returns = get_signature(func=func) + + # Get meta from decorator + dec_params_list = ( + func.__ab_manifest_parameters__ + if hasattr(func, "__ab_manifest_parameters__") + else [] + ) + dec_params = {item.name: item for item in dec_params_list} + + properties = {} + required_fields = [] + for param in sig_params: + dec_param = dec_params.get(param["name"]) + param_type = param.get("type_") or getattr(dec_param, "type", None) + + param_info = { + "name": param["name"], # 参数名称 + "type": param_type, + "description": getattr( + dec_params.get(param["name"]), "description", None + ), # 描述 + "required": param.get( + "required", getattr(dec_params.get(param["name"]), "required", True) + ), # 是否必需 + } + + # 验证类型字段是否有有效值 + if not param_info["type"]: + raise ValueError( + f"参数 '{param['name']}' 缺少类型信息,请在函数签名中指定类型。" + ) + + # 构造 PropertyModel + properties[param["name"]] = PropertyModel( + name=param_info["name"], + type=param_info["type"], + description=param_info["description"], + required=param_info["required"], + ) + + # 记录必需参数 + if param_info["required"]: + required_fields.append(param["name"]) + + # 确定函数的最终名称和描述 + final_name = name or func.__name__ + final_desc = description or func.__doc__ + + if not final_desc: + raise ValueError(f"函数 {final_name} 缺少描述") + + parameters_model = ParametersModel( + type="object", + properties={ + k: v.model_dump(exclude_none=False) for k, v in properties.items() + }, + required=required_fields, + ) + + view = Manifest( + type="function", + function={ + "name": final_name, + "description": final_desc, + "parameters": parameters_model.model_dump(), + }, + ) + + # Attach view to function. + func.__ab_manifest__ = view + + # Compatible to semantic kernel 0.9 + func.__kernel_function__ = True + func.__kernel_function_description__ = final_desc + func.__kernel_function_name__ = final_name + + return func + + return decorator + + +def manifest_parameter( + *, + name: str, + description: str = None, + type: str = None, + required: bool = True, +): + """ + Decorator for function parameters. + + Args: + name -- The name of the parameter + description -- The description of the parameter + default_value -- The default value of the parameter + type -- The type of the parameter, used for function calling + required -- Whether the parameter is required + example -- The example of the parameter + + """ + + def decorator(func): + """Decorator for function parameter.""" + + new_view = PropertyModel( + name=name, + type=type, + description=description, + required=required, + ) + # Update parameter view lists for function_parameter decorator. + # This will be merged into ManifestView if function decorator runs last like: + # @function + # @function_parameter + # @function_parameter + if hasattr(func, "__ab_manifest_parameters__"): + current_views = func.__ab_manifest_parameters__ + else: + current_views = [] + current_views.append(new_view) + func.__ab_manifest_parameters__ = current_views + + # function_parameter runs after function, merge ParameterView in ManifestView + if hasattr(func, "__ab_manifest__"): + # 获取现有的 parameters + parameters_dict = func.__ab_manifest__.function.get("parameters", {}) + parameters_model = ParametersModel(**parameters_dict) + + # 更新 properties + existing_property = parameters_model.properties.get(new_view.name) + if existing_property: + merged_property = PropertyModel.merge(existing_property, new_view) + parameters_model.properties[new_view.name] = merged_property + else: + parameters_model.properties[new_view.name] = new_view + + # 更新 required 字段 + if new_view.required: + if new_view.name not in parameters_model.required: + parameters_model.required.append(new_view.name) + else: + if new_view.name in parameters_model.required: + parameters_model.required.remove(new_view.name) + + # 更新 func.__ab_manifest__.function["parameters"] + func.__ab_manifest__.function["parameters"] = parameters_model.model_dump() + + # Compatible to semantic kernel 0.9 + if hasattr(func, "__kernel_function_parameters__"): + item = { + "name": name, + "description": description, + "type": type, + "required": required, + } + + new_list = _update_list( + item, + func.__kernel_function_parameters__, + lambda item, new_item: item["name"] == new_item["name"], + lambda item, new_item: _merge_dict(item, new_item), + ) + func.__kernel_function_parameters__ = new_list + + return func + + return decorator + + +def _merge_dict(current_dict, new_dict): + result = current_dict.copy() + for k, v in new_dict.items(): + if v: + result[k] = v + return result + + +# Replace the parameter with the same name and keep order. +# Since there are few parameters for each function, keep use list for simplicity +def _update_list(new_item, list, condition, replacer): + new_list = [] + replaced = False + for item in list: + if condition(item, new_item): + replaced = True + merged_item = replacer(item, new_item) + new_list.append(merged_item) + # Missing parameter append to the end. + if not replaced: + new_list.append(new_item) + return new_list diff --git a/python/core/manifest/manifest_signature.py b/python/core/manifest/manifest_signature.py new file mode 100644 index 000000000..39aa78c00 --- /dev/null +++ b/python/core/manifest/manifest_signature.py @@ -0,0 +1,162 @@ +# Copyright (c) 2024 Baidu, Inc. 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 inspect +from inspect import Parameter, Signature +from typing import Any, Dict, Union, Optional, List, get_origin, get_args +import logging + +NoneType = type(None) + +# 映射内置泛型类型到 typing 模块的名称 +TYPE_MAPPING = { + dict: "Dict", + list: "List", + set: "Set", + tuple: "Tuple", + Union: "Union", + Optional: "Optional", + Any: "Any", + int: "int", + str: "str", + float: "float", + bool: "bool", + # 根据需要添加更多映射 +} + + +def get_signature(func): + """ + 获取函数的签名视图。 + + Args: + func (function): 目标函数。 + + Returns: + tuple: 包含两个元素的元组,第一个元素为函数参数的解析结果列表,第二个元素为函数返回值的解析结果字典。 + + """ + + signature = inspect.signature(func) + _parameters = [ + _parse_parameter(param) + for param in signature.parameters.values() + if param.name != "self" + ] + signature_returns = ( + _parse_annotation(signature.return_annotation) + if signature.return_annotation != Signature.empty + else {} + ) + return _parameters, signature_returns + + +def _parse_parameter(param: Parameter) -> Dict[str, Any]: + ret = {} + if param.annotation != Parameter.empty: + ret = _parse_annotation(param.annotation) + else: + ret["type_"] = None + ret["required"] = True # 默认为 True + + ret["name"] = param.name + + if param.default != Parameter.empty: + ret["default_value"] = param.default + ret["required"] = False + else: + ret["required"] = ret.get("required", True) + + return ret + + +def _parse_annotation(annotation: Any) -> Dict[str, Any]: + # The keys of this dict are compatible with semantic-kernel, do not change them + if annotation == Signature.empty: + return {"type_": None, "required": True} + if isinstance(annotation, str): + return {"type_": annotation, "required": True} + ret = _parse_internal_annotation(annotation, True) + if hasattr(annotation, "__metadata__") and annotation.__metadata__: + ret["description"] = annotation.__metadata__[0] + return ret + + +def _parse_internal_annotation(annotation: Any, required: bool) -> Dict[str, Any]: + if hasattr(annotation, "__forward_arg__"): + return {"type_": annotation.__forward_arg__, "required": required} + + # 获取 origin 和 args + origin = get_origin(annotation) + args = get_args(annotation) + + # 确定 parent_type + if origin is not None: + parent_type = TYPE_MAPPING.get( + origin, origin.__name__ if hasattr(origin, "__name__") else str(origin) + ) + else: + parent_type = TYPE_MAPPING.get( + annotation, + annotation.__name__ if hasattr(annotation, "__name__") else str(annotation), + ) + + if parent_type == "Optional": + required = False + + if args: + results = [_parse_internal_annotation(arg, required) for arg in args] + type_objects = [ + result["type_object"] + for result in results + if "type_object" in result and result["type_object"] is not NoneType + ] + str_results = [result["type_"] for result in results] + + if "NoneType" in str_results: + str_results.remove("NoneType") + required = False + + if parent_type == "Union": + if len(str_results) == 1: + type_ = f"Optional[{str_results[0]}]" + else: + type_ = f"Union[{', '.join(str_results)}]" + else: + type_ = f"{parent_type}[{', '.join(str_results)}]" + else: + if parent_type == "Union": + # 所有选项都为非必需 + required = not (all(not result["required"] for result in results)) + type_ = f"{parent_type}[{', '.join(str_results)}]" + + ret = {"type_": type_, "required": required} + if type_objects and len(type_objects) == 1: + ret["type_object"] = type_objects[0] + logging.debug( + f"Parsed annotation: {annotation}, type_: {type_}, required: {required}" + ) + return ret + + type_ = TYPE_MAPPING.get( + annotation, + annotation.__name__ if hasattr(annotation, "__name__") else str(annotation), + ) + logging.debug( + f"Parsed annotation: {annotation}, type_: {type_}, required: {required}" + ) + return { + "type_": type_, + "type_object": annotation, + "required": required, + } diff --git a/python/core/manifest/models.py b/python/core/manifest/models.py new file mode 100644 index 000000000..a5c6e02aa --- /dev/null +++ b/python/core/manifest/models.py @@ -0,0 +1,138 @@ +# Copyright (c) 2023 Baidu, Inc. 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 pydantic import BaseModel, Field +from typing import Dict, List, Literal, Any, Optional + +from appbuilder.core.manifest.validate import ( + validate_function_param_name, + validate_function_name, +) +from appbuilder.core.manifest.manifest_signature import get_signature + + +class PropertyModel(BaseModel): + """参数属性模型,用于描述函数参数的类型和元数据。 + + Attributes: + type (Optional[str]): 参数的类型。 + description (Optional[str]): 参数的描述信息。 + """ + + name: str + type: Optional[str] + description: Optional[str] + required: bool = True + + @classmethod + def merge(cls, a: "PropertyModel", b: "PropertyModel") -> "PropertyModel": + """Merge two parameter views.""" + return cls( + name=a.name, + description=(b.description or a.description), + type=(b.type or a.type), + required=(b.required if b.required is not None else a.required), + ) + + +class ParametersModel(BaseModel): + """函数参数模型,用于定义函数的参数结构。 + + Attributes: + type (Literal["object"]): 表示参数集合的类型,固定为 "object"。 + properties (Dict[str, PropertyModel]): 参数的具体属性映射,其中键是参数名,值是对应的属性模型。 + required (List[str]): 必须提供的参数列表。 + """ + + type: Literal["object"] + properties: Dict[str, PropertyModel] + required: List[str] + + +class Manifest(BaseModel): + """函数模型,用于描述函数的元信息。 + + Attributes: + type (Literal["function"]): 表示模型的类型,固定为 "function"。 + function (Dict[str, Any]): 函数的详细信息,包括名称、描述、参数、返回值等。 + """ + + type: Literal["function"] + function: Dict[str, Any] + + @classmethod + def from_function(cls, func) -> "Manifest": + """ + 利用 manifest_signature.py 提供的 get_signature 方法解析函数的签名和参数信息, + 并生成一个 Manifest 实例。 + + Args: + func: 要转换的函数。 + + Returns: + Manifest: 包含函数元信息的模型。 + """ + if func.__doc__ is None: + raise ValueError(f"函数 {func.__name__} 缺少文档字符串") + + # 使用 manifest_signature 提取函数签名信息 + sig_params, sig_returns = get_signature(func) + + # 构造参数模型 + properties = {} + required = [] + + for param in sig_params: + param_info = { + "name": param["name"], # 参数名称 + "type": param.get("type_", None), # 类型 + "description": param.get("description", None), # 描述 + "required": param.get("required", False), # 是否必需 + } + + # 验证类型字段是否有有效值 + if not param_info["type"]: + raise ValueError( + f"参数 '{param['name']}' 缺少类型信息,请在函数签名中指定类型。" + ) + + # 构造 PropertyModel + properties[param["name"]] = PropertyModel( + name=param_info["name"], + type=param_info["type"], + description=param_info["description"], + required=param_info["required"], + ) + + # 记录必需参数 + if param_info["required"]: + required.append(param["name"]) + + # 构造 ParametersModel + parameters_model = ParametersModel( + type="object", + properties=properties, + required=required, + ) + + # 构造 Manifest 对象 + function_manifest = cls( + type="function", + function={ + "name": func.__name__, + "description": func.__doc__, # 去掉多余的空格 + "parameters": parameters_model.model_dump(), + }, + ) + + return function_manifest diff --git a/python/tests/test_appbuilder_client_toolcall.py b/python/tests/test_appbuilder_client_toolcall.py index af3a407eb..e7e8c4887 100644 --- a/python/tests/test_appbuilder_client_toolcall.py +++ b/python/tests/test_appbuilder_client_toolcall.py @@ -1,11 +1,9 @@ import unittest import appbuilder -import requests -import tempfile import os -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL","") +# @unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL","") class TestAgentRuntime(unittest.TestCase): def setUp(self): """ @@ -23,16 +21,16 @@ def test_appbuilder_client_tool_call(self): # 如果app_id为空,则跳过单测执行, 避免单测因配置无效而失败 """ 如果app_id为空,则跳过单测执行, 避免单测因配置无效而失败 - + Args: self (unittest.TestCase): unittest的TestCase对象 - + Raises: None: 如果app_id不为空,则不会引发任何异常 unittest.SkipTest (optional): 如果app_id为空,则跳过单测执行 """ - builder = appbuilder.AppBuilderClient(self.app_id) - conversation_id = builder.create_conversation() + client = appbuilder.AppBuilderClient(self.app_id) + conversation_id = client.create_conversation() tools = [ { "type": "function", @@ -46,7 +44,10 @@ def test_appbuilder_client_tool_call(self): "type": "string", "description": "城市名,举例:北京", }, - "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}, + "unit": { + "type": "string", + "enum": ["celsius", "fahrenheit"], + }, }, "required": ["location", "unit"], }, @@ -54,29 +55,157 @@ def test_appbuilder_client_tool_call(self): } ] - msg = builder.run( - conversation_id=conversation_id, - query="今天北京天气怎么样?", - tools=tools) + msg = client.run( + conversation_id=conversation_id, query="今天北京天气怎么样?", tools=tools + ) print(msg.model_dump_json(indent=4)) event = msg.content.events[-1] assert event.status == "interrupt" assert event.event_type == "Interrupt" - msg_2 = builder.run( + msg_2 = client.run( conversation_id=conversation_id, tool_outputs=[ - { - "tool_call_id": event.tool_calls[-1].id, - "output": "北京今天35度" - } - ] + {"tool_call_id": event.tool_calls[-1].id, "output": "北京今天35度"} + ], + ) + print(msg_2.model_dump_json(indent=4)) + + """测试functions2model功能""" + + # 定义本地函数 + def get_current_weather(location: str, unit: str) -> str: + """获取指定中国城市的当前天气信息。 + + 仅支持中国城市的天气查询。参数 `location` 为中国城市名称,其他国家城市不支持天气查询。 + + Args: + location (str): 城市名,例如:"北京"。 + unit (int): 温度单位,支持 "celsius" 或 "fahrenheit"。 + + Returns: + str: 天气情况描述 + """ + return "北京今天25度" + + # 定义函数列表 + functions = [get_current_weather] + function_map = {f.__name__: f for f in functions} + # 调用大模型 + msg = client.run( + conversation_id=conversation_id, + query="今天北京的天气怎么样?", + tools=[ + appbuilder.Manifest.from_function(f).model_dump() for f in functions + ], + ) + print(msg.model_dump_json(indent=4)) + # 获取最后的事件和工具调用信息 + event = msg.content.events[-1] + tool_call = event.tool_calls[-1] + + # 获取函数名称和参数 + name = tool_call.function.name + args = tool_call.function.arguments + + # 将函数名称映射到具体的函数并执行 + raw_result = function_map[name](**args) + + # 传递工具的输出 + msg_2 = client.run( + conversation_id=conversation_id, + tool_outputs=[{"tool_call_id": tool_call.id, "output": str(raw_result)}], ) print(msg_2.model_dump_json(indent=4)) - - + """测试装饰器功能功能""" + + @appbuilder.manifest( + description="获取指定中国城市的当前天气信息。仅支持中国城市的天气查询。参数 `location` 为中国城市名称,其他国家城市不支持天气查询。" + ) + @appbuilder.manifest_parameter( + name="location", description="城市名,例如:北京。" + ) + @appbuilder.manifest_parameter( + name="unit", description="温度单位,支持 'celsius' 或 'fahrenheit'" + ) + # 定义示例函数 + def get_current_weather(location: str, unit: str) -> str: + return "北京今天25度" + + # 定义函数列表 + functions = [get_current_weather] + function_map = {f.__name__: f for f in functions} + # 调用大模型 + msg = client.run( + conversation_id=conversation_id, + query="今天北京的天气怎么样?", + tools=[get_current_weather.__ab_manifest__.model_dump()], + ) + print(msg.model_dump_json(indent=4)) + # 获取最后的事件和工具调用信息 + event = msg.content.events[-1] + tool_call = event.tool_calls[-1] + + # 获取函数名称和参数 + name = tool_call.function.name + args = tool_call.function.arguments + + # 将函数名称映射到具体的函数并执行 + raw_result = function_map[name](**args) + + # 传递工具的输出 + msg_2 = client.run( + conversation_id=conversation_id, + tool_outputs=[{"tool_call_id": tool_call.id, "output": str(raw_result)}], + ) + print(msg_2.model_dump_json(indent=4)) + + try: + + @appbuilder.manifest() + @appbuilder.manifest_parameter( + name="location", description="城市名,例如:北京。" + ) + @appbuilder.manifest_parameter( + name="unit", description="温度单位,支持 'celsius' 或 'fahrenheit'" + ) + # 定义示例函数 + def get_current_weather(location: str, unit: str) -> str: + return "北京今天25度" + + except ValueError as e: + # 使用 assert 检查是否抛出 ValueError,且包含 "缺少描述" 的信息 + assert "缺少描述" in str( + e + ), f"Expected '缺少描述' in error message, but got: {str(e)}" + else: + # 如果未抛出异常,测试应失败 + assert False, "Expected ValueError but no exception was raised" + + try: + + @appbuilder.manifest() + @appbuilder.manifest_parameter( + name="location", description="城市名,例如:北京。" + ) + @appbuilder.manifest_parameter( + name="unit", description="温度单位,支持 'celsius' 或 'fahrenheit'" + ) + # 定义示例函数 + def get_current_weather(location: str, unit) -> str: + return "北京今天25度" + + except ValueError as e: + # 使用 assert 检查是否抛出 ValueError,且包含 "缺少描述" 的信息 + assert "缺少类型信息" in str( + e + ), f"Expected '缺少类型信息' in error message, but got: {str(e)}" + else: + # 如果未抛出异常,测试应失败 + assert False, "Expected ValueError but no exception was raised" + -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/python/tests/test_manifest.py b/python/tests/test_manifest.py new file mode 100644 index 000000000..3c2a2f6b7 --- /dev/null +++ b/python/tests/test_manifest.py @@ -0,0 +1,386 @@ +import unittest +import os +from typing import Any, Dict, List, Optional +import appbuilder +from appbuilder import Manifest +from appbuilder import manifest, manifest_parameter + + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") +class TestManifest(unittest.TestCase): + def setUp(self): + """ + 设置环境变量。 + + Args: + 无参数,默认值为空。 + + Returns: + 无返回值,方法中执行了环境变量的赋值操作。 + """ + os.environ["APPBUILDER_TOKEN"] = ( + "bce-v3/ALTAK-DKaql4wY9ojwp2uMe8IEj/7ae1190aff0684153de365381d9b06beab3064c5" + ) + self.app_id = "7cc4c21f-0e25-4a76-baf7-01a2b923a1a7" + + def test_google_style(self): + # Generated by vscode plugin + # https://marketplace.visualstudio.com/items?itemName=njpwerner.autodocstring + def google_style( + name: str, + val: str = None, + val_obj: Optional[Any] = None, + val_list: List[str] = None, + data: Dict[str, int] = None, + ) -> str: + """Google style docstring. + + Args: + name (str): Name of object. + val (str, optional): Value of obj. Defaults to None. + val_obj (Optional[Any], optional): Real object reference. Defaults to None. + val_list (List[str], optional): List of items with object. Defaults to None. + data (Dict[str, int], optional): Data along with object. Defaults to None. + + Returns: + str: Styled string. + """ + return "" + + function_manifest = appbuilder.Manifest.from_function(google_style) + + # 断言顶层的结构 + assert function_manifest.type == "function", "Type does not match 'function'" + assert ( + function_manifest.function["name"] == "google_style" + ), "Function name does not match 'google_style'" + assert ( + function_manifest.function["description"] + == """Google style docstring. + + Args: + name (str): Name of object. + val (str, optional): Value of obj. Defaults to None. + val_obj (Optional[Any], optional): Real object reference. Defaults to None. + val_list (List[str], optional): List of items with object. Defaults to None. + data (Dict[str, int], optional): Data along with object. Defaults to None. + + Returns: + str: Styled string. + """ + ), "Description does not match" + + # 断言参数结构 + parameters = function_manifest.function["parameters"] + assert parameters["type"] == "object", "Parameters type does not match 'object'" + assert "properties" in parameters, "Properties not found in parameters" + + # 断言各个参数的类型和描述 + properties = parameters["properties"] + + # name 参数 + assert "name" in properties, "'name' parameter missing" + # 添加类型检查时的调试信息 + try: + assert ( + properties["name"]["type"] == "str" + ), f"'name' type does not match 'str'. Actual type: {properties['name']['type']}" + except AssertionError as e: + print(f"Debug Info: Actual 'name' type is {properties['name']['type']}") + raise e # 重新抛出异常 + + # val 参数 + assert "val" in properties, "'val' parameter missing" + assert properties["val"]["type"] == "str", "'val' type does not match 'str'" + + # val_obj 参数 + assert "val_obj" in properties, "'val_obj' parameter missing" + assert ( + properties["val_obj"]["type"] == "Optional[Any]" + ), "'val_obj' type does not match 'object'" + + # val_list 参数 + assert "val_list" in properties, "'val_list' parameter missing" + assert ( + properties["val_list"]["type"] == "List[str]" + ), "'val_list' type does not match 'array'" + + # data 参数 + assert "data" in properties, "'data' parameter missing" + assert ( + properties["data"]["type"] == "Dict[str, int]" + ), "'data' type does not match 'object'" + + # 断言必需参数 + assert "required" in parameters, "'required' field missing in parameters" + assert parameters["required"] == ["name"], "'required' does not match ['name']" + + def test_google_style_bad_args_return_dict(self): + def func( + bad_param: str, + bad_generic_param: List[str], + bad_format: int, + val: str = None, + ) -> Dict[str, str]: + """Google style docstring. + + Args: + bad param (str): Bad parameter, name contains whitespace. + bad_generic_param (List): Bad generic parameter, use <> instead of [] + bad_format (int) Bad arg doc format, lost :. + val (str , optional): Value of obj. Defaults to None. + + Returns: + Dict[str, str]: Returns a dict. + """ + return "" + + function_manifest = appbuilder.Manifest.from_function(func) + # 断言顶层的结构 + assert function_manifest.type == "function", "Type does not match 'function'" + assert ( + function_manifest.function["name"] == "func" + ), "Function name does not match 'func'" + assert ( + function_manifest.function["description"] + == """Google style docstring. + + Args: + bad param (str): Bad parameter, name contains whitespace. + bad_generic_param (List): Bad generic parameter, use <> instead of [] + bad_format (int) Bad arg doc format, lost :. + val (str , optional): Value of obj. Defaults to None. + + Returns: + Dict[str, str]: Returns a dict. + """ + ), "Description does not match" + + # 断言参数结构 + parameters = function_manifest.function["parameters"] + assert parameters["type"] == "object", "Parameters type does not match 'object'" + assert "properties" in parameters, "Properties not found in parameters" + + # 断言各个参数的类型和描述 + properties = parameters["properties"] + + # bad_param 参数 + assert "bad_param" in properties, "'bad_param' parameter missing" + assert ( + properties["bad_param"]["type"] == "str" + ), "'bad_param' type does not match 'str'" + + # bad_format 参数 + assert "bad_format" in properties, "'bad_format' parameter missing" + assert ( + properties["bad_format"]["type"] == "int" + ), "'bad_format' type does not match 'int'" + + # val 参数 + assert "val" in properties, "'val' parameter missing" + assert properties["val"]["type"] == "str", "'val' type does not match 'str'" + + # 断言必需参数 + assert "required" in parameters, "'required' field missing in parameters" + assert parameters["required"] == [ + "bad_param", + "bad_generic_param", + "bad_format", + ], "'required' does not match expected required parameters" + + # 断言没有多余参数 + assert len(properties) == 4, "Unexpected number of parameters in properties" + + def test_google_style_no_return(self): + def func( + name: str, + ): + """Google style docstring. + + Args: + name (str): Name of object. + + """ + return "" + + function_manifest = appbuilder.Manifest.from_function(func) + # 断言顶层的结构 + assert function_manifest.type == "function", "Type does not match 'function'" + assert ( + function_manifest.function["name"] == "func" + ), "Function name does not match 'func'" + assert ( + function_manifest.function["description"] + == """Google style docstring. + + Args: + name (str): Name of object. + + """ + ), "Description does not match" + + # 断言参数结构 + parameters = function_manifest.function["parameters"] + assert parameters["type"] == "object", "Parameters type does not match 'object'" + assert "properties" in parameters, "Properties not found in parameters" + + # 断言参数类型和描述 + properties = parameters["properties"] + + # name 参数 + assert "name" in properties, "'name' parameter missing" + assert properties["name"]["type"] == "str", "'name' type does not match 'str'" + + # 断言必需参数 + assert "required" in parameters, "'required' field missing in parameters" + assert parameters["required"] == ["name"], "'required' does not match ['name']" + + # 断言没有["parameters"][1]的参数了 + assert not ( + "parameters" in function_manifest.function + and len(function_manifest.function["parameters"]) == 1 + ) + + def test_google_style_no_args_no_return(self): + def func( + name: str, + /, + *args, + val: str = None, + val_obj: Optional[Any] = None, + data: Dict[str, int] = None, + **kwargs, + ) -> str: + """Google style docstring.""" + return "" + + # 断言这里会抛出参数类型缺失导致的ValueError异常 + try: + function_manifest = appbuilder.Manifest.from_function(func) + except ValueError as e: + # 使用 assert 检查是否抛出 ValueError,且包含 "缺少描述" 的信息 + assert "缺少类型信息" in str( + e + ), f"Expected '缺少类型信息' in error message, but got: {str(e)}" + else: + # 如果未抛出异常,测试应失败 + assert False, "Expected ValueError but no exception was raised" + + def test_no_doc(self): + def func( + name: str, + /, + *args, + val: str = None, + val_obj: Optional[Any] = None, + data: Dict[str, int] = None, + **kwargs, + ) -> str: + return "" + + # 断言这里会抛出缺少文档字符串的 ValueError 异常 + try: + function_manifest = appbuilder.Manifest.from_function(func) + except ValueError as e: + assert ( + str(e) == "函数 func 缺少文档字符串" + ), "未抛出预期的 ValueError 或信息不匹配" + + def test_decorator_google_style_basic(self): + @manifest(description="Function with required parameter.") + def func( + name: str, + ) -> str: + """Function with required parameter. + + Args: + name (str): Name of object. + + Returns: + str: Styled string. + """ + return "" + + # 获取装饰器生成的 Manifest + function_manifest = func.__ab_manifest__ + + # 断言顶层的结构 + assert function_manifest.type == "function", "Type does not match 'function'" + assert ( + function_manifest.function["name"] == "func" + ), "Function name does not match 'func'" + assert ( + function_manifest.function["description"] + == "Function with required parameter." + ), "Description does not match" + + # 断言参数结构 + parameters = function_manifest.function["parameters"] + assert parameters["type"] == "object", "Parameters type does not match 'object'" + assert "properties" in parameters, "Properties not found in parameters" + + # 断言各个参数的类型和描述 + properties = parameters["properties"] + + # name 参数 + assert "name" in properties, "'name' parameter missing" + assert properties["name"]["type"] == "str", "'name' type does not match 'str'" + assert ( + properties["name"]["description"] == None + ), "'name' description does not match" + + # 断言必需参数 + assert "required" in parameters, "'required' field missing in parameters" + assert parameters["required"] == ["name"], "'required' does not match ['name']" + + def test_missing_type(self): + """测试参数缺少类型信息是否抛出 ValueError 异常。""" + try: + + @appbuilder.manifest() + @appbuilder.manifest_parameter( + name="location", description="城市名,例如:北京。" + ) + @appbuilder.manifest_parameter( + name="unit", description="温度单位,支持 'celsius' 或 'fahrenheit'" + ) + # 定义示例函数 + def get_current_weather(location: str, unit) -> str: + return "北京今天25度" + + except ValueError as e: + # 使用 assert 检查是否抛出 ValueError,且包含 "缺少描述" 的信息 + assert "缺少类型信息" in str( + e + ), f"Expected '缺少类型信息' in error message, but got: {str(e)}" + else: + # 如果未抛出异常,测试应失败 + assert False, "Expected ValueError but no exception was raised" + + def test_missing_description(self): + """测试函数缺少描述是否抛出 ValueError 异常。""" + try: + + @appbuilder.manifest() + @appbuilder.manifest_parameter( + name="location", description="城市名,例如:北京。" + ) + @appbuilder.manifest_parameter( + name="unit", description="温度单位,支持 'celsius' 或 'fahrenheit'" + ) + # 定义示例函数 + def get_current_weather(location: str, unit: str) -> str: + return "北京今天25度" + + except ValueError as e: + # 使用 assert 检查是否抛出 ValueError,且包含 "缺少描述" 的信息 + assert "缺少描述" in str( + e + ), f"Expected '缺少描述' in error message, but got: {str(e)}" + else: + # 如果未抛出异常,测试应失败 + assert False, "Expected ValueError but no exception was raised" + + +if __name__ == "__main__": + unittest.main() diff --git a/python/tests/test_manifest_decorator.py b/python/tests/test_manifest_decorator.py new file mode 100644 index 000000000..35a1e6fcd --- /dev/null +++ b/python/tests/test_manifest_decorator.py @@ -0,0 +1,197 @@ +# Copyright (c) 2024 Baidu, Inc. 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. +import os +import unittest +from appbuilder import Manifest, manifest, manifest_parameter + + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") +class TestManifestDecorator(unittest.TestCase): + def test_disable_docstring(self): + @manifest(description="anotated function") + @manifest_parameter(name="param", description="a parameter", type="str") + def func(param: str) -> str: + return param + + function_manifest = func.__ab_manifest__ + + # 断言顶层的结构 + assert function_manifest.type == "function", "Type does not match 'function'" + assert ( + function_manifest.function["name"] == "func" + ), "Function name does not match 'func'" + assert ( + function_manifest.function["description"] == "anotated function" + ), "Description does not match" + + # 断言参数结构 + parameters = function_manifest.function["parameters"] + assert parameters["type"] == "object", "Parameters type does not match 'object'" + assert "properties" in parameters, "Properties not found in parameters" + + # 断言具体参数 + properties = parameters["properties"] + assert "param" in properties, "'param' parameter missing" + assert properties["param"]["type"] == "str", "'param' type does not match 'str'" + assert ( + properties["param"]["description"] == "a parameter" + ), "'param' description does not match" + + # 断言必需参数 + assert "required" in parameters, "'required' field missing in parameters" + assert parameters["required"] == [ + "param" + ], "'required' does not match ['param']" + + def test_combine(self): + @manifest() + @manifest_parameter(name="param") + def func(param: str = "[]") -> int: + """An example function. + + Args: + param (str): A list of numbers. + + Returns: + int: The sum of parameter. + """ + return param + + # 获取装饰器生成的 Manifest + function_manifest = func.__ab_manifest__ + + # 断言顶层结构 + assert function_manifest.type == "function", "Type does not match 'function'" + assert ( + function_manifest.function["name"] == "func" + ), "Function name does not match 'func'" + assert ( + function_manifest.function["description"] + == """An example function. + + Args: + param (str): A list of numbers. + + Returns: + int: The sum of parameter. + """ + ), "Description does not match" + + # 断言参数结构 + parameters = function_manifest.function["parameters"] + assert parameters["type"] == "object", "Parameters type does not match 'object'" + assert "properties" in parameters, "Properties not found in parameters" + + # 断言具体参数 + properties = parameters["properties"] + assert "param" in properties, "'param' parameter missing" + assert properties["param"]["type"] == "str", "'param' type does not match 'str'" + assert "description" in properties["param"], "'param' description missing" + assert ( + properties["param"]["description"] == None + ), "'param' description does not match" + + # 断言必需参数 + assert "required" in parameters, "'required' field missing in parameters" + assert ( + "param" not in parameters["required"] + ), "'param' should not be required as it has a default value" + + def test_reversed_decorators(self): + @manifest_parameter(name="param", description="DECORATOR A list of numbers.") + @manifest() + def func(param: str = "[]") -> int: + """An example function. + + Args: + param (str): A list of numbers. + + Returns: + int: The sum of parameter. + """ + return param + + # 获取装饰器生成的 Manifest + function_manifest = func.__ab_manifest__ + + # 断言顶层结构 + assert function_manifest.type == "function", "Type does not match 'function'" + assert ( + function_manifest.function["name"] == "func" + ), "Function name does not match 'func'" + assert ( + function_manifest.function["description"] + == """An example function. + + Args: + param (str): A list of numbers. + + Returns: + int: The sum of parameter. + """ + ), "Description does not match" + + # 断言参数结构 + parameters = function_manifest.function["parameters"] + assert parameters["type"] == "object", "Parameters type does not match 'object'" + assert "properties" in parameters, "Properties not found in parameters" + + # 断言具体参数 + properties = parameters["properties"] + assert "param" in properties, "'param' parameter missing" + assert properties["param"]["type"] == "str", "'param' type does not match 'str'" + + # 断言必需参数 + assert "required" in parameters, "'required' field missing in parameters" + assert ( + "param" in parameters["required"] + ), "'param' should not be required as it has a default value" + + def test_only_function_decorator(self): + @manifest() + def func(param: str = "[]") -> int: + " " + return param + + view = func.__ab_manifest__ + + # 获取装饰器生成的 Manifest + function_manifest = func.__ab_manifest__ + + # 断言顶层结构 + assert function_manifest.type == "function", "Type does not match 'function'" + assert ( + function_manifest.function["name"] == "func" + ), "Function name does not match 'func'" + assert ( + function_manifest.function["description"] == " " + ), "Description should be None when not explicitly provided" + + # 断言参数结构 + parameters = function_manifest.function["parameters"] + assert parameters["type"] == "object", "Parameters type does not match 'object'" + assert "properties" in parameters, "Properties not found in parameters" + + # 断言具体参数 + properties = parameters["properties"] + assert "param" in properties, "'param' parameter missing" + assert properties["param"]["type"] == "str", "'param' type does not match 'str'" + + # 检查是否为必需参数 + assert "required" in parameters, "'required' field missing in parameters" + assert ( + "param" not in parameters["required"] + ), "'param' should not be required as it has a default value" + + +if __name__ == "__main__": + unittest.main() diff --git a/python/tests/test_manifest_signature.py b/python/tests/test_manifest_signature.py new file mode 100644 index 000000000..ac39d3311 --- /dev/null +++ b/python/tests/test_manifest_signature.py @@ -0,0 +1,629 @@ +# Copyright (c) 2024 Baidu, Inc. 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 unittest +from typing import Any, Dict, List, Optional, Union +from appbuilder import Manifest, manifest + + +# @unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") +class TestManifestSignature(unittest.TestCase): + def test_is_normal(self): + @manifest() + def func(): + return 1 + + view = func.__ab_manifest__ + + assert isinstance(view, Manifest) + + def test_is_async(self): + @manifest(description="test") + async def func(): + import asyncio + + await asyncio.sleep(0) + + view = func.__ab_manifest__ + + assert isinstance(view, Manifest) + + def test_is_stream(self): + @manifest() + def func(): + for i in range(2): + yield i + + view = func.__ab_manifest__ + + assert isinstance(view, Manifest) + + def test_is_async_and_stream(self): + @manifest() + async def func(): + import asyncio + + for i in range(1): + await asyncio.sleep(0) + yield i + + view = func.__ab_manifest__ + + assert isinstance(view, Manifest) + + def test_decorator_google_style_function_description_no_args(self): + @manifest() + def func(): + """A function to test function description. + + Args: + name (str): Name of object. + + Returns: + str: Styled string. + """ + return "" + + view = func.__ab_manifest__ + + assert isinstance(view, Manifest) + assert view.function["name"] == "func" + assert ( + view.function["description"] + == """A function to test function description. + + Args: + name (str): Name of object. + + Returns: + str: Styled string. + """ + ) + + # Assert that parameters are an empty dictionary since there are no function arguments + assert view.function["parameters"]["properties"] == {} + assert view.function["parameters"]["required"] == [] + + def test_decorator_google_style_basic(self): + @manifest() + def func( + name: str, + ) -> str: + """Function with required parameter. + + Args: + name (str): Name of object. + + Returns: + str: Styled string. + """ + return "" + + view = func.__ab_manifest__ + + # 检查生成的 Manifest 对象 + assert isinstance(view, Manifest) + + # 检查函数元信息 + assert view.function["name"] == "func", "Function name does not match 'func'" + assert ( + view.function["description"] + == """Function with required parameter. + + Args: + name (str): Name of object. + + Returns: + str: Styled string. + """ + ) + + # 检查参数结构 + parameters = view.function["parameters"] + assert parameters["type"] == "object", "Parameters type does not match 'object'" + assert "properties" in parameters, "Properties not found in parameters" + + # 检查具体参数 'name' + properties = parameters["properties"] + assert "name" in properties, "'name' parameter missing" + name_property = properties["name"] + assert name_property["type"] == "str", "'name' type does not match 'str'" + assert ( + name_property["description"] is None + ), "'name' description does not match None" + assert name_property["required"] is True, "'name' required does not match True" + + def test_decorator_google_style_list(self): + @manifest() + def func( + val: List[str], + ) -> str: + """Function with a List parameter. + + Args: + val (List[str]): A list of strings. + + Returns: + str: A result string. + """ + return "" + + view = func.__ab_manifest__ + + # 检查生成的 Manifest 对象 + assert isinstance(view, Manifest) + + # 检查参数的总体结构 + parameters = view.function["parameters"] + assert parameters["type"] == "object", "Parameters type does not match 'object'" + assert "properties" in parameters, "Properties not found in parameters" + + # 检查具体参数 'val' + properties = parameters["properties"] + assert "val" in properties, "'val' parameter missing" + val_property = properties["val"] + + # 验证 'val' 参数的各项属性 + assert ( + val_property["type"] == "List[str]" + ), "'val' type does not match 'List[str]'" + assert val_property["required"] is True, "'val' required does not match True" + + def test_decorator_google_style_list_of_dicts(self): + @manifest() + def func( + val: List[Dict[str, List[str]]], + ) -> str: + """Function with a complex nested parameter. + + Args: + val (List[Dict[str, List[str]]]): A list of dictionaries mapping strings to lists of strings. + + Returns: + str: A result string. + """ + return "" + + view = func.__ab_manifest__ + + # 检查生成的 Manifest 对象 + assert isinstance(view, Manifest), "view is not an instance of Manifest" + + # 检查参数的总体结构 + parameters = view.function["parameters"] + assert parameters["type"] == "object", "Parameters type does not match 'object'" + assert "properties" in parameters, "Properties not found in parameters" + + # 检查具体参数 'val' + properties = parameters["properties"] + assert "val" in properties, "'val' parameter missing" + val_property = properties["val"] + + # 验证 'val' 参数的各项属性 + assert ( + val_property["type"] == "List[Dict[str, List[str]]]" + ), "'val' type does not match 'List[Dict[str, List[str]]]'" + assert val_property["required"] is True, "'val' required does not match True" + + def test_decorator_google_style_dict(self): + @manifest() + def func( + val: Dict[str, Any], + ) -> str: + """Function with a dictionary parameter. + + Args: + val (Dict[str, Any]): A dictionary with string keys and any values. + + Returns: + str: A result string. + """ + return "" + + view = func.__ab_manifest__ + + # 检查生成的 Manifest 对象 + assert isinstance(view, Manifest), "view is not an instance of Manifest" + + # 检查参数的总体结构 + parameters = view.function["parameters"] + assert parameters["type"] == "object", "Parameters type does not match 'object'" + assert "properties" in parameters, "Properties not found in parameters" + + # 检查具体参数 'val' + properties = parameters["properties"] + assert "val" in properties, "'val' parameter missing" + val_property = properties["val"] + + # 验证 'val' 参数的各项属性 + assert ( + val_property["type"] == "Dict[str, Any]" + ), "'val' type does not match 'Dict[str, Any]'" + assert val_property["required"] is True, "'val' required does not match True" + + def test_decorator_google_style_dict_of_lists(self): + @manifest() + def func( + val: Dict[str, List[Dict[str, List[str]]]], + ) -> str: + """Function with a complex nested parameter. + + Args: + val (Dict[str, List[Dict[str, List[str]]]]): A dictionary where keys are strings and values are lists of dictionaries. + + Returns: + str: A result string. + """ + return "" + + view = func.__ab_manifest__ + + # 检查生成的 Manifest 对象 + assert isinstance(view, Manifest), "view is not an instance of Manifest" + + # 检查参数的总体结构 + parameters = view.function["parameters"] + assert parameters["type"] == "object", "Parameters type does not match 'object'" + assert "properties" in parameters, "Properties not found in parameters" + + # 检查具体参数 'val' + properties = parameters["properties"] + assert "val" in properties, "'val' parameter missing" + val_property = properties["val"] + + # 验证 'val' 参数的各项属性 + assert ( + val_property["type"] == "Dict[str, List[Dict[str, List[str]]]]" + ), "'val' type does not match 'Dict[str, List[Dict[str, List[str]]]]'" + assert val_property["required"] is True, "'val' required does not match True" + + def test_decorator_google_style_union_simple(self): + @manifest() + def func( + val: Union[str, int, Any], + ) -> str: + """Function with a Union parameter. + + Args: + val (Union[str, int, Any]): A parameter that can accept multiple types. + + Returns: + str: A result string. + """ + return "" + + view = func.__ab_manifest__ + + # 检查生成的 Manifest 对象 + assert isinstance(view, Manifest), "view is not an instance of Manifest" + + # 检查参数的总体结构 + parameters = view.function["parameters"] + assert parameters["type"] == "object", "Parameters type does not match 'object'" + assert "properties" in parameters, "Properties not found in parameters" + + # 检查具体参数 'val' + properties = parameters["properties"] + assert "val" in properties, "'val' parameter missing" + val_property = properties["val"] + + # 验证 'val' 参数的各项属性 + assert ( + val_property["type"] == "Union[str, int, Any]" + ), "'val' type does not match 'Union[str, int, Any]'" + assert val_property["required"] is True, "'val' required does not match True" + + def test_decorator_google_style_union(self): + @manifest() + def func( + val: Union[str, List[int]], + ) -> str: + """Function with a Union parameter. + + Args: + val (Union[str, List[int]]): A parameter that can accept a string or a list of integers. + + Returns: + str: A result string. + """ + return "" + + view = func.__ab_manifest__ + + # 检查生成的 Manifest 对象 + assert isinstance(view, Manifest), "view is not an instance of Manifest" + + # 检查参数的总体结构 + parameters = view.function["parameters"] + assert parameters["type"] == "object", "Parameters type does not match 'object'" + assert "properties" in parameters, "Properties not found in parameters" + + # 检查具体参数 'val' + properties = parameters["properties"] + assert "val" in properties, "'val' parameter missing" + val_property = properties["val"] + + # 验证 'val' 参数的各项属性 + assert ( + val_property["type"] == "Union[str, List[int]]" + ), "'val' type does not match 'Union[str, List[int]]'" + assert val_property["required"] is True, "'val' required does not match True" + + def test_decorator_google_style_union_nest1_dict(self): + @manifest() + def func( + val: Union[float, Dict[str, int]], + ) -> str: + """Function with a nested Union parameter. + + Args: + val (Union[float, Dict[str, int]]): A parameter that can accept a float or a dictionary with string keys and integer values. + + Returns: + str: A result string. + """ + return "" + + view = func.__ab_manifest__ + + # 检查生成的 Manifest 对象 + assert isinstance(view, Manifest), "view is not an instance of Manifest" + + # 检查参数的总体结构 + parameters = view.function["parameters"] + assert parameters["type"] == "object", "Parameters type does not match 'object'" + assert "properties" in parameters, "Properties not found in parameters" + + # 检查具体参数 'val' + properties = parameters["properties"] + assert "val" in properties, "'val' parameter missing" + val_property = properties["val"] + + # 验证 'val' 参数的各项属性 + assert ( + val_property["type"] == "Union[float, Dict[str, int]]" + ), "'val' type does not match 'Union[float, Dict[str, int]]'" + assert val_property["required"] is True, "'val' required does not match True" + + def test_decorator_google_style_union_combine(self): + @manifest() + def func( + val: Union[float, Union[str, int]], + ) -> str: + """Function with a combined Union parameter. + + Args: + val (Union[float, str, int]): A parameter that can accept a float, string, or integer. + + Returns: + str: A result string. + """ + return "" + + view = func.__ab_manifest__ + + # 检查生成的 Manifest 对象 + assert isinstance(view, Manifest), "view is not an instance of Manifest" + + # 检查参数的总体结构 + parameters = view.function["parameters"] + assert parameters["type"] == "object", "Parameters type does not match 'object'" + assert "properties" in parameters, "Properties not found in parameters" + + # 检查具体参数 'val' + properties = parameters["properties"] + assert "val" in properties, "'val' parameter missing" + val_property = properties["val"] + + # 验证 'val' 参数的各项属性 + assert ( + val_property["type"] == "Union[float, str, int]" + ), "'val' type does not match 'Union[float, str, int]'" + assert val_property["required"] is True, "'val' required does not match True" + + def test_decorator_google_style_no_annotation(self): + @manifest(description="Test Function") + def func( + val, + ) -> str: + return "" + + view = func.__ab_manifest__ + + assert isinstance(view, Manifest), "view is not an instance of Manifest" + + parameters = view.function["parameters"] + properties = parameters["properties"] + + assert "val" in properties, "'val' parameter missing" + val_property = properties["val"] + + assert val_property["type"] == "Any", "'val' type does not match 'Any'" + assert val_property["required"] is True, "'val' required does not match True" + + def test_decorator_google_style_default(self): + @manifest(description="Test Function") + def func(val: str = "value") -> str: + return "" + + view = func.__ab_manifest__ + + assert isinstance(view, Manifest), "view is not an instance of Manifest" + + parameters = view.function["parameters"] + properties = parameters["properties"] + + assert "val" in properties, "'val' parameter missing" + val_property = properties["val"] + + assert val_property["type"] == "str", "'val' type does not match 'str'" + assert val_property["required"] is False, "'val' required does not match False" + + def test_decorator_google_style_optional(self): + @manifest(description="Test Function") + def func( + val: Optional[str], + ) -> str: + return "" + + view = func.__ab_manifest__ + + assert isinstance(view, Manifest), "view is not an instance of Manifest" + + parameters = view.function["parameters"] + properties = parameters["properties"] + + assert "val" in properties, "'val' parameter missing" + val_property = properties["val"] + + assert ( + val_property["type"] == "Optional[str]" + ), "'val' type does not match 'Optional[str]'" + assert val_property["required"] is False, "'val' required does not match False" + + def test_decorator_google_style_optional_equals_none(self): + @manifest(description="Test Function") + def func( + val: Optional[str] = None, + ) -> str: + return "" + + view = func.__ab_manifest__ + + assert isinstance(view, Manifest), "view is not an instance of Manifest" + + parameters = view.function["parameters"] + properties = parameters["properties"] + + assert "val" in properties, "'val' parameter missing" + val_property = properties["val"] + + assert ( + val_property["type"] == "Optional[str]" + ), "'val' type does not match 'Optional[str]'" + assert val_property["required"] is False, "'val' required does not match False" + + def test_decorator_google_style_optional_list(self): + @manifest(description="Test Function") + def func( + val: Optional[List[str]], + ) -> str: + return "" + + view = func.__ab_manifest__ + + assert isinstance(view, Manifest), "view is not an instance of Manifest" + + parameters = view.function["parameters"] + properties = parameters["properties"] + + assert "val" in properties, "'val' parameter missing" + val_property = properties["val"] + + assert ( + val_property["type"] == "Optional[List[str]]" + ), "'val' type does not match 'Optional[List[str]]'" + assert val_property["required"] is False, "'val' required does not match False" + + def test_decorator_google_style_list_nest_optional(self): + @manifest(description="Test Function") + def func( + val: List[Optional[str]], + ) -> str: + return "" + + view = func.__ab_manifest__ + + assert isinstance(view, Manifest), "view is not an instance of Manifest" + + parameters = view.function["parameters"] + properties = parameters["properties"] + + assert "val" in properties, "'val' parameter missing" + val_property = properties["val"] + + assert ( + val_property["type"] == "List[Optional[str]]" + ), "'val' type does not match 'List[Optional[str]]'" + assert val_property["required"] is True, "'val' required does not match True" + + def test_decorator_google_style_union_nest_single_optional(self): + @manifest(description="Test Function") + def func( + val: Union[Optional[str]], + ) -> str: + return "" + + view = func.__ab_manifest__ + + assert isinstance(view, Manifest), "view is not an instance of Manifest" + + parameters = view.function["parameters"] + properties = parameters["properties"] + + assert "val" in properties, "'val' parameter missing" + val_property = properties["val"] + + assert ( + val_property["type"] == "Optional[str]" + ), "'val' type does not match 'Optional[str]'" + assert val_property["required"] is False, "'val' required does not match False" + + def test_decorator_google_style_union_nest_optional(self): + @manifest(description="Test Function") + def func( + val: Union[Optional[int], Optional[str]], + ) -> str: + return "" + + view = func.__ab_manifest__ + + assert isinstance(view, Manifest), "view is not an instance of Manifest" + + parameters = view.function["parameters"] + properties = parameters["properties"] + + assert "val" in properties, "'val' parameter missing" + val_property = properties["val"] + + # 验证类型,inspect 可能优化嵌套 Union + assert ( + val_property["type"] == "Union[int, str]" + ), "'val' type does not match 'Union[int, str]'" + assert val_property["required"] is False, "'val' required does not match False" + + def test_decorator_google_style_union_nest1_dict_optional_value(self): + @manifest(description="Test Function") + def func( + val: Union[float, Dict[str, Optional[int]]], + ) -> str: + return "" + + view = func.__ab_manifest__ + + assert isinstance(view, Manifest), "view is not an instance of Manifest" + + parameters = view.function["parameters"] + properties = parameters["properties"] + + assert "val" in properties, "'val' parameter missing" + val_property = properties["val"] + + assert ( + val_property["type"] == "Union[float, Dict[str, Optional[int]]]" + ), "'val' type does not match 'Union[float, Dict[str, Optional[int]]]'" + assert val_property["required"] is True, "'val' required does not match True" + + +if __name__ == "__main__": + unittest.main() From f1ecc68208428addd304331cbc5a402a0ad6016c Mon Sep 17 00:00:00 2001 From: userpj Date: Mon, 25 Nov 2024 10:33:03 +0800 Subject: [PATCH 26/85] update --- python/core/manifest/models.py | 4 ---- python/tests/test_manifest_signature.py | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/python/core/manifest/models.py b/python/core/manifest/models.py index a5c6e02aa..1fd2284a1 100644 --- a/python/core/manifest/models.py +++ b/python/core/manifest/models.py @@ -14,10 +14,6 @@ from pydantic import BaseModel, Field from typing import Dict, List, Literal, Any, Optional -from appbuilder.core.manifest.validate import ( - validate_function_param_name, - validate_function_name, -) from appbuilder.core.manifest.manifest_signature import get_signature diff --git a/python/tests/test_manifest_signature.py b/python/tests/test_manifest_signature.py index ac39d3311..dbccb0539 100644 --- a/python/tests/test_manifest_signature.py +++ b/python/tests/test_manifest_signature.py @@ -627,3 +627,4 @@ def func( if __name__ == "__main__": unittest.main() + From a96b1b2645e7d4395badd0a4c97af6ba58c47ef1 Mon Sep 17 00:00:00 2001 From: userpj Date: Mon, 25 Nov 2024 14:30:46 +0800 Subject: [PATCH 27/85] update --- .../end2end_application/agent/tool_call.ipynb | 22 +++-- .../Platform/Application/appbuilder_client.md | 4 +- python/core/manifest/manifest_decorator.py | 16 +--- python/core/manifest/manifest_signature.py | 16 +--- python/core/manifest/models.py | 10 +-- .../tests/test_appbuilder_client_toolcall.py | 62 +++---------- python/tests/test_manifest.py | 87 ++++--------------- python/tests/test_manifest_signature.py | 3 +- 8 files changed, 54 insertions(+), 166 deletions(-) diff --git a/cookbooks/end2end_application/agent/tool_call.ipynb b/cookbooks/end2end_application/agent/tool_call.ipynb index 782fb9503..27c813d48 100644 --- a/cookbooks/end2end_application/agent/tool_call.ipynb +++ b/cookbooks/end2end_application/agent/tool_call.ipynb @@ -883,7 +883,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -923,12 +923,16 @@ ], "source": [ "# 将 model_dump() 的输出进行格式化打印\n", - "print(json.dumps(get_current_weather.__ab_manifest__.model_dump(), indent=4, ensure_ascii=False))" + "print(\n", + " json.dumps(\n", + " appbuilder.Manifest.from_function(get_current_weather).model_dump(), indent=4, ensure_ascii=False\n", + " )\n", + ")" ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -946,12 +950,12 @@ } ], "source": [ - "#调用大模型\n", + "# 调用大模型\n", "msg = client.run(\n", - " conversation_id=conversation_id,\n", - " query=\"今天北京的天气怎么样?\",\n", - " tools = [get_current_weather.__ab_manifest__.model_dump()]\n", - " )\n", + " conversation_id=conversation_id,\n", + " query=\"今天北京的天气怎么样?\",\n", + " tools=[appbuilder.Manifest.from_function(get_current_weather).model_dump()],\n", + ")\n", "print(msg.model_dump_json(indent=4))\n", "# 获取最后的事件和工具调用信息\n", "event = msg.content.events[-1]\n", @@ -1186,4 +1190,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +} diff --git a/docs/BasisModule/Platform/Application/appbuilder_client.md b/docs/BasisModule/Platform/Application/appbuilder_client.md index 6f3d320fd..1b4fc5204 100644 --- a/docs/BasisModule/Platform/Application/appbuilder_client.md +++ b/docs/BasisModule/Platform/Application/appbuilder_client.md @@ -373,7 +373,7 @@ conversation_id = client.create_conversation() def get_current_weather(location: str, unit: str) -> str: return "北京今天25度" -print(json.dumps((get_current_weather.__ab_manifest__).model_dump(), indent=4, ensure_ascii=False)) +print(json.dumps(appbuilder.Manifest.from_function(get_current_weather).model_dump(), indent=4, ensure_ascii=False)) #定义函数列表 functions = [get_current_weather] function_map = {f.__name__: f for f in functions} @@ -381,7 +381,7 @@ function_map = {f.__name__: f for f in functions} msg = client.run( conversation_id=conversation_id, query="今天北京的天气怎么样?", - tools = [(get_current_weather.__ab_manifest__).model_dump()] + tools = [appbuilder.Manifest.from_function(f).model_dump() for f in functions] ) print(msg.model_dump_json(indent=4)) # 获取最后的事件和工具调用信息 diff --git a/python/core/manifest/manifest_decorator.py b/python/core/manifest/manifest_decorator.py index a1c9e4051..32b0330c6 100644 --- a/python/core/manifest/manifest_decorator.py +++ b/python/core/manifest/manifest_decorator.py @@ -122,12 +122,6 @@ def decorator(func): ), # 是否必需 } - # 验证类型字段是否有有效值 - if not param_info["type"]: - raise ValueError( - f"参数 '{param['name']}' 缺少类型信息,请在函数签名中指定类型。" - ) - # 构造 PropertyModel properties[param["name"]] = PropertyModel( name=param_info["name"], @@ -144,9 +138,6 @@ def decorator(func): final_name = name or func.__name__ final_desc = description or func.__doc__ - if not final_desc: - raise ValueError(f"函数 {final_name} 缺少描述") - parameters_model = ParametersModel( type="object", properties={ @@ -208,9 +199,9 @@ def decorator(func): ) # Update parameter view lists for function_parameter decorator. # This will be merged into ManifestView if function decorator runs last like: - # @function - # @function_parameter - # @function_parameter + # @manifest + # @manifest_parameter + # @manifest_parameter if hasattr(func, "__ab_manifest_parameters__"): current_views = func.__ab_manifest_parameters__ else: @@ -218,7 +209,6 @@ def decorator(func): current_views.append(new_view) func.__ab_manifest_parameters__ = current_views - # function_parameter runs after function, merge ParameterView in ManifestView if hasattr(func, "__ab_manifest__"): # 获取现有的 parameters parameters_dict = func.__ab_manifest__.function.get("parameters", {}) diff --git a/python/core/manifest/manifest_signature.py b/python/core/manifest/manifest_signature.py index 39aa78c00..ec8fa0a7e 100644 --- a/python/core/manifest/manifest_signature.py +++ b/python/core/manifest/manifest_signature.py @@ -63,27 +63,19 @@ def get_signature(func): def _parse_parameter(param: Parameter) -> Dict[str, Any]: ret = {} - if param.annotation != Parameter.empty: + if param != Parameter.empty: ret = _parse_annotation(param.annotation) - else: - ret["type_"] = None - ret["required"] = True # 默认为 True - ret["name"] = param.name - if param.default != Parameter.empty: ret["default_value"] = param.default ret["required"] = False - else: - ret["required"] = ret.get("required", True) - return ret -def _parse_annotation(annotation: Any) -> Dict[str, Any]: - # The keys of this dict are compatible with semantic-kernel, do not change them +def _parse_annotation(annotation: Parameter) -> Dict[str, Any]: + # The keys of this dict is compatible with semantic-kernel, do not change them if annotation == Signature.empty: - return {"type_": None, "required": True} + return {"type_": "Any", "required": True} if isinstance(annotation, str): return {"type_": annotation, "required": True} ret = _parse_internal_annotation(annotation, True) diff --git a/python/core/manifest/models.py b/python/core/manifest/models.py index 1fd2284a1..91a3dca58 100644 --- a/python/core/manifest/models.py +++ b/python/core/manifest/models.py @@ -78,9 +78,6 @@ def from_function(cls, func) -> "Manifest": Returns: Manifest: 包含函数元信息的模型。 """ - if func.__doc__ is None: - raise ValueError(f"函数 {func.__name__} 缺少文档字符串") - # 使用 manifest_signature 提取函数签名信息 sig_params, sig_returns = get_signature(func) @@ -88,6 +85,9 @@ def from_function(cls, func) -> "Manifest": properties = {} required = [] + if hasattr(func, "__ab_manifest__"): + return func.__ab_manifest__ + for param in sig_params: param_info = { "name": param["name"], # 参数名称 @@ -98,9 +98,7 @@ def from_function(cls, func) -> "Manifest": # 验证类型字段是否有有效值 if not param_info["type"]: - raise ValueError( - f"参数 '{param['name']}' 缺少类型信息,请在函数签名中指定类型。" - ) + param_info["type"] = "Any" # 构造 PropertyModel properties[param["name"]] = PropertyModel( diff --git a/python/tests/test_appbuilder_client_toolcall.py b/python/tests/test_appbuilder_client_toolcall.py index e7e8c4887..7e35bbad4 100644 --- a/python/tests/test_appbuilder_client_toolcall.py +++ b/python/tests/test_appbuilder_client_toolcall.py @@ -3,7 +3,7 @@ import os -# @unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL","") +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL","") class TestAgentRuntime(unittest.TestCase): def setUp(self): """ @@ -72,8 +72,8 @@ def test_appbuilder_client_tool_call(self): ) print(msg_2.model_dump_json(indent=4)) + def test_appbuilder_client_tool_call_from_function(self): """测试functions2model功能""" - # 定义本地函数 def get_current_weather(location: str, unit: str) -> str: """获取指定中国城市的当前天气信息。 @@ -92,7 +92,9 @@ def get_current_weather(location: str, unit: str) -> str: # 定义函数列表 functions = [get_current_weather] function_map = {f.__name__: f for f in functions} - # 调用大模型 + + client = appbuilder.AppBuilderClient(self.app_id) + conversation_id = client.create_conversation() msg = client.run( conversation_id=conversation_id, query="今天北京的天气怎么样?", @@ -101,7 +103,6 @@ def get_current_weather(location: str, unit: str) -> str: ], ) print(msg.model_dump_json(indent=4)) - # 获取最后的事件和工具调用信息 event = msg.content.events[-1] tool_call = event.tool_calls[-1] @@ -119,8 +120,7 @@ def get_current_weather(location: str, unit: str) -> str: ) print(msg_2.model_dump_json(indent=4)) - """测试装饰器功能功能""" - + def test_appbuilder_client_tool_call_from_function_decorator(self): @appbuilder.manifest( description="获取指定中国城市的当前天气信息。仅支持中国城市的天气查询。参数 `location` 为中国城市名称,其他国家城市不支持天气查询。" ) @@ -138,10 +138,14 @@ def get_current_weather(location: str, unit: str) -> str: functions = [get_current_weather] function_map = {f.__name__: f for f in functions} # 调用大模型 + client = appbuilder.AppBuilderClient(self.app_id) + conversation_id = client.create_conversation() msg = client.run( conversation_id=conversation_id, query="今天北京的天气怎么样?", - tools=[get_current_weather.__ab_manifest__.model_dump()], + tools=[ + appbuilder.Manifest.from_function(f).model_dump() for f in functions + ], ) print(msg.model_dump_json(indent=4)) # 获取最后的事件和工具调用信息 @@ -162,50 +166,6 @@ def get_current_weather(location: str, unit: str) -> str: ) print(msg_2.model_dump_json(indent=4)) - try: - - @appbuilder.manifest() - @appbuilder.manifest_parameter( - name="location", description="城市名,例如:北京。" - ) - @appbuilder.manifest_parameter( - name="unit", description="温度单位,支持 'celsius' 或 'fahrenheit'" - ) - # 定义示例函数 - def get_current_weather(location: str, unit: str) -> str: - return "北京今天25度" - - except ValueError as e: - # 使用 assert 检查是否抛出 ValueError,且包含 "缺少描述" 的信息 - assert "缺少描述" in str( - e - ), f"Expected '缺少描述' in error message, but got: {str(e)}" - else: - # 如果未抛出异常,测试应失败 - assert False, "Expected ValueError but no exception was raised" - - try: - - @appbuilder.manifest() - @appbuilder.manifest_parameter( - name="location", description="城市名,例如:北京。" - ) - @appbuilder.manifest_parameter( - name="unit", description="温度单位,支持 'celsius' 或 'fahrenheit'" - ) - # 定义示例函数 - def get_current_weather(location: str, unit) -> str: - return "北京今天25度" - - except ValueError as e: - # 使用 assert 检查是否抛出 ValueError,且包含 "缺少描述" 的信息 - assert "缺少类型信息" in str( - e - ), f"Expected '缺少类型信息' in error message, but got: {str(e)}" - else: - # 如果未抛出异常,测试应失败 - assert False, "Expected ValueError but no exception was raised" - if __name__ == "__main__": unittest.main() diff --git a/python/tests/test_manifest.py b/python/tests/test_manifest.py index 3c2a2f6b7..a866c93d2 100644 --- a/python/tests/test_manifest.py +++ b/python/tests/test_manifest.py @@ -241,31 +241,6 @@ def func( and len(function_manifest.function["parameters"]) == 1 ) - def test_google_style_no_args_no_return(self): - def func( - name: str, - /, - *args, - val: str = None, - val_obj: Optional[Any] = None, - data: Dict[str, int] = None, - **kwargs, - ) -> str: - """Google style docstring.""" - return "" - - # 断言这里会抛出参数类型缺失导致的ValueError异常 - try: - function_manifest = appbuilder.Manifest.from_function(func) - except ValueError as e: - # 使用 assert 检查是否抛出 ValueError,且包含 "缺少描述" 的信息 - assert "缺少类型信息" in str( - e - ), f"Expected '缺少类型信息' in error message, but got: {str(e)}" - else: - # 如果未抛出异常,测试应失败 - assert False, "Expected ValueError but no exception was raised" - def test_no_doc(self): def func( name: str, @@ -333,53 +308,21 @@ def func( assert "required" in parameters, "'required' field missing in parameters" assert parameters["required"] == ["name"], "'required' does not match ['name']" - def test_missing_type(self): - """测试参数缺少类型信息是否抛出 ValueError 异常。""" - try: - - @appbuilder.manifest() - @appbuilder.manifest_parameter( - name="location", description="城市名,例如:北京。" - ) - @appbuilder.manifest_parameter( - name="unit", description="温度单位,支持 'celsius' 或 'fahrenheit'" - ) - # 定义示例函数 - def get_current_weather(location: str, unit) -> str: - return "北京今天25度" - - except ValueError as e: - # 使用 assert 检查是否抛出 ValueError,且包含 "缺少描述" 的信息 - assert "缺少类型信息" in str( - e - ), f"Expected '缺少类型信息' in error message, but got: {str(e)}" - else: - # 如果未抛出异常,测试应失败 - assert False, "Expected ValueError but no exception was raised" - - def test_missing_description(self): - """测试函数缺少描述是否抛出 ValueError 异常。""" - try: - - @appbuilder.manifest() - @appbuilder.manifest_parameter( - name="location", description="城市名,例如:北京。" - ) - @appbuilder.manifest_parameter( - name="unit", description="温度单位,支持 'celsius' 或 'fahrenheit'" - ) - # 定义示例函数 - def get_current_weather(location: str, unit: str) -> str: - return "北京今天25度" - - except ValueError as e: - # 使用 assert 检查是否抛出 ValueError,且包含 "缺少描述" 的信息 - assert "缺少描述" in str( - e - ), f"Expected '缺少描述' in error message, but got: {str(e)}" - else: - # 如果未抛出异常,测试应失败 - assert False, "Expected ValueError but no exception was raised" + def test_manifest_decorator(self): + @appbuilder.manifest( + description="获取指定中国城市的当前天气信息。仅支持中国城市的天气查询。参数 `location` 为中国城市名称,其他国家城市不支持天气查询。" + ) + @appbuilder.manifest_parameter( + name="location", description="城市名,例如:北京。" + ) + @appbuilder.manifest_parameter( + name="unit", description="温度单位,支持 'celsius' 或 'fahrenheit'" + ) + # 定义示例函数 + def get_current_weather(location: str, unit) -> str: + return "北京今天25度" + func_manifest = appbuilder.Manifest.from_function(get_current_weather).model_dump() + assert func_manifest.get("function").get("description") is not None if __name__ == "__main__": diff --git a/python/tests/test_manifest_signature.py b/python/tests/test_manifest_signature.py index dbccb0539..298dafbe0 100644 --- a/python/tests/test_manifest_signature.py +++ b/python/tests/test_manifest_signature.py @@ -12,11 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. import unittest +import os from typing import Any, Dict, List, Optional, Union from appbuilder import Manifest, manifest -# @unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") class TestManifestSignature(unittest.TestCase): def test_is_normal(self): @manifest() From f48c45078d28b625f0fe6f62d8523490cc188b35 Mon Sep 17 00:00:00 2001 From: userpj Date: Mon, 25 Nov 2024 14:55:41 +0800 Subject: [PATCH 28/85] update --- cookbooks/end2end_application/agent/tool_call.ipynb | 12 ++++++------ .../Platform/Application/appbuilder_client.md | 6 +++--- .../console/appbuilder_client/appbuilder_client.py | 3 ++- python/core/console/appbuilder_client/data_class.py | 3 ++- python/tests/test_appbuilder_client_toolcall.py | 4 ++-- python/tests/test_manifest.py | 4 ++-- 6 files changed, 17 insertions(+), 15 deletions(-) diff --git a/cookbooks/end2end_application/agent/tool_call.ipynb b/cookbooks/end2end_application/agent/tool_call.ipynb index 27c813d48..6cdcaf186 100644 --- a/cookbooks/end2end_application/agent/tool_call.ipynb +++ b/cookbooks/end2end_application/agent/tool_call.ipynb @@ -711,7 +711,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -754,7 +754,7 @@ } ], "source": [ - "print(json.dumps(appbuilder.Manifest.from_function(get_current_weather).model_dump(), indent=4, ensure_ascii=False))" + "print(json.dumps(appbuilder.Manifest.from_function(get_current_weather), indent=4, ensure_ascii=False))" ] }, { @@ -766,7 +766,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -788,7 +788,7 @@ "msg = client.run(\n", " conversation_id=conversation_id,\n", " query=\"今天北京的天气怎么样?\",\n", - " tools = [appbuilder.Manifest.from_function(f).model_dump() for f in functions]\n", + " tools = [appbuilder.Manifest.from_function(f) for f in functions]\n", " )\n", "print(msg.model_dump_json(indent=4))\n", "# 获取最后的事件和工具调用信息\n", @@ -925,7 +925,7 @@ "# 将 model_dump() 的输出进行格式化打印\n", "print(\n", " json.dumps(\n", - " appbuilder.Manifest.from_function(get_current_weather).model_dump(), indent=4, ensure_ascii=False\n", + " appbuilder.Manifest.from_function(get_current_weather), indent=4, ensure_ascii=False\n", " )\n", ")" ] @@ -954,7 +954,7 @@ "msg = client.run(\n", " conversation_id=conversation_id,\n", " query=\"今天北京的天气怎么样?\",\n", - " tools=[appbuilder.Manifest.from_function(get_current_weather).model_dump()],\n", + " tools=[appbuilder.Manifest.from_function(get_current_weather)],\n", ")\n", "print(msg.model_dump_json(indent=4))\n", "# 获取最后的事件和工具调用信息\n", diff --git a/docs/BasisModule/Platform/Application/appbuilder_client.md b/docs/BasisModule/Platform/Application/appbuilder_client.md index 1b4fc5204..88d8fb06b 100644 --- a/docs/BasisModule/Platform/Application/appbuilder_client.md +++ b/docs/BasisModule/Platform/Application/appbuilder_client.md @@ -323,7 +323,7 @@ function_map = {f.__name__: f for f in functions} msg = client.run( conversation_id=conversation_id, query="今天北京的天气怎么样?", - tools = [appbuilder.Manifest.from_function(f).model_dump() for f in functions] + tools = [appbuilder.Manifest.from_function(f) for f in functions] ) print(msg.model_dump_json(indent=4)) # 获取最后的事件和工具调用信息 @@ -373,7 +373,7 @@ conversation_id = client.create_conversation() def get_current_weather(location: str, unit: str) -> str: return "北京今天25度" -print(json.dumps(appbuilder.Manifest.from_function(get_current_weather).model_dump(), indent=4, ensure_ascii=False)) +print(json.dumps(appbuilder.Manifest.from_function(get_current_weather), indent=4, ensure_ascii=False)) #定义函数列表 functions = [get_current_weather] function_map = {f.__name__: f for f in functions} @@ -381,7 +381,7 @@ function_map = {f.__name__: f for f in functions} msg = client.run( conversation_id=conversation_id, query="今天北京的天气怎么样?", - tools = [appbuilder.Manifest.from_function(f).model_dump() for f in functions] + tools = [appbuilder.Manifest.from_function(f) for f in functions] ) print(msg.model_dump_json(indent=4)) # 获取最后的事件和工具调用信息 diff --git a/python/core/console/appbuilder_client/appbuilder_client.py b/python/core/console/appbuilder_client/appbuilder_client.py index 357580bef..b01525423 100644 --- a/python/core/console/appbuilder_client/appbuilder_client.py +++ b/python/core/console/appbuilder_client/appbuilder_client.py @@ -19,6 +19,7 @@ import queue from typing import Optional from appbuilder.core.component import Message, Component +from appbuilder.core.manifest.models import Manifest from appbuilder.core.console.appbuilder_client import data_class from appbuilder.core._exception import AppBuilderServerException from appbuilder.utils.sse_util import SSEClient @@ -250,7 +251,7 @@ def run(self, conversation_id: str, query: str = "", file_ids: list = [], stream: bool = False, - tools: list[data_class.Tool] = None, + tools: list[Manifest]= None, tool_outputs: list[data_class.ToolOutput] = None, tool_choice: data_class.ToolChoice = None, end_user_id: str = None, diff --git a/python/core/console/appbuilder_client/data_class.py b/python/core/console/appbuilder_client/data_class.py index cbc06ff31..066964322 100644 --- a/python/core/console/appbuilder_client/data_class.py +++ b/python/core/console/appbuilder_client/data_class.py @@ -16,6 +16,7 @@ from pydantic import Field from typing import Union from typing import Optional +from appbuilder.core.manifest.models import Manifest class Function(BaseModel): @@ -112,7 +113,7 @@ class AppBuilderClientRequest(BaseModel): conversation_id: str file_ids: Optional[list[str]] = None app_id: str - tools: Optional[list[Tool]] = None + tools: Optional[list[Manifest]] = None tool_outputs: Optional[list[ToolOutput]] = None tool_choice: Optional[ToolChoice] = None end_user_id: Optional[str] = None diff --git a/python/tests/test_appbuilder_client_toolcall.py b/python/tests/test_appbuilder_client_toolcall.py index 7e35bbad4..94c0c8924 100644 --- a/python/tests/test_appbuilder_client_toolcall.py +++ b/python/tests/test_appbuilder_client_toolcall.py @@ -99,7 +99,7 @@ def get_current_weather(location: str, unit: str) -> str: conversation_id=conversation_id, query="今天北京的天气怎么样?", tools=[ - appbuilder.Manifest.from_function(f).model_dump() for f in functions + appbuilder.Manifest.from_function(f) for f in functions ], ) print(msg.model_dump_json(indent=4)) @@ -144,7 +144,7 @@ def get_current_weather(location: str, unit: str) -> str: conversation_id=conversation_id, query="今天北京的天气怎么样?", tools=[ - appbuilder.Manifest.from_function(f).model_dump() for f in functions + appbuilder.Manifest.from_function(f) for f in functions ], ) print(msg.model_dump_json(indent=4)) diff --git a/python/tests/test_manifest.py b/python/tests/test_manifest.py index a866c93d2..59542974f 100644 --- a/python/tests/test_manifest.py +++ b/python/tests/test_manifest.py @@ -321,8 +321,8 @@ def test_manifest_decorator(self): # 定义示例函数 def get_current_weather(location: str, unit) -> str: return "北京今天25度" - func_manifest = appbuilder.Manifest.from_function(get_current_weather).model_dump() - assert func_manifest.get("function").get("description") is not None + func_manifest = appbuilder.Manifest.from_function(get_current_weather) + assert func_manifest.function.get("description") is not None if __name__ == "__main__": From 85ee4db3f380945ae34b6caa469ba6ec3a694838 Mon Sep 17 00:00:00 2001 From: userpj Date: Mon, 25 Nov 2024 15:13:48 +0800 Subject: [PATCH 29/85] update --- python/core/manifest/manifest_decorator.py | 55 ---------------------- 1 file changed, 55 deletions(-) diff --git a/python/core/manifest/manifest_decorator.py b/python/core/manifest/manifest_decorator.py index 32b0330c6..93fd914ba 100644 --- a/python/core/manifest/manifest_decorator.py +++ b/python/core/manifest/manifest_decorator.py @@ -20,61 +20,6 @@ from appbuilder.core.manifest.manifest_signature import get_signature from appbuilder.core.manifest.models import Manifest, PropertyModel, ParametersModel - -# The following two functions are here to allow dynamically updating function description. -# Check tool/builtin/openapi_plugin.py for example. -def function_description(cls, func): - """Get the description of a function.""" - - if hasattr(func, "__ab_manifest__"): - view = func.__ab_manifest__ - return view.description if view else "" - - -def update_function_description(func, description): - """Update function description.""" - func.__dict__["__kernel_function_description__"] = description or "" - func.__dict__["__ab_manifest_description__"] = description or "" - - -def get_function_schema_with_inspect(method): - ( - args, - _, - varkw, - defaults, - kwonlyargs, - kwonlydefaults, - annotations, - ) = inspect.getfullargspec(method) - if len(args) > 0 and (args[0] == "self" or args[0] == "cls"): - args = args[1:] # remove self or cls - - if args or varkw: - if defaults is None: - defaults = () - non_default_args_count = len(args) - len(defaults) - defaults = (...,) * non_default_args_count + defaults - - keyword_only_params = { - param: kwonlydefaults.get(param, Any) for param in kwonlyargs - } - params = { - param: (annotations.get(param, Any), default) - for param, default in zip(args, defaults) - } - return create_model( - "func", - **params, - **keyword_only_params, - __base__=PydanticBaseModel, - __config__=None, - __doc__="", - ).schema()["properties"] - else: # method has no arguments - return None - - def manifest( *, description: Optional[str] = None, From 67df762029f643bc130f90dc1a65fefba34efc68 Mon Sep 17 00:00:00 2001 From: peiwenYe <963623403@qq.com> Date: Mon, 25 Nov 2024 15:35:03 +0800 Subject: [PATCH 30/85] modify unittest (#612) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * modify unittest * 修改单测 * 修改系统参数变量 * 修改import方式 * 修改Component生成text字段的逻辑 * 修改Component生成text字段的逻辑 --------- Co-authored-by: yepeiwen01 --- python/core/component.py | 31 +++++++++++++++++-- .../v2/animal_recognize/component.py | 4 +-- .../v2/image_understand/component.py | 4 +-- python/tests/component_check.py | 6 ++-- ...output_schemas.py => component_schemas.py} | 20 ------------ python/tests/component_tool_eval_schemas.py | 21 +++++++++++++ python/tests/test_all_components.py | 3 +- python/tests/test_base_component.py | 13 +++++++- 8 files changed, 70 insertions(+), 32 deletions(-) rename python/tests/{component_output_schemas.py => component_schemas.py} (86%) create mode 100644 python/tests/component_tool_eval_schemas.py diff --git a/python/core/component.py b/python/core/component.py index c95435b82..add983871 100644 --- a/python/core/component.py +++ b/python/core/component.py @@ -18,7 +18,7 @@ from enum import Enum from pydantic import BaseModel -from pydantic import Field +from pydantic import Field, field_validator from typing import ( Dict, List, Optional, Any, Generator, Union, AsyncGenerator) from appbuilder.core.utils import ttl_lru_cache @@ -126,9 +126,32 @@ class Content(BaseModel): description="耗时、性能、内存等trace及debug所需信息") type: str = Field(default="text", description="代表event 类型,包括 text、code、files、urls、oral_text、references、image、chart、audio该字段的取值决定了下面text字段的内容结构") - text: Union[Text, Code, Files, Urls, OralText, References, Image, Chart, Audio] = Field(default=Text, + text: Union[Text, Code, Files, Urls, OralText, References, Image, Chart, Audio] = Field(default=Text, description="代表当前 event 元素的内容,每一种 event 对应的 text 结构固定") + @field_validator('text', mode='before') + def set_text(cls, v, values, **kwargs): + if values.data['type'] == 'text': + return Text(**v) + elif values.data['type'] == 'code': + return Code(**v) + elif values.data['type'] == 'files': + return Files(**v) + elif values.data['type'] == 'urls': + return Urls(**v) + elif values.data['type'] == 'oral_text': + return OralText(**v) + elif values.data['type'] == 'references': + return References(**v) + elif values.data['type'] == 'image': + return Image(**v) + elif values.data['type'] == 'chart': + return Chart(**v) + elif values.data['type'] == 'audio': + return Audio(**v) + else: + raise ValueError(f"Invalid value for 'type': {values['type']}") + class ComponentOutput(BaseModel): role: str = Field(default="tool", @@ -472,7 +495,7 @@ def create_output(self, type, text, role="tool", name="", visible_scope="all", r text = {"info": text} else: raise ValueError("Only when type=text/code/urls/oral_text, string text is allowed! Please give dict text") - else: + elif isinstance(text, dict): if type == "text": key_list = ["info"] elif type == "code": @@ -494,6 +517,8 @@ def create_output(self, type, text, role="tool", name="", visible_scope="all", r else: raise ValueError("Unknown type: {}".format(type)) assert all(key in text for key in key_list), "all keys:{} must be included in the text field".format(key_list) + else: + raise ValueError("text must be str or dict") assert role in ["tool", "assistant"], "role must be 'tool' or 'assistant'" result = { diff --git a/python/core/components/v2/animal_recognize/component.py b/python/core/components/v2/animal_recognize/component.py index 9c8b5ed4a..68f8e52b2 100644 --- a/python/core/components/v2/animal_recognize/component.py +++ b/python/core/components/v2/animal_recognize/component.py @@ -161,8 +161,8 @@ def tool_eval( Returns: Union[Generator[str, None, None], str]: 动物识别结果,包括识别出的动物类别和相应的置信度信息。 """ - traceid = kwargs.get("traceid") - file_urls = kwargs.get("file_urls", {}) + traceid = kwargs.get("_sys_traceid") + file_urls = kwargs.get("_sys_file_urls", {}) yield from self._recognize_w_post_process(img_name, img_url, file_urls, request_id=traceid) def _recognize_w_post_process(self, img_name, img_url, file_urls, request_id=None) -> str: diff --git a/python/core/components/v2/image_understand/component.py b/python/core/components/v2/image_understand/component.py index b10874645..f07120891 100644 --- a/python/core/components/v2/image_understand/component.py +++ b/python/core/components/v2/image_understand/component.py @@ -188,8 +188,8 @@ def tool_eval( Returns: Union[Generator[str, None, None], str]: 图片内容理解结果 """ - traceid = kwargs.get("traceid") - file_urls = kwargs.get("file_urls", {}) + traceid = kwargs.get("_sys_traceid") + file_urls = kwargs.get("_sys_file_urls", {}) rec_res, raw_data = self._recognize_w_post_process(img_name, img_url, file_urls, request_id=traceid) llm_result = self.create_output(type="text", text=rec_res, name="text_1", raw_data=raw_data, visible_scope='llm') yield llm_result diff --git a/python/tests/component_check.py b/python/tests/component_check.py index b72f3171c..10ef92a60 100644 --- a/python/tests/component_check.py +++ b/python/tests/component_check.py @@ -20,8 +20,9 @@ from typing import Generator from appbuilder.utils.func_utils import Singleton from appbuilder.utils.json_schema_to_model import json_schema_to_pydantic_model +from appbuilder.tests.component_schemas import type_to_json_schemas from component_tool_eval_cases import component_tool_eval_cases -from component_output_schemas import type_to_json_schemas, components_tool_eval_output_json_maps +from component_tool_eval_schemas import components_tool_eval_output_json_maps class CheckInfo(BaseModel): @@ -265,7 +266,6 @@ class ToolEvalOutputJsonRule(RuleBase): def __init__(self): super().__init__() self.rule_name = 'ToolEvalOutputJsonRule' - self.output_types = list(type_to_json_schemas.keys()) def _check_pre_format(self, outputs): invalid_details = [] @@ -279,7 +279,7 @@ def _check_pre_format(self, outputs): break out_type = content["type"] - if out_type not in self.output_types: + if out_type not in type_to_json_schemas: invalid_details.append("ToolEval返回值不符合JSON Schema:返回content.type={} 不是合法的输出类型".format(out_type)) break return invalid_details diff --git a/python/tests/component_output_schemas.py b/python/tests/component_schemas.py similarity index 86% rename from python/tests/component_output_schemas.py rename to python/tests/component_schemas.py index c515c7b17..440f5ca94 100644 --- a/python/tests/component_output_schemas.py +++ b/python/tests/component_schemas.py @@ -1,16 +1,3 @@ -# Copyright (c) 2024 Baidu, Inc. 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 copy base_item_schema = { "$schema": "base iter template", @@ -259,11 +246,4 @@ "image": image_schema, "chart": chart_schema, "audio": audio_schema -} - -components_tool_eval_output_json_maps = { - "AnimalRecognition": [text_schema], - "ASR": [text_schema], - "TreeMind": [text_schema, url_schema], - "ImageUnderstand": [text_schema] } \ No newline at end of file diff --git a/python/tests/component_tool_eval_schemas.py b/python/tests/component_tool_eval_schemas.py new file mode 100644 index 000000000..68c071f30 --- /dev/null +++ b/python/tests/component_tool_eval_schemas.py @@ -0,0 +1,21 @@ +# Copyright (c) 2024 Baidu, Inc. 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 appbuilder.tests.component_schemas import text_schema, url_schema, image_schema, code_schema, file_schema, oral_text_schema, references_schema, chart_schema, audio_schema + +components_tool_eval_output_json_maps = { + "AnimalRecognition": [text_schema], + "ASR": [text_schema], + "TreeMind": [text_schema, url_schema], + "ImageUnderstand": [text_schema] +} \ No newline at end of file diff --git a/python/tests/test_all_components.py b/python/tests/test_all_components.py index 404c400c1..4cde0ac52 100644 --- a/python/tests/test_all_components.py +++ b/python/tests/test_all_components.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. import unittest +import os import numpy as np import pandas as pd @@ -32,7 +33,7 @@ def write_error_data(txt_file_path, error_df,error_stats): file.write(f"错误信息: {error}, 出现次数: {count}\n") print(f"\n错误信息已写入: {txt_file_path}") -# @unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") class TestComponentManifestsAndToolEval(unittest.TestCase): def setUp(self) -> None: self.all_components = get_all_components() diff --git a/python/tests/test_base_component.py b/python/tests/test_base_component.py index 3d82aea25..1f6e72a90 100644 --- a/python/tests/test_base_component.py +++ b/python/tests/test_base_component.py @@ -1,7 +1,7 @@ import os import unittest from appbuilder.core.component import Component -from appbuilder.core.component import ComponentOutput +from appbuilder.core.component import ComponentOutput, Urls, Chart @unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") class TestBaseComponent(unittest.TestCase): @@ -36,6 +36,17 @@ def test_valid_output_with_dict(self): self.assertIsInstance(output6, ComponentOutput) self.assertIsInstance(output7, ComponentOutput) self.assertIsInstance(output8, ComponentOutput) + + def test_valid_output_type_with_same_key(self): + output1 = self.component.create_output(type="urls", text={"url": "http://www.baidu.com"}) + self.assertIsInstance(output1.content[0].text, Urls) + output2 = self.component.create_output(type="chart", text={"filename": "file.jpg", "url": "http://www.baidu.com"}) + self.assertIsInstance(output2.content[0].text, Chart) + with self.assertRaises(ValueError): + output = self.component.create_output(type="files", text=["http://www.baidu.com"]) + with self.assertRaises(ValueError): + output = self.component.create_output(type="test", text={"filename": "file.txt", "url": ["http://www.baidu.com"]}) + def test_invalid_output_type_json(self): From a8fc85b6541dde8fec746d83945940ce7376a4e3 Mon Sep 17 00:00:00 2001 From: userpj Date: Mon, 25 Nov 2024 15:52:46 +0800 Subject: [PATCH 31/85] update --- python/tests/test_manifest_decorator.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/python/tests/test_manifest_decorator.py b/python/tests/test_manifest_decorator.py index 35a1e6fcd..831501b62 100644 --- a/python/tests/test_manifest_decorator.py +++ b/python/tests/test_manifest_decorator.py @@ -12,6 +12,7 @@ import os import unittest from appbuilder import Manifest, manifest, manifest_parameter +from appbuilder.core.manifest.manifest_decorator import _merge_dict, _update_list @unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") @@ -192,6 +193,23 @@ def func(param: str = "[]") -> int: "param" not in parameters["required"] ), "'param' should not be required as it has a default value" + def test_merge_dict(self): + self.assertEqual(_merge_dict({}, {}), {}) + self.assertEqual(_merge_dict({}, {"a": 1}), {"a": 1}) + + def test_update_list(self): + def condition(item, new_item): + return item['id'] == new_item['id'] + + def replacer(item, new_item): + item.update(new_item) + return item + + existing_item = {'id': 1, 'value': 'a'} + new_item = {'id': 1, 'value': 'b'} + list = [existing_item] + expected_result = [new_item] + self.assertEqual(_update_list(new_item, list, condition, replacer), expected_result) if __name__ == "__main__": unittest.main() From 1943db912782ea2e1afbcb46bd5934a1077604ea Mon Sep 17 00:00:00 2001 From: C9luster <138663536+C9luster@users.noreply.github.com> Date: Mon, 25 Nov 2024 18:02:37 +0800 Subject: [PATCH 32/85] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E7=94=9F?= =?UTF-8?q?=E5=9B=BE=E7=BB=84=E4=BB=B6V2=E7=89=88=E6=9C=AC,=E8=A7=A3?= =?UTF-8?q?=E5=86=B3=E7=BB=84=E4=BB=B6=E6=A3=80=E6=B5=8B=E9=83=A8=E5=88=86?= =?UTF-8?q?BUG=20(#616)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: yinjiaqi --- python/core/components/v2/__init__.py | 2 + .../components/v2/text_to_image/__init__.py | 13 + .../components/v2/text_to_image/component.py | 354 ++++++++++++++++++ python/tests/component_schemas.py | 17 +- python/tests/component_tool_eval_cases.py | 7 +- python/tests/component_tool_eval_schemas.py | 3 +- python/tests/test_v2_text_to_image.py | 71 ++++ 7 files changed, 462 insertions(+), 5 deletions(-) create mode 100644 python/core/components/v2/text_to_image/__init__.py create mode 100644 python/core/components/v2/text_to_image/component.py create mode 100644 python/tests/test_v2_text_to_image.py diff --git a/python/core/components/v2/__init__.py b/python/core/components/v2/__init__.py index d1a3b8811..c9c7d06aa 100644 --- a/python/core/components/v2/__init__.py +++ b/python/core/components/v2/__init__.py @@ -14,8 +14,10 @@ from .animal_recognize.component import AnimalRecognition from .image_understand.component import ImageUnderstand +from .text_to_image.component import Text2Image __V2_COMPONENTS__ = [ "AnimalRecognition", "ImageUnderstand", + "Text2Image", ] # NOQA \ No newline at end of file diff --git a/python/core/components/v2/text_to_image/__init__.py b/python/core/components/v2/text_to_image/__init__.py new file mode 100644 index 000000000..b4518e8cd --- /dev/null +++ b/python/core/components/v2/text_to_image/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2024 Baidu, Inc. 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. \ No newline at end of file diff --git a/python/core/components/v2/text_to_image/component.py b/python/core/components/v2/text_to_image/component.py new file mode 100644 index 000000000..ea09dd8be --- /dev/null +++ b/python/core/components/v2/text_to_image/component.py @@ -0,0 +1,354 @@ +# Copyright (c) 2024 Baidu, Inc. 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. + +r"""Text2Image component. +""" +import time +import math + +from typing import Generator, Union, Optional +from appbuilder.core.component import Component +from appbuilder.core.message import Message +from appbuilder.core._client import HTTPClient +from appbuilder.core._exception import AppBuilderServerException, RiskInputException +from appbuilder.core.components.text_to_image.model import Text2ImageSubmitRequest, Text2ImageQueryRequest, \ + Text2ImageQueryResponse, Text2ImageSubmitResponse, Text2ImageOutMessage +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace + + +class Text2Image(Component): + r""" + 文生图组件,即对于输入的文本,输出生成的图片url。 + + Examples: + + .. code-block:: python + + import appbuilder + text_to_image = appbuilder.Text2Image() + os.environ["APPBUILDER_TOKEN"] = '...' + content_data = {"prompt": "上海的经典风景", "width": 1024, "height": 1024, "image_num": 1} + msg = appbuilder.Message(content_data) + out = text_to_image.run(inp) + # 打印生成结果 + print(out.content) # eg: {"img_urls": ["xxx"]} + """ + name = "text_to_image" + version = "v1" + manifests = [ + { + "name": "text_to_image", + "description": "文生图,该组件只用于图片创作。当用户需要进行场景、人物、海报等内容的绘制时,使用该画图组件。如果用户需要生成图表(柱状图、折线图、雷达图等),则必须使用代码解释器。", + "parameters": { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "文生图用的query。特别注意,这个字段只能由中文字符组成,不能含有任何英语描述。" + } + }, + "required": [ + "query" + ] + } + } + ] + + @HTTPClient.check_param + @components_run_trace + def run( + self, + message: Message, + width: int = 1024, + height: int = 1024, + image_num: int = 1, + image: Optional[str] = None, + url: Optional[str] = None, + pdf_file: Optional[str] = None, + pdf_file_num: Optional[str] = None, + change_degree: Optional[int] = None, + text_content: Optional[str] = None, + task_time_out: Optional[int]= None, + text_check: Optional[int] = 1, + request_id: Optional[str] = None + ): + """ + 执行文本到图像的生成任务。 + + Args: + message (Message): 包含任务相关信息的消息对象。 + width (int, optional): 生成的图像的宽度,默认为1024。 + height (int, optional): 生成的图像的高度,默认为1024。 + image_num (int, optional): 生成图像的数量,默认为1。 + image (Optional[str], optional): 参考图像的路径或URL,默认为None。 + url (Optional[str], optional): 参考图像的URL,默认为None。 + pdf_file (Optional[str], optional): 参考PDF文件的路径,默认为None。 + pdf_file_num (Optional[str], optional): 参考PDF文件中的页码范围,默认为None。 + change_degree (Optional[int], optional): 图像变换的程度,默认为None。 + text_content (Optional[str], optional): 需要转换的文本内容,默认为None。 + task_time_out (Optional[int], optional): 任务超时时间,默认为None。 + text_check (Optional[int], optional): 是否进行文本内容检查,默认为1。 + request_id (Optional[str], optional): 请求的唯一标识,默认为None。 + + Returns: + Message: 包含生成图像URL的消息对象。 + + Raises: + HTTPError: 请求失败时抛出异常。 + + """ + prompt=message.content["prompt"] + img_urls, raw_date = self.__recognize( + prompt = prompt, + width = width, + height = height, + image_num = image_num, + image = image, + url = url, + pdf_file = pdf_file, + pdf_file_num = pdf_file_num, + change_degree = change_degree, + text_content = text_content, + task_time_out = task_time_out, + text_check = text_check, + request_id = request_id + ) + if len(img_urls) == 0: + raise RiskInputException(f'prompt{prompt} 中可能存在敏感词') + out = Text2ImageOutMessage(img_urls=img_urls) + return Message(content=out.model_dump()) + + @components_run_stream_trace + def tool_eval( + self, + query: str, + **kwargs, + ) -> Union[Generator[str, None, None], str]: + """ + 评估工具方法。 + + Args: + query (str): 输入的查询字符串。 + **kwargs: 任意数量的关键字参数,其中可以包括 'origin_query'。 + + Returns: + Union[Generator[str, None, None], str]: + 返回一个生成器,生成类型为 'urls' 的输出,其中包含图片URL列表。 + 如果发生异常,则可能返回错误字符串。 + + Raises: + AppBuilderServerException: 如果绘图服务发生错误,则抛出此异常。 + RiskInputException: 如果查询字符串中包含敏感词,则抛出此异常。 + + """ + traceid = kwargs.get("traceid") + prompt = query + width = kwargs.get("width", 1024) + height = kwargs.get("height", 1024) + image_num = kwargs.get("image_num", 1) + image = kwargs.get("image", None) + url = kwargs.get("url", None) + pdf_file = kwargs.get("pdf_file", None) + pdf_file_num = kwargs.get("pdf_file_num", None) + change_degree = kwargs.get("change_degree", None) + text_content = kwargs.get("text_content", None) + task_time_out = kwargs.get("task_time_out", None) + text_check = kwargs.get("text_check", 1) + request_id = traceid + + img_urls, raw_date = self.__recognize( + prompt = prompt, + width = width, + height = height, + image_num = image_num, + image = image, + url = url, + pdf_file = pdf_file, + pdf_file_num = pdf_file_num, + change_degree = change_degree, + text_content = text_content, + task_time_out = task_time_out, + text_check = text_check, + request_id = request_id + ) + + if len(img_urls) == 0: + raise RiskInputException(f'query:{query} 中可能存在敏感词') + + for url_number in range(len(img_urls)): + yield self.create_output(type = 'urls', text = img_urls[url_number], name=f"url_{url_number + 1}", raw_data = raw_date) + + def __recognize( + self, + prompt: str, + width: int = 1024, + height: int = 1024, + image_num: int = 1, + image: Optional[str] = None, + url: Optional[str] = None, + pdf_file: Optional[str] = None, + pdf_file_num: Optional[str] = None, + change_degree: Optional[int] = None, + text_content: Optional[str] = None, + task_time_out: Optional[int]= None, + text_check: Optional[int] = 1, + request_id: Optional[str] = None + ): + """ + 识别并生成图片。 + + Args: + prompt (str): 提示文本,用于生成图片。 + width (int, optional): 图片宽度,默认为1024。 + height (int, optional): 图片高度,默认为1024。 + image_num (int, optional): 生成图片的数量,默认为1。 + image (str, optional): 传入图片路径,默认为None。 + url (str, optional): 图片URL,默认为None。 + pdf_file (str, optional): PDF文件路径,默认为None。 + pdf_file_num (str, optional): 需要转换的PDF页数,默认为None。 + change_degree (int, optional): 图片旋转角度,默认为None。 + text_content (str, optional): 文本内容,默认为None。 + task_time_out (int, optional): 任务超时时间,默认为None。 + text_check (int, optional): 文本校验选项,默认为1。 + request_id (str, optional): 请求ID,默认为None。 + + Returns: + tuple: 包含生成的图片URL列表和返回数据的元组。 + + """ + headers = self._http_client.auth_header() + headers["Content-Type"] = "application/json" + api_url = self._http_client.service_url("/v1/bce/aip/ernievilg/v1/txt2imgv2") + + req = Text2ImageSubmitRequest( + prompt=prompt, + width=width, + height=height, + image_num=image_num, + image=image, + url=url, + pdf_file=pdf_file, + pdf_file_num=pdf_file_num, + change_degree=change_degree, + text_content=text_content, + task_time_out=task_time_out, + text_check=text_check + ) + response = self.http_client.session.post(api_url, json=req.model_dump(), headers=headers, timeout=None) + self._http_client.check_response_header(response) + data = response.json() + resp= Text2ImageSubmitResponse(**data) + + taskId = resp.data.task_id + if taskId is not None: + task_request_time = 1 + + while True: + request = Text2ImageQueryRequest(task_id=taskId) + text2ImageQueryResponse, data = self._queryText2ImageData(request, request_id=request_id) + if text2ImageQueryResponse.data.task_progress is not None: + task_progress = float(text2ImageQueryResponse.data.task_progress) + if math.isclose(1.0, task_progress, rel_tol=1e-9, abs_tol=0.0): + break + + # NOTE(chengmo):文生图组件的返回时间在10s以上,查询过于频繁会被限流,导致异常报错 + # 此处采用 yangyongzhen老师提供的方案,前三次查询间隔3s,后三次查询间隔逐渐增大 + if task_request_time <= 3: + time.sleep(3) + else: + time.sleep(task_request_time) + task_request_time += 1 + + img_urls = self._extract_img_urls(text2ImageQueryResponse) + + return img_urls, data + + def _queryText2ImageData( + self, + request: Text2ImageQueryRequest, + timeout: float = None, + retry: int = 0, + request_id: str = None, + ) -> Text2ImageQueryResponse: + """ + 将文本查询请求转换为图像数据。 + + Args: + request (Text2ImageQueryRequest): 输入请求,必填参数。 + timeout (float, optional): 请求的超时时间,默认为None。 + retry (int, optional): 请求的重试次数,默认为0。 + request_id (str, optional): 请求的唯一标识符,默认为None。 + + Returns: + Text2ImageQueryResponse: 接口返回的输出消息。 + """ + url = self.http_client.service_url("/v1/bce/aip/ernievilg/v1/getImgv2") + data = { + "task_id": request.task_id + } + headers = self.http_client.auth_header(request_id) + headers['content-type'] = 'application/json' + if retry != self.http_client.retry.total: + self.http_client.retry.total = retry + response = self.http_client.session.post(url, json=data, headers=headers, timeout=timeout) + self.http_client.check_response_header(response) + data = response.json() + self.http_client.check_response_json(data) + request_id = self.http_client.response_request_id(response) + self.__class__._check_service_error(request_id, data) + response = Text2ImageQueryResponse(**data) + return response, data + + def _extract_img_urls(self, response: Text2ImageQueryResponse): + """ + 从作画生成的返回结果中提取图片url。 + + Args: + response (obj:`Text2ImageQueryResponse`): 作画生成的返回结果。 + + Returns: + List[str]: 从返回体中提取的图片url列表。 + + """ + img_urls = [] + if response and response.data and response.data.sub_task_result_list: + for sub_task_result in response.data.sub_task_result_list: + if sub_task_result and sub_task_result.final_image_list: + for final_image in sub_task_result.final_image_list: + if final_image and final_image.img_url: + img_urls.append(final_image.img_url) + + return img_urls + + @staticmethod + def _check_service_error(request_id: str, data: dict): + """ + 检查服务错误信息 + + Args: + request_id (str): 请求ID + data (dict): 响应数据 + + Raises: + AppBuilderServerException: 如果响应数据中包含错误信息,则抛出异常 + + Returns: + None + """ + if "error_code" in data or "error_msg" in data: + raise AppBuilderServerException( + request_id=request_id, + service_err_code=data.get("error_code"), + service_err_message=data.get("error_msg") + ) \ No newline at end of file diff --git a/python/tests/component_schemas.py b/python/tests/component_schemas.py index 440f5ca94..be69d1586 100644 --- a/python/tests/component_schemas.py +++ b/python/tests/component_schemas.py @@ -89,7 +89,7 @@ file_schema["$schema"] = "file_schema" file_schema["properties"]["type"] = { "type": "string", - "enum": ["file"] + "enum": ["files"] } file_schema["properties"]["text"] = { "type": "object", @@ -108,7 +108,7 @@ url_schema["$schema"] = "url_schema" url_schema["properties"]["type"] = { "type": "string", - "enum": ["url"] + "enum": ["urls"] } url_schema["properties"]["text"] = { "type": "object", @@ -138,7 +138,10 @@ references_schema = copy.deepcopy(base_item_schema) references_schema["$schema"] = "references_schema" -references_schema["properties"]["type"] = "references" +references_schema["properties"]["type"] = { + "type": "string", + "enum": ["references"] +} references_schema["properties"]["text"] = { "type": "object", "properties": { @@ -193,6 +196,10 @@ }, "url": { "type": "string" + }, + "byte": { + "type": "string", + "format": "bytes" } }, "required": ["filename", "url"] @@ -231,6 +238,10 @@ }, "url": { "type": "string" + }, + "byte": { + "type": "string", + "format": "bytes" } }, "required": ["filename", "url"] diff --git a/python/tests/component_tool_eval_cases.py b/python/tests/component_tool_eval_cases.py index 0675eca61..403811c8e 100644 --- a/python/tests/component_tool_eval_cases.py +++ b/python/tests/component_tool_eval_cases.py @@ -50,9 +50,14 @@ def inputs(self): def outputs(self): return {"text": ["熊猫"]} +class Text2ImageCase: + def inputs(self): + return {"query": "生成一张熊猫图片"} + component_tool_eval_cases = { "AnimalRecognition": AnimalRecognitionCase, "ImageUnderstand": ImageUnderstandCase, "ASR": ASRCase, - "TreeMind": TreeMindCase + "TreeMind": TreeMindCase, + "Text2Image": Text2ImageCase } \ No newline at end of file diff --git a/python/tests/component_tool_eval_schemas.py b/python/tests/component_tool_eval_schemas.py index 68c071f30..416de877d 100644 --- a/python/tests/component_tool_eval_schemas.py +++ b/python/tests/component_tool_eval_schemas.py @@ -17,5 +17,6 @@ "AnimalRecognition": [text_schema], "ASR": [text_schema], "TreeMind": [text_schema, url_schema], - "ImageUnderstand": [text_schema] + "ImageUnderstand": [text_schema], + "Text2Image": [url_schema] } \ No newline at end of file diff --git a/python/tests/test_v2_text_to_image.py b/python/tests/test_v2_text_to_image.py new file mode 100644 index 000000000..209c79e90 --- /dev/null +++ b/python/tests/test_v2_text_to_image.py @@ -0,0 +1,71 @@ +# Copyright (c) 2024 Baidu, Inc. 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 os +import unittest +import appbuilder + +from appbuilder.core.components.v2 import Text2Image +from appbuilder.core.component import ComponentOutput + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") +class TestText2Image(unittest.TestCase): + def setUp(self): + """ + 设置环境变量。 + + Args: + None + + Returns: + None. + """ + self.text2Image = Text2Image() + + def test_run(self): + """ + 使用原始文本进行单测 + + Args: + None + + Returns: + None + + """ + inp = appbuilder.Message(content={"prompt": "上海的经典风景"}) + out = self.text2Image.run(inp) + self.assertIsNotNone(out) + self.assertIsInstance(out, appbuilder.Message) + + def test_tool_eval(self): + """ + 测试 tool_eval 方法的正确性。 + + Args: + self: 测试类的实例。 + + Returns: + 无返回值。 + + Raises: + 无异常抛出。 + + """ + result = self.text2Image.tool_eval(query = "上海的经典风景") + for res in result: + self.assertIsInstance(res, ComponentOutput) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file From 4e193ff7f3fb89c5d4595802c3500479baf6b36f Mon Sep 17 00:00:00 2001 From: userpj Date: Mon, 25 Nov 2024 22:11:04 +0800 Subject: [PATCH 33/85] update --- .../appbuilder_client/appbuilder_client.py | 10 ++-- .../console/appbuilder_client/data_class.py | 2 +- python/core/manifest/manifest_decorator.py | 4 +- python/core/manifest/models.py | 4 +- python/tests/test_manifest.py | 50 +++++++++---------- python/tests/test_manifest_decorator.py | 40 +++++++-------- 6 files changed, 55 insertions(+), 55 deletions(-) diff --git a/python/core/console/appbuilder_client/appbuilder_client.py b/python/core/console/appbuilder_client/appbuilder_client.py index b01525423..eac5ecb63 100644 --- a/python/core/console/appbuilder_client/appbuilder_client.py +++ b/python/core/console/appbuilder_client/appbuilder_client.py @@ -251,7 +251,7 @@ def run(self, conversation_id: str, query: str = "", file_ids: list = [], stream: bool = False, - tools: list[Manifest]= None, + tools: list[data_class.Tool|Manifest]= None, tool_outputs: list[data_class.ToolOutput] = None, tool_choice: data_class.ToolChoice = None, end_user_id: str = None, @@ -265,7 +265,7 @@ def run(self, conversation_id: str, conversation_id (str): 唯一会话ID,如需开始新的会话,请使用self.create_conversation创建新的会话 file_ids(list[str]): 文件ID列表 stream (bool): 为True时,流式返回,需要将message.content.answer拼接起来才是完整的回答;为False时,对应非流式返回 - tools(list[data_class.Tools]): 一个Tools组成的列表,其中每个Tools对应一个工具的配置, 默认为None + tools(list[data_class.Tool|Manifest]): Tool组成的列表,其中每个Tools对应一个工具的配置, 默认为None tool_outputs(list[data_class.ToolOutput]): 工具输出列表,格式为list[ToolOutput], ToolOutputd内容为本地的工具执行结果,以自然语言/json dump str描述,默认为None tool_choice(data_class.ToolChoice): 控制大模型使用组件的方式,默认为None end_user_id (str): 用户ID,用于区分不同用户 @@ -320,7 +320,7 @@ def run_with_handler(self, conversation_id: str, query: str = "", file_ids: list = [], - tools: list[data_class.Tool] = None, + tools: list[data_class.Tool|Manifest] = None, stream: bool = False, event_handler=None, action=None, @@ -331,10 +331,10 @@ def run_with_handler(self, conversation_id (str): 唯一会话ID,如需开始新的会话,请使用self.create_conversation创建新的会话 query (str): 查询字符串 file_ids (list): 文件ID列表 - tools(list[data_class.Tools], 可选): 一个Tools组成的列表,其中每个Tools对应一个工具的配置, 默认为None + tools(list[data_class.Tool|Manifest], 可选): 一个Tool组成的列表,其中每个Tool对应一个工具的配置, 默认为None stream (bool): 是否流式响应 event_handler (EventHandler): 事件处理器 - action(dataclass.Action) 对话时要进行的特殊操作。如回复工作流agent中“信息收集节点“的消息。 + action(data_class.Action) 对话时要进行的特殊操作。如回复工作流agent中“信息收集节点“的消息。 kwargs: 其他参数 diff --git a/python/core/console/appbuilder_client/data_class.py b/python/core/console/appbuilder_client/data_class.py index 066964322..25190bd0b 100644 --- a/python/core/console/appbuilder_client/data_class.py +++ b/python/core/console/appbuilder_client/data_class.py @@ -113,7 +113,7 @@ class AppBuilderClientRequest(BaseModel): conversation_id: str file_ids: Optional[list[str]] = None app_id: str - tools: Optional[list[Manifest]] = None + tools: Optional[list[Tool|Manifest]] = None tool_outputs: Optional[list[ToolOutput]] = None tool_choice: Optional[ToolChoice] = None end_user_id: Optional[str] = None diff --git a/python/core/manifest/manifest_decorator.py b/python/core/manifest/manifest_decorator.py index 93fd914ba..53b14246c 100644 --- a/python/core/manifest/manifest_decorator.py +++ b/python/core/manifest/manifest_decorator.py @@ -103,7 +103,7 @@ def decorator(func): # Attach view to function. func.__ab_manifest__ = view - # Compatible to semantic kernel 0.9 + # Compatible to semantic kernel 0.9 ~ 1.6 according to this url: https://github.com/microsoft/semantic-kernel/blob/main/python/semantic_kernel/functions/kernel_function_decorator.py func.__kernel_function__ = True func.__kernel_function_description__ = final_desc func.__kernel_function_name__ = final_name @@ -178,7 +178,7 @@ def decorator(func): # 更新 func.__ab_manifest__.function["parameters"] func.__ab_manifest__.function["parameters"] = parameters_model.model_dump() - # Compatible to semantic kernel 0.9 + # Compatible to semantic kernel 0.9 ~ 1.6 according to this url: https://github.com/microsoft/semantic-kernel/blob/main/python/semantic_kernel/functions/kernel_function_decorator.py if hasattr(func, "__kernel_function_parameters__"): item = { "name": name, diff --git a/python/core/manifest/models.py b/python/core/manifest/models.py index 91a3dca58..b0eb53f52 100644 --- a/python/core/manifest/models.py +++ b/python/core/manifest/models.py @@ -120,7 +120,7 @@ def from_function(cls, func) -> "Manifest": ) # 构造 Manifest 对象 - function_manifest = cls( + manifest = cls( type="function", function={ "name": func.__name__, @@ -129,4 +129,4 @@ def from_function(cls, func) -> "Manifest": }, ) - return function_manifest + return manifest diff --git a/python/tests/test_manifest.py b/python/tests/test_manifest.py index 59542974f..8e20d1bac 100644 --- a/python/tests/test_manifest.py +++ b/python/tests/test_manifest.py @@ -47,15 +47,15 @@ def google_style( """ return "" - function_manifest = appbuilder.Manifest.from_function(google_style) + manifest_from_function = appbuilder.Manifest.from_function(google_style) # 断言顶层的结构 - assert function_manifest.type == "function", "Type does not match 'function'" + assert manifest_from_function.type == "function", "Type does not match 'function'" assert ( - function_manifest.function["name"] == "google_style" + manifest_from_function.function["name"] == "google_style" ), "Function name does not match 'google_style'" assert ( - function_manifest.function["description"] + manifest_from_function.function["description"] == """Google style docstring. Args: @@ -71,7 +71,7 @@ def google_style( ), "Description does not match" # 断言参数结构 - parameters = function_manifest.function["parameters"] + parameters = manifest_from_function.function["parameters"] assert parameters["type"] == "object", "Parameters type does not match 'object'" assert "properties" in parameters, "Properties not found in parameters" @@ -135,14 +135,14 @@ def func( """ return "" - function_manifest = appbuilder.Manifest.from_function(func) + manifest_from_function = appbuilder.Manifest.from_function(func) # 断言顶层的结构 - assert function_manifest.type == "function", "Type does not match 'function'" + assert manifest_from_function.type == "function", "Type does not match 'function'" assert ( - function_manifest.function["name"] == "func" + manifest_from_function.function["name"] == "func" ), "Function name does not match 'func'" assert ( - function_manifest.function["description"] + manifest_from_function.function["description"] == """Google style docstring. Args: @@ -157,7 +157,7 @@ def func( ), "Description does not match" # 断言参数结构 - parameters = function_manifest.function["parameters"] + parameters = manifest_from_function.function["parameters"] assert parameters["type"] == "object", "Parameters type does not match 'object'" assert "properties" in parameters, "Properties not found in parameters" @@ -203,14 +203,14 @@ def func( """ return "" - function_manifest = appbuilder.Manifest.from_function(func) + manifest_from_function = appbuilder.Manifest.from_function(func) # 断言顶层的结构 - assert function_manifest.type == "function", "Type does not match 'function'" + assert manifest_from_function.type == "function", "Type does not match 'function'" assert ( - function_manifest.function["name"] == "func" + manifest_from_function.function["name"] == "func" ), "Function name does not match 'func'" assert ( - function_manifest.function["description"] + manifest_from_function.function["description"] == """Google style docstring. Args: @@ -220,7 +220,7 @@ def func( ), "Description does not match" # 断言参数结构 - parameters = function_manifest.function["parameters"] + parameters = manifest_from_function.function["parameters"] assert parameters["type"] == "object", "Parameters type does not match 'object'" assert "properties" in parameters, "Properties not found in parameters" @@ -237,8 +237,8 @@ def func( # 断言没有["parameters"][1]的参数了 assert not ( - "parameters" in function_manifest.function - and len(function_manifest.function["parameters"]) == 1 + "parameters" in manifest_from_function.function + and len(manifest_from_function.function["parameters"]) == 1 ) def test_no_doc(self): @@ -255,7 +255,7 @@ def func( # 断言这里会抛出缺少文档字符串的 ValueError 异常 try: - function_manifest = appbuilder.Manifest.from_function(func) + manifest_from_function = appbuilder.Manifest.from_function(func) except ValueError as e: assert ( str(e) == "函数 func 缺少文档字符串" @@ -277,20 +277,20 @@ def func( return "" # 获取装饰器生成的 Manifest - function_manifest = func.__ab_manifest__ + manifest_from_function = func.__ab_manifest__ # 断言顶层的结构 - assert function_manifest.type == "function", "Type does not match 'function'" + assert manifest_from_function.type == "function", "Type does not match 'function'" assert ( - function_manifest.function["name"] == "func" + manifest_from_function.function["name"] == "func" ), "Function name does not match 'func'" assert ( - function_manifest.function["description"] + manifest_from_function.function["description"] == "Function with required parameter." ), "Description does not match" # 断言参数结构 - parameters = function_manifest.function["parameters"] + parameters = manifest_from_function.function["parameters"] assert parameters["type"] == "object", "Parameters type does not match 'object'" assert "properties" in parameters, "Properties not found in parameters" @@ -321,8 +321,8 @@ def test_manifest_decorator(self): # 定义示例函数 def get_current_weather(location: str, unit) -> str: return "北京今天25度" - func_manifest = appbuilder.Manifest.from_function(get_current_weather) - assert func_manifest.function.get("description") is not None + manifest_from_function = appbuilder.Manifest.from_function(get_current_weather) + assert manifest_from_function.function.get("description") is not None if __name__ == "__main__": diff --git a/python/tests/test_manifest_decorator.py b/python/tests/test_manifest_decorator.py index 831501b62..f828722c4 100644 --- a/python/tests/test_manifest_decorator.py +++ b/python/tests/test_manifest_decorator.py @@ -23,19 +23,19 @@ def test_disable_docstring(self): def func(param: str) -> str: return param - function_manifest = func.__ab_manifest__ + manifest_from_function = func.__ab_manifest__ # 断言顶层的结构 - assert function_manifest.type == "function", "Type does not match 'function'" + assert manifest_from_function.type == "function", "Type does not match 'function'" assert ( - function_manifest.function["name"] == "func" + manifest_from_function.function["name"] == "func" ), "Function name does not match 'func'" assert ( - function_manifest.function["description"] == "anotated function" + manifest_from_function.function["description"] == "anotated function" ), "Description does not match" # 断言参数结构 - parameters = function_manifest.function["parameters"] + parameters = manifest_from_function.function["parameters"] assert parameters["type"] == "object", "Parameters type does not match 'object'" assert "properties" in parameters, "Properties not found in parameters" @@ -68,15 +68,15 @@ def func(param: str = "[]") -> int: return param # 获取装饰器生成的 Manifest - function_manifest = func.__ab_manifest__ + manifest_from_function = func.__ab_manifest__ # 断言顶层结构 - assert function_manifest.type == "function", "Type does not match 'function'" + assert manifest_from_function.type == "function", "Type does not match 'function'" assert ( - function_manifest.function["name"] == "func" + manifest_from_function.function["name"] == "func" ), "Function name does not match 'func'" assert ( - function_manifest.function["description"] + manifest_from_function.function["description"] == """An example function. Args: @@ -88,7 +88,7 @@ def func(param: str = "[]") -> int: ), "Description does not match" # 断言参数结构 - parameters = function_manifest.function["parameters"] + parameters = manifest_from_function.function["parameters"] assert parameters["type"] == "object", "Parameters type does not match 'object'" assert "properties" in parameters, "Properties not found in parameters" @@ -122,15 +122,15 @@ def func(param: str = "[]") -> int: return param # 获取装饰器生成的 Manifest - function_manifest = func.__ab_manifest__ + manifest_from_function = func.__ab_manifest__ # 断言顶层结构 - assert function_manifest.type == "function", "Type does not match 'function'" + assert manifest_from_function.type == "function", "Type does not match 'function'" assert ( - function_manifest.function["name"] == "func" + manifest_from_function.function["name"] == "func" ), "Function name does not match 'func'" assert ( - function_manifest.function["description"] + manifest_from_function.function["description"] == """An example function. Args: @@ -142,7 +142,7 @@ def func(param: str = "[]") -> int: ), "Description does not match" # 断言参数结构 - parameters = function_manifest.function["parameters"] + parameters = manifest_from_function.function["parameters"] assert parameters["type"] == "object", "Parameters type does not match 'object'" assert "properties" in parameters, "Properties not found in parameters" @@ -166,19 +166,19 @@ def func(param: str = "[]") -> int: view = func.__ab_manifest__ # 获取装饰器生成的 Manifest - function_manifest = func.__ab_manifest__ + manifest_from_function = func.__ab_manifest__ # 断言顶层结构 - assert function_manifest.type == "function", "Type does not match 'function'" + assert manifest_from_function.type == "function", "Type does not match 'function'" assert ( - function_manifest.function["name"] == "func" + manifest_from_function.function["name"] == "func" ), "Function name does not match 'func'" assert ( - function_manifest.function["description"] == " " + manifest_from_function.function["description"] == " " ), "Description should be None when not explicitly provided" # 断言参数结构 - parameters = function_manifest.function["parameters"] + parameters = manifest_from_function.function["parameters"] assert parameters["type"] == "object", "Parameters type does not match 'object'" assert "properties" in parameters, "Properties not found in parameters" From 270fbcea3a8b669b25d604f512d40fa74763b324 Mon Sep 17 00:00:00 2001 From: userpj Date: Mon, 25 Nov 2024 22:13:36 +0800 Subject: [PATCH 34/85] update --- python/core/console/appbuilder_client/appbuilder_client.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/core/console/appbuilder_client/appbuilder_client.py b/python/core/console/appbuilder_client/appbuilder_client.py index eac5ecb63..d5d9681e5 100644 --- a/python/core/console/appbuilder_client/appbuilder_client.py +++ b/python/core/console/appbuilder_client/appbuilder_client.py @@ -265,7 +265,7 @@ def run(self, conversation_id: str, conversation_id (str): 唯一会话ID,如需开始新的会话,请使用self.create_conversation创建新的会话 file_ids(list[str]): 文件ID列表 stream (bool): 为True时,流式返回,需要将message.content.answer拼接起来才是完整的回答;为False时,对应非流式返回 - tools(list[data_class.Tool|Manifest]): Tool组成的列表,其中每个Tools对应一个工具的配置, 默认为None + tools(list[data_class.Tool|Manifest]): 一个Tool或Manifest组成的列表,其中每个Tool(Manifest)对应一个工具的配置, 默认为None tool_outputs(list[data_class.ToolOutput]): 工具输出列表,格式为list[ToolOutput], ToolOutputd内容为本地的工具执行结果,以自然语言/json dump str描述,默认为None tool_choice(data_class.ToolChoice): 控制大模型使用组件的方式,默认为None end_user_id (str): 用户ID,用于区分不同用户 @@ -331,7 +331,7 @@ def run_with_handler(self, conversation_id (str): 唯一会话ID,如需开始新的会话,请使用self.create_conversation创建新的会话 query (str): 查询字符串 file_ids (list): 文件ID列表 - tools(list[data_class.Tool|Manifest], 可选): 一个Tool组成的列表,其中每个Tool对应一个工具的配置, 默认为None + tools(list[data_class.Tool|Manifest], 可选): 一个Tool或Manifest组成的列表,其中每个Tool(Manifest)对应一个工具的配置, 默认为None stream (bool): 是否流式响应 event_handler (EventHandler): 事件处理器 action(data_class.Action) 对话时要进行的特殊操作。如回复工作流agent中“信息收集节点“的消息。 @@ -370,7 +370,7 @@ def run_multiple_dialog_with_handler(self, conversation_id (str): 唯一会话ID,如需开始新的会话,请使用self.create_conversation创建新的会话 queries (iter): 查询字符串可迭代对象 file_ids (iter): 文件ID列表 - tools(iter, 可选): 一个Tools组成的列表,其中每个Tools对应一个工具的配置, 默认为None + tools(iter, 可选): 一个Tool或Manifest组成的列表,其中每个Tool(Manifest)对应一个工具的配置, 默认为None stream (bool): 是否流式响应 event_handler (EventHandler): 事件处理器 actions(iter) 对话时要进行的特殊操作。如回复工作流agent中“信息收集节点“的消息。 From 61c102294fde7b9cb740147119aca42e1e6747f8 Mon Sep 17 00:00:00 2001 From: userpj Date: Mon, 25 Nov 2024 22:22:22 +0800 Subject: [PATCH 35/85] update --- .../console/appbuilder_client/appbuilder_client.py | 10 +++++----- python/core/console/appbuilder_client/data_class.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/python/core/console/appbuilder_client/appbuilder_client.py b/python/core/console/appbuilder_client/appbuilder_client.py index d5d9681e5..4814f41d4 100644 --- a/python/core/console/appbuilder_client/appbuilder_client.py +++ b/python/core/console/appbuilder_client/appbuilder_client.py @@ -17,7 +17,7 @@ import json import uuid import queue -from typing import Optional +from typing import Optional,Union from appbuilder.core.component import Message, Component from appbuilder.core.manifest.models import Manifest from appbuilder.core.console.appbuilder_client import data_class @@ -251,7 +251,7 @@ def run(self, conversation_id: str, query: str = "", file_ids: list = [], stream: bool = False, - tools: list[data_class.Tool|Manifest]= None, + tools: list[Union[data_class.Tool,Manifest]]= None, tool_outputs: list[data_class.ToolOutput] = None, tool_choice: data_class.ToolChoice = None, end_user_id: str = None, @@ -265,7 +265,7 @@ def run(self, conversation_id: str, conversation_id (str): 唯一会话ID,如需开始新的会话,请使用self.create_conversation创建新的会话 file_ids(list[str]): 文件ID列表 stream (bool): 为True时,流式返回,需要将message.content.answer拼接起来才是完整的回答;为False时,对应非流式返回 - tools(list[data_class.Tool|Manifest]): 一个Tool或Manifest组成的列表,其中每个Tool(Manifest)对应一个工具的配置, 默认为None + tools(list[Union[data_class.Tool,Manifest]]): 一个Tool或Manifest组成的列表,其中每个Tool(Manifest)对应一个工具的配置, 默认为None tool_outputs(list[data_class.ToolOutput]): 工具输出列表,格式为list[ToolOutput], ToolOutputd内容为本地的工具执行结果,以自然语言/json dump str描述,默认为None tool_choice(data_class.ToolChoice): 控制大模型使用组件的方式,默认为None end_user_id (str): 用户ID,用于区分不同用户 @@ -320,7 +320,7 @@ def run_with_handler(self, conversation_id: str, query: str = "", file_ids: list = [], - tools: list[data_class.Tool|Manifest] = None, + tools: list[Union[data_class.Tool,Manifest]] = None, stream: bool = False, event_handler=None, action=None, @@ -331,7 +331,7 @@ def run_with_handler(self, conversation_id (str): 唯一会话ID,如需开始新的会话,请使用self.create_conversation创建新的会话 query (str): 查询字符串 file_ids (list): 文件ID列表 - tools(list[data_class.Tool|Manifest], 可选): 一个Tool或Manifest组成的列表,其中每个Tool(Manifest)对应一个工具的配置, 默认为None + tools(list[Union[data_class.Tool,Manifest]], 可选): 一个Tool或Manifest组成的列表,其中每个Tool(Manifest)对应一个工具的配置, 默认为None stream (bool): 是否流式响应 event_handler (EventHandler): 事件处理器 action(data_class.Action) 对话时要进行的特殊操作。如回复工作流agent中“信息收集节点“的消息。 diff --git a/python/core/console/appbuilder_client/data_class.py b/python/core/console/appbuilder_client/data_class.py index 25190bd0b..a5c70ddc8 100644 --- a/python/core/console/appbuilder_client/data_class.py +++ b/python/core/console/appbuilder_client/data_class.py @@ -113,7 +113,7 @@ class AppBuilderClientRequest(BaseModel): conversation_id: str file_ids: Optional[list[str]] = None app_id: str - tools: Optional[list[Tool|Manifest]] = None + tools: Optional[list[Union[Tool,Manifest]]] = None tool_outputs: Optional[list[ToolOutput]] = None tool_choice: Optional[ToolChoice] = None end_user_id: Optional[str] = None From eaabe805ff7a191517509a0e7e9edc53cfcadae2 Mon Sep 17 00:00:00 2001 From: C9luster <138663536+C9luster@users.noreply.github.com> Date: Tue, 26 Nov 2024 10:28:34 +0800 Subject: [PATCH 36/85] =?UTF-8?q?=E6=9B=B4=E6=96=B0SDK=E5=8D=95=E5=85=83?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E9=80=BB=E8=BE=91=20(#615)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 更新SDK单元测试逻辑 * 更新单测脚本 * 删除串行运行装饰器 --------- Co-authored-by: yinjiaqi --- python/tests/parallel_ut_run.py | 85 ++----------------- python/tests/test_agent.py | 2 - python/tests/test_animal_recognize.py | 1 - .../tests/test_appbuilder_assistant_trace.py | 1 - .../tests/test_appbuilder_client_chatflow.py | 1 - ...ppbuilder_client_chatflow_event_handler.py | 1 - ...uilder_client_chatflow_event_handler_v2.py | 1 - python/tests/test_appbuilder_client_trace.py | 1 - .../tests/test_appbuilder_components_trace.py | 1 - ...st_appbuilder_core_components_retriever.py | 1 - .../tests/test_appbuilder_sentry_trace_off.py | 1 - .../tests/test_appbuilder_sentry_trace_on.py | 1 - .../test_appbuilder_trace_raise_error.py | 1 - python/tests/test_assistant_basic_import.py | 1 - .../tests/test_assistant_class_assistans.py | 1 - python/tests/test_assistant_class_files.py | 1 - python/tests/test_assistant_class_messages.py | 1 - python/tests/test_assistant_class_runs.py | 1 - python/tests/test_assistant_class_runs_v2.py | 6 +- python/tests/test_assistant_class_threads.py | 1 - python/tests/test_assistant_e2e_funccall.py | 1 - .../test_assistant_e2e_funccall_component.py | 1 - python/tests/test_assistant_e2e_run.py | 1 - .../tests/test_assistant_e2e_stream_cancel.py | 1 - ...test_assistant_e2e_stream_event_handler.py | 1 - ...t_assistant_e2e_stream_event_handler_v2.py | 1 - .../test_assistant_e2e_stream_funccall.py | 1 - python/tests/test_assistant_e2e_stream_run.py | 1 - python/tests/test_console_rag.py | 1 - python/tests/test_core_client.py | 1 - python/tests/test_dialog_summary.py | 1 - python/tests/test_doc_crop_enhance.py | 1 - python/tests/test_gbi_nl2sql.py | 1 - python/tests/test_gbi_select_table.py | 1 - python/tests/test_hallucination_detection.py | 1 - python/tests/test_knowledge_base.py | 1 - python/tests/test_matching.py | 1 - python/tests/test_mrc.py | 1 - python/tests/test_nl2pandas.py | 1 - python/tests/test_oral_query_generation.py | 1 - python/tests/test_qa_llm_excel2figure.py | 1 - python/tests/test_qa_pair_mining.py | 1 - python/tests/test_qrcode_ocr.py | 1 - python/tests/test_rerank.py | 1 - python/tests/test_similar_question.py | 1 - python/tests/test_style_rewrite.py | 1 - python/tests/test_style_writing.py | 1 - python/tests/test_table_ocr.py | 1 - python/tests/test_trace_skip_raise_error.py | 1 - python/tests/test_treemind.py | 1 - 50 files changed, 7 insertions(+), 133 deletions(-) diff --git a/python/tests/parallel_ut_run.py b/python/tests/parallel_ut_run.py index dec626016..c4f769f53 100644 --- a/python/tests/parallel_ut_run.py +++ b/python/tests/parallel_ut_run.py @@ -50,9 +50,6 @@ # 可以CPU并行的单测用例 CPU_PARALLEL_RUN_UNITTEST = [] -# CPU上仅能串行执行的单测用例 -CPU_SERIAL_RUN_UNITTEST = [] - # 分类未知,故在CPU上串行执行的单测用例 UNKNOWN_UNITTEST = [] @@ -69,7 +66,6 @@ def choose_test_case(file): """ skip_case_str = '@unittest.skip(' cpu_parallel_str = '@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL"' - cpu_serial_str = '@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL"' with open(file, 'r') as f: all_line = f.readlines() @@ -82,10 +78,6 @@ def choose_test_case(file): CPU_PARALLEL_RUN_UNITTEST.append(file.split("/")[-1]) return - if list(set([line.strip().find(cpu_serial_str) for line in all_line])) != [-1]: - CPU_SERIAL_RUN_UNITTEST.append(file.split("/")[-1]) - return - UNKNOWN_UNITTEST.append(file.split("/")[-1]) return @@ -112,17 +104,11 @@ def get_all_unittest_file(): for idx, case in enumerate(SKIP_UNITTEST): logger.info("--> {}. {}".format(idx+1, case)) - logger.info( - "\nCPU并行的单测用例:{}个".format(len(CPU_PARALLEL_RUN_UNITTEST))) + logger.info("\nCPU并行的单测用例:{}个".format(len(CPU_PARALLEL_RUN_UNITTEST))) for idx, case in enumerate(CPU_PARALLEL_RUN_UNITTEST): logger.info("--> {}. {}".format(idx+1, case)) - logger.info( - "\nCPU串行执行的单测用例:{}个".format(len(CPU_SERIAL_RUN_UNITTEST))) - for idx, case in enumerate(CPU_SERIAL_RUN_UNITTEST): - logger.info("--> {}. {}".format(idx+1, case)) - - logger.info("\n运行模式未知,串行执行的单测用例:{}个".format(len(UNKNOWN_UNITTEST))) + logger.info("\nCPU串行执行的单测用例:{}个".format(len(UNKNOWN_UNITTEST))) for idx, case in enumerate(UNKNOWN_UNITTEST): logger.info("--> {}. {}".format(idx+1, case)) @@ -278,31 +264,18 @@ def run_cpu_parallel_unittest(): return success_cases, failed_cases, end_time - begin_time - - -def run_cpu_serial_unittest(): - """ - 运行CPU_SERIAL模式下的单元测试,包括并行和串行两种方式,记录并打印成功和失败的情况及耗时 - - Args: - 无 - - Returns: - success_cases (list): 成功运行的测试用例列表 - failed_cases (list): 失败运行的测试用例列表 - total_time (float): 运行总耗时(单位:秒) - """ +def run_unknown_unittest(): os.environ["TEST_CASE"] = "CPU_SERIAL" logger.info("\n================ CPU_SERIAL ================\n") begin_time = time.time() success_cases, failed_cases, total_case_time = parallel_execute_unittest( - CPU_SERIAL_RUN_UNITTEST, 1) + UNKNOWN_UNITTEST, 1) logger.info("\n CPU_SERIAL 运行成功单测:{} 个".format(len(success_cases))) if len(failed_cases) > 0: - logger.info("\n以下单测失败,将重试运行 2 次") + logger.info("\n以下单测失败,将重试运行一次") for case in failed_cases: logger.info("retry case --> {}".format(case)) retry_success_cases, retry_failed_cases, retry_case_time = parallel_execute_unittest( @@ -313,16 +286,6 @@ def run_cpu_serial_unittest(): for success in retry_success_cases: failed_cases.remove(success) - if len(retry_failed_cases) > 0: - logger.info("\n以下单测失败,将重试运行 1 次") - for case in retry_failed_cases: - logger.info("retry case --> {}".format(case)) - second_success_cases, second_failed_cases, second_case_time = parallel_execute_unittest( - retry_failed_cases, 1) - total_case_time += second_case_time - for success in second_success_cases: - failed_cases.remove(success) - end_time = time.time() logger.info("\n CPU_SERIAL 运行失败单测: {} 个".format(len(failed_cases))) for failed in failed_cases: @@ -336,41 +299,6 @@ def run_cpu_serial_unittest(): return success_cases, failed_cases, end_time - begin_time -def run_unknown_unittest(): - os.environ["TEST_CASE"] = "UNKNOWN" - logger.info("\n================ UNKNOWN ================\n") - - begin_time = time.time() - success_cases, failed_cases, total_case_time = parallel_execute_unittest( - UNKNOWN_UNITTEST, 2) - - logger.info("\n UNKNOWN 运行成功单测:{} 个".format(len(success_cases))) - - if len(failed_cases) > 0: - logger.info("\n以下单测失败,将重试运行一次") - for case in failed_cases: - logger.info("retry case --> {}".format(case)) - retry_success_cases, retry_failed_cases, retry_case_time = parallel_execute_unittest( - failed_cases, 1) - - total_case_time += retry_case_time - - for success in retry_success_cases: - failed_cases.remove(success) - - end_time = time.time() - logger.info("\n UNKNOWN 运行失败单测: {} 个".format(len(failed_cases))) - for failed in failed_cases: - logger.info("--> {}".format(failed)) - - logger.info("\n UNKNOWN 单测并行运行总计耗时 {} s".format( - end_time - begin_time)) - logger.info("\n UNKNOWN 单测串行运行总计耗时 {} s".format( - total_case_time)) - - return success_cases, failed_cases, end_time - begin_time - - def create_unittest_report(): """ 生成单元测试报告。 @@ -391,8 +319,7 @@ def create_unittest_report(): total_failed_cases = [] total_ut_time = 0 - test_suite = [run_cpu_parallel_unittest, - run_cpu_serial_unittest, run_unknown_unittest] + test_suite = [run_cpu_parallel_unittest, run_unknown_unittest] for suite in test_suite: success_cases, failed_cases, suite_time = suite() total_success_cases += success_cases diff --git a/python/tests/test_agent.py b/python/tests/test_agent.py index 0cde85ad8..a922aed91 100644 --- a/python/tests/test_agent.py +++ b/python/tests/test_agent.py @@ -14,8 +14,6 @@ AppBuilderClient ) - -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestAgentRuntime(unittest.TestCase): def setUp(self): """ diff --git a/python/tests/test_animal_recognize.py b/python/tests/test_animal_recognize.py index 77f050986..751c6e83d 100644 --- a/python/tests/test_animal_recognize.py +++ b/python/tests/test_animal_recognize.py @@ -17,7 +17,6 @@ import appbuilder import os -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestAnimalRecognition(unittest.TestCase): def setUp(self): """ diff --git a/python/tests/test_appbuilder_assistant_trace.py b/python/tests/test_appbuilder_assistant_trace.py index 775e26261..cd599bd54 100644 --- a/python/tests/test_appbuilder_assistant_trace.py +++ b/python/tests/test_appbuilder_assistant_trace.py @@ -75,7 +75,6 @@ def tool_calls(self, status_event): ] ) -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestAppBuilderTrace(unittest.TestCase): def setUp(self): """ diff --git a/python/tests/test_appbuilder_client_chatflow.py b/python/tests/test_appbuilder_client_chatflow.py index c62a4d60f..534ad8c33 100644 --- a/python/tests/test_appbuilder_client_chatflow.py +++ b/python/tests/test_appbuilder_client_chatflow.py @@ -18,7 +18,6 @@ from appbuilder.core.console.appbuilder_client import data_class -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestAppBuilderClientChatflow(unittest.TestCase): def setUp(self): """ diff --git a/python/tests/test_appbuilder_client_chatflow_event_handler.py b/python/tests/test_appbuilder_client_chatflow_event_handler.py index db82da1ee..00c5e7a5e 100644 --- a/python/tests/test_appbuilder_client_chatflow_event_handler.py +++ b/python/tests/test_appbuilder_client_chatflow_event_handler.py @@ -49,7 +49,6 @@ def run(self, query=None): ) -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestAppBuilderClientChatflow(unittest.TestCase): def setUp(self): """ diff --git a/python/tests/test_appbuilder_client_chatflow_event_handler_v2.py b/python/tests/test_appbuilder_client_chatflow_event_handler_v2.py index 3ca9cb52b..c2f845976 100644 --- a/python/tests/test_appbuilder_client_chatflow_event_handler_v2.py +++ b/python/tests/test_appbuilder_client_chatflow_event_handler_v2.py @@ -47,7 +47,6 @@ def gen_action(self): yield self._create_action() -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestAppBuilderClientChatflow(unittest.TestCase): def setUp(self): """ diff --git a/python/tests/test_appbuilder_client_trace.py b/python/tests/test_appbuilder_client_trace.py index c92110b27..51566885b 100644 --- a/python/tests/test_appbuilder_client_trace.py +++ b/python/tests/test_appbuilder_client_trace.py @@ -20,7 +20,6 @@ from appbuilder.utils.trace.phoenix_wrapper import runtime_main,stop_phoenix,launch_phoenix from appbuilder.core.console.appbuilder_client import get_app_list -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestAppBuilderTrace(unittest.TestCase): def setUp(self): """ diff --git a/python/tests/test_appbuilder_components_trace.py b/python/tests/test_appbuilder_components_trace.py index 494651923..87bc4ef8e 100644 --- a/python/tests/test_appbuilder_components_trace.py +++ b/python/tests/test_appbuilder_components_trace.py @@ -37,7 +37,6 @@ '时候也需特别注意火侯,这样吃起来才会有外脆内Q的口感!') TEST_ANSWER = '澳门新麻蒲烤肉店并不是每天开门,周日休息。' -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestAppBuilderComponentsTrace(unittest.TestCase): def setUp(self): """ diff --git a/python/tests/test_appbuilder_core_components_retriever.py b/python/tests/test_appbuilder_core_components_retriever.py index 84b330ff4..84d3074c3 100644 --- a/python/tests/test_appbuilder_core_components_retriever.py +++ b/python/tests/test_appbuilder_core_components_retriever.py @@ -20,7 +20,6 @@ from appbuilder.core.components.retriever.baidu_vdb.component import _try_import,BaiduVDBVectorStoreIndex -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestAppbuilderCoreComponentsRetriever__try_import(unittest.TestCase): def test_baidu_vdb_baiduvdb_retriever_try_import(self): subprocess.check_call([sys.executable, "-m", "pip", "uninstall", "-y", "pymochow"]) diff --git a/python/tests/test_appbuilder_sentry_trace_off.py b/python/tests/test_appbuilder_sentry_trace_off.py index 80d30dab5..ac7111a0d 100644 --- a/python/tests/test_appbuilder_sentry_trace_off.py +++ b/python/tests/test_appbuilder_sentry_trace_off.py @@ -25,7 +25,6 @@ logging.basicConfig(level=logging.INFO) -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestAppbuilderForSentryOff(unittest.TestCase): def test_sentry_inport_error(self): diff --git a/python/tests/test_appbuilder_sentry_trace_on.py b/python/tests/test_appbuilder_sentry_trace_on.py index 48b66ce12..1a426aa24 100644 --- a/python/tests/test_appbuilder_sentry_trace_on.py +++ b/python/tests/test_appbuilder_sentry_trace_on.py @@ -25,7 +25,6 @@ logging.basicConfig(level=logging.INFO) -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestAppbuilderForSentryOff(unittest.TestCase): def test_sentry_normal(self): """ diff --git a/python/tests/test_appbuilder_trace_raise_error.py b/python/tests/test_appbuilder_trace_raise_error.py index 9b3d13e75..e611b5c2d 100644 --- a/python/tests/test_appbuilder_trace_raise_error.py +++ b/python/tests/test_appbuilder_trace_raise_error.py @@ -19,7 +19,6 @@ from appbuilder.utils.trace._function import _input,_client_trace_generator,_assistant_stream_run_with_handler_output -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestAppbuilderTraceRaiseError(unittest.TestCase): def setUp(self): tracer_provider = trace.get_tracer_provider() diff --git a/python/tests/test_assistant_basic_import.py b/python/tests/test_assistant_basic_import.py index b74f12a8a..31440a40c 100644 --- a/python/tests/test_assistant_basic_import.py +++ b/python/tests/test_assistant_basic_import.py @@ -2,7 +2,6 @@ import os import appbuilder -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestAssistantImport(unittest.TestCase): def setUp(self): os.environ["APPBUILDER_TOKEN"] = os.environ["APPBUILDER_TOKEN_V2"] diff --git a/python/tests/test_assistant_class_assistans.py b/python/tests/test_assistant_class_assistans.py index bd967f0b7..de4bb1ae9 100644 --- a/python/tests/test_assistant_class_assistans.py +++ b/python/tests/test_assistant_class_assistans.py @@ -3,7 +3,6 @@ import appbuilder from tests.pytest_utils import Utils -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestAssistant(unittest.TestCase): def setUp(self): os.environ["APPBUILDER_TOKEN"] = os.environ["APPBUILDER_TOKEN_V2"] diff --git a/python/tests/test_assistant_class_files.py b/python/tests/test_assistant_class_files.py index 91547c05c..9c75cf80f 100644 --- a/python/tests/test_assistant_class_files.py +++ b/python/tests/test_assistant_class_files.py @@ -5,7 +5,6 @@ from appbuilder.core._exception import AssistantServerException from tests.pytest_utils import Utils -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestFilesCreate(unittest.TestCase): def setUp(self): os.environ["APPBUILDER_TOKEN"] = os.environ["APPBUILDER_TOKEN_V2"] diff --git a/python/tests/test_assistant_class_messages.py b/python/tests/test_assistant_class_messages.py index 44bd5e926..892265f16 100644 --- a/python/tests/test_assistant_class_messages.py +++ b/python/tests/test_assistant_class_messages.py @@ -4,7 +4,6 @@ from appbuilder.core._exception import AssistantServerException -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestMessageCreate(unittest.TestCase): def setUp(self): os.environ["APPBUILDER_TOKEN"] = os.environ["APPBUILDER_TOKEN_V2"] diff --git a/python/tests/test_assistant_class_runs.py b/python/tests/test_assistant_class_runs.py index af2aba9a9..da19f1a11 100644 --- a/python/tests/test_assistant_class_runs.py +++ b/python/tests/test_assistant_class_runs.py @@ -34,7 +34,6 @@ def get_data_file(filename): def get_cur_whether(location:str, unit:str): return "{} 的当前温度是30 {}".format(location, unit) -# @unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestFunctionCall(unittest.TestCase): def setUp(self): os.environ["APPBUILDER_TOKEN"] = os.environ["APPBUILDER_TOKEN_V2"] diff --git a/python/tests/test_assistant_class_runs_v2.py b/python/tests/test_assistant_class_runs_v2.py index 784a6ce23..b4266d243 100644 --- a/python/tests/test_assistant_class_runs_v2.py +++ b/python/tests/test_assistant_class_runs_v2.py @@ -2,7 +2,7 @@ import os import appbuilder -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") + def get_cur_whether(location:str, unit:str): return "{} 的当前温度是30 {}".format(location, unit) @@ -61,10 +61,6 @@ def test_run_step_list_v1(self): step_id=last_step_id, ) self.assertEqual(step.id, last_step_id) - - - - if __name__ == '__main__': diff --git a/python/tests/test_assistant_class_threads.py b/python/tests/test_assistant_class_threads.py index d070487da..f90c386c4 100644 --- a/python/tests/test_assistant_class_threads.py +++ b/python/tests/test_assistant_class_threads.py @@ -2,7 +2,6 @@ import os import appbuilder -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestThreadCreate(unittest.TestCase): def setUp(self): os.environ["APPBUILDER_TOKEN"] = os.environ["APPBUILDER_TOKEN_V2"] diff --git a/python/tests/test_assistant_e2e_funccall.py b/python/tests/test_assistant_e2e_funccall.py index 0d9a22334..90018df2c 100644 --- a/python/tests/test_assistant_e2e_funccall.py +++ b/python/tests/test_assistant_e2e_funccall.py @@ -30,7 +30,6 @@ def get_cur_whether(location:str, unit:str): } } -# @unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") @unittest.skip(reason="暂时跳过") class TestFunctionCall(unittest.TestCase): def setUp(self): diff --git a/python/tests/test_assistant_e2e_funccall_component.py b/python/tests/test_assistant_e2e_funccall_component.py index dd26eb20d..1777ab7f5 100644 --- a/python/tests/test_assistant_e2e_funccall_component.py +++ b/python/tests/test_assistant_e2e_funccall_component.py @@ -3,7 +3,6 @@ import appbuilder -# @unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") @unittest.skip(reason="暂时跳过") class TestFunctionCall(unittest.TestCase): def setUp(self): diff --git a/python/tests/test_assistant_e2e_run.py b/python/tests/test_assistant_e2e_run.py index 30c5d75f7..11359ec97 100644 --- a/python/tests/test_assistant_e2e_run.py +++ b/python/tests/test_assistant_e2e_run.py @@ -3,7 +3,6 @@ import os import appbuilder -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestAssistantTalk(unittest.TestCase): def setUp(self): os.environ["APPBUILDER_TOKEN"] = os.environ["APPBUILDER_TOKEN_V2"] diff --git a/python/tests/test_assistant_e2e_stream_cancel.py b/python/tests/test_assistant_e2e_stream_cancel.py index 0d3ae6b36..a1c0737fb 100644 --- a/python/tests/test_assistant_e2e_stream_cancel.py +++ b/python/tests/test_assistant_e2e_stream_cancel.py @@ -31,7 +31,6 @@ def get_cur_whether(location:str, unit:str): } } -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestCancel(unittest.TestCase): def setUp(self): os.environ["APPBUILDER_TOKEN"] = os.environ["APPBUILDER_TOKEN_V2"] diff --git a/python/tests/test_assistant_e2e_stream_event_handler.py b/python/tests/test_assistant_e2e_stream_event_handler.py index 28d383871..fddb99789 100644 --- a/python/tests/test_assistant_e2e_stream_event_handler.py +++ b/python/tests/test_assistant_e2e_stream_event_handler.py @@ -56,7 +56,6 @@ def tool_calls(self, status_event): ) -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestFunctionCall(unittest.TestCase): def setUp(self): os.environ["APPBUILDER_TOKEN"] = os.environ["APPBUILDER_TOKEN_V2"] diff --git a/python/tests/test_assistant_e2e_stream_event_handler_v2.py b/python/tests/test_assistant_e2e_stream_event_handler_v2.py index bd74619f7..0fefbb868 100644 --- a/python/tests/test_assistant_e2e_stream_event_handler_v2.py +++ b/python/tests/test_assistant_e2e_stream_event_handler_v2.py @@ -63,7 +63,6 @@ def tool_calls(self, status_event): ) -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestFunctionCall(unittest.TestCase): def setUp(self): os.environ["APPBUILDER_TOKEN"] = os.environ["APPBUILDER_TOKEN_V2"] diff --git a/python/tests/test_assistant_e2e_stream_funccall.py b/python/tests/test_assistant_e2e_stream_funccall.py index 7b4c27586..efef8261e 100644 --- a/python/tests/test_assistant_e2e_stream_funccall.py +++ b/python/tests/test_assistant_e2e_stream_funccall.py @@ -30,7 +30,6 @@ def get_cur_whether(location:str, unit:str): } } -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestFunctionCall(unittest.TestCase): def setUp(self): os.environ["APPBUILDER_TOKEN"] = os.environ["APPBUILDER_TOKEN_V2"] diff --git a/python/tests/test_assistant_e2e_stream_run.py b/python/tests/test_assistant_e2e_stream_run.py index d7be0f9da..abb164989 100644 --- a/python/tests/test_assistant_e2e_stream_run.py +++ b/python/tests/test_assistant_e2e_stream_run.py @@ -4,7 +4,6 @@ import time import appbuilder -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestAssistantStreamTalk(unittest.TestCase): def setUp(self): os.environ["APPBUILDER_TOKEN"] = os.environ["APPBUILDER_TOKEN_V2"] diff --git a/python/tests/test_console_rag.py b/python/tests/test_console_rag.py index 5874f6270..09d2b9aca 100644 --- a/python/tests/test_console_rag.py +++ b/python/tests/test_console_rag.py @@ -17,7 +17,6 @@ from appbuilder.core.console.rag.rag import RAG -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestRag(unittest.TestCase): def setUp(self): diff --git a/python/tests/test_core_client.py b/python/tests/test_core_client.py index c35c4c3b3..1cd654408 100644 --- a/python/tests/test_core_client.py +++ b/python/tests/test_core_client.py @@ -28,7 +28,6 @@ def __init__(self, status_code, headers, text): def json(self): return json.loads(self.text) -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestCoreClient(unittest.TestCase): def setUp(self): # 保存原始环境变量 diff --git a/python/tests/test_dialog_summary.py b/python/tests/test_dialog_summary.py index 9362fa2ee..7c7aac454 100644 --- a/python/tests/test_dialog_summary.py +++ b/python/tests/test_dialog_summary.py @@ -16,7 +16,6 @@ import unittest import appbuilder -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestDialogSummary(unittest.TestCase): def setUp(self): """ diff --git a/python/tests/test_doc_crop_enhance.py b/python/tests/test_doc_crop_enhance.py index 8cb8d1822..864810952 100644 --- a/python/tests/test_doc_crop_enhance.py +++ b/python/tests/test_doc_crop_enhance.py @@ -16,7 +16,6 @@ import requests import appbuilder import os -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestDocCropEnhance(unittest.TestCase): def setUp(self): """ diff --git a/python/tests/test_gbi_nl2sql.py b/python/tests/test_gbi_nl2sql.py index 65a2dca21..3c9031f92 100644 --- a/python/tests/test_gbi_nl2sql.py +++ b/python/tests/test_gbi_nl2sql.py @@ -69,7 +69,6 @@ 当前问题:"{query}" 回答: """ -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestGBINL2Sql(unittest.TestCase): def setUp(self): diff --git a/python/tests/test_gbi_select_table.py b/python/tests/test_gbi_select_table.py index ed3477583..1ef8cb12d 100644 --- a/python/tests/test_gbi_select_table.py +++ b/python/tests/test_gbi_select_table.py @@ -68,7 +68,6 @@ 问题:{query} 回答: """ -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestGBISelectTable(unittest.TestCase): def setUp(self): diff --git a/python/tests/test_hallucination_detection.py b/python/tests/test_hallucination_detection.py index dec2f82cd..871159fac 100644 --- a/python/tests/test_hallucination_detection.py +++ b/python/tests/test_hallucination_detection.py @@ -38,7 +38,6 @@ TEST_ANSWER = '澳门新麻蒲烤肉店并不是每天开门,周日休息。' -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestHallucinationDetectionComponent(unittest.TestCase): def setUp(self): """ diff --git a/python/tests/test_knowledge_base.py b/python/tests/test_knowledge_base.py index 81e2777bf..6a7702231 100644 --- a/python/tests/test_knowledge_base.py +++ b/python/tests/test_knowledge_base.py @@ -17,7 +17,6 @@ from appbuilder.core._exception import BadRequestException -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestKnowLedge(unittest.TestCase): def setUp(self): self.whether_create_knowledge_base = False diff --git a/python/tests/test_matching.py b/python/tests/test_matching.py index a65da16f9..a65af315f 100644 --- a/python/tests/test_matching.py +++ b/python/tests/test_matching.py @@ -10,7 +10,6 @@ import os import appbuilder -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestMatching(unittest.TestCase): def test_example(self): diff --git a/python/tests/test_mrc.py b/python/tests/test_mrc.py index bcf36f89c..10376e6ad 100644 --- a/python/tests/test_mrc.py +++ b/python/tests/test_mrc.py @@ -16,7 +16,6 @@ import appbuilder import time -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestMRC(unittest.TestCase): def setUp(self): ''' diff --git a/python/tests/test_nl2pandas.py b/python/tests/test_nl2pandas.py index adde549a8..8d7ebd147 100644 --- a/python/tests/test_nl2pandas.py +++ b/python/tests/test_nl2pandas.py @@ -19,7 +19,6 @@ from appbuilder.core.message import Message -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestNl2pandasComponent(unittest.TestCase): def setUp(self): """ diff --git a/python/tests/test_oral_query_generation.py b/python/tests/test_oral_query_generation.py index c9b497f17..46f741048 100644 --- a/python/tests/test_oral_query_generation.py +++ b/python/tests/test_oral_query_generation.py @@ -23,7 +23,6 @@ ',实时视频分享您的生活。') -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestOralQueryGenerationComponent(unittest.TestCase): def setUp(self): """ diff --git a/python/tests/test_qa_llm_excel2figure.py b/python/tests/test_qa_llm_excel2figure.py index 290ab5fc8..e5c8f0594 100644 --- a/python/tests/test_qa_llm_excel2figure.py +++ b/python/tests/test_qa_llm_excel2figure.py @@ -77,7 +77,6 @@ def get_data_file(filename): "bce-auth-v1/e464e6f951124fdbb2410c590ef9ed2f/2024-02-21T09%3A51%3A14Z/-1/host/1802a9c9142ef328d6" ) -# @unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestExcel2figure(unittest.TestCase): # @parameterized.expand([ # param("ERNIE-Bot 4.0", "2020年各个月份的利润分别是多少?使用条形图绘制出来", file_bos_url), diff --git a/python/tests/test_qa_pair_mining.py b/python/tests/test_qa_pair_mining.py index 111d0dc06..ce3c6c822 100644 --- a/python/tests/test_qa_pair_mining.py +++ b/python/tests/test_qa_pair_mining.py @@ -19,7 +19,6 @@ import appbuilder -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestQAPairMiningComponent(unittest.TestCase): def setUp(self): """ diff --git a/python/tests/test_qrcode_ocr.py b/python/tests/test_qrcode_ocr.py index 6ba3d9603..a6fd6eb6a 100644 --- a/python/tests/test_qrcode_ocr.py +++ b/python/tests/test_qrcode_ocr.py @@ -19,7 +19,6 @@ from appbuilder.core._exception import InvalidRequestArgumentError -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestQRcodeOCR(unittest.TestCase): def setUp(self): """ diff --git a/python/tests/test_rerank.py b/python/tests/test_rerank.py index f0045112e..e632791a5 100644 --- a/python/tests/test_rerank.py +++ b/python/tests/test_rerank.py @@ -9,7 +9,6 @@ import appbuilder -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestReranker(unittest.TestCase): def setUp(self): diff --git a/python/tests/test_similar_question.py b/python/tests/test_similar_question.py index 97d8b8d46..346748954 100644 --- a/python/tests/test_similar_question.py +++ b/python/tests/test_similar_question.py @@ -16,7 +16,6 @@ import os import appbuilder -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestSimilarQuestionComponent(unittest.TestCase): def setUp(self): """ diff --git a/python/tests/test_style_rewrite.py b/python/tests/test_style_rewrite.py index 4e99b6279..6bc572078 100644 --- a/python/tests/test_style_rewrite.py +++ b/python/tests/test_style_rewrite.py @@ -19,7 +19,6 @@ from appbuilder.core.components.llms.style_rewrite.base import StyleChoices import appbuilder -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestStyleRewriteComponent(unittest.TestCase): def setUp(self): """ diff --git a/python/tests/test_style_writing.py b/python/tests/test_style_writing.py index 7f8b5c27a..a4c3909da 100644 --- a/python/tests/test_style_writing.py +++ b/python/tests/test_style_writing.py @@ -20,7 +20,6 @@ from appbuilder.core.components.llms.style_writing.component import StyleQueryChoices, LengthChoices -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestStyleWritingComponent(unittest.TestCase): def setUp(self): """ diff --git a/python/tests/test_table_ocr.py b/python/tests/test_table_ocr.py index 85265658d..23117f5ff 100644 --- a/python/tests/test_table_ocr.py +++ b/python/tests/test_table_ocr.py @@ -19,7 +19,6 @@ from appbuilder.core._exception import InvalidRequestArgumentError -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestTableOCR(unittest.TestCase): def setUp(self): """ diff --git a/python/tests/test_trace_skip_raise_error.py b/python/tests/test_trace_skip_raise_error.py index 7083aed37..e465dc63b 100644 --- a/python/tests/test_trace_skip_raise_error.py +++ b/python/tests/test_trace_skip_raise_error.py @@ -111,7 +111,6 @@ def mock_list_trace_01(): def mock_list_trace_02(): raise TestException() -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestTraceSkipRaiseError(unittest.TestCase): def setUp(self): os.environ["APPBUILDER_TRACE_DEBUG"] = "True" diff --git a/python/tests/test_treemind.py b/python/tests/test_treemind.py index 823c9e13f..809f7c755 100644 --- a/python/tests/test_treemind.py +++ b/python/tests/test_treemind.py @@ -16,7 +16,6 @@ from appbuilder.core.message import Message from appbuilder import TreeMind -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestTreeMindComponent(unittest.TestCase): def setUp(self): """ From 72107d84483419f929cb8cc85eaec7a823e6bc45 Mon Sep 17 00:00:00 2001 From: userpj Date: Tue, 26 Nov 2024 21:11:31 +0800 Subject: [PATCH 37/85] =?UTF-8?q?Golang=20SDK:=20Agent=E5=AF=B9=E8=AF=9D?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E6=94=B6=E6=95=9B=E5=88=B0Run=E6=96=B9?= =?UTF-8?q?=E6=B3=95=20(#619)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../agent/tool_choice.ipynb | 2 +- .../Platform/Application/appbuilder_client.md | 195 +++++++----------- go/appbuilder/app_builder_client.go | 1 + 3 files changed, 72 insertions(+), 126 deletions(-) diff --git a/cookbooks/end2end_application/agent/tool_choice.ipynb b/cookbooks/end2end_application/agent/tool_choice.ipynb index 092658e1b..0c3dffb6f 100644 --- a/cookbooks/end2end_application/agent/tool_choice.ipynb +++ b/cookbooks/end2end_application/agent/tool_choice.ipynb @@ -408,7 +408,7 @@ "\tinput := make(map[string]any)\n", "\tinput[\"city\"] = \"北京\"\n", "\tend_user_id := \"go_toolchoice_demo\"\n", - "\ti, err := client.RunWithToolCall(AppBuilderClientRunRequest{\n", + "\ti, err := client.Run(AppBuilderClientRunRequest{\n", "\t\tConversationID: conversationID,\n", "\t\tAppID: appID,\n", "\t\tQuery: \"\",\n", diff --git a/docs/BasisModule/Platform/Application/appbuilder_client.md b/docs/BasisModule/Platform/Application/appbuilder_client.md index 88d8fb06b..08dcc44ca 100644 --- a/docs/BasisModule/Platform/Application/appbuilder_client.md +++ b/docs/BasisModule/Platform/Application/appbuilder_client.md @@ -847,132 +847,23 @@ class AppBuilderClientDemo { #### 方法入参 无 #### 方法出参 -| 参数名称 | 参数类型 | 描述 | 示例值 | -| ------------ | -------- | -------------------------------------------- | ------ | -| conversation | str | 创建成功的对话对象,后续操作都基于该对象进行 | | +| 参数名称 | 参数类型 | 描述 | 示例值 | +| -------------- | -------- | -------------------------------------------- | ------ | +| ConversationId | str | 创建成功的对话对象,后续操作都基于该对象进行 | | -### ```Run()``` -#### Run方法入参 - -| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | -| -------------- | ------------ | -------- | -------------------------------------------------------------------------------- | -------------------- | -| conversationID | string | 是 | 对话ID,可以通过CreateConversation()获取 | | -| query | string | 是 | query内容 | "汽车性能参数怎么样" | -| stream | bool | 是 | 为true时则流式返回,为false时则一次性返回所有内容, 推荐设为true,降低首token时延 | | -| file_ids | list[String] | 否 | 对话可引用的文档ID | | - -#### Run方法出参 - -| 参数名称 | 参数类型 | 描述 | 示例值 | -| ------------------------ | ------------------------ | --------------------------------------- | ------ | -| AppBuilderClientIterator | AppBuilderClientIterator | 回答迭代器,流式/非流式均统一返回该类型 | | -| error | error | 存在错误时error不为nil,反之 | | - -#### 迭代AgentBuilderIterator - -| 参数名称 | 参数类型 | 描述 | 示例值 | -| ------------- | ----------- | -------------------- | --------------------------------------------------------------------------------------- | -| +Answer | string | 智能体应用返回的回答 | | -| +Events | []Event | 事件列表 | | -| +Events[0] | Event | 具体事件内容 | | -| ++Code | string | 错误码 | | -| ++Message | string | 错误具体消息 | | -| ++Status | string | 事件状态 | 状态描述,preparing(准备运行)running(运行中)error(执行错误) done(执行完成) | -| ++EventType | string | 事件类型 | | -| ++ContentType | string | 内容类型 | 可选值包括:code text, image, status,image, function_call, rag, audio、video等 | -| ++Detail | interface{} | 事件输出详情 | 代码解释器、文生图、工具组件、RAG等的详细输出内容 | -| ++Usage | Usage | 模型调用的token用量 | Usage(prompt_tokens=1322, completion_tokens=80, total_tokens=1402, name='ERNIE-4.0-8K') | - - -#### 示例代码 - - -```Go -package main - -import ( - "errors" - "fmt" - "io" - "os" - - "github.com/baidubce/app-builder/go/appbuilder" -) - -func main() { - // 设置APPBUILDER_TOKEN、GATEWAY_URL_V2环境变量 - os.Setenv("APPBUILDER_TOKEN", "请设置正确的应用密钥") - // 默认可不填,默认值是 https://qianfan.baidubce.com - os.Setenv("GATEWAY_URL_V2", "") - config, err := appbuilder.NewSDKConfig("", "") - if err != nil { - fmt.Println("new config failed: ", err) - return - } - // 初始化实例 - appID := "请填写正确的应用ID" - builder, err := appbuilder.NewAppBuilderClient(appID, config) - if err != nil { - fmt.Println("new agent builder failed: ", err) - return - } - // 创建对话ID - conversationID, err := builder.CreateConversation() - if err != nil { - fmt.Println("create conversation failed: ", err) - return - } - // 与创建应用时绑定的知识库不同之处在于, - // 所上传文件仅在本次会话ID下发生作用,如果创建新的会话ID,上传的文件自动失效 - // 而知识库在不同的会话ID下均有效 - fileID, err := builder.UploadLocalFile(conversationID, "/path/to/cv.pdf") - if err != nil { - fmt.Println("upload local file failed:", err) - return - } - // 执行流式对话 - // 注意file_ids不是必填项,如果不需要引用特定的文档,则将[]string{fileID}更换为nil即可 - // 同时还需要将上文的fileID, err := builder.UploadLocalFile(conversationID, "/path/to/cv.pdf")代码 - // 更换为 _, err = client.UploadLocalFile(conversationID, "/path/to/cv.pdf"),否则会报错 - i, err := builder.Run(conversationID, "描述简历中的候选人情况", []string{fileID}, true) - if err != nil { - fmt.Println("run failed: ", err) - return - } - - completedAnswer := "" - var answer *appbuilder.AppBuilderClientAnswer - for answer, err = i.Next(); err == nil; answer, err = i.Next() { - completedAnswer = completedAnswer + answer.Answer - for _, ev := range answer.Events { - evJSON, _ := json.Marshal(ev) - fmt.Println(string(evJSON)) - } - } - // 迭代正常结束err应为io.EOF - if errors.Is(err, io.EOF) { - fmt.Println("run success") - fmt.Println("智能体回答内容: ", completedAnswer) - } else { - fmt.Println("run failed:", err) - } -} -``` - -### ```RunWithToolCall()``` - +### `Run()` #### Run方法入参`AppBuilderClientRunRequest` -| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | -| -------------- | ---------- | -------- | -------------------------------------------------------------------------------- | -------------------- | -| ConversationID | string | 是 | 对话ID,可以通过CreateConversation()获取 | | -| Query | string | 是 | query内容 | "汽车性能参数怎么样" | +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +| -------------- | ---------- | -------- | ------------------------------------------------------------ | -------------------- | +| ConversationID | string | 是 | 对话ID,可以通过CreateConversation()获取 | | +| Query | string | 是 | query内容 | "汽车性能参数怎么样" | | Stream | bool | 是 | 为true时则流式返回,为false时则一次性返回所有内容, 推荐设为true,降低首token时延 | | -| AppID | string | 是 | 应用ID,线上Agent应用的ID | | -| Tools | []Tool | 否 | 一个列表,其中每个字典对应一个工具的配置 | | -| ToolOuptus | []ToolOupt | 否 | 内容为本地的工具执行结果,以自然语言/json dump str描述 | | -| ToolChoice | ToolChoice | 否 | 控制大模型使用组件的方式,仅对自主规划Agent生效。 | | +| AppID | string | 是 | 应用ID,线上Agent应用的ID | | +| Tools | []Tool | 否 | 一个列表,其中每个字典对应一个工具的配置 | | +| ToolOuptus | []ToolOupt | 否 | 内容为本地的工具执行结果,以自然语言/json dump str描述 | | +| ToolChoice | ToolChoice | 否 | 控制大模型使用组件的方式,仅对自主规划Agent生效。 | | `Tool`、`ToolOutput`、`ToolChoice`定义如下: @@ -1026,7 +917,61 @@ type ToolChoiceFunction struct { | ++Detail | interface{} | 事件输出详情 | 代码解释器、文生图、工具组件、RAG等的详细输出内容 | | ++Usage | Usage | 模型调用的token用量 | Usage(prompt_tokens=1322, completion_tokens=80, total_tokens=1402, name='ERNIE-4.0-8K') | -#### ToolCall示例代码 + +#### Run示例代码 + + +```Go +// 安装说明: +// go get github.com/baidubce/app-builder/go/appbuilder + +package main + +import ( + "errors" + "fmt" + "io" + "os" + + "github.com/baidubce/app-builder/go/appbuilder" +) + +func main() { + // 设置环境中的TOKEN,以下TOKEN请替换为您的个人TOKEN,个人TOKEN可通过该页面【获取鉴权参数】或控制台页【密钥管理】处获取 + os.Setenv("APPBUILDER_TOKEN", "bce-v3/ALTAK-xxx90ea58") + // 从AppBuilder控制台【个人空间】-【应用】网页获取已发布应用的ID + appID := "4678492a-xxx-654538d3503c" + config, err := appbuilder.NewSDKConfig("", "") + if err != nil { + fmt.Println("new config failed: ", err) + return + } + + builder, err := appbuilder.NewAppBuilderClient(appID, config) + if err != nil { + fmt.Println("new agent builder failed: ", err) + return + } + conversationID, err := builder.CreateConversation() + if err != nil { + fmt.Println("create conversation failed: ", err) + return + } + + i, err := builder.Run(conversationID, "你好,你能做什么?", nil, false) + if err != nil { + fmt.Println("run failed: ", err) + return + } + + var answer *appbuilder.AppBuilderClientAnswer + for answer, err = i.Next(); err == nil; answer, err = i.Next() { + fmt.Println(answer.Answer) + } +} +``` + +#### ToolCall功能示例代码 ```go package main @@ -1094,7 +1039,7 @@ func main() { return } - i, err := client.RunWithFunctionCall(appbuilder.AppBuilderClientRunRequest{ + i, err := client.Run(appbuilder.AppBuilderClientRunRequest{ AppID: appID, Query: "今天北京的天气怎么样?", ConversationID: conversationID, @@ -1115,7 +1060,7 @@ func main() { } } - i2, err := client.RunWithFunctionCall(appbuilder.AppBuilderClientRunRequest{ + i2, err := client.Run(appbuilder.AppBuilderClientRunRequest{ ConversationID: conversationID, AppID: appID, ToolOutputs: []appbuilder.ToolOutput{ @@ -1197,7 +1142,7 @@ func main() { input := make(map[string]any) input["city"] = "北京" end_user_id := "go_toolchoice_demo" - i, err := client.RunWithToolCall(AppBuilderClientRunRequest{ + i, err := client.Run(AppBuilderClientRunRequest{ ConversationID: conversationID, AppID: appID, Query: "", diff --git a/go/appbuilder/app_builder_client.go b/go/appbuilder/app_builder_client.go index c2c6921c8..387ec7f92 100644 --- a/go/appbuilder/app_builder_client.go +++ b/go/appbuilder/app_builder_client.go @@ -335,6 +335,7 @@ func (t *AppBuilderClient) buildAppBuilderClientRunRequest(param ...interface{}) }, nil } +// Deprecated: Run方法已兼容此方法 func (t *AppBuilderClient) RunWithToolCall(req AppBuilderClientRunRequest) (AppBuilderClientIterator, error) { if len(req.ConversationID) == 0 { return nil, errors.New("conversationID mustn't be empty") From ffeaea60eb194e33f6c3f42a8325af3b7ddf4ad7 Mon Sep 17 00:00:00 2001 From: peiwenYe <963623403@qq.com> Date: Wed, 27 Nov 2024 11:08:19 +0800 Subject: [PATCH 38/85] =?UTF-8?q?=E5=A2=9E=E5=8A=A0plan=E5=92=8Cfunction?= =?UTF-8?q?=5Fcall=E4=B8=A4=E7=A7=8D=E8=BF=94=E5=9B=9Etype=20(#618)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 增加plan和function_call两种返回type * 为数据类型添加exta属性 * 为数据类型添加exta属性 * 为数据类型添加exta属性 --------- Co-authored-by: yepeiwen01 --- python/core/component.py | 44 +++++++++++---- python/tests/component_schemas.py | 60 ++++++++++++++++++++- python/tests/component_tool_eval_schemas.py | 2 +- python/tests/test_all_components.py | 2 +- python/tests/test_base_component.py | 20 ++++--- 5 files changed, 106 insertions(+), 22 deletions(-) diff --git a/python/core/component.py b/python/core/component.py index add983871..74ee6babe 100644 --- a/python/core/component.py +++ b/python/core/component.py @@ -60,28 +60,28 @@ def extract_values_to_dict(self): return inputs -class Text(BaseModel): +class Text(BaseModel, extra='allow'): info: str = Field(default="", description="具体文本内容") -class Code(BaseModel): +class Code(BaseModel, extra='allow'): code: str = Field(default="", description="代码片段") -class Files(BaseModel): +class Files(BaseModel, extra='allow'): filename: str = Field(default="", description="文件名") url: str = Field(default="", description="文件url") -class Urls(BaseModel): +class Urls(BaseModel, extra='allow'): url: str = Field(default="", description="链接地址") -class OralText(BaseModel): +class OralText(BaseModel, extra='allow'): info: str = Field(default="", description="口语化文本内容") -class References(BaseModel): +class References(BaseModel, extra='allow'): type: str = Field(default="", description="类型") resource_type: str = Field(default="", description="资源类型") icon: str = Field(default="", description="站点图标") @@ -96,21 +96,35 @@ class References(BaseModel): video_url: str = Field(default="", description="视频url") -class Image(BaseModel): +class Image(BaseModel, extra='allow'): filename: str = Field(default="", description="图片名称") url: str = Field(default="", description="图片url") byte: Optional[bytes] = Field(default=b'', description="图片二进制数据") -class Chart(BaseModel): +class Chart(BaseModel, extra='allow'): filename: str = Field(default="", description="图表名称") url: str = Field(default="", description="图表url") -class Audio(BaseModel): +class Audio(BaseModel, extra='allow'): filename: str = Field(default="", description="音频名称") url: str = Field(default="", description="音频url") byte: Optional[bytes] = Field(default=b'', description="音频二进制数据") + + +class PlanStep(BaseModel, extra='allow'): + name: str = Field(default="", description="step名") + arguments: dict = Field(default={}, description="step参数") + +class Plan(BaseModel, extra='allow'): + detail: str = Field(default="", description="计划详情") + steps: list[PlanStep] = Field(default=[], description="步骤列表") + +class FunctionCall(BaseModel, extra='allow'): + thought: str = Field(default="", description="思考结果") + name: str = Field(default="", description="工具名") + arguments: dict = Field(default={}, description="参数列表") class Content(BaseModel): @@ -126,7 +140,7 @@ class Content(BaseModel): description="耗时、性能、内存等trace及debug所需信息") type: str = Field(default="text", description="代表event 类型,包括 text、code、files、urls、oral_text、references、image、chart、audio该字段的取值决定了下面text字段的内容结构") - text: Union[Text, Code, Files, Urls, OralText, References, Image, Chart, Audio] = Field(default=Text, + text: Union[Text, Code, Files, Urls, OralText, References, Image, Chart, Audio, Plan, FunctionCall] = Field(default=Text, description="代表当前 event 元素的内容,每一种 event 对应的 text 结构固定") @field_validator('text', mode='before') @@ -149,6 +163,10 @@ def set_text(cls, v, values, **kwargs): return Chart(**v) elif values.data['type'] == 'audio': return Audio(**v) + elif values.data['type'] == 'plan': + return Plan(**v) + elif values.data['type'] == 'function_call': + return FunctionCall(**v) else: raise ValueError(f"Invalid value for 'type': {values['type']}") @@ -514,9 +532,13 @@ def create_output(self, type, text, role="tool", name="", visible_scope="all", r key_list = ["filename", "url"] elif type == "audio": key_list = ["filename", "url"] + elif type == "plan": + key_list = ["detail", "steps"] + elif type == "function_call": + key_list = ["thought", "name", "arguments"] else: raise ValueError("Unknown type: {}".format(type)) - assert all(key in text for key in key_list), "all keys:{} must be included in the text field".format(key_list) + # assert all(key in text for key in key_list), "all keys:{} must be included in the text field".format(key_list) else: raise ValueError("text must be str or dict") diff --git a/python/tests/component_schemas.py b/python/tests/component_schemas.py index be69d1586..b98ddbfea 100644 --- a/python/tests/component_schemas.py +++ b/python/tests/component_schemas.py @@ -247,6 +247,62 @@ "required": ["filename", "url"] } + +plan_schema = copy.deepcopy(base_item_schema) +plan_schema["$schema"] = "plan_schema" +plan_schema["properties"]["type"] = { + "type": "string", + "enum": ["plan"] +} +plan_schema["properties"]["text"] = { + "type": "object", + "properties": { + "detail": { + "type": "string" + }, + "steps": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": {"type": "string"}, + "arguments": { + "type": "object", + "additionalProperties": True + } + }, + "required": ["name", "arguments"] + } + } + }, + "required": ["detail", "steps"] +} + + +function_call_schema = copy.deepcopy(base_item_schema) +function_call_schema["$schema"] = "function_call_schema" +function_call_schema["properties"]["type"] = { + "type": "string", + "enum": ["function_call"] +} +function_call_schema["properties"]["text"] = { + "type": "object", + "properties": { + "thought": { + "type": "string" + }, + "name": { + "type": "string", + }, + "arguments": { + "type": "object", + "additionalProperties": True + } + }, + "required": ["thought", "name", "arguments"], +} + + type_to_json_schemas = { "text": text_schema, 'code': code_schema, @@ -256,5 +312,7 @@ "references": references_schema, "image": image_schema, "chart": chart_schema, - "audio": audio_schema + "audio": audio_schema, + "plan": plan_schema, + "function_call": function_call_schema, } \ No newline at end of file diff --git a/python/tests/component_tool_eval_schemas.py b/python/tests/component_tool_eval_schemas.py index 416de877d..d52cd7a1b 100644 --- a/python/tests/component_tool_eval_schemas.py +++ b/python/tests/component_tool_eval_schemas.py @@ -11,7 +11,7 @@ # 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 appbuilder.tests.component_schemas import text_schema, url_schema, image_schema, code_schema, file_schema, oral_text_schema, references_schema, chart_schema, audio_schema +from appbuilder.tests.component_schemas import text_schema, url_schema, image_schema, code_schema, file_schema, oral_text_schema, references_schema, chart_schema, audio_schema, plan_schema, function_call_schema components_tool_eval_output_json_maps = { "AnimalRecognition": [text_schema], diff --git a/python/tests/test_all_components.py b/python/tests/test_all_components.py index 4cde0ac52..6c6665346 100644 --- a/python/tests/test_all_components.py +++ b/python/tests/test_all_components.py @@ -33,7 +33,7 @@ def write_error_data(txt_file_path, error_df,error_stats): file.write(f"错误信息: {error}, 出现次数: {count}\n") print(f"\n错误信息已写入: {txt_file_path}") -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestComponentManifestsAndToolEval(unittest.TestCase): def setUp(self) -> None: self.all_components = get_all_components() diff --git a/python/tests/test_base_component.py b/python/tests/test_base_component.py index 1f6e72a90..4e605615c 100644 --- a/python/tests/test_base_component.py +++ b/python/tests/test_base_component.py @@ -28,6 +28,8 @@ def test_valid_output_with_dict(self): output6 = self.component.create_output(type="image", text={"filename": "file.png", "url": "http://www.baidu.com"}) output7 = self.component.create_output(type="chart", text={"filename": "file.jpg", "url": "http://www.baidu.com"}) output8 = self.component.create_output(type="audio", text={"filename": "file.mp3", "url": "http://www.baidu.com"}) + output9 = self.component.create_output(type="plan", text={"detail": "hello", "steps":[{"name": "1", "arguments": {"query": "a", "chat_history": "world"}}]}) + output10 = self.component.create_output(type="function_call", text={"thought": "hello", "name": "AppBuilder", "arguments": {"query": "a", "chat_history": "world"}}) self.assertIsInstance(output1, ComponentOutput) self.assertIsInstance(output2, ComponentOutput) self.assertIsInstance(output3, ComponentOutput) @@ -36,6 +38,8 @@ def test_valid_output_with_dict(self): self.assertIsInstance(output6, ComponentOutput) self.assertIsInstance(output7, ComponentOutput) self.assertIsInstance(output8, ComponentOutput) + self.assertIsInstance(output9, ComponentOutput) + self.assertIsInstance(output10, ComponentOutput) def test_valid_output_type_with_same_key(self): output1 = self.component.create_output(type="urls", text={"url": "http://www.baidu.com"}) @@ -52,14 +56,14 @@ def test_valid_output_type_with_same_key(self): def test_invalid_output_type_json(self): with self.assertRaises(ValueError): output = self.component.create_output(type="json", text="") - with self.assertRaises(AssertionError): - output = self.component.create_output(type="files", text={}) - with self.assertRaises(AssertionError): - output = self.component.create_output(type="references", text={"info": "text"}) - with self.assertRaises(AssertionError): - output = self.component.create_output(type="image", text={"url": "https://example.com/img"}) - with self.assertRaises(AssertionError): - output = self.component.create_output(type="chart", text={"url": "https://example.com/chart"}) + # with self.assertRaises(AssertionError): + # output = self.component.create_output(type="files", text={}) + # with self.assertRaises(AssertionError): + # output = self.component.create_output(type="references", text={"info": "text"}) + # with self.assertRaises(AssertionError): + # output = self.component.create_output(type="image", text={"url": "https://example.com/img"}) + # with self.assertRaises(AssertionError): + # output = self.component.create_output(type="chart", text={"url": "https://example.com/chart"}) if __name__ == '__main__': From 60d2cd12645bfedf3060aaf5cfe4b51fbe07f688 Mon Sep 17 00:00:00 2001 From: C9luster <138663536+C9luster@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:14:02 +0800 Subject: [PATCH 39/85] =?UTF-8?q?Revert=20=E6=96=87=E7=94=9F=E5=9B=BE?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=20(#620)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: yinjiaqi --- python/core/components/v2/__init__.py | 2 - .../components/v2/text_to_image/__init__.py | 13 - .../components/v2/text_to_image/component.py | 354 ------------------ python/tests/component_tool_eval_cases.py | 7 +- python/tests/component_tool_eval_schemas.py | 3 +- python/tests/test_v2_text_to_image.py | 71 ---- 6 files changed, 2 insertions(+), 448 deletions(-) delete mode 100644 python/core/components/v2/text_to_image/__init__.py delete mode 100644 python/core/components/v2/text_to_image/component.py delete mode 100644 python/tests/test_v2_text_to_image.py diff --git a/python/core/components/v2/__init__.py b/python/core/components/v2/__init__.py index c9c7d06aa..d1a3b8811 100644 --- a/python/core/components/v2/__init__.py +++ b/python/core/components/v2/__init__.py @@ -14,10 +14,8 @@ from .animal_recognize.component import AnimalRecognition from .image_understand.component import ImageUnderstand -from .text_to_image.component import Text2Image __V2_COMPONENTS__ = [ "AnimalRecognition", "ImageUnderstand", - "Text2Image", ] # NOQA \ No newline at end of file diff --git a/python/core/components/v2/text_to_image/__init__.py b/python/core/components/v2/text_to_image/__init__.py deleted file mode 100644 index b4518e8cd..000000000 --- a/python/core/components/v2/text_to_image/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2024 Baidu, Inc. 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. \ No newline at end of file diff --git a/python/core/components/v2/text_to_image/component.py b/python/core/components/v2/text_to_image/component.py deleted file mode 100644 index ea09dd8be..000000000 --- a/python/core/components/v2/text_to_image/component.py +++ /dev/null @@ -1,354 +0,0 @@ -# Copyright (c) 2024 Baidu, Inc. 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. - -r"""Text2Image component. -""" -import time -import math - -from typing import Generator, Union, Optional -from appbuilder.core.component import Component -from appbuilder.core.message import Message -from appbuilder.core._client import HTTPClient -from appbuilder.core._exception import AppBuilderServerException, RiskInputException -from appbuilder.core.components.text_to_image.model import Text2ImageSubmitRequest, Text2ImageQueryRequest, \ - Text2ImageQueryResponse, Text2ImageSubmitResponse, Text2ImageOutMessage -from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace - - -class Text2Image(Component): - r""" - 文生图组件,即对于输入的文本,输出生成的图片url。 - - Examples: - - .. code-block:: python - - import appbuilder - text_to_image = appbuilder.Text2Image() - os.environ["APPBUILDER_TOKEN"] = '...' - content_data = {"prompt": "上海的经典风景", "width": 1024, "height": 1024, "image_num": 1} - msg = appbuilder.Message(content_data) - out = text_to_image.run(inp) - # 打印生成结果 - print(out.content) # eg: {"img_urls": ["xxx"]} - """ - name = "text_to_image" - version = "v1" - manifests = [ - { - "name": "text_to_image", - "description": "文生图,该组件只用于图片创作。当用户需要进行场景、人物、海报等内容的绘制时,使用该画图组件。如果用户需要生成图表(柱状图、折线图、雷达图等),则必须使用代码解释器。", - "parameters": { - "type": "object", - "properties": { - "query": { - "type": "string", - "description": "文生图用的query。特别注意,这个字段只能由中文字符组成,不能含有任何英语描述。" - } - }, - "required": [ - "query" - ] - } - } - ] - - @HTTPClient.check_param - @components_run_trace - def run( - self, - message: Message, - width: int = 1024, - height: int = 1024, - image_num: int = 1, - image: Optional[str] = None, - url: Optional[str] = None, - pdf_file: Optional[str] = None, - pdf_file_num: Optional[str] = None, - change_degree: Optional[int] = None, - text_content: Optional[str] = None, - task_time_out: Optional[int]= None, - text_check: Optional[int] = 1, - request_id: Optional[str] = None - ): - """ - 执行文本到图像的生成任务。 - - Args: - message (Message): 包含任务相关信息的消息对象。 - width (int, optional): 生成的图像的宽度,默认为1024。 - height (int, optional): 生成的图像的高度,默认为1024。 - image_num (int, optional): 生成图像的数量,默认为1。 - image (Optional[str], optional): 参考图像的路径或URL,默认为None。 - url (Optional[str], optional): 参考图像的URL,默认为None。 - pdf_file (Optional[str], optional): 参考PDF文件的路径,默认为None。 - pdf_file_num (Optional[str], optional): 参考PDF文件中的页码范围,默认为None。 - change_degree (Optional[int], optional): 图像变换的程度,默认为None。 - text_content (Optional[str], optional): 需要转换的文本内容,默认为None。 - task_time_out (Optional[int], optional): 任务超时时间,默认为None。 - text_check (Optional[int], optional): 是否进行文本内容检查,默认为1。 - request_id (Optional[str], optional): 请求的唯一标识,默认为None。 - - Returns: - Message: 包含生成图像URL的消息对象。 - - Raises: - HTTPError: 请求失败时抛出异常。 - - """ - prompt=message.content["prompt"] - img_urls, raw_date = self.__recognize( - prompt = prompt, - width = width, - height = height, - image_num = image_num, - image = image, - url = url, - pdf_file = pdf_file, - pdf_file_num = pdf_file_num, - change_degree = change_degree, - text_content = text_content, - task_time_out = task_time_out, - text_check = text_check, - request_id = request_id - ) - if len(img_urls) == 0: - raise RiskInputException(f'prompt{prompt} 中可能存在敏感词') - out = Text2ImageOutMessage(img_urls=img_urls) - return Message(content=out.model_dump()) - - @components_run_stream_trace - def tool_eval( - self, - query: str, - **kwargs, - ) -> Union[Generator[str, None, None], str]: - """ - 评估工具方法。 - - Args: - query (str): 输入的查询字符串。 - **kwargs: 任意数量的关键字参数,其中可以包括 'origin_query'。 - - Returns: - Union[Generator[str, None, None], str]: - 返回一个生成器,生成类型为 'urls' 的输出,其中包含图片URL列表。 - 如果发生异常,则可能返回错误字符串。 - - Raises: - AppBuilderServerException: 如果绘图服务发生错误,则抛出此异常。 - RiskInputException: 如果查询字符串中包含敏感词,则抛出此异常。 - - """ - traceid = kwargs.get("traceid") - prompt = query - width = kwargs.get("width", 1024) - height = kwargs.get("height", 1024) - image_num = kwargs.get("image_num", 1) - image = kwargs.get("image", None) - url = kwargs.get("url", None) - pdf_file = kwargs.get("pdf_file", None) - pdf_file_num = kwargs.get("pdf_file_num", None) - change_degree = kwargs.get("change_degree", None) - text_content = kwargs.get("text_content", None) - task_time_out = kwargs.get("task_time_out", None) - text_check = kwargs.get("text_check", 1) - request_id = traceid - - img_urls, raw_date = self.__recognize( - prompt = prompt, - width = width, - height = height, - image_num = image_num, - image = image, - url = url, - pdf_file = pdf_file, - pdf_file_num = pdf_file_num, - change_degree = change_degree, - text_content = text_content, - task_time_out = task_time_out, - text_check = text_check, - request_id = request_id - ) - - if len(img_urls) == 0: - raise RiskInputException(f'query:{query} 中可能存在敏感词') - - for url_number in range(len(img_urls)): - yield self.create_output(type = 'urls', text = img_urls[url_number], name=f"url_{url_number + 1}", raw_data = raw_date) - - def __recognize( - self, - prompt: str, - width: int = 1024, - height: int = 1024, - image_num: int = 1, - image: Optional[str] = None, - url: Optional[str] = None, - pdf_file: Optional[str] = None, - pdf_file_num: Optional[str] = None, - change_degree: Optional[int] = None, - text_content: Optional[str] = None, - task_time_out: Optional[int]= None, - text_check: Optional[int] = 1, - request_id: Optional[str] = None - ): - """ - 识别并生成图片。 - - Args: - prompt (str): 提示文本,用于生成图片。 - width (int, optional): 图片宽度,默认为1024。 - height (int, optional): 图片高度,默认为1024。 - image_num (int, optional): 生成图片的数量,默认为1。 - image (str, optional): 传入图片路径,默认为None。 - url (str, optional): 图片URL,默认为None。 - pdf_file (str, optional): PDF文件路径,默认为None。 - pdf_file_num (str, optional): 需要转换的PDF页数,默认为None。 - change_degree (int, optional): 图片旋转角度,默认为None。 - text_content (str, optional): 文本内容,默认为None。 - task_time_out (int, optional): 任务超时时间,默认为None。 - text_check (int, optional): 文本校验选项,默认为1。 - request_id (str, optional): 请求ID,默认为None。 - - Returns: - tuple: 包含生成的图片URL列表和返回数据的元组。 - - """ - headers = self._http_client.auth_header() - headers["Content-Type"] = "application/json" - api_url = self._http_client.service_url("/v1/bce/aip/ernievilg/v1/txt2imgv2") - - req = Text2ImageSubmitRequest( - prompt=prompt, - width=width, - height=height, - image_num=image_num, - image=image, - url=url, - pdf_file=pdf_file, - pdf_file_num=pdf_file_num, - change_degree=change_degree, - text_content=text_content, - task_time_out=task_time_out, - text_check=text_check - ) - response = self.http_client.session.post(api_url, json=req.model_dump(), headers=headers, timeout=None) - self._http_client.check_response_header(response) - data = response.json() - resp= Text2ImageSubmitResponse(**data) - - taskId = resp.data.task_id - if taskId is not None: - task_request_time = 1 - - while True: - request = Text2ImageQueryRequest(task_id=taskId) - text2ImageQueryResponse, data = self._queryText2ImageData(request, request_id=request_id) - if text2ImageQueryResponse.data.task_progress is not None: - task_progress = float(text2ImageQueryResponse.data.task_progress) - if math.isclose(1.0, task_progress, rel_tol=1e-9, abs_tol=0.0): - break - - # NOTE(chengmo):文生图组件的返回时间在10s以上,查询过于频繁会被限流,导致异常报错 - # 此处采用 yangyongzhen老师提供的方案,前三次查询间隔3s,后三次查询间隔逐渐增大 - if task_request_time <= 3: - time.sleep(3) - else: - time.sleep(task_request_time) - task_request_time += 1 - - img_urls = self._extract_img_urls(text2ImageQueryResponse) - - return img_urls, data - - def _queryText2ImageData( - self, - request: Text2ImageQueryRequest, - timeout: float = None, - retry: int = 0, - request_id: str = None, - ) -> Text2ImageQueryResponse: - """ - 将文本查询请求转换为图像数据。 - - Args: - request (Text2ImageQueryRequest): 输入请求,必填参数。 - timeout (float, optional): 请求的超时时间,默认为None。 - retry (int, optional): 请求的重试次数,默认为0。 - request_id (str, optional): 请求的唯一标识符,默认为None。 - - Returns: - Text2ImageQueryResponse: 接口返回的输出消息。 - """ - url = self.http_client.service_url("/v1/bce/aip/ernievilg/v1/getImgv2") - data = { - "task_id": request.task_id - } - headers = self.http_client.auth_header(request_id) - headers['content-type'] = 'application/json' - if retry != self.http_client.retry.total: - self.http_client.retry.total = retry - response = self.http_client.session.post(url, json=data, headers=headers, timeout=timeout) - self.http_client.check_response_header(response) - data = response.json() - self.http_client.check_response_json(data) - request_id = self.http_client.response_request_id(response) - self.__class__._check_service_error(request_id, data) - response = Text2ImageQueryResponse(**data) - return response, data - - def _extract_img_urls(self, response: Text2ImageQueryResponse): - """ - 从作画生成的返回结果中提取图片url。 - - Args: - response (obj:`Text2ImageQueryResponse`): 作画生成的返回结果。 - - Returns: - List[str]: 从返回体中提取的图片url列表。 - - """ - img_urls = [] - if response and response.data and response.data.sub_task_result_list: - for sub_task_result in response.data.sub_task_result_list: - if sub_task_result and sub_task_result.final_image_list: - for final_image in sub_task_result.final_image_list: - if final_image and final_image.img_url: - img_urls.append(final_image.img_url) - - return img_urls - - @staticmethod - def _check_service_error(request_id: str, data: dict): - """ - 检查服务错误信息 - - Args: - request_id (str): 请求ID - data (dict): 响应数据 - - Raises: - AppBuilderServerException: 如果响应数据中包含错误信息,则抛出异常 - - Returns: - None - """ - if "error_code" in data or "error_msg" in data: - raise AppBuilderServerException( - request_id=request_id, - service_err_code=data.get("error_code"), - service_err_message=data.get("error_msg") - ) \ No newline at end of file diff --git a/python/tests/component_tool_eval_cases.py b/python/tests/component_tool_eval_cases.py index 403811c8e..0675eca61 100644 --- a/python/tests/component_tool_eval_cases.py +++ b/python/tests/component_tool_eval_cases.py @@ -50,14 +50,9 @@ def inputs(self): def outputs(self): return {"text": ["熊猫"]} -class Text2ImageCase: - def inputs(self): - return {"query": "生成一张熊猫图片"} - component_tool_eval_cases = { "AnimalRecognition": AnimalRecognitionCase, "ImageUnderstand": ImageUnderstandCase, "ASR": ASRCase, - "TreeMind": TreeMindCase, - "Text2Image": Text2ImageCase + "TreeMind": TreeMindCase } \ No newline at end of file diff --git a/python/tests/component_tool_eval_schemas.py b/python/tests/component_tool_eval_schemas.py index d52cd7a1b..972aaabbb 100644 --- a/python/tests/component_tool_eval_schemas.py +++ b/python/tests/component_tool_eval_schemas.py @@ -17,6 +17,5 @@ "AnimalRecognition": [text_schema], "ASR": [text_schema], "TreeMind": [text_schema, url_schema], - "ImageUnderstand": [text_schema], - "Text2Image": [url_schema] + "ImageUnderstand": [text_schema] } \ No newline at end of file diff --git a/python/tests/test_v2_text_to_image.py b/python/tests/test_v2_text_to_image.py deleted file mode 100644 index 209c79e90..000000000 --- a/python/tests/test_v2_text_to_image.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (c) 2024 Baidu, Inc. 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 os -import unittest -import appbuilder - -from appbuilder.core.components.v2 import Text2Image -from appbuilder.core.component import ComponentOutput - -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") -class TestText2Image(unittest.TestCase): - def setUp(self): - """ - 设置环境变量。 - - Args: - None - - Returns: - None. - """ - self.text2Image = Text2Image() - - def test_run(self): - """ - 使用原始文本进行单测 - - Args: - None - - Returns: - None - - """ - inp = appbuilder.Message(content={"prompt": "上海的经典风景"}) - out = self.text2Image.run(inp) - self.assertIsNotNone(out) - self.assertIsInstance(out, appbuilder.Message) - - def test_tool_eval(self): - """ - 测试 tool_eval 方法的正确性。 - - Args: - self: 测试类的实例。 - - Returns: - 无返回值。 - - Raises: - 无异常抛出。 - - """ - result = self.text2Image.tool_eval(query = "上海的经典风景") - for res in result: - self.assertIsInstance(res, ComponentOutput) - -if __name__ == '__main__': - unittest.main() \ No newline at end of file From f418a33ae58084e8c82c3900ac814bc85f8e5a89 Mon Sep 17 00:00:00 2001 From: peiwenYe <963623403@qq.com> Date: Wed, 27 Nov 2024 16:57:49 +0800 Subject: [PATCH 40/85] =?UTF-8?q?=E4=BF=AE=E6=94=B9Component.create=5Foutp?= =?UTF-8?q?ut=E4=B8=BAclassmethod=E6=96=B9=E6=B3=95=EF=BC=8C=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E7=BB=84=E4=BB=B6=E5=8D=95=E6=B5=8Binit=5Fargs=20(#62?= =?UTF-8?q?1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * create_output变更为classmethod方法,component_tool_eval_schemas移动到case中 * create_output变更为classmethod方法,component_tool_eval_schemas移动到case中 * create_output变更为classmethod方法,component_tool_eval_schemas移动到case中 * create_output变更为classmethod方法,component_tool_eval_schemas移动到case中 --------- Co-authored-by: yepeiwen01 --- python/core/component.py | 5 +- python/tests/component_check.py | 71 ++++++++++----------- python/tests/component_tool_eval_cases.py | 46 +++++++++++-- python/tests/component_tool_eval_schemas.py | 21 ------ 4 files changed, 79 insertions(+), 64 deletions(-) delete mode 100644 python/tests/component_tool_eval_schemas.py diff --git a/python/core/component.py b/python/core/component.py index 74ee6babe..d8b82e62a 100644 --- a/python/core/component.py +++ b/python/core/component.py @@ -486,11 +486,12 @@ def _langchain_tool_eval_implement(self, **kwargs): final_result += step.get("text", "") return final_result - def create_output(self, type, text, role="tool", name="", visible_scope="all", raw_data={}, usage={}, metrics={}): + @classmethod + def create_output(cls, type, text, role="tool", name="", visible_scope="all", raw_data={}, usage={}, metrics={}): """create_text_output Args: - type (str): 类型,包括"text", "code", "files", "urls", "oral_text", "references", "image", "chart", "audio" + type (str): 类型,包括"text", "code", "files", "urls", "oral_text", "references", "image", "chart", "audio", "plan", "function_call" text (str|dict): text字段,可输入str或dict role (str, optional): 当前消息来源. Defaults to "tool". name (str, optional): 当前yield内容的step name. Defaults to "". diff --git a/python/tests/component_check.py b/python/tests/component_check.py index 10ef92a60..fc4d86767 100644 --- a/python/tests/component_check.py +++ b/python/tests/component_check.py @@ -22,7 +22,6 @@ from appbuilder.utils.json_schema_to_model import json_schema_to_pydantic_model from appbuilder.tests.component_schemas import type_to_json_schemas from component_tool_eval_cases import component_tool_eval_cases -from component_tool_eval_schemas import components_tool_eval_output_json_maps class CheckInfo(BaseModel): @@ -288,7 +287,7 @@ def _check_jsonschema(self, outputs, output_schemas): """检查输出格式是否符合对应的json schema """ invalid_details = [] - if len(self._check_pre_format(outputs)) > 0 : + if len(invalid_details:=self._check_pre_format(outputs)) > 0 : return invalid_details for content in outputs["content"]: @@ -369,42 +368,40 @@ def _check_text_and_code(self, component_case, output_dict): def check(self, component_cls) -> CheckInfo: invalid_details = [] component_cls_name = component_cls.__name__ - if component_cls_name not in components_tool_eval_output_json_maps: - invalid_details.append("{} 没有注册到 components_tool_eval_output_json_maps 中".format(component_cls_name)) + if component_cls_name not in component_tool_eval_cases: + invalid_details.append("{} 没有添加测试case到 component_tool_eval_cases 中".format(component_cls_name)) else: - output_json_schemas = components_tool_eval_output_json_maps[component_cls_name] - if component_cls_name not in component_tool_eval_cases: - invalid_details.append("{} 没有添加测试case到 component_tool_eval_cases 中".format(component_cls_name)) - else: - component_case = component_tool_eval_cases[component_cls_name]() - input_dict = component_case.inputs() - component_obj = component_cls() - - try: - stream_output_dict = {"text": "", "oral_text":"", "code": ""} - stream_outputs = component_obj.tool_eval(**input_dict) - for stream_output in stream_outputs: #校验流式输出 - iter_invalid_detail = self._check_jsonschema(stream_output.model_dump(), output_json_schemas) - invalid_details.extend(["流式" + error_message for error_message in iter_invalid_detail]) - iter_output_dict = self._gather_iter_outputs(stream_output) - stream_output_dict["text"] += iter_output_dict["text"] - stream_output_dict["oral_text"] += iter_output_dict["oral_text"] - stream_output_dict["code"] += iter_output_dict["code"] - if len(invalid_details) == 0: - invalid_details.extend(self._check_text_and_code(component_case, stream_output_dict)) - except Exception as e: - invalid_details.append("ToolEval执行失败: {}".format(e)) - - time.sleep(2) - try: - non_stream_outputs = component_obj.non_stream_tool_eval(**input_dict) - non_stream_invalid_details = self._check_jsonschema(non_stream_outputs.model_dump(), output_json_schemas) #校验非流式输出 - invalid_details.extend(["非流式" + error_message for error_message in non_stream_invalid_details]) - if len(invalid_details) == 0: - non_stream_output_dict = self._gather_iter_outputs(non_stream_outputs) - invalid_details.extend(self._check_text_and_code(component_case, non_stream_output_dict)) - except Exception as e: - invalid_details.append(" NonStreamToolEval执行失败: {}".format(e)) + component_case = component_tool_eval_cases[component_cls_name]() + input_dict = component_case.inputs() + output_json_schemas = component_case.schemas() + init_args = component_case.init_args() + component_obj = component_cls(**init_args) + + try: #校验流式输出 + stream_output_dict = {"text": "", "oral_text":"", "code": ""} + stream_outputs = component_obj.tool_eval(**input_dict) + for stream_output in stream_outputs: + iter_invalid_detail = self._check_jsonschema(stream_output.model_dump(), output_json_schemas) + invalid_details.extend(["流式" + error_message for error_message in iter_invalid_detail]) + iter_output_dict = self._gather_iter_outputs(stream_output) + stream_output_dict["text"] += iter_output_dict["text"] + stream_output_dict["oral_text"] += iter_output_dict["oral_text"] + stream_output_dict["code"] += iter_output_dict["code"] + if len(invalid_details) == 0: + invalid_details.extend(self._check_text_and_code(component_case, stream_output_dict)) + except Exception as e: + invalid_details.append("ToolEval执行失败: {}".format(e)) + + time.sleep(2) + try: #校验非流式输出 + non_stream_outputs = component_obj.non_stream_tool_eval(**input_dict) + non_stream_invalid_details = self._check_jsonschema(non_stream_outputs.model_dump(), output_json_schemas) + invalid_details.extend(["非流式" + error_message for error_message in non_stream_invalid_details]) + if len(invalid_details) == 0: + non_stream_output_dict = self._gather_iter_outputs(non_stream_outputs) + invalid_details.extend(self._check_text_and_code(component_case, non_stream_output_dict)) + except Exception as e: + invalid_details.append(" NonStreamToolEval执行失败: {}".format(e)) if len(invalid_details) > 0: return CheckInfo( diff --git a/python/tests/component_tool_eval_cases.py b/python/tests/component_tool_eval_cases.py index 0675eca61..fc71514dc 100644 --- a/python/tests/component_tool_eval_cases.py +++ b/python/tests/component_tool_eval_cases.py @@ -11,8 +11,27 @@ # 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 appbuilder.core.component import Component +from appbuilder.tests.component_schemas import text_schema, url_schema, image_schema, code_schema, file_schema, oral_text_schema, references_schema, chart_schema, audio_schema, plan_schema, function_call_schema -class AnimalRecognitionCase: +class Case(): + def init_args(self): + return {} + + def inputs(self): + return NotImplementedError() + + def outputs(self): + return {} + + def schemas(self): + return NotImplementedError() + + def envs(self): + return {} + + +class AnimalRecognitionCase(Case): def inputs(self): return { "img_name": "", @@ -24,7 +43,10 @@ def inputs(self): def outputs(self): return {"text": ["熊猫"]} -class ASRCase: + def schemas(self): + return [text_schema] + +class ASRCase(Case): def inputs(self): return { "file_url": "https://bj.bcebos.com/v1/appbuilder/asr_test.pcm?authorization=bce-auth-v1" \ @@ -34,11 +56,17 @@ def inputs(self): def outputs(self): return {"text": ["北京科技馆"]} -class TreeMindCase: + def schemas(self): + return [text_schema] + +class TreeMindCase(Case): def inputs(self): return {"query": "生成一份年度总结的思维导图"} + + def schemas(self): + return [text_schema, url_schema] -class ImageUnderstandCase: +class ImageUnderstandCase(Case): def inputs(self): return { "img_url": "https://bj.bcebos.com/v1/appbuilder/animal_recognize_test.png?" \ @@ -50,6 +78,16 @@ def inputs(self): def outputs(self): return {"text": ["熊猫"]} + def schemas(self): + return [text_schema] + +class Text2ImageCase(Case): + def inputs(self): + return {"query": "生成一张熊猫图片"} + + def schemas(self): + return [url_schema] + component_tool_eval_cases = { "AnimalRecognition": AnimalRecognitionCase, "ImageUnderstand": ImageUnderstandCase, diff --git a/python/tests/component_tool_eval_schemas.py b/python/tests/component_tool_eval_schemas.py deleted file mode 100644 index 972aaabbb..000000000 --- a/python/tests/component_tool_eval_schemas.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) 2024 Baidu, Inc. 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 appbuilder.tests.component_schemas import text_schema, url_schema, image_schema, code_schema, file_schema, oral_text_schema, references_schema, chart_schema, audio_schema, plan_schema, function_call_schema - -components_tool_eval_output_json_maps = { - "AnimalRecognition": [text_schema], - "ASR": [text_schema], - "TreeMind": [text_schema, url_schema], - "ImageUnderstand": [text_schema] -} \ No newline at end of file From 4134c33422ef798ede88c79559f81a8f089e7408 Mon Sep 17 00:00:00 2001 From: userpj Date: Wed, 27 Nov 2024 20:24:32 +0800 Subject: [PATCH 41/85] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=8F=91=E7=89=88?= =?UTF-8?q?=E5=8D=95=E6=B5=8B=E7=9A=84=E5=85=BC=E5=AE=B9=E6=80=A7=E9=97=AE?= =?UTF-8?q?=E9=A2=98=20(#623)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 6e7f30321..8206daad9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,4 +12,5 @@ opentelemetry-instrumentation>=0.44b0 opentelemetry-sdk>=1.23.0 opentelemetry-api>=1.23.0 pydub>=0.10.0 -ffmpeg \ No newline at end of file +ffmpeg +urllib3<2.0.0 \ No newline at end of file From f95ca5637d776b58c0838a2713a842c55d4e8ce5 Mon Sep 17 00:00:00 2001 From: peiwenYe <963623403@qq.com> Date: Wed, 27 Nov 2024 20:25:32 +0800 Subject: [PATCH 42/85] =?UTF-8?q?=E4=BF=AE=E6=94=B9tool=5Feval=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E5=92=8Cmanifests=E5=8F=82=E6=95=B0=E5=8C=B9=E9=85=8D?= =?UTF-8?q?=E6=A3=80=E6=B5=8B=E6=96=B9=E6=B3=95=20(#622)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * create_output变更为classmethod方法,component_tool_eval_schemas移动到case中 * create_output变更为classmethod方法,component_tool_eval_schemas移动到case中 * create_output变更为classmethod方法,component_tool_eval_schemas移动到case中 * create_output变更为classmethod方法,component_tool_eval_schemas移动到case中 * 修改tool_eval和manifests参数校验规则 --------- Co-authored-by: yepeiwen01 --- python/tests/component_check.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/tests/component_check.py b/python/tests/component_check.py index fc4d86767..ecbeb018a 100644 --- a/python/tests/component_check.py +++ b/python/tests/component_check.py @@ -148,6 +148,7 @@ def check(self, component_cls) -> CheckInfo: raise ValueError("No manifests found") manifest = manifests[0] properties = manifest['parameters']['properties'] + manifest_var = properties.keys() required_params = [] anyOf = manifest['parameters'].get('anyOf', None) required_exists = False @@ -179,7 +180,7 @@ def check(self, component_cls) -> CheckInfo: if param_name == 'kwargs' or param_name == 'args' or param_name == 'self': continue tool_eval_input_params.append(param_name) - if param_name not in required_params: + if param_name not in manifest_var: check_pass_flag = False ileagal_params.append(param_name) From ec0d46cdcab8b46bc7638666fdde66cbb86245fe Mon Sep 17 00:00:00 2001 From: userpj Date: Wed, 27 Nov 2024 20:54:26 +0800 Subject: [PATCH 43/85] Update for version 0.9.7 (#624) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 修复发版单测的兼容性问题 * Update for version 0.9.7 * update java && go --- README.md | 2 +- docs/DevelopGuide/ChangeLog/changelog.md | 6 +++++- docs/DevelopGuide/HowToContributeCode/README.md | 2 +- .../StartFirstAINativeApplication/install.md | 12 ++++++------ docs/README_en.md | 2 +- docs/README_ja.md | 2 +- docs/Tools/SphinxSh/source/conf.py | 2 +- go/appbuilder/config.go | 2 +- java/pom.xml | 2 +- .../appbuilder/base/utils/http/HttpClient.java | 8 ++++---- python/__init__.py | 2 +- python/utils/bce_deploy.py | 4 ++-- setup.py | 2 +- 13 files changed, 26 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 2342f814a..efe0247cf 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ AppBuilder-SDK不仅提供了百度智能云提供的基础能力组件,同时 ## 如何安装AppBuilder-SDK -#### 百度智能云千帆AppBuilder-SDK 最新版本 0.9.6 (2024-10-26) +#### 百度智能云千帆AppBuilder-SDK 最新版本 0.9.7 (2024-11-27) 百度智能云千帆AppBuilder-SDK 更新记录&最新特性请查阅我们的[版本说明](/docs/DevelopGuide/ChangeLog/changelog.md) diff --git a/docs/DevelopGuide/ChangeLog/changelog.md b/docs/DevelopGuide/ChangeLog/changelog.md index 71dbee66b..18169794e 100644 --- a/docs/DevelopGuide/ChangeLog/changelog.md +++ b/docs/DevelopGuide/ChangeLog/changelog.md @@ -63,4 +63,8 @@ * 更新AppBuilderClient,简化Java & Go语言使用ToolCall的方式 * 新增长文档内容理解组件 * 优化requirements,去除部分组件的版本限制 - * 简化报错堆栈,去除冗余的Trace信息 \ No newline at end of file + * 简化报错堆栈,去除冗余的Trace信息 +* **2024.11.27 v0.9.7版本发布** [ReleaseNote](https://github.com/baidubce/app-builder/releases/tag/0.9.7) + * 新增TreeMind组件 + * 新增工作流Agent回复“信息收集节点”功能,支持多轮对话事件处理 + * Python的ToolCall功能支持通过函数定义、装饰器的形式等生成ToolCall参数 diff --git a/docs/DevelopGuide/HowToContributeCode/README.md b/docs/DevelopGuide/HowToContributeCode/README.md index 6c96406e8..9be7f6d31 100644 --- a/docs/DevelopGuide/HowToContributeCode/README.md +++ b/docs/DevelopGuide/HowToContributeCode/README.md @@ -10,7 +10,7 @@ 当前已集成Python版本AppBuilder-SDK 0.9.4及相关依赖,方便开发者融入个人已有的大模型应用程序。此部分仍在不断建设中。 二次开发可以采用官方提供的开发镜像,便于快速安装各种依赖库。也可在镜像中使用已安装的`appbuilder_trace_server`、`appbuilder_bce_deploy`工具。 ``` shell -docker pull registry.baidubce.com/appbuilder/appbuilder-sdk-devel:0.9.6 +docker pull registry.baidubce.com/appbuilder/appbuilder-sdk-devel:0.9.7 ``` ### 消息(Message) diff --git a/docs/QuickStart/StartFirstAINativeApplication/install.md b/docs/QuickStart/StartFirstAINativeApplication/install.md index 99dbe93e5..e87a66efc 100644 --- a/docs/QuickStart/StartFirstAINativeApplication/install.md +++ b/docs/QuickStart/StartFirstAINativeApplication/install.md @@ -16,23 +16,23 @@ pip install --upgrade appbuilder-sdk com.baidubce appbuilder - 0.9.6 + 0.9.7 ``` #### Gradle 对于Kotlin DSL,在build.gradle.kts的dependencies中添加依赖 ```kotlin -implementation("com.baidubce:appbuilder:0.9.6") +implementation("com.baidubce:appbuilder:0.9.7") ``` 对于Groovy DSL,在build.gradle的dependencies中添加依赖 ```groovy -implementation 'com.baidubce:appbuilder:0.9.6' +implementation 'com.baidubce:appbuilder:0.9.7' ``` #### 本地导入 -点击[链接](https://repo1.maven.org/maven2/com/baidubce/appbuilder/0.9.6/appbuilder-0.9.6.jar) 下载Jar包,将Jar包导入到项目目录下。 +点击[链接](https://repo1.maven.org/maven2/com/baidubce/appbuilder/0.9.7/appbuilder-0.9.7.jar) 下载Jar包,将Jar包导入到项目目录下。 ### Go (仅支持调用端到端应用) -> 支持Go 1.18.1以上版本,当前最新tag为0.9.8 +> 支持Go 1.18.1以上版本,当前最新tag为0.9.9 ```shell go get github.com/baidubce/app-builder/go/appbuilder @@ -40,5 +40,5 @@ go get github.com/baidubce/app-builder/go/appbuilder ### Docker (当前仅集成了Python版本AppBuilder-SDK) ``` shell -docker pull registry.baidubce.com/appbuilder/appbuilder-sdk-devel:0.9.6 +docker pull registry.baidubce.com/appbuilder/appbuilder-sdk-devel:0.9.7 ``` diff --git a/docs/README_en.md b/docs/README_en.md index bd3eaeb3f..be73a170a 100644 --- a/docs/README_en.md +++ b/docs/README_en.md @@ -47,7 +47,7 @@ Baidu AI Cloud Qianfan AppBuilder-SDK offers the following essential features fo ## How to install? -#### The latest version of Baidu AI Cloud Qianfan AppBuilder SDK is 0.9.6 (2024-10-26) +#### The latest version of Baidu AI Cloud Qianfan AppBuilder SDK is 0.9.7 (2024-11-27) Baidu AI Cloud Qianfan AppBuilder SDK ReleaseNote please refer to our [version description](/docs/DevelopGuide/ChangeLog/changelog.md) diff --git a/docs/README_ja.md b/docs/README_ja.md index 7f2bb10a9..fcbb5d41a 100644 --- a/docs/README_ja.md +++ b/docs/README_ja.md @@ -44,7 +44,7 @@ Baidu AI Cloud Qianfan AppBuilder-SDKは、AIアプリケーション開発者 ## どのようにインストールしますか? -#### Baidu AI Cloud Qianfan AppBuilder SDKの最新バージョンは0.9.6(2024-10-26)です +#### Baidu AI Cloud Qianfan AppBuilder SDKの最新バージョンは0.9.7(2024-11-27)です Baidu AI Cloud Qianfan AppBuilder SDKのリリースノートについては、[バージョン説明](DevelopGuide/ChangeLog/changelog.md)をご覧ください。 diff --git a/docs/Tools/SphinxSh/source/conf.py b/docs/Tools/SphinxSh/source/conf.py index 5b376cfcf..166fc9162 100644 --- a/docs/Tools/SphinxSh/source/conf.py +++ b/docs/Tools/SphinxSh/source/conf.py @@ -14,7 +14,7 @@ project = 'Appbuilder-SDK' copyright = '2024, baidubce' author = 'baidubce' -release = '0.9.6' +release = '0.9.7' # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration diff --git a/go/appbuilder/config.go b/go/appbuilder/config.go index 0581e9908..15247d115 100644 --- a/go/appbuilder/config.go +++ b/go/appbuilder/config.go @@ -127,7 +127,7 @@ func (t *SDKConfig) authHeader() http.Header { platform = "unknown" } header.Set("X-Appbuilder-Origin", "appbuilder_sdk") - header.Set("X-Appbuilder-Sdk-Config", "{\"appbuilder_sdk_version\":\"0.9.8\",\"appbuilder_sdk_language\":\"go\",\"appbuilder_sdk_platform\":\""+platform+"\"}") + header.Set("X-Appbuilder-Sdk-Config", "{\"appbuilder_sdk_version\":\"0.9.9\",\"appbuilder_sdk_language\":\"go\",\"appbuilder_sdk_platform\":\""+platform+"\"}") header.Set("X-Appbuilder-Request-Id", uuid.New().String()) return header } diff --git a/java/pom.xml b/java/pom.xml index 0f8c24880..0f30a70a5 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -6,7 +6,7 @@ com.baidubce appbuilder - 0.9.6 + 0.9.7 jar app-builder diff --git a/java/src/main/java/com/baidubce/appbuilder/base/utils/http/HttpClient.java b/java/src/main/java/com/baidubce/appbuilder/base/utils/http/HttpClient.java index 73bf9082f..507473f48 100644 --- a/java/src/main/java/com/baidubce/appbuilder/base/utils/http/HttpClient.java +++ b/java/src/main/java/com/baidubce/appbuilder/base/utils/http/HttpClient.java @@ -105,7 +105,7 @@ public ClassicHttpRequest createPostRequest(String url, HttpEntity entity) { ? System.getenv("APPBUILDER_SDK_PLATFORM") : "unknown"; httpPost.setHeader("X-Appbuilder-Sdk-Config", - "{\"appbuilder_sdk_version\":\"0.9.6\",\"appbuilder_sdk_language\":\"java\",\"appbuilder_sdk_platform\":\"" + "{\"appbuilder_sdk_version\":\"0.9.7\",\"appbuilder_sdk_language\":\"java\",\"appbuilder_sdk_platform\":\"" + platform + "\"}"); httpPost.setHeader("X-Appbuilder-Request-Id", java.util.UUID.randomUUID().toString()); httpPost.setEntity(entity); @@ -134,7 +134,7 @@ public ClassicHttpRequest createPostRequestV2(String url, HttpEntity entity) { ? System.getenv("APPBUILDER_SDK_PLATFORM") : "unknown"; httpPost.setHeader("X-Appbuilder-Sdk-Config", - "{\"appbuilder_sdk_version\":\"0.9.6\",\"appbuilder_sdk_language\":\"java\",\"appbuilder_sdk_platform\":\"" + "{\"appbuilder_sdk_version\":\"0.9.7\",\"appbuilder_sdk_language\":\"java\",\"appbuilder_sdk_platform\":\"" + platform + "\"}"); httpPost.setHeader("X-Appbuilder-Request-Id", java.util.UUID.randomUUID().toString()); httpPost.setEntity(entity); @@ -158,7 +158,7 @@ public ClassicHttpRequest createGetRequestV2(String url, Map map ? System.getenv("APPBUILDER_SDK_PLATFORM") : "unknown"; httpGet.setHeader("X-Appbuilder-Sdk-Config", - "{\"appbuilder_sdk_version\":\"0.9.6\",\"appbuilder_sdk_language\":\"java\",\"appbuilder_sdk_platform\":\"" + "{\"appbuilder_sdk_version\":\"0.9.7\",\"appbuilder_sdk_language\":\"java\",\"appbuilder_sdk_platform\":\"" + platform + "\"}"); httpGet.setHeader("X-Appbuilder-Request-Id", java.util.UUID.randomUUID().toString()); String headers = "headers: \n"; @@ -184,7 +184,7 @@ public ClassicHttpRequest createDeleteRequestV2(String url, Map ? System.getenv("APPBUILDER_SDK_PLATFORM") : "unknown"; httpDelete.setHeader("X-Appbuilder-Sdk-Config", - "{\"appbuilder_sdk_version\":\"0.9.6\",\"appbuilder_sdk_language\":\"java\",\"appbuilder_sdk_platform\":\"" + "{\"appbuilder_sdk_version\":\"0.9.7\",\"appbuilder_sdk_language\":\"java\",\"appbuilder_sdk_platform\":\"" + platform + "\"}"); httpDelete.setHeader("X-Appbuilder-Request-Id", java.util.UUID.randomUUID().toString()); String headers = "headers: \n"; diff --git a/python/__init__.py b/python/__init__.py index c9796bb84..8fac7ff3a 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -13,7 +13,7 @@ # limitations under the License. -__version__ = '0.9.6' +__version__ = '0.9.7' import os import sys diff --git a/python/utils/bce_deploy.py b/python/utils/bce_deploy.py index 027207ab4..617b2060b 100644 --- a/python/utils/bce_deploy.py +++ b/python/utils/bce_deploy.py @@ -113,8 +113,8 @@ def build_user_data(self): + f"rm {self.tar_file_name}\\n" + f"chmod a+x {self.run_script_name}\\n" + "yum install -y docker\\n" - + "docker pull registry.baidubce.com/appbuilder/appbuilder-sdk-cloud:0.9.6\\n" - + f"docker run -itd --net=host -v /root/test:{workspace} --name appbuilder-sdk registry.baidubce.com/appbuilder/appbuilder-sdk-cloud:0.9.6{workspace}/{self.run_script_name}" + + "docker pull registry.baidubce.com/appbuilder/appbuilder-sdk-cloud:0.9.7\\n" + + f"docker run -itd --net=host -v /root/test:{workspace} --name appbuilder-sdk registry.baidubce.com/appbuilder/appbuilder-sdk-cloud:0.9.7{workspace}/{self.run_script_name}" ) return user_data diff --git a/setup.py b/setup.py index 71be5f788..3fe0c4edc 100755 --- a/setup.py +++ b/setup.py @@ -54,7 +54,7 @@ setup( name="appbuilder-sdk", # NOTE(chengmo): 修改此版本号时,请注意同时修改 __init__.py 中的 __version__ - version="0.9.6", + version="0.9.7", author="dongdaxiang", author_email="dongdaxiang@baidu.com", packages=packages, From ff075fdcca07bd1fe4b33259b6d33ca3642ce1d8 Mon Sep 17 00:00:00 2001 From: userpj Date: Thu, 28 Nov 2024 16:50:16 +0800 Subject: [PATCH 44/85] =?UTF-8?q?=E5=B7=A5=E4=BD=9C=E6=B5=81Agent=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E6=96=87=E6=A1=A3=E3=80=81cookbook=E5=AE=8C=E5=96=84?= =?UTF-8?q?=20(#625)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 工作流Agent功能文档、cookbook完善 * add go docs * update cookbook * update * update * update * update --- .../end2end_application/agent/chatflow.ipynb | 356 +++++++++++ .../Platform/Application/appbuilder_client.md | 604 ++++++++++++++++-- go/appbuilder/app_builder_client_test.go | 95 ++- .../model/appbuilderclient/EventContent.java | 1 + .../appbuilder/AppBuilderClientTest.java | 72 ++- 5 files changed, 1062 insertions(+), 66 deletions(-) create mode 100644 cookbooks/end2end_application/agent/chatflow.ipynb diff --git a/cookbooks/end2end_application/agent/chatflow.ipynb b/cookbooks/end2end_application/agent/chatflow.ipynb new file mode 100644 index 000000000..c0d022d3d --- /dev/null +++ b/cookbooks/end2end_application/agent/chatflow.ipynb @@ -0,0 +1,356 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 前言 - 学习本项目你可以获得什么\n", + "- 入门百度智能云千帆AppBuilder,搭建一个工作流Agent应用\n", + "- 使用AppBuilder-SDK进行工作流Agent对话\n", + "- 了解如何使用AppBuilder-SDK简化多轮对话的开发" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1. 项目背景\n", + "\n", + "### 1.1、 什么是AppBuilder\n", + "[百度智能云千帆AppBuilder](https://appbuilder.cloud.baidu.com/)(以下简称AppBuilder)是基于大模型搭建AI原生应用的工作台,旨在降低AI原生应用的开发门槛,赋能开发者和企业快速实现应用搭建。\n", + "\n", + "平台提供了RAG(检索增强生成)、Agent(智能体)等应用框架,内置了文档问答、表格问答、多轮对话、生成创作等多种应用组件,还包括百度搜索和百度地图等特色组件,以及文本处理、图像处理和语音处理等传统AI组件,支持零代码、低代码、全代码三种开发方式,满足不同开发能力的开发者和企业的场景需求。\n", + "\n", + "### 1.2、 什么是AppBuilder-SDK\n", + "\n", + "[百度智能云千帆AppBuilder-SDK](https://github.com/baidubce/app-builder)(以下简称AB-SDK),百度智能云千帆AppBuilder-SDK是百度智能云千帆AppBuilder面向AI原生应用开发者提供的一站式开发平台的客户端SDK。\n", + "\n", + "\"drawing\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 2. 创建工作流Agent应用并对话\n", + "\n", + "## 2.1 什么是工作流Agent?\n", + "通过工作流编排的形式还原业务流程,每轮对话均严格按照工作流执行,提高了AI应用的可控性,并可编排出复杂业务流程,适用于客服、营销、生成、办公等高可控及高复杂度等场景。\n", + "\n", + "**工作流Agent无需设置角色人设,通过工作流编排的形式实现应用功能。**\n", + "\n", + "用户的所有对话均会触发此工作流处理,适用于严格按照流程执行的任务,例如:\n", + "\n", + "* 客服对话智能体,判断终端用户的意图后严格按照任务分支自动执行,无需大模型思考选择。\n", + "* MBTI小助手,根据开发者编排的问题逐个提问,严格按照任务分支和结果执行后续的流程。\n", + "\n", + "工作流Agent支持用户配置工作流完成整个应用的对话过程,通过开始节点的Rawquery传入首轮对话,利用信息收集节点可以进行一组工作流中的多轮对话,最后以结束节点作为整个工作流结束的标志。开始节点和结束节点之前的完整流程构成一组工作流,每个信息收集节点都可以支持终端用户和应用的一次交互。\n", + "\n", + "## 2.2 创建工作流Agent应用-飞行客服小助手\n", + "\n", + "参考产品文档[飞行客服小助手](https://cloud.baidu.com/doc/AppBuilder/s/cm38k8nqr)创建示例应用。通过阅读本篇最佳实践,读者可以深度理解问答节点、信息处理节点、全局跳转节点的功能点和使用方式,搭建自己的工作流agent。对话效果如下:\n", + "\n", + "

\n", + " \n", + " \n", + " \n", + "

\n", + "\n", + "## 2.3 使用AppBuilder-SDK进行对话\n", + "### 2.3.1 方式一:使用SDK直接对话(不推荐)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import appbuilder\n", + "import os\n", + "from appbuilder.core.console.appbuilder_client import data_class\n", + "\n", + "# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5\n", + "# 设置环境变量\n", + "os.environ[\"APPBUILDER_TOKEN\"] = \"...\"\n", + "appbuilder.logger.setLoglevel(\"ERROR\")\n", + "\n", + "# 飞行客服小助手的应用id\n", + "app_id = \"...\"\n", + "# 初始化智能体\n", + "client = appbuilder.AppBuilderClient(app_id)\n", + "# 创建会话\n", + "conversation_id = client.create_conversation()\n", + "interrupt_ids = []\n", + "\n", + "msg = client.run(conversation_id, \"查天气\", stream=True)\n", + "interrupt_event_id = None\n", + "for ans in msg.content:\n", + " for event in ans.events:\n", + " if event.content_type == \"publish_message\":\n", + " print(event.detail.get(\"message\"))\n", + " if event.content_type == \"chatflow_interrupt\":\n", + " interrupt_event_id = event.detail.get(\"interrupt_event_id\")\n", + " break\n", + "interrupt_ids.append(interrupt_event_id)\n", + "\n", + "msg2 = client.run(\n", + " conversation_id,\n", + " \"查航班\",\n", + " stream=True,\n", + " action=data_class.Action.create_resume_action(interrupt_event_id),\n", + ")\n", + "interrupt_event_id = None\n", + "for ans in msg2.content:\n", + " for event in ans.events:\n", + " if event.content_type == \"publish_message\":\n", + " print(event.detail.get(\"message\"))\n", + " if event.content_type == \"chatflow_interrupt\":\n", + " interrupt_event_id = event.detail.get(\"interrupt_event_id\")\n", + " break\n", + " interrupt_ids.append(interrupt_event_id)\n", + "\n", + "msg3 = client.run(\n", + " conversation_id=conversation_id,\n", + " query=\"CA1234\",\n", + " stream=True,\n", + " action=data_class.Action.create_resume_action(interrupt_ids.pop()),\n", + ")\n", + "interrupt_event_id = None\n", + "for ans in msg3.content:\n", + " for event in ans.events:\n", + " if event.content_type == \"text\":\n", + " print(event.detail.get(\"text\"))\n", + " if event.content_type == \"chatflow_interrupt\":\n", + " interrupt_event_id = event.detail.get(\"interrupt_event_id\")\n", + " break\n", + "interrupt_ids.append(interrupt_event_id)\n", + "\n", + "msg4 = client.run(\n", + " conversation_id=conversation_id,\n", + " query=\"北京的\",\n", + " stream=True,\n", + " action=data_class.Action.create_resume_action(interrupt_ids.pop()),\n", + ")\n", + "has_multiple_dialog_event = False\n", + "for ans in msg4.content:\n", + " for event in ans.events:\n", + " if event.content_type == \"text\":\n", + " print(event.detail.get(\"text\"))\n", + " if event.content_type == \"multiple_dialog_event\":\n", + " has_multiple_dialog_event = True\n", + " break" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 2.3.2 方式二 实现自己的EventHandler,更方便地进行对话(推荐)\n", + "直接调用处理较为繁琐。SDK提供了使用AppBuilderEventHandler简化tool_call操作的功能。**继承AppBuilderEventHandler类,并实现针对各类型event的处理方法。**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import appbuilder\n", + "from appbuilder.core.console.appbuilder_client.event_handler import (\n", + " AppBuilderEventHandler,\n", + ")\n", + "\n", + "\n", + "class MyEventHandler(AppBuilderEventHandler):\n", + " def __init__(self):\n", + " super().__init__()\n", + " self.interrupt_ids = []\n", + "\n", + " def handle_content_type(self, run_context, run_response):\n", + " interrupt_event_id = None\n", + " event = run_response.events[-1]\n", + " if event.content_type == \"chatflow_interrupt\":\n", + " interrupt_event_id = event.detail.get(\"interrupt_event_id\")\n", + " if interrupt_event_id is not None:\n", + " self.interrupt_ids.append(interrupt_event_id)\n", + "\n", + " def _create_action(self):\n", + " if len(self.interrupt_ids) == 0:\n", + " return None\n", + " event_id = self.interrupt_ids.pop()\n", + " return {\n", + " \"action_type\": \"resume\",\n", + " \"parameters\": {\"interrupt_event\": {\"id\": event_id, \"type\": \"chat\"}},\n", + " }\n", + "\n", + " def run(self, query=None):\n", + " super().new_dialog(\n", + " query=query,\n", + " action=self._create_action(),\n", + " )\n", + "\n", + "\n", + "def main():\n", + " # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5\n", + " # 设置环境变量\n", + " os.environ[\"APPBUILDER_TOKEN\"] = \"...\"\n", + " appbuilder.logger.setLoglevel(\"DEBUG\")\n", + "\n", + " # 飞行客服小助手的应用id\n", + " app_id = \"...\"\n", + " # 初始化智能体\n", + " client = appbuilder.AppBuilderClient(app_id)\n", + " conversation_id = client.create_conversation()\n", + "\n", + " event_handler = MyEventHandler()\n", + " event_handler.init(\n", + " appbuilder_client=client,\n", + " conversation_id=conversation_id,\n", + " stream=True,\n", + " query=\"查天气\",\n", + " )\n", + " for data in event_handler:\n", + " pass\n", + " event_handler.run(\n", + " query=\"查航班\",\n", + " )\n", + " for data in event_handler:\n", + " pass\n", + " event_handler.run(\n", + " query=\"CA1234\",\n", + " )\n", + " for data in event_handler:\n", + " pass\n", + " event_handler.run(\n", + " query=\"北京的\",\n", + " )\n", + " for data in event_handler:\n", + " pass\n", + "\n", + "\n", + "if __name__ == \"__main__\":\n", + " main()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 2.3.3 方式三、实现自己的EventHandler,更方便地进行多轮对话(推荐)\n", + "SDK提供了run_multiple_dialog_with_handler方法来进行多轮对话,按需传入iterable对象(queries、actions等参数),即可一次调用完成多轮对话。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import appbuilder\n", + "from appbuilder.core.console.appbuilder_client.event_handler import (\n", + " AppBuilderEventHandler,\n", + ")\n", + "\n", + "\n", + "class MyEventHandler(AppBuilderEventHandler):\n", + " def __init__(self):\n", + " super().__init__()\n", + " self.interrupt_ids = []\n", + "\n", + " def handle_content_type(self, run_context, run_response):\n", + " interrupt_event_id = None\n", + " event = run_response.events[-1]\n", + " if event.content_type == \"chatflow_interrupt\":\n", + " interrupt_event_id = event.detail.get(\"interrupt_event_id\")\n", + " if interrupt_event_id is not None:\n", + " self.interrupt_ids.append(interrupt_event_id)\n", + "\n", + " def _create_action(self):\n", + " if len(self.interrupt_ids) == 0:\n", + " return None\n", + " event_id = self.interrupt_ids.pop()\n", + " return {\n", + " \"action_type\": \"resume\",\n", + " \"parameters\": {\"interrupt_event\": {\"id\": event_id, \"type\": \"chat\"}},\n", + " }\n", + "\n", + " def gen_action(self):\n", + " while True:\n", + " yield self._create_action()\n", + "\n", + "\n", + "def main():\n", + " # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5\n", + " # 设置环境变量\n", + " os.environ[\"APPBUILDER_TOKEN\"] = \"...\"\n", + " appbuilder.logger.setLoglevel(\"DEBUG\")\n", + "\n", + " # 飞行客服小助手的应用id\n", + " app_id = \"...\"\n", + " # 初始化智能体\n", + " client = appbuilder.AppBuilderClient(app_id)\n", + " conversation_id = client.create_conversation()\n", + "\n", + " queries = [\"查天气\", \"查航班\", \"CA1234\", \"北京的\"]\n", + " event_handler = MyEventHandler()\n", + " event_handler = client.run_multiple_dialog_with_handler(\n", + " conversation_id=conversation_id,\n", + " queries=queries,\n", + " event_handler=event_handler,\n", + " stream=True,\n", + " actions=event_handler.gen_action(),\n", + " )\n", + " for data in event_handler:\n", + " for ans in data:\n", + " pass\n", + "\n", + "\n", + "if __name__ == \"__main__\":\n", + " main()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 3、项目总结\n", + "\n", + "本项目最终完成了一个工作流Agent应用的创建,并使用Appbuilder-SDK进行对话功能。\n", + "\n", + "希望您可以不吝`Star`,给`AppBuilder-SDK`一些鼓励,期待您的`PR`,一起共建AIAgent生态。\n", + "\n", + "Github地址:https://github.com/baidubce/app-builder\n", + "\n", + "\"drawing\"\n", + "\n", + "最后,您也可以进入`AppBuilder-SDK`的WX交流群,和大家一起交流AppBuilder使用及开发心得。\n", + "\n", + "\"drawing\"" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "testenv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.20" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/BasisModule/Platform/Application/appbuilder_client.md b/docs/BasisModule/Platform/Application/appbuilder_client.md index 08dcc44ca..cf48bff57 100644 --- a/docs/BasisModule/Platform/Application/appbuilder_client.md +++ b/docs/BasisModule/Platform/Application/appbuilder_client.md @@ -60,29 +60,32 @@ AppBuilderClient组件支持调用在[百度智能云千帆AppBuilder](https://c #### 方法参数 -| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | -| --------------- | ------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- | -| conversation_id | String | 是 | 会话ID | | -| query | String | 否 | query问题内容 | "今天天气怎么样?" | -| file_ids | list[String] | 否 | 对话可引用的文档ID | | -| stream | Bool | 否 | 为true时则流式返回,为false时则一次性返回所有内容, 推荐设为true,降低首token时延 | False | -| end_user_id | String | 否 | 终端用户ID,限制6 - 64字符 | | -| tools | List[Tool] | 否 | 一个列表,其中每个字典对应一个工具的配置 | | -| tools[0] | Tool | 否 | 工具配置 | | +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +| --------------- | ------------------ | -------- | ------------------------------------------------------------ | ----------------- | +| conversation_id | String | 是 | 会话ID | | +| query | String | 否 | query问题内容 | "今天天气怎么样?" | +| file_ids | list[String] | 否 | 对话可引用的文档ID | | +| stream | Bool | 否 | 为true时则流式返回,为false时则一次性返回所有内容, 推荐设为true,降低首token时延 | False | +| end_user_id | String | 否 | 终端用户ID,限制6 - 64字符 | | +| tools | List[Tool] | 否 | 一个列表,其中每个字典对应一个工具的配置 | | +| tools[0] | Tool | 否 | 工具配置 | | | +type | String | 否 | 枚举:
**file_retrieval**: 知识库检索工具能够理解文档内容,支持用户针对文档内容的问答。
**code_interpreter**: 代码解释器, 代码解释器能够生成并执行代码,从而协助用户解决复杂问题,涵盖科学计算(包括普通数学计算题)、数据可视化、文件编辑处理(图片、PDF文档、视频、音频等)、文件格式转换(如WAV、MP3、text、SRT、PNG、jpg、MP4、GIF、MP3等)、数据分析&清洗&处理(文件以excel、csv格式为主)、机器学习&深度学习建模&自然语言处理等多个领域。
**function**: 支持fucntion call模式调用工具 | | -| +function | Function | 否 | Function工具描述
仅当**type为**`**function**` 时需要且必须填写 | | -| ++name | String | 否 | 函数名
只允许数字、大小写字母和中划线和下划线,最大长度为64个字符。一次运行中唯一。 | | -| ++description | String | 否 | 工具描述 | | -| ++parameters | Dict | 否 | 工具参数, json_schema格式 | | -| tool_outputs | List[ToolOutput] | 否 | 内容为本地的工具执行结果,以自然语言/json dump str描述 | | -| tool_outputs[0] | ToolOutput | 否 | 工具执行结果 | | -| +tool_call_id | String | 否 | 工具调用ID | | -| +output | String | 否 | 工具输出 | | -| tool_choice | ToolChoice | 否 | 控制大模型使用组件的方式,仅对自主规划Agent生效。 | | -| +type | String | 否 | auto/function,auto表示由LLM自动判断调什么组件;function表示由用户指定调用哪个组件。 | | -| +function | ToolChoiceFunction | 否 | 组件对象,包括组件的英文名称和入参 | | -| ++name | String | 否 | 组件的英文名称(唯一标识) | | -| ++input | String | 否 | 组件入参,当组件没有入参时填入空对象{} | | +| +function | Function | 否 | Function工具描述
仅当**type为**`**function**` 时需要且必须填写 | | +| ++name | String | 否 | 函数名
只允许数字、大小写字母和中划线和下划线,最大长度为64个字符。一次运行中唯一。 | | +| ++description | String | 否 | 工具描述 | | +| ++parameters | Dict | 否 | 工具参数, json_schema格式 | | +| tool_outputs | List[ToolOutput] | 否 | 内容为本地的工具执行结果,以自然语言/json dump str描述 | | +| tool_outputs[0] | ToolOutput | 否 | 工具执行结果 | | +| +tool_call_id | String | 否 | 工具调用ID | | +| +output | String | 否 | 工具输出 | | +| tool_choice | ToolChoice | 否 | 控制大模型使用组件的方式,仅对自主规划Agent生效。 | | +| +type | String | 否 | auto/function,auto表示由LLM自动判断调什么组件;function表示由用户指定调用哪个组件。 | | +| +function | ToolChoiceFunction | 否 | 组件对象,包括组件的英文名称和入参 | | +| ++name | String | 否 | 组件的英文名称(唯一标识) | | +| ++input | String | 否 | 组件入参,当组件没有入参时填入空对象{} | | +| action | Action | 否 | 对话时要进行的特殊操作。如回复工作流agent中“信息收集节点“的消息 | | +| +action_type | String | 是 | 要执行的操作。
可选值为:
resume:回复“信息收集节点” 的消息 | | +| +parameters | Object | 是 | 执行操作时所需的参数 | | #### Run方法非流式返回值 @@ -443,6 +446,244 @@ answer = app_builder_client.run( ) ``` + + +#### Run方法回复工作流Agent “信息收集节点”使用示例: + +使用[“飞行客服小助手”](https://cloud.baidu.com/doc/AppBuilder/s/cm38k8nqr)作为工作流Agent的示例应用。 + +**方式1:** SDK直接进行对话(不推荐) + +```python +import appbuilder +import os +from appbuilder.core.console.appbuilder_client import data_class + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +# 设置环境变量 +os.environ["APPBUILDER_TOKEN"] = ( + "..." +) +appbuilder.logger.setLoglevel("ERROR") + +# 飞行客服小助手的应用id +app_id = "..." +# 初始化智能体 +client = appbuilder.AppBuilderClient(app_id) +# 创建会话 +conversation_id = client.create_conversation() +interrupt_ids = [] + +msg = client.run(conversation_id, "查天气", stream=True) +interrupt_event_id = None +for ans in msg.content: + for event in ans.events: + if event.content_type == "publish_message": + print(event.detail.get("message")) + if event.content_type == "chatflow_interrupt": + interrupt_event_id = event.detail.get("interrupt_event_id") + break +interrupt_ids.append(interrupt_event_id) + +msg2 = client.run( + conversation_id, + "查航班", + stream=True, + action=data_class.Action.create_resume_action(interrupt_event_id), +) +interrupt_event_id = None +for ans in msg2.content: + for event in ans.events: + if event.content_type == "publish_message": + print(event.detail.get("message")) + if event.content_type == "chatflow_interrupt": + interrupt_event_id = event.detail.get("interrupt_event_id") + break + interrupt_ids.append(interrupt_event_id) + +msg3 = client.run( + conversation_id=conversation_id, + query="CA1234", + stream=True, + action=data_class.Action.create_resume_action(interrupt_ids.pop()), +) +interrupt_event_id = None +for ans in msg3.content: + for event in ans.events: + if event.content_type == "text": + print(event.detail.get("text")) + if event.content_type == "chatflow_interrupt": + interrupt_event_id = event.detail.get("interrupt_event_id") + break +interrupt_ids.append(interrupt_event_id) + +msg4 = client.run( + conversation_id=conversation_id, + query="北京的", + stream=True, + action=data_class.Action.create_resume_action(interrupt_ids.pop()), +) +has_multiple_dialog_event = False +for ans in msg4.content: + for event in ans.events: + if event.content_type == "text": + print(event.detail.get("text")) + if event.content_type == "multiple_dialog_event": + has_multiple_dialog_event = True + break + +``` + +**方式2:** 实现自己的EventHandler,更方便地进行对话(推荐) + +```python +import os +import appbuilder +from appbuilder.core.console.appbuilder_client.event_handler import ( + AppBuilderEventHandler, +) + + +class MyEventHandler(AppBuilderEventHandler): + def __init__(self): + super().__init__() + self.interrupt_ids = [] + + def handle_content_type(self, run_context, run_response): + interrupt_event_id = None + event = run_response.events[-1] + if event.content_type == "chatflow_interrupt": + interrupt_event_id = event.detail.get("interrupt_event_id") + if interrupt_event_id is not None: + self.interrupt_ids.append(interrupt_event_id) + + def _create_action(self): + if len(self.interrupt_ids) == 0: + return None + event_id = self.interrupt_ids.pop() + return { + "action_type": "resume", + "parameters": {"interrupt_event": {"id": event_id, "type": "chat"}}, + } + + def run(self, query=None): + super().new_dialog( + query=query, + action=self._create_action(), + ) + + +def main(): + # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 + # 设置环境变量 + os.environ["APPBUILDER_TOKEN"] = "..." + appbuilder.logger.setLoglevel("DEBUG") + + # 飞行客服小助手的应用id + app_id = "..." + # 初始化智能体 + client = appbuilder.AppBuilderClient(app_id) + conversation_id = client.create_conversation() + + event_handler = MyEventHandler() + event_handler.init( + appbuilder_client=client, + conversation_id=conversation_id, + stream=True, + query="查天气", + ) + for data in event_handler: + pass + event_handler.run( + query="查航班", + ) + for data in event_handler: + pass + event_handler.run( + query="CA1234", + ) + for data in event_handler: + pass + event_handler.run( + query="北京的", + ) + for data in event_handler: + pass + + +if __name__ == "__main__": + main() +``` + +**方式3:** 实现自己的EventHandler,更方便地进行多轮对话(推荐) + +```python +import os +import appbuilder +from appbuilder.core.console.appbuilder_client.event_handler import ( + AppBuilderEventHandler, +) + + +class MyEventHandler(AppBuilderEventHandler): + def __init__(self): + super().__init__() + self.interrupt_ids = [] + + def handle_content_type(self, run_context, run_response): + interrupt_event_id = None + event = run_response.events[-1] + if event.content_type == "chatflow_interrupt": + interrupt_event_id = event.detail.get("interrupt_event_id") + if interrupt_event_id is not None: + self.interrupt_ids.append(interrupt_event_id) + + def _create_action(self): + if len(self.interrupt_ids) == 0: + return None + event_id = self.interrupt_ids.pop() + return { + "action_type": "resume", + "parameters": {"interrupt_event": {"id": event_id, "type": "chat"}}, + } + + def gen_action(self): + while True: + yield self._create_action() + + +def main(): + # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 + # 设置环境变量 + os.environ["APPBUILDER_TOKEN"] = "..." + appbuilder.logger.setLoglevel("DEBUG") + + # 飞行客服小助手的应用id + app_id = "..." + # 初始化智能体 + client = appbuilder.AppBuilderClient(app_id) + conversation_id = client.create_conversation() + + queries = ["查天气", "查航班", "CA1234", "北京的"] + event_handler = MyEventHandler() + event_handler = client.run_multiple_dialog_with_handler( + conversation_id=conversation_id, + queries=queries, + event_handler=event_handler, + stream=True, + actions=event_handler.gen_action(), + ) + for data in event_handler: + for ans in data: + pass + + +if __name__ == "__main__": + main() +``` + + + ## Java基本用法 ### ```new AppBuilderClient(appId)``` @@ -473,27 +714,30 @@ answer = app_builder_client.run( #### Run方法入参`AppBuilderCientRunRequest` -| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | -| --------------- | ------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | -| query | String | 是 | query内容 | "汽车性能参数怎么样" | -| conversationId | String | 是 | 对话id,可以通过createConversation()获取 | | -| stream | boolean | 是 | 为true时则流式返回,为false时则一次性返回所有内容, 推荐设为true,降低首token时延 | | -| tools | List[Tool] | 否 | 一个列表,其中每个字典对应一个工具的配置 | | -| tools[0] | Tool | 否 | 工具配置 | | +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +| --------------- | ------------------ | -------- | ------------------------------------------------------------ | -------------------- | +| query | String | 是 | query内容 | "汽车性能参数怎么样" | +| conversationId | String | 是 | 对话id,可以通过createConversation()获取 | | +| stream | boolean | 是 | 为true时则流式返回,为false时则一次性返回所有内容, 推荐设为true,降低首token时延 | | +| tools | List[Tool] | 否 | 一个列表,其中每个字典对应一个工具的配置 | | +| tools[0] | Tool | 否 | 工具配置 | | | +type | String | 否 | 枚举:
**file_retrieval**: 知识库检索工具能够理解文档内容,支持用户针对文档内容的问答。
**code_interpreter**: 代码解释器, 代码解释器能够生成并执行代码,从而协助用户解决复杂问题,涵盖科学计算(包括普通数学计算题)、数据可视化、文件编辑处理(图片、PDF文档、视频、音频等)、文件格式转换(如WAV、MP3、text、SRT、PNG、jpg、MP4、GIF、MP3等)、数据分析&清洗&处理(文件以excel、csv格式为主)、机器学习&深度学习建模&自然语言处理等多个领域。
**function**: 支持fucntion call模式调用工具 | | -| +function | Function | 否 | Function工具描述
仅当**type为**`**function**` 时需要且必须填写 | | -| ++name | String | 否 | 函数名
只允许数字、大小写字母和中划线和下划线,最大长度为64个字符。一次运行中唯一。 | | -| ++description | String | 否 | 工具描述 | | -| ++parameters | Dict | 否 | 工具参数, json_schema格式 | | -| tool_outputs | List[ToolOutput] | 否 | 内容为本地的工具执行结果,以自然语言/json dump str描述 | | -| tool_outputs[0] | ToolOutput | 否 | 工具执行结果 | | -| +tool_call_id | String | 否 | 工具调用ID | | -| +output | String | 否 | 工具输出 | | -| tool_choice | ToolChoice | 否 | 控制大模型使用组件的方式,仅对自主规划Agent生效。 | | -| +type | String | 否 | auto/function,auto表示由LLM自动判断调什么组件;function表示由用户指定调用哪个组件。 | | -| +function | ToolChoiceFunction | 否 | 组件对象,包括组件的英文名称和入参 | | -| ++name | String | 否 | 组件的英文名称(唯一标识) | | -| ++input | String | 否 | 组件入参,当组件没有入参时填入空对象{} | | +| +function | Function | 否 | Function工具描述
仅当**type为**`**function**` 时需要且必须填写 | | +| ++name | String | 否 | 函数名
只允许数字、大小写字母和中划线和下划线,最大长度为64个字符。一次运行中唯一。 | | +| ++description | String | 否 | 工具描述 | | +| ++parameters | Dict | 否 | 工具参数, json_schema格式 | | +| tool_outputs | List[ToolOutput] | 否 | 内容为本地的工具执行结果,以自然语言/json dump str描述 | | +| tool_outputs[0] | ToolOutput | 否 | 工具执行结果 | | +| +tool_call_id | String | 否 | 工具调用ID | | +| +output | String | 否 | 工具输出 | | +| tool_choice | ToolChoice | 否 | 控制大模型使用组件的方式,仅对自主规划Agent生效。 | | +| +type | String | 否 | auto/function,auto表示由LLM自动判断调什么组件;function表示由用户指定调用哪个组件。 | | +| +function | ToolChoiceFunction | 否 | 组件对象,包括组件的英文名称和入参 | | +| ++name | String | 否 | 组件的英文名称(唯一标识) | | +| ++input | String | 否 | 组件入参,当组件没有入参时填入空对象{} | | +| action | Action | 否 | 对话时要进行的特殊操作。如回复工作流agent中“信息收集节点“的消息 | | +| +action_type | String | 是 | 要执行的操作。
可选值为:
resume:回复“信息收集节点” 的消息 | | +| +parameters | Object | 是 | 执行操作时所需的参数 | | #### Run方法出参 | 参数名称 | 参数类型 | 描述 | 示例值 | @@ -832,6 +1076,116 @@ class AppBuilderClientDemo { ``` +#### Run方法回复工作流Agent “信息收集节点”使用示例: + +使用[“飞行客服小助手”](https://cloud.baidu.com/doc/AppBuilder/s/cm38k8nqr)作为工作流Agent的示例应用 + +```java +package org.example; + +import java.io.IOException; +import java.util.*; + +import com.google.gson.annotations.SerializedName; + +import com.baidubce.appbuilder.base.exception.AppBuilderServerException; +import com.baidubce.appbuilder.console.appbuilderclient.AppBuilderClient; +import com.baidubce.appbuilder.model.appbuilderclient.AppBuilderClientIterator; +import com.baidubce.appbuilder.model.appbuilderclient.AppBuilderClientResult; +import com.baidubce.appbuilder.model.appbuilderclient.AppBuilderClientRunRequest; +import com.baidubce.appbuilder.model.appbuilderclient.Event; +import com.baidubce.appbuilder.base.utils.json.JsonUtils; + +class AppBuilderClientDemo { + + public static void main(String[] args) throws IOException, AppBuilderServerException { + System.setProperty("APPBUILDER_TOKEN", "请设置正确的应用密钥"); + String chatflowAppId = "请设置正确的应用ID"; + AppBuilderClient builder = new AppBuilderClient(chatflowAppId); + String conversationId = builder.createConversation(); + + AppBuilderClientRunRequest request = new AppBuilderClientRunRequest(chatflowAppId, conversationId, "查天气", true); + Stack interruptStack = new Stack(); + AppBuilderClientIterator itor = builder.run(request); + String interruptEventId = ""; + while (itor.hasNext()) { + AppBuilderClientResult result = itor.next(); + for (Event event : result.getEvents()) { + if (event.getContentType().equals(EventContent.PublishMessageContentType)) { + String message = event.getDetail().get("message").toString(); + System.out.println(message); + } + if (event.getContentType().equals(EventContent.ChatflowInterruptContentType)) { + interruptEventId = event.getDetail().get("interrupt_event_id").toString(); + interruptStack.push(interruptEventId); + break; + } + } + } + + interruptEventId = ""; + AppBuilderClientRunRequest request2 = new AppBuilderClientRunRequest(chatflowAppId, conversationId, "我先查个航班动态", true); + request2.setAction(AppBuilderClientRunRequest.Action.createAction(interruptStack.pop())); + AppBuilderClientIterator itor2 = builder.run(request2); + while (itor2.hasNext()) { + AppBuilderClientResult result2 = itor2.next(); + for (Event event : result2.getEvents()) { + if (event.getContentType().equals(EventContent.PublishMessageContentType)) { + String message = event.getDetail().get("message").toString(); + System.out.println(message); + } + if (event.getContentType().equals(EventContent.ChatflowInterruptContentType)) { + interruptEventId = event.getDetail().get("interrupt_event_id").toString(); + interruptStack.push(interruptEventId); + break; + } + } + } + + interruptEventId = ""; + AppBuilderClientRunRequest request3 = new AppBuilderClientRunRequest(chatflowAppId, conversationId, "CA1234", true); + request3.setAction(AppBuilderClientRunRequest.Action.createAction(interruptStack.pop())); + AppBuilderClientIterator itor3 = builder.run(request3); + while (itor3.hasNext()) { + AppBuilderClientResult result3 = itor3.next(); + for (Event event : result3.getEvents()) { + if (event.getContentType().equals(EventContent.TextContentType)) { + String text = event.getDetail().get("text").toString(); + System.out.println(text); + } + if (event.getContentType().equals(EventContent.ChatflowInterruptContentType)) { + interruptEventId = event.getDetail().get("interrupt_event_id").toString(); + interruptStack.push(interruptEventId); + break; + } + } + } + + boolean hasMultipleContentType = false; + AppBuilderClientRunRequest request4 = new AppBuilderClientRunRequest(chatflowAppId, conversationId, "北京的", + true); + request4.setAction(AppBuilderClientRunRequest.Action.createAction(interruptStack.pop())); + AppBuilderClientIterator itor4 = builder.run(request4); + while (itor4.hasNext()) { + AppBuilderClientResult result4 = itor4.next(); + for (Event event : result4.getEvents()) { + if (event.getContentType().equals(EventContent.TextContentType)) { + String text = event.getDetail().get("text").toString(); + System.out.println(text); + } + if (event.getContentType().equals(EventContent.MultipleDialogEventContentType)) { + hasMultipleContentType = true; + break; + } + } + } + } +} + +``` + + + ## Go基本用法 ### ```NewAppBuilderClient()``` @@ -1170,3 +1524,169 @@ func main() { } } ``` + +#### Run方法回复工作流Agent “信息收集节点”使用示例: + +使用[“飞行客服小助手”](https://cloud.baidu.com/doc/AppBuilder/s/cm38k8nqr)作为工作流Agent的示例应用 + +```go +package main + +import ( + "fmt" + "os" + + "github.com/baidubce/app-builder/go/appbuilder" +) + +func main() { + // 设置APPBUILDER_TOKEN、GATEWAY_URL_V2环境变量 + os.Setenv("APPBUILDER_TOKEN", "请设置正确的应用密钥") + // 默认可不填,默认值是 https://qianfan.baidubce.com + os.Setenv("GATEWAY_URL_V2", "") + config, err := appbuilder.NewSDKConfig("", "") + if err != nil { + fmt.Println("new config failed: ", err) + return + } + // 初始化实例 + appID := "请填写正确的应用ID" + client, err := appbuilder.NewAppBuilderClient(appID, config) + if err != nil { + fmt.Println("new agent builder failed: ", err) + return + } + // 创建对话ID + conversationID, err := client.CreateConversation() + if err != nil { + fmt.Println("create conversation failed: ", err) + return + } + i, err := client.Run(appbuilder.AppBuilderClientRunRequest{ + ConversationID: conversationID, + AppID: appID, + Query: "查天气", + Stream: true, + }) + + if err != nil { + fmt.Println("run failed: ", err) + return + } + + var interruptId string + interruptStack := make([]string, 0) + for answer, err := i.Next(); err == nil; answer, err = i.Next() { + for _, ev := range answer.Events { + if ev.ContentType == appbuilder.PublishMessageContentType { + detail := ev.Detail.(appbuilder.PublishMessageDetail) + message := detail.Message + fmt.Println(message) + break + } + if ev.ContentType == appbuilder.ChatflowInterruptContentType { + deatil := ev.Detail.(appbuilder.ChatflowInterruptDetail) + interruptId = deatil.InterruptEventID + interruptStack = append(interruptStack, interruptId) + break + } + } + } + if len(interruptId) == 0 { + fmt.Println("interrupt id is empty") + return + } + + interruptId = "" + i2, err := client.Run(appbuilder.AppBuilderClientRunRequest{ + ConversationID: conversationID, + AppID: appID, + Query: "我先查个航班动态", + Stream: true, + Action: appbuilder.NewResumeAction(interruptStack[len(interruptStack)-1]), + }) + if err != nil { + fmt.Println("run failed:", err) + return + } + interruptStack = interruptStack[:len(interruptStack)-1] + for answer, err := i2.Next(); err == nil; answer, err = i2.Next() { + for _, ev := range answer.Events { + if ev.ContentType == appbuilder.PublishMessageContentType { + detail := ev.Detail.(appbuilder.PublishMessageDetail) + message := detail.Message + fmt.Println(message) + break + } + if ev.ContentType == appbuilder.ChatflowInterruptContentType { + deatil := ev.Detail.(appbuilder.ChatflowInterruptDetail) + interruptId = deatil.InterruptEventID + interruptStack = append(interruptStack, interruptId) + break + } + } + } + if len(interruptId) == 0 { + fmt.Println("interrupt id is empty") + return + } + + interruptId = "" + i3, err := client.Run(appbuilder.AppBuilderClientRunRequest{ + ConversationID: conversationID, + AppID: appID, + Query: "CA1234", + Stream: true, + Action: appbuilder.NewResumeAction(interruptStack[len(interruptStack)-1]), + }) + if err != nil { + fmt.Println("run failed:", err) + return + } + interruptStack = interruptStack[:len(interruptStack)-1] + for answer, err := i3.Next(); err == nil; answer, err = i3.Next() { + for _, ev := range answer.Events { + if ev.ContentType == appbuilder.TextContentType { + detail := ev.Detail.(appbuilder.TextDetail) + text := detail.Text + fmt.Println(text) + break + } + if ev.ContentType == appbuilder.ChatflowInterruptContentType { + deatil := ev.Detail.(appbuilder.ChatflowInterruptDetail) + interruptId = deatil.InterruptEventID + interruptStack = append(interruptStack, interruptId) + break + } + } + } + if len(interruptId) == 0 { + fmt.Println("interrupt id is empty") + return + } + + i4, err := client.Run(appbuilder.AppBuilderClientRunRequest{ + ConversationID: conversationID, + AppID: appID, + Query: "北京的", + Stream: true, + Action: appbuilder.NewResumeAction(interruptStack[len(interruptStack)-1]), + }) + if err != nil { + fmt.Println("run failed:", err) + return + } + for answer, err := i4.Next(); err == nil; answer, err = i4.Next() { + for _, ev := range answer.Events { + if ev.ContentType == appbuilder.TextContentType { + detail := ev.Detail.(appbuilder.TextDetail) + text := detail.Text + fmt.Println(text) + break + } + } + } +} + +``` + diff --git a/go/appbuilder/app_builder_client_test.go b/go/appbuilder/app_builder_client_test.go index e6b817566..28141b9e1 100644 --- a/go/appbuilder/app_builder_client_test.go +++ b/go/appbuilder/app_builder_client_test.go @@ -648,58 +648,117 @@ func TestAppBuilderClientRunChatflow(t *testing.T) { } var interruptId string + interruptStack := make([]string, 0) for answer, err := i.Next(); err == nil; answer, err = i.Next() { for _, ev := range answer.Events { + if ev.ContentType == PublishMessageContentType { + detail := ev.Detail.(PublishMessageDetail) + message := detail.Message + fmt.Println(message) + break + } if ev.ContentType == ChatflowInterruptContentType { - if ev.EventType != ChatflowEventType { - t.Logf("%s========== FAIL: %s ==========%s", "\033[31m", t.Name(), "\033[0m") - t.Fatalf("event type error:%v", err) - } - deatil := ev.Detail.(ChatflowInterruptDetail) interruptId = deatil.InterruptEventID + interruptStack = append(interruptStack, interruptId) break } } } - if len(interruptId) == 0 { t.Logf("%s========== FAIL: %s ==========%s", "\033[31m", t.Name(), "\033[0m") t.Fatalf("interrupt id is empty") } + interruptId = "" i2, err := client.Run(AppBuilderClientRunRequest{ ConversationID: conversationID, AppID: appID, Query: "我先查个航班动态", Stream: true, - Action: NewResumeAction(interruptId), + Action: NewResumeAction(interruptStack[len(interruptStack)-1]), }) if err != nil { t.Logf("%s========== FAIL: %s ==========%s", "\033[31m", t.Name(), "\033[0m") t.Fatalf("run failed:%v", err) } - - var message string + interruptStack = interruptStack[:len(interruptStack)-1] for answer, err := i2.Next(); err == nil; answer, err = i2.Next() { for _, ev := range answer.Events { if ev.ContentType == PublishMessageContentType { - if ev.EventType != ChatflowEventType { - t.Logf("%s========== FAIL: %s ==========%s", "\033[31m", t.Name(), "\033[0m") - t.Fatalf("event type error:%v", err) - } - detail := ev.Detail.(PublishMessageDetail) - message = detail.Message + message := detail.Message + fmt.Println(message) + break + } + if ev.ContentType == ChatflowInterruptContentType { + deatil := ev.Detail.(ChatflowInterruptDetail) + interruptId = deatil.InterruptEventID + interruptStack = append(interruptStack, interruptId) + break + } + } + } + if len(interruptId) == 0 { + t.Logf("%s========== FAIL: %s ==========%s", "\033[31m", t.Name(), "\033[0m") + t.Fatalf("interrupt id is empty") + } + + interruptId = "" + i3, err := client.Run(AppBuilderClientRunRequest{ + ConversationID: conversationID, + AppID: appID, + Query: "CA1234", + Stream: true, + Action: NewResumeAction(interruptStack[len(interruptStack)-1]), + }) + if err != nil { + t.Logf("%s========== FAIL: %s ==========%s", "\033[31m", t.Name(), "\033[0m") + t.Fatalf("run failed:%v", err) + } + interruptStack = interruptStack[:len(interruptStack)-1] + for answer, err := i3.Next(); err == nil; answer, err = i3.Next() { + for _, ev := range answer.Events { + if ev.ContentType == TextContentType { + detail := ev.Detail.(TextDetail) + text := detail.Text + fmt.Println(text) + break + } + if ev.ContentType == ChatflowInterruptContentType { + deatil := ev.Detail.(ChatflowInterruptDetail) + interruptId = deatil.InterruptEventID + interruptStack = append(interruptStack, interruptId) break } } } - if len(message) == 0 { + if len(interruptId) == 0 { + t.Logf("%s========== FAIL: %s ==========%s", "\033[31m", t.Name(), "\033[0m") + t.Fatalf("interrupt id is empty") + } + + i4, err := client.Run(AppBuilderClientRunRequest{ + ConversationID: conversationID, + AppID: appID, + Query: "北京的", + Stream: true, + Action: NewResumeAction(interruptStack[len(interruptStack)-1]), + }) + if err != nil { t.Logf("%s========== FAIL: %s ==========%s", "\033[31m", t.Name(), "\033[0m") - t.Fatalf("message is empty") + t.Fatalf("run failed:%v", err) + } + for answer, err := i4.Next(); err == nil; answer, err = i4.Next() { + for _, ev := range answer.Events { + if ev.ContentType == TextContentType { + detail := ev.Detail.(TextDetail) + text := detail.Text + fmt.Println(text) + break + } + } } - fmt.Println(message) // 如果测试失败,则输出缓冲区中的日志 if t.Failed() { diff --git a/java/src/main/java/com/baidubce/appbuilder/model/appbuilderclient/EventContent.java b/java/src/main/java/com/baidubce/appbuilder/model/appbuilderclient/EventContent.java index 4c6a672af..1d0879177 100644 --- a/java/src/main/java/com/baidubce/appbuilder/model/appbuilderclient/EventContent.java +++ b/java/src/main/java/com/baidubce/appbuilder/model/appbuilderclient/EventContent.java @@ -15,6 +15,7 @@ public class EventContent { public static final String StatusContentType = "status"; public static final String ChatflowInterruptContentType = "chatflow_interrupt"; public static final String PublishMessageContentType = "publish_message"; + public static final String MultipleDialogEventContentType = "multiple_dialog_event"; @SerializedName("event_code") private String eventCode; diff --git a/java/src/test/java/com/baidubce/appbuilder/AppBuilderClientTest.java b/java/src/test/java/com/baidubce/appbuilder/AppBuilderClientTest.java index cb33dc655..61376b2b3 100644 --- a/java/src/test/java/com/baidubce/appbuilder/AppBuilderClientTest.java +++ b/java/src/test/java/com/baidubce/appbuilder/AppBuilderClientTest.java @@ -9,6 +9,8 @@ import java.nio.file.Files; import java.util.HashMap; import java.util.Map; +import java.util.Stack; + import com.baidubce.appbuilder.model.appbuilderclient.AppBuilderClientIterator; import com.baidubce.appbuilder.model.appbuilderclient.AppBuilderClientResult; import com.baidubce.appbuilder.model.appbuilderclient.AppListRequest; @@ -133,33 +135,91 @@ public void AppBuilderClientRunChatflowTest() throws IOException, AppBuilderServ AppBuilderClientRunRequest request = new AppBuilderClientRunRequest(chatflowAppId, conversationId, "查天气", true); AppBuilderClientIterator itor = builder.run(request); assertTrue(itor.hasNext()); + Stack interruptStack = new Stack(); String interruptEventId = ""; while (itor.hasNext()) { AppBuilderClientResult result = itor.next(); for (Event event : result.getEvents()) { - System.out.println(event.getContentType()); + if (event.getContentType().equals(EventContent.PublishMessageContentType)) { + String message = event.getDetail().get("message").toString(); + System.out.println(message); + } if (event.getContentType().equals(EventContent.ChatflowInterruptContentType)) { assertEquals(event.getEventType(), Event.ChatflowEventType); interruptEventId = event.getDetail().get("interrupt_event_id").toString(); + interruptStack.push(interruptEventId); + break; } } } - assert interruptEventId != null && !interruptEventId.isEmpty(); + + interruptEventId = ""; AppBuilderClientRunRequest request2 = new AppBuilderClientRunRequest(chatflowAppId, conversationId, "我先查个航班动态", true); - request2.setAction(AppBuilderClientRunRequest.Action.createAction(interruptEventId)); + request2.setAction(AppBuilderClientRunRequest.Action.createAction(interruptStack.pop())); AppBuilderClientIterator itor2 = builder.run(request2); assertTrue(itor2.hasNext()); - String message = ""; while (itor2.hasNext()) { AppBuilderClientResult result2 = itor2.next(); for (Event event : result2.getEvents()) { if (event.getContentType().equals(EventContent.PublishMessageContentType)) { - message = event.getDetail().get("message").toString(); + String message = event.getDetail().get("message").toString(); + System.out.println(message); + } + if (event.getContentType().equals(EventContent.ChatflowInterruptContentType)) { + assertEquals(event.getEventType(), Event.ChatflowEventType); + interruptEventId = event.getDetail().get("interrupt_event_id").toString(); + interruptStack.push(interruptEventId); + break; + } + } + } + assert interruptEventId != null && !interruptEventId.isEmpty(); + + interruptEventId = ""; + AppBuilderClientRunRequest request3 = new AppBuilderClientRunRequest(chatflowAppId, conversationId, "CA1234", + true); + request3.setAction(AppBuilderClientRunRequest.Action.createAction(interruptStack.pop())); + AppBuilderClientIterator itor3 = builder.run(request3); + assertTrue(itor3.hasNext()); + while (itor3.hasNext()) { + AppBuilderClientResult result3 = itor3.next(); + for (Event event : result3.getEvents()) { + if (event.getContentType().equals(EventContent.TextContentType)) { + String text = event.getDetail().get("text").toString(); + System.out.println(text); + } + if (event.getContentType().equals(EventContent.ChatflowInterruptContentType)) { + assertEquals(event.getEventType(), Event.ChatflowEventType); + interruptEventId = event.getDetail().get("interrupt_event_id").toString(); + interruptStack.push(interruptEventId); + break; + } + } + } + assert interruptEventId != null && !interruptEventId.isEmpty(); + + boolean hasMultipleContentType = false; + AppBuilderClientRunRequest request4 = new AppBuilderClientRunRequest(chatflowAppId, conversationId, "北京的", + true); + request4.setAction(AppBuilderClientRunRequest.Action.createAction(interruptStack.pop())); + AppBuilderClientIterator itor4 = builder.run(request4); + assertTrue(itor4.hasNext()); + while (itor4.hasNext()) { + AppBuilderClientResult result4 = itor4.next(); + for (Event event : result4.getEvents()) { + if (event.getContentType().equals(EventContent.TextContentType)) { + String text = event.getDetail().get("text").toString(); + System.out.println(text); + } + if (event.getContentType().equals(EventContent.MultipleDialogEventContentType)) { + assertEquals(event.getEventType(), Event.ChatflowEventType); + hasMultipleContentType = true; + break; } } } - assert message != null && !message.isEmpty(); + assertTrue(hasMultipleContentType); } } From 46ef4d34fe9e1490bdaea2801a6a5f75432fc529 Mon Sep 17 00:00:00 2001 From: userpj Date: Thu, 28 Nov 2024 19:51:43 +0800 Subject: [PATCH 45/85] fix component sample run failed (#627) --- README.md | 2 +- docs/README_en.md | 2 +- docs/README_ja.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index efe0247cf..191ce5b0a 100644 --- a/README.md +++ b/README.md @@ -157,7 +157,7 @@ import os # 设置环境中的TOKEN,以下TOKEN为访问和QPS受限的试用TOKEN,正式使用请替换为您的个人TOKEN os.environ["APPBUILDER_TOKEN"] = "bce-v3/ALTAK-n5AYUIUJMarF7F7iFXVeK/1bf65eed7c8c7efef9b11388524fa1087f90ea58" -rag_with_baidu_search_pro = appbuilder.RagWithBaiduSearchPro(model="Qianfan-Agent-Speed-8k") +rag_with_baidu_search_pro = appbuilder.RagWithBaiduSearchPro(model="ERNIE-Lite-AppBuilder-8K") input = appbuilder.Message("9.11和9.8哪个大") result = rag_with_baidu_search_pro.run( diff --git a/docs/README_en.md b/docs/README_en.md index be73a170a..3611bcab2 100644 --- a/docs/README_en.md +++ b/docs/README_en.md @@ -128,7 +128,7 @@ import os # 设置环境中的TOKEN,以下TOKEN为访问和QPS受限的试用TOKEN,正式使用请替换为您的个人TOKEN os.environ["APPBUILDER_TOKEN"] = "bce-v3/ALTAK-n5AYUIUJMarF7F7iFXVeK/1bf65eed7c8c7efef9b11388524fa1087f90ea58" -rag_with_baidu_search_pro = appbuilder.RagWithBaiduSearchPro(model="Qianfan-Agent-Speed-8k") +rag_with_baidu_search_pro = appbuilder.RagWithBaiduSearchPro(model="ERNIE-Lite-AppBuilder-8K") input = appbuilder.Message("9.11和9.8哪个大") result = rag_with_baidu_search_pro.run( diff --git a/docs/README_ja.md b/docs/README_ja.md index fcbb5d41a..7699ca025 100644 --- a/docs/README_ja.md +++ b/docs/README_ja.md @@ -124,7 +124,7 @@ import os # 環境変数にTOKENを設定します。以下のTOKENはアクセスとQPSが制限された試用TOKENです。正式な使用には個人のTOKENに置き換えてください。 os.environ["APPBUILDER_TOKEN"] = "bce-v3/ALTAK-n5AYUIUJMarF7F7iFXVeK/1bf65eed7c8c7efef9b11388524fa1087f90ea58" -rag_with_baidu_search_pro = appbuilder.RagWithBaiduSearchPro(model="Qianfan-Agent-Speed-8k") +rag_with_baidu_search_pro = appbuilder.RagWithBaiduSearchPro(model="ERNIE-Lite-AppBuilder-8K") input = appbuilder.Message("9.11と9.8のどちらが大きいですか?") result = rag_with_baidu_search_pro.run( From 495b3dcb4893d24ba6c51059a0e7a038729c13bf Mon Sep 17 00:00:00 2001 From: Wei Mingzhi Date: Thu, 28 Nov 2024 19:59:13 +0800 Subject: [PATCH 46/85] tts: add more available voices. (#626) Signed-off-by: Wei Mingzhi --- docs/BasisModule/Components/tts/README.md | 2 +- python/core/components/tts/README.md | 2 +- python/core/components/tts/component.py | 11 +++++++++-- python/core/components/tts/model.py | 18 ++++++++++++++---- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/docs/BasisModule/Components/tts/README.md b/docs/BasisModule/Components/tts/README.md index a21dff6c2..165583de0 100644 --- a/docs/BasisModule/Components/tts/README.md +++ b/docs/BasisModule/Components/tts/README.md @@ -65,7 +65,7 @@ os.environ["APPBUILDER_TOKEN"] = "..." | speed | Integer | 否 | 语音语速,默认是5中等语速,取值范围在0~15之间,仅当模型为`baidu-tts`参数有效,如果模型为`paddlespeech-tts`,参数自动失效 | 5 | | pitch | Integer | 否 | 语音音调,默认是5中等音调,取值范围在0~15之间,仅当模型为`baidu-tts`参数有效,如果模型为`paddlespeech-tts`,参数自动失效 | 5 | | volume | Integer | 否 | 语音音量,默认是5中等音量,取值范围在0~15之间,,仅当模型为`baidu-tts`参数有效,如果模型为`paddlespeech-tts`,参数自动失效 | 5 | -| person | Integer | 否 | 语音人物特征,默认是0(度小美),普通音库可选值包括: 0(度小美)、1(度小宇)、3(度逍遥-基础)、4(度丫丫);精品音库包括:5003(度逍遥-精品)、 5118(度小鹿) 、106(度博文)、 110(度小童)、 111(度小萌)、 103(度米朵)、 5(度小娇),仅当模型为`baidu-tts`参数有效,如果模型为`paddlespeech-tts`,参数自动失效 | 0 | +| person | Integer | 否 | 语音人物特征,默认是0(度小美),普通音库可选值包括: 0(度小美)、1(度小宇)、3(度逍遥-基础)、4(度丫丫);精品音库包括:5003(度逍遥-精品)、5118(度小鹿)、106(度博文)、110(度小童)、111(度小萌)、103(度米朵)、5(度小娇);臻品音库包括:4003(度逍遥-情感男声)、4106(度博文-专业男主播)、4115(度小贤-电台男主播)、4119(度小鹿-甜美女声)、4105(度灵儿-清激女声)、4117(度小乔-活泼女声)、4100(度小雯-活力女主播)、4103(度米朵-可爱女声)、4144(度姗姗-娱乐女声)、4278(度小贝-知识女主播)、4143(度清风-配音男声)、4140(度小新-专业女主播)、4129(度小彦-知识男主播)、4149(度星河-广告男声)、4254(度小清-广告女声)、4206(度博文-综艺男声)、4226(南方-电台女主播)。仅当模型为`baidu-tts`参数有效,如果模型为`paddlespeech-tts`,参数自动失效 | 0 | | audio_type | String | 否 | 音频文件格式,如果使用`baidu-tts`模型可选`mp3`, `wav`; 如果使用`paddlespeech-tts`模型非流式返回,参数只能设为`wav`;如果使用`paddlespeech-tts`模型流式返回,参数只能设为`pcm` | wav | | stream | Bool | 否 | 默认是False, 目前`paddlespeech-tts`模型支持流式返回,`baidu-tts`模型不支持流式返回 | False | | retry | Integer | 否 | HTTP重试次数 | 3 | diff --git a/python/core/components/tts/README.md b/python/core/components/tts/README.md index a21dff6c2..165583de0 100644 --- a/python/core/components/tts/README.md +++ b/python/core/components/tts/README.md @@ -65,7 +65,7 @@ os.environ["APPBUILDER_TOKEN"] = "..." | speed | Integer | 否 | 语音语速,默认是5中等语速,取值范围在0~15之间,仅当模型为`baidu-tts`参数有效,如果模型为`paddlespeech-tts`,参数自动失效 | 5 | | pitch | Integer | 否 | 语音音调,默认是5中等音调,取值范围在0~15之间,仅当模型为`baidu-tts`参数有效,如果模型为`paddlespeech-tts`,参数自动失效 | 5 | | volume | Integer | 否 | 语音音量,默认是5中等音量,取值范围在0~15之间,,仅当模型为`baidu-tts`参数有效,如果模型为`paddlespeech-tts`,参数自动失效 | 5 | -| person | Integer | 否 | 语音人物特征,默认是0(度小美),普通音库可选值包括: 0(度小美)、1(度小宇)、3(度逍遥-基础)、4(度丫丫);精品音库包括:5003(度逍遥-精品)、 5118(度小鹿) 、106(度博文)、 110(度小童)、 111(度小萌)、 103(度米朵)、 5(度小娇),仅当模型为`baidu-tts`参数有效,如果模型为`paddlespeech-tts`,参数自动失效 | 0 | +| person | Integer | 否 | 语音人物特征,默认是0(度小美),普通音库可选值包括: 0(度小美)、1(度小宇)、3(度逍遥-基础)、4(度丫丫);精品音库包括:5003(度逍遥-精品)、5118(度小鹿)、106(度博文)、110(度小童)、111(度小萌)、103(度米朵)、5(度小娇);臻品音库包括:4003(度逍遥-情感男声)、4106(度博文-专业男主播)、4115(度小贤-电台男主播)、4119(度小鹿-甜美女声)、4105(度灵儿-清激女声)、4117(度小乔-活泼女声)、4100(度小雯-活力女主播)、4103(度米朵-可爱女声)、4144(度姗姗-娱乐女声)、4278(度小贝-知识女主播)、4143(度清风-配音男声)、4140(度小新-专业女主播)、4129(度小彦-知识男主播)、4149(度星河-广告男声)、4254(度小清-广告女声)、4206(度博文-综艺男声)、4226(南方-电台女主播)。仅当模型为`baidu-tts`参数有效,如果模型为`paddlespeech-tts`,参数自动失效 | 0 | | audio_type | String | 否 | 音频文件格式,如果使用`baidu-tts`模型可选`mp3`, `wav`; 如果使用`paddlespeech-tts`模型非流式返回,参数只能设为`wav`;如果使用`paddlespeech-tts`模型流式返回,参数只能设为`pcm` | wav | | stream | Bool | 否 | 默认是False, 目前`paddlespeech-tts`模型支持流式返回,`baidu-tts`模型不支持流式返回 | False | | retry | Integer | 否 | HTTP重试次数 | 3 | diff --git a/python/core/components/tts/component.py b/python/core/components/tts/component.py index 43639379d..fef104111 100644 --- a/python/core/components/tts/component.py +++ b/python/core/components/tts/component.py @@ -93,8 +93,15 @@ def run(self, volume (int, 音量): 语音音量,默认是5中等音量,取值范围在0~15之间, 如果选择模型为paddlespeech-tts,参数自动失效。 person (int, 可选): 语音人物特征,默认是0, - 可选值包括度小宇=1 度小美=0 度逍遥(基础)=3 度丫丫=4 度逍遥(精品)=5003 - 度小鹿=5118 度博文=106 度小童=110 度小萌=111 度米朵=103 度小娇=5, + 可选值包括: + 度小宇=1 度小美=0 度逍遥(基础)=3 度丫丫=4 度逍遥(精品)=5003 + 度小鹿=5118 度博文=106 度小童=110 度小萌=111 度米朵=103 度小娇=5 + 度逍遥-情感男声=4003 度博文-专业男主播=4106 度小贤-电台男主播=4115 + 度小鹿-甜美女声=4119 度灵儿-清激女声=4105 度小乔-活泼女声=4117 + 度小雯-活力女主播=4100 度米朵-可爱女声=4103 度姗姗-娱乐女声=4144 + 度小贝-知识女主播=4278 度清风-配音男声=4143 度小新-专业女主播=4140 + 度小彦-知识男主播=4129 度星河-广告男声=4149 度小清-广告女声=4254 + 度博文-综艺男声=4206 南方-电台女主播=4226, 如果选择模型为paddlespeech-tts,参数自动失效。 audio_type (str, 可选): 音频文件格式,默认是`mp3`, 如果选择`paddlespeech-tts`模型,参数只能设为`wav`。 diff --git a/python/core/components/tts/model.py b/python/core/components/tts/model.py index e91608a00..29f10b568 100644 --- a/python/core/components/tts/model.py +++ b/python/core/components/tts/model.py @@ -17,7 +17,6 @@ from pydantic import BaseModel - class TTSRequest(proto.Message): r"""文本转语音请求参数. @@ -32,7 +31,14 @@ class TTSRequest(proto.Message): pit(int, 可选): 语音音调,默认是5中等音调,取值范围在0~15之间,如果选择模型为paddlespeech-tts,参数自动失效. vol(int, 音量): 语音音量,默认是5中等音量,取值范围在0~15之间,如果选择模型为paddlespeech-tts,参数自动失效. per(int, 可选): 语音人物特征,默认是0,可选值包括度小宇=1 度小美=0 度逍遥(基础)=3 度丫丫=4 度逍遥(精品)=5003 - 度小鹿=5118 度博文=106 度小童=110 度小萌=111 度米朵=103 度小娇=5,如果选择模型为paddlespeech-tts,参数自动失效. + 度小鹿=5118 度博文=106 度小童=110 度小萌=111 度米朵=103 度小娇=5 + 度逍遥-情感男声=4003 度博文-专业男主播=4106 度小贤-电台男主播=4115 + 度小鹿-甜美女声=4119 度灵儿-清激女声=4105 度小乔-活泼女声=4117 + 度小雯-活力女主播=4100 度米朵-可爱女声=4103 度姗姗-娱乐女声=4144 + 度小贝-知识女主播=4278 度清风-配音男声=4143 度小新-专业女主播=4140 + 度小彦-知识男主播=4129 度星河-广告男声=4149 度小清-广告女声=4254 + 度博文-综艺男声=4206 南方-电台女主播=4226, + 如果选择模型为paddlespeech-tts,参数自动失效. aue(int, 可选): 语音格式, 默认是3(mp3) 4(pcm-16k) 5(pcm-8k) 6-wav. tp_project_id(str): paddlespeech-tts项目ID tp_per_id(str): paddlespeech-tts音频ID @@ -110,9 +116,13 @@ def __validate(self): def validate_baidu_tts(self): """检查baidu-tts模型请求参数""" self.__validate() - if self.per not in {0, 1, 3, 4, 5, 103, 106, 110, 111, 5003, 5118}: + + _BAIDU_VALID_PER = {0, 1, 3, 4, 5, 103, 106, 110, 111, 4003, 4106, 4115, 4119, + 4105, 4117, 4100, 4103, 4144, 4278, 4143, 4140, 4129, 4149, 4254, 4206, 4226, 5003, 5118} + + if self.per not in _BAIDU_VALID_PER: raise ValueError( - f"per value is illegal, exepcted in {0, 1, 3, 4, 5, 103, 106, 110, 111, 5003, 5118}, got {self.per}" + f"per value is illegal, expected in {_BAIDU_VALID_PER}, got {self.per}" ) if self.aue == 0: self.aue = 3 From 323d8f623a17c9af3dbd1f8e9e432f088c9052b8 Mon Sep 17 00:00:00 2001 From: peiwenYe <963623403@qq.com> Date: Fri, 29 Nov 2024 13:15:34 +0800 Subject: [PATCH 47/85] Add v2 style rewrite (#628) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * create_output变更为classmethod方法,component_tool_eval_schemas移动到case中 * create_output变更为classmethod方法,component_tool_eval_schemas移动到case中 * create_output变更为classmethod方法,component_tool_eval_schemas移动到case中 * create_output变更为classmethod方法,component_tool_eval_schemas移动到case中 * 修改tool_eval和manifests参数校验规则 * 添加v2版本stype rewrite组件 * add v2 stype_rewrite * add v2 stype_rewrite * add v2 stype_rewrite * add v2 stype_rewrite --------- Co-authored-by: yepeiwen01 --- python/core/component.py | 1 + python/core/components/v2/__init__.py | 2 + python/core/components/v2/llms/__init__.py | 15 ++ .../v2/llms/style_rewrite/__init__.py | 2 + .../v2/llms/style_rewrite/component.py | 139 ++++++++++++++++++ python/tests/component_tool_eval_cases.py | 14 +- python/tests/test_v2_style_rewrite.py | 57 +++++++ 7 files changed, 229 insertions(+), 1 deletion(-) create mode 100644 python/core/components/v2/llms/__init__.py create mode 100644 python/core/components/v2/llms/style_rewrite/__init__.py create mode 100644 python/core/components/v2/llms/style_rewrite/component.py create mode 100644 python/tests/test_v2_style_rewrite.py diff --git a/python/core/component.py b/python/core/component.py index d8b82e62a..936a50612 100644 --- a/python/core/component.py +++ b/python/core/component.py @@ -197,6 +197,7 @@ def __init__( secret_key: Optional[str] = None, gateway: str = "", lazy_certification: bool = False, + **kwargs ): r"""Component初始化方法. diff --git a/python/core/components/v2/__init__.py b/python/core/components/v2/__init__.py index d1a3b8811..ada4fd231 100644 --- a/python/core/components/v2/__init__.py +++ b/python/core/components/v2/__init__.py @@ -14,8 +14,10 @@ from .animal_recognize.component import AnimalRecognition from .image_understand.component import ImageUnderstand +from .llms.style_rewrite.component import StyleRewrite __V2_COMPONENTS__ = [ "AnimalRecognition", "ImageUnderstand", + "StyleRewrite" ] # NOQA \ No newline at end of file diff --git a/python/core/components/v2/llms/__init__.py b/python/core/components/v2/llms/__init__.py new file mode 100644 index 000000000..fc3a1a2ea --- /dev/null +++ b/python/core/components/v2/llms/__init__.py @@ -0,0 +1,15 @@ +# Copyright (c) 2023 Baidu, Inc. 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. + + diff --git a/python/core/components/v2/llms/style_rewrite/__init__.py b/python/core/components/v2/llms/style_rewrite/__init__.py new file mode 100644 index 000000000..8637dc58e --- /dev/null +++ b/python/core/components/v2/llms/style_rewrite/__init__.py @@ -0,0 +1,2 @@ +"""StyleRewrite""" +from .component import StyleRewrite diff --git a/python/core/components/v2/llms/style_rewrite/component.py b/python/core/components/v2/llms/style_rewrite/component.py new file mode 100644 index 000000000..288e47615 --- /dev/null +++ b/python/core/components/v2/llms/style_rewrite/component.py @@ -0,0 +1,139 @@ +# Copyright (c) 2023 Baidu, Inc. 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 appbuilder.core.components.llms.base import CompletionBaseComponent +from appbuilder.core.message import Message + +from typing import Optional +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace +from appbuilder.core.components.llms.style_rewrite.base import StyleRewriteArgs + + +class StyleRewrite(CompletionBaseComponent): + """ + 文本风格转写大模型组件, 基于生成式大模型对文本的风格进行改写,支持有营销、客服、直播、激励及教学五种话术。 + + Examples: + + .. code-block:: python + + import os + import appbuilder + os.environ["APPBUILDER_TOKEN"] = '...' + + style_rewrite = appbuilder.StyleRewrite(model="Qianfan-Agent-Speed-8k") + answer = style_rewrite(appbuilder.Message("文心大模型发布新版本"), style="激励话术") + + """ + name = "style_rewrite" + version = "v1" + meta = StyleRewriteArgs + + manifests = [ + { + "name": "style_rewrite", + "description": "能够将一段文本转换成不同的风格(营销、客服、直播、激励及教学话术),同时保持原文的基本意义不变。", + "parameters": { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "需要改写的文本。" + }, + "style": { + "type": "string", + "description": "想要转换的文本风格,目前有营销、客服、直播、激励及教学五种话术可选. 默认是营销话术。", + "enum": ["营销话术", "客服话术", "直播话术", "激励话术", "教学话术"] + } + }, + "required": [ + "query" + ] + } + } + ] + + def __init__( + self, + model=None, + secret_key: Optional[str] = None, + gateway: str = "", + lazy_certification: bool = False, + ): + """初始化StyleRewrite模型。 + + Args: + model (str|None): 模型名称,用于指定要使用的千帆模型。 + secret_key (str, 可选): 用户鉴权token, 默认从环境变量中获取: os.getenv("APPBUILDER_TOKEN", ""). + gateway (str, 可选): 后端网关服务地址,默认从环境变量中获取: os.getenv("GATEWAY_URL", "") + lazy_certification (bool, 可选): 延迟认证,为True时在第一次运行时认证. Defaults to False. + + Returns: + None + + """ + super().__init__( + StyleRewriteArgs, model=model, secret_key=secret_key, gateway=gateway, + lazy_certification=lazy_certification) + + @components_run_trace + def run(self, message, style="营销话术", stream=False, temperature=1e-10, top_p=0.0, request_id=None): + """ + 使用给定的输入运行模型并返回结果。 + + Args: + message (obj:`Message`): 输入消息,用于模型的主要输入内容。这是一个必需的参数。 + style (str, optional): 想要转换的文本风格,目前有营销、客服、直播、激励及教学五种话术可选。默认为"营销话术"。 + stream (bool, optional): 指定是否以流式形式返回响应。默认为 False。 + temperature (float, optional): 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 + top_p (float, optional): 影响输出文本的多样性,取值越大,生成文本的多样性越强。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 + + Returns: + obj:`Message`: 模型运行后的输出消息。 + + """ + return super().run(message=message, style=style, stream=stream, temperature=temperature, top_p=top_p, request_id=request_id) + + @components_run_stream_trace + def tool_eval(self, query: str, style: str = "营销话术", **kwargs): + """ + 执行工具评估函数 + + Args: + name (str): 函数名称,本函数不使用该参数,但保留以符合某些框架的要求。 + streaming (bool, optional): 是否以流的形式返回结果。默认为 False,即一次性返回结果。如果设置为 True,则以生成器形式逐个返回结果。 + **kwargs: 其他参数,包含但不限于: + _sys_traceid (str): 请求的跟踪ID,用于日志记录和跟踪。 + model_configs (dict, optional): 模型配置参数,可选的键包括: + temperature (float, optional): 温度参数,用于控制生成文本的随机性。默认为 1e-10。 + top_p (float, optional): top_p 采样参数,用于控制生成文本的多样性。默认为 0.0。 + + Returns: + 如果 streaming 为 False,则直接返回评估结果字符串。 + 如果 streaming 为 True,则以生成器形式逐个返回评估结果字符串。 + + Raises: + ValueError: 如果缺少参数 'query'。 + """ + traceid = kwargs.get("_sys_traceid") + if not query: + raise ValueError("param `query` is required") + msg = Message(query) + if style not in ["营销话术", "客服话术", "直播话术", "激励话术", "教学话术"]: + style = "营销话术" + model_configs = kwargs.get('model_configs', {}) + temperature = model_configs.get("temperature", 1e-10) + top_p = model_configs.get("top_p", 0.0) + message = super().run(message=msg, style=style, stream=False, temperature=temperature, top_p=top_p, request_id=traceid) + + yield self.create_output(type="text", text=str(message.content), name="text", usage=message.token_usage) diff --git a/python/tests/component_tool_eval_cases.py b/python/tests/component_tool_eval_cases.py index fc71514dc..ab96ae8a2 100644 --- a/python/tests/component_tool_eval_cases.py +++ b/python/tests/component_tool_eval_cases.py @@ -88,9 +88,21 @@ def inputs(self): def schemas(self): return [url_schema] +class StypeRewriteCase(Case): + def init_args(self): + return {"model": "Qianfan-Agent-Speed-8k"} + + def inputs(self): + return {"query": "文心大模型发布新版"} + + def schemas(self): + return [text_schema] + + component_tool_eval_cases = { "AnimalRecognition": AnimalRecognitionCase, "ImageUnderstand": ImageUnderstandCase, "ASR": ASRCase, - "TreeMind": TreeMindCase + "TreeMind": TreeMindCase, + "StyleRewrite": StypeRewriteCase } \ No newline at end of file diff --git a/python/tests/test_v2_style_rewrite.py b/python/tests/test_v2_style_rewrite.py new file mode 100644 index 000000000..f41fa4754 --- /dev/null +++ b/python/tests/test_v2_style_rewrite.py @@ -0,0 +1,57 @@ +# Copyright (c) 2024 Baidu, Inc. 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 os +import unittest +import time +import appbuilder +from appbuilder.core.components.v2 import StyleRewrite +from appbuilder.core.component import ComponentOutput + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") +class TestStyleRewrite(unittest.TestCase): + def setUp(self) -> None: + self.com = StyleRewrite(model="Qianfan-Agent-Speed-8k") + + def test_normal_case(self): + time.sleep(2) + text = "文心大模型发布新版" + style = "激励话术" + msg = appbuilder.Message(content=text) + out = self.com(msg, style=style) + self.assertIn("文心大模型", out.content) + + def test_tool_eval(self): + time.sleep(2) + text = "文心大模型发布新版" + style = "营销话术" + out = self.com.tool_eval(query=text, style=style) + for item in out: + self.assertIsInstance(item, ComponentOutput) + + def test_non_stream_tool_eval(self): + text = "成都是个包容的城市" + style = "直播话术" + out = self.com.non_stream_tool_eval(query=text, style=style) + print(out) + self.assertIsInstance(out, ComponentOutput) + + def test_tool_eval_invalid(self): + """测试 tool 方法对无效请求的处理。""" + with self.assertRaises(TypeError): + result = self.com.tool_eval(name="image_understand", streaming=True, + origin_query="") + + +if __name__ == '__main__': + unittest.main() From 36dec5819508317261e19e4a624452b227c17213 Mon Sep 17 00:00:00 2001 From: C9luster <138663536+C9luster@users.noreply.github.com> Date: Mon, 2 Dec 2024 21:54:55 +0800 Subject: [PATCH 48/85] =?UTF-8?q?Appbuilder-SDK=20V2=E7=89=88=E6=9C=ACComp?= =?UTF-8?q?onents=E7=BB=84=E4=BB=B6Trace=E5=85=BC=E5=AE=B9=E6=80=A7?= =?UTF-8?q?=E5=8F=98=E6=9B=B4=20(#631)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Appbuilder-SDK V2版本Components组件Trace兼容性变更 * 添加单元测试文件 * 删除Excel2Figure组件 * 更新Sentry单元测试 * 更新Sentry Trace Bug --------- Co-authored-by: yinjiaqi --- .../Components/excel2figure/README.md | 86 ------ mkdocs.yml | 1 - python/__init__.py | 2 - python/core/components/excel2figure/README.md | 86 ------ .../core/components/excel2figure/__init__.py | 14 - python/core/components/excel2figure/base.py | 26 -- .../core/components/excel2figure/component.py | 284 ------------------ python/tests/component_collector.py | 1 - .../tests/test_appbuilder_sentry_trace_on.py | 49 ++- python/tests/test_qa_llm_excel2figure.py | 173 ----------- python/tests/test_v2_component_trace.py | 40 +++ python/tests/whitelist_components.txt | 13 - python/utils/trace/_function.py | 45 ++- 13 files changed, 93 insertions(+), 727 deletions(-) delete mode 100644 docs/BasisModule/Components/excel2figure/README.md delete mode 100644 python/core/components/excel2figure/README.md delete mode 100644 python/core/components/excel2figure/__init__.py delete mode 100644 python/core/components/excel2figure/base.py delete mode 100644 python/core/components/excel2figure/component.py delete mode 100644 python/tests/test_qa_llm_excel2figure.py create mode 100644 python/tests/test_v2_component_trace.py delete mode 100644 python/tests/whitelist_components.txt diff --git a/docs/BasisModule/Components/excel2figure/README.md b/docs/BasisModule/Components/excel2figure/README.md deleted file mode 100644 index 0e1d5565a..000000000 --- a/docs/BasisModule/Components/excel2figure/README.md +++ /dev/null @@ -1,86 +0,0 @@ -# Excel转图表(Excel2Figure) - -## 简介 -Excel转图表(Excel2Figure)组件通过理解对表格信息的提问,生成对应语义的图表。 - -### 功能介绍 -Excel2Figure 是一个高级的数据可视化工具,它结合了大语言模型的强大语义理解能力,以帮助用户将 Excel 表格数据转换成直观、易理解的图表。用户只需通过自然语言描述他们想要呈现的数据和图表类型,Excel2Figure 会解析这些指令,自动从Excel数据中提取相关信息,生成符合用户需求的图表。 - -### 特色优势 -- 强大的语义理解:利用文心一言大语言模型的先进技术,Excel2Figure能够理解复杂的自然语言指令,包括数据筛选、分析需求和图表类型等。 -- 用户友好的交互:用户可以用自己熟悉的语言描述数据可视化需求,无需学习复杂的软件操作或编程语言。 -- 支持多样化的图表类型:根据用户的自然语言描述,Excel2Figure能够生成多种类型的图表,包括但不限于柱状图、线形图、饼图、散点图等。 -- 快速准确的数据处理:通过语义理解快速定位和处理Excel中的数据,大大减少了数据准备的时间和出错的可能性。 - -### 应用场景 -- 学术研究:研究人员可以简单描述他们需要的图表类型和数据集,Excel2Figure将帮助他们将研究数据可视化,以便在学术论文或演讲中展示。 -- 市场趋势分析:市场分析师利用Excel2Figure快速生成展示市场调查结果和消费者行为分析的图表,帮助团队理解市场动态。 -- 教育用途:教师可以利用这个工具将复杂的数据集转换为学生更容易理解的图表,提高教学效果和学生的学习兴趣。 -- 个人数据管理和展示:个人用户可以使用Excel2Figure来跟踪和展示自己的财务状况、健康数据或任何其他类型的个人记录。 - - -## 基本用法 - -### 快速开启 - -```python -import appbuilder -import os - -# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 -# 设置环境变量 -os.environ["APPBUILDER_TOKEN"] = '...' - -# 创建 component 对象,推荐使用 ERNIE-Bot 4.0 获取更稳定的画图效果 -component = appbuilder.Excel2Figure(model="ERNIE-Bot 4.0") - -# 准备 excel 文件链接,该链接需要是公网可访问的地址 -excel_file_url = "https://agi-dev-platform-bos.bj.bcebos.com/ut_appbuilder/[测试]超市收入明细表格.xlsx?authorization=bce-auth-v1/e464e6f951124fdbb2410c590ef9ed2f/2024-02-21T09%3A51%3A14Z/-1/host/1802a9c9142ef328d61e7673db7c1f05842b2af93d18a02ac7ef7aa6f64db54e" - -# 针对 excel 文件内容绘制图表 -result = component.run(appbuilder.Message({ - "query": "2020年各个月份的利润分别是多少?使用条形图绘制出来", - "excel_file_url": excel_file_url, -})) - -# 输出运行结果 -print(result) -# Message(name=msg, content="http://可访问的文件地址...", mtype=str) -``` - -如果绘图成功,预期结果为一个可访问的文件地址,文件链接**过期时间限制为24小时**;如果绘图失败,预期结果为空字符串,需要调整query。 - -这里给出上述代码运行得到的图表(每次运行结果可能会发生变化,仅供参考): -![2020年各个月份利润条形图.png](https://agi-dev-platform-bos.bj.bcebos.com/ut_appbuilder/%5B%E6%B5%8B%E8%AF%95%5D2020%E5%B9%B4%E5%90%84%E4%B8%AA%E6%9C%88%E4%BB%BD%E5%88%A9%E6%B6%A6%E6%9D%A1%E5%BD%A2%E5%9B%BE.png?authorization=bce-auth-v1/e464e6f951124fdbb2410c590ef9ed2f/2024-02-21T10%3A00%3A16Z/-1/host/b68f35825ad99075caf8bd009e4871ee9eb7b718e550968fdf12695b1502bc78) - - -## 参数说明 - -### 初始化参数 -- `model`: 模型名称,用于指定要使用的千帆模型。 - -### 调用参数 -| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | -|--------|--------|---|----|------------------------------------------| -| msg | Message | 是 | 输入消息,包含用户提出的问题 query 和一个公网可访问的 excel 文件链接 excel_file_url。| - | -| +query | String | 是 | 用户提出的问题,长度小于 400 | "2020年各个月份的利润分别是多少?使用条形图绘制出来" | -| +excel_file_url | String | 是 | 公网可访问的 excel 文件链接 | "https://agi-dev-platform-bos.bj.bcebos.com/ut_appbuilder/%5B%E6%B5%8B%E8%AF%95%5D%E8%B6%85%E5%B8%82%E6%94%B6%E5%85%A5%E6%98%8E%E7%BB%86%E8%A1%A8%E6%A0%BC.xlsx?authorization=bce-auth-v1/e464e6f951124fdbb2410c590ef9ed2f/2024-02-21T09%3A51%3A14Z/-1/host/1802a9c9142ef328d61e7673db7c1f05842b2af93d18a02ac7ef7aa6f64db54e" | - -### 响应参数 -| 参数名称 | 参数类型 | 描述 | 示例值 | -|--------|--------|----|------| -| result | Message | 返回结果。| - | -| +content | String | 如果图表绘制成功,则会返回一个可下载的图片链接,有效期为24小时;如果绘制失败,则会返回空字符串。 | "https://agi-dev-platform-bos.bj.bcebos.com/ut_appbuilder/%5B%E6%B5%8B%E8%AF%95%5D2020%E5%B9%B4%E5%90%84%E4%B8%AA%E6%9C%88%E4%BB%BD%E5%88%A9%E6%B6%A6%E6%9D%A1%E5%BD%A2%E5%9B%BE.png?authorization=bce-auth-v1/e464e6f951124fdbb2410c590ef9ed2f/2024-02-21T10%3A00%3A16Z/-1/host/b68f35825ad99075caf8bd009e4871ee9eb7b718e550968fdf12695b1502bc78" | - -### 响应示例 -```shell -Message(name=msg, content="https://agi-dev-platform-bos.bj.bcebos.com/ut_appbuilder/%5B%E6%B5%8B%E8%AF%95%5D2020%E5%B9%B4%E5%90%84%E4%B8%AA%E6%9C%88%E4%BB%BD%E5%88%A9%E6%B6%A6%E6%9D%A1%E5%BD%A2%E5%9B%BE.png?authorization=bce-auth-v1/e464e6f951124fdbb2410c590ef9ed2f/2024-02-21T10%3A00%3A16Z/-1/host/b68f35825ad99075caf8bd009e4871ee9eb7b718e550968fdf12695b1502bc78", mtype=str) -``` - -### 错误码 -| 错误码 | 描述 | -|--------|--------| -| pydantic.error_wrappers.ValidationError | 输入参数校验错误 | - -## 更新记录和贡献 -* Excel转图表 (2024-02) diff --git a/mkdocs.yml b/mkdocs.yml index 7ec97eff6..044ab306a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -19,7 +19,6 @@ nav: - 文件生成PPT(PPTGenerationFromFile): BasisModule/Components/ppt_generation_from_file/README.md - GBI 选表: BasisModule/Components/gbi/select_table/README.md - GBI 问表: BasisModule/Components/gbi/nl2sql/README.md - - Excel转图表(Excel2Figure): BasisModule/Components/excel2figure/README.md - 向量计算(Embedding): BasisModule/Components/embeddings/README.md - 论文生成PPT(PPTGenerationFromPaper): BasisModule/Components/ppt_generation_from_paper/README.md - 地标识别(LandmarkRecognition): BasisModule/Components/landmark_recognize/README.md diff --git a/python/__init__.py b/python/__init__.py index 8fac7ff3a..3e949bea7 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -60,7 +60,6 @@ def get_default_header(): from .core.components.rag_with_baidu_search_pro import RagWithBaiduSearchPro from .core.components.rag_with_baidu_search import RAGWithBaiduSearch from .core import console -from .core.components.excel2figure import Excel2Figure from .core.components.llms.mrc import MRC from .core.components.llms.oral_query_generation import OralQueryGeneration from .core.components.llms.qa_pair_mining import QAPairMining @@ -118,7 +117,6 @@ def get_default_header(): __COMPONENTS__ = [ "RagWithBaiduSearchPro", "RAGWithBaiduSearch", - "Excel2Figure", "MRC", "OralQueryGeneration", "QAPairMining", diff --git a/python/core/components/excel2figure/README.md b/python/core/components/excel2figure/README.md deleted file mode 100644 index 0e1d5565a..000000000 --- a/python/core/components/excel2figure/README.md +++ /dev/null @@ -1,86 +0,0 @@ -# Excel转图表(Excel2Figure) - -## 简介 -Excel转图表(Excel2Figure)组件通过理解对表格信息的提问,生成对应语义的图表。 - -### 功能介绍 -Excel2Figure 是一个高级的数据可视化工具,它结合了大语言模型的强大语义理解能力,以帮助用户将 Excel 表格数据转换成直观、易理解的图表。用户只需通过自然语言描述他们想要呈现的数据和图表类型,Excel2Figure 会解析这些指令,自动从Excel数据中提取相关信息,生成符合用户需求的图表。 - -### 特色优势 -- 强大的语义理解:利用文心一言大语言模型的先进技术,Excel2Figure能够理解复杂的自然语言指令,包括数据筛选、分析需求和图表类型等。 -- 用户友好的交互:用户可以用自己熟悉的语言描述数据可视化需求,无需学习复杂的软件操作或编程语言。 -- 支持多样化的图表类型:根据用户的自然语言描述,Excel2Figure能够生成多种类型的图表,包括但不限于柱状图、线形图、饼图、散点图等。 -- 快速准确的数据处理:通过语义理解快速定位和处理Excel中的数据,大大减少了数据准备的时间和出错的可能性。 - -### 应用场景 -- 学术研究:研究人员可以简单描述他们需要的图表类型和数据集,Excel2Figure将帮助他们将研究数据可视化,以便在学术论文或演讲中展示。 -- 市场趋势分析:市场分析师利用Excel2Figure快速生成展示市场调查结果和消费者行为分析的图表,帮助团队理解市场动态。 -- 教育用途:教师可以利用这个工具将复杂的数据集转换为学生更容易理解的图表,提高教学效果和学生的学习兴趣。 -- 个人数据管理和展示:个人用户可以使用Excel2Figure来跟踪和展示自己的财务状况、健康数据或任何其他类型的个人记录。 - - -## 基本用法 - -### 快速开启 - -```python -import appbuilder -import os - -# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 -# 设置环境变量 -os.environ["APPBUILDER_TOKEN"] = '...' - -# 创建 component 对象,推荐使用 ERNIE-Bot 4.0 获取更稳定的画图效果 -component = appbuilder.Excel2Figure(model="ERNIE-Bot 4.0") - -# 准备 excel 文件链接,该链接需要是公网可访问的地址 -excel_file_url = "https://agi-dev-platform-bos.bj.bcebos.com/ut_appbuilder/[测试]超市收入明细表格.xlsx?authorization=bce-auth-v1/e464e6f951124fdbb2410c590ef9ed2f/2024-02-21T09%3A51%3A14Z/-1/host/1802a9c9142ef328d61e7673db7c1f05842b2af93d18a02ac7ef7aa6f64db54e" - -# 针对 excel 文件内容绘制图表 -result = component.run(appbuilder.Message({ - "query": "2020年各个月份的利润分别是多少?使用条形图绘制出来", - "excel_file_url": excel_file_url, -})) - -# 输出运行结果 -print(result) -# Message(name=msg, content="http://可访问的文件地址...", mtype=str) -``` - -如果绘图成功,预期结果为一个可访问的文件地址,文件链接**过期时间限制为24小时**;如果绘图失败,预期结果为空字符串,需要调整query。 - -这里给出上述代码运行得到的图表(每次运行结果可能会发生变化,仅供参考): -![2020年各个月份利润条形图.png](https://agi-dev-platform-bos.bj.bcebos.com/ut_appbuilder/%5B%E6%B5%8B%E8%AF%95%5D2020%E5%B9%B4%E5%90%84%E4%B8%AA%E6%9C%88%E4%BB%BD%E5%88%A9%E6%B6%A6%E6%9D%A1%E5%BD%A2%E5%9B%BE.png?authorization=bce-auth-v1/e464e6f951124fdbb2410c590ef9ed2f/2024-02-21T10%3A00%3A16Z/-1/host/b68f35825ad99075caf8bd009e4871ee9eb7b718e550968fdf12695b1502bc78) - - -## 参数说明 - -### 初始化参数 -- `model`: 模型名称,用于指定要使用的千帆模型。 - -### 调用参数 -| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | -|--------|--------|---|----|------------------------------------------| -| msg | Message | 是 | 输入消息,包含用户提出的问题 query 和一个公网可访问的 excel 文件链接 excel_file_url。| - | -| +query | String | 是 | 用户提出的问题,长度小于 400 | "2020年各个月份的利润分别是多少?使用条形图绘制出来" | -| +excel_file_url | String | 是 | 公网可访问的 excel 文件链接 | "https://agi-dev-platform-bos.bj.bcebos.com/ut_appbuilder/%5B%E6%B5%8B%E8%AF%95%5D%E8%B6%85%E5%B8%82%E6%94%B6%E5%85%A5%E6%98%8E%E7%BB%86%E8%A1%A8%E6%A0%BC.xlsx?authorization=bce-auth-v1/e464e6f951124fdbb2410c590ef9ed2f/2024-02-21T09%3A51%3A14Z/-1/host/1802a9c9142ef328d61e7673db7c1f05842b2af93d18a02ac7ef7aa6f64db54e" | - -### 响应参数 -| 参数名称 | 参数类型 | 描述 | 示例值 | -|--------|--------|----|------| -| result | Message | 返回结果。| - | -| +content | String | 如果图表绘制成功,则会返回一个可下载的图片链接,有效期为24小时;如果绘制失败,则会返回空字符串。 | "https://agi-dev-platform-bos.bj.bcebos.com/ut_appbuilder/%5B%E6%B5%8B%E8%AF%95%5D2020%E5%B9%B4%E5%90%84%E4%B8%AA%E6%9C%88%E4%BB%BD%E5%88%A9%E6%B6%A6%E6%9D%A1%E5%BD%A2%E5%9B%BE.png?authorization=bce-auth-v1/e464e6f951124fdbb2410c590ef9ed2f/2024-02-21T10%3A00%3A16Z/-1/host/b68f35825ad99075caf8bd009e4871ee9eb7b718e550968fdf12695b1502bc78" | - -### 响应示例 -```shell -Message(name=msg, content="https://agi-dev-platform-bos.bj.bcebos.com/ut_appbuilder/%5B%E6%B5%8B%E8%AF%95%5D2020%E5%B9%B4%E5%90%84%E4%B8%AA%E6%9C%88%E4%BB%BD%E5%88%A9%E6%B6%A6%E6%9D%A1%E5%BD%A2%E5%9B%BE.png?authorization=bce-auth-v1/e464e6f951124fdbb2410c590ef9ed2f/2024-02-21T10%3A00%3A16Z/-1/host/b68f35825ad99075caf8bd009e4871ee9eb7b718e550968fdf12695b1502bc78", mtype=str) -``` - -### 错误码 -| 错误码 | 描述 | -|--------|--------| -| pydantic.error_wrappers.ValidationError | 输入参数校验错误 | - -## 更新记录和贡献 -* Excel转图表 (2024-02) diff --git a/python/core/components/excel2figure/__init__.py b/python/core/components/excel2figure/__init__.py deleted file mode 100644 index 9073b37bd..000000000 --- a/python/core/components/excel2figure/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2023 Baidu, Inc. 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 .component import Excel2Figure diff --git a/python/core/components/excel2figure/base.py b/python/core/components/excel2figure/base.py deleted file mode 100644 index bb9d99f1a..000000000 --- a/python/core/components/excel2figure/base.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (c) 2023 Baidu, Inc. 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 pydantic import Field, AnyUrl -from appbuilder.core.component import Component, ComponentArguments - -class Excel2FigureArgs(ComponentArguments): - """ - excel2figure 的参数 - - Attributes: - query: str - excel_file_url: AnyUrl - """ - query: str = Field(..., description="用户的 query 输入", max_length=400) - excel_file_url: AnyUrl = Field(..., description="用户的 excel 文件地址,需要是一个可被公网下载的 URL 地址") \ No newline at end of file diff --git a/python/core/components/excel2figure/component.py b/python/core/components/excel2figure/component.py deleted file mode 100644 index 63ff7738d..000000000 --- a/python/core/components/excel2figure/component.py +++ /dev/null @@ -1,284 +0,0 @@ -# Copyright (c) 2023 Baidu, Inc. 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. -""" -excel2figure component -""" -import os -import uuid -from typing import Dict, List, Optional -from pydantic import ValidationError -import tempfile -import requests -import logging -from openpyxl import load_workbook -from appbuilder.core._exception import ModelNotSupportedException -from appbuilder.core.component import Component -from appbuilder.core.message import Message -from appbuilder.core.utils import ModelInfo, ttl_lru_cache -from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace -from .base import Excel2FigureArgs - -class Excel2Figure(Component): - """ - excel2figure 组件类 - - Args: - model: str - secret_key: Optional[str] - gateway: str - lazy_certification: bool - """ - meta = Excel2FigureArgs - model_type: str = "chat" - excluded_models: List[str] = ["Yi-34B-Chat", "ChatLaw"] - model_info: ModelInfo = None - manifests = [ - { - "name": "excel_to_figure", - "description": "Excel转图表工具,当用户需要根据Excel图表的数据进行数据分析并绘制图表(柱状图、折线图、雷达图等),使用该工具。", - "parameters": { - "type": "object", - "properties": { - "query": { - "type": "string", - "description": "需要根据Excel图表的数据进行数据分析并绘制图表的请求描述。" - } - }, - "required": [ - "query" - ] - } - } - ] - - def __init__( - self, - model: str, - secret_key: Optional[str] = None, - gateway: str = "", - lazy_certification: bool = False, - ): - super().__init__( - meta=Excel2FigureArgs, secret_key=secret_key, gateway=gateway, lazy_certification=lazy_certification) - self.model = model - if not lazy_certification: - self._check_model_and_get_model_url(self.model, self.model_type) - self.server_sub_path = "/v1/ai_engine/copilot_engine/v1/api/agent/excel2figure" - - @ttl_lru_cache(seconds_to_live=1 * 60 * 60) # 1h - def set_secret_key_and_gateway(self, secret_key: Optional[str] = None, gateway: str = ""): - """ - 设置密钥和网关。 - - Args: - secret_key (Optional[str], optional): API密钥,默认为None。如果未指定,则不会更新密钥。 - gateway (str, optional): 网关地址,默认为空字符串。如果未指定,则不会更新网关。 - - Returns: - None - - """ - super(Excel2Figure, self).set_secret_key_and_gateway( - secret_key=secret_key, gateway=gateway) - self.__class__.model_info = ModelInfo(client=self.http_client) - - @ttl_lru_cache(seconds_to_live=1 * 60 * 60) # 1h - def _check_model_and_get_model_url(self, model, model_type): - if model and model in self.excluded_models: - raise ModelNotSupportedException(f"Model {model} not supported, expected in {self.excluded_models}") - if not model: - raise ValueError("model must be provided") - if self.__class__.model_info is None: - self.set_secret_key_and_gateway() - m_type = self.model_info.get_model_type(model) - if m_type != model_type: - raise ModelNotSupportedException( - f"Model {model} with type [{m_type}] not supported, only support {model_type} type") - - model_url = self.model_info.get_model_url(model) - return model_url - - @components_run_trace - def run(self, message: Message) -> Message: - """ - 执行 excel2figure。 - - Args: - message (Message): 消息对象,其 content 属性是一个字典,包含以下键值对: - - query (str): 用户的问题。 - - excel_file_url (str): 用户的 Excel 文件地址。 - - Returns: - Message: 处理后的消息对象。 - - Raises: - ValueError: 当 message.content 解析失败时抛出此异常。 - - """ - try: - inputs = self.meta(**message.content) - except ValidationError as e: - raise ValueError(e) - - result_msg = self._run_excel2figure( - query=inputs.query, excel_file_url=inputs.excel_file_url, model=self.model) - return result_msg - - def _read_excel(self, filename): - wb = load_workbook(filename) - ws = wb.active - - data = [] - columns = [] - for row_index, row in enumerate(ws.iter_rows(values_only=True)): - if row_index == 0: - columns = row - else: - data.append(row) - return columns, data - - def _run_excel2figure(self, query: str, excel_file_url: str, model: str, excel_file_name: str = None): - """ - 运行 - - Args: - query: query - excel_file_url: 用户的 excel 文件地址 - model: 模型名字 - - Returns: - message - """ - headers = self.http_client.auth_header() - headers["Content-Type"] = "application/json" - - with tempfile.TemporaryDirectory() as tmpdir: - # download excel file - try: - if excel_file_name: - file_name = excel_file_name - else: - file_name = str(uuid.uuid4()) + ".xlsx" - local_filename = os.path.join(tmpdir, file_name) - with requests.get(excel_file_url, stream=True) as r: - r.raise_for_status() - with open(local_filename, 'wb') as f: - for chunk in r.iter_content(chunk_size=8192): - if chunk: - f.write(chunk) - except Exception as e: - logging.error(f"download file error: {excel_file_url}") - raise e - - # read file - columns, data = self._read_excel(local_filename) - file_contents = ["打印每一列的名称如下: "] - file_contents.extend(columns) - file_contents.append("展示每一列的数据样例: ") - for i, column in enumerate(columns): - sample_data = ", ".join([str(data[row][i]) for row in range(min(2, len(data)))]) - file_contents.append(f"{column}: {sample_data}") - file_contents.append("") - file_content = "\n".join(file_contents) - - model_url = self._check_model_and_get_model_url(self.model, self.model_type) - payload = { - "query": query, - "response_mode": "blocking", - "user": str(uuid.uuid4()), - "inputs": { - "code_interpreter.files": [{ - "url": str(excel_file_url), - "name": file_name, - }], - "code_interpreter.doc_content": file_content, - }, - "model_configs": { - "first_code_gen.url": model_url, - "followup_code_gen.url": model_url, - } - } - - server_url = self.http_client.service_url(prefix="", sub_path=self.server_sub_path) - response = self.http_client.session.post( - url=server_url, headers=headers, json=payload) - self.http_client.check_response_header(response) - data = response.json() - self.http_client.check_response_json(data) - - figure_url = "" - try: - figure_url = data["result"]["content"][-1]["text"][0] - except Exception as e: - logging.warning( - f"failed to generate figure for query={query}, excel_file_url={excel_file_url}") - return Message(figure_url) - - @components_run_stream_trace - def tool_eval( - self, - streaming: bool, - origin_query: str, - file_urls: dict, - **kwargs, - ): - """ - 对指定的Excel文件进行图表生成和评估。 - - Args: - streaming (bool): 是否以流式传输方式返回结果。如果为True,则通过生成器返回结果;如果为False,则直接返回结果。 - origin_query (str): 原始查询字符串,用于在缺少其他查询参数时使用。 - file_urls (dict): 包含Excel文件信息的字典,其中键为文件名,值为文件URL。 - **kwargs: 其他关键字参数,可以包括查询字符串等。 - - Returns: - 如果streaming为True,则通过生成器返回结果。每个结果是一个字典,包含以下键: - - event (str): 事件类型,始终为'excel_to_figure'。 - - type (str): 数据类型,始终为'files'。 - - text (list of str): 包含生成的图表信息的列表。 - - 如果streaming为False,则直接返回一个包含上述信息的字典。 - - Raises: - ValueError: 如果file_urls的长度不等于1,则抛出异常。 - RuntimeError: 如果Excel文件到图表的转换失败或出现异常,则抛出异常。 - """ - query = kwargs.get("query", "") - if not query: - query = origin_query - try: - if len(file_urls) != 1: - raise ValueError(f"file_urls mismatched, expectd len(file_urls)==1,got {len(file_urls)}") - excel_file_name, excel_file_url = list(file_urls.items())[0] - result_msg = self._run_excel2figure( - query=query, - excel_file_url=excel_file_url, - model=self.model, - excel_file_name=excel_file_name) - - if not result_msg.content: - raise RuntimeError(f"excel to figure failed, retry after modify query") - - result = { - 'event': 'excel_to_figure', - 'type': 'files', - 'text': [result_msg.content], - } - except Exception as e: - raise RuntimeError(f'excel to figure error:{e}') - - if streaming: - yield result - else: - return result diff --git a/python/tests/component_collector.py b/python/tests/component_collector.py index 904c22e01..445dcee5d 100644 --- a/python/tests/component_collector.py +++ b/python/tests/component_collector.py @@ -22,7 +22,6 @@ COMPONENT_WHITE_LIST = [ "RagWithBaiduSearchPro", "RAGWithBaiduSearch", - "Excel2Figure", "MRC", "OralQueryGeneration", "QAPairMining", diff --git a/python/tests/test_appbuilder_sentry_trace_on.py b/python/tests/test_appbuilder_sentry_trace_on.py index 1a426aa24..b8639523a 100644 --- a/python/tests/test_appbuilder_sentry_trace_on.py +++ b/python/tests/test_appbuilder_sentry_trace_on.py @@ -20,8 +20,8 @@ import logging import appbuilder -from appbuilder import AppBuilderTracer, AppbuilderInstrumentor -from appbuilder.utils.trace._function import _components_run_trace_with_sentry,_components_stream_run_trace_with_sentry +from appbuilder import AppbuilderInstrumentor, StyleRewrite +from appbuilder.core.components.v2 import StyleRewrite as StyleRewriteV2 logging.basicConfig(level=logging.INFO) @@ -40,7 +40,6 @@ def test_sentry_normal(self): ImportError: 如果未安装sentry-sdk库,则抛出此异常。 """ - # 配置测试环境 try: subprocess.check_output(['python3','-m','pip', 'install', 'sentry-sdk==1.44.1']) except Exception as e: @@ -48,6 +47,7 @@ def test_sentry_normal(self): os.environ["ENABLE_SENTRY_TRACE"] = "true" os.environ["SENTRY_DSN"] = "test" + os.environ["APPBUILDER_TRACE_DEBUG"] = "true" # 启动跟踪器(仅测试Sentry Trace功能) tracer = AppbuilderInstrumentor() @@ -67,38 +67,37 @@ def test_sentry_normal(self): logging.info("Patch_sentry_sdk_trace_id is initialized successfully.") except Exception as e: print(e) - # 启动事务 with sentry_sdk.start_transaction(op="task", name="UT-Components-trace-test"): # test Components run - play = appbuilder.Playground(prompt_template="你好,{name},我是{bot_name},{bot_name}是一个{bot_type},我可以{bot_function},你可以问我{bot_question}。", model="ERNIE-3.5-8K") - msg = appbuilder.Message({ - "name": "小明", - "bot_name": "机器人", - "bot_type": "聊天机器人", - "bot_function": "聊天", - "bot_question": "你好吗?" - }) - - answer = play.run(message=msg, stream=False, temperature=1) - print(f"Playground Answer: {answer}") + sr = StyleRewrite(model="Qianfan-Agent-Speed-8k") + text = "成都是个包容的城市" + style = "直播话术" + msg = appbuilder.Message(content=text) + run_out = sr.run(message=msg, style=style) + print(run_out) + sr = StyleRewrite(model="Qianfan-Agent-Speed-8k") + tool_eval_out = sr.tool_eval(name="name", query=text, style=style, streaming=True) + for res in tool_eval_out: + print(res) - # test Components tool_eval - audio_file_url = ("https://bj.bcebos.com/v1/appbuilder/asr_test.pcm?authorization=bce-auth-v1" - "%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-11T10%3A56%3A41Z%2F-1%2Fhost" - "%2Fa6c4d2ca8a3f0259f4cae8ae3fa98a9f75afde1a063eaec04847c99ab7d1e411") - asr = appbuilder.ASR() - raw_audio = requests.get(audio_file_url).content - inp = appbuilder.Message(content={"raw_audio": raw_audio}) - result = asr.tool_eval(name="asr", streaming=True, file_url=audio_file_url) - for res in result: - print(f"ASR Tool_Eval Result: {res}") + # test Components v2 tool_eval + sr_v2 = StyleRewriteV2(model="Qianfan-Agent-Speed-8k") + text = "成都是个包容的城市" + style = "直播话术" + tool_eval_out = sr_v2.tool_eval(query=text, style=style) + for res in tool_eval_out: + print(res) # 清理测试环境 try: subprocess.check_output(['python3','-m','pip', 'uninstall', 'sentry-sdk', '-y']) except Exception as e: print('pip uninstall sentry-sdk failed') + del os.environ["ENABLE_SENTRY_TRACE"] + del os.environ["SENTRY_DSN"] + del os.environ["APPBUILDER_TRACE_DEBUG"] + if __name__ == '__main__': unittest.main() \ No newline at end of file diff --git a/python/tests/test_qa_llm_excel2figure.py b/python/tests/test_qa_llm_excel2figure.py deleted file mode 100644 index e5c8f0594..000000000 --- a/python/tests/test_qa_llm_excel2figure.py +++ /dev/null @@ -1,173 +0,0 @@ -# Copyright (c) 2024 Baidu, Inc. 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 unittest -import os -import appbuilder -import appbuilder -class LoadConfig(object): - """ - config - """ - def __init__(self): - """ - 初始化函数,读取配置文件并设置实例属性。 - """ - self.token = os.environ.get("APPBUILDER_TOKEN", "") - self.console_url = os.environ.get("GATEWAY_URL", "https://appbuilder.baidu.com") - self.cookie = os.environ.get("COOKIE", "") - self.csrftoken = os.environ.get('CSRFTOKEN', "") - - log.info("token: %s" % self.token) - log.info("console_url: %s" % self.console_url) - log.info("cookie: %s" % self.cookie) - log.info("csrftoken: %s" % self.csrftoken) - -import random -import string -import os - -class Utils(object): - """ - utils 方法父类 - """ - @staticmethod - def get_random_string(str_len, prefix=None): - """ - 生成随机字符串,可指定前缀 - """ - gen_name = ''.join( - random.choice(string.ascii_letters + string.digits) for _ in range(str_len) - ) - if prefix: - name = str(prefix) + gen_name - else: - name = gen_name - return name - - @staticmethod - def get_data_file(filename): - current_dir = os.path.dirname(os.path.abspath(__file__)) - full_file_path = os.path.join(current_dir, "data", filename) - return full_file_path - -from appbuilder.utils.logger_util import get_logger -from appbuilder.core._exception import ModelNotSupportedException -log = get_logger(__name__) - -text = "用户:喂我想查一下我的话费\n坐席:好的女士您话费余的话还有87.49元钱\n用户:好的知道了谢谢\n坐席:嗯不客气祝您生活愉快再见" - -models = appbuilder.get_model_list("", ["chat"], True) - -file_bos_url = ("https://agi-dev-platform-bos.bj.bcebos.com/ut_appbuilder/[测试]超市收入明细表格.xlsx?authorization=bce-" - "auth-v1/e464e6f951124fdbb2410c590ef9ed2f/2024-02-21T09%3A51%3A14Z/-1/host/1802a9c9142ef328d61e7673db7" - "c1f05842b2af93d18a02ac7ef7aa6f64db54e") -err_file_bos_url = ("https://agi-dev-platform-bos.bj.bcebos.com/ut_appbuilder/[测试]超市收入明细表格.xlsx?authorization=" - "bce-auth-v1/e464e6f951124fdbb2410c590ef9ed2f/2024-02-21T09%3A51%3A14Z/-1/host/1802a9c9142ef328d6" - ) - -class TestExcel2figure(unittest.TestCase): - # @parameterized.expand([ - # param("ERNIE-Bot 4.0", "2020年各个月份的利润分别是多少?使用条形图绘制出来", file_bos_url), - # ] + [param(model, "2020年各个月份的利润分别是多少?使用条形图绘制出来", file_bos_url) for model in models if - # model not in ["Yi-34B-Chat", "ChatLaw", "BLOOMZ-7B", "Qianfan-BLOOMZ-7B-compressed"]]) - # def test_normal_case(self, model_name, query, excel_file_url): - # """ - # 正常用例 - # """ - # # 创建 component 对象,推荐使用 ERNIE-Bot 4.0 获取更稳定的画图效果 - # component = appbuilder.Excel2Figure(model=model_name) - - # # 准备 excel 文件链接,该链接需要是公网可访问的地址 - # # 针对 excel 文件内容绘制图表 - # result = component.run(appbuilder.Message({ - # "query": query, - # "excel_file_url": excel_file_url, - # })) - - # # 输出运行结果 - # content = result.content - # if model_name == "ERNIE-Bot 4.0": - # assert content, "未获取到图片地址" - # time.sleep(1) - - def test_abnormal_case(self): - """ - 异常用例 - """ - # test Model[aaa] not available - with self.assertRaises(ModelNotSupportedException): - builder = appbuilder.Excel2Figure(model="aaa") - - # test query and excel_file_url error - test_list=[ - { - "excel_file_url": file_bos_url - }, - { - "query": "2020年各个月份的利润分别是多少?使用条形图绘制出来", - } - ] - for item in test_list: - try: - builder = appbuilder.Excel2Figure(model="ERNIE-Bot 4.0") - res = builder(builder.run(appbuilder.Message(item))) - except Exception as e: - assert "1 validation error for Excel2FigureArgs" in str(e) - - try: - builder = appbuilder.Excel2Figure(model="ERNIE-Bot 4.0") - res = builder(builder.run(appbuilder.Message({ - "query": "2020年各个月份的利润分别是多少?使用条形图绘制出来"*30, - "excel_file_url": file_bos_url - }))) - except Exception as e: - assert "1 validation error for Excel2FigureArgs" in str(e) - - def test_check_model_and_get_model_url(self): - with self.assertRaises(ModelNotSupportedException): - e2f=appbuilder.Excel2Figure(model="Yi-34B-Chat") - with self.assertRaises(ValueError): - e2f=appbuilder.Excel2Figure(model="") - - def test_run(self): - e2f=appbuilder.Excel2Figure(model="Llama-2-7B-Chat") - msg=appbuilder.Message({ - "query": "2019年各个月份的利润分别是多少?", - "excel_file_url": file_bos_url - }) - result_msg=e2f.run(msg) - - def test_tool_eval(self): - e2f=appbuilder.Excel2Figure(model="Llama-2-7B-Chat") - # with self.assertRaises(RuntimeError) as context: - te=e2f.tool_eval( - streaming=False, - origin_query="", - file_urls={'test1':'test1','test2':'test2'} - ) - with self.assertRaises(RuntimeError): - next(te) - - te=e2f.tool_eval( - streaming=False, - origin_query="2019年各个月份的利润分别是多少?", - file_urls={'[测试]超市收入明细表格.xlsx':file_bos_url} - ) - with self.assertRaises(RuntimeError): - res=next(te) - - -if __name__ == '__main__': - unittest.main() \ No newline at end of file diff --git a/python/tests/test_v2_component_trace.py b/python/tests/test_v2_component_trace.py new file mode 100644 index 000000000..f48361b9a --- /dev/null +++ b/python/tests/test_v2_component_trace.py @@ -0,0 +1,40 @@ +# Copyright (c) 2024 Baidu, Inc. 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 os +import unittest +from appbuilder.core.components.v2 import StyleRewrite +from appbuilder.core.component import ComponentOutput +from appbuilder import AppBuilderTracer + + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") +class TestStyleRewrite(unittest.TestCase): + def setUp(self) -> None: + self.com = StyleRewrite(model="Qianfan-Agent-Speed-8k") + self.tracer = AppBuilderTracer( + enable_phoenix = False, + enable_console = True + ) + + def test_non_stream_tool_eval(self): + self.tracer.start_trace() + text = "成都是个包容的城市" + style = "直播话术" + out = self.com.non_stream_tool_eval(query=text, style=style) + print(out) + self.assertIsInstance(out, ComponentOutput) + self.tracer.end_trace() + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/python/tests/whitelist_components.txt b/python/tests/whitelist_components.txt deleted file mode 100644 index cfabd8879..000000000 --- a/python/tests/whitelist_components.txt +++ /dev/null @@ -1,13 +0,0 @@ -TableOCR -Excel2Figure -HandwriteOCR -GeneralOCR -Translation -ObjectRecognition -DocFormatConverter -MixCardOCR -ASR -PlantRecognition -AnimalRecognition -QRcodeOCR -ImageUnderstand \ No newline at end of file diff --git a/python/utils/trace/_function.py b/python/utils/trace/_function.py index 0c68941a0..2d15dca66 100644 --- a/python/utils/trace/_function.py +++ b/python/utils/trace/_function.py @@ -20,6 +20,7 @@ from appbuilder import Message from appbuilder import AppbuilderTraceException +from appbuilder.core.component import ComponentOutput import logging logging.basicConfig(level=logging.INFO) @@ -723,7 +724,7 @@ def _components_stream_run_trace_with_opentelemetry(tracer, func, *args, **kwarg Generator: 组件流函数返回值的生成器 """ - with tracer.start_as_current_span(_tool_name(args = args)) as span: + with tracer.start_as_current_span(_tool_name(args = args)) as span: start_time = time.time() span.set_attribute("openinference.span.kind",'tool') _input(args = args, kwargs = kwargs, span=span) @@ -731,16 +732,22 @@ def _components_stream_run_trace_with_opentelemetry(tracer, func, *args, **kwarg for item in func(*args, **kwargs): with tracer.start_as_current_span('Component-Stream') as new_span: new_span.set_attribute("openinference.span.kind",'tool') - new_span.set_attribute("output.value", "{}".format(json.dumps(item, ensure_ascii=False))) - if isinstance(item, dict): - run_list.append(item.get('text', None)) + if isinstance(item, ComponentOutput): + new_span.set_attribute("output.value", "{}".format(item.model_dump_json(indent=4))) else: - run_list.append(str(item)) - yield item + new_span.set_attribute("output.value", "{}".format(json.dumps(item, ensure_ascii=False))) + if isinstance(item, dict): + run_list.append(item.get('text', None)) + else: + run_list.append(str(item)) + yield item end_time = time.time() _time(start_time = start_time,end_time = end_time,span = span) - result_str = ''.join(str(res) for res in run_list) - span.set_attribute("output.value",result_str) + if run_list: + result_str = ''.join(str(res) for res in run_list) + span.set_attribute("output.value",result_str) + else: + span.set_attribute("output.value","流式运行结束") def _components_stream_run_trace_with_sentry(func, *args, **kwargs): """ @@ -766,15 +773,21 @@ def _components_stream_run_trace_with_sentry(func, *args, **kwargs): for item in func(*args, **kwargs): with sentry_sdk.start_span(op=_tool_name(args=args), description=_tool_name(args=args)) as new_span: new_span.set_data("Span-kind",'tool') - new_span.set_data("output-value", "{}".format(json.dumps(item, ensure_ascii=False))) - if isinstance(item, dict): - text = item.get('text', None) - run_list.append(text) + if isinstance(item, ComponentOutput): + new_span.set_data("output.value", "{}".format(item.model_dump_json(indent=4))) else: - run_list.append(str(item)) - yield item - result_str = ''.join(str(res) for res in run_list) - span.set_data("output-value",result_str) + new_span.set_data("output-value", "{}".format(json.dumps(item, ensure_ascii=False))) + if isinstance(item, dict): + text = item.get('text', None) + run_list.append(text) + else: + run_list.append(str(item)) + yield item + if run_list: + result_str = ''.join(str(res) for res in run_list) + span.set_data("output-value",result_str) + else: + span.set_data("output-value","流式运行结束") def _list_trace(tracer, func, *args, **kwargs): From 26c4a58447bafd573a731f1b9f6e606c67be9850 Mon Sep 17 00:00:00 2001 From: peiwenYe <963623403@qq.com> Date: Tue, 3 Dec 2024 15:42:31 +0800 Subject: [PATCH 49/85] =?UTF-8?q?=E5=A2=9E=E5=8A=A0v2=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E5=B9=BB=E8=A7=89=E6=A3=80=E6=B5=8B=E7=BB=84=E4=BB=B6=20(#633)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 增加v2版本幻觉检测组件 * 增加v2版本幻觉检测组件 * 修改注释;把manifest不包含parameters和required变为合法输入 --------- Co-authored-by: yepeiwen01 --- python/core/components/v2/__init__.py | 4 +- .../llms/hallucination_detection/__init__.py | 1 + .../llms/hallucination_detection/component.py | 234 ++++++++++++++++++ .../v2/llms/style_rewrite/component.py | 4 +- python/tests/component_check.py | 34 +-- python/tests/component_tool_eval_cases.py | 34 ++- .../tests/test_v2_hallucination_detection.py | 138 +++++++++++ 7 files changed, 418 insertions(+), 31 deletions(-) create mode 100644 python/core/components/v2/llms/hallucination_detection/__init__.py create mode 100644 python/core/components/v2/llms/hallucination_detection/component.py create mode 100644 python/tests/test_v2_hallucination_detection.py diff --git a/python/core/components/v2/__init__.py b/python/core/components/v2/__init__.py index ada4fd231..7267a4ef8 100644 --- a/python/core/components/v2/__init__.py +++ b/python/core/components/v2/__init__.py @@ -15,9 +15,11 @@ from .animal_recognize.component import AnimalRecognition from .image_understand.component import ImageUnderstand from .llms.style_rewrite.component import StyleRewrite +from .llms.hallucination_detection.component import HallucinationDetection __V2_COMPONENTS__ = [ "AnimalRecognition", "ImageUnderstand", - "StyleRewrite" + "StyleRewrite", + "HallucinationDetection" ] # NOQA \ No newline at end of file diff --git a/python/core/components/v2/llms/hallucination_detection/__init__.py b/python/core/components/v2/llms/hallucination_detection/__init__.py new file mode 100644 index 000000000..072825920 --- /dev/null +++ b/python/core/components/v2/llms/hallucination_detection/__init__.py @@ -0,0 +1 @@ +from .component import HallucinationDetection \ No newline at end of file diff --git a/python/core/components/v2/llms/hallucination_detection/component.py b/python/core/components/v2/llms/hallucination_detection/component.py new file mode 100644 index 000000000..627abf496 --- /dev/null +++ b/python/core/components/v2/llms/hallucination_detection/component.py @@ -0,0 +1,234 @@ +# Copyright (c) 2023 Baidu, Inc. 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 pydantic import BaseModel, Field +from typing import Optional + +from appbuilder.core.components.llms.base import CompletionBaseComponent, ModelArgsConfig +from appbuilder.core.message import Message +from appbuilder.core._exception import AppBuilderServerException +from appbuilder.utils.logger_util import logger +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace +from appbuilder.core.components.llms.hallucination_detection.base import HallucinationDetectionArgs + + +class HallucinationDetection(CompletionBaseComponent): + """ + 幻觉检测。输入,判断answer中是否存在幻觉。 + *注:该组件推荐使用Qianfan-Agent-Speed-8k模型。* + + Examples: + + .. code-block:: python + + import os + import appbuilder + # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 + os.environ['APPBUILDER_TOKEN'] = '...' + + hallucination_detection = appbuilder.HallucinationDetection() + + query = '' + context = \ + '''澳门美食: 澳门新麻蒲韩国烤肉店 + 在澳门一年四季之中除了火锅,烤肉也相当受欢迎。提到韩烧,有一间令我印象最深刻,就是号称韩国第一的烤肉店-新麻蒲韩国烤肉店,光是韩国的分店便多达四百多间,海外分店更是遍布世界各地,2016年便落户澳门筷子基区,在原本已经食肆林立的地方一起百花齐放!店内的装修跟韩国分店还完度几乎没差,让食客彷如置身于韩国的感觉,还要大赞其抽风系统不俗,离开时身上都不会沾上烤肉味耶! + 时间:周一至周日 下午5:00 - 上午3:00 + 电话:+853 2823 4012 + 地址:澳门筷子基船澳街海擎天第三座地下O号铺96号 + 必食推介: + 护心肉二人套餐 + 来新麻蒲必试的有两样东西,现在差不多每间烤肉店都有炉边烤蛋,但大家知道吗?原来新麻蒲就是炉边烤蛋的开创者,既然是始祖,这已经是个非吃不可的理由!还有一款必试的就是护心肉,即是猪的横隔膜与肝中间的部分,每头猪也只有200克这种肉,非常珍贵,其味道吃起来有种独特的肉香味,跟牛护心肉一样精彩! + 秘制猪皮 + 很多怕胖的女生看到猪皮就怕怕,但其实猪皮含有大量胶原蛋白,营养价值很高呢!这里红通通的猪皮还经过韩国秘制酱汁处理过,会有一点点辣味。烤猪皮的时候也需特别注意火侯,这样吃起来才会有外脆内Q的口感!''' + answer = '澳门新麻蒲烤肉店并不是每天开门。' + + inputs = {'query': query, 'context': context, 'answer': answer} + msg = appbuilder.Message(inputs) + result = hallucination_detection.run(msg) + + print(result) + """ + name = 'hallucination_detection' + version = 'v1' + meta = HallucinationDetectionArgs + + manifests = [ + { + "name": "hallucination_detection", + "description": "输入用户查询query、检索结果context以及根据检索结果context生成的用户查询query的回答answer,判断answer" \ + "中是否存在幻觉。", + "parameters": { + "type": "object", + "properties": { + "query": { + "text": "string", + "description": "用户查询。" + }, + "context": { + "text": "string", + "description": "检索结果。" + }, + "answer": { + "text": "string", + "description": "根据检索结果context生成的用户查询query的回答answer。" + } + }, + "required": [ + "query", + "context", + "answer" + ] + } + } + ] + + def __init__( + self, + model="Qianfan-Agent-Speed-8K", + secret_key: Optional[str] = None, + gateway: str = "", + lazy_certification: bool = True, + ): + """初始化幻觉检测组件。 + + Args: + model (str|None): 模型名称,用于指定要使用的千帆模型。推荐使用Qianfan-Agent-Speed-8k模型。 + secret_key (str, 可选): 用户鉴权token, 默认从环境变量中获取: os.getenv("APPBUILDER_TOKEN", ""). + gateway (str, 可选): 后端网关服务地址,默认从环境变量中获取: os.getenv("GATEWAY_URL", "") + lazy_certification (bool, 可选): 延迟认证,为True时在第一次运行时认证. Defaults to False. + + Returns: + None + + """ + super().__init__(HallucinationDetectionArgs, + model=model, + secret_key=secret_key, + gateway=gateway, + lazy_certification=lazy_certification) + + def completion(self, version, base_url, request, timeout: float = None, + retry: int = 0): + """ + Send a byte array of an audio file to obtain the result of speech recognition. + + Args: + version (str): API version. + base_url (str): Base URL of the API. + request (Request): Request object containing audio file and other parameters. + timeout (float, optional): Timeout for the request. Defaults to None. + retry (int, optional): Number of retries for the request. Defaults to 0. + + Returns: + Response: Processed response object. + + """ + headers = self.http_client.auth_header() + headers["Content-Type"] = "application/json" + + stream = True if request.response_mode == "streaming" else False + + url = self.http_client.service_url("/app/hallucination_detection", self.base_url) + logger.debug( + "request url: {}, method: {}, json: {}, headers: {}".format(url, + "POST", + request.params, + headers)) + response = self.http_client.session.post(url, json=request.params, headers=headers, timeout=timeout, + stream=stream) + + logger.debug( + "request url: {}, method: {}, json: {}, headers: {}, response: {}".format(url, "POST", + request.params, + headers, + response)) + return self.gene_response(response, stream) + + @components_run_trace + def run(self, message, stream=False, temperature=1e-10, top_p=0.0): + """ + 使用给定的输入运行模型并返回结果。 + + Args: + message (Message): 输入消息,包含 query、context 和 answer。是必需的参数。 + stream (bool, 可选): 是否以流式形式返回响应。默认为 False。 + temperature (float, 可选): 模型配置的温度参数,用于调整模型的生成概率。 + 取值范围为 0.0 到 1.0,较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 + top_p (float, 可选): 影响输出文本的多样性,取值越大,生成文本的多样性越强。 + 取值范围为 0.0 到 1.0,较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 + + Returns: + result (Message): 模型运行后的输出消息。 + + Raises: + AssertionError: 如果输入的 message 中缺少 query、context 或 answer。 + AppBuilderServerException: 如果请求执行失败,将抛出异常,包含服务错误码和错误信息。 + """ + inputs = message.content + query = inputs.pop('query', None) + assert query, 'You must input query and query should not be empty' + assert 'context' in inputs and inputs['context'], 'You must input context and context should not be empty' + assert 'answer' in inputs and inputs['answer'], 'You must input answer and answer should not be empty' + response_mode = "streaming" if stream else "blocking" + user_id = message.id + model_config_inputs = ModelArgsConfig(**{"stream": stream, "temperature": temperature, "top_p": top_p}) + model_config = self.get_model_config(model_config_inputs) + + request = self.gene_request(query, inputs, response_mode, user_id, model_config) + response = self.completion(self.version, self.base_url, request) + + if response.error_no != 0: + raise AppBuilderServerException(service_err_code=response.error_no, service_err_message=response.error_msg) + + result = response.to_message() + + return result + + @components_run_stream_trace + def tool_eval(self, + query: str, + context: str, + answer: str, + **kwargs): + """ + 调用函数进行工具评估。 + + Args: + - query (str): 查询语句。 + - context (str): 上下文信息。 + - answer (str): 参考答案。 + **kwargs: 关键字参数,包含评估所需的输入参数。 + - model_configs (dict, optional): 模型配置信息,默认为空字典。包含以下字段: + - temperature (float, optional): 温度参数,用于控制生成文本的随机性,默认为1e-10。 + - top_p (float, optional): 截断概率,用于控制生成文本的质量,默认为0.0。 + + Returns: + ComponentOutput: 组件输出结果,包含评估结果。 + + Raises: + ValueError: 如果缺少query、context或answer参数,将引发此异常。 + """ + if not query or not context or not answer: + raise ValueError('param `query` and `context` and `answer` are required') + msg = Message({'query': query, 'context': context, 'answer': answer}) + model_configs = kwargs.get('model_configs', {}) + temperature = model_configs.get('temperature', 1e-10) + top_p = model_configs.get('top_p', 0.0) + message = self.run(message=msg, + stream=True, + temperature=temperature, + top_p=top_p) + + for data in message.content: + yield self.create_output(type="text", text=data, usage=message.token_usage, name="text") \ No newline at end of file diff --git a/python/core/components/v2/llms/style_rewrite/component.py b/python/core/components/v2/llms/style_rewrite/component.py index 288e47615..f2cfa6eb4 100644 --- a/python/core/components/v2/llms/style_rewrite/component.py +++ b/python/core/components/v2/llms/style_rewrite/component.py @@ -65,10 +65,10 @@ class StyleRewrite(CompletionBaseComponent): def __init__( self, - model=None, + model: str="Qianfan-Agent-Speed-8K", secret_key: Optional[str] = None, gateway: str = "", - lazy_certification: bool = False, + lazy_certification: bool = True, ): """初始化StyleRewrite模型。 diff --git a/python/tests/component_check.py b/python/tests/component_check.py index ecbeb018a..80c233c4a 100644 --- a/python/tests/component_check.py +++ b/python/tests/component_check.py @@ -147,33 +147,15 @@ def check(self, component_cls) -> CheckInfo: if not manifests or len(manifests) == 0: raise ValueError("No manifests found") manifest = manifests[0] - properties = manifest['parameters']['properties'] - manifest_var = properties.keys() - required_params = [] - anyOf = manifest['parameters'].get('anyOf', None) - required_exists = False - if anyOf: - for anyOf_dict in anyOf: - if 'required' in anyOf_dict: - required_exists = True - required_params += anyOf_dict['required'] - - if not anyOf: - if 'required' in manifest['parameters']: - required_exists = True - required_params += manifest['parameters']['required'] - - if not required_exists: - check_pass_flag = False - invalid_details.append("mainfest 未定义required参数") - return CheckInfo( - check_rule_name=self.rule_name, - check_result=check_pass_flag, - check_detail=",".join(invalid_details)) - + if 'parameters' in manifest and 'properties' in manifest['parameters']: + properties = manifest['parameters']['properties'] + manifest_var = properties.keys() + else: + manifest_var = [] + # 交互检查 tool_eval_input_params = [] - print("required_params: {}".format(required_params)) + print("required_params: {}".format(manifest_var)) signature = inspect.signature(component_cls.tool_eval) ileagal_params = [] for param_name, param in signature.parameters.items(): @@ -188,7 +170,7 @@ def check(self, component_cls) -> CheckInfo: invalid_details.append("tool_eval 参数 {} 不在 mainfest 参数列表中".format(",".join(ileagal_params))) ileagal_params =[] - for required_param in required_params: + for required_param in manifest_var: if required_param not in tool_eval_input_params: check_pass_flag = False ileagal_params.append(required_param) diff --git a/python/tests/component_tool_eval_cases.py b/python/tests/component_tool_eval_cases.py index ab96ae8a2..dc3fbed5b 100644 --- a/python/tests/component_tool_eval_cases.py +++ b/python/tests/component_tool_eval_cases.py @@ -88,7 +88,7 @@ def inputs(self): def schemas(self): return [url_schema] -class StypeRewriteCase(Case): +class StyleRewriteCase(Case): def init_args(self): return {"model": "Qianfan-Agent-Speed-8k"} @@ -99,10 +99,40 @@ def schemas(self): return [text_schema] +class HallucinationDetectionCase(Case): + def inputs(self): + return { + "query": '澳门新麻蒲烤肉店每天开门吗?', + "context": ('澳门美食: 澳门新麻蒲韩国烤肉店\n' + '在澳门一年四季之中除了火锅,烤肉也相当受欢迎。提到韩烧,有一间令我印象最深刻,就是号称韩国第一的烤肉店-新麻蒲韩国烤肉店,光是韩国的分店便多' + '达四百多间,海外分店更是遍布世界各地,2016年便落户澳门筷子基区,在原本已经食肆林立的地方一起百花齐放!店内的装修跟韩国分店还完度几乎没差,让' + '食客彷如置身于韩国的感觉,还要大赞其抽风系统不俗,离开时身上都不会沾上烤肉味耶!\n' + '时间:周一至周日 下午5:00 - 上午3:00\n' + '电话:+853 2823 4012\n' + '地址:澳门筷子基船澳街海擎天第三座地下O号铺96号\n' + '必食推介:\n' + '护心肉二人套餐\n' + '来新麻蒲必试的有两样东西,现在差不多每间烤肉店都有炉边烤蛋,但大家知道吗?原来新麻蒲就是炉边烤蛋的开创者,既然是始祖,这已经是个非吃不可的理' + '由!还有一款必试的就是护心肉,即是猪的横隔膜与肝中间的部分,每头猪也只有200克这种肉,非常珍贵,其味道吃起来有种独特的肉香味,跟牛护心肉一样' + '精彩!\n' + '秘制猪皮\n' + '很多怕胖的女生看到猪皮就怕怕,但其实猪皮含有大量胶原蛋白,营养价值很高呢!这里红通通的猪皮还经过韩国秘制酱汁处理过,会有一点点辣味。烤猪皮的' + '时候也需特别注意火侯,这样吃起来才会有外脆内Q的口感!'), + "answer": '澳门新麻蒲烤肉店并不是每天开门,周日休息。' + } + + def schemas(self): + return [text_schema] + + def outputs(self): + return {"text": ["存在幻觉"]} + + component_tool_eval_cases = { "AnimalRecognition": AnimalRecognitionCase, "ImageUnderstand": ImageUnderstandCase, "ASR": ASRCase, "TreeMind": TreeMindCase, - "StyleRewrite": StypeRewriteCase + "StyleRewrite": StyleRewriteCase, + "HallucinationDetection": HallucinationDetectionCase } \ No newline at end of file diff --git a/python/tests/test_v2_hallucination_detection.py b/python/tests/test_v2_hallucination_detection.py new file mode 100644 index 000000000..3aff6254e --- /dev/null +++ b/python/tests/test_v2_hallucination_detection.py @@ -0,0 +1,138 @@ +# Copyright (c) 2023 Baidu, Inc. 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 os +import unittest +import appbuilder + +from appbuilder.core._exception import AppBuilderServerException +from appbuilder.core.components.v2 import HallucinationDetection + + +TEST_QUERY = '澳门新麻蒲烤肉店每天开门吗?' +TEST_CONTEXT = \ +('澳门美食: 澳门新麻蒲韩国烤肉店\n' +'在澳门一年四季之中除了火锅,烤肉也相当受欢迎。提到韩烧,有一间令我印象最深刻,就是号称韩国第一的烤肉店-新麻蒲韩国烤肉店,光是韩国的分店便多' +'达四百多间,海外分店更是遍布世界各地,2016年便落户澳门筷子基区,在原本已经食肆林立的地方一起百花齐放!店内的装修跟韩国分店还完度几乎没差,让' +'食客彷如置身于韩国的感觉,还要大赞其抽风系统不俗,离开时身上都不会沾上烤肉味耶!\n' +'时间:周一至周日 下午5:00 - 上午3:00\n' +'电话:+853 2823 4012\n' +'地址:澳门筷子基船澳街海擎天第三座地下O号铺96号\n' +'必食推介:\n' +'护心肉二人套餐\n' +'来新麻蒲必试的有两样东西,现在差不多每间烤肉店都有炉边烤蛋,但大家知道吗?原来新麻蒲就是炉边烤蛋的开创者,既然是始祖,这已经是个非吃不可的理' +'由!还有一款必试的就是护心肉,即是猪的横隔膜与肝中间的部分,每头猪也只有200克这种肉,非常珍贵,其味道吃起来有种独特的肉香味,跟牛护心肉一样' +'精彩!\n' +'秘制猪皮\n' +'很多怕胖的女生看到猪皮就怕怕,但其实猪皮含有大量胶原蛋白,营养价值很高呢!这里红通通的猪皮还经过韩国秘制酱汁处理过,会有一点点辣味。烤猪皮的' +'时候也需特别注意火侯,这样吃起来才会有外脆内Q的口感!') +TEST_ANSWER = '澳门新麻蒲烤肉店并不是每天开门,周日休息。' + + +class TestHallucinationDetectionComponent(unittest.TestCase): + def setUp(self): + """ + 设置环境变量。 + + Args: + 无参数,默认值为空。 + + Returns: + 无返回值,方法中执行了环境变量的赋值操作。 + """ + + self.hallucination_detection = HallucinationDetection() + + def test_run_with_default_params(self): + """测试 run 方法使用默认参数 + """ + query = TEST_QUERY + context = TEST_CONTEXT + answer = TEST_ANSWER + msg = appbuilder.Message({'query': query, 'context': context, 'answer': answer}) + answer = self.hallucination_detection(msg) + # print(answer) + self.assertIsNotNone(answer) + print(f'\n[result]\n{answer.content}\n') + + def test_run_with_stream_and_temperature(self): + """测试不同的 stream 和 temperature 参数值 + """ + query = TEST_QUERY + context = TEST_CONTEXT + answer = TEST_ANSWER + msg = appbuilder.Message({'query': query, 'context': context, 'answer': answer}) + answer = self.hallucination_detection(msg, stream=False, temperature=0.5) + # print(answer) + self.assertIsNotNone(answer) + print(f'\n[result]\n{answer.content}\n') + + def test_tool_eval_with_default_params(self): + """测试 tool_eval 方法使用默认参数 + """ + query = TEST_QUERY + context = TEST_CONTEXT + answer = TEST_ANSWER + answer = self.hallucination_detection.tool_eval(query=query, context=context, answer=answer) + print(answer) + self.assertIsNotNone(answer) + print(f'\n[result]\n{answer}\n') + + def test_tool_eval_with_model_configs(self): + """测试 tool_eval 方法使用不同temperature和top_p参数值。 + """ + query = TEST_QUERY + context = TEST_CONTEXT + answer = TEST_ANSWER + model_configs = {'temperature': 0.5, 'top_p': 0.5} + answer = self.hallucination_detection.tool_eval(query=query, + context=context, + answer=answer, + model_configs=model_configs) + print(answer) + self.assertIsNotNone(answer) + print(f'\n[result]\n') + for ans in answer: + print(ans) + + def test_tool_eval_with_default_params(self): + """测试 tool_eval 方法使用默认参数 + """ + query = TEST_QUERY + context = TEST_CONTEXT + answer = TEST_ANSWER + answer = self.hallucination_detection.tool_eval(query=query, context=context, answer=answer) + print(answer) + self.assertIsNotNone(answer) + print(f'\n[result]\n{answer}\n') + + def test_tool_eval_with_model_configs(self): + """测试 tool_eval 方法使用不同temperature和top_p参数值。 + """ + query = TEST_QUERY + context = TEST_CONTEXT + answer = TEST_ANSWER + model_configs = {'temperature': 0.5, 'top_p': 0.5} + answer = self.hallucination_detection.tool_eval(query=query, + context=context, + answer=answer, + model_configs=model_configs) + # print(answer) + self.assertIsNotNone(answer) + print(f'\n[result]\n') + for ans in answer: + print(ans) + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file From df52e7d552826aa2c429ca071535be76a81a81ba Mon Sep 17 00:00:00 2001 From: peiwenYe <963623403@qq.com> Date: Tue, 3 Dec 2024 20:18:18 +0800 Subject: [PATCH 50/85] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=8D=95=E6=B5=8B?= =?UTF-8?q?=E6=A1=86=E6=9E=B6-=E8=A7=84=E5=88=99=E5=92=8C=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E5=88=86=E7=A6=BB=EF=BC=8C=E4=BE=BF=E4=BA=8ESDK-ext?= =?UTF-8?q?=E5=BC=95=E7=94=A8=20(#634)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 优化单测框架 * 优化单测框架 * 优化单测框架 * 优化单测框架 * 优化单测框架 * 优化单测框架 * 优化单测框架 * 单测框架优化 * 单测框架优化 --------- Co-authored-by: yepeiwen01 --- python/tests/component_check.py | 63 +++++------- python/tests/test_all_components.py | 145 ++++++++++++++++++++-------- 2 files changed, 131 insertions(+), 77 deletions(-) diff --git a/python/tests/component_check.py b/python/tests/component_check.py index 80c233c4a..c4fd34212 100644 --- a/python/tests/component_check.py +++ b/python/tests/component_check.py @@ -1,16 +1,3 @@ -# Copyright (c) 2024 Baidu, Inc. 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 os import json import inspect @@ -19,10 +6,8 @@ from pydantic import BaseModel from typing import Generator from appbuilder.utils.func_utils import Singleton -from appbuilder.utils.json_schema_to_model import json_schema_to_pydantic_model from appbuilder.tests.component_schemas import type_to_json_schemas -from component_tool_eval_cases import component_tool_eval_cases - +from appbuilder.utils.json_schema_to_model import json_schema_to_pydantic_model class CheckInfo(BaseModel): check_rule_name: str @@ -71,12 +56,6 @@ def notify(self, component_cls) -> tuple[bool, list]: else: return False, reasons -def register_component_check_rule(rule_name: str, rule_cls: RuleBase): - component_checker = ComponentCheckBase() - component_checker.register_rule(rule_name, rule_cls()) - - - class ManifestValidRule(RuleBase): """ 通过尝试将component的manifest转换为pydantic模型来检查manifest是否符合规范 @@ -152,7 +131,7 @@ def check(self, component_cls) -> CheckInfo: manifest_var = properties.keys() else: manifest_var = [] - + # 交互检查 tool_eval_input_params = [] print("required_params: {}".format(manifest_var)) @@ -245,9 +224,10 @@ class ToolEvalOutputJsonRule(RuleBase): """ 检查tool_eval的输出结果是否符合对应的json schema """ - def __init__(self): + def __init__(self, **kwargs): super().__init__() self.rule_name = 'ToolEvalOutputJsonRule' + self.component_tool_eval_cases = kwargs.get("component_tool_eval_cases") def _check_pre_format(self, outputs): invalid_details = [] @@ -270,7 +250,7 @@ def _check_jsonschema(self, outputs, output_schemas): """检查输出格式是否符合对应的json schema """ invalid_details = [] - if len(invalid_details:=self._check_pre_format(outputs)) > 0 : + if len(self._check_pre_format(outputs)) > 0 : return invalid_details for content in outputs["content"]: @@ -351,19 +331,26 @@ def _check_text_and_code(self, component_case, output_dict): def check(self, component_cls) -> CheckInfo: invalid_details = [] component_cls_name = component_cls.__name__ - if component_cls_name not in component_tool_eval_cases: + + if component_cls_name not in self.component_tool_eval_cases: invalid_details.append("{} 没有添加测试case到 component_tool_eval_cases 中".format(component_cls_name)) else: - component_case = component_tool_eval_cases[component_cls_name]() + component_case = self.component_tool_eval_cases[component_cls_name]() + + envs = {} + if hasattr(component_case, "envs"): + envs = component_case.envs() + os.environ.update(envs) + input_dict = component_case.inputs() - output_json_schemas = component_case.schemas() init_args = component_case.init_args() component_obj = component_cls(**init_args) + output_json_schemas = component_case.schemas() - try: #校验流式输出 + try: stream_output_dict = {"text": "", "oral_text":"", "code": ""} stream_outputs = component_obj.tool_eval(**input_dict) - for stream_output in stream_outputs: + for stream_output in stream_outputs: #校验流式输出 iter_invalid_detail = self._check_jsonschema(stream_output.model_dump(), output_json_schemas) invalid_details.extend(["流式" + error_message for error_message in iter_invalid_detail]) iter_output_dict = self._gather_iter_outputs(stream_output) @@ -376,15 +363,18 @@ def check(self, component_cls) -> CheckInfo: invalid_details.append("ToolEval执行失败: {}".format(e)) time.sleep(2) - try: #校验非流式输出 + try: non_stream_outputs = component_obj.non_stream_tool_eval(**input_dict) - non_stream_invalid_details = self._check_jsonschema(non_stream_outputs.model_dump(), output_json_schemas) + non_stream_invalid_details = self._check_jsonschema(non_stream_outputs.model_dump(), output_json_schemas) #校验非流式输出 invalid_details.extend(["非流式" + error_message for error_message in non_stream_invalid_details]) if len(invalid_details) == 0: non_stream_output_dict = self._gather_iter_outputs(non_stream_outputs) invalid_details.extend(self._check_text_and_code(component_case, non_stream_output_dict)) except Exception as e: invalid_details.append(" NonStreamToolEval执行失败: {}".format(e)) + + for env in envs: + os.environ.pop(env) if len(invalid_details) > 0: return CheckInfo( @@ -396,9 +386,8 @@ def check(self, component_cls) -> CheckInfo: check_rule_name=self.rule_name, check_result=True, check_detail="") - -register_component_check_rule("ManifestValidRule", ManifestValidRule) -register_component_check_rule("MainfestMatchToolEvalRule", MainfestMatchToolEvalRule) -register_component_check_rule("ToolEvalInputNameRule", ToolEvalInputNameRule) -register_component_check_rule("ToolEvalOutputJsonRule", ToolEvalOutputJsonRule) \ No newline at end of file + +def register_component_check_rule(rule_name: str, rule_cls: RuleBase, init_args={}): + component_checker = ComponentCheckBase() + component_checker.register_rule(rule_name, rule_cls(**init_args)) \ No newline at end of file diff --git a/python/tests/test_all_components.py b/python/tests/test_all_components.py index 6c6665346..f084b3c81 100644 --- a/python/tests/test_all_components.py +++ b/python/tests/test_all_components.py @@ -1,29 +1,80 @@ -# Copyright (c) 2024 Baidu, Inc. 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 unittest +from multiprocessing import Pool import os import numpy as np import pandas as pd - -from appbuilder.core.component import Component -from appbuilder.core.components.llms.base import CompletionBaseComponent +import unittest +import os +from appbuilder.tests.component_check import ComponentCheckBase +from appbuilder.tests.component_check import register_component_check_rule +from appbuilder.tests.component_check import ManifestValidRule, MainfestMatchToolEvalRule, ToolEvalInputNameRule, ToolEvalOutputJsonRule from appbuilder.core._exception import AppbuilderBuildexException from component_collector import get_all_components, get_v2_components, get_component_white_list -from appbuilder.tests.component_check import ComponentCheckBase - - -def write_error_data(txt_file_path, error_df,error_stats): +from component_tool_eval_cases import component_tool_eval_cases + +register_component_check_rule("ManifestValidRule", ManifestValidRule, {}) +register_component_check_rule("MainfestMatchToolEvalRule", MainfestMatchToolEvalRule, {}) +register_component_check_rule("ToolEvalInputNameRule", ToolEvalInputNameRule, {}) +register_component_check_rule("ToolEvalOutputJsonRule", ToolEvalOutputJsonRule, \ + {"component_tool_eval_cases": component_tool_eval_cases}) + + +def check_component_with_retry(component_import_res_tuple): + """ + 使用重试机制检查组件。测试用例失败后会重试两次。 + + Args: + component_import_res_tuple (tuple): 包含组件和导入结果的元组。 + + Returns: + list: 包含错误信息的数据列表。 + + """ + component, import_res = component_import_res_tuple + component_check_base = ComponentCheckBase() + + error_data = [] + max_retries = 2 # 设置最大重试次数 + attempts = 0 + + while attempts <= max_retries: + if import_res["import_error"] != "": + error_data.append({"Component Name": component, "Error Message": import_res["import_error"]}) + print("组件名称:{} 错误信息:{}".format(component, import_res["import_error"])) + break + + component_obj = import_res["obj"] + try: + # 此处的self.component_check_base.notify需要根据实际情况修改 + pass_check, reasons = component_check_base.notify(component_obj) # 示例修改 + reasons = list(set(reasons)) + if not pass_check: + error_data.append({"Component Name": component, "Error Message": ", ".join(reasons)}) + print("组件名称:{} 错误信息:{}".format(component, ", ".join(reasons))) + # 如果检查失败,增加尝试次数并重试 + attempts += 1 + if attempts <= max_retries: + print("组件名称:{} 将重试,当前尝试次数:{}".format(component, attempts)) + continue + # 如果检查通过,则退出循环 + break + except Exception as e: + error_data.append({"Component Name": component, "Error Message": str(e)}) + print("组件名称:{} 错误信息:{}".format(component, str(e))) + # 如果发生异常,增加尝试次数并重试 + attempts += 1 + if attempts <= max_retries: + print("组件名称:{} 将重试,当前尝试次数:{}".format(component, attempts)) + continue + + return error_data + +def write_error_data(txt_file_path, error_df, error_stats): + """将组件错误信息写入文件 + + Args: + error_df (Union[pd.DataFrame, None]): 错误信息表格 + error_stats (dict): 错误统计信息 + """ with open(txt_file_path, 'w') as file: file.write("Component Name\tError Message\n") for _, row in error_df.iterrows(): @@ -35,33 +86,46 @@ def write_error_data(txt_file_path, error_df,error_stats): @unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestComponentManifestsAndToolEval(unittest.TestCase): + """ + 组件manifests和tool_eval入参测试类 + Args: + 无 + + Returns: + 无返回值 + + Raises: + 无 + + """ def setUp(self) -> None: + """初始化测试用例,设置component名单和白名单,并初始化ComponentCheckBase实例 + Args: + 无 + Returns: + 无 + """ self.all_components = get_all_components() self.v2_components = get_v2_components() self.whitelist_components = get_component_white_list() - self.component_check_base = ComponentCheckBase() def _test_component(self, components, whitelist_components, txt_file_path): + """测试所有组件的manifests和tool_eval入参 + Args: + 无 + Raises: + AppbuilderBuildexException: 如果有任何组件不在白名单中,则抛出异常 + """ error_data = [] error_stats ={} - for name, import_res in components.items(): - - if import_res["import_error"] != "": - error_data.append({"Component Name": name, "Error Message": import_res["import_error"]}) - print("组件名称:{} 错误信息:{}".format(name, import_res["import_error"])) - continue - - component_obj = import_res["obj"] - try: - pass_check, reasons = self.component_check_base.notify(component_obj) - reasons = list(set(reasons)) - if not pass_check: - error_data.append({"Component Name": name, "Error Message": ", ".join(reasons)}) - print("组件名称:{} 错误信息:{}".format(name, ", ".join(reasons))) - except Exception as e: - error_data.append({"Component Name": name, "Error Message": str(e)}) - print("组件名称:{} 错误信息:{}".format(name, str(e))) + with Pool(processes=os.cpu_count()) as pool: + # 使用pool.map来执行多进程 + results = pool.map(check_component_with_retry, components.items()) + + # 合并每个进程返回的错误数据 + for result in results: + error_data.extend(result) error_df = pd.DataFrame(error_data) if len(error_data) > 0 else None @@ -89,11 +153,12 @@ def _test_component(self, components, whitelist_components, txt_file_path): else: print("\n所有组件测试通过,无错误信息。") - def test_all_components(self): + """测试旧版本组件""" self._test_component(self.all_components, self.whitelist_components, 'components_error_info.txt') def test_v2_components(self): + """测试v2版本组件""" self._test_component(self.v2_components, [], 'v2_components_error_info.txt') From f37f598989658ff8c9c7918ceef037fa2ea6da2e Mon Sep 17 00:00:00 2001 From: Chengmo Date: Wed, 4 Dec 2024 10:47:31 +0800 Subject: [PATCH 51/85] =?UTF-8?q?Update=20Component=EF=BC=9AQRcode/HandWri?= =?UTF-8?q?te/MixCard=20OCR=20(#635)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update Component:QRcode/HandWrite/MixCard OCR --- python/core/components/v2/__init__.py | 8 +- .../components/v2/handwrite_ocr/__init__.py | 13 + .../components/v2/handwrite_ocr/component.py | 223 ++++++++++++++++ .../core/components/v2/handwrite_ocr/model.py | 176 +++++++++++++ .../components/v2/mix_card_ocr/__init__.py | 13 + .../components/v2/mix_card_ocr/component.py | 249 ++++++++++++++++++ .../core/components/v2/mix_card_ocr/model.py | 192 ++++++++++++++ .../core/components/v2/qrcode_ocr/__init__.py | 0 .../components/v2/qrcode_ocr/component.py | 227 ++++++++++++++++ python/core/components/v2/qrcode_ocr/model.py | 130 +++++++++ python/tests/component_tool_eval_cases.py | 55 +++- python/tests/test_v2_handwrite_ocr.py | 93 +++++++ python/tests/test_v2_mix_card_ocr.py | 104 ++++++++ python/tests/test_v2_qrcode_ocr.py | 160 +++++++++++ 14 files changed, 1641 insertions(+), 2 deletions(-) create mode 100644 python/core/components/v2/handwrite_ocr/__init__.py create mode 100644 python/core/components/v2/handwrite_ocr/component.py create mode 100644 python/core/components/v2/handwrite_ocr/model.py create mode 100644 python/core/components/v2/mix_card_ocr/__init__.py create mode 100644 python/core/components/v2/mix_card_ocr/component.py create mode 100644 python/core/components/v2/mix_card_ocr/model.py create mode 100644 python/core/components/v2/qrcode_ocr/__init__.py create mode 100644 python/core/components/v2/qrcode_ocr/component.py create mode 100644 python/core/components/v2/qrcode_ocr/model.py create mode 100644 python/tests/test_v2_handwrite_ocr.py create mode 100644 python/tests/test_v2_mix_card_ocr.py create mode 100644 python/tests/test_v2_qrcode_ocr.py diff --git a/python/core/components/v2/__init__.py b/python/core/components/v2/__init__.py index 7267a4ef8..ff468bdb7 100644 --- a/python/core/components/v2/__init__.py +++ b/python/core/components/v2/__init__.py @@ -16,10 +16,16 @@ from .image_understand.component import ImageUnderstand from .llms.style_rewrite.component import StyleRewrite from .llms.hallucination_detection.component import HallucinationDetection +from .qrcode_ocr.component import QRcodeOCR +from .handwrite_ocr.component import HandwriteOCR +from .mix_card_ocr.component import MixCardOCR __V2_COMPONENTS__ = [ "AnimalRecognition", "ImageUnderstand", "StyleRewrite", - "HallucinationDetection" + "HallucinationDetection", + "QRcodeOCR", + "HandwriteOCR", + "MixCardOCR", ] # NOQA \ No newline at end of file diff --git a/python/core/components/v2/handwrite_ocr/__init__.py b/python/core/components/v2/handwrite_ocr/__init__.py new file mode 100644 index 000000000..c33303636 --- /dev/null +++ b/python/core/components/v2/handwrite_ocr/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2023 Baidu, Inc. 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. diff --git a/python/core/components/v2/handwrite_ocr/component.py b/python/core/components/v2/handwrite_ocr/component.py new file mode 100644 index 000000000..825666edc --- /dev/null +++ b/python/core/components/v2/handwrite_ocr/component.py @@ -0,0 +1,223 @@ +# 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. + +r"""手写文字识别组件""" +import base64 +from typing import Optional +from appbuilder.core._exception import AppBuilderServerException, InvalidRequestArgumentError +from appbuilder.core.component import Component +from appbuilder.core.components.v2.handwrite_ocr.model import * +from appbuilder.core.message import Message +from appbuilder.core._client import HTTPClient +from appbuilder.core import utils +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace + +class HandwriteOCR(Component): + r""" 手写文字识别组件 + + Examples: + + .. code-block:: python + + import os + import appbuilder + os.environ["GATEWAY_URL"] = "..." + os.environ["APPBUILDER_TOKEN"] = "..." + # 从BOS存储读取样例文件 + image_url="https://bj.bcebos.com/v1/appbuilder/test_handwrite_ocr.jpg?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-23T11%3A58%3A09Z%2F-1%2Fhost%2F677f93445fb65157bee11cd492ce213d5c56e7a41827e45ce7e32b083d195c8b" + # 输入参数为一张图片 + inp = appbuilder.Message(content={"url": image_url}) + # 进行植物识别 + handwrite_ocr = HandwriteOCR() + out = handwrite_ocr.run(inp) + # 打印识别结果 + print(out.content) + + """ + + name = "handwriting_ocr" + version = "v1" + manifests = [ + { + "name": "handwriting_ocr", + "description": "需要对图片中手写体文字进行识别时,使用该工具,不支持PDF文件,如果用户没有提供图片文件,应引导用户提供图片,而不是尝试使用该工具", + "parameters": { + "type": "object", + "properties": { + "file_names": { + "type": "array", + "items": { + "type": "string" + }, + "description": "待识别文件的文件名" + }, + "file_urls": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "待识别文件的url下载地址" + } + }, + "required": ["file_names"] + } + } + ] + + @HTTPClient.check_param + @components_run_trace + def run(self, message: Message, timeout: float = None, retry: int = 0) -> Message: + r""" + 输入图片并识别其中的文字 + + Args: + message (Message): 输入图片或图片url下载地址用于执行识别操作.例如: Message(content={"raw_image": b"..."}) 或 Message(content={"url": "https://image/download/url"}). + timeout (float, optional): HTTP超时时间. 默认为None. + retry (int, optional): HTTP重试次数. 默认为0. + + Returns: + Message: 手写体模型识别结果. + """ + inp = HandwriteOCRInMsg(**message.content) + request = HandwriteOCRRequest() + if inp.url: + request.url = inp.url + if inp.raw_image: + request.image = base64.b64encode(inp.raw_image) + request.recognize_granularity = "big" + request.probability = "false" + request.detect_direction = "true" + request.detect_alteration = "true" + response = self._recognize(request, timeout, retry) + out = HandwriteOCROutMsg() + out.direction = response.direction + [out.contents.append( + Content(text=w.words, + position=Position( + left=w.location.left, + top=w.location.top, + width=w.location.width, + height=w.location.height + ))) + for w in response.words_result] + return Message(content=out.model_dump()) + + @components_run_stream_trace + def tool_eval(self, + file_names: Optional[list] = [], + file_urls: Optional[dict] = {}, + **kwargs): + """ + 工具评估函数 + Args: + file_names (Optional[list]): 待识别文件的文件名列表 + file_urls (Optional[dict]): 待识别文件的url下载地址字典 + **kwargs: 其他参数 + + Raises: + InvalidRequestArgumentError: 请求格式错误,文件url不存在 + + Yields: + Generator[Output]: 生成器,每次迭代产生一个输出对象 + """ + traceid = kwargs.get("_sys_traceid", "") + result = "" + + sys_file_names = file_names + sys_file_urls = file_urls + + if not sys_file_names: + sys_file_names = kwargs.get('_sys_file_names', []) + if not sys_file_urls: + sys_file_urls = kwargs.get('_sys_file_urls', {}) + + for file_name in sys_file_names: + if utils.is_url(file_name): + file_url = file_name + else: + file_url = sys_file_urls.get(file_name, None) + if file_url is None: + raise InvalidRequestArgumentError(f"request format error, file {file_name} url does not exist") + req = HandwriteOCRRequest() + req.url = file_url + req.recognize_granularity = "big" + req.probability = "false" + req.detect_direction = "true" + req.detect_alteration = "true" + response = self._recognize(req, request_id=traceid) + text = "".join([w.words for w in response.words_result]) + result += f"{file_name}的手写识别结果是:{text} " + + llm_result = self.create_output( + type = "text", + visible_scope= "llm", + text=result, + name="llm_text" + ) + yield llm_result + + user_result = self.create_output( + type = "text", + visible_scope= "user", + text="", + name="user_text" + ) + yield user_result + + + def _recognize( + self, + request: HandwriteOCRRequest, + timeout: float = None, + retry: int = 0, + request_id: str = None, + ) -> HandwriteOCRResponse: + r"""调用底层接口进行通用文字识别 + 参数: + request (obj: `HandwriteOCRRequest`) : 通用文字识别输入参数 + + 返回: + response (obj: `HandwriteOCRResponse`): 通用文字识别返回结果 + """ + if not request.image and not request.url: + raise ValueError("request format error, one of image or url must be set") + data = request.model_dump() + if self.http_client.retry.total != retry: + self.http_client.retry.total = retry + headers = self.http_client.auth_header(request_id) + headers['content-type'] = 'application/x-www-form-urlencoded' + url = self.http_client.service_url("/v1/bce/aip/ocr/v1/handwriting") + response = self.http_client.session.post(url, headers=headers, data=data, timeout=timeout) + self.http_client.check_response_header(response) + data = response.json() + self.http_client.check_response_json(data) + request_id = self.http_client.response_request_id(response) + self.__class__._check_service_error(request_id, data) + ocr_response = HandwriteOCRResponse(**data) + ocr_response.request_id = request_id + return ocr_response + + @staticmethod + def _check_service_error(request_id: str, data: dict): + r"""个性化服务response参数检查 + 参数: + request (dict) : 通用文字识别body返回 + 返回: + 无 + """ + if "error_code" in data or "error_msg" in data: + raise AppBuilderServerException( + request_id=request_id, + service_err_code=data.get("error_code"), + service_err_message=data.get("error_msg") + ) + diff --git a/python/core/components/v2/handwrite_ocr/model.py b/python/core/components/v2/handwrite_ocr/model.py new file mode 100644 index 000000000..b1f451736 --- /dev/null +++ b/python/core/components/v2/handwrite_ocr/model.py @@ -0,0 +1,176 @@ +# Copyright (c) 2023 Baidu, Inc. 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 proto +from typing import List, Optional +from pydantic import BaseModel, Field + +class HandwriteOCRRequest(BaseModel): + """ 手写文字识别组件请求参数 + 属性: + image (str): + 可选。图像内容的base64编码。 + url (str): + 可选。图像的URL地址,经过base64编码。 + 图像大小必须小于4MB,图像的最短边长大于15像素,最长边长大于4096像素。 + pdf_file (str): + 可选。PDF文件内容的base64编码。 + pdf_file_num (str): + 可选。PDF文件的页数。 + ofd_file (str): + 可选。OFD(Open Format Document)文件内容的base64编码。 + ofd_file_num (str): + 可选。OFD文件的页数。 + recognize_granularity(str): + 可选,识别粒度: + 可能的取值包括: + - "big": 不定位单字符位置 + - "small": 定位单字符位置。 + probability (str): + 可选。是否输出置信度。默认为"false"。 + 可能的取值包括: + - "true": 返回识别结果中每行的置信度。 + - "false": 不返回置信度。 + detect_direction (str): + 可选。是否检测文本方向。默认为"false"。 + 可能的取值包括: + - "true": 检测文本方向。 + - "false": 不检测文本方向。 + detect_alteration(str): + 可选,是否检测涂改痕迹,适用于手写作文场景,默认不检测 + 可选值包括: + - "true":检测,涂改痕迹部分用“☰”返回; + - "false":不检测 + """ + image: str = Field(None, description="图像内容的base64编码") + url: str = Field(None, description="图像的URL地址,经过base64编码") + pdf_file: str = Field(None, description="PDF文件内容的base64编码") + pdf_file_num: str = Field(None, description="PDF文件的页数") + ofd_file: str = Field(None, description="OFD(Open Format Document)文件内容的base64编码") + ofd_file_num: str = Field(None, description="OFD文件的页数") + recognize_granularity: str = Field("small", description="识别粒度") + probability: str = Field("false", description="是否输出置信度") + detect_direction: str = Field("false", description="是否检测文本方向") + detect_alteration: str = Field("false", description="是否检测涂改痕迹") + +class HandwriteLocation(BaseModel): + """ 手写体位置信息. + + 属性: + left (int): 表示定位位置的长方形左上顶点的水平坐标 + top (int): 表示定位位置的长方形左上顶点的垂直坐标 + width (int): 表示定位位置的长方形的宽度 + height (int): 表示定位位置的长方形的高度 + """ + left: Optional[int] = Field(None, description="表示定位位置的长方形左上顶点的水平坐标") + top: Optional[int] = Field(None, description="表示定位位置的长方形左上顶点的垂直坐 标") + width: Optional[int] = Field(None, description="表示定位位置的长方形的宽度") + height: Optional[int] = Field(None, description="表示定位位置的长方形的高度") + + +class HandwriteWordResult(BaseModel): + """ 手写文字识别结果 + + 属性: + words (str): 识别出的文本 + location (Location): 文本位置信息 + """ + words: str = Field(None, description="识别出的文本") + location: Optional[HandwriteLocation] = Field(None, description="文本位置信息") + + +class HandwriteProbability(BaseModel): + """手写体置信度 + + 属性: + average (float): 每行的平均置信度 + variance (float): 每行置信度的方差 + min (float):每行的最小置信度 + """ + average: Optional[float] = Field(None, description="每行的平均置信度") + variance: Optional[float] = Field(None, description="每行置信度的方差") + min: Optional[float] = Field(None, description="每行的最小置信度") + + +class HandwriteOCRResponse(BaseModel): + """手写文字识别结果 + + 属性: + request_id(str): 请求ID + log_id (int): 用于问题跟踪的唯一日志ID + words_result_num (int): 必填。识别结果的数量 + words_result (List[WordResult]): 识别结果的数组 + probability(Probability):当probability=true 时返回该字段,表示识别结果中每一行的置信度值 + direction (int): 当 + detect_direction=true返回改字段,1(未定义)、 + 0(正向)、1(逆时针90度)、2(逆时针180度)、3(逆时针270度) + pdf_file_size (str): 输入PDF文件的总页数。当pdf_file参数有效时返回 + """ + request_id: str = Field(None, description="请求ID") + log_id: int = Field(None, description="用于问题跟踪的唯一日志ID") + words_result_num: int = Field(None, description="必填。识别结果的数量") + words_result: List[HandwriteWordResult] = Field(None, description="识别结果的数组") + probability: Optional[HandwriteProbability] = Field(None, description="当probability=true 时返回该字段,表示识别结果中每一行的置信度值") + direction: int = Field(None, description="当detect_direction=true返回改字段,1(未定义)、0(正向)、1(逆时针90度)、2(逆时针180度)、3(逆时针270度)") + pdf_file_size: str = Field(None, description="输入PDF文件的总页数。当pdf_file参数有效时返回") + + +class HandwriteOCRInMsg(BaseModel): + """ 手写体文字识别输入消息 + + 属性: + raw_image(bytes): 图像原始内容 + url(str): 图像下载链接 + """ + raw_image: bytes = b'' # 原始图片byte数组 + url: str = "" # 图片可下载链接 + + +class Position(BaseModel): + """位置信息 + + 属性: + left (int): 表示定位位置的长方形左上顶点的水平坐标 + top (int): 表示定位位置的长方形左上顶点的垂直坐标 + width (int): 表示定位位置的长方形的宽度 + height (int): 表示定位位置的长方形的高度 + """ + + left: int + top: int + width: int + height: int + + +class Content(BaseModel): + """ 识别文字 + + 属性: + content(str):文字内容 + position(Position): 文字内容的位置信息 + """ + text: str + position: Optional[Position] = None + + +class HandwriteOCROutMsg(BaseModel): + """ 识别文字结果列表 + + 属性: + contents(list[Content]): 手写体文字识别结果列表 + direction(int): 图像旋转角度,0(正向),- 1(逆时针90度),- 2(逆时针180度),- 3(逆时针270度) + """ + contents: List[Content] = list() + direction: int = 0 diff --git a/python/core/components/v2/mix_card_ocr/__init__.py b/python/core/components/v2/mix_card_ocr/__init__.py new file mode 100644 index 000000000..c33303636 --- /dev/null +++ b/python/core/components/v2/mix_card_ocr/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2023 Baidu, Inc. 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. diff --git a/python/core/components/v2/mix_card_ocr/component.py b/python/core/components/v2/mix_card_ocr/component.py new file mode 100644 index 000000000..0f70387c7 --- /dev/null +++ b/python/core/components/v2/mix_card_ocr/component.py @@ -0,0 +1,249 @@ +# 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. + +r"""身份证混贴识别组件""" +import base64 +import json +from typing import Optional +from appbuilder.core import utils +from appbuilder.core._client import HTTPClient +from appbuilder.core._exception import AppBuilderServerException, InvalidRequestArgumentError +from appbuilder.core.component import Component +from appbuilder.core.components.v2.mix_card_ocr.model import * +from appbuilder.core.message import Message +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace + + +class MixCardOCR(Component): + r""" 身份证混贴识别组件 + + Examples: + + .. code-block:: python + + import os + import requests + import appbuilder + + os.environ["GATEWAY_URL"] = "..." + os.environ["APPBUILDER_TOKEN"] = "..." + # 从BOS存储读取样例文件 + image_url="https://bj.bcebos.com/v1/appbuilder/test_mix_card_ocr.jpeg?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-24T06%3A18%3A11Z%2F-1%2Fhost%2F695b8041c1ded194b9e80dbe1865e4393da5a3515e90d72d81ef18296bd29598" + raw_image = requests.get(image_url).content + # 输入参数为一张图片 + inp = appbuilder.Message(content={"raw_image": raw_image}) + # 进行识别 + mix_card_ocr = MixCardOCR() + out = mix_card_ocr.run(inp) + # 打印识别结果 + print(out.content) + """ + + name = "mixcard_ocr" + version = "v1" + manifests = [ + { + "name": "mixcard_ocr", + "description": "当身份证正反面在同一张图片上,需要识别图片中身份证正反面所有字段时,使用该工具", + "parameters": { + "type": "object", + "properties": { + "file_names": { + "type": "array", + "items": { + "type": "string" + }, + "description": "待识别文件的文件名" + }, + "file_urls": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "待识别文件的下载URL" + } + }, + "required": ["file_names"] + } + } + ] + + @HTTPClient.check_param + @components_run_trace + def run(self, message: Message, timeout: float = None, retry: int = 0) -> Message: + """ + 执行身份证识别操作 + + Args: + message (Message): 包含待识别图片或图片下载URL的Message对象. + 示例: Message(content={"raw_image": b"..."}) 或 Message(content={"url": "https://image/download/url"}). + timeout (float, 可选): HTTP请求的超时时间,默认为None. + retry (int, 可选): HTTP请求的重试次数,默认为0. + + Returns: + Message: 包含身份证识别结果的Message对象. + """ + inp = MixCardOCRInMsg(**message.content) + request = MixCardOCRRequest() + if inp.url: + request.url = inp.url + if inp.raw_image: + request.image = base64.b64encode(inp.raw_image) + request.detect_risk = "false" + request.detect_quality = "false" + request.detect_photo = "false" + request.detect_card = "false" + response = self._recognize(request, timeout, retry) + out = MixCardOCROutMsg() + for res in response.words_result: + card_type = res.card_info.card_type + if card_type != "idcard_back" and card_type != "idcard_front": + continue + ref = out.front + if card_type == "idcard_back": + ref = out.back + loc = res.card_info.card_location + ref.position = MixCardPosition( + left=loc.left, top=loc.top, width=loc.width, height=loc.height) + for key, val in res.card_result.items(): + position = MixCardPosition(left=val.location.left, top=val.location.top, width=val.location.width, + height=val.location.height) + ref.fields.append(MixCardField( + key=key, value=val.words, position=position)) + out.direction = response.direction + return Message(content=out.model_dump()) + + def _recognize(self, request: MixCardOCRRequest, timeout: float = None, retry: int = 0, request_id: str = None) -> MixCardOCRResponse: + r"""调用底层身份证混贴识别 + 参数: + request (obj: `GeneralOCRRequest`) : 通用文字识别输入参数 + + 返回: + response (obj: `GeneralOCRResponse`): 通用文字识别返回结果 + """ + if not request.image and not request.url: + raise ValueError( + "request format error, one of image or url must be set") + data = request.model_dump() + if self.http_client.retry.total != retry: + self.http_client.retry.total = retry + headers = self.http_client.auth_header(request_id) + headers['content-type'] = 'application/x-www-form-urlencoded' + url = self.http_client.service_url("/v1/bce/aip/ocr/v1/multi_idcard") + response = self.http_client.session.post( + url, headers=headers, data=data, timeout=timeout) + self.http_client.check_response_header(response) + data = response.json() + self.http_client.check_response_json(data) + request_id = self.http_client.response_request_id(response) + self.__class__._check_service_error(request_id, data) + response = MixCardOCRResponse(**data) + response.request_id = request_id + return response + + @staticmethod + def _check_service_error(request_id: str, data: dict): + r"""个性化服务response参数检查 + 参数: + request (dict) : 通用文字识别body返回 + 返回: + 无 + """ + if "error_code" in data or "error_msg" in data: + raise AppBuilderServerException( + request_id=request_id, + service_err_code=data.get("error_code"), + service_err_message=data.get("error_msg") + ) + + @components_run_stream_trace + def tool_eval(self, + file_names: Optional[list] = [], + file_urls: Optional[dict] = {}, + **kwargs): + """ + 对指定文件进行OCR识别。 + + Args: + name (str): API名称。 + streaming (bool): 是否流式输出。如果为True,则逐个返回识别结果;如果为False,则一次性返回所有识别结果。 + **kwargs: 其他参数。 + + Returns: + 如果streaming为False,则返回包含所有识别结果的JSON字符串。 + 如果streaming为True,则逐个返回包含识别结果的字典,每个字典包含以下字段: + type (str): 消息类型,固定为"text"。 + text (str): 识别结果的JSON字符串。 + visible_scope (str): 消息可见范围,可以是"llm"或"user"。 + + Raises: + InvalidRequestArgumentError: 如果请求格式错误,即文件URL不存在时抛出。 + + """ + result = {} + + traceid = kwargs.get("_sys_traceid", "") + + sys_file_names = file_names + sys_file_urls = file_urls + + if not sys_file_names: + sys_file_names = kwargs.get("_sys_file_names", []) + if not sys_file_urls: + sys_file_urls = kwargs.get("_sys_file_urls", {}) + + for file_name in sys_file_names: + if utils.is_url(file_name): + file_url = file_name + else: + file_url = sys_file_urls.get(file_name, None) + if file_url is None: + raise InvalidRequestArgumentError( + f"request format error, file {file_name} url does not exist") + + request = MixCardOCRRequest() + request.url = file_url + request.detect_risk = "false" + request.detect_quality = "false" + request.detect_photo = "false" + request.detect_card = "false" + response = self._recognize(request, request_id=traceid) + out = MixCardOCROutMsg() + for res in response.words_result: + card_type = res.card_info.card_type + if card_type != "idcard_back" and card_type != "idcard_front": + continue + ref = out.front + if card_type == "idcard_back": + ref = out.back + for key, val in res.card_result.items(): + ref.fields.append(MixCardField( + key=key, value=val.words, position=None)) + out.direction = response.direction + result[file_name] = out.model_dump() + + result = json.dumps(result, ensure_ascii=False) + llm_result = self.create_output( + type="text", + visible_scope="llm", + text={"info": result}, + name="llm_text" + ) + yield llm_result + + user_result = self.create_output( + type="text", + visible_scope="user", + text="", + name="user_text" + ) + yield user_result diff --git a/python/core/components/v2/mix_card_ocr/model.py b/python/core/components/v2/mix_card_ocr/model.py new file mode 100644 index 000000000..bd770aed0 --- /dev/null +++ b/python/core/components/v2/mix_card_ocr/model.py @@ -0,0 +1,192 @@ +# Copyright (c) 2023 Baidu, Inc. 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 proto +from typing import List, Optional +from pydantic import BaseModel, Field + + +class MixCardOCRRequest(BaseModel): + """ 身份证混贴识别 + 属性: + image (str): + 可选。图像内容的base64编码 + url (str): + 可选,图像的URL地址,经过base64编码 + 图像大小必须小于4MB,图像的最短边长大于15像素,最长边长大于4096像素 + detect_risk(str): + 是否检测风险(身份证复印件、临时身份证、身份证翻拍、修改过的身份证)类型,可选值是"true"或“false” + detect_quality(str): + 是否开启身份证质量类型(边框/四角不完整、头像或关键字段被遮挡/马赛克)检测功能,可选值是"true"或“false” + detect_photo(str): + 是否检测头像内容,默认不检测,可选值是"true"或“false” + detect_card(str): + 是否检测身份证并进行裁剪,"true"或“false” + """ + image: str = Field(default="", description="图像内容的base64编码") + url: str = Field(default="", description="图像的URL地址,经过base64编码") + detect_risk: str = Field( + default="", description="是否检测风险(身份证复印件、临时身份证、身份证翻拍、修改过的身份证)类型,可选值是\"true\"或“false\"") + detect_quality: str = Field(default="", description="是否开启身份证质量类型(边框" + "四角不完整、头像或关键字段被遮挡/马赛克)检测功能,可选值是\"true\"或“false\"") + detect_photo: str = Field( + default="", description="是否检测头像内容,默认不检测,可选值是\"true\"或“false\"") + detect_card: str = Field( + default="", description="是否检测身份证并进行裁剪,\"true\"或“false\"") + + +class MixCardOCRLocation(BaseModel): + """ 位置信息. + + 属性: + left (int): 表示定位位置的长方形左上顶点的水平坐标 + top (int): 表示定位位置的长方形左上顶点的垂直坐标 + width (int): 表示定位位置的长方形的宽度 + height (int): 表示定位位置的长方形的高度 + """ + left: int = Field(default=0, description="表示定位位置的长方形左上顶点的水平坐标") + top: int = Field(default=0, description="表示定位位置的长方形左上顶点的垂直坐标") + width: int = Field(default=0, description="表示定位位置的长方形的宽度") + height: int = Field(default=0, description="表示定位位置的长方形的高度") + + +class MixCardOCRInfo(BaseModel): + """ 身份证混贴手识别结果 + + 属性: + card_location (MixCardLocation): 身份证的位置信息(坐标0点为左上角) + card_type(str): idcard_front(头像面)、idcard_back(国徽面) + image_status (str): normal-识别正常、 non_idcard-上传的图片中不包含身份证 、blurred-身份证模糊 + other_type_card-其他类型证照 、over_exposure-身份证关键字段反光或过曝 、over_dark-身份证欠曝(亮度过低) + unknown-未知状态 + """ + card_location: Optional[MixCardOCRLocation] = Field( + default=None, description="身份证的位置信息(坐标0点为左上角)") + card_type: str = Field( + default="", description="idcard_front(头像面)、idcard_back(国徽面)") + image_status: str = Field( + default="", description="normal-识别正常、 non_idcard-上传的图片中不包含身份证 、blurred-身份证模糊") + direction: int = Field(default=0, description="身份证正反面,0-正面,1-背面") + idcard_number_type: int = Field( + default=0, description="身份证号类型,0-普通身份证号码,1-港澳台身份证号码,2-护照号码") + + +class MixCardOCRResult(BaseModel): + """身份证混贴别结果 + + 属性: + words (str): 文本信息 + location(MixCardOCRLocation): 位置信息 + """ + words: str = Field(default="", description="文本信息") + location: Optional[MixCardOCRLocation] = Field( + default=None, description="位置信息") + + +class MixOCRCardInfoResult(BaseModel): + """身份证混贴信息 + + 属性: + card_result (Map[str,MixCardOCRResult]): 身份证字段信息 + card_info(MixCardOCRInfo): 身份证信息 + """ + card_result: Optional[dict[str, MixCardOCRResult]] = Field( + default=None, description="身份证字段信息") + card_info: Optional[MixCardOCRInfo] = Field( + default=None, description="身份证信息") + + +class MixCardOCRResponse(BaseModel): + """身份证混贴识别结果 + + 属性: + request_id(str): 请求ID + log_id (int): 用于问题跟踪的唯一日志ID + words_result (List[MixOCRCardInfoResult]): 识别结果列表 + direction (int): 当detect_direction=true返回改字段,1(未定义)、 + 0(正向)、1(逆时针90度)、2(逆时针180度)、3(逆时针270度) + pdf_file_size (str): 输入PDF文件的总页数。当pdf_file参数有效时返回 + """ + request_id: str = Field(default="", description="请求ID") + log_id: int = Field(default=0, description="用于问题跟踪的唯一日志ID") + words_result: Optional[list[MixOCRCardInfoResult] + ] = Field(default=None, description="识别结果列表") + direction: int = Field( + default=0, description="当detect_direction=true返回改字段,1(未定义)、0(正向)、1(逆时针90度)、2(逆时针180度)、3(逆时针270度)") + + +class MixCardOCRInMsg(BaseModel): + """ 手写体文字识别输入消息 + + 属性: + raw_image(bytes): 图像原始内容 + url(str): 图像下载链接 + """ + raw_image: bytes = b'' # 原始图片byte数组 + url: str = "" # 图片可下载链接 + + +class MixCardPosition(BaseModel): + """位置信息 + + 属性: + left (int): 表示定位位置的长方形左上顶点的水平坐标 + top (int): 表示定位位置的长方形左上顶点的垂直坐标 + width (int): 表示定位位置的长方形的宽度 + height (int): 表示定位位置的长方形的高度 + """ + + left: int + top: int + width: int + height: int + + +class MixCardField(BaseModel): + """ 字段信息 + + 属性: + key(str): 字段名 + value (str): 字段值 + position(MixCardPosition): 字段位置信息 + """ + + key: str + value: str + position: Optional[MixCardPosition] = None + + +class MixCardContent(BaseModel): + """正/反识别结果 + + 属性: + fields(List[MixCardField]):字段列表 + position(MixCardPosition): 正/反面在图像中的位置信息 + """ + fields: List[MixCardField] = list() + position: MixCardPosition = None + + +class MixCardOCROutMsg(BaseModel): + """身份证混贴识别结果 + + 属性: + front(MixCardField): 人像面信息 + back(MixCardField): 国徽面信息 + direction(int): 图像旋转角度,0(正向),- 1(逆时针90度),- 2(逆时针180度),- 3(逆时针270度) + """ + front: MixCardContent = MixCardContent() + back: MixCardContent = MixCardContent() + direction: int = 0 diff --git a/python/core/components/v2/qrcode_ocr/__init__.py b/python/core/components/v2/qrcode_ocr/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/python/core/components/v2/qrcode_ocr/component.py b/python/core/components/v2/qrcode_ocr/component.py new file mode 100644 index 000000000..aaf7d79a9 --- /dev/null +++ b/python/core/components/v2/qrcode_ocr/component.py @@ -0,0 +1,227 @@ +# Copyright (c) 2023 Baidu, Inc. 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. + +"""qrcode ocr component.""" + +import base64 +import json +from typing import Optional + +from appbuilder.core import utils +from appbuilder.core.component import Component +from appbuilder.core.components.v2.qrcode_ocr.model import * +from appbuilder.core.message import Message +from appbuilder.core._client import HTTPClient +from appbuilder.core._exception import AppBuilderServerException, InvalidRequestArgumentError +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace + + +class QRcodeOCR(Component): + r""" + 对图片中的二维码、条形码进行检测和识别,返回存储的文字信息及其位置信息。 + + + Examples: + + .. code-block:: python + + import appbuilder + # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 + os.environ["APPBUILDER_TOKEN"] = '...' + + qrcode_ocr = appbuilder.QRcodeOCR() + with open("./qrcode_ocr_test.png", "rb") as f: + out = self.component.run(appbuilder.Message(content={"raw_image": f.read(),"location": "true"})) + print(out.content) + + """ + + name = "qrcode_ocr" + version = "v1" + manifests = [ + { + "name": "qrcode_ocr", + "description": "需要对图片中的二维码、条形码进行检测和识别,返回存储的文字信息及其位置信息,使用该工具", + "parameters": { + "type": "object", + "properties": { + "file_names": { + "type": "array", + "items": { + "type": "string" + }, + "description": "待识别文件的文件名" + }, + "location": { + "type": "string", + "description": "是否输出二维码/条形码位置信息" + }, + "file_urls": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "待识别文件的URL下载地址" + } + }, + "required": ["file_names"] + } + } + ] + + @HTTPClient.check_param + @components_run_trace + def run(self, message: Message, location: str = "true", timeout: float = None, retry: int = 0) -> Message: + """ + 执行二维码识别操作。 + + Args: + message (Message): 输入的图片或图片URL下载地址,用于执行识别操作。例如: + Message(content={"raw_image": b"...", "location": ""}) 或 + Message(content={"url": "https://image/download/url"})。 + location (str, 可选): 是否需要返回二维码位置信息,默认为 "true"。 + timeout (float, 可选): HTTP请求的超时时间。 + retry (int, 可选): HTTP请求的重试次数。 + + Returns: + Message: 识别结果,包含识别到的二维码信息。例如: + Message(name=msg, content={'codes_result': [{'type': 'QR_CODE', 'text': ['http://weixin.qq.com/r/cS7M1PHE5qyZrbW393tj'], + 'location': {'top': 63, 'left': 950, 'width': 220, 'height': 211}}, ...]}, mtype=dict) + + Raises: + InvalidRequestArgumentError: 如果 location 参数非法,将抛出该异常。 + """ + inp = QRcodeInMsg(**message.content) + req = QRcodeRequest() + if inp.raw_image: + req.image = base64.b64encode(inp.raw_image) + if inp.url: + req.url = inp.url + if not isinstance(location, str) or location not in ('true', 'false'): + raise InvalidRequestArgumentError( + f"illegal location, expected location is 'true' or 'false', got {location}") + req.location = location + result = self._recognize(req, timeout, retry) + out = QRcodeOutMsg(**result.model_dump()) + return Message(content=out.model_dump()) + + def _recognize(self, request: QRcodeRequest, timeout: float = None, + retry: int = 0, request_id: str = None) -> QRcodeResponse: + r"""调用二维码识别底层能力 + 参数: + request (obj: `QRcodeRequest`) : 二维码识别输入参数 + 返回: + response (obj: `QRcodeResponse`): 二维码识别返回结果 + """ + if not request.image and not request.url: + raise ValueError( + "request format error, one of image or url must be set") + + data = QRcodeRequest.model_dump(request) + if self.http_client.retry.total != retry: + self.http_client.retry.total = retry + headers = self.http_client.auth_header(request_id) + headers['content-type'] = 'application/x-www-form-urlencoded' + headers['Accept'] = 'application/json' + url = self.http_client.service_url("/v1/bce/aip/ocr/v1/qrcode") + response = self.http_client.session.post( + url, headers=headers, data=data, timeout=timeout) + + self.http_client.check_response_header(response) + data = response.json() + self.http_client.check_response_json(data) + request_id = self.http_client.response_request_id(response) + self.__class__._check_service_error(request_id, data) + res = QRcodeResponse(**data) + + res.request_id = request_id + return res + + @staticmethod + def _check_service_error(request_id: str, data: dict): + r"""个性化服务response参数检查 + 参数: + request (dict) : 二维码识别body返回 + 返回: + 无 + """ + if "error_code" in data or "error_msg" in data: + raise AppBuilderServerException( + request_id=request_id, + service_err_code=data.get("error_code"), + service_err_message=data.get("error_msg") + ) + + @components_run_stream_trace + def tool_eval(self, file_names:Optional[list]=[], location: Optional[str]="false", file_urls:Optional[dict]={}, **kwargs): + """ + ToolEval方法,用于执行二维码识别操作。 + + Args: + file_names (list, 可选): 待识别文件的文件名列表。 + location (str, 可选): 是否需要返回二维码位置信息,默认为 "false"。 + file_urls (dict, 可选): 待识别文件的URL下载地址字典,格式为 {"filename": "url"}。 + + Yields: + ComponentOutput: 识别结果,包含识别到的二维码信息。 + """ + result = {} + traceid = kwargs.get("_sys_traceid", "") + # file_name + sys_file_names = file_names + sys_file_urls = file_urls + + if not sys_file_names: + sys_file_names = kwargs.get("_sys_file_names", []) + if not sys_file_urls: + sys_file_urls = kwargs.get("_sys_file_urls", {}) + + + for file_name in sys_file_names: + if utils.is_url(file_name): + file_url = file_name + else: + file_url = sys_file_urls.get(file_name, None) + if file_url is None: + raise InvalidRequestArgumentError( + f"request format error, file {file_name} url does not exist") + req = QRcodeRequest() + req.url = file_url + if not isinstance(location, str) or location not in ("true", "false"): + raise InvalidRequestArgumentError( + f"illegal location, expected location is 'true' or 'false', got {location}" + ) + req.location = location + resp = self._recognize(req, request_id=traceid) + result[file_name] = [ + item["text"] for item in resp.model_dump().get("codes_result", []) + ] + + result = json.dumps(result, ensure_ascii=False) + + llm_result = self.create_output( + type="text", + visible_scope="llm", + text={"info": result}, + name="llm_text" + ) + yield llm_result + + user_result = self.create_output( + type="text", + visible_scope="user", + text={"info": ""}, + name="user_text" + ) + yield user_result diff --git a/python/core/components/v2/qrcode_ocr/model.py b/python/core/components/v2/qrcode_ocr/model.py new file mode 100644 index 000000000..579d4814f --- /dev/null +++ b/python/core/components/v2/qrcode_ocr/model.py @@ -0,0 +1,130 @@ +# Copyright (c) 2023 Baidu, Inc. 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. + +"""qrcode ocr model.""" +import proto + +from typing import List, Optional +from pydantic import BaseModel, Field + + +class QRcodeRequest(BaseModel): + """二维码识别请求体参数. + + Attributes: + image (str): 可选。图像内容的base64编码。 + url (str): 可选。图像的URL地址,经过base64编码。 + 图像大小必须小于4MB,图像的最短边长大于15像素,最长边长大于4096像素。 + location (str): 可选。是否输出二维 + - false:默认值,不返回位置信息; + - true:返回图中二维码/条形码的位置信息,包括上边距、左边距、宽度、高度 + 必须设置image或url属性之一,如果两者都设置了,将使用image属性。 + """ + image: str = Field(default="", description="可选。图像内容的base64编码") + url: str = Field( + default="", description= + "可选。图像的URL地址,经过base64编码,图像大小必须小于4MB,\n" + "图像的最短边长大于15像素,最长边长大于4096像素 ") + location: str = Field(default="false", description="可选。是否输出二维码/条形码位置信息") + +class QRcodeLocation(BaseModel): + """条形码/二维码位置信息。 + + 属性: + top (int): 条形码/二维码的上边距。 + left (int): 条形码/二维码的左边距。 + width (int): 条形码/二维码的宽度。 + height (int): 条形码/二维码的高度。 + """ + top: int = Field(default=0, description="条形码/二维码的上边距") + left: int = Field(default=0, description="条形码/二维码的左边距") + width: int = Field(default=0, description="条形码/二维码的宽度") + height: int = Field(default=0, description="条形码/二维码的高度") + +class QRcodeRes(BaseModel): + """二维码识别结果。 + + 属性: + type (str): + 识别码类型条码类型包括:9种条形码(UPC_A、UPC_E、EAN_13、EAN_8、CODE_39、CODE_93、CODE_128、ITF、CODABAR), + 4种二维码(QR_CODE、DATA_MATRIX、AZTEC、PDF_417)。 + text (list[str]): + 条形码/二维码识别内容,目前仅支持输出中英文结果。 + location (QRcodeLocation): + 条形码/二维码位置信息,包括上边距、左边距、宽度、高度,当请求参数 location = true 时返回 + """ + type: str = Field(default="", description="识别码类型条码类型包括:9种条形码(UPC_A、UPC_E、EAN_13\n" + "、EAN_8、CODE_39、CODE_93、CODE_128、ITF、CODABAR),\n" + "4种二维码(QR_CODE、DATA_MATRIX、AZTEC、PDF_417)。") + text: list[str] = Field(default=[], description="条形码/二维码识别内容,目前仅支持输出中英文结果") + location: Optional[QRcodeLocation] = Field(default=None, description="条形码/二维码位置信息,包括上边距、左边距、宽度、高度,当请求参数 location = true 时返回") + + + +class QRcodeResponse(BaseModel): + """二维码识别响应消息。 + Attributes: + request_id (str): 请求ID。 + log_id (int): 用于问题识别的唯一日志ID。 + codes_result_num (int):识别结果数,表示codes_result的元素个数 + result (List[QRcodeRes]): 定位和识别结果数组。 + """ + request_id: str = Field(default="", description="请求ID") + log_id: int = Field(default=0, description="用于问题识别的唯一日志ID") + codes_result_num: int = Field(default=0, description="识别结果数,表示codes_result的元素个数") + codes_result: Optional[list[QRcodeRes]] = Field(default=[], description="定位和识别结果数组") + + +class QRcodeInMsg(BaseModel): + """ 二维码识别输入消息 + + 属性: + raw_image(bytes): 图像原始内容 + url(str): 图像下载链接 + """ + raw_image: bytes = Field(default=b"", description="图像原始内容") + url: str = Field(default="", description="图像下载链接") + + +class QRcodeOCRLocation(BaseModel): + """ 条形码/二维码位置信息 + + 属性: + top(int): 条形码/二维码的上边距 + left(int): 条形码/二维码的左边距 + width(int): 条形码/二维码的宽度 + height(int): 条形码/二维码的高度 + """ + top: int = Field(default=0, description="条形码/二维码的上边距") + left: int = Field(default=0, description="条形码/二维码的左边距") + width: int = Field(default=0, description="条形码/二维码的宽度") + height: int = Field(default=0, description="条形码/二维码的高度") + + +class QRcodeOCRRes(BaseModel): + """ 条形码/二维码位置信息 + + 属性: + type(int): 识别码类型条码类型 + text(List[str]): 条形码/二维码识别内容 + location(Object): 条形码/二维码位置信息 + """ + type: str = Field(default="", description="识别码类型条码类型") + text: List[str] = Field(default=[], description="条形码/二维码识别内容") + location: QRcodeOCRLocation = Field(default=QRcodeOCRLocation(), description="条形码/二维码位置信息") + + +class QRcodeOutMsg(BaseModel): + r"""识别结果列表""" + codes_result: List[QRcodeOCRRes] = Field(default=[], description="识别结果列表") diff --git a/python/tests/component_tool_eval_cases.py b/python/tests/component_tool_eval_cases.py index dc3fbed5b..b23ace2f2 100644 --- a/python/tests/component_tool_eval_cases.py +++ b/python/tests/component_tool_eval_cases.py @@ -98,6 +98,22 @@ def inputs(self): def schemas(self): return [text_schema] +class QRcodeOCRCase(Case): + def inputs(self): + image_url = "https://bj.bcebos.com/v1/appbuilder/qrcode_ocr_test.png?" \ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-" \ + "01-24T12%3A45%3A13Z%2F-1%2Fhost%2Ffc43d07b41903aeeb5a023131ba6" \ + "e74ab057ce26d50e966dc31ff083e6a9c41b" + return { + "file_names": ["text"], + "file_urls": {"text": image_url} + } + + def schemas(self): + return [text_schema] + + def outputs(self): + return {"text": ["ocr文字识别"]} class HallucinationDetectionCase(Case): def inputs(self): @@ -127,6 +143,40 @@ def schemas(self): def outputs(self): return {"text": ["存在幻觉"]} +class HandWriteOCRCase(Case): + def inputs(self): + image_url = "https://bj.bcebos.com/v1/appbuilder/test_handw"\ + "rite_ocr.jpg?authorization=bce-auth-v1%2FALTAKGa8"\ + "m4qCUasgoljdEDAzLm%2F2024-01-23T11%3A58%3A09Z%2F-1%2Fhost%2"\ + "F677f93445fb65157bee11cd492ce213d5c56e7a41827e45ce7e32b083d195c8b" + return { + "file_names": ["text"], + "file_urls": {"text": image_url} + } + + def schemas(self): + return [text_schema] + + def outputs(self): + return {"text": ["银杏树"]} + +class MixCardOCRCase(Case): + def inputs(self): + image_url=("https://bj.bcebos.com/v1/appbuilder/test_mix_card_ocr.jpeg?" + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-24T06" + "%3A18%3A11Z%2F-1%2Fhost%2F695b8041c1ded194b9e80dbe" + "1865e4393da5a3515e90d72d81ef18296bd29598") + return { + "file_names": ["test"], + "file_urls": {"test": image_url} + } + + def schemas(self): + return [text_schema] + + def outputs(self): + return {"text": ["北京市公安局"]} + component_tool_eval_cases = { "AnimalRecognition": AnimalRecognitionCase, @@ -134,5 +184,8 @@ def outputs(self): "ASR": ASRCase, "TreeMind": TreeMindCase, "StyleRewrite": StyleRewriteCase, - "HallucinationDetection": HallucinationDetectionCase + "HallucinationDetection": HallucinationDetectionCase, + "QRcodeOCR": QRcodeOCRCase, + "HandwriteOCR": HandWriteOCRCase, + "MixCardOCR": MixCardOCRCase, } \ No newline at end of file diff --git a/python/tests/test_v2_handwrite_ocr.py b/python/tests/test_v2_handwrite_ocr.py new file mode 100644 index 000000000..dfc8a8396 --- /dev/null +++ b/python/tests/test_v2_handwrite_ocr.py @@ -0,0 +1,93 @@ +# Copyright (c) 2023 Baidu, Inc. 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 os +import unittest + +import requests + +import appbuilder +from appbuilder.core.message import Message +from appbuilder.core._exception import InvalidRequestArgumentError +from appbuilder.core.components.v2 import HandwriteOCR + + +class TestHandWriteOCR(unittest.TestCase): + + def setUp(self): + """ + 设置环境变量 + Args: + None. + Returns: + None. + """ + # 从BOS存储读取样例文件 + self.image_url=("https://bj.bcebos.com/v1/appbuilder/test_handw" + "rite_ocr.jpg?authorization=bce-auth-v1%2FALTAKGa8" + "m4qCUasgoljdEDAzLm%2F2024-01-23T11%3A58%3A09Z%2F-1%2Fhost%2" + "F677f93445fb65157bee11cd492ce213d5c56e7a41827e45ce7e32b083d195c8b") + self.raw_image = requests.get(self.image_url).content + self.handwrite_ocr = HandwriteOCR() + + + def test_run_with_image_url(self): + """ + 使用图片url进行单测 + + Args: + None + + Returns: + None + + """ + # Create message with raw_image + inp = Message(content={"url": self.image_url}) + msg = self.handwrite_ocr.run(inp) + self.assertIsNotNone(msg.content) + + def test_run_with_raw_image(self): + """ + 使用原始图片进行单测 + + Args: + None + + Returns: + None + + """ + # Create message with raw_image + inp = Message(content={"raw_image": self.raw_image}) + msg = self.handwrite_ocr.run(inp) + self.assertIsNotNone(msg.content) + + def test_tool_eval(self): + result=self.handwrite_ocr.tool_eval(file_names=['test']) + with self.assertRaises(InvalidRequestArgumentError): + next(result) + result=self.handwrite_ocr.tool_eval( + file_names=['test'], + file_urls={'test':self.image_url} + ) + res=next(result) + print(res) + self.assertEqual(res.content[-1].visible_scope,'llm') + res=next(result) + self.assertEqual(res.content[-1].visible_scope,'user') + + +if __name__ == '__main__': + unittest.main() diff --git a/python/tests/test_v2_mix_card_ocr.py b/python/tests/test_v2_mix_card_ocr.py new file mode 100644 index 000000000..361014136 --- /dev/null +++ b/python/tests/test_v2_mix_card_ocr.py @@ -0,0 +1,104 @@ +# Copyright (c) 2023 Baidu, Inc. 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 os +import unittest + +import requests + +import appbuilder +from appbuilder.core.message import Message +from appbuilder.core._exception import InvalidRequestArgumentError +from appbuilder.core.components.v2.mix_card_ocr.model import MixCardOCRRequest +from appbuilder.core.components.v2.mix_card_ocr.component import MixCardOCR + + +class TestMixCardOCR(unittest.TestCase): + + def setUp(self): + """ + 设置环境变量 + Args: + None. + Returns: + None. + """ + # 从BOS存储读取样例文件 + self.image_url=("https://bj.bcebos.com/v1/appbuilder/test_mix_card_ocr.jpeg?" + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-24T06" + "%3A18%3A11Z%2F-1%2Fhost%2F695b8041c1ded194b9e80dbe" + "1865e4393da5a3515e90d72d81ef18296bd29598") + + self.raw_image = requests.get(self.image_url).content + self.mix_card_ocr = MixCardOCR() + + # 输入参数为一张图片 + + def test_run_with_image_url(self): + """ + 使用图片url进行单测 + + Args: + None + + Returns: + None + + """ + # Create message with raw_image + inp = Message(content={"url": self.image_url}) + msg = self.mix_card_ocr.run(inp) + self.assertIsNotNone(msg.content) + + def test_run_with_raw_image(self): + """ + 使用原始图片进行单测 + + Args: + None + + Returns: + None + + """ + # Create message with raw_image + inp = Message(content={"raw_image": self.raw_image}) + msg = self.mix_card_ocr.run(inp) + self.assertIsNotNone(msg.content) + + def test_tool_eval(self): + result=self.mix_card_ocr.tool_eval(file_names=['test']) + with self.assertRaises(InvalidRequestArgumentError): + next(result) + result=self.mix_card_ocr.tool_eval( + name='name', + streaming=True, + file_names=['test'], + file_urls={'test':self.image_url} + ) + res=next(result) + print("res: {}".format(res)) + self.assertEqual(res.content[-1].visible_scope,'llm') + res=next(result) + self.assertEqual(res.content[-1].visible_scope,'user') + + def test_recognize_raise(self): + mco=appbuilder.MixCardOCR() + with self.assertRaises(ValueError): + mcor=MixCardOCRRequest() + mco._recognize(request=mcor) + + +if __name__ == '__main__': + unittest.main() diff --git a/python/tests/test_v2_qrcode_ocr.py b/python/tests/test_v2_qrcode_ocr.py new file mode 100644 index 000000000..ae8198d94 --- /dev/null +++ b/python/tests/test_v2_qrcode_ocr.py @@ -0,0 +1,160 @@ +# Copyright (c) 2023 Baidu, Inc. 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 unittest +import os +import requests +import unittest +import appbuilder + +from appbuilder.core._exception import InvalidRequestArgumentError +from appbuilder.core.components.v2 import QRcodeOCR + +class TestQRcodeOCR(unittest.TestCase): + def setUp(self): + """ + 设置环境变量。 + """ + self.qrcode_ocr = QRcodeOCR() + + def test_run_with_raw_image(self): + """ + 使用原始图片进行单测 + + Args: + None + + Returns: + None + + """ + image_url = "https://bj.bcebos.com/v1/appbuilder/qrcode_ocr_test.png?" \ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-" \ + "01-24T12%3A45%3A13Z%2F-1%2Fhost%2Ffc43d07b41903aeeb5a023131ba6" \ + "e74ab057ce26d50e966dc31ff083e6a9c41b" + raw_image = requests.get(image_url).content + # Create message with raw_image + message = appbuilder.Message(content={"raw_image": raw_image}) + # Qrcode ocr + output = self.qrcode_ocr.run(message) + # Assert output is not None + print(output) + self.assertIsNotNone(output) + + def test_run_with_url(self): + """ + 使用图片 URL 进行单测 + + Args: + None + + Returns: + None + + """ + image_url = "https://bj.bcebos.com/v1/appbuilder/qrcode_ocr_test.png?" \ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-" \ + "01-24T12%3A45%3A13Z%2F-1%2Fhost%2Ffc43d07b41903aeeb5a023131ba6" \ + "e74ab057ce26d50e966dc31ff083e6a9c41b" + # Create message with image URL + message = appbuilder.Message(content={"url": image_url}) + # Qrcode ocr + output = self.qrcode_ocr.run(message) + # Assert output is not None + self.assertIsNotNone(output) + + def test_run_with_args(self): + """ + 测试run方法,location、timeout、retry参数 + + Args: + None + + Returns: + None + + """ + image_url = "https://bj.bcebos.com/v1/appbuilder/qrcode_ocr_test.png?" \ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-" \ + "01-24T12%3A45%3A13Z%2F-1%2Fhost%2Ffc43d07b41903aeeb5a023131ba6" \ + "e74ab057ce26d50e966dc31ff083e6a9c41b" + raw_image = requests.get(image_url).content + # Create message with raw_image + message = appbuilder.Message(content={"raw_image": raw_image}) + # Qrcode ocr with timeout and retry parameters + output = self.qrcode_ocr.run(message, location="true", timeout=5.0, retry=3) + + # Assert output is not None + self.assertIsNotNone(output) + + def test_run_with_invalid_input(self): + """ + 测试run函数在传入无效输入的情况下的行为。 + + Args: + None + + Returns: + None + + """ + # create empty message + message = appbuilder.Message(content={}) + # Assert ValueError is raised + with self.assertRaises(ValueError): + self.qrcode_ocr.run(message) + + def test_run_with_invalid_url(self): + """ + 测试run函数在传入无效URL的情况下的行为。 + + Args: + None + + Returns: + None + + """ + url = "http://example.com/invalid_url.jpg" + message = appbuilder.Message(content={"url": url}) + with self.assertRaises(appbuilder.AppBuilderServerException): + self.qrcode_ocr.run(message) + + with self.assertRaises(InvalidRequestArgumentError): + self.qrcode_ocr.run(message=message,location='test') + + + def test_tool_eval(self): + image_url = "https://bj.bcebos.com/v1/appbuilder/qrcode_ocr_test.png?" \ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-" \ + "01-24T12%3A45%3A13Z%2F-1%2Fhost%2Ffc43d07b41903aeeb5a023131ba6" \ + "e74ab057ce26d50e966dc31ff083e6a9c41b" + result=self.qrcode_ocr.tool_eval(file_names=['test']) + with self.assertRaises(InvalidRequestArgumentError): + msg = next(result) + print(msg) + result=self.qrcode_ocr.tool_eval( + file_names=['test'], + file_urls={'test':image_url} + ) + res=next(result) + print(res) + self.assertEqual(res.content[-1].visible_scope,'llm') + res=next(result) + print(res) + self.assertEqual(res.content[-1].visible_scope,'user') + + + +if __name__ == '__main__': + unittest.main() From ca8f1ad814805a5bde351b8a746610ad740bfebc Mon Sep 17 00:00:00 2001 From: C9luster <138663536+C9luster@users.noreply.github.com> Date: Wed, 4 Dec 2024 13:28:50 +0800 Subject: [PATCH 52/85] Update components (#637) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * SDK 组件V2版本更新[12-6] * 增加__init__函数 * GeneralOCR组件接口更改,以及V2版本更新 * GeneralOCR组件接口更改,以及V2版本更新[单测用例更新] * 补充单测 * 更新注释 --------- Co-authored-by: yinjiaqi --- .../core/components/general_ocr/component.py | 18 +- python/core/components/v2/__init__.py | 4 + .../components/v2/general_ocr/__init__.py | 13 ++ .../components/v2/general_ocr/component.py | 208 ++++++++++++++++++ .../core/components/v2/translate/__init__.py | 15 ++ .../core/components/v2/translate/component.py | 191 ++++++++++++++++ python/tests/component_tool_eval_cases.py | 31 +++ python/tests/test_v2_general_ocr.py | 58 +++++ python/tests/test_v2_translate.py | 37 ++++ 9 files changed, 567 insertions(+), 8 deletions(-) create mode 100644 python/core/components/v2/general_ocr/__init__.py create mode 100644 python/core/components/v2/general_ocr/component.py create mode 100644 python/core/components/v2/translate/__init__.py create mode 100644 python/core/components/v2/translate/component.py create mode 100644 python/tests/test_v2_general_ocr.py create mode 100644 python/tests/test_v2_translate.py diff --git a/python/core/components/general_ocr/component.py b/python/core/components/general_ocr/component.py index 598ffe54b..7c2af9ff7 100644 --- a/python/core/components/general_ocr/component.py +++ b/python/core/components/general_ocr/component.py @@ -84,17 +84,18 @@ class GeneralOCR(Component): @HTTPClient.check_param @components_run_trace - def run(self, message: Message, timeout: float = None, retry: int = 0) -> Message: + def run(self, message: Message, timeout: float = None, retry: int = 0, language_type: str = 'CHN_ENG') -> Message: """ - 执行图片中的文字识别。 + 在通用文字识别的基础上,提供更高精度的识别服务,支持更多语种识别 Args: - message (Message): 输入图片或图片url下载地址用于执行识别操作。举例: Message(content={"raw_image": b"..."}) 或 Message(content={"url": "https://image/download/url"})。 - timeout (float, 可选): HTTP超时时间。 - retry (int, 可选): HTTP重试次数。 + message (Message): 包含识别任务的输入信息的消息对象。 + timeout (float, optional): 超时时间,单位秒。默认为None,表示不设置超时。 + retry (int, optional): 重试次数。默认为0,表示不重试。 + language_type (str, optional): 识别语言类型,可选值为'CHN_ENG'(中英文)和'CHN'(中文)。默认为'CHN_ENG'。 Returns: - Message: 模型识别结果。举例: Message(content={"words_result":[{"words":"100"}, {"words":"G8"}]})。 + Message: 包含识别结果的消息对象。 """ inp = GeneralOCRInMsg(**message.content) @@ -104,7 +105,7 @@ def run(self, message: Message, timeout: float = None, retry: int = 0) -> Messag if inp.url: request.url = inp.url request.detect_direction = "true" - request.language_type = "auto_detect" + request.language_type = language_type result = self._recognize(request, timeout, retry) result_dict = proto.Message.to_dict(result) out = GeneralOCROutMsg(**result_dict) @@ -183,6 +184,7 @@ def tool_eval(self, name: str, streaming: bool, **kwargs): """ traceid = kwargs.get("traceid") img_url = kwargs.get("img_url", None) + language_type = kwargs.get("language_type", 'CHN_ENG') if not img_url: file_urls = kwargs.get("file_urls", {}) img_path = kwargs.get("img_name", None) @@ -196,7 +198,7 @@ def tool_eval(self, name: str, streaming: bool, **kwargs): f"request format error, file {img_name} url does not exist") req = GeneralOCRRequest(url=img_url) req.detect_direction = "true" - req.language_type = "auto_detect" + req.language_type = language_type result = proto.Message.to_dict(self._recognize(req, request_id=traceid)) results = { "识别结果": " \n".join(item["words"] for item in result["words_result"]) diff --git a/python/core/components/v2/__init__.py b/python/core/components/v2/__init__.py index ff468bdb7..1d559d86f 100644 --- a/python/core/components/v2/__init__.py +++ b/python/core/components/v2/__init__.py @@ -14,6 +14,8 @@ from .animal_recognize.component import AnimalRecognition from .image_understand.component import ImageUnderstand +from .translate.component import Translation +from .general_ocr.component import GeneralOCR from .llms.style_rewrite.component import StyleRewrite from .llms.hallucination_detection.component import HallucinationDetection from .qrcode_ocr.component import QRcodeOCR @@ -23,6 +25,8 @@ __V2_COMPONENTS__ = [ "AnimalRecognition", "ImageUnderstand", + "Translation", + "GeneralOCR", "StyleRewrite", "HallucinationDetection", "QRcodeOCR", diff --git a/python/core/components/v2/general_ocr/__init__.py b/python/core/components/v2/general_ocr/__init__.py new file mode 100644 index 000000000..ba760dad3 --- /dev/null +++ b/python/core/components/v2/general_ocr/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2024 Baidu, Inc. 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. diff --git a/python/core/components/v2/general_ocr/component.py b/python/core/components/v2/general_ocr/component.py new file mode 100644 index 000000000..cb43a7d40 --- /dev/null +++ b/python/core/components/v2/general_ocr/component.py @@ -0,0 +1,208 @@ +# 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. + +r"""general ocr component.""" +import base64 +import json +import os.path + + +from appbuilder.core._client import HTTPClient +from appbuilder.core._exception import AppBuilderServerException, InvalidRequestArgumentError +from appbuilder.core.component import Component +from appbuilder.core.components.general_ocr.model import * +from appbuilder.core.message import Message +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace + + +class GeneralOCR(Component): + r""" + 提供通用文字识别能力,在通用文字识别的基础上,提供更高精度的识别服务,支持更多语种识别(丹麦语、荷兰语、马来语、 + 瑞典语、印尼语、波兰语、罗马尼亚语、土耳其语、希腊语、匈牙利语、泰语、越语、阿拉伯语、印地语及部分中国少数民族语言), + 并将字库从1w+扩展到2w+,能识别所有常用字和大部分生僻字。 + + Examples: + + .. code-block:: python + + import appbuilder + + # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 + os.environ["APPBUILDER_TOKEN"] = '...' + + general_ocr = appbuilder.GeneralOCR() + with open("./general_ocr_test.png", "rb") as f: + out = general_ocr.run(appbuilder.Message(content={"raw_image": f.read()})) + print(out.content) + + """ + name = "general_ocr" + version = "v1" + manifests = [ + { + "name": "general_ocr", + "description": "提供更高精度的通用文字识别能力,能够识别图片中的文字,不支持html后缀文件的输入", + "parameters": { + "type": "object", + "properties": { + "img_url": { + "type": "string", + "description": "待识别图片的url,根据该url能够获取图片" + }, + "img_name": { + "type": "string", + "description": "待识别图片的文件名,用于生成图片url" + }, + }, + "anyOf": [ + { + "required": [ + "img_url" + ] + }, + { + "required": [ + "img_name" + ] + } + ] + } + } + ] + + @HTTPClient.check_param + @components_run_trace + def run(self, message: Message, timeout: float = None, retry: int = 0, language_type: str = 'CHN_ENG') -> Message: + """ + 在通用文字识别的基础上,提供更高精度的识别服务,支持更多语种识别 + + Args: + message (Message): 包含识别任务的输入信息的消息对象。 + timeout (float, optional): 超时时间,单位秒。默认为None,表示不设置超时。 + retry (int, optional): 重试次数。默认为0,表示不重试。 + language_type (str, optional): 识别语言类型,可选值为'CHN_ENG'(中英文)和'CHN'(中文)。默认为'CHN_ENG'。 + + Returns: + Message: 包含识别结果的消息对象。 + + """ + inp = GeneralOCRInMsg(**message.content) + request = GeneralOCRRequest() + if inp.raw_image: + request.image = base64.b64encode(inp.raw_image) + if inp.url: + request.url = inp.url + request.detect_direction = "true" + request.language_type = language_type + result, _ = self._recognize(request, timeout, retry) + result_dict = proto.Message.to_dict(result) + out = GeneralOCROutMsg(**result_dict) + return Message(content=out.model_dump()) + + def _recognize( + self, + request: GeneralOCRRequest, + timeout: float = None, + retry: int = 0, + request_id: str = None, + ) -> GeneralOCRResponse: + r"""调用底层接口进行通用文字识别 + 参数: + request (obj: `GeneralOCRRequest`) : 通用文字识别输入参数 + + 返回: + response (obj: `GeneralOCRResponse`): 通用文字识别返回结果 + """ + if not request.image and not request.url and not request.pdf_file and not request.ofd_file: + raise ValueError( + "request format error, one of image or url or must pdf_file or ofd_file be set") + data = GeneralOCRRequest.to_dict(request) + if self.http_client.retry.total != retry: + self.http_client.retry.total = retry + headers = self.http_client.auth_header(request_id) + headers['content-type'] = 'application/x-www-form-urlencoded' + url = self.http_client.service_url("/v1/bce/aip/ocr/v1/accurate_basic") + response = self.http_client.session.post( + url, headers=headers, data=data, timeout=timeout) + self.http_client.check_response_header(response) + data = response.json() + self.http_client.check_response_json(data) + request_id = self.http_client.response_request_id(response) + self.__class__._check_service_error(request_id, data) + ocr_response = GeneralOCRResponse.from_json(payload=json.dumps(data)) + ocr_response.request_id = request_id + return ocr_response, data + + @staticmethod + def _check_service_error(request_id: str, data: dict): + r"""个性化服务response参数检查 + 参数: + request (dict) : 通用文字识别body返回 + 返回: + 无 + """ + if "error_code" in data or "error_msg" in data: + raise AppBuilderServerException( + request_id=request_id, + service_err_code=data.get("error_code"), + service_err_message=data.get("error_msg") + ) + + @components_run_stream_trace + def tool_eval( + self, + img_name: str, + img_url: str, + **kwargs + ): + """ + 对图片中的文字进行识别并返回结果。 + + Args: + img_name (str): 图片的文件名。 + img_url (str): 图片的URL地址。 + **kwargs: 其他参数,目前支持以下参数: + _sys_traceid (str): 系统追踪ID,用于跟踪请求。 + language_type (str): 语言类型,默认为'CHN_ENG'(中英文混合)。 + _sys_file_urls (dict): 文件URL字典,key为文件名,value为文件URL。 + + Returns: + Generator: 生成器,每次生成一个包含识别结果的Output对象。 + + Raises: + InvalidRequestArgumentError: 如果请求格式错误或文件URL不存在,将抛出此异常。 + + """ + traceid = kwargs.get("_sys_traceid") + language_type = kwargs.get("language_type", 'CHN_ENG') + if not img_url: + file_urls = kwargs.get("_sys_file_urls", {}) + img_path = img_name + if not img_path: + raise InvalidRequestArgumentError( + "request format error, file name is not set") + img_name = os.path.basename(img_path) + img_url = file_urls.get(img_name, None) + if not img_url: + raise InvalidRequestArgumentError( + f"request format error, file {img_name} url does not exist") + req = GeneralOCRRequest(url=img_url) + req.detect_direction = "true" + req.language_type = language_type + result_response, raw_data = self._recognize(req, request_id=traceid) + result = proto.Message.to_dict(result_response) + results = { + "识别结果": " \n".join(item["words"] for item in result["words_result"]) + } + res = json.dumps(results, ensure_ascii=False, indent=4) + yield self.create_output(type="text", text=res, raw_data=raw_data, visible_scope="llm") + yield self.create_output(type="text", text="", raw_data=raw_data, visible_scope="user") diff --git a/python/core/components/v2/translate/__init__.py b/python/core/components/v2/translate/__init__.py new file mode 100644 index 000000000..fc3a1a2ea --- /dev/null +++ b/python/core/components/v2/translate/__init__.py @@ -0,0 +1,15 @@ +# Copyright (c) 2023 Baidu, Inc. 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. + + diff --git a/python/core/components/v2/translate/component.py b/python/core/components/v2/translate/component.py new file mode 100644 index 000000000..cb55bec75 --- /dev/null +++ b/python/core/components/v2/translate/component.py @@ -0,0 +1,191 @@ +# Copyright (c) 2024 Baidu, Inc. 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. + + +# -*- coding: utf-8 -*- +""" +文本翻译-通用版组件 +""" +import json + +from appbuilder.core.message import Message +from appbuilder.core.component import Component +from appbuilder.core._client import HTTPClient +from appbuilder.core._exception import AppBuilderServerException, InvalidRequestArgumentError +from appbuilder.core.components.translate.model import * +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace + + +class Translation(Component): + r""" + 文本翻译组件,可支持中、英、日、韩等200+语言互译,100+语种自动检测。 + 支持语种列表可参照 https://ai.baidu.com/ai-doc/MT/4kqryjku9#%E8%AF%AD%E7%A7%8D%E5%88%97%E8%A1%A8 + + + Examples: + + .. code-block:: python + + import appbuilder + + # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 + os.environ["APPBUILDER_TOKEN"] = '...' + + translate = appbuilder.Translation() + resp = translate(appbuilder.Message("你好\n中国"), to_lang="en") + # 输出 {'from_lang':'zh', 'to_lang':'en', 'trans_result':[{'src':'你好','dst':'hello'},{'src':'中国','dst':'China'}]} + print(resp.content) + """ + + name = "translate" + version = "v1" + + manifests = [ + { + "name": "translation", + "description": "文本翻译通用版工具,会根据指定的目标语言对文本进行翻译,并返回翻译后的文本。", + "parameters": { + "type": "object", + "properties": { + "q": { + "type": "string", + "description": "需要翻译的源文本,文本翻译工具会将该文本翻译成对应的目标语言" + }, + "to_lang": { + "type": "string", + "description": "翻译的目标语言类型,'en'表示翻译成英语, 'zh'表示翻译成中文,'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'表示翻译成越南语,默认为'en'", + "enum": ["en", "zh", "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"] + } + }, + "required": [ + "q", + "to_lang" + ] + } + } + ] + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + @HTTPClient.check_param + @components_run_trace + def run(self, message: Message, from_lang: str = "auto", to_lang: str = "en", + timeout: float = None, retry: int = 0) -> Message: + """ + 根据提供的文本以及语种参数执行文本翻译 + + Args: + message (Message): 翻译文本。 + from_lang (str): 翻译的源语言。默认为 "auto"。 + to_lang (str): 翻译的目标语言。默认为 "en"。 + timeout (float, optional): 翻译请求的超时时间。 + retry (int, optional): 重试次数。 + + Returns: + Message: 返回的文本翻译结果。 + 例如,Message(content={'from_lang': 'zh', 'to_lang': 'en', 'trans_result': [{'src': '你好', 'dst': 'hello'}]}) + """ + req = TranslateRequest() + req.q = message.content + req.from_lang = from_lang + req.to_lang = to_lang + result, data = self._translate(req, timeout=timeout, retry=retry) + result_dict = proto.Message.to_dict(result) + + out = TranslateOutMsg(**result_dict["result"]) + return Message(content=out.model_dump()) + + def _translate(self, request: TranslateRequest, timeout: float = None, + retry: int = 0, request_id: str = None) -> TranslateResponse: + """ + 根据提供的 TranslateRequest 执行文本翻译。 + + Args: + request (TranslateRequest): 翻译请求参数。 + timeout (float, optional): 请求超时时间。 + retry (int, optional): 重试次数。 + + Returns: + TranslateResponse: 文本翻译结果的响应体。 + """ + if not request.to_lang or not request.q: + raise ValueError("params `to_lang` and `q` must be set") + if not request.from_lang: + request.from_lang = "auto" + request_data = TranslateRequest.to_json(request) + if retry != self.http_client.retry.total: + self.http_client.retry.total = retry + headers = self.http_client.auth_header(request_id) + headers['content-type'] = 'application/json;charset=utf-8' + + url = self.http_client.service_url("/v1/bce/aip/mt/texttrans/v1") + + response = self.http_client.session.post(url, headers=headers, data=request_data, timeout=timeout) + + self.http_client.check_response_header(response) + data = response.json() + request_id = self.http_client.response_request_id(response) + self.http_client.check_response_json(data) + if "error_code" in data and "error_msg" in data: + raise AppBuilderServerException(request_id=request_id, service_err_code=data["error_code"], + service_err_message=data["error_msg"]) + + json_str = json.dumps(data) + return TranslateResponse(TranslateResponse.from_json(json_str)), data + + @components_run_stream_trace + def tool_eval(self, + q: str, + to_lang: str = "en", + **kwargs): + """ + 评估翻译工具的功能。 + + Args: + q (str): 需要翻译的文本。 + to_lang (str, optional): 目标语言,默认为 "en"。 + **kwargs: 其他参数。 + + Returns: + 生成器,生成翻译结果。 + + Raises: + InvalidRequestArgumentError: 如果参数 `q` 未设置,则引发此异常。 + + """ + traceid = kwargs.get("_sys_traceid", None) + text = q + req = TranslateRequest() + if not text: + raise InvalidRequestArgumentError("param `q` must be set") + req.q = text + req.to_lang = to_lang + result_response, raw_data = self._translate(req, request_id=traceid) + results = proto.Message.to_dict(result_response)["result"] + trans_result = results["trans_result"] + res = { + "原文本": "\n ".join(item["src"] for item in trans_result), + "翻译结果": "\n ".join(item["dst"] for item in trans_result) + } + res = json.dumps(res, ensure_ascii=False, indent=4) + yield self.create_output(type="text", text=res, raw_data=raw_data, visible_scope='llm') + yield self.create_output(type="text", text="", raw_data=raw_data, visible_scope='user') diff --git a/python/tests/component_tool_eval_cases.py b/python/tests/component_tool_eval_cases.py index b23ace2f2..600d195fd 100644 --- a/python/tests/component_tool_eval_cases.py +++ b/python/tests/component_tool_eval_cases.py @@ -176,7 +176,36 @@ def schemas(self): def outputs(self): return {"text": ["北京市公安局"]} +class TranslationCase(Case): + def inputs(self): + return { + "q": "你好", + "to_lang": "en", + } + + def schemas(self): + return [text_schema] + + def outputs(self): + return {"text": ["Hello"]} + +class GeneralOCRCase(Case): + def inputs(self): + return { + "img_url": "https://bj.bcebos.com/v1/appbuilder/general_ocr_test.png?" \ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-" \ + "11T10%3A59%3A17Z%2F-1%2Fhost%2F081bf7bcccbda5207c82a4de074628b04ae" \ + "857a27513734d765495f89ffa5f73", + "img_name": "test_img.jpg" + } + + def outputs(self): + return {"text": ["识别结果"]} + + def schemas(self): + return [text_schema] + component_tool_eval_cases = { "AnimalRecognition": AnimalRecognitionCase, @@ -188,4 +217,6 @@ def outputs(self): "QRcodeOCR": QRcodeOCRCase, "HandwriteOCR": HandWriteOCRCase, "MixCardOCR": MixCardOCRCase, + "Translation": TranslationCase, + "GeneralOCR": GeneralOCRCase, } \ No newline at end of file diff --git a/python/tests/test_v2_general_ocr.py b/python/tests/test_v2_general_ocr.py new file mode 100644 index 000000000..5041295cb --- /dev/null +++ b/python/tests/test_v2_general_ocr.py @@ -0,0 +1,58 @@ +# Copyright (c) 2024 Baidu, Inc. 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 os +import requests +import unittest +import appbuilder +from appbuilder.core.components.v2 import GeneralOCR +from appbuilder.core.component import ComponentOutput +from appbuilder.core._exception import InvalidRequestArgumentError + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") +class TestGeneralOCR(unittest.TestCase): + def setUp(self) -> None: + self.com = GeneralOCR() + + def test_run(self): + img_url = "https://bj.bcebos.com/v1/appbuilder/general_ocr_test.png?" \ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-" \ + "11T10%3A59%3A17Z%2F-1%2Fhost%2F081bf7bcccbda5207c82a4de074628b04ae" \ + "857a27513734d765495f89ffa5f73" + raw_image = requests.get(img_url).content + message = appbuilder.Message(content={"raw_image": raw_image}) + output = self.com.run(message) + print(output) + + def test_tool_eval(self): + img_url = "https://bj.bcebos.com/v1/appbuilder/general_ocr_test.png?" \ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-" \ + "11T10%3A59%3A17Z%2F-1%2Fhost%2F081bf7bcccbda5207c82a4de074628b04ae" \ + "857a27513734d765495f89ffa5f73" + img_name = "test_img.jpg" + result = self.com.tool_eval(img_url=img_url, img_name="") + for res in result: + assert isinstance(res, ComponentOutput) + print(res.content) + + def test_error_tool_eval(self): + result = self.com.tool_eval(img_url='', img_name='') + with self.assertRaises(InvalidRequestArgumentError): + list(result) + + result = self.com.tool_eval(img_url='', img_name='test.jpg') + with self.assertRaises(InvalidRequestArgumentError): + list(result) + +if __name__ == "__main__": + unittest.main() diff --git a/python/tests/test_v2_translate.py b/python/tests/test_v2_translate.py new file mode 100644 index 000000000..6ca1c78d0 --- /dev/null +++ b/python/tests/test_v2_translate.py @@ -0,0 +1,37 @@ +# Copyright (c) 2024 Baidu, Inc. 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 os +import unittest +import appbuilder +from appbuilder.core.components.v2 import Translation +from appbuilder.core.component import ComponentOutput + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") +class TestTranslation(unittest.TestCase): + def setUp(self) -> None: + self.com = Translation() + + def test_run(self): + msg = appbuilder.Message(content="你好") + result = self.com.run(message = msg, to_lang="en") + print(result) + + def test_tool_eval(self): + result = self.com.tool_eval(q="你好", to_lang="en") + for res in result: + assert isinstance(res, ComponentOutput) + print(res) + +if __name__ == '__main__': + unittest.main() From 35453a0cb2e5c4ea45a27d71b524c2388d93c03d Mon Sep 17 00:00:00 2001 From: C9luster <138663536+C9luster@users.noreply.github.com> Date: Wed, 4 Dec 2024 14:59:37 +0800 Subject: [PATCH 53/85] =?UTF-8?q?SDK=20Json=20Schemas=20=E7=9A=84Image?= =?UTF-8?q?=E6=A3=80=E6=B5=8B=E9=80=BB=E8=BE=91=E6=9B=B4=E6=96=B0=20(#638)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: yinjiaqi --- .../core/components/v2/animal_recognize/component.py | 12 ++++++------ python/tests/component_schemas.py | 1 - 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/python/core/components/v2/animal_recognize/component.py b/python/core/components/v2/animal_recognize/component.py index 68f8e52b2..e4e16ff9d 100644 --- a/python/core/components/v2/animal_recognize/component.py +++ b/python/core/components/v2/animal_recognize/component.py @@ -150,16 +150,16 @@ def tool_eval( **kwargs, ) -> Union[Generator[str, None, None], str]: """ - 用于工具的执行,通过调用底层接口进行动物识别。 + 对图像进行工具评估。 Args: - name (str): 工具名。 - streaming (bool): 是否流式返回。 - origin_query (str): 用户原始query。 - **kwargs: 工具调用的额外关键字参数。 + img_name (str): 图像名称。 + img_url (str): 图像URL地址。 + **kwargs: 其他关键字参数。 Returns: - Union[Generator[str, None, None], str]: 动物识别结果,包括识别出的动物类别和相应的置信度信息。 + Union[Generator[str, None, None], str]: 返回一个生成器,生成图像识别结果,或者返回图像识别结果的字符串。 + """ traceid = kwargs.get("_sys_traceid") file_urls = kwargs.get("_sys_file_urls", {}) diff --git a/python/tests/component_schemas.py b/python/tests/component_schemas.py index b98ddbfea..e2e117024 100644 --- a/python/tests/component_schemas.py +++ b/python/tests/component_schemas.py @@ -198,7 +198,6 @@ "type": "string" }, "byte": { - "type": "string", "format": "bytes" } }, From 5f23d8cd88a018bc1ce8c6e2c341ac7802a50ea3 Mon Sep 17 00:00:00 2001 From: peiwenYe <963623403@qq.com> Date: Wed, 4 Dec 2024 16:29:50 +0800 Subject: [PATCH 54/85] =?UTF-8?q?manifests=E6=A3=80=E6=9F=A5properties=20i?= =?UTF-8?q?tem=E7=9A=84type=E5=92=8Cdescription=E5=AD=97=E6=AE=B5=20(#639)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: yepeiwen01 --- .../llms/hallucination_detection/component.py | 6 +++--- .../llms/oral_query_generation/component.py | 6 +++--- .../components/ppt_generation_from_file/component.py | 2 +- .../ppt_generation_from_instruction/component.py | 2 +- .../components/ppt_generation_from_paper/component.py | 2 +- .../v2/llms/hallucination_detection/component.py | 6 +++--- python/tests/component_check.py | 11 +++++++++++ python/tests/test_all_components.py | 2 +- 8 files changed, 24 insertions(+), 13 deletions(-) diff --git a/python/core/components/llms/hallucination_detection/component.py b/python/core/components/llms/hallucination_detection/component.py index 2757a61f6..afd53c20f 100644 --- a/python/core/components/llms/hallucination_detection/component.py +++ b/python/core/components/llms/hallucination_detection/component.py @@ -72,15 +72,15 @@ class HallucinationDetection(CompletionBaseComponent): "type": "object", "properties": { "query": { - "text": "string", + "type": "string", "description": "用户查询。" }, "context": { - "text": "string", + "type": "string", "description": "检索结果。" }, "answer": { - "text": "string", + "type": "string", "description": "根据检索结果context生成的用户查询query的回答answer。" } }, diff --git a/python/core/components/llms/oral_query_generation/component.py b/python/core/components/llms/oral_query_generation/component.py index bce15e8e5..a8f4d3e1e 100644 --- a/python/core/components/llms/oral_query_generation/component.py +++ b/python/core/components/llms/oral_query_generation/component.py @@ -59,15 +59,15 @@ class OralQueryGeneration(CompletionBaseComponent): "type": "object", "properties": { "text": { - "text": "string", + "type": "string", "description": "输入文本,组件会根据该输入文本生成query。" }, "query_type": { - "text": "string", + "type": "string", "description": "待生成的query类型,可选问题、短语以及全部(问题 + 短语)。" }, "output_format": { - "text": "string", + "type": "string", "description": "输出格式,可选json或str,str格式与老版本输出格式相同。" } }, diff --git a/python/core/components/ppt_generation_from_file/component.py b/python/core/components/ppt_generation_from_file/component.py index bffb64aa5..f0cd9fe47 100644 --- a/python/core/components/ppt_generation_from_file/component.py +++ b/python/core/components/ppt_generation_from_file/component.py @@ -63,7 +63,7 @@ class PPTGenerationFromFile(Component): "type": "object", "properties": { "file_url": { - "text": "string", + "type": "string", "description": "用户上传的文件的链接。" } }, diff --git a/python/core/components/ppt_generation_from_instruction/component.py b/python/core/components/ppt_generation_from_instruction/component.py index 0202e7b8e..bd3e276a8 100644 --- a/python/core/components/ppt_generation_from_instruction/component.py +++ b/python/core/components/ppt_generation_from_instruction/component.py @@ -66,7 +66,7 @@ class PPTGenerationFromInstruction(Component): "type": "object", "properties": { "text": { - "text": "string", + "type": "string", "description": "用户请求生成PPT的指令。", "example": "生成一个介绍北京的PPT。" } diff --git a/python/core/components/ppt_generation_from_paper/component.py b/python/core/components/ppt_generation_from_paper/component.py index 9f3b463ad..fc65a4cbe 100644 --- a/python/core/components/ppt_generation_from_paper/component.py +++ b/python/core/components/ppt_generation_from_paper/component.py @@ -63,7 +63,7 @@ class PPTGenerationFromPaper(Component): "type": "object", "properties": { "file_key": { - "text": "string", + "type": "string", "description": "用户上传的论文的链接。" } }, diff --git a/python/core/components/v2/llms/hallucination_detection/component.py b/python/core/components/v2/llms/hallucination_detection/component.py index 627abf496..9d4f4cbb8 100644 --- a/python/core/components/v2/llms/hallucination_detection/component.py +++ b/python/core/components/v2/llms/hallucination_detection/component.py @@ -72,15 +72,15 @@ class HallucinationDetection(CompletionBaseComponent): "type": "object", "properties": { "query": { - "text": "string", + "type": "string", "description": "用户查询。" }, "context": { - "text": "string", + "type": "string", "description": "检索结果。" }, "answer": { - "text": "string", + "type": "string", "description": "根据检索结果context生成的用户查询query的回答answer。" } }, diff --git a/python/tests/component_check.py b/python/tests/component_check.py index c4fd34212..1c6fd4b32 100644 --- a/python/tests/component_check.py +++ b/python/tests/component_check.py @@ -85,14 +85,25 @@ def check(self, component_cls) -> CheckInfo: pydantic_model = json_schema_to_pydantic_model(schema, tool_name) check_to_json = pydantic_model.schema_json() json_to_dict = json.loads(check_to_json) + + if "properties" in schema: + properties = schema["properties"] + for key, value in properties.items(): + if "type" not in value: + invalid_details.append("\'type' must be in properties item: {}".format(key)) + if "description" not in value: + invalid_details.append("\'description' must be in properties item: {}".format(key)) + except Exception as e: print(e) check_pass_flag = False invalid_details.append(str(e)) if len(invalid_details) > 0: + check_pass_flag = False invalid_details = ",".join(invalid_details) else: + check_pass_flag = True invalid_details = "" return CheckInfo( check_rule_name=self.rule_name, diff --git a/python/tests/test_all_components.py b/python/tests/test_all_components.py index f084b3c81..ca1b94cca 100644 --- a/python/tests/test_all_components.py +++ b/python/tests/test_all_components.py @@ -84,7 +84,7 @@ def write_error_data(txt_file_path, error_df, error_stats): file.write(f"错误信息: {error}, 出现次数: {count}\n") print(f"\n错误信息已写入: {txt_file_path}") -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") class TestComponentManifestsAndToolEval(unittest.TestCase): """ 组件manifests和tool_eval入参测试类 From 347207755e1f6488c8ad5ab489f3e6688c04ab14 Mon Sep 17 00:00:00 2001 From: C9luster <138663536+C9luster@users.noreply.github.com> Date: Thu, 5 Dec 2024 11:46:54 +0800 Subject: [PATCH 55/85] New update components (#641) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 更新Text2Image\StyleWriting\TableOCR组件 * update * update --------- Co-authored-by: yinjiaqi --- python/core/components/v2/__init__.py | 6 + .../v2/llms/style_writing/__init__.py | 16 + .../v2/llms/style_writing/component.py | 176 +++++++++ .../core/components/v2/table_ocr/__init__.py | 13 + .../core/components/v2/table_ocr/component.py | 230 +++++++++++ .../components/v2/text_to_image/__init__.py | 15 + .../components/v2/text_to_image/component.py | 367 ++++++++++++++++++ python/tests/component_tool_eval_cases.py | 45 ++- python/tests/test_v2_style_writing.py | 43 ++ python/tests/test_v2_table_ocr.py | 40 ++ python/tests/test_v2_text_to_image.py | 67 ++++ 11 files changed, 1017 insertions(+), 1 deletion(-) create mode 100644 python/core/components/v2/llms/style_writing/__init__.py create mode 100644 python/core/components/v2/llms/style_writing/component.py create mode 100644 python/core/components/v2/table_ocr/__init__.py create mode 100644 python/core/components/v2/table_ocr/component.py create mode 100644 python/core/components/v2/text_to_image/__init__.py create mode 100644 python/core/components/v2/text_to_image/component.py create mode 100644 python/tests/test_v2_style_writing.py create mode 100644 python/tests/test_v2_table_ocr.py create mode 100644 python/tests/test_v2_text_to_image.py diff --git a/python/core/components/v2/__init__.py b/python/core/components/v2/__init__.py index 1d559d86f..35f65bf11 100644 --- a/python/core/components/v2/__init__.py +++ b/python/core/components/v2/__init__.py @@ -21,6 +21,9 @@ from .qrcode_ocr.component import QRcodeOCR from .handwrite_ocr.component import HandwriteOCR from .mix_card_ocr.component import MixCardOCR +from .table_ocr.component import TableOCR +from .text_to_image.component import Text2Image +from .llms.style_writing.component import StyleWriting __V2_COMPONENTS__ = [ "AnimalRecognition", @@ -32,4 +35,7 @@ "QRcodeOCR", "HandwriteOCR", "MixCardOCR", + "TableOCR", + "Text2Image", + "StyleWriting" ] # NOQA \ No newline at end of file diff --git a/python/core/components/v2/llms/style_writing/__init__.py b/python/core/components/v2/llms/style_writing/__init__.py new file mode 100644 index 000000000..f25fffc47 --- /dev/null +++ b/python/core/components/v2/llms/style_writing/__init__.py @@ -0,0 +1,16 @@ +# Copyright (c) 2024 Baidu, Inc. 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 .component import StyleWriting diff --git a/python/core/components/v2/llms/style_writing/component.py b/python/core/components/v2/llms/style_writing/component.py new file mode 100644 index 000000000..44b6ab205 --- /dev/null +++ b/python/core/components/v2/llms/style_writing/component.py @@ -0,0 +1,176 @@ +# Copyright (c) 2023 Baidu, Inc. 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 appbuilder.core.components.llms.base import CompletionBaseComponent +from appbuilder.core.message import Message + +from appbuilder.core.component import ComponentArguments +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace + +from pydantic import Field +from typing import Optional + +from appbuilder.core.components.llms.style_writing.base import StyleQueryChoices, LengthChoices + +class StyleWritingArgs(ComponentArguments): + """ + 风格写作配置 + + Attributes: + message: Message = Field(...) + style_query: StyleQueryChoices = Field(...) + length: LengthChoices = Field(...) + """ + message: Message = Field(..., + variable_name="query", + description="输入消息,用于模型的主要输入内容,例如'帮我生成一个介绍保温杯的话术'") + style_query: StyleQueryChoices = Field(..., + variable_name="style_query", + description="风格查询选项,可选值为 'B站', '小红书', '通用'。") + length: LengthChoices = Field(..., + variable_name="length", + description="输出长度,可选值为 '短' (100), '中' (300), '长' (600)。") + + +class StyleWriting(CompletionBaseComponent): + """ + 风格写作大模型组件, 基于生成式大模型进行风格写作,支持B站、小红书等多种风格,可用于文案、广告等多种场景。 + + Examples: + + .. code-block:: python + + import os + import appbuilder + # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 + os.environ["APPBUILDER_TOKEN"] = '...' + + style_writing = appbuilder.StyleWriting(model="Qianfan-Agent-Speed-8k") + answer = style_writing(appbuilder.Message("帮我写一篇关于人体工学椅的文案"), style_query="小红书", length=100) + + """ + + name = "style_writing" + version = "v1" + meta = StyleWritingArgs + + manifests = [ + { + "name": "style_writing", + "description": "根据用户输入的文案要求和文案风格,生成符合特定风格的产品介绍或宣传文案。目前支持生成小红书风格、B站风格或通用风格的文案。", + "parameters": { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "用于描述生成文案的主题和要求。" + }, + "style": { + "type": "string", + "description": "用于定义文案生成的风格,包括通用、B站、小红书,默认为通用。", + "enum": ["通用", "B站", "小红书"] + }, + "length": { + "type": "integer", + "description": "用于定义输出内容的长度。有效的选项包括 100(短)、300(中)、600(长),默认值为 100。", + "enum": [100, 300, 600] + } + }, + "required": [ + "query" + ] + } + } + ] + + def __init__( + self, + model=None, + secret_key: Optional[str] = None, + gateway: str = "", + lazy_certification: bool = False, + ): + """初始化StyleWriting模型。 + + Args: + model (str|None): 模型名称,用于指定要使用的千帆模型。 + secret_key (str, 可选): 用户鉴权token, 默认从环境变量中获取: os.getenv("APPBUILDER_TOKEN", ""). + gateway (str, 可选): 后端网关服务地址,默认从环境变量中获取: os.getenv("GATEWAY_URL", "") + lazy_certification (bool, 可选): 延迟认证,为True时在第一次运行时认证. Defaults to False. + + Returns: + None + + """ + super().__init__( + StyleWritingArgs, model=model, secret_key=secret_key, gateway=gateway, + lazy_certification=lazy_certification) + + @components_run_trace + def run(self, message, style_query="通用", length=100, stream=False, temperature=1e-10, top_p=0, request_id=None): + """ + 使用给定的输入运行模型并返回结果。 + + Args: + message (obj:`Message`): 输入消息,用于模型的主要输入内容。这是一个必需的参数。 + style_query (str): 风格查询选项,用于指定写作风格。有效的选项包括 'B站', '小红书', '通用'。默认值为 '通用'。 + length (int): 输出内容的长度。有效的选项包括 100(短),300(中),600(长)。默认值为 100。 + stream (bool, optional): 指定是否以流式形式返回响应。默认为 False。 + temperature (float, optional): 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 + top_p (float, optional): 影响输出文本的多样性,取值越大,生成文本的多样性越强。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 + request_id (str, optional): 请求ID,用于跟踪和识别请求。 + + Returns: + obj:`Message`: 模型运行后的输出消息。 + """ + return super().run(message=message, style_query=style_query, length=length, stream=stream, + temperature=temperature, top_p=top_p, request_id=request_id) + + @components_run_stream_trace + def tool_eval(self, + query: str, + style: str = "通用", + length: int = 100, + **kwargs): + """ + 对指定的工具进行函数调用评估。 + + Args: + name (str): 工具名称。 + streaming (bool, optional): 是否以流的方式返回结果。默认为False。 + **kwargs: 其他参数。 + + Returns: + str 或 generator: 如果 streaming 为 False,则返回评估结果字符串;如果 streaming 为 True,则返回一个生成器,每次迭代返回评估结果字符串的一部分。 + + Raises: + ValueError: 如果未提供必要的参数 'query'。 + + """ + traceid = kwargs.get("_sys_traceid") + if not query: + raise ValueError("param `query` is required") + msg = Message(query) + try: + length = int(length) + if length not in [100, 300, 600]: + length = 100 + except: + length = 100 + model_configs = kwargs.get('model_configs', {}) + temperature = model_configs.get("temperature", 1e-10) + top_p = model_configs.get("top_p", 0.0) + message = super().run(message=msg, style_query=style, length=length, stream=False, + temperature=temperature, top_p=top_p, request_id=traceid) + + yield self.create_output(type="text", text=str(message.content), usage=message.token_usage) diff --git a/python/core/components/v2/table_ocr/__init__.py b/python/core/components/v2/table_ocr/__init__.py new file mode 100644 index 000000000..b4518e8cd --- /dev/null +++ b/python/core/components/v2/table_ocr/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2024 Baidu, Inc. 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. \ No newline at end of file diff --git a/python/core/components/v2/table_ocr/component.py b/python/core/components/v2/table_ocr/component.py new file mode 100644 index 000000000..114c256eb --- /dev/null +++ b/python/core/components/v2/table_ocr/component.py @@ -0,0 +1,230 @@ +# Copyright (c) 2024 Baidu, Inc. 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. + +"""table ocr component.""" + +import base64 +import json + +from appbuilder.core import utils +from appbuilder.core.component import Component +from appbuilder.core.components.table_ocr.model import * +from appbuilder.core.message import Message +from appbuilder.core._client import HTTPClient +from appbuilder.core._exception import AppBuilderServerException, InvalidRequestArgumentError +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace + + +class TableOCR(Component): + r""" + 支持识别图片中的表格内容,返回各表格的表头表尾内容、单元格文字内容及其行列位置信息,全面覆盖各类表格样式,包括常规有线表格、 + 无线表格、含合并单元格表格。同时,支持多表格内容识别。 + + Examples: + + .. code-block:: python + + import appbuilder + # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 + os.environ["APPBUILDER_TOKEN"] = '...' + + table_ocr = appbuilder.TableOCR() + with open("./table_ocr_test.png", "rb") as f: + out = self.component.run(appbuilder.Message(content={"raw_image": f.read()})) + print(out.content) + + """ + + name = "table_ocr" + version = "v1" + manifests = [ + { + "name": "table_ocr", + "description": "需要识别图片中的表格内容,使用该工具, 但不支持html后缀文件的识别", + "parameters": { + "type": "object", + "properties": { + "file_names": { + "type": "array", + "items": { + "type": "string" + }, + "description": "待识别图片的文件名" + } + }, + "required": ["file_names"] + } + } + ] + + @HTTPClient.check_param + @components_run_trace + def run(self, message: Message, timeout: float = None, retry: int = 0) -> Message: + """ + 表格文字识别 + + Args: + message (Message): 输入图片或图片url下载地址用于执行识别操作。 + 举例: Message(content={"raw_image": b"..."}) + 或 Message(content={"url": "https://image/download/url"})。 + timeout (float, 可选): HTTP超时时间。 + retry (int, 可选): HTTP重试次数。 + + Returns: + message (Message): 识别结果。 + 举例: Message(name=msg, content={'tables_result': [{ + 'table_location': [{'x': 15, 'y': 15}, {'x': 371, 'y': 15}, {'x': 371, 'y': 98}, {'x': 15, + 'y': 98}], 'header': [], 'body': [{'cell_location': [{'x': 15, 'y': 15}, {'x': 120, 'y': 15}, + {'x': 120, 'y': 58}, {'x': 15, 'y': 58}], 'row_start': 0, 'row_end': 1, 'col_start': 0, + 'col_end': 1, 'words': '参数'}, {'cell_location': [{'x': 120, 'y': 15}, {'x': 371, 'y': 15}, + {'x': 371, 'y': 58}, {'x': 120, 'y': 58}], 'row_start': 0, 'row_end': 1, 'col_start': 1, + 'col_end': 2, 'words': '值'}, {'cell_location': [{'x': 15, 'y': 58}, {'x': 120, 'y': 58}, + {'x': 120, 'y': 98}, {'x': 15, 'y': 98}], 'row_start': 1, 'row_end': 2, 'col_start': 0, + 'col_end': 1, 'words': 'Content-Type'}, {'cell_location': [{'x': 120, 'y': 58}, {'x': 371, + 'y': 58}, {'x': 371, 'y': 98}, {'x': 120, 'y': 98}], 'row_start': 1, 'row_end': 2, 'col_start': + 1, 'col_end': 2, 'words': 'application/x-www-form-urlencoded'}], 'footer': []}]}, mtype=dict) + + """ + inp = TableOCRInMsg(**message.content) + req = TableOCRRequest() + if inp.raw_image: + req.image = base64.b64encode(inp.raw_image) + if inp.url: + req.url = inp.url + req.cell_contents = "false" + result, _ = self._recognize(req, timeout, retry) + result_dict = proto.Message.to_dict(result) + out = TableOCROutMsg(**result_dict) + return Message(content=out.model_dump()) + + def _recognize(self, request: TableOCRRequest, timeout: float = None, + retry: int = 0, request_id: str = None) -> TableOCRResponse: + r"""调用底层接口进行表格文字识别 + 参数: + request (obj: `TableOCRRequest`) : 表格文字识别输入参数 + 返回: + response (obj: `TableOCRResponse`): 表格文字识别返回结果 + """ + if not request.image and not request.url: + raise ValueError( + "request format error, one of image or url must be set") + + data = TableOCRRequest.to_dict(request) + if self.http_client.retry.total != retry: + self.http_client.retry.total = retry + headers = self.http_client.auth_header(request_id) + headers['content-type'] = 'application/x-www-form-urlencoded' + url = self.http_client.service_url("/v1/bce/aip/ocr/v1/table") + response = self.http_client.session.post( + url, headers=headers, data=data, timeout=timeout) + self.http_client.check_response_header(response) + data = response.json() + self.http_client.check_response_json(data) + request_id = self.http_client.response_request_id(response) + self.__class__._check_service_error(request_id, data) + res = TableOCRResponse.from_json(json.dumps(data)) + res.request_id = request_id + return res, data + + @staticmethod + def _check_service_error(request_id: str, data: dict): + r"""个性化服务response参数检查 + 参数: + request (dict) : 表格文字识别body返回 + 返回: + 无 + """ + if "error_code" in data or "error_msg" in data: + raise AppBuilderServerException( + request_id=request_id, + service_err_code=data.get("error_code"), + service_err_message=data.get("error_msg") + ) + + def _get_table_markdown(self, tables_result): + """ + 将表格识别结果转换为Markdown格式。 + + Args: + tables_result (list): 表格识别结果列表,每个元素是一个包含表格数据的字典,其中包含表格体(body)等字段。 + + Returns: + list: 包含Markdown格式表格的字符串列表。 + + """ + markdowns = [] + for table in tables_result: + cells = table["body"] + max_row = max(cell['row_end'] for cell in cells) + max_col = max(cell['col_end'] for cell in cells) + # 初始化表格数组 + table_arr = [[''] * max_col for _ in range(max_row)] + # 填充表格数据 + for cell in cells: + row = cell['row_start'] + col = cell['col_start'] + table_arr[row][col] = cell['words'] + + markdown_table = "" + for row in table_arr: + markdown_table += "| " + " | ".join(row) + " |\n" + # 生成分隔行 + separator = "| " + " | ".join(['---'] * max_col) + " |\n" + # 插入分隔行在表头下方 + header, body = markdown_table.split('\n', 1) + markdown_table = header + '\n' + separator + body + markdowns.append(markdown_table) + return markdowns + + @components_run_stream_trace + def tool_eval(self, + file_names: List[str], + **kwargs): + """ + 处理并评估传入的文件列表,并返回表格数据的Markdown格式表示。 + + Args: + file_names (List[str]): 待处理的文件列表。 + **kwargs: 其他可选参数。 + + Returns: + Generator: 生成包含处理结果的生成器。 + + Raises: + InvalidRequestArgumentError: 如果请求格式错误,文件URL不存在。 + + """ + result = {} + traceid = kwargs.get("_sys_traceid") + file_urls = kwargs.get("_sys_file_urls", {}) + for file_name in file_names: + if utils.is_url(file_name): + file_url = file_name + else: + file_url = file_urls.get(file_name, None) + if file_url is None: + raise InvalidRequestArgumentError( + f"request format error, file {file_name} url does not exist" + ) + req = TableOCRRequest() + req.url = file_url + req.cell_contents = "false" + resp, raw_data = self._recognize(req, request_id=traceid) + tables_result = proto.Message.to_dict(resp)["tables_result"] + markdowns = self._get_table_markdown(tables_result) + result[file_name] = markdowns + + result = json.dumps(result, ensure_ascii=False) + yield self.create_output(type="text", text=result, raw_data=raw_data, visible_scope="llm") + yield self.create_output(type="text", text="", raw_data=raw_data, visible_scope="user") diff --git a/python/core/components/v2/text_to_image/__init__.py b/python/core/components/v2/text_to_image/__init__.py new file mode 100644 index 000000000..4c5015aa8 --- /dev/null +++ b/python/core/components/v2/text_to_image/__init__.py @@ -0,0 +1,15 @@ +# Copyright (c) 2024 Baidu, Inc. 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. + + diff --git a/python/core/components/v2/text_to_image/component.py b/python/core/components/v2/text_to_image/component.py new file mode 100644 index 000000000..ba0be82a2 --- /dev/null +++ b/python/core/components/v2/text_to_image/component.py @@ -0,0 +1,367 @@ +# Copyright (c) 2024 Baidu, Inc. 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. + +r"""Text2Image component. +""" +import time +import math + +from typing import Generator, Union, Optional +from appbuilder.core.component import Component +from appbuilder.core.message import Message +from appbuilder.core._client import HTTPClient +from appbuilder.core._exception import AppBuilderServerException, RiskInputException +from appbuilder.core.components.text_to_image.model import Text2ImageSubmitRequest, Text2ImageQueryRequest, \ + Text2ImageQueryResponse, Text2ImageSubmitResponse, Text2ImageOutMessage +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace + + +class Text2Image(Component): + r""" + 文生图组件,即对于输入的文本,输出生成的图片url。 + Examples: + .. code-block:: python + import appbuilder + text_to_image = appbuilder.Text2Image() + os.environ["APPBUILDER_TOKEN"] = '...' + content_data = {"prompt": "上海的经典风景", "width": 1024, "height": 1024, "image_num": 1} + msg = appbuilder.Message(content_data) + out = text_to_image.run(inp) + # 打印生成结果 + print(out.content) # eg: {"img_urls": ["xxx"]} + """ + name = "text_to_image" + version = "v1" + manifests = [ + { + "name": "text_to_image", + "description": "文生图,该组件只用于图片创作。当用户需要进行场景、人物、海报等内容的绘制时,使用该画图组件。如果用户需要生成图表(柱状图、折线图、雷达图等),则必须使用代码解释器。", + "parameters": { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "文生图用的query。特别注意,这个字段只能由中文字符组成,不能含有任何英语描述。" + } + }, + "required": [ + "query" + ] + } + } + ] + + @HTTPClient.check_param + @components_run_trace + def run( + self, + message: Message, + width: int = 1024, + height: int = 1024, + image_num: int = 1, + image: Optional[str] = None, + url: Optional[str] = None, + pdf_file: Optional[str] = None, + pdf_file_num: Optional[str] = None, + change_degree: Optional[int] = None, + text_content: Optional[str] = None, + task_time_out: Optional[int]= None, + text_check: Optional[int] = 1, + request_id: Optional[str] = None + ): + """ + 执行文本到图像的生成任务。 + + Args: + message (Message): 包含任务相关信息的消息对象。 + width (int, optional): 生成的图像的宽度,默认为1024。 + height (int, optional): 生成的图像的高度,默认为1024。 + image_num (int, optional): 生成图像的数量,默认为1。 + image (Optional[str], optional): 参考图像的路径或URL,默认为None。 + url (Optional[str], optional): 参考图像的URL,默认为None。 + pdf_file (Optional[str], optional): 参考PDF文件的路径,默认为None。 + pdf_file_num (Optional[str], optional): 参考PDF文件中的页码范围,默认为None。 + change_degree (Optional[int], optional): 图像变换的程度,默认为None。 + text_content (Optional[str], optional): 需要转换的文本内容,默认为None。 + task_time_out (Optional[int], optional): 任务超时时间,默认为None。 + text_check (Optional[int], optional): 是否进行文本内容检查,默认为1。 + request_id (Optional[str], optional): 请求的唯一标识,默认为None。 + + Returns: + Message: 包含生成图像URL的消息对象。 + + Raises: + HTTPError: 请求失败时抛出异常。 + + """ + prompt=message.content["prompt"] + img_urls, _ = self._recognize( + prompt = prompt, + width = width, + height = height, + image_num = image_num, + image = image, + url = url, + pdf_file = pdf_file, + pdf_file_num = pdf_file_num, + change_degree = change_degree, + text_content = text_content, + task_time_out = task_time_out, + text_check = text_check, + request_id = request_id + ) + if len(img_urls) == 0: + raise RiskInputException(f'prompt{prompt} 中可能存在敏感词') + out = Text2ImageOutMessage(img_urls=img_urls) + return Message(content=out.model_dump()) + + @components_run_stream_trace + def tool_eval( + self, + query: str, + **kwargs, + ) -> Union[Generator[str, None, None], str]: + """ + 根据输入的查询内容生成输出。 + + Args: + query (str): 查询内容。 + **kwargs: 其他参数,包含: + _sys_traceid (str, optional): 系统追踪ID,默认为None。 + width (int, optional): 图片宽度,默认为1024。 + height (int, optional): 图片高度,默认为1024。 + image_num (int, optional): 图片数量,默认为1。 + image (Union[str, bytes], optional): 图片数据,默认为None。 + url (str, optional): 图片URL,默认为None。 + pdf_file (Union[str, bytes], optional): PDF文件数据,默认为None。 + pdf_file_num (int, optional): PDF文件页数,默认为None。 + change_degree (int, optional): 图片旋转角度,默认为None。 + text_content (str, optional): 文本内容,默认为None。 + task_time_out (int, optional): 任务超时时间,默认为None。 + text_check (int, optional): 文本校验等级,默认为1。 + + Returns: + Union[Generator[str, None, None], str]: 生成的输出。 + + Raises: + RiskInputException: 当查询内容中存在敏感词时抛出。 + + """ + request_id = kwargs.get("_sys_traceid", None) + prompt = query + width = kwargs.get("width", 1024) + height = kwargs.get("height", 1024) + image_num = kwargs.get("image_num", 1) + image = kwargs.get("image", None) + url = kwargs.get("url", None) + pdf_file = kwargs.get("pdf_file", None) + pdf_file_num = kwargs.get("pdf_file_num", None) + change_degree = kwargs.get("change_degree", None) + text_content = kwargs.get("text_content", None) + task_time_out = kwargs.get("task_time_out", None) + text_check = kwargs.get("text_check", 1) + + img_urls, raw_data = self._recognize( + prompt = prompt, + width = width, + height = height, + image_num = image_num, + image = image, + url = url, + pdf_file = pdf_file, + pdf_file_num = pdf_file_num, + change_degree = change_degree, + text_content = text_content, + task_time_out = task_time_out, + text_check = text_check, + request_id = request_id + ) + + if len(img_urls) == 0: + raise RiskInputException(f'query:{query} 中可能存在敏感词') + + for url_number in range(len(img_urls)): + yield self.create_output( + type='image', + text={ + 'filename': "", + 'url': img_urls[url_number], + }, + raw_data=raw_data, + visible_scope='all', + ) + + def _recognize( + self, + prompt: str, + width: int = 1024, + height: int = 1024, + image_num: int = 1, + image: Optional[str] = None, + url: Optional[str] = None, + pdf_file: Optional[str] = None, + pdf_file_num: Optional[str] = None, + change_degree: Optional[int] = None, + text_content: Optional[str] = None, + task_time_out: Optional[int]= None, + text_check: Optional[int] = 1, + request_id: Optional[str] = None + ): + """ + 识别并生成图片。 + + Args: + prompt (str): 提示文本,用于生成图片。 + width (int, optional): 图片宽度,默认为1024。 + height (int, optional): 图片高度,默认为1024。 + image_num (int, optional): 生成图片的数量,默认为1。 + image (str, optional): 传入图片路径,默认为None。 + url (str, optional): 图片URL,默认为None。 + pdf_file (str, optional): PDF文件路径,默认为None。 + pdf_file_num (str, optional): 需要转换的PDF页数,默认为None。 + change_degree (int, optional): 图片旋转角度,默认为None。 + text_content (str, optional): 文本内容,默认为None。 + task_time_out (int, optional): 任务超时时间,默认为None。 + text_check (int, optional): 文本校验选项,默认为1。 + request_id (str, optional): 请求ID,默认为None。 + + Returns: + tuple: 包含生成的图片URL列表和返回数据的元组。 + + """ + headers = self._http_client.auth_header() + headers["Content-Type"] = "application/json" + api_url = self._http_client.service_url("/v1/bce/aip/ernievilg/v1/txt2imgv2") + + req = Text2ImageSubmitRequest( + prompt=prompt, + width=width, + height=height, + image_num=image_num, + image=image, + url=url, + pdf_file=pdf_file, + pdf_file_num=pdf_file_num, + change_degree=change_degree, + text_content=text_content, + task_time_out=task_time_out, + text_check=text_check + ) + response = self.http_client.session.post(api_url, json=req.model_dump(), headers=headers, timeout=None) + self._http_client.check_response_header(response) + data = response.json() + resp= Text2ImageSubmitResponse(**data) + + taskId = resp.data.task_id + if taskId is not None: + task_request_time = 1 + + while True: + request = Text2ImageQueryRequest(task_id=taskId) + text2ImageQueryResponse, data = self._queryText2ImageData(request, request_id=request_id) + if text2ImageQueryResponse.data.task_progress is not None: + task_progress = float(text2ImageQueryResponse.data.task_progress) + if math.isclose(1.0, task_progress, rel_tol=1e-9, abs_tol=0.0): + break + + # NOTE(chengmo):文生图组件的返回时间在10s以上,查询过于频繁会被限流,导致异常报错 + # 此处采用 yangyongzhen老师提供的方案,前三次查询间隔3s,后三次查询间隔逐渐增大 + if task_request_time <= 3: + time.sleep(3) + else: + time.sleep(task_request_time) + task_request_time += 1 + + img_urls = self._extract_img_urls(text2ImageQueryResponse) + + return img_urls, data + + def _queryText2ImageData( + self, + request: Text2ImageQueryRequest, + timeout: float = None, + retry: int = 0, + request_id: str = None, + ) -> Text2ImageQueryResponse: + """ + 将文本查询请求转换为图像数据。 + + Args: + request (Text2ImageQueryRequest): 输入请求,必填参数。 + timeout (float, optional): 请求的超时时间,默认为None。 + retry (int, optional): 请求的重试次数,默认为0。 + request_id (str, optional): 请求的唯一标识符,默认为None。 + + Returns: + Text2ImageQueryResponse: 接口返回的输出消息。 + """ + url = self.http_client.service_url("/v1/bce/aip/ernievilg/v1/getImgv2") + data = { + "task_id": request.task_id + } + headers = self.http_client.auth_header(request_id) + headers['content-type'] = 'application/json' + if retry != self.http_client.retry.total: + self.http_client.retry.total = retry + response = self.http_client.session.post(url, json=data, headers=headers, timeout=timeout) + self.http_client.check_response_header(response) + data = response.json() + self.http_client.check_response_json(data) + request_id = self.http_client.response_request_id(response) + self.__class__._check_service_error(request_id, data) + response = Text2ImageQueryResponse(**data) + return response, data + + def _extract_img_urls(self, response: Text2ImageQueryResponse): + """ + 从作画生成的返回结果中提取图片url。 + + Args: + response (obj:`Text2ImageQueryResponse`): 作画生成的返回结果。 + + Returns: + List[str]: 从返回体中提取的图片url列表。 + + """ + img_urls = [] + if response and response.data and response.data.sub_task_result_list: + for sub_task_result in response.data.sub_task_result_list: + if sub_task_result and sub_task_result.final_image_list: + for final_image in sub_task_result.final_image_list: + if final_image and final_image.img_url: + img_urls.append(final_image.img_url) + + return img_urls + + @staticmethod + def _check_service_error(request_id: str, data: dict): + """ + 检查服务错误信息 + + Args: + request_id (str): 请求ID + data (dict): 响应数据 + + Raises: + AppBuilderServerException: 如果响应数据中包含错误信息,则抛出异常 + + Returns: + None + """ + if "error_code" in data or "error_msg" in data: + raise AppBuilderServerException( + request_id=request_id, + service_err_code=data.get("error_code"), + service_err_message=data.get("error_msg") + ) \ No newline at end of file diff --git a/python/tests/component_tool_eval_cases.py b/python/tests/component_tool_eval_cases.py index 600d195fd..eed5c5f2a 100644 --- a/python/tests/component_tool_eval_cases.py +++ b/python/tests/component_tool_eval_cases.py @@ -205,7 +205,47 @@ def outputs(self): def schemas(self): return [text_schema] - +class TableOCRCase(Case): + def inputs(self): + image_url = "https://bj.bcebos.com/v1/appbuilder/table_ocr_test.png?"\ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-24T12%3A37%3A09Z%2F-1%2Fhost%2Fab528a5a9120d328dc6d18c6"\ + "064079145ff4698856f477b820147768fc2187d3" + return { + "file_names": [image_url] + } + + def schemas(self): + return [text_schema] + + def outputs(self): + return {"text": ["http"]} + + +class Text2ImageCase(Case): + def inputs(self): + return { + 'query': '生成一张小猫图片', + } + + def schemas(self): + return [image_schema] + +class StyleWritingCase(Case): + def init_args(self): + return {"model": "ERNIE-3.5-8K"} + + def inputs(self): + return { + "query": "帮我写一篇关于足球的文案", + "style": "小红书", + "length": 100, + } + + def schemas(self): + return [text_schema] + + def outputs(self): + return {"text": ["足球"]} component_tool_eval_cases = { "AnimalRecognition": AnimalRecognitionCase, @@ -219,4 +259,7 @@ def schemas(self): "MixCardOCR": MixCardOCRCase, "Translation": TranslationCase, "GeneralOCR": GeneralOCRCase, + "TableOCR": TableOCRCase, + "Text2Image": Text2ImageCase, + "StyleWriting": StyleWritingCase, } \ No newline at end of file diff --git a/python/tests/test_v2_style_writing.py b/python/tests/test_v2_style_writing.py new file mode 100644 index 000000000..105fd9282 --- /dev/null +++ b/python/tests/test_v2_style_writing.py @@ -0,0 +1,43 @@ +# Copyright (c) 2024 Baidu, Inc. 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 os +import unittest +import appbuilder +from appbuilder.core.components.v2 import StyleWriting +from appbuilder.core.component import ComponentOutput + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") +class TestStyleWriting(unittest.TestCase): + def setUp(self): + self.com = StyleWriting(model = "ERNIE-3.5-8K") + + def test_run(self): + query = "帮我写一篇关于足球的文案" + msg = appbuilder.Message(query) + style = "小红书" + length = 100 + out = self.com.run(msg, style_query=style, length=length) + print(out) + + def test_tool_eval(self): + query = "帮我写一篇关于足球的文案" + style = "小红书" + length = 150 + result = self.com.tool_eval(query, style, length) + for res in result: + assert isinstance(res, ComponentOutput) + print(res) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/python/tests/test_v2_table_ocr.py b/python/tests/test_v2_table_ocr.py new file mode 100644 index 000000000..b4b70c8e8 --- /dev/null +++ b/python/tests/test_v2_table_ocr.py @@ -0,0 +1,40 @@ +# Copyright (c) 2024 Baidu, Inc. 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 os +import unittest +import appbuilder +from appbuilder.core.components.v2 import TableOCR +from appbuilder.core.component import ComponentOutput + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") +class TestTableOCR(unittest.TestCase): + def setUp(self): + self.com = TableOCR() + self.image_url = "https://bj.bcebos.com/v1/appbuilder/table_ocr_test.png?"\ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-24T12%3A37%3A09Z%2F-1%2Fhost%2Fab528a5a9120d328dc6d18c6"\ + "064079145ff4698856f477b820147768fc2187d3" + + def test_run(self): + out = self.com.run(appbuilder.Message(content={"url": self.image_url})) + print(out) + + + def test_tool_eval(self): + result = self.com.tool_eval([self.image_url]) + for res in result: + assert isinstance(res, ComponentOutput) + print(res.role, res.content) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/python/tests/test_v2_text_to_image.py b/python/tests/test_v2_text_to_image.py new file mode 100644 index 000000000..a7850223d --- /dev/null +++ b/python/tests/test_v2_text_to_image.py @@ -0,0 +1,67 @@ +# Copyright (c) 2024 Baidu, Inc. 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 os +import unittest +import appbuilder + +from appbuilder.core.components.v2 import Text2Image +from appbuilder.core.component import ComponentOutput + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") +class TestText2Image(unittest.TestCase): + def setUp(self): + """ + 设置环境变量。 + Args: + None + Returns: + None. + """ + self.com = Text2Image() + + def test_run(self): + """ + 使用原始文本进行单测 + Args: + None + Returns: + None + """ + inp = appbuilder.Message(content={"prompt": "上海的经典风景"}) + out = self.com.run(inp) + self.assertIsNotNone(out) + self.assertIsInstance(out, appbuilder.Message) + + def test_tool_eval(self): + """ + 测试 tool_eval 方法的正确性。 + + Args: + self: 测试类的实例。 + + Returns: + 无返回值。 + + Raises: + 无异常抛出。 + + """ + result = self.com.tool_eval(query = "上海的经典风景") + for res in result: + self.assertIsInstance(res, ComponentOutput) + print(res) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file From f601c79e60f30e3423fa2e97e5b0011ae0a13fdb Mon Sep 17 00:00:00 2001 From: C9luster <138663536+C9luster@users.noreply.github.com> Date: Thu, 5 Dec 2024 19:16:22 +0800 Subject: [PATCH 56/85] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E6=A3=80=E6=B5=8Baudio=5Fschema=E6=A3=80=E6=B5=8B=E9=80=BB?= =?UTF-8?q?=E8=BE=91=20(#642)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: yinjiaqi --- python/tests/component_schemas.py | 1 - 1 file changed, 1 deletion(-) diff --git a/python/tests/component_schemas.py b/python/tests/component_schemas.py index e2e117024..d3955541b 100644 --- a/python/tests/component_schemas.py +++ b/python/tests/component_schemas.py @@ -239,7 +239,6 @@ "type": "string" }, "byte": { - "type": "string", "format": "bytes" } }, From 1298f119f29d349eff642a3470ed3c01e7b0834e Mon Sep 17 00:00:00 2001 From: C9luster <138663536+C9luster@users.noreply.github.com> Date: Thu, 5 Dec 2024 21:17:52 +0800 Subject: [PATCH 57/85] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=BB=84=E4=BB=B6manif?= =?UTF-8?q?ests=20BUG=20(#644)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 更新组件检测audio_schema检测逻辑 * 修复Translation更改了manifests的BUG * update * update * update --------- Co-authored-by: yinjiaqi --- python/core/components/v2/general_ocr/component.py | 7 +++++-- python/core/components/v2/translate/component.py | 3 +-- python/tests/test_v2_general_ocr.py | 3 +-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/python/core/components/v2/general_ocr/component.py b/python/core/components/v2/general_ocr/component.py index cb43a7d40..17dcc2e80 100644 --- a/python/core/components/v2/general_ocr/component.py +++ b/python/core/components/v2/general_ocr/component.py @@ -160,8 +160,8 @@ def _check_service_error(request_id: str, data: dict): @components_run_stream_trace def tool_eval( self, - img_name: str, - img_url: str, + img_name: str = '', + img_url: str = '', **kwargs ): """ @@ -182,6 +182,9 @@ def tool_eval( InvalidRequestArgumentError: 如果请求格式错误或文件URL不存在,将抛出此异常。 """ + if not img_name and not img_url: + raise ValueError( + "request format error, one of image or url or must pdf_file or ofd_file be set") traceid = kwargs.get("_sys_traceid") language_type = kwargs.get("language_type", 'CHN_ENG') if not img_url: diff --git a/python/core/components/v2/translate/component.py b/python/core/components/v2/translate/component.py index cb55bec75..abe7c75cb 100644 --- a/python/core/components/v2/translate/component.py +++ b/python/core/components/v2/translate/component.py @@ -76,8 +76,7 @@ class Translation(Component): } }, "required": [ - "q", - "to_lang" + "q" ] } } diff --git a/python/tests/test_v2_general_ocr.py b/python/tests/test_v2_general_ocr.py index 5041295cb..4603fa2ce 100644 --- a/python/tests/test_v2_general_ocr.py +++ b/python/tests/test_v2_general_ocr.py @@ -39,7 +39,6 @@ def test_tool_eval(self): "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-" \ "11T10%3A59%3A17Z%2F-1%2Fhost%2F081bf7bcccbda5207c82a4de074628b04ae" \ "857a27513734d765495f89ffa5f73" - img_name = "test_img.jpg" result = self.com.tool_eval(img_url=img_url, img_name="") for res in result: assert isinstance(res, ComponentOutput) @@ -47,7 +46,7 @@ def test_tool_eval(self): def test_error_tool_eval(self): result = self.com.tool_eval(img_url='', img_name='') - with self.assertRaises(InvalidRequestArgumentError): + with self.assertRaises(ValueError): list(result) result = self.com.tool_eval(img_url='', img_name='test.jpg') From 91e9f5081813bde89eefb3c7a1412fb87818f5b3 Mon Sep 17 00:00:00 2001 From: C9luster <138663536+C9luster@users.noreply.github.com> Date: Fri, 6 Dec 2024 12:05:40 +0800 Subject: [PATCH 58/85] =?UTF-8?q?=E6=9B=B4=E6=96=B0general=5Focr=E7=BB=84?= =?UTF-8?q?=E4=BB=B6manifests=E5=AE=9A=E4=B9=89=20(#647)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * update * update * 更新GeneralOCR组件manifests * update --------- Co-authored-by: yinjiaqi --- python/core/components/general_ocr/component.py | 9 +++++++++ python/core/components/v2/general_ocr/component.py | 13 +++++++++++-- python/tests/test_general_ocr.py | 9 +++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/python/core/components/general_ocr/component.py b/python/core/components/general_ocr/component.py index 7c2af9ff7..bf0c1265b 100644 --- a/python/core/components/general_ocr/component.py +++ b/python/core/components/general_ocr/component.py @@ -65,6 +65,15 @@ class GeneralOCR(Component): "type": "string", "description": "待识别图片的文件名,用于生成图片url" }, + "language_type": { + "type": "string", + "description": "识别语言类型,'CHN_ENG'为中英文混合,'ENG'为英文, 'JAP'为日语,'KOR'为韩语,'FRE'为法语,'SPA'为西班牙语,'POR'为葡萄牙语," + "'GER'为德语,'ITA'为意大利语,'RUS'为俄语,'DAN'为丹麦语,'DUT'为荷兰语,'MAL'为马来语,'SWE'为瑞典语,'IND'为印尼语,'POL'为波兰语,'ROM'为罗马尼亚语," + "'TUR'为土耳其语,'GRE'为希腊语,'HUN'为匈牙利语,'THA'为泰语,'VIE'为越南语,'ARA'为阿拉伯语,'HIN'为印地语,默认为'CHN_ENG'", + "enum": ['CHN_ENG', 'ENG', 'JAP', 'KOR', 'FRE', 'SPA', 'POR', 'GER', 'ITA', + 'RUS', 'DAN', 'DUT', 'MAL', 'SWE', 'IND', 'POL', 'ROM', 'TUR', + 'GRE', 'HUN', 'THA', 'VIE', 'ARA', 'HIN'], + }, }, "anyOf": [ { diff --git a/python/core/components/v2/general_ocr/component.py b/python/core/components/v2/general_ocr/component.py index 17dcc2e80..9afd161ee 100644 --- a/python/core/components/v2/general_ocr/component.py +++ b/python/core/components/v2/general_ocr/component.py @@ -62,11 +62,20 @@ class GeneralOCR(Component): "type": "string", "description": "待识别图片的文件名,用于生成图片url" }, + "language_type": { + "type": "string", + "description": "识别语言类型,'CHN_ENG'为中英文混合,'ENG'为英文, 'JAP'为日语,'KOR'为韩语,'FRE'为法语,'SPA'为西班牙语,'POR'为葡萄牙语," + "'GER'为德语,'ITA'为意大利语,'RUS'为俄语,'DAN'为丹麦语,'DUT'为荷兰语,'MAL'为马来语,'SWE'为瑞典语,'IND'为印尼语,'POL'为波兰语,'ROM'为罗马尼亚语," + "'TUR'为土耳其语,'GRE'为希腊语,'HUN'为匈牙利语,'THA'为泰语,'VIE'为越南语,'ARA'为阿拉伯语,'HIN'为印地语,默认为'CHN_ENG'", + "enum": ['CHN_ENG', 'ENG', 'JAP', 'KOR', 'FRE', 'SPA', 'POR', 'GER', 'ITA', + 'RUS', 'DAN', 'DUT', 'MAL', 'SWE', 'IND', 'POL', 'ROM', 'TUR', + 'GRE', 'HUN', 'THA', 'VIE', 'ARA', 'HIN'], + }, }, "anyOf": [ { "required": [ - "img_url" + "img_url", ] }, { @@ -162,6 +171,7 @@ def tool_eval( self, img_name: str = '', img_url: str = '', + language_type: str = 'CHN_ENG', **kwargs ): """ @@ -186,7 +196,6 @@ def tool_eval( raise ValueError( "request format error, one of image or url or must pdf_file or ofd_file be set") traceid = kwargs.get("_sys_traceid") - language_type = kwargs.get("language_type", 'CHN_ENG') if not img_url: file_urls = kwargs.get("_sys_file_urls", {}) img_path = img_name diff --git a/python/tests/test_general_ocr.py b/python/tests/test_general_ocr.py index e85c4bf2b..b1e0d761c 100644 --- a/python/tests/test_general_ocr.py +++ b/python/tests/test_general_ocr.py @@ -148,6 +148,15 @@ def test_tool_eval_invalid(self): result = self.general_ocr.tool_eval(name="general_ocr", streaming=True) next(result) + def test_new_tool_eval(self): + img_url = "https://bj.bcebos.com/v1/appbuilder/general_ocr_test.png?" \ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-" \ + "11T10%3A59%3A17Z%2F-1%2Fhost%2F081bf7bcccbda5207c82a4de074628b04ae" \ + "857a27513734d765495f89ffa5f73" + result = self.general_ocr.tool_eval(img_url=img_url, language_type='CHN_ENG', name="general_ocr", streaming=True,) + for res in result: + print(res) + if __name__ == '__main__': unittest.main() From 5c8bb492725c6e3d4d69d8fc544fb6076a5182f6 Mon Sep 17 00:00:00 2001 From: Chengmo Date: Fri, 6 Dec 2024 14:07:19 +0800 Subject: [PATCH 59/85] Update TreeMind&PlantRec (#645) update plant rec --- python/core/components/v2/__init__.py | 4 +- .../components/v2/plant_recognize/__init__.py | 15 ++ .../v2/plant_recognize/component.py | 222 ++++++++++++++++++ .../core/components/v2/tree_mind/__init__.py | 14 ++ .../core/components/v2/tree_mind/component.py | 153 ++++++++++++ python/core/components/v2/tree_mind/model.py | 45 ++++ python/tests/component_tool_eval_cases.py | 29 +++ python/tests/test_v2_plant_recognize.py | 101 ++++++++ python/tests/test_v2_treemind.py | 63 +++++ 9 files changed, 645 insertions(+), 1 deletion(-) create mode 100644 python/core/components/v2/plant_recognize/__init__.py create mode 100644 python/core/components/v2/plant_recognize/component.py create mode 100644 python/core/components/v2/tree_mind/__init__.py create mode 100644 python/core/components/v2/tree_mind/component.py create mode 100644 python/core/components/v2/tree_mind/model.py create mode 100644 python/tests/test_v2_plant_recognize.py create mode 100644 python/tests/test_v2_treemind.py diff --git a/python/core/components/v2/__init__.py b/python/core/components/v2/__init__.py index 35f65bf11..6664963e0 100644 --- a/python/core/components/v2/__init__.py +++ b/python/core/components/v2/__init__.py @@ -24,6 +24,7 @@ from .table_ocr.component import TableOCR from .text_to_image.component import Text2Image from .llms.style_writing.component import StyleWriting +from .tree_mind.component import TreeMind __V2_COMPONENTS__ = [ "AnimalRecognition", @@ -37,5 +38,6 @@ "MixCardOCR", "TableOCR", "Text2Image", - "StyleWriting" + "StyleWriting", + "TreeMind" ] # NOQA \ No newline at end of file diff --git a/python/core/components/v2/plant_recognize/__init__.py b/python/core/components/v2/plant_recognize/__init__.py new file mode 100644 index 000000000..fc3a1a2ea --- /dev/null +++ b/python/core/components/v2/plant_recognize/__init__.py @@ -0,0 +1,15 @@ +# Copyright (c) 2023 Baidu, Inc. 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. + + diff --git a/python/core/components/v2/plant_recognize/component.py b/python/core/components/v2/plant_recognize/component.py new file mode 100644 index 000000000..503943454 --- /dev/null +++ b/python/core/components/v2/plant_recognize/component.py @@ -0,0 +1,222 @@ +# Copyright (c) 2023 Baidu, Inc. 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. + +r"""植物识别组件""" +import base64 + +from appbuilder.core.component import Component +from appbuilder.core.message import Message +from appbuilder.core._client import HTTPClient +from appbuilder.core._exception import AppBuilderServerException +from appbuilder.core.components.plant_recognize.model import * +from typing import Generator, Union +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace +from appbuilder.core.components.plant_recognize.model import TOP_NUM, BAIKE_NUM + + + +class PlantRecognition(Component): + r""" + 植物识别组件,即对于输入的一张图片(可正常解码,且长宽比适宜),输出图片中的植物识别结果 + + Examples: + + .. code-block:: python + + import os + import requests + import appbuilder + + # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 + os.environ["GATEWAY_URL"] = "..." + os.environ["APPBUILDER_TOKEN"] = "..." + image_url = "https://bj.bcebos.com/v1/appbuilder/palnt_recognize_test.jpg?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-23T09%3A51%3A03Z%2F-1%2Fhost%2Faa2217067f78f0236c8262cdd89a4b4f4b2188d971ca547c53d01742af4a2cbe" + + # 从BOS存储读取样例文件 + raw_image = requests.get(image_url).content + inp = appbuilder.Message(content={"raw_image": raw_image}) + # inp = Message(content={"url": image_url}) + + # 运行植物识别 + plant_recognize = appbuilder.PlantRecognition() + out = plant_recognize.run(inp) + # 打印识别结果 + print(out.content) + """ + name = "plant_rec" + version = "v1" + manifests = [ + { + "name": "plant_rec", + "description": "用于识别图片中植物类别", + "parameters": { + "type": "object", + "properties": { + "img_name": { + "type": "string", + "description": "待识别图片的文件名" + }, + "img_url": { + "type": "string", + "description": "待识别图片的url" + } + }, + "anyOf": [ + { + "required": [ + "img_name" + ] + }, + { + "required": [ + "img_url" + ] + } + ] + } + } + ] + + @HTTPClient.check_param + @components_run_trace + def run(self, message: Message, timeout: float = None, retry: int = 0) -> Message: + """ + 输入图片并识别其中的植物 + + Args: + message (Message): 输入图片或图片url下载地址用于执行识别操作. 举例: Message(content={"raw_image": b"..."}) + 或 Message(content={"url": "https://image/download/uel"}). + timeout (float, optional): HTTP超时时间,默认为None + retry (int, optional): HTTP重试次数,默认为0 + + Returns: + Message: 模型识别结果 + """ + inp = PlantRecognitionInMsg(**message.content) + request = PlantRecognitionRequest() + if inp.url: + request.url = inp.url + if inp.raw_image: + request.image = base64.b64encode(inp.raw_image) + request.top_num = 5 + request.baike_num = 0 + response = self.__recognize(request, timeout, retry) + plant_score_list = [] + [plant_score_list.append(PlantScore(name=plant.name, score=plant.score)) for plant in response.result] + out = PlantRecognitionOutMsg(plant_score_list=plant_score_list) + return Message(content=out.model_dump()) + + def __recognize( + self, + request: PlantRecognitionRequest, + timeout: float = None, + retry: int = 0, + request_id: str = None, + ) -> PlantRecognitionResponse: + r"""调用底层接口植物识别 + + 参数: + request (obj: `PlantRecognitionRequest`) : 植物识别输入参数 + + 返回: + response (obj: `PlantRecognitionResponse`): 植物识别返回结果 + """ + if not request.image and not request.url: + raise ValueError("request format error, one of image or url must be set") + data = PlantRecognitionRequest.to_dict(request) + if retry != self.http_client.retry.total: + self.http_client.retry.total = retry + headers = self.http_client.auth_header(request_id) + headers['content-type'] = 'application/x-www-form-urlencoded' + url = self.http_client.service_url("/v1/bce/aip/image-classify/v1/plant") + response = self.http_client.session.post(url, data=data, timeout=timeout, headers=headers) + self.http_client.check_response_header(response) + data = response.json() + self.http_client.check_response_json(data) + request_id = self.http_client.response_request_id(response) + self.__class__.__check_service_error(request_id, data) + return PlantRecognitionResponse(data, request_id=request_id) + + @components_run_stream_trace + def tool_eval( + self, + img_name: str = "", + img_url: str = "", + **kwargs, + ) -> Union[Generator[str, None, None], str]: + """ + 用于工具的执行,通过调用底层接口进行植物识别 + + Args: + name (str): 工具名 + streaming (bool): 是否流式返回 + origin_query (str): 用户原始query + **kwargs: 工具调用的额外关键字参数 + + Returns: + Union[Generator[str, None, None], str]: 植物识别结果,包括识别出的植物类别和相应的置信度信息 + """ + traceid = kwargs.get("_sys_traceid", "") + file_urls = kwargs.get("_sys_file_urls", {}) + rec_res = self._recognize_w_post_process(img_name, img_url, file_urls, request_id=traceid) + + rec_res = self.create_output( + type="text", + text=rec_res, + ) + yield rec_res + + def _recognize_w_post_process(self, img_name, img_url, file_urls, request_id=None): + r"""调底层接口对图片或图片url进行植物识别,并返回类别及其置信度 + 参数: + img_name (str): 图片文件名 + img_url (str): 图片url + file_urls (dict): 文件名与对应文件url的映射 + 返回: + str: 植物识别结果,包括识别出的动物类别和相应的置信度信息 + """ + req = PlantRecognitionRequest() + if img_name in file_urls: + req.url = file_urls[img_name] + if img_url: + if img_url in file_urls: + img_url = file_urls[img_url] + req.url = img_url + req.top_num = TOP_NUM + req.baike_num = BAIKE_NUM + result = self.__recognize(req, request_id=request_id) + result_dict = proto.Message.to_dict(result) + rec_res = "模型识别结果为:\n" + for rec_info in result_dict['result']: + rec_res += "类别: {} 置信度: {}\n".format(rec_info['name'], rec_info['score']) + return rec_res + + @staticmethod + def __check_service_error(request_id: str, data: dict): + r"""个性化服务response参数检查 + + 参数: + request (dict) : 地标识别body返回 + 返回: + 无 + """ + + if "error_code" in data or "error_msg" in data: + raise AppBuilderServerException( + request_id=request_id, + service_err_code=data.get("error_code"), + service_err_message=data.get("error_msg") + ) + + diff --git a/python/core/components/v2/tree_mind/__init__.py b/python/core/components/v2/tree_mind/__init__.py new file mode 100644 index 000000000..3e12d0913 --- /dev/null +++ b/python/core/components/v2/tree_mind/__init__.py @@ -0,0 +1,14 @@ +# Copyright (c) 2023 Baidu, Inc. All Rights Reserved. +# 版权所有 (c) 2023 百度公司。保留所有权利。 +# +# 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. \ No newline at end of file diff --git a/python/core/components/v2/tree_mind/component.py b/python/core/components/v2/tree_mind/component.py new file mode 100644 index 000000000..17b81c0b8 --- /dev/null +++ b/python/core/components/v2/tree_mind/component.py @@ -0,0 +1,153 @@ +# Copyright (c) 2023 Baidu, Inc. 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. + +r"""树图工具""" + +import json +from typing import Dict, List, Optional, Any +from appbuilder.core.message import Message +from appbuilder.core._client import HTTPClient +from appbuilder.core._exception import * +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace +from appbuilder.core.components.v2.tree_mind.model import TreeMindRequest, TreeMindResponse + + +from appbuilder.core.component import Component + +class TreeMind(Component): + r""" + 树图工具,提供智能思维导图制作工具和丰富的模板,支持脑图、逻辑图、树形图、鱼骨图、组织架构图、时间轴、时间线等多种专业格式。 + .. code-block:: python + + import os + import requests + import appbuilder + # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 + os.environ["GATEWAY_URL"] = "..." + os.environ["APPBUILDER_TOKEN"] = "..." + treemind = appbuilder.TreeMind() + resp = treemind.run(appbuilder.Message("生成一份年度总结的思维导图"), to_lang="en") + print(resp.content) + # 输出 {'from_lang':'zh', 'to_lang':'en', 'trans_result':[{'src':'你好','dst':'hello'},{'src':'中国','dst':'China'}]} + """ + + name = "tree_mind" + version = "v1" + manifests = [ + { + "name": "tree_mind", + "description": "根据用户输入的信息,生成详细智能思维导图、脑图、逻辑图、树形图、鱼骨图、组织架构图、时间轴、时间线等多种专业格式思维导图", + "parameters": { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "用户想要生成思维导图的内容" + }, + }, + "required": [ + "query" + ] + } + } + ] + + def _post(self, query, **kwargs): + if query is None or query == "": + raise InvalidRequestArgumentError("query is empty!" ) + request = TreeMindRequest(query_text=query) + headers = self.http_client.auth_header(kwargs.get("_sys_traceid")) + + headers['Content-Type'] = 'application/json' + tree_mind_url = self.http_client.service_url("/v1/component/component/query_mind_open") + + payload = TreeMindRequest.model_dump(request) + + response = self.http_client.session.post(tree_mind_url, headers=headers, json=payload) + self.http_client.check_response_header(response) + data = response.text + treemind_dict = json.loads(data.split("data:")[-1]) + treemind_response = TreeMindResponse(**treemind_dict) + jump_link = treemind_response.info.downloadInfo.fileInfo.jumpLink + img_link = treemind_response.info.downloadInfo.fileInfo.pic + return img_link, jump_link + + @components_run_stream_trace + def tool_eval( + self, + query, + **kwargs, + ): + r"""调用树图查询接口 + Args: + query (string): 用户想要生成思维导图的内容 + Returns: + dict: 返回生成的思维导图的图片链接和跳转链接 + """ + + img_link, jump_link = self._post(query, **kwargs) + + inst = "你必须遵循指令,输出无需总结,只需要将,“原样输出内容”对应的内容原样输出即可:\n" + img_res = f"原样输出内容:![图片url]({img_link})\n" + jump_res = f"{query}的思维导图已经为您生成好了,您可以通过这个链接编辑:编辑链接:{jump_link}。" + end_talk = "如果您觉得这个思维导图还不够完美,或者您的想法需要更自由地表达,点击编辑按钮,对思维导图变形、变色、变内容、甚至可以添加新的元素,快来试试吧!" + result = inst + img_res + jump_res + end_talk + + llm_result = self.create_output( + type="text", + text=result, + visible_scope='llm', + name="text" + ) + yield llm_result + + img_link_result = self.create_output( + type="image", + text={ + "url": img_link + }, + visible_scope='all', + name="img_link_url" + ) + yield img_link_result + + jump_link_result = self.create_output( + type="urls", + text={ + "url": jump_link + }, + visible_scope='all', + name="jump_link_url" + ) + yield jump_link_result + + + @HTTPClient.check_param + @components_run_trace + def run(self, message: Message, **kwargs) -> Message: + """运行组件 + Args: + message (Message): 消息对象 + Returns: + Message: 返回消息对象 + """ + query = message.content + img_link, jump_link = self._post(query, **kwargs) + + result = { + "result": "思维导图已经为您生成好了,您可以点击'img_link'对应的链接查看,如果您觉得这个思维导图还不够完美,或者您的想法需要更自由地表达,点击'edit_link'对应的链接,对思维导图变形、变色、变内容、甚至可以添加新的元素", + "img_link": img_link, + "edit_link": jump_link + } + return Message(content=result) \ No newline at end of file diff --git a/python/core/components/v2/tree_mind/model.py b/python/core/components/v2/tree_mind/model.py new file mode 100644 index 000000000..d0fa54986 --- /dev/null +++ b/python/core/components/v2/tree_mind/model.py @@ -0,0 +1,45 @@ +# Copyright (c) 2023 Baidu, Inc. 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. +""" +树图model +""" + +import proto +from typing import List +from pydantic import BaseModel + + +class TreeMindRequest(BaseModel): + query_text:str + +class FileInfo(BaseModel): + jumpLink: str + jumpText: str + pic: str + +class DownloadInfo(BaseModel): + fileInfo: FileInfo + endDesc: str + +class Info(BaseModel): + downloadInfo: DownloadInfo + +class TreeMindResponse(BaseModel): + errCode: str + errMsg: str + time_diff: int + info: Info + + + diff --git a/python/tests/component_tool_eval_cases.py b/python/tests/component_tool_eval_cases.py index eed5c5f2a..af0fc04c9 100644 --- a/python/tests/component_tool_eval_cases.py +++ b/python/tests/component_tool_eval_cases.py @@ -246,6 +246,33 @@ def schemas(self): def outputs(self): return {"text": ["足球"]} + +class TreeMindCase(Case): + def inputs(self): + return { + "query": "生成一份年度总结的思维导图" + } + + def schemas(self): + return [text_schema, url_schema, image_schema] + +class PlantRecognitionCase(Case): + def inputs(self): + img_url = "https://bj.bcebos.com/v1/appbuilder/animal_recognize_test.png?" \ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-24T" \ + "12%3A19%3A16Z%2F-1%2Fhost%2F411bad53034fa8f9c6edbe5c4909d76ecf6fad68" \ + "62cf937c03f8c5260d51c6ae" + img_name = "test_img.jpg" + return { + "img_url": img_url, + "img_name": img_name + } + + def schemas(self): + return [text_schema] + + def outputs(self): + return {"text": ["非植物"]} component_tool_eval_cases = { "AnimalRecognition": AnimalRecognitionCase, @@ -262,4 +289,6 @@ def outputs(self): "TableOCR": TableOCRCase, "Text2Image": Text2ImageCase, "StyleWriting": StyleWritingCase, + "TreeMind": TreeMindCase, + "PlantRecognition": PlantRecognitionCase, } \ No newline at end of file diff --git a/python/tests/test_v2_plant_recognize.py b/python/tests/test_v2_plant_recognize.py new file mode 100644 index 000000000..d3a177de6 --- /dev/null +++ b/python/tests/test_v2_plant_recognize.py @@ -0,0 +1,101 @@ +# Copyright (c) 2023 Baidu, Inc. 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 os +import unittest +import requests +import appbuilder +from appbuilder.core.message import Message +from appbuilder.core.component import Component +from appbuilder.core.component import ComponentOutput +from appbuilder.core.components.v2.plant_recognize.component import PlantRecognition + +# @unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") +class TestPlantRecognition(unittest.TestCase): + + def setUp(self): + """ + 设置环境变量 + Args: + None. + Returns: + None. + """ + # 从BOS存储读取样例文件 + self.image_url = ("https://bj.bcebos.com/v1/appbuilder/" + "palnt_recognize_test.jpg?authorization=" + "bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%" + "2F2024-01-23T09%3A51%3A03Z%2F-1%2Fhost%2" + "Faa2217067f78f0236c8262cdd89a4b4f4b2188" + "d971ca547c53d01742af4a2cbe") + self.raw_image = requests.get(self.image_url).content + self.plant_recognize = PlantRecognition() + + # 输入参数为一张图片 + + def test_run_with_image_url(self): + """ + 使用图片url进行单测 + + Args: + None + + Returns: + None + + """ + # Create message with raw_image + inp = Message(content={"url": self.image_url}) + msg = self.plant_recognize.run(inp) + self.assertIsNotNone(msg.content) + + def test_run_with_raw_image(self): + """ + 使用原始图片进行单测 + + Args: + None + + Returns: + None + + """ + # Create message with raw_image + inp = Message(content={"raw_image": self.raw_image}) + msg = self.plant_recognize.run(inp) + self.assertIsNotNone(msg.content) + + def test_tool_eval_invalid(self): + """测试 tool 方法对无效请求的处理。""" + with self.assertRaises(ValueError): + result = self.plant_recognize.tool_eval(name="plant_recognition", streaming=True, + origin_query="") + next(result) + + def test_tool_eval(self): + """测试 tool 方法的处理。""" + img_url = "https://bj.bcebos.com/v1/appbuilder/animal_recognize_test.png?" \ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-24T" \ + "12%3A19%3A16Z%2F-1%2Fhost%2F411bad53034fa8f9c6edbe5c4909d76ecf6fad68" \ + "62cf937c03f8c5260d51c6ae" + img_name = "test_img.jpg" + result = self.plant_recognize.tool_eval( + img_name=img_name, img_url=img_url) + for r in result: + print(r) + self.assertIsInstance(r, ComponentOutput) + + +if __name__ == '__main__': + unittest.main() diff --git a/python/tests/test_v2_treemind.py b/python/tests/test_v2_treemind.py new file mode 100644 index 000000000..2adf9f146 --- /dev/null +++ b/python/tests/test_v2_treemind.py @@ -0,0 +1,63 @@ +# Copyright (c) 2024 Baidu, Inc. 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 os +import unittest +from appbuilder.core.message import Message +from appbuilder.core.component import Component +from appbuilder.core.component import ComponentOutput +from appbuilder.core.components.v2 import TreeMind + +class TestTreeMindComponent(unittest.TestCase): + def setUp(self): + """ + 初始化测试用例,设置环境变量和网关URL。 + 如果没有设置CAR_EXPERT_TOKEN环境变量,则使用空字符串。 + Args: + None. + Returns: + None. + """ + self.tm = TreeMind() + self.query = "生成一份年度总结的思维导图" + + def test_treemind_component_tool_eval(self): + """测试tool_eval方法的返回值是否正确 + """ + import time + time.sleep(1) + result = self.tm.tool_eval(query=self.query) + self.assertIsNotNone(result) + for r in result: + self.assertIsNotNone(r) + + def test_run_with_invalid_input(self): + """测试run函数在传入无效输入的情况下的行为。 + """ + message = Message(content={}) + with self.assertRaises(ValueError): + self.tm.run(message) + + def test_tool_eval_invalid(self): + """测试 tool 方法传入无效输入的情况下的行为""" + with self.assertRaises(TypeError): + result = self.tm.tool_eval(name="treemind", streaming=True, origin_query="") + next(result) + + def test_tool_eval(self): + result = self.tm.tool_eval(query=self.query) + for r in result: + self.assertIsInstance(r, ComponentOutput) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file From 1c423e6019435a981a5fd6860857745a2e3f307d Mon Sep 17 00:00:00 2001 From: userpj Date: Tue, 10 Dec 2024 11:06:52 +0800 Subject: [PATCH 60/85] =?UTF-8?q?=E5=AE=9E=E6=97=B6=E9=80=9A=E8=AF=9D?= =?UTF-8?q?=E5=8A=9F=E8=83=BDcookbook=20(#632)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 语音通话功能cookbook --- .../advanced_application/agent_speech.ipynb | 216 ++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 cookbooks/advanced_application/agent_speech.ipynb diff --git a/cookbooks/advanced_application/agent_speech.ipynb b/cookbooks/advanced_application/agent_speech.ipynb new file mode 100644 index 000000000..ba9019d19 --- /dev/null +++ b/cookbooks/advanced_application/agent_speech.ipynb @@ -0,0 +1,216 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 实时语音对话能力\n", + "**注意⚠️:实时语音功能目前处于内测阶段,使用过程中有任何问题,欢迎提issue或微信群反馈~**\n", + "\n", + "## 目标\n", + "实现一个实时语音对话功能,用户可以参考cookbook代码,通过AppBuilder-SDK将实时语音功能很好地融入自己的平台、应用中。\n", + "\n", + "## 实现原理\n", + "通过循环不断处理用户的语音,将语音转文本,然后进行对话,最后将对话结果通过TTS进行播报。。\n", + "* 使用大模型的 ASR 进行语音转文本。\n", + "* 使用用户自己创建的Agent进行对话,适配用户的应用场景,并具有上下文理解能力。\n", + "* 使用大模型的 TTS 进行文本转语音并进行播报。\n", + "\n", + "## 前置条件\n", + "* pip安装pyaudio、webrtcvad依赖包\n", + "* 给程序开放麦克风权限\n", + "* 创建好自己的Agent应用\n", + "\n", + "## 示例代码" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Copyright (c) 2024 Baidu, Inc. All Rights Reserved.\n", + "#\n", + "# Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "#\n", + "# http://www.apache.org/licenses/LICENSE-2.0\n", + "#\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License.\n", + "\n", + "import os\n", + "import time\n", + "import wave\n", + "import sys\n", + "import pyaudio\n", + "import webrtcvad\n", + "import appbuilder\n", + "import re\n", + "\n", + "# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5\n", + "# 设置环境变量\n", + "os.environ[\"APPBUILDER_TOKEN\"] = (\n", + " \"...\"\n", + ")\n", + "# 已发布AppBuilder应用的ID\n", + "app_id = \"...\"\n", + "appbuilder.logger.setLoglevel(\"ERROR\")\n", + "\n", + "CHUNK = 1024\n", + "FORMAT = pyaudio.paInt16\n", + "CHANNELS = 1 if sys.platform == \"darwin\" else 2\n", + "RATE = 16000\n", + "DURATION = 30 # ms\n", + "CHUNK = RATE // 1000 * DURATION\n", + "\n", + "\n", + "class Chatbot:\n", + " def __init__(self):\n", + " self.p = pyaudio.PyAudio()\n", + " self.tts = appbuilder.TTS()\n", + " self.asr = appbuilder.ASR()\n", + " self.agent = appbuilder.AppBuilderClient(app_id)\n", + " self.conversation_id = self.agent.create_conversation()\n", + "\n", + " def run(self):\n", + " self.run_tts_and_play_audio(\n", + " \"我是你的专属聊天机器人,如果你有什么问题,可以直接问我\"\n", + " )\n", + " while True:\n", + " # Record\n", + " audio_path = \"output.wav\"\n", + " print(\"开始记录音频...\")\n", + " if self.record_audio(audio_path) < 1000:\n", + " time.sleep(1)\n", + " continue\n", + " print(\"音频记录结束\")\n", + " \n", + " # ASR\n", + " print(\"开始执行ASR...\")\n", + " query = self.run_asr(audio_path)\n", + " print(\"结束执行ASR\")\n", + "\n", + " # Agent\n", + " print(\"query: \", query)\n", + " if len(query) == 0:\n", + " continue\n", + " answer = self.run_agent(query)\n", + " results = re.findall(r\"(https?://[^\\s]+)\", answer)\n", + " for result in results:\n", + " print(\"链接地址:\", result)\n", + " answer = answer.replace(result, \"\")\n", + " print(\"answer:\", answer)\n", + " \n", + " # TTS\n", + " print(\"开始执行TTS并播报...\")\n", + " self.run_tts_and_play_audio(answer)\n", + " print(\"结束TTS并播报结束\")\n", + "\n", + " def record_audio(self, path):\n", + " with wave.open(path, \"wb\") as wf:\n", + " wf.setnchannels(CHANNELS)\n", + " wf.setsampwidth(self.p.get_sample_size(FORMAT))\n", + " wf.setframerate(RATE)\n", + " stream = self.p.open(\n", + " format=FORMAT, channels=CHANNELS, rate=RATE, input=True\n", + " )\n", + " vad = webrtcvad.Vad(1)\n", + " not_speech_times = 0\n", + " speech_times = 0\n", + " total_times = 0\n", + " start_up_times = 33 * 5 # 初始时间设置为5秒\n", + " history_speech_times = 0\n", + " while True:\n", + " if history_speech_times > 33 * 10:\n", + " break\n", + " data = stream.read(CHUNK, False)\n", + " if vad.is_speech(data, RATE):\n", + " speech_times += 1\n", + " wf.writeframes(data)\n", + " else:\n", + " not_speech_times += 1\n", + " total_times += 1\n", + " if total_times >= start_up_times:\n", + " history_speech_times += speech_times\n", + " # 模拟滑窗重新开始计数\n", + " if float(not_speech_times) / float(total_times) > 0.7:\n", + " break\n", + " not_speech_times = 0\n", + " speech_times = 0\n", + " total_times = 0\n", + " start_up_times = start_up_times / 2\n", + " if start_up_times < 33:\n", + " start_up_times = 33\n", + " stream.close()\n", + " return history_speech_times * DURATION\n", + "\n", + " def run_tts_and_play_audio(self, text: str):\n", + " msg = self.tts.run(\n", + " appbuilder.Message(content={\"text\": text}),\n", + " audio_type=\"pcm\",\n", + " model=\"paddlespeech-tts\",\n", + " stream=True,\n", + " )\n", + " stream = self.p.open(\n", + " format=self.p.get_format_from_width(2),\n", + " channels=1,\n", + " rate=24000,\n", + " output=True,\n", + " frames_per_buffer=2048,\n", + " )\n", + " for pcm in msg.content:\n", + " stream.write(pcm)\n", + " stream.stop_stream()\n", + " stream.close()\n", + "\n", + " def run_asr(self, audio_path: str):\n", + " with open(audio_path, \"rb\") as f:\n", + " content_data = {\"audio_format\": \"wav\", \"raw_audio\": f.read(), \"rate\": 16000}\n", + " msg = appbuilder.Message(content_data)\n", + " out = self.asr.run(msg)\n", + " text = out.content[\"result\"][0]\n", + " return text\n", + "\n", + " def run_agent(self, query):\n", + " msg = self.agent.run(self.conversation_id, query, stream=True)\n", + " answer = \"\"\n", + " for content in msg.content:\n", + " answer += content.answer\n", + " return answer\n", + "\n", + "\n", + "if __name__ == \"__main__\":\n", + " chatbot = Chatbot()\n", + " chatbot.run()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**使用方法**\n", + "\n", + "直接运行程序即可。\n", + "\n", + "用户也可以将下面的功能模块替换成自己的其他实现或模型:\n", + "* record_audio: 录音\n", + "* run_asr: 语音识别语音识别\n", + "* run_agent: Agent对话功能\n", + "* run_tts_and_play_audio:回复的语音生成并播报" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 6bee1d0b8259c331920e28b1e06eb25bbbdc5571 Mon Sep 17 00:00:00 2001 From: Chengmo Date: Tue, 10 Dec 2024 11:16:42 +0800 Subject: [PATCH 61/85] add chart base code (#649) --- python/core/component.py | 4 ++-- python/tests/component_schemas.py | 6 +++--- python/tests/test_base_component.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/python/core/component.py b/python/core/component.py index 936a50612..c316b826f 100644 --- a/python/core/component.py +++ b/python/core/component.py @@ -103,8 +103,8 @@ class Image(BaseModel, extra='allow'): class Chart(BaseModel, extra='allow'): - filename: str = Field(default="", description="图表名称") - url: str = Field(default="", description="图表url") + type: str = Field(default="", description="图表类型") + data: str = Field(default="", description="图表数据, json_str格式") class Audio(BaseModel, extra='allow'): diff --git a/python/tests/component_schemas.py b/python/tests/component_schemas.py index d3955541b..58a42779a 100644 --- a/python/tests/component_schemas.py +++ b/python/tests/component_schemas.py @@ -213,14 +213,14 @@ chart_schema["properties"]["text"] = { "type": "object", "properties": { - "filename": { + "type": { "type": "string" }, - "url": { + "data": { "type": "string" } }, - "required": ["filename", "url"] + "required": ["type", "data"] } audio_schema = copy.deepcopy(base_item_schema) diff --git a/python/tests/test_base_component.py b/python/tests/test_base_component.py index 4e605615c..7c60c5dd0 100644 --- a/python/tests/test_base_component.py +++ b/python/tests/test_base_component.py @@ -26,7 +26,7 @@ def test_valid_output_with_dict(self): output4 = self.component.create_output(type="oral_text", text={"info": "你好"}) output5 = self.component.create_output(type="files", text={"filename": "file.txt", "url": "http://www.baidu.com"}) output6 = self.component.create_output(type="image", text={"filename": "file.png", "url": "http://www.baidu.com"}) - output7 = self.component.create_output(type="chart", text={"filename": "file.jpg", "url": "http://www.baidu.com"}) + output7 = self.component.create_output(type="chart", text={"type": "chart", "data": '{"key": "value"}'}) output8 = self.component.create_output(type="audio", text={"filename": "file.mp3", "url": "http://www.baidu.com"}) output9 = self.component.create_output(type="plan", text={"detail": "hello", "steps":[{"name": "1", "arguments": {"query": "a", "chat_history": "world"}}]}) output10 = self.component.create_output(type="function_call", text={"thought": "hello", "name": "AppBuilder", "arguments": {"query": "a", "chat_history": "world"}}) @@ -44,7 +44,7 @@ def test_valid_output_with_dict(self): def test_valid_output_type_with_same_key(self): output1 = self.component.create_output(type="urls", text={"url": "http://www.baidu.com"}) self.assertIsInstance(output1.content[0].text, Urls) - output2 = self.component.create_output(type="chart", text={"filename": "file.jpg", "url": "http://www.baidu.com"}) + output2 = self.component.create_output(type="chart", text={"type": "chart_sheet", "data": '{"key": "value"}'}) self.assertIsInstance(output2.content[0].text, Chart) with self.assertRaises(ValueError): output = self.component.create_output(type="files", text=["http://www.baidu.com"]) From 926311e1789299e59606749599fa6a10285678f6 Mon Sep 17 00:00:00 2001 From: userpj Date: Tue, 10 Dec 2024 13:53:45 +0800 Subject: [PATCH 62/85] =?UTF-8?q?Agent=E5=AF=B9=E8=AF=9D=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E8=BF=BD=E9=97=AE=E5=8A=9F=E8=83=BD=20(#648)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Agent对话增加追问功能 --- .../Platform/Application/appbuilder_client.md | 12 +-- go/appbuilder/agent_builder_test.go | 57 ++++++----- go/appbuilder/app_builder_client_data.go | 13 ++- go/appbuilder/app_builder_client_test.go | 19 ++-- go/appbuilder/dataset_test.go | 12 +-- go/appbuilder/knowledge_base_test.go | 4 - go/appbuilder/rag_test.go | 12 ++- .../model/appbuilderclient/Event.java | 1 + .../model/appbuilderclient/EventContent.java | 1 + .../appbuilder/AppBuilderClientTest.java | 36 ++++++- .../core/console/knowledge_base/data_class.py | 2 +- .../test_appbuilder_client_follow_up_query.py | 97 +++++++++++++++++++ python/tests/test_knowledge_base.py | 1 + 13 files changed, 204 insertions(+), 63 deletions(-) create mode 100644 python/tests/test_appbuilder_client_follow_up_query.py diff --git a/docs/BasisModule/Platform/Application/appbuilder_client.md b/docs/BasisModule/Platform/Application/appbuilder_client.md index cf48bff57..6656b167d 100644 --- a/docs/BasisModule/Platform/Application/appbuilder_client.md +++ b/docs/BasisModule/Platform/Application/appbuilder_client.md @@ -1003,7 +1003,8 @@ class AppBuilderClientDemo { String ToolCallID = ""; while (itor.hasNext()) { AppBuilderClientResult result = itor.next(); - ToolCallID = result.getEvents()[0].getToolCalls()[0].getId(); + Event lastEvent = result.getEvents()[result.getEvents().length - 1]; + ToolCallID = lastEvent.getToolCalls()[lastEvent.getToolCalls().length - 1].getId(); System.out.println(result); } @@ -1406,12 +1407,9 @@ func main() { totalAnswer := "" toolCallID := "" for answer, err := i.Next(); err == nil; answer, err = i.Next() { - totalAnswer = totalAnswer + answer.Answer - for _, ev := range answer.Events { - toolCallID = ev.ToolCalls[0].ID - evJSON, _ := json.Marshal(ev) - fmt.Println(string(evJSON)) - } + totalAnswer += answer.Answer + lastEvent := answer.Events[len(answer.Events)-1] + toolCallID = lastEvent.ToolCalls[len(lastEvent.ToolCalls)-1].ID } i2, err := client.Run(appbuilder.AppBuilderClientRunRequest{ diff --git a/go/appbuilder/agent_builder_test.go b/go/appbuilder/agent_builder_test.go index d09f6b930..2de286271 100644 --- a/go/appbuilder/agent_builder_test.go +++ b/go/appbuilder/agent_builder_test.go @@ -18,62 +18,62 @@ import ( "bytes" "encoding/json" "fmt" - "os" + "io" "net/http" - "io" - "strings" - "testing" + "os" + "strings" + "testing" ) // 模拟返回 400 错误的 HTTP 响应 type MockHTTPClient struct{} func (m *MockHTTPClient) Do(req *http.Request) (*http.Response, error) { - return &http.Response{ - StatusCode: 400, // 非 2xx 状态码 - Body: io.NopCloser(strings.NewReader(`{"error": "Bad Request"}`)), - }, nil + return &http.Response{ + StatusCode: 400, // 非 2xx 状态码 + Body: io.NopCloser(strings.NewReader(`{"error": "Bad Request"}`)), + }, nil } // FaultyHTTPClient 模拟响应读取时发生错误 type FaultyHTTPClient struct{} func (f *FaultyHTTPClient) Do(req *http.Request) (*http.Response, error) { - return &http.Response{ - StatusCode: 200, // 返回成功的状态码 - Body: &FaultyBody{}, // 使用 FaultyBody,模拟读取时出错 - }, nil + return &http.Response{ + StatusCode: 200, // 返回成功的状态码 + Body: &FaultyBody{}, // 使用 FaultyBody,模拟读取时出错 + }, nil } // FaultyBody 模拟响应体读取错误 type FaultyBody struct{} func (f *FaultyBody) Read(p []byte) (n int, err error) { - return 0, fmt.Errorf("simulated read error") // 模拟读取时发生错误 + return 0, fmt.Errorf("simulated read error") // 模拟读取时发生错误 } func (f *FaultyBody) Close() error { - return nil + return nil } // 模拟无效 JSON 响应 type InvalidJSONHTTPClient struct{} func (m *InvalidJSONHTTPClient) Do(req *http.Request) (*http.Response, error) { - return &http.Response{ - StatusCode: 200, - Body: io.NopCloser(strings.NewReader(`{invalid_json}`)), - }, nil + return &http.Response{ + StatusCode: 200, + Body: io.NopCloser(strings.NewReader(`{invalid_json}`)), + }, nil } // 模拟缺少 id 的 JSON 响应 type MissingIDHTTPClient struct{} func (m *MissingIDHTTPClient) Do(req *http.Request) (*http.Response, error) { - return &http.Response{ - StatusCode: 200, // 成功的状态码,但缺少 id 字段 - Body: io.NopCloser(strings.NewReader(`{"message": "Upload successful", "other_field": "value"}`)), // 缺少 id 字段 - }, nil + return &http.Response{ + StatusCode: 200, // 成功的状态码,但缺少 id 字段 + Body: io.NopCloser(strings.NewReader(`{"message": "Upload successful", "other_field": "value"}`)), // 缺少 id 字段 + }, nil } func TestNewAgentBuilderError(t *testing.T) { t.Parallel() // 并发运行 @@ -166,7 +166,6 @@ func TestNewAgentBuilderUploadLocalFileError1(t *testing.T) { // 测试 UploadLocalFile 2: 文件复制错误 - // 测试 UploadLocalFile 4: t.client.Do 错误 agentBuilder.sdkConfig.GatewayURLV2 = "http://192.0.2.1" _, err = agentBuilder.UploadLocalFile("5665", "./files/test.pdf") @@ -181,7 +180,6 @@ func TestNewAgentBuilderUploadLocalFileError1(t *testing.T) { t.Errorf("expected ServiceURLV2 error, got %v", err) } - } func TestNewAgentBuilderUploadLocalFileError2(t *testing.T) { t.Parallel() // 并发运行 @@ -221,7 +219,7 @@ func TestNewAgentBuilderUploadLocalFileError2(t *testing.T) { // 检查 err 是否为空,并且确保返回的错误信息包含 "id" 这个字段 if err == nil || !strings.Contains(err.Error(), "body") || !strings.Contains(err.Error(), "id") { - + } } func TestNewAgentBuilderRunError(t *testing.T) { @@ -338,11 +336,10 @@ func TestNewAgentBuilder(t *testing.T) { default: // 默认是 json.RawMessage detail, ok := ev.Detail.(json.RawMessage) - if !ok { - t.Fatalf("unknown detail type") + if ok { + log("---------------rawMessage------------") + log("%s", string(detail)) } - log("---------------rawMessage------------") - log("%s", string(detail)) } } } @@ -357,7 +354,7 @@ func TestNewAgentBuilder(t *testing.T) { if t.Failed() { t.Logf("%s========== FAIL: %s ==========%s", "\033[31m", t.Name(), "\033[0m") fmt.Println(logBuffer.String()) - } else { // else 紧跟在右大括号后面 + } else { // else 紧跟在右大括号后面 // 测试通过,打印文件名和测试函数名 t.Logf("%s========== OK: %s ==========%s", "\033[32m", t.Name(), "\033[0m") } diff --git a/go/appbuilder/app_builder_client_data.go b/go/appbuilder/app_builder_client_data.go index 7ca8c5ac0..38383aacf 100644 --- a/go/appbuilder/app_builder_client_data.go +++ b/go/appbuilder/app_builder_client_data.go @@ -33,10 +33,12 @@ const ( StatusContentType = "status" ChatflowInterruptContentType = "chatflow_interrupt" PublishMessageContentType = "publish_message" + JsonContentType = "json" ) const ( - ChatflowEventType = "chatflow" + ChatflowEventType = "chatflow" + FollowUpQueryEventType = "FollowUpQuery" ) var TypeToStruct = map[string]reflect.Type{ @@ -50,6 +52,7 @@ var TypeToStruct = map[string]reflect.Type{ StatusContentType: reflect.TypeOf(StatusDetail{}), ChatflowInterruptContentType: reflect.TypeOf(ChatflowInterruptDetail{}), PublishMessageContentType: reflect.TypeOf(PublishMessageDetail{}), + JsonContentType: reflect.TypeOf(JsonDetail{}), } type AppBuilderClientRunRequest struct { @@ -235,6 +238,14 @@ type PublishMessageDetail struct { MessageID string `json:"message_id"` } +type JsonDetail struct { + Json FollowUpQueries `json:"json"` +} + +type FollowUpQueries struct { + FollowUpQueries []string `json:"follow_up_querys"` +} + type DefaultDetail struct { URLS []string `json:"urls"` Files []string `json:"files"` diff --git a/go/appbuilder/app_builder_client_test.go b/go/appbuilder/app_builder_client_test.go index 28141b9e1..48c93d1c0 100644 --- a/go/appbuilder/app_builder_client_test.go +++ b/go/appbuilder/app_builder_client_test.go @@ -381,7 +381,7 @@ func TestNewAppBuilderClient(t *testing.T) { } log("Number of apps: %d", len(apps2.Data)) - appID := "aa8af334-df27-4855-b3d1-0d249c61fc08" + appID := "fb64d96b-f828-4385-ba1d-835298d635a9" client, err := NewAppBuilderClient(appID, config) if err != nil { t.Logf("%s========== FAIL: %s ==========%s", "\033[31m", t.Name(), "\033[0m") @@ -404,9 +404,19 @@ func TestNewAppBuilderClient(t *testing.T) { t.Fatalf("run failed:%v", err) } totalAnswer := "" + // test follow up queries for answer, err := i.Next(); err == nil; answer, err = i.Next() { totalAnswer += answer.Answer for _, ev := range answer.Events { + if ev.ContentType == JsonContentType { + detail := ev.Detail.(JsonDetail) + folllowUpQueries := detail.Json.FollowUpQueries + fmt.Println(folllowUpQueries) + if len(folllowUpQueries[0]) == 0 { + t.Logf("%s========== FAIL: %s ==========%s", "\033[31m", t.Name(), "\033[0m") + t.Fatal("follow up queries is empty") + } + } evJSON, _ := json.Marshal(ev) log(string(evJSON)) } @@ -498,11 +508,8 @@ func TestAppBuilderClientRunWithToolCall(t *testing.T) { toolCallID := "" for answer, err := i.Next(); err == nil; answer, err = i.Next() { totalAnswer += answer.Answer - for _, ev := range answer.Events { - toolCallID = ev.ToolCalls[0].ID - evJSON, _ := json.Marshal(ev) - log(string(evJSON)) - } + lastEvent := answer.Events[len(answer.Events)-1] + toolCallID = lastEvent.ToolCalls[len(lastEvent.ToolCalls)-1].ID } i2, err := client.RunWithToolCall(AppBuilderClientRunRequest{ diff --git a/go/appbuilder/dataset_test.go b/go/appbuilder/dataset_test.go index 8fae3fe86..2f3e18100 100644 --- a/go/appbuilder/dataset_test.go +++ b/go/appbuilder/dataset_test.go @@ -20,6 +20,7 @@ import ( "os" "testing" ) + func TestDatasetError(t *testing.T) { t.Parallel() // 并发运行 // 测试逻辑 @@ -51,7 +52,7 @@ func TestDatasetError(t *testing.T) { dataset.sdkConfig.GatewayURLV2 = GatewayURL _, err = dataset.Create("测试集合") if err == nil { - + } // 测试 UploadLocalFile 5: 模拟读取 body 时发生错误 dataset.client = &FaultyHTTPClient{} @@ -66,7 +67,7 @@ func TestDatasetError(t *testing.T) { //NewDataset测试2 client == nil config.HTTPClient = nil dataset, _ = NewDataset(config) - + } func TestDataset(t *testing.T) { t.Parallel() // 并发运行 @@ -92,9 +93,8 @@ func TestDataset(t *testing.T) { } log("Dataset created with ID: %s", datasetID) - _,err = dataset.BatchUploadLocaleFile("datasetID", []string{"./files/test.pdf", "./files/test2.pdf"}) + _, err = dataset.BatchUploadLocaleFile("datasetID", []string{"./files/test.pdf", "./files/test2.pdf"}) if err != nil { - t.Logf("%s========== FAIL: %s ==========%s", "\033[31m", t.Name(), "\033[0m") } //log("Documents uploaded with ID: %s", documentIDs) @@ -122,8 +122,8 @@ func TestDataset(t *testing.T) { if t.Failed() { t.Logf("%s========== FAIL: %s ==========%s", "\033[31m", t.Name(), "\033[0m") fmt.Println(logBuffer.String()) - } else { // else 紧跟在右大括号后面 + } else { // else 紧跟在右大括号后面 // 测试通过,打印文件名和测试函数名 t.Logf("%s========== OK: %s ==========%s", "\033[32m", t.Name(), "\033[0m") } -} \ No newline at end of file +} diff --git a/go/appbuilder/knowledge_base_test.go b/go/appbuilder/knowledge_base_test.go index f531b9163..825c3d209 100644 --- a/go/appbuilder/knowledge_base_test.go +++ b/go/appbuilder/knowledge_base_test.go @@ -249,7 +249,6 @@ func TestAddDocumentError(t *testing.T) { func TestCreateKnowledgeBaseError(t *testing.T) { t.Parallel() // 并发运行 os.Setenv("APPBUILDER_LOGLEVEL", "DEBUG") - os.Setenv("APPBUILDER_TOKEN", "") config, err := NewSDKConfig("", os.Getenv(SecretKeyV3)) if err != nil { } @@ -821,7 +820,6 @@ func TestCreateKnowledgeBaseError(t *testing.T) { func TestChunkError(t *testing.T) { t.Parallel() // 并发运行 os.Setenv("APPBUILDER_LOGLEVEL", "DEBUG") - os.Setenv("APPBUILDER_TOKEN", "") documentID := os.Getenv(DocumentIDV3) config, err := NewSDKConfig("", os.Getenv(SecretKeyV3)) @@ -1169,7 +1167,6 @@ func TestCreateKnowledgeBase(t *testing.T) { var logBuffer bytes.Buffer os.Setenv("APPBUILDER_LOGLEVEL", "DEBUG") - os.Setenv("APPBUILDER_TOKEN", "") log := func(format string, args ...any) { fmt.Fprintf(&logBuffer, format+"\n", args...) @@ -1402,7 +1399,6 @@ func TestChunk(t *testing.T) { var logBuffer bytes.Buffer os.Setenv("APPBUILDER_LOGLEVEL", "DEBUG") - os.Setenv("APPBUILDER_TOKEN", "") log := func(format string, args ...any) { fmt.Fprintf(&logBuffer, format+"\n", args...) diff --git a/go/appbuilder/rag_test.go b/go/appbuilder/rag_test.go index 84614d6e9..77c053a4d 100644 --- a/go/appbuilder/rag_test.go +++ b/go/appbuilder/rag_test.go @@ -15,20 +15,21 @@ package appbuilder import ( - "os" "encoding/json" "errors" "fmt" "io" + "os" "testing" ) + func TestNewRAGError(t *testing.T) { t.Parallel() // 并发运行 // 设置环境变量 os.Setenv("APPBUILDER_LOGLEVEL", "DEBUG") // 测试逻辑 - config, err := NewSDKConfig("", "bce-v3/ALTAK-RPJR9XSOVFl6mb5GxHbfU/072be74731e368d8bbb628a8941ec50aaeba01cd") + config, err := NewSDKConfig("", "") if err != nil { t.Logf("%s========== FAIL: %s ==========%s", "\033[31m", t.Name(), "\033[0m") t.Fatalf("new http client config failed: %v", err) @@ -81,12 +82,11 @@ func TestNewRAGError(t *testing.T) { } } func TestNewRAG(t *testing.T) { - t.Parallel() // 并发运行 // 设置环境变量 os.Setenv("APPBUILDER_LOGLEVEL", "DEBUG") // 测试逻辑 - config, err := NewSDKConfig("", "bce-v3/ALTAK-RPJR9XSOVFl6mb5GxHbfU/072be74731e368d8bbb628a8941ec50aaeba01cd") + config, err := NewSDKConfig("", os.Getenv(SecretKey)) if err != nil { t.Logf("%s========== FAIL: %s ==========%s", "\033[31m", t.Name(), "\033[0m") t.Fatalf("new http client config failed: %v", err) @@ -98,9 +98,11 @@ func TestNewRAG(t *testing.T) { if err != nil { t.Fatalf("new RAG instance failed") } - fmt.Println("问题出现在这里2") i, err := rag.Run("", "北京有多少小学生", true) + if err != nil { + t.Fatalf("run RAG failed: %v", err) + } var answer *RAGAnswer for answer, err = i.Next(); err == nil; answer, err = i.Next() { data, _ := json.Marshal(answer) diff --git a/java/src/main/java/com/baidubce/appbuilder/model/appbuilderclient/Event.java b/java/src/main/java/com/baidubce/appbuilder/model/appbuilderclient/Event.java index fe7248974..bff37e68e 100644 --- a/java/src/main/java/com/baidubce/appbuilder/model/appbuilderclient/Event.java +++ b/java/src/main/java/com/baidubce/appbuilder/model/appbuilderclient/Event.java @@ -5,6 +5,7 @@ public class Event { public static final String ChatflowEventType = "chatflow"; + public static final String FollowUpQueryEventType = "FollowUpQuery"; private String code; private String message; diff --git a/java/src/main/java/com/baidubce/appbuilder/model/appbuilderclient/EventContent.java b/java/src/main/java/com/baidubce/appbuilder/model/appbuilderclient/EventContent.java index 1d0879177..08e7d48de 100644 --- a/java/src/main/java/com/baidubce/appbuilder/model/appbuilderclient/EventContent.java +++ b/java/src/main/java/com/baidubce/appbuilder/model/appbuilderclient/EventContent.java @@ -16,6 +16,7 @@ public class EventContent { public static final String ChatflowInterruptContentType = "chatflow_interrupt"; public static final String PublishMessageContentType = "publish_message"; public static final String MultipleDialogEventContentType = "multiple_dialog_event"; + public static final String JsonContentType = "json"; @SerializedName("event_code") private String eventCode; diff --git a/java/src/test/java/com/baidubce/appbuilder/AppBuilderClientTest.java b/java/src/test/java/com/baidubce/appbuilder/AppBuilderClientTest.java index 61376b2b3..f4be40373 100644 --- a/java/src/test/java/com/baidubce/appbuilder/AppBuilderClientTest.java +++ b/java/src/test/java/com/baidubce/appbuilder/AppBuilderClientTest.java @@ -10,6 +10,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Stack; +import java.util.List; import com.baidubce.appbuilder.model.appbuilderclient.AppBuilderClientIterator; import com.baidubce.appbuilder.model.appbuilderclient.AppBuilderClientResult; @@ -26,6 +27,7 @@ public class AppBuilderClientTest { String appId; String chatflowAppId; + String followupqueryId; @Before public void setUp() { @@ -33,6 +35,7 @@ public void setUp() { System.setProperty("APPBUILDER_LOGLEVEL", "DEBUG"); appId = "aa8af334-df27-4855-b3d1-0d249c61fc08"; chatflowAppId = "4403205e-fb83-4fac-96d8-943bdb63796f"; + followupqueryId = "fb64d96b-f828-4385-ba1d-835298d635a9"; } @Test @@ -52,7 +55,7 @@ public void DescribeAppsTest() throws IOException, AppBuilderServerException { @Test public void AppBuilderClientRunTest() throws IOException, AppBuilderServerException { - AppBuilderClient builder = new AppBuilderClient(appId); + AppBuilderClient builder = new AppBuilderClient(followupqueryId); String conversationId = builder.createConversation(); assertNotNull(conversationId); String fileId = builder.uploadLocalFile(conversationId, @@ -63,7 +66,32 @@ public void AppBuilderClientRunTest() throws IOException, AppBuilderServerExcept assertTrue(itor.hasNext()); while (itor.hasNext()) { AppBuilderClientResult result = itor.next(); - System.out.println(result); + for (Event event : result.getEvents()) { + if (!event.getContentType().equals(EventContent.JsonContentType) + || !event.getEventType().equals(Event.FollowUpQueryEventType)) { + continue; + } + Object json = event.getDetail().get("json"); + if (!(json instanceof Map)) { + continue; + } + + for (Map.Entry entry : ((Map) json).entrySet()) { + if (!(entry.getKey() instanceof String && entry.getValue() instanceof List + && !((List) entry.getValue()).isEmpty() + && ((List) entry.getValue()).get(0) instanceof String)) { + continue; + } + + String key = (String) entry.getKey(); + String stringValue = (String) ((List) entry.getValue()).get(0); + + if (key.equals("follow_up_querys")) { + System.out.println(stringValue); + assert !stringValue.isEmpty(); + } + } + } } } @@ -90,7 +118,9 @@ public void AppBuilderClientRunFuncTest() throws IOException, AppBuilderServerEx String ToolCallID = ""; while (itor.hasNext()) { AppBuilderClientResult result = itor.next(); - ToolCallID = result.getEvents()[0].getToolCalls()[0].getId(); + + Event lastEvent = result.getEvents()[result.getEvents().length - 1]; + ToolCallID = lastEvent.getToolCalls()[lastEvent.getToolCalls().length - 1].getId(); System.out.println(result); } diff --git a/python/core/console/knowledge_base/data_class.py b/python/core/console/knowledge_base/data_class.py index ef2e9d718..ee339f825 100644 --- a/python/core/console/knowledge_base/data_class.py +++ b/python/core/console/knowledge_base/data_class.py @@ -98,7 +98,7 @@ class KnowledgeBaseGetDocumentsListResponse(BaseModel): class KnowledgeBaseConfigIndex(BaseModel): type: str = Field(..., description="索引类型", enum=["public", "bes", "vdb"]) - esUrl: Optional[str] = Field(..., description="bes地址") + esUrl: Optional[str] = Field(None, description="bes地址") username: Optional[str] = Field(None, description="bes用户名") password: Optional[str] = Field(None, description="bes密码") diff --git a/python/tests/test_appbuilder_client_follow_up_query.py b/python/tests/test_appbuilder_client_follow_up_query.py new file mode 100644 index 000000000..34e1d2867 --- /dev/null +++ b/python/tests/test_appbuilder_client_follow_up_query.py @@ -0,0 +1,97 @@ +# Copyright (c) 2024 Baidu, Inc. 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 unittest +import os +import appbuilder +from appbuilder.core.console.appbuilder_client.event_handler import ( + AppBuilderEventHandler, +) + + +class MyEventHandler(AppBuilderEventHandler): + def __init__(self): + super().__init__() + self.follow_up_queries = [] + + def handle_content_type(self, run_context, run_response): + event = run_response.events[-1] + if event.content_type == "json" and event.event_type == "FollowUpQuery": + follow_up_queries = event.detail.get("json").get("follow_up_querys") + self.follow_up_queries.extend(follow_up_queries) + + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") +class TestAppBuilderClientChatflow(unittest.TestCase): + def setUp(self): + """ + 设置环境变量。 + + Args: + 无参数,默认值为空。 + + Returns: + 无返回值,方法中执行了环境变量的赋值操作。 + """ + self.app_id = "fb64d96b-f828-4385-ba1d-835298d635a9" + + def test_appbuilder_run_follow_up_query(self): + # 如果app_id为空,则跳过单测执行, 避免单测因配置无效而失败 + """ + 如果app_id为空,则跳过单测执行, 避免单测因配置无效而失败 + + Args: + self (unittest.TestCase): unittest的TestCase对象 + + Raises: + None: 如果app_id不为空,则不会引发任何异常 + unittest.SkipTest (optional): 如果app_id为空,则跳过单测执行 + """ + if len(self.app_id) == 0: + self.skipTest("self.app_id is empty") + appbuilder.logger.setLoglevel("ERROR") + builder = appbuilder.AppBuilderClient(self.app_id) + conversation_id = builder.create_conversation() + msg = builder.run(conversation_id, "你能做什么", stream=True) + + for ans in msg.content: + for event in ans.events: + if event.content_type == "json" and event.event_type == "FollowUpQuery": + follow_up_query = event.detail.get( + "json").get("follow_up_querys")[0] + print(follow_up_query) + assert follow_up_query is not None + + def test_appbuilder_run_followupquery_with_event_handler(self): + if len(self.app_id) == 0: + self.skipTest("self.app_id is empty") + appbuilder.logger.setLoglevel("ERROR") + builder = appbuilder.AppBuilderClient(self.app_id) + conversation_id = builder.create_conversation() + + event_handler = MyEventHandler() + with builder.run_with_handler( + conversation_id = conversation_id, + query = "你能做什么", + stream=True, + event_handler=event_handler, + ) as run: + run.until_done() + + print(event_handler.follow_up_queries) + assert len(event_handler.follow_up_queries) > 0 + + +if __name__ == "__main__": + unittest.main() diff --git a/python/tests/test_knowledge_base.py b/python/tests/test_knowledge_base.py index 6a7702231..ddc0c8924 100644 --- a/python/tests/test_knowledge_base.py +++ b/python/tests/test_knowledge_base.py @@ -23,6 +23,7 @@ def setUp(self): def test_doc_knowledage(self): dataset_id = os.getenv("DATASET_ID", "UNKNOWN") + appbuilder.logger.setLoglevel('DEBUG') knowledge = appbuilder.KnowledgeBase(knowledge_id=dataset_id) upload_res = knowledge.upload_file("./data/qa_appbuilder_client_demo.pdf") From 173aa729221cd05dc51ef6b51b487021d6374770 Mon Sep 17 00:00:00 2001 From: peiwenYe <963623403@qq.com> Date: Tue, 10 Dec 2024 17:23:00 +0800 Subject: [PATCH 63/85] =?UTF-8?q?=E7=BB=84=E4=BB=B6=E8=BE=93=E5=87=BAschem?= =?UTF-8?q?a=E6=A0=A1=E9=AA=8C=E5=8F=96=E6=B6=88=E5=AF=B9non=5Fstream=5Fto?= =?UTF-8?q?ol=5Feval=E7=9A=84=E6=A3=80=E6=9F=A5=20(#651)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: yepeiwen01 --- python/tests/component_check.py | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/python/tests/component_check.py b/python/tests/component_check.py index 1c6fd4b32..6679073e4 100644 --- a/python/tests/component_check.py +++ b/python/tests/component_check.py @@ -361,9 +361,9 @@ def check(self, component_cls) -> CheckInfo: try: stream_output_dict = {"text": "", "oral_text":"", "code": ""} stream_outputs = component_obj.tool_eval(**input_dict) - for stream_output in stream_outputs: #校验流式输出 + for stream_output in stream_outputs: iter_invalid_detail = self._check_jsonschema(stream_output.model_dump(), output_json_schemas) - invalid_details.extend(["流式" + error_message for error_message in iter_invalid_detail]) + invalid_details.extend(iter_invalid_detail) iter_output_dict = self._gather_iter_outputs(stream_output) stream_output_dict["text"] += iter_output_dict["text"] stream_output_dict["oral_text"] += iter_output_dict["oral_text"] @@ -373,17 +373,6 @@ def check(self, component_cls) -> CheckInfo: except Exception as e: invalid_details.append("ToolEval执行失败: {}".format(e)) - time.sleep(2) - try: - non_stream_outputs = component_obj.non_stream_tool_eval(**input_dict) - non_stream_invalid_details = self._check_jsonschema(non_stream_outputs.model_dump(), output_json_schemas) #校验非流式输出 - invalid_details.extend(["非流式" + error_message for error_message in non_stream_invalid_details]) - if len(invalid_details) == 0: - non_stream_output_dict = self._gather_iter_outputs(non_stream_outputs) - invalid_details.extend(self._check_text_and_code(component_case, non_stream_output_dict)) - except Exception as e: - invalid_details.append(" NonStreamToolEval执行失败: {}".format(e)) - for env in envs: os.environ.pop(env) From aa580b5d57896bd82081619469e0685187743a13 Mon Sep 17 00:00:00 2001 From: userpj Date: Tue, 10 Dec 2024 17:56:49 +0800 Subject: [PATCH 64/85] Update for version 0.9.8 (#652) --- README.md | 2 +- docs/DevelopGuide/ChangeLog/changelog.md | 5 +++++ docs/DevelopGuide/HowToContributeCode/README.md | 2 +- docs/QuickStart/StartFirstAINativeApplication/install.md | 2 +- docs/README_en.md | 2 +- docs/README_ja.md | 2 +- docs/Tools/SphinxSh/source/conf.py | 2 +- python/__init__.py | 2 +- python/utils/bce_deploy.py | 4 ++-- setup.py | 2 +- 10 files changed, 15 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 191ce5b0a..004dece09 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ AppBuilder-SDK不仅提供了百度智能云提供的基础能力组件,同时 ## 如何安装AppBuilder-SDK -#### 百度智能云千帆AppBuilder-SDK 最新版本 0.9.7 (2024-11-27) +#### 百度智能云千帆AppBuilder-SDK 最新版本 0.9.8 (2024-12-10) 百度智能云千帆AppBuilder-SDK 更新记录&最新特性请查阅我们的[版本说明](/docs/DevelopGuide/ChangeLog/changelog.md) diff --git a/docs/DevelopGuide/ChangeLog/changelog.md b/docs/DevelopGuide/ChangeLog/changelog.md index 18169794e..cfcf0888c 100644 --- a/docs/DevelopGuide/ChangeLog/changelog.md +++ b/docs/DevelopGuide/ChangeLog/changelog.md @@ -68,3 +68,8 @@ * 新增TreeMind组件 * 新增工作流Agent回复“信息收集节点”功能,支持多轮对话事件处理 * Python的ToolCall功能支持通过函数定义、装饰器的形式等生成ToolCall参数 +* **2024.11.27 v0.9.8版本发布** [ReleaseNote](https://github.com/baidubce/app-builder/releases/tag/0.9.8) + * AppBuilderClient新增追问功能支持 + * TTS组件新增更多语音效果 + * 通用文字识别组件新增更多语言支持 + * 实时语音通话功能内测 \ No newline at end of file diff --git a/docs/DevelopGuide/HowToContributeCode/README.md b/docs/DevelopGuide/HowToContributeCode/README.md index 9be7f6d31..e56877380 100644 --- a/docs/DevelopGuide/HowToContributeCode/README.md +++ b/docs/DevelopGuide/HowToContributeCode/README.md @@ -10,7 +10,7 @@ 当前已集成Python版本AppBuilder-SDK 0.9.4及相关依赖,方便开发者融入个人已有的大模型应用程序。此部分仍在不断建设中。 二次开发可以采用官方提供的开发镜像,便于快速安装各种依赖库。也可在镜像中使用已安装的`appbuilder_trace_server`、`appbuilder_bce_deploy`工具。 ``` shell -docker pull registry.baidubce.com/appbuilder/appbuilder-sdk-devel:0.9.7 +docker pull registry.baidubce.com/appbuilder/appbuilder-sdk-devel:0.9.8 ``` ### 消息(Message) diff --git a/docs/QuickStart/StartFirstAINativeApplication/install.md b/docs/QuickStart/StartFirstAINativeApplication/install.md index e87a66efc..792a9635d 100644 --- a/docs/QuickStart/StartFirstAINativeApplication/install.md +++ b/docs/QuickStart/StartFirstAINativeApplication/install.md @@ -40,5 +40,5 @@ go get github.com/baidubce/app-builder/go/appbuilder ### Docker (当前仅集成了Python版本AppBuilder-SDK) ``` shell -docker pull registry.baidubce.com/appbuilder/appbuilder-sdk-devel:0.9.7 +docker pull registry.baidubce.com/appbuilder/appbuilder-sdk-devel:0.9.8 ``` diff --git a/docs/README_en.md b/docs/README_en.md index 3611bcab2..99b24c4d6 100644 --- a/docs/README_en.md +++ b/docs/README_en.md @@ -47,7 +47,7 @@ Baidu AI Cloud Qianfan AppBuilder-SDK offers the following essential features fo ## How to install? -#### The latest version of Baidu AI Cloud Qianfan AppBuilder SDK is 0.9.7 (2024-11-27) +#### The latest version of Baidu AI Cloud Qianfan AppBuilder SDK is 0.9.8 (2024-12-10) Baidu AI Cloud Qianfan AppBuilder SDK ReleaseNote please refer to our [version description](/docs/DevelopGuide/ChangeLog/changelog.md) diff --git a/docs/README_ja.md b/docs/README_ja.md index 7699ca025..bfac2b37d 100644 --- a/docs/README_ja.md +++ b/docs/README_ja.md @@ -44,7 +44,7 @@ Baidu AI Cloud Qianfan AppBuilder-SDKは、AIアプリケーション開発者 ## どのようにインストールしますか? -#### Baidu AI Cloud Qianfan AppBuilder SDKの最新バージョンは0.9.7(2024-11-27)です +#### Baidu AI Cloud Qianfan AppBuilder SDKの最新バージョンは0.9.8(2024-12-10)です Baidu AI Cloud Qianfan AppBuilder SDKのリリースノートについては、[バージョン説明](DevelopGuide/ChangeLog/changelog.md)をご覧ください。 diff --git a/docs/Tools/SphinxSh/source/conf.py b/docs/Tools/SphinxSh/source/conf.py index 166fc9162..3f633c176 100644 --- a/docs/Tools/SphinxSh/source/conf.py +++ b/docs/Tools/SphinxSh/source/conf.py @@ -14,7 +14,7 @@ project = 'Appbuilder-SDK' copyright = '2024, baidubce' author = 'baidubce' -release = '0.9.7' +release = '0.9.8' # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration diff --git a/python/__init__.py b/python/__init__.py index 3e949bea7..04de5e9f8 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -13,7 +13,7 @@ # limitations under the License. -__version__ = '0.9.7' +__version__ = '0.9.8' import os import sys diff --git a/python/utils/bce_deploy.py b/python/utils/bce_deploy.py index 617b2060b..60d9efb89 100644 --- a/python/utils/bce_deploy.py +++ b/python/utils/bce_deploy.py @@ -113,8 +113,8 @@ def build_user_data(self): + f"rm {self.tar_file_name}\\n" + f"chmod a+x {self.run_script_name}\\n" + "yum install -y docker\\n" - + "docker pull registry.baidubce.com/appbuilder/appbuilder-sdk-cloud:0.9.7\\n" - + f"docker run -itd --net=host -v /root/test:{workspace} --name appbuilder-sdk registry.baidubce.com/appbuilder/appbuilder-sdk-cloud:0.9.7{workspace}/{self.run_script_name}" + + "docker pull registry.baidubce.com/appbuilder/appbuilder-sdk-cloud:0.9.8\\n" + + f"docker run -itd --net=host -v /root/test:{workspace} --name appbuilder-sdk registry.baidubce.com/appbuilder/appbuilder-sdk-cloud:0.9.8{workspace}/{self.run_script_name}" ) return user_data diff --git a/setup.py b/setup.py index 3fe0c4edc..36d420087 100755 --- a/setup.py +++ b/setup.py @@ -54,7 +54,7 @@ setup( name="appbuilder-sdk", # NOTE(chengmo): 修改此版本号时,请注意同时修改 __init__.py 中的 __version__ - version="0.9.7", + version="0.9.8", author="dongdaxiang", author_email="dongdaxiang@baidu.com", packages=packages, From 52ad88770fd0b8b5996c438f676f32c19eceed4e Mon Sep 17 00:00:00 2001 From: C9luster <138663536+C9luster@users.noreply.github.com> Date: Tue, 10 Dec 2024 18:58:00 +0800 Subject: [PATCH 65/85] =?UTF-8?q?=E6=9B=B4=E6=96=B0V2=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E7=BB=84=E4=BB=B6ASR/ObjectRecognition/SimilarQuestion/OralQue?= =?UTF-8?q?ryGeneration=20(#650)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 更新V2版本组件ASR/ObjectRecognition/SimilarQuestion/OralQueryGeneration * 补充单元测试 * 更新单测 --------- Co-authored-by: yinjiaqi --- python/core/components/v2/__init__.py | 10 +- python/core/components/v2/asr/__init__.py | 15 + python/core/components/v2/asr/component.py | 280 ++++++++++++++++++ .../v2/llms/oral_query_generation/__init__.py | 1 + .../llms/oral_query_generation/component.py | 232 +++++++++++++++ .../v2/llms/similar_question/__init__.py | 2 + .../v2/llms/similar_question/component.py | 130 ++++++++ .../v2/llms/style_writing/component.py | 27 +- .../v2/object_recognize/__init__.py | 10 + .../v2/object_recognize/component.py | 197 ++++++++++++ .../components/v2/text_to_image/component.py | 2 + python/tests/component_tool_eval_cases.py | 65 ++++ python/tests/test_v2_asr.py | 65 ++++ python/tests/test_v2_object_recognition.py | 67 +++++ python/tests/test_v2_oral_query_generat.py | 45 +++ python/tests/test_v2_similar_question.py | 44 +++ 16 files changed, 1179 insertions(+), 13 deletions(-) create mode 100644 python/core/components/v2/asr/__init__.py create mode 100644 python/core/components/v2/asr/component.py create mode 100644 python/core/components/v2/llms/oral_query_generation/__init__.py create mode 100644 python/core/components/v2/llms/oral_query_generation/component.py create mode 100644 python/core/components/v2/llms/similar_question/__init__.py create mode 100644 python/core/components/v2/llms/similar_question/component.py create mode 100644 python/core/components/v2/object_recognize/__init__.py create mode 100644 python/core/components/v2/object_recognize/component.py create mode 100644 python/tests/test_v2_asr.py create mode 100644 python/tests/test_v2_object_recognition.py create mode 100644 python/tests/test_v2_oral_query_generat.py create mode 100644 python/tests/test_v2_similar_question.py diff --git a/python/core/components/v2/__init__.py b/python/core/components/v2/__init__.py index 6664963e0..72338dce7 100644 --- a/python/core/components/v2/__init__.py +++ b/python/core/components/v2/__init__.py @@ -25,6 +25,10 @@ from .text_to_image.component import Text2Image from .llms.style_writing.component import StyleWriting from .tree_mind.component import TreeMind +from .asr.component import ASR +from .object_recognize.component import ObjectRecognition +from .llms.similar_question.component import SimilarQuestion +from .llms.oral_query_generation.component import OralQueryGeneration __V2_COMPONENTS__ = [ "AnimalRecognition", @@ -39,5 +43,9 @@ "TableOCR", "Text2Image", "StyleWriting", - "TreeMind" + "TreeMind", + "ASR", + "ObjectRecognition", + "SimilarQuestion", + "OralQueryGeneration", ] # NOQA \ No newline at end of file diff --git a/python/core/components/v2/asr/__init__.py b/python/core/components/v2/asr/__init__.py new file mode 100644 index 000000000..fc3a1a2ea --- /dev/null +++ b/python/core/components/v2/asr/__init__.py @@ -0,0 +1,15 @@ +# Copyright (c) 2023 Baidu, Inc. 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. + + diff --git a/python/core/components/v2/asr/component.py b/python/core/components/v2/asr/component.py new file mode 100644 index 000000000..c98f1a7fd --- /dev/null +++ b/python/core/components/v2/asr/component.py @@ -0,0 +1,280 @@ +# Copyright (c) 2023 Baidu, Inc. 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. + +r"""ASR component. +""" +import os +import uuid +import json + +import requests +import tempfile +from urllib.parse import urlparse + +from appbuilder.core.component import Component +from appbuilder.core.message import Message +from appbuilder.core._exception import AppBuilderServerException, InvalidRequestArgumentError +from appbuilder.core._client import HTTPClient +from appbuilder.core.components.asr.model import ShortSpeechRecognitionRequest, ShortSpeechRecognitionResponse, \ + ASRInMsg, ASROutMsg +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace + +DEFAULT_AUDIO_MAX_DURATION = 55 * 1000 # 55s +# 参考短语音极速版API(https://ai.baidu.com/ai-doc/SPEECH/Jlbxdezuf) +DEFAULT_FRAME_RATE = 16000 +DEV_PID = "80001" + + +class ASR(Component): + r""" + ASR组件,即对于输入的语音文件,输出语音识别结果 + + Examples: + + .. code-block:: python + + import appbuilder + asr = appbuilder.ASR() + os.environ["APPBUILDER_TOKEN"] = '...' + + with open("xxxx.pcm", "rb") as f: + audio_data = f.read() + content_data = {"audio_format": "pcm", "raw_audio": audio_data, "rate": 16000} + msg = appbuilder.Message(content_data) + out = asr.run(msg) + print(out.content) # eg: {"result": ["北京科技馆。"]} + """ + name = "asr" + version = "v1" + + manifests = [ + { + "name": "asr", + "description": "对于输入的语音文件进行识别,输出语音识别结果。", + "parameters": { + "type": "object", + "properties": { + "file_url": { + "type": "string", + "description": "输入语音文件的url,根据url获取到语音文件" + }, + "file_name": { + "type": "string", + "description": "待识别语音文件名,用于生成获取语音的url" + }, + "file_type": { + "type": "string", + "description": "语音文件类型,支持pcm/wav/amr/m4a", + "enum": ["pcm", "wav", "amr", "m4a"] + } + }, + "anyOf": [ + { + "required": [ + "file_url" + ] + }, + { + "required": [ + "file_name" + ] + } + ] + } + } + ] + + @HTTPClient.check_param + @components_run_trace + def run(self, message: Message, audio_format: str = "pcm", rate: int = 16000, + timeout: float = None, retry: int = 0, **kwargs) -> Message: + r""" + 执行语音识别操作,并返回识别结果。 + + Args: + message (Message): 输入消息对象,包含待识别的音频数据。该参数为必需项,格式如:Message(content={"raw_audio": b"..."})。 + audio_format (str, optional): 音频文件格式,支持pcm/wav/amr/m4a,不区分大小写,推荐使用pcm格式。默认为"pcm"。 + rate (int, optional): 音频采样率,固定为16000。默认为16000。 + timeout (float, optional): HTTP请求超时时间。默认为None。 + retry (int, optional): HTTP请求重试次数。默认为0。 + + Returns: + Message: 语音识别结果,格式如:Message(content={"result": ["识别结果"]})。 + """ + inp = ASRInMsg(**message.content) + request = ShortSpeechRecognitionRequest() + request.format = audio_format + request.rate = rate + request.cuid = str(uuid.uuid4()) + request.dev_pid = DEV_PID + request.speech = inp.raw_audio + traceid = kwargs.get("_sys_traceid", "") + response, _ = self._recognize(request, timeout, retry, request_id=traceid) + out = ASROutMsg(result=list(response.result)) + return Message(content=out.model_dump()) + + def _recognize( + self, + request: ShortSpeechRecognitionRequest, + timeout: float = None, + retry: int = 0, + request_id: str = None, + ) -> ShortSpeechRecognitionResponse: + """ + 使用给定的输入并返回语音识别的结果。 + + 参数: + request (obj:`ShortSpeechRecognitionRequest`): 输入请求,这是一个必需的参数。 + timeout (float, 可选): 请求的超时时间。 + retry (int, 可选): 请求的重试次数。 + + 返回: + obj:`ShortSpeechRecognitionResponse`: 接口返回的输出消息。 + """ + ContentType = "audio/" + request.format + ";rate=" + str(request.rate) + headers = self.http_client.auth_header(request_id) + headers['content-type'] = ContentType + params = { + 'dev_pid': request.dev_pid, + 'cuid': request.cuid + } + if retry != self.http_client.retry.total: + self.http_client.retry.total = retry + response = self.http_client.session.post(self.http_client.service_url("/v1/bce/aip_speech/asrpro"), + params=params, headers=headers, data=request.speech, timeout=timeout) + self.http_client.check_response_header(response) + data = response.json() + self.http_client.check_response_json(data) + request_id = self.http_client.response_request_id(response) + self.__class__._check_service_error(request_id, data) + response = ShortSpeechRecognitionResponse.from_json(payload=json.dumps(data)) + response.request_id = request_id + return response, data + + @staticmethod + def _check_service_error(request_id: str, data: dict): + r"""个性化服务response参数检查 + + 参数: + request (dict) : 短语音识别body返回 + 返回: + 无 + """ + if "err_no" in data and "err_msg" in data: + if data["err_no"] != 0: + raise AppBuilderServerException( + request_id=request_id, + service_err_code=data["err_no"], + service_err_message=data["err_msg"] + ) + + @components_run_stream_trace + def tool_eval(self, + file_url: str = '', + file_name: str = '', + file_type: str = '', + **kwargs): + """ + 执行语音识别操作,并返回识别结果 + + Args: + file_url (str, optional): 文件URL。默认为空字符串。 + file_name (str, optional): 文件名。默认为空字符串。 + file_type (str, optional): 文件类型。默认为空字符串。 + **kwargs: 其他关键字参数。 + + Returns: + Generator: 生成器,用于生成输出对象。 + + Raises: + InvalidRequestArgumentError: 请求格式错误。 + + """ + if not file_url: + file_urls = kwargs.get("_sys_file_urls", {}) + file_path = file_name + if not file_path: + raise InvalidRequestArgumentError("request format error, file name is not set") + file_name = os.path.basename(file_path) + file_url = file_urls.get(file_name, None) + if not file_url: + raise InvalidRequestArgumentError( + f"request format error, file {file_url} url does not exist" + ) + + if not file_type or file_type not in ["pcm", "wav", "amr", "m4a"]: + _, file_type = os.path.splitext(os.path.basename(urlparse(file_url).path)) + file_type = file_type.strip('.') + + audio_file = tempfile.NamedTemporaryFile("wb", suffix=file_type) + audio_file.write(requests.get(file_url).content) + + raw_audios = _convert(audio_file.name, file_type) + text = "" + for raw_audio in raw_audios: + request = ShortSpeechRecognitionRequest() + request.format = file_type + request.rate = DEFAULT_FRAME_RATE + request.cuid = str(uuid.uuid4()) + request.dev_pid = DEV_PID + request.speech = raw_audio + traceid = kwargs.get("_sys_traceid", "") + response, raw_data = self._recognize(request, request_id=traceid) + text += "".join(list(response.result)) + results = {"识别结果": text} + audio_file.close() + res = json.dumps(results, ensure_ascii=False, indent=4) + yield self.create_output(type='text', text=res, raw_data=raw_data, visible_scope="llm") + yield self.create_output(type='text', text="", raw_data=raw_data, visible_scope="user") + + +def _convert(path, file_type): + from pydub import AudioSegment + if file_type.lower() == "mp3": + audio = AudioSegment.from_mp3(path) + elif file_type.lower() == "wav": + audio = AudioSegment.from_wav(path) + # 如果是pcm格式,则直接读取并返回 + elif file_type.lower() == "pcm": + with open(path, "rb") as f: + return [f.read()] + else: + # pydub自动检测音频类型 + audio = AudioSegment.from_wav(path) + # 如果取样率为16000且时长小于60s,则直接读取音频并返回 + if (audio.frame_rate == DEFAULT_FRAME_RATE and audio.frame_count() * 1000 + / audio.frame_rate < DEFAULT_AUDIO_MAX_DURATION): + with open(path, "rb") as f: + return [f.read()] + audio = audio.set_frame_rate(DEFAULT_FRAME_RATE) + total_milliseconds = int(audio.frame_count() * 1000 / audio.frame_rate) + start = 0 + raw_audios = [] + while start < total_milliseconds: + end = start + DEFAULT_AUDIO_MAX_DURATION + if start + DEFAULT_AUDIO_MAX_DURATION > total_milliseconds: + end = total_milliseconds + audio_seg = audio[start:end] + audio_seg_file = tempfile.NamedTemporaryFile("wb", suffix="wav") + try: + audio_seg.export(audio_seg_file.name, format="wav") + with open(audio_seg_file.name, "rb") as f: + raw_audios.append(f.read()) + finally: + audio_seg_file.close() + start = end + return raw_audios + + + diff --git a/python/core/components/v2/llms/oral_query_generation/__init__.py b/python/core/components/v2/llms/oral_query_generation/__init__.py new file mode 100644 index 000000000..b6627eee1 --- /dev/null +++ b/python/core/components/v2/llms/oral_query_generation/__init__.py @@ -0,0 +1 @@ +from .component import OralQueryGeneration \ No newline at end of file diff --git a/python/core/components/v2/llms/oral_query_generation/component.py b/python/core/components/v2/llms/oral_query_generation/component.py new file mode 100644 index 000000000..67359d98c --- /dev/null +++ b/python/core/components/v2/llms/oral_query_generation/component.py @@ -0,0 +1,232 @@ +# Copyright (c) 2023 Baidu, Inc. 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 typing import Optional + +from appbuilder.core.components.llms.base import CompletionBaseComponent, ModelArgsConfig +from appbuilder.core.message import Message +from appbuilder.core._exception import AppBuilderServerException +from appbuilder.utils.logger_util import logger +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace +from appbuilder.core.components.llms.oral_query_generation.base import OralQueryGenerationArgs + + +class OralQueryGeneration(CompletionBaseComponent): + r""" + 口语化Query生成,可用于问答场景下对文档增强索引。 + *注:该组件推荐使用Qianfan-Agent-Speed-8k模型。* + + Examples: + + .. code-block:: python + + import os + import appbuilder + + os.environ["APPBUILDER_TOKEN"] = '...' + + text = ('文档标题:在OPPO Reno5上使用视频超级防抖\n' + '文档摘要:OPPO Reno5上的视频超级防抖,视频超级防抖3.0,多代视频防抖算法积累,这一代依旧超级防抖超级稳。 开启视频超级' + '防抖 开启路径:打开「相机 > 视频 > 点击屏幕上方的“超级防抖”标识」 后置视频同时支持超级防抖和超级防抖Pro功能,开启超级' + '防抖后手机屏幕将出现超级防抖Pro开关,点击即可开启或关闭。 除此之外,前置视频同样加持防抖算法,边走边拍也能稳定聚焦脸部' + ',实时视频分享您的生活。') + oral_query_generation = appbuilder.OralQueryGeneration(model='Qianfan-Agent-Speed-8k') + answer = oral_query_generation(appbuilder.Message(text), query_type='全部', output_format='str') + print(answer.content) + """ + name = 'query_generation' + version = 'v1' + meta = OralQueryGenerationArgs + + manifests = [ + { + "name": "query_generation", + "description": "输入文本、待生成的query类型和输出格式,生成query,并按照要求的格式进行输出。", + "parameters": { + "type": "object", + "properties": { + "text": { + "type": "string", + "description": "输入文本,组件会根据该输入文本生成query。" + }, + "query_type": { + "type": "string", + "description": "待生成的query类型,可选问题、短语以及全部(问题 + 短语)。" + }, + "output_format": { + "type": "string", + "description": "输出格式,可选json或str,str格式与老版本输出格式相同。" + } + }, + "required": [ + "text" + ] + } + } + ] + + def __init__( + self, + model: str="Qianfan-Agent-Speed-8K", + secret_key: Optional[str] = None, + gateway: str = "", + lazy_certification: bool = True, + ): + """初始化StyleRewrite模型。 + + Args: + model (str|None): 模型名称,用于指定要使用的千帆模型。 + secret_key (str, 可选): 用户鉴权token, 默认从环境变量中获取: os.getenv("APPBUILDER_TOKEN", ""). + gateway (str, 可选): 后端网关服务地址,默认从环境变量中获取: os.getenv("GATEWAY_URL", "") + lazy_certification (bool, 可选): 延迟认证,为True时在第一次运行时认证. Defaults to False. + + Returns: + None + + """ + super().__init__( + OralQueryGenerationArgs, model=model, secret_key=secret_key, gateway=gateway, + lazy_certification=lazy_certification) + + def _regenerate_output(self, model_output, output_format): + """ + 兼容老版本的输出格式 + """ + if not isinstance(model_output, str): + return model_output + + match_obj = re.search(r'```json\n(.+)\n```', model_output, flags=re.DOTALL) + + regenerated_output = None + if match_obj: + regenerated_output = json.loads(match_obj.group(1)) + else: + dict_json_match_obj = re.search(r'\{(.|\n)+\}', model_output) + dict_json_text = dict_json_match_obj.group(0) if dict_json_match_obj else None + regenerated_output = json.loads(dict_json_text) if dict_json_text is not None else model_output + + if output_format == 'json' or not isinstance(regenerated_output, dict): + return json.dumps(regenerated_output, ensure_ascii=False, indent=4) + + queries = [] + for key in ['问题', '短语']: + queries += regenerated_output.pop(key, []) + for value in regenerated_output.values(): + queries += value + + regenerated_output = '\n'.join([f'{index}. {query}' for index, query in enumerate(queries, 1)]) + return regenerated_output + + def _completion(self, version, base_url, request, timeout: float = None, + retry: int = 0): + r"""Send a byte array of an audio file to obtain the result of speech recognition.""" + + headers = self.http_client.auth_header() + headers["Content-Type"] = "application/json" + + stream = True if request.response_mode == "streaming" else False + + url = self.http_client.service_url("/app/query_generation", self.base_url) + + logger.debug( + "request url: {}, method: {}, json: {}, headers: {}".format(url, + "POST", + request.params, + headers)) + response = self.http_client.session.post(url, json=request.params, headers=headers, timeout=timeout, + stream=stream) + + logger.debug( + "request url: {}, method: {}, json: {}, headers: {}, response: {}".format(url, "POST", + request.params, + headers, + response)) + return self.gene_response(response, stream) + + @components_run_trace + def run(self, message, query_type='全部', output_format='str', stream=False, temperature=1e-10, top_p=0.0): + """ + 使用给定的输入运行模型并返回结果。 + + Args: + message (Message): 输入消息,包含query、context和answer等信息。这是一个必需的参数。 + query_type (str, 可选): 待生成的query类型,包括问题、短语和全部(问题+短语)。默认为全部。 + output_format (str, 可选): 输出格式,包括json和str,当stream为True时,只能以json形式输出。默认为str。 + stream (bool, 可选): 指定是否以流式形式返回响应。默认为 False。 + temperature (float, 可选): 模型配置的温度参数,用于调整模型的生成概率。 + 取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 + top_p (float, 可选): 影响输出文本的多样性,取值越大,生成文本的多样性越强。 + 取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 + + Returns: + result (Message): 模型运行后的输出消息。 + + """ + text = message.content + assert text, 'Input text should be a valid string' + inputs = { + 'text': text, + 'query_type': query_type + } + response_mode = "streaming" if stream else "blocking" + user_id = message.id + model_config_inputs = ModelArgsConfig(**{"stream": stream, "temperature": temperature, "top_p": top_p}) + model_config = self.get_model_config(model_config_inputs) + + request = self.gene_request('', inputs, response_mode, user_id, model_config) + response = self._completion(self.version, self.base_url, request) + + if response.error_no != 0: + raise AppBuilderServerException(service_err_code=response.error_no, service_err_message=response.error_msg) + + result = response.to_message() + result.content = self._regenerate_output(result.content, output_format) + + return result + + @components_run_stream_trace + def tool_eval(self, + text: str, + query_type: str = '全部', + output_format: str = 'str', + **kwargs): + """ + 根据给定的文本和查询类型生成输出。 + + Args: + text (str): 要处理的文本。 + query_type (str, optional): 查询类型,默认为'全部'。 + output_format (str, optional): 输出格式,默认为'str'。 + **kwargs: 其他关键字参数,例如模型配置等。 + + Returns: + generator: 生成输出数据的生成器。 + + """ + msg = Message(text) + model_configs = kwargs.get('model_configs', {}) + temperature = model_configs.get('temperature', 1e-10) + top_p = model_configs.get('top_p', 0.0) + message = self.run(message=msg, + query_type=query_type, + output_format=output_format, + stream=True, + temperature=temperature, + top_p=top_p) + + for data in message.content: + yield self.create_output(type="text", text=data, usage=message.token_usage) + diff --git a/python/core/components/v2/llms/similar_question/__init__.py b/python/core/components/v2/llms/similar_question/__init__.py new file mode 100644 index 000000000..f62aa4607 --- /dev/null +++ b/python/core/components/v2/llms/similar_question/__init__.py @@ -0,0 +1,2 @@ + +from .component import SimilarQuestion diff --git a/python/core/components/v2/llms/similar_question/component.py b/python/core/components/v2/llms/similar_question/component.py new file mode 100644 index 000000000..fdff32f1e --- /dev/null +++ b/python/core/components/v2/llms/similar_question/component.py @@ -0,0 +1,130 @@ +# Copyright (c) 2024 Baidu, Inc. 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. + +""" similar question +""" +from typing import Optional + +from appbuilder.core.message import Message +from appbuilder.core.components.llms.base import CompletionBaseComponent +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace +from appbuilder.core.components.llms.similar_question.base import SimilarQuestionMeta + + +class SimilarQuestion(CompletionBaseComponent): + r""" + 基于输入的问题, 挖掘出与该问题相关的类似问题。广泛用于客服、问答等场景。 + + Examples: + + .. code-block:: python + + import os + import appbuilder + + os.environ["APPBUILDER_TOKEN"] = "..." + + qa_mining = appbuilder.SimilarQuestion(model="Qianfan-Agent-Speed-8k") + + msg = "我想吃冰淇淋,哪里的冰淇淋比较好吃?" + msg = appbuilder.Message(msg) + answer = qa_mining(msg) + + print("Answer: \n{}".format(answer.content)) + """ + name = "similar_question" + version = "v1" + meta = SimilarQuestionMeta + + manifests = [ + { + "name": "similar_question", + "description": "基于输入的问题,挖掘出与该问题相关的类似问题。", + "parameters": { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "输入的问题,用于大模型根据该问题输出相关的类似问题。" + } + }, + "required": [ + "query" + ] + } + } + ] + + def __init__( + self, + model: str="Qianfan-Agent-Speed-8K", + secret_key: Optional[str] = None, + gateway: str = "", + lazy_certification: bool = True, + ): + """初始化StyleRewrite模型。 + + Args: + model (str|None): 模型名称,用于指定要使用的千帆模型。 + secret_key (str, 可选): 用户鉴权token, 默认从环境变量中获取: os.getenv("APPBUILDER_TOKEN", ""). + gateway (str, 可选): 后端网关服务地址,默认从环境变量中获取: os.getenv("GATEWAY_URL", "") + lazy_certification (bool, 可选): 延迟认证,为True时在第一次运行时认证. Defaults to False. + + Returns: + None + + """ + super().__init__( + SimilarQuestionMeta, model=model, secret_key=secret_key, gateway=gateway, + lazy_certification=lazy_certification) + + @components_run_trace + def run(self, message, stream=False, temperature=1e-10, top_p=0.0, request_id=None): + """ + 给定输入(message)到模型运行,同时指定运行参数,并返回结果。 + + Args: + message (obj:`Message`): 输入消息,用于模型的主要输入内容。这是一个必需的参数。 + stream (bool, 可选): 指定是否以流式形式返回响应。默认为 False。 + temperature (float, 可选): 模型配置的温度参数,用于调整模型的生成概率。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 1e-10。 + top_p(float, 可选): 影响输出文本的多样性,取值越大,生成文本的多样性越强。取值范围为 0.0 到 1.0,其中较低的值使生成更确定性,较高的值使生成更多样性。默认值为 0。 + + Returns: + obj:`Message`: 模型运行后的输出消息。 + """ + return super().run(message=message, stream=stream, temperature=temperature, top_p=top_p, request_id=request_id) + + @components_run_stream_trace + def tool_eval(self, + query: str, + **kwargs): + """ + 根据给定的query和可选参数生成并返回文本输出。 + + Args: + query (str): 需要生成文本的输入查询字符串。 + **kwargs: 其他可选参数。 + + Returns: + Generator[Output]: 返回一个生成器,生成类型为Output的对象。 + + """ + traceid = kwargs.get("_sys_traceid") + msg = Message(query) + model_configs = kwargs.get('model_configs', {}) + temperature = model_configs.get("temperature", 1e-10) + top_p = model_configs.get("top_p", 0.0) + message = super().run(message=msg, stream=False, temperature=temperature, top_p=top_p, request_id=traceid) + + yield self.create_output(type="text", text=str(message.content), name="text", usage=message.token_usage) \ No newline at end of file diff --git a/python/core/components/v2/llms/style_writing/component.py b/python/core/components/v2/llms/style_writing/component.py index 44b6ab205..f7fafd3de 100644 --- a/python/core/components/v2/llms/style_writing/component.py +++ b/python/core/components/v2/llms/style_writing/component.py @@ -20,7 +20,7 @@ from pydantic import Field from typing import Optional -from appbuilder.core.components.llms.style_writing.base import StyleQueryChoices, LengthChoices +from appbuilder.core.components.llms.style_writing.base import StyleQueryChoices, LengthChoices, StyleWritingArgs class StyleWritingArgs(ComponentArguments): """ @@ -95,12 +95,12 @@ class StyleWriting(CompletionBaseComponent): def __init__( self, - model=None, + model: str="Qianfan-Agent-Speed-8K", secret_key: Optional[str] = None, gateway: str = "", - lazy_certification: bool = False, + lazy_certification: bool = True, ): - """初始化StyleWriting模型。 + """初始化StyleRewrite模型。 Args: model (str|None): 模型名称,用于指定要使用的千帆模型。 @@ -143,18 +143,21 @@ def tool_eval(self, length: int = 100, **kwargs): """ - 对指定的工具进行函数调用评估。 + 使用指定的模型和参数对输入的查询进行评估,并生成输出。 Args: - name (str): 工具名称。 - streaming (bool, optional): 是否以流的方式返回结果。默认为False。 - **kwargs: 其他参数。 - - Returns: - str 或 generator: 如果 streaming 为 False,则返回评估结果字符串;如果 streaming 为 True,则返回一个生成器,每次迭代返回评估结果字符串的一部分。 + query (str): 要评估的查询字符串。 + style (str, optional): 评估的风格。默认为 "通用"。 + length (int, optional): 输出文本的长度。默认为 100。 + **kwargs: 关键字参数,可以包含以下可选参数: + _sys_traceid (str): 系统跟踪ID。 + model_configs (dict): 模型配置字典,可以包含 "temperature" 和 "top_p" 两个键。 Raises: - ValueError: 如果未提供必要的参数 'query'。 + ValueError: 如果查询字符串为空,则引发 ValueError 异常。 + + Yields: + Output: 生成的输出对象,包含文本类型和文本内容。 """ traceid = kwargs.get("_sys_traceid") diff --git a/python/core/components/v2/object_recognize/__init__.py b/python/core/components/v2/object_recognize/__init__.py new file mode 100644 index 000000000..e511769e1 --- /dev/null +++ b/python/core/components/v2/object_recognize/__init__.py @@ -0,0 +1,10 @@ +# 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. diff --git a/python/core/components/v2/object_recognize/component.py b/python/core/components/v2/object_recognize/component.py new file mode 100644 index 000000000..a02afdfe2 --- /dev/null +++ b/python/core/components/v2/object_recognize/component.py @@ -0,0 +1,197 @@ +# 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. + +"""object recognize component.""" + +import base64 +import json +import os + +from appbuilder.core._client import HTTPClient +from appbuilder.core.component import Component +from appbuilder.core.message import Message +from appbuilder.core._exception import AppBuilderServerException, InvalidRequestArgumentError +from appbuilder.core.components.object_recognize.model import * +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace + +class ObjectRecognition(Component): + r""" + 提供通用物体及场景识别能力,即对于输入的一张图片(可正常解码,且长宽比适宜),输出图片中的多 + 个物体及场景标签。 + + Examples: + + .. code-block:: python + + import appbuilder + # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 + os.environ["APPBUILDER_TOKEN"] = '...' + + object_recognition = appbuilder.ObjectRecognition() + with open("./object_recognition_test.jepg", "rb") as f: + out = self.component.run(appbuilder.Message(content={"raw_image": f.read()})) + print(out.content) + + """ + name = "object_recognition" + version = "v1" + + manifests = [ + { + "name": "object_recognition", + "description": "提供通用物体及场景识别能力,即对于输入的一张图片,输出图片中的多个物体及场景标签。", + "parameters": { + "type": "object", + "properties": { + "img_url": { + "type": "string", + "description": "待识别图片的url,根据该url能够获取图片" + }, + "img_name": { + "type": "string", + "description": "待识别图片的文件名,用于生成图片url" + } + }, + "anyOf": [ + { + "required": [ + "img_url" + ] + }, + { + "required": [ + "img_name" + ] + } + ] + } + } + ] + + @HTTPClient.check_param + @components_run_trace + def run(self, message: Message, timeout: float = None, retry: int = 0) -> Message: + """ + 通用物体识别 + + Args: + message (Message): 输入图片或图片url下载地址用于执行识别操作。 + 例如: Message(content={"raw_image": b"..."}) 或 Message(content={"url": "https://image/download/url"})。 + timeout (float, optional): HTTP超时时间,默认为None。 + retry (int, optional): HTTP重试次数,默认为0。 + + Returns: + Message: 模型识别结果。 + 例如: Message(content={"result":[{"keyword":"苹果", + "score":0.94553,"root":"植物-蔷薇科"},{"keyword":"姬娜果","score":0.730442,"root":"植物-其它"}, + {"keyword":"红富士","score":0.505194,"root":"植物-其它"}]}) + """ + inp = ObjectRecognitionInMsg(**message.content) + req = ObjectRecognitionRequest() + if inp.raw_image: + req.image = base64.b64encode(inp.raw_image) + if inp.url: + req.url = inp.url + result, _ = self._recognize(req, timeout, retry) + result_dict = proto.Message.to_dict(result) + out = ObjectRecognitionOutMsg(**result_dict) + return Message(content=out.model_dump()) + + def _recognize(self, request: ObjectRecognitionRequest, timeout: float = None, + retry: int = 0, request_id: str = None) -> ObjectRecognitionResponse: + r"""调用底层接口进行通用物体与场景识别 + 参数: + request (obj: `ObjectRecognitionRequest`) : 通用物体与场景识别输入参数 + 返回: + response (obj: `ObjectRecognitionResponse`): 通用物体与场景识别返回结果 + """ + if not request.image and not request.url: + raise ValueError("request format error, one of image or url must be set") + + data = ObjectRecognitionRequest.to_dict(request) + if self.http_client.retry.total != retry: + self.http_client.retry.total = retry + headers = self.http_client.auth_header(request_id) + headers['content-type'] = 'application/x-www-form-urlencoded' + url = self.http_client.service_url("/v1/bce/aip/image-classify/v2/advanced_general") + response = self.http_client.session.post(url, headers=headers, data=data, timeout=timeout) + self.http_client.check_response_header(response) + data = response.json() + self.http_client.check_response_json(data) + request_id = self.http_client.response_request_id(response) + self.__class__._check_service_error(request_id,data) + object_response = ObjectRecognitionResponse.from_json(payload=json.dumps(data)) + object_response.request_id = request_id + return object_response, data + + @staticmethod + def _check_service_error(request_id: str, data: dict): + r"""个性化服务response参数检查 + 参数: + request (dict) : 通用物体与场景识别body返回 + 返回: + 无 + """ + if "error_code" in data or "error_msg" in data: + raise AppBuilderServerException( + request_id=request_id, + service_err_code=data.get("error_code"), + service_err_message=data.get("error_msg") + ) + + @components_run_stream_trace + def tool_eval(self, + img_url: str = '', + img_name: str = '', + **kwargs): + """ + 对给定的图片进行物体识别,并返回识别结果。 + + Args: + img_url (str, optional): 图片的URL地址。默认为空字符串。 + img_name (str, optional): 图片的名称。默认为空字符串。 + **kwargs: 其他关键字参数。 + + Returns: + Generator[Output, NoneType, NoneType]: 生成器,包含识别结果的输出对象。 + + Raises: + InvalidRequestArgumentError: 如果请求格式错误,例如文件名未设置或文件URL不存在,则引发此异常。 + + """ + traceid = kwargs.get("_sys_traceid") + if not img_url: + file_urls = kwargs.get("_sys_file_urls", {}) + img_path = img_name + if not img_path: + raise InvalidRequestArgumentError("request format error, file name is not set") + img_name = os.path.basename(img_path) + img_url = file_urls.get(img_name, None) + if not img_url: + raise InvalidRequestArgumentError(f"request format error, file {img_name} url does not exist") + score_threshold = kwargs.get("score_threshold", 0.5) + req = ObjectRecognitionRequest(url=img_url) + result, raw_data = self._recognize(req, request_id=traceid) + result = proto.Message.to_dict(result) + results = [] + for item in result["result"]: + if item["score"] < score_threshold and len(results) > 0: + continue + res = { + "物体或场景名称": item["keyword"], + "置信度": item["score"], + "所属类别": item["root"], + } + results.append(res) + res = json.dumps(results, ensure_ascii=False, indent=4) + yield self.create_output(type="text", text=res, raw_data=raw_data, visible_scope='llm') + yield self.create_output(type="text", text="", raw_data=raw_data, visible_scope='user') \ No newline at end of file diff --git a/python/core/components/v2/text_to_image/component.py b/python/core/components/v2/text_to_image/component.py index ba0be82a2..6da931ff9 100644 --- a/python/core/components/v2/text_to_image/component.py +++ b/python/core/components/v2/text_to_image/component.py @@ -30,7 +30,9 @@ class Text2Image(Component): r""" 文生图组件,即对于输入的文本,输出生成的图片url。 + Examples: + .. code-block:: python import appbuilder text_to_image = appbuilder.Text2Image() diff --git a/python/tests/component_tool_eval_cases.py b/python/tests/component_tool_eval_cases.py index af0fc04c9..9cea3b67d 100644 --- a/python/tests/component_tool_eval_cases.py +++ b/python/tests/component_tool_eval_cases.py @@ -273,6 +273,67 @@ def schemas(self): def outputs(self): return {"text": ["非植物"]} + +class ASRCase(Case): + def inputs(self): + audio_file_url ="https://bj.bcebos.com/v1/appbuilder/asr_test.pcm?authorization=bce-auth-v1" \ + "%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-11T10%3A56%3A41Z%2F-1%2Fhost" \ + "%2Fa6c4d2ca8a3f0259f4cae8ae3fa98a9f75afde1a063eaec04847c99ab7d1e411" + + return { + "file_url": audio_file_url + } + + def schemas(self): + return [text_schema] + + def outputs(self): + return {"text": ["北京科技馆"]} + +class ObjectRecognitionCase(Case): + def inputs(self): + img_url = "https://bj.bcebos.com/v1/appbuilder/object_recognize_test.png?" \ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-" \ + "11T11%3A00%3A19Z%2F-1%2Fhost%2F2c31bf29205f61e58df661dc80af31a1dc" \ + "1ba1de0a8f072bc5a87102bd32f9e3" + return { + "img_url": img_url + } + + def schemas(self): + return [text_schema] + + def outputs(self): + return {"text": ["苹果"]} + +class SimilarQuestionCase(Case): + def inputs(self): + return { + "query": "我想吃冰淇淋,哪里的冰淇淋比较好吃?" + } + + def schemas(self): + return [text_schema] + + def outputs(self): + return {"text": ["冰淇淋"]} + +class OralQueryGenerationCase(Case): + def inputs(self): + text = ('文档标题:在OPPO Reno5上使用视频超级防抖\n' + '文档摘要:OPPO Reno5上的视频超级防抖,视频超级防抖3.0,多代视频防抖算法积累,这一代依旧超级防抖超级稳。 开启视频超级' + '防抖 开启路径:打开「相机 > 视频 > 点击屏幕上方的“超级防抖”标识」 后置视频同时支持超级防抖和超级防抖Pro功能,开启超级' + '防抖后手机屏幕将出现超级防抖Pro开关,点击即可开启或关闭。 除此之外,前置视频同样加持防抖算法,边走边拍也能稳定聚焦脸部' + ',实时视频分享您的生活。') + return { + "text":text + } + + def schemas(self): + return [text_schema] + + def outputs(self): + return {"text": ["OPPO"]} component_tool_eval_cases = { "AnimalRecognition": AnimalRecognitionCase, @@ -291,4 +352,8 @@ def outputs(self): "StyleWriting": StyleWritingCase, "TreeMind": TreeMindCase, "PlantRecognition": PlantRecognitionCase, + "ASR": ASRCase, + "ObjectRecognition": ObjectRecognitionCase, + "SimilarQuestion": SimilarQuestionCase, + "OralQueryGeneration": OralQueryGenerationCase, } \ No newline at end of file diff --git a/python/tests/test_v2_asr.py b/python/tests/test_v2_asr.py new file mode 100644 index 000000000..585e4e5df --- /dev/null +++ b/python/tests/test_v2_asr.py @@ -0,0 +1,65 @@ +# Copyright (c) 2024 Baidu, Inc. 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 os +import unittest + +import requests + +import appbuilder +from appbuilder.core.component import ComponentOutput +from appbuilder.core._exception import InvalidRequestArgumentError +from appbuilder.core.components.v2 import ASR +from appbuilder.core.components.v2.asr.component import _convert as convert + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") +class TestASR(unittest.TestCase): + def setUp(self): + self.audio_file_url = "https://bj.bcebos.com/v1/appbuilder/asr_test.pcm?authorization=bce-auth-v1" \ + "%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-11T10%3A56%3A41Z%2F-1%2Fhost" \ + "%2Fa6c4d2ca8a3f0259f4cae8ae3fa98a9f75afde1a063eaec04847c99ab7d1e411" + self.com = ASR() + + def test_asr_run(self): + raw_audio = requests.get(self.audio_file_url).content + inp = appbuilder.Message(content={"raw_audio": raw_audio}) + out = self.com.run(inp) + self.assertIsNotNone(out) + print(out) + + def test_asr_tool_eval(self): + result = self.com.tool_eval(file_url=self.audio_file_url) + for res in result: + self.assertIsInstance(res, ComponentOutput) + print(res) + + def test_asr_tool_eval_error(self): + result = self.com.tool_eval() + with self.assertRaises(InvalidRequestArgumentError): + next(result) + + result = self.com.tool_eval(file_name='test_path') + with self.assertRaises(InvalidRequestArgumentError): + next(result) + + def test_convert_with_mp3(self): + file_type = ["mp3", "wav", "pcm", "m4a"] + for type in file_type: + with self.assertRaises(FileNotFoundError): + path = os.path.join(os.path.dirname(__file__), "test_audio") + path += ".{}".format(type) + convert(path, type) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/python/tests/test_v2_object_recognition.py b/python/tests/test_v2_object_recognition.py new file mode 100644 index 000000000..991fe42d2 --- /dev/null +++ b/python/tests/test_v2_object_recognition.py @@ -0,0 +1,67 @@ +# Copyright (c) 2024 Baidu, Inc. 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 os +import unittest + +import requests + +import appbuilder +from appbuilder.core.component import ComponentOutput +from appbuilder.core._exception import InvalidRequestArgumentError +from appbuilder.core.components.v2 import ObjectRecognition +from appbuilder.core._exception import AppBuilderServerException +from appbuilder.core.components.object_recognize.model import ObjectRecognitionRequest + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") +class TestObjectRecognition(unittest.TestCase): + def setUp(self): + self.com = ObjectRecognition() + self.image_url = "https://bj.bcebos.com/v1/appbuilder/object_recognize_test.png?" \ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-" \ + "11T11%3A00%3A19Z%2F-1%2Fhost%2F2c31bf29205f61e58df661dc80af31a1dc" \ + "1ba1de0a8f072bc5a87102bd32f9e3" + self.func_recognize = self.com._recognize + + def test_run(self): + raw_image = requests.get(self.image_url).content + message = appbuilder.Message(content={"raw_image": raw_image, "url": self.image_url}) + out = self.com.run(message) + self.assertIsNotNone(out) + print(out) + + def test_tool_eval(self): + result = self.com.tool_eval(img_url=self.image_url) + for res in result: + self.assertIsInstance(res, ComponentOutput) + print(res) + + def test_tool_eval_error(self): + result = self.com.tool_eval(img_name='test_path') + with self.assertRaises(InvalidRequestArgumentError): + next(result) + + result = self.com.tool_eval() + with self.assertRaises(InvalidRequestArgumentError): + next(result) + + def test_recognize_error(self): + with self.assertRaises(ValueError): + self.func_recognize(ObjectRecognitionRequest()) + + with self.assertRaises(AppBuilderServerException): + self.func_recognize(ObjectRecognitionRequest(url='test-url'), retry=1) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/python/tests/test_v2_oral_query_generat.py b/python/tests/test_v2_oral_query_generat.py new file mode 100644 index 000000000..91a7b1878 --- /dev/null +++ b/python/tests/test_v2_oral_query_generat.py @@ -0,0 +1,45 @@ +# Copyright (c) 2024 Baidu, Inc. 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 os +import unittest + +import appbuilder +from appbuilder.core.component import ComponentOutput +from appbuilder.core.components.v2 import OralQueryGeneration + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") +class TestOralQueryGeneration(unittest.TestCase): + def setUp(self): + self.com = OralQueryGeneration() + self.text = ('文档标题:在OPPO Reno5上使用视频超级防抖\n' + '文档摘要:OPPO Reno5上的视频超级防抖,视频超级防抖3.0,多代视频防抖算法积累,这一代依旧超级防抖超级稳。 开启视频超级' + '防抖 开启路径:打开「相机 > 视频 > 点击屏幕上方的“超级防抖”标识」 后置视频同时支持超级防抖和超级防抖Pro功能,开启超级' + '防抖后手机屏幕将出现超级防抖Pro开关,点击即可开启或关闭。 除此之外,前置视频同样加持防抖算法,边走边拍也能稳定聚焦脸部' + ',实时视频分享您的生活。') + + def test_run(self): + message = appbuilder.Message(self.text) + output = self.com.run(message) + self.assertIsNotNone(output) + print(output) + + def test_tool_eval(self): + result = self.com.tool_eval(text = self.text) + for str in result: + assert isinstance(str, ComponentOutput) + print(str) + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/python/tests/test_v2_similar_question.py b/python/tests/test_v2_similar_question.py new file mode 100644 index 000000000..52e9a9304 --- /dev/null +++ b/python/tests/test_v2_similar_question.py @@ -0,0 +1,44 @@ +# Copyright (c) 2024 Baidu, Inc. 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 os +import unittest + +import requests + +import appbuilder +from appbuilder.core.component import ComponentOutput +from appbuilder.core.components.v2 import SimilarQuestion + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") +class TestSimilarQuestion(unittest.TestCase): + def setUp(self): + self.com = SimilarQuestion() + + def test_run(self): + query = "我想吃冰淇淋,哪里的冰淇淋比较好吃?" + msg = appbuilder.Message(query) + out = self.com.run(msg) + self.assertIsNotNone(out) + print(out) + + def test_tool_eval(self): + query = "我想吃冰淇淋,哪里的冰淇淋比较好吃?" + for output in self.com.tool_eval(query): + self.assertIsInstance(output, ComponentOutput) + print(output) + +if __name__ == '__main__': + unittest.main() + From 7eca2041654a95dc9d10a9cd5d268f86f6471e8d Mon Sep 17 00:00:00 2001 From: peiwenYe <963623403@qq.com> Date: Tue, 10 Dec 2024 19:19:15 +0800 Subject: [PATCH 66/85] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=B8=BA=E5=AF=B9?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E5=AE=9E=E4=BE=8B=E7=9A=84manifests=E8=BF=9B?= =?UTF-8?q?=E8=A1=8C=E6=A3=80=E6=9F=A5=20(#653)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 组件输出schema校验取消对non_stream_tool_eval的检查 * 修改为对组件实例的manifests进行检查 * 修改为对组件实例的manifests进行检查 * 修改为对组件实例的manifests进行检查 --------- Co-authored-by: yepeiwen01 --- python/tests/component_check.py | 69 ++++++++++++++++------------- python/tests/test_all_components.py | 3 +- 2 files changed, 40 insertions(+), 32 deletions(-) diff --git a/python/tests/component_check.py b/python/tests/component_check.py index 6679073e4..fc5f51fac 100644 --- a/python/tests/component_check.py +++ b/python/tests/component_check.py @@ -60,44 +60,51 @@ class ManifestValidRule(RuleBase): """ 通过尝试将component的manifest转换为pydantic模型来检查manifest是否符合规范 """ - def __init__(self): + def __init__(self, **kwargs): super().__init__() self.rule_name = "ManifestValidRule" + self.component_tool_eval_cases = kwargs.get("component_tool_eval_cases", {}) def check(self, component_cls) -> CheckInfo: check_pass_flag = True invalid_details = [] + component_cls_name = component_cls.__name__ + if component_cls_name not in self.component_tool_eval_cases: + invalid_details.append("{} 没有添加测试case到 component_tool_eval_cases 中".format(component_cls_name)) + else: + component_case = self.component_tool_eval_cases[component_cls_name]() + init_args = component_case.init_args() - - try: - if not hasattr(component_cls, "manifests"): - raise ValueError("No manifests found") - manifests = component_cls.manifests - # NOTE(暂时检查manifest中的第一个mainfest) - if not manifests or len(manifests) == 0: - raise ValueError("No manifests found") - manifest = manifests[0] - tool_name = manifest['name'] - tool_desc = manifest['description'] - schema = manifest["parameters"] - schema["title"] = tool_name - # 第一步,将json schema转换为pydantic模型 - pydantic_model = json_schema_to_pydantic_model(schema, tool_name) - check_to_json = pydantic_model.schema_json() - json_to_dict = json.loads(check_to_json) - - if "properties" in schema: - properties = schema["properties"] - for key, value in properties.items(): - if "type" not in value: - invalid_details.append("\'type' must be in properties item: {}".format(key)) - if "description" not in value: - invalid_details.append("\'description' must be in properties item: {}".format(key)) - - except Exception as e: - print(e) - check_pass_flag = False - invalid_details.append(str(e)) + try: + component_obj = component_cls(**init_args) + if not hasattr(component_obj, "manifests"): + raise ValueError("No manifests found") + manifests = component_obj.manifests + # NOTE(暂时检查manifest中的第一个mainfest) + if not manifests or len(manifests) == 0: + raise ValueError("No manifests found") + manifest = manifests[0] + tool_name = manifest['name'] + tool_desc = manifest['description'] + schema = manifest["parameters"] + schema["title"] = tool_name + # 第一步,将json schema转换为pydantic模型 + pydantic_model = json_schema_to_pydantic_model(schema, tool_name) + check_to_json = pydantic_model.schema_json() + json_to_dict = json.loads(check_to_json) + + if "properties" in schema: + properties = schema["properties"] + for key, value in properties.items(): + if "type" not in value: + invalid_details.append("\'type' must be in properties item: {}".format(key)) + if "description" not in value: + invalid_details.append("\'description' must be in properties item: {}".format(key)) + + except Exception as e: + print(e) + check_pass_flag = False + invalid_details.append(str(e)) if len(invalid_details) > 0: check_pass_flag = False diff --git a/python/tests/test_all_components.py b/python/tests/test_all_components.py index ca1b94cca..83c605e1e 100644 --- a/python/tests/test_all_components.py +++ b/python/tests/test_all_components.py @@ -11,7 +11,8 @@ from component_collector import get_all_components, get_v2_components, get_component_white_list from component_tool_eval_cases import component_tool_eval_cases -register_component_check_rule("ManifestValidRule", ManifestValidRule, {}) +register_component_check_rule("ManifestValidRule", ManifestValidRule, \ + {"component_tool_eval_cases": component_tool_eval_cases}) register_component_check_rule("MainfestMatchToolEvalRule", MainfestMatchToolEvalRule, {}) register_component_check_rule("ToolEvalInputNameRule", ToolEvalInputNameRule, {}) register_component_check_rule("ToolEvalOutputJsonRule", ToolEvalOutputJsonRule, \ From 3ee57d1fef17f4336ee9f611f7fecd4f410e029f Mon Sep 17 00:00:00 2001 From: peiwenYe <963623403@qq.com> Date: Wed, 11 Dec 2024 14:08:20 +0800 Subject: [PATCH 67/85] json_to_pydantic replace(unique_items, Set) and BugFix (#655) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 组件输出schema校验取消对non_stream_tool_eval的检查 * 修改为对组件实例的manifests进行检查 * 修改为对组件实例的manifests进行检查 * 修改为对组件实例的manifests进行检查 * 组件初始化前设置环境变量 * 修改content取值方式 * 输入校验 json_to_pydantic: replace(unique_items, Set) --------- Co-authored-by: yepeiwen01 --- python/tests/component_check.py | 11 ++++++++--- python/utils/json_schema_to_model.py | 11 ++++++++++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/python/tests/component_check.py b/python/tests/component_check.py index fc5f51fac..52d919a43 100644 --- a/python/tests/component_check.py +++ b/python/tests/component_check.py @@ -73,6 +73,8 @@ def check(self, component_cls) -> CheckInfo: invalid_details.append("{} 没有添加测试case到 component_tool_eval_cases 中".format(component_cls_name)) else: component_case = self.component_tool_eval_cases[component_cls_name]() + envs = component_case.envs() + os.environ.update(envs) init_args = component_case.init_args() try: @@ -105,7 +107,10 @@ def check(self, component_cls) -> CheckInfo: print(e) check_pass_flag = False invalid_details.append(str(e)) - + + for env in envs: + os.environ.pop(env) + if len(invalid_details) > 0: check_pass_flag = False invalid_details = ",".join(invalid_details) @@ -292,9 +297,9 @@ def _gather_iter_outputs(self, outputs): if out_type == "text": text_output += content.text.info elif out_type == "oral_text": - oral_text_output += content.oral_text.info + oral_text_output += content.text.info elif out_type == "code": - code_output += content.code.code + code_output += content.text.code return { "text": text_output, "oral_text": oral_text_output, diff --git a/python/utils/json_schema_to_model.py b/python/utils/json_schema_to_model.py index a81b48c77..2056bdfa5 100644 --- a/python/utils/json_schema_to_model.py +++ b/python/utils/json_schema_to_model.py @@ -88,7 +88,8 @@ def json_schema_to_pydantic_model(json_schema: dict, name_override: str) -> Base class_title = json_schema["title"] pydantic_models_as_str = sed_pydantic_str(pydantic_models_as_str, class_title) - + pydantic_models_as_str = pydantic_models_as_str.replace("unique_items", "Set") + with NamedTemporaryFile(suffix=".py", delete=False) as temp_file: temp_file_path = Path(temp_file.name).resolve() temp_file.write(pydantic_models_as_str.encode()) @@ -119,6 +120,14 @@ def json_schema_to_pydantic_model(json_schema: dict, name_override: str) -> Base "type": "string", "description": "待识别图片的文件名,用于生成图片url" }, + "files": { + "type": "array", + "items": { + "type": "string", + }, + "uniqueItems": True + } + }, "anyOf": [ { From 5138b79c5806a993f5d0bf35adc0495c4522e79b Mon Sep 17 00:00:00 2001 From: userpj Date: Thu, 12 Dec 2024 10:27:34 +0800 Subject: [PATCH 68/85] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=AE=9E=E6=97=B6?= =?UTF-8?q?=E8=AF=AD=E9=9F=B3=E9=80=9A=E8=AF=9D=E5=8A=9F=E8=83=BDcookbook?= =?UTF-8?q?=E6=96=87=E6=A1=A3=20(#656)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 更新实时语音通话功能cookbook文档 * update * update * update --- .../advanced_application/agent_speech.ipynb | 35 +++++++++++++++---- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/cookbooks/advanced_application/agent_speech.ipynb b/cookbooks/advanced_application/agent_speech.ipynb index ba9019d19..e264c97a8 100644 --- a/cookbooks/advanced_application/agent_speech.ipynb +++ b/cookbooks/advanced_application/agent_speech.ipynb @@ -8,7 +8,7 @@ "**注意⚠️:实时语音功能目前处于内测阶段,使用过程中有任何问题,欢迎提issue或微信群反馈~**\n", "\n", "## 目标\n", - "实现一个实时语音对话功能,用户可以参考cookbook代码,通过AppBuilder-SDK将实时语音功能很好地融入自己的平台、应用中。\n", + "实现一个实时语音对话功能,支持多种语音音色。用户可以参考cookbook代码,通过AppBuilder-SDK将实时语音功能很好地融入自己的平台、应用中。\n", "\n", "## 实现原理\n", "通过循环不断处理用户的语音,将语音转文本,然后进行对话,最后将对话结果通过TTS进行播报。。\n", @@ -17,6 +17,7 @@ "* 使用大模型的 TTS 进行文本转语音并进行播报。\n", "\n", "## 前置条件\n", + "* 使用内置ASR、TTS组件之前,请先开通组件服务并够买额度,可参考[开通组件服务](https://cloud.baidu.com/doc/AppBuilder/s/Glqb6dfiz#3%E3%80%81%E5%BC%80%E9%80%9A%E7%BB%84%E4%BB%B6%E6%9C%8D%E5%8A%A1)\n", "* pip安装pyaudio、webrtcvad依赖包\n", "* 给程序开放麦克风权限\n", "* 创建好自己的Agent应用\n", @@ -90,7 +91,7 @@ " time.sleep(1)\n", " continue\n", " print(\"音频记录结束\")\n", - " \n", + "\n", " # ASR\n", " print(\"开始执行ASR...\")\n", " query = self.run_asr(audio_path)\n", @@ -106,7 +107,7 @@ " print(\"链接地址:\", result)\n", " answer = answer.replace(result, \"\")\n", " print(\"answer:\", answer)\n", - " \n", + "\n", " # TTS\n", " print(\"开始执行TTS并播报...\")\n", " self.run_tts_and_play_audio(answer)\n", @@ -151,8 +152,13 @@ " return history_speech_times * DURATION\n", "\n", " def run_tts_and_play_audio(self, text: str):\n", + " # AppBuilder内置的TTS使用文档,用户可根据文档调整参数:https://github.com/baidubce/app-builder/tree/master/python/core/components/tts\n", " msg = self.tts.run(\n", " appbuilder.Message(content={\"text\": text}),\n", + " speed=5,\n", + " pitch=5,\n", + " volume=5,\n", + " person=0,\n", " audio_type=\"pcm\",\n", " model=\"paddlespeech-tts\",\n", " stream=True,\n", @@ -169,6 +175,7 @@ " stream.stop_stream()\n", " stream.close()\n", "\n", + " # AppBuilder内置的ASR使用文档,用户可根据文档调整参数:https://github.com/baidubce/app-builder/blob/master/python/core/components/asr/README.md\n", " def run_asr(self, audio_path: str):\n", " with open(audio_path, \"rb\") as f:\n", " content_data = {\"audio_format\": \"wav\", \"raw_audio\": f.read(), \"rate\": 16000}\n", @@ -194,15 +201,29 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**使用方法**\n", + "## 使用方法\n", "\n", "直接运行程序即可。\n", "\n", "用户也可以将下面的功能模块替换成自己的其他实现或模型:\n", "* record_audio: 录音\n", - "* run_asr: 语音识别语音识别\n", - "* run_agent: Agent对话功能\n", - "* run_tts_and_play_audio:回复的语音生成并播报" + "* run_asr: 语音识别语音识别,[AppBuilder ASR组件使用文档](https://github.com/baidubce/app-builder/blob/master/python/core/components/asr/README.md)\n", + "* run_agent: Agent对话功能,[AppBuilder TTS组件使用文档](https://github.com/baidubce/app-builder/blob/master/python/core/components/tts/README.md)\n", + "* run_tts_and_play_audio:回复的语音生成并播报\n", + "\n", + "**AppBuilder TTS组件参数**\n", + "| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 |\n", + "|------------|---------|------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------|\n", + "| message | String | 是 | 待转成语音的文本 | Message(content={\"text\": \"需合成的文本\"}) |\n", + "| model | String | 否 | 默认是`baidu-tts`模型,可选值:`paddlespeech-tts`、`baidu-tts` | paddlespeech-tts |\n", + "| speed | Integer | 否 | 语音语速,默认是5中等语速,取值范围在0~15之间,仅当模型为`baidu-tts`参数有效,如果模型为`paddlespeech-tts`,参数自动失效 | 5 |\n", + "| pitch | Integer | 否 | 语音音调,默认是5中等音调,取值范围在0~15之间,仅当模型为`baidu-tts`参数有效,如果模型为`paddlespeech-tts`,参数自动失效 | 5 |\n", + "| volume | Integer | 否 | 语音音量,默认是5中等音量,取值范围在0~15之间,,仅当模型为`baidu-tts`参数有效,如果模型为`paddlespeech-tts`,参数自动失效 | 5 |\n", + "| person | Integer | 否 | 语音人物特征,默认是0(度小美),普通音库可选值包括: 0(度小美)、1(度小宇)、3(度逍遥-基础)、4(度丫丫);精品音库包括:5003(度逍遥-精品)、5118(度小鹿)、106(度博文)、110(度小童)、111(度小萌)、103(度米朵)、5(度小娇);臻品音库包括:4003(度逍遥-情感男声)、4106(度博文-专业男主播)、4115(度小贤-电台男主播)、4119(度小鹿-甜美女声)、4105(度灵儿-清激女声)、4117(度小乔-活泼女声)、4100(度小雯-活力女主播)、4103(度米朵-可爱女声)、4144(度姗姗-娱乐女声)、4278(度小贝-知识女主播)、4143(度清风-配音男声)、4140(度小新-专业女主播)、4129(度小彦-知识男主播)、4149(度星河-广告男声)、4254(度小清-广告女声)、4206(度博文-综艺男声)、4226(南方-电台女主播)。仅当模型为`baidu-tts`参数有效,如果模型为`paddlespeech-tts`,参数自动失效 | 0 |\n", + "| audio_type | String | 否 | 音频文件格式,如果使用`baidu-tts`模型可选`mp3`, `wav`; 如果使用`paddlespeech-tts`模型非流式返回,参数只能设为`wav`;如果使用`paddlespeech-tts`模型流式返回,参数只能设为`pcm` | wav |\n", + "| stream | Bool | 否 | 默认是False, 目前`paddlespeech-tts`模型支持流式返回,`baidu-tts`模型不支持流式返回 | False |\n", + "| retry | Integer | 否 | HTTP重试次数 | 3 |\n", + "| timeout | Integer | 否 | HTTP超时时间 | 5 |" ] } ], From 214c5ebf23f48456587a8bd95af90d71cace77b2 Mon Sep 17 00:00:00 2001 From: C9luster <138663536+C9luster@users.noreply.github.com> Date: Thu, 12 Dec 2024 11:07:20 +0800 Subject: [PATCH 69/85] =?UTF-8?q?=E6=9B=B4=E6=96=B0model=5Fname=E6=8A=BD?= =?UTF-8?q?=E5=8F=96=E6=96=B9=E5=BC=8F=20(#657)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 更新model_name抽取方式 * update * update * update * update --------- Co-authored-by: yinjiaqi --- python/utils/json_schema_to_model.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/utils/json_schema_to_model.py b/python/utils/json_schema_to_model.py index 2056bdfa5..02a79c9b8 100644 --- a/python/utils/json_schema_to_model.py +++ b/python/utils/json_schema_to_model.py @@ -13,6 +13,7 @@ NON_ALPHANUMERIC = re.compile(r"[^a-zA-Z0-9]+") +STARTS_WITH_NUMBER = re.compile(r'[0-9]+') UPPER_CAMEL_CASE = re.compile(r"[A-Z][a-zA-Z0-9]+") LOWER_CAMEL_CASE = re.compile(r"[a-z][a-zA-Z0-9]+") @@ -22,7 +23,7 @@ class BadJsonSchema(Exception): def _to_camel_case(name: str) -> str: if any(NON_ALPHANUMERIC.finditer(name)): - return "".join(term.lower().title() for term in NON_ALPHANUMERIC.split(name)) + return "".join(term.lower().title() if not STARTS_WITH_NUMBER.match(term) else term.lower() for term in NON_ALPHANUMERIC.split(name)) if UPPER_CAMEL_CASE.match(name): return name if LOWER_CAMEL_CASE.match(name): From 797c3cb37628a494b1c18f5bbd3804d9e3ffb9cc Mon Sep 17 00:00:00 2001 From: C9luster <138663536+C9luster@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:27:45 +0800 Subject: [PATCH 70/85] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=8D=95=E5=85=83?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E6=B5=81=E6=B0=B4=E7=BA=BF=E8=84=9A=E6=9C=AC?= =?UTF-8?q?=20(#660)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: yinjiaqi --- .github/workflows/python-package.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 2beeff461..bd850fb9d 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -109,6 +109,7 @@ jobs: echo "检测到Python或Shell文件被更改(根据环境变量APPBUILDER_PYTHON_TESTS),准备启动单元测试Install dependencies部分..." cd cicd/app-builder pwd + sudo apt-get update sudo apt-get install ffmpeg libavcodec-extra python3 -m ensurepip --upgrade python3 -m pip install --upgrade pip From 0bf0ed74443b07fc7b6c5d65b697ac7165840d91 Mon Sep 17 00:00:00 2001 From: C9luster <138663536+C9luster@users.noreply.github.com> Date: Thu, 12 Dec 2024 18:53:48 +0800 Subject: [PATCH 71/85] =?UTF-8?q?=E6=96=B0=E5=A2=9EJson=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=20(#661)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 新增Json数据类型 Co-authored-by: yinjiaqi --- python/core/component.py | 8 +++++++- python/tests/test_base_component.py | 12 +++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/python/core/component.py b/python/core/component.py index c316b826f..4254bbefc 100644 --- a/python/core/component.py +++ b/python/core/component.py @@ -126,6 +126,8 @@ class FunctionCall(BaseModel, extra='allow'): name: str = Field(default="", description="工具名") arguments: dict = Field(default={}, description="参数列表") +class Json(BaseModel, extra='allow'): + data: str = Field(default="", description="json数据") class Content(BaseModel): name: str = Field(default="", @@ -140,7 +142,7 @@ class Content(BaseModel): description="耗时、性能、内存等trace及debug所需信息") type: str = Field(default="text", description="代表event 类型,包括 text、code、files、urls、oral_text、references、image、chart、audio该字段的取值决定了下面text字段的内容结构") - text: Union[Text, Code, Files, Urls, OralText, References, Image, Chart, Audio, Plan, FunctionCall] = Field(default=Text, + text: Union[Text, Code, Files, Urls, OralText, References, Image, Chart, Audio, Plan, Json, FunctionCall] = Field(default=Text, description="代表当前 event 元素的内容,每一种 event 对应的 text 结构固定") @field_validator('text', mode='before') @@ -167,6 +169,8 @@ def set_text(cls, v, values, **kwargs): return Plan(**v) elif values.data['type'] == 'function_call': return FunctionCall(**v) + elif values.data['type'] == 'json': + return Json(**v) else: raise ValueError(f"Invalid value for 'type': {values['type']}") @@ -513,6 +517,8 @@ def create_output(cls, type, text, role="tool", name="", visible_scope="all", ra text = {"url": text} elif type == "oral_text": text = {"info": text} + elif type == "json": + text = {"data": text} else: raise ValueError("Only when type=text/code/urls/oral_text, string text is allowed! Please give dict text") elif isinstance(text, dict): diff --git a/python/tests/test_base_component.py b/python/tests/test_base_component.py index 7c60c5dd0..94d5ff4e2 100644 --- a/python/tests/test_base_component.py +++ b/python/tests/test_base_component.py @@ -14,10 +14,12 @@ def test_valid_output_with_str(self): out2 = self.component.create_output(type="code", text="import appbuilder") out3 = self.component.create_output(type="urls", text="http://www.baidu.com") out4 = self.component.create_output(type="oral_text", text="你是哪个") + out5 = self.component.create_output(type="json", text="{'key':'value'}") self.assertIsInstance(out1, ComponentOutput) self.assertIsInstance(out2, ComponentOutput) self.assertIsInstance(out3, ComponentOutput) self.assertIsInstance(out4, ComponentOutput) + self.assertIsInstance(out5, ComponentOutput) def test_valid_output_with_dict(self): output1 = self.component.create_output(type="text", text={"info": "1"}) @@ -55,15 +57,7 @@ def test_valid_output_type_with_same_key(self): def test_invalid_output_type_json(self): with self.assertRaises(ValueError): - output = self.component.create_output(type="json", text="") - # with self.assertRaises(AssertionError): - # output = self.component.create_output(type="files", text={}) - # with self.assertRaises(AssertionError): - # output = self.component.create_output(type="references", text={"info": "text"}) - # with self.assertRaises(AssertionError): - # output = self.component.create_output(type="image", text={"url": "https://example.com/img"}) - # with self.assertRaises(AssertionError): - # output = self.component.create_output(type="chart", text={"url": "https://example.com/chart"}) + output = self.component.create_output(type="test", text="") if __name__ == '__main__': From 16cd3e6b8ddd3ee21c3fc93fdf36f9d3ee3d9d2a Mon Sep 17 00:00:00 2001 From: C9luster <138663536+C9luster@users.noreply.github.com> Date: Fri, 13 Dec 2024 14:40:33 +0800 Subject: [PATCH 72/85] =?UTF-8?q?=E6=81=A2=E5=A4=8D=E9=83=A8=E5=88=86Compo?= =?UTF-8?q?nents=E7=BB=84=E4=BB=B6=E7=9A=84=E7=A7=81=E6=9C=89=E5=87=BD?= =?UTF-8?q?=E6=95=B0,=E6=9B=B4=E6=96=B0=E7=BB=84=E4=BB=B6chart=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E7=B1=BB=E5=9E=8Bkey=5Flist=20(#662)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: yinjiaqi --- python/core/component.py | 2 +- .../v2/llms/oral_query_generation/component.py | 8 ++++---- python/core/components/v2/table_ocr/component.py | 4 ++-- python/core/components/v2/text_to_image/component.py | 12 ++++++------ 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/python/core/component.py b/python/core/component.py index 4254bbefc..3b892c619 100644 --- a/python/core/component.py +++ b/python/core/component.py @@ -537,7 +537,7 @@ def create_output(cls, type, text, role="tool", name="", visible_scope="all", ra elif type == "image": key_list = ["filename", "url"] elif type == "chart": - key_list = ["filename", "url"] + key_list = ["type", "data"] elif type == "audio": key_list = ["filename", "url"] elif type == "plan": diff --git a/python/core/components/v2/llms/oral_query_generation/component.py b/python/core/components/v2/llms/oral_query_generation/component.py index 67359d98c..55ca08f5b 100644 --- a/python/core/components/v2/llms/oral_query_generation/component.py +++ b/python/core/components/v2/llms/oral_query_generation/component.py @@ -101,7 +101,7 @@ def __init__( OralQueryGenerationArgs, model=model, secret_key=secret_key, gateway=gateway, lazy_certification=lazy_certification) - def _regenerate_output(self, model_output, output_format): + def regenerate_output(self, model_output, output_format): """ 兼容老版本的输出格式 """ @@ -130,7 +130,7 @@ def _regenerate_output(self, model_output, output_format): regenerated_output = '\n'.join([f'{index}. {query}' for index, query in enumerate(queries, 1)]) return regenerated_output - def _completion(self, version, base_url, request, timeout: float = None, + def completion(self, version, base_url, request, timeout: float = None, retry: int = 0): r"""Send a byte array of an audio file to obtain the result of speech recognition.""" @@ -187,13 +187,13 @@ def run(self, message, query_type='全部', output_format='str', stream=False, t model_config = self.get_model_config(model_config_inputs) request = self.gene_request('', inputs, response_mode, user_id, model_config) - response = self._completion(self.version, self.base_url, request) + response = self.completion(self.version, self.base_url, request) if response.error_no != 0: raise AppBuilderServerException(service_err_code=response.error_no, service_err_message=response.error_msg) result = response.to_message() - result.content = self._regenerate_output(result.content, output_format) + result.content = self.regenerate_output(result.content, output_format) return result diff --git a/python/core/components/v2/table_ocr/component.py b/python/core/components/v2/table_ocr/component.py index 114c256eb..915cdf926 100644 --- a/python/core/components/v2/table_ocr/component.py +++ b/python/core/components/v2/table_ocr/component.py @@ -152,7 +152,7 @@ def _check_service_error(request_id: str, data: dict): service_err_message=data.get("error_msg") ) - def _get_table_markdown(self, tables_result): + def get_table_markdown(self, tables_result): """ 将表格识别结果转换为Markdown格式。 @@ -222,7 +222,7 @@ def tool_eval(self, req.cell_contents = "false" resp, raw_data = self._recognize(req, request_id=traceid) tables_result = proto.Message.to_dict(resp)["tables_result"] - markdowns = self._get_table_markdown(tables_result) + markdowns = self.get_table_markdown(tables_result) result[file_name] = markdowns result = json.dumps(result, ensure_ascii=False) diff --git a/python/core/components/v2/text_to_image/component.py b/python/core/components/v2/text_to_image/component.py index 6da931ff9..594cbf6bd 100644 --- a/python/core/components/v2/text_to_image/component.py +++ b/python/core/components/v2/text_to_image/component.py @@ -271,7 +271,7 @@ def _recognize( while True: request = Text2ImageQueryRequest(task_id=taskId) - text2ImageQueryResponse, data = self._queryText2ImageData(request, request_id=request_id) + text2ImageQueryResponse, data = self.queryText2ImageData(request, request_id=request_id) if text2ImageQueryResponse.data.task_progress is not None: task_progress = float(text2ImageQueryResponse.data.task_progress) if math.isclose(1.0, task_progress, rel_tol=1e-9, abs_tol=0.0): @@ -285,11 +285,11 @@ def _recognize( time.sleep(task_request_time) task_request_time += 1 - img_urls = self._extract_img_urls(text2ImageQueryResponse) + img_urls = self.extract_img_urls(text2ImageQueryResponse) return img_urls, data - def _queryText2ImageData( + def queryText2ImageData( self, request: Text2ImageQueryRequest, timeout: float = None, @@ -321,11 +321,11 @@ def _queryText2ImageData( data = response.json() self.http_client.check_response_json(data) request_id = self.http_client.response_request_id(response) - self.__class__._check_service_error(request_id, data) + self.__class__.check_service_error(request_id, data) response = Text2ImageQueryResponse(**data) return response, data - def _extract_img_urls(self, response: Text2ImageQueryResponse): + def extract_img_urls(self, response: Text2ImageQueryResponse): """ 从作画生成的返回结果中提取图片url。 @@ -347,7 +347,7 @@ def _extract_img_urls(self, response: Text2ImageQueryResponse): return img_urls @staticmethod - def _check_service_error(request_id: str, data: dict): + def check_service_error(request_id: str, data: dict): """ 检查服务错误信息 From b06664f87f8bbe9b07d606ba99b84fe8a3106bd2 Mon Sep 17 00:00:00 2001 From: userpj Date: Fri, 13 Dec 2024 15:40:18 +0800 Subject: [PATCH 73/85] =?UTF-8?q?chainlit=E6=96=B0=E5=A2=9Echatflow=20agen?= =?UTF-8?q?t=E6=94=AF=E6=8C=81=20(#663)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- python/core/agent.py | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/python/core/agent.py b/python/core/agent.py index 4c248d485..d6d1df809 100644 --- a/python/core/agent.py +++ b/python/core/agent.py @@ -26,7 +26,7 @@ from appbuilder.core.component import Component from appbuilder.core.message import Message from appbuilder.utils.logger_util import logger -from appbuilder.core.console.appbuilder_client.data_class import ToolChoiceFunction, ToolChoice +from appbuilder.core.console.appbuilder_client.data_class import ToolChoiceFunction, ToolChoice, Action # 流式场景首包超时时,最大重试次数 MAX_RETRY_COUNT = 3 @@ -197,7 +197,7 @@ def run(self, message: Message, stream: bool=False): conn.close() """ - + component: Component user_session_config: Optional[Union[Any, str]] = None user_session: Optional[Any] = None @@ -556,6 +556,7 @@ def chainlit_agent(self, host='0.0.0.0', port=8091): self.prepare_chainlit_readme() conversation_ids = [] + interrupt_dict = {} def _chat(message: cl.Message): if len(conversation_ids) == 0: @@ -566,8 +567,31 @@ def _chat(message: cl.Message): file_id = self.component.upload_local_file( conversation_id, message.elements[0].path) file_ids.append(file_id) - return self.component.run(conversation_id=conversation_id, query=message.content, file_ids=file_ids, - stream=True, tool_choice=self.tool_choice) + + interrupt_ids = interrupt_dict.get(conversation_id, []) + interrupt_event_id = interrupt_ids.pop() if len(interrupt_ids) > 0 else None + action = None + if interrupt_event_id is not None: + action = Action.create_resume_action(interrupt_event_id) + + tmp_message = self.component.run(conversation_id=conversation_id, query=message.content, file_ids=file_ids, + stream=True, tool_choice=self.tool_choice, action=action) + res_message=list(tmp_message.content) + + interrupt_event_id = None + for ans in res_message: + for event in ans.events: + if event.content_type == "chatflow_interrupt": + interrupt_event_id = event.detail.get("interrupt_event_id") + if event.content_type == "publish_message" and event.event_type == "chatflow": + answer = event.detail.get("message") + ans.answer += answer + + if interrupt_event_id is not None: + interrupt_ids.append(interrupt_event_id) + interrupt_dict[conversation_id] = interrupt_ids + tmp_message.content = res_message + return tmp_message @cl.on_chat_start async def start(): @@ -575,6 +599,7 @@ async def start(): request_id = str(uuid.uuid4()) init_context(session_id=session_id, request_id=request_id) conversation_ids.append(self.component.create_conversation()) + interrupt_dict[conversation_ids[-1]] = [] @cl.on_message # this function will be called every time a user inputs a message in the UI async def main(message: cl.Message): From 574d1693d31902cb69c80232eee62a004fbd54ea Mon Sep 17 00:00:00 2001 From: userpj Date: Fri, 13 Dec 2024 17:19:45 +0800 Subject: [PATCH 74/85] =?UTF-8?q?chainlit=20chainlit=5Fagent=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=B7=A5=E4=BD=9C=E6=B5=81Agent=E5=BA=94=E7=94=A8=20(?= =?UTF-8?q?#664)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chainlit chainlit_agent支持工作流Agent应用 * update --- cookbooks/components/agent_runtime.ipynb | 13 ++++++++----- .../agent_runtime_with_chainlit_chatflow.png | Bin 0 -> 222045 bytes docs/BasisModule/Deployment/agentruntime.md | 10 ++++------ 3 files changed, 12 insertions(+), 11 deletions(-) create mode 100644 cookbooks/components/image/agent_runtime_with_chainlit_chatflow.png diff --git a/cookbooks/components/agent_runtime.ipynb b/cookbooks/components/agent_runtime.ipynb index 63ee56d78..b222c6e2e 100644 --- a/cookbooks/components/agent_runtime.ipynb +++ b/cookbooks/components/agent_runtime.ipynb @@ -353,7 +353,7 @@ "source": [ "**1.3 对话框交互AppBuilderClient**\n", "\n", - "基于 chainlit 的对话框交互AppBuilderClient,可实现对话交互。\n", + "基于 chainlit 的对话框交互AppBuilderClient,可实现对话交互。支持工作流Agent、自主规划Agent应用。\n", "\n", "执行下面的代码,会启动一个 chainlit 页面,页面地址:0.0.0.0:8091。可在页面上上传文档(可选)、执行对话。" ] @@ -372,8 +372,8 @@ "os.environ[\"APPBUILDER_TOKEN\"] = \"...\"\n", "# 使用之前,在官网个人空间获取应用ID,如下图\n", "app_id= \"...\"\n", - "agent_builder = appbuilder.AppBuilderClient(app_id)\n", - "agent = appbuilder.AgentRuntime(component=agent_builder)\n", + "client = appbuilder.AppBuilderClient(app_id)\n", + "agent = appbuilder.AgentRuntime(client)\n", "agent.chainlit_agent(port=8091)" ] }, @@ -385,8 +385,11 @@ "在官网个人空间获取应用ID\n", "![get app_id](../app_builder_resources/app_id.png)\n", "\n", - "使用服务上传文件并对话示意图如下所示\n", - "![chainlit demo](./image/agent_runtime_with_chainlit_agent.png)" + "使用服务上传文件并对话示例图如下所示\n", + "![chainlit demo](./image/agent_runtime_with_chainlit_agent.png)\n", + "\n", + "使用工作流Agent应用对话示例如下图所示\n", + "![chainlit demo](./image/agent_runtime_with_chainlit_chatflow.png)" ] }, { diff --git a/cookbooks/components/image/agent_runtime_with_chainlit_chatflow.png b/cookbooks/components/image/agent_runtime_with_chainlit_chatflow.png new file mode 100644 index 0000000000000000000000000000000000000000..1c0ba38db736f39c80932b10281a80bcdf1607ac GIT binary patch literal 222045 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

_uONLc)7XYO!l?CsXE_WVuK7OIEY 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 DVqYfH literal 0 HcmV?d00001 diff --git a/docs/BasisModule/Deployment/agentruntime.md b/docs/BasisModule/Deployment/agentruntime.md index fc18c4de8..84759e5f3 100644 --- a/docs/BasisModule/Deployment/agentruntime.md +++ b/docs/BasisModule/Deployment/agentruntime.md @@ -151,7 +151,7 @@ agent.chainlit_demo(port=8091) ``` ### 5、将 appbuilder client 服务化,提供 chainlit demo 页面`AgentRuntime.chainlit_agent(host='0.0.0.0', port=8091)` - +目前支持工作流Agent、自主规划Agent应用。 #### 方法参数 @@ -172,9 +172,7 @@ import os os.environ["APPBUILDER_TOKEN"] = '...' app_id = '...' # 已发布AppBuilder应用ID,可在console端查看 -builder = appbuilder.AppBuilderClient(app_id) -conversation_id = builder.create_conversation() -agent = appbuilder.AgentRuntime(component=builder) -message = appbuilder.Message({"query": "北京今天天气怎么样"}) -print(agent.chat(message, stream=False)) +client = appbuilder.AppBuilderClient(app_id) +agent = appbuilder.AgentRuntime(component=client) +agent.chainlit_agent(port=8091) ``` \ No newline at end of file From 44e732754aaa1caf11cf71ce0c6bba3c211562e9 Mon Sep 17 00:00:00 2001 From: C9luster <138663536+C9luster@users.noreply.github.com> Date: Mon, 16 Dec 2024 16:45:20 +0800 Subject: [PATCH 75/85] =?UTF-8?q?=E6=9B=B4=E6=96=B0Test=E5=AF=B9Json=20sch?= =?UTF-8?q?ema=E7=9A=84=E6=A3=80=E6=B5=8B=20(#665)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: yinjiaqi --- python/tests/component_schemas.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/python/tests/component_schemas.py b/python/tests/component_schemas.py index 58a42779a..5f70bdaa2 100644 --- a/python/tests/component_schemas.py +++ b/python/tests/component_schemas.py @@ -245,6 +245,21 @@ "required": ["filename", "url"] } +json_schema = copy.deepcopy(base_item_schema) +json_schema["$schema"] = "json_schema" +json_schema["properties"]["type"] = { + "type": "string", + "enum": ["json"] +} +json_schema["properties"]["text"] = { + "type": "object", + "properties": { + "data": { + "type": "string" + } + }, + "required": ["data"] +} plan_schema = copy.deepcopy(base_item_schema) plan_schema["$schema"] = "plan_schema" @@ -311,6 +326,7 @@ "image": image_schema, "chart": chart_schema, "audio": audio_schema, + "json": json_schema, "plan": plan_schema, "function_call": function_call_schema, } \ No newline at end of file From cefaa5e69add1de5c5dd8378531fd1d2455a8804 Mon Sep 17 00:00:00 2001 From: Chengmo Date: Tue, 17 Dec 2024 21:16:40 +0800 Subject: [PATCH 76/85] update plan step (#668) --- python/core/component.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python/core/component.py b/python/core/component.py index 3b892c619..b2c005b47 100644 --- a/python/core/component.py +++ b/python/core/component.py @@ -116,6 +116,7 @@ class Audio(BaseModel, extra='allow'): class PlanStep(BaseModel, extra='allow'): name: str = Field(default="", description="step名") arguments: dict = Field(default={}, description="step参数") + thought: str = Field(default="", description="step思考结果") class Plan(BaseModel, extra='allow'): detail: str = Field(default="", description="计划详情") From c06fceee584be8ddb9bf601721b5e268a421490a Mon Sep 17 00:00:00 2001 From: C9luster <138663536+C9luster@users.noreply.github.com> Date: Wed, 18 Dec 2024 10:58:02 +0800 Subject: [PATCH 77/85] =?UTF-8?q?=E6=96=B0=E5=A2=9E2024-12-18=E7=9B=B4?= =?UTF-8?q?=E6=92=ADCookbook=20(#667)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 新增2024-12-18直播Cookbook * update --------- Co-authored-by: yinjiaqi --- .../2024_08_22/knowledgebase.ipynb | 2 +- .../2024_12_18/rag_knowledgebase_01.ipynb | 486 ++++++++++++++++++ 2 files changed, 487 insertions(+), 1 deletion(-) create mode 100644 cookbooks/live_broadcast_material/2024_12_18/rag_knowledgebase_01.ipynb diff --git a/cookbooks/live_broadcast_material/2024_08_22/knowledgebase.ipynb b/cookbooks/live_broadcast_material/2024_08_22/knowledgebase.ipynb index 1bfae0c9d..fff989ef1 100644 --- a/cookbooks/live_broadcast_material/2024_08_22/knowledgebase.ipynb +++ b/cookbooks/live_broadcast_material/2024_08_22/knowledgebase.ipynb @@ -6,7 +6,7 @@ "source": [ "# AppBuilder-Knowledge:生产环境的知识库/文档/切片管理教学\n", "\n", - "[知识库组件](https://github.com/baidubce/app-builder/blob/master/docs/basic_module/knowledgebase.md)\n", + "[知识库组件](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md)\n", "\n", "知识库组件(KnowledgeBase)是对线上知识库操作的组件,可以通过SDK实现创建知识库、添加知识文档、查询知识库文档、删除知识文档等操作,可在平台console中查看结果。对console端知识库进行操作,可以通过SDK实现创建知识库、添加知识文档、查询知识库文档、删除知识文档等操作,可在平台console中查看结果\n", "\n", diff --git a/cookbooks/live_broadcast_material/2024_12_18/rag_knowledgebase_01.ipynb b/cookbooks/live_broadcast_material/2024_12_18/rag_knowledgebase_01.ipynb new file mode 100644 index 000000000..cc6d76ef9 --- /dev/null +++ b/cookbooks/live_broadcast_material/2024_12_18/rag_knowledgebase_01.ipynb @@ -0,0 +1,486 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# AppBuilder-Knowledge:生产环境的知识库/文档/切片管理教学\n", + "\n", + "[知识库组件](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md)\n", + "\n", + "知识库组件(KnowledgeBase)是对线上知识库操作的组件,可以通过SDK实现创建知识库、添加知识文档、查询知识库文档、删除知识文档等操作,可在平台console中查看结果。对console端知识库进行操作,可以通过SDK实现创建知识库、添加知识文档、查询知识库文档、删除知识文档等操作,可在平台console中查看结果\n", + "\n", + "## 1. 创建Agent应用\n", + "\n", + "### 1.1 平台Console创建Agent应用\n", + "\n", + "- 创建空应用并发布\n", + "\n", + "![](https://bj.bcebos.com/v1/appbuilder-sdk-components/console%E5%88%9B%E5%BB%BA%E6%B0%91%E6%B3%95%E5%85%B8.png?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-12-17T07%3A38%3A28Z%2F-1%2Fhost%2Ff497d9db3d8945859043fe24fcca4490c3e93a27042e42abc280d0fea7fd3305)\n", + "\n", + "### 1.2 使用SDK调用创建的民法典智能问答Agent\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "民法典第二编第一章的内容是关于物权编通则的一般规定,主要包括以下几个方面:\n", + "\n", + "### 一、物权编的调整范围\n", + "\n", + "* **第二百零五条**:本编调整因物的归属和利用产生的民事关系。\n", + "\n", + "### 二、国家基本经济制度\n", + "\n", + "* **第二百零六条**:国家坚持和完善公有制为主体、多种所有制经济共同发展,按劳分配为主体、多种分配方式并存,社会主义市场经济体制等社会主义基本经济制度。国家巩固和发展公有制经济,鼓励、支持和引导非公有制经济的发展。国家实行社会主义市场经济,保障一切市场主体的平等法律地位和发展权利。\n", + "\n", + "### 三、物权保护原则\n", + "\n", + "* **第二百零七条**:国家、集体、私人的物权和其他权利人的物权受法律平等保护,任何组织或者个人不得侵犯。\n", + "\n", + "### 四、物权设立、变更、转让和消灭的基本原则\n", + "\n", + "* **第二百零八条**:不动产物权的设立、变更、转让和消灭,应当依照法律规定登记。动产物权的设立和转让,应当依照法律规定交付。\n", + "\n", + "### 五、其他规定\n", + "\n", + "民法典第二编第一章还包含了一些其他与物权相关的一般性规定,这些规定为物权编后续章节的具体内容提供了基础和指导。\n", + "\n", + "综上所述,民法典第二编第一章作为物权编通则的一般规定,明确了物权编的调整范围、国家基本经济制度、物权保护原则以及物权设立、变更、转让和消灭的基本原则,为后续章节的具体规定提供了基础和指导。\n" + ] + } + ], + "source": [ + "import appbuilder\n", + "import os\n", + "\n", + "# 更换为自己的Appbuilder-token\n", + "os.environ[\"APPBUILDER_TOKEN\"] = \"\"\n", + "\n", + "# 更换为自己的App ID\n", + "app_id = \"\"\n", + "client = appbuilder.AppBuilderClient(app_id)\n", + "conversation_id = client.create_conversation()\n", + "message = client.run(conversation_id, \"请输出民法典第二编第一章的内容\")\n", + "# 打印对话结果\n", + "print(message.content.answer)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![](https://bj.bcebos.com/v1/appbuilder-sdk-components/%E6%B0%91%E6%B3%95%E5%85%B8%E7%AC%AC%E4%BA%8C%E7%BC%96%E7%AC%AC%E4%B8%80%E7%AB%A0%E4%B8%80%E8%88%AC%E8%A7%84%E5%AE%9A.png?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-12-17T08%3A10%3A14Z%2F-1%2Fhost%2F7852a4156383383e6190435f76b6709409def221e3835369f7d155d3c9dd438a)\n", + "\n", + "我们可以观察到上述Agent基本回答除了对于民法典的第二编第一章一般规定的内容,但是对照上图中的内容不够准确,这说明LLM在运行中出现了一定程度的幻觉,所以我们需要使用RAG(KnowledgeBase)对Agent进行优化。\n", + "\n", + "## 2. RAG(KnowledgeBase)\n", + "\n", + "Appbuilder中支持多种实现创建知识库、添加知识文档、查询知识库文档、删除知识文档等操作方式,包括:\n", + "- 平台Console实现知识库操作\n", + "- 使用SDK/API代码态实现知识库操作\n", + "这里我们选择使用代码态SDK实现知识库的操作\n", + "\n", + "### 2.1 创建知识库\n", + "\n", + "首先我们需要创建知识库,这里我们使用SDK代码态实现知识库的创建" + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "知识库ID: 6ac10164-e80a-4497-ae9f-06ac35f38b97\n" + ] + } + ], + "source": [ + "knowledge = appbuilder.KnowledgeBase()\n", + "resp = knowledge.create_knowledge_base(\n", + " name=\"中华人民共和国民法典\",\n", + " description=\"中华人民共和国民法典的中文版\",\n", + " type=\"public\",\n", + " )\n", + "my_knowledge_base_id = resp.id # 传入知识库ID\n", + "my_knowledge = appbuilder.KnowledgeBase(my_knowledge_base_id)\n", + "print(\"知识库ID: \", my_knowledge.knowledge_id)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- 我们可以到平台Console中查看知识库是否创建成功,我们可以观察到知识库的名称和ID,知识库创建成功!\n", + "\n", + "![](https://bj.bcebos.com/v1/appbuilder-sdk-components/%E6%9F%A5%E7%9C%8B%E7%9F%A5%E8%AF%86%E5%BA%93%E6%B0%91%E6%B3%95%E5%85%B8.png?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-12-17T08%3A37%3A16Z%2F-1%2Fhost%2F40c0d55c6d01d538853bf3b8167c9c5271d74083a20afd5d17590986b44eb954)\n", + "\n", + "\n", + "### 2.2 上传文档到知识库\n", + "\n", + "上传文档到知识库共有以下三种方式:\n", + "- 上传文档到知识库\n", + "- 上传通用文档\n", + "\n", + "##### 2.2.1 上传文档到知识库\n", + "- 主要提供自定义文档处理策略,向知识库添加文档\n", + " - 文档格式:rawText (允许配置后续分割策略)\n", + " - 文档处理策略\n", + "\n", + "```python\n", + "knowledge_base_id = my_knowledge.knowledge_id\n", + "knowledge.create_documents(\n", + "\tid=knowledge_base_id,\n", + "\tcontentFormat=\"rawText\",\n", + "\tsource=appbuilder.DocumentSource(\n", + "\t\ttype=\"web\",\n", + "\t\turls=[\"网页版的文档链接地址\"],\n", + "\t\turlDepth=1,\n", + "\t),\n", + "\tprocessOption=appbuilder.DocumentProcessOption(\n", + "\t\ttemplate=\"custom\",\n", + "\t\tparser=appbuilder.DocumentChoices(\n", + "\t\t\tchoices=[\"layoutAnalysis\", \"ocr\"]\n", + "\t\t),\n", + "\t\tchunker=appbuilder.DocumentChunker(\n", + "\t\t\tchoices=[\"separator\"],\n", + "\t\t\tseparator=appbuilder.DocumentSeparator(\n", + "\t\t\t\tseparators=[\"。\"],\n", + "\t\t\t\ttargetLength=300,\n", + "\t\t\t\toverlapRate=0.25,\n", + "\t\t\t),\n", + "\t\t\tprependInfo=[\"title\", \"filename\"],\n", + "\t\t),\n", + "\t\tknowledgeAugmentation=appbuilder.DocumentChoices(choices=[\"faq\"]),\n", + "\t),\n", + ")\n", + "```\n", + "\n", + "#### 2.2.2 上传通用文档\n", + "- SDK支持基于代码态的文档上传方法`upload_file`,以及包含文档的自定义切分逻辑的向知识库添加文档的方法``add_document`\n", + "- 需要先将文档下载至本地\n", + "\n", + "#### 采用上传通用文档的方案上传文档到知识库\n", + "- 下面将提供一个缺少第七编第十章的民法典txt文件,同时使用SDK将其上传到知识库中,首先下载我们储存到云端的txt文件。" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "文件已成功下载到 mingfadian.txt\n" + ] + } + ], + "source": [ + "# 下载云端民法典到本地\n", + "import requests\n", + "\n", + "def download_file(url, local_filename):\n", + " try:\n", + " response = requests.get(url, stream=True)\n", + " with open(local_filename, 'wb') as f:\n", + " for chunk in response.iter_content(chunk_size=8192):\n", + " f.write(chunk)\n", + "\n", + " print(f\"文件已成功下载到 {local_filename}\")\n", + " except requests.RequestException as e:\n", + " print(f\"下载文件时发生错误: {e}\")\n", + "\n", + "# 使用示例\n", + "url = 'https://bj.bcebos.com/v1/appbuilder-sdk-components/mingfadian.txt?authorization=bce-auth-v1\\\n", + "%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-12-17T10%3A44%3A33Z%2F-1%2Fhost%2Fb97e44abe836f58de9632c374d4055c391a84f7998562493c22fdff8596cdf49'\n", + "local_filename = 'mingfadian.txt' \n", + "download_file(url, local_filename)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- 接下来我们将下载好的mingfadian.txt文件使用SDK上传到Appbuilder的知识库中,使用`appbuilder.CustomProcessRule`设置文档的切分规则。" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": {}, + "outputs": [], + "source": [ + "upload_res = knowledge.upload_file(local_filename)\n", + "add_res = knowledge.add_document(\n", + " content_type=\"raw_text\",\n", + " file_ids=[upload_res.id],\n", + " custom_process_rule=appbuilder.CustomProcessRule(\n", + " separators=[\"?\"], target_length=600, overlap_rate=0.3\n", + " ),\n", + " knowledge_base_id=my_knowledge.knowledge_id\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- 此时就可以在平台Console中查看到我们上传的民法典知识库了\n", + "\n", + "![](https://bj.bcebos.com/v1/appbuilder-sdk-components/%E6%B0%91%E6%B3%95%E5%85%B8%E7%9F%A5%E8%AF%86%E5%BA%93%E4%B8%AD%E6%9F%A5%E7%9C%8B%E5%AF%B9%E5%BA%94%E6%96%87%E4%BB%B6.png?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-12-17T08%3A51%3A35Z%2F-1%2Fhost%2F4025b0777017d0776e08ebe81c94b794c0883165ceaac1751d6c9824ed5727c2)\n", + "\n", + "### 2.3 为Agent应用添加知识库\n", + "\n", + "- 在平台console中找到我们创建的Agent应用,添加我们创建的民法典知识库,并更新发布应用\n", + "\n", + "![](https://bj.bcebos.com/v1/appbuilder-sdk-components/%E5%BA%94%E7%94%A8%E6%B7%BB%E5%8A%A0%E7%9F%A5%E8%AF%86%E5%BA%93.png?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-12-17T08%3A55%3A22Z%2F-1%2Fhost%2F4213ddd932eb9db15a507d985092797f168f5bb338df0453ea0848a1f9814597)\n", + "\n", + "- 再次调用我们的Agent应用,就可以看到我们添加民法典知识库之后,Agent应用可以正确回答我们的问题了" + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "**民法典第二编第一章的内容如下**:\n", + "\n", + "第一章 一般规定\n", + "\n", + "第二百零五条 本编调整因物的归属和利用产生的民事关系。\n", + "\n", + "第二百零六条 国家坚持和完善公有制为主体、多种所有制经济共同发展,按劳分配为主体、多种分配方式并存,社会主义市场经济体制等社会主义基本经济制度。国家巩固和发展公有制经济,鼓励、支持和引导非公有制经济的发展。国家实行社会主义市场经济,保障一切市场主体的平等法律地位和发展权利。\n", + "\n", + "第二百零七条 国家、集体、私人的物权和其他权利人的物权受法律平等保护,任何组织或者个人不得侵犯。\n", + "\n", + "第二百零八条 不动产物权的设立、变更、转让和消灭,应当依照法律规定登记。动产物权的设立和转让,应当依照法律规定交付。\n" + ] + } + ], + "source": [ + "client = appbuilder.AppBuilderClient(app_id)\n", + "conversation_id = client.create_conversation()\n", + "message = client.run(conversation_id, \"请输出民法典第二编第一章的内容\")\n", + "# 打印对话结果\n", + "print(message.content.answer)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- 可以观察到在使用了RAG(KnowledgeBase)之后,可以准确的输出【民法典的第二编第一分编第一章一般规定】部分的准确内容,接下来我们实验查询我们刻意删除掉的民法典第七编第十章部分的内容,查看Agent能否输出准确答案。(使用此方法模拟我们知识库中的文档更新之后,并没有被上传到知识库的情况)" + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "无法回答此问题。\n" + ] + } + ], + "source": [ + "client = appbuilder.AppBuilderClient(app_id)\n", + "conversation_id = client.create_conversation()\n", + "message = client.run(conversation_id, \"请输出中华人民共和国民法典第七编第十章原文内容\")\n", + "# 打印对话结果\n", + "print(message.content.answer)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- 我们可以发现Agent并没有输出民法典的第七编第十章的准确内容,此时我们就需要Appbuilder的切片管理功能,对知识库新增切片\n", + "\n", + "## 3. 知识库切片管理\n", + "\n", + "Appbuilder中支持多种知识库切片管理操作方式,包括:\n", + "- 平台Console实现知识库切片管理\n", + "- 使用SDK/API代码态实现知识库切片管理(优势)\n", + "这里我们选择使用代码态SDK实现知识库切片管理的操作\n", + "\n", + "### 3.1KnowledgeBase 代码态切片管理功能\n", + "\n", + "* 创建切片\n", + "* 修改切片信息\n", + "* 获取切片信息\n", + "* 获取切片列表\n", + "* 删除切片\n", + "\n", + "\n", + "- 接下来我们使用SDK上传一个民法典第七编第十章部分的内容的切片" + ] + }, + { + "cell_type": "code", + "execution_count": 90, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "切片ID: f1e1962f-70dd-4573-9068-37485980ac5c\n" + ] + } + ], + "source": [ + "# 获取知识库全部文档:get_all_documents\n", + "doc_list = knowledge.get_all_documents(my_knowledge.knowledge_id)\n", + "\n", + "# 获取doc_list列表的第一个文档的document_id\n", + "document_id = doc_list[0].id # 这里我们的知识库只有一个文档,所以获取第一个文档的document_id\n", + "my_knowledge = appbuilder.KnowledgeBase(my_knowledge_base_id)\n", + "\n", + "# 这里我们使用来web_crawler函数来模拟爬虫功能,在生产环境中开发者可以使用自己的爬虫模块来周期性的获取最新的文档更新内容\n", + "def web_crawler():\n", + " \"\"\"\n", + " 此函数模拟爬虫功能,获取最新的文档更新内容内容\n", + " \"\"\"\n", + " content = \"\"\"\n", + " 中华人民共和国民法典第七编第十章原文内容如下:\n", + " 建筑物和物件损害责任\n", + " 第一千二百五十二条 建筑物、构筑物或者其他设施倒塌、塌陷造成他人损害的,由建设单位与施工单位承担连带责任,但是建设单位与施工单位能够证明不存在质量缺陷的除外。建设单位、施工单位赔偿后,有其他责任人的,有权向其他责任人追偿。\n", + " 因所有人、管理人、使用人或者第三人的原因,建筑物、构筑物或者其他设施倒塌、塌陷造成他人损害的,由所有人、管理人、使用人或者第三人承担侵权责任。\n", + " 第一千二百五十三条 建筑物、构筑物或者其他设施及其搁置物、悬挂物发生脱落、坠落造成他人损害,所有人、管理人或者使用人不能证明自己没有过错的,应当承担侵权责任。所有人、管理人或者使用人赔偿后,有其他责任人的,有权向其他责任人追偿。\n", + " 第一千二百五十四条 禁止从建筑物中抛掷物品。从建筑物中抛掷物品或者从建筑物上坠落的物品造成他人损害的,由侵权人依法承担侵权责任;经调查难以确定具体侵权人的,除能够证明自己不是侵权人的外,由可能加害的建筑物使用人给予补偿。可能加害的建筑物使用人补偿后,有权向侵权人追偿。\n", + " 物业服务企业等建筑物管理人应当采取必要的安全保障措施防止前款规定情形的发生;未采取必要的安全保障措施的,应当依法承担未履行安全保障义务的侵权责任。\n", + " 发生本条第一款规定的情形的,公安等机关应当依法及时调查,查清责任人。\n", + " 第一千二百五十五条 堆放物倒塌、滚落或者滑落造成他人损害,堆放人不能证明自己没有过错的,应当承担侵权责任。\n", + " 第一千二百五十六条 在公共道路上堆放、倾倒、遗撒妨碍通行的物品造成他人损害的,由行为人承担侵权责任。公共道路管理人不能证明已经尽到清理、防护、警示等义务的,应当承担相应的责任。\n", + " 第一千二百五十七条 因林木折断、倾倒或者果实坠落等造成他人损害,林木的所有人或者管理人不能证明自己没有过错的,应当承担侵权责任。\n", + " 第一千二百五十八条 在公共场所或者道路上挖掘、修缮安装地下设施等造成他人损害,施工人不能证明已经设置明显标志和采取安全措施的,应当承担侵权责任。\n", + " 窨井等地下设施造成他人损害,管理人不能证明尽到管理职责的,应当承担侵权责任。\n", + " \"\"\"\n", + " return content\n", + "\n", + "\n", + "resp = my_knowledge.create_chunk(documentId=document_id, content=web_crawler())\n", + "print(\"切片ID: \", resp.id)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- 接下来我们再次调用Agent应用,查看一下他对民法典第七编第十章的说明。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "以下是《中华人民共和国民法典》第七编第十章的全部内容:\n", + "\n", + "**建筑物和物件损害责任**\n", + "\n", + "**第一千二百五十二条** 建筑物、构筑物或者其他设施倒塌、塌陷造成他人损害的,由建设单位与施工单位承担连带责任,但是建设单位与施工单位能够证明不存在质量缺陷的除外。建设单位、施工单位赔偿后,有其他责任人的,有权向其他责任人追偿。\n", + "\n", + "因所有人、管理人、使用人或者第三人的原因,建筑物、构筑物或者其他设施倒塌、塌陷造成他人损害的,由所有人、管理人、使用人或者第三人承担侵权责任。\n", + "\n", + "**第一千二百五十三条** 建筑物、构筑物或者其他设施及其搁置物、悬挂物发生脱落、坠落造成他人损害,所有人、管理人或者使用人不能证明自己没有过错的,应当承担侵权责任。所有人、管理人或者使用人赔偿后,有其他责任人的,有权向其他责任人追偿。\n", + "\n", + "**第一千二百五十四条** 禁止从建筑物中抛掷物品。从建筑物中抛掷物品或者从建筑物上坠落的物品造成他人损害的,由侵权人依法承担侵权责任;经调查难以确定具体侵权人的,除能够证明自己不是侵权人的外,由可能加害的建筑物使用人给予补偿。可能加害的建筑物使用人补偿后,有权向侵权人追偿。\n", + "\n", + "物业服务企业等建筑物管理人应当采取必要的安全保障措施防止前款规定情形的发生;未采取必要的安全保障措施的,应当依法承担未履行安全保障义务的侵权责任。\n", + "\n", + "发生本条第一款规定的情形的,公安等机关应当依法及时调查,查清责任人。\n", + "\n", + "**第一千二百五十五条** 堆放物倒塌、滚落或者滑落造成他人损害,堆放人不能证明自己没有过错的,应当承担侵权责任。\n", + "\n", + "**第一千二百五十六条** 在公共道路上堆放、倾倒、遗撒妨碍通行的物品造成他人损害的,由行为人承担侵权责任。公共道路管理人不能证明已经尽到清理、防护、警示等义务的,应当承担相应的责任。\n", + "\n", + "**第一千二百五十七条** 因林木折断、倾倒或者果实坠落等造成他人损害,林木的所有人或者管理人不能证明自己没有过错的,应当承担侵权责任。\n", + "\n", + "**第一千二百五十八条** 在公共场所或者道路上挖掘、修缮安装地下设施等造成他人损害,施工人不能证明已经设置明显标志和采取安全措施的,应当承担侵权责任。\n", + "\n", + "窨井等地下设施造成他人损害,管理人不能证明尽到管理职责的,应当承担侵权责任。\n" + ] + } + ], + "source": [ + "client = appbuilder.AppBuilderClient(app_id)\n", + "conversation_id = client.create_conversation()\n", + "message = client.run(conversation_id, \"请输出中华人民共和国民法典第七编第十章原文内容\")\n", + "# 打印对话结果\n", + "print(message.content.answer)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- 可以观察到此次Agent运行准确的的输出了民法典第七编第十章的内容\n", + "\n", + "#### 其他的知识库文档&切片管理方法\n", + "\n", + "###### 修改切片信息`modify_chunk`\n", + "\n", + "- 如果我们对之前设置的切片信息不满意,可以通过`modify_chunk`方法进行修改。\n", + "\n", + "###### 获取切片信息`describe_chunk`\n", + "\n", + "- 我需要查看我刚才创建切片信息,可以使用`describe_chunk`方法\n", + "\n", + "###### 删除切片信息`delete_chunk`\n", + "\n", + "- 切片信息创建出来后,如果不需要了,可以通过`delete_chunk`方法进行删除。\n", + "\n", + "##### 删除文档和知识库\n", + "\n", + "- 当你不再需要文档知识库时,可以删除它,SDK同样提供删除文档和知识库的方法\n", + " - 从知识库删除文档 `KnowledgeBase().delete_document`\n", + " - 删除知识库`delete_knowledge_base`" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "python-3.10.14", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From e6056859abd891463640f59cc6a71a08b66a0f15 Mon Sep 17 00:00:00 2001 From: C9luster <138663536+C9luster@users.noreply.github.com> Date: Wed, 18 Dec 2024 20:28:30 +0800 Subject: [PATCH 78/85] =?UTF-8?q?=E6=9B=B4=E6=96=B0SDK=E6=96=87=E6=A1=A3?= =?UTF-8?q?=EF=BC=8C=E5=B9=B6=E6=9B=B4=E6=96=B0README=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=E7=9B=B8=E5=AF=B9=E9=93=BE=E6=8E=A5=20(#669)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 更新SDK文档内容以及更换README文件文档链接 * update * update * update --------- Co-authored-by: yinjiaqi --- README.md | 90 +-- .../Application/Agent/BasicKnowledge/agent.md | 759 ++++++++++++++++++ docs/Application/Agent/ToolCall/tool_call.md | 633 +++++++++++++++ .../Agent/ToolChoice/tool_choice.md | 176 ++++ .../use_official_components.md | 32 + .../RAG/DatasetManage/dataset_manage.md | 64 ++ .../AdvancedDevelopment/README.md | 14 + .../ErrorMessage/error_message.md | 92 +++ .../HowToContributeCode/README.md | 444 +++++++++- docs/README.md | 89 +- docs/README_en.md | 91 +-- docs/README_ja.md | 91 +-- mkdocs.yml | 17 +- 13 files changed, 2378 insertions(+), 214 deletions(-) create mode 100644 docs/Application/Agent/BasicKnowledge/agent.md create mode 100644 docs/Application/Agent/ToolCall/tool_call.md create mode 100644 docs/Application/Agent/ToolChoice/tool_choice.md create mode 100644 docs/Application/Agent/UseOfficialComponents/use_official_components.md create mode 100644 docs/Application/RAG/DatasetManage/dataset_manage.md create mode 100644 docs/DevelopGuide/AdvancedDevelopment/README.md create mode 100644 docs/DevelopGuide/ErrorMessage/error_message.md diff --git a/README.md b/README.md index 004dece09..bc2b9f88a 100644 --- a/README.md +++ b/README.md @@ -287,54 +287,48 @@ Hook: - 产业实践应用范例: - [SDK使用示例](https://github.com/baidubce/app-builder/blob/master/docs/QuickStart/ExamplesOfIndustrialPracticeApplications/README.md) - [SDK当前支持的编程语言](https://github.com/baidubce/app-builder/blob/master/docs/QuickStart/CurrentlySupportedProgrammingLanguages/README.md) - - 基础: - - 模型: - - [获取模型列表](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Model/get_model_list.md) - - [组件](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Components/Components.md) - - 监控: - - [TRACE基础功能](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/basic.md) - - [TRACE拓展功能](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/phoenix_method.md) - - [Debug功能](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/Debug.md) - - 部署: - - [交互式前端部署](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/AgentChainlit.md) - - [公有云部署](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/cloud.md) - - [API 访问](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/flask.md) - - [AgentRuntime](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/agentruntime.md) - - [UserSession](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/usersession.md) - - 平台: - - 应用: - - [AppBuilderClient组件](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/Application/appbuilder_client.md) - - [获取AppBuilder已发布的应用列表](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/Application/get_app_list.md) - - 知识库: - - [知识库组件](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md) - - 自定义组件: - - [基础能力组件](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/CustomComponents/components.md) - - 应用: - - Agent: - - [基础知识](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [使用官方组件](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [ToolCall](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [ToolChoice](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [使用异步和流式加速客户端调用](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - RAG: - - [基础知识](https://github.com/baidubce/app-builder/blob/master/docs/Application/RAG/BasicKnowledge/rag.md) - - [知识库管理](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [Reference信息处理](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - Workflow: - - [基础知识](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [从零使用Workflow组装一个RAG应用](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [从零使用Workflow组装一个Agent应用](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - 开发者指南: - - [如何贡献代码](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/HowToContributeCode/README.md) - - [版本升级日志](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/ChangeLog/changelog.md) - - [常见问题FAQ](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [日志管理](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [错误信息](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [环境参数](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/EnvironmentalParameters/env.md) - - API Reference: - - [Python API Reference](https://github.com/baidubce/app-builder/blob/master/docs/API-Reference/Python/PythonAPI.md) - - [Java API Reference](https://github.com/baidubce/app-builder/blob/master/docs/API-Reference/Java/JavaAPI.md) - - [Go API Reference](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - 基础: + - 模型: + - [获取模型列表](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Model/get_model_list.md) + - [组件](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Components/Components.md) + - 监控: + - [TRACE基础功能](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/basic.md) + - [TRACE拓展功能](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/phoenix_method.md) + - 部署: + - [交互式前端部署](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/AgentChainlit.md) + - [公有云部署](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/cloud.md) + - [API 访问](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/flask.md) + - [AgentRuntime](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/agentruntime.md) + - [UserSession](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/usersession.md) + - 平台: + - 应用: + - [AppBuilderClient组件](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/Application/appbuilder_client.md) + - [获取AppBuilder已发布的应用列表](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/Application/get_app_list.md) + - 知识库: + - [知识库组件](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md) + - 自定义组件: + - [基础能力组件](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/CustomComponents/components.md) + - 应用: + - Agent: + - [基础知识](https://github.com/baidubce/app-builder/blob/master/docs/Application/Agent/BasicKnowledge/agent.md) + - [使用官方组件](https://github.com/baidubce/app-builder/blob/master/docs/Application/Agent/UseOfficialComponents/use_official_components.md) + - [ToolCall](https://github.com/baidubce/app-builder/blob/master/docs/Application/Agent/ToolCall/tool_call.md) + - [ToolChoice](https://github.com/baidubce/app-builder/blob/master/docs/Application/Agent/ToolChoice/tool_choice.md) + - [使用异步和流式加速客户端调用](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - RAG: + - [基础知识](https://github.com/baidubce/app-builder/blob/master/docs/Application/RAG/BasicKnowledge/rag.md) + - [知识库管理](https://github.com/baidubce/app-builder/blob/master/docs/Application/RAG/DatasetManage/dataset_manage.md) + - [Reference信息处理](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - Workflow: + - [基础知识](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [从零使用Workflow组装一个RAG应用](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [从零使用Workflow组装一个Agent应用](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - 开发者指南: + - [如何贡献代码](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/HowToContributeCode/README.md) + - [二次开发](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/AdvancedDevelopment/README.md) + - [版本升级日志](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/ChangeLog/changelog.md) + - [错误信息](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/ErrorMessage/error_message.md) + - [环境参数](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/EnvironmentalParameters/env.md) ## 开源社区与活动 diff --git a/docs/Application/Agent/BasicKnowledge/agent.md b/docs/Application/Agent/BasicKnowledge/agent.md new file mode 100644 index 000000000..5fd0f65bc --- /dev/null +++ b/docs/Application/Agent/BasicKnowledge/agent.md @@ -0,0 +1,759 @@ +# console端Agent操作工具(Agent) + +## 简介 + +AIAgent是能够感知环境,基于目标进行决策并执行动作的智能化应用。不同于传统人工智能应用(主要指以规则引擎、机器学习、深度学习等技术为核心)和RPA机器人,AIAgent能够基于目标和对现状能力的认知,在环境约束中,依赖特定资源和现有工具,找到行动规则并将行动拆解为必要的步骤,自主执行步骤,达成目标。 + +AIAgent具备三个核心能力:独立思考、自主执行、持续迭代。 +- 独立思考是指AlAgent能够根据给定任务目标和约束条件,进行任务规划和问题拆解,形成执行步骤(即工作流); +- 自主执行是指AlAgent能够调取各类组件和工具,按照执行步骤依次执行,实现任务目标; +- 持续选代是指AlAgent能够自动记录任务目标、工作流和执行结果,基于结果反馈,沉淀专家知识和案例。 + +AICopilot、AIAgent、大模型等名词在各类文章上经常混淆,此处简要说明下三者的区别。大模型一般是指大模型技术,AlAgent和Al Copilot是基于大模型技术的智能化应用,AlAgent和AlCopilot在功能和场景上存在差别。 + +自主性是AIAgent和AI Copilot之间最大的区别。AI Copilot是“副驾驶”,只是提供建议而非决策,AIAgent是“主驾驶”,需要真正做出决策并开展行动。 + +drawing + +创建的AgentBuilder应用,请参考[AppBuilder应用](../../../BasisModule/Platform/Application/appbuilder_client.md)进行调用。 + + +### 功能介绍 + +利用线上Agent应用进行问答 + +### 特色优势 + +与线上应用联动,利用线上Agent应用进行问答 + +### 应用场景 + +使用SDK利用线上Agent应用进行问答 + +## 基本用法 + +以下是使用SDK进行问答的示例代码,包含Python、Java、Go语言 + + +## Python基本用法 + + +### `AppBuilderClient().__init__()` + + +#### 方法参数 + +| 参数名称 | 参数类型 | 描述 | 示例值 | +| -------- | -------- | ----------------- | -------------- | +| app_id | string | 线上Agent应用的ID | "正确的应用ID" | + +#### 方法返回值 + +```AppBuilderClient```实例化对象 + + +### `AppBuilderClient().create_conversation()-> str` +#### 方法参数 +无 + +#### 方法返回值 + + | 参数名称 | 参数类型 | 描述 | 示例值 | + | --------------- | -------- | -------- | -------------------------------------- | + | conversation_id | string | 会话的ID | "80c5bbee-931d-4ed9-a4ff-63e1971bd071" | + + +### `AppBuilderClient().upload_local_file(file_path: str)-> str` +#### 方法参数 +| 参数名称 | 参数类型 | 描述 | 示例值 | +| --------- | -------- | -------- | ---------------- | +| file_path | string | 文件路径 | "正确的文件路径" | +#### 方法返回值 +| 参数名称 | 参数类型 | 描述 | 示例值 | +| -------- | -------- | ------ | ---------------------------------- | +| file_id | string | 文件ID | "80c5bbee-931d-4ed9-a4ff-63e1971bd | + + +### `AppBuilderClient().run() -> Message` + +#### 方法参数 + +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +| --------------- | ------------------ | -------- | ------------------------------------------------------------ | ----------------- | +| conversation_id | String | 是 | 会话ID | | +| query | String | 否 | query问题内容 | "今天天气怎么样?" | +| file_ids | list[String] | 否 | 对话可引用的文档ID | | +| stream | Bool | 否 | 为true时则流式返回,为false时则一次性返回所有内容, 推荐设为true,降低首token时延 | False | +| end_user_id | String | 否 | 终端用户ID,限制6 - 64字符 | | +| tools | List[Tool] | 否 | 一个列表,其中每个字典对应一个工具的配置 | | +| tools[0] | Tool | 否 | 工具配置 | | +| +type | String | 否 | 枚举:
**file_retrieval**: 知识库检索工具能够理解文档内容,支持用户针对文档内容的问答。
**code_interpreter**: 代码解释器, 代码解释器能够生成并执行代码,从而协助用户解决复杂问题,涵盖科学计算(包括普通数学计算题)、数据可视化、文件编辑处理(图片、PDF文档、视频、音频等)、文件格式转换(如WAV、MP3、text、SRT、PNG、jpg、MP4、GIF、MP3等)、数据分析&清洗&处理(文件以excel、csv格式为主)、机器学习&深度学习建模&自然语言处理等多个领域。
**function**: 支持fucntion call模式调用工具 | | +| +function | Function | 否 | Function工具描述
仅当**type为**`**function**` 时需要且必须填写 | | +| ++name | String | 否 | 函数名
只允许数字、大小写字母和中划线和下划线,最大长度为64个字符。一次运行中唯一。 | | +| ++description | String | 否 | 工具描述 | | +| ++parameters | Dict | 否 | 工具参数, json_schema格式 | | +| tool_outputs | List[ToolOutput] | 否 | 内容为本地的工具执行结果,以自然语言/json dump str描述 | | +| tool_outputs[0] | ToolOutput | 否 | 工具执行结果 | | +| +tool_call_id | String | 否 | 工具调用ID | | +| +output | String | 否 | 工具输出 | | +| tool_choice | ToolChoice | 否 | 控制大模型使用组件的方式,仅对自主规划Agent生效。 | | +| +type | String | 否 | auto/function,auto表示由LLM自动判断调什么组件;function表示由用户指定调用哪个组件。 | | +| +function | ToolChoiceFunction | 否 | 组件对象,包括组件的英文名称和入参 | | +| ++name | String | 否 | 组件的英文名称(唯一标识) | | +| ++input | String | 否 | 组件入参,当组件没有入参时填入空对象{} | | +| action | Action | 否 | 对话时要进行的特殊操作。如回复工作流agent中“信息收集节点“的消息 | | +| +action_type | String | 是 | 要执行的操作。
可选值为:
resume:回复“信息收集节点” 的消息 | | +| +parameters | Object | 是 | 执行操作时所需的参数 | | + +#### Run方法非流式返回值 + +Run非流式方法返回一个`Message`对象,该对象包含以下属性: + +| 参数名称 | 参数类型 | 描述 | 示例值 | +| -------------- | ---------------------- | -------------------- | --------------------------------------------------------------------------------------- | +| content | AppBuilderClientAnswer | 对话返回结果 | | +| +answer | String | 智能体应用返回的回答 | | +| +events | List[Event] | 事件列表 | | +| +events[0] | Event | 具体事件内容 | | +| ++code | String | 错误码 | | +| ++message | String | 错误具体消息 | | +| ++status | String | 事件状态 | 状态描述,preparing(准备运行)running(运行中)error(执行错误) done(执行完成) | +| ++event_type | String | 事件类型 | | +| ++content_type | String | 内容类型 | 可选值包括:code text, image, status,image, function_call, rag, audio、video等 | +| ++detail | Dict | 事件输出详情 | 代码解释器、文生图、工具组件、RAG等的详细输出内容 | +| ++usage | Usage | 模型调用的token用量 | Usage(prompt_tokens=1322, completion_tokens=80, total_tokens=1402, name='ERNIE-4.0-8K') | + +`AppBuilderClientAnswer`类型定义如下: +```python +class AppBuilderClientAnswer(BaseModel): + """执行步骤的具体内容 + 属性: + answer(str): query回答内容 + events( list[Event]): 事件列表 + """ + answer: str = "" + events: list[Event] = [] +``` + +`Event`类型定义如下: +```python +class Event(BaseModel): + """执行步骤的具体内容 + 属性: + code (int): 响应code码 + message (str): 错误详情 + status (str): 状态描述,preparing(准备运行)running(运行中)error(执行错误) done(执行完成) + event_type(str): 事件类型 + content_type(str): 内容类型 + detail(dict): 事件详情 + usage(Usage): 大模型调用的token用量 + """ + code: int = 0 + message: str = "" + status: str = "" + event_type: str = "" + content_type: str = "" + detail: dict = {} + usage: Optional[Usage] = None +``` + + +#### Run方法流式返回值 + +| 参数名称 | 参数类型 | 描述 | 示例值 | +| -------- | ---------------- | ---------------------------------------------- | ------ | +| content | Python Generator | 可迭代,每次迭代返回AppBuilderClientAnswer类型 | 无 | + +#### 非流式调用示例 + +```python +import appbuilder +import os + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +# 设置环境变量 +os.environ["APPBUILDER_TOKEN"] = '...' +app_id = '...' # 已发布AppBuilder应用ID,可在console端查看 +# 初始化智能体 +builder = appbuilder.AppBuilderClient(app_id) +# 创建会话 +conversation_id = builder.create_conversation() +# 运行对话 +out = builder.run(conversation_id, "北京今天天气怎么样") +# 打印会话结果 +print(out.content.answer) +``` + +#### 流式调用示例 + +```python + +import appbuilder +from appbuilder.core.console.appbuilder_client import data_class +import os + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +# 设置环境变量 +os.environ["APPBUILDER_TOKEN"] = '...' +app_id = '...' # 已发布AppBuilder应用的ID +# 初始化智能体 +client = appbuilder.AppBuilderClient(app_id) +# 创建会话 +conversation_id = client.create_conversation() + +# 上传一个介绍某汽车产品的说明文档 +file_id = client.upload_local_file(conversation_id, "/path/to/pdf/file") +# 引用上传的文档,开始对话 +# 注意file_ids不是必填项,如果不需要引用特定文档,file_ids留空即可 +message = client.run(conversation_id, "汽车性能参数怎么样", file_ids=[file_id, ], stream=True) + +answer = "" + +# 每次迭代返回AppBuilderClientAnswer结构,内可能包括多个事件内容 +for content in message.content: + # stream=True时,将answer拼接起来才是完整的的对话结果 + answer += content.answer + for event in content.events: + content_type = event.content_type + detail = event.detail + # 根据content类型对事件详情进行解析 + if content_type == "code": + code_detail = data_class.CodeDetail(**detail) + print(code_detail.code) + elif content_type == "text": + text_detail = data_class.TextDetail(**detail) + print(text_detail.text) + elif content_type == "image": + image_detail = data_class.ImageDetail(**detail) + print(image_detail.url) + elif content_type == "rag": + rag_detail = data_class.RAGDetail(**detail) + if len(rag_detail.references) > 0: + print(rag_detail.references) + elif content_type == "function_call": + function_call_detail = data_class.FunctionCallDetail(**detail) + print(function_call_detail.video) + elif content_type == "audio": + audio_detail = data_class.AudioDetail(**detail) + print(audio_detail) + elif content_type == "video": + video_detail = data_class.VideoDetail(**detail) + print(video_detail) + elif content_type == "status": + status_detail = data_class.StatusDetail(**detail) + print(status_detail) + else: + default_detail = data_class.DefaultDetail(**detail) + print(default_detail) + +# 打印完整的answer结果 +print(answer) +``` + +## Java基本用法 + +### ```new AppBuilderClient(appId)``` + +#### 方法参数 + +| 参数名称 | 参数类型 | 描述 | 示例值 | +| -------- | -------- | ----------------- | -------------- | +| appID | String | 线上Agent应用的ID | "正确的应用ID" | + + +#### 方法返回值 + +```AppBuilderClient```实例化对象 + +### ```AppBuilderClient().createConversation()``` + +#### 方法参数 +无 + +#### 方法返回值 + +| 参数名称 | 参数类型 | 描述 | 示例值 | +| -------------- | -------- | ------------ | -------------- | +| conversationId | String | 创建的会话ID | "正确的会话ID" | + +### ```AppBuilderClient().run()``` + +#### Run方法入参`AppBuilderCientRunRequest` + +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +| --------------- | ------------------ | -------- | ------------------------------------------------------------ | -------------------- | +| query | String | 是 | query内容 | "汽车性能参数怎么样" | +| conversationId | String | 是 | 对话id,可以通过createConversation()获取 | | +| stream | boolean | 是 | 为true时则流式返回,为false时则一次性返回所有内容, 推荐设为true,降低首token时延 | | +| tools | List[Tool] | 否 | 一个列表,其中每个字典对应一个工具的配置 | | +| tools[0] | Tool | 否 | 工具配置 | | +| +type | String | 否 | 枚举:
**file_retrieval**: 知识库检索工具能够理解文档内容,支持用户针对文档内容的问答。
**code_interpreter**: 代码解释器, 代码解释器能够生成并执行代码,从而协助用户解决复杂问题,涵盖科学计算(包括普通数学计算题)、数据可视化、文件编辑处理(图片、PDF文档、视频、音频等)、文件格式转换(如WAV、MP3、text、SRT、PNG、jpg、MP4、GIF、MP3等)、数据分析&清洗&处理(文件以excel、csv格式为主)、机器学习&深度学习建模&自然语言处理等多个领域。
**function**: 支持fucntion call模式调用工具 | | +| +function | Function | 否 | Function工具描述
仅当**type为**`**function**` 时需要且必须填写 | | +| ++name | String | 否 | 函数名
只允许数字、大小写字母和中划线和下划线,最大长度为64个字符。一次运行中唯一。 | | +| ++description | String | 否 | 工具描述 | | +| ++parameters | Dict | 否 | 工具参数, json_schema格式 | | +| tool_outputs | List[ToolOutput] | 否 | 内容为本地的工具执行结果,以自然语言/json dump str描述 | | +| tool_outputs[0] | ToolOutput | 否 | 工具执行结果 | | +| +tool_call_id | String | 否 | 工具调用ID | | +| +output | String | 否 | 工具输出 | | +| tool_choice | ToolChoice | 否 | 控制大模型使用组件的方式,仅对自主规划Agent生效。 | | +| +type | String | 否 | auto/function,auto表示由LLM自动判断调什么组件;function表示由用户指定调用哪个组件。 | | +| +function | ToolChoiceFunction | 否 | 组件对象,包括组件的英文名称和入参 | | +| ++name | String | 否 | 组件的英文名称(唯一标识) | | +| ++input | String | 否 | 组件入参,当组件没有入参时填入空对象{} | | +| action | Action | 否 | 对话时要进行的特殊操作。如回复工作流agent中“信息收集节点“的消息 | | +| +action_type | String | 是 | 要执行的操作。
可选值为:
resume:回复“信息收集节点” 的消息 | | +| +parameters | Object | 是 | 执行操作时所需的参数 | | + +#### Run方法出参 +| 参数名称 | 参数类型 | 描述 | 示例值 | +| ------------------------ | ------------------------ | -------------------------------------------------------------------------------- | ------ | +| AppBuilderClientIterator | AppBuilderClientIterator | 回答迭代器,流式/非流式均统一返回该类型,每次迭代返回AppBuilderClientIterator类型 | | + +#### 迭代AppBuilderClientIterator +| 参数名称 | 参数类型 | 描述 | 示例值 | +| ------------- | ------------------- | -------------------- | --------------------------------------------------------------------------------------- | +| +answer | String | 智能体应用返回的回答 | | +| +events | Event[] | 事件列表 | | +| +events[0] | Event | 具体事件内容 | | +| ++code | string | 错误码 | | +| ++message | string | 错误具体消息 | | +| ++status | string | 事件状态 | 状态描述,preparing(准备运行)running(运行中)error(执行错误) done(执行完成) | +| ++eventType | string | 事件类型 | | +| ++contentType | string | 内容类型 | 可选值包括:code text, image, status,image, function_call, rag, audio、video等 | +| ++detail | Map | 事件输出详情 | 代码解释器、文生图、工具组件、RAG等的详细输出内容 | +| ++usage | Usage | 模型调用的token用量 | Usage(prompt_tokens=1322, completion_tokens=80, total_tokens=1402, name='ERNIE-4.0-8K') | + + +#### 示例代码 + +```Java +package org.example; + +import java.io.IOException; +import java.util.*; + +import com.google.gson.annotations.SerializedName; + +import com.baidubce.appbuilder.base.exception.AppBuilderServerException; +import com.baidubce.appbuilder.console.appbuilderclient.AppBuilderClient; +import com.baidubce.appbuilder.model.appbuilderclient.AppBuilderClientIterator; +import com.baidubce.appbuilder.model.appbuilderclient.AppBuilderClientResult; +import com.baidubce.appbuilder.model.appbuilderclient.Event; +import com.baidubce.appbuilder.base.utils.json.JsonUtils; + +class AppBuilderClientDemo { + + public static void main(String[] args) throws IOException, AppBuilderServerException { + System.setProperty("APPBUILDER_TOKEN", "请设置正确的应用密钥"); + String appId = "请设置正确的应用ID"; + AppBuilderClient builder = new AppBuilderClient(appId); + String conversationId = builder.createConversation(); + // 填写上传文件路径 + String fileId = builder.uploadLocalFile(conversationId, "/Users/zhangxiaoyu15/PycharmProjects/app-builder/test_app_builder_client/test.pdf"); + // 输入query + // 注意file_ids不是必填项,如果不需要引用特定文档,则将new String[]{fileId}更换为new String[]{}即可 + AppBuilderClientIterator itor = builder.run("中国四大传统节日是哪四个", conversationId, new String[]{fileId}, false); + StringBuilder answer = new StringBuilder(); + // itor.hasNext()返回false时,表示流式调用结束 + while(itor.hasNext()) + { + AppBuilderClientResult response = itor.next(); + answer.append(response.getAnswer()); + for (Event event : response.getEvents()) { + switch (event.getContentType()) { + case "rag": + List references = (List)event.getDetail().get("references"); + for (Object reference : references) { + ReferenceDetail ragDetail = JsonUtils.deserialize(JsonUtils.serialize(reference), ReferenceDetail.class); + System.out.println("-----------------------------------"); + System.out.println("参考文献ID:"+ragDetail.getId()); + System.out.println("参考文献内容:"+ragDetail.getContent()); + System.out.println("来源:"+ragDetail.getFrom()); + System.out.println("BaiduSearch链接:"+ragDetail.getUrl()); + System.out.println("类型:"+ragDetail.getType()); + System.out.println("文档片段ID:"+ragDetail.getSegmentId()); + System.out.println("文档ID:"+ragDetail.getDocumentId()); + System.out.println("文档名称:"+ragDetail.getDocumentName()); + System.out.println("文档所属数据集ID:"+ragDetail.getDatasetId()); + System.out.println("-----------------------------------"); + } + break; + default: + // System.out.println(event); + } + } + } + System.out.print("输出:"); + System.out.println(answer); + } +} + +class ReferenceDetail { + private int id; + private String content; + private String from; + private String url; + private String type; + @SerializedName("segment_id") + private String segmentId; + @SerializedName("document_id") + private String documentId; + @SerializedName("document_name") + private String documentName; + @SerializedName("dataset_id") + private String datasetId; + @SerializedName("knowledgebase_id") + private String knowledgebaseId; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getFrom() { + return from; + } + + public void setFrom(String from) { + this.from = from; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getSegmentId() { + return segmentId; + } + + public void setSegmentId(String segmentId) { + this.segmentId = segmentId; + } + + public String getDocumentId() { + return documentId; + } + + public void setDocumentId(String documentId) { + this.documentId = documentId; + } + + public String getDocumentName() { + return documentName; + } + + public void setDocumentName(String documentName) { + this.documentName = documentName; + } + + public String getDatasetId() { + return datasetId; + } + + public void setDatasetId(String datasetId) { + this.datasetId = datasetId; + } + + public String getKnowledgebaseId() { + return knowledgebaseId; + } + + public void setKnowledgebaseId(String knowledgebaseId) { + this.knowledgebaseId = knowledgebaseId; + } + + @Override + public String toString() { + return "RAGReference{" + + "id=" + id + + ", content='" + content + '\'' + + ", from='" + from + '\'' + + ", url='" + url + '\'' + + ", type='" + type + '\'' + + ", segmentId='" + segmentId + '\'' + + ", documentId='" + documentId + '\'' + + ", documentName='" + documentName + '\'' + + ", datasetId='" + datasetId + '\'' + + ", knowledgebaseId='" + knowledgebaseId + '\'' + + '}'; + } +} +``` + +## Go基本用法 + +### ```NewAppBuilderClient()``` + +#### 方法参数 + +| 参数名称 | 参数类型 | 描述 | 示例值 | +| -------- | --------- | ----------------- | -------------- | +| app_id | string | 线上Agent应用的ID | "正确的应用ID" | +| config | SDKConfig | SDK配置信息 | | + +### ```CreateConversation()``` +#### 方法入参 +无 +#### 方法出参 +| 参数名称 | 参数类型 | 描述 | 示例值 | +| -------------- | -------- | -------------------------------------------- | ------ | +| ConversationId | str | 创建成功的对话对象,后续操作都基于该对象进行 | | + + +### `Run()` +#### Run方法入参`AppBuilderClientRunRequest` + +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +| -------------- | ---------- | -------- | ------------------------------------------------------------ | -------------------- | +| ConversationID | string | 是 | 对话ID,可以通过CreateConversation()获取 | | +| Query | string | 是 | query内容 | "汽车性能参数怎么样" | +| Stream | bool | 是 | 为true时则流式返回,为false时则一次性返回所有内容, 推荐设为true,降低首token时延 | | +| AppID | string | 是 | 应用ID,线上Agent应用的ID | | +| Tools | []Tool | 否 | 一个列表,其中每个字典对应一个工具的配置 | | +| ToolOuptus | []ToolOupt | 否 | 内容为本地的工具执行结果,以自然语言/json dump str描述 | | +| ToolChoice | ToolChoice | 否 | 控制大模型使用组件的方式,仅对自主规划Agent生效。 | | + +`Tool`、`ToolOutput`、`ToolChoice`定义如下: + +```go +type Tool struct { + Type string `json:"type"` + Function Function `json:"function"` +} + +type Function struct { + Name string `json:"name"` + Description string `json:"description"` + Parameters map[string]interface{} `json:"parameters"` +} + +type ToolOutput struct { + ToolCallID string `json:"tool_call_id" description:"工具调用ID"` + Output string `json:"output" description:"工具输出"` +} + +type ToolChoice struct { + Type string `json:"type"` + Function ToolChoiceFunction `json:"function"` +} + +type ToolChoiceFunction struct { + Name string `json:"name"` + Input map[string]interface{} `json:"input"` +} +``` + +#### Run方法出参 + +| 参数名称 | 参数类型 | 描述 | 示例值 | +| ------------------------ | ------------------------ | --------------------------------------- | ------ | +| AppBuilderClientIterator | AppBuilderClientIterator | 回答迭代器,流式/非流式均统一返回该类型 | | +| error | error | 存在错误时error不为nil,反之 | | + +#### 迭代AgentBuilderIterator + +| 参数名称 | 参数类型 | 描述 | 示例值 | +| ------------- | ----------- | -------------------- | --------------------------------------------------------------------------------------- | +| +Answer | string | 智能体应用返回的回答 | | +| +Events | []Event | 事件列表 | | +| +Events[0] | Event | 具体事件内容 | | +| ++Code | string | 错误码 | | +| ++Message | string | 错误具体消息 | | +| ++Status | string | 事件状态 | 状态描述,preparing(准备运行)running(运行中)error(执行错误) done(执行完成) | +| ++EventType | string | 事件类型 | | +| ++ContentType | string | 内容类型 | 可选值包括:code text, image, status,image, function_call, rag, audio、video等 | +| ++Detail | interface{} | 事件输出详情 | 代码解释器、文生图、工具组件、RAG等的详细输出内容 | +| ++Usage | Usage | 模型调用的token用量 | Usage(prompt_tokens=1322, completion_tokens=80, total_tokens=1402, name='ERNIE-4.0-8K') | + + +#### Run示例代码 + + +```Go +// 安装说明: +// go get github.com/baidubce/app-builder/go/appbuilder + +package main + +import ( + "errors" + "fmt" + "io" + "os" + + "github.com/baidubce/app-builder/go/appbuilder" +) + +func main() { + // 设置环境中的TOKEN,以下TOKEN请替换为您的个人TOKEN,个人TOKEN可通过该页面【获取鉴权参数】或控制台页【密钥管理】处获取 + os.Setenv("APPBUILDER_TOKEN", "bce-v3/ALTAK-xxx90ea58") + // 从AppBuilder控制台【个人空间】-【应用】网页获取已发布应用的ID + appID := "4678492a-xxx-654538d3503c" + config, err := appbuilder.NewSDKConfig("", "") + if err != nil { + fmt.Println("new config failed: ", err) + return + } + + builder, err := appbuilder.NewAppBuilderClient(appID, config) + if err != nil { + fmt.Println("new agent builder failed: ", err) + return + } + conversationID, err := builder.CreateConversation() + if err != nil { + fmt.Println("create conversation failed: ", err) + return + } + + i, err := builder.Run(conversationID, "你好,你能做什么?", nil, false) + if err != nil { + fmt.Println("run failed: ", err) + return + } + + var answer *appbuilder.AppBuilderClientAnswer + for answer, err = i.Next(); err == nil; answer, err = i.Next() { + fmt.Println(answer.Answer) + } +} +``` + +#### ToolCall功能示例代码 + +```go +package main + +import ( + "errors" + "fmt" + "io" + "os" + + "github.com/baidubce/app-builder/go/appbuilder" +) + +func main() { + // 设置APPBUILDER_TOKEN、GATEWAY_URL_V2环境变量 + os.Setenv("APPBUILDER_TOKEN", "请设置正确的应用密钥") + // 默认可不填,默认值是 https://qianfan.baidubce.com + os.Setenv("GATEWAY_URL_V2", "") + config, err := appbuilder.NewSDKConfig("", "") + if err != nil { + fmt.Println("new config failed: ", err) + return + } + // 初始化实例 + appID := "请填写正确的应用ID" + builder, err := appbuilder.NewAppBuilderClient(appID, config) + if err != nil { + fmt.Println("new agent builder failed: ", err) + return + } + // 创建对话ID + conversationID, err := builder.CreateConversation() + if err != nil { + fmt.Println("create conversation failed: ", err) + return + } + + jsonStr := ` + { + "type": "function", + "function": { + "name": "get_cur_whether", + "description": "这是一个获得指定地点天气的工具", + "parameters": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "省,市名,例如:河北省" + }, + "unit": { + "type": "string", + "enum": ["摄氏度", "华氏度"] + } + }, + "required": ["location"] + } + } + }` + + var tool Tool + err = json.Unmarshal([]byte(jsonStr), &tool) + if err != nil { + fmt.Println("unmarshal tool error:", err) + return + } + + i, err := client.Run(appbuilder.AppBuilderClientRunRequest{ + AppID: appID, + Query: "今天北京的天气怎么样?", + ConversationID: conversationID, + Stream: true, + Tools: []appbuilder.Tool{tool}, + }) + if err != nil { + fmt.Println("run failed:", err) + } + totalAnswer := "" + toolCallID := "" + for answer, err := i.Next(); err == nil; answer, err = i.Next() { + totalAnswer += answer.Answer + lastEvent := answer.Events[len(answer.Events)-1] + toolCallID = lastEvent.ToolCalls[len(lastEvent.ToolCalls)-1].ID + } + + i2, err := client.Run(appbuilder.AppBuilderClientRunRequest{ + ConversationID: conversationID, + AppID: appID, + ToolOutputs: []appbuilder.ToolOutput{ + { + ToolCallID: toolCallID, + Output: "北京今天35度", + }, + }, + Stream: true, + }) + + if err != nil { + fmt.Println("run failed: ", err) + } + + for answer, err := i2.Next(); err == nil; answer, err = i2.Next() { + totalAnswer = totalAnswer + answer.Answer + for _, ev := range answer.Events { + evJSON, _ := json.Marshal(ev) + fmt.Println(string(evJSON)) + } + } + + fmt.Println("----------------answer-------------------") + fmt.Println(totalAnswer) +} +``` diff --git a/docs/Application/Agent/ToolCall/tool_call.md b/docs/Application/Agent/ToolCall/tool_call.md new file mode 100644 index 000000000..a4318c072 --- /dev/null +++ b/docs/Application/Agent/ToolCall/tool_call.md @@ -0,0 +1,633 @@ +# Agent 本地ToolCall(Functioncall) + +## 概述 + +### 什么是ToolCall + +解释该问题,需要了解以下的知识点:`Agent工具` -> `FunctionCall` - `ToolCall` + +AIAgent 有四大核心组件:记忆、规划、工具和执行。其中工具部分,与我们的开发关系最密切,在各类Agent开发平台/工具中,常被称为“组件”、"插件"、"能力"等. + +关于Agent的工具的定义与分类,如下图~ + +drawing + +Agent使用工具的流程,一般称为`FunctionCall`,最早由OpenAI提出,并在[Assistant API](https://platform.openai.com/docs/assistants/overview)中广泛应用。 + + +ToolCall,则是AppBuilder平台提出的一种进阶的FunctionCall,本质与OpenAI的FunctionCall一致,但具有以下两个特点: + +- **端云组件联动**: Agent 调用工具时,可以同时调用云端和本地组件。 + +- **组件类型泛化**: AppBuilder在未来会支持多种类型组件,已经超出了Function的含义,例如数据库、记忆库、工作流等等 + +### 什么是端云组件联动,要解决什么问题 + +我们首先从工具的执行位置出发展开~ 在使用如AppBuilder / Coze 等平台开发Agent时,我们可以使用很多平台组件广场中,官方提供的组件,这里组件开箱即用,非常方便。 + +drawing + +但是存在一个问题,基于平台云端组件开发的应用,无法调用内网/局域网/私域的知识与能力,也无法与本地的工具进行联动,限制了Agent的灵活性。 + +我们在解决实际业务问题时,常遇到需要访问内网链接API或本地/硬件功能的FunctionCall需求,AppBuilder ToolCall可以解决这个问题: + +* 1、用户可注册一个本地运行的组件到已发布的应用 +* 2、由AppBuilder-Agent的云端思考模型进行规划和参数生成 +* 3、用户基于生成的参数调用本地组件,并再上传运行结果 +* 4、以此实现将本地组件能力嵌入到应用整体流程 + + +drawing + +## ToolCall(FunctionCall)基础知识介绍 + +### Agent是如何调用Tool的 + +我们可以将Agent的黑箱拆解为以下几个部分: +1. Agent的背景信息 +2. Agent的输入信息 +3. Agent的思考过程 +4. Agent触发组件调用 +5. Agent基于组件输出反思总结 + +#### Agent的背景信息包含以下几个部分 +- 角色定义描述(Prompt):定义Agent的角色 +- 能力描述(Prompt):定义Agent可以干什么 +- 工具描述(JsonSchema/Str):将工具的输入和输出,按照规范,定义为一段字符串,作为最终大模型Prompt的一部分 + +#### Agent的输入信息包含以下几个部分 +- 用户输入(Query/Prompt):用户输入的文本 +- 对话相关的文件(File/Url):与本地对话相关的文件路径 + +#### Agent的思考过程 +AppBuilder-Agent会将背景信息与输入信息,拼接为最终的Prompt,然后调用大模型推理。 + +Prompt的一个简单且直观的例子是: + +你是`{角色定义描述}`,你可以做以下事情:`{能力描述}`,你可以使用这些工具:`{工具描述-description}`,工具依赖的输入是:`{工具描述-paramters-properties-name}`,这些输入的格式分别是`{工具描述-paramters-properties-type}`。现在用户的问题是`{用户输入}`,与该问题相关的文件是`{对话相关的文件}`,请你解决用户的这个问题。 + +#### Agent触发组件调用 + +如果用户的query和组件能够解决的问题匹配,那么大模型就会尝试根据prompt里给出的工具的描述,从query中提炼出该次调用工具所需的参数,生成一个ToolCall命令,交给执行组件的模块去执行。 + +例如,我们给出的组件能力是"查找公司内指定人员的信息",函数的参数名为"name"。当用户输入"查找张三的信息",大模型会从query中提炼出参数"name=张三"这个信息。 + +drawing + +#### Agent基于组件输出反思总结 + +组件运行模块执行组件后,会给出字符串形式的结果给到Agent,Agent会再次将结果拼接为Prompt,然后调用大模型推理。判断用户的需求是否已经解决。如果解决了,则经过一个对话模块,总结用户的需求,并生成一个对话记录。如果未解决,则继续调用大模型推理,尝试调用更多的工具,直到用户的需求被解决。 + +### 开发者如何命令Agent调用本地Tool + +我们以AppBuilder-SDK中的AppBuilder-Client的基础代码为例,介绍开发者应该如何使用ToolCall功能 + + +```python +import appbuilder + +# 实例化AppBuilderClient +app_client = appbuilder.AppBuilderClient(app_id) +conversation_id = app_client.create_conversation() + +# 第一次对话,输入原始的query 和 工具描述 +message_1 = app_client.run( + conversation_id=conversation_id, + query="请问张三同学的生日是哪天?", + tools=tools +) +tool_call = message_1.content.events[-1].tool_calls[-1] +tool_call_id = tool_call.id + +# 第二次对话,在本地执行组件后,上传组件的运行结果 +tool_call_result = "张三同学的生日是2008年8月8日" +message_2 = app_client.run( + conversation_id=conversation_id, + tool_outputs=[{ + "tool_call_id": tool_call_id, + "output": tool_call_result + }] +) +print(message_2.content) +``` + +其中`AppBuilderClient`的`run`方法是核心,我们展开该函数的定义和参数介绍: + +`AppBuilderClient().run() -> Message` + +```python +def run(self, conversation_id: str, + query: str = "", + file_ids: list = [], + stream: bool = False, + tools: list[data_class.Tool] = None, + tool_outputs: list[data_class.ToolOutput] = None, + **kwargs + ) -> Message: + r""" + 参数: + query (str: 必须): query内容 + conversation_id (str, 必须): 唯一会话ID,如需开始新的会话,请使用self.create_conversation创建新的会话 + file_ids(list[str], 可选): + stream (bool, 可选): 为True时,流式返回,需要将message.content.answer拼接起来才是完整的回答;为False时,对应非流式返回 + tools(list[data_class.Tools], 可选): 一个Tools组成的列表,其中每个Tools对应一个工具的配置, 默认为None + tool_outputs(list[data_class.ToolOutput], 可选): 工具输出列表,格式为list[ToolOutput], ToolOutputd内容为本地的工具执行结果,以自然语言/json dump str描述,默认为None + 返回: message (obj: `Message`): 对话结果. + """ + pass +``` + + +| 参数名称 | 参数类型 | 是否必须 | 描述 | 示例值 | +| --------------- | ---------------- | -------- | ------------------------------------------------------------ | ----------------- | +| conversation_id | String | 是 | 会话ID | | +| query | String | 否 | query问题内容 | "今天天气怎么样?" | +| file_ids | list[String] | 否 | 对话可引用的文档ID | | +| stream | Bool | 否 | 为true时则流式返回,为false时则一次性返回所有内容, 推荐设为true,降低首token时延 | False | +| tools | List[Tool] | 否 | 一个列表,其中每个字典对应一个工具的配置 | | +| tools[0] | Tool | 否 | 工具配置 | | +| +type | String | 否 | 枚举:
**file_retrieval**: 知识库检索工具能够理解文档内容,支持用户针对文档内容的问答。
**code_interpreter**: 代码解释器, 代码解释器能够生成并执行代码,从而协助用户解决复杂问题,涵盖科学计算(包括普通数学计算题)、数据可视化、文件编辑处理(图片、PDF文档、视频、音频等)、文件格式转换(如WAV、MP3、text、SRT、PNG、jpg、MP4、GIF、MP3等)、数据分析&清洗&处理(文件以excel、csv格式为主)、机器学习&深度学习建模&自然语言处理等多个领域。
**function**: 支持fucntion call模式调用工具 | | +| +function | Function | 否 | Function工具描述
仅当**type为**`**function**` 时需要且必须填写 | | +| ++name | String | 否 | 函数名
只允许数字、大小写字母和中划线和下划线,最大长度为64个字符。一次运行中唯一。 | | +| ++description | String | 否 | 工具描述 | | +| ++parameters | Dict | 否 | 工具参数, json_schema格式 | | +| tool_outputs | List[ToolOutput] | 否 | 内容为本地的工具执行结果,以自然语言/json dump str描述 | | +| tool_outputs[0] | ToolOutput | 否 | 工具执行结果 | | +| +tool_call_id | String | 否 | 工具调用ID | | +| +output | String | 否 | 工具输出 | | + +`Tool`与`Function`是本地组件的描述,类型为object,其定义如下: + +```python +class Tool(BaseModel): + type: str = "function" + function: Function = Field(..., description="工具信息") + +class Function(BaseModel): + name: str = Field(..., description="工具名称") + description: str = Field(..., description="工具描述") + parameters: dict = Field(..., description="工具参数, json_schema格式") +``` + +`ToolOutput`是本地组件的执行结果,需要再次上传到Agent,参与思考,类型为object,其定义如下: +```python +class ToolOutput(BaseModel): + tool_call_id: str = Field(..., description="工具调用ID") + output: str = Field(..., description="工具输出") + +``` + + +#### Run方法带ToolCall调用示例--Python + +以下示例展示了三种方式来使用 ToolCall 进行调用,并演示了如何在 AppBuilder 环境中配置和执行会话调用。 + +**方式1:使用 JSONSchema 格式直接描述 tools 调用** + +```python +import appbuilder +from appbuilder.core.console.appbuilder_client import data_class +import os + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +# 设置环境变量 +os.environ["APPBUILDER_TOKEN"] = "..." +app_id = "..." # 已发布AppBuilder应用的ID +# 初始化智能体 +client = appbuilder.AppBuilderClient(app_id) +# 创建会话 +conversation_id = client.create_conversation() +tools = [ + { + "type": "function", + "function": { + "name": "get_current_weather", + "description": "仅支持中国城市的天气查询,参数location为中国城市名称,其他国家城市不支持天气查询", + "parameters": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "城市名,举例:北京", + }, + "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}, + }, + "required": ["location", "unit"], + }, + }, + } +] + +msg = client.run( + conversation_id=conversation_id, query="今天北京天气怎么样?", tools=tools +) +print(msg.model_dump_json(indent=4)) + +event = msg.content.events[-1] + +msg_2 = client.run( + conversation_id=conversation_id, + tool_outputs=[{"tool_call_id": event.tool_calls[-1].id, "output": "北京今天35度"}], +) +print(msg_2.model_dump_json(indent=4)) +``` + +**方式2: 使用 function_to_model 将函数对象传递为 ToolCall 的调用** + +```python +import appbuilder +import os + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +# 设置环境变量 +os.environ["APPBUILDER_TOKEN"] = "..." +app_id = "..." # 已发布AppBuilder应用的ID +# 初始化智能体 +client = appbuilder.AppBuilderClient(app_id) +# 创建会话 +conversation_id = client.create_conversation() +#注意:要使用此方法要为函数写好注释。最好按照谷歌规范来写 + +#定义示例函数 +def get_current_weather(location: str, unit: str) -> str: + """获取指定中国城市的当前天气信息。 + + 仅支持中国城市的天气查询。参数 `location` 为中国城市名称,其他国家城市不支持天气查询。 + + Args: + location (str): 城市名,例如:"北京"。 + unit (int): 温度单位,支持 "celsius" 或 "fahrenheit"。 + + Returns: + str: 天气情况描述 + """ + return "北京今天25度" + +#定义函数列表 +functions = [get_current_weather] +function_map = {f.__name__: f for f in functions} +#调用大模型 +msg = client.run( + conversation_id=conversation_id, + query="今天北京的天气怎么样?", + tools = [appbuilder.Manifest.from_function(f) for f in functions] + ) +print(msg.model_dump_json(indent=4)) +# 获取最后的事件和工具调用信息 +event = msg.content.events[-1] +tool_call = event.tool_calls[-1] + +# 获取函数名称和参数 +name = tool_call.function.name +args = tool_call.function.arguments + +# 将函数名称映射到具体的函数并执行 +raw_result = function_map[name](**args) + +# 传递工具的输出 +msg_2 = client.run( + conversation_id=conversation_id, + tool_outputs=[{ + "tool_call_id": tool_call.id, + "output": str(raw_result) + }], +) +print(msg_2.model_dump_json(indent=4)) +``` + +**方式3: 使用装饰器进行描述** + +```python +import os +import json +import appbuilder +from appbuilder import manifest, manifest_parameter + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +# 设置环境变量 +os.environ["APPBUILDER_TOKEN"] = "" +app_id = "" # 已发布AppBuilder应用的ID +# 初始化智能体 +client = appbuilder.AppBuilderClient(app_id) +# 创建会话 +conversation_id = client.create_conversation() + +#使用manifest装饰描述函数,manifest_parameter装饰器描述参数,manifest_return装饰器描述函数返回值。 +@manifest(description="获取指定中国城市的当前天气信息。仅支持中国城市的天气查询。参数 `location` 为中国城市名称,其他国家城市不支持天气查询。") +@manifest_parameter(name="location", description="城市名,例如:北京。") +@manifest_parameter(name="unit", description="温度单位,支持 'celsius' 或 'fahrenheit'") +#定义示例函数 +def get_current_weather(location: str, unit: str) -> str: + return "北京今天25度" + +print(json.dumps(appbuilder.Manifest.from_function(get_current_weather), indent=4, ensure_ascii=False)) +#定义函数列表 +functions = [get_current_weather] +function_map = {f.__name__: f for f in functions} +#调用大模型 +msg = client.run( + conversation_id=conversation_id, + query="今天北京的天气怎么样?", + tools = [appbuilder.Manifest.from_function(f) for f in functions] + ) +print(msg.model_dump_json(indent=4)) +# 获取最后的事件和工具调用信息 +event = msg.content.events[-1] +tool_call = event.tool_calls[-1] + +# 获取函数名称和参数 +name = tool_call.function.name +args = tool_call.function.arguments + +# 将函数名称映射到具体的函数并执行 +raw_result = function_map[name](**args) + +# 传递工具的输出 +msg_2 = client.run( + conversation_id=conversation_id, + tool_outputs=[{ + "tool_call_id": tool_call.id, + "output": str(raw_result) + }], +) +print(msg_2.model_dump_json(indent=4)) +``` + +#### Run方法带ToolCall调用示例--Java + +**创建tool的json文件** + +```json +{ + "type": "function", + "function": { + "name": "get_cur_whether", + "description": "这是一个获得指定地点天气的工具", + "parameters": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "省,市名,例如:河北省" + }, + "unit": { + "type": "string", + "enum": [ + "摄氏度", + "华氏度" + ] + } + }, + "required": [ + "location" + ] + } + } +} +``` + +```java +package org.example; + +import java.io.IOException; +import java.util.*; + +import com.google.gson.annotations.SerializedName; + +import com.baidubce.appbuilder.base.exception.AppBuilderServerException; +import com.baidubce.appbuilder.console.appbuilderclient.AppBuilderClient; +import com.baidubce.appbuilder.model.appbuilderclient.AppBuilderClientIterator; +import com.baidubce.appbuilder.model.appbuilderclient.AppBuilderClientResult; +import com.baidubce.appbuilder.model.appbuilderclient.Event; +import com.baidubce.appbuilder.base.utils.json.JsonUtils; + +class AppBuilderClientDemo { + + public static void main(String[] args) throws IOException, AppBuilderServerException { + System.setProperty("APPBUILDER_TOKEN", "请设置正确的应用密钥"); + String appId = "请设置正确的应用ID"; + AppBuilderClient builder = new AppBuilderClient(appId); + String conversationId = builder.createConversation(); + + AppBuilderClientRunRequest request = new AppBuilderClientRunRequest(appId, conversationId, "今天北京的天气怎么样?", false); + + String toolJson = new String(Files.readAllBytes(Paths.get("json文件所在的路径"))); + request.setTools(toolJson); + + AppBuilderClientIterator itor = builder.run(request); + String ToolCallID = ""; + while (itor.hasNext()) { + AppBuilderClientResult result = itor.next(); + Event lastEvent = result.getEvents()[result.getEvents().length - 1]; + ToolCallID = lastEvent.getToolCalls()[lastEvent.getToolCalls().length - 1].getId(); + System.out.println(result); + } + + AppBuilderClientRunRequest request2 = new AppBuilderClientRunRequest(appId, conversationId); + request2.setToolOutputs(ToolCallID, "北京今天35度"); + AppBuilderClientIterator itor2 = builder.run(request2); + while (itor2.hasNext()) { + AppBuilderClientResult result = itor2.next(); + System.out.println(result); + } + } +} + +``` + +#### ToolCall功能示例代码--Go + +```go +package main + +import ( + "errors" + "fmt" + "io" + "os" + + "github.com/baidubce/app-builder/go/appbuilder" +) + +func main() { + // 设置APPBUILDER_TOKEN、GATEWAY_URL_V2环境变量 + os.Setenv("APPBUILDER_TOKEN", "请设置正确的应用密钥") + // 默认可不填,默认值是 https://qianfan.baidubce.com + os.Setenv("GATEWAY_URL_V2", "") + config, err := appbuilder.NewSDKConfig("", "") + if err != nil { + fmt.Println("new config failed: ", err) + return + } + // 初始化实例 + appID := "请填写正确的应用ID" + builder, err := appbuilder.NewAppBuilderClient(appID, config) + if err != nil { + fmt.Println("new agent builder failed: ", err) + return + } + // 创建对话ID + conversationID, err := builder.CreateConversation() + if err != nil { + fmt.Println("create conversation failed: ", err) + return + } + + jsonStr := ` + { + "type": "function", + "function": { + "name": "get_cur_whether", + "description": "这是一个获得指定地点天气的工具", + "parameters": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "省,市名,例如:河北省" + }, + "unit": { + "type": "string", + "enum": ["摄氏度", "华氏度"] + } + }, + "required": ["location"] + } + } + }` + + var tool Tool + err = json.Unmarshal([]byte(jsonStr), &tool) + if err != nil { + fmt.Println("unmarshal tool error:", err) + return + } + + i, err := client.Run(appbuilder.AppBuilderClientRunRequest{ + AppID: appID, + Query: "今天北京的天气怎么样?", + ConversationID: conversationID, + Stream: true, + Tools: []appbuilder.Tool{tool}, + }) + if err != nil { + fmt.Println("run failed:", err) + } + totalAnswer := "" + toolCallID := "" + for answer, err := i.Next(); err == nil; answer, err = i.Next() { + totalAnswer += answer.Answer + lastEvent := answer.Events[len(answer.Events)-1] + toolCallID = lastEvent.ToolCalls[len(lastEvent.ToolCalls)-1].ID + } + + i2, err := client.Run(appbuilder.AppBuilderClientRunRequest{ + ConversationID: conversationID, + AppID: appID, + ToolOutputs: []appbuilder.ToolOutput{ + { + ToolCallID: toolCallID, + Output: "北京今天35度", + }, + }, + Stream: true, + }) + + if err != nil { + fmt.Println("run failed: ", err) + } + + for answer, err := i2.Next(); err == nil; answer, err = i2.Next() { + totalAnswer = totalAnswer + answer.Answer + for _, ev := range answer.Events { + evJSON, _ := json.Marshal(ev) + fmt.Println(string(evJSON)) + } + } + + fmt.Println("----------------answer-------------------") + fmt.Println(totalAnswer) +} +``` + +#### ToolChoice示例代码 + +* 注意:当前功能为试运行阶段,可能存在如下问题,如使用过程遇到其他问题,欢迎提issue或微信群讨论。 + + * 需开启"组件/知识库结论可直接作为回复" + + * 组件名称不是界面上的原始名字,而是个人空间组件列表中的英文名 + + * 自定义组件的参数不能使用系统参数,可以使用用户添加的参数 + + * 部分官方组件使用的参数与界面上的参数不一致 + + +```go +package main + +import ( + "errors" + "fmt" + "io" + "os" + + "github.com/baidubce/app-builder/go/appbuilder" +) + +func main() { + // 设置APPBUILDER_TOKEN、GATEWAY_URL_V2环境变量 + os.Setenv("APPBUILDER_TOKEN", "请设置正确的应用密钥") + // 默认可不填,默认值是 https://qianfan.baidubce.com + os.Setenv("GATEWAY_URL_V2", "") + config, err := appbuilder.NewSDKConfig("", "") + if err != nil { + fmt.Println("new config failed: ", err) + return + } + // 初始化实例 + appID := "请填写正确的应用ID" + builder, err := appbuilder.NewAppBuilderClient(appID, config) + if err != nil { + fmt.Println("new agent builder failed: ", err) + return + } + // 创建对话ID + conversationID, err := builder.CreateConversation() + if err != nil { + fmt.Println("create conversation failed: ", err) + return + } + + // 注意使用创建应用中用到的组件。名称、参数均以实际使用的组件为准。 + input := make(map[string]any) + input["city"] = "北京" + end_user_id := "go_toolchoice_demo" + i, err := client.Run(AppBuilderClientRunRequest{ + ConversationID: conversationID, + AppID: appID, + Query: "", + EndUserID: &end_user_id, + Stream: false, + ToolChoice: &ToolChoice{ + Type: "function", + Function: ToolChoiceFunction{ + Name: "WeatherQuery", + Input: input, + }, + }, + }) + + if err != nil { + fmt.Println("run failed: ", err) + return + } + + for answer, err := i.Next(); err == nil; answer, err = i.Next() { + for _, ev := range answer.Events { + evJSON, _ := json.Marshal(ev) + fmt.Println(string(evJSON)) + } + } +} +``` diff --git a/docs/Application/Agent/ToolChoice/tool_choice.md b/docs/Application/Agent/ToolChoice/tool_choice.md new file mode 100644 index 000000000..7d9d70e84 --- /dev/null +++ b/docs/Application/Agent/ToolChoice/tool_choice.md @@ -0,0 +1,176 @@ +# ToolChoice + +#### Run方法带ToolChoice使用示例: + +* 注意:当前功能为试运行阶段,可能存在如下问题,如使用过程遇到其他问题,欢迎提issue或微信群讨论。 + * 需开启"组件/知识库结论可直接作为回复" + * 组件名称不是界面上的原始名字,而是个人空间组件列表中的英文名 + * 自定义组件的参数不能使用系统参数,可以使用用户添加的参数 + * 部分官方组件使用的参数与界面上的参数不一致 + +```python +import appbuilder +import os + +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +# 设置环境变量 +os.environ["APPBUILDER_TOKEN"] = "..." + +app_id = "..." # 已发布AppBuilder应用的ID +# 初始化智能体 +client = appbuilder.AppBuilderClient(app_id) +# 创建会话 +conversation_id = client.create_conversation() + +# 注意使用创建应用中用到的组件。名称、参数均以实际使用的组件为准。 +answer = app_builder_client.run( + conversation_id, + "北京今天的天气", + stream=False, + end_user_id="user_id_toolchoice", + tool_choice={ + "type": "function", + "function": {"name": "WeatherQuery", "input": {"city": "北京"}}, + }, +) +``` + +#### Run方法带Toolchoice使用示例--Java + +* 注意:当前功能为试运行阶段,可能存在如下问题,如使用过程遇到其他问题,欢迎提issue或微信群讨论。 + + * 需开启"组件/知识库结论可直接作为回复" + + * 组件名称不是界面上的原始名字,而是个人空间组件列表中的英文名 + + * 自定义组件的参数不能使用系统参数,可以使用用户添加的参数 + + * 部分官方组件使用的参数与界面上的参数不一致 + +```java +package org.example; + +import java.io.IOException; +import java.util.*; + +import com.google.gson.annotations.SerializedName; + +import com.baidubce.appbuilder.base.exception.AppBuilderServerException; +import com.baidubce.appbuilder.console.appbuilderclient.AppBuilderClient; +import com.baidubce.appbuilder.model.appbuilderclient.AppBuilderClientIterator; +import com.baidubce.appbuilder.model.appbuilderclient.AppBuilderClientResult; +import com.baidubce.appbuilder.model.appbuilderclient.AppBuilderClientRunRequest; +import com.baidubce.appbuilder.model.appbuilderclient.Event; +import com.baidubce.appbuilder.base.utils.json.JsonUtils; + +class AppBuilderClientDemo { + + public static void main(String[] args) throws IOException, AppBuilderServerException { + System.setProperty("APPBUILDER_TOKEN", "请设置正确的应用密钥"); + String appId = "请设置正确的应用ID"; + AppBuilderClient builder = new AppBuilderClient(appId); + String conversationId = builder.createConversation(); + + AppBuilderClientRunRequest request = new AppBuilderClientRunRequest(appId, conversationId, "你能干什么", false); + request.setEndUserId("java_toolchoice_demo"); + + // 注意使用创建应用中用到的组件。名称、参数均以实际使用的组件为准。 + Map input = new HashMap<>(); + input.put("city", "北京"); + AppBuilderClientRunRequest.ToolChoice.Function func = new AppBuilderClientRunRequest.ToolChoice.Function( + "WeatherQuery", input); + AppBuilderClientRunRequest.ToolChoice choice = new AppBuilderClientRunRequest.ToolChoice("function", func); + request.setToolChoice(choice); + + AppBuilderClientIterator itor = builder.run(request); + while (itor.hasNext()) { + AppBuilderClientResult result = itor.next(); + System.out.println(result); + } + } +} + +``` + + +#### ToolChoice示例代码--Go + +* 注意:当前功能为试运行阶段,可能存在如下问题,如使用过程遇到其他问题,欢迎提issue或微信群讨论。 + + * 需开启"组件/知识库结论可直接作为回复" + + * 组件名称不是界面上的原始名字,而是个人空间组件列表中的英文名 + + * 自定义组件的参数不能使用系统参数,可以使用用户添加的参数 + + * 部分官方组件使用的参数与界面上的参数不一致 + + +```go +package main + +import ( + "errors" + "fmt" + "io" + "os" + + "github.com/baidubce/app-builder/go/appbuilder" +) + +func main() { + // 设置APPBUILDER_TOKEN、GATEWAY_URL_V2环境变量 + os.Setenv("APPBUILDER_TOKEN", "请设置正确的应用密钥") + // 默认可不填,默认值是 https://qianfan.baidubce.com + os.Setenv("GATEWAY_URL_V2", "") + config, err := appbuilder.NewSDKConfig("", "") + if err != nil { + fmt.Println("new config failed: ", err) + return + } + // 初始化实例 + appID := "请填写正确的应用ID" + builder, err := appbuilder.NewAppBuilderClient(appID, config) + if err != nil { + fmt.Println("new agent builder failed: ", err) + return + } + // 创建对话ID + conversationID, err := builder.CreateConversation() + if err != nil { + fmt.Println("create conversation failed: ", err) + return + } + + // 注意使用创建应用中用到的组件。名称、参数均以实际使用的组件为准。 + input := make(map[string]any) + input["city"] = "北京" + end_user_id := "go_toolchoice_demo" + i, err := client.Run(AppBuilderClientRunRequest{ + ConversationID: conversationID, + AppID: appID, + Query: "", + EndUserID: &end_user_id, + Stream: false, + ToolChoice: &ToolChoice{ + Type: "function", + Function: ToolChoiceFunction{ + Name: "WeatherQuery", + Input: input, + }, + }, + }) + + if err != nil { + fmt.Println("run failed: ", err) + return + } + + for answer, err := i.Next(); err == nil; answer, err = i.Next() { + for _, ev := range answer.Events { + evJSON, _ := json.Marshal(ev) + fmt.Println(string(evJSON)) + } + } +} +``` \ No newline at end of file diff --git a/docs/Application/Agent/UseOfficialComponents/use_official_components.md b/docs/Application/Agent/UseOfficialComponents/use_official_components.md new file mode 100644 index 000000000..0be61140f --- /dev/null +++ b/docs/Application/Agent/UseOfficialComponents/use_official_components.md @@ -0,0 +1,32 @@ +# Client应用调用官方组件 + +## 简介 + +Agent是基于线上Agent应用的问答组件,可以使用该组件利用线上Agent应用进行问答,同时可以在线上为Client应用添加组件,丰富Agent能力。 + +## Agent添加Components官方组件 + +整体使用流程包括以下两个环节: + +1. 在[百度智能云千帆AppBuilder官网](https://cloud.baidu.com/product/AppBuilder)创建并发布应用(在创建应用时可添加官方组件)、获取应用ID、获取密钥 +2. 引用AppBuilderSDK代码,初始化AppBuilderClient实例、创建会话、上传文档(可选)、执行对话 + +## 示例 + +- 接下来将展示创建一个机票查询Agent应用,并添加官方组件。 + +### 创建应用 + +- 创建应用并添加自己的角色指令,并按照自己的需求设置模型、记忆功能等参数 +![](https://bj.bcebos.com/v1/appbuilder-sdk-components/%E5%88%9B%E5%BB%BA%E8%88%AA%E7%8F%AD%E6%9F%A5%E8%AF%A2%E5%BA%94%E7%94%A8.png?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-12-17T03%3A51%3A31Z%2F-1%2Fhost%2Fde4a26a46469066111552bf91d50202433ef1cdd89f4945e7924ad11de38fb36) + +### 添加官方组件 + +- 添加官方组件航班查询组件 +![](https://bj.bcebos.com/v1/appbuilder-sdk-components/%E6%B7%BB%E5%8A%A0%E8%88%AA%E7%8F%AD%E6%9F%A5%E8%AF%A2%E6%8C%87%E4%BB%A4.png?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-12-17T03%3A52%3A17Z%2F-1%2Fhost%2F9dfcafb04d9e5992a4feda63f58ffe2211afb01b1e7b0f4f3ec4822ba840562c) + +### 发布应用并调用 + +- 完成应用设置,并发布应用,接下来就可以调用添加了官方组件的Client应用了,应用的具体调用方法请参考[Agent应用基础知识](https://github.com/baidubce/app-builder/blob/master/docs/Application/Agent/BasicKnowledge/agent.md) + + diff --git a/docs/Application/RAG/DatasetManage/dataset_manage.md b/docs/Application/RAG/DatasetManage/dataset_manage.md new file mode 100644 index 000000000..057332145 --- /dev/null +++ b/docs/Application/RAG/DatasetManage/dataset_manage.md @@ -0,0 +1,64 @@ +# console端知识库操作助手 + +## 目标 +用户可通过SDK对console端知识库进行操作,实现创建知识库、添加知识文档、查询知识库文档、删除知识文档等操作,可在平台console中查看结果。 + +```python +# 设置环境变量 +import os + +# 设置环境变量 +# 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 +os.environ["APPBUILDER_TOKEN"] = "..." +``` + +## 管理知识库 + +### 初始化已有知识库 +获取线上已有知识库的ID,可在[console](https://console.bce.baidu.com/ai_apaas/dataset)端查看,示例 + +image + +```python +import appbuilder +# 初始化已有线上知识库, dataset_id 可在平台console中查看 +dataset_id = "..." +dataset = appbuilder.console.Dataset(dataset_id) +``` + +### 创建全新知识库 + +```python +# 创建全新知识库 +dataset = appbuilder.console.Dataset.create_dataset("my_dataset") +``` + +### 上传文档到知识库 + +```python +# 设置文档路径,例如“./test.pdf” +file_path1 = "..." +file_path2 = "..." +file_paths = [file_path1, file_path2] +# 将文档上传到知识库 +document_infos = dataset.add_documents(file_paths) +print(document_infos) +``` + +### 获取知识库关联文档 +```python +# 获取第一页的文档列表, 每页10条 +document_list = dataset.get_documents(1, 10) +print(document_list) +``` + +### 删除知识库中的文档 +```python +# 删除第一个文档 +document_ids = [document_list.data[0].id] +dataset.delete_documents(document_ids) +``` + +### 知识库使用示例 + +- [知识库使用示例](https://github.com/baidubce/app-builder/blob/master/cookbooks/end2end_application/rag/qa_system_1_dataset.ipynb) \ No newline at end of file diff --git a/docs/DevelopGuide/AdvancedDevelopment/README.md b/docs/DevelopGuide/AdvancedDevelopment/README.md new file mode 100644 index 000000000..0c496f2dd --- /dev/null +++ b/docs/DevelopGuide/AdvancedDevelopment/README.md @@ -0,0 +1,14 @@ +# 开发指引 + +该文档目录包含以下内容: + +- [二次开发基本介绍](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/AdvancedDevelopment/README.md) +- [AppBuilder SDK 运行环境超参配置说明](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/EnvironmentalParameters/env.md) + + +## 二次开发 +当前已集成Python版本AppBuilder-SDK 0.9.4及相关依赖,方便开发者融入个人已有的大模型应用程序。此部分仍在不断建设中。 +二次开发可以采用官方提供的开发镜像,便于快速安装各种依赖库。也可在镜像中使用已安装的`appbuilder_trace_server`、`appbuilder_bce_deploy`工具。 +``` shell +docker pull registry.baidubce.com/appbuilder/appbuilder-sdk-devel:0.9.8 +``` \ No newline at end of file diff --git a/docs/DevelopGuide/ErrorMessage/error_message.md b/docs/DevelopGuide/ErrorMessage/error_message.md new file mode 100644 index 000000000..de9205112 --- /dev/null +++ b/docs/DevelopGuide/ErrorMessage/error_message.md @@ -0,0 +1,92 @@ +# SDK 错误信息 + +1、 `BaseRPCException` + +- 报错解释: + - Base RPC exception, + - SDK基类异常 + +2、 `BadRequestException` + +- 报错解释: + - BadRequestException represent HTTP Code 400 + - BadRequestException 表示请求错误,错误码为400 + +3、 `ForbiddenException` + +- 报错解释: + - ForbiddenException represent HTTP Code 403 + - ForbiddenException 表示禁止访问,错误码为403 + +4、 `NotFoundException` + +- 报错解释: + - NotFoundException represent HTTP Code 404 + - NotFoundException 表示资源不存在,错误码为404 + +5、 `PreconditionFailedException` + +- 报错解释: + - PreconditionFailedException represent HTTP Code 412 + - PreconditionFailedException 表示前置条件失败,错误码为412 + +6、 `InternalServerErrorException` + +- 报错解释: + - InternalServerErrorException represent HTTP Code 500 + - InternalServerErrorException 表示内部服务器错误,错误码为500 + + +7、 `HTTPConnectionException` + +- 报错解释: + - HTTPConnectionException represent HTTP Connection error + - HTTPConnectionException 表示HTTP连接错误 + +8、 `ModelNotSupportedException` + +- 报错解释: + - ModelNotSupportedException represent model is not supported + - ModelNotSupportedException 表示模型不支持 + +9、 `TypeNotSupportedException` + +- 报错解释: + - TypeNotSupportedException represent type is not supported + - TypeNotSupportedException 表示类型不支持 + +10、 `AppBuilderServerException` + +- 报错解释: + - AppBuilderServerException represent backend server failed response + - AppBuilderServerException 表示后端服务器响应失败 + +11、 `AssistantServerException` + +- 报错解释: + - AssistantSercerException represent assistant server failed response. + - AssistantSercerException 表示助理服务器响应失败 + +12、 `InvalidRequestArgumentError` + +- 报错解释: + - InvalidRequestArgumentError invalid request param + - InvalidRequestArgumentError 表示请求参数无效 + +13、 `RiskInputException` + +- 报错解释: + - RiskInputException represent risk input error + - RiskInputException 表示异常输入错误 + +14、 `AppbuilderBuildexException` + +- 报错解释: + - Appbuilder buledex exception + - AppbuilderBuildexException 表示AppBuilder-SDK构建异常,报错使用与Appbuilder-SDK的构造代码单元检测不符合规范 + +15、 `AppbuilderTraceException` + +- 报错解释: + - Appbuilder trace exception + - AppbuilderTraceException 表示AppBuilder-SDK追踪框架异常,使用`export APPBUILDER_TRACE_DEBUG=TRUE`来开启Appbuilder-SDK的追踪框架的DEBUG模式,展示报错的完整链路并调试 \ No newline at end of file diff --git a/docs/DevelopGuide/HowToContributeCode/README.md b/docs/DevelopGuide/HowToContributeCode/README.md index e56877380..46433c917 100644 --- a/docs/DevelopGuide/HowToContributeCode/README.md +++ b/docs/DevelopGuide/HowToContributeCode/README.md @@ -1,19 +1,23 @@ -# 开发指引 +# SDK 贡献代码规范 -该文档目录包含以下内容: +## 组件开发规范 -- [二次开发基本介绍](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/HowToContributeCode/README.md) -- [AppBuilder SDK 运行环境超参配置说明](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/EnvironmentalParameters/env.md) +### 组件整体介绍 +在无特殊情况下,一个官方组件(class Component)的实现可以拆解为以下几个关键模块,分别是 -## 二次开发 -当前已集成Python版本AppBuilder-SDK 0.9.4及相关依赖,方便开发者融入个人已有的大模型应用程序。此部分仍在不断建设中。 -二次开发可以采用官方提供的开发镜像,便于快速安装各种依赖库。也可在镜像中使用已安装的`appbuilder_trace_server`、`appbuilder_bce_deploy`工具。 -``` shell -docker pull registry.baidubce.com/appbuilder/appbuilder-sdk-devel:0.9.8 -``` +* def run:组件的run函数,规范体现在该函数的输入和输出 + * 输入规范 + * 输出规范 +* def tool_eval:组件功能的核心实现,规范体现在该函数的输入和输出 + * 输入规范 + * 输出规范 +* dict manifest:组件参与FunctionCall时,帮助大模型理解组件功能的说明,规范体现在manifest的内容和格式 + * manifest规范 + +### `run` 函数 -### 消息(Message) +#### 消息(Message) - 构建大模型应用的统一数据结构,基于Pydantic构建,在不同的Component之间流动。Message基类的默认字段是content,类型是Any。 ```python from appbuilder import Message @@ -22,9 +26,13 @@ input_list = Message(["text1", "text2", "text3"]) input_str = Message("红烧肉怎么做") ``` -### 组件(Component) -- 所有能力单元的标准结构,以Message结构作为输入输出,内部执行逻辑可在本地执行或调用云端服务,以下是官方组件的实现示例。 +#### `run` 函数输入输出规范 + +- 所有能力单元的标准结构,以Message结构作为输入输出,内部执行逻辑可在本地执行或调用云端服务,以下是官方组件的实现示例。`run` 函数需要添加 `@components_run_trace` 装饰器,实现对组件的trace。 + ```python +from appbuilder.utils.trace.tracer_wrapper import components_run_trace + class SimilarQuestionMeta(ComponentArguments): """ SimilarQuestionMeta """ @@ -67,6 +75,7 @@ class SimilarQuestion(CompletionBaseComponent): """ super().__init__(SimilarQuestionMeta, model=model) + @components_run_trace def run(self, message, stream=False, temperature=1e-10): """ 给定输入(message)到模型运行,同时指定运行参数,并返回结果。 @@ -80,4 +89,411 @@ class SimilarQuestion(CompletionBaseComponent): obj:`Message`: 模型运行后的输出消息。 """ return super().run(message=message, stream=stream, temperature=temperature) -``` \ No newline at end of file +``` + +### `tool_eval` 函数 + +#### `ComponentOutput` 类 + +```python +class ComponentOutput(BaseModel): + role: str = Field(default="tool", + description="role是区分当前消息来源的重要字段,对于绝大多数组件而言,都是填写tool,标明role所在的消息来源为组件。部分思考及问答组件,role需要填写为assistant") + content: list[Content] = Field(default=[], + description="content是当前组件返回内容的主要payload,List[Content],每个Content Dict 包括了当前输出的一个元素") +``` + +#### `tool_eval` 函数输入输出规范 + +- 组件的核心实现,需要添加 `@components_run_stream_trace` 装饰器,实现对组件的trace。 + +##### `tool_eval` 函数 输入参数 + +* 组件tool_eval方法的输入,除了在manifest中约定的参数外,也可能会传入以下系统变量,辅助组件的运行。 +* 系统入参列表中的字段是保留字段,组件定义的manifest不能与系统参数重名。系统参数中有可以被用户设置的参数例如uploaded_files,也有不能设置的字段例如traceid等。 +* 在组件的开发中,以下系统输入字段体现为 def tool_eval(self, key1, key2, \*\*kwargs)中\*\*kwargs包含的内容,key1和key2是manifest中约定的参数,kwargs中的内容是系统入参。 + +##### `tool_eval` 函数 组件返回字段 + +* 组件返回参数统一采用json字段,固定key名称和对应的value,value默认是dict类型,value本身需要指定visible_scope。 +* 非流式返回结果,按照所有流式内容的key-value进行合并,例如两个event都是references,那么需要两组references合并,所有组件需要支持非流式返回。 +* 基于sse协议提供流式数据 +* content 本身是个 List[Dict],每个 Dict是当前 event 的一个元素,一般有多个元素的返回例如 urls/files 才需要多个 Dict + +###### 组件返回字段总览 + +|字段|类型|是否必须|默认值 及 取值范围|作用说明|备注| +|---|---|---|---|---|---| +|role|str|否|- tool 默认
- user
- assistant
|ole是区分当前消息来源的重要字段,对于绝大多数组件而言,都是填写tool,标明role所在的消息来源为组件。部分思考及问答组件,role需要填写为assistant|{"role": "tool"}| +|content|list[dict]|是|[]Event|当前组件返回内容的主要payload,List[Dict],每个 Dict 包括了当前 event 的一个元素|| +|+ name|str|否|part1,part2或者3d_pics,title|介绍当前yield内容的step name使用name的必要条件,是有不同content需要是属于结构上的不同字段,但又是streaming的|| +|+ type|str|是|* text 默认
* code
* files
* urls
* oral_text
* references
* image
* chart
* audio
* json|代表event 类型,包括 text、code、files、urls、oral_text、references、image、chart、audio、tought、json
该字段的取值决定了下面text字段的内容结构|| +|+ text|dict
object|是|{}|代表当前 event 元素的内容,每一种 event 对应的 text 结构固定|保留字段
"text": {'filename': 'chart_url.png', 'url': 'https://chart_url.png'},| +|+ visible_scope|str|否|all 默认
llm
user
空|为了界面展示明确的说明字段
* llm为思考模型可见,类似function calling结果中submit的执行结果
* user为终端用户可见|workflow中存在消息通知节点,类型为notice
目前实测,llm、user、all用户都可见,只是气泡不一样。llm在下拉框中,user直接输出到气泡中。| +|+ raw_data|dict
object|否|{}|内部信息,由开发者请求透传,内部系统返回的信息,例如API节点收到的resp,大模型节点的MB resp|{
"origin_response": "xxxxx"
}| +|+ usage|list of dict
object|否|{}|大模型的token用量|{
"prompt_tokens": 1547,
"completion_tokens": 2,
"total_tokens": 1549,
"name": "ERNIE Speed-AppBuilder"
}| +|+ metrics|dict
object|否|{}|耗时、性能、内存等trace及debug所需信息|{
"begin_timestamp": xxxxx
"end_timestamp": xxxxx
}| + +##### 包含 `manifests` 定义的 `tool_eval` 函数返回示例 + +```python +class SimilarQuestion(CompletionBaseComponent): + r""" + 基于输入的问题, 挖掘出与该问题相关的类似问题。广泛用于客服、问答等场景。 + + Examples: + + .. code-block:: python + + import os + import appbuilder + + os.environ["APPBUILDER_TOKEN"] = "..." + + qa_mining = appbuilder.SimilarQuestion(model="Qianfan-Agent-Speed-8k") + + msg = "我想吃冰淇淋,哪里的冰淇淋比较好吃?" + msg = appbuilder.Message(msg) + answer = qa_mining(msg) + + print("Answer: \n{}".format(answer.content)) + """ + name = "similar_question" + version = "v1" + meta = SimilarQuestionMeta + + manifests = [ + { + "name": "similar_question", + "description": "基于输入的问题,挖掘出与该问题相关的类似问题。", + "parameters": { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "输入的问题,用于大模型根据该问题输出相关的类似问题。" + } + }, + "required": [ + "query" + ] + } + } + ] + + def __init__( + self, + model: str="Qianfan-Agent-Speed-8K", + secret_key: Optional[str] = None, + gateway: str = "", + lazy_certification: bool = True, + ): + """初始化StyleRewrite模型。 + + Args: + model (str|None): 模型名称,用于指定要使用的千帆模型。 + secret_key (str, 可选): 用户鉴权token, 默认从环境变量中获取: os.getenv("APPBUILDER_TOKEN", ""). + gateway (str, 可选): 后端网关服务地址,默认从环境变量中获取: os.getenv("GATEWAY_URL", "") + lazy_certification (bool, 可选): 延迟认证,为True时在第一次运行时认证. Defaults to False. + + Returns: + None + + """ + super().__init__( + SimilarQuestionMeta, model=model, secret_key=secret_key, gateway=gateway, + lazy_certification=lazy_certification) + + @components_run_stream_trace + def tool_eval(self, + query: str, + **kwargs): + """ + 根据给定的query和可选参数生成并返回文本输出。 + + Args: + query (str): 需要生成文本的输入查询字符串。 + **kwargs: 其他可选参数。 + + Returns: + Generator[Output]: 返回一个生成器,生成类型为Output的对象。 + + """ + traceid = kwargs.get("_sys_traceid") + msg = Message(query) + model_configs = kwargs.get('model_configs', {}) + temperature = model_configs.get("temperature", 1e-10) + top_p = model_configs.get("top_p", 0.0) + message = super().run(message=msg, stream=False, temperature=temperature, top_p=top_p, request_id=traceid) + + yield self.create_output(type="text", text=str(message.content), name="text", usage=message.token_usage) +``` + +### `manifest` 规范 + +* 若组件有tool_eval方法,则必须要求存在manifest + * 现状:算法手动撰写;未来:提供工具,自动从python函数的入参及注释转manifest +* mainfests是一个list[dict],是对组件多个能力的规范化描述,如无特殊情况,一般list中只有一个元素,对应tool_eval的能力 +* manifest dict要满足json schema协议要求 +* 要求manifest dict中parameters-properties定义的参数,与def tool_eval的入参一致 +* 组件中的version字段,会影响的组件URL,参考组件API:组件调用形如: + * /v2/components/${component}/version/{$version}?action=${action} +* 遵循新规范的组件,因输入输出与原组件不兼容,在实现上有显著的标志区分 + +#### Json Schema协议 + +- [Json Schema协议](https://json-schema.org/overview/what-is-jsonschema),以下是示例 + +```python +class BaiduSearchWithModel(Component): + r""" + 百度搜索总结工具 + """ + name = "baidu_search_with_model" + version = "v1" # 修改此处,会影响组件的调用URL + manifests = [ + { + "name": "baidu_search_with_model", # 组件名称 + "description": "对百度搜索结果进行大模型总结", # 组件描述,该字段重要,影响 function calling 效果 + "parameters": { # parameters 描述组件入参列表 + "type": "object", + "properties": { # 多个参数可以指定多个 properties + "query": { + "type": "string", # 参数query 的类型 + "description": "搜索关键词" # 参数query 的描述,该字段重要,影响 function calling 效果 + } + }, + "required": [ + "query" # query参数为必填字段 + ] + } + } + ] +``` + +## 代码合入单元测试规范 + +* 现状: 当前开源Appbuilder-SDK已经部署了单元测试流水线,并要求90%的单元测试覆盖率合入要求,要求开发者实现完整已开发的代码的端到端的测试,并且要求代码增量行覆盖率为90% + +### 单元测试规范 + +#### 单元测试要求 + +- 覆盖if-else分支 + - 对于包含if-else逻辑的代码,需要编写测试用例来确保每个分支都被执行到 +- 输入的边界条件检查 + - 边界条件通常指的是数据范围的极值(如最小值和最大值),或者特定情况下的特殊值(如空值、空字符串、负值等) +- Error raise的覆盖 + - 确保测试覆盖了所有可能抛出异常的代码路径,包括代码自身的预期错误,以及访问远程服务失败后的错误处理 + +#### Test文件目录 + +* test文件需要为『test_』开头 +* 测试类需要形如『class TestAgentRuntime(unittest.TestCase)::』的定义方式 +* test文件需要置于appbuilder-sdk-ext/appbuilder_sdk_ext/tests路径下 + +#### UnitTest提供三种标签实现两种运行模式 + +* 添加下列标签,单元测试脚本实现cpu并行 + * @unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") +* 不添加标签,单元测试脚本默认使用cpu串行 +* 添加下列标签,暂时跳过当前单元测试脚本 + * @unittest.skip(reason="单测暂时跳过") + +##### SKIP标签代码示例 +```python +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") +class TestCreateChatPrompt(unittest.TestCase): + pass + + +class TestCreateChatPrompt(unittest.TestCase): + pass + + +@unittest.skip(reason="单测暂时跳过") +class TestCreateChatPrompt(unittest.TestCase): + pass +``` + +#### SDK测试代码示例 + +下面给出一个通过parameterized库进行多种参数组合的示例,可以大幅简化单测代码,但需注意,多种参数的测试case会并行运行,对服务的QPS有要求 + +```python +import unittest +from parameterized import parameterized, param +import appbuilder + +class TestHandwritingOcr(unittest.TestCase): + @parameterized.expand([ + param(image_url, None, None), + param(image_url, None, 0), + param(image_url, float(120), None), + param(image_url, None, 1), + param(image_url, 120.5, 1), + param(image_url, float(12000), None), + ]) + def test_normal_case(self, image, timeout, retry): + """ + 正常用例 + """ + # 创建表格识别组件实例 + handwrite_ocr = appbuilder.HandwriteOCR() + # 执行识别操作并获取结果 + if timeout is None and retry is None: + out = handwrite_ocr.run(appbuilder.Message(content={"url": image})) + elif timeout is None: + out = handwrite_ocr.run(appbuilder.Message(content={"url": image}), retry=retry) + elif retry is None: + out = handwrite_ocr.run(appbuilder.Message(content={"url": image}), timeout=timeout) + else: + out = handwrite_ocr.run(appbuilder.Message(content={"url": image}), timeout=timeout, retry=retry) + res = out.content + self.assertIsNotNone(res["contents"], "识别结果为空") + self.assertEqual(len(res["contents"]), 6) + + @parameterized.expand([ + # timeout为0 + param(image_url, 0, 0, "ValueError", "timeout", 'but the timeout cannot be set to a value ' + 'less than or equal to 0.'), + # timeout为字符串 + param(image_url, "a", 0, "appbuilder.core._exception.InvalidRequestArgumentError", "timeout", + 'timeout must be float or tuple of float'), + # timeout为0.1,太短了 + param(image_url, float(0.1), 0, "requests.exceptions.ReadTimeout", "timeout", + "Read timed out. (read timeout=0.1)"), + # retry为字符串 + param(image_url, float(10), "a", "TypeError", "str", "'<' not supported between instances of" + " 'str' and 'int'"), + # image_url错误 + param("https://bj.bcebos.com/v1/appbuilder/xxx", 12.5, 1, + "appbuilder.core._exception.AppBuilderServerException", "url", + "service_err_message=url format illegal"), + ]) + def test_abnormal_case(self, image, timeout, retry, err_type, err_param, err_msg): + """ + 异常用例 + """ + try: + # 创建表格识别组件实例 + handwrite_ocr = appbuilder.HandwriteOCR() + # 执行识别操作并获取结果 + out = handwrite_ocr.run(appbuilder.Message(content={"url": image}), timeout=timeout, retry=retry) + res = out.content + log.info(res) + assert False, "未捕获到错误信息" + except Exception as e: + self.assertIsInstance(e, eval(err_type), "捕获的异常不是预期的类型 实际:{}, 预期:{}".format(e, err_type)) + self.assertIn(err_param, str(e), "捕获的异常参数类型不正确, 预期 参数:{}, 实际:{}".format(err_param, str(e))) + self.assertIn(err_msg, str(e), "捕获的异常消息不正确, 预期:{}, 实际:{}".format(err_msg, str(e))) +``` + +## 注释规范 + +- SDK使用注释自动生成API文档,因此非私有函数的注释需要严格按照Google代码注释规范编写 + +### object类注释 + +```python +class AppBuilderClient(Component): + r""" + AppBuilderClient 组件支持调用在[百度智能云千帆AppBuilder](https://cloud.baidu.com/product/AppBuilder)平台上 + 构建并发布的智能体应用,具体包括创建会话、上传文档、运行对话等。 + + Examples: + + .. code-block:: python + + import appbuilder + # 请前往千帆AppBuilder官网创建密钥,流程详见:https://cloud.baidu.com/doc/AppBuilder/s/Olq6grrt6#1%E3%80%81%E5%88%9B%E5%BB%BA%E5%AF%86%E9%92%A5 + os.environ["APPBUILDER_TOKEN"] = '...' + # 可在Console 应用页面获取 + app_id = "app_id" + client = appbuilder.AppBuilderClient("app_id") + conversation_id = client.create_conversation() + file_id = client.upload_local_file(conversation_id, "/path/to/file") + message = client.run(conversation_id, "今天你好吗?") + # 打印对话结果 + print(message.content) + + """ +``` +- 注意 + - 注释必须使用 Examples:之后必须存在一行空行,.. code-block:: python之后也必须要有一行空行 + - code-block前为两点(..)之后为两个冒号(::) + - 方法的示例注释与此规范相同 + +### 函数注释 + +- 私有函数如_recognize_w_post_process等无需按照规范注释函数 + +```python +@components_run_stream_trace +def tool_eval( + self, + name: str, + streaming: bool, + origin_query: str, + **kwargs, +) -> Union[Generator[str, None, None], str]: + """ + 执行工具函数,通过调用底层接口进行动物识别。 + + Args: + name (str): 工具名 + streaming (bool): 是否流式返回结果,True 表示流式返回,False 表示一次性返回 + origin_query (str): 用户原始查询字符串 + **kwargs: 工具调用的额外关键字参数 + + Returns: + Union[Generator[str, None, None], str]: 动物识别结果。如果 streaming 为 True,则返回一个生成器,可以逐个返回识别结果; + 如果 streaming 为 False,则返回一个字符串,包含识别出的动物类别和相应的置信度信息。 + + """ +``` + +### google风格指南与规范示例 + +- Google详情见 [Google注释风格指南](https://google.github.io/styleguide/pyguide.html) + + +```python +class GoogleStyle: + '''Google注释风格 + 用 ``缩进`` 分隔, + 适用于倾向水平,短而简单的文档 + Attributes: + dividend (int or float): 被除数 + name (:obj:`str`, optional): 该类的命名 + ''' + + def __init__(self, dividend, name='GoogleStyle'): + '''初始化''' + self.dividend = dividend + self.name = name + + def divide(self, divisor): + '''除法 + Google注释风格的函数, + 类型主要有Args、Returns、Raises、Examples + Args: + divisor (int):除数 + Returns: + 除法结果 + Raises: + ZeroDivisionError: division by zero + Examples: + + .. code-block:: python + + # 实例代码 + + References: + 除法_百度百科 https://baike.baidu.com/item/%E9%99%A4%E6%B3%95/6280598 + ''' + try: + return self.dividend / divisor + except ZeroDivisionError as e: + return e +``` diff --git a/docs/README.md b/docs/README.md index 8bb36d53f..5bf0a4f9e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -9,53 +9,48 @@ - 产业实践应用范例: - [SDK使用示例](https://github.com/baidubce/app-builder/blob/master/docs/QuickStart/ExamplesOfIndustrialPracticeApplications/README.md) - [SDK当前支持的编程语言](https://github.com/baidubce/app-builder/blob/master/docs/QuickStart/CurrentlySupportedProgrammingLanguages/README.md) - - 基础: - - 模型: - - [获取模型列表](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Model/get_model_list.md) - - [组件](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Components/Components.md) - - 监控: - - [TRACE基础功能](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/basic.md) - - [TRACE拓展功能](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/phoenix_method.md) - - 部署: - - [交互式前端部署](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/AgentChainlit.md) - - [公有云部署](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/cloud.md) - - [API 访问](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/flask.md) - - [AgentRuntime](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/agentruntime.md) - - [UserSession](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/usersession.md) - - 平台: - - 应用: - - [AppBuilderClient组件](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/Application/appbuilder_client.md) - - [获取AppBuilder已发布的应用列表](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/Application/get_app_list.md) - - 知识库: - - [知识库组件](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md) - - 自定义组件: - - [基础能力组件](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/CustomComponents/components.md) - - 应用: - - Agent: - - [基础知识](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [使用官方组件](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [ToolCall](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [ToolChoice](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [使用异步和流式加速客户端调用](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - RAG: - - [基础知识](https://github.com/baidubce/app-builder/blob/master/docs/Application/RAG/BasicKnowledge/rag.md) - - [知识库管理](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [Reference信息处理](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - Workflow: - - [基础知识](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [从零使用Workflow组装一个RAG应用](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [从零使用Workflow组装一个Agent应用](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - 开发者指南: - - [如何贡献代码](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/HowToContributeCode/README.md) - - [版本升级日志](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/ChangeLog/changelog.md) - - [常见问题FAQ](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [日志管理](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [错误信息](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [环境参数](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/EnvironmentalParameters/env.md) - - API Reference: - - [Python API Reference](https://github.com/baidubce/app-builder/blob/master/docs/API-Reference/Python/PythonAPI.md) - - [Java API Reference](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [Go API Reference](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - 基础: + - 模型: + - [获取模型列表](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Model/get_model_list.md) + - [组件](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Components/Components.md) + - 监控: + - [TRACE基础功能](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/basic.md) + - [TRACE拓展功能](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/phoenix_method.md) + - 部署: + - [交互式前端部署](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/AgentChainlit.md) + - [公有云部署](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/cloud.md) + - [API 访问](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/flask.md) + - [AgentRuntime](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/agentruntime.md) + - [UserSession](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/usersession.md) + - 平台: + - 应用: + - [AppBuilderClient组件](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/Application/appbuilder_client.md) + - [获取AppBuilder已发布的应用列表](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/Application/get_app_list.md) + - 知识库: + - [知识库组件](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md) + - 自定义组件: + - [基础能力组件](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/CustomComponents/components.md) + - 应用: + - Agent: + - [基础知识](https://github.com/baidubce/app-builder/blob/master/docs/Application/Agent/BasicKnowledge/agent.md) + - [使用官方组件](https://github.com/baidubce/app-builder/blob/master/docs/Application/Agent/UseOfficialComponents/use_official_components.md) + - [ToolCall](https://github.com/baidubce/app-builder/blob/master/docs/Application/Agent/ToolCall/tool_call.md) + - [ToolChoice](https://github.com/baidubce/app-builder/blob/master/docs/Application/Agent/ToolChoice/tool_choice.md) + - [使用异步和流式加速客户端调用](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - RAG: + - [基础知识](https://github.com/baidubce/app-builder/blob/master/docs/Application/RAG/BasicKnowledge/rag.md) + - [知识库管理](https://github.com/baidubce/app-builder/blob/master/docs/Application/RAG/DatasetManage/dataset_manage.md) + - [Reference信息处理](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - Workflow: + - [基础知识](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [从零使用Workflow组装一个RAG应用](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [从零使用Workflow组装一个Agent应用](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - 开发者指南: + - [如何贡献代码](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/HowToContributeCode/README.md) + - [二次开发](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/AdvancedDevelopment/README.md) + - [版本升级日志](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/ChangeLog/changelog.md) + - [错误信息](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/ErrorMessage/error_message.md) + - [环境参数](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/EnvironmentalParameters/env.md) ## 平台文档 diff --git a/docs/README_en.md b/docs/README_en.md index 99b24c4d6..88a1a5594 100644 --- a/docs/README_en.md +++ b/docs/README_en.md @@ -254,54 +254,49 @@ Hook: - Industrial practice application examples: - [SDK usage examples](https://github.com/baidubce/app-builder/blob/master/docs/QuickStart/ExamplesOfIndustrialPracticeApplications/README.md) - [Currently supported programming languages by SDK](https://github.com/baidubce/app-builder/blob/master/docs/QuickStart/CurrentlySupportedProgrammingLanguages/README.md) - - Basics: - - Models: - - [Get model list](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Model/get_model_list.md) - - [Components](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Components/Components.md) - - Monitoring: - - [TRACE basic functions](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/basic.md) - - [TRACE extended functions](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/phoenix_method.md) - - [Debug functionality](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/Debug.md) - - Deployment: - - [Interactive front-end deployment](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/AgentChainlit.md) - - [Public cloud deployment](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/cloud.md) - - [API access](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/flask.md) - - [AgentRuntime](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/agentruntime.md) - - [UserSession](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/usersession.md) - - Platform: - - Applications: - - [AppBuilderClient component](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/Application/appbuilder_client.md) - - [Get the list of applications published by AppBuilder](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/Application/get_app_list.md) - - Knowledge Base: - - [Knowledge Base component](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md) - - Custom Components: - - [Basic capabilities component](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/CustomComponents/components.md) - - Applications: - - Agent: - - [Basic knowledge](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [Using official components](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [ToolCall](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [ToolChoice](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [Using asynchronous and streaming accelerated client calls](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - RAG: - - [Basic knowledge](https://github.com/baidubce/app-builder/blob/master/docs/Application/RAG/BasicKnowledge/rag.md) - - [Knowledge Base Management](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [Reference Information Processing](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - Workflow: - - [Basic knowledge](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [Assembling a RAG application from scratch using Workflow](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [Assembling an Agent application from scratch using Workflow](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - Developer Guide: - - [How to contribute code](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/HowToContributeCode/README.md) - - [Version upgrade log](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/ChangeLog/changelog.md) - - [FAQ](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [Log Management](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [Error messages](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [Environmental parameters](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/EnvironmentalParameters/env.md) - - API Reference: - - [Python API Reference](https://github.com/baidubce/app-builder/blob/master/docs/API-Reference/Python/PythonAPI.md) - - [Java API Reference](https://github.com/baidubce/app-builder/blob/master/docs/API-Reference/Java/JavaAPI.md) - - [Go API Reference](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - Basics: + - Models: + - [Get model list](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Model/get_model_list.md) + - [Components](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Components/Components.md) + - Monitoring: + - [TRACE basic functions](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/basic.md) + - [TRACE extended functions](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/phoenix_method.md) + - [Debug functionality](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/Debug.md) + - Deployment: + - [Interactive front-end deployment](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/AgentChainlit.md) + - [Public cloud deployment](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/cloud.md) + - [API access](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/flask.md) + - [AgentRuntime](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/agentruntime.md) + - [UserSession](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/usersession.md) + - Platform: + - Applications: + - [AppBuilderClient component](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/Application/appbuilder_client.md) + - [Get the list of applications published by AppBuilder](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/Application/get_app_list.md) + - Knowledge Base: + - [Knowledge Base component](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md) + - Custom Components: + - [Basic capabilities component](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/CustomComponents/components.md) + - Applications: + - Agent: + - [Basic knowledge](https://github.com/baidubce/app-builder/blob/master/docs/Application/Agent/BasicKnowledge/agent.md) + - [Using official components](https://github.com/baidubce/app-builder/blob/master/docs/Application/Agent/UseOfficialComponents/use_official_components.md) + - [ToolCall](https://github.com/baidubce/app-builder/blob/master/docs/Application/Agent/ToolCall/tool_call.md) + - [ToolChoice](https://github.com/baidubce/app-builder/blob/master/docs/Application/Agent/ToolChoice/tool_choice.md) + - [Using asynchronous and streaming accelerated client calls](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - RAG: + - [Basic knowledge](https://github.com/baidubce/app-builder/blob/master/docs/Application/RAG/BasicKnowledge/rag.md) + - [Knowledge Base Management](https://github.com/baidubce/app-builder/blob/master/docs/Application/RAG/DatasetManage/dataset_manage.md) + - [Reference Information Processing](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - Workflow: + - [Basic knowledge](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [Assembling a RAG application from scratch using Workflow](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [Assembling an Agent application from scratch using Workflow](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - Developer Guide: + - [How to contribute code](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/HowToContributeCode/README.md) + - [Secondary Development](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/AdvancedDevelopment/README.md) + - [Version upgrade log](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/ChangeLog/changelog.md) + - [Error messages](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/ErrorMessage/error_message.md) + - [Environmental parameters](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/EnvironmentalParameters/env.md) ## Open source community and activities
diff --git a/docs/README_ja.md b/docs/README_ja.md index bfac2b37d..54c2eca31 100644 --- a/docs/README_ja.md +++ b/docs/README_ja.md @@ -250,54 +250,49 @@ print(answer.content.answer) - 産業実践アプリケーション例: - [SDK使用例](https://github.com/baidubce/app-builder/blob/master/docs/QuickStart/ExamplesOfIndustrialPracticeApplications/README.md) - [現在SDKがサポートしているプログラミング言語](https://github.com/baidubce/app-builder/blob/master/docs/QuickStart/CurrentlySupportedProgrammingLanguages/README.md) - - 基本: - - モデル: - - [モデルリストの取得](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Model/get_model_list.md) - - [コンポーネント](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Components/Components.md) - - 監視: - - [TRACE基本機能](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/basic.md) - - [TRACE拡張機能](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/phoenix_method.md) - - [Debug機能](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/Debug.md) - - デプロイ: - - [インタラクティブなフロントエンドデプロイ](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/AgentChainlit.md) - - [パブリッククラウドデプロイ](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/cloud.md) - - [API アクセス](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/flask.md) - - [AgentRuntime](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/agentruntime.md) - - [UserSession](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/usersession.md) - - プラットフォーム: - - アプリケーション: - - [AppBuilderClientコンポーネント](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/Application/appbuilder_client.md) - - [AppBuilderで公開されたアプリケーションリストの取得](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/Application/get_app_list.md) - - ナレッジベース: - - [ナレッジベースコンポーネント](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md) - - カスタムコンポーネント: - - [基本機能コンポーネント](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/CustomComponents/components.md) - - アプリケーション: - - エージェント: - - [基本知識](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [公式コンポーネントの使用](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [ToolCall](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [ToolChoice](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [非同期およびストリーミングを使用してクライアント呼び出しを加速する](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - RAG: - - [基本知識](https://github.com/baidubce/app-builder/blob/master/docs/Application/RAG/BasicKnowledge/rag.md) - - [ナレッジベース管理](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [Reference情報処理](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - ワークフロー: - - [基本知識](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [Workflowを使ってRAGアプリケーションをゼロから組み立てる](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [Workflowを使ってAgentアプリケーションをゼロから組み立てる](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - 開発者ガイド: - - [コードの貢献方法](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/HowToContributeCode/README.md) - - [バージョンアップログ](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/ChangeLog/changelog.md) - - [よくある質問FAQ](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [ログ管理](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [エラーメッセージ](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) - - [環境パラメータ](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/EnvironmentalParameters/env.md) - - APIリファレンス: - - [Python APIリファレンス](https://github.com/baidubce/app-builder/blob/master/docs/API-Reference/Python/PythonAPI.md) - - [Java APIリファレンス](https://github.com/baidubce/app-builder/blob/master/docs/API-Reference/Java/JavaAPI.md) - - [Go APIリファレンス](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - 基本: + - モデル: + - [モデルリストの取得](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Model/get_model_list.md) + - [コンポーネント](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Components/Components.md) + - 監視: + - [TRACE基本機能](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/basic.md) + - [TRACE拡張機能](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/phoenix_method.md) + - [Debug機能](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Trace/Debug.md) + - デプロイ: + - [インタラクティブなフロントエンドデプロイ](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/AgentChainlit.md) + - [パブリッククラウドデプロイ](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/cloud.md) + - [API アクセス](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/flask.md) + - [AgentRuntime](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/agentruntime.md) + - [UserSession](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Deployment/usersession.md) + - プラットフォーム: + - アプリケーション: + - [AppBuilderClientコンポーネント](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/Application/appbuilder_client.md) + - [AppBuilderで公開されたアプリケーションリストの取得](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/Application/get_app_list.md) + - ナレッジベース: + - [ナレッジベースコンポーネント](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/KnowledgeBase/knowledgebase.md) + - カスタムコンポーネント: + - [基本機能コンポーネント](https://github.com/baidubce/app-builder/blob/master/docs/BasisModule/Platform/CustomComponents/components.md) + - アプリケーション: + - エージェント: + - [基本知識](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [公式コンポーネントの使用](https://github.com/baidubce/app-builder/blob/master/docs/Application/Agent/UseOfficialComponents/use_official_components.md) + - [ToolCall](https://github.com/baidubce/app-builder/blob/master/docs/Application/Agent/ToolCall/tool_call.md) + - [ToolChoice](https://github.com/baidubce/app-builder/blob/master/docs/Application/Agent/ToolChoice/tool_choice.md) + - [非同期およびストリーミングを使用してクライアント呼び出しを加速する](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - RAG: + - [基本知識](https://github.com/baidubce/app-builder/blob/master/docs/Application/RAG/BasicKnowledge/rag.md) + - [ナレッジベース管理](https://github.com/baidubce/app-builder/blob/master/docs/Application/RAG/DatasetManage/dataset_manage.md) + - [Reference情報処理](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - ワークフロー: + - [基本知識](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [Workflowを使ってRAGアプリケーションをゼロから組み立てる](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - [Workflowを使ってAgentアプリケーションをゼロから組み立てる](https://github.com/baidubce/app-builder/blob/master/docs/Tools/DocPass/DocPass.md) + - 開発者ガイド: + - [コードの貢献方法](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/HowToContributeCode/README.md) + - [にじかいせっか](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/AdvancedDevelopment/README.md) + - [バージョンアップログ](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/ChangeLog/changelog.md) + - [エラーメッセージ](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/ErrorMessage/error_message.md) + - [環境パラメータ](https://github.com/baidubce/app-builder/blob/master/docs/DevelopGuide/EnvironmentalParameters/env.md) ## オープンソースコミュニティと活動 diff --git a/mkdocs.yml b/mkdocs.yml index 044ab306a..7311aa4c1 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -82,15 +82,15 @@ nav: - 自定义组件: - 基础能力组件: BasisModule/Platform/CustomComponents/components.md - 应用: - # - Agent: - # - 基础知识: - # - 使用官方组件: - # - ToolCall: - # - ToolChoice: + - Agent: + - 基础知识: Application/Agent/BasicKnowledge/agent.md + - 使用官方组件: Application/Agent/UseOfficialComponents/use_official_components.md + - ToolCall: Application/Agent/ToolCall/tool_call.md + - ToolChoice: Application/Agent/ToolChoice/tool_choice.md # - 使用异步和流式加速客户端调用: - RAG: - 基础知识: Application/RAG/BasicKnowledge/rag.md - # - 知识库管理: + - 知识库管理: Application/RAG/DatasetManage/dataset_manage.md # - Reference信息处理: # - Workflow: # - 基础知识: @@ -98,10 +98,9 @@ nav: # - 从零使用Workflow组装一个Agent应用: - 开发者指南: - 如何贡献代码: DevelopGuide/HowToContributeCode/README.md + - 二次开发: DevelopGuide/AdvancedDevelopment/README.md - 版本升级日志: DevelopGuide/ChangeLog/changelog.md - # - 常见问题FAQ: - # - 日志管理: - # - 错误信息: + - 错误信息: DevelopGuide/ErrorMessage/error_message.md - 环境参数: DevelopGuide/EnvironmentalParameters/env.md - API Reference: - Python API Reference: API-Reference/Python/PythonAPI.md From 9a95c5e47420dfa386921163bd032b3ba5d34e4a Mon Sep 17 00:00:00 2001 From: userpj Date: Thu, 19 Dec 2024 10:25:17 +0800 Subject: [PATCH 79/85] =?UTF-8?q?AppBuilderClient=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=BC=82=E6=AD=A5=E8=B0=83=E7=94=A8=20(#666)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chainlit chainlit_agent支持工作流Agent应用 * update * AppBuilderClient增加异步调用 * 完善debug模式curl命令打印 * 增加异步上传文档方法 * 增加async client单测 * 增加异步event_handler * 异步event_handler增加追问、toolcall单测 * update * add unittest * add unittest * update unittest * update unittest --- python/__init__.py | 26 +- python/core/_client.py | 45 +- python/core/_session.py | 56 +++ python/core/component.py | 40 +- .../appbuilder_client/appbuilder_client.py | 123 ++--- .../async_appbuilder_client.py | 291 +++++++++++ .../appbuilder_client/async_event_handler.py | 459 ++++++++++++++++++ python/tests/test_async_appbuilder_client.py | 79 +++ .../test_async_appbuilder_client_chatflow.py | 204 ++++++++ ...async_appbuilder_client_follow_up_query.py | 72 +++ .../test_async_appbuilder_client_toolcall.py | 122 +++++ python/tests/test_core_client.py | 100 ++-- python/tests/test_core_session.py | 41 ++ python/tests/test_utils.py | 30 +- python/utils/sse_util.py | 153 ++++-- 15 files changed, 1694 insertions(+), 147 deletions(-) create mode 100644 python/core/console/appbuilder_client/async_appbuilder_client.py create mode 100644 python/core/console/appbuilder_client/async_event_handler.py create mode 100644 python/tests/test_async_appbuilder_client.py create mode 100644 python/tests/test_async_appbuilder_client_chatflow.py create mode 100644 python/tests/test_async_appbuilder_client_follow_up_query.py create mode 100644 python/tests/test_async_appbuilder_client_toolcall.py create mode 100644 python/tests/test_core_session.py diff --git a/python/__init__.py b/python/__init__.py index 04de5e9f8..432ae0359 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -178,6 +178,7 @@ def get_default_header(): from appbuilder.core.utils import get_model_list from appbuilder.core.console.appbuilder_client.appbuilder_client import AppBuilderClient +from appbuilder.core.console.appbuilder_client.async_appbuilder_client import AsyncAppBuilderClient from appbuilder.core.console.appbuilder_client.appbuilder_client import AgentBuilder from appbuilder.core.console.appbuilder_client.appbuilder_client import get_app_list, get_all_apps, describe_apps from appbuilder.core.console.knowledge_base.knowledge_base import KnowledgeBase @@ -202,19 +203,20 @@ def get_default_header(): from appbuilder.utils.trace.tracer import AppBuilderTracer, AppbuilderInstrumentor __all__ = [ - 'logger', - 'BadRequestException', - 'ForbiddenException', - 'NotFoundException', - 'PreconditionFailedException', - 'InternalServerErrorException', - 'HTTPConnectionException', - 'AppBuilderServerException', - 'AppbuilderTraceException', - 'AppbuilderTestToolEval', - 'AutomaticTestToolEval', + "logger", + "BadRequestException", + "ForbiddenException", + "NotFoundException", + "PreconditionFailedException", + "InternalServerErrorException", + "HTTPConnectionException", + "AppBuilderServerException", + "AppbuilderTraceException", + "AppbuilderTestToolEval", + "AutomaticTestToolEval", "get_model_list", "AppBuilderClient", + "AsyncAppBuilderClient", "AgentBuilder", "get_app_list", "get_all_apps", @@ -232,5 +234,5 @@ def get_default_header(): "AssistantEventHandler", "AssistantStreamManager", "AppBuilderTracer", - "AppbuilderInstrumentor" + "AppbuilderInstrumentor", ] + __COMPONENTS__ diff --git a/python/core/_client.py b/python/core/_client.py index 9c226dced..83a2c524d 100644 --- a/python/core/_client.py +++ b/python/core/_client.py @@ -21,11 +21,12 @@ import requests from requests.adapters import HTTPAdapter, Retry +from aiohttp import ClientResponse from appbuilder import get_default_header from appbuilder.core._exception import * -from appbuilder.core._session import InnerSession +from appbuilder.core._session import InnerSession, AsyncInnerSession from appbuilder.core.constants import ( GATEWAY_URL, GATEWAY_URL_V2, @@ -100,7 +101,8 @@ def _init_secret_key(self, secret_key: str): secret_key_prefix = os.getenv("SECRET_KEY_PREFIX", SECRET_KEY_PREFIX) if not self.secret_key.startswith(secret_key_prefix): - self.secret_key = "{} {}".format(secret_key_prefix, self.secret_key) + self.secret_key = "{} {}".format( + secret_key_prefix, self.secret_key) logger.debug("AppBuilder Secret key: {}\n".format(self.secret_key)) @@ -181,7 +183,8 @@ def check_console_response(response: requests.Response): data = response.json() if "code" in data and data.get("code") != 0: requestId = __class__.response_request_id(response) - raise AppBuilderServerException(requestId, data["code"], data["message"]) + raise AppBuilderServerException( + requestId, data["code"], data["message"]) def auth_header(self, request_id: Optional[str] = None): r"""auth_header is a helper method return auth info""" @@ -234,6 +237,42 @@ def inner(*args, **kwargs): return inner +class AsyncHTTPClient(HTTPClient): + def __init__(self, secret_key=None, gateway="", gateway_v2=""): + super().__init__(secret_key, gateway, gateway_v2) + self.session = AsyncInnerSession() + + @staticmethod + def check_response_header(response: ClientResponse): + r"""check_response_header is a helper method for check head status . + :param response: requests.Response. + :rtype: + """ + status_code = response.status + if status_code == requests.codes.ok: + return + message = "request_id={} , http status code is {}, body is {}".format( + __class__.response_request_id(response), status_code, response.text + ) + if status_code == requests.codes.bad_request: + raise BadRequestException(message) + elif status_code == requests.codes.forbidden: + raise ForbiddenException(message) + elif status_code == requests.codes.not_found: + raise NotFoundException(message) + elif status_code == requests.codes.precondition_required: + raise PreconditionFailedException(message) + elif status_code == requests.codes.internal_server_error: + raise InternalServerErrorException(message) + else: + raise BaseRPCException(message) + + @staticmethod + def response_request_id(response: ClientResponse): + r"""response_request_id is a helper method to get the unique request id""" + return response.headers.get("X-Appbuilder-Request-Id", "") + + class AssistantHTTPClient(HTTPClient): def service_url(self, sub_path: str, prefix: str = None): """ diff --git a/python/core/_session.py b/python/core/_session.py index be3877e0d..31e398228 100644 --- a/python/core/_session.py +++ b/python/core/_session.py @@ -14,6 +14,8 @@ import requests import json +import aiohttp +from aiohttp import ClientSession, hdrs from appbuilder.utils.logger_util import logger from appbuilder.utils.trace.tracer_wrapper import session_post @@ -72,3 +74,57 @@ def get(self, url, **kwargs): @session_post def put(self, url, data=None, **kwargs): return super().put(url=url, data=data, **kwargs) + + +class AsyncInnerSession(ClientSession): + + def __init__(self, *args, **kwargs): + """ + Initialize inner session. + """ + super(AsyncInnerSession, self).__init__(*args, **kwargs) + + async def build_curl(self, method, url, data=None, json_data=None, **kwargs) -> str: + """ + Generate cURL command from prepared request object. + """ + curl = "curl -X {0} -L '{1}' \\\n".format(method, url) + + headers = kwargs.get("headers", {}) + headers_strs = [ + "-H '{0}: {1}' \\".format(k, v) for k, v in headers.items()] + if headers_strs: + headers_strs[-1] = headers_strs[-1].rstrip(" \\") + curl += "\n".join(headers_strs) + + if data: + try: + body = "'{0}'".format(json.dumps(data, ensure_ascii=False)) + curl += " \\\n-d {0}".format(body) + except: + pass + elif json_data: + body = "'{0}'".format(json.dumps(json_data, ensure_ascii=False)) + curl += " \\\n-d {0}".format(body) + + return curl + + @session_post + async def post(self, url, data=None, json=None, **kwargs): + logger.debug("Curl Command:\n" + await self.build_curl(hdrs.METH_POST, url, data=data, json_data=json, **kwargs) + "\n") + return await super().post(url=url, data=data, json=json, **kwargs) + + @session_post + async def delete(self, url, **kwargs): + logger.debug("Curl Command:\n" + await self.build_curl(hdrs.METH_DELETE, url, **kwargs) + "\n") + return await super().delete(url=url, **kwargs) + + @session_post + async def get(self, url, **kwargs): + logger.debug("Curl Command:\n" + await self.build_curl(hdrs.METH_GET, url, **kwargs) + "\n") + return await super().get(url=url, **kwargs) + + @session_post + async def put(self, url, data=None, **kwargs): + logger.debug("Curl Command:\n" + await self.build_curl(hdrs.METH_PUT, url, data=data, **kwargs) + "\n") + return await super().put(url=url, data=data, **kwargs) diff --git a/python/core/component.py b/python/core/component.py index b2c005b47..23bcbac77 100644 --- a/python/core/component.py +++ b/python/core/component.py @@ -22,7 +22,7 @@ from typing import ( Dict, List, Optional, Any, Generator, Union, AsyncGenerator) from appbuilder.core.utils import ttl_lru_cache -from appbuilder.core._client import HTTPClient +from appbuilder.core._client import HTTPClient, AsyncHTTPClient from appbuilder.core.message import Message @@ -118,18 +118,22 @@ class PlanStep(BaseModel, extra='allow'): arguments: dict = Field(default={}, description="step参数") thought: str = Field(default="", description="step思考结果") + class Plan(BaseModel, extra='allow'): detail: str = Field(default="", description="计划详情") steps: list[PlanStep] = Field(default=[], description="步骤列表") + class FunctionCall(BaseModel, extra='allow'): thought: str = Field(default="", description="思考结果") name: str = Field(default="", description="工具名") arguments: dict = Field(default={}, description="参数列表") - + + class Json(BaseModel, extra='allow'): data: str = Field(default="", description="json数据") + class Content(BaseModel): name: str = Field(default="", description="介绍当前yield内容的阶段名, 使用name的必要条件,是同一组件会输出不同type的content,并且需要加以区分,方便前端渲染与用户展示") @@ -141,10 +145,10 @@ class Content(BaseModel): description="大模型的token用量, ") metrics: dict = Field(default={}, description="耗时、性能、内存等trace及debug所需信息") - type: str = Field(default="text", + type: str = Field(default="text", description="代表event 类型,包括 text、code、files、urls、oral_text、references、image、chart、audio该字段的取值决定了下面text字段的内容结构") - text: Union[Text, Code, Files, Urls, OralText, References, Image, Chart, Audio, Plan, Json, FunctionCall] = Field(default=Text, - description="代表当前 event 元素的内容,每一种 event 对应的 text 结构固定") + text: Union[Text, Code, Files, Urls, OralText, References, Image, Chart, Audio, Plan, Json, FunctionCall] = Field(default=Text, + description="代表当前 event 元素的内容,每一种 event 对应的 text 结构固定") @field_validator('text', mode='before') def set_text(cls, v, values, **kwargs): @@ -180,7 +184,7 @@ class ComponentOutput(BaseModel): role: str = Field(default="tool", description="role是区分当前消息来源的重要字段,对于绝大多数组件而言,都是填写tool,标明role所在的消息来源为组件。部分思考及问答组件,role需要填写为assistant") content: list[Content] = Field(default=[], - description="content是当前组件返回内容的主要payload,List[Content],每个Content Dict 包括了当前输出的一个元素") + description="content是当前组件返回内容的主要payload,List[Content],每个Content Dict 包括了当前输出的一个元素") class Component: @@ -202,6 +206,7 @@ def __init__( secret_key: Optional[str] = None, gateway: str = "", lazy_certification: bool = False, + is_aysnc: bool = False, **kwargs ): r"""Component初始化方法. @@ -219,6 +224,7 @@ def __init__( self.gateway = gateway self._http_client = None self.lazy_certification = lazy_certification + self.is_async = is_aysnc if not self.lazy_certification: self.set_secret_key_and_gateway(self.secret_key, self.gateway) @@ -236,7 +242,10 @@ def set_secret_key_and_gateway(self, secret_key: Optional[str] = None, gateway: """ self.secret_key = secret_key self.gateway = gateway - self._http_client = HTTPClient(self.secret_key, self.gateway) + if self.is_async: + self._http_client = AsyncHTTPClient(self.secret_key, self.gateway) + else: + self._http_client = HTTPClient(self.secret_key, self.gateway) @property def http_client(self): @@ -251,7 +260,11 @@ def http_client(self): """ if self._http_client is None: - self._http_client = HTTPClient(self.secret_key, self.gateway) + if self.is_async: + self._http_client = AsyncHTTPClient( + self.secret_key, self.gateway) + else: + self._http_client = HTTPClient(self.secret_key, self.gateway) return self._http_client def __call__(self, *inputs, **kwargs): @@ -521,7 +534,8 @@ def create_output(cls, type, text, role="tool", name="", visible_scope="all", ra elif type == "json": text = {"data": text} else: - raise ValueError("Only when type=text/code/urls/oral_text, string text is allowed! Please give dict text") + raise ValueError( + "Only when type=text/code/urls/oral_text, string text is allowed! Please give dict text") elif isinstance(text, dict): if type == "text": key_list = ["info"] @@ -534,7 +548,8 @@ def create_output(cls, type, text, role="tool", name="", visible_scope="all", ra elif type == "files": key_list = ["filename", "url"] elif type == "references": - key_list = ["type", "resource_type", "icon", "site_name", "source", "doc_id", "title", "content", "image_content", "image_url", "video_url"] + key_list = ["type", "resource_type", "icon", "site_name", "source", + "doc_id", "title", "content", "image_content", "image_url", "video_url"] elif type == "image": key_list = ["filename", "url"] elif type == "chart": @@ -551,7 +566,8 @@ def create_output(cls, type, text, role="tool", name="", visible_scope="all", ra else: raise ValueError("text must be str or dict") - assert role in ["tool", "assistant"], "role must be 'tool' or 'assistant'" + assert role in [ + "tool", "assistant"], "role must be 'tool' or 'assistant'" result = { "role": role, "content": [{ @@ -564,4 +580,4 @@ def create_output(cls, type, text, role="tool", name="", visible_scope="all", ra "metrics": metrics }] } - return ComponentOutput(**result) \ No newline at end of file + return ComponentOutput(**result) diff --git a/python/core/console/appbuilder_client/appbuilder_client.py b/python/core/console/appbuilder_client/appbuilder_client.py index 4814f41d4..5d8779a4a 100644 --- a/python/core/console/appbuilder_client/appbuilder_client.py +++ b/python/core/console/appbuilder_client/appbuilder_client.py @@ -17,7 +17,7 @@ import json import uuid import queue -from typing import Optional,Union +from typing import Optional, Union from appbuilder.core.component import Message, Component from appbuilder.core.manifest.models import Manifest from appbuilder.core.console.appbuilder_client import data_class @@ -81,7 +81,7 @@ def describe_apps( marker: Optional[str] = None, maxKeys: int = 10, secret_key: Optional[str] = None, - gateway: Optional[str] = None + gateway: Optional[str] = None, ) -> list[data_class.AppOverview]: """ 该接口查询用户下状态为已发布的应用列表 @@ -100,9 +100,7 @@ def describe_apps( headers = client.auth_header_v2() headers["Content-Type"] = "application/json" url = client.service_url_v2("/app?Action=DescribeApps") - request = data_class.DescribeAppsRequest( - MaxKeys=maxKeys, Marker=marker - ) + request = data_class.DescribeAppsRequest(MaxKeys=maxKeys, Marker=marker) response = client.session.post( url=url, json=request.model_dump(), @@ -225,7 +223,8 @@ def upload_local_file(self, conversation_id, local_file_path: str) -> str: """ if len(conversation_id) == 0: raise ValueError( - "conversation_id is empty, you can run self.create_conversation to get a conversation_id") + "conversation_id is empty, you can run self.create_conversation to get a conversation_id" + ) filepath = os.path.abspath(local_file_path) if not os.path.exists(filepath): @@ -247,17 +246,19 @@ def upload_local_file(self, conversation_id, local_file_path: str) -> str: return resp.id @client_run_trace - def run(self, conversation_id: str, - query: str = "", - file_ids: list = [], - stream: bool = False, - tools: list[Union[data_class.Tool,Manifest]]= None, - tool_outputs: list[data_class.ToolOutput] = None, - tool_choice: data_class.ToolChoice = None, - end_user_id: str = None, - action: data_class.Action = None, - **kwargs - ) -> Message: + def run( + self, + conversation_id: str, + query: str = "", + file_ids: list = [], + stream: bool = False, + tools: list[Union[data_class.Tool, Manifest]] = None, + tool_outputs: list[data_class.ToolOutput] = None, + tool_choice: data_class.ToolChoice = None, + end_user_id: str = None, + action: data_class.Action = None, + **kwargs, + ) -> Message: r"""运行智能体应用 Args: @@ -283,7 +284,8 @@ def run(self, conversation_id: str, if query == "" and (tool_outputs is None or len(tool_outputs) == 0): raise ValueError( - "AppBuilderClient Run API: query and tool_outputs cannot both be empty") + "AppBuilderClient Run API: query and tool_outputs cannot both be empty" + ) req = data_class.AppBuilderClientRequest( app_id=self.app_id, @@ -313,18 +315,20 @@ def run(self, conversation_id: str, data = response.json() resp = data_class.AppBuilderClientResponse(**data) out = data_class.AppBuilderClientAnswer() - _transform(resp, out) + AppBuilderClient._transform(resp, out) return Message(content=out) - def run_with_handler(self, - conversation_id: str, - query: str = "", - file_ids: list = [], - tools: list[Union[data_class.Tool,Manifest]] = None, - stream: bool = False, - event_handler=None, - action=None, - **kwargs): + def run_with_handler( + self, + conversation_id: str, + query: str = "", + file_ids: list = [], + tools: list[Union[data_class.Tool, Manifest]] = None, + stream: bool = False, + event_handler=None, + action=None, + **kwargs, + ): r"""运行智能体应用,并通过事件处理器处理事件 Args: @@ -350,20 +354,22 @@ def run_with_handler(self, tools=tools, stream=stream, action=action, - **kwargs + **kwargs, ) return event_handler - def run_multiple_dialog_with_handler(self, - conversation_id: str, - queries: iter = None, - file_ids: iter = None, - tools: iter = None, - stream: bool = False, - event_handler=None, - actions: iter = None, - **kwargs): + def run_multiple_dialog_with_handler( + self, + conversation_id: str, + queries: iter = None, + file_ids: iter = None, + tools: iter = None, + stream: bool = False, + event_handler=None, + actions: iter = None, + **kwargs, + ): r"""运行智能体应用,并通过事件处理器处理事件 Args: @@ -415,7 +421,7 @@ def run_multiple_dialog_with_handler(self, event_handler.reset_state() @staticmethod - def _iterate_events(request_id, events) -> data_class.AppBuilderClientAnswer: + def _iterate_events(request_id, events): for event in events: try: data = event.data @@ -429,7 +435,7 @@ def _iterate_events(request_id, events) -> data_class.AppBuilderClientAnswer: ) inp = data_class.AppBuilderClientResponse(**data) out = data_class.AppBuilderClientAnswer() - _transform(inp, out) + AppBuilderClient._transform(inp, out) yield out @staticmethod @@ -441,6 +447,24 @@ def _check_console_response(request_id: str, data): service_err_message="message={}".format(data["message"]), ) + @staticmethod + def _transform( + inp: data_class.AppBuilderClientResponse, out: data_class.AppBuilderClientAnswer + ): + out.answer = inp.answer + for ev in inp.content: + event = data_class.Event( + code=ev.event_code, + message=ev.event_message, + status=ev.event_status, + event_type=ev.event_type, + content_type=ev.content_type, + detail=ev.outputs, + usage=ev.usage, + tool_calls=ev.tool_calls, + ) + out.events.append(event) + class AgentBuilder(AppBuilderClient): r"""AgentBuilder是继承自AppBuilderClient的一个子类,用于构建和管理智能体应用。 @@ -464,6 +488,7 @@ class AgentBuilder(AppBuilderClient): print(message.content) """ + @deprecated( reason="AgentBuilder is deprecated, please use AppBuilderClient instead", version="1.0.0", @@ -481,21 +506,3 @@ def __init__(self, app_id: str): """ super().__init__(app_id) - - -def _transform( - inp: data_class.AppBuilderClientResponse, out: data_class.AppBuilderClientAnswer -): - out.answer = inp.answer - for ev in inp.content: - event = data_class.Event( - code=ev.event_code, - message=ev.event_message, - status=ev.event_status, - event_type=ev.event_type, - content_type=ev.content_type, - detail=ev.outputs, - usage=ev.usage, - tool_calls=ev.tool_calls, - ) - out.events.append(event) diff --git a/python/core/console/appbuilder_client/async_appbuilder_client.py b/python/core/console/appbuilder_client/async_appbuilder_client.py new file mode 100644 index 000000000..f6f010f2d --- /dev/null +++ b/python/core/console/appbuilder_client/async_appbuilder_client.py @@ -0,0 +1,291 @@ +# Copyright (c) 2024 Baidu, Inc. 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 os +from typing import Union +from aiohttp import FormData +from appbuilder.core.component import Message, Component +from appbuilder.core.console.appbuilder_client import data_class, AppBuilderClient +from appbuilder.core.manifest.models import Manifest +from appbuilder.core._exception import AppBuilderServerException +from appbuilder.utils.sse_util import AsyncSSEClient + + +class AsyncAppBuilderClient(Component): + def __init__(self, app_id, **kwargs): + super().__init__(is_aysnc=True, **kwargs) + if (not isinstance(app_id, str)) or len(app_id) == 0: + raise ValueError( + "app_id must be a str, and length is bigger then zero," + "please go to official website which is 'https://cloud.baidu.com/product/AppBuilder'" + " to get a valid app_id after your application is published." + ) + self.app_id = app_id + + async def create_conversation(self) -> str: + r"""异步创建会话并返回会话ID + + 会话ID在服务端用于上下文管理、绑定会话文档等,如需开始新的会话,请创建并使用新的会话ID + + Args: + 无 + + Returns: + response (str): 唯一会话ID + + """ + headers = self.http_client.auth_header_v2() + headers["Content-Type"] = "application/json" + url = self.http_client.service_url_v2("/app/conversation") + response = await self.http_client.session.post( + url, headers=headers, json={"app_id": self.app_id}, timeout=None + ) + self.http_client.check_response_header(response) + data = await response.json() + resp = data_class.CreateConversationResponse(**data) + return resp.conversation_id + + async def run( + self, + conversation_id: str, + query: str = "", + file_ids: list = [], + stream: bool = False, + tools: list[Union[data_class.Tool, Manifest]] = None, + tool_outputs: list[data_class.ToolOutput] = None, + tool_choice: data_class.ToolChoice = None, + end_user_id: str = None, + action: data_class.Action = None, + **kwargs, + ) -> Message: + r"""异步运行智能体应用 + + Args: + query (str): query内容 + conversation_id (str): 唯一会话ID,如需开始新的会话,请使用self.create_conversation创建新的会话 + file_ids(list[str]): 文件ID列表 + stream (bool): 为True时,流式返回,需要将message.content.answer拼接起来才是完整的回答;为False时,对应非流式返回 + tools(list[Union[data_class.Tool,Manifest]]): 一个Tool或Manifest组成的列表,其中每个Tool(Manifest)对应一个工具的配置, 默认为None + tool_outputs(list[data_class.ToolOutput]): 工具输出列表,格式为list[ToolOutput], ToolOutputd内容为本地的工具执行结果,以自然语言/json dump str描述,默认为None + tool_choice(data_class.ToolChoice): 控制大模型使用组件的方式,默认为None + end_user_id (str): 用户ID,用于区分不同用户 + action(data_class.Action): 对话时要进行的特殊操作。如回复工作流agent中“信息收集节点“的消息。 + kwargs: 其他参数 + + Returns: + message (Message): 对话结果,一个Message对象,使用message.content获取内容。 + """ + + if len(conversation_id) == 0: + raise ValueError( + "conversation_id is empty, you can run self.create_conversation to get a conversation_id" + ) + + if query == "" and (tool_outputs is None or len(tool_outputs) == 0): + raise ValueError( + "AppBuilderClient Run API: query and tool_outputs cannot both be empty" + ) + + req = data_class.AppBuilderClientRequest( + app_id=self.app_id, + conversation_id=conversation_id, + query=query, + stream=True if stream else False, + file_ids=file_ids, + tools=tools, + tool_outputs=tool_outputs, + tool_choice=tool_choice, + end_user_id=end_user_id, + action=action, + ) + + headers = self.http_client.auth_header_v2() + headers["Content-Type"] = "application/json" + url = self.http_client.service_url_v2("/app/conversation/runs") + response = await self.http_client.session.post( + url, headers=headers, json=req.model_dump(), timeout=None + ) + self.http_client.check_response_header(response) + request_id = self.http_client.response_request_id(response) + if stream: + client = AsyncSSEClient(response) + return Message(content=self._iterate_events(request_id, client.events())) + else: + data = await response.json() + resp = data_class.AppBuilderClientResponse(**data) + out = data_class.AppBuilderClientAnswer() + AppBuilderClient._transform(resp, out) + return Message(content=out) + + async def upload_local_file(self, conversation_id, local_file_path: str) -> str: + r"""异步运行,上传文件并将文件与会话ID进行绑定,后续可使用该文件ID进行对话,目前仅支持上传xlsx、jsonl、pdf、png等文件格式 + + 该接口用于在对话中上传文件供大模型处理,文件的有效期为7天并且不超过对话的有效期。一次只能上传一个文件。 + + Args: + conversation_id (str) : 会话ID + local_file_path (str) : 本地文件路径 + + Returns: + response (str): 唯一文件ID + + """ + if len(conversation_id) == 0: + raise ValueError( + "conversation_id is empty, you can run self.create_conversation to get a conversation_id" + ) + + filepath = os.path.abspath(local_file_path) + if not os.path.exists(filepath): + raise FileNotFoundError(f"{filepath} does not exist") + multipart_form_data = FormData() + multipart_form_data.add_field( + name="file", + value=open(local_file_path, "rb"), + filename=os.path.basename(local_file_path), + ) + multipart_form_data.add_field(name="app_id", value=self.app_id) + multipart_form_data.add_field( + name="conversation_id", value=conversation_id) + + headers = self.http_client.auth_header_v2() + url = self.http_client.service_url_v2("/app/conversation/file/upload") + response = await self.http_client.session.post( + url, data=multipart_form_data, headers=headers + ) + self.http_client.check_response_header(response) + data = await response.json() + resp = data_class.FileUploadResponse(**data) + return resp.id + + async def run_with_handler( + self, + conversation_id: str, + query: str = "", + file_ids: list = [], + tools: list[Union[data_class.Tool, Manifest]] = None, + stream: bool = False, + event_handler=None, + action=None, + **kwargs, + ): + r"""异步运行智能体应用,并通过事件处理器处理事件 + + Args: + conversation_id (str): 唯一会话ID,如需开始新的会话,请使用self.create_conversation创建新的会话 + query (str): 查询字符串 + file_ids (list): 文件ID列表 + tools(list[Union[data_class.Tool,Manifest]], 可选): 一个Tool或Manifest组成的列表,其中每个Tool(Manifest)对应一个工具的配置, 默认为None + stream (bool): 是否流式响应 + event_handler (EventHandler): 事件处理器 + action(data_class.Action) 对话时要进行的特殊操作。如回复工作流agent中“信息收集节点“的消息。 + + kwargs: 其他参数 + + Returns: + EventHandler: 事件处理器 + """ + assert event_handler is not None, "event_handler is None" + await event_handler.init( + appbuilder_client=self, + conversation_id=conversation_id, + query=query, + file_ids=file_ids, + tools=tools, + stream=stream, + action=action, + **kwargs, + ) + + return event_handler + + async def run_multiple_dialog_with_handler( + self, + conversation_id: str, + queries: iter = None, + file_ids: iter = None, + tools: iter = None, + stream: bool = False, + event_handler=None, + actions: iter = None, + **kwargs, + ): + r"""运行智能体应用,并通过事件处理器处理事件 + + Args: + conversation_id (str): 唯一会话ID,如需开始新的会话,请使用self.create_conversation创建新的会话 + queries (iter): 查询字符串可迭代对象 + file_ids (iter): 文件ID列表 + tools(iter, 可选): 一个Tool或Manifest组成的列表,其中每个Tool(Manifest)对应一个工具的配置, 默认为None + stream (bool): 是否流式响应 + event_handler (EventHandler): 事件处理器 + actions(iter) 对话时要进行的特殊操作。如回复工作流agent中“信息收集节点“的消息。 + + kwargs: 其他参数 + Returns: + EventHandler: 事件处理器 + """ + assert event_handler is not None, "event_handler is None" + assert queries is not None, "queries is None" + + iter_queries = iter(queries) + iter_file_ids = iter(file_ids) if file_ids else iter([]) + iter_tools = iter(tools) if tools else iter([]) + iter_actions = iter(actions) if actions else iter([]) + + for index, query in enumerate(iter_queries): + file_id = next(iter_file_ids, None) + tool = next(iter_tools, None) + action = next(iter_actions, None) + + if index == 0: + await event_handler.init( + appbuilder_client=self, + conversation_id=conversation_id, + query=query, + file_ids=file_id, + tools=tool, + stream=stream, + action=action, + **kwargs, + ) + yield event_handler + else: + await event_handler.new_dialog( + query=query, + file_ids=file_id, + tools=tool, + stream=stream, + action=action, + ) + yield event_handler + await event_handler.reset_state() + + @staticmethod + async def _iterate_events(request_id, events): + async for event in events: + try: + data = event.data + if len(data) == 0: + data = event.raw + data = json.loads(data) + except json.JSONDecodeError as e: + raise AppBuilderServerException( + request_id=request_id, + message="json decoder failed {}".format(str(e)), + ) + inp = data_class.AppBuilderClientResponse(**data) + out = data_class.AppBuilderClientAnswer() + AppBuilderClient._transform(inp, out) + yield out diff --git a/python/core/console/appbuilder_client/async_event_handler.py b/python/core/console/appbuilder_client/async_event_handler.py new file mode 100644 index 000000000..afa93eef9 --- /dev/null +++ b/python/core/console/appbuilder_client/async_event_handler.py @@ -0,0 +1,459 @@ +# Copyright (c) 2024 Baidu, Inc. 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 appbuilder.utils.logger_util import logger +from appbuilder.core.console.appbuilder_client import data_class + + +class AppBuilderClientRunContext(object): + def __init__(self) -> None: + """ + 初始化方法。 + + Args: + 无参数。 + + Returns: + None + + """ + self.current_event = None + self.current_tool_calls = None + self.current_status = None + self.need_tool_submit = False + self.is_complete = False + self.current_thought = "" + + +class AsyncAppBuilderEventHandler(object): + def __init__(self): + pass + + async def init( + self, + appbuilder_client, + conversation_id, + query, + file_ids=None, + tools=None, + stream: bool = False, + event_handler=None, + action=None, + **kwargs + ): + """ + 初始化类实例并设置相关参数。 + + Args: + appbuilder_client (object): AppBuilder客户端实例对象。 + conversation_id (str): 对话ID。 + query (str): 用户输入的查询语句。 + file_ids (list, optional): 文件ID列表,默认为None。 + tools (list, optional): 工具列表,默认为None。 + stream (bool, optional): 是否使用流式处理,默认为False。 + event_handler (callable, optional): 事件处理函数,默认为None。 + action (object, optional): 对话时要进行的特殊操作。如回复工作流agent中“信息收集节点“的消息。 + **kwargs: 其他可选参数。 + + Returns: + None + + """ + self._appbuilder_client = appbuilder_client + self._conversation_id = conversation_id + self._query = query + self._file_ids = file_ids + self._tools = tools + self._stream = stream + self._event_handler = event_handler + self._kwargs = kwargs + self._is_complete = False + self._need_tool_call = False + self._last_tool_output = None + self._action = action + + self._iterator = ( + self.__run_process__() + if not self._stream + else self.__stream_run_process__() + ) + + async def __run_process__(self): + """ + 运行进程,并在每次执行后生成结果。 + + Args: + 无参数。 + + Returns: + Generator: 生成器,每次执行后返回结果。 + + """ + while not self._is_complete: + if not self._need_tool_call: + res = await self._run() + await self.__event_process__(res) + else: + res = await self._submit_tool_output() + await self.__event_process__(res) + yield res + if self._need_tool_call and self._is_complete: + await self.reset_state() + + async def __async_run_process__(self): + """ + 异步运行进程,并在每次执行后生成结果 + + Args: + 无参数 + + Returns: + Generator[Any, None, None]: 生成器,每次执行后返回结果 + """ + while not self._is_complete: + if not self._need_tool_call: + res = await self._run() + self.__event_process__(res) + else: + res = await self._submit_tool_output() + self.__event_process__(res) + yield res + if self._need_tool_call and self._is_complete: + self.reset_state() + + async def __event_process__(self, run_response): + """ + 处理事件响应。 + + Args: + run_response (RunResponse): 运行时响应对象。 + + Returns: + None + + Raises: + ValueError: 当解析事件时发生异常或工具输出为空时。 + """ + try: + event = run_response.content.events[-1] + except Exception as e: + raise ValueError(e) + + event_status = event.status + + if event.status == "success": + self._is_complete = True + elif event.status == "interrupt": + self._need_tool_call = True + + context_func_map = { + "preparing": self.preparing, + "running": self.running, + "error": self.error, + "done": self.done, + "interrupt": self.interrupt, + "success": self.success, + } + + run_context = AppBuilderClientRunContext() + await self._update_run_context(run_context, run_response.content) + await self.handle_event_type(run_context, run_response.content) + await self.handle_content_type(run_context, run_response.content) + if event_status in context_func_map: + func = context_func_map[event_status] + func_res = await func(run_context, run_response.content) + + if event_status == "interrupt": + assert isinstance(func_res, list) + if len(func_res) == 0: + raise ValueError("Tool output is empty") + else: + if not isinstance(func_res[0], data_class.ToolOutput): + try: + check_tool_output = data_class.ToolOutput(**func_res[0]) + except Exception as e: + logger.error( + "func interrupt's output should be list[ToolOutput] or list[dict(can be trans to ToolOutput)]" + ) + raise ValueError(e) + self._last_tool_output = func_res + else: + logger.warning( + "Unknown status: {}, response data: {}".format( + event_status, run_response + ) + ) + + async def __stream_run_process__(self): + """ + 异步流式运行处理函数 + + Args: + 无参数 + + Returns: + Generator[Any, None, None]: 返回处理结果的生成器 + """ + while not self._is_complete: + if not self._need_tool_call: + res = await self._run() + else: + res = await self._submit_tool_output() + async for msg in self.__stream_event_process__(res): + yield msg + + async def __stream_event_process__(self, run_response): + """ + 处理流事件,并调用对应的方法 + + Args: + run_response: 包含流事件信息的响应对象 + + Returns: + None + + Raises: + ValueError: 当处理事件时发生异常或中断时工具输出为空时 + """ + async for msg in run_response.content: + if len(msg.events) == 0: + continue + try: + event = msg.events[-1] + except Exception as e: + raise ValueError(e) + + event_status = event.status + + if event.status == "success": + self._is_complete = True + elif event.status == "interrupt": + self._need_tool_call = True + + context_func_map = { + "preparing": self.preparing, + "running": self.running, + "error": self.error, + "done": self.done, + "interrupt": self.interrupt, + "success": self.success, + } + + run_context = AppBuilderClientRunContext() + await self._update_run_context(run_context, msg) + await self.handle_event_type(run_context, msg) + await self.handle_content_type(run_context, msg) + if event_status in context_func_map: + func = context_func_map[event_status] + func_res = await func(run_context, msg) + + if event_status == "interrupt": + assert isinstance(func_res, list) + if len(func_res) == 0: + raise ValueError("Tool output is empty") + else: + if not isinstance(func_res[0], data_class.ToolOutput): + try: + check_tool_output = data_class.ToolOutput(**func_res[0]) + except Exception as e: + logger.info( + "func interrupt's output should be list[ToolOutput] or list[dict(can be trans to ToolOutput)]" + ) + raise ValueError(e) + self._last_tool_output = func_res + else: + logger.warning( + "Unknown status: {}, response data: {}".format( + event_status, run_response + ) + ) + + yield msg + + async def _update_run_context(self, run_context, run_response): + """ + 更新运行上下文。 + + Args: + run_context (dict): 运行上下文字典。 + run_response (object): 运行响应对象。 + + Returns: + None + + """ + run_context.current_event = run_response.events[-1] + run_context.current_tool_calls = run_context.current_event.tool_calls + run_context.current_status = run_context.current_event.status + run_context.need_tool_submit = run_context.current_status == "interrupt" + run_context.is_complete = run_context.current_status == "success" + try: + run_context.current_thought = ( + run_context.current_event.detail.get("text", {}) + .get("function_call", {}) + .get("thought", "") + ) + except Exception as e: + pass + + async def _run(self): + res = await self._appbuilder_client.run( + conversation_id=self._conversation_id, + query=self._query, + file_ids=self._file_ids, + stream=self._stream, + tools=self._tools, + action=self._action, + ) + return res + + async def _submit_tool_output(self): + assert self._last_tool_output is not None + res = await self._appbuilder_client.run( + conversation_id=self._conversation_id, + file_ids=self._file_ids, + stream=self._stream, + tool_outputs=self._last_tool_output, + ) + return res + + async def __anext__(self): + return await self._iterator.__anext__() + + async def __aiter__(self): + async for item in self._iterator: + yield item + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb) -> None: + if exc_type is not None: + raise exc_val + + return + + async def reset_state(self): + """ + 重置该对象的状态,将所有实例变量设置为默认值。 + + Args: + 无 + + Returns: + 无 + + """ + self._appbuilder_client = None + self._conversation_id = None + self._query = None + self._file_ids = None + self._tools = None + self._stream = False + self._event_handler = None + self._kwargs = None + self._last_tool_output = None + self._is_complete = False + self._need_tool_call = False + self._iterator = None + + async def new_dialog( + self, + query=None, + file_ids=None, + tools=None, + action=None, + stream: bool = None, + event_handler=None, + **kwargs + ): + """ + 重置handler部分参数,用于复用该handler进行多轮对话。 + + Args: + query (str): 用户输入的查询语句。 + file_ids (list, optional): 文件ID列表,默认为None。 + tools (list, optional): 工具列表,默认为None。 + stream (bool, optional): 是否使用流式处理,默认为False。 + action (object, optional): 对话时要进行的特殊操作。如回复工作流agent中“信息收集节点“的消息。 + event_handler (callable, optional): 事件处理函数,默认为None。 + **kwargs: 其他可选参数。 + + Returns: + None + + """ + self._query = query or self._query + self._stream = stream or self._stream + + self._file_ids = file_ids + self._tools = tools + self._event_handler = event_handler + self._kwargs = kwargs + self._action = action + + # 重置部分状态 + self._is_complete = False + self._need_tool_call = False + self._last_tool_output = None + self._iterator = ( + self.__run_process__() + if not self._stream + else self.__stream_run_process__() + ) + + async def until_done(self): + """ + 迭代并遍历内部迭代器中的所有元素,直到迭代器耗尽。 + + Args: + 无参数。 + + Returns: + 无返回值。 + + """ + async for _ in self._iterator: + pass + + async def handle_content_type(self, run_context, run_response): + # 用户可重载该方法,用于处理不同类型的content_type + pass + + async def handle_event_type(self, run_context, run_response): + # 用户可重载该方法,用于处理不同类型的event_type + pass + + async def interrupt(self, run_context, run_response): + # 用户可重载该方法,当event_status为interrupt时,会调用该方法 + pass + + async def preparing(self, run_context, run_response): + # 用户可重载该方法,当event_status为preparing时,会调用该方法 + pass + + async def running(self, run_context, run_response): + # 用户可重载该方法,当event_status为running时,会调用该方法 + pass + + async def error(self, run_context, run_response): + # 用户可重载该方法,当event_status为error时,会调用该方法 + pass + + async def done(self, run_context, run_response): + # 用户可重载该方法,当event_status为done时,会调用该方法 + pass + + async def success(self, run_context, run_response): + # 用户可重载该方法,当event_status为success时,会调用该方法 + pass diff --git a/python/tests/test_async_appbuilder_client.py b/python/tests/test_async_appbuilder_client.py new file mode 100644 index 000000000..407a7a2d1 --- /dev/null +++ b/python/tests/test_async_appbuilder_client.py @@ -0,0 +1,79 @@ +# Copyright (c) 2024 Baidu, Inc. 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 unittest +import os +import asyncio +import appbuilder + + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") +class TestAppBuilderClientAsync(unittest.TestCase): + def setUp(self): + """ + 设置环境变量。 + + Args: + 无参数,默认值为空。 + + Returns: + 无返回值,方法中执行了环境变量的赋值操作。 + """ + self.app_id = "fb64d96b-f828-4385-ba1d-835298d635a9" + + def test_async_run_stream(self): + appbuilder.logger.setLoglevel("ERROR") + + async def agent_run(client, conversation_id, text): + ans = await client.run(conversation_id, text, stream=True) + async for data in ans.content: + print(data) + + async def agent_handle(): + client = appbuilder.AsyncAppBuilderClient(self.app_id) + conversation_id = await client.create_conversation() + task1 = asyncio.create_task( + agent_run(client, conversation_id, "最早的邮展")) + task2 = asyncio.create_task( + agent_run(client, conversation_id, "最早的漫展")) + await asyncio.gather(task1, task2) + await client.http_client.session.close() + + loop = asyncio.get_event_loop() + loop.run_until_complete(agent_handle()) + + def test_async_run(self): + appbuilder.logger.setLoglevel("ERROR") + + async def agent_run(client, conversation_id, text): + ans = await client.run(conversation_id, text, stream=False) + print(ans.content.answer) + + async def agent_handle(): + client = appbuilder.AsyncAppBuilderClient(self.app_id) + conversation_id = await client.create_conversation() + await client.upload_local_file(conversation_id, "./data/qa_appbuilder_client_demo.pdf") + task1 = asyncio.create_task( + agent_run(client, conversation_id, "最早的邮展")) + task2 = asyncio.create_task( + agent_run(client, conversation_id, "最早的漫展")) + await asyncio.gather(task1, task2) + await client.http_client.session.close() + + loop = asyncio.get_event_loop() + loop.run_until_complete(agent_handle()) + + +if __name__ == "__main__": + unittest.main() diff --git a/python/tests/test_async_appbuilder_client_chatflow.py b/python/tests/test_async_appbuilder_client_chatflow.py new file mode 100644 index 000000000..d6ebe6896 --- /dev/null +++ b/python/tests/test_async_appbuilder_client_chatflow.py @@ -0,0 +1,204 @@ +# Copyright (c) 2024 Baidu, Inc. 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 os +import asyncio +import unittest +import appbuilder +from appbuilder.core.console.appbuilder_client.async_event_handler import ( + AsyncAppBuilderEventHandler, +) + + +class MyEventHandler(AsyncAppBuilderEventHandler): + def __init__(self): + super().__init__() + self.interrupt_ids = [] + + async def handle_content_type(self, run_context, run_response): + interrupt_event_id = None + event = run_response.events[-1] + if event.content_type == "chatflow_interrupt": + interrupt_event_id = event.detail.get("interrupt_event_id") + if interrupt_event_id is not None: + self.interrupt_ids.append(interrupt_event_id) + + def _create_action(self): + if len(self.interrupt_ids) == 0: + return None + event_id = self.interrupt_ids.pop() + return { + "action_type": "resume", + "parameters": {"interrupt_event": {"id": event_id, "type": "chat"}}, + } + + async def run(self, query=None): + await super().new_dialog( + query=query, + action=self._create_action(), + ) + + def gen_action(self): + while True: + yield self._create_action() + + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") +class TestAppBuilderClientChatflow(unittest.TestCase): + def setUp(self): + """ + 设置环境变量。 + + Args: + 无参数,默认值为空。 + + Returns: + 无返回值,方法中执行了环境变量的赋值操作。 + """ + self.app_id = "4403205e-fb83-4fac-96d8-943bdb63796f" + + def test_chatflow(self): + appbuilder.logger.setLoglevel("DEBUG") + + async def agent_handle(): + client = appbuilder.AsyncAppBuilderClient(self.app_id) + conversation_id = await client.create_conversation() + event_handler = MyEventHandler() + await event_handler.init( + appbuilder_client=client, + conversation_id=conversation_id, + stream=False, + query="查天气", + ) + async for data in event_handler: + pass + await event_handler.run( + query="查航班", + ) + async for data in event_handler: + pass + await event_handler.run( + query="CA1234", + ) + async for data in event_handler: + pass + await event_handler.run( + query="北京的", + ) + async for data in event_handler: + pass + + await client.http_client.session.close() + + loop = asyncio.get_event_loop() + loop.run_until_complete(agent_handle()) + + def test_chatflow_stream(self): + appbuilder.logger.setLoglevel("DEBUG") + + async def agent_handle(): + client = appbuilder.AsyncAppBuilderClient(self.app_id) + conversation_id = await client.create_conversation() + event_handler = MyEventHandler() + await event_handler.init( + appbuilder_client=client, + conversation_id=conversation_id, + stream=True, + query="查天气", + ) + async for data in event_handler: + pass + await event_handler.run( + query="查航班", + ) + async for data in event_handler: + pass + await event_handler.run( + query="CA1234", + ) + async for data in event_handler: + pass + await event_handler.run( + query="北京的", + ) + async for data in event_handler: + pass + + await client.http_client.session.close() + + loop = asyncio.get_event_loop() + loop.run_until_complete(agent_handle()) + + def test_chatflow_stream(self): + appbuilder.logger.setLoglevel("DEBUG") + + async def agent_handle(): + client = appbuilder.AsyncAppBuilderClient(self.app_id) + conversation_id = await client.create_conversation() + event_handler = MyEventHandler() + await event_handler.init( + appbuilder_client=client, + conversation_id=conversation_id, + stream=True, + query="查天气", + ) + async for data in event_handler: + pass + await event_handler.run( + query="查航班", + ) + async for data in event_handler: + pass + await event_handler.run( + query="CA1234", + ) + async for data in event_handler: + pass + await event_handler.run( + query="北京的", + ) + async for data in event_handler: + pass + + await client.http_client.session.close() + + loop = asyncio.get_event_loop() + loop.run_until_complete(agent_handle()) + + def test_chatflow_multiple_dialog(self): + appbuilder.logger.setLoglevel("DEBUG") + + async def agent_handle(): + client = appbuilder.AsyncAppBuilderClient(self.app_id) + conversation_id = await client.create_conversation() + queries = ["查天气", "查航班", "CA1234", "北京的"] + event_handler = MyEventHandler() + event_handler = client.run_multiple_dialog_with_handler( + conversation_id=conversation_id, + queries=queries, + event_handler=event_handler, + stream=False, + actions=event_handler.gen_action(), + ) + async for data in event_handler: + async for answer in data: + print(answer) + + await client.http_client.session.close() + + loop = asyncio.get_event_loop() + loop.run_until_complete(agent_handle()) + + +if __name__ == "__main__": + unittest.main() diff --git a/python/tests/test_async_appbuilder_client_follow_up_query.py b/python/tests/test_async_appbuilder_client_follow_up_query.py new file mode 100644 index 000000000..0c7f54ae3 --- /dev/null +++ b/python/tests/test_async_appbuilder_client_follow_up_query.py @@ -0,0 +1,72 @@ +# Copyright (c) 2024 Baidu, Inc. 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 unittest +import os +import asyncio +import appbuilder +from appbuilder.core.console.appbuilder_client.async_event_handler import ( + AsyncAppBuilderEventHandler, +) + + +class MyEventHandler(AsyncAppBuilderEventHandler): + def __init__(self): + super().__init__() + self.follow_up_queries = [] + + async def handle_content_type(self, run_context, run_response): + event = run_response.events[-1] + if event.content_type == "json" and event.event_type == "FollowUpQuery": + follow_up_queries = event.detail.get("json").get("follow_up_querys") + self.follow_up_queries.extend(follow_up_queries) + + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") +class TestAppBuilderClientAsync(unittest.TestCase): + def setUp(self): + """ + 设置环境变量。 + + Args: + 无参数,默认值为空。 + + Returns: + 无返回值,方法中执行了环境变量的赋值操作。 + """ + self.app_id = "fb64d96b-f828-4385-ba1d-835298d635a9" + + def test_async_run_stream(self): + appbuilder.logger.setLoglevel("ERROR") + async def agent_handle(): + client = appbuilder.AsyncAppBuilderClient(self.app_id) + conversation_id = await client.create_conversation() + event_handler = MyEventHandler() + with await client.run_with_handler( + conversation_id = conversation_id, + query = "你能做什么", + stream=True, + event_handler=event_handler, + ) as run: + await run.until_done() + + print(event_handler.follow_up_queries) + assert len(event_handler.follow_up_queries) > 0 + await client.http_client.session.close() + + loop = asyncio.get_event_loop() + loop.run_until_complete(agent_handle()) + +if __name__ == "__main__": + unittest.main() diff --git a/python/tests/test_async_appbuilder_client_toolcall.py b/python/tests/test_async_appbuilder_client_toolcall.py new file mode 100644 index 000000000..00322bf3b --- /dev/null +++ b/python/tests/test_async_appbuilder_client_toolcall.py @@ -0,0 +1,122 @@ +# Copyright (c) 2024 Baidu, Inc. 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 unittest +import appbuilder +import asyncio +import os +from appbuilder.core.console.appbuilder_client.async_event_handler import ( + AsyncAppBuilderEventHandler, +) + + +class MyEventHandler(AsyncAppBuilderEventHandler): + def get_current_weather(self, location=None, unit="摄氏度"): + return "{} 的温度是 {} {}".format(location, 20, unit) + + async def interrupt(self, run_context, run_response): + thought = run_context.current_thought + # 绿色打印 + print("\033[1;32m", "-> Agent 中间思考: ", thought, "\033[0m") + + tool_output = [] + for tool_call in run_context.current_tool_calls: + tool_call_id = tool_call.id + tool_res = self.get_current_weather(**tool_call.function.arguments) + # 蓝色打印 + print("\033[1;34m", "-> 本地ToolCall结果: ", tool_res, "\033[0m\n") + tool_output.append( + {"tool_call_id": tool_call_id, "output": tool_res}) + return tool_output + + async def success(self, run_context, run_response): + print("\n\033[1;31m", "-> Agent 非流式回答: ", + run_response.answer, "\033[0m") + + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") +class TestAgentRuntime(unittest.TestCase): + def setUp(self): + """ + 设置环境变量。 + + Args: + 无参数,默认值为空。 + + Returns: + 无返回值,方法中执行了环境变量的赋值操作。 + """ + self.app_id = "b2a972c5-e082-46e5-b313-acbf51792422" + + def test_appbuilder_client_tool_call(self): + # 如果app_id为空,则跳过单测执行, 避免单测因配置无效而失败 + """ + 如果app_id为空,则跳过单测执行, 避免单测因配置无效而失败 + + Args: + self (unittest.TestCase): unittest的TestCase对象 + + Raises: + None: 如果app_id不为空,则不会引发任何异常 + unittest.SkipTest (optional): 如果app_id为空,则跳过单测执行 + """ + tools = [ + { + "type": "function", + "function": { + "name": "get_current_weather", + "description": "仅支持中国城市的天气查询,参数location为中国城市名称,其他国家城市不支持天气查询", + "parameters": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "城市名,举例:北京", + }, + "unit": {"type": "string", "enum": ["摄氏度", "华氏度"]}, + }, + "required": ["location", "unit"], + }, + }, + } + ] + + appbuilder.logger.setLoglevel("ERROR") + + async def agent_run(client, conversation_id, query): + with await client.run_with_handler( + conversation_id=conversation_id, + query=query, + tools=tools, + event_handler=MyEventHandler(), + ) as run: + await run.until_done() + + async def agent_handle(): + client = appbuilder.AsyncAppBuilderClient(self.app_id) + conversation_id = await client.create_conversation() + task1 = asyncio.create_task( + agent_run(client, conversation_id, "北京的天气怎么样")) + task2 = asyncio.create_task( + agent_run(client, conversation_id, "上海的天气怎么样")) + await asyncio.gather(task1, task2) + + await client.http_client.session.close() + + loop = asyncio.get_event_loop() + loop.run_until_complete(agent_handle()) + + +if __name__ == "__main__": + unittest.main() diff --git a/python/tests/test_core_client.py b/python/tests/test_core_client.py index 1cd654408..b1b8f52ad 100644 --- a/python/tests/test_core_client.py +++ b/python/tests/test_core_client.py @@ -15,100 +15,142 @@ import unittest import json -from appbuilder.core._client import HTTPClient -from appbuilder.core._exception import * +from appbuilder.core._client import HTTPClient, AsyncHTTPClient +from appbuilder.core._exception import * # 创建一个response类,模拟requests.Response + + class Response: def __init__(self, status_code, headers, text): self.status_code = status_code self.headers = headers self.text = text - + + def json(self): + return json.loads(self.text) + + +class AsyncResponse: + def __init__(self, status_code, headers, text): + self.status = status_code + self.headers = headers + self.text = text + def json(self): - return json.loads(self.text) + return json.loads(self.text) + class TestCoreClient(unittest.TestCase): def setUp(self): # 保存原始环境变量 self.original_appbuilder_token = os.getenv('APPBUILDER_TOKEN') self.original_gateway_url = os.getenv('GATEWAY_URL') - + def tearDown(self): # 恢复环境变量 if self.original_appbuilder_token is None: os.unsetenv('APPBUILDER_TOKEN') else: os.environ['APPBUILDER_TOKEN'] = self.original_appbuilder_token - + if self.original_gateway_url is None: os.unsetenv('GATEWAY_URL') else: os.environ['GATEWAY_URL'] = self.original_gateway_url - + def test_core_client_init_non_APPBUILDER_TOKEN(self): os.environ['APPBUILDER_TOKEN'] = '' with self.assertRaises(ValueError): HTTPClient() - + def test_core_client_init_non_GATEWAY_URL(self): - os.environ['GATEWAY_URL'] = 'test' - hp=HTTPClient() + os.environ['GATEWAY_URL'] = 'test' + hp = HTTPClient() assert hp.gateway.startswith('https://') - + def test_core_client_check_response_header(self): # 测试各种response报错 response = Response( status_code=400, - headers={'Content-Type': 'application/json'} , + headers={'Content-Type': 'application/json'}, text='{"code": 0, "message": "success"}' - ) + ) with self.assertRaises(BadRequestException): HTTPClient.check_response_header(response) - + response.status_code = 403 with self.assertRaises(ForbiddenException): HTTPClient.check_response_header(response) - + response.status_code = 404 with self.assertRaises(NotFoundException): HTTPClient.check_response_header(response) - + response.status_code = 428 with self.assertRaises(PreconditionFailedException): HTTPClient.check_response_header(response) - + response.status_code = 500 with self.assertRaises(InternalServerErrorException): HTTPClient.check_response_header(response) - + response.status_code = 201 with self.assertRaises(BaseRPCException): HTTPClient.check_response_header(response) - + + def test_core_client_check_async_response_header(self): + # 测试各种response报错 + response = AsyncResponse( + status_code=400, + headers={'Content-Type': 'application/json'}, + text='{"code": 0, "message": "success"}' + ) + with self.assertRaises(BadRequestException): + AsyncHTTPClient.check_response_header(response) + + response.status = 403 + with self.assertRaises(ForbiddenException): + AsyncHTTPClient.check_response_header(response) + + response.status = 404 + with self.assertRaises(NotFoundException): + AsyncHTTPClient.check_response_header(response) + + response.status = 428 + with self.assertRaises(PreconditionFailedException): + AsyncHTTPClient.check_response_header(response) + + response.status = 500 + with self.assertRaises(InternalServerErrorException): + AsyncHTTPClient.check_response_header(response) + + response.status = 201 + with self.assertRaises(BaseRPCException): + AsyncHTTPClient.check_response_header(response) + def test_core_client_check_response_json(self): - data={ + data = { 'code': 0, 'message': 'test', - 'requestId':'test' - } + 'requestId': 'test' + } with self.assertRaises(AppBuilderServerException): HTTPClient.check_response_json(data) - + def test_core_check_console_response(self): response = Response( status_code=400, - headers={'Content-Type': 'application/json'} , + headers={'Content-Type': 'application/json'}, text=json.dumps({ 'code': 1, 'message': 'test', - 'requestId':'test' + 'requestId': 'test' }) - ) + ) with self.assertRaises(AppBuilderServerException): HTTPClient.check_console_response(response) - - + + if __name__ == '__main__': unittest.main() - \ No newline at end of file diff --git a/python/tests/test_core_session.py b/python/tests/test_core_session.py new file mode 100644 index 000000000..c9d671b0e --- /dev/null +++ b/python/tests/test_core_session.py @@ -0,0 +1,41 @@ +# Copyright (c) 2024 Baidu, Inc. 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 unittest +import appbuilder +import asyncio +import aiohttp +from unittest.mock import patch, MagicMock +from appbuilder.core._session import AsyncInnerSession + +class TestCoreSession(unittest.TestCase): + @patch("aiohttp.ClientSession.put") + def test_async_session_get(self, mock_put): + async def demo(): + return {"status": 200} + + async def async_magic(): + pass + + async def get_demo(): + mock_put.return_value.__aenter__.return_value.json = await demo() + MagicMock.__await__ = lambda x: async_magic().__await__() + session = AsyncInnerSession() + await session.get("http://www.baidu.com") + await session.put("https://example.com") + + loop = asyncio.get_event_loop() + loop.run_until_complete(get_demo()) + +if __name__ == "__main__": + unittest.main() diff --git a/python/tests/test_utils.py b/python/tests/test_utils.py index 8bce87418..b2a4b0011 100644 --- a/python/tests/test_utils.py +++ b/python/tests/test_utils.py @@ -13,9 +13,10 @@ # limitations under the License. import os import unittest +import asyncio from unittest.mock import MagicMock -from appbuilder.utils.sse_util import SSEClient,Event +from appbuilder.utils.sse_util import SSEClient,AsyncSSEClient, Event from appbuilder.utils.model_util import RemoteModel,Models from appbuilder.utils.logger_util import LoggerWithLoggerId,_setup_logging,logger from threading import current_thread @@ -55,6 +56,33 @@ def test_sse_util_SSEClient(self): # test_close sse_client.close() + + def test_sse_util_AsyncSSEClient(self): + async def mock_client(): + mock_event_source = MagicMock() + mock_event_source.__iter__.return_value = iter([ + b'data: Test event 1\n\n', + b'data: Last incomplete event' + ]) + sse_client = AsyncSSEClient(mock_event_source) + event_generator = sse_client._read() + async for data in event_generator: + pass + + # test_events + mock_event_source.__aiter__.return_value = iter([ + b': Test event 1\n\n', + b'test: Test event 2\n\n', + b'data:Testevent3\n\n', + b'data\n\n', + b'event:Testevent5\n\n', + ]) + sse_client = AsyncSSEClient(mock_event_source) + async for event in sse_client.events(): + pass + + loop = asyncio.get_event_loop() + loop.run_until_complete(mock_client()) def test_sse_util_SSEClient_DEBUG(self): logger.setLoglevel("DEBUG") diff --git a/python/utils/sse_util.py b/python/utils/sse_util.py index 027923c8e..a1984a397 100644 --- a/python/utils/sse_util.py +++ b/python/utils/sse_util.py @@ -16,19 +16,21 @@ """ from appbuilder.utils.logger_util import logger import logging +import aiohttp + class SSEClient: """ 一个简易的SSE Client,用于接收服务端发送的SSE事件。 """ - def __init__(self, event_source, char_enc='utf-8'): + def __init__(self, event_source, char_enc="utf-8"): """ 通过现有的事件源初始化 SSE 客户端。 事件源应为二进制流,并具有 close() 方法。 这通常是实现 io.BinaryIOBase 的东西,比如 httplib 或 urllib3HTTPResponse 对象。 """ - logger.info(f'Initialized SSE client from event source {event_source}') + logger.info(f"Initialized SSE client from event source {event_source}") self._event_source = event_source self._char_enc = char_enc @@ -38,23 +40,23 @@ def _read(self): 不幸的是,有些服务器可能会决定在响应中将事件分解为多个HTTP块。 因此,有必要正确地将连续的响应块缝合在一起,并找到SSE分隔符(空的新行),以生成完整、正确的事件块。 """ - data = b'' + data = b"" for chunk in self._event_source: for line in chunk.splitlines(True): data += line - if data.endswith((b'\r\r', b'\n\n', b'\r\n\r\n')): + if data.endswith((b"\r\r", b"\n\n", b"\r\n\r\n")): yield data - data = b'' + data = b"" if data: yield data def events(self): """ 从给定的输入流中读取 Server-Side-Event (SSE) 数据,并生成解析后的 Event 对象。 - + Args: 无 - + Returns: generator: 解析后的 Event 对象的生成器。 """ @@ -66,34 +68,36 @@ def events(self): line = line.decode(self._char_enc) # Lines starting with a separator are comments and are to be # ignored. - if not line.strip() or line.startswith(':'): + if not line.strip() or line.startswith(":"): continue logger.debug(f"raw line: {line}") - data = line.split(':', 1) + data = line.split(":", 1) field = data[0] # Ignore unknown fields. if field not in event.__dict__: event.raw += line - logger.info(f'Saw invalid field {field} while parsing Server Side Event') + logger.info( + f"Saw invalid field {field} while parsing Server Side Event" + ) continue if len(data) > 1: # From the spec: # "If value starts with a single U+0020 SPACE character, # remove it from value." - if data[1].startswith(' '): + if data[1].startswith(" "): value = data[1][1:] else: value = data[1] else: # If no value is present after the separator, # assume an empty value. - value = '' + value = "" # The data field may come over multiple lines and their values # are concatenated with each other. - if field == 'data': - event.__dict__[field] += value + '\n' - event.raw += value + '\n' + if field == "data": + event.__dict__[field] += value + "\n" + event.raw += value + "\n" else: event.__dict__[field] = value event.raw += value @@ -107,15 +111,15 @@ def events(self): continue else: # If the data field ends with a newline, remove it. - if event.data.endswith('\n'): + if event.data.endswith("\n"): event.data = event.data[0:-1] # Empty event names default to 'message' - event.event = event.event or 'message' + event.event = event.event or "message" # Dispatch the event if logger.getEffectiveLevel() == logging.DEBUG: - logger.debug(f'Dispatching {event.debug_str}...') + logger.debug(f"Dispatching {event.debug_str}...") else: - logger.info(f'Dispatching {event}...') + logger.info(f"Dispatching {event}...") yield event def close(self): @@ -125,11 +129,96 @@ def close(self): self._event_source.close() +class AsyncSSEClient: + """ + 一个简易的SSE Client,用于接收服务端发送的SSE事件。 + """ + def __init__(self, response, char_enc='utf-8'): + """ + 通过现有的事件源response初始化 SSE 客户端。 + response应为aiohttp.ClientResponse实例 + """ + self._response = response + self._char_enc = char_enc + + async def _read(self): + """ + 读取传入的事件源流并生成事件块。 + """ + data = b'' + async for chunk in self._response.content.iter_any(): + for line in chunk.splitlines(True): + data += line + if data.endswith((b'\r\r', b'\n\n', b'\r\n\r\n')): + yield data + data = b'' + if data: + yield data + + async def events(self): + """ + 从给定的输入流中读取 Server-Side-Event (SSE) 数据,并生成解析后的 Event 对象。 + Returns: + generator: 解析后的 Event 对象的生成器。 + """ + async for chunk in self._read(): + event = Event() + # Split before decoding so splitlines() only uses \r and \n + for line in chunk.splitlines(): + # Decode the line. + line = line.decode(self._char_enc) + # Lines starting with a separator are comments and are to be ignored. + if not line.strip() or line.startswith(':'): + continue + + data = line.split(':', 1) + field = data[0] + # Ignore unknown fields. + if field not in event.__dict__: + event.raw += line + continue + + if len(data) > 1: + # From the spec: + # "If value starts with a single U+0020 SPACE character, + # remove it from value." + if data[1].startswith(' '): + value = data[1][1:] + else: + value = data[1] + else: + # If no value is present after the separator, + # assume an empty value. + value = '' + + # The data field may come over multiple lines and their values are concatenated with each other. + if field == 'data': + event.__dict__[field] += value + '\n' + event.raw += value + '\n' + else: + event.__dict__[field] = value + event.raw += value + + # Events with no data are not dispatched. + if not event.data: + continue + + # If the data field ends with a newline, remove it. + if event.data.endswith('\n'): + event.data = event.data[0:-1] + + # Empty event names default to 'message' + event.event = event.event or 'message' + + yield event + + class Event(object): """ 事件流中的事件。 """ - def __init__(self, id=None, event='message', data='', retry=None): + + def __init__(self, id=None, event="message", data="", retry=None): self.id = id self.event = event self.data = data @@ -137,30 +226,30 @@ def __init__(self, id=None, event='message', data='', retry=None): self.raw = "" def __str__(self): - s = f'{self.event} event' + s = f"{self.event} event" if self.id: - s += f' #{self.id}' + s += f" #{self.id}" if self.data: - s += f', {len(self.data)} byte' + s += f", {len(self.data)} byte" else: - s += ', no data' + s += ", no data" if self.retry: - s += f', retry in {self.retry} ms' + s += f", retry in {self.retry} ms" return s @property def debug_str(self): - s = f'{self.event} event' + s = f"{self.event} event" if self.id: - s += f' #{self.id}' + s += f" #{self.id}" if self.data: - s += f', {len(self.data)} byte, DATA<<{self.data}>>' + s += f", {len(self.data)} byte, DATA<<{self.data}>>" else: - s += ', no data' + s += ", no data" if self.raw: - s += f', RAW<<{self.raw}>>' + s += f", RAW<<{self.raw}>>" else: - s += ', no raw' + s += ", no raw" if self.retry: - s += f', retry in {self.retry} ms' + s += f", retry in {self.retry} ms" return s From b7b41fc691edcbea9fdeab2d34121e3bb55b977b Mon Sep 17 00:00:00 2001 From: C9luster <138663536+C9luster@users.noreply.github.com> Date: Fri, 20 Dec 2024 13:59:50 +0800 Subject: [PATCH 80/85] =?UTF-8?q?=E6=9A=82=E6=97=B6=E5=9B=9E=E6=BB=9A?= =?UTF-8?q?=E5=BC=82=E6=AD=A5=E8=B0=83=E7=94=A8trace=E8=A3=85=E9=A5=B0?= =?UTF-8?q?=E5=99=A8=20(#670)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: yinjiaqi --- python/core/_session.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/python/core/_session.py b/python/core/_session.py index 31e398228..bc077c3a9 100644 --- a/python/core/_session.py +++ b/python/core/_session.py @@ -109,22 +109,18 @@ async def build_curl(self, method, url, data=None, json_data=None, **kwargs) -> return curl - @session_post async def post(self, url, data=None, json=None, **kwargs): logger.debug("Curl Command:\n" + await self.build_curl(hdrs.METH_POST, url, data=data, json_data=json, **kwargs) + "\n") return await super().post(url=url, data=data, json=json, **kwargs) - @session_post async def delete(self, url, **kwargs): logger.debug("Curl Command:\n" + await self.build_curl(hdrs.METH_DELETE, url, **kwargs) + "\n") return await super().delete(url=url, **kwargs) - @session_post async def get(self, url, **kwargs): logger.debug("Curl Command:\n" + await self.build_curl(hdrs.METH_GET, url, **kwargs) + "\n") return await super().get(url=url, **kwargs) - @session_post async def put(self, url, data=None, **kwargs): logger.debug("Curl Command:\n" + await self.build_curl(hdrs.METH_PUT, url, data=data, **kwargs) + "\n") return await super().put(url=url, data=data, **kwargs) From bbaca946df1f446284b391549eef2f0aa9721f41 Mon Sep 17 00:00:00 2001 From: C9luster <138663536+C9luster@users.noreply.github.com> Date: Fri, 20 Dec 2024 14:00:26 +0800 Subject: [PATCH 81/85] =?UTF-8?q?=E8=B7=B3=E8=BF=87=E9=83=A8=E5=88=86?= =?UTF-8?q?=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95=20(#675)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 跳过部分单元测试 * update --------- Co-authored-by: yinjiaqi --- python/tests/component_collector.py | 12 ++++++++---- python/tests/test_all_components.py | 2 +- python/tests/test_appbuilder_components_trace.py | 15 +++++++-------- python/tests/test_asr.py | 2 +- python/tests/test_tts.py | 2 +- python/tests/test_v2_asr.py | 2 +- 6 files changed, 19 insertions(+), 16 deletions(-) diff --git a/python/tests/component_collector.py b/python/tests/component_collector.py index 445dcee5d..b6887446d 100644 --- a/python/tests/component_collector.py +++ b/python/tests/component_collector.py @@ -18,6 +18,10 @@ SKIP_COMPONENTS = [ ] +V2_SKIP_COMPONENTS = [ + "ASR", +] + # 白名单中的组件因历史原因,检查失败,但可以正常使用,因此加入白名单 COMPONENT_WHITE_LIST = [ "RagWithBaiduSearchPro", @@ -75,10 +79,10 @@ def get_component_white_list(): return COMPONENT_WHITE_LIST -def get_components(components_list, import_prefix): +def get_components(components_list, import_prefix, skip_components): components = {} for component in components_list: - if component in SKIP_COMPONENTS: + if component.__name__ in skip_components: continue try: @@ -98,12 +102,12 @@ def get_components(components_list, import_prefix): def get_all_components(): from appbuilder import __COMPONENTS__ - all_components = get_components(__COMPONENTS__, "appbuilder.") + all_components = get_components(__COMPONENTS__, "appbuilder.", SKIP_COMPONENTS) return all_components def get_v2_components(): from appbuilder.core.components.v2 import __V2_COMPONENTS__ - v2_components = get_components(__V2_COMPONENTS__, "appbuilder.core.components.v2.") + v2_components = get_components(__V2_COMPONENTS__, "appbuilder.core.components.v2.", V2_SKIP_COMPONENTS) return v2_components if __name__ == '__main__': diff --git a/python/tests/test_all_components.py b/python/tests/test_all_components.py index 83c605e1e..00dc956cd 100644 --- a/python/tests/test_all_components.py +++ b/python/tests/test_all_components.py @@ -85,7 +85,7 @@ def write_error_data(txt_file_path, error_df, error_stats): file.write(f"错误信息: {error}, 出现次数: {count}\n") print(f"\n错误信息已写入: {txt_file_path}") -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestComponentManifestsAndToolEval(unittest.TestCase): """ 组件manifests和tool_eval入参测试类 diff --git a/python/tests/test_appbuilder_components_trace.py b/python/tests/test_appbuilder_components_trace.py index 87bc4ef8e..c8adaaeb9 100644 --- a/python/tests/test_appbuilder_components_trace.py +++ b/python/tests/test_appbuilder_components_trace.py @@ -49,10 +49,10 @@ def setUp(self): 无返回值。 """ - self.audio_file_url = "https://bj.bcebos.com/v1/appbuilder/asr_test.pcm?authorization=bce-auth-v1" \ - "%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-11T10%3A56%3A41Z%2F-1%2Fhost" \ - "%2Fa6c4d2ca8a3f0259f4cae8ae3fa98a9f75afde1a063eaec04847c99ab7d1e411" - self.asr = appbuilder.ASR() + self.image_url = "https://bj.bcebos.com/v1/appbuilder/table_ocr_test.png?"\ + "authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-24T12%3A37%3A09Z%2F-1%2Fhost%2Fab528a5a9120d328dc6d18c6"\ + "064079145ff4698856f477b820147768fc2187d3" + self.table_ocr = appbuilder.TableOCR() self.play = appbuilder.Playground(prompt_template="你好,{name},我是{bot_name},{bot_name}是一个{bot_type},我可以{bot_function},你可以问我{bot_question}。", model="ERNIE-3.5-8K") model_name = "ERNIE-3.5-8K" secret_key = os.getenv('SECRET_KEY', None) @@ -77,10 +77,9 @@ def test_trace(self): tracer.start_trace() # test asr run and tool_eval - raw_audio = requests.get(self.audio_file_url).content - inp = appbuilder.Message(content={"raw_audio": raw_audio}) - out = self.asr.run(inp) - result = self.asr.tool_eval(name="asr", streaming=True, file_url=self.audio_file_url) + out = self.table_ocr.run(appbuilder.Message(content={"url": self.image_url})) + print(out) + result = self.table_ocr.tool_eval(name="asr", streaming=True, file_names=[self.image_url]) for res in result: print(res) diff --git a/python/tests/test_asr.py b/python/tests/test_asr.py index fc9cca4ed..3791e2678 100644 --- a/python/tests/test_asr.py +++ b/python/tests/test_asr.py @@ -8,7 +8,7 @@ from appbuilder.core.components.asr.model import ShortSpeechRecognitionRequest, ShortSpeechRecognitionResponse import os -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") +@unittest.skip("测试API超限,暂时跳过") class TestASRComponent(unittest.TestCase): def setUp(self): """ diff --git a/python/tests/test_tts.py b/python/tests/test_tts.py index 3fd2dd7ba..14f704d98 100644 --- a/python/tests/test_tts.py +++ b/python/tests/test_tts.py @@ -16,7 +16,7 @@ from appbuilder.core._exception import InvalidRequestArgumentError import os -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") +@unittest.skip("测试API超限,暂时跳过") class TestTTS(unittest.TestCase): def setUp(self): self.tts = appbuilder.TTS() diff --git a/python/tests/test_v2_asr.py b/python/tests/test_v2_asr.py index 585e4e5df..62a419ca2 100644 --- a/python/tests/test_v2_asr.py +++ b/python/tests/test_v2_asr.py @@ -23,7 +23,7 @@ from appbuilder.core.components.v2 import ASR from appbuilder.core.components.v2.asr.component import _convert as convert -@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_PARALLEL", "") +@unittest.skip("测试API超限,暂时跳过") class TestASR(unittest.TestCase): def setUp(self): self.audio_file_url = "https://bj.bcebos.com/v1/appbuilder/asr_test.pcm?authorization=bce-auth-v1" \ From d55e6fce80f639f5c34c016b42230d5c21282a19 Mon Sep 17 00:00:00 2001 From: userpj Date: Fri, 20 Dec 2024 15:24:30 +0800 Subject: [PATCH 82/85] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=BC=82=E6=AD=A5toolc?= =?UTF-8?q?all=E5=8D=95=E6=B5=8B=E5=B9=B6=E5=8F=91interrupt=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98=20(#677)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 修复异步toolcall单测并发interrupt的问题 * update component --- python/core/_client.py | 6 +- .../async_appbuilder_client.py | 8 +-- .../appbuilder_client/async_event_handler.py | 6 +- python/tests/component_collector.py | 2 +- .../test_async_appbuilder_client_toolcall.py | 13 ++-- python/tests/test_core_client.py | 60 ++++++++++--------- 6 files changed, 50 insertions(+), 45 deletions(-) diff --git a/python/core/_client.py b/python/core/_client.py index 83a2c524d..2f9b87dc6 100644 --- a/python/core/_client.py +++ b/python/core/_client.py @@ -243,7 +243,7 @@ def __init__(self, secret_key=None, gateway="", gateway_v2=""): self.session = AsyncInnerSession() @staticmethod - def check_response_header(response: ClientResponse): + async def check_response_header(response: ClientResponse): r"""check_response_header is a helper method for check head status . :param response: requests.Response. :rtype: @@ -252,7 +252,7 @@ def check_response_header(response: ClientResponse): if status_code == requests.codes.ok: return message = "request_id={} , http status code is {}, body is {}".format( - __class__.response_request_id(response), status_code, response.text + await __class__.response_request_id(response), status_code, await response.text() ) if status_code == requests.codes.bad_request: raise BadRequestException(message) @@ -268,7 +268,7 @@ def check_response_header(response: ClientResponse): raise BaseRPCException(message) @staticmethod - def response_request_id(response: ClientResponse): + async def response_request_id(response: ClientResponse): r"""response_request_id is a helper method to get the unique request id""" return response.headers.get("X-Appbuilder-Request-Id", "") diff --git a/python/core/console/appbuilder_client/async_appbuilder_client.py b/python/core/console/appbuilder_client/async_appbuilder_client.py index f6f010f2d..e116ac6ce 100644 --- a/python/core/console/appbuilder_client/async_appbuilder_client.py +++ b/python/core/console/appbuilder_client/async_appbuilder_client.py @@ -51,7 +51,7 @@ async def create_conversation(self) -> str: response = await self.http_client.session.post( url, headers=headers, json={"app_id": self.app_id}, timeout=None ) - self.http_client.check_response_header(response) + await self.http_client.check_response_header(response) data = await response.json() resp = data_class.CreateConversationResponse(**data) return resp.conversation_id @@ -116,8 +116,8 @@ async def run( response = await self.http_client.session.post( url, headers=headers, json=req.model_dump(), timeout=None ) - self.http_client.check_response_header(response) - request_id = self.http_client.response_request_id(response) + await self.http_client.check_response_header(response) + request_id = await self.http_client.response_request_id(response) if stream: client = AsyncSSEClient(response) return Message(content=self._iterate_events(request_id, client.events())) @@ -164,7 +164,7 @@ async def upload_local_file(self, conversation_id, local_file_path: str) -> str: response = await self.http_client.session.post( url, data=multipart_form_data, headers=headers ) - self.http_client.check_response_header(response) + await self.http_client.check_response_header(response) data = await response.json() resp = data_class.FileUploadResponse(**data) return resp.id diff --git a/python/core/console/appbuilder_client/async_event_handler.py b/python/core/console/appbuilder_client/async_event_handler.py index afa93eef9..992b38334 100644 --- a/python/core/console/appbuilder_client/async_event_handler.py +++ b/python/core/console/appbuilder_client/async_event_handler.py @@ -123,13 +123,13 @@ async def __async_run_process__(self): while not self._is_complete: if not self._need_tool_call: res = await self._run() - self.__event_process__(res) + await self.__event_process__(res) else: res = await self._submit_tool_output() - self.__event_process__(res) + await self.__event_process__(res) yield res if self._need_tool_call and self._is_complete: - self.reset_state() + await self.reset_state() async def __event_process__(self, run_response): """ diff --git a/python/tests/component_collector.py b/python/tests/component_collector.py index b6887446d..2b5b6548d 100644 --- a/python/tests/component_collector.py +++ b/python/tests/component_collector.py @@ -82,7 +82,7 @@ def get_component_white_list(): def get_components(components_list, import_prefix, skip_components): components = {} for component in components_list: - if component.__name__ in skip_components: + if component in skip_components: continue try: diff --git a/python/tests/test_async_appbuilder_client_toolcall.py b/python/tests/test_async_appbuilder_client_toolcall.py index 00322bf3b..1e03f834b 100644 --- a/python/tests/test_async_appbuilder_client_toolcall.py +++ b/python/tests/test_async_appbuilder_client_toolcall.py @@ -35,7 +35,8 @@ async def interrupt(self, run_context, run_response): tool_call_id = tool_call.id tool_res = self.get_current_weather(**tool_call.function.arguments) # 蓝色打印 - print("\033[1;34m", "-> 本地ToolCall结果: ", tool_res, "\033[0m\n") + print("\033[1;34m", "-> 本地ToolCallId: ", tool_call_id, "\033[0m") + print("\033[1;34m", "-> ToolCall结果: ", tool_res, "\033[0m\n") tool_output.append( {"tool_call_id": tool_call_id, "output": tool_res}) return tool_output @@ -92,9 +93,10 @@ def test_appbuilder_client_tool_call(self): } ] - appbuilder.logger.setLoglevel("ERROR") + appbuilder.logger.setLoglevel("DEBUG") - async def agent_run(client, conversation_id, query): + async def agent_run(client, query): + conversation_id = await client.create_conversation() with await client.run_with_handler( conversation_id=conversation_id, query=query, @@ -105,11 +107,10 @@ async def agent_run(client, conversation_id, query): async def agent_handle(): client = appbuilder.AsyncAppBuilderClient(self.app_id) - conversation_id = await client.create_conversation() task1 = asyncio.create_task( - agent_run(client, conversation_id, "北京的天气怎么样")) + agent_run(client, "北京的天气怎么样")) task2 = asyncio.create_task( - agent_run(client, conversation_id, "上海的天气怎么样")) + agent_run(client, "上海的天气怎么样")) await asyncio.gather(task1, task2) await client.http_client.session.close() diff --git a/python/tests/test_core_client.py b/python/tests/test_core_client.py index b1b8f52ad..8420be95a 100644 --- a/python/tests/test_core_client.py +++ b/python/tests/test_core_client.py @@ -14,6 +14,7 @@ import os import unittest import json +import asyncio from appbuilder.core._client import HTTPClient, AsyncHTTPClient from appbuilder.core._exception import * @@ -100,34 +101,37 @@ def test_core_client_check_response_header(self): HTTPClient.check_response_header(response) def test_core_client_check_async_response_header(self): - # 测试各种response报错 - response = AsyncResponse( - status_code=400, - headers={'Content-Type': 'application/json'}, - text='{"code": 0, "message": "success"}' - ) - with self.assertRaises(BadRequestException): - AsyncHTTPClient.check_response_header(response) - - response.status = 403 - with self.assertRaises(ForbiddenException): - AsyncHTTPClient.check_response_header(response) - - response.status = 404 - with self.assertRaises(NotFoundException): - AsyncHTTPClient.check_response_header(response) - - response.status = 428 - with self.assertRaises(PreconditionFailedException): - AsyncHTTPClient.check_response_header(response) - - response.status = 500 - with self.assertRaises(InternalServerErrorException): - AsyncHTTPClient.check_response_header(response) - - response.status = 201 - with self.assertRaises(BaseRPCException): - AsyncHTTPClient.check_response_header(response) + async def run_test(): + # 测试各种response报错 + response = AsyncResponse( + status_code=400, + headers={'Content-Type': 'application/json'}, + text=lambda:asyncio.sleep(0) or '{"code": 0, "message": "success"}' + ) + with self.assertRaises(BadRequestException): + await AsyncHTTPClient.check_response_header(response) + + response.status = 403 + with self.assertRaises(ForbiddenException): + await AsyncHTTPClient.check_response_header(response) + + response.status = 404 + with self.assertRaises(NotFoundException): + await AsyncHTTPClient.check_response_header(response) + + response.status = 428 + with self.assertRaises(PreconditionFailedException): + await AsyncHTTPClient.check_response_header(response) + + response.status = 500 + with self.assertRaises(InternalServerErrorException): + await AsyncHTTPClient.check_response_header(response) + + response.status = 201 + with self.assertRaises(BaseRPCException): + await AsyncHTTPClient.check_response_header(response) + loop = asyncio.get_event_loop() + loop.run_until_complete(run_test()) def test_core_client_check_response_json(self): data = { From a746f08baf224afe0c1c1df5d672e6f424b89032 Mon Sep 17 00:00:00 2001 From: peiwenYe <963623403@qq.com> Date: Mon, 23 Dec 2024 15:47:58 +0800 Subject: [PATCH 83/85] =?UTF-8?q?=E4=BF=AE=E6=94=B9reference=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E4=BF=9D=E7=95=99=E5=AD=97=E6=AE=B5=EF=BC=9B=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E6=A0=87=E5=87=86=E5=8C=96=E5=8D=95=E6=B5=8B=E6=A1=86?= =?UTF-8?q?=E6=9E=B6=E6=9B=B4=E6=96=B0:=20=E6=9B=B4=E6=96=B0=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E5=8F=98=E9=87=8F=EF=BC=8C=E5=A2=9E=E5=8A=A0tool=5Fev?= =?UTF-8?q?al=E5=8F=82=E6=95=B0=E5=92=8Cmanifests=E5=8C=B9=E9=85=8D?= =?UTF-8?q?=E6=80=A7=E6=A3=80=E6=9F=A5=20(#680)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 优化组件标准化单测框架:更新系统变量,增加tool_eval参数和manifests匹配性检查 * 组件标准化manifests更改回滚 * 修改references类型的保留字段 * 修改manifests改动对应的单测 * 修改manifests改动对应的单测 --------- Co-authored-by: yepeiwen01 --- python/core/component.py | 13 +- .../components/v2/handwrite_ocr/component.py | 15 +- .../components/v2/mix_card_ocr/component.py | 24 +- .../components/v2/qrcode_ocr/component.py | 15 +- .../core/components/v2/tree_mind/component.py | 13 + python/tests/component_check.py | 249 +++++++++++------- python/tests/component_tool_eval_cases.py | 6 +- python/tests/test_all_components.py | 99 ++----- python/tests/test_base_component.py | 3 + python/tests/test_v2_handwrite_ocr.py | 2 +- python/tests/test_v2_mix_card_ocr.py | 4 +- python/tests/test_v2_qrcode_ocr.py | 2 +- 12 files changed, 204 insertions(+), 241 deletions(-) diff --git a/python/core/component.py b/python/core/component.py index 23bcbac77..5628ed9f9 100644 --- a/python/core/component.py +++ b/python/core/component.py @@ -83,17 +83,11 @@ class OralText(BaseModel, extra='allow'): class References(BaseModel, extra='allow'): type: str = Field(default="", description="类型") - resource_type: str = Field(default="", description="资源类型") - icon: str = Field(default="", description="站点图标") - site_name: str = Field(default="", description="站点名") source: str = Field(default="", description="来源") doc_id: str = Field(default="", description="文档id") title: str = Field(default="", description="标题") content: str = Field(default="", description="内容") - image_content: str = Field(default="", description="图片内容") - mock_id: Optional[str] = Field(default="", description="模拟数据id") - image_url: str = Field(default="", description="图片url") - video_url: str = Field(default="", description="视频url") + extra: Optional[dict] = Field(default={}, description="其他信息") class Image(BaseModel, extra='allow'): @@ -548,8 +542,7 @@ def create_output(cls, type, text, role="tool", name="", visible_scope="all", ra elif type == "files": key_list = ["filename", "url"] elif type == "references": - key_list = ["type", "resource_type", "icon", "site_name", "source", - "doc_id", "title", "content", "image_content", "image_url", "video_url"] + key_list = ["type", "source", "doc_id", "title", "content"] elif type == "image": key_list = ["filename", "url"] elif type == "chart": @@ -562,7 +555,7 @@ def create_output(cls, type, text, role="tool", name="", visible_scope="all", ra key_list = ["thought", "name", "arguments"] else: raise ValueError("Unknown type: {}".format(type)) - # assert all(key in text for key in key_list), "all keys:{} must be included in the text field".format(key_list) + assert all(key in text for key in key_list), "all keys:{} must be included in the text field".format(key_list) else: raise ValueError("text must be str or dict") diff --git a/python/core/components/v2/handwrite_ocr/component.py b/python/core/components/v2/handwrite_ocr/component.py index 825666edc..06a960864 100644 --- a/python/core/components/v2/handwrite_ocr/component.py +++ b/python/core/components/v2/handwrite_ocr/component.py @@ -59,13 +59,6 @@ class HandwriteOCR(Component): "type": "string" }, "description": "待识别文件的文件名" - }, - "file_urls": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "待识别文件的url下载地址" } }, "required": ["file_names"] @@ -114,13 +107,11 @@ def run(self, message: Message, timeout: float = None, retry: int = 0) -> Messag @components_run_stream_trace def tool_eval(self, file_names: Optional[list] = [], - file_urls: Optional[dict] = {}, **kwargs): """ 工具评估函数 Args: file_names (Optional[list]): 待识别文件的文件名列表 - file_urls (Optional[dict]): 待识别文件的url下载地址字典 **kwargs: 其他参数 Raises: @@ -133,12 +124,10 @@ def tool_eval(self, result = "" sys_file_names = file_names - sys_file_urls = file_urls - if not sys_file_names: sys_file_names = kwargs.get('_sys_file_names', []) - if not sys_file_urls: - sys_file_urls = kwargs.get('_sys_file_urls', {}) + + sys_file_urls = kwargs.get('_sys_file_urls', {}) for file_name in sys_file_names: if utils.is_url(file_name): diff --git a/python/core/components/v2/mix_card_ocr/component.py b/python/core/components/v2/mix_card_ocr/component.py index 0f70387c7..1c700dad3 100644 --- a/python/core/components/v2/mix_card_ocr/component.py +++ b/python/core/components/v2/mix_card_ocr/component.py @@ -63,13 +63,6 @@ class MixCardOCR(Component): "type": "string" }, "description": "待识别文件的文件名" - }, - "file_urls": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "待识别文件的下载URL" } }, "required": ["file_names"] @@ -77,6 +70,7 @@ class MixCardOCR(Component): } ] + @HTTPClient.check_param @components_run_trace def run(self, message: Message, timeout: float = None, retry: int = 0) -> Message: @@ -168,22 +162,16 @@ def _check_service_error(request_id: str, data: dict): @components_run_stream_trace def tool_eval(self, file_names: Optional[list] = [], - file_urls: Optional[dict] = {}, **kwargs): """ 对指定文件进行OCR识别。 Args: - name (str): API名称。 - streaming (bool): 是否流式输出。如果为True,则逐个返回识别结果;如果为False,则一次性返回所有识别结果。 + file_names (Optional[List], optional): 要识别的文件名列表。 **kwargs: 其他参数。 Returns: - 如果streaming为False,则返回包含所有识别结果的JSON字符串。 - 如果streaming为True,则逐个返回包含识别结果的字典,每个字典包含以下字段: - type (str): 消息类型,固定为"text"。 - text (str): 识别结果的JSON字符串。 - visible_scope (str): 消息可见范围,可以是"llm"或"user"。 + ComponentOutput: 识别结果。 Raises: InvalidRequestArgumentError: 如果请求格式错误,即文件URL不存在时抛出。 @@ -194,12 +182,10 @@ def tool_eval(self, traceid = kwargs.get("_sys_traceid", "") sys_file_names = file_names - sys_file_urls = file_urls - if not sys_file_names: sys_file_names = kwargs.get("_sys_file_names", []) - if not sys_file_urls: - sys_file_urls = kwargs.get("_sys_file_urls", {}) + + sys_file_urls = kwargs.get("_sys_file_urls", {}) for file_name in sys_file_names: if utils.is_url(file_name): diff --git a/python/core/components/v2/qrcode_ocr/component.py b/python/core/components/v2/qrcode_ocr/component.py index aaf7d79a9..a38c7cc6a 100644 --- a/python/core/components/v2/qrcode_ocr/component.py +++ b/python/core/components/v2/qrcode_ocr/component.py @@ -66,13 +66,6 @@ class QRcodeOCR(Component): "location": { "type": "string", "description": "是否输出二维码/条形码位置信息" - }, - "file_urls": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "待识别文件的URL下载地址" } }, "required": ["file_names"] @@ -164,14 +157,13 @@ def _check_service_error(request_id: str, data: dict): ) @components_run_stream_trace - def tool_eval(self, file_names:Optional[list]=[], location: Optional[str]="false", file_urls:Optional[dict]={}, **kwargs): + def tool_eval(self, file_names:Optional[list]=[], location: Optional[str]="false", **kwargs): """ ToolEval方法,用于执行二维码识别操作。 Args: file_names (list, 可选): 待识别文件的文件名列表。 location (str, 可选): 是否需要返回二维码位置信息,默认为 "false"。 - file_urls (dict, 可选): 待识别文件的URL下载地址字典,格式为 {"filename": "url"}。 Yields: ComponentOutput: 识别结果,包含识别到的二维码信息。 @@ -180,13 +172,10 @@ def tool_eval(self, file_names:Optional[list]=[], location: Optional[str]="false traceid = kwargs.get("_sys_traceid", "") # file_name sys_file_names = file_names - sys_file_urls = file_urls - if not sys_file_names: sys_file_names = kwargs.get("_sys_file_names", []) - if not sys_file_urls: - sys_file_urls = kwargs.get("_sys_file_urls", {}) + sys_file_urls = kwargs.get("_sys_file_urls", {}) for file_name in sys_file_names: if utils.is_url(file_name): diff --git a/python/core/components/v2/tree_mind/component.py b/python/core/components/v2/tree_mind/component.py index 17b81c0b8..d3f56e4c1 100644 --- a/python/core/components/v2/tree_mind/component.py +++ b/python/core/components/v2/tree_mind/component.py @@ -15,6 +15,7 @@ r"""树图工具""" import json +from urllib.parse import urlparse, unquote from typing import Dict, List, Optional, Any from appbuilder.core.message import Message from appbuilder.core._client import HTTPClient @@ -83,6 +84,17 @@ def _post(self, query, **kwargs): img_link = treemind_response.info.downloadInfo.fileInfo.pic return img_link, jump_link + @staticmethod + def get_filename_from_url(url): + """从给定URL中提取文件名""" + parsed_url = urlparse(url) + # 提取路径部分 + path = parsed_url.path + # 从路径中获取文件名 + filename = path.split('/')[-1] + # 解码URL编码的文件名 + return unquote(filename) + @components_run_stream_trace def tool_eval( self, @@ -115,6 +127,7 @@ def tool_eval( img_link_result = self.create_output( type="image", text={ + "filename": self.get_filename_from_url(img_link), "url": img_link }, visible_scope='all', diff --git a/python/tests/component_check.py b/python/tests/component_check.py index 52d919a43..0351512c3 100644 --- a/python/tests/component_check.py +++ b/python/tests/component_check.py @@ -1,10 +1,8 @@ -import os import json +import os import inspect -import time -from jsonschema import validate, ValidationError, SchemaError +from jsonschema import validate from pydantic import BaseModel -from typing import Generator from appbuilder.utils.func_utils import Singleton from appbuilder.tests.component_schemas import type_to_json_schemas from appbuilder.utils.json_schema_to_model import json_schema_to_pydantic_model @@ -40,12 +38,15 @@ def register_rule(self, rule_name: str, rule_obj: RuleBase): def remove_rule(self, rule_name: str): del self.rules[rule_name] - def notify(self, component_cls) -> tuple[bool, list]: + def notify(self, component_cls, component_case) -> tuple[bool, list]: check_pass = True check_details = {} reasons = [] for rule_name, rule_obj in self.rules.items(): - res = rule_obj.check(component_cls) + if rule_name == "ToolEvalOutputJsonRule": + res = rule_obj.check(component_cls, component_case) + else: + res = rule_obj.check(component_cls) check_details[rule_name] = res if res.check_result == False: check_pass = False @@ -63,53 +64,40 @@ class ManifestValidRule(RuleBase): def __init__(self, **kwargs): super().__init__() self.rule_name = "ManifestValidRule" - self.component_tool_eval_cases = kwargs.get("component_tool_eval_cases", {}) - def check(self, component_cls) -> CheckInfo: + def check(self, component_obj) -> CheckInfo: check_pass_flag = True invalid_details = [] - component_cls_name = component_cls.__name__ - if component_cls_name not in self.component_tool_eval_cases: - invalid_details.append("{} 没有添加测试case到 component_tool_eval_cases 中".format(component_cls_name)) - else: - component_case = self.component_tool_eval_cases[component_cls_name]() - envs = component_case.envs() - os.environ.update(envs) - init_args = component_case.init_args() - try: - component_obj = component_cls(**init_args) - if not hasattr(component_obj, "manifests"): - raise ValueError("No manifests found") - manifests = component_obj.manifests - # NOTE(暂时检查manifest中的第一个mainfest) - if not manifests or len(manifests) == 0: - raise ValueError("No manifests found") - manifest = manifests[0] - tool_name = manifest['name'] - tool_desc = manifest['description'] - schema = manifest["parameters"] - schema["title"] = tool_name - # 第一步,将json schema转换为pydantic模型 - pydantic_model = json_schema_to_pydantic_model(schema, tool_name) - check_to_json = pydantic_model.schema_json() - json_to_dict = json.loads(check_to_json) - - if "properties" in schema: - properties = schema["properties"] - for key, value in properties.items(): - if "type" not in value: - invalid_details.append("\'type' must be in properties item: {}".format(key)) - if "description" not in value: - invalid_details.append("\'description' must be in properties item: {}".format(key)) - - except Exception as e: - print(e) - check_pass_flag = False - invalid_details.append(str(e)) - - for env in envs: - os.environ.pop(env) + try: + if not hasattr(component_obj, "manifests"): + raise ValueError("No manifests found") + manifests = component_obj.manifests + # NOTE(暂时检查manifest中的第一个mainfest) + if not manifests or len(manifests) == 0: + raise ValueError("No manifests found") + manifest = manifests[0] + tool_name = manifest['name'] + tool_desc = manifest['description'] + schema = manifest["parameters"] + schema["title"] = tool_name + # 第一步,将json schema转换为pydantic模型 + pydantic_model = json_schema_to_pydantic_model(schema, tool_name) + check_to_json = pydantic_model.schema_json() + json_to_dict = json.loads(check_to_json) + + if "properties" in schema: + properties = schema["properties"] + for key, value in properties.items(): + if "type" not in value: + invalid_details.append("\'type' must be in properties item: {}".format(key)) + if "description" not in value: + invalid_details.append("\'description' must be in properties item: {}".format(key)) + + except Exception as e: + print(e) + check_pass_flag = False + invalid_details.append(str(e)) if len(invalid_details) > 0: check_pass_flag = False @@ -137,14 +125,14 @@ def __init__(self): self.rule_name = "MainfestMatchToolEvalRule" - def check(self, component_cls) -> CheckInfo: + def check(self, component_obj) -> CheckInfo: check_pass_flag = True invalid_details = [] try: - if not hasattr(component_cls, "manifests"): + if not hasattr(component_obj, "manifests"): raise ValueError("No manifests found") - manifests = component_cls.manifests + manifests = component_obj.manifests # NOTE(暂时检查manifest中的第一个mainfest) if not manifests or len(manifests) == 0: raise ValueError("No manifests found") @@ -158,7 +146,7 @@ def check(self, component_cls) -> CheckInfo: # 交互检查 tool_eval_input_params = [] print("required_params: {}".format(manifest_var)) - signature = inspect.signature(component_cls.tool_eval) + signature = inspect.signature(component_obj.tool_eval) ileagal_params = [] for param_name, param in signature.parameters.items(): if param_name == 'kwargs' or param_name == 'args' or param_name == 'self': @@ -193,10 +181,6 @@ def check(self, component_cls) -> CheckInfo: check_detail=",".join(invalid_details)) - - - - class ToolEvalInputNameRule(RuleBase): """ 检查tool_eval的输入参数中,是否包含系统保留的输入名称 @@ -222,10 +206,15 @@ def __init__(self): "_sys_custom_variables", "_sys_thought_model_config", "_sys_rag_model_config", + "_sys_parent_span_id", + "_sys_span_id", + "_sys_memory", + "_sys_code_execution_endpoint", + "_sys_session_id" ] - def check(self, component_cls) -> CheckInfo: - tool_eval_signature = inspect.signature(component_cls.__init__) + def check(self, component_obj) -> CheckInfo: + tool_eval_signature = inspect.signature(component_obj.tool_eval) params = tool_eval_signature.parameters invalid_details = [] check_pass_flag = True @@ -250,7 +239,6 @@ class ToolEvalOutputJsonRule(RuleBase): def __init__(self, **kwargs): super().__init__() self.rule_name = 'ToolEvalOutputJsonRule' - self.component_tool_eval_cases = kwargs.get("component_tool_eval_cases") def _check_pre_format(self, outputs): invalid_details = [] @@ -351,42 +339,26 @@ def _check_text_and_code(self, component_case, output_dict): else: return [] - def check(self, component_cls) -> CheckInfo: + def check(self, component_obj, component_case) -> CheckInfo: invalid_details = [] - component_cls_name = component_cls.__name__ - if component_cls_name not in self.component_tool_eval_cases: - invalid_details.append("{} 没有添加测试case到 component_tool_eval_cases 中".format(component_cls_name)) - else: - component_case = self.component_tool_eval_cases[component_cls_name]() - - envs = {} - if hasattr(component_case, "envs"): - envs = component_case.envs() - os.environ.update(envs) - - input_dict = component_case.inputs() - init_args = component_case.init_args() - component_obj = component_cls(**init_args) - output_json_schemas = component_case.schemas() - - try: - stream_output_dict = {"text": "", "oral_text":"", "code": ""} - stream_outputs = component_obj.tool_eval(**input_dict) - for stream_output in stream_outputs: - iter_invalid_detail = self._check_jsonschema(stream_output.model_dump(), output_json_schemas) - invalid_details.extend(iter_invalid_detail) - iter_output_dict = self._gather_iter_outputs(stream_output) - stream_output_dict["text"] += iter_output_dict["text"] - stream_output_dict["oral_text"] += iter_output_dict["oral_text"] - stream_output_dict["code"] += iter_output_dict["code"] - if len(invalid_details) == 0: - invalid_details.extend(self._check_text_and_code(component_case, stream_output_dict)) - except Exception as e: - invalid_details.append("ToolEval执行失败: {}".format(e)) - - for env in envs: - os.environ.pop(env) + input_dict = component_case.inputs() + output_json_schemas = component_case.schemas() + + try: + stream_output_dict = {"text": "", "oral_text":"", "code": ""} + stream_outputs = component_obj.tool_eval(**input_dict) + for stream_output in stream_outputs: + iter_invalid_detail = self._check_jsonschema(stream_output.model_dump(), output_json_schemas) + invalid_details.extend(iter_invalid_detail) + iter_output_dict = self._gather_iter_outputs(stream_output) + stream_output_dict["text"] += iter_output_dict["text"] + stream_output_dict["oral_text"] += iter_output_dict["oral_text"] + stream_output_dict["code"] += iter_output_dict["code"] + if len(invalid_details) == 0: + invalid_details.extend(self._check_text_and_code(component_case, stream_output_dict)) + except Exception as e: + invalid_details.append("ToolEval执行失败: {}".format(e)) if len(invalid_details) > 0: return CheckInfo( @@ -400,6 +372,91 @@ def check(self, component_cls) -> CheckInfo: check_detail="") -def register_component_check_rule(rule_name: str, rule_cls: RuleBase, init_args={}): +def register_component_check_rule(rule_name: str, rule_cls: RuleBase): component_checker = ComponentCheckBase() - component_checker.register_rule(rule_name, rule_cls(**init_args)) \ No newline at end of file + component_checker.register_rule(rule_name, rule_cls()) + + +def check_component_with_retry(component_import_res_tuple): + """ + 使用重试机制检查组件。测试用例失败后会重试两次。 + + Args: + component_import_res_tuple (tuple): 包含组件和导入结果的元组。 + + Returns: + list: 包含错误信息的数据列表。 + + """ + component, import_res, component_case_cls = component_import_res_tuple + component_check_base = ComponentCheckBase() + if inspect.isclass(component): + component_name = component.__name__ + else: + component_name = component + error_data = [] + max_retries = 2 # 设置最大重试次数 + attempts = 0 + + while attempts <= max_retries: + if import_res["import_error"] != "": + error_data.append({"Component Name": component_name, "Error Message": import_res["import_error"]}) + print("组件名称:{} 错误信息:{}".format(component_name, import_res["import_error"])) + break + + component_case = component_case_cls() + envs = component_case.envs() + os.environ.update(envs) + component_cls = import_res["obj"] + component_obj = component_cls(**component_case.init_args()) + + try: + # 此处的self.component_check_base.notify需要根据实际情况修改 + pass_check, reasons = component_check_base.notify(component_obj, component_case) # 示例修改 + reasons = list(set(reasons)) + if not pass_check: + error_data.append({"Component Name": component_name, "Error Message": ", ".join(reasons)}) + print("组件名称:{} 错误信息:{}".format(component_name, ", ".join(reasons))) + # 如果检查失败,增加尝试次数并重试 + attempts += 1 + if attempts <= max_retries: + print("组件名称:{} 将重试,当前尝试次数:{}".format(component_name, attempts)) + continue + # 如果检查通过,则退出循环 + break + except Exception as e: + error_data.append({"Component Name": component_name, "Error Message": str(e)}) + print("组件名称:{} 错误信息:{}".format(component_name, str(e))) + # 如果发生异常,增加尝试次数并重试 + attempts += 1 + if attempts <= max_retries: + print("组件名称:{} 将重试,当前尝试次数:{}".format(component_name, attempts)) + continue + + finally: + for env in envs: + os.environ.pop(env) + + return error_data + +def write_error_data(txt_file_path, error_df, error_stats): + """将组件错误信息写入文件 + + Args: + error_df (Union[pd.DataFrame, None]): 错误信息表格 + error_stats (dict): 错误统计信息 + """ + with open(txt_file_path, 'w') as file: + file.write("Component Name\tError Message\n") + for _, row in error_df.iterrows(): + file.write(f"{row['Component Name']}\t{row['Error Message']}\n") + file.write("\n错误统计信息:\n") + for error, count in error_stats.items(): + file.write(f"错误信息: {error}, 出现次数: {count}\n") + print(f"\n错误信息已写入: {txt_file_path}") + + +register_component_check_rule("ManifestValidRule", ManifestValidRule) +register_component_check_rule("MainfestMatchToolEvalRule", MainfestMatchToolEvalRule) +register_component_check_rule("ToolEvalInputNameRule", ToolEvalInputNameRule) +register_component_check_rule("ToolEvalOutputJsonRule", ToolEvalOutputJsonRule) \ No newline at end of file diff --git a/python/tests/component_tool_eval_cases.py b/python/tests/component_tool_eval_cases.py index 9cea3b67d..19f999cc3 100644 --- a/python/tests/component_tool_eval_cases.py +++ b/python/tests/component_tool_eval_cases.py @@ -106,7 +106,7 @@ def inputs(self): "e74ab057ce26d50e966dc31ff083e6a9c41b" return { "file_names": ["text"], - "file_urls": {"text": image_url} + "_sys_file_urls": {"text": image_url} } def schemas(self): @@ -151,7 +151,7 @@ def inputs(self): "F677f93445fb65157bee11cd492ce213d5c56e7a41827e45ce7e32b083d195c8b" return { "file_names": ["text"], - "file_urls": {"text": image_url} + "_sys_file_urls": {"text": image_url} } def schemas(self): @@ -168,7 +168,7 @@ def inputs(self): "1865e4393da5a3515e90d72d81ef18296bd29598") return { "file_names": ["test"], - "file_urls": {"test": image_url} + "_sys_file_urls": {"test": image_url} } def schemas(self): diff --git a/python/tests/test_all_components.py b/python/tests/test_all_components.py index 00dc956cd..728e7eb34 100644 --- a/python/tests/test_all_components.py +++ b/python/tests/test_all_components.py @@ -4,86 +4,12 @@ import pandas as pd import unittest import os -from appbuilder.tests.component_check import ComponentCheckBase -from appbuilder.tests.component_check import register_component_check_rule -from appbuilder.tests.component_check import ManifestValidRule, MainfestMatchToolEvalRule, ToolEvalInputNameRule, ToolEvalOutputJsonRule + from appbuilder.core._exception import AppbuilderBuildexException -from component_collector import get_all_components, get_v2_components, get_component_white_list from component_tool_eval_cases import component_tool_eval_cases +from component_collector import get_all_components, get_v2_components, get_component_white_list +from component_check import check_component_with_retry, write_error_data -register_component_check_rule("ManifestValidRule", ManifestValidRule, \ - {"component_tool_eval_cases": component_tool_eval_cases}) -register_component_check_rule("MainfestMatchToolEvalRule", MainfestMatchToolEvalRule, {}) -register_component_check_rule("ToolEvalInputNameRule", ToolEvalInputNameRule, {}) -register_component_check_rule("ToolEvalOutputJsonRule", ToolEvalOutputJsonRule, \ - {"component_tool_eval_cases": component_tool_eval_cases}) - - -def check_component_with_retry(component_import_res_tuple): - """ - 使用重试机制检查组件。测试用例失败后会重试两次。 - - Args: - component_import_res_tuple (tuple): 包含组件和导入结果的元组。 - - Returns: - list: 包含错误信息的数据列表。 - - """ - component, import_res = component_import_res_tuple - component_check_base = ComponentCheckBase() - - error_data = [] - max_retries = 2 # 设置最大重试次数 - attempts = 0 - - while attempts <= max_retries: - if import_res["import_error"] != "": - error_data.append({"Component Name": component, "Error Message": import_res["import_error"]}) - print("组件名称:{} 错误信息:{}".format(component, import_res["import_error"])) - break - - component_obj = import_res["obj"] - try: - # 此处的self.component_check_base.notify需要根据实际情况修改 - pass_check, reasons = component_check_base.notify(component_obj) # 示例修改 - reasons = list(set(reasons)) - if not pass_check: - error_data.append({"Component Name": component, "Error Message": ", ".join(reasons)}) - print("组件名称:{} 错误信息:{}".format(component, ", ".join(reasons))) - # 如果检查失败,增加尝试次数并重试 - attempts += 1 - if attempts <= max_retries: - print("组件名称:{} 将重试,当前尝试次数:{}".format(component, attempts)) - continue - # 如果检查通过,则退出循环 - break - except Exception as e: - error_data.append({"Component Name": component, "Error Message": str(e)}) - print("组件名称:{} 错误信息:{}".format(component, str(e))) - # 如果发生异常,增加尝试次数并重试 - attempts += 1 - if attempts <= max_retries: - print("组件名称:{} 将重试,当前尝试次数:{}".format(component, attempts)) - continue - - return error_data - -def write_error_data(txt_file_path, error_df, error_stats): - """将组件错误信息写入文件 - - Args: - error_df (Union[pd.DataFrame, None]): 错误信息表格 - error_stats (dict): 错误统计信息 - """ - with open(txt_file_path, 'w') as file: - file.write("Component Name\tError Message\n") - for _, row in error_df.iterrows(): - file.write(f"{row['Component Name']}\t{row['Error Message']}\n") - file.write("\n错误统计信息:\n") - for error, count in error_stats.items(): - file.write(f"错误信息: {error}, 出现次数: {count}\n") - print(f"\n错误信息已写入: {txt_file_path}") @unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestComponentManifestsAndToolEval(unittest.TestCase): @@ -110,7 +36,7 @@ def setUp(self) -> None: self.v2_components = get_v2_components() self.whitelist_components = get_component_white_list() - def _test_component(self, components, whitelist_components, txt_file_path): + def _test_component(self, components, component_cases, whitelist_components, txt_file_path): """测试所有组件的manifests和tool_eval入参 Args: 无 @@ -122,7 +48,16 @@ def _test_component(self, components, whitelist_components, txt_file_path): with Pool(processes=os.cpu_count()) as pool: # 使用pool.map来执行多进程 - results = pool.map(check_component_with_retry, components.items()) + args = [] + for component, import_res in components.items(): + if component not in component_cases: + error_data.append({"Component Name": component, "Error Message": "{} 没有添加测试case到 \ + component_tool_eval_cases 中".format(component)}) + continue + else: + args.append((component, import_res, component_tool_eval_cases[component])) + + results = pool.map(check_component_with_retry, args) # 合并每个进程返回的错误数据 for result in results: @@ -154,13 +89,13 @@ def _test_component(self, components, whitelist_components, txt_file_path): else: print("\n所有组件测试通过,无错误信息。") - def test_all_components(self): + def _test_all_components(self): """测试旧版本组件""" - self._test_component(self.all_components, self.whitelist_components, 'components_error_info.txt') + self._test_component(self.all_components, [], self.whitelist_components, 'components_error_info.txt') def test_v2_components(self): """测试v2版本组件""" - self._test_component(self.v2_components, [], 'v2_components_error_info.txt') + self._test_component(self.v2_components, component_tool_eval_cases, [], 'v2_components_error_info.txt') if __name__ == '__main__': diff --git a/python/tests/test_base_component.py b/python/tests/test_base_component.py index 94d5ff4e2..a560cf78e 100644 --- a/python/tests/test_base_component.py +++ b/python/tests/test_base_component.py @@ -32,6 +32,7 @@ def test_valid_output_with_dict(self): output8 = self.component.create_output(type="audio", text={"filename": "file.mp3", "url": "http://www.baidu.com"}) output9 = self.component.create_output(type="plan", text={"detail": "hello", "steps":[{"name": "1", "arguments": {"query": "a", "chat_history": "world"}}]}) output10 = self.component.create_output(type="function_call", text={"thought": "hello", "name": "AppBuilder", "arguments": {"query": "a", "chat_history": "world"}}) + output11 = self.component.create_output(type="references", text={"type": "engine", "doc_id": "1", "content": "hello, world", "title": "Have a nice day", "source": "bing", "extra": {"key": "value"}}) self.assertIsInstance(output1, ComponentOutput) self.assertIsInstance(output2, ComponentOutput) self.assertIsInstance(output3, ComponentOutput) @@ -42,6 +43,8 @@ def test_valid_output_with_dict(self): self.assertIsInstance(output8, ComponentOutput) self.assertIsInstance(output9, ComponentOutput) self.assertIsInstance(output10, ComponentOutput) + self.assertIsInstance(output11, ComponentOutput) + self.assertEqual(output11.content[0].text.extra["key"], "value") def test_valid_output_type_with_same_key(self): output1 = self.component.create_output(type="urls", text={"url": "http://www.baidu.com"}) diff --git a/python/tests/test_v2_handwrite_ocr.py b/python/tests/test_v2_handwrite_ocr.py index dfc8a8396..059481f4e 100644 --- a/python/tests/test_v2_handwrite_ocr.py +++ b/python/tests/test_v2_handwrite_ocr.py @@ -80,7 +80,7 @@ def test_tool_eval(self): next(result) result=self.handwrite_ocr.tool_eval( file_names=['test'], - file_urls={'test':self.image_url} + _sys_file_urls={'test':self.image_url} ) res=next(result) print(res) diff --git a/python/tests/test_v2_mix_card_ocr.py b/python/tests/test_v2_mix_card_ocr.py index 361014136..1ba67310b 100644 --- a/python/tests/test_v2_mix_card_ocr.py +++ b/python/tests/test_v2_mix_card_ocr.py @@ -82,10 +82,8 @@ def test_tool_eval(self): with self.assertRaises(InvalidRequestArgumentError): next(result) result=self.mix_card_ocr.tool_eval( - name='name', - streaming=True, file_names=['test'], - file_urls={'test':self.image_url} + _sys_file_urls={'test':self.image_url} ) res=next(result) print("res: {}".format(res)) diff --git a/python/tests/test_v2_qrcode_ocr.py b/python/tests/test_v2_qrcode_ocr.py index ae8198d94..d1a4a845a 100644 --- a/python/tests/test_v2_qrcode_ocr.py +++ b/python/tests/test_v2_qrcode_ocr.py @@ -145,7 +145,7 @@ def test_tool_eval(self): print(msg) result=self.qrcode_ocr.tool_eval( file_names=['test'], - file_urls={'test':image_url} + _sys_file_urls={'test':image_url} ) res=next(result) print(res) From 55c8f6d30f070eb99216b7014a60e5dc52730178 Mon Sep 17 00:00:00 2001 From: peiwenYe <963623403@qq.com> Date: Mon, 23 Dec 2024 17:29:24 +0800 Subject: [PATCH 84/85] =?UTF-8?q?=E4=BF=AE=E6=94=B9references=E5=AF=B9?= =?UTF-8?q?=E5=BA=94=E7=9A=84=E8=BE=93=E5=87=BAjsonschema=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=20(#682)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: yepeiwen01 --- python/tests/component_schemas.py | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/python/tests/component_schemas.py b/python/tests/component_schemas.py index 5f70bdaa2..024569280 100644 --- a/python/tests/component_schemas.py +++ b/python/tests/component_schemas.py @@ -148,38 +148,23 @@ "type": { "type": "string" }, - "resource_type": { - "type": "string" - }, "doc_id": { "type": "string" }, - "icon": { - "type": "string", - }, - "site_name": { - "type": "string" - }, "content": { "type": "string" }, "title": { "type": "string" }, - "mock_id": { + "source": { "type": "string" }, - "from": { - "type": "string" - }, - "image_url": { - "type": "string" - }, - "video_url": { - "type": "string" + "extra": { + "type": "object" } }, - "required": ["type", "resource_type", "doc_id", "icon", "site_name", "content", "title", "mock_id", "from", "image_url", "video_url"] + "required": ["type", "doc_id", "content", "title", "source"] } image_schema = copy.deepcopy(base_item_schema) From 35333a20d82fca441e66cd1020ddae9bcbbd0023 Mon Sep 17 00:00:00 2001 From: C9luster <138663536+C9luster@users.noreply.github.com> Date: Mon, 23 Dec 2024 19:42:30 +0800 Subject: [PATCH 85/85] =?UTF-8?q?=E6=B7=BB=E5=8A=A0chainlit=20Markdown?= =?UTF-8?q?=E6=96=87=E4=BB=B6=20(#683)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: yinjiaqi --- .gitignore | 2 +- python/utils/chainlit.md | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 python/utils/chainlit.md diff --git a/.gitignore b/.gitignore index f8d30f31b..f38f8e833 100644 --- a/.gitignore +++ b/.gitignore @@ -132,7 +132,7 @@ FETCH_HEAD # chainlit .chainlit # chainlit.md is generated by chainlit if not found -chainlit.md +# chainlit.md # sqlite *.db diff --git a/python/utils/chainlit.md b/python/utils/chainlit.md new file mode 100644 index 000000000..d9ebfc4d8 --- /dev/null +++ b/python/utils/chainlit.md @@ -0,0 +1,24 @@ +# 欢迎使用AppBuilder-SDK🚀 +您好,欢迎您体验AppBuilder-SDK的Chainlit可视化服务化功能。 + +## 什么是AppBuilder-SDK👋 +百度智能云千帆AppBuilder-SDK是[百度智能云千帆AppBuilder](https://appbuilder.cloud.baidu.com/)面向AI原生应用开发者提供的一站式开发平台的客户端SDK。 + +我们提供自底向上的:基础组件、流程编排、端到端应用 三类功能。使用百度智能云千帆AppBuilder-SDK,你可以: + +- 配合百度智能云千帆AppBuilder平台[网页端](https://console.bce.baidu.com/ai_apaas/app),分钟级在本地搭建包含百度工业实践的`端到端的AI原生应用` +- 配合 `基础组件` & `流程编排`,积木式搭建个性化的Assistant + FunctionCall应用 +- 提供 `API调用` & `交互式窗口` 两种服务化部署方式,支持快速上云,平滑嵌入到你的产品中 + +## 如何使用AppBuilder-SDK & Chainlit可视化功能💻 +当前SDK中的`AgentRuntime`模块基于Chainlit实现了基础的可视化功能,支持AppBuilderClient + 能力组件实现可视化交互。 +- `chainlit_demo`接口支持基础组件的简单交互 +- `chainlit_agent`接口支持AppBuilderClient的进阶交互,提供新建会话和上传文件的功能 + +如果对于可视化有更多需求,可以参考AppBuilder SDK中的代码,基于Chainlit进行二次开发。 + +## 链接 🔗 +- [AppBuilder官网](https://appbuilder.cloud.baidu.com/) +- [AppBuilder-SDK开源代码仓库](https://github.com/baidubce/app-builder) +- [AppBuilder文档中心](https://cloud.baidu.com/doc/AppBuilder/index.html) +- [Chainlit文档中心](https://docs.chainlit.io/get-started/overview) \ No newline at end of file