sunholo 0.88.0__py3-none-any.whl → 0.88.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.
sunholo/cli/cli_init.py CHANGED
@@ -27,7 +27,8 @@ sunholo init my_genai_project
27
27
  This will create a new directory named `my_genai_project` with the template files, allowing users to start building their GenAI application.
28
28
  """
29
29
  project_name = args.project_name
30
- project_dir = os.path.join(os.getcwd(), project_name)
30
+ current_dir = os.getcwd() # This captures the current directory where the command is run
31
+ project_dir = os.path.join(current_dir, project_name)
31
32
 
32
33
  print(f"Initializing project: {project_name} in directory: {project_dir}")
33
34
 
@@ -48,6 +49,59 @@ This will create a new directory named `my_genai_project` with the template file
48
49
  elif os.path.isdir(src_path):
49
50
  shutil.copytree(src_path, dest_path)
50
51
 
52
+ # Determine the location of the generated.tfvars file
53
+ terraform_dir = args.terraform_dir or os.getenv('MULTIVAC_TERRAFORM_DIR')
54
+ if terraform_dir is None:
55
+ raise ValueError("Must specify a terraform_dir or use the MULTIVAC_TERRAFORM_DIR environment variable")
56
+
57
+ tfvars_file = os.path.join(terraform_dir, 'generated.tfvars')
58
+
59
+ # Get the service account, either from the CLI argument or default
60
+ service_account = args.service_account or "sa-llmops" # Default service account
61
+
62
+ # Determine the relative path for the cloud build included directories
63
+ def get_relative_application_path(full_path: str, base_dir: str) -> str:
64
+ application_base_index = full_path.find("application/")
65
+ if application_base_index != -1:
66
+ return full_path[application_base_index:]
67
+ return os.path.relpath(full_path, base_dir)
68
+
69
+ # Paths to be included in the cloud build (based on the current working directory)
70
+ # We want paths to start from 'application/system_services/{project_name}'
71
+ relative_base = os.path.relpath(current_dir, os.path.join(current_dir, "..", ".."))
72
+ included_path = os.path.join(relative_base, project_name, "**")
73
+ cloud_build_path = os.path.join(relative_base, project_name, "cloudbuild.yaml")
74
+
75
+ # Define the cloud_run configuration for 'discord-server' with the correct project_dir path
76
+ cloud_run_config = {
77
+ project_name: {
78
+ "cpu": "1",
79
+ "memory": "2Gi",
80
+ "max_instance_count": 3,
81
+ "timeout_seconds": 1500,
82
+ "port": 8080,
83
+ "service_account": service_account,
84
+ "invokers": ["allUsers"],
85
+ "cloud_build": {
86
+ "included": [included_path],
87
+ "path": cloud_build_path,
88
+ "substitutions": {},
89
+ "repo_name": "",
90
+ "repo_owner": ""
91
+ }
92
+ }
93
+ }
94
+
95
+
96
+ # Initialize the TerraformVarsEditor and update the .tfvars file
97
+ try:
98
+ from ..terraform import TerraformVarsEditor
99
+ editor = TerraformVarsEditor(tfvars_file, terraform_dir)
100
+ editor.update_from_dict(cloud_run_config, 'cloud_run')
101
+ print(f"{tfvars_file} file initialized and updated successfully.")
102
+ except ImportError as e:
103
+ print(f"Error initializing TerraformVarsEditor: {e}")
104
+
51
105
  print(f"Project {project_name} initialized successfully.")
52
106
  print(f"Navigate to {project_dir} and customize the configuration files in the 'config' directory.")
53
107
 
@@ -57,4 +111,6 @@ def setup_init_subparser(subparsers):
57
111
  """
58
112
  init_parser = subparsers.add_parser('init', help='Initializes a new Multivac project.')
59
113
  init_parser.add_argument('project_name', help='The name of the new project.')
114
+ init_parser.add_argument('--terraform-dir', help='The directory where Terraform files will be generated.')
115
+ init_parser.add_argument('--service-account', help='The service account to use for Cloud Run. Defaults to "sa-llmops"')
60
116
  init_parser.set_defaults(func=init_project)
@@ -6,6 +6,7 @@ except ImportError:
6
6
  import json
7
7
  import subprocess
8
8
  import os
9
+ import io
9
10
  from typing import Dict, Any
10
11
  from ..custom_logging import log
11
12
 
@@ -57,7 +58,7 @@ class TerraformVarsEditor:
57
58
  tfvars_file : str
58
59
  The path to the .tfvars file to be edited.
59
60
  terraform_dir : str
60
- The directory where Terraform commands will be executed (default is current directory).
61
+ The directory where Terraform commands will be executed (default is current directory). Will use MULTIVAC_TERRAFORM_DIR env var if present.
61
62
 
62
63
  Example:
63
64
  -------
@@ -65,6 +66,12 @@ class TerraformVarsEditor:
65
66
  """
66
67
  if hcl2 is None:
67
68
  raise ImportError('hcl2 is required for parsing terraform files, install via `pip install sunholo[iac]`')
69
+
70
+ # Check for the MULTIVAC_TERRAFORM_DIR environment variable
71
+ if terraform_dir == '.' and 'MULTIVAC_TERRAFORM_DIR' in os.environ:
72
+ terraform_dir = os.environ['MULTIVAC_TERRAFORM_DIR']
73
+
74
+ log.info(f'MULTIVAC_TERRAFORM_DIR environment variable is set to {terraform_dir}')
68
75
 
69
76
  self.tfvars_file = tfvars_file
70
77
  self.terraform_dir = terraform_dir
@@ -144,8 +151,10 @@ class TerraformVarsEditor:
144
151
  The dictionary containing the instance data.
145
152
 
146
153
  Example:
147
- -------
148
- editor.update_or_add_instance('cloud_run', 'new_service', {'cpu': '1', 'memory': '2Gi'})
154
+
155
+ ```python
156
+ editor.update_or_add_instance('cloud_run', 'new_service', (your dict))
157
+ ```
149
158
  """
150
159
  if main_key not in self.tfvars_data:
151
160
  self.tfvars_data[main_key] = {}
@@ -154,7 +163,7 @@ class TerraformVarsEditor:
154
163
 
155
164
  def validate_terraform(self) -> bool:
156
165
  """
157
- Runs `terraform validate` in the specified directory.
166
+ Runs `terraform init` followed by `terraform validate` in the specified directory.
158
167
 
159
168
  Returns:
160
169
  -------
@@ -163,18 +172,32 @@ class TerraformVarsEditor:
163
172
 
164
173
  Example:
165
174
  -------
175
+ ```python
166
176
  if self.validate_terraform():
167
177
  print("Validation passed.")
178
+ ```
168
179
  """
169
- result = subprocess.run(['terraform', 'validate'], cwd=self.terraform_dir, capture_output=True, text=True)
180
+ # Step 1: Run `terraform init` to ensure the directory is initialized
181
+ init_process = subprocess.run(['terraform', 'init'], cwd=self.terraform_dir, capture_output=True, text=True)
170
182
 
171
- if result.returncode == 0:
183
+ if init_process.returncode != 0:
184
+ log.error("Terraform initialization failed.")
185
+ print(init_process.stdout)
186
+ print(init_process.stderr)
187
+ return False
188
+
189
+ log.info("Terraform initialized successfully.")
190
+
191
+ # Step 2: Run `terraform validate`
192
+ validate_process = subprocess.run(['terraform', 'validate'], cwd=self.terraform_dir, capture_output=True, text=True)
193
+
194
+ if validate_process.returncode == 0:
172
195
  log.info("Terraform validation passed.")
173
196
  return True
174
197
  else:
175
198
  log.error("Terraform validation failed.")
176
- print(result.stdout)
177
- print(result.stderr)
199
+ print(validate_process.stdout)
200
+ print(validate_process.stderr)
178
201
  return False
179
202
 
180
203
  def update_from_json(self, json_file: str, main_key: str) -> None:
@@ -214,6 +237,48 @@ class TerraformVarsEditor:
214
237
  log.info(f"Terraform validation passed, changes saved to {self.tfvars_file}.")
215
238
  os.remove(backup_file) # Remove the backup if validation passes
216
239
 
240
+ def update_from_dict(self, data: Dict[str, Any], main_key: str) -> None:
241
+ """
242
+ Updates the .tfvars file based on the content of a Python dictionary and validates the changes.
243
+
244
+ Parameters:
245
+ ----------
246
+ data : dict
247
+ The dictionary with the new instance data.
248
+ main_key : str
249
+ The top-level key under which the instance is added (e.g., "cloud_run").
250
+
251
+ Example:
252
+ -------
253
+ editor.update_from_dict(data, 'cloud_run')
254
+ """
255
+ # Create an in-memory file-like object from the dictionary by converting it to JSON
256
+ json_data = json.dumps({main_key: data})
257
+ json_file = io.StringIO(json_data)
258
+
259
+ # Load the JSON data from the StringIO object
260
+ parsed_data = json.load(json_file)
261
+
262
+ # Update the tfvars data in memory
263
+ for instance_name, instance_data in parsed_data.get(main_key, {}).items():
264
+ self.update_or_add_instance(main_key, instance_name, instance_data)
265
+
266
+ # Now that the data is updated in memory, proceed to validate and write it back to the file
267
+ # Backup the original .tfvars file
268
+ backup_file = self._backup_tfvars()
269
+
270
+ # Temporarily save the updated data to the original file location
271
+ self._save_tfvars()
272
+
273
+ # Attempt to validate the changes with Terraform
274
+ if not self.validate_terraform():
275
+ # If validation fails, restore the original file from the backup
276
+ self._restore_tfvars(backup_file)
277
+ console.print(f"Changes aborted, original {self.tfvars_file} restored.")
278
+ else:
279
+ console.print(f"Terraform validation passed, changes saved to {self.tfvars_file}.")
280
+ os.remove(backup_file) # Remove the backup if validation passes
281
+
217
282
  def tfvars_command(args):
218
283
  """
219
284
  Executes the tfvars command based on parsed arguments.
@@ -225,11 +290,15 @@ def tfvars_command(args):
225
290
  if console is None:
226
291
  raise ImportError("Need cli tools to use `sunholo tfvars` - install via `pip install sunholo[cli]`")
227
292
 
228
- # Parse the JSON string to a dictionary
293
+ # Load JSON data from the specified file
229
294
  try:
230
- instance_data: Dict[str, Any] = json.loads(args.json_data)
295
+ with open(args.json_file, 'r') as f:
296
+ instance_data = json.load(f)
297
+ except FileNotFoundError:
298
+ console.print(f"Error: The JSON file '{args.json_file}' was not found.")
299
+ return
231
300
  except json.JSONDecodeError as e:
232
- console.print(f"[bold red]Error parsing JSON data: {e}[/bold red]")
301
+ console.print(f"Error parsing JSON data: {e}")
233
302
  return
234
303
 
235
304
  # Create an instance of TerraformVarsEditor
@@ -260,7 +329,7 @@ def setup_tfvarseditor_subparser(subparsers):
260
329
  add_parser.add_argument('tfvars_file', help='Path to the .tfvars file')
261
330
  add_parser.add_argument('main_key', help='The main key under which the instance is added (e.g., "cloud_run")')
262
331
  add_parser.add_argument('instance_name', help='The name of the instance to add or update')
263
- add_parser.add_argument('json_data', help='JSON string representing the instance data')
332
+ add_parser.add_argument('--json-file', help='Path to a JSON file with the instance data', required=True)
264
333
  add_parser.add_argument('--terraform-dir', default='.', help='The directory where Terraform is initialized')
265
334
 
266
335
  tfvars_parser.set_defaults(func=tfvars_command)
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sunholo
3
- Version: 0.88.0
3
+ Version: 0.88.2
4
4
  Summary: Large Language Model DevOps - a package to help deploy LLMs to the Cloud.
5
5
  Home-page: https://github.com/sunholo-data/sunholo-py
6
- Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.88.0.tar.gz
6
+ Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.88.2.tar.gz
7
7
  Author: Holosun ApS
8
8
  Author-email: multivac@sunholo.com
9
9
  License: Apache License, Version 2.0
@@ -44,7 +44,7 @@ sunholo/chunker/splitter.py,sha256=QLAEsJOpEYFZr9-UGZUuAlNVyjfCWb8jvzCHg0rVShE,6
44
44
  sunholo/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
45
  sunholo/cli/chat_vac.py,sha256=jTmCt62U4uqJjeGp_XfIbxnafAAOhQAJMi8TxSkeKlM,23164
46
46
  sunholo/cli/cli.py,sha256=q-ATmnWnc_PPo2q31xhqpp-m3GtOSBoqh0gvMeSo4XU,4374
47
- sunholo/cli/cli_init.py,sha256=JMZ9AX2cPDZ-_mv3adiv2ToFVNyRPtjk9Biszl1kiR0,2358
47
+ sunholo/cli/cli_init.py,sha256=jifczqRd4ATrfUPl-LYCpuhURZiEwqAy1MaAgpSdxQ4,5025
48
48
  sunholo/cli/configs.py,sha256=QUM9DvKOdZmEQRM5uI3Nh887T0YDiSMr7O240zTLqws,4546
49
49
  sunholo/cli/deploy.py,sha256=zxdwUsRTRMC8U5vyRv0JiKBLFn84Ug_Tc88-_h9hJSs,1609
50
50
  sunholo/cli/embedder.py,sha256=v-FKiSPHaQzB6ctClclYueIf3bf3CqYtC1oRgPfT4dY,5566
@@ -120,7 +120,7 @@ sunholo/streaming/streaming.py,sha256=5dRXo1wHYE0E91gll-939RShYTYxQMeVwAVCqFR13h
120
120
  sunholo/summarise/__init__.py,sha256=MZk3dblUMODcPb1crq4v-Z508NrFIpkSWNf9FIO8BcU,38
121
121
  sunholo/summarise/summarise.py,sha256=95A-6PXFGanjona8DvZPnnIHLbzZ2ip5hO0wOAJQhfw,3791
122
122
  sunholo/terraform/__init__.py,sha256=yixxEltc3n9UpZaVi05GlgS-YRq_DVGjUc37I9ajeP4,76
123
- sunholo/terraform/tfvars_editor.py,sha256=cxQaAsLdu_tErDrULsQEsRBhxt3YJe2R3O9cs7HNfjg,9473
123
+ sunholo/terraform/tfvars_editor.py,sha256=iBKyDajnZsDnLWuqGZv9SJKWMfK0J809-I6X0_EnUHU,12395
124
124
  sunholo/tools/__init__.py,sha256=5NuYpwwTX81qGUWvgwfItoSLXteNnp7KjgD7IPZUFjI,53
125
125
  sunholo/tools/web_browser.py,sha256=8Gdf02F4zCOeSnijnfaL6jzk4oaSI0cj48o-esoWzwE,29086
126
126
  sunholo/utils/__init__.py,sha256=Hv02T5L2zYWvCso5hzzwm8FQogwBq0OgtUbN_7Quzqc,89
@@ -143,9 +143,9 @@ sunholo/vertex/init.py,sha256=1OQwcPBKZYBTDPdyU7IM4X4OmiXLdsNV30C-fee2scQ,2875
143
143
  sunholo/vertex/memory_tools.py,sha256=q_phxgGX2TG2j2MXNULF2xGzQnQPENwjPN9nZ_A9Gh0,7526
144
144
  sunholo/vertex/safety.py,sha256=S9PgQT1O_BQAkcqauWncRJaydiP8Q_Jzmu9gxYfy1VA,2482
145
145
  sunholo/vertex/type_dict_to_json.py,sha256=uTzL4o9tJRao4u-gJOFcACgWGkBOtqACmb6ihvCErL8,4694
146
- sunholo-0.88.0.dist-info/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
147
- sunholo-0.88.0.dist-info/METADATA,sha256=t63wl44wdg-BW_ZuKE0gMoIfwwyVxqpUjNcxVp-p1PA,7706
148
- sunholo-0.88.0.dist-info/WHEEL,sha256=Mdi9PDNwEZptOjTlUcAth7XJDFtKrHYaQMPulZeBCiQ,91
149
- sunholo-0.88.0.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
150
- sunholo-0.88.0.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
151
- sunholo-0.88.0.dist-info/RECORD,,
146
+ sunholo-0.88.2.dist-info/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
147
+ sunholo-0.88.2.dist-info/METADATA,sha256=HUlu0wOukUKSs4pQW1k2F3xwu8x6nQD8WRO4dukQGKk,7706
148
+ sunholo-0.88.2.dist-info/WHEEL,sha256=UvcQYKBHoFqaQd6LKyqHw9fxEolWLQnlzP0h_LgJAfI,91
149
+ sunholo-0.88.2.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
150
+ sunholo-0.88.2.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
151
+ sunholo-0.88.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (73.0.1)
2
+ Generator: setuptools (74.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5