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,556 @@
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
+ import asyncio
16
+ import json
17
+ import logging
18
+ from collections.abc import Callable
19
+ from pathlib import Path
20
+
21
+ import backoff
22
+ import google.auth
23
+ from fastapi import FastAPI, HTTPException, WebSocket
24
+ from fastapi.middleware.cors import CORSMiddleware
25
+ from fastapi.responses import FileResponse
26
+ from fastapi.staticfiles import StaticFiles
27
+ from google.adk.agents.live_request_queue import LiveRequest, LiveRequestQueue
28
+ from google.adk.artifacts.in_memory_artifact_service import InMemoryArtifactService
29
+ from google.adk.memory.in_memory_memory_service import InMemoryMemoryService
30
+ from google.adk.runners import Runner
31
+ from google.adk.sessions.in_memory_session_service import InMemorySessionService
32
+ from google.cloud import logging as google_cloud_logging
33
+ from vertexai.agent_engines import _utils
34
+ from websockets.exceptions import ConnectionClosedError
35
+
36
+ from .agent import app as adk_app
37
+ from .app_utils.telemetry import setup_telemetry
38
+ from .app_utils.typing import Feedback
39
+
40
+ app = FastAPI()
41
+ app.add_middleware(
42
+ CORSMiddleware,
43
+ allow_origins=["*"],
44
+ allow_methods=["*"],
45
+ allow_headers=["*"],
46
+ )
47
+
48
+ # Get the path to the frontend build directory
49
+ current_dir = Path(__file__).parent
50
+ frontend_build_dir = current_dir.parent / "frontend" / "build"
51
+
52
+ # Mount static files if build directory exists
53
+ if frontend_build_dir.exists():
54
+ app.mount(
55
+ "/static",
56
+ StaticFiles(directory=str(frontend_build_dir / "static")),
57
+ name="static",
58
+ )
59
+ logging_client = google_cloud_logging.Client()
60
+ logger = logging_client.logger(__name__)
61
+ logging.basicConfig(level=logging.INFO)
62
+
63
+ setup_telemetry()
64
+ _, project_id = google.auth.default()
65
+
66
+
67
+ # Initialize ADK services
68
+ session_service = InMemorySessionService()
69
+ artifact_service = InMemoryArtifactService()
70
+ memory_service = InMemoryMemoryService()
71
+
72
+ # Initialize ADK runner
73
+ runner = Runner(
74
+ app=adk_app,
75
+ session_service=session_service,
76
+ artifact_service=artifact_service,
77
+ memory_service=memory_service,
78
+ )
79
+
80
+
81
+ class AgentSession:
82
+ """Manages bidirectional communication between a client and the agent."""
83
+
84
+ def __init__(self, websocket: WebSocket) -> None:
85
+ """Initialize the agent session.
86
+
87
+ Args:
88
+ websocket: The client websocket connection
89
+ """
90
+ self.websocket = websocket
91
+ self.input_queue: asyncio.Queue[dict] = asyncio.Queue()
92
+ self.user_id: str | None = None
93
+ self.session_id: str | None = None
94
+
95
+ async def receive_from_client(self) -> None:
96
+ """Listen for messages from the client and put them in the queue."""
97
+ while True:
98
+ try:
99
+ message = await self.websocket.receive()
100
+
101
+ if "text" in message:
102
+ data = json.loads(message["text"])
103
+
104
+ if isinstance(data, dict):
105
+ # Skip setup messages - they're for backend logging only
106
+ if "setup" in data:
107
+ logger.log_struct(
108
+ {**data["setup"], "type": "setup"}, severity="INFO"
109
+ )
110
+ logging.info(
111
+ "Received setup message (not forwarding to agent)"
112
+ )
113
+ continue
114
+
115
+ # Forward message to agent engine
116
+ await self.input_queue.put(data)
117
+ else:
118
+ logging.warning(
119
+ f"Received unexpected JSON structure from client: {data}"
120
+ )
121
+
122
+ elif "bytes" in message:
123
+ # Handle binary data
124
+ await self.input_queue.put({"binary_data": message["bytes"]})
125
+
126
+ else:
127
+ logging.warning(
128
+ f"Received unexpected message type from client: {message}"
129
+ )
130
+
131
+ except ConnectionClosedError as e:
132
+ logging.warning(f"Client closed connection: {e}")
133
+ break
134
+ except json.JSONDecodeError as e:
135
+ logging.error(f"Error parsing JSON from client: {e}")
136
+ break
137
+ except Exception as e:
138
+ logging.error(f"Error receiving from client: {e!s}")
139
+ break
140
+
141
+ async def run_agent(self) -> None:
142
+ """Run the agent with the input queue using bidi_stream_query protocol."""
143
+ try:
144
+ # Send setupComplete immediately
145
+ setup_complete_response: dict = {"setupComplete": {}}
146
+ await self.websocket.send_json(setup_complete_response)
147
+
148
+ # Wait for first request with user_id
149
+ first_request = await self.input_queue.get()
150
+ self.user_id = first_request.get("user_id")
151
+ if not self.user_id:
152
+ raise ValueError("The first request must have a user_id.")
153
+
154
+ self.session_id = first_request.get("session_id")
155
+ first_live_request = first_request.get("live_request")
156
+
157
+ # Create session if needed
158
+ if not self.session_id:
159
+ session = await session_service.create_session(
160
+ app_name="live-app",
161
+ user_id=self.user_id,
162
+ )
163
+ self.session_id = session.id
164
+
165
+ # Create LiveRequestQueue
166
+ live_request_queue = LiveRequestQueue()
167
+
168
+ # Add first live request if present
169
+ if first_live_request and isinstance(first_live_request, dict):
170
+ live_request_queue.send(LiveRequest.model_validate(first_live_request))
171
+
172
+ # Forward requests from input_queue to live_request_queue
173
+ async def _forward_requests() -> None:
174
+ while True:
175
+ request = await self.input_queue.get()
176
+ live_request = LiveRequest.model_validate(request)
177
+ live_request_queue.send(live_request)
178
+
179
+ # Forward events from agent to websocket
180
+ async def _forward_events() -> None:
181
+ events_async = runner.run_live(
182
+ user_id=self.user_id,
183
+ session_id=self.session_id,
184
+ live_request_queue=live_request_queue,
185
+ )
186
+ async for event in events_async:
187
+ event_dict = _utils.dump_event_for_json(event)
188
+ await self.websocket.send_json(event_dict)
189
+
190
+ # Check for error responses
191
+ if isinstance(event_dict, dict) and "error" in event_dict:
192
+ logging.error(f"Agent error: {event_dict['error']}")
193
+ break
194
+
195
+ # Run both tasks
196
+ requests_task = asyncio.create_task(_forward_requests())
197
+
198
+ try:
199
+ await _forward_events()
200
+ finally:
201
+ requests_task.cancel()
202
+ try:
203
+ await requests_task
204
+ except asyncio.CancelledError:
205
+ pass
206
+
207
+ except Exception as e:
208
+ logging.error(f"Error in agent: {e}")
209
+ await self.websocket.send_json({"error": str(e)})
210
+
211
+
212
+ def get_connect_and_run_callable(websocket: WebSocket) -> Callable:
213
+ """Create a callable that handles agent connection with retry logic.
214
+
215
+ Args:
216
+ websocket: The client websocket connection
217
+
218
+ Returns:
219
+ Callable: An async function that establishes and manages the agent connection
220
+ """
221
+
222
+ async def on_backoff(details: backoff._typing.Details) -> None:
223
+ await websocket.send_json(
224
+ {
225
+ "status": f"Model connection error, retrying in {details['wait']} seconds..."
226
+ }
227
+ )
228
+
229
+ @backoff.on_exception(
230
+ backoff.expo, ConnectionClosedError, max_tries=10, on_backoff=on_backoff
231
+ )
232
+ async def connect_and_run() -> None:
233
+ logging.info("Starting ADK agent")
234
+ session = AgentSession(websocket)
235
+
236
+ logging.info("Starting bidirectional communication with agent")
237
+ await asyncio.gather(
238
+ session.receive_from_client(),
239
+ session.run_agent(),
240
+ )
241
+
242
+ return connect_and_run
243
+
244
+
245
+ @app.websocket("/ws")
246
+ async def websocket_endpoint(websocket: WebSocket) -> None:
247
+ """Handle new websocket connections."""
248
+ await websocket.accept()
249
+ connect_and_run = get_connect_and_run_callable(websocket)
250
+ await connect_and_run()
251
+
252
+
253
+ @app.get("/")
254
+ async def serve_frontend_root() -> FileResponse:
255
+ """Serve the frontend index.html at the root path."""
256
+ index_file = frontend_build_dir / "index.html"
257
+ if index_file.exists():
258
+ return FileResponse(str(index_file))
259
+ raise HTTPException(
260
+ status_code=404,
261
+ detail="Frontend not built. Run 'npm run build' in the frontend directory.",
262
+ )
263
+
264
+
265
+ @app.get("/{full_path:path}")
266
+ async def serve_frontend_spa(full_path: str) -> FileResponse:
267
+ """Catch-all route to serve the frontend for SPA routing.
268
+
269
+ This ensures that client-side routes are handled by the React app.
270
+ Excludes API routes (ws, feedback) and static assets.
271
+ """
272
+ # Don't intercept API routes
273
+ if full_path.startswith(("ws", "feedback", "static", "api")):
274
+ raise HTTPException(status_code=404, detail="Not found")
275
+
276
+ # Serve index.html for all other routes (SPA routing)
277
+ index_file = frontend_build_dir / "index.html"
278
+ if index_file.exists():
279
+ return FileResponse(str(index_file))
280
+ raise HTTPException(
281
+ status_code=404,
282
+ detail="Frontend not built. Run 'npm run build' in the frontend directory.",
283
+ )
284
+ {% elif cookiecutter.is_adk %}
285
+ import os
286
+ {%- if cookiecutter.is_a2a %}
287
+ from collections.abc import AsyncIterator
288
+ from contextlib import asynccontextmanager
289
+ {%- endif %}
290
+ {%- if cookiecutter.session_type == "cloud_sql" %}
291
+ from urllib.parse import quote
292
+ {%- endif %}
293
+
294
+ import google.auth
295
+ {%- if cookiecutter.is_a2a %}
296
+ from a2a.server.apps import A2AFastAPIApplication
297
+ from a2a.server.request_handlers import DefaultRequestHandler
298
+ from a2a.server.tasks import InMemoryTaskStore
299
+ from a2a.types import AgentCapabilities, AgentCard
300
+ from a2a.utils.constants import (
301
+ AGENT_CARD_WELL_KNOWN_PATH,
302
+ EXTENDED_AGENT_CARD_PATH,
303
+ )
304
+ {%- endif %}
305
+ from fastapi import FastAPI
306
+ {%- if cookiecutter.is_a2a %}
307
+ from google.adk.a2a.executor.a2a_agent_executor import A2aAgentExecutor
308
+ from google.adk.a2a.utils.agent_card_builder import AgentCardBuilder
309
+ from google.adk.artifacts.gcs_artifact_service import GcsArtifactService
310
+ from google.adk.runners import Runner
311
+ from google.adk.sessions import InMemorySessionService
312
+ {%- else %}
313
+ from google.adk.cli.fast_api import get_fast_api_app
314
+ {%- endif %}
315
+ from google.cloud import logging as google_cloud_logging
316
+ {% if cookiecutter.session_type == "agent_engine" -%}
317
+ from vertexai import agent_engines
318
+ {% endif %}
319
+
320
+ {%- if cookiecutter.is_a2a %}
321
+ from {{cookiecutter.agent_directory}}.agent import app as adk_app
322
+ {%- endif %}
323
+ from {{cookiecutter.agent_directory}}.app_utils.gcs import create_bucket_if_not_exists
324
+ from {{cookiecutter.agent_directory}}.app_utils.telemetry import setup_telemetry
325
+ from {{cookiecutter.agent_directory}}.app_utils.typing import Feedback
326
+
327
+ setup_telemetry()
328
+ _, project_id = google.auth.default()
329
+ logging_client = google_cloud_logging.Client()
330
+ logger = logging_client.logger(__name__)
331
+ {%- if not cookiecutter.is_a2a %}
332
+ allow_origins = (
333
+ os.getenv("ALLOW_ORIGINS", "").split(",") if os.getenv("ALLOW_ORIGINS") else None
334
+ )
335
+ {%- endif %}
336
+
337
+ # Artifact bucket for ADK
338
+ bucket_name = f"gs://{project_id}-{{cookiecutter.project_name}}-logs"
339
+ create_bucket_if_not_exists(
340
+ bucket_name=bucket_name, project=project_id, location="us-central1"
341
+ )
342
+
343
+ {%- if cookiecutter.is_a2a %}
344
+
345
+ runner = Runner(
346
+ app=adk_app,
347
+ artifact_service=GcsArtifactService(bucket_name=bucket_name),
348
+ session_service=InMemorySessionService(),
349
+ )
350
+
351
+ request_handler = DefaultRequestHandler(
352
+ agent_executor=A2aAgentExecutor(runner=runner), task_store=InMemoryTaskStore()
353
+ )
354
+
355
+ A2A_RPC_PATH = f"/a2a/{adk_app.name}"
356
+
357
+
358
+ async def build_dynamic_agent_card() -> AgentCard:
359
+ """Builds the Agent Card dynamically from the root_agent."""
360
+ agent_card_builder = AgentCardBuilder(
361
+ agent=adk_app.root_agent,
362
+ capabilities=AgentCapabilities(streaming=True),
363
+ rpc_url=f"{os.getenv('APP_URL', 'http://0.0.0.0:8000')}{A2A_RPC_PATH}",
364
+ agent_version=os.getenv("AGENT_VERSION", "0.1.0"),
365
+ )
366
+ agent_card = await agent_card_builder.build()
367
+ return agent_card
368
+
369
+
370
+ @asynccontextmanager
371
+ async def lifespan(app_instance: FastAPI) -> AsyncIterator[None]:
372
+ agent_card = await build_dynamic_agent_card()
373
+ a2a_app = A2AFastAPIApplication(agent_card=agent_card, http_handler=request_handler)
374
+ a2a_app.add_routes_to_app(
375
+ app_instance,
376
+ agent_card_url=f"{A2A_RPC_PATH}{AGENT_CARD_WELL_KNOWN_PATH}",
377
+ rpc_url=A2A_RPC_PATH,
378
+ extended_agent_card_url=f"{A2A_RPC_PATH}{EXTENDED_AGENT_CARD_PATH}",
379
+ )
380
+ yield
381
+
382
+
383
+ app = FastAPI(
384
+ title="{{cookiecutter.project_name}}",
385
+ description="API for interacting with the Agent {{cookiecutter.project_name}}",
386
+ lifespan=lifespan,
387
+ )
388
+ {%- else %}
389
+
390
+ AGENT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
391
+
392
+ {%- if cookiecutter.session_type == "cloud_sql" %}
393
+ # Cloud SQL session configuration
394
+ db_user = os.environ.get("DB_USER", "postgres")
395
+ db_name = os.environ.get("DB_NAME", "postgres")
396
+ db_pass = os.environ.get("DB_PASS")
397
+ instance_connection_name = os.environ.get("INSTANCE_CONNECTION_NAME")
398
+
399
+ session_service_uri = None
400
+ if instance_connection_name and db_pass:
401
+ # Use Unix socket for Cloud SQL
402
+ # URL-encode username and password to handle special characters (e.g., '[', '?', '#', '$')
403
+ # These characters can cause URL parsing errors, especially '[' which triggers IPv6 validation
404
+ encoded_user = quote(db_user, safe="")
405
+ encoded_pass = quote(db_pass, safe="")
406
+ # URL-encode the connection name to prevent colons from being misinterpreted
407
+ encoded_instance = instance_connection_name.replace(":", "%3A")
408
+
409
+ session_service_uri = (
410
+ f"postgresql+psycopg2://{encoded_user}:{encoded_pass}@"
411
+ f"/{db_name}"
412
+ f"?host=/cloudsql/{encoded_instance}"
413
+ )
414
+ {%- elif cookiecutter.session_type == "agent_engine" %}
415
+ # Agent Engine session configuration
416
+ # Check if we should use in-memory session for testing (set USE_IN_MEMORY_SESSION=true for E2E tests)
417
+ use_in_memory_session = os.environ.get("USE_IN_MEMORY_SESSION", "").lower() in (
418
+ "true",
419
+ "1",
420
+ "yes",
421
+ )
422
+
423
+ if use_in_memory_session:
424
+ # Use in-memory session for local testing
425
+ session_service_uri = None
426
+ else:
427
+ # Use environment variable for agent name, default to project name
428
+ agent_name = os.environ.get(
429
+ "AGENT_ENGINE_SESSION_NAME", "{{cookiecutter.project_name}}"
430
+ )
431
+
432
+ # Check if an agent with this name already exists
433
+ existing_agents = list(agent_engines.list(filter=f"display_name={agent_name}"))
434
+
435
+ if existing_agents:
436
+ # Use the existing agent
437
+ agent_engine = existing_agents[0]
438
+ else:
439
+ # Create a new agent if none exists
440
+ agent_engine = agent_engines.create(display_name=agent_name)
441
+
442
+ session_service_uri = f"agentengine://{agent_engine.resource_name}"
443
+ {%- else %}
444
+ # In-memory session configuration - no persistent storage
445
+ session_service_uri = None
446
+ {%- endif %}
447
+
448
+ app: FastAPI = get_fast_api_app(
449
+ agents_dir=AGENT_DIR,
450
+ web=True,
451
+ artifact_service_uri=bucket_name,
452
+ allow_origins=allow_origins,
453
+ session_service_uri=session_service_uri,
454
+ otel_to_cloud=True,
455
+ )
456
+ app.title = "{{cookiecutter.project_name}}"
457
+ app.description = "API for interacting with the Agent {{cookiecutter.project_name}}"
458
+ {%- endif %}
459
+ {% else %}
460
+ import os
461
+ from collections.abc import AsyncIterator
462
+ from contextlib import asynccontextmanager
463
+
464
+ from a2a.server.apps import A2AFastAPIApplication
465
+ from a2a.server.request_handlers import DefaultRequestHandler
466
+ from a2a.server.tasks import InMemoryTaskStore
467
+ from a2a.types import AgentCapabilities, AgentCard, AgentSkill
468
+ from a2a.utils.constants import (
469
+ AGENT_CARD_WELL_KNOWN_PATH,
470
+ EXTENDED_AGENT_CARD_PATH,
471
+ )
472
+ from fastapi import FastAPI
473
+ from google.cloud import logging as google_cloud_logging
474
+
475
+ from {{cookiecutter.agent_directory}}.agent import root_agent
476
+ from {{cookiecutter.agent_directory}}.app_utils.executor.a2a_agent_executor import (
477
+ LangGraphAgentExecutor,
478
+ )
479
+ from {{cookiecutter.agent_directory}}.app_utils.telemetry import setup_telemetry
480
+ from {{cookiecutter.agent_directory}}.app_utils.typing import Feedback
481
+
482
+ setup_telemetry()
483
+
484
+ request_handler = DefaultRequestHandler(
485
+ agent_executor=LangGraphAgentExecutor(graph=root_agent),
486
+ task_store=InMemoryTaskStore(),
487
+ )
488
+
489
+ A2A_RPC_PATH = "/a2a/{{cookiecutter.agent_directory}}"
490
+
491
+
492
+ def build_agent_card() -> AgentCard:
493
+ """Builds the Agent Card for the LangGraph agent."""
494
+ skill = AgentSkill(
495
+ id="root_agent-get_weather",
496
+ name="get_weather",
497
+ description="Simulates a web search. Use it get information on weather.",
498
+ tags=["llm", "tools"],
499
+ examples=["What's the weather in San Francisco?"],
500
+ )
501
+ agent_card = AgentCard(
502
+ name="root_agent",
503
+ description="API for interacting with the Agent {{cookiecutter.project_name}}",
504
+ url=f"{os.getenv('APP_URL', 'http://0.0.0.0:8000')}{A2A_RPC_PATH}",
505
+ version=os.getenv("AGENT_VERSION", "0.1.0"),
506
+ default_input_modes=["text/plain"],
507
+ default_output_modes=["text/plain"],
508
+ capabilities=AgentCapabilities(streaming=True),
509
+ skills=[skill],
510
+ )
511
+ return agent_card
512
+
513
+
514
+ @asynccontextmanager
515
+ async def lifespan(app_instance: FastAPI) -> AsyncIterator[None]:
516
+ agent_card = build_agent_card()
517
+ a2a_app = A2AFastAPIApplication(agent_card=agent_card, http_handler=request_handler)
518
+ a2a_app.add_routes_to_app(
519
+ app_instance,
520
+ agent_card_url=f"{A2A_RPC_PATH}{AGENT_CARD_WELL_KNOWN_PATH}",
521
+ rpc_url=A2A_RPC_PATH,
522
+ extended_agent_card_url=f"{A2A_RPC_PATH}{EXTENDED_AGENT_CARD_PATH}",
523
+ )
524
+ yield
525
+
526
+
527
+ # Initialize FastAPI app and logging
528
+ app = FastAPI(
529
+ title="{{cookiecutter.project_name}}",
530
+ description="API for interacting with the Agent {{cookiecutter.project_name}}",
531
+ lifespan=lifespan,
532
+ )
533
+
534
+ logging_client = google_cloud_logging.Client()
535
+ logger = logging_client.logger(__name__)
536
+ {% endif %}
537
+
538
+ @app.post("/feedback")
539
+ def collect_feedback(feedback: Feedback) -> dict[str, str]:
540
+ """Collect and log feedback.
541
+
542
+ Args:
543
+ feedback: The feedback data to log
544
+
545
+ Returns:
546
+ Success message
547
+ """
548
+ logger.log_struct(feedback.model_dump(), severity="INFO")
549
+ return {"status": "success"}
550
+
551
+
552
+ # Main execution
553
+ if __name__ == "__main__":
554
+ import uvicorn
555
+
556
+ uvicorn.run(app, host="0.0.0.0", port=8000)