agent-starter-pack 0.3.3__py3-none-any.whl → 0.21.0__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.
- agent_starter_pack/agents/README.md +7 -0
- agents/langgraph_base_react/template/.templateconfig.yaml → agent_starter_pack/agents/adk_a2a_base/.template/templateconfig.yaml +5 -10
- agent_starter_pack/agents/adk_a2a_base/README.md +37 -0
- src/deployment_targets/cloud_run/Dockerfile → agent_starter_pack/agents/adk_a2a_base/app/__init__.py +2 -14
- agent_starter_pack/agents/adk_a2a_base/app/agent.py +70 -0
- agent_starter_pack/agents/adk_a2a_base/notebooks/adk_a2a_app_testing.ipynb +583 -0
- agents/crewai_coding_crew/notebooks/evaluating_crewai_agent.ipynb → agent_starter_pack/agents/adk_a2a_base/notebooks/evaluating_adk_agent.ipynb +163 -199
- {agents/adk_base → agent_starter_pack/agents/adk_a2a_base}/tests/integration/test_agent.py +2 -2
- agents/adk_base/template/.templateconfig.yaml → agent_starter_pack/agents/adk_base/.template/templateconfig.yaml +4 -1
- {agents → agent_starter_pack/agents}/adk_base/README.md +1 -1
- agent_starter_pack/agents/adk_base/app/__init__.py +17 -0
- {agents → agent_starter_pack/agents}/adk_base/app/agent.py +5 -2
- {agents → agent_starter_pack/agents}/adk_base/notebooks/adk_app_testing.ipynb +128 -82
- agents/langgraph_base_react/notebooks/evaluating_langgraph_agent.ipynb → agent_starter_pack/agents/adk_base/notebooks/evaluating_adk_agent.ipynb +181 -207
- agent_starter_pack/agents/adk_base/tests/integration/test_agent.py +58 -0
- agents/crewai_coding_crew/template/.templateconfig.yaml → agent_starter_pack/agents/adk_live/.template/templateconfig.yaml +5 -9
- agent_starter_pack/agents/adk_live/README.md +32 -0
- agent_starter_pack/agents/adk_live/app/__init__.py +17 -0
- agent_starter_pack/agents/adk_live/app/agent.py +51 -0
- agent_starter_pack/agents/adk_live/tests/unit/test_dummy.py +38 -0
- agents/agentic_rag/template/.templateconfig.yaml → agent_starter_pack/agents/agentic_rag/.template/templateconfig.yaml +7 -3
- {agents → agent_starter_pack/agents}/agentic_rag/README.md +1 -1
- agent_starter_pack/agents/agentic_rag/app/__init__.py +17 -0
- {agents → agent_starter_pack/agents}/agentic_rag/app/agent.py +8 -4
- {agents → agent_starter_pack/agents}/agentic_rag/notebooks/adk_app_testing.ipynb +128 -82
- agent_starter_pack/agents/agentic_rag/notebooks/evaluating_adk_agent.ipynb +1535 -0
- {agents → agent_starter_pack/agents}/agentic_rag/tests/integration/test_agent.py +3 -3
- agent_starter_pack/agents/langgraph_base/.template/templateconfig.yaml +31 -0
- agent_starter_pack/agents/langgraph_base/README.md +30 -0
- agent_starter_pack/agents/langgraph_base/app/__init__.py +17 -0
- agent_starter_pack/agents/langgraph_base/app/agent.py +34 -0
- {agents/crewai_coding_crew → agent_starter_pack/agents/langgraph_base}/notebooks/evaluating_langgraph_agent.ipynb +30 -17
- {agents/langgraph_base_react → agent_starter_pack/agents/langgraph_base}/tests/integration/test_agent.py +2 -2
- {src → agent_starter_pack}/base_template/.gitignore +5 -3
- agent_starter_pack/base_template/GEMINI.md +5 -0
- agent_starter_pack/base_template/Makefile +339 -0
- agent_starter_pack/base_template/README.md +267 -0
- agent_starter_pack/base_template/deployment/README.md +11 -0
- {src → agent_starter_pack}/base_template/deployment/terraform/apis.tf +2 -2
- {src → agent_starter_pack}/base_template/deployment/terraform/dev/apis.tf +6 -1
- {src → agent_starter_pack}/base_template/deployment/terraform/dev/iam.tf +12 -11
- {src → agent_starter_pack}/base_template/deployment/terraform/dev/providers.tf +5 -1
- {src → agent_starter_pack}/base_template/deployment/terraform/dev/storage.tf +1 -1
- {src → agent_starter_pack}/base_template/deployment/terraform/dev/variables.tf +10 -10
- agent_starter_pack/base_template/deployment/terraform/dev/{% if cookiecutter.is_adk %}telemetry.tf{% else %}unused_telemetry.tf{% endif %} +193 -0
- agent_starter_pack/base_template/deployment/terraform/github.tf +337 -0
- {src → agent_starter_pack}/base_template/deployment/terraform/iam.tf +20 -41
- {src → agent_starter_pack}/base_template/deployment/terraform/locals.tf +10 -3
- {src/resources/setup_cicd → agent_starter_pack/base_template/deployment/terraform}/providers.tf +8 -1
- {src → agent_starter_pack}/base_template/deployment/terraform/service_accounts.tf +7 -8
- agent_starter_pack/base_template/deployment/terraform/sql/completions.sql +138 -0
- {src → agent_starter_pack}/base_template/deployment/terraform/storage.tf +7 -16
- {src → agent_starter_pack}/base_template/deployment/terraform/variables.tf +61 -28
- {src → agent_starter_pack}/base_template/deployment/terraform/vars/env.tfvars +6 -1
- agent_starter_pack/base_template/deployment/terraform/{% if cookiecutter.cicd_runner == 'github_actions' %}wif.tf{% else %}unused_wif.tf{% endif %} +43 -0
- src/base_template/deployment/terraform/build_triggers.tf → agent_starter_pack/base_template/deployment/terraform/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}build_triggers.tf{% else %}unused_build_triggers.tf{% endif %} +55 -38
- agent_starter_pack/base_template/deployment/terraform/{% if cookiecutter.is_adk %}telemetry.tf{% else %}unused_telemetry.tf{% endif %} +206 -0
- {src → agent_starter_pack}/base_template/pyproject.toml +24 -37
- agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/deploy-to-prod.yaml +132 -0
- agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/pr_checks.yaml +65 -0
- agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/staging.yaml +259 -0
- src/base_template/deployment/cd/deploy-to-prod.yaml → agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/deploy-to-prod.yaml +38 -30
- src/base_template/deployment/ci/pr_checks.yaml → agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/pr_checks.yaml +5 -5
- agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/staging.yaml +322 -0
- agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils/telemetry.py +96 -0
- {src/base_template/app/utils → agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils}/typing.py +7 -9
- agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils/{% if cookiecutter.is_a2a and cookiecutter.agent_name == 'langgraph_base' %}converters{% else %}unused_converters{% endif %}/__init__.py +25 -0
- agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils/{% if cookiecutter.is_a2a and cookiecutter.agent_name == 'langgraph_base' %}converters{% else %}unused_converters{% endif %}/part_converter.py +138 -0
- agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils/{% if cookiecutter.is_a2a and cookiecutter.agent_name == 'langgraph_base' %}executor{% else %}unused_executor{% endif %}/__init__.py +13 -0
- agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils/{% if cookiecutter.is_a2a and cookiecutter.agent_name == 'langgraph_base' %}executor{% else %}unused_executor{% endif %}/a2a_agent_executor.py +265 -0
- agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils/{% if cookiecutter.is_a2a and cookiecutter.agent_name == 'langgraph_base' %}executor{% else %}unused_executor{% endif %}/task_result_aggregator.py +152 -0
- agent_starter_pack/cli/commands/create.py +1256 -0
- agent_starter_pack/cli/commands/enhance.py +611 -0
- agent_starter_pack/cli/commands/list.py +203 -0
- agent_starter_pack/cli/commands/register_gemini_enterprise.py +1070 -0
- agent_starter_pack/cli/commands/setup_cicd.py +862 -0
- {src → agent_starter_pack}/cli/main.py +10 -2
- {src → agent_starter_pack}/cli/utils/cicd.py +139 -48
- agent_starter_pack/cli/utils/gcp.py +263 -0
- agent_starter_pack/cli/utils/logging.py +103 -0
- agent_starter_pack/cli/utils/remote_template.py +677 -0
- agent_starter_pack/cli/utils/template.py +1466 -0
- {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/components/process_data.py +1 -1
- {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/submit_pipeline.py +20 -6
- {src → agent_starter_pack}/data_ingestion/pyproject.toml +1 -0
- {src → agent_starter_pack}/data_ingestion/uv.lock +602 -494
- agent_starter_pack/deployment_targets/agent_engine/tests/integration/test_agent_engine_app.py +484 -0
- agent_starter_pack/deployment_targets/agent_engine/tests/load_test/README.md +84 -0
- agent_starter_pack/deployment_targets/agent_engine/tests/load_test/load_test.py +424 -0
- agent_starter_pack/deployment_targets/agent_engine/tests/{% if cookiecutter.is_a2a %}helpers.py{% else %}unused_helpers.py{% endif %} +138 -0
- agent_starter_pack/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/agent_engine_app.py +263 -0
- agent_starter_pack/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/app_utils/deploy.py +414 -0
- agent_starter_pack/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/app_utils/{% if cookiecutter.is_adk_live %}expose_app.py{% else %}unused_expose_app.py{% endif %} +519 -0
- agent_starter_pack/deployment_targets/cloud_run/Dockerfile +51 -0
- agent_starter_pack/deployment_targets/cloud_run/deployment/terraform/dev/service.tf +243 -0
- agent_starter_pack/deployment_targets/cloud_run/deployment/terraform/service.tf +417 -0
- agent_starter_pack/deployment_targets/cloud_run/tests/integration/test_server_e2e.py +705 -0
- agent_starter_pack/deployment_targets/cloud_run/tests/load_test/.results/.placeholder +321 -0
- agent_starter_pack/deployment_targets/cloud_run/tests/load_test/README.md +165 -0
- agent_starter_pack/deployment_targets/cloud_run/tests/load_test/load_test.py +329 -0
- agent_starter_pack/deployment_targets/cloud_run/{{cookiecutter.agent_directory}}/fast_api_app.py +556 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/package-lock.json +79 -1044
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/package.json +1 -9
- agent_starter_pack/frontends/adk_live_react/frontend/src/App.tsx +65 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/logger/Logger.tsx +8 -3
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/logger/logger.scss +26 -0
- agent_starter_pack/frontends/adk_live_react/frontend/src/components/side-panel/SidePanel.tsx +516 -0
- agent_starter_pack/frontends/adk_live_react/frontend/src/components/side-panel/side-panel.scss +563 -0
- agent_starter_pack/frontends/adk_live_react/frontend/src/components/transcription-preview/TranscriptionPreview.tsx +106 -0
- agent_starter_pack/frontends/adk_live_react/frontend/src/components/transcription-preview/transcription-preview.scss +150 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-live-api.ts +8 -2
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/multimodal-live-types.ts +40 -2
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/audio-recorder.ts +1 -1
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/audio-streamer.ts +1 -1
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/multimodal-live-client.ts +210 -24
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/utils.ts +27 -5
- agent_starter_pack/resources/docs/adk-cheatsheet.md +1628 -0
- agent_starter_pack/resources/locks/uv-adk_a2a_base-agent_engine.lock +4966 -0
- agent_starter_pack/resources/locks/uv-adk_a2a_base-cloud_run.lock +5011 -0
- agent_starter_pack/resources/locks/uv-adk_base-agent_engine.lock +4946 -0
- agent_starter_pack/resources/locks/uv-adk_base-cloud_run.lock +4991 -0
- agent_starter_pack/resources/locks/uv-adk_live-agent_engine.lock +4963 -0
- agent_starter_pack/resources/locks/uv-adk_live-cloud_run.lock +5006 -0
- agent_starter_pack/resources/locks/uv-agentic_rag-agent_engine.lock +5487 -0
- agent_starter_pack/resources/locks/uv-agentic_rag-cloud_run.lock +5532 -0
- agent_starter_pack/resources/locks/uv-langgraph_base-agent_engine.lock +5788 -0
- agent_starter_pack/resources/locks/uv-langgraph_base-cloud_run.lock +5811 -0
- {src → agent_starter_pack}/utils/generate_locks.py +15 -12
- {src → agent_starter_pack}/utils/lock_utils.py +4 -7
- {src → agent_starter_pack}/utils/watch_and_rebuild.py +2 -2
- agent_starter_pack-0.21.0.dist-info/METADATA +182 -0
- agent_starter_pack-0.21.0.dist-info/RECORD +171 -0
- agent_starter_pack-0.21.0.dist-info/entry_points.txt +2 -0
- llm.txt +362 -0
- agent_starter_pack-0.3.3.dist-info/METADATA +0 -164
- agent_starter_pack-0.3.3.dist-info/RECORD +0 -176
- agent_starter_pack-0.3.3.dist-info/entry_points.txt +0 -2
- agents/crewai_coding_crew/README.md +0 -34
- agents/crewai_coding_crew/app/agent.py +0 -86
- agents/crewai_coding_crew/app/crew/config/agents.yaml +0 -39
- agents/crewai_coding_crew/app/crew/config/tasks.yaml +0 -37
- agents/crewai_coding_crew/app/crew/crew.py +0 -71
- agents/crewai_coding_crew/tests/integration/test_agent.py +0 -47
- agents/langgraph_base_react/README.md +0 -9
- agents/langgraph_base_react/app/agent.py +0 -73
- agents/live_api/README.md +0 -37
- agents/live_api/app/agent.py +0 -78
- agents/live_api/app/server.py +0 -196
- agents/live_api/app/templates.py +0 -51
- agents/live_api/app/vector_store.py +0 -55
- agents/live_api/template/.templateconfig.yaml +0 -29
- agents/live_api/tests/integration/test_server_e2e.py +0 -254
- agents/live_api/tests/load_test/load_test.py +0 -40
- agents/live_api/tests/unit/test_server.py +0 -143
- src/base_template/Makefile +0 -72
- src/base_template/README.md +0 -208
- src/base_template/app/__init__.py +0 -3
- src/base_template/app/utils/tracing.py +0 -155
- src/base_template/deployment/README.md +0 -126
- src/base_template/deployment/cd/staging.yaml +0 -216
- src/base_template/deployment/terraform/dev/log_sinks.tf +0 -63
- src/base_template/deployment/terraform/log_sinks.tf +0 -70
- src/base_template/deployment/terraform/providers.tf +0 -37
- src/cli/commands/create.py +0 -664
- src/cli/commands/setup_cicd.py +0 -829
- src/cli/utils/gcp.py +0 -117
- src/cli/utils/logging.py +0 -51
- src/cli/utils/template.py +0 -737
- src/deployment_targets/agent_engine/app/agent_engine_app.py +0 -336
- src/deployment_targets/agent_engine/notebooks/intro_agent_engine.ipynb +0 -1025
- src/deployment_targets/agent_engine/tests/integration/test_agent_engine_app.py +0 -183
- src/deployment_targets/agent_engine/tests/load_test/README.md +0 -42
- src/deployment_targets/agent_engine/tests/load_test/load_test.py +0 -107
- src/deployment_targets/cloud_run/app/server.py +0 -154
- src/deployment_targets/cloud_run/tests/integration/test_server_e2e.py +0 -249
- src/deployment_targets/cloud_run/tests/load_test/.results/.placeholder +0 -0
- src/deployment_targets/cloud_run/tests/load_test/README.md +0 -83
- src/deployment_targets/cloud_run/tests/load_test/load_test.py +0 -118
- src/deployment_targets/cloud_run/uv.lock +0 -6952
- src/frontends/live_api_react/frontend/src/App.tsx +0 -205
- src/frontends/live_api_react/frontend/src/components/control-tray/ControlTray.tsx +0 -217
- src/frontends/live_api_react/frontend/src/components/control-tray/control-tray.scss +0 -201
- src/frontends/live_api_react/frontend/src/components/side-panel/SidePanel.tsx +0 -161
- src/frontends/live_api_react/frontend/src/components/side-panel/side-panel.scss +0 -285
- src/frontends/streamlit/frontend/side_bar.py +0 -214
- src/frontends/streamlit/frontend/streamlit_app.py +0 -265
- src/frontends/streamlit/frontend/style/app_markdown.py +0 -37
- src/frontends/streamlit/frontend/utils/chat_utils.py +0 -67
- src/frontends/streamlit/frontend/utils/local_chat_history.py +0 -125
- src/frontends/streamlit/frontend/utils/message_editing.py +0 -59
- src/frontends/streamlit/frontend/utils/multimodal_utils.py +0 -217
- src/frontends/streamlit/frontend/utils/stream_handler.py +0 -301
- src/frontends/streamlit/frontend/utils/title_summary.py +0 -94
- src/frontends/streamlit_adk/frontend/side_bar.py +0 -214
- src/frontends/streamlit_adk/frontend/streamlit_app.py +0 -314
- src/frontends/streamlit_adk/frontend/style/app_markdown.py +0 -37
- src/frontends/streamlit_adk/frontend/utils/chat_utils.py +0 -84
- src/frontends/streamlit_adk/frontend/utils/local_chat_history.py +0 -110
- src/frontends/streamlit_adk/frontend/utils/message_editing.py +0 -61
- src/frontends/streamlit_adk/frontend/utils/multimodal_utils.py +0 -223
- src/frontends/streamlit_adk/frontend/utils/stream_handler.py +0 -311
- src/frontends/streamlit_adk/frontend/utils/title_summary.py +0 -129
- src/resources/containers/data_processing/Dockerfile +0 -27
- src/resources/containers/e2e-tests/Dockerfile +0 -19
- src/resources/idx/.idx/dev.nix +0 -57
- src/resources/idx/idx-template.json +0 -21
- src/resources/idx/idx-template.nix +0 -26
- src/resources/locks/uv-adk_base-agent_engine.lock +0 -5338
- src/resources/locks/uv-adk_base-cloud_run.lock +0 -5930
- src/resources/locks/uv-agentic_rag-agent_engine.lock +0 -5528
- src/resources/locks/uv-agentic_rag-cloud_run.lock +0 -6120
- src/resources/locks/uv-crewai_coding_crew-agent_engine.lock +0 -6231
- src/resources/locks/uv-crewai_coding_crew-cloud_run.lock +0 -6839
- src/resources/locks/uv-langgraph_base_react-agent_engine.lock +0 -5233
- src/resources/locks/uv-langgraph_base_react-cloud_run.lock +0 -5862
- src/resources/locks/uv-live_api-cloud_run.lock +0 -5832
- src/resources/setup_cicd/cicd_variables.tf +0 -41
- src/resources/setup_cicd/github.tf +0 -87
- {agents → agent_starter_pack/agents}/agentic_rag/app/retrievers.py +0 -0
- {agents → agent_starter_pack/agents}/agentic_rag/app/templates.py +0 -0
- {src → agent_starter_pack}/base_template/deployment/terraform/dev/vars/env.tfvars +0 -0
- {src → agent_starter_pack}/base_template/tests/unit/test_dummy.py +0 -0
- {src/deployment_targets/agent_engine/app/utils → agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils}/gcs.py +0 -0
- {src → agent_starter_pack}/cli/utils/__init__.py +0 -0
- {src → agent_starter_pack}/cli/utils/datastores.py +0 -0
- {src → agent_starter_pack}/cli/utils/version.py +0 -0
- {src → agent_starter_pack}/data_ingestion/README.md +0 -0
- {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/components/ingest_data.py +0 -0
- {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/pipeline.py +0 -0
- {src → agent_starter_pack}/deployment_targets/agent_engine/deployment_metadata.json +0 -0
- {src → agent_starter_pack}/deployment_targets/agent_engine/tests/load_test/.results/.placeholder +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/public/favicon.ico +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/public/index.html +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/public/robots.txt +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/App.scss +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/App.test.tsx +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/audio-pulse/AudioPulse.tsx +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/audio-pulse/audio-pulse.scss +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/logger/mock-logs.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/contexts/LiveAPIContext.tsx +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-media-stream-mux.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-screen-capture.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-webcam.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/index.css +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/index.tsx +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/react-app-env.d.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/reportWebVitals.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/setupTests.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/audioworklet-registry.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/store-logger.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/worklets/audio-processing.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/worklets/vol-meter.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/tsconfig.json +0 -0
- {agent_starter_pack-0.3.3.dist-info → agent_starter_pack-0.21.0.dist-info}/WHEEL +0 -0
- {agent_starter_pack-0.3.3.dist-info → agent_starter_pack-0.21.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,1070 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Copyright 2025 Google LLC
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
"""Utility to register an Agent Engine to Gemini Enterprise."""
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
import os
|
|
20
|
+
import subprocess
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
|
|
23
|
+
import click
|
|
24
|
+
import requests
|
|
25
|
+
import vertexai
|
|
26
|
+
from google.auth import default
|
|
27
|
+
from google.auth.transport.requests import Request as GoogleAuthRequest
|
|
28
|
+
from rich.console import Console
|
|
29
|
+
|
|
30
|
+
console = Console(highlight=False)
|
|
31
|
+
console_err = Console(stderr=True, highlight=False)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def get_discovery_engine_endpoint(location: str) -> str:
|
|
35
|
+
"""Get the appropriate Discovery Engine API endpoint for the given location.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
location: The location/region (e.g., 'global', 'us', 'eu')
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
The Discovery Engine API endpoint base URL
|
|
42
|
+
|
|
43
|
+
Examples:
|
|
44
|
+
>>> get_discovery_engine_endpoint('global')
|
|
45
|
+
'https://discoveryengine.googleapis.com'
|
|
46
|
+
>>> get_discovery_engine_endpoint('eu')
|
|
47
|
+
'https://eu-discoveryengine.googleapis.com'
|
|
48
|
+
>>> get_discovery_engine_endpoint('us')
|
|
49
|
+
'https://us-discoveryengine.googleapis.com'
|
|
50
|
+
"""
|
|
51
|
+
if location == "global":
|
|
52
|
+
return "https://discoveryengine.googleapis.com"
|
|
53
|
+
else:
|
|
54
|
+
# Regional endpoints use the format: https://{region}-discoveryengine.googleapis.com
|
|
55
|
+
return f"https://{location}-discoveryengine.googleapis.com"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def parse_agent_engine_id(agent_engine_id: str) -> dict[str, str] | None:
|
|
59
|
+
"""Parse an Agent Engine resource name to extract components.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
agent_engine_id: Agent Engine resource name
|
|
63
|
+
(e.g., projects/PROJECT_NUM/locations/REGION/reasoningEngines/ENGINE_ID)
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Dictionary with 'project', 'location', 'engine_id' keys, or None if invalid format
|
|
67
|
+
"""
|
|
68
|
+
parts = agent_engine_id.split("/")
|
|
69
|
+
if (
|
|
70
|
+
len(parts) == 6
|
|
71
|
+
and parts[0] == "projects"
|
|
72
|
+
and parts[2] == "locations"
|
|
73
|
+
and parts[4] == "reasoningEngines"
|
|
74
|
+
):
|
|
75
|
+
return {
|
|
76
|
+
"project": parts[1],
|
|
77
|
+
"location": parts[3],
|
|
78
|
+
"engine_id": parts[5],
|
|
79
|
+
}
|
|
80
|
+
return None
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def parse_gemini_enterprise_app_id(app_id: str) -> dict[str, str] | None:
|
|
84
|
+
"""Parse Gemini Enterprise app resource name to extract components.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
app_id: Gemini Enterprise app resource name
|
|
88
|
+
(e.g., projects/{project_number}/locations/{location}/collections/{collection}/engines/{engine_id})
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Dictionary with 'project_number', 'location', 'collection', 'engine_id' keys, or None if invalid format
|
|
92
|
+
"""
|
|
93
|
+
parts = app_id.split("/")
|
|
94
|
+
if (
|
|
95
|
+
len(parts) == 8
|
|
96
|
+
and parts[0] == "projects"
|
|
97
|
+
and parts[2] == "locations"
|
|
98
|
+
and parts[4] == "collections"
|
|
99
|
+
and parts[6] == "engines"
|
|
100
|
+
):
|
|
101
|
+
return {
|
|
102
|
+
"project_number": parts[1],
|
|
103
|
+
"location": parts[3],
|
|
104
|
+
"collection": parts[5],
|
|
105
|
+
"engine_id": parts[7],
|
|
106
|
+
}
|
|
107
|
+
return None
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def get_access_token() -> str:
|
|
111
|
+
"""Get Google Cloud access token.
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
Access token string
|
|
115
|
+
|
|
116
|
+
Raises:
|
|
117
|
+
RuntimeError: If authentication fails
|
|
118
|
+
"""
|
|
119
|
+
try:
|
|
120
|
+
credentials, _ = default()
|
|
121
|
+
auth_req = GoogleAuthRequest()
|
|
122
|
+
credentials.refresh(auth_req)
|
|
123
|
+
return credentials.token
|
|
124
|
+
except Exception as e:
|
|
125
|
+
console_err.print(f"Error getting access token: {e}")
|
|
126
|
+
console_err.print(
|
|
127
|
+
"Please ensure you are authenticated with 'gcloud auth application-default login'"
|
|
128
|
+
)
|
|
129
|
+
raise RuntimeError("Failed to get access token") from e
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def get_identity_token() -> str:
|
|
133
|
+
"""Get Google Cloud identity token.
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
Identity token string
|
|
137
|
+
|
|
138
|
+
Raises:
|
|
139
|
+
RuntimeError: If authentication fails
|
|
140
|
+
"""
|
|
141
|
+
try:
|
|
142
|
+
result = subprocess.run(
|
|
143
|
+
["gcloud", "auth", "print-identity-token"],
|
|
144
|
+
capture_output=True,
|
|
145
|
+
text=True,
|
|
146
|
+
check=True,
|
|
147
|
+
)
|
|
148
|
+
return result.stdout.strip()
|
|
149
|
+
except subprocess.CalledProcessError as e:
|
|
150
|
+
console_err.print(f"Error getting identity token: {e.stderr}")
|
|
151
|
+
console_err.print(
|
|
152
|
+
"Please ensure you are authenticated with 'gcloud auth login'"
|
|
153
|
+
)
|
|
154
|
+
raise RuntimeError("Failed to get identity token") from e
|
|
155
|
+
except FileNotFoundError as e:
|
|
156
|
+
console_err.print("Error: gcloud command not found")
|
|
157
|
+
console_err.print("Please install Google Cloud SDK")
|
|
158
|
+
raise RuntimeError("Failed to get identity token") from e
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def fetch_agent_card_from_url(url: str, deployment_target: str) -> dict | None:
|
|
162
|
+
"""Fetch agent card from a URL with proper authentication.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
url: The URL to fetch the agent card from
|
|
166
|
+
deployment_target: The deployment target ('agent_engine' or 'cloud_run')
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
Agent card dictionary if successful, None otherwise
|
|
170
|
+
"""
|
|
171
|
+
try:
|
|
172
|
+
headers = {}
|
|
173
|
+
|
|
174
|
+
# Use appropriate authentication based on deployment target
|
|
175
|
+
if deployment_target == "agent_engine":
|
|
176
|
+
access_token = get_access_token()
|
|
177
|
+
headers["Authorization"] = f"Bearer {access_token}"
|
|
178
|
+
elif deployment_target == "cloud_run":
|
|
179
|
+
identity_token = get_identity_token()
|
|
180
|
+
headers["Authorization"] = f"Bearer {identity_token}"
|
|
181
|
+
else:
|
|
182
|
+
raise ValueError(
|
|
183
|
+
f"Unknown deployment target: {deployment_target}. "
|
|
184
|
+
f"Expected 'agent_engine' or 'cloud_run'"
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
response = requests.get(url, headers=headers, timeout=10)
|
|
188
|
+
response.raise_for_status()
|
|
189
|
+
|
|
190
|
+
return response.json()
|
|
191
|
+
except requests.exceptions.HTTPError as e:
|
|
192
|
+
console_err.print(
|
|
193
|
+
f"⚠️ HTTP error fetching agent card from {url}: {e}",
|
|
194
|
+
style="yellow",
|
|
195
|
+
)
|
|
196
|
+
if e.response.status_code == 401 or e.response.status_code == 403:
|
|
197
|
+
console_err.print(
|
|
198
|
+
" Authentication failed. Ensure you are logged in with 'gcloud auth application-default login'",
|
|
199
|
+
style="yellow",
|
|
200
|
+
)
|
|
201
|
+
return None
|
|
202
|
+
except Exception as e:
|
|
203
|
+
console_err.print(
|
|
204
|
+
f"⚠️ Could not fetch agent card from {url}: {e}",
|
|
205
|
+
style="yellow",
|
|
206
|
+
)
|
|
207
|
+
return None
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def construct_agent_card_url_from_metadata(
|
|
211
|
+
metadata: dict,
|
|
212
|
+
) -> str | None:
|
|
213
|
+
"""Construct agent card URL from deployment metadata (Agent Engine only).
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
metadata: Deployment metadata dictionary
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
Agent card URL if construction succeeds, None otherwise
|
|
220
|
+
"""
|
|
221
|
+
deployment_target = metadata.get("deployment_target")
|
|
222
|
+
|
|
223
|
+
if deployment_target == "agent_engine":
|
|
224
|
+
# For Agent Engine: construct URL from remote_agent_engine_id
|
|
225
|
+
remote_agent_engine_id = metadata.get("remote_agent_engine_id")
|
|
226
|
+
if remote_agent_engine_id and remote_agent_engine_id != "None":
|
|
227
|
+
parsed = parse_agent_engine_id(remote_agent_engine_id)
|
|
228
|
+
if parsed:
|
|
229
|
+
location = parsed["location"]
|
|
230
|
+
# Agent Engine A2A endpoint format
|
|
231
|
+
agent_card_url = (
|
|
232
|
+
f"https://{location}-aiplatform.googleapis.com/v1beta1/"
|
|
233
|
+
f"{remote_agent_engine_id}/a2a/v1/card"
|
|
234
|
+
)
|
|
235
|
+
return agent_card_url
|
|
236
|
+
|
|
237
|
+
return None
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def prompt_for_agent_card_url_with_auto_construct(
|
|
241
|
+
metadata: dict | None,
|
|
242
|
+
default_url: str | None = None,
|
|
243
|
+
) -> str:
|
|
244
|
+
"""Get agent card URL with automatic construction from deployment metadata.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
metadata: Deployment metadata dictionary (can be None)
|
|
248
|
+
default_url: Default agent card URL (e.g., from CLI arg)
|
|
249
|
+
|
|
250
|
+
Returns:
|
|
251
|
+
Agent card URL
|
|
252
|
+
"""
|
|
253
|
+
# If default URL provided, show as smart default
|
|
254
|
+
if default_url:
|
|
255
|
+
console.print("\nAgent card URL provided:")
|
|
256
|
+
console.print(f" [bold]{default_url}[/]")
|
|
257
|
+
use_default = click.confirm(
|
|
258
|
+
"Use this agent card URL?", default=True, show_default=True
|
|
259
|
+
)
|
|
260
|
+
if use_default:
|
|
261
|
+
return default_url
|
|
262
|
+
|
|
263
|
+
# Try to auto-construct from metadata (Agent Engine only)
|
|
264
|
+
if metadata:
|
|
265
|
+
auto_url = construct_agent_card_url_from_metadata(metadata)
|
|
266
|
+
|
|
267
|
+
if auto_url:
|
|
268
|
+
# Successfully constructed from Agent Engine metadata
|
|
269
|
+
console.print(
|
|
270
|
+
"\n✅ Found Agent Engine deployment in deployment_metadata.json"
|
|
271
|
+
)
|
|
272
|
+
console.print(f" Agent card URL: [bold]{auto_url}[/]")
|
|
273
|
+
|
|
274
|
+
use_auto = click.confirm(
|
|
275
|
+
"\nUse this agent card URL?", default=True, show_default=True
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
if use_auto:
|
|
279
|
+
return auto_url
|
|
280
|
+
|
|
281
|
+
# Fallback: manual entry
|
|
282
|
+
console.print("\n[blue]" + "=" * 70 + "[/]")
|
|
283
|
+
console.print("[blue]A2A AGENT CARD URL[/]")
|
|
284
|
+
console.print("[blue]" + "=" * 70 + "[/]")
|
|
285
|
+
console.print(
|
|
286
|
+
"\nEnter your agent card URL manually"
|
|
287
|
+
"\n[blue]Example: https://your-service.run.app/a2a/app/.well-known/agent-card.json[/]"
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
agent_card_url = click.prompt(
|
|
291
|
+
"\nAgent card URL",
|
|
292
|
+
type=str,
|
|
293
|
+
).strip()
|
|
294
|
+
|
|
295
|
+
return agent_card_url
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def get_agent_engine_metadata(agent_engine_id: str) -> tuple[str | None, str | None]:
|
|
299
|
+
"""Fetch display_name and description from deployed Agent Engine.
|
|
300
|
+
|
|
301
|
+
Args:
|
|
302
|
+
agent_engine_id: Agent Engine resource name
|
|
303
|
+
|
|
304
|
+
Returns:
|
|
305
|
+
Tuple of (display_name, description) - either can be None if not found
|
|
306
|
+
"""
|
|
307
|
+
parts = agent_engine_id.split("/")
|
|
308
|
+
if len(parts) < 6:
|
|
309
|
+
return None, None
|
|
310
|
+
|
|
311
|
+
project_id = parts[1]
|
|
312
|
+
location = parts[3]
|
|
313
|
+
|
|
314
|
+
try:
|
|
315
|
+
client = vertexai.Client(project=project_id, location=location)
|
|
316
|
+
agent_engine = client.agent_engines.get(name=agent_engine_id)
|
|
317
|
+
|
|
318
|
+
display_name = getattr(agent_engine.api_resource, "display_name", None)
|
|
319
|
+
description = getattr(agent_engine.api_resource, "description", None)
|
|
320
|
+
|
|
321
|
+
return display_name, description
|
|
322
|
+
except Exception as e:
|
|
323
|
+
console_err.print(f"Warning: Could not fetch metadata from Agent Engine: {e}")
|
|
324
|
+
return None, None
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def prompt_for_agent_engine_id(default_from_metadata: str | None) -> str:
|
|
328
|
+
"""Prompt user for Agent Engine ID with optional default.
|
|
329
|
+
|
|
330
|
+
Args:
|
|
331
|
+
default_from_metadata: Default value from deployment_metadata.json if available
|
|
332
|
+
|
|
333
|
+
Returns:
|
|
334
|
+
The Agent Engine resource name
|
|
335
|
+
"""
|
|
336
|
+
if default_from_metadata:
|
|
337
|
+
console.print("\nFound Agent Engine ID from deployment_metadata.json:")
|
|
338
|
+
console.print(f" [bold]{default_from_metadata}[/]")
|
|
339
|
+
use_default = click.confirm(
|
|
340
|
+
"Use this Agent Engine ID?", default=True, show_default=True
|
|
341
|
+
)
|
|
342
|
+
if use_default:
|
|
343
|
+
return default_from_metadata
|
|
344
|
+
|
|
345
|
+
console.print(
|
|
346
|
+
"\nEnter your Agent Engine resource name"
|
|
347
|
+
"\n[blue]Example: projects/123456789/locations/us-central1/reasoningEngines/1234567890[/]"
|
|
348
|
+
"\n(You can find this in the Agent Builder Console or deployment_metadata.json)"
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
while True:
|
|
352
|
+
agent_engine_id = click.prompt("Agent Engine ID", type=str).strip()
|
|
353
|
+
parsed = parse_agent_engine_id(agent_engine_id)
|
|
354
|
+
if parsed:
|
|
355
|
+
return agent_engine_id
|
|
356
|
+
else:
|
|
357
|
+
console_err.print(
|
|
358
|
+
"❌ Invalid format. Expected: projects/{project}/locations/{location}/reasoningEngines/{id}",
|
|
359
|
+
style="bold red",
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def prompt_for_gemini_enterprise_components(
|
|
364
|
+
default_project: str | None = None,
|
|
365
|
+
) -> str:
|
|
366
|
+
"""Prompt user for Gemini Enterprise resource components and construct full ID.
|
|
367
|
+
|
|
368
|
+
Args:
|
|
369
|
+
default_project: Default project number from Agent Engine ID
|
|
370
|
+
|
|
371
|
+
Returns:
|
|
372
|
+
Full Gemini Enterprise app resource name
|
|
373
|
+
"""
|
|
374
|
+
console.print("\n[blue]" + "=" * 70 + "[/]")
|
|
375
|
+
console.print("[blue]GEMINI ENTERPRISE CONFIGURATION[/]")
|
|
376
|
+
console.print("[blue]" + "=" * 70 + "[/]")
|
|
377
|
+
|
|
378
|
+
console.print(
|
|
379
|
+
"\nYou need to provide the Gemini Enterprise app details."
|
|
380
|
+
"\nFind these in: Google Cloud Console → Gemini Enterprise → Apps"
|
|
381
|
+
"\nCopy the ID from the 'ID' column for your Gemini Enterprise instance."
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
while True:
|
|
385
|
+
# Project number
|
|
386
|
+
if default_project:
|
|
387
|
+
console.print(f"\n[dim]Default: {default_project}[/]")
|
|
388
|
+
project_number = click.prompt(
|
|
389
|
+
"Project number", type=str, default=default_project or ""
|
|
390
|
+
).strip()
|
|
391
|
+
|
|
392
|
+
# Location - GE apps are typically in 'global', 'us', or 'eu'
|
|
393
|
+
console.print("\nGemini Enterprise apps are in: global, us, or eu")
|
|
394
|
+
location = click.prompt(
|
|
395
|
+
"Location/Region",
|
|
396
|
+
type=str,
|
|
397
|
+
default="global",
|
|
398
|
+
show_default=True,
|
|
399
|
+
).strip()
|
|
400
|
+
|
|
401
|
+
# Gemini Enterprise short ID
|
|
402
|
+
console.print(
|
|
403
|
+
"\nEnter your Gemini Enterprise ID (from the 'ID' column in the Apps table)."
|
|
404
|
+
"\n[blue]Example: gemini-enterprise-123456_1234567890[/]"
|
|
405
|
+
)
|
|
406
|
+
ge_short_id = click.prompt("Gemini Enterprise ID", type=str).strip()
|
|
407
|
+
|
|
408
|
+
# Construct full resource name
|
|
409
|
+
# Format: projects/{project_number}/locations/{location}/collections/default_collection/engines/{ge_id}
|
|
410
|
+
full_id = f"projects/{project_number}/locations/{location}/collections/default_collection/engines/{ge_short_id}"
|
|
411
|
+
|
|
412
|
+
console.print("\nConstructed Gemini Enterprise App ID:")
|
|
413
|
+
console.print(f" [bold]{full_id}[/]")
|
|
414
|
+
confirmed = click.confirm("Is this correct?", default=True)
|
|
415
|
+
|
|
416
|
+
if confirmed:
|
|
417
|
+
return full_id
|
|
418
|
+
|
|
419
|
+
click.echo("Let's try again...")
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def ensure_discovery_engine_invoker_role(
|
|
423
|
+
project_id: str,
|
|
424
|
+
project_number: str,
|
|
425
|
+
) -> None:
|
|
426
|
+
"""Grant Cloud Run Invoker role to Discovery Engine service account at project level.
|
|
427
|
+
|
|
428
|
+
Silently grants the role if not already present. Only shows warnings/errors
|
|
429
|
+
if there are permission issues or unexpected failures.
|
|
430
|
+
|
|
431
|
+
Args:
|
|
432
|
+
project_id: GCP project ID
|
|
433
|
+
project_number: GCP project number
|
|
434
|
+
"""
|
|
435
|
+
try:
|
|
436
|
+
# Construct Discovery Engine service account
|
|
437
|
+
service_account = (
|
|
438
|
+
f"service-{project_number}@gcp-sa-discoveryengine.iam.gserviceaccount.com"
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
result = subprocess.run(
|
|
442
|
+
[
|
|
443
|
+
"gcloud",
|
|
444
|
+
"projects",
|
|
445
|
+
"add-iam-policy-binding",
|
|
446
|
+
project_id,
|
|
447
|
+
f"--member=serviceAccount:{service_account}",
|
|
448
|
+
"--role=roles/run.servicesInvoker",
|
|
449
|
+
"--condition=None",
|
|
450
|
+
"--quiet",
|
|
451
|
+
],
|
|
452
|
+
capture_output=True,
|
|
453
|
+
text=True,
|
|
454
|
+
)
|
|
455
|
+
|
|
456
|
+
if result.returncode != 0:
|
|
457
|
+
error_msg = result.stderr.lower()
|
|
458
|
+
# Ignore "already exists" type errors
|
|
459
|
+
if "already exists" not in error_msg and "already has" not in error_msg:
|
|
460
|
+
# Permission errors - show warning but don't fail
|
|
461
|
+
if "permission" in error_msg or "forbidden" in error_msg:
|
|
462
|
+
console.print(
|
|
463
|
+
f"\n⚠️ [yellow]Warning: Could not grant roles/run.invoker to {service_account}[/]\n"
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
except Exception:
|
|
467
|
+
pass
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
def register_a2a_agent(
|
|
471
|
+
agent_card: dict,
|
|
472
|
+
agent_card_url: str,
|
|
473
|
+
gemini_enterprise_app_id: str,
|
|
474
|
+
display_name: str,
|
|
475
|
+
description: str,
|
|
476
|
+
project_id: str | None = None,
|
|
477
|
+
authorization_id: str | None = None,
|
|
478
|
+
) -> dict:
|
|
479
|
+
"""Register an A2A agent to Gemini Enterprise.
|
|
480
|
+
|
|
481
|
+
Args:
|
|
482
|
+
agent_card: Agent card dictionary fetched from the agent
|
|
483
|
+
agent_card_url: URL where the agent card was fetched from
|
|
484
|
+
gemini_enterprise_app_id: Full Gemini Enterprise app resource name
|
|
485
|
+
display_name: Display name for the agent in Gemini Enterprise
|
|
486
|
+
description: Description of the agent
|
|
487
|
+
project_id: Optional GCP project ID for billing
|
|
488
|
+
authorization_id: Optional OAuth authorization ID
|
|
489
|
+
|
|
490
|
+
Returns:
|
|
491
|
+
API response as dictionary
|
|
492
|
+
|
|
493
|
+
Raises:
|
|
494
|
+
requests.HTTPError: If the API request fails
|
|
495
|
+
ValueError: If gemini_enterprise_app_id format is invalid
|
|
496
|
+
"""
|
|
497
|
+
parsed = parse_gemini_enterprise_app_id(gemini_enterprise_app_id)
|
|
498
|
+
if not parsed:
|
|
499
|
+
raise ValueError(
|
|
500
|
+
f"Invalid GEMINI_ENTERPRISE_APP_ID format. Expected: "
|
|
501
|
+
f"projects/{{project_number}}/locations/{{location}}/collections/{{collection}}/engines/{{engine_id}}, "
|
|
502
|
+
f"got: {gemini_enterprise_app_id}"
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
project_number = parsed["project_number"]
|
|
506
|
+
as_location = parsed["location"]
|
|
507
|
+
collection = parsed["collection"]
|
|
508
|
+
engine_id = parsed["engine_id"]
|
|
509
|
+
|
|
510
|
+
# Use provided project ID or fallback to project number from GE app
|
|
511
|
+
if not project_id:
|
|
512
|
+
project_id = project_number
|
|
513
|
+
|
|
514
|
+
access_token = get_access_token()
|
|
515
|
+
base_endpoint = get_discovery_engine_endpoint(as_location)
|
|
516
|
+
url = (
|
|
517
|
+
f"{base_endpoint}/v1alpha/projects/{project_number}/"
|
|
518
|
+
f"locations/{as_location}/collections/{collection}/engines/{engine_id}/"
|
|
519
|
+
"assistants/default_assistant/agents"
|
|
520
|
+
)
|
|
521
|
+
headers = {
|
|
522
|
+
"Authorization": f"Bearer {access_token}",
|
|
523
|
+
"Content-Type": "application/json",
|
|
524
|
+
"x-goog-user-project": project_id,
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
# Build payload with A2A agent definition
|
|
528
|
+
payload = {
|
|
529
|
+
"displayName": display_name,
|
|
530
|
+
"description": description,
|
|
531
|
+
"icon": {
|
|
532
|
+
"uri": "https://fonts.gstatic.com/s/i/short-term/release/googlesymbols/smart_toy/default/24px.svg"
|
|
533
|
+
},
|
|
534
|
+
"a2aAgentDefinition": {"jsonAgentCard": json.dumps(agent_card)},
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
# Add authorization config if provided
|
|
538
|
+
if authorization_id:
|
|
539
|
+
payload["authorizationConfig"] = {"agentAuthorization": authorization_id}
|
|
540
|
+
|
|
541
|
+
console.print("\n[blue]Registering A2A agent to Gemini Enterprise...[/]")
|
|
542
|
+
console.print(f" Agent Card URL: {agent_card_url}")
|
|
543
|
+
console.print(f" Gemini Enterprise App: {gemini_enterprise_app_id}")
|
|
544
|
+
console.print(f" Display Name: {display_name}")
|
|
545
|
+
|
|
546
|
+
try:
|
|
547
|
+
response = requests.post(url, headers=headers, json=payload, timeout=30)
|
|
548
|
+
response.raise_for_status()
|
|
549
|
+
|
|
550
|
+
result = response.json()
|
|
551
|
+
console.print("\n✅ Successfully registered A2A agent to Gemini Enterprise!")
|
|
552
|
+
console.print(f" Agent Name:\n {result.get('name', 'N/A')}")
|
|
553
|
+
return result
|
|
554
|
+
|
|
555
|
+
except requests.exceptions.HTTPError as http_err:
|
|
556
|
+
# Check if agent already exists and try to update
|
|
557
|
+
if response.status_code in (400, 409):
|
|
558
|
+
try:
|
|
559
|
+
error_data = response.json()
|
|
560
|
+
error_message = error_data.get("error", {}).get("message", "")
|
|
561
|
+
|
|
562
|
+
if (
|
|
563
|
+
"already exists" in error_message.lower()
|
|
564
|
+
or "duplicate" in error_message.lower()
|
|
565
|
+
):
|
|
566
|
+
console.print(
|
|
567
|
+
"\n⚠️ [yellow]Agent already registered. Updating existing registration...[/]"
|
|
568
|
+
)
|
|
569
|
+
|
|
570
|
+
# List and find existing agent
|
|
571
|
+
list_response = requests.get(url, headers=headers, timeout=30)
|
|
572
|
+
list_response.raise_for_status()
|
|
573
|
+
agents_list = list_response.json().get("agents", [])
|
|
574
|
+
|
|
575
|
+
# Find matching agent (by URL in agent card)
|
|
576
|
+
existing_agent = None
|
|
577
|
+
for agent in agents_list:
|
|
578
|
+
a2a_def = agent.get("a2aAgentDefinition", {})
|
|
579
|
+
if a2a_def:
|
|
580
|
+
try:
|
|
581
|
+
card = json.loads(a2a_def.get("jsonAgentCard", "{}"))
|
|
582
|
+
if card.get("url") == agent_card_url:
|
|
583
|
+
existing_agent = agent
|
|
584
|
+
break
|
|
585
|
+
except json.JSONDecodeError:
|
|
586
|
+
continue
|
|
587
|
+
|
|
588
|
+
if existing_agent:
|
|
589
|
+
agent_name = existing_agent.get("name")
|
|
590
|
+
update_url = f"{base_endpoint}/v1alpha/{agent_name}"
|
|
591
|
+
|
|
592
|
+
console.print(f" Updating agent: {agent_name}")
|
|
593
|
+
|
|
594
|
+
update_response = requests.patch(
|
|
595
|
+
update_url, headers=headers, json=payload, timeout=30
|
|
596
|
+
)
|
|
597
|
+
update_response.raise_for_status()
|
|
598
|
+
|
|
599
|
+
result = update_response.json()
|
|
600
|
+
console.print(
|
|
601
|
+
"\n✅ Successfully updated A2A agent registration!"
|
|
602
|
+
)
|
|
603
|
+
console.print(f" Agent Name:\n {result.get('name', 'N/A')}")
|
|
604
|
+
return result
|
|
605
|
+
except (ValueError, KeyError) as e:
|
|
606
|
+
console_err.print(
|
|
607
|
+
f"Warning: Could not parse error response from API: {e}"
|
|
608
|
+
)
|
|
609
|
+
|
|
610
|
+
console_err.print(
|
|
611
|
+
f"\n❌ [red]HTTP error occurred: {http_err}[/]",
|
|
612
|
+
style="bold red",
|
|
613
|
+
)
|
|
614
|
+
console_err.print(f" Response: {response.text}")
|
|
615
|
+
raise
|
|
616
|
+
except requests.exceptions.RequestException as req_err:
|
|
617
|
+
console_err.print(
|
|
618
|
+
f"\n❌ [red]Request error occurred: {req_err}[/]",
|
|
619
|
+
style="bold red",
|
|
620
|
+
)
|
|
621
|
+
raise
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
def register_agent(
|
|
625
|
+
agent_engine_id: str,
|
|
626
|
+
gemini_enterprise_app_id: str,
|
|
627
|
+
display_name: str,
|
|
628
|
+
description: str,
|
|
629
|
+
tool_description: str,
|
|
630
|
+
project_id: str | None = None,
|
|
631
|
+
authorization_id: str | None = None,
|
|
632
|
+
) -> dict:
|
|
633
|
+
"""Register an agent engine to Gemini Enterprise.
|
|
634
|
+
|
|
635
|
+
This function attempts to create a new agent registration. If the agent is already
|
|
636
|
+
registered (same reasoning engine), it will automatically update the existing
|
|
637
|
+
registration instead.
|
|
638
|
+
|
|
639
|
+
Args:
|
|
640
|
+
agent_engine_id: Agent engine resource name (e.g., projects/.../reasoningEngines/...)
|
|
641
|
+
gemini_enterprise_app_id: Full Gemini Enterprise app resource name
|
|
642
|
+
(e.g., projects/{project_number}/locations/{location}/collections/{collection}/engines/{engine_id})
|
|
643
|
+
display_name: Display name for the agent in Gemini Enterprise
|
|
644
|
+
description: Description of the agent
|
|
645
|
+
tool_description: Description of what the tool does
|
|
646
|
+
project_id: Optional GCP project ID for billing (extracted from agent_engine_id if not provided)
|
|
647
|
+
authorization_id: Optional OAuth authorization ID
|
|
648
|
+
(e.g., projects/{project_number}/locations/global/authorizations/{auth_id})
|
|
649
|
+
|
|
650
|
+
Returns:
|
|
651
|
+
API response as dictionary
|
|
652
|
+
|
|
653
|
+
Raises:
|
|
654
|
+
requests.HTTPError: If the API request fails
|
|
655
|
+
ValueError: If gemini_enterprise_app_id format is invalid
|
|
656
|
+
"""
|
|
657
|
+
parsed = parse_gemini_enterprise_app_id(gemini_enterprise_app_id)
|
|
658
|
+
if not parsed:
|
|
659
|
+
raise ValueError(
|
|
660
|
+
f"Invalid GEMINI_ENTERPRISE_APP_ID format. Expected: "
|
|
661
|
+
f"projects/{{project_number}}/locations/{{location}}/collections/{{collection}}/engines/{{engine_id}}, "
|
|
662
|
+
f"got: {gemini_enterprise_app_id}"
|
|
663
|
+
)
|
|
664
|
+
|
|
665
|
+
project_number = parsed["project_number"]
|
|
666
|
+
as_location = parsed["location"]
|
|
667
|
+
collection = parsed["collection"]
|
|
668
|
+
engine_id = parsed["engine_id"]
|
|
669
|
+
|
|
670
|
+
# Use project from agent engine if not explicitly provided (for billing header)
|
|
671
|
+
if not project_id:
|
|
672
|
+
parsed_agent = parse_agent_engine_id(agent_engine_id)
|
|
673
|
+
if parsed_agent:
|
|
674
|
+
project_id = parsed_agent["project"]
|
|
675
|
+
else:
|
|
676
|
+
project_id = project_number
|
|
677
|
+
|
|
678
|
+
# Get access token
|
|
679
|
+
access_token = get_access_token()
|
|
680
|
+
|
|
681
|
+
# Build API endpoint with regional support
|
|
682
|
+
base_endpoint = get_discovery_engine_endpoint(as_location)
|
|
683
|
+
url = (
|
|
684
|
+
f"{base_endpoint}/v1alpha/projects/{project_number}/"
|
|
685
|
+
f"locations/{as_location}/collections/{collection}/engines/{engine_id}/"
|
|
686
|
+
"assistants/default_assistant/agents"
|
|
687
|
+
)
|
|
688
|
+
|
|
689
|
+
# Request headers
|
|
690
|
+
headers = {
|
|
691
|
+
"Authorization": f"Bearer {access_token}",
|
|
692
|
+
"Content-Type": "application/json",
|
|
693
|
+
"x-goog-user-project": project_id,
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
# Request body
|
|
697
|
+
adk_agent_definition: dict = {
|
|
698
|
+
"tool_settings": {"tool_description": tool_description},
|
|
699
|
+
"provisioned_reasoning_engine": {"reasoningEngine": agent_engine_id},
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
# Add OAuth authorization if provided
|
|
703
|
+
if authorization_id:
|
|
704
|
+
adk_agent_definition["authorizations"] = [authorization_id]
|
|
705
|
+
|
|
706
|
+
payload = {
|
|
707
|
+
"displayName": display_name,
|
|
708
|
+
"description": description,
|
|
709
|
+
"icon": {
|
|
710
|
+
"uri": "https://fonts.gstatic.com/s/i/short-term/release/googlesymbols/smart_toy/default/24px.svg"
|
|
711
|
+
},
|
|
712
|
+
"adk_agent_definition": adk_agent_definition,
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
console.print("\n[blue]Registering agent to Gemini Enterprise...[/]")
|
|
716
|
+
console.print(f" Agent Engine: {agent_engine_id}")
|
|
717
|
+
console.print(f" Gemini Enterprise App: {gemini_enterprise_app_id}")
|
|
718
|
+
console.print(f" Display Name: {display_name}")
|
|
719
|
+
|
|
720
|
+
try:
|
|
721
|
+
# Try to create a new registration first
|
|
722
|
+
response = requests.post(url, headers=headers, json=payload, timeout=30)
|
|
723
|
+
response.raise_for_status()
|
|
724
|
+
|
|
725
|
+
result = response.json()
|
|
726
|
+
console.print("\n✅ Successfully registered agent to Gemini Enterprise!")
|
|
727
|
+
console.print(f" Agent Name:\n {result.get('name', 'N/A')}")
|
|
728
|
+
return result
|
|
729
|
+
|
|
730
|
+
except requests.exceptions.HTTPError as http_err:
|
|
731
|
+
# Check if the error is because the agent already exists
|
|
732
|
+
if response.status_code in (400, 409):
|
|
733
|
+
try:
|
|
734
|
+
error_data = response.json()
|
|
735
|
+
error_message = error_data.get("error", {}).get("message", "")
|
|
736
|
+
|
|
737
|
+
# Check if error indicates the agent already exists
|
|
738
|
+
if (
|
|
739
|
+
"already exists" in error_message.lower()
|
|
740
|
+
or "duplicate" in error_message.lower()
|
|
741
|
+
):
|
|
742
|
+
console.print(
|
|
743
|
+
"\n⚠️ [yellow]Agent already registered. Updating existing registration...[/]"
|
|
744
|
+
)
|
|
745
|
+
|
|
746
|
+
# For update, we need to use the specific agent resource name
|
|
747
|
+
# The agent name should be in the error or we can construct it
|
|
748
|
+
# Format: {url}/{agent_id} but we need to find existing agent first
|
|
749
|
+
|
|
750
|
+
# List existing agents to find the one for this reasoning engine
|
|
751
|
+
list_response = requests.get(url, headers=headers, timeout=30)
|
|
752
|
+
list_response.raise_for_status()
|
|
753
|
+
agents_list = list_response.json().get("agents", [])
|
|
754
|
+
|
|
755
|
+
# Find the agent that matches our reasoning engine
|
|
756
|
+
existing_agent = None
|
|
757
|
+
for agent in agents_list:
|
|
758
|
+
re_name = (
|
|
759
|
+
agent.get("adk_agent_definition", {})
|
|
760
|
+
.get("provisioned_reasoning_engine", {})
|
|
761
|
+
.get("reasoningEngine", "")
|
|
762
|
+
)
|
|
763
|
+
if re_name == agent_engine_id:
|
|
764
|
+
existing_agent = agent
|
|
765
|
+
break
|
|
766
|
+
|
|
767
|
+
if existing_agent:
|
|
768
|
+
agent_name = existing_agent.get("name")
|
|
769
|
+
update_url = f"{base_endpoint}/v1alpha/{agent_name}"
|
|
770
|
+
|
|
771
|
+
console.print(f" Updating agent: {agent_name}")
|
|
772
|
+
|
|
773
|
+
# PATCH request to update
|
|
774
|
+
update_response = requests.patch(
|
|
775
|
+
update_url, headers=headers, json=payload, timeout=30
|
|
776
|
+
)
|
|
777
|
+
update_response.raise_for_status()
|
|
778
|
+
|
|
779
|
+
result = update_response.json()
|
|
780
|
+
console.print(
|
|
781
|
+
"\n✅ Successfully updated agent registration in Gemini Enterprise!"
|
|
782
|
+
)
|
|
783
|
+
console.print(f" Agent Name:\n {result.get('name', 'N/A')}")
|
|
784
|
+
return result
|
|
785
|
+
else:
|
|
786
|
+
console_err.print(
|
|
787
|
+
"❌ [red]Could not find existing agent to update[/]",
|
|
788
|
+
style="bold red",
|
|
789
|
+
)
|
|
790
|
+
raise
|
|
791
|
+
except (ValueError, KeyError) as e:
|
|
792
|
+
console_err.print(
|
|
793
|
+
f"Warning: Could not parse error response from API: {e}"
|
|
794
|
+
)
|
|
795
|
+
|
|
796
|
+
# If not an "already exists" error, or update failed, raise the original error
|
|
797
|
+
console_err.print(
|
|
798
|
+
f"\n❌ [red]HTTP error occurred: {http_err}[/]",
|
|
799
|
+
style="bold red",
|
|
800
|
+
)
|
|
801
|
+
console_err.print(f" Response: {response.text}")
|
|
802
|
+
raise
|
|
803
|
+
except requests.exceptions.RequestException as req_err:
|
|
804
|
+
console_err.print(
|
|
805
|
+
f"\n❌ [red]Request error occurred: {req_err}[/]",
|
|
806
|
+
style="bold red",
|
|
807
|
+
)
|
|
808
|
+
raise
|
|
809
|
+
|
|
810
|
+
|
|
811
|
+
@click.command()
|
|
812
|
+
@click.option(
|
|
813
|
+
"--agent-engine-id",
|
|
814
|
+
envvar="AGENT_ENGINE_ID",
|
|
815
|
+
help="Agent Engine resource name (e.g., projects/.../reasoningEngines/...). "
|
|
816
|
+
"If not provided, reads from deployment_metadata.json.",
|
|
817
|
+
)
|
|
818
|
+
@click.option(
|
|
819
|
+
"--metadata-file",
|
|
820
|
+
default="deployment_metadata.json",
|
|
821
|
+
help="Path to deployment metadata file (default: deployment_metadata.json).",
|
|
822
|
+
)
|
|
823
|
+
@click.option(
|
|
824
|
+
"--gemini-enterprise-app-id",
|
|
825
|
+
help="Gemini Enterprise app full resource name "
|
|
826
|
+
"(e.g., projects/{project_number}/locations/{location}/collections/{collection}/engines/{engine_id}). "
|
|
827
|
+
"If not provided, the command will prompt you interactively. "
|
|
828
|
+
"Can also be set via ID or GEMINI_ENTERPRISE_APP_ID env var.",
|
|
829
|
+
)
|
|
830
|
+
@click.option(
|
|
831
|
+
"--display-name",
|
|
832
|
+
envvar="GEMINI_DISPLAY_NAME",
|
|
833
|
+
help="Display name for the agent.",
|
|
834
|
+
)
|
|
835
|
+
@click.option(
|
|
836
|
+
"--description",
|
|
837
|
+
envvar="GEMINI_DESCRIPTION",
|
|
838
|
+
help="Description of the agent.",
|
|
839
|
+
)
|
|
840
|
+
@click.option(
|
|
841
|
+
"--tool-description",
|
|
842
|
+
envvar="GEMINI_TOOL_DESCRIPTION",
|
|
843
|
+
help="Description of what the tool does.",
|
|
844
|
+
)
|
|
845
|
+
@click.option(
|
|
846
|
+
"--project-id",
|
|
847
|
+
envvar="GOOGLE_CLOUD_PROJECT",
|
|
848
|
+
help="GCP project ID (extracted from agent-engine-id if not provided).",
|
|
849
|
+
)
|
|
850
|
+
@click.option(
|
|
851
|
+
"--authorization-id",
|
|
852
|
+
envvar="GEMINI_AUTHORIZATION_ID",
|
|
853
|
+
help="OAuth authorization resource name "
|
|
854
|
+
"(e.g., projects/{project_number}/locations/global/authorizations/{auth_id}).",
|
|
855
|
+
)
|
|
856
|
+
@click.option(
|
|
857
|
+
"--agent-card-url",
|
|
858
|
+
envvar="AGENT_CARD_URL",
|
|
859
|
+
help="URL to fetch the agent card for A2A agents "
|
|
860
|
+
"(e.g., https://your-service.run.app/a2a/app/.well-known/agent-card.json). "
|
|
861
|
+
"If provided, registers as an A2A agent instead of ADK agent.",
|
|
862
|
+
)
|
|
863
|
+
@click.option(
|
|
864
|
+
"--deployment-target",
|
|
865
|
+
envvar="DEPLOYMENT_TARGET",
|
|
866
|
+
type=click.Choice(["agent_engine", "cloud_run"], case_sensitive=False),
|
|
867
|
+
help="Deployment target (agent_engine or cloud_run).",
|
|
868
|
+
)
|
|
869
|
+
@click.option(
|
|
870
|
+
"--project-number",
|
|
871
|
+
envvar="PROJECT_NUMBER",
|
|
872
|
+
help="GCP project number. Used as default when prompting for Gemini Enterprise configuration.",
|
|
873
|
+
)
|
|
874
|
+
def register_gemini_enterprise(
|
|
875
|
+
agent_engine_id: str | None,
|
|
876
|
+
metadata_file: str,
|
|
877
|
+
gemini_enterprise_app_id: str | None,
|
|
878
|
+
display_name: str | None,
|
|
879
|
+
description: str | None,
|
|
880
|
+
tool_description: str | None,
|
|
881
|
+
project_id: str | None,
|
|
882
|
+
authorization_id: str | None,
|
|
883
|
+
agent_card_url: str | None,
|
|
884
|
+
deployment_target: str | None,
|
|
885
|
+
project_number: str | None,
|
|
886
|
+
) -> None:
|
|
887
|
+
"""Register an agent to Gemini Enterprise.
|
|
888
|
+
|
|
889
|
+
This command can run interactively or accept all parameters via command-line options.
|
|
890
|
+
If key parameters are missing, it will prompt the user for input.
|
|
891
|
+
"""
|
|
892
|
+
console.print("\n🤖 Agent → Gemini Enterprise Registration\n")
|
|
893
|
+
|
|
894
|
+
# Read metadata file once to determine agent type and deployment target
|
|
895
|
+
metadata = None
|
|
896
|
+
try:
|
|
897
|
+
metadata_path = Path(metadata_file)
|
|
898
|
+
if metadata_path.exists():
|
|
899
|
+
with open(metadata_path, encoding="utf-8") as f:
|
|
900
|
+
metadata = json.load(f)
|
|
901
|
+
except (json.JSONDecodeError, KeyError, FileNotFoundError):
|
|
902
|
+
pass
|
|
903
|
+
|
|
904
|
+
provided_agent_card_url = agent_card_url or os.getenv("AGENT_CARD_URL")
|
|
905
|
+
|
|
906
|
+
if not provided_agent_card_url:
|
|
907
|
+
if metadata:
|
|
908
|
+
is_a2a = metadata.get("is_a2a", False)
|
|
909
|
+
if is_a2a:
|
|
910
|
+
console.print("[blue]→ Detected A2A agent[/]")
|
|
911
|
+
agent_card_url = prompt_for_agent_card_url_with_auto_construct(
|
|
912
|
+
metadata, None
|
|
913
|
+
)
|
|
914
|
+
if not deployment_target:
|
|
915
|
+
deployment_target = metadata.get("deployment_target", "cloud_run")
|
|
916
|
+
else:
|
|
917
|
+
console.print("[blue]→ Detected ADK agent[/]")
|
|
918
|
+
agent_card_url = None
|
|
919
|
+
else:
|
|
920
|
+
agent_card_url = prompt_for_agent_card_url_with_auto_construct(None, None)
|
|
921
|
+
if not deployment_target:
|
|
922
|
+
deployment_target = "cloud_run"
|
|
923
|
+
else:
|
|
924
|
+
agent_card_url = prompt_for_agent_card_url_with_auto_construct(
|
|
925
|
+
metadata, provided_agent_card_url
|
|
926
|
+
)
|
|
927
|
+
if not deployment_target:
|
|
928
|
+
deployment_target = (
|
|
929
|
+
metadata.get("deployment_target", "cloud_run")
|
|
930
|
+
if metadata
|
|
931
|
+
else "cloud_run"
|
|
932
|
+
)
|
|
933
|
+
|
|
934
|
+
# A2A registration
|
|
935
|
+
if agent_card_url:
|
|
936
|
+
# Ensure deployment_target has a value (default to cloud_run if not set)
|
|
937
|
+
resolved_deployment_target = deployment_target or "cloud_run"
|
|
938
|
+
agent_card = fetch_agent_card_from_url(
|
|
939
|
+
agent_card_url, resolved_deployment_target
|
|
940
|
+
)
|
|
941
|
+
if not agent_card:
|
|
942
|
+
raise click.ClickException(
|
|
943
|
+
f"Failed to fetch agent card from {agent_card_url}. "
|
|
944
|
+
"Please verify the URL is correct and the agent is running."
|
|
945
|
+
)
|
|
946
|
+
|
|
947
|
+
console.print(f"✓ Fetched agent card: {agent_card.get('name', 'Unknown')}")
|
|
948
|
+
|
|
949
|
+
resolved_gemini_enterprise_app_id = (
|
|
950
|
+
gemini_enterprise_app_id
|
|
951
|
+
or os.getenv("ID")
|
|
952
|
+
or os.getenv("GEMINI_ENTERPRISE_APP_ID")
|
|
953
|
+
)
|
|
954
|
+
|
|
955
|
+
if not resolved_gemini_enterprise_app_id:
|
|
956
|
+
default_project = project_number
|
|
957
|
+
if (
|
|
958
|
+
not default_project
|
|
959
|
+
and metadata
|
|
960
|
+
and metadata.get("deployment_target") == "agent_engine"
|
|
961
|
+
):
|
|
962
|
+
remote_agent_engine_id = metadata.get("remote_agent_engine_id")
|
|
963
|
+
if remote_agent_engine_id:
|
|
964
|
+
parsed = parse_agent_engine_id(remote_agent_engine_id)
|
|
965
|
+
if parsed:
|
|
966
|
+
default_project = parsed["project"]
|
|
967
|
+
|
|
968
|
+
resolved_gemini_enterprise_app_id = prompt_for_gemini_enterprise_components(
|
|
969
|
+
default_project=default_project
|
|
970
|
+
)
|
|
971
|
+
|
|
972
|
+
# Get display name and description with smart defaults from agent card
|
|
973
|
+
if not display_name:
|
|
974
|
+
default_display_name = agent_card.get("name") or "My A2A Agent"
|
|
975
|
+
resolved_display_name = click.prompt(
|
|
976
|
+
"Display name", default=default_display_name
|
|
977
|
+
)
|
|
978
|
+
else:
|
|
979
|
+
resolved_display_name = display_name
|
|
980
|
+
|
|
981
|
+
if not description:
|
|
982
|
+
default_description = agent_card.get("description") or "AI Agent"
|
|
983
|
+
resolved_description = click.prompt(
|
|
984
|
+
"Description", default=default_description
|
|
985
|
+
)
|
|
986
|
+
else:
|
|
987
|
+
resolved_description = description
|
|
988
|
+
|
|
989
|
+
# For Cloud Run deployments, ensure Discovery Engine has invoker permissions
|
|
990
|
+
if resolved_deployment_target == "cloud_run":
|
|
991
|
+
parsed_ge = parse_gemini_enterprise_app_id(
|
|
992
|
+
resolved_gemini_enterprise_app_id
|
|
993
|
+
)
|
|
994
|
+
if parsed_ge and project_number:
|
|
995
|
+
ensure_discovery_engine_invoker_role(
|
|
996
|
+
project_id=project_id or parsed_ge["project_number"],
|
|
997
|
+
project_number=project_number,
|
|
998
|
+
)
|
|
999
|
+
|
|
1000
|
+
# Register as A2A agent
|
|
1001
|
+
try:
|
|
1002
|
+
register_a2a_agent(
|
|
1003
|
+
agent_card=agent_card,
|
|
1004
|
+
agent_card_url=agent_card_url,
|
|
1005
|
+
gemini_enterprise_app_id=resolved_gemini_enterprise_app_id,
|
|
1006
|
+
display_name=resolved_display_name,
|
|
1007
|
+
description=resolved_description,
|
|
1008
|
+
project_id=project_id,
|
|
1009
|
+
authorization_id=authorization_id,
|
|
1010
|
+
)
|
|
1011
|
+
except Exception as e:
|
|
1012
|
+
raise click.ClickException(f"Error during A2A registration: {e}") from e
|
|
1013
|
+
|
|
1014
|
+
# ADK
|
|
1015
|
+
else:
|
|
1016
|
+
# Step 1: Get Agent Engine ID
|
|
1017
|
+
resolved_agent_engine_id = agent_engine_id
|
|
1018
|
+
|
|
1019
|
+
if not resolved_agent_engine_id:
|
|
1020
|
+
env_id = os.getenv("AGENT_ENGINE_ID")
|
|
1021
|
+
if env_id:
|
|
1022
|
+
resolved_agent_engine_id = env_id
|
|
1023
|
+
else:
|
|
1024
|
+
metadata_id = (
|
|
1025
|
+
metadata.get("remote_agent_engine_id") if metadata else None
|
|
1026
|
+
)
|
|
1027
|
+
resolved_agent_engine_id = prompt_for_agent_engine_id(metadata_id)
|
|
1028
|
+
|
|
1029
|
+
# Validate and parse Agent Engine ID
|
|
1030
|
+
parsed_ae = parse_agent_engine_id(resolved_agent_engine_id)
|
|
1031
|
+
if not parsed_ae:
|
|
1032
|
+
raise click.ClickException(
|
|
1033
|
+
f"Invalid Agent Engine ID format: {resolved_agent_engine_id}\n"
|
|
1034
|
+
"Expected: projects/{{project}}/locations/{{location}}/reasoningEngines/{{id}}"
|
|
1035
|
+
)
|
|
1036
|
+
|
|
1037
|
+
# Step 2: Get Gemini Enterprise App ID
|
|
1038
|
+
resolved_gemini_enterprise_app_id = (
|
|
1039
|
+
gemini_enterprise_app_id
|
|
1040
|
+
or os.getenv("ID")
|
|
1041
|
+
or os.getenv("GEMINI_ENTERPRISE_APP_ID")
|
|
1042
|
+
)
|
|
1043
|
+
|
|
1044
|
+
if not resolved_gemini_enterprise_app_id:
|
|
1045
|
+
resolved_gemini_enterprise_app_id = prompt_for_gemini_enterprise_components(
|
|
1046
|
+
default_project=parsed_ae["project"]
|
|
1047
|
+
)
|
|
1048
|
+
|
|
1049
|
+
# Step 3: Get display name and description
|
|
1050
|
+
auto_display_name, auto_description = get_agent_engine_metadata(
|
|
1051
|
+
resolved_agent_engine_id
|
|
1052
|
+
)
|
|
1053
|
+
|
|
1054
|
+
resolved_display_name = display_name or auto_display_name or "My Agent"
|
|
1055
|
+
resolved_description = description or auto_description or "AI Agent"
|
|
1056
|
+
resolved_tool_description = tool_description or resolved_description
|
|
1057
|
+
|
|
1058
|
+
# Step 4: Register as ADK agent
|
|
1059
|
+
try:
|
|
1060
|
+
register_agent(
|
|
1061
|
+
agent_engine_id=resolved_agent_engine_id,
|
|
1062
|
+
gemini_enterprise_app_id=resolved_gemini_enterprise_app_id,
|
|
1063
|
+
display_name=resolved_display_name,
|
|
1064
|
+
description=resolved_description,
|
|
1065
|
+
tool_description=resolved_tool_description,
|
|
1066
|
+
project_id=project_id,
|
|
1067
|
+
authorization_id=authorization_id,
|
|
1068
|
+
)
|
|
1069
|
+
except Exception as e:
|
|
1070
|
+
raise click.ClickException(f"Error during ADK registration: {e}") from e
|