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