google-adk 0.0.1__py3-none-any.whl → 0.0.2__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 (312) hide show
  1. google/adk/__init__.py +20 -0
  2. google/adk/agents/__init__.py +32 -0
  3. google/adk/agents/active_streaming_tool.py +38 -0
  4. google/adk/agents/base_agent.py +345 -0
  5. google/adk/agents/callback_context.py +112 -0
  6. google/adk/agents/invocation_context.py +181 -0
  7. google/adk/agents/langgraph_agent.py +140 -0
  8. google/adk/agents/live_request_queue.py +64 -0
  9. google/adk/agents/llm_agent.py +376 -0
  10. google/adk/agents/loop_agent.py +62 -0
  11. google/adk/agents/parallel_agent.py +96 -0
  12. google/adk/agents/readonly_context.py +46 -0
  13. google/adk/agents/remote_agent.py +50 -0
  14. google/adk/agents/run_config.py +87 -0
  15. google/adk/agents/sequential_agent.py +45 -0
  16. google/adk/agents/transcription_entry.py +34 -0
  17. google/adk/artifacts/__init__.py +23 -0
  18. google/adk/artifacts/base_artifact_service.py +128 -0
  19. google/adk/artifacts/gcs_artifact_service.py +195 -0
  20. google/adk/artifacts/in_memory_artifact_service.py +133 -0
  21. google/adk/auth/__init__.py +22 -0
  22. google/adk/auth/auth_credential.py +220 -0
  23. google/adk/auth/auth_handler.py +268 -0
  24. google/adk/auth/auth_preprocessor.py +116 -0
  25. google/adk/auth/auth_schemes.py +67 -0
  26. google/adk/auth/auth_tool.py +55 -0
  27. google/adk/cli/__init__.py +15 -0
  28. google/adk/cli/__main__.py +18 -0
  29. google/adk/cli/agent_graph.py +122 -0
  30. google/adk/cli/browser/adk_favicon.svg +17 -0
  31. google/adk/cli/browser/assets/audio-processor.js +51 -0
  32. google/adk/cli/browser/assets/config/runtime-config.json +3 -0
  33. google/adk/cli/browser/index.html +33 -0
  34. google/adk/cli/browser/main-XUU6OGCC.js +75 -0
  35. google/adk/cli/browser/polyfills-FFHMD2TL.js +18 -0
  36. google/adk/cli/browser/styles-4VDSPQ37.css +17 -0
  37. google/adk/cli/cli.py +181 -0
  38. google/adk/cli/cli_deploy.py +181 -0
  39. google/adk/cli/cli_eval.py +282 -0
  40. google/adk/cli/cli_tools_click.py +479 -0
  41. google/adk/cli/fast_api.py +774 -0
  42. google/adk/cli/media_streamer/__init__.py +19 -0
  43. google/adk/cli/media_streamer/index.html +228 -0
  44. google/adk/cli/utils/__init__.py +49 -0
  45. google/adk/cli/utils/envs.py +57 -0
  46. google/adk/cli/utils/evals.py +93 -0
  47. google/adk/cli/utils/logs.py +72 -0
  48. google/adk/code_executors/__init__.py +49 -0
  49. google/adk/code_executors/base_code_executor.py +97 -0
  50. google/adk/code_executors/code_execution_utils.py +256 -0
  51. google/adk/code_executors/code_executor_context.py +202 -0
  52. google/adk/code_executors/container_code_executor.py +196 -0
  53. google/adk/code_executors/unsafe_local_code_executor.py +71 -0
  54. google/adk/code_executors/vertex_ai_code_executor.py +234 -0
  55. google/adk/evaluation/__init__.py +31 -0
  56. google/adk/evaluation/agent_evaluator.py +329 -0
  57. google/adk/evaluation/evaluation_constants.py +24 -0
  58. google/adk/evaluation/evaluation_generator.py +270 -0
  59. google/adk/evaluation/response_evaluator.py +135 -0
  60. google/adk/evaluation/trajectory_evaluator.py +184 -0
  61. google/adk/events/__init__.py +21 -0
  62. google/adk/events/event.py +130 -0
  63. google/adk/events/event_actions.py +55 -0
  64. google/adk/examples/__init__.py +28 -0
  65. google/adk/examples/base_example_provider.py +35 -0
  66. google/adk/examples/example.py +27 -0
  67. google/adk/examples/example_util.py +123 -0
  68. google/adk/examples/vertex_ai_example_store.py +104 -0
  69. google/adk/flows/__init__.py +14 -0
  70. google/adk/flows/llm_flows/__init__.py +20 -0
  71. google/adk/flows/llm_flows/_base_llm_processor.py +52 -0
  72. google/adk/flows/llm_flows/_code_execution.py +458 -0
  73. google/adk/flows/llm_flows/_nl_planning.py +129 -0
  74. google/adk/flows/llm_flows/agent_transfer.py +132 -0
  75. google/adk/flows/llm_flows/audio_transcriber.py +109 -0
  76. google/adk/flows/llm_flows/auto_flow.py +49 -0
  77. google/adk/flows/llm_flows/base_llm_flow.py +559 -0
  78. google/adk/flows/llm_flows/basic.py +72 -0
  79. google/adk/flows/llm_flows/contents.py +370 -0
  80. google/adk/flows/llm_flows/functions.py +486 -0
  81. google/adk/flows/llm_flows/identity.py +47 -0
  82. google/adk/flows/llm_flows/instructions.py +137 -0
  83. google/adk/flows/llm_flows/single_flow.py +57 -0
  84. google/adk/memory/__init__.py +35 -0
  85. google/adk/memory/base_memory_service.py +74 -0
  86. google/adk/memory/in_memory_memory_service.py +62 -0
  87. google/adk/memory/vertex_ai_rag_memory_service.py +177 -0
  88. google/adk/models/__init__.py +31 -0
  89. google/adk/models/anthropic_llm.py +243 -0
  90. google/adk/models/base_llm.py +87 -0
  91. google/adk/models/base_llm_connection.py +76 -0
  92. google/adk/models/gemini_llm_connection.py +200 -0
  93. google/adk/models/google_llm.py +331 -0
  94. google/adk/models/lite_llm.py +673 -0
  95. google/adk/models/llm_request.py +98 -0
  96. google/adk/models/llm_response.py +111 -0
  97. google/adk/models/registry.py +102 -0
  98. google/adk/planners/__init__.py +23 -0
  99. google/adk/planners/base_planner.py +66 -0
  100. google/adk/planners/built_in_planner.py +75 -0
  101. google/adk/planners/plan_re_act_planner.py +208 -0
  102. google/adk/runners.py +456 -0
  103. google/adk/sessions/__init__.py +41 -0
  104. google/adk/sessions/base_session_service.py +133 -0
  105. google/adk/sessions/database_session_service.py +522 -0
  106. google/adk/sessions/in_memory_session_service.py +206 -0
  107. google/adk/sessions/session.py +54 -0
  108. google/adk/sessions/state.py +71 -0
  109. google/adk/sessions/vertex_ai_session_service.py +356 -0
  110. google/adk/telemetry.py +189 -0
  111. google/adk/tests/__init__.py +14 -0
  112. google/adk/tests/integration/.env.example +10 -0
  113. google/adk/tests/integration/__init__.py +18 -0
  114. google/adk/tests/integration/conftest.py +119 -0
  115. google/adk/tests/integration/fixture/__init__.py +14 -0
  116. google/adk/tests/integration/fixture/agent_with_config/__init__.py +15 -0
  117. google/adk/tests/integration/fixture/agent_with_config/agent.py +88 -0
  118. google/adk/tests/integration/fixture/callback_agent/__init__.py +15 -0
  119. google/adk/tests/integration/fixture/callback_agent/agent.py +105 -0
  120. google/adk/tests/integration/fixture/context_update_test/OWNERS +1 -0
  121. google/adk/tests/integration/fixture/context_update_test/__init__.py +15 -0
  122. google/adk/tests/integration/fixture/context_update_test/agent.py +43 -0
  123. google/adk/tests/integration/fixture/context_update_test/successful_test.session.json +582 -0
  124. google/adk/tests/integration/fixture/context_variable_agent/__init__.py +15 -0
  125. google/adk/tests/integration/fixture/context_variable_agent/agent.py +115 -0
  126. google/adk/tests/integration/fixture/customer_support_ma/__init__.py +15 -0
  127. google/adk/tests/integration/fixture/customer_support_ma/agent.py +172 -0
  128. google/adk/tests/integration/fixture/ecommerce_customer_service_agent/__init__.py +15 -0
  129. google/adk/tests/integration/fixture/ecommerce_customer_service_agent/agent.py +338 -0
  130. google/adk/tests/integration/fixture/ecommerce_customer_service_agent/order_query.test.json +69 -0
  131. google/adk/tests/integration/fixture/ecommerce_customer_service_agent/test_config.json +6 -0
  132. google/adk/tests/integration/fixture/flow_complex_spark/__init__.py +15 -0
  133. google/adk/tests/integration/fixture/flow_complex_spark/agent.py +182 -0
  134. google/adk/tests/integration/fixture/flow_complex_spark/sample.debug.log +243 -0
  135. google/adk/tests/integration/fixture/flow_complex_spark/sample.session.json +190 -0
  136. google/adk/tests/integration/fixture/hello_world_agent/__init__.py +15 -0
  137. google/adk/tests/integration/fixture/hello_world_agent/agent.py +95 -0
  138. google/adk/tests/integration/fixture/hello_world_agent/roll_die.test.json +24 -0
  139. google/adk/tests/integration/fixture/hello_world_agent/test_config.json +6 -0
  140. google/adk/tests/integration/fixture/home_automation_agent/__init__.py +15 -0
  141. google/adk/tests/integration/fixture/home_automation_agent/agent.py +304 -0
  142. google/adk/tests/integration/fixture/home_automation_agent/simple_test.test.json +5 -0
  143. google/adk/tests/integration/fixture/home_automation_agent/simple_test2.test.json +5 -0
  144. google/adk/tests/integration/fixture/home_automation_agent/test_config.json +5 -0
  145. google/adk/tests/integration/fixture/home_automation_agent/test_files/dependent_tool_calls.test.json +18 -0
  146. google/adk/tests/integration/fixture/home_automation_agent/test_files/memorizing_past_events/eval_data.test.json +17 -0
  147. google/adk/tests/integration/fixture/home_automation_agent/test_files/memorizing_past_events/test_config.json +6 -0
  148. google/adk/tests/integration/fixture/home_automation_agent/test_files/simple_multi_turn_conversation.test.json +18 -0
  149. google/adk/tests/integration/fixture/home_automation_agent/test_files/simple_test.test.json +17 -0
  150. google/adk/tests/integration/fixture/home_automation_agent/test_files/simple_test2.test.json +5 -0
  151. google/adk/tests/integration/fixture/home_automation_agent/test_files/test_config.json +5 -0
  152. google/adk/tests/integration/fixture/tool_agent/__init__.py +15 -0
  153. google/adk/tests/integration/fixture/tool_agent/agent.py +218 -0
  154. google/adk/tests/integration/fixture/tool_agent/files/Agent_test_plan.pdf +0 -0
  155. google/adk/tests/integration/fixture/trip_planner_agent/__init__.py +15 -0
  156. google/adk/tests/integration/fixture/trip_planner_agent/agent.py +110 -0
  157. google/adk/tests/integration/fixture/trip_planner_agent/initial.session.json +13 -0
  158. google/adk/tests/integration/fixture/trip_planner_agent/test_config.json +5 -0
  159. google/adk/tests/integration/fixture/trip_planner_agent/test_files/initial.session.json +13 -0
  160. google/adk/tests/integration/fixture/trip_planner_agent/test_files/test_config.json +5 -0
  161. google/adk/tests/integration/fixture/trip_planner_agent/test_files/trip_inquiry_sub_agent.test.json +7 -0
  162. google/adk/tests/integration/fixture/trip_planner_agent/trip_inquiry.test.json +19 -0
  163. google/adk/tests/integration/models/__init__.py +14 -0
  164. google/adk/tests/integration/models/test_google_llm.py +65 -0
  165. google/adk/tests/integration/test_callback.py +70 -0
  166. google/adk/tests/integration/test_context_variable.py +67 -0
  167. google/adk/tests/integration/test_evalute_agent_in_fixture.py +76 -0
  168. google/adk/tests/integration/test_multi_agent.py +28 -0
  169. google/adk/tests/integration/test_multi_turn.py +42 -0
  170. google/adk/tests/integration/test_single_agent.py +23 -0
  171. google/adk/tests/integration/test_sub_agent.py +26 -0
  172. google/adk/tests/integration/test_system_instruction.py +177 -0
  173. google/adk/tests/integration/test_tools.py +287 -0
  174. google/adk/tests/integration/test_with_test_file.py +34 -0
  175. google/adk/tests/integration/tools/__init__.py +14 -0
  176. google/adk/tests/integration/utils/__init__.py +16 -0
  177. google/adk/tests/integration/utils/asserts.py +75 -0
  178. google/adk/tests/integration/utils/test_runner.py +97 -0
  179. google/adk/tests/unittests/__init__.py +14 -0
  180. google/adk/tests/unittests/agents/__init__.py +14 -0
  181. google/adk/tests/unittests/agents/test_base_agent.py +407 -0
  182. google/adk/tests/unittests/agents/test_langgraph_agent.py +191 -0
  183. google/adk/tests/unittests/agents/test_llm_agent_callbacks.py +138 -0
  184. google/adk/tests/unittests/agents/test_llm_agent_fields.py +231 -0
  185. google/adk/tests/unittests/agents/test_loop_agent.py +136 -0
  186. google/adk/tests/unittests/agents/test_parallel_agent.py +92 -0
  187. google/adk/tests/unittests/agents/test_sequential_agent.py +114 -0
  188. google/adk/tests/unittests/artifacts/__init__.py +14 -0
  189. google/adk/tests/unittests/artifacts/test_artifact_service.py +276 -0
  190. google/adk/tests/unittests/auth/test_auth_handler.py +575 -0
  191. google/adk/tests/unittests/conftest.py +73 -0
  192. google/adk/tests/unittests/fast_api/__init__.py +14 -0
  193. google/adk/tests/unittests/fast_api/test_fast_api.py +269 -0
  194. google/adk/tests/unittests/flows/__init__.py +14 -0
  195. google/adk/tests/unittests/flows/llm_flows/__init__.py +14 -0
  196. google/adk/tests/unittests/flows/llm_flows/_test_examples.py +142 -0
  197. google/adk/tests/unittests/flows/llm_flows/test_agent_transfer.py +311 -0
  198. google/adk/tests/unittests/flows/llm_flows/test_functions_long_running.py +244 -0
  199. google/adk/tests/unittests/flows/llm_flows/test_functions_request_euc.py +346 -0
  200. google/adk/tests/unittests/flows/llm_flows/test_functions_sequential.py +93 -0
  201. google/adk/tests/unittests/flows/llm_flows/test_functions_simple.py +258 -0
  202. google/adk/tests/unittests/flows/llm_flows/test_identity.py +66 -0
  203. google/adk/tests/unittests/flows/llm_flows/test_instructions.py +164 -0
  204. google/adk/tests/unittests/flows/llm_flows/test_model_callbacks.py +142 -0
  205. google/adk/tests/unittests/flows/llm_flows/test_other_configs.py +46 -0
  206. google/adk/tests/unittests/flows/llm_flows/test_tool_callbacks.py +269 -0
  207. google/adk/tests/unittests/models/__init__.py +14 -0
  208. google/adk/tests/unittests/models/test_google_llm.py +224 -0
  209. google/adk/tests/unittests/models/test_litellm.py +804 -0
  210. google/adk/tests/unittests/models/test_models.py +60 -0
  211. google/adk/tests/unittests/sessions/__init__.py +14 -0
  212. google/adk/tests/unittests/sessions/test_session_service.py +227 -0
  213. google/adk/tests/unittests/sessions/test_vertex_ai_session_service.py +246 -0
  214. google/adk/tests/unittests/streaming/__init__.py +14 -0
  215. google/adk/tests/unittests/streaming/test_streaming.py +50 -0
  216. google/adk/tests/unittests/tools/__init__.py +14 -0
  217. google/adk/tests/unittests/tools/apihub_tool/clients/test_apihub_client.py +499 -0
  218. google/adk/tests/unittests/tools/apihub_tool/test_apihub_toolset.py +204 -0
  219. google/adk/tests/unittests/tools/application_integration_tool/clients/test_connections_client.py +600 -0
  220. google/adk/tests/unittests/tools/application_integration_tool/clients/test_integration_client.py +630 -0
  221. google/adk/tests/unittests/tools/application_integration_tool/test_application_integration_toolset.py +345 -0
  222. google/adk/tests/unittests/tools/google_api_tool/__init__.py +13 -0
  223. google/adk/tests/unittests/tools/google_api_tool/test_googleapi_to_openapi_converter.py +657 -0
  224. google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_auto_auth_credential_exchanger.py +145 -0
  225. google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_base_auth_credential_exchanger.py +68 -0
  226. google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_oauth2_exchanger.py +153 -0
  227. google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_service_account_exchanger.py +196 -0
  228. google/adk/tests/unittests/tools/openapi_tool/auth/test_auth_helper.py +573 -0
  229. google/adk/tests/unittests/tools/openapi_tool/common/test_common.py +436 -0
  230. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test.yaml +1367 -0
  231. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_openapi_spec_parser.py +628 -0
  232. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_openapi_toolset.py +139 -0
  233. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_operation_parser.py +406 -0
  234. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_rest_api_tool.py +966 -0
  235. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_tool_auth_handler.py +201 -0
  236. google/adk/tests/unittests/tools/retrieval/__init__.py +14 -0
  237. google/adk/tests/unittests/tools/retrieval/test_vertex_ai_rag_retrieval.py +147 -0
  238. google/adk/tests/unittests/tools/test_agent_tool.py +167 -0
  239. google/adk/tests/unittests/tools/test_base_tool.py +141 -0
  240. google/adk/tests/unittests/tools/test_build_function_declaration.py +277 -0
  241. google/adk/tests/unittests/utils.py +304 -0
  242. google/adk/tools/__init__.py +51 -0
  243. google/adk/tools/_automatic_function_calling_util.py +346 -0
  244. google/adk/tools/agent_tool.py +176 -0
  245. google/adk/tools/apihub_tool/__init__.py +19 -0
  246. google/adk/tools/apihub_tool/apihub_toolset.py +209 -0
  247. google/adk/tools/apihub_tool/clients/__init__.py +13 -0
  248. google/adk/tools/apihub_tool/clients/apihub_client.py +332 -0
  249. google/adk/tools/apihub_tool/clients/secret_client.py +115 -0
  250. google/adk/tools/application_integration_tool/__init__.py +19 -0
  251. google/adk/tools/application_integration_tool/application_integration_toolset.py +230 -0
  252. google/adk/tools/application_integration_tool/clients/connections_client.py +903 -0
  253. google/adk/tools/application_integration_tool/clients/integration_client.py +253 -0
  254. google/adk/tools/base_tool.py +144 -0
  255. google/adk/tools/built_in_code_execution_tool.py +59 -0
  256. google/adk/tools/crewai_tool.py +72 -0
  257. google/adk/tools/example_tool.py +62 -0
  258. google/adk/tools/exit_loop_tool.py +23 -0
  259. google/adk/tools/function_parameter_parse_util.py +307 -0
  260. google/adk/tools/function_tool.py +87 -0
  261. google/adk/tools/get_user_choice_tool.py +28 -0
  262. google/adk/tools/google_api_tool/__init__.py +14 -0
  263. google/adk/tools/google_api_tool/google_api_tool.py +59 -0
  264. google/adk/tools/google_api_tool/google_api_tool_set.py +107 -0
  265. google/adk/tools/google_api_tool/google_api_tool_sets.py +55 -0
  266. google/adk/tools/google_api_tool/googleapi_to_openapi_converter.py +521 -0
  267. google/adk/tools/google_search_tool.py +68 -0
  268. google/adk/tools/langchain_tool.py +86 -0
  269. google/adk/tools/load_artifacts_tool.py +113 -0
  270. google/adk/tools/load_memory_tool.py +58 -0
  271. google/adk/tools/load_web_page.py +41 -0
  272. google/adk/tools/long_running_tool.py +39 -0
  273. google/adk/tools/mcp_tool/__init__.py +42 -0
  274. google/adk/tools/mcp_tool/conversion_utils.py +161 -0
  275. google/adk/tools/mcp_tool/mcp_tool.py +113 -0
  276. google/adk/tools/mcp_tool/mcp_toolset.py +272 -0
  277. google/adk/tools/openapi_tool/__init__.py +21 -0
  278. google/adk/tools/openapi_tool/auth/__init__.py +19 -0
  279. google/adk/tools/openapi_tool/auth/auth_helpers.py +498 -0
  280. google/adk/tools/openapi_tool/auth/credential_exchangers/__init__.py +25 -0
  281. google/adk/tools/openapi_tool/auth/credential_exchangers/auto_auth_credential_exchanger.py +105 -0
  282. google/adk/tools/openapi_tool/auth/credential_exchangers/base_credential_exchanger.py +55 -0
  283. google/adk/tools/openapi_tool/auth/credential_exchangers/oauth2_exchanger.py +117 -0
  284. google/adk/tools/openapi_tool/auth/credential_exchangers/service_account_exchanger.py +97 -0
  285. google/adk/tools/openapi_tool/common/__init__.py +19 -0
  286. google/adk/tools/openapi_tool/common/common.py +300 -0
  287. google/adk/tools/openapi_tool/openapi_spec_parser/__init__.py +32 -0
  288. google/adk/tools/openapi_tool/openapi_spec_parser/openapi_spec_parser.py +231 -0
  289. google/adk/tools/openapi_tool/openapi_spec_parser/openapi_toolset.py +144 -0
  290. google/adk/tools/openapi_tool/openapi_spec_parser/operation_parser.py +260 -0
  291. google/adk/tools/openapi_tool/openapi_spec_parser/rest_api_tool.py +496 -0
  292. google/adk/tools/openapi_tool/openapi_spec_parser/tool_auth_handler.py +268 -0
  293. google/adk/tools/preload_memory_tool.py +72 -0
  294. google/adk/tools/retrieval/__init__.py +36 -0
  295. google/adk/tools/retrieval/base_retrieval_tool.py +37 -0
  296. google/adk/tools/retrieval/files_retrieval.py +33 -0
  297. google/adk/tools/retrieval/llama_index_retrieval.py +41 -0
  298. google/adk/tools/retrieval/vertex_ai_rag_retrieval.py +107 -0
  299. google/adk/tools/tool_context.py +90 -0
  300. google/adk/tools/toolbox_tool.py +46 -0
  301. google/adk/tools/transfer_to_agent_tool.py +21 -0
  302. google/adk/tools/vertex_ai_search_tool.py +96 -0
  303. google/adk/version.py +16 -0
  304. google_adk-0.0.1.dist-info/LICENSE.txt → google_adk-0.0.2.dist-info/LICENSE +32 -0
  305. google_adk-0.0.2.dist-info/METADATA +73 -0
  306. google_adk-0.0.2.dist-info/RECORD +308 -0
  307. {google_adk-0.0.1.dist-info → google_adk-0.0.2.dist-info}/WHEEL +1 -2
  308. google_adk-0.0.2.dist-info/entry_points.txt +3 -0
  309. agent_kit/__init__.py +0 -0
  310. google_adk-0.0.1.dist-info/METADATA +0 -15
  311. google_adk-0.0.1.dist-info/RECORD +0 -6
  312. google_adk-0.0.1.dist-info/top_level.txt +0 -1
@@ -0,0 +1,458 @@
1
+ # Copyright 2025 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Handles Code Execution related logic."""
16
+
17
+ from __future__ import annotations
18
+
19
+ import base64
20
+ import copy
21
+ import dataclasses
22
+ import os
23
+ import re
24
+ from typing import AsyncGenerator
25
+ from typing import Generator
26
+ from typing import Optional
27
+ from typing import TYPE_CHECKING
28
+
29
+ from google.genai import types
30
+ from typing_extensions import override
31
+
32
+ from ...agents.invocation_context import InvocationContext
33
+ from ...code_executors.base_code_executor import BaseCodeExecutor
34
+ from ...code_executors.code_execution_utils import CodeExecutionInput
35
+ from ...code_executors.code_execution_utils import CodeExecutionResult
36
+ from ...code_executors.code_execution_utils import CodeExecutionUtils
37
+ from ...code_executors.code_execution_utils import File
38
+ from ...code_executors.code_executor_context import CodeExecutorContext
39
+ from ...events.event import Event
40
+ from ...events.event_actions import EventActions
41
+ from ...models.llm_response import LlmResponse
42
+ from ._base_llm_processor import BaseLlmRequestProcessor
43
+ from ._base_llm_processor import BaseLlmResponseProcessor
44
+
45
+ if TYPE_CHECKING:
46
+ from ...models.llm_request import LlmRequest
47
+
48
+
49
+ @dataclasses.dataclass
50
+ class DataFileUtil:
51
+ """A structure that contains a data file name and its content."""
52
+
53
+ extension: str
54
+ """
55
+ The file extension (e.g., ".csv").
56
+ """
57
+
58
+ loader_code_template: str
59
+ """
60
+ The code template to load the data file.
61
+ """
62
+
63
+
64
+ _DATA_FILE_UTIL_MAP = {
65
+ 'text/csv': DataFileUtil(
66
+ extension='.csv',
67
+ loader_code_template="pd.read_csv('{filename}')",
68
+ ),
69
+ }
70
+
71
+ _DATA_FILE_HELPER_LIB = '''
72
+ import pandas as pd
73
+
74
+ def explore_df(df: pd.DataFrame) -> None:
75
+ """Prints some information about a pandas DataFrame."""
76
+
77
+ with pd.option_context(
78
+ 'display.max_columns', None, 'display.expand_frame_repr', False
79
+ ):
80
+ # Print the column names to never encounter KeyError when selecting one.
81
+ df_dtypes = df.dtypes
82
+
83
+ # Obtain information about data types and missing values.
84
+ df_nulls = (len(df) - df.isnull().sum()).apply(
85
+ lambda x: f'{x} / {df.shape[0]} non-null'
86
+ )
87
+
88
+ # Explore unique total values in columns using `.unique()`.
89
+ df_unique_count = df.apply(lambda x: len(x.unique()))
90
+
91
+ # Explore unique values in columns using `.unique()`.
92
+ df_unique = df.apply(lambda x: crop(str(list(x.unique()))))
93
+
94
+ df_info = pd.concat(
95
+ (
96
+ df_dtypes.rename('Dtype'),
97
+ df_nulls.rename('Non-Null Count'),
98
+ df_unique_count.rename('Unique Values Count'),
99
+ df_unique.rename('Unique Values'),
100
+ ),
101
+ axis=1,
102
+ )
103
+ df_info.index.name = 'Columns'
104
+ print(f"""Total rows: {df.shape[0]}
105
+ Total columns: {df.shape[1]}
106
+
107
+ {df_info}""")
108
+ '''
109
+
110
+
111
+ class _CodeExecutionRequestProcessor(BaseLlmRequestProcessor):
112
+ """Processes code execution requests."""
113
+
114
+ @override
115
+ async def run_async(
116
+ self, invocation_context: InvocationContext, llm_request: LlmRequest
117
+ ) -> AsyncGenerator[Event, None]:
118
+ from ...agents.llm_agent import LlmAgent
119
+
120
+ if not isinstance(invocation_context.agent, LlmAgent):
121
+ return
122
+ if not invocation_context.agent.code_executor:
123
+ return
124
+
125
+ for event in _run_pre_processor(invocation_context, llm_request):
126
+ yield event
127
+
128
+ # Convert the code execution parts to text parts.
129
+ if not isinstance(invocation_context.agent.code_executor, BaseCodeExecutor):
130
+ return
131
+ for content in llm_request.contents:
132
+ CodeExecutionUtils.convert_code_execution_parts(
133
+ content,
134
+ invocation_context.agent.code_executor.code_block_delimiters[0]
135
+ if invocation_context.agent.code_executor.code_block_delimiters
136
+ else ('', ''),
137
+ invocation_context.agent.code_executor.execution_result_delimiters,
138
+ )
139
+
140
+
141
+ request_processor = _CodeExecutionRequestProcessor()
142
+
143
+
144
+ class _CodeExecutionResponseProcessor(BaseLlmResponseProcessor):
145
+ """Processes code execution responses."""
146
+
147
+ @override
148
+ async def run_async(
149
+ self, invocation_context: InvocationContext, llm_response: LlmResponse
150
+ ) -> AsyncGenerator[Event, None]:
151
+ # Skip if the response is partial (streaming).
152
+ if llm_response.partial:
153
+ return
154
+
155
+ for event in _run_post_processor(invocation_context, llm_response):
156
+ yield event
157
+
158
+
159
+ response_processor = _CodeExecutionResponseProcessor()
160
+
161
+
162
+ def _run_pre_processor(
163
+ invocation_context: InvocationContext,
164
+ llm_request: LlmRequest,
165
+ ) -> Generator[Event, None, None]:
166
+ """Pre-process the user message by adding the user message to the Colab notebook."""
167
+ from ...agents.llm_agent import LlmAgent
168
+
169
+ if not isinstance(invocation_context.agent, LlmAgent):
170
+ return
171
+
172
+ agent = invocation_context.agent
173
+ code_executor = agent.code_executor
174
+
175
+ if not code_executor or not isinstance(code_executor, BaseCodeExecutor):
176
+ return
177
+ if not code_executor.optimize_data_file:
178
+ return
179
+
180
+ code_executor_context = CodeExecutorContext(invocation_context.session.state)
181
+
182
+ # Skip if the error count exceeds the max retry attempts.
183
+ if (
184
+ code_executor_context.get_error_count(invocation_context.invocation_id)
185
+ >= code_executor.error_retry_attempts
186
+ ):
187
+ return
188
+
189
+ # [Step 1] Extract data files from the session_history and store them in
190
+ # memory. Meanwhile, mutate the inline data file to text part in session
191
+ # history from all turns.
192
+ all_input_files = _extrac_and_replace_inline_files(
193
+ code_executor_context, llm_request
194
+ )
195
+
196
+ # [Step 2] Run Explore_Df code on the data files from the current turn. We
197
+ # only need to explore the new data files because the previous data files
198
+ # should already be explored and cached in the code execution runtime.
199
+ processed_file_names = set(code_executor_context.get_processed_file_names())
200
+ files_to_process = [
201
+ f for f in all_input_files if f.name not in processed_file_names
202
+ ]
203
+ for file in files_to_process:
204
+ code_str = _get_data_file_preprocessing_code(file)
205
+ # Skip for unsupported file or executor types.
206
+ if not code_str:
207
+ return
208
+
209
+ # Emit the code to execute, and add it to the LLM request.
210
+ code_content = types.Content(
211
+ role='model',
212
+ parts=[
213
+ types.Part(text=f'Processing input file: `{file.name}`'),
214
+ CodeExecutionUtils.build_executable_code_part(code_str),
215
+ ],
216
+ )
217
+ llm_request.contents.append(copy.deepcopy(code_content))
218
+ yield Event(
219
+ invocation_id=invocation_context.invocation_id,
220
+ author=agent.name,
221
+ branch=invocation_context.branch,
222
+ content=code_content,
223
+ )
224
+
225
+ code_execution_result = code_executor.execute_code(
226
+ invocation_context,
227
+ CodeExecutionInput(
228
+ code=code_str,
229
+ input_files=[file],
230
+ execution_id=_get_or_set_execution_id(
231
+ invocation_context, code_executor_context
232
+ ),
233
+ ),
234
+ )
235
+ # Update the processing results to code executor context.
236
+ code_executor_context.update_code_execution_result(
237
+ invocation_context.invocation_id,
238
+ code_str,
239
+ code_execution_result.stdout,
240
+ code_execution_result.stderr,
241
+ )
242
+ code_executor_context.add_processed_file_names([file.name])
243
+
244
+ # Emit the execution result, and add it to the LLM request.
245
+ execution_result_event = _post_process_code_execution_result(
246
+ invocation_context, code_executor_context, code_execution_result
247
+ )
248
+ yield execution_result_event
249
+ llm_request.contents.append(copy.deepcopy(execution_result_event.content))
250
+
251
+
252
+ def _run_post_processor(
253
+ invocation_context: InvocationContext,
254
+ llm_response,
255
+ ) -> Generator[Event, None, None]:
256
+ """Post-process the model response by extracting and executing the first code block."""
257
+ agent = invocation_context.agent
258
+ code_executor = agent.code_executor
259
+
260
+ if not code_executor or not isinstance(code_executor, BaseCodeExecutor):
261
+ return
262
+ if not llm_response or not llm_response.content:
263
+ return
264
+
265
+ code_executor_context = CodeExecutorContext(invocation_context.session.state)
266
+ # Skip if the error count exceeds the max retry attempts.
267
+ if (
268
+ code_executor_context.get_error_count(invocation_context.invocation_id)
269
+ >= code_executor.error_retry_attempts
270
+ ):
271
+ return
272
+
273
+ # [Step 1] Extract code from the model predict response and truncate the
274
+ # content to the part with the first code block.
275
+ response_content = llm_response.content
276
+ code_str = CodeExecutionUtils.extract_code_and_truncate_content(
277
+ response_content, code_executor.code_block_delimiters
278
+ )
279
+ # Terminal state: no code to execute.
280
+ if not code_str:
281
+ return
282
+
283
+ # [Step 2] Executes the code and emit 2 Events for code and execution result.
284
+ yield Event(
285
+ invocation_id=invocation_context.invocation_id,
286
+ author=agent.name,
287
+ branch=invocation_context.branch,
288
+ content=response_content,
289
+ actions=EventActions(),
290
+ )
291
+
292
+ code_execution_result = code_executor.execute_code(
293
+ invocation_context,
294
+ CodeExecutionInput(
295
+ code=code_str,
296
+ input_files=code_executor_context.get_input_files(),
297
+ execution_id=_get_or_set_execution_id(
298
+ invocation_context, code_executor_context
299
+ ),
300
+ ),
301
+ )
302
+ code_executor_context.update_code_execution_result(
303
+ invocation_context.invocation_id,
304
+ code_str,
305
+ code_execution_result.stdout,
306
+ code_execution_result.stderr,
307
+ )
308
+ yield _post_process_code_execution_result(
309
+ invocation_context, code_executor_context, code_execution_result
310
+ )
311
+
312
+ # [Step 3] Skip processing the original model response
313
+ # to continue code generation loop.
314
+ llm_response.content = None
315
+
316
+
317
+ def _extrac_and_replace_inline_files(
318
+ code_executor_context: CodeExecutorContext,
319
+ llm_request: LlmRequest,
320
+ ) -> list[File]:
321
+ """Extracts and replaces inline files with file names in the LLM request."""
322
+ all_input_files = code_executor_context.get_input_files()
323
+ saved_file_names = set(f.name for f in all_input_files)
324
+
325
+ # [Step 1] Process input files from LlmRequest and cache them in CodeExecutor.
326
+ for i in range(len(llm_request.contents)):
327
+ content = llm_request.contents[i]
328
+ # Only process the user message.
329
+ if content.role != 'user' and not content.parts:
330
+ continue
331
+
332
+ for j in range(len(content.parts)):
333
+ part = content.parts[j]
334
+ # Skip if the inline data is not supported.
335
+ if (
336
+ not part.inline_data
337
+ or part.inline_data.mime_type not in _DATA_FILE_UTIL_MAP
338
+ ):
339
+ continue
340
+
341
+ # Replace the inline data file with a file name placeholder.
342
+ mime_type = part.inline_data.mime_type
343
+ file_name = f'data_{i+1}_{j+1}' + _DATA_FILE_UTIL_MAP[mime_type].extension
344
+ llm_request.contents[i].parts[j] = types.Part(
345
+ text='\nAvailable file: `%s`\n' % file_name
346
+ )
347
+
348
+ # Add the inlne data as input file to the code executor context.
349
+ file = File(
350
+ name=file_name,
351
+ content=CodeExecutionUtils.get_encoded_file_content(
352
+ part.inline_data.data
353
+ ).decode(),
354
+ mime_type=mime_type,
355
+ )
356
+ if file_name not in saved_file_names:
357
+ code_executor_context.add_input_files([file])
358
+ all_input_files.append(file)
359
+
360
+ return all_input_files
361
+
362
+
363
+ def _get_or_set_execution_id(
364
+ invocation_context: InvocationContext,
365
+ code_executor_context: CodeExecutorContext,
366
+ ) -> Optional[str]:
367
+ """Returns the ID for stateful code execution or None if not stateful."""
368
+ if not invocation_context.agent.code_executor.stateful:
369
+ return None
370
+
371
+ execution_id = code_executor_context.get_execution_id()
372
+ if not execution_id:
373
+ execution_id = invocation_context.session.id
374
+ code_executor_context.set_execution_id(execution_id)
375
+ return execution_id
376
+
377
+
378
+ def _post_process_code_execution_result(
379
+ invocation_context: InvocationContext,
380
+ code_executor_context: CodeExecutorContext,
381
+ code_execution_result: CodeExecutionResult,
382
+ ) -> Event:
383
+ """Post-process the code execution result and emit an Event."""
384
+ if invocation_context.artifact_service is None:
385
+ raise ValueError('Artifact service is not initialized.')
386
+
387
+ result_content = types.Content(
388
+ role='model',
389
+ parts=[
390
+ CodeExecutionUtils.build_code_execution_result_part(
391
+ code_execution_result
392
+ ),
393
+ ],
394
+ )
395
+ event_actions = EventActions(
396
+ state_delta=code_executor_context.get_state_delta()
397
+ )
398
+
399
+ # Handle code execution error retry.
400
+ if code_execution_result.stderr:
401
+ code_executor_context.increment_error_count(
402
+ invocation_context.invocation_id
403
+ )
404
+ else:
405
+ code_executor_context.reset_error_count(invocation_context.invocation_id)
406
+
407
+ # Handle output files.
408
+ for output_file in code_execution_result.output_files:
409
+ version = invocation_context.artifact_service.save_artifact(
410
+ app_name=invocation_context.app_name,
411
+ user_id=invocation_context.user_id,
412
+ session_id=invocation_context.session.id,
413
+ filename=output_file.name,
414
+ artifact=types.Part.from_bytes(
415
+ data=base64.b64decode(output_file.content),
416
+ mime_type=output_file.mime_type,
417
+ ),
418
+ )
419
+ event_actions.artifact_delta[output_file.name] = version
420
+
421
+ return Event(
422
+ invocation_id=invocation_context.invocation_id,
423
+ author=invocation_context.agent.name,
424
+ branch=invocation_context.branch,
425
+ content=result_content,
426
+ actions=event_actions,
427
+ )
428
+
429
+
430
+ def _get_data_file_preprocessing_code(file: File) -> Optional[str]:
431
+ """Returns the code to explore the data file."""
432
+
433
+ def _get_normalized_file_name(file_name: str) -> str:
434
+ var_name, _ = os.path.splitext(file_name)
435
+ # Replace non-alphanumeric characters with underscores
436
+ var_name = re.sub(r'[^a-zA-Z0-9_]', '_', var_name)
437
+
438
+ # If the filename starts with a digit, prepend an underscore
439
+ if var_name[0].isdigit():
440
+ var_name = '_' + var_name
441
+ return var_name
442
+
443
+ if file.mime_type not in _DATA_FILE_UTIL_MAP:
444
+ return
445
+
446
+ var_name = _get_normalized_file_name(file.name)
447
+ loader_code = _DATA_FILE_UTIL_MAP[file.mime_type].loader_code_template.format(
448
+ filename=file.name
449
+ )
450
+ return f"""
451
+ {_DATA_FILE_HELPER_LIB}
452
+
453
+ # Load the dataframe.
454
+ {var_name} = {loader_code}
455
+
456
+ # Use `explore_df` to guide my analysis.
457
+ explore_df({var_name})
458
+ """
@@ -0,0 +1,129 @@
1
+ # Copyright 2025 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Handles NL planning related logic."""
16
+
17
+ from __future__ import annotations
18
+
19
+ from typing import AsyncGenerator
20
+ from typing import Generator
21
+ from typing import Optional
22
+ from typing import TYPE_CHECKING
23
+
24
+ from typing_extensions import override
25
+
26
+ from ...agents.callback_context import CallbackContext
27
+ from ...agents.invocation_context import InvocationContext
28
+ from ...agents.readonly_context import ReadonlyContext
29
+ from ...events.event import Event
30
+ from ...planners.plan_re_act_planner import PlanReActPlanner
31
+ from ._base_llm_processor import BaseLlmRequestProcessor
32
+ from ._base_llm_processor import BaseLlmResponseProcessor
33
+
34
+ if TYPE_CHECKING:
35
+ from ...models.llm_request import LlmRequest
36
+ from ...models.llm_response import LlmResponse
37
+ from ...planners.base_planner import BasePlanner
38
+ from ...planners.built_in_planner import BuiltInPlanner
39
+
40
+
41
+ class _NlPlanningRequestProcessor(BaseLlmRequestProcessor):
42
+ """Processor for NL planning."""
43
+
44
+ async def run_async(
45
+ self, invocation_context: InvocationContext, llm_request: LlmRequest
46
+ ) -> AsyncGenerator[Event, None]:
47
+ from ...planners.built_in_planner import BuiltInPlanner
48
+
49
+ planner = _get_planner(invocation_context)
50
+ if not planner:
51
+ return
52
+
53
+ if isinstance(planner, BuiltInPlanner):
54
+ planner.apply_thinking_config(llm_request)
55
+
56
+ planning_instruction = planner.build_planning_instruction(
57
+ ReadonlyContext(invocation_context), llm_request
58
+ )
59
+ if planning_instruction:
60
+ llm_request.append_instructions([planning_instruction])
61
+
62
+ _remove_thought_from_request(llm_request)
63
+
64
+ # Maintain async generator behavior
65
+ if False: # Ensures it behaves as a generator
66
+ yield # This is a no-op but maintains generator structure
67
+
68
+
69
+ request_processor = _NlPlanningRequestProcessor()
70
+
71
+
72
+ class _NlPlanningResponse(BaseLlmResponseProcessor):
73
+
74
+ @override
75
+ async def run_async(
76
+ self, invocation_context: InvocationContext, llm_response: LlmResponse
77
+ ) -> AsyncGenerator[Event, None]:
78
+ if (
79
+ not llm_response
80
+ or not llm_response.content
81
+ or not llm_response.content.parts
82
+ ):
83
+ return
84
+
85
+ planner = _get_planner(invocation_context)
86
+ if not planner:
87
+ return
88
+
89
+ # Postprocess the LLM response.
90
+ processed_parts = planner.process_planning_response(
91
+ CallbackContext(invocation_context), llm_response.content.parts
92
+ )
93
+ if processed_parts:
94
+ llm_response.content.parts = processed_parts
95
+
96
+ # Maintain async generator behavior
97
+ if False: # Ensures it behaves as a generator
98
+ yield # This is a no-op but maintains generator structure
99
+
100
+
101
+ response_processor = _NlPlanningResponse()
102
+
103
+
104
+ def _get_planner(
105
+ invocation_context: InvocationContext,
106
+ ) -> Optional[BasePlanner]:
107
+ from ...agents.llm_agent import Agent
108
+ from ...planners.base_planner import BasePlanner
109
+
110
+ agent = invocation_context.agent
111
+ if not isinstance(agent, Agent):
112
+ return None
113
+ if not agent.planner:
114
+ return None
115
+
116
+ if isinstance(agent.planner, BasePlanner):
117
+ return agent.planner
118
+ return PlanReActPlanner()
119
+
120
+
121
+ def _remove_thought_from_request(llm_request: LlmRequest):
122
+ if not llm_request.contents:
123
+ return
124
+
125
+ for content in llm_request.contents:
126
+ if not content.parts:
127
+ continue
128
+ for part in content.parts:
129
+ part.thought = None