reasoning-deployment-service 0.2.9__py3-none-any.whl → 0.3.2__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/__init__.py +25 -0
- reasoning_deployment_service/gui_editor/requirements_minimal.txt +54 -0
- reasoning_deployment_service/gui_editor/run_program.sh +55 -0
- reasoning_deployment_service/reasoning_deployment_service.py +687 -0
- reasoning_deployment_service/runner.py +122 -0
- {reasoning_deployment_service-0.2.9.dist-info → reasoning_deployment_service-0.3.2.dist-info}/METADATA +9 -1
- reasoning_deployment_service-0.3.2.dist-info/RECORD +30 -0
- reasoning_deployment_service-0.3.2.dist-info/entry_points.txt +2 -0
- reasoning_deployment_service-0.3.2.dist-info/top_level.txt +1 -0
- reasoning_deployment_service-0.2.9.dist-info/RECORD +0 -24
- reasoning_deployment_service-0.2.9.dist-info/top_level.txt +0 -2
- {cli_editor → reasoning_deployment_service/cli_editor}/__init__.py +0 -0
- {cli_editor → reasoning_deployment_service/cli_editor}/api_client.py +0 -0
- {cli_editor → reasoning_deployment_service/cli_editor}/cli_runner.py +0 -0
- {cli_editor → reasoning_deployment_service/cli_editor}/config.py +0 -0
- {cli_editor → reasoning_deployment_service/cli_editor}/google_deps.py +0 -0
- {cli_editor → reasoning_deployment_service/cli_editor}/reasoning_engine_creator.py +0 -0
- {gui_editor → reasoning_deployment_service/gui_editor}/__init__.py +0 -0
- {gui_editor → reasoning_deployment_service/gui_editor}/main.py +0 -0
- {gui_editor → reasoning_deployment_service/gui_editor}/src/__init__.py +0 -0
- {gui_editor → reasoning_deployment_service/gui_editor}/src/core/__init__.py +0 -0
- {gui_editor → reasoning_deployment_service/gui_editor}/src/core/api_client.py +0 -0
- {gui_editor → reasoning_deployment_service/gui_editor}/src/core/config.py +0 -0
- {gui_editor → reasoning_deployment_service/gui_editor}/src/core/google_deps.py +0 -0
- {gui_editor → reasoning_deployment_service/gui_editor}/src/core/reasoning_engine_creator.py +0 -0
- {gui_editor → reasoning_deployment_service/gui_editor}/src/ui/__init__.py +0 -0
- {gui_editor → reasoning_deployment_service/gui_editor}/src/ui/agent_space_view.py +0 -0
- {gui_editor → reasoning_deployment_service/gui_editor}/src/ui/authorization_view.py +0 -0
- {gui_editor → reasoning_deployment_service/gui_editor}/src/ui/reasoning_engine_view.py +0 -0
- {gui_editor → reasoning_deployment_service/gui_editor}/src/ui/reasoning_engines_view.py +0 -0
- {gui_editor → reasoning_deployment_service/gui_editor}/src/ui/ui_components.py +0 -0
- {reasoning_deployment_service-0.2.9.dist-info → reasoning_deployment_service-0.3.2.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Initialize the reasoning_deployment_service package
|
|
2
|
+
|
|
3
|
+
from .reasoning_deployment_service import ReasoningEngineDeploymentService
|
|
4
|
+
|
|
5
|
+
# Import submodules for optional use
|
|
6
|
+
try:
|
|
7
|
+
from . import cli_editor
|
|
8
|
+
from . import gui_editor
|
|
9
|
+
CLI_AVAILABLE = True
|
|
10
|
+
GUI_AVAILABLE = True
|
|
11
|
+
except ImportError as e:
|
|
12
|
+
print(f"Warning: Some optional components not available: {e}")
|
|
13
|
+
CLI_AVAILABLE = False
|
|
14
|
+
GUI_AVAILABLE = False
|
|
15
|
+
|
|
16
|
+
# Make main classes available for direct import
|
|
17
|
+
__all__ = [
|
|
18
|
+
'ReasoningEngineDeploymentService',
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
# Add optional components if available
|
|
22
|
+
if CLI_AVAILABLE:
|
|
23
|
+
__all__.append('cli_editor')
|
|
24
|
+
if GUI_AVAILABLE:
|
|
25
|
+
__all__.append('gui_editor')
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Bare minimum requirements for Agent Space Deployment Service
|
|
2
|
+
# Core functionality dependencies only
|
|
3
|
+
|
|
4
|
+
# Google Cloud & Vertex AI (CORE REQUIREMENT)
|
|
5
|
+
google-cloud-aiplatform==1.108.0
|
|
6
|
+
vertexai==1.43.0
|
|
7
|
+
google-auth==2.40.3
|
|
8
|
+
google-auth-oauthlib==1.2.2
|
|
9
|
+
google-api-python-client==2.178.0
|
|
10
|
+
|
|
11
|
+
# Environment management (CORE REQUIREMENT)
|
|
12
|
+
python-dotenv==1.1.1
|
|
13
|
+
|
|
14
|
+
# HTTP requests (CORE REQUIREMENT)
|
|
15
|
+
requests==2.32.4
|
|
16
|
+
|
|
17
|
+
# Google Cloud dependencies (CORE REQUIREMENT)
|
|
18
|
+
google-api-core==2.25.1
|
|
19
|
+
googleapis-common-protos==1.70.0
|
|
20
|
+
grpcio==1.74.0
|
|
21
|
+
protobuf==6.31.1
|
|
22
|
+
|
|
23
|
+
# Authentication dependencies (CORE REQUIREMENT)
|
|
24
|
+
google-auth-httplib2==0.2.0
|
|
25
|
+
cachetools==5.5.2
|
|
26
|
+
pyasn1==0.6.1
|
|
27
|
+
pyasn1_modules==0.4.2
|
|
28
|
+
rsa==4.9.1
|
|
29
|
+
|
|
30
|
+
# Core Python utilities (CORE REQUIREMENT)
|
|
31
|
+
certifi==2025.8.3
|
|
32
|
+
urllib3==2.5.0
|
|
33
|
+
six==1.17.0
|
|
34
|
+
|
|
35
|
+
# OPTIONAL: For enhanced functionality
|
|
36
|
+
# Uncomment if you need these features:
|
|
37
|
+
|
|
38
|
+
# For MCP server support:
|
|
39
|
+
# mcp==1.12.4
|
|
40
|
+
|
|
41
|
+
# For FastAPI endpoints (if you plan to add web interface):
|
|
42
|
+
# fastapi==0.116.1
|
|
43
|
+
# uvicorn==0.35.0
|
|
44
|
+
# pydantic==2.11.7
|
|
45
|
+
|
|
46
|
+
# For document processing in agents:
|
|
47
|
+
# PyPDF2==3.0.1
|
|
48
|
+
# python-docx==1.2.0
|
|
49
|
+
|
|
50
|
+
# For web scraping/HTTP in agents:
|
|
51
|
+
# httpx==0.28.1
|
|
52
|
+
|
|
53
|
+
# For YAML configuration:
|
|
54
|
+
# PyYAML==6.0.2
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
VENV_DIR=".venv"
|
|
5
|
+
|
|
6
|
+
# Detect Python executable
|
|
7
|
+
if command -v python3 &>/dev/null; then
|
|
8
|
+
PYTHON=python3
|
|
9
|
+
elif command -v python &>/dev/null; then
|
|
10
|
+
PYTHON=python
|
|
11
|
+
else
|
|
12
|
+
echo "❌ Python is not installed. Please install Python 3.9+."
|
|
13
|
+
exit 1
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
# Create venv if missing
|
|
17
|
+
if [ ! -d "$VENV_DIR" ]; then
|
|
18
|
+
echo "🔧 Creating virtual environment..."
|
|
19
|
+
$PYTHON -m venv "$VENV_DIR"
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
# Activate venv (POSIX)
|
|
23
|
+
if [ -f "$VENV_DIR/bin/activate" ]; then
|
|
24
|
+
source "$VENV_DIR/bin/activate"
|
|
25
|
+
# Activate venv (Windows Git Bash or Cygwin)
|
|
26
|
+
elif [ -f "$VENV_DIR/Scripts/activate" ]; then
|
|
27
|
+
source "$VENV_DIR/Scripts/activate"
|
|
28
|
+
else
|
|
29
|
+
echo "❌ Could not find venv activation script."
|
|
30
|
+
exit 1
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# Install Tkinter if on Linux
|
|
34
|
+
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
|
35
|
+
echo "🖼 Installing Tkinter for Linux..."
|
|
36
|
+
if command -v apt-get &>/dev/null; then
|
|
37
|
+
sudo apt-get update && sudo apt-get install -y python3-tk
|
|
38
|
+
elif command -v dnf &>/dev/null; then
|
|
39
|
+
sudo dnf install -y python3-tkinter
|
|
40
|
+
elif command -v pacman &>/dev/null; then
|
|
41
|
+
sudo pacman -S --noconfirm tk
|
|
42
|
+
else
|
|
43
|
+
echo "⚠️ Could not determine package manager. Install python3-tk manually."
|
|
44
|
+
fi
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# Install dependencies
|
|
48
|
+
echo "📦 Installing dependencies..."
|
|
49
|
+
pip3 install --upgrade pip
|
|
50
|
+
# Install the reasoning deployment service with GUI extras
|
|
51
|
+
pip3 install "reasoning-deployment-service[gui]"
|
|
52
|
+
|
|
53
|
+
# Run your app
|
|
54
|
+
echo "🚀 Starting app..."
|
|
55
|
+
$PYTHON main.py
|
|
@@ -0,0 +1,687 @@
|
|
|
1
|
+
import json, os, subprocess, yaml, sys
|
|
2
|
+
import urllib.parse, vertexai, google.auth
|
|
3
|
+
import requests as _requests
|
|
4
|
+
from typing import Tuple
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from dotenv import load_dotenv
|
|
7
|
+
from vertexai import agent_engines
|
|
8
|
+
from google.adk.agents import BaseAgent
|
|
9
|
+
from google.auth.transport.requests import Request as GoogleAuthRequest
|
|
10
|
+
from google.api_core.exceptions import NotFound
|
|
11
|
+
import logging
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
|
|
14
|
+
DISCOVERY_ENGINE_URL = "https://discoveryengine.googleapis.com/v1alpha"
|
|
15
|
+
|
|
16
|
+
class ReasoningEngineDeploymentService:
|
|
17
|
+
def __init__(self, root_agent: BaseAgent, deployment_environment: str="DEV"):
|
|
18
|
+
# Setup logging
|
|
19
|
+
self._setup_logging()
|
|
20
|
+
|
|
21
|
+
self._check_required_files_exist()
|
|
22
|
+
load_dotenv(dotenv_path=".env.agent", override=True)
|
|
23
|
+
|
|
24
|
+
self.root_agent = root_agent
|
|
25
|
+
self.deployment_env = deployment_environment
|
|
26
|
+
self.attempt_to_use_existing_auth = False
|
|
27
|
+
|
|
28
|
+
self._staging_bucket = None
|
|
29
|
+
self._project_id = None
|
|
30
|
+
self._project_number = None
|
|
31
|
+
self._project_location = None
|
|
32
|
+
self._oauth_client_id = None
|
|
33
|
+
self._oauth_client_secret = None
|
|
34
|
+
self._agent_space_engine = None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
self._load_agent_definition()
|
|
38
|
+
self._load_deployment_environment_variables(deployment_environment=deployment_environment)
|
|
39
|
+
self._check_requirements_file_present()
|
|
40
|
+
|
|
41
|
+
self._http = _requests.Session()
|
|
42
|
+
self._http.headers.update({"Content-Type": "application/json"})
|
|
43
|
+
self.authenticate()
|
|
44
|
+
|
|
45
|
+
def _setup_logging(self):
|
|
46
|
+
os.makedirs("logs", exist_ok=True)
|
|
47
|
+
|
|
48
|
+
# Setup logger
|
|
49
|
+
self.logger = logging.getLogger("ReasoningEngineDeployment")
|
|
50
|
+
self.logger.setLevel(logging.DEBUG)
|
|
51
|
+
self.logger.propagate = False
|
|
52
|
+
|
|
53
|
+
if not self.logger.handlers:
|
|
54
|
+
self.log_filename = f"logs/deployment_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log"
|
|
55
|
+
file_handler = logging.FileHandler(self.log_filename)
|
|
56
|
+
file_handler.setLevel(logging.DEBUG)
|
|
57
|
+
|
|
58
|
+
console_handler = logging.StreamHandler()
|
|
59
|
+
console_handler.setLevel(logging.INFO)
|
|
60
|
+
|
|
61
|
+
file_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
|
62
|
+
file_handler.setFormatter(file_formatter)
|
|
63
|
+
|
|
64
|
+
console_formatter = logging.Formatter('%(message)s')
|
|
65
|
+
console_handler.setFormatter(console_formatter)
|
|
66
|
+
|
|
67
|
+
self.logger.addHandler(file_handler)
|
|
68
|
+
self.logger.addHandler(console_handler)
|
|
69
|
+
|
|
70
|
+
logging.getLogger('google').setLevel(logging.ERROR)
|
|
71
|
+
logging.getLogger('urllib3').setLevel(logging.ERROR)
|
|
72
|
+
logging.getLogger('requests').setLevel(logging.ERROR)
|
|
73
|
+
|
|
74
|
+
def _log_record_file(self, level="ERROR"):
|
|
75
|
+
"""Log the current deployment record file contents to file only."""
|
|
76
|
+
try:
|
|
77
|
+
record = self._read_engine_deployment_record()
|
|
78
|
+
if record:
|
|
79
|
+
record_str = json.dumps(record, indent=2)
|
|
80
|
+
# Write directly to the same log file without terminal output
|
|
81
|
+
with open(self.log_filename, 'a') as f:
|
|
82
|
+
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S,%f')[:-3]
|
|
83
|
+
f.write(f"{timestamp} - ReasoningEngineDeployment - {level} - Current deployment record:\n{record_str}\n")
|
|
84
|
+
except Exception as e:
|
|
85
|
+
self.logger.error(f"Failed to read deployment record: {e}")
|
|
86
|
+
|
|
87
|
+
def error(self, message: str):
|
|
88
|
+
self.logger.error(f"[DEPLOYMENT SERVICE: CRITICAL FAILURE]: {message}")
|
|
89
|
+
self._log_record_file("ERROR")
|
|
90
|
+
|
|
91
|
+
def warning(self, message: str):
|
|
92
|
+
self.logger.warning(f"[DEPLOYMENT SERVICE: WARNING]: {message}")
|
|
93
|
+
|
|
94
|
+
def info(self, message: str):
|
|
95
|
+
self.logger.info(f"[DEPLOYMENT SERVICE: INFO]: {message}")
|
|
96
|
+
|
|
97
|
+
def _check_required_files_exist(self):
|
|
98
|
+
end_run = False
|
|
99
|
+
if not os.path.exists(".env.agent"):
|
|
100
|
+
self.warning("Creating .env.agent file ... done")
|
|
101
|
+
self._generate_env_agent()
|
|
102
|
+
end_run = True
|
|
103
|
+
|
|
104
|
+
if not os.path.exists("aix_agent.yaml"):
|
|
105
|
+
self._generate_example_yaml_config()
|
|
106
|
+
self.warning("Creating aix_agent.yaml file ... done")
|
|
107
|
+
end_run = True
|
|
108
|
+
|
|
109
|
+
self.warning("Please fill in the required values in the generated files and re-run the deployment.")
|
|
110
|
+
|
|
111
|
+
if end_run:
|
|
112
|
+
sys.exit(1)
|
|
113
|
+
|
|
114
|
+
def _access_token(self) -> str:
|
|
115
|
+
"""Live: fetch ADC access token; raises if not available."""
|
|
116
|
+
creds, _ = google.auth.default(scopes=["https://www.googleapis.com/auth/cloud-platform"])
|
|
117
|
+
|
|
118
|
+
if not creds.valid or (creds.expired and creds.refresh_token):
|
|
119
|
+
creds.refresh(GoogleAuthRequest())
|
|
120
|
+
return creds.token
|
|
121
|
+
|
|
122
|
+
def authenticate(self) -> bool:
|
|
123
|
+
try:
|
|
124
|
+
_ = self._access_token()
|
|
125
|
+
return True
|
|
126
|
+
except Exception:
|
|
127
|
+
pass
|
|
128
|
+
|
|
129
|
+
try:
|
|
130
|
+
subprocess.run(["gcloud", "--version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
|
|
131
|
+
except Exception:
|
|
132
|
+
raise RuntimeError("'gcloud' not found on PATH. Install Google Cloud SDK.")
|
|
133
|
+
|
|
134
|
+
proc = subprocess.run(
|
|
135
|
+
["gcloud", "auth", "application-default", "login"],
|
|
136
|
+
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True
|
|
137
|
+
)
|
|
138
|
+
if proc.returncode != 0:
|
|
139
|
+
raise RuntimeError(f"ADC auth failed:\n{proc.stdout}")
|
|
140
|
+
|
|
141
|
+
_ = self._access_token()
|
|
142
|
+
return True
|
|
143
|
+
|
|
144
|
+
def _get_deployment_record_path(self):
|
|
145
|
+
return Path("deployments") / f"{self.deployment_env}_{self._reasoning_engine_name}.json"
|
|
146
|
+
|
|
147
|
+
def _read_engine_deployment_record(self) -> dict:
|
|
148
|
+
file_path = self._get_deployment_record_path()
|
|
149
|
+
if os.path.exists(file_path):
|
|
150
|
+
with open(file_path, "r") as f:
|
|
151
|
+
return json.load(f) or {}
|
|
152
|
+
return {}
|
|
153
|
+
|
|
154
|
+
def _write_engine_deployment(self, json_payload: dict):
|
|
155
|
+
if not os.path.exists("deployments"):
|
|
156
|
+
self.info(f"Creating deployments directory")
|
|
157
|
+
os.makedirs("deployments")
|
|
158
|
+
|
|
159
|
+
file_path = self._get_deployment_record_path()
|
|
160
|
+
read_engine_deployment = self._read_engine_deployment_record()
|
|
161
|
+
|
|
162
|
+
if read_engine_deployment:
|
|
163
|
+
read_engine_deployment.update(json_payload)
|
|
164
|
+
|
|
165
|
+
with open(file_path, "w") as f:
|
|
166
|
+
json.dump(read_engine_deployment, f, sort_keys=False)
|
|
167
|
+
|
|
168
|
+
self.info(f"Successfully wrote entry {json_payload} to {file_path}")
|
|
169
|
+
return
|
|
170
|
+
|
|
171
|
+
with open(file_path, "w") as f:
|
|
172
|
+
self.info(f"Successfully created {file_path}")
|
|
173
|
+
json.dump(json_payload, f, sort_keys=False)
|
|
174
|
+
self.info(f"Successfully wrote entry {json_payload} to {file_path}")
|
|
175
|
+
|
|
176
|
+
def _generate_env_agent(self, path: str | Path = ".env.agent", overwrite: bool = False) -> Path:
|
|
177
|
+
"""
|
|
178
|
+
Generate a .env.agent template file with all deployment profile
|
|
179
|
+
and app environment variables, left empty for later filling.
|
|
180
|
+
"""
|
|
181
|
+
path = Path(path)
|
|
182
|
+
if path.exists() and not overwrite:
|
|
183
|
+
raise FileExistsError(f"{path} already exists. Pass overwrite=True to replace it.")
|
|
184
|
+
|
|
185
|
+
template = """#===================== **** DEPLOYMENT PROFILE **** =====================
|
|
186
|
+
# Development Profile
|
|
187
|
+
DEV_PROJECT_ID=
|
|
188
|
+
DEV_PROJECT_NUMBER=
|
|
189
|
+
DEV_PROJECT_LOCATION=
|
|
190
|
+
DEV_STAGING_BUCKET=
|
|
191
|
+
DEV_AGENT_SPACE_ENGINE=
|
|
192
|
+
DEV_API_TOKEN=
|
|
193
|
+
DEV_OAUTH_CLIENT_ID=
|
|
194
|
+
DEV_OAUTH_CLIENT_SECRET=
|
|
195
|
+
|
|
196
|
+
# Production Profile
|
|
197
|
+
PROD_PROJECT_ID=
|
|
198
|
+
PROD_PROJECT_NUMBER=
|
|
199
|
+
PROD_PROJECT_LOCATION=
|
|
200
|
+
PROD_STAGING_BUCKET=
|
|
201
|
+
PROD_AGENT_SPACE_ENGINE=
|
|
202
|
+
PROD_API_TOKEN=
|
|
203
|
+
PROD_OAUTH_CLIENT_ID=
|
|
204
|
+
PROD_OAUTH_CLIENT_SECRET=
|
|
205
|
+
|
|
206
|
+
REASONING_DEPLOYMENT_VERSION=0.2.4
|
|
207
|
+
#===================== **** DEPLOYMENT PROFILE **** =====================
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
#===================== **** YOUR APP ENV VARIABLES **** =====================
|
|
211
|
+
DEVELOPER=
|
|
212
|
+
#===================== **** YOUR APP ENV VARIABLES **** =====================
|
|
213
|
+
"""
|
|
214
|
+
|
|
215
|
+
path.write_text(template.strip() + "\n")
|
|
216
|
+
|
|
217
|
+
# Also update .gitignore to include logs
|
|
218
|
+
self._update_gitignore()
|
|
219
|
+
|
|
220
|
+
return path
|
|
221
|
+
|
|
222
|
+
def _update_gitignore(self):
|
|
223
|
+
"""Add common ignore patterns to .gitignore."""
|
|
224
|
+
gitignore_path = Path(".gitignore")
|
|
225
|
+
patterns_to_add = [
|
|
226
|
+
"logs/",
|
|
227
|
+
"deployments/",
|
|
228
|
+
".env.agent",
|
|
229
|
+
"__pycache__/",
|
|
230
|
+
"*.pyc",
|
|
231
|
+
".venv/",
|
|
232
|
+
"deploy_env/"
|
|
233
|
+
]
|
|
234
|
+
|
|
235
|
+
existing_patterns = set()
|
|
236
|
+
if gitignore_path.exists():
|
|
237
|
+
existing_patterns = set(gitignore_path.read_text().splitlines())
|
|
238
|
+
|
|
239
|
+
new_patterns = [p for p in patterns_to_add if p not in existing_patterns]
|
|
240
|
+
|
|
241
|
+
if new_patterns:
|
|
242
|
+
with open(gitignore_path, "a") as f:
|
|
243
|
+
if existing_patterns: # Add newline if file has content
|
|
244
|
+
f.write("\n")
|
|
245
|
+
f.write("# Added by ReasoningEngineDeploymentService\n")
|
|
246
|
+
for pattern in new_patterns:
|
|
247
|
+
f.write(f"{pattern}\n")
|
|
248
|
+
|
|
249
|
+
def _generate_example_yaml_config(self, path: str | Path = "aix_agent.yaml", overwrite: bool = False) -> Path:
|
|
250
|
+
"""
|
|
251
|
+
Create an example YAML config matching the requested schema.
|
|
252
|
+
|
|
253
|
+
Structure:
|
|
254
|
+
defaults:
|
|
255
|
+
scopes: [ ... ]
|
|
256
|
+
metadata: { ... }
|
|
257
|
+
auth: { ... }
|
|
258
|
+
environment_variables: [ ... ]
|
|
259
|
+
"""
|
|
260
|
+
path = Path(path)
|
|
261
|
+
if path.exists() and not overwrite:
|
|
262
|
+
raise FileExistsError(f"{path} already exists. Pass overwrite=True to replace it.")
|
|
263
|
+
|
|
264
|
+
config = {
|
|
265
|
+
"defaults": {
|
|
266
|
+
"scopes": [
|
|
267
|
+
"https://www.googleapis.com/auth/cloud-platform",
|
|
268
|
+
"https://www.googleapis.com/auth/userinfo.email",
|
|
269
|
+
],
|
|
270
|
+
"metadata": {
|
|
271
|
+
"reasoning_engine_name": "reasoning-engine-dev",
|
|
272
|
+
"reasoning_engine_description": "A reasoning engine for development",
|
|
273
|
+
"agent_space_name": "Agent Space Dev Numba Three!",
|
|
274
|
+
"agent_space_description": "Agent spece description, lets go",
|
|
275
|
+
"agent_space_tool_description": "Agent space tool description",
|
|
276
|
+
},
|
|
277
|
+
"agent_folder": "your_agent_folder",
|
|
278
|
+
"auth": {
|
|
279
|
+
"oauth_authorization_id": "test_auth_three",
|
|
280
|
+
},
|
|
281
|
+
"environment_variables": [
|
|
282
|
+
"DEVELOPER",
|
|
283
|
+
],
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
path.write_text(yaml.safe_dump(config, sort_keys=False))
|
|
288
|
+
return path
|
|
289
|
+
|
|
290
|
+
def _load_agent_definition(self):
|
|
291
|
+
try:
|
|
292
|
+
with open("aix_agent.yaml", "r") as f:
|
|
293
|
+
config = yaml.safe_load(f)
|
|
294
|
+
except FileNotFoundError:
|
|
295
|
+
self._generate_example_yaml_config()
|
|
296
|
+
self.error("Could not locate a valid aix_agent.yaml file. Generating example file in your directory.")
|
|
297
|
+
sys.exit(1)
|
|
298
|
+
|
|
299
|
+
try:
|
|
300
|
+
config = config['defaults']
|
|
301
|
+
scopes = config['scopes']
|
|
302
|
+
metadata = config['metadata']
|
|
303
|
+
agent_folder = config['agent_folder']
|
|
304
|
+
auth = config['auth']
|
|
305
|
+
environment_variables = config['environment_variables']
|
|
306
|
+
|
|
307
|
+
reasoning_engine_name = metadata['reasoning_engine_name']
|
|
308
|
+
reasoning_engine_description = metadata['reasoning_engine_description']
|
|
309
|
+
agent_space_name = metadata['agent_space_name']
|
|
310
|
+
agent_space_description = metadata['agent_space_description']
|
|
311
|
+
agent_space_tool_description = metadata["agent_space_tool_description"]
|
|
312
|
+
|
|
313
|
+
self._required_scopes = scopes
|
|
314
|
+
self._agent_folder = agent_folder
|
|
315
|
+
self._reasoning_engine_name = reasoning_engine_name
|
|
316
|
+
self._reasoning_engine_description = reasoning_engine_description
|
|
317
|
+
self._agent_space_name = agent_space_name
|
|
318
|
+
self._agent_space_description = agent_space_description
|
|
319
|
+
self._agent_space_tool_description = agent_space_tool_description
|
|
320
|
+
self._authorization_id = auth.get("oauth_authorization_id", None)
|
|
321
|
+
self._environment_variables = environment_variables or []
|
|
322
|
+
except KeyError as e:
|
|
323
|
+
raise RuntimeError(f"Missing required key in aix_agent.yaml: {e}")
|
|
324
|
+
|
|
325
|
+
def _load_deployment_environment_variables(self, deployment_environment: str):
|
|
326
|
+
required_vars = ['PROJECT_ID', 'PROJECT_NUMBER', 'PROJECT_LOCATION', 'STAGING_BUCKET', 'AGENT_SPACE_ENGINE']
|
|
327
|
+
|
|
328
|
+
for var in required_vars:
|
|
329
|
+
env_var = f"{deployment_environment}_{var}"
|
|
330
|
+
if env_var not in os.environ or not os.getenv(env_var):
|
|
331
|
+
raise RuntimeError(f"Missing required environment variable: {env_var}.")
|
|
332
|
+
|
|
333
|
+
setattr(self, f"_{var.lower()}", os.getenv(env_var))
|
|
334
|
+
|
|
335
|
+
if self._authorization_id:
|
|
336
|
+
required_auth_vars = ['OAUTH_CLIENT_ID', 'OAUTH_CLIENT_SECRET']
|
|
337
|
+
|
|
338
|
+
for var in required_auth_vars:
|
|
339
|
+
env_var = f"{deployment_environment}_{var}"
|
|
340
|
+
if env_var not in os.environ or not os.getenv(env_var):
|
|
341
|
+
raise RuntimeError(f"Missing required environment variable: {env_var}")
|
|
342
|
+
|
|
343
|
+
setattr(self, f"_{var.lower()}", os.getenv(env_var))
|
|
344
|
+
|
|
345
|
+
def _check_requirements_file_present(self):
|
|
346
|
+
if not os.path.exists("requirements.txt"):
|
|
347
|
+
raise RuntimeError("Missing requirements.txt file")
|
|
348
|
+
|
|
349
|
+
def _load_requirements(self):
|
|
350
|
+
with open("requirements.txt", "r") as f:
|
|
351
|
+
return [line.strip() for line in f if line.strip()]
|
|
352
|
+
|
|
353
|
+
def create_reasoning_engine(self):
|
|
354
|
+
vertexai.init(
|
|
355
|
+
project=self._project_id,
|
|
356
|
+
location=self._project_location,
|
|
357
|
+
staging_bucket=self._staging_bucket,
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
creation = agent_engines.create(
|
|
361
|
+
self.root_agent,
|
|
362
|
+
display_name=self._reasoning_engine_name,
|
|
363
|
+
description=self._reasoning_engine_description,
|
|
364
|
+
requirements=self._load_requirements(),
|
|
365
|
+
env_vars=self._environment_variables,
|
|
366
|
+
extra_packages=[f"./{self._agent_folder}"]
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
if creation:
|
|
370
|
+
self.info(f"Reasoning engine created successfully: {creation.resource_name}")
|
|
371
|
+
|
|
372
|
+
json_payload = {"reasoning_engine_id": creation.resource_name}
|
|
373
|
+
self._write_engine_deployment(json_payload)
|
|
374
|
+
|
|
375
|
+
def update_reasoning_engine(self, reasoning_engine_id):
|
|
376
|
+
vertexai.init(
|
|
377
|
+
project=self._project_id,
|
|
378
|
+
location=self._project_location,
|
|
379
|
+
staging_bucket=self._staging_bucket,
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
try:
|
|
383
|
+
updating = agent_engines.update(
|
|
384
|
+
resource_name=reasoning_engine_id,
|
|
385
|
+
agent_engine=self.root_agent,
|
|
386
|
+
display_name=self._reasoning_engine_name,
|
|
387
|
+
description=self._reasoning_engine_description,
|
|
388
|
+
requirements=self._load_requirements(),
|
|
389
|
+
env_vars=self._environment_variables,
|
|
390
|
+
extra_packages=[f"./{self._agent_folder}"]
|
|
391
|
+
)
|
|
392
|
+
except NotFound as e:
|
|
393
|
+
self.error(f"Reasoning engine {reasoning_engine_id} not found. Cannot update.")
|
|
394
|
+
self.error(f"Please inspect using CLI, GUI or GCP Interface to identify root cause.")
|
|
395
|
+
self.error(f"Deleting deployment record {self._get_deployment_record_path()} to allow re-creation.")
|
|
396
|
+
self.error(f"System reported: {e}")
|
|
397
|
+
os.remove(self._get_deployment_record_path())
|
|
398
|
+
sys.exit(1)
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
if updating:
|
|
402
|
+
self.info(f"Reasoning engine updated successfully: {updating.resource_name}")
|
|
403
|
+
|
|
404
|
+
self._write_engine_deployment({'reasoning_engine_id': updating.resource_name})
|
|
405
|
+
|
|
406
|
+
def _get_agent_space_payload(self, reasoning_engine: str) -> Tuple[dict, dict]:
|
|
407
|
+
headers = {
|
|
408
|
+
"Authorization": f"Bearer {self._access_token()}",
|
|
409
|
+
"Content-Type": "application/json",
|
|
410
|
+
"X-Goog-User-Project": self._project_number,
|
|
411
|
+
}
|
|
412
|
+
payload = {
|
|
413
|
+
"displayName": self._agent_space_name,
|
|
414
|
+
"description": self._agent_space_description,
|
|
415
|
+
"adk_agent_definition": {
|
|
416
|
+
"tool_settings": {"tool_description": self._agent_space_tool_description},
|
|
417
|
+
"provisioned_reasoning_engine": {"reasoning_engine": reasoning_engine},
|
|
418
|
+
},
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if self._authorization_id:
|
|
422
|
+
payload["adk_agent_definition"]["authorizations"] = [
|
|
423
|
+
f"projects/{self._project_number}/locations/global/authorizations/{self._authorization_id}"
|
|
424
|
+
]
|
|
425
|
+
|
|
426
|
+
return headers, payload
|
|
427
|
+
|
|
428
|
+
def _get_agent_space_agent_url_new(self):
|
|
429
|
+
return (f"{DISCOVERY_ENGINE_URL}/projects/{self._project_number}/locations/global/collections/default_collection/"
|
|
430
|
+
f"engines/{self._agent_space_engine}/assistants/default_assistant/agents")
|
|
431
|
+
|
|
432
|
+
def _deploy_to_agent_space(self):
|
|
433
|
+
get_reasoning_engine = self._read_engine_deployment_record()
|
|
434
|
+
|
|
435
|
+
if not get_reasoning_engine or not get_reasoning_engine.get("reasoning_engine_id"):
|
|
436
|
+
return ("failed", "Reasoning engine required before deploy", None)
|
|
437
|
+
|
|
438
|
+
if get_reasoning_engine.get("agent_space_id"):
|
|
439
|
+
self.warning("Agent space already exists; skipping creation.")
|
|
440
|
+
|
|
441
|
+
return
|
|
442
|
+
|
|
443
|
+
headers, payload = self._get_agent_space_payload(get_reasoning_engine["reasoning_engine_id"])
|
|
444
|
+
url = self._get_agent_space_agent_url_new()
|
|
445
|
+
r = self._http.post(url, headers=headers, json=payload, timeout=90)
|
|
446
|
+
|
|
447
|
+
if r.status_code < 400:
|
|
448
|
+
self.info("Agent space deployed successfully.")
|
|
449
|
+
if self.attempt_to_use_existing_auth:
|
|
450
|
+
self.warning("Using existing authorization.")
|
|
451
|
+
self._write_engine_deployment({"authorization_id": self._authorization_id, 'scopes': self._required_scopes})
|
|
452
|
+
self._write_engine_deployment({"agent_space_id": r.json().get("name")})
|
|
453
|
+
else:
|
|
454
|
+
# Log API failure details to file only
|
|
455
|
+
with open(self.log_filename, 'a') as f:
|
|
456
|
+
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S,%f')[:-3]
|
|
457
|
+
f.write(f"{timestamp} - ReasoningEngineDeployment - ERROR - Agent space deployment failed with status {r.status_code} {r.reason}\n")
|
|
458
|
+
f.write(f"{timestamp} - ReasoningEngineDeployment - ERROR - URL: {r.url}\n")
|
|
459
|
+
f.write(f"{timestamp} - ReasoningEngineDeployment - ERROR - Response: {r.text}\n")
|
|
460
|
+
if r.headers.get('content-type', '').startswith('application/json'):
|
|
461
|
+
try:
|
|
462
|
+
error_json = r.json()
|
|
463
|
+
f.write(f"{timestamp} - ReasoningEngineDeployment - ERROR - Error details: {json.dumps(error_json, indent=2)}\n")
|
|
464
|
+
except:
|
|
465
|
+
pass
|
|
466
|
+
|
|
467
|
+
# Terminal message - simple
|
|
468
|
+
self.error("Agent space deployment failed")
|
|
469
|
+
# This will also log the record file
|
|
470
|
+
|
|
471
|
+
def _update_in_agent_space(self):
|
|
472
|
+
get_reasoning_engine = self._read_engine_deployment_record()
|
|
473
|
+
|
|
474
|
+
if not get_reasoning_engine or not get_reasoning_engine.get("reasoning_engine_id"):
|
|
475
|
+
return ("failed", "Reasoning engine required before update", None)
|
|
476
|
+
|
|
477
|
+
if not get_reasoning_engine.get("agent_space_id"):
|
|
478
|
+
self.warning("No agent space to update; skipping.")
|
|
479
|
+
return
|
|
480
|
+
|
|
481
|
+
headers, payload = self._get_agent_space_payload(get_reasoning_engine["reasoning_engine_id"])
|
|
482
|
+
url = f'{DISCOVERY_ENGINE_URL}/' + get_reasoning_engine.get("agent_space_id")
|
|
483
|
+
r = self._http.patch(url, headers=headers, json=payload, timeout=90)
|
|
484
|
+
|
|
485
|
+
if r.status_code < 400:
|
|
486
|
+
if self.attempt_to_use_existing_auth:
|
|
487
|
+
self.info("Using existing authorization.")
|
|
488
|
+
self._write_engine_deployment({"authorization_id": self._authorization_id, 'scopes': self._required_scopes})
|
|
489
|
+
self.info("Agent space updated successfully.")
|
|
490
|
+
return True
|
|
491
|
+
else:
|
|
492
|
+
# Log API failure details to file only
|
|
493
|
+
with open(self.log_filename, 'a') as f:
|
|
494
|
+
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S,%f')[:-3]
|
|
495
|
+
f.write(f"{timestamp} - ReasoningEngineDeployment - ERROR - Failed to update agent space with status {r.status_code} {r.reason}\n")
|
|
496
|
+
f.write(f"{timestamp} - ReasoningEngineDeployment - ERROR - URL: {r.url}\n")
|
|
497
|
+
f.write(f"{timestamp} - ReasoningEngineDeployment - ERROR - Response: {r.text}\n")
|
|
498
|
+
if r.headers.get('content-type', '').startswith('application/json'):
|
|
499
|
+
try:
|
|
500
|
+
error_json = r.json()
|
|
501
|
+
f.write(f"{timestamp} - ReasoningEngineDeployment - ERROR - Error details: {json.dumps(error_json, indent=2)}\n")
|
|
502
|
+
except:
|
|
503
|
+
pass
|
|
504
|
+
|
|
505
|
+
# Terminal message - simple
|
|
506
|
+
self.error("Failed to update agent space")
|
|
507
|
+
# This will also log the record file
|
|
508
|
+
return False
|
|
509
|
+
|
|
510
|
+
def _build_authorization_uri(self, client_id: str, scopes: list[str]) -> str:
|
|
511
|
+
base = "https://accounts.google.com/o/oauth2/auth"
|
|
512
|
+
query = {
|
|
513
|
+
"response_type": "code",
|
|
514
|
+
"client_id": client_id,
|
|
515
|
+
"scope": " ".join(scopes),
|
|
516
|
+
"access_type": "offline",
|
|
517
|
+
"prompt": "consent",
|
|
518
|
+
}
|
|
519
|
+
return f"{base}?{urllib.parse.urlencode(query)}"
|
|
520
|
+
|
|
521
|
+
def _create_authorization(self) -> dict:
|
|
522
|
+
read_authorizations = self._read_engine_deployment_record()
|
|
523
|
+
|
|
524
|
+
if not self._authorization_id:
|
|
525
|
+
self.warning("No authorization ID provided; skipping authorization creation.")
|
|
526
|
+
|
|
527
|
+
return
|
|
528
|
+
|
|
529
|
+
if read_authorizations and (read_authorizations.get("authorization_id") and read_authorizations.get("authorization_id") == self._authorization_id):
|
|
530
|
+
self.warning("Authorization already exists; skipping creation.")
|
|
531
|
+
|
|
532
|
+
return
|
|
533
|
+
|
|
534
|
+
discovery_engine_url = "https://discoveryengine.googleapis.com/v1alpha"
|
|
535
|
+
headers = {
|
|
536
|
+
"Authorization": f"Bearer {self._access_token()}",
|
|
537
|
+
"Content-Type": "application/json",
|
|
538
|
+
"X-Goog-User-Project": self._project_number,
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
client_id = self._oauth_client_id or "your-client-id"
|
|
542
|
+
client_secret = self._oauth_client_secret or "your-client-secret"
|
|
543
|
+
|
|
544
|
+
payload = {
|
|
545
|
+
"name": f"projects/{self._project_number}/locations/global/authorizations/{self._authorization_id}",
|
|
546
|
+
"serverSideOauth2": {
|
|
547
|
+
"clientId": client_id,
|
|
548
|
+
"clientSecret": client_secret,
|
|
549
|
+
"authorizationUri": self._build_authorization_uri(client_id, self._required_scopes),
|
|
550
|
+
"tokenUri": "https://oauth2.googleapis.com/token",
|
|
551
|
+
},
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
url = (
|
|
555
|
+
f"{discovery_engine_url}/projects/{self._project_id}/locations/global/authorizations"
|
|
556
|
+
f"?authorizationId={self._authorization_id}"
|
|
557
|
+
)
|
|
558
|
+
|
|
559
|
+
r = self._http.post(url, headers=headers, json=payload, timeout=60)
|
|
560
|
+
|
|
561
|
+
if r.status_code < 400:
|
|
562
|
+
payload = {"authorization_id": self._authorization_id, 'scopes': self._required_scopes}
|
|
563
|
+
self._write_engine_deployment(payload)
|
|
564
|
+
return True
|
|
565
|
+
|
|
566
|
+
if r.status_code == 409:
|
|
567
|
+
self.warning("Authorization conflict detected; attempting to use existing authorization.")
|
|
568
|
+
self.logger.debug(f"Conflict response: {r.text}")
|
|
569
|
+
self.attempt_to_use_existing_auth = True
|
|
570
|
+
elif r.status_code >= 400:
|
|
571
|
+
# Log API failure details to file only
|
|
572
|
+
with open(self.log_filename, 'a') as f:
|
|
573
|
+
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S,%f')[:-3]
|
|
574
|
+
f.write(f"{timestamp} - ReasoningEngineDeployment - ERROR - Authorization creation failed with status {r.status_code} {r.reason}\n")
|
|
575
|
+
f.write(f"{timestamp} - ReasoningEngineDeployment - ERROR - URL: {r.url}\n")
|
|
576
|
+
f.write(f"{timestamp} - ReasoningEngineDeployment - ERROR - Response: {r.text}\n")
|
|
577
|
+
if r.headers.get('content-type', '').startswith('application/json'):
|
|
578
|
+
try:
|
|
579
|
+
error_json = r.json()
|
|
580
|
+
f.write(f"{timestamp} - ReasoningEngineDeployment - ERROR - Error details: {json.dumps(error_json, indent=2)}\n")
|
|
581
|
+
except:
|
|
582
|
+
pass
|
|
583
|
+
|
|
584
|
+
# Terminal message - simple
|
|
585
|
+
self.error("Authorization creation failed")
|
|
586
|
+
# This will also log the record file
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
def _drop_authorization(self) -> bool:
|
|
590
|
+
temp_auth = self._authorization_id
|
|
591
|
+
self._authorization_id = None
|
|
592
|
+
self._update_in_agent_space()
|
|
593
|
+
self._authorization_id = temp_auth
|
|
594
|
+
|
|
595
|
+
read_record = self._read_engine_deployment_record()
|
|
596
|
+
read_record.pop("authorization_id", None)
|
|
597
|
+
|
|
598
|
+
file_path = self._get_deployment_record_path()
|
|
599
|
+
|
|
600
|
+
with open(file_path, "w") as f:
|
|
601
|
+
json.dump(read_record, f, sort_keys=False, indent=2)
|
|
602
|
+
|
|
603
|
+
self.info("Authorization dropped successfully")
|
|
604
|
+
|
|
605
|
+
return True
|
|
606
|
+
|
|
607
|
+
def _delete_authorization(self):
|
|
608
|
+
if not self._authorization_id:
|
|
609
|
+
self.warning("No authorization ID provided; skipping deletion.")
|
|
610
|
+
return
|
|
611
|
+
|
|
612
|
+
discovery_engine_url = "https://discoveryengine.googleapis.com/v1alpha"
|
|
613
|
+
headers = {
|
|
614
|
+
"Authorization": f"Bearer {self._access_token()}",
|
|
615
|
+
"Content-Type": "application/json",
|
|
616
|
+
"X-Goog-User-Project": self._project_number,
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
url = (
|
|
620
|
+
f"{discovery_engine_url}/projects/{self._project_id}/locations/global/authorizations"
|
|
621
|
+
f"?authorizationId={self._authorization_id}"
|
|
622
|
+
)
|
|
623
|
+
|
|
624
|
+
r = self._http.delete(url, headers=headers, timeout=60)
|
|
625
|
+
|
|
626
|
+
if r.status_code < 400:
|
|
627
|
+
self.info("Authorization deleted successfully.")
|
|
628
|
+
self._authorization_id = None
|
|
629
|
+
self._update_in_agent_space()
|
|
630
|
+
return True
|
|
631
|
+
|
|
632
|
+
# Log API failure details to file only
|
|
633
|
+
with open(self.log_filename, 'a') as f:
|
|
634
|
+
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S,%f')[:-3]
|
|
635
|
+
f.write(f"{timestamp} - ReasoningEngineDeployment - ERROR - Failed to delete authorization with status {r.status_code} {r.reason}\n")
|
|
636
|
+
f.write(f"{timestamp} - ReasoningEngineDeployment - ERROR - URL: {r.url}\n")
|
|
637
|
+
f.write(f"{timestamp} - ReasoningEngineDeployment - ERROR - Response: {r.text}\n")
|
|
638
|
+
if r.headers.get('content-type', '').startswith('application/json'):
|
|
639
|
+
try:
|
|
640
|
+
error_json = r.json()
|
|
641
|
+
f.write(f"{timestamp} - ReasoningEngineDeployment - ERROR - Error details: {json.dumps(error_json, indent=2)}\n")
|
|
642
|
+
except:
|
|
643
|
+
pass
|
|
644
|
+
|
|
645
|
+
# Terminal message - simple
|
|
646
|
+
self.error("Failed to delete authorization")
|
|
647
|
+
# This will also log the record file
|
|
648
|
+
return False
|
|
649
|
+
|
|
650
|
+
def one_deployment_with_everything_on_it(self, skip_engine_step=False):
|
|
651
|
+
read_engine = self._read_engine_deployment_record()
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
if not skip_engine_step:
|
|
655
|
+
if read_engine and read_engine.get("reasoning_engine_id"):
|
|
656
|
+
self.info("Updating reasoning engine ... ")
|
|
657
|
+
self.update_reasoning_engine(read_engine["reasoning_engine_id"])
|
|
658
|
+
self.info("Done!")
|
|
659
|
+
else:
|
|
660
|
+
self.create_reasoning_engine()
|
|
661
|
+
else:
|
|
662
|
+
if not read_engine or not read_engine.get("reasoning_engine_id"):
|
|
663
|
+
self.warning("Skipping reasoning engine step, but no existing engine found in record.")
|
|
664
|
+
self.warning("Please ensure the reasoning engine exists before proceeding. Ending Agent Space update attempt")
|
|
665
|
+
|
|
666
|
+
sys.exit(1)
|
|
667
|
+
|
|
668
|
+
needs_auth_reset = False
|
|
669
|
+
|
|
670
|
+
if not read_engine or read_engine.get("authorization_id") != self._authorization_id:
|
|
671
|
+
self.warning("Detected change in authorization ID")
|
|
672
|
+
needs_auth_reset = True
|
|
673
|
+
|
|
674
|
+
elif read_engine and read_engine.get("scopes") != self._required_scopes:
|
|
675
|
+
self.warning("Detected change in authorization scopes")
|
|
676
|
+
needs_auth_reset = True
|
|
677
|
+
|
|
678
|
+
if needs_auth_reset:
|
|
679
|
+
self.info("Resetting authorization...")
|
|
680
|
+
self._drop_authorization()
|
|
681
|
+
self._create_authorization()
|
|
682
|
+
|
|
683
|
+
if not read_engine or (read_engine and not read_engine.get("agent_space_id")):
|
|
684
|
+
self.info("Creating agent space ... ")
|
|
685
|
+
self._deploy_to_agent_space()
|
|
686
|
+
else:
|
|
687
|
+
self._update_in_agent_space()
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import subprocess
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from dotenv import load_dotenv
|
|
6
|
+
import argparse
|
|
7
|
+
|
|
8
|
+
from reasoning_deployment_service import (
|
|
9
|
+
ReasoningEngineDeploymentService,
|
|
10
|
+
)
|
|
11
|
+
from reasoning_deployment_service.gui_editor import GUIEditor
|
|
12
|
+
from reasoning_deployment_service.cli_editor import CLIRunner
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Runner:
|
|
16
|
+
@staticmethod
|
|
17
|
+
def run():
|
|
18
|
+
parser = argparse.ArgumentParser(description="Reasoning Engine Deployment Runner")
|
|
19
|
+
parser.add_argument(
|
|
20
|
+
"--mode",
|
|
21
|
+
choices=["create", "auth", "cli", "gui", "populate_files"],
|
|
22
|
+
required=False,
|
|
23
|
+
help="Operation mode to run",
|
|
24
|
+
)
|
|
25
|
+
args = parser.parse_args()
|
|
26
|
+
|
|
27
|
+
# --- Ensure .gitignore covers junk ---
|
|
28
|
+
Runner._ensure_gitignore()
|
|
29
|
+
|
|
30
|
+
# --- Ensure your_agent_import.py exists ---
|
|
31
|
+
if not Path("your_agent_import.py").exists():
|
|
32
|
+
Runner._create_agent_import()
|
|
33
|
+
|
|
34
|
+
# --- Always ensure venv exists & is up to date ---
|
|
35
|
+
venv_python = Runner._ensure_venv()
|
|
36
|
+
|
|
37
|
+
# --- Install dependencies into venv ---
|
|
38
|
+
Runner._install_deps(venv_python)
|
|
39
|
+
|
|
40
|
+
# --- If no config files, restrict menu ---
|
|
41
|
+
if not Path(".env.agent").exists() or not Path("aix_agent.yaml").exists():
|
|
42
|
+
print("Missing .env.agent or aix_agent.yaml.")
|
|
43
|
+
print("Options:\n 5) Generate placeholder config\n q) Quit")
|
|
44
|
+
choice = input("Enter choice: ").strip()
|
|
45
|
+
if choice == "5":
|
|
46
|
+
subprocess.check_call([venv_python, "deploy.py", "--mode", "populate_files"])
|
|
47
|
+
else:
|
|
48
|
+
sys.exit(0)
|
|
49
|
+
return
|
|
50
|
+
|
|
51
|
+
# --- Dispatch based on mode or menu ---
|
|
52
|
+
if args.mode:
|
|
53
|
+
Runner._dispatch(args.mode, venv_python)
|
|
54
|
+
else:
|
|
55
|
+
Runner._menu(venv_python)
|
|
56
|
+
|
|
57
|
+
# ------------------ HELPERS ------------------
|
|
58
|
+
|
|
59
|
+
@staticmethod
|
|
60
|
+
def _ensure_gitignore():
|
|
61
|
+
gi = Path(".gitignore")
|
|
62
|
+
if not gi.exists():
|
|
63
|
+
gi.write_text("# Generated by Runner\n")
|
|
64
|
+
patterns = [".venv", ".venv_deploy", "venv", "__pycache__", "deploy_env", "your_agent_import.py"]
|
|
65
|
+
content = gi.read_text().splitlines()
|
|
66
|
+
for p in patterns:
|
|
67
|
+
if p not in content:
|
|
68
|
+
with gi.open("a") as f:
|
|
69
|
+
f.write(f"{p}\n")
|
|
70
|
+
|
|
71
|
+
@staticmethod
|
|
72
|
+
def _create_agent_import():
|
|
73
|
+
agent_dir = input("Enter the directory where your root_agent (agent.py) lives: ").strip()
|
|
74
|
+
if not Path(agent_dir, "agent.py").exists():
|
|
75
|
+
print(f"Error: {agent_dir}/agent.py not found")
|
|
76
|
+
sys.exit(1)
|
|
77
|
+
import_path = agent_dir.replace("/", ".") + ".agent"
|
|
78
|
+
Path("your_agent_import.py").write_text(f"from {import_path} import root_agent\n")
|
|
79
|
+
print(f"Created your_agent_import.py pointing to {agent_dir}/agent.py")
|
|
80
|
+
|
|
81
|
+
@staticmethod
|
|
82
|
+
def _ensure_venv():
|
|
83
|
+
venv_dir = Path("deploy_env")
|
|
84
|
+
if not venv_dir.exists():
|
|
85
|
+
subprocess.check_call([sys.executable, "-m", "venv", str(venv_dir)])
|
|
86
|
+
print("Created venv at ./deploy_env")
|
|
87
|
+
return str(venv_dir / "bin" / "python")
|
|
88
|
+
|
|
89
|
+
@staticmethod
|
|
90
|
+
def _install_deps(venv_python):
|
|
91
|
+
subprocess.check_call([venv_python, "-m", "pip", "install", "--upgrade", "pip"])
|
|
92
|
+
subprocess.check_call([venv_python, "-m", "pip", "install", "keyring", "keyrings.google-artifactregistry-auth"])
|
|
93
|
+
# Install reasoning-deployment-service itself (current package)
|
|
94
|
+
subprocess.check_call([venv_python, "-m", "pip", "install", "reasoning-deployment-service"])
|
|
95
|
+
|
|
96
|
+
if Path("requirements.txt").exists():
|
|
97
|
+
subprocess.check_call([venv_python, "-m", "pip", "install", "-r", "requirements.txt"])
|
|
98
|
+
|
|
99
|
+
@staticmethod
|
|
100
|
+
def _dispatch(mode, venv_python):
|
|
101
|
+
from your_agent_import import root_agent
|
|
102
|
+
svc = ReasoningEngineDeploymentService(root_agent, deployment_environment="DEV")
|
|
103
|
+
|
|
104
|
+
if mode == "create":
|
|
105
|
+
svc.one_deployment_with_everything_on_it()
|
|
106
|
+
elif mode == "auth":
|
|
107
|
+
svc.one_deployment_with_everything_on_it(skip_engine_step=True)
|
|
108
|
+
elif mode == "cli":
|
|
109
|
+
CLIRunner().run()
|
|
110
|
+
elif mode == "gui":
|
|
111
|
+
GUIEditor().run()
|
|
112
|
+
elif mode == "populate_files":
|
|
113
|
+
svc._check_required_files_exist()
|
|
114
|
+
|
|
115
|
+
@staticmethod
|
|
116
|
+
def _menu(venv_python):
|
|
117
|
+
print("Choose an operation:\n1) Create/Update\n2) Auth only\n3) CLI\n4) GUI\nq) Quit")
|
|
118
|
+
choice = input("Enter choice: ").strip()
|
|
119
|
+
mapping = {"1": "create", "2": "auth", "3": "cli", "4": "gui"}
|
|
120
|
+
if choice.lower() == "q":
|
|
121
|
+
sys.exit(0)
|
|
122
|
+
Runner._dispatch(mapping.get(choice, ""), venv_python)
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: reasoning-deployment-service
|
|
3
|
-
Version: 0.2
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Deployment helper for Vertex AI Reasoning Engines & Agent Spaces
|
|
5
5
|
Author: AxG-AI-Exchange-GenAI-Initiative
|
|
6
6
|
Author-email: Sergio Estrada <sergio.estrada@accenture.com>
|
|
7
7
|
License: Apache-2.0
|
|
8
|
+
Project-URL: Homepage, https://github.com/AxG-AI-Exchange-GenAI-Initiative/AIXAgentDeploymentService
|
|
9
|
+
Project-URL: Repository, https://github.com/AxG-AI-Exchange-GenAI-Initiative/AIXAgentDeploymentService
|
|
10
|
+
Project-URL: Issues, https://github.com/AxG-AI-Exchange-GenAI-Initiative/AIXAgentDeploymentService/issues
|
|
8
11
|
Requires-Python: >=3.7
|
|
9
12
|
Description-Content-Type: text/markdown
|
|
10
13
|
Requires-Dist: requests>=2.28
|
|
@@ -13,10 +16,15 @@ Requires-Dist: google-auth>=2.20
|
|
|
13
16
|
Requires-Dist: google-cloud-storage>=2.16
|
|
14
17
|
Requires-Dist: keyring>=24.0
|
|
15
18
|
Requires-Dist: keyrings.google-artifactregistry-auth>=1.1
|
|
19
|
+
Requires-Dist: google-cloud-aiplatform>=1.50.0
|
|
20
|
+
Requires-Dist: protobuf<7.0.0,>=4.21
|
|
21
|
+
Requires-Dist: click>=8.0
|
|
16
22
|
Provides-Extra: dev
|
|
17
23
|
Requires-Dist: pytest; extra == "dev"
|
|
18
24
|
Requires-Dist: build; extra == "dev"
|
|
19
25
|
Requires-Dist: twine; extra == "dev"
|
|
26
|
+
Requires-Dist: black; extra == "dev"
|
|
27
|
+
Requires-Dist: ruff; extra == "dev"
|
|
20
28
|
Dynamic: author
|
|
21
29
|
Dynamic: requires-python
|
|
22
30
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
reasoning_deployment_service/__init__.py,sha256=xDuKt9gGviQiTV6vXBdkBvygnlAOIrwnUjVaMGZy0L4,670
|
|
2
|
+
reasoning_deployment_service/reasoning_deployment_service.py,sha256=0QHU2IqqojwFI2wPJ0izrskiHiwBfxdBEB9I_YxYbSA,29133
|
|
3
|
+
reasoning_deployment_service/runner.py,sha256=V3JDWeVaP0q6Xd2iwk8co0Nio8lzQQU2tHbX9VlsrCc,4684
|
|
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
|
|
7
|
+
reasoning_deployment_service/cli_editor/config.py,sha256=lZ8Ng007NVdN1n5spJ0OFC72TOPFWKvPRxa9eKE-FDY,3573
|
|
8
|
+
reasoning_deployment_service/cli_editor/google_deps.py,sha256=PhGwdKEC96GdlFHkQrtSJrg_-w1JoUPes3zvaz22rd0,771
|
|
9
|
+
reasoning_deployment_service/cli_editor/reasoning_engine_creator.py,sha256=6QC8Y9yZAT8SYNkT_R00g_SSOYuwEkIxAN9lBG3br2k,19564
|
|
10
|
+
reasoning_deployment_service/gui_editor/__init__.py,sha256=e5e88iNTk1GC243DRsQFi5E7PqMaT2SXmqOez9FbYzo,128
|
|
11
|
+
reasoning_deployment_service/gui_editor/main.py,sha256=4UzgGUga_xIYIWRVo-80PzhJ1Dlou8PaUXoRiLcLhp8,10914
|
|
12
|
+
reasoning_deployment_service/gui_editor/requirements_minimal.txt,sha256=tiv36KUcIx496Ewuh_FqXyhMhGOrtqsYfKEdn85XXYQ,1179
|
|
13
|
+
reasoning_deployment_service/gui_editor/run_program.sh,sha256=xfim6JJiDc7KtGckcVl_K732WtZYfvBogLfslYBXb0Q,1489
|
|
14
|
+
reasoning_deployment_service/gui_editor/src/__init__.py,sha256=q4YBECQGHtHV_TF4MyL36ElWe6tdVgmoNchN1lQU7z4,25
|
|
15
|
+
reasoning_deployment_service/gui_editor/src/core/__init__.py,sha256=kH-6PxUQ-uVn79ihXN3eLyv_oFhReVsVz-f-kEK_qUo,46
|
|
16
|
+
reasoning_deployment_service/gui_editor/src/core/api_client.py,sha256=rGyFKRyBY_MjRsnWxVI1IX1WAYTEfM1V5X3l1iR8GaI,26845
|
|
17
|
+
reasoning_deployment_service/gui_editor/src/core/config.py,sha256=nQT7biTArZl4zsbSZuSD_G7CU7xTJ1bZ4VPfQkxbpX4,1817
|
|
18
|
+
reasoning_deployment_service/gui_editor/src/core/google_deps.py,sha256=7mMSqtvclJewEBeb-0MevZ7FE0ZEOGHyXh5V73bhpRk,495
|
|
19
|
+
reasoning_deployment_service/gui_editor/src/core/reasoning_engine_creator.py,sha256=6QC8Y9yZAT8SYNkT_R00g_SSOYuwEkIxAN9lBG3br2k,19564
|
|
20
|
+
reasoning_deployment_service/gui_editor/src/ui/__init__.py,sha256=262ZiXO6Luk8vZnhCIoYxOtGiny0bXK-BTKjxUNBx-w,43
|
|
21
|
+
reasoning_deployment_service/gui_editor/src/ui/agent_space_view.py,sha256=TV2f0_pkw5JCQRWjVPuS-iW92toW41kPJk-pR1C0oqQ,12471
|
|
22
|
+
reasoning_deployment_service/gui_editor/src/ui/authorization_view.py,sha256=uiyN411qYe7V2pnPRNdvxRkOifI-aXFI6j2De2RZp1M,10956
|
|
23
|
+
reasoning_deployment_service/gui_editor/src/ui/reasoning_engine_view.py,sha256=tCvSPEf4dW0NRdAqfs3yT5Pa873gYeLzCMMIt2r2T4o,14644
|
|
24
|
+
reasoning_deployment_service/gui_editor/src/ui/reasoning_engines_view.py,sha256=IRjFlBbY98usAZa0roOonjvWQOsF6NBW4bBg_k8KnKI,7860
|
|
25
|
+
reasoning_deployment_service/gui_editor/src/ui/ui_components.py,sha256=HdQHy-oSZ3GobQ3FNdH7y_w3ANbFiuf2rMoflAmff0A,55366
|
|
26
|
+
reasoning_deployment_service-0.3.2.dist-info/METADATA,sha256=cp5JoJ-6QATBqi_AxMZ6mo6imJmUITCL26LqA_FDQDs,5027
|
|
27
|
+
reasoning_deployment_service-0.3.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
28
|
+
reasoning_deployment_service-0.3.2.dist-info/entry_points.txt,sha256=onGKjR5ONTtRv3aqEtK863iw9Ty1kLcjfZlsplkRZrA,84
|
|
29
|
+
reasoning_deployment_service-0.3.2.dist-info/top_level.txt,sha256=GKuQS1xHUYLZbatw9DmcYdBxxLhWhhGkV4FmFxgKdp0,29
|
|
30
|
+
reasoning_deployment_service-0.3.2.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
reasoning_deployment_service
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
cli_editor/__init__.py,sha256=bN8NPkw8riB92pj2lAwJZuEMOQIO_RRuge0ehnJTW1I,118
|
|
2
|
-
cli_editor/api_client.py,sha256=PUXPWUslFOksKMrcKjJSK_gVJkTI0bO_bqtCEYeM85g,27897
|
|
3
|
-
cli_editor/cli_runner.py,sha256=w8jv2ep6TiYfpc_KnlvDOJ9hsCSuZcix4Rv4GQcZO4g,15729
|
|
4
|
-
cli_editor/config.py,sha256=lZ8Ng007NVdN1n5spJ0OFC72TOPFWKvPRxa9eKE-FDY,3573
|
|
5
|
-
cli_editor/google_deps.py,sha256=PhGwdKEC96GdlFHkQrtSJrg_-w1JoUPes3zvaz22rd0,771
|
|
6
|
-
cli_editor/reasoning_engine_creator.py,sha256=6QC8Y9yZAT8SYNkT_R00g_SSOYuwEkIxAN9lBG3br2k,19564
|
|
7
|
-
gui_editor/__init__.py,sha256=e5e88iNTk1GC243DRsQFi5E7PqMaT2SXmqOez9FbYzo,128
|
|
8
|
-
gui_editor/main.py,sha256=4UzgGUga_xIYIWRVo-80PzhJ1Dlou8PaUXoRiLcLhp8,10914
|
|
9
|
-
gui_editor/src/__init__.py,sha256=q4YBECQGHtHV_TF4MyL36ElWe6tdVgmoNchN1lQU7z4,25
|
|
10
|
-
gui_editor/src/core/__init__.py,sha256=kH-6PxUQ-uVn79ihXN3eLyv_oFhReVsVz-f-kEK_qUo,46
|
|
11
|
-
gui_editor/src/core/api_client.py,sha256=rGyFKRyBY_MjRsnWxVI1IX1WAYTEfM1V5X3l1iR8GaI,26845
|
|
12
|
-
gui_editor/src/core/config.py,sha256=nQT7biTArZl4zsbSZuSD_G7CU7xTJ1bZ4VPfQkxbpX4,1817
|
|
13
|
-
gui_editor/src/core/google_deps.py,sha256=7mMSqtvclJewEBeb-0MevZ7FE0ZEOGHyXh5V73bhpRk,495
|
|
14
|
-
gui_editor/src/core/reasoning_engine_creator.py,sha256=6QC8Y9yZAT8SYNkT_R00g_SSOYuwEkIxAN9lBG3br2k,19564
|
|
15
|
-
gui_editor/src/ui/__init__.py,sha256=262ZiXO6Luk8vZnhCIoYxOtGiny0bXK-BTKjxUNBx-w,43
|
|
16
|
-
gui_editor/src/ui/agent_space_view.py,sha256=TV2f0_pkw5JCQRWjVPuS-iW92toW41kPJk-pR1C0oqQ,12471
|
|
17
|
-
gui_editor/src/ui/authorization_view.py,sha256=uiyN411qYe7V2pnPRNdvxRkOifI-aXFI6j2De2RZp1M,10956
|
|
18
|
-
gui_editor/src/ui/reasoning_engine_view.py,sha256=tCvSPEf4dW0NRdAqfs3yT5Pa873gYeLzCMMIt2r2T4o,14644
|
|
19
|
-
gui_editor/src/ui/reasoning_engines_view.py,sha256=IRjFlBbY98usAZa0roOonjvWQOsF6NBW4bBg_k8KnKI,7860
|
|
20
|
-
gui_editor/src/ui/ui_components.py,sha256=HdQHy-oSZ3GobQ3FNdH7y_w3ANbFiuf2rMoflAmff0A,55366
|
|
21
|
-
reasoning_deployment_service-0.2.9.dist-info/METADATA,sha256=vTGStyO1T80DQtRG7l-VKZ9CT5Nnog4ze8BtzG4IZx4,4534
|
|
22
|
-
reasoning_deployment_service-0.2.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
23
|
-
reasoning_deployment_service-0.2.9.dist-info/top_level.txt,sha256=khlnMJ2te_ExPFI1BzPt1VT6JyeJl5W-VgBL5PYyyFg,22
|
|
24
|
-
reasoning_deployment_service-0.2.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{reasoning_deployment_service-0.2.9.dist-info → reasoning_deployment_service-0.3.2.dist-info}/WHEEL
RENAMED
|
File without changes
|