featrixsphere 0.2.1233__py3-none-any.whl → 0.2.1237__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.
featrixsphere/__init__.py CHANGED
@@ -38,7 +38,7 @@ Example:
38
38
  ... labels=['Experiment A', 'Experiment B'])
39
39
  """
40
40
 
41
- __version__ = "0.2.1233"
41
+ __version__ = "0.2.1237"
42
42
  __author__ = "Featrix"
43
43
  __email__ = "support@featrix.com"
44
44
  __license__ = "MIT"
featrixsphere/client.py CHANGED
@@ -4194,93 +4194,126 @@ class FeatrixSphereClient:
4194
4194
  print(f"Training predictor on foundation model {foundation_model_id}...")
4195
4195
  print(f" Target: {target_column} ({target_column_type})")
4196
4196
 
4197
- # Validate that only one data source is provided
4198
- if input_filename and df is not None:
4199
- raise ValueError("Provide either input_filename or df, not both")
4200
-
4201
- # If DataFrame provided, save to temp file and upload it
4202
- temp_file = None
4203
- if df is not None:
4204
- import pandas as pd
4205
- import tempfile
4206
- import os
4207
-
4208
- if not isinstance(df, pd.DataFrame):
4209
- raise ValueError("df must be a pandas DataFrame")
4210
-
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)
4211
4208
  if verbose:
4212
- print(f"📊 Using provided DataFrame ({len(df)} rows, {len(df.columns)} columns)")
4213
-
4214
- # Create temporary CSV file
4215
- temp_file = tempfile.NamedTemporaryFile(mode='w', suffix='.csv', delete=False)
4216
- temp_file_path = temp_file.name
4217
- temp_file.close()
4218
-
4219
- # Save DataFrame to temp file
4220
- df.to_csv(temp_file_path, index=False)
4221
-
4222
- if verbose:
4223
- print(f"📁 Saved to temporary file: {os.path.basename(temp_file_path)}")
4224
- print(f"📤 Uploading file to server...")
4225
-
4226
- # Upload the file
4227
- uploaded_filename = self.upload_file(temp_file_path)
4228
- input_filename = uploaded_filename
4209
+ print(f" Using compute cluster: {foundation_compute_cluster}")
4210
+
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")
4229
4215
 
4230
- if verbose:
4231
- print(f"✅ File uploaded: {input_filename}")
4216
+ # Prepare multipart form data (like train_single_predictor_with_file does)
4217
+ files = None
4218
+ data = {
4219
+ "foundation_model_id": foundation_model_id,
4220
+ "target_column": target_column,
4221
+ "target_column_type": target_column_type,
4222
+ "epochs": str(epochs),
4223
+ "optimize_for": optimize_for,
4224
+ }
4232
4225
 
4233
- # Clean up temp file
4234
- try:
4235
- os.unlink(temp_file_path)
4236
- except Exception:
4237
- pass # Ignore cleanup errors
4238
-
4239
- data = {
4240
- "foundation_model_id": foundation_model_id,
4241
- "target_column": target_column,
4242
- "target_column_type": target_column_type,
4243
- "epochs": epochs,
4244
- "optimize_for": optimize_for,
4245
- }
4246
-
4247
- if input_filename:
4248
- # If absolute path provided, upload the file first
4249
- from pathlib import Path
4250
- input_path = Path(input_filename)
4251
- if input_path.is_absolute():
4252
- # Upload the file first, then use the uploaded filename
4253
- if not input_path.exists():
4254
- raise FileNotFoundError(f"Input file not found: {input_filename}")
4226
+ # Handle file upload - send file directly in multipart form
4227
+ if df is not None:
4228
+ import pandas as pd
4229
+ import tempfile
4230
+ import os
4231
+
4232
+ if not isinstance(df, pd.DataFrame):
4233
+ raise ValueError("df must be a pandas DataFrame")
4255
4234
 
4256
4235
  if verbose:
4257
- print(f"📤 Uploading file from absolute path: {input_filename}")
4236
+ print(f"📊 Using provided DataFrame ({len(df)} rows, {len(df.columns)} columns)")
4237
+
4238
+ # Create temporary CSV file
4239
+ temp_file = tempfile.NamedTemporaryFile(mode='w', suffix='.csv', delete=False)
4240
+ temp_file_path = temp_file.name
4241
+ temp_file.close()
4258
4242
 
4259
- # Upload the file
4260
- uploaded_filename = self.upload_file(str(input_path))
4243
+ # Save DataFrame to temp file
4244
+ df.to_csv(temp_file_path, index=False)
4261
4245
 
4262
4246
  if verbose:
4263
- print(f" File uploaded as: {uploaded_filename}")
4247
+ print(f"📁 Saved to temporary file: {os.path.basename(temp_file_path)}")
4248
+ print(f"📤 Uploading file directly with training request...")
4264
4249
 
4265
- data["input_filename"] = uploaded_filename
4266
- else:
4267
- # Relative filename - assume it's already on the server
4268
- data["input_filename"] = input_filename
4269
- if name:
4270
- data["name"] = name
4271
- if session_name_prefix:
4272
- data["session_name_prefix"] = session_name_prefix
4273
- if rare_label_value:
4274
- data["rare_label_value"] = rare_label_value
4275
- if class_imbalance:
4276
- data["class_imbalance"] = class_imbalance
4277
- if webhooks:
4278
- data["webhooks"] = webhooks
4279
-
4280
- response_data = self._post_json("/compute/train_on_foundational_model", data)
4281
-
4282
- new_session_id = response_data.get('session_id')
4283
- print(f" Predictor training session created: {new_session_id}")
4250
+ # Send file in multipart form
4251
+ files = {'file': (os.path.basename(temp_file_path), open(temp_file_path, 'rb'), 'text/csv')}
4252
+
4253
+ elif input_filename:
4254
+ # If absolute path provided, send file directly
4255
+ from pathlib import Path
4256
+ input_path = Path(input_filename)
4257
+ if input_path.is_absolute():
4258
+ if not input_path.exists():
4259
+ raise FileNotFoundError(f"Input file not found: {input_filename}")
4260
+
4261
+ if verbose:
4262
+ print(f"📤 Sending file directly from absolute path: {input_filename}")
4263
+
4264
+ # Send file in multipart form
4265
+ files = {'file': (input_path.name, open(input_path, 'rb'), 'text/csv' if input_path.suffix == '.csv' else 'application/gzip')}
4266
+ else:
4267
+ # Relative filename - assume it's already on the server
4268
+ data["input_filename"] = input_filename
4269
+
4270
+ if name:
4271
+ data["name"] = name
4272
+ if session_name_prefix:
4273
+ data["session_name_prefix"] = session_name_prefix
4274
+ if rare_label_value:
4275
+ data["rare_label_value"] = rare_label_value
4276
+ if class_imbalance:
4277
+ import json
4278
+ data["class_imbalance"] = json.dumps(class_imbalance)
4279
+ if webhooks:
4280
+ import json
4281
+ data["webhooks"] = json.dumps(webhooks)
4282
+
4283
+ # Send request with file if provided
4284
+ try:
4285
+ if files:
4286
+ response = self._make_request("POST", "/compute/train_on_foundational_model", files=files, data=data)
4287
+ else:
4288
+ response = self._make_request("POST", "/compute/train_on_foundational_model", json=data)
4289
+ response_data = response.json()
4290
+ finally:
4291
+ # Close file handles
4292
+ if files and 'file' in files:
4293
+ files['file'][1].close()
4294
+ # Clean up temp file if we created one
4295
+ if df is not None and temp_file_path:
4296
+ try:
4297
+ os.unlink(temp_file_path)
4298
+ except Exception:
4299
+ pass
4300
+
4301
+ new_session_id = response_data.get('session_id')
4302
+ print(f"✅ Predictor training session created: {new_session_id}")
4303
+
4304
+ # Restore original compute cluster setting
4305
+ if original_compute_cluster != self.compute_cluster:
4306
+ if original_compute_cluster:
4307
+ self.set_compute_cluster(original_compute_cluster)
4308
+ else:
4309
+ self.session.headers = original_headers
4310
+ finally:
4311
+ # Ensure we restore headers even if there's an error
4312
+ if original_compute_cluster != self.compute_cluster:
4313
+ if original_compute_cluster:
4314
+ self.set_compute_cluster(original_compute_cluster)
4315
+ else:
4316
+ self.session.headers = original_headers
4284
4317
 
4285
4318
  if verbose:
4286
4319
  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.1233
3
+ Version: 0.2.1237
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,9 @@
1
+ featrixsphere/__init__.py,sha256=B4omX54pm9GR-ascatiSaHJPIOziXObJlkcE1U5Lte0,1888
2
+ featrixsphere/cli.py,sha256=AW9O3vCvCNJ2UxVGN66eRmeN7XLSiHJlvK6JLZ9UJXc,13358
3
+ featrixsphere/client.py,sha256=5BgOKD0lC1Tsnkj1NsKkx_QMdvQJM3Qn8F6DfQjlCQ8,384584
4
+ featrixsphere/test_client.py,sha256=4SiRbib0ms3poK0UpnUv4G0HFQSzidF3Iswo_J2cjLk,11981
5
+ featrixsphere-0.2.1237.dist-info/METADATA,sha256=UOQ8XXBXkiZzIvATWl8X1xIkcGM8soxDYfs9IzY5u7k,16232
6
+ featrixsphere-0.2.1237.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
7
+ featrixsphere-0.2.1237.dist-info/entry_points.txt,sha256=QreJeYfD_VWvbEqPmMXZ3pqqlFlJ1qZb-NtqnyhEldc,51
8
+ featrixsphere-0.2.1237.dist-info/top_level.txt,sha256=AyN4wjfzlD0hWnDieuEHX0KckphIk_aC73XCG4df5uU,14
9
+ featrixsphere-0.2.1237.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- featrixsphere/__init__.py,sha256=Iwvv27mlpZjhl7DSUg6kz9Qa24K1S-o5aa-QqwgwcFA,1888
2
- featrixsphere/cli.py,sha256=AW9O3vCvCNJ2UxVGN66eRmeN7XLSiHJlvK6JLZ9UJXc,13358
3
- featrixsphere/client.py,sha256=xjYFie7KxepDRp8j8MA0divHyhC_t7fLnjgQUClV0_c,382285
4
- featrixsphere/test_client.py,sha256=4SiRbib0ms3poK0UpnUv4G0HFQSzidF3Iswo_J2cjLk,11981
5
- featrixsphere-0.2.1233.dist-info/METADATA,sha256=lQDkfyCT4NMnCnob9Vhgv77YfKrH96XNSoaP0IFpsZM,16232
6
- featrixsphere-0.2.1233.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
7
- featrixsphere-0.2.1233.dist-info/entry_points.txt,sha256=QreJeYfD_VWvbEqPmMXZ3pqqlFlJ1qZb-NtqnyhEldc,51
8
- featrixsphere-0.2.1233.dist-info/top_level.txt,sha256=AyN4wjfzlD0hWnDieuEHX0KckphIk_aC73XCG4df5uU,14
9
- featrixsphere-0.2.1233.dist-info/RECORD,,