agent-starter-pack 0.15.7__py3-none-any.whl → 0.17.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.

Potentially problematic release.


This version of agent-starter-pack might be problematic. Click here for more details.

Files changed (195) hide show
  1. {agents → agent_starter_pack/agents}/adk_base/.template/templateconfig.yaml +1 -1
  2. {agents/live_api → agent_starter_pack/agents/adk_live}/.template/templateconfig.yaml +5 -7
  3. agent_starter_pack/agents/adk_live/README.md +32 -0
  4. agent_starter_pack/agents/adk_live/app/agent.py +48 -0
  5. agent_starter_pack/agents/adk_live/tests/unit/test_dummy.py +38 -0
  6. {agents → agent_starter_pack/agents}/agentic_rag/.template/templateconfig.yaml +1 -1
  7. agent_starter_pack/agents/crewai_coding_crew/app/agent.py +47 -0
  8. agent_starter_pack/agents/langgraph_base_react/app/agent.py +34 -0
  9. {src → agent_starter_pack}/base_template/GEMINI.md +1 -1
  10. {src → agent_starter_pack}/base_template/Makefile +130 -61
  11. {src → agent_starter_pack}/base_template/README.md +6 -6
  12. {src → agent_starter_pack}/base_template/deployment/terraform/dev/apis.tf +1 -1
  13. {src → agent_starter_pack}/base_template/deployment/terraform/dev/log_sinks.tf +31 -25
  14. {src → agent_starter_pack}/base_template/deployment/terraform/dev/providers.tf +1 -1
  15. {src → agent_starter_pack}/base_template/deployment/terraform/dev/variables.tf +1 -1
  16. {src → agent_starter_pack}/base_template/deployment/terraform/github.tf +14 -0
  17. {src → agent_starter_pack}/base_template/deployment/terraform/locals.tf +1 -1
  18. {src → agent_starter_pack}/base_template/deployment/terraform/log_sinks.tf +37 -28
  19. {src → agent_starter_pack}/base_template/deployment/terraform/providers.tf +1 -1
  20. {src → agent_starter_pack}/base_template/deployment/terraform/variables.tf +1 -1
  21. {src → agent_starter_pack}/base_template/deployment/terraform/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}build_triggers.tf{% else %}unused_build_triggers.tf{% endif %} +4 -2
  22. {src → agent_starter_pack}/base_template/pyproject.toml +22 -21
  23. {src → agent_starter_pack}/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/deploy-to-prod.yaml +5 -5
  24. {src → agent_starter_pack}/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/pr_checks.yaml +3 -3
  25. {src → agent_starter_pack}/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/staging.yaml +74 -11
  26. {src → agent_starter_pack}/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/deploy-to-prod.yaml +6 -6
  27. {src → agent_starter_pack}/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/pr_checks.yaml +4 -4
  28. {src → agent_starter_pack}/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/staging.yaml +95 -13
  29. {src → agent_starter_pack}/base_template/{{cookiecutter.agent_directory}}/utils/tracing.py +1 -1
  30. {src → agent_starter_pack}/base_template/{{cookiecutter.agent_directory}}/utils/typing.py +4 -4
  31. {src → agent_starter_pack}/cli/commands/setup_cicd.py +1 -1
  32. {src → agent_starter_pack}/cli/main.py +2 -2
  33. {src → agent_starter_pack}/cli/utils/gcp.py +1 -1
  34. {src → agent_starter_pack}/cli/utils/remote_template.py +12 -9
  35. {src → agent_starter_pack}/cli/utils/template.py +19 -15
  36. agent_starter_pack/deployment_targets/agent_engine/deployment/terraform/{% if not cookiecutter.is_adk_live %}service.tf{% else %}unused_service.tf{% endif %} +82 -0
  37. agent_starter_pack/deployment_targets/agent_engine/tests/integration/test_agent_engine_app.py +387 -0
  38. agent_starter_pack/deployment_targets/agent_engine/tests/load_test/README.md +84 -0
  39. agent_starter_pack/deployment_targets/agent_engine/tests/load_test/load_test.py +255 -0
  40. {src → agent_starter_pack}/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/agent_engine_app.py +40 -14
  41. {src → agent_starter_pack}/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/utils/deployment.py +13 -4
  42. agent_starter_pack/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/utils/{% if cookiecutter.is_adk_live %}expose_app.py{% else %}unused_expose_app.py{% endif %} +520 -0
  43. {src → agent_starter_pack}/deployment_targets/cloud_run/Dockerfile +3 -3
  44. {src → agent_starter_pack}/deployment_targets/cloud_run/deployment/terraform/dev/service.tf +4 -4
  45. {src → agent_starter_pack}/deployment_targets/cloud_run/deployment/terraform/service.tf +7 -7
  46. {src → agent_starter_pack}/deployment_targets/cloud_run/tests/integration/test_server_e2e.py +207 -5
  47. {src → agent_starter_pack}/deployment_targets/cloud_run/tests/load_test/README.md +82 -0
  48. agent_starter_pack/deployment_targets/cloud_run/tests/load_test/load_test.py +249 -0
  49. {src → agent_starter_pack}/deployment_targets/cloud_run/{{cookiecutter.agent_directory}}/server.py +190 -146
  50. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/package-lock.json +39 -1007
  51. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/package.json +1 -9
  52. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/App.tsx +1 -1
  53. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/logger/Logger.tsx +8 -3
  54. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/logger/logger.scss +26 -0
  55. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/side-panel/SidePanel.tsx +11 -5
  56. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/side-panel/side-panel.scss +146 -115
  57. agent_starter_pack/frontends/adk_live_react/frontend/src/components/transcription-preview/TranscriptionPreview.tsx +106 -0
  58. agent_starter_pack/frontends/adk_live_react/frontend/src/components/transcription-preview/transcription-preview.scss +150 -0
  59. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-live-api.ts +8 -2
  60. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/multimodal-live-types.ts +38 -2
  61. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/audio-recorder.ts +1 -1
  62. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/audio-streamer.ts +1 -1
  63. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/multimodal-live-client.ts +204 -23
  64. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/utils.ts +27 -5
  65. {src → agent_starter_pack}/frontends/streamlit/frontend/utils/local_chat_history.py +2 -0
  66. {src → agent_starter_pack}/resources/docs/adk-cheatsheet.md +5 -5
  67. agent_starter_pack/resources/idx/.idx/dev.nix +64 -0
  68. agent_starter_pack/resources/idx/idx-template.json +6 -0
  69. {src → agent_starter_pack}/resources/idx/idx-template.nix +2 -3
  70. {src → agent_starter_pack}/resources/locks/uv-adk_base-agent_engine.lock +1079 -954
  71. {src → agent_starter_pack}/resources/locks/uv-adk_base-cloud_run.lock +1441 -1309
  72. agent_starter_pack/resources/locks/uv-adk_live-agent_engine.lock +4229 -0
  73. agent_starter_pack/resources/locks/uv-adk_live-cloud_run.lock +4822 -0
  74. {src → agent_starter_pack}/resources/locks/uv-agentic_rag-agent_engine.lock +1107 -997
  75. {src → agent_starter_pack}/resources/locks/uv-agentic_rag-cloud_run.lock +1485 -1368
  76. {src → agent_starter_pack}/resources/locks/uv-crewai_coding_crew-agent_engine.lock +1294 -1297
  77. {src → agent_starter_pack}/resources/locks/uv-crewai_coding_crew-cloud_run.lock +2028 -1807
  78. {src → agent_starter_pack}/resources/locks/uv-langgraph_base_react-agent_engine.lock +1176 -1197
  79. {src → agent_starter_pack}/resources/locks/uv-langgraph_base_react-cloud_run.lock +1947 -1679
  80. {src → agent_starter_pack}/utils/generate_locks.py +12 -7
  81. {src → agent_starter_pack}/utils/lock_utils.py +2 -2
  82. {src → agent_starter_pack}/utils/watch_and_rebuild.py +1 -1
  83. {agent_starter_pack-0.15.7.dist-info → agent_starter_pack-0.17.0.dist-info}/METADATA +17 -18
  84. agent_starter_pack-0.17.0.dist-info/RECORD +179 -0
  85. agent_starter_pack-0.17.0.dist-info/entry_points.txt +2 -0
  86. llm.txt +1 -1
  87. agent_starter_pack-0.15.7.dist-info/RECORD +0 -176
  88. agent_starter_pack-0.15.7.dist-info/entry_points.txt +0 -2
  89. agents/crewai_coding_crew/app/agent.py +0 -86
  90. agents/langgraph_base_react/app/agent.py +0 -73
  91. agents/live_api/README.md +0 -37
  92. agents/live_api/app/agent.py +0 -72
  93. agents/live_api/tests/integration/test_server_e2e.py +0 -260
  94. agents/live_api/tests/load_test/load_test.py +0 -40
  95. agents/live_api/tests/unit/test_server.py +0 -144
  96. src/deployment_targets/agent_engine/tests/integration/test_agent_engine_app.py +0 -186
  97. src/deployment_targets/agent_engine/tests/load_test/README.md +0 -37
  98. src/deployment_targets/agent_engine/tests/load_test/load_test.py +0 -126
  99. src/deployment_targets/cloud_run/tests/load_test/load_test.py +0 -122
  100. src/resources/idx/.idx/dev.nix +0 -50
  101. src/resources/idx/idx-template.json +0 -21
  102. src/resources/locks/uv-live_api-cloud_run.lock +0 -6118
  103. {agents → agent_starter_pack/agents}/README.md +0 -0
  104. {agents → agent_starter_pack/agents}/adk_base/README.md +0 -0
  105. {agents → agent_starter_pack/agents}/adk_base/app/__init__.py +0 -0
  106. {agents → agent_starter_pack/agents}/adk_base/app/agent.py +0 -0
  107. {agents → agent_starter_pack/agents}/adk_base/notebooks/adk_app_testing.ipynb +0 -0
  108. {agents → agent_starter_pack/agents}/adk_base/notebooks/evaluating_adk_agent.ipynb +0 -0
  109. {agents → agent_starter_pack/agents}/adk_base/tests/integration/test_agent.py +0 -0
  110. {agents → agent_starter_pack/agents}/agentic_rag/README.md +0 -0
  111. {agents → agent_starter_pack/agents}/agentic_rag/app/__init__.py +0 -0
  112. {agents → agent_starter_pack/agents}/agentic_rag/app/agent.py +0 -0
  113. {agents → agent_starter_pack/agents}/agentic_rag/app/retrievers.py +0 -0
  114. {agents → agent_starter_pack/agents}/agentic_rag/app/templates.py +0 -0
  115. {agents → agent_starter_pack/agents}/agentic_rag/notebooks/adk_app_testing.ipynb +0 -0
  116. {agents → agent_starter_pack/agents}/agentic_rag/notebooks/evaluating_adk_agent.ipynb +0 -0
  117. {agents → agent_starter_pack/agents}/agentic_rag/tests/integration/test_agent.py +0 -0
  118. {agents → agent_starter_pack/agents}/crewai_coding_crew/.template/templateconfig.yaml +0 -0
  119. {agents → agent_starter_pack/agents}/crewai_coding_crew/README.md +0 -0
  120. {agents → agent_starter_pack/agents}/crewai_coding_crew/app/crew/config/agents.yaml +0 -0
  121. {agents → agent_starter_pack/agents}/crewai_coding_crew/app/crew/config/tasks.yaml +0 -0
  122. {agents → agent_starter_pack/agents}/crewai_coding_crew/app/crew/crew.py +0 -0
  123. {agents → agent_starter_pack/agents}/crewai_coding_crew/notebooks/evaluating_crewai_agent.ipynb +0 -0
  124. {agents → agent_starter_pack/agents}/crewai_coding_crew/notebooks/evaluating_langgraph_agent.ipynb +0 -0
  125. {agents → agent_starter_pack/agents}/crewai_coding_crew/tests/integration/test_agent.py +0 -0
  126. {agents → agent_starter_pack/agents}/langgraph_base_react/.template/templateconfig.yaml +0 -0
  127. {agents → agent_starter_pack/agents}/langgraph_base_react/README.md +0 -0
  128. {agents → agent_starter_pack/agents}/langgraph_base_react/notebooks/evaluating_langgraph_agent.ipynb +0 -0
  129. {agents → agent_starter_pack/agents}/langgraph_base_react/tests/integration/test_agent.py +0 -0
  130. {src → agent_starter_pack}/base_template/.gitignore +0 -0
  131. {src → agent_starter_pack}/base_template/deployment/README.md +0 -0
  132. {src → agent_starter_pack}/base_template/deployment/terraform/apis.tf +0 -0
  133. {src → agent_starter_pack}/base_template/deployment/terraform/dev/iam.tf +0 -0
  134. {src → agent_starter_pack}/base_template/deployment/terraform/dev/storage.tf +0 -0
  135. {src → agent_starter_pack}/base_template/deployment/terraform/dev/vars/env.tfvars +0 -0
  136. {src → agent_starter_pack}/base_template/deployment/terraform/iam.tf +0 -0
  137. {src → agent_starter_pack}/base_template/deployment/terraform/service_accounts.tf +0 -0
  138. {src → agent_starter_pack}/base_template/deployment/terraform/storage.tf +0 -0
  139. {src → agent_starter_pack}/base_template/deployment/terraform/vars/env.tfvars +0 -0
  140. {src → agent_starter_pack}/base_template/deployment/terraform/{% if cookiecutter.cicd_runner == 'github_actions' %}wif.tf{% else %}unused_wif.tf{% endif %} +0 -0
  141. {src → agent_starter_pack}/base_template/tests/unit/test_dummy.py +0 -0
  142. {src → agent_starter_pack}/base_template/{{cookiecutter.agent_directory}}/utils/gcs.py +0 -0
  143. {src → agent_starter_pack}/cli/commands/create.py +0 -0
  144. {src → agent_starter_pack}/cli/commands/enhance.py +0 -0
  145. {src → agent_starter_pack}/cli/commands/list.py +0 -0
  146. {src → agent_starter_pack}/cli/utils/__init__.py +0 -0
  147. {src → agent_starter_pack}/cli/utils/cicd.py +0 -0
  148. {src → agent_starter_pack}/cli/utils/datastores.py +0 -0
  149. {src → agent_starter_pack}/cli/utils/logging.py +0 -0
  150. {src → agent_starter_pack}/cli/utils/version.py +0 -0
  151. {src → agent_starter_pack}/data_ingestion/README.md +0 -0
  152. {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/components/ingest_data.py +0 -0
  153. {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/components/process_data.py +0 -0
  154. {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/pipeline.py +0 -0
  155. {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/submit_pipeline.py +0 -0
  156. {src → agent_starter_pack}/data_ingestion/pyproject.toml +0 -0
  157. {src → agent_starter_pack}/data_ingestion/uv.lock +0 -0
  158. {src → agent_starter_pack}/deployment_targets/agent_engine/deployment_metadata.json +0 -0
  159. {src → agent_starter_pack}/deployment_targets/agent_engine/notebooks/intro_agent_engine.ipynb +0 -0
  160. {src → agent_starter_pack}/deployment_targets/agent_engine/tests/load_test/.results/.placeholder +0 -0
  161. {src → agent_starter_pack}/deployment_targets/cloud_run/tests/load_test/.results/.placeholder +0 -0
  162. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/public/favicon.ico +0 -0
  163. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/public/index.html +0 -0
  164. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/public/robots.txt +0 -0
  165. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/App.scss +0 -0
  166. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/App.test.tsx +0 -0
  167. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/audio-pulse/AudioPulse.tsx +0 -0
  168. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/audio-pulse/audio-pulse.scss +0 -0
  169. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/logger/mock-logs.ts +0 -0
  170. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/contexts/LiveAPIContext.tsx +0 -0
  171. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-media-stream-mux.ts +0 -0
  172. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-screen-capture.ts +0 -0
  173. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-webcam.ts +0 -0
  174. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/index.css +0 -0
  175. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/index.tsx +0 -0
  176. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/react-app-env.d.ts +0 -0
  177. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/reportWebVitals.ts +0 -0
  178. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/setupTests.ts +0 -0
  179. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/audioworklet-registry.ts +0 -0
  180. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/store-logger.ts +0 -0
  181. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/worklets/audio-processing.ts +0 -0
  182. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/worklets/vol-meter.ts +0 -0
  183. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/tsconfig.json +0 -0
  184. {src → agent_starter_pack}/frontends/streamlit/frontend/side_bar.py +0 -0
  185. {src → agent_starter_pack}/frontends/streamlit/frontend/streamlit_app.py +0 -0
  186. {src → agent_starter_pack}/frontends/streamlit/frontend/style/app_markdown.py +0 -0
  187. {src → agent_starter_pack}/frontends/streamlit/frontend/utils/chat_utils.py +0 -0
  188. {src → agent_starter_pack}/frontends/streamlit/frontend/utils/message_editing.py +0 -0
  189. {src → agent_starter_pack}/frontends/streamlit/frontend/utils/multimodal_utils.py +0 -0
  190. {src → agent_starter_pack}/frontends/streamlit/frontend/utils/stream_handler.py +0 -0
  191. {src → agent_starter_pack}/frontends/streamlit/frontend/utils/title_summary.py +0 -0
  192. {src → agent_starter_pack}/resources/containers/data_processing/Dockerfile +0 -0
  193. {src → agent_starter_pack}/resources/containers/e2e-tests/Dockerfile +0 -0
  194. {agent_starter_pack-0.15.7.dist-info → agent_starter_pack-0.17.0.dist-info}/WHEEL +0 -0
  195. {agent_starter_pack-0.15.7.dist-info → agent_starter_pack-0.17.0.dist-info}/licenses/LICENSE +0 -0
@@ -11,26 +11,33 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
- {% if cookiecutter.agent_name == "live_api" %}
14
+ {% if cookiecutter.agent_name == "adk_live" %}
15
15
  import asyncio
16
16
  import json
17
17
  import logging
18
18
  from collections.abc import Callable
19
19
  from pathlib import Path
20
- from typing import Any, Literal
21
20
 
22
21
  import backoff
22
+ import google.auth
23
23
  from fastapi import FastAPI, HTTPException, WebSocket
24
24
  from fastapi.middleware.cors import CORSMiddleware
25
25
  from fastapi.responses import FileResponse
26
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
27
32
  from google.cloud import logging as google_cloud_logging
28
- from google.genai import types
29
- from google.genai.types import LiveServerToolCall
30
- from pydantic import BaseModel
33
+ from opentelemetry import trace
34
+ from opentelemetry.sdk.trace import TracerProvider, export
35
+ from vertexai.agent_engines import _utils
31
36
  from websockets.exceptions import ConnectionClosedError
32
37
 
33
- from .agent import MODEL_ID, genai_client, live_connect_config, tool_functions
38
+ from .agent import root_agent
39
+ from .utils.tracing import CloudTraceLoggingSpanExporter
40
+ from .utils.typing import Feedback
34
41
 
35
42
  app = FastAPI()
36
43
  app.add_middleware(
@@ -55,118 +62,169 @@ logging_client = google_cloud_logging.Client()
55
62
  logger = logging_client.logger(__name__)
56
63
  logging.basicConfig(level=logging.INFO)
57
64
 
65
+ _, project_id = google.auth.default()
66
+ provider = TracerProvider()
67
+ processor = export.BatchSpanProcessor(
68
+ CloudTraceLoggingSpanExporter(project_id=project_id)
69
+ )
70
+ provider.add_span_processor(processor)
71
+ trace.set_tracer_provider(provider)
72
+
73
+
74
+ # Initialize ADK services
75
+ session_service = InMemorySessionService()
76
+ artifact_service = InMemoryArtifactService()
77
+ memory_service = InMemoryMemoryService()
78
+
79
+ # Initialize ADK runner
80
+ runner = Runner(
81
+ agent=root_agent,
82
+ session_service=session_service,
83
+ artifact_service=artifact_service,
84
+ memory_service=memory_service,
85
+ app_name="live-app",
86
+ )
58
87
 
59
- class GeminiSession:
60
- """Manages bidirectional communication between a client and the Gemini model."""
61
88
 
62
- def __init__(
63
- self, session: Any, websocket: WebSocket, tool_functions: dict[str, Callable]
64
- ) -> None:
65
- """Initialize the Gemini session.
89
+ class AgentSession:
90
+ """Manages bidirectional communication between a client and the agent."""
91
+
92
+ def __init__(self, websocket: WebSocket) -> None:
93
+ """Initialize the agent session.
66
94
 
67
95
  Args:
68
- session: The Gemini session
69
96
  websocket: The client websocket connection
70
- user_id: Unique identifier for this client
71
- tool_functions: Dictionary of available tool functions
72
97
  """
73
- self.session = session
74
98
  self.websocket = websocket
75
- self.run_id = "n/a"
76
- self.user_id = "n/a"
77
- self.tool_functions = tool_functions
78
- self._tool_tasks: list[asyncio.Task] = []
99
+ self.input_queue: asyncio.Queue[dict] = asyncio.Queue()
100
+ self.user_id: str | None = None
101
+ self.session_id: str | None = None
79
102
 
80
103
  async def receive_from_client(self) -> None:
81
- """Listen for and process messages from the client.
82
-
83
- Continuously receives messages and forwards audio data to Gemini.
84
- Handles connection errors gracefully.
85
- """
104
+ """Listen for messages from the client and put them in the queue."""
86
105
  while True:
87
106
  try:
88
- data = await self.websocket.receive_json()
89
-
90
- if isinstance(data, dict) and (
91
- "realtimeInput" in data or "clientContent" in data
92
- ):
93
- await self.session._ws.send(json.dumps(data))
94
- elif "setup" in data:
95
- self.run_id = data["setup"]["run_id"]
96
- self.user_id = data["setup"]["user_id"]
97
- logger.log_struct(
98
- {**data["setup"], "type": "setup"}, severity="INFO"
99
- )
107
+ message = await self.websocket.receive()
108
+
109
+ if "text" in message:
110
+ data = json.loads(message["text"])
111
+
112
+ if isinstance(data, dict):
113
+ # Skip setup messages - they're for backend logging only
114
+ if "setup" in data:
115
+ logger.log_struct(
116
+ {**data["setup"], "type": "setup"}, severity="INFO"
117
+ )
118
+ logging.info(
119
+ "Received setup message (not forwarding to agent)"
120
+ )
121
+ continue
122
+
123
+ # Forward message to agent engine
124
+ await self.input_queue.put(data)
125
+ else:
126
+ logging.warning(
127
+ f"Received unexpected JSON structure from client: {data}"
128
+ )
129
+
130
+ elif "bytes" in message:
131
+ # Handle binary data
132
+ await self.input_queue.put({"binary_data": message["bytes"]})
133
+
100
134
  else:
101
- logging.warning(f"Received unexpected input from client: {data}")
135
+ logging.warning(
136
+ f"Received unexpected message type from client: {message}"
137
+ )
138
+
102
139
  except ConnectionClosedError as e:
103
- logging.warning(f"Client {self.user_id} closed connection: {e}")
140
+ logging.warning(f"Client closed connection: {e}")
141
+ break
142
+ except json.JSONDecodeError as e:
143
+ logging.error(f"Error parsing JSON from client: {e}")
104
144
  break
105
145
  except Exception as e:
106
- logging.error(f"Error receiving from client {self.user_id}: {e!s}")
146
+ logging.error(f"Error receiving from client: {e!s}")
107
147
  break
108
148
 
109
- def _get_func(self, action_label: str | None) -> Callable | None:
110
- """Get the tool function for a given action label."""
111
- if action_label is None or action_label == "":
112
- return None
113
- return self.tool_functions.get(action_label)
114
-
115
- async def _handle_tool_call(
116
- self, session: Any, tool_call: LiveServerToolCall
117
- ) -> None:
118
- """Process tool calls from Gemini and send back responses."""
119
- if tool_call.function_calls is None:
120
- logging.debug("No function calls in tool_call")
121
- return
122
-
123
- for fc in tool_call.function_calls:
124
- logging.debug(f"Calling tool function: {fc.name} with args: {fc.args}")
125
- func = self._get_func(fc.name)
126
- if func is None:
127
- logging.error(f"Function {fc.name} not found")
128
- continue
129
- args = fc.args if fc.args is not None else {}
130
-
131
- # Handle both async and sync functions appropriately
132
- if asyncio.iscoroutinefunction(func):
133
- # Function is already async
134
- response = await func(**args)
135
- else:
136
- # Run sync function in a thread pool to avoid blocking
137
- response = await asyncio.to_thread(func, **args)
138
-
139
- tool_response = types.LiveClientToolResponse(
140
- function_responses=[
141
- types.FunctionResponse(name=fc.name, id=fc.id, response=response)
142
- ]
143
- )
144
- logging.debug(f"Tool response: {tool_response}")
145
- await session.send(input=tool_response)
146
-
147
- async def receive_from_gemini(self) -> None:
148
- """Listen for and process messages from Gemini without blocking."""
149
- while result := await self.session._ws.recv(decode=False):
150
- await self.websocket.send_bytes(result)
151
- raw_message = json.loads(result)
152
- if "toolCall" in raw_message:
153
- message = types.LiveServerMessage.model_validate(raw_message)
154
- tool_call = LiveServerToolCall.model_validate(message.tool_call)
155
- # Create a separate task to handle the tool call without blocking
156
- task = asyncio.create_task(
157
- self._handle_tool_call(self.session, tool_call)
149
+ async def run_agent(self) -> None:
150
+ """Run the agent with the input queue using bidi_stream_query protocol."""
151
+ try:
152
+ # Send setupComplete immediately
153
+ setup_complete_response: dict = {"setupComplete": {}}
154
+ await self.websocket.send_json(setup_complete_response)
155
+
156
+ # Wait for first request with user_id
157
+ first_request = await self.input_queue.get()
158
+ self.user_id = first_request.get("user_id")
159
+ if not self.user_id:
160
+ raise ValueError("The first request must have a user_id.")
161
+
162
+ self.session_id = first_request.get("session_id")
163
+ first_live_request = first_request.get("live_request")
164
+
165
+ # Create session if needed
166
+ if not self.session_id:
167
+ session = await session_service.create_session(
168
+ app_name="live-app",
169
+ user_id=self.user_id,
170
+ )
171
+ self.session_id = session.id
172
+
173
+ # Create LiveRequestQueue
174
+ live_request_queue = LiveRequestQueue()
175
+
176
+ # Add first live request if present
177
+ if first_live_request and isinstance(first_live_request, dict):
178
+ live_request_queue.send(LiveRequest.model_validate(first_live_request))
179
+
180
+ # Forward requests from input_queue to live_request_queue
181
+ async def _forward_requests() -> None:
182
+ while True:
183
+ request = await self.input_queue.get()
184
+ live_request = LiveRequest.model_validate(request)
185
+ live_request_queue.send(live_request)
186
+
187
+ # Forward events from agent to websocket
188
+ async def _forward_events() -> None:
189
+ events_async = runner.run_live(
190
+ user_id=self.user_id,
191
+ session_id=self.session_id,
192
+ live_request_queue=live_request_queue,
158
193
  )
159
- self._tool_tasks.append(task)
194
+ async for event in events_async:
195
+ event_dict = _utils.dump_event_for_json(event)
196
+ await self.websocket.send_json(event_dict)
197
+
198
+ # Check for error responses
199
+ if isinstance(event_dict, dict) and "error" in event_dict:
200
+ logging.error(f"Agent error: {event_dict['error']}")
201
+ break
202
+
203
+ # Run both tasks
204
+ requests_task = asyncio.create_task(_forward_requests())
205
+
206
+ try:
207
+ await _forward_events()
208
+ finally:
209
+ requests_task.cancel()
210
+ try:
211
+ await requests_task
212
+ except asyncio.CancelledError:
213
+ pass
214
+
215
+ except Exception as e:
216
+ logging.error(f"Error in agent: {e}")
217
+ await self.websocket.send_json({"error": str(e)})
160
218
 
161
219
 
162
220
  def get_connect_and_run_callable(websocket: WebSocket) -> Callable:
163
- """Create a callable that handles Gemini connection with retry logic.
221
+ """Create a callable that handles agent connection with retry logic.
164
222
 
165
223
  Args:
166
224
  websocket: The client websocket connection
167
225
 
168
226
  Returns:
169
- Callable: An async function that establishes and manages the Gemini connection
227
+ Callable: An async function that establishes and manages the agent connection
170
228
  """
171
229
 
172
230
  async def on_backoff(details: backoff._typing.Details) -> None:
@@ -180,18 +238,14 @@ def get_connect_and_run_callable(websocket: WebSocket) -> Callable:
180
238
  backoff.expo, ConnectionClosedError, max_tries=10, on_backoff=on_backoff
181
239
  )
182
240
  async def connect_and_run() -> None:
183
- async with genai_client.aio.live.connect(
184
- model=MODEL_ID, config=live_connect_config
185
- ) as session:
186
- await websocket.send_json({"status": "Backend is ready for conversation"})
187
- gemini_session = GeminiSession(
188
- session=session, websocket=websocket, tool_functions=tool_functions
189
- )
190
- logging.info("Starting bidirectional communication")
191
- await asyncio.gather(
192
- gemini_session.receive_from_client(),
193
- gemini_session.receive_from_gemini(),
194
- )
241
+ logging.info("Starting ADK agent")
242
+ session = AgentSession(websocket)
243
+
244
+ logging.info("Starting bidirectional communication with agent")
245
+ await asyncio.gather(
246
+ session.receive_from_client(),
247
+ session.run_agent(),
248
+ )
195
249
 
196
250
  return connect_and_run
197
251
 
@@ -204,15 +258,38 @@ async def websocket_endpoint(websocket: WebSocket) -> None:
204
258
  await connect_and_run()
205
259
 
206
260
 
207
- class Feedback(BaseModel):
208
- """Represents feedback for a conversation."""
261
+ @app.get("/")
262
+ async def serve_frontend_root() -> FileResponse:
263
+ """Serve the frontend index.html at the root path."""
264
+ index_file = frontend_build_dir / "index.html"
265
+ if index_file.exists():
266
+ return FileResponse(str(index_file))
267
+ raise HTTPException(
268
+ status_code=404,
269
+ detail="Frontend not built. Run 'npm run build' in the frontend directory.",
270
+ )
271
+
272
+
273
+ @app.get("/{full_path:path}")
274
+ async def serve_frontend_spa(full_path: str) -> FileResponse:
275
+ """Catch-all route to serve the frontend for SPA routing.
276
+
277
+ This ensures that client-side routes are handled by the React app.
278
+ Excludes API routes (ws, feedback) and static assets.
279
+ """
280
+ # Don't intercept API routes
281
+ if full_path.startswith(("ws", "feedback", "static", "api")):
282
+ raise HTTPException(status_code=404, detail="Not found")
209
283
 
210
- score: int | float
211
- text: str | None = ""
212
- run_id: str
213
- user_id: str | None
214
- log_type: Literal["feedback"] = "feedback"
215
- {% elif "adk" in cookiecutter.tags %}
284
+ # Serve index.html for all other routes (SPA routing)
285
+ index_file = frontend_build_dir / "index.html"
286
+ if index_file.exists():
287
+ return FileResponse(str(index_file))
288
+ raise HTTPException(
289
+ status_code=404,
290
+ detail="Frontend not built. Run 'npm run build' in the frontend directory.",
291
+ )
292
+ {% elif cookiecutter.is_adk %}
216
293
  import os
217
294
 
218
295
  import google.auth
@@ -358,7 +435,7 @@ def stream_messages(
358
435
  set_tracing_properties(config)
359
436
  input_dict = input.model_dump()
360
437
 
361
- for data in agent.stream(input_dict, config=config, stream_mode="messages"): # type: ignore[arg-type]
438
+ for data in agent.stream(input_dict, config=config, stream_mode="messages"):
362
439
  yield dumps(data) + "\n"
363
440
 
364
441
 
@@ -397,40 +474,7 @@ def collect_feedback(feedback: Feedback) -> dict[str, str]:
397
474
  """
398
475
  logger.log_struct(feedback.model_dump(), severity="INFO")
399
476
  return {"status": "success"}
400
- {% if cookiecutter.agent_name == "live_api" %}
401
477
 
402
- @app.get("/")
403
- async def serve_frontend_root() -> FileResponse:
404
- """Serve the frontend index.html at the root path."""
405
- index_file = frontend_build_dir / "index.html"
406
- if index_file.exists():
407
- return FileResponse(str(index_file))
408
- raise HTTPException(
409
- status_code=404,
410
- detail="Frontend not built. Run 'npm run build' in the frontend directory.",
411
- )
412
-
413
-
414
- @app.get("/{full_path:path}")
415
- async def serve_frontend_spa(full_path: str) -> FileResponse:
416
- """Catch-all route to serve the frontend for SPA routing.
417
-
418
- This ensures that client-side routes are handled by the React app.
419
- Excludes API routes (ws, feedback) and static assets.
420
- """
421
- # Don't intercept API routes
422
- if full_path.startswith(("ws", "feedback", "static", "api")):
423
- raise HTTPException(status_code=404, detail="Not found")
424
-
425
- # Serve index.html for all other routes (SPA routing)
426
- index_file = frontend_build_dir / "index.html"
427
- if index_file.exists():
428
- return FileResponse(str(index_file))
429
- raise HTTPException(
430
- status_code=404,
431
- detail="Frontend not built. Run 'npm run build' in the frontend directory.",
432
- )
433
- {% endif %}
434
478
 
435
479
  # Main execution
436
480
  if __name__ == "__main__":