agent-starter-pack 0.15.7__py3-none-any.whl → 0.17.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (195) hide show
  1. {agents → agent_starter_pack/agents}/adk_base/.template/templateconfig.yaml +1 -1
  2. {agents/live_api → agent_starter_pack/agents/adk_live}/.template/templateconfig.yaml +5 -7
  3. agent_starter_pack/agents/adk_live/README.md +32 -0
  4. agent_starter_pack/agents/adk_live/app/agent.py +48 -0
  5. agent_starter_pack/agents/adk_live/tests/unit/test_dummy.py +38 -0
  6. {agents → agent_starter_pack/agents}/agentic_rag/.template/templateconfig.yaml +1 -1
  7. agent_starter_pack/agents/crewai_coding_crew/app/agent.py +47 -0
  8. agent_starter_pack/agents/langgraph_base_react/app/agent.py +34 -0
  9. {src → agent_starter_pack}/base_template/GEMINI.md +1 -1
  10. {src → agent_starter_pack}/base_template/Makefile +130 -61
  11. {src → agent_starter_pack}/base_template/README.md +6 -6
  12. {src → agent_starter_pack}/base_template/deployment/terraform/dev/apis.tf +1 -1
  13. {src → agent_starter_pack}/base_template/deployment/terraform/dev/log_sinks.tf +31 -25
  14. {src → agent_starter_pack}/base_template/deployment/terraform/dev/providers.tf +1 -1
  15. {src → agent_starter_pack}/base_template/deployment/terraform/dev/variables.tf +1 -1
  16. {src → agent_starter_pack}/base_template/deployment/terraform/github.tf +14 -0
  17. {src → agent_starter_pack}/base_template/deployment/terraform/locals.tf +1 -1
  18. {src → agent_starter_pack}/base_template/deployment/terraform/log_sinks.tf +37 -28
  19. {src → agent_starter_pack}/base_template/deployment/terraform/providers.tf +1 -1
  20. {src → agent_starter_pack}/base_template/deployment/terraform/variables.tf +1 -1
  21. {src → agent_starter_pack}/base_template/deployment/terraform/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}build_triggers.tf{% else %}unused_build_triggers.tf{% endif %} +4 -2
  22. {src → agent_starter_pack}/base_template/pyproject.toml +22 -21
  23. {src → agent_starter_pack}/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/deploy-to-prod.yaml +5 -5
  24. {src → agent_starter_pack}/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/pr_checks.yaml +3 -3
  25. {src → agent_starter_pack}/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/staging.yaml +74 -11
  26. {src → agent_starter_pack}/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/deploy-to-prod.yaml +6 -6
  27. {src → agent_starter_pack}/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/pr_checks.yaml +4 -4
  28. {src → agent_starter_pack}/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/staging.yaml +95 -13
  29. {src → agent_starter_pack}/base_template/{{cookiecutter.agent_directory}}/utils/tracing.py +1 -1
  30. {src → agent_starter_pack}/base_template/{{cookiecutter.agent_directory}}/utils/typing.py +4 -4
  31. {src → agent_starter_pack}/cli/commands/setup_cicd.py +1 -1
  32. {src → agent_starter_pack}/cli/main.py +2 -2
  33. {src → agent_starter_pack}/cli/utils/gcp.py +1 -1
  34. {src → agent_starter_pack}/cli/utils/remote_template.py +12 -9
  35. {src → agent_starter_pack}/cli/utils/template.py +19 -15
  36. agent_starter_pack/deployment_targets/agent_engine/deployment/terraform/{% if not cookiecutter.is_adk_live %}service.tf{% else %}unused_service.tf{% endif %} +82 -0
  37. agent_starter_pack/deployment_targets/agent_engine/tests/integration/test_agent_engine_app.py +387 -0
  38. agent_starter_pack/deployment_targets/agent_engine/tests/load_test/README.md +84 -0
  39. agent_starter_pack/deployment_targets/agent_engine/tests/load_test/load_test.py +255 -0
  40. {src → agent_starter_pack}/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/agent_engine_app.py +40 -14
  41. {src → agent_starter_pack}/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/utils/deployment.py +13 -4
  42. agent_starter_pack/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/utils/{% if cookiecutter.is_adk_live %}expose_app.py{% else %}unused_expose_app.py{% endif %} +520 -0
  43. {src → agent_starter_pack}/deployment_targets/cloud_run/Dockerfile +3 -3
  44. {src → agent_starter_pack}/deployment_targets/cloud_run/deployment/terraform/dev/service.tf +4 -4
  45. {src → agent_starter_pack}/deployment_targets/cloud_run/deployment/terraform/service.tf +7 -7
  46. {src → agent_starter_pack}/deployment_targets/cloud_run/tests/integration/test_server_e2e.py +207 -5
  47. {src → agent_starter_pack}/deployment_targets/cloud_run/tests/load_test/README.md +82 -0
  48. agent_starter_pack/deployment_targets/cloud_run/tests/load_test/load_test.py +249 -0
  49. {src → agent_starter_pack}/deployment_targets/cloud_run/{{cookiecutter.agent_directory}}/server.py +190 -146
  50. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/package-lock.json +39 -1007
  51. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/package.json +1 -9
  52. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/App.tsx +1 -1
  53. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/logger/Logger.tsx +8 -3
  54. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/logger/logger.scss +26 -0
  55. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/side-panel/SidePanel.tsx +11 -5
  56. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/side-panel/side-panel.scss +146 -115
  57. agent_starter_pack/frontends/adk_live_react/frontend/src/components/transcription-preview/TranscriptionPreview.tsx +106 -0
  58. agent_starter_pack/frontends/adk_live_react/frontend/src/components/transcription-preview/transcription-preview.scss +150 -0
  59. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-live-api.ts +8 -2
  60. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/multimodal-live-types.ts +38 -2
  61. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/audio-recorder.ts +1 -1
  62. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/audio-streamer.ts +1 -1
  63. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/multimodal-live-client.ts +204 -23
  64. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/utils.ts +27 -5
  65. {src → agent_starter_pack}/frontends/streamlit/frontend/utils/local_chat_history.py +2 -0
  66. {src → agent_starter_pack}/resources/docs/adk-cheatsheet.md +5 -5
  67. agent_starter_pack/resources/idx/.idx/dev.nix +64 -0
  68. agent_starter_pack/resources/idx/idx-template.json +6 -0
  69. {src → agent_starter_pack}/resources/idx/idx-template.nix +2 -3
  70. {src → agent_starter_pack}/resources/locks/uv-adk_base-agent_engine.lock +1079 -954
  71. {src → agent_starter_pack}/resources/locks/uv-adk_base-cloud_run.lock +1441 -1309
  72. agent_starter_pack/resources/locks/uv-adk_live-agent_engine.lock +4229 -0
  73. agent_starter_pack/resources/locks/uv-adk_live-cloud_run.lock +4822 -0
  74. {src → agent_starter_pack}/resources/locks/uv-agentic_rag-agent_engine.lock +1107 -997
  75. {src → agent_starter_pack}/resources/locks/uv-agentic_rag-cloud_run.lock +1485 -1368
  76. {src → agent_starter_pack}/resources/locks/uv-crewai_coding_crew-agent_engine.lock +1294 -1297
  77. {src → agent_starter_pack}/resources/locks/uv-crewai_coding_crew-cloud_run.lock +2028 -1807
  78. {src → agent_starter_pack}/resources/locks/uv-langgraph_base_react-agent_engine.lock +1176 -1197
  79. {src → agent_starter_pack}/resources/locks/uv-langgraph_base_react-cloud_run.lock +1947 -1679
  80. {src → agent_starter_pack}/utils/generate_locks.py +12 -7
  81. {src → agent_starter_pack}/utils/lock_utils.py +2 -2
  82. {src → agent_starter_pack}/utils/watch_and_rebuild.py +1 -1
  83. {agent_starter_pack-0.15.7.dist-info → agent_starter_pack-0.17.0.dist-info}/METADATA +17 -18
  84. agent_starter_pack-0.17.0.dist-info/RECORD +179 -0
  85. agent_starter_pack-0.17.0.dist-info/entry_points.txt +2 -0
  86. llm.txt +1 -1
  87. agent_starter_pack-0.15.7.dist-info/RECORD +0 -176
  88. agent_starter_pack-0.15.7.dist-info/entry_points.txt +0 -2
  89. agents/crewai_coding_crew/app/agent.py +0 -86
  90. agents/langgraph_base_react/app/agent.py +0 -73
  91. agents/live_api/README.md +0 -37
  92. agents/live_api/app/agent.py +0 -72
  93. agents/live_api/tests/integration/test_server_e2e.py +0 -260
  94. agents/live_api/tests/load_test/load_test.py +0 -40
  95. agents/live_api/tests/unit/test_server.py +0 -144
  96. src/deployment_targets/agent_engine/tests/integration/test_agent_engine_app.py +0 -186
  97. src/deployment_targets/agent_engine/tests/load_test/README.md +0 -37
  98. src/deployment_targets/agent_engine/tests/load_test/load_test.py +0 -126
  99. src/deployment_targets/cloud_run/tests/load_test/load_test.py +0 -122
  100. src/resources/idx/.idx/dev.nix +0 -50
  101. src/resources/idx/idx-template.json +0 -21
  102. src/resources/locks/uv-live_api-cloud_run.lock +0 -6118
  103. {agents → agent_starter_pack/agents}/README.md +0 -0
  104. {agents → agent_starter_pack/agents}/adk_base/README.md +0 -0
  105. {agents → agent_starter_pack/agents}/adk_base/app/__init__.py +0 -0
  106. {agents → agent_starter_pack/agents}/adk_base/app/agent.py +0 -0
  107. {agents → agent_starter_pack/agents}/adk_base/notebooks/adk_app_testing.ipynb +0 -0
  108. {agents → agent_starter_pack/agents}/adk_base/notebooks/evaluating_adk_agent.ipynb +0 -0
  109. {agents → agent_starter_pack/agents}/adk_base/tests/integration/test_agent.py +0 -0
  110. {agents → agent_starter_pack/agents}/agentic_rag/README.md +0 -0
  111. {agents → agent_starter_pack/agents}/agentic_rag/app/__init__.py +0 -0
  112. {agents → agent_starter_pack/agents}/agentic_rag/app/agent.py +0 -0
  113. {agents → agent_starter_pack/agents}/agentic_rag/app/retrievers.py +0 -0
  114. {agents → agent_starter_pack/agents}/agentic_rag/app/templates.py +0 -0
  115. {agents → agent_starter_pack/agents}/agentic_rag/notebooks/adk_app_testing.ipynb +0 -0
  116. {agents → agent_starter_pack/agents}/agentic_rag/notebooks/evaluating_adk_agent.ipynb +0 -0
  117. {agents → agent_starter_pack/agents}/agentic_rag/tests/integration/test_agent.py +0 -0
  118. {agents → agent_starter_pack/agents}/crewai_coding_crew/.template/templateconfig.yaml +0 -0
  119. {agents → agent_starter_pack/agents}/crewai_coding_crew/README.md +0 -0
  120. {agents → agent_starter_pack/agents}/crewai_coding_crew/app/crew/config/agents.yaml +0 -0
  121. {agents → agent_starter_pack/agents}/crewai_coding_crew/app/crew/config/tasks.yaml +0 -0
  122. {agents → agent_starter_pack/agents}/crewai_coding_crew/app/crew/crew.py +0 -0
  123. {agents → agent_starter_pack/agents}/crewai_coding_crew/notebooks/evaluating_crewai_agent.ipynb +0 -0
  124. {agents → agent_starter_pack/agents}/crewai_coding_crew/notebooks/evaluating_langgraph_agent.ipynb +0 -0
  125. {agents → agent_starter_pack/agents}/crewai_coding_crew/tests/integration/test_agent.py +0 -0
  126. {agents → agent_starter_pack/agents}/langgraph_base_react/.template/templateconfig.yaml +0 -0
  127. {agents → agent_starter_pack/agents}/langgraph_base_react/README.md +0 -0
  128. {agents → agent_starter_pack/agents}/langgraph_base_react/notebooks/evaluating_langgraph_agent.ipynb +0 -0
  129. {agents → agent_starter_pack/agents}/langgraph_base_react/tests/integration/test_agent.py +0 -0
  130. {src → agent_starter_pack}/base_template/.gitignore +0 -0
  131. {src → agent_starter_pack}/base_template/deployment/README.md +0 -0
  132. {src → agent_starter_pack}/base_template/deployment/terraform/apis.tf +0 -0
  133. {src → agent_starter_pack}/base_template/deployment/terraform/dev/iam.tf +0 -0
  134. {src → agent_starter_pack}/base_template/deployment/terraform/dev/storage.tf +0 -0
  135. {src → agent_starter_pack}/base_template/deployment/terraform/dev/vars/env.tfvars +0 -0
  136. {src → agent_starter_pack}/base_template/deployment/terraform/iam.tf +0 -0
  137. {src → agent_starter_pack}/base_template/deployment/terraform/service_accounts.tf +0 -0
  138. {src → agent_starter_pack}/base_template/deployment/terraform/storage.tf +0 -0
  139. {src → agent_starter_pack}/base_template/deployment/terraform/vars/env.tfvars +0 -0
  140. {src → agent_starter_pack}/base_template/deployment/terraform/{% if cookiecutter.cicd_runner == 'github_actions' %}wif.tf{% else %}unused_wif.tf{% endif %} +0 -0
  141. {src → agent_starter_pack}/base_template/tests/unit/test_dummy.py +0 -0
  142. {src → agent_starter_pack}/base_template/{{cookiecutter.agent_directory}}/utils/gcs.py +0 -0
  143. {src → agent_starter_pack}/cli/commands/create.py +0 -0
  144. {src → agent_starter_pack}/cli/commands/enhance.py +0 -0
  145. {src → agent_starter_pack}/cli/commands/list.py +0 -0
  146. {src → agent_starter_pack}/cli/utils/__init__.py +0 -0
  147. {src → agent_starter_pack}/cli/utils/cicd.py +0 -0
  148. {src → agent_starter_pack}/cli/utils/datastores.py +0 -0
  149. {src → agent_starter_pack}/cli/utils/logging.py +0 -0
  150. {src → agent_starter_pack}/cli/utils/version.py +0 -0
  151. {src → agent_starter_pack}/data_ingestion/README.md +0 -0
  152. {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/components/ingest_data.py +0 -0
  153. {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/components/process_data.py +0 -0
  154. {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/pipeline.py +0 -0
  155. {src → agent_starter_pack}/data_ingestion/data_ingestion_pipeline/submit_pipeline.py +0 -0
  156. {src → agent_starter_pack}/data_ingestion/pyproject.toml +0 -0
  157. {src → agent_starter_pack}/data_ingestion/uv.lock +0 -0
  158. {src → agent_starter_pack}/deployment_targets/agent_engine/deployment_metadata.json +0 -0
  159. {src → agent_starter_pack}/deployment_targets/agent_engine/notebooks/intro_agent_engine.ipynb +0 -0
  160. {src → agent_starter_pack}/deployment_targets/agent_engine/tests/load_test/.results/.placeholder +0 -0
  161. {src → agent_starter_pack}/deployment_targets/cloud_run/tests/load_test/.results/.placeholder +0 -0
  162. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/public/favicon.ico +0 -0
  163. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/public/index.html +0 -0
  164. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/public/robots.txt +0 -0
  165. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/App.scss +0 -0
  166. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/App.test.tsx +0 -0
  167. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/audio-pulse/AudioPulse.tsx +0 -0
  168. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/audio-pulse/audio-pulse.scss +0 -0
  169. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/components/logger/mock-logs.ts +0 -0
  170. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/contexts/LiveAPIContext.tsx +0 -0
  171. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-media-stream-mux.ts +0 -0
  172. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-screen-capture.ts +0 -0
  173. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/hooks/use-webcam.ts +0 -0
  174. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/index.css +0 -0
  175. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/index.tsx +0 -0
  176. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/react-app-env.d.ts +0 -0
  177. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/reportWebVitals.ts +0 -0
  178. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/setupTests.ts +0 -0
  179. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/audioworklet-registry.ts +0 -0
  180. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/store-logger.ts +0 -0
  181. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/worklets/audio-processing.ts +0 -0
  182. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/src/utils/worklets/vol-meter.ts +0 -0
  183. {src/frontends/live_api_react → agent_starter_pack/frontends/adk_live_react}/frontend/tsconfig.json +0 -0
  184. {src → agent_starter_pack}/frontends/streamlit/frontend/side_bar.py +0 -0
  185. {src → agent_starter_pack}/frontends/streamlit/frontend/streamlit_app.py +0 -0
  186. {src → agent_starter_pack}/frontends/streamlit/frontend/style/app_markdown.py +0 -0
  187. {src → agent_starter_pack}/frontends/streamlit/frontend/utils/chat_utils.py +0 -0
  188. {src → agent_starter_pack}/frontends/streamlit/frontend/utils/message_editing.py +0 -0
  189. {src → agent_starter_pack}/frontends/streamlit/frontend/utils/multimodal_utils.py +0 -0
  190. {src → agent_starter_pack}/frontends/streamlit/frontend/utils/stream_handler.py +0 -0
  191. {src → agent_starter_pack}/frontends/streamlit/frontend/utils/title_summary.py +0 -0
  192. {src → agent_starter_pack}/resources/containers/data_processing/Dockerfile +0 -0
  193. {src → agent_starter_pack}/resources/containers/e2e-tests/Dockerfile +0 -0
  194. {agent_starter_pack-0.15.7.dist-info → agent_starter_pack-0.17.0.dist-info}/WHEEL +0 -0
  195. {agent_starter_pack-0.15.7.dist-info → agent_starter_pack-0.17.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,144 +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 json
16
- import logging
17
- import os
18
- from collections.abc import Generator
19
- from unittest.mock import AsyncMock, MagicMock, patch
20
-
21
- import pytest
22
- from fastapi.testclient import TestClient
23
- from google.auth.credentials import Credentials
24
-
25
- # Set up logging
26
- logging.basicConfig(level=logging.INFO)
27
- logger = logging.getLogger(__name__)
28
-
29
-
30
- @pytest.fixture(autouse=True)
31
- def mock_google_cloud_credentials() -> Generator[None, None, None]:
32
- """Mock Google Cloud credentials for testing."""
33
- with patch.dict(
34
- os.environ,
35
- {
36
- "GOOGLE_APPLICATION_CREDENTIALS": "/path/to/mock/credentials.json",
37
- "GOOGLE_CLOUD_PROJECT_ID": "mock-project-id",
38
- },
39
- ):
40
- yield
41
-
42
-
43
- @pytest.fixture(autouse=True)
44
- def mock_google_auth_default() -> Generator[None, None, None]:
45
- """Mock the google.auth.default function for testing."""
46
- mock_credentials = MagicMock(spec=Credentials)
47
- mock_project = "mock-project-id"
48
-
49
- with patch("google.auth.default", return_value=(mock_credentials, mock_project)):
50
- yield
51
-
52
-
53
- @pytest.fixture(autouse=True)
54
- def mock_dependencies() -> Generator[None, None, None]:
55
- """
56
- Mock Vertex AI dependencies for testing.
57
- Patches genai client and tool functions.
58
- """
59
- with (
60
- patch("{{cookiecutter.agent_directory}}.server.genai_client") as mock_genai,
61
- patch("{{cookiecutter.agent_directory}}.server.tool_functions") as mock_tools,
62
- ):
63
- mock_genai.aio.live.connect = AsyncMock()
64
- mock_tools.return_value = {}
65
- yield
66
-
67
-
68
- @pytest.mark.asyncio
69
- async def test_websocket_endpoint() -> None:
70
- """
71
- Test the websocket endpoint to ensure it correctly handles
72
- websocket connections and messages.
73
- """
74
- from {{cookiecutter.agent_directory}}.server import app
75
-
76
- mock_session = AsyncMock()
77
- mock_session._ws = AsyncMock()
78
- # Configure mock to return proper response format and close after one message
79
- mock_session._ws.recv.side_effect = [
80
- json.dumps(
81
- {
82
- "serverContent": {
83
- "modelTurn": {
84
- "role": "model",
85
- "parts": [{"text": "Hello, how can I help you?"}],
86
- }
87
- }
88
- }
89
- ).encode(), # Encode as bytes since recv(decode=False) is used
90
- None, # Add None to trigger StopAsyncIteration after first message
91
- ]
92
-
93
- with patch("{{cookiecutter.agent_directory}}.server.genai_client") as mock_genai:
94
- mock_genai.aio.live.connect.return_value.__aenter__.return_value = mock_session
95
- client = TestClient(app)
96
- with client.websocket_connect("/ws") as websocket:
97
- # Test initial connection message
98
- data = websocket.receive_json()
99
- assert data["status"] == "Backend is ready for conversation"
100
-
101
- # Test sending a message
102
- websocket.send_json(
103
- {"setup": {"run_id": "test-run", "user_id": "test-user"}}
104
- )
105
-
106
- # Test sending audio stream
107
- dummy_audio = bytes([0] * 1024) # 1KB of silence
108
- websocket.send_json(
109
- {
110
- "realtimeInput": {
111
- "mediaChunks": [
112
- {
113
- "mimeType": "audio/pcm;rate=16000",
114
- "data": dummy_audio.hex(),
115
- }
116
- ]
117
- }
118
- }
119
- )
120
-
121
- # Receive response as bytes
122
- response = websocket.receive_bytes()
123
- response_data = json.loads(response.decode())
124
- assert "serverContent" in response_data
125
-
126
- # Verify mock interactions
127
- mock_genai.aio.live.connect.assert_called_once()
128
- assert mock_session._ws.recv.called
129
- await mock_session._ws.recv.aclose()
130
-
131
-
132
- @pytest.mark.asyncio
133
- def test_websocket_error_handling() -> None:
134
- """Test websocket error handling."""
135
- from {{cookiecutter.agent_directory}}.server import app
136
-
137
- with patch("{{cookiecutter.agent_directory}}.server.genai_client") as mock_genai:
138
- mock_genai.aio.live.connect.side_effect = Exception("Connection failed")
139
-
140
- client = TestClient(app)
141
- with pytest.raises(Exception) as exc:
142
- with client.websocket_connect("/ws"):
143
- pass
144
- assert str(exc.value) == "Connection failed"
@@ -1,186 +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
-
17
- import pytest
18
- {%- if "adk" in cookiecutter.tags %}
19
- from google.adk.events.event import Event
20
-
21
- from {{cookiecutter.agent_directory}}.agent import root_agent
22
- from {{cookiecutter.agent_directory}}.agent_engine_app import AgentEngineApp
23
- {%- else %}
24
-
25
- from {{cookiecutter.agent_directory}}.agent_engine_app import AgentEngineApp
26
- {%- endif %}
27
-
28
-
29
- @pytest.fixture
30
- def agent_app() -> AgentEngineApp:
31
- """Fixture to create and set up AgentEngineApp instance"""
32
- {%- if "adk" in cookiecutter.tags %}
33
- app = AgentEngineApp(agent=root_agent)
34
- {%- else %}
35
- app = AgentEngineApp()
36
- {%- endif %}
37
- app.set_up()
38
- return app
39
-
40
- {% if "adk" in cookiecutter.tags %}
41
- @pytest.mark.asyncio
42
- async def test_agent_stream_query(agent_app: AgentEngineApp) -> None:
43
- """
44
- Integration test for the agent stream query functionality.
45
- Tests that the agent returns valid streaming responses.
46
- """
47
- # Create message and events for the async_stream_query
48
- message = "What's the weather in San Francisco?"
49
- events = []
50
- async for event in agent_app.async_stream_query(message=message, user_id="test"):
51
- events.append(event)
52
- assert len(events) > 0, "Expected at least one chunk in response"
53
-
54
- # Check for valid content in the response
55
- has_text_content = False
56
- for event in events:
57
- validated_event = Event.model_validate(event)
58
- content = validated_event.content
59
- if (
60
- content is not None
61
- and content.parts
62
- and any(part.text for part in content.parts)
63
- ):
64
- has_text_content = True
65
- break
66
-
67
- assert has_text_content, "Expected at least one event with text content"
68
-
69
-
70
- def test_agent_feedback(agent_app: AgentEngineApp) -> None:
71
- """
72
- Integration test for the agent feedback functionality.
73
- Tests that feedback can be registered successfully.
74
- """
75
- feedback_data = {
76
- "score": 5,
77
- "text": "Great response!",
78
- "invocation_id": "test-run-123",
79
- }
80
-
81
- # Should not raise any exceptions
82
- agent_app.register_feedback(feedback_data)
83
-
84
- # Test invalid feedback
85
- with pytest.raises(ValueError):
86
- invalid_feedback = {
87
- "score": "invalid", # Score must be numeric
88
- "text": "Bad feedback",
89
- "invocation_id": "test-run-123",
90
- }
91
- agent_app.register_feedback(invalid_feedback)
92
-
93
- logging.info("All assertions passed for agent feedback test")
94
- {% else %}
95
- def test_agent_stream_query(agent_app: AgentEngineApp) -> None:
96
- """
97
- Integration test for the agent stream query functionality.
98
- Tests that the agent returns valid streaming responses.
99
- """
100
- input_dict = {
101
- "messages": [
102
- {"type": "human", "content": "Test message"},
103
- ],
104
- "user_id": "test-user",
105
- "session_id": "test-session",
106
- }
107
-
108
- events = list(agent_app.stream_query(input=input_dict))
109
-
110
- assert len(events) > 0, "Expected at least one chunk in response"
111
-
112
- # Verify each event is a tuple of message and metadata
113
- for event in events:
114
- assert isinstance(event, list), "Event should be a list"
115
- assert len(event) == 2, "Event should contain message and metadata"
116
- message, _ = event
117
-
118
- # Verify message structure
119
- assert isinstance(message, dict), "Message should be a dictionary"
120
- assert message["type"] == "constructor"
121
- assert "kwargs" in message, "Constructor message should have kwargs"
122
-
123
- # Verify at least one message has content
124
- has_content = False
125
- for event in events:
126
- message = event[0]
127
- if message.get("type") == "constructor" and "content" in message["kwargs"]:
128
- has_content = True
129
- break
130
- assert has_content, "At least one message should have content"
131
-
132
-
133
- def test_agent_query(agent_app: AgentEngineApp) -> None:
134
- """
135
- Integration test for the agent query functionality.
136
- Tests that the agent returns valid responses.
137
- """
138
- input_dict = {
139
- "messages": [
140
- {"type": "human", "content": "Test message"},
141
- ],
142
- "user_id": "test-user",
143
- "session_id": "test-session",
144
- }
145
-
146
- response = agent_app.query(input=input_dict)
147
-
148
- # Basic response validation
149
- assert isinstance(response, dict), "Response should be a dictionary"
150
- assert "messages" in response, "Response should contain messages"
151
- assert len(response["messages"]) > 0, "Response should have at least one message"
152
-
153
- # Validate last message is AI response with content
154
- message = response["messages"][-1]
155
- kwargs = message["kwargs"]
156
- assert kwargs["type"] == "ai", "Last message should be AI response"
157
- assert len(kwargs["content"]) > 0, "AI message content should not be empty"
158
-
159
- logging.info("All assertions passed for agent query test")
160
-
161
-
162
- def test_agent_feedback(agent_app: AgentEngineApp) -> None:
163
- """
164
- Integration test for the agent feedback functionality.
165
- Tests that feedback can be registered successfully.
166
- """
167
- feedback_data = {
168
- "score": 5,
169
- "text": "Great response!",
170
- "run_id": "test-run-123",
171
- }
172
-
173
- # Should not raise any exceptions
174
- agent_app.register_feedback(feedback_data)
175
-
176
- # Test invalid feedback
177
- with pytest.raises(ValueError):
178
- invalid_feedback = {
179
- "score": "invalid", # Score must be numeric
180
- "text": "Bad feedback",
181
- "run_id": "test-run-123",
182
- }
183
- agent_app.register_feedback(invalid_feedback)
184
-
185
- logging.info("All assertions passed for agent feedback test")
186
- {% endif %}
@@ -1,37 +0,0 @@
1
- # Robust Load Testing for Generative AI Applications
2
-
3
- This directory provides a comprehensive load testing framework for your Generative AI application, leveraging the power of [Locust](http://locust.io), a leading open-source load testing tool.
4
-
5
- ## Load Testing
6
-
7
- Before running load tests, ensure you have deployed the backend remotely.
8
-
9
- Follow these steps to execute load tests:
10
-
11
- **1. Deploy the Backend Remotely:**
12
- ```bash
13
- gcloud config set project <your-dev-project-id>
14
- make backend
15
- ```
16
-
17
- **2. Create a Virtual Environment for Locust:**
18
- It's recommended to use a separate terminal tab and create a virtual environment for Locust to avoid conflicts with your application's Python environment.
19
-
20
- ```bash
21
- python3 -m venv .locust_env && source .locust_env/bin/activate && pip install locust==2.31.1
22
- ```
23
-
24
- **3. Execute the Load Test:**
25
- Trigger the Locust load test with the following command:
26
-
27
- ```bash
28
- export _AUTH_TOKEN=$(gcloud auth print-access-token -q)
29
- locust -f tests/load_test/load_test.py \
30
- --headless \
31
- -t 30s -u 5 -r 2 \
32
- --csv=tests/load_test/.results/results \
33
- --html=tests/load_test/.results/report.html
34
- ```
35
-
36
- This command initiates a 30-second load test, simulating 2 users spawning per second, reaching a maximum of 10 concurrent users.
37
-
@@ -1,126 +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 json
16
- import logging
17
- import os
18
- import time
19
-
20
- from locust import HttpUser, between, task
21
-
22
- # Configure logging
23
- logging.basicConfig(
24
- level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
25
- )
26
- logger = logging.getLogger(__name__)
27
-
28
- # Initialize Vertex AI and load agent config
29
- with open("deployment_metadata.json") as f:
30
- remote_agent_engine_id = json.load(f)["remote_agent_engine_id"]
31
-
32
- parts = remote_agent_engine_id.split("/")
33
- project_id = parts[1]
34
- location = parts[3]
35
- engine_id = parts[5]
36
-
37
- # Convert remote agent engine ID to streaming URL.
38
- base_url = f"https://{location}-aiplatform.googleapis.com"
39
- url_path = f"/v1/projects/{project_id}/locations/{location}/reasoningEngines/{engine_id}:streamQuery"
40
-
41
- logger.info("Using remote agent engine ID: %s", remote_agent_engine_id)
42
- logger.info("Using base URL: %s", base_url)
43
- logger.info("Using URL path: %s", url_path)
44
-
45
-
46
- class ChatStreamUser(HttpUser):
47
- """Simulates a user interacting with the chat stream API."""
48
-
49
- wait_time = between(1, 3) # Wait 1-3 seconds between tasks
50
- host = base_url # Set the base host URL for Locust
51
-
52
- @task
53
- def chat_stream(self) -> None:
54
- """Simulates a chat stream interaction."""
55
- headers = {"Content-Type": "application/json"}
56
- headers["Authorization"] = f"Bearer {os.environ['_AUTH_TOKEN']}"
57
- {% if "adk" in cookiecutter.tags %}
58
- data = {
59
- "class_method": "async_stream_query",
60
- "input": {
61
- "user_id": "test",
62
- "message": "What's the weather in San Francisco?",
63
- },
64
- }
65
- {% else %}
66
- data = {
67
- "input": {
68
- "input": {
69
- "messages": [
70
- {"type": "human", "content": "Hello, AI!"},
71
- {"type": "ai", "content": "Hello!"},
72
- {"type": "human", "content": "How are you?"},
73
- ]
74
- },
75
- "config": {
76
- "metadata": {"user_id": "test-user", "session_id": "test-session"}
77
- },
78
- }
79
- }
80
- {% endif %}
81
- start_time = time.time()
82
- with self.client.post(
83
- url_path,
84
- headers=headers,
85
- json=data,
86
- catch_response=True,
87
- {%- if "adk" in cookiecutter.tags %}
88
- name="/streamQuery async_stream_query",
89
- {%- else %}
90
- name="/stream_messages first message",
91
- {%- endif %}
92
- stream=True,
93
- params={"alt": "sse"},
94
- ) as response:
95
- if response.status_code == 200:
96
- events = []
97
- for line in response.iter_lines():
98
- if line:
99
- line_str = line.decode("utf-8")
100
- events.append(line_str)
101
-
102
- if "429 Too Many Requests" in line_str:
103
- self.environment.events.request.fire(
104
- request_type="POST",
105
- name=f"{url_path} rate_limited 429s",
106
- response_time=0,
107
- response_length=len(line),
108
- response=response,
109
- context={},
110
- )
111
- end_time = time.time()
112
- total_time = end_time - start_time
113
- self.environment.events.request.fire(
114
- request_type="POST",
115
- {%- if "adk" in cookiecutter.tags %}
116
- name="/streamQuery end",
117
- {%- else %}
118
- name="/stream_messages end",
119
- {%- endif %}
120
- response_time=total_time * 1000, # Convert to milliseconds
121
- response_length=len(events),
122
- response=response,
123
- context={},
124
- )
125
- else:
126
- response.failure(f"Unexpected status code: {response.status_code}")
@@ -1,122 +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
- import time
17
- {%- if "adk" in cookiecutter.tags %}
18
- import uuid
19
-
20
- import requests
21
- from locust import HttpUser, between, task
22
- {%- else %}
23
-
24
- from locust import HttpUser, between, task
25
- {%- endif %}
26
- {% if "adk" in cookiecutter.tags %}
27
- ENDPOINT = "/run_sse"
28
- {% else %}
29
- ENDPOINT = "/stream_messages"
30
- {% endif %}
31
-
32
- class ChatStreamUser(HttpUser):
33
- """Simulates a user interacting with the chat stream API."""
34
-
35
- wait_time = between(1, 3) # Wait 1-3 seconds between tasks
36
-
37
- @task
38
- def chat_stream(self) -> None:
39
- """Simulates a chat stream interaction."""
40
- headers = {"Content-Type": "application/json"}
41
- if os.environ.get("_ID_TOKEN"):
42
- headers["Authorization"] = f"Bearer {os.environ['_ID_TOKEN']}"
43
- {%- if "adk" in cookiecutter.tags %}
44
- # Create session first
45
- user_id = f"user_{uuid.uuid4()}"
46
- session_data = {"state": {"preferred_language": "English", "visit_count": 1}}
47
-
48
- session_url = f"{self.client.base_url}/apps/{{cookiecutter.agent_directory}}/users/{user_id}/sessions"
49
- session_response = requests.post(
50
- session_url,
51
- headers=headers,
52
- json=session_data,
53
- timeout=10,
54
- )
55
-
56
- # Get session_id from response
57
- session_id = session_response.json()["id"]
58
-
59
- # Send chat message
60
- data = {
61
- "app_name": "{{cookiecutter.agent_directory}}",
62
- "user_id": user_id,
63
- "session_id": session_id,
64
- "new_message": {
65
- "role": "user",
66
- "parts": [{"text": "Hello! Weather in New york?"}],
67
- },
68
- "streaming": True,
69
- }
70
- {%- else %}
71
- data = {
72
- "input": {
73
- "messages": [
74
- {"type": "human", "content": "Hello, AI!"},
75
- {"type": "ai", "content": "Hello!"},
76
- {"type": "human", "content": "Who are you?"},
77
- ]
78
- },
79
- "config": {
80
- "metadata": {"user_id": "test-user", "session_id": "test-session"}
81
- },
82
- }
83
- {%- endif %}
84
- start_time = time.time()
85
-
86
- with self.client.post(
87
- ENDPOINT,
88
- name=f"{ENDPOINT} message",
89
- headers=headers,
90
- json=data,
91
- catch_response=True,
92
- stream=True,
93
- params={"alt": "sse"},
94
- ) as response:
95
- if response.status_code == 200:
96
- events = []
97
- for line in response.iter_lines():
98
- if line:
99
- line_str = line.decode("utf-8")
100
- events.append(line_str)
101
-
102
- if "429 Too Many Requests" in line_str:
103
- self.environment.events.request.fire(
104
- request_type="POST",
105
- name=f"{ENDPOINT} rate_limited 429s",
106
- response_time=0,
107
- response_length=len(line),
108
- response=response,
109
- context={},
110
- )
111
- end_time = time.time()
112
- total_time = end_time - start_time
113
- self.environment.events.request.fire(
114
- request_type="POST",
115
- name=f"{ENDPOINT} end",
116
- response_time=total_time * 1000, # Convert to milliseconds
117
- response_length=len(events),
118
- response=response,
119
- context={},
120
- )
121
- else:
122
- response.failure(f"Unexpected status code: {response.status_code}")
@@ -1,50 +0,0 @@
1
-
2
- # To learn more about how to use Nix to configure your environment
3
- # see: https://firebase.google.com/docs/studio/customize-workspace
4
- { pkgs, ... }: {
5
- # Which nixpkgs channel to use.
6
- channel = "stable-24.11"; # or "unstable"
7
-
8
- # Use https://search.nixos.org/packages to find packages
9
- packages = [
10
- pkgs.uv
11
- pkgs.gnumake
12
- pkgs.terraform
13
- pkgs.gh
14
- ];
15
- # Sets environment variables in the workspace
16
- env = {};
17
- idx = {
18
- # Search for the extensions you want on https://open-vsx.org/ and use "publisher.id"
19
- extensions = [
20
- "ms-toolsai.jupyter"
21
- "ms-python.python"
22
- "krish-r.vscode-toggle-terminal"
23
- ];
24
- workspace = {
25
- # Runs when a workspace is first created with this `dev.nix` file
26
- onCreate = {
27
- create-venv = ''
28
- # Load environment variables from .env file if it exists
29
- source .env
30
- echo "Logging into gcloud..."
31
- echo "Please authenticate with Google Cloud by following the prompts."
32
- gcloud auth login --update-adc --brief --quiet
33
-
34
- echo "Setting gcloud project..."
35
- gcloud config set project $GOOGLE_CLOUD_PROJECT
36
-
37
- echo "Running agent starter pack creation..."
38
- uvx agent-starter-pack create $AGENT_NAME
39
- code ~/$WS_NAME/$AGENT_NAME/README.md
40
- exec bash
41
- '';
42
- # Open editors for the following files by default, if they exist:
43
- default.openFiles = [];
44
- };
45
- # To run something each time the workspace is (re)started, use the `onStart` hook
46
- };
47
- # Enable previews and customize configuration
48
- previews = {};
49
- };
50
- }
@@ -1,21 +0,0 @@
1
- {
2
- "name": "Agent Starter Pack",
3
- "description": "Production-ready Gen AI Agent templates for Google Cloud. Addressing common challenges (Deployment & Operations, Evaluation, Customization, Observability) in building and deploying GenAI agents.",
4
- "icon": "https://github.com/GoogleCloudPlatform/agent-starter-pack/blob/main/docs/images/icon.png?raw=true",
5
- "params": [
6
- {
7
- "id": "agent_name",
8
- "name": "Agent Name",
9
- "description": "The name of the agent being created.",
10
- "type": "string",
11
- "required": true
12
- },
13
- {
14
- "id": "google_cloud_project_id",
15
- "name": "Your Google Cloud Project ID",
16
- "description": "The Google Cloud Platform Project ID where resources will be managed.",
17
- "type": "string",
18
- "required": true
19
- }
20
- ]
21
- }