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,673 @@
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
+
16
+ import base64
17
+ import json
18
+ import logging
19
+ from typing import Any
20
+ from typing import AsyncGenerator
21
+ from typing import cast
22
+ from typing import Dict
23
+ from typing import Generator
24
+ from typing import Iterable
25
+ from typing import Literal
26
+ from typing import Optional
27
+ from typing import Tuple
28
+ from typing import Union
29
+
30
+ from google.genai import types
31
+ from litellm import acompletion
32
+ from litellm import ChatCompletionAssistantMessage
33
+ from litellm import ChatCompletionDeveloperMessage
34
+ from litellm import ChatCompletionImageUrlObject
35
+ from litellm import ChatCompletionMessageToolCall
36
+ from litellm import ChatCompletionTextObject
37
+ from litellm import ChatCompletionToolMessage
38
+ from litellm import ChatCompletionUserMessage
39
+ from litellm import ChatCompletionVideoUrlObject
40
+ from litellm import completion
41
+ from litellm import CustomStreamWrapper
42
+ from litellm import Function
43
+ from litellm import Message
44
+ from litellm import ModelResponse
45
+ from litellm import OpenAIMessageContent
46
+ from pydantic import BaseModel
47
+ from pydantic import Field
48
+ from typing_extensions import override
49
+
50
+ from .base_llm import BaseLlm
51
+ from .llm_request import LlmRequest
52
+ from .llm_response import LlmResponse
53
+
54
+ logger = logging.getLogger(__name__)
55
+
56
+ _NEW_LINE = "\n"
57
+ _EXCLUDED_PART_FIELD = {"inline_data": {"data"}}
58
+
59
+
60
+ class FunctionChunk(BaseModel):
61
+ id: Optional[str]
62
+ name: Optional[str]
63
+ args: Optional[str]
64
+
65
+
66
+ class TextChunk(BaseModel):
67
+ text: str
68
+
69
+
70
+ class LiteLLMClient:
71
+ """Provides acompletion method (for better testability)."""
72
+
73
+ async def acompletion(
74
+ self, model, messages, tools, **kwargs
75
+ ) -> Union[ModelResponse, CustomStreamWrapper]:
76
+ """Asynchronously calls acompletion.
77
+
78
+ Args:
79
+ model: The model name.
80
+ messages: The messages to send to the model.
81
+ tools: The tools to use for the model.
82
+ **kwargs: Additional arguments to pass to acompletion.
83
+
84
+ Returns:
85
+ The model response as a message.
86
+ """
87
+
88
+ return await acompletion(
89
+ model=model,
90
+ messages=messages,
91
+ tools=tools,
92
+ **kwargs,
93
+ )
94
+
95
+ def completion(
96
+ self, model, messages, tools, stream=False, **kwargs
97
+ ) -> Union[ModelResponse, CustomStreamWrapper]:
98
+ """Synchronously calls completion. This is used for streaming only.
99
+
100
+ Args:
101
+ model: The model to use.
102
+ messages: The messages to send.
103
+ tools: The tools to use for the model.
104
+ stream: Whether to stream the response.
105
+ **kwargs: Additional arguments to pass to completion.
106
+
107
+ Returns:
108
+ The response from the model.
109
+ """
110
+
111
+ return completion(
112
+ model=model,
113
+ messages=messages,
114
+ tools=tools,
115
+ stream=stream,
116
+ **kwargs,
117
+ )
118
+
119
+
120
+ def _safe_json_serialize(obj) -> str:
121
+ """Convert any Python object to a JSON-serializable type or string.
122
+
123
+ Args:
124
+ obj: The object to serialize.
125
+
126
+ Returns:
127
+ The JSON-serialized object string or string.
128
+ """
129
+
130
+ try:
131
+ # Try direct JSON serialization first
132
+ return json.dumps(obj)
133
+ except (TypeError, OverflowError):
134
+ return str(obj)
135
+
136
+
137
+ def _content_to_message_param(
138
+ content: types.Content,
139
+ ) -> Message:
140
+ """Converts a types.Content to a litellm Message.
141
+
142
+ Args:
143
+ content: The content to convert.
144
+
145
+ Returns:
146
+ The litellm Message.
147
+ """
148
+
149
+ if content.parts and content.parts[0].function_response:
150
+ return ChatCompletionToolMessage(
151
+ role="tool",
152
+ tool_call_id=content.parts[0].function_response.id,
153
+ content=_safe_json_serialize(
154
+ content.parts[0].function_response.response
155
+ ),
156
+ )
157
+
158
+ role = _to_litellm_role(content.role)
159
+
160
+ if role == "user":
161
+ return ChatCompletionUserMessage(
162
+ role="user", content=_get_content(content.parts)
163
+ )
164
+ else:
165
+
166
+ tool_calls = [
167
+ ChatCompletionMessageToolCall(
168
+ type="function",
169
+ id=part.function_call.id,
170
+ function=Function(
171
+ name=part.function_call.name,
172
+ arguments=part.function_call.args,
173
+ ),
174
+ )
175
+ for part in content.parts
176
+ if part.function_call
177
+ ]
178
+
179
+ return ChatCompletionAssistantMessage(
180
+ role=role,
181
+ content=_get_content(content.parts),
182
+ tool_calls=tool_calls or None,
183
+ )
184
+
185
+
186
+ def _get_content(parts: Iterable[types.Part]) -> OpenAIMessageContent | str:
187
+ """Converts a list of parts to litellm content.
188
+
189
+ Args:
190
+ parts: The parts to convert.
191
+
192
+ Returns:
193
+ The litellm content.
194
+ """
195
+
196
+ content_objects = []
197
+ for part in parts:
198
+ if part.text:
199
+ if len(parts) == 1:
200
+ return part.text
201
+ content_objects.append(
202
+ ChatCompletionTextObject(
203
+ type="text",
204
+ text=part.text,
205
+ )
206
+ )
207
+ elif (
208
+ part.inline_data
209
+ and part.inline_data.data
210
+ and part.inline_data.mime_type
211
+ ):
212
+ base64_string = base64.b64encode(part.inline_data.data).decode("utf-8")
213
+ data_uri = f"data:{part.inline_data.mime_type};base64,{base64_string}"
214
+
215
+ if part.inline_data.mime_type.startswith("image"):
216
+ content_objects.append(
217
+ ChatCompletionImageUrlObject(
218
+ type="image_url",
219
+ image_url=data_uri,
220
+ )
221
+ )
222
+ elif part.inline_data.mime_type.startswith("video"):
223
+ content_objects.append(
224
+ ChatCompletionVideoUrlObject(
225
+ type="video_url",
226
+ video_url=data_uri,
227
+ )
228
+ )
229
+ else:
230
+ raise ValueError("LiteLlm(BaseLlm) does not support this content part.")
231
+
232
+ return content_objects
233
+
234
+
235
+ def _to_litellm_role(role: Optional[str]) -> Literal["user", "assistant"]:
236
+ """Converts a types.Content role to a litellm role.
237
+
238
+ Args:
239
+ role: The types.Content role.
240
+
241
+ Returns:
242
+ The litellm role.
243
+ """
244
+
245
+ if role in ["model", "assistant"]:
246
+ return "assistant"
247
+ return "user"
248
+
249
+
250
+ TYPE_LABELS = {
251
+ "STRING": "string",
252
+ "NUMBER": "number",
253
+ "BOOLEAN": "boolean",
254
+ "OBJECT": "object",
255
+ "ARRAY": "array",
256
+ "INTEGER": "integer",
257
+ }
258
+
259
+
260
+ def _schema_to_dict(schema: types.Schema) -> dict:
261
+ """Recursively converts a types.Schema to a dictionary.
262
+
263
+ Args:
264
+ schema: The schema to convert.
265
+
266
+ Returns:
267
+ The dictionary representation of the schema.
268
+ """
269
+
270
+ schema_dict = schema.model_dump(exclude_none=True)
271
+ if "type" in schema_dict:
272
+ schema_dict["type"] = schema_dict["type"].lower()
273
+ if "items" in schema_dict:
274
+ if isinstance(schema_dict["items"], dict):
275
+ schema_dict["items"] = _schema_to_dict(
276
+ types.Schema.model_validate(schema_dict["items"])
277
+ )
278
+ elif isinstance(schema_dict["items"]["type"], types.Type):
279
+ schema_dict["items"]["type"] = TYPE_LABELS[
280
+ schema_dict["items"]["type"].value
281
+ ]
282
+ if "properties" in schema_dict:
283
+ properties = {}
284
+ for key, value in schema_dict["properties"].items():
285
+ if isinstance(value, types.Schema):
286
+ properties[key] = _schema_to_dict(value)
287
+ else:
288
+ properties[key] = value
289
+ if "type" in properties[key]:
290
+ properties[key]["type"] = properties[key]["type"].lower()
291
+ schema_dict["properties"] = properties
292
+ return schema_dict
293
+
294
+
295
+ def _function_declaration_to_tool_param(
296
+ function_declaration: types.FunctionDeclaration,
297
+ ) -> dict:
298
+ """Converts a types.FunctionDeclaration to a openapi spec dictionary.
299
+
300
+ Args:
301
+ function_declaration: The function declaration to convert.
302
+
303
+ Returns:
304
+ The openapi spec dictionary representation of the function declaration.
305
+ """
306
+
307
+ assert function_declaration.name
308
+
309
+ properties = {}
310
+ if (
311
+ function_declaration.parameters
312
+ and function_declaration.parameters.properties
313
+ ):
314
+ for key, value in function_declaration.parameters.properties.items():
315
+ properties[key] = _schema_to_dict(value)
316
+
317
+ return {
318
+ "type": "function",
319
+ "function": {
320
+ "name": function_declaration.name,
321
+ "description": function_declaration.description or "",
322
+ "parameters": {
323
+ "type": "object",
324
+ "properties": properties,
325
+ },
326
+ },
327
+ }
328
+
329
+
330
+ def _model_response_to_chunk(
331
+ response: ModelResponse,
332
+ ) -> Generator[
333
+ Tuple[Optional[Union[TextChunk, FunctionChunk]], Optional[str]], None, None
334
+ ]:
335
+ """Converts a litellm message to text or function chunk.
336
+
337
+ Args:
338
+ response: The response from the model.
339
+
340
+ Yields:
341
+ A tuple of text or function chunk and finish reason.
342
+ """
343
+
344
+ message = None
345
+ if response.get("choices", None):
346
+ message = response["choices"][0].get("message", None)
347
+ finish_reason = response["choices"][0].get("finish_reason", None)
348
+ # check streaming delta
349
+ if message is None and response["choices"][0].get("delta", None):
350
+ message = response["choices"][0]["delta"]
351
+
352
+ if message.get("content", None):
353
+ yield TextChunk(text=message.get("content")), finish_reason
354
+
355
+ if message.get("tool_calls", None):
356
+ for tool_call in message.get("tool_calls"):
357
+ # aggregate tool_call
358
+ if tool_call.type == "function":
359
+ yield FunctionChunk(
360
+ id=tool_call.id,
361
+ name=tool_call.function.name,
362
+ args=tool_call.function.arguments,
363
+ ), finish_reason
364
+
365
+ if finish_reason and not (
366
+ message.get("content", None) or message.get("tool_calls", None)
367
+ ):
368
+ yield None, finish_reason
369
+
370
+ if not message:
371
+ yield None, None
372
+
373
+
374
+ def _model_response_to_generate_content_response(
375
+ response: ModelResponse,
376
+ ) -> LlmResponse:
377
+ """Converts a litellm response to LlmResponse.
378
+
379
+ Args:
380
+ response: The model response.
381
+
382
+ Returns:
383
+ The LlmResponse.
384
+ """
385
+
386
+ message = None
387
+ if response.get("choices", None):
388
+ message = response["choices"][0].get("message", None)
389
+
390
+ if not message:
391
+ raise ValueError("No message in response")
392
+ return _message_to_generate_content_response(message)
393
+
394
+
395
+ def _message_to_generate_content_response(
396
+ message: Message, is_partial: bool = False
397
+ ) -> LlmResponse:
398
+ """Converts a litellm message to LlmResponse.
399
+
400
+ Args:
401
+ message: The message to convert.
402
+ is_partial: Whether the message is partial.
403
+
404
+ Returns:
405
+ The LlmResponse.
406
+ """
407
+
408
+ parts = []
409
+ if message.get("content", None):
410
+ parts.append(types.Part.from_text(text=message.get("content")))
411
+
412
+ if message.get("tool_calls", None):
413
+ for tool_call in message.get("tool_calls"):
414
+ if tool_call.type == "function":
415
+ part = types.Part.from_function_call(
416
+ name=tool_call.function.name,
417
+ args=json.loads(tool_call.function.arguments or "{}"),
418
+ )
419
+ part.function_call.id = tool_call.id
420
+ parts.append(part)
421
+
422
+ return LlmResponse(
423
+ content=types.Content(role="model", parts=parts), partial=is_partial
424
+ )
425
+
426
+
427
+ def _get_completion_inputs(
428
+ llm_request: LlmRequest,
429
+ ) -> tuple[Iterable[Message], Iterable[dict]]:
430
+ """Converts an LlmRequest to litellm inputs.
431
+
432
+ Args:
433
+ llm_request: The LlmRequest to convert.
434
+
435
+ Returns:
436
+ The litellm inputs (message list and tool dictionary).
437
+ """
438
+ messages = [
439
+ _content_to_message_param(content)
440
+ for content in llm_request.contents or []
441
+ ]
442
+
443
+ if llm_request.config.system_instruction:
444
+ messages.insert(
445
+ 0,
446
+ ChatCompletionDeveloperMessage(
447
+ role="developer",
448
+ content=llm_request.config.system_instruction,
449
+ ),
450
+ )
451
+
452
+ tools = None
453
+ if (
454
+ llm_request.config
455
+ and llm_request.config.tools
456
+ and llm_request.config.tools[0].function_declarations
457
+ ):
458
+ tools = [
459
+ _function_declaration_to_tool_param(tool)
460
+ for tool in llm_request.config.tools[0].function_declarations
461
+ ]
462
+ return messages, tools
463
+
464
+
465
+ def _build_function_declaration_log(
466
+ func_decl: types.FunctionDeclaration,
467
+ ) -> str:
468
+ """Builds a function declaration log.
469
+
470
+ Args:
471
+ func_decl: The function declaration to convert.
472
+
473
+ Returns:
474
+ The function declaration log.
475
+ """
476
+
477
+ param_str = "{}"
478
+ if func_decl.parameters and func_decl.parameters.properties:
479
+ param_str = str({
480
+ k: v.model_dump(exclude_none=True)
481
+ for k, v in func_decl.parameters.properties.items()
482
+ })
483
+ return_str = "None"
484
+ if func_decl.response:
485
+ return_str = str(func_decl.response.model_dump(exclude_none=True))
486
+ return f"{func_decl.name}: {param_str} -> {return_str}"
487
+
488
+
489
+ def _build_request_log(req: LlmRequest) -> str:
490
+ """Builds a request log.
491
+
492
+ Args:
493
+ req: The request to convert.
494
+
495
+ Returns:
496
+ The request log.
497
+ """
498
+
499
+ function_decls: list[types.FunctionDeclaration] = cast(
500
+ list[types.FunctionDeclaration],
501
+ req.config.tools[0].function_declarations if req.config.tools else [],
502
+ )
503
+ function_logs = (
504
+ [
505
+ _build_function_declaration_log(func_decl)
506
+ for func_decl in function_decls
507
+ ]
508
+ if function_decls
509
+ else []
510
+ )
511
+ contents_logs = [
512
+ content.model_dump_json(
513
+ exclude_none=True,
514
+ exclude={
515
+ "parts": {
516
+ i: _EXCLUDED_PART_FIELD for i in range(len(content.parts))
517
+ }
518
+ },
519
+ )
520
+ for content in req.contents
521
+ ]
522
+
523
+ return f"""
524
+ LLM Request:
525
+ -----------------------------------------------------------
526
+ System Instruction:
527
+ {req.config.system_instruction}
528
+ -----------------------------------------------------------
529
+ Contents:
530
+ {_NEW_LINE.join(contents_logs)}
531
+ -----------------------------------------------------------
532
+ Functions:
533
+ {_NEW_LINE.join(function_logs)}
534
+ -----------------------------------------------------------
535
+ """
536
+
537
+
538
+ class LiteLlm(BaseLlm):
539
+ """Wrapper around litellm.
540
+
541
+ This wrapper can be used with any of the models supported by litellm. The
542
+ environment variable(s) needed for authenticating with the model endpoint must
543
+ be set prior to instantiating this class.
544
+
545
+ Example usage:
546
+ ```
547
+ os.environ["VERTEXAI_PROJECT"] = "your-gcp-project-id"
548
+ os.environ["VERTEXAI_LOCATION"] = "your-gcp-location"
549
+
550
+ agent = Agent(
551
+ model=LiteLlm(model="vertex_ai/claude-3-7-sonnet@20250219"),
552
+ ...
553
+ )
554
+ ```
555
+
556
+ Attributes:
557
+ model: The name of the LiteLlm model.
558
+ llm_client: The LLM client to use for the model.
559
+ model_config: The model config.
560
+ """
561
+
562
+ llm_client: LiteLLMClient = Field(default_factory=LiteLLMClient)
563
+ """The LLM client to use for the model."""
564
+
565
+ _additional_args: Dict[str, Any] = None
566
+
567
+ def __init__(self, model: str, **kwargs):
568
+ """Initializes the LiteLlm class.
569
+
570
+ Args:
571
+ model: The name of the LiteLlm model.
572
+ **kwargs: Additional arguments to pass to the litellm completion api.
573
+ """
574
+ super().__init__(model=model, **kwargs)
575
+ self._additional_args = kwargs
576
+ # preventing generation call with llm_client
577
+ # and overriding messages, tools and stream which are managed internally
578
+ self._additional_args.pop("llm_client", None)
579
+ self._additional_args.pop("messages", None)
580
+ self._additional_args.pop("tools", None)
581
+ # public api called from runner determines to stream or not
582
+ self._additional_args.pop("stream", None)
583
+
584
+ async def generate_content_async(
585
+ self, llm_request: LlmRequest, stream: bool = False
586
+ ) -> AsyncGenerator[LlmResponse, None]:
587
+ """Generates content asynchronously.
588
+
589
+ Args:
590
+ llm_request: LlmRequest, the request to send to the LiteLlm model.
591
+ stream: bool = False, whether to do streaming call.
592
+
593
+ Yields:
594
+ LlmResponse: The model response.
595
+ """
596
+
597
+ logger.info(_build_request_log(llm_request))
598
+
599
+ messages, tools = _get_completion_inputs(llm_request)
600
+
601
+ completion_args = {
602
+ "model": self.model,
603
+ "messages": messages,
604
+ "tools": tools,
605
+ }
606
+ completion_args.update(self._additional_args)
607
+
608
+ if stream:
609
+ text = ""
610
+ function_name = ""
611
+ function_args = ""
612
+ function_id = None
613
+ completion_args["stream"] = True
614
+ for part in self.llm_client.completion(**completion_args):
615
+ for chunk, finish_reason in _model_response_to_chunk(part):
616
+ if isinstance(chunk, FunctionChunk):
617
+ if chunk.name:
618
+ function_name += chunk.name
619
+ if chunk.args:
620
+ function_args += chunk.args
621
+ function_id = chunk.id or function_id
622
+ elif isinstance(chunk, TextChunk):
623
+ text += chunk.text
624
+ yield _message_to_generate_content_response(
625
+ ChatCompletionAssistantMessage(
626
+ role="assistant",
627
+ content=chunk.text,
628
+ ),
629
+ is_partial=True,
630
+ )
631
+ if finish_reason == "tool_calls" and function_id:
632
+ yield _message_to_generate_content_response(
633
+ ChatCompletionAssistantMessage(
634
+ role="assistant",
635
+ content="",
636
+ tool_calls=[
637
+ ChatCompletionMessageToolCall(
638
+ type="function",
639
+ id=function_id,
640
+ function=Function(
641
+ name=function_name,
642
+ arguments=function_args,
643
+ ),
644
+ )
645
+ ],
646
+ )
647
+ )
648
+ function_name = ""
649
+ function_args = ""
650
+ function_id = None
651
+ elif finish_reason == "stop" and text:
652
+ yield _message_to_generate_content_response(
653
+ ChatCompletionAssistantMessage(role="assistant", content=text)
654
+ )
655
+ text = ""
656
+
657
+ else:
658
+ response = await self.llm_client.acompletion(**completion_args)
659
+ yield _model_response_to_generate_content_response(response)
660
+
661
+ @staticmethod
662
+ @override
663
+ def supported_models() -> list[str]:
664
+ """Provides the list of supported models.
665
+
666
+ LiteLlm supports all models supported by litellm. We do not keep track of
667
+ these models here. So we return an empty list.
668
+
669
+ Returns:
670
+ A list of supported models.
671
+ """
672
+
673
+ return []