diff --git a/python/__init__.py b/python/__init__.py index 1920fe27..f3143143 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -13,7 +13,7 @@ # limitations under the License. -__version__ = '1.1.0' +__version__ = '1.1.2' import os import sys diff --git a/python/core/console/appbuilder_client/appbuilder_client.py b/python/core/console/appbuilder_client/appbuilder_client.py index 58fbcc3f..ba1d9fa5 100644 --- a/python/core/console/appbuilder_client/appbuilder_client.py +++ b/python/core/console/appbuilder_client/appbuilder_client.py @@ -15,7 +15,7 @@ """AppBuilderClient组件""" import os import json -from typing import Optional, Union +from typing import Optional, Union, Any from appbuilder.core.component import Message, Component from appbuilder.core.manifest.models import Manifest from appbuilder.core.console.appbuilder_client import data_class @@ -318,6 +318,8 @@ def run( end_user_id: str = None, action: data_class.Action = None, mcp_authorization: list[dict] = None, + parameters: dict[str, Any] = None, + custom_metadata: data_class.CustomMetadata = None, **kwargs, ) -> Message: r"""运行智能体应用 @@ -333,6 +335,12 @@ def run( end_user_id (str): 用户ID,用于区分不同用户 action (data_class.Action): 对话时要进行的特殊操作。如回复工作流agent中“信息收集节点“的消息。 mcp_authorization (list[dict]): mcp鉴权配置:目前仅适配百度网盘 + parameters: 用户在工作流Agent中自定义添加的参数,对应画布中开始节点用户新增的参数。例如: + "parameters": + {"custom_variable1": "abc", + "custom_variable2": 1.23 + } + custom_metadata: 自定义角色指令,适用于自主规划agent kwargs: 其他参数 Returns: @@ -373,6 +381,8 @@ def run( end_user_id=end_user_id, action=action, mcp_authorization=mcp_authorization, + parameters=parameters, + custom_metadata=custom_metadata, ) headers = self.http_client.auth_header_v2(mcp_context=self._mcp_context) diff --git a/python/core/console/appbuilder_client/async_appbuilder_client.py b/python/core/console/appbuilder_client/async_appbuilder_client.py index ba5e89b5..26874994 100644 --- a/python/core/console/appbuilder_client/async_appbuilder_client.py +++ b/python/core/console/appbuilder_client/async_appbuilder_client.py @@ -13,7 +13,7 @@ # limitations under the License. import json import os -from typing import Union +from typing import Union, Any from aiohttp import FormData from appbuilder.core.component import Message, Component from appbuilder.core.console.appbuilder_client import data_class, AppBuilderClient @@ -69,6 +69,8 @@ async def run( end_user_id: str = None, action: data_class.Action = None, mcp_authorization: list[dict] = None, + parameters: dict[str, Any] = None, + custom_metadata: data_class.CustomMetadata = None, **kwargs, ) -> Message: r"""异步运行智能体应用 @@ -84,6 +86,12 @@ async def run( end_user_id (str): 用户ID,用于区分不同用户 action (data_class.Action): 对话时要进行的特殊操作。如回复工作流agent中“信息收集节点“的消息。 mcp_authorization (list[dict]): mcp鉴权配置:目前仅适配百度网盘 + parameters: 用户在工作流Agent中自定义添加的参数,对应画布中开始节点用户新增的参数。例如: + "parameters": + {"custom_variable1": "abc", + "custom_variable2": 1.23 + } + custom_metadata: 自定义角色指令,适用于自主规划agent kwargs: 其他参数 Returns: @@ -124,6 +132,8 @@ async def run( end_user_id=end_user_id, action=action, mcp_authorization=mcp_authorization, + parameters=parameters, + conversation=custom_metadata, ) headers = self.http_client.auth_header_v2(mcp_context=self._mcp_context) diff --git a/python/core/console/appbuilder_client/data_class.py b/python/core/console/appbuilder_client/data_class.py index ab53e181..76557e0f 100644 --- a/python/core/console/appbuilder_client/data_class.py +++ b/python/core/console/appbuilder_client/data_class.py @@ -14,7 +14,7 @@ from pydantic import BaseModel from pydantic import Field -from typing import Union +from typing import Union, Any from typing import Optional from appbuilder.core.manifest.models import Manifest @@ -40,7 +40,8 @@ def ToAppBuilderTool(tool): if "type" in tool and tool["type"]: return Tool(**tool), False if hasattr(tool, 'inputSchema') and hasattr(tool, 'inputSchema'): - return Tool(type="function", function=Function(name=tool.name, description=tool.description, parameters=tool.inputSchema)), True + return Tool(type="function", + function=Function(name=tool.name, description=tool.description, parameters=tool.inputSchema)), True else: return tool, False @@ -93,6 +94,10 @@ class ActionParameters(BaseModel): ..., description="要回复的'信息收集节点'中断事件") +class CustomMetadata(BaseModel): + override_role_instruction: str = Field(..., description="自定义角色指令,适用于自主规划agent") + + class Action(BaseModel): action_type: str = Field(..., description="action类型,目前可用值'resume', 用于回复信息收集节点的消息") @@ -134,6 +139,8 @@ class AppBuilderClientRequest(BaseModel): end_user_id: Optional[str] = None action: Optional[Action] = None mcp_authorization: Optional[list[dict]] = None + parameters: Optional[dict[str, Any]] = None + custom_metadata: Optional[CustomMetadata] = None class Usage(BaseModel): @@ -354,7 +361,8 @@ class AppBuilderClientAppListRequest(BaseModel): 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 以获取列表的下一页数据。") + default="", + description="用于分页的游标。after 是一个应用的id,它定义了在列表中的位置。例如,如果你发出一个列表请求并收到 10个对象,以 app_id_123 结束,那么你后续的调用可以包含 after=app_id_123 以获取列表的下一页数据。") before: str = Field(default="", description="用于分页的游标。与after相反,填写它将获取前一页数据") @@ -380,7 +388,8 @@ 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 来获取列表的下一页数据") + default=None, + description="用于分页的游标。marker 是应用的id,它定义了在列表中的位置。例如,如果你发出一个列表请求并收到 10个对象,以 app_id_123 开始,那么可以使用 marker=app_id_123 来获取列表的下一页数据") class DescribeAppsResponse(BaseModel): diff --git a/python/tests/test_appbuilder_client_custom_metadata.py b/python/tests/test_appbuilder_client_custom_metadata.py new file mode 100644 index 00000000..bb0f1170 --- /dev/null +++ b/python/tests/test_appbuilder_client_custom_metadata.py @@ -0,0 +1,104 @@ +# Copyright (c) 2025 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.data_class import CustomMetadata + + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") +class TestAppBuilderClientFeedback(unittest.TestCase): + def setUp(self): + """ + 设置环境变量。 + + Args: + 无参数,默认值为空。 + + Returns: + 无返回值,方法中执行了环境变量的赋值操作。 + """ + self.app_id = "a3654cd9-378a-4b46-a33b-2259ca3b304e" + + def test_appbuilder_custom_metadata(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=False, custom_metadata=CustomMetadata( + override_role_instruction="# 角色任务\n" + + "作为高情商大师,你的主要任务是根据提问,做出最佳的建议。\n" + + "\n" + + "# 工具能力\n" + + "\n" + + "无工具集提供\n" + + "\n" + + "# 要求与限制\n" + + "\n" + + "1. 输出内容的风格为幽默\n" + + "2.输出的字数限制为100字以内", + )) + + print(msg.content.answer) + + def test_appbuilder_custom_metadata_stream(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, custom_metadata=CustomMetadata( + override_role_instruction="# 角色任务\n" + + "作为高情商大师,你的主要任务是根据提问,做出最佳的建议。\n" + + "\n" + + "# 工具能力\n" + + "\n" + + "无工具集提供\n" + + "\n" + + "# 要求与限制\n" + + "\n" + + "1. 输出内容的风格为幽默\n" + + "2.输出的字数限制为100字以内", + )) + for content in msg.content: + print(content.answer) + + +if __name__ == "__main__": + unittest.main() diff --git a/python/tests/test_appbuilder_client_parameters.py b/python/tests/test_appbuilder_client_parameters.py new file mode 100644 index 00000000..b5f640c0 --- /dev/null +++ b/python/tests/test_appbuilder_client_parameters.py @@ -0,0 +1,78 @@ +# Copyright (c) 2025 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 + + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") +class TestAppBuilderClientFeedback(unittest.TestCase): + def setUp(self): + """ + 设置环境变量。 + + Args: + 无参数,默认值为空。 + + Returns: + 无返回值,方法中执行了环境变量的赋值操作。 + """ + self.app_id = "2313e282-baa6-4db6-92dd-a21e99cfd59e" + + def test_appbuilder_parameters(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=False, parameters={"city": "北京"}) + print(msg.content.answer) + + def test_appbuilder_parameters_stream(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, parameters={"city": "北京"}) + + for content in msg.content: + print(content.answer) + + +if __name__ == "__main__": + unittest.main() diff --git a/python/tests/test_async_appbuilder_client_custom_metadata.py b/python/tests/test_async_appbuilder_client_custom_metadata.py new file mode 100644 index 00000000..26b367f0 --- /dev/null +++ b/python/tests/test_async_appbuilder_client_custom_metadata.py @@ -0,0 +1,114 @@ +# Copyright (c) 2025 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 asyncio +import os +import unittest + +import appbuilder +from appbuilder.core.console.appbuilder_client.data_class import CustomMetadata + + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") +class TestAppBuilderClientFeedback(unittest.TestCase): + def setUp(self): + """ + 设置环境变量。 + + Args: + 无参数,默认值为空。 + + Returns: + 无返回值,方法中执行了环境变量的赋值操作。 + """ + self.app_id = "a3654cd9-378a-4b46-a33b-2259ca3b304e" + + def test_async_appbuilder_custom_metadata(self): + # 如果app_id为空,则跳过单测执行, 避免单测因配置无效而失败 + """ + 如果app_id为空,则跳过单测执行, 避免单测因配置无效而失败 + + Args: + self (unittest.TestCase): unittest的TestCase对象 + + Raises: + None: 如果app_id不为空,则不会引发任何异常 + unittest.SkipTest (optional): 如果app_id为空,则跳过单测执行 + """ + + async def agent_handle(): + if len(self.app_id) == 0: + self.skipTest("self.app_id is empty") + appbuilder.logger.setLoglevel("ERROR") + builder = appbuilder.AsyncAppBuilderClient(self.app_id) + conversation_id = await builder.create_conversation() + msg = await builder.run(conversation_id, "我要回老家相亲", stream=False, custom_metadata=CustomMetadata( + override_role_instruction= "# 角色任务\n" + + "作为高情商大师,你的主要任务是根据提问,做出最佳的建议。\n" + + "\n" + + "# 工具能力\n" + + "\n" + + "无工具集提供\n" + + "\n" + + "# 要求与限制\n" + + "\n" + + "1. 输出内容的风格为幽默\n" + + "2.输出的字数限制为100字以内", + )) + print(msg.content.answer) + await builder.http_client.session.close() + + asyncio.run(agent_handle()) + + def test_async_appbuilder_custom_metadata_stream(self): + # 如果app_id为空,则跳过单测执行, 避免单测因配置无效而失败 + """ + 如果app_id为空,则跳过单测执行, 避免单测因配置无效而失败 + + Args: + self (unittest.TestCase): unittest的TestCase对象 + + Raises: + None: 如果app_id不为空,则不会引发任何异常 + unittest.SkipTest (optional): 如果app_id为空,则跳过单测执行 + """ + + async def agent_handle(): + if len(self.app_id) == 0: + self.skipTest("self.app_id is empty") + appbuilder.logger.setLoglevel("ERROR") + builder = appbuilder.AsyncAppBuilderClient(self.app_id) + conversation_id = await builder.create_conversation() + msg = await builder.run(conversation_id, "我要回老家相亲", stream=True, custom_metadata=CustomMetadata( + override_role_instruction= "# 角色任务\n" + + "作为高情商大师,你的主要任务是根据提问,做出最佳的建议。\n" + + "\n" + + "# 工具能力\n" + + "\n" + + "无工具集提供\n" + + "\n" + + "# 要求与限制\n" + + "\n" + + "1. 输出内容的风格为幽默\n" + + "2.输出的字数限制为100字以内", + )) + + async for content in msg.content: + print(content.answer) + await builder.http_client.session.close() + + asyncio.run(agent_handle()) + + +if __name__ == "__main__": + unittest.main() diff --git a/python/tests/test_async_appbuilder_client_parameters.py b/python/tests/test_async_appbuilder_client_parameters.py new file mode 100644 index 00000000..1e1a4451 --- /dev/null +++ b/python/tests/test_async_appbuilder_client_parameters.py @@ -0,0 +1,88 @@ +# Copyright (c) 2025 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 asyncio +import unittest +import os +import appbuilder + + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") +class TestAppBuilderClientFeedback(unittest.TestCase): + def setUp(self): + """ + 设置环境变量。 + + Args: + 无参数,默认值为空。 + + Returns: + 无返回值,方法中执行了环境变量的赋值操作。 + """ + self.app_id = "2313e282-baa6-4db6-92dd-a21e99cfd59e" + + def test_async_appbuilder_parameters(self): + # 如果app_id为空,则跳过单测执行, 避免单测因配置无效而失败 + """ + 如果app_id为空,则跳过单测执行, 避免单测因配置无效而失败 + + Args: + self (unittest.TestCase): unittest的TestCase对象 + + Raises: + None: 如果app_id不为空,则不会引发任何异常 + unittest.SkipTest (optional): 如果app_id为空,则跳过单测执行 + """ + + async def agent_handle(): + if len(self.app_id) == 0: + self.skipTest("self.app_id is empty") + appbuilder.logger.setLoglevel("ERROR") + builder = appbuilder.AsyncAppBuilderClient(self.app_id) + conversation_id = await builder.create_conversation() + msg = await builder.run(conversation_id, "国庆长假", stream=False, parameters={"city": "北京"}) + print(msg.content.answer) + await builder.http_client.session.close() + + asyncio.run(agent_handle()) + + def test_async_appbuilder_parameters_stream(self): + # 如果app_id为空,则跳过单测执行, 避免单测因配置无效而失败 + """ + 如果app_id为空,则跳过单测执行, 避免单测因配置无效而失败 + + Args: + self (unittest.TestCase): unittest的TestCase对象 + + Raises: + None: 如果app_id不为空,则不会引发任何异常 + unittest.SkipTest (optional): 如果app_id为空,则跳过单测执行 + """ + + async def agent_handle(): + if len(self.app_id) == 0: + self.skipTest("self.app_id is empty") + appbuilder.logger.setLoglevel("ERROR") + builder = appbuilder.AsyncAppBuilderClient(self.app_id) + conversation_id = await builder.create_conversation() + msg = await builder.run(conversation_id, "元旦节", stream=True, parameters={"city": "北京"}) + + async for content in msg.content: + print(content.answer) + await builder.http_client.session.close() + + asyncio.run(agent_handle()) + + +if __name__ == "__main__": + unittest.main() diff --git a/setup.py b/setup.py index 5f6b0432..9fae532e 100755 --- a/setup.py +++ b/setup.py @@ -55,7 +55,7 @@ setup( name="appbuilder-sdk", # NOTE(chengmo): 修改此版本号时,请注意同时修改 __init__.py 中的 __version__ - version="1.1.0", + version="1.1.2", author="dongdaxiang", author_email="dongdaxiang@baidu.com", packages=packages,