@tiens.nguyen/gonext-local-worker 1.0.326 → 1.0.328
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.
- package/gonext-repl.mjs +14 -0
- package/gonext_agent_chat.py +40 -1
- package/package.json +1 -1
package/gonext-repl.mjs
CHANGED
|
@@ -456,6 +456,20 @@ function slashCloseSeq(line, cursor, cols) {
|
|
|
456
456
|
`\r\x1b[${Math.min(width, PROMPT_COLS + cursor + 1)}G`
|
|
457
457
|
);
|
|
458
458
|
}
|
|
459
|
+
// A window resize reflows every row, so the overlay's row arithmetic (and the width it
|
|
460
|
+
// clipped to) is stale the moment it happens: rows that fit before may now wrap, which is
|
|
461
|
+
// precisely what made the menu stack in the first place (#109 v3). Redraw at the new width
|
|
462
|
+
// — the render ends with ESC[J, so a block that used to be taller leaves no orphans behind.
|
|
463
|
+
if (process.stdout.isTTY) {
|
|
464
|
+
process.stdout.on("resize", () => {
|
|
465
|
+
if (!slashOverlayActive) return;
|
|
466
|
+
try {
|
|
467
|
+
renderSlashOverlay(slashSel);
|
|
468
|
+
} catch {
|
|
469
|
+
closeSlashOverlay(); // never let a resize wedge the prompt
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
}
|
|
459
473
|
if (process.stdin.isTTY) {
|
|
460
474
|
process.stdin.on("keypress", (_ch, key) => {
|
|
461
475
|
// An arrow-key list picker (/server) is up → ↑/↓ move the highlight, Enter chooses.
|
package/gonext_agent_chat.py
CHANGED
|
@@ -1469,6 +1469,28 @@ def _plain_reply(messages: list, base_url: str, api_key: str, model_id: str,
|
|
|
1469
1469
|
# • a genuine model/agent error — worth the plain-reply degrade.
|
|
1470
1470
|
# `str(e)` for a proxy error is the raw nginx HTML ("<html>…502 Bad Gateway…"), so match
|
|
1471
1471
|
# on the status text/codes and the common connection-failure phrases.
|
|
1472
|
+
def _http_status_of(err):
|
|
1473
|
+
"""The HTTP status an exception carries, or None if it isn't an HTTP error.
|
|
1474
|
+
|
|
1475
|
+
openai-python's APIStatusError exposes `.status_code` (and `.response.status_code`).
|
|
1476
|
+
smolagents wraps model exceptions in AgentGenerationError, so the chain is walked —
|
|
1477
|
+
otherwise every error reaching the turn-level handler would look status-less and fall
|
|
1478
|
+
back to matching on message text."""
|
|
1479
|
+
cur, depth = err, 0
|
|
1480
|
+
while cur is not None and depth < 6:
|
|
1481
|
+
for attr in ("status_code", "http_status", "status"):
|
|
1482
|
+
code = getattr(cur, attr, None)
|
|
1483
|
+
if isinstance(code, int) and 100 <= code <= 599:
|
|
1484
|
+
return code
|
|
1485
|
+
resp = getattr(cur, "response", None)
|
|
1486
|
+
code = getattr(resp, "status_code", None)
|
|
1487
|
+
if isinstance(code, int) and 100 <= code <= 599:
|
|
1488
|
+
return code
|
|
1489
|
+
cur = (getattr(cur, "__cause__", None) or getattr(cur, "__context__", None))
|
|
1490
|
+
depth += 1
|
|
1491
|
+
return None
|
|
1492
|
+
|
|
1493
|
+
|
|
1472
1494
|
def _is_backend_overloaded(err) -> bool:
|
|
1473
1495
|
"""True when the model server ACCEPTED the connection but REFUSED the work because it
|
|
1474
1496
|
is saturated or we are over a quota — 429 / engine_overloaded / rate limit (task #114).
|
|
@@ -1481,7 +1503,15 @@ def _is_backend_overloaded(err) -> bool:
|
|
|
1481
1503
|
Live trigger: Moonshot returned
|
|
1482
1504
|
Error code: 429 - {'error': {'message': 'The engine is currently overloaded, please
|
|
1483
1505
|
try again later', 'type': 'engine_overloaded_error'}}
|
|
1484
|
-
A local Ollama/MLX box does not produce these, so nothing keyed off this fires there.
|
|
1506
|
+
A local Ollama/MLX box does not produce these, so nothing keyed off this fires there.
|
|
1507
|
+
|
|
1508
|
+
A STRUCTURED status wins when the exception carries one: a real 429 is an overload
|
|
1509
|
+
whatever its body says, and a real 401/500/... is NOT one even if some tool output
|
|
1510
|
+
quoted the words "rate limit" into the message. Only when there is no status at all
|
|
1511
|
+
(a bare string, a wrapped error that lost its cause) do we fall back to matching text."""
|
|
1512
|
+
code = _http_status_of(err)
|
|
1513
|
+
if code is not None:
|
|
1514
|
+
return code == 429
|
|
1485
1515
|
s = str(err or "").lower()
|
|
1486
1516
|
if "429" in s or "too many requests" in s:
|
|
1487
1517
|
return True
|
|
@@ -1555,6 +1585,15 @@ def _is_backend_unavailable(err) -> bool:
|
|
|
1555
1585
|
# down". Checked FIRST because a 429 body can also contain the word "timeout".
|
|
1556
1586
|
if _is_backend_overloaded(err):
|
|
1557
1587
|
return False
|
|
1588
|
+
# A structured gateway/timeout status IS an outage regardless of wording: a provider
|
|
1589
|
+
# that returns `Error code: 502 - {"error": …}` carries no "bad gateway" text for the
|
|
1590
|
+
# phrase list below to match, and would otherwise be misfiled as a generic error.
|
|
1591
|
+
# 408 and Cloudflare's 522/524 are included so a real timeout STATUS keeps the same
|
|
1592
|
+
# answer the "timed out" text below has always given it — without them, adding the
|
|
1593
|
+
# structured check would have quietly demoted those.
|
|
1594
|
+
code = _http_status_of(err)
|
|
1595
|
+
if code is not None:
|
|
1596
|
+
return code in (502, 503, 504, 408, 522, 524)
|
|
1558
1597
|
return (
|
|
1559
1598
|
"502 bad gateway" in s
|
|
1560
1599
|
or "503 service" in s
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiens.nguyen/gonext-local-worker",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.328",
|
|
4
4
|
"description": "Polls GoNext cloud API for async local LLM jobs and runs them against Ollama/OpenAI-compatible servers on this Mac",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|