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
@@ -1,78 +0,0 @@
1
- # Copyright 2025 Google LLC
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
-
15
- import os
16
-
17
- import google
18
- import vertexai
19
- from google import genai
20
- from google.genai.types import (
21
- Content,
22
- LiveConnectConfig,
23
- )
24
- from langchain_google_vertexai import VertexAIEmbeddings
25
-
26
- from app.templates import FORMAT_DOCS, SYSTEM_INSTRUCTION
27
- from app.vector_store import get_vector_store
28
-
29
- # Constants
30
- VERTEXAI = os.getenv("VERTEXAI", "true").lower() == "true"
31
- LOCATION = "us-central1"
32
- EMBEDDING_MODEL = "text-embedding-004"
33
- MODEL_ID = "gemini-2.0-flash-exp"
34
- URLS = [
35
- "https://cloud.google.com/architecture/deploy-operate-generative-ai-applications"
36
- ]
37
-
38
- # Initialize Google Cloud clients
39
- credentials, project_id = google.auth.default()
40
- vertexai.init(project=project_id, location=LOCATION)
41
-
42
-
43
- if VERTEXAI:
44
- genai_client = genai.Client(project=project_id, location=LOCATION, vertexai=True)
45
- else:
46
- # API key should be set using GOOGLE_API_KEY environment variable
47
- genai_client = genai.Client(http_options={"api_version": "v1alpha"})
48
-
49
- # Initialize vector store and retriever
50
- embedding = VertexAIEmbeddings(model_name=EMBEDDING_MODEL)
51
- vector_store = get_vector_store(embedding=embedding, urls=URLS)
52
- retriever = vector_store.as_retriever()
53
-
54
-
55
- def retrieve_docs(query: str) -> dict[str, str]:
56
- """
57
- Retrieves pre-formatted documents about MLOps (Machine Learning Operations),
58
- Gen AI lifecycle, and production deployment best practices.
59
-
60
- Args:
61
- query: Search query string related to MLOps, Gen AI, or production deployment.
62
-
63
- Returns:
64
- A set of relevant, pre-formatted documents.
65
- """
66
- docs = retriever.invoke(query)
67
- formatted_docs = FORMAT_DOCS.format(docs=docs)
68
- return {"output": formatted_docs}
69
-
70
-
71
- # Configure tools available to the agent and live connection
72
- tool_functions = {"retrieve_docs": retrieve_docs}
73
-
74
- live_connect_config = LiveConnectConfig(
75
- response_modalities=["AUDIO"],
76
- tools=[retrieve_docs],
77
- system_instruction=Content(parts=[{"text": SYSTEM_INSTRUCTION}]),
78
- )
@@ -1,196 +0,0 @@
1
- # Copyright 2025 Google LLC
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
-
15
- import asyncio
16
- import json
17
- import logging
18
- from collections.abc import Callable
19
- from typing import Any, Literal
20
-
21
- import backoff
22
- from fastapi import FastAPI, WebSocket
23
- from fastapi.middleware.cors import CORSMiddleware
24
- from google.cloud import logging as google_cloud_logging
25
- from google.genai import types
26
- from google.genai.types import LiveServerToolCall
27
- from pydantic import BaseModel
28
- from websockets.exceptions import ConnectionClosedError
29
-
30
- from app.agent import MODEL_ID, genai_client, live_connect_config, tool_functions
31
-
32
- app = FastAPI()
33
- app.add_middleware(
34
- CORSMiddleware,
35
- allow_origins=["*"],
36
- allow_methods=["*"],
37
- allow_headers=["*"],
38
- )
39
- logging_client = google_cloud_logging.Client()
40
- logger = logging_client.logger(__name__)
41
- logging.basicConfig(level=logging.INFO)
42
-
43
-
44
- class GeminiSession:
45
- """Manages bidirectional communication between a client and the Gemini model."""
46
-
47
- def __init__(
48
- self, session: Any, websocket: WebSocket, tool_functions: dict[str, Callable]
49
- ) -> None:
50
- """Initialize the Gemini session.
51
-
52
- Args:
53
- session: The Gemini session
54
- websocket: The client websocket connection
55
- user_id: Unique identifier for this client
56
- tool_functions: Dictionary of available tool functions
57
- """
58
- self.session = session
59
- self.websocket = websocket
60
- self.run_id = "n/a"
61
- self.user_id = "n/a"
62
- self.tool_functions = tool_functions
63
-
64
- async def receive_from_client(self) -> None:
65
- """Listen for and process messages from the client.
66
-
67
- Continuously receives messages and forwards audio data to Gemini.
68
- Handles connection errors gracefully.
69
- """
70
- while True:
71
- try:
72
- data = await self.websocket.receive_json()
73
- if isinstance(data, dict) and (
74
- "realtimeInput" in data or "clientContent" in data
75
- ):
76
- await self.session._ws.send(json.dumps(data))
77
- elif "setup" in data:
78
- self.run_id = data["setup"]["run_id"]
79
- self.user_id = data["setup"]["user_id"]
80
- logger.log_struct(
81
- {**data["setup"], "type": "setup"}, severity="INFO"
82
- )
83
- else:
84
- logging.warning(f"Received unexpected input from client: {data}")
85
- except ConnectionClosedError as e:
86
- logging.warning(f"Client {self.user_id} closed connection: {e}")
87
- break
88
- except Exception as e:
89
- logging.error(f"Error receiving from client {self.user_id}: {e!s}")
90
- break
91
-
92
- def _get_func(self, action_label: str) -> Callable | None:
93
- """Get the tool function for a given action label."""
94
- return None if action_label == "" else self.tool_functions.get(action_label)
95
-
96
- async def _handle_tool_call(
97
- self, session: Any, tool_call: LiveServerToolCall
98
- ) -> None:
99
- """Process tool calls from Gemini and send back responses.
100
-
101
- Args:
102
- session: The Gemini session
103
- tool_call: Tool call request from Gemini
104
- """
105
- for fc in tool_call.function_calls:
106
- logging.debug(f"Calling tool function: {fc.name} with args: {fc.args}")
107
- response = self._get_func(fc.name)(**fc.args)
108
- tool_response = types.LiveClientToolResponse(
109
- function_responses=[
110
- types.FunctionResponse(name=fc.name, id=fc.id, response=response)
111
- ]
112
- )
113
- logging.debug(f"Tool response: {tool_response}")
114
- await session.send(input=tool_response)
115
-
116
- async def receive_from_gemini(self) -> None:
117
- """Listen for and process messages from Gemini.
118
-
119
- Continuously receives messages from Gemini, forwards them to the client,
120
- and handles any tool calls. Handles connection errors gracefully.
121
- """
122
- while result := await self.session._ws.recv(decode=False):
123
- await self.websocket.send_bytes(result)
124
- raw_message = json.loads(result)
125
- if "toolCall" in raw_message:
126
- message = types.LiveServerMessage.model_validate(raw_message)
127
- tool_call = LiveServerToolCall.model_validate(message.tool_call)
128
- await self._handle_tool_call(self.session, tool_call)
129
-
130
-
131
- def get_connect_and_run_callable(websocket: WebSocket) -> Callable:
132
- """Create a callable that handles Gemini connection with retry logic.
133
-
134
- Args:
135
- websocket: The client websocket connection
136
-
137
- Returns:
138
- Callable: An async function that establishes and manages the Gemini connection
139
- """
140
-
141
- async def on_backoff(details: backoff._typing.Details) -> None:
142
- await websocket.send_json(
143
- {
144
- "status": f"Model connection error, retrying in {details['wait']} seconds..."
145
- }
146
- )
147
-
148
- @backoff.on_exception(
149
- backoff.expo, ConnectionClosedError, max_tries=10, on_backoff=on_backoff
150
- )
151
- async def connect_and_run() -> None:
152
- async with genai_client.aio.live.connect(
153
- model=MODEL_ID, config=live_connect_config
154
- ) as session:
155
- await websocket.send_json({"status": "Backend is ready for conversation"})
156
- gemini_session = GeminiSession(
157
- session=session, websocket=websocket, tool_functions=tool_functions
158
- )
159
- logging.info("Starting bidirectional communication")
160
- await asyncio.gather(
161
- gemini_session.receive_from_client(),
162
- gemini_session.receive_from_gemini(),
163
- )
164
-
165
- return connect_and_run
166
-
167
-
168
- @app.websocket("/ws")
169
- async def websocket_endpoint(websocket: WebSocket) -> None:
170
- """Handle new websocket connections."""
171
- await websocket.accept()
172
- connect_and_run = get_connect_and_run_callable(websocket)
173
- await connect_and_run()
174
-
175
-
176
- class Feedback(BaseModel):
177
- """Represents feedback for a conversation."""
178
-
179
- score: int | float
180
- text: str | None = ""
181
- run_id: str
182
- user_id: str | None
183
- log_type: Literal["feedback"] = "feedback"
184
-
185
-
186
- @app.post("/feedback")
187
- async def collect_feedback(feedback_dict: Feedback) -> None:
188
- """Collect and log feedback."""
189
- feedback_data = feedback_dict.model_dump()
190
- logger.log_struct(feedback_data, severity="INFO")
191
-
192
-
193
- if __name__ == "__main__":
194
- import uvicorn
195
-
196
- uvicorn.run(app, host="0.0.0.0", port=8000, log_level="debug")
@@ -1,51 +0,0 @@
1
- # Copyright 2025 Google LLC
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
-
15
- from langchain_core.prompts import PromptTemplate
16
-
17
- FORMAT_DOCS = PromptTemplate.from_template(
18
- """## Context provided:
19
- {% for doc in docs%}
20
- <Document {{ loop.index0 }}>
21
- {{ doc.page_content | safe }}
22
- </Document {{ loop.index0 }}>
23
- {% endfor %}
24
- """,
25
- template_format="jinja2",
26
- )
27
-
28
- SYSTEM_INSTRUCTION = """You are "MLOps Expert," a specialized AI assistant designed to provide accurate and up-to-date information on Machine Learning Operations (MLOps), the lifecycle of Generative AI applications, and best practices for production deployment.
29
-
30
- Your primary knowledge source is a powerful search tool that provides access to the most current MLOps documentation and resources. **For any question related to MLOps, the lifecycle of Gen AI Apps, or best practices for production deployment, you MUST use this tool as your first and foremost source of information.** Do not rely on your internal knowledge for these topics, as it may be outdated or incomplete.
31
-
32
- **Here's how you should operate:**
33
-
34
- 1. **Analyze the User's Question:** Determine if the question falls within the domain of MLOps, Gen AI lifecycle, or production deployment best practices.
35
- 2. **Prioritize Tool Usage:** If the question is within the defined domain, use the provided search tool to find relevant information.
36
- 3. **Synthesize and Respond:** Craft a clear, concise, and informative answer based *solely* on the information retrieved from the tool.
37
- 4. **Cite Sources (Optional):** If possible and relevant, indicate which part of the answer came from the tool. For example, you can say, "According to the documentation I found..." or provide links if applicable.
38
- 5. **Out-of-Scope Questions:** If the question is outside the scope of MLOps, Gen AI, or production deployment, politely state that the topic is beyond your current expertise. For example: "My expertise is in MLOps, and that question seems to be about a different area. I'm not equipped to answer it accurately."
39
-
40
- **Your Persona:**
41
-
42
- * You are an expert MLOps consultant, knowledgeable and up-to-date with the latest industry trends and best practices.
43
- * You are helpful, professional, and eager to provide accurate information.
44
- * You are concise and avoid unnecessary conversational filler. Get straight to the point.
45
-
46
- **Example Interaction:**
47
-
48
- **User:** "What are the best practices for monitoring a deployed ML model?"
49
-
50
- **MLOps Expert:** (Uses the tool to search for "monitoring deployed ML model") "According to the MLOps documentation I have access to, the best practices for monitoring a deployed ML model include tracking data drift, model performance degradation, and system health metrics. Key metrics to monitor are..." (continues with information found in the tool).
51
- """
@@ -1,55 +0,0 @@
1
- # Copyright 2025 Google LLC
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
-
15
- import logging
16
- import os
17
-
18
- from langchain.schema import Document
19
- from langchain.text_splitter import CharacterTextSplitter
20
- from langchain_community.document_loaders import WebBaseLoader
21
- from langchain_community.vectorstores import SKLearnVectorStore
22
- from langchain_core.embeddings import Embeddings
23
-
24
- PERSIST_PATH = ".persist_vector_store"
25
-
26
-
27
- def load_and_split_documents(urls: list[str]) -> list[Document]:
28
- """Load and split documents from a list of URLs."""
29
- docs = [WebBaseLoader(url).load() for url in urls]
30
- docs_list = [item for sublist in docs for item in sublist]
31
- logging.info(f"# of documents loaded (pre-chunking) = {len(docs_list)}")
32
-
33
- text_splitter = CharacterTextSplitter(chunk_size=2000, chunk_overlap=50)
34
- doc_splits = text_splitter.split_documents(docs_list)
35
- logging.info(f"# of documents after split = {len(doc_splits)}")
36
-
37
- return doc_splits
38
-
39
-
40
- def get_vector_store(
41
- embedding: Embeddings, urls: list[str], persist_path: str = PERSIST_PATH
42
- ) -> SKLearnVectorStore:
43
- """Get or create a vector store."""
44
-
45
- if os.path.exists(persist_path):
46
- vector_store = SKLearnVectorStore(
47
- embedding=embedding, persist_path=persist_path
48
- )
49
- else:
50
- doc_splits = load_and_split_documents(urls=urls)
51
- vector_store = SKLearnVectorStore.from_documents(
52
- documents=doc_splits, embedding=embedding, persist_path=persist_path
53
- )
54
- vector_store.persist()
55
- return vector_store
@@ -1,29 +0,0 @@
1
- # Copyright 2025 Google LLC
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
-
15
- description: "A real-time multimodal RAG agent powered by Gemini Live API, supporting audio/video/text chat with vector DB-backed responses"
16
- settings:
17
- requires_data_ingestion: false
18
- frontend_type: "live_api_react"
19
- deployment_targets: ["cloud_run"]
20
- extra_dependencies: [
21
- "backoff~=2.2.1",
22
- "beautifulsoup4~=4.12.3",
23
- "google-genai~=1.8.0",
24
- "jinja2~=3.1.4",
25
- "langchain~=0.3.13",
26
- "langchain-community~=0.3.13",
27
- "langchain-google-vertexai~=2.0.9",
28
- "scikit-learn>=1.0.0,<2.0.0",
29
- ]
@@ -1,254 +0,0 @@
1
- # Copyright 2025 Google LLC
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
-
15
- import asyncio
16
- import json
17
- import logging
18
- import os
19
- import subprocess
20
- import sys
21
- import threading
22
- import time
23
- import uuid
24
- from collections.abc import Iterator
25
- from typing import Any
26
-
27
- import pytest
28
- import requests
29
- import websockets.client
30
-
31
- # Configure logging
32
- logging.basicConfig(level=logging.DEBUG)
33
- logger = logging.getLogger(__name__)
34
-
35
- BASE_URL = "ws://127.0.0.1:8000/"
36
- WS_URL = BASE_URL + "ws"
37
-
38
- FEEDBACK_URL = "http://127.0.0.1:8000/feedback"
39
- HEADERS = {"Content-Type": "application/json"}
40
-
41
-
42
- def log_output(pipe: Any, log_func: Any) -> None:
43
- """Log the output from the given pipe."""
44
- for line in iter(pipe.readline, ""):
45
- log_func(line.strip())
46
-
47
-
48
- def start_server() -> subprocess.Popen[str]:
49
- """Start the FastAPI server using subprocess and log its output."""
50
- command = [
51
- sys.executable,
52
- "-m",
53
- "uvicorn",
54
- "app.server:app",
55
- "--host",
56
- "0.0.0.0",
57
- "--port",
58
- "8000",
59
- ]
60
- env = os.environ.copy()
61
- env["INTEGRATION_TEST"] = "TRUE"
62
- process = subprocess.Popen(
63
- command,
64
- stdout=subprocess.PIPE,
65
- stderr=subprocess.PIPE,
66
- text=True,
67
- bufsize=1,
68
- env=env,
69
- )
70
-
71
- # Start threads to log stdout and stderr in real-time
72
- threading.Thread(
73
- target=log_output, args=(process.stdout, logger.info), daemon=True
74
- ).start()
75
- threading.Thread(
76
- target=log_output, args=(process.stderr, logger.error), daemon=True
77
- ).start()
78
-
79
- return process
80
-
81
-
82
- def wait_for_server(timeout: int = 60, interval: int = 1) -> bool:
83
- """Wait for the server to be ready."""
84
- start_time = time.time()
85
- while time.time() - start_time < timeout:
86
- try:
87
- response = requests.get("http://127.0.0.1:8000/docs", timeout=10)
88
- if response.status_code == 200:
89
- logger.info("Server is ready")
90
- return True
91
- except Exception:
92
- pass
93
- time.sleep(interval)
94
- logger.error(f"Server did not become ready within {timeout} seconds")
95
- return False
96
-
97
-
98
- @pytest.fixture(scope="session")
99
- def server_fixture(request: Any) -> Iterator[subprocess.Popen[str]]:
100
- """Pytest fixture to start and stop the server for testing."""
101
- logger.info("Starting server process")
102
- server_process = start_server()
103
- if not wait_for_server():
104
- pytest.fail("Server failed to start")
105
- logger.info("Server process started")
106
-
107
- def stop_server() -> None:
108
- logger.info("Stopping server process")
109
- server_process.terminate()
110
- server_process.wait()
111
- logger.info("Server process stopped")
112
-
113
- request.addfinalizer(stop_server)
114
- yield server_process
115
-
116
-
117
- @pytest.mark.asyncio
118
- async def test_websocket_connection(server_fixture: subprocess.Popen[str]) -> None:
119
- """Test the websocket connection and message exchange."""
120
-
121
- async def send_message(websocket: Any, message: dict[str, Any]) -> None:
122
- """Helper function to send messages and log them."""
123
- await websocket.send(json.dumps(message))
124
-
125
- async def receive_message(websocket: Any, timeout: float = 5.0) -> dict[str, Any]:
126
- """Helper function to receive messages with timeout."""
127
- try:
128
- response = await asyncio.wait_for(websocket.recv(), timeout=timeout)
129
- if isinstance(response, bytes):
130
- return json.loads(response.decode())
131
- if isinstance(response, str):
132
- return json.loads(response)
133
- return response
134
- except asyncio.TimeoutError as exc:
135
- raise TimeoutError(
136
- f"No response received within {timeout} seconds"
137
- ) from exc
138
-
139
- try:
140
- await asyncio.sleep(2)
141
-
142
- async with websockets.connect(
143
- WS_URL, ping_timeout=10, close_timeout=10
144
- ) as websocket:
145
- try:
146
- # Wait for initial ready message
147
- initial_response = None
148
- for _ in range(10):
149
- try:
150
- initial_response = await receive_message(websocket, timeout=5.0)
151
- if (
152
- initial_response is not None
153
- and initial_response.get("status")
154
- == "Backend is ready for conversation"
155
- ):
156
- break
157
- except TimeoutError:
158
- if _ == 9:
159
- raise
160
- continue
161
-
162
- assert (
163
- initial_response is not None
164
- and initial_response.get("status")
165
- == "Backend is ready for conversation"
166
- )
167
-
168
- # Send messages
169
- setup_msg = {"setup": {"run_id": "test-run", "user_id": "test-user"}}
170
- await send_message(websocket, setup_msg)
171
-
172
- dummy_audio = bytes([0] * 1024)
173
- audio_msg = {
174
- "realtimeInput": {
175
- "mediaChunks": [
176
- {
177
- "mimeType": "audio/pcm;rate=16000",
178
- "data": dummy_audio.hex(),
179
- }
180
- ]
181
- }
182
- }
183
- await send_message(websocket, audio_msg)
184
-
185
- text_msg = {
186
- "clientContent": {
187
- "turns": [
188
- {"role": "user", "parts": [{"text": "Hello, how are you?"}]}
189
- ],
190
- "turnComplete": True,
191
- }
192
- }
193
- await send_message(websocket, text_msg)
194
-
195
- # Collect responses with timeout
196
- responses = []
197
- try:
198
- while True:
199
- try:
200
- response = await receive_message(websocket, timeout=10.0)
201
- responses.append(response)
202
- if (
203
- len(responses) >= 3
204
- ): # Exit after receiving enough responses
205
- break
206
- except TimeoutError:
207
- break
208
- except asyncio.TimeoutError:
209
- logger.info("Response collection timed out")
210
-
211
- # Verify responses
212
- assert len(responses) > 0, "No responses received from server"
213
- assert any(
214
- isinstance(r, dict) and "serverContent" in r for r in responses
215
- )
216
- logger.info(
217
- f"Test completed successfully. Received {len(responses)} responses"
218
- )
219
-
220
- finally:
221
- # Ensure websocket is closed properly
222
- await websocket.close()
223
-
224
- except Exception as e:
225
- logger.error(f"Test failed with error: {e!s}")
226
- raise
227
-
228
- finally:
229
- # Clean up any remaining tasks
230
- tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()]
231
- for task in tasks:
232
- task.cancel()
233
- if tasks:
234
- await asyncio.gather(*tasks, return_exceptions=True)
235
-
236
-
237
- def test_collect_feedback(server_fixture: subprocess.Popen[str]) -> None:
238
- """
239
- Test the feedback collection endpoint (/feedback) to ensure it properly
240
- logs the received feedback.
241
- """
242
- # Create sample feedback data
243
- feedback_data = {
244
- "score": 4,
245
- "text": "Great response!",
246
- "run_id": str(uuid.uuid4()),
247
- "user_id": "user1",
248
- "log_type": "feedback",
249
- }
250
-
251
- response = requests.post(
252
- FEEDBACK_URL, json=feedback_data, headers=HEADERS, timeout=10
253
- )
254
- assert response.status_code == 200