codex-lb 0.1.4__py3-none-any.whl → 0.1.5__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.
- app/core/balancer/logic.py +5 -12
- app/core/utils/retry.py +14 -0
- app/modules/proxy/load_balancer.py +3 -3
- app/static/index.js +3 -11
- {codex_lb-0.1.4.dist-info → codex_lb-0.1.5.dist-info}/METADATA +1 -1
- {codex_lb-0.1.4.dist-info → codex_lb-0.1.5.dist-info}/RECORD +9 -9
- {codex_lb-0.1.4.dist-info → codex_lb-0.1.5.dist-info}/WHEEL +0 -0
- {codex_lb-0.1.4.dist-info → codex_lb-0.1.5.dist-info}/entry_points.txt +0 -0
- {codex_lb-0.1.4.dist-info → codex_lb-0.1.5.dist-info}/licenses/LICENSE +0 -0
app/core/balancer/logic.py
CHANGED
|
@@ -5,7 +5,7 @@ from dataclasses import dataclass
|
|
|
5
5
|
from typing import Iterable
|
|
6
6
|
|
|
7
7
|
from app.core.balancer.types import UpstreamError
|
|
8
|
-
from app.core.utils.retry import parse_retry_after
|
|
8
|
+
from app.core.utils.retry import backoff_seconds, parse_retry_after
|
|
9
9
|
from app.db.models import AccountStatus
|
|
10
10
|
|
|
11
11
|
PERMANENT_FAILURE_CODES = {
|
|
@@ -22,7 +22,7 @@ class AccountState:
|
|
|
22
22
|
account_id: str
|
|
23
23
|
status: AccountStatus
|
|
24
24
|
used_percent: float | None = None
|
|
25
|
-
reset_at:
|
|
25
|
+
reset_at: float | None = None
|
|
26
26
|
last_error_at: float | None = None
|
|
27
27
|
last_selected_at: float | None = None
|
|
28
28
|
error_count: int = 0
|
|
@@ -97,18 +97,11 @@ def handle_rate_limit(state: AccountState, error: UpstreamError) -> None:
|
|
|
97
97
|
state.status = AccountStatus.RATE_LIMITED
|
|
98
98
|
state.error_count += 1
|
|
99
99
|
state.last_error_at = time.time()
|
|
100
|
-
|
|
101
|
-
reset_at = _extract_reset_at(error)
|
|
102
|
-
if reset_at is not None:
|
|
103
|
-
state.reset_at = reset_at
|
|
104
|
-
return
|
|
105
|
-
|
|
106
100
|
message = error.get("message")
|
|
107
101
|
delay = parse_retry_after(message) if message else None
|
|
108
|
-
if delay:
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
state.reset_at = int(time.time() + 300)
|
|
102
|
+
if delay is None:
|
|
103
|
+
delay = backoff_seconds(state.error_count)
|
|
104
|
+
state.reset_at = time.time() + delay
|
|
112
105
|
|
|
113
106
|
|
|
114
107
|
def handle_quota_exceeded(state: AccountState, error: UpstreamError) -> None:
|
app/core/utils/retry.py
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import random
|
|
3
4
|
import re
|
|
4
5
|
|
|
5
6
|
_RETRY_PATTERN = re.compile(r"(?i)try again in\s*(\d+(?:\.\d+)?)\s*(s|ms|seconds?)")
|
|
7
|
+
_BACKOFF_INITIAL_DELAY_MS = 200
|
|
8
|
+
_BACKOFF_FACTOR = 2.0
|
|
9
|
+
_BACKOFF_JITTER_MIN = 0.9
|
|
10
|
+
_BACKOFF_JITTER_MAX = 1.1
|
|
6
11
|
|
|
7
12
|
|
|
8
13
|
def parse_retry_after(message: str) -> float | None:
|
|
@@ -14,3 +19,12 @@ def parse_retry_after(message: str) -> float | None:
|
|
|
14
19
|
if unit == "ms":
|
|
15
20
|
return value / 1000
|
|
16
21
|
return value
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def backoff_seconds(attempt: int) -> float:
|
|
25
|
+
if attempt < 1:
|
|
26
|
+
attempt = 1
|
|
27
|
+
exponent = _BACKOFF_FACTOR ** (attempt - 1)
|
|
28
|
+
base_ms = _BACKOFF_INITIAL_DELAY_MS * exponent
|
|
29
|
+
jitter = random.uniform(_BACKOFF_JITTER_MIN, _BACKOFF_JITTER_MAX)
|
|
30
|
+
return (base_ms * jitter) / 1000.0
|
|
@@ -20,7 +20,7 @@ from app.modules.usage.repository import UsageRepository
|
|
|
20
20
|
|
|
21
21
|
@dataclass
|
|
22
22
|
class RuntimeState:
|
|
23
|
-
reset_at:
|
|
23
|
+
reset_at: float | None = None
|
|
24
24
|
last_error_at: float | None = None
|
|
25
25
|
last_selected_at: float | None = None
|
|
26
26
|
error_count: int = 0
|
|
@@ -179,10 +179,10 @@ def _apply_secondary_quota(
|
|
|
179
179
|
*,
|
|
180
180
|
status: AccountStatus,
|
|
181
181
|
primary_used: float | None,
|
|
182
|
-
runtime_reset:
|
|
182
|
+
runtime_reset: float | None,
|
|
183
183
|
secondary_used: float | None,
|
|
184
184
|
secondary_reset: int | None,
|
|
185
|
-
) -> tuple[AccountStatus, float | None,
|
|
185
|
+
) -> tuple[AccountStatus, float | None, float | None]:
|
|
186
186
|
used_percent = primary_used
|
|
187
187
|
reset_at = runtime_reset
|
|
188
188
|
|
app/static/index.js
CHANGED
|
@@ -569,16 +569,9 @@
|
|
|
569
569
|
return acc;
|
|
570
570
|
}, {});
|
|
571
571
|
|
|
572
|
-
const mergeUsageIntoAccounts = (
|
|
573
|
-
accounts,
|
|
574
|
-
primaryUsage,
|
|
575
|
-
secondaryUsage,
|
|
576
|
-
summary,
|
|
577
|
-
) => {
|
|
572
|
+
const mergeUsageIntoAccounts = (accounts, primaryUsage, secondaryUsage) => {
|
|
578
573
|
const primaryMap = buildUsageIndex(primaryUsage || []);
|
|
579
574
|
const secondaryMap = buildUsageIndex(secondaryUsage || []);
|
|
580
|
-
const resetAtPrimary = summary?.primaryWindow?.resetAt ?? null;
|
|
581
|
-
const resetAtSecondary = summary?.secondaryWindow?.resetAt ?? null;
|
|
582
575
|
return accounts.map((account) => {
|
|
583
576
|
const primaryRow = primaryMap[account.id];
|
|
584
577
|
const secondaryRow = secondaryMap[account.id];
|
|
@@ -598,8 +591,8 @@
|
|
|
598
591
|
account.usage?.secondaryRemainingPercent ??
|
|
599
592
|
0,
|
|
600
593
|
},
|
|
601
|
-
resetAtPrimary:
|
|
602
|
-
resetAtSecondary:
|
|
594
|
+
resetAtPrimary: account.resetAtPrimary ?? null,
|
|
595
|
+
resetAtSecondary: account.resetAtSecondary ?? null,
|
|
603
596
|
};
|
|
604
597
|
});
|
|
605
598
|
};
|
|
@@ -1191,7 +1184,6 @@
|
|
|
1191
1184
|
accountsResult.value,
|
|
1192
1185
|
primaryUsage,
|
|
1193
1186
|
secondaryUsage,
|
|
1194
|
-
summary,
|
|
1195
1187
|
);
|
|
1196
1188
|
this.applyData(
|
|
1197
1189
|
{
|
|
@@ -10,7 +10,7 @@ app/core/auth/__init__.py,sha256=wR52dbGd66W1haAOUXn5RMzn3bj-EeXm4vm7zqI3kHE,281
|
|
|
10
10
|
app/core/auth/models.py,sha256=iyygu0uHK7fu8txyIyCkXmlgqYPbqEZu3IFysD1t-gk,1557
|
|
11
11
|
app/core/auth/refresh.py,sha256=jhYxT2mQFX4CCvuBQfYNcI53iOtWa0vSkTuZAfbpd9k,5105
|
|
12
12
|
app/core/balancer/__init__.py,sha256=bGy7gITRPObt6P9NdG3pg8t3qWoSW0XWoADqfNYrhdg,405
|
|
13
|
-
app/core/balancer/logic.py,sha256=
|
|
13
|
+
app/core/balancer/logic.py,sha256=aBuluReYRaYRmXNBntyUhOy1oPsK6Ol4Yt6WSkBLzn4,5059
|
|
14
14
|
app/core/balancer/types.py,sha256=gDgjlTy-NH3YhHYl2-YYpIabnckN9Q8-4cRy6S1u0K4,191
|
|
15
15
|
app/core/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
16
|
app/core/clients/http.py,sha256=yfFwsaIZNbSmNtyV02WnMKn00JdoNNSlSIx5xB3QY4o,987
|
|
@@ -30,7 +30,7 @@ app/core/usage/pricing.py,sha256=6p8rJ26Gk61mz2t_h9sa0T7NiPDUTiNpzoDewMzT6E0,546
|
|
|
30
30
|
app/core/usage/types.py,sha256=CbFF6JYSLvALa2P0qYuj9J9b_w53Fgjg0JOu4RzMWbI,2104
|
|
31
31
|
app/core/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
32
32
|
app/core/utils/request_id.py,sha256=gxafI-Se8dRQTir-HNGelMzC9S2gwYZntTkVzEqzp_I,705
|
|
33
|
-
app/core/utils/retry.py,sha256=
|
|
33
|
+
app/core/utils/retry.py,sha256=UmBap1Wh-CBT7r4fHzVb_PI9-LR9-HjUtDzRnhRjP2U,822
|
|
34
34
|
app/core/utils/sse.py,sha256=ER3_7mm50BUC1CPIKoVOmQikz-vlEWKNSRDEVqzI_KA,387
|
|
35
35
|
app/core/utils/time.py,sha256=B6FfSe43Eq_puE6eourly1X3gajyihK2VOAwJ8M3wyI,497
|
|
36
36
|
app/db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -52,7 +52,7 @@ app/modules/oauth/templates/oauth_success.html,sha256=YNSGUIozcZEJQjpFtM2sgF4n8j
|
|
|
52
52
|
app/modules/proxy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
53
53
|
app/modules/proxy/api.py,sha256=BR_qNlNZg2Ft5GYQ-7AzlLlJFf6RVJmlzJzFr_KHUIc,2921
|
|
54
54
|
app/modules/proxy/auth_manager.py,sha256=sFcbStd-OK0g8S56z7XcSkFJ22kEirBU0fYdz2drS0o,2185
|
|
55
|
-
app/modules/proxy/load_balancer.py,sha256=
|
|
55
|
+
app/modules/proxy/load_balancer.py,sha256=L1a7duA4mcR61huXKU052QNz4JAGgUwhMuOPpv5BV4w,7447
|
|
56
56
|
app/modules/proxy/schemas.py,sha256=55pXtUCl2R_93kAPOJJ7Ji4Jn3qVu10vq2KSCCkNdp4,2748
|
|
57
57
|
app/modules/proxy/service.py,sha256=XBpL1OIZuzss8nvsYajC5KsaRdGztjci5RgnWORZ1ss,26816
|
|
58
58
|
app/modules/proxy/types.py,sha256=iqEyoO8vGr8N5oEzUSvVWCai7UZbJAU62IvO7JNS9qs,927
|
|
@@ -72,9 +72,9 @@ app/modules/usage/service.py,sha256=_LQA3-L1FxIpzPoIQ-rxz3n3KjiHpTSxRimZapzwx3o,
|
|
|
72
72
|
app/static/7.css,sha256=9EHW2Ouff2fRXgcQbYuCuglxTFgQZGNLLTXKTS6S5aI,80687
|
|
73
73
|
app/static/index.css,sha256=ct1FfBg0PY7c6KxSS6A7JM_WDwdeJqhQa4pXTgyJ19Q,8786
|
|
74
74
|
app/static/index.html,sha256=FmWQ0l40rTBsHIR9i0ttGTzG17ePASsO2VDv6Q48CNw,24477
|
|
75
|
-
app/static/index.js,sha256=
|
|
76
|
-
codex_lb-0.1.
|
|
77
|
-
codex_lb-0.1.
|
|
78
|
-
codex_lb-0.1.
|
|
79
|
-
codex_lb-0.1.
|
|
80
|
-
codex_lb-0.1.
|
|
75
|
+
app/static/index.js,sha256=2gbVWp_Ot00NLdPpLE2Cc-YWk38H04x8TZw3OFO2SKk,52209
|
|
76
|
+
codex_lb-0.1.5.dist-info/METADATA,sha256=Cjw3NQBgM5hFgPAQUZ81PFBOI2mAq8SiwViALHOkxxA,3828
|
|
77
|
+
codex_lb-0.1.5.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
78
|
+
codex_lb-0.1.5.dist-info/entry_points.txt,sha256=SEa5T6Uz2Fhy574No6Y0XyGmYi3PXLrhu2xStJTqyI8,42
|
|
79
|
+
codex_lb-0.1.5.dist-info/licenses/LICENSE,sha256=cHPibxiL0TXwrUX_kNY6ym544EX1UCzKhxdaca5cFuk,1062
|
|
80
|
+
codex_lb-0.1.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|