feat(halacha): ספי-עצירה-רכים לדריינר — 5-שעות 75% / שבועי 65% (עצירה לפני 429) (#259)
Co-authored-by: Chaim <chaim@marcus-law.co.il> Co-committed-by: Chaim <chaim@marcus-law.co.il>
This commit was merged in pull request #259.
This commit is contained in:
@@ -114,7 +114,7 @@
|
|||||||
| `ingest_incoming_batch.py` | python | קליטת batch של החלטות ועדת ערר מ-`data/precedents/incoming/` דרך המסלול הקנוני (`ingest_internal_decision`) + חילוץ מטא-דאטה לכל תיק (המסלול הפנימי לא מתזמן metadata — INV-ING3). רצף (לא מקבילי, להימנע מעומס CLI). רשימת `DECISIONS` נערכת ידנית לכל batch. config מ-`~/.env`. תומך תהליך [[project_precedent_incoming_workflow]]. | ידני, per-batch (חלופה ל-MCP `internal_decision_upload` כש-batch גדול) |
|
| `ingest_incoming_batch.py` | python | קליטת batch של החלטות ועדת ערר מ-`data/precedents/incoming/` דרך המסלול הקנוני (`ingest_internal_decision`) + חילוץ מטא-דאטה לכל תיק (המסלול הפנימי לא מתזמן metadata — INV-ING3). רצף (לא מקבילי, להימנע מעומס CLI). רשימת `DECISIONS` נערכת ידנית לכל batch. config מ-`~/.env`. תומך תהליך [[project_precedent_incoming_workflow]]. | ידני, per-batch (חלופה ל-MCP `internal_decision_upload` כש-batch גדול) |
|
||||||
| `drain_halacha_queue.py` | python | ריקון תור חילוץ ההלכות (`process_pending_extractions kind='halacha'`) ב-batches של 4 עד שהתור ריק (2 סבבים ריקים). **רץ רק בחלון-לילה 23:00–05:00 שעון ישראל** (`_in_window`, zoneinfo DST-safe — המכונה UTC); מחוץ לחלון `===SKIP===`, ונעצר `===STOP===` כשהחלון נסגר (השאר ממשיך בלילה הבא, FIFO+checkpoint). env: `HALACHA_DRAIN_WINDOW_START`/`_END`/`HALACHA_DRAIN_TZ`. **kill-switch `/operations`:** בודק `is_drain_disabled` בעלייה **וגם בתחילת כל סבב** — כיבוי באמצע-ריצה עוצר את הלולאה בגבול-הסבב הבא (התהליך עצמו נהרג מיד דרך ה-UI-toggle/סופרוייזר). חילוץ-הלכות נשאר על claude_session (לא Gemini). self-heal ל-orphaned `processing`. ההלכות נוחתות `pending_review` (שער-יו"ר). **חילוץ תיק-בודד שהיו"ר מבקש רץ מיד דרך ה-CEO (`precedent_extract_halachot`) ואינו מגודר כאן.** | דרך `legal-halacha-drain.config.cjs` (pm2 cron) / ידני |
|
| `drain_halacha_queue.py` | python | ריקון תור חילוץ ההלכות (`process_pending_extractions kind='halacha'`) ב-batches של 4 עד שהתור ריק (2 סבבים ריקים). **רץ רק בחלון-לילה 23:00–05:00 שעון ישראל** (`_in_window`, zoneinfo DST-safe — המכונה UTC); מחוץ לחלון `===SKIP===`, ונעצר `===STOP===` כשהחלון נסגר (השאר ממשיך בלילה הבא, FIFO+checkpoint). env: `HALACHA_DRAIN_WINDOW_START`/`_END`/`HALACHA_DRAIN_TZ`. **kill-switch `/operations`:** בודק `is_drain_disabled` בעלייה **וגם בתחילת כל סבב** — כיבוי באמצע-ריצה עוצר את הלולאה בגבול-הסבב הבא (התהליך עצמו נהרג מיד דרך ה-UI-toggle/סופרוייזר). חילוץ-הלכות נשאר על claude_session (לא Gemini). self-heal ל-orphaned `processing`. ההלכות נוחתות `pending_review` (שער-יו"ר). **חילוץ תיק-בודד שהיו"ר מבקש רץ מיד דרך ה-CEO (`precedent_extract_halachot`) ואינו מגודר כאן.** | דרך `legal-halacha-drain.config.cjs` (pm2 cron) / ידני |
|
||||||
| `legal-halacha-drain.config.cjs` | pm2/js | **תזמון חלון-לילה של `drain_halacha_queue.py`** (cron UTC `10 20,21,22,23,0,1,2,3 * * *` = superset שמכסה את 23:00–05:00 ישראל בקיץ ובחורף; הסקריפט גוזם לחלון המדויק ב-zoneinfo). דקת-הצתה `:10` (לא `:00`) כדי לא לחלוק דקה עם metadata-drain (`:00`) או supervisor (`:05`) — מונע deadlock של DDL-המיגרציה כששני דריינים עולים יחד. `HALACHA_DRAIN_CRON` לעקיפה. ירייה כל שעה גם מחדשת one-shot שמת באמצע (advisory-lock הופך חפיפה לבטוחה). דורש claude CLI. התקנה: `pm2 start scripts/legal-halacha-drain.config.cjs && pm2 save`. | pm2 cron (host-side) |
|
| `legal-halacha-drain.config.cjs` | pm2/js | **תזמון חלון-לילה של `drain_halacha_queue.py`** (cron UTC `10 20,21,22,23,0,1,2,3 * * *` = superset שמכסה את 23:00–05:00 ישראל בקיץ ובחורף; הסקריפט גוזם לחלון המדויק ב-zoneinfo). דקת-הצתה `:10` (לא `:00`) כדי לא לחלוק דקה עם metadata-drain (`:00`) או supervisor (`:05`) — מונע deadlock של DDL-המיגרציה כששני דריינים עולים יחד. `HALACHA_DRAIN_CRON` לעקיפה. ירייה כל שעה גם מחדשת one-shot שמת באמצע (advisory-lock הופך חפיפה לבטוחה). דורש claude CLI. התקנה: `pm2 start scripts/legal-halacha-drain.config.cjs && pm2 save`. | pm2 cron (host-side) |
|
||||||
| `halacha_drain_supervisor.py` | python | **מנהל-בריאות קבוע ל-`legal-halacha-drain`** (אפס צריכת-Claude — קורא DB/לוגים/pm2 ומצית את הדריינר הקיים). טיק יחיד: **מכבד `is_drain_disabled` בעדיפות עליונה — אם כבוי ב-/operations עוצר את הדריינר ולא מצית** · מצית כשבטל+תור≠ריק · restart ל-run תקוע (liveness לפי checkpoints-per-chunk, **לא** mtime-לוג שמתעדכן רק בסיום תיק ~10 דק') · **זיהוי rate-limit: ה-PRIMARY הוא `quota_exhausted()` הסמכותי (קורא `subscription_usage()` — endpoint לא-מתועד `GET /api/oauth/usage`, token מ-`~/.claude/.credentials.json` + UA `claude-code/*` חובה אחרת 429 — אותו אחוז-ניצול 5-שעות/שבועי/שבועי-Sonnet שה-UI מציג, חלון≥100%=מוצה), durable ואינו תלוי בעומק-זנב-הלוג; `scan_rate_limit` (429 + parse איפוס, מגודר-טריות) רק כ-fallback כש-endpoint לא-זמין — תיקון הבאג שבו 429 שגלל מתחת ל-churn של ה-restart נקרא כ"hung" וגרר restart-storm שבזבז מכסה.** בזמן מוגבל **עוצר את הדריינר** (`hold-stopped`) כדי לא להלום 429, ומצית-מחדש כשהמכסה חוזרת (כל החלונות <100%; fallback `claude -p` זעיר אם ה-endpoint לא זמין). `status` מדפיס את האחוזים · **חלון catch-up בוקר `[05:00–07:00 IDT)` (`CATCHUP_END`) שנפתח רק לניקוי backlog שנותר כשהמכסה חזרה — איפוס-5-שעות מאוחר נוחת לרוב מעט אחרי 05:00; מגודר ב-(לא-מוגבל)+(תור≠ריק), והקצה המורחב מועבר לדריינר כך ש-window-self-guard שלו מקבל את סבבי-ה-catch-up** · מאמת ש-staging מתחייב. **BURST** (חלון "רוץ ברצף עכשיו" ידני): מקור-אמת = `drain_controls.burst_until` ב-DB — אותו ערך ש-/operations קורא/כותב (G1 מקור-יחיד, G2 בלי מסלול מקביל); בעתיד→חלון מורם, אחרת חלון-לילה 23-05; פג-תוקף אוטומטי במועד. תת-פקודות: `tick` (ברירת-מחדל), `burst-on [--until]`, `burst-off`, `status`. | דרך `legal-halacha-supervisor.config.cjs` (pm2 cron) / ידני / כפתור /operations |
|
| `halacha_drain_supervisor.py` | python | **מנהל-בריאות קבוע ל-`legal-halacha-drain`** (אפס צריכת-Claude — קורא DB/לוגים/pm2 ומצית את הדריינר הקיים). טיק יחיד: **מכבד `is_drain_disabled` בעדיפות עליונה — אם כבוי ב-/operations עוצר את הדריינר ולא מצית** · מצית כשבטל+תור≠ריק · restart ל-run תקוע (liveness לפי checkpoints-per-chunk, **לא** mtime-לוג שמתעדכן רק בסיום תיק ~10 דק') · **זיהוי rate-limit: ה-PRIMARY הוא `quota_exhausted()` הסמכותי (קורא `subscription_usage()` — endpoint לא-מתועד `GET /api/oauth/usage`, token מ-`~/.claude/.credentials.json` + UA `claude-code/*` חובה אחרת 429 — אותו אחוז-ניצול 5-שעות/שבועי/שבועי-Sonnet שה-UI מציג. **ספי-עצירה-רכים `USAGE_CEILINGS` (נוהל-יו"ר 2026-06-15): עוצר לפני 429 — 5-שעות ≥75%, שבועי+שבועי-Sonnet ≥65% (עקיפה: `HALACHA_DRAIN_CEILING_5H`/`HALACHA_DRAIN_CEILING_WEEKLY`); הגעה לסף = בדיוק כמו מיצוי, cooldown עד `resets_at` של אותו חלון. הנימוק: 429 באמצע-תיק כופה חילוץ-מחדש של תיק שהושלם ומשחית אותו**), durable ואינו תלוי בעומק-זנב-הלוג; `scan_rate_limit` (429 + parse איפוס, מגודר-טריות) רק כ-fallback כש-endpoint לא-זמין — תיקון הבאג שבו 429 שגלל מתחת ל-churn של ה-restart נקרא כ"hung" וגרר restart-storm שבזבז מכסה.** בזמן מוגבל **עוצר את הדריינר** (`hold-stopped`) כדי לא להלום 429, ומצית-מחדש כשהמכסה חוזרת (כל החלונות חזרו מתחת לסף; fallback `claude -p` זעיר אם ה-endpoint לא זמין). `status` מדפיס ניצול/סף-עצירה · **חלון catch-up בוקר `[05:00–07:00 IDT)` (`CATCHUP_END`) שנפתח רק לניקוי backlog שנותר כשהמכסה חזרה — איפוס-5-שעות מאוחר נוחת לרוב מעט אחרי 05:00; מגודר ב-(לא-מוגבל)+(תור≠ריק), והקצה המורחב מועבר לדריינר כך ש-window-self-guard שלו מקבל את סבבי-ה-catch-up** · מאמת ש-staging מתחייב. **BURST** (חלון "רוץ ברצף עכשיו" ידני): מקור-אמת = `drain_controls.burst_until` ב-DB — אותו ערך ש-/operations קורא/כותב (G1 מקור-יחיד, G2 בלי מסלול מקביל); בעתיד→חלון מורם, אחרת חלון-לילה 23-05; פג-תוקף אוטומטי במועד. תת-פקודות: `tick` (ברירת-מחדל), `burst-on [--until]`, `burst-off`, `status`. | דרך `legal-halacha-supervisor.config.cjs` (pm2 cron) / ידני / כפתור /operations |
|
||||||
| `legal-halacha-supervisor.config.cjs` | pm2/js | **תזמון כל 15 דק' של `halacha_drain_supervisor.py`** (cron `5-59/15 * * * *` = `:05,:20,:35,:50`, `HALACHA_SUPERVISOR_CRON` לעקיפה; דקת-הצתה `:05` כדי לא לחלוק דקה עם metadata-drain `:00` או halacha-drain `:10` — מונע deadlock של DDL-המיגרציה). `autorestart:false` (one-shot per tick). מצב-state ב-`~/halacha-drain-monitor/` (מחוץ ל-repo). התקנה: `pm2 start scripts/legal-halacha-supervisor.config.cjs && pm2 save`. | pm2 cron (host-side) |
|
| `legal-halacha-supervisor.config.cjs` | pm2/js | **תזמון כל 15 דק' של `halacha_drain_supervisor.py`** (cron `5-59/15 * * * *` = `:05,:20,:35,:50`, `HALACHA_SUPERVISOR_CRON` לעקיפה; דקת-הצתה `:05` כדי לא לחלוק דקה עם metadata-drain `:00` או halacha-drain `:10` — מונע deadlock של DDL-המיגרציה). `autorestart:false` (one-shot per tick). מצב-state ב-`~/halacha-drain-monitor/` (מחוץ ל-repo). התקנה: `pm2 start scripts/legal-halacha-supervisor.config.cjs && pm2 save`. | pm2 cron (host-side) |
|
||||||
| `ingest_digests_batch.py` | python | קליטת batch של יומוני "כל יום" מ-`data/digests/incoming/` דרך המסלול העצמאי של קורפוס-הגילוי (`digest_library.ingest_digest`) — חילוץ-LLM (תג-מושג, כותרת-הלכה, מראה-מקום, שני-תאריכים), embedding יחיד, ו-autolink לפסק המקורי (X12/INV-DIG3). רצף (לא מקבילי). מזהה-יומון+תאריך נגזרים משם-הקובץ; העלון החודשי מדולג. **לא מעביר קבצים** — ה-DB (content_hash) הוא מקור-האמת היחיד; הרצה חוזרת מדלגת על קיימים (`exists`). config מ-`~/.env`. | ידני, per-batch (חלופה ל-MCP `digest_upload`) |
|
| `ingest_digests_batch.py` | python | קליטת batch של יומוני "כל יום" מ-`data/digests/incoming/` דרך המסלול העצמאי של קורפוס-הגילוי (`digest_library.ingest_digest`) — חילוץ-LLM (תג-מושג, כותרת-הלכה, מראה-מקום, שני-תאריכים), embedding יחיד, ו-autolink לפסק המקורי (X12/INV-DIG3). רצף (לא מקבילי). מזהה-יומון+תאריך נגזרים משם-הקובץ; העלון החודשי מדולג. **לא מעביר קבצים** — ה-DB (content_hash) הוא מקור-האמת היחיד; הרצה חוזרת מדלגת על קיימים (`exists`). config מ-`~/.env`. | ידני, per-batch (חלופה ל-MCP `digest_upload`) |
|
||||||
| `drain_digests.py` | python | ריקון תור ההעשרה של יומונים (X12): מעבד כל digest בסטטוס `pending` דרך `digest_library.enrich_digest` (חילוץ-LLM Sonnet + embedding + autolink). מקבילי (CONCURRENCY=3, env-tunable), idempotent. מוסיף `~/.local/bin` ל-PATH כדי שה-claude CLI יימצא תחת cron. בודק דגל `drain_controls('legal-digest-drain')` ב-startup → no-op כשכבוי מ-/operations. | דרך `legal-digest-drain.config.cjs` (pm2 cron) + ידני אחרי backfill. חלופת-MCP: `digest_process_pending` |
|
| `drain_digests.py` | python | ריקון תור ההעשרה של יומונים (X12): מעבד כל digest בסטטוס `pending` דרך `digest_library.enrich_digest` (חילוץ-LLM Sonnet + embedding + autolink). מקבילי (CONCURRENCY=3, env-tunable), idempotent. מוסיף `~/.local/bin` ל-PATH כדי שה-claude CLI יימצא תחת cron. בודק דגל `drain_controls('legal-digest-drain')` ב-startup → no-op כשכבוי מ-/operations. | דרך `legal-digest-drain.config.cjs` (pm2 cron) + ידני אחרי backfill. חלופת-MCP: `digest_process_pending` |
|
||||||
|
|||||||
@@ -77,6 +77,31 @@ NIGHT_START, NIGHT_END = 23, 5 # the drain's normal window (IDT hours)
|
|||||||
CATCHUP_END = 7 # soft window end (IDT) for early-morning catch-up — see fix B
|
CATCHUP_END = 7 # soft window end (IDT) for early-morning catch-up — see fix B
|
||||||
|
|
||||||
|
|
||||||
|
def _env_int(name: str, default: int) -> int:
|
||||||
|
try:
|
||||||
|
return int(os.environ.get(name, default))
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return default
|
||||||
|
|
||||||
|
|
||||||
|
# Soft utilization ceilings — stop the drain BEFORE a window actually exhausts
|
||||||
|
# (429s). Hitting a 429 mid-case forces re-extraction of an already-completed
|
||||||
|
# case under the rate limit, DEGRADING it; stopping at the chair's ceilings instead
|
||||||
|
# lets the in-flight halacha case finish cleanly and the drain idle until the
|
||||||
|
# window resets. Reaching a ceiling is treated EXACTLY like 100% exhaustion
|
||||||
|
# (cooldown until that window's resets_at). Per the chair (2026-06-15): the 5-hour
|
||||||
|
# ("hourly session") window stops at 75%, the weekly windows at 65%. Both keys map
|
||||||
|
# to the same windows quota_available / quota_exhausted gate on; overridable via
|
||||||
|
# env for ops tuning without a redeploy.
|
||||||
|
CEILING_FIVE_HOUR = _env_int("HALACHA_DRAIN_CEILING_5H", 75)
|
||||||
|
CEILING_WEEKLY = _env_int("HALACHA_DRAIN_CEILING_WEEKLY", 65)
|
||||||
|
USAGE_CEILINGS = {
|
||||||
|
"five_hour": CEILING_FIVE_HOUR,
|
||||||
|
"seven_day": CEILING_WEEKLY,
|
||||||
|
"seven_day_sonnet": CEILING_WEEKLY,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def _now_utc():
|
def _now_utc():
|
||||||
return datetime.now(timezone.utc)
|
return datetime.now(timezone.utc)
|
||||||
|
|
||||||
@@ -139,11 +164,12 @@ def quota_available() -> bool:
|
|||||||
"""Is the claude.ai quota actually usable right now?
|
"""Is the claude.ai quota actually usable right now?
|
||||||
|
|
||||||
Primary: read the authoritative utilization from the OAuth usage endpoint
|
Primary: read the authoritative utilization from the OAuth usage endpoint
|
||||||
(subscription_usage) and treat a window as exhausted only at >=100%. Cheaper
|
(subscription_usage) and treat a window as exhausted at its USAGE_CEILINGS
|
||||||
and more precise than a probe — no Opus call, and it sees every limit
|
ceiling (the chair's soft stop-before-429 thresholds, NOT 100%). Cheaper and
|
||||||
(5-hour, weekly all-models, weekly-Sonnet) the way the UI does. The 429 reset
|
more precise than a probe — no Opus call, and it sees every limit (5-hour,
|
||||||
time claude.ai reports is often conservative, so this resumes the drain the
|
weekly all-models, weekly-Sonnet) the way the UI does. The 429 reset time
|
||||||
moment a window actually frees up rather than waiting blindly.
|
claude.ai reports is often conservative, so this resumes the drain the moment
|
||||||
|
a window actually frees back under its ceiling rather than waiting blindly.
|
||||||
|
|
||||||
Fallback (endpoint unreachable — it is undocumented): a tiny `claude -p`
|
Fallback (endpoint unreachable — it is undocumented): a tiny `claude -p`
|
||||||
probe via the official CLI. Conservative on failure: any non-zero exit,
|
probe via the official CLI. Conservative on failure: any non-zero exit,
|
||||||
@@ -151,14 +177,13 @@ def quota_available() -> bool:
|
|||||||
usage = subscription_usage()
|
usage = subscription_usage()
|
||||||
if usage is not None:
|
if usage is not None:
|
||||||
# A drain run needs the 5-hour window, the weekly all-models cap, AND
|
# A drain run needs the 5-hour window, the weekly all-models cap, AND
|
||||||
# the weekly per-model cap all below 100%. On this account the per-model
|
# the weekly per-model cap all below their ceilings. On this account the
|
||||||
# cap that's actually populated is Sonnet (seven_day_opus is null — no
|
# per-model cap that's actually populated is Sonnet (seven_day_opus is
|
||||||
# separate Opus cap); the all-models seven_day cap is the backstop for
|
# null — no separate Opus cap); the all-models seven_day cap is the
|
||||||
# Opus usage either way. null utilization → treated as 0% (not limiting).
|
# backstop for Opus usage either way. null utilization → treated as 0%.
|
||||||
windows = ("five_hour", "seven_day", "seven_day_sonnet")
|
utils = {w: (usage.get(w) or {}).get("utilization") for w in USAGE_CEILINGS}
|
||||||
utils = [(usage.get(w) or {}).get("utilization") for w in windows]
|
|
||||||
# utilization may be None (window inactive / no data) → treat as 0%.
|
# utilization may be None (window inactive / no data) → treat as 0%.
|
||||||
return all((u or 0) < 100 for u in utils)
|
return all((u or 0) < USAGE_CEILINGS[w] for w, u in utils.items())
|
||||||
# ── fallback: official-CLI probe ──
|
# ── fallback: official-CLI probe ──
|
||||||
try:
|
try:
|
||||||
r = subprocess.run([CLAUDE, "-p", "Reply with exactly: OK"],
|
r = subprocess.run([CLAUDE, "-p", "Reply with exactly: OK"],
|
||||||
@@ -184,15 +209,16 @@ def quota_exhausted():
|
|||||||
|
|
||||||
Returns (exhausted: bool, earliest_reset_utc: datetime|None), or None when the
|
Returns (exhausted: bool, earliest_reset_utc: datetime|None), or None when the
|
||||||
endpoint is unreachable (caller falls back to the log scrape). A window counts
|
endpoint is unreachable (caller falls back to the log scrape). A window counts
|
||||||
as exhausting the drain at >=100% utilization — same windows quota_available
|
as exhausting the drain at >= its USAGE_CEILINGS ceiling (the chair's soft
|
||||||
gates on (5-hour, weekly all-models, weekly-Sonnet)."""
|
stop-before-429 thresholds) — same windows quota_available gates on (5-hour,
|
||||||
|
weekly all-models, weekly-Sonnet)."""
|
||||||
usage = subscription_usage()
|
usage = subscription_usage()
|
||||||
if usage is None:
|
if usage is None:
|
||||||
return None
|
return None
|
||||||
exhausted, resets = False, []
|
exhausted, resets = False, []
|
||||||
for w in ("five_hour", "seven_day", "seven_day_sonnet"):
|
for w, ceiling in USAGE_CEILINGS.items():
|
||||||
info = usage.get(w) or {}
|
info = usage.get(w) or {}
|
||||||
if (info.get("utilization") or 0) >= 100:
|
if (info.get("utilization") or 0) >= ceiling:
|
||||||
exhausted = True
|
exhausted = True
|
||||||
r = info.get("resets_at")
|
r = info.get("resets_at")
|
||||||
if r:
|
if r:
|
||||||
@@ -606,17 +632,20 @@ def cmd_status():
|
|||||||
def _w(key):
|
def _w(key):
|
||||||
w = usage.get(key) or {}
|
w = usage.get(key) or {}
|
||||||
u = w.get("utilization")
|
u = w.get("utilization")
|
||||||
|
cap = USAGE_CEILINGS.get(key)
|
||||||
|
capf = f"/{cap}%" if cap is not None else ""
|
||||||
if u is None:
|
if u is None:
|
||||||
return "—"
|
return f"—{capf}"
|
||||||
r = w.get("resets_at")
|
r = w.get("resets_at")
|
||||||
try:
|
try:
|
||||||
rt = f" (איפוס {datetime.fromisoformat(r).astimezone(IDT):%H:%M}" if r else ""
|
rt = f" (איפוס {datetime.fromisoformat(r).astimezone(IDT):%H:%M}" if r else ""
|
||||||
rt += ")" if r else ""
|
rt += ")" if r else ""
|
||||||
except Exception:
|
except Exception:
|
||||||
rt = ""
|
rt = ""
|
||||||
return f"{u:.0f}%{rt}"
|
hit = " ⛔" if (u or 0) >= (cap or 100) else ""
|
||||||
print(f"מכסת claude.ai: 5-שעות={_w('five_hour')} · שבועי={_w('seven_day')} · "
|
return f"{u:.0f}%{capf}{rt}{hit}"
|
||||||
f"שבועי-Sonnet={_w('seven_day_sonnet')}")
|
print(f"מכסת claude.ai (ניצול/סף-עצירה): 5-שעות={_w('five_hour')} · "
|
||||||
|
f"שבועי={_w('seven_day')} · שבועי-Sonnet={_w('seven_day_sonnet')}")
|
||||||
else:
|
else:
|
||||||
print("מכסת claude.ai: (endpoint לא זמין)")
|
print("מכסת claude.ai: (endpoint לא זמין)")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user