agent-starter-pack 0.9.2__py3-none-any.whl → 0.10.1__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-0.9.2.dist-info → agent_starter_pack-0.10.1.dist-info}/METADATA +2 -2
- {agent_starter_pack-0.9.2.dist-info → agent_starter_pack-0.10.1.dist-info}/RECORD +54 -52
- agents/adk_base/.template/templateconfig.yaml +1 -1
- agents/adk_gemini_fullstack/.template/templateconfig.yaml +2 -1
- agents/agentic_rag/.template/templateconfig.yaml +1 -1
- agents/agentic_rag/README.md +1 -1
- agents/live_api/tests/integration/test_server_e2e.py +7 -1
- agents/live_api/tests/unit/test_server.py +2 -1
- llm.txt +3 -2
- src/base_template/Makefile +4 -3
- src/base_template/README.md +7 -2
- src/base_template/deployment/README.md +6 -121
- src/base_template/deployment/terraform/github.tf +284 -0
- src/base_template/deployment/terraform/providers.tf +5 -0
- src/base_template/deployment/terraform/variables.tf +40 -1
- src/base_template/deployment/terraform/vars/env.tfvars +7 -1
- src/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 → {% if cookiecutter.cicd_runner == 'google_cloud_build' %}build_triggers.tf{% else %}unused_build_triggers.tf{% endif %} } +33 -18
- src/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/deploy-to-prod.yaml +114 -0
- src/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/pr_checks.yaml +65 -0
- src/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/staging.yaml +170 -0
- src/base_template/{deployment/cd/deploy-to-prod.yaml → {% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/deploy-to-prod.yaml } +7 -7
- src/base_template/{deployment/cd/staging.yaml → {% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/staging.yaml } +7 -7
- src/cli/commands/create.py +149 -4
- src/cli/commands/list.py +1 -1
- src/cli/commands/setup_cicd.py +293 -299
- src/cli/utils/cicd.py +19 -7
- src/cli/utils/remote_template.py +4 -4
- src/cli/utils/template.py +67 -19
- src/deployment_targets/cloud_run/app/server.py +19 -0
- src/deployment_targets/cloud_run/deployment/terraform/dev/service.tf +4 -3
- src/deployment_targets/cloud_run/deployment/terraform/service.tf +4 -3
- src/deployment_targets/cloud_run/tests/integration/test_server_e2e.py +35 -0
- src/deployment_targets/cloud_run/tests/load_test/README.md +1 -1
- src/frontends/live_api_react/frontend/package-lock.json +19 -16
- src/frontends/streamlit/frontend/side_bar.py +1 -1
- src/frontends/streamlit/frontend/utils/chat_utils.py +1 -1
- src/frontends/streamlit/frontend/utils/local_chat_history.py +2 -2
- src/resources/docs/adk-cheatsheet.md +1 -1
- src/resources/locks/uv-adk_base-agent_engine.lock +164 -131
- src/resources/locks/uv-adk_base-cloud_run.lock +177 -144
- src/resources/locks/uv-adk_gemini_fullstack-agent_engine.lock +164 -131
- src/resources/locks/uv-adk_gemini_fullstack-cloud_run.lock +177 -144
- src/resources/locks/uv-agentic_rag-agent_engine.lock +223 -190
- src/resources/locks/uv-agentic_rag-cloud_run.lock +251 -218
- src/resources/locks/uv-crewai_coding_crew-agent_engine.lock +315 -485
- src/resources/locks/uv-crewai_coding_crew-cloud_run.lock +358 -531
- src/resources/locks/uv-langgraph_base_react-agent_engine.lock +281 -249
- src/resources/locks/uv-langgraph_base_react-cloud_run.lock +323 -290
- src/resources/locks/uv-live_api-cloud_run.lock +350 -327
- src/resources/setup_cicd/cicd_variables.tf +0 -41
- src/resources/setup_cicd/github.tf +0 -87
- src/resources/setup_cicd/providers.tf +0 -39
- {agent_starter_pack-0.9.2.dist-info → agent_starter_pack-0.10.1.dist-info}/WHEEL +0 -0
- {agent_starter_pack-0.9.2.dist-info → agent_starter_pack-0.10.1.dist-info}/entry_points.txt +0 -0
- {agent_starter_pack-0.9.2.dist-info → agent_starter_pack-0.10.1.dist-info}/licenses/LICENSE +0 -0
- /src/base_template/{deployment/ci → {% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}}/pr_checks.yaml +0 -0
src/cli/utils/cicd.py
CHANGED
@@ -125,6 +125,7 @@ def create_github_connection(
|
|
125
125
|
stdout=subprocess.PIPE,
|
126
126
|
stderr=subprocess.PIPE,
|
127
127
|
text=True,
|
128
|
+
encoding="utf-8",
|
128
129
|
)
|
129
130
|
|
130
131
|
# Send 'y' followed by enter key to handle both the API enablement prompt and any other prompts
|
@@ -263,12 +264,12 @@ class ProjectConfig:
|
|
263
264
|
cicd_project_id: str
|
264
265
|
agent: str
|
265
266
|
deployment_target: str
|
267
|
+
repository_name: str
|
268
|
+
repository_owner: str
|
266
269
|
region: str = "us-central1"
|
267
270
|
dev_project_id: str | None = None
|
268
271
|
project_name: str | None = None
|
269
|
-
|
270
|
-
repository_owner: str | None = None
|
271
|
-
repository_exists: bool | None = None
|
272
|
+
create_repository: bool | None = None
|
272
273
|
host_connection_name: str | None = None
|
273
274
|
github_pat: str | None = None
|
274
275
|
github_app_installation_id: str | None = None
|
@@ -405,6 +406,7 @@ def run_command(
|
|
405
406
|
capture_output: bool = False,
|
406
407
|
shell: bool = False,
|
407
408
|
input: str | None = None,
|
409
|
+
env_vars: dict[str, str] | None = None,
|
408
410
|
) -> subprocess.CompletedProcess:
|
409
411
|
"""Run a command and display it to the user"""
|
410
412
|
# Format command for display
|
@@ -413,6 +415,14 @@ def run_command(
|
|
413
415
|
if cwd:
|
414
416
|
print(f"📂 In directory: {cwd}")
|
415
417
|
|
418
|
+
# Prepare environment variables
|
419
|
+
env = None
|
420
|
+
if env_vars:
|
421
|
+
import os
|
422
|
+
|
423
|
+
env = os.environ.copy()
|
424
|
+
env.update(env_vars)
|
425
|
+
|
416
426
|
# Run the command
|
417
427
|
result = subprocess.run(
|
418
428
|
cmd,
|
@@ -422,6 +432,7 @@ def run_command(
|
|
422
432
|
text=True,
|
423
433
|
shell=shell,
|
424
434
|
input=input,
|
435
|
+
env=env,
|
425
436
|
)
|
426
437
|
|
427
438
|
# Display output if captured
|
@@ -475,6 +486,7 @@ def handle_github_authentication() -> None:
|
|
475
486
|
stdout=subprocess.PIPE,
|
476
487
|
stderr=subprocess.PIPE,
|
477
488
|
text=True,
|
489
|
+
encoding="utf-8",
|
478
490
|
)
|
479
491
|
stdout, stderr = process.communicate(input=token + "\n")
|
480
492
|
|
@@ -565,7 +577,7 @@ class E2EDeployment:
|
|
565
577
|
project_dir / "deployment" / "terraform" / "dev" / "vars" / "env.tfvars"
|
566
578
|
)
|
567
579
|
|
568
|
-
with open(tf_vars_path) as f:
|
580
|
+
with open(tf_vars_path, encoding="utf-8") as f:
|
569
581
|
content = f.read()
|
570
582
|
|
571
583
|
# Replace dev project ID
|
@@ -580,7 +592,7 @@ class E2EDeployment:
|
|
580
592
|
project_dir / "deployment" / "terraform" / "vars" / "env.tfvars"
|
581
593
|
)
|
582
594
|
|
583
|
-
with open(tf_vars_path) as f:
|
595
|
+
with open(tf_vars_path, encoding="utf-8") as f:
|
584
596
|
content = f.read()
|
585
597
|
|
586
598
|
# Replace all project IDs
|
@@ -613,7 +625,7 @@ class E2EDeployment:
|
|
613
625
|
)
|
614
626
|
|
615
627
|
# Write updated content
|
616
|
-
with open(tf_vars_path, "w") as f:
|
628
|
+
with open(tf_vars_path, "w", encoding="utf-8") as f:
|
617
629
|
f.write(content)
|
618
630
|
|
619
631
|
def setup_terraform_state(self, project_dir: Path, env: Environment) -> None:
|
@@ -667,7 +679,7 @@ class E2EDeployment:
|
|
667
679
|
state_prefix = "dev" if is_dev_dir else "prod"
|
668
680
|
|
669
681
|
backend_file = tf_dir / "backend.tf"
|
670
|
-
with open(backend_file, "w") as f:
|
682
|
+
with open(backend_file, "w", encoding="utf-8") as f:
|
671
683
|
f.write(f'''terraform {{
|
672
684
|
backend "gcs" {{
|
673
685
|
bucket = "{bucket_name}"
|
src/cli/utils/remote_template.py
CHANGED
@@ -198,7 +198,7 @@ def load_remote_template_config(template_dir: pathlib.Path) -> dict[str, Any]:
|
|
198
198
|
return {}
|
199
199
|
|
200
200
|
try:
|
201
|
-
with open(config_path) as f:
|
201
|
+
with open(config_path, encoding="utf-8") as f:
|
202
202
|
config = yaml.safe_load(f)
|
203
203
|
return config if config else {}
|
204
204
|
except Exception as e:
|
@@ -265,7 +265,7 @@ def render_and_merge_makefiles(
|
|
265
265
|
# Render the base Makefile
|
266
266
|
base_makefile_path = base_template_path / "Makefile"
|
267
267
|
if base_makefile_path.exists():
|
268
|
-
with open(base_makefile_path) as f:
|
268
|
+
with open(base_makefile_path, encoding="utf-8") as f:
|
269
269
|
base_template = env.from_string(f.read())
|
270
270
|
rendered_base_makefile = base_template.render(cookiecutter=cookiecutter_config)
|
271
271
|
else:
|
@@ -276,7 +276,7 @@ def render_and_merge_makefiles(
|
|
276
276
|
if remote_template_path:
|
277
277
|
remote_makefile_path = remote_template_path / "Makefile"
|
278
278
|
if remote_makefile_path.exists():
|
279
|
-
with open(remote_makefile_path) as f:
|
279
|
+
with open(remote_makefile_path, encoding="utf-8") as f:
|
280
280
|
remote_template = env.from_string(f.read())
|
281
281
|
rendered_remote_makefile = remote_template.render(
|
282
282
|
cookiecutter=cookiecutter_config
|
@@ -316,6 +316,6 @@ def render_and_merge_makefiles(
|
|
316
316
|
final_makefile_content = rendered_base_makefile
|
317
317
|
|
318
318
|
# Write the final merged Makefile
|
319
|
-
with open(final_destination / "Makefile", "w") as f:
|
319
|
+
with open(final_destination / "Makefile", "w", encoding="utf-8") as f:
|
320
320
|
f.write(final_makefile_content)
|
321
321
|
logging.debug("Rendered and merged Makefile written to final destination.")
|
src/cli/utils/template.py
CHANGED
@@ -48,7 +48,7 @@ class TemplateConfig:
|
|
48
48
|
def from_file(cls, config_path: pathlib.Path) -> "TemplateConfig":
|
49
49
|
"""Load template config from file with validation"""
|
50
50
|
try:
|
51
|
-
with open(config_path) as f:
|
51
|
+
with open(config_path, encoding="utf-8") as f:
|
52
52
|
data = yaml.safe_load(f)
|
53
53
|
|
54
54
|
if not isinstance(data, dict):
|
@@ -101,7 +101,7 @@ def get_available_agents(deployment_target: str | None = None) -> dict:
|
|
101
101
|
template_config_path = agent_dir / ".template" / "templateconfig.yaml"
|
102
102
|
if template_config_path.exists():
|
103
103
|
try:
|
104
|
-
with open(template_config_path) as f:
|
104
|
+
with open(template_config_path, encoding="utf-8") as f:
|
105
105
|
config = yaml.safe_load(f)
|
106
106
|
agent_name = agent_dir.name
|
107
107
|
|
@@ -150,7 +150,7 @@ def load_template_config(template_dir: pathlib.Path) -> dict[str, Any]:
|
|
150
150
|
return {}
|
151
151
|
|
152
152
|
try:
|
153
|
-
with open(config_file) as f:
|
153
|
+
with open(config_file, encoding="utf-8") as f:
|
154
154
|
config = yaml.safe_load(f)
|
155
155
|
return config if config else {}
|
156
156
|
except Exception as e:
|
@@ -230,6 +230,10 @@ def prompt_session_type_selection() -> str:
|
|
230
230
|
"display_name": "AlloyDB",
|
231
231
|
"description": "Use AlloyDB for session management. Comes with terraform resources for deployment.",
|
232
232
|
},
|
233
|
+
"agent_engine": {
|
234
|
+
"display_name": "Vertex AI Agent Engine",
|
235
|
+
"description": "Managed session service that automatically handles conversation history",
|
236
|
+
},
|
233
237
|
}
|
234
238
|
|
235
239
|
console.print("\n> Please select a session type:")
|
@@ -357,6 +361,36 @@ def prompt_datastore_selection(
|
|
357
361
|
return datastore_type
|
358
362
|
|
359
363
|
|
364
|
+
def prompt_cicd_runner_selection() -> str:
|
365
|
+
"""Ask user to select a CI/CD runner."""
|
366
|
+
console = Console()
|
367
|
+
|
368
|
+
cicd_runners = {
|
369
|
+
"google_cloud_build": {
|
370
|
+
"display_name": "Google Cloud Build",
|
371
|
+
"description": "Fully managed CI/CD, deeply integrated with GCP for fast, consistent builds and deployments.",
|
372
|
+
},
|
373
|
+
"github_actions": {
|
374
|
+
"display_name": "GitHub Actions",
|
375
|
+
"description": "GitHub Actions: CI/CD with secure workload identity federation directly in GitHub.",
|
376
|
+
},
|
377
|
+
}
|
378
|
+
|
379
|
+
console.print("\n> Please select a CI/CD runner:")
|
380
|
+
for idx, (_key, info) in enumerate(cicd_runners.items(), 1):
|
381
|
+
console.print(
|
382
|
+
f"{idx}. [bold]{info['display_name']}[/] - [dim]{info['description']}[/]"
|
383
|
+
)
|
384
|
+
|
385
|
+
choice = IntPrompt.ask(
|
386
|
+
"\nEnter the number of your CI/CD runner choice",
|
387
|
+
default=1,
|
388
|
+
show_default=True,
|
389
|
+
)
|
390
|
+
|
391
|
+
return list(cicd_runners.keys())[choice - 1]
|
392
|
+
|
393
|
+
|
360
394
|
def get_template_path(agent_name: str, debug: bool = False) -> pathlib.Path:
|
361
395
|
"""Get the absolute path to the agent template directory."""
|
362
396
|
current_dir = pathlib.Path(__file__).parent.parent.parent.parent
|
@@ -404,6 +438,7 @@ def process_template(
|
|
404
438
|
template_dir: pathlib.Path,
|
405
439
|
project_name: str,
|
406
440
|
deployment_target: str | None = None,
|
441
|
+
cicd_runner: str | None = None,
|
407
442
|
include_data_ingestion: bool = False,
|
408
443
|
datastore: str | None = None,
|
409
444
|
session_type: str | None = None,
|
@@ -418,6 +453,7 @@ def process_template(
|
|
418
453
|
template_dir: Directory containing the template files
|
419
454
|
project_name: Name of the project to create
|
420
455
|
deployment_target: Optional deployment target (agent_engine or cloud_run)
|
456
|
+
cicd_runner: Optional CI/CD runner to use
|
421
457
|
include_data_ingestion: Whether to include data pipeline components
|
422
458
|
datastore: Optional datastore type for data ingestion
|
423
459
|
session_type: Optional session type for cloud_run deployment
|
@@ -585,13 +621,13 @@ def process_template(
|
|
585
621
|
/ "docs"
|
586
622
|
/ "adk-cheatsheet.md"
|
587
623
|
)
|
588
|
-
with open(adk_cheatsheet_path) as f:
|
624
|
+
with open(adk_cheatsheet_path, encoding="utf-8") as f:
|
589
625
|
adk_cheatsheet_content = f.read()
|
590
626
|
|
591
627
|
llm_txt_path = (
|
592
628
|
pathlib.Path(__file__).parent.parent.parent.parent / "llm.txt"
|
593
629
|
)
|
594
|
-
with open(llm_txt_path) as f:
|
630
|
+
with open(llm_txt_path, encoding="utf-8") as f:
|
595
631
|
llm_txt_content = f.read()
|
596
632
|
|
597
633
|
cookiecutter_config = {
|
@@ -605,6 +641,7 @@ def process_template(
|
|
605
641
|
"settings": settings,
|
606
642
|
"tags": tags,
|
607
643
|
"deployment_target": deployment_target or "",
|
644
|
+
"cicd_runner": cicd_runner or "google_cloud_build",
|
608
645
|
"session_type": session_type or "",
|
609
646
|
"frontend_type": frontend_type,
|
610
647
|
"extra_dependencies": [extra_deps],
|
@@ -629,7 +666,9 @@ def process_template(
|
|
629
666
|
],
|
630
667
|
}
|
631
668
|
|
632
|
-
with open(
|
669
|
+
with open(
|
670
|
+
cookiecutter_template / "cookiecutter.json", "w", encoding="utf-8"
|
671
|
+
) as f:
|
633
672
|
json.dump(cookiecutter_config, f, indent=4)
|
634
673
|
|
635
674
|
logging.debug(f"Template structure created at {cookiecutter_template}")
|
@@ -641,6 +680,7 @@ def process_template(
|
|
641
680
|
cookiecutter(
|
642
681
|
str(cookiecutter_template),
|
643
682
|
no_input=True,
|
683
|
+
overwrite_if_exists=True,
|
644
684
|
extra_context={
|
645
685
|
"project_name": project_name,
|
646
686
|
"agent_name": agent_name,
|
@@ -681,6 +721,27 @@ def process_template(
|
|
681
721
|
file_path.unlink()
|
682
722
|
logging.debug(f"Deleted {file_path}")
|
683
723
|
|
724
|
+
# Clean up unused_* files and directories created by conditional templates
|
725
|
+
import glob
|
726
|
+
|
727
|
+
unused_patterns = [
|
728
|
+
final_destination / "unused_*",
|
729
|
+
final_destination / "**" / "unused_*",
|
730
|
+
]
|
731
|
+
|
732
|
+
for pattern in unused_patterns:
|
733
|
+
for unused_path_str in glob.glob(str(pattern), recursive=True):
|
734
|
+
unused_path = pathlib.Path(unused_path_str)
|
735
|
+
if unused_path.exists():
|
736
|
+
if unused_path.is_dir():
|
737
|
+
shutil.rmtree(unused_path)
|
738
|
+
logging.debug(
|
739
|
+
f"Deleted unused directory: {unused_path}"
|
740
|
+
)
|
741
|
+
else:
|
742
|
+
unused_path.unlink()
|
743
|
+
logging.debug(f"Deleted unused file: {unused_path}")
|
744
|
+
|
684
745
|
# Handle pyproject.toml and uv.lock files
|
685
746
|
if is_remote and remote_template_path:
|
686
747
|
# For remote templates, use their pyproject.toml and uv.lock if they exist
|
@@ -696,19 +757,6 @@ def process_template(
|
|
696
757
|
if remote_uv_lock.exists():
|
697
758
|
shutil.copy2(remote_uv_lock, final_destination / "uv.lock")
|
698
759
|
logging.debug("Used uv.lock from remote template")
|
699
|
-
elif deployment_target:
|
700
|
-
# Fallback to base template lock file
|
701
|
-
base_template_name = get_base_template_name(remote_config or {})
|
702
|
-
lock_path = (
|
703
|
-
pathlib.Path(__file__).parent.parent.parent.parent
|
704
|
-
/ "src"
|
705
|
-
/ "resources"
|
706
|
-
/ "locks"
|
707
|
-
/ f"uv-{base_template_name}-{deployment_target}.lock"
|
708
|
-
)
|
709
|
-
if lock_path.exists():
|
710
|
-
shutil.copy2(lock_path, final_destination / "uv.lock")
|
711
|
-
logging.debug(f"Used fallback lock file from {lock_path}")
|
712
760
|
elif deployment_target:
|
713
761
|
# For local templates, use the existing logic
|
714
762
|
lock_path = (
|
@@ -20,6 +20,9 @@ from google.adk.cli.fast_api import get_fast_api_app
|
|
20
20
|
from google.cloud import logging as google_cloud_logging
|
21
21
|
from opentelemetry import trace
|
22
22
|
from opentelemetry.sdk.trace import TracerProvider, export
|
23
|
+
{%- if cookiecutter.session_type == "agent_engine" %}
|
24
|
+
from vertexai import agent_engines
|
25
|
+
{%- endif %}
|
23
26
|
|
24
27
|
from app.utils.gcs import create_bucket_if_not_exists
|
25
28
|
from app.utils.tracing import CloudTraceLoggingSpanExporter
|
@@ -55,6 +58,22 @@ db_host = os.environ.get("DB_HOST")
|
|
55
58
|
session_service_uri = None
|
56
59
|
if db_host and db_pass:
|
57
60
|
session_service_uri = f"postgresql://{db_user}:{db_pass}@{db_host}:5432/{db_name}"
|
61
|
+
{%- elif cookiecutter.session_type == "agent_engine" %}
|
62
|
+
# Agent Engine session configuration
|
63
|
+
# Use environment variable for agent name, default to project name
|
64
|
+
agent_name = os.environ.get("AGENT_ENGINE_SESSION_NAME", "{{cookiecutter.project_name}}")
|
65
|
+
|
66
|
+
# Check if an agent with this name already exists
|
67
|
+
existing_agents = list(agent_engines.list(filter=f"display_name={agent_name}"))
|
68
|
+
|
69
|
+
if existing_agents:
|
70
|
+
# Use the existing agent
|
71
|
+
agent_engine = existing_agents[0]
|
72
|
+
else:
|
73
|
+
# Create a new agent if none exists
|
74
|
+
agent_engine = agent_engines.create(display_name=agent_name)
|
75
|
+
|
76
|
+
session_service_uri = f"agentengine://{agent_engine.resource_name}"
|
58
77
|
{%- else %}
|
59
78
|
# In-memory session configuration - no persistent storage
|
60
79
|
session_service_uri = None
|
@@ -63,9 +63,10 @@ resource "google_service_networking_connection" "vpc_connection" {
|
|
63
63
|
|
64
64
|
# AlloyDB Cluster
|
65
65
|
resource "google_alloydb_cluster" "session_db_cluster" {
|
66
|
-
project
|
67
|
-
cluster_id
|
68
|
-
location
|
66
|
+
project = var.dev_project_id
|
67
|
+
cluster_id = "${var.project_name}-alloydb-cluster"
|
68
|
+
location = var.region
|
69
|
+
deletion_policy = "FORCE"
|
69
70
|
|
70
71
|
network_config {
|
71
72
|
network = google_compute_network.default.id
|
@@ -75,9 +75,10 @@ resource "google_service_networking_connection" "vpc_connection" {
|
|
75
75
|
resource "google_alloydb_cluster" "session_db_cluster" {
|
76
76
|
for_each = local.deploy_project_ids
|
77
77
|
|
78
|
-
project
|
79
|
-
cluster_id
|
80
|
-
location
|
78
|
+
project = local.deploy_project_ids[each.key]
|
79
|
+
cluster_id = "${var.project_name}-alloydb-cluster"
|
80
|
+
location = var.region
|
81
|
+
deletion_policy = "FORCE"
|
81
82
|
|
82
83
|
network_config {
|
83
84
|
network = google_compute_network.default[each.key].id
|
@@ -62,6 +62,10 @@ def start_server() -> subprocess.Popen[str]:
|
|
62
62
|
]
|
63
63
|
env = os.environ.copy()
|
64
64
|
env["INTEGRATION_TEST"] = "TRUE"
|
65
|
+
{%- if cookiecutter.session_type == "agent_engine" %}
|
66
|
+
# Set test-specific agent engine session name
|
67
|
+
env["AGENT_ENGINE_SESSION_NAME"] = "test-{{cookiecutter.project_name}}"
|
68
|
+
{%- endif %}
|
65
69
|
process = subprocess.Popen(
|
66
70
|
command,
|
67
71
|
stdout=subprocess.PIPE,
|
@@ -250,3 +254,34 @@ def test_collect_feedback(server_fixture: subprocess.Popen[str]) -> None:
|
|
250
254
|
FEEDBACK_URL, json=feedback_data, headers=HEADERS, timeout=10
|
251
255
|
)
|
252
256
|
assert response.status_code == 200
|
257
|
+
{%- if cookiecutter.session_type == "agent_engine" %}
|
258
|
+
|
259
|
+
|
260
|
+
@pytest.fixture(scope="session", autouse=True)
|
261
|
+
def cleanup_agent_engine_sessions() -> None:
|
262
|
+
"""Cleanup agent engine sessions created during tests."""
|
263
|
+
yield # Run tests first
|
264
|
+
|
265
|
+
# Cleanup after tests complete
|
266
|
+
from vertexai import agent_engines
|
267
|
+
|
268
|
+
try:
|
269
|
+
# Use same environment variable as server, default to project name
|
270
|
+
agent_name = os.environ.get(
|
271
|
+
"AGENT_ENGINE_SESSION_NAME", "{{cookiecutter.project_name}}"
|
272
|
+
)
|
273
|
+
|
274
|
+
# Find and delete agent engines with this name
|
275
|
+
existing_agents = list(agent_engines.list(filter=f"display_name={agent_name}"))
|
276
|
+
|
277
|
+
for agent_engine in existing_agents:
|
278
|
+
try:
|
279
|
+
agent_engines.delete(resource_name=agent_engine.name)
|
280
|
+
logger.info(f"Cleaned up agent engine: {agent_engine.name}")
|
281
|
+
except Exception as e:
|
282
|
+
logger.warning(
|
283
|
+
f"Failed to cleanup agent engine {agent_engine.name}: {e}"
|
284
|
+
)
|
285
|
+
except Exception as e:
|
286
|
+
logger.warning(f"Failed to cleanup agent engine sessions: {e}")
|
287
|
+
{%- endif %}
|
@@ -41,7 +41,7 @@ Comprehensive CSV and HTML reports detailing the load test performance will be g
|
|
41
41
|
|
42
42
|
## Remote Load Testing (Targeting Cloud Run)
|
43
43
|
|
44
|
-
This framework also supports load testing against remote targets, such as a staging Cloud Run instance. This process is seamlessly integrated into the Continuous Delivery
|
44
|
+
This framework also supports load testing against remote targets, such as a staging Cloud Run instance. This process is seamlessly integrated into the Continuous Delivery (CD) pipeline.
|
45
45
|
|
46
46
|
**Prerequisites:**
|
47
47
|
|
@@ -6452,16 +6452,16 @@
|
|
6452
6452
|
}
|
6453
6453
|
},
|
6454
6454
|
"node_modules/compression": {
|
6455
|
-
"version": "1.
|
6456
|
-
"resolved": "https://registry.npmjs.org/compression/-/compression-1.
|
6457
|
-
"integrity": "sha512-
|
6455
|
+
"version": "1.8.1",
|
6456
|
+
"resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz",
|
6457
|
+
"integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==",
|
6458
6458
|
"license": "MIT",
|
6459
6459
|
"dependencies": {
|
6460
6460
|
"bytes": "3.1.2",
|
6461
6461
|
"compressible": "~2.0.18",
|
6462
6462
|
"debug": "2.6.9",
|
6463
6463
|
"negotiator": "~0.6.4",
|
6464
|
-
"on-headers": "~1.0
|
6464
|
+
"on-headers": "~1.1.0",
|
6465
6465
|
"safe-buffer": "5.2.1",
|
6466
6466
|
"vary": "~1.1.2"
|
6467
6467
|
},
|
@@ -8038,14 +8038,15 @@
|
|
8038
8038
|
}
|
8039
8039
|
},
|
8040
8040
|
"node_modules/es-set-tostringtag": {
|
8041
|
-
"version": "2.0
|
8042
|
-
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.
|
8043
|
-
"integrity": "sha512-
|
8041
|
+
"version": "2.1.0",
|
8042
|
+
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
8043
|
+
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
8044
8044
|
"license": "MIT",
|
8045
8045
|
"dependencies": {
|
8046
|
-
"
|
8046
|
+
"es-errors": "^1.3.0",
|
8047
|
+
"get-intrinsic": "^1.2.6",
|
8047
8048
|
"has-tostringtag": "^1.0.2",
|
8048
|
-
"hasown": "^2.0.
|
8049
|
+
"hasown": "^2.0.2"
|
8049
8050
|
},
|
8050
8051
|
"engines": {
|
8051
8052
|
"node": ">= 0.4"
|
@@ -9335,14 +9336,16 @@
|
|
9335
9336
|
}
|
9336
9337
|
},
|
9337
9338
|
"node_modules/form-data": {
|
9338
|
-
"version": "3.0.
|
9339
|
-
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.
|
9340
|
-
"integrity": "sha512-
|
9339
|
+
"version": "3.0.4",
|
9340
|
+
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.4.tgz",
|
9341
|
+
"integrity": "sha512-f0cRzm6dkyVYV3nPoooP8XlccPQukegwhAnpoLcXy+X+A8KfpGOoXwDr9FLZd3wzgLaBGQBE3lY93Zm/i1JvIQ==",
|
9341
9342
|
"license": "MIT",
|
9342
9343
|
"dependencies": {
|
9343
9344
|
"asynckit": "^0.4.0",
|
9344
9345
|
"combined-stream": "^1.0.8",
|
9345
|
-
"
|
9346
|
+
"es-set-tostringtag": "^2.1.0",
|
9347
|
+
"hasown": "^2.0.2",
|
9348
|
+
"mime-types": "^2.1.35"
|
9346
9349
|
},
|
9347
9350
|
"engines": {
|
9348
9351
|
"node": ">= 6"
|
@@ -12916,9 +12919,9 @@
|
|
12916
12919
|
}
|
12917
12920
|
},
|
12918
12921
|
"node_modules/on-headers": {
|
12919
|
-
"version": "1.0
|
12920
|
-
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.
|
12921
|
-
"integrity": "sha512-
|
12922
|
+
"version": "1.1.0",
|
12923
|
+
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
|
12924
|
+
"integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
|
12922
12925
|
"license": "MIT",
|
12923
12926
|
"engines": {
|
12924
12927
|
"node": ">= 0.8"
|
@@ -31,7 +31,7 @@ DEFAULT_BASE_URL = "http://localhost:8000/"
|
|
31
31
|
|
32
32
|
DEFAULT_REMOTE_AGENT_ENGINE_ID = "N/A"
|
33
33
|
if os.path.exists("deployment_metadata.json"):
|
34
|
-
with open("deployment_metadata.json") as f:
|
34
|
+
with open("deployment_metadata.json", encoding="utf-8") as f:
|
35
35
|
DEFAULT_REMOTE_AGENT_ENGINE_ID = json.load(f)["remote_agent_engine_id"]
|
36
36
|
DEFAULT_AGENT_CALLABLE_PATH = "app.agent_engine_app.AgentEngineApp"
|
37
37
|
|
@@ -56,7 +56,7 @@ def save_chat(st: Any) -> None:
|
|
56
56
|
if len(messages) > 0:
|
57
57
|
session["messages"] = sanitize_messages(session["messages"])
|
58
58
|
filename = f"{session_id}.yaml"
|
59
|
-
with open(Path(SAVED_CHAT_PATH) / filename, "w") as file:
|
59
|
+
with open(Path(SAVED_CHAT_PATH) / filename, "w", encoding="utf-8") as file:
|
60
60
|
yaml.dump(
|
61
61
|
[session],
|
62
62
|
file,
|
@@ -49,7 +49,7 @@ class LocalChatMessageHistory(BaseChatMessageHistory):
|
|
49
49
|
for filename in os.listdir(self.user_dir):
|
50
50
|
if filename.endswith(".yaml"):
|
51
51
|
file_path = os.path.join(self.user_dir, filename)
|
52
|
-
with open(file_path) as f:
|
52
|
+
with open(file_path, encoding="utf-8") as f:
|
53
53
|
conversation = yaml.safe_load(f)
|
54
54
|
if not isinstance(conversation, list) or len(conversation) > 1:
|
55
55
|
raise ValueError(
|
@@ -71,7 +71,7 @@ class LocalChatMessageHistory(BaseChatMessageHistory):
|
|
71
71
|
def upsert_session(self, session: dict) -> None:
|
72
72
|
"""Updates or inserts a session into the local storage."""
|
73
73
|
session["update_time"] = datetime.now().isoformat()
|
74
|
-
with open(self.session_file, "w") as f:
|
74
|
+
with open(self.session_file, "w", encoding="utf-8") as f:
|
75
75
|
yaml.dump(
|
76
76
|
[session],
|
77
77
|
f,
|
@@ -356,7 +356,7 @@ document_pipeline = SequentialAgent(
|
|
356
356
|
Executes `sub_agents` simultaneously. Useful for independent tasks to reduce overall latency. All sub-agents share the same `session.state`.
|
357
357
|
|
358
358
|
```python
|
359
|
-
from google.adk.agents import ParallelAgent, Agent
|
359
|
+
from google.adk.agents import ParallelAgent, Agent, SequentialAgent
|
360
360
|
|
361
361
|
# Agents to fetch data concurrently
|
362
362
|
fetch_stock_price = Agent(name="StockPriceFetcher", ..., output_key="stock_data")
|