featrixsphere 0.2.1314__py3-none-any.whl → 0.2.1439__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 +1 -1
- featrixsphere/client.py +113 -29
- {featrixsphere-0.2.1314.dist-info → featrixsphere-0.2.1439.dist-info}/METADATA +1 -1
- featrixsphere-0.2.1439.dist-info/RECORD +9 -0
- featrixsphere-0.2.1314.dist-info/RECORD +0 -9
- {featrixsphere-0.2.1314.dist-info → featrixsphere-0.2.1439.dist-info}/WHEEL +0 -0
- {featrixsphere-0.2.1314.dist-info → featrixsphere-0.2.1439.dist-info}/entry_points.txt +0 -0
- {featrixsphere-0.2.1314.dist-info → featrixsphere-0.2.1439.dist-info}/top_level.txt +0 -0
featrixsphere/__init__.py
CHANGED
featrixsphere/client.py
CHANGED
|
@@ -715,53 +715,101 @@ class FeatrixSphereClient:
|
|
|
715
715
|
except Exception as e:
|
|
716
716
|
return False, f"Error checking foundation model: {e}"
|
|
717
717
|
|
|
718
|
-
def get_model_card(self, session_id: str, max_retries: int = None
|
|
718
|
+
def get_model_card(self, session_id: str, max_retries: int = None) -> Dict[str, Any]:
|
|
719
719
|
"""
|
|
720
720
|
Get the model card JSON for a given session.
|
|
721
721
|
|
|
722
|
+
Handles on-demand model card generation:
|
|
723
|
+
- If model card is being generated (202), waits and retries
|
|
724
|
+
- If model card doesn't exist (404) but session is DONE, retries (on-demand creation may be in progress)
|
|
725
|
+
|
|
722
726
|
Args:
|
|
723
727
|
session_id: The session ID to get the model card for
|
|
724
728
|
max_retries: Maximum number of retries (defaults to client default)
|
|
725
|
-
check_status_first: If True, check session status before fetching model card.
|
|
726
|
-
Provides better error messages if session is still training.
|
|
727
729
|
|
|
728
730
|
Returns:
|
|
729
731
|
Dictionary containing the model card JSON data
|
|
730
732
|
|
|
731
733
|
Raises:
|
|
732
|
-
requests.exceptions.HTTPError: If the request fails
|
|
733
|
-
FileNotFoundError: If the model card doesn't exist (404)
|
|
734
|
-
ValueError: If session is not ready and check_status_first is True
|
|
734
|
+
requests.exceptions.HTTPError: If the request fails after all retries
|
|
735
|
+
FileNotFoundError: If the model card doesn't exist (404) and can't be created
|
|
735
736
|
|
|
736
737
|
Example:
|
|
737
738
|
>>> client = FeatrixSphereClient()
|
|
738
739
|
>>> model_card = client.get_model_card("session_123")
|
|
739
740
|
>>> print(model_card["model_details"]["name"])
|
|
740
741
|
"""
|
|
741
|
-
|
|
742
|
-
|
|
742
|
+
if max_retries is None:
|
|
743
|
+
max_retries = self.default_max_retries
|
|
744
|
+
|
|
745
|
+
# Use a longer retry window for model card requests (on-demand generation can take time)
|
|
746
|
+
max_retry_time = 60.0 # 60 seconds for model card generation
|
|
747
|
+
start_time = time.time()
|
|
748
|
+
attempt = 0
|
|
749
|
+
|
|
750
|
+
while True:
|
|
751
|
+
attempt += 1
|
|
752
|
+
elapsed = time.time() - start_time
|
|
753
|
+
|
|
743
754
|
try:
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
755
|
+
response = self._make_request(
|
|
756
|
+
"GET",
|
|
757
|
+
f"/session/{session_id}/model_card",
|
|
758
|
+
max_retries=1, # Don't use default retries, handle manually
|
|
759
|
+
max_retry_time=None
|
|
760
|
+
)
|
|
761
|
+
|
|
762
|
+
# Check for 202 Accepted (generation in progress)
|
|
763
|
+
if response.status_code == 202:
|
|
764
|
+
response_data = response.json()
|
|
765
|
+
message = response_data.get("message", "Model card generation in progress")
|
|
766
|
+
|
|
767
|
+
# If we have time left, wait and retry
|
|
768
|
+
if elapsed < max_retry_time and attempt <= max_retries:
|
|
769
|
+
wait_time = min(5.0, max_retry_time - elapsed) # Wait up to 5 seconds
|
|
770
|
+
if wait_time > 0:
|
|
771
|
+
print(f"⏳ Model card generation in progress (attempt {attempt}/{max_retries}), waiting {wait_time:.1f}s...")
|
|
772
|
+
time.sleep(wait_time)
|
|
773
|
+
continue
|
|
774
|
+
else:
|
|
775
|
+
# Out of time or retries
|
|
776
|
+
raise requests.exceptions.HTTPError(
|
|
777
|
+
f"Model card generation timed out after {elapsed:.1f}s. {message}",
|
|
778
|
+
response=response
|
|
779
|
+
)
|
|
780
|
+
|
|
781
|
+
# Success - return the model card
|
|
782
|
+
return response.json()
|
|
783
|
+
|
|
751
784
|
except requests.exceptions.HTTPError as e:
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
785
|
+
if e.response is not None and e.response.status_code == 404:
|
|
786
|
+
# 404 - model card doesn't exist
|
|
787
|
+
# Check if we should retry (on-demand generation might be starting)
|
|
788
|
+
if elapsed < max_retry_time and attempt <= max_retries:
|
|
789
|
+
# Check session status to see if it's DONE (model card should exist or be creatable)
|
|
790
|
+
try:
|
|
791
|
+
session_status = self.get_session_status(session_id, max_retries=1)
|
|
792
|
+
if session_status.status in ["done", "DONE"]:
|
|
793
|
+
# Session is done, model card should be creatable - retry after a short delay
|
|
794
|
+
wait_time = min(3.0, max_retry_time - elapsed)
|
|
795
|
+
if wait_time > 0:
|
|
796
|
+
print(f"⏳ Model card not found for DONE session (attempt {attempt}/{max_retries}), retrying in {wait_time:.1f}s (on-demand generation may be starting)...")
|
|
797
|
+
time.sleep(wait_time)
|
|
798
|
+
continue
|
|
799
|
+
except Exception:
|
|
800
|
+
# If we can't check session status, just retry once more
|
|
801
|
+
if attempt <= 2:
|
|
802
|
+
wait_time = min(2.0, max_retry_time - elapsed)
|
|
803
|
+
if wait_time > 0:
|
|
804
|
+
print(f"⏳ Model card not found (attempt {attempt}/{max_retries}), retrying in {wait_time:.1f}s...")
|
|
805
|
+
time.sleep(wait_time)
|
|
806
|
+
continue
|
|
807
|
+
|
|
808
|
+
# Out of retries or session not DONE - raise the 404
|
|
809
|
+
raise FileNotFoundError(f"Model card not found for session {session_id}") from e
|
|
810
|
+
else:
|
|
811
|
+
# Other HTTP errors - re-raise
|
|
812
|
+
raise
|
|
765
813
|
|
|
766
814
|
def publish_session(self, session_id: str) -> Dict[str, Any]:
|
|
767
815
|
"""
|
|
@@ -915,7 +963,7 @@ class FeatrixSphereClient:
|
|
|
915
963
|
|
|
916
964
|
def wait_for_session_completion(self, session_id: str, max_wait_time: int = 3600,
|
|
917
965
|
check_interval: int = 10, show_live_training_movie: bool = None,
|
|
918
|
-
training_interval_movie: int = 3) -> SessionInfo:
|
|
966
|
+
training_interval_movie: int = 3, status_callback: callable = None) -> SessionInfo:
|
|
919
967
|
"""
|
|
920
968
|
Wait for a session to complete, with smart progress display.
|
|
921
969
|
|
|
@@ -926,10 +974,16 @@ class FeatrixSphereClient:
|
|
|
926
974
|
show_live_training_movie: If True, show live training visualization as epochs progress.
|
|
927
975
|
If None, auto-enable in notebook environments (default: None)
|
|
928
976
|
training_interval_movie: Show training movie updates every N epochs (default: 3)
|
|
977
|
+
status_callback: Optional callback function(session_info, elapsed_seconds) called on each status check.
|
|
978
|
+
If provided, uses this instead of default display methods.
|
|
929
979
|
|
|
930
980
|
Returns:
|
|
931
981
|
Final SessionInfo when session completes or times out
|
|
932
982
|
"""
|
|
983
|
+
# If callback provided, use it instead of display methods
|
|
984
|
+
if status_callback is not None:
|
|
985
|
+
return self._wait_with_callback(session_id, max_wait_time, check_interval, status_callback)
|
|
986
|
+
|
|
933
987
|
# Auto-enable live training movie in notebooks if not explicitly set
|
|
934
988
|
if show_live_training_movie is None:
|
|
935
989
|
show_live_training_movie = self._is_notebook()
|
|
@@ -977,6 +1031,14 @@ class FeatrixSphereClient:
|
|
|
977
1031
|
def _wait_with_smart_display(self, session_id: str, max_wait_time: int, check_interval: int, show_live_training_movie: bool = False, training_interval_movie: int = 3) -> SessionInfo:
|
|
978
1032
|
"""Smart progress display that adapts to environment."""
|
|
979
1033
|
|
|
1034
|
+
# Check if we're in a thread (not main thread) - Rich doesn't support multiple live displays
|
|
1035
|
+
import threading
|
|
1036
|
+
is_main_thread = threading.current_thread() is threading.main_thread()
|
|
1037
|
+
|
|
1038
|
+
# If not in main thread, use simple display to avoid Rich LiveError
|
|
1039
|
+
if not is_main_thread:
|
|
1040
|
+
return self._wait_with_simple_display(session_id, max_wait_time, check_interval)
|
|
1041
|
+
|
|
980
1042
|
if self._is_notebook():
|
|
981
1043
|
return self._wait_with_notebook_display(session_id, max_wait_time, check_interval, show_live_training_movie, training_interval_movie)
|
|
982
1044
|
elif self._has_rich():
|
|
@@ -1156,6 +1218,28 @@ class FeatrixSphereClient:
|
|
|
1156
1218
|
# Fallback if rich not available
|
|
1157
1219
|
return self._wait_with_simple_display(session_id, max_wait_time, check_interval)
|
|
1158
1220
|
|
|
1221
|
+
def _wait_with_callback(self, session_id: str, max_wait_time: int, check_interval: int, status_callback: callable) -> SessionInfo:
|
|
1222
|
+
"""Wait for session completion using a custom callback for status updates."""
|
|
1223
|
+
import time
|
|
1224
|
+
|
|
1225
|
+
start_time = time.time()
|
|
1226
|
+
|
|
1227
|
+
while time.time() - start_time < max_wait_time:
|
|
1228
|
+
session_info = self.get_session_status(session_id)
|
|
1229
|
+
elapsed = time.time() - start_time
|
|
1230
|
+
|
|
1231
|
+
# Call the callback with current status
|
|
1232
|
+
status_callback(session_info, elapsed)
|
|
1233
|
+
|
|
1234
|
+
# Check if completed
|
|
1235
|
+
if session_info.status in ['completed', 'done', 'DONE']:
|
|
1236
|
+
return session_info
|
|
1237
|
+
|
|
1238
|
+
time.sleep(check_interval)
|
|
1239
|
+
|
|
1240
|
+
# Timeout - return final status
|
|
1241
|
+
return self.get_session_status(session_id)
|
|
1242
|
+
|
|
1159
1243
|
def _wait_with_simple_display(self, session_id: str, max_wait_time: int, check_interval: int) -> SessionInfo:
|
|
1160
1244
|
"""Simple display with line overwriting for basic terminals."""
|
|
1161
1245
|
import sys
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
featrixsphere/__init__.py,sha256=7qEuBqtpDfJZYvEJQAVVrtdL-nZvI74oPx7tcVJN9Jo,1888
|
|
2
|
+
featrixsphere/cli.py,sha256=AW9O3vCvCNJ2UxVGN66eRmeN7XLSiHJlvK6JLZ9UJXc,13358
|
|
3
|
+
featrixsphere/client.py,sha256=NM-dBRANvN0GpcLf0BPqUxpqwMhzHP6hJktD8FndyCM,384646
|
|
4
|
+
featrixsphere/test_client.py,sha256=4SiRbib0ms3poK0UpnUv4G0HFQSzidF3Iswo_J2cjLk,11981
|
|
5
|
+
featrixsphere-0.2.1439.dist-info/METADATA,sha256=_KJ6nguFKv3xMG1kbWWS5_7hN2DjQEa-z3sloLIh5z4,16232
|
|
6
|
+
featrixsphere-0.2.1439.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
7
|
+
featrixsphere-0.2.1439.dist-info/entry_points.txt,sha256=QreJeYfD_VWvbEqPmMXZ3pqqlFlJ1qZb-NtqnyhEldc,51
|
|
8
|
+
featrixsphere-0.2.1439.dist-info/top_level.txt,sha256=AyN4wjfzlD0hWnDieuEHX0KckphIk_aC73XCG4df5uU,14
|
|
9
|
+
featrixsphere-0.2.1439.dist-info/RECORD,,
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
featrixsphere/__init__.py,sha256=S0XWndX6ycMk8j03X_Dt_GpMuCMG2k0uwzoVJ6EbbnA,1888
|
|
2
|
-
featrixsphere/cli.py,sha256=AW9O3vCvCNJ2UxVGN66eRmeN7XLSiHJlvK6JLZ9UJXc,13358
|
|
3
|
-
featrixsphere/client.py,sha256=c1axFTTB6Hvdu2cWngN0VnkBdU0W0neTDKwzIU-IFXc,380183
|
|
4
|
-
featrixsphere/test_client.py,sha256=4SiRbib0ms3poK0UpnUv4G0HFQSzidF3Iswo_J2cjLk,11981
|
|
5
|
-
featrixsphere-0.2.1314.dist-info/METADATA,sha256=gu4_0nXC3gk8GfKB-lDbgrZPhdvTQCRcUEIlIMtWy-I,16232
|
|
6
|
-
featrixsphere-0.2.1314.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
7
|
-
featrixsphere-0.2.1314.dist-info/entry_points.txt,sha256=QreJeYfD_VWvbEqPmMXZ3pqqlFlJ1qZb-NtqnyhEldc,51
|
|
8
|
-
featrixsphere-0.2.1314.dist-info/top_level.txt,sha256=AyN4wjfzlD0hWnDieuEHX0KckphIk_aC73XCG4df5uU,14
|
|
9
|
-
featrixsphere-0.2.1314.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|