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.
- google/adk/__init__.py +20 -0
- google/adk/agents/__init__.py +32 -0
- google/adk/agents/active_streaming_tool.py +38 -0
- google/adk/agents/base_agent.py +345 -0
- google/adk/agents/callback_context.py +112 -0
- google/adk/agents/invocation_context.py +181 -0
- google/adk/agents/langgraph_agent.py +140 -0
- google/adk/agents/live_request_queue.py +64 -0
- google/adk/agents/llm_agent.py +376 -0
- google/adk/agents/loop_agent.py +62 -0
- google/adk/agents/parallel_agent.py +96 -0
- google/adk/agents/readonly_context.py +46 -0
- google/adk/agents/remote_agent.py +50 -0
- google/adk/agents/run_config.py +87 -0
- google/adk/agents/sequential_agent.py +45 -0
- google/adk/agents/transcription_entry.py +34 -0
- google/adk/artifacts/__init__.py +23 -0
- google/adk/artifacts/base_artifact_service.py +128 -0
- google/adk/artifacts/gcs_artifact_service.py +195 -0
- google/adk/artifacts/in_memory_artifact_service.py +133 -0
- google/adk/auth/__init__.py +22 -0
- google/adk/auth/auth_credential.py +220 -0
- google/adk/auth/auth_handler.py +268 -0
- google/adk/auth/auth_preprocessor.py +116 -0
- google/adk/auth/auth_schemes.py +67 -0
- google/adk/auth/auth_tool.py +55 -0
- google/adk/cli/__init__.py +15 -0
- google/adk/cli/__main__.py +18 -0
- google/adk/cli/agent_graph.py +122 -0
- google/adk/cli/browser/adk_favicon.svg +17 -0
- google/adk/cli/browser/assets/audio-processor.js +51 -0
- google/adk/cli/browser/assets/config/runtime-config.json +3 -0
- google/adk/cli/browser/index.html +33 -0
- google/adk/cli/browser/main-XUU6OGCC.js +75 -0
- google/adk/cli/browser/polyfills-FFHMD2TL.js +18 -0
- google/adk/cli/browser/styles-4VDSPQ37.css +17 -0
- google/adk/cli/cli.py +181 -0
- google/adk/cli/cli_deploy.py +181 -0
- google/adk/cli/cli_eval.py +282 -0
- google/adk/cli/cli_tools_click.py +479 -0
- google/adk/cli/fast_api.py +774 -0
- google/adk/cli/media_streamer/__init__.py +19 -0
- google/adk/cli/media_streamer/index.html +228 -0
- google/adk/cli/utils/__init__.py +49 -0
- google/adk/cli/utils/envs.py +57 -0
- google/adk/cli/utils/evals.py +93 -0
- google/adk/cli/utils/logs.py +72 -0
- google/adk/code_executors/__init__.py +49 -0
- google/adk/code_executors/base_code_executor.py +97 -0
- google/adk/code_executors/code_execution_utils.py +256 -0
- google/adk/code_executors/code_executor_context.py +202 -0
- google/adk/code_executors/container_code_executor.py +196 -0
- google/adk/code_executors/unsafe_local_code_executor.py +71 -0
- google/adk/code_executors/vertex_ai_code_executor.py +234 -0
- google/adk/evaluation/__init__.py +31 -0
- google/adk/evaluation/agent_evaluator.py +329 -0
- google/adk/evaluation/evaluation_constants.py +24 -0
- google/adk/evaluation/evaluation_generator.py +270 -0
- google/adk/evaluation/response_evaluator.py +135 -0
- google/adk/evaluation/trajectory_evaluator.py +184 -0
- google/adk/events/__init__.py +21 -0
- google/adk/events/event.py +130 -0
- google/adk/events/event_actions.py +55 -0
- google/adk/examples/__init__.py +28 -0
- google/adk/examples/base_example_provider.py +35 -0
- google/adk/examples/example.py +27 -0
- google/adk/examples/example_util.py +123 -0
- google/adk/examples/vertex_ai_example_store.py +104 -0
- google/adk/flows/__init__.py +14 -0
- google/adk/flows/llm_flows/__init__.py +20 -0
- google/adk/flows/llm_flows/_base_llm_processor.py +52 -0
- google/adk/flows/llm_flows/_code_execution.py +458 -0
- google/adk/flows/llm_flows/_nl_planning.py +129 -0
- google/adk/flows/llm_flows/agent_transfer.py +132 -0
- google/adk/flows/llm_flows/audio_transcriber.py +109 -0
- google/adk/flows/llm_flows/auto_flow.py +49 -0
- google/adk/flows/llm_flows/base_llm_flow.py +559 -0
- google/adk/flows/llm_flows/basic.py +72 -0
- google/adk/flows/llm_flows/contents.py +370 -0
- google/adk/flows/llm_flows/functions.py +486 -0
- google/adk/flows/llm_flows/identity.py +47 -0
- google/adk/flows/llm_flows/instructions.py +137 -0
- google/adk/flows/llm_flows/single_flow.py +57 -0
- google/adk/memory/__init__.py +35 -0
- google/adk/memory/base_memory_service.py +74 -0
- google/adk/memory/in_memory_memory_service.py +62 -0
- google/adk/memory/vertex_ai_rag_memory_service.py +177 -0
- google/adk/models/__init__.py +31 -0
- google/adk/models/anthropic_llm.py +243 -0
- google/adk/models/base_llm.py +87 -0
- google/adk/models/base_llm_connection.py +76 -0
- google/adk/models/gemini_llm_connection.py +200 -0
- google/adk/models/google_llm.py +331 -0
- google/adk/models/lite_llm.py +673 -0
- google/adk/models/llm_request.py +98 -0
- google/adk/models/llm_response.py +111 -0
- google/adk/models/registry.py +102 -0
- google/adk/planners/__init__.py +23 -0
- google/adk/planners/base_planner.py +66 -0
- google/adk/planners/built_in_planner.py +75 -0
- google/adk/planners/plan_re_act_planner.py +208 -0
- google/adk/runners.py +456 -0
- google/adk/sessions/__init__.py +41 -0
- google/adk/sessions/base_session_service.py +133 -0
- google/adk/sessions/database_session_service.py +522 -0
- google/adk/sessions/in_memory_session_service.py +206 -0
- google/adk/sessions/session.py +54 -0
- google/adk/sessions/state.py +71 -0
- google/adk/sessions/vertex_ai_session_service.py +356 -0
- google/adk/telemetry.py +189 -0
- google/adk/tests/__init__.py +14 -0
- google/adk/tests/integration/.env.example +10 -0
- google/adk/tests/integration/__init__.py +18 -0
- google/adk/tests/integration/conftest.py +119 -0
- google/adk/tests/integration/fixture/__init__.py +14 -0
- google/adk/tests/integration/fixture/agent_with_config/__init__.py +15 -0
- google/adk/tests/integration/fixture/agent_with_config/agent.py +88 -0
- google/adk/tests/integration/fixture/callback_agent/__init__.py +15 -0
- google/adk/tests/integration/fixture/callback_agent/agent.py +105 -0
- google/adk/tests/integration/fixture/context_update_test/OWNERS +1 -0
- google/adk/tests/integration/fixture/context_update_test/__init__.py +15 -0
- google/adk/tests/integration/fixture/context_update_test/agent.py +43 -0
- google/adk/tests/integration/fixture/context_update_test/successful_test.session.json +582 -0
- google/adk/tests/integration/fixture/context_variable_agent/__init__.py +15 -0
- google/adk/tests/integration/fixture/context_variable_agent/agent.py +115 -0
- google/adk/tests/integration/fixture/customer_support_ma/__init__.py +15 -0
- google/adk/tests/integration/fixture/customer_support_ma/agent.py +172 -0
- google/adk/tests/integration/fixture/ecommerce_customer_service_agent/__init__.py +15 -0
- google/adk/tests/integration/fixture/ecommerce_customer_service_agent/agent.py +338 -0
- google/adk/tests/integration/fixture/ecommerce_customer_service_agent/order_query.test.json +69 -0
- google/adk/tests/integration/fixture/ecommerce_customer_service_agent/test_config.json +6 -0
- google/adk/tests/integration/fixture/flow_complex_spark/__init__.py +15 -0
- google/adk/tests/integration/fixture/flow_complex_spark/agent.py +182 -0
- google/adk/tests/integration/fixture/flow_complex_spark/sample.debug.log +243 -0
- google/adk/tests/integration/fixture/flow_complex_spark/sample.session.json +190 -0
- google/adk/tests/integration/fixture/hello_world_agent/__init__.py +15 -0
- google/adk/tests/integration/fixture/hello_world_agent/agent.py +95 -0
- google/adk/tests/integration/fixture/hello_world_agent/roll_die.test.json +24 -0
- google/adk/tests/integration/fixture/hello_world_agent/test_config.json +6 -0
- google/adk/tests/integration/fixture/home_automation_agent/__init__.py +15 -0
- google/adk/tests/integration/fixture/home_automation_agent/agent.py +304 -0
- google/adk/tests/integration/fixture/home_automation_agent/simple_test.test.json +5 -0
- google/adk/tests/integration/fixture/home_automation_agent/simple_test2.test.json +5 -0
- google/adk/tests/integration/fixture/home_automation_agent/test_config.json +5 -0
- google/adk/tests/integration/fixture/home_automation_agent/test_files/dependent_tool_calls.test.json +18 -0
- google/adk/tests/integration/fixture/home_automation_agent/test_files/memorizing_past_events/eval_data.test.json +17 -0
- google/adk/tests/integration/fixture/home_automation_agent/test_files/memorizing_past_events/test_config.json +6 -0
- google/adk/tests/integration/fixture/home_automation_agent/test_files/simple_multi_turn_conversation.test.json +18 -0
- google/adk/tests/integration/fixture/home_automation_agent/test_files/simple_test.test.json +17 -0
- google/adk/tests/integration/fixture/home_automation_agent/test_files/simple_test2.test.json +5 -0
- google/adk/tests/integration/fixture/home_automation_agent/test_files/test_config.json +5 -0
- google/adk/tests/integration/fixture/tool_agent/__init__.py +15 -0
- google/adk/tests/integration/fixture/tool_agent/agent.py +218 -0
- google/adk/tests/integration/fixture/tool_agent/files/Agent_test_plan.pdf +0 -0
- google/adk/tests/integration/fixture/trip_planner_agent/__init__.py +15 -0
- google/adk/tests/integration/fixture/trip_planner_agent/agent.py +110 -0
- google/adk/tests/integration/fixture/trip_planner_agent/initial.session.json +13 -0
- google/adk/tests/integration/fixture/trip_planner_agent/test_config.json +5 -0
- google/adk/tests/integration/fixture/trip_planner_agent/test_files/initial.session.json +13 -0
- google/adk/tests/integration/fixture/trip_planner_agent/test_files/test_config.json +5 -0
- google/adk/tests/integration/fixture/trip_planner_agent/test_files/trip_inquiry_sub_agent.test.json +7 -0
- google/adk/tests/integration/fixture/trip_planner_agent/trip_inquiry.test.json +19 -0
- google/adk/tests/integration/models/__init__.py +14 -0
- google/adk/tests/integration/models/test_google_llm.py +65 -0
- google/adk/tests/integration/test_callback.py +70 -0
- google/adk/tests/integration/test_context_variable.py +67 -0
- google/adk/tests/integration/test_evalute_agent_in_fixture.py +76 -0
- google/adk/tests/integration/test_multi_agent.py +28 -0
- google/adk/tests/integration/test_multi_turn.py +42 -0
- google/adk/tests/integration/test_single_agent.py +23 -0
- google/adk/tests/integration/test_sub_agent.py +26 -0
- google/adk/tests/integration/test_system_instruction.py +177 -0
- google/adk/tests/integration/test_tools.py +287 -0
- google/adk/tests/integration/test_with_test_file.py +34 -0
- google/adk/tests/integration/tools/__init__.py +14 -0
- google/adk/tests/integration/utils/__init__.py +16 -0
- google/adk/tests/integration/utils/asserts.py +75 -0
- google/adk/tests/integration/utils/test_runner.py +97 -0
- google/adk/tests/unittests/__init__.py +14 -0
- google/adk/tests/unittests/agents/__init__.py +14 -0
- google/adk/tests/unittests/agents/test_base_agent.py +407 -0
- google/adk/tests/unittests/agents/test_langgraph_agent.py +191 -0
- google/adk/tests/unittests/agents/test_llm_agent_callbacks.py +138 -0
- google/adk/tests/unittests/agents/test_llm_agent_fields.py +231 -0
- google/adk/tests/unittests/agents/test_loop_agent.py +136 -0
- google/adk/tests/unittests/agents/test_parallel_agent.py +92 -0
- google/adk/tests/unittests/agents/test_sequential_agent.py +114 -0
- google/adk/tests/unittests/artifacts/__init__.py +14 -0
- google/adk/tests/unittests/artifacts/test_artifact_service.py +276 -0
- google/adk/tests/unittests/auth/test_auth_handler.py +575 -0
- google/adk/tests/unittests/conftest.py +73 -0
- google/adk/tests/unittests/fast_api/__init__.py +14 -0
- google/adk/tests/unittests/fast_api/test_fast_api.py +269 -0
- google/adk/tests/unittests/flows/__init__.py +14 -0
- google/adk/tests/unittests/flows/llm_flows/__init__.py +14 -0
- google/adk/tests/unittests/flows/llm_flows/_test_examples.py +142 -0
- google/adk/tests/unittests/flows/llm_flows/test_agent_transfer.py +311 -0
- google/adk/tests/unittests/flows/llm_flows/test_functions_long_running.py +244 -0
- google/adk/tests/unittests/flows/llm_flows/test_functions_request_euc.py +346 -0
- google/adk/tests/unittests/flows/llm_flows/test_functions_sequential.py +93 -0
- google/adk/tests/unittests/flows/llm_flows/test_functions_simple.py +258 -0
- google/adk/tests/unittests/flows/llm_flows/test_identity.py +66 -0
- google/adk/tests/unittests/flows/llm_flows/test_instructions.py +164 -0
- google/adk/tests/unittests/flows/llm_flows/test_model_callbacks.py +142 -0
- google/adk/tests/unittests/flows/llm_flows/test_other_configs.py +46 -0
- google/adk/tests/unittests/flows/llm_flows/test_tool_callbacks.py +269 -0
- google/adk/tests/unittests/models/__init__.py +14 -0
- google/adk/tests/unittests/models/test_google_llm.py +224 -0
- google/adk/tests/unittests/models/test_litellm.py +804 -0
- google/adk/tests/unittests/models/test_models.py +60 -0
- google/adk/tests/unittests/sessions/__init__.py +14 -0
- google/adk/tests/unittests/sessions/test_session_service.py +227 -0
- google/adk/tests/unittests/sessions/test_vertex_ai_session_service.py +246 -0
- google/adk/tests/unittests/streaming/__init__.py +14 -0
- google/adk/tests/unittests/streaming/test_streaming.py +50 -0
- google/adk/tests/unittests/tools/__init__.py +14 -0
- google/adk/tests/unittests/tools/apihub_tool/clients/test_apihub_client.py +499 -0
- google/adk/tests/unittests/tools/apihub_tool/test_apihub_toolset.py +204 -0
- google/adk/tests/unittests/tools/application_integration_tool/clients/test_connections_client.py +600 -0
- google/adk/tests/unittests/tools/application_integration_tool/clients/test_integration_client.py +630 -0
- google/adk/tests/unittests/tools/application_integration_tool/test_application_integration_toolset.py +345 -0
- google/adk/tests/unittests/tools/google_api_tool/__init__.py +13 -0
- google/adk/tests/unittests/tools/google_api_tool/test_googleapi_to_openapi_converter.py +657 -0
- google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_auto_auth_credential_exchanger.py +145 -0
- google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_base_auth_credential_exchanger.py +68 -0
- google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_oauth2_exchanger.py +153 -0
- google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_service_account_exchanger.py +196 -0
- google/adk/tests/unittests/tools/openapi_tool/auth/test_auth_helper.py +573 -0
- google/adk/tests/unittests/tools/openapi_tool/common/test_common.py +436 -0
- google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test.yaml +1367 -0
- google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_openapi_spec_parser.py +628 -0
- google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_openapi_toolset.py +139 -0
- google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_operation_parser.py +406 -0
- google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_rest_api_tool.py +966 -0
- google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_tool_auth_handler.py +201 -0
- google/adk/tests/unittests/tools/retrieval/__init__.py +14 -0
- google/adk/tests/unittests/tools/retrieval/test_vertex_ai_rag_retrieval.py +147 -0
- google/adk/tests/unittests/tools/test_agent_tool.py +167 -0
- google/adk/tests/unittests/tools/test_base_tool.py +141 -0
- google/adk/tests/unittests/tools/test_build_function_declaration.py +277 -0
- google/adk/tests/unittests/utils.py +304 -0
- google/adk/tools/__init__.py +51 -0
- google/adk/tools/_automatic_function_calling_util.py +346 -0
- google/adk/tools/agent_tool.py +176 -0
- google/adk/tools/apihub_tool/__init__.py +19 -0
- google/adk/tools/apihub_tool/apihub_toolset.py +209 -0
- google/adk/tools/apihub_tool/clients/__init__.py +13 -0
- google/adk/tools/apihub_tool/clients/apihub_client.py +332 -0
- google/adk/tools/apihub_tool/clients/secret_client.py +115 -0
- google/adk/tools/application_integration_tool/__init__.py +19 -0
- google/adk/tools/application_integration_tool/application_integration_toolset.py +230 -0
- google/adk/tools/application_integration_tool/clients/connections_client.py +903 -0
- google/adk/tools/application_integration_tool/clients/integration_client.py +253 -0
- google/adk/tools/base_tool.py +144 -0
- google/adk/tools/built_in_code_execution_tool.py +59 -0
- google/adk/tools/crewai_tool.py +72 -0
- google/adk/tools/example_tool.py +62 -0
- google/adk/tools/exit_loop_tool.py +23 -0
- google/adk/tools/function_parameter_parse_util.py +307 -0
- google/adk/tools/function_tool.py +87 -0
- google/adk/tools/get_user_choice_tool.py +28 -0
- google/adk/tools/google_api_tool/__init__.py +14 -0
- google/adk/tools/google_api_tool/google_api_tool.py +59 -0
- google/adk/tools/google_api_tool/google_api_tool_set.py +107 -0
- google/adk/tools/google_api_tool/google_api_tool_sets.py +55 -0
- google/adk/tools/google_api_tool/googleapi_to_openapi_converter.py +521 -0
- google/adk/tools/google_search_tool.py +68 -0
- google/adk/tools/langchain_tool.py +86 -0
- google/adk/tools/load_artifacts_tool.py +113 -0
- google/adk/tools/load_memory_tool.py +58 -0
- google/adk/tools/load_web_page.py +41 -0
- google/adk/tools/long_running_tool.py +39 -0
- google/adk/tools/mcp_tool/__init__.py +42 -0
- google/adk/tools/mcp_tool/conversion_utils.py +161 -0
- google/adk/tools/mcp_tool/mcp_tool.py +113 -0
- google/adk/tools/mcp_tool/mcp_toolset.py +272 -0
- google/adk/tools/openapi_tool/__init__.py +21 -0
- google/adk/tools/openapi_tool/auth/__init__.py +19 -0
- google/adk/tools/openapi_tool/auth/auth_helpers.py +498 -0
- google/adk/tools/openapi_tool/auth/credential_exchangers/__init__.py +25 -0
- google/adk/tools/openapi_tool/auth/credential_exchangers/auto_auth_credential_exchanger.py +105 -0
- google/adk/tools/openapi_tool/auth/credential_exchangers/base_credential_exchanger.py +55 -0
- google/adk/tools/openapi_tool/auth/credential_exchangers/oauth2_exchanger.py +117 -0
- google/adk/tools/openapi_tool/auth/credential_exchangers/service_account_exchanger.py +97 -0
- google/adk/tools/openapi_tool/common/__init__.py +19 -0
- google/adk/tools/openapi_tool/common/common.py +300 -0
- google/adk/tools/openapi_tool/openapi_spec_parser/__init__.py +32 -0
- google/adk/tools/openapi_tool/openapi_spec_parser/openapi_spec_parser.py +231 -0
- google/adk/tools/openapi_tool/openapi_spec_parser/openapi_toolset.py +144 -0
- google/adk/tools/openapi_tool/openapi_spec_parser/operation_parser.py +260 -0
- google/adk/tools/openapi_tool/openapi_spec_parser/rest_api_tool.py +496 -0
- google/adk/tools/openapi_tool/openapi_spec_parser/tool_auth_handler.py +268 -0
- google/adk/tools/preload_memory_tool.py +72 -0
- google/adk/tools/retrieval/__init__.py +36 -0
- google/adk/tools/retrieval/base_retrieval_tool.py +37 -0
- google/adk/tools/retrieval/files_retrieval.py +33 -0
- google/adk/tools/retrieval/llama_index_retrieval.py +41 -0
- google/adk/tools/retrieval/vertex_ai_rag_retrieval.py +107 -0
- google/adk/tools/tool_context.py +90 -0
- google/adk/tools/toolbox_tool.py +46 -0
- google/adk/tools/transfer_to_agent_tool.py +21 -0
- google/adk/tools/vertex_ai_search_tool.py +96 -0
- google/adk/version.py +16 -0
- google_adk-0.0.1.dist-info/LICENSE.txt → google_adk-0.0.2.dist-info/LICENSE +32 -0
- google_adk-0.0.2.dist-info/METADATA +73 -0
- google_adk-0.0.2.dist-info/RECORD +308 -0
- {google_adk-0.0.1.dist-info → google_adk-0.0.2.dist-info}/WHEEL +1 -2
- google_adk-0.0.2.dist-info/entry_points.txt +3 -0
- agent_kit/__init__.py +0 -0
- google_adk-0.0.1.dist-info/METADATA +0 -15
- google_adk-0.0.1.dist-info/RECORD +0 -6
- google_adk-0.0.1.dist-info/top_level.txt +0 -1
@@ -0,0 +1,220 @@
|
|
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
|
+
from enum import Enum
|
16
|
+
from typing import Any
|
17
|
+
from typing import Dict
|
18
|
+
from typing import List
|
19
|
+
from typing import Optional
|
20
|
+
|
21
|
+
from pydantic import BaseModel
|
22
|
+
from pydantic import Field
|
23
|
+
|
24
|
+
|
25
|
+
class BaseModelWithConfig(BaseModel):
|
26
|
+
model_config = {"extra": "allow"}
|
27
|
+
|
28
|
+
|
29
|
+
class HttpCredentials(BaseModelWithConfig):
|
30
|
+
"""Represents the secret token value for HTTP authentication, like user name, password, oauth token, etc."""
|
31
|
+
|
32
|
+
username: Optional[str] = None
|
33
|
+
password: Optional[str] = None
|
34
|
+
token: Optional[str] = None
|
35
|
+
|
36
|
+
@classmethod
|
37
|
+
def model_validate(cls, data: Dict[str, Any]) -> "HttpCredentials":
|
38
|
+
return cls(
|
39
|
+
username=data.get("username"),
|
40
|
+
password=data.get("password"),
|
41
|
+
token=data.get("token"),
|
42
|
+
)
|
43
|
+
|
44
|
+
|
45
|
+
class HttpAuth(BaseModelWithConfig):
|
46
|
+
"""The credentials and metadata for HTTP authentication."""
|
47
|
+
|
48
|
+
# The name of the HTTP Authorization scheme to be used in the Authorization
|
49
|
+
# header as defined in RFC7235. The values used SHOULD be registered in the
|
50
|
+
# IANA Authentication Scheme registry.
|
51
|
+
# Examples: 'basic', 'bearer'
|
52
|
+
scheme: str
|
53
|
+
credentials: HttpCredentials
|
54
|
+
|
55
|
+
|
56
|
+
class OAuth2Auth(BaseModelWithConfig):
|
57
|
+
"""Represents credential value and its metadata for a OAuth2 credential."""
|
58
|
+
|
59
|
+
client_id: Optional[str] = None
|
60
|
+
client_secret: Optional[str] = None
|
61
|
+
# tool or adk can generate the auth_uri with the state info thus client
|
62
|
+
# can verify the state
|
63
|
+
auth_uri: Optional[str] = None
|
64
|
+
state: Optional[str] = None
|
65
|
+
# tool or adk can decide the redirect_uri if they don't want client to decide
|
66
|
+
redirect_uri: Optional[str] = None
|
67
|
+
auth_response_uri: Optional[str] = None
|
68
|
+
auth_code: Optional[str] = None
|
69
|
+
token: Optional[Dict[str, Any]] = None
|
70
|
+
|
71
|
+
|
72
|
+
class ServiceAccountCredential(BaseModelWithConfig):
|
73
|
+
"""Represents Google Service Account configuration.
|
74
|
+
|
75
|
+
Attributes:
|
76
|
+
type: The type should be "service_account".
|
77
|
+
project_id: The project ID.
|
78
|
+
private_key_id: The ID of the private key.
|
79
|
+
private_key: The private key.
|
80
|
+
client_email: The client email.
|
81
|
+
client_id: The client ID.
|
82
|
+
auth_uri: The authorization URI.
|
83
|
+
token_uri: The token URI.
|
84
|
+
auth_provider_x509_cert_url: URL for auth provider's X.509 cert.
|
85
|
+
client_x509_cert_url: URL for the client's X.509 cert.
|
86
|
+
universe_domain: The universe domain.
|
87
|
+
|
88
|
+
Example:
|
89
|
+
|
90
|
+
config = ServiceAccountCredential(
|
91
|
+
type_="service_account",
|
92
|
+
project_id="your_project_id",
|
93
|
+
private_key_id="your_private_key_id",
|
94
|
+
private_key="-----BEGIN PRIVATE KEY-----...",
|
95
|
+
client_email="...@....iam.gserviceaccount.com",
|
96
|
+
client_id="your_client_id",
|
97
|
+
auth_uri="https://accounts.google.com/o/oauth2/auth",
|
98
|
+
token_uri="https://oauth2.googleapis.com/token",
|
99
|
+
auth_provider_x509_cert_url="https://www.googleapis.com/oauth2/v1/certs",
|
100
|
+
client_x509_cert_url="https://www.googleapis.com/robot/v1/metadata/x509/...",
|
101
|
+
universe_domain="googleapis.com"
|
102
|
+
)
|
103
|
+
|
104
|
+
|
105
|
+
config = ServiceAccountConfig.model_construct(**{
|
106
|
+
...service account config dict
|
107
|
+
})
|
108
|
+
"""
|
109
|
+
|
110
|
+
type_: str = Field("", alias="type")
|
111
|
+
project_id: str
|
112
|
+
private_key_id: str
|
113
|
+
private_key: str
|
114
|
+
client_email: str
|
115
|
+
client_id: str
|
116
|
+
auth_uri: str
|
117
|
+
token_uri: str
|
118
|
+
auth_provider_x509_cert_url: str
|
119
|
+
client_x509_cert_url: str
|
120
|
+
universe_domain: str
|
121
|
+
|
122
|
+
|
123
|
+
class ServiceAccount(BaseModelWithConfig):
|
124
|
+
"""Represents Google Service Account configuration."""
|
125
|
+
|
126
|
+
service_account_credential: Optional[ServiceAccountCredential] = None
|
127
|
+
scopes: List[str]
|
128
|
+
use_default_credential: Optional[bool] = False
|
129
|
+
|
130
|
+
|
131
|
+
class AuthCredentialTypes(str, Enum):
|
132
|
+
"""Represents the type of authentication credential."""
|
133
|
+
|
134
|
+
# API Key credential:
|
135
|
+
# https://swagger.io/docs/specification/v3_0/authentication/api-keys/
|
136
|
+
API_KEY = "apiKey"
|
137
|
+
|
138
|
+
# Credentials for HTTP Auth schemes:
|
139
|
+
# https://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml
|
140
|
+
HTTP = "http"
|
141
|
+
|
142
|
+
# OAuth2 credentials:
|
143
|
+
# https://swagger.io/docs/specification/v3_0/authentication/oauth2/
|
144
|
+
OAUTH2 = "oauth2"
|
145
|
+
|
146
|
+
# OpenID Connect credentials:
|
147
|
+
# https://swagger.io/docs/specification/v3_0/authentication/openid-connect-discovery/
|
148
|
+
OPEN_ID_CONNECT = "openIdConnect"
|
149
|
+
|
150
|
+
# Service Account credentials:
|
151
|
+
# https://cloud.google.com/iam/docs/service-account-creds
|
152
|
+
SERVICE_ACCOUNT = "serviceAccount"
|
153
|
+
|
154
|
+
|
155
|
+
class AuthCredential(BaseModelWithConfig):
|
156
|
+
"""Data class representing an authentication credential.
|
157
|
+
|
158
|
+
To exchange for the actual credential, please use
|
159
|
+
CredentialExchanger.exchange_credential().
|
160
|
+
|
161
|
+
Examples: API Key Auth
|
162
|
+
AuthCredential(
|
163
|
+
auth_type=AuthCredentialTypes.API_KEY,
|
164
|
+
api_key="1234",
|
165
|
+
)
|
166
|
+
|
167
|
+
Example: HTTP Auth
|
168
|
+
AuthCredential(
|
169
|
+
auth_type=AuthCredentialTypes.HTTP,
|
170
|
+
http=HttpAuth(
|
171
|
+
scheme="basic",
|
172
|
+
credentials=HttpCredentials(username="user", password="password"),
|
173
|
+
),
|
174
|
+
)
|
175
|
+
|
176
|
+
Example: OAuth2 Bearer Token in HTTP Header
|
177
|
+
AuthCredential(
|
178
|
+
auth_type=AuthCredentialTypes.HTTP,
|
179
|
+
http=HttpAuth(
|
180
|
+
scheme="bearer",
|
181
|
+
credentials=HttpCredentials(token="eyAkaknabna...."),
|
182
|
+
),
|
183
|
+
)
|
184
|
+
|
185
|
+
Example: OAuth2 Auth with Authorization Code Flow
|
186
|
+
AuthCredential(
|
187
|
+
auth_type=AuthCredentialTypes.OAUTH2,
|
188
|
+
oauth2=OAuth2Auth(
|
189
|
+
client_id="1234",
|
190
|
+
client_secret="secret",
|
191
|
+
),
|
192
|
+
)
|
193
|
+
|
194
|
+
Example: OpenID Connect Auth
|
195
|
+
AuthCredential(
|
196
|
+
auth_type=AuthCredentialTypes.OPEN_ID_CONNECT,
|
197
|
+
oauth2=OAuth2Auth(
|
198
|
+
client_id="1234",
|
199
|
+
client_secret="secret",
|
200
|
+
redirect_uri="https://example.com",
|
201
|
+
scopes=["scope1", "scope2"],
|
202
|
+
),
|
203
|
+
)
|
204
|
+
|
205
|
+
Example: Auth with resource reference
|
206
|
+
AuthCredential(
|
207
|
+
auth_type=AuthCredentialTypes.API_KEY,
|
208
|
+
resource_ref="projects/1234/locations/us-central1/resources/resource1",
|
209
|
+
)
|
210
|
+
"""
|
211
|
+
|
212
|
+
auth_type: AuthCredentialTypes
|
213
|
+
# Resource reference for the credential.
|
214
|
+
# This will be supported in the future.
|
215
|
+
resource_ref: Optional[str] = None
|
216
|
+
|
217
|
+
api_key: Optional[str] = None
|
218
|
+
http: Optional[HttpAuth] = None
|
219
|
+
service_account: Optional[ServiceAccount] = None
|
220
|
+
oauth2: Optional[OAuth2Auth] = None
|
@@ -0,0 +1,268 @@
|
|
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
|
+
from __future__ import annotations
|
16
|
+
|
17
|
+
from typing import TYPE_CHECKING
|
18
|
+
|
19
|
+
from fastapi.openapi.models import OAuth2
|
20
|
+
from fastapi.openapi.models import SecurityBase
|
21
|
+
|
22
|
+
from .auth_credential import AuthCredential
|
23
|
+
from .auth_credential import AuthCredentialTypes
|
24
|
+
from .auth_credential import OAuth2Auth
|
25
|
+
from .auth_schemes import AuthSchemeType
|
26
|
+
from .auth_schemes import OAuthGrantType
|
27
|
+
from .auth_schemes import OpenIdConnectWithConfig
|
28
|
+
from .auth_tool import AuthConfig
|
29
|
+
|
30
|
+
if TYPE_CHECKING:
|
31
|
+
from ..sessions.state import State
|
32
|
+
|
33
|
+
try:
|
34
|
+
from authlib.integrations.requests_client import OAuth2Session
|
35
|
+
|
36
|
+
SUPPORT_TOKEN_EXCHANGE = True
|
37
|
+
except ImportError:
|
38
|
+
SUPPORT_TOKEN_EXCHANGE = False
|
39
|
+
|
40
|
+
|
41
|
+
class AuthHandler:
|
42
|
+
|
43
|
+
def __init__(self, auth_config: AuthConfig):
|
44
|
+
self.auth_config = auth_config
|
45
|
+
|
46
|
+
def exchange_auth_token(
|
47
|
+
self,
|
48
|
+
) -> AuthCredential:
|
49
|
+
"""Generates an auth token from the authorization response.
|
50
|
+
|
51
|
+
Returns:
|
52
|
+
An AuthCredential object containing the access token.
|
53
|
+
|
54
|
+
Raises:
|
55
|
+
ValueError: If the token endpoint is not configured in the auth
|
56
|
+
scheme.
|
57
|
+
AuthCredentialMissingError: If the access token cannot be retrieved
|
58
|
+
from the token endpoint.
|
59
|
+
"""
|
60
|
+
auth_scheme = self.auth_config.auth_scheme
|
61
|
+
auth_credential = self.auth_config.exchanged_auth_credential
|
62
|
+
if not SUPPORT_TOKEN_EXCHANGE:
|
63
|
+
return auth_credential
|
64
|
+
if isinstance(auth_scheme, OpenIdConnectWithConfig):
|
65
|
+
if not hasattr(auth_scheme, "token_endpoint"):
|
66
|
+
return self.auth_config.exchanged_auth_credential
|
67
|
+
token_endpoint = auth_scheme.token_endpoint
|
68
|
+
scopes = auth_scheme.scopes
|
69
|
+
elif isinstance(auth_scheme, OAuth2):
|
70
|
+
if (
|
71
|
+
not auth_scheme.flows.authorizationCode
|
72
|
+
or not auth_scheme.flows.authorizationCode.tokenUrl
|
73
|
+
):
|
74
|
+
return self.auth_config.exchanged_auth_credential
|
75
|
+
token_endpoint = auth_scheme.flows.authorizationCode.tokenUrl
|
76
|
+
scopes = list(auth_scheme.flows.authorizationCode.scopes.keys())
|
77
|
+
else:
|
78
|
+
return self.auth_config.exchanged_auth_credential
|
79
|
+
|
80
|
+
if (
|
81
|
+
not auth_credential
|
82
|
+
or not auth_credential.oauth2
|
83
|
+
or not auth_credential.oauth2.client_id
|
84
|
+
or not auth_credential.oauth2.client_secret
|
85
|
+
or auth_credential.oauth2.token
|
86
|
+
):
|
87
|
+
return self.auth_config.exchanged_auth_credential
|
88
|
+
|
89
|
+
client = OAuth2Session(
|
90
|
+
auth_credential.oauth2.client_id,
|
91
|
+
auth_credential.oauth2.client_secret,
|
92
|
+
scope=" ".join(scopes),
|
93
|
+
redirect_uri=auth_credential.oauth2.redirect_uri,
|
94
|
+
state=auth_credential.oauth2.state,
|
95
|
+
)
|
96
|
+
token = client.fetch_token(
|
97
|
+
token_endpoint,
|
98
|
+
authorization_response=auth_credential.oauth2.auth_response_uri,
|
99
|
+
code=auth_credential.oauth2.auth_code,
|
100
|
+
grant_type=OAuthGrantType.AUTHORIZATION_CODE,
|
101
|
+
)
|
102
|
+
|
103
|
+
updated_credential = AuthCredential(
|
104
|
+
auth_type=AuthCredentialTypes.OAUTH2,
|
105
|
+
oauth2=OAuth2Auth(token=dict(token)),
|
106
|
+
)
|
107
|
+
return updated_credential
|
108
|
+
|
109
|
+
def parse_and_store_auth_response(self, state: State) -> None:
|
110
|
+
|
111
|
+
credential_key = self.get_credential_key()
|
112
|
+
|
113
|
+
state[credential_key] = self.auth_config.exchanged_auth_credential
|
114
|
+
if not isinstance(
|
115
|
+
self.auth_config.auth_scheme, SecurityBase
|
116
|
+
) or self.auth_config.auth_scheme.type_ not in (
|
117
|
+
AuthSchemeType.oauth2,
|
118
|
+
AuthSchemeType.openIdConnect,
|
119
|
+
):
|
120
|
+
return
|
121
|
+
|
122
|
+
state[credential_key] = self.exchange_auth_token()
|
123
|
+
|
124
|
+
def _validate(self) -> None:
|
125
|
+
if not self.auth_scheme:
|
126
|
+
raise ValueError("auth_scheme is empty.")
|
127
|
+
|
128
|
+
def get_auth_response(self, state: State) -> AuthCredential:
|
129
|
+
credential_key = self.get_credential_key()
|
130
|
+
return state.get(credential_key, None)
|
131
|
+
|
132
|
+
def generate_auth_request(self) -> AuthConfig:
|
133
|
+
if not isinstance(
|
134
|
+
self.auth_config.auth_scheme, SecurityBase
|
135
|
+
) or self.auth_config.auth_scheme.type_ not in (
|
136
|
+
AuthSchemeType.oauth2,
|
137
|
+
AuthSchemeType.openIdConnect,
|
138
|
+
):
|
139
|
+
return self.auth_config.model_copy(deep=True)
|
140
|
+
|
141
|
+
# auth_uri already in exchanged credential
|
142
|
+
if (
|
143
|
+
self.auth_config.exchanged_auth_credential
|
144
|
+
and self.auth_config.exchanged_auth_credential.oauth2
|
145
|
+
and self.auth_config.exchanged_auth_credential.oauth2.auth_uri
|
146
|
+
):
|
147
|
+
return self.auth_config.model_copy(deep=True)
|
148
|
+
|
149
|
+
# Check if raw_auth_credential exists
|
150
|
+
if not self.auth_config.raw_auth_credential:
|
151
|
+
raise ValueError(
|
152
|
+
f"Auth Scheme {self.auth_config.auth_scheme.type_} requires"
|
153
|
+
" auth_credential."
|
154
|
+
)
|
155
|
+
|
156
|
+
# Check if oauth2 exists in raw_auth_credential
|
157
|
+
if not self.auth_config.raw_auth_credential.oauth2:
|
158
|
+
raise ValueError(
|
159
|
+
f"Auth Scheme {self.auth_config.auth_scheme.type_} requires oauth2 in"
|
160
|
+
" auth_credential."
|
161
|
+
)
|
162
|
+
|
163
|
+
# auth_uri in raw credential
|
164
|
+
if self.auth_config.raw_auth_credential.oauth2.auth_uri:
|
165
|
+
return AuthConfig(
|
166
|
+
auth_scheme=self.auth_config.auth_scheme,
|
167
|
+
raw_auth_credential=self.auth_config.raw_auth_credential,
|
168
|
+
exchanged_auth_credential=self.auth_config.raw_auth_credential.model_copy(
|
169
|
+
deep=True
|
170
|
+
),
|
171
|
+
)
|
172
|
+
|
173
|
+
# Check for client_id and client_secret
|
174
|
+
if (
|
175
|
+
not self.auth_config.raw_auth_credential.oauth2.client_id
|
176
|
+
or not self.auth_config.raw_auth_credential.oauth2.client_secret
|
177
|
+
):
|
178
|
+
raise ValueError(
|
179
|
+
f"Auth Scheme {self.auth_config.auth_scheme.type_} requires both"
|
180
|
+
" client_id and client_secret in auth_credential.oauth2."
|
181
|
+
)
|
182
|
+
|
183
|
+
# Generate new auth URI
|
184
|
+
exchanged_credential = self.generate_auth_uri()
|
185
|
+
return AuthConfig(
|
186
|
+
auth_scheme=self.auth_config.auth_scheme,
|
187
|
+
raw_auth_credential=self.auth_config.raw_auth_credential,
|
188
|
+
exchanged_auth_credential=exchanged_credential,
|
189
|
+
)
|
190
|
+
|
191
|
+
def get_credential_key(self) -> str:
|
192
|
+
"""Generates a unique key for the given auth scheme and credential."""
|
193
|
+
auth_scheme = self.auth_config.auth_scheme
|
194
|
+
auth_credential = self.auth_config.raw_auth_credential
|
195
|
+
if auth_scheme.model_extra:
|
196
|
+
auth_scheme = auth_scheme.model_copy(deep=True)
|
197
|
+
auth_scheme.model_extra.clear()
|
198
|
+
scheme_name = (
|
199
|
+
f"{auth_scheme.type_.name}_{hash(auth_scheme.model_dump_json())}"
|
200
|
+
if auth_scheme
|
201
|
+
else ""
|
202
|
+
)
|
203
|
+
if auth_credential.model_extra:
|
204
|
+
auth_credential = auth_credential.model_copy(deep=True)
|
205
|
+
auth_credential.model_extra.clear()
|
206
|
+
credential_name = (
|
207
|
+
f"{auth_credential.auth_type.value}_{hash(auth_credential.model_dump_json())}"
|
208
|
+
if auth_credential
|
209
|
+
else ""
|
210
|
+
)
|
211
|
+
|
212
|
+
return f"temp:adk_{scheme_name}_{credential_name}"
|
213
|
+
|
214
|
+
def generate_auth_uri(
|
215
|
+
self,
|
216
|
+
) -> AuthCredential:
|
217
|
+
"""Generates an response containing the auth uri for user to sign in.
|
218
|
+
|
219
|
+
Returns:
|
220
|
+
An AuthCredential object containing the auth URI and state.
|
221
|
+
|
222
|
+
Raises:
|
223
|
+
ValueError: If the authorization endpoint is not configured in the auth
|
224
|
+
scheme.
|
225
|
+
"""
|
226
|
+
auth_scheme = self.auth_config.auth_scheme
|
227
|
+
auth_credential = self.auth_config.raw_auth_credential
|
228
|
+
|
229
|
+
if isinstance(auth_scheme, OpenIdConnectWithConfig):
|
230
|
+
authorization_endpoint = auth_scheme.authorization_endpoint
|
231
|
+
scopes = auth_scheme.scopes
|
232
|
+
else:
|
233
|
+
authorization_endpoint = (
|
234
|
+
auth_scheme.flows.implicit
|
235
|
+
and auth_scheme.flows.implicit.authorizationUrl
|
236
|
+
or auth_scheme.flows.authorizationCode
|
237
|
+
and auth_scheme.flows.authorizationCode.authorizationUrl
|
238
|
+
or auth_scheme.flows.clientCredentials
|
239
|
+
and auth_scheme.flows.clientCredentials.tokenUrl
|
240
|
+
or auth_scheme.flows.password
|
241
|
+
and auth_scheme.flows.password.tokenUrl
|
242
|
+
)
|
243
|
+
scopes = (
|
244
|
+
auth_scheme.flows.implicit
|
245
|
+
and auth_scheme.flows.implicit.scopes
|
246
|
+
or auth_scheme.flows.authorizationCode
|
247
|
+
and auth_scheme.flows.authorizationCode.scopes
|
248
|
+
or auth_scheme.flows.clientCredentials
|
249
|
+
and auth_scheme.flows.clientCredentials.scopes
|
250
|
+
or auth_scheme.flows.password
|
251
|
+
and auth_scheme.flows.password.scopes
|
252
|
+
)
|
253
|
+
scopes = list(scopes.keys())
|
254
|
+
|
255
|
+
client = OAuth2Session(
|
256
|
+
auth_credential.oauth2.client_id,
|
257
|
+
auth_credential.oauth2.client_secret,
|
258
|
+
scope=" ".join(scopes),
|
259
|
+
redirect_uri=auth_credential.oauth2.redirect_uri,
|
260
|
+
)
|
261
|
+
uri, state = client.create_authorization_url(
|
262
|
+
url=authorization_endpoint, access_type="offline", prompt="consent"
|
263
|
+
)
|
264
|
+
exchanged_auth_credential = auth_credential.model_copy(deep=True)
|
265
|
+
exchanged_auth_credential.oauth2.auth_uri = uri
|
266
|
+
exchanged_auth_credential.oauth2.state = state
|
267
|
+
|
268
|
+
return exchanged_auth_credential
|
@@ -0,0 +1,116 @@
|
|
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
|
+
from __future__ import annotations
|
16
|
+
|
17
|
+
from typing import AsyncGenerator
|
18
|
+
from typing import TYPE_CHECKING
|
19
|
+
|
20
|
+
from typing_extensions import override
|
21
|
+
|
22
|
+
from ..agents.invocation_context import InvocationContext
|
23
|
+
from ..events.event import Event
|
24
|
+
from ..flows.llm_flows import functions
|
25
|
+
from ..flows.llm_flows._base_llm_processor import BaseLlmRequestProcessor
|
26
|
+
from ..flows.llm_flows.functions import REQUEST_EUC_FUNCTION_CALL_NAME
|
27
|
+
from ..models.llm_request import LlmRequest
|
28
|
+
from .auth_handler import AuthHandler
|
29
|
+
from .auth_tool import AuthConfig
|
30
|
+
from .auth_tool import AuthToolArguments
|
31
|
+
|
32
|
+
if TYPE_CHECKING:
|
33
|
+
from ..agents.llm_agent import LlmAgent
|
34
|
+
|
35
|
+
|
36
|
+
class _AuthLlmRequestProcessor(BaseLlmRequestProcessor):
|
37
|
+
"""Handles auth information to build the LLM request."""
|
38
|
+
|
39
|
+
@override
|
40
|
+
async def run_async(
|
41
|
+
self, invocation_context: InvocationContext, llm_request: LlmRequest
|
42
|
+
) -> AsyncGenerator[Event, None]:
|
43
|
+
from ..agents.llm_agent import LlmAgent
|
44
|
+
|
45
|
+
agent = invocation_context.agent
|
46
|
+
if not isinstance(agent, LlmAgent):
|
47
|
+
return
|
48
|
+
events = invocation_context.session.events
|
49
|
+
if not events:
|
50
|
+
return
|
51
|
+
request_euc_function_call_response_event = events[-1]
|
52
|
+
responses = (
|
53
|
+
request_euc_function_call_response_event.get_function_responses()
|
54
|
+
)
|
55
|
+
if not responses:
|
56
|
+
return
|
57
|
+
|
58
|
+
request_euc_function_call_ids = set()
|
59
|
+
|
60
|
+
for function_call_response in responses:
|
61
|
+
if function_call_response.name != REQUEST_EUC_FUNCTION_CALL_NAME:
|
62
|
+
continue
|
63
|
+
|
64
|
+
# found the function call response for the system long running request euc
|
65
|
+
# function call
|
66
|
+
request_euc_function_call_ids.add(function_call_response.id)
|
67
|
+
auth_config = AuthConfig.model_validate(function_call_response.response)
|
68
|
+
AuthHandler(auth_config=auth_config).parse_and_store_auth_response(
|
69
|
+
state=invocation_context.session.state
|
70
|
+
)
|
71
|
+
|
72
|
+
if not request_euc_function_call_ids:
|
73
|
+
return
|
74
|
+
|
75
|
+
for i in range(len(events) - 2, -1, -1):
|
76
|
+
event = events[i]
|
77
|
+
# looking for the system long running reqeust euc function call
|
78
|
+
function_calls = event.get_function_calls()
|
79
|
+
if not function_calls:
|
80
|
+
continue
|
81
|
+
|
82
|
+
tools_to_resume = set()
|
83
|
+
|
84
|
+
for function_call in function_calls:
|
85
|
+
if function_call.id not in request_euc_function_call_ids:
|
86
|
+
continue
|
87
|
+
args = AuthToolArguments.model_validate(function_call.args)
|
88
|
+
|
89
|
+
tools_to_resume.add(args.function_call_id)
|
90
|
+
if not tools_to_resume:
|
91
|
+
continue
|
92
|
+
# found the the system long running reqeust euc function call
|
93
|
+
# looking for original function call that requests euc
|
94
|
+
for j in range(i - 1, -1, -1):
|
95
|
+
event = events[j]
|
96
|
+
function_calls = event.get_function_calls()
|
97
|
+
if not function_calls:
|
98
|
+
continue
|
99
|
+
for function_call in function_calls:
|
100
|
+
function_response_event = None
|
101
|
+
if function_call.id in tools_to_resume:
|
102
|
+
function_response_event = await functions.handle_function_calls_async(
|
103
|
+
invocation_context,
|
104
|
+
event,
|
105
|
+
{tool.name: tool for tool in agent.canonical_tools},
|
106
|
+
# there could be parallel function calls that require auth
|
107
|
+
# auth response would be a dict keyed by function call id
|
108
|
+
tools_to_resume,
|
109
|
+
)
|
110
|
+
if function_response_event:
|
111
|
+
yield function_response_event
|
112
|
+
return
|
113
|
+
return
|
114
|
+
|
115
|
+
|
116
|
+
request_processor = _AuthLlmRequestProcessor()
|
@@ -0,0 +1,67 @@
|
|
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
|
+
from enum import Enum
|
16
|
+
from typing import List
|
17
|
+
from typing import Optional
|
18
|
+
from typing import Union
|
19
|
+
|
20
|
+
from fastapi.openapi.models import OAuthFlows
|
21
|
+
from fastapi.openapi.models import SecurityBase
|
22
|
+
from fastapi.openapi.models import SecurityScheme
|
23
|
+
from fastapi.openapi.models import SecuritySchemeType
|
24
|
+
from pydantic import Field
|
25
|
+
|
26
|
+
|
27
|
+
class OpenIdConnectWithConfig(SecurityBase):
|
28
|
+
type_: SecuritySchemeType = Field(
|
29
|
+
default=SecuritySchemeType.openIdConnect, alias="type"
|
30
|
+
)
|
31
|
+
authorization_endpoint: str
|
32
|
+
token_endpoint: str
|
33
|
+
userinfo_endpoint: Optional[str] = None
|
34
|
+
revocation_endpoint: Optional[str] = None
|
35
|
+
token_endpoint_auth_methods_supported: Optional[List[str]] = None
|
36
|
+
grant_types_supported: Optional[List[str]] = None
|
37
|
+
scopes: Optional[List[str]] = None
|
38
|
+
|
39
|
+
|
40
|
+
# AuthSchemes contains SecuritySchemes from OpenAPI 3.0 and an extra flattened OpenIdConnectWithConfig.
|
41
|
+
AuthScheme = Union[SecurityScheme, OpenIdConnectWithConfig]
|
42
|
+
|
43
|
+
|
44
|
+
class OAuthGrantType(str, Enum):
|
45
|
+
"""Represents the OAuth2 flow (or grant type)."""
|
46
|
+
|
47
|
+
CLIENT_CREDENTIALS = "client_credentials"
|
48
|
+
AUTHORIZATION_CODE = "authorization_code"
|
49
|
+
IMPLICIT = "implicit"
|
50
|
+
PASSWORD = "password"
|
51
|
+
|
52
|
+
@staticmethod
|
53
|
+
def from_flow(flow: OAuthFlows) -> "OAuthGrantType":
|
54
|
+
"""Converts an OAuthFlows object to a OAuthGrantType."""
|
55
|
+
if flow.clientCredentials:
|
56
|
+
return OAuthGrantType.CLIENT_CREDENTIALS
|
57
|
+
if flow.authorizationCode:
|
58
|
+
return OAuthGrantType.AUTHORIZATION_CODE
|
59
|
+
if flow.implicit:
|
60
|
+
return OAuthGrantType.IMPLICIT
|
61
|
+
if flow.password:
|
62
|
+
return OAuthGrantType.PASSWORD
|
63
|
+
return None
|
64
|
+
|
65
|
+
|
66
|
+
# AuthSchemeType re-exports SecuritySchemeType from OpenAPI 3.0.
|
67
|
+
AuthSchemeType = SecuritySchemeType
|