reasoning-deployment-service 0.7.5__py3-none-any.whl → 0.8.4__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.

@@ -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()
@@ -35,6 +35,7 @@ class ReasoningEngineDeploymentService:
35
35
  self._oauth_client_id = None
36
36
  self._oauth_client_secret = None
37
37
  self._agent_space_engine = None
38
+ self._authorization_id = None
38
39
 
39
40
 
40
41
  self._load_agent_definition()
@@ -98,10 +99,16 @@ class ReasoningEngineDeploymentService:
98
99
  def info(self, message: str):
99
100
  self.logger.info(f"[DEPLOYMENT SERVICE: INFO]: {message}")
100
101
 
101
- def _generate_authorization_id(self) -> str:
102
+ def _generate_authorization_id(self) -> Optional[str]:
102
103
  get_deployment_environment = os.getenv('AGENT_DEPLOYMENT_PIPELINE_ID', "LOCAL_RUN")
103
104
 
104
- return f"{get_deployment_environment}-{self._reasoning_engine_name}-{self._agent_space_engine}-auth".lower()
105
+ if self._authorization_override:
106
+ return self._authorization_override.lower()
107
+
108
+ if not self._authorization_override:
109
+ return None
110
+
111
+ return f"{get_deployment_environment}-{self._reasoning_engine_name}-{str(uuid.uuid4())}-auth".lower()
105
112
 
106
113
  def _load_runtime_variables(self):
107
114
  load_dotenv(dotenv_path=".env", override=True)
@@ -112,9 +119,15 @@ class ReasoningEngineDeploymentService:
112
119
  runtime_vars[key] = os.environ[key]
113
120
 
114
121
  runtime_vars.update(self._runtime_variable_definitions or {})
115
- runtime_vars.update({'AUTHORIZATION_ID': self._generate_authorization_id()})
122
+ local_auth_id = self._generate_authorization_id()
123
+
124
+ if self._use_authorization and local_auth_id:
125
+ runtime_vars.update({'AUTHORIZATION_ID': local_auth_id})
126
+ self._authorization_id = local_auth_id
127
+ runtime_vars.update({f"DEPLOYED_PROJECT_NUMBER": self._project_number})
128
+ runtime_vars.update({f"DEPLOYED_PROJECT_ID": self._project_id})
129
+ runtime_vars.update({f"AGENT_DEPLOYMENT_PIPELINE_ID": self._deployed_environment})
116
130
 
117
- self._authorization_id = runtime_vars.get('AUTHORIZATION_ID')
118
131
  self._environment_variables = runtime_vars
119
132
 
120
133
  def _check_required_files_exist(self):
@@ -213,8 +226,6 @@ class ReasoningEngineDeploymentService:
213
226
  DEV_OAUTH_CLIENT_SECRET="""
214
227
 
215
228
  path.write_text(template.strip() + "\n")
216
-
217
- # Also update .gitignore to include logs
218
229
  self._update_gitignore()
219
230
 
220
231
  return path
@@ -256,7 +267,6 @@ class ReasoningEngineDeploymentService:
256
267
 
257
268
  config = {
258
269
  "defaults": {
259
- "deployment_service": "0.7",
260
270
  "reasoning_engine": {
261
271
  "name": "reasoning-engine-dev",
262
272
  "description": "A reasoning engine for development"
@@ -295,11 +305,6 @@ class ReasoningEngineDeploymentService:
295
305
 
296
306
  try:
297
307
  config = config['defaults']
298
- deployment_service_version = config.get('deployment_service')
299
- version = float(deployment_service_version)
300
- if version < 0.7:
301
- raise RuntimeError(f"Unsupported deployment_service version: {version}. Expected minimum '0.7'")
302
-
303
308
  authorization = config['authorization']
304
309
  gemini_enterprise = config['gemini_enterprise']
305
310
  reasoning_engine = config['reasoning_engine']
@@ -308,14 +313,17 @@ class ReasoningEngineDeploymentService:
308
313
 
309
314
  reasoning_engine_name = reasoning_engine.get('name')
310
315
  reasoning_engine_description = reasoning_engine.get('description')
316
+ reasoning_engine_bypass = reasoning_engine.get('skip_build', False)
311
317
 
312
318
  gemini_enterprise_name = gemini_enterprise.get('name')
313
319
  gemini_enterprise_description = gemini_enterprise.get('description')
314
320
  gemini_enterprise_tool_description = gemini_enterprise.get('tool_description')
315
321
  gemini_enterprise_engine_id = gemini_enterprise.get('target_deployment_engine_id')
322
+ gemini_enterprise_icon_uri = gemini_enterprise.get('icon_uri')
316
323
 
317
- print(gemini_enterprise)
318
324
 
325
+ self._reasoning_engine_bypass = reasoning_engine_bypass
326
+ self._icon_uri = gemini_enterprise_icon_uri
319
327
  self._agent_space_engine = gemini_enterprise_engine_id or os.getenv(f"{self.deployment_env}_AGENT_SPACE_ENGINE")
320
328
  self._required_scopes = authorization.get('scopes', [])
321
329
  self._agent_folder = "agent"
@@ -325,8 +333,9 @@ class ReasoningEngineDeploymentService:
325
333
  self._agent_space_description = gemini_enterprise_description
326
334
  self._agent_space_tool_description = gemini_enterprise_tool_description
327
335
  self._use_authorization = authorization.get('enabled', False)
336
+ self._authorization_override = authorization.get('authorization_id_override', None)
328
337
  except KeyError as e:
329
- raise RuntimeError(f"Missing required key in agent.yaml: {e}")
338
+ raise RuntimeError(f"Missing required key in agent.yaml: {e}. Your agent.yaml file is not valid for this deployment service version.")
330
339
 
331
340
  def _load_deployment_environment_variables(self, deployment_environment: str):
332
341
  required_vars = ['PROJECT_ID', 'PROJECT_NUMBER', 'PROJECT_LOCATION', 'STAGING_BUCKET']
@@ -351,6 +360,8 @@ class ReasoningEngineDeploymentService:
351
360
 
352
361
  setattr(self, f"_{var.lower()}", os.getenv(env_var))
353
362
 
363
+ self._deployed_environment = os.getenv(f"AGENT_DEPLOYMENT_PIPELINE_ID", "unregistered_environment")
364
+
354
365
  def _check_requirements_file_present(self):
355
366
  if not os.path.exists("requirements.txt"):
356
367
  raise RuntimeError("Missing requirements.txt file")
@@ -427,7 +438,10 @@ class ReasoningEngineDeploymentService:
427
438
  },
428
439
  }
429
440
 
430
- if self._authorization_id:
441
+ if self._icon_uri:
442
+ payload["icon"] = {"uri": self._icon_uri}
443
+
444
+ if self._use_authorization and self._authorization_id:
431
445
  payload["adk_agent_definition"]["authorizations"] = [
432
446
  f"projects/{self._project_number}/locations/global/authorizations/{self._authorization_id}"
433
447
  ]
@@ -530,7 +544,7 @@ class ReasoningEngineDeploymentService:
530
544
  def _create_authorization(self) -> dict:
531
545
  read_authorizations = self._read_engine_deployment_record()
532
546
 
533
- if not self._authorization_id:
547
+ if not self._authorization_id or not self._use_authorization:
534
548
  self.warning("No authorization ID provided; skipping authorization creation.")
535
549
 
536
550
  return
@@ -613,8 +627,10 @@ class ReasoningEngineDeploymentService:
613
627
 
614
628
  return True
615
629
 
616
- def _delete_authorization(self):
617
- if not self._authorization_id:
630
+ def _delete_authorization(self, drop_authorization_for_refresh: Optional[str] = None):
631
+ auth_to_drop = drop_authorization_for_refresh or self._authorization_id
632
+
633
+ if not auth_to_drop:
618
634
  self.warning("No authorization ID provided; skipping deletion.")
619
635
  return
620
636
 
@@ -627,18 +643,21 @@ class ReasoningEngineDeploymentService:
627
643
 
628
644
  url = (
629
645
  f"{discovery_engine_url}/projects/{self._project_id}/locations/global/authorizations"
630
- f"?authorizationId={self._authorization_id}"
646
+ f"?authorizationId={auth_to_drop}"
631
647
  )
632
648
 
633
649
  r = self._http.delete(url, headers=headers, timeout=60)
634
650
 
635
651
  if r.status_code < 400:
652
+ if drop_authorization_for_refresh:
653
+ self.info(f"Authorization {drop_authorization_for_refresh} deleted successfully for refresh.")
654
+ return True
655
+
636
656
  self.info("Authorization deleted successfully.")
637
657
  self._authorization_id = None
638
658
  self._update_in_agent_space()
639
659
  return True
640
660
 
641
- # Log API failure details to file only
642
661
  with open(self.log_filename, 'a') as f:
643
662
  timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S,%f')[:-3]
644
663
  f.write(f"{timestamp} - ReasoningEngineDeployment - ERROR - Failed to delete authorization with status {r.status_code} {r.reason}\n")
@@ -651,9 +670,8 @@ class ReasoningEngineDeploymentService:
651
670
  except:
652
671
  pass
653
672
 
654
- # Terminal message - simple
673
+
655
674
  self.error("Failed to delete authorization")
656
- # This will also log the record file
657
675
  return False
658
676
 
659
677
  def one_deployment_with_everything_on_it(self, skip_engine_step=False):
@@ -791,6 +809,7 @@ class ReasoningEngineDeploymentService:
791
809
  new_description: Optional[str] = None,
792
810
  new_reasoning_engine: Optional[str] = None,
793
811
  new_authorizations: Optional[list[str]] = None,
812
+ icon_uri: Optional[str] = None,
794
813
  ) -> dict:
795
814
  """
796
815
  Safely patch metadata (displayName, description) and linkage fields
@@ -817,12 +836,15 @@ class ReasoningEngineDeploymentService:
817
836
  }
818
837
  }
819
838
 
839
+ if icon_uri:
840
+ agent_updates_body["icon"] = {"uri": icon_uri}
841
+
820
842
  self.info(agent_updates_body)
821
843
 
822
844
  headers = self._get_headers()
823
845
 
824
846
  update_mask = ["displayName", "description", "adk_agent_definition.tool_settings.tool_description",
825
- "adk_agent_definition.provisioned_reasoning_engine.reasoning_engine", "adk_agent_definition.authorizations"]
847
+ "adk_agent_definition.provisioned_reasoning_engine.reasoning_engine", "adk_agent_definition.authorizations", 'icon.uri']
826
848
  params = {"update_mask": ",".join(update_mask)}
827
849
  resp = self._http.patch(url, headers=headers, params=params, json=agent_updates_body, timeout=60)
828
850
 
@@ -856,6 +878,42 @@ class ReasoningEngineDeploymentService:
856
878
  r = self._http.patch(url, headers=headers, json=payload, timeout=60)
857
879
  r.raise_for_status()
858
880
  return r.json()
881
+
882
+ def detect_scope_change(self, auth_full_name, want_scopes) -> Optional[bool]:
883
+ auth_url = f"{DISCOVERY_ENGINE_URL}/{auth_full_name}"
884
+ hdrs = self._get_headers().copy()
885
+
886
+ if "Authorization" in hdrs:
887
+ hdrs["Authorization"] = "Bearer ***"
888
+
889
+ self.info(f"[AUTH] GET {auth_url} headers={json.dumps(hdrs)}")
890
+ r = self._http.get(auth_url, headers=self._get_headers(), timeout=60)
891
+ self.info(f"[AUTH] GET status={r.status_code} ct={r.headers.get('content-type','')}")
892
+
893
+ try:
894
+ self.info(f"[AUTH] GET body={json.dumps(r.json(), indent=2)[:4000]}")
895
+ except Exception:
896
+ self.info(f"[AUTH] GET text={(r.text or '')[:1000]}")
897
+ r.raise_for_status()
898
+
899
+ data = r.json() or {}
900
+ existing_uri = (((data.get("serverSideOauth2") or {}).get("authorizationUri")) or "")
901
+ self.info(f"[AUTH] existing authorizationUri={existing_uri!r}")
902
+ existing_scopes = set()
903
+
904
+ if existing_uri:
905
+ parsed = urlparse(existing_uri)
906
+ qs = parse_qs(parsed.query)
907
+ scope_str = (qs.get("scope", [""])[0] or "")
908
+ existing_scopes = set(scope_str.split())
909
+
910
+ self.info(
911
+ f"[AUTH] scopes existing={sorted(existing_scopes)} want={sorted(want_scopes)} "
912
+ f"missing={sorted(want_scopes - existing_scopes)} extra={sorted(existing_scopes - want_scopes)}"
913
+ )
914
+
915
+ if existing_scopes != want_scopes:
916
+ return True
859
917
 
860
918
  def one_github_deployment_to_go(self, skip_engine=False):
861
919
  """
@@ -873,6 +931,7 @@ class ReasoningEngineDeploymentService:
873
931
  )
874
932
 
875
933
  self._cicd_deploy = True
934
+ delete_old_authorization = False
876
935
  self.info(f"[INIT] vertexai.init(project={self._project_id}, location={self._project_location}, staging_bucket={self._staging_bucket})")
877
936
  vertexai.init(
878
937
  project=self._project_id,
@@ -884,7 +943,7 @@ class ReasoningEngineDeploymentService:
884
943
  engine_rn = self.find_engine_by_name(self._reasoning_engine_name)
885
944
  self.info(f"[ENGINE] find_engine_by_name -> {engine_rn}")
886
945
 
887
- if not skip_engine:
946
+ if not skip_engine and not self._reasoning_engine_bypass:
888
947
  if not engine_rn:
889
948
  self.info(f"[ENGINE] '{self._reasoning_engine_name}' not found. Creating...")
890
949
  self.create_reasoning_engine()
@@ -901,58 +960,34 @@ class ReasoningEngineDeploymentService:
901
960
 
902
961
  self.info(f"[ENGINE] final engine_rn={engine_rn}")
903
962
 
963
+ if not engine_rn:
964
+ self.error("[ENGINE] Reasoning engine required for Agent Space deployment.")
965
+ raise RuntimeError("Reasoning engine resolution failed.")
966
+
904
967
  auth_full_name = None
905
- if self._authorization_id:
968
+ if self._authorization_id and self._use_authorization:
906
969
  want_scopes = set(self._required_scopes or [])
907
970
  self.info(f"[AUTH] id={self._authorization_id} want_scopes={sorted(want_scopes)}")
908
971
  auth_full_name = self.find_authorization_by_id(self._authorization_id)
909
972
  self.info(f"[AUTH] find_authorization_by_id -> {auth_full_name}")
910
973
 
974
+ if auth_full_name and self.detect_scope_change(auth_full_name, want_scopes):
975
+ self.info(f"[AUTH] Scopes changed; patching authorization {self._authorization_id}...")
976
+ delete_old_authorization = auth_full_name
977
+ self._authorization_id = self._generate_authorization_id()
978
+ auth_full_name = None
979
+
911
980
  if not auth_full_name:
912
981
  self.info(f"[AUTH] '{self._authorization_id}' not found. Creating...")
913
982
  ok = self._create_authorization()
914
983
  self.info(f"[AUTH] _create_authorization -> {ok}")
915
984
  auth_full_name = self.find_authorization_by_id(self._authorization_id)
916
985
  self.info(f"[AUTH] post-create resolve -> {auth_full_name}")
986
+
917
987
  if not ok or not auth_full_name:
918
988
  self.error("[AUTH] Creation failed or did not resolve.")
989
+
919
990
  raise RuntimeError("Authorization creation failed.")
920
- else:
921
- auth_url = f"{DISCOVERY_ENGINE_URL}/{auth_full_name}"
922
- hdrs = self._get_headers().copy()
923
- if "Authorization" in hdrs:
924
- hdrs["Authorization"] = "Bearer ***"
925
- self.info(f"[AUTH] GET {auth_url} headers={json.dumps(hdrs)}")
926
- r = self._http.get(auth_url, headers=self._get_headers(), timeout=60)
927
- self.info(f"[AUTH] GET status={r.status_code} ct={r.headers.get('content-type','')}")
928
- try:
929
- self.info(f"[AUTH] GET body={json.dumps(r.json(), indent=2)[:4000]}")
930
- except Exception:
931
- self.info(f"[AUTH] GET text={(r.text or '')[:1000]}")
932
- r.raise_for_status()
933
-
934
- data = r.json() or {}
935
- existing_uri = (((data.get("serverSideOauth2") or {}).get("authorizationUri")) or "")
936
- self.info(f"[AUTH] existing authorizationUri={existing_uri!r}")
937
-
938
- existing_scopes = set()
939
- if existing_uri:
940
- parsed = urlparse(existing_uri)
941
- qs = parse_qs(parsed.query)
942
- scope_str = (qs.get("scope", [""])[0] or "")
943
- existing_scopes = set(scope_str.split())
944
-
945
- self.info(
946
- f"[AUTH] scopes existing={sorted(existing_scopes)} want={sorted(want_scopes)} "
947
- f"missing={sorted(want_scopes - existing_scopes)} extra={sorted(existing_scopes - want_scopes)}"
948
- )
949
-
950
- if existing_scopes != want_scopes:
951
- self.info("[AUTH] Scopes changed. Patching authorization...")
952
- self.update_authorization_scopes(self._authorization_id, list(want_scopes), self._oauth_client_id)
953
- self.info("[AUTH] Authorization updated.")
954
- else:
955
- self.info("[AUTH] Scopes unchanged; no update needed.")
956
991
  else:
957
992
  self.info("[AUTH] No authorization_id configured; skipping authorization step.")
958
993
 
@@ -985,7 +1020,13 @@ class ReasoningEngineDeploymentService:
985
1020
  new_authorizations=[
986
1021
  f"projects/{self._project_number}/locations/global/authorizations/{self._authorization_id}"
987
1022
  ] if self._authorization_id else None,
1023
+ icon_uri=self._icon_uri,
988
1024
  )
989
1025
  self.info(f"[AGENT] PATCH result -> {json.dumps(patched, indent=2)[:2000]}")
990
1026
 
1027
+
1028
+ if delete_old_authorization:
1029
+ self.info(f"[AUTH] Deleting old authorization {delete_old_authorization}...")
1030
+ self._delete_authorization(delete_old_authorization)
1031
+
991
1032
  self.info("GitHub deployment completed successfully.")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reasoning-deployment-service
3
- Version: 0.7.5
3
+ Version: 0.8.4
4
4
  Summary: Deployment helper for Vertex AI Reasoning Engines & Agent Spaces
5
5
  Author-email: Sergio Estrada <sergio.estrada@accenture.com>
6
6
  License: Apache-2.0
@@ -1,5 +1,5 @@
1
1
  reasoning_deployment_service/__init__.py,sha256=xDuKt9gGviQiTV6vXBdkBvygnlAOIrwnUjVaMGZy0L4,670
2
- reasoning_deployment_service/reasoning_deployment_service.py,sha256=s_I6PYzTDhsmncnj0I5A1B3s3judboBJssz1FTZ62Ko,43774
2
+ reasoning_deployment_service/reasoning_deployment_service.py,sha256=7hf5VYvAFuKXworJO_fVOLUU0v5LNJ40vRJJqdWpZaQ,45336
3
3
  reasoning_deployment_service/runner.py,sha256=A88IhRPPAWtqedWPWOVne3Lg16uXhXuqARKGD5ORsWc,5597
4
4
  reasoning_deployment_service/cli_editor/__init__.py,sha256=bN8NPkw8riB92pj2lAwJZuEMOQIO_RRuge0ehnJTW1I,118
5
5
  reasoning_deployment_service/cli_editor/api_client.py,sha256=bcuV0kEHxyNobqJ1k2Iwp73EaFjuOWa4XJ77MRrWQr0,33106
@@ -21,8 +21,8 @@ reasoning_deployment_service/gui_editor/src/ui/authorization_view.py,sha256=BoNc
21
21
  reasoning_deployment_service/gui_editor/src/ui/reasoning_engine_view.py,sha256=T_kBop74wHv8W7tk9aY17ty44rLu8Dc-vRZdRvhmeH0,13317
22
22
  reasoning_deployment_service/gui_editor/src/ui/reasoning_engines_view.py,sha256=IRjFlBbY98usAZa0roOonjvWQOsF6NBW4bBg_k8KnKI,7860
23
23
  reasoning_deployment_service/gui_editor/src/ui/ui_components.py,sha256=HdQHy-oSZ3GobQ3FNdH7y_w3ANbFiuf2rMoflAmff0A,55366
24
- reasoning_deployment_service-0.7.5.dist-info/METADATA,sha256=ZrVyhbEK7_QcEISP5966g529gOFC8TTS8edDs9Ug6zg,5302
25
- reasoning_deployment_service-0.7.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
26
- reasoning_deployment_service-0.7.5.dist-info/entry_points.txt,sha256=onGKjR5ONTtRv3aqEtK863iw9Ty1kLcjfZlsplkRZrA,84
27
- reasoning_deployment_service-0.7.5.dist-info/top_level.txt,sha256=GKuQS1xHUYLZbatw9DmcYdBxxLhWhhGkV4FmFxgKdp0,29
28
- reasoning_deployment_service-0.7.5.dist-info/RECORD,,
24
+ reasoning_deployment_service-0.8.4.dist-info/METADATA,sha256=3ylGfj_4YjvBThijeiUh1EhS2dKqTF46h9xmsWDAA-o,5302
25
+ reasoning_deployment_service-0.8.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
26
+ reasoning_deployment_service-0.8.4.dist-info/entry_points.txt,sha256=onGKjR5ONTtRv3aqEtK863iw9Ty1kLcjfZlsplkRZrA,84
27
+ reasoning_deployment_service-0.8.4.dist-info/top_level.txt,sha256=GKuQS1xHUYLZbatw9DmcYdBxxLhWhhGkV4FmFxgKdp0,29
28
+ reasoning_deployment_service-0.8.4.dist-info/RECORD,,