featrixsphere 0.2.1232__tar.gz → 0.2.1235__tar.gz

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.
Files changed (19) hide show
  1. {featrixsphere-0.2.1232/featrixsphere.egg-info → featrixsphere-0.2.1235}/PKG-INFO +1 -1
  2. featrixsphere-0.2.1235/VERSION +1 -0
  3. {featrixsphere-0.2.1232 → featrixsphere-0.2.1235}/featrixsphere/__init__.py +1 -1
  4. {featrixsphere-0.2.1232 → featrixsphere-0.2.1235}/featrixsphere/client.py +101 -57
  5. {featrixsphere-0.2.1232 → featrixsphere-0.2.1235/featrixsphere.egg-info}/PKG-INFO +1 -1
  6. featrixsphere-0.2.1232/VERSION +0 -1
  7. {featrixsphere-0.2.1232 → featrixsphere-0.2.1235}/MANIFEST.in +0 -0
  8. {featrixsphere-0.2.1232 → featrixsphere-0.2.1235}/README.md +0 -0
  9. {featrixsphere-0.2.1232 → featrixsphere-0.2.1235}/featrixsphere/cli.py +0 -0
  10. {featrixsphere-0.2.1232 → featrixsphere-0.2.1235}/featrixsphere/test_client.py +0 -0
  11. {featrixsphere-0.2.1232 → featrixsphere-0.2.1235}/featrixsphere.egg-info/SOURCES.txt +0 -0
  12. {featrixsphere-0.2.1232 → featrixsphere-0.2.1235}/featrixsphere.egg-info/dependency_links.txt +0 -0
  13. {featrixsphere-0.2.1232 → featrixsphere-0.2.1235}/featrixsphere.egg-info/entry_points.txt +0 -0
  14. {featrixsphere-0.2.1232 → featrixsphere-0.2.1235}/featrixsphere.egg-info/not-zip-safe +0 -0
  15. {featrixsphere-0.2.1232 → featrixsphere-0.2.1235}/featrixsphere.egg-info/requires.txt +0 -0
  16. {featrixsphere-0.2.1232 → featrixsphere-0.2.1235}/featrixsphere.egg-info/top_level.txt +0 -0
  17. {featrixsphere-0.2.1232 → featrixsphere-0.2.1235}/requirements.txt +0 -0
  18. {featrixsphere-0.2.1232 → featrixsphere-0.2.1235}/setup.cfg +0 -0
  19. {featrixsphere-0.2.1232 → featrixsphere-0.2.1235}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: featrixsphere
3
- Version: 0.2.1232
3
+ Version: 0.2.1235
4
4
  Summary: Transform any CSV into a production-ready ML model in minutes, not months.
5
5
  Home-page: https://github.com/Featrix/sphere
6
6
  Author: Featrix
@@ -0,0 +1 @@
1
+ 0.2.1235
@@ -38,7 +38,7 @@ Example:
38
38
  ... labels=['Experiment A', 'Experiment B'])
39
39
  """
40
40
 
41
- __version__ = "0.2.1232"
41
+ __version__ = "0.2.1235"
42
42
  __author__ = "Featrix"
43
43
  __email__ = "support@featrix.com"
44
44
  __license__ = "MIT"
@@ -1748,6 +1748,21 @@ class FeatrixSphereClient:
1748
1748
  response = self._make_request("POST", "/compute/upload_file", files=files)
1749
1749
 
1750
1750
  response_data = response.json()
1751
+
1752
+ # Handle S3 upload response (returns s3_url and filename)
1753
+ if 's3_url' in response_data:
1754
+ # S3 upload - extract filename from key or use returned filename
1755
+ filename = response_data.get('filename')
1756
+ if not filename:
1757
+ # Extract from S3 key if filename not provided
1758
+ s3_key = response_data.get('key', '')
1759
+ if s3_key:
1760
+ filename = PathLib(s3_key).name
1761
+ if not filename:
1762
+ raise ValueError("Server did not return filename in S3 upload response")
1763
+ return filename
1764
+
1765
+ # Handle local file upload response (returns filename)
1751
1766
  filename = response_data.get('filename')
1752
1767
  if not filename:
1753
1768
  raise ValueError("Server did not return filename in upload response")
@@ -4179,19 +4194,34 @@ class FeatrixSphereClient:
4179
4194
  print(f"Training predictor on foundation model {foundation_model_id}...")
4180
4195
  print(f" Target: {target_column} ({target_column_type})")
4181
4196
 
4182
- # Validate that only one data source is provided
4183
- if input_filename and df is not None:
4184
- raise ValueError("Provide either input_filename or df, not both")
4197
+ # Get the compute cluster from the foundation model session
4198
+ # This ensures we upload files to the same node where the foundation model lives
4199
+ foundation_session = self.get_session_status(foundation_model_id)
4200
+ foundation_compute_cluster = self.get_last_server_metadata()
4201
+ foundation_compute_cluster = foundation_compute_cluster.get('compute_cluster') if foundation_compute_cluster else None
4202
+
4203
+ # Temporarily set compute cluster for file uploads if we found one
4204
+ original_compute_cluster = self.compute_cluster
4205
+ original_headers = self.session.headers.copy()
4206
+ if foundation_compute_cluster:
4207
+ self.set_compute_cluster(foundation_compute_cluster)
4208
+ if verbose:
4209
+ print(f" Using compute cluster: {foundation_compute_cluster}")
4185
4210
 
4186
- # If DataFrame provided, save to temp file and upload it
4187
- temp_file = None
4188
- if df is not None:
4189
- import pandas as pd
4190
- import tempfile
4191
- import os
4192
-
4193
- if not isinstance(df, pd.DataFrame):
4194
- raise ValueError("df must be a pandas DataFrame")
4211
+ try:
4212
+ # Validate that only one data source is provided
4213
+ if input_filename and df is not None:
4214
+ raise ValueError("Provide either input_filename or df, not both")
4215
+
4216
+ # If DataFrame provided, save to temp file and upload it
4217
+ temp_file = None
4218
+ if df is not None:
4219
+ import pandas as pd
4220
+ import tempfile
4221
+ import os
4222
+
4223
+ if not isinstance(df, pd.DataFrame):
4224
+ raise ValueError("df must be a pandas DataFrame")
4195
4225
 
4196
4226
  if verbose:
4197
4227
  print(f"📊 Using provided DataFrame ({len(df)} rows, {len(df.columns)} columns)")
@@ -4221,51 +4251,65 @@ class FeatrixSphereClient:
4221
4251
  except Exception:
4222
4252
  pass # Ignore cleanup errors
4223
4253
 
4224
- data = {
4225
- "foundation_model_id": foundation_model_id,
4226
- "target_column": target_column,
4227
- "target_column_type": target_column_type,
4228
- "epochs": epochs,
4229
- "optimize_for": optimize_for,
4230
- }
4231
-
4232
- if input_filename:
4233
- # If absolute path provided, upload the file first
4234
- from pathlib import Path
4235
- input_path = Path(input_filename)
4236
- if input_path.is_absolute():
4237
- # Upload the file first, then use the uploaded filename
4238
- if not input_path.exists():
4239
- raise FileNotFoundError(f"Input file not found: {input_filename}")
4240
-
4241
- if verbose:
4242
- print(f"📤 Uploading file from absolute path: {input_filename}")
4243
-
4244
- # Upload the file
4245
- uploaded_filename = self.upload_file(str(input_path))
4246
-
4247
- if verbose:
4248
- print(f"✅ File uploaded as: {uploaded_filename}")
4249
-
4250
- data["input_filename"] = uploaded_filename
4251
- else:
4252
- # Relative filename - assume it's already on the server
4253
- data["input_filename"] = input_filename
4254
- if name:
4255
- data["name"] = name
4256
- if session_name_prefix:
4257
- data["session_name_prefix"] = session_name_prefix
4258
- if rare_label_value:
4259
- data["rare_label_value"] = rare_label_value
4260
- if class_imbalance:
4261
- data["class_imbalance"] = class_imbalance
4262
- if webhooks:
4263
- data["webhooks"] = webhooks
4264
-
4265
- response_data = self._post_json("/compute/train_on_foundational_model", data)
4266
-
4267
- new_session_id = response_data.get('session_id')
4268
- print(f"✅ Predictor training session created: {new_session_id}")
4254
+ data = {
4255
+ "foundation_model_id": foundation_model_id,
4256
+ "target_column": target_column,
4257
+ "target_column_type": target_column_type,
4258
+ "epochs": epochs,
4259
+ "optimize_for": optimize_for,
4260
+ }
4261
+
4262
+ if input_filename:
4263
+ # If absolute path provided, upload the file first
4264
+ from pathlib import Path
4265
+ input_path = Path(input_filename)
4266
+ if input_path.is_absolute():
4267
+ # Upload the file first, then use the uploaded filename
4268
+ if not input_path.exists():
4269
+ raise FileNotFoundError(f"Input file not found: {input_filename}")
4270
+
4271
+ if verbose:
4272
+ print(f"📤 Uploading file from absolute path: {input_filename}")
4273
+
4274
+ # Upload the file
4275
+ uploaded_filename = self.upload_file(str(input_path))
4276
+
4277
+ if verbose:
4278
+ print(f"✅ File uploaded as: {uploaded_filename}")
4279
+
4280
+ data["input_filename"] = uploaded_filename
4281
+ else:
4282
+ # Relative filename - assume it's already on the server
4283
+ data["input_filename"] = input_filename
4284
+ if name:
4285
+ data["name"] = name
4286
+ if session_name_prefix:
4287
+ data["session_name_prefix"] = session_name_prefix
4288
+ if rare_label_value:
4289
+ data["rare_label_value"] = rare_label_value
4290
+ if class_imbalance:
4291
+ data["class_imbalance"] = class_imbalance
4292
+ if webhooks:
4293
+ data["webhooks"] = webhooks
4294
+
4295
+ response_data = self._post_json("/compute/train_on_foundational_model", data)
4296
+
4297
+ new_session_id = response_data.get('session_id')
4298
+ print(f"✅ Predictor training session created: {new_session_id}")
4299
+
4300
+ # Restore original compute cluster setting
4301
+ if original_compute_cluster != self.compute_cluster:
4302
+ if original_compute_cluster:
4303
+ self.set_compute_cluster(original_compute_cluster)
4304
+ else:
4305
+ self.session.headers = original_headers
4306
+ finally:
4307
+ # Ensure we restore headers even if there's an error
4308
+ if original_compute_cluster != self.compute_cluster:
4309
+ if original_compute_cluster:
4310
+ self.set_compute_cluster(original_compute_cluster)
4311
+ else:
4312
+ self.session.headers = original_headers
4269
4313
 
4270
4314
  if verbose:
4271
4315
  print(f"⏳ Waiting for training to complete...")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: featrixsphere
3
- Version: 0.2.1232
3
+ Version: 0.2.1235
4
4
  Summary: Transform any CSV into a production-ready ML model in minutes, not months.
5
5
  Home-page: https://github.com/Featrix/sphere
6
6
  Author: Featrix
@@ -1 +0,0 @@
1
- 0.2.1232