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.
- agent_starter_pack/agents/README.md +7 -0
- agents/langgraph_base_react/template/.templateconfig.yaml → agent_starter_pack/agents/adk_a2a_base/.template/templateconfig.yaml +5 -10
- agent_starter_pack/agents/adk_a2a_base/README.md +37 -0
- src/deployment_targets/cloud_run/Dockerfile → agent_starter_pack/agents/adk_a2a_base/app/__init__.py +2 -14
- agent_starter_pack/agents/adk_a2a_base/app/agent.py +70 -0
- agent_starter_pack/agents/adk_a2a_base/notebooks/adk_a2a_app_testing.ipynb +583 -0
- agents/crewai_coding_crew/notebooks/evaluating_crewai_agent.ipynb → agent_starter_pack/agents/adk_a2a_base/notebooks/evaluating_adk_agent.ipynb +163 -199
- {agents/adk_base → agent_starter_pack/agents/adk_a2a_base}/tests/integration/test_agent.py +2 -2
- agents/adk_base/template/.templateconfig.yaml → agent_starter_pack/agents/adk_base/.template/templateconfig.yaml +4 -1
- {agents → agent_starter_pack/agents}/adk_base/README.md +1 -1
- agent_starter_pack/agents/adk_base/app/__init__.py +17 -0
- {agents → agent_starter_pack/agents}/adk_base/app/agent.py +5 -2
- {agents → agent_starter_pack/agents}/adk_base/notebooks/adk_app_testing.ipynb +128 -82
- agents/langgraph_base_react/notebooks/evaluating_langgraph_agent.ipynb → agent_starter_pack/agents/adk_base/notebooks/evaluating_adk_agent.ipynb +181 -207
- agent_starter_pack/agents/adk_base/tests/integration/test_agent.py +58 -0
- agents/crewai_coding_crew/template/.templateconfig.yaml → agent_starter_pack/agents/adk_live/.template/templateconfig.yaml +5 -9
- agent_starter_pack/agents/adk_live/README.md +32 -0
- agent_starter_pack/agents/adk_live/app/__init__.py +17 -0
- agent_starter_pack/agents/adk_live/app/agent.py +51 -0
- agent_starter_pack/agents/adk_live/tests/unit/test_dummy.py +38 -0
- agents/agentic_rag/template/.templateconfig.yaml → agent_starter_pack/agents/agentic_rag/.template/templateconfig.yaml +7 -3
- {agents → agent_starter_pack/agents}/agentic_rag/README.md +1 -1
- agent_starter_pack/agents/agentic_rag/app/__init__.py +17 -0
- {agents → agent_starter_pack/agents}/agentic_rag/app/agent.py +8 -4
- {agents → agent_starter_pack/agents}/agentic_rag/notebooks/adk_app_testing.ipynb +128 -82
- agent_starter_pack/agents/agentic_rag/notebooks/evaluating_adk_agent.ipynb +1535 -0
- {agents → agent_starter_pack/agents}/agentic_rag/tests/integration/test_agent.py +3 -3
- agent_starter_pack/agents/langgraph_base/.template/templateconfig.yaml +31 -0
- agent_starter_pack/agents/langgraph_base/README.md +30 -0
- agent_starter_pack/agents/langgraph_base/app/__init__.py +17 -0
- agent_starter_pack/agents/langgraph_base/app/agent.py +34 -0
- {agents/crewai_coding_crew → agent_starter_pack/agents/langgraph_base}/notebooks/evaluating_langgraph_agent.ipynb +30 -17
- {agents/langgraph_base_react → agent_starter_pack/agents/langgraph_base}/tests/integration/test_agent.py +2 -2
- {src → agent_starter_pack}/base_template/.gitignore +5 -3
- agent_starter_pack/base_template/GEMINI.md +5 -0
- agent_starter_pack/base_template/Makefile +339 -0
- agent_starter_pack/base_template/README.md +267 -0
- agent_starter_pack/base_template/deployment/README.md +11 -0
- {src → agent_starter_pack}/base_template/deployment/terraform/apis.tf +2 -2
- {src → agent_starter_pack}/base_template/deployment/terraform/dev/apis.tf +6 -1
- {src → agent_starter_pack}/base_template/deployment/terraform/dev/iam.tf +12 -11
- {src → agent_starter_pack}/base_template/deployment/terraform/dev/providers.tf +5 -1
- {src → agent_starter_pack}/base_template/deployment/terraform/dev/storage.tf +1 -1
- {src → agent_starter_pack}/base_template/deployment/terraform/dev/variables.tf +10 -10
- agent_starter_pack/base_template/deployment/terraform/dev/{% if cookiecutter.is_adk %}telemetry.tf{% else %}unused_telemetry.tf{% endif %} +193 -0
- agent_starter_pack/base_template/deployment/terraform/github.tf +337 -0
- {src → agent_starter_pack}/base_template/deployment/terraform/iam.tf +20 -41
- {src → agent_starter_pack}/base_template/deployment/terraform/locals.tf +10 -3
- {src/resources/setup_cicd → agent_starter_pack/base_template/deployment/terraform}/providers.tf +8 -1
- {src → agent_starter_pack}/base_template/deployment/terraform/service_accounts.tf +7 -8
- agent_starter_pack/base_template/deployment/terraform/sql/completions.sql +138 -0
- {src → agent_starter_pack}/base_template/deployment/terraform/storage.tf +7 -16
- {src → agent_starter_pack}/base_template/deployment/terraform/variables.tf +61 -28
- {src → agent_starter_pack}/base_template/deployment/terraform/vars/env.tfvars +6 -1
- agent_starter_pack/base_template/deployment/terraform/{% if cookiecutter.cicd_runner == 'github_actions' %}wif.tf{% else %}unused_wif.tf{% endif %} +43 -0
- 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
- agent_starter_pack/base_template/deployment/terraform/{% if cookiecutter.is_adk %}telemetry.tf{% else %}unused_telemetry.tf{% endif %} +206 -0
- {src → agent_starter_pack}/base_template/pyproject.toml +24 -37
- agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/deploy-to-prod.yaml +132 -0
- agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/pr_checks.yaml +65 -0
- agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/staging.yaml +259 -0
- 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
- 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
- agent_starter_pack/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/staging.yaml +322 -0
- agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils/telemetry.py +96 -0
- {src/base_template/app/utils → agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils}/typing.py +7 -9
- 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
- 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
- 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
- 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
- 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
- agent_starter_pack/cli/commands/create.py +1256 -0
- agent_starter_pack/cli/commands/enhance.py +611 -0
- agent_starter_pack/cli/commands/list.py +203 -0
- agent_starter_pack/cli/commands/register_gemini_enterprise.py +1070 -0
- agent_starter_pack/cli/commands/setup_cicd.py +862 -0
- {src → agent_starter_pack}/cli/main.py +10 -2
- {src → agent_starter_pack}/cli/utils/cicd.py +139 -48
- agent_starter_pack/cli/utils/gcp.py +263 -0
- agent_starter_pack/cli/utils/logging.py +103 -0
- agent_starter_pack/cli/utils/remote_template.py +677 -0
- agent_starter_pack/cli/utils/template.py +1466 -0
- {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/components/process_data.py +1 -1
- {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/submit_pipeline.py +20 -6
- {src → agent_starter_pack}/data_ingestion/pyproject.toml +1 -0
- {src → agent_starter_pack}/data_ingestion/uv.lock +602 -494
- agent_starter_pack/deployment_targets/agent_engine/tests/integration/test_agent_engine_app.py +484 -0
- agent_starter_pack/deployment_targets/agent_engine/tests/load_test/README.md +84 -0
- agent_starter_pack/deployment_targets/agent_engine/tests/load_test/load_test.py +424 -0
- agent_starter_pack/deployment_targets/agent_engine/tests/{% if cookiecutter.is_a2a %}helpers.py{% else %}unused_helpers.py{% endif %} +138 -0
- agent_starter_pack/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/agent_engine_app.py +263 -0
- agent_starter_pack/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/app_utils/deploy.py +414 -0
- 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
- agent_starter_pack/deployment_targets/cloud_run/Dockerfile +51 -0
- agent_starter_pack/deployment_targets/cloud_run/deployment/terraform/dev/service.tf +243 -0
- agent_starter_pack/deployment_targets/cloud_run/deployment/terraform/service.tf +417 -0
- agent_starter_pack/deployment_targets/cloud_run/tests/integration/test_server_e2e.py +705 -0
- agent_starter_pack/deployment_targets/cloud_run/tests/load_test/.results/.placeholder +321 -0
- agent_starter_pack/deployment_targets/cloud_run/tests/load_test/README.md +165 -0
- agent_starter_pack/deployment_targets/cloud_run/tests/load_test/load_test.py +329 -0
- agent_starter_pack/deployment_targets/cloud_run/{{cookiecutter.agent_directory}}/fast_api_app.py +556 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/package-lock.json +79 -1044
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/package.json +1 -9
- agent_starter_pack/frontends/adk_live_react/frontend/src/App.tsx +65 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/logger/Logger.tsx +8 -3
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/logger/logger.scss +26 -0
- agent_starter_pack/frontends/adk_live_react/frontend/src/components/side-panel/SidePanel.tsx +516 -0
- agent_starter_pack/frontends/adk_live_react/frontend/src/components/side-panel/side-panel.scss +563 -0
- agent_starter_pack/frontends/adk_live_react/frontend/src/components/transcription-preview/TranscriptionPreview.tsx +106 -0
- agent_starter_pack/frontends/adk_live_react/frontend/src/components/transcription-preview/transcription-preview.scss +150 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-live-api.ts +8 -2
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/multimodal-live-types.ts +40 -2
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/audio-recorder.ts +1 -1
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/audio-streamer.ts +1 -1
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/multimodal-live-client.ts +210 -24
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/utils.ts +27 -5
- agent_starter_pack/resources/docs/adk-cheatsheet.md +1628 -0
- agent_starter_pack/resources/locks/uv-adk_a2a_base-agent_engine.lock +4966 -0
- agent_starter_pack/resources/locks/uv-adk_a2a_base-cloud_run.lock +5011 -0
- agent_starter_pack/resources/locks/uv-adk_base-agent_engine.lock +4946 -0
- agent_starter_pack/resources/locks/uv-adk_base-cloud_run.lock +4991 -0
- agent_starter_pack/resources/locks/uv-adk_live-agent_engine.lock +4963 -0
- agent_starter_pack/resources/locks/uv-adk_live-cloud_run.lock +5006 -0
- agent_starter_pack/resources/locks/uv-agentic_rag-agent_engine.lock +5487 -0
- agent_starter_pack/resources/locks/uv-agentic_rag-cloud_run.lock +5532 -0
- agent_starter_pack/resources/locks/uv-langgraph_base-agent_engine.lock +5788 -0
- agent_starter_pack/resources/locks/uv-langgraph_base-cloud_run.lock +5811 -0
- {src → agent_starter_pack}/utils/generate_locks.py +15 -12
- {src → agent_starter_pack}/utils/lock_utils.py +4 -7
- {src → agent_starter_pack}/utils/watch_and_rebuild.py +2 -2
- agent_starter_pack-0.21.0.dist-info/METADATA +182 -0
- agent_starter_pack-0.21.0.dist-info/RECORD +171 -0
- agent_starter_pack-0.21.0.dist-info/entry_points.txt +2 -0
- llm.txt +362 -0
- agent_starter_pack-0.3.3.dist-info/METADATA +0 -164
- agent_starter_pack-0.3.3.dist-info/RECORD +0 -176
- agent_starter_pack-0.3.3.dist-info/entry_points.txt +0 -2
- agents/crewai_coding_crew/README.md +0 -34
- agents/crewai_coding_crew/app/agent.py +0 -86
- agents/crewai_coding_crew/app/crew/config/agents.yaml +0 -39
- agents/crewai_coding_crew/app/crew/config/tasks.yaml +0 -37
- agents/crewai_coding_crew/app/crew/crew.py +0 -71
- agents/crewai_coding_crew/tests/integration/test_agent.py +0 -47
- agents/langgraph_base_react/README.md +0 -9
- agents/langgraph_base_react/app/agent.py +0 -73
- agents/live_api/README.md +0 -37
- agents/live_api/app/agent.py +0 -78
- agents/live_api/app/server.py +0 -196
- agents/live_api/app/templates.py +0 -51
- agents/live_api/app/vector_store.py +0 -55
- agents/live_api/template/.templateconfig.yaml +0 -29
- agents/live_api/tests/integration/test_server_e2e.py +0 -254
- agents/live_api/tests/load_test/load_test.py +0 -40
- agents/live_api/tests/unit/test_server.py +0 -143
- src/base_template/Makefile +0 -72
- src/base_template/README.md +0 -208
- src/base_template/app/__init__.py +0 -3
- src/base_template/app/utils/tracing.py +0 -155
- src/base_template/deployment/README.md +0 -126
- src/base_template/deployment/cd/staging.yaml +0 -216
- src/base_template/deployment/terraform/dev/log_sinks.tf +0 -63
- src/base_template/deployment/terraform/log_sinks.tf +0 -70
- src/base_template/deployment/terraform/providers.tf +0 -37
- src/cli/commands/create.py +0 -664
- src/cli/commands/setup_cicd.py +0 -829
- src/cli/utils/gcp.py +0 -117
- src/cli/utils/logging.py +0 -51
- src/cli/utils/template.py +0 -737
- src/deployment_targets/agent_engine/app/agent_engine_app.py +0 -336
- src/deployment_targets/agent_engine/notebooks/intro_agent_engine.ipynb +0 -1025
- src/deployment_targets/agent_engine/tests/integration/test_agent_engine_app.py +0 -183
- src/deployment_targets/agent_engine/tests/load_test/README.md +0 -42
- src/deployment_targets/agent_engine/tests/load_test/load_test.py +0 -107
- src/deployment_targets/cloud_run/app/server.py +0 -154
- src/deployment_targets/cloud_run/tests/integration/test_server_e2e.py +0 -249
- src/deployment_targets/cloud_run/tests/load_test/.results/.placeholder +0 -0
- src/deployment_targets/cloud_run/tests/load_test/README.md +0 -83
- src/deployment_targets/cloud_run/tests/load_test/load_test.py +0 -118
- src/deployment_targets/cloud_run/uv.lock +0 -6952
- src/frontends/live_api_react/frontend/src/App.tsx +0 -205
- src/frontends/live_api_react/frontend/src/components/control-tray/ControlTray.tsx +0 -217
- src/frontends/live_api_react/frontend/src/components/control-tray/control-tray.scss +0 -201
- src/frontends/live_api_react/frontend/src/components/side-panel/SidePanel.tsx +0 -161
- src/frontends/live_api_react/frontend/src/components/side-panel/side-panel.scss +0 -285
- src/frontends/streamlit/frontend/side_bar.py +0 -214
- src/frontends/streamlit/frontend/streamlit_app.py +0 -265
- src/frontends/streamlit/frontend/style/app_markdown.py +0 -37
- src/frontends/streamlit/frontend/utils/chat_utils.py +0 -67
- src/frontends/streamlit/frontend/utils/local_chat_history.py +0 -125
- src/frontends/streamlit/frontend/utils/message_editing.py +0 -59
- src/frontends/streamlit/frontend/utils/multimodal_utils.py +0 -217
- src/frontends/streamlit/frontend/utils/stream_handler.py +0 -301
- src/frontends/streamlit/frontend/utils/title_summary.py +0 -94
- src/frontends/streamlit_adk/frontend/side_bar.py +0 -214
- src/frontends/streamlit_adk/frontend/streamlit_app.py +0 -314
- src/frontends/streamlit_adk/frontend/style/app_markdown.py +0 -37
- src/frontends/streamlit_adk/frontend/utils/chat_utils.py +0 -84
- src/frontends/streamlit_adk/frontend/utils/local_chat_history.py +0 -110
- src/frontends/streamlit_adk/frontend/utils/message_editing.py +0 -61
- src/frontends/streamlit_adk/frontend/utils/multimodal_utils.py +0 -223
- src/frontends/streamlit_adk/frontend/utils/stream_handler.py +0 -311
- src/frontends/streamlit_adk/frontend/utils/title_summary.py +0 -129
- src/resources/containers/data_processing/Dockerfile +0 -27
- src/resources/containers/e2e-tests/Dockerfile +0 -19
- src/resources/idx/.idx/dev.nix +0 -57
- src/resources/idx/idx-template.json +0 -21
- src/resources/idx/idx-template.nix +0 -26
- src/resources/locks/uv-adk_base-agent_engine.lock +0 -5338
- src/resources/locks/uv-adk_base-cloud_run.lock +0 -5930
- src/resources/locks/uv-agentic_rag-agent_engine.lock +0 -5528
- src/resources/locks/uv-agentic_rag-cloud_run.lock +0 -6120
- src/resources/locks/uv-crewai_coding_crew-agent_engine.lock +0 -6231
- src/resources/locks/uv-crewai_coding_crew-cloud_run.lock +0 -6839
- src/resources/locks/uv-langgraph_base_react-agent_engine.lock +0 -5233
- src/resources/locks/uv-langgraph_base_react-cloud_run.lock +0 -5862
- src/resources/locks/uv-live_api-cloud_run.lock +0 -5832
- src/resources/setup_cicd/cicd_variables.tf +0 -41
- src/resources/setup_cicd/github.tf +0 -87
- {agents → agent_starter_pack/agents}/agentic_rag/app/retrievers.py +0 -0
- {agents → agent_starter_pack/agents}/agentic_rag/app/templates.py +0 -0
- {src → agent_starter_pack}/base_template/deployment/terraform/dev/vars/env.tfvars +0 -0
- {src → agent_starter_pack}/base_template/tests/unit/test_dummy.py +0 -0
- {src/deployment_targets/agent_engine/app/utils → agent_starter_pack/base_template/{{cookiecutter.agent_directory}}/app_utils}/gcs.py +0 -0
- {src → agent_starter_pack}/cli/utils/__init__.py +0 -0
- {src → agent_starter_pack}/cli/utils/datastores.py +0 -0
- {src → agent_starter_pack}/cli/utils/version.py +0 -0
- {src → agent_starter_pack}/data_ingestion/README.md +0 -0
- {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/components/ingest_data.py +0 -0
- {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/pipeline.py +0 -0
- {src → agent_starter_pack}/deployment_targets/agent_engine/deployment_metadata.json +0 -0
- {src → agent_starter_pack}/deployment_targets/agent_engine/tests/load_test/.results/.placeholder +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/public/favicon.ico +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/public/index.html +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/public/robots.txt +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/App.scss +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/App.test.tsx +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/audio-pulse/AudioPulse.tsx +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/audio-pulse/audio-pulse.scss +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/logger/mock-logs.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/contexts/LiveAPIContext.tsx +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-media-stream-mux.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-screen-capture.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-webcam.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/index.css +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/index.tsx +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/react-app-env.d.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/reportWebVitals.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/setupTests.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/audioworklet-registry.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/store-logger.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/worklets/audio-processing.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/worklets/vol-meter.ts +0 -0
- {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/tsconfig.json +0 -0
- {agent_starter_pack-0.3.3.dist-info → agent_starter_pack-0.21.0.dist-info}/WHEEL +0 -0
- {agent_starter_pack-0.3.3.dist-info → agent_starter_pack-0.21.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2025 Google LLC
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
.transcription-preview {
|
|
18
|
+
width: 0;
|
|
19
|
+
height: 100%;
|
|
20
|
+
background: var(--Neutral-00);
|
|
21
|
+
border-left: 1px solid var(--Neutral-20);
|
|
22
|
+
display: flex;
|
|
23
|
+
flex-direction: column;
|
|
24
|
+
gap: 0;
|
|
25
|
+
overflow: hidden;
|
|
26
|
+
transition: width 0.2s ease-in-out;
|
|
27
|
+
flex-shrink: 0;
|
|
28
|
+
|
|
29
|
+
&.open {
|
|
30
|
+
width: 300px;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.transcription-section {
|
|
34
|
+
flex: 1;
|
|
35
|
+
display: flex;
|
|
36
|
+
flex-direction: column;
|
|
37
|
+
overflow: hidden;
|
|
38
|
+
opacity: 0;
|
|
39
|
+
transition: opacity 0.2s ease-in-out 0.1s;
|
|
40
|
+
|
|
41
|
+
&:first-child {
|
|
42
|
+
border-bottom: 1px solid var(--Neutral-20);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.transcription-header {
|
|
46
|
+
display: flex;
|
|
47
|
+
align-items: center;
|
|
48
|
+
gap: 8px;
|
|
49
|
+
padding: 12px 20px;
|
|
50
|
+
background: var(--Neutral-05);
|
|
51
|
+
border-bottom: 1px solid var(--Neutral-20);
|
|
52
|
+
flex-shrink: 0;
|
|
53
|
+
|
|
54
|
+
.material-symbols-outlined {
|
|
55
|
+
font-size: 20px;
|
|
56
|
+
color: var(--Neutral-70);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
h3 {
|
|
60
|
+
margin: 0;
|
|
61
|
+
font-size: 12px;
|
|
62
|
+
font-weight: 600;
|
|
63
|
+
color: var(--Neutral-90);
|
|
64
|
+
text-transform: uppercase;
|
|
65
|
+
letter-spacing: 0.5px;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.transcription-content {
|
|
70
|
+
flex: 1;
|
|
71
|
+
padding: 12px 16px;
|
|
72
|
+
overflow-y: auto;
|
|
73
|
+
overflow-x: hidden;
|
|
74
|
+
word-wrap: break-word;
|
|
75
|
+
|
|
76
|
+
p {
|
|
77
|
+
margin: 0 0 12px 0;
|
|
78
|
+
color: var(--Neutral-90);
|
|
79
|
+
font-size: 13px;
|
|
80
|
+
line-height: 1.5;
|
|
81
|
+
white-space: pre-wrap;
|
|
82
|
+
word-break: break-word;
|
|
83
|
+
font-family: var(--font-family);
|
|
84
|
+
|
|
85
|
+
&.placeholder {
|
|
86
|
+
color: var(--Neutral-50);
|
|
87
|
+
font-style: italic;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
&.previous {
|
|
91
|
+
color: var(--Neutral-60);
|
|
92
|
+
opacity: 0.7;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
&.current {
|
|
96
|
+
color: var(--Neutral-90);
|
|
97
|
+
font-weight: 500;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
&:last-child {
|
|
101
|
+
margin-bottom: 0;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
&.open .transcription-section {
|
|
108
|
+
opacity: 1;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.input-section {
|
|
112
|
+
.transcription-header {
|
|
113
|
+
.material-symbols-outlined {
|
|
114
|
+
color: var(--Green-500);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
h3 {
|
|
118
|
+
color: var(--Green-500);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.transcription-content {
|
|
123
|
+
background: linear-gradient(
|
|
124
|
+
to bottom,
|
|
125
|
+
rgba(0, 200, 100, 0.02),
|
|
126
|
+
transparent
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.output-section {
|
|
132
|
+
.transcription-header {
|
|
133
|
+
.material-symbols-outlined {
|
|
134
|
+
color: var(--Blue-500);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
h3 {
|
|
138
|
+
color: var(--Blue-500);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.transcription-content {
|
|
143
|
+
background: linear-gradient(
|
|
144
|
+
to bottom,
|
|
145
|
+
rgba(0, 100, 255, 0.02),
|
|
146
|
+
transparent
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -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
|
-
|
|
101
|
-
}, [client
|
|
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
|
|
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,40 @@ 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
|
+
user_id: string;
|
|
248
|
+
session_id: string;
|
|
249
|
+
author: string;
|
|
250
|
+
actions: {
|
|
251
|
+
state_delta: any;
|
|
252
|
+
artifact_delta: any;
|
|
253
|
+
requested_auth_configs: any;
|
|
254
|
+
requested_tool_confirmations: any;
|
|
255
|
+
};
|
|
256
|
+
id: string;
|
|
257
|
+
timestamp: number;
|
|
258
|
+
// Optional ADK fields
|
|
259
|
+
input_transcription?: { text: string };
|
|
260
|
+
output_transcription?: { text: string };
|
|
261
|
+
content?: any;
|
|
262
|
+
interrupted?: boolean;
|
|
263
|
+
turn_complete?: boolean;
|
|
264
|
+
partial?: boolean;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// ADK Event type guards
|
|
268
|
+
export const isAdkEvent = (a: unknown): a is AdkEvent =>
|
|
269
|
+
typeof a === "object" &&
|
|
270
|
+
a !== null &&
|
|
271
|
+
typeof (a as any).user_id === "string" &&
|
|
272
|
+
typeof (a as any).session_id === "string" &&
|
|
273
|
+
typeof (a as any).author === "string" &&
|
|
274
|
+
typeof (a as any).actions === "object";
|
|
275
|
+
|
|
276
|
+
export const isInputTranscription = (a: unknown): a is AdkEvent =>
|
|
277
|
+
isAdkEvent(a) && (a as AdkEvent).input_transcription !== undefined;
|
|
278
|
+
|
|
279
|
+
export const isOutputTranscription = (a: unknown): a is AdkEvent =>
|
|
280
|
+
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
|
|
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(([
|
|
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,15 +78,27 @@ 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`;
|
|
91
|
+
url = url || defaultWsUrl;
|
|
78
92
|
this.url = new URL("ws", url).href;
|
|
79
93
|
this.userId = userId;
|
|
80
94
|
this.runId = runId || crypto.randomUUID(); // Ensure runId is always a string by providing default
|
|
81
95
|
this.send = this.send.bind(this);
|
|
82
96
|
}
|
|
83
97
|
|
|
98
|
+
get currentRunId(): string {
|
|
99
|
+
return this.runId;
|
|
100
|
+
}
|
|
101
|
+
|
|
84
102
|
log(type: string, message: StreamingLog["message"]) {
|
|
85
103
|
const log: StreamingLog = {
|
|
86
104
|
date: new Date(),
|
|
@@ -98,15 +116,37 @@ export class MultimodalLiveClient extends EventEmitter<MultimodalLiveClientEvent
|
|
|
98
116
|
this.runId = newRunId;
|
|
99
117
|
}
|
|
100
118
|
|
|
119
|
+
// Reset connection state
|
|
120
|
+
this.firstContentSent = false;
|
|
121
|
+
this.audioChunksSent = 0;
|
|
122
|
+
this.lastAudioSendTime = 0;
|
|
123
|
+
|
|
101
124
|
ws.addEventListener("message", async (evt: MessageEvent) => {
|
|
102
125
|
if (evt.data instanceof Blob) {
|
|
103
126
|
this.receive(evt.data);
|
|
104
127
|
} else if (typeof evt.data === "string") {
|
|
105
128
|
try {
|
|
106
129
|
const jsonData = JSON.parse(evt.data);
|
|
107
|
-
|
|
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) {
|
|
108
142
|
this.log("server.status", jsonData.status);
|
|
109
|
-
console.log("Status:", jsonData.status);
|
|
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'}));
|
|
110
150
|
}
|
|
111
151
|
} catch (error) {
|
|
112
152
|
console.error("Error parsing message:", error);
|
|
@@ -129,11 +169,12 @@ export class MultimodalLiveClient extends EventEmitter<MultimodalLiveClientEvent
|
|
|
129
169
|
this.emit("open");
|
|
130
170
|
|
|
131
171
|
this.ws = ws;
|
|
132
|
-
// Send initial setup message with
|
|
172
|
+
// Send initial setup message with user_id for backend
|
|
133
173
|
const setupMessage = {
|
|
174
|
+
user_id: this.userId || "default_user",
|
|
134
175
|
setup: {
|
|
135
176
|
run_id: this.runId,
|
|
136
|
-
user_id: this.userId,
|
|
177
|
+
user_id: this.userId || "default_user",
|
|
137
178
|
},
|
|
138
179
|
};
|
|
139
180
|
this._sendDirect(setupMessage);
|
|
@@ -213,11 +254,18 @@ export class MultimodalLiveClient extends EventEmitter<MultimodalLiveClientEvent
|
|
|
213
254
|
if (isModelTurn(serverContent)) {
|
|
214
255
|
let parts: Part[] = serverContent.modelTurn.parts;
|
|
215
256
|
|
|
216
|
-
// when its audio that is returned for modelTurn
|
|
257
|
+
// when its audio that is returned for modelTurn (check both camelCase and snake_case)
|
|
217
258
|
const audioParts = parts.filter(
|
|
218
|
-
(p) =>
|
|
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
|
+
}
|
|
219
264
|
);
|
|
220
|
-
const base64s = audioParts.map((p) =>
|
|
265
|
+
const base64s = audioParts.map((p: any) => {
|
|
266
|
+
const inlineData = p.inlineData || p.inline_data;
|
|
267
|
+
return inlineData?.data;
|
|
268
|
+
});
|
|
221
269
|
|
|
222
270
|
// strip the audio parts out of the modelTurn
|
|
223
271
|
const otherParts = difference(parts, audioParts);
|
|
@@ -240,6 +288,98 @@ export class MultimodalLiveClient extends EventEmitter<MultimodalLiveClientEvent
|
|
|
240
288
|
this.emit("content", content);
|
|
241
289
|
this.log(`server.content`, response);
|
|
242
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
|
+
}
|
|
243
383
|
} else {
|
|
244
384
|
console.log("received unmatched message", response);
|
|
245
385
|
this.log("received unmatched message", response);
|
|
@@ -250,6 +390,12 @@ export class MultimodalLiveClient extends EventEmitter<MultimodalLiveClientEvent
|
|
|
250
390
|
* send realtimeInput, this is base64 chunks of "audio/pcm" and/or "image/jpg"
|
|
251
391
|
*/
|
|
252
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
|
+
|
|
253
399
|
let hasAudio = false;
|
|
254
400
|
let hasVideo = false;
|
|
255
401
|
for (let i = 0; i < chunks.length; i++) {
|
|
@@ -264,6 +410,25 @@ export class MultimodalLiveClient extends EventEmitter<MultimodalLiveClientEvent
|
|
|
264
410
|
break;
|
|
265
411
|
}
|
|
266
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
|
+
|
|
267
432
|
const message =
|
|
268
433
|
hasAudio && hasVideo
|
|
269
434
|
? "audio + video"
|
|
@@ -273,12 +438,26 @@ export class MultimodalLiveClient extends EventEmitter<MultimodalLiveClientEvent
|
|
|
273
438
|
? "video"
|
|
274
439
|
: "unknown";
|
|
275
440
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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
|
+
}
|
|
282
461
|
this.log(`client.realtimeInput`, message);
|
|
283
462
|
}
|
|
284
463
|
|
|
@@ -297,22 +476,29 @@ export class MultimodalLiveClient extends EventEmitter<MultimodalLiveClientEvent
|
|
|
297
476
|
/**
|
|
298
477
|
* send normal content parts such as { text }
|
|
299
478
|
*/
|
|
300
|
-
send(parts: Part | Part[],
|
|
479
|
+
send(parts: Part | Part[], _turnComplete: boolean = true) {
|
|
301
480
|
parts = Array.isArray(parts) ? parts : [parts];
|
|
302
481
|
const content: Content = {
|
|
303
482
|
role: "user",
|
|
304
483
|
parts,
|
|
305
484
|
};
|
|
306
485
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
turnComplete,
|
|
311
|
-
},
|
|
486
|
+
// Convert to LiveRequest format for backend
|
|
487
|
+
let data: any = {
|
|
488
|
+
content: content,
|
|
312
489
|
};
|
|
313
490
|
|
|
314
|
-
|
|
315
|
-
this.
|
|
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`);
|
|
316
502
|
}
|
|
317
503
|
|
|
318
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
}
|