agent-starter-pack 0.9.1__py3-none-any.whl → 0.10.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.
Files changed (57) hide show
  1. {agent_starter_pack-0.9.1.dist-info → agent_starter_pack-0.10.0.dist-info}/METADATA +5 -7
  2. {agent_starter_pack-0.9.1.dist-info → agent_starter_pack-0.10.0.dist-info}/RECORD +54 -52
  3. agents/adk_base/.template/templateconfig.yaml +1 -1
  4. agents/adk_gemini_fullstack/.template/templateconfig.yaml +1 -1
  5. agents/agentic_rag/.template/templateconfig.yaml +1 -1
  6. agents/agentic_rag/README.md +1 -1
  7. agents/live_api/tests/integration/test_server_e2e.py +7 -1
  8. llm.txt +3 -2
  9. src/base_template/Makefile +4 -3
  10. src/base_template/README.md +7 -2
  11. src/base_template/deployment/README.md +6 -121
  12. src/base_template/deployment/terraform/github.tf +284 -0
  13. src/base_template/deployment/terraform/providers.tf +5 -0
  14. src/base_template/deployment/terraform/variables.tf +40 -1
  15. src/base_template/deployment/terraform/vars/env.tfvars +7 -1
  16. src/base_template/deployment/terraform/{% if cookiecutter.cicd_runner == 'github_actions' %}wif.tf{% else %}unused_wif.tf{% endif %} +43 -0
  17. 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
  18. src/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/deploy-to-prod.yaml +114 -0
  19. src/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/pr_checks.yaml +65 -0
  20. src/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/staging.yaml +170 -0
  21. 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
  22. src/base_template/{deployment/cd/staging.yaml → {% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/staging.yaml } +7 -7
  23. src/cli/commands/create.py +223 -41
  24. src/cli/commands/list.py +4 -10
  25. src/cli/commands/setup_cicd.py +292 -298
  26. src/cli/utils/cicd.py +19 -7
  27. src/cli/utils/remote_template.py +82 -15
  28. src/cli/utils/template.py +79 -19
  29. src/deployment_targets/cloud_run/app/server.py +19 -0
  30. src/deployment_targets/cloud_run/deployment/terraform/dev/service.tf +4 -3
  31. src/deployment_targets/cloud_run/deployment/terraform/service.tf +4 -3
  32. src/deployment_targets/cloud_run/tests/integration/test_server_e2e.py +35 -0
  33. src/deployment_targets/cloud_run/tests/load_test/README.md +1 -1
  34. src/frontends/live_api_react/frontend/package-lock.json +19 -16
  35. src/frontends/streamlit/frontend/side_bar.py +1 -1
  36. src/frontends/streamlit/frontend/utils/chat_utils.py +1 -1
  37. src/frontends/streamlit/frontend/utils/local_chat_history.py +2 -2
  38. src/resources/containers/e2e-tests/Dockerfile +39 -17
  39. src/resources/docs/adk-cheatsheet.md +1 -1
  40. src/resources/locks/uv-adk_base-agent_engine.lock +164 -131
  41. src/resources/locks/uv-adk_base-cloud_run.lock +177 -144
  42. src/resources/locks/uv-adk_gemini_fullstack-agent_engine.lock +164 -131
  43. src/resources/locks/uv-adk_gemini_fullstack-cloud_run.lock +177 -144
  44. src/resources/locks/uv-agentic_rag-agent_engine.lock +223 -190
  45. src/resources/locks/uv-agentic_rag-cloud_run.lock +251 -218
  46. src/resources/locks/uv-crewai_coding_crew-agent_engine.lock +315 -485
  47. src/resources/locks/uv-crewai_coding_crew-cloud_run.lock +358 -531
  48. src/resources/locks/uv-langgraph_base_react-agent_engine.lock +281 -249
  49. src/resources/locks/uv-langgraph_base_react-cloud_run.lock +323 -290
  50. src/resources/locks/uv-live_api-cloud_run.lock +350 -327
  51. src/resources/setup_cicd/cicd_variables.tf +0 -41
  52. src/resources/setup_cicd/github.tf +0 -87
  53. src/resources/setup_cicd/providers.tf +0 -39
  54. {agent_starter_pack-0.9.1.dist-info → agent_starter_pack-0.10.0.dist-info}/WHEEL +0 -0
  55. {agent_starter_pack-0.9.1.dist-info → agent_starter_pack-0.10.0.dist-info}/entry_points.txt +0 -0
  56. {agent_starter_pack-0.9.1.dist-info → agent_starter_pack-0.10.0.dist-info}/licenses/LICENSE +0 -0
  57. /src/base_template/{deployment/ci → {% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}}/pr_checks.yaml +0 -0
@@ -15,14 +15,16 @@
15
15
  import logging
16
16
  import os
17
17
  import pathlib
18
+ import shutil
18
19
  import subprocess
20
+ import tempfile
19
21
 
20
22
  import click
21
23
  from click.core import ParameterSource
22
24
  from rich.console import Console
23
25
  from rich.prompt import IntPrompt, Prompt
24
26
 
25
- from ..utils.datastores import DATASTORE_TYPES
27
+ from ..utils.datastores import DATASTORE_TYPES, DATASTORES
26
28
  from ..utils.gcp import verify_credentials, verify_vertex_connection
27
29
  from ..utils.logging import handle_cli_error
28
30
  from ..utils.remote_template import (
@@ -34,9 +36,11 @@ from ..utils.remote_template import (
34
36
  )
35
37
  from ..utils.template import (
36
38
  get_available_agents,
39
+ get_deployment_targets,
37
40
  get_template_path,
38
41
  load_template_config,
39
42
  process_template,
43
+ prompt_cicd_runner_selection,
40
44
  prompt_datastore_selection,
41
45
  prompt_deployment_target,
42
46
  prompt_session_type_selection,
@@ -93,6 +97,11 @@ def normalize_project_name(project_name: str) -> str:
93
97
  type=click.Choice(["agent_engine", "cloud_run"]),
94
98
  help="Deployment target name",
95
99
  )
100
+ @click.option(
101
+ "--cicd-runner",
102
+ type=click.Choice(["google_cloud_build", "github_actions"]),
103
+ help="CI/CD runner to use",
104
+ )
96
105
  @click.option(
97
106
  "--include-data-ingestion",
98
107
  "-i",
@@ -107,7 +116,7 @@ def normalize_project_name(project_name: str) -> str:
107
116
  )
108
117
  @click.option(
109
118
  "--session-type",
110
- type=click.Choice(["in_memory", "alloydb"]),
119
+ type=click.Choice(["in_memory", "alloydb", "agent_engine"]),
111
120
  help="Type of session storage to use",
112
121
  )
113
122
  @click.option("--debug", is_flag=True, help="Enable debug logging")
@@ -128,7 +137,7 @@ def normalize_project_name(project_name: str) -> str:
128
137
  @click.option(
129
138
  "--skip-checks",
130
139
  is_flag=True,
131
- help="Skip verification checks for uv, GCP and Vertex AI",
140
+ help="Skip verification checks for GCP and Vertex AI",
132
141
  default=False,
133
142
  )
134
143
  @handle_cli_error
@@ -137,6 +146,7 @@ def create(
137
146
  project_name: str,
138
147
  agent: str | None,
139
148
  deployment_target: str | None,
149
+ cicd_runner: str | None,
140
150
  include_data_ingestion: bool,
141
151
  datastore: str | None,
142
152
  session_type: str | None,
@@ -201,17 +211,28 @@ def create(
201
211
  # Agent selection - handle remote templates
202
212
  selected_agent = None
203
213
  template_source_path = None
214
+ temp_dir_to_clean = None
204
215
 
205
216
  if agent:
206
217
  if agent.startswith("local@"):
207
218
  path_str = agent.split("@", 1)[1]
208
- template_source_path = pathlib.Path(path_str).resolve()
209
- if not template_source_path.is_dir():
219
+ local_path = pathlib.Path(path_str).resolve()
220
+ if not local_path.is_dir():
210
221
  raise click.ClickException(
211
- f"Local path not found or not a directory: {template_source_path}"
222
+ f"Local path not found or not a directory: {local_path}"
212
223
  )
224
+
225
+ # Create a temporary directory and copy the local template to it
226
+ temp_dir = tempfile.mkdtemp(prefix="asp_local_template_")
227
+ temp_dir_to_clean = temp_dir
228
+ template_source_path = pathlib.Path(temp_dir) / local_path.name
229
+ shutil.copytree(local_path, template_source_path)
230
+
213
231
  selected_agent = f"local_{template_source_path.name}"
214
- console.print(f"Using local template: {template_source_path}")
232
+ console.print(f"Using local template: {local_path}")
233
+ logging.debug(
234
+ f"Copied local template to temporary dir: {template_source_path}"
235
+ )
215
236
  else:
216
237
  # Check if it's a remote template specification
217
238
  remote_spec = parse_agent_spec(agent)
@@ -223,7 +244,10 @@ def create(
223
244
  )
224
245
  else:
225
246
  console.print(f"Fetching remote template: {agent}")
226
- template_source_path = fetch_remote_template(remote_spec)
247
+ template_source_path, temp_dir_path = fetch_remote_template(
248
+ remote_spec
249
+ )
250
+ temp_dir_to_clean = str(temp_dir_path)
227
251
  selected_agent = f"remote_{hash(agent)}" # Generate unique name for remote template
228
252
  else:
229
253
  # Handle local agent selection
@@ -244,13 +268,17 @@ def create(
244
268
  f"Invalid agent name or number: {agent}"
245
269
  ) from err
246
270
 
247
- final_agent = (
248
- selected_agent
249
- if selected_agent
250
- else display_agent_selection(deployment_target)
251
- )
271
+ # Agent selection
272
+ final_agent = selected_agent
273
+ if not final_agent:
274
+ if auto_approve:
275
+ raise click.ClickException(
276
+ "Error: --agent is required when running with --auto-approve."
277
+ )
278
+ final_agent = display_agent_selection(deployment_target)
279
+
252
280
  if debug:
253
- logging.debug(f"Selected agent: {agent}")
281
+ logging.debug(f"Selected agent: {final_agent}")
254
282
 
255
283
  # Load template configuration based on whether it's remote or local
256
284
  if template_source_path:
@@ -302,14 +330,19 @@ def create(
302
330
  config = load_template_config(template_path)
303
331
  # Data ingestion and datastore selection
304
332
  if include_data_ingestion or datastore:
305
- # If datastore is specified but include_data_ingestion is not, set it to True
306
333
  include_data_ingestion = True
307
-
308
- # If include_data_ingestion is True but no datastore is specified, prompt for it
309
334
  if not datastore:
310
- # Pass a flag to indicate this is from explicit CLI flag
311
- datastore = prompt_datastore_selection(final_agent, from_cli_flag=True)
312
-
335
+ if auto_approve:
336
+ # Default to the first available datastore in non-interactive mode
337
+ datastore = next(iter(DATASTORES.keys()))
338
+ console.print(
339
+ f"Info: --datastore not specified. Defaulting to '{datastore}' in auto-approve mode.",
340
+ style="yellow",
341
+ )
342
+ else:
343
+ datastore = prompt_datastore_selection(
344
+ final_agent, from_cli_flag=True
345
+ )
313
346
  if debug:
314
347
  logging.debug(f"Data ingestion enabled: {include_data_ingestion}")
315
348
  logging.debug(f"Selected datastore type: {datastore}")
@@ -317,8 +350,15 @@ def create(
317
350
  # Check if the agent requires data ingestion
318
351
  if config and config.get("settings", {}).get("requires_data_ingestion"):
319
352
  include_data_ingestion = True
320
- datastore = prompt_datastore_selection(final_agent)
321
-
353
+ if not datastore:
354
+ if auto_approve:
355
+ datastore = next(iter(DATASTORES.keys()))
356
+ console.print(
357
+ f"Info: --datastore not specified. Defaulting to '{datastore}' in auto-approve mode.",
358
+ style="yellow",
359
+ )
360
+ else:
361
+ datastore = prompt_datastore_selection(final_agent)
322
362
  if debug:
323
363
  logging.debug(
324
364
  f"Data ingestion required by agent: {include_data_ingestion}"
@@ -334,13 +374,25 @@ def create(
334
374
  deployment_agent_name = get_base_template_name(config)
335
375
  remote_config = config
336
376
 
337
- final_deployment = (
338
- deployment_target
339
- if deployment_target
340
- else prompt_deployment_target(
377
+ final_deployment = deployment_target
378
+ if not final_deployment:
379
+ available_targets = get_deployment_targets(
341
380
  deployment_agent_name, remote_config=remote_config
342
381
  )
343
- )
382
+ if auto_approve:
383
+ if not available_targets:
384
+ raise click.ClickException(
385
+ f"Error: No deployment targets available for agent '{deployment_agent_name}'."
386
+ )
387
+ final_deployment = available_targets[0]
388
+ console.print(
389
+ f"Info: --deployment-target not specified. Defaulting to '{final_deployment}' in auto-approve mode.",
390
+ style="yellow",
391
+ )
392
+ else:
393
+ final_deployment = prompt_deployment_target(
394
+ deployment_agent_name, remote_config=remote_config
395
+ )
344
396
  if debug:
345
397
  logging.debug(f"Selected deployment target: {final_deployment}")
346
398
 
@@ -359,8 +411,19 @@ def create(
359
411
  )
360
412
  return
361
413
 
362
- if final_deployment in ("cloud_run") and not session_type:
363
- final_session_type = prompt_session_type_selection()
414
+ if (
415
+ final_deployment is not None
416
+ and final_deployment in ("cloud_run")
417
+ and not session_type
418
+ ):
419
+ if auto_approve:
420
+ final_session_type = "in_memory"
421
+ console.print(
422
+ "Info: --session-type not specified. Defaulting to 'in_memory' in auto-approve mode.",
423
+ style="yellow",
424
+ )
425
+ else:
426
+ final_session_type = prompt_session_type_selection()
364
427
  else:
365
428
  # Agents that don't require session management always use in-memory sessions
366
429
  final_session_type = "in_memory"
@@ -374,6 +437,20 @@ def create(
374
437
  if debug and final_session_type:
375
438
  logging.debug(f"Selected session type: {final_session_type}")
376
439
 
440
+ # CI/CD runner selection
441
+ final_cicd_runner = cicd_runner
442
+ if not final_cicd_runner:
443
+ if auto_approve:
444
+ final_cicd_runner = "google_cloud_build"
445
+ console.print(
446
+ "Info: --cicd-runner not specified. Defaulting to 'google_cloud_build' in auto-approve mode.",
447
+ style="yellow",
448
+ )
449
+ else:
450
+ final_cicd_runner = prompt_cicd_runner_selection()
451
+ if debug:
452
+ logging.debug(f"Selected CI/CD runner: {final_cicd_runner}")
453
+
377
454
  # Region confirmation (if not explicitly passed)
378
455
  if (
379
456
  not auto_approve
@@ -404,7 +481,7 @@ def create(
404
481
  )
405
482
  console.print(
406
483
  "> Please check your authentication settings and permissions. "
407
- "Visit https://cloud.google.com/vertex-ai/docs/authentication for help.",
484
+ "> Visit https://cloud.google.com/vertex-ai/docs/authentication for help.",
408
485
  style="yellow",
409
486
  )
410
487
  console.print(
@@ -434,6 +511,7 @@ def create(
434
511
  template_path,
435
512
  project_name,
436
513
  deployment_target=final_deployment,
514
+ cicd_runner=final_cicd_runner,
437
515
  include_data_ingestion=include_data_ingestion,
438
516
  datastore=datastore,
439
517
  session_type=final_session_type,
@@ -441,10 +519,22 @@ def create(
441
519
  remote_template_path=template_source_path,
442
520
  remote_config=config if template_source_path else None,
443
521
  )
444
- finally:
522
+
445
523
  # Replace region in all files if a different region was specified
446
524
  if region != "us-central1":
447
525
  replace_region_in_files(project_path, region, debug=debug)
526
+ finally:
527
+ # Clean up the temporary directory if one was created
528
+ if temp_dir_to_clean:
529
+ try:
530
+ shutil.rmtree(temp_dir_to_clean)
531
+ logging.debug(
532
+ f"Successfully cleaned up temporary directory: {temp_dir_to_clean}"
533
+ )
534
+ except OSError as e:
535
+ logging.warning(
536
+ f"Failed to clean up temporary directory {temp_dir_to_clean}: {e}"
537
+ )
448
538
 
449
539
  project_path = destination_dir / project_name
450
540
  cd_path = project_path if output_dir else project_name
@@ -474,14 +564,12 @@ def create(
474
564
  console.print("\n🚀 To get started, run the following command:")
475
565
 
476
566
  # Check if the agent has a 'dev' command in its settings
477
- if config["settings"].get("commands", {}).get("extra", {}).get("dev"):
478
- console.print(
479
- f" [bold bright_green]cd {cd_path} && make install && make dev[/]"
480
- )
481
- else:
482
- console.print(
483
- f" [bold bright_green]cd {cd_path} && make install && make playground[/]"
484
- )
567
+ interactive_command = config.get("settings", {}).get(
568
+ "interactive_command", "playground"
569
+ )
570
+ console.print(
571
+ f" [bold bright_green]cd {cd_path} && make install && make {interactive_command}[/]"
572
+ )
485
573
  except Exception:
486
574
  if debug:
487
575
  logging.exception(
@@ -518,14 +606,108 @@ def display_agent_selection(deployment_target: str | None = None) -> str:
518
606
  f"{num}. [bold]{agent['name']}[/] - [dim]{agent['description']}[/]"
519
607
  )
520
608
 
609
+ # Add special option for adk-samples
610
+ adk_samples_option = len(agents) + 1
611
+ console.print(
612
+ f"{adk_samples_option}. [bold]Browse agents from [link=https://github.com/google/adk-samples]google/adk-samples[/link][/] - [dim]Discover additional samples[/]"
613
+ )
614
+
521
615
  choice = IntPrompt.ask(
522
616
  "\nEnter the number of your template choice", default=1, show_default=True
523
617
  )
524
618
 
525
- if choice not in agents:
619
+ if choice == adk_samples_option:
620
+ return display_adk_samples_selection()
621
+ elif choice in agents:
622
+ return agents[choice]["name"]
623
+ else:
526
624
  raise ValueError(f"Invalid agent selection: {choice}")
527
625
 
528
- return agents[choice]["name"]
626
+
627
+ def display_adk_samples_selection() -> str:
628
+ """Display adk-samples agents and prompt for selection."""
629
+
630
+ from ..utils.remote_template import fetch_remote_template, parse_agent_spec
631
+
632
+ console.print("\n> Fetching agents from [bold blue]google/adk-samples[/]...")
633
+
634
+ try:
635
+ # Parse the adk-samples repository
636
+ spec = parse_agent_spec("https://github.com/google/adk-samples")
637
+ if not spec:
638
+ raise RuntimeError("Failed to parse adk-samples repository")
639
+
640
+ # Fetch the repository
641
+ repo_path, _ = fetch_remote_template(spec)
642
+
643
+ # Scan for agents in the repository
644
+ adk_agents = {}
645
+ agent_count = 1
646
+
647
+ # Search for templateconfig.yaml files to identify agents
648
+ for config_path in sorted(repo_path.glob("**/templateconfig.yaml")):
649
+ try:
650
+ import yaml
651
+
652
+ with open(config_path, encoding="utf-8") as f:
653
+ config = yaml.safe_load(f)
654
+
655
+ agent_name = config.get("name", config_path.parent.parent.name)
656
+ description = config.get("description", "No description available")
657
+
658
+ # Get the relative path from repo root
659
+ relative_path = config_path.parent.parent.relative_to(repo_path)
660
+
661
+ adk_agents[agent_count] = {
662
+ "name": agent_name,
663
+ "description": description,
664
+ "path": str(relative_path),
665
+ "spec": f"adk@{relative_path}",
666
+ }
667
+ agent_count += 1
668
+
669
+ except Exception as e:
670
+ logging.warning(
671
+ f"Could not load agent from {config_path.parent.parent}: {e}"
672
+ )
673
+
674
+ if not adk_agents:
675
+ console.print("No agents found in adk-samples repository", style="yellow")
676
+ # Fall back to local agents
677
+ return display_agent_selection()
678
+
679
+ console.print("\n> Available agents from [bold blue]google/adk-samples[/]:")
680
+ for num, agent in adk_agents.items():
681
+ console.print(
682
+ f"{num}. [bold]{agent['name']}[/] - [dim]{agent['description']}[/]"
683
+ )
684
+
685
+ # Add option to go back to local agents
686
+ back_option = len(adk_agents) + 1
687
+ console.print(
688
+ f"{back_option}. [bold]← Back to built-in agents[/] - [dim]Return to local agent selection[/]"
689
+ )
690
+
691
+ choice = IntPrompt.ask(
692
+ "\nEnter the number of your choice", default=1, show_default=True
693
+ )
694
+
695
+ if choice == back_option:
696
+ return display_agent_selection()
697
+ elif choice in adk_agents:
698
+ # Return the adk@ spec for the selected agent
699
+ selected_agent = adk_agents[choice]
700
+ console.print(
701
+ f"\n> Selected: [bold]{selected_agent['name']}[/] from adk-samples"
702
+ )
703
+ return selected_agent["spec"]
704
+ else:
705
+ raise ValueError(f"Invalid agent selection: {choice}")
706
+
707
+ except Exception as e:
708
+ console.print(f"Error fetching adk-samples agents: {e}", style="bold red")
709
+ console.print("Falling back to built-in agents...", style="yellow")
710
+ return display_agent_selection()
529
711
 
530
712
 
531
713
  def set_gcp_project(project_id: str, set_quota_project: bool = True) -> None:
src/cli/commands/list.py CHANGED
@@ -45,7 +45,7 @@ def display_agents_from_path(base_path: pathlib.Path, source_name: str) -> None:
45
45
  # Search for templateconfig.yaml files to identify agents
46
46
  for config_path in sorted(base_path.glob("**/templateconfig.yaml")):
47
47
  try:
48
- with open(config_path) as f:
48
+ with open(config_path, encoding="utf-8") as f:
49
49
  config = yaml.safe_load(f)
50
50
 
51
51
  agent_name = config.get("name", config_path.parent.parent.name)
@@ -82,15 +82,9 @@ def list_remote_agents(remote_source: str, scan_from_root: bool = False) -> None
82
82
  # specific template directory within the repo.
83
83
  template_dir_path = fetch_remote_template(spec)
84
84
 
85
- if scan_from_root:
86
- # For ADK, we want to scan the entire repo. We need to find the
87
- # root of the repo and scan from there.
88
- scan_path = template_dir_path
89
- while not (scan_path / ".git").exists() and scan_path.parent != scan_path:
90
- scan_path = scan_path.parent
91
- else:
92
- # For other git repos, respect the path given in the URL.
93
- scan_path = template_dir_path
85
+ # fetch_remote_template always returns a tuple of (repo_path, template_path)
86
+ repo_path, template_path = template_dir_path
87
+ scan_path = repo_path if scan_from_root else template_path
94
88
 
95
89
  display_agents_from_path(scan_path, remote_source)
96
90