reasoning-deployment-service 0.2.8__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 reasoning-deployment-service might be problematic. Click here for more details.
- examples/programmatic_usage.py +154 -0
- reasoning_deployment_service/__init__.py +25 -0
- reasoning_deployment_service/cli_editor/__init__.py +5 -0
- reasoning_deployment_service/cli_editor/api_client.py +666 -0
- reasoning_deployment_service/cli_editor/cli_runner.py +343 -0
- reasoning_deployment_service/cli_editor/config.py +82 -0
- reasoning_deployment_service/cli_editor/google_deps.py +29 -0
- reasoning_deployment_service/cli_editor/reasoning_engine_creator.py +448 -0
- reasoning_deployment_service/gui_editor/__init__.py +5 -0
- reasoning_deployment_service/gui_editor/main.py +280 -0
- reasoning_deployment_service/gui_editor/requirements_minimal.txt +54 -0
- reasoning_deployment_service/gui_editor/run_program.sh +55 -0
- reasoning_deployment_service/gui_editor/src/__init__.py +1 -0
- reasoning_deployment_service/gui_editor/src/core/__init__.py +1 -0
- reasoning_deployment_service/gui_editor/src/core/api_client.py +647 -0
- reasoning_deployment_service/gui_editor/src/core/config.py +43 -0
- reasoning_deployment_service/gui_editor/src/core/google_deps.py +22 -0
- reasoning_deployment_service/gui_editor/src/core/reasoning_engine_creator.py +448 -0
- reasoning_deployment_service/gui_editor/src/ui/__init__.py +1 -0
- reasoning_deployment_service/gui_editor/src/ui/agent_space_view.py +312 -0
- reasoning_deployment_service/gui_editor/src/ui/authorization_view.py +280 -0
- reasoning_deployment_service/gui_editor/src/ui/reasoning_engine_view.py +354 -0
- reasoning_deployment_service/gui_editor/src/ui/reasoning_engines_view.py +204 -0
- reasoning_deployment_service/gui_editor/src/ui/ui_components.py +1221 -0
- reasoning_deployment_service/reasoning_deployment_service.py +687 -0
- reasoning_deployment_service-0.2.8.dist-info/METADATA +177 -0
- reasoning_deployment_service-0.2.8.dist-info/RECORD +29 -0
- reasoning_deployment_service-0.2.8.dist-info/WHEEL +5 -0
- reasoning_deployment_service-0.2.8.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Google Cloud dependencies check and imports."""
|
|
2
|
+
|
|
3
|
+
HAS_GOOGLE = True
|
|
4
|
+
try:
|
|
5
|
+
import requests
|
|
6
|
+
import google.auth
|
|
7
|
+
from google.auth.transport.requests import Request as GoogleAuthRequest
|
|
8
|
+
import vertexai
|
|
9
|
+
from vertexai.preview import reasoning_engines
|
|
10
|
+
from vertexai import agent_engines
|
|
11
|
+
except Exception:
|
|
12
|
+
HAS_GOOGLE = False
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"HAS_GOOGLE",
|
|
16
|
+
"requests",
|
|
17
|
+
"google",
|
|
18
|
+
"GoogleAuthRequest",
|
|
19
|
+
"vertexai",
|
|
20
|
+
"reasoning_engines",
|
|
21
|
+
"agent_engines"
|
|
22
|
+
]
|
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
"""Reasoning Engine creation with robust venv lifecycle and safe imports.
|
|
2
|
+
|
|
3
|
+
This module provides a `ReasoningEngineCreator` that:
|
|
4
|
+
- Creates an isolated virtual environment per engine build
|
|
5
|
+
- Installs dependencies using the *venv's* interpreter (`python -m pip`)
|
|
6
|
+
- Temporarily exposes the venv's site-packages to the current process for imports
|
|
7
|
+
- Emulates activation for subprocesses via PATH/VIRTUAL_ENV
|
|
8
|
+
- Stages a clean copy of the agent directory as an extra package
|
|
9
|
+
- Cleans up the venv after completion
|
|
10
|
+
|
|
11
|
+
Pass a config dict to `create_advanced_engine` with keys:
|
|
12
|
+
- display_name (str)
|
|
13
|
+
- description (str, optional)
|
|
14
|
+
- enable_tracing (bool, optional)
|
|
15
|
+
- requirements_source_type ("file" | "text")
|
|
16
|
+
- requirements_file (str, optional when source_type == "file")
|
|
17
|
+
- requirements_text (str, optional when source_type == "text")
|
|
18
|
+
- agent_file_path (str, path to the python file exporting `root_agent`)
|
|
19
|
+
- project_id, location, staging_bucket should be supplied to the constructor
|
|
20
|
+
"""
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import importlib
|
|
24
|
+
import importlib.util
|
|
25
|
+
import json
|
|
26
|
+
import os
|
|
27
|
+
import platform
|
|
28
|
+
import shutil
|
|
29
|
+
import subprocess
|
|
30
|
+
import sys
|
|
31
|
+
import tempfile
|
|
32
|
+
import venv
|
|
33
|
+
from datetime import datetime
|
|
34
|
+
from pathlib import Path
|
|
35
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
36
|
+
|
|
37
|
+
from vertexai import init as vertexai_init
|
|
38
|
+
from vertexai.preview.reasoning_engines import AdkApp
|
|
39
|
+
from vertexai import agent_engines
|
|
40
|
+
|
|
41
|
+
# --- helpers for clean packaging ---
|
|
42
|
+
EXCLUDES = [
|
|
43
|
+
".env",
|
|
44
|
+
".env.*",
|
|
45
|
+
".git",
|
|
46
|
+
"__pycache__",
|
|
47
|
+
".pytest_cache",
|
|
48
|
+
".mypy_cache",
|
|
49
|
+
".DS_Store",
|
|
50
|
+
"*.pyc",
|
|
51
|
+
"*.pyo",
|
|
52
|
+
"*.pyd",
|
|
53
|
+
".venv",
|
|
54
|
+
"venv",
|
|
55
|
+
"tests",
|
|
56
|
+
"docs",
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class ReasoningEngineCreator:
|
|
61
|
+
"""
|
|
62
|
+
Dedicated class for creating reasoning engines with advanced virtual environment management.
|
|
63
|
+
Handles all the complex logic for venv creation, dependency installation, and deployment.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
def __init__(self, project_id: str, location: str, staging_bucket: str, debug: bool = False):
|
|
67
|
+
self.project_id = project_id
|
|
68
|
+
self.location = location
|
|
69
|
+
self.staging_bucket = staging_bucket
|
|
70
|
+
self.debug = debug
|
|
71
|
+
|
|
72
|
+
# Ensure staging bucket has gs:// prefix
|
|
73
|
+
if not self.staging_bucket.startswith("gs://"):
|
|
74
|
+
self.staging_bucket = f"gs://{self.staging_bucket}"
|
|
75
|
+
|
|
76
|
+
# ---------------- Vertex init ----------------
|
|
77
|
+
def _ensure_vertex_inited(self) -> None:
|
|
78
|
+
"""Initialize Vertex AI once and reuse."""
|
|
79
|
+
if not getattr(self, "_vertex_inited", False):
|
|
80
|
+
vertexai_init(project=self.project_id, location=self.location, staging_bucket=self.staging_bucket)
|
|
81
|
+
self._vertex_inited = True
|
|
82
|
+
|
|
83
|
+
# ---------------- Virtual Environment Management ----------------
|
|
84
|
+
def _create_venv_name(self, engine_name: str) -> str:
|
|
85
|
+
"""Generate a unique virtual environment name with timestamp."""
|
|
86
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
87
|
+
# Clean engine name for filesystem safety
|
|
88
|
+
clean_name = "".join(c for c in engine_name if c.isalnum() or c in "_-").lower()
|
|
89
|
+
return f"venv_{timestamp}_{clean_name}"
|
|
90
|
+
|
|
91
|
+
def _deactivate_current_venv(self) -> bool:
|
|
92
|
+
"""Deactivate any currently active virtual environment."""
|
|
93
|
+
if not os.environ.get("VIRTUAL_ENV"):
|
|
94
|
+
if self.debug:
|
|
95
|
+
print("π No virtual environment currently active.")
|
|
96
|
+
return True
|
|
97
|
+
if self.debug:
|
|
98
|
+
print(f"π Deactivating current virtual environment: {os.environ.get('VIRTUAL_ENV')}")
|
|
99
|
+
# No-op for current process; we'll spawn new processes with the target venv
|
|
100
|
+
return True
|
|
101
|
+
|
|
102
|
+
def _create_and_activate_venv(self, venv_name: str, project_dir: str) -> Tuple[bool, str, str]:
|
|
103
|
+
"""
|
|
104
|
+
Create a new virtual environment.
|
|
105
|
+
Returns: (success, venv_path, python_executable)
|
|
106
|
+
"""
|
|
107
|
+
try:
|
|
108
|
+
venv_base = os.path.join(os.path.expanduser("~"), ".agent_venvs")
|
|
109
|
+
os.makedirs(venv_base, exist_ok=True)
|
|
110
|
+
venv_path = os.path.join(venv_base, venv_name)
|
|
111
|
+
|
|
112
|
+
print(f"π§ Creating virtual environment: {venv_path}")
|
|
113
|
+
venv.create(venv_path, with_pip=True, clear=True)
|
|
114
|
+
|
|
115
|
+
if platform.system() == "Windows":
|
|
116
|
+
python_exe = os.path.join(venv_path, "Scripts", "python.exe")
|
|
117
|
+
else:
|
|
118
|
+
python_exe = os.path.join(venv_path, "bin", "python")
|
|
119
|
+
|
|
120
|
+
if not os.path.exists(python_exe):
|
|
121
|
+
raise RuntimeError(f"Python executable not found at: {python_exe}")
|
|
122
|
+
|
|
123
|
+
print("β
Virtual environment created successfully")
|
|
124
|
+
print(f"π Python executable: {python_exe}")
|
|
125
|
+
return True, venv_path, python_exe
|
|
126
|
+
except Exception as e:
|
|
127
|
+
print(f"β Failed to create virtual environment: {e}")
|
|
128
|
+
return False, "", ""
|
|
129
|
+
|
|
130
|
+
def _install_requirements_in_venv(self, python_exe: str, requirements: List[str]) -> bool:
|
|
131
|
+
"""Install requirements in the specified virtual environment using interpreter-correct pip."""
|
|
132
|
+
if not requirements:
|
|
133
|
+
print("π No requirements to install.")
|
|
134
|
+
return True
|
|
135
|
+
try:
|
|
136
|
+
print(f"π¦ Installing {len(requirements)} requirements...")
|
|
137
|
+
for req in requirements:
|
|
138
|
+
print(f" π¦ Installing: {req}")
|
|
139
|
+
cmd = [python_exe, "-m", "pip", "install", req]
|
|
140
|
+
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
|
|
141
|
+
if result.returncode != 0:
|
|
142
|
+
print(f"β Failed to install {req}\nSTDOUT:\n{result.stdout}\nSTDERR:\n{result.stderr}")
|
|
143
|
+
return False
|
|
144
|
+
print("β
All requirements installed successfully!")
|
|
145
|
+
return True
|
|
146
|
+
except subprocess.TimeoutExpired:
|
|
147
|
+
print("β Package installation timed out")
|
|
148
|
+
return False
|
|
149
|
+
except Exception as e:
|
|
150
|
+
print(f"β Error installing requirements: {e}")
|
|
151
|
+
return False
|
|
152
|
+
|
|
153
|
+
def _cleanup_venv(self, venv_path: str) -> bool:
|
|
154
|
+
"""Remove the virtual environment directory."""
|
|
155
|
+
if not venv_path or not os.path.exists(venv_path):
|
|
156
|
+
if self.debug:
|
|
157
|
+
print("π Virtual environment path doesn't exist, nothing to clean up.")
|
|
158
|
+
return True
|
|
159
|
+
try:
|
|
160
|
+
print(f"π§Ή Cleaning up virtual environment: {venv_path}")
|
|
161
|
+
shutil.rmtree(venv_path)
|
|
162
|
+
print("β
Virtual environment cleaned up successfully!")
|
|
163
|
+
return True
|
|
164
|
+
except Exception as e:
|
|
165
|
+
print(f"β οΈ Warning: Failed to clean up virtual environment: {e}")
|
|
166
|
+
return False
|
|
167
|
+
|
|
168
|
+
def _add_venv_to_sys_path(self, python_exe: str) -> Optional[str]:
|
|
169
|
+
"""Add the virtual environment's site-packages to sys.path for imports."""
|
|
170
|
+
try:
|
|
171
|
+
code = "import sysconfig, json; print(json.dumps(sysconfig.get_paths()))"
|
|
172
|
+
result = subprocess.check_output([python_exe, "-c", code], text=True)
|
|
173
|
+
paths = json.loads(result.strip())
|
|
174
|
+
site_pkgs = paths.get("purelib") or paths.get("platlib")
|
|
175
|
+
if site_pkgs and site_pkgs not in sys.path:
|
|
176
|
+
print(f"π Adding venv site-packages to sys.path: {site_pkgs}")
|
|
177
|
+
sys.path.insert(0, site_pkgs)
|
|
178
|
+
return site_pkgs
|
|
179
|
+
return site_pkgs
|
|
180
|
+
except Exception as e:
|
|
181
|
+
print(f"β οΈ Warning: Could not add venv to sys.path: {e}")
|
|
182
|
+
return None
|
|
183
|
+
|
|
184
|
+
def _remove_venv_from_sys_path(self, site_pkgs_path: Optional[str]) -> None:
|
|
185
|
+
"""Remove the virtual environment's site-packages from sys.path."""
|
|
186
|
+
if site_pkgs_path and site_pkgs_path in sys.path:
|
|
187
|
+
sys.path.remove(site_pkgs_path)
|
|
188
|
+
print(f"π Removed venv site-packages from sys.path: {site_pkgs_path}")
|
|
189
|
+
|
|
190
|
+
def _push_venv_envvars(self, venv_path: str) -> None:
|
|
191
|
+
"""Temporarily emulate activation for subprocesses/tools."""
|
|
192
|
+
self._old_env = {
|
|
193
|
+
"PATH": os.environ.get("PATH", ""),
|
|
194
|
+
"VIRTUAL_ENV": os.environ.get("VIRTUAL_ENV"),
|
|
195
|
+
}
|
|
196
|
+
bin_dir = os.path.join(venv_path, "Scripts" if platform.system() == "Windows" else "bin")
|
|
197
|
+
os.environ["VIRTUAL_ENV"] = venv_path
|
|
198
|
+
os.environ["PATH"] = bin_dir + os.pathsep + self._old_env["PATH"]
|
|
199
|
+
|
|
200
|
+
def _pop_venv_envvars(self) -> None:
|
|
201
|
+
if hasattr(self, "_old_env"):
|
|
202
|
+
os.environ["PATH"] = self._old_env["PATH"]
|
|
203
|
+
if self._old_env["VIRTUAL_ENV"] is None:
|
|
204
|
+
os.environ.pop("VIRTUAL_ENV", None)
|
|
205
|
+
else:
|
|
206
|
+
os.environ["VIRTUAL_ENV"] = self._old_env["VIRTUAL_ENV"]
|
|
207
|
+
del self._old_env
|
|
208
|
+
|
|
209
|
+
# ---------------- Validation ----------------
|
|
210
|
+
def _assert_no_google_shadow(self, agent_dir: str) -> None:
|
|
211
|
+
"""Ensure no local package named 'google' shadows site-packages."""
|
|
212
|
+
local_google = os.path.join(agent_dir, "google")
|
|
213
|
+
if os.path.isdir(local_google) or os.path.isfile(local_google + ".py"):
|
|
214
|
+
raise RuntimeError(
|
|
215
|
+
f"Found local '{local_google}'. This will shadow 'google.adk'. "
|
|
216
|
+
"Rename/remove it or move agent code under a different package."
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
# ---------------- Agent Loading and Staging ----------------
|
|
220
|
+
def _stage_clean_copy(self, src_dir: str) -> str:
|
|
221
|
+
"""Copy agent directory to temp dir, excluding dev files and secrets."""
|
|
222
|
+
src = Path(src_dir).resolve()
|
|
223
|
+
dst_root = Path(tempfile.mkdtemp(prefix="agent_stage_"))
|
|
224
|
+
dst = dst_root / src.name
|
|
225
|
+
|
|
226
|
+
if self.debug:
|
|
227
|
+
print("π¦ Staging agent directory...")
|
|
228
|
+
print(f"π Source: {src}")
|
|
229
|
+
print(f"π Destination: {dst}")
|
|
230
|
+
print(f"π« Excluding: {EXCLUDES}")
|
|
231
|
+
if src.exists():
|
|
232
|
+
print("π Source contents:")
|
|
233
|
+
for item in sorted(src.iterdir()):
|
|
234
|
+
print(f" {'π' if item.is_dir() else 'π'} {item.name}{'/' if item.is_dir() else ''}")
|
|
235
|
+
|
|
236
|
+
shutil.copytree(src, dst, ignore=shutil.ignore_patterns(*EXCLUDES), dirs_exist_ok=True)
|
|
237
|
+
|
|
238
|
+
if self.debug:
|
|
239
|
+
print("π Staged contents:")
|
|
240
|
+
for item in sorted(dst.iterdir()):
|
|
241
|
+
print(f" {'π' if item.is_dir() else 'π'} {item.name}{'/' if item.is_dir() else ''}")
|
|
242
|
+
|
|
243
|
+
# Clean up .env files
|
|
244
|
+
for p in dst.rglob(".env*"):
|
|
245
|
+
try:
|
|
246
|
+
p.unlink()
|
|
247
|
+
if self.debug:
|
|
248
|
+
print(f"ποΈ Removed: {p}")
|
|
249
|
+
except Exception:
|
|
250
|
+
pass
|
|
251
|
+
|
|
252
|
+
if self.debug:
|
|
253
|
+
print(f"β
Staging complete: {dst}")
|
|
254
|
+
return str(dst)
|
|
255
|
+
|
|
256
|
+
def _load_agent_from_file(self, agent_file_path: str):
|
|
257
|
+
"""Load root_agent from a Python file, handling relative imports properly."""
|
|
258
|
+
agent_file = Path(agent_file_path).resolve()
|
|
259
|
+
if not agent_file.exists():
|
|
260
|
+
raise RuntimeError(f"Agent file not found: {agent_file}")
|
|
261
|
+
|
|
262
|
+
agent_dir = agent_file.parent
|
|
263
|
+
package_name = agent_dir.name
|
|
264
|
+
module_name = f"{package_name}.{agent_file.stem}"
|
|
265
|
+
|
|
266
|
+
parent_dir = str(agent_dir.parent)
|
|
267
|
+
agent_dir_str = str(agent_dir)
|
|
268
|
+
|
|
269
|
+
print(f"π€ Loading {agent_file.stem} from: {agent_file}")
|
|
270
|
+
print(f"π Agent directory: {agent_dir}")
|
|
271
|
+
print(f"π¦ Package name: {package_name}")
|
|
272
|
+
print(f"π§ Module name: {module_name}")
|
|
273
|
+
if self.debug:
|
|
274
|
+
print(f"π€οΈ Adding to sys.path: {parent_dir} (for package imports)")
|
|
275
|
+
print(f"π€οΈ Adding to sys.path: {agent_dir_str} (for absolute imports like 'tools')")
|
|
276
|
+
|
|
277
|
+
# Add both parent directory and agent directory
|
|
278
|
+
paths_added: List[str] = []
|
|
279
|
+
if parent_dir not in sys.path:
|
|
280
|
+
sys.path.insert(0, parent_dir)
|
|
281
|
+
paths_added.append(parent_dir)
|
|
282
|
+
if agent_dir_str not in sys.path:
|
|
283
|
+
sys.path.insert(0, agent_dir_str)
|
|
284
|
+
paths_added.append(agent_dir_str)
|
|
285
|
+
|
|
286
|
+
try:
|
|
287
|
+
# Optionally create a package for proper relative import resolution
|
|
288
|
+
init_py = agent_dir / "__init__.py"
|
|
289
|
+
package_spec = importlib.util.spec_from_file_location(package_name, init_py if init_py.exists() else None)
|
|
290
|
+
if package_spec:
|
|
291
|
+
package_module = importlib.util.module_from_spec(package_spec)
|
|
292
|
+
sys.modules[package_name] = package_module
|
|
293
|
+
if package_spec.loader and init_py.exists():
|
|
294
|
+
package_spec.loader.exec_module(package_module)
|
|
295
|
+
|
|
296
|
+
spec = importlib.util.spec_from_file_location(module_name, agent_file)
|
|
297
|
+
if spec is None or spec.loader is None:
|
|
298
|
+
raise RuntimeError(f"Could not load module spec from {agent_file}")
|
|
299
|
+
|
|
300
|
+
module = importlib.util.module_from_spec(spec)
|
|
301
|
+
module.__package__ = package_name # help relative imports
|
|
302
|
+
sys.modules[module_name] = module
|
|
303
|
+
spec.loader.exec_module(module)
|
|
304
|
+
|
|
305
|
+
if not hasattr(module, "root_agent"):
|
|
306
|
+
raise RuntimeError(f"Module '{agent_file}' does not define `root_agent`.")
|
|
307
|
+
print(f"β
Successfully loaded root_agent from {agent_file}")
|
|
308
|
+
return getattr(module, "root_agent")
|
|
309
|
+
except Exception as e:
|
|
310
|
+
print(f"β Failed to load agent: {e}")
|
|
311
|
+
raise RuntimeError(f"Failed to execute agent module {agent_file}: {e}") from e
|
|
312
|
+
finally:
|
|
313
|
+
for path in reversed(paths_added):
|
|
314
|
+
while path in sys.path:
|
|
315
|
+
sys.path.remove(path)
|
|
316
|
+
# clean any submodules from this package
|
|
317
|
+
mods_to_remove = [name for name in list(sys.modules.keys()) if name.startswith(package_name)]
|
|
318
|
+
for name in mods_to_remove:
|
|
319
|
+
sys.modules.pop(name, None)
|
|
320
|
+
|
|
321
|
+
# ---------------- Utilities ----------------
|
|
322
|
+
@staticmethod
|
|
323
|
+
def _merge_requirements(baseline: List[str], user: List[str]) -> List[str]:
|
|
324
|
+
seen = set()
|
|
325
|
+
out: List[str] = []
|
|
326
|
+
for seq in (baseline, user):
|
|
327
|
+
for item in seq:
|
|
328
|
+
key = item.strip().lower()
|
|
329
|
+
if not key or key in seen:
|
|
330
|
+
continue
|
|
331
|
+
seen.add(key)
|
|
332
|
+
out.append(item.strip())
|
|
333
|
+
return out
|
|
334
|
+
|
|
335
|
+
# ---------------- Main Creation Method ----------------
|
|
336
|
+
def create_advanced_engine(self, config: Dict[str, Any]) -> Tuple[str, str, Optional[str]]:
|
|
337
|
+
"""Create a reasoning engine with advanced configuration options."""
|
|
338
|
+
print("π Starting advanced reasoning engine creation...")
|
|
339
|
+
if self.debug:
|
|
340
|
+
print("π Configuration:")
|
|
341
|
+
try:
|
|
342
|
+
print(json.dumps(config, indent=2))
|
|
343
|
+
except Exception:
|
|
344
|
+
print(str(config))
|
|
345
|
+
|
|
346
|
+
try:
|
|
347
|
+
display_name = config["display_name"]
|
|
348
|
+
description = config.get("description", "")
|
|
349
|
+
enable_tracing = config.get("enable_tracing", True)
|
|
350
|
+
|
|
351
|
+
# Requirements
|
|
352
|
+
requirements: List[str] = []
|
|
353
|
+
if config["requirements_source_type"] == "file":
|
|
354
|
+
req_file = config.get("requirements_file")
|
|
355
|
+
if req_file and os.path.exists(req_file):
|
|
356
|
+
with open(req_file, "r", encoding="utf-8") as f:
|
|
357
|
+
requirements = [line.strip() for line in f if line.strip() and not line.strip().startswith("#")]
|
|
358
|
+
elif config["requirements_source_type"] == "text":
|
|
359
|
+
requirements_text = config.get("requirements_text", "").strip()
|
|
360
|
+
requirements = [line.strip() for line in requirements_text.splitlines() if line.strip() and not line.strip().startswith("#")]
|
|
361
|
+
|
|
362
|
+
# Ensure baseline ADK deps exist in the build venv (idempotent)
|
|
363
|
+
baseline = [
|
|
364
|
+
"google-adk>=1.0.0",
|
|
365
|
+
"google-cloud-aiplatform[agent_engines]>=1.93.0,<2.0.0",
|
|
366
|
+
"google-genai>=1.16.1,<2.0.0",
|
|
367
|
+
]
|
|
368
|
+
requirements = self._merge_requirements(baseline, requirements)
|
|
369
|
+
|
|
370
|
+
# Paths
|
|
371
|
+
agent_file_path = config["agent_file_path"]
|
|
372
|
+
agent_dir = os.path.dirname(agent_file_path)
|
|
373
|
+
|
|
374
|
+
# venv lifecycle
|
|
375
|
+
print("π Setting up isolated virtual environment...")
|
|
376
|
+
self._deactivate_current_venv()
|
|
377
|
+
venv_name = self._create_venv_name(display_name)
|
|
378
|
+
v_success, venv_path, python_exe = self._create_and_activate_venv(venv_name, agent_dir)
|
|
379
|
+
if not v_success:
|
|
380
|
+
raise RuntimeError("Failed to create virtual environment")
|
|
381
|
+
|
|
382
|
+
# installs
|
|
383
|
+
if requirements:
|
|
384
|
+
if not self._install_requirements_in_venv(python_exe, requirements):
|
|
385
|
+
self._cleanup_venv(venv_path)
|
|
386
|
+
raise RuntimeError("Failed to install requirements in virtual environment")
|
|
387
|
+
|
|
388
|
+
# make imports & subprocesses behave like activated
|
|
389
|
+
self._push_venv_envvars(venv_path)
|
|
390
|
+
venv_site_pkgs = self._add_venv_to_sys_path(python_exe)
|
|
391
|
+
|
|
392
|
+
try:
|
|
393
|
+
# quick guard against local google/ package
|
|
394
|
+
self._assert_no_google_shadow(agent_dir)
|
|
395
|
+
|
|
396
|
+
print("π Checking agent directory structure...")
|
|
397
|
+
agent_path = Path(agent_dir)
|
|
398
|
+
tools_path = agent_path / "tools"
|
|
399
|
+
if tools_path.exists() and self.debug:
|
|
400
|
+
tool_files = [p.name for p in tools_path.glob("*.py")]
|
|
401
|
+
print(f"β
Found tools directory with files: {tool_files}")
|
|
402
|
+
elif not tools_path.exists():
|
|
403
|
+
print(f"β WARNING: tools directory not found at {tools_path}")
|
|
404
|
+
|
|
405
|
+
staged_dir = self._stage_clean_copy(agent_dir)
|
|
406
|
+
staged_tools = Path(staged_dir) / "tools"
|
|
407
|
+
if not staged_tools.exists():
|
|
408
|
+
print("β ERROR: Tools directory missing from staged copy!")
|
|
409
|
+
raise RuntimeError("Tools directory was not properly staged")
|
|
410
|
+
|
|
411
|
+
# load agent
|
|
412
|
+
print(f"π€ Loading root_agent from: {agent_file_path}")
|
|
413
|
+
root_agent = self._load_agent_from_file(agent_file_path)
|
|
414
|
+
|
|
415
|
+
# vertex init + create
|
|
416
|
+
self._ensure_vertex_inited()
|
|
417
|
+
print("π Creating reasoning engine with venv dependenciesβ¦")
|
|
418
|
+
app = AdkApp(agent=root_agent, enable_tracing=enable_tracing)
|
|
419
|
+
remote = agent_engines.create(
|
|
420
|
+
app,
|
|
421
|
+
display_name=display_name,
|
|
422
|
+
description=description,
|
|
423
|
+
requirements=requirements,
|
|
424
|
+
extra_packages=[staged_dir],
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
print("β
Engine creation successful!")
|
|
428
|
+
return (
|
|
429
|
+
"created",
|
|
430
|
+
f"Advanced engine '{display_name}' created successfully",
|
|
431
|
+
remote.resource_name,
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
except Exception as e:
|
|
435
|
+
print(f"β Deployment failed: {e}")
|
|
436
|
+
raise
|
|
437
|
+
finally:
|
|
438
|
+
# undo import/env tweaks, then remove the venv
|
|
439
|
+
if venv_site_pkgs:
|
|
440
|
+
self._remove_venv_from_sys_path(venv_site_pkgs)
|
|
441
|
+
self._pop_venv_envvars()
|
|
442
|
+
print("π§Ή Cleaning up virtual environment...")
|
|
443
|
+
self._cleanup_venv(venv_path)
|
|
444
|
+
|
|
445
|
+
except Exception as e:
|
|
446
|
+
import traceback
|
|
447
|
+
traceback.print_exc()
|
|
448
|
+
return ("failed", f"Creation failed: {str(e)}", None)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""User interface components and views."""
|