aip-agents-binary 0.6.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of aip-agents-binary might be problematic. Click here for more details.
- aip_agents/__init__.py +65 -0
- aip_agents/__init__.pyi +19 -0
- aip_agents/a2a/__init__.py +19 -0
- aip_agents/a2a/__init__.pyi +3 -0
- aip_agents/a2a/server/__init__.py +10 -0
- aip_agents/a2a/server/__init__.pyi +4 -0
- aip_agents/a2a/server/base_executor.py +1086 -0
- aip_agents/a2a/server/base_executor.pyi +73 -0
- aip_agents/a2a/server/google_adk_executor.py +198 -0
- aip_agents/a2a/server/google_adk_executor.pyi +51 -0
- aip_agents/a2a/server/langflow_executor.py +180 -0
- aip_agents/a2a/server/langflow_executor.pyi +43 -0
- aip_agents/a2a/server/langgraph_executor.py +270 -0
- aip_agents/a2a/server/langgraph_executor.pyi +47 -0
- aip_agents/a2a/types.py +232 -0
- aip_agents/a2a/types.pyi +132 -0
- aip_agents/agent/__init__.py +27 -0
- aip_agents/agent/__init__.pyi +9 -0
- aip_agents/agent/base_agent.py +970 -0
- aip_agents/agent/base_agent.pyi +221 -0
- aip_agents/agent/base_langgraph_agent.py +3037 -0
- aip_agents/agent/base_langgraph_agent.pyi +233 -0
- aip_agents/agent/google_adk_agent.py +926 -0
- aip_agents/agent/google_adk_agent.pyi +141 -0
- aip_agents/agent/google_adk_constants.py +6 -0
- aip_agents/agent/google_adk_constants.pyi +3 -0
- aip_agents/agent/hitl/__init__.py +24 -0
- aip_agents/agent/hitl/__init__.pyi +6 -0
- aip_agents/agent/hitl/config.py +28 -0
- aip_agents/agent/hitl/config.pyi +15 -0
- aip_agents/agent/hitl/langgraph_hitl_mixin.py +515 -0
- aip_agents/agent/hitl/langgraph_hitl_mixin.pyi +42 -0
- aip_agents/agent/hitl/manager.py +532 -0
- aip_agents/agent/hitl/manager.pyi +200 -0
- aip_agents/agent/hitl/models.py +18 -0
- aip_agents/agent/hitl/models.pyi +3 -0
- aip_agents/agent/hitl/prompt/__init__.py +9 -0
- aip_agents/agent/hitl/prompt/__init__.pyi +4 -0
- aip_agents/agent/hitl/prompt/base.py +42 -0
- aip_agents/agent/hitl/prompt/base.pyi +24 -0
- aip_agents/agent/hitl/prompt/deferred.py +73 -0
- aip_agents/agent/hitl/prompt/deferred.pyi +30 -0
- aip_agents/agent/hitl/registry.py +149 -0
- aip_agents/agent/hitl/registry.pyi +101 -0
- aip_agents/agent/interface.py +138 -0
- aip_agents/agent/interface.pyi +81 -0
- aip_agents/agent/interfaces.py +65 -0
- aip_agents/agent/interfaces.pyi +44 -0
- aip_agents/agent/langflow_agent.py +464 -0
- aip_agents/agent/langflow_agent.pyi +133 -0
- aip_agents/agent/langgraph_memory_enhancer_agent.py +767 -0
- aip_agents/agent/langgraph_memory_enhancer_agent.pyi +50 -0
- aip_agents/agent/langgraph_react_agent.py +2856 -0
- aip_agents/agent/langgraph_react_agent.pyi +170 -0
- aip_agents/agent/system_instruction_context.py +34 -0
- aip_agents/agent/system_instruction_context.pyi +13 -0
- aip_agents/clients/__init__.py +10 -0
- aip_agents/clients/__init__.pyi +4 -0
- aip_agents/clients/langflow/__init__.py +10 -0
- aip_agents/clients/langflow/__init__.pyi +4 -0
- aip_agents/clients/langflow/client.py +477 -0
- aip_agents/clients/langflow/client.pyi +140 -0
- aip_agents/clients/langflow/types.py +18 -0
- aip_agents/clients/langflow/types.pyi +7 -0
- aip_agents/constants.py +23 -0
- aip_agents/constants.pyi +7 -0
- aip_agents/credentials/manager.py +132 -0
- aip_agents/examples/__init__.py +5 -0
- aip_agents/examples/__init__.pyi +0 -0
- aip_agents/examples/compare_streaming_client.py +783 -0
- aip_agents/examples/compare_streaming_client.pyi +48 -0
- aip_agents/examples/compare_streaming_server.py +142 -0
- aip_agents/examples/compare_streaming_server.pyi +18 -0
- aip_agents/examples/hello_world_a2a_google_adk_client.py +49 -0
- aip_agents/examples/hello_world_a2a_google_adk_client.pyi +9 -0
- aip_agents/examples/hello_world_a2a_google_adk_client_agent.py +48 -0
- aip_agents/examples/hello_world_a2a_google_adk_client_agent.pyi +9 -0
- aip_agents/examples/hello_world_a2a_google_adk_client_streaming.py +60 -0
- aip_agents/examples/hello_world_a2a_google_adk_client_streaming.pyi +9 -0
- aip_agents/examples/hello_world_a2a_google_adk_server.py +79 -0
- aip_agents/examples/hello_world_a2a_google_adk_server.pyi +15 -0
- aip_agents/examples/hello_world_a2a_langchain_client.py +39 -0
- aip_agents/examples/hello_world_a2a_langchain_client.pyi +5 -0
- aip_agents/examples/hello_world_a2a_langchain_client_agent.py +39 -0
- aip_agents/examples/hello_world_a2a_langchain_client_agent.pyi +5 -0
- aip_agents/examples/hello_world_a2a_langchain_client_lm_invoker.py +37 -0
- aip_agents/examples/hello_world_a2a_langchain_client_lm_invoker.pyi +5 -0
- aip_agents/examples/hello_world_a2a_langchain_client_streaming.py +41 -0
- aip_agents/examples/hello_world_a2a_langchain_client_streaming.pyi +5 -0
- aip_agents/examples/hello_world_a2a_langchain_reference_client_streaming.py +60 -0
- aip_agents/examples/hello_world_a2a_langchain_reference_client_streaming.pyi +5 -0
- aip_agents/examples/hello_world_a2a_langchain_reference_server.py +105 -0
- aip_agents/examples/hello_world_a2a_langchain_reference_server.pyi +15 -0
- aip_agents/examples/hello_world_a2a_langchain_server.py +79 -0
- aip_agents/examples/hello_world_a2a_langchain_server.pyi +15 -0
- aip_agents/examples/hello_world_a2a_langchain_server_lm_invoker.py +78 -0
- aip_agents/examples/hello_world_a2a_langchain_server_lm_invoker.pyi +15 -0
- aip_agents/examples/hello_world_a2a_langflow_client.py +83 -0
- aip_agents/examples/hello_world_a2a_langflow_client.pyi +9 -0
- aip_agents/examples/hello_world_a2a_langflow_server.py +82 -0
- aip_agents/examples/hello_world_a2a_langflow_server.pyi +14 -0
- aip_agents/examples/hello_world_a2a_langgraph_artifact_client.py +73 -0
- aip_agents/examples/hello_world_a2a_langgraph_artifact_client.pyi +5 -0
- aip_agents/examples/hello_world_a2a_langgraph_artifact_client_streaming.py +76 -0
- aip_agents/examples/hello_world_a2a_langgraph_artifact_client_streaming.pyi +5 -0
- aip_agents/examples/hello_world_a2a_langgraph_artifact_server.py +92 -0
- aip_agents/examples/hello_world_a2a_langgraph_artifact_server.pyi +16 -0
- aip_agents/examples/hello_world_a2a_langgraph_client.py +54 -0
- aip_agents/examples/hello_world_a2a_langgraph_client.pyi +9 -0
- aip_agents/examples/hello_world_a2a_langgraph_client_agent.py +54 -0
- aip_agents/examples/hello_world_a2a_langgraph_client_agent.pyi +9 -0
- aip_agents/examples/hello_world_a2a_langgraph_client_agent_lm_invoker.py +32 -0
- aip_agents/examples/hello_world_a2a_langgraph_client_agent_lm_invoker.pyi +2 -0
- aip_agents/examples/hello_world_a2a_langgraph_client_streaming.py +50 -0
- aip_agents/examples/hello_world_a2a_langgraph_client_streaming.pyi +9 -0
- aip_agents/examples/hello_world_a2a_langgraph_client_streaming_lm_invoker.py +44 -0
- aip_agents/examples/hello_world_a2a_langgraph_client_streaming_lm_invoker.pyi +5 -0
- aip_agents/examples/hello_world_a2a_langgraph_client_streaming_tool_streaming.py +92 -0
- aip_agents/examples/hello_world_a2a_langgraph_client_streaming_tool_streaming.pyi +5 -0
- aip_agents/examples/hello_world_a2a_langgraph_server.py +84 -0
- aip_agents/examples/hello_world_a2a_langgraph_server.pyi +14 -0
- aip_agents/examples/hello_world_a2a_langgraph_server_lm_invoker.py +79 -0
- aip_agents/examples/hello_world_a2a_langgraph_server_lm_invoker.pyi +15 -0
- aip_agents/examples/hello_world_a2a_langgraph_server_tool_streaming.py +132 -0
- aip_agents/examples/hello_world_a2a_langgraph_server_tool_streaming.pyi +15 -0
- aip_agents/examples/hello_world_a2a_mcp_langgraph.py +196 -0
- aip_agents/examples/hello_world_a2a_mcp_langgraph.pyi +48 -0
- aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_client.py +244 -0
- aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_client.pyi +48 -0
- aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_server.py +251 -0
- aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_server.pyi +45 -0
- aip_agents/examples/hello_world_a2a_with_metadata_langchain_client.py +57 -0
- aip_agents/examples/hello_world_a2a_with_metadata_langchain_client.pyi +5 -0
- aip_agents/examples/hello_world_a2a_with_metadata_langchain_server_lm_invoker.py +80 -0
- aip_agents/examples/hello_world_a2a_with_metadata_langchain_server_lm_invoker.pyi +15 -0
- aip_agents/examples/hello_world_google_adk.py +41 -0
- aip_agents/examples/hello_world_google_adk.pyi +5 -0
- aip_agents/examples/hello_world_google_adk_mcp_http.py +34 -0
- aip_agents/examples/hello_world_google_adk_mcp_http.pyi +5 -0
- aip_agents/examples/hello_world_google_adk_mcp_http_stream.py +40 -0
- aip_agents/examples/hello_world_google_adk_mcp_http_stream.pyi +5 -0
- aip_agents/examples/hello_world_google_adk_mcp_sse.py +44 -0
- aip_agents/examples/hello_world_google_adk_mcp_sse.pyi +5 -0
- aip_agents/examples/hello_world_google_adk_mcp_sse_stream.py +48 -0
- aip_agents/examples/hello_world_google_adk_mcp_sse_stream.pyi +5 -0
- aip_agents/examples/hello_world_google_adk_mcp_stdio.py +44 -0
- aip_agents/examples/hello_world_google_adk_mcp_stdio.pyi +5 -0
- aip_agents/examples/hello_world_google_adk_mcp_stdio_stream.py +48 -0
- aip_agents/examples/hello_world_google_adk_mcp_stdio_stream.pyi +5 -0
- aip_agents/examples/hello_world_google_adk_stream.py +44 -0
- aip_agents/examples/hello_world_google_adk_stream.pyi +5 -0
- aip_agents/examples/hello_world_langchain.py +28 -0
- aip_agents/examples/hello_world_langchain.pyi +5 -0
- aip_agents/examples/hello_world_langchain_lm_invoker.py +15 -0
- aip_agents/examples/hello_world_langchain_lm_invoker.pyi +2 -0
- aip_agents/examples/hello_world_langchain_mcp_http.py +34 -0
- aip_agents/examples/hello_world_langchain_mcp_http.pyi +5 -0
- aip_agents/examples/hello_world_langchain_mcp_http_interactive.py +130 -0
- aip_agents/examples/hello_world_langchain_mcp_http_interactive.pyi +16 -0
- aip_agents/examples/hello_world_langchain_mcp_http_stream.py +42 -0
- aip_agents/examples/hello_world_langchain_mcp_http_stream.pyi +5 -0
- aip_agents/examples/hello_world_langchain_mcp_multi_server.py +155 -0
- aip_agents/examples/hello_world_langchain_mcp_multi_server.pyi +18 -0
- aip_agents/examples/hello_world_langchain_mcp_sse.py +34 -0
- aip_agents/examples/hello_world_langchain_mcp_sse.pyi +5 -0
- aip_agents/examples/hello_world_langchain_mcp_sse_stream.py +40 -0
- aip_agents/examples/hello_world_langchain_mcp_sse_stream.pyi +5 -0
- aip_agents/examples/hello_world_langchain_mcp_stdio.py +30 -0
- aip_agents/examples/hello_world_langchain_mcp_stdio.pyi +5 -0
- aip_agents/examples/hello_world_langchain_mcp_stdio_stream.py +41 -0
- aip_agents/examples/hello_world_langchain_mcp_stdio_stream.pyi +5 -0
- aip_agents/examples/hello_world_langchain_stream.py +36 -0
- aip_agents/examples/hello_world_langchain_stream.pyi +5 -0
- aip_agents/examples/hello_world_langchain_stream_lm_invoker.py +39 -0
- aip_agents/examples/hello_world_langchain_stream_lm_invoker.pyi +5 -0
- aip_agents/examples/hello_world_langflow_agent.py +163 -0
- aip_agents/examples/hello_world_langflow_agent.pyi +35 -0
- aip_agents/examples/hello_world_langgraph.py +39 -0
- aip_agents/examples/hello_world_langgraph.pyi +5 -0
- aip_agents/examples/hello_world_langgraph_gl_connector_twitter.py +44 -0
- aip_agents/examples/hello_world_langgraph_gl_connector_twitter.pyi +5 -0
- aip_agents/examples/hello_world_langgraph_mcp_http.py +31 -0
- aip_agents/examples/hello_world_langgraph_mcp_http.pyi +5 -0
- aip_agents/examples/hello_world_langgraph_mcp_http_stream.py +34 -0
- aip_agents/examples/hello_world_langgraph_mcp_http_stream.pyi +5 -0
- aip_agents/examples/hello_world_langgraph_mcp_sse.py +35 -0
- aip_agents/examples/hello_world_langgraph_mcp_sse.pyi +5 -0
- aip_agents/examples/hello_world_langgraph_mcp_sse_stream.py +50 -0
- aip_agents/examples/hello_world_langgraph_mcp_sse_stream.pyi +5 -0
- aip_agents/examples/hello_world_langgraph_mcp_stdio.py +35 -0
- aip_agents/examples/hello_world_langgraph_mcp_stdio.pyi +5 -0
- aip_agents/examples/hello_world_langgraph_mcp_stdio_stream.py +50 -0
- aip_agents/examples/hello_world_langgraph_mcp_stdio_stream.pyi +5 -0
- aip_agents/examples/hello_world_langgraph_stream.py +43 -0
- aip_agents/examples/hello_world_langgraph_stream.pyi +5 -0
- aip_agents/examples/hello_world_langgraph_stream_lm_invoker.py +37 -0
- aip_agents/examples/hello_world_langgraph_stream_lm_invoker.pyi +5 -0
- aip_agents/examples/hello_world_model_switch_cli.py +210 -0
- aip_agents/examples/hello_world_model_switch_cli.pyi +30 -0
- aip_agents/examples/hello_world_multi_agent_adk.py +75 -0
- aip_agents/examples/hello_world_multi_agent_adk.pyi +6 -0
- aip_agents/examples/hello_world_multi_agent_langchain.py +54 -0
- aip_agents/examples/hello_world_multi_agent_langchain.pyi +5 -0
- aip_agents/examples/hello_world_multi_agent_langgraph.py +66 -0
- aip_agents/examples/hello_world_multi_agent_langgraph.pyi +5 -0
- aip_agents/examples/hello_world_multi_agent_langgraph_lm_invoker.py +69 -0
- aip_agents/examples/hello_world_multi_agent_langgraph_lm_invoker.pyi +5 -0
- aip_agents/examples/hello_world_pii_logger.py +21 -0
- aip_agents/examples/hello_world_pii_logger.pyi +5 -0
- aip_agents/examples/hello_world_ptc.py +49 -0
- aip_agents/examples/hello_world_ptc.pyi +5 -0
- aip_agents/examples/hello_world_sentry.py +133 -0
- aip_agents/examples/hello_world_sentry.pyi +21 -0
- aip_agents/examples/hello_world_step_limits.py +273 -0
- aip_agents/examples/hello_world_step_limits.pyi +17 -0
- aip_agents/examples/hello_world_stock_a2a_server.py +103 -0
- aip_agents/examples/hello_world_stock_a2a_server.pyi +17 -0
- aip_agents/examples/hello_world_tool_output_client.py +55 -0
- aip_agents/examples/hello_world_tool_output_client.pyi +5 -0
- aip_agents/examples/hello_world_tool_output_server.py +114 -0
- aip_agents/examples/hello_world_tool_output_server.pyi +19 -0
- aip_agents/examples/hitl_demo.py +724 -0
- aip_agents/examples/hitl_demo.pyi +67 -0
- aip_agents/examples/mcp_configs/configs.py +63 -0
- aip_agents/examples/mcp_servers/common.py +76 -0
- aip_agents/examples/mcp_servers/mcp_name.py +29 -0
- aip_agents/examples/mcp_servers/mcp_server_http.py +19 -0
- aip_agents/examples/mcp_servers/mcp_server_sse.py +19 -0
- aip_agents/examples/mcp_servers/mcp_server_stdio.py +19 -0
- aip_agents/examples/mcp_servers/mcp_time.py +10 -0
- aip_agents/examples/pii_demo_langgraph_client.py +69 -0
- aip_agents/examples/pii_demo_langgraph_client.pyi +5 -0
- aip_agents/examples/pii_demo_langgraph_server.py +126 -0
- aip_agents/examples/pii_demo_langgraph_server.pyi +20 -0
- aip_agents/examples/pii_demo_multi_agent_client.py +80 -0
- aip_agents/examples/pii_demo_multi_agent_client.pyi +5 -0
- aip_agents/examples/pii_demo_multi_agent_server.py +247 -0
- aip_agents/examples/pii_demo_multi_agent_server.pyi +40 -0
- aip_agents/examples/todolist_planning_a2a_langchain_client.py +70 -0
- aip_agents/examples/todolist_planning_a2a_langchain_client.pyi +5 -0
- aip_agents/examples/todolist_planning_a2a_langgraph_server.py +88 -0
- aip_agents/examples/todolist_planning_a2a_langgraph_server.pyi +19 -0
- aip_agents/examples/tools/__init__.py +27 -0
- aip_agents/examples/tools/__init__.pyi +9 -0
- aip_agents/examples/tools/adk_arithmetic_tools.py +36 -0
- aip_agents/examples/tools/adk_arithmetic_tools.pyi +24 -0
- aip_agents/examples/tools/adk_weather_tool.py +60 -0
- aip_agents/examples/tools/adk_weather_tool.pyi +18 -0
- aip_agents/examples/tools/data_generator_tool.py +103 -0
- aip_agents/examples/tools/data_generator_tool.pyi +15 -0
- aip_agents/examples/tools/data_visualization_tool.py +312 -0
- aip_agents/examples/tools/data_visualization_tool.pyi +19 -0
- aip_agents/examples/tools/image_artifact_tool.py +136 -0
- aip_agents/examples/tools/image_artifact_tool.pyi +26 -0
- aip_agents/examples/tools/langchain_arithmetic_tools.py +26 -0
- aip_agents/examples/tools/langchain_arithmetic_tools.pyi +17 -0
- aip_agents/examples/tools/langchain_currency_exchange_tool.py +88 -0
- aip_agents/examples/tools/langchain_currency_exchange_tool.pyi +20 -0
- aip_agents/examples/tools/langchain_graph_artifact_tool.py +172 -0
- aip_agents/examples/tools/langchain_graph_artifact_tool.pyi +25 -0
- aip_agents/examples/tools/langchain_weather_tool.py +48 -0
- aip_agents/examples/tools/langchain_weather_tool.pyi +19 -0
- aip_agents/examples/tools/langgraph_streaming_tool.py +130 -0
- aip_agents/examples/tools/langgraph_streaming_tool.pyi +43 -0
- aip_agents/examples/tools/mock_retrieval_tool.py +56 -0
- aip_agents/examples/tools/mock_retrieval_tool.pyi +13 -0
- aip_agents/examples/tools/pii_demo_tools.py +189 -0
- aip_agents/examples/tools/pii_demo_tools.pyi +54 -0
- aip_agents/examples/tools/random_chart_tool.py +142 -0
- aip_agents/examples/tools/random_chart_tool.pyi +20 -0
- aip_agents/examples/tools/serper_tool.py +202 -0
- aip_agents/examples/tools/serper_tool.pyi +16 -0
- aip_agents/examples/tools/stock_tools.py +82 -0
- aip_agents/examples/tools/stock_tools.pyi +36 -0
- aip_agents/examples/tools/table_generator_tool.py +167 -0
- aip_agents/examples/tools/table_generator_tool.pyi +22 -0
- aip_agents/examples/tools/time_tool.py +82 -0
- aip_agents/examples/tools/time_tool.pyi +15 -0
- aip_agents/examples/tools/weather_forecast_tool.py +38 -0
- aip_agents/examples/tools/weather_forecast_tool.pyi +14 -0
- aip_agents/executor/agent_executor.py +473 -0
- aip_agents/executor/base.py +48 -0
- aip_agents/guardrails/__init__.py +83 -0
- aip_agents/guardrails/__init__.pyi +6 -0
- aip_agents/guardrails/engines/__init__.py +69 -0
- aip_agents/guardrails/engines/__init__.pyi +4 -0
- aip_agents/guardrails/engines/base.py +90 -0
- aip_agents/guardrails/engines/base.pyi +61 -0
- aip_agents/guardrails/engines/nemo.py +101 -0
- aip_agents/guardrails/engines/nemo.pyi +46 -0
- aip_agents/guardrails/engines/phrase_matcher.py +113 -0
- aip_agents/guardrails/engines/phrase_matcher.pyi +48 -0
- aip_agents/guardrails/exceptions.py +39 -0
- aip_agents/guardrails/exceptions.pyi +23 -0
- aip_agents/guardrails/manager.py +163 -0
- aip_agents/guardrails/manager.pyi +42 -0
- aip_agents/guardrails/middleware.py +199 -0
- aip_agents/guardrails/middleware.pyi +87 -0
- aip_agents/guardrails/schemas.py +63 -0
- aip_agents/guardrails/schemas.pyi +43 -0
- aip_agents/guardrails/utils.py +45 -0
- aip_agents/guardrails/utils.pyi +19 -0
- aip_agents/mcp/__init__.py +1 -0
- aip_agents/mcp/__init__.pyi +0 -0
- aip_agents/mcp/client/__init__.py +14 -0
- aip_agents/mcp/client/__init__.pyi +5 -0
- aip_agents/mcp/client/base_mcp_client.py +369 -0
- aip_agents/mcp/client/base_mcp_client.pyi +148 -0
- aip_agents/mcp/client/connection_manager.py +228 -0
- aip_agents/mcp/client/connection_manager.pyi +51 -0
- aip_agents/mcp/client/google_adk/__init__.py +11 -0
- aip_agents/mcp/client/google_adk/__init__.pyi +3 -0
- aip_agents/mcp/client/google_adk/client.py +381 -0
- aip_agents/mcp/client/google_adk/client.pyi +75 -0
- aip_agents/mcp/client/langchain/__init__.py +11 -0
- aip_agents/mcp/client/langchain/__init__.pyi +3 -0
- aip_agents/mcp/client/langchain/client.py +265 -0
- aip_agents/mcp/client/langchain/client.pyi +48 -0
- aip_agents/mcp/client/persistent_session.py +612 -0
- aip_agents/mcp/client/persistent_session.pyi +122 -0
- aip_agents/mcp/client/session_pool.py +351 -0
- aip_agents/mcp/client/session_pool.pyi +101 -0
- aip_agents/mcp/client/transports.py +263 -0
- aip_agents/mcp/client/transports.pyi +132 -0
- aip_agents/mcp/utils/__init__.py +7 -0
- aip_agents/mcp/utils/__init__.pyi +0 -0
- aip_agents/mcp/utils/config_validator.py +139 -0
- aip_agents/mcp/utils/config_validator.pyi +82 -0
- aip_agents/memory/__init__.py +14 -0
- aip_agents/memory/__init__.pyi +5 -0
- aip_agents/memory/adapters/__init__.py +10 -0
- aip_agents/memory/adapters/__init__.pyi +4 -0
- aip_agents/memory/adapters/base_adapter.py +811 -0
- aip_agents/memory/adapters/base_adapter.pyi +176 -0
- aip_agents/memory/adapters/mem0.py +84 -0
- aip_agents/memory/adapters/mem0.pyi +22 -0
- aip_agents/memory/base.py +84 -0
- aip_agents/memory/base.pyi +60 -0
- aip_agents/memory/constants.py +49 -0
- aip_agents/memory/constants.pyi +25 -0
- aip_agents/memory/factory.py +86 -0
- aip_agents/memory/factory.pyi +24 -0
- aip_agents/memory/guidance.py +20 -0
- aip_agents/memory/guidance.pyi +3 -0
- aip_agents/memory/simple_memory.py +47 -0
- aip_agents/memory/simple_memory.pyi +23 -0
- aip_agents/middleware/__init__.py +17 -0
- aip_agents/middleware/__init__.pyi +5 -0
- aip_agents/middleware/base.py +96 -0
- aip_agents/middleware/base.pyi +75 -0
- aip_agents/middleware/manager.py +150 -0
- aip_agents/middleware/manager.pyi +84 -0
- aip_agents/middleware/todolist.py +274 -0
- aip_agents/middleware/todolist.pyi +125 -0
- aip_agents/ptc/__init__.py +48 -0
- aip_agents/ptc/__init__.pyi +10 -0
- aip_agents/ptc/doc_gen.py +122 -0
- aip_agents/ptc/doc_gen.pyi +40 -0
- aip_agents/ptc/exceptions.py +39 -0
- aip_agents/ptc/exceptions.pyi +22 -0
- aip_agents/ptc/executor.py +143 -0
- aip_agents/ptc/executor.pyi +73 -0
- aip_agents/ptc/mcp/__init__.py +45 -0
- aip_agents/ptc/mcp/__init__.pyi +7 -0
- aip_agents/ptc/mcp/sandbox_bridge.py +668 -0
- aip_agents/ptc/mcp/sandbox_bridge.pyi +47 -0
- aip_agents/ptc/mcp/templates/__init__.py +1 -0
- aip_agents/ptc/mcp/templates/__init__.pyi +0 -0
- aip_agents/ptc/mcp/templates/mcp_client.py.template +239 -0
- aip_agents/ptc/naming.py +184 -0
- aip_agents/ptc/naming.pyi +76 -0
- aip_agents/ptc/payload.py +26 -0
- aip_agents/ptc/payload.pyi +15 -0
- aip_agents/ptc/prompt_builder.py +571 -0
- aip_agents/ptc/prompt_builder.pyi +55 -0
- aip_agents/ptc/ptc_helper.py +16 -0
- aip_agents/ptc/ptc_helper.pyi +1 -0
- aip_agents/ptc/sandbox_bridge.py +58 -0
- aip_agents/ptc/sandbox_bridge.pyi +25 -0
- aip_agents/ptc/template_utils.py +33 -0
- aip_agents/ptc/template_utils.pyi +13 -0
- aip_agents/ptc/templates/__init__.py +1 -0
- aip_agents/ptc/templates/__init__.pyi +0 -0
- aip_agents/ptc/templates/ptc_helper.py.template +134 -0
- aip_agents/sandbox/__init__.py +43 -0
- aip_agents/sandbox/__init__.pyi +5 -0
- aip_agents/sandbox/defaults.py +9 -0
- aip_agents/sandbox/defaults.pyi +2 -0
- aip_agents/sandbox/e2b_runtime.py +267 -0
- aip_agents/sandbox/e2b_runtime.pyi +51 -0
- aip_agents/sandbox/template_builder.py +131 -0
- aip_agents/sandbox/template_builder.pyi +36 -0
- aip_agents/sandbox/types.py +24 -0
- aip_agents/sandbox/types.pyi +14 -0
- aip_agents/sandbox/validation.py +50 -0
- aip_agents/sandbox/validation.pyi +20 -0
- aip_agents/schema/__init__.py +69 -0
- aip_agents/schema/__init__.pyi +9 -0
- aip_agents/schema/a2a.py +56 -0
- aip_agents/schema/a2a.pyi +40 -0
- aip_agents/schema/agent.py +111 -0
- aip_agents/schema/agent.pyi +65 -0
- aip_agents/schema/hitl.py +157 -0
- aip_agents/schema/hitl.pyi +89 -0
- aip_agents/schema/langgraph.py +37 -0
- aip_agents/schema/langgraph.pyi +28 -0
- aip_agents/schema/model_id.py +97 -0
- aip_agents/schema/model_id.pyi +54 -0
- aip_agents/schema/step_limit.py +108 -0
- aip_agents/schema/step_limit.pyi +63 -0
- aip_agents/schema/storage.py +40 -0
- aip_agents/schema/storage.pyi +21 -0
- aip_agents/sentry/__init__.py +11 -0
- aip_agents/sentry/__init__.pyi +3 -0
- aip_agents/sentry/sentry.py +151 -0
- aip_agents/sentry/sentry.pyi +48 -0
- aip_agents/storage/__init__.py +41 -0
- aip_agents/storage/__init__.pyi +8 -0
- aip_agents/storage/base.py +85 -0
- aip_agents/storage/base.pyi +58 -0
- aip_agents/storage/clients/__init__.py +12 -0
- aip_agents/storage/clients/__init__.pyi +3 -0
- aip_agents/storage/clients/minio_client.py +318 -0
- aip_agents/storage/clients/minio_client.pyi +137 -0
- aip_agents/storage/config.py +62 -0
- aip_agents/storage/config.pyi +29 -0
- aip_agents/storage/providers/__init__.py +15 -0
- aip_agents/storage/providers/__init__.pyi +5 -0
- aip_agents/storage/providers/base.py +106 -0
- aip_agents/storage/providers/base.pyi +88 -0
- aip_agents/storage/providers/memory.py +114 -0
- aip_agents/storage/providers/memory.pyi +79 -0
- aip_agents/storage/providers/object_storage.py +214 -0
- aip_agents/storage/providers/object_storage.pyi +98 -0
- aip_agents/tools/__init__.py +64 -0
- aip_agents/tools/__init__.pyi +11 -0
- aip_agents/tools/browser_use/__init__.py +82 -0
- aip_agents/tools/browser_use/__init__.pyi +14 -0
- aip_agents/tools/browser_use/action_parser.py +103 -0
- aip_agents/tools/browser_use/action_parser.pyi +18 -0
- aip_agents/tools/browser_use/browser_use_tool.py +1120 -0
- aip_agents/tools/browser_use/browser_use_tool.pyi +50 -0
- aip_agents/tools/browser_use/llm_config.py +120 -0
- aip_agents/tools/browser_use/llm_config.pyi +52 -0
- aip_agents/tools/browser_use/minio_storage.py +198 -0
- aip_agents/tools/browser_use/minio_storage.pyi +109 -0
- aip_agents/tools/browser_use/schemas.py +119 -0
- aip_agents/tools/browser_use/schemas.pyi +32 -0
- aip_agents/tools/browser_use/session.py +76 -0
- aip_agents/tools/browser_use/session.pyi +4 -0
- aip_agents/tools/browser_use/session_errors.py +132 -0
- aip_agents/tools/browser_use/session_errors.pyi +53 -0
- aip_agents/tools/browser_use/steel_session_recording.py +317 -0
- aip_agents/tools/browser_use/steel_session_recording.pyi +63 -0
- aip_agents/tools/browser_use/streaming.py +815 -0
- aip_agents/tools/browser_use/streaming.pyi +81 -0
- aip_agents/tools/browser_use/structured_data_parser.py +257 -0
- aip_agents/tools/browser_use/structured_data_parser.pyi +86 -0
- aip_agents/tools/browser_use/structured_data_recovery.py +204 -0
- aip_agents/tools/browser_use/structured_data_recovery.pyi +43 -0
- aip_agents/tools/browser_use/types.py +78 -0
- aip_agents/tools/browser_use/types.pyi +45 -0
- aip_agents/tools/code_sandbox/__init__.py +26 -0
- aip_agents/tools/code_sandbox/__init__.pyi +3 -0
- aip_agents/tools/code_sandbox/constant.py +13 -0
- aip_agents/tools/code_sandbox/constant.pyi +4 -0
- aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.py +306 -0
- aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.pyi +102 -0
- aip_agents/tools/code_sandbox/e2b_sandbox_tool.py +411 -0
- aip_agents/tools/code_sandbox/e2b_sandbox_tool.pyi +29 -0
- aip_agents/tools/constants.py +177 -0
- aip_agents/tools/constants.pyi +138 -0
- aip_agents/tools/date_range_tool.py +554 -0
- aip_agents/tools/date_range_tool.pyi +21 -0
- aip_agents/tools/document_loader/__init__.py +44 -0
- aip_agents/tools/document_loader/__init__.pyi +7 -0
- aip_agents/tools/document_loader/base_reader.py +302 -0
- aip_agents/tools/document_loader/base_reader.pyi +75 -0
- aip_agents/tools/document_loader/docx_reader_tool.py +68 -0
- aip_agents/tools/document_loader/docx_reader_tool.pyi +10 -0
- aip_agents/tools/document_loader/excel_reader_tool.py +171 -0
- aip_agents/tools/document_loader/excel_reader_tool.pyi +26 -0
- aip_agents/tools/document_loader/pdf_reader_tool.py +79 -0
- aip_agents/tools/document_loader/pdf_reader_tool.pyi +11 -0
- aip_agents/tools/document_loader/pdf_splitter.py +169 -0
- aip_agents/tools/document_loader/pdf_splitter.pyi +18 -0
- aip_agents/tools/execute_ptc_code.py +308 -0
- aip_agents/tools/execute_ptc_code.pyi +90 -0
- aip_agents/tools/gl_connector/__init__.py +5 -0
- aip_agents/tools/gl_connector/__init__.pyi +3 -0
- aip_agents/tools/gl_connector/tool.py +383 -0
- aip_agents/tools/gl_connector/tool.pyi +74 -0
- aip_agents/tools/gl_connector_tools.py +119 -0
- aip_agents/tools/gl_connector_tools.pyi +39 -0
- aip_agents/tools/memory_search/__init__.py +29 -0
- aip_agents/tools/memory_search/__init__.pyi +5 -0
- aip_agents/tools/memory_search/base.py +200 -0
- aip_agents/tools/memory_search/base.pyi +69 -0
- aip_agents/tools/memory_search/mem0.py +365 -0
- aip_agents/tools/memory_search/mem0.pyi +29 -0
- aip_agents/tools/memory_search/schema.py +81 -0
- aip_agents/tools/memory_search/schema.pyi +25 -0
- aip_agents/tools/memory_search_tool.py +34 -0
- aip_agents/tools/memory_search_tool.pyi +3 -0
- aip_agents/tools/time_tool.py +117 -0
- aip_agents/tools/time_tool.pyi +16 -0
- aip_agents/tools/tool_config_injector.py +300 -0
- aip_agents/tools/tool_config_injector.pyi +26 -0
- aip_agents/tools/web_search/__init__.py +15 -0
- aip_agents/tools/web_search/__init__.pyi +3 -0
- aip_agents/tools/web_search/serper_tool.py +187 -0
- aip_agents/tools/web_search/serper_tool.pyi +19 -0
- aip_agents/types/__init__.py +70 -0
- aip_agents/types/__init__.pyi +36 -0
- aip_agents/types/a2a_events.py +13 -0
- aip_agents/types/a2a_events.pyi +3 -0
- aip_agents/utils/__init__.py +79 -0
- aip_agents/utils/__init__.pyi +11 -0
- aip_agents/utils/a2a_connector.py +1757 -0
- aip_agents/utils/a2a_connector.pyi +146 -0
- aip_agents/utils/artifact_helpers.py +502 -0
- aip_agents/utils/artifact_helpers.pyi +203 -0
- aip_agents/utils/constants.py +22 -0
- aip_agents/utils/constants.pyi +10 -0
- aip_agents/utils/datetime/__init__.py +34 -0
- aip_agents/utils/datetime/__init__.pyi +4 -0
- aip_agents/utils/datetime/normalization.py +231 -0
- aip_agents/utils/datetime/normalization.pyi +95 -0
- aip_agents/utils/datetime/timezone.py +206 -0
- aip_agents/utils/datetime/timezone.pyi +48 -0
- aip_agents/utils/env_loader.py +27 -0
- aip_agents/utils/env_loader.pyi +10 -0
- aip_agents/utils/event_handler_registry.py +58 -0
- aip_agents/utils/event_handler_registry.pyi +23 -0
- aip_agents/utils/file_prompt_utils.py +176 -0
- aip_agents/utils/file_prompt_utils.pyi +21 -0
- aip_agents/utils/final_response_builder.py +211 -0
- aip_agents/utils/final_response_builder.pyi +34 -0
- aip_agents/utils/formatter_llm_client.py +231 -0
- aip_agents/utils/formatter_llm_client.pyi +71 -0
- aip_agents/utils/langgraph/__init__.py +19 -0
- aip_agents/utils/langgraph/__init__.pyi +3 -0
- aip_agents/utils/langgraph/converter.py +128 -0
- aip_agents/utils/langgraph/converter.pyi +49 -0
- aip_agents/utils/langgraph/tool_managers/__init__.py +15 -0
- aip_agents/utils/langgraph/tool_managers/__init__.pyi +5 -0
- aip_agents/utils/langgraph/tool_managers/a2a_tool_manager.py +99 -0
- aip_agents/utils/langgraph/tool_managers/a2a_tool_manager.pyi +35 -0
- aip_agents/utils/langgraph/tool_managers/base_tool_manager.py +66 -0
- aip_agents/utils/langgraph/tool_managers/base_tool_manager.pyi +48 -0
- aip_agents/utils/langgraph/tool_managers/delegation_tool_manager.py +1096 -0
- aip_agents/utils/langgraph/tool_managers/delegation_tool_manager.pyi +56 -0
- aip_agents/utils/langgraph/tool_output_management.py +1047 -0
- aip_agents/utils/langgraph/tool_output_management.pyi +329 -0
- aip_agents/utils/logger.py +195 -0
- aip_agents/utils/logger.pyi +60 -0
- aip_agents/utils/metadata/__init__.py +27 -0
- aip_agents/utils/metadata/__init__.pyi +5 -0
- aip_agents/utils/metadata/activity_metadata_helper.py +407 -0
- aip_agents/utils/metadata/activity_metadata_helper.pyi +25 -0
- aip_agents/utils/metadata/activity_narrative/__init__.py +35 -0
- aip_agents/utils/metadata/activity_narrative/__init__.pyi +7 -0
- aip_agents/utils/metadata/activity_narrative/builder.py +817 -0
- aip_agents/utils/metadata/activity_narrative/builder.pyi +35 -0
- aip_agents/utils/metadata/activity_narrative/constants.py +51 -0
- aip_agents/utils/metadata/activity_narrative/constants.pyi +10 -0
- aip_agents/utils/metadata/activity_narrative/context.py +49 -0
- aip_agents/utils/metadata/activity_narrative/context.pyi +32 -0
- aip_agents/utils/metadata/activity_narrative/formatters.py +230 -0
- aip_agents/utils/metadata/activity_narrative/formatters.pyi +48 -0
- aip_agents/utils/metadata/activity_narrative/utils.py +35 -0
- aip_agents/utils/metadata/activity_narrative/utils.pyi +12 -0
- aip_agents/utils/metadata/schemas/__init__.py +16 -0
- aip_agents/utils/metadata/schemas/__init__.pyi +4 -0
- aip_agents/utils/metadata/schemas/activity_schema.py +29 -0
- aip_agents/utils/metadata/schemas/activity_schema.pyi +18 -0
- aip_agents/utils/metadata/schemas/thinking_schema.py +31 -0
- aip_agents/utils/metadata/schemas/thinking_schema.pyi +20 -0
- aip_agents/utils/metadata/thinking_metadata_helper.py +38 -0
- aip_agents/utils/metadata/thinking_metadata_helper.pyi +4 -0
- aip_agents/utils/metadata_helper.py +358 -0
- aip_agents/utils/metadata_helper.pyi +117 -0
- aip_agents/utils/name_preprocessor/__init__.py +17 -0
- aip_agents/utils/name_preprocessor/__init__.pyi +6 -0
- aip_agents/utils/name_preprocessor/base_name_preprocessor.py +73 -0
- aip_agents/utils/name_preprocessor/base_name_preprocessor.pyi +52 -0
- aip_agents/utils/name_preprocessor/google_name_preprocessor.py +100 -0
- aip_agents/utils/name_preprocessor/google_name_preprocessor.pyi +38 -0
- aip_agents/utils/name_preprocessor/name_preprocessor.py +87 -0
- aip_agents/utils/name_preprocessor/name_preprocessor.pyi +41 -0
- aip_agents/utils/name_preprocessor/openai_name_preprocessor.py +48 -0
- aip_agents/utils/name_preprocessor/openai_name_preprocessor.pyi +34 -0
- aip_agents/utils/pii/__init__.py +25 -0
- aip_agents/utils/pii/__init__.pyi +5 -0
- aip_agents/utils/pii/pii_handler.py +397 -0
- aip_agents/utils/pii/pii_handler.pyi +96 -0
- aip_agents/utils/pii/pii_helper.py +207 -0
- aip_agents/utils/pii/pii_helper.pyi +78 -0
- aip_agents/utils/pii/uuid_deanonymizer_mapping.py +195 -0
- aip_agents/utils/pii/uuid_deanonymizer_mapping.pyi +73 -0
- aip_agents/utils/reference_helper.py +273 -0
- aip_agents/utils/reference_helper.pyi +81 -0
- aip_agents/utils/sse_chunk_transformer.py +831 -0
- aip_agents/utils/sse_chunk_transformer.pyi +166 -0
- aip_agents/utils/step_limit_manager.py +265 -0
- aip_agents/utils/step_limit_manager.pyi +112 -0
- aip_agents/utils/token_usage_helper.py +156 -0
- aip_agents/utils/token_usage_helper.pyi +60 -0
- aip_agents_binary-0.6.4.dist-info/METADATA +673 -0
- aip_agents_binary-0.6.4.dist-info/RECORD +612 -0
- aip_agents_binary-0.6.4.dist-info/WHEEL +5 -0
- aip_agents_binary-0.6.4.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,970 @@
|
|
|
1
|
+
"""Base class for concrete agent implementations.
|
|
2
|
+
|
|
3
|
+
This class provides common functionalities like A2A client capabilities.
|
|
4
|
+
|
|
5
|
+
Authors:
|
|
6
|
+
Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
|
|
7
|
+
Raymond Christopher (raymond.christopher@gdplabs.id)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import asyncio
|
|
11
|
+
from collections.abc import AsyncGenerator
|
|
12
|
+
from importlib import import_module
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Any
|
|
15
|
+
from warnings import warn
|
|
16
|
+
|
|
17
|
+
import httpx
|
|
18
|
+
from a2a.server.apps import A2AStarletteApplication
|
|
19
|
+
from a2a.server.request_handlers import DefaultRequestHandler
|
|
20
|
+
from a2a.server.tasks import InMemoryTaskStore
|
|
21
|
+
from a2a.types import AgentCard
|
|
22
|
+
from gllm_core.utils.retry import RetryConfig
|
|
23
|
+
from gllm_inference.builder import build_lm_invoker
|
|
24
|
+
from gllm_inference.lm_invoker.lm_invoker import BaseLMInvoker
|
|
25
|
+
from langchain_core.language_models import BaseChatModel
|
|
26
|
+
from starlette.applications import Starlette
|
|
27
|
+
|
|
28
|
+
from aip_agents.agent.interface import AgentInterface
|
|
29
|
+
from aip_agents.credentials.manager import CredentialsManager
|
|
30
|
+
from aip_agents.mcp.client.base_mcp_client import BaseMCPClient
|
|
31
|
+
from aip_agents.schema.agent import A2AClientConfig, AgentConfig, BaseAgentConfig, CredentialType
|
|
32
|
+
from aip_agents.schema.model_id import ModelId, ModelProvider
|
|
33
|
+
from aip_agents.utils.a2a_connector import A2AConnector
|
|
34
|
+
from aip_agents.utils.logger import get_logger
|
|
35
|
+
from aip_agents.utils.name_preprocessor.name_preprocessor import NamePreprocessor
|
|
36
|
+
|
|
37
|
+
logger = get_logger(__name__)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _get_agent_executor_mapping() -> dict[str, str]:
|
|
41
|
+
"""Map agent class names to their executor import paths."""
|
|
42
|
+
return {
|
|
43
|
+
"LangGraphReactAgent": "aip_agents.a2a.server.langgraph_executor.LangGraphA2AExecutor",
|
|
44
|
+
"LangGraphAgent": "aip_agents.a2a.server.langgraph_executor.LangGraphA2AExecutor",
|
|
45
|
+
"LangChainAgent": "aip_agents.a2a.server.langgraph_executor.LangGraphA2AExecutor",
|
|
46
|
+
"LangflowAgent": "aip_agents.a2a.server.langflow_executor.LangflowA2AExecutor",
|
|
47
|
+
"GoogleADKAgent": "aip_agents.a2a.server.google_adk_executor.GoogleADKExecutor",
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
AGENT_EXECUTOR_MAPPING = _get_agent_executor_mapping()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _load_executor_class(candidate: Any) -> type[Any]:
|
|
55
|
+
"""Resolve an executor class from a dotted path or a direct reference.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
candidate (Any): Either a string path (e.g., "module.Class") or a class/type object.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
type[Any]: The resolved executor class.
|
|
62
|
+
"""
|
|
63
|
+
if isinstance(candidate, str):
|
|
64
|
+
module_name, class_name = candidate.rsplit(".", 1)
|
|
65
|
+
module = import_module(module_name)
|
|
66
|
+
return getattr(module, class_name)
|
|
67
|
+
|
|
68
|
+
if isinstance(candidate, type): # Already a class reference
|
|
69
|
+
return candidate
|
|
70
|
+
|
|
71
|
+
if callable(candidate):
|
|
72
|
+
return candidate # type: ignore[return-value]
|
|
73
|
+
|
|
74
|
+
raise TypeError(f"Unsupported executor mapping entry: {candidate!r}")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _get_executor_class_for_agent(agent: "BaseAgent") -> type[Any]:
|
|
78
|
+
"""Resolve the appropriate executor class for the given agent instance.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
agent (BaseAgent): The agent instance to find an executor for.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
type[Any]: The appropriate executor class for the agent.
|
|
85
|
+
"""
|
|
86
|
+
for cls in agent.__class__.__mro__:
|
|
87
|
+
executor_path = AGENT_EXECUTOR_MAPPING.get(cls.__name__)
|
|
88
|
+
if executor_path:
|
|
89
|
+
return _load_executor_class(executor_path)
|
|
90
|
+
raise KeyError(f"No A2A executor registered for agent class '{agent.__class__.__name__}'")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
DEFAULT_RETRY_CONFIG = RetryConfig(max_retries=5, timeout=240.0)
|
|
94
|
+
|
|
95
|
+
LM_EXCLUDE_FIELDS = {
|
|
96
|
+
"lm_base_url",
|
|
97
|
+
"lm_api_key",
|
|
98
|
+
"lm_name",
|
|
99
|
+
"lm_provider",
|
|
100
|
+
"lm_hyperparameters",
|
|
101
|
+
"lm_retry_config",
|
|
102
|
+
"lm_credentials",
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
CUSTOM_PROVIDERS = {
|
|
106
|
+
"openai-compatible/": ModelProvider.OPENAI_COMPATIBLE,
|
|
107
|
+
"azure-openai/": ModelProvider.AZURE_OPENAI,
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
OUTPUT_ANALYTICS_KEY = "output_analytics"
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class BaseAgent(AgentInterface):
|
|
114
|
+
"""Base class for agents, providing common A2A client method implementations.
|
|
115
|
+
|
|
116
|
+
Concrete agent implementations (e.g., LangGraphAgent, GoogleADKAgent)
|
|
117
|
+
should inherit from this class if they need to utilize the shared A2A
|
|
118
|
+
client functionalities.
|
|
119
|
+
|
|
120
|
+
This class now supports flexible model handling:
|
|
121
|
+
- model: Optional[Any] - can be an lm_invoker, string/ModelId, LangChain BaseChatModel, or other types
|
|
122
|
+
- Automatically sets self.lm_invoker if an lm_invoker is provided or can be built
|
|
123
|
+
- Stores the original model in self.model for subclass use
|
|
124
|
+
- Enhanced credential support with automatic type detection
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
def __init__( # noqa: PLR0913
|
|
128
|
+
self,
|
|
129
|
+
name: str,
|
|
130
|
+
instruction: str,
|
|
131
|
+
description: str | None = None,
|
|
132
|
+
model: Any | None = None,
|
|
133
|
+
tools: list[Any] | None = None,
|
|
134
|
+
config: BaseAgentConfig | dict[str, Any] | None = None,
|
|
135
|
+
tool_configs: dict[str, Any] | None = None,
|
|
136
|
+
**kwargs: Any,
|
|
137
|
+
):
|
|
138
|
+
"""Initializes the BaseAgent.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
name: The name of the agent.
|
|
142
|
+
instruction: The core directive or system prompt for the agent.
|
|
143
|
+
description: Human-readable description. Defaults to instruction if not provided.
|
|
144
|
+
model: The model to use. Can be:
|
|
145
|
+
- BaseLMInvoker instance (will be set as self.lm_invoker)
|
|
146
|
+
- String or ModelId (will build an lm_invoker)
|
|
147
|
+
- LangChain BaseChatModel (will be stored in self.model)
|
|
148
|
+
- Any other type (will be stored in self.model)
|
|
149
|
+
tools: List of tools available to the agent.
|
|
150
|
+
config: Additional configuration for the agent. Can be a BaseAgentConfig instance or dict.
|
|
151
|
+
tool_configs: Default tool configurations applied to all tool calls from this agent.
|
|
152
|
+
**kwargs: Additional keyword arguments for AgentInterface.
|
|
153
|
+
"""
|
|
154
|
+
# Convert config to BaseAgentConfig if it's a dict (backward compatibility)
|
|
155
|
+
processed_config = self._process_config(config, tools)
|
|
156
|
+
|
|
157
|
+
# Process model parameter to set up lm_invoker and model attributes
|
|
158
|
+
processed_lm_invoker, processed_model = self._process_model_parameter(
|
|
159
|
+
name, model, tools or [], processed_config
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
# Pass the lm_invoker to the parent class
|
|
163
|
+
super().__init__(
|
|
164
|
+
name=name,
|
|
165
|
+
instruction=instruction,
|
|
166
|
+
description=description,
|
|
167
|
+
lm_invoker=processed_lm_invoker,
|
|
168
|
+
config=processed_config,
|
|
169
|
+
**kwargs,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
# Store processed model and other attributes
|
|
173
|
+
self.model = processed_model
|
|
174
|
+
self.tools = tools or []
|
|
175
|
+
# Private MCP configuration to prevent tampering and maintain sync
|
|
176
|
+
self._mcp_config: dict[str, dict[str, Any]] = {}
|
|
177
|
+
self.tool_configs = tool_configs or {}
|
|
178
|
+
|
|
179
|
+
self._mcp_tools_initialized: bool = False
|
|
180
|
+
self.mcp_client: BaseMCPClient | None = None
|
|
181
|
+
self._mcp_init_lock: asyncio.Lock = asyncio.Lock()
|
|
182
|
+
|
|
183
|
+
self.name_preprocessor = self.get_name_preprocessor()
|
|
184
|
+
|
|
185
|
+
def get_name_preprocessor(self) -> NamePreprocessor:
|
|
186
|
+
"""Get the name preprocessor based on the provider.
|
|
187
|
+
|
|
188
|
+
This will be used to correct the agent name and tool name. (mostly tool name)
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
NamePreprocessor: The name preprocessor for the model.
|
|
192
|
+
"""
|
|
193
|
+
return NamePreprocessor(self.model_provider)
|
|
194
|
+
|
|
195
|
+
@property
|
|
196
|
+
def model_provider(self) -> str:
|
|
197
|
+
"""Get the provider of the model with simplified logic.
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
str: The provider of the model.
|
|
201
|
+
"""
|
|
202
|
+
if hasattr(self, "lm_invoker") and self.lm_invoker is not None:
|
|
203
|
+
return self.lm_invoker.model_provider
|
|
204
|
+
|
|
205
|
+
if hasattr(self, "model") and self.model is not None:
|
|
206
|
+
return self._detect_provider_from_model(self.model)
|
|
207
|
+
|
|
208
|
+
return "unknown"
|
|
209
|
+
|
|
210
|
+
def _detect_provider_from_model(self, model: Any) -> str:
|
|
211
|
+
"""Detect provider from model object.
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
model: The model object.
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
str: The provider of the model.
|
|
218
|
+
"""
|
|
219
|
+
if isinstance(model, str):
|
|
220
|
+
return self._detect_provider_from_string(model)
|
|
221
|
+
|
|
222
|
+
if hasattr(model, "__class__"):
|
|
223
|
+
return self._detect_provider_from_class(model.__class__.__name__)
|
|
224
|
+
|
|
225
|
+
return "unknown"
|
|
226
|
+
|
|
227
|
+
def _detect_provider_from_string(self, model_str: str) -> str:
|
|
228
|
+
"""Detect provider from model string.
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
model_str: The model string.
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
str: The provider of the model.
|
|
235
|
+
"""
|
|
236
|
+
model_lower = model_str.lower()
|
|
237
|
+
|
|
238
|
+
if model_lower.startswith(("gemini", "google")):
|
|
239
|
+
return "google"
|
|
240
|
+
|
|
241
|
+
return model_str.split("/")[0] if "/" in model_str else model_str
|
|
242
|
+
|
|
243
|
+
def _detect_provider_from_class(self, class_name: str) -> str:
|
|
244
|
+
"""Detect provider from class name.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
class_name: The class name.
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
str: The provider of the model.
|
|
251
|
+
"""
|
|
252
|
+
class_name_lower = class_name.lower()
|
|
253
|
+
|
|
254
|
+
provider_mappings = {"openai": "openai", "google": "google", "vertex": "google", "anthropic": "anthropic"}
|
|
255
|
+
|
|
256
|
+
for keyword, provider in provider_mappings.items():
|
|
257
|
+
if keyword in class_name_lower:
|
|
258
|
+
return provider
|
|
259
|
+
|
|
260
|
+
return "unknown"
|
|
261
|
+
|
|
262
|
+
def _process_config(
|
|
263
|
+
self, config: BaseAgentConfig | dict[str, Any] | None, tools: list[Any] | None = None
|
|
264
|
+
) -> BaseAgentConfig | None:
|
|
265
|
+
"""Process config parameter to ensure it's a BaseAgentConfig instance.
|
|
266
|
+
|
|
267
|
+
Args:
|
|
268
|
+
config: Configuration parameter that can be dict, BaseAgentConfig, or None.
|
|
269
|
+
tools: List of tools to include in config if not already present.
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
BaseAgentConfig instance or None.
|
|
273
|
+
"""
|
|
274
|
+
if config is None:
|
|
275
|
+
if tools:
|
|
276
|
+
return AgentConfig(tools=tools)
|
|
277
|
+
return None
|
|
278
|
+
|
|
279
|
+
if isinstance(config, BaseAgentConfig):
|
|
280
|
+
if tools and not config.tools:
|
|
281
|
+
config.tools = tools
|
|
282
|
+
return config
|
|
283
|
+
|
|
284
|
+
if isinstance(config, dict):
|
|
285
|
+
config_dict = config.copy()
|
|
286
|
+
|
|
287
|
+
if tools and "tools" not in config_dict:
|
|
288
|
+
config_dict["tools"] = tools
|
|
289
|
+
|
|
290
|
+
if "lm_hyperparameters" in config_dict and "default_hyperparameters" not in config_dict:
|
|
291
|
+
config_dict["default_hyperparameters"] = config_dict.pop("lm_hyperparameters")
|
|
292
|
+
|
|
293
|
+
return AgentConfig(**config_dict)
|
|
294
|
+
|
|
295
|
+
raise TypeError(f"Config must be BaseAgentConfig, dict, or None, got {type(config)}")
|
|
296
|
+
|
|
297
|
+
def _extract_credentials_from_config(
|
|
298
|
+
self, config: BaseAgentConfig | None
|
|
299
|
+
) -> tuple[CredentialType, str | dict[str, Any] | None]:
|
|
300
|
+
"""Extract and auto-detect credentials from config with ultra-simple logic.
|
|
301
|
+
|
|
302
|
+
This method supports multiple credential formats with automatic type detection:
|
|
303
|
+
- New lm_credentials field: Auto-detects type based on content
|
|
304
|
+
- Legacy lm_api_key field: For backward compatibility
|
|
305
|
+
|
|
306
|
+
Auto-detection logic:
|
|
307
|
+
- Dict: Passed through as-is (CredentialType.DICT)
|
|
308
|
+
- String + file exists: Treated as file path (CredentialType.FILE)
|
|
309
|
+
- String + file doesn't exist: Treated as API key (CredentialType.API_KEY)
|
|
310
|
+
|
|
311
|
+
Args:
|
|
312
|
+
config: Configuration object.
|
|
313
|
+
|
|
314
|
+
Returns:
|
|
315
|
+
Tuple containing:
|
|
316
|
+
- credential_type: CredentialType enum value
|
|
317
|
+
- credentials: The extracted credentials or None if not found
|
|
318
|
+
"""
|
|
319
|
+
if not config:
|
|
320
|
+
return CredentialType.API_KEY, None
|
|
321
|
+
|
|
322
|
+
if hasattr(config, "lm_credentials") and config.lm_credentials is not None:
|
|
323
|
+
detected_type, formatted_creds = self._auto_detect_credential_type(config.lm_credentials)
|
|
324
|
+
return detected_type, formatted_creds
|
|
325
|
+
|
|
326
|
+
if hasattr(config, "lm_api_key") and config.lm_api_key:
|
|
327
|
+
warn(
|
|
328
|
+
(
|
|
329
|
+
"The lm_api_key is deprecated as of version 0.5.0. "
|
|
330
|
+
"Use lm_credentials instead which supports auto-detection of API keys, "
|
|
331
|
+
"file paths, and dictionary credentials."
|
|
332
|
+
),
|
|
333
|
+
DeprecationWarning,
|
|
334
|
+
stacklevel=2,
|
|
335
|
+
)
|
|
336
|
+
return CredentialType.API_KEY, config.lm_api_key
|
|
337
|
+
|
|
338
|
+
return CredentialType.API_KEY, None
|
|
339
|
+
|
|
340
|
+
def _auto_detect_credential_type(self, credentials: Any) -> tuple[CredentialType, Any]:
|
|
341
|
+
"""Automatically detect credential type using simple file existence rules.
|
|
342
|
+
|
|
343
|
+
This method uses ultra-simple detection logic:
|
|
344
|
+
1. If credentials is dict -> CredentialType.DICT (Bedrock, LangChain credentials)
|
|
345
|
+
2. If credentials is string and exists on disk -> CredentialType.FILE
|
|
346
|
+
3. Everything else -> CredentialType.API_KEY (simple fallback)
|
|
347
|
+
|
|
348
|
+
Args:
|
|
349
|
+
credentials: Raw credentials from config.
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
Tuple containing:
|
|
353
|
+
- credential_type: CredentialType enum value
|
|
354
|
+
- formatted_credentials: The credentials in the detected format
|
|
355
|
+
"""
|
|
356
|
+
if isinstance(credentials, dict):
|
|
357
|
+
return CredentialType.DICT, credentials
|
|
358
|
+
|
|
359
|
+
if isinstance(credentials, str):
|
|
360
|
+
if not credentials.strip():
|
|
361
|
+
return CredentialType.API_KEY, credentials
|
|
362
|
+
|
|
363
|
+
try:
|
|
364
|
+
path = Path(credentials)
|
|
365
|
+
if path.exists():
|
|
366
|
+
return CredentialType.FILE, credentials
|
|
367
|
+
except (ValueError, OSError):
|
|
368
|
+
pass
|
|
369
|
+
|
|
370
|
+
return CredentialType.API_KEY, credentials
|
|
371
|
+
|
|
372
|
+
return CredentialType.API_KEY, str(credentials)
|
|
373
|
+
|
|
374
|
+
@property
|
|
375
|
+
def mcp_config(self) -> dict[str, dict[str, Any]]:
|
|
376
|
+
"""Read-only view of MCP configuration.
|
|
377
|
+
|
|
378
|
+
Returns a copy to prevent direct mutation; use add_mcp_server() for changes.
|
|
379
|
+
"""
|
|
380
|
+
return self._mcp_config.copy()
|
|
381
|
+
|
|
382
|
+
@mcp_config.setter
|
|
383
|
+
def mcp_config(self, value: dict[str, dict[str, Any]]) -> None:
|
|
384
|
+
"""Set MCP configuration and maintain synchronization.
|
|
385
|
+
|
|
386
|
+
Automatically resets initialization flag and recreates client to ensure consistency.
|
|
387
|
+
Prefer using add_mcp_server() for proper validation.
|
|
388
|
+
|
|
389
|
+
Args:
|
|
390
|
+
value (dict[str, dict[str, Any]]): The MCP configuration to set.
|
|
391
|
+
"""
|
|
392
|
+
if not isinstance(value, dict):
|
|
393
|
+
raise ValueError("mcp_config must be a dict[str, dict[str, Any]]")
|
|
394
|
+
self._mcp_config = value.copy()
|
|
395
|
+
# Reset flag and recreate client object to maintain sync. This is lightweight
|
|
396
|
+
# (no connections created) and safe to perform synchronously. Actual session
|
|
397
|
+
# initialization remains lazy in the event loop via _ensure_mcp_tools_initialized().
|
|
398
|
+
self._mcp_tools_initialized = False
|
|
399
|
+
if self._mcp_config:
|
|
400
|
+
self._initialize_mcp_client()
|
|
401
|
+
else:
|
|
402
|
+
# Clear client for empty config
|
|
403
|
+
self.mcp_client = None
|
|
404
|
+
|
|
405
|
+
def _get_credentials(self, model: str | ModelId, config: BaseAgentConfig | None) -> str | dict[str, Any] | None:
|
|
406
|
+
"""Get credentials for the model with enhanced type support.
|
|
407
|
+
|
|
408
|
+
This method now supports multiple credential formats through the new
|
|
409
|
+
_extract_credentials_from_config method while maintaining backward compatibility.
|
|
410
|
+
|
|
411
|
+
Args:
|
|
412
|
+
model: Model identifier.
|
|
413
|
+
config: Configuration object.
|
|
414
|
+
|
|
415
|
+
Returns:
|
|
416
|
+
Credentials if found, None otherwise. Can be:
|
|
417
|
+
- str: For API keys or file paths
|
|
418
|
+
- dict: For structured credentials (Bedrock, LangChain)
|
|
419
|
+
"""
|
|
420
|
+
credentials = None
|
|
421
|
+
|
|
422
|
+
if config:
|
|
423
|
+
_, credentials = self._extract_credentials_from_config(config)
|
|
424
|
+
|
|
425
|
+
if not credentials:
|
|
426
|
+
credentials = CredentialsManager.get_credentials(model)
|
|
427
|
+
|
|
428
|
+
return credentials
|
|
429
|
+
|
|
430
|
+
def _extract_retry_config(self, config: BaseAgentConfig | None, use_default: bool = True) -> RetryConfig | None:
|
|
431
|
+
"""Extract and process retry config from agent config.
|
|
432
|
+
|
|
433
|
+
Args:
|
|
434
|
+
config: Configuration object.
|
|
435
|
+
use_default: If True, return a default RetryConfig when none is found.
|
|
436
|
+
|
|
437
|
+
Returns:
|
|
438
|
+
RetryConfig instance if found, default RetryConfig if use_default=True and none found, None otherwise.
|
|
439
|
+
"""
|
|
440
|
+
if config and isinstance(config, AgentConfig) and config.lm_retry_config:
|
|
441
|
+
if isinstance(config.lm_retry_config, dict):
|
|
442
|
+
return RetryConfig(**config.lm_retry_config)
|
|
443
|
+
return config.lm_retry_config
|
|
444
|
+
|
|
445
|
+
if use_default:
|
|
446
|
+
return DEFAULT_RETRY_CONFIG
|
|
447
|
+
|
|
448
|
+
return None
|
|
449
|
+
|
|
450
|
+
def _update_config_with_tools(
|
|
451
|
+
self, tools: list[Any], config: BaseAgentConfig | None = None
|
|
452
|
+
) -> dict[str, Any] | None:
|
|
453
|
+
"""Update config with tools if not already present and convert to dict for lm_invoker.
|
|
454
|
+
|
|
455
|
+
Args:
|
|
456
|
+
tools: List of tools.
|
|
457
|
+
config: Configuration object.
|
|
458
|
+
|
|
459
|
+
Returns:
|
|
460
|
+
Configuration dictionary for lm_invoker.
|
|
461
|
+
"""
|
|
462
|
+
if config is None:
|
|
463
|
+
config_dict = {"tools": tools} if tools else {}
|
|
464
|
+
config_dict["retry_config"] = self._extract_retry_config(config)
|
|
465
|
+
return config_dict
|
|
466
|
+
|
|
467
|
+
# Convert BaseAgentConfig to dict, excluding LM-specific fields
|
|
468
|
+
config_dict = config.model_dump(
|
|
469
|
+
exclude_none=True,
|
|
470
|
+
exclude=LM_EXCLUDE_FIELDS,
|
|
471
|
+
)
|
|
472
|
+
if isinstance(config, AgentConfig) and config.lm_hyperparameters:
|
|
473
|
+
config_dict["default_hyperparameters"] = config.lm_hyperparameters
|
|
474
|
+
|
|
475
|
+
config_dict["retry_config"] = self._extract_retry_config(config)
|
|
476
|
+
|
|
477
|
+
if tools:
|
|
478
|
+
config_dict["tools"] = tools
|
|
479
|
+
|
|
480
|
+
return config_dict if config_dict else None
|
|
481
|
+
|
|
482
|
+
def _finalize_lm_invoker_config(self, tools: list[Any], config: BaseAgentConfig | None) -> dict[str, Any]:
|
|
483
|
+
"""Finalize lm_invoker config by adding tools and output analytics.
|
|
484
|
+
|
|
485
|
+
Args:
|
|
486
|
+
tools: List of tools.
|
|
487
|
+
config: Configuration object for lm_invoker.
|
|
488
|
+
|
|
489
|
+
Returns:
|
|
490
|
+
Configuration dictionary for lm_invoker.
|
|
491
|
+
"""
|
|
492
|
+
processed_config = self._update_config_with_tools(tools, config)
|
|
493
|
+
if processed_config is None:
|
|
494
|
+
processed_config = {}
|
|
495
|
+
|
|
496
|
+
if OUTPUT_ANALYTICS_KEY not in processed_config:
|
|
497
|
+
processed_config[OUTPUT_ANALYTICS_KEY] = True
|
|
498
|
+
|
|
499
|
+
return processed_config
|
|
500
|
+
|
|
501
|
+
def _setup_lm_invoker_param(
|
|
502
|
+
self, model: str | ModelId, tools: list[Any], config: BaseAgentConfig | None = None
|
|
503
|
+
) -> tuple[ModelId | str, str | None, dict[str, Any] | None]:
|
|
504
|
+
"""Setup parameter for build_lm_invoker.
|
|
505
|
+
|
|
506
|
+
Args:
|
|
507
|
+
model (str | ModelId): The model identifier.
|
|
508
|
+
tools (list[Any]): List of tools.
|
|
509
|
+
config (BaseAgentConfig | None): Configuration object.
|
|
510
|
+
|
|
511
|
+
Returns:
|
|
512
|
+
- model_id: ModelId | str
|
|
513
|
+
- credentials: str | None
|
|
514
|
+
- config: dict[str, Any] | None
|
|
515
|
+
as tuple
|
|
516
|
+
"""
|
|
517
|
+
model_id: str | ModelId = model
|
|
518
|
+
credentials: str | None = None
|
|
519
|
+
processed_as_custom = False
|
|
520
|
+
|
|
521
|
+
if isinstance(model, str):
|
|
522
|
+
for prefix, provider in CUSTOM_PROVIDERS.items():
|
|
523
|
+
if model.startswith(prefix):
|
|
524
|
+
processed_as_custom = True
|
|
525
|
+
|
|
526
|
+
if not config or not isinstance(config, AgentConfig):
|
|
527
|
+
raise ValueError(f"AgentConfig is required for model '{model}'")
|
|
528
|
+
|
|
529
|
+
try:
|
|
530
|
+
model_id = ModelId.from_string(model)
|
|
531
|
+
except ValueError as e:
|
|
532
|
+
base_url = config.lm_base_url
|
|
533
|
+
if not base_url:
|
|
534
|
+
raise ValueError(f"lm_base_url in AgentConfig is required for model '{model}'") from e
|
|
535
|
+
|
|
536
|
+
model_name = model.removeprefix(prefix)
|
|
537
|
+
model_id = ModelId(provider=provider, name=model_name, path=base_url)
|
|
538
|
+
|
|
539
|
+
# Extract credentials using new enhanced method
|
|
540
|
+
_, credentials = self._extract_credentials_from_config(config)
|
|
541
|
+
break
|
|
542
|
+
|
|
543
|
+
if not processed_as_custom or credentials is None:
|
|
544
|
+
credentials = self._get_credentials(model_id, config)
|
|
545
|
+
|
|
546
|
+
processed_config = self._finalize_lm_invoker_config(tools, config)
|
|
547
|
+
return model_id, credentials, processed_config
|
|
548
|
+
|
|
549
|
+
def _process_model_parameter(
|
|
550
|
+
self, agent_name: str, model: Any | None, tools: list[Any], config: BaseAgentConfig | None = None
|
|
551
|
+
) -> tuple[Any | None, Any | None]:
|
|
552
|
+
"""Process the model parameter and determine lm_invoker and model attributes.
|
|
553
|
+
|
|
554
|
+
Args:
|
|
555
|
+
agent_name: The name of the agent (for logging).
|
|
556
|
+
model: The model parameter from initialization.
|
|
557
|
+
tools: List of tools for lm_invoker configuration.
|
|
558
|
+
config: Configuration object.
|
|
559
|
+
|
|
560
|
+
Returns:
|
|
561
|
+
Tuple of (lm_invoker, processed_model) where:
|
|
562
|
+
- lm_invoker: Built LM Invoker if model is string/ModelId or existing BaseLMInvoker, None otherwise
|
|
563
|
+
- processed_model: LangChain model if it's a BaseChatModel,
|
|
564
|
+
original model for other types, None if lm_invoker was created
|
|
565
|
+
or if model is None
|
|
566
|
+
"""
|
|
567
|
+
# If model is already an lm_invoker instance
|
|
568
|
+
if BaseLMInvoker and isinstance(model, BaseLMInvoker):
|
|
569
|
+
logger.debug(f"Agent '{agent_name}': Using provided LM Invoker: {model.__class__.__name__}")
|
|
570
|
+
return model, None
|
|
571
|
+
|
|
572
|
+
# Check if model is a string or ModelId - build lm_invoker
|
|
573
|
+
if isinstance(model, str) or (ModelId and isinstance(model, ModelId)):
|
|
574
|
+
if not build_lm_invoker:
|
|
575
|
+
logger.warning(
|
|
576
|
+
f"Agent '{agent_name}': gllm-inference not available, cannot build LM Invoker from {model}"
|
|
577
|
+
)
|
|
578
|
+
return None, model
|
|
579
|
+
|
|
580
|
+
logger.info(f"Agent '{agent_name}': Building LM Invoker from model identifier: {model}")
|
|
581
|
+
try:
|
|
582
|
+
model_id, credentials, preprocessed_config = self._setup_lm_invoker_param(model, tools, config)
|
|
583
|
+
|
|
584
|
+
lm_invoker = build_lm_invoker(
|
|
585
|
+
model_id=model_id,
|
|
586
|
+
credentials=credentials,
|
|
587
|
+
config=preprocessed_config,
|
|
588
|
+
)
|
|
589
|
+
return lm_invoker, None
|
|
590
|
+
except Exception as e:
|
|
591
|
+
logger.error(f"Agent '{agent_name}': Failed to build LM Invoker from {model}: {e}")
|
|
592
|
+
raise RuntimeError(f"Failed to build LM Invoker from model '{model}': {e}") from e
|
|
593
|
+
|
|
594
|
+
# If it's a LangChain model, use it directly
|
|
595
|
+
elif BaseChatModel and hasattr(model, "__class__") and issubclass(model.__class__, BaseChatModel):
|
|
596
|
+
logger.debug(f"Agent '{agent_name}': Using provided LangChain model: {model.__class__.__name__}")
|
|
597
|
+
return None, model
|
|
598
|
+
|
|
599
|
+
# If model is None, that's acceptable for some use cases
|
|
600
|
+
elif model is None:
|
|
601
|
+
logger.debug(f"Agent '{agent_name}': No model provided")
|
|
602
|
+
return None, None
|
|
603
|
+
|
|
604
|
+
# For any other type, store as model (e.g., Google ADK agent instance)
|
|
605
|
+
else:
|
|
606
|
+
logger.debug(f"Agent '{agent_name}': Using provided model of type: {type(model)}")
|
|
607
|
+
return None, model
|
|
608
|
+
|
|
609
|
+
def to_a2a(self, agent_card: AgentCard, **kwargs: Any) -> Starlette:
|
|
610
|
+
"""Converts the agent to an A2A-compatible ASGI application.
|
|
611
|
+
|
|
612
|
+
This implementation provides a base setup for A2A server components.
|
|
613
|
+
Subclasses can override this method if they need custom executor
|
|
614
|
+
or task store implementations.
|
|
615
|
+
|
|
616
|
+
Args:
|
|
617
|
+
agent_card: The agent card to use for the A2A application.
|
|
618
|
+
**kwargs: Additional keyword arguments for ASGI application configuration.
|
|
619
|
+
|
|
620
|
+
Returns:
|
|
621
|
+
A Starlette ASGI application that can be used with any ASGI server.
|
|
622
|
+
"""
|
|
623
|
+
# Use provided task store or create default in-memory store
|
|
624
|
+
task_store = kwargs.get("task_store", InMemoryTaskStore())
|
|
625
|
+
|
|
626
|
+
# Create default request handler if not provided
|
|
627
|
+
try:
|
|
628
|
+
executor_cls = _get_executor_class_for_agent(self)
|
|
629
|
+
except KeyError as exc:
|
|
630
|
+
raise ValueError(f"No A2A executor registered for agent type '{self.__class__.__name__}'.") from exc
|
|
631
|
+
|
|
632
|
+
agent_executor = executor_cls(self)
|
|
633
|
+
request_handler = kwargs.get(
|
|
634
|
+
"request_handler",
|
|
635
|
+
DefaultRequestHandler(
|
|
636
|
+
agent_executor=agent_executor,
|
|
637
|
+
task_store=task_store,
|
|
638
|
+
),
|
|
639
|
+
)
|
|
640
|
+
|
|
641
|
+
# Create A2A application
|
|
642
|
+
a2a_app = A2AStarletteApplication(agent_card=agent_card, http_handler=request_handler)
|
|
643
|
+
|
|
644
|
+
# Get base routes from A2A app
|
|
645
|
+
routes = a2a_app.routes()
|
|
646
|
+
|
|
647
|
+
# Add any additional routes if provided
|
|
648
|
+
if "routes" in kwargs:
|
|
649
|
+
routes.extend(kwargs["routes"])
|
|
650
|
+
|
|
651
|
+
# Create and return Starlette application
|
|
652
|
+
return Starlette(routes=routes)
|
|
653
|
+
|
|
654
|
+
@classmethod
|
|
655
|
+
def discover_agents(cls, a2a_config: A2AClientConfig, **kwargs: Any) -> list[AgentCard]:
|
|
656
|
+
"""Discover agents from the URLs specified in a2a_config.discovery_urls.
|
|
657
|
+
|
|
658
|
+
This concrete implementation fetches and parses .well-known/agent.json
|
|
659
|
+
from each discovery URL to build a list of available agents.
|
|
660
|
+
|
|
661
|
+
Args:
|
|
662
|
+
a2a_config: Configuration containing discovery URLs and other A2A settings.
|
|
663
|
+
**kwargs: Additional keyword arguments (unused in this implementation).
|
|
664
|
+
|
|
665
|
+
Returns:
|
|
666
|
+
A list of AgentCard objects representing discovered agents.
|
|
667
|
+
"""
|
|
668
|
+
discovered_cards: list[AgentCard] = []
|
|
669
|
+
|
|
670
|
+
if not a2a_config or not a2a_config.discovery_urls:
|
|
671
|
+
logger.debug("No discovery URLs configured")
|
|
672
|
+
return discovered_cards
|
|
673
|
+
|
|
674
|
+
httpx_client_options = {}
|
|
675
|
+
if a2a_config.httpx_client_options:
|
|
676
|
+
httpx_client_options = a2a_config.httpx_client_options.model_dump(exclude_none=True)
|
|
677
|
+
|
|
678
|
+
with httpx.Client(**httpx_client_options) as client:
|
|
679
|
+
for base_url in a2a_config.discovery_urls:
|
|
680
|
+
try:
|
|
681
|
+
agent_json_url = f"{base_url.rstrip('/')}/.well-known/agent.json"
|
|
682
|
+
|
|
683
|
+
response = client.get(agent_json_url)
|
|
684
|
+
response.raise_for_status()
|
|
685
|
+
|
|
686
|
+
try:
|
|
687
|
+
agent_card = AgentCard.model_validate(response.json())
|
|
688
|
+
discovered_cards.append(agent_card)
|
|
689
|
+
logger.info(f"Successfully discovered agent '{agent_card.name}' at {base_url}")
|
|
690
|
+
except Exception as parse_error:
|
|
691
|
+
logger.error(f"Error parsing agent card from {agent_json_url}: {parse_error}")
|
|
692
|
+
continue
|
|
693
|
+
|
|
694
|
+
except Exception as e:
|
|
695
|
+
logger.error(f"Unexpected error discovering agents from {base_url}: {e}")
|
|
696
|
+
continue
|
|
697
|
+
|
|
698
|
+
agent_list = "\n".join([f"{i + 1}. {agent.name}" for i, agent in enumerate(discovered_cards)])
|
|
699
|
+
logger.info(f"Discovered agents ({len(discovered_cards)} Agents): \n{agent_list}")
|
|
700
|
+
return discovered_cards
|
|
701
|
+
|
|
702
|
+
def send_to_agent(
|
|
703
|
+
self,
|
|
704
|
+
agent_card: AgentCard,
|
|
705
|
+
message: str | dict[str, Any],
|
|
706
|
+
**kwargs: Any,
|
|
707
|
+
) -> dict[str, Any]:
|
|
708
|
+
"""Synchronously sends a message to another agent using the A2A protocol.
|
|
709
|
+
|
|
710
|
+
This method is a synchronous wrapper around asend_to_agent. It handles the creation
|
|
711
|
+
of an event loop if one doesn't exist, and manages the asynchronous call internally.
|
|
712
|
+
|
|
713
|
+
Args:
|
|
714
|
+
agent_card: The AgentCard instance containing the target agent's details including
|
|
715
|
+
URL, authentication requirements, and capabilities.
|
|
716
|
+
message: The message to send to the agent. Can be either a string for simple text
|
|
717
|
+
messages or a dictionary for structured data.
|
|
718
|
+
**kwargs: Additional keyword arguments passed to asend_to_agent.
|
|
719
|
+
|
|
720
|
+
Returns:
|
|
721
|
+
A dictionary containing the response details:
|
|
722
|
+
- status (str): 'success' or 'error'
|
|
723
|
+
- content (str): Extracted text content from the response
|
|
724
|
+
- task_id (str, optional): ID of the created/updated task
|
|
725
|
+
- task_state (str, optional): Current state of the task
|
|
726
|
+
- raw_response (str): Complete JSON response from the A2A client
|
|
727
|
+
- error_type (str, optional): Type of error if status is 'error'
|
|
728
|
+
- message (str, optional): Error message if status is 'error'
|
|
729
|
+
|
|
730
|
+
Raises:
|
|
731
|
+
RuntimeError: If called from within an existing event loop or if asend_to_agent
|
|
732
|
+
encounters an unhandled exception.
|
|
733
|
+
"""
|
|
734
|
+
try:
|
|
735
|
+
return A2AConnector.send_to_agent(agent_card, message, **kwargs)
|
|
736
|
+
except RuntimeError as e:
|
|
737
|
+
raise RuntimeError(f"Agent '{self.name}': Error in sync 'send_to_agent'. Original error: {e}") from e
|
|
738
|
+
|
|
739
|
+
async def asend_to_agent(
|
|
740
|
+
self,
|
|
741
|
+
agent_card: AgentCard,
|
|
742
|
+
message: str | dict[str, Any],
|
|
743
|
+
**kwargs: Any,
|
|
744
|
+
) -> dict[str, Any]:
|
|
745
|
+
"""Asynchronously sends a message to another agent using the A2A protocol.
|
|
746
|
+
|
|
747
|
+
This method handles the core A2A communication logic, creating and sending properly
|
|
748
|
+
formatted A2A messages and processing the responses.
|
|
749
|
+
|
|
750
|
+
Args:
|
|
751
|
+
agent_card: The AgentCard instance containing the target agent's details including
|
|
752
|
+
URL, authentication requirements, and capabilities.
|
|
753
|
+
message: The message to send to the agent. Can be either a string for simple text
|
|
754
|
+
messages or a dictionary for structured data.
|
|
755
|
+
**kwargs: Additional keyword arguments.
|
|
756
|
+
|
|
757
|
+
Returns:
|
|
758
|
+
A dictionary containing the response details:
|
|
759
|
+
- status (str): 'success' or 'error'
|
|
760
|
+
- content (str): Extracted text content from the response
|
|
761
|
+
- task_id (str, optional): ID of the created/updated task
|
|
762
|
+
- task_state (str, optional): Current state of the task
|
|
763
|
+
- raw_response (str): Complete JSON response from the A2A client
|
|
764
|
+
- error_type (str, optional): Type of error if status is 'error'
|
|
765
|
+
- message (str, optional): Error message if status is 'error'
|
|
766
|
+
|
|
767
|
+
Raises:
|
|
768
|
+
httpx.HTTPError: If there's an HTTP-related error during the request.
|
|
769
|
+
Exception: For any other unexpected errors during message sending or processing.
|
|
770
|
+
"""
|
|
771
|
+
return await A2AConnector.asend_to_agent(agent_card, message, **kwargs)
|
|
772
|
+
|
|
773
|
+
async def astream_to_agent(
|
|
774
|
+
self,
|
|
775
|
+
agent_card: AgentCard,
|
|
776
|
+
message: str | dict[str, Any],
|
|
777
|
+
**kwargs: Any,
|
|
778
|
+
) -> AsyncGenerator[dict[str, Any], None]:
|
|
779
|
+
"""Asynchronously sends a streaming message to another agent using the A2A protocol.
|
|
780
|
+
|
|
781
|
+
This method supports streaming responses from the target agent, yielding chunks of
|
|
782
|
+
the response as they become available. It handles various types of streaming events
|
|
783
|
+
including task status updates, artifact updates, and message parts.
|
|
784
|
+
|
|
785
|
+
Args:
|
|
786
|
+
agent_card: The AgentCard instance containing the target agent's details including
|
|
787
|
+
URL, authentication requirements, and capabilities.
|
|
788
|
+
message: The message to send to the agent. Can be either a string for simple text
|
|
789
|
+
messages or a dictionary for structured data.
|
|
790
|
+
**kwargs: Additional keyword arguments.
|
|
791
|
+
|
|
792
|
+
Yields:
|
|
793
|
+
Dictionaries containing streaming response chunks:
|
|
794
|
+
For successful chunks:
|
|
795
|
+
- status (str): 'success'
|
|
796
|
+
- content (str): Extracted text content from the chunk
|
|
797
|
+
- task_id (str): ID of the associated task
|
|
798
|
+
- task_state (str): Current state of the task
|
|
799
|
+
- final (bool): Whether this is the final chunk
|
|
800
|
+
- artifact_name (str, optional): Name of the artifact if chunk is an artifact update
|
|
801
|
+
For error chunks:
|
|
802
|
+
- status (str): 'error'
|
|
803
|
+
- error_type (str): Type of error encountered
|
|
804
|
+
- message (str): Error description
|
|
805
|
+
|
|
806
|
+
Raises:
|
|
807
|
+
httpx.HTTPError: If there's an HTTP-related error during the streaming request.
|
|
808
|
+
Exception: For any other unexpected errors during message streaming or processing.
|
|
809
|
+
"""
|
|
810
|
+
# Default to the richer A2A event payload so integrations (e.g. HITL streaming) receive
|
|
811
|
+
async for chunk in A2AConnector.astream_to_agent(agent_card, message, **kwargs):
|
|
812
|
+
yield chunk
|
|
813
|
+
|
|
814
|
+
@staticmethod
|
|
815
|
+
def format_agent_description(agent_card: AgentCard) -> str:
|
|
816
|
+
"""Format the description of an agent card including skills information.
|
|
817
|
+
|
|
818
|
+
Args:
|
|
819
|
+
agent_card (AgentCard): The agent card to format.
|
|
820
|
+
|
|
821
|
+
Returns:
|
|
822
|
+
str: The formatted description including skills.
|
|
823
|
+
"""
|
|
824
|
+
# Start with the base description
|
|
825
|
+
formatted_description = agent_card.description or ""
|
|
826
|
+
|
|
827
|
+
if agent_card.skills:
|
|
828
|
+
formatted_description += "\n\nSkills:"
|
|
829
|
+
for skill in agent_card.skills:
|
|
830
|
+
formatted_description += f"\n• {skill.name}: {skill.description}"
|
|
831
|
+
|
|
832
|
+
if skill.tags:
|
|
833
|
+
tags_str = ", ".join(skill.tags)
|
|
834
|
+
formatted_description += f" (Tags: {tags_str})"
|
|
835
|
+
|
|
836
|
+
if skill.examples:
|
|
837
|
+
formatted_description += "\n Examples:"
|
|
838
|
+
for example in skill.examples:
|
|
839
|
+
formatted_description += f"\n - {example}"
|
|
840
|
+
|
|
841
|
+
return formatted_description
|
|
842
|
+
|
|
843
|
+
def add_mcp_server(self, mcp_config: dict[str, dict[str, Any]]) -> None:
|
|
844
|
+
"""Adds MCP servers to the agent.
|
|
845
|
+
|
|
846
|
+
Args:
|
|
847
|
+
mcp_config: A dictionary containing MCP server configurations.
|
|
848
|
+
|
|
849
|
+
Raises:
|
|
850
|
+
ValueError: If the MCP configuration is empty or None.
|
|
851
|
+
KeyError: If a server with the same name already exists in the MCP configuration.
|
|
852
|
+
"""
|
|
853
|
+
if not mcp_config:
|
|
854
|
+
raise ValueError("MCP configuration must not be empty or None")
|
|
855
|
+
|
|
856
|
+
for server_name, config in mcp_config.items():
|
|
857
|
+
if server_name in self.mcp_config:
|
|
858
|
+
raise KeyError(f"Server '{server_name}' already exists in MCP configuration")
|
|
859
|
+
if not isinstance(config, dict):
|
|
860
|
+
raise ValueError(f"Configuration for server '{server_name}' must be a dictionary")
|
|
861
|
+
if not config:
|
|
862
|
+
raise ValueError(f"Configuration for server '{server_name}' must not be empty")
|
|
863
|
+
|
|
864
|
+
# Validate that either URL or command is present
|
|
865
|
+
required_keys = ["url"] if "url" in config else ["command"]
|
|
866
|
+
if not any(key in config for key in required_keys):
|
|
867
|
+
raise ValueError(
|
|
868
|
+
f"Server '{server_name}' missing required configuration: must have either 'url' or 'command'"
|
|
869
|
+
)
|
|
870
|
+
|
|
871
|
+
self._mcp_config.update(mcp_config)
|
|
872
|
+
# Initialize/recreate MCP client object (lightweight, no sessions yet)
|
|
873
|
+
# This satisfies existing unit tests and keeps lazy async session init intact.
|
|
874
|
+
self._initialize_mcp_client()
|
|
875
|
+
# Mark that we need to initialize MCP tools (lazy initialization)
|
|
876
|
+
self._mcp_tools_initialized = False
|
|
877
|
+
|
|
878
|
+
def _initialize_mcp_client(self) -> None:
|
|
879
|
+
"""Initialize/recreate MCP client with current config.
|
|
880
|
+
|
|
881
|
+
To be implemented by child agents as each agent type has its own MCP client.
|
|
882
|
+
For agents that don't support MCP (e.g., LangflowAgent), this can be a no-op.
|
|
883
|
+
"""
|
|
884
|
+
# Default implementation is no-op for agents that don't support MCP
|
|
885
|
+
pass
|
|
886
|
+
|
|
887
|
+
def _set_mcp_client_safely(self, new_client: BaseMCPClient | None) -> None:
|
|
888
|
+
"""Replace current MCP client with cleanup of the previous instance.
|
|
889
|
+
|
|
890
|
+
This helper ensures we don't leak persistent sessions when recreating the client.
|
|
891
|
+
It attempts asynchronous cleanup when an event loop is running, otherwise performs
|
|
892
|
+
a synchronous cleanup using asyncio.run.
|
|
893
|
+
|
|
894
|
+
Args:
|
|
895
|
+
new_client (BaseMCPClient | None): The new MCP client to set, or None to clear the current client.
|
|
896
|
+
"""
|
|
897
|
+
prev_client = self.mcp_client
|
|
898
|
+
self.mcp_client = new_client
|
|
899
|
+
if prev_client is not None and prev_client is not new_client:
|
|
900
|
+
try:
|
|
901
|
+
loop = asyncio.get_running_loop()
|
|
902
|
+
loop.create_task(prev_client.cleanup())
|
|
903
|
+
except RuntimeError:
|
|
904
|
+
try:
|
|
905
|
+
asyncio.run(prev_client.cleanup())
|
|
906
|
+
except Exception:
|
|
907
|
+
# Best-effort cleanup; ignore failures
|
|
908
|
+
pass
|
|
909
|
+
|
|
910
|
+
async def _ensure_mcp_tools_initialized(self) -> None:
|
|
911
|
+
"""Ensure MCP tools are initialized lazily (one-time operation).
|
|
912
|
+
|
|
913
|
+
This method ensures MCP tools are initialized only once during the first run,
|
|
914
|
+
avoiding event loop issues by doing initialization in the correct event loop.
|
|
915
|
+
"""
|
|
916
|
+
if self._mcp_tools_initialized:
|
|
917
|
+
return
|
|
918
|
+
|
|
919
|
+
# Prevent concurrent initialization across simultaneous runs
|
|
920
|
+
async with self._mcp_init_lock:
|
|
921
|
+
if self._mcp_tools_initialized:
|
|
922
|
+
return
|
|
923
|
+
|
|
924
|
+
await self._handle_mcp_client_initialization()
|
|
925
|
+
|
|
926
|
+
# If we have a client at this point, proceed with tool registration
|
|
927
|
+
if self.mcp_client is not None:
|
|
928
|
+
await self._perform_mcp_tool_registration()
|
|
929
|
+
|
|
930
|
+
async def _handle_mcp_client_initialization(self) -> None:
|
|
931
|
+
"""Handle MCP client initialization based on current state.
|
|
932
|
+
|
|
933
|
+
Sets the initialization flag appropriately for different scenarios.
|
|
934
|
+
"""
|
|
935
|
+
if self.mcp_client is not None:
|
|
936
|
+
return # Client already exists
|
|
937
|
+
|
|
938
|
+
if not self._mcp_config:
|
|
939
|
+
# No config at all - skip and mark as initialized to avoid repeated logs
|
|
940
|
+
logger.debug(f"Agent '{self.name}': MCP client not configured; skipping MCP tool registration")
|
|
941
|
+
self._mcp_tools_initialized = True
|
|
942
|
+
return
|
|
943
|
+
|
|
944
|
+
# Config exists but no client - try to initialize
|
|
945
|
+
self._initialize_mcp_client()
|
|
946
|
+
if self.mcp_client is None:
|
|
947
|
+
# Still no client after init - agent type doesn't support MCP
|
|
948
|
+
logger.warning(f"Agent '{self.name}': MCP config present but agent type doesn't support MCP")
|
|
949
|
+
|
|
950
|
+
async def _perform_mcp_tool_registration(self) -> None:
|
|
951
|
+
"""Perform the actual MCP tool registration.
|
|
952
|
+
|
|
953
|
+
Raises:
|
|
954
|
+
RuntimeError: If tool registration fails
|
|
955
|
+
"""
|
|
956
|
+
try:
|
|
957
|
+
await self._register_mcp_tools()
|
|
958
|
+
self._mcp_tools_initialized = True
|
|
959
|
+
except Exception as e:
|
|
960
|
+
logger.error(f"Agent '{self.name}': Failed to initialize MCP tools: {e}", exc_info=True)
|
|
961
|
+
raise RuntimeError(f"Agent '{self.name}': MCP tool initialization failed: {e}") from e
|
|
962
|
+
|
|
963
|
+
async def _register_mcp_tools(self) -> None:
|
|
964
|
+
"""Register MCP tools with the agent.
|
|
965
|
+
|
|
966
|
+
To be implemented by child agents as each agent type has its own way
|
|
967
|
+
of registering tools. For agents that don't support MCP, this can be a no-op.
|
|
968
|
+
"""
|
|
969
|
+
# Default implementation is no-op for agents that don't support MCP
|
|
970
|
+
pass
|