reasoning-deployment-service 0.3.6__py3-none-any.whl → 0.3.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.

@@ -1,15 +1,17 @@
1
1
  import json, os, subprocess, yaml, sys
2
2
  import urllib.parse, vertexai, google.auth
3
3
  import requests as _requests
4
- from typing import Tuple
4
+ from typing import Dict, Optional, Tuple
5
5
  from pathlib import Path
6
6
  from dotenv import load_dotenv
7
7
  from vertexai import agent_engines
8
+ from vertexai.agent_engines import AgentEngine
8
9
  from google.adk.agents import BaseAgent
9
10
  from google.auth.transport.requests import Request as GoogleAuthRequest
10
11
  from google.api_core.exceptions import NotFound
11
12
  import logging
12
13
  from datetime import datetime
14
+ from urllib.parse import urlparse, parse_qs
13
15
 
14
16
  DISCOVERY_ENGINE_URL = "https://discoveryengine.googleapis.com/v1alpha"
15
17
 
@@ -24,7 +26,8 @@ class ReasoningEngineDeploymentService:
24
26
  self.root_agent = root_agent
25
27
  self.deployment_env = deployment_environment
26
28
  self.attempt_to_use_existing_auth = False
27
-
29
+
30
+ self._cicd_deploy = False
28
31
  self._staging_bucket = None
29
32
  self._project_id = None
30
33
  self._project_number = None
@@ -101,9 +104,9 @@ class ReasoningEngineDeploymentService:
101
104
  self._generate_env_agent()
102
105
  end_run = True
103
106
 
104
- if not os.path.exists("aix_agent.yaml"):
107
+ if not os.path.exists("agent.yaml"):
105
108
  self._generate_example_yaml_config()
106
- self.warning("Creating aix_agent.yaml file ... done")
109
+ self.warning("Creating agent.yaml file ... done")
107
110
  end_run = True
108
111
 
109
112
  self.warning("Please fill in the required values in the generated files and re-run the deployment.")
@@ -246,7 +249,7 @@ class ReasoningEngineDeploymentService:
246
249
  for pattern in new_patterns:
247
250
  f.write(f"{pattern}\n")
248
251
 
249
- def _generate_example_yaml_config(self, path: str | Path = "aix_agent.yaml", overwrite: bool = False) -> Path:
252
+ def _generate_example_yaml_config(self, path: str | Path = "agent.yaml", overwrite: bool = False) -> Path:
250
253
  """
251
254
  Create an example YAML config matching the requested schema.
252
255
 
@@ -274,7 +277,6 @@ class ReasoningEngineDeploymentService:
274
277
  "agent_space_description": "Agent spece description, lets go",
275
278
  "agent_space_tool_description": "Agent space tool description",
276
279
  },
277
- "agent_folder": "your_agent_folder",
278
280
  "auth": {
279
281
  "oauth_authorization_id": "test_auth_three",
280
282
  },
@@ -289,11 +291,11 @@ class ReasoningEngineDeploymentService:
289
291
 
290
292
  def _load_agent_definition(self):
291
293
  try:
292
- with open("aix_agent.yaml", "r") as f:
294
+ with open("agent.yaml", "r") as f:
293
295
  config = yaml.safe_load(f)
294
296
  except FileNotFoundError:
295
297
  self._generate_example_yaml_config()
296
- self.error("Could not locate a valid aix_agent.yaml file. Generating example file in your directory.")
298
+ self.error("Could not locate a valid agent.yaml file. Generating example file in your directory.")
297
299
  sys.exit(1)
298
300
 
299
301
  try:
@@ -311,7 +313,7 @@ class ReasoningEngineDeploymentService:
311
313
  agent_space_tool_description = metadata["agent_space_tool_description"]
312
314
 
313
315
  self._required_scopes = scopes
314
- self._agent_folder = agent_folder
316
+ self._agent_folder = "agent"
315
317
  self._reasoning_engine_name = reasoning_engine_name
316
318
  self._reasoning_engine_description = reasoning_engine_description
317
319
  self._agent_space_name = agent_space_name
@@ -320,7 +322,7 @@ class ReasoningEngineDeploymentService:
320
322
  self._authorization_id = auth.get("oauth_authorization_id", None)
321
323
  self._environment_variables = environment_variables or []
322
324
  except KeyError as e:
323
- raise RuntimeError(f"Missing required key in aix_agent.yaml: {e}")
325
+ raise RuntimeError(f"Missing required key in agent.yaml: {e}")
324
326
 
325
327
  def _load_deployment_environment_variables(self, deployment_environment: str):
326
328
  required_vars = ['PROJECT_ID', 'PROJECT_NUMBER', 'PROJECT_LOCATION', 'STAGING_BUCKET', 'AGENT_SPACE_ENGINE']
@@ -684,4 +686,203 @@ class ReasoningEngineDeploymentService:
684
686
  self.info("Creating agent space ... ")
685
687
  self._deploy_to_agent_space()
686
688
  else:
687
- self._update_in_agent_space()
689
+ self._update_in_agent_space()
690
+
691
+
692
+ def _ensure_vertex_inited(self):
693
+ """Initialize Vertex AI once and reuse to avoid repeated heavy init calls."""
694
+ if not self._vertex_inited:
695
+ vertexai.init()
696
+ self._vertex_inited = True
697
+
698
+
699
+ def find_engine_by_name(self, engine_name: str) -> Optional[str]:
700
+ engines = list(agent_engines.list(filter=f'display_name="{engine_name}"'))
701
+ if not engines:
702
+ return None
703
+ if len(engines) > 1:
704
+ raise RuntimeError(f"Multiple engines found with display_name='{engine_name}'. Use unique names/labels.")
705
+ eng = engines[0]
706
+ if not isinstance(eng, AgentEngine) or not hasattr(eng, "resource_name"):
707
+ raise RuntimeError("Unexpected engine object; missing AgentEngine/resource_name.")
708
+ return eng.resource_name
709
+
710
+
711
+ def _get_headers(self) -> dict:
712
+ return {
713
+ "Authorization": f"Bearer {self._access_token()}",
714
+ "Content-Type": "application/json",
715
+ "Accept": "application/json",
716
+ "X-Goog-User-Project": str(self._project_number),
717
+ }
718
+
719
+ def find_authorization_by_id(self, authorization_id: str) -> Optional[str]:
720
+ name = f"projects/{self._project_id}/locations/global/authorizations/{authorization_id}"
721
+ url = f"{DISCOVERY_ENGINE_URL}/{name}"
722
+ r = self._http.get(url, headers=self._get_headers(), timeout=60)
723
+
724
+ if r.status_code == 404:
725
+ return None
726
+
727
+ r.raise_for_status()
728
+
729
+ return r.json().get("name", name)
730
+
731
+ def find_agent_space_agents_by_display(self, display_name: str) -> Optional[dict]:
732
+ base = (
733
+ f"{DISCOVERY_ENGINE_URL}/projects/{self._project_id}/locations/global/"
734
+ f"collections/default_collection/engines/{self._agent_space_engine}/"
735
+ f"assistants/default_assistant/agents"
736
+ )
737
+
738
+ headers = self._get_headers()
739
+ page = None
740
+ matches =[]
741
+
742
+ while True:
743
+ url = base + (f"?pageToken={page}" if page else "")
744
+ r = self._http.get(url, headers=headers, timeout=60)
745
+ r.raise_for_status()
746
+ data = r.json()
747
+
748
+ for a in data.get("agents", []):
749
+ if a.get("displayName") != display_name:
750
+ continue
751
+ full = a.get("name", "")
752
+ matches.append({
753
+ "id": full.split("/")[-1] if full else "",
754
+ "display_name": a.get("displayName", ""),
755
+ "full_name": full,
756
+ "labels": a.get("labels", {}),
757
+ })
758
+
759
+ page = data.get("nextPageToken")
760
+ if not page:
761
+ break
762
+
763
+ if not matches:
764
+ return None
765
+ if len(matches) > 1:
766
+ raise RuntimeError(
767
+ f"Found {len(matches)} agents with displayName='{display_name}'. "
768
+ "Provide an ID or labels (e.g., env/app/agent) to disambiguate."
769
+ )
770
+
771
+ return matches[0]
772
+
773
+
774
+ def one_github_deployment_to_go(self):
775
+ """
776
+ CI-friendly deploy:
777
+ - Engine: create or update by display_name.
778
+ - Authorization: create if missing; patch if scopes changed.
779
+ - Agent Space: create if missing; patch if found (by displayName under engine).
780
+ """
781
+ self.info("Starting GitHub deployment...")
782
+
783
+ # Ensure Vertex SDK calls have context for list/update
784
+ self._cicd_deploy = True
785
+ vertexai.init(
786
+ project=self._project_id,
787
+ location=self._project_location,
788
+ staging_bucket=self._staging_bucket,
789
+ )
790
+
791
+ # -----------------------------
792
+ # 1) Reasoning Engine (create or update)
793
+ # -----------------------------
794
+ engine_rn = self.find_engine_by_name(self._reasoning_engine_name)
795
+ if not engine_rn:
796
+ self.info(f"Engine '{self._reasoning_engine_name}' not found. Creating...")
797
+ self.create_reasoning_engine()
798
+ # read back the created id (or re-resolve by name as fallback)
799
+ engine_rn = self._read_engine_deployment_record().get("reasoning_engine_id") or \
800
+ self.find_engine_by_name(self._reasoning_engine_name)
801
+ if not engine_rn:
802
+ self.error("Engine creation did not yield a resource name.")
803
+ raise RuntimeError("Engine creation failed.")
804
+ else:
805
+ self.info(f"Engine '{self._reasoning_engine_name}' exists. Updating...")
806
+ self.update_reasoning_engine(engine_rn)
807
+
808
+ # -----------------------------
809
+ # 2) Authorization (create if missing; update scopes if changed)
810
+ # -----------------------------
811
+ if self._authorization_id:
812
+ want_scopes = set(self._required_scopes or [])
813
+ auth_full_name = self.find_authorization_by_id(self._authorization_id)
814
+
815
+ if not auth_full_name:
816
+ self.info(f"Authorization '{self._authorization_id}' not found. Creating...")
817
+ ok = self._create_authorization()
818
+ if not ok:
819
+ self.error("Authorization creation failed.")
820
+ raise RuntimeError("Authorization creation failed.")
821
+ auth_full_name = self.find_authorization_by_id(self._authorization_id)
822
+ if not auth_full_name:
823
+ self.error("Authorization creation did not resolve to a resource.")
824
+ raise RuntimeError("Authorization creation failed to resolve.")
825
+ else:
826
+ # Compare scopes; patch if different
827
+ r = self._http.get(f"{DISCOVERY_ENGINE_URL}/{auth_full_name}",
828
+ headers=self._get_headers(), timeout=60)
829
+ r.raise_for_status()
830
+ data = r.json() or {}
831
+ existing_uri = (((data.get("serverSideOauth2") or {}).get("authorizationUri")) or "")
832
+ existing_scopes = set()
833
+ if existing_uri:
834
+ parsed = urlparse(existing_uri)
835
+ qs = parse_qs(parsed.query)
836
+ scope_str = (qs.get("scope", [""])[0] or "")
837
+ existing_scopes = set(scope_str.split())
838
+
839
+ if existing_scopes != want_scopes:
840
+ self.info("Authorization scopes changed. Patching authorization...")
841
+ new_auth_uri = self._build_authorization_uri(self._oauth_client_id, list(want_scopes))
842
+ patch_payload = {
843
+ "serverSideOauth2": {
844
+ "clientId": self._oauth_client_id,
845
+ "clientSecret": self._oauth_client_secret,
846
+ "authorizationUri": new_auth_uri,
847
+ "tokenUri": "https://oauth2.googleapis.com/token",
848
+ }
849
+ }
850
+ pr = self._http.patch(f"{DISCOVERY_ENGINE_URL}/{auth_full_name}",
851
+ headers=self._get_headers(), json=patch_payload, timeout=60)
852
+ pr.raise_for_status()
853
+ self.info("Authorization updated.")
854
+ else:
855
+ self.info("Authorization scopes unchanged; no update needed.")
856
+ else:
857
+ self.info("No authorization_id configured; skipping authorization step.")
858
+
859
+ # -----------------------------
860
+ # 3) Agent Space Agent (create or update by displayName under engine)
861
+ # -----------------------------
862
+ existing_agent = self.find_agent_space_agents_by_display(self._agent_space_name)
863
+ headers, payload = self._get_agent_space_payload(engine_rn)
864
+
865
+ if not existing_agent:
866
+ self.info(f"Agent Space agent '{self._agent_space_name}' not found. Creating...")
867
+ create_url = self._get_agent_space_agent_url_new()
868
+ cr = self._http.post(create_url, headers=headers, json=payload, timeout=90)
869
+ if cr.status_code >= 400:
870
+ # File-only details already handled elsewhere; surface concise error here
871
+ self.error(f"Agent creation failed [{cr.status_code}].")
872
+ cr.raise_for_status()
873
+ agent_name = (cr.json() or {}).get("name")
874
+ if agent_name:
875
+ self._write_engine_deployment({"agent_space_id": agent_name})
876
+ self.info(f"Agent Space agent created: {agent_name}")
877
+ else:
878
+ self.warning("Agent created but response missing name. Verify in console.")
879
+ else:
880
+ self.info(f"Agent Space agent '{self._agent_space_name}' exists. Updating...")
881
+ patch_url = f"{DISCOVERY_ENGINE_URL}/{existing_agent['full_name']}"
882
+ ur = self._http.patch(patch_url, headers=headers, json=payload, timeout=90)
883
+ if ur.status_code >= 400:
884
+ self.error(f"Agent update failed [{ur.status_code}].")
885
+ ur.raise_for_status()
886
+ self.info("Agent Space agent updated.")
887
+
888
+ self.info("GitHub deployment completed successfully.")
@@ -29,8 +29,8 @@ class Runner:
29
29
  root_agent = Runner._load_agent(args.agent_path)
30
30
 
31
31
  # --- Require config files ---
32
- if not Path(".env.agent").exists() or not Path("aix_agent.yaml").exists():
33
- print("Missing .env.agent or aix_agent.yaml.")
32
+ if not Path(".env.agent").exists() or not Path("agent.yaml").exists():
33
+ print("Missing .env.agent or agent.yaml.")
34
34
  print("Options:\n 5) Generate placeholder config\n q) Quit")
35
35
  choice = input("Enter choice: ").strip()
36
36
  if choice == "5":
@@ -88,8 +88,7 @@ class Runner:
88
88
 
89
89
  # Case 3: bootstrap (first run)
90
90
  print("No agent path configured. Let's set it up once.")
91
- agent_dir = input("Enter the directory where your root_agent (agent.py) lives: ").strip()
92
- agent_dir = agent_dir.rstrip("/")
91
+ agent_dir = "agent"
93
92
 
94
93
  if not Path(agent_dir, "agent.py").exists():
95
94
  print(f"Error: {agent_dir}/agent.py not found")
@@ -132,6 +131,8 @@ class Runner:
132
131
  GUIEditor().run()
133
132
  elif mode == "populate_files":
134
133
  svc._check_required_files_exist()
134
+ elif mode == "github_deployment":
135
+ svc.one_github_deployment_to_go()
135
136
 
136
137
  @staticmethod
137
138
  def _menu(root_agent):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reasoning-deployment-service
3
- Version: 0.3.6
3
+ Version: 0.3.8
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
@@ -67,7 +67,7 @@ q) Quit
67
67
  - The tool will guide you to create the starter files:
68
68
  - `your_agent_import.py` (shim pointing to your agent folder)
69
69
  - `.env.agent` (environment and project settings)
70
- - `aix_agent.yaml` (agent engine and space definition)
70
+ - `agent.yaml` (agent engine and space definition)
71
71
 
72
72
  Once these are in place, all modes will be fully available
73
73
 
@@ -83,7 +83,7 @@ Your project must have the following at minimum:
83
83
  my_project/
84
84
  ├── requirements.txt # pinned dependencies
85
85
  ├── .env.agent # environment and project config
86
- ├── aix_agent.yaml # engine and agent space definition
86
+ ├── agent.yaml # engine and agent space definition
87
87
  ├── my_agent/ # your agent folder
88
88
  │ └── agent.py # defines root_agent
89
89
  ```
@@ -173,7 +173,7 @@ DEVELOPER=your.name
173
173
 
174
174
  ---
175
175
 
176
- ### ✅ aix_agent.yaml
176
+ ### ✅ agent.yaml
177
177
 
178
178
  Example starter file:
179
179
 
@@ -1,6 +1,6 @@
1
1
  reasoning_deployment_service/__init__.py,sha256=xDuKt9gGviQiTV6vXBdkBvygnlAOIrwnUjVaMGZy0L4,670
2
- reasoning_deployment_service/reasoning_deployment_service.py,sha256=0QHU2IqqojwFI2wPJ0izrskiHiwBfxdBEB9I_YxYbSA,29133
3
- reasoning_deployment_service/runner.py,sha256=e2Uu4DdRWuCHQI4H1x8Q6s1eMbBNoWFkBTnAaspK1tk,5413
2
+ reasoning_deployment_service/reasoning_deployment_service.py,sha256=FVK0xWo6CAXoFjxysDShwgBM5OJFk8WR1lDDw65E9k0,38298
3
+ reasoning_deployment_service/runner.py,sha256=J11rdxAEAl4_xDtMrKnK-_lH3rfDcXIpvHmybQzhXvM,5381
4
4
  reasoning_deployment_service/cli_editor/__init__.py,sha256=bN8NPkw8riB92pj2lAwJZuEMOQIO_RRuge0ehnJTW1I,118
5
5
  reasoning_deployment_service/cli_editor/api_client.py,sha256=Kzx5iYp0MmowggrSmPLE7I2kt1-8xvdGBAgde9a1gCY,33681
6
6
  reasoning_deployment_service/cli_editor/cli_runner.py,sha256=1KkHtgAhVZ7VHQj7o76JibLHnr7NMUB-tieDX_KrAcY,18239
@@ -22,8 +22,8 @@ reasoning_deployment_service/gui_editor/src/ui/authorization_view.py,sha256=BoNc
22
22
  reasoning_deployment_service/gui_editor/src/ui/reasoning_engine_view.py,sha256=tCvSPEf4dW0NRdAqfs3yT5Pa873gYeLzCMMIt2r2T4o,14644
23
23
  reasoning_deployment_service/gui_editor/src/ui/reasoning_engines_view.py,sha256=IRjFlBbY98usAZa0roOonjvWQOsF6NBW4bBg_k8KnKI,7860
24
24
  reasoning_deployment_service/gui_editor/src/ui/ui_components.py,sha256=HdQHy-oSZ3GobQ3FNdH7y_w3ANbFiuf2rMoflAmff0A,55366
25
- reasoning_deployment_service-0.3.6.dist-info/METADATA,sha256=yn2cDwndN4bqUfVOt_kixzD3LvUD5z0XslS5-gtu7GY,5314
26
- reasoning_deployment_service-0.3.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
27
- reasoning_deployment_service-0.3.6.dist-info/entry_points.txt,sha256=onGKjR5ONTtRv3aqEtK863iw9Ty1kLcjfZlsplkRZrA,84
28
- reasoning_deployment_service-0.3.6.dist-info/top_level.txt,sha256=GKuQS1xHUYLZbatw9DmcYdBxxLhWhhGkV4FmFxgKdp0,29
29
- reasoning_deployment_service-0.3.6.dist-info/RECORD,,
25
+ reasoning_deployment_service-0.3.8.dist-info/METADATA,sha256=oJmnjcngCuNLhUHc8_Hgklk0rAVQhQwahuLI0fWX28w,5302
26
+ reasoning_deployment_service-0.3.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
27
+ reasoning_deployment_service-0.3.8.dist-info/entry_points.txt,sha256=onGKjR5ONTtRv3aqEtK863iw9Ty1kLcjfZlsplkRZrA,84
28
+ reasoning_deployment_service-0.3.8.dist-info/top_level.txt,sha256=GKuQS1xHUYLZbatw9DmcYdBxxLhWhhGkV4FmFxgKdp0,29
29
+ reasoning_deployment_service-0.3.8.dist-info/RECORD,,