reasoning-deployment-service 0.4.0__py3-none-any.whl → 0.8.3__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.
- reasoning_deployment_service/cli_editor/api_client.py +0 -14
- reasoning_deployment_service/gui_editor/src/ui/reasoning_engine_view.py +0 -34
- reasoning_deployment_service/reasoning_deployment_service.py +304 -158
- reasoning_deployment_service/runner.py +5 -3
- {reasoning_deployment_service-0.4.0.dist-info → reasoning_deployment_service-0.8.3.dist-info}/METADATA +1 -1
- {reasoning_deployment_service-0.4.0.dist-info → reasoning_deployment_service-0.8.3.dist-info}/RECORD +9 -10
- reasoning_deployment_service/cli_editor/reasoning_engine_creator.py +0 -448
- {reasoning_deployment_service-0.4.0.dist-info → reasoning_deployment_service-0.8.3.dist-info}/WHEEL +0 -0
- {reasoning_deployment_service-0.4.0.dist-info → reasoning_deployment_service-0.8.3.dist-info}/entry_points.txt +0 -0
- {reasoning_deployment_service-0.4.0.dist-info → reasoning_deployment_service-0.8.3.dist-info}/top_level.txt +0 -0
|
@@ -14,14 +14,11 @@ try:
|
|
|
14
14
|
HAS_GOOGLE, google, GoogleAuthRequest,
|
|
15
15
|
vertexai, agent_engines
|
|
16
16
|
)
|
|
17
|
-
from reasoning_engine_creator import ReasoningEngineCreator
|
|
18
17
|
except ImportError as e:
|
|
19
18
|
from .google_deps import (HAS_GOOGLE, google, GoogleAuthRequest, vertexai, agent_engines)
|
|
20
|
-
from .reasoning_engine_creator import ReasoningEngineCreator
|
|
21
19
|
|
|
22
20
|
BASE_URL = "https://discoveryengine.googleapis.com/v1alpha"
|
|
23
21
|
|
|
24
|
-
|
|
25
22
|
# --- helpers for clean packaging ---
|
|
26
23
|
EXCLUDES = [
|
|
27
24
|
".env", ".env.*", ".git", "__pycache__", ".pytest_cache", ".mypy_cache",
|
|
@@ -501,17 +498,6 @@ class ApiClient:
|
|
|
501
498
|
if name in sys.modules:
|
|
502
499
|
del sys.modules[name]
|
|
503
500
|
|
|
504
|
-
def create_reasoning_engine_advanced(self, config: Dict[str, Any]) -> Tuple[str, str, Optional[str]]:
|
|
505
|
-
"""Create a reasoning engine with advanced configuration options."""
|
|
506
|
-
creator = ReasoningEngineCreator(
|
|
507
|
-
project_id=self.project_id,
|
|
508
|
-
location=self.location,
|
|
509
|
-
staging_bucket=self.staging_bucket,
|
|
510
|
-
debug=self.debug,
|
|
511
|
-
)
|
|
512
|
-
|
|
513
|
-
return creator.create_advanced_engine(config)
|
|
514
|
-
|
|
515
501
|
def delete_reasoning_engine(self) -> Tuple[str, str]:
|
|
516
502
|
if not self._profile.get("name"):
|
|
517
503
|
return ("not_found", "No engine")
|
|
@@ -290,40 +290,6 @@ class ReasoningEngineView(ttk.Frame):
|
|
|
290
290
|
finally:
|
|
291
291
|
self.engines_menu.grab_release()
|
|
292
292
|
|
|
293
|
-
def _create_engine_advanced(self):
|
|
294
|
-
"""Create a new reasoning engine with advanced configuration."""
|
|
295
|
-
if not self.api.is_authenticated:
|
|
296
|
-
self.log("❌ Authentication required")
|
|
297
|
-
return
|
|
298
|
-
|
|
299
|
-
# Show advanced create engine dialog
|
|
300
|
-
dialog = CreateReasoningEngineAdvancedDialog(self.winfo_toplevel(), self.api)
|
|
301
|
-
self.wait_window(dialog)
|
|
302
|
-
|
|
303
|
-
if not dialog.result:
|
|
304
|
-
return # User cancelled
|
|
305
|
-
|
|
306
|
-
config = dialog.result
|
|
307
|
-
self.log(f"⚙️ Creating advanced reasoning engine '{config['display_name']}'...")
|
|
308
|
-
# self.create_advanced_btn.set_enabled(False, "Creating...")
|
|
309
|
-
|
|
310
|
-
def callback(res):
|
|
311
|
-
# self.create_advanced_btn.set_enabled(True)
|
|
312
|
-
if isinstance(res, Exception):
|
|
313
|
-
self.log(f"❌ {res}")
|
|
314
|
-
return
|
|
315
|
-
|
|
316
|
-
status, msg, resource = res
|
|
317
|
-
self.log(f"{status.upper()}: {msg}")
|
|
318
|
-
if resource:
|
|
319
|
-
self.log(f"resource: {resource}")
|
|
320
|
-
|
|
321
|
-
# Refresh engines list to show the new engine
|
|
322
|
-
self._refresh_engines()
|
|
323
|
-
self._update_button_states()
|
|
324
|
-
|
|
325
|
-
async_operation(lambda: self.api.create_reasoning_engine_advanced(config), callback=callback, ui_widget=self)
|
|
326
|
-
|
|
327
293
|
def update_api(self, api: ApiClient):
|
|
328
294
|
"""Update the API client reference."""
|
|
329
295
|
self.api = api
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import json, os, subprocess, yaml, sys
|
|
2
|
+
import uuid
|
|
2
3
|
import urllib.parse, vertexai, google.auth
|
|
3
4
|
import requests as _requests
|
|
4
5
|
from typing import Dict, Optional, Tuple
|
|
@@ -17,7 +18,6 @@ DISCOVERY_ENGINE_URL = "https://discoveryengine.googleapis.com/v1alpha"
|
|
|
17
18
|
|
|
18
19
|
class ReasoningEngineDeploymentService:
|
|
19
20
|
def __init__(self, root_agent: BaseAgent, deployment_environment: str="DEV"):
|
|
20
|
-
# Setup logging
|
|
21
21
|
self._setup_logging()
|
|
22
22
|
|
|
23
23
|
self._check_required_files_exist()
|
|
@@ -39,6 +39,7 @@ class ReasoningEngineDeploymentService:
|
|
|
39
39
|
|
|
40
40
|
self._load_agent_definition()
|
|
41
41
|
self._load_deployment_environment_variables(deployment_environment=deployment_environment)
|
|
42
|
+
self._load_runtime_variables()
|
|
42
43
|
self._check_requirements_file_present()
|
|
43
44
|
|
|
44
45
|
self._http = _requests.Session()
|
|
@@ -97,6 +98,37 @@ class ReasoningEngineDeploymentService:
|
|
|
97
98
|
def info(self, message: str):
|
|
98
99
|
self.logger.info(f"[DEPLOYMENT SERVICE: INFO]: {message}")
|
|
99
100
|
|
|
101
|
+
def _generate_authorization_id(self) -> Optional[str]:
|
|
102
|
+
get_deployment_environment = os.getenv('AGENT_DEPLOYMENT_PIPELINE_ID', "LOCAL_RUN")
|
|
103
|
+
|
|
104
|
+
if self._authorization_override:
|
|
105
|
+
return self._authorization_override.lower()
|
|
106
|
+
|
|
107
|
+
if not self._authorization_override:
|
|
108
|
+
return None
|
|
109
|
+
|
|
110
|
+
return f"{get_deployment_environment}-{self._reasoning_engine_name}-{str(uuid.uuid4())}-auth".lower()
|
|
111
|
+
|
|
112
|
+
def _load_runtime_variables(self):
|
|
113
|
+
load_dotenv(dotenv_path=".env", override=True)
|
|
114
|
+
runtime_vars = {}
|
|
115
|
+
|
|
116
|
+
for key in self._specific_dot_env_variables:
|
|
117
|
+
if key in os.environ:
|
|
118
|
+
runtime_vars[key] = os.environ[key]
|
|
119
|
+
|
|
120
|
+
runtime_vars.update(self._runtime_variable_definitions or {})
|
|
121
|
+
local_auth_id = self._generate_authorization_id()
|
|
122
|
+
|
|
123
|
+
if self._use_authorization and local_auth_id:
|
|
124
|
+
runtime_vars.update({'AUTHORIZATION_ID': local_auth_id})
|
|
125
|
+
self._authorization_id = local_auth_id
|
|
126
|
+
runtime_vars.update({f"DEPLOYED_PROJECT_NUMBER": self._project_number})
|
|
127
|
+
runtime_vars.update({f"DEPLOYED_PROJECT_ID": self._project_id})
|
|
128
|
+
runtime_vars.update({f"AGENT_DEPLOYMENT_PIPELINE_ID": self._deployed_environment})
|
|
129
|
+
|
|
130
|
+
self._environment_variables = runtime_vars
|
|
131
|
+
|
|
100
132
|
def _check_required_files_exist(self):
|
|
101
133
|
end_run = False
|
|
102
134
|
if not os.path.exists(".env.agent"):
|
|
@@ -185,37 +217,14 @@ class ReasoningEngineDeploymentService:
|
|
|
185
217
|
if path.exists() and not overwrite:
|
|
186
218
|
raise FileExistsError(f"{path} already exists. Pass overwrite=True to replace it.")
|
|
187
219
|
|
|
188
|
-
template = """
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
DEV_AGENT_SPACE_ENGINE=
|
|
195
|
-
DEV_API_TOKEN=
|
|
196
|
-
DEV_OAUTH_CLIENT_ID=
|
|
197
|
-
DEV_OAUTH_CLIENT_SECRET=
|
|
198
|
-
|
|
199
|
-
# Production Profile
|
|
200
|
-
PROD_PROJECT_ID=
|
|
201
|
-
PROD_PROJECT_NUMBER=
|
|
202
|
-
PROD_PROJECT_LOCATION=
|
|
203
|
-
PROD_STAGING_BUCKET=
|
|
204
|
-
PROD_AGENT_SPACE_ENGINE=
|
|
205
|
-
PROD_API_TOKEN=
|
|
206
|
-
PROD_OAUTH_CLIENT_ID=
|
|
207
|
-
PROD_OAUTH_CLIENT_SECRET=
|
|
208
|
-
#===================== **** DEPLOYMENT PROFILE **** =====================
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
#===================== **** YOUR APP ENV VARIABLES **** =====================
|
|
212
|
-
DEVELOPER=dev
|
|
213
|
-
#===================== **** YOUR APP ENV VARIABLES **** =====================
|
|
214
|
-
"""
|
|
220
|
+
template = """
|
|
221
|
+
DEV_PROJECT_ID=
|
|
222
|
+
DEV_PROJECT_NUMBER=
|
|
223
|
+
DEV_PROJECT_LOCATION=
|
|
224
|
+
DEV_OAUTH_CLIENT_ID=
|
|
225
|
+
DEV_OAUTH_CLIENT_SECRET="""
|
|
215
226
|
|
|
216
227
|
path.write_text(template.strip() + "\n")
|
|
217
|
-
|
|
218
|
-
# Also update .gitignore to include logs
|
|
219
228
|
self._update_gitignore()
|
|
220
229
|
|
|
221
230
|
return path
|
|
@@ -250,13 +259,6 @@ class ReasoningEngineDeploymentService:
|
|
|
250
259
|
def _generate_example_yaml_config(self, path: str | Path = "agent.yaml", overwrite: bool = False) -> Path:
|
|
251
260
|
"""
|
|
252
261
|
Create an example YAML config matching the requested schema.
|
|
253
|
-
|
|
254
|
-
Structure:
|
|
255
|
-
defaults:
|
|
256
|
-
scopes: [ ... ]
|
|
257
|
-
metadata: { ... }
|
|
258
|
-
auth: { ... }
|
|
259
|
-
environment_variables: [ ... ]
|
|
260
262
|
"""
|
|
261
263
|
path = Path(path)
|
|
262
264
|
if path.exists() and not overwrite:
|
|
@@ -264,23 +266,27 @@ class ReasoningEngineDeploymentService:
|
|
|
264
266
|
|
|
265
267
|
config = {
|
|
266
268
|
"defaults": {
|
|
267
|
-
"
|
|
268
|
-
"
|
|
269
|
-
"
|
|
270
|
-
|
|
271
|
-
"
|
|
272
|
-
"
|
|
273
|
-
"
|
|
274
|
-
"
|
|
275
|
-
"
|
|
276
|
-
"agent_space_tool_description": "Agent space tool description",
|
|
269
|
+
"reasoning_engine": {
|
|
270
|
+
"name": "reasoning-engine-dev",
|
|
271
|
+
"description": "A reasoning engine for development"
|
|
272
|
+
},
|
|
273
|
+
"gemini_enterprise": {
|
|
274
|
+
"target_deployment_engine_id": "your-gemini-enterprise-engine-id",
|
|
275
|
+
"name": "Agent Name Here",
|
|
276
|
+
"description": "Agent description here",
|
|
277
|
+
"tool_description": "Tool description here",
|
|
277
278
|
},
|
|
278
|
-
"
|
|
279
|
-
"
|
|
279
|
+
"authorization": {
|
|
280
|
+
"enabled": True,
|
|
281
|
+
"scopes": [
|
|
282
|
+
"https://www.googleapis.com/auth/cloud-platform",
|
|
283
|
+
"https://www.googleapis.com/auth/userinfo.email"
|
|
284
|
+
]
|
|
280
285
|
},
|
|
281
|
-
"
|
|
282
|
-
|
|
283
|
-
|
|
286
|
+
"import_from_dot_env_by_name": ["TEST_ENV_VAR"],
|
|
287
|
+
"runtime_variable_definitions":{
|
|
288
|
+
"EXAMPLE_VAR": "An example environment variable for the agent runtime"
|
|
289
|
+
}
|
|
284
290
|
}
|
|
285
291
|
}
|
|
286
292
|
|
|
@@ -298,31 +304,40 @@ class ReasoningEngineDeploymentService:
|
|
|
298
304
|
|
|
299
305
|
try:
|
|
300
306
|
config = config['defaults']
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
307
|
+
authorization = config['authorization']
|
|
308
|
+
gemini_enterprise = config['gemini_enterprise']
|
|
309
|
+
reasoning_engine = config['reasoning_engine']
|
|
310
|
+
self._specific_dot_env_variables = config.get('import_from_dot_env_by_name', [])
|
|
311
|
+
self._runtime_variable_definitions = config.get('runtime_variable_definitions', {})
|
|
312
|
+
|
|
313
|
+
reasoning_engine_name = reasoning_engine.get('name')
|
|
314
|
+
reasoning_engine_description = reasoning_engine.get('description')
|
|
315
|
+
reasoning_engine_bypass = reasoning_engine.get('skip_build', False)
|
|
316
|
+
|
|
317
|
+
gemini_enterprise_name = gemini_enterprise.get('name')
|
|
318
|
+
gemini_enterprise_description = gemini_enterprise.get('description')
|
|
319
|
+
gemini_enterprise_tool_description = gemini_enterprise.get('tool_description')
|
|
320
|
+
gemini_enterprise_engine_id = gemini_enterprise.get('target_deployment_engine_id')
|
|
321
|
+
gemini_enterprise_icon_uri = gemini_enterprise.get('icon_uri')
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
self._reasoning_engine_bypass = reasoning_engine_bypass
|
|
325
|
+
self._icon_uri = gemini_enterprise_icon_uri
|
|
326
|
+
self._agent_space_engine = gemini_enterprise_engine_id or os.getenv(f"{self.deployment_env}_AGENT_SPACE_ENGINE")
|
|
327
|
+
self._required_scopes = authorization.get('scopes', [])
|
|
313
328
|
self._agent_folder = "agent"
|
|
314
329
|
self._reasoning_engine_name = reasoning_engine_name
|
|
315
330
|
self._reasoning_engine_description = reasoning_engine_description
|
|
316
|
-
self._agent_space_name =
|
|
317
|
-
self._agent_space_description =
|
|
318
|
-
self._agent_space_tool_description =
|
|
319
|
-
self.
|
|
320
|
-
self.
|
|
331
|
+
self._agent_space_name = gemini_enterprise_name
|
|
332
|
+
self._agent_space_description = gemini_enterprise_description
|
|
333
|
+
self._agent_space_tool_description = gemini_enterprise_tool_description
|
|
334
|
+
self._use_authorization = authorization.get('enabled', False)
|
|
335
|
+
self._authorization_override = authorization.get('authorization_id_override', None)
|
|
321
336
|
except KeyError as e:
|
|
322
|
-
raise RuntimeError(f"Missing required key in agent.yaml: {e}")
|
|
337
|
+
raise RuntimeError(f"Missing required key in agent.yaml: {e}. Your agent.yaml file is not valid for this deployment service version.")
|
|
323
338
|
|
|
324
339
|
def _load_deployment_environment_variables(self, deployment_environment: str):
|
|
325
|
-
required_vars = ['PROJECT_ID', 'PROJECT_NUMBER', 'PROJECT_LOCATION', 'STAGING_BUCKET'
|
|
340
|
+
required_vars = ['PROJECT_ID', 'PROJECT_NUMBER', 'PROJECT_LOCATION', 'STAGING_BUCKET']
|
|
326
341
|
|
|
327
342
|
for var in required_vars:
|
|
328
343
|
env_var = f"{deployment_environment}_{var}"
|
|
@@ -331,7 +346,10 @@ class ReasoningEngineDeploymentService:
|
|
|
331
346
|
|
|
332
347
|
setattr(self, f"_{var.lower()}", os.getenv(env_var))
|
|
333
348
|
|
|
334
|
-
if self.
|
|
349
|
+
if not self._agent_space_engine:
|
|
350
|
+
raise RuntimeError(f"Missing AGENT_SPACE_ENGINE for deployment environment {deployment_environment}.")
|
|
351
|
+
|
|
352
|
+
if self._use_authorization:
|
|
335
353
|
required_auth_vars = ['OAUTH_CLIENT_ID', 'OAUTH_CLIENT_SECRET']
|
|
336
354
|
|
|
337
355
|
for var in required_auth_vars:
|
|
@@ -341,6 +359,8 @@ class ReasoningEngineDeploymentService:
|
|
|
341
359
|
|
|
342
360
|
setattr(self, f"_{var.lower()}", os.getenv(env_var))
|
|
343
361
|
|
|
362
|
+
self._deployed_environment = os.getenv(f"AGENT_DEPLOYMENT_PIPELINE_ID", "unregistered_environment")
|
|
363
|
+
|
|
344
364
|
def _check_requirements_file_present(self):
|
|
345
365
|
if not os.path.exists("requirements.txt"):
|
|
346
366
|
raise RuntimeError("Missing requirements.txt file")
|
|
@@ -417,7 +437,10 @@ class ReasoningEngineDeploymentService:
|
|
|
417
437
|
},
|
|
418
438
|
}
|
|
419
439
|
|
|
420
|
-
if self.
|
|
440
|
+
if self._icon_uri:
|
|
441
|
+
payload["icon"] = {"uri": self._icon_uri}
|
|
442
|
+
|
|
443
|
+
if self._use_authorization and self._authorization_id:
|
|
421
444
|
payload["adk_agent_definition"]["authorizations"] = [
|
|
422
445
|
f"projects/{self._project_number}/locations/global/authorizations/{self._authorization_id}"
|
|
423
446
|
]
|
|
@@ -520,7 +543,7 @@ class ReasoningEngineDeploymentService:
|
|
|
520
543
|
def _create_authorization(self) -> dict:
|
|
521
544
|
read_authorizations = self._read_engine_deployment_record()
|
|
522
545
|
|
|
523
|
-
if not self._authorization_id:
|
|
546
|
+
if not self._authorization_id or not self._use_authorization:
|
|
524
547
|
self.warning("No authorization ID provided; skipping authorization creation.")
|
|
525
548
|
|
|
526
549
|
return
|
|
@@ -603,8 +626,10 @@ class ReasoningEngineDeploymentService:
|
|
|
603
626
|
|
|
604
627
|
return True
|
|
605
628
|
|
|
606
|
-
def _delete_authorization(self):
|
|
607
|
-
|
|
629
|
+
def _delete_authorization(self, drop_authorization_for_refresh: Optional[str] = None):
|
|
630
|
+
auth_to_drop = drop_authorization_for_refresh or self._authorization_id
|
|
631
|
+
|
|
632
|
+
if not auth_to_drop:
|
|
608
633
|
self.warning("No authorization ID provided; skipping deletion.")
|
|
609
634
|
return
|
|
610
635
|
|
|
@@ -617,18 +642,21 @@ class ReasoningEngineDeploymentService:
|
|
|
617
642
|
|
|
618
643
|
url = (
|
|
619
644
|
f"{discovery_engine_url}/projects/{self._project_id}/locations/global/authorizations"
|
|
620
|
-
f"?authorizationId={
|
|
645
|
+
f"?authorizationId={auth_to_drop}"
|
|
621
646
|
)
|
|
622
647
|
|
|
623
648
|
r = self._http.delete(url, headers=headers, timeout=60)
|
|
624
649
|
|
|
625
650
|
if r.status_code < 400:
|
|
651
|
+
if drop_authorization_for_refresh:
|
|
652
|
+
self.info(f"Authorization {drop_authorization_for_refresh} deleted successfully for refresh.")
|
|
653
|
+
return True
|
|
654
|
+
|
|
626
655
|
self.info("Authorization deleted successfully.")
|
|
627
656
|
self._authorization_id = None
|
|
628
657
|
self._update_in_agent_space()
|
|
629
658
|
return True
|
|
630
659
|
|
|
631
|
-
# Log API failure details to file only
|
|
632
660
|
with open(self.log_filename, 'a') as f:
|
|
633
661
|
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S,%f')[:-3]
|
|
634
662
|
f.write(f"{timestamp} - ReasoningEngineDeployment - ERROR - Failed to delete authorization with status {r.status_code} {r.reason}\n")
|
|
@@ -641,9 +669,8 @@ class ReasoningEngineDeploymentService:
|
|
|
641
669
|
except:
|
|
642
670
|
pass
|
|
643
671
|
|
|
644
|
-
|
|
672
|
+
|
|
645
673
|
self.error("Failed to delete authorization")
|
|
646
|
-
# This will also log the record file
|
|
647
674
|
return False
|
|
648
675
|
|
|
649
676
|
def one_deployment_with_everything_on_it(self, skip_engine_step=False):
|
|
@@ -721,7 +748,14 @@ class ReasoningEngineDeploymentService:
|
|
|
721
748
|
if r.status_code == 404:
|
|
722
749
|
return None
|
|
723
750
|
|
|
724
|
-
r.
|
|
751
|
+
self.info(r.json())
|
|
752
|
+
self.info(r.text)
|
|
753
|
+
|
|
754
|
+
if r.status_code == 403:
|
|
755
|
+
self.warning("Access denied")
|
|
756
|
+
return None
|
|
757
|
+
else:
|
|
758
|
+
r.raise_for_status()
|
|
725
759
|
|
|
726
760
|
return r.json().get("name", name)
|
|
727
761
|
|
|
@@ -766,9 +800,121 @@ class ReasoningEngineDeploymentService:
|
|
|
766
800
|
)
|
|
767
801
|
|
|
768
802
|
return matches[0]
|
|
803
|
+
|
|
804
|
+
def patch_agent_space_metadata_and_auth(
|
|
805
|
+
self,
|
|
806
|
+
agent_id: str,
|
|
807
|
+
new_display_name: Optional[str] = None,
|
|
808
|
+
new_description: Optional[str] = None,
|
|
809
|
+
new_reasoning_engine: Optional[str] = None,
|
|
810
|
+
new_authorizations: Optional[list[str]] = None,
|
|
811
|
+
icon_uri: Optional[str] = None,
|
|
812
|
+
) -> dict:
|
|
813
|
+
"""
|
|
814
|
+
Safely patch metadata (displayName, description) and linkage fields
|
|
815
|
+
(reasoningEngine, authorizations) for an existing Agent Space agent.
|
|
816
|
+
Preserves all other required adkAgentDefinition fields.
|
|
817
|
+
"""
|
|
818
|
+
url = (
|
|
819
|
+
f"{DISCOVERY_ENGINE_URL}/projects/{self._project_id}/locations/global/"
|
|
820
|
+
f"collections/default_collection/engines/{self._agent_space_engine}/"
|
|
821
|
+
f"assistants/default_assistant/agents/{agent_id}"
|
|
822
|
+
)
|
|
769
823
|
|
|
824
|
+
agent_updates_body = {
|
|
825
|
+
"displayName": new_display_name,
|
|
826
|
+
"description": new_description,
|
|
827
|
+
"adk_agent_definition": {
|
|
828
|
+
"tool_settings": {
|
|
829
|
+
"tool_description": new_description
|
|
830
|
+
},
|
|
831
|
+
"provisioned_reasoning_engine":{
|
|
832
|
+
"reasoning_engine": new_reasoning_engine
|
|
833
|
+
},
|
|
834
|
+
"authorizations": new_authorizations
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
if icon_uri:
|
|
839
|
+
agent_updates_body["icon"] = {"uri": icon_uri}
|
|
840
|
+
|
|
841
|
+
self.info(agent_updates_body)
|
|
770
842
|
|
|
771
|
-
|
|
843
|
+
headers = self._get_headers()
|
|
844
|
+
|
|
845
|
+
update_mask = ["displayName", "description", "adk_agent_definition.tool_settings.tool_description",
|
|
846
|
+
"adk_agent_definition.provisioned_reasoning_engine.reasoning_engine", "adk_agent_definition.authorizations", 'icon.uri']
|
|
847
|
+
params = {"update_mask": ",".join(update_mask)}
|
|
848
|
+
resp = self._http.patch(url, headers=headers, params=params, json=agent_updates_body, timeout=60)
|
|
849
|
+
|
|
850
|
+
return resp.json()
|
|
851
|
+
|
|
852
|
+
|
|
853
|
+
def one_githhub_deployment_to_go_with_skip(self):
|
|
854
|
+
return self.one_github_deployment_to_go(skip_engine=True)
|
|
855
|
+
|
|
856
|
+
def update_authorization_scopes(self, auth_id: str, scopes: list, oauth_client_id: str) -> dict:
|
|
857
|
+
"""Patch the scopes for a specific authorization by ID, updating the authorizationUri as well."""
|
|
858
|
+
scopes_str = " ".join(scopes)
|
|
859
|
+
authorization_uri = (
|
|
860
|
+
"https://accounts.google.com/o/oauth2/auth"
|
|
861
|
+
"?response_type=code"
|
|
862
|
+
f"&client_id={oauth_client_id}"
|
|
863
|
+
f"&scope={scopes_str}"
|
|
864
|
+
"&access_type=offline&prompt=consent"
|
|
865
|
+
)
|
|
866
|
+
payload = {
|
|
867
|
+
"serverSideOauth2": {
|
|
868
|
+
"authorizationUri": authorization_uri
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
url = f"{DISCOVERY_ENGINE_URL}/projects/{self._project_id}/locations/global/authorizations/{auth_id}?update_mask=server_side_oauth2.authorization_uri"
|
|
872
|
+
headers = {
|
|
873
|
+
"Authorization": f"Bearer {self._access_token()}",
|
|
874
|
+
"Content-Type": "application/json",
|
|
875
|
+
"X-Goog-User-Project": self._project_number,
|
|
876
|
+
}
|
|
877
|
+
r = self._http.patch(url, headers=headers, json=payload, timeout=60)
|
|
878
|
+
r.raise_for_status()
|
|
879
|
+
return r.json()
|
|
880
|
+
|
|
881
|
+
def detect_scope_change(self, auth_full_name, want_scopes) -> Optional[bool]:
|
|
882
|
+
auth_url = f"{DISCOVERY_ENGINE_URL}/{auth_full_name}"
|
|
883
|
+
hdrs = self._get_headers().copy()
|
|
884
|
+
|
|
885
|
+
if "Authorization" in hdrs:
|
|
886
|
+
hdrs["Authorization"] = "Bearer ***"
|
|
887
|
+
|
|
888
|
+
self.info(f"[AUTH] GET {auth_url} headers={json.dumps(hdrs)}")
|
|
889
|
+
r = self._http.get(auth_url, headers=self._get_headers(), timeout=60)
|
|
890
|
+
self.info(f"[AUTH] GET status={r.status_code} ct={r.headers.get('content-type','')}")
|
|
891
|
+
|
|
892
|
+
try:
|
|
893
|
+
self.info(f"[AUTH] GET body={json.dumps(r.json(), indent=2)[:4000]}")
|
|
894
|
+
except Exception:
|
|
895
|
+
self.info(f"[AUTH] GET text={(r.text or '')[:1000]}")
|
|
896
|
+
r.raise_for_status()
|
|
897
|
+
|
|
898
|
+
data = r.json() or {}
|
|
899
|
+
existing_uri = (((data.get("serverSideOauth2") or {}).get("authorizationUri")) or "")
|
|
900
|
+
self.info(f"[AUTH] existing authorizationUri={existing_uri!r}")
|
|
901
|
+
existing_scopes = set()
|
|
902
|
+
|
|
903
|
+
if existing_uri:
|
|
904
|
+
parsed = urlparse(existing_uri)
|
|
905
|
+
qs = parse_qs(parsed.query)
|
|
906
|
+
scope_str = (qs.get("scope", [""])[0] or "")
|
|
907
|
+
existing_scopes = set(scope_str.split())
|
|
908
|
+
|
|
909
|
+
self.info(
|
|
910
|
+
f"[AUTH] scopes existing={sorted(existing_scopes)} want={sorted(want_scopes)} "
|
|
911
|
+
f"missing={sorted(want_scopes - existing_scopes)} extra={sorted(existing_scopes - want_scopes)}"
|
|
912
|
+
)
|
|
913
|
+
|
|
914
|
+
if existing_scopes != want_scopes:
|
|
915
|
+
return True
|
|
916
|
+
|
|
917
|
+
def one_github_deployment_to_go(self, skip_engine=False):
|
|
772
918
|
"""
|
|
773
919
|
CI-friendly deploy:
|
|
774
920
|
- Engine: create or update by display_name.
|
|
@@ -776,110 +922,110 @@ class ReasoningEngineDeploymentService:
|
|
|
776
922
|
- Agent Space: create if missing; patch if found (by displayName under engine).
|
|
777
923
|
"""
|
|
778
924
|
self.info("Starting GitHub deployment...")
|
|
925
|
+
self.info(
|
|
926
|
+
f"[CFG] project_id={self._project_id} project_number={self._project_number} "
|
|
927
|
+
f"location={self._project_location} engine_name={self._reasoning_engine_name} "
|
|
928
|
+
f"agent_space_engine={self._agent_space_engine} auth_id={self._authorization_id} "
|
|
929
|
+
f"scopes={self._required_scopes} staging_bucket={self._staging_bucket}"
|
|
930
|
+
)
|
|
779
931
|
|
|
780
|
-
# Ensure Vertex SDK calls have context for list/update
|
|
781
932
|
self._cicd_deploy = True
|
|
933
|
+
delete_old_authorization = False
|
|
934
|
+
self.info(f"[INIT] vertexai.init(project={self._project_id}, location={self._project_location}, staging_bucket={self._staging_bucket})")
|
|
782
935
|
vertexai.init(
|
|
783
936
|
project=self._project_id,
|
|
784
937
|
location=self._project_location,
|
|
785
938
|
staging_bucket=self._staging_bucket,
|
|
786
939
|
)
|
|
787
940
|
|
|
788
|
-
|
|
789
|
-
# 1) Reasoning Engine (create or update)
|
|
790
|
-
# -----------------------------
|
|
941
|
+
self.info(f"[ENGINE] Resolving by display_name={self._reasoning_engine_name}")
|
|
791
942
|
engine_rn = self.find_engine_by_name(self._reasoning_engine_name)
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
# read back the created id (or re-resolve by name as fallback)
|
|
796
|
-
engine_rn = self._read_engine_deployment_record().get("reasoning_engine_id") or \
|
|
797
|
-
self.find_engine_by_name(self._reasoning_engine_name)
|
|
943
|
+
self.info(f"[ENGINE] find_engine_by_name -> {engine_rn}")
|
|
944
|
+
|
|
945
|
+
if not skip_engine and not self._reasoning_engine_bypass:
|
|
798
946
|
if not engine_rn:
|
|
799
|
-
self.
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
947
|
+
self.info(f"[ENGINE] '{self._reasoning_engine_name}' not found. Creating...")
|
|
948
|
+
self.create_reasoning_engine()
|
|
949
|
+
rec_after_create = self._read_engine_deployment_record()
|
|
950
|
+
self.info(f"[ENGINE] record after create -> {json.dumps(rec_after_create, indent=2)}")
|
|
951
|
+
engine_rn = rec_after_create.get("reasoning_engine_id") or self.find_engine_by_name(self._reasoning_engine_name)
|
|
952
|
+
self.info(f"[ENGINE] post-create resolution -> {engine_rn}")
|
|
953
|
+
if not engine_rn:
|
|
954
|
+
self.error("[ENGINE] Creation did not yield a resource name.")
|
|
955
|
+
raise RuntimeError("Engine creation failed.")
|
|
956
|
+
else:
|
|
957
|
+
self.info(f"[ENGINE] '{self._reasoning_engine_name}' exists. Updating...")
|
|
958
|
+
self.update_reasoning_engine(engine_rn)
|
|
804
959
|
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
960
|
+
self.info(f"[ENGINE] final engine_rn={engine_rn}")
|
|
961
|
+
|
|
962
|
+
if not engine_rn:
|
|
963
|
+
self.error("[ENGINE] Reasoning engine required for Agent Space deployment.")
|
|
964
|
+
raise RuntimeError("Reasoning engine resolution failed.")
|
|
965
|
+
|
|
966
|
+
auth_full_name = None
|
|
967
|
+
if self._authorization_id and self._use_authorization:
|
|
809
968
|
want_scopes = set(self._required_scopes or [])
|
|
969
|
+
self.info(f"[AUTH] id={self._authorization_id} want_scopes={sorted(want_scopes)}")
|
|
810
970
|
auth_full_name = self.find_authorization_by_id(self._authorization_id)
|
|
971
|
+
self.info(f"[AUTH] find_authorization_by_id -> {auth_full_name}")
|
|
972
|
+
|
|
973
|
+
if auth_full_name and self.detect_scope_change(auth_full_name, want_scopes):
|
|
974
|
+
self.info(f"[AUTH] Scopes changed; patching authorization {self._authorization_id}...")
|
|
975
|
+
delete_old_authorization = auth_full_name
|
|
976
|
+
self._authorization_id = self._generate_authorization_id()
|
|
977
|
+
auth_full_name = None
|
|
811
978
|
|
|
812
979
|
if not auth_full_name:
|
|
813
|
-
self.info(f"
|
|
980
|
+
self.info(f"[AUTH] '{self._authorization_id}' not found. Creating...")
|
|
814
981
|
ok = self._create_authorization()
|
|
815
|
-
|
|
816
|
-
self.error("Authorization creation failed.")
|
|
817
|
-
raise RuntimeError("Authorization creation failed.")
|
|
982
|
+
self.info(f"[AUTH] _create_authorization -> {ok}")
|
|
818
983
|
auth_full_name = self.find_authorization_by_id(self._authorization_id)
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
headers=self._get_headers(), timeout=60)
|
|
826
|
-
r.raise_for_status()
|
|
827
|
-
data = r.json() or {}
|
|
828
|
-
existing_uri = (((data.get("serverSideOauth2") or {}).get("authorizationUri")) or "")
|
|
829
|
-
existing_scopes = set()
|
|
830
|
-
if existing_uri:
|
|
831
|
-
parsed = urlparse(existing_uri)
|
|
832
|
-
qs = parse_qs(parsed.query)
|
|
833
|
-
scope_str = (qs.get("scope", [""])[0] or "")
|
|
834
|
-
existing_scopes = set(scope_str.split())
|
|
835
|
-
|
|
836
|
-
if existing_scopes != want_scopes:
|
|
837
|
-
self.info("Authorization scopes changed. Patching authorization...")
|
|
838
|
-
new_auth_uri = self._build_authorization_uri(self._oauth_client_id, list(want_scopes))
|
|
839
|
-
patch_payload = {
|
|
840
|
-
"serverSideOauth2": {
|
|
841
|
-
"clientId": self._oauth_client_id,
|
|
842
|
-
"clientSecret": self._oauth_client_secret,
|
|
843
|
-
"authorizationUri": new_auth_uri,
|
|
844
|
-
"tokenUri": "https://oauth2.googleapis.com/token",
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
pr = self._http.patch(f"{DISCOVERY_ENGINE_URL}/{auth_full_name}",
|
|
848
|
-
headers=self._get_headers(), json=patch_payload, timeout=60)
|
|
849
|
-
pr.raise_for_status()
|
|
850
|
-
self.info("Authorization updated.")
|
|
851
|
-
else:
|
|
852
|
-
self.info("Authorization scopes unchanged; no update needed.")
|
|
984
|
+
self.info(f"[AUTH] post-create resolve -> {auth_full_name}")
|
|
985
|
+
|
|
986
|
+
if not ok or not auth_full_name:
|
|
987
|
+
self.error("[AUTH] Creation failed or did not resolve.")
|
|
988
|
+
|
|
989
|
+
raise RuntimeError("Authorization creation failed.")
|
|
853
990
|
else:
|
|
854
|
-
self.info("No authorization_id configured; skipping authorization step.")
|
|
991
|
+
self.info("[AUTH] No authorization_id configured; skipping authorization step.")
|
|
855
992
|
|
|
856
|
-
|
|
857
|
-
# 3) Agent Space Agent (create or update by displayName under engine)
|
|
858
|
-
# -----------------------------
|
|
993
|
+
self.info(f"[AGENT] Resolving by display_name={self._agent_space_name}")
|
|
859
994
|
existing_agent = self.find_agent_space_agents_by_display(self._agent_space_name)
|
|
860
|
-
|
|
995
|
+
self.info(f"[AGENT] find_agent_space_agents_by_display -> {json.dumps(existing_agent, indent=2)}")
|
|
861
996
|
|
|
862
997
|
if not existing_agent:
|
|
863
|
-
|
|
998
|
+
headers, payload = self._get_agent_space_payload(engine_rn)
|
|
864
999
|
create_url = self._get_agent_space_agent_url_new()
|
|
1000
|
+
self.info(f"[AGENT] POST {create_url}")
|
|
865
1001
|
cr = self._http.post(create_url, headers=headers, json=payload, timeout=90)
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
1002
|
+
self.info(f"[AGENT] POST status={cr.status_code}")
|
|
1003
|
+
self.info(f"[AGENT] POST ct={cr.headers.get('content-type','')}")
|
|
1004
|
+
self.info(f"[AGENT] POST body={(cr.text or '')[:4000]}")
|
|
1005
|
+
cr.raise_for_status()
|
|
870
1006
|
agent_name = (cr.json() or {}).get("name")
|
|
871
1007
|
if agent_name:
|
|
872
1008
|
self._write_engine_deployment({"agent_space_id": agent_name})
|
|
873
|
-
self.info(f"
|
|
1009
|
+
self.info(f"[AGENT] Created: {agent_name}")
|
|
874
1010
|
else:
|
|
875
|
-
self.warning("
|
|
1011
|
+
self.warning("[AGENT] Created but response missing name. Verify in console.")
|
|
876
1012
|
else:
|
|
877
|
-
self.info(f"
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
self.
|
|
882
|
-
|
|
883
|
-
|
|
1013
|
+
self.info(f"[AGENT] '{self._agent_space_name}' exists. Patching metadata and auth only...")
|
|
1014
|
+
patched = self.patch_agent_space_metadata_and_auth(
|
|
1015
|
+
agent_id=existing_agent["id"],
|
|
1016
|
+
new_display_name=self._agent_space_name,
|
|
1017
|
+
new_description=self._agent_space_description,
|
|
1018
|
+
new_reasoning_engine=engine_rn,
|
|
1019
|
+
new_authorizations=[
|
|
1020
|
+
f"projects/{self._project_number}/locations/global/authorizations/{self._authorization_id}"
|
|
1021
|
+
] if self._authorization_id else None,
|
|
1022
|
+
icon_uri=self._icon_uri,
|
|
1023
|
+
)
|
|
1024
|
+
self.info(f"[AGENT] PATCH result -> {json.dumps(patched, indent=2)[:2000]}")
|
|
1025
|
+
|
|
1026
|
+
|
|
1027
|
+
if delete_old_authorization:
|
|
1028
|
+
self.info(f"[AUTH] Deleting old authorization {delete_old_authorization}...")
|
|
1029
|
+
self._delete_authorization(delete_old_authorization)
|
|
884
1030
|
|
|
885
1031
|
self.info("GitHub deployment completed successfully.")
|
|
@@ -13,7 +13,7 @@ class Runner:
|
|
|
13
13
|
parser = argparse.ArgumentParser(description="Reasoning Engine Deployment Runner")
|
|
14
14
|
parser.add_argument(
|
|
15
15
|
"--mode",
|
|
16
|
-
choices=["create", "auth", "cli", "gui", "populate_files", "github_deployment"],
|
|
16
|
+
choices=["create", "auth", "cli", "gui", "populate_files", "github_deployment", "github_deployment_without_engine"],
|
|
17
17
|
help="Operation mode to run",
|
|
18
18
|
)
|
|
19
19
|
parser.add_argument(
|
|
@@ -105,7 +105,7 @@ class Runner:
|
|
|
105
105
|
@staticmethod
|
|
106
106
|
def _check_proper_configuration():
|
|
107
107
|
"""Ensure required environment variables are set in .env.agent."""
|
|
108
|
-
required_vars = ['PROJECT_ID', 'PROJECT_NUMBER', 'PROJECT_LOCATION'
|
|
108
|
+
required_vars = ['PROJECT_ID', 'PROJECT_NUMBER', 'PROJECT_LOCATION']
|
|
109
109
|
load_dotenv(dotenv_path=".env.agent")
|
|
110
110
|
|
|
111
111
|
ok = True
|
|
@@ -133,12 +133,14 @@ class Runner:
|
|
|
133
133
|
svc._check_required_files_exist()
|
|
134
134
|
elif mode == "github_deployment":
|
|
135
135
|
svc.one_github_deployment_to_go()
|
|
136
|
+
elif mode == "github_deployment_without_engine":
|
|
137
|
+
svc.one_githhub_deployment_to_go_with_skip()
|
|
136
138
|
|
|
137
139
|
@staticmethod
|
|
138
140
|
def _menu(root_agent):
|
|
139
141
|
print("Choose an operation:\n1) Create/Update\n2) Auth only\n3) CLI\n4) GUI\nq) Quit")
|
|
140
142
|
choice = input("Enter choice: ").strip().lower()
|
|
141
|
-
mapping = {"1": "create", "2": "auth", "3": "cli", "4": "gui", "5": "github_deployment"}
|
|
143
|
+
mapping = {"1": "create", "2": "auth", "3": "cli", "4": "gui", "5": "github_deployment", "6": "github_deployment_without_engine"}
|
|
142
144
|
if choice == "q":
|
|
143
145
|
sys.exit(0)
|
|
144
146
|
Runner._dispatch(mapping.get(choice, ""), root_agent)
|
{reasoning_deployment_service-0.4.0.dist-info → reasoning_deployment_service-0.8.3.dist-info}/RECORD
RENAMED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
reasoning_deployment_service/__init__.py,sha256=xDuKt9gGviQiTV6vXBdkBvygnlAOIrwnUjVaMGZy0L4,670
|
|
2
|
-
reasoning_deployment_service/reasoning_deployment_service.py,sha256=
|
|
3
|
-
reasoning_deployment_service/runner.py,sha256=
|
|
2
|
+
reasoning_deployment_service/reasoning_deployment_service.py,sha256=8CLjzDYFKAwKr6Avd97YlJ1b43T8_Oa3Pqi0Xfim5C8,45298
|
|
3
|
+
reasoning_deployment_service/runner.py,sha256=A88IhRPPAWtqedWPWOVne3Lg16uXhXuqARKGD5ORsWc,5597
|
|
4
4
|
reasoning_deployment_service/cli_editor/__init__.py,sha256=bN8NPkw8riB92pj2lAwJZuEMOQIO_RRuge0ehnJTW1I,118
|
|
5
|
-
reasoning_deployment_service/cli_editor/api_client.py,sha256=
|
|
5
|
+
reasoning_deployment_service/cli_editor/api_client.py,sha256=bcuV0kEHxyNobqJ1k2Iwp73EaFjuOWa4XJ77MRrWQr0,33106
|
|
6
6
|
reasoning_deployment_service/cli_editor/cli_runner.py,sha256=1KkHtgAhVZ7VHQj7o76JibLHnr7NMUB-tieDX_KrAcY,18239
|
|
7
7
|
reasoning_deployment_service/cli_editor/config.py,sha256=lZ8Ng007NVdN1n5spJ0OFC72TOPFWKvPRxa9eKE-FDY,3573
|
|
8
8
|
reasoning_deployment_service/cli_editor/google_deps.py,sha256=PhGwdKEC96GdlFHkQrtSJrg_-w1JoUPes3zvaz22rd0,771
|
|
9
|
-
reasoning_deployment_service/cli_editor/reasoning_engine_creator.py,sha256=6QC8Y9yZAT8SYNkT_R00g_SSOYuwEkIxAN9lBG3br2k,19564
|
|
10
9
|
reasoning_deployment_service/gui_editor/__init__.py,sha256=e5e88iNTk1GC243DRsQFi5E7PqMaT2SXmqOez9FbYzo,128
|
|
11
10
|
reasoning_deployment_service/gui_editor/agent_checkbox_list.py,sha256=ElxFqSgT3iUqDv2U9eR4eV-MfLUHqOXbDz6DqEEevOk,1783
|
|
12
11
|
reasoning_deployment_service/gui_editor/main.py,sha256=4UzgGUga_xIYIWRVo-80PzhJ1Dlou8PaUXoRiLcLhp8,10914
|
|
@@ -19,11 +18,11 @@ reasoning_deployment_service/gui_editor/src/core/reasoning_engine_creator.py,sha
|
|
|
19
18
|
reasoning_deployment_service/gui_editor/src/ui/__init__.py,sha256=262ZiXO6Luk8vZnhCIoYxOtGiny0bXK-BTKjxUNBx-w,43
|
|
20
19
|
reasoning_deployment_service/gui_editor/src/ui/agent_space_view.py,sha256=UTUMRFEzpUuRONl3K7bsCPRjZ_hiVE1s9fTsIHTZtSs,17130
|
|
21
20
|
reasoning_deployment_service/gui_editor/src/ui/authorization_view.py,sha256=BoNcGRFZ-Rb2pnOAAZxraP7yDdbwMJNvIrBrjMc_hbw,16970
|
|
22
|
-
reasoning_deployment_service/gui_editor/src/ui/reasoning_engine_view.py,sha256=
|
|
21
|
+
reasoning_deployment_service/gui_editor/src/ui/reasoning_engine_view.py,sha256=T_kBop74wHv8W7tk9aY17ty44rLu8Dc-vRZdRvhmeH0,13317
|
|
23
22
|
reasoning_deployment_service/gui_editor/src/ui/reasoning_engines_view.py,sha256=IRjFlBbY98usAZa0roOonjvWQOsF6NBW4bBg_k8KnKI,7860
|
|
24
23
|
reasoning_deployment_service/gui_editor/src/ui/ui_components.py,sha256=HdQHy-oSZ3GobQ3FNdH7y_w3ANbFiuf2rMoflAmff0A,55366
|
|
25
|
-
reasoning_deployment_service-0.
|
|
26
|
-
reasoning_deployment_service-0.
|
|
27
|
-
reasoning_deployment_service-0.
|
|
28
|
-
reasoning_deployment_service-0.
|
|
29
|
-
reasoning_deployment_service-0.
|
|
24
|
+
reasoning_deployment_service-0.8.3.dist-info/METADATA,sha256=hNyOtztxXHUY45RW7jQFn_n_apls8D61leAm-88wCJ0,5302
|
|
25
|
+
reasoning_deployment_service-0.8.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
26
|
+
reasoning_deployment_service-0.8.3.dist-info/entry_points.txt,sha256=onGKjR5ONTtRv3aqEtK863iw9Ty1kLcjfZlsplkRZrA,84
|
|
27
|
+
reasoning_deployment_service-0.8.3.dist-info/top_level.txt,sha256=GKuQS1xHUYLZbatw9DmcYdBxxLhWhhGkV4FmFxgKdp0,29
|
|
28
|
+
reasoning_deployment_service-0.8.3.dist-info/RECORD,,
|
|
@@ -1,448 +0,0 @@
|
|
|
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)
|
{reasoning_deployment_service-0.4.0.dist-info → reasoning_deployment_service-0.8.3.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|