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
@@ -76,6 +76,10 @@ export function useLiveAPI({
76
76
  setConnected(false);
77
77
  };
78
78
 
79
+ const onSetupComplete = () => {
80
+ setConnected(true);
81
+ };
82
+
79
83
  const stopAudioStreamer = () => audioStreamerRef.current?.stop();
80
84
 
81
85
  const onAudio = (data: ArrayBuffer) =>
@@ -83,12 +87,14 @@ export function useLiveAPI({
83
87
 
84
88
  client
85
89
  .on("close", onClose)
90
+ .on("setupcomplete", onSetupComplete)
86
91
  .on("interrupted", stopAudioStreamer)
87
92
  .on("audio", onAudio);
88
93
 
89
94
  return () => {
90
95
  client
91
96
  .off("close", onClose)
97
+ .off("setupcomplete", onSetupComplete)
92
98
  .off("interrupted", stopAudioStreamer)
93
99
  .off("audio", onAudio);
94
100
  };
@@ -97,8 +103,8 @@ export function useLiveAPI({
97
103
  const connect = useCallback(async () => {
98
104
  client.disconnect();
99
105
  await client.connect();
100
- setConnected(true);
101
- }, [client, setConnected]);
106
+ // Don't set connected here - wait for setupcomplete event
107
+ }, [client]);
102
108
 
103
109
  const disconnect = useCallback(async () => {
104
110
  client.disconnect();
@@ -94,7 +94,8 @@ export type LiveIncomingMessage =
94
94
  | ToolCallMessage
95
95
  | ToolCallCancellationMessage
96
96
  | SetupCompleteMessage
97
- | ServerContentMessage;
97
+ | ServerContentMessage
98
+ | AdkEvent;
98
99
 
99
100
  export type SetupCompleteMessage = { setupComplete: {} };
100
101
 
@@ -148,7 +149,7 @@ export type StreamingLog = {
148
149
 
149
150
  // Type-Guards
150
151
 
151
- const prop = (a: any, prop: string, kind: string = "object") =>
152
+ const prop = (a: any, prop: string) =>
152
153
  typeof a === "object" && typeof a[prop] === "object";
153
154
 
154
155
  // outgoing messages
@@ -240,3 +241,38 @@ export const isToolCallCancellation = (
240
241
  a: unknown,
241
242
  ): a is ToolCallCancellationMessage["toolCallCancellation"] =>
242
243
  typeof a === "object" && Array.isArray((a as any).ids);
244
+
245
+ // ADK Event types
246
+ export interface AdkEvent {
247
+ invocation_id: string;
248
+ author: string;
249
+ actions: {
250
+ state_delta: any;
251
+ artifact_delta: any;
252
+ requested_auth_configs: any;
253
+ requested_tool_confirmations: any;
254
+ };
255
+ id: string;
256
+ timestamp: number;
257
+ // Optional ADK fields
258
+ input_transcription?: { text: string };
259
+ output_transcription?: { text: string };
260
+ content?: any;
261
+ interrupted?: boolean;
262
+ turn_complete?: boolean;
263
+ partial?: boolean;
264
+ }
265
+
266
+ // ADK Event type guards
267
+ export const isAdkEvent = (a: unknown): a is AdkEvent =>
268
+ typeof a === "object" &&
269
+ a !== null &&
270
+ typeof (a as any).invocation_id === "string" &&
271
+ typeof (a as any).author === "string" &&
272
+ typeof (a as any).actions === "object";
273
+
274
+ export const isInputTranscription = (a: unknown): a is AdkEvent =>
275
+ isAdkEvent(a) && (a as AdkEvent).input_transcription !== undefined;
276
+
277
+ export const isOutputTranscription = (a: unknown): a is AdkEvent =>
278
+ isAdkEvent(a) && (a as AdkEvent).output_transcription !== undefined;
@@ -50,7 +50,7 @@ export class AudioRecorder extends EventEmitter {
50
50
  throw new Error("Could not request user media");
51
51
  }
52
52
 
53
- this.starting = new Promise(async (resolve, reject) => {
53
+ this.starting = new Promise(async (resolve) => {
54
54
  this.stream = await navigator.mediaDevices.getUserMedia({ audio: true });
55
55
  this.audioContext = await audioContext({ sampleRate: this.sampleRate });
56
56
  this.source = this.audioContext.createMediaStreamSource(this.stream);
@@ -154,7 +154,7 @@ export class AudioStreamer {
154
154
  const worklets = registeredWorklets.get(this.context);
155
155
 
156
156
  if (worklets) {
157
- Object.entries(worklets).forEach(([workletName, graph]) => {
157
+ Object.entries(worklets).forEach(([, graph]) => {
158
158
  const { node, handlers } = graph;
159
159
  if (node) {
160
160
  source.connect(node);
@@ -18,7 +18,6 @@ import { Content, GenerativeContentBlob, Part } from "@google/generative-ai";
18
18
  import { EventEmitter } from "eventemitter3";
19
19
  import { difference } from "lodash";
20
20
  import {
21
- ClientContentMessage,
22
21
  isInterrupted,
23
22
  isModelTurn,
24
23
  isServerContenteMessage,
@@ -26,15 +25,18 @@ import {
26
25
  isToolCallCancellationMessage,
27
26
  isToolCallMessage,
28
27
  isTurnComplete,
28
+ isAdkEvent,
29
+ isInputTranscription,
30
+ isOutputTranscription,
29
31
  LiveIncomingMessage,
30
32
  ModelTurn,
31
- RealtimeInputMessage,
32
33
  ServerContent,
33
34
  StreamingLog,
34
35
  ToolCall,
35
36
  ToolCallCancellation,
36
37
  ToolResponseMessage,
37
38
  type LiveConfig,
39
+ type AdkEvent,
38
40
  } from "../multimodal-live-types";
39
41
  import { blobToJSON, base64ToArrayBuffer } from "./utils";
40
42
 
@@ -53,6 +55,10 @@ interface MultimodalLiveClientEventTypes {
53
55
  turncomplete: () => void;
54
56
  toolcall: (toolCall: ToolCall) => void;
55
57
  toolcallcancellation: (toolcallCancellation: ToolCallCancellation) => void;
58
+ // ADK events
59
+ inputtranscription: (text: string) => void;
60
+ outputtranscription: (text: string) => void;
61
+ adkevent: (event: AdkEvent) => void;
56
62
  }
57
63
 
58
64
  export type MultimodalLiveAPIClientConnection = {
@@ -72,6 +78,13 @@ export class MultimodalLiveClient extends EventEmitter<MultimodalLiveClientEvent
72
78
  public url: string = "";
73
79
  private runId: string;
74
80
  private userId?: string;
81
+ private firstContentSent: boolean = false;
82
+ private audioChunksSent: number = 0;
83
+ private lastAudioSendTime: number = 0;
84
+ private readonly INITIAL_SEND_INTERVAL_MS = 300; // Start slow: 300ms between chunks
85
+ private readonly NORMAL_SEND_INTERVAL_MS = 125; // Normal rate: 125ms (8 chunks/sec)
86
+ private readonly RAMPUP_CHUNKS = 10; // Number of chunks to send at reduced rate
87
+
75
88
  constructor({ url, userId, runId }: MultimodalLiveAPIClientConnection) {
76
89
  super();
77
90
  const defaultWsUrl = `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/ws`;
@@ -103,15 +116,37 @@ export class MultimodalLiveClient extends EventEmitter<MultimodalLiveClientEvent
103
116
  this.runId = newRunId;
104
117
  }
105
118
 
119
+ // Reset connection state
120
+ this.firstContentSent = false;
121
+ this.audioChunksSent = 0;
122
+ this.lastAudioSendTime = 0;
123
+
106
124
  ws.addEventListener("message", async (evt: MessageEvent) => {
107
125
  if (evt.data instanceof Blob) {
108
126
  this.receive(evt.data);
109
127
  } else if (typeof evt.data === "string") {
110
128
  try {
111
129
  const jsonData = JSON.parse(evt.data);
112
- if (jsonData.status) {
130
+
131
+ // Handle different message types from backend
132
+ if (jsonData.setupComplete) {
133
+ this.emit("setupcomplete");
134
+ this.log("server.setupComplete", "Session ready");
135
+ } else if (jsonData.serverContent) {
136
+ // Handle serverContent messages
137
+ this.receive(new Blob([JSON.stringify(jsonData)], {type: 'application/json'}));
138
+ } else if (jsonData.toolCall) {
139
+ // Handle tool calls
140
+ this.receive(new Blob([JSON.stringify(jsonData)], {type: 'application/json'}));
141
+ } else if (jsonData.status) {
113
142
  this.log("server.status", jsonData.status);
114
- console.log("Status:", jsonData.status); // This will show in console
143
+ console.log("Status:", jsonData.status);
144
+ } else if (jsonData.error) {
145
+ this.log("server.error", jsonData.error);
146
+ console.error("Server error:", jsonData.error);
147
+ } else {
148
+ // Try to process as a regular message
149
+ this.receive(new Blob([JSON.stringify(jsonData)], {type: 'application/json'}));
115
150
  }
116
151
  } catch (error) {
117
152
  console.error("Error parsing message:", error);
@@ -134,11 +169,12 @@ export class MultimodalLiveClient extends EventEmitter<MultimodalLiveClientEvent
134
169
  this.emit("open");
135
170
 
136
171
  this.ws = ws;
137
- // Send initial setup message with runId
172
+ // Send initial setup message with user_id for backend
138
173
  const setupMessage = {
174
+ user_id: this.userId || "default_user",
139
175
  setup: {
140
176
  run_id: this.runId,
141
- user_id: this.userId,
177
+ user_id: this.userId || "default_user",
142
178
  },
143
179
  };
144
180
  this._sendDirect(setupMessage);
@@ -218,11 +254,18 @@ export class MultimodalLiveClient extends EventEmitter<MultimodalLiveClientEvent
218
254
  if (isModelTurn(serverContent)) {
219
255
  let parts: Part[] = serverContent.modelTurn.parts;
220
256
 
221
- // when its audio that is returned for modelTurn
257
+ // when its audio that is returned for modelTurn (check both camelCase and snake_case)
222
258
  const audioParts = parts.filter(
223
- (p) => p.inlineData && p.inlineData.mimeType.startsWith("audio/pcm"),
259
+ (p: any) => {
260
+ const inlineData = p.inlineData || p.inline_data;
261
+ const mimeType = inlineData?.mimeType || inlineData?.mime_type;
262
+ return inlineData && mimeType && mimeType.startsWith("audio/pcm");
263
+ }
224
264
  );
225
- const base64s = audioParts.map((p) => p.inlineData?.data);
265
+ const base64s = audioParts.map((p: any) => {
266
+ const inlineData = p.inlineData || p.inline_data;
267
+ return inlineData?.data;
268
+ });
226
269
 
227
270
  // strip the audio parts out of the modelTurn
228
271
  const otherParts = difference(parts, audioParts);
@@ -245,6 +288,98 @@ export class MultimodalLiveClient extends EventEmitter<MultimodalLiveClientEvent
245
288
  this.emit("content", content);
246
289
  this.log(`server.content`, response);
247
290
  }
291
+ } else if (isAdkEvent(response)) {
292
+ // Handle ADK events
293
+ this.emit("adkevent", response);
294
+
295
+ // Handle specific ADK event types
296
+ if (isInputTranscription(response)) {
297
+ this.emit("inputtranscription", response.input_transcription!.text);
298
+ }
299
+
300
+ if (isOutputTranscription(response)) {
301
+ this.emit("outputtranscription", response.output_transcription!.text);
302
+ }
303
+
304
+ // Handle ADK content (text responses from agent)
305
+ if (response.content && response.content.parts) {
306
+ const parts = response.content.parts;
307
+
308
+ // Extract function calls for tool call logging
309
+ const functionCallParts = parts.filter((p: any) => p.function_call);
310
+
311
+ // Log function calls as tool calls for the console
312
+ if (functionCallParts.length > 0) {
313
+ const functionCalls = functionCallParts.map((p: any) => ({
314
+ id: p.function_call.id,
315
+ name: p.function_call.name,
316
+ args: p.function_call.args || {}
317
+ }));
318
+
319
+ const toolCallMessage = {
320
+ toolCall: {
321
+ functionCalls: functionCalls
322
+ }
323
+ };
324
+
325
+ this.log("server.toolCall", toolCallMessage);
326
+ this.emit("toolcall", toolCallMessage.toolCall);
327
+ }
328
+
329
+ // Extract audio parts for playing (check both camelCase and snake_case)
330
+ const audioParts = parts.filter(
331
+ (p: any) => {
332
+ const inlineData = p.inlineData || p.inline_data;
333
+ const mimeType = inlineData?.mimeType || inlineData?.mime_type;
334
+ return inlineData && mimeType && mimeType.startsWith("audio/");
335
+ }
336
+ );
337
+
338
+ // Play audio if present
339
+ audioParts.forEach((audioPart: any) => {
340
+ const inlineData = audioPart.inlineData || audioPart.inline_data;
341
+ if (inlineData && inlineData.data) {
342
+ const audioData = base64ToArrayBuffer(inlineData.data);
343
+
344
+ // Only emit audio if we have a valid buffer with data
345
+ if (audioData.byteLength > 0) {
346
+ this.emit("audio", audioData);
347
+ this.log(`server.audio`, `buffer (${audioData.byteLength}) - ${inlineData.mime_type || inlineData.mimeType}`);
348
+ } else {
349
+ this.log(`server.audio`, `invalid audio buffer - skipped`);
350
+ }
351
+ }
352
+ });
353
+
354
+ // Send content for other parts (text, etc.) - exclude function calls and audio
355
+ const nonAudioNonFunctionParts = parts.filter(
356
+ (p: any) => {
357
+ const inlineData = p.inlineData || p.inline_data;
358
+ const mimeType = inlineData?.mimeType || inlineData?.mime_type;
359
+ const hasAudio = inlineData && mimeType && mimeType.startsWith("audio/");
360
+ const hasFunctionCall = p.function_call;
361
+ return !hasAudio && !hasFunctionCall;
362
+ }
363
+ );
364
+
365
+ if (nonAudioNonFunctionParts.length > 0) {
366
+ const content: ModelTurn = { modelTurn: { parts: nonAudioNonFunctionParts } };
367
+ this.emit("content", content);
368
+ this.log("server.content", `content with ${nonAudioNonFunctionParts.length} non-audio, non-function parts`);
369
+ }
370
+ }
371
+
372
+ // Handle turn complete
373
+ if (response.turn_complete) {
374
+ this.emit("turncomplete");
375
+ this.log("server.turncomplete", "ADK turn complete");
376
+ }
377
+
378
+ // Handle interruption
379
+ if (response.interrupted) {
380
+ this.emit("interrupted");
381
+ this.log("server.interrupted", "ADK interrupted");
382
+ }
248
383
  } else {
249
384
  console.log("received unmatched message", response);
250
385
  this.log("received unmatched message", response);
@@ -255,6 +390,12 @@ export class MultimodalLiveClient extends EventEmitter<MultimodalLiveClientEvent
255
390
  * send realtimeInput, this is base64 chunks of "audio/pcm" and/or "image/jpg"
256
391
  */
257
392
  sendRealtimeInput(chunks: GenerativeContentBlob[]) {
393
+ // Don't send if WebSocket is not open - this prevents flooding the queue
394
+ // during connection setup
395
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
396
+ return;
397
+ }
398
+
258
399
  let hasAudio = false;
259
400
  let hasVideo = false;
260
401
  for (let i = 0; i < chunks.length; i++) {
@@ -269,6 +410,25 @@ export class MultimodalLiveClient extends EventEmitter<MultimodalLiveClientEvent
269
410
  break;
270
411
  }
271
412
  }
413
+
414
+ // Throttle audio chunks during initial connection phase
415
+ if (hasAudio && !hasVideo) {
416
+ const now = Date.now();
417
+
418
+ // Calculate required interval based on how many chunks we've sent
419
+ const requiredInterval = this.audioChunksSent < this.RAMPUP_CHUNKS
420
+ ? this.INITIAL_SEND_INTERVAL_MS
421
+ : this.NORMAL_SEND_INTERVAL_MS;
422
+
423
+ // If not enough time has passed since last send, drop this chunk
424
+ if (this.lastAudioSendTime > 0 && (now - this.lastAudioSendTime) < requiredInterval) {
425
+ return;
426
+ }
427
+
428
+ this.lastAudioSendTime = now;
429
+ this.audioChunksSent++;
430
+ }
431
+
272
432
  const message =
273
433
  hasAudio && hasVideo
274
434
  ? "audio + video"
@@ -278,12 +438,26 @@ export class MultimodalLiveClient extends EventEmitter<MultimodalLiveClientEvent
278
438
  ? "video"
279
439
  : "unknown";
280
440
 
281
- const data: RealtimeInputMessage = {
282
- realtimeInput: {
283
- mediaChunks: chunks,
284
- },
285
- };
286
- this._sendDirect(data);
441
+ // Convert to LiveRequest format for backend
442
+ for (const chunk of chunks) {
443
+ let data: any = {
444
+ blob: {
445
+ mimeType: chunk.mimeType,
446
+ data: chunk.data,
447
+ },
448
+ };
449
+
450
+ // For remote mode: wrap first content in {user_id, live_request} format
451
+ if (!this.firstContentSent) {
452
+ data = {
453
+ user_id: this.userId || "default_user",
454
+ live_request: data,
455
+ };
456
+ this.firstContentSent = true;
457
+ }
458
+
459
+ this._sendDirect(data);
460
+ }
287
461
  this.log(`client.realtimeInput`, message);
288
462
  }
289
463
 
@@ -302,22 +476,29 @@ export class MultimodalLiveClient extends EventEmitter<MultimodalLiveClientEvent
302
476
  /**
303
477
  * send normal content parts such as { text }
304
478
  */
305
- send(parts: Part | Part[], turnComplete: boolean = true) {
479
+ send(parts: Part | Part[], _turnComplete: boolean = true) {
306
480
  parts = Array.isArray(parts) ? parts : [parts];
307
481
  const content: Content = {
308
482
  role: "user",
309
483
  parts,
310
484
  };
311
485
 
312
- const clientContentRequest: ClientContentMessage = {
313
- clientContent: {
314
- turns: [content],
315
- turnComplete,
316
- },
486
+ // Convert to LiveRequest format for backend
487
+ let data: any = {
488
+ content: content,
317
489
  };
318
490
 
319
- this._sendDirect(clientContentRequest);
320
- this.log(`client.send`, clientContentRequest);
491
+ // For remote mode: wrap first content in {user_id, live_request} format
492
+ if (!this.firstContentSent) {
493
+ data = {
494
+ user_id: this.userId || "default_user",
495
+ live_request: data,
496
+ };
497
+ this.firstContentSent = true;
498
+ }
499
+
500
+ this._sendDirect(data);
501
+ this.log(`client.send`, `content with ${parts.length} parts`);
321
502
  }
322
503
 
323
504
  /**
@@ -76,11 +76,33 @@ export const blobToJSON = (blob: Blob) =>
76
76
  reader.readAsText(blob);
77
77
  });
78
78
 
79
+ function cleanBase64String(base64: string): string {
80
+ // Convert URL-safe base64 to standard base64
81
+ let cleaned = base64
82
+ .replace(/-/g, '+') // Replace - with +
83
+ .replace(/_/g, '/') // Replace _ with /
84
+ .replace(/[^A-Za-z0-9+/=]/g, ''); // Remove any other invalid characters
85
+
86
+ // Ensure proper padding (base64 strings must be multiples of 4)
87
+ return cleaned + '='.repeat((4 - cleaned.length % 4) % 4);
88
+ }
89
+
79
90
  export function base64ToArrayBuffer(base64: string) {
80
- var binaryString = atob(base64);
81
- var bytes = new Uint8Array(binaryString.length);
82
- for (let i = 0; i < binaryString.length; i++) {
83
- bytes[i] = binaryString.charCodeAt(i);
91
+ const cleanedBase64 = cleanBase64String(base64);
92
+
93
+ try {
94
+ var binaryString = atob(cleanedBase64);
95
+ var bytes = new Uint8Array(binaryString.length);
96
+ for (let i = 0; i < binaryString.length; i++) {
97
+ bytes[i] = binaryString.charCodeAt(i);
98
+ }
99
+ return bytes.buffer;
100
+ } catch (error) {
101
+ console.error('Failed to decode base64 audio data:', error);
102
+ console.error('Original base64 length:', base64.length);
103
+ console.error('Cleaned base64 length:', cleanedBase64.length);
104
+ console.error('First 100 chars:', base64.substring(0, 100));
105
+ // Return empty buffer on error
106
+ return new ArrayBuffer(0);
84
107
  }
85
- return bytes.buffer;
86
108
  }
@@ -109,6 +109,8 @@ class LocalChatMessageHistory(BaseChatMessageHistory):
109
109
  for msg in messages
110
110
  if msg["type"] in ("ai", "human") and isinstance(msg["content"], str)
111
111
  ]
112
+ # Convert messages to the format expected by chain_title.invoke
113
+ messages = {"messages": messages}
112
114
 
113
115
  response = chain_title.invoke(messages)
114
116
  title = (
@@ -1464,8 +1464,8 @@ if __name__ == "__main__":
1464
1464
 
1465
1465
  For detailed specifications of all classes, methods, and commands, refer to the official reference documentation.
1466
1466
 
1467
- * [Python API Reference](./api-reference/python/index.html)
1468
- * [Java API Reference](./api-reference/java/index.html)
1469
- * [CLI Reference](./api-reference/cli/index.html)
1470
- * [REST API Reference](./api-reference/rest/index.md)
1471
- * [Agent Config YAML Reference](./api-reference/agentconfig/index.html)
1467
+ * [Python API Reference](https://github.com/google/adk-docs/tree/main/docs/api-reference/python)
1468
+ * [Java API Reference](https://github.com/google/adk-docs/tree/main/docs/api-reference/java)
1469
+ * [CLI Reference](https://github.com/google/adk-docs/tree/main/docs/api-reference/cli)
1470
+ * [REST API Reference](https://github.com/google/adk-docs/tree/main/docs/api-reference/rest)
1471
+ * [Agent Config YAML Reference](https://github.com/google/adk-docs/tree/main/docs/api-reference/agentconfig)
@@ -0,0 +1,64 @@
1
+
2
+ # To learn more about how to use Nix to configure your environment
3
+ # see: https://firebase.google.com/docs/studio/customize-workspace
4
+ { pkgs, ... }: {
5
+ # Which nixpkgs channel to use.
6
+ channel = "stable-24.11"; # or "unstable"
7
+
8
+ # Use https://search.nixos.org/packages to find packages
9
+ packages = [
10
+ pkgs.uv
11
+ pkgs.gnumake
12
+ pkgs.terraform
13
+ pkgs.gh
14
+ ];
15
+ # Sets environment variables in the workspace
16
+ env = {};
17
+ idx = {
18
+ # Search for the extensions you want on https://open-vsx.org/ and use "publisher.id"
19
+ extensions = [
20
+ ];
21
+ workspace = {
22
+ # Runs when a workspace is first created with this `dev.nix` file
23
+ onCreate = {
24
+ create-venv = ''
25
+ # Load environment variables from .env file if it exists
26
+ source .env
27
+
28
+ # Beautiful prints for gcloud setup
29
+ echo ""
30
+ echo "╔════════════════════════════════════════════════════════════╗"
31
+ echo "║ 🔐 GCLOUD SETUP REQUIRED ║"
32
+ echo "╚════════════════════════════════════════════════════════════╝"
33
+ echo ""
34
+ echo "📝 Before proceeding, please ensure:"
35
+ echo " 1️⃣ You are logged in to gcloud"
36
+ echo " 2️⃣ You have selected the correct project"
37
+ echo ""
38
+
39
+ auth_status=$(gcloud auth list --quiet 2>&1)
40
+
41
+ echo ""
42
+ echo "⚙️ We will now set the project you want to use..."
43
+ gcloud config get project
44
+
45
+ echo ""
46
+ echo "💡 Need to setup? Run these commands:"
47
+ echo " → gcloud auth login"
48
+ echo " → gcloud config set project YOUR_PROJECT_ID"
49
+ echo ""
50
+
51
+ echo "Running agent starter pack creation..."
52
+ uvx agent-starter-pack create $WS_NAME
53
+ code ~/$WS_NAME/$WS_NAME/README.md
54
+ exec bash
55
+ '';
56
+ # Open editors for the following files by default, if they exist:
57
+ default.openFiles = [];
58
+ };
59
+ # To run something each time the workspace is (re)started, use the `onStart` hook
60
+ };
61
+ # Enable previews and customize configuration
62
+ previews = {};
63
+ };
64
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "Agent Starter Pack",
3
+ "description": "Production-ready Gen AI Agent templates for Google Cloud. Addressing common challenges (Deployment & Operations, Evaluation, Customization, Observability) in building and deploying GenAI agents.",
4
+ "icon": "https://github.com/GoogleCloudPlatform/agent-starter-pack/blob/main/docs/images/icon.png?raw=true",
5
+ "params": []
6
+ }
@@ -1,3 +1,4 @@
1
+
1
2
  # No user-configurable parameters
2
3
  # Accept additional arguments to this template corresponding to template
3
4
  # parameter IDs
@@ -14,8 +15,6 @@
14
15
 
15
16
  # Create .env file with the parameter values
16
17
  cat > "$out/.env" << EOF
17
- AGENT_NAME=${agent_name}
18
- GOOGLE_CLOUD_PROJECT=${google_cloud_project_id}
19
18
  WS_NAME=$WS_NAME
20
19
  EOF
21
20
 
@@ -23,4 +22,4 @@
23
22
  # Git repository
24
23
  rm -rf "$out/.git" "$out/idx-template".{nix,json}
25
24
  '';
26
- }
25
+ }