aip-agents-binary 0.5.20__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.
Files changed (280) hide show
  1. aip_agents/__init__.py +65 -0
  2. aip_agents/a2a/__init__.py +19 -0
  3. aip_agents/a2a/server/__init__.py +10 -0
  4. aip_agents/a2a/server/base_executor.py +1086 -0
  5. aip_agents/a2a/server/google_adk_executor.py +198 -0
  6. aip_agents/a2a/server/langflow_executor.py +180 -0
  7. aip_agents/a2a/server/langgraph_executor.py +270 -0
  8. aip_agents/a2a/types.py +232 -0
  9. aip_agents/agent/__init__.py +27 -0
  10. aip_agents/agent/base_agent.py +970 -0
  11. aip_agents/agent/base_langgraph_agent.py +2942 -0
  12. aip_agents/agent/google_adk_agent.py +926 -0
  13. aip_agents/agent/google_adk_constants.py +6 -0
  14. aip_agents/agent/hitl/__init__.py +24 -0
  15. aip_agents/agent/hitl/config.py +28 -0
  16. aip_agents/agent/hitl/langgraph_hitl_mixin.py +515 -0
  17. aip_agents/agent/hitl/manager.py +532 -0
  18. aip_agents/agent/hitl/models.py +18 -0
  19. aip_agents/agent/hitl/prompt/__init__.py +9 -0
  20. aip_agents/agent/hitl/prompt/base.py +42 -0
  21. aip_agents/agent/hitl/prompt/deferred.py +73 -0
  22. aip_agents/agent/hitl/registry.py +149 -0
  23. aip_agents/agent/interface.py +138 -0
  24. aip_agents/agent/interfaces.py +65 -0
  25. aip_agents/agent/langflow_agent.py +464 -0
  26. aip_agents/agent/langgraph_memory_enhancer_agent.py +433 -0
  27. aip_agents/agent/langgraph_react_agent.py +2514 -0
  28. aip_agents/agent/system_instruction_context.py +34 -0
  29. aip_agents/clients/__init__.py +10 -0
  30. aip_agents/clients/langflow/__init__.py +10 -0
  31. aip_agents/clients/langflow/client.py +477 -0
  32. aip_agents/clients/langflow/types.py +18 -0
  33. aip_agents/constants.py +23 -0
  34. aip_agents/credentials/manager.py +132 -0
  35. aip_agents/examples/__init__.py +5 -0
  36. aip_agents/examples/compare_streaming_client.py +783 -0
  37. aip_agents/examples/compare_streaming_server.py +142 -0
  38. aip_agents/examples/demo_memory_recall.py +401 -0
  39. aip_agents/examples/hello_world_a2a_google_adk_client.py +49 -0
  40. aip_agents/examples/hello_world_a2a_google_adk_client_agent.py +48 -0
  41. aip_agents/examples/hello_world_a2a_google_adk_client_streaming.py +60 -0
  42. aip_agents/examples/hello_world_a2a_google_adk_server.py +79 -0
  43. aip_agents/examples/hello_world_a2a_langchain_client.py +39 -0
  44. aip_agents/examples/hello_world_a2a_langchain_client_agent.py +39 -0
  45. aip_agents/examples/hello_world_a2a_langchain_client_lm_invoker.py +37 -0
  46. aip_agents/examples/hello_world_a2a_langchain_client_streaming.py +41 -0
  47. aip_agents/examples/hello_world_a2a_langchain_reference_client_streaming.py +60 -0
  48. aip_agents/examples/hello_world_a2a_langchain_reference_server.py +105 -0
  49. aip_agents/examples/hello_world_a2a_langchain_server.py +79 -0
  50. aip_agents/examples/hello_world_a2a_langchain_server_lm_invoker.py +78 -0
  51. aip_agents/examples/hello_world_a2a_langflow_client.py +83 -0
  52. aip_agents/examples/hello_world_a2a_langflow_server.py +82 -0
  53. aip_agents/examples/hello_world_a2a_langgraph_artifact_client.py +73 -0
  54. aip_agents/examples/hello_world_a2a_langgraph_artifact_client_streaming.py +76 -0
  55. aip_agents/examples/hello_world_a2a_langgraph_artifact_server.py +92 -0
  56. aip_agents/examples/hello_world_a2a_langgraph_client.py +54 -0
  57. aip_agents/examples/hello_world_a2a_langgraph_client_agent.py +54 -0
  58. aip_agents/examples/hello_world_a2a_langgraph_client_agent_lm_invoker.py +32 -0
  59. aip_agents/examples/hello_world_a2a_langgraph_client_streaming.py +50 -0
  60. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_lm_invoker.py +44 -0
  61. aip_agents/examples/hello_world_a2a_langgraph_client_streaming_tool_streaming.py +92 -0
  62. aip_agents/examples/hello_world_a2a_langgraph_server.py +84 -0
  63. aip_agents/examples/hello_world_a2a_langgraph_server_lm_invoker.py +79 -0
  64. aip_agents/examples/hello_world_a2a_langgraph_server_tool_streaming.py +132 -0
  65. aip_agents/examples/hello_world_a2a_mcp_langgraph.py +196 -0
  66. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_client.py +244 -0
  67. aip_agents/examples/hello_world_a2a_three_level_agent_hierarchy_server.py +251 -0
  68. aip_agents/examples/hello_world_a2a_with_metadata_langchain_client.py +57 -0
  69. aip_agents/examples/hello_world_a2a_with_metadata_langchain_server_lm_invoker.py +80 -0
  70. aip_agents/examples/hello_world_google_adk.py +41 -0
  71. aip_agents/examples/hello_world_google_adk_mcp_http.py +34 -0
  72. aip_agents/examples/hello_world_google_adk_mcp_http_stream.py +40 -0
  73. aip_agents/examples/hello_world_google_adk_mcp_sse.py +44 -0
  74. aip_agents/examples/hello_world_google_adk_mcp_sse_stream.py +48 -0
  75. aip_agents/examples/hello_world_google_adk_mcp_stdio.py +44 -0
  76. aip_agents/examples/hello_world_google_adk_mcp_stdio_stream.py +48 -0
  77. aip_agents/examples/hello_world_google_adk_stream.py +44 -0
  78. aip_agents/examples/hello_world_langchain.py +28 -0
  79. aip_agents/examples/hello_world_langchain_lm_invoker.py +15 -0
  80. aip_agents/examples/hello_world_langchain_mcp_http.py +34 -0
  81. aip_agents/examples/hello_world_langchain_mcp_http_interactive.py +130 -0
  82. aip_agents/examples/hello_world_langchain_mcp_http_stream.py +42 -0
  83. aip_agents/examples/hello_world_langchain_mcp_multi_server.py +155 -0
  84. aip_agents/examples/hello_world_langchain_mcp_sse.py +34 -0
  85. aip_agents/examples/hello_world_langchain_mcp_sse_stream.py +40 -0
  86. aip_agents/examples/hello_world_langchain_mcp_stdio.py +30 -0
  87. aip_agents/examples/hello_world_langchain_mcp_stdio_stream.py +41 -0
  88. aip_agents/examples/hello_world_langchain_stream.py +36 -0
  89. aip_agents/examples/hello_world_langchain_stream_lm_invoker.py +39 -0
  90. aip_agents/examples/hello_world_langflow_agent.py +163 -0
  91. aip_agents/examples/hello_world_langgraph.py +39 -0
  92. aip_agents/examples/hello_world_langgraph_bosa_twitter.py +41 -0
  93. aip_agents/examples/hello_world_langgraph_mcp_http.py +31 -0
  94. aip_agents/examples/hello_world_langgraph_mcp_http_stream.py +34 -0
  95. aip_agents/examples/hello_world_langgraph_mcp_sse.py +35 -0
  96. aip_agents/examples/hello_world_langgraph_mcp_sse_stream.py +50 -0
  97. aip_agents/examples/hello_world_langgraph_mcp_stdio.py +35 -0
  98. aip_agents/examples/hello_world_langgraph_mcp_stdio_stream.py +50 -0
  99. aip_agents/examples/hello_world_langgraph_stream.py +43 -0
  100. aip_agents/examples/hello_world_langgraph_stream_lm_invoker.py +37 -0
  101. aip_agents/examples/hello_world_model_switch_cli.py +210 -0
  102. aip_agents/examples/hello_world_multi_agent_adk.py +75 -0
  103. aip_agents/examples/hello_world_multi_agent_langchain.py +54 -0
  104. aip_agents/examples/hello_world_multi_agent_langgraph.py +66 -0
  105. aip_agents/examples/hello_world_multi_agent_langgraph_lm_invoker.py +69 -0
  106. aip_agents/examples/hello_world_pii_logger.py +21 -0
  107. aip_agents/examples/hello_world_sentry.py +133 -0
  108. aip_agents/examples/hello_world_step_limits.py +273 -0
  109. aip_agents/examples/hello_world_stock_a2a_server.py +103 -0
  110. aip_agents/examples/hello_world_tool_output_client.py +46 -0
  111. aip_agents/examples/hello_world_tool_output_server.py +114 -0
  112. aip_agents/examples/hitl_demo.py +724 -0
  113. aip_agents/examples/mcp_configs/configs.py +63 -0
  114. aip_agents/examples/mcp_servers/common.py +76 -0
  115. aip_agents/examples/mcp_servers/mcp_name.py +29 -0
  116. aip_agents/examples/mcp_servers/mcp_server_http.py +19 -0
  117. aip_agents/examples/mcp_servers/mcp_server_sse.py +19 -0
  118. aip_agents/examples/mcp_servers/mcp_server_stdio.py +19 -0
  119. aip_agents/examples/mcp_servers/mcp_time.py +10 -0
  120. aip_agents/examples/pii_demo_langgraph_client.py +69 -0
  121. aip_agents/examples/pii_demo_langgraph_server.py +126 -0
  122. aip_agents/examples/pii_demo_multi_agent_client.py +80 -0
  123. aip_agents/examples/pii_demo_multi_agent_server.py +247 -0
  124. aip_agents/examples/todolist_planning_a2a_langchain_client.py +70 -0
  125. aip_agents/examples/todolist_planning_a2a_langgraph_server.py +88 -0
  126. aip_agents/examples/tools/__init__.py +27 -0
  127. aip_agents/examples/tools/adk_arithmetic_tools.py +36 -0
  128. aip_agents/examples/tools/adk_weather_tool.py +60 -0
  129. aip_agents/examples/tools/data_generator_tool.py +103 -0
  130. aip_agents/examples/tools/data_visualization_tool.py +312 -0
  131. aip_agents/examples/tools/image_artifact_tool.py +136 -0
  132. aip_agents/examples/tools/langchain_arithmetic_tools.py +26 -0
  133. aip_agents/examples/tools/langchain_currency_exchange_tool.py +88 -0
  134. aip_agents/examples/tools/langchain_graph_artifact_tool.py +172 -0
  135. aip_agents/examples/tools/langchain_weather_tool.py +48 -0
  136. aip_agents/examples/tools/langgraph_streaming_tool.py +130 -0
  137. aip_agents/examples/tools/mock_retrieval_tool.py +56 -0
  138. aip_agents/examples/tools/pii_demo_tools.py +189 -0
  139. aip_agents/examples/tools/random_chart_tool.py +142 -0
  140. aip_agents/examples/tools/serper_tool.py +202 -0
  141. aip_agents/examples/tools/stock_tools.py +82 -0
  142. aip_agents/examples/tools/table_generator_tool.py +167 -0
  143. aip_agents/examples/tools/time_tool.py +82 -0
  144. aip_agents/examples/tools/weather_forecast_tool.py +38 -0
  145. aip_agents/executor/agent_executor.py +473 -0
  146. aip_agents/executor/base.py +48 -0
  147. aip_agents/mcp/__init__.py +1 -0
  148. aip_agents/mcp/client/__init__.py +14 -0
  149. aip_agents/mcp/client/base_mcp_client.py +369 -0
  150. aip_agents/mcp/client/connection_manager.py +193 -0
  151. aip_agents/mcp/client/google_adk/__init__.py +11 -0
  152. aip_agents/mcp/client/google_adk/client.py +381 -0
  153. aip_agents/mcp/client/langchain/__init__.py +11 -0
  154. aip_agents/mcp/client/langchain/client.py +265 -0
  155. aip_agents/mcp/client/persistent_session.py +359 -0
  156. aip_agents/mcp/client/session_pool.py +351 -0
  157. aip_agents/mcp/client/transports.py +215 -0
  158. aip_agents/mcp/utils/__init__.py +7 -0
  159. aip_agents/mcp/utils/config_validator.py +139 -0
  160. aip_agents/memory/__init__.py +14 -0
  161. aip_agents/memory/adapters/__init__.py +10 -0
  162. aip_agents/memory/adapters/base_adapter.py +717 -0
  163. aip_agents/memory/adapters/mem0.py +84 -0
  164. aip_agents/memory/base.py +84 -0
  165. aip_agents/memory/constants.py +49 -0
  166. aip_agents/memory/factory.py +86 -0
  167. aip_agents/memory/guidance.py +20 -0
  168. aip_agents/memory/simple_memory.py +47 -0
  169. aip_agents/middleware/__init__.py +17 -0
  170. aip_agents/middleware/base.py +88 -0
  171. aip_agents/middleware/manager.py +128 -0
  172. aip_agents/middleware/todolist.py +274 -0
  173. aip_agents/schema/__init__.py +69 -0
  174. aip_agents/schema/a2a.py +56 -0
  175. aip_agents/schema/agent.py +111 -0
  176. aip_agents/schema/hitl.py +157 -0
  177. aip_agents/schema/langgraph.py +37 -0
  178. aip_agents/schema/model_id.py +97 -0
  179. aip_agents/schema/step_limit.py +108 -0
  180. aip_agents/schema/storage.py +40 -0
  181. aip_agents/sentry/__init__.py +11 -0
  182. aip_agents/sentry/sentry.py +151 -0
  183. aip_agents/storage/__init__.py +41 -0
  184. aip_agents/storage/base.py +85 -0
  185. aip_agents/storage/clients/__init__.py +12 -0
  186. aip_agents/storage/clients/minio_client.py +318 -0
  187. aip_agents/storage/config.py +62 -0
  188. aip_agents/storage/providers/__init__.py +15 -0
  189. aip_agents/storage/providers/base.py +106 -0
  190. aip_agents/storage/providers/memory.py +114 -0
  191. aip_agents/storage/providers/object_storage.py +214 -0
  192. aip_agents/tools/__init__.py +33 -0
  193. aip_agents/tools/bosa_tools.py +105 -0
  194. aip_agents/tools/browser_use/__init__.py +82 -0
  195. aip_agents/tools/browser_use/action_parser.py +103 -0
  196. aip_agents/tools/browser_use/browser_use_tool.py +1112 -0
  197. aip_agents/tools/browser_use/llm_config.py +120 -0
  198. aip_agents/tools/browser_use/minio_storage.py +198 -0
  199. aip_agents/tools/browser_use/schemas.py +119 -0
  200. aip_agents/tools/browser_use/session.py +76 -0
  201. aip_agents/tools/browser_use/session_errors.py +132 -0
  202. aip_agents/tools/browser_use/steel_session_recording.py +317 -0
  203. aip_agents/tools/browser_use/streaming.py +813 -0
  204. aip_agents/tools/browser_use/structured_data_parser.py +257 -0
  205. aip_agents/tools/browser_use/structured_data_recovery.py +204 -0
  206. aip_agents/tools/browser_use/types.py +78 -0
  207. aip_agents/tools/code_sandbox/__init__.py +26 -0
  208. aip_agents/tools/code_sandbox/constant.py +13 -0
  209. aip_agents/tools/code_sandbox/e2b_cloud_sandbox_extended.py +257 -0
  210. aip_agents/tools/code_sandbox/e2b_sandbox_tool.py +411 -0
  211. aip_agents/tools/constants.py +165 -0
  212. aip_agents/tools/document_loader/__init__.py +44 -0
  213. aip_agents/tools/document_loader/base_reader.py +302 -0
  214. aip_agents/tools/document_loader/docx_reader_tool.py +68 -0
  215. aip_agents/tools/document_loader/excel_reader_tool.py +171 -0
  216. aip_agents/tools/document_loader/pdf_reader_tool.py +79 -0
  217. aip_agents/tools/document_loader/pdf_splitter.py +169 -0
  218. aip_agents/tools/gl_connector/__init__.py +5 -0
  219. aip_agents/tools/gl_connector/tool.py +351 -0
  220. aip_agents/tools/memory_search/__init__.py +22 -0
  221. aip_agents/tools/memory_search/base.py +200 -0
  222. aip_agents/tools/memory_search/mem0.py +258 -0
  223. aip_agents/tools/memory_search/schema.py +48 -0
  224. aip_agents/tools/memory_search_tool.py +26 -0
  225. aip_agents/tools/time_tool.py +117 -0
  226. aip_agents/tools/tool_config_injector.py +300 -0
  227. aip_agents/tools/web_search/__init__.py +15 -0
  228. aip_agents/tools/web_search/serper_tool.py +187 -0
  229. aip_agents/types/__init__.py +70 -0
  230. aip_agents/types/a2a_events.py +13 -0
  231. aip_agents/utils/__init__.py +79 -0
  232. aip_agents/utils/a2a_connector.py +1757 -0
  233. aip_agents/utils/artifact_helpers.py +502 -0
  234. aip_agents/utils/constants.py +22 -0
  235. aip_agents/utils/datetime/__init__.py +34 -0
  236. aip_agents/utils/datetime/normalization.py +231 -0
  237. aip_agents/utils/datetime/timezone.py +206 -0
  238. aip_agents/utils/env_loader.py +27 -0
  239. aip_agents/utils/event_handler_registry.py +58 -0
  240. aip_agents/utils/file_prompt_utils.py +176 -0
  241. aip_agents/utils/final_response_builder.py +211 -0
  242. aip_agents/utils/formatter_llm_client.py +231 -0
  243. aip_agents/utils/langgraph/__init__.py +19 -0
  244. aip_agents/utils/langgraph/converter.py +128 -0
  245. aip_agents/utils/langgraph/tool_managers/__init__.py +15 -0
  246. aip_agents/utils/langgraph/tool_managers/a2a_tool_manager.py +99 -0
  247. aip_agents/utils/langgraph/tool_managers/base_tool_manager.py +66 -0
  248. aip_agents/utils/langgraph/tool_managers/delegation_tool_manager.py +1071 -0
  249. aip_agents/utils/langgraph/tool_output_management.py +967 -0
  250. aip_agents/utils/logger.py +195 -0
  251. aip_agents/utils/metadata/__init__.py +27 -0
  252. aip_agents/utils/metadata/activity_metadata_helper.py +407 -0
  253. aip_agents/utils/metadata/activity_narrative/__init__.py +35 -0
  254. aip_agents/utils/metadata/activity_narrative/builder.py +817 -0
  255. aip_agents/utils/metadata/activity_narrative/constants.py +51 -0
  256. aip_agents/utils/metadata/activity_narrative/context.py +49 -0
  257. aip_agents/utils/metadata/activity_narrative/formatters.py +230 -0
  258. aip_agents/utils/metadata/activity_narrative/utils.py +35 -0
  259. aip_agents/utils/metadata/schemas/__init__.py +16 -0
  260. aip_agents/utils/metadata/schemas/activity_schema.py +29 -0
  261. aip_agents/utils/metadata/schemas/thinking_schema.py +31 -0
  262. aip_agents/utils/metadata/thinking_metadata_helper.py +38 -0
  263. aip_agents/utils/metadata_helper.py +358 -0
  264. aip_agents/utils/name_preprocessor/__init__.py +17 -0
  265. aip_agents/utils/name_preprocessor/base_name_preprocessor.py +73 -0
  266. aip_agents/utils/name_preprocessor/google_name_preprocessor.py +100 -0
  267. aip_agents/utils/name_preprocessor/name_preprocessor.py +87 -0
  268. aip_agents/utils/name_preprocessor/openai_name_preprocessor.py +48 -0
  269. aip_agents/utils/pii/__init__.py +25 -0
  270. aip_agents/utils/pii/pii_handler.py +397 -0
  271. aip_agents/utils/pii/pii_helper.py +207 -0
  272. aip_agents/utils/pii/uuid_deanonymizer_mapping.py +195 -0
  273. aip_agents/utils/reference_helper.py +273 -0
  274. aip_agents/utils/sse_chunk_transformer.py +831 -0
  275. aip_agents/utils/step_limit_manager.py +265 -0
  276. aip_agents/utils/token_usage_helper.py +156 -0
  277. aip_agents_binary-0.5.20.dist-info/METADATA +681 -0
  278. aip_agents_binary-0.5.20.dist-info/RECORD +280 -0
  279. aip_agents_binary-0.5.20.dist-info/WHEEL +5 -0
  280. aip_agents_binary-0.5.20.dist-info/top_level.txt +1 -0
@@ -0,0 +1,312 @@
1
+ """Tool that creates visualizations from data with automatic output storage.
2
+
3
+ This tool demonstrates the tool output management system by creating
4
+ visual charts from data (including referenced data from other tools).
5
+
6
+ Authors:
7
+ Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
8
+ """
9
+
10
+ import io
11
+ from typing import Any
12
+
13
+ from langchain_core.tools import BaseTool
14
+ from langgraph.types import Command
15
+ from pydantic import BaseModel, Field
16
+
17
+ from aip_agents.a2a.types import MimeType
18
+ from aip_agents.utils.artifact_helpers import create_artifact_command
19
+
20
+ # Optional PIL import for image generation
21
+ try:
22
+ from PIL import Image, ImageDraw # type: ignore
23
+
24
+ PIL_AVAILABLE = True
25
+ except ImportError:
26
+ PIL_AVAILABLE = False
27
+ Image = None
28
+ ImageDraw = None
29
+
30
+
31
+ class ChartInput(BaseModel):
32
+ """Input schema for chart generation."""
33
+
34
+ data_source: Any = Field(
35
+ description="Data to visualize (can be a reference like $tool_output.xyz)",
36
+ )
37
+ chart_type: str = Field(
38
+ default="bar",
39
+ description="Type of chart to generate (bar or line).",
40
+ pattern="^(bar|line)$",
41
+ )
42
+ title: str = Field(
43
+ default="Chart",
44
+ description="Title for the visualization",
45
+ )
46
+
47
+
48
+ class DataVisualizerTool(BaseTool):
49
+ """Tool that creates visualizations with automatic output storage."""
50
+
51
+ name: str = "data_visualizer"
52
+ description: str = "Create visual charts from data (supports tool output references)"
53
+ args_schema: type[BaseModel] = ChartInput
54
+
55
+ def _run(
56
+ self,
57
+ data_source: Any,
58
+ chart_type: str = "bar",
59
+ title: str = "Chart",
60
+ **kwargs,
61
+ ) -> Command:
62
+ """Create a visualization from data.
63
+
64
+ Args:
65
+ data_source: Data to visualize (can be a reference)
66
+ chart_type: Type of chart (bar or line)
67
+ title: Chart title
68
+ **kwargs: Additional arguments including tool output context
69
+
70
+ Returns:
71
+ Command with visualization artifact and metadata
72
+ """
73
+ try:
74
+ # Extract chartable data
75
+ chart_data = self._extract_chart_data(data_source)
76
+ if not chart_data:
77
+ return Command(
78
+ update={
79
+ "result": f"❌ Cannot create {chart_type} chart: No valid data found",
80
+ "metadata": {"error": "No valid data found"},
81
+ }
82
+ )
83
+
84
+ # Generate the image
85
+ image_bytes = self._create_chart_image(chart_data, chart_type, title)
86
+
87
+ # Create artifact with proper encoding
88
+ return create_artifact_command(
89
+ result=f"✅ Created {chart_type} chart '{title}' with {len(chart_data['values'])} data points",
90
+ artifact_data=image_bytes,
91
+ artifact_name=f"{title.lower().replace(' ', '_')}_{chart_type}.png",
92
+ artifact_description=f"{chart_type.capitalize()} chart: {title}",
93
+ mime_type=MimeType.IMAGE_PNG,
94
+ metadata_update={
95
+ "visualization": {
96
+ "success": True,
97
+ "chart_type": chart_type,
98
+ "title": title,
99
+ "data_points": len(chart_data["values"]),
100
+ }
101
+ },
102
+ )
103
+
104
+ except Exception as e:
105
+ return Command(
106
+ update={"result": f"❌ Error creating visualization: {str(e)}", "metadata": {"error": str(e)}}
107
+ )
108
+
109
+ async def _arun(
110
+ self,
111
+ data_source: Any,
112
+ chart_type: str = "bar",
113
+ title: str = "Chart",
114
+ **kwargs,
115
+ ) -> Command:
116
+ """Async version of _run.
117
+
118
+ Args:
119
+ data_source (Any): Data to visualize (can be a reference).
120
+ chart_type (str, optional): Type of chart (bar or line). Defaults to "bar".
121
+ title (str, optional): Chart title. Defaults to "Chart".
122
+ **kwargs: Additional arguments including tool output context.
123
+
124
+ Returns:
125
+ Command: Command with visualization artifact and metadata.
126
+ """
127
+ return self._run(data_source, chart_type, title, **kwargs)
128
+
129
+ def _extract_chart_data(self, data_source: Any) -> dict[str, Any]:
130
+ """Extract chartable data from various formats.
131
+
132
+ Args:
133
+ data_source (Any): Data source to extract chartable data from.
134
+
135
+ Returns:
136
+ dict[str, Any]: Dictionary containing labels and values for charting.
137
+ """
138
+ if isinstance(data_source, dict):
139
+ # Handle dict with values
140
+ if "values" in data_source:
141
+ return {
142
+ "values": data_source["values"][:10], # Limit to 10 points
143
+ "labels": data_source.get("labels", [f"Point {i + 1}" for i in range(len(data_source["values"]))]),
144
+ }
145
+ # Handle dict with numbers
146
+ numeric_items = [(str(k), float(v)) for k, v in data_source.items() if isinstance(v, int | float)][:10]
147
+ if numeric_items:
148
+ return {
149
+ "labels": [k for k, v in numeric_items],
150
+ "values": [v for k, v in numeric_items],
151
+ }
152
+
153
+ elif isinstance(data_source, list | tuple):
154
+ # Handle numeric lists
155
+ if all(isinstance(x, int | float) for x in data_source):
156
+ return {
157
+ "values": data_source[:10],
158
+ "labels": [f"Point {i + 1}" for i in range(len(data_source[:10]))],
159
+ }
160
+
161
+ # Fallback for strings - character frequency
162
+ elif isinstance(data_source, str):
163
+ char_counts = {}
164
+ for c in data_source[:100]: # Limit input size
165
+ if c.isalnum():
166
+ char_counts[c] = char_counts.get(c, 0) + 1
167
+ if char_counts:
168
+ items = sorted(char_counts.items(), key=lambda x: x[1], reverse=True)[:10]
169
+ return {
170
+ "labels": [k for k, v in items],
171
+ "values": [v for k, v in items],
172
+ }
173
+
174
+ return {}
175
+
176
+ def _create_chart_image(
177
+ self,
178
+ chart_data: dict[str, Any],
179
+ chart_type: str,
180
+ title: str,
181
+ ) -> bytes:
182
+ """Create a chart image using PIL.
183
+
184
+ Args:
185
+ chart_data (dict[str, Any]): Data to visualize containing labels and values.
186
+ chart_type (str): Type of chart to create.
187
+ title (str): Title for the chart.
188
+
189
+ Returns:
190
+ bytes: Image data as PNG bytes.
191
+ """
192
+ if not PIL_AVAILABLE:
193
+ # Fallback to text representation
194
+ return self._create_text_chart(chart_data, chart_type, title)
195
+
196
+ # Canvas setup
197
+ width, height = 400, 300
198
+ image = Image.new("RGB", (width, height), "white")
199
+ draw = ImageDraw.Draw(image)
200
+
201
+ # Draw title
202
+ draw.text((10, 10), title[:40], fill="black")
203
+
204
+ # Chart area
205
+ chart_x, chart_y = 40, 40
206
+ chart_width = width - 60
207
+ chart_height = height - 80
208
+
209
+ values = chart_data.get("values", [])
210
+ labels = chart_data.get("labels", [])
211
+
212
+ if not values:
213
+ draw.text((chart_x, chart_y + 50), "No data to visualize", fill="red")
214
+ buf = io.BytesIO()
215
+ image.save(buf, format="PNG")
216
+ return buf.getvalue()
217
+
218
+ # Normalize values
219
+ max_val = max(values)
220
+ min_val = min(values)
221
+ val_range = max_val - min_val if max_val != min_val else 1
222
+
223
+ if chart_type == "bar":
224
+ # Bar chart
225
+ bar_width = chart_width // len(values)
226
+ for i, (label, value) in enumerate(zip(labels, values, strict=False)):
227
+ x = chart_x + i * bar_width
228
+ normalized_height = ((value - min_val) / val_range) * chart_height
229
+ y = chart_y + chart_height - normalized_height
230
+
231
+ # Draw bar
232
+ draw.rectangle(
233
+ [x + 2, y, x + bar_width - 2, chart_y + chart_height],
234
+ fill="#4C78A8",
235
+ outline="black",
236
+ )
237
+
238
+ # Draw label
239
+ label_text = str(label)[:8]
240
+ draw.text(
241
+ (x + 2, chart_y + chart_height + 5),
242
+ label_text,
243
+ fill="black",
244
+ )
245
+
246
+ else: # line chart
247
+ # Calculate points
248
+ points = []
249
+ step = chart_width / (len(values) - 1) if len(values) > 1 else chart_width
250
+ for i, value in enumerate(values):
251
+ x = chart_x + i * step
252
+ normalized_height = ((value - min_val) / val_range) * chart_height
253
+ y = chart_y + chart_height - normalized_height
254
+ points.append((x, y))
255
+
256
+ # Draw lines
257
+ MIN_POINTS_FOR_LINE = 2
258
+ if len(points) >= MIN_POINTS_FOR_LINE:
259
+ draw.line(points, fill="#F58518", width=2)
260
+
261
+ # Draw points
262
+ for x, y in points:
263
+ draw.ellipse(
264
+ [x - 3, y - 3, x + 3, y + 3],
265
+ fill="#F58518",
266
+ outline="black",
267
+ )
268
+
269
+ # Draw value
270
+ draw.text((x - 10, y - 15), str(round(values[points.index((x, y))], 1)), fill="black")
271
+
272
+ # Save to bytes
273
+ buf = io.BytesIO()
274
+ image.save(buf, format="PNG")
275
+ return buf.getvalue()
276
+
277
+ def _create_text_chart(
278
+ self,
279
+ chart_data: dict[str, Any],
280
+ chart_type: str,
281
+ title: str,
282
+ ) -> bytes:
283
+ """Fallback text-based chart when PIL is not available.
284
+
285
+ Args:
286
+ chart_data (dict[str, Any]): Data to visualize containing labels and values.
287
+ chart_type (str): Type of chart to create.
288
+ title (str): Title for the chart.
289
+
290
+ Returns:
291
+ bytes: Text chart data encoded as UTF-8 bytes.
292
+ """
293
+ values = chart_data.get("values", [])
294
+ labels = chart_data.get("labels", [])
295
+
296
+ lines = [
297
+ f"=== {title} ===",
298
+ f"Chart Type: {chart_type}",
299
+ f"Data Points: {len(values)}",
300
+ "",
301
+ ]
302
+
303
+ if values:
304
+ max_val = max(values)
305
+ for label, value in zip(labels, values, strict=False):
306
+ bar_length = int((value / max_val) * 20) if max_val > 0 else 0
307
+ bar = "█" * bar_length
308
+ lines.append(f"{label[:10]:10} |{bar:20}| {value}")
309
+ else:
310
+ lines.append("No data to display")
311
+
312
+ return "\n".join(lines).encode("utf-8")
@@ -0,0 +1,136 @@
1
+ """Image artifact tool for demonstrating artifact generation.
2
+
3
+ This tool generates simple images and returns them as artifacts to demonstrate
4
+ the standardized artifact format for binary data.
5
+
6
+ Authors:
7
+ Christian Trisno Sen Long Chen (christian.t.s.l.chen@gdplabs.id)
8
+ """
9
+
10
+ import io
11
+ from typing import Any
12
+
13
+ from langchain_core.tools import BaseTool
14
+ from pydantic import BaseModel, Field
15
+
16
+ from aip_agents.a2a.types import MimeType
17
+ from aip_agents.utils.artifact_helpers import create_artifact_response, create_error_response
18
+
19
+ # Optional PIL import for image generation
20
+ try:
21
+ from PIL import Image, ImageDraw, ImageFont # type: ignore
22
+
23
+ PIL_AVAILABLE = True
24
+ except ImportError:
25
+ PIL_AVAILABLE = False
26
+ Image = None
27
+ ImageDraw = None
28
+ ImageFont = None
29
+
30
+
31
+ class ImageArtifactInput(BaseModel):
32
+ """Input schema for image artifact tool."""
33
+
34
+ width: int = Field(default=400, description="Width of the image in pixels", ge=100, le=1000)
35
+ height: int = Field(default=300, description="Height of the image in pixels", ge=100, le=1000)
36
+ color: str = Field(default="blue", description="Primary color for the image")
37
+ text: str = Field(default="Sample Image", description="Text to display on the image")
38
+ image_name: str = Field(default="generated_image", description="Name for the generated image")
39
+
40
+
41
+ class ImageArtifactTool(BaseTool):
42
+ """Tool that generates simple images with artifact support.
43
+
44
+ This tool demonstrates the standardized artifact format for binary data by:
45
+ 1. Generating a simple image using PIL
46
+ 2. Providing a confirmation message for the agent
47
+ 3. Creating an image file artifact for the user
48
+ """
49
+
50
+ name: str = "image_generator"
51
+ description: str = "Generate simple images with text and colors"
52
+ args_schema: type[BaseModel] = ImageArtifactInput
53
+
54
+ def _run(
55
+ self,
56
+ width: int = 400,
57
+ height: int = 300,
58
+ color: str = "blue",
59
+ text: str = "Sample Image",
60
+ image_name: str = "generated_image",
61
+ ) -> dict[str, Any]:
62
+ """Generate an image synchronously.
63
+
64
+ Args:
65
+ width (int, optional): Width of the image in pixels. Defaults to 400.
66
+ height (int, optional): Height of the image in pixels. Defaults to 300.
67
+ color (str, optional): Primary color for the image. Defaults to "blue".
68
+ text (str, optional): Text to display on the image. Defaults to "Sample Image".
69
+ image_name (str, optional): Name for the generated image. Defaults to "generated_image".
70
+
71
+ Returns:
72
+ dict[str, Any]: Response containing artifact data and metadata.
73
+ """
74
+ if not PIL_AVAILABLE:
75
+ return create_error_response("PIL (Pillow) library is not installed. Cannot generate images.")
76
+
77
+ # Create image
78
+ image = Image.new("RGB", (width, height), color=color)
79
+ draw = ImageDraw.Draw(image)
80
+
81
+ # Try to use a default font, fall back to basic font if not available
82
+ try:
83
+ font = ImageFont.load_default()
84
+ except Exception:
85
+ font = None
86
+
87
+ # Calculate text position (center)
88
+ if font:
89
+ bbox = draw.textbbox((0, 0), text, font=font)
90
+ text_width = bbox[2] - bbox[0]
91
+ text_height = bbox[3] - bbox[1]
92
+ else:
93
+ # Rough estimation for basic font
94
+ text_width = len(text) * 6
95
+ text_height = 11
96
+
97
+ x = (width - text_width) // 2
98
+ y = (height - text_height) // 2
99
+
100
+ # Draw text
101
+ draw.text((x, y), text, fill="white", font=font)
102
+
103
+ # Convert to bytes
104
+ img_buffer = io.BytesIO()
105
+ image.save(img_buffer, format="PNG")
106
+ img_bytes = img_buffer.getvalue()
107
+
108
+ return create_artifact_response(
109
+ result=f"I have generated a {width}x{height} {color} image with the text '{text}'.",
110
+ artifact_data=img_bytes,
111
+ artifact_name=f"{image_name}.png",
112
+ artifact_description=f"Generated {width}x{height} {color} image with text: {text}",
113
+ mime_type=MimeType.IMAGE_PNG,
114
+ )
115
+
116
+ async def _arun(
117
+ self,
118
+ width: int = 400,
119
+ height: int = 300,
120
+ color: str = "blue",
121
+ text: str = "Sample Image",
122
+ image_name: str = "generated_image",
123
+ ) -> dict[str, Any]:
124
+ """Generate an image asynchronously.
125
+
126
+ Args:
127
+ width (int, optional): Width of the image in pixels. Defaults to 400.
128
+ height (int, optional): Height of the image in pixels. Defaults to 300.
129
+ color (str, optional): Primary color for the image. Defaults to "blue".
130
+ text (str, optional): Text to display on the image. Defaults to "Sample Image".
131
+ image_name (str, optional): Name for the generated image. Defaults to "generated_image".
132
+
133
+ Returns:
134
+ dict[str, Any]: Response containing artifact data and metadata.
135
+ """
136
+ return self._run(width, height, color, text, image_name)
@@ -0,0 +1,26 @@
1
+ """LangChain-specific arithmetic tools for GLLM agent examples."""
2
+
3
+ from langchain_core.tools import tool
4
+
5
+
6
+ # Decorate the plain function to create a LangChain Tool
7
+ @tool
8
+ def add_numbers(a: int, b: int) -> str:
9
+ """Adds two numbers and returns the result as a string.
10
+
11
+ This is a self-contained LangChain tool.
12
+ For example:
13
+ add_numbers(a=5, b=3)
14
+
15
+ Args:
16
+ a (int): First number to add.
17
+ b (int): Second number to add.
18
+
19
+ Returns:
20
+ str: Sum of the two numbers as a string.
21
+ """
22
+ try:
23
+ result = a + b
24
+ return str(result)
25
+ except Exception as e:
26
+ return f"Error: Could not add numbers. Reason: {str(e)}"
@@ -0,0 +1,88 @@
1
+ """Simple currency exchange tool demonstrating RunnableConfig tool configuration."""
2
+
3
+ import asyncio
4
+
5
+ from langchain_core.runnables import RunnableConfig
6
+ from langchain_core.tools import BaseTool
7
+ from langgraph.types import Command
8
+ from pydantic import BaseModel, Field
9
+
10
+
11
+ class CurrencyConfig(BaseModel):
12
+ """Tool configuration schema."""
13
+
14
+ tenant_id: str = Field(description="Tenant identifier")
15
+ auth_key: str = Field(description="Authentication key")
16
+
17
+
18
+ class CurrencyInput(BaseModel):
19
+ """Input schema for currency exchange."""
20
+
21
+ amount: float = Field(description="Amount to convert", gt=0)
22
+ from_currency: str = Field(description="Source currency (USD, EUR)")
23
+ to_currency: str = Field(description="Target currency (USD, EUR)")
24
+
25
+
26
+ class CurrencyExchangeTool(BaseTool):
27
+ """Simple currency exchange tool with SDK auto-injection configuration."""
28
+
29
+ name: str = "currency_exchange"
30
+ description: str = "Convert currencies with tenant-specific rates"
31
+ args_schema: type[BaseModel] = CurrencyInput
32
+ tool_config_schema: type[BaseModel] = CurrencyConfig
33
+
34
+ def _run(self, amount: float, from_currency: str, to_currency: str, config: RunnableConfig, **kwargs) -> str:
35
+ """Run the tool asynchronously.
36
+
37
+ Args:
38
+ amount (float): Amount to convert.
39
+ from_currency (str): Source currency (USD, EUR).
40
+ to_currency (str): Target currency (USD, EUR).
41
+ config (RunnableConfig): Tool configuration containing tenant and auth info.
42
+ **kwargs: Additional keyword arguments.
43
+
44
+ Returns:
45
+ str: Conversion result text.
46
+ """
47
+ return asyncio.run(self._arun(amount, from_currency, to_currency, config, **kwargs))
48
+
49
+ async def _arun(self, amount: float, from_currency: str, to_currency: str, config: RunnableConfig, **kwargs) -> str:
50
+ """Convert currency using effective configuration.
51
+
52
+ Args:
53
+ amount (float): Amount to convert.
54
+ from_currency (str): Source currency (USD, EUR).
55
+ to_currency (str): Target currency (USD, EUR).
56
+ config (RunnableConfig): Tool configuration containing tenant and auth info.
57
+ **kwargs: Additional keyword arguments.
58
+
59
+ Returns:
60
+ str: Command with conversion result and metadata.
61
+ """
62
+ # Get effective config (runtime override or agent default)
63
+ effective_config = self.get_tool_config(config)
64
+
65
+ if not effective_config:
66
+ return "❌ No configuration provided"
67
+
68
+ # Simple tenant validation
69
+ rates = {"premium_corp": 0.85, "standard_business": 1.5}
70
+ keys = {"premium_corp": "premium_key_123", "standard_business": "standard_key_456"}
71
+
72
+ if effective_config.tenant_id not in rates or effective_config.auth_key != keys.get(effective_config.tenant_id):
73
+ return f"❌ Invalid tenant: {effective_config.tenant_id}"
74
+
75
+ # Simple USD/EUR conversion
76
+ rate = rates[effective_config.tenant_id] if from_currency == "USD" else 1 / rates[effective_config.tenant_id]
77
+ result = amount * rate
78
+
79
+ result_text = f"💱 {amount} {from_currency} = {result:.2f} {to_currency} (tenant: {effective_config.tenant_id})"
80
+
81
+ return Command(
82
+ update={
83
+ "result": result_text,
84
+ "metadata": {
85
+ "message": "Conversion successful",
86
+ },
87
+ }
88
+ )