agent-starter-pack 0.11.2__py3-none-any.whl → 0.12.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agent_starter_pack-0.11.2.dist-info → agent_starter_pack-0.12.0.dist-info}/METADATA +1 -1
- {agent_starter_pack-0.11.2.dist-info → agent_starter_pack-0.12.0.dist-info}/RECORD +41 -66
- agents/adk_base/app/__init__.py +17 -0
- agents/adk_base/notebooks/adk_app_testing.ipynb +4 -1
- agents/adk_base/tests/integration/test_agent.py +1 -1
- agents/agentic_rag/app/__init__.py +17 -0
- agents/agentic_rag/app/agent.py +2 -2
- agents/agentic_rag/notebooks/adk_app_testing.ipynb +4 -1
- agents/agentic_rag/tests/integration/test_agent.py +2 -2
- agents/crewai_coding_crew/tests/integration/test_agent.py +1 -1
- agents/langgraph_base_react/tests/integration/test_agent.py +1 -1
- agents/live_api/tests/unit/test_server.py +6 -6
- llm.txt +15 -4
- src/base_template/Makefile +5 -5
- src/base_template/README.md +4 -4
- src/base_template/deployment/terraform/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}build_triggers.tf{% else %}unused_build_triggers.tf{% endif %} +2 -2
- src/base_template/pyproject.toml +2 -2
- src/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/deploy-to-prod.yaml +1 -1
- 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 +2 -2
- src/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/deploy-to-prod.yaml +1 -1
- src/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/staging.yaml +1 -1
- src/cli/commands/create.py +25 -2
- src/cli/commands/enhance.py +94 -15
- src/cli/commands/list.py +1 -1
- src/cli/utils/remote_template.py +1 -1
- src/cli/utils/template.py +120 -41
- src/deployment_targets/agent_engine/tests/integration/test_agent_engine_app.py +3 -3
- src/deployment_targets/agent_engine/{app → {{cookiecutter.agent_directory}}}/agent_engine_app.py +10 -10
- src/deployment_targets/cloud_run/Dockerfile +2 -2
- src/deployment_targets/cloud_run/tests/integration/test_server_e2e.py +3 -3
- src/deployment_targets/cloud_run/tests/load_test/README.md +1 -1
- src/deployment_targets/cloud_run/tests/load_test/load_test.py +2 -2
- {agents/live_api/app → src/deployment_targets/cloud_run/{{cookiecutter.agent_directory}}}/server.py +186 -7
- src/resources/docs/adk-cheatsheet.md +3 -3
- src/base_template/app/__init__.py +0 -3
- src/deployment_targets/cloud_run/app/server.py +0 -206
- src/frontends/adk_gemini_fullstack/frontend/components.json +0 -21
- src/frontends/adk_gemini_fullstack/frontend/eslint.config.js +0 -28
- src/frontends/adk_gemini_fullstack/frontend/index.html +0 -12
- src/frontends/adk_gemini_fullstack/frontend/package-lock.json +0 -6105
- src/frontends/adk_gemini_fullstack/frontend/package.json +0 -47
- src/frontends/adk_gemini_fullstack/frontend/src/App.tsx +0 -564
- src/frontends/adk_gemini_fullstack/frontend/src/components/ActivityTimeline.tsx +0 -244
- src/frontends/adk_gemini_fullstack/frontend/src/components/ChatMessagesView.tsx +0 -420
- src/frontends/adk_gemini_fullstack/frontend/src/components/InputForm.tsx +0 -60
- src/frontends/adk_gemini_fullstack/frontend/src/components/WelcomeScreen.tsx +0 -56
- src/frontends/adk_gemini_fullstack/frontend/src/components/ui/badge.tsx +0 -46
- src/frontends/adk_gemini_fullstack/frontend/src/components/ui/button.tsx +0 -59
- src/frontends/adk_gemini_fullstack/frontend/src/components/ui/card.tsx +0 -92
- src/frontends/adk_gemini_fullstack/frontend/src/components/ui/input.tsx +0 -21
- src/frontends/adk_gemini_fullstack/frontend/src/components/ui/scroll-area.tsx +0 -56
- src/frontends/adk_gemini_fullstack/frontend/src/components/ui/select.tsx +0 -183
- src/frontends/adk_gemini_fullstack/frontend/src/components/ui/tabs.tsx +0 -64
- src/frontends/adk_gemini_fullstack/frontend/src/components/ui/textarea.tsx +0 -18
- src/frontends/adk_gemini_fullstack/frontend/src/global.css +0 -154
- src/frontends/adk_gemini_fullstack/frontend/src/main.tsx +0 -13
- src/frontends/adk_gemini_fullstack/frontend/src/utils.ts +0 -7
- src/frontends/adk_gemini_fullstack/frontend/src/vite-env.d.ts +0 -1
- src/frontends/adk_gemini_fullstack/frontend/tsconfig.json +0 -28
- src/frontends/adk_gemini_fullstack/frontend/tsconfig.node.json +0 -24
- src/frontends/adk_gemini_fullstack/frontend/vite.config.ts +0 -41
- {agent_starter_pack-0.11.2.dist-info → agent_starter_pack-0.12.0.dist-info}/WHEEL +0 -0
- {agent_starter_pack-0.11.2.dist-info → agent_starter_pack-0.12.0.dist-info}/entry_points.txt +0 -0
- {agent_starter_pack-0.11.2.dist-info → agent_starter_pack-0.12.0.dist-info}/licenses/LICENSE +0 -0
- /src/base_template/{app → {{cookiecutter.agent_directory}}}/utils/gcs.py +0 -0
- /src/base_template/{app → {{cookiecutter.agent_directory}}}/utils/tracing.py +0 -0
- /src/base_template/{app → {{cookiecutter.agent_directory}}}/utils/typing.py +0 -0
src/cli/commands/create.py
CHANGED
@@ -19,10 +19,10 @@ import pathlib
|
|
19
19
|
import shutil
|
20
20
|
import subprocess
|
21
21
|
import tempfile
|
22
|
+
import tomllib
|
22
23
|
from collections.abc import Callable
|
23
24
|
|
24
25
|
import click
|
25
|
-
import tomllib
|
26
26
|
from click.core import ParameterSource
|
27
27
|
from rich.console import Console
|
28
28
|
from rich.prompt import IntPrompt, Prompt
|
@@ -101,6 +101,11 @@ def shared_template_options(f: Callable) -> Callable:
|
|
101
101
|
type=click.Choice(["agent_engine", "cloud_run"]),
|
102
102
|
help="Deployment target name",
|
103
103
|
)(f)
|
104
|
+
f = click.option(
|
105
|
+
"--agent-directory",
|
106
|
+
"-dir",
|
107
|
+
help="Name of the agent directory (overrides template default)",
|
108
|
+
)(f)
|
104
109
|
return f
|
105
110
|
|
106
111
|
|
@@ -232,8 +237,10 @@ def create(
|
|
232
237
|
region: str,
|
233
238
|
skip_checks: bool,
|
234
239
|
in_folder: bool,
|
240
|
+
agent_directory: str | None,
|
235
241
|
base_template: str | None = None,
|
236
242
|
skip_welcome: bool = False,
|
243
|
+
cli_overrides: dict | None = None,
|
237
244
|
) -> None:
|
238
245
|
"""Create GCP-based AI agent projects from templates."""
|
239
246
|
try:
|
@@ -434,6 +441,14 @@ def create(
|
|
434
441
|
/ ".template"
|
435
442
|
)
|
436
443
|
config = load_template_config(template_path)
|
444
|
+
|
445
|
+
# Apply CLI overrides for local templates if provided (e.g., from enhance command)
|
446
|
+
if cli_overrides:
|
447
|
+
config = merge_template_configs(config, cli_overrides)
|
448
|
+
if debug:
|
449
|
+
logging.debug(
|
450
|
+
f"Applied CLI overrides to local template config: {cli_overrides}"
|
451
|
+
)
|
437
452
|
# Data ingestion and datastore selection
|
438
453
|
if include_data_ingestion or datastore:
|
439
454
|
include_data_ingestion = True
|
@@ -610,6 +625,13 @@ def create(
|
|
610
625
|
if debug:
|
611
626
|
logging.debug(f"Output directory: {destination_dir}")
|
612
627
|
|
628
|
+
# Construct CLI overrides for template processing
|
629
|
+
final_cli_overrides = cli_overrides or {}
|
630
|
+
if agent_directory:
|
631
|
+
if "settings" not in final_cli_overrides:
|
632
|
+
final_cli_overrides["settings"] = {}
|
633
|
+
final_cli_overrides["settings"]["agent_directory"] = agent_directory
|
634
|
+
|
613
635
|
try:
|
614
636
|
# Process template (handles both local and remote templates)
|
615
637
|
process_template(
|
@@ -623,8 +645,9 @@ def create(
|
|
623
645
|
session_type=final_session_type,
|
624
646
|
output_dir=destination_dir,
|
625
647
|
remote_template_path=template_source_path,
|
626
|
-
remote_config=config
|
648
|
+
remote_config=config,
|
627
649
|
in_folder=in_folder,
|
650
|
+
cli_overrides=final_cli_overrides,
|
628
651
|
)
|
629
652
|
|
630
653
|
# Replace region in all files if a different region was specified
|
src/cli/commands/enhance.py
CHANGED
@@ -13,6 +13,7 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
15
|
import pathlib
|
16
|
+
from typing import Any
|
16
17
|
|
17
18
|
import click
|
18
19
|
from rich.console import Console
|
@@ -48,7 +49,7 @@ def display_base_template_selection(current_base: str) -> str:
|
|
48
49
|
choice_num = 1
|
49
50
|
current_choice = None
|
50
51
|
|
51
|
-
for
|
52
|
+
for agent in agents.values():
|
52
53
|
template_choices[choice_num] = agent["name"]
|
53
54
|
current_indicator = " (current)" if agent["name"] == current_base else ""
|
54
55
|
console.print(
|
@@ -89,6 +90,10 @@ def display_base_template_selection(current_base: str) -> str:
|
|
89
90
|
"--base-template",
|
90
91
|
help="Base template to inherit from (e.g., adk_base, langgraph_base_react, agentic_rag)",
|
91
92
|
)
|
93
|
+
@click.option(
|
94
|
+
"--agent-directory",
|
95
|
+
help="Custom directory name for agent files (default: 'app' or auto-detected from pyproject.toml)",
|
96
|
+
)
|
92
97
|
@shared_template_options
|
93
98
|
@handle_cli_error
|
94
99
|
def enhance(
|
@@ -105,6 +110,7 @@ def enhance(
|
|
105
110
|
region: str,
|
106
111
|
skip_checks: bool,
|
107
112
|
base_template: str | None,
|
113
|
+
agent_directory: str | None,
|
108
114
|
) -> None:
|
109
115
|
"""Enhance your existing project with AI agent capabilities.
|
110
116
|
|
@@ -113,7 +119,8 @@ def enhance(
|
|
113
119
|
creating a new project directory.
|
114
120
|
|
115
121
|
For best compatibility, your project should follow the agent-starter-pack structure
|
116
|
-
with agent code organized in an
|
122
|
+
with agent code organized in an agent directory (default: /app, configurable via
|
123
|
+
--agent-directory).
|
117
124
|
|
118
125
|
TEMPLATE_PATH can be:
|
119
126
|
- A local directory path (e.g., . for current directory)
|
@@ -191,10 +198,13 @@ def enhance(
|
|
191
198
|
load_remote_template_config,
|
192
199
|
)
|
193
200
|
|
194
|
-
# Prepare CLI overrides for base template
|
195
|
-
cli_overrides = {}
|
201
|
+
# Prepare CLI overrides for base template and agent directory
|
202
|
+
cli_overrides: dict[str, Any] = {}
|
196
203
|
if base_template:
|
197
204
|
cli_overrides["base_template"] = base_template
|
205
|
+
if agent_directory:
|
206
|
+
cli_overrides["settings"] = cli_overrides.get("settings", {})
|
207
|
+
cli_overrides["settings"]["agent_directory"] = agent_directory
|
198
208
|
|
199
209
|
# Load config from current directory for inheritance info
|
200
210
|
current_dir = pathlib.Path.cwd()
|
@@ -209,6 +219,10 @@ def enhance(
|
|
209
219
|
if selected_base_template != original_base_template_name:
|
210
220
|
# Update CLI overrides with the selected base template
|
211
221
|
cli_overrides["base_template"] = selected_base_template
|
222
|
+
# Preserve agent_directory override if it was set
|
223
|
+
if agent_directory:
|
224
|
+
cli_overrides["settings"] = cli_overrides.get("settings", {})
|
225
|
+
cli_overrides["settings"]["agent_directory"] = agent_directory
|
212
226
|
base_template = selected_base_template
|
213
227
|
console.print(
|
214
228
|
f"✅ Selected base template: [cyan]{selected_base_template}[/cyan]"
|
@@ -232,9 +246,53 @@ def enhance(
|
|
232
246
|
# Validate project structure when using current directory template
|
233
247
|
if template_path == pathlib.Path("."):
|
234
248
|
current_dir = pathlib.Path.cwd()
|
235
|
-
app_folder = current_dir / "app"
|
236
249
|
|
237
|
-
|
250
|
+
# Determine agent directory: CLI param > pyproject.toml detection > default
|
251
|
+
detected_agent_directory = "app" # default
|
252
|
+
if not agent_directory: # Only try to detect if not provided via CLI
|
253
|
+
pyproject_path = current_dir / "pyproject.toml"
|
254
|
+
if pyproject_path.exists():
|
255
|
+
try:
|
256
|
+
import tomllib
|
257
|
+
|
258
|
+
with open(pyproject_path, "rb") as f:
|
259
|
+
pyproject_data = tomllib.load(f)
|
260
|
+
packages = (
|
261
|
+
pyproject_data.get("tool", {})
|
262
|
+
.get("hatch", {})
|
263
|
+
.get("build", {})
|
264
|
+
.get("targets", {})
|
265
|
+
.get("wheel", {})
|
266
|
+
.get("packages", [])
|
267
|
+
)
|
268
|
+
if packages:
|
269
|
+
# Find the first package that isn't 'frontend'
|
270
|
+
for pkg in packages:
|
271
|
+
if pkg != "frontend":
|
272
|
+
detected_agent_directory = pkg
|
273
|
+
break
|
274
|
+
except Exception as e:
|
275
|
+
if debug:
|
276
|
+
console.print(
|
277
|
+
f"[dim]Could not auto-detect agent directory: {e}[/dim]"
|
278
|
+
)
|
279
|
+
pass # Fall back to default
|
280
|
+
|
281
|
+
final_agent_directory = agent_directory or detected_agent_directory
|
282
|
+
|
283
|
+
# Show info about agent directory selection
|
284
|
+
if agent_directory:
|
285
|
+
console.print(
|
286
|
+
f"ℹ️ Using CLI-specified agent directory: [cyan]{agent_directory}[/cyan]"
|
287
|
+
)
|
288
|
+
elif detected_agent_directory != "app":
|
289
|
+
console.print(
|
290
|
+
f"ℹ️ Auto-detected agent directory: [cyan]{detected_agent_directory}[/cyan]"
|
291
|
+
)
|
292
|
+
|
293
|
+
agent_folder = current_dir / final_agent_directory
|
294
|
+
|
295
|
+
if not agent_folder.exists() or not agent_folder.is_dir():
|
238
296
|
console.print()
|
239
297
|
console.print(
|
240
298
|
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
@@ -245,42 +303,61 @@ def enhance(
|
|
245
303
|
)
|
246
304
|
console.print()
|
247
305
|
console.print(
|
248
|
-
"📁 [bold]Expected Structure:[/bold] [cyan]/
|
306
|
+
f"📁 [bold]Expected Structure:[/bold] [cyan]/{final_agent_directory}[/cyan] folder containing your agent code"
|
249
307
|
)
|
250
308
|
console.print(f"📍 [bold]Current Directory:[/bold] {current_dir}")
|
251
|
-
console.print(
|
309
|
+
console.print(
|
310
|
+
f"❌ [bold red]Missing:[/bold red] /{final_agent_directory} folder"
|
311
|
+
)
|
252
312
|
console.print()
|
253
313
|
console.print(
|
254
|
-
"
|
255
|
-
" your agent code should be organized in
|
314
|
+
f"The enhance command can still proceed, but for best compatibility"
|
315
|
+
f" your agent code should be organized in a /{final_agent_directory} folder structure."
|
256
316
|
)
|
257
317
|
console.print()
|
258
318
|
|
259
319
|
# Ask for confirmation after showing the structure warning
|
320
|
+
console.print("💡 Options:")
|
260
321
|
console.print(
|
261
|
-
"
|
322
|
+
f" • Create a /{final_agent_directory} folder and move your agent code there"
|
262
323
|
)
|
324
|
+
if final_agent_directory == "app":
|
325
|
+
console.print(
|
326
|
+
" • Use [cyan]--agent-directory <custom_name>[/cyan] if your agent code is in a different directory"
|
327
|
+
)
|
328
|
+
else:
|
329
|
+
console.print(
|
330
|
+
" • Use [cyan]--agent-directory <custom_name>[/cyan] to specify your existing agent directory"
|
331
|
+
)
|
263
332
|
console.print()
|
264
333
|
|
265
334
|
if not auto_approve:
|
266
335
|
if not click.confirm(
|
267
|
-
"Continue with enhancement despite missing /
|
336
|
+
f"Continue with enhancement despite missing /{final_agent_directory} folder?",
|
268
337
|
default=True,
|
269
338
|
):
|
270
339
|
console.print("✋ [yellow]Enhancement cancelled.[/yellow]")
|
271
340
|
return
|
272
341
|
else:
|
273
342
|
# Check for common agent files
|
274
|
-
agent_py =
|
343
|
+
agent_py = agent_folder / "agent.py"
|
275
344
|
if agent_py.exists():
|
276
345
|
console.print(
|
277
|
-
"Detected existing agent structure with [cyan]/
|
346
|
+
f"Detected existing agent structure with [cyan]/{final_agent_directory}/agent.py[/cyan]"
|
278
347
|
)
|
279
348
|
else:
|
280
349
|
console.print(
|
281
|
-
"ℹ️ [blue]Found /
|
350
|
+
f"ℹ️ [blue]Found /{final_agent_directory} folder[/blue] - ensure your agent code is properly organized within it, including an agent.py file"
|
282
351
|
)
|
283
352
|
|
353
|
+
# Prepare CLI overrides to pass to create command
|
354
|
+
final_cli_overrides: dict[str, Any] = {}
|
355
|
+
if base_template:
|
356
|
+
final_cli_overrides["base_template"] = base_template
|
357
|
+
if agent_directory:
|
358
|
+
final_cli_overrides["settings"] = final_cli_overrides.get("settings", {})
|
359
|
+
final_cli_overrides["settings"]["agent_directory"] = agent_directory
|
360
|
+
|
284
361
|
# Call the create command with in-folder mode enabled
|
285
362
|
ctx.invoke(
|
286
363
|
create,
|
@@ -297,6 +374,8 @@ def enhance(
|
|
297
374
|
region=region,
|
298
375
|
skip_checks=skip_checks,
|
299
376
|
in_folder=True, # Always use in-folder mode for enhance
|
377
|
+
agent_directory=agent_directory,
|
300
378
|
base_template=base_template,
|
301
379
|
skip_welcome=True, # Skip welcome message since enhance shows its own
|
380
|
+
cli_overrides=final_cli_overrides if final_cli_overrides else None,
|
302
381
|
)
|
src/cli/commands/list.py
CHANGED
src/cli/utils/remote_template.py
CHANGED
src/cli/utils/template.py
CHANGED
@@ -34,9 +34,6 @@ from .remote_template import (
|
|
34
34
|
render_and_merge_makefiles,
|
35
35
|
)
|
36
36
|
|
37
|
-
ADK_FILES = ["app/__init__.py"]
|
38
|
-
NON_ADK_FILES: list[str] = []
|
39
|
-
|
40
37
|
|
41
38
|
@dataclass
|
42
39
|
class TemplateConfig:
|
@@ -72,7 +69,11 @@ class TemplateConfig:
|
|
72
69
|
raise ValueError(f"Error loading template config: {err}") from err
|
73
70
|
|
74
71
|
|
75
|
-
|
72
|
+
def get_overwrite_folders(agent_directory: str) -> list[str]:
|
73
|
+
"""Get folders to overwrite with configurable agent directory."""
|
74
|
+
return [agent_directory, "frontend", "tests", "notebooks"]
|
75
|
+
|
76
|
+
|
76
77
|
TEMPLATE_CONFIG_FILE = "templateconfig.yaml"
|
77
78
|
DEPLOYMENT_FOLDERS = ["cloud_run", "agent_engine"]
|
78
79
|
DEFAULT_FRONTEND = "streamlit"
|
@@ -446,6 +447,7 @@ def process_template(
|
|
446
447
|
remote_template_path: pathlib.Path | None = None,
|
447
448
|
remote_config: dict[str, Any] | None = None,
|
448
449
|
in_folder: bool = False,
|
450
|
+
cli_overrides: dict[str, Any] | None = None,
|
449
451
|
) -> None:
|
450
452
|
"""Process the template directory and create a new project.
|
451
453
|
|
@@ -462,12 +464,25 @@ def process_template(
|
|
462
464
|
remote_template_path: Optional path to remote template for overlay
|
463
465
|
remote_config: Optional remote template configuration
|
464
466
|
in_folder: Whether to template directly into the output directory instead of creating a subdirectory
|
467
|
+
cli_overrides: Optional CLI override values that should take precedence over template config
|
465
468
|
"""
|
466
469
|
logging.debug(f"Processing template from {template_dir}")
|
467
470
|
logging.debug(f"Project name: {project_name}")
|
468
471
|
logging.debug(f"Include pipeline: {datastore}")
|
469
472
|
logging.debug(f"Output directory: {output_dir}")
|
470
473
|
|
474
|
+
def get_agent_directory(
|
475
|
+
template_config: dict[str, Any], cli_overrides: dict[str, Any] | None = None
|
476
|
+
) -> str:
|
477
|
+
"""Get agent directory with CLI override support."""
|
478
|
+
if (
|
479
|
+
cli_overrides
|
480
|
+
and "settings" in cli_overrides
|
481
|
+
and "agent_directory" in cli_overrides["settings"]
|
482
|
+
):
|
483
|
+
return cli_overrides["settings"]["agent_directory"]
|
484
|
+
return template_config.get("settings", {}).get("agent_directory", "app")
|
485
|
+
|
471
486
|
# Handle remote vs local templates
|
472
487
|
is_remote = remote_template_path is not None
|
473
488
|
|
@@ -519,7 +534,21 @@ def process_template(
|
|
519
534
|
base_template_path = (
|
520
535
|
pathlib.Path(__file__).parent.parent.parent / "base_template"
|
521
536
|
)
|
522
|
-
|
537
|
+
# Get agent directory from config early for use in file copying
|
538
|
+
# Load config early to get agent_directory
|
539
|
+
if is_remote:
|
540
|
+
early_config = remote_config or {}
|
541
|
+
else:
|
542
|
+
template_path = pathlib.Path(template_dir)
|
543
|
+
early_config = load_template_config(template_path)
|
544
|
+
agent_directory = get_agent_directory(early_config, cli_overrides)
|
545
|
+
copy_files(
|
546
|
+
base_template_path,
|
547
|
+
project_template,
|
548
|
+
agent_name,
|
549
|
+
overwrite=True,
|
550
|
+
agent_directory=agent_directory,
|
551
|
+
)
|
523
552
|
logging.debug(f"1. Copied base template from {base_template_path}")
|
524
553
|
|
525
554
|
# 2. Process deployment target if specified
|
@@ -535,6 +564,7 @@ def process_template(
|
|
535
564
|
project_template,
|
536
565
|
agent_name=agent_name,
|
537
566
|
overwrite=True,
|
567
|
+
agent_directory=agent_directory,
|
538
568
|
)
|
539
569
|
logging.debug(
|
540
570
|
f"2. Processed deployment files for target: {deployment_target}"
|
@@ -556,27 +586,11 @@ def process_template(
|
|
556
586
|
copy_frontend_files(frontend_type, project_template)
|
557
587
|
logging.debug(f"4. Processed frontend files for type: {frontend_type}")
|
558
588
|
|
559
|
-
#
|
560
|
-
|
561
|
-
for folder in OVERWRITE_FOLDERS:
|
562
|
-
agent_folder = agent_path / folder
|
563
|
-
project_folder = project_template / folder
|
564
|
-
if agent_folder.exists():
|
565
|
-
logging.debug(f"5. Copying agent folder {folder} with override")
|
566
|
-
copy_files(
|
567
|
-
agent_folder, project_folder, agent_name, overwrite=True
|
568
|
-
)
|
569
|
-
|
570
|
-
# 6. Finally, overlay remote template files if present
|
589
|
+
# 6. Skip remote template files during cookiecutter processing
|
590
|
+
# Remote files will be copied after cookiecutter to avoid Jinja conflicts
|
571
591
|
if is_remote and remote_template_path:
|
572
592
|
logging.debug(
|
573
|
-
|
574
|
-
)
|
575
|
-
copy_files(
|
576
|
-
remote_template_path,
|
577
|
-
project_template,
|
578
|
-
agent_name=agent_name,
|
579
|
-
overwrite=True,
|
593
|
+
"6. Skipping remote template files during cookiecutter processing - will copy after templating"
|
580
594
|
)
|
581
595
|
|
582
596
|
# Load and validate template config first
|
@@ -602,6 +616,45 @@ def process_template(
|
|
602
616
|
# Use the already loaded config
|
603
617
|
template_config = config
|
604
618
|
|
619
|
+
# 5. Copy agent-specific files to override base template (using final config)
|
620
|
+
if agent_path.exists():
|
621
|
+
agent_directory = get_agent_directory(template_config, cli_overrides)
|
622
|
+
|
623
|
+
# Get the template's default agent directory (usually "app")
|
624
|
+
template_agent_directory = template_config.get("settings", {}).get(
|
625
|
+
"agent_directory", "app"
|
626
|
+
)
|
627
|
+
|
628
|
+
# Copy agent directory (always from "app" to target directory)
|
629
|
+
source_agent_folder = agent_path / template_agent_directory
|
630
|
+
target_agent_folder = project_template / agent_directory
|
631
|
+
if source_agent_folder.exists():
|
632
|
+
logging.debug(
|
633
|
+
f"5. Copying agent folder {template_agent_directory} -> {agent_directory} with override"
|
634
|
+
)
|
635
|
+
copy_files(
|
636
|
+
source_agent_folder,
|
637
|
+
target_agent_folder,
|
638
|
+
agent_name,
|
639
|
+
overwrite=True,
|
640
|
+
agent_directory=agent_directory,
|
641
|
+
)
|
642
|
+
|
643
|
+
# Copy other folders (frontend, tests, notebooks)
|
644
|
+
other_folders = ["frontend", "tests", "notebooks"]
|
645
|
+
for folder in other_folders:
|
646
|
+
agent_folder = agent_path / folder
|
647
|
+
project_folder = project_template / folder
|
648
|
+
if agent_folder.exists():
|
649
|
+
logging.debug(f"5. Copying {folder} folder with override")
|
650
|
+
copy_files(
|
651
|
+
agent_folder,
|
652
|
+
project_folder,
|
653
|
+
agent_name,
|
654
|
+
overwrite=True,
|
655
|
+
agent_directory=agent_directory,
|
656
|
+
)
|
657
|
+
|
605
658
|
# Check if data processing should be included
|
606
659
|
if include_data_ingestion and datastore:
|
607
660
|
logging.debug(
|
@@ -649,6 +702,7 @@ def process_template(
|
|
649
702
|
"extra_dependencies": [extra_deps],
|
650
703
|
"data_ingestion": include_data_ingestion,
|
651
704
|
"datastore_type": datastore if datastore else "",
|
705
|
+
"agent_directory": get_agent_directory(template_config, cli_overrides),
|
652
706
|
"adk_cheatsheet": adk_cheatsheet_content,
|
653
707
|
"llm_txt": llm_txt_content,
|
654
708
|
"_copy_without_render": [
|
@@ -664,7 +718,9 @@ def process_template(
|
|
664
718
|
"*templates.py", # Don't render templates files
|
665
719
|
"Makefile", # Don't render Makefile - handled by render_and_merge_makefiles
|
666
720
|
# Don't render agent.py unless it's agentic_rag
|
667
|
-
"
|
721
|
+
f"{get_agent_directory(template_config, cli_overrides)}/agent.py"
|
722
|
+
if agent_name != "agentic_rag"
|
723
|
+
else "",
|
668
724
|
],
|
669
725
|
}
|
670
726
|
|
@@ -690,6 +746,21 @@ def process_template(
|
|
690
746
|
)
|
691
747
|
logging.debug("Template processing completed successfully")
|
692
748
|
|
749
|
+
# Now overlay remote template files if present (after cookiecutter processing)
|
750
|
+
if is_remote and remote_template_path:
|
751
|
+
generated_project_dir = temp_path / project_name
|
752
|
+
logging.debug(
|
753
|
+
f"Copying remote template files from {remote_template_path} to {generated_project_dir}"
|
754
|
+
)
|
755
|
+
copy_files(
|
756
|
+
remote_template_path,
|
757
|
+
generated_project_dir,
|
758
|
+
agent_name=agent_name,
|
759
|
+
overwrite=True,
|
760
|
+
agent_directory=agent_directory,
|
761
|
+
)
|
762
|
+
logging.debug("Remote template files copied successfully")
|
763
|
+
|
693
764
|
# Move the generated project to the final destination
|
694
765
|
generated_project_dir = temp_path / project_name
|
695
766
|
|
@@ -842,15 +913,7 @@ def process_template(
|
|
842
913
|
)
|
843
914
|
|
844
915
|
# Delete appropriate files based on ADK tag
|
845
|
-
|
846
|
-
files_to_delete = [final_destination / f for f in NON_ADK_FILES]
|
847
|
-
else:
|
848
|
-
files_to_delete = [final_destination / f for f in ADK_FILES]
|
849
|
-
|
850
|
-
for file_path in files_to_delete:
|
851
|
-
if file_path.exists():
|
852
|
-
file_path.unlink()
|
853
|
-
logging.debug(f"Deleted {file_path}")
|
916
|
+
agent_directory = get_agent_directory(template_config, cli_overrides)
|
854
917
|
|
855
918
|
# Clean up unused_* files and directories created by conditional templates
|
856
919
|
import glob
|
@@ -923,11 +986,15 @@ def process_template(
|
|
923
986
|
os.chdir(original_dir)
|
924
987
|
|
925
988
|
|
926
|
-
def should_exclude_path(
|
989
|
+
def should_exclude_path(
|
990
|
+
path: pathlib.Path, agent_name: str, agent_directory: str = "app"
|
991
|
+
) -> bool:
|
927
992
|
"""Determine if a path should be excluded based on the agent type."""
|
928
993
|
if agent_name == "live_api":
|
929
|
-
# Exclude the unit test utils folder and
|
930
|
-
if "tests/unit/test_utils" in str(path) or "
|
994
|
+
# Exclude the unit test utils folder and agent utils folder for live_api
|
995
|
+
if "tests/unit/test_utils" in str(path) or f"{agent_directory}/utils" in str(
|
996
|
+
path
|
997
|
+
):
|
931
998
|
logging.debug(f"Excluding path for live_api: {path}")
|
932
999
|
return True
|
933
1000
|
return False
|
@@ -938,6 +1005,7 @@ def copy_files(
|
|
938
1005
|
dst: pathlib.Path,
|
939
1006
|
agent_name: str | None = None,
|
940
1007
|
overwrite: bool = False,
|
1008
|
+
agent_directory: str = "app",
|
941
1009
|
) -> None:
|
942
1010
|
"""
|
943
1011
|
Copy files with configurable behavior for exclusions and overwrites.
|
@@ -947,6 +1015,7 @@ def copy_files(
|
|
947
1015
|
dst: Destination path
|
948
1016
|
agent_name: Name of the agent (for agent-specific exclusions)
|
949
1017
|
overwrite: Whether to overwrite existing files (True) or skip them (False)
|
1018
|
+
agent_directory: Name of the agent directory (for agent-specific exclusions)
|
950
1019
|
"""
|
951
1020
|
|
952
1021
|
def should_skip(path: pathlib.Path) -> bool:
|
@@ -957,7 +1026,9 @@ def copy_files(
|
|
957
1026
|
return True
|
958
1027
|
if ".git" in path.parts:
|
959
1028
|
return True
|
960
|
-
if agent_name is not None and should_exclude_path(
|
1029
|
+
if agent_name is not None and should_exclude_path(
|
1030
|
+
path, agent_name, agent_directory
|
1031
|
+
):
|
961
1032
|
return True
|
962
1033
|
if path.is_dir() and path.name == ".template":
|
963
1034
|
return True
|
@@ -970,9 +1041,10 @@ def copy_files(
|
|
970
1041
|
if should_skip(item):
|
971
1042
|
logging.debug(f"Skipping file/directory: {item}")
|
972
1043
|
continue
|
1044
|
+
|
973
1045
|
d = dst / item.name
|
974
1046
|
if item.is_dir():
|
975
|
-
copy_files(item, d, agent_name, overwrite)
|
1047
|
+
copy_files(item, d, agent_name, overwrite, agent_directory)
|
976
1048
|
else:
|
977
1049
|
if overwrite or not d.exists():
|
978
1050
|
logging.debug(f"Copying file: {item} -> {d}")
|
@@ -1012,7 +1084,10 @@ def copy_frontend_files(frontend_type: str, project_template: pathlib.Path) -> N
|
|
1012
1084
|
|
1013
1085
|
|
1014
1086
|
def copy_deployment_files(
|
1015
|
-
deployment_target: str,
|
1087
|
+
deployment_target: str,
|
1088
|
+
agent_name: str,
|
1089
|
+
project_template: pathlib.Path,
|
1090
|
+
agent_directory: str = "app",
|
1016
1091
|
) -> None:
|
1017
1092
|
"""Copy files from the specified deployment target folder."""
|
1018
1093
|
if not deployment_target:
|
@@ -1028,7 +1103,11 @@ def copy_deployment_files(
|
|
1028
1103
|
logging.debug(f"Copying deployment files from {deployment_path}")
|
1029
1104
|
# Pass agent_name to respect agent-specific exclusions
|
1030
1105
|
copy_files(
|
1031
|
-
deployment_path,
|
1106
|
+
deployment_path,
|
1107
|
+
project_template,
|
1108
|
+
agent_name=agent_name,
|
1109
|
+
overwrite=True,
|
1110
|
+
agent_directory=agent_directory,
|
1032
1111
|
)
|
1033
1112
|
else:
|
1034
1113
|
logging.warning(f"Deployment target directory not found: {deployment_path}")
|
@@ -18,11 +18,11 @@ import pytest
|
|
18
18
|
{%- if "adk" in cookiecutter.tags %}
|
19
19
|
from google.adk.events.event import Event
|
20
20
|
|
21
|
-
from
|
22
|
-
from
|
21
|
+
from {{cookiecutter.agent_directory}}.agent import root_agent
|
22
|
+
from {{cookiecutter.agent_directory}}.agent_engine_app import AgentEngineApp
|
23
23
|
{%- else %}
|
24
24
|
|
25
|
-
from
|
25
|
+
from {{cookiecutter.agent_directory}}.agent_engine_app import AgentEngineApp
|
26
26
|
{%- endif %}
|
27
27
|
|
28
28
|
|
src/deployment_targets/agent_engine/{app → {{cookiecutter.agent_directory}}}/agent_engine_app.py
RENAMED
@@ -30,10 +30,10 @@ from opentelemetry.sdk.trace import TracerProvider, export
|
|
30
30
|
from vertexai import agent_engines
|
31
31
|
from vertexai.preview.reasoning_engines import AdkApp
|
32
32
|
|
33
|
-
from
|
34
|
-
from
|
35
|
-
from
|
36
|
-
from
|
33
|
+
from {{cookiecutter.agent_directory}}.agent import root_agent
|
34
|
+
from {{cookiecutter.agent_directory}}.utils.gcs import create_bucket_if_not_exists
|
35
|
+
from {{cookiecutter.agent_directory}}.utils.tracing import CloudTraceLoggingSpanExporter
|
36
|
+
from {{cookiecutter.agent_directory}}.utils.typing import Feedback
|
37
37
|
|
38
38
|
|
39
39
|
class AgentEngineApp(AdkApp):
|
@@ -95,9 +95,9 @@ from langchain_core.runnables import RunnableConfig
|
|
95
95
|
from traceloop.sdk import Instruments, Traceloop
|
96
96
|
from vertexai import agent_engines
|
97
97
|
|
98
|
-
from
|
99
|
-
from
|
100
|
-
from
|
98
|
+
from {{cookiecutter.agent_directory}}.utils.gcs import create_bucket_if_not_exists
|
99
|
+
from {{cookiecutter.agent_directory}}.utils.tracing import CloudTraceLoggingSpanExporter
|
100
|
+
from {{cookiecutter.agent_directory}}.utils.typing import Feedback, InputChat, dumpd, ensure_valid_config
|
101
101
|
|
102
102
|
|
103
103
|
class AgentEngineApp:
|
@@ -110,7 +110,7 @@ class AgentEngineApp:
|
|
110
110
|
def set_up(self) -> None:
|
111
111
|
"""The set_up method is used to define application initialization logic"""
|
112
112
|
# Lazy import agent at setup time to avoid deployment dependencies
|
113
|
-
from
|
113
|
+
from {{cookiecutter.agent_directory}}.agent import agent
|
114
114
|
|
115
115
|
logging_client = google_cloud_logging.Client(project=self.project_id)
|
116
116
|
self.logger = logging_client.logger(__name__)
|
@@ -206,7 +206,7 @@ def deploy_agent_engine_app(
|
|
206
206
|
location: str,
|
207
207
|
agent_name: str | None = None,
|
208
208
|
requirements_file: str = ".requirements.txt",
|
209
|
-
extra_packages: list[str] = ["./
|
209
|
+
extra_packages: list[str] = ["./{{cookiecutter.agent_directory}}"],
|
210
210
|
env_vars: dict[str, str] = {},
|
211
211
|
service_account: str | None = None,
|
212
212
|
) -> agent_engines.AgentEngine:
|
@@ -305,7 +305,7 @@ if __name__ == "__main__":
|
|
305
305
|
parser.add_argument(
|
306
306
|
"--extra-packages",
|
307
307
|
nargs="+",
|
308
|
-
default=["./
|
308
|
+
default=["./{{cookiecutter.agent_directory}}"],
|
309
309
|
help="Additional packages to include",
|
310
310
|
)
|
311
311
|
parser.add_argument(
|