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

@@ -30,6 +30,27 @@ EXCLUDES = [
30
30
 
31
31
 
32
32
  class ApiClient:
33
+ def get_authorization_info(self, auth_id: str) -> dict:
34
+ """Get details for a specific authorization by ID."""
35
+ if not self.is_live:
36
+ # Return mock info for testing
37
+ return {
38
+ "id": auth_id,
39
+ "scopes": [
40
+ "https://www.googleapis.com/auth/cloud-platform",
41
+ "https://www.googleapis.com/auth/userinfo.email"
42
+ ],
43
+ "status": "mock"
44
+ }
45
+ headers = {
46
+ "Authorization": f"Bearer {self._access_token()}",
47
+ "Content-Type": "application/json",
48
+ "X-Goog-User-Project": self.project_id,
49
+ }
50
+ url = f"https://discoveryengine.googleapis.com/v1alpha/projects/{self.project_id}/locations/global/authorizations/{auth_id}"
51
+ r = self._http.get(url, headers=headers, timeout=60)
52
+ r.raise_for_status()
53
+ return r.json()
33
54
  """
34
55
  Single responsibility: hold configuration & credentials and expose API calls.
35
56
  This class has both 'live' and 'mock' modes; the public surface is identical.
@@ -559,6 +580,63 @@ class ApiClient:
559
580
  else:
560
581
  return ("failed", f"{r.status_code} {r.text}")
561
582
 
583
+ def get_authorization_info(self, auth_id: str) -> Dict[str, Any]:
584
+ """Get details for a specific authorization by ID."""
585
+ if not self.is_live:
586
+ # Return mock info for testing
587
+ return {
588
+ "id": auth_id,
589
+ "scopes": [
590
+ "https://www.googleapis.com/auth/cloud-platform",
591
+ "https://www.googleapis.com/auth/userinfo.email"
592
+ ],
593
+ "status": "mock"
594
+ }
595
+ headers = {
596
+ "Authorization": f"Bearer {self._access_token()}",
597
+ "Content-Type": "application/json",
598
+ "X-Goog-User-Project": self.project_id,
599
+ }
600
+ url = f"{BASE_URL}/projects/{self.project_id}/locations/global/authorizations/{auth_id}"
601
+ r = self._http.get(url, headers=headers, timeout=60)
602
+ r.raise_for_status()
603
+ return r.json()
604
+
605
+ def update_authorization_scopes(self, auth_id: str, scopes: list) -> Dict[str, Any]:
606
+ """Patch the scopes for a specific authorization by ID, updating the authorizationUri as well."""
607
+ if not self.is_live:
608
+ return {
609
+ "id": auth_id,
610
+ "scopes": scopes,
611
+ "status": "mock-patched"
612
+ }
613
+ headers = {
614
+ "Authorization": f"Bearer {self._access_token()}",
615
+ "Content-Type": "application/json",
616
+ "X-Goog-User-Project": self.project_number,
617
+ }
618
+ url = f"{BASE_URL}/projects/{self.project_number}/locations/global/authorizations/{auth_id}?update_mask=server_side_oauth2.authorization_uri"
619
+ scopes_str = "%20".join(scopes)
620
+ authorization_uri = (
621
+ "https://accounts.google.com/o/oauth2/auth"
622
+ "?response_type=code"
623
+ f"&client_id={self.oauth_client_id or 'your-client-id'}"
624
+ f"&scope={scopes_str}"
625
+ "&access_type=offline&prompt=consent"
626
+ )
627
+
628
+ payload = {
629
+ "serverSideOauth2": {
630
+ "authorizationUri": authorization_uri
631
+ }
632
+ }
633
+ r = self._http.patch(url, headers=headers, json=payload, timeout=60)
634
+ print(r.status_code)
635
+ print(r.text)
636
+ print(r.json())
637
+ r.raise_for_status()
638
+ return r.json()
639
+
562
640
  def _ensure_authorization(self, auth_name: str) -> Tuple[bool, str]:
563
641
  if not self.is_live:
564
642
  time.sleep(0.02)
@@ -663,4 +741,60 @@ class ApiClient:
663
741
  }
664
742
  self._profile["agent_space_agent_id"] = full
665
743
  self._save_profile()
666
- return ("created", "Deployed", item)
744
+ return ("created", "Deployed", item)
745
+
746
+ def remove_authorization_from_agent(self, agent_id: str) -> Dict[str, Any]:
747
+ """Remove all authorizations from an agent by PATCHing with an empty authorizations list."""
748
+ if not self.is_live:
749
+ return {
750
+ "id": agent_id,
751
+ "authorizations": [],
752
+ "status": "mock-removed"
753
+ }
754
+ headers = {
755
+ "Authorization": f"Bearer {self._access_token()}",
756
+ "Content-Type": "application/json",
757
+ "X-Goog-User-Project": self.project_id,
758
+ }
759
+ url = f"{BASE_URL}/projects/{self.project_id}/locations/global/assistants/default_assistant/agents/{agent_id}"
760
+ payload = {"authorizations": []}
761
+ r = self._http.patch(url, headers=headers, json=payload, timeout=60)
762
+ r.raise_for_status()
763
+ return r.json()
764
+
765
+ def drop_agent_authorizations(self, agent_id: str) -> Dict[str, Any]:
766
+ """Drop all authorizations for an agent space agent by PATCHing with all attributes except authorizations (fully omitted)."""
767
+ if not self.is_live:
768
+ return {
769
+ "id": agent_id,
770
+ "authorizations": [],
771
+ "status": "mock-dropped"
772
+ }
773
+ headers = {
774
+ "Authorization": f"Bearer {self._access_token()}",
775
+ "Content-Type": "application/json",
776
+ "X-Goog-User-Project": self.project_id,
777
+ }
778
+ url = (f"{BASE_URL}/projects/{self.project_id}/locations/global/collections/default_collection/"
779
+ f"engines/{self.engine_name}/assistants/default_assistant/agents/{agent_id}")
780
+ # GET the current agent definition
781
+ get_resp = self._http.get(url, headers=headers, timeout=60)
782
+ get_resp.raise_for_status()
783
+ agent_data = get_resp.json()
784
+ # Build PATCH payload: copy all attributes except authorizations
785
+ patch_payload = {}
786
+ for key in ["displayName", "description"]:
787
+ if key in agent_data:
788
+ patch_payload[key] = agent_data[key]
789
+ adk_def = agent_data.get("adkAgentDefinition", {})
790
+ patch_adk_def = {}
791
+ # Copy all adkAgentDefinition fields except 'authorizations'
792
+ for k, v in adk_def.items():
793
+ if k != "authorizations":
794
+ patch_adk_def[k] = v
795
+ if patch_adk_def:
796
+ patch_payload["adk_agent_definition"] = patch_adk_def
797
+ # PATCH with the new payload
798
+ patch_resp = self._http.patch(url, headers=headers, json=patch_payload, timeout=60)
799
+ patch_resp.raise_for_status()
800
+ return patch_resp.json()
@@ -43,6 +43,14 @@ class CLIRunner:
43
43
  subparsers.add_parser("list-authorizations", help="List all authorizations in the project")
44
44
  delete_auth_parser = subparsers.add_parser("delete-authorization", help="Delete an authorization by list position")
45
45
  delete_auth_parser.add_argument("position", help="Position number from the list (e.g., 1, 2, 3...)")
46
+ # New commands
47
+ get_auth_info_parser = subparsers.add_parser("get-authorization-info", help="Get details for a specific authorization")
48
+ get_auth_info_parser.add_argument("auth_id", help="Authorization ID")
49
+ update_auth_scopes_parser = subparsers.add_parser("update-authorization-scopes", help="Update scopes for a specific authorization")
50
+ update_auth_scopes_parser.add_argument("auth_id", help="Authorization ID")
51
+ update_auth_scopes_parser.add_argument("scopes", help="Comma-separated list of scopes")
52
+ drop_agent_auth_parser = subparsers.add_parser("drop-agent-authorizations", help="Drop all authorizations for an agent space agent")
53
+ drop_agent_auth_parser.add_argument("agent_id", help="Agent ID")
46
54
 
47
55
  def run(self):
48
56
  # Load environment variables from .env.agent file (same as GUI editor)
@@ -113,12 +121,20 @@ class CLIRunner:
113
121
  self._list_authorizations()
114
122
  elif args.command == "delete-authorization":
115
123
  self._delete_authorization(args.position)
124
+ elif args.command == "get-authorization-info":
125
+ self._get_authorization_info(args.auth_id)
126
+ elif args.command == "update-authorization-scopes":
127
+ scopes = [s.strip() for s in args.scopes.split(",") if s.strip()]
128
+ self._update_authorization_scopes(args.auth_id, scopes)
129
+ elif args.command == "drop-agent-authorizations":
130
+ self._drop_agent_authorizations(args.agent_id)
116
131
  else:
117
132
  self.parser.print_help()
118
133
  except SystemExit:
119
134
  # Catch argparse's SystemExit to prevent CLI from closing
120
135
  print("Invalid command. Please try again.")
121
136
  except Exception as e:
137
+ raise
122
138
  print(f"An error occurred: {e}")
123
139
  print("\nAvailable commands:")
124
140
  print(" list-engines List all reasoning engines in the project")
@@ -339,5 +355,35 @@ class CLIRunner:
339
355
  except Exception as e:
340
356
  print(f"❌ Error deleting authorization: {e}")
341
357
 
358
+ def _get_authorization_info(self, auth_id):
359
+ """Get details for a specific authorization."""
360
+ print(f"🔍 Getting info for authorization: {auth_id}")
361
+ try:
362
+ info = self.api_client.get_authorization_info(auth_id)
363
+ from pprint import pprint
364
+ pprint(info)
365
+ except Exception as e:
366
+ print(f"❌ Error getting authorization info: {e}")
367
+
368
+ def _update_authorization_scopes(self, auth_id, scopes):
369
+ """Update scopes for a specific authorization."""
370
+ print(f"🔧 Updating scopes for authorization: {auth_id}")
371
+ print(f"New scopcdscsdes: {scopes}")
372
+
373
+ result = self.api_client.update_authorization_scopes(auth_id, scopes)
374
+ from pprint import pprint
375
+ pprint(result)
376
+
377
+ def _drop_agent_authorizations(self, agent_id):
378
+ """Drop all authorizations for an agent space agent."""
379
+ print(f"🗑️ Dropping all authorizations for agent: {agent_id}")
380
+ try:
381
+ result = self.api_client.drop_agent_authorizations(agent_id)
382
+ from pprint import pprint
383
+ pprint(result)
384
+ except Exception as e:
385
+ raise
386
+ print(f"❌ Error dropping agent authorizations: {e}")
387
+
342
388
  if __name__ == "__main__":
343
389
  CLIRunner().run()
@@ -0,0 +1,48 @@
1
+ """
2
+ Scrollable agent list with checkboxes for mass selection and actions.
3
+ """
4
+ import tkinter as tk
5
+ from tkinter import ttk, messagebox
6
+
7
+ class AgentCheckboxList(tk.Frame):
8
+ def __init__(self, master, agents, on_mass_action):
9
+ super().__init__(master)
10
+ self.agents = agents
11
+ self.on_mass_action = on_mass_action
12
+ self.vars = {}
13
+ self._setup_ui()
14
+
15
+ def _setup_ui(self):
16
+ canvas = tk.Canvas(self)
17
+ scrollbar = ttk.Scrollbar(self, orient="vertical", command=canvas.yview)
18
+ self.scrollable_frame = tk.Frame(canvas)
19
+
20
+ self.scrollable_frame.bind(
21
+ "<Configure>",
22
+ lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
23
+ )
24
+
25
+ canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
26
+ canvas.configure(yscrollcommand=scrollbar.set, height=300)
27
+
28
+ canvas.pack(side="left", fill="both", expand=True)
29
+ scrollbar.pack(side="right", fill="y")
30
+
31
+ for agent in self.agents:
32
+ var = tk.BooleanVar()
33
+ self.vars[agent["id"]] = var
34
+ cb = tk.Checkbutton(self.scrollable_frame, text=f"{agent.get('display_name', agent['id'])}", variable=var)
35
+ cb.pack(anchor="w", padx=4, pady=2)
36
+
37
+ self.action_btn = ttk.Button(self, text="Drop Authorizations for Checked Agents", command=self._do_mass_action)
38
+ self.action_btn.pack(fill="x", pady=8)
39
+
40
+ def get_checked_agent_ids(self):
41
+ return [aid for aid, var in self.vars.items() if var.get()]
42
+
43
+ def _do_mass_action(self):
44
+ checked = self.get_checked_agent_ids()
45
+ if not checked:
46
+ messagebox.showinfo("No agents selected", "Please check agents to drop authorizations.")
47
+ return
48
+ self.on_mass_action(checked)
@@ -25,6 +25,94 @@ EXCLUDES = [
25
25
 
26
26
 
27
27
  class ApiClient:
28
+ def drop_agent_authorizations(self, agent_id: str) -> dict:
29
+ """Drop all authorizations for an agent space agent by PATCHing with all attributes except authorizations (fully omitted)."""
30
+ if not self.is_live:
31
+ return {
32
+ "id": agent_id,
33
+ "authorizations": [],
34
+ "status": "mock-dropped"
35
+ }
36
+ headers = {
37
+ "Authorization": f"Bearer {self._access_token()}",
38
+ "Content-Type": "application/json",
39
+ "X-Goog-User-Project": self.project_id,
40
+ }
41
+ url = (f"{BASE_URL}/projects/{self.project_id}/locations/global/collections/default_collection/"
42
+ f"engines/{self.engine_name}/assistants/default_assistant/agents/{agent_id}")
43
+ # GET the current agent definition
44
+ get_resp = self._http.get(url, headers=headers, timeout=60)
45
+ get_resp.raise_for_status()
46
+ agent_data = get_resp.json()
47
+ # Build PATCH payload: copy all attributes except authorizations
48
+ patch_payload = {}
49
+ for key in ["displayName", "description"]:
50
+ if key in agent_data:
51
+ patch_payload[key] = agent_data[key]
52
+ adk_def = agent_data.get("adkAgentDefinition", {})
53
+ patch_adk_def = {}
54
+ # Copy all adkAgentDefinition fields except 'authorizations'
55
+ for k, v in adk_def.items():
56
+ if k != "authorizations":
57
+ patch_adk_def[k] = v
58
+ if patch_adk_def:
59
+ patch_payload["adk_agent_definition"] = patch_adk_def
60
+ # PATCH with the new payload
61
+ patch_resp = self._http.patch(url, headers=headers, json=patch_payload, timeout=60)
62
+ patch_resp.raise_for_status()
63
+ return patch_resp.json()
64
+ def update_authorization_scopes(self, auth_id: str, scopes: list, oauth_client_id: str) -> dict:
65
+ """Patch the scopes for a specific authorization by ID, updating the authorizationUri as well."""
66
+ if not self.is_live:
67
+ return {
68
+ "id": auth_id,
69
+ "scopes": scopes,
70
+ "status": "mock-patched"
71
+ }
72
+ import requests
73
+ scopes_str = " ".join(scopes)
74
+ authorization_uri = (
75
+ "https://accounts.google.com/o/oauth2/auth"
76
+ "?response_type=code"
77
+ f"&client_id={oauth_client_id}"
78
+ f"&scope={scopes_str}"
79
+ "&access_type=offline&prompt=consent"
80
+ )
81
+ payload = {
82
+ "serverSideOauth2": {
83
+ "authorizationUri": authorization_uri
84
+ }
85
+ }
86
+ url = f"{BASE_URL}/projects/{self.project_id}/locations/global/authorizations/{auth_id}?update_mask=server_side_oauth2.authorization_uri"
87
+ headers = {
88
+ "Authorization": f"Bearer {self._access_token()}",
89
+ "Content-Type": "application/json",
90
+ "X-Goog-User-Project": self.project_id,
91
+ }
92
+ r = self._http.patch(url, headers=headers, json=payload, timeout=60)
93
+ r.raise_for_status()
94
+ return r.json()
95
+ def get_authorization_info(self, auth_id: str) -> dict:
96
+ """Get details for a specific authorization by ID."""
97
+ if not self.is_live:
98
+ # Return mock info for testing
99
+ return {
100
+ "id": auth_id,
101
+ "scopes": [
102
+ "https://www.googleapis.com/auth/cloud-platform",
103
+ "https://www.googleapis.com/auth/userinfo.email"
104
+ ],
105
+ "status": "mock"
106
+ }
107
+ headers = {
108
+ "Authorization": f"Bearer {self._access_token()}",
109
+ "Content-Type": "application/json",
110
+ "X-Goog-User-Project": self.project_id,
111
+ }
112
+ url = f"{BASE_URL}/projects/{self.project_id}/locations/global/authorizations/{auth_id}"
113
+ r = self._http.get(url, headers=headers, timeout=60)
114
+ r.raise_for_status()
115
+ return r.json()
28
116
  """
29
117
  Single responsibility: hold configuration & credentials and expose API calls.
30
118
  This class has both 'live' and 'mock' modes; the public surface is identical.
@@ -6,6 +6,7 @@ from typing import Callable
6
6
 
7
7
  from ..core.api_client import ApiClient
8
8
  from .ui_components import async_operation, StatusButton, AgentDetailsDialog, LoadingDialog
9
+ from reasoning_deployment_service.gui_editor.agent_checkbox_list import AgentCheckboxList
9
10
 
10
11
 
11
12
  class AgentSpaceView(ttk.Frame):
@@ -34,6 +35,9 @@ class AgentSpaceView(ttk.Frame):
34
35
  self.delete_btn = StatusButton(btns, text="Delete Selected", command=self.delete_selected)
35
36
  self.delete_btn.pack(side="left", padx=8)
36
37
 
38
+ self.drop_auth_btn = StatusButton(btns, text="Drop Authorizations (Selected)", command=self.drop_selected_authorizations)
39
+ self.drop_auth_btn.pack(side="left", padx=8)
40
+
37
41
  self.details_btn = StatusButton(btns, text="More Agent Details", command=self.show_agent_details)
38
42
  self.details_btn.pack(side="left", padx=8)
39
43
 
@@ -80,6 +84,10 @@ class AgentSpaceView(ttk.Frame):
80
84
  self._cached_selection = None
81
85
  self._selection_is_dirty = True
82
86
 
87
+ # Checkbox agent list (initially empty, filled on refresh)
88
+ self.checkbox_list_frame = ttk.LabelFrame(self, text="Mass Select Agents (Checkboxes)")
89
+ self.checkbox_list_frame.pack(fill="x", padx=10, pady=8)
90
+ self.agent_checkbox_list = None
83
91
  # Initialize button states without triggering immediate API calls
84
92
  self._update_button_states()
85
93
 
@@ -106,29 +114,66 @@ class AgentSpaceView(ttk.Frame):
106
114
 
107
115
  def _update_button_states(self):
108
116
  """Update button states based on current conditions - IMMEDIATE, no timers."""
109
- # Refresh button - always enabled if authenticated (use cached state)
110
117
  is_auth = self._get_cached_auth_state()
111
118
  self.refresh_btn.set_enabled(
112
119
  is_auth,
113
120
  "Authentication required" if not is_auth else ""
114
121
  )
115
-
116
- # Get selection once and cache it
117
122
  selection = self._get_selection()
118
123
  has_selection = bool(selection)
119
124
  single_selection = len(selection) == 1
120
-
121
- # Delete button - enabled only if agents are selected
122
125
  self.delete_btn.set_enabled(
123
126
  has_selection,
124
127
  "Select agents to delete" if not has_selection else ""
125
128
  )
126
-
127
- # Details button - enabled only if a single agent is selected
129
+ self.drop_auth_btn.set_enabled(
130
+ has_selection,
131
+ "Select agents to drop authorizations" if not has_selection else ""
132
+ )
128
133
  self.details_btn.set_enabled(
129
134
  single_selection,
130
135
  "Select a single agent to view details" if not single_selection else ""
131
136
  )
137
+ def drop_selected_authorizations(self):
138
+ """Drop authorizations for selected agents."""
139
+ selection = self._get_selection()
140
+ if not selection:
141
+ messagebox.showinfo("No selection", "Select agents to drop authorizations.")
142
+ return
143
+ count = len(selection)
144
+ if not messagebox.askyesno("Confirm", f"Drop authorizations for {count} agent{'s' if count != 1 else ''}?"):
145
+ return
146
+ agent_ids = []
147
+ for item in selection:
148
+ values = self.tree.item(item, "values")
149
+ if len(values) >= 1:
150
+ agent_ids.append(values[0])
151
+ self.status.set(f"Dropping authorizations for {count} agent{'s' if count != 1 else ''}...")
152
+ def callback(results):
153
+ if isinstance(results, Exception):
154
+ self.status.set(f"Error: {results}")
155
+ self.log(f"❌ Error dropping authorizations: {results}")
156
+ return
157
+ ok = [k for k, v in results.items() if isinstance(v, dict)]
158
+ bad = {k: v for k, v in results.items() if not isinstance(v, dict)}
159
+ if ok:
160
+ self.log(f"✅ Dropped authorizations for {len(ok)} agent(s): {', '.join(ok[:10])}{'…' if len(ok) > 10 else ''}")
161
+ for k, v in list(bad.items())[:10]:
162
+ self.log(f"⚠️ {k}: {v}")
163
+ if len(bad) > 10:
164
+ self.log(f"…and {len(bad)-10} more failures")
165
+ self.status.set(f"Done. Dropped authorizations for {len(ok)} agent(s).")
166
+ self._update_button_states()
167
+ self.refresh()
168
+ def mass_drop():
169
+ results = {}
170
+ for agent_id in agent_ids:
171
+ try:
172
+ results[agent_id] = self.api.drop_agent_authorizations(agent_id)
173
+ except Exception as e:
174
+ results[agent_id] = str(e)
175
+ return results
176
+ async_operation(mass_drop, callback=callback, ui_widget=self)
132
177
 
133
178
  def _on_selection_change(self, event=None):
134
179
  """Handle tree selection changes - IMMEDIATE update, no debouncing."""
@@ -158,7 +203,54 @@ class AgentSpaceView(ttk.Frame):
158
203
  count = len(rows)
159
204
  self.status.set(f"Loaded {count} agent{'s' if count != 1 else ''}.")
160
205
  self.log(f"✅ Loaded {count} agent space agent{'s' if count != 1 else ''}. Data keys: {len(self._agents_data)}")
206
+ # Update checkbox list
207
+ self._update_checkbox_list(list(self._agents_data.values()))
208
+ self._update_button_states()
209
+
210
+ def _update_checkbox_list(self, agents):
211
+ # Remove previous checkbox list if present
212
+ if self.agent_checkbox_list:
213
+ self.agent_checkbox_list.destroy()
214
+ self.agent_checkbox_list = AgentCheckboxList(
215
+ self.checkbox_list_frame,
216
+ agents,
217
+ self._mass_drop_authorizations_from_checkboxes
218
+ )
219
+ self.agent_checkbox_list.pack(fill="x", padx=4, pady=4)
220
+
221
+ def _mass_drop_authorizations_from_checkboxes(self, agent_ids):
222
+ if not agent_ids:
223
+ messagebox.showinfo("No agents selected", "Please check agents to drop authorizations.")
224
+ return
225
+ count = len(agent_ids)
226
+ if not messagebox.askyesno("Confirm", f"Drop authorizations for {count} agent{'s' if count != 1 else ''}?"):
227
+ return
228
+ self.status.set(f"Dropping authorizations for {count} agent{'s' if count != 1 else ''}...")
229
+ def callback(results):
230
+ if isinstance(results, Exception):
231
+ self.status.set(f"Error: {results}")
232
+ self.log(f"❌ Error dropping authorizations: {results}")
233
+ return
234
+ ok = [k for k, v in results.items() if isinstance(v, dict)]
235
+ bad = {k: v for k, v in results.items() if not isinstance(v, dict)}
236
+ if ok:
237
+ self.log(f"✅ Dropped authorizations for {len(ok)} agent(s): {', '.join(ok[:10])}{'…' if len(ok) > 10 else ''}")
238
+ for k, v in list(bad.items())[:10]:
239
+ self.log(f"⚠️ {k}: {v}")
240
+ if len(bad) > 10:
241
+ self.log(f"…and {len(bad)-10} more failures")
242
+ self.status.set(f"Done. Dropped authorizations for {len(ok)} agent(s).")
161
243
  self._update_button_states()
244
+ self.refresh()
245
+ def mass_drop():
246
+ results = {}
247
+ for agent_id in agent_ids:
248
+ try:
249
+ results[agent_id] = self.api.drop_agent_authorizations(agent_id)
250
+ except Exception as e:
251
+ results[agent_id] = str(e)
252
+ return results
253
+ async_operation(mass_drop, callback=callback, ui_widget=self)
162
254
 
163
255
  def refresh(self):
164
256
  """Refresh the agent list from the API."""
@@ -16,28 +16,44 @@ class AuthorizationView(ttk.Frame):
16
16
  self.api = api
17
17
  self.log = log
18
18
  self._auth_auto_loaded = False # Track if authorizations have been auto-loaded
19
-
20
19
  # Cache authentication state to avoid repeated API calls
21
20
  self._cached_auth_state = None
22
21
  self._last_auth_check = 0
23
22
  self._auth_cache_duration = 30 # 30 seconds
24
-
25
23
  self._setup_ui()
26
24
 
27
25
  def _setup_ui(self):
28
26
  # Control buttons
29
27
  btns = ttk.Frame(self)
30
28
  btns.pack(fill="x", pady=(6, 4))
31
-
29
+
32
30
  self.refresh_btn = StatusButton(btns, text="Refresh Authorizations", command=self.refresh)
33
31
  self.refresh_btn.pack(side="left", padx=4)
34
-
32
+
35
33
  self.delete_btn = StatusButton(btns, text="Delete Selected", command=self.delete_selected)
36
34
  self.delete_btn.pack(side="left", padx=8)
37
-
35
+
38
36
  self.status = tk.StringVar(value="Ready.")
39
37
  ttk.Label(btns, textvariable=self.status).pack(side="right")
40
38
 
39
+ # Update scopes UI
40
+ scopes_frame = ttk.LabelFrame(self, text="Update Authorization Scopes")
41
+ scopes_frame.pack(fill="x", padx=4, pady=4)
42
+ ttk.Label(scopes_frame, text="Authorization ID:").grid(row=0, column=0, sticky="w")
43
+ self.auth_id_entry = ttk.Entry(scopes_frame, width=30)
44
+ self.auth_id_entry.grid(row=0, column=1, sticky="w")
45
+ ttk.Label(scopes_frame, text="Scopes:").grid(row=1, column=0, sticky="nw")
46
+ self.scopes_var_list = []
47
+ self.scopes_check_frame = ttk.Frame(scopes_frame)
48
+ self.scopes_check_frame.grid(row=1, column=1, sticky="nw")
49
+ self.add_scope_btn = ttk.Button(scopes_frame, text="Add Scope", command=self._add_scope_dialog)
50
+ self.add_scope_btn.grid(row=2, column=1, sticky="w", pady=(2,0))
51
+ ttk.Label(scopes_frame, text="OAuth Client ID:").grid(row=2, column=0, sticky="w")
52
+ self.client_id_entry = ttk.Entry(scopes_frame, width=40)
53
+ self.client_id_entry.grid(row=2, column=1, sticky="w")
54
+ self.update_scopes_btn = ttk.Button(scopes_frame, text="Update Scopes", command=self.update_scopes)
55
+ self.update_scopes_btn.grid(row=3, column=0, columnspan=2, pady=4)
56
+
41
57
  # Info label
42
58
  info_frame = ttk.Frame(self)
43
59
  info_frame.pack(fill="x", padx=4, pady=4)
@@ -49,40 +65,94 @@ class AuthorizationView(ttk.Frame):
49
65
  wrap.pack(fill="both", expand=True)
50
66
  cols = ("id", "name")
51
67
  self.tree = ttk.Treeview(wrap, columns=cols, show="headings", selectmode="extended")
52
-
68
+
53
69
  for c, t, w in [
54
70
  ("id", "Authorization ID", 300),
55
71
  ("name", "Full Resource Name", 600),
56
72
  ]:
57
73
  self.tree.heading(c, text=t)
58
74
  self.tree.column(c, width=w, anchor="w")
59
-
75
+
60
76
  self.tree.pack(side="left", fill="both", expand=True)
61
77
  vsb = ttk.Scrollbar(wrap, orient="vertical", command=self.tree.yview)
62
78
  self.tree.configure(yscroll=vsb.set)
63
79
  vsb.pack(side="right", fill="y")
64
-
80
+
65
81
  # Event bindings
66
82
  self.tree.bind("<<TreeviewSelect>>", self._on_selection_change)
67
83
  self.tree.bind("<Button-3>", self._popup)
68
-
84
+
69
85
  # Context menu
70
86
  self.menu = tk.Menu(self, tearoff=0)
71
87
  self.menu.add_command(label="Delete", command=self.delete_selected)
72
-
88
+
73
89
  # Debouncing for button updates
74
90
  self._update_timer = None
75
-
91
+
76
92
  # Store full authorization data
77
93
  self._authorizations_data = {}
78
-
94
+
79
95
  # Cache selection state to avoid redundant tree.selection() calls
80
96
  self._cached_selection = None
81
97
  self._selection_is_dirty = True
82
-
98
+
83
99
  # Initialize button states without immediate API calls
84
100
  self._update_button_states()
85
101
 
102
+ def update_scopes(self):
103
+ """Update scopes for the given authorization."""
104
+ auth_id = self.auth_id_entry.get().strip()
105
+ scopes = [var.get().strip() for var, cb_var in self.scopes_var_list if cb_var.get() and var.get().strip()]
106
+ client_id = self.client_id_entry.get().strip()
107
+ if not auth_id or not scopes or not client_id:
108
+ messagebox.showwarning("Missing info", "Please fill all fields.")
109
+ return
110
+ self.status.set(f"Updating scopes for {auth_id}...")
111
+ def callback(result):
112
+ if isinstance(result, Exception):
113
+ self.status.set(f"Error: {result}")
114
+ self.log(f"❌ Error updating scopes: {result}")
115
+ return
116
+ self.status.set(f"Scopes updated for {auth_id}.")
117
+ self.log(f"✅ Scopes updated for {auth_id}: {result}")
118
+ async_operation(lambda: self.api.update_authorization_scopes(auth_id, scopes, client_id), callback=callback, ui_widget=self)
119
+
120
+ def _add_scope_dialog(self):
121
+ dialog = tk.Toplevel(self)
122
+ dialog.title("Add Scope")
123
+ ttk.Label(dialog, text="Enter new scope:").pack(padx=8, pady=8)
124
+ entry = ttk.Entry(dialog, width=50)
125
+ entry.pack(padx=8, pady=(0,8))
126
+ def on_add():
127
+ scope = entry.get().strip()
128
+ if scope:
129
+ self._add_scope_checkbox(scope, checked=True)
130
+ dialog.destroy()
131
+ ttk.Button(dialog, text="Add", command=on_add).pack(pady=(0,8))
132
+ entry.focus_set()
133
+ dialog.transient(self)
134
+ dialog.grab_set()
135
+ self.wait_window(dialog)
136
+
137
+ def _add_scope_checkbox(self, scope, checked=False):
138
+ var = tk.StringVar(value=scope)
139
+ cb_var = tk.BooleanVar(value=checked)
140
+ frame = ttk.Frame(self.scopes_check_frame)
141
+ cb = ttk.Checkbutton(frame, text=scope, variable=cb_var)
142
+ cb.pack(side="left")
143
+ del_btn = ttk.Button(frame, text="✕", width=2, command=lambda: self._remove_scope_checkbox(frame, var, cb_var))
144
+ del_btn.pack(side="left", padx=(4,0))
145
+ frame.pack(anchor="w", pady=1)
146
+ self.scopes_var_list.append((var, cb_var))
147
+ # Update checkbutton text if scope changes
148
+ def update_text(*_):
149
+ cb.config(text=var.get())
150
+ var.trace_add('write', update_text)
151
+
152
+ def _remove_scope_checkbox(self, frame, var, cb_var):
153
+ frame.destroy()
154
+ self.scopes_var_list = [(v, c) for v, c in self.scopes_var_list if v != var]
155
+
86
156
  def _get_cached_auth_state(self) -> bool:
87
157
  """Get authentication state with local caching to reduce API calls."""
88
158
  now = time.time()
@@ -127,6 +197,50 @@ class AuthorizationView(ttk.Frame):
127
197
  # Immediate update - no timers or delays
128
198
  self._update_button_states()
129
199
 
200
+ # Auto-populate update scopes fields if a single authorization is selected
201
+ sel = self._get_selection()
202
+ if len(sel) == 1:
203
+ item_id = sel[0]
204
+ auth_data = self._authorizations_data.get(item_id)
205
+ if auth_data:
206
+ self.auth_id_entry.delete(0, tk.END)
207
+ self.auth_id_entry.insert(0, auth_data.get("id", ""))
208
+ # Try to fetch scopes and client id from API if possible
209
+ try:
210
+ info = self.api.get_authorization_info(auth_data.get("id", ""))
211
+ import pprint
212
+ print("[DEBUG] get_authorization_info response:")
213
+ pprint.pprint(info)
214
+ scopes = []
215
+ sso = info.get("serverSideOauth2", {})
216
+ # Prefer parsing scopes from authorizationUri if present
217
+ if "authorizationUri" in sso and isinstance(sso["authorizationUri"], str):
218
+ import urllib.parse
219
+ parsed = urllib.parse.parse_qs(urllib.parse.urlparse(sso["authorizationUri"]).query)
220
+ scope_str = parsed.get("scope", [""])[0]
221
+ if scope_str:
222
+ # Split scopes by space, comma, or plus, and decode each
223
+ import re
224
+ raw_scopes = re.split(r"[ ,+]+", scope_str)
225
+ scopes = [urllib.parse.unquote_plus(s) for s in raw_scopes if s.strip()]
226
+ # Fallback to scopes field if present
227
+ if not scopes and "scopes" in info:
228
+ scopes = info["scopes"]
229
+ # Clear previous checkboxes
230
+ for child in self.scopes_check_frame.winfo_children():
231
+ child.destroy()
232
+ self.scopes_var_list.clear()
233
+ # Add one checkbox per parsed scope
234
+ for scope in scopes:
235
+ self._add_scope_checkbox(scope, checked=True)
236
+ client_id = sso.get("clientId", "")
237
+ if client_id:
238
+ self.client_id_entry.delete(0, tk.END)
239
+ self.client_id_entry.insert(0, client_id)
240
+ except Exception as e:
241
+ print(f"[ERROR] Exception in _on_selection_change: {e}")
242
+ pass
243
+
130
244
  def refresh(self):
131
245
  """Refresh the list of authorizations."""
132
246
  # Update button states immediately on click
@@ -1,6 +1,7 @@
1
- import sys
1
+ import sys, os
2
2
  import argparse
3
3
  from pathlib import Path
4
+ from dotenv import load_dotenv
4
5
  from reasoning_deployment_service import ReasoningEngineDeploymentService
5
6
  from reasoning_deployment_service.gui_editor import GUIEditor
6
7
  from reasoning_deployment_service.cli_editor import CLIRunner
@@ -27,7 +28,7 @@ class Runner:
27
28
  # --- Load root_agent dynamically ---
28
29
  root_agent = Runner._load_agent(args.agent_path)
29
30
 
30
- # --- Check env config ---
31
+ # --- Require config files ---
31
32
  if not Path(".env.agent").exists() or not Path("aix_agent.yaml").exists():
32
33
  print("Missing .env.agent or aix_agent.yaml.")
33
34
  print("Options:\n 5) Generate placeholder config\n q) Quit")
@@ -37,6 +38,11 @@ class Runner:
37
38
  svc._check_required_files_exist()
38
39
  sys.exit(0)
39
40
 
41
+ # --- Sanity check env vars ---
42
+ if not Runner._check_proper_configuration():
43
+ print("Error: Missing required environment variables in .env.agent")
44
+ sys.exit(1)
45
+
40
46
  # --- Run mode or menu ---
41
47
  if args.mode:
42
48
  Runner._dispatch(args.mode, root_agent)
@@ -61,11 +67,12 @@ class Runner:
61
67
 
62
68
  @staticmethod
63
69
  def _load_agent(agent_path_arg: str):
64
- """Load root_agent either from --agent-path or your_agent_import.py."""
70
+ """Load root_agent either from --agent-path or your_agent_import.py, bootstrapping if needed."""
65
71
  sys.path.insert(0, str(Path.cwd())) # ensure project root on sys.path
72
+ import importlib
66
73
 
74
+ # Case 1: explicit --agent-path (power user / devs only)
67
75
  if agent_path_arg:
68
- import importlib
69
76
  if ":" in agent_path_arg:
70
77
  module_path, attr = agent_path_arg.split(":")
71
78
  else:
@@ -73,12 +80,43 @@ class Runner:
73
80
  module = importlib.import_module(module_path)
74
81
  return getattr(module, attr)
75
82
 
76
- if Path("your_agent_import.py").exists():
83
+ # Case 2: shim already exists
84
+ shim = Path("your_agent_import.py")
85
+ if shim.exists():
77
86
  from your_agent_import import root_agent
78
87
  return root_agent
79
88
 
80
- print("Error: No agent path provided and your_agent_import.py not found.")
81
- sys.exit(1)
89
+ # Case 3: bootstrap (first run)
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("/")
93
+
94
+ if not Path(agent_dir, "agent.py").exists():
95
+ print(f"Error: {agent_dir}/agent.py not found")
96
+ sys.exit(1)
97
+
98
+ import_path = agent_dir.replace("/", ".") + ".agent"
99
+ shim.write_text(f"from {import_path} import root_agent\n")
100
+ print(f"Created {shim} pointing to {agent_dir}/agent.py")
101
+
102
+ from your_agent_import import root_agent
103
+ return root_agent
104
+
105
+
106
+ @staticmethod
107
+ def _check_proper_configuration():
108
+ """Ensure required environment variables are set in .env.agent."""
109
+ required_vars = ['PROJECT_ID', 'PROJECT_NUMBER', 'PROJECT_LOCATION', 'AGENT_SPACE_ENGINE']
110
+ load_dotenv(dotenv_path=".env.agent")
111
+
112
+ ok = True
113
+ for var in required_vars:
114
+ dev_var = f"DEV_{var}"
115
+ prod_var = f"PROD_{var}"
116
+ if not (Path(".env.agent").exists() and (os.getenv(dev_var) or os.getenv(prod_var))):
117
+ print(f"Missing: {dev_var} or {prod_var}")
118
+ ok = False
119
+ return ok
82
120
 
83
121
  @staticmethod
84
122
  def _dispatch(mode, root_agent):
@@ -0,0 +1,211 @@
1
+ Metadata-Version: 2.4
2
+ Name: reasoning-deployment-service
3
+ Version: 0.3.6
4
+ Summary: Deployment helper for Vertex AI Reasoning Engines & Agent Spaces
5
+ Author-email: Sergio Estrada <sergio.estrada@accenture.com>
6
+ License: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/AxG-AI-Exchange-GenAI-Initiative/AIXAgentDeploymentService
8
+ Project-URL: Repository, https://github.com/AxG-AI-Exchange-GenAI-Initiative/AIXAgentDeploymentService
9
+ Project-URL: Issues, https://github.com/AxG-AI-Exchange-GenAI-Initiative/AIXAgentDeploymentService/issues
10
+ Requires-Python: >=3.9
11
+ Description-Content-Type: text/markdown
12
+ Requires-Dist: requests>=2.28
13
+ Requires-Dist: python-dotenv>=1.0
14
+ Requires-Dist: google-auth>=2.20
15
+ Requires-Dist: google-cloud-storage>=2.16
16
+ Requires-Dist: google-cloud-aiplatform[adk,agent-engines]>=1.88.0
17
+ Requires-Dist: protobuf<7.0.0,>=4.21
18
+ Requires-Dist: keyring>=24.0
19
+ Requires-Dist: keyrings.google-artifactregistry-auth>=1.1
20
+ Requires-Dist: PyYAML>=6.0
21
+ Requires-Dist: click>=8.0
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest; extra == "dev"
24
+ Requires-Dist: build; extra == "dev"
25
+ Requires-Dist: twine; extra == "dev"
26
+ Requires-Dist: black; extra == "dev"
27
+ Requires-Dist: ruff; extra == "dev"
28
+
29
+ # Reasoning Deployment Service
30
+
31
+ Helper package for deploying Vertex AI Reasoning Engines and Agent Spaces
32
+
33
+ ---
34
+
35
+ ## 📦 Installation
36
+
37
+ Install the package directly from PyPI (version 0.3.5):
38
+
39
+ ```bash
40
+ pip install reasoning-deployment-service==0.3.5
41
+ ```
42
+
43
+ ---
44
+
45
+ ## 🚀 First Run
46
+
47
+ Run the CLI tool with no arguments:
48
+
49
+ ```bash
50
+ reasoning-deploy
51
+ ```
52
+
53
+ ### What happens
54
+
55
+ - You will see a list of available modes:
56
+
57
+ ```
58
+ Choose an operation:
59
+ 1) Create/Update
60
+ 2) Auth only
61
+ 3) CLI
62
+ 4) GUI
63
+ q) Quit
64
+ ```
65
+
66
+ - If required files are missing, usage will be limited until you bootstrap the project
67
+ - The tool will guide you to create the starter files:
68
+ - `your_agent_import.py` (shim pointing to your agent folder)
69
+ - `.env.agent` (environment and project settings)
70
+ - `aix_agent.yaml` (agent engine and space definition)
71
+
72
+ Once these are in place, all modes will be fully available
73
+
74
+ ⚠️ Important: You must be running inside the same virtual environment where you installed reasoning-deployment-service
75
+
76
+ ---
77
+
78
+ ## ⚙️ Required Project Structure
79
+
80
+ Your project must have the following at minimum:
81
+
82
+ ```
83
+ my_project/
84
+ ├── requirements.txt # pinned dependencies
85
+ ├── .env.agent # environment and project config
86
+ ├── aix_agent.yaml # engine and agent space definition
87
+ ├── my_agent/ # your agent folder
88
+ │ └── agent.py # defines root_agent
89
+ ```
90
+
91
+ ---
92
+
93
+ ### ✅ requirements.txt
94
+
95
+ This file must be in your project root and contain all dependencies
96
+
97
+ Example:
98
+
99
+ ```text
100
+ google-adk==1.0.0
101
+ google-cloud-aiplatform[adk,agent_engines]>=1.88.0
102
+ google-generativeai>=0.7.0
103
+ pydantic>=2.0.0
104
+ python-dotenv>=1.0.0
105
+ python-docx>=1.1.0
106
+ google-api-python-client>=2.100.0
107
+ google-auth>=2.25.0
108
+ google-auth-httplib2>=0.2.0
109
+ google-auth-oauthlib>=1.2.0
110
+ absl-py>=2.0.0
111
+ PyPDF2>=3.0.0
112
+ deprecated>=1.2.14
113
+ reasoning-deployment-service==0.3.5
114
+ ```
115
+
116
+ Install with:
117
+
118
+ ```bash
119
+ pip install -r requirements.txt
120
+ ```
121
+
122
+ ---
123
+
124
+ ### ✅ my_agent/agent.py
125
+
126
+ Inside your agent folder (for example `my_agent`) you must define a variable called `root_agent`
127
+
128
+ Example:
129
+
130
+ ```python
131
+ from google.adk.agents import LlmAgent
132
+
133
+ root_agent = LlmAgent(
134
+ name="my_agent",
135
+ model="gemini-2.5-flash",
136
+ description="My reasoning agent",
137
+ instruction="You do not do a whole lot yet"
138
+ )
139
+ ```
140
+
141
+ This `root_agent` instance is what gets deployed
142
+ It can be an `LlmAgent`, `Agent`, or any derived class
143
+
144
+ ---
145
+
146
+ ### ✅ .env.agent
147
+
148
+ Environment specific config for DEV and PROD
149
+
150
+ Example:
151
+
152
+ ```env
153
+ DEV_PROJECT_ID=your-dev-project-id
154
+ DEV_PROJECT_NUMBER=123456789012
155
+ DEV_PROJECT_LOCATION=us-central1
156
+ DEV_STAGING_BUCKET=gs://your-dev-staging-bucket
157
+ DEV_AGENT_SPACE_ENGINE=your-dev-agentspace-engine
158
+ DEV_OAUTH_CLIENT_ID=your-dev-oauth-client-id.apps.googleusercontent.com
159
+ DEV_OAUTH_CLIENT_SECRET=your-dev-oauth-client-secret
160
+
161
+ PROD_PROJECT_ID=
162
+ PROD_PROJECT_NUMBER=
163
+ PROD_PROJECT_LOCATION=
164
+ PROD_STAGING_BUCKET=
165
+ PROD_AGENT_SPACE_ENGINE=
166
+ PROD_OAUTH_CLIENT_ID=
167
+ PROD_OAUTH_CLIENT_SECRET=
168
+
169
+ REASONING_DEPLOYMENT_VERSION=0.3.5
170
+
171
+ DEVELOPER=your.name
172
+ ```
173
+
174
+ ---
175
+
176
+ ### ✅ aix_agent.yaml
177
+
178
+ Example starter file:
179
+
180
+ ```yaml
181
+ defaults:
182
+ scopes:
183
+ - https://www.googleapis.com/auth/cloud-platform
184
+ - https://www.googleapis.com/auth/userinfo.email
185
+ metadata:
186
+ reasoning_engine_name: reasoning-engine-dev
187
+ reasoning_engine_description: My Engine
188
+ agent_space_name: My Agent Space
189
+ agent_space_description: Example Agent Space
190
+ agent_space_tool_description: Example Tool
191
+ agent_folder: my_agent
192
+ auth:
193
+ oauth_authorization_id: test_auth
194
+ environment_variables:
195
+ - DEVELOPER
196
+ ```
197
+
198
+ ---
199
+
200
+ ## 🛠 Potential Issues
201
+
202
+ - If you cannot run `reasoning-deploy`, try deleting your venv and making a new one
203
+ - If you see a message saying you can only install on user site-packages, you will most likely need a fresh venv and new requirements install
204
+ - Always ensure you are running `reasoning-deploy` inside the venv where it was installed
205
+
206
+ ---
207
+
208
+ ## ⚠️ Experimental Notice
209
+
210
+ This tool is still experimental
211
+ If it takes longer than 20 to 30 minutes to set up, please go back to what already works for you and send logs and feedback to the development team
@@ -1,28 +1,29 @@
1
1
  reasoning_deployment_service/__init__.py,sha256=xDuKt9gGviQiTV6vXBdkBvygnlAOIrwnUjVaMGZy0L4,670
2
2
  reasoning_deployment_service/reasoning_deployment_service.py,sha256=0QHU2IqqojwFI2wPJ0izrskiHiwBfxdBEB9I_YxYbSA,29133
3
- reasoning_deployment_service/runner.py,sha256=Vv8LYj4E8TQ0vmXoQXpe4Sm0Og-C1OD6JSFd6i6JWkk,3874
3
+ reasoning_deployment_service/runner.py,sha256=e2Uu4DdRWuCHQI4H1x8Q6s1eMbBNoWFkBTnAaspK1tk,5413
4
4
  reasoning_deployment_service/cli_editor/__init__.py,sha256=bN8NPkw8riB92pj2lAwJZuEMOQIO_RRuge0ehnJTW1I,118
5
- reasoning_deployment_service/cli_editor/api_client.py,sha256=PUXPWUslFOksKMrcKjJSK_gVJkTI0bO_bqtCEYeM85g,27897
6
- reasoning_deployment_service/cli_editor/cli_runner.py,sha256=w8jv2ep6TiYfpc_KnlvDOJ9hsCSuZcix4Rv4GQcZO4g,15729
5
+ reasoning_deployment_service/cli_editor/api_client.py,sha256=Kzx5iYp0MmowggrSmPLE7I2kt1-8xvdGBAgde9a1gCY,33681
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
9
  reasoning_deployment_service/cli_editor/reasoning_engine_creator.py,sha256=6QC8Y9yZAT8SYNkT_R00g_SSOYuwEkIxAN9lBG3br2k,19564
10
10
  reasoning_deployment_service/gui_editor/__init__.py,sha256=e5e88iNTk1GC243DRsQFi5E7PqMaT2SXmqOez9FbYzo,128
11
+ reasoning_deployment_service/gui_editor/agent_checkbox_list.py,sha256=ElxFqSgT3iUqDv2U9eR4eV-MfLUHqOXbDz6DqEEevOk,1783
11
12
  reasoning_deployment_service/gui_editor/main.py,sha256=4UzgGUga_xIYIWRVo-80PzhJ1Dlou8PaUXoRiLcLhp8,10914
12
13
  reasoning_deployment_service/gui_editor/src/__init__.py,sha256=q4YBECQGHtHV_TF4MyL36ElWe6tdVgmoNchN1lQU7z4,25
13
14
  reasoning_deployment_service/gui_editor/src/core/__init__.py,sha256=kH-6PxUQ-uVn79ihXN3eLyv_oFhReVsVz-f-kEK_qUo,46
14
- reasoning_deployment_service/gui_editor/src/core/api_client.py,sha256=rGyFKRyBY_MjRsnWxVI1IX1WAYTEfM1V5X3l1iR8GaI,26845
15
+ reasoning_deployment_service/gui_editor/src/core/api_client.py,sha256=-AWhwKxTUM_-s5gOyTECDzEBakJSj3ik4DwXbNL4bDM,30772
15
16
  reasoning_deployment_service/gui_editor/src/core/config.py,sha256=nQT7biTArZl4zsbSZuSD_G7CU7xTJ1bZ4VPfQkxbpX4,1817
16
17
  reasoning_deployment_service/gui_editor/src/core/google_deps.py,sha256=7mMSqtvclJewEBeb-0MevZ7FE0ZEOGHyXh5V73bhpRk,495
17
18
  reasoning_deployment_service/gui_editor/src/core/reasoning_engine_creator.py,sha256=6QC8Y9yZAT8SYNkT_R00g_SSOYuwEkIxAN9lBG3br2k,19564
18
19
  reasoning_deployment_service/gui_editor/src/ui/__init__.py,sha256=262ZiXO6Luk8vZnhCIoYxOtGiny0bXK-BTKjxUNBx-w,43
19
- reasoning_deployment_service/gui_editor/src/ui/agent_space_view.py,sha256=TV2f0_pkw5JCQRWjVPuS-iW92toW41kPJk-pR1C0oqQ,12471
20
- reasoning_deployment_service/gui_editor/src/ui/authorization_view.py,sha256=uiyN411qYe7V2pnPRNdvxRkOifI-aXFI6j2De2RZp1M,10956
20
+ reasoning_deployment_service/gui_editor/src/ui/agent_space_view.py,sha256=UTUMRFEzpUuRONl3K7bsCPRjZ_hiVE1s9fTsIHTZtSs,17130
21
+ reasoning_deployment_service/gui_editor/src/ui/authorization_view.py,sha256=BoNcGRFZ-Rb2pnOAAZxraP7yDdbwMJNvIrBrjMc_hbw,16970
21
22
  reasoning_deployment_service/gui_editor/src/ui/reasoning_engine_view.py,sha256=tCvSPEf4dW0NRdAqfs3yT5Pa873gYeLzCMMIt2r2T4o,14644
22
23
  reasoning_deployment_service/gui_editor/src/ui/reasoning_engines_view.py,sha256=IRjFlBbY98usAZa0roOonjvWQOsF6NBW4bBg_k8KnKI,7860
23
24
  reasoning_deployment_service/gui_editor/src/ui/ui_components.py,sha256=HdQHy-oSZ3GobQ3FNdH7y_w3ANbFiuf2rMoflAmff0A,55366
24
- reasoning_deployment_service-0.3.4.dist-info/METADATA,sha256=SGmPBaX-F-ReutdQgR8vi4fdLKjI9W-r6O2nDrQpTbU,4991
25
- reasoning_deployment_service-0.3.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
26
- reasoning_deployment_service-0.3.4.dist-info/entry_points.txt,sha256=onGKjR5ONTtRv3aqEtK863iw9Ty1kLcjfZlsplkRZrA,84
27
- reasoning_deployment_service-0.3.4.dist-info/top_level.txt,sha256=GKuQS1xHUYLZbatw9DmcYdBxxLhWhhGkV4FmFxgKdp0,29
28
- reasoning_deployment_service-0.3.4.dist-info/RECORD,,
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,,
@@ -1,183 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: reasoning-deployment-service
3
- Version: 0.3.4
4
- Summary: Deployment helper for Vertex AI Reasoning Engines & Agent Spaces
5
- Author-email: Sergio Estrada <sergio.estrada@accenture.com>
6
- License: Apache-2.0
7
- Project-URL: Homepage, https://github.com/AxG-AI-Exchange-GenAI-Initiative/AIXAgentDeploymentService
8
- Project-URL: Repository, https://github.com/AxG-AI-Exchange-GenAI-Initiative/AIXAgentDeploymentService
9
- Project-URL: Issues, https://github.com/AxG-AI-Exchange-GenAI-Initiative/AIXAgentDeploymentService/issues
10
- Requires-Python: >=3.9
11
- Description-Content-Type: text/markdown
12
- Requires-Dist: requests>=2.28
13
- Requires-Dist: python-dotenv>=1.0
14
- Requires-Dist: google-auth>=2.20
15
- Requires-Dist: google-cloud-storage>=2.16
16
- Requires-Dist: google-cloud-aiplatform[adk,agent-engines]>=1.88.0
17
- Requires-Dist: protobuf<7.0.0,>=4.21
18
- Requires-Dist: keyring>=24.0
19
- Requires-Dist: keyrings.google-artifactregistry-auth>=1.1
20
- Requires-Dist: PyYAML>=6.0
21
- Requires-Dist: click>=8.0
22
- Provides-Extra: dev
23
- Requires-Dist: pytest; extra == "dev"
24
- Requires-Dist: build; extra == "dev"
25
- Requires-Dist: twine; extra == "dev"
26
- Requires-Dist: black; extra == "dev"
27
- Requires-Dist: ruff; extra == "dev"
28
-
29
- # Reasoning Deployment Service
30
-
31
- A comprehensive service for deploying reasoning agents with CLI and GUI editors.
32
-
33
- ## Installation
34
-
35
- ### Basic Installation (Core deployment service only)
36
-
37
- ```bash
38
- pip install reasoning-deployment-service
39
- ```
40
-
41
- ### Installation with GUI Editor
42
-
43
- ```bash
44
- pip install "reasoning-deployment-service[gui]"
45
- ```
46
-
47
- ### Installation with CLI Editor
48
-
49
- ```bash
50
- pip install "reasoning-deployment-service[cli]"
51
- ```
52
-
53
- ### Full Installation (Everything)
54
-
55
- ```bash
56
- pip install "reasoning-deployment-service[full]"
57
- ```
58
-
59
- ## Quick Start
60
-
61
- ### Using the Deploy Script (Recommended)
62
-
63
- The easiest way to get started is using the provided deploy script:
64
-
65
- ```bash
66
- # Download and run the deploy script
67
- curl -O https://raw.githubusercontent.com/your-org/AIXAgentDeploymentService/main/deploy.sh
68
- chmod +x deploy.sh
69
- ./deploy.sh
70
- ```
71
-
72
- The deploy script will:
73
-
74
- - Set up a virtual environment
75
- - Install all necessary dependencies
76
- - Create a `deploy.py` file configured for your agent
77
- - Run the deployment
78
-
79
- ## Manual Usage
80
-
81
- ### Core Deployment Service
82
-
83
- The main deployment service for reasoning engines:
84
-
85
- ```python
86
- from reasoning_deployment_service import ReasoningEngineDeploymentService
87
- from google.adk.agents import BaseAgent
88
-
89
- # Your agent implementation
90
- class MyAgent(BaseAgent):
91
- # ... your agent implementation
92
-
93
- # Deploy using the service
94
- agent = MyAgent()
95
- deployment_service = ReasoningEngineDeploymentService(
96
- root_agent=agent,
97
- deployment_environment="DEV" # or "PROD"
98
- )
99
-
100
- # Deploy everything
101
- deployment_service.one_deployment_with_everything_on_it()
102
- ```
103
-
104
- ### CLI Editor (Optional)
105
-
106
- For command-line management of reasoning engines and agent spaces:
107
-
108
- ```python
109
- from reasoning_deployment_service.cli_editor import CLIRunner
110
-
111
- # Start the CLI interface
112
- cli = CLIRunner()
113
- cli.run()
114
- ```
115
-
116
- ### GUI Editor (Optional)
117
-
118
- For graphical management interface:
119
-
120
- ```python
121
- from reasoning_deployment_service.gui_editor import GUIEditor
122
-
123
- # Start the GUI application
124
- app = GUIEditor()
125
- app.mainloop()
126
- ```
127
-
128
- ## Configuration
129
-
130
- Create a `.env` file with your deployment environment variables:
131
-
132
- ```bash
133
- # Development Profile
134
- DEV_PROJECT_ID=your-project-id
135
- DEV_PROJECT_NUMBER=your-project-number
136
- DEV_PROJECT_LOCATION=us-central1
137
- DEV_STAGING_BUCKET=gs://your-staging-bucket
138
- DEV_AGENT_SPACE_ENGINE=your-agent-space-engine
139
- DEV_OAUTH_CLIENT_ID=your-oauth-client-id
140
- DEV_OAUTH_CLIENT_SECRET=your-oauth-client-secret
141
-
142
- # Production Profile
143
- PROD_PROJECT_ID=your-prod-project-id
144
- PROD_PROJECT_NUMBER=your-prod-project-number
145
- # ... etc
146
- ```
147
-
148
- Create an `aix_agent.yaml` file with your agent metadata:
149
-
150
- ```yaml
151
- defaults:
152
- scopes:
153
- - "https://www.googleapis.com/auth/cloud-platform"
154
- metadata:
155
- reasoning_engine_name: "my-reasoning-engine"
156
- reasoning_engine_description: "My custom reasoning engine"
157
- agent_space_name: "my-agent-space"
158
- agent_space_description: "My agent space"
159
- agent_space_tool_description: "Tool description"
160
- auth:
161
- oauth_authorization_id: "my-auth-id"
162
- environment_variables:
163
- - "CUSTOM_VAR=value"
164
- ```
165
-
166
- ## Dependency Management
167
-
168
- Dependencies are now centrally managed in `setup.py`:
169
-
170
- - **Core dependencies**: Installed automatically with the base package
171
- - **GUI dependencies**: Install with `[gui]` extra for tkinter-based interface
172
- - **CLI dependencies**: Install with `[cli]` extra (currently no additional deps)
173
- - **Full dependencies**: Install with `[full]` extra for complete functionality
174
-
175
- ## Modular Usage
176
-
177
- Each component can be used independently:
178
-
179
- - **Core Service**: Just use `ReasoningEngineDeploymentService` for programmatic deployment
180
- - **CLI Editor**: Add `cli_editor` for command-line management
181
- - **GUI Editor**: Add `gui_editor` for graphical interface
182
-
183
- The package is designed so users can import only what they need and instantiate the components they want to use.