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,484 @@
|
|
|
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
|
+
{%- if cookiecutter.agent_name == "adk_live" %}
|
|
15
|
+
|
|
16
|
+
import asyncio
|
|
17
|
+
import json
|
|
18
|
+
import logging
|
|
19
|
+
import subprocess
|
|
20
|
+
import sys
|
|
21
|
+
import threading
|
|
22
|
+
import time
|
|
23
|
+
from collections.abc import Iterator
|
|
24
|
+
from typing import Any
|
|
25
|
+
|
|
26
|
+
import pytest
|
|
27
|
+
import requests
|
|
28
|
+
from websockets.asyncio.client import connect
|
|
29
|
+
|
|
30
|
+
# Configure logging
|
|
31
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
32
|
+
logger = logging.getLogger(__name__)
|
|
33
|
+
|
|
34
|
+
WS_URL = "ws://127.0.0.1:8000/ws"
|
|
35
|
+
FEEDBACK_URL = "http://127.0.0.1:8000/feedback"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def log_output(pipe: Any, log_func: Any) -> None:
|
|
39
|
+
"""Log the output from the given pipe."""
|
|
40
|
+
for line in iter(pipe.readline, ""):
|
|
41
|
+
log_func(line.strip())
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def start_server() -> subprocess.Popen[str]:
|
|
45
|
+
"""Start the server using expose_app in local mode."""
|
|
46
|
+
command = [
|
|
47
|
+
sys.executable,
|
|
48
|
+
"-m",
|
|
49
|
+
"uvicorn",
|
|
50
|
+
"app.app_utils.expose_app:app",
|
|
51
|
+
"--host",
|
|
52
|
+
"0.0.0.0",
|
|
53
|
+
"--port",
|
|
54
|
+
"8000",
|
|
55
|
+
]
|
|
56
|
+
process = subprocess.Popen(
|
|
57
|
+
command,
|
|
58
|
+
stdout=subprocess.PIPE,
|
|
59
|
+
stderr=subprocess.PIPE,
|
|
60
|
+
text=True,
|
|
61
|
+
bufsize=1,
|
|
62
|
+
encoding="utf-8",
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
# Start threads to log stdout and stderr in real-time
|
|
66
|
+
threading.Thread(
|
|
67
|
+
target=log_output, args=(process.stdout, logger.info), daemon=True
|
|
68
|
+
).start()
|
|
69
|
+
threading.Thread(
|
|
70
|
+
target=log_output, args=(process.stderr, logger.error), daemon=True
|
|
71
|
+
).start()
|
|
72
|
+
|
|
73
|
+
return process
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def wait_for_server(timeout: int = 60, interval: int = 1) -> bool:
|
|
77
|
+
"""Wait for the server to be ready."""
|
|
78
|
+
start_time = time.time()
|
|
79
|
+
while time.time() - start_time < timeout:
|
|
80
|
+
try:
|
|
81
|
+
response = requests.get("http://127.0.0.1:8000/docs", timeout=10)
|
|
82
|
+
if response.status_code == 200:
|
|
83
|
+
logger.info("Server is ready")
|
|
84
|
+
return True
|
|
85
|
+
except Exception:
|
|
86
|
+
pass
|
|
87
|
+
time.sleep(interval)
|
|
88
|
+
logger.error(f"Server did not become ready within {timeout} seconds")
|
|
89
|
+
return False
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@pytest.fixture(scope="module")
|
|
93
|
+
def server_fixture(request: Any) -> Iterator[subprocess.Popen[str]]:
|
|
94
|
+
"""Pytest fixture to start and stop the server for testing."""
|
|
95
|
+
logger.info("Starting server process")
|
|
96
|
+
server_process = start_server()
|
|
97
|
+
if not wait_for_server():
|
|
98
|
+
pytest.fail("Server failed to start")
|
|
99
|
+
logger.info("Server process started")
|
|
100
|
+
|
|
101
|
+
def stop_server() -> None:
|
|
102
|
+
logger.info("Stopping server process")
|
|
103
|
+
server_process.terminate()
|
|
104
|
+
try:
|
|
105
|
+
server_process.wait(timeout=5)
|
|
106
|
+
except subprocess.TimeoutExpired:
|
|
107
|
+
logger.warning("Server process did not terminate, killing it")
|
|
108
|
+
server_process.kill()
|
|
109
|
+
server_process.wait()
|
|
110
|
+
logger.info("Server process stopped")
|
|
111
|
+
|
|
112
|
+
request.addfinalizer(stop_server)
|
|
113
|
+
yield server_process
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@pytest.mark.asyncio
|
|
117
|
+
async def test_websocket_audio_input(server_fixture: subprocess.Popen[str]) -> None:
|
|
118
|
+
"""Test websocket with audio input in local mode."""
|
|
119
|
+
|
|
120
|
+
async def send_message(websocket: Any, message: dict[str, Any]) -> None:
|
|
121
|
+
"""Helper to send JSON messages."""
|
|
122
|
+
await websocket.send(json.dumps(message))
|
|
123
|
+
|
|
124
|
+
async def receive_message(websocket: Any, timeout: float = 5.0) -> dict[str, Any]:
|
|
125
|
+
"""Helper to receive messages with timeout."""
|
|
126
|
+
try:
|
|
127
|
+
response = await asyncio.wait_for(websocket.recv(), timeout=timeout)
|
|
128
|
+
if isinstance(response, bytes):
|
|
129
|
+
return json.loads(response.decode())
|
|
130
|
+
if isinstance(response, str):
|
|
131
|
+
return json.loads(response)
|
|
132
|
+
return response
|
|
133
|
+
except asyncio.TimeoutError as exc:
|
|
134
|
+
raise TimeoutError(
|
|
135
|
+
f"No response received within {timeout} seconds"
|
|
136
|
+
) from exc
|
|
137
|
+
|
|
138
|
+
try:
|
|
139
|
+
await asyncio.sleep(2)
|
|
140
|
+
|
|
141
|
+
async with connect(WS_URL, ping_timeout=10, close_timeout=10) as websocket:
|
|
142
|
+
try:
|
|
143
|
+
# Wait for setupComplete
|
|
144
|
+
setup_response = await receive_message(websocket, timeout=10.0)
|
|
145
|
+
assert "setupComplete" in setup_response
|
|
146
|
+
logger.info("Received setupComplete")
|
|
147
|
+
|
|
148
|
+
# Send dummy audio chunk with user_id
|
|
149
|
+
dummy_audio = bytes([0] * 1024)
|
|
150
|
+
audio_msg = {
|
|
151
|
+
"user_id": "test-user",
|
|
152
|
+
"realtimeInput": {
|
|
153
|
+
"mediaChunks": [
|
|
154
|
+
{
|
|
155
|
+
"mimeType": "audio/pcm;rate=16000",
|
|
156
|
+
"data": dummy_audio.hex(),
|
|
157
|
+
}
|
|
158
|
+
]
|
|
159
|
+
},
|
|
160
|
+
}
|
|
161
|
+
await send_message(websocket, audio_msg)
|
|
162
|
+
logger.info("Sent audio chunk")
|
|
163
|
+
|
|
164
|
+
# Send text message to complete the turn (matching frontend format)
|
|
165
|
+
text_msg = {
|
|
166
|
+
"content": {
|
|
167
|
+
"role": "user",
|
|
168
|
+
"parts": [{"text": "Test audio"}],
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
await send_message(websocket, text_msg)
|
|
172
|
+
logger.info("Sent text completion")
|
|
173
|
+
|
|
174
|
+
# Collect responses
|
|
175
|
+
responses = []
|
|
176
|
+
for _ in range(10):
|
|
177
|
+
try:
|
|
178
|
+
response = await receive_message(websocket, timeout=5.0)
|
|
179
|
+
responses.append(response)
|
|
180
|
+
logger.info(f"Received: {response}")
|
|
181
|
+
|
|
182
|
+
if isinstance(response, dict) and response.get("turn_complete"):
|
|
183
|
+
break
|
|
184
|
+
except TimeoutError:
|
|
185
|
+
break
|
|
186
|
+
|
|
187
|
+
# Verify we got responses
|
|
188
|
+
assert len(responses) > 0, "No responses received"
|
|
189
|
+
|
|
190
|
+
logger.info(f"Audio test passed. Received {len(responses)} responses")
|
|
191
|
+
|
|
192
|
+
finally:
|
|
193
|
+
await websocket.close()
|
|
194
|
+
|
|
195
|
+
except Exception as e:
|
|
196
|
+
logger.error(f"Audio test failed: {e}")
|
|
197
|
+
raise
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def test_feedback_endpoint(server_fixture: subprocess.Popen[str]) -> None:
|
|
201
|
+
"""Test the feedback endpoint."""
|
|
202
|
+
feedback_data = {
|
|
203
|
+
"score": 5,
|
|
204
|
+
"text": "Great response!",
|
|
205
|
+
"user_id": "test-user-123",
|
|
206
|
+
"session_id": "test-session-123",
|
|
207
|
+
"log_type": "feedback",
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
response = requests.post(FEEDBACK_URL, json=feedback_data, timeout=10)
|
|
211
|
+
assert response.status_code == 200
|
|
212
|
+
assert response.json() == {"status": "success"}
|
|
213
|
+
logger.info("Feedback endpoint test passed")
|
|
214
|
+
{% else %}
|
|
215
|
+
|
|
216
|
+
# mypy: disable-error-code="arg-type"
|
|
217
|
+
{%- if cookiecutter.is_a2a %}
|
|
218
|
+
|
|
219
|
+
import os
|
|
220
|
+
|
|
221
|
+
import pytest
|
|
222
|
+
|
|
223
|
+
from {{cookiecutter.agent_directory}}.agent_engine_app import AgentEngineApp
|
|
224
|
+
from tests.helpers import (
|
|
225
|
+
build_get_request,
|
|
226
|
+
build_post_request,
|
|
227
|
+
poll_task_completion,
|
|
228
|
+
)
|
|
229
|
+
{%- elif cookiecutter.is_adk %}
|
|
230
|
+
|
|
231
|
+
import logging
|
|
232
|
+
|
|
233
|
+
import pytest
|
|
234
|
+
from google.adk.events.event import Event
|
|
235
|
+
|
|
236
|
+
from {{cookiecutter.agent_directory}}.agent_engine_app import AgentEngineApp
|
|
237
|
+
{%- else %}
|
|
238
|
+
|
|
239
|
+
import logging
|
|
240
|
+
|
|
241
|
+
import pytest
|
|
242
|
+
|
|
243
|
+
from {{cookiecutter.agent_directory}}.agent_engine_app import AgentEngineApp
|
|
244
|
+
{%- endif %}
|
|
245
|
+
{%- if cookiecutter.is_a2a %}
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
@pytest.fixture
|
|
249
|
+
def agent_app() -> AgentEngineApp:
|
|
250
|
+
"""Fixture to create and set up AgentEngineApp instance"""
|
|
251
|
+
from {{cookiecutter.agent_directory}}.agent_engine_app import agent_engine
|
|
252
|
+
|
|
253
|
+
agent_engine.set_up()
|
|
254
|
+
return agent_engine
|
|
255
|
+
{%- else %}
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
@pytest.fixture
|
|
259
|
+
def agent_app() -> AgentEngineApp:
|
|
260
|
+
"""Fixture to create and set up AgentEngineApp instance"""
|
|
261
|
+
from {{cookiecutter.agent_directory}}.agent_engine_app import agent_engine
|
|
262
|
+
|
|
263
|
+
agent_engine.set_up()
|
|
264
|
+
return agent_engine
|
|
265
|
+
{% endif %}
|
|
266
|
+
{%- if cookiecutter.is_a2a %}
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
@pytest.mark.asyncio
|
|
270
|
+
async def test_agent_on_message_send(agent_app: AgentEngineApp) -> None:
|
|
271
|
+
"""Test complete A2A message workflow from send to task completion with artifacts."""
|
|
272
|
+
# Send message
|
|
273
|
+
message_data = {
|
|
274
|
+
"message": {
|
|
275
|
+
"messageId": f"msg-{os.urandom(8).hex()}",
|
|
276
|
+
"content": [{"text": "What is the capital of France?"}],
|
|
277
|
+
"role": "ROLE_USER",
|
|
278
|
+
},
|
|
279
|
+
}
|
|
280
|
+
response = await agent_app.on_message_send(
|
|
281
|
+
request=build_post_request(message_data),
|
|
282
|
+
context=None,
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
# Verify task creation
|
|
286
|
+
assert "task" in response and "id" in response["task"], (
|
|
287
|
+
"Expected task with ID in response"
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
# Poll for completion
|
|
291
|
+
final_response = await poll_task_completion(agent_app, response["task"]["id"])
|
|
292
|
+
|
|
293
|
+
# Verify artifacts
|
|
294
|
+
assert final_response.get("artifacts"), "Expected artifacts in completed task"
|
|
295
|
+
artifact = final_response["artifacts"][0]
|
|
296
|
+
assert artifact.get("parts") and artifact["parts"][0].get("text"), (
|
|
297
|
+
"Expected artifact with text content"
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
@pytest.mark.asyncio
|
|
302
|
+
async def test_agent_card(agent_app: AgentEngineApp) -> None:
|
|
303
|
+
"""Test agent card retrieval and validation of required A2A fields."""
|
|
304
|
+
response = await agent_app.handle_authenticated_agent_card(
|
|
305
|
+
request=build_get_request(None),
|
|
306
|
+
context=None,
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
# Verify core agent card fields
|
|
310
|
+
assert response.get("name") == "root_agent", "Expected agent name 'root_agent'"
|
|
311
|
+
assert response.get("protocolVersion") == "0.3.0", "Expected protocol version 0.3.0"
|
|
312
|
+
assert response.get("preferredTransport") == "HTTP+JSON", (
|
|
313
|
+
"Expected HTTP+JSON transport"
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
# Verify capabilities
|
|
317
|
+
capabilities = response.get("capabilities", {})
|
|
318
|
+
assert capabilities.get("streaming") is False, "Expected streaming disabled"
|
|
319
|
+
|
|
320
|
+
# Verify skills
|
|
321
|
+
skills = response.get("skills", [])
|
|
322
|
+
assert len(skills) > 0, "Expected at least one skill"
|
|
323
|
+
for skill in skills:
|
|
324
|
+
assert all(key in skill for key in ["id", "name", "description"]), (
|
|
325
|
+
"Expected id, name, and description in each skill"
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
# Verify extended card support
|
|
329
|
+
assert response.get("supportsAuthenticatedExtendedCard") is True, (
|
|
330
|
+
"Expected supportsAuthenticatedExtendedCard to be True"
|
|
331
|
+
)
|
|
332
|
+
{% elif cookiecutter.is_adk %}
|
|
333
|
+
|
|
334
|
+
@pytest.mark.asyncio
|
|
335
|
+
async def test_agent_stream_query(agent_app: AgentEngineApp) -> None:
|
|
336
|
+
"""
|
|
337
|
+
Integration test for the agent stream query functionality.
|
|
338
|
+
Tests that the agent returns valid streaming responses.
|
|
339
|
+
"""
|
|
340
|
+
# Create message and events for the async_stream_query
|
|
341
|
+
message = "What's the weather in San Francisco?"
|
|
342
|
+
events = []
|
|
343
|
+
async for event in agent_app.async_stream_query(message=message, user_id="test"):
|
|
344
|
+
events.append(event)
|
|
345
|
+
assert len(events) > 0, "Expected at least one chunk in response"
|
|
346
|
+
|
|
347
|
+
# Check for valid content in the response
|
|
348
|
+
has_text_content = False
|
|
349
|
+
for event in events:
|
|
350
|
+
validated_event = Event.model_validate(event)
|
|
351
|
+
content = validated_event.content
|
|
352
|
+
if (
|
|
353
|
+
content is not None
|
|
354
|
+
and content.parts
|
|
355
|
+
and any(part.text for part in content.parts)
|
|
356
|
+
):
|
|
357
|
+
has_text_content = True
|
|
358
|
+
break
|
|
359
|
+
|
|
360
|
+
assert has_text_content, "Expected at least one event with text content"
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def test_agent_feedback(agent_app: AgentEngineApp) -> None:
|
|
364
|
+
"""
|
|
365
|
+
Integration test for the agent feedback functionality.
|
|
366
|
+
Tests that feedback can be registered successfully.
|
|
367
|
+
"""
|
|
368
|
+
feedback_data = {
|
|
369
|
+
"score": 5,
|
|
370
|
+
"text": "Great response!",
|
|
371
|
+
"user_id": "test-user-456",
|
|
372
|
+
"session_id": "test-session-456",
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
# Should not raise any exceptions
|
|
376
|
+
agent_app.register_feedback(feedback_data)
|
|
377
|
+
|
|
378
|
+
# Test invalid feedback
|
|
379
|
+
with pytest.raises(ValueError):
|
|
380
|
+
invalid_feedback = {
|
|
381
|
+
"score": "invalid", # Score must be numeric
|
|
382
|
+
"text": "Bad feedback",
|
|
383
|
+
"user_id": "test-user-789",
|
|
384
|
+
"session_id": "test-session-789",
|
|
385
|
+
}
|
|
386
|
+
agent_app.register_feedback(invalid_feedback)
|
|
387
|
+
|
|
388
|
+
logging.info("All assertions passed for agent feedback test")
|
|
389
|
+
{% else %}
|
|
390
|
+
|
|
391
|
+
def test_agent_stream_query(agent_app: AgentEngineApp) -> None:
|
|
392
|
+
"""
|
|
393
|
+
Integration test for the agent stream query functionality.
|
|
394
|
+
Tests that the agent returns valid streaming responses.
|
|
395
|
+
"""
|
|
396
|
+
input_dict = {
|
|
397
|
+
"messages": [
|
|
398
|
+
{"type": "human", "content": "Test message"},
|
|
399
|
+
],
|
|
400
|
+
"user_id": "test-user",
|
|
401
|
+
"session_id": "test-session",
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
events = list(agent_app.stream_query(input=input_dict))
|
|
405
|
+
|
|
406
|
+
assert len(events) > 0, "Expected at least one chunk in response"
|
|
407
|
+
|
|
408
|
+
# Verify each event is a tuple of message and metadata
|
|
409
|
+
for event in events:
|
|
410
|
+
assert isinstance(event, list), "Event should be a list"
|
|
411
|
+
assert len(event) == 2, "Event should contain message and metadata"
|
|
412
|
+
message, _ = event
|
|
413
|
+
|
|
414
|
+
# Verify message structure
|
|
415
|
+
assert isinstance(message, dict), "Message should be a dictionary"
|
|
416
|
+
assert message["type"] == "constructor"
|
|
417
|
+
assert "kwargs" in message, "Constructor message should have kwargs"
|
|
418
|
+
|
|
419
|
+
# Verify at least one message has content
|
|
420
|
+
has_content = False
|
|
421
|
+
for event in events:
|
|
422
|
+
message = event[0]
|
|
423
|
+
if message.get("type") == "constructor" and "content" in message["kwargs"]:
|
|
424
|
+
has_content = True
|
|
425
|
+
break
|
|
426
|
+
assert has_content, "At least one message should have content"
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
def test_agent_query(agent_app: AgentEngineApp) -> None:
|
|
430
|
+
"""
|
|
431
|
+
Integration test for the agent query functionality.
|
|
432
|
+
Tests that the agent returns valid responses.
|
|
433
|
+
"""
|
|
434
|
+
input_dict = {
|
|
435
|
+
"messages": [
|
|
436
|
+
{"type": "human", "content": "Test message"},
|
|
437
|
+
],
|
|
438
|
+
"user_id": "test-user",
|
|
439
|
+
"session_id": "test-session",
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
response = agent_app.query(input=input_dict)
|
|
443
|
+
|
|
444
|
+
# Basic response validation
|
|
445
|
+
assert isinstance(response, dict), "Response should be a dictionary"
|
|
446
|
+
assert "messages" in response, "Response should contain messages"
|
|
447
|
+
assert len(response["messages"]) > 0, "Response should have at least one message"
|
|
448
|
+
|
|
449
|
+
# Validate last message is AI response with content
|
|
450
|
+
message = response["messages"][-1]
|
|
451
|
+
kwargs = message["kwargs"]
|
|
452
|
+
assert kwargs["type"] == "ai", "Last message should be AI response"
|
|
453
|
+
assert len(kwargs["content"]) > 0, "AI message content should not be empty"
|
|
454
|
+
|
|
455
|
+
logging.info("All assertions passed for agent query test")
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def test_agent_feedback(agent_app: AgentEngineApp) -> None:
|
|
459
|
+
"""
|
|
460
|
+
Integration test for the agent feedback functionality.
|
|
461
|
+
Tests that feedback can be registered successfully.
|
|
462
|
+
"""
|
|
463
|
+
feedback_data = {
|
|
464
|
+
"score": 5,
|
|
465
|
+
"text": "Great response!",
|
|
466
|
+
"user_id": "test-user-456",
|
|
467
|
+
"session_id": "test-session-456",
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
# Should not raise any exceptions
|
|
471
|
+
agent_app.register_feedback(feedback_data)
|
|
472
|
+
|
|
473
|
+
# Test invalid feedback
|
|
474
|
+
with pytest.raises(ValueError):
|
|
475
|
+
invalid_feedback = {
|
|
476
|
+
"score": "invalid", # Score must be numeric
|
|
477
|
+
"text": "Bad feedback",
|
|
478
|
+
"user_id": "test-user-789",
|
|
479
|
+
"session_id": "test-session-789",
|
|
480
|
+
}
|
|
481
|
+
agent_app.register_feedback(invalid_feedback)
|
|
482
|
+
|
|
483
|
+
logging.info("All assertions passed for agent feedback test")
|
|
484
|
+
{% endif %}{% endif %}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{%- if cookiecutter.agent_name == "adk_live" %}
|
|
2
|
+
# WebSocket Load Testing for Remote Agent Engine
|
|
3
|
+
|
|
4
|
+
This directory provides a comprehensive load testing framework for your Agent Engine application using WebSocket connections, leveraging the power of [Locust](http://locust.io), a leading open-source load testing tool.
|
|
5
|
+
|
|
6
|
+
The load test simulates realistic user interactions by:
|
|
7
|
+
- Establishing WebSocket connections
|
|
8
|
+
- Sending audio chunks in the proper `realtimeInput` format
|
|
9
|
+
- Sending text messages to complete turns
|
|
10
|
+
- Collecting and measuring responses until `turn_complete`
|
|
11
|
+
|
|
12
|
+
## Load Testing with Remote Agent Engine
|
|
13
|
+
|
|
14
|
+
**1. Start the Expose App in Remote Mode:**
|
|
15
|
+
|
|
16
|
+
Launch the expose app server in a separate terminal, pointing to your deployed agent engine:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
uv run python -m app.app_utils.expose_app --mode remote --remote-id <your-agent-engine-id>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Or if you have `deployment_metadata.json` in your project root:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
uv run python -m app.app_utils.expose_app --mode remote
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**2. Execute the Load Test:**
|
|
29
|
+
|
|
30
|
+
Using another terminal tab, trigger the Locust load test:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
uv run --with locust==2.31.1 --with websockets locust -f tests/load_test/load_test.py \
|
|
34
|
+
-H http://127.0.0.1:8000 \
|
|
35
|
+
--headless \
|
|
36
|
+
-t 30s -u 1 -r 1 \
|
|
37
|
+
--csv=tests/load_test/.results/results \
|
|
38
|
+
--html=tests/load_test/.results/report.html
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
This command initiates a 30-second load test with 1 concurrent user.
|
|
42
|
+
|
|
43
|
+
**Results:**
|
|
44
|
+
|
|
45
|
+
Comprehensive CSV and HTML reports detailing the load test performance will be generated and saved in the `tests/load_test/.results` directory.
|
|
46
|
+
{%- else %}
|
|
47
|
+
# Robust Load Testing for Generative AI Applications
|
|
48
|
+
|
|
49
|
+
This directory provides a comprehensive load testing framework for your Generative AI application, leveraging the power of [Locust](http://locust.io), a leading open-source load testing tool.
|
|
50
|
+
|
|
51
|
+
## Load Testing
|
|
52
|
+
|
|
53
|
+
Before running load tests, ensure you have deployed the backend remotely.
|
|
54
|
+
|
|
55
|
+
Follow these steps to execute load tests:
|
|
56
|
+
|
|
57
|
+
**1. Deploy the Backend Remotely:**
|
|
58
|
+
```bash
|
|
59
|
+
gcloud config set project <your-dev-project-id>
|
|
60
|
+
make deploy
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**2. Create a Virtual Environment for Locust:**
|
|
64
|
+
It's recommended to use a separate terminal tab and create a virtual environment for Locust to avoid conflicts with your application's Python environment.
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
python3 -m venv .locust_env && source .locust_env/bin/activate && pip install locust==2.31.1
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**3. Execute the Load Test:**
|
|
71
|
+
Trigger the Locust load test with the following command:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
export _AUTH_TOKEN=$(gcloud auth print-access-token -q)
|
|
75
|
+
locust -f tests/load_test/load_test.py \
|
|
76
|
+
--headless \
|
|
77
|
+
-t 30s -u 5 -r 2 \
|
|
78
|
+
--csv=tests/load_test/.results/results \
|
|
79
|
+
--html=tests/load_test/.results/report.html
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
This command initiates a 30-second load test, simulating 2 users spawning per second, reaching a maximum of 10 concurrent users.
|
|
83
|
+
{%- endif %}
|
|
84
|
+
|