mainsequence 9.0.6.dev40__py3-none-any.whl → 9.0.6.dev41__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.
- mainsequence/client/_transport.py +154 -0
- mainsequence/client/client.py +17 -11
- mainsequence/client/inference.py +2 -2
- mainsequence/client/utils.py +43 -115
- {mainsequence-9.0.6.dev40.dist-info → mainsequence-9.0.6.dev41.dist-info}/METADATA +1 -1
- {mainsequence-9.0.6.dev40.dist-info → mainsequence-9.0.6.dev41.dist-info}/RECORD +10 -9
- {mainsequence-9.0.6.dev40.dist-info → mainsequence-9.0.6.dev41.dist-info}/WHEEL +0 -0
- {mainsequence-9.0.6.dev40.dist-info → mainsequence-9.0.6.dev41.dist-info}/entry_points.txt +0 -0
- {mainsequence-9.0.6.dev40.dist-info → mainsequence-9.0.6.dev41.dist-info}/licenses/LICENSE +0 -0
- {mainsequence-9.0.6.dev40.dist-info → mainsequence-9.0.6.dev41.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"""One bounded retry policy for SDK HTTP sessions."""
|
|
2
|
+
|
|
3
|
+
import math
|
|
4
|
+
import time
|
|
5
|
+
from contextlib import contextmanager
|
|
6
|
+
from contextvars import ContextVar
|
|
7
|
+
|
|
8
|
+
import requests
|
|
9
|
+
from requests.adapters import HTTPAdapter
|
|
10
|
+
from urllib3.exceptions import InvalidHeader
|
|
11
|
+
from urllib3.util import Retry, Timeout
|
|
12
|
+
|
|
13
|
+
DEFAULT_ALLOWED_METHODS = frozenset({"HEAD", "GET", "OPTIONS"})
|
|
14
|
+
DEFAULT_STATUS_FORCELIST = (429, 500, 502, 503, 504)
|
|
15
|
+
DEFAULT_TIMEOUT = (5.0, 120.0)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class RequestBudget:
|
|
19
|
+
def __init__(self, timeout):
|
|
20
|
+
if timeout is None:
|
|
21
|
+
timeout = DEFAULT_TIMEOUT
|
|
22
|
+
if isinstance(timeout, Timeout):
|
|
23
|
+
connect, read = timeout.connect_timeout, timeout.read_timeout
|
|
24
|
+
total = timeout.total
|
|
25
|
+
elif isinstance(timeout, tuple):
|
|
26
|
+
connect, read = timeout
|
|
27
|
+
total = None
|
|
28
|
+
else:
|
|
29
|
+
connect = read = total = timeout
|
|
30
|
+
self.connect = self._seconds(connect)
|
|
31
|
+
self.read = self._seconds(read)
|
|
32
|
+
self.deadline = time.monotonic() + self._seconds(
|
|
33
|
+
self.connect + self.read if total is None else total
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
@staticmethod
|
|
37
|
+
def _seconds(value):
|
|
38
|
+
seconds = float(value)
|
|
39
|
+
if not math.isfinite(seconds) or seconds <= 0:
|
|
40
|
+
raise ValueError("Request timeouts must be positive, finite seconds.")
|
|
41
|
+
return seconds
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def remaining(self):
|
|
45
|
+
return self.deadline - time.monotonic()
|
|
46
|
+
|
|
47
|
+
def timeout(self):
|
|
48
|
+
remaining = self.remaining
|
|
49
|
+
if remaining <= 0:
|
|
50
|
+
raise requests.Timeout("The SDK request's total timeout budget is exhausted.")
|
|
51
|
+
return Timeout(
|
|
52
|
+
total=remaining,
|
|
53
|
+
connect=min(self.connect, remaining),
|
|
54
|
+
read=min(self.read, remaining),
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
_ACTIVE_BUDGET: ContextVar[RequestBudget | None] = ContextVar("sdk_request_budget", default=None)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@contextmanager
|
|
62
|
+
def request_budget(timeout):
|
|
63
|
+
budget = RequestBudget(timeout)
|
|
64
|
+
parent = _ACTIVE_BUDGET.get()
|
|
65
|
+
if parent is not None:
|
|
66
|
+
budget.deadline = min(budget.deadline, parent.deadline)
|
|
67
|
+
token = _ACTIVE_BUDGET.set(budget)
|
|
68
|
+
try:
|
|
69
|
+
yield budget
|
|
70
|
+
finally:
|
|
71
|
+
_ACTIVE_BUDGET.reset(token)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def remaining_timeout(timeout):
|
|
75
|
+
budget = _ACTIVE_BUDGET.get()
|
|
76
|
+
if budget is None:
|
|
77
|
+
return timeout
|
|
78
|
+
connect, read = timeout
|
|
79
|
+
remaining = budget.timeout().total
|
|
80
|
+
return Timeout(total=remaining, connect=min(connect, remaining), read=min(read, remaining))
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class DeadlineHTTPAdapter(HTTPAdapter):
|
|
84
|
+
def __init__(self, *, retries, backoff_factor):
|
|
85
|
+
if not isinstance(retries, int) or retries < 0:
|
|
86
|
+
raise ValueError("retries must be a non-negative integer.")
|
|
87
|
+
if not math.isfinite(backoff_factor) or backoff_factor < 0:
|
|
88
|
+
raise ValueError("backoff_factor must be non-negative and finite.")
|
|
89
|
+
# urllib3 must never add a second retry loop or resend a write on connect failure.
|
|
90
|
+
super().__init__(
|
|
91
|
+
max_retries=Retry(
|
|
92
|
+
total=0,
|
|
93
|
+
connect=0,
|
|
94
|
+
read=False,
|
|
95
|
+
status=0,
|
|
96
|
+
other=0,
|
|
97
|
+
allowed_methods=DEFAULT_ALLOWED_METHODS,
|
|
98
|
+
)
|
|
99
|
+
)
|
|
100
|
+
self.retries = retries
|
|
101
|
+
self.backoff_factor = backoff_factor
|
|
102
|
+
|
|
103
|
+
def send(self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None):
|
|
104
|
+
budget = RequestBudget(timeout)
|
|
105
|
+
parent = _ACTIVE_BUDGET.get()
|
|
106
|
+
if parent is not None:
|
|
107
|
+
budget.deadline = min(budget.deadline, parent.deadline)
|
|
108
|
+
retryable = request.method in DEFAULT_ALLOWED_METHODS
|
|
109
|
+
for attempt in range(self.retries + 1):
|
|
110
|
+
response = None
|
|
111
|
+
error = None
|
|
112
|
+
try:
|
|
113
|
+
response = super().send(
|
|
114
|
+
request,
|
|
115
|
+
stream=stream,
|
|
116
|
+
timeout=budget.timeout(),
|
|
117
|
+
verify=verify,
|
|
118
|
+
cert=cert,
|
|
119
|
+
proxies=proxies,
|
|
120
|
+
)
|
|
121
|
+
if not stream:
|
|
122
|
+
# Read before deciding to retry, so body failures share the same budget.
|
|
123
|
+
_ = response.content
|
|
124
|
+
if not retryable or response.status_code not in DEFAULT_STATUS_FORCELIST:
|
|
125
|
+
return response
|
|
126
|
+
except requests.exceptions.SSLError:
|
|
127
|
+
raise
|
|
128
|
+
except (
|
|
129
|
+
requests.ConnectionError,
|
|
130
|
+
requests.Timeout,
|
|
131
|
+
requests.exceptions.ChunkedEncodingError,
|
|
132
|
+
) as exc:
|
|
133
|
+
error = exc
|
|
134
|
+
|
|
135
|
+
delay = min(self.backoff_factor * 2**attempt, Retry.DEFAULT_BACKOFF_MAX)
|
|
136
|
+
if error is None:
|
|
137
|
+
try:
|
|
138
|
+
retry_after = self.max_retries.get_retry_after(response)
|
|
139
|
+
except InvalidHeader:
|
|
140
|
+
retry_after = None
|
|
141
|
+
if retry_after is not None:
|
|
142
|
+
delay = max(delay, retry_after)
|
|
143
|
+
|
|
144
|
+
if not retryable or attempt == self.retries or delay >= budget.remaining:
|
|
145
|
+
if error is not None:
|
|
146
|
+
if response is not None:
|
|
147
|
+
response.close()
|
|
148
|
+
raise error
|
|
149
|
+
return response
|
|
150
|
+
if response is not None:
|
|
151
|
+
response.close()
|
|
152
|
+
time.sleep(delay)
|
|
153
|
+
|
|
154
|
+
raise AssertionError("Unreachable retry state")
|
mainsequence/client/client.py
CHANGED
|
@@ -9,7 +9,7 @@ import requests
|
|
|
9
9
|
from mainsequence.defaults import resolve_backend_endpoint
|
|
10
10
|
|
|
11
11
|
from .exceptions import raise_for_response
|
|
12
|
-
from .utils import AuthLoaders, build_session
|
|
12
|
+
from .utils import AuthLoaders, build_session, make_request
|
|
13
13
|
from .utils import loaders as _global_loaders
|
|
14
14
|
from .utils import session as _global_session
|
|
15
15
|
|
|
@@ -35,6 +35,7 @@ class MainSequenceClient:
|
|
|
35
35
|
- Uses ONE shared loaders + session (from utils) by default
|
|
36
36
|
- Can be reconfigured once at startup
|
|
37
37
|
"""
|
|
38
|
+
|
|
38
39
|
_default: MainSequenceClient | None = None
|
|
39
40
|
|
|
40
41
|
def __init__(
|
|
@@ -72,15 +73,18 @@ class MainSequenceClient:
|
|
|
72
73
|
timeout: tuple[float, float] | float | None = None,
|
|
73
74
|
) -> Any:
|
|
74
75
|
url = self.config.api_root.rstrip("/") + "/" + path.lstrip("/")
|
|
75
|
-
to =
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
76
|
+
to = self.config.timeout if timeout is None else timeout
|
|
77
|
+
payload = {"params": params, "json": json}
|
|
78
|
+
if files is not None:
|
|
79
|
+
payload["files"] = files
|
|
80
|
+
r = make_request(
|
|
81
|
+
self.session,
|
|
82
|
+
method,
|
|
83
|
+
url,
|
|
84
|
+
self.loaders,
|
|
85
|
+
payload=payload,
|
|
86
|
+
time_out=to,
|
|
87
|
+
)
|
|
84
88
|
|
|
85
89
|
raise_for_response(r, payload={"params": params, "json": json})
|
|
86
90
|
|
|
@@ -95,7 +99,9 @@ class MainSequenceClient:
|
|
|
95
99
|
"""
|
|
96
100
|
if cls._default is None:
|
|
97
101
|
# Reuse the existing global singletons from utils to avoid two sessions in prod.
|
|
98
|
-
cls._default = cls(
|
|
102
|
+
cls._default = cls(
|
|
103
|
+
MainSequenceClientConfig(), loaders=_global_loaders, session=_global_session
|
|
104
|
+
)
|
|
99
105
|
return cls._default
|
|
100
106
|
|
|
101
107
|
@classmethod
|
mainsequence/client/inference.py
CHANGED
|
@@ -69,8 +69,8 @@ class InferenceClient:
|
|
|
69
69
|
if self.organization_environment_uid is not None:
|
|
70
70
|
query["organization_environment_uid"] = self.organization_environment_uid
|
|
71
71
|
url = f"{utils.API_ENDPOINT.rstrip('/')}/{path}"
|
|
72
|
-
#
|
|
73
|
-
#
|
|
72
|
+
# Inference opts out of safe-read retries as well: an uncertain outcome
|
|
73
|
+
# must preserve the caller's explicit idempotency key.
|
|
74
74
|
with utils.build_session(loaders=utils.loaders, retries=0) as session:
|
|
75
75
|
headers = dict(utils.loaders.refresh_headers(force=False, session=session))
|
|
76
76
|
if key is not None:
|
mainsequence/client/utils.py
CHANGED
|
@@ -17,26 +17,21 @@ from uuid import UUID, getnode
|
|
|
17
17
|
|
|
18
18
|
import psutil
|
|
19
19
|
import requests
|
|
20
|
-
from requests.adapters import HTTPAdapter
|
|
21
20
|
from requests.structures import CaseInsensitiveDict
|
|
22
|
-
from urllib3.util.retry import Retry
|
|
23
21
|
|
|
24
22
|
from mainsequence.defaults import resolve_backend_endpoint
|
|
25
23
|
from mainsequence.logconf import logger
|
|
26
24
|
|
|
25
|
+
from ._transport import DEFAULT_ALLOWED_METHODS as DEFAULT_ALLOWED_METHODS
|
|
26
|
+
from ._transport import DEFAULT_STATUS_FORCELIST as DEFAULT_STATUS_FORCELIST
|
|
27
|
+
from ._transport import DEFAULT_TIMEOUT as DEFAULT_TIMEOUT
|
|
28
|
+
from ._transport import DeadlineHTTPAdapter, remaining_timeout, request_budget
|
|
29
|
+
|
|
27
30
|
# ---- Backend defaults (single source of truth) ----
|
|
28
31
|
MAINSEQUENCE_ENDPOINT = resolve_backend_endpoint()
|
|
29
32
|
API_ENDPOINT = f"{MAINSEQUENCE_ENDPOINT}/api/v1"
|
|
30
33
|
AUTH_ENDPOINT = MAINSEQUENCE_ENDPOINT.rstrip("/")
|
|
31
34
|
|
|
32
|
-
DEFAULT_STATUS_FORCELIST = (429, 500, 502, 503, 504)
|
|
33
|
-
DEFAULT_ALLOWED_METHODS = frozenset(["HEAD", "GET", "OPTIONS", "POST", "PUT", "PATCH", "DELETE"])
|
|
34
|
-
|
|
35
|
-
# requests supports either a float or (connect, read). Preserve previous ~120s read behavior,
|
|
36
|
-
# but add a sane connect timeout.
|
|
37
|
-
DEFAULT_TIMEOUT: tuple[float, float] = (5.0, 120.0)
|
|
38
|
-
|
|
39
|
-
|
|
40
35
|
DATE_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ"
|
|
41
36
|
|
|
42
37
|
|
|
@@ -383,7 +378,7 @@ class RuntimeCredentialAuthProvider(BaseAuthProvider):
|
|
|
383
378
|
"credential_secret": credential_secret,
|
|
384
379
|
},
|
|
385
380
|
headers={"Content-Type": "application/json"},
|
|
386
|
-
timeout=self.timeout,
|
|
381
|
+
timeout=remaining_timeout(self.timeout),
|
|
387
382
|
allow_redirects=False,
|
|
388
383
|
)
|
|
389
384
|
if response.status_code < 200 or response.status_code >= 300:
|
|
@@ -515,7 +510,7 @@ class JWTAuthProvider(BaseAuthProvider):
|
|
|
515
510
|
self.refresh_url,
|
|
516
511
|
json={"refresh": self.refresh_token},
|
|
517
512
|
headers={"Content-Type": "application/json"},
|
|
518
|
-
timeout=self.timeout,
|
|
513
|
+
timeout=remaining_timeout(self.timeout),
|
|
519
514
|
)
|
|
520
515
|
|
|
521
516
|
if r.status_code in _REFUSED_RENEWAL_STATUSES:
|
|
@@ -564,7 +559,7 @@ class JWTAuthProvider(BaseAuthProvider):
|
|
|
564
559
|
self.obtain_url,
|
|
565
560
|
json={"username": username, "password": password},
|
|
566
561
|
headers={"Content-Type": "application/json"},
|
|
567
|
-
timeout=self.timeout,
|
|
562
|
+
timeout=remaining_timeout(self.timeout),
|
|
568
563
|
)
|
|
569
564
|
r.raise_for_status()
|
|
570
565
|
data = r.json()
|
|
@@ -671,63 +666,46 @@ def make_request(
|
|
|
671
666
|
):
|
|
672
667
|
from requests.models import Response
|
|
673
668
|
|
|
674
|
-
TIMEOFF = 0.25
|
|
675
|
-
TRIES = int(15 // TIMEOFF)
|
|
676
669
|
timeout = DEFAULT_TIMEOUT if time_out is None else time_out
|
|
677
670
|
payload = {} if payload is None else payload
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
return session.get
|
|
682
|
-
elif r_type == "POST":
|
|
683
|
-
return session.post
|
|
684
|
-
elif r_type == "PUT":
|
|
685
|
-
return session.put
|
|
686
|
-
elif r_type == "PATCH":
|
|
687
|
-
return session.patch
|
|
688
|
-
elif r_type == "DELETE":
|
|
689
|
-
return session.delete
|
|
690
|
-
else:
|
|
691
|
-
raise NotImplementedError(f"Unsupported method: {r_type}")
|
|
671
|
+
r_type = r_type.upper()
|
|
672
|
+
if r_type not in DEFAULT_ALLOWED_METHODS | {"POST", "PUT", "PATCH", "DELETE"}:
|
|
673
|
+
raise NotImplementedError(f"Unsupported method: {r_type}")
|
|
692
674
|
|
|
693
675
|
request_kwargs = {}
|
|
694
676
|
if r_type in ("POST", "PATCH") and "files" in payload:
|
|
695
|
-
request_kwargs
|
|
696
|
-
request_kwargs["
|
|
677
|
+
request_kwargs = dict(payload)
|
|
678
|
+
request_kwargs["data"] = request_kwargs.pop("json", {})
|
|
697
679
|
s.headers.pop("Content-Type", None)
|
|
698
680
|
else:
|
|
699
|
-
request_kwargs = payload
|
|
700
|
-
|
|
701
|
-
req =
|
|
702
|
-
keep_request = True
|
|
703
|
-
counter = 0
|
|
704
|
-
auth_retried = False
|
|
681
|
+
request_kwargs = dict(payload)
|
|
682
|
+
request_kwargs.setdefault("allow_redirects", False)
|
|
683
|
+
req = getattr(s, r_type.lower())
|
|
705
684
|
|
|
706
685
|
if accept_gzip:
|
|
707
686
|
s.headers.setdefault("Accept-Encoding", "gzip")
|
|
708
687
|
|
|
709
|
-
|
|
688
|
+
with request_budget(timeout) as budget:
|
|
710
689
|
try:
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
break
|
|
690
|
+
for auth_attempt in range(2):
|
|
691
|
+
if loaders is not None:
|
|
692
|
+
budget.timeout()
|
|
693
|
+
s.headers.update(loaders.refresh_headers(force=bool(auth_attempt), session=s))
|
|
694
|
+
|
|
695
|
+
start_time = time.perf_counter()
|
|
696
|
+
logger.debug(f"Requesting {r_type} from {url}")
|
|
697
|
+
r = req(url, timeout=budget.timeout(), **request_kwargs)
|
|
698
|
+
duration = time.perf_counter() - start_time
|
|
699
|
+
logger.debug(f"{url} took {duration:.4f} seconds.")
|
|
700
|
+
if (
|
|
701
|
+
r.status_code != 401
|
|
702
|
+
or loaders is None
|
|
703
|
+
or auth_attempt
|
|
704
|
+
or r_type not in DEFAULT_ALLOWED_METHODS
|
|
705
|
+
):
|
|
706
|
+
return r
|
|
707
|
+
r.close()
|
|
708
|
+
logger.warning("Error 401; forcing auth refresh once within the request budget")
|
|
731
709
|
|
|
732
710
|
except AuthError as e:
|
|
733
711
|
logger.warning(f"Auth error for {url}: {e}")
|
|
@@ -736,32 +714,15 @@ def make_request(
|
|
|
736
714
|
r.error_type = "auth_error"
|
|
737
715
|
r.status_code = 401
|
|
738
716
|
r._content = str(e).encode("utf-8")
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
except requests.exceptions.ConnectionError:
|
|
743
|
-
logger.exception(f"Error connecting {url}")
|
|
744
|
-
except TypeError as e:
|
|
745
|
-
logger.exception(f"Type error for {url} exception {e}")
|
|
746
|
-
raise e
|
|
747
|
-
except Exception as e:
|
|
748
|
-
logger.exception(f"Error connecting {url} exception {e}")
|
|
749
|
-
|
|
750
|
-
counter += 1
|
|
751
|
-
if counter >= TRIES:
|
|
752
|
-
keep_request = False
|
|
717
|
+
return r
|
|
718
|
+
except requests.RequestException as e:
|
|
719
|
+
logger.warning(f"Request {r_type} to {url} failed: {e}")
|
|
753
720
|
r = Response()
|
|
754
721
|
r.code = "expired"
|
|
755
722
|
r.error_type = "expired"
|
|
756
723
|
r.status_code = 500
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
logger.debug(
|
|
760
|
-
f"Trying request again after {TIMEOFF}s - Counter: {counter}/{TRIES} - URL: {url}"
|
|
761
|
-
)
|
|
762
|
-
time.sleep(TIMEOFF)
|
|
763
|
-
|
|
764
|
-
return r
|
|
724
|
+
r._content = str(e).encode("utf-8")
|
|
725
|
+
return r
|
|
765
726
|
|
|
766
727
|
|
|
767
728
|
def build_session(
|
|
@@ -779,23 +740,7 @@ def build_session(
|
|
|
779
740
|
if accept_gzip:
|
|
780
741
|
s.headers.setdefault("Accept-Encoding", "gzip")
|
|
781
742
|
|
|
782
|
-
|
|
783
|
-
total=retries,
|
|
784
|
-
connect=retries,
|
|
785
|
-
read=retries,
|
|
786
|
-
status=retries,
|
|
787
|
-
backoff_factor=backoff_factor,
|
|
788
|
-
status_forcelist=DEFAULT_STATUS_FORCELIST,
|
|
789
|
-
respect_retry_after_header=True,
|
|
790
|
-
raise_on_status=False,
|
|
791
|
-
)
|
|
792
|
-
|
|
793
|
-
try:
|
|
794
|
-
retry_cfg = Retry(allowed_methods=DEFAULT_ALLOWED_METHODS, **retry_kwargs)
|
|
795
|
-
except TypeError:
|
|
796
|
-
retry_cfg = Retry(method_whitelist=DEFAULT_ALLOWED_METHODS, **retry_kwargs)
|
|
797
|
-
|
|
798
|
-
adapter = HTTPAdapter(max_retries=retry_cfg)
|
|
743
|
+
adapter = DeadlineHTTPAdapter(retries=retries, backoff_factor=backoff_factor)
|
|
799
744
|
s.mount("https://", adapter)
|
|
800
745
|
s.mount("http://", adapter)
|
|
801
746
|
return s
|
|
@@ -929,24 +874,7 @@ def _install_retry_adapters_in_place(
|
|
|
929
874
|
Configure retry adapters on an EXISTING session object (do not rebind 'session').
|
|
930
875
|
This is critical so 'from utils import session' users still get the updated behavior.
|
|
931
876
|
"""
|
|
932
|
-
|
|
933
|
-
total=retries,
|
|
934
|
-
connect=retries,
|
|
935
|
-
read=retries,
|
|
936
|
-
status=retries,
|
|
937
|
-
backoff_factor=backoff_factor,
|
|
938
|
-
status_forcelist=DEFAULT_STATUS_FORCELIST,
|
|
939
|
-
respect_retry_after_header=True,
|
|
940
|
-
raise_on_status=False,
|
|
941
|
-
)
|
|
942
|
-
|
|
943
|
-
# urllib3 compatibility across versions
|
|
944
|
-
try:
|
|
945
|
-
retry_cfg = Retry(allowed_methods=DEFAULT_ALLOWED_METHODS, **retry_kwargs)
|
|
946
|
-
except TypeError:
|
|
947
|
-
retry_cfg = Retry(method_whitelist=DEFAULT_ALLOWED_METHODS, **retry_kwargs)
|
|
948
|
-
|
|
949
|
-
adapter = HTTPAdapter(max_retries=retry_cfg)
|
|
877
|
+
adapter = DeadlineHTTPAdapter(retries=retries, backoff_factor=backoff_factor)
|
|
950
878
|
|
|
951
879
|
# Close old adapters' pools (best-effort), then mount new ones
|
|
952
880
|
for prefix in ("https://", "http://"):
|
|
@@ -34,27 +34,28 @@ mainsequence/cli/sdk_utils.py,sha256=SFuSzh8WDNf21mLiDa51K8Fqe5XiLWh2KhHj8zUjCXo
|
|
|
34
34
|
mainsequence/cli/ssh_utils.py,sha256=m7HgFHZQ545sxY9igfQLpaNjWHm4SiN9VwCkAuGy2yw,11124
|
|
35
35
|
mainsequence/cli/ui.py,sha256=memd6pmjALfGj8zndGCx_z80ztQeqTcOvcofU0un34U,2277
|
|
36
36
|
mainsequence/client/__init__.py,sha256=lsr9JWxeVqOG0KRoHErW5M40ACPSR7SAEE508dTN5XM,672
|
|
37
|
+
mainsequence/client/_transport.py,sha256=l2xkbJ21SbrJhzgOf58Yikxg-uzOQPyo-H5hENdCBo8,5374
|
|
37
38
|
mainsequence/client/agent_runtime_models.py,sha256=lay0DN3lN5p6-3ajVsaHvPgcmrURFwrNfTb6upoImm4,66642
|
|
38
39
|
mainsequence/client/base.py,sha256=T3k3xptM09TreiVogrxAPWoIy8zM6Dtn3Cbwsnb33ag,42678
|
|
39
|
-
mainsequence/client/client.py,sha256=
|
|
40
|
+
mainsequence/client/client.py,sha256=Jaw9RYObdVDS5J2O0pln99-XpuW9e4vW-SsbJRNldfw,3866
|
|
40
41
|
mainsequence/client/compute_validation.py,sha256=ByeX0CkIE7qq06ggZFfN92U3Qp2e9cvo2RTIGWa5_Vw,6662
|
|
41
42
|
mainsequence/client/exceptions.py,sha256=xx2L4l3OMNsTybbYkiTKK398ahnutEascM0It35tGr0,3815
|
|
42
43
|
mainsequence/client/github_issues.py,sha256=pSTUw3UPrmE6wqnpoJctartTaNSq6iu6PQRrd8hip6I,14568
|
|
43
|
-
mainsequence/client/inference.py,sha256=
|
|
44
|
+
mainsequence/client/inference.py,sha256=g24H9LWumMRABIevho8FS7BBOuyYvf80pRwCmJZpLeY,7353
|
|
44
45
|
mainsequence/client/models_foundry.py,sha256=iN1jJzDokfniEQF0Ac5eepAB7L5pVjJnKavaHLP7BHA,36310
|
|
45
46
|
mainsequence/client/models_helpers.py,sha256=y7cgcsH8yO3dfIWPhRdcYam7ItWHw3Ps63-WYDe4YHE,46262
|
|
46
47
|
mainsequence/client/models_user.py,sha256=BU_hfn4-4KqYz3nsaPlXuYZmUG6_-7YM892mkM92Zyo,37613
|
|
47
48
|
mainsequence/client/observability.py,sha256=_WrDKCaNk_TXmKElKCXC1inork6Exxxe8FRY4m1GI9s,16008
|
|
48
|
-
mainsequence/client/utils.py,sha256=
|
|
49
|
+
mainsequence/client/utils.py,sha256=c4JjMIqKfwSz8DdkwLGmGtQ9lSmVn_KEw9KWGDwz_T0,30133
|
|
49
50
|
mainsequence/client/value_sets.py,sha256=7TA-mpuAd3hOduNgO1WTQQTuNOGaFgue-VqCsDWKaC8,6181
|
|
50
51
|
mainsequence/instrumentation/__init__.py,sha256=J_9XmfjprElvz0u2t9rTyJT0kFdyKpxdbkdQiaw28_U,316
|
|
51
52
|
mainsequence/instrumentation/utils.py,sha256=b-ihjSioAepeLfMpudM2SqCtvnxMqIXileHx3klBCQU,3377
|
|
52
53
|
mainsequence/server/__init__.py,sha256=BHUAhtdWPDRdxX07NYO_6wrBv5mf0GsXflecO52PmUc,73
|
|
53
54
|
mainsequence/server/caller_assertions.py,sha256=hZW1pkkvQ9gaFL075bxUbbctTRrgMMQRTSc2Da0vKCM,9705
|
|
54
55
|
mainsequence/server/fastapi.py,sha256=tD1aVuJOgoBTu7H8bs9IldPNYRT4fhkr1leN3KZP1pI,10521
|
|
55
|
-
mainsequence-9.0.6.
|
|
56
|
-
mainsequence-9.0.6.
|
|
57
|
-
mainsequence-9.0.6.
|
|
58
|
-
mainsequence-9.0.6.
|
|
59
|
-
mainsequence-9.0.6.
|
|
60
|
-
mainsequence-9.0.6.
|
|
56
|
+
mainsequence-9.0.6.dev41.dist-info/licenses/LICENSE,sha256=b6Ecsk8wjsEby3lbSzdljR6hHiFGHBizt9iWV2p1c_I,1074
|
|
57
|
+
mainsequence-9.0.6.dev41.dist-info/METADATA,sha256=0Dok8XfsCUQJJDt_13aGFAKOU8jLBJzFUbqHm-yh7Uo,7402
|
|
58
|
+
mainsequence-9.0.6.dev41.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
59
|
+
mainsequence-9.0.6.dev41.dist-info/entry_points.txt,sha256=XGAOq3-mF2lWWpeRJvoaWyMarRcK167mH1g124aGdUs,88
|
|
60
|
+
mainsequence-9.0.6.dev41.dist-info/top_level.txt,sha256=BFeblM_Q9q0cUWBvTxnfWcWQEHtf5XZi9nOS-Gdfwus,28
|
|
61
|
+
mainsequence-9.0.6.dev41.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|