agent-starter-pack 0.0.1b0__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (162) hide show
  1. agent_starter_pack-0.0.1b0.dist-info/METADATA +143 -0
  2. agent_starter_pack-0.0.1b0.dist-info/RECORD +162 -0
  3. agent_starter_pack-0.0.1b0.dist-info/WHEEL +4 -0
  4. agent_starter_pack-0.0.1b0.dist-info/entry_points.txt +2 -0
  5. agent_starter_pack-0.0.1b0.dist-info/licenses/LICENSE +201 -0
  6. agents/agentic_rag_vertexai_search/README.md +22 -0
  7. agents/agentic_rag_vertexai_search/app/agent.py +145 -0
  8. agents/agentic_rag_vertexai_search/app/retrievers.py +79 -0
  9. agents/agentic_rag_vertexai_search/app/templates.py +53 -0
  10. agents/agentic_rag_vertexai_search/notebooks/evaluating_langgraph_agent.ipynb +1561 -0
  11. agents/agentic_rag_vertexai_search/template/.templateconfig.yaml +14 -0
  12. agents/agentic_rag_vertexai_search/tests/integration/test_agent.py +57 -0
  13. agents/crewai_coding_crew/README.md +34 -0
  14. agents/crewai_coding_crew/app/agent.py +86 -0
  15. agents/crewai_coding_crew/app/crew/config/agents.yaml +39 -0
  16. agents/crewai_coding_crew/app/crew/config/tasks.yaml +37 -0
  17. agents/crewai_coding_crew/app/crew/crew.py +71 -0
  18. agents/crewai_coding_crew/notebooks/evaluating_crewai_agent.ipynb +1571 -0
  19. agents/crewai_coding_crew/notebooks/evaluating_langgraph_agent.ipynb +1561 -0
  20. agents/crewai_coding_crew/template/.templateconfig.yaml +12 -0
  21. agents/crewai_coding_crew/tests/integration/test_agent.py +47 -0
  22. agents/langgraph_base_react/README.md +9 -0
  23. agents/langgraph_base_react/app/agent.py +73 -0
  24. agents/langgraph_base_react/notebooks/evaluating_langgraph_agent.ipynb +1561 -0
  25. agents/langgraph_base_react/template/.templateconfig.yaml +13 -0
  26. agents/langgraph_base_react/tests/integration/test_agent.py +48 -0
  27. agents/multimodal_live_api/README.md +50 -0
  28. agents/multimodal_live_api/app/agent.py +86 -0
  29. agents/multimodal_live_api/app/server.py +193 -0
  30. agents/multimodal_live_api/app/templates.py +51 -0
  31. agents/multimodal_live_api/app/vector_store.py +55 -0
  32. agents/multimodal_live_api/template/.templateconfig.yaml +15 -0
  33. agents/multimodal_live_api/tests/integration/test_server_e2e.py +254 -0
  34. agents/multimodal_live_api/tests/load_test/load_test.py +40 -0
  35. agents/multimodal_live_api/tests/unit/test_server.py +143 -0
  36. src/base_template/.gitignore +197 -0
  37. src/base_template/Makefile +37 -0
  38. src/base_template/README.md +91 -0
  39. src/base_template/app/utils/tracing.py +143 -0
  40. src/base_template/app/utils/typing.py +115 -0
  41. src/base_template/deployment/README.md +123 -0
  42. src/base_template/deployment/cd/deploy-to-prod.yaml +98 -0
  43. src/base_template/deployment/cd/staging.yaml +215 -0
  44. src/base_template/deployment/ci/pr_checks.yaml +51 -0
  45. src/base_template/deployment/terraform/apis.tf +34 -0
  46. src/base_template/deployment/terraform/build_triggers.tf +122 -0
  47. src/base_template/deployment/terraform/dev/apis.tf +42 -0
  48. src/base_template/deployment/terraform/dev/iam.tf +90 -0
  49. src/base_template/deployment/terraform/dev/log_sinks.tf +66 -0
  50. src/base_template/deployment/terraform/dev/providers.tf +29 -0
  51. src/base_template/deployment/terraform/dev/storage.tf +76 -0
  52. src/base_template/deployment/terraform/dev/variables.tf +126 -0
  53. src/base_template/deployment/terraform/dev/vars/env.tfvars +21 -0
  54. src/base_template/deployment/terraform/iam.tf +130 -0
  55. src/base_template/deployment/terraform/locals.tf +50 -0
  56. src/base_template/deployment/terraform/log_sinks.tf +72 -0
  57. src/base_template/deployment/terraform/providers.tf +35 -0
  58. src/base_template/deployment/terraform/service_accounts.tf +42 -0
  59. src/base_template/deployment/terraform/storage.tf +100 -0
  60. src/base_template/deployment/terraform/variables.tf +202 -0
  61. src/base_template/deployment/terraform/vars/env.tfvars +43 -0
  62. src/base_template/pyproject.toml +113 -0
  63. src/base_template/tests/unit/test_utils/test_tracing_exporter.py +140 -0
  64. src/cli/commands/create.py +534 -0
  65. src/cli/commands/setup_cicd.py +730 -0
  66. src/cli/main.py +35 -0
  67. src/cli/utils/__init__.py +35 -0
  68. src/cli/utils/cicd.py +662 -0
  69. src/cli/utils/gcp.py +120 -0
  70. src/cli/utils/logging.py +51 -0
  71. src/cli/utils/template.py +644 -0
  72. src/data_ingestion/README.md +79 -0
  73. src/data_ingestion/data_ingestion_pipeline/components/ingest_data.py +175 -0
  74. src/data_ingestion/data_ingestion_pipeline/components/process_data.py +321 -0
  75. src/data_ingestion/data_ingestion_pipeline/pipeline.py +58 -0
  76. src/data_ingestion/data_ingestion_pipeline/submit_pipeline.py +184 -0
  77. src/data_ingestion/pyproject.toml +17 -0
  78. src/data_ingestion/uv.lock +999 -0
  79. src/deployment_targets/agent_engine/app/agent_engine_app.py +238 -0
  80. src/deployment_targets/agent_engine/app/utils/gcs.py +42 -0
  81. src/deployment_targets/agent_engine/deployment_metadata.json +4 -0
  82. src/deployment_targets/agent_engine/notebooks/intro_reasoning_engine.ipynb +869 -0
  83. src/deployment_targets/agent_engine/tests/integration/test_agent_engine_app.py +120 -0
  84. src/deployment_targets/agent_engine/tests/load_test/.results/.placeholder +0 -0
  85. src/deployment_targets/agent_engine/tests/load_test/.results/report.html +264 -0
  86. src/deployment_targets/agent_engine/tests/load_test/.results/results_exceptions.csv +1 -0
  87. src/deployment_targets/agent_engine/tests/load_test/.results/results_failures.csv +1 -0
  88. src/deployment_targets/agent_engine/tests/load_test/.results/results_stats.csv +3 -0
  89. src/deployment_targets/agent_engine/tests/load_test/.results/results_stats_history.csv +22 -0
  90. src/deployment_targets/agent_engine/tests/load_test/README.md +42 -0
  91. src/deployment_targets/agent_engine/tests/load_test/load_test.py +100 -0
  92. src/deployment_targets/agent_engine/tests/unit/test_dummy.py +22 -0
  93. src/deployment_targets/cloud_run/Dockerfile +29 -0
  94. src/deployment_targets/cloud_run/app/server.py +128 -0
  95. src/deployment_targets/cloud_run/deployment/terraform/artifact_registry.tf +22 -0
  96. src/deployment_targets/cloud_run/deployment/terraform/dev/service_accounts.tf +20 -0
  97. src/deployment_targets/cloud_run/tests/integration/test_server_e2e.py +192 -0
  98. src/deployment_targets/cloud_run/tests/load_test/.results/.placeholder +0 -0
  99. src/deployment_targets/cloud_run/tests/load_test/README.md +79 -0
  100. src/deployment_targets/cloud_run/tests/load_test/load_test.py +85 -0
  101. src/deployment_targets/cloud_run/tests/unit/test_server.py +142 -0
  102. src/deployment_targets/cloud_run/uv.lock +6952 -0
  103. src/frontends/live_api_react/frontend/package-lock.json +19405 -0
  104. src/frontends/live_api_react/frontend/package.json +56 -0
  105. src/frontends/live_api_react/frontend/public/favicon.ico +0 -0
  106. src/frontends/live_api_react/frontend/public/index.html +62 -0
  107. src/frontends/live_api_react/frontend/public/robots.txt +3 -0
  108. src/frontends/live_api_react/frontend/src/App.scss +189 -0
  109. src/frontends/live_api_react/frontend/src/App.test.tsx +25 -0
  110. src/frontends/live_api_react/frontend/src/App.tsx +205 -0
  111. src/frontends/live_api_react/frontend/src/components/audio-pulse/AudioPulse.tsx +64 -0
  112. src/frontends/live_api_react/frontend/src/components/audio-pulse/audio-pulse.scss +68 -0
  113. src/frontends/live_api_react/frontend/src/components/control-tray/ControlTray.tsx +217 -0
  114. src/frontends/live_api_react/frontend/src/components/control-tray/control-tray.scss +201 -0
  115. src/frontends/live_api_react/frontend/src/components/logger/Logger.tsx +241 -0
  116. src/frontends/live_api_react/frontend/src/components/logger/logger.scss +133 -0
  117. src/frontends/live_api_react/frontend/src/components/logger/mock-logs.ts +151 -0
  118. src/frontends/live_api_react/frontend/src/components/side-panel/SidePanel.tsx +161 -0
  119. src/frontends/live_api_react/frontend/src/components/side-panel/side-panel.scss +285 -0
  120. src/frontends/live_api_react/frontend/src/contexts/LiveAPIContext.tsx +48 -0
  121. src/frontends/live_api_react/frontend/src/hooks/use-live-api.ts +115 -0
  122. src/frontends/live_api_react/frontend/src/hooks/use-media-stream-mux.ts +23 -0
  123. src/frontends/live_api_react/frontend/src/hooks/use-screen-capture.ts +72 -0
  124. src/frontends/live_api_react/frontend/src/hooks/use-webcam.ts +69 -0
  125. src/frontends/live_api_react/frontend/src/index.css +28 -0
  126. src/frontends/live_api_react/frontend/src/index.tsx +35 -0
  127. src/frontends/live_api_react/frontend/src/multimodal-live-types.ts +242 -0
  128. src/frontends/live_api_react/frontend/src/react-app-env.d.ts +17 -0
  129. src/frontends/live_api_react/frontend/src/reportWebVitals.ts +31 -0
  130. src/frontends/live_api_react/frontend/src/setupTests.ts +21 -0
  131. src/frontends/live_api_react/frontend/src/utils/audio-recorder.ts +111 -0
  132. src/frontends/live_api_react/frontend/src/utils/audio-streamer.ts +270 -0
  133. src/frontends/live_api_react/frontend/src/utils/audioworklet-registry.ts +43 -0
  134. src/frontends/live_api_react/frontend/src/utils/multimodal-live-client.ts +329 -0
  135. src/frontends/live_api_react/frontend/src/utils/store-logger.ts +64 -0
  136. src/frontends/live_api_react/frontend/src/utils/utils.ts +86 -0
  137. src/frontends/live_api_react/frontend/src/utils/worklets/audio-processing.ts +73 -0
  138. src/frontends/live_api_react/frontend/src/utils/worklets/vol-meter.ts +65 -0
  139. src/frontends/live_api_react/frontend/tsconfig.json +25 -0
  140. src/frontends/streamlit/frontend/side_bar.py +213 -0
  141. src/frontends/streamlit/frontend/streamlit_app.py +263 -0
  142. src/frontends/streamlit/frontend/style/app_markdown.py +37 -0
  143. src/frontends/streamlit/frontend/utils/chat_utils.py +67 -0
  144. src/frontends/streamlit/frontend/utils/local_chat_history.py +125 -0
  145. src/frontends/streamlit/frontend/utils/message_editing.py +59 -0
  146. src/frontends/streamlit/frontend/utils/multimodal_utils.py +217 -0
  147. src/frontends/streamlit/frontend/utils/stream_handler.py +282 -0
  148. src/frontends/streamlit/frontend/utils/title_summary.py +77 -0
  149. src/resources/containers/data_processing/Dockerfile +25 -0
  150. src/resources/locks/uv-agentic_rag_vertexai_search-agent_engine.lock +4684 -0
  151. src/resources/locks/uv-agentic_rag_vertexai_search-cloud_run.lock +5799 -0
  152. src/resources/locks/uv-crewai_coding_crew-agent_engine.lock +5509 -0
  153. src/resources/locks/uv-crewai_coding_crew-cloud_run.lock +6688 -0
  154. src/resources/locks/uv-langgraph_base_react-agent_engine.lock +4595 -0
  155. src/resources/locks/uv-langgraph_base_react-cloud_run.lock +5710 -0
  156. src/resources/locks/uv-multimodal_live_api-cloud_run.lock +5665 -0
  157. src/resources/setup_cicd/cicd_variables.tf +36 -0
  158. src/resources/setup_cicd/github.tf +85 -0
  159. src/resources/setup_cicd/providers.tf +39 -0
  160. src/utils/generate_locks.py +135 -0
  161. src/utils/lock_utils.py +82 -0
  162. src/utils/watch_and_rebuild.py +190 -0
@@ -0,0 +1,36 @@
1
+ # Copyright 2025 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ variable "repository_owner" {
16
+ description = "Owner of the GitHub repository"
17
+ type = string
18
+ }
19
+
20
+ variable "github_app_installation_id" {
21
+ description = "GitHub App Installation ID"
22
+ type = string
23
+ }
24
+
25
+ variable "github_pat_secret_id" {
26
+ description = "GitHub PAT secret id in Cloud Secret Manager"
27
+ type = string
28
+ default = "github-pat"
29
+ }
30
+
31
+ variable "connection_exists" {
32
+ description = "Flag indicating if a Cloud Build connection already exists"
33
+ type = bool
34
+ default = false
35
+ }
36
+
@@ -0,0 +1,85 @@
1
+ # Copyright 2025 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ provider "github" {
16
+ token = data.google_secret_manager_secret_version.github_token.secret_data
17
+ owner = var.repository_owner
18
+ }
19
+
20
+ data "google_secret_manager_secret_version" "github_token" {
21
+ project = var.cicd_runner_project_id
22
+ secret = var.github_pat_secret_id
23
+ version = "latest"
24
+ }
25
+
26
+ # Create the GitHub connection
27
+ resource "google_cloudbuildv2_connection" "github_connection" {
28
+ count = var.connection_exists ? 0 : 1
29
+ project = var.cicd_runner_project_id
30
+ location = var.region
31
+ name = var.host_connection_name
32
+
33
+ github_config {
34
+ app_installation_id = var.github_app_installation_id
35
+ authorizer_credential {
36
+ oauth_token_secret_version = "projects/${var.cicd_runner_project_id}/secrets/${var.github_pat_secret_id}/versions/latest"
37
+ }
38
+ }
39
+ depends_on = [resource.google_project_service.cicd_services, resource.google_project_service.shared_services]
40
+ }
41
+
42
+ locals {
43
+ connection_name = var.host_connection_name
44
+ }
45
+
46
+
47
+ # Try to get existing repo
48
+ data "github_repository" "existing_repo" {
49
+ full_name = "${var.repository_owner}/${var.repository_name}"
50
+ }
51
+
52
+ # Only create GitHub repo if it doesn't already exist
53
+ resource "github_repository" "repo" {
54
+ count = data.github_repository.existing_repo.repo_id == null ? 1 : 0
55
+ name = var.repository_name
56
+ description = "Repository created by Terraform"
57
+ visibility = "private"
58
+
59
+ has_issues = true
60
+ has_wiki = false
61
+ has_projects = false
62
+ has_downloads = false
63
+
64
+ allow_merge_commit = true
65
+ allow_squash_merge = true
66
+ allow_rebase_merge = true
67
+
68
+ auto_init = false
69
+ }
70
+
71
+
72
+ resource "google_cloudbuildv2_repository" "repo" {
73
+ project = var.cicd_runner_project_id
74
+ location = var.region
75
+ name = var.repository_name
76
+
77
+ parent_connection = "projects/${var.cicd_runner_project_id}/locations/${var.region}/connections/${local.connection_name}"
78
+ remote_uri = "https://github.com/${var.repository_owner}/${var.repository_name}.git"
79
+ depends_on = [
80
+ resource.google_project_service.cicd_services,
81
+ resource.google_project_service.shared_services,
82
+ data.github_repository.existing_repo,
83
+ github_repository.repo
84
+ ]
85
+ }
@@ -0,0 +1,39 @@
1
+ # Copyright 2025 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ terraform {
16
+ required_version = ">= 1.0.0"
17
+ required_providers {
18
+ google = {
19
+ source = "hashicorp/google"
20
+ version = "< 7.0.0"
21
+ }
22
+ github = {
23
+ source = "integrations/github"
24
+ version = "~> 6.5.0"
25
+ }
26
+ }
27
+ }
28
+
29
+ provider "google" {
30
+ alias = "staging_billing_override"
31
+ billing_project = var.staging_project_id
32
+ user_project_override = true
33
+ }
34
+
35
+ provider "google" {
36
+ alias = "prod_billing_override"
37
+ billing_project = var.prod_project_id
38
+ user_project_override = true
39
+ }
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/env python3
2
+ # Copyright 2025 Google LLC
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ """Utility script to generate lock files for all agent and deployment target combinations."""
17
+
18
+ import logging
19
+ import pathlib
20
+ import shutil
21
+ import subprocess
22
+ import tempfile
23
+
24
+ import click
25
+ from jinja2 import Template
26
+ from lock_utils import get_agent_configs, get_lock_filename
27
+
28
+
29
+ def ensure_lock_dir() -> pathlib.Path:
30
+ """Ensure the locks directory exists and is empty.
31
+
32
+ Returns:
33
+ Path to the locks directory
34
+ """
35
+ lock_dir = pathlib.Path("src/resources/locks")
36
+
37
+ # Remove if exists
38
+ if lock_dir.exists():
39
+ shutil.rmtree(lock_dir)
40
+
41
+ # Create fresh directory
42
+ lock_dir.mkdir(parents=True)
43
+
44
+ return lock_dir
45
+
46
+
47
+ def generate_pyproject(
48
+ template_path: pathlib.Path, deployment_target: str, extra_dependencies: list[str]
49
+ ) -> str:
50
+ """Generate pyproject.toml content from template.
51
+
52
+ Args:
53
+ template_path: Path to the pyproject.toml template
54
+ deployment_target: Target deployment platform
55
+ extra_dependencies: List of additional dependencies from .templateconfig.yaml
56
+ """
57
+ with open(template_path, encoding="utf-8") as f:
58
+ template = Template(f.read(), trim_blocks=True, lstrip_blocks=True)
59
+
60
+ # Convert list to proper format for template
61
+ context = {
62
+ "cookiecutter": {
63
+ "project_name": "locked-template",
64
+ "deployment_target": deployment_target,
65
+ # Ensure extra_dependencies is a list
66
+ "extra_dependencies": list(extra_dependencies)
67
+ if extra_dependencies
68
+ else [],
69
+ }
70
+ }
71
+
72
+ # Add debug logging
73
+ logging.debug(f"Template context: {context}")
74
+ result = template.render(context)
75
+ logging.debug(f"Generated pyproject.toml:\n{result}")
76
+
77
+ return result
78
+
79
+
80
+ def generate_lock_file(pyproject_content: str, output_path: pathlib.Path) -> None:
81
+ """Generate uv.lock file from pyproject content."""
82
+ with tempfile.TemporaryDirectory() as tmpdir:
83
+ tmp_dir = pathlib.Path(tmpdir)
84
+
85
+ # Write temporary pyproject.toml
86
+ with open(tmp_dir / "pyproject.toml", "w", encoding="utf-8") as f:
87
+ f.write(pyproject_content)
88
+
89
+ # Run uv pip compile to generate lock file
90
+ subprocess.run(["uv", "lock"], cwd=tmp_dir, check=True)
91
+ # Replace locked-template with {{cookiecutter.project_name}} in generated lock file
92
+ lock_file_path = tmp_dir / "uv.lock"
93
+ with open(lock_file_path, "r+", encoding="utf-8") as f:
94
+ lock_content = f.read()
95
+ f.seek(0)
96
+ f.write(
97
+ lock_content.replace("locked-template", "{{cookiecutter.project_name}}")
98
+ )
99
+ f.truncate()
100
+
101
+ # Copy the generated lock file to output location
102
+ shutil.copy2(lock_file_path, output_path)
103
+
104
+
105
+ @click.command()
106
+ @click.option(
107
+ "--template",
108
+ type=click.Path(exists=True, path_type=pathlib.Path),
109
+ default="src/base_template/pyproject.toml",
110
+ help="Path to template pyproject.toml",
111
+ )
112
+ def main(template: pathlib.Path) -> None:
113
+ """Generate lock files for all agent and deployment target combinations."""
114
+ lock_dir = ensure_lock_dir()
115
+ agent_configs = get_agent_configs()
116
+
117
+ for agent_name, config in agent_configs.items():
118
+ for target in config.targets:
119
+ print(f"Generating lock file for {agent_name} with {target}...")
120
+
121
+ # Generate pyproject content
122
+ content = generate_pyproject(
123
+ template,
124
+ deployment_target=target,
125
+ extra_dependencies=config.dependencies,
126
+ )
127
+
128
+ # Generate lock file
129
+ output_path = lock_dir / get_lock_filename(agent_name, target)
130
+ generate_lock_file(content, output_path)
131
+ print(f"Generated {output_path}")
132
+
133
+
134
+ if __name__ == "__main__":
135
+ main()
@@ -0,0 +1,82 @@
1
+ # Copyright 2025 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Utilities for managing Poetry lock files and dependencies."""
16
+
17
+ import pathlib
18
+ from pathlib import Path
19
+ from typing import NamedTuple
20
+
21
+ import yaml
22
+
23
+
24
+ class AgentConfig(NamedTuple):
25
+ """Configuration for an agent template."""
26
+
27
+ targets: set[str]
28
+ dependencies: list[str]
29
+
30
+
31
+ def get_agent_configs(
32
+ agents_dir: pathlib.Path = pathlib.Path("agents"),
33
+ ) -> dict[str, AgentConfig]:
34
+ """Get all agents and their supported deployment targets.
35
+
36
+ Args:
37
+ agents_dir: Path to the agents directory
38
+
39
+ Returns:
40
+ Dictionary mapping agent names to their configuration
41
+ """
42
+ agent_configs = {}
43
+
44
+ for agent_dir in agents_dir.iterdir():
45
+ if not agent_dir.is_dir():
46
+ continue
47
+
48
+ config_file = agent_dir / "template" / ".templateconfig.yaml"
49
+ if not config_file.exists():
50
+ continue
51
+
52
+ with open(config_file, encoding="utf-8") as f:
53
+ config = yaml.safe_load(f)
54
+
55
+ agent_name = agent_dir.name
56
+ settings = config.get("settings", {})
57
+
58
+ agent_configs[agent_name] = AgentConfig(
59
+ targets=set(settings.get("deployment_targets", [])),
60
+ dependencies=settings.get("extra_dependencies", []),
61
+ )
62
+
63
+ return agent_configs
64
+
65
+
66
+ def get_lock_filename(agent_name: str, deployment_target: str) -> str:
67
+ """Generate the lock filename for a given agent and deployment target.
68
+
69
+ Args:
70
+ agent_name: Name of the agent
71
+ deployment_target: Target deployment platform
72
+
73
+ Returns:
74
+ Formatted lock filename
75
+ """
76
+ return f"uv-{agent_name}-{deployment_target}.lock"
77
+
78
+
79
+ def get_lock_path(agent_name: str, deployment_target: str) -> Path:
80
+ """Get the path to the appropriate lock file."""
81
+ lock_filename = get_lock_filename(agent_name, deployment_target)
82
+ return Path("src/resources/locks") / lock_filename
@@ -0,0 +1,190 @@
1
+ # Copyright 2025 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import logging
16
+ import pathlib
17
+ import shutil
18
+ import subprocess
19
+ import time
20
+
21
+ import click
22
+ from rich.console import Console
23
+ from watchdog.events import FileSystemEventHandler
24
+ from watchdog.observers import Observer
25
+
26
+ console = Console()
27
+
28
+
29
+ class TemplateHandler(FileSystemEventHandler):
30
+ def __init__(
31
+ self,
32
+ agent_name: str,
33
+ project_name: str,
34
+ deployment_target: str,
35
+ output_dir: str | None,
36
+ region: str,
37
+ ):
38
+ self.agent_name = agent_name
39
+ self.project_name = project_name
40
+ self.deployment_target = deployment_target
41
+ self.output_dir = output_dir
42
+ self.region = region
43
+ self.last_rebuild = 0
44
+ self.rebuild_cooldown = 1 # Seconds to wait between rebuilds
45
+
46
+ def on_modified(self, event):
47
+ if event.is_directory:
48
+ return
49
+
50
+ # Implement cooldown to prevent multiple rapid rebuilds
51
+ current_time = time.time()
52
+ if current_time - self.last_rebuild < self.rebuild_cooldown:
53
+ return
54
+
55
+ self.last_rebuild = current_time
56
+
57
+ console.print(f"Detected change in {event.src_path}")
58
+ self.rebuild_template()
59
+
60
+ def rebuild_template(self):
61
+ try:
62
+ # Clean output directory
63
+ project_path = (
64
+ pathlib.Path(self.output_dir) / self.project_name
65
+ if self.output_dir
66
+ else pathlib.Path(self.project_name)
67
+ )
68
+
69
+ # Check if the project directory exists and remove it
70
+ if project_path.exists():
71
+ console.print(
72
+ f"Removing existing directory: {project_path}", style="yellow"
73
+ )
74
+ shutil.rmtree(project_path)
75
+
76
+ # Rebuild using the CLI tool with agent and deployment target
77
+ cmd = [
78
+ "uv",
79
+ "run",
80
+ "-m",
81
+ "src.cli.main",
82
+ "create",
83
+ str(self.project_name),
84
+ "--agent",
85
+ self.agent_name,
86
+ "--deployment-target",
87
+ self.deployment_target,
88
+ "--output-dir",
89
+ str(self.output_dir) if self.output_dir else ".",
90
+ "--auto-approve",
91
+ "--region",
92
+ self.region,
93
+ ]
94
+ console.print(f"Executing: {' '.join(cmd)}", style="bold blue")
95
+ subprocess.run(cmd, check=True)
96
+
97
+ console.print("✨ Template rebuilt successfully!", style="bold green")
98
+
99
+ except subprocess.CalledProcessError as e:
100
+ console.print(f"Error rebuilding template: {e}", style="bold red")
101
+ except Exception as e:
102
+ console.print(f"Unexpected error: {e}", style="bold red")
103
+
104
+
105
+ @click.command()
106
+ @click.argument("agent")
107
+ @click.argument("project_name")
108
+ @click.option("--deployment-target", "-d", help="Deployment target to use")
109
+ @click.option(
110
+ "--output-dir",
111
+ "-o",
112
+ type=click.Path(),
113
+ default="target",
114
+ help="Output directory for the project",
115
+ )
116
+ @click.option("--debug", is_flag=True, help="Enable debug logging")
117
+ @click.option("--region", default="us-central1", help="GCP region to use")
118
+ def watch(
119
+ agent: str,
120
+ project_name: str,
121
+ deployment_target: str,
122
+ output_dir: str | None,
123
+ debug: bool,
124
+ region: str,
125
+ ):
126
+ """
127
+ Watch a agent's template and automatically rebuild when changes are detected.
128
+
129
+ agent: Name of the agent to watch (e.g., langgraph_base_react)
130
+ PROJECT_NAME: Name of the project to generate
131
+ """
132
+ if debug:
133
+ logging.basicConfig(level=logging.DEBUG)
134
+
135
+ # Get directories to watch
136
+ root_dir = pathlib.Path(__file__).parent.parent.parent.resolve()
137
+ src_dir = root_dir / "src"
138
+ agents_dir = root_dir / "agents"
139
+
140
+ if not agents_dir.exists():
141
+ raise click.BadParameter(f"agents directory not found: {agents_dir}")
142
+
143
+ if not src_dir.exists():
144
+ raise click.BadParameter(f"Source directory not found: {src_dir}")
145
+
146
+ # Create output directory if it doesn't exist
147
+ if output_dir:
148
+ output_path = pathlib.Path(output_dir)
149
+ output_path.mkdir(parents=True, exist_ok=True)
150
+ console.print(f"Using output directory: {output_path}")
151
+
152
+ console.print(f"Watching agent: {agent}")
153
+ console.print(f"Deployment target: {deployment_target}")
154
+ console.print(f"Source directory: {src_dir}")
155
+ console.print(f"agents directory: {agents_dir}")
156
+ console.print(f"Project name: {project_name}")
157
+ console.print(f"Region: {region}")
158
+
159
+ event_handler = TemplateHandler(
160
+ agent_name=agent,
161
+ project_name=project_name,
162
+ deployment_target=deployment_target,
163
+ output_dir=output_dir,
164
+ region=region,
165
+ )
166
+
167
+ observer = Observer()
168
+ # Watch both src and agents directories
169
+ observer.schedule(event_handler, str(src_dir), recursive=True)
170
+ observer.schedule(event_handler, str(agents_dir), recursive=True)
171
+ observer.start()
172
+
173
+ try:
174
+ # Trigger initial build
175
+ console.print("\n🏗️ Performing initial build...", style="bold blue")
176
+ event_handler.rebuild_template()
177
+
178
+ console.print(
179
+ "\n🔍 Watching for changes (Press Ctrl+C to stop)...", style="bold blue"
180
+ )
181
+ while True:
182
+ time.sleep(1)
183
+ except KeyboardInterrupt:
184
+ console.print("\n⏹️ Stopping watch...", style="bold yellow")
185
+ observer.stop()
186
+ observer.join()
187
+
188
+
189
+ if __name__ == "__main__":
190
+ watch()