agent-starter-pack 0.15.7__py3-none-any.whl → 0.16.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.
- {agent_starter_pack-0.15.7.dist-info → agent_starter_pack-0.16.0.dist-info}/METADATA +2 -2
- {agent_starter_pack-0.15.7.dist-info → agent_starter_pack-0.16.0.dist-info}/RECORD +96 -94
- agents/adk_base/.template/templateconfig.yaml +1 -1
- agents/{live_api → adk_live}/.template/templateconfig.yaml +5 -7
- agents/adk_live/README.md +31 -0
- agents/adk_live/app/agent.py +48 -0
- agents/adk_live/tests/unit/test_dummy.py +38 -0
- agents/agentic_rag/.template/templateconfig.yaml +1 -1
- agents/crewai_coding_crew/app/agent.py +18 -57
- agents/langgraph_base_react/app/agent.py +7 -46
- llm.txt +1 -1
- src/base_template/GEMINI.md +1 -1
- src/base_template/Makefile +130 -61
- src/base_template/README.md +6 -6
- src/base_template/deployment/terraform/dev/apis.tf +1 -1
- src/base_template/deployment/terraform/dev/variables.tf +1 -1
- src/base_template/deployment/terraform/locals.tf +1 -1
- src/base_template/deployment/terraform/variables.tf +1 -1
- src/base_template/pyproject.toml +22 -21
- src/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/deploy-to-prod.yaml +2 -2
- src/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/pr_checks.yaml +1 -1
- src/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/staging.yaml +71 -8
- src/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/deploy-to-prod.yaml +2 -2
- src/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/pr_checks.yaml +1 -1
- src/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/staging.yaml +90 -8
- src/base_template/{{cookiecutter.agent_directory}}/utils/tracing.py +1 -1
- src/base_template/{{cookiecutter.agent_directory}}/utils/typing.py +4 -4
- src/cli/utils/template.py +12 -5
- src/deployment_targets/agent_engine/tests/integration/test_agent_engine_app.py +205 -4
- src/deployment_targets/agent_engine/tests/load_test/README.md +47 -0
- src/deployment_targets/agent_engine/tests/load_test/load_test.py +132 -3
- src/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/agent_engine_app.py +11 -3
- src/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/utils/deployment.py +5 -1
- src/deployment_targets/agent_engine/{{cookiecutter.agent_directory}}/utils/{% if cookiecutter.is_adk_live %}expose_app.py{% else %}unused_expose_app.py{% endif %} +461 -0
- src/deployment_targets/cloud_run/Dockerfile +3 -3
- src/deployment_targets/cloud_run/deployment/terraform/dev/service.tf +4 -4
- src/deployment_targets/cloud_run/deployment/terraform/service.tf +7 -7
- src/deployment_targets/cloud_run/tests/integration/test_server_e2e.py +207 -5
- src/deployment_targets/cloud_run/tests/load_test/README.md +82 -0
- src/deployment_targets/cloud_run/tests/load_test/load_test.py +130 -3
- src/deployment_targets/cloud_run/{{cookiecutter.agent_directory}}/server.py +178 -146
- src/frontends/{live_api_react → adk_live_react}/frontend/package-lock.json +39 -1007
- src/frontends/{live_api_react → adk_live_react}/frontend/package.json +1 -9
- src/frontends/{live_api_react → adk_live_react}/frontend/src/App.tsx +1 -1
- src/frontends/{live_api_react → adk_live_react}/frontend/src/components/logger/Logger.tsx +8 -3
- src/frontends/{live_api_react → adk_live_react}/frontend/src/components/logger/logger.scss +26 -0
- src/frontends/{live_api_react → adk_live_react}/frontend/src/components/side-panel/SidePanel.tsx +11 -5
- src/frontends/{live_api_react → adk_live_react}/frontend/src/components/side-panel/side-panel.scss +146 -115
- src/frontends/adk_live_react/frontend/src/components/transcription-preview/TranscriptionPreview.tsx +106 -0
- src/frontends/adk_live_react/frontend/src/components/transcription-preview/transcription-preview.scss +150 -0
- src/frontends/{live_api_react → adk_live_react}/frontend/src/hooks/use-live-api.ts +8 -2
- src/frontends/{live_api_react → adk_live_react}/frontend/src/multimodal-live-types.ts +38 -2
- src/frontends/{live_api_react → adk_live_react}/frontend/src/utils/audio-recorder.ts +1 -1
- src/frontends/{live_api_react → adk_live_react}/frontend/src/utils/audio-streamer.ts +1 -1
- src/frontends/{live_api_react → adk_live_react}/frontend/src/utils/multimodal-live-client.ts +204 -23
- src/frontends/{live_api_react → adk_live_react}/frontend/src/utils/utils.ts +27 -5
- src/frontends/streamlit/frontend/utils/local_chat_history.py +2 -0
- src/resources/idx/.idx/dev.nix +25 -11
- src/resources/idx/idx-template.json +1 -16
- src/resources/idx/idx-template.nix +2 -3
- src/resources/locks/uv-adk_base-agent_engine.lock +434 -349
- src/resources/locks/uv-adk_base-cloud_run.lock +502 -409
- src/resources/locks/uv-adk_live-agent_engine.lock +4189 -0
- src/resources/locks/{uv-live_api-cloud_run.lock → uv-adk_live-cloud_run.lock} +884 -2219
- src/resources/locks/uv-agentic_rag-agent_engine.lock +473 -388
- src/resources/locks/uv-agentic_rag-cloud_run.lock +557 -464
- src/resources/locks/uv-crewai_coding_crew-agent_engine.lock +498 -515
- src/resources/locks/uv-crewai_coding_crew-cloud_run.lock +898 -687
- src/resources/locks/uv-langgraph_base_react-agent_engine.lock +455 -483
- src/resources/locks/uv-langgraph_base_react-cloud_run.lock +910 -645
- src/utils/generate_locks.py +8 -4
- agents/live_api/README.md +0 -37
- agents/live_api/app/agent.py +0 -72
- agents/live_api/tests/integration/test_server_e2e.py +0 -260
- agents/live_api/tests/load_test/load_test.py +0 -40
- agents/live_api/tests/unit/test_server.py +0 -144
- {agent_starter_pack-0.15.7.dist-info → agent_starter_pack-0.16.0.dist-info}/WHEEL +0 -0
- {agent_starter_pack-0.15.7.dist-info → agent_starter_pack-0.16.0.dist-info}/entry_points.txt +0 -0
- {agent_starter_pack-0.15.7.dist-info → agent_starter_pack-0.16.0.dist-info}/licenses/LICENSE +0 -0
- /src/frontends/{live_api_react → adk_live_react}/frontend/public/favicon.ico +0 -0
- /src/frontends/{live_api_react → adk_live_react}/frontend/public/index.html +0 -0
- /src/frontends/{live_api_react → adk_live_react}/frontend/public/robots.txt +0 -0
- /src/frontends/{live_api_react → adk_live_react}/frontend/src/App.scss +0 -0
- /src/frontends/{live_api_react → adk_live_react}/frontend/src/App.test.tsx +0 -0
- /src/frontends/{live_api_react → adk_live_react}/frontend/src/components/audio-pulse/AudioPulse.tsx +0 -0
- /src/frontends/{live_api_react → adk_live_react}/frontend/src/components/audio-pulse/audio-pulse.scss +0 -0
- /src/frontends/{live_api_react → adk_live_react}/frontend/src/components/logger/mock-logs.ts +0 -0
- /src/frontends/{live_api_react → adk_live_react}/frontend/src/contexts/LiveAPIContext.tsx +0 -0
- /src/frontends/{live_api_react → adk_live_react}/frontend/src/hooks/use-media-stream-mux.ts +0 -0
- /src/frontends/{live_api_react → adk_live_react}/frontend/src/hooks/use-screen-capture.ts +0 -0
- /src/frontends/{live_api_react → adk_live_react}/frontend/src/hooks/use-webcam.ts +0 -0
- /src/frontends/{live_api_react → adk_live_react}/frontend/src/index.css +0 -0
- /src/frontends/{live_api_react → adk_live_react}/frontend/src/index.tsx +0 -0
- /src/frontends/{live_api_react → adk_live_react}/frontend/src/react-app-env.d.ts +0 -0
- /src/frontends/{live_api_react → adk_live_react}/frontend/src/reportWebVitals.ts +0 -0
- /src/frontends/{live_api_react → adk_live_react}/frontend/src/setupTests.ts +0 -0
- /src/frontends/{live_api_react → adk_live_react}/frontend/src/utils/audioworklet-registry.ts +0 -0
- /src/frontends/{live_api_react → adk_live_react}/frontend/src/utils/store-logger.ts +0 -0
- /src/frontends/{live_api_react → adk_live_react}/frontend/src/utils/worklets/audio-processing.ts +0 -0
- /src/frontends/{live_api_react → adk_live_react}/frontend/src/utils/worklets/vol-meter.ts +0 -0
- /src/frontends/{live_api_react → adk_live_react}/frontend/tsconfig.json +0 -0
|
@@ -13,61 +13,22 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
# mypy: disable-error-code="union-attr"
|
|
16
|
-
from langchain_core.messages import BaseMessage
|
|
17
|
-
from langchain_core.runnables import RunnableConfig
|
|
18
|
-
from langchain_core.tools import tool
|
|
19
16
|
from langchain_google_vertexai import ChatVertexAI
|
|
20
|
-
from langgraph.
|
|
21
|
-
from langgraph.prebuilt import ToolNode
|
|
17
|
+
from langgraph.prebuilt import create_react_agent
|
|
22
18
|
|
|
23
19
|
LOCATION = "global"
|
|
24
20
|
LLM = "gemini-2.5-flash"
|
|
25
21
|
|
|
22
|
+
llm = ChatVertexAI(model=LLM, location=LOCATION, temperature=0)
|
|
26
23
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def search(query: str) -> str:
|
|
24
|
+
|
|
25
|
+
def get_weather(query: str) -> str:
|
|
30
26
|
"""Simulates a web search. Use it get information on weather"""
|
|
31
27
|
if "sf" in query.lower() or "san francisco" in query.lower():
|
|
32
28
|
return "It's 60 degrees and foggy."
|
|
33
29
|
return "It's 90 degrees and sunny."
|
|
34
30
|
|
|
35
31
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
llm = ChatVertexAI(
|
|
40
|
-
model=LLM, location=LOCATION, temperature=0, max_tokens=1024, streaming=True
|
|
41
|
-
).bind_tools(tools)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
# 3. Define workflow components
|
|
45
|
-
def should_continue(state: MessagesState) -> str:
|
|
46
|
-
"""Determines whether to use tools or end the conversation."""
|
|
47
|
-
last_message = state["messages"][-1]
|
|
48
|
-
return "tools" if last_message.tool_calls else END
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def call_model(state: MessagesState, config: RunnableConfig) -> dict[str, BaseMessage]:
|
|
52
|
-
"""Calls the language model and returns the response."""
|
|
53
|
-
system_message = "You are a helpful AI assistant."
|
|
54
|
-
messages_with_system = [{"type": "system", "content": system_message}] + state[
|
|
55
|
-
"messages"
|
|
56
|
-
]
|
|
57
|
-
# Forward the RunnableConfig object to ensure the agent is capable of streaming the response.
|
|
58
|
-
response = llm.invoke(messages_with_system, config)
|
|
59
|
-
return {"messages": response}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
# 4. Create the workflow graph
|
|
63
|
-
workflow = StateGraph(MessagesState)
|
|
64
|
-
workflow.add_node("agent", call_model)
|
|
65
|
-
workflow.add_node("tools", ToolNode(tools))
|
|
66
|
-
workflow.set_entry_point("agent")
|
|
67
|
-
|
|
68
|
-
# 5. Define graph edges
|
|
69
|
-
workflow.add_conditional_edges("agent", should_continue)
|
|
70
|
-
workflow.add_edge("tools", "agent")
|
|
71
|
-
|
|
72
|
-
# 6. Compile the workflow
|
|
73
|
-
agent = workflow.compile()
|
|
32
|
+
agent = create_react_agent(
|
|
33
|
+
model=llm, tools=[get_weather], prompt="You are a helpful assistant"
|
|
34
|
+
)
|
llm.txt
CHANGED
|
@@ -138,7 +138,7 @@ Templates for the `create` command (via `-a` or `--agent`):
|
|
|
138
138
|
| `agentic_rag` | RAG agent for document retrieval & Q&A |
|
|
139
139
|
| `langgraph_base_react` | Base ReAct agent (LangGraph) |
|
|
140
140
|
| `crewai_coding_crew` | Multi-agent collaborative coding assistance |
|
|
141
|
-
| `
|
|
141
|
+
| `adk_live` | Real-time multimodal RAG agent |
|
|
142
142
|
|
|
143
143
|
---
|
|
144
144
|
|
src/base_template/GEMINI.md
CHANGED
src/base_template/Makefile
CHANGED
|
@@ -1,99 +1,128 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# Installation & Setup
|
|
3
|
+
# ==============================================================================
|
|
4
|
+
|
|
1
5
|
# Install dependencies using uv package manager
|
|
2
6
|
install:
|
|
3
|
-
@command -v uv >/dev/null 2>&1 || { echo "uv is not installed. Installing uv..."; curl -LsSf https://astral.sh/uv/0.
|
|
4
|
-
{%- if cookiecutter.settings.get("commands", {}).get("override", {}).get("install") %}
|
|
7
|
+
@command -v uv >/dev/null 2>&1 || { echo "uv is not installed. Installing uv..."; curl -LsSf https://astral.sh/uv/0.8.13/install.sh | sh; source $HOME/.local/bin/env; }
|
|
8
|
+
{%- if cookiecutter.settings.get("commands", {}).get("override", {}).get("install") %}
|
|
5
9
|
{{cookiecutter.settings.get("commands", {}).get("override", {}).get("install")}}
|
|
6
10
|
{%- else %}
|
|
7
|
-
uv sync --dev{% if cookiecutter.
|
|
8
|
-
{%- endif %}
|
|
9
|
-
|
|
10
|
-
{%- if cookiecutter.settings.get("commands", {}).get("extra", {}) %}
|
|
11
|
-
{%- for cmd_name, cmd_value in cookiecutter.settings.get("commands", {}).get("extra", {}).items() %}
|
|
12
|
-
|
|
13
|
-
# {{ cmd_value.get("description") }}
|
|
14
|
-
{{ cmd_name }}:
|
|
15
|
-
{%- if cmd_value is mapping %}
|
|
16
|
-
{%- if cmd_value.command is mapping and cookiecutter.deployment_target in cmd_value.command %}
|
|
17
|
-
{{ cmd_value.command[cookiecutter.deployment_target] }}
|
|
18
|
-
{%- else %}
|
|
19
|
-
{{ cmd_value.command if cmd_value.command is string else "" }}
|
|
20
|
-
{%- endif %}
|
|
21
|
-
{%- else %}
|
|
22
|
-
{{ cmd_value }}
|
|
11
|
+
uv sync --dev{% if not cookiecutter.is_adk and not cookiecutter.is_adk_live %} --extra streamlit{%- endif %}{% if cookiecutter.is_adk_live %} && (cd frontend && npm install){%- endif %}
|
|
23
12
|
{%- endif %}
|
|
24
|
-
{%- endfor %}{%- endif %}
|
|
25
13
|
|
|
26
|
-
|
|
27
|
-
#
|
|
28
|
-
|
|
29
|
-
(cd frontend && npm run build)
|
|
14
|
+
# ==============================================================================
|
|
15
|
+
# Playground Targets
|
|
16
|
+
# ==============================================================================
|
|
30
17
|
|
|
31
|
-
{%- endif %}
|
|
32
18
|
# Launch local dev playground
|
|
33
|
-
playground:{%- if cookiecutter.
|
|
19
|
+
playground:{%- if cookiecutter.is_adk_live %} build-frontend-if-needed{%- endif %}
|
|
34
20
|
{%- if cookiecutter.settings.get("commands", {}).get("override", {}).get("playground") %}
|
|
21
|
+
{%- if cookiecutter.settings.get("commands", {}).get("override", {}).get("playground") is mapping %}
|
|
22
|
+
{{cookiecutter.settings.get("commands", {}).get("override", {}).get("playground").get(cookiecutter.deployment_target, "")}}
|
|
23
|
+
{%- else %}
|
|
35
24
|
{{cookiecutter.settings.get("commands", {}).get("override", {}).get("playground")}}
|
|
25
|
+
{%- endif %}
|
|
36
26
|
{%- else %}
|
|
37
27
|
@echo "==============================================================================="
|
|
38
28
|
@echo "| 🚀 Starting your agent playground... |"
|
|
39
29
|
@echo "| |"
|
|
40
|
-
{%- if cookiecutter.
|
|
30
|
+
{%- if cookiecutter.is_adk_live %}
|
|
41
31
|
@echo "| 🌐 Access your app at: http://localhost:8000 |"
|
|
42
32
|
{%- endif %}
|
|
43
33
|
@echo "| 💡 Try asking: {{cookiecutter.example_question}}|"
|
|
44
|
-
{%- if
|
|
34
|
+
{%- if cookiecutter.is_adk %}
|
|
45
35
|
@echo "| |"
|
|
46
36
|
@echo "| 🔍 IMPORTANT: Select the '{{cookiecutter.agent_directory}}' folder to interact with your agent. |"
|
|
47
37
|
{%- endif %}
|
|
48
38
|
@echo "==============================================================================="
|
|
49
|
-
{%- if
|
|
39
|
+
{%- if cookiecutter.is_adk_live %}
|
|
40
|
+
{%- if cookiecutter.deployment_target == 'agent_engine' %}
|
|
41
|
+
uv run python -m {{cookiecutter.agent_directory}}.utils.expose_app --mode local
|
|
42
|
+
{%- else %}
|
|
43
|
+
uv run uvicorn {{cookiecutter.agent_directory}}.server:app --host localhost --port 8000 --reload
|
|
44
|
+
{%- endif %}
|
|
45
|
+
{%- elif cookiecutter.is_adk %}
|
|
50
46
|
uv run adk web . --port 8501 --reload_agents
|
|
51
47
|
{%- else %}
|
|
52
48
|
{%- if cookiecutter.deployment_target == 'cloud_run' %}
|
|
53
|
-
|
|
54
|
-
uv run uvicorn {{cookiecutter.agent_directory}}.server:app --host 0.0.0.0 --port 8000 --reload
|
|
55
|
-
{%- else %}
|
|
56
|
-
uv run uvicorn {{cookiecutter.agent_directory}}.server:app --host 0.0.0.0 --port 8000 --reload &
|
|
57
|
-
{%- endif %}
|
|
49
|
+
uv run uvicorn {{cookiecutter.agent_directory}}.server:app --host localhost --port 8000 --reload &
|
|
58
50
|
{%- endif %}
|
|
59
|
-
{%- if cookiecutter.agent_name != 'live_api' %}
|
|
60
51
|
{% if cookiecutter.deployment_target == 'agent_engine' %}PYTHONPATH=. {% endif %}uv run streamlit run frontend/streamlit_app.py --browser.serverAddress=localhost --server.enableCORS=false --server.enableXsrfProtection=false
|
|
61
52
|
{%- endif %}
|
|
62
53
|
{%- endif %}
|
|
54
|
+
{%- if cookiecutter.settings.get("commands", {}).get("extra", {}) %}
|
|
55
|
+
|
|
56
|
+
# ==============================================================================
|
|
57
|
+
# Agent-Specific Commands
|
|
58
|
+
# ==============================================================================
|
|
59
|
+
{%- for cmd_name, cmd_value in cookiecutter.settings.get("commands", {}).get("extra", {}).items() %}
|
|
60
|
+
{%- if cmd_value is mapping %}
|
|
61
|
+
{%- if cmd_value.command is mapping %}
|
|
62
|
+
{%- if cookiecutter.deployment_target in cmd_value.command %}
|
|
63
|
+
|
|
64
|
+
# {{ cmd_value.get("description") }}
|
|
65
|
+
{{ cmd_name }}:
|
|
66
|
+
{{ cmd_value.command[cookiecutter.deployment_target] }}
|
|
63
67
|
{%- endif %}
|
|
68
|
+
{%- else %}
|
|
64
69
|
|
|
65
|
-
#
|
|
66
|
-
{
|
|
67
|
-
|
|
70
|
+
# {{ cmd_value.get("description") }}
|
|
71
|
+
{{ cmd_name }}:
|
|
72
|
+
{{ cmd_value.command if cmd_value.command is string else "" }}
|
|
68
73
|
{%- endif %}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
--memory "4Gi" \
|
|
75
|
-
--project $$PROJECT_ID \
|
|
76
|
-
--region "us-central1" \
|
|
77
|
-
--no-allow-unauthenticated \
|
|
78
|
-
--no-cpu-throttling \
|
|
79
|
-
--labels "{% if "adk" in cookiecutter.tags %}created-by=adk{% if cookiecutter.agent_garden %},{% endif %}{% endif %}{% if cookiecutter.agent_garden %}deployed-with=agent-garden{% if cookiecutter.agent_sample_id %},vertex-agent-sample-id={{cookiecutter.agent_sample_id}},vertex-agent-sample-publisher={{cookiecutter.agent_sample_publisher}}{% endif %}{% endif %}" \
|
|
80
|
-
--set-env-vars \
|
|
81
|
-
"COMMIT_SHA=$(shell git rev-parse HEAD){%- if cookiecutter.data_ingestion %}{%- if cookiecutter.datastore_type == "vertex_ai_search" %},DATA_STORE_ID={{cookiecutter.project_name}}-datastore,DATA_STORE_REGION=us{%- elif cookiecutter.datastore_type == "vertex_ai_vector_search" %},VECTOR_SEARCH_INDEX={{cookiecutter.project_name}}-vector-search,VECTOR_SEARCH_INDEX_ENDPOINT={{cookiecutter.project_name}}-vector-search-endpoint,VECTOR_SEARCH_BUCKET=$$PROJECT_ID-{{cookiecutter.project_name}}-vs{%- endif %}{%- endif %}" \
|
|
82
|
-
$(if $(IAP),--iap) \
|
|
83
|
-
$(if $(PORT),--port=$(PORT))
|
|
84
|
-
{%- elif cookiecutter.deployment_target == 'agent_engine' %}
|
|
85
|
-
# Export dependencies to requirements file using uv export.
|
|
86
|
-
uv export --no-hashes --no-header --no-dev --no-emit-project --no-annotate > .requirements.txt 2>/dev/null || \
|
|
87
|
-
uv export --no-hashes --no-header --no-dev --no-emit-project > .requirements.txt && uv run {{cookiecutter.agent_directory}}/agent_engine_app.py
|
|
74
|
+
{%- else %}
|
|
75
|
+
|
|
76
|
+
# {{ cmd_value.get("description", "") }}
|
|
77
|
+
{{ cmd_name }}:
|
|
78
|
+
{{ cmd_value }}
|
|
88
79
|
{%- endif %}
|
|
80
|
+
{%- endfor %}
|
|
81
|
+
{%- endif %}
|
|
82
|
+
{%- if cookiecutter.is_adk_live %}
|
|
83
|
+
|
|
84
|
+
# ==============================================================================
|
|
85
|
+
# ADK Live Commands
|
|
86
|
+
# ==============================================================================
|
|
87
|
+
|
|
88
|
+
# Build the frontend for production
|
|
89
|
+
build-frontend:
|
|
90
|
+
(cd frontend && npm run build)
|
|
91
|
+
|
|
92
|
+
# Build the frontend only if needed (conditional build)
|
|
93
|
+
build-frontend-if-needed:
|
|
94
|
+
@if [ ! -d "frontend/build" ] || [ ! -f "frontend/build/index.html" ]; then \
|
|
95
|
+
echo "Frontend build directory not found or incomplete. Building..."; \
|
|
96
|
+
$(MAKE) build-frontend; \
|
|
97
|
+
elif [ "frontend/package.json" -nt "frontend/build/index.html" ] || \
|
|
98
|
+
find frontend/src -newer frontend/build/index.html 2>/dev/null | head -1 | grep -q .; then \
|
|
99
|
+
echo "Frontend source files are newer than build. Rebuilding..."; \
|
|
100
|
+
$(MAKE) build-frontend; \
|
|
101
|
+
else \
|
|
102
|
+
echo "Frontend build is up to date. Skipping build..."; \
|
|
103
|
+
fi
|
|
89
104
|
{%- if cookiecutter.deployment_target == 'cloud_run' %}
|
|
90
105
|
|
|
91
106
|
# Launch local development server with hot-reload
|
|
92
107
|
local-backend:
|
|
93
|
-
uv run uvicorn {{cookiecutter.agent_directory}}.server:app --host
|
|
108
|
+
uv run uvicorn {{cookiecutter.agent_directory}}.server:app --host localhost --port 8000 --reload
|
|
109
|
+
{%- endif %}
|
|
110
|
+
{%- if cookiecutter.deployment_target == 'agent_engine' %}
|
|
111
|
+
|
|
112
|
+
# Launch local development server with hot-reload
|
|
113
|
+
local-backend:
|
|
114
|
+
uv run python -m {{cookiecutter.agent_directory}}.utils.expose_app --mode local --port 8000
|
|
115
|
+
|
|
116
|
+
# Connect to remote deployed agent
|
|
117
|
+
playground-remote: build-frontend-if-needed
|
|
118
|
+
@echo "==============================================================================="
|
|
119
|
+
@echo "| 🚀 Connecting to REMOTE agent... |"
|
|
120
|
+
@echo "| |"
|
|
121
|
+
@echo "| 🌐 Access your app at: http://localhost:8000 |"
|
|
122
|
+
@echo "| ☁️ Connected to deployed agent engine |"
|
|
123
|
+
@echo "==============================================================================="
|
|
124
|
+
uv run python -m {{cookiecutter.agent_directory}}.utils.expose_app --mode remote
|
|
94
125
|
{%- endif %}
|
|
95
|
-
{%- if cookiecutter.deployment_target == 'cloud_run' %}
|
|
96
|
-
{%- if cookiecutter.agent_name == 'live_api' %}
|
|
97
126
|
|
|
98
127
|
# Start the frontend UI separately for development (requires backend running separately)
|
|
99
128
|
ui:
|
|
@@ -106,7 +135,7 @@ playground-dev:
|
|
|
106
135
|
@echo "| |"
|
|
107
136
|
@echo "| 🌐 Frontend: http://localhost:8501 |"
|
|
108
137
|
@echo "| 🌐 Backend: http://localhost:8000 |"
|
|
109
|
-
@echo "| 💡 Try asking:
|
|
138
|
+
@echo "| 💡 Try asking: {{cookiecutter.example_question}}|"
|
|
110
139
|
@echo "| 🔄 Both frontend and backend will auto-reload on changes |"
|
|
111
140
|
@echo "==============================================================================="
|
|
112
141
|
@echo "Starting backend server..."
|
|
@@ -114,15 +143,51 @@ playground-dev:
|
|
|
114
143
|
@echo "Starting frontend dev server..."
|
|
115
144
|
$(MAKE) ui
|
|
116
145
|
{%- endif %}
|
|
146
|
+
|
|
147
|
+
# ==============================================================================
|
|
148
|
+
# Backend Deployment Targets
|
|
149
|
+
# ==============================================================================
|
|
150
|
+
|
|
151
|
+
# Deploy the agent remotely
|
|
152
|
+
{%- if cookiecutter.deployment_target == 'cloud_run' %}
|
|
153
|
+
# Usage: make backend [IAP=true] [PORT=8080] - Set IAP=true to enable Identity-Aware Proxy, PORT to specify container port
|
|
154
|
+
{%- endif %}
|
|
155
|
+
backend:
|
|
156
|
+
{%- if cookiecutter.deployment_target == 'cloud_run' %}
|
|
157
|
+
PROJECT_ID=$$(gcloud config get-value project) && \
|
|
158
|
+
gcloud beta run deploy {{cookiecutter.project_name}} \
|
|
159
|
+
--source . \
|
|
160
|
+
--memory "4Gi" \
|
|
161
|
+
--project $$PROJECT_ID \
|
|
162
|
+
--region "us-central1" \
|
|
163
|
+
--no-allow-unauthenticated \
|
|
164
|
+
--no-cpu-throttling \
|
|
165
|
+
--labels "{% if cookiecutter.is_adk %}created-by=adk{% if cookiecutter.agent_garden %},{% endif %}{% endif %}{% if cookiecutter.agent_garden %}deployed-with=agent-garden{% if cookiecutter.agent_sample_id %},vertex-agent-sample-id={{cookiecutter.agent_sample_id}},vertex-agent-sample-publisher={{cookiecutter.agent_sample_publisher}}{% endif %}{% endif %}" \
|
|
166
|
+
--set-env-vars \
|
|
167
|
+
"COMMIT_SHA=$(shell git rev-parse HEAD){%- if cookiecutter.data_ingestion %}{%- if cookiecutter.datastore_type == "vertex_ai_search" %},DATA_STORE_ID={{cookiecutter.project_name}}-datastore,DATA_STORE_REGION=us{%- elif cookiecutter.datastore_type == "vertex_ai_vector_search" %},VECTOR_SEARCH_INDEX={{cookiecutter.project_name}}-vector-search,VECTOR_SEARCH_INDEX_ENDPOINT={{cookiecutter.project_name}}-vector-search-endpoint,VECTOR_SEARCH_BUCKET=$$PROJECT_ID-{{cookiecutter.project_name}}-vs{%- endif %}{%- endif %}" \
|
|
168
|
+
$(if $(IAP),--iap) \
|
|
169
|
+
$(if $(PORT),--port=$(PORT))
|
|
170
|
+
{%- elif cookiecutter.deployment_target == 'agent_engine' %}
|
|
171
|
+
# Export dependencies to requirements file using uv export.
|
|
172
|
+
uv export --no-hashes --no-header --no-dev --no-emit-project --no-annotate > .requirements.txt 2>/dev/null || \
|
|
173
|
+
uv export --no-hashes --no-header --no-dev --no-emit-project > .requirements.txt && uv run {{cookiecutter.agent_directory}}/agent_engine_app.py
|
|
117
174
|
{%- endif %}
|
|
118
175
|
|
|
176
|
+
|
|
177
|
+
# ==============================================================================
|
|
178
|
+
# Infrastructure Setup
|
|
179
|
+
# ==============================================================================
|
|
180
|
+
|
|
119
181
|
# Set up development environment resources using Terraform
|
|
120
182
|
setup-dev-env:
|
|
121
183
|
PROJECT_ID=$$(gcloud config get-value project) && \
|
|
122
184
|
(cd deployment/terraform/dev && terraform init && terraform apply --var-file vars/env.tfvars --var dev_project_id=$$PROJECT_ID --auto-approve)
|
|
123
|
-
|
|
124
185
|
{%- if cookiecutter.data_ingestion %}
|
|
125
186
|
|
|
187
|
+
# ==============================================================================
|
|
188
|
+
# Data Ingestion (RAG capabilities)
|
|
189
|
+
# ==============================================================================
|
|
190
|
+
|
|
126
191
|
# Run the data ingestion pipeline for RAG capabilities
|
|
127
192
|
data-ingestion:
|
|
128
193
|
PROJECT_ID=$$(gcloud config get-value project) && \
|
|
@@ -142,6 +207,10 @@ data-ingestion:
|
|
|
142
207
|
--pipeline-name="data-ingestion-pipeline")
|
|
143
208
|
{%- endif %}
|
|
144
209
|
|
|
210
|
+
# ==============================================================================
|
|
211
|
+
# Testing & Code Quality
|
|
212
|
+
# ==============================================================================
|
|
213
|
+
|
|
145
214
|
# Run unit and integration tests
|
|
146
215
|
test:
|
|
147
216
|
uv run pytest tests/unit && uv run pytest tests/integration
|
src/base_template/README.md
CHANGED
|
@@ -58,11 +58,11 @@ make install && make playground
|
|
|
58
58
|
{%- endfor %}
|
|
59
59
|
{%- endif %}
|
|
60
60
|
{%- if cookiecutter.deployment_target == 'cloud_run' %}
|
|
61
|
-
| `make playground` | Launch local development environment with backend and frontend{%- if
|
|
61
|
+
| `make playground` | Launch local development environment with backend and frontend{%- if cookiecutter.is_adk %} - leveraging `adk web` command. {%- endif %}|
|
|
62
62
|
| `make backend` | Deploy agent to Cloud Run (use `IAP=true` to enable Identity-Aware Proxy) |
|
|
63
63
|
| `make local-backend` | Launch local development server |
|
|
64
64
|
{%- if cookiecutter.deployment_target == 'cloud_run' %}
|
|
65
|
-
{%- if cookiecutter.
|
|
65
|
+
{%- if cookiecutter.is_adk_live %}
|
|
66
66
|
| `make ui` | Launch Agent Playground front-end only |
|
|
67
67
|
{%- endif %}
|
|
68
68
|
{%- endif %}
|
|
@@ -80,7 +80,7 @@ make install && make playground
|
|
|
80
80
|
|
|
81
81
|
For full command options and usage, refer to the [Makefile](Makefile).
|
|
82
82
|
|
|
83
|
-
{% if cookiecutter.
|
|
83
|
+
{% if cookiecutter.is_adk_live %}
|
|
84
84
|
## Usage
|
|
85
85
|
|
|
86
86
|
This template follows a "bring your own agent" approach - you focus on your business logic in `{{cookiecutter.agent_directory}}/agent.py`, and the template handles the surrounding components (UI, infrastructure, deployment, monitoring).
|
|
@@ -158,7 +158,7 @@ You can test deployment towards a Dev Environment using the following command:
|
|
|
158
158
|
gcloud config set project <your-dev-project-id>
|
|
159
159
|
make backend
|
|
160
160
|
```
|
|
161
|
-
{% if cookiecutter.
|
|
161
|
+
{% if cookiecutter.is_adk_live %}
|
|
162
162
|
**Note:** For secure access to your deployed backend, consider using Identity-Aware Proxy (IAP) by running `make backend IAP=true`.
|
|
163
163
|
{%- endif %}
|
|
164
164
|
|
|
@@ -169,9 +169,9 @@ See [deployment/README.md](deployment/README.md) for instructions.
|
|
|
169
169
|
|
|
170
170
|
The repository includes a Terraform configuration for the setup of a production Google Cloud project. Refer to [deployment/README.md](deployment/README.md) for detailed instructions on how to deploy the infrastructure and application.
|
|
171
171
|
|
|
172
|
-
{% if cookiecutter.
|
|
172
|
+
{% if not cookiecutter.is_adk_live %}
|
|
173
173
|
## Monitoring and Observability
|
|
174
|
-
> You can use [this Looker Studio dashboard]({%- if
|
|
174
|
+
> You can use [this Looker Studio dashboard]({%- if cookiecutter.is_adk %}https://lookerstudio.google.com/reporting/46b35167-b38b-4e44-bd37-701ef4307418/page/tEnnC{%- else %}https://lookerstudio.google.com/c/reporting/fa742264-4b4b-4c56-81e6-a667dd0f853f/page/tEnnC{%- endif %}
|
|
175
175
|
) template for visualizing events being logged in BigQuery. See the "Setup Instructions" tab to getting started.
|
|
176
176
|
|
|
177
177
|
The application uses OpenTelemetry for comprehensive observability with all events being sent to Google Cloud Trace and Logging for monitoring and to BigQuery for long term storage.
|
|
@@ -25,7 +25,7 @@ locals {
|
|
|
25
25
|
"serviceusage.googleapis.com",
|
|
26
26
|
"logging.googleapis.com",
|
|
27
27
|
"cloudtrace.googleapis.com",
|
|
28
|
-
{%- if
|
|
28
|
+
{%- if cookiecutter.is_adk and cookiecutter.session_type == "alloydb" %}
|
|
29
29
|
"compute.googleapis.com",
|
|
30
30
|
"servicenetworking.googleapis.com",
|
|
31
31
|
"alloydb.googleapis.com",
|
|
@@ -32,7 +32,7 @@ variable "region" {
|
|
|
32
32
|
variable "telemetry_logs_filter" {
|
|
33
33
|
type = string
|
|
34
34
|
description = "Log Sink filter for capturing telemetry data. Captures logs with the `traceloop.association.properties.log_type` attribute set to `tracing`."
|
|
35
|
-
{%- if
|
|
35
|
+
{%- if cookiecutter.is_adk %}
|
|
36
36
|
default = "labels.service_name=\"{{cookiecutter.project_name}}\" labels.type=\"agent_telemetry\""
|
|
37
37
|
{%- else %}
|
|
38
38
|
default = "jsonPayload.attributes.\"traceloop.association.properties.log_type\"=\"tracing\" jsonPayload.resource.attributes.\"service.name\"=\"{{cookiecutter.project_name}}\""
|
|
@@ -33,7 +33,7 @@ locals {
|
|
|
33
33
|
"serviceusage.googleapis.com",
|
|
34
34
|
"logging.googleapis.com",
|
|
35
35
|
"cloudtrace.googleapis.com",
|
|
36
|
-
{%- if
|
|
36
|
+
{%- if cookiecutter.is_adk and cookiecutter.session_type == "alloydb" %}
|
|
37
37
|
"compute.googleapis.com",
|
|
38
38
|
"servicenetworking.googleapis.com",
|
|
39
39
|
"alloydb.googleapis.com",
|
|
@@ -53,7 +53,7 @@ variable "repository_name" {
|
|
|
53
53
|
variable "telemetry_logs_filter" {
|
|
54
54
|
type = string
|
|
55
55
|
description = "Log Sink filter for capturing telemetry data. Captures logs with the `traceloop.association.properties.log_type` attribute set to `tracing`."
|
|
56
|
-
{%- if
|
|
56
|
+
{%- if cookiecutter.is_adk %}
|
|
57
57
|
default = "labels.service_name=\"{{cookiecutter.project_name}}\" labels.type=\"agent_telemetry\""
|
|
58
58
|
{%- else %}
|
|
59
59
|
default = "jsonPayload.attributes.\"traceloop.association.properties.log_type\"=\"tracing\" jsonPayload.resource.attributes.\"service.name\"=\"{{cookiecutter.project_name}}\""
|
src/base_template/pyproject.toml
CHANGED
|
@@ -9,19 +9,20 @@ dependencies = [
|
|
|
9
9
|
{%- for dep in cookiecutter.extra_dependencies %}
|
|
10
10
|
"{{ dep }}",
|
|
11
11
|
{%- endfor %}
|
|
12
|
-
"opentelemetry-exporter-gcp-trace
|
|
13
|
-
{%- if
|
|
12
|
+
"opentelemetry-exporter-gcp-trace>=1.9.0,<2.0.0",
|
|
13
|
+
{%- if not cookiecutter.is_adk %}
|
|
14
14
|
"langchain-core~=0.3.9",
|
|
15
15
|
"traceloop-sdk~=0.38.7",
|
|
16
16
|
{%- endif %}
|
|
17
|
-
"google-cloud-logging>=3.12.0",
|
|
17
|
+
"google-cloud-logging>=3.12.0,<4.0.0",
|
|
18
18
|
{%- if cookiecutter.deployment_target == 'cloud_run' %}
|
|
19
|
-
"google-cloud-aiplatform[evaluation]
|
|
19
|
+
"google-cloud-aiplatform[evaluation]>=1.118.0,<2.0.0",
|
|
20
20
|
"fastapi~=0.115.8",
|
|
21
21
|
"uvicorn~=0.34.0",
|
|
22
|
-
"psycopg2-binary>=2.9.10",
|
|
22
|
+
"psycopg2-binary>=2.9.10,<3.0.0",
|
|
23
23
|
{%- elif cookiecutter.deployment_target == 'agent_engine' %}
|
|
24
|
-
"google-cloud-aiplatform[evaluation,agent-engines]
|
|
24
|
+
"google-cloud-aiplatform[evaluation,agent-engines]>=1.118.0,<2.0.0",
|
|
25
|
+
"protobuf>=6.31.1,<7.0.0"
|
|
25
26
|
{%- endif %}
|
|
26
27
|
]
|
|
27
28
|
{% if cookiecutter.deployment_target == 'cloud_run' %}
|
|
@@ -32,29 +33,29 @@ requires-python = ">=3.10,<3.13"
|
|
|
32
33
|
|
|
33
34
|
[dependency-groups]
|
|
34
35
|
dev = [
|
|
35
|
-
"pytest>=8.3.4",
|
|
36
|
-
"pytest-asyncio>=0.23.8",
|
|
37
|
-
"nest-asyncio>=1.6.0",
|
|
36
|
+
"pytest>=8.3.4,<9.0.0",
|
|
37
|
+
"pytest-asyncio>=0.23.8,<1.0.0",
|
|
38
|
+
"nest-asyncio>=1.6.0,<2.0.0",
|
|
38
39
|
]
|
|
39
40
|
|
|
40
41
|
[project.optional-dependencies]
|
|
41
|
-
{% if
|
|
42
|
+
{% if not cookiecutter.is_adk %}
|
|
42
43
|
streamlit = [
|
|
43
|
-
"streamlit
|
|
44
|
-
"streamlit-extras
|
|
45
|
-
"extra-streamlit-components
|
|
46
|
-
"streamlit-feedback
|
|
44
|
+
"streamlit>=1.42.0,<2.0.0",
|
|
45
|
+
"streamlit-extras>=0.4.3,<1.0.0",
|
|
46
|
+
"extra-streamlit-components>=0.1.71,<1.0.0",
|
|
47
|
+
"streamlit-feedback>=0.1.3,<1.0.0",
|
|
47
48
|
]
|
|
48
49
|
{% endif %}
|
|
49
50
|
jupyter = [
|
|
50
|
-
"jupyter
|
|
51
|
+
"jupyter>=1.0.0,<2.0.0",
|
|
51
52
|
]
|
|
52
53
|
lint = [
|
|
53
|
-
"ruff>=0.4.6",
|
|
54
|
-
"mypy
|
|
55
|
-
"codespell
|
|
56
|
-
"types-pyyaml
|
|
57
|
-
"types-requests
|
|
54
|
+
"ruff>=0.4.6,<1.0.0",
|
|
55
|
+
"mypy>=1.15.0,<2.0.0",
|
|
56
|
+
"codespell>=2.2.0,<3.0.0",
|
|
57
|
+
"types-pyyaml>=6.0.12.20240917,<7.0.0",
|
|
58
|
+
"types-requests>=2.32.0.20240914,<3.0.0",
|
|
58
59
|
]
|
|
59
60
|
|
|
60
61
|
[tool.ruff]
|
|
@@ -92,7 +93,7 @@ follow_imports = "silent"
|
|
|
92
93
|
ignore_missing_imports = true
|
|
93
94
|
explicit_package_bases = true
|
|
94
95
|
disable_error_code = ["misc", "no-untyped-call", "no-any-return"]
|
|
95
|
-
{% if cookiecutter.
|
|
96
|
+
{% if cookiecutter.is_adk_live %}
|
|
96
97
|
exclude = [".venv","./frontend"]
|
|
97
98
|
{% else %}
|
|
98
99
|
exclude = [".venv"]
|
|
@@ -67,14 +67,14 @@ jobs:
|
|
|
67
67
|
{%- if cookiecutter.deployment_target == 'agent_engine' %}
|
|
68
68
|
- name: Install uv and dependencies
|
|
69
69
|
run: |
|
|
70
|
-
pip install uv==0.
|
|
70
|
+
pip install uv==0.8.13
|
|
71
71
|
uv sync --locked
|
|
72
72
|
{%- endif %}
|
|
73
73
|
|
|
74
74
|
{%- if cookiecutter.data_ingestion %}
|
|
75
75
|
- name: Deploy data ingestion pipeline (Production)
|
|
76
76
|
run: |
|
|
77
|
-
cd data_ingestion && pip install uv==0.
|
|
77
|
+
cd data_ingestion && pip install uv==0.8.13 && cd data_ingestion_pipeline && \
|
|
78
78
|
uv sync --locked && uv run python submit_pipeline.py
|
|
79
79
|
env:
|
|
80
80
|
PIPELINE_ROOT: {% raw %}${{ vars.PIPELINE_GCS_ROOT_PROD }}{% endraw %}
|
|
@@ -58,7 +58,7 @@ jobs:
|
|
|
58
58
|
|
|
59
59
|
- name: Deploy data ingestion pipeline (Staging)
|
|
60
60
|
run: |
|
|
61
|
-
cd data_ingestion && pip install uv==0.
|
|
61
|
+
cd data_ingestion && pip install uv==0.8.13 && cd data_ingestion_pipeline && \
|
|
62
62
|
uv sync --locked && uv run python submit_pipeline.py
|
|
63
63
|
env:
|
|
64
64
|
PIPELINE_ROOT: {% raw %}${{ vars.PIPELINE_GCS_ROOT_STAGING }}{% endraw %}
|
|
@@ -113,7 +113,7 @@ jobs:
|
|
|
113
113
|
|
|
114
114
|
- name: Install uv and dependencies
|
|
115
115
|
run: |
|
|
116
|
-
pip install uv==0.
|
|
116
|
+
pip install uv==0.8.13
|
|
117
117
|
uv sync --locked
|
|
118
118
|
|
|
119
119
|
- name: Deploy to Staging (Agent Engine)
|
|
@@ -135,23 +135,86 @@ jobs:
|
|
|
135
135
|
|
|
136
136
|
- name: Run load test
|
|
137
137
|
run: |
|
|
138
|
-
{%- if cookiecutter.deployment_target == 'cloud_run' %}
|
|
138
|
+
{%- if cookiecutter.deployment_target == 'cloud_run' and cookiecutter.agent_name == 'adk_live' %}
|
|
139
|
+
# Install dependencies for load test
|
|
140
|
+
pip install locust==2.31.1 websockets
|
|
141
|
+
|
|
142
|
+
# Install Cloud Run proxy component
|
|
143
|
+
gcloud components install cloud-run-proxy --quiet
|
|
144
|
+
|
|
145
|
+
# Start Cloud Run proxy in background
|
|
146
|
+
gcloud run services proxy {{cookiecutter.project_name}} \
|
|
147
|
+
--port=8080 \
|
|
148
|
+
--region {% raw %}${{ vars.REGION }}{% endraw %} \
|
|
149
|
+
--project {% raw %}${{ vars.STAGING_PROJECT_ID }}{% endraw %} \
|
|
150
|
+
--quiet &
|
|
151
|
+
PROXY_PID=$!
|
|
152
|
+
|
|
153
|
+
# Wait for proxy to be ready
|
|
154
|
+
echo "Waiting for proxy to start..."
|
|
155
|
+
sleep 10
|
|
156
|
+
|
|
157
|
+
# Run load test and capture exit code
|
|
158
|
+
locust -f tests/load_test/load_test.py \
|
|
159
|
+
--headless \
|
|
160
|
+
-H http://127.0.0.1:8080 \
|
|
161
|
+
-t 30s -u 2 -r 2 \
|
|
162
|
+
--csv=tests/load_test/.results/results \
|
|
163
|
+
--html=tests/load_test/.results/report.html
|
|
164
|
+
LOCUST_EXIT_CODE=$?
|
|
165
|
+
|
|
166
|
+
# Clean up proxy
|
|
167
|
+
kill $PROXY_PID || true
|
|
168
|
+
|
|
169
|
+
# Exit with load test result to fail build if tests failed
|
|
170
|
+
if [ $LOCUST_EXIT_CODE -ne 0 ]; then
|
|
171
|
+
echo "Load test failed with exit code $LOCUST_EXIT_CODE"
|
|
172
|
+
exit $LOCUST_EXIT_CODE
|
|
173
|
+
fi
|
|
174
|
+
{%- elif cookiecutter.deployment_target == 'cloud_run' %}
|
|
139
175
|
export _ID_TOKEN="{% raw %}${{ steps.fetch-token.outputs._id_token }}{% endraw %}"
|
|
140
176
|
export _STAGING_URL="{% raw %}${{ steps.fetch-url.outputs._staging_url }}{% endraw %}"
|
|
141
|
-
{%- elif cookiecutter.deployment_target == 'agent_engine' %}
|
|
142
|
-
export _AUTH_TOKEN="{% raw %}${{ steps.fetch-token.outputs._auth_token }}{% endraw %}"
|
|
143
|
-
{%- endif %}
|
|
144
177
|
pip install locust==2.31.1
|
|
145
178
|
locust -f tests/load_test/load_test.py \
|
|
146
179
|
--headless \
|
|
147
|
-
{%- if cookiecutter.deployment_target == 'cloud_run' %}
|
|
148
180
|
-H ${_STAGING_URL} \
|
|
149
181
|
-t 30s -u 10 -r 0.5 \
|
|
182
|
+
--csv=tests/load_test/.results/results \
|
|
183
|
+
--html=tests/load_test/.results/report.html
|
|
184
|
+
{%- elif cookiecutter.deployment_target == 'agent_engine' and cookiecutter.is_adk_live %}
|
|
185
|
+
# Start expose app in remote mode (uses deployment_metadata.json by default)
|
|
186
|
+
uv run python -m {{cookiecutter.agent_directory}}.utils.expose_app --mode remote &
|
|
187
|
+
EXPOSE_PID=$!
|
|
188
|
+
|
|
189
|
+
# Wait for expose app to be ready
|
|
190
|
+
sleep 10
|
|
191
|
+
|
|
192
|
+
# Run load test against local expose app
|
|
193
|
+
uv run --with locust==2.31.1 --with websockets locust -f tests/load_test/load_test.py \
|
|
194
|
+
-H http://127.0.0.1:8000 \
|
|
195
|
+
--headless \
|
|
196
|
+
-t 30s -u 2 -r 1 \
|
|
197
|
+
--csv=tests/load_test/.results/results \
|
|
198
|
+
--html=tests/load_test/.results/report.html
|
|
199
|
+
LOCUST_EXIT_CODE=$?
|
|
200
|
+
|
|
201
|
+
# Stop expose app
|
|
202
|
+
kill $EXPOSE_PID
|
|
203
|
+
|
|
204
|
+
# Exit with error if locust test failed
|
|
205
|
+
if [ $LOCUST_EXIT_CODE -ne 0 ]; then
|
|
206
|
+
echo "Load test failed with exit code $LOCUST_EXIT_CODE"
|
|
207
|
+
exit $LOCUST_EXIT_CODE
|
|
208
|
+
fi
|
|
150
209
|
{%- elif cookiecutter.deployment_target == 'agent_engine' %}
|
|
210
|
+
export _AUTH_TOKEN="{% raw %}${{ steps.fetch-token.outputs._auth_token }}{% endraw %}"
|
|
211
|
+
pip install locust==2.31.1
|
|
212
|
+
locust -f tests/load_test/load_test.py \
|
|
213
|
+
--headless \
|
|
151
214
|
-t 30s -u 2 -r 0.5 \
|
|
152
|
-
{%- endif %}
|
|
153
215
|
--csv=tests/load_test/.results/results \
|
|
154
216
|
--html=tests/load_test/.results/report.html
|
|
217
|
+
{%- endif %}
|
|
155
218
|
|
|
156
219
|
- name: Export Load Test Results to GCS
|
|
157
220
|
run: |
|