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.
Files changed (255) hide show
  1. agent_starter_pack/agents/README.md +7 -0
  2. agents/langgraph_base_react/template/.templateconfig.yaml → agent_starter_pack/agents/adk_a2a_base/.template/templateconfig.yaml +5 -10
  3. agent_starter_pack/agents/adk_a2a_base/README.md +37 -0
  4. src/deployment_targets/cloud_run/Dockerfile → agent_starter_pack/agents/adk_a2a_base/app/__init__.py +2 -14
  5. agent_starter_pack/agents/adk_a2a_base/app/agent.py +70 -0
  6. agent_starter_pack/agents/adk_a2a_base/notebooks/adk_a2a_app_testing.ipynb +583 -0
  7. agents/crewai_coding_crew/notebooks/evaluating_crewai_agent.ipynb → agent_starter_pack/agents/adk_a2a_base/notebooks/evaluating_adk_agent.ipynb +163 -199
  8. {agents/adk_base → agent_starter_pack/agents/adk_a2a_base}/tests/integration/test_agent.py +2 -2
  9. agents/adk_base/template/.templateconfig.yaml → agent_starter_pack/agents/adk_base/.template/templateconfig.yaml +4 -1
  10. {agents → agent_starter_pack/agents}/adk_base/README.md +1 -1
  11. agent_starter_pack/agents/adk_base/app/__init__.py +17 -0
  12. {agents → agent_starter_pack/agents}/adk_base/app/agent.py +5 -2
  13. {agents → agent_starter_pack/agents}/adk_base/notebooks/adk_app_testing.ipynb +128 -82
  14. agents/langgraph_base_react/notebooks/evaluating_langgraph_agent.ipynb → agent_starter_pack/agents/adk_base/notebooks/evaluating_adk_agent.ipynb +181 -207
  15. agent_starter_pack/agents/adk_base/tests/integration/test_agent.py +58 -0
  16. agents/crewai_coding_crew/template/.templateconfig.yaml → agent_starter_pack/agents/adk_live/.template/templateconfig.yaml +5 -9
  17. agent_starter_pack/agents/adk_live/README.md +32 -0
  18. agent_starter_pack/agents/adk_live/app/__init__.py +17 -0
  19. agent_starter_pack/agents/adk_live/app/agent.py +51 -0
  20. agent_starter_pack/agents/adk_live/tests/unit/test_dummy.py +38 -0
  21. agents/agentic_rag/template/.templateconfig.yaml → agent_starter_pack/agents/agentic_rag/.template/templateconfig.yaml +7 -3
  22. {agents → agent_starter_pack/agents}/agentic_rag/README.md +1 -1
  23. agent_starter_pack/agents/agentic_rag/app/__init__.py +17 -0
  24. {agents → agent_starter_pack/agents}/agentic_rag/app/agent.py +8 -4
  25. {agents → agent_starter_pack/agents}/agentic_rag/notebooks/adk_app_testing.ipynb +128 -82
  26. agent_starter_pack/agents/agentic_rag/notebooks/evaluating_adk_agent.ipynb +1535 -0
  27. {agents → agent_starter_pack/agents}/agentic_rag/tests/integration/test_agent.py +3 -3
  28. agent_starter_pack/agents/langgraph_base/.template/templateconfig.yaml +31 -0
  29. agent_starter_pack/agents/langgraph_base/README.md +30 -0
  30. agent_starter_pack/agents/langgraph_base/app/__init__.py +17 -0
  31. agent_starter_pack/agents/langgraph_base/app/agent.py +34 -0
  32. {agents/crewai_coding_crew → agent_starter_pack/agents/langgraph_base}/notebooks/evaluating_langgraph_agent.ipynb +30 -17
  33. {agents/langgraph_base_react → agent_starter_pack/agents/langgraph_base}/tests/integration/test_agent.py +2 -2
  34. {src → agent_starter_pack}/base_template/.gitignore +5 -3
  35. agent_starter_pack/base_template/GEMINI.md +5 -0
  36. agent_starter_pack/base_template/Makefile +339 -0
  37. agent_starter_pack/base_template/README.md +267 -0
  38. agent_starter_pack/base_template/deployment/README.md +11 -0
  39. {src → agent_starter_pack}/base_template/deployment/terraform/apis.tf +2 -2
  40. {src → agent_starter_pack}/base_template/deployment/terraform/dev/apis.tf +6 -1
  41. {src → agent_starter_pack}/base_template/deployment/terraform/dev/iam.tf +12 -11
  42. {src → agent_starter_pack}/base_template/deployment/terraform/dev/providers.tf +5 -1
  43. {src → agent_starter_pack}/base_template/deployment/terraform/dev/storage.tf +1 -1
  44. {src → agent_starter_pack}/base_template/deployment/terraform/dev/variables.tf +10 -10
  45. agent_starter_pack/base_template/deployment/terraform/dev/{% if cookiecutter.is_adk %}telemetry.tf{% else %}unused_telemetry.tf{% endif %} +193 -0
  46. agent_starter_pack/base_template/deployment/terraform/github.tf +337 -0
  47. {src → agent_starter_pack}/base_template/deployment/terraform/iam.tf +20 -41
  48. {src → agent_starter_pack}/base_template/deployment/terraform/locals.tf +10 -3
  49. {src/resources/setup_cicd → agent_starter_pack/base_template/deployment/terraform}/providers.tf +8 -1
  50. {src → agent_starter_pack}/base_template/deployment/terraform/service_accounts.tf +7 -8
  51. agent_starter_pack/base_template/deployment/terraform/sql/completions.sql +138 -0
  52. {src → agent_starter_pack}/base_template/deployment/terraform/storage.tf +7 -16
  53. {src → agent_starter_pack}/base_template/deployment/terraform/variables.tf +61 -28
  54. {src → agent_starter_pack}/base_template/deployment/terraform/vars/env.tfvars +6 -1
  55. agent_starter_pack/base_template/deployment/terraform/{% if cookiecutter.cicd_runner == 'github_actions' %}wif.tf{% else %}unused_wif.tf{% endif %} +43 -0
  56. 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
  57. agent_starter_pack/base_template/deployment/terraform/{% if cookiecutter.is_adk %}telemetry.tf{% else %}unused_telemetry.tf{% endif %} +206 -0
  58. {src → agent_starter_pack}/base_template/pyproject.toml +24 -37
  59. agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/deploy-to-prod.yaml +132 -0
  60. agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/pr_checks.yaml +65 -0
  61. agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/staging.yaml +259 -0
  62. 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
  63. 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
  64. agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/staging.yaml +322 -0
  65. agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils/telemetry.py +96 -0
  66. {src/base_template/app/utils → agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils}/typing.py +7 -9
  67. 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
  68. 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
  69. 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
  70. 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
  71. 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
  72. agent_starter_pack/cli/commands/create.py +1256 -0
  73. agent_starter_pack/cli/commands/enhance.py +611 -0
  74. agent_starter_pack/cli/commands/list.py +203 -0
  75. agent_starter_pack/cli/commands/register_gemini_enterprise.py +1070 -0
  76. agent_starter_pack/cli/commands/setup_cicd.py +862 -0
  77. {src → agent_starter_pack}/cli/main.py +10 -2
  78. {src → agent_starter_pack}/cli/utils/cicd.py +139 -48
  79. agent_starter_pack/cli/utils/gcp.py +263 -0
  80. agent_starter_pack/cli/utils/logging.py +103 -0
  81. agent_starter_pack/cli/utils/remote_template.py +677 -0
  82. agent_starter_pack/cli/utils/template.py +1466 -0
  83. {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/components/process_data.py +1 -1
  84. {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/submit_pipeline.py +20 -6
  85. {src → agent_starter_pack}/data_ingestion/pyproject.toml +1 -0
  86. {src → agent_starter_pack}/data_ingestion/uv.lock +602 -494
  87. agent_starter_pack/deployment_targets/agent_engine/tests/integration/test_agent_engine_app.py +484 -0
  88. agent_starter_pack/deployment_targets/agent_engine/tests/load_test/README.md +84 -0
  89. agent_starter_pack/deployment_targets/agent_engine/tests/load_test/load_test.py +424 -0
  90. agent_starter_pack/deployment_targets/agent_engine/tests/{% if cookiecutter.is_a2a %}helpers.py{% else %}unused_helpers.py{% endif %} +138 -0
  91. agent_starter_pack/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/agent_engine_app.py +263 -0
  92. agent_starter_pack/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/app_utils/deploy.py +414 -0
  93. 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
  94. agent_starter_pack/deployment_targets/cloud_run/Dockerfile +51 -0
  95. agent_starter_pack/deployment_targets/cloud_run/deployment/terraform/dev/service.tf +243 -0
  96. agent_starter_pack/deployment_targets/cloud_run/deployment/terraform/service.tf +417 -0
  97. agent_starter_pack/deployment_targets/cloud_run/tests/integration/test_server_e2e.py +705 -0
  98. agent_starter_pack/deployment_targets/cloud_run/tests/load_test/.results/.placeholder +321 -0
  99. agent_starter_pack/deployment_targets/cloud_run/tests/load_test/README.md +165 -0
  100. agent_starter_pack/deployment_targets/cloud_run/tests/load_test/load_test.py +329 -0
  101. agent_starter_pack/deployment_targets/cloud_run/{{cookiecutter.agent_directory}}/fast_api_app.py +556 -0
  102. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/package-lock.json +79 -1044
  103. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/package.json +1 -9
  104. agent_starter_pack/frontends/adk_live_react/frontend/src/App.tsx +65 -0
  105. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/logger/Logger.tsx +8 -3
  106. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/logger/logger.scss +26 -0
  107. agent_starter_pack/frontends/adk_live_react/frontend/src/components/side-panel/SidePanel.tsx +516 -0
  108. agent_starter_pack/frontends/adk_live_react/frontend/src/components/side-panel/side-panel.scss +563 -0
  109. agent_starter_pack/frontends/adk_live_react/frontend/src/components/transcription-preview/TranscriptionPreview.tsx +106 -0
  110. agent_starter_pack/frontends/adk_live_react/frontend/src/components/transcription-preview/transcription-preview.scss +150 -0
  111. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-live-api.ts +8 -2
  112. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/multimodal-live-types.ts +40 -2
  113. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/audio-recorder.ts +1 -1
  114. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/audio-streamer.ts +1 -1
  115. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/multimodal-live-client.ts +210 -24
  116. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/utils.ts +27 -5
  117. agent_starter_pack/resources/docs/adk-cheatsheet.md +1628 -0
  118. agent_starter_pack/resources/locks/uv-adk_a2a_base-agent_engine.lock +4966 -0
  119. agent_starter_pack/resources/locks/uv-adk_a2a_base-cloud_run.lock +5011 -0
  120. agent_starter_pack/resources/locks/uv-adk_base-agent_engine.lock +4946 -0
  121. agent_starter_pack/resources/locks/uv-adk_base-cloud_run.lock +4991 -0
  122. agent_starter_pack/resources/locks/uv-adk_live-agent_engine.lock +4963 -0
  123. agent_starter_pack/resources/locks/uv-adk_live-cloud_run.lock +5006 -0
  124. agent_starter_pack/resources/locks/uv-agentic_rag-agent_engine.lock +5487 -0
  125. agent_starter_pack/resources/locks/uv-agentic_rag-cloud_run.lock +5532 -0
  126. agent_starter_pack/resources/locks/uv-langgraph_base-agent_engine.lock +5788 -0
  127. agent_starter_pack/resources/locks/uv-langgraph_base-cloud_run.lock +5811 -0
  128. {src → agent_starter_pack}/utils/generate_locks.py +15 -12
  129. {src → agent_starter_pack}/utils/lock_utils.py +4 -7
  130. {src → agent_starter_pack}/utils/watch_and_rebuild.py +2 -2
  131. agent_starter_pack-0.21.0.dist-info/METADATA +182 -0
  132. agent_starter_pack-0.21.0.dist-info/RECORD +171 -0
  133. agent_starter_pack-0.21.0.dist-info/entry_points.txt +2 -0
  134. llm.txt +362 -0
  135. agent_starter_pack-0.3.3.dist-info/METADATA +0 -164
  136. agent_starter_pack-0.3.3.dist-info/RECORD +0 -176
  137. agent_starter_pack-0.3.3.dist-info/entry_points.txt +0 -2
  138. agents/crewai_coding_crew/README.md +0 -34
  139. agents/crewai_coding_crew/app/agent.py +0 -86
  140. agents/crewai_coding_crew/app/crew/config/agents.yaml +0 -39
  141. agents/crewai_coding_crew/app/crew/config/tasks.yaml +0 -37
  142. agents/crewai_coding_crew/app/crew/crew.py +0 -71
  143. agents/crewai_coding_crew/tests/integration/test_agent.py +0 -47
  144. agents/langgraph_base_react/README.md +0 -9
  145. agents/langgraph_base_react/app/agent.py +0 -73
  146. agents/live_api/README.md +0 -37
  147. agents/live_api/app/agent.py +0 -78
  148. agents/live_api/app/server.py +0 -196
  149. agents/live_api/app/templates.py +0 -51
  150. agents/live_api/app/vector_store.py +0 -55
  151. agents/live_api/template/.templateconfig.yaml +0 -29
  152. agents/live_api/tests/integration/test_server_e2e.py +0 -254
  153. agents/live_api/tests/load_test/load_test.py +0 -40
  154. agents/live_api/tests/unit/test_server.py +0 -143
  155. src/base_template/Makefile +0 -72
  156. src/base_template/README.md +0 -208
  157. src/base_template/app/__init__.py +0 -3
  158. src/base_template/app/utils/tracing.py +0 -155
  159. src/base_template/deployment/README.md +0 -126
  160. src/base_template/deployment/cd/staging.yaml +0 -216
  161. src/base_template/deployment/terraform/dev/log_sinks.tf +0 -63
  162. src/base_template/deployment/terraform/log_sinks.tf +0 -70
  163. src/base_template/deployment/terraform/providers.tf +0 -37
  164. src/cli/commands/create.py +0 -664
  165. src/cli/commands/setup_cicd.py +0 -829
  166. src/cli/utils/gcp.py +0 -117
  167. src/cli/utils/logging.py +0 -51
  168. src/cli/utils/template.py +0 -737
  169. src/deployment_targets/agent_engine/app/agent_engine_app.py +0 -336
  170. src/deployment_targets/agent_engine/notebooks/intro_agent_engine.ipynb +0 -1025
  171. src/deployment_targets/agent_engine/tests/integration/test_agent_engine_app.py +0 -183
  172. src/deployment_targets/agent_engine/tests/load_test/README.md +0 -42
  173. src/deployment_targets/agent_engine/tests/load_test/load_test.py +0 -107
  174. src/deployment_targets/cloud_run/app/server.py +0 -154
  175. src/deployment_targets/cloud_run/tests/integration/test_server_e2e.py +0 -249
  176. src/deployment_targets/cloud_run/tests/load_test/.results/.placeholder +0 -0
  177. src/deployment_targets/cloud_run/tests/load_test/README.md +0 -83
  178. src/deployment_targets/cloud_run/tests/load_test/load_test.py +0 -118
  179. src/deployment_targets/cloud_run/uv.lock +0 -6952
  180. src/frontends/live_api_react/frontend/src/App.tsx +0 -205
  181. src/frontends/live_api_react/frontend/src/components/control-tray/ControlTray.tsx +0 -217
  182. src/frontends/live_api_react/frontend/src/components/control-tray/control-tray.scss +0 -201
  183. src/frontends/live_api_react/frontend/src/components/side-panel/SidePanel.tsx +0 -161
  184. src/frontends/live_api_react/frontend/src/components/side-panel/side-panel.scss +0 -285
  185. src/frontends/streamlit/frontend/side_bar.py +0 -214
  186. src/frontends/streamlit/frontend/streamlit_app.py +0 -265
  187. src/frontends/streamlit/frontend/style/app_markdown.py +0 -37
  188. src/frontends/streamlit/frontend/utils/chat_utils.py +0 -67
  189. src/frontends/streamlit/frontend/utils/local_chat_history.py +0 -125
  190. src/frontends/streamlit/frontend/utils/message_editing.py +0 -59
  191. src/frontends/streamlit/frontend/utils/multimodal_utils.py +0 -217
  192. src/frontends/streamlit/frontend/utils/stream_handler.py +0 -301
  193. src/frontends/streamlit/frontend/utils/title_summary.py +0 -94
  194. src/frontends/streamlit_adk/frontend/side_bar.py +0 -214
  195. src/frontends/streamlit_adk/frontend/streamlit_app.py +0 -314
  196. src/frontends/streamlit_adk/frontend/style/app_markdown.py +0 -37
  197. src/frontends/streamlit_adk/frontend/utils/chat_utils.py +0 -84
  198. src/frontends/streamlit_adk/frontend/utils/local_chat_history.py +0 -110
  199. src/frontends/streamlit_adk/frontend/utils/message_editing.py +0 -61
  200. src/frontends/streamlit_adk/frontend/utils/multimodal_utils.py +0 -223
  201. src/frontends/streamlit_adk/frontend/utils/stream_handler.py +0 -311
  202. src/frontends/streamlit_adk/frontend/utils/title_summary.py +0 -129
  203. src/resources/containers/data_processing/Dockerfile +0 -27
  204. src/resources/containers/e2e-tests/Dockerfile +0 -19
  205. src/resources/idx/.idx/dev.nix +0 -57
  206. src/resources/idx/idx-template.json +0 -21
  207. src/resources/idx/idx-template.nix +0 -26
  208. src/resources/locks/uv-adk_base-agent_engine.lock +0 -5338
  209. src/resources/locks/uv-adk_base-cloud_run.lock +0 -5930
  210. src/resources/locks/uv-agentic_rag-agent_engine.lock +0 -5528
  211. src/resources/locks/uv-agentic_rag-cloud_run.lock +0 -6120
  212. src/resources/locks/uv-crewai_coding_crew-agent_engine.lock +0 -6231
  213. src/resources/locks/uv-crewai_coding_crew-cloud_run.lock +0 -6839
  214. src/resources/locks/uv-langgraph_base_react-agent_engine.lock +0 -5233
  215. src/resources/locks/uv-langgraph_base_react-cloud_run.lock +0 -5862
  216. src/resources/locks/uv-live_api-cloud_run.lock +0 -5832
  217. src/resources/setup_cicd/cicd_variables.tf +0 -41
  218. src/resources/setup_cicd/github.tf +0 -87
  219. {agents → agent_starter_pack/agents}/agentic_rag/app/retrievers.py +0 -0
  220. {agents → agent_starter_pack/agents}/agentic_rag/app/templates.py +0 -0
  221. {src → agent_starter_pack}/base_template/deployment/terraform/dev/vars/env.tfvars +0 -0
  222. {src → agent_starter_pack}/base_template/tests/unit/test_dummy.py +0 -0
  223. {src/deployment_targets/agent_engine/app/utils → agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils}/gcs.py +0 -0
  224. {src → agent_starter_pack}/cli/utils/__init__.py +0 -0
  225. {src → agent_starter_pack}/cli/utils/datastores.py +0 -0
  226. {src → agent_starter_pack}/cli/utils/version.py +0 -0
  227. {src → agent_starter_pack}/data_ingestion/README.md +0 -0
  228. {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/components/ingest_data.py +0 -0
  229. {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/pipeline.py +0 -0
  230. {src → agent_starter_pack}/deployment_targets/agent_engine/deployment_metadata.json +0 -0
  231. {src → agent_starter_pack}/deployment_targets/agent_engine/tests/load_test/.results/.placeholder +0 -0
  232. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/public/favicon.ico +0 -0
  233. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/public/index.html +0 -0
  234. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/public/robots.txt +0 -0
  235. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/App.scss +0 -0
  236. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/App.test.tsx +0 -0
  237. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/audio-pulse/AudioPulse.tsx +0 -0
  238. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/audio-pulse/audio-pulse.scss +0 -0
  239. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/logger/mock-logs.ts +0 -0
  240. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/contexts/LiveAPIContext.tsx +0 -0
  241. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-media-stream-mux.ts +0 -0
  242. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-screen-capture.ts +0 -0
  243. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-webcam.ts +0 -0
  244. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/index.css +0 -0
  245. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/index.tsx +0 -0
  246. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/react-app-env.d.ts +0 -0
  247. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/reportWebVitals.ts +0 -0
  248. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/setupTests.ts +0 -0
  249. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/audioworklet-registry.ts +0 -0
  250. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/store-logger.ts +0 -0
  251. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/worklets/audio-processing.ts +0 -0
  252. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/worklets/vol-meter.ts +0 -0
  253. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/tsconfig.json +0 -0
  254. {agent_starter_pack-0.3.3.dist-info → agent_starter_pack-0.21.0.dist-info}/WHEEL +0 -0
  255. {agent_starter_pack-0.3.3.dist-info → agent_starter_pack-0.21.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,705 @@
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 in local mode."""
46
+ command = [
47
+ sys.executable,
48
+ "-m",
49
+ "uvicorn",
50
+ "{{cookiecutter.agent_directory}}.fast_api_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
+ import json
217
+ import logging
218
+ import os
219
+ import subprocess
220
+ import sys
221
+ import threading
222
+ import time
223
+ {%- if cookiecutter.is_a2a %}
224
+ import uuid
225
+ {%- endif %}
226
+ from collections.abc import Iterator
227
+ from typing import Any
228
+
229
+ import pytest
230
+ import requests
231
+ {%- if cookiecutter.is_a2a %}
232
+ from a2a.types import (
233
+ JSONRPCErrorResponse,
234
+ Message,
235
+ MessageSendParams,
236
+ Part,
237
+ Role,
238
+ SendMessageRequest,
239
+ SendMessageResponse,
240
+ SendStreamingMessageRequest,
241
+ SendStreamingMessageResponse,
242
+ TextPart,
243
+ )
244
+ {%- endif %}
245
+ from requests.exceptions import RequestException
246
+
247
+ # Configure logging
248
+ logging.basicConfig(level=logging.INFO)
249
+ logger = logging.getLogger(__name__)
250
+
251
+ BASE_URL = "http://127.0.0.1:8000/"
252
+ {%- if cookiecutter.is_a2a %}
253
+ A2A_RPC_URL = BASE_URL + "a2a/{{cookiecutter.agent_directory}}/"
254
+ AGENT_CARD_URL = A2A_RPC_URL + ".well-known/agent-card.json"
255
+ {%- elif cookiecutter.is_adk %}
256
+ STREAM_URL = BASE_URL + "run_sse"
257
+ {%- else %}
258
+ STREAM_URL = BASE_URL + "stream_messages"
259
+ {%- endif %}
260
+ FEEDBACK_URL = BASE_URL + "feedback"
261
+
262
+ HEADERS = {"Content-Type": "application/json"}
263
+
264
+
265
+ def log_output(pipe: Any, log_func: Any) -> None:
266
+ """Log the output from the given pipe."""
267
+ for line in iter(pipe.readline, ""):
268
+ log_func(line.strip())
269
+
270
+
271
+ def start_server() -> subprocess.Popen[str]:
272
+ """Start the FastAPI server using subprocess and log its output."""
273
+ command = [
274
+ sys.executable,
275
+ "-m",
276
+ "uvicorn",
277
+ "{{cookiecutter.agent_directory}}.fast_api_app:app",
278
+ "--host",
279
+ "0.0.0.0",
280
+ "--port",
281
+ "8000",
282
+ ]
283
+ env = os.environ.copy()
284
+ env["INTEGRATION_TEST"] = "TRUE"
285
+ {%- if cookiecutter.session_type == "agent_engine" %}
286
+ # Use in-memory session for local E2E tests instead of creating Agent Engine
287
+ env["USE_IN_MEMORY_SESSION"] = "true"
288
+ {%- endif %}
289
+ process = subprocess.Popen(
290
+ command,
291
+ stdout=subprocess.PIPE,
292
+ stderr=subprocess.PIPE,
293
+ text=True,
294
+ bufsize=1,
295
+ env=env,
296
+ )
297
+
298
+ # Start threads to log stdout and stderr in real-time
299
+ threading.Thread(
300
+ target=log_output, args=(process.stdout, logger.info), daemon=True
301
+ ).start()
302
+ threading.Thread(
303
+ target=log_output, args=(process.stderr, logger.error), daemon=True
304
+ ).start()
305
+
306
+ return process
307
+
308
+
309
+ def wait_for_server(timeout: int = 90, interval: int = 1) -> bool:
310
+ """Wait for the server to be ready."""
311
+ start_time = time.time()
312
+ while time.time() - start_time < timeout:
313
+ try:
314
+ {%- if cookiecutter.is_a2a %}
315
+ response = requests.get(AGENT_CARD_URL, timeout=10)
316
+ {%- else %}
317
+ response = requests.get("http://127.0.0.1:8000/docs", timeout=10)
318
+ {%- endif %}
319
+ if response.status_code == 200:
320
+ logger.info("Server is ready")
321
+ return True
322
+ except RequestException:
323
+ pass
324
+ time.sleep(interval)
325
+ logger.error(f"Server did not become ready within {timeout} seconds")
326
+ return False
327
+
328
+
329
+ @pytest.fixture(scope="session")
330
+ def server_fixture(request: Any) -> Iterator[subprocess.Popen[str]]:
331
+ """Pytest fixture to start and stop the server for testing."""
332
+ logger.info("Starting server process")
333
+ server_process = start_server()
334
+ if not wait_for_server():
335
+ pytest.fail("Server failed to start")
336
+ logger.info("Server process started")
337
+
338
+ def stop_server() -> None:
339
+ logger.info("Stopping server process")
340
+ server_process.terminate()
341
+ server_process.wait()
342
+ logger.info("Server process stopped")
343
+
344
+ request.addfinalizer(stop_server)
345
+ yield server_process
346
+
347
+
348
+ def test_chat_stream(server_fixture: subprocess.Popen[str]) -> None:
349
+ {%- if cookiecutter.is_a2a %}
350
+ """Test the chat stream functionality using A2A JSON-RPC protocol."""
351
+ logger.info("Starting chat stream test")
352
+
353
+ message = Message(
354
+ message_id=f"msg-user-{uuid.uuid4()}",
355
+ role=Role.user,
356
+ parts=[Part(root=TextPart(text="What's the weather in San Francisco?"))],
357
+ )
358
+
359
+ request = SendStreamingMessageRequest(
360
+ id="test-req-001",
361
+ params=MessageSendParams(message=message),
362
+ )
363
+
364
+ # Send the request
365
+ response = requests.post(
366
+ A2A_RPC_URL,
367
+ headers=HEADERS,
368
+ json=request.model_dump(mode="json", exclude_none=True),
369
+ stream=True,
370
+ timeout=60,
371
+ )
372
+ assert response.status_code == 200
373
+
374
+ # Parse streaming JSON-RPC responses
375
+ responses: list[SendStreamingMessageResponse] = []
376
+
377
+ for line in response.iter_lines():
378
+ if line:
379
+ line_str = line.decode("utf-8")
380
+ if line_str.startswith("data: "):
381
+ event_json = line_str[6:]
382
+ json_data = json.loads(event_json)
383
+ streaming_response = SendStreamingMessageResponse.model_validate(
384
+ json_data
385
+ )
386
+ responses.append(streaming_response)
387
+
388
+ assert responses, "No responses received from stream"
389
+
390
+ # Check for final status update
391
+ final_responses = [
392
+ r.root
393
+ for r in responses
394
+ if hasattr(r.root, "result")
395
+ and hasattr(r.root.result, "final")
396
+ and r.root.result.final is True
397
+ ]
398
+ assert final_responses, "No final response received"
399
+
400
+ final_response = final_responses[-1]
401
+ assert final_response.result.kind == "status-update"
402
+ assert hasattr(final_response.result, "status")
403
+ assert final_response.result.status.state == "completed"
404
+
405
+ # Check for artifact content
406
+ artifact_responses = [
407
+ r.root
408
+ for r in responses
409
+ if hasattr(r.root, "result") and r.root.result.kind == "artifact-update"
410
+ ]
411
+ assert artifact_responses, "No artifact content received in stream"
412
+
413
+ # Verify text content is in the artifact
414
+ artifact_response = artifact_responses[-1]
415
+ assert hasattr(artifact_response.result, "artifact")
416
+ artifact = artifact_response.result.artifact
417
+ assert artifact.parts, "Artifact has no parts"
418
+
419
+ has_text = any(
420
+ part.root.kind == "text" and hasattr(part.root, "text") and part.root.text
421
+ for part in artifact.parts
422
+ )
423
+ assert has_text, "No text content found in artifact"
424
+ {%- else %}
425
+ """Test the chat stream functionality."""
426
+ logger.info("Starting chat stream test")
427
+ {% if cookiecutter.is_adk %}
428
+ # Create session first
429
+ user_id = "test_user_123"
430
+ session_data = {"state": {"preferred_language": "English", "visit_count": 1}}
431
+
432
+ session_url = f"{BASE_URL}/apps/{{cookiecutter.agent_directory}}/users/{user_id}/sessions"
433
+ session_response = requests.post(
434
+ session_url,
435
+ headers=HEADERS,
436
+ json=session_data,
437
+ timeout=60,
438
+ )
439
+ assert session_response.status_code == 200
440
+ logger.info(f"Session creation response: {session_response.json()}")
441
+ session_id = session_response.json()["id"]
442
+
443
+ # Then send chat message
444
+ data = {
445
+ "app_name": "{{cookiecutter.agent_directory}}",
446
+ "user_id": user_id,
447
+ "session_id": session_id,
448
+ "new_message": {
449
+ "role": "user",
450
+ "parts": [{"text": "What's the weather in San Francisco?"}],
451
+ },
452
+ "streaming": True,
453
+ }
454
+ {% else %}
455
+ data = {
456
+ "input": {
457
+ "messages": [
458
+ {"type": "human", "content": "Hello, AI!"},
459
+ {"type": "ai", "content": "Hello!"},
460
+ {"type": "human", "content": "Who are you?"},
461
+ ]
462
+ },
463
+ "config": {"metadata": {"user_id": "test-user", "session_id": "test-session"}},
464
+ }
465
+ {% endif %}
466
+ response = requests.post(
467
+ STREAM_URL, headers=HEADERS, json=data, stream=True, timeout=60
468
+ )
469
+ assert response.status_code == 200
470
+
471
+ {%- if cookiecutter.is_adk %}
472
+ # Parse SSE events from response
473
+ events = []
474
+ for line in response.iter_lines():
475
+ if line:
476
+ # SSE format is "data: {json}"
477
+ line_str = line.decode("utf-8")
478
+ if line_str.startswith("data: "):
479
+ event_json = line_str[6:] # Remove "data: " prefix
480
+ event = json.loads(event_json)
481
+ events.append(event)
482
+
483
+ assert events, "No events received from stream"
484
+ # Check for valid content in the response
485
+ has_text_content = False
486
+ for event in events:
487
+ content = event.get("content")
488
+ if (
489
+ content is not None
490
+ and content.get("parts")
491
+ and any(part.get("text") for part in content["parts"])
492
+ ):
493
+ has_text_content = True
494
+ break
495
+ {%- else %}
496
+ events = [json.loads(line) for line in response.iter_lines() if line]
497
+ assert events, "No events received from stream"
498
+
499
+ # Verify each event is a tuple of message and metadata
500
+ for event in events:
501
+ assert isinstance(event, list), "Event should be a list"
502
+ assert len(event) == 2, "Event should contain message and metadata"
503
+ message, _ = event
504
+
505
+ # Verify message structure
506
+ assert isinstance(message, dict), "Message should be a dictionary"
507
+ assert message["type"] == "constructor"
508
+ assert "kwargs" in message, "Constructor message should have kwargs"
509
+
510
+ # Verify at least one message has content
511
+ has_content = False
512
+ for event in events:
513
+ message = event[0]
514
+ if message.get("type") == "constructor" and "content" in message["kwargs"]:
515
+ has_content = True
516
+ break
517
+ assert has_content, "At least one message should have content"
518
+ {%- endif %}
519
+ {%- endif %}
520
+
521
+
522
+ {%- if cookiecutter.is_a2a %}
523
+
524
+
525
+ def test_chat_non_streaming(server_fixture: subprocess.Popen[str]) -> None:
526
+ """Test the non-streaming chat functionality using A2A JSON-RPC protocol."""
527
+ logger.info("Starting non-streaming chat test")
528
+
529
+ message = Message(
530
+ message_id=f"msg-user-{uuid.uuid4()}",
531
+ role=Role.user,
532
+ parts=[Part(root=TextPart(text="What's the weather in San Francisco?"))],
533
+ )
534
+
535
+ request = SendMessageRequest(
536
+ id="test-req-002",
537
+ params=MessageSendParams(message=message),
538
+ )
539
+
540
+ response = requests.post(
541
+ A2A_RPC_URL,
542
+ headers=HEADERS,
543
+ json=request.model_dump(mode="json", exclude_none=True),
544
+ timeout=60,
545
+ )
546
+ assert response.status_code == 200
547
+
548
+ # Parse the single JSON-RPC response
549
+ response_data = response.json()
550
+ message_response = SendMessageResponse.model_validate(response_data)
551
+ logger.info(f"Received response: {message_response}")
552
+
553
+ # For non-streaming, the result is a Task object
554
+ json_rpc_resp = message_response.root
555
+ assert hasattr(json_rpc_resp, "result")
556
+ task = json_rpc_resp.result
557
+ assert task.kind == "task"
558
+ assert hasattr(task, "status")
559
+ assert task.status.state == "completed"
560
+
561
+ # Check that we got artifacts (the final agent output)
562
+ assert hasattr(task, "artifacts")
563
+ assert task.artifacts, "No artifacts in task"
564
+
565
+ # Verify we got text content in the artifact
566
+ artifact = task.artifacts[0]
567
+ assert artifact.parts, "Artifact has no parts"
568
+
569
+ has_text = any(
570
+ part.root.kind == "text" and hasattr(part.root, "text") and part.root.text
571
+ for part in artifact.parts
572
+ )
573
+ assert has_text, "No text content found in artifact"
574
+
575
+
576
+ def test_chat_stream_error_handling(server_fixture: subprocess.Popen[str]) -> None:
577
+ """Test the chat stream error handling with invalid A2A request."""
578
+ logger.info("Starting chat stream error handling test")
579
+
580
+ invalid_data = {
581
+ "jsonrpc": "2.0",
582
+ "id": "test-error-001",
583
+ "method": "message/send",
584
+ "params": {
585
+ "message": {
586
+ "role": "user",
587
+ # Missing required 'parts' field
588
+ "messageId": f"msg-user-{uuid.uuid4()}",
589
+ }
590
+ },
591
+ }
592
+
593
+ response = requests.post(
594
+ A2A_RPC_URL, headers=HEADERS, json=invalid_data, timeout=10
595
+ )
596
+ assert response.status_code == 200
597
+
598
+ response_data = response.json()
599
+ error_response = JSONRPCErrorResponse.model_validate(response_data)
600
+ assert "error" in response_data, "Expected JSON-RPC error in response"
601
+
602
+ # Assert error for invalid parameters
603
+ assert error_response.error.code == -32602
604
+
605
+ logger.info("Error handling test completed successfully")
606
+ {%- else %}
607
+
608
+
609
+ def test_chat_stream_error_handling(server_fixture: subprocess.Popen[str]) -> None:
610
+ """Test the chat stream error handling."""
611
+ logger.info("Starting chat stream error handling test")
612
+ data = {
613
+ "input": {"messages": [{"type": "invalid_type", "content": "Cause an error"}]}
614
+ }
615
+ response = requests.post(
616
+ STREAM_URL, headers=HEADERS, json=data, stream=True, timeout=10
617
+ )
618
+
619
+ assert response.status_code == 422, (
620
+ f"Expected status code 422, got {response.status_code}"
621
+ )
622
+ logger.info("Error handling test completed successfully")
623
+ {%- endif %}
624
+
625
+
626
+ def test_collect_feedback(server_fixture: subprocess.Popen[str]) -> None:
627
+ """
628
+ Test the feedback collection endpoint (/feedback) to ensure it properly
629
+ logs the received feedback.
630
+ """
631
+ # Create sample feedback data
632
+ feedback_data = {
633
+ "score": 4,
634
+ "user_id": "test-user-456",
635
+ "session_id": "test-session-456",
636
+ "text": "Great response!",
637
+ }
638
+
639
+ response = requests.post(
640
+ FEEDBACK_URL, json=feedback_data, headers=HEADERS, timeout=10
641
+ )
642
+ assert response.status_code == 200
643
+
644
+
645
+ {%- if cookiecutter.is_a2a %}
646
+
647
+
648
+ def test_a2a_agent_json_generation(server_fixture: subprocess.Popen[str]) -> None:
649
+ """
650
+ Test that the agent.json file is automatically generated and served correctly
651
+ via the well-known URI.
652
+ """
653
+ # Verify the A2A endpoint serves the agent card
654
+ response = requests.get(AGENT_CARD_URL, timeout=10)
655
+ assert response.status_code == 200, f"A2A endpoint returned {response.status_code}"
656
+
657
+ # Validate required fields in served agent card
658
+ served_agent_card = response.json()
659
+ required_fields = [
660
+ "name",
661
+ "description",
662
+ "skills",
663
+ "capabilities",
664
+ "url",
665
+ "version",
666
+ ]
667
+ for field in required_fields:
668
+ assert field in served_agent_card, (
669
+ f"Missing required field in served agent card: {field}"
670
+ )
671
+
672
+
673
+ {%- endif %}
674
+ {%- if cookiecutter.session_type == "agent_engine" %}
675
+
676
+
677
+ @pytest.fixture(scope="session", autouse=True)
678
+ def cleanup_agent_engine_sessions() -> None:
679
+ """Cleanup agent engine sessions created during tests."""
680
+ yield # Run tests first
681
+
682
+ # Cleanup after tests complete
683
+ from vertexai import agent_engines
684
+
685
+ try:
686
+ # Use same environment variable as server, default to project name
687
+ agent_name = os.environ.get(
688
+ "AGENT_ENGINE_SESSION_NAME", "{{cookiecutter.project_name}}"
689
+ )
690
+
691
+ # Find and delete agent engines with this name
692
+ existing_agents = list(agent_engines.list(filter=f"display_name={agent_name}"))
693
+
694
+ for agent_engine in existing_agents:
695
+ try:
696
+ agent_engines.delete(resource_name=agent_engine.name)
697
+ logger.info(f"Cleaned up agent engine: {agent_engine.name}")
698
+ except Exception as e:
699
+ logger.warning(
700
+ f"Failed to cleanup agent engine {agent_engine.name}: {e}"
701
+ )
702
+ except Exception as e:
703
+ logger.warning(f"Failed to cleanup agent engine sessions: {e}")
704
+ {%- endif %}
705
+ {%- endif %}