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.
- reasoning_deployment_service/cli_editor/api_client.py +135 -1
- reasoning_deployment_service/cli_editor/cli_runner.py +46 -0
- reasoning_deployment_service/gui_editor/agent_checkbox_list.py +48 -0
- reasoning_deployment_service/gui_editor/src/core/api_client.py +88 -0
- reasoning_deployment_service/gui_editor/src/ui/agent_space_view.py +99 -7
- reasoning_deployment_service/gui_editor/src/ui/authorization_view.py +127 -13
- reasoning_deployment_service/runner.py +45 -7
- reasoning_deployment_service-0.3.6.dist-info/METADATA +211 -0
- {reasoning_deployment_service-0.3.4.dist-info → reasoning_deployment_service-0.3.6.dist-info}/RECORD +12 -11
- reasoning_deployment_service-0.3.4.dist-info/METADATA +0 -183
- {reasoning_deployment_service-0.3.4.dist-info → reasoning_deployment_service-0.3.6.dist-info}/WHEEL +0 -0
- {reasoning_deployment_service-0.3.4.dist-info → reasoning_deployment_service-0.3.6.dist-info}/entry_points.txt +0 -0
- {reasoning_deployment_service-0.3.4.dist-info → reasoning_deployment_service-0.3.6.dist-info}/top_level.txt +0 -0
|
@@ -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
|
-
|
|
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
|
-
# ---
|
|
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
|
-
|
|
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
|
-
|
|
81
|
-
|
|
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
|
{reasoning_deployment_service-0.3.4.dist-info → reasoning_deployment_service-0.3.6.dist-info}/RECORD
RENAMED
|
@@ -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=
|
|
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=
|
|
6
|
-
reasoning_deployment_service/cli_editor/cli_runner.py,sha256=
|
|
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
|
|
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=
|
|
20
|
-
reasoning_deployment_service/gui_editor/src/ui/authorization_view.py,sha256=
|
|
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.
|
|
25
|
-
reasoning_deployment_service-0.3.
|
|
26
|
-
reasoning_deployment_service-0.3.
|
|
27
|
-
reasoning_deployment_service-0.3.
|
|
28
|
-
reasoning_deployment_service-0.3.
|
|
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.
|
{reasoning_deployment_service-0.3.4.dist-info → reasoning_deployment_service-0.3.6.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|